Skip to content

Smart Docs Monitor #169

Smart Docs Monitor

Smart Docs Monitor #169

name: Smart Docs Monitor
on:
schedule:
- cron: '0 */6 * * *' # Run every 6 hours
workflow_dispatch:
inputs:
hours_back:
description: 'Hours to look back for merged PRs'
required: false
default: '6'
permissions:
contents: read
issues: write
id-token: write
jobs:
monitor:
runs-on: ubuntu-latest
steps:
- name: Checkout docs repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch merged PRs and create review batch
id: batch
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const repos = fs.readFileSync('repos-to-monitor.txt', 'utf8')
.split('\n')
.filter(line => line.trim() && !line.startsWith('#'));
const hoursBack = parseInt('${{ github.event.inputs.hours_back || '6' }}');
const since = new Date(Date.now() - hoursBack * 60 * 60 * 1000).toISOString();
let batchSummary = '# Merged PRs to Review\n\n';
let prCount = 0;
for (const repo of repos) {
const [owner, repoName] = repo.split('/');
try {
const { data: pulls } = await github.rest.pulls.list({
owner,
repo: repoName,
state: 'closed',
sort: 'updated',
direction: 'desc',
per_page: 30
});
const merged = pulls.filter(pr =>
pr.merged_at &&
new Date(pr.merged_at) > new Date(since)
);
if (merged.length > 0) {
batchSummary += `## ${repo}\n\n`;
for (const pr of merged) {
batchSummary += `### PR #${pr.number}: ${pr.title}\n`;
batchSummary += `- **URL:** ${pr.html_url}\n`;
batchSummary += `- **Merged:** ${pr.merged_at}\n`;
batchSummary += `- **Diff:** ${pr.diff_url}\n`;
if (pr.body) {
batchSummary += `- **Description:** ${pr.body.substring(0, 200)}...\n`;
}
batchSummary += '\n';
prCount++;
}
batchSummary += '\n';
}
} catch (error) {
console.log(`Error with ${repo}:`, error.message);
}
}
fs.writeFileSync('pr-batch.md', batchSummary);
console.log(`Found ${prCount} merged PRs`);
core.setOutput('pr_count', prCount);
return prCount;
- name: Analyze PRs with Claude
if: steps.batch.outputs.pr_count > 0
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
prompt: |
Review the merged PRs listed in `pr-batch.md` and determine which ones require documentation updates.
**Your Process:**
1. **Read pr-batch.md** to see all merged PRs
2. **For each PR:**
- Analyze the description and changes
- Determine if it affects user-facing features, APIs, or workflows
3. **Review our documentation:**
- Check /concepts directory for all conceptual documentation
- Check /guides-and-tutorials directory for tutorials and how-to guides
- Check /reference directory for API and reference documentation
- Read README.md
- Look for setup/configuration guides
4. **Output JSON for issues to create:**
For each PR needing documentation updates, create a JSON object with this structure:
{
"title": "[Docs Update] [repo-name] PR #123: Brief description",
"body": "## Source PR\n- Repository: [repo-name]\n- PR: #123 - [PR title]\n- URL: [PR URL]\n- Merged: [date]\n\n## What Changed\n[Clear summary of the functional changes]\n\n## Documentation Impact\n\n### Files Needing Updates\n- [ ] `/path/to/file1.md` - [What needs to change]\n- [ ] `/path/to/file2.md` - [What needs to change]\n\n## Recommended Changes\n\n### In `/path/to/file1.md`\n[Specific text to add/change with examples]\n\n## Priority\n[High/Medium/Low] - [Why]\n\n---\n*Auto-generated by docs sync monitor*",
"labels": ["documentation", "auto-generated", "sync-needed", "[repo-name]", "priority-[level]"]
}
**Save the output as a JSON array to a file called `issues-to-create.json`**
Example:
[
{
"title": "[Docs Update] stacks-core PR #456: Add new Clarity function",
"body": "## Source PR\n...",
"labels": ["documentation", "auto-generated", "sync-needed", "stacks-core", "priority-high"]
}
]
5. **Optimization:**
- Skip PRs with only internal refactoring, tests, or CI changes
- Group multiple PRs affecting the same doc section into one issue if appropriate
- Prioritize user-facing changes (API, features, setup)
- Be specific - don't create vague "update docs" issues
**Important:**
- Only include PRs that genuinely need documentation updates
- Save the JSON array to `issues-to-create.json`
- If no issues are needed, save an empty array: []
claude_args: |
--max-turns 40
--model claude-sonnet-4-20250514
- name: Create issues from Claude analysis
if: steps.batch.outputs.pr_count > 0
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
if (!fs.existsSync('issues-to-create.json')) {
console.log('No issues file found - Claude may not have found any PRs needing docs updates');
return;
}
const content = fs.readFileSync('issues-to-create.json', 'utf8');
console.log('Issues file content:', content);
const issues = JSON.parse(content);
if (!Array.isArray(issues) || issues.length === 0) {
console.log('No issues to create');
return;
}
console.log(`Creating ${issues.length} issue(s)...`);
for (const issue of issues) {
try {
const result = await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: issue.title,
body: issue.body,
labels: issue.labels || []
});
console.log(`✓ Created issue #${result.data.number}: ${issue.title}`);
} catch (error) {
console.error(`✗ Failed to create issue "${issue.title}":`, error.message);
}
}