Skip to content

Hooks

Hooks run a command or model check at a supported Wuu lifecycle event. Use them to reject a tool call, adjust its arguments, add check results, or record an event. They run trusted code or send data to a model; they are not an operating-system sandbox.

Merge a hooks section into the user configuration, normally ~/.wuu/config.json or $WUU_HOME/config.json, then restart the runtime that should use it. Enabled plugin packages can contribute hooks too. A skill’s hooks frontmatter is parsed for compatibility but does not register hooks.

{
"hooks": {
"PreToolUse": [
{
"matcher": "bash",
"type": "command",
"command": "python3 /absolute/path/check-shell.py",
"timeout": 10
}
]
}
}

This is a fragment, not a replacement for your provider and agent configuration. Project configuration follows the normal loading and trust rules; explicit automation configuration should contain only hooks you intend to execute.

FieldMeaning
matcherCase-insensitive exact tool name; empty or * matches everything
typecommand by default, or prompt
commandShell command for a command hook
promptModel-check template; $ARGUMENTS inserts the event JSON
modelOptional model name for a prompt hook; otherwise the configured hook client default
timeoutPer-hook seconds; omitted or nonpositive means 30

Matchers do not support glob expressions. Events without a tool name need an empty matcher or *. Matching hooks run in configuration order and stop at the first error. In PreToolUse, each argument replacement becomes the tool_input seen by later hooks. Other single-value fields use the last supplied value. Additional context accumulates in order, separated by blank lines; a later hook failure does not erase earlier context.

Calls with matching PreToolUse hooks run serially after model output completes: their original arguments cannot establish that the rewritten operation is safe for early or concurrent execution. Unmatched calls retain normal scheduling. Tool authorization still checks the rewritten arguments.

These events describe Wuu runtime paths; they do not promise equivalent interception inside an external engine.

EventUse and effect
PreToolUseBefore execution; can block or replace tool arguments
PermissionRequestDuring tool authorization; can block, but does not grant broader permissions
PostToolUseAfter success; can add model context, not undo the operation
PostToolUseFailureAfter a Go execution error or a rich result marked is_error; hook errors do not replace the original outcome
UserPromptSubmitBefore a prompt starts a turn; can block it
PreCompactBefore compaction; can prevent it
PostCompactAfter compaction returns; can reject adoption of the result
SubagentStart, SubagentStopAround a subagent turn; errors fail the associated operation
SessionStart, SessionEndOn session binding and cleanup; errors propagate to those operations
StopAt turn completion; can mark the turn failed
FileChangedAfter a successful Wuu file-tool write or edit; output does not alter the result

FileChanged is not a filesystem watcher. Manual edits, shell commands, and other programs need not trigger it. Post-tool hooks cannot turn a completed side effect into an action that never happened.

Wuu starts the command through a shell, inherits its process environment, and sends one JSON input object on stdin. Read cwd from that object rather than assuming the hook process starts in the workspace.

Timeouts and cancellation terminate the running shell’s process group on Unix and attempt process-tree termination on Windows. Commands should wait for their children rather than detach them. Pipe draining is bounded if descendants keep output handles open. Stdout and stderr are each limited to 1 MiB; exceeding either limit fails the hook without interpreting a partial decision.

{
"hook_event_name": "PreToolUse",
"session_id": "example-session",
"cwd": "/path/to/project",
"tool_name": "bash",
"tool_input": { "command": "go test ./..." }
}

Event-specific fields include tool_response for successful result text, error for failure details, prompt, file_path, compact_reason, and agent_id. Not every path supplies a nonempty session_id. Rich tool results are projected to text for hooks rather than passed as the full internal object.

Write one JSON object to stdout and diagnostics to stderr. All output fields are optional:

FieldEffect
decision: "block" or continue: falseReject the operation where the event supports blocking
reasonExplain the decision
updated_inputReplace the full argument object in PreToolUse
additional_contextAdd context after a successful PostToolUse

For example, this script demonstrates explicit blocking of one command pattern:

import json
import sys
event = json.load(sys.stdin)
command = (event.get("tool_input") or {}).get("command", "")
if "git push" in command:
json.dump({"decision": "block", "reason": "Publish changes manually."}, sys.stdout)
else:
json.dump({}, sys.stdout)

This substring example illustrates the protocol, not a complete shell security policy. Equivalent shell commands can have different text.

Exit 2 always blocks, including when stdout contains {} or a decision to continue. A JSON reason explains the block; otherwise Wuu uses stderr. Exit 0 uses the JSON decision, or continues without one. Other nonzero codes are execution failures. Invalid or mixed stdout is not interpreted as structured output.

A prompt hook asks a model for an ok boolean and a reason:

{
"type": "prompt",
"matcher": "bash",
"prompt": "Check whether this action fits the requested review-only task: $ARGUMENTS",
"timeout": 20
}

Place this entry under the chosen event. ok: false blocks. A missing model client, failed model request, or unparseable response currently passes through, so prompt hooks must not be your only security boundary. They also add model requests, latency, cost, and disclosure of event data to the selected service.

Start with a harmless event and verify its input, matcher, and output separately. Keep stdout free of logging. Check the command path, dependencies, timeout, and whether the script is waiting for interactive input. Restart the runtime after editing file-based configuration.

Hook input can contain source code, prompts, paths, and tool results. Remove temporary logging when finished and do not put secrets in command strings or shared configuration. Hooks do not become read-only simply because the agent uses Read only mode; see the security model.