prizmkit 1.1.124 → 1.1.125
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/adapters/claude/command-adapter.js +4 -6
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +3 -4
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +4 -3
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +4 -3
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +7 -2
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +5 -4
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +4 -4
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +5 -4
- package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +6 -5
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +2 -1
- package/bundled/dev-pipeline/tests/test_unified_cli.py +16 -10
- package/bundled/skills/_metadata.json +4 -4
- package/bundled/skills/prizmkit-code-review/SKILL.md +90 -96
- package/bundled/skills/prizmkit-code-review/assets/reviewer-role.json +3 -5
- package/bundled/skills/prizmkit-code-review/references/review-report-template.md +18 -31
- package/bundled/skills/prizmkit-code-review/references/reviewer-agent-prompt.md +44 -62
- package/bundled/skills/prizmkit-code-review/scripts/render_review_report.py +86 -112
- package/package.json +1 -1
- package/bundled/skills/prizmkit-code-review/references/reviewer-execution-protocol.md +0 -179
- package/bundled/skills/prizmkit-code-review/references/reviewer-workspace-protocol.md +0 -118
- package/bundled/skills/prizmkit-code-review/scripts/check_loop.py +0 -380
- 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())
|