documator
A documentation CLI. This readme is itself rendered from templates/README.md into the repository root by the documator render command.
Install
uv sync
Usage
documator [--quiet] render INPUT_DIR OUTPUT_DIR [--watch] [--check] [--timeout SECONDS]
documator [--quiet] skills INPUT_DIR OUTPUT_DIR [--watch] [--timeout SECONDS]
render mirrors the input tree.
Usage: documator render [OPTIONS] {input_dir} {output_dir}
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
│ * input_dir <parse> [required] │
│ * output_dir <parse> [required] │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --watch │
│ --check │
│ --timeout SECONDS [default: root=10.0] │
│ --help Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
--check renders the whole tree and writes none of it: nothing is created, overwritten or pruned. Every output that is missing, out of date, or still tracked after its template went away is reported, and the run exits non-zero — so CI can fail on a repository whose generated files have drifted from their templates without the job being able to paper over it. Blocks still run, since their output is what the comparison is about; --check bounds what documator writes, not what a template's commands do. It cannot be combined with --watch.
skills compiles the same templates into the flat <skill-name>/SKILL.md layout Claude's skill loader expects: nesting in the input tree is organisational only, the filename stem becomes the skill name, and the frontmatter is generated — any keys the template declares pass through, and a declared description wins over the name-derived placeholder.
A skill is marked with an @ on its own name: @foo.md, or @bar/ for a folder holding a SKILL.md. Everything unmarked is a term — a note that names something for links to point at and compiles to nothing.
An example tree might look like:
my-templates/
├── @foo.md
├── @bar/
│ └── SKILL.md
├── Tracer bullet.md
├── _hidden.md
and would result in:
.skills/
├── foo/
│ └── SKILL.md
├── bar/
│ └── SKILL.md
Usage: documator skills [OPTIONS] {input_dir} {output_dir}
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
│ * input_dir <parse> [required] │
│ * output_dir <parse> [required] │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --watch │
│ --timeout SECONDS [default: root=10.0] │
│ --help Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
Notes
_ means the same thing on a heading as it does on a path segment: this is scratch, and it emits nothing. A ## _Notes section — or ## _Improvements, or whatever you want to call it — is somewhere to collect thoughts beside the work they are about, and it is dropped from the output along with every subsection under it. Both commands drop it, from a SKILL.md, a mirrored note and a bundled reference alike.
It is dropped before links resolve and before any command block runs, so a [[dangling link]] there cannot fail the run and a command there never executes. That is what makes the section a safe place to be sloppy — and the reason not to park a link you are relying on in one, because the rename-everywhere guarantee does not reach into it.
A marked section runs to the next heading at its own level or above, or to a --- divider — whichever comes first. The divider is how you close scratch that sits in the middle of a section rather than at the end of the note: the rule and the blank air under it are dropped along with the scratch, and the prose below goes on belonging to the heading above. A --- sitting directly under a line of text is that line's underline, so it names a section instead of closing one, and a --- inside a fence is code.
Embedding a whole note leaves its marked sections behind, at any depth, so no embed ever drags someone else's scratch into yours. An embed whose #fragment names a marked section still gets it, because that is an author asking for it by name. A heading you want rendered that happens to start with _ should be a code span, ## `_private`, which does not take the mark.
Links
A plain [[wikilink]] is resolved and replaced; only an embed — the same brackets behind a ! — still pulls text in. What it becomes depends on what it points at, so the same link reads correctly in both layouts:
| Target | Emits |
|---|---|
| an unmarked note — a term | its name: [[Tracer bullet]] → Tracer bullet |
an @-marked note — a skill |
an invocation, in a code span: [[@grill]] → `/grill` |
| an attachment | a path to where it landed: [[diagram.png]] → [diagram](../assets/diagram.png) |
The @ and _ prefixes are input-side vocabulary and never reach the reader. [[Target|display]] sets the wording for a term or an attachment; a skill ignores it, because /grill is a call rather than prose, and the call is emitted in a code span so it reads as one. A #fragment rides along on whatever the link emits.
Renaming a note is therefore a rename everywhere it is mentioned, and a link that resolves to nothing — or to two notes at once — is reported and fails the run with exit 1, leaving the source [[link]] in place so the author can see what they wrote. A link inside a fence or a code span is quoted rather than resolved, which is how this table is written; so is anything a command prints.
Output
Every problem is logged where it arises and again at the end of the run, so a long render does not bury its errors above the scrollback. The run closes with a count — 12 files, 1 warning, 2 errors — followed by the warnings and then the errors, worst news nearest the prompt. Under --watch each recompile closes the same way, since a session has no other end to report at.
Log lines are coloured by level when stderr is a terminal, and plain when it is a pipe or when NO_COLOR is set. --quiet drops the per-file chatter and keeps warnings, errors and the summary; a quiet run that prints nothing is a run that had nothing to report.
Try it
mkdir -p 'docs/guides/@plan/references' out compiled
printf '# Review\n\nRun [[@plan]] against a [[Tracer bullet]] first.\n' > 'docs/guides/@review.md'
printf -- '---\ndescription: Plan a change\n---\n# Plan\n' > 'docs/guides/@plan/SKILL.md'
echo '# Spec' > 'docs/guides/@plan/references/spec.md'
echo 'A thin vertical slice.' > 'docs/Tracer bullet.md'
uv run documator render docs out
uv run documator skills docs compiled
Two layouts over one tree. render mirrors it into out/, so guides/@review.md stays guides/review.md. skills flattens it into compiled/: the bare guides/@review.md becomes review/SKILL.md with a name-derived description, and the guides/@plan/ folder becomes plan/SKILL.md — carrying its declared description through — with references/spec.md bundled beside it as plan/references/spec.md. Tracer bullet.md is a term: render mirrors it like any other note, but it compiles to no skill, and it exists so the link in the review has something to name.
This block is extracted verbatim and run by test_readme.py.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file documator-1.4.1.tar.gz.
File metadata
- Download URL: documator-1.4.1.tar.gz
- Upload date:
- Size: 99.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
402be7b0f87a15b3660695974f993dd2c7f4260bbef57f427f234eaef86c1e07
|
|
| MD5 |
a9bc028f213cfbdfa0a6d1afbd6bf480
|
|
| BLAKE2b-256 |
0160289655996a6b360d70b764f232fddbc01ebe2bbea43a53ad9cd91926fbba
|
Provenance
The following attestation bundles were made for documator-1.4.1.tar.gz:
Publisher:
ci.yml on MartinBernstorff/documator
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
documator-1.4.1.tar.gz -
Subject digest:
402be7b0f87a15b3660695974f993dd2c7f4260bbef57f427f234eaef86c1e07 - Sigstore transparency entry: 2514479745
- Sigstore integration time:
-
Permalink:
MartinBernstorff/documator@fe77b800bf71ac98c1a006d51b604fc22b7a81ee -
Branch / Tag:
refs/heads/main - Owner: https://github.com/MartinBernstorff
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@fe77b800bf71ac98c1a006d51b604fc22b7a81ee -
Trigger Event:
push
-
Statement type:
File details
Details for the file documator-1.4.1-py3-none-any.whl.
File metadata
- Download URL: documator-1.4.1-py3-none-any.whl
- Upload date:
- Size: 75.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5bad94bc40bfe0c4290387fb45261f6a93675e6ee5c10c707bf900a290a9b51d
|
|
| MD5 |
1b3d1b196585ab4aa555ae5deb8682f3
|
|
| BLAKE2b-256 |
69ddd5aba291b893f07812d1c5aec5cb1a63eb71800d16d7ac0643cbc9736f38
|
Provenance
The following attestation bundles were made for documator-1.4.1-py3-none-any.whl:
Publisher:
ci.yml on MartinBernstorff/documator
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
documator-1.4.1-py3-none-any.whl -
Subject digest:
5bad94bc40bfe0c4290387fb45261f6a93675e6ee5c10c707bf900a290a9b51d - Sigstore transparency entry: 2514479752
- Sigstore integration time:
-
Permalink:
MartinBernstorff/documator@fe77b800bf71ac98c1a006d51b604fc22b7a81ee -
Branch / Tag:
refs/heads/main - Owner: https://github.com/MartinBernstorff
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@fe77b800bf71ac98c1a006d51b604fc22b7a81ee -
Trigger Event:
push
-
Statement type: