# Creating Prompt-Based Hooks for Task Verification in Claude Code

> Automate task verification in Claude Code using prompt-based hooks. Learn how to intercept session events and evaluate custom LLM prompts for seamless validation.

- Repository: [Luong NGUYEN/claude-howto](https://github.com/luongnv89/claude-howto)
- Tags: how-to-guide
- Published: 2026-03-30

---

**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`](https://github.com/luongnv89/claude-howto/blob/main/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`](https://github.com/luongnv89/claude-howto/blob/main/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`](https://github.com/luongnv89/claude-howto/blob/main/.claude/settings.json), or within plugin directories at [`hooks/hooks.json`](https://github.com/luongnv89/claude-howto/blob/main/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`](https://github.com/luongnv89/claude-howto/blob/main/src/main.py) and [`tests/test_main.py`](https://github.com/luongnv89/claude-howto/blob/main/tests/test_main.py). The following configuration in `~/.claude/settings.json` automatically verifies these files exist when the session stops:

```json
{
  "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:

```json
{
  "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:

```bash

# 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:

1. **Input validation** via [`validate-prompt.py`](https://github.com/luongnv89/claude-howto/blob/main/validate-prompt.py) (lines 600-640 in the README) screens dangerous commands before execution
2. **Post-processing** via [`format-code.sh`](https://github.com/luongnv89/claude-howto/blob/main/format-code.sh) (lines 630-660) and [`security-scan.py`](https://github.com/luongnv89/claude-howto/blob/main/security-scan.py) (lines 680-720) sanitizes outputs
3. **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.json`](https://github.com/luongnv89/claude-howto/blob/main/.claude/settings.json) in 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.py`](https://github.com/luongnv89/claude-howto/blob/main/validate-bash.py) as 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_message` after Claude completes its response
- Configuration resides in `~/.claude/settings.json` or project-local [`.claude/settings.json`](https://github.com/luongnv89/claude-howto/blob/main/.claude/settings.json) using 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.md`](https://github.com/luongnv89/claude-howto/blob/main/06-hooks/README.md) provides reference implementations including [`validate-prompt.py`](https://github.com/luongnv89/claude-howto/blob/main/validate-prompt.py), [`format-code.sh`](https://github.com/luongnv89/claude-howto/blob/main/format-code.sh), and [`security-scan.py`](https://github.com/luongnv89/claude-howto/blob/main/security-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`](https://github.com/luongnv89/claude-howto/blob/main/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`](https://github.com/luongnv89/claude-howto/blob/main/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`](https://github.com/luongnv89/claude-howto/blob/main/settings.json) syntax using `python3 -m json.tool` before deployment, as malformed JSON prevents hook registration without explicit error messages.