Skip to content

feat(#289): native AskUserQuestion picker (render + single-select answering) - #323

Merged
Juliusolsson05 merged 2 commits into
mainfrom
feat/289-native-question-picker
Jun 21, 2026
Merged

Juliusolsson05 merged 2 commits into
mainfrom
feat/289-native-question-picker

Conversation

@Juliusolsson05

Copy link
Copy Markdown
Owner

What this does

Claude Code's AskUserQuestion tool draws a blocking TUI picker. Today Agent Code only surfaces it as a dead work · AskUserQuestion indicator, 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.

  • New AskUserQuestionRow.tsx reads block.parsedInput.questions[] defensively (guards partial streaming with a "Question loading…" placeholder) and renders each question's header chip, question text, and options as a clickable, numbered list, reusing existing feed primitives (MarkerRow, the app's design tokens).
  • Single-select answering sends the option's number to the PTY: window.api.sendInput(sessionId, String(i + 1)). Claude's picker numbers options 1..N in parsedInput order 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 the tool_result lands it sets resultAt; BlockRow then 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)

  • Multi-select: a digit key toggles rather than submits, so the number trick is unsafe. Rendered read-only with a "Multi-select — answer in the terminal for now" note and clicking disabled. Answering is deferred.
  • Free-text answers (the auto-injected "write your own" entry) and any screen parser: out of scope here.

Renderer-only — no claude-code-headless changes, no SubAgentState/IPC contract changes.

Verification

npx tsc -p tsconfig.web.json --noEmit reports 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

Juliusolsson05 and others added 2 commits June 21, 2026 20:44
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant