refactor: report-only skill — actionable findings, no apply/modes

The skill no longer modifies code. It emits exactly two artifacts under simplify/:
findings.json (machine-actionable ledger) + a ranked report. Every finding now REQUIRES
title/action/verify (locked in ledger.schema.json) so each is independently executable by
the calling agent. Removed: apply/report modes, Track A autofix execution, baseline gate,
verify-each-change loop, the include-tests switch (tests excluded by default). Reframed
SKILL.md, README, PATTERNS (deterministic/judgment), DETECTION, OPTIMIZATION (judge-only
model tiering), PORTABILITY, SVELTE, CONSOLIDATION accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Giancarmine Salucci
2026-06-23 02:53:19 +02:00
co-authored by Claude Opus 4.8
parent fc7f825268
commit 81b33fff26
10 changed files with 251 additions and 470 deletions
+24 -16
View File
@@ -4,15 +4,16 @@ A portable [Agent Skill](https://agentskills.io/specification) that simplifies a
codebase **safely and incrementally** from any agent runtime — Claude Code, GitHub
Copilot (CLI / VS Code), pi, Codex CLI, Gemini CLI, Cursor.
It reduces complexity, nesting, duplication, magic numbers, dead code, and long
functions **while preserving behavior** — detecting opportunities mechanically with a
bundled [`ast-grep`](https://ast-grep.github.io/) engine, ranking the biggest wins,
and applying fixes one at a time with build/test verification.
It finds complexity, nesting, duplication, magic numbers, dead code, long functions,
duplicated enums/types, near-duplicate functions, and reinvented utilities — detecting
mechanically with bundled engines ([`ast-grep`](https://ast-grep.github.io/), cpd, scc,
biome, ruff), ranking the biggest wins, and writing **one actionable report** for an agent
to act on. It does **not** modify code.
## Two passes
## Two analysis passes (both report-only)
- **Pass A — structural simplification** (per-node): nesting, magic numbers, booleans,
params, dead branches — apply or report, verified per change.
- **Pass A — structural** (per-node): nesting, magic numbers, booleans, params, dead
branches — each emitted as a finding with a concrete `action`.
- **Pass B — consolidation & anti-pattern analysis** (cross-file, **report-only**):
duplicate enums/types, near-duplicate functions, untyped repeated DTOs, reinvented
utilities, plus lint/metrics enrichment. Targets the measured AI failure mode
@@ -24,14 +25,20 @@ All engines are bundled single static binaries (ast-grep, cpd, scc, biome, ruff
`bin/manifest.json`); nothing is assumed about the target environment, and Pass B never
writes into the project.
## Report-only — it never modifies your code
This skill **does not edit, fix, or format anything**. It analyzes and writes two files
under `simplify/`: `findings.json` (machine-actionable ledger) and a ranked
`*-report.md` action queue. The calling **agent** reads the report and acts on it; the
skill has no apply/verify/modify step and no modes.
## Why it stays fast and cheap
- **Codebase out of context.** `ast-grep` finds + ranks candidates mechanically; the
model reads only the flagged spans, from a small JSON ledger on disk.
- **Two-track apply.** Mechanical fixes are applied by deterministic `ast-grep`
autofix rules (zero model tokens, zero hallucination); only genuinely semantic
refactors go to the model.
- **Resumable.** Progress is a `status` field in `simplify/findings.json`; reruns skip
- **Codebase out of context.** Bundled binaries find + rank candidates mechanically; the
model reads only flagged spans, and is used only to *judge* cross-file candidates.
- **Actionable findings.** Each finding carries an explicit `action` (what to change) and
`verify` (how to confirm after acting) so a finding is independently executable.
- **Resumable.** The acting agent flips a `status` field in `findings.json`; reruns keep
finished work.
## Design constraints (deliberate)
@@ -52,7 +59,7 @@ writes into the project.
SKILL.md entry point (read this first)
sgconfig.yml ast-grep project config → rules/
bin/ bundled ast-grep binary per platform
rules/<lang>/ ast-grep rule packs (detect + autofix)
rules/<lang>/ ast-grep rule packs (detection + suggested fixes for the report)
references/ PATTERNS · DETECTION · OPTIMIZATION · PORTABILITY
assets/ ledger.schema.json
fixtures/ messy sample code for validating the skill
@@ -61,8 +68,9 @@ fixtures/ messy sample code for validating the skill
## Install
Copy or symlink this directory into your runtime's skills location — see
`references/PORTABILITY.md` for per-OS, per-runtime instructions. Then ask your agent
to "simplify this codebase" (preview) or "simplify and apply".
`references/PORTABILITY.md` for per-OS, per-runtime instructions. Then ask your agent to
"simplify this codebase" / "find duplication" — it writes `simplify/findings.json` + a
report, which the agent (or you) then acts on.
## Languages
+115 -256
View File
@@ -1,302 +1,161 @@
---
name: simplify-code
description: >-
Simplify and refactor a codebase safely and incrementally — reduce complexity,
nesting, duplication, magic numbers, dead code, and long functions while
preserving behavior. Detects opportunities mechanically with ast-grep, ranks
the biggest wins, and applies fixes one at a time with build/test verification.
Use when asked to simplify, clean up, refactor, reduce complexity, remove dead
code, or improve readability of code in any language (JS/TS, Python, Go, Rust,
Java, Svelte/SvelteKit, and more).
Analyze a codebase and produce a single actionable report of simplification &
consolidation opportunities — complexity, nesting, duplication, magic numbers, dead
code, long functions, duplicated enums/types, near-duplicate functions, reinvented
utilities — for an agent to then act on. Detects mechanically with bundled tools
(ast-grep, cpd, scc, biome, ruff) and ranks the biggest wins. This skill NEVER modifies
code: it only writes a report. Use when asked to simplify, clean up, refactor, reduce
complexity, de-duplicate, remove dead code, find reuse, or review anti-patterns in any
language (JS/TS, Python, Go, Rust, Java, Svelte/SvelteKit, and more).
license: MIT
metadata:
author: simplify-code
version: "0.1.0"
allowed-tools: Read Edit Write Bash(git:*) Bash(ast-grep:*) Bash(sg:*)
version: "0.2.0"
allowed-tools: Read Write Bash(git:*) Bash(ast-grep:*) Bash(sg:*) Bash(cpd:*) Bash(scc:*) Bash(biome:*) Bash(ruff:*)
---
# Simplify Code
Systematic, behavior-preserving code simplification that runs the same way in any
agent runtime (Claude Code, GitHub Copilot CLI/VS Code, pi, Codex, Gemini CLI).
A **report generator**, not an editor. It analyzes a codebase and writes one actionable
report; **it never modifies source files**. The calling agent reads the report and decides
what to act on. Runs the same way in any runtime (Claude Code, GitHub Copilot CLI/VS Code,
pi, Codex, Gemini CLI).
The skill keeps the codebase **out of your context**: a bundled `ast-grep` binary
finds and ranks candidates mechanically, you assemble a small JSON **ledger** on
disk, and then you touch only the flagged spans — one fix at a time, verified.
It keeps the codebase **out of your context**: bundled binaries find and rank candidates
mechanically; you assemble a small JSON ledger on disk; the model is used only to *judge*
cross-file candidates, never to edit.
> Throughout, "run" means execute via your shell/terminal tool; "read the span"
> means read only the indicated line range; "edit" means your native file-edit
> tool. These map to whatever each runtime calls them.
> "run" = execute via your shell/terminal tool; "read the span" = read only the indicated
> line range. There is no edit step — this skill does not change code.
## When to use
Activate when the user wants to simplify / clean up / refactor / de-duplicate /
reduce complexity / remove dead code / improve readability — for a file, a
directory, or a whole repository.
When the user wants to simplify / clean up / refactor / de-duplicate / reduce complexity /
remove dead code / find reuse / review anti-patterns — for a file, directory, or repo. The
output is a report the user (or the agent) then acts on separately.
## What it produces (the output contract)
Always exactly two files under `simplify/` in the target repo — nothing else is written:
1. **`simplify/findings.json`** — the machine-actionable ledger. Every finding MUST carry:
`id`, `kind` (`structural|lint|metric|consolidation`), `detector`, `title`,
`members` (`[{file,line}]`), `severity` (0100), `confidence` (01),
**`action`** (a concrete, executable instruction — what to change), **`verify`** (the
command(s) to confirm it after acting), `status` (`pending` initially), and for
consolidation: `verdict` (`consolidate|partial|keep-separate`). Schema + example:
`assets/ledger.schema.json`.
2. **`simplify/<report>.md`** — a ranked, human/agent-readable **action queue** built from
the ledger, plus a "kept separate (and why)" section.
Every reported finding is **independently executable**: an agent can take one finding, do
its `action`, run its `verify`, and mark `status: applied` — without re-running this skill.
## How it works (overview)
Two complementary passes:
- **Pass A — structural simplification** (Steps 07 below): per-node fixes via ast-grep
(nesting, magic numbers, booleans, params…), with apply/report modes.
- **Pass B — consolidation & anti-pattern analysis** (see "Pass B" section): cross-file,
AI-typical problems (duplication, failure to reuse, reinvented utilities) — **report-only**.
Trigger when the user mentions duplication / consolidate / reuse / DRY / "duplicated
enums or types" / anti-patterns, or asks for a deeper review. Full method:
`references/CONSOLIDATION.md`; taxonomy: `references/ANTIPATTERNS.md`.
```
Pass A: detect (ast-grep, 0 tokens) → assemble ledger (cheap) → infer build/test/lint (once)
baseline gate → apply top findings (Track A: ast-grep autofix | Track B: span edit)
verify each change → report
locate engines → scope files → DETECT (Pass A structural + Pass B consolidation/lint/metrics)
infer build/test (for the `verify` fields) → assemble findings.json (each with action+verify)
JUDGE cross-file candidates (cheap model + AHA guardrails) → write report. No edits, ever.
```
Two modes (default **report**):
- **report** — dry-run: produce proposed diffs + `simplification-report.md`, change nothing.
- **apply** — edit in place, re-running build/test after every change.
Pick mode from the user's words ("dry run / preview / report" → report; "apply / do it
/ fix it" → apply). If unclear, do **report** first and offer to apply.
No modes, no flags, no apply step. The skill analyzes and reports; acting is the agent's job.
---
## Step 0 — Locate the ast-grep engine
## Step 0 — Locate the engines
`ast-grep` is the only required tool and is **bundled** with this skill — no install.
Detect platform and pick the binary; fall back gracefully.
Bundled per platform (`bin/manifest.json`) — no install. Detect `<os>-<arch>`:
- OS: Linux→`linux`, macOS→`darwin`, Windows→`win32`. Arch: x86_64→`x64`, arm64→`arm64`.
1. Determine `<os>-<arch>`:
- OS: Linux→`linux`, macOS/Darwin→`darwin`, Windows→`win32`.
- Arch: x86_64/amd64→`x64`, arm64/aarch64→`arm64`.
2. Use `<skill-dir>/bin/<os>-<arch>/ast-grep` (`ast-grep.exe` on Windows). Make it
executable if needed (`chmod +x` on Unix).
3. The binaries are bundled via **git-lfs**, so a normal clone has them. **If the file is
an LFS pointer or missing** (lfs not installed / not pulled), run `git lfs pull`, or
fetch the pinned release once into that dir and verify against `bin/checksums.txt`:
the version is in `bin/VERSION`; releases are at
`https://github.com/ast-grep/ast-grep/releases/download/<VERSION>/app-<triple>.zip`
(triples: `x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`,
`x86_64-apple-darwin`, `aarch64-apple-darwin`, `x86_64-pc-windows-msvc`). Or just use a
PATH `ast-grep` / install it (next step).
5. Verify: run `ast-grep --version`.
6. **Fallbacks**, in order, if the bundled binary won't run (rare arch, or macOS
Gatekeeper / Windows SmartScreen blocking an unsigned binary):
a. an `ast-grep` (or `sg`) already on `PATH`;
b. if none, tell the user the one-line install for their OS
(`npm i -g @ast-grep/cli` · `brew install ast-grep` · `cargo install ast-grep`
· `scoop install ast-grep`) and offer to continue with **LLM-heuristic
detection** (grep + reading suspicious files) — slower, less precise.
Required: **ast-grep** at `<skill-dir>/bin/<os>-<arch>/ast-grep` (`.exe` on Windows;
`chmod +x` on Unix). Aux (for Pass B): **cpd, scc, biome, ruff** in the same dir. Verify
`ast-grep --version`. Binaries are git-lfs; if a file is an LFS pointer/missing, run
`git lfs pull`, else fetch the asset from `bin/manifest.json` and verify against
`bin/checksums.txt`, else use a PATH copy, else degrade (that detector is skipped with a
note). Absence of any **aux** tool never blocks the run. Refer to the main binary as
`AST_GREP`.
Refer to the binary below as `AST_GREP`.
## Step 1 — Scope
**Aux engines for Pass B** (optional; same bundle/fetch/verify mechanism): `cpd`
(duplication), `scc` (metrics), `biome` (JS/TS lint), `ruff` (Python lint) — see
`bin/manifest.json` for per-platform assets + `bin/checksums.txt`. Each is selected the same
way (`bin/<os>-<arch>/<tool>`), and absence of any aux tool never blocks a run (the relevant
detector just degrades or is skipped with a note).
Read-only scoping; never touch git-ignored files.
- Git repo: candidates = `git ls-files` + `git ls-files --others --exclude-standard`.
- Not a git repo: ask which paths to include.
- Honor any path the user named (intersect with the above).
- **Test files are excluded** (their literals/structure are usually intentional). Filter:
```
\.(test|spec)\.[mc]?[jt]sx?$ · (^|/)(__tests__|tests?|e2e|__mocks__|__snapshots__)/
_test\.go$ · (^|/)test_[^/]*\.py$|_test\.py$|conftest\.py · (^|/)src/test/|Tests?\.java$
```
Record `config.include_tests: false`. (Widen only if the user explicitly asks to include tests.)
## Step 1Scope the candidate files
## Step 2Detect (mechanical, ~0 model tokens)
Never touch files git ignores.
**Pass A — structural** (`AST_GREP scan -c <skill-dir>/sgconfig.yml --json=compact <paths>`):
redundant booleans, magic numbers, deep nesting, long params, etc. (`references/DETECTION.md`).
For Svelte `.svelte`, see `references/SVELTE.md` (project tooling / opt-in grammar).
- In a git repo: candidates = `git ls-files` plus
`git ls-files --others --exclude-standard` (tracked + untracked-but-unignored).
- Not a git repo: ask the user which paths to include before doing anything.
- Honor any path the user named (a file, dir, or glob) by intersecting with the above.
**Pass B — consolidation & anti-patterns** (`references/CONSOLIDATION.md` for the full method):
- *Duplicate enums/labels/unions* — `AST_GREP` extracts enum/union/`as const` defs → member-set cluster.
- *Near-duplicate functions* — `cpd <paths> --min-tokens 50 --reporters json --output simplify/cpd --silent` (exact) **+** `AST_GREP` skeleton extraction (renamed/structural).
- *Untyped repeated DTOs* — `AST_GREP` extracts object literals → key-set cluster; keep only shapes repeated ≥3× with **no** matching interface.
- *Reinventing-the-wheel* — `AST_GREP` function-signature index → name/signature similarity.
- *Lint (defer, don't reimplement)* — bundled `biome lint --reporter=json <paths>` (JS/TS) and `ruff check --output-format=json <paths>` (Python), read-only. Deeper/type-aware/other-language → optional project toolchain only-if-present.
- *Metrics* — `scc --by-file -f csv --no-cocomo <paths>` (+ ast-grep-derived nesting/length/params) → flag god/long functions.
**Exclude test files BY DEFAULT.** Test literals/structure are usually intentional, so
filter the candidate list (e.g. `git ls-files | grep -vE '<pattern>'`) unless the user
opts in. Record the choice in the ledger `config.include_tests`. Default patterns:
## Step 3 — Infer build / test / lint (for the report's `verify` fields)
```
\.(test|spec)\.[mc]?[jt]sx?$ # foo.test.ts, foo.spec.jsx, ...
(^|/)(__tests__|tests?|e2e|__mocks__|__snapshots__)/ # test dirs (any language)
_test\.go$ # Go
(^|/)test_[^/]*\.py$|_test\.py$|(^|/)conftest\.py$ # Python
(^|/)src/test/|Tests?\.java$ # Java
```
Infer from manifests (don't hardcode) and record in `config`; these are written into each
finding's `verify` so the agent can confirm its own changes later. NOT run by this skill.
- Node `package.json` scripts; Python `pytest`/ruff; Go `go build/test`; Rust `cargo
build/test`; Java Maven/Gradle. Set `null` if absent.
**Switch to include tests:** if the user says "include tests / also tests /
--include-tests / with tests", skip the filter and set `config.include_tests: true`.
## Step 4 — Assemble findings + judge cross-file candidates
## Step 2 — Detect (mechanical, ~0 tokens)
Write `simplify/findings.json` (use a cheap/fast model). For each candidate produce a
finding with the **full output contract** (id, kind, detector, title, members, severity,
confidence, **action**, **verify**, status:pending; verdict for consolidation).
- **Structural / lint / metric** findings: `action` = the concrete fix (for a mechanical
ast-grep rule, the suggested rewrite or `AST_GREP scan --rule <id> --update-all`; for a
semantic one, the pattern from `references/PATTERNS.md` + the span). `verify` = `config`
build/test (+lint).
- **Consolidation (Pass B)** findings are **mandatory-judged** before inclusion: give the
cheap model the real spans and apply the judge contract + AHA guardrails in
`references/CONSOLIDATION.md`. Only include `verdict != keep-separate` with
`confidence ≥ 0.6`. Record rejected clusters under `kept_separate` (with the reason) so
reruns don't re-flag. Fan out one judge per cluster where the runtime supports sub-agents.
Run ast-grep with this skill's bundled rules and emit JSON:
Rank by `severity` (biggest win first). **Resumable:** if `findings.json` exists, keep
entries already `status: applied` and refresh the rest.
```
AST_GREP scan -c <skill-dir>/sgconfig.yml --json <paths>
```
## Step 5 — Write the report
- `sgconfig.yml` points at `rules/` (per-language packs: detection + autofix).
- Rules cover: redundant boolean/ternary, magic numbers, dead/unreachable code,
deep nesting, long parameter lists, simplifiable APIs, duplicate-ish patterns.
- **Optional enrichment, only if already installed** (never install, never block on
absence): `scc`, `gocyclo`, `staticcheck`, `cargo clippy`, `knip`, `jscpd`, `pmd`.
Fold their output into the ledger as extra `signal`/findings.
Write `simplify/consolidation-report.md`: a header (mode, scope, `config` commands), a
**ranked action queue** (one block per finding: id · verdict/kind · confidence · title ·
**Where** · **Do** (the action) · **Verify** · Why), a **"Kept separate (do NOT
consolidate)"** section, a **latent drift/bugs** callout, and short Pass-A/lint/metric
summaries. See the example in `assets/ledger.schema.json` and `references/CONSOLIDATION.md`.
See `references/DETECTION.md` for rule authoring and the full enrichment matrix.
**Svelte / SvelteKit (`.svelte`):** ast-grep has no built-in Svelte grammar, so handle
these specially (full guide: `references/SVELTE.md`):
- Detect primarily with the project's own Svelte tooling **if present** — run
`svelte-check` (machine output), the project's `eslint` (eslint-plugin-svelte) on
`**/*.svelte`, and/or `svelte/compiler` warnings; fold results into the ledger.
- Optionally, if the tree-sitter-svelte grammar is built (one command, see SVELTE.md),
scan `.svelte` with `sgconfig.svelte.yml` — TS/JS packs then apply to `<script>` blocks
via injection, plus `rules-svelte/` template rules.
- Otherwise do model-driven refactors (runes migration, store→`$state`, etc.) — and
**obey the SvelteKit guardrails in `references/SVELTE.md`** (never break
`+page`/`+layout`/`load`/form-action conventions). Verify with `svelte-check` too.
## Step 3 — Assemble the ledger (`findings.json`)
Read ast-grep's JSON and write `simplify/findings.json` (create the `simplify/` dir).
Use a **cheap/fast model** for this step if your runtime lets you choose one.
For each match, compute and record a finding (schema: `assets/ledger.schema.json`):
- location (`file`, `start_line`, `end_line`), `lang`, `detector` (rule id),
- `signal` (metric values — e.g. nesting depth, param count, function length derived
from the match range; plus any enrichment metrics),
- `suggested_pattern` (from the catalog in `references/PATTERNS.md`),
- `apply_track`: `ast-grep` if the rule carries a `fix:`, else `llm`,
- `difficulty`: `mechanical` (has fix) | `semantic` (needs judgment) | `hard` (multi-file/risky),
- `confidence` (01), `severity` (0100), `status: pending`.
Rank biggest-win-first:
`severity = max(0, cyclomatic-10)*(lines/20) + unused_params*10 + dup_blocks*50 + ast_grep_hits*5`
(tune as needed; see DETECTION.md).
Write a top-level `config:` block too (filled in Step 4) so everything lives in one file.
**Resumability:** if `findings.json` already exists, load it and process only
`status: pending` items — do not re-scan-and-overwrite completed work.
## Step 4 — Infer build / test / lint commands (once, cached)
These are project-specific and must NOT be hardcoded. Infer them from the project's
manifests/structure, then cache in `findings.json``config:`:
```json
"config": {
"build": "<cmd or null>",
"test": "<cmd or null>",
"lint": "<cmd or null>",
"format": "<cmd or null>",
"detected_from": "package.json|pyproject.toml|go.mod|Cargo.toml|pom.xml|Makefile|...",
"runtime": "node|python|go|rust|java|...",
"inferred_at": "<commit-or-marker>"
}
```
Hints by ecosystem (verify they exist before trusting them):
- Node: `package.json` scripts (`build`/`test`/`lint`), pnpm/yarn/npm.
- Python: `pyproject.toml`/`tox.ini`/`pytest.ini``pytest`; ruff/black for lint/format.
- Go: `go build ./...`, `go test ./...`, `gofmt`, `go vet`.
- Rust: `cargo build`, `cargo test`, `cargo fmt`, `cargo clippy`.
- Java: Maven (`mvn -q test`) / Gradle (`./gradlew test`).
Re-infer only if `config` is missing or the manifest changed. If a command can't be
found, set it `null` and tell the user; treat a missing test command as "no automatic
verification available" (be more conservative; prefer report mode).
## Step 5 — Baseline gate
Run `config.build` then `config.test`. If either FAILS, **STOP** and report — never
simplify on top of a red baseline. (If both are `null`, warn and proceed cautiously.)
## Step 6 — Apply (process the ledger top-down)
Take the top-N `pending` findings (start small, e.g. N=10; respect any user/token budget).
For each, **one change at a time**:
### Track A — mechanical (ast-grep autofix, 0 LLM tokens)
The rule carries a `fix:`.
- **apply mode:** `AST_GREP scan -c sgconfig.yml --update-all` (or `--rule <file>` to
scope to one rule).
- **report mode:** capture the diff with `AST_GREP scan -c sgconfig.yml --json` /
dry-run; do not write.
### Track B — semantic (LLM span edit)
- Read **only** the flagged span (± a few context lines), not the whole file.
- Apply the catalog pattern (`references/PATTERNS.md`) via your native edit tool
(a search/replace style edit — keep the surrounding code identical). For a large
rewrite, replace the whole function/block.
- Keep behavior unchanged. Do not invent abstractions. One pattern per step.
### After each change → verify
Run cached `config.build` + `config.test` (+ `config.lint` if cheap):
- **pass** → mark finding `status: applied`, record `model_used`; continue.
- **fail** → revert that one change, mark `status: failed` with the diagnostic, then
retry up to 3× with more context. Still failing → if your runtime allows a stronger
model, escalate it for that finding; otherwise mark `failed` and move on.
Never batch unrelated changes into a single verify step. Update `findings.json` as you go.
## Step 7 — Report
Write `simplify/simplification-report.md`:
- summary (counts by status, by pattern, by language),
- per-finding: file, pattern, track, result, and (report mode) the proposed diff /
(apply mode) the applied diff,
- anything skipped/failed with the reason and rollback guidance,
- remaining `pending` findings for a future run.
In **report mode** also emit proposed changes as `simplify/patches/*.patch` (e.g.
`git diff` of a throwaway application, or ast-grep diffs) so a human can apply them.
---
## Pass B — consolidation & anti-pattern analysis (report-only)
Run when asked to de-duplicate / consolidate / find reuse / DRY / review anti-patterns, or
as a deeper second pass. **Pass B never edits files** — it produces a report a human (or a
later explicit apply) acts on. Full method + the mandatory judge contract + AHA guardrails:
`references/CONSOLIDATION.md`. Taxonomy of what each anti-pattern maps to:
`references/ANTIPATTERNS.md`.
Scope = the same git-visible, test-excluded set as Step 1. Every detector follows
**extract (0 tokens) → cluster → JUDGE (mandatory, cheap model + AHA guardrails) → report**;
only report `verdict != keep-separate` with `confidence ≥ 0.6`.
- **F1 Consolidation core**
- *Duplicate enums/labels/unions*: `AST_GREP` extracts enum/union/`as const` defs →
member-set cluster → judge (merge vs derive intentional subset vs keep-separate).
- *Near-duplicate functions*: `cpd` for exact copy-paste (`cpd <paths> --min-tokens 50
--reporters json --output simplify/cpd --silent`) **and** `AST_GREP` skeleton extraction
for renamed/structural near-dups → judge (unify / partial / keep-separate).
- *Untyped repeated DTOs*: `AST_GREP` extracts object literals → key-set cluster → report
only shapes repeated ≥3× with **no** matching interface → judge.
- **F2 Reinventing-the-wheel**: `AST_GREP` builds a function-signature index → name/signature
similarity → judge whether new code re-implements an existing util (recommend reuse).
- **F3 Lint enrichment (defer, don't reimplement)**: run bundled `biome` (JS/TS) and `ruff`
(Python) **read-only** (no config needed, nothing written): e.g.
`biome lint --reporter=json <paths>`, `ruff check --output-format=json <paths>`. Map each
diagnostic into the ledger (`detector: "biome:<rule>"`); a tool-fixable rule → mechanical.
Deeper/type-aware/other-language lint and cross-file dead code: optional project toolchain
only-if-present (tsc/eslint, knip, vulture, staticcheck, clippy) — never assume it.
- **F4 Complexity/metrics**: `scc` (file-level complexity/LOC) + `AST_GREP`-derived
per-function nesting/length/params → flag god/long functions; feeds ranking. Report.
Output: `simplify/consolidation-report.md` (clusters + verdict/why/risk/recommendation; a
"kept separate (and why)" section; a "latent drift/bugs" callout) + ledger findings with
`kind: "consolidation"`.
Then stop. The skill's job is done; the agent acts on `findings.json` / the report.
---
## Guardrails
- Never modify git-ignored files. Never change public/observable behavior unless the
user explicitly asks. No speculative abstractions, no drive-by rewrites.
- Prefer Track A (deterministic) over Track B whenever a vetted `fix:` exists.
- If classification is uncertain, treat the finding as Track B + rely on the verify gate.
- Keep your own context lean: work from the ledger and span reads, not whole files.
- Stop and ask if the baseline is red, the repo isn't under version control, or a
change would alter an API/exported symbol.
- **This skill never modifies source files** — no edits, no autofix, no formatting. It only
writes `simplify/findings.json` and `simplify/*.md`.
- Never read or report on git-ignored files. Test files excluded by default.
- Pass B is judge-gated: report only real, confident findings; preserve intentional
duplication (AHA — rule of three, don't merge across layers/bounded contexts).
- Keep your own context lean: work from tool output + the ledger, not whole files.
- If not a git repo, ask for scope before analyzing.
## Optimization & portability
## References
- Token/model tactics (span reads, cheap-model triage, optional sub-agent fan-out,
cache-friendly prefixes): `references/OPTIMIZATION.md`.
- Per-runtime install locations, invocation, and model-override notes:
`references/PORTABILITY.md`.
- Svelte / SvelteKit handling, the opt-in grammar, and guardrails: `references/SVELTE.md`.
- Detection rules & enrichment: `references/DETECTION.md` · Pattern catalog: `references/PATTERNS.md`
- Consolidation pipeline + judge contract + AHA guardrails: `references/CONSOLIDATION.md`
- AI anti-pattern taxonomy → treatment: `references/ANTIPATTERNS.md`
- Token/model tactics: `references/OPTIMIZATION.md` · Per-runtime notes: `references/PORTABILITY.md`
- Svelte / SvelteKit: `references/SVELTE.md` · Bundled engines: `bin/manifest.json`
+35 -19
View File
@@ -34,11 +34,14 @@
"type": "array",
"items": {
"type": "object",
"required": ["id", "file", "start_line", "end_line", "lang", "detector", "apply_track", "difficulty", "severity", "status"],
"required": ["id", "kind", "detector", "title", "members", "severity", "confidence", "action", "verify", "status"],
"properties": {
"id": { "type": "string", "description": "stable id, e.g. f0001" },
"kind": { "enum": ["structural", "lint", "metric", "consolidation"], "description": "structural=Pass A rule; lint=Biome/ruff; metric=scc/derived; consolidation=Pass B (report-only)" },
"verdict": { "enum": ["consolidate", "partial", "keep-separate"], "description": "Pass B only: the judge's verdict" },
"id": { "type": "string", "description": "stable id, e.g. c01 / f0001" },
"kind": { "enum": ["structural", "lint", "metric", "consolidation"], "description": "structural=Pass A rule; lint=Biome/ruff; metric=scc/derived; consolidation=Pass B" },
"title": { "type": "string", "description": "REQUIRED: one-line summary of the finding (what + where, human-scannable)" },
"action": { "type": "string", "description": "REQUIRED: the concrete, executable change the agent should make (not a vague suggestion). The skill itself never performs it." },
"verify": { "type": "string", "description": "REQUIRED: command(s) to confirm the change after the agent applies it (usually config.build/test/typecheck)" },
"verdict": { "enum": ["consolidate", "partial", "keep-separate"], "description": "Pass B (consolidation) only: the judge's verdict" },
"members": { "type": "array", "items": { "type": "object", "properties": { "file": {"type": "string"}, "line": {"type": "integer"} } }, "description": "Pass B only: the cluster's member locations" },
"recommendation": { "type": "string", "description": "Pass B only: the concrete suggested change (never auto-applied)" },
"file": { "type": "string" },
@@ -58,12 +61,11 @@
"dup_blocks": { "type": "number" }
}
},
"apply_track": { "enum": ["ast-grep", "llm"] },
"fix_kind": { "enum": ["deterministic", "edit", "manual"], "description": "informational only (skill never applies): deterministic=an ast-grep autofix command exists; edit=a span edit the agent makes; manual=needs human judgment" },
"difficulty": { "enum": ["mechanical", "semantic", "hard"] },
"confidence": { "type": "number", "minimum": 0, "maximum": 1 },
"severity": { "type": "number", "minimum": 0, "maximum": 100, "description": "ranking score; biggest-win-first" },
"status": { "enum": ["pending", "applied", "skipped", "failed"] },
"model_used": { "type": ["string", "null"] },
"status": { "enum": ["pending", "applied", "skipped"], "description": "pending initially; the ACTING agent updates it after acting (not this skill)" },
"notes": { "type": "string" }
}
}
@@ -72,24 +74,38 @@
"examples": [
{
"config": {
"build": "npm run build", "test": "npm test", "lint": "npm run lint", "format": "npx prettier -w .",
"detected_from": "package.json", "runtime": "node", "inferred_at": "a1b2c3d"
"build": "pnpm -r build", "test": "vitest run", "lint": null, "typecheck": "pnpm -r typecheck",
"format": null, "detected_from": "package.json", "runtime": "node",
"include_tests": false, "passes": ["A", "B"]
},
"engine": { "ast_grep_version": "0.44.0", "binary": "linux-x64", "enrichment": ["knip"] },
"engine": { "ast_grep": "0.44.0", "cpd": "5.0.11", "scc": "3.7.0", "biome": "2.5.0", "binary": "linux-x64" },
"summary": { "passA_structural": 180, "passB_consolidation": 9, "lint": 149, "kept_separate": 3 },
"findings": [
{
"id": "f0001", "file": "src/cart.ts", "start_line": 4, "end_line": 4, "lang": "typescript",
"detector": "ts-prefer-includes", "suggested_pattern": "prefer-includes",
"apply_track": "ast-grep", "difficulty": "mechanical", "confidence": 0.99,
"severity": 5, "status": "pending", "model_used": null, "notes": ""
"id": "c01", "kind": "consolidation", "detector": "dup-function", "verdict": "consolidate",
"title": "Response helpers jsonErr()/json() copy-pasted across 21+ SvelteKit routes",
"members": [
{ "file": "dashboard/src/routes/api/projects/+server.ts", "line": 6 },
{ "file": "dashboard/src/routes/api/stories/[id]/abandon/+server.ts", "line": 6 }
],
"fix_kind": "edit", "difficulty": "semantic", "confidence": 0.95, "severity": 80,
"action": "Create dashboard/src/lib/response.ts exporting json(data,status=200) and jsonErr(status,error)=json({error},status); replace the ~29 local definitions with `import { json, jsonErr } from \"$lib/response\"`.",
"verify": "pnpm -r typecheck && vitest run",
"status": "pending"
},
{
"id": "f0002", "file": "src/cart.ts", "start_line": 20, "end_line": 38, "lang": "typescript",
"detector": "ts-deep-nesting", "suggested_pattern": "guard-clauses",
"signal": { "nesting": 3, "lines": 18 },
"apply_track": "llm", "difficulty": "semantic", "confidence": 0.7,
"severity": 38, "status": "pending", "model_used": null, "notes": ""
"id": "f0002", "kind": "structural", "detector": "ts-deep-nesting",
"title": "Deeply nested conditional in dispatch.ts — guard clauses",
"members": [ { "file": "core/orchestrator/src/dispatch.ts", "line": 222 } ],
"signal": { "nesting": 3 }, "fix_kind": "edit", "difficulty": "semantic",
"confidence": 0.7, "severity": 22,
"action": "Flatten with early returns (see references/PATTERNS.md → guard-clauses); read lines 218-236 and invert the outer conditions.",
"verify": "pnpm -r typecheck && vitest run",
"status": "pending"
}
],
"kept_separate": [
{ "detector": "dup-function", "title": "dead-letter.ts vs run-monitor.ts", "why": "Same skeleton, different concerns (Redis delivery-count vs DB elapsed-time); not duplication." }
]
}
]
+22 -137
View File
@@ -1,146 +1,31 @@
{
"config": {
"build": "npm run build",
"test": "npm test",
"lint": "npm run lint",
"format": "npx prettier -w .",
"detected_from": "package.json",
"runtime": "node",
"inferred_at": "<commit>"
},
"engine": {
"ast_grep_version": "0.44.0",
"binary": "linux-x64",
"enrichment": []
"build": "npm run build", "test": "npm test", "lint": null, "typecheck": "tsc --noEmit",
"format": null, "detected_from": "package.json", "runtime": "node",
"include_tests": false, "passes": ["A", "B"]
},
"engine": { "ast_grep": "0.44.0", "cpd": "5.0.11", "scc": "3.7.0", "biome": "2.5.0", "binary": "linux-x64" },
"summary": { "passA_structural": 2, "passB_consolidation": 0, "lint": 0, "kept_separate": 0 },
"findings": [
{
"id": "f0006",
"file": "fixtures/ts/messy.ts",
"start_line": 31,
"end_line": 36,
"lang": "typescript",
"detector": "ts-deep-nesting",
"suggested_pattern": "guard-clauses",
"apply_track": "llm",
"difficulty": "semantic",
"confidence": 0.7,
"severity": 20.3,
"status": "pending",
"model_used": null,
"notes": ""
"id": "f0001", "kind": "structural", "detector": "ts-prefer-includes",
"title": "Use .includes() instead of indexOf()!==-1 in fixtures/ts/messy.ts",
"members": [ { "file": "fixtures/ts/messy.ts", "line": 4 } ],
"fix_kind": "deterministic", "difficulty": "mechanical", "confidence": 0.99, "severity": 5,
"action": "Replace `tags.indexOf(tag) !== -1` with `tags.includes(tag)` (or run `ast-grep scan -c sgconfig.yml --rule ts-prefer-includes --update-all`).",
"verify": "npm run build && npm test",
"status": "pending"
},
{
"id": "f0008",
"file": "fixtures/ts/messy.ts",
"start_line": 33,
"end_line": 35,
"lang": "typescript",
"detector": "ts-deep-nesting",
"suggested_pattern": "guard-clauses",
"apply_track": "llm",
"difficulty": "semantic",
"confidence": 0.7,
"severity": 20.1,
"status": "pending",
"model_used": null,
"notes": ""
},
{
"id": "f0005",
"file": "fixtures/ts/messy.ts",
"start_line": 20,
"end_line": 27,
"lang": "typescript",
"detector": "ts-long-param-list",
"suggested_pattern": "parameter-object",
"apply_track": "llm",
"difficulty": "semantic",
"confidence": 0.7,
"severity": 10.4,
"status": "pending",
"model_used": null,
"notes": ""
},
{
"id": "f0001",
"file": "fixtures/ts/messy.ts",
"start_line": 4,
"end_line": 4,
"lang": "typescript",
"detector": "ts-prefer-includes",
"suggested_pattern": "prefer-includes",
"apply_track": "ast-grep",
"difficulty": "mechanical",
"confidence": 0.95,
"severity": 5,
"status": "pending",
"model_used": null,
"notes": ""
},
{
"id": "f0002",
"file": "fixtures/ts/messy.ts",
"start_line": 8,
"end_line": 8,
"lang": "typescript",
"detector": "ts-prefer-not-includes",
"suggested_pattern": "prefer-includes",
"apply_track": "ast-grep",
"difficulty": "mechanical",
"confidence": 0.95,
"severity": 5,
"status": "pending",
"model_used": null,
"notes": ""
},
{
"id": "f0003",
"file": "fixtures/ts/messy.ts",
"start_line": 12,
"end_line": 12,
"lang": "typescript",
"detector": "ts-ternary-to-boolean",
"suggested_pattern": "simplify-boolean",
"apply_track": "ast-grep",
"difficulty": "mechanical",
"confidence": 0.95,
"severity": 5,
"status": "pending",
"model_used": null,
"notes": ""
},
{
"id": "f0004",
"file": "fixtures/ts/messy.ts",
"start_line": 16,
"end_line": 16,
"lang": "typescript",
"detector": "ts-ternary-negate",
"suggested_pattern": "simplify-boolean",
"apply_track": "ast-grep",
"difficulty": "mechanical",
"confidence": 0.95,
"severity": 5,
"status": "pending",
"model_used": null,
"notes": ""
},
{
"id": "f0007",
"file": "fixtures/ts/messy.ts",
"start_line": 33,
"end_line": 33,
"lang": "typescript",
"detector": "ts-magic-number",
"suggested_pattern": "magic-number",
"apply_track": "llm",
"difficulty": "semantic",
"confidence": 0.7,
"severity": 3.0,
"status": "pending",
"model_used": null,
"notes": ""
"id": "f0002", "kind": "structural", "detector": "ts-deep-nesting",
"title": "Deeply nested conditional in price() — guard clauses",
"members": [ { "file": "fixtures/ts/messy.ts", "line": 29 } ],
"signal": { "nesting": 3 }, "fix_kind": "edit", "difficulty": "semantic",
"confidence": 0.7, "severity": 22,
"action": "Flatten with early returns (references/PATTERNS.md -> guard-clauses); read the span and invert the outer conditions.",
"verify": "npm run build && npm test",
"status": "pending"
}
]
],
"kept_separate": []
}
+6
View File
@@ -29,6 +29,12 @@ For each candidate cluster, give the model the real code spans and ask for exact
Only report findings with `verdict != keep-separate` AND `confidence >= 0.6`. For
`keep-separate`, record the reason in the report so re-runs don't re-flag it.
When you write the finding into `findings.json`, the judge's `recommendation` becomes the
required **`action`** (a concrete, executable instruction) and you must add a **`verify`**
(the `config` build/test/typecheck command) and a one-line **`title`** — these three fields
are mandatory for every finding (see `assets/ledger.schema.json`) so the acting agent can
execute it without re-running this skill.
### AHA guardrails (put these in the judge prompt — they prevent harmful merges)
- **Rule of three**: 2 occurrences usually isn't worth abstracting; prefer ≥3.
- **"The wrong abstraction is worse than duplication"** (Sandi Metz). If unifying would
+2 -1
View File
@@ -32,7 +32,8 @@ Each match object includes (ast-grep 0.44):
For every `ruleId`, the manifest gives:
- `pattern` — catalog key (`references/PATTERNS.md`),
- `track``ast-grep` (rule carries a `fix:`, deterministic Track A) or `llm` (Track B),
- `track``ast-grep` (a deterministic autofix command exists) or `llm` (needs a judgment
edit); the assembly maps this to the finding's `fix_kind`. The skill never applies it,
- `difficulty``mechanical | semantic | hard`,
- `weight` — contribution to the ranking score,
- optional `requires` — a capability gate (e.g. `svelte-grammar`).
+19 -18
View File
@@ -5,12 +5,13 @@ apply as many as your runtime supports. All are optional accelerators — the sk
correct without any of them.
## 1. Push work out of the model
- Detection is the bundled `ast-grep` binary**zero model tokens**.
- Mechanical fixes are Track A (`ast-grep --update-all`) — the model never reads them.
- Goal: the model only ever touches `difficulty: semantic | hard` findings.
- Detection is the bundled binaries**zero model tokens**.
- Mechanical findings just record a deterministic autofix command in their `action` the
model never reads their code.
- Goal: the model only *judges* `difficulty: semantic | hard` cross-file candidates.
## 2. Span-scoped reads
Never read a whole file to make one edit. From the ledger you already have
Never read a whole file. From the ledger you already have
`file` + `start_line`/`end_line`; read only that range plus a few context lines (~95% fewer
read tokens on large files). The codebase enters context only as flagged spans.
@@ -20,22 +21,22 @@ Use a small/fast model (Haiku / GPT-5-mini / Gemini Flash) for:
- **classification / triage** — mechanical vs semantic, confidence;
- **verification reading** — interpreting build/test output (pass/fail + which finding).
## 4. Model tiering (capability-detected)
- Semantic apply → a mid model (Sonnet).
- `hard` / multi-file / low-confidence / post-retry → escalate to a frontier model (Opus)
**only then**. Confidence-based cascading typically cuts cost ~5085% at ~95% quality.
## 4. Model tiering for the JUDGE (capability-detected)
The model is used only to *judge* cross-file candidates (Pass B), never to edit.
- Cluster + judge on a cheap model.
- `hard` / multi-file / low-confidence → escalate to a frontier model **only then**.
Confidence-based cascading typically cuts cost ~5085% at ~95% quality.
- **How to route**, by runtime:
- Claude Code — sub-agents with a per-agent `model:` override.
- pi — provider-agnostic; pick the model per sub-agent/step.
- Copilot / others without per-step model choice — run on the invoked model and use
`confidence`/`difficulty` to order work and decide attempt-vs-defer (defer `hard` ones
to report mode for a human).
`confidence` to decide which findings to include vs leave out (low-confidence clusters
simply aren't reported).
## 5. Optional sub-agent fan-out
On runtimes with sub-agents (Claude Code, pi), process independent findings/files in
parallel isolated contexts; each returns a compact JSON status row to the parent. Keeps the
orchestrator context clean. Falls back to a sequential ledger loop everywhere else. Keep
each change's verify gate inside its own sub-agent so failures isolate cleanly.
On runtimes with sub-agents (Claude Code, pi), run one **judge** per cluster in parallel
isolated contexts; each returns a compact JSON verdict to the parent. Keeps the
orchestrator context clean. Falls back to a sequential judge loop everywhere else.
## 6. Cache-friendly prefix
Keep static context (the pattern catalog, the manifest, the ledger summary) front-loaded
@@ -48,7 +49,7 @@ span) after it. Don't reorder the stable prefix between findings.
Don't re-scan-and-overwrite a ledger that still has `pending` work.
## Rough budget intuition
detect = free (binary). assembly + triage = cheap model, one pass over compact JSON.
apply = one mid-model span edit per semantic finding + one verify read. Mechanical findings
= free. So cost scales with the number of *semantic* findings you choose to process
(top-N), not with repo size.
detect = free (binaries). assembly = cheap model, one pass over compact JSON. judging =
one cheap-model verdict per cross-file cluster (mechanical/structural findings need no
judging — free). So cost scales with the number of *cross-file clusters* to judge, not with
repo size. The skill performs no edits, so there is no apply/verify token cost.
+21 -17
View File
@@ -1,15 +1,19 @@
# Simplification pattern catalog
The refactoring patterns this skill applies. Each entry: what it is, which rule(s) detect
it, the apply track, a before/after, and **when NOT to apply**. Behavior preservation is
non-negotiable — when in doubt, downgrade to Track B (model edit) and rely on the verify
gate.
The refactoring patterns this skill **recommends in its report** (it never applies them —
the acting agent does). Each entry: what it is, which rule(s) detect it, the fix nature, a
before/after, and **when NOT to do it**. The labels below describe the *kind of action*
the report tells the agent to take:
- **deterministic** — a vetted, behavior-preserving rewrite (an ast-grep autofix command
the agent may run; the finding's `action` names it). Safe for all operand types.
- **judgment** — needs the agent to read the span and edit with care.
`suggested_pattern` keys in the ledger map to the headings here.
`suggested_pattern` keys in the ledger map to the headings here. Behavior preservation is
non-negotiable — when in doubt, mark it judgment and include a `verify` command.
---
## simplify-boolean — Track A (mechanical)
## simplify-boolean — deterministic
Redundant boolean expressions.
Detectors: `ts/js-ternary-to-boolean`, `*-ternary-negate`, `go/rs/java-eq-true`, `*-eq-false`.
@@ -23,7 +27,7 @@ Detectors: `ts/js-ternary-to-boolean`, `*-ternary-negate`, `go/rs/java-eq-true`,
on a non-boolean (we only rewrite the `? true : false` form there, which is always safe;
`== true` rewrites are restricted to statically-typed Go/Rust/Java).
## prefer-includes — Track A (mechanical)
## prefer-includes — deterministic
Detectors: `ts/js-prefer-includes`, `*-prefer-not-includes`.
```diff
@@ -33,7 +37,7 @@ Detectors: `ts/js-prefer-includes`, `*-prefer-not-includes`.
**Not when:** the index value is used afterwards, or `NaN` membership matters (`includes`
finds `NaN`, `indexOf` doesn't).
## identity-compare / membership / truthiness — Track A (Python)
## identity-compare / membership / truthiness — deterministic (Python)
Pattern keys: `identity-compare`, `membership`, `truthiness` (this one section covers all three).
Detectors: `py-is-none`, `py-is-not-none`, `py-not-in`, `py-not-is`, `py-empty-seq-falsy`.
@@ -48,7 +52,7 @@ Detectors: `py-is-none`, `py-is-not-none`, `py-not-in`, `py-not-is`, `py-empty-s
where `len()` doesn't apply / `0` is meaningful (for the truthiness rewrite — the rule
notes this).
## guard-clauses — Track B (semantic)
## guard-clauses — judgment
Detector: `*-deep-nesting` (≥3 nested conditionals).
Flatten with early returns / continues instead of deep nesting.
@@ -66,13 +70,13 @@ Flatten with early returns / continues instead of deep nesting.
**Not when:** branches have `else` side effects, cleanup must run on all paths, or
inverting conditions changes short-circuit evaluation. Read the whole block first.
## parameter-object — Track B (semantic)
## parameter-object — judgment
Detector: `*-long-param-list` (≥5 params).
Group related params into an object/struct/dataclass.
**Not when:** params are unrelated, the function is a hot path where an allocation matters,
or it's a public API signature you may not change.
## magic-number — Track B (semantic)
## magic-number — judgment
Detector: `*-magic-number`.
Replace an unexplained literal with a named constant.
@@ -84,20 +88,20 @@ Replace an unexplained literal with a named constant.
**Not when:** the literal is self-evident (array index, `0`/`1`), or already adjacent to a
clear name.
## extract-method — Track B (semantic)
## extract-method — judgment
No detector rule (so no `manifest.json` entry) — triggered by long functions (length from
ledger `signal.lines`) or repeated blocks. Pull a cohesive chunk into a well-named helper.
**Not when:** the chunk shares many locals (would need a long param list — reconsider), or
extraction would hurt readability.
## remove-dead-code — Track B (semantic), or tool-applied
## remove-dead-code — judgment (or tool-reported)
No detector rule (no `manifest.json` entry) — sourced from enrichment (`knip`, `vulture`,
`staticcheck`, `cargo-machete`) or unreachable-branch findings. Delete unused
exports/vars/imports/branches.
**Not when:** the symbol is part of a public API, used via reflection/dynamic import, or
referenced only in files outside the scanned set — verify first.
## Svelte patterns — Track B (opt-in grammar / enrichment)
## Svelte patterns — judgment (opt-in grammar / enrichment)
Full catalog + SvelteKit guardrails: `SVELTE.md`. Pattern keys used in the ledger:
- `svelte-event-property` — detector `svelte-event-directive-to-property`: `on:click={h}`
`onclick={h}` (model edits the span; the event name varies, so it's not a fixed rule).
@@ -106,7 +110,7 @@ Full catalog + SvelteKit guardrails: `SVELTE.md`. Pattern keys used in the ledge
---
### Applying a pattern (Track B)
### How the ACTING agent applies one (this skill only writes the finding)
1. Read only the finding's span (± a few context lines).
2. Apply exactly one pattern with the native edit tool; keep surrounding code byte-identical.
3. Verify (build/test). Pass → `applied`; fail → revert, retry ≤3, then escalate/defer.
2. Make exactly the change in the finding's `action`; keep surrounding code byte-identical.
3. Run the finding's `verify`. Pass → set `status: applied`; fail → revert, retry ≤3, defer.
+5 -5
View File
@@ -39,8 +39,8 @@ clean up / refactor / reduce complexity / remove dead code". You can also name i
explicitly (e.g. `/simplify-code` where slash-skills are supported, or "use the
simplify-code skill").
Choose a mode in plain words: "preview / dry-run / report" → **report**; "apply / fix it /
do it" → **apply**. Default is report.
There are no modes — the skill always just writes `simplify/findings.json` + a report.
Acting on it is the calling agent's job, separate from this skill.
## The bundled engine
@@ -51,9 +51,9 @@ rare arch, it falls back to a PATH `ast-grep`, then to LLM-heuristic detection.
## Model routing per runtime
See `OPTIMIZATION.md` §4. Summary:
- **Claude Code** — sub-agents accept a `model:` override; route triage→Haiku,
apply→Sonnet, hard→Opus.
The model is used only to judge cross-file candidates. See `OPTIMIZATION.md` §4. Summary:
- **Claude Code** — sub-agents accept a `model:` override; route judging→Haiku,
hard/low-confidence→a stronger model.
- **pi** — provider-agnostic; set the model per step/sub-agent.
- **Copilot / others** — no per-step model pick; run on the invoked model and use the
ledger's `confidence`/`difficulty` to order and defer.
+2 -1
View File
@@ -101,5 +101,6 @@ Refactor the *insides* freely; never violate these conventions:
`action="?/name"`. Don't rename one without the other.
- Never touch generated/`$app/*`/`$env/*` wiring or `.svelte-kit/`.
After any Svelte change, the verify gate should run the project's `svelte-check` and
Each Svelte finding's `verify` should include the project's `svelte-check` (so the acting
agent confirms after it makes the change), alongside
build/test from the ledger `config`, not just a generic build.