prizmkit 1.1.91 → 1.1.93
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/.env.example +0 -4
- package/bundled/dev-pipeline/README.md +9 -39
- package/bundled/dev-pipeline/SCHEMA_ANALYSIS.md +0 -4
- package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +0 -2
- package/bundled/dev-pipeline/launch-bugfix-daemon.sh +0 -1
- package/bundled/dev-pipeline/launch-feature-daemon.sh +2 -3
- package/bundled/dev-pipeline/launch-refactor-daemon.sh +0 -1
- package/bundled/dev-pipeline/lib/branch.sh +8 -5
- package/bundled/dev-pipeline/lib/common.sh +122 -0
- package/bundled/dev-pipeline/lib/heartbeat.sh +13 -3
- package/bundled/dev-pipeline/run-bugfix.sh +149 -98
- package/bundled/dev-pipeline/run-feature.sh +90 -103
- package/bundled/dev-pipeline/run-recovery.sh +1 -32
- package/bundled/dev-pipeline/run-refactor.sh +148 -99
- package/bundled/dev-pipeline/scripts/update-bug-status.py +39 -4
- package/bundled/dev-pipeline/scripts/update-feature-status.py +43 -6
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +36 -4
- package/bundled/dev-pipeline/tests/test-deploy-safety.sh +3 -3
- package/bundled/dev-pipeline/tests/test_auto_skip.py +92 -3
- package/bundled/dev-pipeline-windows/.env.example +0 -4
- package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +0 -4
- package/bundled/dev-pipeline-windows/assets/prizm-dev-team-integration.md +0 -2
- package/bundled/dev-pipeline-windows/lib/branch.ps1 +17 -4
- package/bundled/dev-pipeline-windows/lib/common.ps1 +9 -0
- package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +89 -108
- package/bundled/dev-pipeline-windows/run-recovery.ps1 +4 -19
- package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +39 -4
- package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +43 -6
- package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +36 -4
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +1 -1
- package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +5 -15
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +4 -4
- package/bundled/skills/feature-pipeline-launcher/references/configuration.md +3 -13
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +3 -3
- package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +4 -14
- package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +1 -1
- package/bundled/skills-windows/bugfix-pipeline-launcher/references/configuration.md +4 -14
- package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +4 -4
- package/bundled/skills-windows/feature-pipeline-launcher/references/configuration.md +2 -12
- package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +3 -3
- package/bundled/skills-windows/refactor-pipeline-launcher/references/configuration.md +4 -14
- package/package.json +1 -1
- package/bundled/dev-pipeline/scripts/cleanup-logs.py +0 -192
- package/bundled/dev-pipeline-windows/scripts/cleanup-logs.py +0 -192
package/bundled/VERSION.json
CHANGED
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
|
|
24
24
|
# ─── Pipeline Execution ──────────────────────────────────────────────
|
|
25
25
|
# MAX_RETRIES=3 # Max retry attempts per task before marking failed
|
|
26
|
-
# SESSION_TIMEOUT=0 # Session timeout in seconds (0 = no limit)
|
|
27
26
|
# VERBOSE=1 # Verbose logging (1=on, 0=off)
|
|
28
27
|
|
|
29
28
|
# ─── Feature Pipeline Only ────────────────────────────────────────────
|
|
@@ -45,6 +44,3 @@
|
|
|
45
44
|
# CODEX_WAIT_STALE_KILL_THRESHOLD=3600 # Longer no-log window while Codex waits on subagents
|
|
46
45
|
# CODEX_SUBAGENT_TIMEOUT_SECONDS=3300 # Codex subagent max runtime; defaults to wait threshold - 300
|
|
47
46
|
# CB_SUBAGENT_STALE_KILL_THRESHOLD=3600 # Longer no-log window while CodeBuddy sub-agents are running
|
|
48
|
-
# LOG_CLEANUP_ENABLED=1 # Periodic log cleanup (1=on, 0=off)
|
|
49
|
-
# LOG_RETENTION_DAYS=14 # Delete logs older than N days
|
|
50
|
-
# LOG_MAX_TOTAL_MB=1024 # Keep total logs under N MB via oldest-first cleanup
|
|
@@ -53,7 +53,6 @@ Main entry point. Drives the full feature development pipeline.
|
|
|
53
53
|
| Option | Description |
|
|
54
54
|
|--------|-------------|
|
|
55
55
|
| `--dry-run` | Generate bootstrap prompt only, don't spawn AI session |
|
|
56
|
-
| `--timeout N` | Override `SESSION_TIMEOUT` for this run (seconds) |
|
|
57
56
|
| `--resume-phase N` | Resume from specific phase number |
|
|
58
57
|
| `--no-reset` | Don't reset feature artifacts before running |
|
|
59
58
|
|
|
@@ -71,9 +70,6 @@ Main entry point. Drives the full feature development pipeline.
|
|
|
71
70
|
|
|
72
71
|
# Resume from Phase 6 (implementation)
|
|
73
72
|
./.prizmkit/dev-pipeline/run-feature.sh run .prizmkit/plans/feature-list.json --resume-phase 6
|
|
74
|
-
|
|
75
|
-
# With timeout per session
|
|
76
|
-
SESSION_TIMEOUT=7200 ./.prizmkit/dev-pipeline/run-feature.sh run .prizmkit/plans/feature-list.json
|
|
77
73
|
```
|
|
78
74
|
|
|
79
75
|
If `.prizmkit/plans/feature-list.json` path is omitted, defaults to `.prizmkit/plans/feature-list.json` in the project root.
|
|
@@ -140,7 +136,7 @@ Manages `run-feature.sh` as a background daemon process with PID tracking and lo
|
|
|
140
136
|
```bash
|
|
141
137
|
./.prizmkit/dev-pipeline/launch-feature-daemon.sh start .prizmkit/plans/feature-list.json \
|
|
142
138
|
--mode standard \
|
|
143
|
-
--env "MAX_RETRIES=5
|
|
139
|
+
--env "MAX_RETRIES=5 MODEL=claude-sonnet-4.6"
|
|
144
140
|
```
|
|
145
141
|
|
|
146
142
|
**Output (JSON on stdout for programmatic consumption):**
|
|
@@ -177,7 +173,7 @@ Equivalent to `run-feature.sh` but for the bug-fix pipeline.
|
|
|
177
173
|
./run-bugfix.sh help
|
|
178
174
|
```
|
|
179
175
|
|
|
180
|
-
**Single-bug options:** `--dry-run
|
|
176
|
+
**Single-bug options:** `--dry-run` (same as `run-feature.sh`)
|
|
181
177
|
|
|
182
178
|
Processes bugs by: **severity** (critical > high > medium > low) then **priority** (high > medium > low).
|
|
183
179
|
|
|
@@ -342,7 +338,7 @@ python3 scripts/update-feature-status.py \
|
|
|
342
338
|
| `complete` | `--feature-id` | Shortcut for manually marking completed |
|
|
343
339
|
|
|
344
340
|
**Session status values (for `--session-status`):**
|
|
345
|
-
`success`, `partial_resumable`, `partial_not_resumable`, `failed`, `crashed`, `
|
|
341
|
+
`success`, `partial_resumable`, `partial_not_resumable`, `failed`, `crashed`, `commit_missing`, `docs_missing`, `merge_conflict`
|
|
346
342
|
|
|
347
343
|
**Output (JSON to stdout):**
|
|
348
344
|
|
|
@@ -429,26 +425,6 @@ python3 scripts/detect-stuck.py \
|
|
|
429
425
|
|
|
430
426
|
---
|
|
431
427
|
|
|
432
|
-
### `scripts/cleanup-logs.py` — Log File Cleanup
|
|
433
|
-
|
|
434
|
-
Manages log file size by age and total size thresholds.
|
|
435
|
-
|
|
436
|
-
```bash
|
|
437
|
-
python3 scripts/cleanup-logs.py \
|
|
438
|
-
--state-dir <path> \
|
|
439
|
-
[--retention-days <n>] \
|
|
440
|
-
[--max-total-mb <n>] \
|
|
441
|
-
[--dry-run]
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
| Option | Default | Description |
|
|
445
|
-
|--------|---------|-------------|
|
|
446
|
-
| `--retention-days` | 14 | Delete files older than N days |
|
|
447
|
-
| `--max-total-mb` | 1024 | If total > N MB, remove oldest first |
|
|
448
|
-
| `--dry-run` | false | Show what would be deleted without deleting |
|
|
449
|
-
|
|
450
|
-
---
|
|
451
|
-
|
|
452
428
|
### `scripts/parse-stream-progress.py` — Real-Time Progress Parser
|
|
453
429
|
|
|
454
430
|
Reads AI CLI `stream-json` output and extracts progress metrics.
|
|
@@ -533,7 +509,6 @@ Also exports: `log_info`, `log_warn`, `log_error`, `log_success` (with timestamp
|
|
|
533
509
|
| Variable | Default | Used By | Description |
|
|
534
510
|
|----------|---------|---------|-------------|
|
|
535
511
|
| `MAX_RETRIES` | `3` | run-feature.sh | Max retry attempts per feature before marking as failed |
|
|
536
|
-
| `SESSION_TIMEOUT` | `0` (none) | run-feature.sh, run-bugfix.sh | Timeout in seconds per AI CLI session. 0 = no timeout |
|
|
537
512
|
| `PIPELINE_MODE` | (auto) | run-feature.sh, launch-feature-daemon.sh | Override mode for all features: `lite\|standard\|full` |
|
|
538
513
|
| `ENABLE_CRITIC` | `false` | run-feature.sh, launch-feature-daemon.sh | Enable adversarial critic review: `true\|false` |
|
|
539
514
|
| `DEV_BRANCH` | auto-generated | run-feature.sh | Custom git branch name (default: `dev/{feature-id}-{timestamp}`) |
|
|
@@ -555,21 +530,18 @@ Also exports: `log_info`, `log_warn`, `log_error`, `log_success` (with timestamp
|
|
|
555
530
|
|----------|---------|---------|-------------|
|
|
556
531
|
| `HEARTBEAT_INTERVAL` | `30` | run-feature.sh | Seconds between heartbeat log output |
|
|
557
532
|
| `HEARTBEAT_STALE_THRESHOLD` | `600` | run-feature.sh | Seconds before a session is considered stale/stuck |
|
|
558
|
-
| `LOG_CLEANUP_ENABLED` | `1` | run-feature.sh | Run log cleanup before pipeline execution |
|
|
559
|
-
| `LOG_RETENTION_DAYS` | `14` | run-feature.sh | Delete logs older than N days |
|
|
560
|
-
| `LOG_MAX_TOTAL_MB` | `1024` | run-feature.sh | Keep total log size under N MB |
|
|
561
533
|
|
|
562
534
|
**Examples:**
|
|
563
535
|
|
|
564
536
|
```bash
|
|
565
537
|
# All env vars in one invocation
|
|
566
|
-
MAX_RETRIES=5
|
|
538
|
+
MAX_RETRIES=5 MODEL=claude-sonnet-4.6 VERBOSE=1 \
|
|
567
539
|
./.prizmkit/dev-pipeline/run-feature.sh run .prizmkit/plans/feature-list.json
|
|
568
540
|
|
|
569
541
|
# Via daemon with --env
|
|
570
542
|
./.prizmkit/dev-pipeline/launch-feature-daemon.sh start .prizmkit/plans/feature-list.json \
|
|
571
543
|
--mode standard \
|
|
572
|
-
--env "MAX_RETRIES=5
|
|
544
|
+
--env "MAX_RETRIES=5 AUTO_PUSH=1"
|
|
573
545
|
```
|
|
574
546
|
|
|
575
547
|
---
|
|
@@ -721,11 +693,10 @@ Features are executed in dependency order via DAG:
|
|
|
721
693
|
|
|
722
694
|
1. **Bootstrap prompt** generated from feature spec, context, and acceptance criteria
|
|
723
695
|
2. AI CLI spawned as background process with optional `--model` flag
|
|
724
|
-
3. **
|
|
725
|
-
4. **
|
|
726
|
-
5.
|
|
727
|
-
6.
|
|
728
|
-
7. Feature status updated; on failure, retry count increments
|
|
696
|
+
3. **Heartbeat monitor** prints progress every `HEARTBEAT_INTERVAL` seconds
|
|
697
|
+
4. **Progress parser** (if stream-json supported) extracts phase/tool metrics
|
|
698
|
+
5. On completion, `session-status.json` determines success/failure
|
|
699
|
+
6. Feature status updated; on failure, retry count increments
|
|
729
700
|
|
|
730
701
|
### Heartbeat Output
|
|
731
702
|
|
|
@@ -882,7 +853,6 @@ run-bugfix.sh main loop
|
|
|
882
853
|
| +-- update-feature-status.py # Feature state machine (8 actions)
|
|
883
854
|
| +-- update-bug-status.py # Bug state machine (6 actions)
|
|
884
855
|
| +-- detect-stuck.py # Detect stuck/stale features
|
|
885
|
-
| +-- cleanup-logs.py # Age/size-based log cleanup
|
|
886
856
|
| +-- parse-stream-progress.py # Real-time stream-json progress parser
|
|
887
857
|
|
|
|
888
858
|
+-- templates/
|
|
@@ -344,7 +344,6 @@ pending, in_progress, completed, failed, skipped
|
|
|
344
344
|
| `AI_CLI` | string | auto-detect | claude or cbc |
|
|
345
345
|
| `MODEL` | string | (unset) | AI model override (per-task overrides this) |
|
|
346
346
|
| `MAX_RETRIES` | integer | (not specified) | Retry attempts per task |
|
|
347
|
-
| `SESSION_TIMEOUT` | integer | 0 | 0 = no limit |
|
|
348
347
|
| `VERBOSE` | integer | (not specified) | 1=on, 0=off |
|
|
349
348
|
| `ENABLE_CRITIC` | boolean | false | Adversarial review enable |
|
|
350
349
|
| `PIPELINE_MODE` | string | auto-detect | lite/standard/full override |
|
|
@@ -356,9 +355,6 @@ pending, in_progress, completed, failed, skipped
|
|
|
356
355
|
| `STALE_KILL_THRESHOLD` | integer | 900 | Auto-kill after N seconds without parent log progress |
|
|
357
356
|
| `CODEX_WAIT_STALE_KILL_THRESHOLD` | integer | 3600 | Longer no-log stale window while Codex waits on subagents |
|
|
358
357
|
| `CODEX_SUBAGENT_TIMEOUT_SECONDS` | integer | 3300 | Codex subagent max runtime |
|
|
359
|
-
| `LOG_CLEANUP_ENABLED` | integer | 1 | Periodic cleanup |
|
|
360
|
-
| `LOG_RETENTION_DAYS` | integer | 14 | Delete logs older than N days |
|
|
361
|
-
| `LOG_MAX_TOTAL_MB` | integer | 1024 | Max total logs (MB) |
|
|
362
358
|
|
|
363
359
|
**Priority:** Command-line env vars > .env file > defaults
|
|
364
360
|
|
|
@@ -51,7 +51,6 @@ Located at `.prizmkit/dev-pipeline/scripts/`:
|
|
|
51
51
|
| `check-session-status.py` | Read and validate session-status.json output |
|
|
52
52
|
| `detect-stuck.py` | Detect stuck/hung pipeline sessions via heartbeat |
|
|
53
53
|
| `parse-stream-progress.py` | Parse AI CLI output stream for progress tracking |
|
|
54
|
-
| `cleanup-logs.py` | Clean up old pipeline logs and state files |
|
|
55
54
|
| `utils.py` | Shared utility functions for pipeline scripts |
|
|
56
55
|
|
|
57
56
|
## Artifact Mapping
|
|
@@ -125,7 +124,6 @@ The agent MUST write `session-status.json` before exiting:
|
|
|
125
124
|
| `status: "partial"`, `can_resume: false` | Retry from scratch |
|
|
126
125
|
| `status: "failed"` | Retry (up to MAX_RETRIES) |
|
|
127
126
|
| No status file (crash) | Treat as failed, retry |
|
|
128
|
-
| Timeout (exit 124) | Treat as timed_out, retry |
|
|
129
127
|
|
|
130
128
|
## Team Naming Convention
|
|
131
129
|
|
|
@@ -482,7 +482,6 @@ Examples:
|
|
|
482
482
|
|
|
483
483
|
Environment Variables (pass via --env):
|
|
484
484
|
MAX_RETRIES Max retries per bug (default: 3)
|
|
485
|
-
SESSION_TIMEOUT Session timeout in seconds (default: 0 = no limit)
|
|
486
485
|
VERBOSE Set to 1 for verbose AI CLI output
|
|
487
486
|
HEARTBEAT_INTERVAL Heartbeat log interval in seconds (default: 30)
|
|
488
487
|
DEV_BRANCH Custom dev branch name (default: auto-generated)
|
|
@@ -106,7 +106,7 @@ cmd_start() {
|
|
|
106
106
|
--env)
|
|
107
107
|
shift
|
|
108
108
|
if [[ $# -eq 0 ]]; then
|
|
109
|
-
log_error "--env requires a value (e.g. --env \"MAX_RETRIES=5
|
|
109
|
+
log_error "--env requires a value (e.g. --env \"MAX_RETRIES=5\")"
|
|
110
110
|
exit 1
|
|
111
111
|
fi
|
|
112
112
|
env_overrides="$1"
|
|
@@ -609,7 +609,7 @@ Examples:
|
|
|
609
609
|
./launch-feature-daemon.sh start --features F-001,F-003,F-007 # Run specific features
|
|
610
610
|
./launch-feature-daemon.sh start --mode full # Full mode for complex features
|
|
611
611
|
./launch-feature-daemon.sh start --critic # Enable adversarial critic review
|
|
612
|
-
./launch-feature-daemon.sh start --env "MAX_RETRIES=5
|
|
612
|
+
./launch-feature-daemon.sh start --env "MAX_RETRIES=5"
|
|
613
613
|
./launch-feature-daemon.sh start .prizmkit/plans/feature-list.json --mode full --critic --env "VERBOSE=1"
|
|
614
614
|
./launch-feature-daemon.sh status # Check if running (JSON on stdout)
|
|
615
615
|
./launch-feature-daemon.sh logs --follow # Live log tailing
|
|
@@ -619,7 +619,6 @@ Examples:
|
|
|
619
619
|
|
|
620
620
|
Environment Variables (pass via --env):
|
|
621
621
|
MAX_RETRIES Max retries per feature (default: 3)
|
|
622
|
-
SESSION_TIMEOUT Session timeout in seconds (default: 0 = no limit)
|
|
623
622
|
VERBOSE Set to 1 for verbose AI CLI output
|
|
624
623
|
HEARTBEAT_INTERVAL Heartbeat log interval in seconds (default: 30)
|
|
625
624
|
DEV_BRANCH Custom dev branch name (default: auto-generated)
|
|
@@ -483,7 +483,6 @@ Examples:
|
|
|
483
483
|
|
|
484
484
|
Environment Variables (pass via --env):
|
|
485
485
|
MAX_RETRIES Max retries per refactor (default: 3)
|
|
486
|
-
SESSION_TIMEOUT Session timeout in seconds (default: 0 = no limit)
|
|
487
486
|
VERBOSE Set to 1 for verbose AI CLI output
|
|
488
487
|
STRICT_BEHAVIOR_CHECK Force full test suite after each refactor (default: 1)
|
|
489
488
|
HEARTBEAT_INTERVAL Heartbeat log interval in seconds (default: 30)
|
|
@@ -211,18 +211,21 @@ branch_save_wip() {
|
|
|
211
211
|
return 0
|
|
212
212
|
fi
|
|
213
213
|
|
|
214
|
-
# Check if there are any uncommitted changes (tracked or untracked,
|
|
214
|
+
# Check if there are any uncommitted changes (tracked or untracked,
|
|
215
|
+
# excluding gitignored and root-level hidden tool temporary worktrees)
|
|
215
216
|
local has_changes
|
|
216
|
-
has_changes=$(
|
|
217
|
+
has_changes=$(prizm_git_status_safe "$project_root" || true)
|
|
217
218
|
if [[ -z "$has_changes" ]]; then
|
|
218
219
|
return 0
|
|
219
220
|
fi
|
|
220
221
|
|
|
221
222
|
log_warn "Saving uncommitted work-in-progress on branch: $dev_branch"
|
|
222
223
|
|
|
223
|
-
# Stage all changes (tracked + untracked, respects .gitignore)
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
# Stage all changes (tracked + untracked, respects .gitignore), excluding
|
|
225
|
+
# root-level hidden tool temporary worktrees so nested agent repos are not
|
|
226
|
+
# recorded as accidental gitlinks in the user's project.
|
|
227
|
+
if ! prizm_git_add_all_safe "$project_root"; then
|
|
228
|
+
log_warn "git add failed — uncommitted work may be lost on branch switch"
|
|
226
229
|
return 0
|
|
227
230
|
fi
|
|
228
231
|
|
|
@@ -23,6 +23,128 @@ log_warn() { echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*
|
|
|
23
23
|
log_error() { echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
24
24
|
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
25
25
|
|
|
26
|
+
prizm_require_positive_int() {
|
|
27
|
+
local option_name="$1"
|
|
28
|
+
local option_value="$2"
|
|
29
|
+
if [[ ! "$option_value" =~ ^[1-9][0-9]*$ ]]; then
|
|
30
|
+
log_error "$option_name must be a positive integer: $option_value"
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Ignore temporary git worktrees created under root-level hidden AI tool dirs
|
|
36
|
+
# matching .<tool>/worktree(s), without hiding normal source directories.
|
|
37
|
+
PRIZM_GIT_HIDDEN_TOOL_WORKTREE_EXCLUDES=(
|
|
38
|
+
':(top,exclude,glob).*/worktree'
|
|
39
|
+
':(top,exclude,glob).*/worktree/**'
|
|
40
|
+
':(top,exclude,glob).*/worktrees'
|
|
41
|
+
':(top,exclude,glob).*/worktrees/**'
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
prizm_git_status_safe() {
|
|
45
|
+
local project_root="$1"
|
|
46
|
+
git -C "$project_root" status --porcelain -- . \
|
|
47
|
+
"${PRIZM_GIT_HIDDEN_TOOL_WORKTREE_EXCLUDES[@]}" 2>/dev/null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
prizm_git_add_all_safe() {
|
|
51
|
+
local project_root="$1"
|
|
52
|
+
git -C "$project_root" add -A -- . \
|
|
53
|
+
"${PRIZM_GIT_HIDDEN_TOOL_WORKTREE_EXCLUDES[@]}" 2>/dev/null
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
prizm_git_relative_path() {
|
|
57
|
+
local project_root="$1"
|
|
58
|
+
local target_path="$2"
|
|
59
|
+
python3 - "$project_root" "$target_path" <<'PY' 2>/dev/null || true
|
|
60
|
+
import os
|
|
61
|
+
import sys
|
|
62
|
+
root = os.path.abspath(sys.argv[1])
|
|
63
|
+
target = os.path.abspath(sys.argv[2])
|
|
64
|
+
print(os.path.relpath(target, root).replace(os.sep, "/"))
|
|
65
|
+
PY
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
prizm_git_status_non_bookkeeping() {
|
|
69
|
+
local project_root="$1"
|
|
70
|
+
local state_dir="$2"
|
|
71
|
+
local list_path="$3"
|
|
72
|
+
|
|
73
|
+
local state_rel list_rel
|
|
74
|
+
state_rel="$(prizm_git_relative_path "$project_root" "$state_dir")"
|
|
75
|
+
list_rel="$(prizm_git_relative_path "$project_root" "$list_path")"
|
|
76
|
+
|
|
77
|
+
local line path
|
|
78
|
+
while IFS= read -r line; do
|
|
79
|
+
[[ -n "$line" ]] || continue
|
|
80
|
+
path="${line:3}"
|
|
81
|
+
if [[ "$path" == *" -> "* ]]; then
|
|
82
|
+
path="${path##* -> }"
|
|
83
|
+
fi
|
|
84
|
+
path="${path%\"}"
|
|
85
|
+
path="${path#\"}"
|
|
86
|
+
path="${path#./}"
|
|
87
|
+
if [[ -n "$list_rel" && "$path" == "$list_rel" ]]; then
|
|
88
|
+
continue
|
|
89
|
+
fi
|
|
90
|
+
if [[ -n "$state_rel" && ( "$path" == "$state_rel" || "$path" == "$state_rel/"* ) ]]; then
|
|
91
|
+
continue
|
|
92
|
+
fi
|
|
93
|
+
printf '%s\n' "$line"
|
|
94
|
+
done < <(prizm_git_status_safe "$project_root")
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
prizm_git_status_bookkeeping() {
|
|
98
|
+
local project_root="$1"
|
|
99
|
+
local state_dir="$2"
|
|
100
|
+
local list_path="$3"
|
|
101
|
+
|
|
102
|
+
local state_rel list_rel
|
|
103
|
+
state_rel="$(prizm_git_relative_path "$project_root" "$state_dir")"
|
|
104
|
+
list_rel="$(prizm_git_relative_path "$project_root" "$list_path")"
|
|
105
|
+
|
|
106
|
+
local line path
|
|
107
|
+
while IFS= read -r line; do
|
|
108
|
+
[[ -n "$line" ]] || continue
|
|
109
|
+
path="${line:3}"
|
|
110
|
+
if [[ "$path" == *" -> "* ]]; then
|
|
111
|
+
path="${path##* -> }"
|
|
112
|
+
fi
|
|
113
|
+
path="${path%\"}"
|
|
114
|
+
path="${path#\"}"
|
|
115
|
+
path="${path#./}"
|
|
116
|
+
if [[ -n "$list_rel" && "$path" == "$list_rel" ]]; then
|
|
117
|
+
printf '%s\n' "$line"
|
|
118
|
+
continue
|
|
119
|
+
fi
|
|
120
|
+
if [[ -n "$state_rel" && ( "$path" == "$state_rel" || "$path" == "$state_rel/"* ) ]]; then
|
|
121
|
+
printf '%s\n' "$line"
|
|
122
|
+
fi
|
|
123
|
+
done < <(prizm_git_status_safe "$project_root")
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
prizm_git_commit_paths() {
|
|
127
|
+
local project_root="$1"
|
|
128
|
+
local message="$2"
|
|
129
|
+
shift 2
|
|
130
|
+
|
|
131
|
+
local paths=()
|
|
132
|
+
local path rel_path
|
|
133
|
+
for path in "$@"; do
|
|
134
|
+
[[ -n "$path" && -e "$path" ]] || continue
|
|
135
|
+
rel_path="$(prizm_git_relative_path "$project_root" "$path")"
|
|
136
|
+
[[ -n "$rel_path" ]] || continue
|
|
137
|
+
paths+=("$rel_path")
|
|
138
|
+
done
|
|
139
|
+
[[ ${#paths[@]} -gt 0 ]] || return 0
|
|
140
|
+
|
|
141
|
+
git -C "$project_root" add -- "${paths[@]}" 2>/dev/null || true
|
|
142
|
+
if ! git -C "$project_root" diff --cached --quiet -- "${paths[@]}" 2>/dev/null; then
|
|
143
|
+
git -C "$project_root" commit --no-verify -m "$message" -- "${paths[@]}" 2>/dev/null || true
|
|
144
|
+
fi
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
26
148
|
# ============================================================
|
|
27
149
|
# Path resolution (single source of truth)
|
|
28
150
|
# ============================================================
|
|
@@ -46,6 +46,7 @@ start_heartbeat() {
|
|
|
46
46
|
local prev_progress_signature=""
|
|
47
47
|
local prev_error_loop_signature=""
|
|
48
48
|
local stale_seconds=0
|
|
49
|
+
local stale_reason="stale_session"
|
|
49
50
|
while kill -0 "$cli_pid" 2>/dev/null; do
|
|
50
51
|
sleep "$heartbeat_interval"
|
|
51
52
|
elapsed=$((elapsed + heartbeat_interval))
|
|
@@ -240,16 +241,20 @@ PY
|
|
|
240
241
|
# child activity as the primary progress signals — the parent process is
|
|
241
242
|
# still running and children may still be producing output.
|
|
242
243
|
if [[ "$error_loop_detected" == "true" ]]; then
|
|
244
|
+
stale_reason="error_loop"
|
|
243
245
|
stale_seconds=$effective_stale_kill_threshold
|
|
244
246
|
elif [[ "$log_truncated" == "true" ]]; then
|
|
247
|
+
stale_reason="stale_session"
|
|
245
248
|
if [[ $progress_advanced -gt 0 || $child_growth -gt 0 ]]; then
|
|
246
249
|
stale_seconds=0
|
|
247
250
|
else
|
|
248
251
|
stale_seconds=$((stale_seconds + heartbeat_interval))
|
|
249
252
|
fi
|
|
250
253
|
elif [[ $growth -le 0 && $child_growth -eq 0 && $progress_advanced -eq 0 ]]; then
|
|
254
|
+
stale_reason="stale_session"
|
|
251
255
|
stale_seconds=$((stale_seconds + heartbeat_interval))
|
|
252
256
|
else
|
|
257
|
+
stale_reason="stale_session"
|
|
253
258
|
stale_seconds=0
|
|
254
259
|
fi
|
|
255
260
|
|
|
@@ -333,14 +338,19 @@ PY
|
|
|
333
338
|
# stale window to surface stuck agents promptly.
|
|
334
339
|
if [[ $effective_stale_kill_threshold -gt 0 && $stale_seconds -ge $effective_stale_kill_threshold ]]; then
|
|
335
340
|
local stale_mins=$((stale_seconds / 60))
|
|
336
|
-
|
|
337
|
-
|
|
341
|
+
if [[ "$stale_reason" == "error_loop" ]]; then
|
|
342
|
+
echo -e " ${RED}[HEARTBEAT]${NC} ${mins}m${secs}s | log: ${size_display} | ${RED}STALE-KILL: repeated read-offset/wasted-call error loop (threshold: ${effective_stale_kill_threshold}s)${NC}"
|
|
343
|
+
echo -e " ${RED}[HEARTBEAT]${NC} Killing AI CLI process $cli_pid (error loop)..."
|
|
344
|
+
else
|
|
345
|
+
echo -e " ${RED}[HEARTBEAT]${NC} ${mins}m${secs}s | log: ${size_display} | ${RED}STALE-KILL: no progress for ${stale_mins}m (threshold: ${effective_stale_kill_threshold}s)${NC}"
|
|
346
|
+
echo -e " ${RED}[HEARTBEAT]${NC} Killing AI CLI process $cli_pid (stale session)..."
|
|
347
|
+
fi
|
|
338
348
|
# Write the marker before killing. Some CLIs exit quickly, and the
|
|
339
349
|
# parent runner may stop this heartbeat process immediately after
|
|
340
350
|
# wait(1) returns.
|
|
341
351
|
local _marker_dir
|
|
342
352
|
_marker_dir="$(dirname "$session_log")"
|
|
343
|
-
echo "{\"killed_at\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\", \"reason\": \"
|
|
353
|
+
echo "{\"killed_at\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\", \"reason\": \"${stale_reason}\", \"stale_seconds\": $stale_seconds, \"threshold\": $effective_stale_kill_threshold}" > "$_marker_dir/stale-kill.json" 2>/dev/null || true
|
|
344
354
|
kill -TERM "$cli_pid" 2>/dev/null || true
|
|
345
355
|
# Give process 10s to exit gracefully, then force kill
|
|
346
356
|
local stale_kill_grace_seconds="${STALE_KILL_GRACE_SECONDS:-10}"
|