prizmkit 1.1.124 → 1.1.126

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 (32) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/adapters/claude/command-adapter.js +4 -6
  3. package/bundled/dev-pipeline/prizmkit_runtime/reset.py +49 -3
  4. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +75 -14
  5. package/bundled/dev-pipeline/prizmkit_runtime/runtime_helper.py +37 -0
  6. package/bundled/dev-pipeline/prizmkit_runtime/task_checkout.py +267 -0
  7. package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +4 -4
  8. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +1 -1
  9. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +5 -16
  10. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +6 -14
  11. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +8 -4
  12. package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +9 -8
  13. package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +12 -15
  14. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +11 -16
  15. package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +9 -8
  16. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +8 -4
  17. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +1 -1
  18. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +1 -1
  19. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +206 -3
  20. package/bundled/dev-pipeline/tests/test_runtime_helper.py +36 -0
  21. package/bundled/dev-pipeline/tests/test_unified_cli.py +21 -12
  22. package/bundled/skills/_metadata.json +4 -4
  23. package/bundled/skills/prizmkit-code-review/SKILL.md +130 -103
  24. package/bundled/skills/prizmkit-code-review/assets/reviewer-role.json +3 -5
  25. package/bundled/skills/prizmkit-code-review/references/review-report-template.md +89 -38
  26. package/bundled/skills/prizmkit-code-review/references/reviewer-agent-prompt.md +44 -62
  27. package/bundled/skills/prizmkit-code-review/scripts/render_review_report.py +249 -143
  28. package/package.json +1 -1
  29. package/bundled/skills/prizmkit-code-review/references/reviewer-execution-protocol.md +0 -179
  30. package/bundled/skills/prizmkit-code-review/references/reviewer-workspace-protocol.md +0 -118
  31. package/bundled/skills/prizmkit-code-review/scripts/check_loop.py +0 -380
  32. package/bundled/skills/prizmkit-code-review/scripts/workspace_snapshot.py +0 -222
