Repository navigation
feat(#289): native AskUserQuestion picker (render + single-select answering) - #323
Merged
Merged
Conversation
…swer via number-key Claude Code's AskUserQuestion tool draws a blocking TUI picker. Until now Agent Code only showed it as a dead "work · AskUserQuestion" indicator, forcing the user into the raw terminal to answer. This renders it as a native in-feed picker and lets the user answer SINGLE-SELECT questions by clicking an option. WHY clicking sends the option's NUMBER to the PTY: Claude's picker renders options numbered 1..N in parsedInput order and selects + submits ATOMICALLY when a digit key is pressed (proven live). So answering option i is just `sendInput(sessionId, String(i + 1))` over the same keystroke path the terminal uses — no cursor tracking, no arrow navigation, no separate submit, and crucially no screen parser. That atomicity is what lets this PR ship answering without reading the terminal back. WHY driven by parsedInput, not the screen: The semantic layer already parses the full input into `block.parsedInput.questions[]` on tool_input_finalized/block_completed. That is the source of truth; rebuilding it from terminal paint would reintroduce the brittle heuristics the semantic path exists to kill. How it dismisses: an unresolved block (`!resultAt`) means the picker is live. When the tool_result lands, `resultAt` is set, BlockRow stops routing to the picker, and the row unmounts on its own. Scope boundary — deferred to follow-up PRs: multi-select (a digit toggles instead of submitting, so the number trick is unsafe — rendered read-only with a "answer in the terminal" note + disabled clicks), free-text answers, and any screen parser. Renderer-only; no IPC/SubAgentState contract changes; no headless changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ynchronous double-submit latch — review fixes Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Claude Code's
AskUserQuestiontool draws a blocking TUI picker. Today Agent Code only surfaces it as a deadwork · AskUserQuestionindicator, so the only way to answer is to drop into the raw terminal. This PR renders the picker natively in the feed from the already-parsed semantic input, and lets the user answer a single-select question by clicking an option.AskUserQuestionRow.tsxreadsblock.parsedInput.questions[]defensively (guards partial streaming with a "Question loading…" placeholder) and renders each question'sheaderchip,questiontext, andoptionsas a clickable, numbered list, reusing existing feed primitives (MarkerRow, the app's design tokens).window.api.sendInput(sessionId, String(i + 1)). Claude's picker numbers options1..NinparsedInputorder and selects and submits atomically on a digit key (proven via the live probe) — so one keystroke does the whole thing. No cursor tracking, no arrow navigation, no screen parser. After a click the row latches into a disabled "Answering…" state to prevent double-submit.How it dismisses
An unresolved block (
!block.resultAt) means the picker is live and awaiting the user. When thetool_resultlands it setsresultAt;BlockRowthen stops routing to the picker and the row unmounts on its own. We never hide ourselves manually — ownership is decided one level up.Scope boundary (deferred to follow-up PRs)
Renderer-only — no
claude-code-headlesschanges, noSubAgentState/IPC contract changes.Verification
npx tsc -p tsconfig.web.json --noEmitreports zero new errors in the touched files (AskUserQuestionRow,BlockRow,renderUnits). The select+submit-on-digit behavior was confirmed against a live picker.🤖 Generated with Claude Code