prizmkit 1.0.0 → 1.0.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 (89) hide show
  1. package/bundled/VERSION.json +5 -0
  2. package/bundled/adapters/claude/agent-adapter.js +108 -0
  3. package/bundled/adapters/claude/command-adapter.js +104 -0
  4. package/bundled/adapters/claude/paths.js +35 -0
  5. package/bundled/adapters/claude/rules-adapter.js +77 -0
  6. package/bundled/adapters/claude/settings-adapter.js +73 -0
  7. package/bundled/adapters/claude/team-adapter.js +183 -0
  8. package/bundled/adapters/codebuddy/agent-adapter.js +43 -0
  9. package/bundled/adapters/codebuddy/paths.js +29 -0
  10. package/bundled/adapters/codebuddy/settings-adapter.js +47 -0
  11. package/bundled/adapters/codebuddy/skill-adapter.js +68 -0
  12. package/bundled/adapters/codebuddy/team-adapter.js +46 -0
  13. package/bundled/adapters/shared/frontmatter.js +77 -0
  14. package/bundled/agents/prizm-dev-team-coordinator.md +142 -0
  15. package/bundled/agents/prizm-dev-team-dev.md +99 -0
  16. package/bundled/agents/prizm-dev-team-pm.md +114 -0
  17. package/bundled/agents/prizm-dev-team-reviewer.md +119 -0
  18. package/bundled/dev-pipeline/README.md +482 -0
  19. package/bundled/dev-pipeline/assets/feature-list-example.json +147 -0
  20. package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +138 -0
  21. package/bundled/dev-pipeline/launch-bugfix-daemon.sh +425 -0
  22. package/bundled/dev-pipeline/launch-daemon.sh +549 -0
  23. package/bundled/dev-pipeline/reset-feature.sh +209 -0
  24. package/bundled/dev-pipeline/retry-bug.sh +344 -0
  25. package/bundled/dev-pipeline/retry-feature.sh +338 -0
  26. package/bundled/dev-pipeline/run-bugfix.sh +638 -0
  27. package/bundled/dev-pipeline/run.sh +845 -0
  28. package/bundled/dev-pipeline/scripts/check-session-status.py +158 -0
  29. package/bundled/dev-pipeline/scripts/detect-stuck.py +385 -0
  30. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +598 -0
  31. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +402 -0
  32. package/bundled/dev-pipeline/scripts/init-bugfix-pipeline.py +294 -0
  33. package/bundled/dev-pipeline/scripts/init-dev-team.py +134 -0
  34. package/bundled/dev-pipeline/scripts/init-pipeline.py +335 -0
  35. package/bundled/dev-pipeline/scripts/update-bug-status.py +748 -0
  36. package/bundled/dev-pipeline/scripts/update-feature-status.py +1076 -0
  37. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +262 -0
  38. package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +159 -0
  39. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +291 -0
  40. package/bundled/dev-pipeline/templates/feature-list-schema.json +112 -0
  41. package/bundled/dev-pipeline/templates/session-status-schema.json +77 -0
  42. package/bundled/skills/_metadata.json +267 -0
  43. package/bundled/skills/app-planner/SKILL.md +580 -0
  44. package/bundled/skills/app-planner/assets/planning-guide.md +313 -0
  45. package/bundled/skills/app-planner/scripts/validate-and-generate.py +758 -0
  46. package/bundled/skills/bug-planner/SKILL.md +235 -0
  47. package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +252 -0
  48. package/bundled/skills/dev-pipeline-launcher/SKILL.md +223 -0
  49. package/bundled/skills/prizm-kit/SKILL.md +151 -0
  50. package/bundled/skills/prizm-kit/assets/claude-md-template.md +38 -0
  51. package/bundled/skills/prizm-kit/assets/codebuddy-md-template.md +35 -0
  52. package/bundled/skills/prizm-kit/assets/hooks/prizm-commit-hook.json +15 -0
  53. package/bundled/skills/prizmkit-adr-manager/SKILL.md +68 -0
  54. package/bundled/skills/prizmkit-adr-manager/assets/adr-template.md +26 -0
  55. package/bundled/skills/prizmkit-analyze/SKILL.md +194 -0
  56. package/bundled/skills/prizmkit-api-doc-generator/SKILL.md +56 -0
  57. package/bundled/skills/prizmkit-bug-fix-workflow/SKILL.md +351 -0
  58. package/bundled/skills/prizmkit-bug-reproducer/SKILL.md +62 -0
  59. package/bundled/skills/prizmkit-ci-cd-generator/SKILL.md +54 -0
  60. package/bundled/skills/prizmkit-clarify/SKILL.md +52 -0
  61. package/bundled/skills/prizmkit-code-review/SKILL.md +70 -0
  62. package/bundled/skills/prizmkit-committer/SKILL.md +117 -0
  63. package/bundled/skills/prizmkit-db-migration/SKILL.md +65 -0
  64. package/bundled/skills/prizmkit-dependency-health/SKILL.md +123 -0
  65. package/bundled/skills/prizmkit-deployment-strategy/SKILL.md +58 -0
  66. package/bundled/skills/prizmkit-error-triage/SKILL.md +55 -0
  67. package/bundled/skills/prizmkit-implement/SKILL.md +47 -0
  68. package/bundled/skills/prizmkit-init/SKILL.md +156 -0
  69. package/bundled/skills/prizmkit-log-analyzer/SKILL.md +55 -0
  70. package/bundled/skills/prizmkit-monitoring-setup/SKILL.md +75 -0
  71. package/bundled/skills/prizmkit-onboarding-generator/SKILL.md +70 -0
  72. package/bundled/skills/prizmkit-perf-profiler/SKILL.md +55 -0
  73. package/bundled/skills/prizmkit-plan/SKILL.md +54 -0
  74. package/bundled/skills/prizmkit-plan/assets/plan-template.md +37 -0
  75. package/bundled/skills/prizmkit-prizm-docs/SKILL.md +140 -0
  76. package/bundled/skills/prizmkit-prizm-docs/assets/PRIZM-SPEC.md +943 -0
  77. package/bundled/skills/prizmkit-retrospective/SKILL.md +79 -0
  78. package/bundled/skills/prizmkit-security-audit/SKILL.md +130 -0
  79. package/bundled/skills/prizmkit-specify/SKILL.md +52 -0
  80. package/bundled/skills/prizmkit-specify/assets/spec-template.md +37 -0
  81. package/bundled/skills/prizmkit-summarize/SKILL.md +51 -0
  82. package/bundled/skills/prizmkit-summarize/assets/registry-template.md +18 -0
  83. package/bundled/skills/prizmkit-tasks/SKILL.md +50 -0
  84. package/bundled/skills/prizmkit-tasks/assets/tasks-template.md +21 -0
  85. package/bundled/skills/prizmkit-tech-debt-tracker/SKILL.md +139 -0
  86. package/bundled/team/prizm-dev-team.json +47 -0
  87. package/bundled/templates/claude-md-template.md +38 -0
  88. package/bundled/templates/codebuddy-md-template.md +35 -0
  89. package/package.json +2 -1
