claude-issue-solver 1.31.0 ā 1.33.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/dist/commands/review.d.ts +6 -2
- package/dist/commands/review.js +40 -159
- package/dist/index.js +4 -3
- package/package.json +1 -1
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
export declare function reviewCommand(issueNumber: number
|
|
2
|
-
|
|
1
|
+
export declare function reviewCommand(issueNumber: number, options?: {
|
|
2
|
+
merge?: boolean;
|
|
3
|
+
}): Promise<void>;
|
|
4
|
+
export declare function selectReviewCommand(options?: {
|
|
5
|
+
merge?: boolean;
|
|
6
|
+
}): Promise<void>;
|
package/dist/commands/review.js
CHANGED
|
@@ -43,12 +43,13 @@ const ora_1 = __importDefault(require("ora"));
|
|
|
43
43
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
44
44
|
const fs = __importStar(require("fs"));
|
|
45
45
|
const path = __importStar(require("path"));
|
|
46
|
+
const os = __importStar(require("os"));
|
|
46
47
|
const child_process_1 = require("child_process");
|
|
47
48
|
const github_1 = require("../utils/github");
|
|
48
49
|
const git_1 = require("../utils/git");
|
|
49
50
|
const helpers_1 = require("../utils/helpers");
|
|
50
51
|
const config_1 = require("./config");
|
|
51
|
-
async function reviewCommand(issueNumber) {
|
|
52
|
+
async function reviewCommand(issueNumber, options = {}) {
|
|
52
53
|
const spinner = (0, ora_1.default)(`Fetching issue #${issueNumber}...`).start();
|
|
53
54
|
const issue = (0, github_1.getIssue)(issueNumber);
|
|
54
55
|
if (!issue) {
|
|
@@ -57,22 +58,21 @@ async function reviewCommand(issueNumber) {
|
|
|
57
58
|
}
|
|
58
59
|
spinner.succeed(`Found issue #${issueNumber}`);
|
|
59
60
|
const projectRoot = (0, git_1.getProjectRoot)();
|
|
60
|
-
|
|
61
|
-
const baseBranch = (0, git_1.getDefaultBranch)();
|
|
62
|
-
const branchSlug = (0, helpers_1.slugify)(issue.title);
|
|
63
|
-
const branchName = `issue-${issueNumber}-${branchSlug}`;
|
|
64
|
-
const worktreePath = path.join(path.dirname(projectRoot), `${projectName}-${branchName}`);
|
|
65
|
-
// Check if there's a PR for this issue
|
|
61
|
+
// Check if there's a PR for this issue - search by issue number in all PRs
|
|
66
62
|
const prCheckSpinner = (0, ora_1.default)('Checking for PR...').start();
|
|
67
63
|
let prNumber = null;
|
|
64
|
+
let branchName = null;
|
|
68
65
|
try {
|
|
69
|
-
|
|
66
|
+
// Search for PRs that mention the issue number in their branch name
|
|
67
|
+
const prOutput = (0, child_process_1.execSync)(`gh pr list --state open --json number,headRefName --jq '.[] | select(.headRefName | test("issue-${issueNumber}-")) | "\\(.number) \\(.headRefName)"'`, {
|
|
70
68
|
cwd: projectRoot,
|
|
71
69
|
encoding: 'utf-8',
|
|
72
70
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
73
71
|
}).trim();
|
|
74
72
|
if (prOutput) {
|
|
75
|
-
|
|
73
|
+
const [num, branch] = prOutput.split(' ');
|
|
74
|
+
prNumber = num;
|
|
75
|
+
branchName = branch;
|
|
76
76
|
prCheckSpinner.succeed(`Found PR #${prNumber}`);
|
|
77
77
|
}
|
|
78
78
|
else {
|
|
@@ -90,55 +90,6 @@ async function reviewCommand(issueNumber) {
|
|
|
90
90
|
console.log(chalk_1.default.bold(`š Reviewing: ${issue.title}`));
|
|
91
91
|
console.log(chalk_1.default.dim(`š PR: https://github.com/${getRepoName(projectRoot)}/pull/${prNumber}`));
|
|
92
92
|
console.log();
|
|
93
|
-
// Fetch latest
|
|
94
|
-
const fetchSpinner = (0, ora_1.default)(`Fetching latest changes...`).start();
|
|
95
|
-
try {
|
|
96
|
-
(0, child_process_1.execSync)(`git fetch origin ${branchName} --quiet`, { cwd: projectRoot, stdio: 'pipe' });
|
|
97
|
-
fetchSpinner.succeed('Fetched latest changes');
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
fetchSpinner.warn('Could not fetch branch');
|
|
101
|
-
}
|
|
102
|
-
// Check if worktree already exists
|
|
103
|
-
if (fs.existsSync(worktreePath)) {
|
|
104
|
-
console.log(chalk_1.default.yellow(`\nšæ Using existing worktree at: ${worktreePath}`));
|
|
105
|
-
// Pull latest changes
|
|
106
|
-
try {
|
|
107
|
-
(0, child_process_1.execSync)('git pull --quiet', { cwd: worktreePath, stdio: 'pipe' });
|
|
108
|
-
}
|
|
109
|
-
catch {
|
|
110
|
-
// Ignore pull errors
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
const worktreeSpinner = (0, ora_1.default)(`Creating worktree for review...`).start();
|
|
115
|
-
try {
|
|
116
|
-
if ((0, git_1.branchExists)(branchName)) {
|
|
117
|
-
(0, child_process_1.execSync)(`git worktree add "${worktreePath}" "${branchName}"`, {
|
|
118
|
-
cwd: projectRoot,
|
|
119
|
-
stdio: 'pipe',
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
// Branch should exist if PR exists, but handle edge case
|
|
124
|
-
(0, child_process_1.execSync)(`git worktree add "${worktreePath}" "origin/${branchName}"`, {
|
|
125
|
-
cwd: projectRoot,
|
|
126
|
-
stdio: 'pipe',
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
worktreeSpinner.succeed(`Created worktree at: ${worktreePath}`);
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
worktreeSpinner.fail('Failed to create worktree');
|
|
133
|
-
console.error(error);
|
|
134
|
-
process.exit(1);
|
|
135
|
-
}
|
|
136
|
-
// Copy env files and symlink node_modules
|
|
137
|
-
const setupSpinner = (0, ora_1.default)('Setting up worktree...').start();
|
|
138
|
-
(0, helpers_1.copyEnvFiles)(projectRoot, worktreePath);
|
|
139
|
-
(0, helpers_1.symlinkNodeModules)(projectRoot, worktreePath);
|
|
140
|
-
setupSpinner.succeed('Worktree setup complete');
|
|
141
|
-
}
|
|
142
93
|
// Get the diff for context
|
|
143
94
|
let diffContent = '';
|
|
144
95
|
try {
|
|
@@ -247,8 +198,9 @@ The \`suggestion\` code block creates a "Commit suggestion" button on GitHub.
|
|
|
247
198
|
${diffContent ? `\n\`\`\`diff\n${diffContent.slice(0, 50000)}\n\`\`\`\n` : 'Run `gh pr diff ' + prNumber + '` to see the changes.'}
|
|
248
199
|
|
|
249
200
|
Start by examining the diff and the changed files, then provide your review.`;
|
|
250
|
-
// Write prompt to
|
|
251
|
-
const
|
|
201
|
+
// Write prompt and runner script to temp directory
|
|
202
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cis-review-'));
|
|
203
|
+
const promptFile = path.join(tempDir, '.claude-review-prompt.txt');
|
|
252
204
|
fs.writeFileSync(promptFile, prompt);
|
|
253
205
|
// Bot token already fetched above
|
|
254
206
|
const botTokenEnv = botToken ? `export BOT_TOKEN="${botToken}"\nexport GH_TOKEN="${botToken}"` : '# No bot token configured';
|
|
@@ -256,9 +208,9 @@ Start by examining the diff and the changed files, then provide your review.`;
|
|
|
256
208
|
? 'Using bot token for reviews (can approve/request changes)'
|
|
257
209
|
: 'No bot token - using your account (may have limitations on own PRs)';
|
|
258
210
|
// Create runner script for review
|
|
259
|
-
const runnerScript = path.join(
|
|
211
|
+
const runnerScript = path.join(tempDir, '.claude-review-runner.sh');
|
|
260
212
|
const runnerContent = `#!/bin/bash
|
|
261
|
-
cd "${
|
|
213
|
+
cd "${projectRoot}"
|
|
262
214
|
|
|
263
215
|
# Set bot token if configured
|
|
264
216
|
${botTokenEnv}
|
|
@@ -275,14 +227,15 @@ echo "${botNote}"
|
|
|
275
227
|
echo ""
|
|
276
228
|
echo "Claude will review the PR and post suggestions."
|
|
277
229
|
echo "You can commit suggestions directly on GitHub."
|
|
230
|
+
${options.merge ? 'echo "\\nš Auto-merge enabled: will merge if approved."' : ''}
|
|
278
231
|
echo "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"
|
|
279
232
|
echo ""
|
|
280
233
|
|
|
281
234
|
# Run Claude interactively
|
|
282
235
|
claude --dangerously-skip-permissions "$(cat '${promptFile}')"
|
|
283
236
|
|
|
284
|
-
# Clean up
|
|
285
|
-
rm -
|
|
237
|
+
# Clean up temp files
|
|
238
|
+
rm -rf '${tempDir}'
|
|
286
239
|
|
|
287
240
|
echo ""
|
|
288
241
|
echo "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"
|
|
@@ -295,30 +248,15 @@ REVIEW_STATUS=$(gh pr view ${prNumber} --json reviewDecision --jq '.reviewDecisi
|
|
|
295
248
|
if [ "$REVIEW_STATUS" = "APPROVED" ]; then
|
|
296
249
|
echo "ā
PR #${prNumber} is approved!"
|
|
297
250
|
echo ""
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if [[ \\$REPLY =~ ^[Yy]$ ]]; then
|
|
251
|
+
${options.merge ? ` echo "š¤ Auto-merging PR #${prNumber}..."
|
|
252
|
+
if gh pr merge ${prNumber} --squash --delete-branch; then
|
|
301
253
|
echo ""
|
|
302
|
-
echo "
|
|
303
|
-
if gh pr merge ${prNumber} --squash --delete-branch; then
|
|
304
|
-
echo ""
|
|
305
|
-
echo "ā
PR merged successfully!"
|
|
306
|
-
echo ""
|
|
307
|
-
echo "Cleaning up worktree..."
|
|
308
|
-
cd "${projectRoot}"
|
|
309
|
-
git worktree remove "${worktreePath}" --force 2>/dev/null || rm -rf "${worktreePath}"
|
|
310
|
-
git worktree prune 2>/dev/null
|
|
311
|
-
git branch -D "${branchName}" 2>/dev/null
|
|
312
|
-
echo "ā
Cleanup complete!"
|
|
313
|
-
else
|
|
314
|
-
echo ""
|
|
315
|
-
echo "ā ļø Merge failed. You can try manually: gh pr merge ${prNumber} --squash"
|
|
316
|
-
fi
|
|
254
|
+
echo "ā
PR merged successfully!"
|
|
317
255
|
else
|
|
318
256
|
echo ""
|
|
319
|
-
echo "
|
|
320
|
-
|
|
321
|
-
|
|
257
|
+
echo "ā ļø Merge failed. You can try manually: gh pr merge ${prNumber} --squash"
|
|
258
|
+
fi` : ` echo "View PR: gh pr view ${prNumber} --web"
|
|
259
|
+
echo "To merge: cis merge"`}
|
|
322
260
|
else
|
|
323
261
|
echo "View PR: gh pr view ${prNumber} --web"
|
|
324
262
|
fi
|
|
@@ -337,7 +275,6 @@ exec bash
|
|
|
337
275
|
console.log(chalk_1.default.dim(` Claude is reviewing in a new terminal window.`));
|
|
338
276
|
console.log();
|
|
339
277
|
console.log(chalk_1.default.dim(` View PR: gh pr view ${prNumber} --web`));
|
|
340
|
-
console.log(chalk_1.default.dim(` To clean up later: claude-issue clean ${issueNumber}`));
|
|
341
278
|
}
|
|
342
279
|
function getRepoName(projectRoot) {
|
|
343
280
|
try {
|
|
@@ -374,7 +311,7 @@ function getOpenPRs(projectRoot) {
|
|
|
374
311
|
return [];
|
|
375
312
|
}
|
|
376
313
|
}
|
|
377
|
-
async function selectReviewCommand() {
|
|
314
|
+
async function selectReviewCommand(options = {}) {
|
|
378
315
|
const projectRoot = (0, git_1.getProjectRoot)();
|
|
379
316
|
const projectName = (0, git_1.getProjectName)();
|
|
380
317
|
console.log(chalk_1.default.bold(`\nOpen PRs for ${projectName}:\n`));
|
|
@@ -425,56 +362,13 @@ async function selectReviewCommand() {
|
|
|
425
362
|
console.log();
|
|
426
363
|
// Launch reviews in parallel
|
|
427
364
|
for (const pr of selected) {
|
|
428
|
-
await launchReviewForPR(pr, projectRoot, projectName);
|
|
365
|
+
await launchReviewForPR(pr, projectRoot, projectName, options);
|
|
429
366
|
}
|
|
430
367
|
console.log();
|
|
431
368
|
console.log(chalk_1.default.green(`ā
Started ${selected.length} review session(s)!`));
|
|
432
369
|
console.log(chalk_1.default.dim(' Each review is running in its own terminal window.'));
|
|
433
370
|
}
|
|
434
|
-
async function launchReviewForPR(pr, projectRoot,
|
|
435
|
-
const baseBranch = (0, git_1.getDefaultBranch)();
|
|
436
|
-
const branchName = pr.headRefName;
|
|
437
|
-
const worktreePath = path.join(path.dirname(projectRoot), `${projectName}-${branchName}`);
|
|
438
|
-
// Fetch latest
|
|
439
|
-
try {
|
|
440
|
-
(0, child_process_1.execSync)(`git fetch origin ${branchName} --quiet`, { cwd: projectRoot, stdio: 'pipe' });
|
|
441
|
-
}
|
|
442
|
-
catch {
|
|
443
|
-
// Ignore fetch errors
|
|
444
|
-
}
|
|
445
|
-
// Check if worktree already exists
|
|
446
|
-
if (!fs.existsSync(worktreePath)) {
|
|
447
|
-
try {
|
|
448
|
-
if ((0, git_1.branchExists)(branchName)) {
|
|
449
|
-
(0, child_process_1.execSync)(`git worktree add "${worktreePath}" "${branchName}"`, {
|
|
450
|
-
cwd: projectRoot,
|
|
451
|
-
stdio: 'pipe',
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
else {
|
|
455
|
-
(0, child_process_1.execSync)(`git worktree add "${worktreePath}" "origin/${branchName}"`, {
|
|
456
|
-
cwd: projectRoot,
|
|
457
|
-
stdio: 'pipe',
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
// Copy env files and symlink node_modules
|
|
461
|
-
(0, helpers_1.copyEnvFiles)(projectRoot, worktreePath);
|
|
462
|
-
(0, helpers_1.symlinkNodeModules)(projectRoot, worktreePath);
|
|
463
|
-
}
|
|
464
|
-
catch (error) {
|
|
465
|
-
console.log(chalk_1.default.yellow(`ā ļø Could not create worktree for PR #${pr.number}`));
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
else {
|
|
470
|
-
// Pull latest changes
|
|
471
|
-
try {
|
|
472
|
-
(0, child_process_1.execSync)('git pull --quiet', { cwd: worktreePath, stdio: 'pipe' });
|
|
473
|
-
}
|
|
474
|
-
catch {
|
|
475
|
-
// Ignore pull errors
|
|
476
|
-
}
|
|
477
|
-
}
|
|
371
|
+
async function launchReviewForPR(pr, projectRoot, _projectName, options = {}) {
|
|
478
372
|
// Get the diff for context
|
|
479
373
|
let diffContent = '';
|
|
480
374
|
try {
|
|
@@ -587,18 +481,19 @@ The \`suggestion\` code block creates a "Commit suggestion" button on GitHub.
|
|
|
587
481
|
${diffContent ? `\n\`\`\`diff\n${diffContent.slice(0, 50000)}\n\`\`\`\n` : 'Run `gh pr diff ' + pr.number + '` to see the changes.'}
|
|
588
482
|
|
|
589
483
|
Start by examining the diff and the changed files, then provide your review.`;
|
|
590
|
-
// Write prompt to
|
|
591
|
-
const
|
|
484
|
+
// Write prompt and runner script to temp directory
|
|
485
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cis-review-'));
|
|
486
|
+
const promptFile = path.join(tempDir, '.claude-review-prompt.txt');
|
|
592
487
|
fs.writeFileSync(promptFile, prompt);
|
|
593
488
|
const botTokenEnv = botToken ? `export BOT_TOKEN="${botToken}"\nexport GH_TOKEN="${botToken}"` : '# No bot token configured';
|
|
594
489
|
const botNote = botToken
|
|
595
490
|
? 'Using bot token for reviews (can approve/request changes)'
|
|
596
491
|
: 'No bot token - using your account (may have limitations on own PRs)';
|
|
597
492
|
// Create runner script for review
|
|
598
|
-
const runnerScript = path.join(
|
|
493
|
+
const runnerScript = path.join(tempDir, '.claude-review-runner.sh');
|
|
599
494
|
const escapedTitle = pr.title.replace(/"/g, '\\"').slice(0, 50);
|
|
600
495
|
const runnerContent = `#!/bin/bash
|
|
601
|
-
cd "${
|
|
496
|
+
cd "${projectRoot}"
|
|
602
497
|
|
|
603
498
|
# Set bot token if configured
|
|
604
499
|
${botTokenEnv}
|
|
@@ -615,14 +510,15 @@ echo "${botNote}"
|
|
|
615
510
|
echo ""
|
|
616
511
|
echo "Claude will review the PR and post suggestions."
|
|
617
512
|
echo "You can commit suggestions directly on GitHub."
|
|
513
|
+
${options.merge ? 'echo "\\nš Auto-merge enabled: will merge if approved."' : ''}
|
|
618
514
|
echo "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"
|
|
619
515
|
echo ""
|
|
620
516
|
|
|
621
517
|
# Run Claude interactively
|
|
622
518
|
claude --dangerously-skip-permissions "$(cat '${promptFile}')"
|
|
623
519
|
|
|
624
|
-
# Clean up
|
|
625
|
-
rm -
|
|
520
|
+
# Clean up temp files
|
|
521
|
+
rm -rf '${tempDir}'
|
|
626
522
|
|
|
627
523
|
echo ""
|
|
628
524
|
echo "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"
|
|
@@ -635,30 +531,15 @@ REVIEW_STATUS=$(gh pr view ${pr.number} --json reviewDecision --jq '.reviewDecis
|
|
|
635
531
|
if [ "$REVIEW_STATUS" = "APPROVED" ]; then
|
|
636
532
|
echo "ā
PR #${pr.number} is approved!"
|
|
637
533
|
echo ""
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
if [[ \\$REPLY =~ ^[Yy]$ ]]; then
|
|
534
|
+
${options.merge ? ` echo "š¤ Auto-merging PR #${pr.number}..."
|
|
535
|
+
if gh pr merge ${pr.number} --squash --delete-branch; then
|
|
641
536
|
echo ""
|
|
642
|
-
echo "
|
|
643
|
-
if gh pr merge ${pr.number} --squash --delete-branch; then
|
|
644
|
-
echo ""
|
|
645
|
-
echo "ā
PR merged successfully!"
|
|
646
|
-
echo ""
|
|
647
|
-
echo "Cleaning up worktree..."
|
|
648
|
-
cd "${projectRoot}"
|
|
649
|
-
git worktree remove "${worktreePath}" --force 2>/dev/null || rm -rf "${worktreePath}"
|
|
650
|
-
git worktree prune 2>/dev/null
|
|
651
|
-
git branch -D "${branchName}" 2>/dev/null
|
|
652
|
-
echo "ā
Cleanup complete!"
|
|
653
|
-
else
|
|
654
|
-
echo ""
|
|
655
|
-
echo "ā ļø Merge failed. You can try manually: gh pr merge ${pr.number} --squash"
|
|
656
|
-
fi
|
|
537
|
+
echo "ā
PR merged successfully!"
|
|
657
538
|
else
|
|
658
539
|
echo ""
|
|
659
|
-
echo "
|
|
660
|
-
|
|
661
|
-
|
|
540
|
+
echo "ā ļø Merge failed. You can try manually: gh pr merge ${pr.number} --squash"
|
|
541
|
+
fi` : ` echo "View PR: gh pr view ${pr.number} --web"
|
|
542
|
+
echo "To merge: cis merge"`}
|
|
662
543
|
else
|
|
663
544
|
echo "View PR: gh pr view ${pr.number} --web"
|
|
664
545
|
fi
|
package/dist/index.js
CHANGED
|
@@ -157,17 +157,18 @@ program
|
|
|
157
157
|
program
|
|
158
158
|
.command('review [issue]')
|
|
159
159
|
.description('Review PRs with Claude and post suggestions')
|
|
160
|
-
.
|
|
160
|
+
.option('-m, --merge', 'Automatically merge PR if approved')
|
|
161
|
+
.action(async (issue, options) => {
|
|
161
162
|
if (issue) {
|
|
162
163
|
const issueNumber = parseInt(issue, 10);
|
|
163
164
|
if (isNaN(issueNumber)) {
|
|
164
165
|
console.log(chalk_1.default.red(`ā Invalid issue number: ${issue}`));
|
|
165
166
|
process.exit(1);
|
|
166
167
|
}
|
|
167
|
-
await (0, review_1.reviewCommand)(issueNumber);
|
|
168
|
+
await (0, review_1.reviewCommand)(issueNumber, { merge: options.merge });
|
|
168
169
|
}
|
|
169
170
|
else {
|
|
170
|
-
await (0, review_1.selectReviewCommand)();
|
|
171
|
+
await (0, review_1.selectReviewCommand)({ merge: options.merge });
|
|
171
172
|
}
|
|
172
173
|
});
|
|
173
174
|
// Config command - manage settings
|