# Codebase Map

> Generate an interactive, zoomable map of any codebase — a high-level service/module call graph that drills into each module's endpoints and code structure. Project-agnostic. Scripts do all scanning and HTML generation; the LLM only adds semantic labels (roles, edge meanings). Use when the user asks to "map", "visualize", "diagram", "explore", or "understand the architecture of" a repo or multi-repo workspace, or to "refresh" an existing map after code changes.

- Skill: `mzvonar/codebase-map` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add mzvonar/codebase-map`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mzvonar/codebase-map/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: mzvonar (https://skillmd.com/u/mzvonar)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/mzvonar/codebase-map

---


# 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.json` the 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`:

```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.edges` as candidates. Set a human `label` (e.g. `REST`, `gRPC`, `GraphQL`, `AMQP`), fix direction, set `kind` (`planned` for not-yet-built/target calls, else `current`), drop obvious false positives, add obvious missing ones. Keep it light.
- **groups:** logical grouping (by domain or by stack). Palette optional — `build.py` has 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 by `build.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)
1. Re-run **step 1** (analyze). Read `changes.json`.
2. If nothing changed, just rebuild/serve. Otherwise update **only** the
   `added`/`changed` module entries in `enrichment.json` (merge — keep the rest),
   and delete `removed` ones. Re-curate only edges touching changed modules.
3. 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.

1. **Gather context (script):**
   ```
   python3 ${CLAUDE_PLUGIN_ROOT}/skills/codebase-map/scripts/deepen.py --module <id> --in <ROOT>/.codemap
   ```
   Writes `<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.

2. **Author the outline (LLM, bounded):** Read that context file, then read **only**
   the handler + role-class files it points to (a few `Read`s — 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.

3. **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`'s `EXTRACTORS` to 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`.

