mirror of
https://github.com/dtzp555-max/ocp.git
synced 2026-07-23 22:15:09 +00:00
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:
+9
-3
@@ -182,8 +182,13 @@ function tuiCapturePane(tmux, tmuxName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// True once claude's input bar is rendered and ready for keystrokes.
|
// True once claude's input bar is rendered and ready for keystrokes.
|
||||||
|
// Two known ready-state footers across claude versions: the classic `? for shortcuts`
|
||||||
|
// hint, and the `shift+tab to cycle` hint (part of "⏵⏵ bypass permissions on (shift+tab
|
||||||
|
// to cycle)") that newer claude 2.1.x renders in its place. Match EITHER — a matcher that
|
||||||
|
// only knew the classic string silently reported "never ready" on those builds, timing out
|
||||||
|
// every boot (tui_pane_not_ready) and, with the warm pool on, failing every pre-boot.
|
||||||
function tuiInputReady(pane) {
|
function tuiInputReady(pane) {
|
||||||
return /\? for shortcuts/.test(pane);
|
return /\? for shortcuts|shift\+tab to cycle/.test(pane);
|
||||||
}
|
}
|
||||||
|
|
||||||
// True once the pasted prompt has POSITIVELY landed in the input box. We only trust
|
// True once the pasted prompt has POSITIVELY landed in the input box. We only trust
|
||||||
@@ -615,8 +620,9 @@ export async function bootTuiPane({
|
|||||||
// A pooled pane is SINGLE-USE: it already carries its own fresh --session-id, it
|
// A pooled pane is SINGLE-USE: it already carries its own fresh --session-id, it
|
||||||
// serves this one turn, and it is killed in the finally like any other pane.
|
// serves this one turn, and it is killed in the finally like any other pane.
|
||||||
// 2. On a MISS: pre-trust the scratch cwd, boot an interactive `claude` in a fresh tmux
|
// 2. On a MISS: pre-trust the scratch cwd, boot an interactive `claude` in a fresh tmux
|
||||||
// session in the scratch cwd, poll capture-pane until the `? for shortcuts` input bar
|
// session in the scratch cwd, poll capture-pane until the input bar appears — see
|
||||||
// appears (bootTuiPane). BOOT_MS is the max wait, not a fixed delay.
|
// tuiInputReady for the ready-state footers matched (bootTuiPane). BOOT_MS is the max
|
||||||
|
// wait, not a fixed delay.
|
||||||
// 3. Write prompt to a 0600 temp file (no shell injection from prompt content).
|
// 3. Write prompt to a 0600 temp file (no shell injection from prompt content).
|
||||||
// 4. Paste the prompt via tmux load-buffer + paste-buffer -p (bracketed paste) —
|
// 4. Paste the prompt via tmux load-buffer + paste-buffer -p (bracketed paste) —
|
||||||
// reliable for large multi-line prompts where send-keys -l is not (issue #130).
|
// reliable for large multi-line prompts where send-keys -l is not (issue #130).
|
||||||
|
|||||||
+10
-2
@@ -3504,7 +3504,7 @@ if (process.env.OCP_TUI_LIVE === "1") {
|
|||||||
// Replicates tuiInputReady, tuiPromptLanded verbatim from lib/tui/session.mjs.
|
// Replicates tuiInputReady, tuiPromptLanded verbatim from lib/tui/session.mjs.
|
||||||
// Keep in sync with the definitions there.
|
// Keep in sync with the definitions there.
|
||||||
function _tuiInputReady(pane) {
|
function _tuiInputReady(pane) {
|
||||||
return /\? for shortcuts/.test(pane);
|
return /\? for shortcuts|shift\+tab to cycle/.test(pane);
|
||||||
}
|
}
|
||||||
function _tuiPromptLanded(pane, prompt) {
|
function _tuiPromptLanded(pane, prompt) {
|
||||||
const flatPane = pane.replace(/\s+/g, " ");
|
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
|
const TUI_LANDED_PANE = `❯ Reply with exactly: PONG_TEST
|
||||||
? for shortcuts · ← for agents`;
|
? 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 │`;
|
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):");
|
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)", () => {
|
test("tuiInputReady(LANDED_PANE) === true (input bar still present after paste)", () => {
|
||||||
assert.equal(_tuiInputReady(TUI_LANDED_PANE), true);
|
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)", () => {
|
test("tuiInputReady(BOOT_PANE) === false (welcome splash, no input bar yet)", () => {
|
||||||
assert.equal(_tuiInputReady(TUI_BOOT_PANE), false);
|
assert.equal(_tuiInputReady(TUI_BOOT_PANE), false);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user