AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Bug Echo

skill-terryc21-bug-echo-bug-echo · by Terryc21

After fixing a bug, find and rate other instances of the same pattern in the codebase. Two modes: described, or inferred from a recent fix with self-validation. Triggers: "run bug-echo", "echo this fix", "scan for similar bugs", "find other instances", "after-fix scan".

No reviews yet
0 installs
18 views
0.0% view→install

Install

$ agentstack add skill-terryc21-bug-echo-bug-echo

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-terryc21-bug-echo-bug-echo)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Bug Echo? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

bug-echo

> Quick Ref: After a bug fix, identify the pattern, scan the codebase, classify findings, and produce a rated report. > Output: .agents/research/YYYY-MM-DD-bug-echo-.md.

Best invoked after a real fix

bug-echo is most effective when the pattern came from a fix that just shipped. A real fix proves which anti-pattern matters in your specific codebase. Pattern matching after a real fix is dramatically more accurate than pattern matching from a theoretical catalog — the fix is the evidence the pattern is a bug.

The high-leverage loop is surface → verify → generalize, three skills working in sequence:

  1. Surface/unforget (or any tracker) shows you a deferred row you're about to mark Fixed.
  2. Verify — Before trusting the closure, confirm the fix is real. /radar-suite focus on (or just reading the file) catches stale Open rows where the fix shipped weeks ago and nobody updated the ledger.
  3. Generalize — Run /bug-echo with a one-sentence description of what the fix replaced. The output is a rated list of every echo of that anti-pattern across the codebase — including instances that haven't crashed yet but sit under the same runtime conditions.

Bugs that haven't fired yet are the highest-ROI thing in any audit cycle. They cost the same to fix as crashed bugs, but you skip the cost of the crash itself (lost user trust, support tickets, root-cause investigation under deadline). bug-echo is the systematic way to find them.

Companion skills:

  • unforget (https://github.com/Terryc21/unforget) — the surface; consolidates deferred work in one file
  • radar-suite (https://github.com/Terryc21/radar-suite) — the verifier; confirms the fix is real before bug-echo generalizes

bug-echo also runs standalone when you describe the pattern manually (Step 2A below) — useful when no recent fix exists but you've spotted a shape worth chasing.


YOU MUST EXECUTE THIS WORKFLOW. Do not just describe it.

Required output: Every BUG finding MUST include Urgency, Risk of Fixing, Risk of Not Fixing, ROI, Blast Radius, and Fix Effort using the 9-column Issue Rating Table format defined in Step 5. Findings missing any of the six dimensions are invalid.

This skill uses Claude's native tools only. No external scripts or pattern catalogs. AST-grep is optional; if it is installed, prefer it for higher precision on Swift, otherwise fall back to regex via the Grep tool.


Pre-flight

Before any scanning work, verify the working environment is sane.

  1. Check for uncommitted changes:
  • Run git status --porcelain via Bash.
  • If output is non-empty (uncommitted changes exist), use AskUserQuestion to ask: "There are uncommitted changes. If these are from a prior bug-echo session, commit them first so this run has a clean baseline. Otherwise: commit before scanning, or proceed anyway?" Options: "Commit first", "Proceed (accept risk)", "Cancel". On "Commit first", show files changed and stop with a request that the user commit. On "Cancel", stop. On "Proceed (accept risk)", log "User accepted risk of uncommitted changes" and continue.
  1. Note build manifest presence (advisory):
  • Detect via Glob whether Package.swift, xcodeproj, Cargo.toml, package.json, or a similar build manifest exists in the project root. Note the result in the report header. Do NOT run a build — that's the user's responsibility before applying any fix this skill suggests. If no manifest is detected, mention it but continue scanning. This step is advisory metadata for the report, not a gate.
  1. Resolve output directory:
  • Default: .agents/research/. This is a convention shared with radar-suite, bug-prospector, and other Coffee & Code audit skills; if your project doesn't already use it, this run creates it via mkdir -p .agents/research/.
  • Override: if the invoking prompt contains output= (e.g., /bug-echo output=docs/audits/), use that path instead. Create it with mkdir -p if missing. Trailing slash is optional.
  • Write the resolved path to a variable used by Step 5's report-generation step. The report header should also record the resolved path so the user can find it.

Freshness rule

Base all findings on the current source tree only. Do not read prior reports in .agents/research/, scratch/, or auto-memory caches as a source of findings. See § Deferred to v1.4+ for the planned recurrence-detection mode that would cross-reference prior reports.

Scale invariants (protect the small-codebase path)

bug-echo must run identically on a 20-file package and a 5,000-file app on the common path. Large-codebase handling is always a branch entered by an explicit threshold, never a tax on the default flow. Any future edit MUST preserve these invariants:

  1. The sub-500-file scan path is untouched by scale logic. File-count branching lives only in Step 3; a repo under 500 files scans directly and never enters sub-agent dispatch, dedup-across-batches, or any large-repo accommodation.
  2. Count-gated features are inert below their threshold. The already-swept exclusion (Step 2.5, gated ≥6 candidates + prior bug-echo commits) and the high-count tighten offer (Step 2.5, gated ≥25 candidates) must be no-ops below their gates. A small or first-ever run may make at most one cheap, empty git log call and must otherwise behave exactly as v1.2 did.
  3. Thresholds are absolute, not relative to repo size. The 25-match tighten offer keys off raw match count, not a fraction of files, so it never fires spuriously on a small codebase where a pattern legitimately repeats.
  4. No feature promotes a large-codebase branch to the default. Sub-agent dispatch, git-history reads, and pattern-tightening prompts are opt-in-by-threshold. Making any of them unconditional is a regression, not an enhancement.

If you add scale handling, gate it and add it to this list. A change that makes the small-codebase path slower or more talkative has failed review regardless of what it does for large repos.


Step 1: Determine pattern source

Two modes are supported:

  1. User-described: the invoking prompt includes a description of the pattern. Skip to Step 2A.
  2. Inferred from recent fix: the session has a recent edit (in conversation context or from git log -p -1). Use AskUserQuestion to confirm. Go to Step 2B.

If both are possible, disambiguate with AskUserQuestion:

Question (header: "Source"): "How should I identify the pattern?"
Options:
- "Infer from my recent fix" (Recommended). Analyze the diff and derive the pattern.
- "I'll describe it". I'll write out the pattern.
- "Cancel". Stop.

See § Deferred to v1.4+ for the planned catalog-selection mode.


Step 2A: User-described pattern

Summarize the pattern back to the user:

**Pattern:** [name]
**Anti-pattern:** [what the bad code looks like]
**Correct pattern:** [what it should be]
**Search scope:** [file globs or directories]
**Platforms:** [iOS, iPadOS, macOS, watchOS, or "all"]

Conditions form (recommended for multi-condition patterns). Many real bug shapes only fire when 2-3 conditions hold together. Asking the user to articulate them up front produces a sharper scan than free-form prose. Suggested form:

**Condition 1:** [e.g., "Identifiable struct with `let id = UUID()`"]
**Condition 2:** [e.g., "constructed inside a computed `var`/`func` returning `[T]`"]
**Condition 3:** [e.g., "that array feeds a SwiftUI `ForEach` / `List` / `Picker`"]
**Consumer impact:** [why the conditions together produce the bug]

A single-condition pattern (e.g., a deprecated API name) doesn't need this form. Free-form prose is fine. Use the conditions form when the user's description includes "and" twice or more, or when the pattern is shape-based rather than name-based.

Confirm with AskUserQuestion (Yes scan now / Refine / Cancel) before proceeding to Step 3.


Step 2B: Infer from recent fix

This is bug-echo's distinctive mode. Execute these steps directly using Bash and native tools:

  1. Identify the diff source. In priority order:
  • Staged changes: git diff --cached via Bash.
  • Unstaged changes: git diff via Bash.
  • Most recent commit: git log -p -1 via Bash.

Use the first non-empty result. If all are empty, fall back to Step 2A.

Guard against inferring from bug-echo's own fix commit. This applies only when the selected source is the most recent commit (git log -p -1) — staged and unstaged diffs are genuine new work and are never blocked. Step 6 commits applied fixes with a subject line starting bug-echo:. If a user runs /bug-echo, applies fixes, commits via that flow, then runs /bug-echo again with no intervening real fix, git log -p -1 would hand back bug-echo's own fix commit — and inferring a pattern from it just re-derives the pattern those fixes already resolved. To prevent this degenerate self-referential loop: before parsing the most-recent-commit diff, run git log -1 --format=%s via Bash. If the subject starts with bug-echo:, do NOT infer from it. Skip to Step 2A (described mode) and explain briefly: "The most recent commit is a bug-echo fix commit — inferring from it would just re-derive the pattern it already fixed. Describe the pattern you want scanned, or point me at a different fix (e.g., HEAD~1)." This check is universal (not size-gated) and inert unless HEAD is a bug-echo commit.

  1. Parse the diff.
  • Lines starting with - (and not ---) are removed lines (the anti-pattern).
  • Lines starting with + (and not +++) are added lines (the correct pattern).
  • Strip leading whitespace differences when constructing the pattern.
  1. Construct a search pattern from the removed lines.
  • Identify the smallest distinctive substring of the removed code that captures the anti-pattern. Avoid matching on comments, formatting, or unrelated changes.
  • If which ast-grep returns a path via Bash, prefer constructing an AST-grep pattern. Otherwise construct a regex compatible with the Grep tool.
  • Keep the pattern focused. A pattern that matches try? would match every optional-try in the codebase; that's not useful. Prefer something like try?\\s+context\\.fetch for a try?-on-fetch fix.
  1. Self-validate against the pre-fix file.
  • Determine the file the fix was applied to (from the diff header --- a/path/to/file.swift).
  • Read the pre-fix version using whichever of these matches the diff source:
  • Staged diff (Step 1 used git diff --cached): the pre-fix version is the HEAD baseline. Use git show HEAD:path/to/file.swift.
  • Unstaged diff (Step 1 used git diff): the pre-fix version is HEAD. Use git show HEAD:path/to/file.swift.
  • Most recent commit (Step 1 used git log -p -1): the pre-fix version is HEAD~1. Use git show HEAD~1:path/to/file.swift. If this returns empty because the repo has only one commit (git rev-list --count HEAD returns 1), abort with "no pre-fix baseline available; switch to Step 2A described mode."
  • File renamed in the most recent commit: detect via git log --follow --name-status -1 -- path/to/file.swift. If the R line shows a prior path, use git show HEAD~1: instead.
  • Compare the result against the constructed pattern using Grep.
  • If the pattern matches the pre-fix file: validated. Proceed.
  • If the pattern does not match anything: unvalidated. The constructed pattern doesn't actually find the bug it's supposed to find. Halt and try one of:
  • Construct a different pattern (broader or narrower).
  • Fall back to Step 2A and ask the user to describe the pattern manually.
  • Abort with explanation.
  • Do not scan with an unvalidated pattern. Scanning with a bad pattern produces nonsense findings and erodes user trust.
  1. Present the inferred pattern using the Step 2A summary format and confirm with the user before scanning.

The validation step is non-negotiable. If you cannot construct a pattern that matches the pre-fix file, the inference has failed. Do not proceed with a guess.


Step 2.5: Recon scout (decide report shape)

Before running the full scan + classify + rate + write ceremony, run the validated pattern once to count candidates. The count decides the response shape. This step prevents the most common over-spend in bug-echo runs: rendering a 200-line rated report on a pattern that turns out to be already localized.

Execute:

  1. Run a single Grep with the validated pattern across the project's source tree (use the search scope from Step 2A or the file's directory from Step 2B). No classification yet — just count file:line matches.
  2. Exclude sites you already swept (large-codebase multi-pass guard). On a big codebase you will often bug-echo the same anti-pattern across several sessions as you chip away at it. Sites fixed-and-committed in a prior bug-echo run should not resurface as fresh candidates.

Gate — run this sub-step only when ALL of these hold:

  • recon_candidate_count from step 1 is ≥ 6 (below that, the bucket is already lightweight and the extra work is not worth it), AND
  • git log --grep="^bug-echo:" --oneline -1 via Bash returns at least one commit (i.e., this repo has prior bug-echo fix commits).

If either condition is false, skip this sub-step entirely — set recon_swept_count = 0 and continue. On a small codebase, or a first-ever run, this is a single cheap git log call that returns empty and changes nothing.

When the gate opens:

  • Read the file:line pairs touched by prior bug-echo commits: git log --grep="^bug-echo:" --name-only --format="%H" -n 50 via Bash. Collect the set of files those commits modified. Cap the walk at the 50 most recent bug-echo: commits (the -n 50 above): the cost of the walk is bounded by commit count, not calendar time, so it stays predictable no matter how long the project has used bug-echo. If more than 50 exist, note in the reconciliation line that older bug-echo commits were not cross-referenced — those sites simply get re-classified as fresh, which is safe (the worst case is a little redundant work, never a wrongly hidden bug).
  • For each current candidate match, if its file appears in that set, re-read the match site (Read tool, 5-line window) and check whether the anti-pattern is still present. Git history says the file was touched; only the live source says whether this specific line is still buggy. Do not exclude on filename alone — a later edit may have reintroduced the pattern, and re-finding it is exactly the value.
  • Exclude a candidate only when the prior-swept file and the current line no longer matches the anti-pattern. Count excluded sites as recon_swept_count.
  • Subtract recon_swept_count from recon_candidate_count before bucketing in the next step. Report both numbers so the user sees the reconciliation (e.g., "9 raw matches, 3 already swept in prior bug-echo commits, 6 fresh").

This is the conservative, ground-truth-only slice of the deferred "recurrence detection" feature: it reads git commit history (authoritative record of what was fixed), never prior .agents/research/ reports (which the Freshness rule forbids as a finding source).

  1. Exclude the just-fixed site itself. When in Step 2B inference mode, the original-fix file already has the fix applied; if the pattern is constructed from removed lines, it should produce 0 matches there. When in Step 2A described mode, no exclusion needed.
  2. Bucket the result:

| Candidates | Report shape | |---|---| | 0 | Emit a one-line note in conversation: "No echoes found. Pattern appears localized to the original fix site." Do NOT write a .agents/research/ report. Stop. | | 1-5 | Lightweight inline report. Classify each match in conversation using Step 4 rules. Render a single Issue Rating Tabl

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.