Skip to content

feat: improve thinking detection and add usage overlay - #3

Merged
MartianGreed merged 1 commit into
mainfrom
feat/thinking-detection-usage-overlay
Jan 26, 2026
Merged

MartianGreed merged 1 commit into
mainfrom
feat/thinking-detection-usage-overlay

Conversation

@MartianGreed

Copy link
Copy Markdown
Owner

Summary

  • Improve thinking state detection with new patterns for thought for Xs, (esc to cancel), (ctrl+c to interrupt), unicode ellipsis, and lowercase variants
  • Add global usage overlay (press u) showing active sessions and all-time usage across projects
  • Persist Claude session ID to survive app restarts

Test plan

  • All tests pass (make test)
  • Linter passes (make lint)
  • Build succeeds (make build)

@greptile-apps

greptile-apps Bot commented Jan 21, 2026

Copy link
Copy Markdown

Greptile Summary

Enhanced thinking state detection with new patterns (thinking..., thinking…, reasoning, (esc to cancel), (ctrl+c to interrupt), thought for Xs) and added global usage overlay (press u) showing active sessions and all-time usage across projects. Persisted Claude session ID to SQLite database to maintain usage tracking across app restarts.

Key Changes:

  • Improved thinking detection with case-insensitive patterns and unicode ellipsis support
  • Added database migration for claude_session_id column in sessions table
  • Implemented global usage aggregation across all Claude projects
  • Modified header to show total usage across all active sessions instead of selected session only
  • Added store dependency to Monitor for session ID persistence

Issues Found:

  • Race condition in internal/tui/model.go:572-577 where goroutine updates m.globalUsage without synchronization while UI reads it concurrently
  • Overly broad regex pattern thinking in internal/claude/detector.go:83 will match any text containing "thinking", causing false positives

Confidence Score: 3/5

  • This PR has useful features but contains a race condition that needs fixing before merging
  • Score reflects one critical race condition in the usage overlay feature where a goroutine writes to m.globalUsage while the UI reads it without synchronization, plus an overly broad regex pattern that may cause false positive thinking state detections. The session ID persistence and usage aggregation logic is well-implemented with proper error handling.
  • Pay close attention to internal/tui/model.go for the race condition fix and internal/claude/detector.go for the overly broad thinking pattern

Important Files Changed

Filename Overview
internal/claude/detector.go Enhanced thinking detection patterns but added overly broad thinking regex that may cause false positives; removed (?m)>\s*$ idle pattern
internal/tui/model.go Added usage overlay feature with goroutine that creates race condition accessing m.globalUsage without synchronization
internal/daemon/monitor.go Added store dependency and Claude session ID persistence logic to survive app restarts

Sequence Diagram

