prizmkit 1.1.104 → 1.1.106
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/README.md +6 -7
- package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +3 -3
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +0 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runner_bookkeeping.py +83 -12
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +38 -2
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +497 -154
- package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +98 -9
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +37 -28
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +4 -3
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +62 -38
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +57 -49
- package/bundled/dev-pipeline/templates/sections/critical-paths-agent.md +0 -1
- package/bundled/dev-pipeline/templates/sections/critical-paths-full.md +0 -1
- package/bundled/dev-pipeline/templates/sections/directory-convention-agent.md +2 -2
- package/bundled/dev-pipeline/templates/sections/directory-convention-full.md +2 -2
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +1 -0
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +1 -0
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +46 -0
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +308 -0
- package/bundled/dev-pipeline/tests/test_unified_cli.py +1 -0
- package/bundled/skills/_metadata.json +1 -1
- package/package.json +1 -1
- package/bundled/dev-pipeline/templates/agent-prompts/dev-implement.md +0 -71
package/bundled/VERSION.json
CHANGED
|
@@ -678,10 +678,9 @@ cli.py feature run command
|
|
|
678
678
|
+- AI CLI session # claude -p "$(cat prompt)" --dangerously-skip-permissions
|
|
679
679
|
| | # cbc --print -y < prompt
|
|
680
680
|
| | # codex --ask-for-approval never --sandbox danger-full-access exec --cd "$PROJECT_ROOT" --skip-git-repo-check - < prompt
|
|
681
|
-
| +-
|
|
682
|
-
| +-
|
|
683
|
-
| +-
|
|
684
|
-
| +- Reviewer # Analyze + code review
|
|
681
|
+
| +- session orchestrator # Main: init, context, plan, direct implementation, review gate, retrospective, commit
|
|
682
|
+
| +- Critic # Optional plan challenge
|
|
683
|
+
| +- Reviewer # Code review via /prizmkit-code-review
|
|
685
684
|
|
|
|
686
685
|
+- check-session-status.py # Parse session outcome
|
|
687
686
|
+- update-feature-status.py # Update feature state (completed/failed/retry)
|
|
@@ -696,8 +695,8 @@ The bootstrap prompt adapts based on feature complexity:
|
|
|
696
695
|
| Tier | Template | Agents | Use Case |
|
|
697
696
|
|------|----------|--------|----------|
|
|
698
697
|
| Tier 1 (lite) | `bootstrap-tier1.md` | Single agent handles everything | Low complexity features |
|
|
699
|
-
| Tier 2 (standard) | `bootstrap-tier2.md` | Orchestrator
|
|
700
|
-
| Tier 3 (full) | `bootstrap-tier3.md` |
|
|
698
|
+
| Tier 2 (standard) | `bootstrap-tier2.md` | Orchestrator implements directly + optional Critic/Reviewer | Medium/high complexity |
|
|
699
|
+
| Tier 3 (full) | `bootstrap-tier3.md` | Orchestrator implements directly + Critic/Reviewer review phases | High/critical complexity |
|
|
701
700
|
|
|
702
701
|
**Self-evolve mode:** Uses Tier 3 template with additional framework guardrails for developing the PrizmKit framework itself.
|
|
703
702
|
|
|
@@ -919,7 +918,7 @@ cli.py bugfix run command
|
|
|
919
918
|
| Claude Code | `.claude/agents/prizm-dev-team-*.md` | `.claude/team-info.json` |
|
|
920
919
|
| CodeBuddy | `.codebuddy/agents/prizm-dev-team-*.md` | `~/.codebuddy/teams/prizm-dev-team/config.json` |
|
|
921
920
|
|
|
922
|
-
Agent files
|
|
921
|
+
Agent files include Reviewer/Critic definitions used by feature prompt flows. `prizm-dev-team-dev.md` remains available for targeted fix/specialized handoffs such as the internal `/prizmkit-code-review` fix loop, but top-level feature implementation is performed directly by the main orchestrator with `/prizmkit-implement`. Paths are auto-resolved by `generate-bootstrap-prompt.py`.
|
|
923
922
|
|
|
924
923
|
---
|
|
925
924
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
dev-pipeline drives
|
|
5
|
+
dev-pipeline drives feature sessions through the unified Python outer loop. Each iteration spawns a new AI CLI session with a bootstrap prompt that makes the main session orchestrator responsible for implementation, while Critic and Reviewer agents remain available for challenge/review phases.
|
|
6
6
|
|
|
7
7
|
## Architecture
|
|
8
8
|
|
|
@@ -19,7 +19,7 @@ dev-pipeline (outer loop)
|
|
|
19
19
|
├── Phase 1-2: Context snapshot + Specify + Plan (Orchestrator)
|
|
20
20
|
├── Phase 3: Analyze (Reviewer agent) [tier2] / Phase 4: Analyze [tier3]
|
|
21
21
|
├── Phase 3.5: Plan Challenge (Critic agent(s), 1 or 3 parallel) [tier2+]
|
|
22
|
-
├── Phase 4: Implement (
|
|
22
|
+
├── Phase 4: Implement (main orchestrator runs /prizmkit-implement directly)
|
|
23
23
|
├── Phase 5: Review (Reviewer agent)
|
|
24
24
|
└── Phase 6: Retrospective & Commit (Orchestrator)
|
|
25
25
|
│
|
|
@@ -30,7 +30,7 @@ dev-pipeline (outer loop)
|
|
|
30
30
|
|
|
31
31
|
| Agent | Definition Path | Type |
|
|
32
32
|
|-------|----------------|------|
|
|
33
|
-
| Dev | `.claude/agents/prizm-dev-team-dev.md` (or `.codebuddy/agents/`) | prizm-dev-team-dev |
|
|
33
|
+
| Dev | `.claude/agents/prizm-dev-team-dev.md` (or `.codebuddy/agents/`) | prizm-dev-team-dev; reserved for targeted fix/specialized handoffs, not top-level feature implementation |
|
|
34
34
|
| Reviewer | `.claude/agents/prizm-dev-team-reviewer.md` (or `.codebuddy/agents/`) | prizm-dev-team-reviewer |
|
|
35
35
|
| Critic | `.claude/agents/prizm-dev-team-critic.md` (or `.codebuddy/agents/`) | prizm-dev-team-critic |
|
|
36
36
|
|
|
@@ -565,7 +565,6 @@ def branch_ensure_return_plan(context: BranchContext) -> GitCommandPlan:
|
|
|
565
565
|
),
|
|
566
566
|
_git("stash", "push", "-m", "pipeline-ensure-return-stash", allow_failure=True, description="stash checkout blockers"),
|
|
567
567
|
_git("checkout", context.base_branch, allow_failure=True, description="return to original branch"),
|
|
568
|
-
_git("stash", "pop", allow_failure=True, description="restore stashed checkout blockers"),
|
|
569
568
|
),
|
|
570
569
|
never_fails=True,
|
|
571
570
|
notes=("Always returns success to support trap/finally cleanup paths.",),
|
|
@@ -7,10 +7,11 @@ legacy ordering around branch creation and final status updates.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
from collections.abc import Sequence
|
|
10
11
|
from dataclasses import dataclass
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
|
|
13
|
-
from .gitops import GitCommandResult, git_status_safe, run_git_command
|
|
14
|
+
from .gitops import HIDDEN_TOOL_WORKTREE_EXCLUDES, GitCommandResult, git_status_safe, run_git_command
|
|
14
15
|
from .runner_models import RunnerFamily
|
|
15
16
|
|
|
16
17
|
|
|
@@ -27,25 +28,95 @@ class BookkeepingResult:
|
|
|
27
28
|
return self.result is None or self.result.return_code == 0
|
|
28
29
|
|
|
29
30
|
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
def _bookkeeping_paths(project_root: Path, family: RunnerFamily, item_id: str) -> tuple[str, ...]:
|
|
32
|
+
return (
|
|
33
|
+
_repo_path(project_root, family.plan_path),
|
|
34
|
+
_repo_path(project_root, family.state_dir / item_id / "status.json"),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _repo_path(project_root: Path, path: Path) -> str:
|
|
39
|
+
try:
|
|
40
|
+
return path.resolve().relative_to(project_root.resolve()).as_posix()
|
|
41
|
+
except ValueError:
|
|
42
|
+
return path.as_posix()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _changed_paths_in(project_root: Path, paths: Sequence[str]) -> tuple[str, ...]:
|
|
46
|
+
result = run_git_command(project_root, ("status", "--porcelain", "--", *paths))
|
|
47
|
+
if result.return_code != 0:
|
|
48
|
+
return ()
|
|
49
|
+
changed: list[str] = []
|
|
50
|
+
for line in result.stdout.splitlines():
|
|
51
|
+
path = line[3:].strip()
|
|
52
|
+
if " -> " in path:
|
|
53
|
+
path = path.split(" -> ", 1)[1].strip()
|
|
54
|
+
if path:
|
|
55
|
+
changed.append(path)
|
|
56
|
+
return tuple(changed)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def commit_bookkeeping_changes(
|
|
60
|
+
project_root: Path,
|
|
61
|
+
family: RunnerFamily,
|
|
62
|
+
item_id: str,
|
|
63
|
+
message: str,
|
|
64
|
+
) -> BookkeepingResult:
|
|
65
|
+
"""Stage and commit only runner-owned bookkeeping paths when present."""
|
|
66
|
+
paths = _changed_paths_in(project_root, _bookkeeping_paths(project_root, family, item_id))
|
|
67
|
+
if not paths:
|
|
68
|
+
return BookkeepingResult(attempted=False, committed=False)
|
|
69
|
+
run_git_command(project_root, ("add", "-A", "--", *paths))
|
|
70
|
+
result = run_git_command(project_root, ("commit", "--no-verify", "-m", message))
|
|
71
|
+
return BookkeepingResult(attempted=True, committed=result.return_code == 0, result=result)
|
|
33
72
|
|
|
34
73
|
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
|
|
74
|
+
def commit_task_branch_changes(
|
|
75
|
+
project_root: Path,
|
|
76
|
+
family: RunnerFamily,
|
|
77
|
+
item_id: str,
|
|
78
|
+
status: str,
|
|
79
|
+
) -> BookkeepingResult:
|
|
80
|
+
"""Commit task branch code, artifacts, and status without switching branches."""
|
|
81
|
+
message = f"chore({item_id}): save {family.kind} {status} state"
|
|
82
|
+
if status == "completed":
|
|
83
|
+
message = f"chore({item_id}): complete {family.kind} task"
|
|
84
|
+
if not git_status_safe(project_root).strip():
|
|
38
85
|
return BookkeepingResult(attempted=False, committed=False)
|
|
39
|
-
run_git_command(project_root, ("add", "-A", "--", "."))
|
|
86
|
+
run_git_command(project_root, ("add", "-A", "--", ".", *HIDDEN_TOOL_WORKTREE_EXCLUDES))
|
|
40
87
|
result = run_git_command(project_root, ("commit", "--no-verify", "-m", message))
|
|
41
88
|
return BookkeepingResult(attempted=True, committed=result.return_code == 0, result=result)
|
|
42
89
|
|
|
43
90
|
|
|
91
|
+
def commit_wip_changes(
|
|
92
|
+
project_root: Path,
|
|
93
|
+
family: RunnerFamily,
|
|
94
|
+
item_id: str,
|
|
95
|
+
status: str,
|
|
96
|
+
) -> BookkeepingResult:
|
|
97
|
+
"""Commit all visible task-branch changes as preserved WIP."""
|
|
98
|
+
if not git_status_safe(project_root).strip():
|
|
99
|
+
return BookkeepingResult(attempted=False, committed=False)
|
|
100
|
+
run_git_command(project_root, ("add", "-A", "--", ".", *HIDDEN_TOOL_WORKTREE_EXCLUDES))
|
|
101
|
+
result = run_git_command(
|
|
102
|
+
project_root,
|
|
103
|
+
(
|
|
104
|
+
"commit",
|
|
105
|
+
"--no-verify",
|
|
106
|
+
"-m",
|
|
107
|
+
f"wip({item_id}): preserve {family.kind} {status} state",
|
|
108
|
+
"-m",
|
|
109
|
+
"Pipeline preserved task branch work without merging it to the base branch.",
|
|
110
|
+
),
|
|
111
|
+
)
|
|
112
|
+
return BookkeepingResult(attempted=True, committed=result.return_code == 0, result=result)
|
|
113
|
+
|
|
114
|
+
|
|
44
115
|
def commit_pre_branch_bookkeeping(project_root: Path, family: RunnerFamily, item_id: str) -> BookkeepingResult:
|
|
45
|
-
"""Commit status
|
|
46
|
-
return
|
|
116
|
+
"""Commit in-progress status on the current task branch."""
|
|
117
|
+
return commit_bookkeeping_changes(project_root, family, item_id, f"chore({item_id}): mark in_progress")
|
|
47
118
|
|
|
48
119
|
|
|
49
120
|
def commit_final_bookkeeping(project_root: Path, family: RunnerFamily, item_id: str, status: str) -> BookkeepingResult:
|
|
50
|
-
"""Commit final
|
|
51
|
-
return
|
|
121
|
+
"""Commit final runner-owned status changes on the current task branch."""
|
|
122
|
+
return commit_bookkeeping_changes(project_root, family, item_id, f"chore({item_id}): update {family.kind} status {status}")
|
|
@@ -43,10 +43,18 @@ def classify_session(
|
|
|
43
43
|
) -> SessionClassification:
|
|
44
44
|
"""Classify raw AI session signals with durable success precedence."""
|
|
45
45
|
fingerprint = progress_fingerprint(project_root, prompt)
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
has_commit_since_base = _has_commit_since(project_root, base_head)
|
|
47
|
+
|
|
48
|
+
if _is_terminal_success(result) and _has_completed_workflow_artifact(prompt):
|
|
49
|
+
return SessionClassification(
|
|
50
|
+
"success",
|
|
51
|
+
reason="terminal_success_with_completed_checkpoint",
|
|
52
|
+
progress_fingerprint=fingerprint,
|
|
53
|
+
)
|
|
48
54
|
|
|
49
55
|
if _is_context_overflow(result):
|
|
56
|
+
if has_commit_since_base:
|
|
57
|
+
return SessionClassification("success", reason="commit_since_base", progress_fingerprint=fingerprint)
|
|
50
58
|
no_progress_count = _next_no_progress_count(previous_fingerprint, fingerprint, previous_no_progress_count)
|
|
51
59
|
if no_progress_count >= 2:
|
|
52
60
|
return SessionClassification(
|
|
@@ -72,6 +80,8 @@ def classify_session(
|
|
|
72
80
|
return SessionClassification("timed_out", reason="timeout", progress_fingerprint=fingerprint)
|
|
73
81
|
if result.exit_code not in (0, None):
|
|
74
82
|
return SessionClassification("crashed", reason=f"exit_code={result.exit_code}", progress_fingerprint=fingerprint)
|
|
83
|
+
if has_commit_since_base:
|
|
84
|
+
return SessionClassification("success", reason="commit_since_base", progress_fingerprint=fingerprint)
|
|
75
85
|
if _has_workspace_changes(project_root):
|
|
76
86
|
_auto_commit_workspace(project_root)
|
|
77
87
|
return SessionClassification("success", reason="auto_commit_uncommitted_changes", progress_fingerprint=fingerprint)
|
|
@@ -100,6 +110,32 @@ def progress_fingerprint(project_root: Path, prompt: PromptGenerationResult | No
|
|
|
100
110
|
}
|
|
101
111
|
|
|
102
112
|
|
|
113
|
+
def _is_terminal_success(result: AISessionResult) -> bool:
|
|
114
|
+
return (
|
|
115
|
+
result.exit_code in (0, None)
|
|
116
|
+
and not result.timed_out
|
|
117
|
+
and result.termination_reason not in {"timeout", "stale_session"}
|
|
118
|
+
and not result.fatal_error_code
|
|
119
|
+
and not result.progress_summary.fatal_error_code
|
|
120
|
+
and result.progress_summary.result_subtype == "success"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _has_completed_workflow_artifact(prompt: PromptGenerationResult | None) -> bool:
|
|
125
|
+
if prompt is None:
|
|
126
|
+
return False
|
|
127
|
+
if prompt.checkpoint_path and prompt.checkpoint_path.is_file():
|
|
128
|
+
try:
|
|
129
|
+
data = json.loads(prompt.checkpoint_path.read_text(encoding="utf-8"))
|
|
130
|
+
except (OSError, json.JSONDecodeError):
|
|
131
|
+
data = {}
|
|
132
|
+
if isinstance(data, dict) and _checkpoint_cursor(data) == "complete":
|
|
133
|
+
return True
|
|
134
|
+
if prompt.artifact_path and (prompt.artifact_path / "completion-summary.json").is_file():
|
|
135
|
+
return True
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
|
|
103
139
|
def _next_no_progress_count(previous: object | None, current: object, count: int) -> int:
|
|
104
140
|
if previous and previous == current:
|
|
105
141
|
return max(0, count) + 1
|