claude-issue-solver 1.6.6 → 1.7.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.
- package/README.md +24 -1
- package/claude-issue-solver-logo.webp +0 -0
- package/dist/commands/clean.js +52 -6
- package/dist/utils/github.d.ts +9 -0
- package/dist/utils/github.js +29 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
# Claude Issue Solver
|
|
2
2
|
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="claude-issue-solver-logo.webp" alt="Claude Issue Solver Logo" width="200">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
3
7
|
Automatically solve GitHub issues using [Claude Code](https://claude.ai/code).
|
|
4
8
|
|
|
5
9
|
This CLI tool fetches an issue from your repo, creates a worktree, opens Claude Code in a new terminal to solve it, and creates a PR when done.
|
|
6
10
|
|
|
11
|
+
> **⚠️ DISCLAIMER: USE AT YOUR OWN RISK**
|
|
12
|
+
>
|
|
13
|
+
> This tool runs Claude Code with the `--dangerously-skip-permissions` flag, which allows Claude to execute commands and modify files **without asking for confirmation**. This is powerful but potentially risky.
|
|
14
|
+
>
|
|
15
|
+
> **Before using this tool:**
|
|
16
|
+
> - Understand that Claude will have unrestricted access to your codebase
|
|
17
|
+
> - Review what Claude is doing in the terminal
|
|
18
|
+
> - Use git to review changes before merging PRs
|
|
19
|
+
> - Never run this on production systems or sensitive repositories without careful consideration
|
|
20
|
+
>
|
|
21
|
+
> By using this tool, you accept full responsibility for any changes made to your code.
|
|
22
|
+
|
|
7
23
|
## Demo
|
|
8
24
|
|
|
9
25
|
```bash
|
|
@@ -78,6 +94,12 @@ claude-issue pr 42
|
|
|
78
94
|
# Clean up worktree and branch after PR is merged
|
|
79
95
|
claude-issue clean 42
|
|
80
96
|
|
|
97
|
+
# Clean all worktrees (shows PR/issue status)
|
|
98
|
+
claude-issue clean
|
|
99
|
+
|
|
100
|
+
# Navigate to a worktree or open its PR
|
|
101
|
+
claude-issue go
|
|
102
|
+
|
|
81
103
|
# Show help
|
|
82
104
|
claude-issue --help
|
|
83
105
|
```
|
|
@@ -135,8 +157,9 @@ claude-issue --help
|
|
|
135
157
|
|
|
136
158
|
- Use `/exit` in Claude to end the session and trigger PR creation
|
|
137
159
|
- Worktrees share the same `.git` so commits are visible in main repo
|
|
138
|
-
- Run `claude-issue clean
|
|
160
|
+
- Run `claude-issue clean` after merging to clean up - it shows PR status (merged/open/closed)
|
|
139
161
|
- You can work on multiple issues in parallel - each gets its own worktree
|
|
162
|
+
- Use `claude-issue go` to quickly navigate to worktrees or open PRs in browser
|
|
140
163
|
|
|
141
164
|
## License
|
|
142
165
|
|
|
Binary file
|
package/dist/commands/clean.js
CHANGED
|
@@ -45,6 +45,7 @@ const fs = __importStar(require("fs"));
|
|
|
45
45
|
const path = __importStar(require("path"));
|
|
46
46
|
const os = __importStar(require("os"));
|
|
47
47
|
const child_process_1 = require("child_process");
|
|
48
|
+
const github_1 = require("../utils/github");
|
|
48
49
|
const git_1 = require("../utils/git");
|
|
49
50
|
function closeWindowsWithPath(folderPath, issueNumber) {
|
|
50
51
|
if (os.platform() !== 'darwin')
|
|
@@ -119,6 +120,30 @@ function closeWindowsWithPath(folderPath, issueNumber) {
|
|
|
119
120
|
// VS Code not running or no matching windows
|
|
120
121
|
}
|
|
121
122
|
}
|
|
123
|
+
function getStatusLabel(wt) {
|
|
124
|
+
if (!wt.branch) {
|
|
125
|
+
return chalk_1.default.yellow('(orphaned folder)');
|
|
126
|
+
}
|
|
127
|
+
if (wt.prStatus) {
|
|
128
|
+
switch (wt.prStatus.state) {
|
|
129
|
+
case 'merged':
|
|
130
|
+
return chalk_1.default.green('✓ PR merged');
|
|
131
|
+
case 'open':
|
|
132
|
+
return chalk_1.default.blue('◐ PR open');
|
|
133
|
+
case 'closed':
|
|
134
|
+
return chalk_1.default.red('✗ PR closed');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (wt.issueStatus) {
|
|
138
|
+
switch (wt.issueStatus.state) {
|
|
139
|
+
case 'closed':
|
|
140
|
+
return chalk_1.default.dim('● Issue closed');
|
|
141
|
+
case 'open':
|
|
142
|
+
return chalk_1.default.cyan('○ Issue open');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return chalk_1.default.dim('? Unknown');
|
|
146
|
+
}
|
|
122
147
|
function getIssueWorktrees() {
|
|
123
148
|
const projectRoot = (0, git_1.getProjectRoot)();
|
|
124
149
|
const projectName = (0, git_1.getProjectName)();
|
|
@@ -184,9 +209,21 @@ async function cleanAllCommand() {
|
|
|
184
209
|
console.log(chalk_1.default.yellow('\nNo issue worktrees found.'));
|
|
185
210
|
return;
|
|
186
211
|
}
|
|
212
|
+
// Fetch status for all worktrees
|
|
213
|
+
const statusSpinner = (0, ora_1.default)('Fetching issue and PR status...').start();
|
|
214
|
+
const worktreesWithStatus = worktrees.map((wt) => ({
|
|
215
|
+
...wt,
|
|
216
|
+
issueStatus: (0, github_1.getIssueStatus)(parseInt(wt.issueNumber, 10)),
|
|
217
|
+
prStatus: wt.branch ? (0, github_1.getPRForBranch)(wt.branch) : null,
|
|
218
|
+
}));
|
|
219
|
+
statusSpinner.stop();
|
|
187
220
|
console.log(chalk_1.default.bold('\n🧹 Found issue worktrees:\n'));
|
|
188
|
-
for (const wt of
|
|
189
|
-
|
|
221
|
+
for (const wt of worktreesWithStatus) {
|
|
222
|
+
const status = getStatusLabel(wt);
|
|
223
|
+
console.log(` ${chalk_1.default.cyan(`#${wt.issueNumber}`)}\t${status}`);
|
|
224
|
+
if (wt.branch) {
|
|
225
|
+
console.log(chalk_1.default.dim(` \t${wt.branch}`));
|
|
226
|
+
}
|
|
190
227
|
console.log(chalk_1.default.dim(` \t${wt.path}`));
|
|
191
228
|
console.log();
|
|
192
229
|
}
|
|
@@ -319,12 +356,21 @@ async function cleanCommand(issueNumber) {
|
|
|
319
356
|
const branchName = worktree.branch;
|
|
320
357
|
const worktreePath = worktree.path;
|
|
321
358
|
const isOrphaned = !branchName;
|
|
359
|
+
// Fetch status
|
|
360
|
+
const statusSpinner = (0, ora_1.default)('Fetching issue and PR status...').start();
|
|
361
|
+
const issueStatus = (0, github_1.getIssueStatus)(issueNumber);
|
|
362
|
+
const prStatus = branchName ? (0, github_1.getPRForBranch)(branchName) : null;
|
|
363
|
+
statusSpinner.stop();
|
|
364
|
+
const wtWithStatus = {
|
|
365
|
+
...worktree,
|
|
366
|
+
issueStatus,
|
|
367
|
+
prStatus,
|
|
368
|
+
};
|
|
369
|
+
const status = getStatusLabel(wtWithStatus);
|
|
322
370
|
console.log();
|
|
323
371
|
console.log(chalk_1.default.bold(`🧹 Cleaning up issue #${issueNumber}`));
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
else {
|
|
372
|
+
console.log(` Status: ${status}`);
|
|
373
|
+
if (!isOrphaned) {
|
|
328
374
|
console.log(chalk_1.default.dim(` Branch: ${branchName}`));
|
|
329
375
|
}
|
|
330
376
|
console.log(chalk_1.default.dim(` Folder: ${worktreePath}`));
|
package/dist/utils/github.d.ts
CHANGED
|
@@ -11,3 +11,12 @@ export interface IssueListItem {
|
|
|
11
11
|
export declare function getIssue(issueNumber: number): Issue | null;
|
|
12
12
|
export declare function listIssues(limit?: number): IssueListItem[];
|
|
13
13
|
export declare function createPullRequest(title: string, body: string, branch: string, base?: string): string;
|
|
14
|
+
export interface IssueStatus {
|
|
15
|
+
state: 'open' | 'closed';
|
|
16
|
+
}
|
|
17
|
+
export interface PRStatus {
|
|
18
|
+
state: 'open' | 'closed' | 'merged';
|
|
19
|
+
url: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function getIssueStatus(issueNumber: number): IssueStatus | null;
|
|
22
|
+
export declare function getPRForBranch(branch: string): PRStatus | null;
|
package/dist/utils/github.js
CHANGED
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getIssue = getIssue;
|
|
4
4
|
exports.listIssues = listIssues;
|
|
5
5
|
exports.createPullRequest = createPullRequest;
|
|
6
|
+
exports.getIssueStatus = getIssueStatus;
|
|
7
|
+
exports.getPRForBranch = getPRForBranch;
|
|
6
8
|
const child_process_1 = require("child_process");
|
|
7
9
|
function getIssue(issueNumber) {
|
|
8
10
|
try {
|
|
@@ -32,3 +34,30 @@ function createPullRequest(title, body, branch, base = 'main') {
|
|
|
32
34
|
const output = (0, child_process_1.execSync)(`gh pr create --title "${title}" --body "${body.replace(/"/g, '\\"')}" --head "${branch}" --base "${base}"`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
33
35
|
return output.trim();
|
|
34
36
|
}
|
|
37
|
+
function getIssueStatus(issueNumber) {
|
|
38
|
+
try {
|
|
39
|
+
const output = (0, child_process_1.execSync)(`gh issue view ${issueNumber} --json state`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
40
|
+
const data = JSON.parse(output);
|
|
41
|
+
return {
|
|
42
|
+
state: data.state.toLowerCase(),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function getPRForBranch(branch) {
|
|
50
|
+
try {
|
|
51
|
+
const output = (0, child_process_1.execSync)(`gh pr list --head "${branch}" --state all --json state,url --limit 1`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
52
|
+
const data = JSON.parse(output);
|
|
53
|
+
if (data.length === 0)
|
|
54
|
+
return null;
|
|
55
|
+
return {
|
|
56
|
+
state: data[0].state.toLowerCase(),
|
|
57
|
+
url: data[0].url,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-issue-solver",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "Automatically solve GitHub issues using Claude Code",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"files": [
|
|
43
43
|
"dist",
|
|
44
|
-
"package.json"
|
|
44
|
+
"package.json",
|
|
45
|
+
"claude-issue-solver-logo.webp"
|
|
45
46
|
]
|
|
46
47
|
}
|