@@ -356,6 +356,7 @@ this exact call`. The session cache resets on `/new`.
356356/reasoning on|off toggle the streaming reasoning panel
357357/verbose on|off show every tool call with its full args + result
358358/trace on|off show nested subagent/critic tool calls (indented)
359+ /hooks list, trust, enable, or disable configured hooks
359360/compare [model] re-send the last prompt against OpenRouter for A/B
360361/fusion on|tool|off use OpenRouter Fusion alias or attach Fusion to a model
361362/route select|apply select or apply a configured multi-model stack route
@@ -592,6 +593,151 @@ Small Harness spawns each server at startup, lists its tools, and exposes
592593them through the same approval-gated tool layer with names like
593594` mcp__fs__read_file ` . JSON-RPC over stdio; no extra dependencies.
594595
596+ ### Hooks
597+
598+ Hooks let trusted local commands observe or influence harness events. They are
599+ useful for terminal integrations, status tracking, policy checks, and progress
600+ bridges for launchers, terminal orchestrators, and agent status dashboards.
601+
602+ Project hooks live in ` agent.config.json ` :
603+
604+ ``` json
605+ {
606+ "hooks" : {
607+ "PlanUpdated" : [
608+ {
609+ "hooks" : [
610+ { "type" : " command" , "command" : " $HOME/bin/agent-plan-hook" }
611+ ]
612+ }
613+ ],
614+ "PreToolUse" : [
615+ {
616+ "matcher" : " shell|file_write" ,
617+ "hooks" : [
618+ {
619+ "type" : " command" ,
620+ "command" : " $HOME/bin/check-tool-policy" ,
621+ "timeoutSec" : 5
622+ }
623+ ]
624+ }
625+ ]
626+ }
627+ }
628+ ```
629+
630+ Command hooks receive a JSON payload on stdin and may print JSON on stdout:
631+
632+ ``` json
633+ { "decision" : " block" , "reason" : " shell command not allowed" }
634+ ```
635+
636+ Supported decisions are ` allow ` , ` deny ` , ` block ` , and ` stop ` . Hooks can also
637+ return ` additionalContext ` , ` updatedInput ` , or ` feedback ` . For ` PreToolUse ` ,
638+ ` updatedInput ` is honored only with ` {"decision":"allow"} ` and is discarded if
639+ any hook blocks, denies, or stops. Exit code ` 2 ` maps to a blocking decision using
640+ stderr as the reason. For ` PreToolUse ` and ` PermissionRequest ` , hook runner
641+ failures such as timeouts, spawn/pipe failures, and shell infrastructure exits
642+ ` 126 ` /` 127 ` fail closed and block gated execution; ordinary nonzero exits still
643+ warn unless the hook explicitly blocks. A pre-execution ` stop ` prevents other
644+ pending tool calls in the same assistant step from running; a ` PostToolUse ` stop
645+ applies after that tool has already run and stops the next model step.
646+
647+ Hook events include ` SessionStart ` , ` UserPromptSubmit ` , ` PreToolUse ` ,
648+ ` PermissionRequest ` , ` PostToolUse ` , ` PreCompact ` , ` PostCompact ` ,
649+ ` PlanUpdated ` , ` SubagentStart ` , ` SubagentStop ` , ` Stop ` , and ` SessionEnd ` .
650+ Payloads include common fields such as ` hook_event_name ` , ` session_id ` ,
651+ ` turn_id ` , ` cwd ` , ` workspace_root ` , ` transcript_path ` , ` events_path ` , ` backend ` ,
652+ ` model ` , ` approval_policy ` , and ` source ` , plus event-specific fields like
653+ ` tool_name ` , ` tool_input ` , ` tool_response ` , and ` progress ` . ` source ` is
654+ ` interactive ` , ` one-shot ` , ` auto ` , ` fix ` , ` iterate ` , or ` play ` depending on
655+ what started the turn.
656+
657+ Hook payload stdin is raw and unredacted so trusted hooks can make decisions on
658+ the actual prompt/tool data; do not log it unless your hook performs its own
659+ redaction. Hook child processes start with a cleared environment and receive the
660+ minimal inherited shell environment (` PATH ` , ` HOME ` or Windows home/system vars),
661+ explicit parent process variables listed in ` envVars ` , literal values from
662+ ` env ` , plus ` SMALL_HARNESS_HOOK_EVENT ` , ` SMALL_HARNESS_SESSION_ID ` ,
663+ ` SMALL_HARNESS_TURN_ID ` , ` SMALL_HARNESS_TRANSCRIPT_PATH ` , and
664+ ` SMALL_HARNESS_EVENTS_PATH ` . Parent LLM provider credentials are not passed
665+ through unless a hook explicitly names them in ` envVars ` .
666+
667+ Matchers are Codex-style: absent, empty, or ` * ` matches all; exact ` | `
668+ alternation matches tool/event names; other matchers are treated as full-match
669+ regexes. Use ` .* ` when partial regex matching is intended. Invalid matcher
670+ regexes are shown by ` /hooks ` and skipped. ` UserPromptSubmit ` , ` PlanUpdated ` ,
671+ ` Stop ` , and ` SessionEnd ` ignore matchers. The default hook timeout is 600
672+ seconds for Codex parity; status/progress hooks should set a shorter
673+ ` timeoutSec ` if a slow hook would make the turn feel stuck.
674+
675+ The ` task ` tool uses ` SubagentStart ` and ` SubagentStop ` ; it does not also run
676+ generic ` PostToolUse ` hooks. ` Stop ` hook ` additionalContext ` and ` feedback ` are
677+ bounded, redacted, and added as context for the next turn.
678+
679+ ` PermissionRequest ` runs only when the harness would otherwise ask for approval.
680+ Use ` PreToolUse ` for blanket policy gates that must also cover auto-approved or
681+ read-only tools.
682+
683+ Project hooks are skipped until their current hash is trusted in user-owned
684+ state. Project-controlled ` hooks.state ` entries are ignored for execution
685+ safety. Manage trust with:
686+
687+ ``` text
688+ /hooks list hooks and trust state
689+ /hooks trust <key> trust one hook hash in user state
690+ /hooks trust-all trust all new/modified hooks
691+ /hooks disable <key> disable one hook
692+ /hooks enable <key> enable one hook
693+ ```
694+
695+ Trust is stored in ` $XDG_CONFIG_HOME/small-harness/hooks-state.json ` , falling
696+ back to ` ~/.config/small-harness/hooks-state.json ` . Trusted hook successes are
697+ quiet in the normal TUI; warnings, blocks, denies, stops, and feedback are
698+ shown. The event log records hook start/end/decision records with redacted,
699+ bounded stdout/stderr previews.
700+
701+ Launchers can inject ephemeral managed launch hooks without changing user
702+ config:
703+
704+ ``` bash
705+ SMALL_HARNESS_MANAGED_HOOKS_FILE=" $TMPDIR /agent-status-hooks.json" small-harness
706+ ```
707+
708+ ` SMALL_HARNESS_MANAGED_HOOKS_JSON ` accepts the same document inline for small
709+ launchers. Managed launch hooks are not cryptographic signatures or Codex
710+ enterprise-managed hooks; they are process-local launcher-trusted commands for
711+ wrappers that own the process invocation. Small Harness intentionally reads
712+ these only from the real process environment, not repo ` .env ` files. This lets
713+ integrations observe status without mutating the user's config.
714+
715+ Use ` envVars ` when a managed hook command needs launcher state:
716+
717+ ``` json
718+ {
719+ "source" : " terminal-orchestrator" ,
720+ "hooks" : {
721+ "Stop" : [
722+ {
723+ "hooks" : [
724+ {
725+ "type" : " command" ,
726+ "command" : " zentty ipc agent-event" ,
727+ "envVars" : [
728+ " ZENTTY_INSTANCE_SOCKET" ,
729+ " ZENTTY_WORKLANE_ID" ,
730+ " ZENTTY_PANE_ID" ,
731+ " ZENTTY_PANE_TOKEN"
732+ ]
733+ }
734+ ]
735+ }
736+ ]
737+ }
738+ }
739+ ```
740+
595741### Image input
596742
597743` /image <path> ` attaches an image to your next prompt. Small Harness encodes
@@ -703,6 +849,8 @@ AGENT_TOOL_SELECTION=auto # auto | fixed
703849WARMUP=true # pre-warm prompt cache at startup
704850SMALL_HARNESS_NO_WIZARD=false # skip first-run setup
705851SMALL_HARNESS_NO_UPDATE_CHECK=false # skip the GitHub release check
852+ SMALL_HARNESS_MANAGED_HOOKS_JSON=' {"source":"terminal-orchestrator","hooks":{...}}'
853+ SMALL_HARNESS_MANAGED_HOOKS_FILE=/tmp/agent-status-hooks.json
706854```
707855
708856Full list with comments in [ ` .env.example ` ] ( .env.example ) .
@@ -795,6 +943,11 @@ root. Common shape:
795943 },
796944 "mcpServers" : {
797945 "fs" : { "command" : " /usr/local/bin/some-mcp-server" , "args" : [] }
946+ },
947+ "hooks" : {
948+ "PlanUpdated" : [
949+ { "hooks" : [{ "type" : " command" , "command" : " $HOME/bin/plan-hook" }] }
950+ ]
798951 }
799952}
800953```
0 commit comments