cli.py
cli.py — one command. Bare documate does the whole job; flags pick a mode.
Overview
cli.py — one command. Bare documate does the whole job; flags pick a mode.
documate index → write/refresh the docs → gate the result
documate --check gate only, writes nothing: generated docs fresh, anchors
real, no authored page lying about changed code (CI/hooks)
documate --watch keep running: regenerate whenever a tracked file changes
documate --ai the opt-in model layer: draft every missing docstring and
repair drifted pages via the claude CLI, then re-verify
through the gate (default model haiku; `--ai sonnet` upgrades)
documate --stats the dashboard: coverage bars, doc lines +/−, and the
all-time --ai bill (ledgers live next to the graph)
documate --init scaffold a documate.config.json (defaults, ready to edit),
then run the normal job — first-time setup in one command
Everything else is an override: documate PATH (or --root) points the same
binary at any repo or monorepo sub-tree, --base picks the drift ref, --full
re-indexes from scratch, --html adds the static site, --briefs emits work
orders whenever the gate runs. --only/--dry-run/--budget aim, preview, and
cap an --ai run; --undo reverts the last one from its recorded manifest;
--rewrap-docs reflows over-long doc comments to doc_width with no model at all;
--list-undocumented prints the missing-docs map as JSON. One Context per
invocation, no import-time globals. --watch --ai is refused: a model call
on every save is a token faucet — run --ai as a deliberate one-shot.
depends on src/documate/briefs.py, src/documate/check.py, src/documate/config.py, src/documate/core.py, src/documate/docs.py, src/documate/prose.py, src/documate/rewrap.py, src/documate/site.py, src/documate/stats.py, src/documate/ui.py, src/documate/undo.py
API
F_index(ctx: Context, full: bool) -> None
Refresh the graph before anything reads it. Default is incremental-when-a- graph-exists: re-parse only what changed since the last build (fast on big repos, the whole point). --full forces a from-scratch rebuild; a first index (no db yet) is always full regardless. index() decides the safe path. A spinner covers the wait (this is the one silent-slow step), then one line says what it did.
_dispatch, _watchF_snapshot(ctx: Context) -> dict[str, float]
{tracked file: mtime} — everything that can change the generated docs.
Tracked files only (git ls-files), matching what the indexer sees, minus the
docs and site trees: regeneration writes there, so watching them would retrigger
forever. A vanished file drops out of the dict, which is itself a change.
_watchF_watch(ctx: Context, html: bool) -> int
documate --watch — poll for source changes, regenerate on each one.
A dumb 1s mtime poll, no watcher dependency: an idle cycle is one git ls-files
+ stats, and a regeneration is incremental, so the loop stays sub-second even on
large repos. A brand-new file starts triggering once it's git-tracked (git add)
— untracked files are invisible to the poll and the indexer alike. Ctrl-C stops.
Each cycle prints which source files triggered it, then a colored unified diff of every doc page that changed in response — the live terminal view of the docs.
_dispatch · calls _index, _snapshotF_repo_here() -> bool
Is the cwd inside a git work tree? Decides whether a bare documate can act
(there's a repo to document) or should explain itself (the help screen).
mainFbuild_parser() -> argparse.ArgumentParser
Build the one flat parser: no subcommands, so documate -h is the whole
surface on one screen (rendered through rich-argparse, the one cosmetic
dependency). The three modes come first, overrides after.
mainF_scaffold(ctx: Context) -> None
--init: write the starter config, then teach the ignore defaults so the
user knows what's already skipped before they add to it. Never clobbers an
existing config — that path just says the file is already there. Behaviourally
a no-op on this run (the scaffold is defaults only), so the docs + gate that
follow see exactly what they would have without it.
_dispatchF_dispatch(args) -> int
Route one parsed invocation: index first (every mode reads the graph), then the mode — gate only (--check), the model layer (--ai), or the default job: docs, site if asked, then either the watch loop or the gate. --watch skips the gate on purpose: it's a dev loop, and gating every save would be noise.
main · calls _index, _scaffold, _watchFmain(argv=None) -> int
Console entry point: parse argv, dispatch, return the exit code.
Bare documate inside a repo does the whole job; outside any repo (nothing to
act on) it lands on the help screen instead of an error. -h/--help and argparse
errors are converted from SystemExit to plain return codes so embedders (and
the tests) get a value, not an exit.
_dispatch, _repo_here, build_parser