Skip to content

Commit 4458cca

Browse files
committed
feat: add docs watch run report output
1 parent da75f8e commit 4458cca

4 files changed

Lines changed: 216 additions & 5 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,12 @@ Local commands:
114114
- `npm run sync:github-docs-state` (seed or refresh private baseline snapshots)
115115
- `npm run check:github-docs`
116116
- `npm run diff:github-docs`
117-
- `npm run docs-watch:local` (check + readable diff)
117+
- `npm run report:github-docs`
118+
- `npm run docs-watch:local` (check + readable diff + run report)
118119

119-
Local outputs default to `.tmp/docs-watch/`.
120+
Local outputs default to `.tmp/docs-watch/`, including `.tmp/docs-watch/docs-watch-report.md`.
120121

121-
Recommended weekly automation: run `npm run docs-watch:local`, then have Codex review `.tmp/docs-watch/docs-diff.md`, classify impact, and open a draft fix PR when project changes are required.
122+
Recommended weekly automation: run `npm run docs-watch:local`, review `.tmp/docs-watch/docs-watch-report.md` for correctness, then have Codex classify impact and open a draft fix PR when project changes are required.
122123

123124

124125
## Disclaimer

docs/github-documentation/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Example local state location:
2222
1. (Optional, first run) `npm run sync:github-docs-state`
2323
2. `npm run check:github-docs`
2424
3. `npm run diff:github-docs`
25-
4. Ask Codex to review `.tmp/docs-watch/docs-diff.md` and classify project impact.
25+
4. `npm run report:github-docs`
26+
5. Ask Codex to review `.tmp/docs-watch/docs-watch-report.md` and classify project impact.
2627

