docs-reference-check
Dangling doc-reference gate: flag markdown naming repo paths or relative links that no longer exist. Use when docs cite moved/deleted files, or wiring a doc-drift lint.
- License 许可
- MIT
- 最近更新
- 2026-11-11
- Stars
- ★ 59
- Forks
- 6
01 前言 Front Matter
- args
- [--strict] [--chezmoi auto|on|off] [--allow-file <path>] [files...]
- name
- docs-reference-check
- created
- 1794182400000
- modified
- 1794182400000
- reviewed
- 1794182400000
- description
- Dangling doc-reference gate: flag markdown naming repo paths or relative links that no longer exist. Use when docs cite moved/deleted files, or wiring a doc-drift lint.
- allowed-tools
- Bash(python3 ${CLAUDE_SKILL_DIR}/scripts/check-doc-references.py *), Bash(cp *), Read, Grep, Glob, Edit, Write, TodoWrite
- argument-hint
- [--strict] [--chezmoi auto|on|off] [--allow-file <path>] [files...]
02 资源文件 2
03 SKILL.md 正文
Dangling Doc-Reference Check
A doc that names <dir>/deploy.sh or links [setup](../setup.md) after the
file moved still reads as authoritative, and nothing fails. This skill runs a
deterministic, stdlib-only scanner over a repo's tracked markdown and reports
every repo-relative reference that resolves to nothing. It checks existence
only; whether the prose is still true is a review question.
When to Use This Skill
| Use this skill when... | Use instead when... |
|---|---|
| Docs may cite files that were renamed, moved or deleted | Prose claims must stay pinned to specific code symbols: configure-plugin:configure-surface (Surface fingerprints) |
| Adding a doc-drift lint to pre-commit or CI | Content leaves for a public destination and links must not escape the export: documentation-plugin:public-export-sanitization |
| Cleaning up after a refactor that moved scripts or docs | Auditing PRD/ADR/CLAUDE.md staleness and structure: code-quality-plugin:code-docs-quality |
| A chezmoi repo's docs name managed files by their rendered names | A figure or count in a doc may have gone stale: documentation-plugin:docs-claim-hygiene |
What it flags
Precision-first: a gate that cries wolf gets bypassed, so it flags two shapes only.
| Shape | Flagged when |
|---|---|
Relative markdown link [text](path) |
The target resolves neither from the doc's directory nor (for /path) from the repo root. URLs, mailto:, #anchors, templated and placeholder targets are skipped |
Inline-code path `dir/file.ext` |
Its first segment is a real tracked top-level entry, and it resolves neither from the repo root nor from the doc's directory. ./name names a repo-root file and is checked |
Not flagged: bare filenames (deploy.sh), slash commands, domains, ~/ and
absolute paths, placeholders (NNNN, <name>, ..., example), and anything
inside fenced code blocks. A token whose first segment is not a top-level entry
(consumer/scripts/x.sh) is treated as a path in some other repo.
Run it
python3 ${CLAUDE_SKILL_DIR}/scripts/check-doc-references.py
It scans every tracked *.md from the git toplevel of the current directory,
or only the files given as arguments. Output:
=== DOC REFERENCE CHECK ===
CHEZMOI_MODE=off
DOCS_CHECKED=41
ALLOWLISTED_PATTERNS=1
DANGLING_COUNT=2
STATUS=WARN
REASON=dangling_ref: docs/setup.md:12 scripts/old.sh (+1 more)
ISSUE_COUNT=2
ISSUES:
- SEVERITY=WARN TYPE=dangling_ref FILE=docs/setup.md:12 MSG=scripts/old.sh
- SEVERITY=WARN TYPE=dangling_link FILE=README.md:40 MSG=docs/gone.md
=== END DOC REFERENCE CHECK ===
| Mode | Findings report | Exit |
|---|---|---|
| default (advisory) | STATUS=WARN |
0 |
--strict |
STATUS=ERROR |
1 |
| no findings | STATUS=OK |
0 |
not a git repo, or --allow-file missing |
stderr message | 2 |
SCANNED_EMPTY=true appears when no doc was checked, so an empty scan never
reads as a clean one.
Triage each finding
For every ISSUES: row, decide one of:
- The file moved: find its new path (
git log --follow --diff-filter=Ror Glob on the basename) and update the reference. - The file is gone for good: reword or remove the reference.
- The doc describes another repo's layout (a template, a plugin skill
telling consumers to create
.github/workflows/x.yml): allowlist the doc. - The doc is an immutable record (an ADR citing a path accurate when written): allowlist it.
Re-run until STATUS=OK.
Allowlist
.doc-reference-allow at the repo root (or --allow-file <path>): one
fnmatch glob per line, matched against the doc's repo-relative path, #
comments allowed. A matching doc is skipped whole.
# Immutable records: ADRs cite paths accurate when the decision was made.
docs/adrs/**
# Docs that describe consumer repos, not this one.
templates/**
Comment every entry with why it is exempt. A repo whose docs mostly describe other repos' layouts (a plugin marketplace, a project template) needs those docs allowlisted before the gate is useful there.
chezmoi mode
In a chezmoi source repo a doc names <dir>/nudge.sh while the repo holds
<dir>/executable_nudge.sh.tmpl, and .zshrc while it holds dot_zshrc.
With chezmoi mode on, a path also resolves when a sibling's rendered name
matches (attribute prefixes, dot_, and .tmpl stripped).
--chezmoi |
Effect |
|---|---|
auto (default) |
On when the repo root has a .chezmoi* marker (.chezmoiignore, .chezmoiroot, .chezmoidata.toml, …) |
on / off |
Forced |
CHEZMOI_MODE= in the output shows which applied.
Wire it as a gate
A plugin's path is not available in CI or to other contributors, so copy the script into the repo first:
cp ${CLAUDE_SKILL_DIR}/scripts/check-doc-references.py scripts/check-doc-references.py
Pre-commit (advisory): shows findings on every commit and never blocks.
- repo: local
hooks:
- id: check-doc-references
name: check docs for dangling references (advisory)
entry: python3 scripts/check-doc-references.py
language: system
pass_filenames: false
always_run: true
verbose: true
Lint task (strict): the enforcing variant for CI.
# mise.toml
[tasks."lint:docs"]
run = "python3 scripts/check-doc-references.py --strict"
# justfile
lint-docs:
python3 scripts/check-doc-references.py --strict
Get the tree to STATUS=OK (fix or allowlist) before turning on --strict,
or the first CI run fails on old drift.
Agentic Optimizations
| Context | Command |
|---|---|
| Whole repo, advisory | python3 ${CLAUDE_SKILL_DIR}/scripts/check-doc-references.py |
| Summary lines only | python3 ${CLAUDE_SKILL_DIR}/scripts/check-doc-references.py | grep -E '^(STATUS|REASON|ISSUE_COUNT)=' |
| Changed docs only | python3 ${CLAUDE_SKILL_DIR}/scripts/check-doc-references.py <files...> |
| CI gate | python3 ${CLAUDE_SKILL_DIR}/scripts/check-doc-references.py --strict |
| Another checkout | python3 ${CLAUDE_SKILL_DIR}/scripts/check-doc-references.py --project-dir <repo> |
04 同仓库更多 Skills
kitty-show-image
Show the user an image with a note on what to judge, via a file send or a labelled kitty tab. Use when the user must see an image, or icat fails with no /dev/tty.
kitty-session-persistence
Snapshot and restore kitty terminal sessions on macOS. Use when surviving WindowServer hangs without losing tabs, configuring snapshots, or restoring after a force-reboot.
kitty-remote-control
Safe kitty remote control from an agent — read panes with kitty @ ls/get-text, notify, and never mutate the live terminal unasked. Use when running inside kitty.
youtube-upload
Upload a video to YouTube via the Data API v3 and edit its title, description, tags or thumbnail afterwards. Use when publishing or retitling a YouTube video.
youtube-studio-share
Share a private YouTube video with a whole Google Workspace organization by scripting YouTube Studio in the browser. Use when a video must be org-only.
workflow-wave-dispatch
Sequential-wave dispatch for multi-agent work with cross-task dependencies. Use when planning multi-step work, fixing parallel-dispatch order, or gating waves on verification.