claude-issue-solver 1.6.3 ā 1.6.5
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.js +126 -56
- package/package.json +1 -1
package/dist/commands/clean.js
CHANGED
|
@@ -46,11 +46,12 @@ const path = __importStar(require("path"));
|
|
|
46
46
|
const os = __importStar(require("os"));
|
|
47
47
|
const child_process_1 = require("child_process");
|
|
48
48
|
const git_1 = require("../utils/git");
|
|
49
|
-
function closeWindowsWithPath(folderPath) {
|
|
49
|
+
function closeWindowsWithPath(folderPath, issueNumber) {
|
|
50
50
|
if (os.platform() !== 'darwin')
|
|
51
51
|
return;
|
|
52
52
|
const folderName = path.basename(folderPath);
|
|
53
|
-
|
|
53
|
+
const issuePattern = `Issue #${issueNumber}`;
|
|
54
|
+
// Try to close iTerm2 tabs/windows with this path or issue number
|
|
54
55
|
try {
|
|
55
56
|
(0, child_process_1.execSync)(`osascript -e '
|
|
56
57
|
tell application "iTerm"
|
|
@@ -58,7 +59,7 @@ function closeWindowsWithPath(folderPath) {
|
|
|
58
59
|
repeat with t in tabs of w
|
|
59
60
|
repeat with s in sessions of t
|
|
60
61
|
set sessionName to name of s
|
|
61
|
-
if sessionName contains "${folderName}" then
|
|
62
|
+
if sessionName contains "${folderName}" or sessionName contains "${issuePattern}" then
|
|
62
63
|
close s
|
|
63
64
|
end if
|
|
64
65
|
end repeat
|
|
@@ -70,12 +71,13 @@ function closeWindowsWithPath(folderPath) {
|
|
|
70
71
|
catch {
|
|
71
72
|
// iTerm not running or no matching sessions
|
|
72
73
|
}
|
|
73
|
-
// Try to close Terminal.app windows with this path
|
|
74
|
+
// Try to close Terminal.app windows with this path or issue number
|
|
74
75
|
try {
|
|
75
76
|
(0, child_process_1.execSync)(`osascript -e '
|
|
76
77
|
tell application "Terminal"
|
|
77
78
|
repeat with w in windows
|
|
78
|
-
|
|
79
|
+
set windowName to name of w
|
|
80
|
+
if windowName contains "${folderName}" or windowName contains "${issuePattern}" then
|
|
79
81
|
close w
|
|
80
82
|
end if
|
|
81
83
|
end repeat
|
|
@@ -86,6 +88,16 @@ function closeWindowsWithPath(folderPath) {
|
|
|
86
88
|
// Terminal not running or no matching windows
|
|
87
89
|
}
|
|
88
90
|
// Try to close VS Code windows with this path
|
|
91
|
+
try {
|
|
92
|
+
// Use VS Code CLI to close the folder if it's open
|
|
93
|
+
(0, child_process_1.execSync)(`code --folder-uri "file://${folderPath}" --command "workbench.action.closeWindow"`, {
|
|
94
|
+
stdio: 'pipe',
|
|
95
|
+
timeout: 3000
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// VS Code CLI method failed, try AppleScript
|
|
100
|
+
}
|
|
89
101
|
try {
|
|
90
102
|
(0, child_process_1.execSync)(`osascript -e '
|
|
91
103
|
tell application "System Events"
|
|
@@ -112,30 +124,57 @@ function getIssueWorktrees() {
|
|
|
112
124
|
const projectName = (0, git_1.getProjectName)();
|
|
113
125
|
const parentDir = path.dirname(projectRoot);
|
|
114
126
|
const worktrees = [];
|
|
127
|
+
const foundPaths = new Set();
|
|
115
128
|
// Get all worktrees from git
|
|
116
129
|
const output = (0, git_1.exec)('git worktree list --porcelain', projectRoot);
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
130
|
+
if (output) {
|
|
131
|
+
const lines = output.split('\n');
|
|
132
|
+
let currentPath = '';
|
|
133
|
+
let currentBranch = '';
|
|
134
|
+
for (const line of lines) {
|
|
135
|
+
if (line.startsWith('worktree ')) {
|
|
136
|
+
currentPath = line.replace('worktree ', '');
|
|
137
|
+
}
|
|
138
|
+
else if (line.startsWith('branch refs/heads/')) {
|
|
139
|
+
currentBranch = line.replace('branch refs/heads/', '');
|
|
140
|
+
// Check if this is an issue branch
|
|
141
|
+
const match = currentBranch.match(/^issue-(\d+)-/);
|
|
142
|
+
if (match && currentPath.includes(`${projectName}-issue-`)) {
|
|
143
|
+
worktrees.push({
|
|
144
|
+
path: currentPath,
|
|
145
|
+
branch: currentBranch,
|
|
146
|
+
issueNumber: match[1],
|
|
147
|
+
});
|
|
148
|
+
foundPaths.add(currentPath);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
125
151
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
152
|
+
}
|
|
153
|
+
// Also check for orphaned folders (folders that exist but aren't in git worktree list)
|
|
154
|
+
// This can happen when git worktree remove fails but the folder remains
|
|
155
|
+
try {
|
|
156
|
+
const folderPattern = `${projectName}-issue-`;
|
|
157
|
+
const entries = fs.readdirSync(parentDir, { withFileTypes: true });
|
|
158
|
+
for (const entry of entries) {
|
|
159
|
+
if (entry.isDirectory() && entry.name.startsWith(folderPattern)) {
|
|
160
|
+
const folderPath = path.join(parentDir, entry.name);
|
|
161
|
+
if (!foundPaths.has(folderPath)) {
|
|
162
|
+
// Extract issue number from folder name (e.g., "project-issue-38-slug")
|
|
163
|
+
const match = entry.name.match(new RegExp(`${projectName}-issue-(\\d+)-`));
|
|
164
|
+
if (match) {
|
|
165
|
+
worktrees.push({
|
|
166
|
+
path: folderPath,
|
|
167
|
+
branch: '', // No branch known for orphaned folders
|
|
168
|
+
issueNumber: match[1],
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
136
172
|
}
|
|
137
173
|
}
|
|
138
174
|
}
|
|
175
|
+
catch {
|
|
176
|
+
// Ignore errors reading parent directory
|
|
177
|
+
}
|
|
139
178
|
return worktrees;
|
|
140
179
|
}
|
|
141
180
|
async function cleanAllCommand() {
|
|
@@ -147,7 +186,7 @@ async function cleanAllCommand() {
|
|
|
147
186
|
}
|
|
148
187
|
console.log(chalk_1.default.bold('\nš§¹ Found issue worktrees:\n'));
|
|
149
188
|
for (const wt of worktrees) {
|
|
150
|
-
console.log(` ${chalk_1.default.cyan(`#${wt.issueNumber}`)}\t${wt.branch}`);
|
|
189
|
+
console.log(` ${chalk_1.default.cyan(`#${wt.issueNumber}`)}\t${wt.branch || chalk_1.default.yellow('(orphaned folder)')}`);
|
|
151
190
|
console.log(chalk_1.default.dim(` \t${wt.path}`));
|
|
152
191
|
console.log();
|
|
153
192
|
}
|
|
@@ -169,7 +208,9 @@ async function cleanAllCommand() {
|
|
|
169
208
|
try {
|
|
170
209
|
// Close terminal and VS Code windows for this worktree
|
|
171
210
|
try {
|
|
172
|
-
closeWindowsWithPath(wt.path);
|
|
211
|
+
closeWindowsWithPath(wt.path, wt.issueNumber);
|
|
212
|
+
// Give windows time to close before removing folder
|
|
213
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
173
214
|
}
|
|
174
215
|
catch {
|
|
175
216
|
// Ignore errors closing windows
|
|
@@ -183,20 +224,28 @@ async function cleanAllCommand() {
|
|
|
183
224
|
});
|
|
184
225
|
}
|
|
185
226
|
catch {
|
|
186
|
-
// If git worktree remove fails, try removing directory manually
|
|
187
|
-
|
|
227
|
+
// If git worktree remove fails, try removing directory manually with rm -rf
|
|
228
|
+
// This handles locked files better than fs.rmSync
|
|
229
|
+
try {
|
|
230
|
+
(0, child_process_1.execSync)(`rm -rf "${wt.path}"`, { stdio: 'pipe' });
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
fs.rmSync(wt.path, { recursive: true, force: true });
|
|
234
|
+
}
|
|
188
235
|
(0, child_process_1.execSync)('git worktree prune', { cwd: projectRoot, stdio: 'pipe' });
|
|
189
236
|
}
|
|
190
237
|
}
|
|
191
|
-
// Delete branch
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
238
|
+
// Delete branch (if we have one)
|
|
239
|
+
if (wt.branch) {
|
|
240
|
+
try {
|
|
241
|
+
(0, child_process_1.execSync)(`git branch -D "${wt.branch}"`, {
|
|
242
|
+
cwd: projectRoot,
|
|
243
|
+
stdio: 'pipe',
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// Branch may already be deleted
|
|
248
|
+
}
|
|
200
249
|
}
|
|
201
250
|
spinner.succeed(`Cleaned issue #${wt.issueNumber}`);
|
|
202
251
|
}
|
|
@@ -212,7 +261,9 @@ async function cleanAllCommand() {
|
|
|
212
261
|
async function cleanCommand(issueNumber) {
|
|
213
262
|
const projectRoot = (0, git_1.getProjectRoot)();
|
|
214
263
|
const projectName = (0, git_1.getProjectName)();
|
|
264
|
+
const parentDir = path.dirname(projectRoot);
|
|
215
265
|
// Find the worktree for this issue number (don't need to fetch from GitHub)
|
|
266
|
+
// This now also includes orphaned folders
|
|
216
267
|
const worktrees = getIssueWorktrees();
|
|
217
268
|
const worktree = worktrees.find((wt) => wt.issueNumber === String(issueNumber));
|
|
218
269
|
if (!worktree) {
|
|
@@ -222,7 +273,7 @@ async function cleanCommand(issueNumber) {
|
|
|
222
273
|
const branches = output.split('\n').map((b) => b.trim().replace('* ', ''));
|
|
223
274
|
const matchingBranch = branches.find((b) => b.startsWith(branchPattern));
|
|
224
275
|
if (!matchingBranch) {
|
|
225
|
-
console.log(chalk_1.default.red(`\nā No worktree or branch found for issue #${issueNumber}`));
|
|
276
|
+
console.log(chalk_1.default.red(`\nā No worktree, folder, or branch found for issue #${issueNumber}`));
|
|
226
277
|
return;
|
|
227
278
|
}
|
|
228
279
|
// Found a branch but no worktree - just delete the branch
|
|
@@ -250,16 +301,22 @@ async function cleanCommand(issueNumber) {
|
|
|
250
301
|
}
|
|
251
302
|
const branchName = worktree.branch;
|
|
252
303
|
const worktreePath = worktree.path;
|
|
304
|
+
const isOrphaned = !branchName;
|
|
253
305
|
console.log();
|
|
254
306
|
console.log(chalk_1.default.bold(`š§¹ Cleaning up issue #${issueNumber}`));
|
|
255
|
-
|
|
256
|
-
|
|
307
|
+
if (isOrphaned) {
|
|
308
|
+
console.log(chalk_1.default.yellow(` (Orphaned folder - no git worktree reference)`));
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
console.log(chalk_1.default.dim(` Branch: ${branchName}`));
|
|
312
|
+
}
|
|
313
|
+
console.log(chalk_1.default.dim(` Folder: ${worktreePath}`));
|
|
257
314
|
console.log();
|
|
258
315
|
const { confirm } = await inquirer_1.default.prompt([
|
|
259
316
|
{
|
|
260
317
|
type: 'confirm',
|
|
261
318
|
name: 'confirm',
|
|
262
|
-
message: 'Remove worktree and delete branch?',
|
|
319
|
+
message: isOrphaned ? 'Remove orphaned folder?' : 'Remove worktree and delete branch?',
|
|
263
320
|
default: false,
|
|
264
321
|
},
|
|
265
322
|
]);
|
|
@@ -268,11 +325,15 @@ async function cleanCommand(issueNumber) {
|
|
|
268
325
|
return;
|
|
269
326
|
}
|
|
270
327
|
// Close terminal and VS Code windows for this worktree
|
|
328
|
+
const windowSpinner = (0, ora_1.default)('Closing terminal and VS Code windows...').start();
|
|
271
329
|
try {
|
|
272
|
-
closeWindowsWithPath(worktreePath);
|
|
330
|
+
closeWindowsWithPath(worktreePath, String(issueNumber));
|
|
331
|
+
// Give windows time to close before removing folder
|
|
332
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
333
|
+
windowSpinner.succeed('Windows closed');
|
|
273
334
|
}
|
|
274
335
|
catch {
|
|
275
|
-
|
|
336
|
+
windowSpinner.warn('Could not close some windows');
|
|
276
337
|
}
|
|
277
338
|
// Remove worktree
|
|
278
339
|
if (fs.existsSync(worktreePath)) {
|
|
@@ -285,28 +346,37 @@ async function cleanCommand(issueNumber) {
|
|
|
285
346
|
worktreeSpinner.succeed('Worktree removed');
|
|
286
347
|
}
|
|
287
348
|
catch {
|
|
288
|
-
// If git worktree remove fails, try removing directory manually
|
|
349
|
+
// If git worktree remove fails, try removing directory manually with rm -rf
|
|
289
350
|
try {
|
|
290
|
-
|
|
351
|
+
(0, child_process_1.execSync)(`rm -rf "${worktreePath}"`, { stdio: 'pipe' });
|
|
291
352
|
(0, child_process_1.execSync)('git worktree prune', { cwd: projectRoot, stdio: 'pipe' });
|
|
292
353
|
worktreeSpinner.succeed('Worktree removed (manually)');
|
|
293
354
|
}
|
|
294
355
|
catch {
|
|
295
|
-
|
|
356
|
+
try {
|
|
357
|
+
fs.rmSync(worktreePath, { recursive: true, force: true });
|
|
358
|
+
(0, child_process_1.execSync)('git worktree prune', { cwd: projectRoot, stdio: 'pipe' });
|
|
359
|
+
worktreeSpinner.succeed('Worktree removed (manually)');
|
|
360
|
+
}
|
|
361
|
+
catch {
|
|
362
|
+
worktreeSpinner.warn('Could not remove worktree directory');
|
|
363
|
+
}
|
|
296
364
|
}
|
|
297
365
|
}
|
|
298
366
|
}
|
|
299
|
-
// Delete branch
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
367
|
+
// Delete branch (if we have one)
|
|
368
|
+
if (branchName) {
|
|
369
|
+
const branchSpinner = (0, ora_1.default)('Deleting branch...').start();
|
|
370
|
+
try {
|
|
371
|
+
(0, child_process_1.execSync)(`git branch -D "${branchName}"`, {
|
|
372
|
+
cwd: projectRoot,
|
|
373
|
+
stdio: 'pipe',
|
|
374
|
+
});
|
|
375
|
+
branchSpinner.succeed('Branch deleted');
|
|
376
|
+
}
|
|
377
|
+
catch {
|
|
378
|
+
branchSpinner.warn('Could not delete branch (may already be deleted)');
|
|
379
|
+
}
|
|
310
380
|
}
|
|
311
381
|
console.log();
|
|
312
382
|
console.log(chalk_1.default.green('ā
Cleanup complete!'));
|