prizmkit 1.1.117 → 1.1.119

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "frameworkVersion": "1.1.117",
3
- "bundledAt": "2026-07-09T14:16:53.861Z",
4
- "bundledFrom": "7699f61"
2
+ "frameworkVersion": "1.1.119",
3
+ "bundledAt": "2026-07-09T15:35:51.674Z",
4
+ "bundledFrom": "8a729da"
5
5
  }
@@ -33,12 +33,13 @@ HIDDEN_TOOL_WORKTREE_EXCLUDES = (
33
33
  ":(top,exclude).prizmkit/manifest.json",
34
34
  ":(top,exclude).prizmkit/state",
35
35
  ":(top,exclude,glob).prizmkit/state/**",
36
- ":(top,exclude).prizmkit/dev-pipeline/scripts",
37
- ":(top,exclude,glob).prizmkit/dev-pipeline/scripts/**",
38
- ":(top,exclude).prizmkit/dev-pipeline/prizmkit_runtime",
39
- ":(top,exclude,glob).prizmkit/dev-pipeline/prizmkit_runtime/**",
36
+ ":(top,exclude).prizmkit/dev-pipeline",
37
+ ":(top,exclude,glob).prizmkit/dev-pipeline/**",
40
38
  )
41
39
 
40
+ PIPELINE_FALLBACK_GIT_NAME = "PrizmKit Pipeline"
41
+ PIPELINE_FALLBACK_GIT_EMAIL = "prizmkit-pipeline@example.invalid"
42
+
42
43
  WORKTREE_PLATFORM_SUPPORT_DIRS = (".claude", ".codebuddy", ".codex", ".agents")
43
44
  WORKTREE_ROOT_SUPPORT_FILES = ("skills-lock.json", "AGENTS.md")
44
45
  WORKTREE_PRIZMKIT_SUPPORT_ENTRIES = ("prizm-docs", "config.json", "manifest.json")
