Skip to content

Commit 6c65061

Browse files
authored
Merge pull request #61 from netfoundry/upload-doc-skill
add doc-check skill
2 parents c114c5d + e2b33a2 commit 6c65061

1 file changed

Lines changed: 317 additions & 0 deletions

File tree

skills/doc-check/SKILL.md

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
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

Comments
 (0)