2728
Outputs are written under `.tmp/docs-watch/` by default.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
"generate:self-test": "tsx scripts/generate-self-test.ts",
2626
"check:github-docs": "node scripts/check-github-docs.mjs --state-dir \"${DOC_WATCH_STATE_DIR:-.tmp/docs-watch/state}\" --remote-snapshots-dir .tmp/docs-watch/remote --output .tmp/docs-watch/docs-check.json --write-summary",
2727
"diff:github-docs": "node scripts/render-github-doc-diff.mjs --check .tmp/docs-watch/docs-check.json --output .tmp/docs-watch/docs-diff.md --patch-output .tmp/docs-watch/docs-diff.patch --write-summary",
28-
"docs-watch:local": "npm run check:github-docs && npm run diff:github-docs",
28+
"report:github-docs": "node scripts/write-docs-watch-report.mjs --check .tmp/docs-watch/docs-check.json --diff .tmp/docs-watch/docs-diff.patch --diff-markdown .tmp/docs-watch/docs-diff.md --output .tmp/docs-watch/docs-watch-report.md --write-summary",
29+
"docs-watch:local": "npm run check:github-docs && npm run diff:github-docs && npm run report:github-docs",
2930
"sync:github-docs-state": "node scripts/check-github-docs.mjs --state-dir \"${DOC_WATCH_STATE_DIR:-.tmp/docs-watch/state}\" --write-state --update-frontmatter --output .tmp/docs-watch/docs-check-sync.json --write-summary",
3031
"clean": "rm -rf dist",
3132
"prepare": "husky",
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
#!/usr/bin/env node
2+
3+
import fs from 'fs';
4+
import path from 'path';
5+
6+
const args = process.argv.slice(2);
7+
8+
function getArgValue(flag, fallback = null) {
9+
const idx = args.indexOf(flag);
10+
if (idx === -1) return fallback;
11+
const value = args[idx + 1];
12+
return value ?? fallback;
13+
}
14+
15+
function ensureParent(filePath) {
16+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
17+
}
18+
19+
function shortHash(value) {
20+
if (!value) return 'missing';
21+
const text = String(value);
22+
return text.length <= 12 ? text : text.slice(0, 12);
23+
}
24+
25+
function parsePatchStats(patchText) {
26+
const stats = new Map();
27+
28+
if (!patchText.trim()) {
29+
return stats;
30+
}
31+
32+
let currentFile = null;
33+
for (const line of patchText.split('\n')) {
34+
if (line.startsWith('diff --git ')) {
35+
const match = line.match(/^diff --git a\/(.+?) b\//);
36+
currentFile = match ? match[1] : null;
37+
if (currentFile && !stats.has(currentFile)) {
38+
stats.set(currentFile, {
39+
file: currentFile,
40+
additions: 0,
41+
deletions: 0,
42+
hunks: 0,
43+
});
44+
}
45+
continue;
46+
}
47+
48+
if (!currentFile) continue;
49+
50+
const current = stats.get(currentFile);
51+
if (!current) continue;
52+
53+
if (line.startsWith('@@')) {
54+
current.hunks += 1;
55+
continue;
56+
}
57+
58+
if (line.startsWith('+') && !line.startsWith('+++')) {
59+
current.additions += 1;
60+
continue;
61+
}
62+
63+
if (line.startsWith('-') && !line.startsWith('---')) {
64+
current.deletions += 1;
65+
}
66+
}
67+
68+
return stats;
69+
}
70+
71+
function defaultAssessment(payload) {
72+
if (!payload.changed) {
73+
return {
74+
status: 'completed',
75+
impactLevel: 'none',
76+
requiresProjectChanges: 'false',
77+
confidence: '0.95',
78+
rationale: 'No monitored documentation changes were detected in this run.',
79+
recommendedAction: 'No action required.',
80+
};
81+
}
82+
83+
return {
84+
status: 'pending-codex-review',
85+
impactLevel: 'pending',
86+
requiresProjectChanges: 'unknown',
87+
confidence: 'n/a',
88+
rationale:
89+
'Changes were detected. Review the diff report and classify whether project code or docs need updates.',
90+
recommendedAction:
91+
'Review `.tmp/docs-watch/docs-diff.md`, classify impact, and open/update a draft PR if changes are required.',
92+
};
93+
}
94+
95+
function escapePipe(value) {
96+
return String(value ?? '').replace(/\|/g, '\\|');
97+
}
98+
99+
async function main() {
100+
const checkPath = getArgValue('--check', '.tmp/docs-watch/docs-check.json');
101+
const diffPath = getArgValue('--diff', '.tmp/docs-watch/docs-diff.patch');
102+
const diffMarkdownPath = getArgValue('--diff-markdown', '.tmp/docs-watch/docs-diff.md');
103+
const outputPath = getArgValue('--output', '.tmp/docs-watch/docs-watch-report.md');
104+
const writeSummary = args.includes('--write-summary');
105+
106+
const checkPayload = JSON.parse(fs.readFileSync(checkPath, 'utf8'));
107+
const changed = checkPayload.results.filter((item) => item.changed);
108+
const patchText = fs.existsSync(diffPath) ? fs.readFileSync(diffPath, 'utf8') : '';
109+
const patchStats = parsePatchStats(patchText);
110+
111+
const assessment = defaultAssessment(checkPayload);
112+
113+
const lines = [];
114+
lines.push('# Docs Watch Run Report');
115+
lines.push('');
116+
lines.push(`- generated_at: ${new Date().toISOString()}`);
117+
lines.push(`- check_file: ${checkPath}`);
118+
lines.push(`- diff_patch_file: ${diffPath}`);
119+
lines.push(`- diff_markdown_file: ${diffMarkdownPath}`);
120+
lines.push(`- changed: ${checkPayload.changed}`);
121+
lines.push(`- changed_count: ${checkPayload.changed_count}`);
122+
lines.push(`- state_dir: ${checkPayload.state_dir ?? 'none'}`);
123+
lines.push('');
124+
125+
lines.push('## Monitored Files');
126+
lines.push('');
127+
lines.push('| File | Changed | Baseline | Expected | Actual | First Diff Line |');
128+
lines.push('| --- | --- | --- | --- | --- | --- |');
129+
130+
for (const item of checkPayload.results) {
131+
const diffLine = item.diff?.line ?? '-';
132+
lines.push(
133+
`| ${escapePipe(item.file)} | ${item.changed} | ${escapePipe(item.baseline_source ?? 'unknown')} | ${shortHash(item.expected_hash)} | ${shortHash(item.actual_hash)} | ${diffLine} |`,
134+
);
135+
}
136+
lines.push('');
137+
138+
lines.push('## Diff Stats');
139+
lines.push('');
140+
141+
if (changed.length === 0) {
142+
lines.push('(no changed files)');
143+
} else {
144+
lines.push('| File | Hunks | Additions | Deletions |');
145+
lines.push('| --- | ---: | ---: | ---: |');
146+
147+
for (const item of changed) {
148+
const stats = patchStats.get(item.file) ?? {
149+
hunks: 0,
150+
additions: 0,
151+
deletions: 0,
152+
};
153+
154+
lines.push(
155+
`| ${escapePipe(item.file)} | ${stats.hunks} | ${stats.additions} | ${stats.deletions} |`,
156+
);
157+
}
158+
}
159+
lines.push('');
160+
161+
lines.push('## Codex Assessment');
162+
lines.push('');
163+
lines.push(`- status: ${assessment.status}`);
164+
lines.push(`- impact_level: ${assessment.impactLevel}`);
165+
lines.push(`- requires_project_changes: ${assessment.requiresProjectChanges}`);
166+
lines.push(`- confidence: ${assessment.confidence}`);
167+
lines.push('');
168+
lines.push('### Rationale');
169+
lines.push('');
170+
lines.push(assessment.rationale);
171+
lines.push('');
172+
lines.push('### Recommended Action');
173+
lines.push('');
174+
lines.push(assessment.recommendedAction);
175+
lines.push('');
176+
177+
ensureParent(outputPath);
178+
fs.writeFileSync(outputPath, lines.join('\n'), 'utf8');
179+
180+
if (process.env.GITHUB_OUTPUT) {
181+
fs.appendFileSync(process.env.GITHUB_OUTPUT, `report_path=${path.resolve(outputPath)}\n`);
182+
fs.appendFileSync(process.env.GITHUB_OUTPUT, `changed=${checkPayload.changed}\n`);
183+
fs.appendFileSync(process.env.GITHUB_OUTPUT, `changed_count=${checkPayload.changed_count}\n`);
184+
fs.appendFileSync(process.env.GITHUB_OUTPUT, `assessment_status=${assessment.status}\n`);
185+
}
186+
187+
if (writeSummary) {
188+
const summaryLines = [
189+
'# Docs watch report summary',
190+
'',
191+
`- report: ${outputPath}`,
192+
`- changed: ${checkPayload.changed}`,
193+
`- changed_count: ${checkPayload.changed_count}`,
194+
`- assessment_status: ${assessment.status}`,
195+
];
196+
197+
if (process.env.GITHUB_STEP_SUMMARY) {
198+
fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, summaryLines.join('\n') + '\n');
199+
} else {
200+
console.log(summaryLines.join('\n'));
201+
}
202+
}
203+
}
204+
205+
main().catch((error) => {
206+
console.error(error);
207+
process.exit(1);
208+
});

0 commit comments

Comments
 (0)