Present your architecture instead of slides. Forma turns any codebase into an interactive, stack-agnostic C4 explorer — big-picture → drill-down to the leaf — generated from the code and kept true to it by a deterministic drift check. No more slide decks that lie the day after you draw them.
Forma is the companion to arbiter: arbiter governs the process, Forma shows the system and
guarantees the picture matches reality.
▶ Live demo — forma's own architecture, generated by
forma from this repo on every push to main.
A hand-drawn architecture diagram is stale the moment code changes. Forma walks your source for the real structure, infers relationships from cross-references to exported symbol names (heuristic, additive), and fails a check when the model and the code disagree. What you present is what actually exists.
npx forma-arch <command> # or: npm i -D forma-arch| Command | What it does |
|---|---|
forma init |
Seed docs/architecture/c4-topology.json from your source dirs (best-effort; then curate) |
forma gen |
Walk src/ leaves + derive container edges from cross-references; fill box text from docstrings/READMEs; group flat containers into components → c4-model.json |
forma check |
Deterministic drift check — fails if the model no longer matches the code |
forma doc |
Project the arc42 scaffold (ARCHITECTURE.scaffold.md), or --attach <file> to inject a governed block into an existing doc |
forma serve |
Open the live explorer at http://localhost:4173 |
forma verify |
Refresh status from live GitHub issues through your gh CLI — the only networked command |
Box text comes from your docs. gen fills each box with the module's docstring (Python """…""", JS/TS leading block), else the directory README.md, else a mapped arc42 section — so the explorer shows meaning, not a list of symbols. On a flat directory of many foo_* files it also synthesizes a component layer, described from its children's docs (--no-cluster to disable; --cluster-min <n> = leaves before a container is clustered, default 8; --group-min <n> = files sharing a prefix before they become a component, default 3).
Programme state is curated, not guessed. Code shows what exists, never how far along it is. Drop a docs/architecture/c4-status.json (--status <path> to move it) and gen decorates nodes by id with status2, completion, statusWord, current, target, verify, issues — never func, which belongs to the docs. gen validates the form (ids resolve, fields known, enums and issue numbers well-shaped) and never the prose; forma check fails if the overlay decorates a node the model no longer has.
{ "nodes": { "engine": { "status2": "in-progress", "completion": 60, "statusWord": "v2 in progress",
"current": "Live on ACA: RAG + citations. Hardening this week.",
"target": "Multi-surface substrate with client-ready output.",
"verify": { "source": "ADR-040 on main" }, "issues": ["#534"] } } }Curated state, verified against reality. forma verify asks your gh CLI for the state of every issue the model references (--gh-repo owner/repo, or meta.ghRepo in the topology), marks the nodes whose issues are closed as done, and prefixes their current with dated evidence. It touches state, never structure, and re-running it never stacks the evidence. It is opt-in and separate on purpose: gen and check never open a socket. In the served viewer, RE-VERIFY re-reads the model without losing your level, layout or mode.
One source of truth. forma doc --attach docs/architecture/arc42.md injects the generated diagrams/tables between <!-- forma:begin --> / <!-- forma:end --> markers in your existing doc; your prose lives outside them, and forma check fails if that block drifts. Attached files are recorded in source.attachedDocs, so the gate governs every doc you attach — not just the model's docPath; deleting the markers (or the file) from a registered doc fails the check rather than quietly un-governing it. That registry lives in c4-model.json, so commit the model — a lost model takes the registry with it. Where a repo lacks docs, forma gen --enrich can fill the remaining box holes with an LLM — opt-in, cached, never on the deterministic gate:
--enricher |
Use it when | Network |
|---|---|---|
agent |
An agent is driving forma. Writes enrich-plan.json with the holes; the agent writes the sentences (reading the sources if it wants) and gen --enrich-apply <file> applies them with the same cache and provenance. |
none |
anthropic (default) |
Headless / CI, with ANTHROPIC_API_KEY. |
REST |
openai |
Same, with OPENAI_API_KEY. |
REST |
ollama |
Sensitive repos: a local model, nothing leaves the machine. | localhost |
code ──► forma gen ──► c4-model.json ──┬──► c4-viewer.html (present / explore)
(single source) ├──► ARCHITECTURE.md (arc42, via forma doc)
└──► forma check (gate: model == code?)
One source of truth (c4-model.json); two renderings (the interactive viewer and the arc42 doc);
one deterministic check that keeps them honest. The file contract is
lib/schema/c4-model.schema.json.
The engine is plain Node — no LLM required. Structure is auto-walked, relationships are derived from
code, the check is deterministic. The only human (or agent) step is curating the topology groupings
and writing the arc42 prose — and any model can do that against the documented JSON/Markdown
contract. The Claude skill in adapters/ is a thin wrapper, not the product.
The viewer is a live C4 map, not a static picture:
- Click any box to read its explanation — what it does, current state, target, verification source — at every level, from context down to a leaf.
- Double-click a box (or its
[+] DRILL) to descend into it; BACK, the breadcrumb, orESCclimb back out. - Drag boxes to lay out the view your way; RESET LAYOUT restores the arrangement (your curated hints if the topology has them, the automatic one otherwise). To keep a layout, drag it, pick Export layout JSON, and paste the result under
"layout"in the topology —gencarries it intometa.layoutand the viewer pins those boxes, auto-arranging everything else clear of them. - Arrow labels are painted on the diagram while the level stays readable (≤14 arrows) and turn off above that; LABELS forces them on or off, and hovering an arrow always reveals its label.
- PRINT / EXPORT to SVG or PNG for docs and slides — exported arrows carry their labels.
- The breadcrumb names the C4 level you are on (
C4-L1 · CONTEXT→C4-L3 · COMPONENTS) and, when the level carries curated state, tallies it: a dot per status with its count plus mean completion, so a level reads as a programme board and not just a diagram. - Every level shrink-wraps its own content — a context with four boxes renders zoomed and dense instead of adrift in a fixed canvas.
The viewer ships with swappable skins (holo, blueprint) via a dropdown or ?skin=. Themes are
CSS variables; the engine is decoupled from the look.
Apache-2.0. Not affiliated with C4 or arc42 — see NOTICE.