Alerts with a face.
Any tool that can curl can pop her speech bubble. Click to jump back to the terminal, URL, or file that needs you.
A tiny axolotl that lives in the corner of your screen, surfaces alerts from any tool that can POST JSON, and cheers you on.
View on GitHubAny tool that can curl can pop her speech bubble. Click to jump back to the terminal, URL, or file that needs you.
Drags anywhere on screen. Nudges you to breathe, hydrate, stretch. Fidgets and naps when things go quiet.
One 700 KB Swift binary. No Electron, no helpers, no telemetry. Idles at 0 % CPU and ~40 MB RAM.
On macOS with the Xcode command line tools:
git clone https://github.com/Roach/axol.git
cd axol
./build.sh
./axol
Axol listens on 127.0.0.1:47329. Anything you can curl can talk to her — post an envelope from a script, a Makefile, a CI step:
curl -s -X POST http://127.0.0.1:47329/ \
-H 'Content-Type: application/json' \
-d '{
"title": "ci-bot",
"body": "build passed on main",
"priority": "normal",
"source": "github-actions",
"actions": [{ "type": "open-url", "url": "https://github.com/.../runs/1" }]
}'
Required field: title. Priority high / urgent floats worry bubbles above her head; urgent also pins the speech bubble open until you handle it. Actions are a closed set — focus-pid, open-url, reveal-file, noop — so there's no shell execution path.
Adapters are JSON files that translate a third-party payload into Axol's envelope. Drop one into ~/Library/Application Support/Axol/adapters/ and anything that POSTs JSON to localhost:47329 can talk to her — no code required.
Whatever JSON your tool already emits. GitHub sends this to a webhook on every workflow run.
{
"workflow_run": {
"conclusion": "failure",
"head_branch": "main",
"html_url": "https://github.com/.../runs/42"
}
}
match picks the adapter. switch + cases choose a template. {{…}} placeholders are filled from the payload.
{
"match": { "field": "workflow_run", "exists": true },
"switch": "workflow_run.conclusion",
"cases": {
"failure": {
"title": "{{workflow_run.head_branch}}",
"body": "CI failed",
"priority": "urgent",
"actions": [{ "type": "open-url",
"url": "{{workflow_run.html_url}}" }]
}
}
}
Axol's common shape. Any payload that reaches the bubble goes through this — adapters can't smuggle in unknown action types or shell commands.
{
"title": "main",
"body": "CI failed",
"priority": "urgent",
"actions": [{ "type": "open-url",
"url": "https://github.com/.../runs/42" }]
}
Urgent bubbles pin open until clicked. Clicking runs the first action — here, opening the workflow run in the browser.
One adapter ships with Axol (adapters/claude-code.json, for Claude Code hooks). Everything else you add lives in ~/Library/Application Support/Axol/adapters/.
Template language — deliberately tiny:
{{field}} top-level substitution
{{nested.field}} dot-path into objects
{{field | default 'x'}} fallback when missing
{{basename path}} last path component
{{trim field}} strip surrounding whitespace