prizmkit 1.0.7 → 1.0.9
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 +3 -3
- package/bundled/dev-pipeline/launch-bugfix-daemon.sh +4 -0
- package/bundled/dev-pipeline/launch-daemon.sh +4 -0
- package/bundled/dev-pipeline/lib/heartbeat.sh +173 -0
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +7 -1
- package/bundled/skills/dev-pipeline-launcher/SKILL.md +32 -2
- package/package.json +1 -1
- package/src/scaffold.js +1 -1
package/bundled/VERSION.json
CHANGED
|
@@ -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
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ============================================================
|
|
3
|
+
# dev-pipeline/lib/heartbeat.sh - Shared heartbeat monitoring
|
|
4
|
+
#
|
|
5
|
+
# Provides start_heartbeat / stop_heartbeat functions that read
|
|
6
|
+
# structured progress from progress.json (written by
|
|
7
|
+
# parse-stream-progress.py) and fall back to tail-based monitoring.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# source "$SCRIPT_DIR/lib/heartbeat.sh"
|
|
11
|
+
# start_heartbeat "$cli_pid" "$session_log" "$progress_json" "$interval"
|
|
12
|
+
# # ... wait for CLI to finish ...
|
|
13
|
+
# stop_heartbeat "$_HEARTBEAT_PID"
|
|
14
|
+
#
|
|
15
|
+
# Requires: colors (GREEN, YELLOW, BLUE, NC) and log functions
|
|
16
|
+
# to be defined before sourcing.
|
|
17
|
+
# ============================================================
|
|
18
|
+
|
|
19
|
+
# Start a heartbeat monitor in the background.
|
|
20
|
+
# Sets _HEARTBEAT_PID to the background process PID.
|
|
21
|
+
#
|
|
22
|
+
# Arguments:
|
|
23
|
+
# $1 - cli_pid PID of the AI CLI process to monitor
|
|
24
|
+
# $2 - session_log Path to session.log
|
|
25
|
+
# $3 - progress_json Path to progress.json (may not exist if stream-json disabled)
|
|
26
|
+
# $4 - interval Heartbeat interval in seconds
|
|
27
|
+
start_heartbeat() {
|
|
28
|
+
local cli_pid="$1"
|
|
29
|
+
local session_log="$2"
|
|
30
|
+
local progress_json="$3"
|
|
31
|
+
local heartbeat_interval="$4"
|
|
32
|
+
|
|
33
|
+
(
|
|
34
|
+
local elapsed=0
|
|
35
|
+
local prev_size=0
|
|
36
|
+
while kill -0 "$cli_pid" 2>/dev/null; do
|
|
37
|
+
sleep "$heartbeat_interval"
|
|
38
|
+
elapsed=$((elapsed + heartbeat_interval))
|
|
39
|
+
kill -0 "$cli_pid" 2>/dev/null || break
|
|
40
|
+
|
|
41
|
+
# Get log file size
|
|
42
|
+
local cur_size=0
|
|
43
|
+
if [[ -f "$session_log" ]]; then
|
|
44
|
+
cur_size=$(wc -c < "$session_log" 2>/dev/null || echo 0)
|
|
45
|
+
cur_size=$(echo "$cur_size" | tr -d ' ')
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
local growth=$((cur_size - prev_size))
|
|
49
|
+
prev_size=$cur_size
|
|
50
|
+
|
|
51
|
+
local size_display
|
|
52
|
+
if [[ $cur_size -gt 1048576 ]]; then
|
|
53
|
+
size_display="$((cur_size / 1048576))MB"
|
|
54
|
+
elif [[ $cur_size -gt 1024 ]]; then
|
|
55
|
+
size_display="$((cur_size / 1024))KB"
|
|
56
|
+
else
|
|
57
|
+
size_display="${cur_size}B"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
local mins=$((elapsed / 60))
|
|
61
|
+
local secs=$((elapsed % 60))
|
|
62
|
+
|
|
63
|
+
local status_icon
|
|
64
|
+
if [[ $growth -gt 0 ]]; then
|
|
65
|
+
status_icon="${GREEN}▶${NC}"
|
|
66
|
+
else
|
|
67
|
+
status_icon="${YELLOW}⏸${NC}"
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# Try structured progress from progress.json
|
|
71
|
+
if [[ -f "$progress_json" ]]; then
|
|
72
|
+
local phase tool msgs tools_total
|
|
73
|
+
phase=$(python3 -c "
|
|
74
|
+
import json, sys
|
|
75
|
+
try:
|
|
76
|
+
with open('$progress_json') as f:
|
|
77
|
+
d = json.load(f)
|
|
78
|
+
parts = []
|
|
79
|
+
if d.get('current_phase'):
|
|
80
|
+
parts.append('phase: ' + d['current_phase'])
|
|
81
|
+
if d.get('current_tool'):
|
|
82
|
+
parts.append('tool: ' + d['current_tool'])
|
|
83
|
+
parts.append('msgs: ' + str(d.get('message_count', 0)))
|
|
84
|
+
parts.append(str(d.get('total_tool_calls', 0)) + ' tool calls')
|
|
85
|
+
print(' | '.join(parts))
|
|
86
|
+
except Exception:
|
|
87
|
+
sys.exit(1)
|
|
88
|
+
" 2>/dev/null) && {
|
|
89
|
+
echo -e " ${status_icon} ${BLUE}[HEARTBEAT]${NC} ${mins}m${secs}s | log: ${size_display} | ${phase}"
|
|
90
|
+
continue
|
|
91
|
+
}
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# Fallback: tail-based activity detection
|
|
95
|
+
local last_activity=""
|
|
96
|
+
if [[ -f "$session_log" ]]; then
|
|
97
|
+
last_activity=$(tail -20 "$session_log" 2>/dev/null | grep -v '^$' | tail -1 | cut -c1-80 || echo "")
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
echo -e " ${status_icon} ${BLUE}[HEARTBEAT]${NC} ${mins}m${secs}s elapsed | log: ${size_display} (+${growth}B) | ${last_activity}"
|
|
101
|
+
done
|
|
102
|
+
) &
|
|
103
|
+
_HEARTBEAT_PID=$!
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# Stop a heartbeat monitor process.
|
|
107
|
+
#
|
|
108
|
+
# Arguments:
|
|
109
|
+
# $1 - heartbeat_pid PID returned by start_heartbeat
|
|
110
|
+
stop_heartbeat() {
|
|
111
|
+
local heartbeat_pid="$1"
|
|
112
|
+
if [[ -n "$heartbeat_pid" ]]; then
|
|
113
|
+
kill "$heartbeat_pid" 2>/dev/null || true
|
|
114
|
+
wait "$heartbeat_pid" 2>/dev/null || true
|
|
115
|
+
fi
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# Start the stream-json progress parser as a background process.
|
|
119
|
+
# Sets _PARSER_PID to the background process PID.
|
|
120
|
+
# No-op if USE_STREAM_JSON is not "true".
|
|
121
|
+
#
|
|
122
|
+
# Arguments:
|
|
123
|
+
# $1 - session_log Path to session.log
|
|
124
|
+
# $2 - progress_json Path to write progress.json
|
|
125
|
+
# $3 - scripts_dir Path to scripts/ directory
|
|
126
|
+
start_progress_parser() {
|
|
127
|
+
local session_log="$1"
|
|
128
|
+
local progress_json="$2"
|
|
129
|
+
local scripts_dir="$3"
|
|
130
|
+
|
|
131
|
+
_PARSER_PID=""
|
|
132
|
+
|
|
133
|
+
if [[ "${USE_STREAM_JSON:-}" != "true" ]]; then
|
|
134
|
+
return 0
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
local parser_script="$scripts_dir/parse-stream-progress.py"
|
|
138
|
+
if [[ ! -f "$parser_script" ]]; then
|
|
139
|
+
return 0
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
python3 "$parser_script" \
|
|
143
|
+
--session-log "$session_log" \
|
|
144
|
+
--progress-file "$progress_json" &
|
|
145
|
+
_PARSER_PID=$!
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
# Stop the progress parser process.
|
|
149
|
+
#
|
|
150
|
+
# Arguments:
|
|
151
|
+
# $1 - parser_pid PID returned by start_progress_parser
|
|
152
|
+
stop_progress_parser() {
|
|
153
|
+
local parser_pid="$1"
|
|
154
|
+
if [[ -n "$parser_pid" ]]; then
|
|
155
|
+
kill "$parser_pid" 2>/dev/null || true
|
|
156
|
+
wait "$parser_pid" 2>/dev/null || true
|
|
157
|
+
fi
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
# Detect whether the AI CLI supports --output-format stream-json.
|
|
161
|
+
# Sets USE_STREAM_JSON to "true" or "false".
|
|
162
|
+
#
|
|
163
|
+
# Arguments:
|
|
164
|
+
# $1 - cli_cmd The AI CLI command
|
|
165
|
+
detect_stream_json_support() {
|
|
166
|
+
local cli_cmd="$1"
|
|
167
|
+
USE_STREAM_JSON="false"
|
|
168
|
+
|
|
169
|
+
# Try to detect support via --help output
|
|
170
|
+
if "$cli_cmd" --help 2>&1 | grep -q "stream-json" 2>/dev/null; then
|
|
171
|
+
USE_STREAM_JSON="true"
|
|
172
|
+
fi
|
|
173
|
+
}
|
|
@@ -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: "
|
|
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: "
|
|
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
|
|
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
|
package/package.json
CHANGED
package/src/scaffold.js
CHANGED
|
@@ -435,7 +435,7 @@ async function installPipeline(projectRoot, dryRun) {
|
|
|
435
435
|
const items = [
|
|
436
436
|
'run.sh', 'retry-feature.sh', 'reset-feature.sh', 'launch-daemon.sh',
|
|
437
437
|
'run-bugfix.sh', 'retry-bug.sh', 'launch-bugfix-daemon.sh',
|
|
438
|
-
'scripts', 'templates', 'assets', 'README.md', '.gitignore',
|
|
438
|
+
'lib', 'scripts', 'templates', 'assets', 'README.md', '.gitignore',
|
|
439
439
|
];
|
|
440
440
|
|
|
441
441
|
let installedCount = 0;
|