Skip to content

Repository files navigation

AgentBar

CI License: MIT macOS 12+ Linux CLI Swift

One approval queue for every AI coding agent.

Your agents stop and ask before they run something. AgentBar is where they ask — all of them, in one place, without a terminal to hunt for.

AgentBar demo: Claude session works, needs approval, one-click Allow, then a Codex session takes over the bar
Menu bar mode

AgentBar as a Dynamic Island: the pill under the notch says approve?, opens on hover into the session panel with the mini-diff, one click on Allow, and the pill flashes ✓ Allowed
Dynamic Island mode — pick either (or both) in the welcome window

AgentBar is a lightweight, native macOS app that sits in the permission path of your AI coding sessions — Claude Code and Claude Cowork, Codex, Cursor CLI, Gemini CLI, Qwen Code, OpenCode, plus GitHub Copilot and Google Antigravity. When one of them wants to run a command or write a file, you see exactly what it asked for and answer it in a click; the rest of the time it shows you which session is working and which is waiting. Nothing decides on your behalf, ever. Each agent gets its own mark built from its real identity — Clawd the crab for Claude, the OpenAI knot with a braille dot-matrix for Codex, the official pixel-art head for Copilot, the pixel rainbow arch for Antigravity — and it always surfaces the session that needs you most. Live in the menu bar, as a Dynamic Island pill under the notch, or both — you pick on first launch. On Linux, the same protocol drives the agentbar CLI. On Windows, AgentBar for Windows is a native system-tray counterpart that shares the same ~/.agentbar hook protocol.

Quick start