@@ -1,222 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Generate a deterministic, path-safe workspace manifest for code review."""
3
-
4
- from __future__ import annotations
5
-
6
- import argparse
7
- import hashlib
8
- import json
9
- import os
10
- import subprocess
11
- import sys
12
- from pathlib import Path
13
- from typing import Any
14
-
15
- SNAPSHOT_PROTOCOL_VERSION = 1
16
-
17
-
18
- class SnapshotError(RuntimeError):
19
- """Raised when the workspace snapshot cannot be generated safely."""
20
-
21
-
22
- def _git(root: Path, *args: str) -> bytes:
23
- try:
24
- return subprocess.check_output(
25
- ["git", "-c", "core.quotepath=false", *args],
26
- cwd=root,
27
- stderr=subprocess.PIPE,
28
- )
29
- except (OSError, subprocess.CalledProcessError) as error:
30
- detail = getattr(error, "stderr", b"").decode("utf-8", "replace").strip()
31
- raise SnapshotError(detail or f"git {' '.join(args)} failed") from error
32
-
33
-
34
- def _decode(value: bytes) -> str:
35
- return value.decode("utf-8", "surrogateescape")
36
-
37
-
38
- def _sha256_bytes(value: bytes) -> str:
39
- return hashlib.sha256(value).hexdigest()
40
-
41
-
42
- def _file_identity(root: Path, relative_path: str) -> dict[str, Any] | None:
43
- path = root / relative_path
44
- if not path.is_symlink() and not path.is_file():
45
- return None
46
- if path.is_symlink():
47
- content = os.readlink(path).encode("utf-8", "surrogateescape")
48
- kind = "symlink"
49
- else:
50
- content = path.read_bytes()
51
- kind = "file"
52
- return {"kind": kind, "sha256": _sha256_bytes(content), "size": len(content)}
53
-
54
-
55
- def _index_identity(root: Path, relative_path: str) -> str | None:
56
- try:
57
- return _decode(_git(root, "rev-parse", f":{relative_path}")).strip()
58
- except SnapshotError:
59
- return None
60
-
61
-
62
- def _baseline_identity(root: Path, relative_path: str) -> str | None:
63
- try:
64
- return _decode(_git(root, "rev-parse", f"HEAD:{relative_path}")).strip()
65
- except SnapshotError:
66
- return None
67
-
68
-
69
- def _parse_name_status(raw: bytes, layer: str, root: Path) -> list[dict[str, Any]]:
70
- fields = raw.rstrip(b"\0").split(b"\0") if raw else []
71
- entries: list[dict[str, Any]] = []
72
- index = 0
73
- while index < len(fields):
74
- status = _decode(fields[index])
75
- index += 1
76
- code = status[:1]
77
- if code in {"R", "C"}:
78
- if index + 1 >= len(fields):
79
- raise SnapshotError("malformed rename or copy record")
80
- source = _decode(fields[index])
81
- destination = _decode(fields[index + 1])
82
- index += 2
83
- entry = {
84
- "layer": layer,
85
- "status": code,
86
- "similarity": status[1:] or None,
87
- "source_path": source,
88
- "path": destination,
89
- }
90
- else:
91
- if index >= len(fields):
92
- raise SnapshotError("malformed name-status record")
93
- path = _decode(fields[index])
94
- index += 1
95
- entry = {"layer": layer, "status": code, "path": path}
96
-
97
- entry["worktree_content"] = _file_identity(root, entry["path"])
98
- baseline_path = entry.get("source_path", entry["path"])
99
- entry["baseline_blob"] = _baseline_identity(root, baseline_path)
100
- if layer == "staged" and code != "D":
101
- entry["index_blob"] = _index_identity(root, entry["path"])
102
- entries.append(entry)
103
-
104
- return entries
105
-
106
-
107
- def _untracked_entries(root: Path) -> list[dict[str, Any]]:
108
- raw = _git(root, "ls-files", "--others", "--exclude-standard", "-z")
109
- paths = [_decode(field) for field in raw.rstrip(b"\0").split(b"\0") if field]
110
- return [
111
- {
112
- "layer": "untracked",
113
- "status": "A",
114
- "path": path,
115
- "worktree_content": _file_identity(root, path),
116
- "baseline_blob": None,
117
- }
118
- for path in paths
119
- ]
120
-
121
-
122
- def _repository_identity(root: Path) -> str:
123
- try:
124
- remote = _decode(_git(root, "config", "--get", "remote.origin.url")).strip()
125
- except SnapshotError:
126
- remote = ""
127
- return remote or root.name
128
-
129
-
130
- def _supporting_context(root: Path, paths: list[str]) -> list[dict[str, Any]]:
131
- records: list[dict[str, Any]] = []
132
- for relative_path in sorted(set(paths)):
133
- candidate = (root / relative_path).resolve()
134
- try:
135
- candidate.relative_to(root)
136
- except ValueError as error:
137
- raise SnapshotError(
138
- f"supporting context escapes repository: {relative_path}"
139
- ) from error
140
- identity = _file_identity(root, relative_path)
141
- if identity is None:
142
- raise SnapshotError(f"supporting context is not a file: {relative_path}")
143
- records.append({"path": relative_path, "content": identity})
144
- return records
145
-
146
-
147
- def build_manifest(root: Path, supporting_paths: list[str] | None = None) -> dict[str, Any]:
148
- """Return a deterministic manifest for the repository's current logical change."""
149
- repository_root = Path(
150
- _decode(_git(root, "rev-parse", "--show-toplevel")).strip()
151
- ).resolve()
152
- baseline = _decode(_git(repository_root, "rev-parse", "HEAD")).strip()
153
-
154
- entries = [
155
- *_parse_name_status(
156
- _git(repository_root, "diff", "--cached", "--name-status", "-z", "-M"),
157
- "staged",
158
- repository_root,
159
- ),
160
- *_parse_name_status(
161
- _git(repository_root, "diff", "--name-status", "-z", "-M"),
162
- "unstaged",
163
- repository_root,
164
- ),
165
- *_untracked_entries(repository_root),
166
- ]
167
- entries.sort(
168
- key=lambda entry: (
169
- entry["path"],
170
- entry["layer"],
171
- entry.get("source_path", ""),
172
- )
173
- )
174
-
175
- manifest: dict[str, Any] = {
176
- "protocol_version": SNAPSHOT_PROTOCOL_VERSION,
177
- "repository_identity": _repository_identity(repository_root),
178
- "baseline_identity": baseline,
179
- "changes": entries,
180
- "supporting_context": _supporting_context(
181
- repository_root, supporting_paths or []
182
- ),
183
- }
184
- canonical = json.dumps(
185
- manifest,
186
- ensure_ascii=True,
187
- separators=(",", ":"),
188
- sort_keys=True,
189
- ).encode("utf-8")
190
- manifest["snapshot_identity"] = _sha256_bytes(canonical)
191
- return manifest
192
-
193
-
194
- def parse_args() -> argparse.Namespace:
195
- parser = argparse.ArgumentParser(description=__doc__)
196
- parser.add_argument("--root", default=".", help="path inside the Git repository")
197
- parser.add_argument(
198
- "--supporting-context",
199
- action="append",
200
- default=[],
201
- metavar="PATH",
202
- help="unchanged repository-relative file required for review; repeat as needed",
203
- )
204
- return parser.parse_args()
205
-
206
-
207
- def main() -> int:
208
- args = parse_args()
209
- try:
210
- manifest = build_manifest(
211
- Path(args.root).resolve(), args.supporting_context
212
- )
213
- except SnapshotError as error:
214
- print(json.dumps({"error": str(error)}, sort_keys=True))
215
- return 1
216
-
217
- print(json.dumps(manifest, ensure_ascii=True, indent=2, sort_keys=True))
218
- return 0
219
-
220
-
221
- if __name__ == "__main__":
222
- sys.exit(main())