{"slug": "claude-code-lightbar-for-mac-os", "title": "Claude Code Lightbar for Mac OS", "summary": "Claude Code Lightbar for Mac OS, a full-width red bar that displays Claude Code session status across all screens, is now available in version 1.0 exclusively for macOS. The bar shows a solid red when a session is busy, pulses when user input is needed, and disappears when a turn finishes, driven by Claude Code hooks via a local Swift app and LaunchAgent.", "body_md": "**Version 1.0** · **macOS only** (uses AppKit + `launchd`\n\n; there is no Windows or Linux build).\n\nA full-width **red bar** along one edge of every screen that tells you, from\nacross the room, what Claude Code is doing. It sits on the **top** edge by default\n(position, thickness, and color are all changeable — see [Configuration](#configuration)).\n\n**Solid red bar**— at least one session is busy (Claude working).** Pulsing red bar**— a session** needs a response from you**now (a permission prompt, or Claude blocked waiting for input).** No bar**— a turn simply finished, or nothing running.\n\nBusy wins: if any session is still working the bar is solid; it pulses only when one\nhas an active prompt for you (a permission request or an explicit question). A turn\nthat just *finishes* clears the bar. The bar is click-through (it never intercepts\nyour mouse) and floats above everything, including fullscreen apps, on every Space\nand every monitor.\n\nNote: pulsing is driven by Claude Code's\n\n`Notification`\n\nhook, which fires for a real prompt (a permission request, etc.). The ~60sidlenotification is deliberately ignored (`hook.sh`\n\nfilters the \"waiting for your input\" message), so the bar never blinks just because a finished session is sitting there. One consequence: Claude Code has no distinct event for \"ended a turn with a question,\" so a plain conversational question at the end of a turn reads as done (no bar) — only actual prompts pulse. Structured`AskUserQuestion`\n\npromptsdopulse, via a`PreToolUse`\n\n/`PostToolUse`\n\nhook on that tool.\n\n*Left: solid red along the bottom edge = Claude is working; it pulses when Claude needs a response from you. Right: the menu-bar menu for live configuration.*\n\n**macOS**(uses AppKit +`launchd`\n\n).**Xcode Command Line Tools**— provides`swiftc`\n\nto build. Install with:\n\n```\nxcode-select --install\n```\n\n**Claude Code CLI**— this is driven by[Claude Code](https://docs.anthropic.com/en/docs/claude-code)hooks. It works with any** locally-running**Claude Code (terminal or IDE extension). It does** not**work with cloud sessions (claude.ai/code) — those run remotely and can't reach your local machine.\n\nNo other dependencies: no Homebrew, no `jq`\n\n, no runtime. Just the system Swift\ncompiler and `plutil`\n\n(built into macOS).\n\n```\ngit clone https://github.com/gregsramblings/claude-status-bar.git\ncd claude-status-bar\n./install.sh\n```\n\n`install.sh`\n\nbuilds the app, installs a run-at-login LaunchAgent (paths derived from\nwherever you cloned — nothing hardcoded), starts it, then **prints a hooks block**.\nCopy that block into\n\n`~/.claude/settings.json`\n\n(merge it into the top-level JSON\nobject; if you already have a `\"hooks\"`\n\nkey, add the four events to it). The block\nlooks like this, with real paths filled in:\n\n```\n\"hooks\": {\n  \"UserPromptSubmit\": [{ \"hooks\": [{ \"type\": \"command\", \"command\": \"bash /path/to/claude-status-bar/hook.sh busy\" }] }],\n  \"Stop\":            [{ \"hooks\": [{ \"type\": \"command\", \"command\": \"bash /path/to/claude-status-bar/hook.sh waiting\" }] }],\n  \"Notification\":    [{ \"hooks\": [{ \"type\": \"command\", \"command\": \"bash /path/to/claude-status-bar/hook.sh needs_input\" }] }],\n  \"SessionEnd\":      [{ \"hooks\": [{ \"type\": \"command\", \"command\": \"bash /path/to/claude-status-bar/hook.sh end\" }] }]\n}\n```\n\nThen **start a new Claude Code session** and give it something to do — the red bar\nappears while it works. (Hooks load at session start, so already-open sessions won't\ndrive the bar until restarted.)\n\n```\necho '{\"session_id\":\"test\"}' | ./hook.sh busy    # bar should appear\necho '{\"session_id\":\"test\"}' | ./hook.sh end     # bar should disappear\n./uninstall.sh\n```\n\nStops and removes the LaunchAgent and the state directory. It intentionally does not\nedit `settings.json`\n\n— remove the four ccbar `hooks`\n\nentries yourself, then delete\nthe repo folder.\n\n- Claude Code\n**hooks** fire on state change and run`hook.sh`\n\n, which writes one small JSON file per session into`~/.claude/ccbar/state/<session_id>.json`\n\n:`UserPromptSubmit`\n\n→`busy`\n\n(you handed off; Claude is working)`Stop`\n\n→`waiting`\n\n(turn finished)`Notification`\n\n→`notify`\n\n, which`hook.sh`\n\nsplits into`needs_input`\n\n(a real prompt → pulse) or`waiting`\n\n(the idle timeout → no bar)`PreToolUse`\n\n(matching`AskUserQuestion`\n\n) →`needs_input`\n\n(pulse while a multiple-choice question waits on you);`PostToolUse`\n\n→`busy`\n\n(solid again once you answer)`SessionEnd`\n\n→ deletes the file\n\n`ccbar`\n\n(a tiny AppKit app, one borderless click-through window per screen at`.screenSaver`\n\nlevel, on all Spaces) polls the state directory every 0.4s:**solid** if any live session is`busy`\n\n, else**pulsing** if any is`needs_input`\n\n, else hidden (`waiting`\n\n— a finished turn — shows nothing).\n\nMulti-session just works: each session is its own file keyed by `session_id`\n\n, so\nthree concurrent sessions share one bar — solid if *any* is working, pulsing if *any*\nneeds your input. Stale files (older than 12h, e.g. from a crash that skipped\n`SessionEnd`\n\n) are ignored.\n\nccbar adds a small bar-shaped icon to the **macOS menu bar**. Click it to configure\nthe bar live — no rebuild, no restart:\n\n| Menu item | Default | Meaning |\n|---|---|---|\nPosition |\nTop | Pin the bar to the Top or Bottom edge. |\nThickness |\n`6` px |\nBar thickness (presets `4` –`30` ). Bump for a big room. |\nBar Color… |\nred | Opens the macOS color picker; recolors the bar live. |\nAbout ccbar |\n— | Description, author, link to this repo. |\nQuit ccbar |\n— | Stops the app (unloads the LaunchAgent so it stays down). |\n\nChoices persist in `UserDefaults`\n\nacross restarts. The remaining tunables are still\ncompile-time constants at the top of `ccbar.swift`\n\n(rebuild + restart after changing —\n`./install.sh`\n\nagain does both):\n\n| Constant | Default | Meaning |\n|---|---|---|\n`pollInterval` |\n`0.4` |\nSeconds between state-dir polls. |\n`staleAfter` |\n`43200` |\nIgnore state files older than this (seconds). |\n\n| File | Purpose |\n|---|---|\n`ccbar.swift` |\nThe whole app (AppKit, single file). |\n`hook.sh` |\nClaude Code hook target; writes one state file per session. |\n`install.sh` |\nBuild + install LaunchAgent + print the hooks block. |\n`uninstall.sh` |\nStop + remove the LaunchAgent and state dir. |\n`ccbar` |\nCompiled binary (gitignored — built by `install.sh` ). |\n\n**Fragility — the idle filter is a string match.**`hook.sh`\n\ndistinguishes the ~60s idle notification from a real prompt by matching the substring`waiting for your input`\n\nin the notification`message`\n\n. This is Claude Code's current idle wording, not a stable API. If Anthropic changes that text, the idle timeout will start pulsing the bar again. The fix is one line — update the`case`\n\npattern in`hook.sh`\n\n. (Conversely, only known idle text is suppressed, so any new message defaults to*pulse*, never to silently hiding a real prompt.)`Stop`\n\nis the main-agent stop only (`SubagentStop`\n\nis a separate, unhooked event), so the bar tracks real turn boundaries, not sub-agent churn.- The bar defaults to the top edge. Switch\n**Position → Bottom**(or the reverse) and bump** Thickness**in the menu-bar menu to taste — e.g. move it off an edge where a bottom Dock or the notch would hide it.\n\nPublic domain — [The Unlicense](/gregsramblings/claude-status-bar/blob/main/LICENSE). Do whatever you want with it, no\nattribution required, no warranty, no liability.", "url": "https://wpnews.pro/news/claude-code-lightbar-for-mac-os", "canonical_source": "https://github.com/gregsramblings/claude-status-bar", "published_at": "2026-07-25 14:50:12+00:00", "updated_at": "2026-07-25 15:23:01.673157+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools", "artificial-intelligence"], "entities": ["Claude Code", "Anthropic", "macOS", "Xcode Command Line Tools", "Swift", "LaunchAgent", "AppKit", "launchd"], "alternates": {"html": "https://wpnews.pro/news/claude-code-lightbar-for-mac-os", "markdown": "https://wpnews.pro/news/claude-code-lightbar-for-mac-os.md", "text": "https://wpnews.pro/news/claude-code-lightbar-for-mac-os.txt", "jsonld": "https://wpnews.pro/news/claude-code-lightbar-for-mac-os.jsonld"}}