curl -fsSL https://raw.githubusercontent.com/michalstrnadel/AgentBar/main/Scripts/install.sh | bash
  1. The app lands in /Applications (or ~/Applications when that isn't writable; AGENTBAR_INSTALL_DIR overrides), launches, and installs its hooks. A welcome window asks where it should live — menu bar, Dynamic Island, or both.
  2. Open a new Claude Code session (hooks load at session start) and give it any task.
  3. Watch it: the mascot animates while the agent works, and the moment it asks for permission you get a needs approval row — click ✓ Allow, ✓ Always, or ✕ Deny right there. No terminal switch needed.

That's the whole loop. More install options below; troubleshooting at the bottom.

What the installer changes (and how to undo it)

AgentBar is local-only — no telemetry, and it makes exactly two network calls, one of which is off until you switch it on: the daily update check against GitHub Releases, and — only if you tick Settings ▸ Usage — a request to Anthropic for Claude's own quota, using the login Claude Code already stored. That switch reports what came back, in a sentence under itself, including every way it can fail; Check now asks again on the spot. The simplest way in is Sign in to Claude…, which opens claude.ai's own login page in a window and keeps the session in AgentBar — no terminal, no token, and no reading of your browser's cookies, which AgentBar does not do. If your sessions run under their own CLAUDE_CONFIG_DIR, that login is somewhere AgentBar cannot read — Use a token… takes one from claude setup-token and keeps it in AgentBar's own Keychain item, which is the only secret this app stores and the same button removes. Everything else, including every token count and every other provider's quota, is read from files already on your disk. The install touches exactly these, all reversible (see Uninstall):

  • Copies the hook scripts to ~/.agentbar/hooks/.
  • Merges AgentBar hook entries into your Claude Code settings — ~/.claude/settings.json, and your CLAUDE_CONFIG_DIR if you set one. Existing hooks are preserved.
  • Writes into ~/.codex/config.toml only if you use Codex: a notify line if it has none, and a hooks block between two marker comments — what is outside them stays as you wrote it, and Codex asks you once before running any of it. Merges into ~/.cursor/hooks.json / ~/.gemini/settings.json / ~/.gemini/antigravity{,-cli}/hooks.json / ~/.qwen/settings.json only if those exist. Writes its own ~/.copilot/hooks/agentbar.json only if you use Copilot — a separate file, so your own hooks stay untouched.
  • Copies a plugin to ~/.config/opencode/plugins/agentbar.js only if you use OpenCode.
  • The SessionStart hook launches AgentBar in the background when an agent session begins.
  • Nothing else is granted automatically: the exact-tab jump-back asks for Automation access the first time you click a row, and approving a plan (or a Codex/Copilot prompt) asks for Accessibility. Decline either and AgentBar falls back to bringing the app forward and letting you answer there.

The installer prints this summary before doing anything, and never modifies a tool you don't use. Hooks are snapshotted per session — start a new agent session afterward.

Features

  • Two surfaces, your pick — the classic menu bar item, a Dynamic Island pill under the notch, or both. Pick it in the welcome window on first launch, change it any time from Appearance…; no relaunch. See Dynamic Island.
  • Live status per agent — an animated mascot works the bar while an agent works: Clawd the crab (Claude), the knot + a braille dot-matrix that literally spells codex (Codex), the pixel mascot head + dots spelling copilot (Copilot), and the animated pixel rainbow arch (Antigravity).
  • Notifications that only carry what wants you — off by default, and three separate switches: an agent needs approval (Allow and Deny on the banner itself, answerable without switching apps), a session failed, and everything went quiet — one summary of the whole batch, and only once you have actually been away from the keyboard for a couple of minutes. Nothing is announced for merely finishing. Send a test checks they reach you; if nothing appears but it shows up in Notification Center, a Focus is on — macOS files banners rather than showing them, which is working as designed.
  • What happened today, with its weight — sessions that finished, how long they took, what they cost and what moved in the repo: "12 sessions · 3h 40m · 4.1M tokens", and per row "AgentBar · 34m · 1.2M · 7 files +210 −80". Token counts come from each agent's own local files (Claude's transcript, Codex's rollout, Copilot's session store); the agents that publish nothing simply show nothing, never a zero. In the menu bar dropdown it is the Today row, and behind the island's ⋯. Appearance… can also put it along the bottom of the island — the day's total, a bar per session, or both. agentbar history says the same in a terminal. Live status forgets a session the moment it ends; this doesn't.
  • Pick the island's display — on a multi-monitor desk the island can be pinned to one screen instead of following the pointer around. The welcome window draws your displays the way System Settings does; unplug the pinned one and it falls back to the pointer until it's back.
  • Permission alerts — an amber dot the moment an agent waits for your approval.
  • It remembers what you decided — the fifth time an agent asks to run the same thing, the card says so: “Allowed 23× here · last Tue”, counted per repo, because a command that is routine in one checkout is the opposite in another. Once a prompt has been allowed five times and never refused, ✓ Always is pointed at — pointed at, never pressed. Kept in ~/.agentbar/decisions.jsonl, never sent anywhere, switchable off in Settings ▸ Approvals, and agentbar forget empties it. Keystroke approvals (Antigravity, and Codex sessions older than its hooks) write nothing: a key pressed at a terminal is not a decision anybody here witnessed.
  • Rules you wrote — the one thing AgentBar will answer without asking, and only ever a rule you typed yourself. When you have answered the same prompt the same way five times, the card offers to write it down; Settings ▸ Rules is where they live, in plain JSON at ~/.agentbar/rules.json that you can edit by hand. Writing one shows you what it will not answer, and gives you a field where you type a real command — git push --force origin main — and are told on the spot whether this rule would have taken it, and which clause stopped it. A new rule starts out watching: it answers nothing and writes down what it would have done, so you can look at a week of that before you let it speak for you. A rule that refuses may cover every repository on the machine. A rule that approves names one — and before it answers, the command itself is checked again, not just its shape: anything chained, piped, redirected or substituted, anything under sudo, a destructive git or rm, anything reaching off this Mac, a path outside that directory, or anything that touches how permission itself is configured comes back to you. No setting turns that off. Every firing writes a row naming the rule, so Settings ▸ Approvals and agentbar rules can tell you what each one has actually done. Nothing in the file applies while any of it is wrong, and Diagnostics says so — because a rule that silently stopped working looks exactly like AgentBar working normally.
  • How long they waited on you — the other half of the day's account, under Today: “18 answered · 3 by your rules · they waited 34m on you”. Nothing else on the machine is standing in the right place to measure it, and the two counts stay apart — a rule's answer is not one you gave. agentbar approvals says the same, with the prompts you answer most.
  • Multi-session — every running session listed with its agent's mark, project, git branch, state and elapsed time; click a row to jump to its app or terminal.
  • Open anything — launch any supported agent (Claude, Codex, Copilot, Antigravity, Cursor, Gemini, Qwen, OpenCode) straight from the menu.
  • Start a task, not just an agent — New task… in the menu (or ⌥⌘N, once you switch that on) opens a small panel: a project you have worked in, an agent, and a line of what you want. The agent opens in a terminal, in that directory, with the prompt already given. It closes the moment it loses focus and takes no space until you ask for it. The prompt goes in as an argument, never as synthesized keystrokes, and only to the agents whose CLI documents one — the rest open in the right place and wait for you to type. A terminal that can't be handed a command (Warp) gets the command on your clipboard and says so, rather than opening on the wrong thing.
  • Two looks — full-color mascots, or a monochrome System mode that matches the menu bar.
  • Remote Allow/Deny — answer Claude Code permission prompts straight from the menu: see exactly what's requested, then Allow once, Always allow, Deny, or defer to terminal.
  • A diff you can actually read — an edit shows the lines that moved, with a line of context and a marker where untouched lines were skipped. Where a single line was edited, the characters that changed stay at full strength and the rest of the line fades, and a change past the right edge slides into view instead of truncating — both halves by the same amount, so the columns still line up. The +N −M beside it counts what moved, not the size of the window it moved in.
  • Answer questions too — when Claude asks a multiple-choice question, the island and the menu show the actual options: tap one and the session continues, no terminal switch. The terminal wizard stays live the whole time — whoever answers first wins. Multi-question calls become a one-question-at-a-time wizard on the island: each tap records and slides to the next, with Back and a 2/4 mark.
  • Deny with a note — refuse and say what to do instead: "use pnpm here, not npm". Type it on the island card (Deny with a note…), on the notification banner, or agentbar deny --note "…"; the agent reads it as the reason and changes course rather than trying the next thing. On a plan it is the feedback the plan goes back with. A denying rule can carry one too (Tell it), so a refusal you wrote down once explains itself every time it fires.
  • Plan review — when Claude finishes planning, the full plan renders on the island as formatted Markdown (scrollable when long). Keep planning sends Claude back to refine it without touching the terminal; Approve plan jumps to the session's exact tab and answers the plan dialog for you.
  • What's left, at a glance — one small meter per provider, answering one question: how much is spent and how much is left. On the island it is the footer line, drawn rather than written, at the height that line always had; in the menu it is the fuller block, with both windows and their reset times. Codex reports the exact percentage of its 5-hour and weekly windows with real reset times, plus a credit balance when the account has one. Copilot carries its own priced ledger, so its line is what it actually charged today in its own AIU — and it gets no bar, because the ceiling lives on github.com and a meter drawn against a guessed one would be a picture of a number that doesn't exist. Claude keeps its windows on its own servers: tick Settings ▸ Usage and AgentBar asks for them with the login Claude Code already stored (off by default, five-minute polling, and it never touches your refresh token); leave it off and you get the tokens its transcripts record for the current 5-hour block. A window past its reset says so rather than repeating the old number, everything is hidden the moment it goes stale, and agentbar usage says the same in a terminal.
  • A failure looks like one — a turn that errors out shows red and named instead of a green "Done", and never plays the finish chime.
  • Precise jump-back — clicking a session row selects the exact terminal tab or split pane the session runs in: iTerm2, Terminal.app, WezTerm and tmux (pane, window and client — then the terminal hosting it) by tty; kitty (with remote control on), Ghostty and the VS Code, Cursor or Zed window best effort. Anything else comes forward as the app the agent actually runs in, found by its process ancestry rather than guessed from TERM_PROGRAM.
  • Turn recaps — a finished session's row says what finished: one line of the agent's closing words under "Done", not just a green dot.
  • Activity breadcrumb — while a session works, the island hero shows its last few tool steps ("Reading · Searching · Editing"), so you can tell a session that is grinding through files from one that is thinking.
  • Sound cues (opt-in) — four tiny synthesized retro-console motifs: needs approval, question, done, and an answer-confirm tick. Generated in code (no audio files), silent while your screen is locked, off until you flip them on in Settings or the menu. Or your own: drop permission, question, done or ack (.wav, .aiff, .caf, .mp3, .m4a, up to 2 MB and 3 s) into ~/.agentbar/sounds/ — Settings ▸ General ▸ Open folder….
  • Shortcuts, Raycast, Alfred — open agentbar://focus jumps to the session waiting on you; agentbar://new-task?cwd=…&agent=…&prompt=… fills the launcher in (Return is still yours). No link can approve, deny or answer anything. See docs/url-scheme.md.
  • Built-in updates — a quiet daily check of GitHub Releases plus Check for Updates… in the menu; one click installs the new version and relaunches.
  • Linux too — the agentbar CLI is a full peer of the menu bar app: live status, pending approvals, a/d remote Allow/Deny, digit keys to answer questions, waybar module.
  • Nothing else — no dock icon, no countdown timers, no sounds unless you ask for them, nothing that unfolds over your screen on its own. One process, tiny footprint.

Requirements

  • macOS 12+ (Apple Silicon or Intel) for the menu bar app — or Linux via the agentbar CLI
  • Node.js (for the hook scripts; found via Homebrew paths or your login shell)
  • Xcode Command Line Tools to build the macOS app from source

Install

Homebrew — the recommended way:

brew install --cask michalstrnadel/tap/agentbar

One-liner — downloads the latest release (or builds from source when none exists):

curl -fsSL https://raw.githubusercontent.com/michalstrnadel/AgentBar/main/Scripts/install.sh | bash

Via your AI agent — paste into Claude Code (or any coding agent):

Install AgentBar: run curl -fsSL https://raw.githubusercontent.com/michalstrnadel/AgentBar/main/Scripts/install.sh | bash

From source:

git clone https://github.com/michalstrnadel/AgentBar.git && cd AgentBar
./Scripts/build.sh          # add --native if the x86_64 half fails to link
open "build/AgentBar.app"

./Scripts/build.sh produces a universal binary, which needs a full Xcode: recent Command Line Tools ship the Swift compatibility libraries for arm64 only, so the x86_64 half won't link without one. --native builds for your Mac alone, which is all you need to run it yourself.

First launch installs hooks automatically for every supported tool you have — see the agent table. New agent sessions appear in the bar from then on; ones already open started before the hooks and stay out of it until you start another.

What you'll see the first time: Homebrew and the one-liner clear the download quarantine, so macOS opens the app without its "cannot verify" warning — that one appears only for a zip downloaded by hand (see Troubleshooting). If your projects live in Documents, Desktop or Downloads, macOS asks once whether AgentBar may read them; that is for the git branch and changes shown on each row, and nothing leaves your Mac.

Updating: the app checks GitHub Releases daily and offers new versions in the menu (Check for Updates… works any time). Homebrew users can keep using brew upgrade --cask agentbar — both paths install the same bundle.

What install touches: hook scripts are copied to ~/.agentbar/hooks/, hook entries are merged into your Claude settings.json (~/.claude and a custom CLAUDE_CONFIG_DIR, both; existing hooks are preserved), and ~/.codex/config.toml gets a notify line if it has none, plus a hooks block between # >>> agentbar >>> and # <<< agentbar <<< — everything outside those two lines is left byte for byte, and Codex asks you once before it runs any of it, so writing them decides nothing on your behalf. Then — only for tools you already have — hook entries are merged into ~/.cursor/hooks.json, ~/.gemini/settings.json, ~/.gemini/antigravity{,-cli}/hooks.json and ~/.qwen/settings.json, and the OpenCode plugin is copied to ~/.config/opencode/plugins/agentbar.js. A config that exists but isn't valid JSON is never touched. The Claude SessionStart hook also auto-launches AgentBar in the background when a session begins. Hooks are snapshotted per session — start a new agent session after installing.

Linux (CLI)

The protocol is just files (~/.agentbar, see docs/protocol.md) and the hooks are plain Node — so on Linux, the agentbar CLI is the frontend:

git clone https://github.com/michalstrnadel/AgentBar.git && cd AgentBar
./Scripts/cli/agentbar install-hooks   # wires Claude/Codex/Cursor/Antigravity/Gemini/Qwen/Copilot/OpenCode hooks
sudo ln -s "$PWD/Scripts/cli/agentbar" /usr/local/bin/agentbar   # optional

agentbar                 # session list (same rows as the macOS menu)
agentbar watch           # live view; a = allow, d = deny, 1-9 = answer a question, q = quit
agentbar requests        # pending approvals & questions with the mini-diff / options
agentbar approve --always
agentbar answer Blue     # answer a pending question by option label (or number)
agentbar history         # what finished today (--days N, --json)
agentbar usage           # what's left of each provider's quota
agentbar approvals       # the prompts you keep answering (--days N, --json)
agentbar rules           # the rules you wrote and what each has done (--json)
agentbar forget          # empty the decision ledger
agentbar doctor          # why an agent isn't showing up; --json for a bug report

