{"slug": "claude-red-bar-visual-and-audible-notifier-of-task-completion", "title": "Claude Red Bar (visual and audible notifier of task completion)", "summary": "Claude Red Bar 1.0, a macOS-only visual and audible notifier for Claude Code, displays a full-width red bar on screen edges to indicate when Claude is working (solid) or needs user input (pulsing), with an optional completion chime. The tool, built with AppKit and launchd, works only with locally-running Claude Code sessions and requires Xcode Command Line Tools and the Claude Code CLI. It is installed via a script that sets up hooks in ~/.claude/settings.json.", "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.** Completion chime**— an optional sound (default: Glass) plays the moment the bar clears, i.e. when the last busy session finishes — so you hear it even if you aren't looking at the screen. Pick a different system sound, or turn it off, in the menu (see[Configuration](#configuration)).\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-red-bar.git\ncd claude-red-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-red-bar/hook.sh busy\" }] }],\n  \"Stop\":            [{ \"hooks\": [{ \"type\": \"command\", \"command\": \"bash /path/to/claude-red-bar/hook.sh waiting\" }] }],\n  \"Notification\":    [{ \"hooks\": [{ \"type\": \"command\", \"command\": \"bash /path/to/claude-red-bar/hook.sh needs_input\" }] }],\n  \"SessionEnd\":      [{ \"hooks\": [{ \"type\": \"command\", \"command\": \"bash /path/to/claude-red-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 ccredbar `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/ccredbar/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`ccredbar`\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). When the bar transitions from visible to hidden, the completion chime plays once.\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\nccredbar 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. |\nDone Sound |\nGlass | Chime when the bar clears (all sessions finished). Pick a system sound or None. |\nAbout ccredbar |\n— | Description, author, link to this repo. |\nQuit ccredbar |\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 `ccredbar.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`ccredbar.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`ccredbar` |\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-red-bar/blob/main/LICENSE). Do whatever you want with it, no\nattribution required, no warranty, no liability.", "url": "https://wpnews.pro/news/claude-red-bar-visual-and-audible-notifier-of-task-completion", "canonical_source": "https://github.com/gregsramblings/claude-red-bar", "published_at": "2026-09-01 01:34:36+00:00", "updated_at": "2026-09-01 01:52:09.769900+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["Claude Red Bar", "Claude Code", "Anthropic", "AppKit", "launchd", "Xcode Command Line Tools", "GitHub"], "alternates": {"html": "https://wpnews.pro/news/claude-red-bar-visual-and-audible-notifier-of-task-completion", "markdown": "https://wpnews.pro/news/claude-red-bar-visual-and-audible-notifier-of-task-completion.md", "text": "https://wpnews.pro/news/claude-red-bar-visual-and-audible-notifier-of-task-completion.txt", "jsonld": "https://wpnews.pro/news/claude-red-bar-visual-and-audible-notifier-of-task-completion.jsonld"}}