Thank you for your interest in contributing! This guide will help you get started.
Everyone taking part in the project follows the Code of Conduct. Report security vulnerabilities privately as described in SECURITY.md, not in public issues.
- Node.js from
.node-versionand npm - Git available to the CodeNomad backend
- OpenCode V2 CLI (
opencode) in yourPATHor selected in CodeNomad settings; see Requirements for minimum and recommended versions. CodeNomad uses one shared native V2 service for all workspace locations.
git clone https://github.com/NeuralNomadsAI/CodeNomad.git
cd CodeNomad
npm install
npm run devBrowse open 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.
# 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| 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
# Install dependencies
npm install
# Run the dev server
npm run dev
# Run type checking
npm run typecheck --workspace @codenomad/uiWrite clear, descriptive commit messages. Explain what changed and why.
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."git push origin your-branch-nameThen 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
| 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 |
- Server and UI pin
@opencode/client@2.2.3; native plugins pin@opencode/plugin@2.2.3for authoring but resolve Effect and the public SDK from the daemon at runtime. Upgrade pins together with the lockfile and isolated native validation. The runtime CLI is managed independently; its demonstrated minimum and recommended/tested versions remain separate constants inpackages/server/src/opencode/runtime-support.ts(minimum 2.2.3 for theexperimental_image_generateimage tool contract of upstream #60258, consumed by the Images gallery, Design agent and Images settings; recommended 2.2.5). A build pin alone is never a runtime requirement, and unfamiliar newer/custom labels retain authenticated contract recognition. Review current OpenCode documentation, installed declarations, and proxy/API parity whenever the client contract changes. - Upgrade references: OpenCode V2 changelog (GitHub Releases list V1), the exact release tags (
vX.Y.Z) or published npm packages for source/declaration deltas, OpenCode V2 documentation, andnode_modules/@opencode/client/dist/promise/. packages/server/src/workspaces/opencode-service.tsuses the selected host or WSL CLI's officialservice status,service start, andservice get passwordlifecycle 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_HOMEownership settings are ignored. - Closing a project tab immediately detaches its UI and deletes that tab's logical backend workspace record. Sessions keep running in OpenCode; CodeNomad never evicts native Locations. Separately opened occurrences are independent; another window displaying the same record closes its tab with a notification. Whole-window close, quit, reload and backend shutdown retain their existing restoration behavior.
- 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-pluginand 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-windowalways 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.
- Browser preview iframes are sandboxed without same-origin access, so DOM comment inspection is web-only. SideCar iframes are same-origin by design: SideCars are trusted extensions of the application (see
dev-docs/architecture.md).
| 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-guideskill:.opencode/skills/codenomad-architecture-guide/SKILL.md
- 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
- Use
useI18n()in components,tGlobal()in stores - Messages live in
packages/ui/src/lib/i18n/messages/<locale>/ - When adding a string: add to
en/first, then add the same key to every other locale - Placeholders use
{name}syntax (word characters only)
- 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
- Check existing issues and PRs
- Ask in the issue you're working on
- Review the server documentation for CLI flags and configuration