Floating desktop traffic light for the Claude Code plugin inside VS Code on macOS and Windows.
Claude Status Light sits on your desktop and mirrors Claude Code session state in one glance while you work in VS Code:
yellow solid= Claude is runningred blinking= Claude is waiting for yougreen solid= Claude answered and is done with the current turn
It is built with Tauri + React, plus a small local hook bridge that listens to Claude Code hooks and writes shared state for the desktop app.
Current scope:
- supported workflow: Claude Code inside VS Code
- current docs and release testing are written around the VS Code plugin flow
- other Claude Code surfaces may work if they emit the same hooks, but they are not the primary documented target today
- Fix: button cutoff in expanded state — v0.3.0 added a profile label row to the usage panel but did not increase the window height, causing the bottom two buttons to be clipped. Window height increased from 418 to 432 px.
- Account switching across config paths — a new tray Account submenu lists every Claude config directory found in your home folder (
~/.claude,~/.claude-company, …). Pick one and the usage dials track that account; the choice persists across restarts. See "Switching Accounts" below. - Per-path credentials — the OAuth token is read from the selected path's
.credentials.json, or on macOS from the Keychain entry Claude apps create per config dir, so any app logged into a path can be tracked. - No more silent failures — if the selected path has no usable login (missing or expired token, or the API rejects it), the panel shows
NO ACTIVE LOGINnaming the path instead of hiding the dials. Transient network errors still keep the last good values. - Hooks everywhere — automatic hook setup now installs the bridge into every discovered config path, so sessions from any of them drive the light.
- Fixed bundled bridge resolution — installed builds now find the bridge under
Resources/_up_/bridge(previously they silently fell back to a dev path, breaking hook setup on machines without the repo).
Windows-focused fixes:
- No more border halo on Windows — calls
DwmSetWindowAttribute(DWMWA_BORDER_COLOR, DWMWA_COLOR_NONE)on startup to remove the Windows 11 system-drawn border highlight around the transparent window. - Transparent background — explicitly sets
background: transparenton the root HTML element so WebView2 never renders a white backdrop before CSS loads.
macOS behavior is unchanged.
macOS-focused fixes:
- Usage dials work on macOS — the app now reads the Claude OAuth token from the login Keychain, where Claude Code stores credentials on macOS, so the Session (5h) and Weekly (7d) dials populate (previously it only read
~/.claude/.credentials.json, which macOS doesn't use). - Truly transparent window — enabled the macOS private-API path so the window background renders see-through instead of opaque.
- No more shadow halo — disabled the native window drop shadow that drew a rectangular outline around the custom-shaped light housing.
Windows build configuration and behavior are unchanged; all changes are macOS-gated or visual-only.
- Plan usage dials — Session (5h) and Weekly (7d) usage, mirrored from Claude Code's
/usage, shown as two ring gauges below the light. Orange-yellow under 80%, orange at 80%+. - Show/Hide Details — collapse everything below the traffic light down to a compact signal, from the tray menu or the in-window chevron button; the window resizes to match.
- Sharper contrast — status and usage text now stay legible on any desktop wallpaper.
Claude Code inside VS Code often lives inside a busy editor window. This app makes status ambient:
- keep coding or reading on another screen
- notice immediately when Claude needs input
- hear a sound when status changes
- glance at your Claude plan usage (5-hour and weekly windows)
- run the same idea on Windows and macOS
Releases: github.com/guu5ama/claude-status-light/releases
Current release assets:
- Windows installer:
.msi - Windows portable:
.zip - macOS test build: unsigned
aarch64 .dmg
macOS note:
- the current DMG is for Apple Silicon
- it is unsigned, so Gatekeeper warnings are expected during testing
- Claude Code fires hooks for
UserPromptSubmit,Notification,PreToolUse(AskUserQuestion),PostToolUse, andStop. bridge/claude-hook.mjsreceives the hook payload onstdin.- The bridge writes normalized state to a shared
state.json. - The desktop app polls that state and updates the light and sounds.
The app also tries to configure Claude hooks automatically on startup.
This project is currently aimed at one main use case:
- you use Claude Code inside VS Code
- you want a floating desktop indicator outside the editor
- you want hook setup to be mostly automatic
The underlying hook system is shared Claude Code behavior, so other surfaces may work. But the README, release flow, and troubleshooting guidance are intentionally centered on the VS Code plugin experience first.
UserPromptSubmit->runningNotification(permission_prompt | idle_prompt | elicitation_dialog)->pending_userPreToolUse(AskUserQuestion)->pending_userPostToolUse->running(so the light leaves red after you answer a question or approve a permission, since those don't fireUserPromptSubmit)Stop:- wait-for-user language ->
pending_user - explicit completion language ->
done - direct answer with no follow-up request ->
done
- wait-for-user language ->
This means a plain factual answer should turn green even if it does not literally say "done".
Below the traffic light, two circular dials mirror the same plan usage shown by Claude Code's /usage:
5H= the 5-hour session window7D= the 7-day weekly window
Each dial shows the percentage used in the center and resets in Xh below. The ring and percentage are orange-yellow below 80% and orange at 80% and above.
How it is fetched:
- the Tauri backend reads the OAuth token belonging to the selected config path (see "Switching Accounts" below) and calls the official
https://api.anthropic.com/api/oauth/usageendpoint - the token comes from
<config dir>/.credentials.json, or on macOS from the login Keychain entry that Claude apps create for that config dir - it is polled at a low frequency (every 5 minutes) because the endpoint rate-limits aggressively
- on transient errors (network, rate limits) the last known values are kept; nothing is ever sent anywhere, usage percentages are only read back for display
- if the selected config path has no credentials, an expired token, or the API rejects the token, the panel shows
NO ACTIVE LOGINwith the config path — sign in again with whatever Claude app uses that path
If you run Claude with more than one account — for example a personal account in ~/.claude and a company account in ~/.claude-company via CLAUDE_CONFIG_DIR — the tray menu gets an Account submenu listing every Claude config directory found in your home folder (.claude* directories that contain Claude footprints such as settings.json or projects/).
- pick a config path in the Account submenu to point the usage dials at that account
- the small path label above the dials shows which config path is being tracked
- the choice is remembered across restarts (stored in the app's own
profiles.json, never inside any Claude config dir) - the app never logs in or out for you; it only reads tokens that Claude apps already stored for each path, so any app (Claude Code CLI, the VS Code extension, or whatever replaces them) logged into a path can be tracked
- config dirs outside your home folder can be added manually to
profiles.jsonviaextraConfigDirs
On startup, the app tries to manage settings.json in every discovered Claude config directory (~/.claude, ~/.claude-company, …) so sessions from any of them drive the light.
Behavior:
- merges only the Claude Status Light hooks it needs
- preserves unrelated existing hooks
- backs up the original
settings.jsonbefore any real write - treats the current app location as the only valid Claude Status Light bridge path
- removes stale Claude Status Light paths from old repos or moved portable folders
- tolerates UTF-8 BOM in
settings.json
If the app rewrites hooks:
- it shows
HOOKS UPDATED - it shows the active bridge path and backup path
- the Claude Code panel in VS Code should be reopened so it reloads the new hook path
Tray controls:
Open/HideSound On/OffShow/Hide DetailsConfigure Claude HooksReconnect SessionQuit
Reconnect Session resets binding to idle_unbound so the next real Claude event can claim the app cleanly.
Show/Hide Details (also available as the chevron button in the window) collapses the area below the traffic light and shrinks the window down to just the light and the control buttons.
Windows:
- Claude settings path:
C:\Users\<user>\.claude\settings.json - installed and portable builds store runtime state under local app data
macOS:
- Claude settings path:
/Users/<user>/.claude/settings.json - app ships as native
.app/.dmg, not Docker
Sounds:
- prefer local MP3 assets in
public/sounds/ - fall back to synthesized tones if file playback is unavailable
Requirements:
- Node.js 20+
- npm
- Rust + Cargo
- Tauri prerequisites for your platform
Platform prerequisites:
- Windows: Visual Studio C++ Build Tools
- macOS: Xcode Command Line Tools via
xcode-select --install
Install:
npm installRun tests and frontend build:
npm test
npm run buildRun desktop app in development:
npm run tauri:devBuild release packages:
Windows:
npm run tauri:build:windowsmacOS:
npm run tauri:build:macImportant:
- run
npm run tauri:build:windowson Windows - run
npm run tauri:build:macon a Mac, or use the GitHub Actions macOS workflow
Automatic setup is the default and recommended path. For development or troubleshooting, a local checkout can be wired manually like this:
{
"hooks": {
"UserPromptSubmit": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "node \"C:/code/claude-status-light/bridge/claude-hook.mjs\""
}
]
}
],
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "node \"C:/code/claude-status-light/bridge/claude-hook.mjs\""
}
]
}
],
"PreToolUse": [
{
"matcher": "AskUserQuestion",
"hooks": [
{
"type": "command",
"command": "node \"C:/code/claude-status-light/bridge/claude-hook.mjs\""
}
]
}
],
"PostToolUse": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "node \"C:/code/claude-status-light/bridge/claude-hook.mjs\""
}
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "node \"C:/code/claude-status-light/bridge/claude-hook.mjs\""
}
]
}
]
}
}After manual edits:
- Reopen Claude Code.
- Click
Reconnect Session. - Send a new Claude message.
Bridge details: bridge/README.md
The light stays idle_unbound
- Claude hooks are missing or malformed in
~/.claude/settings.json - the Claude Code panel in VS Code has not been reopened after hook changes
Reconnect Sessionwas not used after manual bridge tests
The label says SETUP NEEDED
- automatic Claude hook configuration failed
- rerun
Configure Claude Hooks - inspect
~/.claude/settings.json - enable debug logging if needed
The light stays yellow forever
- check whether
Stopis arriving - check whether
PreToolUse(AskUserQuestion)is configured - inspect
lastMessageTextin the state file
macOS build exists but the app will not open
- unsigned local builds may be blocked by Gatekeeper
- wider distribution will need Apple signing and notarization
Current verification status:
- GitHub Actions
macOS Verifypasses onmacos-latest - the workflow runs
npm test - the workflow runs
npm run build - the workflow runs
npm run tauri:build:mac -- --no-sign - the workflow uploads unsigned
.appand.dmgartifacts
Still recommended on a real Mac:
- tray behavior
- transparent always-on-top window behavior
- drag behavior
- local sound playback
- real Claude hook flow end-to-end

