docsync — track and fix doc drift

Caution

Docs go stale the moment code moves on. Nobody remembers to re-read SKILL.md after a refactor, and a forgotten README.md means the next session starts from wrong assumptions. Manual checks don’t scale across dozens of files.

Tip

Frontmatter is the source of truth — no separate ledger. Docsync installs three project-local hooks that quietly record which .md files you read or edit. At the end of a turn, if any touched doc is stale by its own last_updated date, it nags once and asks whether to sync. Replaces brewdoc:auto-sync.

Quick reference

FieldValue
Command/brewdoc:docsync [mode]
Argumentsinit · status · sync [--all] · reread · frontmatter · uninstall · free text (RU/EN)
Modelsonnet
ToolsRead, Write, Edit, Bash, Glob, Grep, AskUserQuestion

When to use

ScenarioCommand
First time in a project — set up staleness tracking/brewdoc:docsync (empty + not installed → init)
Check what’s tracked and what’s stale, no changes/brewdoc:docsync status
Refresh stale docs after confirming which ones/brewdoc:docsync sync
Force a full resync of every in-scope doc/brewdoc:docsync sync --all
Reload tracked docs into context without editing/brewdoc:docsync reread
Retro-add tracking frontmatter to existing docs/brewdoc:docsync frontmatter
Remove the tracker without touching foreign hooks/brewdoc:docsync uninstall

Examples

# First run in a project — asks threshold + excludes, installs 3 hooks
/brewdoc:docsync
# Natural language also works, RU or EN
"что устарело в документации"
"track doc staleness in this project"
"docsync status"

status prints an age table per tracked doc:

## Status
| Doc | doc_type | last_updated | age | state |
|-----|----------|--------------|-----|-------|
| README.md | user | 2026-07-01 | 18d | stale |
| SKILL.md | llm | 2026-07-17 | 2d | fresh |
| CHANGELOG.md | skip ||| excluded |

Flow

  1. Resolve mode

    Free text in RU or EN maps to a mode (init, status, sync, reread, frontmatter, uninstall). Empty input picks init when hooks aren’t installed yet, otherwise status.

  2. Install (once)

    init asks for a staleness threshold (default 7 days) and exclude globs, copies the 3 hooks into .claude/hooks/, writes .claude/docsync/config.json, and idempotently merges .claude/settings.json — always backed up to .bak first. It never adds frontmatter to your docs.

  3. Track silently

    From the next session on, docsync-track.mjs (PostToolUse on Write|Edit|MultiEdit) and docsync-watch.mjs (PostToolUse on Read) record every .md file touched during a session — no output, no interruption.

  4. Nag once

    At Stop, docsync-gate.mjs checks touched docs against their own last_updated frontmatter. If any is older than threshold_days, it blocks the turn once and tells Claude to ask about syncing.

  5. Sync on confirmation

    /brewdoc:docsync sync lists the stale set, asks via AskUserQuestion, then refreshes each confirmed doc per its own sync_procedure and bumps last_updated to today. Compression depth follows doc_type: llm = deep, user = light.

Internals

Frontmatter schema — every tracked .md carries its own state, no separate registry:

---
doc_type:       llm            # optional; absent => user. values: llm | user | skip
last_updated:   2026-07-19     # sole staleness input (YYYY-MM-DD, LOCAL time)
sync_procedure: "what to check / where to look when syncing"   # optional, prose
---

Staleness is DATE ONLY: today - last_updated > threshold_days. No hashing, no depends_on graph. doc_type: skip excludes a file entirely.

The three hooks

FileEventMatcherAction
docsync-track.mjsPostToolUseWrite|Edit|MultiEditRecords touched .md; nudges to add last_updated when missing
docsync-watch.mjsPostToolUseReadRecords touched .md (silent)
docsync-gate.mjsStopBlocks once if a touched doc is stale; tells Claude to ask about syncing

Hooks are self-contained ESM (Node built-ins only), read state from .claude/docsync/ at runtime, and take effect starting the next session.

State / config.claude/docsync/:

  • config.json{ "threshold_days": 7, "exclude": ["node_modules/**", ...] }
  • state.json{ "session_id": "...", "touched": [], "asked": false } (hook-managed, resets per session)

Scope — all *.md in the project minus exclude globs and any file marked doc_type: skip. Only docs actually read or edited in a session are candidates for the end-of-turn nag; untouched docs are never nagged.

Uninstall removes only the 3 docsync hook entries from settings.json (foreign entries preserved), deletes the 3 hook files, and asks separately whether to drop .claude/docsync/.

🧠

Memory

Interactively compact and optimize memory files — pairs well with keeping docs fresh.

🔗

GitHub source

Source code, hook assets, and full SKILL.md for docsync.

🚀

Brewdoc overview

All brewdoc skills in one place.

Updating plugins

Use /brewtools:plugin-update to check and update the brewcode plugin suite in one command. See the FAQ for details.