@@ -0,0 +1,638 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # ============================================================
5
+ # dev-pipeline/run-bugfix.sh - Autonomous Bug Fix Pipeline Runner
6
+ #
7
+ # Drives the prizm-dev-team through iterative AI CLI sessions to
8
+ # fix bugs from a bug-fix-list.json specification.
9
+ #
10
+ # Usage:
11
+ # ./run-bugfix.sh run [bug-fix-list.json] Run all bugs
12
+ # ./run-bugfix.sh run <bug-id> [options] Run a single bug
13
+ # ./run-bugfix.sh status [bug-fix-list.json] Show pipeline status
14
+ # ./run-bugfix.sh reset Clear all state
15
+ #
16
+ # Environment Variables:
17
+ # MAX_RETRIES Max retries per bug (default: 3)
18
+ # SESSION_TIMEOUT Session timeout in seconds (default: 0 = no limit)
19
+ # AI_CLI AI CLI command name (auto-detected: cbc or claude)
20
+ # VERBOSE Set to 1 to enable --verbose on AI CLI
21
+ # HEARTBEAT_INTERVAL Heartbeat log interval in seconds (default: 30)
22
+ # HEARTBEAT_STALE_THRESHOLD Heartbeat stale threshold in seconds (default: 600)
23
+ # ============================================================
24
+
25
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
26
+ STATE_DIR="$SCRIPT_DIR/bugfix-state"
27
+ SCRIPTS_DIR="$SCRIPT_DIR/scripts"
28
+
29
+ # Configuration
30
+ MAX_RETRIES=${MAX_RETRIES:-3}
31
+ SESSION_TIMEOUT=${SESSION_TIMEOUT:-0}
32
+ HEARTBEAT_STALE_THRESHOLD=${HEARTBEAT_STALE_THRESHOLD:-600}
33
+ HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-30}
34
+ VERBOSE=${VERBOSE:-0}
35
+
36
+ # AI CLI detection
37
+ if [[ -n "${AI_CLI:-}" ]]; then
38
+ CLI_CMD="$AI_CLI"
39
+ elif [[ -n "${CODEBUDDY_CLI:-}" ]]; then
40
+ CLI_CMD="$CODEBUDDY_CLI"
41
+ elif command -v cbc &>/dev/null; then
42
+ CLI_CMD="cbc"
43
+ elif command -v claude &>/dev/null; then
44
+ CLI_CMD="claude"
45
+ else
46
+ echo "ERROR: No AI CLI found. Install CodeBuddy (cbc) or Claude Code (claude)." >&2
47
+ exit 1
48
+ fi
49
+
50
+ # Platform detection
51
+ if [[ -n "${PRIZMKIT_PLATFORM:-}" ]]; then
52
+ PLATFORM="$PRIZMKIT_PLATFORM"
53
+ elif [[ "$CLI_CMD" == *"claude"* ]]; then
54
+ PLATFORM="claude"
55
+ else
56
+ PLATFORM="codebuddy"
57
+ fi
58
+ export PRIZMKIT_PLATFORM="$PLATFORM"
59
+
60
+ # Bug list path (set in main, used by cleanup trap)
61
+ BUG_LIST=""
62
+
63
+ # Colors
64
+ RED='\033[0;31m'
65
+ GREEN='\033[0;32m'
66
+ YELLOW='\033[1;33m'
67
+ BLUE='\033[0;34m'
68
+ MAGENTA='\033[0;35m'
69
+ BOLD='\033[1m'
70
+ NC='\033[0m'
71
+
72
+ log_info() { echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
73
+ log_warn() { echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
74
+ log_error() { echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
75
+ log_success() { echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
76
+
77
+ # ============================================================
78
+ # Shared: Spawn AI CLI session and wait for result
79
+ # ============================================================
80
+
81
+ spawn_and_wait_session() {
82
+ local bug_id="$1"
83
+ local bug_list="$2"
84
+ local session_id="$3"
85
+ local bootstrap_prompt="$4"
86
+ local session_dir="$5"
87
+ local max_retries="$6"
88
+
89
+ local session_log="$session_dir/logs/session.log"
90
+
91
+ local verbose_flag=""
92
+ if [[ "$VERBOSE" == "1" ]]; then
93
+ verbose_flag="--verbose"
94
+ fi
95
+
96
+ case "$CLI_CMD" in
97
+ *claude*)
98
+ "$CLI_CMD" \
99
+ --print \
100
+ -p "$(cat "$bootstrap_prompt")" \
101
+ --yes \
102
+ $verbose_flag \
103
+ > "$session_log" 2>&1 &
104
+ ;;
105
+ *)
106
+ "$CLI_CMD" \
107
+ --print \
108
+ -y \
109
+ $verbose_flag \
110
+ < "$bootstrap_prompt" \
111
+ > "$session_log" 2>&1 &
112
+ ;;
113
+ esac
114
+ local cli_pid=$!
115
+
116
+ # Timeout watchdog
117
+ local watcher_pid=""
118
+ if [[ $SESSION_TIMEOUT -gt 0 ]]; then
119
+ ( sleep "$SESSION_TIMEOUT" && kill -TERM "$cli_pid" 2>/dev/null ) &
120
+ watcher_pid=$!
121
+ fi
122
+
123
+ # Heartbeat monitor
124
+ local heartbeat_interval=$HEARTBEAT_INTERVAL
125
+ (
126
+ local elapsed=0
127
+ local prev_size=0
128
+ while kill -0 "$cli_pid" 2>/dev/null; do
129
+ sleep "$heartbeat_interval"
130
+ elapsed=$((elapsed + heartbeat_interval))
131
+ kill -0 "$cli_pid" 2>/dev/null || break
132
+
133
+ local cur_size=0
134
+ if [[ -f "$session_log" ]]; then
135
+ cur_size=$(wc -c < "$session_log" 2>/dev/null || echo 0)
136
+ cur_size=$(echo "$cur_size" | tr -d ' ')
137
+ fi
138
+
139
+ local growth=$((cur_size - prev_size))
140
+ prev_size=$cur_size
141
+
142
+ local size_display
143
+ if [[ $cur_size -gt 1048576 ]]; then
144
+ size_display="$((cur_size / 1048576))MB"
145
+ elif [[ $cur_size -gt 1024 ]]; then
146
+ size_display="$((cur_size / 1024))KB"
147
+ else
148
+ size_display="${cur_size}B"
149
+ fi
150
+
151
+ local mins=$((elapsed / 60))
152
+ local secs=$((elapsed % 60))
153
+
154
+ local last_activity=""
155
+ if [[ -f "$session_log" ]]; then
156
+ last_activity=$(tail -20 "$session_log" 2>/dev/null | grep -v '^$' | tail -1 | cut -c1-80 || echo "")
157
+ fi
158
+
159
+ local status_icon
160
+ if [[ $growth -gt 0 ]]; then
161
+ status_icon="${GREEN}▶${NC}"
162
+ else
163
+ status_icon="${YELLOW}⏸${NC}"
164
+ fi
165
+
166
+ echo -e " ${status_icon} ${BLUE}[HEARTBEAT]${NC} ${mins}m${secs}s elapsed | log: ${size_display} (+${growth}B) | ${last_activity}"
167
+ done
168
+ ) &
169
+ local heartbeat_pid=$!
170
+
171
+ # Wait for AI CLI to finish
172
+ local exit_code=0
173
+ if wait "$cli_pid" 2>/dev/null; then
174
+ exit_code=0
175
+ else
176
+ exit_code=$?
177
+ fi
178
+
179
+ # Cleanup
180
+ [[ -n "$watcher_pid" ]] && kill "$watcher_pid" 2>/dev/null || true
181
+ kill "$heartbeat_pid" 2>/dev/null || true
182
+ [[ -n "$watcher_pid" ]] && wait "$watcher_pid" 2>/dev/null || true
183
+ wait "$heartbeat_pid" 2>/dev/null || true
184
+
185
+ [[ $exit_code -eq 143 ]] && exit_code=124
186
+
187
+ # Session summary
188
+ if [[ -f "$session_log" ]]; then
189
+ local final_size=$(wc -c < "$session_log" 2>/dev/null | tr -d ' ')
190
+ local final_lines=$(wc -l < "$session_log" 2>/dev/null | tr -d ' ')
191
+ log_info "Session log: $final_lines lines, $((final_size / 1024))KB"
192
+ fi
193
+
194
+ # Check session outcome
195
+ local session_status_file="$session_dir/session-status.json"
196
+ local session_status
197
+
198
+ if [[ $exit_code -eq 124 ]]; then
199
+ log_warn "Session timed out after ${SESSION_TIMEOUT}s"
200
+ session_status="timed_out"
201
+ elif [[ -f "$session_status_file" ]]; then
202
+ session_status=$(python3 "$SCRIPTS_DIR/check-session-status.py" \
203
+ --status-file "$session_status_file" 2>/dev/null) || session_status="crashed"
204
+ else
205
+ log_warn "Session ended without status file — treating as crashed"
206
+ session_status="crashed"
207
+ fi
208
+
209
+ log_info "Session result: $session_status"
210
+
211
+ # Update bug status
212
+ python3 "$SCRIPTS_DIR/update-bug-status.py" \
213
+ --bug-list "$bug_list" \
214
+ --state-dir "$STATE_DIR" \
215
+ --bug-id "$bug_id" \
216
+ --session-status "$session_status" \
217
+ --session-id "$session_id" \
218
+ --max-retries "$max_retries" \
219
+ --action update >/dev/null 2>&1 || true
220
+
221
+ _SPAWN_RESULT="$session_status"
222
+ }
223
+
224
+ # ============================================================
225
+ # Graceful Shutdown
226
+ # ============================================================
227
+
228
+ cleanup() {
229
+ echo ""
230
+ log_warn "Received interrupt signal. Saving state..."
231
+
232
+ if [[ -n "$BUG_LIST" && -f "$BUG_LIST" ]]; then
233
+ python3 "$SCRIPTS_DIR/update-bug-status.py" \
234
+ --bug-list "$BUG_LIST" \
235
+ --state-dir "$STATE_DIR" \
236
+ --action pause 2>/dev/null || true
237
+ fi
238
+
239
+ log_info "Bug fix pipeline paused. Run './run-bugfix.sh run' to resume."
240
+ exit 130
241
+ }
242
+ trap cleanup SIGINT SIGTERM
243
+
244
+ # ============================================================
245
+ # Dependency Check
246
+ # ============================================================
247
+
248
+ check_dependencies() {
249
+ if ! command -v jq &>/dev/null; then
250
+ log_error "jq is required but not installed. Install with: brew install jq"
251
+ exit 1
252
+ fi
253
+ if ! command -v python3 &>/dev/null; then
254
+ log_error "python3 is required but not installed."
255
+ exit 1
256
+ fi
257
+ }
258
+
259
+ # ============================================================
260
+ # run-one: Run a single bug fix
261
+ # ============================================================
262
+
263
+ run_one() {
264
+ local bug_id=""
265
+ local bug_list=""
266
+ local dry_run=false
267
+
268
+ while [[ $# -gt 0 ]]; do
269
+ case "$1" in
270
+ --dry-run) dry_run=true; shift ;;
271
+ --timeout) shift; SESSION_TIMEOUT="${1:-0}"; shift ;;
272
+ B-*|b-*) bug_id="$1"; shift ;;
273
+ *) bug_list="$1"; shift ;;
274
+ esac
275
+ done
276
+
277
+ if [[ -z "$bug_id" ]]; then
278
+ log_error "Bug ID is required (e.g. B-001)"
279
+ echo ""
280
+ show_help
281
+ exit 1
282
+ fi
283
+
284
+ if [[ -z "$bug_list" ]]; then
285
+ bug_list="bug-fix-list.json"
286
+ fi
287
+ if [[ ! "$bug_list" = /* ]]; then
288
+ bug_list="$(pwd)/$bug_list"
289
+ fi
290
+ BUG_LIST="$bug_list"
291
+
292
+ if [[ ! -f "$bug_list" ]]; then
293
+ log_error "Bug fix list not found: $bug_list"
294
+ exit 1
295
+ fi
296
+
297
+ check_dependencies
298
+
299
+ # Initialize state if needed
300
+ if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
301
+ log_info "Initializing bugfix pipeline state..."
302
+ python3 "$SCRIPTS_DIR/init-bugfix-pipeline.py" \
303
+ --bug-list "$bug_list" \
304
+ --state-dir "$STATE_DIR" >/dev/null 2>&1 || {
305
+ log_error "Failed to initialize bugfix pipeline state"
306
+ exit 1
307
+ }
308
+ fi
309
+
310
+ # Verify bug exists
311
+ local bug_title
312
+ bug_title=$(python3 -c "
313
+ import json, sys
314
+ with open('$bug_list') as f:
315
+ data = json.load(f)
316
+ for bug in data.get('bugs', []):
317
+ if bug.get('id') == '$bug_id':
318
+ print(bug.get('title', ''))
319
+ sys.exit(0)
320
+ sys.exit(1)
321
+ " 2>/dev/null) || {
322
+ log_error "Bug $bug_id not found in $bug_list"
323
+ exit 1
324
+ }
325
+
326
+ local bug_severity
327
+ bug_severity=$(python3 -c "
328
+ import json, sys
329
+ with open('$bug_list') as f:
330
+ data = json.load(f)
331
+ for bug in data.get('bugs', []):
332
+ if bug.get('id') == '$bug_id':
333
+ print(bug.get('severity', 'medium'))
334
+ sys.exit(0)
335
+ sys.exit(1)
336
+ " 2>/dev/null) || bug_severity="medium"
337
+
338
+ # Reset bug status
339
+ python3 "$SCRIPTS_DIR/update-bug-status.py" \
340
+ --bug-list "$bug_list" \
341
+ --state-dir "$STATE_DIR" \
342
+ --bug-id "$bug_id" \
343
+ --action reset >/dev/null 2>&1 || true
344
+
345
+ # Generate bootstrap prompt
346
+ local run_id session_id session_dir bootstrap_prompt
347
+ run_id=$(jq -r '.run_id' "$STATE_DIR/pipeline.json")
348
+ session_id="${bug_id}-$(date +%Y%m%d%H%M%S)"
349
+ session_dir="$STATE_DIR/bugs/$bug_id/sessions/$session_id"
350
+ mkdir -p "$session_dir/logs"
351
+
352
+ bootstrap_prompt="$session_dir/bootstrap-prompt.md"
353
+
354
+ log_info "Generating bugfix bootstrap prompt..."
355
+ python3 "$SCRIPTS_DIR/generate-bugfix-prompt.py" \
356
+ --bug-list "$bug_list" \
357
+ --bug-id "$bug_id" \
358
+ --session-id "$session_id" \
359
+ --run-id "$run_id" \
360
+ --retry-count 0 \
361
+ --resume-phase "null" \
362
+ --state-dir "$STATE_DIR" \
363
+ --output "$bootstrap_prompt" >/dev/null 2>&1
364
+
365
+ if [[ "$dry_run" == true ]]; then
366
+ echo ""
367
+ echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
368
+ echo -e "${BOLD} Dry Run: $bug_id — $bug_title${NC}"
369
+ echo -e "${BOLD} Severity: $bug_severity${NC}"
370
+ echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
371
+ echo ""
372
+ log_info "Bootstrap prompt written to:"
373
+ echo " $bootstrap_prompt"
374
+ echo ""
375
+ log_success "Dry run complete. Inspect full prompt with:"
376
+ echo " cat $bootstrap_prompt"
377
+ return 0
378
+ fi
379
+
380
+ echo ""
381
+ echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
382
+ echo -e "${BOLD} Bug Fix: $bug_id — $bug_title${NC}"
383
+ echo -e "${BOLD} Severity: $bug_severity${NC}"
384
+ echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
385
+ log_info "Session ID: $session_id"
386
+ log_info "Prompt: $bootstrap_prompt"
387
+ log_info "Log: $session_dir/logs/session.log"
388
+ echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
389
+ echo ""
390
+
391
+ cleanup() {
392
+ echo ""
393
+ log_warn "Interrupted. Killing session..."
394
+ kill 0 2>/dev/null || true
395
+ log_info "Session log: $session_dir/logs/session.log"
396
+ exit 130
397
+ }
398
+ trap cleanup SIGINT SIGTERM
399
+
400
+ _SPAWN_RESULT=""
401
+ spawn_and_wait_session \
402
+ "$bug_id" "$bug_list" "$session_id" \
403
+ "$bootstrap_prompt" "$session_dir" 999
404
+ local session_status="$_SPAWN_RESULT"
405
+
406
+ echo ""
407
+ if [[ "$session_status" == "success" ]]; then
408
+ log_success "════════════════════════════════════════════════════"
409
+ log_success " $bug_id fixed successfully!"
410
+ log_success "════════════════════════════════════════════════════"
411
+ else
412
+ log_error "════════════════════════════════════════════════════"
413
+ log_error " $bug_id result: $session_status"
414
+ log_error " Review log: $session_dir/logs/session.log"
415
+ log_error "════════════════════════════════════════════════════"
416
+ fi
417
+ }
418
+
419
+ # ============================================================
420
+ # Main Loop: Run all bugs
421
+ # ============================================================
422
+
423
+ main() {
424
+ local bug_list="${1:-bug-fix-list.json}"
425
+
426
+ if [[ ! "$bug_list" = /* ]]; then
427
+ bug_list="$(pwd)/$bug_list"
428
+ fi
429
+ BUG_LIST="$bug_list"
430
+
431
+ if [[ ! -f "$bug_list" ]]; then
432
+ log_error "Bug fix list not found: $bug_list"
433
+ log_info "Create a bug fix list first using the bug-planner skill,"
434
+ log_info "or provide a path: ./run-bugfix.sh run <path-to-bug-fix-list.json>"
435
+ exit 1
436
+ fi
437
+
438
+ check_dependencies
439
+
440
+ # Initialize pipeline state if needed
441
+ if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
442
+ log_info "Initializing bugfix pipeline state..."
443
+ local init_result
444
+ init_result=$(python3 "$SCRIPTS_DIR/init-bugfix-pipeline.py" \
445
+ --bug-list "$bug_list" \
446
+ --state-dir "$STATE_DIR" 2>&1)
447
+
448
+ local init_valid
449
+ init_valid=$(echo "$init_result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('valid', False))" 2>/dev/null || echo "False")
450
+
451
+ if [[ "$init_valid" != "True" ]]; then
452
+ log_error "Bugfix pipeline initialization failed:"
453
+ echo "$init_result"
454
+ exit 1
455
+ fi
456
+
457
+ local bugs_count
458
+ bugs_count=$(echo "$init_result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('bugs_count', 0))" 2>/dev/null || echo "0")
459
+ log_success "Bugfix pipeline initialized with $bugs_count bugs"
460
+ else
461
+ log_info "Resuming existing bugfix pipeline..."
462
+ fi
463
+
464
+ # Print header
465
+ echo ""
466
+ echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
467
+ echo -e "${BOLD} Bug-Fix Pipeline Runner Started${NC}"
468
+ echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
469
+ log_info "Bug fix list: $bug_list"
470
+ log_info "Max retries per bug: $MAX_RETRIES"
471
+ if [[ $SESSION_TIMEOUT -gt 0 ]]; then
472
+ log_info "Session timeout: ${SESSION_TIMEOUT}s"
473
+ else
474
+ log_info "Session timeout: none"
475
+ fi
476
+ log_info "AI CLI: $CLI_CMD (platform: $PLATFORM)"
477
+ echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
478
+ echo ""
479
+
480
+ local session_count=0
481
+
482
+ while true; do
483
+ # Find next bug to process
484
+ local next_bug
485
+ next_bug=$(python3 "$SCRIPTS_DIR/update-bug-status.py" \
486
+ --bug-list "$bug_list" \
487
+ --state-dir "$STATE_DIR" \
488
+ --max-retries "$MAX_RETRIES" \
489
+ --action get_next 2>/dev/null) || true
490
+
491
+ if [[ "$next_bug" == "PIPELINE_COMPLETE" ]]; then
492
+ echo ""
493
+ log_success "════════════════════════════════════════════════════"
494
+ log_success " All bugs processed! Bug fix pipeline finished."
495
+ log_success " Total sessions: $session_count"
496
+ log_success "════════════════════════════════════════════════════"
497
+ break
498
+ fi
499
+
500
+ if [[ "$next_bug" == "PIPELINE_BLOCKED" ]]; then
501
+ log_warn "All remaining bugs are blocked (needs_info/failed)."
502
+ log_warn "Run './run-bugfix.sh status' to see details."
503
+ log_warn "Waiting 60s before re-checking... (Ctrl+C to stop)"
504
+ sleep 60
505
+ continue
506
+ fi
507
+
508
+ # Parse bug info
509
+ local bug_id bug_title bug_severity retry_count resume_phase
510
+ bug_id=$(echo "$next_bug" | jq -r '.bug_id')
511
+ bug_title=$(echo "$next_bug" | jq -r '.title')
512
+ bug_severity=$(echo "$next_bug" | jq -r '.severity')
513
+ retry_count=$(echo "$next_bug" | jq -r '.retry_count // 0')
514
+ resume_phase=$(echo "$next_bug" | jq -r '.resume_from_phase // "null"')
515
+
516
+ echo ""
517
+ echo -e "${BOLD}────────────────────────────────────────────────────${NC}"
518
+ log_info "Bug: ${BOLD}$bug_id${NC} — $bug_title"
519
+ log_info "Severity: $bug_severity | Retry: $retry_count / $MAX_RETRIES"
520
+ if [[ "$resume_phase" != "null" ]]; then
521
+ log_info "Resuming from Phase $resume_phase"
522
+ fi
523
+ echo -e "${BOLD}────────────────────────────────────────────────────${NC}"
524
+
525
+ # Generate session
526
+ local session_id run_id
527
+ run_id=$(jq -r '.run_id' "$STATE_DIR/pipeline.json")
528
+ session_id="${bug_id}-$(date +%Y%m%d%H%M%S)"
529
+
530
+ local session_dir="$STATE_DIR/bugs/$bug_id/sessions/$session_id"
531
+ mkdir -p "$session_dir/logs"
532
+
533
+ local bootstrap_prompt="$session_dir/bootstrap-prompt.md"
534
+ python3 "$SCRIPTS_DIR/generate-bugfix-prompt.py" \
535
+ --bug-list "$bug_list" \
536
+ --bug-id "$bug_id" \
537
+ --session-id "$session_id" \
538
+ --run-id "$run_id" \
539
+ --retry-count "$retry_count" \
540
+ --resume-phase "$resume_phase" \
541
+ --state-dir "$STATE_DIR" \
542
+ --output "$bootstrap_prompt" >/dev/null 2>&1
543
+
544
+ # Track current session
545
+ python3 -c "
546
+ import json
547
+ from datetime import datetime
548
+ data = {
549
+ 'bug_id': '$bug_id',
550
+ 'session_id': '$session_id',
551
+ 'started_at': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
552
+ }
553
+ with open('$STATE_DIR/current-session.json', 'w') as f:
554
+ json.dump(data, f, indent=2)
555
+ "
556
+
557
+ # Spawn session
558
+ log_info "Spawning AI CLI session: $session_id"
559
+ _SPAWN_RESULT=""
560
+ spawn_and_wait_session \
561
+ "$bug_id" "$bug_list" "$session_id" \
562
+ "$bootstrap_prompt" "$session_dir" "$MAX_RETRIES"
563
+
564
+ session_count=$((session_count + 1))
565
+
566
+ log_info "Pausing 5s before next bug..."
567
+ sleep 5
568
+ done
569
+ }
570
+
571
+ # ============================================================
572
+ # Entry Point
573
+ # ============================================================
574
+
575
+ show_help() {
576
+ echo "Usage: $0 <command> [options]"
577
+ echo ""
578
+ echo "Commands:"
579
+ echo " run [bug-fix-list.json] Run all bugs by severity/priority order"
580
+ echo " run <bug-id> [options] Run a single bug fix"
581
+ echo " status [bug-fix-list.json] Show bug fix pipeline status"
582
+ echo " reset Clear all bugfix state"
583
+ echo " help Show this help message"
584
+ echo ""
585
+ echo "Single Bug Options (run <bug-id>):"
586
+ echo " --dry-run Generate bootstrap prompt only, don't spawn session"
587
+ echo " --timeout N Session timeout in seconds (default: 0 = no limit)"
588
+ echo ""
589
+ echo "Environment Variables:"
590
+ echo " MAX_RETRIES Max retries per bug (default: 3)"
591
+ echo " SESSION_TIMEOUT Session timeout in seconds (default: 0 = no limit)"
592
+ echo " AI_CLI AI CLI command name (auto-detected: cbc or claude)"
593
+ echo " VERBOSE Set to 1 for verbose AI CLI output"
594
+ echo " HEARTBEAT_INTERVAL Heartbeat log interval in seconds (default: 30)"
595
+ echo ""
596
+ echo "Examples:"
597
+ echo " ./run-bugfix.sh run # Run all bugs"
598
+ echo " ./run-bugfix.sh run bug-fix-list.json # Custom bug list"
599
+ echo " ./run-bugfix.sh run B-001 --dry-run # Inspect generated prompt"
600
+ echo " ./run-bugfix.sh run B-001 --timeout 3600 # 1h timeout"
601
+ echo " ./run-bugfix.sh status # Show status"
602
+ echo " MAX_RETRIES=5 ./run-bugfix.sh run # Custom retries"
603
+ }
604
+
605
+ case "${1:-run}" in
606
+ run|resume)
607
+ shift || true
608
+ if [[ "${1:-}" =~ ^[Bb]-[0-9]+ ]]; then
609
+ run_one "$@"
610
+ else
611
+ main "${1:-bug-fix-list.json}"
612
+ fi
613
+ ;;
614
+ status)
615
+ check_dependencies
616
+ if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
617
+ log_error "No bugfix pipeline state found. Run './run-bugfix.sh run' first."
618
+ exit 1
619
+ fi
620
+ python3 "$SCRIPTS_DIR/update-bug-status.py" \
621
+ --bug-list "${2:-bug-fix-list.json}" \
622
+ --state-dir "$STATE_DIR" \
623
+ --action status
624
+ ;;
625
+ reset)
626
+ log_warn "Resetting bugfix pipeline state..."
627
+ rm -rf "$STATE_DIR"
628
+ log_success "Bugfix state cleared. Run './run-bugfix.sh run' to start fresh."
629
+ ;;
630
+ help|--help|-h)
631
+ show_help
632
+ ;;
633
+ *)
634
+ log_error "Unknown command: $1"
635
+ show_help
636
+ exit 1
637
+ ;;
638
+ esac