Smart Docs Monitor #169
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | |
| } | |
| } |