Prose question guard

Put questions where the user can answer them. Catch a possible question in Claude's final reply and prompt it to use AskUserQuestion.

How it works

The hook runs at Claude Code's Stop event. It checks the final reply for question marks and phrases such as “would you like,” “let me know,” and “needs your approval.” If it finds a match, it blocks that stop and asks Claude to use AskUserQuestion.

If stop_hook_active is already true, the hook allows the stop. This prevents a loop. A false positive can pass on the next stop without changing the reply.

It runs locally with Python 3 and uses no external packages, model calls, or network access. It does not send phone notifications. Delivery to a phone depends on your client and notification setup.

The hook in use

The terminal shows the guard catching a possible prose question. Claude then explains that it was summarizing questions sent to someone else.

This is a false positive: the loop guard allows the next stop, so Claude can finish without asking the user again.

The status lines show the model, context use, active goal, and background work from my session.

The reusable hook uses neutral wording. Phone delivery depends on your client and notification setup.

Claude Code terminal showing the prose-question-guard Stop hook, a false-positive explanation, and status lines for the model, context use, active goal, and background shells.

Install in Claude Code

The same Python 3 file works on Windows, macOS, and Linux. It reads UTF-8 hook input and transcripts, uses native file paths, and runs no shell commands. From a clone of this repository, copy the hook:

macOS and Linux

mkdir -p ~/.claude/hooks
cp hooks/prose-question-guard.py ~/.claude/hooks/

Windows PowerShell

New-Item -ItemType Directory -Force "$env:USERPROFILE/.claude/hooks"
Copy-Item ./hooks/prose-question-guard.py "$env:USERPROFILE/.claude/hooks/"

Merge the entry below into the existing hooks.Stop array in ~/.claude/settings.json. Keep your other settings and hooks. Replace the example path with the absolute path on your machine.

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "python3 /absolute/path/to/.claude/hooks/prose-question-guard.py",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

On Windows, edit %USERPROFILE%/.claude/settings.json. Use py -3 or your installed Python 3 command with an absolute path. Quote paths containing spaces inside the JSON command:

"command": "py -3 \"C:/Users/Your Name/.claude/hooks/prose-question-guard.py\""

On macOS and Linux, use python3. Quote paths containing spaces there too. The installation reference includes all three platforms.

This is a manual hook install. Installing a marketplace skill package does not enable it. It uses Claude Code's Stop event and question tool; it is not a Codex hook.

What it catches

Examples of final replies and hook decisions
Reply or stateResult
Should I publish this?Blocks once; prompts AskUserQuestion
Let me know which draft you prefer.Blocks once, even without a question mark
The build passed.Allows
A question inside fenced code, inline code, a URL, a double-quoted phrase, or a Markdown blockquoteAllows
AskUserQuestion was the last tool call in this turnAllows
stop_hook_active is trueAllows

Limits and unattended runs

Check your installation

# macOS and Linux
python3 -m pytest hooks/tests/test_prose_question_guard.py -q

# Windows PowerShell
py -3 -m pytest hooks/tests/test_prose_question_guard.py -q

In a fresh interactive Claude Code session with the hook loaded, ask for a final reply containing a plain-text question. Confirm that Claude receives the hook reason and uses AskUserQuestion. If the match is not a question for you, the next stop can pass.

Read the full hook reference or Claude Code's Stop hook contract.