@pavanyn/docugen
v1.0.4
Published
Deterministic codebase documentation engine. Extracts what the code proves; never states what it cannot verify.
Maintainers
Readme
@pavanyn/docugen
Documentation that cannot lie.
docgen reads a repository and writes down what it finds — routes, endpoints, database schema, background jobs, configuration — with every statement linked to the file and line it came from. Then it asks a language model what each of those things does, badges every word of that as unverified, and turns everything the model could not work out into a question routed to the developer who last touched the code.
Answering a question is one command. That answer becomes permanent, and the question is never asked again.
npx @pavanyn/docugen init # tell the coding agent in this repo about it
npx @pavanyn/docugen session start # refresh evidence, plans, and questions
npx @pavanyn/docugen extract # structure — free, no model, no network
npx @pavanyn/docugen bootstrap # behaviour — uses a coding CLI you already have
npx @pavanyn/docugen ask --mine # the questions waiting on youNode 20.11 or newer. Nothing else — bootstrap drives a coding CLI you have already signed in to (Claude Code, Codex, or Cursor Agent), so nobody needs a new API key.
Why
Documentation written by hand rots. Documentation invented by a model is worse than none — it reads as fact and becomes QA's de facto spec. Every line docgen writes belongs to exactly one lane, and the lane is visible on the line:
| Lane | Where it came from | How to treat it |
|---|---|---|
| verified | A parser reading real code, or a named developer's answer on record | Fact |
| inferred | A model reading the codebase, with links to the lines it cites | A starting point. Unchecked. |
| unknown | Could not be determined | A question — never a claim |
The static lane is forbidden by an enforced import boundary from ever reaching a model. A claim with no citation is rejected by the schema rather than published. Anything the model cannot establish becomes a question instead of a guess.
Before any optional model call, Docgen redacts common credentials and reports
the files, bytes, provider, model, and redaction count being sent. Repository
configuration can allowlist providers and model ids or set localOnly: true to
disable inference completely.
Commands
| | |
|---|---|
| extract | Static analysis. Free. |
| report | Coverage and cross-extractor findings. Free. |
| legacy inventory | Inventory stale prose and create an approval manifest. Free. |
| session | Common start, after-edit, and end lifecycle for every coding agent. Free. |
| mcp | Expose graph, impact, plan, question, and handoff tools over stdio. Free. |
| policy | Enforce plans, handoffs, critical verification, tests, and expiring exceptions. Free. |
| security | Scan dependency provenance and generate a CycloneDX SBOM offline. Free. |
| doctor | Diagnose config, schema, cache, Git, and interrupted-write health. Free. |
| migrate | Upgrade governed artifact schemas with backups and rollback. Free. |
| pilot | Measure extraction quality against attributed reviewer expectations. Free. |
| impact / change / handoff | Trace code changes to QA surfaces, requirements, explicit test citations, and generated pages. Free. |
| bootstrap | Infer behaviour. The only command that costs money. |
| ask | The open question queue. Free. |
| answer | Record an answer as ground truth. Free. |
| triage | Classify answers into requirements, defects, and decisions. Free. |
| trace | Link requirements to the tests that check them. Free. |
| sync | Bring every generated file up to date. Free. |
| check | CI gate: fail when the docs are stale. Free. |
| status | This repo's documentation, evidence-graph, and governance health. Free. |
| fleet | One graph-backed dashboard across many repositories. Free. |
| init | Install agent skills, MCP, CI, and optional Git-hook adapters. Free. |
Run docgen <command> --help for flags.
docgen security scan checks lockfiles, integrity, dependency sources,
install-time scripts, and Python pins/hashes. It does not download live
advisories and therefore never claims CVE coverage. Pair it with a current
advisory scanner in CI.
Output
docs/
generated/ regenerated; never hand-edit
README.md index, coverage, detected stack
routes.md every screen, with auth and params
api.md every endpoint, grouped by resource
schema.md tables, fields, relations, indexes
jobs.md crons, queue consumers, scheduled tasks
config.md env vars: where read, where declared
behaviour.md what each surface does (inferred)
behaviour/ one page per screen, endpoint group, or job
requirements.md what a developer confirmed (verified)
test-cases.md one case per confirmed requirement
traceability.md requirement → test, and what is not covered
diagrams/ sitemap, ERD, modules, integrations (Mermaid)
.cards/ model output (data, regenerated)
.answers/ developer answers (ground truth, never regenerated)
.requirements/ triaged decisions (ground truth, never regenerated)
.legacy/ human-reviewed legacy migration decisions
legacy-archive/ recoverable, approval-gated moves of superseded proseCommit all of it. The cards make the next run cheap; the answers make the documentation true.
With extract --json, written lists every file actually written by the
command. It includes .gitattributes when the generated-file marker was created
or changed, omits it when already correct, and is empty during --dry-run.
Coverage across stacks
docgen separates recognising a technology from being able to parse it. It detects the stack across every workspace — including backend/ + frontend/ splits with no root manifest — and says plainly what it could not read, because an unsupported stack and a genuinely empty repo otherwise look identical:
Detected stack
3 workspaces
ok SQL migrations in supabase/migrations/
gap FastAPI in backend/
ok Next.js in frontend/
warn docgen cannot document 1 detected technology. The output below is
warn incomplete — an empty section does not mean the repo has nothing there.| | Documented today | |---|---| | Routes | Next.js App Router, Next.js Pages Router, React Router | | Schema | Prisma, Mongoose, SQL migrations (DDL), TypeORM, Sequelize, Django, SQLAlchemy | | Endpoints | Express (including cross-file mounts and class-property applications), NestJS, Next.js route handlers and Pages API, FastAPI, Django | | Symbols and flows | TypeScript/JavaScript compiler AST; Python via the official Tree-sitter grammar; proven database access and Bull, BullMQ, or amqplib producer-to-consumer paths | | Recognised, not yet parsed | Fastify, MedusaJS, Flask, Rails, Laravel, Spring Boot, MikroORM, Drizzle, Knex, GORM |
An existing OpenAPI or Swagger spec is cross-checked, never trusted. Code is what runs; an annotation is a claim about the code that may have rotted. Endpoints present in code but missing from the spec, and spec entries with no handler behind them, are both reported.
Express applications assigned through this.app = express() or a class-property
initializer are scoped by source file, class, and property. Router mounts and
direct calls such as this.app.get(...) are followed across class methods and
static aliases. Mount prefixes made from literals, local constants,
concatenation, template strings, literal configuration objects, or provable
imported defaults are resolved without executing code. If only part of a prefix
is knowable, the path keeps a stable placeholder such as
/api/{config.server.apiVersion}/payments and a mount-prefix-unresolved
finding records the expression and application. The router remains mounted;
the placeholder is not presented as a fully resolved URL.
Inline @openapi and @swagger blocks inherit their router's mount graph and
are compared only with the applicable runtime application. A router mounted at
multiple prefixes or into multiple applications is evaluated in each proven
scope. Ambiguous source documents remain unannotated and produce at most one
scope finding per workspace, source, application/reason—not one warning per
operation. The generated summary distinguishes compared operations, both kinds
of mismatch, skipped operations, and distinct ambiguous documents.
Secret-name classification uses credential-token boundaries. Names such as
ADMIN_SERVICE_KEY, AUTH_TOKEN, and CLIENT_SECRET suppress their defaults;
broad words alone do not hide benign settings such as AUTH_SERVICE_URL,
CERTIFICATE_PATH, or SALT_ROUNDS. Recognizable credential-shaped literals
are still suppressed even when the configuration name is generic.
Monorepos and multi-service repositories
Run from the monorepo root when you want one inventory across the whole tree:
cd company-platform
npx @pavanyn/docugen extract
npx @pavanyn/docugen check --jsonDocugen assigns endpoints, OpenAPI documents, schemas, and configuration to the nearest manifest directory. In multi-workspace output, tables label the owning workspace and runtime application. Identical routes or schema names in separate services are not reported as competing definitions; OpenAPI comparisons and environment declarations stay inside their applicable service. An unmounted router remains explicitly unresolved and is never guessed into another app.
For service-local documentation and Git history, run separately from each service directory as well. A root run answers “what exists across this monorepo?”; a service run answers “what does this deployable service own?” and uses that service checkout's Git provenance. Root-level specs or configuration are treated conservatively unless their service relationship can be proved.
Python symbol relationships use a real Tree-sitter syntax tree and are high-certainty. Python ORM model extraction still uses conservative pattern matching; those schema entries remain marked low-certainty and the run says they were read heuristically.
Configuration
Optional; defaults work on most repositories. To customise, add docgen.config.ts at the repo root:
import { defineConfig } from '@pavanyn/docugen/config';
export default defineConfig({
exclude: ['src/legacy/**'],
extractors: { jobs: false },
infer: { agent: 'claude', maxFilesPerSurface: 16 },
});Unknown keys are rejected rather than ignored, so a typo fails loudly.
Guarantees
- Deterministic. Same evidence in, same bytes out — the source suite is verified across Windows, Linux, and macOS on current toolchain runtimes, with a separate packed-artifact smoke test on the minimum supported Node 20.11 runtime. Sorting is locale-independent, paths are POSIX, line endings are LF, and generated pages carry a canonical evidence fingerprint. Feature dates still come from Git.
- Two lanes, never mixed. Only
bootstrapcalls a model, and it says so before it runs. Nothing a model produced is ever stampedverified. - Graph-grounded inference. Model context is limited to a deterministic, extracted-only surface neighborhood and numbered source excerpts. Every returned citation is checked against the exact transmitted ranges.
- No secrets.
.envvalues are never read or recorded — only variable names and where they are used. Literal source fallbacks are also discarded when the variable name is secret-like or the literal has a recognizable credential shape; the variable and source locations remain documented. - Never fabricates. Gaps are recorded, unknowns become questions, and neither is filled with a plausible value.
- Diagrams parse. Every generated
.mmdis run through the real Mermaid parser in CI, not a lookalike.
Documentation
Full guides live in the repository under docs/:
| Guide | For | |---|---| | Getting started | Your first run, in about five minutes | | The trust model | What each badge guarantees | | Command reference | Every command, every flag | | For developers | The daily loop: answering questions without writing docs | | For QA | How to read the output | | Configuration | Every option, and when you need it | | CI and automation | The drift gate | | Rolling out across repos | Handing this to a team and many repositories | | Troubleshooting | When something looks wrong |
Licence
See LICENSE.
