Claude Code sometimes stops and asks you a multiple-choice question with the AskUserQuestion tool ("Which framework?", "Keep the old API or replace it?"). If you're away from the terminal, the run just sits there.
The hooks docs describe answering it in -p mode: a PreToolUse hook returns allow plus an updatedInput that carries the answers. I wanted to know if the same works in a normal interactive session through a PermissionRequest hook, so I tested it on Claude Code 2.1.283. It does.
What the hook receives
When Claude calls AskUserQuestion in an interactive session, a PermissionRequest hook gets this on stdin (trimmed):
{
"hook_event_name": "PermissionRequest",
"tool_name": "AskUserQuestion",
"tool_input": {
"questions": [
{
"question": "Which color do you want?",
"header": "Color",
"options": [
{ "label": "Red", "description": "red" },
{ "label": "Blue", "description": "blue" }
],
"multiSelect": false
}
]
}
}
What the hook returns
Copy tool_input, add an answers object that maps each question's text to the chosen label (comma-separated for multi-select), and return it as updatedInput with behavior: "allow":
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow",
"updatedInput": {
"questions": [ ...same as input... ],
"answers": { "Which color do you want?": "Blue" }
}
}
}
}
Claude Code then shows:
⏺ User answered Claude's questions:
⎿ · Which color do you want? → Blue
⎿ Allowed by PermissionRequest hook
and Claude continues with "Blue". No terminal prompt appears.
A minimal test hook
#!/bin/sh
# Answers every AskUserQuestion with the last option. For testing only.
python3 -c '
import json, sys
d = json.load(sys.stdin)
if d.get("tool_name") != "AskUserQuestion":
sys.exit(0) # no decision: the normal permission prompt shows
ti = d["tool_input"]
ti["answers"] = {q["question"]: q["options"][-1]["label"] for q in ti.get("questions", [])}
print(json.dumps({"hookSpecificOutput": {"hookEventName": "PermissionRequest",
"decision": {"behavior": "allow", "updatedInput": ti}}}))
'
Register it under hooks.PermissionRequest in a settings file and try claude --settings test-settings.json.
Two things to watch
-
allowalone doesn't answer the question. If your approval hook (or an approval app) just returnsallowforAskUserQuestion, the question stays open and the session still waits. I hit exactly this: my phone approval said "allowed", but Claude was still waiting. - The hook blocks until it returns. To answer from somewhere else, the hook has to wait for that answer (and give up before the hook timeout so the terminal prompt still works).
Using it from a phone
I build Lidnight, a menu bar app that keeps a Mac awake while Claude Code and Codex work. Version 1.7.79 uses this: when Claude asks a question, the options show up on the phone status page (end-to-end encrypted) and in the Mac menu bar, and the one you tap goes back to Claude through the hook. You can still answer in the terminal; whichever comes first wins.
Top comments (0)