Skip to content
internal/gen
On this page

Auto-generating a project's documentation: one reference page per module or package, the generated index, the CLI reference pages and the root templates.

#internal/gen

#internal/gen

Package gen auto-generates documentation pages from a project's structure.

Two artifacts come out of it. GenerateDocs walks the declared source paths and writes one Markdown reference page per module (Python, TypeScript and the other per-file languages) or per package (Go), plus a generated index page, plus the CLI reference pages for a strictcli-based project. GenerateRootFiles resolves the underscore-prefixed templates a project declares in root_files and writes the project root's own files -- README.md, CLAUDE.md -- from them.

#Machine text is a placeholder, handwritten text is not

Every page this package writes is read-only (0444) and carries a generated: true marker, and only a file carrying that marker is ever overwritten or swept as stale. A page's frontmatter description is the one field a person is expected to rewrite: it is preserved verbatim unless the ownership predicate says the current text is machine residue -- the module template, a legacy index phrase, or the seed hash recorded for that page in the staleness store. The seeded: true marker is an output, never an input to that decision: a page whose description was hand-rewritten while the marker stayed behind keeps its text.

#GenResult

Go go
type GenResult struct

GenResult is what one GenerateDocs call did: the pages it wrote and the stale pages it deleted, each path relative to the generated pages directory (and prefixed with the locale subdirectory for a multi-locale project).

#GenerateDocs

Go go
func GenerateDocs(config map[string]any, baseDir, versionOverride string, handle *effects.Handle) (GenResult, error)

GenerateDocs auto-discovers a project's source files and generates its documentation pages.

Source entries are grouped by language and one generation pass runs per group; the stale sweep runs once afterwards, so one language's output is never deleted by another language's pass.

The pages are written into selfdoc's generated pages directory, never beside the handwritten ones: the build merges the two roots into one namespace, so a page keeps the address it had when both kinds sat in one directory. A multi-locale project generates under a locale subdirectory of that root, one per declared locale, and a single-locale project with no locale subdirectory of its own generates into the root directly. A handwritten page already holding a name is left alone and no page is generated for it.

A project that declares no source entries is an error: every page written here is derived from source code, so there is nothing to write and an "API reference index covering 0 modules" would be an empty artifact that reads like a real reference page. A caller that knows the project is codeless skips this call and generates only its root files.

versionOverride, when non-empty, is recorded on config under the runtime version-override key, which is what the var directive reads for project.version. It is set here as well as in GenerateRootFiles because config is the one object that reaches every directive resolver, and the two calls share it.

#FileToModulePath

Go go
func FileToModulePath(filePath, baseDir, language string) (string, bool)

FileToModulePath converts a source file path to the module or package path a ref directive names, relative to baseDir (the project root).

Python drops a trailing /__init__ and joins the rest with dots, so mylib/config.py becomes mylib.config and mylib/__init__.py becomes mylib. Every other language keeps the path separators, so pkg/handler.go becomes pkg/handler and src/utils.ts becomes src/utils.

It reports false for a path with no module path at all -- the project root's own __init__.py, whose package is the project directory rather than any importable name.

#ModuleToFilename

Go go
func ModuleToFilename(modulePath, language string) string

ModuleToFilename converts a module path to the Markdown filename its page is written to, replacing Python's dots or every other language's slashes with dashes: selfdoc.config becomes selfdoc-config.md, pkg/handler becomes pkg-handler.md.

#GenerateRootFiles

Go go
func GenerateRootFiles(config map[string]any, baseDir, versionOverride string, handle *effects.Handle) ([]string, error)

GenerateRootFiles resolves a project's root-file templates and writes them to the project root.

config's root_files key lists the templates (paths like "docs/_CLAUDE.md"). Each one's basename must start with an underscore, which the output name drops: docs/_CLAUDE.md generates CLAUDE.md. The template's frontmatter is dropped, its directives are resolved through the project's own resolver, and the result is written read-only (0444) under the auto-generated header.

Every directive's attributes are validated BEFORE anything is resolved, so an unknown or a missing required attribute is a hard error naming the template and the true line in it -- the same refusal selfdoc check makes, rather than a page that renders an error marker where its content belongs.

An existing output file whose first line is not the auto-generated header is never overwritten: that is a hard error naming the exact header line to add in order to adopt the file.

versionOverride, when non-empty, is recorded on config under the runtime version-override key, which is what the var directive reads for project.version.

It returns the output names, relative to baseDir.

More tools from this site

  • claudestream Drive Claude Code from Python: run it as a subprocess and read its output as typed events, with async and sync sessions, sandbox policies, and tools you define in Python
  • claudewheel A TUI Claude Code Launcher that lets you have more than one profile, manage sessions lifecycle, pick the exact CC version, model to use (even older unlisted ones), pick which GitHub account to use, etc.
  • dirstat Fast, single-binary directory statistics CLI: every file under a tree grouped by format, with counts, sizes, and lines of code, as a colored terminal table or as JSON
  • fastware A batteries-included ASGI framework: msgspec JSON, a managed Granian server, dependency injection, SSE, WebSockets, auth, and a test client
  • go-toml-edit Zero-dep TOML editing library for Go with comment preservation
  • howmuchleft The fastest Claude Code statusline: context window, 5-hour, and weekly limit usage as three customizable gradient bars, rendering in about 6 ms
  • orxtra
  • pgdesign
  • predraw Declarative rendering pipeline: describe a scene in JSON and get SVG, PNG and WebP out, with light and dark style tokens, reusable components and text converted to path outlines
  • reposummary Turn a git repository's history into a Markdown journal: pick a time window or revision range and get a readable digest of what changed, optionally narrated by an LLM
  • rlsbl Release orchestration and project scaffolding CLI that bumps versions, validates a structured JSONL changelog, tags only the commit CI verified, and publishes to npm, PyPI, Go and more
  • safegit git wrapper CLI that gives each commit its own temporary index and retries ref updates on conflict, so concurrent agents share one repository
  • saferm Command-line replacement for rm that archives every deletion with a mandatory reason and the context it ran in, so deleted files can be listed, inspected and restored
  • strictcli
  • stricttest An always-on test-isolation floor: a pytest plugin and a Go env-hygiene module that make a test suite structurally unable to reach real credentials, the real HOME, the network, or the development repository.
  • wesktop A Python framework that turns an ASGI web app into a desktop application, serving it from a local Granian server and displaying it in a native OS window via pywebview
Search