# Contributing to CodeNomad Thank you for your interest in contributing! This guide will help you get started. ## Prerequisites - **Node.js** from [`.node-version`](.node-version) and npm - **Git** available to the CodeNomad backend - **OpenCode V2 CLI** (`opencode`) in your `PATH` or selected in CodeNomad settings; see [Requirements](README.md#requirements) for minimum and recommended versions. CodeNomad uses one shared native V2 service for all workspace locations. ## Quick Start ```bash git clone https://github.com/NeuralNomadsAI/CodeNomad.git cd CodeNomad npm install npm run dev ``` ## Finding Issues to Work On Browse [open issues](https://github.com/NeuralNomadsAI/CodeNomad/issues) and look for these labels: | Label | Meaning | |---|---| | `ready-to-work` | Clear scope, ready for anyone to pick up | | `good-first-issue` | Good for first-time contributors | | `enhancement` | New feature requests | | `bug` | Bug reports | **Before starting:** comment on the issue so we can discuss approach and avoid duplicate work. ## Development Workflow ### 1. Fork and Branch ```bash # Fork the repo on GitHub, then clone your fork git clone https://github.com/YOUR_USERNAME/CodeNomad.git cd CodeNomad # Add the upstream remote git remote add upstream https://github.com/NeuralNomadsAI/CodeNomad.git # Create a branch from upstream/dev git fetch upstream git checkout -b fix/your-branch-name upstream/dev ``` ### 2. Branch Naming | Prefix | Use for | |---|---| | `fix/` | Bug fixes | | `feat/` | New features | | `docs/` | Documentation changes | | `refactor/` | Code refactoring | | `chore/` | Build, config, maintenance | Examples: `fix/question-queue-ordering`, `feat/retry-tool-call`, `docs/contributing-guide` ### 3. Make Your Changes ```bash # Install dependencies npm install # Run the dev server npm run dev # Run type checking npm run typecheck --workspace @codenomad/ui ``` ### 4. Commit Write clear, descriptive commit messages. Explain **what** changed and **why**. ```bash git add . git commit -m "fix(ui): preserve question queue order when upserting duplicate requests When a question arrives as a global entry and later resolves to a tool part with a newer timestamp, the original enqueue time was lost, causing the question to move behind newer entries and break interruption order." ``` ### 5. Push and Create a PR ```bash git push origin your-branch-name ``` Then open a pull request on GitHub targeting the `dev` branch. **PR checklist:** - [ ] Branch is based on latest `upstream/dev` - [ ] One issue per PR (don't mix unrelated changes) - [ ] Type checking passes: `npm run typecheck` (root) or the workspace-specific script matching your change area - [ ] Tests pass (if applicable) - [ ] PR description explains the change, includes relevant screenshots for UI changes, and links related issues when applicable ## Project Structure | Package | Description | |---|---| | `packages/server` | Core logic & CLI — workspaces, OpenCode proxy, API, auth | | `packages/ui` | SolidJS frontend — reactive UI components and stores | | `packages/electron-app` | Electron desktop shell | | `packages/tauri-app` | Tauri desktop shell (experimental) | | `packages/cloudflare` | Cloudflare deployment adapters | ### OpenCode V2 Boundaries - Server and UI pin `@opencode/client@2.0.4`; the pruning plugin pins `@opencode/plugin@2.0.4`. Upgrade them together with the lockfile and isolated native validation. The runtime CLI is managed independently, and startup must not reject an otherwise compatible service solely for a different version string. Review current OpenCode documentation, installed declarations, and proxy/API parity whenever the client contract changes. - Upgrade references: [OpenCode releases](https://github.com/anomalyco/opencode/releases), [OpenCode V2 documentation](https://opencode.ai/v2/docs/), and `node_modules/@opencode/client/dist/promise/`. - `packages/server/src/workspaces/opencode-service.ts` uses the selected host or WSL CLI's official `service status`, `service start`, and `service get password` lifecycle to connect to one externally owned global daemon. CodeNomad owns no private port, database, registration, or daemon PID and never stops the daemon on backend shutdown. - WSL requires Windows localhost forwarding and runs the Linux CLI lifecycle inside the distribution; never inspect or signal Linux PIDs from Windows. - OpenCode owns the global daemon's standard state and database. Configured allowed environment variables apply only when CodeNomad starts a missing daemon; an existing daemon is unchanged, and legacy `OPENCODE_DB`/`XDG_STATE_HOME` ownership settings are ignored. - Explicit **Stop Workspace** evicts the native location/resources. Closing a tab or window only detaches that local UI and must never delete or evict the workspace. - OpenCode session calls use `/workspaces/:id/instance/api/*`; CodeNomad control routes and multiplexed events use `/api/*` and `/api/events`. - The proxy is method/path allowlisted, so new upstream functionality is not exposed automatically. - Shell mode (`client.session.shell`) and prompt instructions (`client.session.instructions.entry`) remain separate from background shells and interactive PTYs. - Location-scoped background shells use `client.shell.*` and are listed in the Status panel. The UI refreshes them on Shell events and reconnect, displays native metadata, and supports ownership-checked removal. `client.pty.*` remains reserved for interactive terminals. `packages/opencode-plugin` and the server plugin/background-process paths remain deleted and must not be restored. - Native events are volatile. Reconnect handlers must refetch authoritative state instead of assuming missed events will replay. - Git mutations and Yolo policy remain CodeNomad-owned server boundaries. - Native desktop identity is channel plus config profile: one singleton process/backend per profile and multiple UUID windows. A second launch opens another window by default; Advanced settings can restore MRU focus, while `--new-window` always requests another window. Stable, dev, and non-default profiles isolate native/browser/client state; OpenCode sessions/messages stay shared while tabs, drafts, and views are per-window. - Desktop restore uses a V3 per-window envelope over the V2 content-addressed partition graph. Preserve atomic publication/migration, ownership write fencing, and post-commit conservative garbage collection in both Electron and Tauri. - Native SideCar/browser previews are sandboxed without same-origin access, so DOM comment inspection is web-only. ### Key UI Files | Path | Purpose | |---|---| | `packages/ui/src/stores/session-events.ts` | SSE event handlers (idle, status, permissions, questions) | | `packages/ui/src/stores/session-actions.ts` | User actions (send message, abort, revert, fork) | | `packages/ui/src/stores/message-v2/` | Message store (v2 architecture) | | `packages/ui/src/stores/instances.ts` | Instance management and interruption queues | | `packages/ui/src/components/tool-call.tsx` | Tool call rendering | | `packages/ui/src/components/message-block.tsx` | Message display blocks | | `packages/ui/src/components/session/session-view.tsx` | Main session view | | `packages/ui/src/lib/i18n/messages/` | Translation files (en, es, fr, ja, ru, he, zh-Hans, de, ne, tr) | > For the package map, native OpenCode V2 integration, ownership boundaries, and feature traces, load the `codenomad-architecture-guide` skill: > `.opencode/skills/codenomad-architecture-guide/SKILL.md` ### Styling - Tokens: `packages/ui/src/styles/tokens.css` - Utilities: `packages/ui/src/styles/utilities.css` - Component styles: `packages/ui/src/styles/components/`, `packages/ui/src/styles/messaging/`, `packages/ui/src/styles/panels/` - Keep style files under ~150 lines; split by component ### Internationalization (i18n) - Use `useI18n()` in components, `tGlobal()` in stores - Messages live in `packages/ui/src/lib/i18n/messages//` - When adding a string: add to `en/` first, then add the same key to every other locale - Placeholders use `{name}` syntax (word characters only) ## Code Principles - **KISS**: Keep modules narrowly scoped - **DRY**: Share helpers before copy-pasting - **Single responsibility**: Split files when concerns diverge - **Composable primitives**: Prefer signals, hooks, utilities over deep inheritance ## Need Help? - Check existing [issues](https://github.com/NeuralNomadsAI/CodeNomad/issues) and [PRs](https://github.com/NeuralNomadsAI/CodeNomad/pulls) - Ask in the issue you're working on - Review the [server documentation](packages/server/README.md) for CLI flags and configuration