@bobbyg603/mog 1.3.0 → 1.5.0
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/github.ts +92 -4
- package/src/index.ts +136 -33
- package/src/sandbox.ts +9 -1
package/package.json
CHANGED
package/src/github.ts
CHANGED
|
@@ -4,6 +4,7 @@ export interface Issue {
|
|
|
4
4
|
title: string;
|
|
5
5
|
body: string;
|
|
6
6
|
labels: string;
|
|
7
|
+
comments: string;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export function fetchIssue(repo: string, issueNum: string): Issue {
|
|
@@ -12,7 +13,7 @@ export function fetchIssue(repo: string, issueNum: string): Issue {
|
|
|
12
13
|
const proc = Bun.spawnSync([
|
|
13
14
|
"gh", "issue", "view", issueNum,
|
|
14
15
|
"--repo", repo,
|
|
15
|
-
"--json", "title,body,labels",
|
|
16
|
+
"--json", "title,body,labels,comments",
|
|
16
17
|
]);
|
|
17
18
|
|
|
18
19
|
if (proc.exitCode !== 0) {
|
|
@@ -21,10 +22,15 @@ export function fetchIssue(repo: string, issueNum: string): Issue {
|
|
|
21
22
|
|
|
22
23
|
const json = JSON.parse(proc.stdout.toString());
|
|
23
24
|
|
|
25
|
+
const comments = (json.comments || [])
|
|
26
|
+
.map((c: { author: { login: string }; body: string }) => `**@${c.author.login}:** ${c.body}`)
|
|
27
|
+
.join("\n\n");
|
|
28
|
+
|
|
24
29
|
return {
|
|
25
30
|
title: json.title,
|
|
26
31
|
body: json.body || "No description provided.",
|
|
27
32
|
labels: json.labels?.map((l: { name: string }) => l.name).join(", ") || "none",
|
|
33
|
+
comments,
|
|
28
34
|
};
|
|
29
35
|
}
|
|
30
36
|
|
|
@@ -70,6 +76,57 @@ export function listIssues(repo: string, verbose: boolean): void {
|
|
|
70
76
|
}
|
|
71
77
|
}
|
|
72
78
|
|
|
79
|
+
export interface PRFeedback {
|
|
80
|
+
prNumber: number;
|
|
81
|
+
prUrl: string;
|
|
82
|
+
reviews: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function fetchPRFeedback(repo: string, branchName: string): PRFeedback | null {
|
|
86
|
+
const proc = Bun.spawnSync([
|
|
87
|
+
"gh", "pr", "list",
|
|
88
|
+
"--repo", repo,
|
|
89
|
+
"--head", branchName,
|
|
90
|
+
"--state", "open",
|
|
91
|
+
"--json", "number,url",
|
|
92
|
+
]);
|
|
93
|
+
|
|
94
|
+
if (proc.exitCode !== 0) return null;
|
|
95
|
+
|
|
96
|
+
const prs = JSON.parse(proc.stdout.toString());
|
|
97
|
+
if (prs.length === 0) return null;
|
|
98
|
+
|
|
99
|
+
const prNumber = prs[0].number;
|
|
100
|
+
const prUrl = prs[0].url;
|
|
101
|
+
|
|
102
|
+
// Fetch review comments
|
|
103
|
+
const reviewProc = Bun.spawnSync([
|
|
104
|
+
"gh", "pr", "view", String(prNumber),
|
|
105
|
+
"--repo", repo,
|
|
106
|
+
"--json", "reviews,comments",
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
let reviews = "";
|
|
110
|
+
if (reviewProc.exitCode === 0) {
|
|
111
|
+
const data = JSON.parse(reviewProc.stdout.toString());
|
|
112
|
+
|
|
113
|
+
const reviewEntries = (data.reviews || [])
|
|
114
|
+
.filter((r: { body: string }) => r.body?.trim())
|
|
115
|
+
.map((r: { author: { login: string }; state: string; body: string }) =>
|
|
116
|
+
`**@${r.author.login}** (${r.state}):\n${r.body}`
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const commentEntries = (data.comments || [])
|
|
120
|
+
.map((c: { author: { login: string }; body: string }) =>
|
|
121
|
+
`**@${c.author.login}:**\n${c.body}`
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
reviews = [...reviewEntries, ...commentEntries].join("\n\n");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return { prNumber, prUrl, reviews };
|
|
128
|
+
}
|
|
129
|
+
|
|
73
130
|
export function pushAndCreatePR(
|
|
74
131
|
repo: string,
|
|
75
132
|
worktreeDir: string,
|
|
@@ -77,7 +134,8 @@ export function pushAndCreatePR(
|
|
|
77
134
|
defaultBranch: string,
|
|
78
135
|
issueNum: string,
|
|
79
136
|
issue: Issue,
|
|
80
|
-
summary?: string
|
|
137
|
+
summary?: string,
|
|
138
|
+
existingPR?: PRFeedback,
|
|
81
139
|
): void {
|
|
82
140
|
// Check for unpushed commits or uncommitted changes
|
|
83
141
|
const unpushed = Bun.spawnSync(["git", "log", `origin/${defaultBranch}..HEAD`, "--oneline"], { cwd: worktreeDir });
|
|
@@ -107,14 +165,44 @@ export function pushAndCreatePR(
|
|
|
107
165
|
}
|
|
108
166
|
}
|
|
109
167
|
|
|
110
|
-
//
|
|
168
|
+
// Squash all commits into one
|
|
169
|
+
const commitCount = Bun.spawnSync(["git", "rev-list", "--count", `${defaultBranch}..HEAD`], { cwd: worktreeDir });
|
|
170
|
+
const count = parseInt(commitCount.stdout.toString().trim(), 10) || 0;
|
|
171
|
+
if (count > 1) {
|
|
172
|
+
log.info(`Squashing ${count} commits into one...`);
|
|
173
|
+
const prefix = issue.labels.includes("enhancement") || issue.labels.includes("feature") ? "feat" : "fix";
|
|
174
|
+
const squash = Bun.spawnSync(["git", "reset", "--soft", defaultBranch], { cwd: worktreeDir });
|
|
175
|
+
if (squash.exitCode === 0) {
|
|
176
|
+
const msg = `${prefix}: ${issue.title.toLowerCase()} (#${issueNum})`;
|
|
177
|
+
Bun.spawnSync(["git", "commit", "-m", msg], { cwd: worktreeDir });
|
|
178
|
+
log.ok("Commits squashed.");
|
|
179
|
+
} else {
|
|
180
|
+
log.warn("Failed to squash — pushing individual commits instead.");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Push (force-with-lease when updating an existing PR)
|
|
111
185
|
log.info(`Pushing branch '${branchName}' to origin...`);
|
|
112
|
-
const
|
|
186
|
+
const pushArgs = existingPR
|
|
187
|
+
? ["git", "push", "--force-with-lease", "-u", "origin", branchName]
|
|
188
|
+
: ["git", "push", "-u", "origin", branchName];
|
|
189
|
+
const push = Bun.spawnSync(pushArgs, { cwd: worktreeDir });
|
|
113
190
|
if (push.exitCode !== 0) {
|
|
114
191
|
log.die("Failed to push. Check your git credentials.");
|
|
115
192
|
}
|
|
116
193
|
log.ok("Branch pushed.");
|
|
117
194
|
|
|
195
|
+
if (existingPR) {
|
|
196
|
+
// Update existing PR
|
|
197
|
+
log.ok("Existing PR updated!");
|
|
198
|
+
console.log(`\x1b[0;32m${existingPR.prUrl}\x1b[0m`);
|
|
199
|
+
console.log();
|
|
200
|
+
log.ok(`All done! Issue #${issueNum} → Branch '${branchName}' → PR updated.`);
|
|
201
|
+
log.info(`Worktree: ${worktreeDir}`);
|
|
202
|
+
log.info(`To clean up the worktree later: git worktree remove ${worktreeDir}`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
118
206
|
// Create PR
|
|
119
207
|
log.info("Opening pull request...");
|
|
120
208
|
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fetchIssue, listIssues, fetchPRFeedback } from "./github";
|
|
4
6
|
import { detectRepo, ensureRepo, createWorktree } from "./worktree";
|
|
5
7
|
import { runClaude } from "./sandbox";
|
|
6
8
|
import { pushAndCreatePR } from "./github";
|
|
9
|
+
import type { PRFeedback } from "./github";
|
|
7
10
|
import { log } from "./log";
|
|
8
11
|
|
|
9
12
|
const SANDBOX_NAME = "mog";
|
|
@@ -111,30 +114,54 @@ async function main() {
|
|
|
111
114
|
console.log(" mog list [--verbose] — list open issues (auto-detect repo)");
|
|
112
115
|
console.log(" mog <owner/repo> list [--verbose] — list open issues for a repo");
|
|
113
116
|
console.log();
|
|
117
|
+
console.log("Options:");
|
|
118
|
+
console.log(" --include <file> — copy a file into the worktree (repeatable)");
|
|
119
|
+
console.log(" --fresh — ignore existing PR, start a brand new one");
|
|
120
|
+
console.log();
|
|
114
121
|
console.log("Example:");
|
|
115
122
|
console.log(" mog init");
|
|
116
123
|
console.log(" mog 123");
|
|
124
|
+
console.log(" mog 123 --include .env");
|
|
117
125
|
console.log(" mog workingdevshero/automate-it 123");
|
|
118
126
|
console.log(" mog list");
|
|
119
127
|
console.log(" mog list --verbose");
|
|
120
128
|
return;
|
|
121
129
|
}
|
|
122
130
|
|
|
131
|
+
// Parse --include and --fresh flags
|
|
132
|
+
const includeFiles: string[] = [];
|
|
133
|
+
const filteredArgs: string[] = [];
|
|
134
|
+
let fresh = false;
|
|
135
|
+
for (let i = 0; i < args.length; i++) {
|
|
136
|
+
if (args[i] === "--include" && i + 1 < args.length) {
|
|
137
|
+
const filePath = path.resolve(args[i + 1]!);
|
|
138
|
+
if (!fs.existsSync(filePath)) {
|
|
139
|
+
log.die(`Include file not found: ${args[i + 1]}`);
|
|
140
|
+
}
|
|
141
|
+
includeFiles.push(filePath);
|
|
142
|
+
i++; // skip the path argument
|
|
143
|
+
} else if (args[i] === "--fresh") {
|
|
144
|
+
fresh = true;
|
|
145
|
+
} else {
|
|
146
|
+
filteredArgs.push(args[i]!);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
123
150
|
let repo: string;
|
|
124
151
|
let issueNum: string;
|
|
125
152
|
|
|
126
|
-
if (/^\d+$/.test(
|
|
153
|
+
if (/^\d+$/.test(filteredArgs[0])) {
|
|
127
154
|
// mog <issue_number> — auto-detect repo
|
|
128
155
|
const detected = detectRepo();
|
|
129
156
|
if (!detected) {
|
|
130
157
|
log.die("Could not detect repo from git remote. Run from inside a git repo or use: mog <owner/repo> <issue_num>");
|
|
131
158
|
}
|
|
132
159
|
repo = detected;
|
|
133
|
-
issueNum =
|
|
134
|
-
} else if (
|
|
160
|
+
issueNum = filteredArgs[0];
|
|
161
|
+
} else if (filteredArgs.length >= 2) {
|
|
135
162
|
// mog <owner/repo> <issue_number>
|
|
136
|
-
repo =
|
|
137
|
-
issueNum =
|
|
163
|
+
repo = filteredArgs[0];
|
|
164
|
+
issueNum = filteredArgs[1];
|
|
138
165
|
if (!/^\d+$/.test(issueNum)) {
|
|
139
166
|
log.die(`Invalid issue number: '${issueNum}'. Must be a positive integer.`);
|
|
140
167
|
}
|
|
@@ -146,9 +173,14 @@ async function main() {
|
|
|
146
173
|
console.log(" mog list [--verbose] — list open issues (auto-detect repo)");
|
|
147
174
|
console.log(" mog <owner/repo> list [--verbose] — list open issues for a repo");
|
|
148
175
|
console.log();
|
|
176
|
+
console.log("Options:");
|
|
177
|
+
console.log(" --include <file> — copy a file into the worktree (repeatable)");
|
|
178
|
+
console.log(" --fresh — ignore existing PR, start a brand new one");
|
|
179
|
+
console.log();
|
|
149
180
|
console.log("Example:");
|
|
150
181
|
console.log(" mog init");
|
|
151
182
|
console.log(" mog 123");
|
|
183
|
+
console.log(" mog 123 --include .env");
|
|
152
184
|
console.log(" mog workingdevshero/automate-it 123");
|
|
153
185
|
console.log(" mog list");
|
|
154
186
|
console.log(" mog list --verbose");
|
|
@@ -188,10 +220,32 @@ async function main() {
|
|
|
188
220
|
reposDir, owner, repoName, defaultBranch, issueNum, issue.title
|
|
189
221
|
);
|
|
190
222
|
|
|
223
|
+
// Check for existing PR (unless --fresh)
|
|
224
|
+
let existingPR: PRFeedback | undefined;
|
|
225
|
+
if (!fresh) {
|
|
226
|
+
const pr = fetchPRFeedback(repo, branchName);
|
|
227
|
+
if (pr) {
|
|
228
|
+
existingPR = pr;
|
|
229
|
+
log.ok(`Found existing PR #${pr.prNumber} — will include review feedback and update it.`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Copy included files into worktree
|
|
234
|
+
const copiedFiles: string[] = [];
|
|
235
|
+
for (const filePath of includeFiles) {
|
|
236
|
+
const basename = path.basename(filePath);
|
|
237
|
+
const dest = path.join(worktreeDir, basename);
|
|
238
|
+
fs.copyFileSync(filePath, dest);
|
|
239
|
+
copiedFiles.push(dest);
|
|
240
|
+
log.ok(`Included: ${basename}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
191
243
|
// Build prompts
|
|
192
|
-
const
|
|
244
|
+
const prFeedback = existingPR?.reviews || "";
|
|
245
|
+
const planningPrompt = buildPlanningPrompt(repo, issueNum, issue, prFeedback);
|
|
193
246
|
const buildingPromptFn = (remaining: string[], plan: string) =>
|
|
194
247
|
buildBuildingPrompt(repo, issueNum, issue, remaining, plan);
|
|
248
|
+
const reviewPrompt = buildReviewPrompt(repo, issueNum, issue);
|
|
195
249
|
|
|
196
250
|
// Run Claude in sandbox
|
|
197
251
|
log.info("Launching Claude Code in sandbox...");
|
|
@@ -199,10 +253,21 @@ async function main() {
|
|
|
199
253
|
log.info(`Worktree: ${worktreeDir}`);
|
|
200
254
|
console.log();
|
|
201
255
|
|
|
202
|
-
const summary = await runClaude(SANDBOX_NAME, worktreeDir, planningPrompt, buildingPromptFn);
|
|
256
|
+
const summary = await runClaude(SANDBOX_NAME, worktreeDir, planningPrompt, buildingPromptFn, reviewPrompt);
|
|
257
|
+
|
|
258
|
+
// Remove included files so they don't end up in the PR
|
|
259
|
+
for (const filePath of copiedFiles) {
|
|
260
|
+
try {
|
|
261
|
+
fs.unlinkSync(filePath);
|
|
262
|
+
// Unstage if Claude happened to git add it
|
|
263
|
+
Bun.spawnSync(["git", "rm", "--cached", "--ignore-unmatch", path.basename(filePath)], { cwd: worktreeDir });
|
|
264
|
+
} catch {
|
|
265
|
+
// File may already be gone
|
|
266
|
+
}
|
|
267
|
+
}
|
|
203
268
|
|
|
204
|
-
// Push and create PR
|
|
205
|
-
pushAndCreatePR(repo, worktreeDir, branchName, defaultBranch, issueNum, issue, summary);
|
|
269
|
+
// Push and create/update PR
|
|
270
|
+
pushAndCreatePR(repo, worktreeDir, branchName, defaultBranch, issueNum, issue, summary, existingPR);
|
|
206
271
|
}
|
|
207
272
|
|
|
208
273
|
function getReposDir(): string {
|
|
@@ -254,29 +319,54 @@ function tryRecoverSandbox(reposDir: string): boolean {
|
|
|
254
319
|
return true;
|
|
255
320
|
}
|
|
256
321
|
|
|
257
|
-
function
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
## Issue: ${issue.title}
|
|
322
|
+
function formatIssueContext(issueNum: string, issue: { title: string; body: string; labels: string; comments: string }, prFeedback?: string): string {
|
|
323
|
+
let context = `## Issue: ${issue.title}
|
|
261
324
|
|
|
262
325
|
### Description
|
|
263
326
|
${issue.body}
|
|
264
327
|
|
|
265
328
|
### Labels
|
|
266
|
-
${issue.labels}
|
|
329
|
+
${issue.labels}`;
|
|
330
|
+
|
|
331
|
+
if (issue.comments) {
|
|
332
|
+
context += `
|
|
333
|
+
|
|
334
|
+
### Comments
|
|
335
|
+
${issue.comments}`;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (prFeedback) {
|
|
339
|
+
context += `
|
|
340
|
+
|
|
341
|
+
### Previous PR Review Feedback
|
|
342
|
+
A previous attempt at this issue was reviewed. Address the following feedback in your implementation:
|
|
343
|
+
|
|
344
|
+
${prFeedback}`;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return context;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function buildPlanningPrompt(repo: string, issueNum: string, issue: { title: string; body: string; labels: string; comments: string }, prFeedback?: string): string {
|
|
351
|
+
return `You are working on GitHub issue #${issueNum} for the repository ${repo}.
|
|
352
|
+
|
|
353
|
+
${formatIssueContext(issueNum, issue, prFeedback)}
|
|
267
354
|
|
|
268
355
|
## Instructions
|
|
269
356
|
|
|
270
357
|
Your job in this step is to **plan only** — do NOT implement anything and do NOT commit.
|
|
271
358
|
|
|
272
359
|
1. Read and understand the codebase structure thoroughly.
|
|
273
|
-
2.
|
|
274
|
-
3.
|
|
360
|
+
2. **Search the entire codebase** for code related to the issue — look for similar patterns, duplicate logic, and any modules that handle the same concern. Use Grep and Glob liberally to find all relevant locations, not just the most obvious one.
|
|
361
|
+
3. Analyze the issue and break it down into small, atomic implementation tasks.
|
|
362
|
+
4. Create a file called \`IMPLEMENTATION_PLAN.md\` in the root of the repository with a checklist of tasks.
|
|
275
363
|
|
|
276
364
|
The plan should:
|
|
277
365
|
- Have 3-8 tasks (fewer for simple issues, more for complex ones)
|
|
278
366
|
- Order tasks by dependency (implement foundations first)
|
|
279
367
|
- Each task should be a single, atomic unit of work that results in one commit
|
|
368
|
+
- **Include tasks to update ALL locations** where the same pattern or concern exists — not just the most obvious one. If the same logic appears in multiple modules, the plan must cover all of them.
|
|
369
|
+
- If you find duplicate or near-duplicate logic across modules, include a task to consolidate it into a shared utility or function.
|
|
280
370
|
- Use markdown checklist format: \`- [ ] Task description\`
|
|
281
371
|
|
|
282
372
|
Example format:
|
|
@@ -295,7 +385,7 @@ Do NOT implement any code changes. Do NOT make any commits. Only create the plan
|
|
|
295
385
|
function buildBuildingPrompt(
|
|
296
386
|
repo: string,
|
|
297
387
|
issueNum: string,
|
|
298
|
-
issue: { title: string; body: string; labels: string },
|
|
388
|
+
issue: { title: string; body: string; labels: string; comments: string },
|
|
299
389
|
remainingItems: string[],
|
|
300
390
|
planContent: string,
|
|
301
391
|
): string {
|
|
@@ -303,19 +393,13 @@ function buildBuildingPrompt(
|
|
|
303
393
|
if (remainingItems.length === 0 && !planContent) {
|
|
304
394
|
return `You are working on GitHub issue #${issueNum} for the repository ${repo}.
|
|
305
395
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
### Description
|
|
309
|
-
${issue.body}
|
|
310
|
-
|
|
311
|
-
### Labels
|
|
312
|
-
${issue.labels}
|
|
396
|
+
${formatIssueContext(issueNum, issue)}
|
|
313
397
|
|
|
314
398
|
## Instructions
|
|
315
399
|
1. Read and understand the codebase structure first.
|
|
316
400
|
2. Implement the changes described in the issue above.
|
|
317
401
|
3. Write clean, well-documented code that follows the existing project conventions.
|
|
318
|
-
4.
|
|
402
|
+
4. If the project has an existing test suite, add or update tests to cover the changes.
|
|
319
403
|
5. Make sure the code builds/lints without errors if there's a build system.
|
|
320
404
|
6. Commit your changes with a clear commit message referencing issue #${issueNum}.
|
|
321
405
|
|
|
@@ -327,13 +411,7 @@ a message like: "fix: <short description> (#${issueNum})"`;
|
|
|
327
411
|
|
|
328
412
|
return `You are working on GitHub issue #${issueNum} for the repository ${repo}.
|
|
329
413
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
### Description
|
|
333
|
-
${issue.body}
|
|
334
|
-
|
|
335
|
-
### Labels
|
|
336
|
-
${issue.labels}
|
|
414
|
+
${formatIssueContext(issueNum, issue)}
|
|
337
415
|
|
|
338
416
|
## Current Implementation Plan
|
|
339
417
|
|
|
@@ -352,6 +430,31 @@ Rules:
|
|
|
352
430
|
5. Do NOT work on any other tasks after committing.`;
|
|
353
431
|
}
|
|
354
432
|
|
|
433
|
+
function buildReviewPrompt(
|
|
434
|
+
repo: string,
|
|
435
|
+
issueNum: string,
|
|
436
|
+
issue: { title: string; body: string; labels: string; comments: string },
|
|
437
|
+
): string {
|
|
438
|
+
return `You are reviewing changes made for GitHub issue #${issueNum} in the repository ${repo}.
|
|
439
|
+
|
|
440
|
+
${formatIssueContext(issueNum, issue)}
|
|
441
|
+
|
|
442
|
+
## Instructions
|
|
443
|
+
|
|
444
|
+
All implementation tasks are complete. Your job is to **review the entire branch** for quality and completeness.
|
|
445
|
+
|
|
446
|
+
Run \`git diff main...HEAD\` (or the equivalent for the default branch) to see all changes made.
|
|
447
|
+
|
|
448
|
+
Check for:
|
|
449
|
+
1. **Missed locations**: Search the codebase for similar patterns, logic, or code that handles the same concern as the changes. If the fix or feature was applied in one place but a similar pattern exists elsewhere, apply it there too.
|
|
450
|
+
2. **Code duplication**: If the changes introduced logic that duplicates existing code (or if pre-existing duplication was missed), consolidate it into a shared function or utility.
|
|
451
|
+
3. **Quality issues**: Look for missing edge cases, error handling gaps, or inconsistencies with the rest of the codebase.
|
|
452
|
+
4. **Tests**: If the project has an existing test suite, verify that tests were added or updated to cover the changes. If not, add them. Do not create a test framework or test infrastructure from scratch.
|
|
453
|
+
|
|
454
|
+
If you find issues, fix them and commit each fix separately with a clear commit message referencing #${issueNum}.
|
|
455
|
+
If everything looks good, do nothing.`;
|
|
456
|
+
}
|
|
457
|
+
|
|
355
458
|
main().catch((err) => {
|
|
356
459
|
log.die(err.message);
|
|
357
460
|
});
|
package/src/sandbox.ts
CHANGED
|
@@ -76,6 +76,7 @@ export async function runClaude(
|
|
|
76
76
|
worktreeDir: string,
|
|
77
77
|
planningPrompt: string,
|
|
78
78
|
buildingPromptFn: (remainingItems: string[], planContent: string) => string,
|
|
79
|
+
reviewPrompt?: string,
|
|
79
80
|
): Promise<string> {
|
|
80
81
|
let lastResult = "";
|
|
81
82
|
|
|
@@ -152,7 +153,14 @@ export async function runClaude(
|
|
|
152
153
|
}
|
|
153
154
|
}
|
|
154
155
|
|
|
155
|
-
// Phase 3 —
|
|
156
|
+
// Phase 3 — Review
|
|
157
|
+
if (reviewPrompt) {
|
|
158
|
+
log.info("Phase 3: Reviewing changes for quality and completeness...");
|
|
159
|
+
const reviewResult = await execClaude(sandboxName, worktreeDir, ["-p", reviewPrompt]);
|
|
160
|
+
if (reviewResult) lastResult = reviewResult;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Phase 4 — Cleanup
|
|
156
164
|
cleanupPlanFile(sandboxName, worktreeDir);
|
|
157
165
|
|
|
158
166
|
const finalPlan = readPlanFile(worktreeDir);
|