agentleFS
charlesmsiegelcharlesmsiegel/coding-skills/skills/code-visualization/SKILL.md

code-visualization

Skill2 starsChanged 4 months agoMITView on GitHub

Build a single-file, tabbed HTML "codebase atlas" that helps a reader understand a codebase well enough to catch errors and poor design — dependency graph with cycle detection (imports plus runtime loads of templates/prompts), churn×complexity hotspots, inventory, test-coverage rendering from existing artifacts, LLM call sites and prompt lineage, plus judgment tabs (architecture overview and reading order, critical-flow sequence diagrams, boundaries, invariants and risks, glossary). Use whenever the user wants to understand, audit, review, onboard into, or document a codebase — "help me understand this repo", "map the architecture", "where are the risky parts", or any request for diagrams/overviews of an existing codebase. Also use it to update or verify a previously generated atlas after the code changed ("update the atlas", "is this atlas still accurate") — it re-checks every file:line citation and revises what went stale. For a single PR/diff/branch, use the pr-visualization skill instead.

The file

We split this skill into the rules it gives an agent

5 rules. About 0 are common, 3 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Read $SKILL/references/llm-tabs.md for the fragment format, available styled primitives, Mermaid patterns, and the per-tab content guide.

    line 51
  2. Very specific:

    Meta convention: always include the commit sha in --meta — update mode (below) uses it to compute what changed since the atlas was written.

    line 99
  3. Distinctive:

    Optimization for small drift: revise in place: When the delta since the old atlas is small relative to what the judgment tabs discuss (a …

    line 109
  4. Distinctive:

    Verify every citation (the "make sure it's right" step, not optional):

    line 114
  5. Distinctive:

    Honest degradation: No git history → no Hotspots tab (assembler drops empty fragments automatically).

    line 128

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Code Visualization — Codebase Atlas

Produce one self-contained HTML file with tabs. Five tabs are generated by bundled scripts (deterministic analysis — inventory, dependencies, hotspots, coverage, LLM ops); the rest you write yourself after reading the code (judgment analysis). The assembler stitches everything together.

The atlas exists to help a reader catch errors and poor design, not to admire the code. Every tab should either orient the reader or surface something checkable: cycles, hotspots, smeared concerns, unenforced invariants, intent-vs-implementation gaps.

Workflow

Let SKILL=/path/to/this/skill and pick a scratch working dir for intermediate files (any temp location works, e.g. WORK=<temp>/atlas-<repo>), with TABS=$WORK/tabs. Commands below are POSIX-shell shaped; adapt paths/variables to your shell (PowerShell: $env:TEMP, ${SKILL}).

1. Locate the repo

The target is a local directory (uploaded, cloned, or already present). If the user gives a GitHub URL, clone it (git clone --depth 200 <url> — some history is needed for hotspots; use full clone if small). If there's no git history, hotspots will be skipped automatically — that's fine.

2. Run the automated analyzers

mkdir -p $TABS
python $SKILL/scripts/analyze_inventory.py <repo> --tabs-dir $TABS
python $SKILL/scripts/analyze_deps.py      <repo> --tabs-dir $TABS
python $SKILL/scripts/analyze_hotspots.py  <repo> --tabs-dir $TABS   # add --since "12 months ago" to tune the window
python $SKILL/scripts/analyze_coverage.py  <repo> --tabs-dir $TABS   # renders an existing coverage artifact; never runs tests
python $SKILL/scripts/analyze_llm.py       <repo> --tabs-dir $TABS   # LLM call sites, models, prompt lineage; writes nothing if the repo calls no model

analyze_coverage.py searches the repo for a parseable artifact (coverage.xml, lcov.info, coverage.out). Three outcomes: it renders a Coverage tab; it prints hints naming an artifact that needs one conversion command (e.g. a .coverage file → run coverage xml, then re-run with --coverage-file); or it finds nothing — then ask the user where coverage lives or whether to skip the tab. Never generate coverage by running the test suite unprompted, and never fabricate numbers. Watch artifact_age_days in its summary: stale coverage is reported with a warning, not hidden.

Each writes its tab fragment and prints a JSON summary to stdout. Read those summaries — they are input for your judgment tabs: cycles found by analyze_deps need semantic explanation in the Boundaries tab; top hotspots from analyze_hotspots deserve a look when you assess risks; the largest/branch-heaviest files from analyze_inventory tell you where to spend reading time.

Dependency extraction covers Python, JS/TS (including @/ root aliases), Go (every go.mod in the repo), Rust (workspace crates and mod declarations), Java/Kotlin/Scala (resolved against declared packages, so Gradle/Maven module layouts work), C# (declared namespaces), Ruby. Check resolution in the summary: it reports, per language, how many imports that appear to name the repo's own code actually resolved. A low ratio (the tab shows a warning too) means the graph understates that language's coupling — say so in the Overview, and consider building that part of the graph by hand from what you can see (build files, package declarations) rather than presenting sparseness as decoupling. JS/TS entries also carry skipped_in_strings: import-shaped text found inside string and template literals and left out on purpose. It is near zero in ordinary code; a count that rivals the imports counted (the tab warns) means the literal guard misread the code and the graph is missing real imports. For languages outside the list the graph may be sparse — note that honestly in the Overview rather than presenting an empty graph as "no coupling". Each summary also reports what was excluded (unrecognized-language files, >2 MB files, churned non-code files) — carry those caveats into the judgment tabs instead of letting a sparse tab read as a small codebase.

The graph draws a second kind of edge: runtime loads (dashed), where a file reads another file instead of importing it — a rendered Jinja template, a prompt assembled from prompts/*.md, an embedded schema. A referenced asset becomes a node; an unreferenced one does not. Two things in analyze_deps's summary deserve judgment rather than transcription: orphan_assets (a prompt nothing loads is either dead or loaded by a path this analysis cannot see — say which you believe and why) and any cycle that exists only because of load edges. Resolution is textual, so a path built entirely at runtime is invisible; resource_caveat carries the wording to repeat when you cite these numbers.

analyze_llm.py writes the LLM Ops tab only when the repo actually calls a model. Its summary is input for two judgment tabs: prompt assets and their call sites belong in Critical Flows (a prompt is a step in the flow, not a constant), and its gaps list — missing max_tokens, no timeout or retry, a model id frozen at a call site, an interpolated system prompt — belongs in the risk ranking in Invariants & Risks, where you decide which of them actually matter here. The gaps are mechanical absences, not verdicts: a missing timeout inside a batch job may be entirely deliberate, and saying so is a finding too.

Check the module grouping before you trust the dependency graph. analyze_deps prints module_list — read it against what you know of the repo. The grouping is wrong if modules are named for packaging rather than design (src, app, a lone repo-name directory), if a subsystem you know exists is missing, or if a codebase you have reason to think is large came back as a handful of nodes. Re-run with --depth N (counts nesting levels, skipping structurally-named directories: --depth 1 on a frontend/+backend/ repo gives the two arms, --depth 2 gives the modules inside each). If the graph still can't be made to reflect the real structure, say so in the Overview rather than presenting a misleadingly simple picture as the architecture.

Other knobs: --max-nodes N caps the graph size; every analyzer takes --exclude dir1,dir2 to skip generated code (*.pb.go trees, migrations/, openapi/) that would otherwise swamp size and churn rankings. --exclude matches directory names anywhere in the tree, so it cannot tell scripts/ from tests/scripts/. To map one part of a repo, give every analyzer --root DIR instead, repeated once per directory (--root src/billing --root tests): only files under the roots are reported, while the rest of the repo is still read for context — imports and loads resolve exactly as in a repo-wide run, and analyze_deps counts the edges that leave the roots under outside_roots instead of drawing them or calling them unresolved. A root that is not a directory in the repo is refused (exit 2).

3. Read the code

Before writing judgment tabs, actually read: entry points, the files the summaries flagged (top fan-in, top hotspots, largest files), configuration, and a sample of tests (tests reveal intended behavior and invariants). Budget reading toward where the summaries point. For large repos, read structure everywhere but depth only where it matters.

4. Write the judgment tabs

Read $SKILL/references/llm-tabs.md for the fragment format, available styled primitives, Mermaid patterns, and the per-tab content guide. Write these fragments into $TABS:

FileTabContent in brief
01-overview.htmlOverviewThesis, entry points, reading order, intended-architecture map, decisions
05-flows.htmlCritical FlowsSequence diagrams for 2–4 flows + their risky seams
06-boundaries.htmlBoundaries & OwnershipConcern ownership, data ownership, violations, cycle explanations
07-invariants.htmlInvariants & RisksInvariants + where enforced, failure handling, concurrency, risk ranking
08-glossary.htmlGlossaryDomain term ↔ code name drift, misleading names

(Numbers 02–04, 09 and 10 are the script-generated tabs; the prefix controls tab order. Never hand-edit them.)

Eval results, if the project has them. While reading, look for evaluation artifacts — evals/, benchmarks/, eval_results*.json, benchmark reports in CI config or the README. If found, summarize what is measured and the latest results in the Overview (and treat systematically-failing evals as entries for the risk ranking in Invariants). If the project clearly runs evals but you can't locate results, ask the user where they live. No eval infrastructure at all is worth one honest line in the Overview, not an invented section.

5. Verify citations against the codebase

Judgment tabs are full of file:line claims; check them mechanically before shipping:

python $SKILL/scripts/verify_citations.py <repo> --tabs-dir $TABS --fragments 01,05,06,07,08

Exit 1 means hard breaks (missing/ambiguous files, lines out of range) — fix the fragments. Exit 0 is necessary, not sufficient: the JSON echoes the current content of every cited line (cited_content); skim it and confirm each line still supports the sentence around it. Also check citations_skipped — tokens that look like citations but weren't verified (unknown extension, odd format); rewrite them as path/file.ext:LINE so they get checked. Rewrite bare filenames the verifier had to disambiguate (e.g. termui.py → src/click/termui.py) so future verification stays unambiguous.

Then lint the fragment protocol mechanically — a stray <script> in a judgment tab would ship and execute:

python $SKILL/scripts/lint_fragments.py --tabs-dir $TABS

6. Assemble and deliver

The atlas's canonical home is inside the repo at docs/codemap.html — it's a self-contained, commit-friendly artifact that should version with the code it describes (and update mode looks for it there).

mkdir -p <repo>/docs
python $SKILL/scripts/assemble.py --tabs-dir $TABS \
  --out <repo>/docs/codemap.html \
  --title "<repo> — Codebase Atlas" \
  --subtitle "One-sentence description of the system" \
  --meta "generated <date> · <commit short-sha> · <N> files, <M> LOC" \
  --footer "Generated by code-visualization. Automated tabs: Inventory, Dependencies, Hotspots, Coverage, LLM Ops. Judgment tabs authored from code reading — verify citations before acting."

If the repo is the user's working copy (e.g., in Claude Code), that file is the deliverable — offer to commit it. If instead the repo is a throwaway clone in a sandboxed chat environment, the user can't browse it: copy docs/codemap.html to the user-visible outputs directory (e.g. /mnt/user-data/outputs/<repo>-codemap.html) and present that copy, mentioning that its intended home is docs/codemap.html in the repo.

Open the result once (at minimum grep that your Mermaid blocks and JSON blocks made it in, and that no fragment was skipped unexpectedly in assembler output). Then present the file with a 2–4 sentence summary of the most important findings (the risk ranking headline, cycles if any). Don't recap every tab.

Meta convention: always include the commit sha in --meta — update mode (below) uses it to compute what changed since the atlas was written.

When the codebase has moved on: rerun vs. revise

(Reviewing a PR does not update the atlas — pr-visualization deliberately leaves docs/codemap.html alone. Every atlas update, small drift or large, runs from here.)

An existing atlas has gone stale after merges. Start with the trust check — python $SKILL/scripts/check_codemap_state.py <repo> (add --codemap path/to.html if the atlas lives somewhere nonstandard; exit codes: 0 current, 2 missing, 1 everything needing action — so don't chain it with &&). How to update depends on its verdict: current needs nothing; unknown-vintage (no parseable sha) means verify everything as if stale; stale permits either path below; merge-resolution-suspect means the codemap was last changed by a merge commit (hand-resolved conflict or a silent auto-splice of two branches' revisions) and demands full re-verification with reconciliation against both merge parents' versions, or a rebuild; conflict-markers means the file is corrupt — always rebuild, using both parents' extracted versions as findings checklists. Then pick between two ways to bring it current:

Default: rerun from scratch, then reconcile. Judgment tabs written by revising old claims inherit their framing — the classic failure is patched citations wrapped around a now-wrong narrative. So for anything beyond small drift (many merges, refactors, or changes touching files the judgment tabs discuss), regenerate: run the full workflow above against the current code without re-reading the old judgment tabs first. Then use the old atlas as a findings checklist, not a draft: extract it (extract_tabs.py <repo>/docs/codemap.html --out-dir <WORK>/old-tabs) and compare finding-by-finding — every risk, cycle explanation, and invariant in the old atlas must be either present in the new one or deliberately closed ("resolved by <sha>"), and closures are worth a line in the new atlas since "risk #2 from last time is fixed" is information a fresh atlas can't otherwise express. This reconciliation pass is what protects against fresh-run variance silently dropping coverage.

Optimization for small drift: revise in place. When the delta since the old atlas is small relative to what the judgment tabs discuss (a few merged PRs, no structural change), revision is cheaper and keeps continuity:

  1. Recover fragments from the canonical location: python $SKILL/scripts/extract_tabs.py <repo>/docs/codemap.html --out-dir $TABS (fall back to wherever the user says the atlas lives). Fragments come back with their canonical numbering — an atlas that dropped a tab (e.g. no Hotspots) leaves a gap rather than shifting later tabs — so the analyzer re-runs and --fragments lists below stay correct. The printed meta holds the sha the atlas was generated at (per the meta convention).
  2. Regenerate the automated tabs by re-running the three analyzers with the same --tabs-dir — they overwrite fragments 02–04 with fresh data. Never hand-edit automated tabs.
  3. Find what moved: git diff --stat <old-sha>..HEAD, git log --oneline <old-sha>..HEAD. A pr-visualization report for the merged change is a ready-made list of behavioral changes to fold in. Any file that both changed and is cited or discussed in a judgment tab needs its claims re-checked against the code, not just re-cited.
  4. Verify every citation (the "make sure it's right" step, not optional):
    python $SKILL/scripts/verify_citations.py <repo> --tabs-dir $TABS --fragments 01,05,06,07,08
    Fix hard breaks (exit 1). For every citation in a file the diff touched, compare cited_content against the claim — line numbers frequently survive while the code at them changes meaning.
  5. Revise: update stale findings, delete resolved ones (a fixed risk should disappear, noted once as closed, not linger), add findings the new analyzer data or diff introduces. If little changed, say so — a near-identical atlas after a small diff is the correct output.
  6. Reassemble to <repo>/docs/codemap.html (overwriting the stale atlas) with --meta recording both shas: "updated <date> · <old-sha> → <new-sha> · …", and present a short "what changed in this atlas" note.

If in doubt between the two, rerun — revision saves time only when the re-reading it requires is genuinely small, and a wrong atlas is worse than a slow one. The same extraction + verification also answers "is this atlas still accurate?" without updating anything: extract, run verify_citations.py, report what's stale.

Quality bar

  • Findings over descriptions. "The db module and models module import each other" is a finding. "The db module handles database access" is filler.
  • Citations everywhere. Judgment-tab claims cite path/file.py (with :line when specific) so the reader can verify. Mark genuinely uncertain claims as unverified rather than asserting.
  • Honest degradation. No git history → no Hotspots tab (assembler drops empty fragments automatically). Unsupported language → say the dependency graph is partial. Never fabricate data to fill a view.
  • Proportion. A 500-line library gets a lean atlas (Overview, Inventory, Dependencies, Invariants may be all that's warranted — empty fragments are dropped). A 100k-line service gets the full set.
  • Fragment protocol: first line <!-- tab: Title -->, then panel HTML using only the template's primitives — no <style> or <script> blocks except the application/json data blocks paired with .viz containers.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.