@@ -640,17 +641,60 @@ def branch_ensure_return(project_root: Path, base_branch: str, working_branch: s
640
641
  context = BranchContext(base_branch=base_branch, working_branch=working_branch or active, project_root=project_root)
641
642
  return run_git_plan(project_root, branch_ensure_return_plan(context))
642
643
 
644
+
645
+ def _git_config_has_value(project_root: Path, key: str) -> bool:
646
+ completed = subprocess.run(
647
+ ["git", "-C", str(project_root), "config", "--get", key],
648
+ stdout=subprocess.PIPE,
649
+ stderr=subprocess.PIPE,
650
+ text=True,
651
+ check=False,
652
+ )
653
+ return completed.returncode == 0 and bool(completed.stdout.strip())
654
+
655
+
656
+ def _identity_value_available(project_root: Path, config_key: str, author_env: str, committer_env: str) -> bool:
657
+ return _git_config_has_value(project_root, config_key) or bool(
658
+ os.environ.get(author_env) and os.environ.get(committer_env)
659
+ )
660
+
661
+
662
+ def _commit_identity_global_args(project_root: Path) -> tuple[str, ...]:
663
+ global_args: list[str] = []
664
+ if not _identity_value_available(project_root, "user.name", "GIT_AUTHOR_NAME", "GIT_COMMITTER_NAME"):
665
+ global_args.extend(("-c", f"user.name={PIPELINE_FALLBACK_GIT_NAME}"))
666
+ if not _identity_value_available(project_root, "user.email", "GIT_AUTHOR_EMAIL", "GIT_COMMITTER_EMAIL"):
667
+ global_args.extend(("-c", f"user.email={PIPELINE_FALLBACK_GIT_EMAIL}"))
668
+ return tuple(global_args)
669
+
670
+
671
+ def _commit_args_without_signing(args: tuple[str, ...]) -> tuple[str, ...]:
672
+ explicit_signing = any(
673
+ arg == "-S" or arg.startswith("-S") or arg == "--gpg-sign" or arg.startswith("--gpg-sign=")
674
+ for arg in args[1:]
675
+ )
676
+ if explicit_signing or "--no-gpg-sign" in args:
677
+ return args
678
+ return ("commit", "--no-gpg-sign", *args[1:])
679
+
680
+
643
681
  def run_git_command(project_root: Path, args: Sequence[str]) -> GitCommandResult:
644
682
  """Run one git command in a project root and capture output."""
683
+ command_args = tuple(map(str, args))
684
+ global_args: tuple[str, ...] = ()
685
+ executable_args = command_args
686
+ if command_args and command_args[0] == "commit":
687
+ global_args = _commit_identity_global_args(project_root)
688
+ executable_args = _commit_args_without_signing(command_args)
645
689
  completed = subprocess.run(
646
- ["git", "-C", str(project_root), *map(str, args)],
690
+ ["git", *global_args, "-C", str(project_root), *executable_args],
647
691
  stdout=subprocess.PIPE,
648
692
  stderr=subprocess.PIPE,
649
693
  text=True,
650
694
  check=False,
651
695
  )
652
696
  return GitCommandResult(
653
- args=tuple(map(str, args)),
697
+ args=command_args,
654
698
  return_code=completed.returncode,
655
699
  stdout=completed.stdout,
656
700
  stderr=completed.stderr,
@@ -46,18 +46,27 @@ def _visible_status_result(project_root: Path) -> GitCommandResult:
46
46
  return run_git_command(project_root, ("status", "--porcelain", "--", ".", *HIDDEN_TOOL_WORKTREE_EXCLUDES))
47
47
 
48
48
 
49
+ def _paths_from_status(stdout: str) -> tuple[str, ...]:
50
+ changed: list[str] = []
51
+ seen: set[str] = set()
52
+ for line in stdout.splitlines():
53
+ if len(line) < 4:
54
+ continue
55
+ path = line[3:].strip()
56
+ paths = path.split(" -> ", 1) if " -> " in path else [path]
57
+ for candidate in paths:
58
+ candidate = candidate.strip()
59
+ if candidate and candidate not in seen:
60
+ changed.append(candidate)
61
+ seen.add(candidate)
62
+ return tuple(changed)
63
+
64
+
49
65
  def _changed_paths_in(project_root: Path, paths: Sequence[str]) -> tuple[str, ...]:
50
66
  result = run_git_command(project_root, ("status", "--porcelain", "--", *paths))
51
67
  if result.return_code != 0:
52
68
  return ()
53
- changed: list[str] = []
54
- for line in result.stdout.splitlines():
55
- path = line[3:].strip()
56
- if " -> " in path:
57
- path = path.split(" -> ", 1)[1].strip()
58
- if path:
59
- changed.append(path)
60
- return tuple(changed)
69
+ return _paths_from_status(result.stdout)
61
70
 
62
71
 
63
72
  def commit_bookkeeping_changes(
@@ -80,14 +89,15 @@ def commit_preflight_ready_changes(project_root: Path, item_id: str) -> Bookkeep
80
89
  visible_status = _visible_status_result(project_root)
81
90
  if visible_status.return_code != 0:
82
91
  return BookkeepingResult(attempted=True, committed=False, result=visible_status)
83
- if not visible_status.stdout.strip():
92
+ paths = _paths_from_status(visible_status.stdout)
93
+ if not paths:
84
94
  return BookkeepingResult(attempted=False, committed=False)
85
- add_result = run_git_command(project_root, ("add", "-A", "--", ".", *HIDDEN_TOOL_WORKTREE_EXCLUDES))
95
+ add_result = run_git_command(project_root, ("add", "-A", "-f", "--", *paths))
86
96
  if add_result.return_code != 0:
87
97
  return BookkeepingResult(attempted=True, committed=False, result=add_result)
88
98
  result = run_git_command(
89
99
  project_root,
90
- ("commit", "--no-verify", "-m", f"chore: ready for {item_id}", "--", ".", *HIDDEN_TOOL_WORKTREE_EXCLUDES),
100
+ ("commit", "--no-verify", "-m", f"chore: ready for {item_id}", "--", *paths),
91
101
  )
92
102
  if result.return_code != 0:
93
103
  return BookkeepingResult(attempted=True, committed=False, result=result)
@@ -1570,14 +1570,29 @@ def test_no_worktree_interrupt_saves_wip_and_returns_to_base(monkeypatch, tmp_pa
1570
1570
  assert ensure_calls == [("main", "dev/F-001-interrupt")]
1571
1571
 
1572
1572
 
1573
- def _init_bookkeeping_repo(project_root):
1573
+ def _init_bookkeeping_repo(project_root, *, configure_identity=True):
1574
1574
  subprocess.run(["git", "init", "-b", "main"], cwd=project_root, check=True, stdout=subprocess.DEVNULL)
1575
- subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=project_root, check=True)
1576
- subprocess.run(["git", "config", "user.name", "Test"], cwd=project_root, check=True)
1575
+ if configure_identity:
1576
+ subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=project_root, check=True)
1577
+ subprocess.run(["git", "config", "user.name", "Test"], cwd=project_root, check=True)
1577
1578
  (project_root / "staged.txt").write_text("base staged\n", encoding="utf-8")
1578
1579
  (project_root / "unstaged.txt").write_text("base unstaged\n", encoding="utf-8")
1579
1580
  subprocess.run(["git", "add", "staged.txt", "unstaged.txt"], cwd=project_root, check=True)
1580
- subprocess.run(["git", "commit", "-m", "base"], cwd=project_root, check=True, stdout=subprocess.DEVNULL)
1581
+ subprocess.run(
1582
+ [
1583
+ "git",
1584
+ "-c",
1585
+ "user.email=test@example.com",
1586
+ "-c",
1587
+ "user.name=Test",
1588
+ "commit",
1589
+ "-m",
1590
+ "base",
1591
+ ],
1592
+ cwd=project_root,
1593
+ check=True,
1594
+ stdout=subprocess.DEVNULL,
1595
+ )
1581
1596
 
1582
1597
 
1583
1598
  def _head_commit_count(project_root):
@@ -1754,6 +1769,69 @@ def test_process_item_creates_one_ready_commit_before_branch_setup_for_mixed_vis
1754
1769
 
1755
1770
 
1756
1771
 
1772
+ def test_process_item_ready_commit_uses_fallback_identity_before_branch_setup(monkeypatch, tmp_path):
1773
+ from prizmkit_runtime import runner_bookkeeping, runners
1774
+ from prizmkit_runtime.gitops import HIDDEN_TOOL_WORKTREE_EXCLUDES, PIPELINE_FALLBACK_GIT_EMAIL, PIPELINE_FALLBACK_GIT_NAME
1775
+ from prizmkit_runtime.runner_models import family_for, parse_invocation
1776
+
1777
+ monkeypatch.setenv("GIT_CONFIG_GLOBAL", str(tmp_path / "missing-global-gitconfig"))
1778
+ monkeypatch.setenv("GIT_CONFIG_NOSYSTEM", "1")
1779
+ for key in ("GIT_AUTHOR_NAME", "GIT_AUTHOR_EMAIL", "GIT_COMMITTER_NAME", "GIT_COMMITTER_EMAIL"):
1780
+ monkeypatch.delenv(key, raising=False)
1781
+ paths = _make_paths(tmp_path)
1782
+ _init_bookkeeping_repo(paths.project_root, configure_identity=False)
1783
+ (paths.project_root / "new-visible.txt").write_text("dirty before run\n", encoding="utf-8")
1784
+ before_count = _head_commit_count(paths.project_root)
1785
+ branch_events = []
1786
+
1787
+ monkeypatch.setenv("DEV_BRANCH", "dev/F-001-ready-no-identity")
1788
+ family = family_for("feature", paths)
1789
+ invocation = parse_invocation(family, "run", ())
1790
+ invocation = invocation.__class__(**{**invocation.__dict__, "list_path": paths.feature_plan})
1791
+ _install_runner_session_fakes(monkeypatch, runners)
1792
+ monkeypatch.setattr(runners, "commit_preflight_ready_changes", runner_bookkeeping.commit_preflight_ready_changes)
1793
+
1794
+ def fake_run_git_plan(project_root, plan):
1795
+ if plan.name == "branch_create":
1796
+ author = subprocess.run(
1797
+ ["git", "log", "-1", "--format=%an <%ae>"],
1798
+ cwd=project_root,
1799
+ check=True,
1800
+ stdout=subprocess.PIPE,
1801
+ text=True,
1802
+ ).stdout.strip()
1803
+ branch_events.append(
1804
+ {
1805
+ "commit_count": _head_commit_count(project_root),
1806
+ "message": _head_message(project_root),
1807
+ "author": author,
1808
+ "visible_status": subprocess.run(
1809
+ ["git", "status", "--porcelain", "--", ".", *HIDDEN_TOOL_WORKTREE_EXCLUDES],
1810
+ cwd=project_root,
1811
+ check=True,
1812
+ stdout=subprocess.PIPE,
1813
+ text=True,
1814
+ ).stdout,
1815
+ }
1816
+ )
1817
+ return SimpleNamespace(ok=True, plan=plan, results=())
1818
+
1819
+ monkeypatch.setattr(runners, "run_git_plan", fake_run_git_plan)
1820
+
1821
+ status = runners._process_item(family, invocation, "F-001", paths, initial_metadata={})
1822
+
1823
+ assert status == "completed"
1824
+ assert branch_events == [
1825
+ {
1826
+ "commit_count": before_count + 1,
1827
+ "message": "chore: ready for F-001",
1828
+ "author": f"{PIPELINE_FALLBACK_GIT_NAME} <{PIPELINE_FALLBACK_GIT_EMAIL}>",
1829
+ "visible_status": "",
1830
+ }
1831
+ ]
1832
+ assert "new-visible.txt" in _head_files(paths.project_root)
1833
+
1834
+
1757
1835
  def test_bookkeeping_commits_only_runner_owned_paths(tmp_path):
1758
1836
  from prizmkit_runtime.runner_bookkeeping import commit_final_bookkeeping
1759
1837
  from prizmkit_runtime.runner_models import RunnerFamily
@@ -1771,19 +1771,35 @@ def test_git_status_safe_ignores_worktree_support_assets(monkeypatch, tmp_path):
1771
1771
  joined = " ".join(status_args)
1772
1772
  assert ":(top,exclude).prizmkit-worktree" in status_args
1773
1773
  assert ":(top,exclude).claude" in status_args
1774
- assert ":(top,exclude).prizmkit/dev-pipeline/scripts" in status_args
1774
+ assert ":(top,exclude).prizmkit/dev-pipeline" in status_args
1775
+ assert ":(top,exclude,glob).prizmkit/dev-pipeline/**" in status_args
1775
1776
  assert all(exclude in status_args for exclude in HIDDEN_TOOL_WORKTREE_EXCLUDES)
1776
1777
  assert ".prizmkit-worktree" in joined
1777
1778
 
1778
1779
 
1779
- def _init_temp_repo(repo):
1780
+ def _init_temp_repo(repo, *, configure_identity=True):
1780
1781
  repo.mkdir(parents=True, exist_ok=True)
1781
1782
  subprocess.run(["git", "init", "-b", "main"], cwd=repo, check=True, stdout=subprocess.DEVNULL)
1782
- subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=repo, check=True)
1783
- subprocess.run(["git", "config", "user.name", "Test"], cwd=repo, check=True)
1783
+ if configure_identity:
1784
+ subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=repo, check=True)
1785
+ subprocess.run(["git", "config", "user.name", "Test"], cwd=repo, check=True)
1784
1786
  (repo / "base.txt").write_text("base\n", encoding="utf-8")
