This flake packages the ledger suite: an MCP server plus terminal and browser
frontends. The coding-agent packages, settings, and yolo sandbox live in the
separate ponygirls flake. CQ imports it
and adds the ledger MCP server and CQ prompt assets through
homeManagerModules.dev-llm.
flake.nix # ledger outputs and ponygirls integration
nix/
pkg/
cq-ledgers/ # the Bun/TypeScript ledger workspace (run `bun` here)
packages/{ledger,ledger-live,ledger-mcp,ledger-tui,ledger-web}
package.json bun.lock tsconfig*.json …
examples/sample-ledger/ # ready-made dataset
cq-assets/ # ledger's contributed LLM assets (assets.nix, commands/, agents/)
pi-extensions/ # CQ-specific Pi dispatch and status extensions
lib/ # CQ prompt and integration checks
docs/ # operator documentation and historical research
A ledger is an ordered set of milestones; each milestone holds typed items
(tasks, defects, hypotheses, questions, decisions, goals, …). Everything is
stored in an out-of-tree XDG SQLite primary, or accessed through a remote
cq serve service. Optional Markdown backups are diffable and git-friendly.
Milestones form a dependency DAG via their
dependsOn / blockedBy references.
| Package | What it is |
|---|---|
@cq/ledger |
The library: SQLite storage, private PostgreSQL service storage, Markdown backup codecs, schema/registry, FTS index, and MCP tool definitions. |
@cq/ledger-mcp |
Standalone MCP server exposing the 68-tool management ledger surface over stdio or Streamable HTTP. |
@cq/ledger-tui |
Ink terminal UI — a pure MCP client. Runs against a remote cq mcp --http (--mcp-url) or, by default, with the MCP server embedded in-process (--cwd). |
@cq/ledger-web |
Browser explorer/editor + milestone DAG view — a pure MCP client served as a static bundle. Reverse-proxies to a remote cq mcp (--mcp-url) or, by default, embeds the MCP server in-process (--cwd). |
The two frontends never read the ledger files directly — they always speak the
MCP protocol. Embedded mode does not change that invariant: it merely
co-locates the MCP server in the frontend's own process (an in-memory
transport for the TUI; a co-hosted /mcp + /ws for the web server), so a
single command needs no separately-running server.
enumerate_ledgers, fetch_ledger, fetch_ledger_archive, fetch_item,
update_item, create_item, create_ledger, search_items, fts_search,
archive_milestone, archive_terminal_items, execute_finalize,
list_milestone_items, snapshot, workset, derive_predicates,
materialize_operator_action,
acknowledge_operator_action, record_operator_action_evidence,
revise_operator_action, complete_operator_action, reopen_item,
unarchive_item, read_log, get_config, get_usage_stats, prepare_dispatch,
fetch_dispatch_input, store_result, confirm_dispatch_completion,
abort_dispatch, fetch_dispatch_result, start_dispatch, fetch_prompt, list_projects,
mint_plan_claim_authority, claim_plan, publish_plan_draft,
release_plan_claim, finalize_plan, worktree_manage, git_commit,
git_resolve_continue, get_cohort_completion_status, get_cohort_status.
The seven attestation dispatch-lifecycle tools require both a supported durable backend
(xdg, or PostgreSQL in its supported server construction) and an
attested prompt surface. A server that cannot satisfy those prerequisites
omits those seven names; broker availability similarly controls the two git
dispatch-lifecycle tools. Omitting all nine leaves the canonical 34-tool
ordinary non-dispatch surface instead of advertising handlers that can only fail.
The 68-tool management ledger surface uses a single breaking wire-response contract:
item-bearing reads require an explicit compact/complement/full projection and eligible
mutations return acknowledgements rather than full entities. See the
@cq/ledger-mcp response matrix
for every tool, retained field, pagination rule, and schema-checked example.
The persisted workset stores canonical roots and closes them over the active ownership,
dependency, milestone, and plan graph. Management sessions may replace those roots;
ordinary sessions may only get the configured graph or fetch a non-mutating preview.
That closed graph bounds guarded mutations and external effects. UI visibility never
expands workset authority. In the workset manager, ideas:I25 and goals:G159 remain
visually excluded unless the configured roots close over them.
Anthropic SDK in-process hosts use createLedgerSdkMcpServer from
@cq/ledger. It retains Zod handler validation while publishing the same
compact tools/list definitions as stdio. createLedgerMcpTools remains the
compatibility object factory for direct invocation and composition.
Embedded (one command, no separate server) — the frontend runs the MCP server in-process against a project's configured storage (XDG by default):
# Terminal UI, embedded:
nix run .#cq -- tui --cwd /abs/path/to/ledger-root
# Browser UI, embedded (serves a static bundle; open the printed URL):
nix run .#cq -- web --cwd /abs/path/to/ledger-root --port 5180The embedded root resolves as --cwd > $LEDGER_ROOT > the process CWD.
Remote (shared server) — run one cq mcp --http and point the
frontends at it (e.g. several UIs against one ledger, or a remote host):
# 1. Start the MCP server over HTTP against a ledger root.
nix run .#cq -- mcp --cwd /abs/path/to/ledger-root --http 7777
# 2a. Terminal UI:
nix run .#cq -- tui --mcp-url http://127.0.0.1:7777/mcp
# 2b. Browser UI:
nix run .#cq -- web --port 5180 --mcp-url http://127.0.0.1:7777/mcpcq mcp also speaks stdio for clients that spawn it as a child
(Claude Code, Codex, …): cq mcp --cwd /abs/path (no --http).
A ready-made dataset lives in
nix/pkg/cq-ledgers/examples/sample-ledger
— import its portable dump into an isolated XDG project before opening the UIs.
See its README for the commands.
nixosModules.cq-server runs the cq serve multi-tenant hub against a native,
tuned PostgreSQL on the same host (no containers). Minimal config:
imports = [ inputs.cq.nixosModules.cq-server ];
services.cq-server.enable = true; # binds 127.0.0.1:5190, provisions local PostgresSet host / port to change the bind, tokenFile for a bearer token
(required for a non-loopback bind; injected via CQ_SERVE_TOKEN, not a
ps-visible flag), an admin token for project-admin MCP, and
postgres.tune.{totalMemoryMB,maxConnections,ssd} to size the database.
Checkouts never select backend = "postgres"; they use backend = "remote"
and CQ_LEDGER_REMOTE_TOKEN. See
docs/drafts/20260819-2230-g81-remote-owner.md
for the operator commands.
A checkout selects backend = "xdg" (default) or backend = "remote" in
cq.toml. XDG primary state is keyed by project identity, not by checkout path:
$XDG_STATE_HOME/cq/projects/<projectKey>/
ledger.db # primary ledger state
logs/ # out-of-tree session/raw-log artifacts
When XDG_STATE_HOME is unset, the base is ~/.local/state. Backups default
to none; in-tree exports a .cq/ Markdown dump and orphan-branch exports
the same portable layout to the configured Git branch. cq restore imports
a portable dump. Git plumbing remains supported for backups and managed Git
effects; it is not a primary ledger backend. Remote checkouts use
serverUrl and CQ_LEDGER_REMOTE_TOKEN; PostgreSQL stays private to cq serve.
homeManagerModules.dev-llm composes the ponygirls Home Manager module with
CQ's ledger package and prompt assets. The ponygirls module also works on its
own, without CQ. The composed module sets up Claude Code, Codex, Pi, the
platform yolo sandbox, and a shared programs.mcp registry (codegraph and
ledger).
Any local-model (ollama) provider config is deliberately left to the consumer.
For a complete new-machine procedure, including prerequisites, activation, authentication, project initialization, hooks, skills, and the macOS Seatbelt sandbox, see Install the cq harness on a new Mac with Home Manager.
# flake.nix
inputs.cq.url = "github:7mind/cq";
# home-manager configuration
imports = [ inputs.cq.homeManagerModules.dev-llm ];
smind.hm.dev.llm.enable = true;For the agents and sandbox without CQ, import
inputs.ponygirls.homeManagerModules.dev-llm from
github:7mind/ponygirls instead. Both modules use the same
smind.hm.dev.llm option namespace.
Host/hardware facts the module cannot infer are surfaced as plain options the consumer wires from its own system config:
| Option | Purpose |
|---|---|
smind.hm.dev.llm.yolo.promptExtensions |
Add tagged agent prompt fragments; works on Linux and macOS. |
smind.hm.dev.llm.yolo.{extraReadOnlyPaths,extraReadWritePaths,extraDevicePaths} |
Linux bubblewrap binds and device passthrough. |
smind.hm.dev.llm.yolo.{packages,sessionVariables,secretSessionVariables,hooks} |
Sandbox packages, environment, secret files, and pre-start hooks on Linux and macOS. |
smind.hm.dev.llm.podman.{socketPath,socketUri} |
Linux rootless-Podman socket for container access. |
smind.hm.dev.llm.llmSshKeyPath |
Linux SSH-key bind plus an agent prompt fragment. |
smind.hm.dev.llm.pi.mcpDirectTools |
Expose selected MCP servers directly in Pi instead of only through its proxy. |
smind.hm.dev.llm.{memorySections,assetBundles} |
Append memory text or asset bundles. |
smind.hm.dev.llm.extraSkills |
Add user-defined skills (name → SKILL.md body) to every skill-aware agent; wins on name collision. |
Other modules can append their own assetBundles (same shape as
cq.llmAssets); the merged result is exposed read-only at
smind.hm.dev.llm.merged.{skills,commands,agents,memoryText} for sibling
modules to reuse.
The harness building blocks are exposed by ponygirls as individual packages —
packages.<system>.{claude-code,codex,pi-coding-agent,llm-skills,llm-contexts}
plus Linux yolo/reattach-llm or macOS yolo-darwin — so they can be built
or consumed directly.
The harness gives the agent a planning loop backed by the ledger, and exposes
it through a tiny command surface — you mostly only ever type four things:
/cq:plan, /cq:investigate, /cq:plan:follow-up, and /cq:advance. The
agent does the rest; the ledger is where you and it meet.
A typical run:
- Stand up the sandbox. Launch through
yolo(bubblewrap on Linux, Seatbelt on macOS) so the agent runs with project-scoped filesystem access and without per-action permission prompts. - Install the assets. Bring in the MCP servers and the prompt/skill/command
bundles — via the
homeManagerModules.dev-llmmodule this is one import; that is what makes the/cq:*commands and theledgerMCP server available. - Kick off the work. Start the agent and give it one of:
/cq:plan <task description>— start planning a new piece of work, or/cq:investigate <defect description>— start root-causing a defect.
- Answer its questions. The agent stops and files clarifying questions into
the ledger. Open a frontend (
cq weborcq tui) and answer them there — your answers feed straight back into the plan. - Refine the plan (optional, repeatable). Use
/cq:plan:follow-up <goal id> <additional scope>to extend or adjust the plan, then answer the next round of questions. Repeat steps 4–5 until you are satisfied with the plan. - Let it run.
/cq:advancedrives the whole flow — investigate → plan → implement — autonomously, pausing only when it needs something from you (an unanswered question or a user action). Re-run it after you unblock it.
Agent tools reach the host tmux clipboard through a per-launch host broker
(yolo-clipboard-proxy, defects:D262) instead of a raw tmux socket bind. The
broker grants exactly two fixed operations — set the host clipboard
(tmux load-buffer -) and read it back (tmux save-buffer -) — over a
dedicated 0600 socket inside a 0700 per-launch directory, while a
PATH-prepended tmux shim inside the sandbox forwards only
load-buffer/save-buffer/show-buffer and rejects every other tmux verb. The
result is bounded clipboard read/write authority with no host tmux command
authority: payloads are capped at 1 MiB, the sandbox TMUX coordinate names
only the broker socket (never the host socket path or server PID), every
bind that would expose the inherited socket is masked by the alias-resistant
confinement pass, and every failure fails closed (clipboard disabled, never
a raw host socket). Accepted fixed invocations can still trigger
host-configured tmux hooks — after-load-buffer, after-save-buffer, and
command-error — which belong to the host server's own configuration and
receive no client-supplied argv; they are the only tmux side effects the
bridge retains.
cq init writes cq.toml; cq.toml.example documents the
complete schema. Model tokens use exactly one executable prefix:
claude:<model>, codex:<model>, or pi:<provider>/<model>. In particular,
pi:openai-codex/gpt-5.6-sol:xhigh remains a Pi provider route, while
codex:gpt-5.6-sol:ultra selects the Codex executable. The packaged Codex
vocabulary accepts low, medium, high, xhigh, max, and ultra.
[dispatch] has two global booleans. forceShellout defaults to false.
unsafeDisableCodexReadOnlySandbox also defaults to false; when enabled, a
Codex role that requested read-only instead runs with
danger-full-access. This temporary compatibility switch removes Codex's OS
sandbox, but does not widen the role's ledger tool profile. Both settings
apply identically under the claude, codex, and pi active harnesses;
[harness.*] blocks cannot override them.
Dispatch is CQ-driven. A parent calls start_dispatch with a role and its
typed input; CQ resolves the role's token from cq.toml (or a configured panel
token the parent names), and the token's harness is the dispatch's target
harness, so cross-harness dispatch follows from configuration. CQ prepares
against the target harness's prompt surface and launches the role through that
harness's process boundary: claude -p with the role's attested built-in tools
and its own profile-narrowed ledger server for Claude, the packaged
cq-codex-role launcher for Codex, and pi -p for Pi (whose fenced result CQ
stores). The parent never holds a capability; it reads the outcome with
fetch_dispatch_result and a bounded waitMs, which returns the body exactly
once. The resolved per-role token must name the target harness, so
pi:openai-codex/... remains a Pi process/provider route rather than becoming
a Codex route. A server cannot host a same-session native launch, so none is
used.
Codex repository mutation remains brokered. implement-worker can receive
only gitChangeCapability and use it only for git_commit, returning the
durable gitReceipts chain. implement-conflict-resolver can receive only
gitConflictCapability and use it only for git_resolve_continue, returning
the durable conflictReceipts chain. Capabilities never enter argv, prompts,
result bodies, or route metadata; the parent releases the managed worktree
only after consuming and verifying the bound result.
The Bun workspace lives under nix/pkg/cq-ledgers/:
nix develop # bun + node + toolchain (from repo root)
cd nix/pkg/cq-ledgers
bun install
bun test # full suite
bun run typecheck # tsc -b
bun run lint # eslint
bun run check # all threepackages.node-modules is a fixed-output derivation that fetches all npm
dependencies. After changing dependencies (and bun.lock), refresh its
outputHash in flake.nix: set it to sha256-AAAA… (52 As), run
nix build .#node-modules, and paste the got: hash back.
Outputs:
packages.{cq,node-modules}+apps.{default,cq}(default iscq mcp).homeManagerModules.dev-llm— ponygirls with CQ's ledger and prompts.nixosModules.cq-server— runscq serveover a native, tuned PostgreSQL.llmAssets— the ledger's system-agnostic prompt/skill asset bundle.