overwatch

Documentation

Watchers

The test runners overwatch finds in a project, and how it reads what they print.

LanguageProject markerFull run firstWatcher
zigbuild.zigthe watcher’s first buildzig build -Doptimize=fast test --watch
pythonpyproject.toml, with uv on PATHuv run --with pytest --with pytest-testmon pytest --testmon-noselectuv run --with pytest --with pytest-watcher --with pytest-testmon ptw . --testmon
pythonpyproject.toml, with pytest on PATHpytestnone: reruns the full run after edits
jsnode_modules/.bin/vitestthe watcher’s first runnpx --no -- vitest watch
jsnode_modules/.bin/jestthe watcher’s first runnpx --no -- jest --watchAll
jsbun.lock or bun.lockbthe watcher’s first runbun test --watch
denodeno.json or deno.jsoncthe watcher’s first rundeno test --watch
gogo.mod, with gotestsum on PATHgotestsum -- ./...gotestsum --watch
gogo.modgo test ./...none: reruns the full run after edits
rustCargo.tomlcargo testnone: reruns the full run after edits
swiftPackage.swiftswift testnone: reruns the full run after edits

Each language’s rows are alternatives, tried in order: the first whose marker, and tool if it names one, is present runs. So a JS project runs the vitest or jest it has installed, ahead of bun test, Go uses gotestsum’s watcher when it is installed, and a Python project without uv runs the pytest on PATH. A polyglot project runs a watcher for every language it has, side by side, each with its own part of the status line, for example overwatch go ✗ 1 failed: TestAdd · overwatch rust ✓ passed.

A language the project’s root lacks is looked for in its sub-projects, the folders up to two levels down, leaving out hidden ones and those search in watchers.json names (node_modules, target, vendor, dist, build and the like). Each folder that has the language gets a watcher of its own, run in that folder and named after it, overwatch go services/api ✗ 1 failed: TestAdd, unless a folder above it has it already. A language at the root covers the whole project, as a Cargo workspace or a JS monorepo with its runner installed at the root does.

A command with no watcher of its own is read by its exit code, so a compile error fails it as surely as a failing test. Any full run that exits nonzero without printing its summary line counts as failed too, rather than going unreported. Colour codes are stripped before anything is read, since some tools (deno) print them even into a pipe.

When a build fails, overwatch names the failing tests (or compile errors, where the tool prints them on one line, such as Rust’s error[E0308]: mismatched types) at the top of the agent’s report, and the first of them in the status line: overwatch python ✗ 1 failed, 2 passed in 0.03s: tests/test_add.py::test_add. A report longer than about 8,000 characters keeps its first quarter and the rest from its end, where cargo, go and pytest print what failed and why.

In every project, whatever its marker, zanity runs beside the test watcher whenever zanity is on PATH, as zanity check . --watch --no-color (zanity 0.1.12 or later). Any error or warning it reports counts as a failure, handed to the agent like a failing build, and the status line shows both results, for example overwatch zig ✓ 4/4 steps succeeded; 48/48 tests passed · overwatch zanity ✓ checked 12 files, no issues found.. Codex, OpenCode, and Pi run the same command after a change, at the same time as each language’s suite, and stop it after its first report. Add another checker to checkers in watchers.json, its marker naming the executable to look for on PATH.

Add a language by adding an entry to watchers in watchers.json, the one table every integration reads, then run python3 scripts/table.py, which copies it into hooks/table.ts for Claude Code (whose plugins cannot import JSON); the tests fail while the two differ. An entry names its marker file, its command, optionally a command that runs the whole suite once first, the line that closes one build’s report, and the pattern on that line that means it failed. Optionally: language, to make it an alternative to the entries before it with the same one; requires, an executable that must be on PATH; result, the line to show when the build’s result comes before the line that closes its report; and failure, a pattern matching each failing test, named by its first group that matched. An entry with no watch command may leave out the summary and be read by its exit code. Patterns are JavaScript regexes, written as JSON strings. When no baseline is configured, the short-lived agent integrations run the watch command through its first complete report and then stop it.