Skip to content

Hooks

What each Claude Code hook does for just-say-so, and when it runs. A hook is a command the harness runs at a fixed point, such as “the user submitted a prompt” or “the agent just wrote a file”. The hook wiring is hooks/hooks.json; each entry runs a stdin shim in src/hooks/ around a pure function in src/lib/hooks/.

Hook What it does
UserPromptSubmit Injects the condensed rules, with the reply report, on the prompt after a reply broke a rule. On /just-say-so:restate, records the rejected reply instead. Never blocks your prompt.
SessionStart Injects the rules at the start of every context, the full text by default (rules.contextStartText): new session, resume, /clear, and after compaction. Also says so, once, when checks are on but the vale binary is missing, when the Vale config predates the 0.9.0 names, or when a config file still sets 0.8.x keys.
SubagentStart Injects the rules into each subagent when it spawns, the full text by default. Subagents start with fresh context and never see the main session’s reminders — without this, they’d discover the rules only by violating them.
PreToolUse A confirmation prompt before the agent edits a policy file (.just-say-so.json, .vale.ini, a vocabulary accept.txt), plus the pre-publication check for gh commands and named MCP tools. In block mode the check denies the call before the text is published.
PostToolUse Runs Vale on the file a tool just wrote (Write, Edit, MultiEdit, NotebookEdit). The hook drops alerts outside the lines this edit added, so on a legacy file the agent answers only for its own additions. The hook records errors for the Stop gate, and keeps an earlier edit’s unfixed errors on record when a later edit to the same file is clean.
Stop In files.mode: "block", the session gate: re-lint the recorded files and block the turn while their errors remain. When a rewrite leaves the same alerts, the gate reports once, lets the turn end, and waives those alerts for the rest of the session. Also the reply check (reply.mode): block forces a rewrite; warn, the default, records per-rule counts, which ride the next rules injection as a report. After /just-say-so:restate, adds the rewrite to the rejection record.

A hook that throws exits 0 with nothing on stdout, so a bug in the plugin never blocks a prompt or a tool call. It is not silent: one line goes to stderr, and one JSON line (time, hook, message, the top of the stack) is appended to hook-errors.jsonl in the state directory. The doctor warns with the count for the last 7 days and the latest failure.

The PreToolUse publish check and the Stop reply check have their own pages: Checking published text and Configuration.