Creating Prompt-Based Hooks for Task Verification in Claude Code
Claude Code supports four hook types—command, http, prompt, and agent—that intercept 25 predefined session events, enabling automated task verification by evaluating custom LLM prompts when the Stop event fires.
Creating prompt-based hooks for task verification in Claude Code allows you to automate quality checks without writing custom scripts. The luongnv89/claude-howto repository provides a complete reference implementation in 06-hooks/README.md, including ready-to-use validators and verification workflows that inspect Claude's final output.
Understanding Claude Code Hook Architecture
Hook Events and Types
Claude Code exposes 25 predefined hook events that capture specific moments in a session lifecycle. According to the source documentation in 06-hooks/README.md, these include UserPromptSubmit for incoming prompts, PreToolUse and PostToolUse for tool execution, and critically, Stop and SubagentStop which fire after Claude finishes responding.
The system supports four distinct hook types:
- command: Executes shell commands or scripts
- http: POSTs JSON payloads to remote endpoints
- prompt: Evaluates custom LLM prompts to generate structured decisions
- agent: Spawns sub-agents for multi-step validation tasks
Configuration Schema and Data Flow
Hooks are declared in JSON configuration files located at ~/.claude/settings.json, project-local .claude/settings.json, or within plugin directories at hooks/hooks.json. The configuration requires a top-level "hooks" object mapping event names to arrays of hook definitions.
Every hook receives a JSON blob on stdin containing session metadata, the triggering event name, tool input, and tool name. Hooks must write a JSON response to stdout; returning exit code 2 blocks the operation. Prompt-based hooks specifically return decision objects that Claude evaluates without executing external code.
Implementing Prompt-Based Hooks for Task Verification
Leveraging the Stop Event for Final Verification
The Stop event is optimal for task verification because it fires immediately after Claude generates its final response. At this moment, the hook can inspect last_assistant_message (documented at line 264 in the README) to determine whether the requested work was actually completed.
Unlike command hooks that execute system calls, prompt-based hooks evaluate natural language instructions through Claude's LLM and return structured JSON decisions. This enables semantic verification—such as checking if explanations are complete or if files were mentioned in the response—without writing parsing scripts.
Configuration Structure for Prompt Hooks
A prompt-based hook requires a JSON object with "type": "prompt" and a "prompt" string containing the evaluation instructions. You can optionally specify a "timeout" in seconds (defaulting to 60) to prevent hanging sessions.
Practical Example: Verifying File Creation
Consider a workflow where Claude must generate src/main.py and tests/test_main.py. The following configuration in ~/.claude/settings.json automatically verifies these files exist when the session stops:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Check that the following files were created and are non-empty: src/main.py, tests/test_main.py. Respond with a JSON object containing \"decision\": \"approve\" if all are present, otherwise \"reject\" and include a brief reason.",
"timeout": 30
}
]
}
]
}
}
When Claude finishes responding, the hook evaluates the prompt and returns a JSON object like:
{
"hookEventName": "Stop",
"decision": "approve",
"reason": "Both files exist and contain content."
}
If files are missing, the decision becomes "reject" with an explanatory reason, allowing downstream automation to trigger rollback procedures or CI pipeline failures.
Complete End-to-End Implementation
The luongnv89/claude-howto repository demonstrates a full validation pipeline combining command hooks for real-time checks with prompt-based verification. The following setup intercepts user prompts, sanitizes code after edits, and performs final task verification:
# Install example hooks from the repository
mkdir -p ~/.claude/hooks
cp 06-hooks/*.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/*.sh
# Configure the complete hook chain
cat > ~/.claude/settings.json <<'EOF'
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/validate-prompt.py\""
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/format-code.sh\""
},
{
"type": "command",
"command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/security-scan.py\""
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Check that the files src/main.py and tests/test_main.py exist and contain at least one non-empty line. Return a JSON object with \"decision\": \"approve\" if both are OK, otherwise \"reject\" and a short reason.",
"timeout": 30
}
]
}
]
}
}
EOF
This configuration chains three phases:
- Input validation via
validate-prompt.py(lines 600-640 in the README) screens dangerous commands before execution - Post-processing via
format-code.sh(lines 630-660) andsecurity-scan.py(lines 680-720) sanitizes outputs - Verification via the prompt hook confirms deliverables meet requirements
Security Considerations and Best Practices
Prompt-based hooks provide inherent safety boundaries because they cannot execute code or perform I/O operations—they only return structured JSON payloads. However, follow these guidelines when implementing verification workflows:
- Keep prompts explicit and deterministic – Ambiguous natural language instructions may yield inconsistent decisions across Claude model versions
- Set conservative timeouts – Override the default 60-second timeout for complex verification prompts to prevent session hangs
- Version-control configurations – Store
.claude/settings.jsonin your repository to ensure hooks travel with the codebase - Separate concerns – Use command hooks for file system validation if you need to enforce security before Claude runs, reserving prompt hooks for semantic analysis
- Validate dangerous operations – The repository includes
validate-bash.pyas a reference implementation for filtering destructive commands through command-type validators
Summary
- Prompt-based hooks in Claude Code enable automated task verification by evaluating custom LLM prompts at specific session events
- The Stop event is the optimal trigger for final verification, providing access to
last_assistant_messageafter Claude completes its response - Configuration resides in
~/.claude/settings.jsonor project-local.claude/settings.jsonusing a top-level"hooks"object - Hooks receive JSON input on stdin and must return JSON decisions; exit code 2 blocks operations
- The luongnv89/claude-howto repository at
06-hooks/README.mdprovides reference implementations includingvalidate-prompt.py,format-code.sh, andsecurity-scan.py - Prompt hooks return structured approve/reject decisions with reasons, enabling CI/CD integration without custom parsing code
Frequently Asked Questions
What is the difference between command hooks and prompt hooks in Claude Code?
Command hooks execute shell commands or scripts with full system access, making them suitable for file system checks, formatting, and security scanning. Prompt hooks evaluate natural language instructions through Claude's LLM and return structured JSON decisions without executing code, making them ideal for semantic verification tasks like confirming completion status or validating explanation quality.
Which hook event should I use for final task verification?
Use the Stop event (or SubagentStop for sub-agent sessions) for final verification. This event fires immediately after Claude generates its final response, allowing the hook to inspect the complete output via last_assistant_message before the session terminates. According to the source documentation in 06-hooks/README.md, this is the only event that guarantees access to the assistant's final deliverables.
Can prompt-based hooks block or modify Claude's output?
Prompt-based hooks can block operations by returning a "reject" decision in their JSON response, but they cannot directly modify Claude's generated content. To alter output, use a PostToolUse command hook that transforms files after Claude writes them, such as the format-code.sh example in the repository which runs prettier, black, or gofmt based on file extensions.
How do I debug hook execution in Claude Code?
Run Claude Code with the --debug flag to see verbose hook logs in the session transcript. The JSON payload sent to each hook appears in the logs, along with the hook's stdout response and execution time. Verify your settings.json syntax using python3 -m json.tool before deployment, as malformed JSON prevents hook registration without explicit error messages.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →