claude-issue-solver 1.7.2 → 1.8.1

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.
@@ -1,2 +1,3 @@
1
1
  export declare function cleanAllCommand(): Promise<void>;
2
2
  export declare function cleanCommand(issueNumber: number): Promise<void>;
3
+ export declare function cleanMergedCommand(): Promise<void>;
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.cleanAllCommand = cleanAllCommand;
40
40
  exports.cleanCommand = cleanCommand;
41
+ exports.cleanMergedCommand = cleanMergedCommand;
41
42
  const chalk_1 = __importDefault(require("chalk"));
42
43
  const ora_1 = __importDefault(require("ora"));
43
44
  const inquirer_1 = __importDefault(require("inquirer"));
@@ -222,11 +223,19 @@ function getIssueWorktrees() {
222
223
  }
223
224
  async function cleanAllCommand() {
224
225
  const projectRoot = (0, git_1.getProjectRoot)();
226
+ const currentDir = process.cwd();
225
227
  const worktrees = getIssueWorktrees();
226
228
  if (worktrees.length === 0) {
227
229
  console.log(chalk_1.default.yellow('\nNo issue worktrees found.'));
228
230
  return;
229
231
  }
232
+ // Warn if user is inside a worktree that might be deleted
233
+ const inWorktree = worktrees.find((wt) => currentDir.startsWith(wt.path));
234
+ if (inWorktree) {
235
+ console.log(chalk_1.default.yellow(`\n⚠️ You are inside worktree #${inWorktree.issueNumber}`));
236
+ console.log(chalk_1.default.yellow(` Run this command from the main project directory for best results.`));
237
+ console.log(chalk_1.default.dim(` cd ${projectRoot}\n`));
238
+ }
230
239
  // Fetch status for all worktrees
231
240
  const statusSpinner = (0, ora_1.default)('Fetching issue and PR status...').start();
232
241
  const worktreesWithStatus = worktrees.map((wt) => ({
@@ -492,3 +501,133 @@ async function cleanCommand(issueNumber) {
492
501
  console.log();
493
502
  console.log(chalk_1.default.green('✅ Cleanup complete!'));
494
503
  }
504
+ async function cleanMergedCommand() {
505
+ const projectRoot = (0, git_1.getProjectRoot)();
506
+ const currentDir = process.cwd();
507
+ const worktrees = getIssueWorktrees();
508
+ if (worktrees.length === 0) {
509
+ console.log(chalk_1.default.yellow('\nNo issue worktrees found.'));
510
+ return;
511
+ }
512
+ // Warn if user is inside a worktree that might be deleted
513
+ const inWorktree = worktrees.find((wt) => currentDir.startsWith(wt.path));
514
+ if (inWorktree) {
515
+ console.log(chalk_1.default.yellow(`\n⚠️ You are inside worktree #${inWorktree.issueNumber}`));
516
+ console.log(chalk_1.default.yellow(` Run this command from the main project directory for best results.`));
517
+ console.log(chalk_1.default.dim(` cd ${projectRoot}\n`));
518
+ }
519
+ // Fetch status for all worktrees
520
+ const statusSpinner = (0, ora_1.default)('Fetching PR status...').start();
521
+ const worktreesWithStatus = worktrees.map((wt) => ({
522
+ ...wt,
523
+ issueStatus: (0, github_1.getIssueStatus)(parseInt(wt.issueNumber, 10)),
524
+ prStatus: wt.branch ? (0, github_1.getPRForBranch)(wt.branch) : null,
525
+ }));
526
+ statusSpinner.stop();
527
+ // Filter to only merged PRs
528
+ const mergedWorktrees = worktreesWithStatus.filter((wt) => wt.prStatus?.state === 'merged');
529
+ if (mergedWorktrees.length === 0) {
530
+ console.log(chalk_1.default.yellow('\nNo worktrees with merged PRs found.'));
531
+ // Show what's available
532
+ if (worktreesWithStatus.length > 0) {
533
+ console.log(chalk_1.default.dim('\nExisting worktrees:'));
534
+ for (const wt of worktreesWithStatus) {
535
+ const status = getStatusLabel(wt);
536
+ console.log(` ${chalk_1.default.cyan(`#${wt.issueNumber}`)}\t${status}`);
537
+ }
538
+ }
539
+ return;
540
+ }
541
+ console.log(chalk_1.default.bold(`\n🧹 Cleaning ${mergedWorktrees.length} worktree(s) with merged PRs:\n`));
542
+ for (const wt of mergedWorktrees) {
543
+ console.log(` ${chalk_1.default.cyan(`#${wt.issueNumber}`)}\t${chalk_1.default.green('✓ PR merged')}`);
544
+ if (wt.branch) {
545
+ console.log(chalk_1.default.dim(` \t${wt.branch}`));
546
+ }
547
+ }
548
+ console.log();
549
+ for (const wt of mergedWorktrees) {
550
+ const spinner = (0, ora_1.default)(`Cleaning issue #${wt.issueNumber}...`).start();
551
+ try {
552
+ // Close terminal and VS Code windows for this worktree
553
+ try {
554
+ closeWindowsWithPath(wt.path, wt.issueNumber);
555
+ await new Promise((resolve) => setTimeout(resolve, 500));
556
+ }
557
+ catch {
558
+ // Ignore errors closing windows
559
+ }
560
+ // Remove worktree/folder
561
+ const isOrphaned = !wt.branch;
562
+ // Try git worktree remove first (only if not orphaned)
563
+ if (!isOrphaned && fs.existsSync(wt.path)) {
564
+ try {
565
+ (0, child_process_1.execSync)(`git worktree remove "${wt.path}" --force`, {
566
+ cwd: projectRoot,
567
+ stdio: 'pipe',
568
+ });
569
+ }
570
+ catch {
571
+ // Ignore - we'll force delete below
572
+ }
573
+ }
574
+ // Always try to force delete the folder
575
+ if (fs.existsSync(wt.path)) {
576
+ try {
577
+ (0, child_process_1.execSync)(`/bin/rm -rf "${wt.path}"`, { stdio: 'pipe', timeout: 10000 });
578
+ }
579
+ catch {
580
+ try {
581
+ (0, child_process_1.execSync)(`rm -rf "${wt.path}"`, { shell: '/bin/bash', stdio: 'pipe', timeout: 10000 });
582
+ }
583
+ catch {
584
+ try {
585
+ fs.rmSync(wt.path, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
586
+ }
587
+ catch {
588
+ // Will report failure below
589
+ }
590
+ }
591
+ }
592
+ }
593
+ // Prune git worktrees
594
+ try {
595
+ (0, child_process_1.execSync)('git worktree prune', { cwd: projectRoot, stdio: 'pipe' });
596
+ }
597
+ catch {
598
+ // Ignore
599
+ }
600
+ // Delete branch (if we have one)
601
+ if (wt.branch) {
602
+ try {
603
+ (0, child_process_1.execSync)(`git branch -D "${wt.branch}"`, {
604
+ cwd: projectRoot,
605
+ stdio: 'pipe',
606
+ });
607
+ }
608
+ catch {
609
+ // Branch may already be deleted
610
+ }
611
+ }
612
+ // Check if cleanup was successful
613
+ if (fs.existsSync(wt.path)) {
614
+ spinner.warn(`Cleaned issue #${wt.issueNumber} (folder may remain: ${wt.path})`);
615
+ }
616
+ else {
617
+ spinner.succeed(`Cleaned issue #${wt.issueNumber}`);
618
+ }
619
+ }
620
+ catch (error) {
621
+ spinner.fail(`Failed to clean issue #${wt.issueNumber}: ${error}`);
622
+ }
623
+ }
624
+ // Prune stale worktrees
625
+ try {
626
+ (0, child_process_1.execSync)('git worktree prune', { cwd: projectRoot, stdio: 'pipe' });
627
+ }
628
+ catch {
629
+ // May fail if current directory was deleted
630
+ }
631
+ console.log();
632
+ console.log(chalk_1.default.green(`✅ Cleaned up ${mergedWorktrees.length} merged worktree(s)!`));
633
+ }
package/dist/index.js CHANGED
@@ -76,9 +76,13 @@ program
76
76
  .command('clean [issue]')
77
77
  .alias('rm')
78
78
  .option('-a, --all', 'Clean all issue worktrees')
79
- .description('Remove worktree and branch for an issue (or all with --all)')
79
+ .option('-m, --merged', 'Clean only worktrees with merged PRs (no confirmation)')
80
+ .description('Remove worktree and branch for an issue (or all with --all, or merged with --merged)')
80
81
  .action(async (issue, options) => {
81
- if (options.all) {
82
+ if (options.merged) {
83
+ await (0, clean_1.cleanMergedCommand)();
84
+ }
85
+ else if (options.all) {
82
86
  await (0, clean_1.cleanAllCommand)();
83
87
  }
84
88
  else if (issue) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-issue-solver",
3
- "version": "1.7.2",
3
+ "version": "1.8.1",
4
4
  "description": "Automatically solve GitHub issues using Claude Code",
5
5
  "main": "dist/index.js",
6
6
  "bin": {