@warpmetrics/coder 0.2.9 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/git.js +8 -0
- package/src/revise.js +50 -19
- package/src/warp.js +1 -1
- package/src/watch.js +5 -5
package/package.json
CHANGED
package/src/git.js
CHANGED
|
@@ -17,6 +17,10 @@ export function createBranch(dir, name) {
|
|
|
17
17
|
run(`git checkout -b ${name}`, { cwd: dir });
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
export function getHead(dir) {
|
|
21
|
+
return run(`git rev-parse HEAD`, { cwd: dir });
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
export function hasNewCommits(dir, base = 'main') {
|
|
21
25
|
const log = run(`git log ${base}..HEAD --oneline`, { cwd: dir });
|
|
22
26
|
return log.length > 0;
|
|
@@ -59,6 +63,10 @@ export function getReviewComments(prNumber, { repo }) {
|
|
|
59
63
|
return JSON.parse(out);
|
|
60
64
|
}
|
|
61
65
|
|
|
66
|
+
export function dismissReview(prNumber, reviewId, { repo, message }) {
|
|
67
|
+
run(`gh api repos/${repo}/pulls/${prNumber}/reviews/${reviewId}/dismissals -X PUT -f message=${JSON.stringify(message)}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
62
70
|
export function updatePRBody(prNumber, { repo, body }) {
|
|
63
71
|
run(`gh pr edit ${prNumber} --repo ${repo} --body ${JSON.stringify(body)}`);
|
|
64
72
|
}
|
package/src/revise.js
CHANGED
|
@@ -158,6 +158,8 @@ export async function revise(item, { board, config, log, refActId, since }) {
|
|
|
158
158
|
|
|
159
159
|
const prompt = promptParts.join('\n');
|
|
160
160
|
|
|
161
|
+
const headBefore = git.getHead(workdir);
|
|
162
|
+
|
|
161
163
|
log(' running claude...');
|
|
162
164
|
claudeResult = await claude.run({
|
|
163
165
|
prompt,
|
|
@@ -182,32 +184,61 @@ export async function revise(item, { board, config, log, refActId, since }) {
|
|
|
182
184
|
git.commitAll(workdir, 'Address review feedback');
|
|
183
185
|
}
|
|
184
186
|
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
|
|
187
|
+
// Check if Claude actually made changes
|
|
188
|
+
const headAfter = git.getHead(workdir);
|
|
189
|
+
if (headAfter === headBefore) {
|
|
190
|
+
log(' no changes needed — review feedback already addressed');
|
|
188
191
|
|
|
189
|
-
|
|
190
|
-
if (actId) {
|
|
192
|
+
// Dismiss active CHANGES_REQUESTED reviews (they're stale)
|
|
191
193
|
try {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
const reviews = git.getReviews(prNumber, { repo: repoName });
|
|
195
|
+
for (const r of reviews) {
|
|
196
|
+
if (r.state === 'CHANGES_REQUESTED') {
|
|
197
|
+
git.dismissReview(prNumber, r.id, {
|
|
198
|
+
repo: repoName,
|
|
199
|
+
message: 'Code verified correct by warp-coder — no changes needed.',
|
|
200
|
+
});
|
|
201
|
+
log(` dismissed stale review ${r.id}`);
|
|
202
|
+
}
|
|
196
203
|
}
|
|
197
|
-
git.updatePRBody(prNumber, { repo: repoName, body });
|
|
198
204
|
} catch (err) {
|
|
199
|
-
log(` warning: could not
|
|
205
|
+
log(` warning: could not dismiss stale reviews: ${err.message}`);
|
|
200
206
|
}
|
|
201
|
-
}
|
|
202
207
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
208
|
+
// Move back to In Review (no active CHANGES_REQUESTED, so won't be picked up again)
|
|
209
|
+
try {
|
|
210
|
+
await board.moveToReview(item);
|
|
211
|
+
} catch (err) {
|
|
212
|
+
log(` warning: could not move to In Review: ${err.message}`);
|
|
213
|
+
}
|
|
214
|
+
success = true;
|
|
215
|
+
} else {
|
|
216
|
+
// Push
|
|
217
|
+
log(' pushing...');
|
|
218
|
+
git.push(workdir, branch);
|
|
219
|
+
|
|
220
|
+
// Update PR body with new act ID for next review cycle
|
|
221
|
+
if (actId) {
|
|
222
|
+
try {
|
|
223
|
+
let body = git.getPRBody(prNumber, { repo: repoName });
|
|
224
|
+
body = body.replace(/<!-- wm:act:wm_act_\w+ -->/, `<!-- wm:act:${actId} -->`);
|
|
225
|
+
if (!body.includes(`<!-- wm:act:${actId} -->`)) {
|
|
226
|
+
body += `\n\n<!-- wm:act:${actId} -->`;
|
|
227
|
+
}
|
|
228
|
+
git.updatePRBody(prNumber, { repo: repoName, body });
|
|
229
|
+
} catch (err) {
|
|
230
|
+
log(` warning: could not update PR body with act ID: ${err.message}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
209
233
|
|
|
210
|
-
|
|
234
|
+
// Move back to In Review
|
|
235
|
+
try {
|
|
236
|
+
await board.moveToReview(item);
|
|
237
|
+
} catch (err) {
|
|
238
|
+
log(` warning: could not move to In Review: ${err.message}`);
|
|
239
|
+
}
|
|
240
|
+
success = true;
|
|
241
|
+
}
|
|
211
242
|
} catch (err) {
|
|
212
243
|
taskError = err.message;
|
|
213
244
|
log(` failed: ${err.message}`);
|
package/src/warp.js
CHANGED
|
@@ -204,7 +204,7 @@ export async function findIssueRun(apiKey, { repo, issueNumber }) {
|
|
|
204
204
|
const lastOutcome = outcomes[outcomes.length - 1];
|
|
205
205
|
const blockedAt = lastOutcome?.name === 'Max Retries' ? lastOutcome.timestamp : null;
|
|
206
206
|
|
|
207
|
-
return { runId: match.id, blockedAt };
|
|
207
|
+
return { runId: match.id, blockedAt, countSince: null };
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
export async function countRevisions(apiKey, { prNumber, repo, since }) {
|
package/src/watch.js
CHANGED
|
@@ -58,7 +58,7 @@ export async function watch() {
|
|
|
58
58
|
const issue = await warp.createIssueRun(config.warpmetricsApiKey, {
|
|
59
59
|
repo: repoName, issueNumber, issueTitle,
|
|
60
60
|
});
|
|
61
|
-
issueRuns.set(issueNumber, { runId: issue.runId, blockedAt: null });
|
|
61
|
+
issueRuns.set(issueNumber, { runId: issue.runId, blockedAt: null, countSince: null });
|
|
62
62
|
implementActId = issue.actId;
|
|
63
63
|
log(` issue run: ${issue.runId}`);
|
|
64
64
|
} catch (err) {
|
|
@@ -92,11 +92,10 @@ export async function watch() {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// Detect resume: item was previously blocked, human moved it back to In Review
|
|
95
|
-
let since = null;
|
|
96
95
|
if (issueCtx?.blockedAt) {
|
|
97
|
-
|
|
96
|
+
issueCtx.countSince = issueCtx.blockedAt;
|
|
98
97
|
issueCtx.blockedAt = null;
|
|
99
|
-
log(` resumed (counter reset)`);
|
|
98
|
+
log(` resumed from blocked (counter reset, since: ${issueCtx.countSince})`);
|
|
100
99
|
if (config.warpmetricsApiKey) {
|
|
101
100
|
try {
|
|
102
101
|
await warp.closeIssueRun(config.warpmetricsApiKey, {
|
|
@@ -106,12 +105,13 @@ export async function watch() {
|
|
|
106
105
|
}
|
|
107
106
|
}
|
|
108
107
|
|
|
109
|
-
const result = await revise(item, { board, config, log, refActId: item._reviewActId, since });
|
|
108
|
+
const result = await revise(item, { board, config, log, refActId: item._reviewActId, since: issueCtx?.countSince });
|
|
110
109
|
|
|
111
110
|
// Record outcome on the issue run if revision failed terminally
|
|
112
111
|
if (!result.success && issueCtx && config.warpmetricsApiKey) {
|
|
113
112
|
const name = result.reason === 'max_retries' ? 'Max Retries' : 'Revision Failed';
|
|
114
113
|
issueCtx.blockedAt = new Date().toISOString();
|
|
114
|
+
issueCtx.countSince = null;
|
|
115
115
|
try {
|
|
116
116
|
await warp.closeIssueRun(config.warpmetricsApiKey, {
|
|
117
117
|
runId: issueCtx.runId,
|