Claude Code hooks are shell scripts that fire automatically in response to tool-use and session events. qq uses hooks for auto-compilation, compile and review gating, skill modification tracking, auto-sync, session cleanup, and post-compaction resume hints. All plugin-level hooks are defined in hooks/hooks.json; a few project-local hooks (e.g. pre-push-test.sh) are wired through .claude/settings.json instead.
| Trigger | Matcher | Script | Purpose |
|---|---|---|---|
| PreToolUse | Edit|Write |
compile-gate-check.sh |
Block edits to engine source files when compile is red, or when the project has never been opened in its editor |
| PreToolUse | Edit|Write |
review-gate.sh check |
Block edits while review verification is pending |
| PreToolUse | Bash (project-local) |
pre-push-test.sh |
Block git push until ./test.sh passes |
| PostToolUse | Write|Edit |
auto-compile.sh |
Auto-compile engine source files via qq-compile.sh (multi-engine dispatcher) |
| PostToolUse | Write|Edit |
skill-modified-track.sh |
Record when skill files are modified |
| PostToolUse | Bash |
review-gate.sh set |
Activate the review gate after a code/plan review script runs |
| PostToolUse | Agent |
review-gate.sh count |
Count verification subagent completions to release the review gate |
| Stop | (all) | check-skill-review.sh |
Block session end if skills were modified without /qq:self-review |
| Stop | (all) | review-gate.sh stop |
Block session exit if review verification is incomplete |
| Stop | (all) | auto-pipeline-stop.sh |
Block session exit during an active /qq:execute --auto pipeline |
| Stop | (all) | session-cleanup.sh |
Remove temp files, clear gate, prune stale runtime data |
| SessionStart | (startup) | auto-sync.sh |
Sync plugin scripts into the target project after a plugin upgrade |
| SessionStart | compact |
execute-resume-hint.sh |
Surface in-flight /qq:execute progress after context compaction |
| SessionStart | compact |
auto-pipeline-resume-hint.sh |
Surface in-flight auto-pipeline state after context compaction |
- PreToolUse -- runs before a tool executes. A non-zero exit or
"decision":"block"response prevents the tool from running. - PostToolUse -- runs after a tool completes. Can inject context back into the conversation via
hookSpecificOutput. - Stop -- runs when the session is about to end. Can block session termination.
- SessionStart -- runs when a session starts (
startup) or after context compaction (compact). Can inject startup context.
The compile gate is a project-level guard that blocks edits to engine source files until the compile is green and the project has been opened at least once in its editor.
Script: scripts/hooks/compile-gate-check.sh
Trigger: PreToolUse for Edit|Write
Timeout: 5 seconds
The hook runs two checks against the file path being edited (only when the file matches an engine source pattern via qq_engine.py matches-source):
- Virgin project check -- a project-level fact, read from the filesystem. Unity projects need
Library/; Godot projects need.godot/; Unreal projects needIntermediate/. If the marker is missing, the agent is told to open the project in its editor first and let the initial import finish. - Compile gate check -- a session-level state, scoped by
$PPID. After a failed compile,auto-compile.shwrites$QQ_TEMP_DIR/compile-gate-$PPID. The check hook reads the file, verifies it has not aged out (1 hour), and blocks the edit until the gate clears. The gate clears automatically on the next successful compile, or expires after 1 hour.
The gate is keyed by $PPID so concurrent Claude Code sessions never see each other's compile state.
Script: scripts/hooks/auto-compile.sh
Trigger: PostToolUse for Write|Edit
Timeout: 120 seconds
When a file is written or edited, this hook checks whether the file is an engine source file (decided by qq_engine.py matches-source against the active engine). If so, it calls qq-compile.sh, which delegates to the right engine path:
- Unity →
unity-compile-smart.sh, which picks tykit HTTP, editor trigger (osascript / PowerShell), or batch mode - Godot →
godot-compile.sh(headless GDScript validation) - Unreal →
unreal-compile.sh(UnrealBuildTool + editor commandlet) - S&box →
sbox-compile.sh(dotnet build)
Compile output appears in the terminal. The agent reads any errors and fixes them automatically in the same turn. On a red compile, the hook writes compile-gate-$PPID so the next edit is blocked by the compile gate. On a green compile, the gate file is removed.
The hook is gated by the auto_compile setting in the active qq profile -- if disabled, it exits immediately.
A unified script (review-gate.sh) coordinates four subcommands that together enforce verification before code edits resume after a code or plan review.
Earlier installs may still have legacy
review-gate-{check,set,count,stop}.shfiles; new hook bindings should use the unifiedreview-gate.sh <subcommand>form.
Script: scripts/hooks/review-gate.sh set
Trigger: PostToolUse for Bash
After a Bash command completes, this hook checks whether the command invoked code-review.sh, claude-review.sh, plan-review.sh, or claude-plan-review.sh. If so, it writes a gate file at $QQ_TEMP_DIR/review-gate-$PPID with the three-field format <unix_timestamp>:<completed>:<expected> (timestamp, zero completed verifications, expected verification count). It also injects context telling the agent to dispatch verification subagents for each finding.
Script: scripts/hooks/review-gate.sh check
Trigger: PreToolUse for Edit|Write
Timeout: 5 seconds
Before any edit or write, this hook checks whether a gate file exists for the current session. If the gate is active and not all verification subagents have completed (completed < expected), it blocks the edit. The gate only blocks edits to relevant file types (.cs files and Docs/*.md). Gates expire automatically after 2 hours.
Script: scripts/hooks/review-gate.sh count
Trigger: PostToolUse for Agent
Each time a subagent completes, this hook increments the completed counter in the gate file. Once completed >= expected, the gate releases edits. The hook injects context confirming the count.
Script: scripts/hooks/review-gate.sh stop
Trigger: Stop
When the session is about to end, this hook checks whether the review gate is active with incomplete verifications (completed < expected). If so, it blocks termination so verification subagents can finish.
Script: scripts/hooks/skill-modified-track.sh
Trigger: PostToolUse for Write|Edit
When a skill file is written or edited (paths matching */.claude/commands/*.md or */skills/*/SKILL.md), this hook appends the file path to $QQ_TEMP_DIR/claude-skill-modified-marker-$PPID.
At session end, the Stop hook check-skill-review.sh checks the marker file. If skills were modified but /qq:self-review was never run, the hook blocks termination with an error listing the modified files. Running /qq:self-review clears the marker.
Script: scripts/hooks/auto-pipeline-stop.sh
Trigger: Stop
When /qq:execute --auto is running an unattended pipeline (.qq/state/auto-pipeline.json is present and active), this hook prevents the session from ending mid-pipeline. The actual circuit-breaker logic lives in qq-execute-checkpoint.py pipeline-block, which decides whether the pipeline is still in progress.
Script: scripts/hooks/session-cleanup.sh
Trigger: Stop
Timeout: 2 seconds
Removes session-scoped temp files (gate file, skill modification marker, anything tagged with $PPID) and prunes stale runtime data via qq_runtime_prune.
Script: scripts/hooks/auto-sync.sh
Timeout: 10 seconds
When a session starts, this hook calls qq-auto-sync.py to mirror the latest plugin scripts into the target project's scripts/ directory. This is how a plugin upgrade reaches an installed project without forcing the user to re-run install.sh.
After context compaction, two hooks read state files written by long-running skills and inject resume hints so the agent picks up where it left off:
execute-resume-hint.sh-- reads.qq/state/execute-progress.json(written by/qq:execute) and reports the in-flight phase, completed steps, and what to run next.auto-pipeline-resume-hint.sh-- reads.qq/state/auto-pipeline.json(written by/qq:execute --auto) and reports the in-flight pipeline stage so the unattended loop continues correctly.
Both hooks exit silently if the corresponding state file is absent.
Script: scripts/hooks/pre-push-test.sh
This is a project-local hook registered through .claude/settings.json (not the plugin's hooks.json), so it only fires for projects that opt in. When installed via install.sh --with-pre-push (or by hand), it intercepts git push commands and runs ./test.sh before allowing the push. If tests fail, the push is blocked.
All temp files use the $PPID suffix (the parent process ID of the hook shell). This ensures concurrent Claude Code sessions do not interfere with each other -- each session's compile gate, review gate, skill markers, and run records are scoped to its own PID. Shared hook state would cause one session's red compile to block another session's edits.
Hook scripts read tool input from stdin via the shared qq_hook_input helper in scripts/qq-runtime.sh. The helper prefers jq when available and falls back to $QQ_PY (python3) otherwise -- so hooks work on Windows boxes that have python3 but not jq, without crashing the host script under set -euo pipefail.
file_path="$(qq_hook_input tool_input.file_path)"
cmd="$(qq_hook_input tool_input.command)"New hook scripts should never call jq directly.
Hooks may fire twice for the same input (Claude Code retries on transient failures). Side effects (writing gate files, appending to markers, recording run state) must be idempotent. Most current hooks achieve this by writing-then-overwriting rather than appending, or by guarding appends with a marker check.
Hooks that care about engine type call qq_engine.py matches-source rather than hardcoding *.cs. This is how the same auto-compile.sh and compile-gate-check.sh work correctly across Unity, Godot, Unreal, and S&box.
- Architecture Overview
- Cross-Model Review -- how the review gate fits into the tribunal flow
- Configuration -- controlling which hooks run via the active profile