Metadata-Version: 2.5
Name: documator
Version: 1.3.0
Summary: A documentation tool
Author-email: Martin Bernstorff <martinbernstorff@gmail.com>
Requires-Python: >=3.14
Requires-Dist: colorlog>=6.12.0
Requires-Dist: iterpy>=1.14.0
Requires-Dist: pydantic>=2.13.4
Requires-Dist: typer>=0.27.0
Requires-Dist: watchfiles>=1.2.0
Description-Content-Type: text/markdown

<!-- Generated by documator from README.md — edit the template, not this file. -->
# 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.

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

```sh
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`.
