prizmkit 1.1.118 → 1.1.120
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 +1 -5
- package/bundled/VERSION.json +3 -3
- package/bundled/dev-pipeline/README.md +41 -38
- package/bundled/dev-pipeline/assets/skill-subagent-integration.md +76 -0
- package/bundled/dev-pipeline/prizmkit_runtime/config.py +2 -8
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +2 -4
- package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/platform_detection.py +3 -3
- package/bundled/dev-pipeline/prizmkit_runtime/runner_bookkeeping.py +21 -11
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +45 -3
- package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +28 -6
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +60 -102
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +1 -19
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +1 -19
- package/bundled/dev-pipeline/scripts/{init-dev-team.py → init-change-artifact.py} +6 -16
- package/bundled/dev-pipeline/scripts/parse-stream-progress.py +364 -76
- package/bundled/dev-pipeline/scripts/utils.py +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +2 -2
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +7 -9
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +2 -2
- package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +1 -1
- package/bundled/dev-pipeline/templates/sections/critical-paths-agent.md +0 -1
- package/bundled/dev-pipeline/templates/sections/critical-paths-full.md +0 -2
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +8 -1
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +6 -7
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +6 -7
- package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +4 -2
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +2 -2
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +4 -2
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +20 -0
- package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +2 -2
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +204 -141
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +16 -10
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +2 -13
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +13 -14
- package/bundled/dev-pipeline/tests/test_unified_cli.py +227 -7
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/app-planner/SKILL.md +2 -2
- package/bundled/skills/app-planner/references/architecture-decisions.md +1 -3
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +4 -2
- package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +40 -1
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +4 -2
- package/bundled/skills/feature-pipeline-launcher/references/configuration.md +40 -1
- package/bundled/skills/prizmkit-code-review/SKILL.md +16 -14
- package/bundled/skills/prizmkit-code-review/references/reviewer-agent-prompt.md +9 -9
- package/bundled/skills/prizmkit-implement/SKILL.md +21 -1
- package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +67 -0
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +4 -2
- package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +40 -1
- package/package.json +1 -1
- package/src/clean.js +12 -8
- package/src/config.js +24 -42
- package/src/index.js +1 -10
- package/src/manifest.js +3 -9
- package/src/metadata.js +0 -26
- package/src/prompts.js +0 -13
- package/src/scaffold.js +76 -201
- package/src/upgrade.js +16 -33
- package/bundled/adapters/claude/agent-adapter.js +0 -96
- package/bundled/adapters/claude/team-adapter.js +0 -183
- package/bundled/adapters/codebuddy/agent-adapter.js +0 -42
- package/bundled/adapters/codebuddy/team-adapter.js +0 -46
- package/bundled/adapters/codex/agent-adapter.js +0 -38
- package/bundled/adapters/codex/team-adapter.js +0 -37
- package/bundled/agents/prizm-dev-team-dev.md +0 -123
- package/bundled/agents/prizm-dev-team-reviewer.md +0 -113
- package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +0 -65
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +0 -436
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +0 -510
- package/bundled/team/prizm-dev-team.json +0 -27
|
@@ -811,8 +811,8 @@ def test_prompt_generation_passes_explicit_execution_root_to_generator(monkeypat
|
|
|
811
811
|
"output_path": str(session_paths.prompt_path),
|
|
812
812
|
"checkpoint_path": str(execution_root / ".prizmkit" / "specs" / "001-low" / "workflow-checkpoint.json"),
|
|
813
813
|
"model": "",
|
|
814
|
-
"pipeline_mode": "
|
|
815
|
-
"agent_count":
|
|
814
|
+
"pipeline_mode": "full",
|
|
815
|
+
"agent_count": 3,
|
|
816
816
|
}
|
|
817
817
|
),
|
|
818
818
|
stderr="",
|
|
@@ -876,8 +876,8 @@ def _install_runner_session_fakes(monkeypatch, runners, *, statuses=("success",)
|
|
|
876
876
|
return PromptGenerationResult(
|
|
877
877
|
output_path=session_paths.prompt_path,
|
|
878
878
|
model="per-item-model",
|
|
879
|
-
pipeline_mode="
|
|
880
|
-
agent_count=
|
|
879
|
+
pipeline_mode="full",
|
|
880
|
+
agent_count=3,
|
|
881
881
|
checkpoint_path=execution_root / ".prizmkit" / "specs" / "001-low" / "workflow-checkpoint.json",
|
|
882
882
|
artifact_path=execution_root / ".prizmkit" / "specs" / "001-low",
|
|
883
883
|
raw={},
|
|
@@ -1090,13 +1090,12 @@ def test_explicit_worktree_materializes_ignored_support_assets_before_prompt_and
|
|
|
1090
1090
|
subprocess.run(["git", "add", ".gitignore", "tracked.txt"], cwd=project, check=True)
|
|
1091
1091
|
subprocess.run(["git", "commit", "-m", "base"], cwd=project, check=True, stdout=subprocess.DEVNULL)
|
|
1092
1092
|
|
|
1093
|
-
(project / ".claude" / "
|
|
1094
|
-
(project / ".claude" / "
|
|
1095
|
-
(project / ".claude" / "team-info.json").write_text("{}", encoding="utf-8")
|
|
1093
|
+
(project / ".claude" / "commands").mkdir(parents=True)
|
|
1094
|
+
(project / ".claude" / "commands" / "prizmkit.md").write_text("skill", encoding="utf-8")
|
|
1096
1095
|
(project / ".prizmkit" / "prizm-docs").mkdir(parents=True)
|
|
1097
1096
|
(project / ".prizmkit" / "prizm-docs" / "root.prizm").write_text("PRIZM_VERSION: 1\n", encoding="utf-8")
|
|
1098
1097
|
(project / ".prizmkit" / "dev-pipeline" / "scripts").mkdir(parents=True)
|
|
1099
|
-
(project / ".prizmkit" / "dev-pipeline" / "scripts" / "init-
|
|
1098
|
+
(project / ".prizmkit" / "dev-pipeline" / "scripts" / "init-change-artifact.py").write_text("# init", encoding="utf-8")
|
|
1100
1099
|
(project / ".prizmkit" / "config.json").write_text("{}", encoding="utf-8")
|
|
1101
1100
|
(project / ".prizmkit" / "manifest.json").write_text("{}", encoding="utf-8")
|
|
1102
1101
|
|
|
@@ -1128,18 +1127,18 @@ def test_explicit_worktree_materializes_ignored_support_assets_before_prompt_and
|
|
|
1128
1127
|
def fake_generate_prompt(family, invocation, **kwargs):
|
|
1129
1128
|
execution_root = kwargs["execution_root"]
|
|
1130
1129
|
assert execution_root != project
|
|
1131
|
-
assert (execution_root / ".claude" / "
|
|
1130
|
+
assert (execution_root / ".claude" / "commands" / "prizmkit.md").is_file()
|
|
1132
1131
|
assert (execution_root / ".prizmkit" / "prizm-docs" / "root.prizm").is_file()
|
|
1133
1132
|
assert (execution_root / ".prizmkit" / "config.json").is_file()
|
|
1134
1133
|
assert (execution_root / ".prizmkit" / "manifest.json").is_file()
|
|
1135
|
-
assert (execution_root / ".prizmkit" / "dev-pipeline" / "scripts" / "init-
|
|
1134
|
+
assert (execution_root / ".prizmkit" / "dev-pipeline" / "scripts" / "init-change-artifact.py").is_file()
|
|
1136
1135
|
assert not (execution_root / ".prizmkit" / "dev-pipeline" / "run-feature.sh").exists()
|
|
1137
1136
|
session_paths = kwargs["session_paths"]
|
|
1138
1137
|
return PromptGenerationResult(
|
|
1139
1138
|
output_path=session_paths.prompt_path,
|
|
1140
1139
|
model="",
|
|
1141
|
-
pipeline_mode="
|
|
1142
|
-
agent_count=
|
|
1140
|
+
pipeline_mode="full",
|
|
1141
|
+
agent_count=3,
|
|
1143
1142
|
checkpoint_path=execution_root / ".prizmkit" / "specs" / "001-support" / "workflow-checkpoint.json",
|
|
1144
1143
|
artifact_path=execution_root / ".prizmkit" / "specs" / "001-support",
|
|
1145
1144
|
raw={},
|
|
@@ -1535,8 +1534,8 @@ def test_foreground_runner_emits_structured_feature_progress(monkeypatch, tmp_pa
|
|
|
1535
1534
|
assert "Feature: F-001 — Readable output" in captured.err
|
|
1536
1535
|
assert "Code retry: 1 / 3" in captured.err
|
|
1537
1536
|
assert "Preflight: no user-visible changes to preserve before feature task F-001" in captured.err
|
|
1538
|
-
assert "Pipeline mode:
|
|
1539
|
-
assert "Agents:
|
|
1537
|
+
assert "Pipeline mode: full (Tier 3 — Orchestrator + Reviewer)" in captured.err
|
|
1538
|
+
assert "Agents: 3" in captured.err
|
|
1540
1539
|
assert "Spawning AI CLI session: F-001-session-1" in captured.err
|
|
1541
1540
|
assert "Session result: success" in captured.err
|
|
1542
1541
|
|
|
@@ -41,6 +41,17 @@ def run_cli(*args):
|
|
|
41
41
|
)
|
|
42
42
|
|
|
43
43
|
|
|
44
|
+
def _load_progress_parser_module():
|
|
45
|
+
import importlib.util
|
|
46
|
+
|
|
47
|
+
parser_path = PIPELINE_ROOT / "scripts" / "parse-stream-progress.py"
|
|
48
|
+
spec = importlib.util.spec_from_file_location("parse_stream_progress", parser_path)
|
|
49
|
+
module = importlib.util.module_from_spec(spec)
|
|
50
|
+
assert spec.loader is not None
|
|
51
|
+
spec.loader.exec_module(module)
|
|
52
|
+
return module
|
|
53
|
+
|
|
54
|
+
|
|
44
55
|
def test_cli_help_exposes_python_runtime_command_groups():
|
|
45
56
|
result = run_cli("--help")
|
|
46
57
|
|
|
@@ -793,7 +804,7 @@ def test_session_preamble_writes_redacted_start_command_for_all_launch_profiles(
|
|
|
793
804
|
|
|
794
805
|
def test_headless_subagent_prompt_guidance_keeps_no_worktree_boundary_visible():
|
|
795
806
|
guidance_files = [
|
|
796
|
-
REPO_ROOT / "dev-pipeline" / "templates" / "
|
|
807
|
+
REPO_ROOT / "dev-pipeline" / "templates" / "sections" / "phase-review-full.md",
|
|
797
808
|
REPO_ROOT / "dev-pipeline" / "templates" / "bootstrap-tier3.md",
|
|
798
809
|
REPO_ROOT / "core" / "skills" / "prizmkit-skill" / "prizmkit-code-review" / "SKILL.md",
|
|
799
810
|
]
|
|
@@ -840,6 +851,145 @@ def test_progress_parser_adds_required_metadata(tmp_path):
|
|
|
840
851
|
assert data["permission_denial_count"] == 1
|
|
841
852
|
|
|
842
853
|
|
|
854
|
+
|
|
855
|
+
def test_progress_parser_normalizes_claude_subagents_without_double_counting(tmp_path):
|
|
856
|
+
module = _load_progress_parser_module()
|
|
857
|
+
tracker = module.ProgressTracker()
|
|
858
|
+
|
|
859
|
+
tracker.process_event({"type": "system", "subtype": "init", "session_id": "claude-session", "cwd": str(tmp_path)})
|
|
860
|
+
tracker.process_event({
|
|
861
|
+
"type": "assistant",
|
|
862
|
+
"message": {"content": [{
|
|
863
|
+
"type": "tool_use",
|
|
864
|
+
"id": "tool-1",
|
|
865
|
+
"name": "Agent",
|
|
866
|
+
"input": {"description": "review code", "prompt": "check", "subagent_type": "reviewer"},
|
|
867
|
+
}]},
|
|
868
|
+
})
|
|
869
|
+
tracker.process_event({
|
|
870
|
+
"type": "system",
|
|
871
|
+
"subtype": "task_started",
|
|
872
|
+
"task_id": "task-1",
|
|
873
|
+
"tool_use_id": "tool-1",
|
|
874
|
+
"task_type": "in_process_teammate",
|
|
875
|
+
"subagent_type": "reviewer",
|
|
876
|
+
"description": "review code",
|
|
877
|
+
})
|
|
878
|
+
tracker.process_event({"type": "assistant", "parent_tool_use_id": "tool-1", "message": {"content": [{"type": "text", "text": "child activity"}]}})
|
|
879
|
+
tracker.process_event({"type": "system", "subtype": "task_started", "task_id": "bash-1", "task_type": "local_bash", "description": "ls"})
|
|
880
|
+
tracker.process_event({"type": "system", "subtype": "task_notification", "task_id": "task-1", "tool_use_id": "tool-1", "status": "completed", "summary": "done"})
|
|
881
|
+
|
|
882
|
+
data = tracker.to_dict()
|
|
883
|
+
|
|
884
|
+
assert data["subagent_stats_source"] == "session_log"
|
|
885
|
+
assert data["subagent_spawn_count"] == 1
|
|
886
|
+
assert data["active_subagent_count"] == 0
|
|
887
|
+
assert data["subagent_states"] == [{"status": "completed", "count": 1}]
|
|
888
|
+
assert "task-1" in data["subagent_observed_ids"]
|
|
889
|
+
assert "tool-1" in data["subagent_statistics"]["parent_activity_ids"]
|
|
890
|
+
assert all("bash" not in record["id"] for record in data["subagent_statistics"]["records"])
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
def test_progress_parser_normalizes_codebuddy_agent_tools_with_deduplication():
|
|
894
|
+
module = _load_progress_parser_module()
|
|
895
|
+
tracker = module.ProgressTracker()
|
|
896
|
+
args = {"prompt": "inspect", "description": "inspect", "subagent_type": "Explore"}
|
|
897
|
+
|
|
898
|
+
tracker.process_event({
|
|
899
|
+
"type": "message",
|
|
900
|
+
"sessionId": "cb-session",
|
|
901
|
+
"cwd": "/tmp/project",
|
|
902
|
+
"role": "assistant",
|
|
903
|
+
"content": [{"type": "tool_use", "id": "call-1", "name": "Agent", "input": args}],
|
|
904
|
+
})
|
|
905
|
+
tracker.process_event({
|
|
906
|
+
"type": "function_call",
|
|
907
|
+
"sessionId": "cb-session",
|
|
908
|
+
"cwd": "/tmp/project",
|
|
909
|
+
"id": "call-1",
|
|
910
|
+
"name": "Agent",
|
|
911
|
+
"arguments": json.dumps(args),
|
|
912
|
+
})
|
|
913
|
+
tracker.process_event({
|
|
914
|
+
"type": "function_call",
|
|
915
|
+
"sessionId": "cb-session",
|
|
916
|
+
"cwd": "/tmp/project",
|
|
917
|
+
"id": "task-call",
|
|
918
|
+
"name": "TaskCreate",
|
|
919
|
+
"arguments": json.dumps({"description": "background", "subject": "status"}),
|
|
920
|
+
})
|
|
921
|
+
|
|
922
|
+
data = tracker.to_dict()
|
|
923
|
+
|
|
924
|
+
assert data["cb_session_id"] == "cb-session"
|
|
925
|
+
assert data["subagent_provider"] == "codebuddy"
|
|
926
|
+
assert data["subagent_spawn_count"] == 2
|
|
927
|
+
assert data["active_subagent_count"] == 2
|
|
928
|
+
assert {item["status"] for item in data["subagent_states"]} == {"running"}
|
|
929
|
+
assert len(data["subagent_statistics"]["records"]) == 2
|
|
930
|
+
|
|
931
|
+
|
|
932
|
+
def test_progress_parser_normalizes_codex_collab_metadata_with_missing_fields():
|
|
933
|
+
module = _load_progress_parser_module()
|
|
934
|
+
tracker = module.ProgressTracker()
|
|
935
|
+
|
|
936
|
+
tracker.process_event({"type": "thread.started", "thread_id": "parent"})
|
|
937
|
+
tracker.process_event({
|
|
938
|
+
"type": "item.started",
|
|
939
|
+
"item": {
|
|
940
|
+
"type": "collab_tool_call",
|
|
941
|
+
"tool": "delegate",
|
|
942
|
+
"receiver_thread_ids": ["thread-a", "thread-b"],
|
|
943
|
+
"agents_states": {
|
|
944
|
+
"thread-a": {"status": "running", "message": "working"},
|
|
945
|
+
"thread-b": {"status": "completed", "message": "done"},
|
|
946
|
+
},
|
|
947
|
+
},
|
|
948
|
+
})
|
|
949
|
+
tracker.process_event({"type": "item.completed", "item": {"type": "collab_tool_call", "tool": "delegate"}})
|
|
950
|
+
|
|
951
|
+
data = tracker.to_dict()
|
|
952
|
+
|
|
953
|
+
assert data["event_format"] == "codex-json"
|
|
954
|
+
assert data["child_thread_ids"] == ["thread-a", "thread-b"]
|
|
955
|
+
assert data["subagent_spawn_count"] == 2
|
|
956
|
+
assert data["active_subagent_count"] == 1
|
|
957
|
+
assert {state["status"]: state["count"] for state in data["subagent_states"]} == {"running": 1, "completed": 1}
|
|
958
|
+
assert data["subagent_source_metadata"]["child_activity_source"] == "filesystem_liveness_only"
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
def test_progress_parser_keeps_child_files_liveness_only(tmp_path, monkeypatch):
|
|
962
|
+
module = _load_progress_parser_module()
|
|
963
|
+
claude_home = tmp_path / "claude"
|
|
964
|
+
project_key = str(tmp_path).replace("\\", "-").replace("/", "-").replace(":", "")
|
|
965
|
+
subagents_dir = claude_home / "projects" / project_key / "parent-session" / "subagents"
|
|
966
|
+
subagents_dir.mkdir(parents=True)
|
|
967
|
+
child_log = subagents_dir / "agent-child.jsonl"
|
|
968
|
+
child_log.write_text("child activity", encoding="utf-8")
|
|
969
|
+
monkeypatch.setenv("CLAUDE_HOME", str(claude_home))
|
|
970
|
+
|
|
971
|
+
tracker = module.ProgressTracker()
|
|
972
|
+
tracker.process_event({"type": "system", "subtype": "init", "session_id": "parent-session", "cwd": str(tmp_path)})
|
|
973
|
+
tracker.process_event({
|
|
974
|
+
"type": "system",
|
|
975
|
+
"subtype": "task_started",
|
|
976
|
+
"task_id": "task-1",
|
|
977
|
+
"tool_use_id": "tool-1",
|
|
978
|
+
"task_type": "in_process_teammate",
|
|
979
|
+
"description": "agent",
|
|
980
|
+
})
|
|
981
|
+
first = tracker.to_dict()
|
|
982
|
+
child_log.write_text("child activity grew", encoding="utf-8")
|
|
983
|
+
tracker.refresh_child_session_activity(force=True)
|
|
984
|
+
second = tracker.to_dict()
|
|
985
|
+
|
|
986
|
+
assert first["subagent_spawn_count"] == 1
|
|
987
|
+
assert second["subagent_spawn_count"] == 1
|
|
988
|
+
assert second["child_total_bytes"] > first["child_total_bytes"]
|
|
989
|
+
assert second["child_session_files"]
|
|
990
|
+
assert second["child_activity_source"] == "filesystem_liveness_only"
|
|
991
|
+
assert second["subagent_statistics"]["source"] == "session_log"
|
|
992
|
+
|
|
843
993
|
def test_session_dual_writes_progress_and_recovers_backup(tmp_path):
|
|
844
994
|
from prizmkit_runtime.sessions import AISessionConfig, AISessionLauncher, recover_session_log
|
|
845
995
|
|
|
@@ -935,6 +1085,10 @@ def test_custom_session_launch_writes_existing_logs_and_progress(tmp_path):
|
|
|
935
1085
|
progress_data = json.loads(progress.read_text(encoding="utf-8"))
|
|
936
1086
|
assert progress_data["event_type_counts"]["result"] == 1
|
|
937
1087
|
assert progress_data["has_usage"] is True
|
|
1088
|
+
assert progress_data["subagent_stats_source"] == "session_log"
|
|
1089
|
+
assert progress_data["subagent_spawn_count"] == 0
|
|
1090
|
+
assert progress_data["subagent_statistics"]["providers"] == []
|
|
1091
|
+
assert progress_data["child_activity_source"] == "filesystem_liveness_only"
|
|
938
1092
|
|
|
939
1093
|
|
|
940
1094
|
@pytest.mark.parametrize("platform", ["claude", "codex", "codebuddy"])
|
|
@@ -1031,7 +1185,10 @@ def test_compact_progress_line_hides_stale_until_no_progress(tmp_path):
|
|
|
1031
1185
|
json.dumps(
|
|
1032
1186
|
{
|
|
1033
1187
|
"child_total_bytes": 2 * 1024 * 1024,
|
|
1188
|
+
"child_session_files": [{"path": "a"}, {"path": "b"}],
|
|
1034
1189
|
"subagent_spawn_count": 5,
|
|
1190
|
+
"active_subagent_count": 2,
|
|
1191
|
+
"subagent_states": [{"status": "running", "count": 2}, {"status": "completed", "count": 3}],
|
|
1035
1192
|
"current_phase": "stream-derived-phase",
|
|
1036
1193
|
"current_tool": "Read",
|
|
1037
1194
|
"message_count": 465,
|
|
@@ -1059,7 +1216,9 @@ def test_compact_progress_line_hides_stale_until_no_progress(tmp_path):
|
|
|
1059
1216
|
|
|
1060
1217
|
assert "▶ [HEARTBEAT]" in running_line
|
|
1061
1218
|
assert "log: 4MB" in running_line
|
|
1062
|
-
assert "
|
|
1219
|
+
assert "subagents: spawned=5 active=2 states:running=2,completed=3" in running_line
|
|
1220
|
+
assert "child logs: 2MB/2 files" in running_line
|
|
1221
|
+
assert "child: 2MB/5" not in running_line
|
|
1063
1222
|
assert "phase: Retrospective" in running_line
|
|
1064
1223
|
assert "stream-derived-phase" not in running_line
|
|
1065
1224
|
assert "tool: Read" in running_line
|
|
@@ -1417,6 +1576,46 @@ def test_recovery_sessions_receive_live_heartbeat_configuration(tmp_path, monkey
|
|
|
1417
1576
|
assert config.suppress_stream_output is True
|
|
1418
1577
|
|
|
1419
1578
|
|
|
1579
|
+
|
|
1580
|
+
def test_session_summary_reports_normalized_subagents_and_child_liveness(tmp_path, capsys):
|
|
1581
|
+
import prizmkit_runtime.runners as runners
|
|
1582
|
+
from prizmkit_runtime.runner_models import SessionPaths
|
|
1583
|
+
|
|
1584
|
+
logs = tmp_path / "sessions" / "s1" / "logs"
|
|
1585
|
+
logs.mkdir(parents=True)
|
|
1586
|
+
session_log = logs / "session.log"
|
|
1587
|
+
progress = logs / "progress.json"
|
|
1588
|
+
session_log.write_text("one\ntwo\n", encoding="utf-8")
|
|
1589
|
+
progress.write_text(
|
|
1590
|
+
json.dumps({
|
|
1591
|
+
"subagent_spawn_count": 2,
|
|
1592
|
+
"active_subagent_count": 1,
|
|
1593
|
+
"subagent_states": [{"status": "running", "count": 1}, {"status": "completed", "count": 1}],
|
|
1594
|
+
"subagent_statistics": {"spawned": 2, "active": 1, "providers": ["claude"], "source": "session_log"},
|
|
1595
|
+
"child_total_bytes": 2048,
|
|
1596
|
+
"child_session_files": [{"path": "child-a"}],
|
|
1597
|
+
}),
|
|
1598
|
+
encoding="utf-8",
|
|
1599
|
+
)
|
|
1600
|
+
paths = SessionPaths(
|
|
1601
|
+
session_dir=logs.parent,
|
|
1602
|
+
prompt_path=logs.parent / "bootstrap-prompt.md",
|
|
1603
|
+
logs_dir=logs,
|
|
1604
|
+
session_log=session_log,
|
|
1605
|
+
progress_json=progress,
|
|
1606
|
+
heartbeat_json=logs / "heartbeat.json",
|
|
1607
|
+
session_status_json=logs / "session-status.json",
|
|
1608
|
+
)
|
|
1609
|
+
result = type("Result", (), {"exit_code": 0})()
|
|
1610
|
+
classification = type("Classification", (), {"session_status": "success"})()
|
|
1611
|
+
|
|
1612
|
+
runners._emit_session_summary(result, paths, classification)
|
|
1613
|
+
|
|
1614
|
+
captured = capsys.readouterr()
|
|
1615
|
+
assert "Subagents: spawned=2, active=1, states: running=1, completed=1 (claude; source=session.log)" in captured.err
|
|
1616
|
+
assert "Child log activity: 2KB across 1 files (heartbeat liveness only)" in captured.err
|
|
1617
|
+
assert "Subagent calls detected" not in captured.err
|
|
1618
|
+
|
|
1420
1619
|
def test_session_live_output_does_not_forward_cli_output_without_verbose(tmp_path, capsys):
|
|
1421
1620
|
from prizmkit_runtime.sessions import AISessionConfig, AISessionLauncher
|
|
1422
1621
|
|
|
@@ -1771,7 +1970,8 @@ def test_git_status_safe_ignores_worktree_support_assets(monkeypatch, tmp_path):
|
|
|
1771
1970
|
joined = " ".join(status_args)
|
|
1772
1971
|
assert ":(top,exclude).prizmkit-worktree" in status_args
|
|
1773
1972
|
assert ":(top,exclude).claude" in status_args
|
|
1774
|
-
assert ":(top,exclude).prizmkit/dev-pipeline
|
|
1973
|
+
assert ":(top,exclude).prizmkit/dev-pipeline" in status_args
|
|
1974
|
+
assert ":(top,exclude,glob).prizmkit/dev-pipeline/**" in status_args
|
|
1775
1975
|
assert all(exclude in status_args for exclude in HIDDEN_TOOL_WORKTREE_EXCLUDES)
|
|
1776
1976
|
assert ".prizmkit-worktree" in joined
|
|
1777
1977
|
|
|
@@ -1958,6 +2158,26 @@ def test_preflight_ready_commit_preserves_dirty_changes_without_repo_git_identit
|
|
|
1958
2158
|
assert _short_status(repo) == ""
|
|
1959
2159
|
|
|
1960
2160
|
|
|
2161
|
+
def test_preflight_ready_commit_force_stages_visible_file_when_prizmkit_dir_is_ignored(tmp_path):
|
|
2162
|
+
from prizmkit_runtime.runner_bookkeeping import commit_preflight_ready_changes
|
|
2163
|
+
|
|
2164
|
+
repo = tmp_path / "repo"
|
|
2165
|
+
_init_temp_repo(repo)
|
|
2166
|
+
(repo / ".gitignore").write_text(".prizmkit/*\n", encoding="utf-8")
|
|
2167
|
+
subprocess.run(["git", "add", ".gitignore"], cwd=repo, check=True)
|
|
2168
|
+
subprocess.run(["git", "commit", "-m", "ignore prizmkit"], cwd=repo, check=True, stdout=subprocess.DEVNULL)
|
|
2169
|
+
(repo / ".prizmkit" / "dev-pipeline" / "templates").mkdir(parents=True)
|
|
2170
|
+
(repo / ".prizmkit" / "dev-pipeline" / "templates" / "bootstrap-prompt.md").write_text("ignored runtime\n", encoding="utf-8")
|
|
2171
|
+
(repo / "visible.txt").write_text("dirty\n", encoding="utf-8")
|
|
2172
|
+
|
|
2173
|
+
result = commit_preflight_ready_changes(repo, "F-008")
|
|
2174
|
+
|
|
2175
|
+
assert result.committed
|
|
2176
|
+
assert _head_commit_message(repo) == "chore: ready for F-008"
|
|
2177
|
+
assert _head_files(repo) == {"visible.txt"}
|
|
2178
|
+
assert _short_status(repo) == ""
|
|
2179
|
+
|
|
2180
|
+
|
|
1961
2181
|
def test_materialize_worktree_support_assets_copies_ignored_assets_without_retired_shell_sources(tmp_path):
|
|
1962
2182
|
from prizmkit_runtime.gitops import (
|
|
1963
2183
|
BranchContext,
|
|
@@ -1974,8 +2194,8 @@ def test_materialize_worktree_support_assets_copies_ignored_assets_without_retir
|
|
|
1974
2194
|
subprocess.run(["git", "add", ".gitignore"], cwd=repo, check=True)
|
|
1975
2195
|
subprocess.run(["git", "commit", "-m", "ignore support"], cwd=repo, check=True, stdout=subprocess.DEVNULL)
|
|
1976
2196
|
|
|
1977
|
-
(repo / ".claude" / "
|
|
1978
|
-
(repo / ".claude" / "
|
|
2197
|
+
(repo / ".claude" / "commands").mkdir(parents=True)
|
|
2198
|
+
(repo / ".claude" / "commands" / "prizmkit.md").write_text("skill", encoding="utf-8")
|
|
1979
2199
|
(repo / ".prizmkit" / "prizm-docs").mkdir(parents=True)
|
|
1980
2200
|
(repo / ".prizmkit" / "prizm-docs" / "root.prizm").write_text("PRIZM_VERSION: 1\n", encoding="utf-8")
|
|
1981
2201
|
(repo / ".prizmkit" / "config.json").write_text("{}", encoding="utf-8")
|
|
@@ -1993,12 +2213,12 @@ def test_materialize_worktree_support_assets_copies_ignored_assets_without_retir
|
|
|
1993
2213
|
)
|
|
1994
2214
|
setup = ensure_linked_worktree(runtime)
|
|
1995
2215
|
assert setup.ok
|
|
1996
|
-
assert not (runtime.worktree_path / ".claude" / "
|
|
2216
|
+
assert not (runtime.worktree_path / ".claude" / "commands" / "prizmkit.md").exists()
|
|
1997
2217
|
|
|
1998
2218
|
result = materialize_worktree_support_assets(runtime)
|
|
1999
2219
|
|
|
2000
2220
|
assert result.ok
|
|
2001
|
-
assert (runtime.worktree_path / ".claude" / "
|
|
2221
|
+
assert (runtime.worktree_path / ".claude" / "commands" / "prizmkit.md").is_file()
|
|
2002
2222
|
assert (runtime.worktree_path / ".prizmkit" / "prizm-docs" / "root.prizm").is_file()
|
|
2003
2223
|
assert (runtime.worktree_path / ".prizmkit" / "config.json").is_file()
|
|
2004
2224
|
assert (runtime.worktree_path / ".prizmkit" / "manifest.json").is_file()
|
|
@@ -47,7 +47,7 @@ If you believe the task is better suited for a different workflow, you MUST:
|
|
|
47
47
|
- Claude Code → `CLAUDE.md`
|
|
48
48
|
- CodeBuddy → `CODEBUDDY.md`
|
|
49
49
|
- `all` → all three files.
|
|
50
|
-
- Only when the manifest is missing, fall back to installed PrizmKit-owned platform files such as `.
|
|
50
|
+
- Only when the manifest is missing, fall back to installed PrizmKit-owned platform files such as `.claude/commands/prizmkit.md`, `.agents/skills/prizmkit/SKILL.md`, or `.codebuddy/skills/prizmkit/SKILL.md`. Do not treat a generic `.agents/` directory as Codex.
|
|
51
51
|
|
|
52
52
|
**After planning is complete**, you MUST:
|
|
53
53
|
1. Present the summary of captured project-level context (vision, conventions, architecture decisions, project brief)
|
|
@@ -126,7 +126,7 @@ Before questions, check optional context files (never block if absent):
|
|
|
126
126
|
Note:
|
|
127
127
|
- This skill **reads** `.prizmkit/config.json` if present.
|
|
128
128
|
- This skill does **not** create `.prizmkit/config.json` directly.
|
|
129
|
-
- Creation/update is handled by bootstrap/init flows (e.g., `prizmkit-init`, `.prizmkit/dev-pipeline/scripts/init-
|
|
129
|
+
- Creation/update is handled by bootstrap/init flows (e.g., `prizmkit-init`, `.prizmkit/dev-pipeline/scripts/init-change-artifact.py`).
|
|
130
130
|
|
|
131
131
|
## Interaction Style (Hard Rule)
|
|
132
132
|
|
|
@@ -29,9 +29,7 @@ After Phase 2 (Confirm constraints and tech assumptions), before Phase 3 (Captur
|
|
|
29
29
|
- `codex` → append to `AGENTS.md`
|
|
30
30
|
- `claude` → append to `CLAUDE.md`
|
|
31
31
|
- `codebuddy` → append to `CODEBUDDY.md`
|
|
32
|
-
- `all` → append to all three files.
|
|
33
|
-
- Only when the manifest is missing, fall back to PrizmKit-owned install artifacts: `.codex/agents/*.toml`, `.claude/commands/prizmkit.md`, `.codebuddy/skills/prizmkit/SKILL.md`.
|
|
34
|
-
- Do not treat a generic `.agents/` directory as Codex; it may contain unrelated third-party skills.
|
|
32
|
+
- `all` → append to all three files.
|
|
35
33
|
- If no platform can be determined, skip (no project instruction file).
|
|
36
34
|
|
|
37
35
|
2. **Check for existing section** — read the target file and look for `### Architecture Decisions` heading:
|
|
@@ -46,13 +46,15 @@ Before any action, validate:
|
|
|
46
46
|
|
|
47
47
|
1. **bugfix pipeline exists**: Confirm `.prizmkit/dev-pipeline/cli.py` is present
|
|
48
48
|
2. **For start**: `.prizmkit/plans/bug-fix-list.json` must exist in `.prizmkit/plans/` (or user-specified path)
|
|
49
|
-
3. **Dependencies**: `python3`, `git`, and AI CLI
|
|
49
|
+
3. **Dependencies**: `python3`, `git`, and the configured AI CLI must be in PATH. Resolve and report the AI CLI from `AI_CLI` env, then `.prizmkit/config.json` `ai_cli`, then fallback to `claude` only when neither is configured. Read `${SKILL_DIR}/references/configuration.md` §Configured AI CLI Prerequisite Check before running the AI CLI check.
|
|
50
50
|
4. **Python version**: Requires Python 3.10+ for the unified dev-pipeline runtime
|
|
51
51
|
5. **Browser tools** (optional): If any bug has `browser_interaction` field, check the corresponding tool is available. Bugs may specify `tool: "playwright-cli"`, `tool: "opencli"`, or `tool: "auto"` (AI chooses at runtime).
|
|
52
52
|
|
|
53
53
|
Quick check:
|
|
54
54
|
```bash
|
|
55
|
-
command -v python3 && command -v git
|
|
55
|
+
command -v python3 >/dev/null && command -v git >/dev/null && echo "Core dependencies OK"
|
|
56
|
+
# AI CLI check: read `${SKILL_DIR}/references/configuration.md` §Configured AI CLI Prerequisite Check.
|
|
57
|
+
# It must print `Configured AI CLI: <name>` and verify that exact executable.
|
|
56
58
|
# Optional: browser interaction support (check both tools — bugs may use either)
|
|
57
59
|
command -v playwright-cli && echo "playwright-cli OK" || echo "playwright-cli not found (playwright browser verification will be skipped)"
|
|
58
60
|
command -v opencli && echo "opencli OK" || echo "opencli not found (opencli browser verification will be skipped)"
|
|
@@ -2,6 +2,45 @@
|
|
|
2
2
|
|
|
3
3
|
Environment variable mappings for the bugfix launcher.
|
|
4
4
|
|
|
5
|
+
## Configured AI CLI Prerequisite Check
|
|
6
|
+
|
|
7
|
+
Read this section during launcher prerequisite validation before reporting AI CLI availability.
|
|
8
|
+
|
|
9
|
+
Runtime AI CLI selection is config-driven. Resolve the executable name in this order:
|
|
10
|
+
1. `AI_CLI` environment variable when set.
|
|
11
|
+
2. `.prizmkit/config.json` `ai_cli` when present.
|
|
12
|
+
3. `claude` fallback only when neither is configured.
|
|
13
|
+
|
|
14
|
+
Run this quick check from the project root:
|
|
15
|
+
```bash
|
|
16
|
+
command -v python3 >/dev/null && command -v git >/dev/null || { echo "python3 or git missing"; exit 1; }
|
|
17
|
+
AI_CLI="$(
|
|
18
|
+
python3 - <<'PY'
|
|
19
|
+
import json, os, shlex
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
cli = os.environ.get("AI_CLI", "").strip()
|
|
22
|
+
if not cli:
|
|
23
|
+
config_path = Path(".prizmkit/config.json")
|
|
24
|
+
if config_path.is_file():
|
|
25
|
+
try:
|
|
26
|
+
data = json.loads(config_path.read_text(encoding="utf-8"))
|
|
27
|
+
cli = str(data.get("ai_cli") or "").strip()
|
|
28
|
+
except (OSError, json.JSONDecodeError):
|
|
29
|
+
cli = ""
|
|
30
|
+
cli = cli or "claude"
|
|
31
|
+
try:
|
|
32
|
+
print(shlex.split(cli)[0])
|
|
33
|
+
except ValueError:
|
|
34
|
+
print(cli.split()[0] if cli.split() else "claude")
|
|
35
|
+
PY
|
|
36
|
+
)"
|
|
37
|
+
printf 'Configured AI CLI: %s\n' "$AI_CLI"
|
|
38
|
+
command -v "$AI_CLI" >/dev/null && printf 'AI CLI OK: %s\n' "$(command -v "$AI_CLI")" || { printf 'AI CLI not found: %s\n' "$AI_CLI"; exit 1; }
|
|
39
|
+
echo "All dependencies OK"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Report the configured executable, for example `Configured AI CLI: claude`. Do not report the first arbitrary PATH match such as `cbc` when project config selects a different AI CLI.
|
|
43
|
+
|
|
5
44
|
## Environment Variable Mapping
|
|
6
45
|
|
|
7
46
|
Translating user responses to env vars:
|
|
@@ -73,7 +112,7 @@ Note: Bug filter defaults to all bugs. Runtime selects eligible bugs in stable l
|
|
|
73
112
|
| `.prizmkit/plans/bug-fix-list.json` not found | Tell user to run `bug-planner` skill first |
|
|
74
113
|
| `python3` not installed | Install Python 3.10+ and rerun the Python runtime command |
|
|
75
114
|
| `git` not installed | Install git; the Python runtime uses git for branch/worktree/status operations |
|
|
76
|
-
|
|
|
115
|
+
| Configured AI CLI not in PATH | Install the executable selected by `AI_CLI` or `.prizmkit/config.json` `ai_cli`, or update the config to a CLI available in PATH. |
|
|
77
116
|
| Bugfix pipeline already running | Show status, ask if user wants to stop and restart |
|
|
78
117
|
| PID file stale (process dead) | `python3 ./.prizmkit/dev-pipeline/cli.py daemon bugfix start .prizmkit/plans/bug-fix-list.json` auto-cleans, retry start |
|
|
79
118
|
| Launch failed (process died immediately) | Show last 20 lines of log: `python3 ./.prizmkit/dev-pipeline/cli.py daemon bugfix logs --lines 20` |
|
|
@@ -40,13 +40,15 @@ Before any action, validate:
|
|
|
40
40
|
|
|
41
41
|
1. **dev-pipeline exists**: Confirm `.prizmkit/dev-pipeline/cli.py` is present
|
|
42
42
|
2. **For start**: `.prizmkit/plans/feature-list.json` must exist in `.prizmkit/plans/` (or user-specified path)
|
|
43
|
-
3. **Dependencies**: `python3`, `git`, and AI CLI
|
|
43
|
+
3. **Dependencies**: `python3`, `git`, and the configured AI CLI must be in PATH. Resolve and report the AI CLI from `AI_CLI` env, then `.prizmkit/config.json` `ai_cli`, then fallback to `claude` only when neither is configured. Read `${SKILL_DIR}/references/configuration.md` §Configured AI CLI Prerequisite Check before running the AI CLI check.
|
|
44
44
|
4. **Python version**: Requires Python 3.10+ for the unified dev-pipeline runtime
|
|
45
45
|
5. **Browser tools** (optional): If any feature has `browser_interaction` field, check the corresponding tool is available. Features may specify `tool: "playwright-cli"`, `tool: "opencli"`, or `tool: "auto"` (AI chooses at runtime).
|
|
46
46
|
|
|
47
47
|
Quick check:
|
|
48
48
|
```bash
|
|
49
|
-
command -v python3 && command -v git
|
|
49
|
+
command -v python3 >/dev/null && command -v git >/dev/null && echo "Core dependencies OK"
|
|
50
|
+
# AI CLI check: read `${SKILL_DIR}/references/configuration.md` §Configured AI CLI Prerequisite Check.
|
|
51
|
+
# It must print `Configured AI CLI: <name>` and verify that exact executable.
|
|
50
52
|
# Optional: browser interaction support (check both tools — features may use either)
|
|
51
53
|
command -v playwright-cli && echo "playwright-cli OK" || echo "playwright-cli not found (playwright browser verification will be skipped)"
|
|
52
54
|
command -v opencli && echo "opencli OK" || echo "opencli not found (opencli browser verification will be skipped)"
|
|
@@ -22,6 +22,45 @@ Asked only when the user chose "Yes" to Advanced config in step 6.
|
|
|
22
22
|
- xhigh — Extensive reasoning (`PRIZMKIT_EFFORT=xhigh`)
|
|
23
23
|
- max — Maximum reasoning, Claude Code only (`PRIZMKIT_EFFORT=max`)
|
|
24
24
|
|
|
25
|
+
## Configured AI CLI Prerequisite Check
|
|
26
|
+
|
|
27
|
+
Read this section during launcher prerequisite validation before reporting AI CLI availability.
|
|
28
|
+
|
|
29
|
+
Runtime AI CLI selection is config-driven. Resolve the executable name in this order:
|
|
30
|
+
1. `AI_CLI` environment variable when set.
|
|
31
|
+
2. `.prizmkit/config.json` `ai_cli` when present.
|
|
32
|
+
3. `claude` fallback only when neither is configured.
|
|
33
|
+
|
|
34
|
+
Run this quick check from the project root:
|
|
35
|
+
```bash
|
|
36
|
+
command -v python3 >/dev/null && command -v git >/dev/null || { echo "python3 or git missing"; exit 1; }
|
|
37
|
+
AI_CLI="$(
|
|
38
|
+
python3 - <<'PY'
|
|
39
|
+
import json, os, shlex
|
|
40
|
+
from pathlib import Path
|
|
41
|
+
cli = os.environ.get("AI_CLI", "").strip()
|
|
42
|
+
if not cli:
|
|
43
|
+
config_path = Path(".prizmkit/config.json")
|
|
44
|
+
if config_path.is_file():
|
|
45
|
+
try:
|
|
46
|
+
data = json.loads(config_path.read_text(encoding="utf-8"))
|
|
47
|
+
cli = str(data.get("ai_cli") or "").strip()
|
|
48
|
+
except (OSError, json.JSONDecodeError):
|
|
49
|
+
cli = ""
|
|
50
|
+
cli = cli or "claude"
|
|
51
|
+
try:
|
|
52
|
+
print(shlex.split(cli)[0])
|
|
53
|
+
except ValueError:
|
|
54
|
+
print(cli.split()[0] if cli.split() else "claude")
|
|
55
|
+
PY
|
|
56
|
+
)"
|
|
57
|
+
printf 'Configured AI CLI: %s\n' "$AI_CLI"
|
|
58
|
+
command -v "$AI_CLI" >/dev/null && printf 'AI CLI OK: %s\n' "$(command -v "$AI_CLI")" || { printf 'AI CLI not found: %s\n' "$AI_CLI"; exit 1; }
|
|
59
|
+
echo "All dependencies OK"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Report the configured executable, for example `Configured AI CLI: claude`. Do not report the first arbitrary PATH match such as `cbc` when project config selects a different AI CLI.
|
|
63
|
+
|
|
25
64
|
## Environment Variable Mapping
|
|
26
65
|
|
|
27
66
|
Translating user responses to env vars:
|
|
@@ -55,7 +94,7 @@ Not exposed in interactive menu, pass via `--env`:
|
|
|
55
94
|
| `.prizmkit/plans/feature-list.json` not found | Tell user to run `feature-planner` skill first |
|
|
56
95
|
| `python3` not installed | Install Python 3.10+ and rerun the Python runtime command |
|
|
57
96
|
| `git` not installed | Install git; the Python runtime uses git for branch/worktree/status operations |
|
|
58
|
-
|
|
|
97
|
+
| Configured AI CLI not in PATH | Install the executable selected by `AI_CLI` or `.prizmkit/config.json` `ai_cli`, or update the config to a CLI available in PATH. |
|
|
59
98
|
| Pipeline already running | Show status, ask if user wants to stop and restart |
|
|
60
99
|
| PID file stale (process dead) | `python3 ./.prizmkit/dev-pipeline/cli.py daemon feature start .prizmkit/plans/feature-list.json` auto-cleans, retry start |
|
|
61
100
|
| Launch failed (process died immediately) | Show last 20 lines of log: `python3 ./.prizmkit/dev-pipeline/cli.py daemon feature logs --lines 20` |
|