Tips & Tricks (更新: 2026/6/2)

用Claude Code自动生成文档:README、API规格、ADR与验证流程

用Claude Code生成README、OpenAPI、ADR和变更日志,并用Node脚本验证,避免漂亮但错误的文档。

用Claude Code自动生成文档:README、API规格、ADR与验证流程

不要只让Claude Code写文档,要让它基于证据写

README、API规格、ADR和变更日志最容易在项目里落后。功能上线后,团队往往先修bug、发版本、处理用户反馈,文档就变成“以后再补”的任务。等到新人加入或外部用户查看API时,才发现命令已经变了,接口返回也和说明不一致。

Claude Code可以明显缩短这部分工作。官方的Claude Code overview说明,它可以读取代码库、编辑文件并运行命令。也就是说,它不仅能写一段说明,还能结合真实文件更新文档。但风险也在这里:如果没有给出清楚证据,它可能写出语气自然、结构完整、却并不存在的命令或接口。

本文把“自动生成文档”拆成一个可落地的流程:先生成仓库上下文,再用Claude Code skill更新README、OpenAPI、ADR和CHANGELOG,最后用Node脚本验证。你也可以把它和Claude Code验证收据流程一起使用,把“做了什么、怎么确认、还有什么风险”记录下来。

总体流程:先定义事实来源,再生成正文

最差的提示词是“帮我把README写好一点”。Claude Code会尽力完成,但它不知道哪些文件是事实来源,也不知道哪些信息不能公开。更稳妥的方式是先把package.json、git差异、最近提交和文件列表压缩成一份上下文,再让Claude Code在这个边界内更新文档。

flowchart LR
  A["代码差异"] --> B["doc-context.mjs"]
  B --> C["Claude Code skill"]
  C --> D["README / OpenAPI / ADR / CHANGELOG"]
  D --> E["verify-docs.mjs"]
  E --> F["人工发布判断"]

ADR是Architecture Decision Record,也就是“架构决策记录”,用来解释为什么选择某个设计。OpenAPI是描述API端点、请求体和响应的机器可读格式。初学者不需要一开始就写得很完整,重点是让这些文档可以被检查,而不是只看起来专业。

第一步:生成给Claude Code看的仓库上下文

把下面的脚本保存为scripts/doc-context.mjs,在仓库根目录运行node scripts/doc-context.mjs。它会把当前脚本、未提交差异、最近提交和部分文件列表写入docs/_generated/doc-context.md

import { execSync } from "node:child_process";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import path from "node:path";

function run(command) {
  try {
    return execSync(command, {
      encoding: "utf8",
      stdio: ["ignore", "pipe", "pipe"],
    }).trim();
  } catch {
    return "";
  }
}

function readIfExists(filePath) {
  return existsSync(filePath) ? readFileSync(filePath, "utf8") : "";
}

const root = process.cwd();
const outDir = path.join(root, "docs", "_generated");
mkdirSync(outDir, { recursive: true });

const packageJson = readIfExists("package.json");
const packageSummary = packageJson
  ? JSON.stringify(JSON.parse(packageJson).scripts ?? {}, null, 2)
  : "{}";
const fence = String.fromCharCode(96, 96, 96);

const trackedFiles = run("git ls-files")
  .split(/\r?\n/)
  .filter(Boolean)
  .filter((file) => !file.startsWith("node_modules/"))
  .filter((file) => !file.startsWith("dist/"))
  .filter((file) => !file.startsWith(".git/"))
  .slice(0, 400);

const changedFiles = run("git status --short") || "(no uncommitted changes)";
const recentCommits = run("git log --oneline -8") || "(no commits found)";

const content = [
  "# Documentation Context",
  "",
  "## Package scripts",
  `${fence}json`,
  packageSummary,
  fence,
  "",
  "## Changed files",
  `${fence}text`,
  changedFiles,
  fence,
  "",
  "## Recent commits",
  `${fence}text`,
  recentCommits,
  fence,
  "",
  "## Tracked file sample",
  `${fence}text`,
  trackedFiles.join("\n"),
  fence,
  "",
].join("\n");

const outFile = path.join(outDir, "doc-context.md");
writeFileSync(outFile, content);
console.log(`Wrote ${outFile}`);

这份文件不是给读者看的,而是给Claude Code看的工作材料。把它放在docs/_generated下面,可以避免它和正式文档混在一起。正式文档应该面向读者,生成上下文只负责给AI提供证据。

第二步:用skill固定文档更新规则

Claude Code的skill适合保存重复流程。官方skills和slash commands文档说明,SKILL.md可以创建像/docs-refresh这样的命令,旧的.claude/commands/仍然可用。文档更新每次都要检查同样的内容,所以很适合做成skill。

创建.claude/skills/docs-refresh/SKILL.md

---
description: Refresh README, OpenAPI, ADR, and changelog from current code and generated documentation context.
---

## Inputs to inspect first

- Repository context: @docs/_generated/doc-context.md
- README: @README.md
- Existing docs: @docs
- Package metadata: @package.json

