Replay Theater: step through the file edits Claude made in the last turn, one diff at a time.

One approval queue for your coding agents, and rules that watch before they answer.
<img src="docs/assets/agentbar-tour.gif?v=2026-10-16" width="640" alt="AgentBar tour: the island under the notch opens on a Claude permission request for git push origin main with Codex and Copilot sessions listed below, one click on Allow; then the welcome window's Menu bar / Dynamic Island / Both choice, then a walk through Settings">
Why. Your agents stop and ask before they run something, and the question waits in whichever terminal tab they happen to be in. AgentBar is where they ask — every agent, every session, in the menu bar or under the notch — so you answer in a click instead of hunting for the window. Nothing is answered behind your back: only you do, or a rule you wrote yourself, and every answer is written down.
| Do this | AgentBar does that |
|---|---|
| Give Claude Code, Codex or Copilot a task that runs a command | A needs approval card with the exact command (a mini-diff for an edit); Allow or Deny in one click, no terminal switch |
| Try an approval in the welcome window | A made-up request waits at the notch (or in the menu bar) — answer it the way you would a real one, before any agent is wired. Nothing runs |
| Click Deny with a note… and type "use pnpm here, not npm" | The agent reads the note as the reason and changes course instead of trying the next thing |
| Push the pointer up to the notch | The island opens: every session, what it is doing, and the one that needs you on top |
| Click a session row | Jumps to the exact tab or pane — iTerm2, Terminal, WezTerm, tmux — or brings forward the app it runs in |
| Answer the same prompt the same way five times | The card offers to write it down as a rule; a new rule starts out watching and answers nothing until you let it |
Type git push --force origin main into a rule's test field | Says on the spot whether that rule would have taken it, and which clause stopped it |
| Switch on ⌥⌘A / ⌥⌘D in Settings ▸ Shortcuts | Allows or denies the newest request from any app |
| New Task… in the menu (or ⌥⌘N, once switched on) | A project, an agent and one line of what you want; the agent opens in a terminal with the prompt given |
| Turn on Hide the island when nothing is running (or …while you're away) | The pill slips out of sight; push the pointer up to the notch to peek. Anything waiting on you keeps it up |
| Settings ▸ Agents ▸ Show changes… | Every write AgentBar made into an agent's settings, as a unified diff, with the copy it kept beside the file |
| Flip an agent's switch off in Settings ▸ Agents | Shows what it will take out of that agent's settings, then removes only AgentBar's own entries — and leaves them out from then on |
agentbar report --agent aider --name Aider --state tool --label Editing --pid $$ | Any tool you run joins the bar under its own name and mark — wrap it in a few lines, no Swift |
| Your Day… in the menu or the island's ⋯ | Your day — or week — with your agents on one card: who you were today, agent time, the hours, your top agent, what changed, your answers; copy it, post it, or save it as a video |
| Click the cup in the island's footer | Keep Mac Awake while your agents work: the Mac stays up while a session runs and sleeps five minutes after its last turn. Right-click or hold for 15 or 30 minutes, an hour, two, until a time, or until you say. The time left counts down beside the cup and at the top of its menu, where Add 15 Minutes stretches a timed choice. The keyboard goes dark while you're away (and the screen can lock, if you switch that on), it can sleep the Mac when the agents finish, and Settings ▸ Awake Triggers starts it by itself when an agent works, on the charger, with a display, while an app runs, or while an app asks the Mac to stay awake (an export, a download). With the lid closed it keeps going, and when it ends the Mac goes to sleep instead of staying up in your bag |
| Drag a screenshot or a file onto a session in the island | Its path lands in that agent's prompt — pasted into the right tab, never with Return |
| Let a quota window run hot | The meter says when it runs out at this pace — "out ~15:40" — when that is before it resets |
| Right-click a session in the island | Continue in another agent: the launcher opens in the same project with a prompt that says where the work got to — Return is still yours |
open agentbar://focus from Shortcuts or Raycast | Jumps to the session waiting on you — and no link can approve or deny anything |
What it covers. Agents: Claude Code and Claude Cowork, Codex, Cursor CLI, Gemini CLI, GitHub Copilot, Google Antigravity, Qwen Code and OpenCode — plus Cursor cloud agents, Devin and Codex cloud tasks, and sessions on your own machines over ssh. Terminals: any — the exact tab in iTerm2, Terminal, WezTerm and tmux, best effort in kitty, Ghostty and VS Code, Cursor or Zed, and anything else (Warp included) comes forward as the app the agent runs in. Sessions: as many at once as you run, the one that needs you first. Approvals: a per-repo history of what you decided, and rules you wrote, every firing on record. Surfaces: the menu bar, a Dynamic Island under the notch, or both, pinned to the display you pick. Elsewhere: the agentbar CLI on Linux, and AgentBar for Windows, a native system-tray counterpart on the same ~/.agentbar hook protocol.
curl -fsSL https://raw.githubusercontent.com/michalstrnadel/AgentBar/main/Scripts/install.sh | bash
/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.That's the whole loop. More install options below; troubleshooting at the bottom.
AgentBar is local-only — no telemetry, and it talks to three places, two of which stay silent until you switch them on: the update check every few hours against GitHub Releases, and — only if you tick Settings ▸ Usage — Claude's own quota, from api.anthropic.com using the login Claude Code already stored, or from claude.ai if you sign in there. 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 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):
~/.agentbar/hooks/.~/.claude/settings.json, and your CLAUDE_CONFIG_DIR if you set one. Existing hooks are preserved.~/.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.~/.config/opencode/plugins/agentbar.js only if you use OpenCode.~/.agentbar/mods/ — and that is all, until you switch it on: it is the one integration that starts off.settings.json.agentbar-bak-20261001-142233 (local time) — and keeps only the newest three of its own copies per file. A launch that would change nothing writes nothing and keeps nothing, so the copies only appear when something actually moved. Settings ▸ Agents ▸ Show changes… (or See what changed… in the welcome window) shows every write as a unified diff with the copy it kept, and what a re-install would change right now before you let it. On Linux, agentbar install-hooks prints the same diff for each file before writing it and keeps the same copies.agentbar unwire <id> on Linux (or install-hooks --skip a,b / --only a,b). Turning one off takes AgentBar's entries back out of that agent's settings — backed up and diffed like any other write — and every later run leaves it alone (~/.agentbar/wire-disabled). agentbar wire <id> or the switch puts it back.The installer prints this summary before doing anything, and never modifies a tool you don't use. To undo a single change, copy the .agentbar-bak-… file back over the original. Hooks are snapshotted per session — start a new agent session afterward.
<img src="docs/assets/demo-claude-codex.gif?v=2026-09-23" width="640" alt="AgentBar demo: Claude session works, needs approval, one-click Allow, then a Codex session takes over the bar">
<img src="docs/assets/demo-island.gif?v=2026-09-23" width="640" alt="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">
<img src="docs/assets/hand-a-file.gif?v=2026-10-07" width="720" alt="A screenshot dragged from the desktop onto a Claude session in the island: the row says 'Drop to hand it to Claude', then '✓ In Claude's prompt — add a word and press Return', and the escaped path appears in the terminal's prompt with no Return pressed. A Codex row says 'quiet 12m?', and the footer meter reads 'Claude 12% left · out ~15:03' in amber">
agentbar history says the same in a terminal. Live status forgets a session the moment it ends; this doesn't.~/.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.~/.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. The rule's line says whether you, answering those same prompts, did what it would have done — and once it has matched you ten times across three days without a single disagreement, it offers a Let it answer button that still asks before it changes anything. 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 ▸ Rules 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.<img src="docs/assets/week-of-decisions.png" width="560" alt="Settings ▸ Approvals, Your week of decisions: since 1 Oct, asked 49×, 25m waited, your rules answered 5 more. git push 14× · 7m waited, allowed 14, no rule yet, with a Write a rule… button; rm 6× denied, its deny rule is off; npm test 11×, a watching allow rule would have answered 9 of 9, about 4m; edit Sources/*.swift 11×; curl 3×">
agentbar approvals says the same, with the prompts you answer most.+N −M beside it counts what moved, not the size of the window it moved in.npm test in the same folder are one decision: the card says Allow all 3. Only the exact same request counts (same tool, the whole same input, the same directory), only the ones on screen when you click, and each goes into the approval history as your own answer.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.agentbar usage says the same in a terminal.git diff first". You read it, you press Return; the first session is left exactly as it was.hooks/replay-theater.mjs 9 lines1 on("session.start", async ($, e, next) => {
2 on("command.run", { command: "replay" }, async ($, e) => {
3 on("tool.call", async ($, e, next) => {
4 on("turn.start", ($, e, next) => {
5 on("turn.complete", async ($, e, next) => {
6 on("ui.render", { component: "AbovePrompt" }, ($, e, next) => {
7 on("ui.render", { component: "Pane" }, ($, e, next) => {
8 on("ui.close", ($, e, next) => {
9