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,209 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # ============================================================
5
+ # dev-pipeline/reset-feature.sh - Reset a failed/stuck feature
6
+ #
7
+ # Clears all state and artifacts for a feature so it can be
8
+ # re-executed from scratch by the pipeline.
9
+ #
10
+ # Usage:
11
+ # ./reset-feature.sh <feature-id> [options] [feature-list.json]
12
+ #
13
+ # Options:
14
+ # --clean Also delete session history and .prizmkit/specs/{slug}/ artifacts
15
+ # --run After reset, immediately retry the feature (calls retry-feature.sh)
16
+ #
17
+ # Examples:
18
+ # ./reset-feature.sh F-007 # Reset status only
19
+ # ./reset-feature.sh F-007 --clean # Reset + delete artifacts
20
+ # ./reset-feature.sh F-007 --clean --run # Reset + delete + retry
21
+ # ./reset-feature.sh F-007 --clean my-features.json # Custom feature list
22
+ # ============================================================
23
+
24
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
25
+ STATE_DIR="$SCRIPT_DIR/state"
26
+ SCRIPTS_DIR="$SCRIPT_DIR/scripts"
27
+
28
+ # Colors
29
+ RED='\033[0;31m'
30
+ GREEN='\033[0;32m'
31
+ YELLOW='\033[1;33m'
32
+ BLUE='\033[0;34m'
33
+ BOLD='\033[1m'
34
+ NC='\033[0m'
35
+
36
+ log_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
37
+ log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
38
+ log_error() { echo -e "${RED}[ERROR]${NC} $*"; }
39
+ log_success() { echo -e "${GREEN}[OK]${NC} $*"; }
40
+
41
+ # ============================================================
42
+ # Parse args
43
+ # ============================================================
44
+
45
+ FEATURE_ID=""
46
+ FEATURE_LIST=""
47
+ DO_CLEAN=false
48
+ DO_RUN=false
49
+
50
+ for arg in "$@"; do
51
+ case "$arg" in
52
+ --clean) DO_CLEAN=true ;;
53
+ --run) DO_RUN=true ;;
54
+ -h|--help) ;;
55
+ F-*) FEATURE_ID="$arg" ;;
56
+ *) FEATURE_LIST="$arg" ;;
57
+ esac
58
+ done
59
+
60
+ if [[ -z "$FEATURE_ID" ]]; then
61
+ echo "Usage: $0 <feature-id> [--clean] [--run] [feature-list.json]"
62
+ echo ""
63
+ echo " feature-id Feature to reset (e.g. F-007)"
64
+ echo " --clean Delete session history and .prizmkit artifacts"
65
+ echo " --run Retry the feature immediately after reset"
66
+ echo " feature-list.json Path to feature list (default: feature-list.json)"
67
+ exit 1
68
+ fi
69
+
70
+ FEATURE_LIST="${FEATURE_LIST:-feature-list.json}"
71
+
72
+ # Resolve absolute path
73
+ if [[ ! "$FEATURE_LIST" = /* ]]; then
74
+ FEATURE_LIST="$(pwd)/$FEATURE_LIST"
75
+ fi
76
+
77
+ # ============================================================
78
+ # Validation
79
+ # ============================================================
80
+
81
+ if [[ ! -f "$FEATURE_LIST" ]]; then
82
+ log_error "Feature list not found: $FEATURE_LIST"
83
+ exit 1
84
+ fi
85
+
86
+ if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
87
+ log_error "No pipeline state found. Run './run.sh run' first to initialize."
88
+ exit 1
89
+ fi
90
+
91
+ # Get feature info from feature list
92
+ FEATURE_INFO=$(python3 -c "
93
+ import json, sys, re
94
+ with open('$FEATURE_LIST') as f:
95
+ data = json.load(f)
96
+ for feat in data.get('features', []):
97
+ if feat.get('id') == '$FEATURE_ID':
98
+ title = feat.get('title', '')
99
+ # Compute slug
100
+ numeric = '$FEATURE_ID'.replace('F-', '').replace('f-', '').zfill(3)
101
+ slug = title.lower()
102
+ slug = re.sub(r'[^a-z0-9\s-]', '', slug)
103
+ slug = re.sub(r'[\s]+', '-', slug.strip())
104
+ slug = re.sub(r'-+', '-', slug).strip('-')
105
+ slug = '{}-{}'.format(numeric, slug)
106
+ print(json.dumps({'title': title, 'slug': slug, 'status': feat.get('status', 'unknown')}))
107
+ sys.exit(0)
108
+ sys.exit(1)
109
+ " 2>/dev/null) || {
110
+ log_error "Feature $FEATURE_ID not found in $FEATURE_LIST"
111
+ exit 1
112
+ }
113
+
114
+ FEATURE_TITLE=$(echo "$FEATURE_INFO" | python3 -c "import sys,json; print(json.load(sys.stdin)['title'])")
115
+ FEATURE_SLUG=$(echo "$FEATURE_INFO" | python3 -c "import sys,json; print(json.load(sys.stdin)['slug'])")
116
+ FEATURE_STATUS=$(echo "$FEATURE_INFO" | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
117
+
118
+ # ============================================================
119
+ # Show current state
120
+ # ============================================================
121
+
122
+ echo ""
123
+ echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
124
+ echo -e "${BOLD} Reset: $FEATURE_ID — $FEATURE_TITLE${NC}"
125
+ echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
126
+
127
+ # Read current status.json
128
+ STATUS_FILE="$STATE_DIR/features/$FEATURE_ID/status.json"
129
+ if [[ -f "$STATUS_FILE" ]]; then
130
+ CURRENT_STATUS=$(python3 -c "import json; d=json.load(open('$STATUS_FILE')); print(d.get('status','?'))")
131
+ CURRENT_RETRY=$(python3 -c "import json; d=json.load(open('$STATUS_FILE')); print(d.get('retry_count',0))")
132
+ SESSION_COUNT=$(python3 -c "import json; d=json.load(open('$STATUS_FILE')); print(len(d.get('sessions',[])))")
133
+ log_info "Current status: $CURRENT_STATUS (retry $CURRENT_RETRY, $SESSION_COUNT sessions)"
134
+ else
135
+ log_info "No status file found (never executed)"
136
+ fi
137
+
138
+ # Count artifacts
139
+ SPECS_DIR="$(cd "$SCRIPT_DIR/.." 2>/dev/null && pwd)/.prizmkit/specs/$FEATURE_SLUG"
140
+ SPECS_COUNT=0
141
+ if [[ -d "$SPECS_DIR" ]]; then
142
+ SPECS_COUNT=$(find "$SPECS_DIR" -type f 2>/dev/null | wc -l | tr -d ' ')
143
+ log_info "PrizmKit artifacts: $SPECS_COUNT files in .prizmkit/specs/$FEATURE_SLUG/"
144
+ fi
145
+
146
+ SESSIONS_DIR="$STATE_DIR/features/$FEATURE_ID/sessions"
147
+ SESSIONS_COUNT=0
148
+ if [[ -d "$SESSIONS_DIR" ]]; then
149
+ SESSIONS_COUNT=$(find "$SESSIONS_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')
150
+ log_info "Session history: $SESSIONS_COUNT session(s)"
151
+ fi
152
+
153
+ echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
154
+
155
+ # ============================================================
156
+ # Execute reset
157
+ # ============================================================
158
+
159
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
160
+
161
+ if [[ "$DO_CLEAN" == true ]]; then
162
+ log_info "Cleaning $FEATURE_ID (reset + delete artifacts)..."
163
+ RESULT=$(python3 "$SCRIPTS_DIR/update-feature-status.py" \
164
+ --feature-list "$FEATURE_LIST" \
165
+ --state-dir "$STATE_DIR" \
166
+ --feature-id "$FEATURE_ID" \
167
+ --feature-slug "$FEATURE_SLUG" \
168
+ --project-root "$PROJECT_ROOT" \
169
+ --action clean 2>&1)
170
+ else
171
+ log_info "Resetting $FEATURE_ID status..."
172
+ RESULT=$(python3 "$SCRIPTS_DIR/update-feature-status.py" \
173
+ --feature-list "$FEATURE_LIST" \
174
+ --state-dir "$STATE_DIR" \
175
+ --feature-id "$FEATURE_ID" \
176
+ --action reset 2>&1)
177
+ fi
178
+
179
+ # Check for errors
180
+ if echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); sys.exit(0 if 'error' not in d else 1)" 2>/dev/null; then
181
+ : # no error
182
+ else
183
+ ERROR_MSG=$(echo "$RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('error','unknown'))" 2>/dev/null || echo "$RESULT")
184
+ log_error "Reset failed: $ERROR_MSG"
185
+ exit 1
186
+ fi
187
+
188
+ # ============================================================
189
+ # Summary
190
+ # ============================================================
191
+
192
+ echo ""
193
+ if [[ "$DO_CLEAN" == true ]]; then
194
+ log_success "$FEATURE_ID cleaned: status → pending, $SESSIONS_COUNT session(s) deleted, $SPECS_COUNT artifact(s) deleted"
195
+ else
196
+ log_success "$FEATURE_ID reset: status → pending, retry count → 0"
197
+ fi
198
+
199
+ echo ""
200
+ echo -e "${BOLD}Next steps:${NC}"
201
+ if [[ "$DO_RUN" == true ]]; then
202
+ log_info "Auto-retrying $FEATURE_ID..."
203
+ echo ""
204
+ exec "$SCRIPT_DIR/retry-feature.sh" "$FEATURE_ID" "$FEATURE_LIST"
205
+ else
206
+ log_info " ./dev-pipeline/retry-feature.sh $FEATURE_ID # Retry once"
207
+ log_info " ./dev-pipeline/run.sh run feature-list.json # Resume pipeline"
208
+ fi
209
+ echo ""
@@ -0,0 +1,344 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # ============================================================
5
+ # dev-pipeline/retry-bug.sh - Retry a single failed bug fix
6
+ #
7
+ # Runs exactly ONE AI CLI session for the specified bug, then exits.
8
+ # Use this to manually retry a failed bug without restarting
9
+ # the full bugfix pipeline.
10
+ #
11
+ # Usage:
12
+ # ./retry-bug.sh <bug-id> [bug-fix-list.json]
13
+ #
14
+ # Examples:
15
+ # ./retry-bug.sh B-001
16
+ # ./retry-bug.sh B-001 bug-fix-list.json
17
+ # SESSION_TIMEOUT=3600 ./retry-bug.sh B-001
18
+ # ============================================================
19
+
20
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
21
+ STATE_DIR="$SCRIPT_DIR/bugfix-state"
22
+ SCRIPTS_DIR="$SCRIPT_DIR/scripts"
23
+
24
+ SESSION_TIMEOUT=${SESSION_TIMEOUT:-0}
25
+ HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-30}
26
+
27
+ # AI CLI detection
28
+ if [[ -n "${AI_CLI:-}" ]]; then
29
+ CLI_CMD="$AI_CLI"
30
+ elif [[ -n "${CODEBUDDY_CLI:-}" ]]; then
31
+ CLI_CMD="$CODEBUDDY_CLI"
32
+ elif command -v cbc &>/dev/null; then
33
+ CLI_CMD="cbc"
34
+ elif command -v claude &>/dev/null; then
35
+ CLI_CMD="claude"
36
+ else
37
+ echo "ERROR: No AI CLI found. Install CodeBuddy (cbc) or Claude Code (claude)." >&2
38
+ exit 1
39
+ fi
40
+
41
+ # Platform detection
42
+ if [[ -n "${PRIZMKIT_PLATFORM:-}" ]]; then
43
+ PLATFORM="$PRIZMKIT_PLATFORM"
44
+ elif [[ "$CLI_CMD" == *"claude"* ]]; then
45
+ PLATFORM="claude"
46
+ else
47
+ PLATFORM="codebuddy"
48
+ fi
49
+ export PRIZMKIT_PLATFORM="$PLATFORM"
50
+
51
+ # Colors
52
+ RED='\033[0;31m'
53
+ GREEN='\033[0;32m'
54
+ YELLOW='\033[1;33m'
55
+ BLUE='\033[0;34m'
56
+ BOLD='\033[1m'
57
+ NC='\033[0m'
58
+
59
+ log_info() { echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
60
+ log_warn() { echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
61
+ log_error() { echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
62
+ log_success() { echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
63
+
64
+ # ============================================================
65
+ # Args
66
+ # ============================================================
67
+
68
+ if [[ $# -lt 1 ]]; then
69
+ echo "Usage: $0 <bug-id> [bug-fix-list.json]"
70
+ echo ""
71
+ echo " bug-id Bug to retry (e.g. B-001)"
72
+ echo " bug-fix-list.json Path to bug fix list (default: bug-fix-list.json)"
73
+ echo ""
74
+ echo "Environment Variables:"
75
+ echo " SESSION_TIMEOUT Timeout in seconds (default: 0 = no limit)"
76
+ echo " HEARTBEAT_INTERVAL Heartbeat interval in seconds (default: 30)"
77
+ exit 1
78
+ fi
79
+
80
+ BUG_ID="$1"
81
+ BUG_LIST="${2:-bug-fix-list.json}"
82
+
83
+ if [[ ! "$BUG_LIST" = /* ]]; then
84
+ BUG_LIST="$(pwd)/$BUG_LIST"
85
+ fi
86
+
87
+ # ============================================================
88
+ # Validation
89
+ # ============================================================
90
+
91
+ if [[ ! -f "$BUG_LIST" ]]; then
92
+ log_error "Bug fix list not found: $BUG_LIST"
93
+ exit 1
94
+ fi
95
+
96
+ if ! command -v jq &>/dev/null; then
97
+ log_error "jq is required. Install with: brew install jq"
98
+ exit 1
99
+ fi
100
+
101
+ # Initialize state if needed
102
+ if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
103
+ log_info "Initializing bugfix pipeline state..."
104
+ python3 "$SCRIPTS_DIR/init-bugfix-pipeline.py" \
105
+ --bug-list "$BUG_LIST" \
106
+ --state-dir "$STATE_DIR" >/dev/null 2>&1 || {
107
+ log_error "Failed to initialize bugfix pipeline state"
108
+ exit 1
109
+ }
110
+ fi
111
+
112
+ # Verify bug exists
113
+ BUG_TITLE=$(python3 -c "
114
+ import json, sys
115
+ with open('$BUG_LIST') as f:
116
+ data = json.load(f)
117
+ for bug in data.get('bugs', []):
118
+ if bug.get('id') == '$BUG_ID':
119
+ print(bug.get('title', ''))
120
+ sys.exit(0)
121
+ sys.exit(1)
122
+ " 2>/dev/null) || {
123
+ log_error "Bug $BUG_ID not found in $BUG_LIST"
124
+ exit 1
125
+ }
126
+
127
+ BUG_SEVERITY=$(python3 -c "
128
+ import json, sys
129
+ with open('$BUG_LIST') as f:
130
+ data = json.load(f)
131
+ for bug in data.get('bugs', []):
132
+ if bug.get('id') == '$BUG_ID':
133
+ print(bug.get('severity', 'medium'))
134
+ sys.exit(0)
135
+ sys.exit(1)
136
+ " 2>/dev/null) || BUG_SEVERITY="medium"
137
+
138
+ # ============================================================
139
+ # Clean bug artifacts + reset status for a full restart
140
+ # ============================================================
141
+
142
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
143
+
144
+ log_info "Cleaning $BUG_ID artifacts for full restart..."
145
+ python3 "$SCRIPTS_DIR/update-bug-status.py" \
146
+ --bug-list "$BUG_LIST" \
147
+ --state-dir "$STATE_DIR" \
148
+ --bug-id "$BUG_ID" \
149
+ --project-root "$PROJECT_ROOT" \
150
+ --action clean >/dev/null 2>&1 || {
151
+ log_warn "Failed to clean bug artifacts (continuing with fresh session only)"
152
+ }
153
+
154
+ # ============================================================
155
+ # Generate bootstrap prompt
156
+ # ============================================================
157
+
158
+ RUN_ID=$(jq -r '.run_id' "$STATE_DIR/pipeline.json")
159
+ SESSION_ID="${BUG_ID}-$(date +%Y%m%d%H%M%S)"
160
+ SESSION_DIR="$STATE_DIR/bugs/$BUG_ID/sessions/$SESSION_ID"
161
+ mkdir -p "$SESSION_DIR/logs"
162
+
163
+ BOOTSTRAP_PROMPT="$SESSION_DIR/bootstrap-prompt.md"
164
+
165
+ log_info "Generating bugfix bootstrap prompt..."
166
+ python3 "$SCRIPTS_DIR/generate-bugfix-prompt.py" \
167
+ --bug-list "$BUG_LIST" \
168
+ --bug-id "$BUG_ID" \
169
+ --session-id "$SESSION_ID" \
170
+ --run-id "$RUN_ID" \
171
+ --retry-count 0 \
172
+ --resume-phase "null" \
173
+ --state-dir "$STATE_DIR" \
174
+ --output "$BOOTSTRAP_PROMPT" >/dev/null 2>&1
175
+
176
+ # ============================================================
177
+ # Run single AI CLI session
178
+ # ============================================================
179
+
180
+ echo ""
181
+ echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
182
+ echo -e "${BOLD} Retry Bug Fix: $BUG_ID — $BUG_TITLE${NC}"
183
+ echo -e "${BOLD} Severity: $BUG_SEVERITY${NC}"
184
+ echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
185
+ log_info "CLI: $CLI_CMD (platform: $PLATFORM)"
186
+ if [[ $SESSION_TIMEOUT -gt 0 ]]; then
187
+ log_info "Session timeout: ${SESSION_TIMEOUT}s"
188
+ else
189
+ log_info "Session timeout: none"
190
+ fi
191
+ log_info "Prompt: $BOOTSTRAP_PROMPT"
192
+ log_info "Log: $SESSION_DIR/logs/session.log"
193
+ echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
194
+ echo ""
195
+
196
+ SESSION_LOG="$SESSION_DIR/logs/session.log"
197
+
198
+ # Spawn AI CLI session
199
+ case "$CLI_CMD" in
200
+ *claude*)
201
+ "$CLI_CMD" \
202
+ --print \
203
+ -p "$(cat "$BOOTSTRAP_PROMPT")" \
204
+ --yes \
205
+ > "$SESSION_LOG" 2>&1 &
206
+ ;;
207
+ *)
208
+ "$CLI_CMD" \
209
+ --print \
210
+ -y \
211
+ < "$BOOTSTRAP_PROMPT" \
212
+ > "$SESSION_LOG" 2>&1 &
213
+ ;;
214
+ esac
215
+ CLI_PID=$!
216
+
217
+ # Timeout watchdog
218
+ WATCHER_PID=""
219
+ if [[ $SESSION_TIMEOUT -gt 0 ]]; then
220
+ ( sleep "$SESSION_TIMEOUT" && kill -TERM "$CLI_PID" 2>/dev/null ) &
221
+ WATCHER_PID=$!
222
+ fi
223
+
224
+ # Heartbeat
225
+ (
226
+ elapsed=0
227
+ prev_size=0
228
+ while kill -0 "$CLI_PID" 2>/dev/null; do
229
+ sleep "$HEARTBEAT_INTERVAL"
230
+ elapsed=$((elapsed + HEARTBEAT_INTERVAL))
231
+ kill -0 "$CLI_PID" 2>/dev/null || break
232
+
233
+ cur_size=0
234
+ if [[ -f "$SESSION_LOG" ]]; then
235
+ cur_size=$(wc -c < "$SESSION_LOG" 2>/dev/null || echo 0)
236
+ cur_size=$(echo "$cur_size" | tr -d ' ')
237
+ fi
238
+
239
+ growth=$((cur_size - prev_size))
240
+ prev_size=$cur_size
241
+
242
+ if [[ $cur_size -gt 1048576 ]]; then
243
+ size_display="$((cur_size / 1048576))MB"
244
+ elif [[ $cur_size -gt 1024 ]]; then
245
+ size_display="$((cur_size / 1024))KB"
246
+ else
247
+ size_display="${cur_size}B"
248
+ fi
249
+
250
+ mins=$((elapsed / 60))
251
+ secs=$((elapsed % 60))
252
+
253
+ last_activity=""
254
+ if [[ -f "$SESSION_LOG" ]]; then
255
+ last_activity=$(tail -20 "$SESSION_LOG" 2>/dev/null | grep -v '^$' | tail -1 | cut -c1-80 || echo "")
256
+ fi
257
+
258
+ if [[ $growth -gt 0 ]]; then
259
+ icon="${GREEN}▶${NC}"
260
+ else
261
+ icon="${YELLOW}⏸${NC}"
262
+ fi
263
+
264
+ echo -e " ${icon} ${BLUE}[HEARTBEAT]${NC} ${mins}m${secs}s | log: ${size_display} (+${growth}B) | ${last_activity}"
265
+ done
266
+ ) &
267
+ HEARTBEAT_PID=$!
268
+
269
+ # Ctrl+C cleanup
270
+ cleanup() {
271
+ echo ""
272
+ log_warn "Interrupted. Killing session..."
273
+ kill "$CLI_PID" 2>/dev/null || true
274
+ [[ -n "$WATCHER_PID" ]] && kill "$WATCHER_PID" 2>/dev/null || true
275
+ kill "$HEARTBEAT_PID" 2>/dev/null || true
276
+ wait "$CLI_PID" 2>/dev/null || true
277
+ [[ -n "$WATCHER_PID" ]] && wait "$WATCHER_PID" 2>/dev/null || true
278
+ wait "$HEARTBEAT_PID" 2>/dev/null || true
279
+ log_info "Session log: $SESSION_LOG"
280
+ exit 130
281
+ }
282
+ trap cleanup SIGINT SIGTERM
283
+
284
+ # Wait
285
+ EXIT_CODE=0
286
+ if wait "$CLI_PID" 2>/dev/null; then
287
+ EXIT_CODE=0
288
+ else
289
+ EXIT_CODE=$?
290
+ fi
291
+
292
+ # Cleanup background processes
293
+ [[ -n "$WATCHER_PID" ]] && kill "$WATCHER_PID" 2>/dev/null || true
294
+ kill "$HEARTBEAT_PID" 2>/dev/null || true
295
+ [[ -n "$WATCHER_PID" ]] && wait "$WATCHER_PID" 2>/dev/null || true
296
+ wait "$HEARTBEAT_PID" 2>/dev/null || true
297
+
298
+ [[ $EXIT_CODE -eq 143 ]] && EXIT_CODE=124
299
+
300
+ # ============================================================
301
+ # Check result
302
+ # ============================================================
303
+
304
+ echo ""
305
+ if [[ -f "$SESSION_LOG" ]]; then
306
+ FINAL_LINES=$(wc -l < "$SESSION_LOG" 2>/dev/null | tr -d ' ')
307
+ FINAL_SIZE=$(wc -c < "$SESSION_LOG" 2>/dev/null | tr -d ' ')
308
+ log_info "Session log: $FINAL_LINES lines, $((FINAL_SIZE / 1024))KB"
309
+ fi
310
+
311
+ SESSION_STATUS_FILE="$SESSION_DIR/session-status.json"
312
+
313
+ if [[ $EXIT_CODE -eq 124 ]]; then
314
+ log_warn "Session timed out after ${SESSION_TIMEOUT}s"
315
+ SESSION_STATUS="timed_out"
316
+ elif [[ -f "$SESSION_STATUS_FILE" ]]; then
317
+ SESSION_STATUS=$(python3 "$SCRIPTS_DIR/check-session-status.py" \
318
+ --status-file "$SESSION_STATUS_FILE" 2>/dev/null) || SESSION_STATUS="crashed"
319
+ else
320
+ log_warn "Session ended without status file"
321
+ SESSION_STATUS="crashed"
322
+ fi
323
+
324
+ # Update bug status
325
+ python3 "$SCRIPTS_DIR/update-bug-status.py" \
326
+ --bug-list "$BUG_LIST" \
327
+ --state-dir "$STATE_DIR" \
328
+ --bug-id "$BUG_ID" \
329
+ --session-status "$SESSION_STATUS" \
330
+ --session-id "$SESSION_ID" \
331
+ --max-retries 999 \
332
+ --action update >/dev/null 2>&1 || true
333
+
334
+ echo ""
335
+ if [[ "$SESSION_STATUS" == "success" ]]; then
336
+ log_success "════════════════════════════════════════════════════"
337
+ log_success " $BUG_ID fixed successfully!"
338
+ log_success "════════════════════════════════════════════════════"
339
+ else
340
+ log_error "════════════════════════════════════════════════════"
341
+ log_error " $BUG_ID result: $SESSION_STATUS"
342
+ log_error " Review log: $SESSION_LOG"
343
+ log_error "════════════════════════════════════════════════════"
344
+ fi