Skip to content
Contributing

Contributing

Architecture

    graph TD
    subgraph Browser
        SPA[React SPA]
    end

    subgraph ocman Server
        HTTP[HTTP Server]
        Static[Embedded Static Assets]
        Registry[Platform Registry]
        Archive[Auto-Archive Goroutine]
    end

    subgraph Adapters
        OCAd[OpenCode adapter]
    end

    subgraph Storage
        OC_DB[(OpenCode DB<br/>read-only)]
        State_DB[(ocman state.db<br/>read-write)]
    end

    subgraph External
        OC[Running OpenCode instances]
    end

    SPA -->|/api/*| HTTP
    SPA -->|static files| Static
    HTTP --> Registry
    Registry --> OCAd
    OCAd --> OC_DB
    OCAd -->|lsof + HTTP| OC
    HTTP -->|archived/seen| State_DB
    Archive --> State_DB
  

Project structure

main.go                                    entrypoint (-addr, -db, -platforms, -auth-* flags)
frontend/                                  React + TypeScript + Vite SPA
e2e/                                       Playwright end-to-end suite
internal/platforms/                        Platform interface, Registry, common types/errors
internal/platforms/opencode/               OpenCode adapter (DB + HTTP proxy)
internal/platforms/claudecode/             Claude Code adapter (JSONL scanner, hook handler)
internal/db/                               SQLite queries against OpenCode's DB
internal/state/                            ocman's own writable state DB (archived/seen,
                                            auth secret, favorites, cached projects)
internal/server/                           HTTP server, API handlers, static file serving,
                                            OpenCode port discovery, auth
internal/server/static/                    Vite build output (embedded into Go binary)
internal/tmux/                             tmux process control + opencode launchers
internal/term/                             in-app browser terminals (tmux windows + PTY bridge)
internal/whisper/                          voice transcription via local whisper-cpp
internal/autoapprove/                      LLM-judged permission auto-approve pipeline
spec/                                      Requirements + architecture notes per feature

Development

Requirements

  • Go 1.24+
  • Node.js 22+ (provided by mise)
  • pnpm (provided by mise; pinned via packageManager in frontend/package.json)
  • mise — provides air, node, and pnpm (mise install)
  • tmux on PATH for the in-UI launcher

Note: All Node-side commands use pnpm. npm is no longer supported — running npm install will not produce the expected pnpm-lock.yaml and will be rejected by CI.

Quick command reference

CommandPortLive ReloadUse Case
make dev8228Yes (HMR)Regular development
make dev-prod-watch8228Manual refreshMemory profiling, production testing
make dev-prod8228NoOne-time production test

All commands

CommandDescription
make devBackend (air) + frontend (vite dev) with HMR on :8228
make dev-prodBackend (air) + frontend (vite preview of production build) on :8228
make dev-prod-watchBackend (air) + frontend (vite preview, auto-rebuilds on changes) on :8228
make dev-backendGo backend only (air, port 8229)
make dev-frontendVite dev server only (port 8228)
make testgo test ./..., vitest run, and the Playwright e2e suite
make lintgo vet, tsc -b, eslint, and the platform-branching check
make buildProduction build (frontend then Go binary)
make cleanRemove binary, tmp/, and static/assets/

Build pipeline

  1. cd frontend && pnpm install --frozen-lockfile && pnpm build — builds frontend into internal/server/static/
  2. go build -o ocman . — embeds internal/server/static/ via //go:embed

Order matters: the frontend must be built before go build.

Development workflows

Regular frontend development:

make dev
# → Instant HMR updates; best for UI iteration

Memory profiling / production testing:

make dev-prod-watch
# → Edit code → auto-rebuild → manually refresh browser

Vite’s preview mode doesn’t include HMR. The watcher rebuilds automatically but you need to refresh the browser manually. This is intentional — production builds are for testing, not rapid iteration.

Tests

The repo has 180+ Go tests (unit + integration), 81 frontend tests (vitest), and a Playwright end-to-end suite covering auth, the dashboard, composer, SSE streaming, and prompts. CI runs all three on every PR; make test runs them locally.

Platform-agnostic frontend

The frontend must not branch on session.platform === 'opencode' (or similar string comparisons). UI capability gating goes through /api/capabilities + useCapabilities() instead. This is enforced by scripts/check-platform-branching.sh, which make lint runs. If you hit a false positive, the pragma // ocman:allow-platform-branch suppresses the check on the next line — use sparingly (e.g. for generic helpers that legitimately need the platform identity).