Two routes to the app, and one step that both routes need. Take the disk image unless you have a reason to build from source.
This is the route to prefer. The disk image is signed with a Developer ID, notarised by Apple, and stapled.
Download for macOSVerify the download before you open it. coffee-bar asks you to trust an app that overrides your Mac's own sleep policy, so it gives you the means to test that trust first.
shasum -a 256 coffee-bar-0.3.1.dmg # ca72a571f5595da27d377bbb69ed0fe3b20869bcc9823caeb490fbc2afc2badd spctl --assess --type open --context context:primary-signature -vv coffee-bar-0.3.1.dmg # accepted # source=Notarized Developer ID
The notarisation ticket is stapled to the disk image, so the second command answers from the ticket in the file. It needs no network connection.
From 0.2.1 the app inside the image carries its own ticket too, so the copy
you drag to /Applications launches on a machine that has never been
online. In 0.2.0 only the image was stapled, and that first launch had to ask
Apple.
brew tap ArangoGutierrez/coffee-bar brew install coffee-bar
Both routes ship 0.3.1. Either way you get the same two
things: the app bundle, assembled by the repository's own build script, and the
coffee-bar-probe binary.
What differs is the signature, not the features. Homebrew compiles on your machine, so that copy is signed only ad hoc and is not notarised. The disk image is signed with a Developer ID, notarised by Apple and stapled, which is what lets Gatekeeper accept a copy that arrived over the network. Take the disk image unless you have a reason to build from source.
The formula lives in a separate tap repository,
ArangoGutierrez/homebrew-coffee-bar,
because brew tap user/repo resolves to
github.com/user/homebrew-repo. Homebrew formulae do not write to
/Applications, so the app lands in the Homebrew prefix and the
install prints the one command that links it there.
Both routes need this step. coffee-bar learns what your agent sessions are doing from Claude Code hooks and from nothing else, so until these entries exist the app runs and no session event ever reaches it. coffee-bar never writes your settings file for you.
Open Preferences… from the foot of the panel, or press ⌘, while coffee-bar
is frontmost. Under Agent tools each settings file is listed
with a Copy hook snippet button beside it. Click the one next
to ~/.claude/settings.json and the whole block is on your
pasteboard, ready to merge into the file.
Prefer that button to the block below. It is generated from the same constant
the health check reads, so it cannot tell you to wire a set the panel then
reports as missing. It carries the ingest endpoint belonging to the tool you
picked, which is how an arriving payload is attributed to that tool at all, and
it puts the matcher key on exactly the events that need one. The
block below explains none of that, and a hook config wrong in any of those ways
fails silently: the app sees nothing and the panel still says the file is
wired.
The button writes to the pasteboard and stops there. Merging is yours, because that file is yours.
Worth doing if you want to read what you are pasting before it goes in, or if coffee-bar is not running yet.
Add these to ~/.claude/settings.json:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "curl -sS -o /dev/null --fail-with-body --max-time 5 --unix-socket \"$HOME/Library/Application Support/coffee-bar/ingest.sock\" -X POST --data-binary @- http://localhost/event"
}
]
}
],
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "curl -sS -o /dev/null --fail-with-body --max-time 5 --unix-socket \"$HOME/Library/Application Support/coffee-bar/ingest.sock\" -X POST --data-binary @- http://localhost/event"
}
]
}
],
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "curl -sS -o /dev/null --fail-with-body --max-time 5 --unix-socket \"$HOME/Library/Application Support/coffee-bar/ingest.sock\" -X POST --data-binary @- http://localhost/event"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "curl -sS -o /dev/null --fail-with-body --max-time 5 --unix-socket \"$HOME/Library/Application Support/coffee-bar/ingest.sock\" -X POST --data-binary @- http://localhost/event"
}
]
}
],
"PermissionDenied": [
{
"hooks": [
{
"type": "command",
"command": "curl -sS -o /dev/null --fail-with-body --max-time 5 --unix-socket \"$HOME/Library/Application Support/coffee-bar/ingest.sock\" -X POST --data-binary @- http://localhost/event"
}
]
}
]
}
}
The two tool events take a matcher; the other three take none.
Keep -o /dev/null. It pairs with
--fail-with-body, which exists to print the server's error body
rather than swallow it — and curl prints a body to standard output,
which is where Claude Code looks for a hook's decision. Without the redirect, an
ingest error reaches your agent as something to act on. The exit status, which
is the part --fail-with-body is wanted for, is unaffected.
If your settings file already has a hooks key, merge
these entries into it. Pasting the block whole replaces whatever
hooks you already run.
Restart Claude Code, so that the new entries load. Start coffee-bar before the next session: the app creates the socket, and a hook that finds no socket makes Claude Code report an error on every event.
Then open the panel and read its advisory lines. The panel names any hook it
cannot find in ~/.claude/settings.json, and it says separately when
this copy of coffee-bar is not listening on its socket. Those are two different
questions, so the panel answers them on two different lines.
coffee-bar has no Dock icon and opens no window. Look for the cup at the right end of the menu bar, near the clock, and click it to open the panel. If you cannot see the cup, the app is almost certainly running: on a full menu bar the cup is there and you cannot see it, and on a MacBook with a notch it is usually behind the notch.
Read the Docs page next. It defines all three controls — Serving in the panel, Display and Battery floor in the Preferences window — what coffee-bar asks macOS for, and when it refuses to hold.
arm64.