fix: detect idle state with inline suggestions and esc-to-interrupt signal
Claude Code shows inline suggestions after the prompt when idle (e.g. "❯ check CI status on the PR"). Also strip separator lines (─) from terminal output before detection. Use status bar "esc to interrupt" presence as the authoritative active-vs-idle signal. - Strip separator lines and empty lines alongside status bar lines - Check raw output for "esc to interrupt" before stripping - Treat ❯ + text as idle when status bar lacks esc to interrupt - 4 new tests covering suggestion detection edge cases
This commit is contained in:
parent
f389c99a9c
commit
fad2938804
|
|
@ -423,6 +423,39 @@ describe("detectActivity", () => {
|
|||
agent.detectActivity("⏵⏵ bypass permissions on (shift+tab to cycle)\n"),
|
||||
).toBe("idle");
|
||||
});
|
||||
|
||||
it("returns idle for prompt with separator and status bar (no esc to interrupt)", () => {
|
||||
// Full TUI frame: prompt + separator + status bar, idle state
|
||||
expect(
|
||||
agent.detectActivity("❯ \n────────────────────────────────────────────────────────────────────────────────\n ⏵⏵ bypass permissions on (shift+tab to cycle)"),
|
||||
).toBe("idle");
|
||||
});
|
||||
|
||||
it("returns idle for prompt with inline suggestion (no esc to interrupt)", () => {
|
||||
// Claude Code shows suggestions like "❯ check CI status" when idle
|
||||
expect(
|
||||
agent.detectActivity("❯ check CI status on the PR\n────────────────────────────────────────────────────────────────────────────────\n ⏵⏵ bypass permissions on (shift+tab to cycle)"),
|
||||
).toBe("idle");
|
||||
});
|
||||
|
||||
it("returns active for prompt with input being processed (esc to interrupt)", () => {
|
||||
// When Claude Code is actively processing, status bar has "esc to interrupt"
|
||||
expect(
|
||||
agent.detectActivity("❯ fix the bug\n────────────────────────────────────────────────────────────────────────────────\n ⏵⏵ bypass permissions on (shift+tab to cycle) · esc to interrupt"),
|
||||
).toBe("active");
|
||||
});
|
||||
|
||||
it("returns idle for real finished session output with suggestion", () => {
|
||||
const realOutput = [
|
||||
" - 18 new tests (12 for the command, 6 for plugin resolution) — all passing",
|
||||
"✻ Churned for 10m 56s",
|
||||
"────────────────────────────────────────────────────────────────────────────────",
|
||||
"❯ check CI status on the PR",
|
||||
"────────────────────────────────────────────────────────────────────────────────",
|
||||
" ⏵⏵ bypass permissions on (shift+tab to cycle)",
|
||||
].join("\n");
|
||||
expect(agent.detectActivity(realOutput)).toBe("idle");
|
||||
});
|
||||
});
|
||||
|
||||
// =========================================================================
|
||||
|
|
|
|||
|
|
@ -472,6 +472,13 @@ function classifyTerminalOutput(terminalOutput: string): ActivityState {
|
|||
|
||||
const lines = terminalOutput.trim().split("\n");
|
||||
|
||||
// Claude Code's status bar is the most reliable activity signal:
|
||||
// - Active: "⏵⏵ bypass permissions on ... · esc to interrupt"
|
||||
// - Idle: "⏵⏵ bypass permissions on ..." (no esc to interrupt)
|
||||
// Check BEFORE stripping decorative lines.
|
||||
const rawTail = lines.slice(-3).join("\n");
|
||||
const hasEscToInterrupt = /esc to interrupt/i.test(rawTail);
|
||||
|
||||
// Strip trailing decorative lines from Claude Code's TUI. From bottom up,
|
||||
// remove: status bar lines (contain ⏵), separator lines (all ─), and
|
||||
// empty lines. This exposes the actual prompt or content line for detection.
|
||||
|
|
@ -493,6 +500,11 @@ function classifyTerminalOutput(terminalOutput: string): ActivityState {
|
|||
// The ❯ is Claude Code's prompt character.
|
||||
if (/^[❯>$#]\s*$/.test(lastLine)) return "idle";
|
||||
|
||||
// Claude Code shows inline suggestions after the prompt when idle, e.g.
|
||||
// "❯ check CI status on the PR". Distinguish from active processing by
|
||||
// checking the status bar: no "esc to interrupt" = idle with suggestion.
|
||||
if (/^[❯>]\s+/.test(lastLine) && !hasEscToInterrupt) return "idle";
|
||||
|
||||
// Check the bottom of the buffer for permission prompts BEFORE checking
|
||||
// full-buffer active indicators. Historical "Thinking"/"Reading" text in
|
||||
// the buffer must not override a current permission prompt at the bottom.
|
||||
|
|
|
|||
Loading…
Reference in New Issue