prizmkit 1.0.13 → 1.0.14
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/bin/create-prizmkit.js +4 -1
- package/bundled/VERSION.json +3 -3
- package/bundled/adapters/claude/command-adapter.js +35 -4
- package/bundled/adapters/claude/rules-adapter.js +6 -58
- package/bundled/adapters/claude/team-adapter.js +2 -2
- package/bundled/adapters/codebuddy/agent-adapter.js +0 -1
- package/bundled/adapters/codebuddy/rules-adapter.js +30 -0
- package/bundled/adapters/shared/frontmatter.js +3 -1
- package/bundled/dev-pipeline/README.md +13 -3
- package/bundled/dev-pipeline/launch-bugfix-daemon.sh +10 -0
- package/bundled/dev-pipeline/launch-daemon.sh +18 -4
- package/bundled/dev-pipeline/lib/common.sh +105 -0
- package/bundled/dev-pipeline/run-bugfix.sh +57 -57
- package/bundled/dev-pipeline/run.sh +75 -59
- package/bundled/dev-pipeline/scripts/check-session-status.py +47 -2
- package/bundled/dev-pipeline/scripts/cleanup-logs.py +192 -0
- package/bundled/dev-pipeline/scripts/detect-stuck.py +15 -3
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +32 -27
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +23 -23
- package/bundled/dev-pipeline/scripts/update-feature-status.py +50 -2
- package/bundled/dev-pipeline/scripts/utils.py +22 -0
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +18 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +19 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +18 -2
- package/bundled/dev-pipeline/templates/session-status-schema.json +7 -1
- package/bundled/dev-pipeline/tests/__init__.py +0 -0
- package/bundled/dev-pipeline/tests/conftest.py +133 -0
- package/bundled/dev-pipeline/tests/test_check_session.py +127 -0
- package/bundled/dev-pipeline/tests/test_cleanup_logs.py +119 -0
- package/bundled/dev-pipeline/tests/test_detect_stuck.py +207 -0
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +181 -0
- package/bundled/dev-pipeline/tests/test_generate_prompt.py +190 -0
- package/bundled/dev-pipeline/tests/test_init_bugfix_pipeline.py +153 -0
- package/bundled/dev-pipeline/tests/test_init_pipeline.py +241 -0
- package/bundled/dev-pipeline/tests/test_update_bug_status.py +142 -0
- package/bundled/dev-pipeline/tests/test_update_feature_status.py +277 -0
- package/bundled/dev-pipeline/tests/test_utils.py +141 -0
- package/bundled/rules/USAGE.md +153 -0
- package/bundled/rules/_rules-metadata.json +43 -0
- package/bundled/rules/general/prefer-linux-commands.md +9 -0
- package/bundled/rules/prizm/prizm-commit-workflow.md +10 -0
- package/bundled/rules/prizm/prizm-documentation.md +19 -0
- package/bundled/rules/prizm/prizm-progressive-loading.md +11 -0
- package/bundled/skills/_metadata.json +130 -67
- package/bundled/skills/app-planner/SKILL.md +252 -499
- package/bundled/skills/app-planner/assets/evaluation-guide.md +44 -0
- package/bundled/skills/app-planner/scripts/validate-and-generate.py +143 -4
- package/bundled/skills/bug-planner/SKILL.md +58 -13
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +5 -7
- package/bundled/skills/dev-pipeline-launcher/SKILL.md +16 -7
- package/bundled/skills/feature-workflow/SKILL.md +175 -234
- package/bundled/skills/prizm-kit/SKILL.md +17 -31
- package/bundled/skills/{prizmkit-adr-manager → prizmkit-tool-adr-manager}/SKILL.md +6 -7
- package/bundled/skills/{prizmkit-api-doc-generator → prizmkit-tool-api-doc-generator}/SKILL.md +4 -5
- package/bundled/skills/{prizmkit-bug-reproducer → prizmkit-tool-bug-reproducer}/SKILL.md +4 -5
- package/bundled/skills/{prizmkit-ci-cd-generator → prizmkit-tool-ci-cd-generator}/SKILL.md +4 -5
- package/bundled/skills/{prizmkit-db-migration → prizmkit-tool-db-migration}/SKILL.md +4 -5
- package/bundled/skills/{prizmkit-dependency-health → prizmkit-tool-dependency-health}/SKILL.md +3 -4
- package/bundled/skills/{prizmkit-deployment-strategy → prizmkit-tool-deployment-strategy}/SKILL.md +4 -5
- package/bundled/skills/{prizmkit-error-triage → prizmkit-tool-error-triage}/SKILL.md +4 -5
- package/bundled/skills/{prizmkit-log-analyzer → prizmkit-tool-log-analyzer}/SKILL.md +4 -5
- package/bundled/skills/{prizmkit-monitoring-setup → prizmkit-tool-monitoring-setup}/SKILL.md +4 -5
- package/bundled/skills/{prizmkit-onboarding-generator → prizmkit-tool-onboarding-generator}/SKILL.md +4 -5
- package/bundled/skills/{prizmkit-perf-profiler → prizmkit-tool-perf-profiler}/SKILL.md +4 -5
- package/bundled/skills/{prizmkit-security-audit → prizmkit-tool-security-audit}/SKILL.md +3 -4
- package/bundled/skills/{prizmkit-tech-debt-tracker → prizmkit-tool-tech-debt-tracker}/SKILL.md +3 -4
- package/bundled/skills/refactor-skill/SKILL.md +371 -0
- package/bundled/skills/refactor-workflow/SKILL.md +17 -119
- package/package.json +1 -1
- package/src/external-skills.js +71 -0
- package/src/index.js +62 -4
- package/src/metadata.js +36 -0
- package/src/scaffold.js +136 -32
- package/bundled/skills/prizmkit-bug-fix-workflow/SKILL.md +0 -356
- package/bundled/templates/claude-md-template.md +0 -38
- package/bundled/templates/codebuddy-md-template.md +0 -35
- /package/bundled/skills/{prizmkit-adr-manager → prizmkit-tool-adr-manager}/assets/adr-template.md +0 -0
|
@@ -17,9 +17,14 @@ set -euo pipefail
|
|
|
17
17
|
# MAX_RETRIES Max retries per bug (default: 3)
|
|
18
18
|
# SESSION_TIMEOUT Session timeout in seconds (default: 0 = no limit)
|
|
19
19
|
# AI_CLI AI CLI command name (auto-detected: cbc or claude)
|
|
20
|
+
# CODEBUDDY_CLI Legacy alias for AI_CLI (deprecated, use AI_CLI instead)
|
|
21
|
+
# PRIZMKIT_PLATFORM Force platform: 'codebuddy' or 'claude' (auto-detected)
|
|
20
22
|
# VERBOSE Set to 1 to enable --verbose on AI CLI
|
|
21
23
|
# HEARTBEAT_INTERVAL Heartbeat log interval in seconds (default: 30)
|
|
22
24
|
# HEARTBEAT_STALE_THRESHOLD Heartbeat stale threshold in seconds (default: 600)
|
|
25
|
+
# LOG_CLEANUP_ENABLED Run periodic log cleanup (default: 1)
|
|
26
|
+
# LOG_RETENTION_DAYS Delete logs older than N days (default: 14)
|
|
27
|
+
# LOG_MAX_TOTAL_MB Keep total logs under N MB via oldest-first cleanup (default: 1024)
|
|
23
28
|
# ============================================================
|
|
24
29
|
|
|
25
30
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -31,31 +36,14 @@ MAX_RETRIES=${MAX_RETRIES:-3}
|
|
|
31
36
|
SESSION_TIMEOUT=${SESSION_TIMEOUT:-0}
|
|
32
37
|
HEARTBEAT_STALE_THRESHOLD=${HEARTBEAT_STALE_THRESHOLD:-600}
|
|
33
38
|
HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-30}
|
|
39
|
+
LOG_CLEANUP_ENABLED=${LOG_CLEANUP_ENABLED:-1}
|
|
40
|
+
LOG_RETENTION_DAYS=${LOG_RETENTION_DAYS:-14}
|
|
41
|
+
LOG_MAX_TOTAL_MB=${LOG_MAX_TOTAL_MB:-1024}
|
|
34
42
|
VERBOSE=${VERBOSE:-0}
|
|
35
43
|
|
|
36
|
-
#
|
|
37
|
-
|
|
38
|
-
|
|
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"
|
|
44
|
+
# Source shared common helpers (CLI/platform detection + logs + deps)
|
|
45
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
46
|
+
prizm_detect_cli_and_platform
|
|
59
47
|
|
|
60
48
|
# Source shared heartbeat library
|
|
61
49
|
source "$SCRIPT_DIR/lib/heartbeat.sh"
|
|
@@ -66,20 +54,6 @@ detect_stream_json_support "$CLI_CMD"
|
|
|
66
54
|
# Bug list path (set in main, used by cleanup trap)
|
|
67
55
|
BUG_LIST=""
|
|
68
56
|
|
|
69
|
-
# Colors
|
|
70
|
-
RED='\033[0;31m'
|
|
71
|
-
GREEN='\033[0;32m'
|
|
72
|
-
YELLOW='\033[1;33m'
|
|
73
|
-
BLUE='\033[0;34m'
|
|
74
|
-
MAGENTA='\033[0;35m'
|
|
75
|
-
BOLD='\033[1m'
|
|
76
|
-
NC='\033[0m'
|
|
77
|
-
|
|
78
|
-
log_info() { echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
79
|
-
log_warn() { echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
80
|
-
log_error() { echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
81
|
-
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
82
|
-
|
|
83
57
|
# ============================================================
|
|
84
58
|
# Shared: Spawn AI CLI session and wait for result
|
|
85
59
|
# ============================================================
|
|
@@ -234,13 +208,29 @@ trap cleanup SIGINT SIGTERM
|
|
|
234
208
|
# ============================================================
|
|
235
209
|
|
|
236
210
|
check_dependencies() {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
211
|
+
prizm_check_common_dependencies "$CLI_CMD"
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
run_log_cleanup() {
|
|
215
|
+
if [[ "$LOG_CLEANUP_ENABLED" != "1" ]]; then
|
|
216
|
+
return 0
|
|
240
217
|
fi
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
218
|
+
|
|
219
|
+
local cleanup_result
|
|
220
|
+
cleanup_result=$(python3 "$SCRIPTS_DIR/cleanup-logs.py" \
|
|
221
|
+
--state-dir "$STATE_DIR" \
|
|
222
|
+
--retention-days "$LOG_RETENTION_DAYS" \
|
|
223
|
+
--max-total-mb "$LOG_MAX_TOTAL_MB" 2>/dev/null) || {
|
|
224
|
+
log_warn "Log cleanup failed (continuing)"
|
|
225
|
+
return 0
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
local deleted reclaimed_kb
|
|
229
|
+
deleted=$(echo "$cleanup_result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('deleted_files', 0))" 2>/dev/null || echo "0")
|
|
230
|
+
reclaimed_kb=$(echo "$cleanup_result" | python3 -c "import sys,json; print(int(json.load(sys.stdin).get('reclaimed_bytes', 0)/1024))" 2>/dev/null || echo "0")
|
|
231
|
+
|
|
232
|
+
if [[ "$deleted" -gt 0 ]]; then
|
|
233
|
+
log_info "Log cleanup: deleted $deleted files, reclaimed ${reclaimed_kb}KB"
|
|
244
234
|
fi
|
|
245
235
|
}
|
|
246
236
|
|
|
@@ -283,6 +273,7 @@ run_one() {
|
|
|
283
273
|
fi
|
|
284
274
|
|
|
285
275
|
check_dependencies
|
|
276
|
+
run_log_cleanup
|
|
286
277
|
|
|
287
278
|
# Initialize state if needed
|
|
288
279
|
if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
|
|
@@ -299,14 +290,14 @@ run_one() {
|
|
|
299
290
|
local bug_title
|
|
300
291
|
bug_title=$(python3 -c "
|
|
301
292
|
import json, sys
|
|
302
|
-
with open(
|
|
293
|
+
with open(sys.argv[1]) as f:
|
|
303
294
|
data = json.load(f)
|
|
304
295
|
for bug in data.get('bugs', []):
|
|
305
|
-
if bug.get('id') ==
|
|
296
|
+
if bug.get('id') == sys.argv[2]:
|
|
306
297
|
print(bug.get('title', ''))
|
|
307
298
|
sys.exit(0)
|
|
308
299
|
sys.exit(1)
|
|
309
|
-
" 2>/dev/null) || {
|
|
300
|
+
" "$bug_list" "$bug_id" 2>/dev/null) || {
|
|
310
301
|
log_error "Bug $bug_id not found in $bug_list"
|
|
311
302
|
exit 1
|
|
312
303
|
}
|
|
@@ -314,14 +305,14 @@ sys.exit(1)
|
|
|
314
305
|
local bug_severity
|
|
315
306
|
bug_severity=$(python3 -c "
|
|
316
307
|
import json, sys
|
|
317
|
-
with open(
|
|
308
|
+
with open(sys.argv[1]) as f:
|
|
318
309
|
data = json.load(f)
|
|
319
310
|
for bug in data.get('bugs', []):
|
|
320
|
-
if bug.get('id') ==
|
|
311
|
+
if bug.get('id') == sys.argv[2]:
|
|
321
312
|
print(bug.get('severity', 'medium'))
|
|
322
313
|
sys.exit(0)
|
|
323
314
|
sys.exit(1)
|
|
324
|
-
" 2>/dev/null) || bug_severity="medium"
|
|
315
|
+
" "$bug_list" "$bug_id" 2>/dev/null) || bug_severity="medium"
|
|
325
316
|
|
|
326
317
|
# Reset bug status
|
|
327
318
|
python3 "$SCRIPTS_DIR/update-bug-status.py" \
|
|
@@ -376,14 +367,14 @@ sys.exit(1)
|
|
|
376
367
|
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
377
368
|
echo ""
|
|
378
369
|
|
|
379
|
-
|
|
370
|
+
cleanup_single_bug() {
|
|
380
371
|
echo ""
|
|
381
372
|
log_warn "Interrupted. Killing session..."
|
|
382
373
|
kill 0 2>/dev/null || true
|
|
383
374
|
log_info "Session log: $session_dir/logs/session.log"
|
|
384
375
|
exit 130
|
|
385
376
|
}
|
|
386
|
-
trap
|
|
377
|
+
trap cleanup_single_bug SIGINT SIGTERM
|
|
387
378
|
|
|
388
379
|
_SPAWN_RESULT=""
|
|
389
380
|
spawn_and_wait_session \
|
|
@@ -424,6 +415,7 @@ main() {
|
|
|
424
415
|
fi
|
|
425
416
|
|
|
426
417
|
check_dependencies
|
|
418
|
+
run_log_cleanup
|
|
427
419
|
|
|
428
420
|
# Initialize pipeline state if needed
|
|
429
421
|
if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
|
|
@@ -482,6 +474,7 @@ main() {
|
|
|
482
474
|
log_success " All bugs processed! Bug fix pipeline finished."
|
|
483
475
|
log_success " Total sessions: $session_count"
|
|
484
476
|
log_success "════════════════════════════════════════════════════"
|
|
477
|
+
rm -f "$STATE_DIR/current-session.json"
|
|
485
478
|
break
|
|
486
479
|
fi
|
|
487
480
|
|
|
@@ -529,18 +522,22 @@ main() {
|
|
|
529
522
|
--state-dir "$STATE_DIR" \
|
|
530
523
|
--output "$bootstrap_prompt" >/dev/null 2>&1
|
|
531
524
|
|
|
532
|
-
# Track current session
|
|
525
|
+
# Track current session (atomic write via temp file)
|
|
533
526
|
python3 -c "
|
|
534
|
-
import json
|
|
527
|
+
import json, sys, os
|
|
535
528
|
from datetime import datetime
|
|
529
|
+
bug_id, session_id, state_dir = sys.argv[1], sys.argv[2], sys.argv[3]
|
|
536
530
|
data = {
|
|
537
|
-
'bug_id':
|
|
538
|
-
'session_id':
|
|
531
|
+
'bug_id': bug_id,
|
|
532
|
+
'session_id': session_id,
|
|
539
533
|
'started_at': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
|
|
540
534
|
}
|
|
541
|
-
|
|
535
|
+
target = os.path.join(state_dir, 'current-session.json')
|
|
536
|
+
tmp = target + '.tmp'
|
|
537
|
+
with open(tmp, 'w') as f:
|
|
542
538
|
json.dump(data, f, indent=2)
|
|
543
|
-
|
|
539
|
+
os.replace(tmp, target)
|
|
540
|
+
" "$bug_id" "$session_id" "$STATE_DIR"
|
|
544
541
|
|
|
545
542
|
# Spawn session
|
|
546
543
|
log_info "Spawning AI CLI session: $session_id"
|
|
@@ -580,6 +577,9 @@ show_help() {
|
|
|
580
577
|
echo " AI_CLI AI CLI command name (auto-detected: cbc or claude)"
|
|
581
578
|
echo " VERBOSE Set to 1 for verbose AI CLI output"
|
|
582
579
|
echo " HEARTBEAT_INTERVAL Heartbeat log interval in seconds (default: 30)"
|
|
580
|
+
echo " LOG_CLEANUP_ENABLED Run log cleanup before execution (default: 1)"
|
|
581
|
+
echo " LOG_RETENTION_DAYS Delete logs older than N days (default: 14)"
|
|
582
|
+
echo " LOG_MAX_TOTAL_MB Keep total logs under N MB (default: 1024)"
|
|
583
583
|
echo ""
|
|
584
584
|
echo "Examples:"
|
|
585
585
|
echo " ./run-bugfix.sh run # Run all bugs"
|
|
@@ -17,12 +17,15 @@ set -euo pipefail
|
|
|
17
17
|
# Environment Variables:
|
|
18
18
|
# MAX_RETRIES Max retries per feature (default: 3)
|
|
19
19
|
# SESSION_TIMEOUT Session timeout in seconds (default: 0 = no limit)
|
|
20
|
-
# AI_CLI AI CLI command name (
|
|
20
|
+
# AI_CLI AI CLI command name (override; also readable from .prizmkit/config.json)
|
|
21
21
|
# CODEBUDDY_CLI Legacy alias for AI_CLI (deprecated, use AI_CLI instead)
|
|
22
22
|
# PRIZMKIT_PLATFORM Force platform: 'codebuddy' or 'claude' (auto-detected)
|
|
23
23
|
# VERBOSE Set to 1 to enable --verbose on AI CLI (shows subagent output)
|
|
24
24
|
# HEARTBEAT_INTERVAL Heartbeat log interval in seconds (default: 30)
|
|
25
25
|
# HEARTBEAT_STALE_THRESHOLD Heartbeat stale threshold in seconds (default: 600)
|
|
26
|
+
# LOG_CLEANUP_ENABLED Run periodic log cleanup (default: 1)
|
|
27
|
+
# LOG_RETENTION_DAYS Delete logs older than N days (default: 14)
|
|
28
|
+
# LOG_MAX_TOTAL_MB Keep total logs under N MB via oldest-first cleanup (default: 1024)
|
|
26
29
|
# ============================================================
|
|
27
30
|
|
|
28
31
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
@@ -34,32 +37,14 @@ MAX_RETRIES=${MAX_RETRIES:-3}
|
|
|
34
37
|
SESSION_TIMEOUT=${SESSION_TIMEOUT:-0}
|
|
35
38
|
HEARTBEAT_STALE_THRESHOLD=${HEARTBEAT_STALE_THRESHOLD:-600}
|
|
36
39
|
HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-30}
|
|
40
|
+
LOG_CLEANUP_ENABLED=${LOG_CLEANUP_ENABLED:-1}
|
|
41
|
+
LOG_RETENTION_DAYS=${LOG_RETENTION_DAYS:-14}
|
|
42
|
+
LOG_MAX_TOTAL_MB=${LOG_MAX_TOTAL_MB:-1024}
|
|
37
43
|
VERBOSE=${VERBOSE:-0}
|
|
38
44
|
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
elif [[ -n "${CODEBUDDY_CLI:-}" ]]; then
|
|
43
|
-
CLI_CMD="$CODEBUDDY_CLI"
|
|
44
|
-
elif command -v cbc &>/dev/null; then
|
|
45
|
-
CLI_CMD="cbc"
|
|
46
|
-
elif command -v claude &>/dev/null; then
|
|
47
|
-
CLI_CMD="claude"
|
|
48
|
-
else
|
|
49
|
-
echo "ERROR: No AI CLI found. Install CodeBuddy (cbc) or Claude Code (claude)." >&2
|
|
50
|
-
exit 1
|
|
51
|
-
fi
|
|
52
|
-
|
|
53
|
-
# Platform detection
|
|
54
|
-
if [[ -n "${PRIZMKIT_PLATFORM:-}" ]]; then
|
|
55
|
-
PLATFORM="$PRIZMKIT_PLATFORM"
|
|
56
|
-
elif [[ "$CLI_CMD" == *"claude"* ]]; then
|
|
57
|
-
PLATFORM="claude"
|
|
58
|
-
else
|
|
59
|
-
PLATFORM="codebuddy"
|
|
60
|
-
fi
|
|
61
|
-
|
|
62
|
-
export PRIZMKIT_PLATFORM="$PLATFORM"
|
|
45
|
+
# Source shared common helpers (CLI/platform detection + logs + deps)
|
|
46
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
47
|
+
prizm_detect_cli_and_platform
|
|
63
48
|
|
|
64
49
|
# Source shared heartbeat library
|
|
65
50
|
source "$SCRIPT_DIR/lib/heartbeat.sh"
|
|
@@ -70,19 +55,6 @@ detect_stream_json_support "$CLI_CMD"
|
|
|
70
55
|
# Feature list path (set in main, used by cleanup trap)
|
|
71
56
|
FEATURE_LIST=""
|
|
72
57
|
|
|
73
|
-
# Colors for output
|
|
74
|
-
RED='\033[0;31m'
|
|
75
|
-
GREEN='\033[0;32m'
|
|
76
|
-
YELLOW='\033[1;33m'
|
|
77
|
-
BLUE='\033[0;34m'
|
|
78
|
-
BOLD='\033[1m'
|
|
79
|
-
NC='\033[0m'
|
|
80
|
-
|
|
81
|
-
log_info() { echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
82
|
-
log_warn() { echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
83
|
-
log_error() { echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
84
|
-
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
85
|
-
|
|
86
58
|
# ============================================================
|
|
87
59
|
# Shared: Spawn an AI CLI session and wait for result
|
|
88
60
|
# ============================================================
|
|
@@ -206,13 +178,42 @@ spawn_and_wait_session() {
|
|
|
206
178
|
local dirty_files=""
|
|
207
179
|
dirty_files=$(git -C "$project_root" status --porcelain 2>/dev/null || true)
|
|
208
180
|
if [[ -n "$dirty_files" ]]; then
|
|
209
|
-
|
|
181
|
+
log_warn "Session reported success but git working tree is not clean."
|
|
210
182
|
echo "$dirty_files" | sed 's/^/ - /'
|
|
211
|
-
session_status="
|
|
183
|
+
session_status="commit_missing"
|
|
184
|
+
else
|
|
185
|
+
# Bugfix commits (fix: / fix(scope):) are exempt from docs check.
|
|
186
|
+
local last_commit_subject=""
|
|
187
|
+
last_commit_subject=$(git -C "$project_root" log -1 --pretty=%s 2>/dev/null || true)
|
|
188
|
+
if [[ "$last_commit_subject" =~ ^fix(\(|:) ]]; then
|
|
189
|
+
log_info "Detected bugfix commit prefix; skipping docs check."
|
|
190
|
+
else
|
|
191
|
+
local docs_changed=""
|
|
192
|
+
docs_changed=$(git -C "$project_root" log --name-only --format="" -1 2>/dev/null \
|
|
193
|
+
| grep -E '(\.prizm-docs/|\.prizmkit/specs/REGISTRY\.md)' | head -1 || true)
|
|
194
|
+
if [[ -z "$docs_changed" ]]; then
|
|
195
|
+
log_warn "Session committed but no .prizm-docs or REGISTRY.md changes detected."
|
|
196
|
+
session_status="docs_missing"
|
|
197
|
+
fi
|
|
198
|
+
fi
|
|
212
199
|
fi
|
|
213
200
|
fi
|
|
214
201
|
fi
|
|
215
202
|
|
|
203
|
+
# Persist final pipeline-resolved status back to session-status.json
|
|
204
|
+
# only for post-check statuses introduced by the pipeline itself.
|
|
205
|
+
if [[ -f "$session_status_file" && ( "$session_status" == "commit_missing" || "$session_status" == "docs_missing" ) ]]; then
|
|
206
|
+
python3 -c "
|
|
207
|
+
import json, sys
|
|
208
|
+
p, st = sys.argv[1], sys.argv[2]
|
|
209
|
+
with open(p, 'r', encoding='utf-8') as f:
|
|
210
|
+
data = json.load(f)
|
|
211
|
+
data['status'] = st
|
|
212
|
+
with open(p, 'w', encoding='utf-8') as f:
|
|
213
|
+
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
214
|
+
" "$session_status_file" "$session_status" >/dev/null 2>&1 || true
|
|
215
|
+
fi
|
|
216
|
+
|
|
216
217
|
log_info "Session result: $session_status"
|
|
217
218
|
|
|
218
219
|
# Update feature status
|
|
@@ -254,23 +255,29 @@ trap cleanup SIGINT SIGTERM
|
|
|
254
255
|
# ============================================================
|
|
255
256
|
|
|
256
257
|
check_dependencies() {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
log_error "jq is required but not installed. Install with: brew install jq"
|
|
260
|
-
exit 1
|
|
261
|
-
fi
|
|
258
|
+
prizm_check_common_dependencies "$CLI_CMD"
|
|
259
|
+
}
|
|
262
260
|
|
|
263
|
-
|
|
264
|
-
if
|
|
265
|
-
|
|
266
|
-
exit 1
|
|
261
|
+
run_log_cleanup() {
|
|
262
|
+
if [[ "$LOG_CLEANUP_ENABLED" != "1" ]]; then
|
|
263
|
+
return 0
|
|
267
264
|
fi
|
|
268
265
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
266
|
+
local cleanup_result
|
|
267
|
+
cleanup_result=$(python3 "$SCRIPTS_DIR/cleanup-logs.py" \
|
|
268
|
+
--state-dir "$STATE_DIR" \
|
|
269
|
+
--retention-days "$LOG_RETENTION_DAYS" \
|
|
270
|
+
--max-total-mb "$LOG_MAX_TOTAL_MB" 2>/dev/null) || {
|
|
271
|
+
log_warn "Log cleanup failed (continuing)"
|
|
272
|
+
return 0
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
local deleted reclaimed_kb
|
|
276
|
+
deleted=$(echo "$cleanup_result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('deleted_files', 0))" 2>/dev/null || echo "0")
|
|
277
|
+
reclaimed_kb=$(echo "$cleanup_result" | python3 -c "import sys,json; print(int(json.load(sys.stdin).get('reclaimed_bytes', 0)/1024))" 2>/dev/null || echo "0")
|
|
278
|
+
|
|
279
|
+
if [[ "$deleted" -gt 0 ]]; then
|
|
280
|
+
log_info "Log cleanup: deleted $deleted files, reclaimed ${reclaimed_kb}KB"
|
|
274
281
|
fi
|
|
275
282
|
}
|
|
276
283
|
|
|
@@ -385,6 +392,7 @@ run_one() {
|
|
|
385
392
|
fi
|
|
386
393
|
|
|
387
394
|
check_dependencies
|
|
395
|
+
run_log_cleanup
|
|
388
396
|
|
|
389
397
|
# Verify feature exists
|
|
390
398
|
local feature_title
|
|
@@ -550,8 +558,8 @@ sys.exit(1)
|
|
|
550
558
|
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
551
559
|
echo ""
|
|
552
560
|
|
|
553
|
-
# Override cleanup trap for single-feature mode
|
|
554
|
-
|
|
561
|
+
# Override cleanup trap for single-feature mode (use distinct name to avoid overwriting global cleanup)
|
|
562
|
+
cleanup_single_feature() {
|
|
555
563
|
echo ""
|
|
556
564
|
log_warn "Interrupted. Killing session..."
|
|
557
565
|
# Kill all child processes
|
|
@@ -559,7 +567,7 @@ sys.exit(1)
|
|
|
559
567
|
log_info "Session log: $session_dir/logs/session.log"
|
|
560
568
|
exit 130
|
|
561
569
|
}
|
|
562
|
-
trap
|
|
570
|
+
trap cleanup_single_feature SIGINT SIGTERM
|
|
563
571
|
|
|
564
572
|
_SPAWN_RESULT=""
|
|
565
573
|
spawn_and_wait_session \
|
|
@@ -603,6 +611,7 @@ main() {
|
|
|
603
611
|
fi
|
|
604
612
|
|
|
605
613
|
check_dependencies
|
|
614
|
+
run_log_cleanup
|
|
606
615
|
|
|
607
616
|
# Initialize pipeline state if needed
|
|
608
617
|
if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
|
|
@@ -683,6 +692,7 @@ for f in data.get('stuck_features', []):
|
|
|
683
692
|
log_success " All features completed! Pipeline finished."
|
|
684
693
|
log_success " Total sessions: $session_count"
|
|
685
694
|
log_success "════════════════════════════════════════════════════"
|
|
695
|
+
rm -f "$STATE_DIR/current-session.json"
|
|
686
696
|
break
|
|
687
697
|
fi
|
|
688
698
|
|
|
@@ -729,7 +739,7 @@ for f in data.get('stuck_features', []):
|
|
|
729
739
|
--state-dir "$STATE_DIR" \
|
|
730
740
|
--output "$bootstrap_prompt" >/dev/null 2>&1
|
|
731
741
|
|
|
732
|
-
# Update current session tracking
|
|
742
|
+
# Update current session tracking (atomic write via temp file)
|
|
733
743
|
python3 -c "
|
|
734
744
|
import json, sys, os
|
|
735
745
|
from datetime import datetime
|
|
@@ -739,8 +749,11 @@ data = {
|
|
|
739
749
|
'session_id': session_id,
|
|
740
750
|
'started_at': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
|
|
741
751
|
}
|
|
742
|
-
|
|
752
|
+
target = os.path.join(state_dir, 'current-session.json')
|
|
753
|
+
tmp = target + '.tmp'
|
|
754
|
+
with open(tmp, 'w') as f:
|
|
743
755
|
json.dump(data, f, indent=2)
|
|
756
|
+
os.replace(tmp, target)
|
|
744
757
|
" "$feature_id" "$session_id" "$STATE_DIR"
|
|
745
758
|
|
|
746
759
|
# Mark feature as in-progress before spawning session
|
|
@@ -794,6 +807,9 @@ show_help() {
|
|
|
794
807
|
echo " AI_CLI AI CLI command name (auto-detected: cbc or claude)"
|
|
795
808
|
echo " HEARTBEAT_INTERVAL Heartbeat log interval in seconds (default: 30)"
|
|
796
809
|
echo " HEARTBEAT_STALE_THRESHOLD Heartbeat stale threshold in seconds (default: 600)"
|
|
810
|
+
echo " LOG_CLEANUP_ENABLED Run log cleanup before execution (default: 1)"
|
|
811
|
+
echo " LOG_RETENTION_DAYS Delete logs older than N days (default: 14)"
|
|
812
|
+
echo " LOG_MAX_TOTAL_MB Keep total logs under N MB (default: 1024)"
|
|
797
813
|
echo ""
|
|
798
814
|
echo "Examples:"
|
|
799
815
|
echo " ./run.sh run # Run all features"
|
|
@@ -13,9 +13,13 @@ import argparse
|
|
|
13
13
|
import json
|
|
14
14
|
import sys
|
|
15
15
|
|
|
16
|
+
from utils import setup_logging
|
|
17
|
+
|
|
16
18
|
|
|
17
19
|
REQUIRED_FIELDS = ["session_id", "feature_id", "status", "timestamp"]
|
|
18
20
|
|
|
21
|
+
LOGGER = setup_logging("check-session-status")
|
|
22
|
+
|
|
19
23
|
|
|
20
24
|
def parse_args():
|
|
21
25
|
parser = argparse.ArgumentParser(
|
|
@@ -61,7 +65,8 @@ def validate_required_fields(data):
|
|
|
61
65
|
def determine_status(data):
|
|
62
66
|
"""Determine the single-line status string from the parsed data.
|
|
63
67
|
|
|
64
|
-
Returns one of: success, partial_resumable, partial_not_resumable,
|
|
68
|
+
Returns one of: success, partial_resumable, partial_not_resumable,
|
|
69
|
+
failed, commit_missing, docs_missing.
|
|
65
70
|
"""
|
|
66
71
|
status = data.get("status", "")
|
|
67
72
|
|
|
@@ -75,6 +80,10 @@ def determine_status(data):
|
|
|
75
80
|
return "partial_not_resumable"
|
|
76
81
|
elif status == "failed":
|
|
77
82
|
return "failed"
|
|
83
|
+
elif status == "commit_missing":
|
|
84
|
+
return "commit_missing"
|
|
85
|
+
elif status == "docs_missing":
|
|
86
|
+
return "docs_missing"
|
|
78
87
|
else:
|
|
79
88
|
# Unknown status value — treat as crashed
|
|
80
89
|
return "crashed"
|
|
@@ -155,4 +164,40 @@ def main():
|
|
|
155
164
|
|
|
156
165
|
|
|
157
166
|
if __name__ == "__main__":
|
|
158
|
-
|
|
167
|
+
try:
|
|
168
|
+
main()
|
|
169
|
+
except KeyboardInterrupt:
|
|
170
|
+
detail = {
|
|
171
|
+
"status": "crashed",
|
|
172
|
+
"feature_id": None,
|
|
173
|
+
"completed_phases": [],
|
|
174
|
+
"checkpoint_reached": None,
|
|
175
|
+
"tasks_completed": 0,
|
|
176
|
+
"tasks_total": 0,
|
|
177
|
+
"error_count": 1,
|
|
178
|
+
"can_resume": False,
|
|
179
|
+
"resume_from_phase": None,
|
|
180
|
+
"internal_error": "Interrupted",
|
|
181
|
+
}
|
|
182
|
+
sys.stderr.write(json.dumps(detail, indent=2, ensure_ascii=False) + "\n")
|
|
183
|
+
print("crashed")
|
|
184
|
+
sys.exit(0)
|
|
185
|
+
except SystemExit:
|
|
186
|
+
raise
|
|
187
|
+
except Exception as exc:
|
|
188
|
+
LOGGER.exception("Unhandled exception in check-session-status")
|
|
189
|
+
detail = {
|
|
190
|
+
"status": "crashed",
|
|
191
|
+
"feature_id": None,
|
|
192
|
+
"completed_phases": [],
|
|
193
|
+
"checkpoint_reached": None,
|
|
194
|
+
"tasks_completed": 0,
|
|
195
|
+
"tasks_total": 0,
|
|
196
|
+
"error_count": 1,
|
|
197
|
+
"can_resume": False,
|
|
198
|
+
"resume_from_phase": None,
|
|
199
|
+
"internal_error": str(exc),
|
|
200
|
+
}
|
|
201
|
+
sys.stderr.write(json.dumps(detail, indent=2, ensure_ascii=False) + "\n")
|
|
202
|
+
print("crashed")
|
|
203
|
+
sys.exit(0)
|