wiggum-cli 0.14.0 → 0.15.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.
Files changed (38) hide show
  1. package/README.md +24 -5
  2. package/dist/ai/providers.js +19 -14
  3. package/dist/commands/run.d.ts +1 -1
  4. package/dist/commands/run.js +2 -2
  5. package/dist/index.js +7 -1
  6. package/dist/repl/session-state.d.ts +2 -0
  7. package/dist/templates/config/ralph.config.cjs.tmpl +1 -1
  8. package/dist/templates/prompts/PROMPT_review_auto.md.tmpl +7 -41
  9. package/dist/templates/prompts/PROMPT_review_merge.md.tmpl +163 -0
  10. package/dist/templates/scripts/feature-loop-actions.test.ts +92 -0
  11. package/dist/templates/scripts/feature-loop.sh.tmpl +157 -7
  12. package/dist/tui/app.js +20 -2
  13. package/dist/tui/components/ChatInput.d.ts +3 -1
  14. package/dist/tui/components/ChatInput.js +23 -4
  15. package/dist/tui/components/CommandDropdown.d.ts +3 -1
  16. package/dist/tui/components/CommandDropdown.js +10 -7
  17. package/dist/tui/components/SpecCompletionSummary.d.ts +3 -2
  18. package/dist/tui/components/SpecCompletionSummary.js +26 -9
  19. package/dist/tui/components/SummaryBox.d.ts +0 -3
  20. package/dist/tui/components/SummaryBox.js +4 -2
  21. package/dist/tui/orchestration/interview-orchestrator.js +35 -5
  22. package/dist/tui/screens/MainShell.js +2 -1
  23. package/dist/tui/screens/RunScreen.js +81 -12
  24. package/dist/tui/utils/action-inbox.d.ts +43 -0
  25. package/dist/tui/utils/action-inbox.js +109 -0
  26. package/dist/tui/utils/polishGoal.d.ts +37 -0
  27. package/dist/tui/utils/polishGoal.js +170 -0
  28. package/dist/utils/config.d.ts +1 -1
  29. package/dist/utils/fuzzy-match.d.ts +5 -0
  30. package/dist/utils/fuzzy-match.js +16 -0
  31. package/dist/utils/spec-names.d.ts +6 -0
  32. package/dist/utils/spec-names.js +23 -0
  33. package/package.json +9 -4
  34. package/src/templates/config/ralph.config.cjs.tmpl +1 -1
  35. package/src/templates/prompts/PROMPT_review_auto.md.tmpl +7 -41
  36. package/src/templates/prompts/PROMPT_review_merge.md.tmpl +163 -0
  37. package/src/templates/scripts/feature-loop-actions.test.ts +92 -0
  38. package/src/templates/scripts/feature-loop.sh.tmpl +157 -7
@@ -95,7 +95,7 @@ fi
95
95
  ```
96
96
 
97
97
  **Handle review feedback:**
98
- - If Claude outputs "APPROVED" -> Proceed to Step 5 (rebase and merge)
98
+ - If Claude outputs "APPROVED" -> Done. The PR is ready for manual merge by the user.
99
99
  - If Claude lists issues:
100
100
  1. Address each issue with code fixes
101
101
  2. Commit: `git -C {{appDir}} add -A && git -C {{appDir}} commit -m "fix($FEATURE): address review feedback"`
@@ -103,47 +103,14 @@ fi
103
103
  4. Re-run the Claude review command above
104
104
  - Max 3 review iterations before requiring manual intervention
105
105
 
106
- ### Step 5: Rebase Before Merge (for Parallel Execution)
107
- Before merging, ensure branch is up-to-date with main:
108
- ```bash
109
- cd {{appDir}} && git fetch origin main
110
- cd {{appDir}} && git rebase origin/main
111
- ```
112
-
113
- If rebase has conflicts:
114
- 1. Resolve conflicts in affected files
115
- 2. `git add .` the resolved files
116
- 3. `git rebase --continue`
117
- 4. Re-run tests: `{{testCommand}} && {{buildCommand}}`
118
-
119
- Push rebased branch:
120
- ```bash
121
- cd {{appDir}} && git push --force-with-lease origin feat/$FEATURE
122
- ```
123
-
124
- ### Step 6: Merge PR
125
- When Claude review is approved and branch is rebased:
126
- ```bash
127
- cd {{appDir}} && gh pr merge --squash --delete-branch
128
- ```
129
-
130
- ### Step 7: Post-Merge Cleanup
131
- 1. If using worktree, remove it:
132
- ```bash
133
- # Only if this feature used a worktree ({{appDir}}-$FEATURE directory exists)
134
- git -C {{appDir}} worktree remove "../{{appDir}}-$FEATURE" 2>/dev/null || true
135
- ```
136
- 2. Checkout main and pull:
137
- ```bash
138
- git -C {{appDir}} checkout main && git -C {{appDir}} pull
139
- ```
140
-
141
- Note: Spec status updates are handled in the Spec Verification phase before PR creation.
106
+ ### Step 5: Final Summary
107
+ After review is complete (approved or max iterations reached):
108
+ 1. Post a summary comment on the PR with the review outcome
109
+ 2. Do NOT merge the user will review and merge manually
142
110
 
143
111
  ## Rules
144
- - Do NOT merge without Claude Code approval
145
- - Address ALL review comments before merging
146
- - Use squash merge to keep history clean
112
+ - Do NOT merge the PR auto mode only reviews, the user merges
113
+ - Address ALL review comments before marking as approved
147
114
  - If gh CLI fails, check authentication: `gh auth status`
148
115
  - Keep review conversation focused and professional
149
116
 
@@ -152,7 +119,6 @@ Note: Spec status updates are handled in the Spec Verification phase before PR c
152
119
  - **gh auth error** -> Run: `gh auth login`
153
120
  - **PR already exists** -> Use: `gh pr view` to see status
154
121
  - **Claude Code CLI not installed** -> Install: `npm install -g @anthropic-ai/claude-code`
155
- - **Rebase conflicts** -> Resolve carefully, re-run all tests after
156
122
 
157
123
  ## Learning Capture
158
124
  If the review revealed patterns worth remembering, append to @.ralph/LEARNINGS.md:
@@ -0,0 +1,163 @@
1
+ ## Context
2
+ Study @.ralph/AGENTS.md for commands and patterns.
3
+ Study @.ralph/specs/$FEATURE.md for feature specification.
4
+ Study @.ralph/specs/$FEATURE-implementation-plan.md for completed tasks.
5
+
6
+ ## Learnings
7
+ Read @.ralph/LEARNINGS.md for patterns from previous features.
8
+ Capture any review feedback patterns for future iterations.
9
+
10
+ ## Task
11
+ All implementation and E2E tasks are complete. Create PR, review, and merge.
12
+
13
+ ### Step 1: Verify Ready State
14
+ 1. Check all tasks are complete in implementation plan (no `- [ ]` items)
15
+ 2. Verify tests pass: `cd {{appDir}} && {{testCommand}}`
16
+ 3. Verify build succeeds: `cd {{appDir}} && {{buildCommand}}`
17
+
18
+ If any fail, fix before proceeding.
19
+
20
+ ### Step 2: Check Git Status
21
+ ```bash
22
+ cd {{appDir}} && git status
23
+ cd {{appDir}} && git log --oneline -5
24
+ ```
25
+
26
+ Ensure:
27
+ - On branch `feat/$FEATURE`
28
+ - All changes are committed
29
+ - Branch is pushed to remote
30
+
31
+ If uncommitted changes exist:
32
+ ```bash
33
+ git -C {{appDir}} add -A && git -C {{appDir}} commit -m "chore($FEATURE): final cleanup"
34
+ git -C {{appDir}} push origin feat/$FEATURE
35
+ ```
36
+
37
+ ### Step 3: Create PR
38
+ Check if PR already exists:
39
+ ```bash
40
+ cd {{appDir}} && gh pr list --head feat/$FEATURE
41
+ ```
42
+
43
+ If no PR exists, create one:
44
+ ```bash
45
+ cd {{appDir}} && gh pr create --base main --head feat/$FEATURE \
46
+ --title "feat($FEATURE): [read description from spec]" \
47
+ --body "$(cat <<'EOF'
48
+ ## Summary
49
+ [Read from spec Purpose section]
50
+
51
+ ## Changes
52
+ [Read from implementation plan - list completed phases]
53
+
54
+ ## Testing
55
+ - [x] Unit/integration tests: 97 passing
56
+ - [x] E2E tests: All scenarios passed via Playwright MCP
57
+ - [x] Build succeeds
58
+
59
+ ## E2E Test Results
60
+ [Copy from implementation plan Phase 9]
61
+
62
+ Generated with Claude Code
63
+ EOF
64
+ )"
65
+ ```
66
+
67
+ ### Step 4: Request Claude Code Review
68
+
69
+ Run automated code review using Claude Code CLI:
70
+
71
+ ```bash
72
+ # Check if Claude Code CLI is installed
73
+ if ! command -v claude &> /dev/null; then
74
+ echo "WARNING: Claude Code CLI not installed. Manual review needed."
75
+ cd {{appDir}} && gh pr comment --body "Manual review requested - Claude Code CLI not available. Install: https://docs.anthropic.com/en/docs/claude-code/overview"
76
+ else
77
+ echo "Running Claude Code review..."
78
+
79
+ cd {{appDir}} && claude -p "You are reviewing a PR for the $FEATURE feature.
80
+
81
+ Review the git diff against main and check:
82
+ - Code quality and patterns consistency
83
+ - Test coverage adequacy
84
+ - Potential bugs or edge cases
85
+ - Security concerns (injection, XSS, etc.)
86
+ - Performance implications
87
+ - Error handling completeness
88
+
89
+ Run: git diff main
90
+
91
+ Respond with:
92
+ - APPROVED if everything looks good
93
+ - Or list specific issues with file:line references that need to be fixed"
94
+ fi
95
+ ```
96
+
97
+ **Handle review feedback:**
98
+ - If Claude outputs "APPROVED" -> Proceed to Step 5 (rebase and merge)
99
+ - If Claude lists issues:
100
+ 1. Address each issue with code fixes
101
+ 2. Commit: `git -C {{appDir}} add -A && git -C {{appDir}} commit -m "fix($FEATURE): address review feedback"`
102
+ 3. Push: `git -C {{appDir}} push origin feat/$FEATURE`
103
+ 4. Re-run the Claude review command above
104
+ - Max 3 review iterations before requiring manual intervention
105
+
106
+ ### Step 5: Rebase Before Merge (for Parallel Execution)
107
+ Before merging, ensure branch is up-to-date with main:
108
+ ```bash
109
+ cd {{appDir}} && git fetch origin main
110
+ cd {{appDir}} && git rebase origin/main
111
+ ```
112
+
113
+ If rebase has conflicts:
114
+ 1. Resolve conflicts in affected files
115
+ 2. `git add .` the resolved files
116
+ 3. `git rebase --continue`
117
+ 4. Re-run tests: `{{testCommand}} && {{buildCommand}}`
118
+
119
+ Push rebased branch:
120
+ ```bash
121
+ cd {{appDir}} && git push --force-with-lease origin feat/$FEATURE
122
+ ```
123
+
124
+ ### Step 6: Merge PR
125
+ When Claude review is approved and branch is rebased:
126
+ ```bash
127
+ cd {{appDir}} && gh pr merge --squash --delete-branch
128
+ ```
129
+
130
+ ### Step 7: Post-Merge Cleanup
131
+ 1. If using worktree, remove it:
132
+ ```bash
133
+ # Only if this feature used a worktree ({{appDir}}-$FEATURE directory exists)
134
+ git -C {{appDir}} worktree remove "../{{appDir}}-$FEATURE" 2>/dev/null || true
135
+ ```
136
+ 2. Checkout main and pull:
137
+ ```bash
138
+ git -C {{appDir}} checkout main && git -C {{appDir}} pull
139
+ ```
140
+
141
+ Note: Spec status updates are handled in the Spec Verification phase before PR creation.
142
+
143
+ ## Rules
144
+ - Do NOT merge without Claude Code approval
145
+ - Address ALL review comments before merging
146
+ - Use squash merge to keep history clean
147
+ - If gh CLI fails, check authentication: `gh auth status`
148
+ - Keep review conversation focused and professional
149
+
150
+ ## Troubleshooting
151
+ - **gh: command not found** -> Install GitHub CLI: `brew install gh`
152
+ - **gh auth error** -> Run: `gh auth login`
153
+ - **PR already exists** -> Use: `gh pr view` to see status
154
+ - **Claude Code CLI not installed** -> Install: `npm install -g @anthropic-ai/claude-code`
155
+ - **Rebase conflicts** -> Resolve carefully, re-run all tests after
156
+
157
+ ## Learning Capture
158
+ If the review revealed patterns worth remembering, append to @.ralph/LEARNINGS.md:
159
+ - Code quality feedback -> Add under "## Anti-Patterns" or "## Patterns"
160
+ - Common review issues -> Add under "## Anti-Patterns"
161
+ - Good practices identified -> Add under "## Patterns"
162
+
163
+ Format: `- [YYYY-MM-DD] [$FEATURE] Brief description`
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Tests for the action request JSON embedded in feature-loop.sh.tmpl
3
+ *
4
+ * The shell template contains a JSON payload written by write_action_request().
5
+ * These tests validate that the JSON structure matches the ActionRequest schema
6
+ * expected by the TUI and action-inbox helpers.
7
+ *
8
+ * Note: Full bash integration tests are out of scope. This focuses on the
9
+ * JSON schema validation by reading the template file directly.
10
+ */
11
+
12
+ import { describe, it, expect } from 'vitest';
13
+ import { readFileSync } from 'node:fs';
14
+ import { join, dirname } from 'node:path';
15
+ import { fileURLToPath } from 'node:url';
16
+
17
+ const __dirname = dirname(fileURLToPath(import.meta.url));
18
+ const TEMPLATE_PATH = join(__dirname, 'feature-loop.sh.tmpl');
19
+
20
+ /**
21
+ * Extract the JSON block written by write_action_request() from the template.
22
+ * The JSON lives between `cat > "$action_file" << 'EOF'` and `EOF`.
23
+ */
24
+ function extractActionRequestJson(): unknown {
25
+ const content = readFileSync(TEMPLATE_PATH, 'utf-8');
26
+
27
+ // Match the heredoc block: cat > ... << 'EOF' ... EOF
28
+ const match = content.match(/cat\s*>\s*"\$action_file"\s*<<\s*'EOF'\s*\n([\s\S]*?)\nEOF/);
29
+ if (!match?.[1]) {
30
+ throw new Error('Could not find action request JSON in feature-loop.sh.tmpl');
31
+ }
32
+
33
+ return JSON.parse(match[1]);
34
+ }
35
+
36
+ describe('feature-loop.sh.tmpl — action request JSON schema', () => {
37
+ it('parses the embedded JSON without errors', () => {
38
+ const parsed = extractActionRequestJson();
39
+ expect(parsed).toBeDefined();
40
+ });
41
+
42
+ it('has a non-empty string id field', () => {
43
+ const parsed = extractActionRequestJson() as Record<string, unknown>;
44
+ expect(typeof parsed.id).toBe('string');
45
+ expect((parsed.id as string).length).toBeGreaterThan(0);
46
+ });
47
+
48
+ it('has a non-empty string prompt field', () => {
49
+ const parsed = extractActionRequestJson() as Record<string, unknown>;
50
+ expect(typeof parsed.prompt).toBe('string');
51
+ expect((parsed.prompt as string).length).toBeGreaterThan(0);
52
+ });
53
+
54
+ it('has a choices array with at least one entry', () => {
55
+ const parsed = extractActionRequestJson() as Record<string, unknown>;
56
+ expect(Array.isArray(parsed.choices)).toBe(true);
57
+ expect((parsed.choices as unknown[]).length).toBeGreaterThan(0);
58
+ });
59
+
60
+ it('each choice has a non-empty string id and label', () => {
61
+ const parsed = extractActionRequestJson() as Record<string, unknown>;
62
+ const choices = parsed.choices as Array<Record<string, unknown>>;
63
+
64
+ for (const choice of choices) {
65
+ expect(typeof choice.id).toBe('string');
66
+ expect((choice.id as string).length).toBeGreaterThan(0);
67
+ expect(typeof choice.label).toBe('string');
68
+ expect((choice.label as string).length).toBeGreaterThan(0);
69
+ }
70
+ });
71
+
72
+ it('has a non-empty string default field', () => {
73
+ const parsed = extractActionRequestJson() as Record<string, unknown>;
74
+ expect(typeof parsed.default).toBe('string');
75
+ expect((parsed.default as string).length).toBeGreaterThan(0);
76
+ });
77
+
78
+ it('default value matches one of the choice ids', () => {
79
+ const parsed = extractActionRequestJson() as Record<string, unknown>;
80
+ const choices = parsed.choices as Array<Record<string, unknown>>;
81
+ const choiceIds = choices.map((c) => c.id as string);
82
+ expect(choiceIds).toContain(parsed.default as string);
83
+ });
84
+
85
+ it('all required ActionRequest fields are present', () => {
86
+ const parsed = extractActionRequestJson() as Record<string, unknown>;
87
+ expect(parsed).toHaveProperty('id');
88
+ expect(parsed).toHaveProperty('prompt');
89
+ expect(parsed).toHaveProperty('choices');
90
+ expect(parsed).toHaveProperty('default');
91
+ });
92
+ });
@@ -6,8 +6,8 @@
6
6
  # Options:
7
7
  # --worktree Use git worktree for isolation (enables parallel execution)
8
8
  # --resume Resume an interrupted loop (reuses existing branch/worktree)
9
- # --model MODEL Claude model to use (e.g., opus, sonnet, claude-sonnet-4-5-20250514)
10
- # --review-mode MODE Review mode: 'manual' (stop at PR) or 'auto' (review + merge). Default: 'manual'
9
+ # --model MODEL Claude model to use (e.g., opus, sonnet, claude-sonnet-4-5-20250929)
10
+ # --review-mode MODE Review mode: 'manual' (stop at PR), 'auto' (review, no merge), or 'merge' (review + merge). Default: 'manual'
11
11
 
12
12
  set -e
13
13
  set -o pipefail
@@ -91,8 +91,8 @@ if [ -z "$REVIEW_MODE" ]; then
91
91
  fi
92
92
 
93
93
  # Validate review mode
94
- if [ "$REVIEW_MODE" != "manual" ] && [ "$REVIEW_MODE" != "auto" ]; then
95
- echo "ERROR: Invalid review mode: '$REVIEW_MODE'. Allowed values are 'manual' or 'auto'." >&2
94
+ if [ "$REVIEW_MODE" != "manual" ] && [ "$REVIEW_MODE" != "auto" ] && [ "$REVIEW_MODE" != "merge" ]; then
95
+ echo "ERROR: Invalid review mode: '$REVIEW_MODE'. Allowed values are 'manual', 'auto', or 'merge'." >&2
96
96
  exit 1
97
97
  fi
98
98
 
@@ -138,6 +138,68 @@ parse_and_accumulate_tokens() {
138
138
  echo "${new_input}|${new_output}" > "$TOKENS_FILE"
139
139
  }
140
140
 
141
+ # Action inbox: write request file if not already present
142
+ write_action_request() {
143
+ local action_file="/tmp/ralph-loop-${FEATURE}.action.json"
144
+ if [ -f "$action_file" ]; then
145
+ echo "WARNING: Action request file already exists, skipping write: $action_file" >&2
146
+ return 0
147
+ fi
148
+ cat > "$action_file" << 'EOF'
149
+ {
150
+ "id": "post_pr_choice",
151
+ "prompt": "Loop complete. What would you like to do?",
152
+ "choices": [
153
+ {"id": "done", "label": "Done — end loop"},
154
+ {"id": "merge_local", "label": "Merge back to main locally"},
155
+ {"id": "keep_branch", "label": "Keep branch as-is"},
156
+ {"id": "discard", "label": "Discard this work"}
157
+ ],
158
+ "default": "done"
159
+ }
160
+ EOF
161
+ echo "Action request written: $action_file"
162
+ }
163
+
164
+ # Action inbox: poll for reply file, fallback to default after 15 minutes
165
+ poll_action_reply() {
166
+ local action_file="/tmp/ralph-loop-${FEATURE}.action.json"
167
+ local reply_file="/tmp/ralph-loop-${FEATURE}.action.reply.json"
168
+ local default_choice="keep_branch"
169
+ local timeout=900 # 15 minutes in seconds
170
+ local elapsed=0
171
+
172
+ # Read default from action file if present
173
+ if [ -f "$action_file" ]; then
174
+ local parsed_default
175
+ parsed_default=$(node -e "try { const d=require('fs').readFileSync(process.argv[1],'utf8'); console.log(JSON.parse(d).default||'keep_branch'); } catch(e) { console.log('keep_branch'); }" "$action_file" 2>/dev/null || echo "keep_branch")
176
+ if [ -n "$parsed_default" ]; then
177
+ default_choice="$parsed_default"
178
+ fi
179
+ fi
180
+
181
+ while [ $elapsed -lt $timeout ]; do
182
+ if [ -f "$reply_file" ]; then
183
+ local choice
184
+ choice=$(node -e "try { const d=require('fs').readFileSync(process.argv[1],'utf8'); console.log(JSON.parse(d).choice||''); } catch(e) { console.log(''); }" "$reply_file" 2>/dev/null || echo "")
185
+ if [ -n "$choice" ]; then
186
+ echo "User selected: $choice" >&2
187
+ # Cleanup both files
188
+ rm -f "$action_file" "$reply_file" 2>/dev/null || true
189
+ echo "$choice"
190
+ return 0
191
+ fi
192
+ fi
193
+ sleep 1
194
+ elapsed=$((elapsed + 1))
195
+ done
196
+
197
+ # Timeout: use default
198
+ echo "Action reply timeout after ${timeout}s, using default: $default_choice" >&2
199
+ rm -f "$action_file" "$reply_file" 2>/dev/null || true
200
+ echo "$default_choice"
201
+ }
202
+
141
203
  # Initialize tokens
142
204
  init_tokens
143
205
 
@@ -343,18 +405,106 @@ echo "======================== PR & REVIEW PHASE ========================"
343
405
  write_phase_start "pr_review"
344
406
  export FEATURE APP_DIR SPEC_DIR PROMPTS_DIR
345
407
  PR_STATUS="success"
408
+ MAX_REVIEW_ATTEMPTS=3
409
+
346
410
  if [ "$REVIEW_MODE" = "manual" ]; then
347
411
  if ! cat "$PROMPTS_DIR/PROMPT_review_manual.md" | envsubst | $CLAUDE_CMD_OPUS 2>&1 | tee "$CLAUDE_OUTPUT"; then
348
412
  PR_STATUS="failed"
349
413
  fi
350
- else
351
- if ! cat "$PROMPTS_DIR/PROMPT_review_auto.md" | envsubst | $CLAUDE_CMD_OPUS 2>&1 | tee "$CLAUDE_OUTPUT"; then
414
+ parse_and_accumulate_tokens "$CLAUDE_OUTPUT"
415
+
416
+ elif [ "$REVIEW_MODE" = "merge" ]; then
417
+ # Merge mode: create PR, iterate review+fixes until approved, then merge
418
+ REVIEW_ATTEMPT=0
419
+ REVIEW_APPROVED=false
420
+ while [ $REVIEW_ATTEMPT -lt $MAX_REVIEW_ATTEMPTS ]; do
421
+ REVIEW_ATTEMPT=$((REVIEW_ATTEMPT + 1))
422
+ echo "--- Review attempt $REVIEW_ATTEMPT of $MAX_REVIEW_ATTEMPTS ---"
423
+ cat "$PROMPTS_DIR/PROMPT_review_merge.md" | envsubst | $CLAUDE_CMD_OPUS 2>&1 | tee "$CLAUDE_OUTPUT" || true
424
+ parse_and_accumulate_tokens "$CLAUDE_OUTPUT"
425
+
426
+ # Check if output contains APPROVED
427
+ if grep -qi "APPROVED" "$CLAUDE_OUTPUT" 2>/dev/null; then
428
+ echo "Review approved!"
429
+ REVIEW_APPROVED=true
430
+ break
431
+ fi
432
+
433
+ if [ $REVIEW_ATTEMPT -lt $MAX_REVIEW_ATTEMPTS ]; then
434
+ echo "Review found issues. Running fix iteration..."
435
+ echo "Fix the issues found in the code review above. Run git diff main to see the current changes, then:
436
+ 1. Fix each issue referenced in the review
437
+ 2. Run tests: npm test
438
+ 3. Commit and push the fixes
439
+ Do NOT propose completion options or ask interactive questions. Just fix, test, commit, push." | $CLAUDE_CMD_IMPL 2>&1 | tee "$CLAUDE_OUTPUT" || true
440
+ parse_and_accumulate_tokens "$CLAUDE_OUTPUT"
441
+ fi
442
+ done
443
+ if [ "$REVIEW_APPROVED" != true ]; then
444
+ echo "Review not approved after $MAX_REVIEW_ATTEMPTS attempts."
352
445
  PR_STATUS="failed"
353
446
  fi
447
+
448
+ else
449
+ # Auto mode: create PR, iterate review+fixes until approved (no merge)
450
+ REVIEW_ATTEMPT=0
451
+ REVIEW_APPROVED=false
452
+ while [ $REVIEW_ATTEMPT -lt $MAX_REVIEW_ATTEMPTS ]; do
453
+ REVIEW_ATTEMPT=$((REVIEW_ATTEMPT + 1))
454
+ echo "--- Review attempt $REVIEW_ATTEMPT of $MAX_REVIEW_ATTEMPTS ---"
455
+ cat "$PROMPTS_DIR/PROMPT_review_auto.md" | envsubst | $CLAUDE_CMD_OPUS 2>&1 | tee "$CLAUDE_OUTPUT" || true
456
+ parse_and_accumulate_tokens "$CLAUDE_OUTPUT"
457
+
458
+ # Check if output contains APPROVED
459
+ if grep -qi "APPROVED" "$CLAUDE_OUTPUT" 2>/dev/null; then
460
+ echo "Review approved!"
461
+ REVIEW_APPROVED=true
462
+ break
463
+ fi
464
+
465
+ if [ $REVIEW_ATTEMPT -lt $MAX_REVIEW_ATTEMPTS ]; then
466
+ echo "Review found issues. Running fix iteration..."
467
+ echo "Fix the issues found in the code review above. Run git diff main to see the current changes, then:
468
+ 1. Fix each issue referenced in the review
469
+ 2. Run tests: npm test
470
+ 3. Commit and push the fixes
471
+ Do NOT propose completion options or ask interactive questions. Just fix, test, commit, push." | $CLAUDE_CMD_IMPL 2>&1 | tee "$CLAUDE_OUTPUT" || true
472
+ parse_and_accumulate_tokens "$CLAUDE_OUTPUT"
473
+ fi
474
+ done
475
+ if [ "$REVIEW_APPROVED" != true ]; then
476
+ echo "Review not approved after $MAX_REVIEW_ATTEMPTS attempts. PR ready for manual review."
477
+ fi
354
478
  fi
355
- parse_and_accumulate_tokens "$CLAUDE_OUTPUT"
356
479
  write_phase_end "pr_review" "$PR_STATUS"
357
480
 
481
+ # Phase 7.5: Post-completion action request
482
+ echo "======================== ACTION REQUEST PHASE ========================"
483
+ write_action_request
484
+ CHOSEN_ACTION=$(poll_action_reply)
485
+ echo "User chose: $CHOSEN_ACTION"
486
+
487
+ # Dispatch based on user choice
488
+ case "$CHOSEN_ACTION" in
489
+ done)
490
+ echo "Loop complete. Exiting."
491
+ ;;
492
+ merge_local)
493
+ echo "Merging back to main locally..."
494
+ git checkout main 2>/dev/null || git checkout master
495
+ git merge --squash "$BRANCH" && git commit -m "feat($FEATURE): squash merge from $BRANCH"
496
+ echo "Merged. You can delete the branch with: git branch -D $BRANCH"
497
+ ;;
498
+ discard)
499
+ echo "Discarding work on branch $BRANCH..."
500
+ git checkout main 2>/dev/null || git checkout master
501
+ git branch -D "$BRANCH" 2>/dev/null || echo "Branch $BRANCH not found locally."
502
+ ;;
503
+ keep_branch|*)
504
+ echo "Keeping branch $BRANCH as-is."
505
+ ;;
506
+ esac
507
+
358
508
  # Persist final status for TUI summaries
359
509
  if ! echo "$ITERATION|$MAX_ITERATIONS|$(date +%s)|done" > "$FINAL_STATUS_FILE"; then
360
510
  echo "WARNING: Failed to write final status file: $FINAL_STATUS_FILE" >&2