| name | refresh-docs |
| description | Refresh stale knowledge-base docs, and create/bind new ones. Detects living docs whose doc_source subtrees have drifted (by commits, churn, or age) past their device-local anchor, then incrementally rewrites each — minimal changes, preserving human annotations — and re-anchors to HEAD. Use to bring docs up to date with code, to turn a doc into a bound doc, or after a todo report shows DOC- tasks.
|
Refresh Docs
Commit-anchored freshness for living docs — a collection distinct from dated event memory.
Event memory is immutable/append-only/decaying; living docs are mutable, code-anchored, and
never evicted. Kept out of the memory tree, so refresh-in-place has no append-only invariant to
fight and prune never touches them.
A bound doc declares in its frontmatter only the semantic binding — what code it documents
(+ optional thresholds). The volatile "last validated at commit X" anchor is NOT in the doc:
it lives device-locally in .claude/.rem-state.json (gitignored), so the tracked doc stays pure and a
teammate/fresh clone conservatively re-reviews rather than trusting your local anchor.
---
name: setup-architecture
description: how the setup pipeline works
metadata:
type: reference
doc_source: [scripts/setup/, claude_settings.template.json]
---
Location & discovery (no config)
A doc's kind is decided by its frontmatter, not its path — so bound docs are discovered,
not configured. doc-freshness.js lists .md files via git ls-files (honoring .gitignore
for free) and keeps those with doc_source. So a doc plugs into the project's own doc system
(docs/ for mkdocs/Sphinx/Jekyll — they already use YAML frontmatter, so our fields coexist)
just by living there with doc_source — zero config.
Process
-
Detect drift:
node ${CLAUDE_PLUGIN_ROOT}/scripts/doc-freshness.js --json
Prints { "stale": [ { path, doc_source, git_hash, commits, lines, days, dangling? } ] }.
Stop when stale is []. (Exit 2 with { "ambiguous": true, "candidates": [...] } under
--json means multiple doc roots — resolve that first, see Location above.)
-
For each stale doc, gather only the increment — never re-read the whole codebase:
git diff <git_hash>..HEAD -- <doc_source...>
- If
git_hash is null (unanchored / fresh clone) there is no range to diff — instead read
the doc_source files directly to (re)establish what the doc should say.
- If
dangling is set, the binding is broken: fix the doc_source paths (or the code moved)
before anchoring.
-
Incrementally rewrite the doc file in place (never relocate it — its path is its stable
identity):
- Change only the sections the diff touches. Leave everything else byte-identical.
- Never edit inside
<!-- rem:manual --> … <!-- /rem:manual --> blocks — these are
human annotations. Preserve them verbatim, including position.
- Keep the doc's structure, headings, and voice.
-
Re-anchor (device-local, not the frontmatter) to HEAD + today:
node ${CLAUDE_PLUGIN_ROOT}/scripts/doc-freshness.js --set-anchor <relPath>
Binding a new doc
To turn a doc into a bound doc: add doc_source (+ optional thresholds) to its frontmatter,
then set its baseline anchor so it doesn't immediately read stale:
doc-freshness.js --set-anchor <relPath>.
Architecture doc-sets (complex projects)
A doc-set = one index doc (type: reference) + chapter docs, each chapter binding a distinct
doc_source subtree. Freshness is per-chapter — only chapters whose subtree changed get
flagged and refreshed. The directory tree is the table of contents; the index doc only changes
when chapters are added or removed.
Constraints
- Minimal change — the diff drives edits; do not rewrite untouched prose.
- Preserve
<!-- rem:manual --> blocks exactly.
- Refresh in place — same file path; only touched sections change (the anchor is external).
- Stale docs surface in
/todo as virtual DOC-<path> rows; they clear automatically once the
anchor advances — there is no mark/remove step.
- Multi-round refresh: if refreshing many docs in one turn without background tasks, set
taskActiveUntil so the Stop hook doesn't fire mid-run.