1785
1787
  subprocess.run(["git", "add", "base.txt"], cwd=repo, check=True)
1786
- subprocess.run(["git", "commit", "-m", "base"], cwd=repo, check=True, stdout=subprocess.DEVNULL)
1788
+ subprocess.run(
1789
+ [
1790
+ "git",
1791
+ "-c",
1792
+ "user.email=test@example.com",
1793
+ "-c",
1794
+ "user.name=Test",
1795
+ "commit",
1796
+ "-m",
1797
+ "base",
1798
+ ],
1799
+ cwd=repo,
1800
+ check=True,
1801
+ stdout=subprocess.DEVNULL,
1802
+ )
1787
1803
 
1788
1804
 
1789
1805
  def _head_commit_message(repo):
@@ -1915,6 +1931,54 @@ def test_preflight_ready_commit_excludes_ignored_tool_worktree_directories(tmp_p
1915
1931
  assert (repo / ".cache" / "worktrees" / "agent" / "artifact.txt").is_file()
1916
1932
 
1917
1933
 
1934
+ def test_preflight_ready_commit_preserves_dirty_changes_without_repo_git_identity(tmp_path, monkeypatch):
1935
+ from prizmkit_runtime.gitops import PIPELINE_FALLBACK_GIT_EMAIL, PIPELINE_FALLBACK_GIT_NAME
1936
+ from prizmkit_runtime.runner_bookkeeping import commit_preflight_ready_changes
1937
+
1938
+ repo = tmp_path / "repo"
1939
+ monkeypatch.setenv("GIT_CONFIG_GLOBAL", str(tmp_path / "missing-global-gitconfig"))
1940
+ monkeypatch.setenv("GIT_CONFIG_NOSYSTEM", "1")
1941
+ for key in ("GIT_AUTHOR_NAME", "GIT_AUTHOR_EMAIL", "GIT_COMMITTER_NAME", "GIT_COMMITTER_EMAIL"):
1942
+ monkeypatch.delenv(key, raising=False)
1943
+ _init_temp_repo(repo, configure_identity=False)
1944
+ (repo / "visible.txt").write_text("dirty\n", encoding="utf-8")
1945
+
1946
+ result = commit_preflight_ready_changes(repo, "F-007")
1947
+ author = subprocess.run(
1948
+ ["git", "log", "-1", "--format=%an <%ae>"],
1949
+ cwd=repo,
1950
+ check=True,
1951
+ stdout=subprocess.PIPE,
1952
+ text=True,
1953
+ ).stdout.strip()
1954
+
1955
+ assert result.committed
1956
+ assert _head_commit_message(repo) == "chore: ready for F-007"
1957
+ assert _head_files(repo) == {"visible.txt"}
1958
+ assert author == f"{PIPELINE_FALLBACK_GIT_NAME} <{PIPELINE_FALLBACK_GIT_EMAIL}>"
1959
+ assert _short_status(repo) == ""
1960
+
1961
+
1962
+ def test_preflight_ready_commit_force_stages_visible_file_when_prizmkit_dir_is_ignored(tmp_path):
1963
+ from prizmkit_runtime.runner_bookkeeping import commit_preflight_ready_changes
1964
+
1965
+ repo = tmp_path / "repo"
1966
+ _init_temp_repo(repo)
1967
+ (repo / ".gitignore").write_text(".prizmkit/*\n", encoding="utf-8")
1968
+ subprocess.run(["git", "add", ".gitignore"], cwd=repo, check=True)
1969
+ subprocess.run(["git", "commit", "-m", "ignore prizmkit"], cwd=repo, check=True, stdout=subprocess.DEVNULL)
1970
+ (repo / ".prizmkit" / "dev-pipeline" / "templates").mkdir(parents=True)
1971
+ (repo / ".prizmkit" / "dev-pipeline" / "templates" / "bootstrap-prompt.md").write_text("ignored runtime\n", encoding="utf-8")
1972
+ (repo / "visible.txt").write_text("dirty\n", encoding="utf-8")
1973
+
1974
+ result = commit_preflight_ready_changes(repo, "F-008")
1975
+
1976
+ assert result.committed
1977
+ assert _head_commit_message(repo) == "chore: ready for F-008"
1978
+ assert _head_files(repo) == {"visible.txt"}
1979
+ assert _short_status(repo) == ""
1980
+
1981
+
1918
1982
  def test_materialize_worktree_support_assets_copies_ignored_assets_without_retired_shell_sources(tmp_path):
1919
1983
  from prizmkit_runtime.gitops import (
1920
1984
  BranchContext,
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.1.117",
2
+ "version": "1.1.119",
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.",
@@ -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` |
@@ -47,13 +47,15 @@ Before any action, validate:
47
47
 
48
48
  1. **refactor pipeline exists**: Confirm `.prizmkit/dev-pipeline/cli.py` is present
49
49
  2. **For start**: `.prizmkit/plans/refactor-list.json` must exist in `.prizmkit/plans/` (or user-specified path)
50
- 3. **Dependencies**: `python3`, `git`, and AI CLI (`cbc` or `claude`) must be in PATH
50
+ 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.
51
51
  4. **Python version**: Requires Python 3.10+ for the unified dev-pipeline runtime
52
52
  5. **Browser tools** (optional): If any refactor has `browser_interaction` field, check the corresponding tool is available. Refactors may specify `tool: "playwright-cli"`, `tool: "opencli"`, or `tool: "auto"` (AI chooses at runtime).
53
53
 
54
54
  Quick check:
55
55
  ```bash
56
- command -v python3 && command -v git && (command -v cbc || command -v claude) && echo "All dependencies OK"
56
+ command -v python3 >/dev/null && command -v git >/dev/null && echo "Core dependencies OK"
57
+ # AI CLI check: read `${SKILL_DIR}/references/configuration.md` §Configured AI CLI Prerequisite Check.
58
+ # It must print `Configured AI CLI: <name>` and verify that exact executable.
57
59
  # Optional: browser interaction support (check both tools — refactors may use either)
58
60
  command -v playwright-cli && echo "playwright-cli OK" || echo "playwright-cli not found (playwright browser verification will be skipped)"
59
61
  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 refactor 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:
@@ -38,7 +77,7 @@ Not exposed in interactive menu, pass via `--env`:
38
77
  | Test baseline failing | Fix failing tests before starting refactoring — behavior preservation requires a green baseline |
39
78
  | `python3` not installed | Install Python 3.10+ and rerun the Python runtime command |
40
79
  | `git` not installed | Install git; the Python runtime uses git for branch/worktree/status operations |
41
- | `cbc`/`claude` not in PATH | Check AI CLI installation |
80
+ | 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. |
42
81
  | Refactor pipeline already running | Show status, ask if user wants to stop and restart |
43
82
  | PID file stale (process dead) | `python3 ./.prizmkit/dev-pipeline/cli.py daemon refactor start .prizmkit/plans/refactor-list.json` auto-cleans, retry start |
44
83
  | Launch failed (process died immediately) | Show last 20 lines of log: `python3 ./.prizmkit/dev-pipeline/cli.py daemon refactor logs --lines 20` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prizmkit",
3
- "version": "1.1.117",
3
+ "version": "1.1.119",
4
4
  "description": "Create a new PrizmKit-powered project with clean initialization — no framework dev files, just what you need.",
5
5
  "type": "module",
6
6
  "bin": {