|
| 1 | +--- |
| 2 | +name: doc-check |
| 3 | +description: Check merged PRs in a repo for customer-facing changes, cross-reference against existing docs, and flag what is missing, stale, or already covered |
| 4 | +--- |
| 5 | + |
| 6 | +Check merged PRs in a product's source repo(s) for customer-facing changes. For each flagged PR, search the local doc |
| 7 | +directory to assess whether coverage already exists, is stale, or is missing entirely. Produce an actionable report, |
| 8 | +then optionally generate a doc draft. |
| 9 | + |
| 10 | +## Setup |
| 11 | + |
| 12 | +Before using this skill, configure it for your organization: |
| 13 | + |
| 14 | +1. **Update the product registry** below with your products, source repos, auth method, and local doc paths. |
| 15 | +2. **Set auth environment variables** as needed (see Auth section below). |
| 16 | +3. **Update the style guide reference** in step 7 to point to your own documentation style guide. |
| 17 | + |
| 18 | +## Invocation |
| 19 | + |
| 20 | +``` |
| 21 | +/doc-check status |
| 22 | +/doc-check <product> |
| 23 | +/doc-check <product> --since YYYY-MM-DD |
| 24 | +/doc-check <product> --draft <owner/repo#PR> |
| 25 | +``` |
| 26 | + |
| 27 | +- `status` — show a summary table of all products and their last scan results, then exit. Does not fetch any PRs or |
| 28 | + update state. Regenerates `STATUS.md` from existing output files. |
| 29 | +- `<product>` is a product name defined in your registry below |
| 30 | +- `--since` overrides the last-checked date from state |
| 31 | +- `--draft <owner/repo#PR>` skips the scan and generates a doc draft for a specific PR |
| 32 | + |
| 33 | +## Product registry |
| 34 | + |
| 35 | +Each product has one or more **source repos** (scanned for PRs) and one **local doc path** (searched for coverage). |
| 36 | +Replace the example rows below with your own products. |
| 37 | + |
| 38 | +| Product | Source repos | Auth | Local doc path | |
| 39 | +|------------|-----------------------|--------------|---------------------------------------| |
| 40 | +| product-a | your-org/repo-a | gh CLI | /path/to/product-a/docs | |
| 41 | +| product-b | your-org/repo-b | GH_TOKEN | /path/to/product-b/docs | |
| 42 | +| product-c | your-org/repo-c | BB_EMAIL+BB_TOKEN | /path/to/product-c/docs | |
| 43 | + |
| 44 | +**Note on source repos vs. doc repos**: The source repos are the *product code* repositories — that's where |
| 45 | +customer-facing changes originate. The local doc path is a separate, locally cloned *documentation* repository used |
| 46 | +only for coverage searching (grep). You do not need the product code repos cloned locally; diffs are fetched via API. |
| 47 | + |
| 48 | +**Bitbucket auth**: `BB_TOKEN` is an Atlassian account-level API token (generated at |
| 49 | +id.atlassian.com/manage-profile/security/api-tokens). It covers all Bitbucket repos the account has access to. |
| 50 | +Use with `BB_EMAIL` (the account's email address) as basic auth credentials. |
| 51 | + |
| 52 | +**GitHub auth**: Use the `gh` CLI. Run `gh auth status` to confirm it's authenticated before running a scan. |
| 53 | +If `gh` is not available or the user explicitly requests token-based fallback, use a personal access token via |
| 54 | +`GH_TOKEN` env variable in curl — but prefer `gh`. For private repos that require a specific token, set the |
| 55 | +token variable name in the Auth column of your registry and use `GH_TOKEN=<your-token-variable>` when calling `gh`. |
| 56 | + |
| 57 | +## State file |
| 58 | + |
| 59 | +State is stored at `~/.claude/doc-check-state.json`. One entry per **product** (not per source repo). Format: |
| 60 | + |
| 61 | +```json |
| 62 | +{ |
| 63 | + "product-a": { "last_checked": "2026-03-20T15:00:00Z" }, |
| 64 | + "product-b": { "last_checked": "2026-03-25T17:00:00Z" } |
| 65 | +} |
| 66 | +``` |
| 67 | + |
| 68 | +Read this file at the start of each run. Update it with the current timestamp after completing a scan. |
| 69 | + |
| 70 | +## Step-by-step instructions |
| 71 | + |
| 72 | +### 1. Determine the since date |
| 73 | + |
| 74 | +- If `--since` was passed, use that date as the cutoff for all source repos |
| 75 | +- Otherwise read `last_checked` from the state file for the given product |
| 76 | +- **If no state exists** (first run): do not use a date cutoff — instead fetch the last 5 PRs per source repo |
| 77 | + (see Step 2). This avoids pulling unbounded history on first use. |
| 78 | +- **Hard cap: never analyze more than 10 PRs in a single run** (across all source repos combined). If more are found, |
| 79 | + process the 10 most recent and warn the user, listing the skipped PR identifiers and suggesting `--since` to work |
| 80 | + backwards in chunks. |
| 81 | + |
| 82 | +### 2. Fetch merged PRs |
| 83 | + |
| 84 | +**Before fetching, check for a same-day re-run:** If `last_checked` exists in the state file and its date matches |
| 85 | +today's date (ignore time), and `--since` was not passed, stop immediately and tell the user: |
| 86 | + |
| 87 | +> Already ran today (last checked: `<timestamp>`). Use `--since YYYY-MM-DD` to re-scan from an earlier date, or |
| 88 | +> just run it again tomorrow. |
| 89 | +
|
| 90 | +Do not fetch PRs, write a file, or update state. |
| 91 | + |
| 92 | +**For each source repo in the product**, fetch merged PRs: |
| 93 | + |
| 94 | +**GitHub repos:** |
| 95 | + |
| 96 | +```bash |
| 97 | +# Date-based (last_checked exists or --since passed): |
| 98 | +gh pr list --repo <owner>/<repo> --state merged --limit 50 \ |
| 99 | + --json number,title,mergedAt,body |
| 100 | + |
| 101 | +# First run (no last_checked): fetch last 5 only |
| 102 | +gh pr list --repo <owner>/<repo> --state merged --limit 5 \ |
| 103 | + --json number,title,mergedAt,body |
| 104 | +``` |
| 105 | + |
| 106 | +Filter results in Python to PRs where `mergedAt` is after the since date (when date-based). |
| 107 | + |
| 108 | +**Bitbucket repos:** |
| 109 | + |
| 110 | +```bash |
| 111 | +# Date-based: |
| 112 | +curl -s -u "$BB_EMAIL:$BB_TOKEN" \ |
| 113 | + "https://api.bitbucket.org/2.0/repositories/<your-org>/<slug>/pullrequests?state=MERGED&sort=-updated_on&pagelen=50" |
| 114 | + |
| 115 | +# First run: fetch last 5 only |
| 116 | +curl -s -u "$BB_EMAIL:$BB_TOKEN" \ |
| 117 | + "https://api.bitbucket.org/2.0/repositories/<your-org>/<slug>/pullrequests?state=MERGED&sort=-updated_on&pagelen=5" |
| 118 | +``` |
| 119 | + |
| 120 | +Filter to PRs where `updated_on` is after the since date (when date-based). |
| 121 | + |
| 122 | +**When a product has multiple source repos**, collect all results into one list, then apply the 10-PR hard cap to the |
| 123 | +combined total (most recent first across repos). In the report, prefix each PR with its source repo: |
| 124 | +`your-org/repo-a#42`, `your-org/repo-b#17`, etc. |
| 125 | + |
| 126 | +**Monorepos**: If a source repo hosts multiple products, read the PR title, description, and diff to determine which |
| 127 | +product it belongs to before assessing doc coverage. Mark PRs for other products as skipped with a note. |
| 128 | + |
| 129 | +### 3. Assess each PR: customer-facing or internal? |
| 130 | + |
| 131 | +Fetch the diff for each PR: |
| 132 | + |
| 133 | +**GitHub:** |
| 134 | + |
| 135 | +```bash |
| 136 | +gh pr diff <number> --repo <owner>/<repo> |
| 137 | +``` |
| 138 | + |
| 139 | +**Bitbucket:** |
| 140 | + |
| 141 | +The simple `/pullrequests/<id>/diff` endpoint returns empty results. Batch-extract commit hashes from the PR list |
| 142 | +response (fields `source.commit.hash` and `destination.commit.hash`) and construct the diff URL as: |
| 143 | + |
| 144 | +```bash |
| 145 | +curl -s -u "$BB_EMAIL:$BB_TOKEN" \ |
| 146 | + "https://api.bitbucket.org/2.0/repositories/<your-org>/<slug>/diff/<your-org>/<slug>:<src_hash>%0D<dst_hash>?from_pullrequest_id=<id>&topic=true" |
| 147 | +``` |
| 148 | + |
| 149 | +Mark as **customer-facing** (proceed to step 4) if the diff contains: |
| 150 | + |
| 151 | +- New or changed CLI flags, commands, or subcommands |
| 152 | +- New or changed API endpoints, request/response shapes |
| 153 | +- New or changed config file options or environment variables |
| 154 | +- New UI flows, screens, or settings |
| 155 | +- Changed default behaviors |
| 156 | +- New features called out in the PR title or description |
| 157 | + |
| 158 | +Mark as **internal** (skip, add to skipped list) if changes are limited to: |
| 159 | + |
| 160 | +- Tests only |
| 161 | +- CI/CD or build configuration |
| 162 | +- Dependency bumps with no behavior change |
| 163 | +- Internal refactors with no user-visible effect |
| 164 | +- The PR itself is documentation work (changes only to doc files) |
| 165 | + |
| 166 | +### 4. Cross-reference flagged PRs against existing docs |
| 167 | + |
| 168 | +For each customer-facing PR, search the local doc path for the product (see registry above) to determine whether |
| 169 | +coverage already exists. Use grep and file reads — do not guess. |
| 170 | + |
| 171 | +Extract 2–4 key terms from the PR (feature name, CLI flag, config key, endpoint name, etc.) and search for them: |
| 172 | + |
| 173 | +```bash |
| 174 | +grep -r "<term>" <local-doc-path> --include="*.md" --include="*.mdx" -l |
| 175 | +``` |
| 176 | + |
| 177 | +Based on what you find, assign one of three statuses: |
| 178 | + |
| 179 | +- **Missing** — no relevant doc content found. New doc or section needed. |
| 180 | +- **Stale** — relevant doc found but it predates this change or describes the old behavior. Existing doc needs updating. |
| 181 | +- **Covered** — existing doc already accurately describes the feature. No action needed. |
| 182 | + |
| 183 | +Use judgement: a doc that mentions a feature name but doesn't cover the new flag or behavior is **stale**, not covered. |
| 184 | +If you're uncertain, lean toward **stale** rather than **covered** — it's better to flag a false positive than miss a gap. |
| 185 | + |
| 186 | +Dismiss **covered** PRs from the flagged list entirely (move them to a "no action needed" section). |
| 187 | + |
| 188 | +### 5. Output the report |
| 189 | + |
| 190 | +``` |
| 191 | +## doc-check: product-a (since 2026-03-18) |
| 192 | +
|
| 193 | +Sources: your-org/repo-a (6 PRs), your-org/repo-b (4 PRs) — 10 total · 2 need doc work · 1 already covered · 7 skipped (internal) |
| 194 | +
|
| 195 | +### Needs doc work |
| 196 | +
|
| 197 | +**your-org/repo-a#42 — Add --output flag to CLI** (merged 2026-03-20) · MISSING |
| 198 | +- New `--output json|yaml|table` flag on all list subcommands |
| 199 | +- No existing doc found for output formatting options |
| 200 | +- Suggested location: reference/cli section |
| 201 | +- `/doc-check product-a --draft your-org/repo-a#42` to generate a draft |
| 202 | +
|
| 203 | +**your-org/repo-b#17 — Dark mode toggle** (merged 2026-03-19) · STALE |
| 204 | +- New dark/light mode toggle in settings panel |
| 205 | +- Existing doc: `reference/ui-overview.md` — covers UI layout but not appearance settings |
| 206 | +- `/doc-check product-a --draft your-org/repo-b#17` to generate a draft |
| 207 | +
|
| 208 | +### Already covered |
| 209 | +- your-org/repo-a#38 — TLS cert rotation: `operations/certificates.md` covers this area and appears current |
| 210 | +
|
| 211 | +### Skipped (internal) |
| 212 | +- your-org/repo-a#40 — Fix race condition in reconnect logic (internal bug fix) |
| 213 | +- your-org/repo-a#39 — Go 1.22 bump (build tooling) |
| 214 | +- your-org/repo-b#15 — Cypress test updates (tests only) |
| 215 | +``` |
| 216 | + |
| 217 | +### 6. Offer to draft |
| 218 | + |
| 219 | +After the report, prompt: |
| 220 | +> Run `/doc-check <product> --draft <owner/repo#PR>` for any flagged PR, or tell me which one to draft. |
| 221 | +
|
| 222 | +### 7. Generating a draft (`--draft` mode) |
| 223 | + |
| 224 | +When `--draft <owner/repo#PR>` is passed: |
| 225 | + |
| 226 | +1. Fetch the PR title, description, and full diff from the specified repo |
| 227 | +2. If the status was **stale**, read the existing doc file first — the draft should update it, not replace it |
| 228 | +3. If the status was **missing**, write a new file from scratch |
| 229 | +4. Identify what changed from a user perspective |
| 230 | +5. Determine the appropriate doc type (how-to, reference, concept explanation) using Diátaxis |
| 231 | +6. Write the draft following your organization's documentation style guide: |
| 232 | + - Sentence-style headers; imperative verb phrases for how-to titles |
| 233 | + - Active voice, second person ("you/your") |
| 234 | + - Backticks for CLI flags, commands, config keys, code tokens |
| 235 | + - 120-character line length limit |
| 236 | +7. Present the full draft inline in the terminal, followed by the suggested file path, then prompt the user with these |
| 237 | + options: |
| 238 | + |
| 239 | + > **What would you like to do?** |
| 240 | + > - **Execute** — write the draft directly to the suggested file path in the doc repo |
| 241 | + > - **Edit** — describe changes and I'll update the draft before writing |
| 242 | + > - **Save to drafts** — save to `output/<product>/drafts/<repo>-<PR>-<slug>.md` for later without touching the doc repo |
| 243 | +
|
| 244 | + **Do not call Edit or Write on any doc repo file until the user selects "Execute".** Saving to drafts is always safe |
| 245 | + and does not require confirmation. |
| 246 | + |
| 247 | +#### Drafts folder |
| 248 | + |
| 249 | +Drafts are saved to `output/<product>/drafts/` under the doc-check skill folder: |
| 250 | + |
| 251 | +``` |
| 252 | +<skills-dir>/doc-check/output/<product>/drafts/<repo>-<PR>-<slug>.md |
| 253 | +``` |
| 254 | + |
| 255 | +Example: `output/product-a/drafts/repo-a-42-add-output-flag.md` |
| 256 | + |
| 257 | +Each draft file includes a frontmatter block for traceability: |
| 258 | + |
| 259 | +```markdown |
| 260 | +--- |
| 261 | +pr: <owner/repo#PR> |
| 262 | +status: MISSING | STALE |
| 263 | +suggested_path: <doc-repo-relative path where the file should land> |
| 264 | +--- |
| 265 | +``` |
| 266 | + |
| 267 | +### 8. Update state |
| 268 | + |
| 269 | +After completing a scan (not `--draft` mode), write the current ISO timestamp to `~/.claude/doc-check-state.json` |
| 270 | +for the product that was checked (one entry per product, shared across all its source repos). |
| 271 | + |
| 272 | +### 9. Save output |
| 273 | + |
| 274 | +After completing a scan (not `--draft` mode), save the full report to a dated file in a per-product subdirectory |
| 275 | +under `<skills-dir>/doc-check/output/<product>/`. Create the directory if it doesn't exist. |
| 276 | + |
| 277 | +Filename format: `YYYY-MM-DD.md` (use today's date). If a file with that name already exists, append a counter: |
| 278 | +`YYYY-MM-DD-2.md`, etc. |
| 279 | + |
| 280 | +Write the report in the same markdown format shown in step 5, with a one-line header added at the top: |
| 281 | + |
| 282 | +``` |
| 283 | +# doc-check: <product> — YYYY-MM-DD |
| 284 | +``` |
| 285 | + |
| 286 | +### 10. Update STATUS.md |
| 287 | + |
| 288 | +After saving the report, regenerate `STATUS.md` at the root of the doc-check skill folder: |
| 289 | + |
| 290 | +``` |
| 291 | +<skills-dir>/doc-check/STATUS.md |
| 292 | +``` |
| 293 | + |
| 294 | +To build it, scan the output directory for all products. For each product subdirectory, find the most recent dated |
| 295 | +report file and parse it for: |
| 296 | + |
| 297 | +- **Last checked**: the date from the filename (`YYYY-MM-DD`) |
| 298 | +- **Needs work**: count of PRs in the "Needs doc work" section |
| 299 | +- **Covered**: count of PRs in the "Already covered" section |
| 300 | +- **Skipped**: count of PRs in the "Skipped (internal)" section |
| 301 | + |
| 302 | +Produce a table sorted by last-checked date descending (most recently checked first), followed by any products in the |
| 303 | +registry that have no output yet (listed as "never"): |
| 304 | + |
| 305 | +```markdown |
| 306 | +# Doc coverage status |
| 307 | + |
| 308 | +Last updated: YYYY-MM-DD |
| 309 | + |
| 310 | +| Product | Last checked | Needs work | Covered | Skipped | |
| 311 | +|:----------|:-------------|:-----------|:--------|:--------| |
| 312 | +| product-a | 2026-04-06 | 2 | 1 | 5 | |
| 313 | +| product-b | 2026-04-01 | 0 | 3 | 8 | |
| 314 | +| product-c | never | — | — | — | |
| 315 | +``` |
| 316 | + |
| 317 | +Overwrite `STATUS.md` completely on each run — it is always regenerated from the output files, never manually edited. |
0 commit comments