Guides
Build a Docs Agent That Keeps Docs in Sync
Checked 2026-10-31
Loading poll...
A docs agent compares what your documentation says with what your code does, lists where they disagree, and drafts fixes for a person to review. Build it as a Claude Code skill that works from a diff, cites a file and line for every claim, runs the commands it documents, and opens a pull request instead of publishing. Facts checked 31 October 2026.
What you will have at the end
- A skill that audits docs against a code change and drafts edits
- A style section the agent follows, kept short enough to be read
- Automatic checks for commands, links and config keys
- A headless run that opens a draft pull request after each release
- Rules that keep the agent from inventing features
If you have not written a skill yet, how to write a Claude Code skill covers the file format first. For a related pattern with a different job, see build a code review agent.
Why docs drift, and what an agent can fix
Docs go wrong in four ways. A setting is renamed and the page keeps the old name. A default changes and the page states the old one. A feature ships and nobody writes it down. A page describes something that was removed. The first two and the last are comparisons between two texts, which a model does well. The third needs a person to decide what is worth documenting.
Docs problem | Agent can find it? | Agent can fix it? |
|---|---|---|
Renamed setting still in the docs | Yes | Yes, with a citation |
Wrong default value | Yes | Yes, with a citation |
Removed feature still described | Yes | Draft a deletion for a person to approve |
New feature with no page | Yes | Draft only; a person decides if it belongs |
Page is unclear or badly organized | Weakly | No; this is an editor's job |
A claim about behavior the code does not show | Flag it | No; ask the owner |
This is why the best first target is drift detection, not writing new pages from scratch. Drift is checkable, and checkable work is where an agent earns trust.
The design
Layer | Job | Why it is separate |
|---|---|---|
Docs map | Lists which docs cover which code | The agent does not guess which page to open |
Skill | The audit procedure | Loads only for docs work |
Checks | Scripts that test commands, links and keys | Exact, cheap and repeatable |
Draft PR | The output | A person reviews every change |
Keep the model's job narrow. It reads a diff, finds the affected pages, proposes edits with citations. Scripts do everything that can be tested exactly.
Step 1: Write a docs map
Without a map the agent searches the whole repo for every change. A short table in the repo tells it where to look.
# docs/MAP.md
Code area | Docs that cover it |
|---|---|
src/config/ | docs/settings.md, docs/reference/config.md |
src/api/ | docs/api/*.md, openapi.yaml |
cli/ | docs/cli.md, README.md#usage |
src/hooks/ | docs/developer/hooks.md |
Update the map in the same pull request that adds a new area. A stale map is worse than none, since the agent will trust it. Add a line to your contributing guide so the habit sticks.
## Step 2: Write the style rules, briefly
Docs style is the kind of knowledge Claude should hold while editing docs. A few concrete lines in a rules file or in your instructions file beat a style essay.
Docs style
- Second person, present tense, short sentences
- Every setting: name, type, default, one example
- Every command in a fenced block with a language tag
- Product and feature names exactly as in the UI
- No marketing words; state what a thing does
- Never document behavior you cannot point to in the code
If these live in `CLAUDE.md`, keep that file under 200 lines, as the docs recommend. For path-specific rules, a `.claude/rules/` file with a `paths` filter on `docs/**` loads only when Claude works on documentation. See our [CLAUDE.md guide](/guides/write-a-good-claude-md/).
## Step 3: Write the skill
The audit is a procedure Claude should run when asked, so it is a task skill. Save it as `.claude/skills/docs-audit/SKILL.md`. Setting `disable-model-invocation: true` means it runs only when you type `/docs-audit`, which suits something that edits files.
---
name: docs-audit
description: Audit documentation against a code change and draft fixes. Use when the user asks to check docs, update docs after a change, or run a docs audit.
disable-model-invocation: true
argument-hint: [base-ref]
allowed-tools: Read Grep Glob Edit Bash(git diff *) Bash(git log *)
---
Change under review
!git diff $ARGUMENTS...HEAD --stat
Procedure
- Read docs/MAP.md and docs/style.md.
- List the changed files from the stat above and the matching docs pages.
- For each affected page, read the page and the changed code.
- Find statements the change makes wrong: names, defaults, flags, steps, examples.
- Edit only those statements. For each edit, add a line to docs-audit-report.md:
page, old text, new text, and the code file and line that supports it.
- For behavior with no clear support in code, do not edit. List it under "Needs an owner".
- For a new feature with no page, list it under "Candidates". Do not write a page.
Never invent a setting, default or step. If you cannot cite it, leave it out.
The line with `!` and a backtick is dynamic context injection: Claude Code runs the command and puts its output into the skill before Claude reads it. Here it hands over the list of changed files. The `$ARGUMENTS` placeholder takes what you typed after the skill name, so `/docs-audit v2.3.0` compares against that tag.
`allowed-tools` pre-approves the listed tools for the turn that runs the skill and clears when you send your next message. The skill gets `Edit` but not an unrestricted `Bash`, only `git diff` and `git log`. That limits the blast radius of a mistake. Remember that deny rules and hooks still apply, and see the [hooks guide](/guides/claude-code-hooks-guide/) for protecting paths the agent should never edit, such as `src/`.
## Step 4: Keep the agent out of the code
A docs agent should change only docs. Enforce it with a `PreToolUse` hook rather than a sentence.
#!/bin/bash
.claude/hooks/docs-only.sh
INPUT=$(cat)
FILEPATH=$(echo "$INPUT" | jq -r '.toolinput.file_path // empty')
case "$FILE_PATH" in
docs/*|README.md|docs-audit-report.md|"") exit 0 ;;
*) echo "Blocked: the docs agent may only edit docs/ and README.md, not $FILE_PATH" >&2; exit 2 ;;
esac
Register it on `Edit|Write`. Because a hook in settings applies in every session, scope it to the skill instead by putting it in the skill's frontmatter. Skill hooks register when the skill is invoked and last for the rest of the session. Check the hooks reference for the exact limits on skill hooks before relying on them, and test it by asking the agent to edit a source file.
hooks:
PreToolUse:
- matcher: "Edit|Write"
hooks:
- type: command
command: "\"$CLAUDEPROJECTDIR\"/.claude/hooks/docs-only.sh"
## Step 5: Add checks that do not need a model
Scripts verify what a model might only claim. Run them after the agent finishes, and fail the job if they fail.
Check | How | Catches |
|---|---|---|
Commands run | Extract fenced | Wrong flags, removed commands |
Links resolve | A link checker over the changed pages | Dead and renamed pages |
Config keys exist | Grep each documented key in the source | Renamed or removed settings |
Code samples compile | Run the language's syntax check | Broken examples |
Docs lint | Your Markdown linter | Formatting and heading order |
A small script for the key check shows the idea.
#!/bin/bash
# For every `setting_name` in docs/settings.md, confirm it appears in src/
missing=0
for key in $(grep -oE '`[a-z_]+`' docs/settings.md | tr -d '`' | sort -u); do
if ! grep -rqw "$key" src/; then
echo "Not found in src/: $key"
missing=1
fi
done
exit $missingIt is crude, and it will flag harmless words, so keep an allowlist. Its value is that it fails loudly on a renamed setting.
Never run commands from docs blindly. A documented rm or curl | sh is a risk in a script, so run only an allowlist of read-only commands, or run the checks in a throwaway container.
Step 6: Run it headless after a release
After tagging a release, run the skill in headless mode and let the result become a draft pull request.
claude --bare -p "/docs-audit v2.3.0" \
--allowedTools "Read,Grep,Glob,Edit,Bash(git diff *),Bash(git log *)" \
--permission-mode dontAsk \
--output-format json > audit.json
./scripts/check-docs.sh
git checkout -b docs/audit-v2.4.0
git add docs README.md docs-audit-report.md
git commit -m "Docs audit against v2.3.0"
gh pr create --draft --title "Docs audit for v2.4.0" --body-file docs-audit-report.mdTwo details need care. A skill's slash name is only recognized at the start of the prompt, and --bare skips skill auto-discovery, so a bare run will not find /docs-audit on its own. The headless docs say bare mode still loads skills from the .claude/skills/ folder of a directory you name with --add-dir, so pass the skill's directory that way. Bare mode also never reads your subscription login, so set ANTHROPIC_API_KEY in the CI environment, with a key made in the Claude Console. There is a security reason to prefer bare mode here too: the headless docs say that without --bare, a -p session runs the hooks in a project's .claude/settings.json and connects the servers in its .mcp.json, even in a folder you have never trusted, with no trust dialog or per-server approval prompt. A docs audit that runs on pull requests from forks should not load whatever configuration the pull request carries.. And dontAsk denies anything not pre-approved, which is what you want in CI.
The pull request is a draft, and the body is the audit report with a citation per edit. A reviewer can check each claim against the cited line in a minute.
Step 7: Review like an editor
Reviewing an agent's docs edit is different from reviewing code. Use this short list.
- Open each citation. Does the cited file and line support the new sentence?
- Read "Needs an owner" first. These are the places the agent was honest about not knowing.
- Look for added claims. A diff that adds a sentence the old text did not have deserves extra care.
- Run the commands. Copy each changed command into a clean shell.
- Check the voice. The agent follows the style file. Sloppy output often means the style file is vague.
Merge when you would sign your name under the page. A reviewer who skims will let a wrong default through, which is the very error you built this to prevent.
A worked example of one audit
Take an imaginary release where a setting called cache_ttl was renamed to cache_ttl_seconds and its default changed from 60 to 300. The numbers here are examples.
The diff stat lists src/config/defaults.ts. The map sends the agent to docs/settings.md and docs/reference/config.md. It reads both pages and finds cache_ttl three times with a default of 60. The report then has three rows like this one.
Page | Old text | New text | Support |
|---|---|---|---|
docs/settings.md |
|
| src/config/defaults.ts:42 |
It also finds a paragraph on a legacy_cache option that no longer appears anywhere in src/, so it does not edit that paragraph and lists it under "Needs an owner". The key-check script then confirms cache_ttl_seconds exists in the source and that no page still uses the old name. A reviewer opens line 42, sees the new default, and approves in two minutes. Without the agent, the old name would have stayed on the page until a customer filed a ticket.
What to document and what to leave out
An agent will happily document everything it sees. That is not always right. Give it a filter.
- Document: anything a user can set, run, call or see, and every breaking change.
- Skip: internal helpers, private functions and test fixtures.
- Flag for a person: security behavior, billing behavior and anything with legal weight, since a wrong sentence there carries real cost.
- Never state: future plans, performance numbers or comparisons with other products unless a person supplied and checked them.
Put the filter in the skill so it applies to every run, and let the "Candidates" list carry the new-feature suggestions to a human editor.
Moving docs between tools
If your docs live in a repository, the agent works the same whatever tool renders them. If they live in a hosted editor, give the agent a way to read a copy, such as an export or an API, and keep the draft in a repository so the review step still exists. The principle is constant: the agent writes a proposal, a person accepts it, and only then does anything change on the live site.
Keeping it honest
A docs agent has one dangerous habit: producing fluent text with no support. Fluent and wrong reads better than awkward and right.
- Citations are mandatory. An edit with no
file:lineis removed. - Prefer deletion to invention. If a page describes something you cannot find, flag it.
- Mark unknowns. The skill writes a VERIFY note for anything it cannot confirm, so no unverified claim ships unmarked.
- Do not trust version numbers it did not read. Dates, versions and prices come from files or release pages, never from memory.
- Keep generated text distinguishable in the pull request so a reviewer knows what to check closely.
Large documentation sets
For a site with hundreds of pages, run the audit per area and fan out. Subagents run in parallel and keep each page's reading out of the main context, so a skill with context: fork and a defined agent fits well. Remember that a forked skill does not see your conversation, so put everything the task needs in the skill. Cap the number of pages per run, and keep the docs map accurate, since it decides what the agent reads. Mind the default limit of 20 concurrent subagents in a session.
Costs
The cost of an audit scales with the diff and the pages it touches. Because the agent reads only mapped pages, a typical release audit stays small. Log total_cost_usd from the JSON output per run. If cost climbs, check that the diff excludes generated files and that the map is not sending the agent to your whole site.
Our take
The strongest docs agent is a narrow one. Give it a diff, a map and a rule that every claim needs a citation. It will find the renamed setting that nobody remembered to update, which is the kind of error that costs a support ticket each. Leave the tone, structure and the choice of what to document to people.
We would start by running the skill by hand on your last release, read the report, and see how many real errors it finds. If it finds five, schedule it. If it finds none, your docs are fine or your map is wrong, and the report will tell you which.
Checklist
docs/MAP.mdmaps code areas to pages- A short style file the agent can follow
- A skill with a diff, a procedure and a citation rule
- A hook that limits edits to docs paths
- Scripts that check commands, links and keys
- Output goes to a draft pull request, never to the live site
- A person reviews each citation
- Unknowns are marked, not guessed
Related pages
Questions
What does a docs agent do?
It compares what the docs say with what the code does, lists the places they disagree, and drafts fixes for a person to review. The most useful version works from a diff, not from the whole repository.
Should it publish docs by itself?
No. Have it open a pull request or write a draft, and let a person merge. Docs are a promise to customers, and a wrong claim published automatically is worse than an old page.
How do I stop it inventing features?
Require every claim to cite a file and line, and run the checks you can automate: commands run, links resolve, config keys exist. Drop any sentence it cannot support.
Where should the agent's instructions live?
In a skill for the procedure, with a short CLAUDE.md or AGENTS.md section for the docs style. A skill loads only when needed, so it costs nothing in sessions that are not about docs.
Loading poll...