Repository navigation
fix(lint): main lint is red — split native-chat-ask.ts under the max-lines limit #31734
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| import type { AskPrompt } from './native-chat-ask-types' | ||
| import { answerLabels, type AskAnswerSelection } from './native-chat-ask' | ||
|
|
||
| /** A single keystroke group to write to the agent PTY. `raw` bytes (option | ||
| * numbers, Enter, arrows) are written verbatim as keystrokes; `text` is a | ||
| * free-text answer the caller runs through its paste sanitizer before writing. */ | ||
| export type AskAnswerKeyGroup = { raw: string } | { text: string } | ||
|
|
||
| // Claude's AskUserQuestion is an arrow-navigate selector: a bare Enter commits | ||
| // the HIGHLIGHTED default (the first option), and pasted label text does not move | ||
| // the highlight — so answering by label silently delivered every non-first pick | ||
| // as the first option (STA-1860). Instead we drive the selector by each option's | ||
| // stable 1-based number (which matches the card's badge), the marker it commits | ||
| // on. Right-arrow steps to the next question / the Submit tab. Verified live | ||
| // against Claude Code's TUI; groups are written spaced apart (see the senders) | ||
| // because a navigation keystroke batched with Enter commits before the selector | ||
| // has applied it. | ||
| const ASK_ENTER = '\r' | ||
| const ASK_NEXT_TAB = '\x1b[C' | ||
| const ASK_PREVIOUS_ROW = '\x1b[A' | ||
| const ASK_NEXT_ROW = '\x1b[B' | ||
| const ASK_NOTES = '\t' | ||
|
|
||
| /** Build the ordered keystroke groups that answer a Claude Code AskUserQuestion. | ||
| * Each group is written a step apart so the selector applies it before the next. | ||
| * | ||
| * - single-select pick → the option number (selects AND commits; in a | ||
| * multi-question prompt it auto-advances to the next question) | ||
| * - free-text answer → the "Type something" row number, the text, then Enter | ||
| * - multi-select → each option number TOGGLES its checkbox, then a step | ||
| * to the Submit tab | ||
| * - a multi-question prompt (and a lone multi-select) finishes on a Submit | ||
| * confirmation, so it ends with one Enter | ||
| * | ||
| * (Option counts are ≤ the tool's cap of a few, so single-digit numbers always | ||
| * address every row.) */ | ||
| export function buildAskAnswerKeys( | ||
| prompt: AskPrompt, | ||
| selections: AskAnswerSelection[] | ||
| ): AskAnswerKeyGroup[] { | ||
| const questions = prompt.questions | ||
| const multiQuestion = questions.length > 1 | ||
| const groups: AskAnswerKeyGroup[] = [] | ||
|
|
||
| questions.forEach((q, qi) => { | ||
| const sel = selections[qi] | ||
| const other = (sel?.other ?? '').trim() | ||
| const typeSomething = String(q.options.length + 1) | ||
|
|
||
| if (q.multiSelect) { | ||
| for (const i of sel?.indices ?? []) { | ||
| groups.push({ raw: String(i + 1) }) | ||
| } | ||
| if (other) { | ||
| groups.push({ raw: typeSomething }, { text: other }, { raw: ASK_ENTER }) | ||
| } | ||
| // A multi-select never auto-advances; step to the next tab (the Submit tab | ||
| // when this is the last question). | ||
| groups.push({ raw: ASK_NEXT_TAB }) | ||
| } else if (other) { | ||
| // Single-select can only carry one value, so route any answer that | ||
| // includes free text through the "Type something" row as one string. | ||
| groups.push( | ||
| { raw: typeSomething }, | ||
| { text: answerLabels(q, sel).join(', ') }, | ||
| { raw: ASK_ENTER } | ||
| ) | ||
| } else if ((sel?.indices.length ?? 0) > 0) { | ||
| groups.push({ raw: String(sel!.indices[0]! + 1) }) | ||
| } else if (multiQuestion) { | ||
| // Unanswered question in a multi-question prompt: step past it. | ||
| groups.push({ raw: ASK_NEXT_TAB }) | ||
| } | ||
| }) | ||
|
|
||
| const endsOnSubmitTab = | ||
| multiQuestion || (questions.length === 1 && questions[0]!.multiSelect === true) | ||
| if (endsOnSubmitTab && groups.length > 0) { | ||
| groups.push({ raw: ASK_ENTER }) | ||
| } | ||
| return groups | ||
| } | ||
|
|
||
| /** Build keystrokes for Codex's request_user_input overlay. | ||
| * | ||
| * Unlike Claude, Codex submits on the final option digit and attaches free text | ||
| * as notes to the highlighted row. The overlay starts on the first row, so note | ||
| * answers move to the target without committing, open notes with Tab, then | ||
| * submit with Enter. */ | ||
| export function buildCodexAskAnswerKeys( | ||
| prompt: AskPrompt, | ||
| selections: AskAnswerSelection[] | ||
| ): AskAnswerKeyGroup[] { | ||
| const groups: AskAnswerKeyGroup[] = [] | ||
| let hasUnanswered = false | ||
|
|
||
| prompt.questions.forEach((question, questionIndex) => { | ||
| const selection = selections[questionIndex] | ||
| const selectedIndex = selection?.indices[0] | ||
| const note = (selection?.other ?? '').trim() | ||
|
|
||
| if (note) { | ||
| const targetIndex = selectedIndex ?? question.options.length | ||
| const rowCount = question.options.length + 1 | ||
| const nextSteps = targetIndex | ||
| const previousSteps = rowCount - targetIndex | ||
| const usePrevious = previousSteps < nextSteps | ||
| const navigationKey = usePrevious ? ASK_PREVIOUS_ROW : ASK_NEXT_ROW | ||
| const navigationSteps = usePrevious ? previousSteps : nextSteps | ||
| for (let index = 0; index < navigationSteps; index += 1) { | ||
| groups.push({ raw: navigationKey }) | ||
| } | ||
| groups.push({ raw: ASK_NOTES }, { text: note }, { raw: ASK_ENTER }) | ||
| return | ||
| } | ||
|
|
||
| if (selectedIndex !== undefined) { | ||
| groups.push({ raw: String(selectedIndex + 1) }) | ||
| return | ||
| } | ||
|
|
||
| hasUnanswered = true | ||
| groups.push({ raw: '\x7f' }) | ||
| if (questionIndex < prompt.questions.length - 1) { | ||
| groups.push({ raw: ASK_NEXT_TAB }) | ||
| } else { | ||
| groups.push({ raw: ASK_ENTER }) | ||
| } | ||
| }) | ||
|
|
||
| // Codex opens a confirmation after the last question when any were skipped; | ||
| // Proceed is highlighted by default, so one Enter submits the partial answer. | ||
| if (hasUnanswered) { | ||
| groups.push({ raw: ASK_ENTER }) | ||
| } | ||
| return groups | ||
| } |
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
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: stablyai/orca
Length of output: 4662
🏁 Script executed:
Repository: stablyai/orca
Length of output: 11485
Update the remaining stale import.
tests/e2e/native-chat-ssh-delivery.unit.test.tsimportsbuildAskAnswerKeysfrom the old module. Import it fromnative-chat-ask-keystrokesinstead.Suggested fix