Codebase Map
Builds a self-contained interactive site: a directed graph of modules/services (who calls whom) where clicking a module zooms into its endpoints and structure, with a description of what each module is and does. Works on a single repo or a multi-repo workspace.
Design contract — minimize tokens
Scripts do everything mechanical; you (the LLM) only do what scripts cannot.
- Do NOT read source files to build the map. Operate on the compact
.codemap/model.jsonthe script produces. - Your entire contribution is one small JSON file (
enrichment.json): module roles/descriptions, edge labels + current/planned classification, grouping. - On refresh you touch only the modules the script reports as changed.
Prerequisites
python3(stdlib only). The viewer needs internet on the viewing device (CDN libs).
Procedure
Resolve the target root: the user-supplied path, else the current directory.
For a multi-repo workspace, point at the directory that contains the repos.
All state lives in <root>/.codemap/; the site in <root>/codebase-map-site/.
(Suggest adding both to .gitignore.)
1. Scan (script — no tokens on source)
python3 ${CLAUDE_PLUGIN_ROOT}/skills/codebase-map/scripts/analyze.py --root <ROOT> --out <ROOT>/.codemap [--depth 4]
Writes model.json (modules, stacks, endpoints, inferred edges, per-module hashes)
and, if a prior model exists, changes.json ({added, changed, removed}).
2. Enrich (LLM — one bounded pass over model.json)
Read <ROOT>/.codemap/model.json only. Write <ROOT>/.codemap/enrichment.json:
{
"title": "<repo name> — Codebase Map",
"groups": { "<group>": {"color": "#4a90e2", "shape": "box"} },
"modules": {
"<module-id>": {"group": "<group>", "role": "1–2 sentences: what it is, what it does, what it owns"}
},
"edges": [ {"from": "<id>", "to": "<id|extraNodeId>", "label": "<protocol/purpose>", "kind": "current|planned"} ],
"extraNodes": [ {"id": "<id>", "label": "<name>", "group": "infra", "description": "<short>"} ]
}
Rules:
- Infer roles from the module name, stack, markers, and endpoint list in
model.json— do not open source files. If genuinely ambiguous, read at most that module's README/main file. - Edges: treat
model.edgesas candidates. Set a humanlabel(e.g.REST,gRPC,GraphQL,AMQP), fix direction, setkind(plannedfor not-yet-built/target calls, elsecurrent), drop obvious false positives, add obvious missing ones. Keep it light. - groups: logical grouping (by domain or by stack). Palette optional —
build.pyhas sensible defaults; only set colors when it adds clarity. - extraNodes: add datastores/brokers/third-party systems only when they aid understanding (e.g. Postgres, RabbitMQ, an external API).
- Do NOT write per-module
markdown. The endpoint drill-down is auto-generated bybuild.py. (Only author richer markdown — e.g. controller→service→repository code chains — when the user explicitly asks to "deepen"/"detail" a specific module, and only then read that module's source.)
3. Build (script)
python3 ${CLAUDE_PLUGIN_ROOT}/skills/codebase-map/scripts/build.py --in <ROOT>/.codemap --out <ROOT>/codebase-map-site
4. Serve & hand off
bash ${CLAUDE_PLUGIN_ROOT}/skills/codebase-map/scripts/serve.sh <ROOT>/codebase-map-site 8777
Give the user the printed LAN URL. Mention scroll=zoom, click a module to drill in,
back button / Esc to return, collapsible legend.
Refresh (after code changes)
- Re-run step 1 (analyze). Read
changes.json. - If nothing changed, just rebuild/serve. Otherwise update only the
added/changedmodule entries inenrichment.json(merge — keep the rest), and deleteremovedones. Re-curate only edges touching changed modules. - Re-run step 3 (build) and serve. This keeps refresh near-zero-token.
Deepen a module — tie code to endpoints (first-class flow)
When the user asks to see the code behind a module's endpoints (controller → service → repository/client chains), use this 3-step flow. Scripts gather a focused reading list and inject the result; you only read a handful of files and write plain markdown — no JSON escaping, no whole-module scan.
Gather context (script):
python3 ${CLAUDE_PLUGIN_ROOT}/skills/codebase-map/scripts/deepen.py --module <id> --in <ROOT>/.codemapWrites
<ROOT>/.codemap/deepen/<id>.context.md— the endpoints with their handler files, plus an index of role classes (*Controller/*Service/*Repository/*Gateway/ *Mapper/*Client…) and where they live.Author the outline (LLM, bounded): Read that context file, then read only the handler + role-class files it points to (a few
Reads — not the module). Trace each endpoint's call chain and write a plain markdown file (e.g.<ROOT>/.codemap/deepen/<id>.md):## <id> - **Role:** ... - **Endpoints** - `METHOD /path` name - Controller: `Class.method` → Service: `Class.method` → Repository/Client: `Class` - (note async/saga/outbox or mapper steps where present)Mark anything inferred-but-unverified. These chains are best-effort, not a guaranteed call graph.
Inject + rebuild (scripts):
python3 ${CLAUDE_PLUGIN_ROOT}/skills/codebase-map/scripts/set_markdown.py --module <id> --md-file <ROOT>/.codemap/deepen/<id>.md --in <ROOT>/.codemap python3 ${CLAUDE_PLUGIN_ROOT}/skills/codebase-map/scripts/build.py --in <ROOT>/.codemap --out <ROOT>/codebase-map-site
To revert a module to the auto-generated endpoint list:
python3 ${CLAUDE_PLUGIN_ROOT}/skills/codebase-map/scripts/set_markdown.py --module <id> --clear --in <ROOT>/.codemap.
Leave other modules unset (auto-generated). Re-running deepen after a refresh updates
the context for changed modules.
Notes
- The graph stops auto-animating once layout stabilizes (physics frozen).
- If clicks only highlight, the markmap CDN failed to load — check connectivity.
- Supported endpoint extractors: Spring (Kotlin/Java) + OpenAPI, Express/Nest/Fastify,
FastAPI/Flask, Go (gin/chi/net-http), Rails. Other stacks still map at the
module/edge level; extend
analyze.py'sEXTRACTORSto add more.
To change this skill, do not edit this copy: use /dev-tools:update-skill, or see docs/updating-skills.md in mzvonar/claude-skills-public.