No description
  • Python 88.6%
  • HTML 11.4%
Find a file
Lumorian 64f1fd35b6 fix: render tray icon and right-click menu on Wayland/Quickshell
Three related StatusNotifierItem fixes for the niri + DankMaterialShell
(Quickshell) setup, plus a regression test suite.

- Missing icon: the item answered Properties.Get but not GetAll, which
  Quickshell uses to populate an item in one call. GetAll fell through to
  GDBus (UnknownObject) so the host dropped the item. Add a GetAll handler
  (single _sni_properties() source of truth) and embed a real ARGB32
  IconPixmap (new icons.rgba_to_argb32: big-endian A,R,G,B with rowstride
  handling).

- "C" letter instead of the logo: DankMaterialShell flattens
  IconName+IconThemePath to file://<themepath>/<name>, missing our
  freedesktop theme layout, so the image failed and DMS showed a first-letter
  fallback. Set IconName to the absolute PNG path and drop IconThemePath,
  matching other tray apps that render correctly here.

- Wrong icon source: icons.py hardcoded a lowercase ~/Project/claudecode-monitor
  path that never matched the real capitalised checkout, so it silently used
  the bundled icon. Resolve claudecode-color.svg relative to the package (repo
  root), with a ~/.config/claude-monitor/icon.svg user override.

- Right-click menu: a client-side Gtk.Menu cannot anchor over the panel icon
  on Wayland. Publish a com.canonical.dbusmenu (new dbusmenu.py) and a Menu
  property so the host renders it; clicks return as dbusmenu Event calls.

Tests: add test_dbusmenu.py, test_tray.py, ARGB conversion and icon
source-path tests (138 passing).
2026-06-09 04:37:17 +08:00
.superpowers/brainstorm/340163-1780756025 chore: address code review issues (gitignore, dev deps) 2026-06-06 23:23:41 +08:00
assets feat: use Claude Code text logo for tray icon 2026-06-07 02:23:02 +08:00
docs/superpowers fix: filter stopped/zombie/terminal-less processes in scanner 2026-06-07 01:40:33 +08:00
src/claude_monitor fix: render tray icon and right-click menu on Wayland/Quickshell 2026-06-09 04:37:17 +08:00
tests fix: render tray icon and right-click menu on Wayland/Quickshell 2026-06-09 04:37:17 +08:00
.gitignore chore: address code review issues (gitignore, dev deps) 2026-06-06 23:23:41 +08:00
claude-color.svg feat: use official Claude SVG logo for tray icons 2026-06-07 02:18:51 +08:00
claudecode-color.svg feat: use official Claude SVG logo for tray icons 2026-06-07 02:18:51 +08:00
pyproject.toml feat: render tray + notification icons from claudecode-color.svg 2026-06-07 16:58:44 +08:00
README.md docs: add bilingual README (en/zh) with full project documentation 2026-06-07 19:01:40 +08:00
README_zh.md docs: add bilingual README (en/zh) with full project documentation 2026-06-07 19:01:40 +08:00
uv.lock fix: address final review issues (remove unused dep, add public API methods, fix enum values) 2026-06-07 00:18:43 +08:00

Claude Code Monitor

A Linux system tray application that monitors Claude Code sessions, displays status via tray icon, sends desktop notifications when Claude needs your input/selection, and provides a card-style sessions overview.

Features

  • System Tray Icon — Claude Code logo with per-state corner badges (green=running, blue=thinking, amber!=waiting, red X=error, dimmed=idle)
  • Desktop Notifications — Alerts you when Claude waits for input or prompts a selection ("Do you want to proceed?")
  • Auto-dismiss — Notification closes automatically once you respond
  • Notification Sound — Plays a sound on alert (configurable)
  • Session Cards — Pop-up window with full session detail (path, state, PID) — un-truncated, no DBusMenu width cap
  • Right-click Menu — Per-session state icons + short status text
  • Waits-for-selection vs waits-for-input — Notifications and labels distinguish the two ("等待选择" vs "等待输入")

