prizmkit 1.0.8 → 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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "frameworkVersion": "1.0.8",
3
- "bundledAt": "2026-03-11T17:33:39.633Z",
4
- "bundledFrom": "66dd874"
2
+ "frameworkVersion": "1.0.9",
3
+ "bundledAt": "2026-03-11T17:48:07.887Z",
4
+ "bundledFrom": "41d8286"
5
5
  }
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prizmkit",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
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": {
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;