Perturbation

Red while an agent works.
Amber when one needs you.
Green when they're all free.

One Chrome toolbar lamp for every coding agent on your machine: Claude Code, Codex CLI, GitHub Copilot CLI and Antigravity CLI, plus opencode, Goose and Qwen Code on the free tier. Optional desktop notifications. It works on macOS, Ubuntu and Windows.

perturbation (n.): in physics and astronomy, a small disturbance of a system by an outside influence, stated without any judgement of whether the disturbance is welcome.

One lamp, four states

Only one lamp is ever lit, and the fill pattern carries the meaning without colour: solid, dotted, ring, dashed.

Perturbed

At least one agent is working. The badge counts the busy sessions.

Needs you

Nothing is working, but a session waits for a permission or an answer.

Unperturbed

Every watched agent is free.

Not connected

The local helper isn't running, or no agent is watched. Run the installer.

What you get

Small, local, and with no dependencies beyond Python 3.

Install

Requires Python 3.9 or newer and Google Chrome.

git clone https://github.com/Tpojka/perturbation.git
cd perturbation
./install.sh

Choose what to watch and what to install

Which agents should be watched?
  [x] 1) Claude Code            found ~/.claude
  [x] 2) Codex CLI              found ~/.codex
  [ ] 3) GitHub Copilot CLI     not found
  [x] 4) Antigravity CLI        found ~/.gemini/antigravity-cli
  [x] 5) opencode               found ~/.config/opencode
  [ ] 6) Goose                  not found
  [ ] 7) Qwen Code              not found
Space or 1-7 toggles, ↑/↓ moves, a: all, n: none, Enter confirms:

What should be installed?
  1) Chrome extension
  2) Chrome extension + OS notifier
  3) Nothing (exit)
Choose 1, 2 or 3: 2
Play a sound with notifications? [Y/n]:
Let Antigravity show "needs you" alerts? This sets its status line command. [y/N]:

Finish in Chrome (once)

  1. Open chrome://extensions and turn on Developer mode.
  2. Click Load unpacked and pick the extension folder the installer printed.
  3. Pin Perturbation to the toolbar.
  4. Restart any running agent sessions so they load the hooks. In Codex, run /hooks and trust the new hooks.

Had Claudication, Codexalgia, Copilonidal or Antigravalgia installed? The installer lists what they left behind and offers to remove it, keeps your notification settings, and prints the old extension IDs to remove at chrome://extensions.

OS notifier

You get a notification when an agent finishes a turn and when it needs you. The title names the agent and the project.

WhenTitleText
An agent finishes a turnClaude is ready · projectthe first line of its last message, or "Task finished"
An agent needs a permission or an answerCodex needs you · projectthe prompt, or what it wants to run
A turn ends with an errorClaude stopped · projectthe error type

Notifications are sent only when a session's state actually changes, never for a reminder that repeats a finished turn, and each agent can be muted in the popup.

Change settings any time

Run these from the repository. They take effect at once, with no restart:

python3 -m perturbation.install set agents claude,codex      # watch exactly these
python3 -m perturbation.install set sound off
python3 -m perturbation.install set notifications off
python3 -m perturbation.install mute codex on                # silence one agent, keep its lamp
python3 -m perturbation.install statusline antigravity on    # "needs you" alerts for Antigravity
python3 -m perturbation.install status
python3 -m perturbation.install doctor

On Windows, use py -3 instead of python3.

How it works

Each agent's hooks write its sessions' state. One small local helper sends the sum to Chrome.

Claude Code   ──hook──┐
Codex CLI     ──hook──┤
Copilot CLI   ──hook──┼──► perturbation.pyz hook <agent> ──► sessions/<agent>/<session>  (busy | waiting | ready)
Antigravity   ──hook──┘                                 └──► desktop notification (optional)

Chrome ──starts──► perturbation.pyz host
                      watches sessions/, pushes a per-agent summary on change
                                  │  native messaging
                                  ▼
                   extension: lamp + badge + tooltip, popup on click

Each agent is one small adapter that knows its hook file and its events. Everything else is shared: one hook handler, one state tree, one host, one lamp.

AgentBusyNeeds youReady
Claude Codeprompts, tool calls, compactionpermission prompts, questionsStop, a manual compaction, an error
Codex CLIprompts, tool callspermission requests unanswered for 5 sStop, Interrupt
GitHub Copilot CLIprompts, tool callspermission prompts, questionsStop, an unrecoverable error
Antigravity CLIinvocations, tool callsits status line, when you turn it onStop when fully idle
opencodesession status, tool callspermission promptssession.idle, an error
Gooseprompts, tool calls, file and shell stepsnever: Goose has no permission eventStop
Qwen Codeprompts, tool calls, compactionpermission prompts and requestsStop, a manual compaction, an error

Every hook command exits 0 and prints nothing, even when Python or the app is missing. Copilot denies a tool call when a hook fails, Claude's PreCompact blocks compaction on exit 2, and Antigravity reads a hook's stdout as a decision, so this is what keeps the agents unharmed. A busy session with no activity for 15 minutes counts as ready, and one waiting for an hour does too, for the agents that never say goodbye.