You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/CONFIG.md
+124Lines changed: 124 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -189,6 +189,130 @@ Accepted top-level keys:
189
189
|`acp_port`| integer | TCP bind port for ACP server mode (equivalent to `--acp-port`, default: 7243). |
190
190
|`colors`| object | Background color overrides for the TUI. See the colors section below. |
191
191
192
+
## Hooks
193
+
194
+
Requires the `hooks` Cargo feature, which is **default-off** — a prebuilt
195
+
binary or package must have been compiled with `--features hooks` (or
196
+
`--all-features`) for any of this to apply. When the feature isn't compiled
197
+
in, none of the flags, files, or `/hooks`/`--hooks-test` commands below exist.
198
+
199
+
Hooks let external commands observe or gate agent behavior at defined points
200
+
(a tool call, a user prompt, the agent finishing a turn, a session
201
+
starting/ending, a subagent starting/stopping), using the same
202
+
`settings.json` shape, stdin envelope, and exit-code/stdout-JSON contract as
203
+
Claude Code, so an existing CC hooks setup is largely compatible (see the
204
+
`$CLAUDE_PROJECT_DIR` caveat below for the one script-level change some
205
+
setups need).
206
+
207
+
### Config file locations and precedence
208
+
209
+
Hook config lives in a `settings.json` (JSON, not `config.toml`/`.yaml`) at up
210
+
to three locations, loaded and merged in this order:
211
+
212
+
| Location | Trust |
213
+
| -------- | ----- |
214
+
|`~/.config/zerostack/settings.json` (global; on macOS `~/Library/Application Support/zerostack/settings.json`; on Windows `%APPDATA%\zerostack\settings.json`, experimental) | Trusted by default |
215
+
|`.zerostack/settings.json` (project, relative to CWD) |**Not** trusted by default — see Trust model below |
|`type`| string | Only `"command"` is supported. |
252
+
|`command`| string | Shell command, run via `sh -c` on Unix (`powershell -Command` on Windows, experimental). Receives the stdin envelope as JSON; `$ZEROSTACK_PROJECT_DIR` is set in its environment. |
253
+
|`args`| array of strings | When present, bypasses the shell entirely: `command` is executed directly as the program with `args` as its argv (no shell metacharacter expansion). |
254
+
|`timeout`| integer (seconds) | Per-hook timeout; the whole process group is killed on expiry. Default: 60. |
255
+
|`async`| boolean | When `true`, the hook runs in the background and its decision is ignored. Default: `false`. |
256
+
|`if`| string | A shell command evaluated (with the same stdin envelope) before the handler runs; the handler only runs if it exits `0`. Fails closed: a broken/unparseable/timed-out condition still runs the handler, with a warning. |
257
+
|`once`| boolean | Runs the handler at most once per event per session; later matches are skipped. |
258
+
259
+
`matcher` (on the handler group, not the handler) follows Claude Code
260
+
semantics: omitted, `""`, or `"*"` matches every tool; a bare name or a
261
+
`|`/`,`-separated list is an exact case-insensitive match after tool-name
`PreCompact` and `Notification` are not currently implemented.
271
+
272
+
Only `PreToolUse` is permission-blockable by default. A handler's stdout JSON
273
+
may set `"permissionDecision"` to `"deny"`, `"ask"`, `"allow"`, or omit it
274
+
(defer to the normal permission system). `deny` always blocks, holding even
275
+
under `--yolo`. `ask` forces an interactive confirmation regardless of
276
+
permission mode, and escalates to deny in non-interactive contexts (`-p`,
277
+
`--loop`) where no confirmation is possible. `allow` suppresses the
278
+
interactive prompt for that one call only — it can never override a deny
279
+
from a rule, security mode, managed policy, or another hook. `PreToolUse` may
280
+
also set `"updatedInput"` to rewrite the tool's arguments before it runs, and
281
+
`PostToolUse` may set `"result"` to rewrite the model-visible output.
282
+
283
+
`UserPromptSubmit` and `SubagentStart` can set `"additionalContext"` to
284
+
prepend text to the prompt. `Stop` and `SubagentStop` can set
285
+
`"decision": "block"` with a `"reason"` to force the agent (or subagent) to
286
+
continue instead of finishing, using `reason` as the next instruction; `Stop`
287
+
gives up after 8 consecutive blocks without progress.
288
+
289
+
Any handler can also signal via **exit code** instead of JSON: exit `0` means
290
+
no objection, exit `2` blocks (for blockable events) with stderr as the
291
+
reason, and any other exit code is a non-blocking error. Exit `2` combined
292
+
with stdout JSON is a mixed-channel warning — the JSON is ignored.
293
+
294
+
### Trust model
295
+
296
+
Project-level hook handlers (`.zerostack/settings.json` — global and managed
297
+
hooks are trusted automatically) require interactive confirmation the first
298
+
time they'd run, keyed by a hash of the handler's definition (event +
299
+
matcher + command/args/timeout/etc.); changing the definition changes the
300
+
hash and requires re-confirmation. Confirmations persist to
301
+
`$XDG_DATA_HOME/zerostack/trusted-hooks.json` (a user-level file, so child
302
+
processes/orchestrated subagents sharing it inherit trust automatically). In
303
+
headless contexts (`-p`, `--loop`) an unconfirmed project hook is skipped
304
+
with a warning rather than prompting.
305
+
306
+
### Global switches
307
+
308
+
| Flag | Effect |
309
+
| ---- | ------ |
310
+
|`--no-hooks`| Disables all non-managed hooks for this run. |
311
+
|`disableAllHooks: true` (in global or project `settings.json`) | Same effect, via config. |
312
+
|`--hooks-test <tool> [--hooks-test-input <json>]`| Dry-runs `PreToolUse` for `tool` against the loaded/trust-filtered dispatcher and prints the merged verdict/reason/`updatedInput`, then exits — no session, agent, or API key required. |
313
+
314
+
See [COMMANDS.md](COMMANDS.md#hooks) for the `/hooks` slash command.
315
+
192
316
## Mid-turn compaction
193
317
194
318
By default zerostack only compacts the conversation *between* turns, after a
0 commit comments