prizmkit 1.1.72 → 1.1.76
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/agents/prizm-dev-team-dev.md +11 -1
- package/bundled/dev-pipeline/.env.example +1 -0
- package/bundled/dev-pipeline/lib/common.sh +53 -0
- package/bundled/dev-pipeline/lib/heartbeat.sh +94 -42
- package/bundled/dev-pipeline/run-bugfix.sh +5 -0
- package/bundled/dev-pipeline/run-feature.sh +5 -0
- package/bundled/dev-pipeline/run-recovery.sh +6 -0
- package/bundled/dev-pipeline/run-refactor.sh +5 -0
- package/bundled/dev-pipeline/scripts/parse-stream-progress.py +265 -5
- package/bundled/dev-pipeline/templates/agent-prompts/dev-implement.md +21 -0
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +5 -9
- package/bundled/dev-pipeline/templates/sections/feature-context.md +3 -18
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-base.md +6 -12
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +10 -3
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +1 -0
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +4 -8
- package/bundled/dev-pipeline-windows/lib/common.ps1 +23 -0
- package/bundled/dev-pipeline-windows/lib/heartbeat.sh +439 -0
- package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +33 -2
- package/bundled/dev-pipeline-windows/run-recovery.ps1 +11 -0
- package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +265 -5
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +21 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +1 -1
- package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +27 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +543 -14
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +664 -14
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +741 -14
- package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +2 -2
- package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +1 -1
- package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +1 -1
- package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +3 -3
- package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +3 -18
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +239 -40
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +75 -26
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +142 -36
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +2 -2
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +7 -17
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +10 -3
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +3 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +7 -3
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +1 -3
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +2 -2
- package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +13 -17
- package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +2 -4
- package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +1 -1
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +1 -0
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +1 -0
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +1 -0
- package/package.json +1 -1
|
@@ -137,7 +137,10 @@ class ProgressTracker:
|
|
|
137
137
|
self.event_format = ""
|
|
138
138
|
self.active_subagent_count = 0
|
|
139
139
|
self.subagent_status_counts = Counter()
|
|
140
|
+
self._subagent_spawn_count = 0
|
|
140
141
|
self.codex_child_thread_ids = set()
|
|
142
|
+
self.cb_session_id = ""
|
|
143
|
+
self.cb_cwd = ""
|
|
141
144
|
self.claude_session_id = ""
|
|
142
145
|
self.claude_cwd = ""
|
|
143
146
|
self.claude_task_states = {}
|
|
@@ -147,6 +150,7 @@ class ProgressTracker:
|
|
|
147
150
|
self.last_child_activity_at = ""
|
|
148
151
|
self._codex_child_session_paths = {}
|
|
149
152
|
self._claude_child_session_files = []
|
|
153
|
+
self._cb_child_session_files = []
|
|
150
154
|
self._last_child_scan_at = 0.0
|
|
151
155
|
self._last_claude_fallback_scan_at = 0.0
|
|
152
156
|
self._last_claude_fallback_scan_key = ""
|
|
@@ -158,10 +162,13 @@ class ProgressTracker:
|
|
|
158
162
|
def process_event(self, event):
|
|
159
163
|
"""Process a single stream-json event and update state.
|
|
160
164
|
|
|
161
|
-
Supports
|
|
165
|
+
Supports three formats:
|
|
162
166
|
1. Claude API raw stream: message_start, content_block_start, content_block_delta, etc.
|
|
163
167
|
2. Claude Code verbose stream-json: assistant, user, tool_result, system, etc.
|
|
164
168
|
(produced by claude/claude-internal --verbose --output-format stream-json)
|
|
169
|
+
3. CodeBuddy stream-json: message/function_call/function_call_result events
|
|
170
|
+
(produced by cbc --print -y --output-format stream-json — message type
|
|
171
|
+
with sessionId/cwd metadata, function_call for tool invocations).
|
|
165
172
|
"""
|
|
166
173
|
event_type = event.get("type", "")
|
|
167
174
|
|
|
@@ -233,6 +240,93 @@ class ProgressTracker:
|
|
|
233
240
|
|
|
234
241
|
return
|
|
235
242
|
|
|
243
|
+
# ── CodeBuddy stream-json format ──────────────────────────────
|
|
244
|
+
# cbc --print -y --output-format stream-json emits:
|
|
245
|
+
# message (role=user/assistant), function_call, function_call_result,
|
|
246
|
+
# file-history-snapshot, error. The first user message carries
|
|
247
|
+
# sessionId and cwd metadata; later function_call items carry the
|
|
248
|
+
# same fields.
|
|
249
|
+
# NOTE: cbc emits tool invocations as EITHER tool_use blocks inside a
|
|
250
|
+
# message (role=assistant) event OR as standalone function_call events,
|
|
251
|
+
# but not both for the same invocation. The two handlers are
|
|
252
|
+
# complementary rather than redundant, so no deduplication is needed.
|
|
253
|
+
# Detect via event_type=="message" with a "sessionId" field.
|
|
254
|
+
# Must run before the Claude Code assistant / tool_result branches
|
|
255
|
+
# because those also accept those types but use different internals.
|
|
256
|
+
if event_type == "message" and isinstance(event.get("sessionId"), str):
|
|
257
|
+
self.event_format = self.event_format or "stream-json"
|
|
258
|
+
role = event.get("role", "")
|
|
259
|
+
sid = event.get("sessionId", "")
|
|
260
|
+
cwd = event.get("cwd", "")
|
|
261
|
+
if sid and not self.cb_session_id:
|
|
262
|
+
self.cb_session_id = sid.strip()
|
|
263
|
+
if cwd and not self.cb_cwd:
|
|
264
|
+
self.cb_cwd = cwd.strip()
|
|
265
|
+
if role == "assistant":
|
|
266
|
+
self.message_count += 1
|
|
267
|
+
self.is_active = True
|
|
268
|
+
content = event.get("content", [])
|
|
269
|
+
if isinstance(content, list):
|
|
270
|
+
for block in content:
|
|
271
|
+
block_type = block.get("type", "")
|
|
272
|
+
if block_type == "tool_use":
|
|
273
|
+
tool_name = block.get("name", "unknown")
|
|
274
|
+
self.current_tool = tool_name
|
|
275
|
+
self.tool_call_counts[tool_name] += 1
|
|
276
|
+
self.total_tool_calls += 1
|
|
277
|
+
tool_input = block.get("input", {})
|
|
278
|
+
if isinstance(tool_input, dict):
|
|
279
|
+
self._extract_tool_summary_from_dict(tool_input)
|
|
280
|
+
self._detect_phase(
|
|
281
|
+
json.dumps(tool_input, ensure_ascii=False)[:500]
|
|
282
|
+
)
|
|
283
|
+
# Track CodeBuddy Agent/Task tool invocations as
|
|
284
|
+
# potential sub-agent spawns (Gap 3 fix).
|
|
285
|
+
self._record_cb_agent_tool_call(tool_name, tool_input)
|
|
286
|
+
elif block_type == "text":
|
|
287
|
+
text = block.get("text", "")
|
|
288
|
+
if text.strip():
|
|
289
|
+
self.last_text_snippet = text.strip()[:120]
|
|
290
|
+
self._detect_phase(text)
|
|
291
|
+
self._detect_terminal_error(
|
|
292
|
+
text, require_error_context=True
|
|
293
|
+
)
|
|
294
|
+
return
|
|
295
|
+
|
|
296
|
+
# CodeBuddy function_call / function_call_result in stream-json
|
|
297
|
+
if event_type in ("function_call", "function_call_result"):
|
|
298
|
+
self.event_format = self.event_format or "stream-json"
|
|
299
|
+
sid = event.get("sessionId", "")
|
|
300
|
+
cwd = event.get("cwd", "")
|
|
301
|
+
if sid and not self.cb_session_id:
|
|
302
|
+
self.cb_session_id = sid.strip()
|
|
303
|
+
if cwd and not self.cb_cwd:
|
|
304
|
+
self.cb_cwd = cwd.strip()
|
|
305
|
+
if event_type == "function_call":
|
|
306
|
+
tool_name = event.get("name", "unknown")
|
|
307
|
+
self.current_tool = tool_name
|
|
308
|
+
self.tool_call_counts[tool_name] += 1
|
|
309
|
+
self.total_tool_calls += 1
|
|
310
|
+
self.is_active = True
|
|
311
|
+
# Extract summary from arguments
|
|
312
|
+
raw_args = event.get("arguments", "")
|
|
313
|
+
if isinstance(raw_args, str) and raw_args.strip():
|
|
314
|
+
self._extract_tool_summary(raw_args)
|
|
315
|
+
self._detect_phase(raw_args[:500])
|
|
316
|
+
# Track Agent/Task tool invocations as sub-agent spawns
|
|
317
|
+
self._record_cb_agent_tool_call(tool_name, raw_args)
|
|
318
|
+
elif event_type == "function_call_result":
|
|
319
|
+
self.current_tool = None
|
|
320
|
+
# Check result text for phase / error hints
|
|
321
|
+
result_text = event.get("output") or ""
|
|
322
|
+
if result_text and isinstance(result_text, str):
|
|
323
|
+
stripped = result_text.strip()
|
|
324
|
+
if stripped:
|
|
325
|
+
self.last_text_snippet = stripped[:120]
|
|
326
|
+
self._detect_phase(stripped)
|
|
327
|
+
self._detect_terminal_error(stripped, require_error_context=True)
|
|
328
|
+
return
|
|
329
|
+
|
|
236
330
|
# ── Claude Code verbose format ──────────────────────────────
|
|
237
331
|
if event_type == "assistant":
|
|
238
332
|
self.event_format = self.event_format or "stream-json"
|
|
@@ -259,11 +353,52 @@ class ProgressTracker:
|
|
|
259
353
|
self._detect_phase(text)
|
|
260
354
|
self._detect_terminal_error(text, require_error_context=True)
|
|
261
355
|
|
|
262
|
-
elif event_type == "tool_result" or
|
|
356
|
+
elif event_type == "tool_result" or (
|
|
357
|
+
event_type == "user"
|
|
358
|
+
and not isinstance(event.get("sessionId"), str)
|
|
359
|
+
):
|
|
263
360
|
# tool_result contains output from tool execution
|
|
264
361
|
self.event_format = self.event_format or "stream-json"
|
|
265
362
|
self.is_active = True
|
|
266
363
|
|
|
364
|
+
# Check for error patterns in tool_result content (supports both formats):
|
|
365
|
+
# A) Top-level tool_result events: event["content"] is the result text
|
|
366
|
+
# B) Nested user events: event["message"]["content"][] has type=="tool_result" items
|
|
367
|
+
content_candidates = []
|
|
368
|
+
|
|
369
|
+
# Format A: top-level tool_result (Claude Code)
|
|
370
|
+
if event_type == "tool_result":
|
|
371
|
+
content_candidates.append(str(event.get("content", "")))
|
|
372
|
+
|
|
373
|
+
# Format B: nested inside user event (Claude Code, NOT CodeBuddy)
|
|
374
|
+
if event_type == "user":
|
|
375
|
+
message = event.get("message", {})
|
|
376
|
+
content_list = message.get("content", [])
|
|
377
|
+
if isinstance(content_list, list):
|
|
378
|
+
for item in content_list:
|
|
379
|
+
if isinstance(item, dict) and item.get("type") == "tool_result":
|
|
380
|
+
content_candidates.append(str(item.get("content", "")))
|
|
381
|
+
|
|
382
|
+
for result_text in content_candidates:
|
|
383
|
+
if "shorter than the provided offset" in result_text:
|
|
384
|
+
self.errors.append({
|
|
385
|
+
"type": "read_offset_overflow",
|
|
386
|
+
"tool": self.current_tool,
|
|
387
|
+
"at": datetime.now(timezone.utc).isoformat(),
|
|
388
|
+
})
|
|
389
|
+
break # one error per event is enough
|
|
390
|
+
elif "Wasted call" in result_text:
|
|
391
|
+
self.errors.append({
|
|
392
|
+
"type": "wasted_call",
|
|
393
|
+
"tool": self.current_tool,
|
|
394
|
+
"at": datetime.now(timezone.utc).isoformat(),
|
|
395
|
+
})
|
|
396
|
+
break
|
|
397
|
+
|
|
398
|
+
# Keep only last 20 errors to prevent unbounded growth in progress.json
|
|
399
|
+
if len(self.errors) > 20:
|
|
400
|
+
self.errors = self.errors[-20:]
|
|
401
|
+
|
|
267
402
|
elif event_type == "system":
|
|
268
403
|
# System events (hooks, init, task notifications, etc.) — track but don't count as messages.
|
|
269
404
|
self.event_format = self.event_format or "stream-json"
|
|
@@ -427,15 +562,60 @@ class ProgressTracker:
|
|
|
427
562
|
self._current_tool_input_parts = []
|
|
428
563
|
|
|
429
564
|
elif event_type == "error":
|
|
565
|
+
self.event_format = self.event_format or "stream-json"
|
|
430
566
|
error_msg = event.get("error", {}).get("message", "Unknown error")
|
|
567
|
+
errors = event.get("errors") or []
|
|
568
|
+
if isinstance(errors, list) and errors:
|
|
569
|
+
error_msg = "; ".join(str(e) for e in errors[:3])
|
|
431
570
|
self.errors.append(error_msg)
|
|
432
571
|
self._detect_terminal_error(str(error_msg))
|
|
433
572
|
|
|
573
|
+
# ── CodeBuddy file-history-snapshot (ignore) ─────────────────
|
|
574
|
+
elif event_type == "file-history-snapshot":
|
|
575
|
+
return
|
|
576
|
+
|
|
434
577
|
# Check for subagent indicator
|
|
435
578
|
if event.get("parent_tool_use_id"):
|
|
436
579
|
# This is a sub-agent event; tool name is still tracked normally
|
|
437
580
|
pass
|
|
438
581
|
|
|
582
|
+
def _record_cb_agent_tool_call(self, tool_name, raw_args):
|
|
583
|
+
"""Record a CodeBuddy Agent/Task* tool invocation for sub-agent tracking.
|
|
584
|
+
|
|
585
|
+
CodeBuddy spawns sub-agents via:
|
|
586
|
+
- Agent(prompt=..., run_in_background=True/False) — synchronous or bg fork
|
|
587
|
+
- TaskCreate(subject=..., description=...) — scheduled task
|
|
588
|
+
|
|
589
|
+
(TaskUpdate/TaskOutput exist but are lifecycle-only — they track
|
|
590
|
+
existing tasks rather than creating new sub-agents, so we don't
|
|
591
|
+
count them as spawns.)
|
|
592
|
+
|
|
593
|
+
We don't get child session ids from these tool calls in the stream,
|
|
594
|
+
but recording the count lets the heartbeat code in heartbeat.sh apply
|
|
595
|
+
an extended stale-kill window just as it does for Codex spawn_agent.
|
|
596
|
+
"""
|
|
597
|
+
if tool_name not in ("Agent", "TaskCreate"):
|
|
598
|
+
return
|
|
599
|
+
# Both dict (from message events) and str/JSON (from function_call events)
|
|
600
|
+
# are supported.
|
|
601
|
+
if isinstance(raw_args, dict):
|
|
602
|
+
args = raw_args
|
|
603
|
+
elif isinstance(raw_args, str):
|
|
604
|
+
try:
|
|
605
|
+
args = json.loads(raw_args)
|
|
606
|
+
except (json.JSONDecodeError, TypeError):
|
|
607
|
+
return
|
|
608
|
+
else:
|
|
609
|
+
return
|
|
610
|
+
# For Agent tool, subagent_type or run_in_background hints at delegation
|
|
611
|
+
if tool_name == "Agent":
|
|
612
|
+
subagent_type = args.get("subagent_type", "")
|
|
613
|
+
prompt = args.get("prompt", "")
|
|
614
|
+
if subagent_type or prompt:
|
|
615
|
+
self._subagent_spawn_count += 1
|
|
616
|
+
elif tool_name == "TaskCreate":
|
|
617
|
+
self._subagent_spawn_count += 1
|
|
618
|
+
|
|
439
619
|
def _record_terminal_result(self, text="", is_error=False, api_error_status=None, api_error_code=""):
|
|
440
620
|
"""Record a Claude Code terminal result event."""
|
|
441
621
|
terminal_text = str(text or "")
|
|
@@ -719,17 +899,85 @@ class ProgressTracker:
|
|
|
719
899
|
return []
|
|
720
900
|
return matches
|
|
721
901
|
|
|
902
|
+
def _cb_projects_dir(self):
|
|
903
|
+
"""Return the CodeBuddy projects directory for transcript lookup.
|
|
904
|
+
|
|
905
|
+
CodeBuddy stores per-project session transcripts and sub-agent data
|
|
906
|
+
under ~/.codebuddy/projects/{projectDir}/{sessionId}/.
|
|
907
|
+
"""
|
|
908
|
+
cb_config = os.environ.get("CODEBUDDY_CONFIG_DIR")
|
|
909
|
+
if cb_config:
|
|
910
|
+
return Path(cb_config).expanduser() / "projects"
|
|
911
|
+
cb_home = os.environ.get("CODEBUDDY_HOME")
|
|
912
|
+
if cb_home:
|
|
913
|
+
return Path(cb_home).expanduser() / "projects"
|
|
914
|
+
return Path.home() / ".codebuddy" / "projects"
|
|
915
|
+
|
|
916
|
+
def _cb_project_key(self):
|
|
917
|
+
"""Encode cwd the same way CodeBuddy stores project subdirs.
|
|
918
|
+
|
|
919
|
+
CodeBuddy uses the same sanitisation as Claude Code (\\, /, : → -)
|
|
920
|
+
but strips the leading '-' so "/Users/test/MyProject" becomes
|
|
921
|
+
"Users-test-MyProject".
|
|
922
|
+
"""
|
|
923
|
+
cwd = self.cb_cwd
|
|
924
|
+
if not cwd:
|
|
925
|
+
return ""
|
|
926
|
+
return cwd.replace("\\", "-").replace("/", "-").replace(":", "").lstrip("-")
|
|
927
|
+
|
|
928
|
+
def _find_cb_child_session_files(self):
|
|
929
|
+
"""Find CodeBuddy subagent transcript data for this parent session.
|
|
930
|
+
|
|
931
|
+
CodeBuddy sub-agents store tool-results/{callId}.txt files; conversation
|
|
932
|
+
transcripts are not (as of 2026) written as agent-*.jsonl files in the
|
|
933
|
+
subagents/ directory. We track the tool-results .txt files as a proxy
|
|
934
|
+
for child activity so the heartbeat monitor can extend the stale-kill
|
|
935
|
+
window while sub-agents are running.
|
|
936
|
+
"""
|
|
937
|
+
if not self.cb_session_id:
|
|
938
|
+
return []
|
|
939
|
+
|
|
940
|
+
projects_dir = self._cb_projects_dir()
|
|
941
|
+
if not projects_dir.exists():
|
|
942
|
+
return []
|
|
943
|
+
|
|
944
|
+
project_key = self._cb_project_key()
|
|
945
|
+
if not project_key:
|
|
946
|
+
return []
|
|
947
|
+
|
|
948
|
+
subagents_dir = (
|
|
949
|
+
projects_dir / project_key / self.cb_session_id / "subagents"
|
|
950
|
+
)
|
|
951
|
+
if not subagents_dir.exists():
|
|
952
|
+
return []
|
|
953
|
+
|
|
954
|
+
# Collect all files under each agent-{id} subdirectory. These are
|
|
955
|
+
# currently tool-results/{callId}.txt files, but the glob also picks
|
|
956
|
+
# up future agent-*.jsonl transcripts should CodeBuddy add them.
|
|
957
|
+
try:
|
|
958
|
+
return sorted(
|
|
959
|
+
p for p in subagents_dir.glob("**/*")
|
|
960
|
+
if p.is_file()
|
|
961
|
+
)
|
|
962
|
+
except OSError:
|
|
963
|
+
return []
|
|
964
|
+
|
|
722
965
|
def refresh_child_session_activity(self, force=False):
|
|
723
966
|
"""Refresh child transcript file stats.
|
|
724
967
|
|
|
725
968
|
The heartbeat monitor uses this activity signature to treat subagent
|
|
726
969
|
transcript growth as real progress while the parent session is blocked
|
|
727
|
-
waiting for a child agent/tool result. Supports Codex child threads
|
|
728
|
-
Claude Code in-process teammate transcripts
|
|
970
|
+
waiting for a child agent/tool result. Supports Codex child threads,
|
|
971
|
+
Claude Code in-process teammate transcripts, and CodeBuddy sub-agent
|
|
972
|
+
file activity under subagents/.
|
|
729
973
|
"""
|
|
730
974
|
previous_signature = self.child_activity_signature
|
|
731
975
|
|
|
732
|
-
if
|
|
976
|
+
if (
|
|
977
|
+
not self.codex_child_thread_ids
|
|
978
|
+
and not self.claude_session_id
|
|
979
|
+
and not self.cb_session_id
|
|
980
|
+
):
|
|
733
981
|
self.child_session_files = []
|
|
734
982
|
self.child_total_bytes = 0
|
|
735
983
|
self.child_activity_signature = ""
|
|
@@ -750,6 +998,7 @@ class ProgressTracker:
|
|
|
750
998
|
if found:
|
|
751
999
|
self._codex_child_session_paths[thread_id] = found
|
|
752
1000
|
self._claude_child_session_files = self._find_claude_child_session_files()
|
|
1001
|
+
self._cb_child_session_files = self._find_cb_child_session_files()
|
|
753
1002
|
self._last_child_scan_at = now
|
|
754
1003
|
|
|
755
1004
|
files = []
|
|
@@ -788,6 +1037,14 @@ class ProgressTracker:
|
|
|
788
1037
|
for path in self._claude_child_session_files:
|
|
789
1038
|
add_file("claude", path.stem, path)
|
|
790
1039
|
|
|
1040
|
+
for path in self._cb_child_session_files:
|
|
1041
|
+
# Identifier for CodeBuddy sub-agent files: use the parent
|
|
1042
|
+
# directory name (agent-{id}) so heartbeat can distinguish
|
|
1043
|
+
# activity across different sub-agent instances.
|
|
1044
|
+
parent_name = path.parent.name if hasattr(path, "parent") else ""
|
|
1045
|
+
identifier = parent_name if parent_name.startswith("agent-") else path.name
|
|
1046
|
+
add_file("codebuddy", identifier, path)
|
|
1047
|
+
|
|
791
1048
|
self.child_session_files = files
|
|
792
1049
|
self.child_total_bytes = total_bytes
|
|
793
1050
|
self.child_activity_signature = "|".join(signature_parts)
|
|
@@ -823,11 +1080,14 @@ class ProgressTracker:
|
|
|
823
1080
|
"total_tool_calls": self.total_tool_calls,
|
|
824
1081
|
"active_subagent_count": self.active_subagent_count,
|
|
825
1082
|
"subagent_states": subagent_states,
|
|
1083
|
+
"subagent_spawn_count": self._subagent_spawn_count,
|
|
826
1084
|
"child_thread_ids": sorted(self.codex_child_thread_ids),
|
|
827
1085
|
"child_session_files": self.child_session_files,
|
|
828
1086
|
"child_total_bytes": self.child_total_bytes,
|
|
829
1087
|
"child_activity_signature": self.child_activity_signature,
|
|
830
1088
|
"last_child_activity_at": self.last_child_activity_at,
|
|
1089
|
+
"cb_session_id": self.cb_session_id,
|
|
1090
|
+
"cb_cwd": self.cb_cwd,
|
|
831
1091
|
"last_text_snippet": self.last_text_snippet,
|
|
832
1092
|
"last_result_is_error": self.last_result_is_error,
|
|
833
1093
|
"api_error_status": self.api_error_status,
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
"Read {{DEV_SUBAGENT_PATH}}. Implement feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}).
|
|
2
2
|
|
|
3
|
+
## Task Summary Card
|
|
4
|
+
|
|
5
|
+
**Objective**: Implement {{FEATURE_TITLE}}.
|
|
6
|
+
|
|
7
|
+
**Primary files** (see context-snapshot.md Section 4 for full manifest):
|
|
8
|
+
- Review plan.md Tasks section for the complete task-to-file mapping.
|
|
9
|
+
- Each task's `— file:` suffix identifies the target file.
|
|
10
|
+
|
|
11
|
+
**Test command**: `{{TEST_CMD}}`
|
|
12
|
+
|
|
13
|
+
**Known baseline failures**: `{{BASELINE_FAILURES}}`
|
|
14
|
+
|
|
15
|
+
**DO NOT**:
|
|
16
|
+
- Re-read source files already listed in context-snapshot.md Section 4 File Manifest
|
|
17
|
+
- Create new files unless a plan.md task explicitly requires it
|
|
18
|
+
- Run git commands
|
|
19
|
+
- Use mock success data or fake rows in UI/tests
|
|
20
|
+
|
|
3
21
|
## Required Inputs
|
|
4
22
|
|
|
5
23
|
1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` first.
|
|
@@ -35,6 +53,9 @@ Before returning, append `## Implementation Log` to `context-snapshot.md` with:
|
|
|
35
53
|
- Carry forward the Dev-isolated subset: skip scaffold/generated files listed in `context-snapshot.md`; verify dependency versions before install/build commands that resolve dependencies; after build/compile commands, ensure outputs are ignored and never commit generated artifacts.
|
|
36
54
|
- If tests fail, follow this Test Failure Recovery subset: classify failures as baseline, new regression, brittle test, or environment/tooling; fix new regressions and brittle tests while progress is being made; document baseline failures; write `failure-log.md` for blockers.
|
|
37
55
|
- Do not run git commands; staging and commit are handled by the orchestrator.
|
|
56
|
+
- **Edit safety**: If an Edit fails with 'String to replace not found', grep for the target text before retrying. Never guess file offsets — verify them with a Read or grep first.
|
|
57
|
+
- **Read safety**: If 3 consecutive Reads to the same file return 'shorter than offset' or 'Wasted call', STOP and report BLOCKED.
|
|
58
|
+
- **Test early**: Run `{{TEST_CMD}}` after every 3 successful Edit operations. Capture output to /tmp/test-out.txt and grep for failures.
|
|
38
59
|
|
|
39
60
|
Do not return success unless:
|
|
40
61
|
1. implementation tasks are complete;
|
|
@@ -131,7 +131,7 @@ If MISSING — build it now:
|
|
|
131
131
|
```bash
|
|
132
132
|
find . -maxdepth 2 -type d -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' -not -path '*/build/*' -not -path '*/__pycache__/*' -not -path '*/vendor/*' | sed -e 's;[^/]*/;|____;g;s;____|; |;g'
|
|
133
133
|
```
|
|
134
|
-
- **Section 3 —
|
|
134
|
+
- **Section 3 — Key TRAPS & RULES**: relevant TRAPS/RULES from prizm-docs (not full copies)
|
|
135
135
|
- **Section 4 — File Manifest**: For each file relevant to this feature, list: file path, why it's needed (modify/reference/test), key interface signatures (function names + params + return types). Do NOT include full file content — agents read files on-demand. Format:
|
|
136
136
|
### Files to Modify
|
|
137
137
|
| File | Why Needed | Key Interfaces |
|
|
@@ -148,12 +148,9 @@ If `EXISTING_CODE` is non-empty: your spec/plan/tasks must reflect this existing
|
|
|
148
148
|
Identify the top-level source directories from the results.
|
|
149
149
|
3. Scan the detected source directories for files related to this feature; read each one
|
|
150
150
|
4. Write `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md`:
|
|
151
|
-
- **Section 1 — Feature Brief**: feature description + acceptance criteria (copy from above)
|
|
152
|
-
- **Section 2 —
|
|
153
|
-
|
|
154
|
-
find . -maxdepth 2 -type d -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' -not -path '*/build/*' -not -path '*/__pycache__/*' -not -path '*/vendor/*' | sed -e 's;[^/]*/;|____;g;s;____|; |;g'
|
|
155
|
-
```
|
|
156
|
-
- **Section 3 — Prizm Context**: full content of root.prizm and relevant L1/L2 docs
|
|
151
|
+
- **Section 1 — Feature Brief**: feature description (2-3 lines) + acceptance criteria (copy from above)
|
|
152
|
+
- **Section 2 — Verification Gates**: copy the gate checklist from the Task Contract above
|
|
153
|
+
- **Section 3 — Key TRAPS & RULES**: extract only relevant TRAPS/RULES from prizm-docs that apply to files in this scope. Do NOT copy full L0/L1/L2 content.
|
|
157
154
|
- **Section 4 — File Manifest**: For each file relevant to this feature, list: file path, why it's needed (modify/reference/test), key interface signatures (function names + params + return types). Do NOT include full file content — agents read files on-demand. Format:
|
|
158
155
|
### Files to Modify
|
|
159
156
|
| File | Why Needed | Key Interfaces |
|
|
@@ -167,7 +164,6 @@ If `EXISTING_CODE` is non-empty: your spec/plan/tasks must reflect this existing
|
|
|
167
164
|
|
|
168
165
|
### Known TRAPS (from .prizmkit/prizm-docs/)
|
|
169
166
|
- <trap entries extracted from L1/L2 docs>
|
|
170
|
-
- **Section 5 — Existing Tests**: full content of related test files as code blocks
|
|
171
167
|
4. Confirm: `ls .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md`
|
|
172
168
|
|
|
173
169
|
**After Step A**: Use context-snapshot.md Section 4 File Manifest to guide targeted file reads. Do NOT scan directories or read unrelated files.
|
|
@@ -200,7 +196,7 @@ Spawn failure cap: for team/config/lock errors, retry at most once for this Revi
|
|
|
200
196
|
|
|
201
197
|
Prompt:
|
|
202
198
|
> "Read {{REVIEWER_SUBAGENT_PATH}}. For feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}):
|
|
203
|
-
> 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` FIRST — Section 3 has
|
|
199
|
+
> 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` FIRST — Section 3 has TRAPS/RULES, Section 4 has file manifest.
|
|
204
200
|
> 2. Cross-check `spec.md` and `plan.md` (including Tasks section) for consistency.
|
|
205
201
|
> 3. Before flagging CRITICAL or HIGH issues, read the relevant source files listed in the File Manifest to verify.
|
|
206
202
|
> Report: CRITICAL, HIGH, MEDIUM issues found (or 'No issues found')."
|
|
@@ -230,7 +226,7 @@ Spawn failure cap: for team/config/lock errors, retry at most once for this Crit
|
|
|
230
226
|
Prompt:
|
|
231
227
|
> "Read {{CRITIC_SUBAGENT_PATH}}. For feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}):
|
|
232
228
|
> **MODE: Plan Challenge**
|
|
233
|
-
> 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` FIRST — Section 3 has
|
|
229
|
+
> 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` FIRST — Section 3 has TRAPS/RULES, Section 4 has file manifest.
|
|
234
230
|
> 2. Read `.prizmkit/prizm-docs/root.prizm` and relevant L1/L2 docs for affected modules.
|
|
235
231
|
> 3. Read existing source files in the modules this plan touches.
|
|
236
232
|
> 4. Challenge plan.md against the project's existing architecture, patterns, and style.
|
|
@@ -4,33 +4,18 @@
|
|
|
4
4
|
Use this material according to the following priority:
|
|
5
5
|
|
|
6
6
|
1. **Task Contract** — defines current scope and Verification Gates.
|
|
7
|
-
2. **
|
|
8
|
-
3. **
|
|
9
|
-
4. **Project Brief** — product background and architecture alignment.
|
|
10
|
-
5. **App Global Context** — stack, runtime, and testing conventions.
|
|
11
|
-
6. **Project Conventions** — repository-specific rules.
|
|
12
|
-
|
|
13
|
-
### User Raw Context
|
|
14
|
-
|
|
15
|
-
{{USER_CONTEXT}}
|
|
7
|
+
2. **Completed Dependencies** — existing interfaces; do not re-implement.
|
|
8
|
+
3. **App Global Context** — stack, runtime, testing conventions.
|
|
16
9
|
|
|
17
10
|
### Completed Dependencies
|
|
18
11
|
|
|
19
|
-
> Use this section to understand available interfaces and avoid duplicating completed work.
|
|
20
|
-
|
|
21
12
|
{{COMPLETED_DEPENDENCIES}}
|
|
22
13
|
|
|
23
|
-
### Project Brief
|
|
24
|
-
|
|
25
|
-
> Use this section for alignment only. Do not treat unrelated backlog items as current feature scope.
|
|
26
|
-
|
|
27
|
-
{{PROJECT_BRIEF}}
|
|
28
|
-
|
|
29
14
|
### App Global Context
|
|
30
15
|
|
|
31
16
|
{{GLOBAL_CONTEXT}}
|
|
32
17
|
|
|
33
18
|
### Project Conventions
|
|
34
19
|
|
|
35
|
-
> Read {{PLATFORM_CONVENTIONS}} for project-level coding standards
|
|
20
|
+
> Read {{PLATFORM_CONVENTIONS}} for project-level coding standards and architecture decisions.
|
|
36
21
|
</feature-context>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
- **Section 3 —
|
|
1
|
+
- **Section 3 — Key TRAPS & RULES**: extract only relevant TRAPS/RULES from prizm-docs. Do NOT copy full L0/L1/L2 content — the orchestrator already loaded them.
|
|
2
2
|
- **Section 4 — File Manifest**: For each file relevant to this feature, list: file path, why it's needed (modify/reference/test), key interface signatures (function names + params + return types). Do NOT include full file content — agents read files on-demand. Format:
|
|
3
3
|
### Files to Modify
|
|
4
4
|
| File | Why Needed | Key Interfaces |
|
|
@@ -6,15 +6,9 @@ ls .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md 2>/dev/null && echo "EXI
|
|
|
6
6
|
|
|
7
7
|
If MISSING — build it now:
|
|
8
8
|
1. Read `.prizmkit/prizm-docs/root.prizm` and relevant L1/L2 prizm docs
|
|
9
|
-
2.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
4. Write `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md`:
|
|
16
|
-
- **Section 1 — Task Contract**: Objective, scope rule, non-scope rule, and Verification Gates from the Task Contract above
|
|
17
|
-
- **Section 2 — Project Structure**: run the following to get a visual directory tree, then paste output:
|
|
18
|
-
```bash
|
|
19
|
-
find . -maxdepth 2 -type d -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' -not -path '*/build/*' -not -path '*/__pycache__/*' -not -path '*/vendor/*' | sed -e 's;[^/]*/;|____;g;s;____|; |;g'
|
|
20
|
-
```
|
|
9
|
+
2. Read source files listed in the feature's scope (use prizm-docs KEY_FILES for guidance)
|
|
10
|
+
3. Write `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md`:
|
|
11
|
+
- **Section 1 — Feature Brief**: 2-3 line feature description from Task Contract
|
|
12
|
+
- **Section 2 — Verification Gates**: gate checklist from Task Contract
|
|
13
|
+
- **Section 3 — Key TRAPS & RULES**: extract only relevant TRAPS/RULES from prizm-docs that apply to files in this scope. Do NOT copy full L0/L1/L2 content.
|
|
14
|
+
- **Section 4 — File Manifest**: file path, why needed, key interfaces. Format: Files to Modify | Files for Reference tables.
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
- **Section 3 —
|
|
2
|
-
- **Section 4 —
|
|
3
|
-
|
|
1
|
+
- **Section 3 — Key TRAPS & RULES**: extract only relevant TRAPS/RULES from prizm-docs that apply to files in this scope. Do NOT copy full L0/L1/L2 content.
|
|
2
|
+
- **Section 4 — File Manifest**: For each relevant file, list path, why needed, key interface signatures. Use table format:
|
|
3
|
+
|
|
4
|
+
### Files to Modify
|
|
5
|
+
| File | Why Needed | Key Interfaces |
|
|
6
|
+
|------|-----------|----------------|
|
|
7
|
+
|
|
8
|
+
### Files for Reference
|
|
9
|
+
| File | Why Needed | Key Interfaces |
|
|
10
|
+
|------|-----------|----------------|
|
|
4
11
|
|
|
5
12
|
|
|
6
13
|
**Checkpoint update**: Run the update script to set step `context-snapshot` to `"completed"`:
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|-----------|-------|
|
|
8
8
|
| subagent_type | prizm-dev-team-dev |
|
|
9
9
|
| run_in_background | false |
|
|
10
|
+
| isolation | Leave empty — Dev works directly in the repo, no worktree |
|
|
10
11
|
|
|
11
12
|
**Agent spawn failure policy**:
|
|
12
13
|
- If spawning Dev fails with team/config/lock errors, retry at most once.
|
|
@@ -19,13 +19,10 @@ ls .prizmkit/specs/{{FEATURE_SLUG}}/ 2>/dev/null
|
|
|
19
19
|
Identify the top-level source directories from the results.
|
|
20
20
|
3. Scan the detected source directories for files related to this feature; read each one
|
|
21
21
|
4. Write `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md`:
|
|
22
|
-
- **Section 1 —
|
|
23
|
-
- **Section 2 —
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
- **Section 3 — Prizm Context**: full content of root.prizm and relevant L1/L2 docs
|
|
28
|
-
- **Section 4 — File Manifest**: For each file relevant to this feature, list: file path, why it's needed (modify/reference/test), key interface signatures (function names + params + return types). Do NOT include full file content — agents read files on-demand. Format:
|
|
22
|
+
- **Section 1 — Feature Brief**: feature description (copy from Task Contract above, 2-3 lines only)
|
|
23
|
+
- **Section 2 — Verification Gates**: copy the gate checklist from the Task Contract above
|
|
24
|
+
- **Section 3 — Key TRAPS & RULES**: extract TRAPS and RULES entries from loaded prizm-docs that apply to files in this feature's scope. Do NOT copy full L0/L1/L2 content — only the specific TRAPS/RULES lines relevant to the files listed in Section 4.
|
|
25
|
+
- **Section 4 — File Manifest**: For each file relevant to this feature, list path, why needed (modify/reference/test), key interface signatures. Do NOT include full file content. Format:
|
|
29
26
|
### Files to Modify
|
|
30
27
|
| File | Why Needed | Key Interfaces |
|
|
31
28
|
|------|-----------|----------------|
|
|
@@ -38,7 +35,6 @@ ls .prizmkit/specs/{{FEATURE_SLUG}}/ 2>/dev/null
|
|
|
38
35
|
|
|
39
36
|
### Known TRAPS (from .prizmkit/prizm-docs/)
|
|
40
37
|
- <trap entries extracted from L1/L2 docs>
|
|
41
|
-
- **Section 5 — Existing Tests**: full content of related test files as code blocks
|
|
42
38
|
5. Confirm: `ls .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md`
|
|
43
39
|
|
|
44
40
|
**After Step A**: Use context-snapshot.md Section 4 File Manifest to guide targeted file reads. Do NOT scan directories or read unrelated files.
|
|
@@ -531,6 +531,26 @@ function Test-PrizmStreamJsonSupport {
|
|
|
531
531
|
}
|
|
532
532
|
}
|
|
533
533
|
|
|
534
|
+
function Test-PrizmEffort {
|
|
535
|
+
param([string]$CliCommand)
|
|
536
|
+
if ([string]::IsNullOrEmpty($env:PRIZMKIT_EFFORT)) { return }
|
|
537
|
+
|
|
538
|
+
$sessionPlatform = Get-PrizmSessionPlatform $CliCommand
|
|
539
|
+
$valid = @{
|
|
540
|
+
claude = @('low', 'medium', 'high', 'xhigh', 'max')
|
|
541
|
+
codex = @('low', 'medium', 'high', 'xhigh')
|
|
542
|
+
codebuddy = @('low', 'medium', 'high', 'xhigh')
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
$allowed = $valid[$sessionPlatform]
|
|
546
|
+
if ($env:PRIZMKIT_EFFORT -notin $allowed) {
|
|
547
|
+
Write-Host "ERROR: PRIZMKIT_EFFORT='$env:PRIZMKIT_EFFORT' is not supported by the detected CLI ($sessionPlatform)." -ForegroundColor Red
|
|
548
|
+
Write-Host " Supported values for $($sessionPlatform): $($allowed -join ', ')" -ForegroundColor Red
|
|
549
|
+
Write-Host " Set `$env:PRIZMKIT_EFFORT to one of the above, or unset it to use the CLI default." -ForegroundColor Red
|
|
550
|
+
exit 1
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
534
554
|
function Start-PrizmProgressParser {
|
|
535
555
|
param(
|
|
536
556
|
[string[]]$PythonCommand,
|
|
@@ -656,6 +676,7 @@ function Invoke-PrizmAiSession {
|
|
|
656
676
|
$cliArgs += @('-p', '--dangerously-skip-permissions')
|
|
657
677
|
if ($env:VERBOSE -in @('1','true','yes','on') -or $useStreamJson) { $cliArgs += '--verbose' }
|
|
658
678
|
if ($useStreamJson) { $cliArgs += @('--output-format', 'stream-json') }
|
|
679
|
+
if ($env:PRIZMKIT_EFFORT) { $cliArgs += @('--effort', $env:PRIZMKIT_EFFORT) }
|
|
659
680
|
if ($Model) { $cliArgs += @('--model', $Model) }
|
|
660
681
|
} elseif ($sessionPlatform -eq 'codex') {
|
|
661
682
|
$cliArgs += @('--ask-for-approval', 'never', '--sandbox', 'danger-full-access')
|
|
@@ -666,11 +687,13 @@ function Invoke-PrizmAiSession {
|
|
|
666
687
|
$cliArgs += @('exec', '--cd', $ProjectRoot, '--skip-git-repo-check')
|
|
667
688
|
if ($useStreamJson) { $cliArgs += '--json' }
|
|
668
689
|
if ($Model) { $cliArgs += @('--model', $Model) }
|
|
690
|
+
if ($env:PRIZMKIT_EFFORT) { $cliArgs += @('--config', "model_reasoning_effort=$env:PRIZMKIT_EFFORT") }
|
|
669
691
|
$cliArgs += '-'
|
|
670
692
|
} else {
|
|
671
693
|
$cliArgs += @('--print', '-y')
|
|
672
694
|
if ($env:VERBOSE -in @('1','true','yes','on')) { $cliArgs += '--verbose' }
|
|
673
695
|
if ($useStreamJson) { $cliArgs += @('--output-format', 'stream-json') }
|
|
696
|
+
if ($env:PRIZMKIT_EFFORT) { $cliArgs += @('--effort', $env:PRIZMKIT_EFFORT) }
|
|
674
697
|
if ($Model) { $cliArgs += @('--model', $Model) }
|
|
675
698
|
}
|
|
676
699
|
$generatedArgs = (($cliArgs | ForEach-Object { ConvertTo-PrizmProcessArgument $_ }) -join ' ')
|