How it works

Waiting/selection detection uses Claude Code's Notification hook (notification_type: permission_prompt, idle_prompt) — the only reliable signal for "Claude is blocked on the user". The hook script (hook_script.py, installed to ~/.local/share/claude-monitor/) writes $XDG_RUNTIME_DIR/claude-monitor/session-<id>.json on hook events. The tray app polls these files.

/proc polling is not used for waiting detection (it cannot distinguish a selection menu from tool execution). The scanner (scanner.py) only runs as a fallback when hooks aren't installed — it reports sessions as RUNNING, never WAITING.

Requirements

  • Python >= 3.12
  • System packages (Arch):
    sudo pacman -S python-gobject libayatana-appindicator3 libnotify cairo librsvg python-cairo
    
  • Notification daemon (DMS, dunst, mako, swaync, etc.)

Installation

git clone https://git.lumorian.org/Lumorian/Claudecode-monitor.git
cd Claudecode-monitor

# Install dependencies
pip install -e .

# Register Claude Code hooks (required!)
claude-monitor --install-hooks

--install-hooks merges entries into ~/.claude/settings.json (with a backup) and coexists with hooks from other tools. Restart any running Claude Code sessions after installing — hooks load at session start.

To remove later: claude-monitor --uninstall-hooks.

Usage

claude-monitor

The icon appears in your system tray. Right-click to see sessions. Click "查看会话" for a card overview.

Configuration

~/.config/claude-monitor/config.json (auto-created on first run):

{
  "scan_interval_sec": 1,
  "notify_cooldown_sec": 5,
  "enable_notifications": true,
  "notify_on_error": true,
  "notify_sound": "message"
}
Key Default Description
scan_interval_sec 1 Poll interval for hook files (seconds)
notify_cooldown_sec 5 Min seconds between notifications
enable_notifications true Whether to send desktop notifications
notify_on_error true Notify on session errors
notify_sound "message" freedesktop sound event, file path, or "" to disable

States

Icon Badge State Description
Dimmed logo Idle No Claude Code sessions
Green dot Running Session active
Blue dot Thinking Claude generating
Amber ! Waiting Needs input → notification
Red X Error Session error

Project Structure

src/claude_monitor/
├── main.py            # Entry point + --install-hooks / --uninstall-hooks
├── app.py             # Orchestrator (hook source → state → notify → tray)
├── models.py          # Data models + state_label helper
├── paths.py           # Shared runtime/cache paths
├── icons.py           # SVG → PNG rendering (cairo/Rsvg), state badges
├── hook_script.py     # Claude Code hook (stdlib-only, writes session files)
├── hook_installer.py  # Registers hooks in ~/.claude/settings.json
├── hook_watcher.py    # HookSource — reads session state files
├── state.py           # State manager + transition events
├── notifier.py        # Desktop notifications (notify-send + gdbus auto-close)
├── scanner.py         # Fallback live-process discovery (pid → RUNNING only)
├── tray.py            # System tray icon + context menu
└── window.py          # Card-style sessions overview popup
tests/
├── conftest.py        # Shared fixtures
├── test_models.py     # 19 tests (model creation, state_label)
├── test_scanner.py    # 11 tests (process filter + cwd fallback)
├── test_state.py      # 11 tests (session_id keying + callbacks)
├── test_notifier.py   # 19 tests (notify, cooldown, ID capture, close, sound)
├── test_hook_source.py   # 7 tests (HookSource reads + prunes)
├── test_hook_script.py   # 8 tests (hook event → state mapping)
├── test_hook_installer.py # 6 tests (merge, idempotent, uninstall)
└── test_icons.py         # 10 tests (SVG rendering, glyphs)

Development

pip install -e ".[dev]"

# Run tests (96 pass)
pytest tests/ -v

# Coverage
pytest --cov=src --cov-report=term-missing

License

MIT