## Task

Update documentation only where the current code, package scripts, or git diff prove that the text is stale.

Required outputs:

1. README: setup steps, commands, environment notes, and troubleshooting.
2. OpenAPI: update `docs/api/openapi.yaml` when API routes changed.
3. ADR: create `docs/adr/NNNN-short-title.md` when a new architectural decision is visible.
4. CHANGELOG: add a draft entry under `Unreleased` when user-facing behavior changed.

Rules:

- Do not invent endpoints, commands, environment variables, prices, or dates.
- If evidence is missing, write a short "needs confirmation" note instead of guessing.
- Keep secret names generic. Never copy `.env` values into docs.
- After editing, run `node scripts/verify-docs.mjs` and report the result.

这里最重要的是“documentation only”。Claude Code可以修改代码,如果任务只是刷新文档,就必须明确限制范围。团队使用时,还应该参考Claude Code权限设置指南,决定哪些命令可以运行,哪些文件绝对不能读取。

实例一:把README改成新人入门手册

README不只是项目介绍。对新成员来说,它应该回答五个问题:如何安装、如何启动、如何测试、常见错误怎么处理、下一步读什么。让Claude Code更新README时,要指定读者是“刚clone仓库的人”。

# Task API

## Getting started

```bash
npm ci
npm run dev
```

## Commands

| Command | Purpose |
| --- | --- |
| `npm run dev` | Start the local server |
| `npm run test` | Run unit tests |
| `npm run docs:context` | Generate documentation context |
| `npm run docs:verify` | Verify documentation files |

## Troubleshooting

- If install fails, delete `node_modules` and run `npm ci` again.
- If API examples fail, confirm the server is running on the documented port.
- If generated docs mention unknown env vars, check `.env.example`, not `.env`.

这个例子短,但每一行都能执行或帮助排错。相比“写得更清楚”,给Claude Code一个具体读者会得到更实用的结果。

实例二:API规格用OpenAPI保存

API说明如果只写自然语言,很难验证。对于有接口的项目,让Claude Code根据路由文件、验证schema和测试更新docs/api/openapi.yaml。不要追求一次写完整,先保证已有内容真实。

openapi: 3.1.0
info:
  title: Task API
  version: 0.1.0
  description: API for creating and listing tasks.
paths:
  /api/tasks:
    get:
      summary: List tasks
      responses:
        "200":
          description: Task list
          content:
            application/json:
              schema:
                type: object
                properties:
                  tasks:
                    type: array
                    items:
                      type: object
                      required: [id, title, status]
                      properties:
                        id:
                          type: string
                        title:
                          type: string
                        status:
                          type: string
                          enum: [todo, doing, done]
    post:
      summary: Create a task
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [title]
              properties:
                title:
                  type: string
      responses:
        "201":
          description: Created task

这里的陷阱是“补全过度”。如果真实接口返回open,文档却写todo,文档就会误导使用者。没有证据的字段应放进“needs confirmation”,不要硬写成确定结论。

实例三:用ADR保留设计理由

ADR的价值不是记录“我改了什么”,而是记录“为什么这样设计”。例如把生成上下文放在docs/_generated,就是一个需要说明的团队约定。

# ADR-0001: Keep generated documentation under docs/_generated

## Status

Accepted

## Context

Claude Code needs a compact summary of the repository before refreshing documentation.
Putting generated context beside hand-written docs can confuse reviewers.

## Decision

Generated context files will be written to `docs/_generated/`.
Hand-written documentation stays in `docs/api`, `docs/adr`, and root README files.

## Consequences

- Reviewers can separate generated context from authored documentation.
- The context file can be regenerated before each documentation refresh.
- The verification script must ignore unstable generated content when checking prose quality.

Claude Code可以起草ADR,但最终理由要由人确认。否则ADR会变成AI编出的解释,看似合理,却不能代表团队共识。

实例四:从差异生成CHANGELOG草稿

CHANGELOG最好在发布前就写草稿,而不是发布当天凭记忆补。让Claude Code把变化放到Unreleased下,不要让它提前确定版本号或日期。

# Changelog

## Unreleased

### Added

- Added documentation context generation for README, API specs, ADRs, and changelog updates.

### Changed

- Updated the documentation refresh workflow to verify generated files before publication.

### Needs confirmation

- Confirm the final release version and release date before moving this entry out of `Unreleased`.

这种写法保留了信息,也不会假装版本已经发布。

第三步:用脚本验证生成结果

把下面的脚本保存为scripts/verify-docs.mjs。它会检查必需文件、必需短语、ADR标题、Markdown代码围栏和明显的未完成占位符。

import { existsSync, readdirSync, readFileSync } from "node:fs";
import path from "node:path";

const requiredFiles = [
  {
    file: "README.md",
    phrases: ["## Getting started", "## Commands"],
  },
  {
    file: "docs/api/openapi.yaml",
    phrases: ["openapi: 3.", "paths:"],
  },
  {
    file: "CHANGELOG.md",
    phrases: ["## Unreleased"],
  },
];

