Aletheia

25. Installers and automatic capture

Status: implemented and validated 2026-10-26. This page records what each installer writes, which hook fields it reads, and what is verified on this machine. Five more hosts and Claude Desktop were added on 2026-10-26.

aletheia install --list prints the capability level and verification for every tool:

ToolLevelVerificationHooksMCPRules
claude-codefullfrom-docs-only (real config files checked)5 events~/.claude.jsonCLAUDE.md
command-codefullfrom-docs-only (bundled reference + real CLI)3 events~/.commandcode/mcp.jsonAGENTS.md
cursorfullfrom-docs-only6 events~/.cursor/mcp.json.cursor/rules/*.mdc (sync)
codexfullfrom-docs-only5 events~/.codex/config.tomlAGENTS.md
gemini-clifullfrom-docs-only5 events~/.gemini/settings.jsonGEMINI.md
windsurffullfrom-docs-only4 events~/.config/devin/mcp_config.json (or legacy)global rules + .windsurf/rules/*.md
opencodemcp+rulesfrom-docs-onlynone shipped~/.config/opencode/opencode.jsonAGENTS.md

No tool was opened with this config in this pass. "from-docs-only" means the format came from the tool's official documentation (URL and date below) and the installer was tested against fixtures; it does not mean a real install was tested. Claude Code's shape was also checked against a real plugin hook file, and Command Code's against its bundled reference and a real CLI, but neither ran a real session here.

One command

aletheia install claude-code --write
aletheia install command-code --write

Without --write the command is a dry run: it prints every step and changes nothing. aletheia uninstall <tool> --write removes only the entries the install manifest lists. Every changed file gets a copy named <file>.aletheia-bak-<timestamp>.

The manifest is <data dir>/install-manifest.json. The default data dir is ~/.aletheia.

What Claude Code gets

StepFileWhat is written
MCP~/.claude.jsonmcpServers.aletheia = {type: "stdio", command: <bun>, args: [<mcp.mjs>], env: {}}
Hooks~/.claude/settings.jsonOne entry per event under hooks.<Event>; other hooks and settings are kept
Rules~/.claude/CLAUDE.mdThe managed rules block between the aletheia:rules markers

Hooks:

EventMatcherWhat the hook does
SessionStart`startup\clear\compact`Stores nothing. Prints the context block as hookSpecificOutput.additionalContext. resume is deliberately silent.
UserPromptSubmitnoneStores the prompt, redacted.
PostToolUse*Stores tool name, input summary and output cut to 1500 characters.
StopnoneQueues compression and stores last_assistant_message as a summary when present.
SessionEndnoneQueues compression and stores last_assistant_message when present.

What Command Code gets

StepFileWhat is written
MCP~/.commandcode/mcp.jsonmcpServers.aletheia = {transport: "stdio", command: <bun>, args: [<mcp.mjs>]}
Hooks~/.commandcode/settings.jsonOne entry per event under hooks.<Event>; permissions and other settings are kept
Rules~/.commandcode/AGENTS.mdThe managed rules block

Hooks: SessionStart (injects context on startup and clear, silent on resume), PostToolUse (stores tool events), Stop (queues compression).

What Command Code cannot capture. It has no UserPromptSubmit hook, no SessionEnd hook, and its SessionStart source is startup | resume | clear with no compact. Prompts and session-end summaries are therefore not captured there. The installer prints this, and the install manifest records it. Do not claim otherwise in the README.

The hook command

Each entry runs:

<bun> <data dir>/aletheia.bundle.mjs hook <tool> <event> # aletheia-managed

The absolute Bun path avoids a PATH lookup. The trailing comment is the marker used by uninstall and doctor. apply builds the bundle with Bun's built-in bundler; if that fails it points the hooks at bin/aletheia.mjs instead. The bundle exists because a hook has a ~100 ms budget: loading a dozen modules costs more than the work.

Hook runtime guarantees

Sources checked

SourceWhat it gaveDate
/home/mashraf/.claude/plugins/marketplaces/thedotmack/plugin/hooks/hooks.json (real installed Claude Code plugin)hooks.<Event>[].hooks[].{type, command, timeout}, the SessionStart matcher values, and UserPromptSubmit/PostToolUse/Stop/SessionEnd event names2026-10-26
~/.claude.json on this machineUser-scope MCP shape: top-level mcpServers.<name> = {type, command, args, env}2026-10-26
claude mcp --help--scope user exists; stdio form is claude mcp add <name> -- <command>2026-10-26
Command Code bundled reference command-code/dist/bundled/command-code-knowledge/reference/hooks.md (header: generated from https://commandcode.ai/docs)Event list, settings.json schema, stdin fields, stdout fields, source values2026-10-26
command-code/dist/bundled/command-code-knowledge/reference/mcp.md~/.commandcode/mcp.json and the {transport: "stdio", command, args} entry2026-10-26
~/.commandcode/settings.json and ~/.commandcode/mcp.json on this machineReal files use the same shapes; the settings file had permissions and no hooks2026-10-26

Official documentation fetches on 2026-10-26 failed with HTTP 502: https://code.claude.com/docs/en/hooks, https://docs.anthropic.com/en/docs/claude-code/hooks, https://commandcode.ai/docs/hooks. The formats above come from the real files and the tools' own bundled reference instead.

UNVERIFIED

Cursor

StepFileShape
MCP~/.cursor/mcp.jsonmcpServers.aletheia = {command, args}
Hooks~/.cursor/hooks.json{version: 1, hooks: {event: [{command, timeout}]}}
Rules.cursor/rules/aletheia.mdcYAML frontmatter + managed block, via rules sync

Events installed: sessionStart, beforeSubmitPrompt, postToolUse, afterAgentResponse, stop, sessionEnd. Fields read: common conversation_id, workspace_roots, transcript_path; prompt; tool_name, tool_input, tool_output; text; session_id. sessionStart returns {additional_context}. Cursor's stop has no assistant text, so afterAgentResponse stores the summary. There is no global Cursor rules file. Cursor was not installed here.

Source: https://cursor.com/docs/agent/hooks, https://cursor.com/docs/context/mcp, https://cursor.com/docs/context/rules (fetched 2026-10-26).

Codex CLI

StepFileShape
MCP~/.codex/config.toml[mcp_servers.aletheia] command = ...; args = [...]
Hooks~/.codex/hooks.json{hooks: {Event: [{matcher?, hooks: [{type: "command", command, timeout}]}]}}
RulesAGENTS.md / ~/.codex/AGENTS.mdmanaged block

Events installed: SessionStart, UserPromptSubmit, PostToolUse, Stop, SessionEnd. Fields read: common session_id, cwd, hook_event_name; prompt; tool_name, tool_input, tool_response; last_assistant_message. Codex requires non-managed hooks to be reviewed once with /hooks; that was not exercised. The TOML guard is structural, not a full parser. Codex 0.130.0 is installed, but no real hook ran.

Source: https://developers.openai.com/codex/hooks, https://developers.openai.com/codex/config-reference, https://developers.openai.com/codex/mcp, https://developers.openai.com/codex/agent-configuration/agents-md (fetched 2026-10-26).

Gemini CLI

StepFileShape
MCP + hooks~/.gemini/settings.jsonmcpServers.aletheia = {command, args} and hooks.<Event>[].hooks[]
RulesGEMINI.md / ~/.gemini/GEMINI.mdmanaged block

Events installed: SessionStart, BeforeAgent, AfterTool, AfterAgent, SessionEnd. Fields read: common session_id, cwd, hook_event_name; prompt; tool_name, tool_input, tool_response; prompt_response. Gemini's hook timeout is milliseconds; the installer writes 10000. MCP and hooks share one file, so the installer merges them in one step. Gemini 0.37.0 is installed, but no real hook ran.

Source: https://raw.githubusercontent.com/google-gemini/gemini-cli/main/docs/hooks/reference.md, https://raw.githubusercontent.com/google-gemini/gemini-cli/main/docs/tools/mcp-server.md, https://raw.githubusercontent.com/google-gemini/gemini-cli/main/docs/cli/gemini-md.md (fetched 2026-10-26).

Windsurf / Devin Desktop

StepFileShape
MCP~/.config/devin/mcp_config.json (legacy ~/.codeium/windsurf/mcp_config.json if it exists)mcpServers.aletheia = {command, args}
Hooks~/.codeium/windsurf/hooks.json{hooks: {event: [{command, show_output?}]}}
Rules~/.codeium/windsurf/memories/global_rules.md, .windsurf/rules/aletheia.mdblock; workspace rule has trigger: always_on

Events installed: pre_user_prompt, post_run_command, post_write_code, post_cascade_response. Fields read: common trajectory_id, tool_info; user_prompt; command_line, cwd; file_path, edits; response. Cascade has no session-start or session-end event: no context is injected and no session-end summary is stored. Hook commands run from the workspace root by default, so a payload without a cwd uses the hook process cwd. The docs now call the product Devin Desktop; the legacy Windsurf user paths are used.

Source: https://docs.windsurf.com/windsurf/cascade/hooks, https://docs.windsurf.com/windsurf/cascade/mcp, https://docs.windsurf.com/windsurf/cascade/memories (fetched 2026-10-26).

OpenCode (mcp+rules)

StepFileShape
MCP~/.config/opencode/opencode.jsonmcp.aletheia = {type: "local", command: [bun, mcp.mjs], enabled: true}
RulesAGENTS.md / ~/.config/opencode/AGENTS.mdmanaged block

No hook or plugin is written. The official plugin page lists ~/.config/opencode/plugins/ and event names (session.idle, message.updated, tool.execute.after, ...) but not the fields inside each event payload. Reading a guessed field such as event.properties.sessionID would be fabrication, so the capability is mcp+rules. A JSONC-only opencode.jsonc is refused rather than parsed as JSON. OpenCode is installed here; no plugin was loaded by us.

Source: https://opencode.ai/docs/mcp-servers/, https://opencode.ai/docs/rules/, https://opencode.ai/docs/plugins/ (fetched 2026-10-26).

Claude Desktop (MCP only)

No hooks and no rules file. The config merge is documented and fixture-tested in docs/claude-desktop.md. The Linux path is observed on this machine, not in the MCP quickstart, and is marked UNVERIFIED.

Rule hygiene

aletheia rules lint [--dir path] [--tools a,b] [--json] checks the store and the managed blocks before a sync. It reports duplicate rules once per group, direct always/never contradictions with the same scope and condition, a rule over 300 characters, an enabled set over 4,000 characters, instructions that ignore safety (ignore previous instructions, bypass security, rm -rf /, ...), and text that matches the redaction patterns (a secret). For every managed block that exists under the project directory or the user home it compares the parsed bullets with the enabled store rules: a rule only in the file and a rule only in the store are both drift, each with the file and line or the rule id. Each finding has a fix hint.

The check is a warning, not a gate: rules sync still runs. --json returns {findings, checked}. A file without the managed-block markers is skipped, so a user's own CLAUDE.md/AGENTS.md text is never compared.

Plugin manifests

Written 2026-10-26 from the official pages below. Every manifest carries "x-aletheia-verification": "from-docs-only": it was parsed and its paths were resolved against this package, but no host was opened to load it. test/manifests.test.mjs checks the JSON, the paths and the hook commands.

HostFileWhat it declaresSource (fetched 2026-10-26)
Gemini CLIgemini-extension.jsonmetadata and an mcpServers.aletheia entry using ${extensionPath}/bin/mcp.mjs and cwd: ${extensionPath}https://raw.githubusercontent.com/google-gemini/gemini-cli/main/docs/extensions/reference.md

What was not written, and why:

No manifest claims a hook whose script is missing, and no path escapes the package.

Transcript watcher

Tools without hooks can still be imported from their session files. aletheia watch polls the three verified layouts, keeps a byte offset and turn counter per file in ~/.aletheia/watcher.db, and imports only new lines:

aletheia watch --once
aletheia watch --interval 1000 --batch 50 --tool claude-code,command-code,codex
ToolFolderVerified from
Claude Code~/.claude/projects/<slug>/<session>.jsonlT-121, 2026-10-26
Command Code~/.commandcode/projects/<slug>/<session>.jsonlT-121, 2026-10-26
Codex CLI~/.codex/sessions/<year>/<month>/<day>/rollout-*.jsonltwo real rollouts on this machine, 2026-10-26

The source files are opened read-only. A file that shrinks is re-read from zero. A line without a newline is left for the next pass. A JSON line the parser does not recognise, and a line that is not JSON, are stored as a kind = "raw" episode instead of being dropped. The polling loop is clamped to at least 1000 ms, takes a batch size per file, and the CLI sets nice 10. Restarting resumes at the stored offset; a re-read is deduped by source_ref and content hash.

End-to-end test

test/e2e-more-hosts.test.mjs installs all six full-capability hosts into a temp HOME, reads the command back from each config, pipes a recorded payload set through it, and checks rows, redaction and p95. On 2026-10-26: 2 tests pass, p95 1.24 to 2.48 ms per host (hook work only). This is the real installed command, not a call to the adapter; it is still not the host application.

Sources for the five new hosts

SourceWhat it gaveDate
https://cursor.com/docs/agent/hookshooks.json shape, events, payload fields2026-10-26
https://cursor.com/docs/context/mcp~/.cursor/mcp.json, mcpServers shape2026-10-26
https://cursor.com/docs/context/rules.cursor/rules/*.mdc, frontmatter2026-10-26
https://developers.openai.com/codex/hookshooks.json/inline shape, events, fields2026-10-26
https://developers.openai.com/codex/config-reference[mcp_servers], hooks feature2026-10-26
https://developers.openai.com/codex/agent-configuration/agents-mdproject and global AGENTS.md2026-10-26
https://raw.githubusercontent.com/google-gemini/gemini-cli/main/docs/hooks/reference.mdhooks shape, events, fields2026-10-26
https://raw.githubusercontent.com/google-gemini/gemini-cli/main/docs/tools/mcp-server.mdmcpServers2026-10-26
https://raw.githubusercontent.com/google-gemini/gemini-cli/main/docs/cli/gemini-md.mdGEMINI.md paths2026-10-26
https://docs.windsurf.com/windsurf/cascade/hookshooks shape, events, fields2026-10-26
https://docs.windsurf.com/windsurf/cascade/mcpmcp_config.json paths2026-10-26
https://docs.windsurf.com/windsurf/cascade/memoriesrules paths and trigger2026-10-26
https://opencode.ai/docs/mcp-servers/mcp key, local entry2026-10-26
https://opencode.ai/docs/rules/AGENTS.md paths2026-10-26
https://opencode.ai/docs/plugins/plugin dirs and event names only2026-10-26
https://modelcontextprotocol.io/quickstart/userClaude Desktop paths (macOS/Windows)2026-10-26