agentbar doctor is the thing to run when an agent simply never appears. It re-derives the whole installation from disk — is node where the configs say it is, are the hooks wired, is ~/.agentbar writable, when did each agent last report — and answers in the words of the fix. Every check id is listed in docs/diagnostics.md.

Remote Allow/Deny works exactly like on macOS: while agentbar watch (or a waybar poll) is running, a Claude Code or Copilot CLI permission prompt appears in the CLI and your a/d answers it — and when Claude asks a multiple-choice question, its options render right in the list and a digit key (or agentbar answer) picks one. With no watcher running, hooks stay silent and the normal terminal prompt appears.

Rules are listed here, not applied here. agentbar rules reads the same ~/.agentbar/rules.json the app does and says what is in it, but only the macOS app answers from a rule. The check that makes an approving rule safe — the live command, not its shape — is one table in one language, and a second copy of it in the CLI would be a second thing to keep byte-identical in the one place where drifting apart means approving something nobody meant to. The command says so in its own output.

Waybar module:

"custom/agentbar": {
  "exec": "agentbar waybar", "return-type": "json", "interval": 15
}

The module's class (and alt) is one of permission, question, working, idle or empty, in that priority order — style them in your waybar CSS; the text is ✋ n / ❓ n / ● n / the session count.

Updating: the CLI has no release channel of its own — git pull in the checkout is the update, and re-run install-hooks afterwards so the copies in ~/.agentbar/hooks/ are refreshed. (On macOS the app does that for you on every launch; on Linux the CLI is AgentBar, so nothing does it behind your back.)

install-hooks writes an absolute path to the node that will run the hooks, because a GUI-launched agent's PATH can't be relied on. It picks a stable path — /usr/bin/node, /usr/local/bin/node, /opt/homebrew/bin/node or ~/.local/bin/node — whenever one of those is the same binary as the node running the CLI, rather than the version-pinned path a version manager resolves to. A hook config outlives the next node upgrade; if it named …/node/v20.11.0/bin/node, every hook would silently stop firing the day you upgrade. On a version manager with no stable alias it falls back to the running interpreter, so re-run install-hooks after a major node change.

The CLI works on macOS too (same protocol, handy over SSH). A native tray app (StatusNotifierItem) may come later if there's demand.

Uninstall

osascript -e 'quit app "AgentBar"'
rm -rf ~/.agentbar
# remove the AgentBar hook entries (they all reference ~/.agentbar/hooks/):
#   ~/.claude/settings.json (and your CLAUDE_CONFIG_DIR) — delete rules whose command contains "/.agentbar/hooks/"
#   ~/.codex/config.toml       — delete the notify line referencing "/.agentbar/hooks/",
#                                and the block from "# >>> agentbar >>>" to "# <<< agentbar <<<"
#                                (plus any [hooks.state] entry naming that file, which is
#                                 Codex's record of you having accepted them)
#   ~/.cursor/hooks.json       — delete entries whose command references "/.agentbar/hooks/cursor/"
#   ~/.gemini/settings.json    — delete hook groups whose command references "/.agentbar/hooks/gemini/"
#   ~/.gemini/antigravity/hooks.json and ~/.gemini/antigravity-cli/hooks.json
#                              — delete the top-level "agentbar" key
#   ~/.qwen/settings.json      — delete hook groups whose command references "/.agentbar/hooks/claude/"
# Copilot and OpenCode are whole files AgentBar owns, so they just go:
rm -f ~/.copilot/hooks/agentbar.json
rm -f ~/.config/opencode/plugins/agentbar.js
./Scripts/cloud/install.sh uninstall   # only if you installed the cloud poller

Wiping ~/.agentbar takes cloud.json (and its API keys) with it; the poller's launchd agent has to be booted out separately, which is what the last line does.

Agent support

