# claudectl

Orchestrate a swarm of Claude Code agents with a local-LLM brain that learns from you.

## Build & Test

```bash
cargo build                  # Debug build
cargo build --release        # Release build, default features (bus+coord+relay+hive) — ~6.3 MB; matches the Homebrew bottle
cargo build --release --no-default-features --features hive   # Minimal sync-only build — ~3.5 MB
cargo test                   # Run all tests
cargo clippy -- -D warnings  # Lint (warnings are errors in CI)
cargo fmt --check            # Check formatting
```

## Architecture

### Workspace layout

This is a Cargo workspace with three crates. Dependencies flow strictly downward — `claudectl → claudectl-tui → claudectl-core` — and CI enforces it (Layering grep on core, plus standalone build jobs for each crate). Adding upward arrows is what the workspace exists to prevent.

```
crates/
├── claudectl-core/    # foundational types, IO, the UI↔runtime trait contract
│                      #   session, discovery, monitor, process, transcript,
│                      #   models, theme, logger, helpers, history, terminals/,
│                      #   health, rules, config, hooks, launch, skills, runtime
└── claudectl-tui/     # terminal UI + recording + demo fixtures
                       #   app.rs, ui/*, recorder.rs, session_recorder.rs, demo.rs
                       #   Depends on claudectl-core. Never on the binary.
src/                   # the binary crate `claudectl`
                       #   main.rs + brain/ + bus/ + coord/ + hive/ + relay/ +
                       #   orchestrator + init + commands + config.rs +
                       #   brain_screen.rs.
                       #   Implements the runtime traits over the real
                       #   subsystems via `src/runtime/`.
```

**UI ↔ runtime contract** lives at `crates/claudectl-core/src/runtime.rs`. Eight traits — `SessionSource`, `BrainView`, `CoordView`, `BusView`, `Actions`, `BrainReviewView`, `Orchestrator`, `HiveActions`, plus the stateful `BrainDriver` — wrapped in a `Runtime` aggregate. Core-owned DTOs (`SessionSnapshot`, `DecisionSummary`, `LeaseSummary`, `HiveViewSnapshot`, etc.) so the contract doesn't drag brain / coord / bus types upward. The binary's `src/runtime/` provides `Live*` adapters that implement each trait over the real subsystem.

**Re-export bridge:** `src/lib.rs` and `src/main.rs` both do `pub use claudectl_core::{session, discovery, …};` and `pub use claudectl_tui::{app, demo, recorder, session_recorder, ui};` so existing `crate::session::*` / `crate::app::*` paths in the binary keep resolving without rewriting every import.

**Feature propagation:** the binary's `coord`, `relay`, `hive` features propagate to `claudectl-tui` via `claudectl-tui/coord` etc. in `[features]`, so a single `#[cfg(feature = "...")]` gate resolves consistently across both crates.

### Core modules — `claudectl-core` (`crates/claudectl-core/src/`)

- `runtime.rs` — view traits + DTOs + `Runtime` + `MockRuntime`. See above.
- `session.rs` — Session data structures and formatting
- `discovery.rs` — Scans `~/.claude/sessions/*.json` and resolves JSONL paths
- `monitor.rs` — Parses JSONL conversation logs for tokens, cost, status events
- `process.rs` — Process introspection via native `ps` (not sysinfo crate)
- `history.rs` — Session history persistence and cost analytics
- `health.rs` — Session health monitoring (cache ratio, cost spikes, loop detection, stalls, context saturation). Owns `HealthThresholds`.
- `rules.rs` — Auto-rule engine: match sessions by status/tool/command/project/cost, then approve/deny/send/terminate/route/spawn/delegate
- `models.rs` — Model pricing profiles (built-in + user overrides) for cost tracking
- `transcript.rs` — JSONL transcript parser (messages, tool use, tool results, usage data)
- `theme.rs` — Color theming (dark/light/monochrome, respects NO_COLOR)
- `logger.rs` — Structured diagnostic logging
- `helpers.rs` — Shared utilities (webhook, notification, kill_process, aggregate session)
- `hooks.rs` — Event hook system (shell commands fired on session events)
- `launch.rs` — Launch and resume Claude Code sessions from the TUI or CLI
- `skills.rs` — Skill registry + claude-plugin metadata
- `config.rs` — `BrainConfig` / `IdleConfig` data structs (the binary still owns TOML parsing in `src/config.rs`; only the value types live here)
- `terminals/` — Terminal backends; see below.

### TUI modules — `claudectl-tui` (`crates/claudectl-tui/src/`)

