ast-grep-driven detection + ranking into a JSON ledger, two-track apply (deterministic autofix + model span-edits), configurable report/apply modes. Rule packs for JS/TS, Python, Go, Rust, Java; Svelte/SvelteKit support via project tooling + opt-in grammar. Runtime-neutral SKILL.md (Agent Skills spec). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
11 KiB
name, description, license, metadata, allowed-tools
| name | description | license | metadata | allowed-tools | ||||
|---|---|---|---|---|---|---|---|---|
| simplify-code | 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). | MIT |
|
Read Edit Write Bash(git:*) Bash(ast-grep:*) Bash(sg:*) |
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).
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.
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.
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.
How it works (overview)
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
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.
Step 0 — Locate the ast-grep engine
ast-grep is the only required tool and is bundled with this skill — no install.
Detect platform and pick the binary; fall back gracefully.
- Determine
<os>-<arch>:- OS: Linux→
linux, macOS/Darwin→darwin, Windows→win32. - Arch: x86_64/amd64→
x64, arm64/aarch64→arm64.
- OS: Linux→
- Use
<skill-dir>/bin/<os>-<arch>/ast-grep(ast-grep.exeon Windows). Make it executable if needed (chmod +xon Unix). - If that file is missing (fresh clone — binaries aren't committed), fetch the pinned
release once into that dir, then verify against
bin/checksums.txt: the version is inbin/VERSION; releases are athttps://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 PATHast-grep/ install it (next step). - Verify: run
ast-grep --version. - 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(orsg) already onPATH; 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.
Refer to the binary below as AST_GREP.
Step 1 — Scope the candidate files
Never touch files git ignores.
- In a git repo: candidates =
git ls-filesplusgit 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.
Step 2 — Detect (mechanical, ~0 tokens)
Run ast-grep with this skill's bundled rules and emit JSON:
AST_GREP scan -c <skill-dir>/sgconfig.yml --json <paths>
sgconfig.ymlpoints atrules/(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 extrasignal/findings.
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'seslint(eslint-plugin-svelte) on**/*.svelte, and/orsvelte/compilerwarnings; fold results into the ledger. - Optionally, if a tree-sitter-svelte grammar has been built and enabled in
sgconfig.yml, the TS/JS packs apply to<script>blocks via injection. - Otherwise do model-driven refactors (runes migration, store→
$state, etc.) — and obey the SvelteKit guardrails inreferences/SVELTE.md(never break+page/+layout/load/form-action conventions). Verify withsvelte-checktoo.
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 inreferences/PATTERNS.md),apply_track:ast-grepif the rule carries afix:, elsellm,difficulty:mechanical(has fix) |semantic(needs judgment) |hard(multi-file/risky),confidence(0–1),severity(0–100),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::
"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.jsonscripts (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, recordmodel_used; continue. - fail → revert that one change, mark
status: failedwith 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 markfailedand 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
pendingfindings 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.
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.
Optimization & portability
- 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.