13 KiB
name, description
| name | description |
|---|---|
| codemap | Generate and incrementally maintain an interactive architecture map plus a per-module code-quality audit (health scores 0-100, smell findings, lines-of-code, and a clickable dependency graph) for any codebase. Use when the user wants to visualize a project's functional modules, audit or score code quality, check whether the architecture map is stale / up to date, incrementally refresh it after code changes, or auto-fix the findings of a specific module. Module audits are run by independent subagents against a fixed rubric. Triggers: "architecture map", "module map", "audit the codebase", "score the code", "visualize the project", "is the arch map current", "update the architecture diagram", "fix module X". |
Architecture Audit & Map
Builds and maintains three coupled artifacts for a project:
modules.json— the source of truth: every functional module (not file) with its paths, dependencies, coupling, LoC, content hash, score, grade, tags, findings.architecture-map.html— a self-contained interactive map (layered modules, dependency highlighting, health coloring, audit-report view).architecture-audit.md— the written report (per-layer scores, per-module LoC table, worst offenders, cross-cutting themes).
The HTML and MD are always regenerated from modules.json by render.py. Never
hand-edit them. The state file makes everything incremental: a content hash per
module tells us exactly what changed and what needs re-auditing.
Standard
The scoring rubric, smell taxonomy, severity levels, and the required subagent prompt
are fixed in reference/STANDARDS.md — read it and follow it verbatim. The state
schema is in reference/DATA_MODEL.md. Do not improvise scoring or invent tags.
Conventions
SKILL_DIR= this skill's directory. Scripts are atSKILL_DIR/scripts/*.py, template atSKILL_DIR/assets/template.html. Use python3, stdlib only.- Default artifact locations (override if the user/project prefers): state (the data)
at
<project>/.claude/codemap/modules.json; the generated outputs at<project>/dev_docs/architecture-map.htmlanddev_docs/architecture-audit.md. The state lives under.claude/(tooling data, kept out of the docs tree); only the two human-facing artifacts go indev_docs/. Setmeta.htmlPath/meta.mdPathso the reciprocal links are correct. - A re-render command (run after any state change):
python3 SKILL_DIR/scripts/render.py --state <state> \ --template SKILL_DIR/assets/template.html \ --out-html <htmlPath> --out-md <mdPath>
Targeting modules without reading the whole state (query.py)
modules.json can be large. To decide what to audit/fix/test, DO NOT read the whole
file — use scripts/query.py to select exactly the modules you need and get back just
ids, file globs, or findings. This keeps agent context small.
# ids of every C-and-below module (feed a fix/audit loop)
python3 SKILL_DIR/scripts/query.py --state <state> --max-grade C --format ids
# modules carrying a specific problem (compact table)
python3 SKILL_DIR/scripts/query.py --state <state> --tag dual-format
# only the file globs to read for the D/F modules → read just those files
python3 SKILL_DIR/scripts/query.py --state <state> --max-grade D --format paths
# the exact findings to fix for one tag, as text
python3 SKILL_DIR/scripts/query.py --state <state> --tag glue --format findings
# what needs re-auditing
python3 SKILL_DIR/scripts/query.py --state <state> --needs-audit --format ids
Filters (AND-combined): --max-grade {A..F} (that grade and worse), --min-score/--max-score,
--tag T (repeatable; ANY, or --match-all), --sev HIGH|MED|LOW, --band, --coupling,
--needs-audit. Output --format: ids | paths | findings | table | json | count. Use
--format paths to read ONLY the relevant source, and --format ids to drive the
per-module subagent loop — never load the full modules.json just to pick targets.
Hard rules
- Every module score comes from an independent subagent. One subagent audits one
module against its
paths, using the prompt inreference/STANDARDS.md. Never score inline in the main thread; never copy one module's score to another. Spawn them in parallel (one message, multiple Agent calls — Explore or general-purpose). - Scripts are deterministic; only decomposition, auditing, and theme-synthesis are
model work.
scan.py/render.py/apply_audit.pynever make quality judgments. modules.jsonis the only thing you edit by hand (structure/decomposition). HTML/MD are generated. Runscan.py --writebefore every render so LoC/hashes are fresh.- Functional modules, not files. A module is a capability (a store, a handler
group, a feature folder, a plugin). Map each to a glob set in
paths. Give every module a 1-linedesc("what it does", shown on click) authored inmeta.lang(setmeta.langto"zh"/"en"; it localizes the UI chrome — module names/ids are never translated). - Four separate, independent subagent roles — never merge two: auditor (scores quality), test-author (writes tests), fixer (changes code), acceptance/verifier (proves no regression). A fix is accepted ONLY when an independent acceptance subagent shows the pre-fix green tests are still green and the build/typecheck is clean. A fixer may not write/edit its own tests or grade its own work — that defeats the gate.
Command: generate (first build)
Use when no modules.json exists yet.
- Decompose the project into functional modules. Explore the tree (parallel Explore
agents for big repos). Identify capabilities and group them into bands (visual
layers in data-flow order, e.g. UI → stores → transport → │wire│ → app → handlers →
core → persistence → plugins). For each module record
id, label, band, path, paths (globs), coupling, deps, desc. Addbands,spine(the critical request path), andmeta(project, htmlPath, mdPath, spineDesc). Write this tomodules.json(no scores yet). Coupling = structural centrality (low/med/high/core); core = the spine hubs. - Compute size:
python3 scripts/scan.py --root <proj> --state <state> --write. It reports every module asunaudited. - Audit — one independent subagent per module, in parallel. For each id in
needs_audit, spawn a subagent with thereference/STANDARDS.mdprompt (filled with the module's label/paths). Collect each JSON result and apply it:python3 scripts/apply_audit.py --state <state> --id <id> --json '<result>' [--rev <git rev>]. Batch the audits (dozens of modules → many parallel agents, but stay within sane concurrency; chunk if needed). - Synthesize
reportThemes(4–7 cross-cutting patterns) from the collected findings and write them intomodules.json. - Render: run the render command. Report the result: avg score, grade spread, worst offenders, and the two artifact paths.
Command: check (is the map current? — read-only)
Use when the user asks "is the architecture map up to date / still accurate?".
python3 scripts/scan.py --root <proj> --state <state>(no--write).- Read the JSON: report
up_to_date, the stale list (code changed since audit), unaudited (new modules with no score), and empty (paths match nothing → likely deleted modules). Do not modify anything. Tell the user exactly which modules drifted and offer to runupdate. - Also sanity-check for new capabilities not yet in
modules.json(a quick look at new top-level dirs / large new files). New modules are model-discovered, not scan-detected.
Command: update (incremental refresh)
Use after code changes, or when check found drift. Only re-audits what changed.
- Reconcile structure first (cheap): if modules were added/removed/renamed, edit
modules.json(add new module entries withpaths; dropemptyones; fix globs). python3 scripts/scan.py --root <proj> --state <state> --write→ getneeds_audit(= stale + unaudited).- Re-audit only those modules, each with its own independent subagent (same
protocol as
generatestep 3). Apply each viaapply_audit.py. Fresh modules keep their cached audit untouched — that is the whole point of the content hash. - Refresh
reportThemesif the changes are material (otherwise keep them). - Render. Summarize what changed: which modules were re-scored and how their score moved.
Command: test <module> (generate tests)
A test-author subagent generates tests for a module — independently of fixing. This
is also the prerequisite for a safe fix (it builds the regression net). Two modes:
- characterization (default before a fix): lock the module's CURRENT observable behavior so a later change can't silently alter it. Assert "same as today", not "correct".
- coverage: add missing unit tests for the module's public surface and the behaviors
named in its
findings.
Steps:
- Detect the repo's test framework + location (pytest / jest / vitest / go test / …) from existing tests near the module; match their style and placement. Do NOT invent a new framework or harness.
- One test-author subagent writes tests against the module's
paths, runs them, and iterates until green on the CURRENT (unmodified) code. It reports: files added, what behavior is now locked, and a coverage note. If a test only passes by asserting a known bug, it must FLAG the bug, not bake it in as desired behavior. - Tests are real source — they stay in the tree (they are the regression net). Record
their globs in the module's
testsfield inmodules.json. Re-runscan.py --writeandrender.py(test LoC is tracked but excluded from the module's own audit scope).
Keep test-author distinct from fixer and auditor.
Command: fix <module-or-finding> (auto-fix, regression-gated)
Use when the user says "fix the findings in module X" / "auto-fix the worst offenders". A fix is only accepted if an independent acceptance subagent proves no regression. Four separate subagents (hard rule 5): test-author → fixer → acceptance → auditor.
- Scope (via
query.py). Resolve the target set withquery.pyinstead of reading the whole state — e.g.--max-grade C --tag dual-format --format idsfor "all C-and- below dual-format modules", then--format findingsfor just the findings to fix and--format pathsfor just the files to read. Confirm with the user before risky fixes (duplication merges, dual-format removal touching a protocol, deleting "dead" code — first verify it is truly unused). - Baseline (test-author subagent). Ensure the module has tests that lock its CURRENT
behavior; if coverage is thin, run
test <module>(characterization mode) first. Run the module's tests + the narrowest build/typecheck on the UNMODIFIED code and record the green baseline (which tests pass, build/type status, key outputs). If you can't get a green baseline, STOP and tell the user — auto-fixing without a behavioral net is not safe. - Fix (fixer subagent,
isolation: "worktree"). Give it the module's paths, findings, andreference/STANDARDS.mdrules; implement the fix and preserve behavior. The fixer must not edit tests (no moving the goalposts) and must not touch files outside its paths without flagging. - Acceptance gate (independent verifier subagent — NOT the fixer). Re-run the SAME
baseline tests + build/typecheck on the fixed code. Return
{pass: bool, regressions: [...], evidence: "..."}. PASS only if every baseline-green test is still green and there are no new build/type errors. On FAIL: report the regression with evidence and revert / hand back to the fixer — do not accept. - Re-audit (auditor subagent, independent). Only after PASS: re-score the module →
scan.py --write→apply_audit.py→ render. Show before/after score AND the acceptance evidence (tests run, all green, build clean). - Never auto-commit unless asked. Report honestly — a fix that fails the gate is
reported as failed, not merged. Record the outcome in the module's
lastFixfield.
Notes
- Coupling vs score are independent (structural vs quality) — see DATA_MODEL.md. The map can color by either (toggle in the header).
- Big repos: parallelize decomposition (Explore) and auditing (one agent per module). Chunk audits if there are many dozens of modules.
- Determinism: same
modules.json→ identical HTML/MD. Commitmodules.jsonso the audit history and incremental diffs are reviewable. - Languages: the engine is language-agnostic —
pathsglobs and LoC counting work for any stack; the subagent reads whatever code the globs point at.