fix(tui): accept shift+tab to cycle as an input-ready marker (#188)

Newer claude 2.1.x renders the input bar with a `shift+tab to cycle`
footer (part of "⏵⏵ bypass permissions on (shift+tab to cycle)") instead
of the classic `? for shortcuts` hint. tuiInputReady() matched only the
old string, so on those builds every pane read as never-ready: cold boots
timed out (tui_pane_not_ready) and, with the warm pool on, every pre-boot
failed (bootFailures climbed, warm hits stayed at zero) with no error
surfaced to the operator.

Widen the matcher to accept either footer. Keep the test replica verbatim
and add a positive case for the new footer.

Co-authored-by: sumlin <3495838+sumlin@users.noreply.github.com>
This commit is contained in:
sumlin
2026-07-23 13:33:15 +10:00
committed by GitHub
co-authored by sumlin
parent bdb6662c7c
commit c3294b404a
2 changed files with 19 additions and 5 deletions
+10 -2
View File
@@ -3504,7 +3504,7 @@ if (process.env.OCP_TUI_LIVE === "1") {
// Replicates tuiInputReady, tuiPromptLanded verbatim from lib/tui/session.mjs.
// Keep in sync with the definitions there.
function _tuiInputReady(pane) {
return /\? for shortcuts/.test(pane);
return /\? for shortcuts|shift\+tab to cycle/.test(pane);
}
function _tuiPromptLanded(pane, prompt) {
const flatPane = pane.replace(/\s+/g, " ");
@@ -3522,7 +3522,12 @@ const TUI_READY_PANE = ` Try "how does <filepath> work?"
const TUI_LANDED_PANE = ` Reply with exactly: PONG_TEST
? for shortcuts · ← for agents`;
// Welcome splash shown before input bar is rendered — no `? for shortcuts`.
// Newer claude 2.1.x renders the input bar with a `shift+tab to cycle` footer instead of
// `? for shortcuts` — the matcher must accept it too, or the pane reads as never-ready.
const TUI_READY_PANE_SHIFT_TAB = ` Try "how does <filepath> work?"
⏵⏵ bypass permissions on (shift+tab to cycle)`;
// Welcome splash shown before input bar is rendered — neither ready-state footer.
const TUI_BOOT_PANE = `╭─ Claude Code v2.1.114 ─ Welcome back Tao! ─╮\n│ Tips for getting started │`;
console.log("\nTUI readiness + paste-verify predicates (issue #130):");
@@ -3533,6 +3538,9 @@ test("tuiInputReady(READY_PANE) === true (input bar rendered)", () => {
test("tuiInputReady(LANDED_PANE) === true (input bar still present after paste)", () => {
assert.equal(_tuiInputReady(TUI_LANDED_PANE), true);
});
test("tuiInputReady(READY_PANE_SHIFT_TAB) === true (newer claude `shift+tab to cycle` footer)", () => {
assert.equal(_tuiInputReady(TUI_READY_PANE_SHIFT_TAB), true);
});
test("tuiInputReady(BOOT_PANE) === false (welcome splash, no input bar yet)", () => {
assert.equal(_tuiInputReady(TUI_BOOT_PANE), false);
});