Waiting on CI is dead time. You push, you stare at a spinner, and five minutes later you find out a lint step failed in the first ten seconds. That round-trip is annoying for a human; it’s genuinely expensive for a coding agent that’s blocked, polling a run it can’t reason about, burning a turn on a build that was never going to go green.
The built-in gh run watch was supposed to solve this for me, but in practice it’s flaky. It dies mid-run for reasons I could never pin down, which is the last thing you want a workflow depending on. So I built my own.
Actions Watcher exists to fail fast and stay reliable. It polls one or more workflow runs and exits the moment any job or step hits a terminal failure, whether failure, cancelled, or timed_out, rather than politely waiting for the rest of the pipeline to wrap up. The bad news arrives the instant it exists, so the next fix can start immediately.
Why it ended up in my agent loops
The thing I actually care about here is feedback latency. In an agent-driven workflow, “time to first signal” is the bottleneck: every second an agent waits on CI is a second it isn’t fixing the problem. Watcher collapses that wait to the earliest moment a run can be known to be doomed.
But the part I lean on most is the control flow it unlocks. Instead of an agent sitting in a polling loop, repeatedly checking status and burning tokens and turns on “still running… still running…”, it just kicks off Watcher as a background task and stops. The process blocks silently until the run resolves, then exits and wakes the agent back up exactly once, whether the run failed or passed. No polling, no wasted context, no babysitting. One command in, one clean signal out.
Design choices
- Zero-config by default. Auth resolves from
--token, thenGH_TOKEN/GITHUB_TOKEN, thengh auth token. The repo slug resolves from--repo, thenGITHUB_REPOSITORY, then yourgit remote. Drop it into any repo and it just works, which matters when an agent is invoking it unattended. - Watch several runs at once. Pass multiple run IDs and it monitors them together, exiting on the first failure across all of them.
- Tunable polling down to sub-second intervals for tight local debugging loops.
- Ships as a single Go binary distributed over npm (
npm i -g actions-watcher), so there’s no runtime to install on whatever machine or sandbox needs it.
