You just fixed a bug. Is the same mistake somewhere else in your project?
bug-echo looks at the fix you just made, works out what the underlying mistake was, and searches the rest of your code for the same thing hiding elsewhere. You get a short report: here's a real one, here's a false alarm, here's one you should look at yourself.
4 min read · how it works · what changed
When you fix a bug, you fix it in one place. But the same mistake is often sitting in three other places you've forgotten about — copy-pasted, typed again from habit, or left behind when a refactor didn't reach every corner.
Those copies wait. One of them breaks for a real user, weeks later.
Finding them by hand means remembering every place the same shape could have appeared, which is exactly what people are worst at.
A linter checks your code against a list of problems somebody wrote down in advance. That's useful, and you should keep using one.
bug-echo works the other way round. It looks for one specific thing: the bug that just proved it was real, in your own code, fifteen minutes ago. Nobody had to predict it. Reality picked it out for you.
That's why it finds bugs your linter has no rule for — the bug was novel enough to need fixing in the first place.
| A linter is better at | bug-echo is better at |
|---|---|
| Running on every save | Running once, after a fix |
| Problems everyone already knows about | The specific bug you just hit |
| Style and formatting | Copies of your latest real bug |
| Mature, well-tested rules | New bugs nobody wrote a rule for |
The safeguard. Before searching, bug-echo tests its understanding against the broken version of your file. If it got the bug wrong, it stops rather than searching for the wrong thing. You never get a report built on a misunderstanding.
Both apps use the same skill in skills/bug-echo/. No separate
Codex copy or fork is needed. You need a coding assistant with access to your project;
Git is needed to infer a pattern from a fix. This is not a custom GPT for browser chat.
Ask Codex:
$skill-installer Install the bug-echo skill from https://github.com/Terryc21/bug-echo/tree/main/skills/bug-echo.
Then, in your project:
$bug-echo
$bug-echo "await inside a forEach, so nothing actually waits"
$bug-echo --version
If it does not appear, start a fresh Codex session.
Run these one at a time inside Claude Code, not Terminal:
/plugin marketplace add Terryc21/bug-echo
/plugin install bug-echo@bug-echo
Then use the installed command (normally /bug-echo:bug-echo; check the / menu):
/bug-echo:bug-echo
/bug-echo:bug-echo "await inside a forEach, so nothing actually waits"
/bug-echo:bug-echo --version
For contributors or anyone who wants both apps to read the exact same files, clone once and link the skill subfolder into both discovery locations:
git clone https://github.com/Terryc21/bug-echo.git /path/to/bug-echo
mkdir -p ~/.agents/skills ~/.claude/skills
ln -s /path/to/bug-echo/skills/bug-echo ~/.agents/skills/bug-echo
ln -s /path/to/bug-echo/skills/bug-echo ~/.claude/skills/bug-echoReplace /path/to/bug-echo with your checkout's absolute path. If either destination
already exists, preserve it before replacing it; do not overwrite an existing skill
blindly. For Claude Code, choose this manual install or the plugin, so two copies
do not compete. A manual Claude install uses /bug-echo.
Update a linked checkout with git -C /path/to/bug-echo pull --ff-only; both apps then
read that checkout. For a Codex copy installed by the installer, update or reinstall
it from the same repository subfolder. For a Claude plugin install, update its
marketplace and plugin. Start fresh sessions and ask --version to confirm the
version and loaded path. A copied installation does not track GitHub automatically.
The examples below use /bug-echo as shorthand. In Codex, substitute $bug-echo;
for a Claude plugin, use its listed command. You can also ask in plain words.
After a bug fix, run the skill to look for siblings. A committed fix is convenient, but staged or unstaged fixes also work; name the source when several changes are present. No recent fix? Describe the pattern instead.
A real one. I'd fixed a bug where saving on macOS closed the whole window.
- Read my fix and worked out the pattern.
- Checked itself against the broken version of the file. If it had misunderstood, it would have stopped here.
- Searched all 596 files. Two seconds, three hits.
- Read each one in context. One was my own fix. One only looked similar and was harmless. One was real — a "Done" button on the same screen with the same mistake, written slightly differently.
- Wrote a report with the file, the line, how bad it was, and a suggested fix.
That third one had been in shipping code for weeks. It would have found a user eventually.
Read the full report → · the same thing in TypeScript · a run where the fix already existed elsewhere
Each finding is labelled BUG (real), WATCH (fine today, one small step from breaking), OK (false alarm), or REVIEW (your call), with a file and line number.
This is the most useful thing in this README, so it isn't buried at the bottom.
Some bugs aren't in any one file. Every file is fine on its own, and the problem only appears in how they fit together.
A real one from my app: a screen added a button to its toolbar — correct. Its parent screen added the same button — also correct. Neither file was wrong. But the parent drew the child inside its own navigation stack, the two toolbars merged, and you saw the button twice.
There is nothing to search for there. You can't grep for "a screen that adds a toolbar button while its parent also adds one." That isn't text, it's a relationship.
So stop searching for the bug and search for the ingredient. The bug has no shape; the setup does:
- Search for one half. In my case, every screen putting that button in a toolbar. 28 files.
- Open each one's callers. Is it drawn inline, shown as a sheet, or pushed as a new screen? You cannot tell from the file itself.
- Only some of those are bugs. Inline under a parent that owns a toolbar is broken. A sheet is fine — it gets its own. A pushed screen is fine.
Step 2 is the part no tool can do for you. Steps 1 and 3 are mechanical.
Mine took 20 minutes across 28 files and found nothing new. That's still worth having — "I checked, there aren't any more" beats "I wonder if there are more" the day before you ship.
And here's the thing worth noticing: bug-echo would have handed me a 28-row list too, and every row would have been a false alarm, because putting that button in a toolbar isn't a bug by itself. I'd have done the walk anyway, after paying for a report first.
When every match is a false alarm, that usually isn't a pattern that needs tightening. It's a sign the bug isn't pattern-shaped and you've reached for the wrong tool.
How to tell in advance: could you spot the bug reading one file top to bottom? Then bug-echo is right. Would you need a second file open to know whether the first one is wrong? Do the walk.
For SwiftUI I eventually turned this walk into its own skill, ui-path-radar, because I kept doing it by hand.
- Typos, one-character changes, anything isolated
- One-off code nothing else calls
- Cleanup where the pattern is on its way out anyway
- A fix whose diff is mostly unrelated tidying — describe the bug in words instead
Rule of thumb: if the bug surprised you, run it. Surprise means the shape wasn't on your mental list of things to watch for, which makes it exactly the kind of thing likely to be sitting somewhere else unnoticed.
The skill finds what a search can express, judged with about 20 lines of surrounding code. It will not find:
- Bugs in the relationship between two correct files — race conditions, coordination problems. Every file passes on its own. (What to do instead.)
- Patterns buried in noise. If the search matches 200 places and 195 are fine, the report is unusable. Describe the pattern by hand instead.
- Fixes with no shape — a reworded message, a changed constant, a comment. Nothing to learn from.
A clean run means no matches for that pattern. It does not mean no bugs.
For the rest: linters catch single-file style problems, bug-prospector looks for what hasn't broken yet, profilers catch memory and concurrency, and tests catch wrong logic. bug-echo covers one slot — copies of the bug you just fixed.
| bug-echo | bug-prospector | |
|---|---|---|
| Run it | Right after fixing a bug | Before a release, after a crash |
| It asks | "Where else is this same mistake?" | "What could go wrong that I haven't found?" |
| It needs | The fix you just made | Just your code |
Many people run both — prospector before releases, echo after every fix.
v1.8.0. Used through real App Store release cycles on Stuffolio, the app it was built for. Works in any language for finding the pattern; a few conveniences are Apple-specific.
Validation: Three representative scans passed in each app: committed Swift, unstaged Swift, and a described TypeScript pattern. Results, tested versions, and limits.
Contributing. Pull requests welcome. One rule that matters: projects under 500 files must keep scanning directly, with none of the large-project machinery in the path. If a change puts extra steps in front of a small project's scan, it won't be accepted.
bug-prospector — hunt for bugs before a release · workflow-audit — trace SwiftUI behaviour · radar-suite — six skills tracing user paths · unforget — one file for everything you deferred · prompter — rewrite prompts before running them · skill-reviewer — candid reviews of other skills · tutorial-creator — lessons from your own code
New to skills? A skill is a set of instructions your coding assistant follows. Install it once, invoke bug-echo in your app, and it performs the scan. Two words worth knowing: a diff is the before-and-after of a change you made, which is what bug-echo reads; a sibling is another place in your project with the same mistake.
Terry Nyberg, Coffee & Code LLC. Built while shipping Stuffolio through real App Store submissions. If bug-echo has caught something for you, a coffee is appreciated — though a note about how it went on a non-Swift project is worth more.
Apache 2.0 — see LICENSE.