Skip to content

fix: detect Claude Code status transitions via capture-pane when hooks do not fire#69

Merged
110y merged 1 commit into
mainfrom
escape-status
Jun 10, 2026
Merged

fix: detect Claude Code status transitions via capture-pane when hooks do not fire#69
110y merged 1 commit into
mainfrom
escape-status

Conversation

@110y

@110y 110y commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Claude Code sessions could show a wrong status whenever the corresponding hook did not fire (or fired in an unrecognized form): a session stayed running after Escape, bounced waiting → running → idle on cancellation, was briefly shown idle right after approving a permission prompt, flickered running ↔ idle mid-turn, and went idle → running instead of idle → waiting when a permission prompt appeared.

This change makes the monitor derive Claude Code status transitions from the terminal (tmux capture-pane) as a fallback:

  • running → idle when the agent was interrupted with no JSONL trace and no Stop hook (e.g. Escape right after submitting), requiring a sustained idle window so brief no-spinner gaps mid-turn do not flicker the status.
  • running/idle → waiting when a permission prompt is visible, using a strict prompt-chrome matcher (footer hints and option lists) so assistant prose like "Would you like me to…?" is not mistaken for a prompt.
  • waiting → idle directly on cancellation and waiting → running immediately on approval (terminal activity or a fresh transcript turn), never bouncing through the wrong state.
  • Interruption/API-error detection now inspects the last conversation line, so trailing bookkeeping markers (last-prompt, file-history-snapshot) no longer mask them.
  • Busy detection matches the live status line signals (↑/↓ N tokens readout, esc to interrupt) across the whole pane, so a multi-line composer draft cannot hide them.

The monitor heartbeat now uses a build ID (version + executable size + mtime) instead of the version alone, so a rebuilt binary restarts a stale monitor during local development.

Copilot AI review requested due to automatic review settings June 10, 2026 13:54
@110y 110y merged commit b25e61b into main Jun 10, 2026
5 checks passed
@110y 110y deleted the escape-status branch June 10, 2026 13:57
@muxac-cd muxac-cd Bot mentioned this pull request Jun 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the Claude Code session monitor so status transitions remain accurate even when tmux hooks are missing, delayed, or unrecognized, by deriving state from tmux capture-pane output and by making monitor heartbeats sensitive to local rebuilds.

Changes:

  • Add capture-pane-based Claude activity/prompt detection with debounced transitions to prevent running↔idle flicker and incorrect waiting handling.
  • Fix interruption/API-error detection to inspect the last conversation JSONL line (ignoring trailing bookkeeping markers).
  • Replace heartbeat “version” matching with a build ID (version + executable size + mtime) to restart stale monitors during local development.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
internal/monitor/monitor.go Adds Claude capture-pane fallback logic (activity, permission prompt detection, debounced transitions) and improves JSONL tail interpretation; heartbeat now writes build ID.
internal/monitor/monitor_test.go Adds extensive coverage for the new Claude status transition heuristics and prompt/activity matchers.
internal/monitor/ensure.go Introduces monitorBuildID and uses it to decide whether a running monitor is “current”.
internal/monitor/ensure_test.go Updates EnsureRunning tests to assert build-ID-based heartbeat matching.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +502 to +505
output, err := tmuxRunner.CapturePane(ctx, tmuxName)
if err != nil {
return nil // non-fatal
}
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.

2 participants