- `app.rs` — `App` state struct, refresh loop, keyboard event handling. Holds a `claudectl_core::runtime::Runtime` for all brain/coord/bus reads and writes; never touches binary-only modules directly.
- `ui/` — Render functions: `table.rs` (session list), `detail.rs` (expanded panel), `help.rs` (overlay), `status_bar.rs` (footer), `peers.rs` (relay peers panel, feature `relay`), `skills.rs` (skills overlay).
- `recorder.rs` — Dashboard recording (asciicast/GIF capture of full TUI).
- `session_recorder.rs` — Per-session highlight reel recording (extracts edits, commands, errors; strips idle time).
- `demo.rs` — Deterministic fake sessions for screenshots, recordings, demos. Includes `DemoHighlightState` and `demo_peers` (feature `relay`).

Feature flags: `coord`, `relay`, `hive` mirror the binary's same-named features and gate `App` fields and ui panels.

### Binary modules — `claudectl` (`src/`)

- `main.rs` — CLI entry point, mode dispatch (TUI, watch, JSON, list, history, stats, orchestrator, clean, brain-eval, brain-query, mode, insights, init, doctor).
- `brain_screen.rs` — Full-screen Brain Review surface (scorecard + interactive review). Stays in the binary because it depends on `brain::metrics` and `brain::risk`; `main.rs` calls `brain_screen::render_brain_screen` for the brain panel. Every other ui render goes through `claudectl_tui::ui::*`.
- `doctor.rs` — `claudectl doctor` (#326). Unified install + runtime health checklist (PATH, hooks, plugin files, brain endpoint, bus feature, bus DB, session discovery, terminal integration). Returns `Check` rows with Pass/Advisory/Fail/Skipped + fix hints; `--json` form for scripting. Replaces the scattered `--doctor` flag and `init --check` paths.
- `config.rs` — Layered TOML config: CLI flags > `.claudectl.toml` > `~/.config/claudectl/config.toml` > defaults. Re-exports `BrainConfig`, `IdleConfig`, `HealthThresholds` from `claudectl-core`.
- `orchestrator.rs` — Multi-session task runner with dependency ordering.
- `commands.rs` — CLI command dispatch shared between modes.
- `init/` — `claudectl init` opinionated onboarding wizard (5 phases: budget, brain, plugin, bus, skills); see `docs/AGENT_BUS.md` §8 and issue #257. `init/plugin_assets.rs` (#325) embeds every `claude-plugin/` file via `include_str!` so the Plugin phase writes a working install to `~/.claude/plugins/claudectl/` without a repo clone.
- `runtime/` — `Live*` adapters that implement the `claudectl-core::runtime` traits against the real subsystems (sessions, brain, coord SQLite, bus SQLite, orchestrator, hive). See `runtime/{sessions,brain,brain_driver,brain_review,coord,bus,actions,orchestrator,hive}.rs`.

**Claude Code Plugin** (`claude-plugin/`): Integrates the brain directly into Claude Code sessions.
- `hooks/scripts/brain-gate.sh` — PreToolUse hook: queries brain for approve/deny on Bash/Write/Edit calls
- `hooks/scripts/budget-check.sh` — PreToolUse hook: enforces spend limits
- `commands/` — Slash commands: `/sessions`, `/spend`, `/brain-stats`, `/brain`, `/auto-insights`
- `agents/supervisor.md` — Session health triage agent
- `skills/session-monitoring/` — Auto-activated session awareness skill

**Brain** (`src/brain/`): Local LLM auto-pilot subsystem.
- `engine.rs` — Main brain loop: observes sessions, evaluates rules, queries LLM, executes decisions
- `client.rs` — HTTP client for local LLM endpoints (ollama, llama.cpp, vLLM, LM Studio)
- `context.rs` — Builds session context summaries for LLM prompts
- `decisions.rs` — Decision logging and few-shot retrieval (learns from past corrections)
- `agents.rs` — Agent delegation support
- `mailbox.rs` — Message passing between brain and TUI
- `prompts.rs` — Prompt templates (built-in + user overrides via `~/.claudectl/brain/prompts/`)
- `evals.rs` — Eval harness for testing brain decision quality against scenarios
- `insights.rs` — Auto-insights: friction pattern detection, rule suggestions, differential tracking

**Relay** (`src/relay/`): Cross-machine TCP transport (feature-gated behind `relay`).
- `mod.rs` — PeerId, RelayMessage, MessageType, identity persistence, peer PSK storage
- `crypto.rs` — Inline SHA-256 + HMAC-SHA256, PSK generation/formatting
- `protocol.rs` — NDJSON framing over TCP, HMAC challenge-response auth
- `peer.rs` — PeerConnection: connect, send, reader thread, heartbeat, reconnect
- `listener.rs` — TcpListener accept loop with auth rate limiting, max peers
- `mesh.rs` — PeerRegistry: broadcast, send_to, message dedup, heartbeat tick
- `delegation.rs` — DelegationContext, message builders for DelegateTask/TaskStatus/TaskHandoff/TaskInterrupt
- `worker.rs` — RemoteWorker: accepts delegated tasks, spawns claude sessions, reports status
- `invite.rs` — Invite codes (base32), word phrases (256-word list), invite links (cctl://), QR rendering
- `lan.rs` — UDP broadcast LAN discovery: announcer and scanner threads
- `cli.rs` — CLI dispatch for all relay subcommands (serve, invite, join, discover, delegate, etc.)

**Bus** (`src/bus/`): Agent-bus MCP server, durable role directory, and mailbox (feature-gated behind `bus`; see `docs/AGENT_BUS.md`).
- `mod.rs` — module surface
- `store.rs` — SQLite (WAL) at `~/.claudectl/bus/bus.db`: roles + messages tables (with `hop_count` column), drain + peek semantics
- `roles.rs` — Role addressing, cwd-inference, ambiguity/unbound resolution. Caller may override with `CLAUDECTL_BUS_ROLE` or `--role`
- `policy.rs` — Guardrails: subject grammar, type allowlist, body cap, leading-`/` neutralization (§9), hop cap (default 8), reserved-role guard (`supervisor`/`operator` cannot be bound)
- `rate_limit.rs` — In-process token bucket per `sender_role`; default 60 messages/min, refills continuously without a background timer
- `mcp.rs` — rmcp stdio server exposing `whoami`, `list_agents`, `publish` (with `parent_hop`), `read_inbox` (with `peek`), plus supervisor tools `submit_task` / `list_tasks` / `task_status`
- `cli.rs` — `claudectl bus` subcommand (stdio, role bind/list, send, inbox + `--peek`, whoami, stop-hook driver, prune)

**Supervisor** (`src/coord/{supervisor,actuator,verify,resume,tasks,session_policy,hook_events,events,exporter,supervisor_cli}.rs`, `src/ingest.rs`): Durable, verified task lifecycles on top of the bus (see `docs/AGENT_BUS.md#10-supervisor` and README §Supervisor).
- `tasks.rs` — `tasks` / `task_attempts` / `task_verifications` / `task_transitions` CRUD + state machine. Every transition writes state + log in one transaction so the ledger invariant "every state has a cause" holds.
- `supervisor.rs` — **pure** reconciler. Reads desired state from coord, observed state from `Sensors`, returns `Vec<Action>`. No I/O. Health-check → `HealthAction` mapping table. `Unknown` observed status is the no-actuation backstop (RFC §6).
- `actuator.rs` — Carries out `Action`s. Three-step: insert attempt → perform side effect → log transition. Side-effect failure stops *before* the transition is recorded, so the next tick re-emits.
- `verify.rs` — `Verifier` enum (Run/Brain/Agent) + `run_verifier` dispatch. Fail-closed on missing PASS/FAIL marker. `build_retry_prompt` composes original + failure output + "fix these issues" feedback.
- `resume.rs` — Tree-state hash (git when available, mtime fallback) + recovery-prompt builder. Resume the *task*, not the session.
- `session_policy.rs` — Atomic per-session policy file at `~/.claudectl/coord/session-policy/<session>.json`. Brain-gate hook reads it on every tool call; fail-open to `Inherit` keeps the manual-upgrade gap non-breaking.
- `hook_events.rs` — `hook_events` table (latency-only signal path; JSONL tail stays authoritative).
- `events.rs` — v1 NDJSON event schema (frozen). Three families: `task.transition`, `task.verification`, `task.escalated`.
- `exporter.rs` — Hand-rolled HTTP `/metrics` server (no web framework dep). Worker thread per scrape so the tick loop never blocks. Exposes `claudectl_tasks_by_state`, `claudectl_fleet_cost_usd_total`, `claudectl_retries_total`, `claudectl_verifier_pass_rate`.
- `supervisor_cli.rs` — `claudectl supervisor` subcommand (run / submit / status / logs / cancel / drain / undrain). Hand-rolled TOML reader for the `tasks.toml` subset.
- `src/ingest.rs` — `claudectl ingest --hook <name>` subcommand. Bash hooks call `claudectl ingest --hook PostToolUse 2>/dev/null || true`. Best-effort by construction.

Coord schema is gated on `PRAGMA user_version` (`EXPECTED_COORD_SCHEMA_VERSION = 3` today). A binary that meets a newer schema refuses to start with the exact `init --upgrade` remediation in the error.

**Hive** (`src/hive/`): Gossip-based knowledge sharing across connected brains (feature-gated behind `relay`).
- `mod.rs` — KnowledgeUnit, KnowledgeScope, KnowledgeContent types, semantic key, broadcast channel
- `store.rs` — JSONL-backed knowledge store with semantic index, atomic save
- `distiller.rs` — Converts DistilledPreferences/Insights into KnowledgeUnits with thresholds
- `merger.rs` — Conflict resolution: local always wins, peer-vs-peer by confidence*evidence
- `gossip.rs` — GossipEngine: incremental sync, snapshot pagination, epidemic propagation
- `trust.rs` — PeerTrust with auto-drift, TrustTier classification, TrustStore persistence
- `injection.rs` — Brain prompt integration with trust labels, concordance checking for drift
- `cli.rs` — CLI dispatch for hive subcommands (status, knowledge, export, import, trust)

**Terminal backends** (`crates/claudectl-core/src/terminals/`): Ghostty, Kitty, tmux, WezTerm, Warp, iTerm2, Terminal.app, Gnome Terminal, Windows Terminal — auto-detected, used for tab switching and input sending.

## Key Design Decisions

- **Minimal dependencies** — 7 runtime crates in the sync core. Startup under 50ms. **Exception:** the `bus` feature pulls rmcp + Tokio + schemars (every available MCP SDK is async) and is in the default feature set since the supervisor RFC (#342) — `cargo install` and `brew install` produce the same ~6 MB binary. The minimal sync-only build path (`--no-default-features --features hive`) remains CI-tested as the lower bound.
- **Native `ps`** over `sysinfo` crate to keep binary small.
- **Multi-signal status inference** — combines CPU usage, JSONL events, and timestamps (not just one signal).
- **Incremental JSONL parsing** — tracks file offsets, never rereads full files.
- **No async runtime** — synchronous with polling. Keeps complexity low. **Exception:** the `bus` MCP server (`src/bus/mcp.rs`) runs inside a current-thread Tokio runtime when invoked as `claudectl bus stdio`. The TUI and every other code path remain sync.
- **Deny-first rule evaluation** — deny rules always override approve/brain suggestions, regardless of config order.
- **Brain decisions are local-only** — all decision logs and few-shot examples stay on the user's machine.
- **Brain gate mode** — `~/.claudectl/brain/gate-mode` controls on/off/auto. File absent = on (default). The plugin hook and `--brain-query` both check this before querying the LLM.
- **Insights mode** — `~/.claudectl/brain/insights-mode` controls auto-generation of insights. File absent = off (opt-in). When on, insights are generated alongside preference distillation every 10 decisions.

## Conventions

- Run `cargo fmt` and `cargo clippy -- -D warnings` before committing.
- Tests live in `tests/integration_tests.rs` and `tests/unit_tests.rs`.
- Status inference logic has extensive test coverage — do not change status detection without updating tests.
- Health checks in `crates/claudectl-core/src/health.rs` have full unit test coverage — add tests for new checks.
- Terminal backends implement the pattern in `crates/claudectl-core/src/terminals/mod.rs` — add new terminals there.
- Config fields must be added to all three layers (CLI args in `main.rs`, TOML struct in `config.rs`, merge logic in `config.rs`).
- **Dependency direction:** `claudectl` (binary) depends on `claudectl-tui` and `claudectl-core`. `claudectl-tui` depends on `claudectl-core` only. `claudectl-core` depends on nothing claudectl-specific — no `crate::brain`, `crate::bus`, `crate::coord`, `crate::hive`, etc. references from inside core. CI rejects upward references via a grep guard plus standalone build jobs (`Core (standalone)`, `TUI (standalone)`).
- Foundational modules in core (and now TUI) never have `pub(crate)` on items downstream needs to call — promote to `pub` instead. The binary is a downstream consumer of both other crates.
- Brain prompt templates can be overridden by placing files in `~/.claudectl/brain/prompts/` — run `--brain-prompts` to list sources.
- Plugin hook scripts must check for `claudectl` availability and exit 0 on failure — never block Claude Code.
- Plugin commands call `claudectl` CLI modes and format output — they don't implement logic directly.
