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.
- package/bundled/VERSION.json +3 -3
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +50 -6
- package/bundled/dev-pipeline/prizmkit_runtime/runner_bookkeeping.py +21 -11
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +82 -4
- package/bundled/dev-pipeline/tests/test_unified_cli.py +69 -5
- package/bundled/skills/_metadata.json +1 -1
- 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/refactor-pipeline-launcher/SKILL.md +4 -2
- package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +40 -1
- package/package.json +1 -1
package/bundled/VERSION.json
CHANGED
|
@@ -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
|
|
37
|
-
":(top,exclude,glob).prizmkit/dev-pipeline
|
|
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), *
|
|
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=
|
|
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
|
-
|
|
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
|
-
|
|
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", "
|
|
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}", "--",
|
|
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
|
-
|
|
1576
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
1783
|
-
|
|
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(
|
|
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,
|
|
@@ -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` |
|
|
@@ -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
|
|
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
|
|
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
|
-
|
|
|
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` |
|