AI-agent-agnostic documentation-map generator for FE/BE codebases.
docmap deterministically discovers the modules of a project (via a
framework adapter), then delegates writing each module's business-logic
description to a headless AI agent of your choice — Claude Code, Codex, or
Gemini CLI. The result is a .docmap/ tree that mirrors your project's
module structure: one root doc per module plus one doc per element, with a
top-level .docmap/index.md cross-module relationship graph.
Discovery (what exists, what depends on what) is always deterministic and free — no AI call is needed to preview it. Only the prose explaining why a module exists and what its business logic does is generated by an agent.
- Magento 2 (PHP) — modules via
registration.php; elements fromController/,Model/,Observer/,Plugin/,Block/,Api/,Helper/,Setup/; cross-module relations fromdi.xml(preference/plugin),events.xml, and PHPusestatements. - Nuxt 4 — layers/top-level dirs as modules (
pages,components,composables,stores,server); relations from imports,$fetch(...)calls resolved against Nitro server routes, and Pinia store usage. - NestJS — every
*.module.tsdefines a module boundary (its directory, minus any nested directory that has its own*.module.ts); elements classified by filename suffix (.controller.,.service.,.guard.,.dto., ...); relations parsed straight from each@Module({ imports: [...] })— the actual DI graph, not a guess. - Vue 3 (standalone, no Nuxt) —
src/{views|pages, components, composables|hooks, stores|store, layouts, router}as modules; relations from imports,useXStore()usage, and each vue-router route'scomponent(static or() => import(...)) resolved to its view. - Generic fallback — any other stack. Top-level directories become modules, cross-module edges come from import-statement heuristics. Any file is documentable by default (code, markdown docs, yaml/shell deploy scripts, sql — whatever the project actually uses) except known binary/asset types (images, fonts, archives, lockfiles, ...); a directory with nothing but those isn't treated as a module. Always available so the tool works on day one of a new project or an unsupported language.
npm install -g @ray_of_goodness_/docmap # global — gives you the `docmap` command everywhere
# or, without installing anything:
npx @ray_of_goodness_/docmap initThe package is scoped (@ray_of_goodness_/docmap) but the CLI binary is
always just docmap, regardless of how it's installed.
docmap init # scaffold docmap.config.json + .docmap/, gitignore .docmap/
docmap scan # preview modules & relations, no AI calls
docmap generate --runner claude # write .docmap/ using Claude Code
docmap status # missing / up-to-date / stale, per moduleRe-running generate only calls the agent for modules whose source files
changed since the last run (tracked via a content fingerprint stored in
each doc's frontmatter) — safe and cheap to run repeatedly, e.g. in CI or
a pre-commit hook.
init adds a .docmap/ entry to the project's .gitignore (creating the
file if it doesn't exist yet) — generated docs aren't meant to be
versioned. Every command also respects the project's .gitignore: files
it ignores are never scanned or documented, no config needed. Use --dir <path> on scan/generate/status to restrict discovery to one
subdirectory of the project — modules entirely outside it are dropped
from the output.
Scaffolds a new project for docmap. Writes docmap.config.json (with
defaults — see Configuration) unless one already
exists, creates the (empty) .docmap/ output directory, and adds a
.docmap/ entry to .gitignore (creating that file too if it's
missing). Safe to re-run: an existing config is left untouched, the
.gitignore entry is only added once.
docmap init # defaults: framework auto-detect, language en
docmap init --lang uk # generated prose in Ukrainian
docmap init --framework nuxt4 # skip auto-detection, force an adapterRuns discovery only — no AI agent is invoked, so this is instant and
free. Finds the framework adapter, lists every module it found with its
element count and cross-module relations (with each relation's
confidence: deterministic, from a framework-specific signal like
Magento's di.xml, or heuristic, from an import-statement guess). This
is what actually gets sent to the agent in generate, so it doubles as a
"preview what will be documented" step before spending any AI budget.
docmap scan # human-readable tree
docmap scan --json # same data as JSON, for scripting
docmap scan --dir app/pages # only look inside app/pages
docmap scan --framework magento2 # override auto-detectionscan --json's output is a stable, versioned, public contract for
external tooling (CI bots, dashboards, scripts) to build against — it is
not just a dump of docmap's internal data structures, which are free to
change shape between releases. Breaking changes bump schema_version.
Every path in this output — modules[].path, elements[].files[] — is
project-root-relative (relPath). Absolute filesystem paths
(absPath) are intentionally never included, so the output is safe to
share outside the machine it was generated on and stable across
checkouts at different locations. Treat relPath-style paths as
relative to the project root you ran docmap scan from.
For a split-repo setup (e.g. a Nuxt frontend calling a separate Magento
backend), configure peers in docmap.config.json to match this
project's REST/GraphQL call sites against a sibling project's declared
API surface (etc/webapi.xml routes, etc/schema.graphqls fields):
{
"peers": [
{ "name": "backend", "path": "../eva-magento" }
]
}Each peer is discovered read-only: docmap scans the peer project to
learn its modules and API surface, but never calls resolveRelations on
it and never generates docs for it — the peer's own internal relations
and its dependents/used-by index are not populated, only its
module/element/summaryHints shape is used for matching. If you want
the peer's own full docmap output (its internal relations, its own
used-by index), run docmap generate on that project directly.
A matched call site becomes a cross-project api-call relation, tagged
peer:<name>::<moduleId> on toId/toModule so it's never confused
with a local module id. These show up in:
docmap scan --json'srelations[], withtoModulepeer-prefixed.- The
## Relationshipsmermaid diagram inindex.md(dashed, since the match is heuristic), with the peer module rendered as its own node. - The
## Integration surfacetable inindex.md— endpoint/operation → frontend consumer → backend owner, for regression-testing "what breaks if this backend module changes." ## Scenariosend-to-end chains that cross into a peer backend.
The full pipeline: discovery, then one AI agent call per module (not per
element — cheaper, and gives the agent the whole module's context at
once) to write the module's README.md and each element's .md file
under .docmap/. A module is skipped if its content fingerprint already
matches the doc on disk from a previous run, so re-running after a small
change only regenerates what actually changed.
A module with more elements than maxFilesPerPrompt is automatically
split into several agent calls (batches of maxFilesPerPrompt) instead
of one — every element ends up documented from its actual source, rather
than the agent being asked to write hundreds of blocks for files it was
never shown. Only the first batch writes the module overview; later
batches only write per-element blocks. The module-level relationship
summary sent in every prompt is deduplicated (distinct type + target
pairs, capped at 40 lines) — a module where 200 files all call the same
useCartStore() produces one summary line, not 200.
| Flag | Effect |
|---|---|
--module <id> |
Only generate this module (repeatable — pass it multiple times for several) |
--runner <name> |
claude | codex | gemini | mock — which agent CLI to shell out to |
--lang <lang> |
Override config.language for this run |
--force |
Ignore the fingerprint cache, regenerate every targeted module |
--dry-run |
Build the prompts and write them to .docmap/.cache/prompts/<module>.txt (or <module>.batch<N>.txt for a split module) with a rough token estimate — no agent is called, nothing under .docmap/ (besides the cache) is touched |
--concurrency <n> |
Max parallel agent calls (default 2, capped at 8) |
--fail-fast |
Stop after the first module that errors, instead of continuing through the rest |
--model <model> |
Passed straight through to the runner CLI (e.g. claude --model <model>) |
--framework <name> |
Override auto-detection |
--dir <path> |
Restrict discovery to one subdirectory — see Quick start |
--skill <path> |
A markdown file (a Claude Code SKILL.md works as-is — YAML frontmatter is stripped) whose content is folded into every prompt as project-specific documentation instructions: house style, tone, what to emphasize, conventions discovery can't infer on its own |
docmap generate --runner claude # generate/refresh everything
docmap generate --runner claude --module checkout # just one module
docmap generate --runner mock # zero-cost structural preview
docmap generate --dry-run # see the prompts without spending anything
docmap generate --runner claude --skill docs-style.md # follow custom house-style instructionsA module ends up with status: error in its report (not written to
disk) only if its first batch's response is missing the module-overview
block, even after one retry with a stricter prompt (config.maxRetries)
— the run continues past it rather than aborting, unless --fail-fast is
set. A later batch that comes back empty doesn't fail the module: its
elements get a placeholder doc and a warning is logged, but everything
else that did generate is still written.
Cheap, no-AI check of where every module's docs stand: missing (never
generated), up-to-date (fingerprint matches source), or stale
(source changed since the doc was written). Good for CI — fail the build
if anything is stale or missing.
docmap status
docmap status --json
docmap status --dir server # only report on one subdirectoryCopies the thin agent-integration wrappers from the package's skills/
directory into the target project, so Claude Code / Codex / Gemini CLI
pick them up automatically in that project going forward (no logic in
them beyond "run docmap generate, then summarize the result" — see
Using it from an agent).
docmap install-skills # all three: claude, codex, gemini
docmap install-skills --agent claude # just .claude/skills/docmap/Deletes the entire .docmap/ directory. Requires --yes — without it,
the command refuses and exits non-zero, so it can't be fired by accident
(e.g. via a mistyped script or muscle memory from a different command).
docmap clean --yes--dry-run and --runner mock (on generate) both exist to let you
sanity-check cost/structure before spending anything: --dry-run shows
you the exact prompts that would be sent, mock runs the full pipeline
with a deterministic placeholder instead of a real agent call — useful
in CI or for previewing the module tree.
docmap generate is safe to re-run because it skips any module whose
fingerprint hasn't changed — but the moment a module's source does
change, generate will happily overwrite its doc with prose describing
the new code, whatever that new code does. Nothing compares the new
behavior to the old one first. A refactor that quietly starts cancelling
orders that used to be untouchable slides straight into the doc as the
new normal — the previous doc, the project's eталон (reference
baseline) for what the code is supposed to do, is gone the moment
generate runs again.
docmap verify closes that gap. For each stale module it hands the
agent both the old doc (the eталон) and the current source, and asks it
to judge — per documented invariant — whether the code still honors it,
changed it, or violated it, plus whether any new undocumented behavior
showed up. Nothing is overwritten; verify only reports.
Recommended workflow (run verify before generate regenerates
anything):
code changes
→ docmap status # what's stale
→ docmap verify # did any documented behavior break
→ human reviews the report:
BREAKING and unintended → fix the code
change was intentional → docmap generate (bakes in the new eталон)
docmap verify # check every stale module
docmap verify --module checkout # just one module
docmap verify --strict # CHANGED also fails the build
docmap verify --since HEAD~5 # focus the prompt on a git diff
docmap verify --against .history/checkout/2026-08-01-1200.md # a specific past baseline| Flag | Effect |
|---|---|
--module <id> |
Only verify this module (repeatable) |
--runner <name> |
claude | codex | gemini | mock — which agent CLI to shell out to |
--json |
Machine-readable report on stdout, for CI/scripting |
--strict |
CHANGED also fails the build (exit 1) — by default only BREAKING does |
--since <ref> |
Focuses each stale module's prompt on the git diff against <ref>, instead of handing over the whole module |
--against <path> |
Verify against a specific .docmap/.history/ snapshot instead of the current on-disk doc |
Exit code is non-zero when any module comes back BREAKING (and, with
--strict, also when any comes back CHANGED) — wire it into CI the
same way as docmap status. A human-readable report is always written
to .docmap/.reports/verify-<yyyy-MM-dd-HHmm>.md: a summary table of
every module's status/verdict, plus per-invariant detail for anything
CHANGED or BREAKING.
Business rules are only comparable module-over-module if they're a
list, not a paragraph of prose. Every generated module doc has an
## Invariants section — a numbered list of observable, checkable
business rules (e.g. "an order can only be cancelled from
pending/on-hold", "a discount never exceeds 50%"). Each item gets an
id (I1, I2, ...); verify's report references those same ids when
it says an invariant was KEPT, CHANGED, or VIOLATED.
Before generate overwrites a module's doc, the previous version is
snapshotted to .docmap/.history/<module>/<generated_at>.md — so a
generate run never destroys the eталон verify would have compared
against. Retention is controlled by config.historyKeep (default 5
snapshots per module, oldest rotated out first; 0 disables history
entirely). Point verify --against at one of these snapshots to check
current source against an older baseline instead of the latest doc.
--since <ref> only narrows the diff shown to the agent for modules
verify decides to check — it does not change which modules get
checked. A module is still skipped as "unchanged" whenever its
fingerprint matches its on-disk doc, even if the --since diff touched
it. In practice that means: if generate already ran again after a
regressing change (so the doc's fingerprint caught up with the bad
code before anyone ran verify), the doc has silently adopted the
regression as its new eталон, and --since won't surface it — verify --against a .docmap/.history/ snapshot from before the regression is
the way to check that case.
docmap.config.json (or .docmaprc[.json], resolved via cosmiconfig):
| Key | Default | Notes |
|---|---|---|
framework |
"auto" |
magento2 | nuxt4 | nestjs | vue3 | generic | auto |
language |
"en"/"uk" |
Language code written into generated prose |
runner |
"claude" |
claude | codex | gemini | mock |
model |
— | Passed through to the runner CLI, if set |
concurrency |
2 |
Parallel agent calls (max 8) |
include / exclude |
— / node_modules, vendor, .git, dist, build, .docmap | Glob-style path filters, matched per scanned subtree |
scanDir |
— | Same as --dir: restrict discovery to one subdirectory |
skill |
— | Same as --skill: path to project-specific documentation instructions |
maxFilesPerPrompt |
20 |
Source files included per module prompt |
maxFileExcerptBytes |
12000 |
Per-file excerpt cap |
maxRetries |
1 |
Retries when the agent's output is missing the required markers |
timeoutMs |
600000 |
Per-agent-call timeout |
elementDocThreshold |
1 |
Modules with this many elements or fewer get their docs folded into the module README instead of separate files |
historyKeep |
5 |
Previous-version snapshots kept per module under .docmap/.history/ before each overwrite; 0 disables history |
peers |
[] |
Sibling projects to match cross-stack API calls against — see Cross-stack peers |
docmap install-skills drops thin wrappers into the project so Claude
Code, Codex, and Gemini CLI can drive docmap generate themselves as part
of a conversation — the wrappers contain no logic beyond "run the CLI,
then summarize what changed." See skills/.
Each module doc (.docmap/<modulePath>/README.md) and element doc share a
YAML frontmatter (docmap_version, id, status, fingerprint,
generated_by, elements/dependencies or relations, ...) plus a
fixed set of body sections: Purpose, Responsibilities,
Business Logic, Inputs / Outputs, Relationships. The format is
direction-agnostic by design: a future "interview mode" will write the
same files with status: planned (spec-first) before any code exists,
letting an agent build the implementation from the doc instead of the
other way around.
npm install
npm run build # tsup -> dist/cli.js
npm test # vitest — all tests run against the mock runner, no live LLM calls
npm run typecheckMIT
{ "schema_version": 1, "framework": "nuxt4", "modules": [ { "id": "moduleA", "name": "moduleA", "path": "app/moduleA", // project-root-relative "elements": [ { "id": "moduleA::index", "kind": "component", "files": ["app/moduleA/index.ts"] } ], "relations": [ { "type": "import", "fromId": "moduleA::index", "toId": "moduleB::helper", "toModule": "moduleB", "detail": "...", "confidence": "heuristic" } ] } ] }