Start a terminal coding-agent session in the folder you mean, on the account you mean, at the model and effort you mean.
Two clicks from the menu bar. No config file to edit, no session to correct after it starts.
macOS 14 or later · Apple Silicon · Download the latest release · GPL-3.0-or-later
A ⚠︎ on a row marks a target that bypasses the agent's own permission prompts.
Changing model or effort inside a running agent session costs tokens and attention: the session is already loaded when the correction happens. AgentMenu moves that choice to launch time, where it belongs.
Apple Silicon only. The build is arm64 and does not run on an Intel Mac.
- Download
AgentMenu-<version>.zipfrom the Releases page. - Unzip it.
- Move
AgentMenu.appto/Applications. - Open it.
The app is signed with a Developer ID and notarized, so it opens with no warning — no right-click dance, no trip through System Settings.
Betas. Releases marked Pre-release on the Releases page are betas — signed and notarized the same way as a final release, just published first, on the beta update channel. A final release supersedes the beta it followed. Install one by hand if you want a change early; once in-app updates ship, a Settings toggle will opt a copy into betas instead.
- Launch targets: folders you configure, the folder of the front Finder
window, and
$HOME(which is there on first run, before you configure anything). - A preset per entry: agent, terminal, account profile, model, effort, permission mode and advisor state. An entry overrides only the fields it cares about; the rest inherit from the global default. One folder can have several entries — the same project on the work account and on the personal one, or on opus and on sonnet — and the duplicate button makes the second one from the first, so only the value you want to change is left to change.
- A one-shot override: expand a row, change model or effort, launch. The saved preset is untouched unless you explicitly save it to the folder or to the global default.
- Two accounts, kept apart: the dropdown is separated by profile, so a work folder cannot be launched on the personal account by mis-click.
- Rate-limit readout: 5-hour and weekly consumption per account, with how old the reading is — never presented as current when it isn't. The menu-bar icon turns into a meter when an account is near its limit.
- Agents and terminals are data. Each is a manifest file; adding one is a file you write, not a fork you maintain. See docs/adding-an-agent.md and docs/adding-a-terminal.md.
- A launch is a full command, with the agent binary's absolute path and an environment prefix. It never depends on a shell alias or a shell function, so it works regardless of how your shell is set up.
- It writes into an agent's configuration only what it names, only when you
ask, and removes it on request. Today that is one thing: the status-line
bridge, which you install deliberately from Settings → Accounts. Installing
writes
agentmenu-statusline.shinto that account's configuration directory and sets one key,statusLine.command, in itssettings.json; the dialog names both before anything is written, and every other key in the file is left as it is. Once installed, the bridge writes two more files beside them,tb-rate-snapshot.jsonandtb-rate-history.jsonl. AgentMenu otherwise readssettings.jsononce, to seed its defaults. Sessions are read, never written: the session registry (sessions/<pid>.json) and the transcripts (projects/) that Claude Code keeps are opened read-only, and AgentMenu adds nothing to either. Removing the bridge is the Remove button next to Install in Settings → Accounts, and it takes all of it back out: the dialog names the three files and the key, then it deletes the files and setsstatusLine.commandback to the status line you had before (or removesstatusLineif you had none). If you changedstatusLine.commandafter installing, it refuses and touches nothing. - Sessions, not just launches. A second tab lists the agent sessions running on this Mac, what each one is doing, and which ones are waiting for you. See Sessions.
| Status | |
|---|---|
| Claude Code | Verified end to end (against 2.1.266). The default. |
| Codex, OpenCode | Manifests are present, disabled and marked unverified: the flags were not executed, so the app will not pretend otherwise. Enable one in Settings once you have checked it. |
| iTerm2 | Verified. Preferred when it is installed. |
| Terminal.app | Verified. The fallback every Mac has, so a first run always has somewhere to launch. |
| Ghostty | Manifest is present, disabled and marked unverified — same rule as the agents above. |
Every one of these is a small TOML file under Resources/, and a file with the
same id in ~/.config/agentmenu/agents/ or ~/.config/agentmenu/terminals/
overrides it on your machine alone.
With no ~/.config/agentmenu/config.toml, AgentMenu writes one that already
works before it asks you anything: the agent whose binary it can find, the
terminal that is installed, the account profiles whose configuration
directories exist, and $HOME as a launch target. Then the popover shows a
setup card with the two decisions it cannot make for you — which agents to use,
and which project folders — and detects the git checkouts under the usual code
directories to make the second one a matter of ticking boxes.
The card stays in the popover until at least one real project folder is configured. Everything else — renaming accounts, more folders, the status-line bridge — lives in Settings.
Claude Code hands rate-limit data to exactly one place: the standard input of
your statusLine command. There is no claude usage subcommand, and hooks
receive nothing. So AgentMenu reads a small snapshot file that a status-line
bridge writes.
Install it from Settings → Accounts, per account. It writes the bridge
script into that profile's configuration directory and points statusLine at
it. If you already have a status-line command, the bridge calls yours and
passes its stdin through, so your status line keeps working — installing is
additive, not a replacement. Remove in the same place undoes it.
Two consequences worth knowing:
- The reading is only as fresh as your last session — the snapshot updates while an agent session runs, throttled to once a minute. After an idle evening it is hours old, and AgentMenu shows that age rather than pretending.
- With no snapshot for an account, the readout is hidden entirely.
The popover has two tabs, Launch and Sessions, and it always opens on Launch. Sessions lists every Claude Code session running on this Mac, grouped by folder, each marked Working, Needs you, Your turn or Unknown. Click a row and its Terminal or iTerm2 tab comes to the front. Quit, rename and reopen are in the row and header menus, and a closed session can be reopened later.
Sessions launched from AgentMenu can keep running when the window closes. That is on by default and is a preset field you can turn off per folder or for one launch. It runs the agent under a small tmux that ships inside the app and is separate from any tmux of yours.
AgentMenu learns about sessions by reading the files Claude Code already keeps. It does not write to them. Other agents are listed with process-level status only: running, or not.
docs/sessions.md covers the tab, keep running (including a Shift+Enter limit in Terminal.app), restore, notifications, the macOS permissions involved, and where AgentMenu keeps its own state.
It is almost certainly your menu-bar manager, not the install. Ice, Bartender, Hidden Bar and friends park a new status item in their hidden section, and the standard macOS positioning key does not override that — we verified it: the item lands off-screen at x ≈ -4000 and reports itself as present the whole time.
Open your menu-bar manager and unhide AgentMenu (in Ice: reveal the hidden section and drag the AgentMenu item out of it). The app is running; the icon is just parked.
Earlier versions of this README documented agentmenu as a typed command —
resolving a folder's account from a shell script, installing the status-line
bridge. That is reversed: nothing puts agentmenu on your PATH, and there
is no supported way to type it yourself.
The CLI at Contents/Resources/bin/agentmenu, inside the app bundle, still
exists, but it is internal. The app invokes it itself — to install or remove the
status-line bridge — and the bridge script it writes invokes it again on every
refresh.
Xcode is not required. Command Line Tools plus SwiftPM is the whole toolchain.
git clone https://github.com/Facens/agentmenu.git
cd agentmenu
make bundle # -> dist/AgentMenu.app
make test # the AgentMenuKit suite
open dist/AgentMenu.appmake bundle signs with the maintainer's Developer ID Application
certificate when it is in the keychain. On any other machine — yours, most
likely — there is no certificate, so it signs ad hoc instead and says so with
a banner. An ad-hoc build runs on the machine that built it; it will not pass
Gatekeeper anywhere else and cannot be notarized.
The test suite is a plain executable target, not an XCTest bundle: neither XCTest nor swift-testing exists in a Command Line Tools install, and requiring Xcode to run the tests would make "no Xcode" true only for the maintainer.
A release is exercised on a clean machine before it ships, by a script that clicks the app rather than calling into it. A script cannot see what the app decided, only what it drew — so the app can be asked, at launch, to keep a journal of what it detected, what it is showing and what you chose. It is a read-only record: it performs no action and changes nothing about how the app behaves.
It is off, and only you can turn it on. There is one switch, a preference key, and it names a file:
defaults write dev.facens.agentmenu harnessJournal journal.ndjson # on
defaults delete dev.facens.agentmenu harnessJournal # offThe value is a file name, not a path. AgentMenu writes it in one fixed
place — ~/Library/Application Support/dev.facens.agentmenu/harness/ — and a
value containing / or .. is refused outright: nothing is written anywhere,
and one line in the app's log is the only trace. The file is created at mode
0600, is never written through a symbolic link, and is only ever appended to.
Each line is one JSON object: a sequence number, a timestamp, the schema
version, the build, and an event with its data. The events are the app's own
decisions — harness started, detecting started, detecting finished,
setup shown, setup finished, launch requested, launch result,
bridge installed, save failed — and the values are the ones the popover
was already showing you: the agents found on this machine, the folders
suggested, the binary a launch resolved to, the folder it would run in, the
model and effort. For a launch it records the names of the environment
variables the command sets and never their values, and never the argument
vector. No file contents, no credentials, nothing you could not read off the
screen.
Three more things worth knowing before you switch it on:
- It stops growing. The journal is capped at 1 MiB; past that, the oldest lines are dropped to make room for the newest. A single value longer than 512 characters is shortened, so one long error message cannot push a run's own history out of the file.
- It cleans up after itself. Launch AgentMenu with the key unset and any journal left in that directory is deleted. Files in there that are not journals are left alone.
- It is readable by anything running as you. The directory carries no secret and confinement is not the point — any process that could read it could also have set the key in the first place. The point is that the app writes nothing outside that one directory.
Nothing listens. The journal opens no socket, no port and no network
traffic; it is a file, and the only way to read it is to read it. The one
socket AgentMenu does create is separate from the journal: the bundled tmux
that keeps sessions running talks over a Unix socket file under
~/Library/Application Support/dev.facens.agentmenu/host/, in a directory you
own. It is a local path, not a port, and nothing on the network can reach it.
See docs/sessions.md.
Every control this exercises carries a stable accessibility identifier.
The harness drives the built app by AXIdentifier alone — never a
coordinate, never a label — so the same identifiers a screen reader would
see are also what the script clicks. They're part of the app's contract with
that script, not incidental UI detail; see
CONTRIBUTING.md for the rule.
Every release carries the redacted result of a real run. Before a
release is presented as current, this exact asset — the same zip you'd
download — is installed and driven through every scenario on a vanilla
macOS VM that never saw this machine before, and the release carries the
result: report.public.json, attached to it on GitHub. It holds a verdict,
a list of finding codes, the asset's SHA-256, which build of the golden
image it ran against (the macOS and Claude Code versions, and when it was
built), the scenario names, and a run id — and nothing else. No value in it
may contain a /, so no screenshot, no host path and no hostname ever
reaches it, and a finding is always one of a fixed, published set of codes,
never free text.
The cheapest useful contribution is a manifest for an agent or a terminal you have actually run, and it needs no licence grant at all. Code contributions do carry one; CONTRIBUTING.md says what and why, upfront.
GPL-3.0-or-later. See LICENSE.
A paid version may exist later. If it does, it will be code that is never published, not a relicensing of this repository — what is GPL here stays GPL. Contributions carry a grant that permits that; it is stated upfront in CONTRIBUTING.md rather than announced afterwards. The cheapest useful contribution — an agent or terminal manifest — needs no grant at all.