claude-issue-solver 1.7.1 โ 1.8.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/clean.d.ts +1 -0
- package/dist/commands/clean.js +183 -35
- package/dist/index.js +6 -2
- package/package.json +1 -1
package/dist/commands/clean.d.ts
CHANGED
package/dist/commands/clean.js
CHANGED
|
@@ -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"));
|
|
@@ -89,16 +90,31 @@ function closeWindowsWithPath(folderPath, issueNumber) {
|
|
|
89
90
|
// Terminal not running or no matching windows
|
|
90
91
|
}
|
|
91
92
|
// Try to close VS Code windows with this path
|
|
93
|
+
// Method 1: AppleScript to close windows matching the folder name
|
|
92
94
|
try {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
(0, child_process_1.execSync)(`osascript -e '
|
|
96
|
+
tell application "System Events"
|
|
97
|
+
if exists process "Code" then
|
|
98
|
+
tell process "Code"
|
|
99
|
+
set windowList to every window
|
|
100
|
+
repeat with w in windowList
|
|
101
|
+
try
|
|
102
|
+
set windowName to name of w
|
|
103
|
+
if windowName contains "${folderName}" then
|
|
104
|
+
perform action "AXPress" of (first button of w whose subrole is "AXCloseButton")
|
|
105
|
+
delay 0.2
|
|
106
|
+
end if
|
|
107
|
+
end try
|
|
108
|
+
end repeat
|
|
109
|
+
end tell
|
|
110
|
+
end if
|
|
111
|
+
end tell
|
|
112
|
+
'`, { stdio: 'pipe', timeout: 5000 });
|
|
98
113
|
}
|
|
99
114
|
catch {
|
|
100
|
-
// VS Code
|
|
115
|
+
// VS Code not running or no matching windows
|
|
101
116
|
}
|
|
117
|
+
// Method 2: Also try matching the issue number in window title
|
|
102
118
|
try {
|
|
103
119
|
(0, child_process_1.execSync)(`osascript -e '
|
|
104
120
|
tell application "System Events"
|
|
@@ -106,15 +122,18 @@ function closeWindowsWithPath(folderPath, issueNumber) {
|
|
|
106
122
|
tell process "Code"
|
|
107
123
|
set windowList to every window
|
|
108
124
|
repeat with w in windowList
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
125
|
+
try
|
|
126
|
+
set windowName to name of w
|
|
127
|
+
if windowName contains "${issuePattern}" then
|
|
128
|
+
perform action "AXPress" of (first button of w whose subrole is "AXCloseButton")
|
|
129
|
+
delay 0.2
|
|
130
|
+
end if
|
|
131
|
+
end try
|
|
113
132
|
end repeat
|
|
114
133
|
end tell
|
|
115
134
|
end if
|
|
116
135
|
end tell
|
|
117
|
-
'`, { stdio: 'pipe' });
|
|
136
|
+
'`, { stdio: 'pipe', timeout: 5000 });
|
|
118
137
|
}
|
|
119
138
|
catch {
|
|
120
139
|
// VS Code not running or no matching windows
|
|
@@ -254,40 +273,46 @@ async function cleanAllCommand() {
|
|
|
254
273
|
}
|
|
255
274
|
// Remove worktree/folder
|
|
256
275
|
const isOrphaned = !wt.branch;
|
|
276
|
+
// Try git worktree remove first (only if not orphaned)
|
|
277
|
+
if (!isOrphaned && fs.existsSync(wt.path)) {
|
|
278
|
+
try {
|
|
279
|
+
(0, child_process_1.execSync)(`git worktree remove "${wt.path}" --force`, {
|
|
280
|
+
cwd: projectRoot,
|
|
281
|
+
stdio: 'pipe',
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
// Ignore - we'll force delete below
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Always try to force delete the folder
|
|
257
289
|
if (fs.existsSync(wt.path)) {
|
|
258
|
-
// Try
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
(0, child_process_1.execSync)(`git worktree remove "${wt.path}" --force`, {
|
|
262
|
-
cwd: projectRoot,
|
|
263
|
-
stdio: 'pipe',
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
catch {
|
|
267
|
-
// Ignore - we'll force delete below if needed
|
|
268
|
-
}
|
|
290
|
+
// Try multiple deletion methods
|
|
291
|
+
try {
|
|
292
|
+
(0, child_process_1.execSync)(`/bin/rm -rf "${wt.path}"`, { stdio: 'pipe', timeout: 10000 });
|
|
269
293
|
}
|
|
270
|
-
|
|
271
|
-
|
|
294
|
+
catch {
|
|
295
|
+
// Fallback 1: try with shell
|
|
272
296
|
try {
|
|
273
|
-
(0, child_process_1.execSync)(
|
|
297
|
+
(0, child_process_1.execSync)(`rm -rf "${wt.path}"`, { shell: '/bin/bash', stdio: 'pipe', timeout: 10000 });
|
|
274
298
|
}
|
|
275
299
|
catch {
|
|
300
|
+
// Fallback 2: Node.js rmSync
|
|
276
301
|
try {
|
|
277
|
-
fs.rmSync(wt.path, { recursive: true, force: true, maxRetries:
|
|
302
|
+
fs.rmSync(wt.path, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
|
|
278
303
|
}
|
|
279
304
|
catch {
|
|
280
|
-
//
|
|
305
|
+
// Will report failure below
|
|
281
306
|
}
|
|
282
307
|
}
|
|
283
308
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
309
|
+
}
|
|
310
|
+
// Prune git worktrees
|
|
311
|
+
try {
|
|
312
|
+
(0, child_process_1.execSync)('git worktree prune', { cwd: projectRoot, stdio: 'pipe' });
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
// Ignore
|
|
291
316
|
}
|
|
292
317
|
// Delete branch (if we have one)
|
|
293
318
|
if (wt.branch) {
|
|
@@ -301,7 +326,13 @@ async function cleanAllCommand() {
|
|
|
301
326
|
// Branch may already be deleted
|
|
302
327
|
}
|
|
303
328
|
}
|
|
304
|
-
|
|
329
|
+
// Check if cleanup was successful
|
|
330
|
+
if (fs.existsSync(wt.path)) {
|
|
331
|
+
spinner.warn(`Cleaned issue #${wt.issueNumber} (folder may remain: ${wt.path})`);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
spinner.succeed(`Cleaned issue #${wt.issueNumber}`);
|
|
335
|
+
}
|
|
305
336
|
}
|
|
306
337
|
catch (error) {
|
|
307
338
|
spinner.fail(`Failed to clean issue #${wt.issueNumber}: ${error}`);
|
|
@@ -462,3 +493,120 @@ async function cleanCommand(issueNumber) {
|
|
|
462
493
|
console.log();
|
|
463
494
|
console.log(chalk_1.default.green('โ
Cleanup complete!'));
|
|
464
495
|
}
|
|
496
|
+
async function cleanMergedCommand() {
|
|
497
|
+
const projectRoot = (0, git_1.getProjectRoot)();
|
|
498
|
+
const worktrees = getIssueWorktrees();
|
|
499
|
+
if (worktrees.length === 0) {
|
|
500
|
+
console.log(chalk_1.default.yellow('\nNo issue worktrees found.'));
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
// Fetch status for all worktrees
|
|
504
|
+
const statusSpinner = (0, ora_1.default)('Fetching PR status...').start();
|
|
505
|
+
const worktreesWithStatus = worktrees.map((wt) => ({
|
|
506
|
+
...wt,
|
|
507
|
+
issueStatus: (0, github_1.getIssueStatus)(parseInt(wt.issueNumber, 10)),
|
|
508
|
+
prStatus: wt.branch ? (0, github_1.getPRForBranch)(wt.branch) : null,
|
|
509
|
+
}));
|
|
510
|
+
statusSpinner.stop();
|
|
511
|
+
// Filter to only merged PRs
|
|
512
|
+
const mergedWorktrees = worktreesWithStatus.filter((wt) => wt.prStatus?.state === 'merged');
|
|
513
|
+
if (mergedWorktrees.length === 0) {
|
|
514
|
+
console.log(chalk_1.default.yellow('\nNo worktrees with merged PRs found.'));
|
|
515
|
+
// Show what's available
|
|
516
|
+
if (worktreesWithStatus.length > 0) {
|
|
517
|
+
console.log(chalk_1.default.dim('\nExisting worktrees:'));
|
|
518
|
+
for (const wt of worktreesWithStatus) {
|
|
519
|
+
const status = getStatusLabel(wt);
|
|
520
|
+
console.log(` ${chalk_1.default.cyan(`#${wt.issueNumber}`)}\t${status}`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
console.log(chalk_1.default.bold(`\n๐งน Cleaning ${mergedWorktrees.length} worktree(s) with merged PRs:\n`));
|
|
526
|
+
for (const wt of mergedWorktrees) {
|
|
527
|
+
console.log(` ${chalk_1.default.cyan(`#${wt.issueNumber}`)}\t${chalk_1.default.green('โ PR merged')}`);
|
|
528
|
+
if (wt.branch) {
|
|
529
|
+
console.log(chalk_1.default.dim(` \t${wt.branch}`));
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
console.log();
|
|
533
|
+
for (const wt of mergedWorktrees) {
|
|
534
|
+
const spinner = (0, ora_1.default)(`Cleaning issue #${wt.issueNumber}...`).start();
|
|
535
|
+
try {
|
|
536
|
+
// Close terminal and VS Code windows for this worktree
|
|
537
|
+
try {
|
|
538
|
+
closeWindowsWithPath(wt.path, wt.issueNumber);
|
|
539
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
540
|
+
}
|
|
541
|
+
catch {
|
|
542
|
+
// Ignore errors closing windows
|
|
543
|
+
}
|
|
544
|
+
// Remove worktree/folder
|
|
545
|
+
const isOrphaned = !wt.branch;
|
|
546
|
+
// Try git worktree remove first (only if not orphaned)
|
|
547
|
+
if (!isOrphaned && fs.existsSync(wt.path)) {
|
|
548
|
+
try {
|
|
549
|
+
(0, child_process_1.execSync)(`git worktree remove "${wt.path}" --force`, {
|
|
550
|
+
cwd: projectRoot,
|
|
551
|
+
stdio: 'pipe',
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
catch {
|
|
555
|
+
// Ignore - we'll force delete below
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
// Always try to force delete the folder
|
|
559
|
+
if (fs.existsSync(wt.path)) {
|
|
560
|
+
try {
|
|
561
|
+
(0, child_process_1.execSync)(`/bin/rm -rf "${wt.path}"`, { stdio: 'pipe', timeout: 10000 });
|
|
562
|
+
}
|
|
563
|
+
catch {
|
|
564
|
+
try {
|
|
565
|
+
(0, child_process_1.execSync)(`rm -rf "${wt.path}"`, { shell: '/bin/bash', stdio: 'pipe', timeout: 10000 });
|
|
566
|
+
}
|
|
567
|
+
catch {
|
|
568
|
+
try {
|
|
569
|
+
fs.rmSync(wt.path, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
|
|
570
|
+
}
|
|
571
|
+
catch {
|
|
572
|
+
// Will report failure below
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
// Prune git worktrees
|
|
578
|
+
try {
|
|
579
|
+
(0, child_process_1.execSync)('git worktree prune', { cwd: projectRoot, stdio: 'pipe' });
|
|
580
|
+
}
|
|
581
|
+
catch {
|
|
582
|
+
// Ignore
|
|
583
|
+
}
|
|
584
|
+
// Delete branch (if we have one)
|
|
585
|
+
if (wt.branch) {
|
|
586
|
+
try {
|
|
587
|
+
(0, child_process_1.execSync)(`git branch -D "${wt.branch}"`, {
|
|
588
|
+
cwd: projectRoot,
|
|
589
|
+
stdio: 'pipe',
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
catch {
|
|
593
|
+
// Branch may already be deleted
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
// Check if cleanup was successful
|
|
597
|
+
if (fs.existsSync(wt.path)) {
|
|
598
|
+
spinner.warn(`Cleaned issue #${wt.issueNumber} (folder may remain: ${wt.path})`);
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
spinner.succeed(`Cleaned issue #${wt.issueNumber}`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
catch (error) {
|
|
605
|
+
spinner.fail(`Failed to clean issue #${wt.issueNumber}: ${error}`);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
// Prune stale worktrees
|
|
609
|
+
(0, child_process_1.execSync)('git worktree prune', { cwd: projectRoot, stdio: 'pipe' });
|
|
610
|
+
console.log();
|
|
611
|
+
console.log(chalk_1.default.green(`โ
Cleaned up ${mergedWorktrees.length} merged worktree(s)!`));
|
|
612
|
+
}
|
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
|
-
.
|
|
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.
|
|
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) {
|