Allow hooks to trigger interactive terminal prompts with custom options
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
Hooks communicate with Claude Code via exit codes and stderr, but can only hard-block (exit 2) or silently allow (exit 0). There's no way for a hook to pause and ask the user a question with custom options — like the built-in permission prompt does with Allow/Deny/Always Allow.
Real use case: I have a protect-files.sh PreToolUse hook that blocks edits to sensitive files (.env, .pem, lockfiles, .git/). Currently it hard-blocks every attempt, and I have to manually work around it. I'd like the hook to show a prompt like:
Hook: protect-files wants to confirm
Edit to .git/info/exclude — this is a protected path.
1. Allow once
2. Block
3. Allow for this session
...and return the user's choice to the hook so it can decide the exit code.
Related: #51255 (asks for "ask" to work reliably in auto mode — adjacent but different; this request is about custom prompts with user-defined options, not reusing the existing permission dialog).
Proposed Solution
A hook should be able to output a structured JSON prompt request on stdout that Claude Code intercepts and renders as a terminal prompt — reusing the same TUI as the existing permission system.
Example hook stdout:
{
"prompt": {
"message": "Edit to .git/info/exclude — this is a protected path.",
"options": [
{"key": "1", "label": "Allow once", "value": "allow"},
{"key": "2", "label": "Block", "value": "block"},
{"key": "3", "label": "Allow for session", "value": "allow_session"}
]
}
}
Claude Code renders the prompt, waits for a keypress, and pipes the selected value back to the hook's stdin (or sets an env var like $HOOK_USER_CHOICE) so the hook can decide the final exit code.
Alternative Solutions
- macOS
osascriptdialogs — works but pops a separate window outside the terminal, breaking the workflow - Exit code 2 + stderr message — blocks unconditionally and relies on Claude (the model) to relay the message and ask the user; indirect, the user has no direct control, and the model may not relay it faithfully
PermissionRequesthook with"behavior": "ask"— only intercepts existing permission dialogs, can't define custom questions or options
Additional Context
The infrastructure already exists — Claude Code's terminal UI renders interactive prompts with keyboard shortcuts for the permission system. Hooks are child processes of Claude Code and already communicate via structured JSON on stdin/stdout. Bridging these two would make hooks significantly more powerful for safety and workflow gates without requiring hard blocks.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