ThrashWatch
Spots an AI coding agent stuck in a loop - rewriting the same file over and over, or flipping
it back and forth between two versions - using nothing but file-system behavior. It works with
any agent: Claude Code, Codex CLI, Aider, Copilot agent mode, Cline, or a shell script.
The problem
Coding agents sometimes get stuck. They "fix" a file, break something, undo the fix, reapply
it, and keep going - burning tokens and time while you look at something else. The loop is
easy to see once you look at the diffs, but nothing in the editor tells you it's happening.
Existing tools approach this from the agent's side: transcript watchers that parse a
particular agent's JSONL logs, or gateways that sit between the agent and its model API. Both
depend on the agent's format or setup. ThrashWatch looks at the one thing every agent has in
common - the files it writes - so there is nothing to configure per agent.
How it works
- A single file-system watcher sees every create, change and delete in the workspace.
node_modules, .git, dist, build, out and .venv are always ignored, plus any
globs you add in thrashWatch.exclude.
- External vs human writes. ThrashWatch records when VS Code saves each file. A change on
disk with no VS Code save for that file in the last ~1.5 s (
thrashWatch.saveGraceMs) is
counted as an external write - an agent or a terminal command. Your own saves are ignored.
- For each external write, the file is read and hashed (SHA-1). Files over 1 MB and binary
files are skipped. Each file keeps a short rolling history of content hashes.
- Three signals:
- Oscillation - the new content matches an earlier version of the file, but not the
one right before it (A → B → A, or a longer cycle). This is the classic "undo my own fix"
loop.
- Churn - at least 6 external writes to the same file within 5 minutes (configurable).
- Burst (optional) - at least 60 external writes across the whole workspace within a
minute.
- When a file trips a signal:
- the status bar switches from
$(pulse) agent: calm to $(warning) agent looping: foo.ts;
- you get one non-modal warning per file per churn window, with Show details and
Mute for this file;
- the event is logged to the ThrashWatch Output channel.
- The status bar goes back to "calm" on its own once the loop stops.
Commands
| Command |
What it does |
ThrashWatch: Show Loop Events |
QuickPick of files with recent loop events (reverts, churn alerts, bursts, time since last). Pick a file to open it. Also opens from the status bar item. |
ThrashWatch: Reset History and Mutes |
Forget all hashes, events and muted files. |
ThrashWatch: Toggle Enabled |
Turn watching on or off (saved to your user settings). |
Settings
| Setting |
Type |
Default |
Description |
thrashWatch.enabled |
boolean |
true |
Watch the workspace for agent loops. |
thrashWatch.churnThreshold |
number |
6 |
External writes to one file within the churn window that count as churn. |
thrashWatch.churnWindowMinutes |
number |
5 |
Churn window length. Also the per-file notification throttle. |
thrashWatch.oscillationWindowMinutes |
number |
15 |
How far back content history is compared for reverts. |
thrashWatch.historyLength |
number |
12 |
Distinct content hashes remembered per file (max 100). |
thrashWatch.burstThreshold |
number |
60 |
External writes across the workspace within 60 s that count as a burst. 0 disables it. |
thrashWatch.saveGraceMs |
number |
1500 |
A change this close to a VS Code save of the same file counts as your save. |
thrashWatch.includeEditorSaves |
boolean |
false |
Also count VS Code's own saves. Turn on for agents that edit through the editor (Copilot agent mode, Cline). Leave off if you use auto-save. |
thrashWatch.maxFileSizeKB |
number |
1024 |
Larger files are ignored. |
thrashWatch.notifications |
boolean |
true |
Show warning notifications (status bar and log update either way). |
thrashWatch.exclude |
string[] |
[] |
Extra globs to ignore, e.g. "*.log", "coverage/**". |
Privacy
ThrashWatch makes zero network calls and collects zero telemetry. It stores content
hashes, not content, and only in memory: nothing is written to disk and everything is gone
when the window closes. Memory is bounded (at most 500 tracked files, 12 hashes per file by
default, 200 logged events).
Workspace Trust: ThrashWatch only reads and hashes files. It never runs anything from the
workspace, so it is enabled in untrusted workspaces too.
Known limitations
- External-write detection is a heuristic. VS Code has no API that says which process
wrote a file, so ThrashWatch goes by timing: a change with no VS Code save just before or
after it is treated as external. Git checkouts, formatters run from the terminal, code
generators and build watchers writing into non-excluded folders all look like "external
writes" too. Add their output folders to
thrashWatch.exclude.
- Switching git branches back and forth can look like an oscillation, because the files
really do flip between two versions.
- Agents that edit through the editor (Copilot agent mode, Cline) save through VS Code, so
their writes are ignored unless
thrashWatch.includeEditorSaves is on - and with auto-save
enabled, that setting will also count your own typing.
- The very first revert is missed. ThrashWatch doesn't know what a file looked like before
it first saw it change, so going back to the pre-agent version the first time isn't flagged.
Every later cycle is.
- Files over the size limit and binary files are ignored.
- Local file system only. Virtual workspaces (e.g. remote repositories opened without a
clone) aren't supported.
- It detects, it doesn't stop. ThrashWatch warns you; interrupting the agent is up to you.
License
MIT - the full license text is included with the extension.
| |