claude-queue 1.5.0 → 1.5.2
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 +60 -69
- package/claude-queue.sh +85 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# claude-queue
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A CLI tool that solves GitHub issues using [Claude Code](https://docs.anthropic.com/en/docs/claude-code). It picks up open issues from your repo, solves them one by one, and opens a pull request with all the fixes. It can also create well-structured GitHub issues from a text description or interactive interview.
|
|
4
4
|
|
|
5
|
-
claude-queue
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
The typical workflow is: open issues for whatever you need done, run `claude-queue`, and come back to a pull request with everything solved. I usually do this at night and review the PR in the morning.
|
|
6
|
+
|
|
7
|
+
Issues don't have to be code changes — they can be investigative tasks like "figure out why the API is slow and document what you find" or "audit the codebase for accessibility issues". Claude will research, document findings, and commit whatever it produces.
|
|
8
8
|
|
|
9
9
|
## Prerequisites
|
|
10
10
|
|
|
@@ -18,7 +18,7 @@ claude-queue has two modes:
|
|
|
18
18
|
npm install -g claude-queue
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Or run directly
|
|
21
|
+
Or run directly:
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
24
|
npx claude-queue
|
|
@@ -34,32 +34,40 @@ Run from inside any git repository with GitHub issues:
|
|
|
34
34
|
claude-queue
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
#### Solve options
|
|
38
|
-
|
|
39
37
|
| Flag | Default | Description |
|
|
40
38
|
|------|---------|-------------|
|
|
39
|
+
| `--issue ID` | all issues | Solve specific issue(s) by ID, URL, or comma-separated IDs |
|
|
41
40
|
| `--max-retries N` | `3` | Max retry attempts per issue before marking it failed |
|
|
42
|
-
| `--max-turns N` | `50` | Max Claude Code turns per attempt
|
|
43
|
-
| `--label LABEL` | all issues | Only process issues
|
|
41
|
+
| `--max-turns N` | `50` | Max Claude Code turns per attempt |
|
|
42
|
+
| `--label LABEL` | all issues | Only process issues with this label (can be repeated) |
|
|
44
43
|
| `--model MODEL` | CLI default | Claude model to use (e.g. `claude-sonnet-4-5-20250929`) |
|
|
45
|
-
| `-h, --help` | | Show help |
|
|
46
|
-
|
|
47
|
-
#### Solve examples
|
|
48
44
|
|
|
49
45
|
```bash
|
|
50
46
|
# Solve all open issues
|
|
51
47
|
claude-queue
|
|
52
48
|
|
|
49
|
+
# Solve a specific issue by number
|
|
50
|
+
claude-queue --issue 42
|
|
51
|
+
|
|
52
|
+
# Solve a specific issue by URL
|
|
53
|
+
claude-queue --issue https://github.com/owner/repo/issues/42
|
|
54
|
+
|
|
55
|
+
# Solve multiple specific issues
|
|
56
|
+
claude-queue --issue 1,2,3
|
|
57
|
+
|
|
53
58
|
# Only solve issues labeled "bug"
|
|
54
59
|
claude-queue --label bug
|
|
55
60
|
|
|
61
|
+
# Filter by multiple labels
|
|
62
|
+
claude-queue --label bug --label urgent
|
|
63
|
+
|
|
56
64
|
# Use a specific model with more retries
|
|
57
65
|
claude-queue --max-retries 5 --model claude-sonnet-4-5-20250929
|
|
58
66
|
```
|
|
59
67
|
|
|
60
68
|
### Creating issues
|
|
61
69
|
|
|
62
|
-
Generate
|
|
70
|
+
Generate GitHub issues from a text description or an interactive interview with Claude.
|
|
63
71
|
|
|
64
72
|
```bash
|
|
65
73
|
claude-queue create "Add dark mode and fix the login bug"
|
|
@@ -71,42 +79,31 @@ There are three ways to provide input:
|
|
|
71
79
|
2. **Stdin** — run `claude-queue create` with no arguments, type or paste your text, then press Ctrl+D
|
|
72
80
|
3. **Interactive** — run `claude-queue create -i` and Claude will ask clarifying questions before generating issues
|
|
73
81
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
#### Create options
|
|
82
|
+
Claude decomposes the input into individual issues with titles, markdown bodies, and labels (reusing existing repo labels where possible). You get a preview before anything is created.
|
|
77
83
|
|
|
78
84
|
| Flag | Default | Description |
|
|
79
85
|
|------|---------|-------------|
|
|
80
86
|
| `-i, --interactive` | off | Interview mode — Claude asks clarifying questions first |
|
|
81
87
|
| `--label LABEL` | none | Add this label to every created issue |
|
|
82
88
|
| `--model MODEL` | CLI default | Claude model to use |
|
|
83
|
-
| `-h, --help` | | Show help for create |
|
|
84
|
-
|
|
85
|
-
#### Create examples
|
|
86
89
|
|
|
87
90
|
```bash
|
|
88
|
-
#
|
|
89
|
-
claude-queue create "Add user avatars, implement search, and fix the 404 on /settings"
|
|
90
|
-
|
|
91
|
-
# Interactive mode — Claude asks questions first
|
|
91
|
+
# Interactive mode
|
|
92
92
|
claude-queue create -i
|
|
93
93
|
|
|
94
|
-
#
|
|
95
|
-
claude-queue create
|
|
96
|
-
|
|
97
|
-
# Add a label to all created issues (useful with --label on solve)
|
|
94
|
+
# Add a label to all created issues
|
|
98
95
|
claude-queue create --label backlog "Refactor the auth module and add rate limiting"
|
|
99
96
|
```
|
|
100
97
|
|
|
101
|
-
###
|
|
98
|
+
### Create then solve workflow
|
|
102
99
|
|
|
103
|
-
The `--label` flag on both commands lets you create a
|
|
100
|
+
The `--label` flag on both commands lets you create a pipeline where `create` plans the issues and `claude-queue` solves them:
|
|
104
101
|
|
|
105
102
|
```bash
|
|
106
|
-
# Plan
|
|
103
|
+
# Plan
|
|
107
104
|
claude-queue create --label nightshift "Add dark mode and fix the login bug"
|
|
108
105
|
|
|
109
|
-
# Solve
|
|
106
|
+
# Solve
|
|
110
107
|
claude-queue --label nightshift
|
|
111
108
|
```
|
|
112
109
|
|
|
@@ -120,64 +117,58 @@ Use TypeScript strict mode.
|
|
|
120
117
|
Never modify files in the src/legacy/ directory.
|
|
121
118
|
```
|
|
122
119
|
|
|
123
|
-
These instructions are appended to the prompt Claude receives for each issue.
|
|
120
|
+
These instructions are appended to the prompt Claude receives for each issue. Useful for project-specific conventions that aren't in `CLAUDE.md`.
|
|
124
121
|
|
|
125
122
|
## How It Works
|
|
126
123
|
|
|
127
|
-
###
|
|
124
|
+
### Preflight
|
|
128
125
|
|
|
129
|
-
Verifies all dependencies
|
|
126
|
+
Verifies all dependencies (`gh`, `claude`, `git`, `jq`), checks that `gh` is authenticated, and ensures the working tree is clean.
|
|
130
127
|
|
|
131
|
-
###
|
|
128
|
+
### Label setup
|
|
132
129
|
|
|
133
130
|
Creates three labels on the repo (skips if they already exist):
|
|
134
131
|
|
|
135
|
-
| Label |
|
|
136
|
-
|
|
137
|
-
| `claude-queue:in-progress` |
|
|
138
|
-
| `claude-queue:solved` |
|
|
139
|
-
| `claude-queue:failed` |
|
|
132
|
+
| Label | Meaning |
|
|
133
|
+
|-------|---------|
|
|
134
|
+
| `claude-queue:in-progress` | Currently being worked on |
|
|
135
|
+
| `claude-queue:solved` | Successfully fixed |
|
|
136
|
+
| `claude-queue:failed` | Could not be solved after all retries |
|
|
137
|
+
|
|
138
|
+
### Branching
|
|
140
139
|
|
|
141
|
-
|
|
140
|
+
Creates a branch `claude-queue/YYYY-MM-DD` off your default branch. All fixes go into this one branch. If the branch already exists, a timestamp suffix is added.
|
|
142
141
|
|
|
143
|
-
###
|
|
142
|
+
### Issue processing
|
|
144
143
|
|
|
145
|
-
|
|
144
|
+
For each open issue (up to 200, oldest first), or for the specific issues passed via `--issue`:
|
|
146
145
|
|
|
147
|
-
|
|
146
|
+
1. **Skip** — issues with any `claude-queue:*` label are skipped (unless targeted via `--issue`). Remove the label to re-process.
|
|
147
|
+
2. **Label** — marks the issue `claude-queue:in-progress`.
|
|
148
|
+
3. **Solve** — launches Claude Code with a prompt to read the issue, explore the codebase, implement a fix, and run tests.
|
|
149
|
+
4. **Evaluate** — if Claude produced file changes, they are committed. If not, the attempt is retried.
|
|
150
|
+
5. **Retry** — on failure, the working tree is reset and Claude gets a fresh context. Up to 3 attempts (configurable with `--max-retries`).
|
|
151
|
+
6. **Result** — marks the issue `claude-queue:solved` or `claude-queue:failed`.
|
|
148
152
|
|
|
149
|
-
|
|
153
|
+
Issues are solved sequentially so later fixes build on earlier ones within a single branch.
|
|
150
154
|
|
|
151
|
-
|
|
152
|
-
- **Label** — marks the issue `claude-queue:in-progress`
|
|
153
|
-
- **Solve** — launches a fresh Claude Code process (`claude -p`) with a prompt that tells it to:
|
|
154
|
-
- Read the issue via `gh issue view`
|
|
155
|
-
- Explore the codebase
|
|
156
|
-
- Implement a fix
|
|
157
|
-
- Run existing tests
|
|
158
|
-
- **Evaluate** — if Claude produced file changes, they are committed. If not, the attempt is retried.
|
|
159
|
-
- **Retry** — on failure, the working tree is reset to the last checkpoint (`git reset --hard`) and Claude gets a completely fresh context. Up to 3 attempts per issue (configurable with `--max-retries`).
|
|
160
|
-
- **Label result** — marks the issue `claude-queue:solved` or `claude-queue:failed`
|
|
155
|
+
### Review pass
|
|
161
156
|
|
|
162
|
-
|
|
157
|
+
After all issues are processed, Claude does a second pass reviewing all committed changes for bugs, incomplete implementations, and style issues — fixing anything it finds.
|
|
163
158
|
|
|
164
|
-
###
|
|
159
|
+
### Pull request
|
|
165
160
|
|
|
166
|
-
Once
|
|
161
|
+
Once done, the branch is pushed and a PR is opened with:
|
|
167
162
|
|
|
168
|
-
-
|
|
169
|
-
-
|
|
170
|
-
-
|
|
171
|
-
- **Chain logs** — collapsible per-issue logs showing Claude's full output for each attempt
|
|
163
|
+
- Summary table with solved/failed/skipped counts and run duration
|
|
164
|
+
- Tables of solved and failed issues with links
|
|
165
|
+
- Collapsible per-issue logs showing Claude's full output
|
|
172
166
|
|
|
173
|
-
|
|
167
|
+
No PR is created if nothing was solved.
|
|
174
168
|
|
|
175
|
-
### Interruption
|
|
169
|
+
### Interruption handling
|
|
176
170
|
|
|
177
|
-
If
|
|
178
|
-
- Removes the `claude-queue:in-progress` label from the current issue
|
|
179
|
-
- Marks it as `claude-queue:failed`
|
|
180
|
-
- Prints where your commits and logs are so nothing is lost
|
|
171
|
+
If interrupted (Ctrl+C, SIGTERM), the script removes the `claude-queue:in-progress` label from the current issue, marks it as failed, and prints where your commits and logs are.
|
|
181
172
|
|
|
182
173
|
## Logs
|
|
183
174
|
|
|
@@ -190,7 +181,7 @@ Full logs for each run are saved to `/tmp/claude-queue-DATE-TIMESTAMP/`:
|
|
|
190
181
|
├── issue-42-attempt-2.log # Raw Claude output, attempt 2
|
|
191
182
|
├── issue-57.md
|
|
192
183
|
├── issue-57-attempt-1.log
|
|
193
|
-
└── pr-body.md #
|
|
184
|
+
└── pr-body.md # Generated PR description
|
|
194
185
|
```
|
|
195
186
|
|
|
196
187
|
## License
|
package/claude-queue.sh
CHANGED
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
# claude-queue create [options] Create issues from text or interactively
|
|
8
8
|
#
|
|
9
9
|
# Solve options:
|
|
10
|
+
# --issue ID Solve specific issue(s) by ID, URL, or comma-separated IDs
|
|
10
11
|
# --max-retries N Max retries per issue (default: 3)
|
|
11
12
|
# --max-turns N Max Claude turns per attempt (default: 50)
|
|
12
|
-
# --label LABEL Only process issues with this label
|
|
13
|
+
# --label LABEL Only process issues with this label (can be repeated)
|
|
13
14
|
# --model MODEL Claude model to use
|
|
14
15
|
# -v, --version Show version
|
|
15
16
|
# -h, --help Show this help message
|
|
@@ -26,7 +27,8 @@ VERSION=$(node -p "require('$(dirname "$0")/package.json').version" 2>/dev/null
|
|
|
26
27
|
|
|
27
28
|
MAX_RETRIES=3
|
|
28
29
|
MAX_TURNS=50
|
|
29
|
-
|
|
30
|
+
declare -a ISSUE_FILTERS=()
|
|
31
|
+
ISSUE_IDS=""
|
|
30
32
|
MODEL_FLAG=""
|
|
31
33
|
DATE=$(date +%Y-%m-%d)
|
|
32
34
|
TIMESTAMP=$(date +%H%M%S)
|
|
@@ -49,6 +51,7 @@ declare -a SOLVED_ISSUES=()
|
|
|
49
51
|
declare -a FAILED_ISSUES=()
|
|
50
52
|
declare -a SKIPPED_ISSUES=()
|
|
51
53
|
CURRENT_ISSUE=""
|
|
54
|
+
CHILD_PID=""
|
|
52
55
|
START_TIME=$(date +%s)
|
|
53
56
|
|
|
54
57
|
show_help() {
|
|
@@ -59,9 +62,10 @@ show_help() {
|
|
|
59
62
|
echo " claude-queue create [options] [text] Create issues from text or interactively"
|
|
60
63
|
echo ""
|
|
61
64
|
echo "Solve options:"
|
|
65
|
+
echo " --issue ID Solve specific issue(s) by ID, URL, or comma-separated IDs"
|
|
62
66
|
echo " --max-retries N Max retries per issue (default: 3)"
|
|
63
67
|
echo " --max-turns N Max Claude turns per attempt (default: 50)"
|
|
64
|
-
echo " --label LABEL Only process issues with this label"
|
|
68
|
+
echo " --label LABEL Only process issues with this label (can be repeated)"
|
|
65
69
|
echo " --model MODEL Claude model to use"
|
|
66
70
|
echo " -v, --version Show version"
|
|
67
71
|
echo " -h, --help Show this help message"
|
|
@@ -108,6 +112,17 @@ cleanup() {
|
|
|
108
112
|
log "Logs saved to: ${LOG_DIR}"
|
|
109
113
|
fi
|
|
110
114
|
}
|
|
115
|
+
|
|
116
|
+
handle_interrupt() {
|
|
117
|
+
if [ -n "$CHILD_PID" ] && kill -0 "$CHILD_PID" 2>/dev/null; then
|
|
118
|
+
kill -TERM "$CHILD_PID" 2>/dev/null || true
|
|
119
|
+
wait "$CHILD_PID" 2>/dev/null || true
|
|
120
|
+
fi
|
|
121
|
+
CHILD_PID=""
|
|
122
|
+
exit 130
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
trap handle_interrupt INT TERM
|
|
111
126
|
trap cleanup EXIT
|
|
112
127
|
|
|
113
128
|
preflight() {
|
|
@@ -181,15 +196,61 @@ setup_branch() {
|
|
|
181
196
|
}
|
|
182
197
|
|
|
183
198
|
fetch_issues() {
|
|
184
|
-
local args=(--state open --json "number,title,body,labels" --limit 200)
|
|
199
|
+
local args=(--state open --json "number,title,body,labels" --limit 200 --sort created --direction asc)
|
|
185
200
|
|
|
186
|
-
|
|
187
|
-
args+=(--label "$
|
|
188
|
-
|
|
201
|
+
for filter in "${ISSUE_FILTERS[@]}"; do
|
|
202
|
+
args+=(--label "$filter")
|
|
203
|
+
done
|
|
189
204
|
|
|
190
205
|
gh issue list "${args[@]}"
|
|
191
206
|
}
|
|
192
207
|
|
|
208
|
+
parse_issue_number() {
|
|
209
|
+
local input="$1"
|
|
210
|
+
|
|
211
|
+
local num
|
|
212
|
+
num=$(echo "$input" | grep -oE '[0-9]+$' || echo "")
|
|
213
|
+
|
|
214
|
+
if [ -z "$num" ]; then
|
|
215
|
+
log_error "Invalid issue identifier: $input"
|
|
216
|
+
return 1
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
echo "$num"
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
fetch_specific_issues() {
|
|
223
|
+
local ids_str="$1"
|
|
224
|
+
local result="["
|
|
225
|
+
local first=true
|
|
226
|
+
|
|
227
|
+
IFS=',' read -ra id_array <<< "$ids_str"
|
|
228
|
+
for id in "${id_array[@]}"; do
|
|
229
|
+
id=$(echo "$id" | xargs)
|
|
230
|
+
|
|
231
|
+
local num
|
|
232
|
+
if ! num=$(parse_issue_number "$id"); then
|
|
233
|
+
continue
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
local issue_json
|
|
237
|
+
issue_json=$(gh issue view "$num" --json "number,title,body,labels" 2>/dev/null) || {
|
|
238
|
+
log_error "Could not fetch issue #${num}"
|
|
239
|
+
continue
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if [ "$first" = true ]; then
|
|
243
|
+
first=false
|
|
244
|
+
else
|
|
245
|
+
result+=","
|
|
246
|
+
fi
|
|
247
|
+
result+="$issue_json"
|
|
248
|
+
done
|
|
249
|
+
|
|
250
|
+
result+="]"
|
|
251
|
+
echo "$result"
|
|
252
|
+
}
|
|
253
|
+
|
|
193
254
|
process_issue() {
|
|
194
255
|
local issue_number=$1
|
|
195
256
|
local issue_title="$2"
|
|
@@ -267,7 +328,10 @@ description of what you changed and why."
|
|
|
267
328
|
--dangerously-skip-permissions \
|
|
268
329
|
--max-turns "$MAX_TURNS" \
|
|
269
330
|
$MODEL_FLAG \
|
|
270
|
-
> "$attempt_log" 2>&1
|
|
331
|
+
> "$attempt_log" 2>&1 &
|
|
332
|
+
CHILD_PID=$!
|
|
333
|
+
wait "$CHILD_PID" || claude_exit=$?
|
|
334
|
+
CHILD_PID=""
|
|
271
335
|
|
|
272
336
|
if [ "$claude_exit" -ne 0 ]; then
|
|
273
337
|
log_warn "Claude exited with code ${claude_exit}"
|
|
@@ -377,7 +441,10 @@ When you are done, output a line that says CLAUDE_QUEUE_REVIEW followed by a bri
|
|
|
377
441
|
--dangerously-skip-permissions \
|
|
378
442
|
--max-turns "$MAX_TURNS" \
|
|
379
443
|
$MODEL_FLAG \
|
|
380
|
-
> "$review_log" 2>&1
|
|
444
|
+
> "$review_log" 2>&1 &
|
|
445
|
+
CHILD_PID=$!
|
|
446
|
+
wait "$CHILD_PID" 2>/dev/null || true
|
|
447
|
+
CHILD_PID=""
|
|
381
448
|
|
|
382
449
|
local changed_files
|
|
383
450
|
changed_files=$(git diff --name-only 2>/dev/null; git ls-files --others --exclude-standard 2>/dev/null)
|
|
@@ -516,7 +583,12 @@ main() {
|
|
|
516
583
|
log_header "Fetching Issues"
|
|
517
584
|
|
|
518
585
|
local issues
|
|
519
|
-
|
|
586
|
+
if [ -n "$ISSUE_IDS" ]; then
|
|
587
|
+
log "Fetching specific issue(s): ${ISSUE_IDS}"
|
|
588
|
+
issues=$(fetch_specific_issues "$ISSUE_IDS")
|
|
589
|
+
else
|
|
590
|
+
issues=$(fetch_issues)
|
|
591
|
+
fi
|
|
520
592
|
local total
|
|
521
593
|
total=$(echo "$issues" | jq length)
|
|
522
594
|
|
|
@@ -533,7 +605,7 @@ main() {
|
|
|
533
605
|
title=$(echo "$issues" | jq -r ".[$i].title")
|
|
534
606
|
labels=$(echo "$issues" | jq -r "[.[$i].labels[].name] | join(\",\")" 2>/dev/null || echo "")
|
|
535
607
|
|
|
536
|
-
if echo "$labels" | grep -q "claude-queue:"; then
|
|
608
|
+
if [ -z "$ISSUE_IDS" ] && echo "$labels" | grep -q "claude-queue:"; then
|
|
537
609
|
log "Skipping #${number} (already has a claude-queue label)"
|
|
538
610
|
SKIPPED_ISSUES+=("${number}|${title}")
|
|
539
611
|
continue
|
|
@@ -931,9 +1003,10 @@ case "$SUBCOMMAND" in
|
|
|
931
1003
|
"")
|
|
932
1004
|
while [[ $# -gt 0 ]]; do
|
|
933
1005
|
case $1 in
|
|
1006
|
+
--issue) ISSUE_IDS="$2"; shift 2 ;;
|
|
934
1007
|
--max-retries) MAX_RETRIES="$2"; shift 2 ;;
|
|
935
1008
|
--max-turns) MAX_TURNS="$2"; shift 2 ;;
|
|
936
|
-
--label)
|
|
1009
|
+
--label) ISSUE_FILTERS+=("$2"); shift 2 ;;
|
|
937
1010
|
--model) MODEL_FLAG="--model $2"; shift 2 ;;
|
|
938
1011
|
-v|--version) echo "claude-queue v${VERSION}"; exit 0 ;;
|
|
939
1012
|
-h|--help) show_help; exit 0 ;;
|