sequenceDiagram
    participant User
    participant TUI as TUI Model
    participant Monitor
    participant Store as SQLite Store
    participant Detector
    participant Usage as Usage Parser
    participant FS as File System

    Note over User,FS: App Initialization
    User->>TUI: Start ccmanager
    TUI->>Monitor: NewMonitor(pollInterval, store)
    Monitor->>Detector: NewDetector()
    Monitor->>Store: Reference stored

    Note over Monitor,FS: Session Discovery & Persistence
    Monitor->>Monitor: poll()
    Monitor->>Detector: DetectState(content)
    Detector-->>Monitor: StateThinking/StateIdle/StateUrgent
    Monitor->>Store: GetClaudeSessionID(tmuxSession)
    Store-->>Monitor: claudeSessionID (from DB)
    
    alt No persisted session ID
        Monitor->>Usage: FindActiveSessionID(workingDir)
        Usage->>FS: Read .claude/projects/*/session_*.jsonl
        FS-->>Usage: Active session ID
        Usage-->>Monitor: claudeSessionID
        Monitor->>Store: CreateSession(tmuxSession)
        Monitor->>Store: SetClaudeSessionID(tmuxSession, claudeSessionID)
    end
    
    Monitor->>Usage: GetSessionByID(workingDir, claudeSessionID)
    Usage->>FS: Parse session JSONL file
    FS-->>Usage: Session data
    Usage-->>Monitor: SessionUsage with tokens & cost

    Note over User,FS: Usage Overlay Feature (Press 'u')
    User->>TUI: Press 'u' key
    TUI->>TUI: showUsage = true
    TUI->>TUI: Launch goroutine
    TUI->>Usage: GetGlobalUsage()
    Usage->>FS: ReadDir(.claude/projects)
    Usage->>FS: FindSessionFiles for each project
    Usage->>FS: ParseSessionFile for each session
    FS-->>Usage: Token data from all sessions
    Usage-->>TUI: GlobalUsage (aggregated)
    TUI->>TUI: Set m.globalUsage (race condition)
    TUI->>User: Display usage overlay

    Note over TUI,Store: Enhanced Thinking Detection
    Monitor->>Detector: DetectState(terminal content)
    Detector->>Detector: Match thinking patterns
    Note over Detector: New patterns: thinking..., thinking…,<br/>reasoning, (esc to cancel),<br/>(ctrl+c to interrupt), thought for Xs
    Detector-->>Monitor: StateThinking
    Monitor-->>TUI: Session state update
Loading

@greptile-apps greptile-apps Bot 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.

9 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment thread internal/tui/model.go
Comment on lines +572 to +577
case "u":
m.showUsage = true
go func() {
global, _ := usage.GetGlobalUsage()
m.globalUsage = global
}()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

logic: Race condition: the goroutine updates m.globalUsage without synchronization while the UI reads it in viewUsageOverlay() on line 736-741 of view.go. This can cause data races.

Suggested change
case "u":
m.showUsage = true
go func() {
global, _ := usage.GetGlobalUsage()
m.globalUsage = global
}()
case "u":
m.showUsage = true
return tea.Cmd(func() tea.Msg {
global, _ := usage.GetGlobalUsage()
return messages.UsageLoaded{Usage: global}
})
Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/tui/model.go
Line: 572:577

Comment:
**logic:** Race condition: the goroutine updates `m.globalUsage` without synchronization while the UI reads it in `viewUsageOverlay()` on line 736-741 of view.go. This can cause data races.

```suggestion
	case "u":
		m.showUsage = true
		return tea.Cmd(func() tea.Msg {
			global, _ := usage.GetGlobalUsage()
			return messages.UsageLoaded{Usage: global}
		})
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +74 to +83
regexp.MustCompile(`(?i)thinking\.{3}`),
regexp.MustCompile(`(?i)thinking…`),
regexp.MustCompile(`(?i)reasoning`),
regexp.MustCompile(`⠋|⠙|⠹|⠸|⠼|⠴|⠦|⠧|⠇|⠏`),
regexp.MustCompile(`Working\.\.\.`),
regexp.MustCompile(`Processing\.\.\.`),
regexp.MustCompile(`\(esc to cancel\)`),
regexp.MustCompile(`\(ctrl.* to interrupt\)`),
regexp.MustCompile(`thought for \d+`),
regexp.MustCompile(`thinking`),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

logic: Overly broad pattern: the regexp.MustCompile(\thinking`)` pattern on line 83 will match any text containing "thinking" (e.g., "I'm thinking about the problem" in user input). This is too broad and will cause false positives. The case-insensitive patterns on lines 74-76 already cover thinking states properly.

Suggested change
regexp.MustCompile(`(?i)thinking\.{3}`),
regexp.MustCompile(`(?i)thinking…`),
regexp.MustCompile(`(?i)reasoning`),
regexp.MustCompile(`⠋|⠙|⠹|⠸|⠼|⠴|⠦|⠧|⠇|⠏`),
regexp.MustCompile(`Working\.\.\.`),
regexp.MustCompile(`Processing\.\.\.`),
regexp.MustCompile(`\(esc to cancel\)`),
regexp.MustCompile(`\(ctrl.* to interrupt\)`),
regexp.MustCompile(`thought for \d+`),
regexp.MustCompile(`thinking`),
thinkingPatterns: []*regexp.Regexp{
regexp.MustCompile(`(?i)thinking\.{3}`),
regexp.MustCompile(`(?i)thinking…`),
regexp.MustCompile(`(?i)reasoning`),
regexp.MustCompile(`⠋|⠙|⠹|⠸|⠼|⠴|⠦|⠧|⠇|⠏`),
regexp.MustCompile(`Working\.\.\.`),
regexp.MustCompile(`Processing\.\.\.`),
regexp.MustCompile(`\(esc to cancel\)`),
regexp.MustCompile(`\(ctrl.* to interrupt\)`),
regexp.MustCompile(`thought for \d+`),
},

Was the standalone "thinking" pattern intentional for matching lowercase thinking states without punctuation?

Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/claude/detector.go
Line: 74:83

Comment:
**logic:** Overly broad pattern: the `regexp.MustCompile(\`thinking\`)` pattern on line 83 will match any text containing "thinking" (e.g., "I'm thinking about the problem" in user input). This is too broad and will cause false positives. The case-insensitive patterns on lines 74-76 already cover thinking states properly.

```suggestion
		thinkingPatterns: []*regexp.Regexp{
			regexp.MustCompile(`(?i)thinking\.{3}`),
			regexp.MustCompile(`(?i)thinking…`),
			regexp.MustCompile(`(?i)reasoning`),
			regexp.MustCompile(`⠋|⠙|⠹|⠸|⠼|⠴|⠦|⠧|⠇|⠏`),
			regexp.MustCompile(`Working\.\.\.`),
			regexp.MustCompile(`Processing\.\.\.`),
			regexp.MustCompile(`\(esc to cancel\)`),
			regexp.MustCompile(`\(ctrl.* to interrupt\)`),
			regexp.MustCompile(`thought for \d+`),
		},
```

 Was the standalone "thinking" pattern intentional for matching lowercase thinking states without punctuation?

How can I resolve this? If you propose a fix, please make it concise.

- Send double Enter only for multi-line content (3+ lines) to signal paste completion
- Fix mainHeight calculation to prevent UI extending beyond screen
- Improve thinking state detection patterns
- Add global usage overlay (press 'u')
- Persist Claude session ID for usage tracking across restarts
@MartianGreed
MartianGreed force-pushed the feat/thinking-detection-usage-overlay branch from 72e445b to d637996 Compare January 22, 2026 16:52
@MartianGreed
MartianGreed merged commit 3dbb43d into main Jan 26, 2026
6 checks passed
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.

1 participant