prizmkit 1.0.6 → 1.0.8

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "frameworkVersion": "1.0.6",
3
- "bundledAt": "2026-03-11T13:45:52.858Z",
4
- "bundledFrom": "34eeda9"
2
+ "frameworkVersion": "1.0.8",
3
+ "bundledAt": "2026-03-11T17:33:39.633Z",
4
+ "bundledFrom": "66dd874"
5
5
  }
@@ -468,15 +468,17 @@ dev-pipeline/bugfix-state/ # Runtime state (gitignored)
468
468
  └── session.log # Full session output
469
469
  ```
470
470
 
471
- ### Differences from Feature Pipeline
472
-
473
- | Aspect | Feature Pipeline | Bug Fix Pipeline |
474
- |--------|-----------------|-----------------|
475
- | Input file | `feature-list.json` | `bug-fix-list.json` |
476
- | ID format | `F-NNN` | `B-NNN` |
477
- | State dir | `state/` | `bugfix-state/` |
478
- | Ordering | Dependencies DAG → priority | Severity → priority (no dependencies) |
479
- | Phases | 10-phase (specify → plan → tasks → implement → review) | 5-phase (triage → reproduce → fix → verify → commit) |
480
- | Agents | Coordinator + PM + Dev + Reviewer | Dev + Reviewer only |
481
- | Artifacts | spec.md, plan.md, tasks.md, REGISTRY.md | fix-plan.md, fix-report.md only |
482
- | Commit prefix | `feat(<scope>):` | `fix(<scope>):` |
471
+ ### Differences Between Pipelines
472
+
473
+ | Aspect | Feature Pipeline | Refactor Workflow | Bug Fix Pipeline |
474
+ |--------|-----------------|-------------------|------------------|
475
+ | Input file | `feature-list.json` | N/A (conversation trigger) | `bug-fix-list.json` |
476
+ | ID format | `F-NNN` | `<refactor-slug>` | `B-NNN` |
477
+ | State dir | `state/` | N/A (in-session) | `bugfix-state/` |
478
+ | Ordering | Dependencies DAG → priority | N/A (single refactor per session) | Severity → priority (no dependencies) |
479
+ | Phases | 10-phase (specify → plan → tasks → implement → review) | 6-phase (analyze → plan → tasks → implement → review → commit) | 5-phase (triage → reproduce → fix → verify → commit) |
480
+ | Agents | Coordinator + PM + Dev + Reviewer | Dev + Reviewer only | Dev + Reviewer only |
481
+ | Artifacts | spec.md, plan.md, tasks.md, REGISTRY.md | refactor-analysis.md, plan.md, tasks.md | fix-plan.md, fix-report.md only |
482
+ | Commit prefix | `feat(<scope>):` | `refactor(<scope>):` | `fix(<scope>):` |
483
+ | Scope Guard | N/A | ✅ (behavior change → STOP) | N/A |
484
+ | Test Strategy | TDD per task | Full suite after EVERY task | Reproduction test |
@@ -14,6 +14,10 @@ set -euo pipefail
14
14
  # ./launch-bugfix-daemon.sh logs [--lines N] [--follow]
15
15
  # ./launch-bugfix-daemon.sh restart [bug-fix-list.json] [--env "KEY=VAL ..."]
16
16
  #
17
+ # NOTE:
18
+ # In AI skill sessions, always use this daemon wrapper.
19
+ # Do NOT call `run-bugfix.sh run ...` directly, because foreground sessions may be killed by CLI timeout.
20
+ #
17
21
  # Files managed:
18
22
  # bugfix-state/.pipeline.pid - PID of the background run-bugfix.sh process
19
23
  # bugfix-state/pipeline-daemon.log - Consolidated stdout+stderr
@@ -14,6 +14,10 @@ set -euo pipefail
14
14
  # ./launch-daemon.sh logs [--lines N] [--follow]
15
15
  # ./launch-daemon.sh restart [feature-list.json] [--env "KEY=VAL ..."]
16
16
  #
17
+ # NOTE:
18
+ # In AI skill sessions, always use this daemon wrapper.
19
+ # Do NOT call `run.sh run ...` directly, because foreground sessions may be killed by CLI timeout.
20
+ #
17
21
  # Files managed:
18
22
  # state/.pipeline.pid - PID of the background run.sh process
19
23
  # state/pipeline-daemon.log - Consolidated stdout+stderr from run.sh
@@ -48,6 +48,12 @@ else
48
48
  fi
49
49
  export PRIZMKIT_PLATFORM="$PLATFORM"
50
50
 
51
+ # Source shared heartbeat library
52
+ source "$SCRIPT_DIR/lib/heartbeat.sh"
53
+
54
+ # Detect stream-json support
55
+ detect_stream_json_support "$CLI_CMD"
56
+
51
57
  # Colors
52
58
  RED='\033[0;31m'
53
59
  GREEN='\033[0;32m'
@@ -194,6 +200,13 @@ echo -e "${BOLD}═════════════════════
194
200
  echo ""
195
201
 
196
202
  SESSION_LOG="$SESSION_DIR/logs/session.log"
203
+ PROGRESS_JSON="$SESSION_DIR/logs/progress.json"
204
+
205
+ # Build stream-json flag
206
+ STREAM_JSON_FLAG=""
207
+ if [[ "$USE_STREAM_JSON" == "true" ]]; then
208
+ STREAM_JSON_FLAG="--output-format stream-json"
209
+ fi
197
210
 
198
211
  # Spawn AI CLI session
199
212
  case "$CLI_CMD" in
@@ -202,18 +215,24 @@ case "$CLI_CMD" in
202
215
  --print \
203
216
  -p "$(cat "$BOOTSTRAP_PROMPT")" \
204
217
  --yes \
218
+ $STREAM_JSON_FLAG \
205
219
  > "$SESSION_LOG" 2>&1 &
206
220
  ;;
207
221
  *)
208
222
  "$CLI_CMD" \
209
223
  --print \
210
224
  -y \
225
+ $STREAM_JSON_FLAG \
211
226
  < "$BOOTSTRAP_PROMPT" \
212
227
  > "$SESSION_LOG" 2>&1 &
213
228
  ;;
214
229
  esac
215
230
  CLI_PID=$!
216
231
 
232
+ # Start progress parser (no-op if stream-json not supported)
233
+ start_progress_parser "$SESSION_LOG" "$PROGRESS_JSON" "$SCRIPTS_DIR"
234
+ PARSER_PID="${_PARSER_PID:-}"
235
+
217
236
  # Timeout watchdog
218
237
  WATCHER_PID=""
219
238
  if [[ $SESSION_TIMEOUT -gt 0 ]]; then
@@ -222,49 +241,8 @@ if [[ $SESSION_TIMEOUT -gt 0 ]]; then
222
241
  fi
223
242
 
224
243
  # 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=$!
244
+ start_heartbeat "$CLI_PID" "$SESSION_LOG" "$PROGRESS_JSON" "$HEARTBEAT_INTERVAL"
245
+ HEARTBEAT_PID="${_HEARTBEAT_PID:-}"
268
246
 
269
247
  # Ctrl+C cleanup
270
248
  cleanup() {
@@ -272,10 +250,10 @@ cleanup() {
272
250
  log_warn "Interrupted. Killing session..."
273
251
  kill "$CLI_PID" 2>/dev/null || true
274
252
  [[ -n "$WATCHER_PID" ]] && kill "$WATCHER_PID" 2>/dev/null || true
275
- kill "$HEARTBEAT_PID" 2>/dev/null || true
253
+ stop_heartbeat "$HEARTBEAT_PID"
254
+ stop_progress_parser "$PARSER_PID"
276
255
  wait "$CLI_PID" 2>/dev/null || true
277
256
  [[ -n "$WATCHER_PID" ]] && wait "$WATCHER_PID" 2>/dev/null || true
278
- wait "$HEARTBEAT_PID" 2>/dev/null || true
279
257
  log_info "Session log: $SESSION_LOG"
280
258
  exit 130
281
259
  }
@@ -291,9 +269,9 @@ fi
291
269
 
292
270
  # Cleanup background processes
293
271
  [[ -n "$WATCHER_PID" ]] && kill "$WATCHER_PID" 2>/dev/null || true
294
- kill "$HEARTBEAT_PID" 2>/dev/null || true
272
+ stop_heartbeat "$HEARTBEAT_PID"
273
+ stop_progress_parser "$PARSER_PID"
295
274
  [[ -n "$WATCHER_PID" ]] && wait "$WATCHER_PID" 2>/dev/null || true
296
- wait "$HEARTBEAT_PID" 2>/dev/null || true
297
275
 
298
276
  [[ $EXIT_CODE -eq 143 ]] && EXIT_CODE=124
299
277
 
@@ -48,6 +48,12 @@ else
48
48
  fi
49
49
  export PRIZMKIT_PLATFORM="$PLATFORM"
50
50
 
51
+ # Source shared heartbeat library
52
+ source "$SCRIPT_DIR/lib/heartbeat.sh"
53
+
54
+ # Detect stream-json support
55
+ detect_stream_json_support "$CLI_CMD"
56
+
51
57
  # Colors
52
58
  RED='\033[0;31m'
53
59
  GREEN='\033[0;32m'
@@ -188,6 +194,13 @@ echo -e "${BOLD}═════════════════════
188
194
  echo ""
189
195
 
190
196
  SESSION_LOG="$SESSION_DIR/logs/session.log"
197
+ PROGRESS_JSON="$SESSION_DIR/logs/progress.json"
198
+
199
+ # Build stream-json flag
200
+ STREAM_JSON_FLAG=""
201
+ if [[ "$USE_STREAM_JSON" == "true" ]]; then
202
+ STREAM_JSON_FLAG="--output-format stream-json"
203
+ fi
191
204
 
192
205
  # Spawn AI CLI session
193
206
  case "$CLI_CMD" in
@@ -196,18 +209,24 @@ case "$CLI_CMD" in
196
209
  --print \
197
210
  -p "$(cat "$BOOTSTRAP_PROMPT")" \
198
211
  --yes \
212
+ $STREAM_JSON_FLAG \
199
213
  > "$SESSION_LOG" 2>&1 &
200
214
  ;;
201
215
  *)
202
216
  "$CLI_CMD" \
203
217
  --print \
204
218
  -y \
219
+ $STREAM_JSON_FLAG \
205
220
  < "$BOOTSTRAP_PROMPT" \
206
221
  > "$SESSION_LOG" 2>&1 &
207
222
  ;;
208
223
  esac
209
224
  CBC_PID=$!
210
225
 
226
+ # Start progress parser (no-op if stream-json not supported)
227
+ start_progress_parser "$SESSION_LOG" "$PROGRESS_JSON" "$SCRIPTS_DIR"
228
+ PARSER_PID="${_PARSER_PID:-}"
229
+
211
230
  # Timeout watchdog (only if SESSION_TIMEOUT > 0)
212
231
  WATCHER_PID=""
213
232
  if [[ $SESSION_TIMEOUT -gt 0 ]]; then
@@ -216,49 +235,8 @@ if [[ $SESSION_TIMEOUT -gt 0 ]]; then
216
235
  fi
217
236
 
218
237
  # Heartbeat
219
- (
220
- elapsed=0
221
- prev_size=0
222
- while kill -0 "$CBC_PID" 2>/dev/null; do
223
- sleep "$HEARTBEAT_INTERVAL"
224
- elapsed=$((elapsed + HEARTBEAT_INTERVAL))
225
- kill -0 "$CBC_PID" 2>/dev/null || break
226
-
227
- cur_size=0
228
- if [[ -f "$SESSION_LOG" ]]; then
229
- cur_size=$(wc -c < "$SESSION_LOG" 2>/dev/null || echo 0)
230
- cur_size=$(echo "$cur_size" | tr -d ' ')
231
- fi
232
-
233
- growth=$((cur_size - prev_size))
234
- prev_size=$cur_size
235
-
236
- if [[ $cur_size -gt 1048576 ]]; then
237
- size_display="$((cur_size / 1048576))MB"
238
- elif [[ $cur_size -gt 1024 ]]; then
239
- size_display="$((cur_size / 1024))KB"
240
- else
241
- size_display="${cur_size}B"
242
- fi
243
-
244
- mins=$((elapsed / 60))
245
- secs=$((elapsed % 60))
246
-
247
- last_activity=""
248
- if [[ -f "$SESSION_LOG" ]]; then
249
- last_activity=$(tail -20 "$SESSION_LOG" 2>/dev/null | grep -v '^$' | tail -1 | cut -c1-80 || echo "")
250
- fi
251
-
252
- if [[ $growth -gt 0 ]]; then
253
- icon="${GREEN}▶${NC}"
254
- else
255
- icon="${YELLOW}⏸${NC}"
256
- fi
257
-
258
- echo -e " ${icon} ${BLUE}[HEARTBEAT]${NC} ${mins}m${secs}s | log: ${size_display} (+${growth}B) | ${last_activity}"
259
- done
260
- ) &
261
- HEARTBEAT_PID=$!
238
+ start_heartbeat "$CBC_PID" "$SESSION_LOG" "$PROGRESS_JSON" "$HEARTBEAT_INTERVAL"
239
+ HEARTBEAT_PID="${_HEARTBEAT_PID:-}"
262
240
 
263
241
  # Ctrl+C cleanup
264
242
  cleanup() {
@@ -266,10 +244,10 @@ cleanup() {
266
244
  log_warn "Interrupted. Killing session..."
267
245
  kill "$CBC_PID" 2>/dev/null || true
268
246
  [[ -n "$WATCHER_PID" ]] && kill "$WATCHER_PID" 2>/dev/null || true
269
- kill "$HEARTBEAT_PID" 2>/dev/null || true
247
+ stop_heartbeat "$HEARTBEAT_PID"
248
+ stop_progress_parser "$PARSER_PID"
270
249
  wait "$CBC_PID" 2>/dev/null || true
271
250
  [[ -n "$WATCHER_PID" ]] && wait "$WATCHER_PID" 2>/dev/null || true
272
- wait "$HEARTBEAT_PID" 2>/dev/null || true
273
251
  log_info "Session log: $SESSION_LOG"
274
252
  exit 130
275
253
  }
@@ -285,9 +263,9 @@ fi
285
263
 
286
264
  # Cleanup background processes
287
265
  [[ -n "$WATCHER_PID" ]] && kill "$WATCHER_PID" 2>/dev/null || true
288
- kill "$HEARTBEAT_PID" 2>/dev/null || true
266
+ stop_heartbeat "$HEARTBEAT_PID"
267
+ stop_progress_parser "$PARSER_PID"
289
268
  [[ -n "$WATCHER_PID" ]] && wait "$WATCHER_PID" 2>/dev/null || true
290
- wait "$HEARTBEAT_PID" 2>/dev/null || true
291
269
 
292
270
  [[ $EXIT_CODE -eq 143 ]] && EXIT_CODE=124
293
271
 
@@ -57,6 +57,12 @@ else
57
57
  fi
58
58
  export PRIZMKIT_PLATFORM="$PLATFORM"
59
59
 
60
+ # Source shared heartbeat library
61
+ source "$SCRIPT_DIR/lib/heartbeat.sh"
62
+
63
+ # Detect stream-json support
64
+ detect_stream_json_support "$CLI_CMD"
65
+
60
66
  # Bug list path (set in main, used by cleanup trap)
61
67
  BUG_LIST=""
62
68
 
@@ -87,12 +93,18 @@ spawn_and_wait_session() {
87
93
  local max_retries="$6"
88
94
 
89
95
  local session_log="$session_dir/logs/session.log"
96
+ local progress_json="$session_dir/logs/progress.json"
90
97
 
91
98
  local verbose_flag=""
92
99
  if [[ "$VERBOSE" == "1" ]]; then
93
100
  verbose_flag="--verbose"
94
101
  fi
95
102
 
103
+ local stream_json_flag=""
104
+ if [[ "$USE_STREAM_JSON" == "true" ]]; then
105
+ stream_json_flag="--output-format stream-json"
106
+ fi
107
+
96
108
  case "$CLI_CMD" in
97
109
  *claude*)
98
110
  "$CLI_CMD" \
@@ -100,6 +112,7 @@ spawn_and_wait_session() {
100
112
  -p "$(cat "$bootstrap_prompt")" \
101
113
  --yes \
102
114
  $verbose_flag \
115
+ $stream_json_flag \
103
116
  > "$session_log" 2>&1 &
104
117
  ;;
105
118
  *)
@@ -107,12 +120,17 @@ spawn_and_wait_session() {
107
120
  --print \
108
121
  -y \
109
122
  $verbose_flag \
123
+ $stream_json_flag \
110
124
  < "$bootstrap_prompt" \
111
125
  > "$session_log" 2>&1 &
112
126
  ;;
113
127
  esac
114
128
  local cli_pid=$!
115
129
 
130
+ # Start progress parser (no-op if stream-json not supported)
131
+ start_progress_parser "$session_log" "$progress_json" "$SCRIPTS_DIR"
132
+ local parser_pid="${_PARSER_PID:-}"
133
+
116
134
  # Timeout watchdog
117
135
  local watcher_pid=""
118
136
  if [[ $SESSION_TIMEOUT -gt 0 ]]; then
@@ -121,52 +139,8 @@ spawn_and_wait_session() {
121
139
  fi
122
140
 
123
141
  # 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=$!
142
+ start_heartbeat "$cli_pid" "$session_log" "$progress_json" "$HEARTBEAT_INTERVAL"
143
+ local heartbeat_pid="${_HEARTBEAT_PID:-}"
170
144
 
171
145
  # Wait for AI CLI to finish
172
146
  local exit_code=0
@@ -178,9 +152,9 @@ spawn_and_wait_session() {
178
152
 
179
153
  # Cleanup
180
154
  [[ -n "$watcher_pid" ]] && kill "$watcher_pid" 2>/dev/null || true
181
- kill "$heartbeat_pid" 2>/dev/null || true
155
+ stop_heartbeat "$heartbeat_pid"
156
+ stop_progress_parser "$parser_pid"
182
157
  [[ -n "$watcher_pid" ]] && wait "$watcher_pid" 2>/dev/null || true
183
- wait "$heartbeat_pid" 2>/dev/null || true
184
158
 
185
159
  [[ $exit_code -eq 143 ]] && exit_code=124
186
160
 
@@ -61,6 +61,12 @@ fi
61
61
 
62
62
  export PRIZMKIT_PLATFORM="$PLATFORM"
63
63
 
64
+ # Source shared heartbeat library
65
+ source "$SCRIPT_DIR/lib/heartbeat.sh"
66
+
67
+ # Detect stream-json support
68
+ detect_stream_json_support "$CLI_CMD"
69
+
64
70
  # Feature list path (set in main, used by cleanup trap)
65
71
  FEATURE_LIST=""
66
72
 
@@ -100,6 +106,7 @@ spawn_and_wait_session() {
100
106
  local max_retries="$6"
101
107
 
102
108
  local session_log="$session_dir/logs/session.log"
109
+ local progress_json="$session_dir/logs/progress.json"
103
110
 
104
111
  # Spawn AI CLI session
105
112
  local verbose_flag=""
@@ -107,6 +114,11 @@ spawn_and_wait_session() {
107
114
  verbose_flag="--verbose"
108
115
  fi
109
116
 
117
+ local stream_json_flag=""
118
+ if [[ "$USE_STREAM_JSON" == "true" ]]; then
119
+ stream_json_flag="--output-format stream-json"
120
+ fi
121
+
110
122
  case "$CLI_CMD" in
111
123
  *claude*)
112
124
  # Claude Code: prompt via -p argument, --yes for auto-accept
@@ -115,6 +127,7 @@ spawn_and_wait_session() {
115
127
  -p "$(cat "$bootstrap_prompt")" \
116
128
  --yes \
117
129
  $verbose_flag \
130
+ $stream_json_flag \
118
131
  > "$session_log" 2>&1 &
119
132
  ;;
120
133
  *)
@@ -123,12 +136,17 @@ spawn_and_wait_session() {
123
136
  --print \
124
137
  -y \
125
138
  $verbose_flag \
139
+ $stream_json_flag \
126
140
  < "$bootstrap_prompt" \
127
141
  > "$session_log" 2>&1 &
128
142
  ;;
129
143
  esac
130
144
  local cbc_pid=$!
131
145
 
146
+ # Start progress parser (no-op if stream-json not supported)
147
+ start_progress_parser "$session_log" "$progress_json" "$SCRIPTS_DIR"
148
+ local parser_pid="${_PARSER_PID:-}"
149
+
132
150
  # Timeout watchdog (only if SESSION_TIMEOUT > 0)
133
151
  local watcher_pid=""
134
152
  if [[ $SESSION_TIMEOUT -gt 0 ]]; then
@@ -136,53 +154,9 @@ spawn_and_wait_session() {
136
154
  watcher_pid=$!
137
155
  fi
138
156
 
139
- # Heartbeat monitor
140
- local heartbeat_interval=$HEARTBEAT_INTERVAL
141
- (
142
- local elapsed=0
143
- local prev_size=0
144
- while kill -0 "$cbc_pid" 2>/dev/null; do
145
- sleep "$heartbeat_interval"
146
- elapsed=$((elapsed + heartbeat_interval))
147
- kill -0 "$cbc_pid" 2>/dev/null || break
148
-
149
- local cur_size=0
150
- if [[ -f "$session_log" ]]; then
151
- cur_size=$(wc -c < "$session_log" 2>/dev/null || echo 0)
152
- cur_size=$(echo "$cur_size" | tr -d ' ')
153
- fi
154
-
155
- local growth=$((cur_size - prev_size))
156
- prev_size=$cur_size
157
-
158
- local size_display
159
- if [[ $cur_size -gt 1048576 ]]; then
160
- size_display="$((cur_size / 1048576))MB"
161
- elif [[ $cur_size -gt 1024 ]]; then
162
- size_display="$((cur_size / 1024))KB"
163
- else
164
- size_display="${cur_size}B"
165
- fi
166
-
167
- local mins=$((elapsed / 60))
168
- local secs=$((elapsed % 60))
169
-
170
- local last_activity=""
171
- if [[ -f "$session_log" ]]; then
172
- last_activity=$(tail -20 "$session_log" 2>/dev/null | grep -v '^$' | tail -1 | cut -c1-80 || echo "")
173
- fi
174
-
175
- local status_icon
176
- if [[ $growth -gt 0 ]]; then
177
- status_icon="${GREEN}▶${NC}"
178
- else
179
- status_icon="${YELLOW}⏸${NC}"
180
- fi
181
-
182
- echo -e " ${status_icon} ${BLUE}[HEARTBEAT]${NC} ${mins}m${secs}s elapsed | log: ${size_display} (+${growth}B) | ${last_activity}"
183
- done
184
- ) &
185
- local heartbeat_pid=$!
157
+ # Heartbeat monitor (reads progress.json when available, falls back to tail)
158
+ start_heartbeat "$cbc_pid" "$session_log" "$progress_json" "$HEARTBEAT_INTERVAL"
159
+ local heartbeat_pid="${_HEARTBEAT_PID:-}"
186
160
 
187
161
  # Wait for AI CLI to finish
188
162
  local exit_code=0
@@ -192,11 +166,11 @@ spawn_and_wait_session() {
192
166
  exit_code=$?
193
167
  fi
194
168
 
195
- # Clean up watcher and heartbeat
169
+ # Clean up watcher, heartbeat, and parser
196
170
  [[ -n "$watcher_pid" ]] && kill "$watcher_pid" 2>/dev/null || true
197
- kill "$heartbeat_pid" 2>/dev/null || true
171
+ stop_heartbeat "$heartbeat_pid"
172
+ stop_progress_parser "$parser_pid"
198
173
  [[ -n "$watcher_pid" ]] && wait "$watcher_pid" 2>/dev/null || true
199
- wait "$heartbeat_pid" 2>/dev/null || true
200
174
 
201
175
  # Map SIGTERM (143) to timeout code 124
202
176
  if [[ $exit_code -eq 143 ]]; then
@@ -0,0 +1,296 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ parse-stream-progress.py - Real-time stream-json progress parser
4
+
5
+ Continuously reads an AI CLI session log (JSONL from --output-format stream-json),
6
+ extracts tool calls, phase changes, and activity metrics, and writes structured
7
+ progress to a progress.json file for heartbeat monitoring.
8
+
9
+ Usage:
10
+ python3 parse-stream-progress.py --session-log <path> --progress-file <path>
11
+
12
+ The script runs until:
13
+ - The session log stops growing and the CLI process exits
14
+ - It receives SIGTERM/SIGINT
15
+ """
16
+
17
+ import argparse
18
+ import json
19
+ import os
20
+ import signal
21
+ import sys
22
+ import tempfile
23
+ import time
24
+ from collections import Counter
25
+ from datetime import datetime, timezone
26
+
27
+
28
+ # Phase keywords for detection
29
+ PHASE_KEYWORDS = {
30
+ "specify": ["prizmkit-specify", "spec.md", "specification", "gathering requirements"],
31
+ "plan": ["prizmkit-plan", "plan.md", "architecture", "design plan"],
32
+ "tasks": ["prizmkit-tasks", "tasks.md", "task checklist", "task breakdown"],
33
+ "analyze": ["prizmkit-analyze", "cross-check", "consistency", "analyzing"],
34
+ "implement": ["prizmkit-implement", "implement", "TDD", "coding", "writing code"],
35
+ "code-review": ["prizmkit-code-review", "code review", "review verdict", "reviewing"],
36
+ "summarize": ["prizmkit-summarize", "REGISTRY.md", "summarize", "summary"],
37
+ "commit": ["prizmkit-committer", "git commit", "feat(", "fix(", "committing"],
38
+ }
39
+
40
+
41
+ class ProgressTracker:
42
+ """Tracks progress state from stream-json events."""
43
+
44
+ def __init__(self):
45
+ self.message_count = 0
46
+ self.current_tool = None
47
+ self.current_tool_input_summary = ""
48
+ self.current_phase = None
49
+ self.detected_phases = []
50
+ self.tool_call_counts = Counter()
51
+ self.total_tool_calls = 0
52
+ self.last_text_snippet = ""
53
+ self.is_active = True
54
+ self.errors = []
55
+ self._text_buffer = ""
56
+ self._in_tool_use = False
57
+ self._current_tool_input_parts = []
58
+
59
+ def process_event(self, event):
60
+ """Process a single stream-json event and update state."""
61
+ event_type = event.get("type", "")
62
+
63
+ if event_type == "message_start":
64
+ self.message_count += 1
65
+ self.is_active = True
66
+
67
+ elif event_type == "message_stop":
68
+ self.current_tool = None
69
+ self.current_tool_input_summary = ""
70
+ self._in_tool_use = False
71
+ self._current_tool_input_parts = []
72
+
73
+ elif event_type == "content_block_start":
74
+ content_block = event.get("content_block", {})
75
+ block_type = content_block.get("type", "")
76
+
77
+ if block_type == "tool_use":
78
+ tool_name = content_block.get("name", "unknown")
79
+ self.current_tool = tool_name
80
+ self.current_tool_input_summary = ""
81
+ self.tool_call_counts[tool_name] += 1
82
+ self.total_tool_calls += 1
83
+ self._in_tool_use = True
84
+ self._current_tool_input_parts = []
85
+
86
+ elif block_type == "text":
87
+ self._text_buffer = ""
88
+ self._in_tool_use = False
89
+
90
+ elif event_type == "content_block_delta":
91
+ delta = event.get("delta", {})
92
+ delta_type = delta.get("type", "")
93
+
94
+ if delta_type == "text_delta":
95
+ text = delta.get("text", "")
96
+ self._text_buffer += text
97
+ # Keep last meaningful snippet
98
+ stripped = text.strip()
99
+ if stripped:
100
+ self.last_text_snippet = stripped[:120]
101
+ # Try to detect phase from text
102
+ self._detect_phase(text)
103
+
104
+ elif delta_type == "input_json_delta":
105
+ partial = delta.get("partial_json", "")
106
+ self._current_tool_input_parts.append(partial)
107
+ # Build a summary from accumulated input
108
+ accumulated = "".join(self._current_tool_input_parts)
109
+ self.current_tool_input_summary = accumulated[:150]
110
+
111
+ elif event_type == "content_block_stop":
112
+ if self._in_tool_use:
113
+ # Try to extract a better summary from complete tool input
114
+ full_input = "".join(self._current_tool_input_parts)
115
+ self._extract_tool_summary(full_input)
116
+ self._detect_phase(full_input)
117
+ else:
118
+ # Text block finished - detect phase from accumulated text
119
+ if self._text_buffer:
120
+ self._detect_phase(self._text_buffer)
121
+ self._in_tool_use = False
122
+ self._current_tool_input_parts = []
123
+
124
+ elif event_type == "error":
125
+ error_msg = event.get("error", {}).get("message", "Unknown error")
126
+ self.errors.append(error_msg)
127
+
128
+ # Check for subagent indicator
129
+ if event.get("parent_tool_use_id"):
130
+ # This is a sub-agent event; tool name is still tracked normally
131
+ pass
132
+
133
+ def _detect_phase(self, text):
134
+ """Detect pipeline phase from text content."""
135
+ text_lower = text.lower()
136
+ for phase, keywords in PHASE_KEYWORDS.items():
137
+ for keyword in keywords:
138
+ if keyword.lower() in text_lower:
139
+ self.current_phase = phase
140
+ if phase not in self.detected_phases:
141
+ self.detected_phases.append(phase)
142
+ return
143
+
144
+ def _extract_tool_summary(self, raw_input):
145
+ """Extract a human-readable summary from tool input JSON."""
146
+ try:
147
+ data = json.loads(raw_input)
148
+ # Common patterns in tool inputs
149
+ if isinstance(data, dict):
150
+ # Agent tool - look for description or prompt
151
+ if "description" in data:
152
+ self.current_tool_input_summary = str(data["description"])[:100]
153
+ elif "command" in data:
154
+ self.current_tool_input_summary = str(data["command"])[:100]
155
+ elif "file_path" in data:
156
+ self.current_tool_input_summary = str(data["file_path"])[:100]
157
+ elif "pattern" in data:
158
+ self.current_tool_input_summary = str(data["pattern"])[:100]
159
+ elif "query" in data:
160
+ self.current_tool_input_summary = str(data["query"])[:100]
161
+ elif "prompt" in data:
162
+ self.current_tool_input_summary = str(data["prompt"])[:100]
163
+ except (json.JSONDecodeError, TypeError):
164
+ # Keep whatever partial summary we had
165
+ pass
166
+
167
+ def to_dict(self):
168
+ """Export current state as a dictionary for JSON serialization."""
169
+ tool_calls = [
170
+ {"name": name, "count": count}
171
+ for name, count in self.tool_call_counts.most_common()
172
+ ]
173
+ return {
174
+ "updated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
175
+ "message_count": self.message_count,
176
+ "current_tool": self.current_tool,
177
+ "current_tool_input_summary": self.current_tool_input_summary,
178
+ "current_phase": self.current_phase,
179
+ "detected_phases": self.detected_phases,
180
+ "tool_calls": tool_calls,
181
+ "total_tool_calls": self.total_tool_calls,
182
+ "last_text_snippet": self.last_text_snippet,
183
+ "is_active": self.is_active,
184
+ "errors": self.errors[-10:], # Keep last 10 errors
185
+ }
186
+
187
+
188
+ def atomic_write_json(data, filepath):
189
+ """Write JSON data atomically using tmp file + rename."""
190
+ dir_path = os.path.dirname(filepath)
191
+ tmp_path = None
192
+ try:
193
+ fd, tmp_path = tempfile.mkstemp(dir=dir_path, suffix=".tmp")
194
+ with os.fdopen(fd, "w") as f:
195
+ json.dump(data, f, indent=2)
196
+ f.write("\n")
197
+ os.rename(tmp_path, filepath)
198
+ except OSError:
199
+ # Best effort - remove tmp file if rename failed
200
+ if tmp_path:
201
+ try:
202
+ os.unlink(tmp_path)
203
+ except OSError:
204
+ pass
205
+
206
+
207
+ def tail_and_parse(session_log, progress_file, poll_interval=0.5):
208
+ """Tail session log and parse stream-json events."""
209
+ tracker = ProgressTracker()
210
+ last_write_state = None
211
+
212
+ # Wait for log file to appear
213
+ wait_count = 0
214
+ while not os.path.exists(session_log):
215
+ time.sleep(poll_interval)
216
+ wait_count += 1
217
+ if wait_count > 120: # 60 seconds max wait
218
+ sys.stderr.write(f"Timeout waiting for {session_log}\n")
219
+ sys.exit(1)
220
+
221
+ with open(session_log, "r") as f:
222
+ idle_count = 0
223
+ while True:
224
+ line = f.readline()
225
+ if line:
226
+ idle_count = 0
227
+ line = line.strip()
228
+ if not line:
229
+ continue
230
+ try:
231
+ event = json.loads(line)
232
+ tracker.process_event(event)
233
+ except json.JSONDecodeError:
234
+ # Not a JSON line (could be stderr mixed in)
235
+ # Use it as a text snippet if meaningful
236
+ stripped = line.strip()
237
+ if stripped and len(stripped) > 5:
238
+ tracker.last_text_snippet = stripped[:120]
239
+ continue
240
+
241
+ # Write progress if state changed
242
+ current_state = tracker.to_dict()
243
+ state_key = (
244
+ current_state["message_count"],
245
+ current_state["current_tool"],
246
+ current_state["current_phase"],
247
+ current_state["total_tool_calls"],
248
+ )
249
+ if state_key != last_write_state:
250
+ atomic_write_json(current_state, progress_file)
251
+ last_write_state = state_key
252
+ else:
253
+ idle_count += 1
254
+ # After 2 seconds of no new data, write current state anyway
255
+ # (ensures progress.json stays fresh)
256
+ if idle_count == 4:
257
+ current_state = tracker.to_dict()
258
+ atomic_write_json(current_state, progress_file)
259
+
260
+ # After 3600 idle cycles (30 min), mark inactive and exit
261
+ if idle_count > 3600:
262
+ tracker.is_active = False
263
+ atomic_write_json(tracker.to_dict(), progress_file)
264
+ break
265
+
266
+ time.sleep(poll_interval)
267
+
268
+ # Final write
269
+ tracker.is_active = False
270
+ atomic_write_json(tracker.to_dict(), progress_file)
271
+
272
+
273
+ def main():
274
+ parser = argparse.ArgumentParser(description="Parse stream-json progress")
275
+ parser.add_argument("--session-log", required=True, help="Path to session.log (JSONL)")
276
+ parser.add_argument("--progress-file", required=True, help="Path to write progress.json")
277
+ args = parser.parse_args()
278
+
279
+ # Handle graceful shutdown
280
+ def handle_signal(signum, frame):
281
+ sys.exit(0)
282
+
283
+ signal.signal(signal.SIGTERM, handle_signal)
284
+ signal.signal(signal.SIGINT, handle_signal)
285
+
286
+ try:
287
+ tail_and_parse(args.session_log, args.progress_file)
288
+ except SystemExit:
289
+ pass
290
+ except Exception as e:
291
+ sys.stderr.write(f"parse-stream-progress.py error: {e}\n")
292
+ sys.exit(1)
293
+
294
+
295
+ if __name__ == "__main__":
296
+ main()
@@ -7,6 +7,12 @@ description: "Launch and manage the bugfix pipeline from within a cbc session. S
7
7
 
8
8
  Launch the autonomous bug fix pipeline from within a cbc conversation. The pipeline runs as a fully detached background process -- closing the cbc session does NOT stop the pipeline.
9
9
 
10
+ ### Mandatory Execution Mode (MUST)
11
+
12
+ - Always use daemon mode via `dev-pipeline/launch-bugfix-daemon.sh` for start/stop/status/log actions.
13
+ - NEVER run `dev-pipeline/run-bugfix.sh run ...` directly from this skill.
14
+ - Reason: foreground `run-bugfix.sh` can be terminated by AI CLI command timeout (e.g. cbc 120s), while daemon mode survives session timeout.
15
+
10
16
  ### When to Use
11
17
 
12
18
  **Start bugfix pipeline** -- User says:
@@ -14,7 +20,7 @@ Launch the autonomous bug fix pipeline from within a cbc conversation. The pipel
14
20
  - "start bug fix", "run bug fix", "execute bug list", "begin fixing"
15
21
  - "启动 bug 修复", "开始修复 bug", "运行 bug 修复流水线", "开始修 bug"
16
22
  - "修复所有 bug", "批量修复", "启动修复流水线"
17
- - After bug-planner completes: "go", "start", "fix them", "开始吧", "开始修复"
23
+ - After bug-planner completes: "fix them", "开始修复"
18
24
 
19
25
  **Check status** -- User says:
20
26
  - "bugfix status", "check bug fixes", "how's the fixing going", "bug fix progress"
@@ -7,6 +7,12 @@ description: "Launch and manage the dev-pipeline from within a cbc session. Star
7
7
 
8
8
  Launch the autonomous development pipeline from within a cbc conversation. The pipeline runs as a fully detached background process -- closing the cbc session does NOT stop the pipeline.
9
9
 
10
+ ### Mandatory Execution Mode (MUST)
11
+
12
+ - Always use daemon mode via `dev-pipeline/launch-daemon.sh` for start/stop/status/log actions.
13
+ - NEVER run `dev-pipeline/run.sh run ...` directly from this skill.
14
+ - Reason: foreground `run.sh` can be terminated by AI CLI command timeout (e.g. cbc 120s), while daemon mode survives session timeout.
15
+
10
16
  ### When to Use
11
17
 
12
18
  **Start pipeline** -- User says:
@@ -14,7 +20,7 @@ Launch the autonomous development pipeline from within a cbc conversation. The p
14
20
  - "run the features", "execute feature list", "start implementing"
15
21
  - "启动流水线", "开始实现", "运行流水线", "开始自动开发"
16
22
  - "实现接下来的步骤", "执行 feature list", "开始构建"
17
- - After app-planner completes: "go", "start", "build it", "开始吧"
23
+ - After app-planner completes: "build it", " feature list 开始开发"
18
24
 
19
25
  **Check status** -- User says:
20
26
  - "pipeline status", "check pipeline", "how's it going", "progress"
@@ -28,6 +34,10 @@ Launch the autonomous development pipeline from within a cbc conversation. The p
28
34
  - "show logs", "pipeline logs", "tail logs", "what's happening"
29
35
  - "查看日志", "流水线日志", "看看日志"
30
36
 
37
+ **Retry single feature node** -- User says:
38
+ - "retry F-003", "retry this feature", "retry this node"
39
+ - "重试 F-003", "重试这个节点", "重跑这个 feature"
40
+
31
41
  **Do NOT use this skill when:**
32
42
  - User wants to plan features (use `app-planner` instead)
33
43
  - User wants to implement a single feature manually within current session (use `prizmkit-implement`)
@@ -201,6 +211,26 @@ Pass via `--env`:
201
211
  dev-pipeline/launch-daemon.sh start feature-list.json --env "SESSION_TIMEOUT=7200 MAX_RETRIES=5 VERBOSE=1"
202
212
  ```
203
213
 
214
+ ---
215
+
216
+ #### Intent F: Retry Single Feature Node
217
+
218
+ When user says "retry F-003" or "重试 F-003":
219
+
220
+ ```bash
221
+ dev-pipeline/retry-feature.sh F-003 feature-list.json
222
+ ```
223
+
224
+ When user says "从头重试 F-003" or "clean retry F-003":
225
+
226
+ ```bash
227
+ dev-pipeline/reset-feature.sh F-003 --clean --run feature-list.json
228
+ ```
229
+
230
+ Notes:
231
+ - `retry-feature.sh` runs exactly one feature session and exits.
232
+ - Keep pipeline daemon mode for main run management (`launch-daemon.sh`).
233
+
204
234
  ### Error Handling
205
235
 
206
236
  | Error | Action |
@@ -211,7 +241,7 @@ dev-pipeline/launch-daemon.sh start feature-list.json --env "SESSION_TIMEOUT=720
211
241
  | Pipeline already running | Show status, ask if user wants to stop and restart |
212
242
  | PID file stale (process dead) | `launch-daemon.sh` auto-cleans, retry start |
213
243
  | Launch failed (process died immediately) | Show last 20 lines of log: `tail -20 dev-pipeline/state/pipeline-daemon.log` |
214
- | All features blocked/failed | Show status, suggest resetting failed features: `dev-pipeline/run.sh run <F-XXX> --clean` |
244
+ | All features blocked/failed | Show status, suggest daemon-safe recovery: `dev-pipeline/reset-feature.sh <F-XXX> --clean --run feature-list.json` |
215
245
  | Permission denied on script | Run `chmod +x dev-pipeline/launch-daemon.sh dev-pipeline/run.sh` |
216
246
 
217
247
  ### Integration Notes
@@ -283,22 +283,27 @@ The pipeline supports resuming from the last completed phase by detecting existi
283
283
  | `prizmkit-summarize` | Phase 7: archive to REGISTRY |
284
284
  | `prizmkit-retrospective` | Optional: post-feature lessons learned |
285
285
  | `prizmkit-bug-fix-workflow` | NOT used (separate pipeline for bugs) |
286
+ | `refactor-workflow` | NOT used (separate pipeline for refactoring) |
286
287
  | `app-planner` | Pre-pipeline: interactive feature planning |
287
288
 
288
289
  ---
289
290
 
290
- ## Comparison with Bug Fix Pipeline
291
-
292
- | Dimension | Feature Workflow | Bug Fix Pipeline |
293
- |-----------|-----------------|-----------------|
294
- | Input | Natural language requirement | Bug description / stack trace |
295
- | Pipeline Phases | 7 (Fast Path: 5) | 5 (Fast Path: 3) |
296
- | Artifact Docs | 3: spec.md + plan.md + tasks.md | 2: fix-plan.md + fix-report.md |
297
- | Artifact Path | `.prizmkit/specs/<feature-slug>/` | `.prizmkit/bugfix/<bug-id>/` |
298
- | Skills Chain | specify plan → tasks → analyze → implement → review → commit + summarize | error-triage → bug-reproduce → implement → code-review → commit |
299
- | Commit Prefix | `feat(<scope>):` | `fix(<scope>):` |
300
- | REGISTRY Update | via summarize | not applicable |
301
- | Resume Support | ✅ artifact-based detection | ❌ |
291
+ ## Comparison with Refactor and Bug Fix Pipelines
292
+
293
+ | Dimension | Feature Workflow | Refactor Workflow | Bug Fix Pipeline |
294
+ |-----------|-----------------|-------------------|------------------|
295
+ | Input | Natural language requirement | Module/code target | Bug description / stack trace |
296
+ | Pipeline Phases | 7 (Fast Path: 5) | 6 (Fast Path: 4) | 5 (Fast Path: 3) |
297
+ | Phase 1 | Specify (spec.md) | Analyze (refactor-analysis.md) | Triage (fix-plan.md) |
298
+ | Artifact Docs | 3: spec.md + plan.md + tasks.md | 3: refactor-analysis.md + plan.md + tasks.md | 2: fix-plan.md + fix-report.md |
299
+ | Artifact Path | `.prizmkit/specs/<feature-slug>/` | `.prizmkit/refactor/<slug>/` | `.prizmkit/bugfix/<bug-id>/` |
300
+ | Skills Chain | specify → plan → tasks → analyze → implement → review → commit + summarize | tech-debt-tracker → plan → tasks → implement → review → commit | error-triage → bug-reproduce → implement → code-review → commit |
301
+ | Commit Prefix | `feat(<scope>):` | `refactor(<scope>):` | `fix(<scope>):` |
302
+ | REGISTRY Update | ✅ via summarize | ❌ not applicable | ❌ not applicable |
303
+ | Test Strategy | TDD per task | Full suite after EVERY task | Reproduction test |
304
+ | Scope Guard | N/A | ✅ (enforced) | N/A |
305
+ | Behavior Change | ✅ Expected | ❌ Forbidden | ✅ Fix behavior |
306
+ | Resume Support | ✅ artifact-based detection | ✅ artifact-based detection | ❌ |
302
307
 
303
308
  ## Path References
304
309
 
@@ -315,26 +315,31 @@ When `affected_feature` is non-empty:
315
315
  | `prizmkit-specify` | NOT used (no spec.md for bugs) |
316
316
  | `prizmkit-plan` | NOT used (no plan.md for bugs) |
317
317
  | `prizmkit-tasks` | NOT used (no tasks.md for bugs) |
318
+ | `refactor-workflow` | NOT used (separate pipeline for refactoring) |
318
319
 
319
320
  ---
320
321
 
321
- ## Comparison with Feature Pipeline
322
-
323
- | Dimension | Feature Pipeline | Bug Fix Pipeline |
324
- |-----------|-----------------|-----------------|
325
- | Input Skill | `app-planner` (7-phase interactive) | `bug-planner` (multi-format parser) |
326
- | Input File | `feature-list.json` (F-NNN) | `bug-fix-list.json` (B-NNN) |
327
- | Schema Version | `dev-pipeline-feature-list-v1` | `dev-pipeline-bug-fix-list-v1` |
328
- | Pipeline Phases | 7 Phase (Fast Path: 5) | 5 Phase (Fast Path: 3) |
329
- | Artifact Docs | 3: spec.md + plan.md + tasks.md | 2: fix-plan.md + fix-report.md |
330
- | Artifact Path | `.prizmkit/specs/<feature-slug>/` | `.prizmkit/bugfix/<bug-id>/` |
331
- | Prompt Template | `bootstrap-prompt.md` | `bugfix-bootstrap-prompt.md` |
332
- | Skills Chain | specify plan tasks → implement → review → summarize → commit | error-triage → bug-reproduce → implement → code-review → commit |
333
- | Commit Prefix | `feat(<scope>):` | `fix(<scope>):` |
334
- | REGISTRY Update | via summarize | not applicable |
335
- | TRAPS Update | Only in retrospective | automatic after every fix |
336
- | Manual Verification | None | Supported (verification_type=manual/hybrid) |
337
- | Agent Roles | PM Dev Reviewer Doc | Dev Reviewer (streamlined) |
322
+ ## Comparison with Feature and Refactor Pipelines
323
+
324
+ | Dimension | Feature Pipeline | Refactor Workflow | Bug Fix Pipeline |
325
+ |-----------|-----------------|-------------------|------------------|
326
+ | Input Skill | `app-planner` (7-phase interactive) | Direct invocation (prizmkit.refactor) | `bug-planner` (multi-format parser) |
327
+ | Input File | `feature-list.json` (F-NNN) | N/A (conversation trigger) | `bug-fix-list.json` (B-NNN) |
328
+ | Schema Version | `dev-pipeline-feature-list-v1` | N/A | `dev-pipeline-bug-fix-list-v1` |
329
+ | Pipeline Phases | 7 Phase (Fast Path: 5) | 6 Phase (Fast Path: 4) | 5 Phase (Fast Path: 3) |
330
+ | Phase 1 | Specify (spec.md) | Analyze (refactor-analysis.md) | Triage (fix-plan.md) |
331
+ | Artifact Docs | 3: spec.md + plan.md + tasks.md | 3: refactor-analysis.md + plan.md + tasks.md | 2: fix-plan.md + fix-report.md |
332
+ | Artifact Path | `.prizmkit/specs/<feature-slug>/` | `.prizmkit/refactor/<slug>/` | `.prizmkit/bugfix/<bug-id>/` |
333
+ | Prompt Template | `bootstrap-prompt.md` | N/A (in-session) | `bugfix-bootstrap-prompt.md` |
334
+ | Skills Chain | specify → plan → tasks → implement → review → summarize → commit | tech-debt-tracker → plan → tasks → implement → review → commit | error-triage → bug-reproduce → implement → code-review → commit |
335
+ | Commit Prefix | `feat(<scope>):` | `refactor(<scope>):` | `fix(<scope>):` |
336
+ | REGISTRY Update | via summarize | not applicable | not applicable |
337
+ | TRAPS Update | Only in retrospective | Resolved TRAPS removed | ✅ automatic after every fix |
338
+ | Test Strategy | TDD per task | Full suite after EVERY task | Reproduction test |
339
+ | Scope Guard | N/A | ✅ (enforced) | N/A |
340
+ | Behavior Change | ✅ Expected | ❌ Forbidden | ✅ Fix behavior |
341
+ | Manual Verification | None | None | Supported (verification_type=manual/hybrid) |
342
+ | Agent Roles | PM → Dev → Reviewer → Doc | Dev → Reviewer (streamlined) | Dev → Reviewer (streamlined) |
338
343
 
339
344
  ## Path References
340
345
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prizmkit",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Create a new PrizmKit-powered project with clean initialization — no framework dev files, just what you need.",
5
5
  "type": "module",
6
6
  "bin": {