Write docs
Scaffolds, extends, and reconciles a project's `docs/` tree in the TitusKirch docs format — one stack-agnostic convention shared across all repos, ADRs included (append-only decision records in `docs/99.adr/`). Routes by state — scaffolds when `docs/` is missing, adds to the right section when it exists, reconciles existing pages when asked (never rewriting prose). Always previews a plan and writes only after confirmation. Use when the user wants to write, add, scaffold, or update documentation, set up a docs/ tree, document a feature, record or supersede an architecture decision, or says things like "write the docs", "add a docs page", "document this", "reconcile the docs", "write an ADR", "record this decision", "Doku schreiben", "docs aktualisieren". Also trigger proactively once a feature has cleared review and final approval — when the work is settled, not when implementation finishes — to document the shipped result.From its SKILL.md
npx -y skills add TitusKirch/skills --skill write-docsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
14.0 KB, ~3.0k tokens by cl100k_base, as published. Nobody here has run it
write-docs
The TitusKirch docs format — one opinionated, stack-agnostic convention for a project's docs/ tree, the same in every repo (including client projects). This skill owns the convention; the page mechanics live in REFERENCE.md, the skeletons in templates/. Pages are plain Markdown with title + description frontmatter and numeric-prefixed paths — a clean tree any file-based docs generator can render, index, and feed to an LLM.
Opted out? If the repo config sets docs to false, this skill is disabled for the repo — stop immediately (including the proactive trigger) and tell the user docs are turned off in .tituskirch-skills.json. An absent docs block is not disabled — that falls back to defaults/detection. Check .docs == false on the resolved config before any job — and before indexing .docs.*. A missing jq or config exits non-zero too, so a pass is not evidence the config was read.
Jobs — pick by repo state + intent
| State / intent | Job |
|---|---|
docs/ missing | scaffold |
docs/ exists + "document feature X" | route/add |
| "update / align / migrate / reconcile docs" | reconcile |
Optional verb shortcuts: /write-docs init, /write-docs add <topic>, /write-docs reconcile. Otherwise infer from state and the request. Always: plan → confirm → apply.
Proactive trigger — don't wait to be asked. Once a feature has passed all its reviews and reached final approval (signed off or merged), engage this skill yourself and run the route/add job for that feature. Trigger on final approval, not on implementation finished — code still facing review is too early, and a feature that gets reworked shouldn't be documented twice. The write still follows plan → confirm → apply.
What belongs in docs at all
Code says what happens. Docs say why. An agent reads the code, so a page that narrates what the code does is a slower, staler copy of something already open in the editor — and if the code needs narrating, the fix is the code, not a page about it.
Before writing anything, ask what the reader could not recover by reading the source. Exactly three answers survive that test:
| Code cannot express | Because | Goes to |
|---|---|---|
| The roads not taken | What was rejected, and why, leaves no trace in what shipped | adr, concepts |
| How new work here is done | The intended approach for the next change is not a property of the current one | conventions, guides |
| Where to start | A repo has no first line; which seam to enter by, and how the parts relate, is not in a file | concepts, getting-started |
Everything else — what a function does, which options exist, what a command prints — either belongs in the code and its own reference output, or is already in a file that owns it. If a planned page fits none of the three rows, that is the answer: don't write it.
Routing matrix — what you changed → page type → section
| You added / changed | Page type | Section |
|---|---|---|
| A user-facing capability's usage | guide / tutorial | guides |
| A repeatable task / "add a new X" | how-to (ends w/checklist) | guides |
| A subsystem, model, or how-it-works | concept / architecture | concepts |
| A lookup value: env var, CLI flag, config, API | reference entry | reference |
| Setup / install / first-run change | (update existing) | getting-started |
| A run / deploy / maintain procedure | how-to or concept | operations |
| A project-specific rule or pattern | concept | conventions |
| An architectural decision + its reasoning | ADR (own schema) | adr |
A real feature usually spans several types: how-it-works (concepts) + usage (guides) + lookup values (reference). "How it works" means the shape and the reasoning — the seams, the invariants, why it is built this way — never a walk through the implementation, which the code states better and keeps current for free.
The reference row is the one to challenge. Lookup values earn a page only where nothing machine-readable already holds them — an HTTP API with no published schema, env vars with no .env.example. Where a schema, a manifest or --help is the real answer, the reference page is one sentence naming it plus whatever it cannot say (which values are safe to change, which combinations conflict). A transcribed option table is the single most common stale page in any repo. Lead with how-it-works and how-to-use; push every lookup value to reference and link to it. Page type is implied by section + template — it is not a frontmatter field. Catalogue, core sections and presets: REFERENCE.md.
ADRs are the standing exception. They live in docs/99.adr/ (fixed prefix, flat), are named NNNN-title.md for a permanent decision id, carry status + date on top of the house frontmatter, and are append-only — a reversed decision writes a new ADR and marks the old one superseded, never edits it. A concept page explains how a thing works today; an ADR records why it was chosen, then. Full contract: REFERENCE.md.
Scaffold — docs/ is missing
- Resolve the preset —
docs.presetconfig → detect from the repo (bin/CLI →cli, a published manifest — library, module, agent/skill set →package, a UI app →app, a server entrypoint with no frontend build →service, IaC →infra) → ask. The preset is the whole set of sections to scaffold; none is implicit. Several signals hit at once in most repos — a CLI that also publishes a library, a module shipping a demo app — so resolve by how the thing is primarily consumed, not by what the repo contains: invoked →cli, imported into another project →package, run and used →app/service, applied to an environment →infra. Genuinely co-equal → ask. - Resolve the language —
docs.language→ rootlanguage→ existing docs/repo language →en(see REFERENCE.md#config). When set,docs.instructionsshapes the generated-docs wording (tone, house conventions) — additive only, never overriding the docs format or guardrails. - Plan the tree — the preset's sections, plus any its conditional column earns here; show it.
- Drop any section that would only redirect. For each planned section ask: does this repo have a page's worth of material that is not already canonical somewhere else — a README that covers install and first run, a committed schema that is the config reference, per-module docs that ship with their module? If not, the section's
index.mdcan only say "the real thing is over there", which costs a click and returns nothing. Leave it out, name it in the plan with the reason, and let route/add create it later when it has a page of its own. This applies to every section the preset names — a preset is the starting set, not an obligation. - On confirm — create numeric-prefixed section dirs, each with an
index.md(frontmattertitle+description, plain-text H1), plus adocs/index.mdlanding page. Generated docs are emoji-free. Skeletons:templates/.
A scaffold that produces one section is a success, not a failure — the tree's job is to hold what has no other home, and in a repo whose parts already document themselves that can be a single section. Numbering starts at 1. over whatever survives, so nothing looks missing.
Route / add — docs/ exists
- Run the routing matrix; list every page to create or touch.
- New page → next free
N.kebab.mdin the section, frontmattertitle+description, body from the matching template, then link it from that section'sindex.md. - How-tos end with a checklist. Add a
> [!NOTE]Status callout (see REFERENCE.md#status-marker) if the feature isn't shipped yet. - Delta principle — don't restate what something else already documents; link it, and write the project-specific delta plus the glue. Three sources outrank a page here, in descending order of how badly a copy hurts:
- A file in this repo.
package.json's scripts, a workflow, a schema, a lockfile, a config — a doc that lists their contents is wrong the moment the file changes, and nothing will tell you. Name the file and what is surprising about it; never transcribe values, versions or option tables out of it. - Anything the repo ships. A library, plugin or skill travels without
docs/, so it must carry its own reference — a second copy here is the one guaranteed to drift. - An authoritative upstream source — framework docs, a dependency's README, a standard. A strong guideline, not a hard block: cross-cutting glue, and the reason a thing is set the way it is, are exactly what no source file states.
- A file in this repo.
- Updating an existing topic — the one-topic-per-page rule means there's usually exactly one page;
grep docs/for the term, edit it in place, never open a second page on the same topic, and update affected reference tables + cross-links. Never for an ADR — a changed decision is a new ADR that supersedes the old one, and the ADR section'sindex.mddecision log gets the row in the same change. - Verify every fact against current code before writing.
Reconcile — align existing docs to the convention
Desired-state, idempotent — like a --fix linter for the docs tree.
- Read the whole
docs/tree fresh (it is live state — never cached). - Diff against the convention and group the plan:
- Auto-fix (mechanical) — numbering gaps/dupes, missing
index.md, removed/unknown frontmatter keys,N.kebab.mdfilename normalization, unambiguous broken relative links. - Prompt (value-needing) — a missing required field (
title/description): derive a candidate from the H1 / first paragraph and confirm. - Report only — a how-to without a checklist, a page that fits no section, suspected duplication of an upstream source or of a file in the repo, any secret detected. Never auto-edited.
- Auto-fix (mechanical) — numbering gaps/dupes, missing
- Show the plan + diff; on confirm apply only structure + frontmatter. Never rewrite prose. Only touch files inside
docs/.
99.adr/ is exempt — links and the decision log only. An ADR id is permanent, so never renumber one, never rename it to the dot-schema, never close a gap in the sequence (REFERENCE.md).
Guardrails (inherited)
- Docs are never the source of truth — the repo is. Code says what happens; a page exists for the why, the rejected alternatives, and the way in — the three things code cannot express. Documentation that mirrors code or a file costs tokens on every read and starts lying at the next commit, so when a page would restate one, link it and write only the delta.
- Plan/preview first; apply only after confirmation. Respect plan-only / dry-run.
- Keep generated content attribution-free — no agent self-naming or
Generated with/🤖 lines. - No secrets in generated docs — scan, warn, exclude.
- Only the requested action — nothing closed or changed unasked.
- No cache — the
docs/tree is live state, always read fresh.
Reference
- Section catalogue, core, presets, frontmatter contract, page types, status marker, ADR contract, reconcile rules, config keys: REFERENCE.md.
- Page skeletons to copy:
templates/.
Gap report (mandatory final step)
If you used a section, page type, or preset not in REFERENCE.md, end the turn with a short note (Gap report: section "{x}" — no catalogue entry; added ad hoc.). Only report; don't edit REFERENCE.md yourself — the user folds gaps back in. A new slug is a real gap only when it names a missing type; a subject section (plugins, themes, integrations) is not — route its content into the type sections (nested if it needs grouping) and report that instead. If everything matched: Gap report: no gaps.
What ships with it: 10 files
41.4 KB alongside SKILL.md, 1 of them executable
templates/
- adr-index.md1.1 KB
- adr.md1.7 KB
- concept.md482 B
- docs-index.md783 B
- guide.md438 B
- how-to.md522 B
- reference.md496 B
- resolve-config.shruns2.0 KB
- section-index.md269 B
- REFERENCE.md33.7 KB