claude-queue 1.4.0 → 1.5.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.
Files changed (3) hide show
  1. package/README.md +83 -48
  2. package/claude-queue.sh +455 -22
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # claude-queue
2
2
 
3
- Automated GitHub issue solver. Queue up issues, let Claude Code solve them, wake up to a PR.
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 fetches all open issues from your repo, uses [Claude Code](https://docs.anthropic.com/en/docs/claude-code) to solve each one, and opens a pull request with a summary of what was solved, what failed, and the full changelog.
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.
6
8
 
7
9
  ## Prerequisites
8
10
 
@@ -16,7 +18,7 @@ claude-queue fetches all open issues from your repo, uses [Claude Code](https://
16
18
  npm install -g claude-queue
17
19
  ```
18
20
 
19
- Or run directly with npx:
21
+ Or run directly:
20
22
 
21
23
  ```bash
22
24
  npx claude-queue
@@ -24,23 +26,20 @@ npx claude-queue
24
26
 
25
27
  ## Usage
26
28
 
29
+ ### Solving issues
30
+
27
31
  Run from inside any git repository with GitHub issues:
28
32
 
29
33
  ```bash
30
34
  claude-queue
31
35
  ```
32
36
 
33
- ### Options
34
-
35
37
  | Flag | Default | Description |
36
38
  |------|---------|-------------|
37
39
  | `--max-retries N` | `3` | Max retry attempts per issue before marking it failed |
38
- | `--max-turns N` | `50` | Max Claude Code turns per attempt (prevents runaway sessions) |
39
- | `--label LABEL` | all issues | Only process issues that have this label |
40
+ | `--max-turns N` | `50` | Max Claude Code turns per attempt |
41
+ | `--label LABEL` | all issues | Only process issues with this label |
40
42
  | `--model MODEL` | CLI default | Claude model to use (e.g. `claude-sonnet-4-5-20250929`) |
41
- | `-h, --help` | | Show help |
42
-
43
- ### Examples
44
43
 
45
44
  ```bash
46
45
  # Solve all open issues
@@ -53,6 +52,48 @@ claude-queue --label bug
53
52
  claude-queue --max-retries 5 --model claude-sonnet-4-5-20250929
54
53
  ```
55
54
 
55
+ ### Creating issues
56
+
57
+ Generate GitHub issues from a text description or an interactive interview with Claude.
58
+
59
+ ```bash
60
+ claude-queue create "Add dark mode and fix the login bug"
61
+ ```
62
+
63
+ There are three ways to provide input:
64
+
65
+ 1. **Inline text** — pass your description as an argument
66
+ 2. **Stdin** — run `claude-queue create` with no arguments, type or paste your text, then press Ctrl+D
67
+ 3. **Interactive** — run `claude-queue create -i` and Claude will ask clarifying questions before generating issues
68
+
69
+ 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.
70
+
71
+ | Flag | Default | Description |
72
+ |------|---------|-------------|
73
+ | `-i, --interactive` | off | Interview mode — Claude asks clarifying questions first |
74
+ | `--label LABEL` | none | Add this label to every created issue |
75
+ | `--model MODEL` | CLI default | Claude model to use |
76
+
77
+ ```bash
78
+ # Interactive mode
79
+ claude-queue create -i
80
+
81
+ # Add a label to all created issues
82
+ claude-queue create --label backlog "Refactor the auth module and add rate limiting"
83
+ ```
84
+
85
+ ### Create then solve workflow
86
+
87
+ The `--label` flag on both commands lets you create a pipeline where `create` plans the issues and `claude-queue` solves them:
88
+
89
+ ```bash
90
+ # Plan
91
+ claude-queue create --label nightshift "Add dark mode and fix the login bug"
92
+
93
+ # Solve
94
+ claude-queue --label nightshift
95
+ ```
96
+
56
97
  ## Configuration
57
98
 
58
99
  Create a `.claude-queue` file in your repo root to add custom instructions to every issue prompt:
@@ -63,64 +104,58 @@ Use TypeScript strict mode.
63
104
  Never modify files in the src/legacy/ directory.
64
105
  ```
65
106
 
66
- These instructions are appended to the prompt Claude receives for each issue. This is useful for project-specific conventions that aren't captured in `CLAUDE.md`.
107
+ These instructions are appended to the prompt Claude receives for each issue. Useful for project-specific conventions that aren't in `CLAUDE.md`.
67
108
 
68
109
  ## How It Works
69
110
 
70
- ### 1. Preflight
111
+ ### Preflight
71
112
 
72
- Verifies all dependencies are available (`gh`, `claude`, `git`, `jq`), checks that `gh` is authenticated, and ensures the git working tree is clean. Aborts immediately if anything is missing.
113
+ Verifies all dependencies (`gh`, `claude`, `git`, `jq`), checks that `gh` is authenticated, and ensures the working tree is clean.
73
114
 
74
- ### 2. Label Setup
115
+ ### Label setup
75
116
 
76
117
  Creates three labels on the repo (skips if they already exist):
77
118
 
78
- | Label | Color | Meaning |
79
- |-------|-------|---------|
80
- | `claude-queue:in-progress` | Yellow | Currently being worked on |
81
- | `claude-queue:solved` | Green | Successfully fixed |
82
- | `claude-queue:failed` | Red | Could not be solved after all retries |
119
+ | Label | Meaning |
120
+ |-------|---------|
121
+ | `claude-queue:in-progress` | Currently being worked on |
122
+ | `claude-queue:solved` | Successfully fixed |
123
+ | `claude-queue:failed` | Could not be solved after all retries |
83
124
 
84
- These labels let you see at a glance which issues were handled and what the outcome was.
125
+ ### Branching
85
126
 
86
- ### 3. Branch
127
+ 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.
87
128
 
88
- Creates a single branch `claude-queue/YYYY-MM-DD` off your default branch. All fixes for the night go into this one branch. If the branch already exists (e.g. from a previous run), a timestamp suffix is added.
89
-
90
- ### 4. Issue Processing
129
+ ### Issue processing
91
130
 
92
131
  For each open issue (up to 200, oldest first):
93
132
 
94
- - **Skip check** — issues that already have any `claude-queue:*` label are skipped. Remove the label to re-process.
95
- - **Label** — marks the issue `claude-queue:in-progress`
96
- - **Solve** — launches a fresh Claude Code process (`claude -p`) with a prompt that tells it to:
97
- - Read the issue via `gh issue view`
98
- - Explore the codebase
99
- - Implement a fix
100
- - Run existing tests
101
- - **Evaluate** if Claude produced file changes, they are committed. If not, the attempt is retried.
102
- - **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`).
103
- - **Label result** — marks the issue `claude-queue:solved` or `claude-queue:failed`
133
+ 1. **Skip** — issues with any `claude-queue:*` label are skipped. Remove the label to re-process.
134
+ 2. **Label** — marks the issue `claude-queue:in-progress`.
135
+ 3. **Solve** — launches Claude Code with a prompt to read the issue, explore the codebase, implement a fix, and run tests.
136
+ 4. **Evaluate** if Claude produced file changes, they are committed. If not, the attempt is retried.
137
+ 5. **Retry** — on failure, the working tree is reset and Claude gets a fresh context. Up to 3 attempts (configurable with `--max-retries`).
138
+ 6. **Result** — marks the issue `claude-queue:solved` or `claude-queue:failed`.
139
+
140
+ Issues are solved sequentially so later fixes build on earlier ones within a single branch.
141
+
142
+ ### Review pass
104
143
 
105
- Each issue is solved sequentially so later fixes build on top of earlier onesall in a single branch.
144
+ 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.
106
145
 
107
- ### 5. Pull Request
146
+ ### Pull request
108
147
 
109
- Once all issues are processed, the branch is pushed and a PR is opened with:
148
+ Once done, the branch is pushed and a PR is opened with:
110
149
 
111
- - **Summary table** solved/failed/skipped counts and run duration
112
- - **Solved issues** table of all issues that were fixed with links
113
- - **Failed issues** table of issues that couldn't be solved
114
- - **Chain logs** — collapsible per-issue logs showing Claude's full output for each attempt
150
+ - Summary table with solved/failed/skipped counts and run duration
151
+ - Tables of solved and failed issues with links
152
+ - Collapsible per-issue logs showing Claude's full output
115
153
 
116
- If no issues were solved, no PR is created.
154
+ No PR is created if nothing was solved.
117
155
 
118
- ### Interruption Handling
156
+ ### Interruption handling
119
157
 
120
- If the script is interrupted (Ctrl+C, SIGTERM), it:
121
- - Removes the `claude-queue:in-progress` label from the current issue
122
- - Marks it as `claude-queue:failed`
123
- - Prints where your commits and logs are so nothing is lost
158
+ 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.
124
159
 
125
160
  ## Logs
126
161
 
@@ -133,7 +168,7 @@ Full logs for each run are saved to `/tmp/claude-queue-DATE-TIMESTAMP/`:
133
168
  ├── issue-42-attempt-2.log # Raw Claude output, attempt 2
134
169
  ├── issue-57.md
135
170
  ├── issue-57-attempt-1.log
136
- └── pr-body.md # The generated PR description
171
+ └── pr-body.md # Generated PR description
137
172
  ```
138
173
 
139
174
  ## License
package/claude-queue.sh CHANGED
@@ -1,20 +1,24 @@
1
1
  #!/usr/bin/env bash
2
2
  #
3
- # claude-queue - Automated GitHub issue solver
3
+ # claude-queue - Automated GitHub issue solver & creator
4
4
  #
5
- # Fetches all open issues from the current repo, uses Claude Code CLI
6
- # to solve each one, and opens a PR in the morning with everything.
5
+ # Commands:
6
+ # claude-queue [options] Solve open issues (default)
7
+ # claude-queue create [options] Create issues from text or interactively
7
8
  #
8
- # Usage:
9
- # claude-queue [options]
10
- #
11
- # Options:
9
+ # Solve options:
12
10
  # --max-retries N Max retries per issue (default: 3)
13
11
  # --max-turns N Max Claude turns per attempt (default: 50)
14
12
  # --label LABEL Only process issues with this label
15
13
  # --model MODEL Claude model to use
16
14
  # -v, --version Show version
17
15
  # -h, --help Show this help message
16
+ #
17
+ # Create options:
18
+ # -i, --interactive Interview mode (Claude asks questions)
19
+ # --label LABEL Add this label to all created issues
20
+ # --model MODEL Claude model to use
21
+ # -h, --help Show help for create
18
22
 
19
23
  set -euo pipefail
20
24
 
@@ -45,19 +49,41 @@ declare -a SOLVED_ISSUES=()
45
49
  declare -a FAILED_ISSUES=()
46
50
  declare -a SKIPPED_ISSUES=()
47
51
  CURRENT_ISSUE=""
52
+ CHILD_PID=""
48
53
  START_TIME=$(date +%s)
49
54
 
50
- while [[ $# -gt 0 ]]; do
51
- case $1 in
52
- --max-retries) MAX_RETRIES="$2"; shift 2 ;;
53
- --max-turns) MAX_TURNS="$2"; shift 2 ;;
54
- --label) ISSUE_FILTER="$2"; shift 2 ;;
55
- --model) MODEL_FLAG="--model $2"; shift 2 ;;
56
- -v|--version) echo "claude-queue v${VERSION}"; exit 0 ;;
57
- -h|--help) head -16 "$0" | tail -14; exit 0 ;;
58
- *) echo "Unknown option: $1"; exit 1 ;;
59
- esac
60
- done
55
+ show_help() {
56
+ echo "claude-queue v${VERSION} — Automated GitHub issue solver & creator"
57
+ echo ""
58
+ echo "Usage:"
59
+ echo " claude-queue [options] Solve open issues (default)"
60
+ echo " claude-queue create [options] [text] Create issues from text or interactively"
61
+ echo ""
62
+ echo "Solve options:"
63
+ echo " --max-retries N Max retries per issue (default: 3)"
64
+ echo " --max-turns N Max Claude turns per attempt (default: 50)"
65
+ echo " --label LABEL Only process issues with this label"
66
+ echo " --model MODEL Claude model to use"
67
+ echo " -v, --version Show version"
68
+ echo " -h, --help Show this help message"
69
+ echo ""
70
+ echo "Run 'claude-queue create --help' for create options."
71
+ }
72
+
73
+ show_create_help() {
74
+ echo "claude-queue create — Generate GitHub issues from text or an interactive interview"
75
+ echo ""
76
+ echo "Usage:"
77
+ echo " claude-queue create \"description\" Create issues from inline text"
78
+ echo " claude-queue create Prompt for text input (Ctrl+D to finish)"
79
+ echo " claude-queue create -i Interactive interview mode"
80
+ echo ""
81
+ echo "Options:"
82
+ echo " -i, --interactive Interview mode (Claude asks clarifying questions first)"
83
+ echo " --label LABEL Add this label to every created issue"
84
+ echo " --model MODEL Claude model to use"
85
+ echo " -h, --help Show this help message"
86
+ }
61
87
 
62
88
  log() { echo -e "${DIM}$(date +%H:%M:%S)${NC} ${BLUE}[claude-queue]${NC} $1"; }
63
89
  log_success() { echo -e "${DIM}$(date +%H:%M:%S)${NC} ${GREEN}[claude-queue]${NC} $1"; }
@@ -79,8 +105,21 @@ cleanup() {
79
105
  log_warn "Branch '${BRANCH}' has your commits. Push manually if needed."
80
106
  fi
81
107
 
82
- log "Logs saved to: ${LOG_DIR}"
108
+ if [ -d "$LOG_DIR" ]; then
109
+ log "Logs saved to: ${LOG_DIR}"
110
+ fi
111
+ }
112
+
113
+ handle_interrupt() {
114
+ if [ -n "$CHILD_PID" ] && kill -0 "$CHILD_PID" 2>/dev/null; then
115
+ kill -TERM "$CHILD_PID" 2>/dev/null || true
116
+ wait "$CHILD_PID" 2>/dev/null || true
117
+ fi
118
+ CHILD_PID=""
119
+ exit 130
83
120
  }
121
+
122
+ trap handle_interrupt INT TERM
84
123
  trap cleanup EXIT
85
124
 
86
125
  preflight() {
@@ -240,7 +279,10 @@ description of what you changed and why."
240
279
  --dangerously-skip-permissions \
241
280
  --max-turns "$MAX_TURNS" \
242
281
  $MODEL_FLAG \
243
- > "$attempt_log" 2>&1 || claude_exit=$?
282
+ > "$attempt_log" 2>&1 &
283
+ CHILD_PID=$!
284
+ wait "$CHILD_PID" || claude_exit=$?
285
+ CHILD_PID=""
244
286
 
245
287
  if [ "$claude_exit" -ne 0 ]; then
246
288
  log_warn "Claude exited with code ${claude_exit}"
@@ -350,7 +392,10 @@ When you are done, output a line that says CLAUDE_QUEUE_REVIEW followed by a bri
350
392
  --dangerously-skip-permissions \
351
393
  --max-turns "$MAX_TURNS" \
352
394
  $MODEL_FLAG \
353
- > "$review_log" 2>&1 || true
395
+ > "$review_log" 2>&1 &
396
+ CHILD_PID=$!
397
+ wait "$CHILD_PID" 2>/dev/null || true
398
+ CHILD_PID=""
354
399
 
355
400
  local changed_files
356
401
  changed_files=$(git diff --name-only 2>/dev/null; git ls-files --others --exclude-standard 2>/dev/null)
@@ -536,4 +581,392 @@ main() {
536
581
  log "Logs: ${LOG_DIR}"
537
582
  }
538
583
 
539
- main "$@"
584
+ create_preflight() {
585
+ log_header "Preflight Checks"
586
+
587
+ local failed=false
588
+
589
+ for cmd in gh claude jq; do
590
+ if command -v "$cmd" &>/dev/null; then
591
+ log " $cmd ... found"
592
+ else
593
+ log_error " $cmd ... NOT FOUND"
594
+ failed=true
595
+ fi
596
+ done
597
+
598
+ if ! gh auth status &>/dev/null; then
599
+ log_error " gh auth ... not authenticated"
600
+ failed=true
601
+ else
602
+ log " gh auth ... ok"
603
+ fi
604
+
605
+ if ! git rev-parse --is-inside-work-tree &>/dev/null; then
606
+ log_error " git repo ... not inside a git repository"
607
+ failed=true
608
+ else
609
+ log " git repo ... ok"
610
+ fi
611
+
612
+ if [ "$failed" = true ]; then
613
+ log_error "Preflight failed. Aborting."
614
+ exit 1
615
+ fi
616
+ }
617
+
618
+ get_repo_labels() {
619
+ gh label list --json name -q '.[].name' 2>/dev/null | paste -sd ',' -
620
+ }
621
+
622
+ extract_json() {
623
+ local input="$1"
624
+ local json
625
+
626
+ json=$(echo "$input" | sed -n '/^```\(json\)\?$/,/^```$/{ /^```/d; p; }')
627
+ if [ -z "$json" ]; then
628
+ json="$input"
629
+ fi
630
+
631
+ if echo "$json" | jq empty 2>/dev/null; then
632
+ echo "$json"
633
+ return 0
634
+ fi
635
+
636
+ json=$(echo "$input" | grep -o '\[.*\]' | head -1)
637
+ if [ -n "$json" ] && echo "$json" | jq empty 2>/dev/null; then
638
+ echo "$json"
639
+ return 0
640
+ fi
641
+
642
+ return 1
643
+ }
644
+
645
+ create_from_text() {
646
+ local user_text="$1"
647
+ local repo_labels
648
+ repo_labels=$(get_repo_labels)
649
+
650
+ log "Analyzing text and generating issues..."
651
+
652
+ local prompt
653
+ prompt="You are a GitHub issue planner. The user wants to create issues for a repository.
654
+
655
+ Existing labels in the repo: ${repo_labels}
656
+
657
+ The user's description:
658
+ ${user_text}
659
+
660
+ Decompose this into a JSON array of well-structured GitHub issues. Each issue should have:
661
+ - \"title\": a clear, concise issue title
662
+ - \"body\": a detailed issue body in markdown (include acceptance criteria where appropriate)
663
+ - \"labels\": an array of label strings (reuse existing repo labels when they fit, or suggest new ones)
664
+
665
+ Rules:
666
+ - Create separate issues for logically distinct tasks
667
+ - Each issue should be independently actionable
668
+ - Use clear, imperative titles (e.g. \"Add dark mode toggle to settings page\")
669
+ - If the description is vague, make reasonable assumptions and note them in the body
670
+
671
+ Output ONLY the JSON array, no other text."
672
+
673
+ local output
674
+ # shellcheck disable=SC2086
675
+ output=$(claude -p "$prompt" $MODEL_FLAG 2>/dev/null)
676
+
677
+ local json
678
+ if ! json=$(extract_json "$output"); then
679
+ log_error "Failed to parse Claude's response as JSON"
680
+ log_error "Raw output:"
681
+ echo "$output"
682
+ exit 1
683
+ fi
684
+
685
+ local count
686
+ count=$(echo "$json" | jq length)
687
+ if [ "$count" -eq 0 ]; then
688
+ log_error "No issues were generated"
689
+ exit 1
690
+ fi
691
+
692
+ echo "$json"
693
+ }
694
+
695
+ create_interactive() {
696
+ local repo_labels
697
+ repo_labels=$(get_repo_labels)
698
+ local conversation=""
699
+ local max_turns=10
700
+ local turn=0
701
+
702
+ local system_prompt="You are a GitHub issue planner conducting an interview to understand what issues to create for a repository.
703
+
704
+ Existing labels in the repo: ${repo_labels}
705
+
706
+ Your job:
707
+ 1. Ask focused questions to understand what the user wants to build or fix
708
+ 2. Ask about priorities, scope, and acceptance criteria
709
+ 3. When you have enough information, output the marker CLAUDE_QUEUE_READY on its own line, followed by a JSON array of issues
710
+
711
+ Each issue in the JSON array should have:
712
+ - \"title\": a clear, concise issue title
713
+ - \"body\": a detailed issue body in markdown
714
+ - \"labels\": an array of label strings (reuse existing repo labels when they fit)
715
+
716
+ Rules:
717
+ - Ask one question at a time
718
+ - Keep questions short and specific
719
+ - After 2-3 questions you should have enough context — don't over-interview
720
+ - If the user says \"done\", immediately generate the issues with what you know
721
+ - Output ONLY your question text (no JSON) until you're ready to generate issues
722
+ - When ready, output CLAUDE_QUEUE_READY on its own line followed by ONLY the JSON array"
723
+
724
+ echo -e "${BOLD}Interactive issue creation${NC}"
725
+ echo -e "${DIM}Answer Claude's questions. Type 'done' to generate issues at any time.${NC}"
726
+ echo ""
727
+
728
+ while [ "$turn" -lt "$max_turns" ]; do
729
+ turn=$((turn + 1))
730
+
731
+ local prompt
732
+ if [ -z "$conversation" ]; then
733
+ prompt="${system_prompt}
734
+
735
+ Start by asking your first question."
736
+ else
737
+ prompt="${system_prompt}
738
+
739
+ Conversation so far:
740
+ ${conversation}
741
+
742
+ Continue the interview or, if you have enough information, output CLAUDE_QUEUE_READY followed by the JSON array."
743
+ fi
744
+
745
+ local output
746
+ # shellcheck disable=SC2086
747
+ output=$(claude -p "$prompt" $MODEL_FLAG 2>/dev/null)
748
+
749
+ if echo "$output" | grep -q "CLAUDE_QUEUE_READY"; then
750
+ local json_part
751
+ json_part=$(echo "$output" | sed -n '/CLAUDE_QUEUE_READY/,$ p' | tail -n +2)
752
+
753
+ local json
754
+ if ! json=$(extract_json "$json_part"); then
755
+ log_error "Failed to parse generated issues as JSON"
756
+ exit 1
757
+ fi
758
+
759
+ echo "$json"
760
+ return 0
761
+ fi
762
+
763
+ echo -e "${BLUE}Claude:${NC} ${output}"
764
+ echo ""
765
+
766
+ local user_input
767
+ read -r -p "You: " user_input
768
+
769
+ if [ "$user_input" = "done" ]; then
770
+ conversation="${conversation}
771
+ Claude: ${output}
772
+ User: Please generate the issues now with what you know."
773
+
774
+ local final_prompt="${system_prompt}
775
+
776
+ Conversation so far:
777
+ ${conversation}
778
+
779
+ The user wants you to generate the issues now. Output CLAUDE_QUEUE_READY followed by the JSON array."
780
+
781
+ local final_output
782
+ # shellcheck disable=SC2086
783
+ final_output=$(claude -p "$final_prompt" $MODEL_FLAG 2>/dev/null)
784
+
785
+ local final_json_part
786
+ final_json_part=$(echo "$final_output" | sed -n '/CLAUDE_QUEUE_READY/,$ p' | tail -n +2)
787
+ if [ -z "$final_json_part" ]; then
788
+ final_json_part="$final_output"
789
+ fi
790
+
791
+ local json
792
+ if ! json=$(extract_json "$final_json_part"); then
793
+ log_error "Failed to parse generated issues as JSON"
794
+ exit 1
795
+ fi
796
+
797
+ echo "$json"
798
+ return 0
799
+ fi
800
+
801
+ conversation="${conversation}
802
+ Claude: ${output}
803
+ User: ${user_input}"
804
+ done
805
+
806
+ log_warn "Reached maximum interview turns, generating issues with current information..."
807
+
808
+ local final_prompt="${system_prompt}
809
+
810
+ Conversation so far:
811
+ ${conversation}
812
+
813
+ You've reached the maximum number of questions. Output CLAUDE_QUEUE_READY followed by the JSON array now."
814
+
815
+ local final_output
816
+ # shellcheck disable=SC2086
817
+ final_output=$(claude -p "$final_prompt" $MODEL_FLAG 2>/dev/null)
818
+
819
+ local final_json_part
820
+ final_json_part=$(echo "$final_output" | sed -n '/CLAUDE_QUEUE_READY/,$ p' | tail -n +2)
821
+ if [ -z "$final_json_part" ]; then
822
+ final_json_part="$final_output"
823
+ fi
824
+
825
+ local json
826
+ if ! json=$(extract_json "$final_json_part"); then
827
+ log_error "Failed to parse generated issues as JSON"
828
+ exit 1
829
+ fi
830
+
831
+ echo "$json"
832
+ }
833
+
834
+ preview_issues() {
835
+ local json="$1"
836
+ local count
837
+ count=$(echo "$json" | jq length)
838
+
839
+ echo ""
840
+ echo -e "${BOLD}═══ Issue Preview ═══${NC}"
841
+ echo ""
842
+
843
+ for i in $(seq 0 $((count - 1))); do
844
+ local title labels body
845
+ title=$(echo "$json" | jq -r ".[$i].title")
846
+ labels=$(echo "$json" | jq -r ".[$i].labels // [] | join(\", \")")
847
+ body=$(echo "$json" | jq -r ".[$i].body" | head -3)
848
+
849
+ echo -e " ${BOLD}$((i + 1)). ${title}${NC}"
850
+ if [ -n "$labels" ]; then
851
+ echo -e " ${DIM}Labels: ${labels}${NC}"
852
+ fi
853
+ echo -e " ${DIM}$(echo "$body" | head -1)${NC}"
854
+ echo ""
855
+ done
856
+ }
857
+
858
+ confirm_and_create() {
859
+ local json="$1"
860
+ local extra_label="$2"
861
+ local count
862
+ count=$(echo "$json" | jq length)
863
+
864
+ local prompt_text="Create ${count} issue(s)? [y/N] "
865
+ read -r -p "$prompt_text" confirm
866
+
867
+ if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
868
+ log "Cancelled."
869
+ exit 0
870
+ fi
871
+
872
+ echo ""
873
+
874
+ for i in $(seq 0 $((count - 1))); do
875
+ local title body
876
+ title=$(echo "$json" | jq -r ".[$i].title")
877
+ body=$(echo "$json" | jq -r ".[$i].body")
878
+
879
+ local label_args=()
880
+ local issue_labels
881
+ issue_labels=$(echo "$json" | jq -r ".[$i].labels // [] | .[]")
882
+ while IFS= read -r lbl; do
883
+ if [ -n "$lbl" ]; then
884
+ label_args+=(--label "$lbl")
885
+ fi
886
+ done <<< "$issue_labels"
887
+
888
+ if [ -n "$extra_label" ]; then
889
+ label_args+=(--label "$extra_label")
890
+ fi
891
+
892
+ local issue_url
893
+ issue_url=$(gh issue create --title "$title" --body "$body" "${label_args[@]}" 2>&1)
894
+ log_success "Created: ${issue_url}"
895
+ done
896
+
897
+ echo ""
898
+ log_success "Created ${count} issue(s)"
899
+ }
900
+
901
+ cmd_create() {
902
+ local interactive=false
903
+ local extra_label=""
904
+ local user_text=""
905
+
906
+ while [[ $# -gt 0 ]]; do
907
+ case $1 in
908
+ -i|--interactive) interactive=true; shift ;;
909
+ --label) extra_label="$2"; shift 2 ;;
910
+ --model) MODEL_FLAG="--model $2"; shift 2 ;;
911
+ -h|--help) show_create_help; exit 0 ;;
912
+ -*) echo "Unknown option: $1"; echo ""; show_create_help; exit 1 ;;
913
+ *) user_text="$1"; shift ;;
914
+ esac
915
+ done
916
+
917
+ create_preflight
918
+
919
+ local json
920
+
921
+ if [ "$interactive" = true ]; then
922
+ json=$(create_interactive)
923
+ elif [ -n "$user_text" ]; then
924
+ json=$(create_from_text "$user_text")
925
+ else
926
+ echo -e "${BOLD}Describe what issues you want to create.${NC}"
927
+ echo -e "${DIM}Type or paste your text, then press Ctrl+D when done.${NC}"
928
+ echo ""
929
+ user_text=$(cat)
930
+ if [ -z "$user_text" ]; then
931
+ log_error "No input provided"
932
+ exit 1
933
+ fi
934
+ json=$(create_from_text "$user_text")
935
+ fi
936
+
937
+ preview_issues "$json"
938
+ confirm_and_create "$json" "$extra_label"
939
+ }
940
+
941
+ # --- Subcommand routing ---
942
+
943
+ SUBCOMMAND=""
944
+ if [[ $# -gt 0 ]] && [[ "$1" != -* ]]; then
945
+ SUBCOMMAND="$1"; shift
946
+ fi
947
+
948
+ case "$SUBCOMMAND" in
949
+ "")
950
+ while [[ $# -gt 0 ]]; do
951
+ case $1 in
952
+ --max-retries) MAX_RETRIES="$2"; shift 2 ;;
953
+ --max-turns) MAX_TURNS="$2"; shift 2 ;;
954
+ --label) ISSUE_FILTER="$2"; shift 2 ;;
955
+ --model) MODEL_FLAG="--model $2"; shift 2 ;;
956
+ -v|--version) echo "claude-queue v${VERSION}"; exit 0 ;;
957
+ -h|--help) show_help; exit 0 ;;
958
+ *) echo "Unknown option: $1"; exit 1 ;;
959
+ esac
960
+ done
961
+ main
962
+ ;;
963
+ create)
964
+ cmd_create "$@"
965
+ ;;
966
+ *)
967
+ echo "Unknown command: $SUBCOMMAND"
968
+ echo ""
969
+ show_help
970
+ exit 1
971
+ ;;
972
+ esac
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-queue",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
4
4
  "description": "Automated GitHub issue solver powered by Claude Code",
5
5
  "bin": {
6
6
  "claude-queue": "./claude-queue.sh"