Claude Code hooks — the surface Argus runs on¶
Argus's capture and recall ride on Claude Code's hook system. This documents the hook events we rely on and can verify, how Argus uses each, and — separately, clearly quarantined — a set of additional events surfaced by 2026 web research that we could not confirm against authoritative docs and must verify before trusting.
Verification status (be honest about it): the events in §1 are confirmed by the
Claude Code settings schema bundled with this CLI (the update-config reference) and
by Argus's own working hooks. The events in the Appendix came from third-party blogs
and a research subagent; treat them as leads, not facts, until checked against
https://code.claude.com/docs/en/hooks.
1. Verified hook events¶
All hooks receive JSON on stdin and may return JSON on stdout. Common stdin
fields: session_id, transcript_path, cwd, plus event-specific fields.
Output is wrapped in hookSpecificOutput (with hookEventName), plus top-level
controls: continue (false to stop), stopReason, suppressOutput, decision
("block"), reason, and systemMessage (shown to the user).
| Event | Matcher | Fires | Key payload | Can output |
|---|---|---|---|---|
| SessionStart | startup\|resume\|compact |
session begins/resumes | session_id, cwd, source | additionalContext (injected into context) |
| UserPromptSubmit | — | user submits a prompt | the prompt text, session_id, cwd | additionalContext (prepended), decision:block |
| PreToolUse | tool name / mcp__srv__tool |
before a tool runs | tool_name, tool_input |
permissionDecision (allow/deny/ask), permissionDecisionReason, updatedInput |
| PostToolUse | tool name | after a tool succeeds | tool_name, tool_input, tool_response |
additionalContext, decision:block |
| PostToolUseFailure | tool name | after a tool fails | tool_name, tool_input, error |
additionalContext |
| PermissionRequest | tool name / rule | before the permission prompt | tool_name, tool_input |
permissionDecision |
| Notification | notification type | Claude emits a notification | type, message | suppressOutput |
| Stop | — | Claude finishes a response | session_id, transcript_path | decision:block (force continue) |
| PreCompact | auto / manual |
before context compaction | transcript_path, trigger |
feedback; additionalContext |
| PostCompact | auto / manual |
after compaction | the compacted summary | additionalContext |
| SessionEnd | — | session ends | session_id, transcript_path | (output ignored) |
Hook types (the type field)¶
- command — runs a shell command; stdin JSON in, stdout JSON out. The default,
most versatile; what every Argus hook uses. Supports
timeout, async variants. - prompt — asks Claude (LLM) a yes/no; slow, non-deterministic. Tool events only.
- agent — spawns a subagent to decide/verify; powerful, expensive. Tool events only.
Exit codes (command hooks)¶
- 0 — success; parse stdout as JSON.
- 2 — blocking error; stderr becomes the reason Claude sees.
- other — non-blocking; logged, execution continues.
2. How Argus uses them¶
Argus deliberately uses a small, append-NDJSON, fail-loud set — matching the 2026
best practice (capture = no LLM on the hot path; never exit 0 silently on failure).
Wired per machine in settings.json via thin wrappers in ~/.argus/bin/ that source
~/.argus/env (URL + ARGUS_CAPTURE_TOKEN) and exec the real binary.
| Hook | Argus wrapper | Binary | What it does |
|---|---|---|---|
| Stop | argus-capture-hook |
capture-hook |
Capture the just-finished turn(s) → POST /capture |
| SessionEnd | argus-capture-hook |
capture-hook |
Final capture of the session tail |
PreCompact (auto) |
argus-capture-hook |
capture-hook |
Capture before compaction discards turns |
SessionStart (startup\|resume\|compact) |
argus-recall-hook |
recall-hook -mode=context |
Inject project + global durable facts (deterministic, importance-ranked) |
| UserPromptSubmit | argus-recall-hook -mode=prompt |
recall-hook |
Hybrid recall against the prompt; inject top-5 (gated: system-turn → substance → floor) |
Why these and not more: capture needs the turn text (Stop/SessionEnd) and the pre-compaction window (PreCompact). Recall needs session start (SessionStart) and each user query (UserPromptSubmit). PreToolUse/PostToolUse-style capture is deliberately NOT used — tool I/O is noise for a decision/knowledge memory, and capturing it was a source of the system-turn noise we just gated out.
Reliability contract: every hook is fail-open for the session but loud on stderr — a memory hiccup can never block or pollute a session, but it never fails silently either (the anti-pattern the research explicitly named). The recall hook prints nothing to stdout and exits 0 on any problem; the capture hook logs the error.
Verify a hook is firing: for the cron-driven monitor tick the log is
~/tdarr-monitor-cron.log; for the Argus hooks, the proof is data —
raw_items rows appearing per session (capture) and injections rows appearing per
recall (recall). A machine whose hooks aren't installed shows no rows from that host.
3. Hook config locations & precedence¶
settings.json merges across scopes (later overrides earlier, but hooks accumulate):
managed → user (~/.claude) → project (.claude) → local (.claude/*.local.json)
→ plugin → session. View active hooks with /hooks. Argus's hooks live in the
user scope (the dotfiles claude/settings.json, symlinked by install.sh), so
they apply across every project on a machine.
Appendix — UNVERIFIED events (do not trust without confirming)¶
A 2026 research subagent (sourcing third-party blogs) listed ~27 hook events. The
ones not in §1 are listed here unconfirmed. Several look plausible (newer
releases add events), but some smell invented (e.g. an identity_status:
ALIVE/WEAK/SILENT PostCompact field appears to come from one specific blog/project,
not Anthropic). Verify against the official hooks reference before building on any
of these.
Claimed-but-unverified events: Setup, UserPromptExpansion, PostToolBatch,
PermissionDenied, StopFailure, SubagentStart, SubagentStop, FileChanged,
CwdChanged, ConfigChange, WorktreeCreate/WorktreeRemove, InstructionsLoaded,
Elicitation/ElicitationResult, TaskCreated/TaskCompleted/TeammateIdle.
Claimed-but-unverified hook types: http (POST to an endpoint), mcp_tool (call an
MCP tool directly). An HTTP hook type, if real, would be a cleaner transport for
Argus than the wrapper-binary pattern — worth confirming; it would let capture/
recall POST straight to argus-mem.aaronbrazier.com without a local binary.
Claimed-but-unverified payload field names (the research used user_message,
assistant_message, tool_output, compacted_summary, etc.; the authoritative
schema uses tool_input/tool_response and a prompt field — confirm exact names
before parsing them).
Action item if this matters: spend 10 minutes against the official docs to promote the real ones into §1 and delete the rest. Until then, §1 is the trustworthy set.