claude-issue-solver 1.27.1 → 1.28.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/README.md +4 -0
- package/dist/commands/select.d.ts +3 -1
- package/dist/commands/select.js +3 -3
- package/dist/commands/solve.d.ts +3 -1
- package/dist/commands/solve.js +153 -4
- package/dist/index.js +4 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -112,6 +112,10 @@ cis
|
|
|
112
112
|
# Solve a specific issue directly
|
|
113
113
|
claude-issue 42
|
|
114
114
|
|
|
115
|
+
# Auto mode: solve → review → fix until approved (max 3 iterations)
|
|
116
|
+
claude-issue --auto # Interactive selection with auto mode
|
|
117
|
+
claude-issue 42 --auto # Solve specific issue with auto mode
|
|
118
|
+
|
|
115
119
|
# List open issues
|
|
116
120
|
claude-issue list
|
|
117
121
|
claude-issue ls
|
package/dist/commands/select.js
CHANGED
|
@@ -9,7 +9,7 @@ const inquirer_1 = __importDefault(require("inquirer"));
|
|
|
9
9
|
const github_1 = require("../utils/github");
|
|
10
10
|
const git_1 = require("../utils/git");
|
|
11
11
|
const solve_1 = require("./solve");
|
|
12
|
-
async function selectCommand() {
|
|
12
|
+
async function selectCommand(options = {}) {
|
|
13
13
|
const projectName = (0, git_1.getProjectName)();
|
|
14
14
|
console.log(chalk_1.default.bold(`\nOpen issues for ${projectName}:\n`));
|
|
15
15
|
const issues = (0, github_1.listIssues)();
|
|
@@ -42,9 +42,9 @@ async function selectCommand() {
|
|
|
42
42
|
console.log(chalk_1.default.dim('No issues selected.'));
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
|
-
console.log(chalk_1.default.cyan(`\nStarting ${issueNumbers.length} issue(s)...\n`));
|
|
45
|
+
console.log(chalk_1.default.cyan(`\nStarting ${issueNumbers.length} issue(s)${options.auto ? ' in auto mode' : ''}...\n`));
|
|
46
46
|
for (const issueNumber of issueNumbers) {
|
|
47
|
-
await (0, solve_1.solveCommand)(issueNumber);
|
|
47
|
+
await (0, solve_1.solveCommand)(issueNumber, { auto: options.auto });
|
|
48
48
|
console.log();
|
|
49
49
|
}
|
|
50
50
|
}
|
package/dist/commands/solve.d.ts
CHANGED
package/dist/commands/solve.js
CHANGED
|
@@ -45,7 +45,8 @@ const child_process_1 = require("child_process");
|
|
|
45
45
|
const github_1 = require("../utils/github");
|
|
46
46
|
const git_1 = require("../utils/git");
|
|
47
47
|
const helpers_1 = require("../utils/helpers");
|
|
48
|
-
|
|
48
|
+
const config_1 = require("./config");
|
|
49
|
+
async function solveCommand(issueNumber, options = {}) {
|
|
49
50
|
const spinner = (0, ora_1.default)(`Fetching issue #${issueNumber}...`).start();
|
|
50
51
|
const issue = (0, github_1.getIssue)(issueNumber);
|
|
51
52
|
if (!issue) {
|
|
@@ -122,6 +123,9 @@ Instructions:
|
|
|
122
123
|
// Write prompt to a file to avoid shell escaping issues with backticks, <>, etc.
|
|
123
124
|
const promptFile = path.join(worktreePath, '.claude-prompt.txt');
|
|
124
125
|
fs.writeFileSync(promptFile, prompt);
|
|
126
|
+
// Get bot token for auto mode
|
|
127
|
+
const botToken = (0, config_1.getBotToken)();
|
|
128
|
+
const autoMode = options.auto || false;
|
|
125
129
|
// Create runner script
|
|
126
130
|
const runnerScript = path.join(worktreePath, '.claude-runner.sh');
|
|
127
131
|
const runnerContent = `#!/bin/bash
|
|
@@ -133,11 +137,16 @@ echo -ne "\\033]0;Issue #${issueNumber}: ${issue.title.replace(/"/g, '\\"').slic
|
|
|
133
137
|
echo "🤖 Claude Code - Issue #${issueNumber}: ${issue.title}"
|
|
134
138
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
135
139
|
echo ""
|
|
136
|
-
echo "When Claude commits, a PR will be created automatically."
|
|
140
|
+
${autoMode ? 'echo "🔄 AUTO MODE: Will solve → review → fix until approved (max 3 iterations)"' : 'echo "When Claude commits, a PR will be created automatically."'}
|
|
137
141
|
echo "The terminal stays open for follow-up changes."
|
|
138
142
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
139
143
|
echo ""
|
|
140
144
|
|
|
145
|
+
${botToken ? `# Bot token for reviews
|
|
146
|
+
export BOT_TOKEN="${botToken}"
|
|
147
|
+
export GH_TOKEN="${botToken}"
|
|
148
|
+
` : ''}
|
|
149
|
+
|
|
141
150
|
# Function to create PR
|
|
142
151
|
create_pr() {
|
|
143
152
|
COMMITS=$(git log origin/${baseBranch}..HEAD --oneline 2>/dev/null | wc -l | tr -d ' ')
|
|
@@ -182,6 +191,7 @@ $COMMIT_LIST
|
|
|
182
191
|
# Update terminal title with PR info
|
|
183
192
|
PR_NUM=$(echo "$PR_URL" | grep -oE '[0-9]+$')
|
|
184
193
|
echo -ne "\\033]0;Issue #${issueNumber} → PR #\$PR_NUM\\007"
|
|
194
|
+
echo "$PR_NUM"
|
|
185
195
|
fi
|
|
186
196
|
else
|
|
187
197
|
# PR exists, just push new commits
|
|
@@ -191,16 +201,27 @@ $COMMIT_LIST
|
|
|
191
201
|
echo "📤 Pushed new commits to PR #$EXISTING_PR"
|
|
192
202
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
193
203
|
echo ""
|
|
204
|
+
echo "$EXISTING_PR"
|
|
194
205
|
fi
|
|
195
206
|
fi
|
|
196
207
|
}
|
|
197
208
|
|
|
209
|
+
# Function to get PR number
|
|
210
|
+
get_pr_number() {
|
|
211
|
+
gh pr list --head "${branchName}" --json number --jq '.[0].number' 2>/dev/null
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
# Function to get PR review status
|
|
215
|
+
get_review_status() {
|
|
216
|
+
${botToken ? 'GH_TOKEN="${BOT_TOKEN}"' : ''} gh pr view "$1" --json reviewDecision --jq '.reviewDecision' 2>/dev/null
|
|
217
|
+
}
|
|
218
|
+
|
|
198
219
|
# Watch for new commits in background and create PR
|
|
199
220
|
LAST_COMMIT=""
|
|
200
221
|
while true; do
|
|
201
222
|
CURRENT_COMMIT=$(git rev-parse HEAD 2>/dev/null)
|
|
202
223
|
if [ "$CURRENT_COMMIT" != "$LAST_COMMIT" ] && [ -n "$LAST_COMMIT" ]; then
|
|
203
|
-
create_pr
|
|
224
|
+
create_pr > /dev/null
|
|
204
225
|
fi
|
|
205
226
|
LAST_COMMIT="$CURRENT_COMMIT"
|
|
206
227
|
sleep 2
|
|
@@ -217,7 +238,135 @@ rm -f '${promptFile}'
|
|
|
217
238
|
kill $WATCHER_PID 2>/dev/null
|
|
218
239
|
|
|
219
240
|
# Final PR check after Claude exits
|
|
220
|
-
create_pr
|
|
241
|
+
create_pr > /dev/null
|
|
242
|
+
|
|
243
|
+
${autoMode ? `
|
|
244
|
+
# AUTO MODE: Review loop
|
|
245
|
+
MAX_ITERATIONS=3
|
|
246
|
+
ITERATION=0
|
|
247
|
+
|
|
248
|
+
while [ $ITERATION -lt $MAX_ITERATIONS ]; do
|
|
249
|
+
ITERATION=$((ITERATION + 1))
|
|
250
|
+
|
|
251
|
+
# Wait for PR to be created
|
|
252
|
+
sleep 2
|
|
253
|
+
PR_NUM=$(get_pr_number)
|
|
254
|
+
|
|
255
|
+
if [ -z "$PR_NUM" ]; then
|
|
256
|
+
echo ""
|
|
257
|
+
echo "⚠️ No PR found, skipping auto-review"
|
|
258
|
+
break
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
echo ""
|
|
262
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
263
|
+
echo "🔍 AUTO-REVIEW: Iteration $ITERATION of $MAX_ITERATIONS"
|
|
264
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
265
|
+
echo ""
|
|
266
|
+
|
|
267
|
+
# Get PR diff for review
|
|
268
|
+
PR_DIFF=$(gh pr diff $PR_NUM 2>/dev/null | head -500)
|
|
269
|
+
|
|
270
|
+
# Create review prompt
|
|
271
|
+
REVIEW_PROMPT="You are reviewing PR #$PR_NUM for issue #${issueNumber}: ${issue.title.replace(/"/g, '\\"')}
|
|
272
|
+
|
|
273
|
+
## Issue Description
|
|
274
|
+
${issue.body.replace(/"/g, '\\"').replace(/\$/g, '\\$').replace(/\`/g, '\\`')}
|
|
275
|
+
|
|
276
|
+
## Your Task
|
|
277
|
+
Review the code changes in this PR. Look for:
|
|
278
|
+
1. Bugs and logic errors
|
|
279
|
+
2. Security vulnerabilities
|
|
280
|
+
3. Missing error handling
|
|
281
|
+
4. Code quality issues
|
|
282
|
+
5. Performance problems
|
|
283
|
+
|
|
284
|
+
## How to Leave Feedback
|
|
285
|
+
${botToken ? `Use GH_TOKEN=\\$BOT_TOKEN prefix for all gh commands.
|
|
286
|
+
|
|
287
|
+
If the code looks good:
|
|
288
|
+
\\\`\\\`\\\`bash
|
|
289
|
+
GH_TOKEN=\\$BOT_TOKEN gh pr review $PR_NUM --approve --body \\"LGTM! Code looks good.\\"
|
|
290
|
+
\\\`\\\`\\\`
|
|
291
|
+
|
|
292
|
+
If changes are needed:
|
|
293
|
+
\\\`\\\`\\\`bash
|
|
294
|
+
GH_TOKEN=\\$BOT_TOKEN gh pr review $PR_NUM --request-changes --body \\"<your feedback>\\"
|
|
295
|
+
\\\`\\\`\\\`
|
|
296
|
+
` : `If the code looks good:
|
|
297
|
+
\\\`\\\`\\\`bash
|
|
298
|
+
gh pr review $PR_NUM --approve --body \\"LGTM! Code looks good.\\"
|
|
299
|
+
\\\`\\\`\\\`
|
|
300
|
+
|
|
301
|
+
If changes are needed:
|
|
302
|
+
\\\`\\\`\\\`bash
|
|
303
|
+
gh pr review $PR_NUM --request-changes --body \\"<your feedback>\\"
|
|
304
|
+
\\\`\\\`\\\`
|
|
305
|
+
`}
|
|
306
|
+
|
|
307
|
+
## PR Diff (first 500 lines)
|
|
308
|
+
\\\`\\\`\\\`diff
|
|
309
|
+
$PR_DIFF
|
|
310
|
+
\\\`\\\`\\\`
|
|
311
|
+
|
|
312
|
+
Review the code and either approve or request changes."
|
|
313
|
+
|
|
314
|
+
# Run Claude for review
|
|
315
|
+
claude --dangerously-skip-permissions "$REVIEW_PROMPT"
|
|
316
|
+
|
|
317
|
+
# Check review status
|
|
318
|
+
sleep 2
|
|
319
|
+
REVIEW_STATUS=$(get_review_status $PR_NUM)
|
|
320
|
+
|
|
321
|
+
echo ""
|
|
322
|
+
echo "📊 Review status: $REVIEW_STATUS"
|
|
323
|
+
|
|
324
|
+
if [ "$REVIEW_STATUS" = "APPROVED" ]; then
|
|
325
|
+
echo ""
|
|
326
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
327
|
+
echo "✅ PR APPROVED! Ready to merge."
|
|
328
|
+
echo " Run: cis merge"
|
|
329
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
330
|
+
break
|
|
331
|
+
elif [ "$REVIEW_STATUS" = "CHANGES_REQUESTED" ]; then
|
|
332
|
+
if [ $ITERATION -lt $MAX_ITERATIONS ]; then
|
|
333
|
+
echo ""
|
|
334
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
335
|
+
echo "🔧 Changes requested. Claude will fix them..."
|
|
336
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
337
|
+
echo ""
|
|
338
|
+
|
|
339
|
+
# Get the review comments
|
|
340
|
+
REVIEW_COMMENTS=$(${botToken ? 'GH_TOKEN="${BOT_TOKEN}"' : ''} gh pr view $PR_NUM --json reviews --jq '.reviews[-1].body' 2>/dev/null)
|
|
341
|
+
|
|
342
|
+
FIX_PROMPT="The code review requested changes. Please fix them:
|
|
343
|
+
|
|
344
|
+
## Review Feedback
|
|
345
|
+
$REVIEW_COMMENTS
|
|
346
|
+
|
|
347
|
+
Please address the feedback above, make the necessary changes, and commit them."
|
|
348
|
+
|
|
349
|
+
# Run Claude to fix
|
|
350
|
+
claude --dangerously-skip-permissions "$FIX_PROMPT"
|
|
351
|
+
|
|
352
|
+
# Push changes
|
|
353
|
+
git push origin "${branchName}" 2>/dev/null
|
|
354
|
+
sleep 2
|
|
355
|
+
else
|
|
356
|
+
echo ""
|
|
357
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
358
|
+
echo "⚠️ Max iterations reached. Manual review needed."
|
|
359
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
360
|
+
fi
|
|
361
|
+
else
|
|
362
|
+
echo ""
|
|
363
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
364
|
+
echo "ℹ️ Review status: $REVIEW_STATUS"
|
|
365
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
366
|
+
break
|
|
367
|
+
fi
|
|
368
|
+
done
|
|
369
|
+
` : ''}
|
|
221
370
|
|
|
222
371
|
echo ""
|
|
223
372
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
package/dist/index.js
CHANGED
|
@@ -52,17 +52,18 @@ program.hook('preAction', (thisCommand) => {
|
|
|
52
52
|
// Default command - interactive selection
|
|
53
53
|
program
|
|
54
54
|
.argument('[issue]', 'Issue number to solve')
|
|
55
|
-
.
|
|
55
|
+
.option('--auto', 'Auto mode: solve → review → fix until approved (max 3 iterations)')
|
|
56
|
+
.action(async (issue, options) => {
|
|
56
57
|
if (issue) {
|
|
57
58
|
const issueNumber = parseInt(issue, 10);
|
|
58
59
|
if (isNaN(issueNumber)) {
|
|
59
60
|
console.log(chalk_1.default.red(`❌ Invalid issue number: ${issue}`));
|
|
60
61
|
process.exit(1);
|
|
61
62
|
}
|
|
62
|
-
await (0, solve_1.solveCommand)(issueNumber);
|
|
63
|
+
await (0, solve_1.solveCommand)(issueNumber, { auto: options.auto });
|
|
63
64
|
}
|
|
64
65
|
else {
|
|
65
|
-
await (0, select_1.selectCommand)();
|
|
66
|
+
await (0, select_1.selectCommand)({ auto: options.auto });
|
|
66
67
|
}
|
|
67
68
|
});
|
|
68
69
|
// List command
|