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.