function read(file) {
  return readFileSync(file, "utf8");
}

function listMarkdownFiles(dir) {
  if (!existsSync(dir)) return [];
  return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
    const fullPath = path.join(dir, entry.name);
    if (entry.isDirectory()) return listMarkdownFiles(fullPath);
    return entry.isFile() && /\.(md|mdx)$/.test(entry.name) ? [fullPath] : [];
  });
}

const errors = [];

for (const item of requiredFiles) {
  if (!existsSync(item.file)) {
    errors.push(`${item.file}: missing file`);
    continue;
  }

  const source = read(item.file);
  for (const phrase of item.phrases) {
    if (!source.includes(phrase)) {
      errors.push(`${item.file}: missing phrase "${phrase}"`);
    }
  }
}

for (const adr of listMarkdownFiles("docs/adr")) {
  const source = read(adr);
  for (const heading of ["## Status", "## Context", "## Decision", "## Consequences"]) {
    if (!source.includes(heading)) {
      errors.push(`${adr}: missing ${heading}`);
    }
  }
}

for (const file of ["README.md", "CHANGELOG.md", ...listMarkdownFiles("docs")]) {
  if (!existsSync(file)) continue;
  const source = read(file);
  const fenceMarker = String.fromCharCode(96, 96, 96);
  const fenceCount = (source.match(new RegExp(fenceMarker, "g")) ?? []).length;
  if (fenceCount % 2 !== 0) errors.push(`${file}: unbalanced code fence`);
  if (/TODO|TBD|REPLACE_ME/.test(source)) {
    errors.push(`${file}: unresolved placeholder remains`);
  }
}

if (errors.length > 0) {
  console.error("Documentation verification failed:");
  for (const error of errors) console.error(`- ${error}`);
  process.exit(1);
}

console.log("Documentation verification passed.");

package.json里加上命令:

{
  "scripts": {
    "docs:context": "node scripts/doc-context.mjs",
    "docs:verify": "node scripts/verify-docs.mjs"
  }
}

团队使用时,可以根据官方settings documentation把项目设置和个人设置分开。下面的例子允许文档检查脚本,拒绝读取秘密文件。

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "allow": [
      "Bash(node scripts/doc-context.mjs)",
      "Bash(node scripts/verify-docs.mjs)",
      "Read(README.md)",
      "Read(docs/**)",
      "Read(src/**)"
    ],
    "deny": [
      "Read(.env)",
      "Read(.env.*)",
      "Read(secrets/**)",
      "Bash(curl *)"
    ]
  }
}

如果以后要在文件修改后自动验证,可以再阅读官方hooks reference。建议先手动运行,确认哪些失败真的应该阻止发布,再加入hooks。

常见失败和避免方法

第一种失败是写出不存在的命令。很多项目有npm run build,但你的项目可能用pnpm buildmake build。README里的命令必须来自package.json、Makefile或CI配置。

第二种失败是API规格过度补全。结构漂亮不等于正确。公开前要和测试、真实响应或后端代码对照。

第三种失败是泄露秘密信息。.env、客户名、内部URL和令牌都不应该进入文档。用.env.example说明变量,用权限设置禁止读取真实秘密。

第四种失败是把内部上下文当作公开文档。docs/_generated/doc-context.md只是给AI看的材料,不应该直接面向读者。

第五种失败是把验证当作最后一分钟的形式流程。越早运行docs:contextdocs:verify,越容易修复文档偏差。

模板、教材和咨询入口

个人项目可以先用本文的两个Node脚本和docs-refresh skill开始。如果想让Claude Code长期记住项目规则,可以继续阅读CLAUDE.md最佳实践,把构建命令、文档规则和审查标准写进项目说明。

如果你只是想把常用命令放在手边,可以从免费Claude Code速查表开始。每周都要重复README、调试、审查和文档提示词时,再考虑Gumroad模板包。

如果是团队导入,难点通常不是生成一篇README,而是谁负责最终确认、哪些文档会阻止发布、Gumroad和咨询CTA如何检查。这类规则适合通过导入咨询按真实仓库来设计。

我实际试用后的结果

我用这个流程先生成doc-context.md,再把README、OpenAPI、ADR和CHANGELOG拆成不同责任交给Claude Code处理。和直接说“帮我写README”相比,虚构命令明显减少,ADR也更容易保留“为什么”。不过API规格仍然需要人工和真实响应对照。最可靠的结果不是完全自动化,而是快速生成、证据清楚、验证步骤明确的文档流程。

#Claude Code #documentation #JSDoc #API specs #auto-generation
免费

免费 PDF: Claude Code 速查表

输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。

我们会妥善保护你的信息,不发送垃圾邮件。

把 Claude Code 变成真正能带来结果的工作流

先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。

Masa

关于作者

Masa

专注 Claude Code 实务流程、团队导入和内容转化的工程师。