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.
Files changed (70) hide show
  1. package/bin/create-prizmkit.js +1 -5
  2. package/bundled/VERSION.json +3 -3
  3. package/bundled/dev-pipeline/README.md +41 -38
  4. package/bundled/dev-pipeline/assets/skill-subagent-integration.md +76 -0
  5. package/bundled/dev-pipeline/prizmkit_runtime/config.py +2 -8
  6. package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +2 -4
  7. package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +1 -1
  8. package/bundled/dev-pipeline/prizmkit_runtime/platform_detection.py +3 -3
  9. package/bundled/dev-pipeline/prizmkit_runtime/runner_bookkeeping.py +21 -11
  10. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +45 -3
  11. package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +28 -6
  12. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +60 -102
  13. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +1 -19
  14. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +1 -19
  15. package/bundled/dev-pipeline/scripts/{init-dev-team.py → init-change-artifact.py} +6 -16
  16. package/bundled/dev-pipeline/scripts/parse-stream-progress.py +364 -76
  17. package/bundled/dev-pipeline/scripts/utils.py +1 -1
  18. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +2 -2
  19. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +7 -9
  20. package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +2 -2
  21. package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +1 -1
  22. package/bundled/dev-pipeline/templates/sections/critical-paths-agent.md +0 -1
  23. package/bundled/dev-pipeline/templates/sections/critical-paths-full.md +0 -2
  24. package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +8 -1
  25. package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +6 -7
  26. package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +6 -7
  27. package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +4 -2
  28. package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +2 -2
  29. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +4 -2
  30. package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +20 -0
  31. package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +2 -2
  32. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +204 -141
  33. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +16 -10
  34. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +2 -13
  35. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +13 -14
  36. package/bundled/dev-pipeline/tests/test_unified_cli.py +227 -7
  37. package/bundled/skills/_metadata.json +1 -1
  38. package/bundled/skills/app-planner/SKILL.md +2 -2
  39. package/bundled/skills/app-planner/references/architecture-decisions.md +1 -3
  40. package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +4 -2
  41. package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +40 -1
  42. package/bundled/skills/feature-pipeline-launcher/SKILL.md +4 -2
  43. package/bundled/skills/feature-pipeline-launcher/references/configuration.md +40 -1
  44. package/bundled/skills/prizmkit-code-review/SKILL.md +16 -14
  45. package/bundled/skills/prizmkit-code-review/references/reviewer-agent-prompt.md +9 -9
  46. package/bundled/skills/prizmkit-implement/SKILL.md +21 -1
  47. package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +67 -0
  48. package/bundled/skills/refactor-pipeline-launcher/SKILL.md +4 -2
  49. package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +40 -1
  50. package/package.json +1 -1
  51. package/src/clean.js +12 -8
  52. package/src/config.js +24 -42
  53. package/src/index.js +1 -10
  54. package/src/manifest.js +3 -9
  55. package/src/metadata.js +0 -26
  56. package/src/prompts.js +0 -13
  57. package/src/scaffold.js +76 -201
  58. package/src/upgrade.js +16 -33
  59. package/bundled/adapters/claude/agent-adapter.js +0 -96
  60. package/bundled/adapters/claude/team-adapter.js +0 -183
  61. package/bundled/adapters/codebuddy/agent-adapter.js +0 -42
  62. package/bundled/adapters/codebuddy/team-adapter.js +0 -46
  63. package/bundled/adapters/codex/agent-adapter.js +0 -38
  64. package/bundled/adapters/codex/team-adapter.js +0 -37
  65. package/bundled/agents/prizm-dev-team-dev.md +0 -123
  66. package/bundled/agents/prizm-dev-team-reviewer.md +0 -113
  67. package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +0 -65
  68. package/bundled/dev-pipeline/templates/bootstrap-tier1.md +0 -436
  69. package/bundled/dev-pipeline/templates/bootstrap-tier2.md +0 -510
  70. 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": "lite",
815
- "agent_count": 1,
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="lite",
880
- agent_count=1,
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" / "agents").mkdir(parents=True)
1094
- (project / ".claude" / "agents" / "prizm-dev-team-dev.md").write_text("dev agent", encoding="utf-8")
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-dev-team.py").write_text("# init", encoding="utf-8")
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" / "agents" / "prizm-dev-team-dev.md").is_file()
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-dev-team.py").is_file()
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="lite",
1142
- agent_count=1,
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: lite (Tier 1Single Agent)" in captured.err
1539
- assert "Agents: 1" in captured.err
1537
+ assert "Pipeline mode: full (Tier 3Orchestrator + 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" / "bootstrap-tier2.md",
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 "child: 2MB/5" in running_line
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/scripts" in status_args
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" / "agents").mkdir(parents=True)
1978
- (repo / ".claude" / "agents" / "prizm-dev-team-dev.md").write_text("dev", encoding="utf-8")
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" / "agents" / "prizm-dev-team-dev.md").exists()
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" / "agents" / "prizm-dev-team-dev.md").is_file()
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()
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.1.118",
2
+ "version": "1.1.120",
3
3
  "skills": {
4
4
  "prizmkit": {
5
5
  "description": "Full-lifecycle dev toolkit. Covers spec-driven development, Prizm context docs, code quality, debugging, deployment, and knowledge management.",
@@ -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 `.codex/agents/*.toml`, `.claude/commands/prizmkit.md`, or `.codebuddy/skills/prizmkit/SKILL.md`. Do not treat a generic `.agents/` directory as Codex.
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-dev-team.py`).
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. Legacy manifests may contain `both`; treat it as read-only compatibility and append to `CLAUDE.md` and `CODEBUDDY.md` only when encountered.
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 (`cbc` or `claude`) must be in PATH
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 && (command -v cbc || command -v claude) && echo "All dependencies OK"
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
- | `cbc`/`claude` not in PATH | Check AI CLI installation |
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 (`cbc` or `claude`) must be in PATH
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 && (command -v cbc || command -v claude) && echo "All dependencies OK"
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
- | `cbc`/`claude` not in PATH | Check AI CLI installation |
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` |