Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

41 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CC Companion

CC Companion Icon

A desktop companion app that monitors your Claude Code sessions in real time. Built with Electron.

CC Companion is a lightweight always-on-top window that sits at the top of your screen, tracking every Claude Code instance running on your machine — which project it's in, whether it's actively working or idle, CPU and memory usage, token consumption, conversation turns, and how long it's been in each state.

demo.mp4
Light mode Light mode working Settings panel
Light mode Working state Settings + detail
Dark mode detail Session history
Dark mode with detail panel Session history

Features

Claude Code Instance Tracking

  • Auto-detects all running Claude Code processes (filters out Claude desktop app and subagent processes)
  • Per-instance tile showing project name, model, status, and context usage
  • Session analytics: turn count, input/output token usage, context usage, model name — read directly from Claude's session files
  • Session timing: start time (with date if not today) and total elapsed time shown in detail panel
  • Anti-flicker: 3-second idle grace period prevents flickering between working/ready during brief pauses between tool calls
  • Smart timer reset: timer resets when a new user turn starts, so quick back-and-forth exchanges get fresh timers
  • Instances display in a 2-column grid, scrollable when more than 6 are running
  • Rename instances — right-click any tile to give it a custom name (useful when running multiple instances in the same project folder)
  • Close instances — right-click a tile and confirm to terminate a Claude Code session (terminal tab stays open)
  • Click any instance tile to focus its terminal window — works across Terminal.app, iTerm2, Ghostty, WezTerm, kitty, Cursor, and VS Code (see Terminal support)
  • Drag tiles to reorder them

Session History

  • History panel (⏳) — browse your last 50 Claude Code sessions across all projects
  • Each entry shows project name, first message, turn count, and relative time
  • Resume button — opens a new Terminal tab with claude --resume in the correct project directory
  • Reads directly from ~/.claude/history.jsonl — no external services

Settings Panel

Gear button opens a floating popup with all settings (persisted across restarts):

  • Theme — toggle between Dark and Light mode
  • Show Work Timer — display elapsed working time on active instance tiles
  • Show Ready Timer — display elapsed idle time on ready instance tiles
  • Opacity — background transparency (Light 80% / Mid 90% / Full 100%) — only affects background, not text

Instance Detail

Click the button on any tile to view detailed stats:

  • Session start time and total elapsed
  • Model and git branch
  • Context usage percentage
  • Input/output/cached token counts
  • CPU and memory usage
  • Working directory path

Controls

  • Settings (⚙) — open settings panel
  • History (⏳) — browse and resume past sessions
  • Center (⊙) — snap the island to center-top of screen
  • Minimize (−) — minimize to dock (native macOS animation)
  • Quit (✕) — exit the app
  • Tooltips — hover any button to see its function

Getting Started

Run from source (recommended)

git clone https://github.com/jiahongc/cc-companion.git
cd cc-companion
npm install
npm start

Requires Node.js v18+.

DMG download

A pre-built DMG is available on the Releases page. Since the app is unsigned, macOS will block it on first launch. After dragging to Applications, run:

xattr -cr /Applications/CC\ Companion.app

Project Structure

cc-companion/
├── electron/
│   ├── main.js          # Electron main process, IPC handlers, window management
│   ├── preload.js       # Context bridge API for renderer
│   └── watcher.js       # Claude Code process detection, session analytics
├── src/
│   ├── compact.html     # Dynamic Island window
│   ├── compact.css      # Dynamic Island styles
│   └── compact.js       # Dynamic Island renderer
├── test/
│   └── watcher.test.js  # 66 tests covering detection, state, tokens, formatting
├── assets/
│   ├── icon_1024.png    # App icon (1024x1024 source)
│   ├── icon.icns        # macOS app icon
│   └── iconTemplate.png # Tray icon
└── package.json

How It Works

Process Detection

The watcher polls ps every 2 seconds to find Claude CLI processes (case-insensitive), filtering out the Claude desktop app, helper processes, subagent child processes, and Electron/system binaries. It resolves each process's working directory via lsof -d cwd to get the project name. Async instance initialization is guarded against duplicate creation during the discovery window.

