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.
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.
curl -fsSL https://raw.githubusercontent.com/michalstrnadel/AgentBar/main/Scripts/install.sh | bash- The app lands in
/Applications(or~/Applicationswhen that isn't writable;AGENTBAR_INSTALL_DIRoverrides), launches, and installs its hooks. A welcome window asks where it should live — menu bar, Dynamic Island, or both. - Open a new Claude Code session (hooks load at session start) and give it any task.
- 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.
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 yourCLAUDE_CONFIG_DIRif you set one. Existing hooks are preserved. - Writes into
~/.codex/config.tomlonly if you use Codex: anotifyline 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.jsononly if those exist. Writes its own~/.copilot/hooks/agentbar.jsononly if you use Copilot — a separate file, so your own hooks stay untouched. - Copies a plugin to
~/.config/opencode/plugins/agentbar.jsonly 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.
- 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 historysays 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, andagentbar forgetempties 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.jsonthat 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 undersudo, a destructive git orrm, 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 andagentbar rulescan 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 approvalssays 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 −Mbeside 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 usagesays 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,doneorack(.wav,.aiff,.caf,.mp3,.m4a, up to 2 MB and 3 s) into~/.agentbar/sounds/— Settings ▸ General ▸ Open folder…. - Shortcuts, Raycast, Alfred —
open agentbar://focusjumps 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
agentbarCLI is a full peer of the menu bar app: live status, pending approvals,a/dremote 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.
- macOS 12+ (Apple Silicon or Intel) for the menu bar app — or Linux via the
agentbarCLI - 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
Homebrew — the recommended way:
brew install --cask michalstrnadel/tap/agentbarOne-liner — downloads the latest release (or builds from source when none exists):
curl -fsSL https://raw.githubusercontent.com/michalstrnadel/AgentBar/main/Scripts/install.sh | bashVia 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 Claudesettings.json(~/.claudeand a customCLAUDE_CONFIG_DIR, both; existing hooks are preserved), and~/.codex/config.tomlgets anotifyline 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.jsonand~/.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.
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 reportagentbar 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:
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.
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 pollerWiping ~/.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 | 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.
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.jsonCursor 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.
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.
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.
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.
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".
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.
- 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.tomlalready had anotifyentry, AgentBar deliberately leaves it alone; wireScripts/hooks/codex/notify.jsinto 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.appand 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 outdatedreports an old AgentBar version — the in-app updater swaps/Applications/AgentBar.appwithout telling Homebrew, so brew's install record lags behind after an in-app update. Runbrew upgrade --cask agentbarto re-sync; both update paths install the exact same release bundle, so nothing is lost either way.
MIT — see LICENSE. Third-party marks: see THIRD_PARTY_NOTICES.md.