Agent Live status Open Mascot Notes
Claude Code (CLI + desktop) full yes Clawd the crab hooks: prompt, tool, permission, stop, lifecycle
Claude Cowork (desktop) working / approval / question / done — older local mode only yes Clawd the crab watched, not hooked: Cowork gives each session a throwaway config dir, so there is nothing to install into. CoworkWatcher reads the audit log the app writes per session. Newer desktop builds run Cowork inside a VM that writes no session files on the host — those sessions can't be shown until the app exposes something host-side
Codex CLI full hooks yes knot + braille dot-matrix hooks auto-installed; Codex asks once before it runs them
Cursor CLI working / done yes pointer hooks in ~/.cursor/hooks.json (auto-wired if Cursor is installed). No remote approval: its hooks can refuse a tool call but not approve one — see what each agent will let somebody else decide
Gemini CLI working / done yes spark hooks in ~/.gemini/settings.json (auto-wired if Gemini is installed). No remote approval, for the same reason as Cursor: BeforeTool takes block, deny or ask, and has no allow
GitHub Copilot CLI working / done / failed / approval yes pixel head + dot-matrix Claude-shaped hooks in ~/.copilot/hooks/agentbar.json (auto-wired if Copilot is installed; needs CLI 1.0.67+ and a fresh session — it reads hook config only at startup). Remote Allow/Deny via its permissionRequest hook; no "Always", which its output contract has no room for
Qwen Code working / done / failed yes Q ring Claude-style hooks in ~/.qwen/settings.json (auto-wired if Qwen is installed); remote approval waits until its decision contract is verified
OpenCode working / approval / done / failed yes prompt chevron plugin in ~/.config/opencode/plugins/ (auto-installed if OpenCode is installed); observe-only
Google Antigravity working / done yes pixel rainbow arch + dot-matrix hooks in ~/.gemini/antigravity{,-cli}/hooks.json (auto-wired); desktop 2.3.x only honors per-workspace .agents/hooks.json, and only PostToolUse fires — quiet sessions decay to done
Devin (cloud) working / blocked / finished / suspended yes D letterform no local process at all — rows come from the cloud poller, clicking opens the exact thread in Devin Desktop (or the web)

Hook readiness: Claude Code, Codex (config.toml), Cursor (hooks.json), Gemini (settings.json), Antigravity (hooks.json), Qwen Code (settings.json), Copilot CLI (hooks/agentbar.json), and OpenCode (plugin) hooks all install automatically at launch (idempotently — every launch re-checks, nothing is duplicated) for the tools you have. Copilot reads its hook config once at startup, so a session already open won't report until you restart it.

Which of them you can actually answer for is a shorter list than which of them report, and the difference is the vendor's, not AgentBar's: what each agent will let somebody else decide records it per agent, measured, with the version each answer was measured against.

Cloud agents

Runs that live on a vendor's infrastructure — Cursor cloud agents, Devin sessions, Codex cloud tasks — have no local pid, tty, or hook, but they are sessions all the same. The optional Scripts/cloud/ poller mirrors them into the bar as protocol rows (entrypoint: "cloud" plus a url); a row click opens the run where it actually lives: the cursor:// run deep link, the exact thread in Devin Desktop (via its ACP URL handler, with the web thread as the handler's own fallback), or the task on chatgpt.com. Cloud rows never grow approval affordances — there is no local hook an answer could reach.

./Scripts/cloud/install.sh    # launchd agent + a starter ~/.agentbar/cloud.json

Cursor and Devin poll their REST APIs with keys from their dashboards; Codex rides the codex CLI's existing login. Vendors fail independently — one expired key collapses that vendor to a single clickable "check API key" row and never touches the others. Setup, config, and lifecycle rules: Scripts/cloud/README.md.

Your own machines, too. The same poller can read a devbox or a GPU server over ssh (off until you list hosts in cloud.json): the host runs AgentBar's hooks via the Linux CLI, and its sessions appear here as gpu: my-repo, a click opening ssh://gpu. Read-only — a remote session waiting on permission says so and is answered where it runs.

Dynamic Island

Instead of (or alongside) the menu bar item, AgentBar can live as a pill just under the notch:

  • At rest it's tiny — the mark of whichever agent is working, plus one line of what it's doing, plus a count once two or more sessions are live. Nothing running, and it shrinks to the mark alone. It never grows on its own: even a pending approval stays a pill that says approve?.
  • Push the pointer up to the notch and it opens — whatever needs you leads as a boxed hero row: what you asked for ("You: fix the auth bug in middleware"), a coloured status line, and chips naming the agent, model, the terminal (or app) it lives in and how long it's been at it. The other sessions follow as quiet one-liners named by their task. Click a row to jump to that session. The collapsed pill itself is click-through and never opens by accident: tab strips and toolbars living at the top of a maximized window stay fully usable under it.
  • Answer right there — a waiting approval is a proper Permission Request card: the tool and its target, the mini-diff with +3 −1 counts, Allow / Deny in front and Always allow / Answer in terminal quiet beside them. Your answer flashes back in the pill — ✓ Allowed — as the panel folds away. An AskUserQuestion shows the actual options as tappable cards; several questions become a wizard, one at a time, with a 2/4 mark and ‹ Back. An ExitPlanMode shows the whole plan as formatted Markdown with Keep planning / Approve plan. A failed turn says failed in red and stays silent — no green tick, no chime.
  • The day along the bottom (off by default) — Appearance… ▸ Today, along the bottom has two switches you can take separately. The day's total is one line: "12 sessions · 3h 40m · 4.1M tokens". A bar per session draws today's finished sessions oldest-first, wider the longer each ran, in the agent's colour, red for what failed and half-lit for one nobody could time — point at a bar for its project, duration, tokens and what changed in its repo. Both are off until you ask, because the strip costs height and a day spent in a single agent draws one long bar that does not earn it.
  • It scrolls when it must — the panel is sized to its content, and past the screen limit the rows scroll while the day's strip, the ⋯ menu and the quota line stay pinned along the bottom.
  • No notch, or an external display? Same panel, centred at the top of whichever screen your pointer is on. It stays put over fullscreen windows — that is where the agents are actually running — and it never takes focus, so you can keep typing in your editor with the panel open. The pill is 30pt tall and only opens when you point at it.
  • Or pin it to one display. Following the pointer is right for a laptop and wrong for a fixed desk, where a status surface that moves is one you have to look for. Island on: in the welcome window draws every connected display — a laptop for the built-in one, notch included — and one click pins the island there for good. A pinned display that gets unplugged falls back to the pointer and re-pins itself when it returns, so AgentBar never goes missing.

In Island-only mode the menu bar item is hidden, so the panel's ⋯ button carries Appearance, Color, the Sounds toggle, Settings, Check for Updates and Quit.

Appearance

All of it lives in one window — Appearance… in the menu, and the same window you get on first launch. The preview above the buttons is the real mascot driven through the real code, not a picture of one, so it animates exactly as the bar will.

AgentBar's Appearance window: a live mascot preview, the Menu bar / Dynamic Island / Both picker, an 'Island on:' row of drawn displays with 'Follow pointer' selected, the mark colour choice, and the list of agents whose hooks are wired up

Island on: draws your displays rather than listing them — a laptop for the built-in one, notch included, a monitor on a stand for the rest. Click one and the island stays there instead of following the pointer around, which is what a fixed multi-monitor desk wants. Unplug a pinned display and the island falls back to the pointer until it's back; the choice is remembered, and it is keyed to the display's UUID, so a monitor that comes back under a different display ID is still recognised as the same one.

Remote Allow/Deny

AgentBar menu with a pending Claude Code permission request: yellow needs-approval row, the requested command, and an inline Allow / Deny / Terminal button strip

When a Claude Code or Copilot CLI session asks for permission, the request appears right under the yellow "needs approval" row: what's requested (e.g. Bash: git push origin main; full input in the tooltip) plus an inline button strip — ✓ Allow, ✓ Always (only when Claude Code suggests a rule; the rule is in the tooltip), ✕ Deny, and ⌨ Terminal / ⧉ Claude app to answer in the session's own UI instead. Clicking the session row does the same hand-off. Decisions return through Claude Code's PermissionRequest hook, so the terminal prompt never appears; if AgentBar isn't running, quits mid-wait, or you ignore the request for 10 minutes, the prompt shows in the terminal exactly as before. (Known cosmetic issue: the terminal dialog can flash briefly even when approved from the menu — upstream claude-code #12176.)

Codex answers natively too, since 1.28.0. It speaks Claude's hook dialect, so the same scripts serve it, and a Codex prompt arrives as a real card with real buttons — and in the ledger, and under the rules you wrote. Two things are its own. There is no ✓ Always: Codex has no channel for a standing rule, so an "always" would quietly be a one-shot allow and the button is not offered. And Codex asks you once before it will run a hook at all — until you accept, the hooks do nothing and Diagnostics says so; AgentBar will not sign that acceptance for you.

Agents with no decision hook still offer Approve in terminal (sends keystroke), and so do Codex sessions that were running before the hooks were accepted — AgentBar brings the session's own tab forward and presses the approval key. It waits for that tab to be confirmed by tty and sends nothing if it can't be found, so a keystroke never lands in a tab it couldn't verify — in tmux that means both the pane and the outer terminal's tab; on terminals with no tab targeting (Warp, Ghostty, kitty) it falls back to the app and types nothing. Best-effort by design, and it needs the Accessibility permission (the menu item offers to open System Settings until it's granted).

Copilot CLI answers natively too, through its permissionRequest hook — no keystroke and no Accessibility permission. Two differences from Claude. There is no ✓ Always: GitHub's output contract is {behavior, message, interrupt}, with no channel for a standing rule, so an "always" would quietly be a one-shot allow and the button is simply not offered. And a session that was already running when AgentBar installed the hook still falls back to keystrokes — Copilot reads hook config only at startup, so remote approval begins with the next copilot session.

Approving a plan works the same way, for a different reason: Claude Code ignores a hook's allow at the plan dialog, because approving a plan also picks the next permission mode — something a hook decision can't express. So Approve plan selects the session's exact tab and answers the dialog there. It needs Accessibility, and a terminal AgentBar can aim (iTerm2, Terminal.app, WezTerm); anywhere else the button hands you the dialog instead of typing into a tab it cannot verify. Keep planning needs none of that — it goes through the hook as an explicit "refine this first".

How it works

Tiny hook scripts (Node.js) write one JSON file per session to ~/.agentbar/state.d/. The app watches that folder and renders. No sockets, no daemons; the only network traffic is the update check against GitHub Releases. Permission approvals use two more folders of the same protocol: the blocking hook writes requests.d/, the app answers into answers.d/. The contract is docs/protocol.md; everything outside the Swift app (hooks, bridges, the OpenCode plugin, the CLI) is covered by four bash test suites that run on Linux and macOS — see docs/testing.md.

Troubleshooting

  • No sessions appear — hooks load when a session starts: open a new agent session after installing. If you use a custom CLAUDE_CONFIG_DIR, see issue #4.
  • Cowork sessions don't appear — newer Claude desktop builds run Cowork inside an isolated VM: the session's audit log lives on the VM's disk image, so nothing exists on the host for AgentBar to read. Upstream limitation; sessions from the older host-side "local mode" still show.
  • Still nothing — the installer needs node; if none is found the Claude hooks are skipped (logged to Console.app). Install Node.js and relaunch AgentBar.
  • Codex rows never show — if ~/.codex/config.toml already had a notify entry, AgentBar deliberately leaves it alone; wire Scripts/hooks/codex/notify.js into your existing notify chain manually.
  • Keystroke approval does nothing — grant AgentBar the Accessibility permission (the menu item offers to open System Settings).
  • macOS says it "cannot verify AgentBar is free of malware" — the app is signed with the project's own certificate, not notarized by Apple. Don't click Move to Trash; click Done, then open System Settings ▸ Privacy & Security, scroll to the line about AgentBar and click Open Anyway (macOS 15 and later; on macOS 14 and earlier, right-click the app ▸ Open works too). Or run xattr -dr com.apple.quarantine /Applications/AgentBar.app and open it again. The install script and the Homebrew cask do this for you; the dialog appears only after downloading the zip by hand from Releases. To check that zip first, see Verifying a download.
  • brew outdated reports an old AgentBar version — the in-app updater swaps /Applications/AgentBar.app without telling Homebrew, so brew's install record lags behind after an in-app update. Run brew upgrade --cask agentbar to re-sync; both update paths install the exact same release bundle, so nothing is lost either way.

License

MIT — see LICENSE. Third-party marks: see THIRD_PARTY_NOTICES.md.

About

Approve Claude Code permission prompts right from your menu bar — no terminal switching — plus live status for every AI coding agent (Claude, Codex, Copilot, Antigravity).

Topics

Resources

Contributing

Security policy

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages