prizmkit 1.0.0 → 1.0.2
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
- package/src/scaffold.js +1 -1
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# ============================================================
|
|
5
|
+
# dev-pipeline/launch-daemon.sh - Daemon wrapper for run.sh
|
|
6
|
+
#
|
|
7
|
+
# Manages run.sh as a background daemon process with PID tracking,
|
|
8
|
+
# log consolidation, and lifecycle commands.
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# ./launch-daemon.sh start [feature-list.json] [--env "KEY=VAL ..."]
|
|
12
|
+
# ./launch-daemon.sh stop
|
|
13
|
+
# ./launch-daemon.sh status
|
|
14
|
+
# ./launch-daemon.sh logs [--lines N] [--follow]
|
|
15
|
+
# ./launch-daemon.sh restart [feature-list.json] [--env "KEY=VAL ..."]
|
|
16
|
+
#
|
|
17
|
+
# Files managed:
|
|
18
|
+
# state/.pipeline.pid - PID of the background run.sh process
|
|
19
|
+
# state/pipeline-daemon.log - Consolidated stdout+stderr from run.sh
|
|
20
|
+
# ============================================================
|
|
21
|
+
|
|
22
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
+
STATE_DIR="$SCRIPT_DIR/state"
|
|
24
|
+
PID_FILE="$STATE_DIR/.pipeline.pid"
|
|
25
|
+
LOG_FILE="$STATE_DIR/pipeline-daemon.log"
|
|
26
|
+
RUN_SCRIPT="$SCRIPT_DIR/run.sh"
|
|
27
|
+
|
|
28
|
+
# Colors
|
|
29
|
+
RED='\033[0;31m'
|
|
30
|
+
GREEN='\033[0;32m'
|
|
31
|
+
YELLOW='\033[1;33m'
|
|
32
|
+
BLUE='\033[0;34m'
|
|
33
|
+
BOLD='\033[1m'
|
|
34
|
+
NC='\033[0m'
|
|
35
|
+
|
|
36
|
+
log_info() { echo -e "${BLUE}[INFO]${NC} $*" >&2; }
|
|
37
|
+
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*" >&2; }
|
|
38
|
+
log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
|
39
|
+
log_success() { echo -e "${GREEN}[OK]${NC} $*" >&2; }
|
|
40
|
+
|
|
41
|
+
# ============================================================
|
|
42
|
+
# Helpers
|
|
43
|
+
# ============================================================
|
|
44
|
+
|
|
45
|
+
# Check if pipeline process is alive
|
|
46
|
+
# Returns 0 if alive, 1 if dead/no PID file
|
|
47
|
+
is_running() {
|
|
48
|
+
if [[ ! -f "$PID_FILE" ]]; then
|
|
49
|
+
return 1
|
|
50
|
+
fi
|
|
51
|
+
local pid
|
|
52
|
+
pid=$(cat "$PID_FILE" 2>/dev/null) || return 1
|
|
53
|
+
if [[ -z "$pid" ]]; then
|
|
54
|
+
return 1
|
|
55
|
+
fi
|
|
56
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
57
|
+
return 0
|
|
58
|
+
else
|
|
59
|
+
return 1
|
|
60
|
+
fi
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Get PID from file (or empty string)
|
|
64
|
+
get_pid() {
|
|
65
|
+
if [[ -f "$PID_FILE" ]]; then
|
|
66
|
+
cat "$PID_FILE" 2>/dev/null || echo ""
|
|
67
|
+
else
|
|
68
|
+
echo ""
|
|
69
|
+
fi
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Clean stale PID file
|
|
73
|
+
clean_stale_pid() {
|
|
74
|
+
if [[ -f "$PID_FILE" ]]; then
|
|
75
|
+
local pid
|
|
76
|
+
pid=$(get_pid)
|
|
77
|
+
if [[ -n "$pid" ]] && ! kill -0 "$pid" 2>/dev/null; then
|
|
78
|
+
rm -f "$PID_FILE"
|
|
79
|
+
log_warn "Cleaned stale PID file (process $pid no longer running)"
|
|
80
|
+
fi
|
|
81
|
+
fi
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# ============================================================
|
|
85
|
+
# start: Launch run.sh in background
|
|
86
|
+
# ============================================================
|
|
87
|
+
|
|
88
|
+
cmd_start() {
|
|
89
|
+
local feature_list=""
|
|
90
|
+
local env_overrides=""
|
|
91
|
+
|
|
92
|
+
# Parse arguments
|
|
93
|
+
while [[ $# -gt 0 ]]; do
|
|
94
|
+
case "$1" in
|
|
95
|
+
--env)
|
|
96
|
+
shift
|
|
97
|
+
if [[ $# -eq 0 ]]; then
|
|
98
|
+
log_error "--env requires a value (e.g. --env \"MAX_RETRIES=5 SESSION_TIMEOUT=3600\")"
|
|
99
|
+
exit 1
|
|
100
|
+
fi
|
|
101
|
+
env_overrides="$1"
|
|
102
|
+
shift
|
|
103
|
+
;;
|
|
104
|
+
*)
|
|
105
|
+
feature_list="$1"
|
|
106
|
+
shift
|
|
107
|
+
;;
|
|
108
|
+
esac
|
|
109
|
+
done
|
|
110
|
+
|
|
111
|
+
# Default feature list
|
|
112
|
+
if [[ -z "$feature_list" ]]; then
|
|
113
|
+
feature_list="feature-list.json"
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# Resolve to absolute path
|
|
117
|
+
if [[ ! "$feature_list" = /* ]]; then
|
|
118
|
+
feature_list="$(cd "$(dirname "$feature_list")" 2>/dev/null && pwd)/$(basename "$feature_list")"
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
# Validate feature list
|
|
122
|
+
if [[ ! -f "$feature_list" ]]; then
|
|
123
|
+
log_error "Feature list not found: $feature_list"
|
|
124
|
+
log_error "Run the app-planner skill first to generate feature-list.json"
|
|
125
|
+
exit 2
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# Validate run.sh exists
|
|
129
|
+
if [[ ! -x "$RUN_SCRIPT" ]]; then
|
|
130
|
+
log_error "run.sh not found or not executable: $RUN_SCRIPT"
|
|
131
|
+
exit 2
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
# Clean stale PID if needed
|
|
135
|
+
clean_stale_pid
|
|
136
|
+
|
|
137
|
+
# Check if already running
|
|
138
|
+
if is_running; then
|
|
139
|
+
local pid
|
|
140
|
+
pid=$(get_pid)
|
|
141
|
+
log_error "Pipeline is already running (PID: $pid)"
|
|
142
|
+
log_error "Use './launch-daemon.sh stop' first, or './launch-daemon.sh restart'"
|
|
143
|
+
exit 1
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# Ensure state directory exists
|
|
147
|
+
mkdir -p "$STATE_DIR"
|
|
148
|
+
|
|
149
|
+
# Build environment prefix
|
|
150
|
+
local env_cmd=""
|
|
151
|
+
if [[ -n "$env_overrides" ]]; then
|
|
152
|
+
env_cmd="env $env_overrides"
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# Record start time
|
|
156
|
+
local start_time
|
|
157
|
+
start_time=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
|
|
158
|
+
|
|
159
|
+
# Launch run.sh in background, fully detached
|
|
160
|
+
log_info "Launching pipeline..."
|
|
161
|
+
log_info "Feature list: $feature_list"
|
|
162
|
+
log_info "Log file: $LOG_FILE"
|
|
163
|
+
if [[ -n "$env_overrides" ]]; then
|
|
164
|
+
log_info "Environment overrides: $env_overrides"
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
# Write a separator to the log file
|
|
168
|
+
{
|
|
169
|
+
echo ""
|
|
170
|
+
echo "================================================================"
|
|
171
|
+
echo " Pipeline Daemon Started: $start_time"
|
|
172
|
+
echo " Feature list: $feature_list"
|
|
173
|
+
if [[ -n "$env_overrides" ]]; then
|
|
174
|
+
echo " Environment: $env_overrides"
|
|
175
|
+
fi
|
|
176
|
+
echo "================================================================"
|
|
177
|
+
echo ""
|
|
178
|
+
} >> "$LOG_FILE"
|
|
179
|
+
|
|
180
|
+
# Launch with nohup + disown for full detachment
|
|
181
|
+
if [[ -n "$env_cmd" ]]; then
|
|
182
|
+
nohup $env_cmd "$RUN_SCRIPT" run "$feature_list" >> "$LOG_FILE" 2>&1 &
|
|
183
|
+
else
|
|
184
|
+
nohup "$RUN_SCRIPT" run "$feature_list" >> "$LOG_FILE" 2>&1 &
|
|
185
|
+
fi
|
|
186
|
+
local pipeline_pid=$!
|
|
187
|
+
disown "$pipeline_pid" 2>/dev/null || true
|
|
188
|
+
|
|
189
|
+
# Write PID file
|
|
190
|
+
echo "$pipeline_pid" > "$PID_FILE"
|
|
191
|
+
|
|
192
|
+
# Write start metadata
|
|
193
|
+
python3 -c "
|
|
194
|
+
import json
|
|
195
|
+
from datetime import datetime
|
|
196
|
+
data = {
|
|
197
|
+
'pid': $pipeline_pid,
|
|
198
|
+
'started_at': '$start_time',
|
|
199
|
+
'feature_list': '$feature_list',
|
|
200
|
+
'env_overrides': '$env_overrides',
|
|
201
|
+
'log_file': '$LOG_FILE'
|
|
202
|
+
}
|
|
203
|
+
with open('$STATE_DIR/.pipeline-meta.json', 'w') as f:
|
|
204
|
+
json.dump(data, f, indent=2)
|
|
205
|
+
" 2>/dev/null || true
|
|
206
|
+
|
|
207
|
+
# Wait briefly and verify
|
|
208
|
+
sleep 2
|
|
209
|
+
if is_running; then
|
|
210
|
+
log_success "Pipeline started successfully (PID: $pipeline_pid)"
|
|
211
|
+
log_info "Monitor logs: ./launch-daemon.sh logs --follow"
|
|
212
|
+
log_info "Check status: ./launch-daemon.sh status"
|
|
213
|
+
|
|
214
|
+
# Output JSON on stdout for programmatic consumption
|
|
215
|
+
echo "{\"success\": true, \"pid\": $pipeline_pid, \"log_file\": \"$LOG_FILE\", \"started_at\": \"$start_time\"}"
|
|
216
|
+
else
|
|
217
|
+
log_error "Pipeline process died immediately after launch"
|
|
218
|
+
log_error "Check log for errors: tail -20 $LOG_FILE"
|
|
219
|
+
rm -f "$PID_FILE"
|
|
220
|
+
exit 1
|
|
221
|
+
fi
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
# ============================================================
|
|
225
|
+
# stop: Gracefully stop the pipeline
|
|
226
|
+
# ============================================================
|
|
227
|
+
|
|
228
|
+
cmd_stop() {
|
|
229
|
+
if [[ ! -f "$PID_FILE" ]]; then
|
|
230
|
+
log_info "Pipeline is not running (no PID file)"
|
|
231
|
+
echo '{"success": true, "message": "not running"}'
|
|
232
|
+
return 0
|
|
233
|
+
fi
|
|
234
|
+
|
|
235
|
+
local pid
|
|
236
|
+
pid=$(get_pid)
|
|
237
|
+
|
|
238
|
+
if [[ -z "$pid" ]]; then
|
|
239
|
+
log_info "Pipeline is not running (empty PID file)"
|
|
240
|
+
rm -f "$PID_FILE"
|
|
241
|
+
echo '{"success": true, "message": "not running"}'
|
|
242
|
+
return 0
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
if ! kill -0 "$pid" 2>/dev/null; then
|
|
246
|
+
log_info "Pipeline is not running (process $pid already exited)"
|
|
247
|
+
rm -f "$PID_FILE"
|
|
248
|
+
echo '{"success": true, "message": "already exited"}'
|
|
249
|
+
return 0
|
|
250
|
+
fi
|
|
251
|
+
|
|
252
|
+
log_info "Stopping pipeline (PID: $pid)..."
|
|
253
|
+
|
|
254
|
+
# Send SIGTERM for graceful shutdown (triggers run.sh cleanup trap)
|
|
255
|
+
kill -TERM "$pid" 2>/dev/null || true
|
|
256
|
+
|
|
257
|
+
# Wait up to 30 seconds for graceful exit
|
|
258
|
+
local waited=0
|
|
259
|
+
while [[ $waited -lt 30 ]]; do
|
|
260
|
+
if ! kill -0 "$pid" 2>/dev/null; then
|
|
261
|
+
break
|
|
262
|
+
fi
|
|
263
|
+
sleep 1
|
|
264
|
+
waited=$((waited + 1))
|
|
265
|
+
done
|
|
266
|
+
|
|
267
|
+
# Force kill if still alive
|
|
268
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
269
|
+
log_warn "Process did not exit after 30s, sending SIGKILL..."
|
|
270
|
+
kill -9 "$pid" 2>/dev/null || true
|
|
271
|
+
sleep 1
|
|
272
|
+
fi
|
|
273
|
+
|
|
274
|
+
rm -f "$PID_FILE"
|
|
275
|
+
|
|
276
|
+
if ! kill -0 "$pid" 2>/dev/null; then
|
|
277
|
+
log_success "Pipeline stopped"
|
|
278
|
+
echo "{\"success\": true, \"pid\": $pid, \"message\": \"stopped\"}"
|
|
279
|
+
else
|
|
280
|
+
log_error "Failed to stop pipeline (PID: $pid)"
|
|
281
|
+
echo "{\"success\": false, \"pid\": $pid, \"message\": \"failed to stop\"}"
|
|
282
|
+
exit 1
|
|
283
|
+
fi
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
# ============================================================
|
|
287
|
+
# status: Check pipeline status
|
|
288
|
+
# ============================================================
|
|
289
|
+
|
|
290
|
+
cmd_status() {
|
|
291
|
+
clean_stale_pid
|
|
292
|
+
|
|
293
|
+
if ! is_running; then
|
|
294
|
+
log_info "Pipeline is not running"
|
|
295
|
+
|
|
296
|
+
# Check if log file exists for last run info
|
|
297
|
+
if [[ -f "$LOG_FILE" ]]; then
|
|
298
|
+
local log_size
|
|
299
|
+
log_size=$(wc -c < "$LOG_FILE" 2>/dev/null | tr -d ' ')
|
|
300
|
+
log_info "Last log: $LOG_FILE ($((log_size / 1024))KB)"
|
|
301
|
+
fi
|
|
302
|
+
|
|
303
|
+
# Show feature-level progress from last run if metadata exists
|
|
304
|
+
if [[ -f "$STATE_DIR/.pipeline-meta.json" ]]; then
|
|
305
|
+
local last_feature_list
|
|
306
|
+
last_feature_list=$(python3 -c "
|
|
307
|
+
import json
|
|
308
|
+
with open('$STATE_DIR/.pipeline-meta.json') as f:
|
|
309
|
+
print(json.load(f).get('feature_list', ''))
|
|
310
|
+
" 2>/dev/null || echo "")
|
|
311
|
+
|
|
312
|
+
if [[ -n "$last_feature_list" && -f "$last_feature_list" ]]; then
|
|
313
|
+
echo "" >&2
|
|
314
|
+
log_info "Last run feature progress:"
|
|
315
|
+
python3 "$SCRIPT_DIR/scripts/update-feature-status.py" \
|
|
316
|
+
--feature-list "$last_feature_list" \
|
|
317
|
+
--state-dir "$STATE_DIR" \
|
|
318
|
+
--action status >&2 2>/dev/null || true
|
|
319
|
+
echo "" >&2
|
|
320
|
+
fi
|
|
321
|
+
fi
|
|
322
|
+
|
|
323
|
+
echo '{"running": false}'
|
|
324
|
+
return 1
|
|
325
|
+
fi
|
|
326
|
+
|
|
327
|
+
local pid
|
|
328
|
+
pid=$(get_pid)
|
|
329
|
+
|
|
330
|
+
# Gather metadata
|
|
331
|
+
local log_size_kb=0
|
|
332
|
+
if [[ -f "$LOG_FILE" ]]; then
|
|
333
|
+
local log_size
|
|
334
|
+
log_size=$(wc -c < "$LOG_FILE" 2>/dev/null | tr -d ' ')
|
|
335
|
+
log_size_kb=$((log_size / 1024))
|
|
336
|
+
fi
|
|
337
|
+
|
|
338
|
+
local started_at=""
|
|
339
|
+
local feature_list_path=""
|
|
340
|
+
if [[ -f "$STATE_DIR/.pipeline-meta.json" ]]; then
|
|
341
|
+
started_at=$(python3 -c "
|
|
342
|
+
import json
|
|
343
|
+
with open('$STATE_DIR/.pipeline-meta.json') as f:
|
|
344
|
+
print(json.load(f).get('started_at', ''))
|
|
345
|
+
" 2>/dev/null || echo "")
|
|
346
|
+
feature_list_path=$(python3 -c "
|
|
347
|
+
import json
|
|
348
|
+
with open('$STATE_DIR/.pipeline-meta.json') as f:
|
|
349
|
+
print(json.load(f).get('feature_list', ''))
|
|
350
|
+
" 2>/dev/null || echo "")
|
|
351
|
+
fi
|
|
352
|
+
|
|
353
|
+
log_success "Pipeline is running (PID: $pid)"
|
|
354
|
+
if [[ -n "$started_at" ]]; then
|
|
355
|
+
log_info "Started at: $started_at"
|
|
356
|
+
fi
|
|
357
|
+
if [[ -n "$feature_list_path" ]]; then
|
|
358
|
+
log_info "Feature list: $feature_list_path"
|
|
359
|
+
fi
|
|
360
|
+
log_info "Log file: $LOG_FILE (${log_size_kb}KB)"
|
|
361
|
+
|
|
362
|
+
# Show feature-level progress if feature list is available
|
|
363
|
+
if [[ -n "$feature_list_path" && -f "$feature_list_path" ]]; then
|
|
364
|
+
echo "" >&2
|
|
365
|
+
python3 "$SCRIPT_DIR/scripts/update-feature-status.py" \
|
|
366
|
+
--feature-list "$feature_list_path" \
|
|
367
|
+
--state-dir "$STATE_DIR" \
|
|
368
|
+
--action status >&2 2>/dev/null || true
|
|
369
|
+
echo "" >&2
|
|
370
|
+
fi
|
|
371
|
+
|
|
372
|
+
# Show last few log lines
|
|
373
|
+
if [[ -f "$LOG_FILE" ]]; then
|
|
374
|
+
log_info "--- Last 5 log lines ---"
|
|
375
|
+
tail -5 "$LOG_FILE" >&2 || true
|
|
376
|
+
echo "" >&2
|
|
377
|
+
fi
|
|
378
|
+
|
|
379
|
+
# JSON output on stdout (enhanced with progress info)
|
|
380
|
+
local progress_json=""
|
|
381
|
+
if [[ -n "$feature_list_path" && -f "$feature_list_path" ]]; then
|
|
382
|
+
progress_json=$(python3 -c "
|
|
383
|
+
import json, sys, os
|
|
384
|
+
sys.path.insert(0, '$SCRIPT_DIR/scripts')
|
|
385
|
+
from datetime import datetime
|
|
386
|
+
|
|
387
|
+
def load_json(p):
|
|
388
|
+
with open(p, 'r') as f:
|
|
389
|
+
return json.load(f)
|
|
390
|
+
|
|
391
|
+
fl = load_json('$feature_list_path')
|
|
392
|
+
features = fl.get('features', [])
|
|
393
|
+
total = len(features)
|
|
394
|
+
counts = {'completed': 0, 'in_progress': 0, 'failed': 0, 'pending': 0, 'skipped': 0}
|
|
395
|
+
for feat in features:
|
|
396
|
+
fid = feat.get('id', '')
|
|
397
|
+
sp = os.path.join('$STATE_DIR', 'features', fid, 'status.json')
|
|
398
|
+
if os.path.isfile(sp):
|
|
399
|
+
fs = load_json(sp)
|
|
400
|
+
st = fs.get('status', 'pending')
|
|
401
|
+
else:
|
|
402
|
+
st = 'pending'
|
|
403
|
+
if st in counts:
|
|
404
|
+
counts[st] += 1
|
|
405
|
+
else:
|
|
406
|
+
counts['pending'] += 1
|
|
407
|
+
|
|
408
|
+
pct = round(counts['completed'] / total * 100, 1) if total > 0 else 0
|
|
409
|
+
print(json.dumps({
|
|
410
|
+
'total': total,
|
|
411
|
+
'completed': counts['completed'],
|
|
412
|
+
'in_progress': counts['in_progress'],
|
|
413
|
+
'failed': counts['failed'],
|
|
414
|
+
'pending': counts['pending'],
|
|
415
|
+
'percent': pct
|
|
416
|
+
}))
|
|
417
|
+
" 2>/dev/null || echo "")
|
|
418
|
+
fi
|
|
419
|
+
|
|
420
|
+
if [[ -n "$progress_json" ]]; then
|
|
421
|
+
# Merge progress into the main JSON output
|
|
422
|
+
cat <<EOF
|
|
423
|
+
{"running": true, "pid": $pid, "log_file": "$LOG_FILE", "log_size_kb": $log_size_kb, "started_at": "$started_at", "feature_list": "$feature_list_path", "progress": $progress_json}
|
|
424
|
+
EOF
|
|
425
|
+
else
|
|
426
|
+
cat <<EOF
|
|
427
|
+
{"running": true, "pid": $pid, "log_file": "$LOG_FILE", "log_size_kb": $log_size_kb, "started_at": "$started_at", "feature_list": "$feature_list_path"}
|
|
428
|
+
EOF
|
|
429
|
+
fi
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
# ============================================================
|
|
433
|
+
# logs: View or follow pipeline logs
|
|
434
|
+
# ============================================================
|
|
435
|
+
|
|
436
|
+
cmd_logs() {
|
|
437
|
+
local lines=50
|
|
438
|
+
local follow=false
|
|
439
|
+
|
|
440
|
+
while [[ $# -gt 0 ]]; do
|
|
441
|
+
case "$1" in
|
|
442
|
+
--lines|-n)
|
|
443
|
+
shift
|
|
444
|
+
if [[ $# -eq 0 ]]; then
|
|
445
|
+
log_error "--lines requires a number"
|
|
446
|
+
exit 1
|
|
447
|
+
fi
|
|
448
|
+
lines="$1"
|
|
449
|
+
shift
|
|
450
|
+
;;
|
|
451
|
+
--follow|-f)
|
|
452
|
+
follow=true
|
|
453
|
+
shift
|
|
454
|
+
;;
|
|
455
|
+
*)
|
|
456
|
+
log_error "Unknown option: $1"
|
|
457
|
+
exit 1
|
|
458
|
+
;;
|
|
459
|
+
esac
|
|
460
|
+
done
|
|
461
|
+
|
|
462
|
+
if [[ ! -f "$LOG_FILE" ]]; then
|
|
463
|
+
log_info "No log file found at $LOG_FILE"
|
|
464
|
+
log_info "Pipeline has not been started yet"
|
|
465
|
+
exit 0
|
|
466
|
+
fi
|
|
467
|
+
|
|
468
|
+
if [[ "$follow" == true ]]; then
|
|
469
|
+
log_info "Following $LOG_FILE (Ctrl+C to stop)..."
|
|
470
|
+
echo "" >&2
|
|
471
|
+
tail -f "$LOG_FILE"
|
|
472
|
+
else
|
|
473
|
+
tail -"$lines" "$LOG_FILE"
|
|
474
|
+
fi
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
# ============================================================
|
|
478
|
+
# restart: Stop + Start
|
|
479
|
+
# ============================================================
|
|
480
|
+
|
|
481
|
+
cmd_restart() {
|
|
482
|
+
cmd_stop 2>/dev/null || true
|
|
483
|
+
sleep 1
|
|
484
|
+
cmd_start "$@"
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
# ============================================================
|
|
488
|
+
# Entry point
|
|
489
|
+
# ============================================================
|
|
490
|
+
|
|
491
|
+
show_help() {
|
|
492
|
+
cat <<'HELP'
|
|
493
|
+
Usage: launch-daemon.sh <command> [options]
|
|
494
|
+
|
|
495
|
+
Commands:
|
|
496
|
+
start [feature-list.json] [--env "K=V ..."] Start pipeline in background
|
|
497
|
+
stop Gracefully stop pipeline
|
|
498
|
+
status Check if pipeline is running
|
|
499
|
+
logs [--lines N] [--follow] View pipeline logs
|
|
500
|
+
restart [feature-list.json] [--env "K=V ..."] Stop + start pipeline
|
|
501
|
+
help Show this help
|
|
502
|
+
|
|
503
|
+
Examples:
|
|
504
|
+
./launch-daemon.sh start # Start with default feature-list.json
|
|
505
|
+
./launch-daemon.sh start my-features.json # Start with custom feature list
|
|
506
|
+
./launch-daemon.sh start --env "MAX_RETRIES=5 SESSION_TIMEOUT=7200"
|
|
507
|
+
./launch-daemon.sh status # Check if running (JSON on stdout)
|
|
508
|
+
./launch-daemon.sh logs --follow # Live log tailing
|
|
509
|
+
./launch-daemon.sh logs --lines 100 # Last 100 lines
|
|
510
|
+
./launch-daemon.sh stop # Graceful shutdown
|
|
511
|
+
./launch-daemon.sh restart # Stop + start
|
|
512
|
+
|
|
513
|
+
Environment Variables (pass via --env):
|
|
514
|
+
MAX_RETRIES Max retries per feature (default: 3)
|
|
515
|
+
SESSION_TIMEOUT Session timeout in seconds (default: 0 = no limit)
|
|
516
|
+
VERBOSE Set to 1 for verbose AI CLI output
|
|
517
|
+
HEARTBEAT_INTERVAL Heartbeat log interval in seconds (default: 30)
|
|
518
|
+
HELP
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
case "${1:-help}" in
|
|
522
|
+
start)
|
|
523
|
+
shift
|
|
524
|
+
cmd_start "$@"
|
|
525
|
+
;;
|
|
526
|
+
stop)
|
|
527
|
+
cmd_stop
|
|
528
|
+
;;
|
|
529
|
+
status)
|
|
530
|
+
cmd_status
|
|
531
|
+
;;
|
|
532
|
+
logs|log)
|
|
533
|
+
shift
|
|
534
|
+
cmd_logs "$@"
|
|
535
|
+
;;
|
|
536
|
+
restart)
|
|
537
|
+
shift
|
|
538
|
+
cmd_restart "$@"
|
|
539
|
+
;;
|
|
540
|
+
help|--help|-h)
|
|
541
|
+
show_help
|
|
542
|
+
;;
|
|
543
|
+
*)
|
|
544
|
+
log_error "Unknown command: $1"
|
|
545
|
+
echo "" >&2
|
|
546
|
+
show_help
|
|
547
|
+
exit 1
|
|
548
|
+
;;
|
|
549
|
+
esac
|