Activity detection uses a multi-signal approach with tiered staleness:

  1. JSONL state (primary) — reads the last entry from Claude's session JSONL to determine ground truth. Some entries are immediately idle (end_turn, system, file-history-snapshot). Active entries get entry-type-specific staleness thresholds:

    Entry Staleness Rationale
    assistant(null) 10s Streaming is continuous; 10s silence = interrupted
    assistant(tool_use) 5 min Tools (builds, browser) run long without writes
    progress 5 min Subagents run long without writes
    user 2 min Claude should start responding within 2 min
    queue-operation 30s Quick task notifications
    result 30s Tool output; Claude should pick up quickly
  2. CPU fallback — beyond any staleness threshold, if CPU >= 5%, the instance is still treated as active. Also used when no JSONL file exists yet (brand new process).

  3. Idle grace period — when transitioning from active to idle, the watcher waits 3 seconds (2 consecutive polls) before confirming the transition. This prevents UI flickering during brief pauses between tool calls or multi-step responses.

  4. Turn-aware timer reset — the working timer resets when a new user turn is detected (turn count increases on idle→active transition), so quick exchanges get fresh timers instead of accumulating from the original session start.

State transitions (active → idle, idle → active) are timestamped for duration tracking.

Session Analytics

For each detected instance, the watcher reads:

  • ~/.claude/sessions/{pid}.json — session ID and start time
  • ~/.claude/projects/{project-key}/{session-id}.jsonl — conversation log

From the JSONL it extracts:

  • Turn count — real user prompts (excludes tool-use results, auto-compact summaries, and meta/system-injected messages)
  • Token usage — input, output, cache read, cache creation tokens
  • Context tokens — current context window fill (input + cache read + cache creation from last entry)
  • Model — which Claude model is active (Fable 5, Opus 4.8, Opus 4.7, Opus 4.6, Sonnet 4.6, Haiku 4.5, and earlier)
  • Git branch — current branch name

Context limit detection

The context-usage bar denominator adapts to the model and whether the 1M-token beta is active:

Model Default With 1M beta
Fable 5, Opus 4.8, Opus 4.7, Opus 4.6, Sonnet 4.6 200k 1M
Opus 4.5 / 4.1 / 4, Sonnet 4.5 / 4, all Haiku 200k 200k (no beta support)

The 1M beta is detected from two signals:

  1. CLAUDE_CODE_ENABLE_1M_CONTEXT=1 in the Claude process env (set via shell or settings.json's env block) — sniffed once via ps eww at instance init.
  2. Observed context usage exceeding 200k — catches the /model claude-opus-4-7[1m] runtime toggle, which isn't persisted to env or disk.

Stats refresh every 5 seconds. Only emits updates when data actually changes (deduplicated via snapshot key).

Terminal support

Clicking an instance tile brings the hosting terminal tab to the front. The best focus mechanism depends on what the terminal exposes:

Terminal Mechanism Notes
Terminal.app Native AppleScript, TTY match Works out of the box
iTerm2 Native AppleScript, TTY match Works out of the box
Ghostty Native AppleScript, CWD match Works out of the box
WezTerm wezterm cli activate-pane, CWD match Works out of the box — uses the wezterm CLI
kitty kitty @ focus-tab, CWD match Requires allow_remote_control yes and a listen_on socket in kitty.conf
Cursor / VS Code System Events, window-title match Works because these set their window title to the project name
Warp, Alacritty, Hyper, Rio, Tabby System Events fallback Best-effort — will at least bring the app to the front

Testing

npm test          # run all 66 tests
npm run test:watch  # watch mode

Tests cover activity detection, state transitions, idle grace period, timer resets, session reset on /clear, model switching, token counting, snapshot deduplication, duplicate prevention, and formatting helpers.

Build from Source

To package as a standalone .dmg:

npm run build:mac

Output goes to the dist/ folder.

Security & Privacy

  • Local only — CC Companion runs entirely on your machine. No data is sent to any server, no network requests are made, no telemetry or analytics.
  • Read-only — The app only reads Claude Code's session files (~/.claude/sessions/, ~/.claude/projects/). It never writes to them or modifies your Claude sessions in any way.
  • No secrets — The app does not access, store, or transmit API keys, tokens, or credentials. It reads process metadata (ps) and session JSONL files, which contain conversation structure but not your API keys.
  • Process isolation — Electron runs with contextIsolation: true and nodeIntegration: false. The renderer communicates with the main process only through a restricted preload API.
  • Open source — Run from source so you can verify the code yourself before running it.

Contributing

  1. Fork this repo
  2. Create a feature branch (git checkout -b feature/my-feature)
  3. Commit your changes (git commit -am 'Add my feature')
  4. Push to the branch (git push origin feature/my-feature)
  5. Open a Pull Request

License

MIT

About

A desktop companion app for Claude Code — monitors multiple instances all in one view

Resources

Stars

13 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages