From 81b33fff261a5cf5088f54f01e8822b3c27401f5 Mon Sep 17 00:00:00 2001 From: Giancarmine Salucci Date: Tue, 23 Jun 2026 02:53:19 +0200 Subject: [PATCH] =?UTF-8?q?refactor:=20report-only=20skill=20=E2=80=94=20a?= =?UTF-8?q?ctionable=20findings,=20no=20apply/modes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- README.md | 40 ++-- SKILL.md | 371 ++++++++++----------------------- assets/ledger.schema.json | 54 +++-- fixtures/EXAMPLE-findings.json | 159 ++------------ references/CONSOLIDATION.md | 6 + references/DETECTION.md | 3 +- references/OPTIMIZATION.md | 37 ++-- references/PATTERNS.md | 38 ++-- references/PORTABILITY.md | 10 +- references/SVELTE.md | 3 +- 10 files changed, 251 insertions(+), 470 deletions(-) diff --git a/README.md b/README.md index 21a9cc7..e979fcc 100644 --- a/README.md +++ b/README.md @@ -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// ast-grep rule packs (detect + autofix) +rules// 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 diff --git a/SKILL.md b/SKILL.md index 982b084..fc014a5 100644 --- a/SKILL.md +++ b/SKILL.md @@ -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` (0–100), `confidence` (0–1), + **`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/.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 0–7 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: Linux→`linux`, macOS→`darwin`, Windows→`win32`. Arch: x86_64→`x64`, arm64→`arm64`. -1. Determine `-`: - - OS: Linux→`linux`, macOS/Darwin→`darwin`, Windows→`win32`. - - Arch: x86_64/amd64→`x64`, arm64/aarch64→`arm64`. -2. Use `/bin/-/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//app-.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 `/bin/-/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/-/`), 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 1 — Scope the candidate files +## Step 2 — Detect (mechanical, ~0 model tokens) -Never touch files git ignores. +**Pass A — structural** (`AST_GREP scan -c /sgconfig.yml --json=compact `): +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 --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 ` (JS/TS) and `ruff check --output-format=json ` (Python), read-only. Deeper/type-aware/other-language → optional project toolchain only-if-present. +- *Metrics* — `scc --by-file -f csv --no-cocomo ` (+ 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 ''`) 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 --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 /sgconfig.yml --json -``` +## 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 `