prizmkit 1.1.57 → 1.1.60
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 +8 -6
- package/bundled/VERSION.json +3 -3
- package/bundled/adapters/codex/agent-adapter.js +38 -0
- package/bundled/adapters/codex/paths.js +27 -0
- package/bundled/adapters/codex/rules-adapter.js +30 -0
- package/bundled/adapters/codex/settings-adapter.js +27 -0
- package/bundled/adapters/codex/skill-adapter.js +65 -0
- package/bundled/adapters/codex/team-adapter.js +37 -0
- package/bundled/dev-pipeline/.env.example +2 -1
- package/bundled/dev-pipeline/README.md +10 -7
- package/bundled/dev-pipeline/lib/common.sh +278 -37
- package/bundled/dev-pipeline/run-bugfix.sh +10 -61
- package/bundled/dev-pipeline/run-feature.sh +10 -78
- package/bundled/dev-pipeline/run-recovery.sh +10 -46
- package/bundled/dev-pipeline/run-refactor.sh +10 -61
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +17 -7
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +9 -3
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +9 -3
- package/bundled/dev-pipeline/scripts/utils.py +6 -4
- package/bundled/dev-pipeline-windows/.env.example +28 -0
- package/bundled/dev-pipeline-windows/README.md +30 -0
- package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +525 -0
- package/bundled/dev-pipeline-windows/assets/feature-list-example.json +146 -0
- package/bundled/dev-pipeline-windows/assets/prizm-dev-team-integration.md +138 -0
- package/bundled/dev-pipeline-windows/launch-bugfix-daemon.ps1 +9 -0
- package/bundled/dev-pipeline-windows/launch-feature-daemon.ps1 +9 -0
- package/bundled/dev-pipeline-windows/launch-refactor-daemon.ps1 +9 -0
- package/bundled/dev-pipeline-windows/lib/common.ps1 +432 -0
- package/bundled/dev-pipeline-windows/lib/daemon.ps1 +140 -0
- package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +446 -0
- package/bundled/dev-pipeline-windows/lib/reset.ps1 +87 -0
- package/bundled/dev-pipeline-windows/reset-bug.ps1 +9 -0
- package/bundled/dev-pipeline-windows/reset-feature.ps1 +9 -0
- package/bundled/dev-pipeline-windows/reset-refactor.ps1 +9 -0
- package/bundled/dev-pipeline-windows/run-bugfix.ps1 +9 -0
- package/bundled/dev-pipeline-windows/run-feature.ps1 +9 -0
- package/bundled/dev-pipeline-windows/run-recovery.ps1 +76 -0
- package/bundled/dev-pipeline-windows/run-refactor.ps1 +9 -0
- package/bundled/dev-pipeline-windows/scripts/check-session-status.py +228 -0
- package/bundled/dev-pipeline-windows/scripts/cleanup-logs.py +192 -0
- package/bundled/dev-pipeline-windows/scripts/detect-stuck.py +530 -0
- package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +1737 -0
- package/bundled/dev-pipeline-windows/scripts/generate-bugfix-prompt.py +685 -0
- package/bundled/dev-pipeline-windows/scripts/generate-recovery-prompt.py +805 -0
- package/bundled/dev-pipeline-windows/scripts/generate-refactor-prompt.py +763 -0
- package/bundled/dev-pipeline-windows/scripts/init-bugfix-pipeline.py +316 -0
- package/bundled/dev-pipeline-windows/scripts/init-dev-team.py +134 -0
- package/bundled/dev-pipeline-windows/scripts/init-pipeline.py +380 -0
- package/bundled/dev-pipeline-windows/scripts/init-refactor-pipeline.py +399 -0
- package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +388 -0
- package/bundled/dev-pipeline-windows/scripts/patch-completion-notes.py +191 -0
- package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +864 -0
- package/bundled/dev-pipeline-windows/scripts/update-checkpoint.py +173 -0
- package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +1501 -0
- package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +1073 -0
- package/bundled/dev-pipeline-windows/scripts/utils.py +542 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/critic-plan-challenge.md +7 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-fix.md +7 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +30 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-resume.md +5 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +7 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +46 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +43 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +43 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +43 -0
- package/bundled/dev-pipeline-windows/templates/bug-fix-list-schema.json +263 -0
- package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +320 -0
- package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +237 -0
- package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +331 -0
- package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +270 -0
- package/bundled/dev-pipeline-windows/templates/sections/ac-verification-checklist.md +13 -0
- package/bundled/dev-pipeline-windows/templates/sections/checkpoint-system.md +91 -0
- package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +33 -0
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-agent.md +10 -0
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-full.md +12 -0
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-lite.md +7 -0
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-agent.md +8 -0
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-full.md +9 -0
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-lite.md +6 -0
- package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +21 -0
- package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +31 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +72 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +63 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +62 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +71 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +64 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +23 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +24 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +12 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +53 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +32 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +37 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +50 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +52 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +27 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +27 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +27 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +29 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +77 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase0-init.md +13 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +23 -0
- package/bundled/dev-pipeline-windows/templates/sections/session-context.md +5 -0
- package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +6 -0
- package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-agent.md +67 -0
- package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-lite.md +58 -0
- package/bundled/dev-pipeline-windows/templates/session-status-schema.json +83 -0
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/app-planner/SKILL.md +26 -18
- package/bundled/skills/app-planner/references/architecture-decisions.md +9 -5
- package/bundled/skills/app-planner/references/frontend-design-guide.md +1 -1
- package/bundled/skills/feature-planner/SKILL.md +9 -2
- package/bundled/skills/prizmkit-init/SKILL.md +7 -6
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +2 -0
- package/bundled/skills-windows/app-planner/SKILL.md +639 -0
- package/bundled/skills-windows/app-planner/assets/app-design-guide.md +101 -0
- package/bundled/skills-windows/app-planner/references/architecture-decisions.md +52 -0
- package/bundled/skills-windows/app-planner/references/brainstorm-guide.md +101 -0
- package/bundled/skills-windows/app-planner/references/frontend-design-guide.md +71 -0
- package/bundled/skills-windows/app-planner/references/project-brief-guide.md +82 -0
- package/bundled/skills-windows/app-planner/references/red-team-checklist.md +40 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/derivation-rules.md +609 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/fixed-rules.md +285 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/question-bank.md +249 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/template.md +173 -0
- package/bundled/skills-windows/app-planner/references/rules/database/derivation-rules.md +373 -0
- package/bundled/skills-windows/app-planner/references/rules/database/fixed-rules.md +211 -0
- package/bundled/skills-windows/app-planner/references/rules/database/question-bank.md +184 -0
- package/bundled/skills-windows/app-planner/references/rules/database/template.md +158 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/derivation-rules.md +810 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/fixed-rules.md +188 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/question-bank.md +302 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/template.md +320 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/derivation-rules.md +639 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/fixed-rules.md +290 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/question-bank.md +232 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/template.md +175 -0
- package/bundled/skills-windows/bug-fix-workflow/SKILL.md +415 -0
- package/bundled/skills-windows/bug-planner/SKILL.md +395 -0
- package/bundled/skills-windows/bug-planner/assets/bug-confirmation-template.md +43 -0
- package/bundled/skills-windows/bug-planner/references/critic-and-verification.md +44 -0
- package/bundled/skills-windows/bug-planner/references/error-recovery.md +73 -0
- package/bundled/skills-windows/bug-planner/references/input-formats.md +53 -0
- package/bundled/skills-windows/bug-planner/references/schema-validation.md +25 -0
- package/bundled/skills-windows/bug-planner/references/severity-rules.md +16 -0
- package/bundled/skills-windows/bug-planner/scripts/validate-bug-list.py +322 -0
- package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +380 -0
- package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +441 -0
- package/bundled/skills-windows/feature-pipeline-launcher/scripts/preflight-check.py +462 -0
- package/bundled/skills-windows/feature-planner/SKILL.md +401 -0
- package/bundled/skills-windows/feature-planner/assets/evaluation-guide.md +64 -0
- package/bundled/skills-windows/feature-planner/assets/planning-guide.md +214 -0
- package/bundled/skills-windows/feature-planner/references/browser-interaction.md +59 -0
- package/bundled/skills-windows/feature-planner/references/completeness-review.md +57 -0
- package/bundled/skills-windows/feature-planner/references/decomposition-patterns.md +75 -0
- package/bundled/skills-windows/feature-planner/references/error-recovery.md +90 -0
- package/bundled/skills-windows/feature-planner/references/incremental-feature-planning.md +112 -0
- package/bundled/skills-windows/feature-planner/references/new-project-planning.md +85 -0
- package/bundled/skills-windows/feature-planner/scripts/validate-and-generate.py +1029 -0
- package/bundled/skills-windows/feature-workflow/SKILL.md +531 -0
- package/bundled/skills-windows/prizmkit-init/SKILL.md +356 -0
- package/bundled/skills-windows/prizmkit-init/assets/project-brief-template.md +82 -0
- package/bundled/skills-windows/prizmkit-init/references/config-schema.md +68 -0
- package/bundled/skills-windows/prizmkit-init/references/rules/layer-detection.md +41 -0
- package/bundled/skills-windows/prizmkit-init/references/tech-stack-catalog.md +13 -0
- package/bundled/skills-windows/prizmkit-init/references/update-supplement.md +9 -0
- package/bundled/skills-windows/recovery-workflow/SKILL.md +456 -0
- package/bundled/skills-windows/recovery-workflow/evals/evals.json +46 -0
- package/bundled/skills-windows/recovery-workflow/scripts/detect-recovery-state.py +544 -0
- package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +406 -0
- package/bundled/skills-windows/refactor-planner/SKILL.md +540 -0
- package/bundled/skills-windows/refactor-planner/assets/planning-guide.md +292 -0
- package/bundled/skills-windows/refactor-planner/references/behavior-preservation.md +301 -0
- package/bundled/skills-windows/refactor-planner/references/refactor-scoping-guide.md +221 -0
- package/bundled/skills-windows/refactor-planner/scripts/validate-and-generate-refactor.py +858 -0
- package/bundled/skills-windows/refactor-workflow/SKILL.md +503 -0
- package/package.json +3 -2
- package/src/clean.js +73 -2
- package/src/config.js +159 -50
- package/src/detect-platform.js +16 -8
- package/src/external-skills.js +26 -19
- package/src/index.js +31 -9
- package/src/manifest.js +6 -2
- package/src/metadata.js +43 -5
- package/src/platforms.js +36 -0
- package/src/prompts.js +31 -6
- package/src/runtimes.js +20 -0
- package/src/scaffold.js +314 -110
- package/src/upgrade.js +81 -41
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
. "$PSScriptRoot\common.ps1"
|
|
2
|
+
|
|
3
|
+
function Get-PrizmDaemonProcess {
|
|
4
|
+
param([string]$PidFile)
|
|
5
|
+
if (-not (Test-Path $PidFile)) { return $null }
|
|
6
|
+
$rawPid = (Get-Content $PidFile -ErrorAction SilentlyContinue | Select-Object -First 1)
|
|
7
|
+
$processId = 0
|
|
8
|
+
if (-not [int]::TryParse($rawPid, [ref]$processId)) { return $null }
|
|
9
|
+
return Get-Process -Id $processId -ErrorAction SilentlyContinue
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function Convert-PrizmEnvString {
|
|
13
|
+
param([string]$EnvString)
|
|
14
|
+
$envMap = @{}
|
|
15
|
+
foreach ($pair in ($EnvString -split '\s+')) {
|
|
16
|
+
if (-not $pair) { continue }
|
|
17
|
+
$parts = $pair -split '=', 2
|
|
18
|
+
if ($parts.Count -ne 2 -or -not $parts[0]) {
|
|
19
|
+
Write-PrizmWarn "Ignoring invalid env override: $pair"
|
|
20
|
+
continue
|
|
21
|
+
}
|
|
22
|
+
if ($parts[0] -notmatch '^[A-Za-z_][A-Za-z0-9_]*$') {
|
|
23
|
+
Write-PrizmWarn "Ignoring invalid env variable name: $($parts[0])"
|
|
24
|
+
continue
|
|
25
|
+
}
|
|
26
|
+
$envMap[$parts[0]] = $parts[1]
|
|
27
|
+
}
|
|
28
|
+
return $envMap
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function Invoke-PrizmDaemon {
|
|
32
|
+
param(
|
|
33
|
+
[ValidateSet('feature','bugfix','refactor')][string]$Kind,
|
|
34
|
+
[string]$ScriptRoot,
|
|
35
|
+
[string]$RunScript,
|
|
36
|
+
[string[]]$Args
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
$global:PRIZM_DAEMON_EXIT_CODE = 0
|
|
40
|
+
$paths = Initialize-PrizmPaths $ScriptRoot
|
|
41
|
+
$command = if ($Args.Count -gt 0) { $Args[0] } else { 'start' }
|
|
42
|
+
$remaining = if ($Args.Count -gt 1) { $Args[1..($Args.Count - 1)] } else { @() }
|
|
43
|
+
$daemonDir = Join-Path $paths.PrizmkitDir 'state\daemon'
|
|
44
|
+
New-Item -ItemType Directory -Force -Path $daemonDir | Out-Null
|
|
45
|
+
$pidFile = Join-Path $daemonDir "$Kind.pid"
|
|
46
|
+
$logFile = Join-Path $daemonDir "$Kind-daemon.log"
|
|
47
|
+
$errorLogFile = Join-Path $daemonDir "$Kind-daemon.err.log"
|
|
48
|
+
|
|
49
|
+
switch ($command) {
|
|
50
|
+
{ $_ -in @('help','--help','-h') } {
|
|
51
|
+
Write-Host "Usage: .\launch-$Kind-daemon.ps1 [start|status|stop|logs] [run options]"
|
|
52
|
+
Write-Host " start Launch .\run-$Kind.ps1 run in the background"
|
|
53
|
+
Write-Host " status Show daemon process state"
|
|
54
|
+
Write-Host " stop Stop the daemon process if it is still running"
|
|
55
|
+
Write-Host " logs Print daemon stdout/stderr logs"
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
'start' {
|
|
59
|
+
$existing = Get-PrizmDaemonProcess $pidFile
|
|
60
|
+
if ($existing) {
|
|
61
|
+
Write-PrizmWarn "$Kind daemon already running (PID $($existing.Id))."
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
$runArgs = @()
|
|
66
|
+
$envOverrides = @{}
|
|
67
|
+
for ($i = 0; $i -lt $remaining.Count; $i++) {
|
|
68
|
+
$arg = $remaining[$i]
|
|
69
|
+
if ($arg -eq '--env') {
|
|
70
|
+
if ($i + 1 -ge $remaining.Count) { throw '--env requires a value, for example --env "VERBOSE=1 MAX_RETRIES=5"' }
|
|
71
|
+
$parsedEnv = Convert-PrizmEnvString $remaining[$i + 1]
|
|
72
|
+
foreach ($key in $parsedEnv.Keys) {
|
|
73
|
+
$envOverrides[$key] = $parsedEnv[$key]
|
|
74
|
+
}
|
|
75
|
+
$i++
|
|
76
|
+
continue
|
|
77
|
+
}
|
|
78
|
+
$runArgs += $arg
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
$hostPath = Resolve-PrizmPowerShellHost
|
|
82
|
+
$arguments = Join-PrizmProcessArguments (@('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $RunScript, 'run') + $runArgs)
|
|
83
|
+
$originalEnv = @{}
|
|
84
|
+
foreach ($key in $envOverrides.Keys) {
|
|
85
|
+
$originalEnv[$key] = [Environment]::GetEnvironmentVariable($key, 'Process')
|
|
86
|
+
[Environment]::SetEnvironmentVariable($key, $envOverrides[$key], 'Process')
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
$process = Start-Process -FilePath $hostPath `
|
|
90
|
+
-ArgumentList $arguments `
|
|
91
|
+
-WorkingDirectory $paths.ProjectRoot `
|
|
92
|
+
-RedirectStandardOutput $logFile `
|
|
93
|
+
-RedirectStandardError $errorLogFile `
|
|
94
|
+
-PassThru
|
|
95
|
+
} finally {
|
|
96
|
+
foreach ($key in $envOverrides.Keys) {
|
|
97
|
+
[Environment]::SetEnvironmentVariable($key, $originalEnv[$key], 'Process')
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
Set-Content -Path $pidFile -Value ([string]$process.Id) -Encoding UTF8
|
|
101
|
+
Write-PrizmSuccess "$Kind daemon started (PID $($process.Id))."
|
|
102
|
+
Write-PrizmInfo "Log: $logFile"
|
|
103
|
+
if (Test-Path $errorLogFile) { Write-PrizmInfo "Error log: $errorLogFile" }
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
'status' {
|
|
107
|
+
$process = Get-PrizmDaemonProcess $pidFile
|
|
108
|
+
if ($process) {
|
|
109
|
+
Write-PrizmSuccess "$Kind daemon running (PID $($process.Id))."
|
|
110
|
+
} else {
|
|
111
|
+
if (Test-Path $pidFile) { Remove-Item -Force $pidFile }
|
|
112
|
+
Write-PrizmInfo "$Kind daemon is not running."
|
|
113
|
+
}
|
|
114
|
+
Write-PrizmInfo "Log: $logFile"
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
'stop' {
|
|
118
|
+
$process = Get-PrizmDaemonProcess $pidFile
|
|
119
|
+
if (-not $process) {
|
|
120
|
+
if (Test-Path $pidFile) { Remove-Item -Force $pidFile }
|
|
121
|
+
Write-PrizmInfo "$Kind daemon is not running."
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
Stop-PrizmProcessTreeById -ProcessId $process.Id
|
|
125
|
+
Remove-Item -Force $pidFile
|
|
126
|
+
Write-PrizmSuccess "$Kind daemon stopped (PID $($process.Id))."
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
'logs' {
|
|
130
|
+
if (Test-Path $logFile) { Get-Content $logFile -Tail 120 }
|
|
131
|
+
if (Test-Path $errorLogFile) { Get-Content $errorLogFile -Tail 120 }
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
default {
|
|
135
|
+
Write-PrizmError "Unknown daemon command: $command"
|
|
136
|
+
$global:PRIZM_DAEMON_EXIT_CODE = 1
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
. "$PSScriptRoot\common.ps1"
|
|
2
|
+
$global:PRIZM_EXIT_CODE = 0
|
|
3
|
+
|
|
4
|
+
function Invoke-PrizmPipeline {
|
|
5
|
+
param(
|
|
6
|
+
[ValidateSet('feature','bugfix','refactor')][string]$Kind,
|
|
7
|
+
[string]$ScriptRoot,
|
|
8
|
+
[string[]]$Args
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
$paths = Initialize-PrizmPaths $ScriptRoot
|
|
12
|
+
Import-PrizmEnv (Join-Path $paths.PrizmkitDir '.env')
|
|
13
|
+
Set-Location $paths.ProjectRoot
|
|
14
|
+
$python = Resolve-PrizmPython
|
|
15
|
+
|
|
16
|
+
$command = if ($Args.Count -gt 0) { $Args[0] } else { 'help' }
|
|
17
|
+
$remaining = if ($Args.Count -gt 1) { $Args[1..($Args.Count - 1)] } else { @() }
|
|
18
|
+
|
|
19
|
+
$idName = @{ feature = 'feature'; bugfix = 'bug'; refactor = 'refactor' }[$Kind]
|
|
20
|
+
$idOption = @{ feature = '--feature-id'; bugfix = '--bug-id'; refactor = '--refactor-id' }[$Kind]
|
|
21
|
+
$listOption = @{ feature = '--feature-list'; bugfix = '--bug-list'; refactor = '--refactor-list' }[$Kind]
|
|
22
|
+
$initScript = @{ feature = 'init-pipeline.py'; bugfix = 'init-bugfix-pipeline.py'; refactor = 'init-refactor-pipeline.py' }[$Kind]
|
|
23
|
+
$updateScript = @{ feature = 'update-feature-status.py'; bugfix = 'update-bug-status.py'; refactor = 'update-refactor-status.py' }[$Kind]
|
|
24
|
+
$promptScript = @{ feature = 'generate-bootstrap-prompt.py'; bugfix = 'generate-bugfix-prompt.py'; refactor = 'generate-refactor-prompt.py' }[$Kind]
|
|
25
|
+
$defaultList = Get-PrizmListDefault $Kind $paths.ProjectRoot
|
|
26
|
+
$stateDir = Get-PrizmStateDir $Kind $paths.ProjectRoot
|
|
27
|
+
$envMaxRetryArgs = @()
|
|
28
|
+
if ($env:MAX_RETRIES) {
|
|
29
|
+
$parsedEnvMaxRetries = 0
|
|
30
|
+
if (-not [int]::TryParse($env:MAX_RETRIES, [ref]$parsedEnvMaxRetries) -or $parsedEnvMaxRetries -lt 1) {
|
|
31
|
+
throw "MAX_RETRIES must be a positive integer: $($env:MAX_RETRIES)"
|
|
32
|
+
}
|
|
33
|
+
$envMaxRetryArgs = @('--max-retries', [string]$parsedEnvMaxRetries)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if ($command -in @('help','--help','-h')) {
|
|
37
|
+
Write-Host "Usage: .\run-$Kind.ps1 run [item-id] [list-path] [--dry-run] [--mode lite|standard|full] [--critic] [--max-retries N] [--timeout seconds]"
|
|
38
|
+
Write-Host " .\run-$Kind.ps1 status [list-path]"
|
|
39
|
+
Write-Host " .\run-$Kind.ps1 reset"
|
|
40
|
+
$global:PRIZM_EXIT_CODE = 0
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if ($command -eq 'reset') {
|
|
45
|
+
if (Test-Path $stateDir) { Remove-Item -Recurse -Force $stateDir }
|
|
46
|
+
New-Item -ItemType Directory -Force -Path $stateDir | Out-Null
|
|
47
|
+
Write-PrizmSuccess "State reset: $stateDir"
|
|
48
|
+
$global:PRIZM_EXIT_CODE = 0
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if ($command -eq 'status') {
|
|
53
|
+
$listPath = if ($remaining.Count -gt 0) { $remaining[0] } else { $defaultList }
|
|
54
|
+
Invoke-PrizmPythonText $python (@((Join-Path $paths.ScriptsDir $updateScript), $listOption, $listPath, '--state-dir', $stateDir, '--action', 'status') + $envMaxRetryArgs)
|
|
55
|
+
$global:PRIZM_EXIT_CODE = $LASTEXITCODE
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if ($command -ne 'run') { throw "Unknown command: $command" }
|
|
60
|
+
|
|
61
|
+
$itemId = $null
|
|
62
|
+
$listPath = $defaultList
|
|
63
|
+
$dryRun = $false
|
|
64
|
+
$mode = $null
|
|
65
|
+
if ($env:PIPELINE_MODE) { $mode = $env:PIPELINE_MODE.Trim() }
|
|
66
|
+
$critic = $null
|
|
67
|
+
$criticEnv = if ($env:ENABLE_CRITIC) { $env:ENABLE_CRITIC.Trim().ToLowerInvariant() } else { '' }
|
|
68
|
+
if ($criticEnv -in @('true','1','yes','on')) {
|
|
69
|
+
$critic = 'true'
|
|
70
|
+
} elseif ($criticEnv -in @('false','0','no','off')) {
|
|
71
|
+
$critic = 'false'
|
|
72
|
+
} elseif ($criticEnv) {
|
|
73
|
+
Write-PrizmWarn "Ignoring unsupported ENABLE_CRITIC value: $($env:ENABLE_CRITIC)"
|
|
74
|
+
}
|
|
75
|
+
$maxRetries = $null
|
|
76
|
+
if ($envMaxRetryArgs.Count -gt 0) { $maxRetries = [int]$envMaxRetryArgs[1] }
|
|
77
|
+
$verboseEnabled = $env:VERBOSE -in @('1','true','yes','on')
|
|
78
|
+
$timeoutSeconds = 0
|
|
79
|
+
if ($env:SESSION_TIMEOUT) {
|
|
80
|
+
$parsedEnvTimeout = 0
|
|
81
|
+
if (-not [int]::TryParse($env:SESSION_TIMEOUT, [ref]$parsedEnvTimeout) -or $parsedEnvTimeout -lt 0) {
|
|
82
|
+
throw "SESSION_TIMEOUT must be a non-negative integer: $($env:SESSION_TIMEOUT)"
|
|
83
|
+
}
|
|
84
|
+
$timeoutSeconds = $parsedEnvTimeout
|
|
85
|
+
}
|
|
86
|
+
$featuresFilter = $null
|
|
87
|
+
|
|
88
|
+
for ($i = 0; $i -lt $remaining.Count; $i++) {
|
|
89
|
+
$arg = $remaining[$i]
|
|
90
|
+
switch -Regex ($arg) {
|
|
91
|
+
'^--dry-run$' { $dryRun = $true; continue }
|
|
92
|
+
'^--critic$' { $critic = 'true'; continue }
|
|
93
|
+
'^--no-critic$' { $critic = 'false'; continue }
|
|
94
|
+
'^--mode$' { $i++; $mode = $remaining[$i]; continue }
|
|
95
|
+
'^--max-retries$' {
|
|
96
|
+
$i++
|
|
97
|
+
$parsedMaxRetries = 0
|
|
98
|
+
if (-not [int]::TryParse($remaining[$i], [ref]$parsedMaxRetries) -or $parsedMaxRetries -lt 1) {
|
|
99
|
+
throw "--max-retries must be a positive integer: $($remaining[$i])"
|
|
100
|
+
}
|
|
101
|
+
$maxRetries = $parsedMaxRetries
|
|
102
|
+
continue
|
|
103
|
+
}
|
|
104
|
+
'^--timeout$' {
|
|
105
|
+
$i++
|
|
106
|
+
if ($i -ge $remaining.Count) { throw '--timeout requires a seconds value.' }
|
|
107
|
+
$parsedTimeout = 0
|
|
108
|
+
if (-not [int]::TryParse($remaining[$i], [ref]$parsedTimeout) -or $parsedTimeout -lt 0) {
|
|
109
|
+
throw "--timeout must be a non-negative integer: $($remaining[$i])"
|
|
110
|
+
}
|
|
111
|
+
$timeoutSeconds = $parsedTimeout
|
|
112
|
+
continue
|
|
113
|
+
}
|
|
114
|
+
'^--features$' { $i++; $featuresFilter = $remaining[$i]; continue }
|
|
115
|
+
'^-' { Write-PrizmWarn "Ignoring unsupported option: $arg"; continue }
|
|
116
|
+
default {
|
|
117
|
+
if (-not $itemId -and $arg -match '^[FBR]-\d+') { $itemId = $arg; continue }
|
|
118
|
+
$listPath = $arg
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if ($mode -and $mode -notin @('lite','standard','full')) {
|
|
124
|
+
throw "PIPELINE_MODE/--mode must be one of: lite, standard, full. Got: $mode"
|
|
125
|
+
}
|
|
126
|
+
$maxRetryArgs = @()
|
|
127
|
+
if ($maxRetries -ne $null) { $maxRetryArgs = @('--max-retries', [string]$maxRetries) }
|
|
128
|
+
if ($verboseEnabled) {
|
|
129
|
+
$modeLabel = if ($mode) { $mode } else { 'auto' }
|
|
130
|
+
$criticLabel = if ($critic) { $critic } else { 'plan-default' }
|
|
131
|
+
$retryLabel = if ($maxRetries -ne $null) { [string]$maxRetries } else { 'default' }
|
|
132
|
+
Write-PrizmInfo "Verbose mode enabled."
|
|
133
|
+
Write-PrizmInfo "Effective options: mode=$modeLabel critic=$criticLabel maxRetries=$retryLabel timeoutSeconds=$timeoutSeconds dryRun=$dryRun"
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (-not (Test-Path $listPath)) { throw "List file not found: $listPath" }
|
|
137
|
+
$pipelineStatePath = Join-Path $stateDir 'pipeline.json'
|
|
138
|
+
if (-not $dryRun) {
|
|
139
|
+
New-Item -ItemType Directory -Force -Path $stateDir | Out-Null
|
|
140
|
+
if (-not (Test-Path $pipelineStatePath)) {
|
|
141
|
+
Invoke-PrizmPythonText $python @((Join-Path $paths.ScriptsDir $initScript), $listOption, $listPath, '--state-dir', $stateDir)
|
|
142
|
+
} else {
|
|
143
|
+
Write-PrizmInfo "Using existing pipeline state: $pipelineStatePath"
|
|
144
|
+
}
|
|
145
|
+
} elseif (-not (Test-Path $pipelineStatePath)) {
|
|
146
|
+
Write-PrizmInfo "Dry-run mode: not initializing pipeline state."
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function Test-PrizmGitRepository {
|
|
150
|
+
param([string]$ProjectRoot)
|
|
151
|
+
& git -C $ProjectRoot rev-parse --is-inside-work-tree *> $null
|
|
152
|
+
return $LASTEXITCODE -eq 0
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function Get-PrizmGitHead {
|
|
156
|
+
param([string]$ProjectRoot)
|
|
157
|
+
$head = & git -C $ProjectRoot rev-parse HEAD 2>$null
|
|
158
|
+
if ($LASTEXITCODE -eq 0 -and $head) { return ($head | Select-Object -First 1) }
|
|
159
|
+
return ''
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function Test-PrizmGitCommitsSince {
|
|
163
|
+
param([string]$ProjectRoot, [string]$BaseCommit)
|
|
164
|
+
if (-not $BaseCommit) {
|
|
165
|
+
return -not [string]::IsNullOrWhiteSpace([string](Get-PrizmGitHead $ProjectRoot))
|
|
166
|
+
}
|
|
167
|
+
$commit = & git -C $ProjectRoot log "$BaseCommit..HEAD" --oneline 2>$null | Select-Object -First 1
|
|
168
|
+
return -not [string]::IsNullOrWhiteSpace([string]$commit)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function ConvertTo-PrizmGitRelativePath {
|
|
172
|
+
param([string]$ProjectRoot, [string]$Path)
|
|
173
|
+
if (-not $Path) { return '' }
|
|
174
|
+
try {
|
|
175
|
+
$rootFull = [System.IO.Path]::GetFullPath($ProjectRoot).TrimEnd([char[]]@('\', '/')) + [System.IO.Path]::DirectorySeparatorChar
|
|
176
|
+
$pathInput = if ([System.IO.Path]::IsPathRooted($Path)) { $Path } else { Join-Path $ProjectRoot $Path }
|
|
177
|
+
$pathFull = [System.IO.Path]::GetFullPath($pathInput)
|
|
178
|
+
$rootUri = [System.Uri]::new($rootFull)
|
|
179
|
+
$pathUri = [System.Uri]::new($pathFull)
|
|
180
|
+
return ([System.Uri]::UnescapeDataString($rootUri.MakeRelativeUri($pathUri).ToString()) -replace '\\', '/').TrimStart([char[]]@('.', '/'))
|
|
181
|
+
} catch {
|
|
182
|
+
return ($Path -replace '\\', '/').TrimStart([char[]]@('.', '/'))
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function Get-PrizmGitDirtyPaths {
|
|
187
|
+
param([string]$ProjectRoot)
|
|
188
|
+
$lines = & git -C $ProjectRoot status --porcelain 2>$null
|
|
189
|
+
$paths = @()
|
|
190
|
+
foreach ($line in $lines) {
|
|
191
|
+
if ([string]::IsNullOrWhiteSpace($line) -or $line.Length -lt 4) { continue }
|
|
192
|
+
$path = $line.Substring(3).Trim()
|
|
193
|
+
if ($path -match ' -> ') { $path = ($path -split ' -> ')[-1].Trim() }
|
|
194
|
+
$paths += (($path -replace '\\', '/').Trim('"').TrimStart([char[]]@('.', '/')))
|
|
195
|
+
}
|
|
196
|
+
return @($paths)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function Test-PrizmPipelineBookkeepingPath {
|
|
200
|
+
param(
|
|
201
|
+
[string]$ProjectRoot,
|
|
202
|
+
[string]$Path,
|
|
203
|
+
[string]$StateDir,
|
|
204
|
+
[string]$ListPath
|
|
205
|
+
)
|
|
206
|
+
$normalizedPath = (($Path -replace '\\', '/').Trim('"').TrimStart([char[]]@('.', '/')))
|
|
207
|
+
$stateRel = ConvertTo-PrizmGitRelativePath $ProjectRoot $StateDir
|
|
208
|
+
$listRel = ConvertTo-PrizmGitRelativePath $ProjectRoot $ListPath
|
|
209
|
+
if ($stateRel -and ($normalizedPath -eq $stateRel -or $normalizedPath.StartsWith("$stateRel/"))) { return $true }
|
|
210
|
+
if ($listRel -and $normalizedPath -eq $listRel) { return $true }
|
|
211
|
+
return $false
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function Test-PrizmGitWorkDirty {
|
|
215
|
+
param([string]$ProjectRoot, [string]$StateDir, [string]$ListPath)
|
|
216
|
+
foreach ($path in (Get-PrizmGitDirtyPaths $ProjectRoot)) {
|
|
217
|
+
if (-not (Test-PrizmPipelineBookkeepingPath $ProjectRoot $path $StateDir $ListPath)) {
|
|
218
|
+
return $true
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return $false
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function Test-PrizmGitDirty {
|
|
225
|
+
param([string]$ProjectRoot)
|
|
226
|
+
$dirty = & git -C $ProjectRoot status --porcelain 2>$null | Select-Object -First 1
|
|
227
|
+
return -not [string]::IsNullOrWhiteSpace([string]$dirty)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function Invoke-PrizmGitAutoCommit {
|
|
231
|
+
param([string]$ProjectRoot, [string]$Message)
|
|
232
|
+
& git -C $ProjectRoot add -A *> $null
|
|
233
|
+
& git -C $ProjectRoot commit --no-verify -m $Message *> $null
|
|
234
|
+
return $LASTEXITCODE -eq 0
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function Invoke-PrizmGitIncludeRemainingArtifacts {
|
|
238
|
+
param([string]$ProjectRoot, [string]$ItemId)
|
|
239
|
+
& git -C $ProjectRoot add -A *> $null
|
|
240
|
+
& git -C $ProjectRoot commit --no-verify --amend --no-edit *> $null
|
|
241
|
+
if ($LASTEXITCODE -eq 0) { return }
|
|
242
|
+
& git -C $ProjectRoot commit --no-verify -m "chore($ItemId): include remaining session artifacts" *> $null
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function Invoke-PrizmGitIncludeBookkeepingArtifacts {
|
|
246
|
+
param([string]$ProjectRoot, [string]$StateDir, [string]$ListPath)
|
|
247
|
+
$listRel = ConvertTo-PrizmGitRelativePath $ProjectRoot $ListPath
|
|
248
|
+
if (-not $listRel) { return }
|
|
249
|
+
& git -C $ProjectRoot ls-files --error-unmatch -- $listRel *> $null
|
|
250
|
+
if ($LASTEXITCODE -ne 0) { return }
|
|
251
|
+
& git -C $ProjectRoot commit --no-verify --amend --no-edit --only -- $listRel *> $null
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function Invoke-PrizmPipelineItem {
|
|
255
|
+
param([string]$CurrentItemId)
|
|
256
|
+
$script:PRIZM_ITEM_EXIT_CODE = 0
|
|
257
|
+
|
|
258
|
+
$sessionId = New-PrizmSessionId $Kind
|
|
259
|
+
$runId = "run-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
|
|
260
|
+
$retryCount = '0'
|
|
261
|
+
$resumePhase = 'null'
|
|
262
|
+
$isGitRepository = if (-not $dryRun) { Test-PrizmGitRepository $paths.ProjectRoot } else { $false }
|
|
263
|
+
$baseCommit = if ($isGitRepository) { Get-PrizmGitHead $paths.ProjectRoot } else { '' }
|
|
264
|
+
$hadDirtyBaseline = if ($isGitRepository) { Test-PrizmGitWorkDirty $paths.ProjectRoot $stateDir $listPath } else { $false }
|
|
265
|
+
if ($hadDirtyBaseline) {
|
|
266
|
+
Write-PrizmWarn "Dirty working tree detected before pipeline bookkeeping; session success requires a new commit."
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if ($dryRun) {
|
|
270
|
+
$statusPath = Join-Path $stateDir "$($idName)s\$CurrentItemId\status.json"
|
|
271
|
+
if (Test-Path $statusPath) {
|
|
272
|
+
try {
|
|
273
|
+
$itemStatus = Get-Content -Raw $statusPath | ConvertFrom-Json
|
|
274
|
+
if ($itemStatus.retry_count -ne $null) { $retryCount = [string]$itemStatus.retry_count }
|
|
275
|
+
if ($itemStatus.resume_from_phase -ne $null) { $resumePhase = [string]$itemStatus.resume_from_phase }
|
|
276
|
+
} catch {
|
|
277
|
+
Write-PrizmWarn "Could not read dry-run status file: $statusPath"
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
} else {
|
|
281
|
+
$start = Invoke-PrizmPythonJson $python (@((Join-Path $paths.ScriptsDir $updateScript), $listOption, $listPath, '--state-dir', $stateDir, '--action', 'start', $idOption, $CurrentItemId, '--session-id', $sessionId) + $maxRetryArgs)
|
|
282
|
+
if ($start.retry_count -ne $null) { $retryCount = [string]$start.retry_count }
|
|
283
|
+
if ($start.resume_from_phase -ne $null) { $resumePhase = [string]$start.resume_from_phase }
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
$sessionDir = if ($dryRun) {
|
|
287
|
+
Join-Path ([System.IO.Path]::GetTempPath()) "prizmkit-dry-run\$sessionId"
|
|
288
|
+
} else {
|
|
289
|
+
Join-Path $stateDir "$($idName)s\$CurrentItemId\sessions\$sessionId"
|
|
290
|
+
}
|
|
291
|
+
$logsDir = Join-Path $sessionDir 'logs'
|
|
292
|
+
New-Item -ItemType Directory -Force -Path $logsDir | Out-Null
|
|
293
|
+
$promptPath = Join-Path $sessionDir 'bootstrap-prompt.md'
|
|
294
|
+
$sessionLog = Join-Path $logsDir 'session.log'
|
|
295
|
+
$pidPath = Join-Path $logsDir 'ai.pid'
|
|
296
|
+
|
|
297
|
+
$cli = $null
|
|
298
|
+
if (-not $dryRun) {
|
|
299
|
+
$cli = Resolve-PrizmAiCli $paths.ProjectRoot $paths.PrizmkitDir
|
|
300
|
+
$env:PRIZMKIT_PLATFORM = Get-PrizmPlatformFromProject $paths.ProjectRoot $paths.PrizmkitDir $cli
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
$promptArgs = @((Join-Path $paths.ScriptsDir $promptScript), $listOption, $listPath, $idOption, $CurrentItemId, '--session-id', $sessionId, '--run-id', $runId, '--retry-count', $retryCount, '--resume-phase', $resumePhase, '--state-dir', $stateDir, '--output', $promptPath)
|
|
304
|
+
if ($mode) { $promptArgs += @('--mode', $mode) }
|
|
305
|
+
if ($critic) { $promptArgs += @('--critic', $critic) }
|
|
306
|
+
if ($dryRun) { $promptArgs += '--no-checkpoint' }
|
|
307
|
+
if ($verboseEnabled) { Write-PrizmInfo "Prompt args: $($promptArgs -join ' ')" }
|
|
308
|
+
$promptResult = Invoke-PrizmPythonJson $python $promptArgs
|
|
309
|
+
$itemModel = ''
|
|
310
|
+
if ($promptResult -and $promptResult.PSObject.Properties['model'] -and $promptResult.model) {
|
|
311
|
+
$itemModel = [string]$promptResult.model
|
|
312
|
+
}
|
|
313
|
+
$effectiveModel = if ($itemModel) { $itemModel } else { $env:MODEL }
|
|
314
|
+
Write-PrizmSuccess "Generated prompt: $promptPath"
|
|
315
|
+
|
|
316
|
+
if ($dryRun) {
|
|
317
|
+
Get-Content $promptPath
|
|
318
|
+
$script:PRIZM_ITEM_EXIT_CODE = 0
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
Write-PrizmInfo "Starting $cli session for $CurrentItemId ($sessionId)"
|
|
323
|
+
|
|
324
|
+
$job = Start-Job -ScriptBlock {
|
|
325
|
+
param($commonPath, $cli, $promptPath, $sessionLog, $projectRoot, $model, $pidPath)
|
|
326
|
+
. $commonPath
|
|
327
|
+
Invoke-PrizmAiSession -CliCommand $cli -PromptPath $promptPath -LogPath $sessionLog -ProjectRoot $projectRoot -Model $model -PidPath $pidPath
|
|
328
|
+
} -ArgumentList (Join-Path $paths.PipelineDir 'lib\common.ps1'), $cli, $promptPath, $sessionLog, $paths.ProjectRoot, $effectiveModel, $pidPath
|
|
329
|
+
|
|
330
|
+
$completed = if ($timeoutSeconds -le 0) {
|
|
331
|
+
Wait-Job $job
|
|
332
|
+
} else {
|
|
333
|
+
Wait-Job $job -Timeout $timeoutSeconds
|
|
334
|
+
}
|
|
335
|
+
if (-not $completed) {
|
|
336
|
+
if (Test-Path $pidPath) {
|
|
337
|
+
$rawPid = Get-Content $pidPath -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
338
|
+
$aiPid = 0
|
|
339
|
+
if ([int]::TryParse($rawPid, [ref]$aiPid)) {
|
|
340
|
+
Stop-PrizmProcessTreeById -ProcessId $aiPid
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
Stop-Job $job
|
|
344
|
+
Remove-Job $job
|
|
345
|
+
Invoke-PrizmPythonText $python (@((Join-Path $paths.ScriptsDir $updateScript), $listOption, $listPath, '--state-dir', $stateDir, '--action', 'update', $idOption, $CurrentItemId, '--session-id', $sessionId, '--session-status', 'timed_out') + $maxRetryArgs)
|
|
346
|
+
throw "AI session timed out after $timeoutSeconds seconds. Log: $sessionLog"
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
$exitCode = [int](Receive-Job $job)
|
|
350
|
+
Remove-Job $job
|
|
351
|
+
$status = 'crashed'
|
|
352
|
+
if ($exitCode -ne 0) {
|
|
353
|
+
Write-PrizmWarn "AI session exited with code $exitCode"
|
|
354
|
+
} elseif (-not $isGitRepository) {
|
|
355
|
+
Write-PrizmWarn "AI session exited cleanly, but project is not a git repository; cannot verify work was committed."
|
|
356
|
+
} elseif (Test-PrizmGitCommitsSince $paths.ProjectRoot $baseCommit) {
|
|
357
|
+
$status = 'success'
|
|
358
|
+
} elseif ($hadDirtyBaseline) {
|
|
359
|
+
Write-PrizmWarn "AI session exited cleanly but produced no new commits; pre-existing dirty tree will not be auto-committed."
|
|
360
|
+
} elseif (Test-PrizmGitWorkDirty $paths.ProjectRoot $stateDir $listPath) {
|
|
361
|
+
Write-PrizmWarn "AI session exited cleanly but produced no commits; auto-committing dirty work tree."
|
|
362
|
+
if (Invoke-PrizmGitAutoCommit $paths.ProjectRoot "chore($CurrentItemId): auto-commit session work") {
|
|
363
|
+
$status = 'success'
|
|
364
|
+
} else {
|
|
365
|
+
Write-PrizmWarn "Auto-commit failed; treating session as crashed."
|
|
366
|
+
}
|
|
367
|
+
} else {
|
|
368
|
+
Write-PrizmWarn "AI session exited cleanly but produced no commits and no changes."
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
Invoke-PrizmPythonText $python (@((Join-Path $paths.ScriptsDir $updateScript), $listOption, $listPath, '--state-dir', $stateDir, '--action', 'update', $idOption, $CurrentItemId, '--session-id', $sessionId, '--session-status', $status) + $maxRetryArgs)
|
|
372
|
+
|
|
373
|
+
if ($status -eq 'success' -and (Test-PrizmGitDirty $paths.ProjectRoot)) {
|
|
374
|
+
if ($hadDirtyBaseline) {
|
|
375
|
+
Write-PrizmInfo "Auto-committing pipeline bookkeeping artifacts only."
|
|
376
|
+
Invoke-PrizmGitIncludeBookkeepingArtifacts $paths.ProjectRoot $stateDir $listPath
|
|
377
|
+
} else {
|
|
378
|
+
Write-PrizmInfo "Auto-committing remaining session artifacts."
|
|
379
|
+
Invoke-PrizmGitIncludeRemainingArtifacts $paths.ProjectRoot $CurrentItemId
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if ($status -eq 'success') {
|
|
384
|
+
Write-PrizmSuccess "$Kind item completed: $CurrentItemId"
|
|
385
|
+
} else {
|
|
386
|
+
Write-PrizmError "$Kind item failed: $CurrentItemId. Log: $sessionLog"
|
|
387
|
+
}
|
|
388
|
+
$script:PRIZM_ITEM_EXIT_CODE = if ($status -eq 'success') { 0 } else { 1 }
|
|
389
|
+
return
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if ($itemId) {
|
|
393
|
+
Invoke-PrizmPipelineItem $itemId
|
|
394
|
+
$global:PRIZM_EXIT_CODE = $script:PRIZM_ITEM_EXIT_CODE
|
|
395
|
+
return
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
$processedCount = 0
|
|
399
|
+
$lastExitCode = 0
|
|
400
|
+
while ($true) {
|
|
401
|
+
$getNextArgs = @((Join-Path $paths.ScriptsDir $updateScript), $listOption, $listPath, '--state-dir', $stateDir, '--action', 'get_next')
|
|
402
|
+
if ($featuresFilter -and $Kind -eq 'feature') { $getNextArgs += @('--features', $featuresFilter) }
|
|
403
|
+
$getNextOutput = (Invoke-PrizmPythonOutput $python $getNextArgs).Trim()
|
|
404
|
+
if (-not $getNextOutput -or $getNextOutput -eq 'PIPELINE_COMPLETE') {
|
|
405
|
+
if ($processedCount -eq 0) {
|
|
406
|
+
Write-PrizmSuccess "No pending $Kind items."
|
|
407
|
+
} else {
|
|
408
|
+
Write-PrizmSuccess "Processed $processedCount $Kind item(s)."
|
|
409
|
+
}
|
|
410
|
+
$global:PRIZM_EXIT_CODE = $lastExitCode
|
|
411
|
+
return
|
|
412
|
+
}
|
|
413
|
+
if ($getNextOutput -eq 'PIPELINE_BLOCKED') {
|
|
414
|
+
Write-PrizmWarn "All remaining $Kind items are blocked."
|
|
415
|
+
Write-PrizmWarn "Run .\run-$Kind.ps1 status to see details."
|
|
416
|
+
$global:PRIZM_EXIT_CODE = $lastExitCode
|
|
417
|
+
return
|
|
418
|
+
}
|
|
419
|
+
if ($getNextOutput -notmatch '^\s*[\{\[]') {
|
|
420
|
+
throw "Unexpected get_next output: $getNextOutput"
|
|
421
|
+
}
|
|
422
|
+
$next = $getNextOutput | ConvertFrom-Json
|
|
423
|
+
$nextItemId = $next.PSObject.Properties["${idName}_id"].Value
|
|
424
|
+
if (-not $nextItemId) {
|
|
425
|
+
if ($processedCount -eq 0) {
|
|
426
|
+
Write-PrizmSuccess "No pending $Kind items."
|
|
427
|
+
} else {
|
|
428
|
+
Write-PrizmSuccess "Processed $processedCount $Kind item(s)."
|
|
429
|
+
}
|
|
430
|
+
$global:PRIZM_EXIT_CODE = $lastExitCode
|
|
431
|
+
return
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
$processedCount++
|
|
435
|
+
Invoke-PrizmPipelineItem $nextItemId
|
|
436
|
+
$lastExitCode = $script:PRIZM_ITEM_EXIT_CODE
|
|
437
|
+
if ($dryRun) {
|
|
438
|
+
$global:PRIZM_EXIT_CODE = $lastExitCode
|
|
439
|
+
return
|
|
440
|
+
}
|
|
441
|
+
if ($lastExitCode -ne 0 -and $env:STOP_ON_FAILURE -eq '1') {
|
|
442
|
+
$global:PRIZM_EXIT_CODE = $lastExitCode
|
|
443
|
+
return
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
. "$PSScriptRoot\common.ps1"
|
|
2
|
+
|
|
3
|
+
function Get-PrizmFeatureSlug {
|
|
4
|
+
param([string]$ListPath, [string]$FeatureId)
|
|
5
|
+
$data = Get-Content $ListPath -Raw | ConvertFrom-Json
|
|
6
|
+
$feature = $data.features | Where-Object { $_.id -eq $FeatureId } | Select-Object -First 1
|
|
7
|
+
if (-not $feature) { throw "Feature not found in list: $FeatureId" }
|
|
8
|
+
$numeric = ($FeatureId -replace '^[Ff]-', '').PadLeft(3, '0')
|
|
9
|
+
$slug = ([string]$feature.title).ToLowerInvariant()
|
|
10
|
+
$slug = $slug -replace '[^a-z0-9\s-]', ''
|
|
11
|
+
$slug = ($slug.Trim() -replace '\s+', '-')
|
|
12
|
+
$slug = ($slug -replace '-+', '-').Trim('-')
|
|
13
|
+
if (-not $slug) { $slug = 'feature' }
|
|
14
|
+
return "$numeric-$slug"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function Invoke-PrizmReset {
|
|
18
|
+
param(
|
|
19
|
+
[ValidateSet('feature','bugfix','refactor')][string]$Kind,
|
|
20
|
+
[string]$ScriptRoot,
|
|
21
|
+
[string[]]$Args
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
$global:PRIZM_RESET_EXIT_CODE = 0
|
|
25
|
+
$paths = Initialize-PrizmPaths $ScriptRoot
|
|
26
|
+
Set-Location $paths.ProjectRoot
|
|
27
|
+
$python = Resolve-PrizmPython
|
|
28
|
+
|
|
29
|
+
$idOption = @{ feature = '--feature-id'; bugfix = '--bug-id'; refactor = '--refactor-id' }[$Kind]
|
|
30
|
+
$listOption = @{ feature = '--feature-list'; bugfix = '--bug-list'; refactor = '--refactor-list' }[$Kind]
|
|
31
|
+
$updateScript = @{ feature = 'update-feature-status.py'; bugfix = 'update-bug-status.py'; refactor = 'update-refactor-status.py' }[$Kind]
|
|
32
|
+
$runScript = @{ feature = 'run-feature.ps1'; bugfix = 'run-bugfix.ps1'; refactor = 'run-refactor.ps1' }[$Kind]
|
|
33
|
+
$idPattern = @{ feature = '^[Ff]-\d+'; bugfix = '^[Bb]-\d+'; refactor = '^[Rr]-\d+' }[$Kind]
|
|
34
|
+
$label = @{ feature = 'feature'; bugfix = 'bug'; refactor = 'refactor' }[$Kind]
|
|
35
|
+
$listPath = Get-PrizmListDefault $Kind $paths.ProjectRoot
|
|
36
|
+
$stateDir = Get-PrizmStateDir $Kind $paths.ProjectRoot
|
|
37
|
+
$itemId = $null
|
|
38
|
+
$clean = $false
|
|
39
|
+
$runAfterReset = $false
|
|
40
|
+
|
|
41
|
+
foreach ($arg in $Args) {
|
|
42
|
+
switch -Regex ($arg) {
|
|
43
|
+
'^--clean$' { $clean = $true; continue }
|
|
44
|
+
'^--run$' { $runAfterReset = $true; continue }
|
|
45
|
+
'^(--help|-h)$' {
|
|
46
|
+
Write-Host "Usage: .\reset-$label.ps1 [$($label)-id] [--clean] [--run] [list-path]"
|
|
47
|
+
Write-Host " No ID clears the whole $Kind pipeline state."
|
|
48
|
+
Write-Host " With an ID, reset only that item. --clean removes item artifacts. --run retries it immediately."
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
'^-.*' { Write-PrizmWarn "Ignoring unsupported option: $arg"; continue }
|
|
52
|
+
default {
|
|
53
|
+
if (-not $itemId -and $arg -match $idPattern) {
|
|
54
|
+
$itemId = $arg.ToUpperInvariant()
|
|
55
|
+
} else {
|
|
56
|
+
$listPath = $arg
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (-not $itemId) {
|
|
63
|
+
& (Join-Path $paths.PipelineDir $runScript) reset
|
|
64
|
+
$global:PRIZM_RESET_EXIT_CODE = $LASTEXITCODE
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (-not (Test-Path $listPath)) { throw "List file not found: $listPath" }
|
|
69
|
+
New-Item -ItemType Directory -Force -Path $stateDir | Out-Null
|
|
70
|
+
|
|
71
|
+
$action = if ($clean) { 'clean' } else { 'reset' }
|
|
72
|
+
$resetArgs = @((Join-Path $paths.ScriptsDir $updateScript), $listOption, $listPath, '--state-dir', $stateDir, '--action', $action, $idOption, $itemId)
|
|
73
|
+
if ($clean) {
|
|
74
|
+
$resetArgs += @('--project-root', $paths.ProjectRoot)
|
|
75
|
+
if ($Kind -eq 'feature') {
|
|
76
|
+
$resetArgs += @('--feature-slug', (Get-PrizmFeatureSlug $listPath $itemId))
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Invoke-PrizmPythonText $python $resetArgs
|
|
81
|
+
Write-PrizmSuccess "Reset $label item: $itemId"
|
|
82
|
+
|
|
83
|
+
if ($runAfterReset) {
|
|
84
|
+
& (Join-Path $paths.PipelineDir $runScript) run $itemId $listPath
|
|
85
|
+
$global:PRIZM_RESET_EXIT_CODE = $LASTEXITCODE
|
|
86
|
+
}
|
|
87
|
+
}
|