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.
- package/bundled/VERSION.json +5 -0
- package/bundled/adapters/claude/agent-adapter.js +108 -0
- package/bundled/adapters/claude/command-adapter.js +104 -0
- package/bundled/adapters/claude/paths.js +35 -0
- package/bundled/adapters/claude/rules-adapter.js +77 -0
- package/bundled/adapters/claude/settings-adapter.js +73 -0
- package/bundled/adapters/claude/team-adapter.js +183 -0
- package/bundled/adapters/codebuddy/agent-adapter.js +43 -0
- package/bundled/adapters/codebuddy/paths.js +29 -0
- package/bundled/adapters/codebuddy/settings-adapter.js +47 -0
- package/bundled/adapters/codebuddy/skill-adapter.js +68 -0
- package/bundled/adapters/codebuddy/team-adapter.js +46 -0
- package/bundled/adapters/shared/frontmatter.js +77 -0
- package/bundled/agents/prizm-dev-team-coordinator.md +142 -0
- package/bundled/agents/prizm-dev-team-dev.md +99 -0
- package/bundled/agents/prizm-dev-team-pm.md +114 -0
- package/bundled/agents/prizm-dev-team-reviewer.md +119 -0
- package/bundled/dev-pipeline/README.md +482 -0
- package/bundled/dev-pipeline/assets/feature-list-example.json +147 -0
- package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +138 -0
- package/bundled/dev-pipeline/launch-bugfix-daemon.sh +425 -0
- package/bundled/dev-pipeline/launch-daemon.sh +549 -0
- package/bundled/dev-pipeline/reset-feature.sh +209 -0
- package/bundled/dev-pipeline/retry-bug.sh +344 -0
- package/bundled/dev-pipeline/retry-feature.sh +338 -0
- package/bundled/dev-pipeline/run-bugfix.sh +638 -0
- package/bundled/dev-pipeline/run.sh +845 -0
- package/bundled/dev-pipeline/scripts/check-session-status.py +158 -0
- package/bundled/dev-pipeline/scripts/detect-stuck.py +385 -0
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +598 -0
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +402 -0
- package/bundled/dev-pipeline/scripts/init-bugfix-pipeline.py +294 -0
- package/bundled/dev-pipeline/scripts/init-dev-team.py +134 -0
- package/bundled/dev-pipeline/scripts/init-pipeline.py +335 -0
- package/bundled/dev-pipeline/scripts/update-bug-status.py +748 -0
- package/bundled/dev-pipeline/scripts/update-feature-status.py +1076 -0
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +262 -0
- package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +159 -0
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +291 -0
- package/bundled/dev-pipeline/templates/feature-list-schema.json +112 -0
- package/bundled/dev-pipeline/templates/session-status-schema.json +77 -0
- package/bundled/skills/_metadata.json +267 -0
- package/bundled/skills/app-planner/SKILL.md +580 -0
- package/bundled/skills/app-planner/assets/planning-guide.md +313 -0
- package/bundled/skills/app-planner/scripts/validate-and-generate.py +758 -0
- package/bundled/skills/bug-planner/SKILL.md +235 -0
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +252 -0
- package/bundled/skills/dev-pipeline-launcher/SKILL.md +223 -0
- package/bundled/skills/prizm-kit/SKILL.md +151 -0
- package/bundled/skills/prizm-kit/assets/claude-md-template.md +38 -0
- package/bundled/skills/prizm-kit/assets/codebuddy-md-template.md +35 -0
- package/bundled/skills/prizm-kit/assets/hooks/prizm-commit-hook.json +15 -0
- package/bundled/skills/prizmkit-adr-manager/SKILL.md +68 -0
- package/bundled/skills/prizmkit-adr-manager/assets/adr-template.md +26 -0
- package/bundled/skills/prizmkit-analyze/SKILL.md +194 -0
- package/bundled/skills/prizmkit-api-doc-generator/SKILL.md +56 -0
- package/bundled/skills/prizmkit-bug-fix-workflow/SKILL.md +351 -0
- package/bundled/skills/prizmkit-bug-reproducer/SKILL.md +62 -0
- package/bundled/skills/prizmkit-ci-cd-generator/SKILL.md +54 -0
- package/bundled/skills/prizmkit-clarify/SKILL.md +52 -0
- package/bundled/skills/prizmkit-code-review/SKILL.md +70 -0
- package/bundled/skills/prizmkit-committer/SKILL.md +117 -0
- package/bundled/skills/prizmkit-db-migration/SKILL.md +65 -0
- package/bundled/skills/prizmkit-dependency-health/SKILL.md +123 -0
- package/bundled/skills/prizmkit-deployment-strategy/SKILL.md +58 -0
- package/bundled/skills/prizmkit-error-triage/SKILL.md +55 -0
- package/bundled/skills/prizmkit-implement/SKILL.md +47 -0
- package/bundled/skills/prizmkit-init/SKILL.md +156 -0
- package/bundled/skills/prizmkit-log-analyzer/SKILL.md +55 -0
- package/bundled/skills/prizmkit-monitoring-setup/SKILL.md +75 -0
- package/bundled/skills/prizmkit-onboarding-generator/SKILL.md +70 -0
- package/bundled/skills/prizmkit-perf-profiler/SKILL.md +55 -0
- package/bundled/skills/prizmkit-plan/SKILL.md +54 -0
- package/bundled/skills/prizmkit-plan/assets/plan-template.md +37 -0
- package/bundled/skills/prizmkit-prizm-docs/SKILL.md +140 -0
- package/bundled/skills/prizmkit-prizm-docs/assets/PRIZM-SPEC.md +943 -0
- package/bundled/skills/prizmkit-retrospective/SKILL.md +79 -0
- package/bundled/skills/prizmkit-security-audit/SKILL.md +130 -0
- package/bundled/skills/prizmkit-specify/SKILL.md +52 -0
- package/bundled/skills/prizmkit-specify/assets/spec-template.md +37 -0
- package/bundled/skills/prizmkit-summarize/SKILL.md +51 -0
- package/bundled/skills/prizmkit-summarize/assets/registry-template.md +18 -0
- package/bundled/skills/prizmkit-tasks/SKILL.md +50 -0
- package/bundled/skills/prizmkit-tasks/assets/tasks-template.md +21 -0
- package/bundled/skills/prizmkit-tech-debt-tracker/SKILL.md +139 -0
- package/bundled/team/prizm-dev-team.json +47 -0
- package/bundled/templates/claude-md-template.md +38 -0
- package/bundled/templates/codebuddy-md-template.md +35 -0
- 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
|