prizmkit 1.1.153 → 1.1.155
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/adapters/codex/skill-adapter.js +1 -1
- package/bundled/adapters/pi/skill-adapter.js +2 -1
- package/bundled/dev-pipeline/.env.example +2 -6
- package/bundled/dev-pipeline/README.md +120 -113
- package/bundled/dev-pipeline/assets/skill-subagent-integration.md +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +355 -15
- package/bundled/dev-pipeline/prizmkit_runtime/cli.py +189 -110
- package/bundled/dev-pipeline/prizmkit_runtime/commands.py +146 -111
- package/bundled/dev-pipeline/prizmkit_runtime/daemon.py +4 -20
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +48 -14
- package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +0 -1
- package/bundled/dev-pipeline/prizmkit_runtime/paths.py +0 -3
- package/bundled/dev-pipeline/prizmkit_runtime/reset.py +122 -15
- package/bundled/dev-pipeline/prizmkit_runtime/reset_preserve.py +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +50 -2
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +1 -10
- package/bundled/dev-pipeline/prizmkit_runtime/runner_prompts.py +3 -3
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +139 -74
- package/bundled/dev-pipeline/prizmkit_runtime/runtime_commit.py +482 -0
- package/bundled/dev-pipeline/prizmkit_runtime/status.py +106 -1
- package/bundled/dev-pipeline/prizmkit_runtime/test_result.py +64 -11
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +145 -367
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +91 -62
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +105 -69
- package/bundled/dev-pipeline/scripts/init-bugfix-pipeline.py +2 -2
- package/bundled/dev-pipeline/scripts/prompt_framework.py +36 -41
- package/bundled/dev-pipeline/scripts/update-bug-status.py +197 -71
- package/bundled/dev-pipeline/scripts/update-checkpoint.py +77 -16
- package/bundled/dev-pipeline/scripts/update-feature-status.py +46 -121
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +46 -123
- package/bundled/dev-pipeline/scripts/utils.py +119 -0
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +22 -27
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +71 -104
- package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +3 -2
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +59 -111
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +37 -72
- package/bundled/dev-pipeline/templates/sections/artifact-manifest.md +39 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-critical-paths.md +3 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-commit-report.md +8 -25
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +3 -3
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-implement.md +2 -2
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +3 -3
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +2 -2
- package/bundled/dev-pipeline/templates/sections/bugfix-reminders.md +4 -4
- package/bundled/dev-pipeline/templates/sections/bugfix-session-context.md +0 -1
- package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +7 -4
- package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +14 -27
- package/bundled/dev-pipeline/templates/sections/feature-completion-summary.md +20 -0
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +8 -71
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +7 -7
- package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +3 -3
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +4 -4
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +5 -17
- package/bundled/dev-pipeline/templates/sections/refactor-critical-paths.md +3 -1
- package/bundled/dev-pipeline/templates/sections/refactor-phase-commit-report.md +10 -23
- package/bundled/dev-pipeline/templates/sections/refactor-phase-implement.md +2 -2
- package/bundled/dev-pipeline/templates/sections/refactor-phase-plan.md +2 -2
- package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +3 -3
- package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +2 -2
- package/bundled/dev-pipeline/templates/sections/refactor-reminders.md +7 -7
- package/bundled/dev-pipeline/templates/sections/refactor-session-context.md +0 -1
- package/bundled/dev-pipeline/templates/sections/retrospective.md +12 -0
- package/bundled/dev-pipeline/templates/sections/runtime-commit-handoff.md +35 -0
- package/bundled/dev-pipeline/templates/session-status-schema.json +45 -62
- package/bundled/dev-pipeline/tests/test_auto_skip.py +111 -32
- package/bundled/dev-pipeline/tests/test_checkpoint_state.py +617 -30
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +262 -194
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +62 -23
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +76 -28
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +281 -206
- package/bundled/dev-pipeline/tests/test_recovery_workflow.py +322 -0
- package/bundled/dev-pipeline/tests/test_reset_modes.py +935 -0
- package/bundled/dev-pipeline/tests/test_reset_preserve.py +5 -7
- package/bundled/dev-pipeline/tests/test_runtime_commit.py +608 -0
- package/bundled/dev-pipeline/tests/test_unified_cli.py +617 -188
- package/bundled/skills/_metadata.json +9 -9
- package/bundled/skills/app-planner/SKILL.md +3 -3
- package/bundled/skills/app-planner/references/generated-plan-review.md +2 -1
- package/bundled/skills/app-planner/references/rules/backend/derivation-rules.md +56 -64
- package/bundled/skills/app-planner/references/rules/backend/fixed-rules.md +85 -240
- package/bundled/skills/app-planner/references/rules/backend/question-bank.md +25 -25
- package/bundled/skills/app-planner/references/rules/backend/question-manifest.json +16 -7
- package/bundled/skills/app-planner/references/rules/backend/template.md +74 -92
- package/bundled/skills/app-planner/references/rules/database/derivation-rules.md +93 -93
- package/bundled/skills/app-planner/references/rules/database/fixed-rules.md +66 -173
- package/bundled/skills/app-planner/references/rules/database/question-bank.md +31 -26
- package/bundled/skills/app-planner/references/rules/database/question-manifest.json +13 -4
- package/bundled/skills/app-planner/references/rules/database/template.md +76 -98
- package/bundled/skills/app-planner/references/rules/frontend/derivation-rules.md +68 -95
- package/bundled/skills/app-planner/references/rules/frontend/fixed-rules.md +93 -166
- package/bundled/skills/app-planner/references/rules/frontend/question-bank.md +46 -36
- package/bundled/skills/app-planner/references/rules/frontend/question-manifest.json +11 -2
- package/bundled/skills/app-planner/references/rules/frontend/template.md +79 -257
- package/bundled/skills/app-planner/references/rules/mobile/derivation-rules.md +91 -99
- package/bundled/skills/app-planner/references/rules/mobile/fixed-rules.md +73 -246
- package/bundled/skills/app-planner/references/rules/mobile/question-bank.md +17 -17
- package/bundled/skills/app-planner/references/rules/mobile/question-manifest.json +17 -2
- package/bundled/skills/app-planner/references/rules/mobile/template.md +73 -85
- package/bundled/skills/app-planner/references/rules-configuration.md +133 -65
- package/bundled/skills/app-planner/scripts/validate-rules-configuration.py +291 -0
- package/bundled/skills/bug-planner/references/schema-validation.md +1 -1
- package/bundled/skills/bug-planner/scripts/validate-bug-list.py +1 -1
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +21 -13
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +22 -14
- package/bundled/skills/prizmkit/SKILL.md +17 -18
- package/bundled/skills/prizmkit/references/workflow-state-protocol.md +22 -18
- package/bundled/skills/prizmkit-code-review/SKILL.md +22 -58
- package/bundled/skills/prizmkit-code-review/references/independent-code-review.md +6 -6
- package/bundled/skills/prizmkit-code-review/references/review-report-template.md +2 -2
- package/bundled/skills/prizmkit-committer/SKILL.md +82 -132
- package/bundled/skills/prizmkit-deploy/SKILL.md +27 -11
- package/bundled/skills/prizmkit-deploy/assets/deploy-document-template.md +89 -0
- package/bundled/skills/prizmkit-deploy/references/ci-cd-workflows.md +44 -14
- package/bundled/skills/prizmkit-deploy/references/cloud-platform-deploy.md +19 -3
- package/bundled/skills/prizmkit-deploy/references/database-setup.md +22 -12
- package/bundled/skills/prizmkit-deploy/references/deploy-config-schema.md +3 -1
- package/bundled/skills/prizmkit-deploy/references/deploy-history-schema.md +22 -6
- package/bundled/skills/prizmkit-deploy/references/deploy-metadata-schema.json +49 -0
- package/bundled/skills/prizmkit-deploy/references/live-validation-notes.md +1 -1
- package/bundled/skills/prizmkit-deploy/references/pending-input-schema.json +164 -0
- package/bundled/skills/prizmkit-deploy/references/ssh-adapter-flow.md +9 -8
- package/bundled/skills/prizmkit-deploy/references/ssh-execution-flow.md +6 -6
- package/bundled/skills/prizmkit-implement/SKILL.md +30 -66
- package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +4 -4
- package/bundled/skills/prizmkit-init/SKILL.md +4 -0
- package/bundled/skills/prizmkit-plan/SKILL.md +21 -64
- package/bundled/skills/prizmkit-plan/references/artifact-identity.md +38 -0
- package/bundled/skills/prizmkit-plan/references/independent-plan-review.md +4 -4
- package/bundled/skills/prizmkit-plan/references/review-plan-spec-loop.md +5 -5
- package/bundled/skills/prizmkit-prizm-docs/SKILL.md +11 -1
- package/bundled/skills/prizmkit-retrospective/SKILL.md +63 -77
- package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +3 -3
- package/bundled/skills/prizmkit-retrospective/references/retrospective-result-schema.json +138 -0
- package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +17 -23
- package/bundled/skills/prizmkit-test/SKILL.md +64 -51
- package/bundled/skills/prizmkit-test/references/boundary-contract-and-test-double-guidance.md +206 -0
- package/bundled/skills/prizmkit-test/references/independent-test-review.md +17 -9
- package/bundled/skills/prizmkit-test/references/test-coverage-model.md +45 -21
- package/bundled/skills/prizmkit-test/references/test-report-template.md +20 -13
- package/bundled/skills/prizmkit-workflow/SKILL.md +35 -37
- package/bundled/skills/prizmkit-workflow/references/artifact-identity.md +34 -0
- package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +22 -18
- package/bundled/skills/recovery-workflow/SKILL.md +9 -6
- package/bundled/skills/recovery-workflow/evals/evals.json +2 -2
- package/bundled/skills/recovery-workflow/references/detection.md +7 -6
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +16 -6
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +21 -13
- package/bundled/templates/project-memory-template.md +19 -11
- package/package.json +1 -1
- package/src/scaffold.js +33 -22
- package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +0 -228
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +0 -767
- package/bundled/dev-pipeline/templates/sections/bugfix-artifacts.md +0 -16
- package/bundled/dev-pipeline/templates/sections/bugfix-session-status.md +0 -31
- package/bundled/dev-pipeline/templates/sections/critical-paths-agent.md +0 -7
- package/bundled/dev-pipeline/templates/sections/critical-paths-lite.md +0 -7
- package/bundled/dev-pipeline/templates/sections/directory-convention-agent.md +0 -9
- package/bundled/dev-pipeline/templates/sections/directory-convention-lite.md +0 -6
- package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +0 -15
- package/bundled/dev-pipeline/templates/sections/phase-commit.md +0 -61
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +0 -24
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +0 -12
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +0 -44
- package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +0 -25
- package/bundled/dev-pipeline/templates/sections/phase-plan-agent.md +0 -20
- package/bundled/dev-pipeline/templates/sections/phase-plan-lite.md +0 -20
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +0 -20
- package/bundled/dev-pipeline/templates/sections/refactor-artifacts.md +0 -17
- package/bundled/dev-pipeline/templates/sections/refactor-session-status.md +0 -28
- package/bundled/dev-pipeline/templates/sections/test-failure-recovery-agent.md +0 -52
- package/bundled/dev-pipeline/templates/sections/test-failure-recovery-lite.md +0 -52
- package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-test/references/external-contract-mock-guidance.md +0 -119
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
|
|
6
6
|
import argparse
|
|
7
7
|
import json
|
|
8
|
+
import re
|
|
8
9
|
import subprocess
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
|
|
@@ -14,19 +15,19 @@ FAMILIES = (
|
|
|
14
15
|
"name": "feature",
|
|
15
16
|
"state_dir": "features",
|
|
16
17
|
"plan_path": ".prizmkit/plans/feature-list.json",
|
|
17
|
-
"
|
|
18
|
+
"id_pattern": r"^F-\d{3}$",
|
|
18
19
|
},
|
|
19
20
|
{
|
|
20
21
|
"name": "bugfix",
|
|
21
22
|
"state_dir": "bugfix",
|
|
22
23
|
"plan_path": ".prizmkit/plans/bug-fix-list.json",
|
|
23
|
-
"
|
|
24
|
+
"id_pattern": r"^B-\d{3}$",
|
|
24
25
|
},
|
|
25
26
|
{
|
|
26
27
|
"name": "refactor",
|
|
27
28
|
"state_dir": "refactor",
|
|
28
29
|
"plan_path": ".prizmkit/plans/refactor-list.json",
|
|
29
|
-
"
|
|
30
|
+
"id_pattern": r"^R-\d{3}$",
|
|
30
31
|
},
|
|
31
32
|
)
|
|
32
33
|
|
|
@@ -66,7 +67,7 @@ def current_branch(project_root: Path) -> str:
|
|
|
66
67
|
|
|
67
68
|
|
|
68
69
|
def command_for(family: dict[str, str], task_id: str) -> str:
|
|
69
|
-
return f"python3 ./.prizmkit/dev-pipeline/cli.py {family['name']}
|
|
70
|
+
return f"python3 ./.prizmkit/dev-pipeline/cli.py run {family['name']} {task_id}"
|
|
70
71
|
|
|
71
72
|
|
|
72
73
|
def detect_candidates(project_root: Path) -> list[dict[str, object]]:
|
|
@@ -83,9 +84,18 @@ def detect_candidates(project_root: Path) -> list[dict[str, object]]:
|
|
|
83
84
|
data = load_json(checkout_path)
|
|
84
85
|
if not data or str(data.get("state") or "") != "active":
|
|
85
86
|
continue
|
|
86
|
-
|
|
87
|
+
raw_task_id = data.get("task_id")
|
|
88
|
+
if not isinstance(raw_task_id, str):
|
|
89
|
+
continue
|
|
90
|
+
task_id = raw_task_id.strip()
|
|
87
91
|
branch = str(data.get("active_dev_branch") or "").strip()
|
|
88
|
-
if
|
|
92
|
+
if (
|
|
93
|
+
task_id != raw_task_id
|
|
94
|
+
or data.get("task_type") != family["name"]
|
|
95
|
+
or checkout_path.parent.name != task_id
|
|
96
|
+
or re.fullmatch(family["id_pattern"], task_id) is None
|
|
97
|
+
or not branch_exists(project_root, branch)
|
|
98
|
+
):
|
|
89
99
|
continue
|
|
90
100
|
candidates.append(
|
|
91
101
|
{
|
|
@@ -79,7 +79,7 @@ If the project instruction files or package metadata name a baseline test comman
|
|
|
79
79
|
|
|
80
80
|
Use one `AskUserQuestion` call with exactly one question:
|
|
81
81
|
|
|
82
|
-
- **Foreground (Recommended)** — build `refactor
|
|
82
|
+
- **Foreground (Recommended)** — build `run refactor`; output remains attached to the user's terminal.
|
|
83
83
|
- **Background daemon** — build `daemon refactor start`; the runtime manages detached execution and logs.
|
|
84
84
|
|
|
85
85
|
Wait for the response before asking configuration.
|
|
@@ -113,7 +113,7 @@ Foreground template:
|
|
|
113
113
|
|
|
114
114
|
```bash
|
|
115
115
|
VERBOSE=1 MAX_RETRIES=3 STRICT_BEHAVIOR_CHECK=1 \
|
|
116
|
-
python3 ./.prizmkit/dev-pipeline/cli.py refactor
|
|
116
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run refactor .prizmkit/plans/refactor-list.json
|
|
117
117
|
```
|
|
118
118
|
|
|
119
119
|
Background template:
|
|
@@ -131,7 +131,7 @@ Return:
|
|
|
131
131
|
|
|
132
132
|
```bash
|
|
133
133
|
python3 ./.prizmkit/dev-pipeline/cli.py daemon refactor status
|
|
134
|
-
python3 ./.prizmkit/dev-pipeline/cli.py refactor
|
|
134
|
+
python3 ./.prizmkit/dev-pipeline/cli.py status refactor .prizmkit/plans/refactor-list.json
|
|
135
135
|
```
|
|
136
136
|
|
|
137
137
|
Do not infer completion from the plan file alone when runtime state may exist.
|
|
@@ -164,39 +164,47 @@ Optionally include the daemon status command for the user to run afterward. Do n
|
|
|
164
164
|
|
|
165
165
|
## Retry Command
|
|
166
166
|
|
|
167
|
-
Reset
|
|
167
|
+
Reset never starts execution and rejects `--run`. Every reset command must select exactly one mode: `--state-only`, `--fresh-checkout`, `--clean`, or `--preserve-runtime`. Always return reset and run as separate commands.
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
State-only recovery resets selected failed/skipped roots and only causal `auto_skipped` descendants whose complete blocker set is recovered. It resets retry/session/continuation state but never changes checkout identity, branches, worktrees, source changes, checkpoints, or artifact files. Selecting an auto-skipped refactor includes all of its transitive blocking roots; `--all` recovers all eligible chains. Fresh-checkout performs the former standard reset, can discard uncommitted changes on the active task branch, and records a new checkout boundary. Clean additionally deletes the task branch and clears task history/artifacts. Preserve-runtime scans the whole list for failed roots, retains runtime history and valid checkout/WIP, and alone may replace a missing branch from the recorded base and rewind implementation-and-later checkpoints. Never hide these effects behind the word “retry.”
|
|
170
170
|
|
|
171
171
|
Use `AskUserQuestion` before constructing retry commands:
|
|
172
172
|
|
|
173
|
-
- **
|
|
174
|
-
- **
|
|
173
|
+
- **State-only causal recovery** — recover one refactor closure, or all closures with `--all`, while preserving Git/checkouts/checkpoints/artifact files.
|
|
174
|
+
- **Preserve failed runtime (Recommended for continuing failed work)** — recover all failed chains without cleanup while retaining history and continuation evidence; disclose that this is whole-list failed-only recovery.
|
|
175
|
+
- **Fresh-checkout retry** — reset one refactor, then run it separately; warn that the active task branch can lose uncommitted changes.
|
|
175
176
|
- **Clean retry** — reset one refactor, delete its task branch/history/artifacts, then run it separately.
|
|
176
177
|
- **Cancel** — return no reset or run command.
|
|
177
178
|
|
|
179
|
+
State-only template:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
python3 ./.prizmkit/dev-pipeline/cli.py reset refactor R-001 --state-only .prizmkit/plans/refactor-list.json
|
|
183
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run refactor R-001 .prizmkit/plans/refactor-list.json
|
|
184
|
+
```
|
|
185
|
+
|
|
178
186
|
Preserve-runtime template:
|
|
179
187
|
|
|
180
188
|
```bash
|
|
181
189
|
python3 ./.prizmkit/dev-pipeline/cli.py reset refactor --failed --preserve-runtime .prizmkit/plans/refactor-list.json
|
|
182
|
-
python3 ./.prizmkit/dev-pipeline/cli.py refactor
|
|
190
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run refactor .prizmkit/plans/refactor-list.json
|
|
183
191
|
```
|
|
184
192
|
|
|
185
|
-
|
|
193
|
+
Fresh-checkout template:
|
|
186
194
|
|
|
187
195
|
```bash
|
|
188
|
-
python3 ./.prizmkit/dev-pipeline/cli.py reset refactor R-001 .prizmkit/plans/refactor-list.json
|
|
189
|
-
python3 ./.prizmkit/dev-pipeline/cli.py refactor
|
|
196
|
+
python3 ./.prizmkit/dev-pipeline/cli.py reset refactor R-001 --fresh-checkout .prizmkit/plans/refactor-list.json
|
|
197
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run refactor R-001 .prizmkit/plans/refactor-list.json
|
|
190
198
|
```
|
|
191
199
|
|
|
192
200
|
Clean template:
|
|
193
201
|
|
|
194
202
|
```bash
|
|
195
203
|
python3 ./.prizmkit/dev-pipeline/cli.py reset refactor R-001 --clean .prizmkit/plans/refactor-list.json
|
|
196
|
-
python3 ./.prizmkit/dev-pipeline/cli.py refactor
|
|
204
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run refactor R-001 .prizmkit/plans/refactor-list.json
|
|
197
205
|
```
|
|
198
206
|
|
|
199
|
-
|
|
207
|
+
State-only requires exactly one ID or explicit `--all` and rejects ranges/status filters. Fresh-checkout and clean accept one ID, range, or supported status filter. Preserve-runtime requires `--failed`, scans the whole list, and rejects IDs, ranges, `--all`, clean, other modes, and other filters. Retry-budget flags belong on the run command, never reset. For fresh-checkout or clean retry, require explicit acknowledgement of possible uncommitted-change loss; clean additionally requires acknowledgement of branch/history/artifact deletion.
|
|
200
208
|
|
|
201
209
|
## Output Contract
|
|
202
210
|
|
|
@@ -44,18 +44,22 @@ Placeholders: `<family> = feature | bugfix | refactor`; `<task-id> = F-NNN | B-N
|
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
46
|
# Run/resume the whole list or one task
|
|
47
|
-
python3 ./.prizmkit/dev-pipeline/cli.py <family>
|
|
48
|
-
python3 ./.prizmkit/dev-pipeline/cli.py <family>
|
|
47
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run <family> [<list>] [--max-retries N] [--max-infra-retries N]
|
|
48
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run <family> <task-id> [<list>] [--max-retries N] [--max-infra-retries N]
|
|
49
49
|
|
|
50
|
-
# Inspect
|
|
51
|
-
python3 ./.prizmkit/dev-pipeline/cli.py <family>
|
|
52
|
-
python3 ./.prizmkit/dev-pipeline/cli.py
|
|
53
|
-
python3 ./.prizmkit/dev-pipeline/cli.py recovery detect
|
|
50
|
+
# Inspect task state
|
|
51
|
+
python3 ./.prizmkit/dev-pipeline/cli.py status <family> [<list>]
|
|
52
|
+
python3 ./.prizmkit/dev-pipeline/cli.py status all
|
|
54
53
|
|
|
55
|
-
#
|
|
56
|
-
python3 ./.prizmkit/dev-pipeline/cli.py
|
|
54
|
+
# Inspect runtime and AI CLI resolution
|
|
55
|
+
python3 ./.prizmkit/dev-pipeline/cli.py diagnostics ai-cli
|
|
56
|
+
|
|
57
|
+
# Reset state (exactly one explicit mode; never starts execution)
|
|
58
|
+
python3 ./.prizmkit/dev-pipeline/cli.py reset <family> <task-id> --state-only [<list>]
|
|
59
|
+
python3 ./.prizmkit/dev-pipeline/cli.py reset <family> --state-only --all [<list>]
|
|
60
|
+
python3 ./.prizmkit/dev-pipeline/cli.py reset <family> <task-id-or-range> --fresh-checkout [<list>]
|
|
61
|
+
python3 ./.prizmkit/dev-pipeline/cli.py reset <family> <task-id-or-range> --clean [<list>]
|
|
57
62
|
python3 ./.prizmkit/dev-pipeline/cli.py reset <family> --failed --preserve-runtime [<list>]
|
|
58
|
-
python3 ./.prizmkit/dev-pipeline/cli.py reset <family> <task-id> --clean [<list>]
|
|
59
63
|
|
|
60
64
|
# Background operation
|
|
61
65
|
python3 ./.prizmkit/dev-pipeline/cli.py daemon <family> start [<list>]
|
|
@@ -64,10 +68,14 @@ python3 ./.prizmkit/dev-pipeline/cli.py daemon <family> logs [--lines N|--follow
|
|
|
64
68
|
python3 ./.prizmkit/dev-pipeline/cli.py daemon <family> stop
|
|
65
69
|
```
|
|
66
70
|
|
|
71
|
+
Interrupted-task recovery: invoke `recovery-workflow`. Its read-only detector reports active checkout candidates and returns the ordinary `python3 ./.prizmkit/dev-pipeline/cli.py run <family> <task-id>` command without executing it.
|
|
72
|
+
|
|
67
73
|
Runtime boundaries:
|
|
68
74
|
- Reset and run are separate operations; reset rejects `--run`. Retry ceilings belong to `run`, not reset.
|
|
69
|
-
- `--
|
|
70
|
-
-
|
|
75
|
+
- Exactly one of `--state-only`, `--fresh-checkout`, `--clean`, or `--preserve-runtime` is required. Missing/conflicting modes fail before state or Git mutation.
|
|
76
|
+
- State-only accepts one ID or explicit `--all`; it resets causal dependency state while preserving checkout identity, branches/worktrees, source changes, checkpoints, and artifact files.
|
|
77
|
+
- Fresh-checkout and clean accept an ID, range, or supported status filter. Fresh-checkout publishes a new checkout boundary; clean additionally deletes branch/history/artifact resources.
|
|
78
|
+
- Preserve-runtime requires `--failed`, scans the whole list, and rejects IDs, ranges, `--all`, clean, other modes, and other filters. It retains runtime history and valid checkout/WIP; a missing branch is replaced from the recorded base with implementation-and-later checkpoint rewind.
|
|
71
79
|
- In zsh/Bash multiline commands, `\` must be the final character on the line with no trailing space.
|
|
72
80
|
|
|
73
81
|
### Lightweight Planning Path
|
package/package.json
CHANGED
package/src/scaffold.js
CHANGED
|
@@ -13,7 +13,8 @@ import path from 'path';
|
|
|
13
13
|
import { readFileSync } from 'fs';
|
|
14
14
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
15
15
|
import { dirname, join } from 'path';
|
|
16
|
-
import { execSync } from 'child_process';
|
|
16
|
+
import { execFileSync, execSync } from 'child_process';
|
|
17
|
+
import os from 'os';
|
|
17
18
|
import {
|
|
18
19
|
loadMetadata,
|
|
19
20
|
getTemplatesDir,
|
|
@@ -1373,6 +1374,19 @@ export async function installPlaywrightCli(projectRoot, dryRun) {
|
|
|
1373
1374
|
// OpenCLI 安装
|
|
1374
1375
|
// ============================================================
|
|
1375
1376
|
|
|
1377
|
+
/**
|
|
1378
|
+
* Side-effect-free OpenCLI presence check. Running OpenCLI itself is unsafe here:
|
|
1379
|
+
* browser-oriented commands such as `opencli doctor` create a live browser session.
|
|
1380
|
+
*/
|
|
1381
|
+
function isOpenCliAvailable() {
|
|
1382
|
+
try {
|
|
1383
|
+
execFileSync(os.platform() === 'win32' ? 'where.exe' : 'which', ['opencli'], { stdio: 'ignore' });
|
|
1384
|
+
return true;
|
|
1385
|
+
} catch {
|
|
1386
|
+
return false;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1376
1390
|
/**
|
|
1377
1391
|
* 安装 opencli(全局 npm 包)
|
|
1378
1392
|
* 用于浏览器交互验证,复用 Chrome 已登录会话(OAuth/SSO/第三方集成)
|
|
@@ -1383,12 +1397,11 @@ export async function installOpenCli(projectRoot, dryRun, autoDownload = true) {
|
|
|
1383
1397
|
return;
|
|
1384
1398
|
}
|
|
1385
1399
|
|
|
1386
|
-
//
|
|
1387
|
-
|
|
1388
|
-
|
|
1400
|
+
// Detect the executable without running it. This keeps repeated PrizmKit
|
|
1401
|
+
// installs/upgrades idempotent and avoids starting an OpenCLI browser session.
|
|
1402
|
+
if (isOpenCliAvailable()) {
|
|
1389
1403
|
console.log(chalk.green(' ✓ opencli (already installed)'));
|
|
1390
|
-
}
|
|
1391
|
-
// Not installed, install it
|
|
1404
|
+
} else {
|
|
1392
1405
|
try {
|
|
1393
1406
|
console.log(chalk.blue(' ⏳ Installing @jackwener/opencli globally...'));
|
|
1394
1407
|
execSync('npm install -g @jackwener/opencli@latest', { stdio: 'pipe', timeout: 120000 });
|
|
@@ -1400,22 +1413,20 @@ export async function installOpenCli(projectRoot, dryRun, autoDownload = true) {
|
|
|
1400
1413
|
}
|
|
1401
1414
|
}
|
|
1402
1415
|
|
|
1403
|
-
//
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
} catch {
|
|
1418
|
-
// doctor command itself failed — already warned about install issues
|
|
1416
|
+
// Check only PrizmKit's downloaded extension files. Connectivity checks are
|
|
1417
|
+
// intentionally user-initiated because `opencli doctor` opens a browser tab.
|
|
1418
|
+
const extensionManifest = path.join(
|
|
1419
|
+
process.env.HOME || process.env.USERPROFILE || '.',
|
|
1420
|
+
'.opencli',
|
|
1421
|
+
'extension',
|
|
1422
|
+
'manifest.json',
|
|
1423
|
+
);
|
|
1424
|
+
if (fs.pathExistsSync(extensionManifest)) {
|
|
1425
|
+
console.log(chalk.green(' ✓ OpenCLI extension files (already downloaded)'));
|
|
1426
|
+
} else if (autoDownload) {
|
|
1427
|
+
await downloadOpenCliExtension();
|
|
1428
|
+
} else {
|
|
1429
|
+
printOpenCliManualSteps();
|
|
1419
1430
|
}
|
|
1420
1431
|
}
|
|
1421
1432
|
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
"""Recovery detection and execution for Python foreground runner commands."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import json
|
|
6
|
-
import subprocess
|
|
7
|
-
import sys
|
|
8
|
-
import time
|
|
9
|
-
from dataclasses import dataclass
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
|
|
12
|
-
from .checkpoint_state import load_checkpoint_state
|
|
13
|
-
from .config import load_runtime_config
|
|
14
|
-
from .paths import RuntimePaths
|
|
15
|
-
from .runner_models import RunnerEnvironment, SessionPaths
|
|
16
|
-
from .sessions import AISessionConfig, AISessionLauncher, detect_stream_json_support
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@dataclass(frozen=True)
|
|
20
|
-
class RecoveryDetectorResolver:
|
|
21
|
-
"""Resolve recovery detector sources without coupling to one platform copy."""
|
|
22
|
-
|
|
23
|
-
paths: RuntimePaths
|
|
24
|
-
|
|
25
|
-
def candidates(self) -> tuple[Path, ...]:
|
|
26
|
-
project = self.paths.project_root
|
|
27
|
-
return (
|
|
28
|
-
project / ".agents" / "skills" / "recovery-workflow" / "scripts" / "detect-recovery-state.py",
|
|
29
|
-
project / ".claude" / "command-assets" / "recovery-workflow" / "scripts" / "detect-recovery-state.py",
|
|
30
|
-
project / ".codebuddy" / "skills" / "recovery-workflow" / "scripts" / "detect-recovery-state.py",
|
|
31
|
-
project / ".pi" / "skills" / "recovery-workflow" / "scripts" / "detect-recovery-state.py",
|
|
32
|
-
project
|
|
33
|
-
/ "core"
|
|
34
|
-
/ "skills"
|
|
35
|
-
/ "orchestration-skill"
|
|
36
|
-
/ "workflows"
|
|
37
|
-
/ "recovery-workflow"
|
|
38
|
-
/ "scripts"
|
|
39
|
-
/ "detect-recovery-state.py",
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
def resolve(self) -> Path:
|
|
43
|
-
for candidate in self.candidates():
|
|
44
|
-
if candidate.is_file():
|
|
45
|
-
return candidate
|
|
46
|
-
raise FileNotFoundError("recovery detector script not found")
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def run_recovery_detect(paths: RuntimePaths) -> tuple[int, str, str, dict[str, object] | None]:
|
|
50
|
-
"""Run recovery state detection and parse the JSON report."""
|
|
51
|
-
detector = RecoveryDetectorResolver(paths).resolve()
|
|
52
|
-
completed = subprocess.run(
|
|
53
|
-
[sys.executable, str(detector), "--project-root", str(paths.project_root)],
|
|
54
|
-
cwd=str(paths.project_root),
|
|
55
|
-
stdout=subprocess.PIPE,
|
|
56
|
-
stderr=subprocess.PIPE,
|
|
57
|
-
text=True,
|
|
58
|
-
check=False,
|
|
59
|
-
)
|
|
60
|
-
data = None
|
|
61
|
-
if completed.stdout.strip():
|
|
62
|
-
try:
|
|
63
|
-
data = json.loads(completed.stdout)
|
|
64
|
-
except json.JSONDecodeError:
|
|
65
|
-
data = None
|
|
66
|
-
return completed.returncode, completed.stdout, completed.stderr, data
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def run_recovery(paths: RuntimePaths, legacy_args: tuple[str, ...] = ()) -> tuple[int, str, str]:
|
|
70
|
-
"""Detect recovery state, render a prompt, launch an AI session, and classify."""
|
|
71
|
-
dry_run = "--dry-run" in legacy_args
|
|
72
|
-
model_override = _option_value(legacy_args, "--model")
|
|
73
|
-
code, stdout, stderr, detection = run_recovery_detect(paths)
|
|
74
|
-
if code != 0 or not detection:
|
|
75
|
-
return code or 1, stdout, stderr
|
|
76
|
-
if not detection.get("detected"):
|
|
77
|
-
return 0, stdout, stderr
|
|
78
|
-
|
|
79
|
-
session_id = "recovery-{}-{}".format(
|
|
80
|
-
str(detection.get("workflow_type") or "workflow"),
|
|
81
|
-
time.strftime("%Y%m%d-%H%M%S"),
|
|
82
|
-
)
|
|
83
|
-
session_dir = paths.recovery_state_dir / session_id
|
|
84
|
-
session_paths = SessionPaths(
|
|
85
|
-
session_dir=session_dir,
|
|
86
|
-
prompt_path=session_dir / "bootstrap-prompt.md",
|
|
87
|
-
logs_dir=session_dir / "logs",
|
|
88
|
-
session_log=session_dir / "logs" / "session.log",
|
|
89
|
-
progress_json=session_dir / "logs" / "progress.json",
|
|
90
|
-
heartbeat_json=session_dir / "logs" / "heartbeat.json",
|
|
91
|
-
session_status_json=session_dir / "session-status.json",
|
|
92
|
-
)
|
|
93
|
-
detection_json = session_paths.session_dir / "detection.json"
|
|
94
|
-
session_paths.session_dir.mkdir(parents=True, exist_ok=True)
|
|
95
|
-
detection_json.write_text(json.dumps(detection, indent=2, ensure_ascii=False), encoding="utf-8")
|
|
96
|
-
prompt_output = session_paths.prompt_path
|
|
97
|
-
generator = paths.pipeline_root / "scripts" / "generate-recovery-prompt.py"
|
|
98
|
-
prompt_cmd = [
|
|
99
|
-
sys.executable,
|
|
100
|
-
str(generator),
|
|
101
|
-
"--detection-json",
|
|
102
|
-
str(detection_json),
|
|
103
|
-
"--output",
|
|
104
|
-
str(prompt_output),
|
|
105
|
-
"--project-root",
|
|
106
|
-
str(paths.project_root),
|
|
107
|
-
"--session-id",
|
|
108
|
-
session_id,
|
|
109
|
-
]
|
|
110
|
-
prompt_result = subprocess.run(
|
|
111
|
-
prompt_cmd,
|
|
112
|
-
cwd=str(paths.project_root),
|
|
113
|
-
stdout=subprocess.PIPE,
|
|
114
|
-
stderr=subprocess.PIPE,
|
|
115
|
-
text=True,
|
|
116
|
-
check=False,
|
|
117
|
-
)
|
|
118
|
-
if prompt_result.returncode != 0:
|
|
119
|
-
return prompt_result.returncode, prompt_result.stdout, prompt_result.stderr
|
|
120
|
-
if dry_run:
|
|
121
|
-
return 0, prompt_result.stdout, prompt_result.stderr
|
|
122
|
-
|
|
123
|
-
config = load_runtime_config(paths)
|
|
124
|
-
if not config.ai_client.command:
|
|
125
|
-
return 1, "", "No supported AI CLI command was found for recovery\n"
|
|
126
|
-
env = RunnerEnvironment.from_env()
|
|
127
|
-
stream_json = False
|
|
128
|
-
if env.live_output and getattr(config.ai_client, "launch_profile", None) is None:
|
|
129
|
-
stream_json = detect_stream_json_support(config.ai_client.command, config.ai_client.platform).enabled
|
|
130
|
-
launcher = AISessionLauncher(
|
|
131
|
-
AISessionConfig(
|
|
132
|
-
cli_command=config.ai_client.command,
|
|
133
|
-
platform=config.ai_client.platform or "codebuddy",
|
|
134
|
-
model=model_override or config.model,
|
|
135
|
-
prompt_path=prompt_output,
|
|
136
|
-
cwd=paths.project_root,
|
|
137
|
-
log_path=session_paths.session_log,
|
|
138
|
-
progress_path=session_paths.progress_json,
|
|
139
|
-
heartbeat_path=session_paths.heartbeat_json,
|
|
140
|
-
effort=config.effort,
|
|
141
|
-
verbose=env.verbose,
|
|
142
|
-
live_output=env.live_output,
|
|
143
|
-
use_stream_json=stream_json,
|
|
144
|
-
launch_profile=getattr(config.ai_client, "launch_profile", None),
|
|
145
|
-
stale_kill_threshold_seconds=env.stale_kill_threshold_seconds,
|
|
146
|
-
heartbeat_stale_threshold_seconds=env.heartbeat_stale_threshold_seconds,
|
|
147
|
-
live_progress_interval_seconds=env.heartbeat_interval_seconds,
|
|
148
|
-
live_banner=False,
|
|
149
|
-
suppress_stream_output=True,
|
|
150
|
-
)
|
|
151
|
-
)
|
|
152
|
-
result = launcher.run()
|
|
153
|
-
checkpoint_path = _recovery_checkpoint_path(paths.project_root, detection)
|
|
154
|
-
outcome, semantic = _recovery_outcome(
|
|
155
|
-
paths.project_root,
|
|
156
|
-
result.exit_code,
|
|
157
|
-
checkpoint_path=checkpoint_path,
|
|
158
|
-
)
|
|
159
|
-
session_paths.session_status_json.write_text(
|
|
160
|
-
json.dumps(
|
|
161
|
-
{
|
|
162
|
-
"session_id": session_id,
|
|
163
|
-
"outcome": outcome,
|
|
164
|
-
"checkpoint_path": str(checkpoint_path) if checkpoint_path else None,
|
|
165
|
-
"checkpoint_state": semantic,
|
|
166
|
-
},
|
|
167
|
-
indent=2,
|
|
168
|
-
)
|
|
169
|
-
+ "\n",
|
|
170
|
-
encoding="utf-8",
|
|
171
|
-
)
|
|
172
|
-
return 0 if outcome in {"success", "no_changes"} else 1, outcome + "\n", ""
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def _option_value(args: tuple[str, ...], flag: str) -> str | None:
|
|
176
|
-
for index, arg in enumerate(args):
|
|
177
|
-
if arg == flag and index + 1 < len(args):
|
|
178
|
-
return args[index + 1]
|
|
179
|
-
if arg.startswith(flag + "="):
|
|
180
|
-
return arg.split("=", 1)[1]
|
|
181
|
-
return None
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
def _recovery_checkpoint_path(project_root: Path, detection: dict[str, object]) -> Path | None:
|
|
185
|
-
"""Select the L4 checkpoint identified by recovery inspection."""
|
|
186
|
-
semantic = detection.get("semantic_checkpoint")
|
|
187
|
-
if isinstance(semantic, dict) and semantic.get("path"):
|
|
188
|
-
candidate = project_root / str(semantic["path"])
|
|
189
|
-
if candidate.is_file():
|
|
190
|
-
return candidate
|
|
191
|
-
execution_owner = detection.get("execution_owner")
|
|
192
|
-
checkpoints = execution_owner.get("l4_checkpoints") if isinstance(execution_owner, dict) else []
|
|
193
|
-
if isinstance(checkpoints, list) and len(checkpoints) == 1:
|
|
194
|
-
candidate = project_root / str(checkpoints[0])
|
|
195
|
-
if candidate.is_file():
|
|
196
|
-
return candidate
|
|
197
|
-
return None
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
def _recovery_outcome(
|
|
201
|
-
project_root: Path,
|
|
202
|
-
exit_code: int | None,
|
|
203
|
-
*,
|
|
204
|
-
checkpoint_path: Path | None = None,
|
|
205
|
-
) -> tuple[str, dict[str, object]]:
|
|
206
|
-
"""Classify recovery from semantic L4 state without mutating partial work."""
|
|
207
|
-
if checkpoint_path is not None:
|
|
208
|
-
state = load_checkpoint_state(checkpoint_path, project_root=project_root)
|
|
209
|
-
semantic = state.semantic_snapshot
|
|
210
|
-
if state.valid and state.semantic_complete:
|
|
211
|
-
return "success", semantic
|
|
212
|
-
if state.valid and state.semantic_blocked:
|
|
213
|
-
return "workflow_blocked", semantic
|
|
214
|
-
if state.exists and not state.valid:
|
|
215
|
-
return "blocked_invalid_checkpoint", semantic
|
|
216
|
-
return "partial" if exit_code == 0 else "failed", semantic
|
|
217
|
-
|
|
218
|
-
status = subprocess.run(
|
|
219
|
-
["git", "-C", str(project_root), "status", "--porcelain"],
|
|
220
|
-
stdout=subprocess.PIPE,
|
|
221
|
-
stderr=subprocess.DEVNULL,
|
|
222
|
-
text=True,
|
|
223
|
-
check=False,
|
|
224
|
-
)
|
|
225
|
-
has_changes = bool(status.stdout.strip())
|
|
226
|
-
if exit_code == 0 and not has_changes:
|
|
227
|
-
return "no_changes", {}
|
|
228
|
-
return ("partial" if has_changes else "failed"), {}
|