Local runtime for coding agents — session management, process supervision, event normalization, and git isolation.
Process manager for coding agents: PM2 + tmux + git worktrees + normalized events
CodeDeck is not an agent. It manages the lifecycle of existing harnesses:
- Claude Code (
claude -p --output-format stream-json) - Codex (
codex exec --json/ app-server) - OpenCode (
opencode run --format json) - OMP (
omp --mode rpc)
The codedeck CLI provides a single unified interface for all of them:
npx codedeck run "implement authentication" --agent claude
npx codedeck run "fix the tests" --agent codex
npx codedeck run "investigate this bug" --agent opencode
npx codedeck run "refactor this module" --agent omp
npx codedeck ps
npx codedeck logs a83f --follow
npx codedeck show a83f
npx codedeck send a83f "add tests"
npx codedeck stop a83f
npx codedeck diff a83f
npx codedeck doctor- TypeScript / Node.js
- SQLite (
node:sqlite—DatabaseSync) - Unix Domain Socket for IPC
- Git worktrees for isolation
npm install -g codedeck
# or
npx codedeckBinary: codedeck
For local development:
npm install
npm run build
node dist/cli/index.js doctor
# or create a local alias
npm link
npx codedeck doctorcodedeck CLI
│ IPC (Unix Socket, NDJSON)
▼
CodeDeck Daemon
├── Session Store (SQLite)
├── Event Store (SQLite)
├── Process Manager
└── Driver Registry
├── ClaudeDriver (stream-json)
├── CodexDriver (exec --json)
├── OpencodeDriver (run --format json)
└── OmpDriver (rpc)
The daemon owns the sessions. The CLI only follows events — closing the terminal does not kill the agent.
| Command | Description |
|---|---|
npx codedeck doctor |
Check Node, Git, harnesses, daemon, and database |
| `npx codedeck run "" --agent [--model ] [--name ] [--worktree] [--bg | --detach]` |
npx codedeck wait <id> [--json] |
Wait for a session to reach a terminal state without polling |
npx codedeck ps [--all] [--json] |
List recent sessions |
npx codedeck show <id> [--json] |
Show session details |
npx codedeck logs <id> [--follow] [--json] [--raw] |
Show normalized events |
npx codedeck send <id> "<msg>" |
Continue a session (new turn) |
npx codedeck stop <id> |
Graceful interrupt → SIGTERM → SIGKILL |
npx codedeck diff <id> [--stat] [--json] |
Git diff against base commit |
Session {
id: string // e.g. a83f (CodeDeck)
nativeSessionId? // internal harness id
agent: "claude" | "codex" | "opencode" | "omp"
status: "starting" | "working" | "needs_input" | "idle" | "completed" | "failed" | "stopped" | "orphaned"
cwd, worktree, branch, repository, baseCommit
pid, usage, createdAt, updatedAt
}nativeSessionId is an internal detail. Users only see the CodeDeck ID.
AgentEvent =
| session.started | turn.started | text.delta | message
| tool.started | tool.completed | file.changed
| permission.requested | permission.resolved
| usage.updated | turn.completed
| session.completed | session.failed | errorEvery event is persisted with its original raw payload intact for debugging and future compatibility.
Tables:
sessions— per-session stateevents— monotonic log(session_id, sequence)
Codedeck is consumed by agents as a subprocess, so failures are machine-readable:
session.failedevents carry a structuredfailureobject:
{
"type": "session.failed",
"error": "EPIPE: broken pipe, write",
"failure": { "code": "HARNESS_CRASH", "blame": "harness", "retryable": true, "reason": "unhandled_rejection" }
}blameseparates a harness crash (harness→ retry the session) from failed work (task→ fix the code) from a setup problem (infra).- The same object is hydrated on the session row:
codedeck show <id> --json→session.failure. - A harness death without a terminal frame is reported as
session.failed(blameharness, retryable) — never as a silentcompleted.
| Code | Meaning |
|---|---|
| 0 | session completed or stopped |
| 1 | task failed — the agent's work failed; retrying unchanged won't help |
| 2 | harness crashed (EPIPE, signal, unhandled rejection) — retryable |
| 3 | infra — daemon, worktree, spawn, or usage errors |
run blocks and follows events by default. run --bg starts the session,
prints its session object or ID, and exits without waiting. --detach remains
accepted as an alias for --bg.
wait <id> blocks without printing the event stream, then prints one terminal
result. wait --json prints the final session object as one JSON line. Closing
the terminal or pressing Ctrl+C detaches the waiter; it does not stop the
session.
Harness processes run detached from the daemon and write stdout/stderr to
per-session files under ~/.run-agent/logs/. A daemon restart therefore does
not close the harness output pipe, send EPIPE, or apply pipe backpressure.
On startup the daemon checks each active session's persisted PID. If the
process is still alive, it reattaches to the session log from the stored byte
offset and keeps the session working; it does not mark the session
orphaned or spawn a duplicate harness. If the process finished while the
daemon was down, the new daemon drains the log, records any terminal event,
and synthesizes a structured failure when the harness left no terminal frame.
codedeck stop <id> also falls back to the persisted PID, so stopping a
reattached session works even when no in-memory process handle exists.
npx codedeck run "implement oauth" --worktree
# creates ~/.run-agent/worktrees/<repo-hash>/<session-id>
# branch: ra/<slug>-<session-id>The driver receives the worktree as cwd. Isolation is the responsibility of CodeDeck, not the harness.
Global config: ~/.config/run-agent/config.json or ~/.run-agent/ (fallback)
{
"defaultAgent": "claude",
"worktree": true
}Validates each harness in isolation before abstractions:
spikes/claude.ts
spikes/codex.ts
spikes/opencode.ts
spikes/omp.ts
Run with:
npx tsx spikes/claude.tsEach spike proves: detect, start, prompt, events, nativeSessionId, completion, interrupt, resume, stderr, and cleanup.
src/
cli/ → commander, commands (run/ps/show/logs/send/stop/diff/doctor)
core/ → driver interface, session, events, capabilities, errors
drivers/ → claude / codex / opencode / omp
daemon/ → daemon, ipc (Unix socket), process-manager, protocol
store/ → SQLite (sessions, events)
git/ → repository, worktree, diff
config/ → paths, config
utils/
Build:
npm run build
npm testcd example-project
npx codedeck doctor
npx codedeck run "find one improvement and implement it" --agent claude --worktree --bg
npx codedeck run "find one improvement and implement it" --agent codex --worktree --bg
npx codedeck ps
npx codedeck wait <claude-session>
npx codedeck logs <claude-session> --follow
npx codedeck show <codex-session>
npx codedeck diff <claude-session>
npx codedeck diff <codex-session>
npx codedeck send <claude-session> "run the tests before finishing"
npx codedeck stop <codex-session>The same experience across all four harnesses.
- Does not store API keys/tokens — uses harness authentication.
- Raw events may contain sensitive data (documented).
- Does not silently modify user configuration.
MIT