Docs Features

Architecture

The core design principle: dumb server, smart agents.

The server has zero AI. It makes no LLM calls, holds no API keys, and forms no opinions about your codebase. It’s a pure CRUD coordination layer with atomic task pickup. All planning, decomposition, and reasoning happens in the agents.

System layers

┌─────────────────────────────────────────────────────┐
│  Agents (any machine, any tool)                     │
│  Claude Code · Codex · Cursor · Copilot · scripts  │
└────────────────────┬────────────────────────────────┘
                     │ cpk CLI (~250 tokens/call)

┌─────────────────────────────────────────────────────┐
│  codepakt server (Hono + SQLite)                    │
│  ├── REST API   /api/*  (port 41920)               │
│  └── Dashboard  /       (same port)                │
└────────────────────┬────────────────────────────────┘


┌─────────────────────────────────────────────────────┐
│  <project>/.codepakt/data.db  (SQLite, WAL mode)    │
└─────────────────────────────────────────────────────┘

Technology stack

LayerChoiceReason
HTTP frameworkHonoLightweight, fast, TypeScript-native
Databasebetter-sqlite3Synchronous API, WAL mode, zero config, embeds in npm package
CLICommander.jsMature, minimal, well-typed
ValidationZodRuntime type safety at API boundaries
Query layerTyped functions, no ORMSQL is readable; ORMs add abstraction without benefit here

Single npm package

Everything ships in one package: codepakt.

  • CLI binary (cpk)
  • HTTP server (Hono)
  • SQLite driver (better-sqlite3)
  • Dashboard HTML/JS (served from the same process)

No docker. No separate database service. No config file beyond what cpk init creates. Install and run.

Atomic task pickup

The central concurrency problem: two agents calling cpk task pickup simultaneously must not receive the same task.

Solution: BEGIN IMMEDIATE transaction in SQLite.

BEGIN IMMEDIATE;

SELECT id FROM tasks
WHERE status = 'open'
  AND deps_met = 1
ORDER BY CASE priority WHEN 'P0' THEN 0 WHEN 'P1' THEN 1 ELSE 2 END, created_at ASC
LIMIT 1;

UPDATE tasks SET status = 'in-progress', assignee = :agent WHERE id = :task_id;

COMMIT;

BEGIN IMMEDIATE acquires a write lock at transaction start, before the SELECT. Any concurrent transaction attempting to start waits. The first writer wins; others retry. This is SQLite’s built-in serialization — no application-level locking needed.

Audit trail

Every mutation (task creation, status change, pickup, completion) writes to an events table:

events
  id          TEXT PRIMARY KEY
  type        TEXT  -- 'task.created', 'task.pickup', 'task.done', etc.
  entity_id   TEXT  -- task ID, agent name, doc ID
  actor       TEXT  -- agent name or 'system'
  payload     TEXT  -- JSON snapshot
  created_at  TEXT

This is an append-only log. Nothing is ever deleted from events. It’s the source of truth for “what happened and when.”

Dependency resolution

When a task reaches review (agent calls cpk task done), the server runs a dependency check:

  1. Find all tasks whose depends_on includes the completed task’s ID
  2. For each, check whether all other dependencies are in review or done
  3. If yes, set deps_met = true and transition from backlog to open

Dependencies resolve on review, not on done. This means the pipeline keeps moving without waiting for human approval. The check runs synchronously inside the same transaction. No background jobs, no eventual consistency.

Dashboard delivery

The dashboard is a static HTML/JS application bundled into the npm package. The server serves it from the root path (/). The JS makes fetch calls to /api/* — the same origin, no CORS needed.

In v0.1 the dashboard is embedded in the server binary at build time. There is no separate deployment step.

What’s not in the server

  • No authentication or authorization (v0.1, single-user)
  • No LLM calls
  • No file watching or filesystem access
  • No webhooks or external integrations
  • No background job queue
  • No Postgres (planned for v0.2)

Keeping the server simple is a deliberate choice. Complexity lives in the agents, where it can be reasoned about, tested, and replaced independently.