Supercharge your coding agent.
Coodra is a local-first plugin architecture for AI coding agents: native agent plugins install Coodra MCP, the lifecycle_event hook tool, managed Graphify, recipes, Deep Wiki, durable memory, and policy governance so sessions start with context and end with evidence.
What Coodra is, in one breath.
~3 min
A coordination layer between human architects and AI coding agents — Claude Code, Codex, Cursor, Devin, Antigravity, and MCP-speaking clients. It runs locally, installs through native plugins, and routes lifecycle events through Coodra's MCP (Model Context Protocol) server.
Native agent plugins
Generated plugin bundles install the Coodra MCP server, the lifecycle_event hook tool, managed Graphify MCP entry, and task skills for context, recipes, wiki, Work Packs, and graph-aware development.
Context Packs
Context Packs, decisions, Work Packs, wiki pages, recipes, and run history live in SQLite/Postgres. Agents see a manifest at boot, then pull the specific memory they need through MCP tools.
Policy enforcement
Pre-tool-use checks, kill switches, native advisory rules, and audit rows keep humans in charge. Coodra returns a verdict only when it has a rule; otherwise the agent's native permission system remains authority.
/01.1Design principles
Five things that are true everywhere in the codebase, and that any contribution must respect.
FAIL_OPEN_RESULTresumed_at. ADR-007DATABASE_URL.Five minutes to your first run.
Zero network
If you have Node 22, pnpm, and a supported agent installed locally, you are a few commands away. Solo mode is the default after coodra init; native plugins carry the MCP and lifecycle wiring.
Confirm the prereqs.
You need Node 22.16.0 (pinned in .nvmrc), pnpm 10.33.0 via corepack, git, and a supported agent installed locally. macOS uses launchd; Linux uses a fallback daemon manager with PID files.
# Versions Coodra pins to nvm use # 22.16.0 per .nvmrc corepack enable # activates pnpm 10.33.0 node --version # → v22.16.0 pnpm --version # → 10.33.0
Install the CLI.
The only published artefact is @coodra/cli. It ships as a self-contained bundle — bring-your-own SQLite native module is rebuilt on install.
npm i -g @coodra/cli # or, in a workspace: pnpm add -D @coodra/cli
Initialise the project.
From your repo root, run coodra init. It registers the project under .coodra/, seeds the default policy, creates recipe/wiki/graphify/Work Pack directories, and leaves repo-root MCP config user-owned.
coodra init # picks the slug from git remote, asks which IDE, # defaults to --solo. Use --team to wire Clerk. # What it creates: .coodra/config.json {"projectSlug":"my-app"} .coodra/manifest.json project registration ledger .coodra/recipes/ agent recipe workspace .coodra/graphify/ managed structural graph output .coodra/wiki/ Deep Wiki grounding + mirrors ~/.coodra/data.db SQLite primary store ~/.coodra/.env solo-mode env, ready to grow into team
Start the daemons.
One command brings up the local MCP server and web UI. Native plugins report SessionStart, prompt, tool, compaction, and SessionEnd events through the Coodra MCP hook tool, lifecycle_event. Sync-daemon stays asleep in solo mode.
coodra start # → mcp-server listening on 127.0.0.1:3100 # → web-v2 http://localhost:3001 # Confirm everything's alive coodra status coodra doctor # 12 essential checks (36 with --full)
Open the agent & watch.
Run coodra agent add <agent> or coodra agent repair <agent>, then launch your agent in the repo. SessionStart reaches Coodra through the plugin, opens a runs row, captures base_sha, and returns a compact manifest of recipes, decisions, context, and project state. The agent pulls bodies through MCP when needed.
Visit http://localhost:3001/runs to watch the trace populate live.
Now skim Core concepts to understand what's happening under the hood, or jump to the CLI reference for every subcommand.
Two modes. Same primitives.
COODRA_MODEenv flag
Solo and Team mode share schema, code, and CLI. The difference is who sees what, and whether the sync-daemon boots.
Everything on your laptop.
SQLite at ~/.coodra/data.db. Loopback only. Solo-bypass auth — no Clerk, no JWT, just SOLO_ACTOR stamped on every write. The sync-daemon does not boot. Zero network egress.
Best for: individual contributors, OSS hacking, air-gapped envs, contractors with a strict no-cloud policy.
Cloud-synced memory.
Same local SQLite — plus a Postgres + pgvector mirror you operate, plus Clerk for identity. The sync-daemon pushes sync_to_cloud outbox rows and pulls teammates' runs, decisions, and packs back into local SQLite.
Best for: small teams sharing context, multi-laptop developers, BYO-cloud self-host.
/03.1Mode matrix
| Capability | Solo | Team |
|---|---|---|
| Local SQLite primary store | YES | YES |
| Native plugin + Coodra MCP lifecycle | YES | YES |
Web UI on :3001 | YES | YES |
| Sync-daemon boots | NO | YES |
| Clerk auth · JWT verification | BYPASS | REQUIRED |
| Cross-teammate visibility | NO | YES |
| Cross-machine (same user) | NO | VIA team install |
| Network egress required | NONE | POSTGRES + CLERK |
Promote a project from solo to team without losing history with coodra team setup → coodra cloud-migrate. The migration is tracked in _migration_attempts with per-row mapping in _migration_map for surgical rollback.
The vocabulary.
It pays back.
These nouns carry the whole system. Internalise them and the rest of the docs read like an implementation map: agent events enter through MCP, memory lives in the database, Work Packs bind issue work to evidence, Graphify supplies the code graph, and the Deep Wiki turns that structure into navigable documentation.
flowchart TD
Agent["Coding agent"] --> Plugin["Native Coodra plugin"]
Plugin --> Event["Lifecycle event"]
Event --> Tool["lifecycle_event MCP tool"]
Tool --> Run["Run"]
Run --> Events[("run_events")]
Tool --> Policy["Policy engine"]
Policy --> PolicyRows[("policy_decisions")]
Run --> Memory["Coodra memory"]
Memory --> Packs["Context Packs"]
Memory --> Decisions["Decisions"]
Memory --> Work["Work Packs"]
Work --> External["External issue or PR"]
Graph["Graphify code graph"] --> Wiki["Deep Wiki"]
Graph --> Decisions
Wiki --> Memory
Packs --> Future["Future agent session"]
Decisions --> Future
Work --> Future
runsruns table — agent type, mode, status, base_sha, created_by_user_id, project_id. Bound to a session by get_run_id; reused across get_run_id calls within the same session. One run per session per projectSessionStart, UserPromptSubmit, PreToolUse, PostToolUse, PostCompact, Stop, or SessionEnd. Events resolve runs, refresh context, check policy, record activity, and finalize evidence.lifecycle_event. The Coodra MCP tool native plugins call for lifecycle events. It is the developer entrypoint for run creation, prompt-time memory refresh, tool policy checks, event recording, compaction bookkeeping, and SessionEnd finalization.context_packs, linked to runs and optionally Work Packs, searchable by MCP, and created by the agent or by SessionEnd lifecycle fallback..coodra/work-packs/<slug>/ and the work_packs tables. Work Packs connect external work items from Jira, Linear-style planning tools, GitHub/GitLab, or manual sources to local plan/status, runs, decisions, and Context Packs..coodra/recipes/<slug>/. Pulled on demand via list_recipes / get_recipe when a task matches a recipe's trigger — not auto-injected.sha256(description)[:32]. Surfaced to later sessions through lifecycle relevance refresh.coodra/graphify/out/: graph.json, GRAPH_REPORT.md, and graph.html. It is queried through a separate Graphify MCP server and used for file-neighborhood context, wiki planning, and graph-backed decision lookup.coodra wiki build creates grounding from source, Graphify, and prior Coodra memory; an agent saves structure and pages through wiki_save_structure / wiki_save_page; wiki_ask retrieves authored pages for future work.pd:{sessionId}:{toolUseId}:{toolName}:{eventType}. ADR-007/04.1Runs & sessions
A session is what the agent calls itself. A run is what Coodra calls that work. The first call to get_run_id for a session either opens a new runs row, or — if the agent crashed and restarted — finds and resumes the existing one.
// MCP tool: get_run_id · apps/mcp-server/src/tools/get-run-id/ const { runId } = await mcp.call("get_run_id", { sessionId: "cc-sess-2026-05-16-8a3f2c", projectSlug: "my-app", agentType: "claude_code", }); // First call: inserts a runs row, returns runId. // Repeat call: returns the same runId (UNIQUE on project_id, session_id).
Every run_events row, every policy_decisions row, and the eventual context_packs row hang off this runId. run_events.run_id uses ON DELETE SET NULL so PreToolUse events that fire before the run exists still land — they just attach later when the row appears.
/04.2Lifecycle events
Lifecycle events are not user-facing commands. They are the machine-readable contract between a native agent plugin and Coodra's MCP server.
| Event | Primary Coodra behavior | Developer mental model |
|---|---|---|
SessionStart | Open or resume a run, capture base_sha, return session contract and memory manifest. | Boot context. |
UserPromptSubmit | Perform bounded relevance refresh using prompt, file, Work Pack, FTS/BM25, and Graphify signals. | Just-in-time context. |
PreToolUse | Evaluate kill switches and policy rules, then write policy_decisions. | Govern before action. |
PostToolUse | Record the event, external MCP activity, latency/error metadata, and pull-side memory access where applicable. | Evidence after action. |
PostCompact | Advance baseline generation; later prompt-time context belongs to the new generation. | Keep post-compaction pull-through attribution honest. |
Stop · SessionEnd | Finalize the run, capture diff, write fallback Context Pack, update Work Packs, and trigger safe graph refreshes when available. | Close the evidence loop. |
The implementation center is apps/mcp-server/src/tools/lifecycle-event/handler.ts, with reusable finalization and memory workers in packages/lifecycle/. The important boundary is that the plugin reports events; Coodra normalizes, records, and answers through MCP.
/04.3Memory model
Coodra memory exists to make future agent sessions start with project history, architectural intent, and task evidence without stuffing the whole past into every prompt.
flowchart TD
Session["Agent session"] --> Save["Save memory"]
Save --> CP["save_context_pack"]
Save --> DEC["record_decision"]
Save --> WP["work_pack_upsert / update"]
Save --> WIKI["wiki_save_page"]
CP --> Store[("SQLite / Postgres")]
DEC --> Store
WP --> Store
WIKI --> Store
Store --> StartSurface["SessionStart manifest surfaces ids and titles"]
Store --> PromptSurface["UserPromptSubmit relevance refresh surfaces context"]
Store --> PolicyTeach["Policy reason teaches motivating decisions"]
StartSurface --> PushRows["push rows: session_start_manifest"]
PolicyTeach --> PushRows2["push rows: policy_reason"]
PushRows --> Access[("memory_access_events")]
PushRows2 --> Access
StartSurface --> AgentPull["Agent pulls bodies through MCP tools"]
PromptSurface --> AgentPull
AgentPull --> Tools["read_context_pack / search / query / wiki_ask / get_recipe"]
Tools --> Access[("memory_access_events")]
Access --> Rollups["memory_access_daily / memory_cohorts"]
Store --> Freshness["memory gardening marks freshness"]
Freshness --> AgentPull
Continuity, not prompt stuffing
Memory gives the next agent the project state it cannot infer from the current prompt: what happened, why decisions exist, which issue is active, what files are implicated, and where the deeper explanation lives.
DB-backed and MCP-mediated
Solo mode writes to ~/.coodra/data.db. Team mode keeps the same local hot path and syncs to operator-owned Postgres. Agents read and write through MCP tools, not by mutating markdown mirrors directly.
Usefulness is observable
Coodra records two push channels today — session_start_manifest and policy_reason — plus pull rows from instrumented retrieval tools. Pull-through, stale-at-access share, bytes, latency, and actor breakdowns show whether memory helped or merely existed.
| Memory surface | How it is saved | How it is retrieved | What it is for |
|---|---|---|---|
| Context Packs | save_context_pack or SessionEnd fallback. | search_packs_nl, list_context_packs, read_context_pack. | Resume what changed, what was tested, and what remains open. |
| Decisions | record_decision with rationale, alternatives, reversibility, and impact. | query_decisions, query_decisions_by_file, prompt-time recent decisions. | Preserve why an architecture choice exists. |
| Work Packs | Agent fetches provider state through that provider's native MCP, then persists a local map with work_pack_upsert / work_pack_update. | work_pack_status; Work Pack filters on packs and decisions. | Resume Jira, Linear-style, PR, or manual implementation work with linked evidence. |
| Deep Wiki | wiki_save_structure and wiki_save_page after grounded authoring. | wiki_ask and local coodra wiki ask. | Explain subsystems and architecture from source, Graphify, and prior memory. |
| Recipes | .coodra/recipes/<slug>/ plus recipe index metadata. | list_recipes, get_recipe, get_recipe_file. | Reusable task guidance before work starts. |
| Policy teaching | Active decisions linked to affected paths and policy audit context. | Pre-tool policy responses and decision lookup. | Teach the agent why a permission prompt or denial matters. |
At session start, Coodra can surface a compact manifest and bounded recent context, and that manifest is recorded as session_start_manifest push rows. On prompt submit, it performs a narrower relevance refresh using exact issue/file signals, Work Pack hints, FTS/BM25, and Graphify-backed file-decision lookup; that context is surfaced to the agent, but it does not currently create push rows. The agent then pulls full bodies only when useful, and pull access is recorded from instrumented tool outputs rather than guessed from the prompt.
Freshness is layered on top of memory instead of rewriting it. Gardening can mark a pack or decision stale, unverified, or fresh and can propose repairs, but it does not invisibly edit the historical record. That keeps Coodra useful as both memory and audit trail.
/04.4Work Packs
A Work Pack is Coodra's local, evidence-backed representation of a unit of implementation work. It can mirror Jira, Linear-style planning tools, GitHub/GitLab PRs, or a manual task, but provider reads and writes stay in the provider's own native MCP.
Issue-bound work state
Lives in .coodra/work-packs/<slug>/ and work_packs. It carries normalized Coodra type/status, provider-native issue type/status, local spec, implementation notes, sync notes, relationships, and metadata.
Native MCP handoff
The agent fetches fresh Jira or Linear-style planning data through that provider's MCP, then calls work_pack_upsert to persist the local map. Coodra stores provider keys and evidence; it does not own provider credentials.
Context with evidence
work_pack_status returns local Work Pack state for the agent, while Context Packs and Decisions linked to the same pack show what changed, why, and what remains open.
| Moment | Agent action | Coodra record |
|---|---|---|
| Import | Read Jira, Linear-style, GitHub/GitLab, or manual task details through the relevant MCP or local source. | external_work_items, work_packs, work_pack_external_links. |
| Plan | Write implementation notes, relationships, and normalized type/status through work_pack_upsert. | spec_markdown, implementation_markdown, sync_markdown, relationship rows. |
| Work | Save Context Packs and Decisions with Work Pack slugs attached. | work_pack_context_pack_links, work_pack_decision_links, latest activity rollup. |
| Refresh | Fetch fresh provider state through the provider MCP, then persist updated local state. | sync_events, provider status fields, local_ahead / local_only sync state. |
| Resume | Call work_pack_status, then pull linked packs/decisions/context bodies only when needed. | Local resume surface plus memory pull-through telemetry where instrumented memory tools are used. |
Provider MCPs are the authority for external systems. Coodra is the local execution memory: it remembers what the agent understood, what it changed, which evidence is linked, and what should be resumed next.
/04.5Recipes & Context Packs
These three are easy to confuse because all three help an agent continue work. They differ by ownership and time: recipes guide before work, Work Packs scope issue-bound work, and Context Packs preserve after-work evidence.
Agent Recipe
Human- or agent-authored. Lives in .coodra/recipes/<slug>/. Reusable guidance the agent pulls on demand via list_recipes / get_recipe when a task matches its trigger.
Direction: human → agent.
Work Pack
Issue-bound implementation state. Lives in .coodra/work-packs/<slug>/ and work_packs, with links to runs, decisions, Context Packs, relationships, and external work items fetched through Jira, Linear-style, GitHub/GitLab, or manual workflows.
Direction: issue ↔ agent ↔ evidence.
Context Pack
Agent-authored (or lifecycle fallback on session end). One row per run in context_packs with title, content, content_excerpt, meta. Queryable cross-session via search_packs_nl, list_context_packs, read_context_pack.
Direction: agent → future agents.
Coodra memory is not a prompt dump. SessionStart can surface a compact manifest; the agent then pulls bodies through instrumented tools such as read_context_pack, search_packs_nl, list_context_packs, query_decisions, query_decisions_by_file, wiki_ask, list_recipes, and get_recipe. Work Pack state is retrieved through work_pack_status, but that tool is a resume surface rather than a pull-through telemetry source today.
If a saved pack has source: 'agent' and a new save_context_pack call arrives with the same runId, the response is status: 'idempotent_hit'. Automatically generated packs can be upgraded in place when the agent later provides richer context for the same run.
Work Packs are provider-neutral on purpose. Coodra does not own Jira or Linear credentials and does not call those APIs directly; the coding agent uses the relevant native MCP to fetch or update the planning tool, then calls Coodra's Work Pack tools to persist the local implementation map and evidence links.
/04.6Code graph & Deep Wiki
Graphify and Deep Wiki are connected, but they are not the same artifact. Graphify maps structure; the wiki explains it.
Structural code graph
Coodra manages Graphify output at .coodra/graphify/out/. The current graph provides graph.json, GRAPH_REPORT.md, and graph.html; native plugins wire a separate Graphify MCP server beside Coodra MCP.
Grounded architecture docs
coodra wiki build creates grounding.md and job.md from source, Graphify communities, god nodes, and prior recorded work. Pages are saved through MCP and mirrored under .coodra/wiki/<slug>/md/.
For developers, the practical rule is simple: use Graphify when you need structural neighborhoods or dependency paths; use Deep Wiki when you need a human-readable explanation of the subsystem. wiki_ask retrieves authored wiki pages, while Graphify MCP answers graph questions directly.
/04.7Decisions
An ADR-grade record the agent emits when it makes a non-trivial choice: pick an ORM, change a public API shape, defer a refactor. They survive across sessions and surface to the next agent via the UserPromptSubmit event.
await mcp.call("record_decision", { runId, description: "Use better-sqlite3 instead of sql.js for primary store", rationale: "Synchronous API matches MCP request lifecycle; ~3x faster on writes.", alternatives: ["sql.js", "libsql/Turso"], confidence: "high", reversible: false, }); // Idempotent on dec:{runId}:{sha256(description)[:32]} // Resurfaces on next UserPromptSubmit through lifecycle_event relevance refresh
/04.8Policy & kill switches
Two layers of guardrail. Kill switches sit above policy — they're operator-level pauses, evaluated first.
(scope, target) — global, project, tool, agent_type. 5s in-process cache. Soft-resume keeps the row.timeout(100ms) + circuitBreaker(5) fuse. Picomatch path globs. First match wins by priority ASC.policy_decisions with ON CONFLICT (idempotency_key) DO NOTHING. Async; the hook returns before the write.{ decision: 'allow', reason: 'policy_check_unavailable' }.Policy rules match on five axes — event type, tool name, path glob, agent type, and priority. Decisions are append-only. The default policy seed lives in packages/db/src/ensure-default-policy.ts.
Process topology.
plugin-first
The primary runtime is plugin-first: agents call Coodra's MCP server, lifecycle events enter through lifecycle_event, and Graphify runs as a separate managed MCP server beside Coodra.
/05.1Long-running processes
| Process | Port | Bind | DB | Started by |
|---|---|---|---|---|
mcp-server26 MCP tools · stdio + HTTP transport |
3100 |
127.0.0.1 |
SQLite (local) | coodra start · native plugins · hook-runner |
sync-daemonPush + pull · kill-switch puller |
none | n/a | SQLite + Postgres | coodra start · team mode only |
web-v2Next.js 15 · 56 page/route files |
3001 |
0.0.0.0 |
SQLite (server actions) | coodra start · skip with --no-web |
/05.2MCP lifecycle
Exactly what happens between an agent session starting and the final run evidence landing in Coodra.
lifecycle_event, opens or resumes a runs row, captures base_sha, and returns session context plus a pullable memory manifest.allow/ask/deny. Outbox writes policy_decisions.run_events row with phase, tool name, tool input, outcome.@coodra/lifecycle marks completion, captures run diff, writes fallback Context Pack, updates Work Packs, and can trigger structural Graphify refreshes on daemon transports.Data model.
36 Postgres tables
Drizzle dual-dialect schemas. Postgres mirrors SQLite for shared product tables and adds cloud-only migration/audit tables. The intentional column drift: context_packs.summary_embedding is vector(384) in Postgres and text in SQLite.
| Table | Purpose | Key constraints |
|---|---|---|
projects | Project registry. One durable row per repo slug and org scope. | slug UNIQUE · org_id |
runs · run_events · run_diffs | Agent session envelope, append-only tool activity, and end-of-run diff evidence. | UNIQUE (project_id, session_id) · diff PK = run_id |
context_packs · decisions · decision_edges | Long-lived memory: run summaries, architectural decisions, supersession, and file/Work Pack/code-graph impact edges. | UNIQUE context_packs.run_id · UNIQUE decisions.idempotency_key |
memory_access_events · memory_access_daily · memory_cohorts | Raw memory access telemetry, daily utilization rollups, and per-item pull-through cohorts. | Daily grain includes project_id, day, channel, site, memory_type, actor_user_id |
policies · policy_rules · policy_decisions | Active tool policies, ordered rule axes, and append-only PreToolUse audit rows. | UNIQUE policy_decisions.idempotency_key |
policy_versions · policy_exceptions · policy_grants | Policy change history, time-bound exceptions, and capability grants. | Project-scoped indexes for policy review and enforcement. |
controls · control_attestations | Imported governance controls and native advisory attestation state. | Control-catalog/project indexes. |
kill_switches | Polymorphic operator pauses for global, project, tool, or agent-type scopes. | idx (resumed_at, scope, target) |
features | Agent Recipe index rows derived from .coodra/recipes/<slug>/recipe.md. | UNIQUE (project_id, slug) |
wikis · wiki_pages | Deep Wiki structure and page bodies authored through wiki MCP tools. | Project/slug and wiki/page indexes. |
work_packs · work_pack_*_links · external_work_items | Issue-bound implementation packs, provider item mirrors, relationships, and links to decisions/context packs. | UNIQUE (project_id, slug) · link-pair uniqueness |
integration_connections · sync_events · pending_jobs | Provider sync configuration, local sync audit, and durable outbox queues. | Provider/project uniqueness · queue/status indexes |
audit_events · team_invites | General audit stream and HMAC-signed onboarding tokens. | Invite jti UNIQUE |
/06.1ER summary
projects ─1───n─ runs ─1───n─ run_events │ │ ├─0/1─ context_packs (UNIQUE run_id) │ │ ├─0/1─ run_diffs (PK run_id, CASCADE) │ │ └─0/n─ decisions (FK ON DELETE SET NULL) │ │ │ ├──1───n─ policies ─1───n─ policy_rules │ │ │ │ │ └─ policy_decisions.matched_rule_id │ │ │ ├──1───n─ work_packs ───n─ work_pack_decision_links │ │ └─n─ work_pack_context_pack_links │ │ │ └──1───n─ wikis ─1───n─ wiki_pages │ ├─ context_packs.project_id ├─ policy_decisions.project_id ├─ memory_access_events.project_id └─ features.project_id (Agent Recipes; UNIQUE project_id, slug) # Top-level (not FK'd to projects): kill_switches, team_invites, pending_jobs # Sentinel projects (seeded at every boot): '__global__' ensureGlobalProject() # events before a real project '__solo__' SOLO_ACTOR.orgId # solo-mode org # Cloud-only mirrors (Postgres): _migration_attempts ─1───n─ _migration_map # migrate resume + rollback knowledge_audit # cloud knowledge audit
/06.2What lives in ~/.coodra/
COODRA_SQLITE_PATH. Managed by coodra db backup / restorecontext_packs rows. Filename: {date}-{sanitised-runId}.md. Gitignored. Useful for grep + AI agent file-side reads.coodra/graphify/out/.coodra logs <service> --followlaunchctl print.init, login, team setup.coodra login. Consumed by the MCP server identity guard.coodra team setup output./06.3Sync & multi-tenancy
In team mode, the sync-daemon pushes outbox rows to cloud Postgres and pulls teammates' rows back into local SQLite — both directions on a 1-second tick.
['sync_to_cloud']. Every record_decision, save_context_pack, etc. enqueues a paired job that the daemon drains.runs · decisions · context_packs · run_events cloud → local via ON CONFLICT DO NOTHING. Skips __global__ + __solo__.Every shared table carries Clerk user attribution: created_by_user_id on runs, decisions, context_packs, policies; paused_by_user_id / resumed_by_user_id on kill_switches. Web-v2 enforces single-tenant via COODRA_EXPECTED_ORG_ID — a deployment refuses to boot without it.
CLI commands.
36 doctor checks
Every command is a thin shell over a typed core module. Pass --json almost anywhere for machine output. Pass --help for the up-to-date flag list.
/07.1Lifecycle commands
| Command | What it does | Key flags |
|---|---|---|
coodra install | Install or repair machine-level runtime state under ~/.coodra/. | --dry-run · --json |
coodra init | Bootstrap a project. Writes project-local .coodra/ config, manifest, recipes, graphify, wiki, and Work Pack directories. | --project-slug · --solo · --team · --force · --dry-run |
coodra start | Bring up local runtime services. Picks launchd / fallback manager based on platform. | --no-mcp · --no-sync · --no-web · --foreground · --tunnel |
coodra stop | Tear down daemons. --uninstall removes launchd plists. | --service · --uninstall |
coodra status | Health snapshot of each daemon + DB. | --json |
coodra metrics · coodra roi | Print ROI / value KPIs for captured knowledge, reuse, governance, and modeled net value. | --project · --json |
coodra doctor | 12 essential checks by default. Add --full for all 36. | --full · --fix · --timeout-ms · --json |
coodra agents | Detect installed IDEs and confirm native plugin MCP, hook, and skill wiring. | --json |
coodra agent | Install, repair, remove, or inspect one native agent plugin. | add · repair · remove · status · --dry-run · --json |
coodra files | Inspect or clean files Coodra generated for this project from .coodra/manifest.json. | status · clean · --force · --dry-run · --json |
coodra logs <svc> | Tail ~/.coodra/logs/<svc>.log across platforms. | --follow · --lines · --since |
coodra upgrade | Compares installed version against npm view. Restarts daemons unless --no-restart. | --check-only · --no-restart |
coodra uninstall | Removes the CLI. --purge deletes ~/.coodra/. | --purge · --dry-run |
/07.2Data & memory commands
| Command | Subcommands | Use case |
|---|---|---|
coodra project | list · show · reset · promote · demote | Manage which projects Coodra tracks; promote a transient project to active. |
coodra run | list · show · cancel · capabilities | Inspect runs, cancel non-terminal sessions, or adjust capability context. |
coodra work | status · show · import | Manage local Work Pack artifacts under .coodra/work-packs/. |
coodra recipe | add · list · show · edit · index · remove · migrate | Manage Agent Recipes under .coodra/recipes/. |
coodra wiki | build · status · list · open · clean · ask | Generate, inspect, open, clean, or query the project Deep Wiki. |
coodra graphify | build · status · open · clean | Build and inspect Coodra-managed Graphify code graph artifacts. |
coodra policy | list · show · add · enable · disable · sync · catalog · workflow | Author policy rules, sync policy projections, import control catalogs, and render workflow policy. |
coodra template | list · install <source> | Install reusable Coodra templates. |
coodra export <runId> | — | Export a run as markdown · json · html · slack. With --include-audit for the full policy trail. |
coodra db | migrate · backup · restore | Drizzle migrations + snapshot management. |
/07.3Team & auth commands
| Command | Subcommands | What it does |
|---|---|---|
coodra login | — | Browser handoff to Clerk hosted sign-in. Captures JWT via loopback listener. |
coodra logout | — | Clears ~/.coodra/clerk-token.json. --force skips confirmation. |
coodra invite <email> | — | Mints an HMAC-signed invite URL + writes team_invites row in cloud. |
coodra org | status · switch <slug> | Inspect or switch the active Clerk org for this machine. |
coodra team | setup · init · install · join · leave · migrate · login · logout | Full team lifecycle. setup provisions Clerk + DB; install redeems an invite on a second laptop. |
coodra pause | — | Trip a kill switch. Scope = global · project · tool · agent_type; mode = hard · soft. |
coodra resume | — | Un-pause. --id, --all, or scope+target. |
coodra cloud-migrate | — | One-shot SQLite → Postgres migration. Tracked in _migration_attempts with surgical rollback. |
coodra ui | — | Foreground Ink TUI for live triage. |
The coodra team namespace re-exports login and logout as aliases — both coodra login and coodra team login work. The doctor runs 12 essential checks by default; --full runs the 36-check registry under packages/cli/src/doctor/checks/.
MCP tools — all 26.
tools/index.ts
Order matches registration order in apps/mcp-server/src/tools/index.ts. Graphify is not folded into this list: native plugins wire it as a separate managed MCP server pointed at .coodra/graphify/out/graph.json.
| # | Tool | Kind | What it does |
|---|---|---|---|
01 | ping | read | Health check. Returns { pong, serverTime, sessionId, idempotencyKey }. |
02 | get_run_id | write | Idempotently opens or returns a runs row for this (project, session). |
03 | lifecycle_event | write | Canonical native-plugin event path for SessionStart, prompts, tool use, compaction, and SessionEnd finalization. |
04 | save_context_pack | write | Persists an agent-authored Context Pack, including Work Pack links and idempotent automatic fallback upgrades. |
05 | search_packs_nl | read | Local keyword search over Context Packs. The agent ranks and synthesizes from returned evidence. |
06 | record_decision | write | Idempotent durable decision write with file, graph-node, supersession, and Work Pack targeting. |
07 | query_run_history | read | Recent runs, attached Context Packs, issue/PR references, and chronological session history. |
08 | check_policy | write | Evaluates policy rules and kill switches, writes audit rows, and defers to native permissions when Coodra has no rule. |
09 | query_decisions | read | Cross-session decision history with Work Pack and relationship-aware filters. |
10 | query_decisions_by_file | read | File-impact lookup using decision targets plus Graphify-backed neighborhood matching when available. |
11 | list_context_packs | read | Paginated, newest-first Context Pack index with Work Pack-aware filters. |
12 | read_context_pack | read | Full Context Pack body, metadata, linked decisions, and memory-access instrumentation. |
13 | list_recipes | read | Lightweight Agent Recipe index from .coodra/recipes/; agents choose what to pull. |
14 | get_recipe | read | Pull-on-trigger recipe body plus supporting file list. Does not inline every file blindly. |
15 | get_recipe_file | read | Reads one allowed supporting recipe file with path-escape and size guards. |
16 | query_run_diff | read | Unified diff plus per-file numstat. Soft failures include analysis_pending, no_base_sha, and git_diff_failed. |
17 | link_run_to_issue | write | Binds the run to an issue key locally. It does not call Jira. |
18 | link_run_to_pr | write | Binds the run to a PR/MR reference locally. Provider reads/writes stay in provider MCPs. |
19 | prepare_jira_comment | read | Assembles a markdown Jira comment from run evidence. Posts nothing itself. |
20 | wiki_save_structure | write | Deep Wiki pass 1: persists the planned structure and pending page skeletons. |
21 | wiki_save_page | write | Deep Wiki pass 2: authors one page's Markdown with Mermaid validation. |
22 | wiki_status | read | Deep Wiki progress and resume status. |
23 | wiki_ask | read | Cloud-facing/wiki-store retrieval counterpart to local coodra wiki ask. |
24 | work_pack_upsert | write | Creates or refreshes a local issue-bound Work Pack and external-work-item map. |
25 | work_pack_update | write | Partial local Work Pack editorial patch; marks linked provider items local-ahead for later sync. |
26 | work_pack_status | read | Reads current local Work Pack state so agents can resume issue-bound work safely. |
/08.1Lifecycle events
Wired by native Coodra plugins and normalized through lifecycle_event. Invalid or unsupported payloads fail softly so the agent can keep working.
| Event | Handler | Side effects |
|---|---|---|
SessionStart | lifecycle_event | Opens / resumes run · captures base_sha · returns manifest and recent context |
UserPromptSubmit | lifecycle_event | Bounded relevance refresh using exact, FTS/BM25, Work Pack, file, and graph signals |
PreToolUse | lifecycle_event | Kill-switch · policy check · native-permission defer · audit rows |
PostToolUse | lifecycle_event | Inserts run_events row and records external MCP activity |
PostCompact | lifecycle_event | Bumps baseline generation; the next prompt-time refresh can surface post-compaction context |
Stop · SessionEnd | @coodra/lifecycle | Mark completed · run diff · auto Context Pack · Work Pack update · graph refresh triggers |
Configuration.
~/.coodra/.env
Defaults are sensible. You usually only need to touch COODRA_MODE, DATABASE_URL, and the Clerk pair. Everything else has a documented fallback.
| Variable | Default | Purpose |
|---|---|---|
COODRA_MODE | solo | solo | team. Controls auth strategy and whether sync-daemon boots. |
DATABASE_URL | none | Postgres connection string. Required in team mode. |
CLERK_SECRET_KEY | sk_test_replace_me | Sentinel value triggers solo bypass; real key required in team mode. |
CLERK_PUBLISHABLE_KEY | pk_test_replace_me | Same sentinel pattern. |
CLERK_JWT_ISSUER | none | Tenant JWT issuer URL for Clerk JWT verification. |
LOCAL_HOOK_SECRET | none | Shared secret for X-Local-Hook-Secret header. Required outside solo bypass. |
COODRA_EXPECTED_ORG_ID | none | Single-tenant pin. Web-v2 refuses to boot without it in team mode. |
COODRA_INVITE_HMAC_SECRET | none | HMAC signing key for invite tokens. |
| Variable | Default | Purpose |
|---|---|---|
MCP_SERVER_PORT | 3100 | HTTP transport port. |
MCP_SERVER_HOST | 127.0.0.1 | HTTP bind. |
MCP_SERVER_TRANSPORT | both | stdio · http · both. |
COODRA_WEB_PORT | 3001 | Service-manager override for the web-v2 port. |
PORT | 3001 | Port passed to the Next.js standalone process. |
COODRA_SQLITE_PATH | ~/.coodra/data.db | Override the SQLite file location. |
COODRA_HOME | ~/.coodra | Override the whole data dir. Used in compose. |
COODRA_CONTEXT_PACKS_ROOT | ~/.coodra/packs | Where Context Pack markdown mirrors are materialised. |
GRAPHIFY_OUT | .coodra/graphify/out | Managed project-local Graphify output for graph.json, GRAPH_REPORT.md, and graph.html. |
COODRA_SYNC_TICK_MS | 1000 | Outbox + puller cadence. |
COODRA_SYNC_LEASE_MS | 30000 | Worker lease duration. |
COODRA_WEB_URL | http://localhost:3001 | Used by CLI login for browser handoff. |
LOG_LEVEL | info | pino level. |
The runtime source of truth is the generated machine env under ~/.coodra/.env plus the service manager. For the web app, Coodra resolves COODRA_WEB_PORT and starts Next.js with PORT; the documented default is 3001.
Self-host the team stack.
docker compose
Team mode runs on your own Postgres. deploy/compose.yaml ships the database, migrator, MCP server, and sync-daemon; native agent plugins still keep the hot path local-first.
| Service | Image | Depends on | Port |
|---|---|---|---|
postgres | pgvector/pgvector:pg16 | — | ${POSTGRES_PORT:-5432}:5432 |
cloud-migrate | deploy/Dockerfile.cloud-migrate | postgres healthy | one-shot |
mcp-server | deploy/Dockerfile.mcp-server | cloud-migrate done | ${MCP_SERVER_PORT:-3100}:3100 |
sync-daemon | deploy/Dockerfile.sync-daemon | cloud-migrate done | — |
# 1. Clone & build images locally git clone https://github.com/your-org/coodra cd coodra cp deploy/.env.example deploy/.env # Fill in: DATABASE_URL, CLERK_*, COODRA_EXPECTED_ORG_ID, LOCAL_HOOK_SECRET # 2. Bring it up docker compose -f deploy/compose.yaml up -d # 3. Point laptops at it # Each developer runs: coodra team install <invite-url> coodra start
/10.1Auth chain & RBAC
The MCP server, web app, and sync daemon share the same identity rules. First match wins.
CLERK_SECRET_KEY === 'sk_test_replace_me' OR COODRA_MODE === 'solo'. Attaches SOLO_IDENTITY.LOCAL_HOOK_SECRET. Attaches { source: 'local-plugin' }. Used by generated native plugin callers.Authorization: Bearer <token> → verifyClerkJwt. Required in team mode.401 { ok: false, error: 'unauthorized' }. No further processing.RBAC has three roles — viewer, member, admin — parsed from Clerk's org:admin/org:viewer (anything else → member). The shared assertCanEdit helper enforces admin-OR-(member + ownership); viewer never edits. Knowledge mutations have their own gate, assertCanAuthorKnowledge.
When things break.
coodra doctor
Every problem in Coodra leaves a trail in ~/.coodra/logs/, in policy_decisions, or in the doctor output. Start there.
Agent never sees Coodra context
SessionStart is not reaching Coodra's MCP lifecycle path. Confirm the native plugin is installed and healthy:
coodra agents reports the target agent fully wired?coodra status shows mcp-server healthy on 3100?tail ~/.coodra/logs/mcp-server.log shows native_plugin_lifecycle_event?
Policy denies something it shouldn't
Look up the audit row by idempotency_key = pd:{sessionId}:{toolUseId}:{toolName}:{eventType}. The matched_rule_id tells you exactly which rule fired.
If reason: 'policy_check_unavailable' — the engine fail-opened, never a deny. Check the breaker state in logs.
Sync-daemon can't connect
Symptoms: queue depth climbs in doctor (check 26), nothing appears on teammates' laptops.
Confirm DATABASE_URL reachable from this machine. coodra doctor --full runs cloud-reachability check 24. Look at ~/.coodra/logs/sync-daemon.log for connection errors. Restart with coodra stop --service sync-daemon && coodra start.
web-v2 refuses to boot
Almost always COODRA_EXPECTED_ORG_ID missing in team mode. The middleware refuses to start without it — by design, to prevent any Clerk-authed account in this deployment's Clerk app from reading your team's data.
If set but you land on /forbidden?reason=org_mismatch — your Clerk session is in a different org. Sign out and back in, or run coodra org switch <slug>.
SQLite locked or corrupted
Stop all daemons, snapshot the file, repair:
coodra stop && coodra db backupsqlite3 ~/.coodra/data.db "PRAGMA integrity_check;"coodra db restore <snapshot> if needed.
Open an issue with the doctor JSON + log tail.
No run_diff for completed run
query_run_diff returns analysis_pending, no_base_sha, or git_diff_failed. Common cause: the agent operated outside a git repo, or base_sha was unreachable when SessionEnd fired.
Re-trigger with coodra run show <id> --regen-diff after checking out the right commit.
Graphify or wiki context is stale
Check the managed graph first: coodra graphify status. Current managed output lives under .coodra/graphify/out/, not root-level graphify-out/.
Rebuild structurally without LLM spend via coodra graphify build --no-llm, then refresh the wiki with coodra wiki build and the two-pass wiki save flow.
Build & test.
Biome
A monorepo with Turbo for orchestration, Biome for lint, and Vitest for everything from unit to e2e. CI currently runs six jobs: Linux verification/integration/e2e, cross-platform hook-adapter smoke, and two Windows smoke lanes.
/12.1Install & build
nvm use # 22.16.0 per .nvmrc corepack enable # pnpm 10.33.0 pnpm install pnpm rebuild # native modules: better-sqlite3 + sqlite-vec # Day-to-day: pnpm build # turbo run build pnpm typecheck # turbo run typecheck pnpm lint # biome check . pnpm lint:fix pnpm --filter @coodra/cli build # tsc + esbuild bundle
/12.2Test tiers
| Command | Runner | Needs |
|---|---|---|
pnpm test:unit | Vitest | nothing — runs entirely in-process |
pnpm test:integration | Vitest | Postgres + Redis service containers |
pnpm test:e2e | Vitest · testcontainers | Docker · fileParallelism: false · 120s hook timeout |
pnpm test:functional | bash | __tests__/functional/run-all.sh · 13 shell scripts |
/12.3CI jobs
pnpm test:integration against postgres + redis containers.scripts/hook-adapters/__tests__/smoke.sh on both ubuntu and macos.coodra init, MCP daemon start, /healthz, status, stop.Contribution guide.
PRs welcome
Coodra is built linearly, start to finish, as a production-grade system. Contributions are welcome — and held to the same bar.
/13.1Core principles (read first)
- Ship complete or don't merge. No placeholder surfaces, no mocked endpoints to make the UI render, no "we'll come back to this later." Every module ships end-to-end.
- Local-first remains the default. Any new feature must work offline. If it requires the cloud, it must degrade gracefully back to local + offer a clear path to enable.
- Fail-open in the hot path. Hooks return ALLOW on engine fault. Hot-path code paths catch and log async — never throw at the agent.
- Append-only audit. Don't add an UPDATE on
policy_decisions,decisions, orrun_events. Resurface via a new row with the same idempotency key. - Document divergence. If your PR contradicts an ADR, README claim, or essentials doc — update the source-of-truth doc in the same commit.
/13.2Workflow
Pick or file the issue.
Look for good-first-issue or help-wanted. For non-trivial work, open an issue first so we can align on shape before code lands.
Branch off main.
Naming: feat/<short-slug>, fix/<short-slug>, chore/<short-slug>, docs/<short-slug>. Squash-merge expected at the end.
Write the tests.
Unit tests live next to source as *.test.ts. New tables/migrations require a schema-parity test. New MCP tools require a happy-path + soft-failure test at minimum.
Update docs.
If you touched the public surface — env vars, CLI flags, MCP tools, lifecycle payloads — update the relevant table in this docs page in the same PR. If the change alters durable architecture, record or supersede the decision through record_decision.
Pre-commit & push.
The .githooks/pre-commit hook runs the migration-lock check automatically when packages/db/* is staged. Before pushing: pnpm lint:fix && pnpm typecheck && pnpm test:unit.
Open the PR.
Title in conventional-commit form (feat:, fix:, chore:, docs:). Body should state: what changed, why, how to verify locally, and any decision/docs it touches. CI will run verify, integration, hook-adapter-smoke, windows-core-smoke, windows-native-full-smoke, and e2e.
/13.3Code conventions
Biome is the source of truth. Rules ratcheted up over time — these are the ones most likely to flag a PR.
const. Reach for let only when re-assigning.import type { Foo } from '...'any is a code smell. Prefer unknown + narrowing, or a real type.x!. Prefer explicit guard + early return.apps/mcp-server/src/lib/**. If your import cycles, refactor the shared bits up a layer.logger (pino-backed). console.log only for CLI user output./13.4Where things live
Coodra/ ├── apps/ │ ├── mcp-server/ # 26 MCP tools · stdio + HTTP transport │ ├── sync-daemon/ # push + pull workers · team mode only │ └── web-v2/ # Next.js 15 dashboard · 56 page/route files ├── packages/ │ ├── cli/ # @coodra/cli npm package · only published artefact │ ├── db/ # Drizzle dual-dialect schema + migrations │ ├── lifecycle/ # shared SessionStart/End, diff, graph refresh, memory workers │ ├── policy/ # Cockatiel-wrapped evaluator + types │ └── shared/ # auth, roles, recipes, wiki, logger, env ├── .coodra/ │ ├── recipes/ # project-local Agent Recipes │ ├── graphify/out/ # managed graph.json, GRAPH_REPORT.md, graph.html │ ├── wiki/ # Deep Wiki grounding + Markdown mirrors │ └── work-packs/ # local issue-bound implementation artifacts ├── docs/ │ ├── index.html # public docs source of truth │ ├── brand/ # brand kit │ ├── deploy/ # self-hosting │ ├── DEVELOPMENT.md │ ├── PRD-memory-utilization.md │ ├── team-*.md # team setup/onboarding manuals │ └── value-story.md ├── deploy/ # 4 Dockerfiles + compose.yaml ├── scripts/ # maintenance + publishing helpers ├── __tests__/ # e2e + functional + manual harnesses └── .github/workflows/ # ci.yml (4 jobs)
Hot tip: when in doubt about whether a behaviour is correct, start from .coodra/wiki/coodra/md/ and verify targeted source files. The wiki is grounded by Graphify, but source files remain the final authority.
Decisions of record.
curated highlights
Architectural decisions are durable Coodra memory, written through record_decision and superseded through decision edges rather than maintained as standalone ADR files. The cards below are curated highlights of the most load-bearing decisions.
Append-only audit
Policy decisions, decisions records, and kill-switch state changes are append-only. Idempotency keys gate writes. Resumes don't delete — they stamp resumed_at on the existing row. Recovery and forensics become trivial.
Graphify is a reader
Coodra consumes Graphify as a separate managed MCP server pointed at .coodra/graphify/out/graph.json. It installs and refreshes the structural graph, but does not fold graph queries into Coodra's own tool list or mint Work Packs from graph communities.
Agent-driven NL assembly
Natural-language memory assembly stays inside the agent loop. Coodra exposes retrieval tools (search_packs_nl, list_context_packs, read_context_pack); the agent ranks, synthesizes, and cites returned evidence.
Run Diff in process
Shared lifecycle finalization captures base_sha → head_sha, stores deterministic git diff evidence, and links the result back to the run without depending on a separate analysis service.
Tier 2.5 RBAC
Three roles — viewer, member, admin — parsed from Clerk org roles. assertCanEdit centralises the gate; ownership unlocks member-level edits on rows they created. Knowledge mutations are their own narrower gate.
Lifecycle is MCP-native
Native plugins route session events to lifecycle_event. Shared @coodra/lifecycle owns finalization, diff capture, auto Context Packs, Work Pack updates, and graph refresh.
External dependencies.
libraries
Only the libraries that shape behaviour. If something in the runtime surprises you, the explanation is usually here.
| Package | Where | What it gives us |
|---|---|---|
@modelcontextprotocol/sdk | mcp-server | MCP protocol server with stdio + HTTP transports. |
hono · @hono/node-server | mcp-server HTTP | Tiny HTTP framework. Validates payloads via @hono/zod-validator. |
drizzle-orm | packages/db | Dual-dialect schemas + migrations. |
better-sqlite3 | everywhere local | Synchronous SQLite driver. Native module — rebuild after install. |
sqlite-vec | packages/db extension | Vector search for the context_packs_vec virtual table. |
postgres | web-v2 · packages/db | Postgres driver. |
next 15 · react 19 | web-v2 | Server framework + UI. |
@clerk/nextjs · @clerk/backend | web-v2 · cli | Identity, org membership, JWT. |
cockatiel | packages/policy | Circuit breaker + timeout fuse around DB reads. |
picomatch | policy · mcp-server | Path glob matching for policy rules. |
zod | everywhere | Schema validation at every untrusted boundary. |
commander · execa · ink | cli | CLI parser, subprocess management, React-for-terminal TUI. |
vitest · testcontainers | tests | Unit + integration + e2e runners. |
biome | repo | Lint + format. |
turbo | repo | Workspace orchestration. |
Not present, in case you're checking: no @anthropic-ai/sdk, no openai, no @google/generative-ai, no web-tree-sitter, no bullmq, no @octokit/*, no JIRA SDK. The MCP server is the only LLM-facing surface — and it talks to the agent, not the model.
Frequently asked.
linked deep dives
.coodra/graphify/out/ artifact path; Graphify owns the graph engine. See the repository NOTICE file for attribution.mcp-server.~/.coodra/data.db. In team mode: also in the Postgres you operate. Coodra ships no hosted backend.scripts/wipe-disposable-projects.sh or scripts/wipe-all.sh. Append-only doesn't mean undeletable; it means we don't update rows in place.COODRA_WEB_PORT for Coodra's service manager, or PORT when running the Next.js standalone process directly. Default is 3001.coodra start --no-web skips it. coodra ui gives you a TUI alternative.Glossary.
definitions
userId, orgId, role, source. Solo bypass attaches SOLO_ACTOR.--full.packages/db/src/schema/{sqlite,postgres}.ts..coodra/graphify/out/, queried through a separate managed Graphify MCP server.lifecycle_event, the canonical Coodra MCP tool native plugins call for SessionStart, prompt, tool, compaction, and SessionEnd events.coodra ui.~/Library/LaunchAgents/ during start.pending_jobs table) used for async writes and cloud sync.policy_rules.match_path_glob.runs.base_sha and runs.head_sha, captured at session end.__global__ for events before a real project exists; __solo__ for the solo-mode org.