lecode is a minimalist terminal AI coding agent, written in Python. Installable as a
CLI via uv tool install, runs in the terminal's normal scrollback (no alternate
screen), talks to OpenRouter and any generic OpenAI-compatible API (local
servers included), executes tools under a rich permission system, persists sessions, and
supports power features: subagents, MCP servers (with Exa and context7 auto-configured),
git worktrees, skills, custom user-defined agents, LSP diagnostics, persistent memory,
a post-task reviewer model, multimodal input, lifecycle hooks, session export, loop
mode, prompt chaining.
Mandatory session naming: every interactive start asks for a session name up front,
before the chat opens — the prompt cannot be skipped or left empty (Ctrl-C/Ctrl-D
quits instead). The goal is enforced order: every session is named, browsable, and
greppable from the moment it exists. Headless mode (-p, --loop) skips the prompt and
auto-generates a name; --continue/--resume keep the existing session's name.
Requires three external binaries on PATH: fd, ripgrep (rg), and rtk — checked
at startup with a clear error and install hint if missing.
Design stance (from kon): the default harness stays tiny — default system prompt under ~300 tokens, project context loaded externally via AGENTS.md files and skills. A rich, detailed prompt (personas, mode prompts) is available as a config option.
(Release: GitHub-only — git tag + GitHub Release via release-please; not published to PyPI.)
- Tiny default system prompt; externalized project context (AGENTS.md from git root down to cwd + global file)
- Skills system:
SKILL.mdpacks in.agents/skills/(project) and~/.agents/skills/(global), YAML frontmatter (name,description,register_cmd,cmd_info), discoverable by the model and optionally registered as slash commands - UX extras: steer queue (Alt+Enter queues a priority message processed before normal
queued prompts; 5+5 queue limits),
!cmd(run shell, show result) and!!cmd(also feed output to the LLM), Tab path completion,/copy(last response to clipboard),/handoff(synthesize a focused prompt from the current session into a new one), session picker with delete,/sessionstats - Audio notifications on task finish / error / awaiting approval (volume config)
- Append-only JSONL session files (inspectable, robust; full history survives compaction)
- Config schema versioning with automatic forward migration
- Thinking levels (
/thinking), collapsible thinking display, turn cooldown and tool-call idle timeout knobs - Headless
-pmode with meaningful exit codes (0 done / 1 error / 2 startup / 3 max turns) --base-url+ auth-policy flags for local/custom OpenAI-compatible endpointsfdandripgrepas hard dependencies (kon-style), plusrtk— verified at startup
Not taken from kon: OAuth provider logins (auth stays API-key-only), auto-downloading binaries, two-mode permissions (too coarse), dedicated per-vendor provider plugins, DuckDuckGo web tools (Exa MCP instead), anonymous sessions.
- Permission system: per-tool glob + regex rule layers, last-match-wins, unbypassable denies, six modes (standard / restrictive / readonly / planwrite / guarded / yolo), session "allow always" allowlist, doom-loop detection; Exa / context7 / grep.app MCP tools treated as read-equivalent
- Session undo / redo / rewind-to-turn
- Subagents (
tasktool), MCP client (stdio + HTTP + OAuth for MCP servers) with Exa auto-configured by default and context7 one flag away, git worktrees, loop mode, prompt chaining, JSONL import + HTML export + gist sharing, themes, slash-command catalog, prompt caching (via OpenRouter), model catalog with pricing - LSP integration: diagnostics appended to edits
rtkoutput compaction for bash- Persistent memory: Markdown store — long-term
MEMORY.md(auto-injected, 32 KB cap), daily logs, project scratchpad checklists, named notes; four tools (memory_write/edit/read/search, regex keyword search); compaction summaries flushed to the daily log; atomic writes +.bakbackups;/memorycommands - Pierre mode: a post-task reviewer — a second model compares the user's request
with the finished result and reports whether it delivered;
/pierre on|off|model - Multimodal input: image/audio/PDF attachments via
/addand the@picker (20 MB cap), sent as OpenAI-compatible content parts where the selected model supports them - Lifecycle hooks: user-defined subprocess hooks —
PreToolUse,PostToolUse,Stop,UserPromptSubmit, session and subagent start/end; handlers receive a JSON envelope on stdin and return verdicts (Allow/Defer/Ask/Deny, most-severe-wins merge) plus optional input rewrites; hooks can only narrow permissions, never grant;/hooksstatus command and--hooks-testdry-run - Setup wizard (
--setup)
- Custom user-defined agents: markdown files in
~/.config/lecode/agents/(global) and.lecode/agents/(project) with frontmatter (description,mode: primary|subagent|all,model,temperature,permissionoverlay,hidden,color). Primary agents are cycled with Tab (built-ins:build= full access,plan= read-only/ask — maps onto the permission modes); subagents are invoked automatically by the model viataskor manually by@mention. Agent permissions layer over the global permission config.
Considered and deferred (not v1): formatter-runners after edits, customizable keybinds,
parent↔child session navigation, JS-style plugins, desktop app, /connect billing
portal.
- Language/runtime: Python 3.12+, single
asyncioevent loop. - TUI:
prompt_toolkit+Rich. Append-only feed, pinned input box. No alternate screen, no mouse capture, no full-screen dialogs. - Session naming: interactive startup always begins with a name prompt (a single
inline prompt_toolkit line, validated non-empty, duplicates suffixed
-2,-3, …); skipped in-p/--loop(auto-name from timestamp) and on-c/-rresume. - Statusline: one fixed, well-designed statusline (session name · agent · model · cwd+git branch · context meter · tokens · cost · spinner/queue state) — not user-configurable beyond theme colors. No segment-layout engine.
- Providers: one thin hand-written async streaming client on
httpximplementing the OpenAI Chat Completions protocol (SSE parsed directly). OpenRouter is a preset over the same client (base URL, app-identity headers, model listing + pricing, provider routing hints, prompt caching pass-through). No LiteLLM, no vendor SDKs. - Auth: API keys only. Priority chain: CLI flag > env var (
OPENROUTER_API_KEY,OPENAI_API_KEY, or per-custom-provider env) > config file. Custom/local endpoints may run keyless (auth = "auto|required|none"policy per provider). - External binaries (mandatory):
fdpowersfind_files,rgpowersgrep,rtkcompactsbashoutput (rtk rewrite, fail-open with 5s timeout on individual rewrites). Startup verifies all three exist on PATH and exits with install instructions otherwise (exit code 2). No auto-download, no fallback code paths. - Packaging: installable from the GitHub repo via
uv tool install(not on PyPI). - Tooling:
uv,pytest+pytest-asyncio+respx,ruff,pydanticv2,typer.
- CLI (typer): model/provider/api-key/base-url flags,
-pheadless mode,-c/--continue,-r/--resume <id|prefix|name>,--extra-tools, auth-policy and TLS-skip flags for local endpoints, tool allowlisting, read-only mode,--setup,--hooks-test. Startup dependency check forfd/rg/rtk; interactive startup then runs the mandatory session-name prompt before entering the chat. - Config:
~/.config/lecode/config.toml(TOML preferred; YAML/JSON accepted), auto-created on first run, schema version + automatic migration, project-local.lecode/config.tomldeep-merged over global, unknown-key warnings, CLI overrides. Sections:[llm](provider/model/thinking level/timeouts/base URL/auth policy/TLS),[llm.system_prompt](minimal default;style = "rich"opts into the detailed prompt + personas),[compaction],[agent](max turns, context window, cooldown),[tools],[ui](theme, thinking collapse, welcome shortcuts, hidden models),[permissions],[notifications],[mcp](enable_exadefault true,enable_context7default false, server table),[lsp](server overrides),[memory],[pierre],[hooks](event → handler commands), plus permission rule tables, colors, custom provider definitions (name → base_url + api_key_env + headers). - Providers: OpenRouter (first-class preset: model catalog refresh, pricing,
app-identity headers, caching) + generic OpenAI-compatible endpoints (Ollama, LM
Studio, llama.cpp, corporate gateways, DeepSeek/xAI/ZhiPu-style hosts — all via
[custom_providers]config or--base-url). Streaming everywhere, tool calling, retry with exponential backoff + jitter and per-error retryability,/modelslisting, static bundled model catalog (context size + pricing). - Agent loop: multi-turn streaming runner — stream a turn, collect tool calls, execute in parallel (results paired by call id), append, re-invoke; empty-response guard (max 3); "Please continue." injection when a turn ends without a final answer; instant cancellation of stream + in-flight tools; per-turn token/cost accounting; configurable max turns (default ~500) and turn cooldown.
- Tools: core 6 —
read(pagination, repeat-read guard, image rendering for multimodal models),write(atomic),edit(two engines: fuzzy whitespace-normalized search/replace and CRC-anchored line addressing),bash(timeout, truncation, idle timeout,rtkoutput compaction),grep(regex + glob + context; implemented overrg),find_files(glob; implemented overfd); pluslist_dir,todo_write,lsp_diagnostics,ask_user(inline structured multiple-choice questions, keyboard-answered in the TUI), the fourmemory_*tools;bash/taskacceptrun_in_background(detached, manager-owned, completion fed back into the next turn) withtasks_list/tasks_output/tasks_wait/tasks_stopto inspect and control them, plus a/taskscommand; MCP-provided web search/fetch via Exa. - Custom agents: built-in primaries
build(full access) andplan(read-only + ask), cycled with Tab and shown in the statusline; user agents from markdown files (global + project) with per-agent model/temperature/prompt/permission overlay; subagents invocable viataskor@mention; hidden agents excluded from pickers. - LSP integration: spawn language servers for files the agent writes/edits;
diagnostics appended to
write/editresults (fail-open — LSP problems never block the agent); built-in server registry (per-language command + file patterns) with[lsp]config overrides;lsp_diagnosticstool for on-demand queries; raw async JSON-RPC client over stdio. - Permissions: per-tool glob and regex rule layers, last-match-wins, deny rules
unbypassable, six modes with per-mode fallbacks, per-agent permission overlays,
session-scoped "allow always" allowlist, doom-loop detection (≥3 identical
consecutive calls → coach/ask/deny), interactive ask with AllowOnce /
AllowAlways(pattern) / Deny. Exa web search, context7, and grep.app MCP tools
classified read-equivalent → auto-allowed like read tools. Hooks sit strictly
outside the checker: they can only narrow a decision, never grant one.
/permissionsswitches modes. - Lifecycle hooks: subprocess handlers per event (
PreToolUse,PostToolUse,Stop,UserPromptSubmit, session start/end, subagent start/end); JSON envelope on stdin, verdict + optional rewritten input on stdout;PreToolUseverdicts merge most-severe-wins (Deny > Ask > Defer > Allow); tools are decorated at build time;--hooks-testdry-runs the hook pipeline without executing tools. - Memory: project-scoped Markdown store (project slug from cwd);
MEMORY.mdlong-term (auto-injected into the system context, 32 KB cap), daily log files, scratchpad checklist, named notes; toolsmemory_write/memory_edit/memory_read/memory_search(regex keyword search); compaction summaries appended to the daily log; atomic writes with.bakbackups;/memory show|edit|search|logcommands. - Pierre mode: after every completed task, a second model compares the request
with the result and gives feedback in the feed;
/pierre on|off|model. - Multimodal input:
/addand@accept image/audio/PDF files (20 MB cap); attachments sent as OpenAI-compatibleimage_url/ document content parts; a clear error when the selected model lacks the modality; attachment list shown in the feed. - Sessions: append-only JSONL per session in the config dir, always named
(mandatory prompt at interactive startup;
/newasks for a name too); resume by id / unique prefix / recency / name; session picker (sorted, searchable, with delete);/sessionmetadata + token stats;/rename; undo / redo / rewind-to-turn with restore point;/handoff(asks for the new session's name); per-session permission allowlist. - Cost reporting: money is a first-class metric — every time a chat finishes
(interactive run end,
/quit, and headless completion), display the session totals: money spent (USD, from catalog pricing), input tokens, output tokens. Running totals also live in the statusline (tokens · cost). - Context management: token estimation calibrated by provider usage; manual
(
/compact) and automatic compaction near the window (summarize old messages, keep recent tail;on_overflow = continue|pause, buffer tokens); optional mid-turn threshold; long tool results truncated head/tail with overflow to files; full history always retained in the JSONL file. - Context loading:
AGENTS.md/CLAUDE.mdfrom global dir and ancestors (git root → cwd); skills from.agents/skills/and~/.agents/skills/(SKILL.md + frontmatter, model-discovered,register_cmd: trueexposes them as slash commands); 16 built-in named prompt personas (rich prompt mode and.promptprefix), user-overridable; memory injection as above. - TUI: startup session-name prompt (validated, unskippable); multiline input, Emacs
bindings + kill ring, history with drafts (persisted), bracketed paste, Ctrl+G opens
$EDITOR, Tab path completion (and Tab agent-cycle when the input is empty),@fuzzy file/agent picker,/command picker,.prompt picker, argument pickers for models (withhidden_modelstrimming) / themes / providers, two-stage rewind picker; queued prompts (Enter while running) + steer queue (Alt+Enter, priority, 5+5 limits); streaming markdown feed with syntax highlighting; collapsible thinking blocks; fixed statusline (session name · agent · model · git branch · context meter · tokens · cost · state); braille spinner; inline permission prompt (y/a/n/ESC); 17+ JSON themes (/theme); OSC 8 hyperlinks; clipboard (OSC 52 / pbcopy / xclip),/copy; audio notifications;--no-color. - Slash commands (~50):
/new /clear /resume /session /undo /redo /rewind /retry /rename /history /quit /exit /handoff /compact /compress /model /models /thinking /reasoning /permissions /mode /toggle /theme /themes /prompt /editsys /add /drop /drop-all /init /help /welcome /tutor /review /btw /queue /copy /export /import /share /loop /worktree /wt-exit /wt-merge /mcp /model-subagent /models-subagent /notifications /memory /pierre /doctor /hooks /agents+ skill-registered commands. Prefixes:!cmd,!!cmd,.prompt,@file,@agent. - Headless:
-p [prompt](inline or stdin), tools auto-approved, final response to stdout, exit codes 0/1/2/3, auto-generated session name;--loopiterative mode against a plan file with optional per-iteration command and max iterations.
- Subagents:
tasktool spawning parallel child agents (per-prompt, timeout + response cap) — built-in read-onlyexploreplus user-defined agents; own model (/model-subagent), tool calls visible in feed; subagent lifecycle hooks fire. - MCP client: stdio + streamable-HTTP + SSE servers via the official
mcpPython SDK, with optional interactive OAuth for remote servers (auth = "oauth"; credentials under<config_dir>/mcp-auth/, unrelated to LLM auth), per-server timeouts/reconnect, tools under the permission system,/mcpmanagement. Auto-configured servers: Exa web search (default on, needsEXA_API_KEYor dashboard key flow) and context7 docs lookup (default off,enable_context7 = true). - Git worktrees:
--worktree <name>isolated worktree+branch, merge-back on exit (/wt-merge /wt-exit), conflict detection. - Export: standalone HTML (
/export), re-importable JSONL (/import), secret-gist sharing (/share). - Prompt chaining: brainstorm → plan → code → review phases with auto-transitions.
- Status signals: start/stop/git-conflict events over a Unix socket.
- No alternate screen, mouse capture, full-screen dialogs.
- No OAuth for LLM providers; no dedicated Anthropic/Gemini/Copilot/Codex/xAI/Azure plugins — API keys + OpenAI-compatible protocol only.
- No anonymous sessions in interactive mode (name is mandatory).
- No configurable statusline layout (one fixed design).
- No editor (ACP) integration, no output-compaction beyond
rtk. - No formatter-runners, customizable keybinds, or child-session navigation (deferred opencode ideas).
- No OS sandboxing of shell commands (config stub for later).
- No auto-downloading of binaries;
fd/rg/rtkmust be installed by the user. - Windows support is best-effort (targets macOS/Linux).
lecode/
├── pyproject.toml # console_script: lecode = lecode.cli:main
├── src/lecode/
│ ├── cli.py # typer CLI + startup checks (fd/rg/rtk) + name prompt hook
│ ├── config/ # pydantic models, loader, schema migrations, project merge
│ ├── auth.py # API-key priority chain (CLI > env > config), auth policy
│ ├── providers/ # openai_compat.py (the one client), openrouter.py (preset:
│ │ # headers, catalog, pricing), retry.py, catalog.py
│ ├── agent/
│ │ ├── runner.py # asyncio multi-turn streaming loop
│ │ ├── builder.py # prompt assembly (minimal|rich), tool/skill/agent wiring
│ │ └── tools/ # core tools (rg/fd/rtk backed) + task +
│ │ # memory_* + MCP bridge
│ ├── lsp/ # async JSON-RPC client, server registry, manager,
│ │ # lsp_diagnostics tool
│ ├── session/ # JSONL model/storage, naming, compaction, undo/rewind,
│ │ # handoff, input history
│ ├── permission/ # patterns, checker, six modes, agent overlays, doom-loop,
│ │ # MCP read-equiv
│ ├── hooks/ # event dispatcher, subprocess runner, verdict merge,
│ │ # tool decorator, --hooks-test
│ ├── memory/ # markdown store, 4 tools, injection, /memory handlers
│ ├── multimodal.py # attachment detection/encoding, modality capability check
│ ├── context/ # AGENTS.md walk, skills loader, agents loader,
│ │ # prompt/theme resources
│ ├── tui/
│ │ ├── app.py # prompt_toolkit application + asyncio wiring
│ │ ├── feed.py # append-only Rich feed (markdown, thinking collapse)
│ │ ├── input.py # editor, bindings, history, triggers, queues, Tab complete
│ │ ├── pickers.py # fuzzy pickers (files/agents/commands/models/sessions…)
│ │ ├── statusline.py # one fixed, polished statusline (name · agent · model …)
│ │ ├── notify.py # audio notifications
│ │ └── themes.py
│ ├── slash/ # command registry + handlers (skills register here too)
│ ├── extras/ # subagents, mcp_client (exa/context7 presets), worktree,
│ │ # loop_mode, export, chain, status_signals, rtk.py
│ ├── setup_wizard.py # linear onboarding dialogs
│ └── data/ # prompts/, themes/, models.json, export template
├── tests/
└── docs/
- One
asyncioloop; components communicate overasyncio.Queues with a small event taxonomy (Token / Reasoning / ToolCall / ToolResult / Error / Retrying / Done, user input, permission ask/reply). TUI never calls providers; runner never renders. - Startup sequence (interactive): dependency check → config load →
--setupif unconfigured → session-name prompt (loop until non-empty; Ctrl-C/Ctrl-D exits cleanly with code 0 before any session file is created) → session created on disk under that name → chat opens./newand/handoffreuse the same prompt component. - Cancellation is
asyncio.Task.cancel(); subagents in a task group cancelled together; steer queue is a second priority input queue drained first between turns. - One streaming client implementation (OpenAI Chat Completions with SSE via
httpx.AsyncClient.stream()+ line iteration); OpenRouter behavior is a thin preset layer over it (headers, catalog/pricing fetch, routing params). Subagents and the pierre reviewer reuse the same client with different model/prompt params. - External binaries: one shared async subprocess wrapper
(
asyncio.create_subprocess_exec, timeout, output caps) used bygrep(rg),find_files(fd),bash(rtk), and the hook handlers. Presence verified once at startup; individualrtk rewritefailures at runtime stay fail-open (original output used). Hook subprocess timeouts deny-safe (hook failure never widens access). - Hooks are applied as a decorator around every tool at build time; the permission checker runs first and hooks can only narrow its verdict (Allow → Ask/Deny, never the reverse).
- LSP manager: lazily spawns one server per language root on first write/edit to a
matching file; async JSON-RPC over stdio; diagnostics pulled via
textDocument/diagnostic(orpublishDiagnosticsnotifications) and appended to the tool result; every failure mode is swallowed (fail-open) with a debug log. - Multimodal: attachment files are sniffed by extension + magic bytes, base64-encoded, and attached to the user message as OpenAI content parts; the model catalog carries per-model modality flags to fail fast with a clear message.
- Static data (prompts, themes, model catalog, HTML template) ships as package data via
importlib.resources; override precedence embedded < global dir < project dir. - External programs (
$EDITOR,git) via prompt_toolkit'srun_in_terminal— trivial with no alternate screen to suspend.
pytest+pytest-asyncio(auto mode) — async tests must run underuv run python -m pytest;respxfor HTTP-level mocking. CI runners install fd/ripgrep/rtk (or a shim for rtk) before the suite runs.- Fake provider: scripted streaming responder (text chunks, tool calls, errors) with request/history capture — headless end-to-end tests of the full agent loop. Pierre and subagents run against the same fake.
- Provider contract tests: SSE decode, tool-call round-trips, multimodal payload shaping, retry classification, OpenRouter headers/catalog handling, error mapping, keyless local endpoints.
- TUI tests: prompt_toolkit
create_pipe_inputdrives the app headlessly; feed and statusline asserted with RichConsole(record=True)snapshots; the startup name prompt is covered by pipe-input tests (empty input rejected, duplicate suffixed, Ctrl-C aborts before session creation). - Tool tests: grep/find_files run against real
rg/fd(skipped with a clear marker if absent); startup dependency check tested with a doctored PATH; rtk compaction tested with a fakertkshim, including runtime fail-open on rewrite errors; LSP tested against a mock JSON-RPC server subprocess (spawn, initialize, diagnostics, fail-open on crash). - Hooks tests: corpus of fake handler scripts (each verdict, rewrites, timeouts,
crashes) asserting merge order and narrow-only semantics;
--hooks-testdry-run coverage. - Memory tests: store CRUD, injection budget caps,
.bakbehavior, search ranking. - Coverage emphasis (safety-critical first): permission checker + modes + agent overlays + MCP read-equivalence, hook verdict merging, edit engines, session JSONL storage / naming / compaction / undo / handoff, config merge + migrations, skills and agents loaders (frontmatter validation), runner retry/cancellation, queue/steer ordering. Target ~550–700 tests at v1.
- CI:
ruff check,ruff format --check,pyteston Python 3.12/3.13, Linux + macOS.
- Bootstrap — uv project, pyproject (deps: prompt_toolkit, rich, httpx, pydantic,
PyYAML, mcp, typer; dev: pytest, pytest-asyncio, respx, ruff), CI (installing
fd/rg), startup dependency check,
lecode --version. - Foundations — config system (models, loader, migrations, project merge, default writer), API-key auth chain, model catalog (incl. modality flags), AGENTS.md walk, prompt/theme resources.
- Provider layer — the OpenAI-compatible streaming client (SSE, tool calling, multimodal content parts, retry) + OpenRouter preset (headers, catalog refresh, pricing) + custom-provider resolution. respx contract tests.
- Session + permissions — JSONL sessions, naming (mandatory, duplicate-safe), compaction, undo/redo/rewind, handoff; permission checker (both rule layers, six modes, doom-loop, MCP read-equivalence). Heaviest test phase.
- Tools + agent loop — core tools with permission gating (rg/fd/rtk backed),
prompt assembly (minimal default + rich option), multi-turn runner, cancellation,
queues.
-pheadless mode works against the fake provider + one real provider. - Skills + custom agents — skills loader and
register_cmdslash registration; agents loader (markdown + frontmatter), build/plan primaries, Tab cycling,@mentionsubagent invocation, per-agent permission overlays. - Hooks + memory — hook events, subprocess runner, verdict merge, tool decorator,
--hooks-test,/hooks; memory store, four tools, injection,/memory. - TUI — startup session-name prompt, input editor (Emacs, history, Tab completion,
triggers, steer queue, agent cycling), streaming markdown feed with thinking
collapse, pickers (incl. session picker with delete), the fixed statusline, spinner,
themes, inline permission prompt, clipboard,
/copy, notifications, attachment display. - Slash commands + pierre + multimodal — full command registry; pierre post-task
review (
/pierre); multimodal/add+@attachments with capability checks. - Power features — in order: subagents, prompt chaining, export/import/share,
git worktrees, loop mode, status signals, LSP integration (registry, manager,
diagnostics-in-results,
lsp_diagnostics), then MCP client with Exa/context7 auto-config (largest, own sub-phase). - Onboarding + release —
--setupwizard, first-run defaults,/init,/welcome, docs (including fd/rg/rtk install instructions); automated release workflow (release-please: semver tag + GitHub Release, no PyPI); clean-machineuv tool install→ setup → real coding task; full CI green.
Roughly 19–26k LOC of Python (memory ~1k, hooks ~1.2k, pierre ~0.2k, multimodal ~0.3k, custom agents ~0.8k, LSP ~0.8k on top of the ~14–19k base). Phases 2–5 are the critical path to a usable agent; phase 8 is the largest single chunk; MCP + LSP (phase 10) are the biggest extras.
- Every phase:
uv run python -m pytestgreen,uv run ruff checkclean. - End of phase 5: scripted multi-turn tool-use transcripts pass against the fake
provider; live smoke test against OpenRouter and one local OpenAI-compatible server;
-pexit codes verified; startup check rejects a PATH missing fd/rg/rtk. - End of phase 7: hook corpus passes (verdict merge, narrow-only, timeout deny-safe); memory injection respects caps.
- End of phase 8: interactive start requires a session name (empty rejected, duplicate
suffixed, abort leaves no session file);
-pskips the prompt. - End of phase 11: clean-machine
uv tool install→--setup→ one real coding task completed (including one hook firing, one pierre review, one image attachment); suite green in CI.