Repository navigation
129 lines (110 loc) · 4.68 KB
/
Copy pathpr-ready.yml
File metadata and controls
129 lines (110 loc) · 4.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# Rewrite PR body with Claude (pr-ready skill) on ready-for-review label.
name: PR Ready Body
on:
pull_request:
types: [labeled]
permissions:
contents: read
pull-requests: write
concurrency:
group: pr-ready-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
rewrite-body:
if: >-
github.event.label.name == 'ready-for-review' &&
github.actor != 'dependabot[bot]' &&
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout (skill + PR template only)
uses: actions/checkout@v7
with:
sparse-checkout: |
.claude/skills/pr-ready/SKILL.md
.github/PULL_REQUEST_TEMPLATE.md
sparse-checkout-cone-mode: false
persist-credentials: false
- name: Rewrite PR body with Claude
uses: actions/github-script@v9
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
with:
script: |
const fs = require('fs');
const pr = context.payload.pull_request;
if (!process.env.ANTHROPIC_API_KEY) {
core.setFailed('ANTHROPIC_API_KEY secret is not set');
return;
}
const skill = fs.readFileSync('.claude/skills/pr-ready/SKILL.md', 'utf8');
const template = fs.readFileSync('.github/PULL_REQUEST_TEMPLATE.md', 'utf8');
const { data: rawDiff } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
mediaType: { format: 'diff' },
});
// Cap huge diffs (lockfiles, fixtures) to fit context.
const DIFF_CHAR_CAP = 400000;
const diff = rawDiff.length > DIFF_CHAR_CAP
? rawDiff.slice(0, DIFF_CHAR_CAP) + '\n\n[diff truncated]'
: rawDiff;
const prompt = `You are rewriting the body of a GitHub pull request.
Follow the skill below exactly. Keep the repository PR template's sections (## Issue, ## Checklist, ## Notes) and fill them from the current body; replace the template's ## Summary with the skill's three sections (## Summary, ## Evidence, ## Merge Danger) in that order, between ## Issue and ## Checklist. Keep any "Closes ..." issue reference verbatim; if none, write "Closes #PLEASE_TYPE_ISSUE_NUMBER". Preserve checklist [x]/[ ] states from the current body. Do not invent evidence: only cite tests, numbers or screenshots present in the current body or diff. Output ONLY the markdown body.
<skill>
${skill}
</skill>
<pull_request_template>
${template}
</pull_request_template>
PR title: ${pr.title}
Branch: ${pr.head.ref} -> ${pr.base.ref}
<current_body>
${pr.body || '(empty)'}
</current_body>
<diff>
${diff}
</diff>`;
const response = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.ANTHROPIC_API_KEY,
'anthropic-version': '2023-06-01',
'anthropic-beta': 'server-side-fallback-2026-07-01',
},
body: JSON.stringify({
model: 'claude-opus-5-5',
max_tokens: 8000,
fallbacks: 'default',
output_config: { effort: 'medium' },
messages: [{ role: 'user', content: prompt }],
}),
});
const data = await response.json();
if (!response.ok) {
core.setFailed(`Anthropic API error: ${JSON.stringify(data)}`);
return;
}
if (data.stop_reason === 'refusal') {
core.setFailed(`Claude declined the request: ${JSON.stringify(data.stop_details)}`);
return;
}
const body = data.content
.filter((block) => block.type === 'text')
.map((block) => block.text)
.join('')
.trim();
if (!body) {
core.setFailed('Empty response from Claude');
return;
}
const original = pr.body || '(empty)';
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
body: `${body}\n\n<details><summary>Previous PR description</summary>\n\n${original}\n\n</details>`,
});