prizmkit 1.1.7 → 1.1.9

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 (132) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/adapters/codebuddy/skill-adapter.js +21 -7
  3. package/bundled/agents/prizm-dev-team-reviewer.md +53 -173
  4. package/bundled/dev-pipeline/.env.example +45 -0
  5. package/bundled/dev-pipeline/README.md +64 -64
  6. package/bundled/dev-pipeline/SCHEMA_ANALYSIS.md +535 -0
  7. package/bundled/dev-pipeline/assets/feature-list-example.json +0 -1
  8. package/bundled/dev-pipeline/launch-bugfix-daemon.sh +64 -18
  9. package/bundled/dev-pipeline/launch-feature-daemon.sh +15 -12
  10. package/bundled/dev-pipeline/launch-refactor-daemon.sh +64 -18
  11. package/bundled/dev-pipeline/lib/branch.sh +6 -1
  12. package/bundled/dev-pipeline/lib/common.sh +71 -0
  13. package/bundled/dev-pipeline/lib/heartbeat.sh +2 -2
  14. package/bundled/dev-pipeline/reset-bug.sh +10 -9
  15. package/bundled/dev-pipeline/reset-feature.sh +9 -8
  16. package/bundled/dev-pipeline/reset-refactor.sh +10 -9
  17. package/bundled/dev-pipeline/retry-bugfix.sh +67 -29
  18. package/bundled/dev-pipeline/retry-feature.sh +54 -18
  19. package/bundled/dev-pipeline/retry-refactor.sh +112 -29
  20. package/bundled/dev-pipeline/run-bugfix.sh +281 -59
  21. package/bundled/dev-pipeline/run-feature.sh +53 -18
  22. package/bundled/dev-pipeline/run-refactor.sh +392 -66
  23. package/bundled/dev-pipeline/scripts/check-session-status.py +24 -1
  24. package/bundled/dev-pipeline/scripts/cleanup-logs.py +2 -2
  25. package/bundled/dev-pipeline/scripts/detect-stuck.py +195 -85
  26. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +57 -33
  27. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +25 -9
  28. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +104 -17
  29. package/bundled/dev-pipeline/scripts/init-bugfix-pipeline.py +34 -9
  30. package/bundled/dev-pipeline/scripts/init-pipeline.py +10 -10
  31. package/bundled/dev-pipeline/scripts/init-refactor-pipeline.py +19 -8
  32. package/bundled/dev-pipeline/scripts/parse-stream-progress.py +1 -5
  33. package/bundled/dev-pipeline/scripts/patch-completion-notes.py +191 -0
  34. package/bundled/dev-pipeline/scripts/update-bug-status.py +167 -22
  35. package/bundled/dev-pipeline/scripts/update-feature-status.py +104 -62
  36. package/bundled/dev-pipeline/scripts/update-refactor-status.py +351 -21
  37. package/bundled/dev-pipeline/templates/agent-prompts/dev-fix.md +1 -1
  38. package/bundled/dev-pipeline/templates/agent-prompts/reviewer-review.md +7 -11
  39. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +41 -7
  40. package/bundled/dev-pipeline/templates/bootstrap-tier1.md +27 -3
  41. package/bundled/dev-pipeline/templates/bootstrap-tier2.md +43 -19
  42. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +54 -26
  43. package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +6 -15
  44. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +36 -25
  45. package/bundled/dev-pipeline/templates/feature-list-schema.json +109 -31
  46. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +270 -0
  47. package/bundled/dev-pipeline/templates/refactor-list-schema.json +11 -3
  48. package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +3 -1
  49. package/bundled/dev-pipeline/templates/sections/critical-paths-agent.md +1 -0
  50. package/bundled/dev-pipeline/templates/sections/feature-context.md +2 -0
  51. package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +29 -2
  52. package/bundled/dev-pipeline/templates/sections/phase-commit.md +22 -0
  53. package/bundled/dev-pipeline/templates/sections/phase-deploy-verification.md +2 -2
  54. package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +8 -6
  55. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +7 -5
  56. package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +3 -3
  57. package/bundled/skills/_metadata.json +5 -22
  58. package/bundled/skills/app-planner/SKILL.md +98 -72
  59. package/bundled/skills/app-planner/assets/app-design-guide.md +1 -1
  60. package/bundled/skills/app-planner/references/architecture-decisions.md +1 -1
  61. package/bundled/skills/app-planner/references/project-brief-guide.md +69 -66
  62. package/bundled/skills/bug-fix-workflow/SKILL.md +52 -9
  63. package/bundled/skills/bug-planner/SKILL.md +139 -197
  64. package/bundled/skills/bug-planner/assets/bug-confirmation-template.md +43 -0
  65. package/bundled/skills/bug-planner/references/critic-and-verification.md +44 -0
  66. package/bundled/skills/bug-planner/references/error-recovery.md +73 -0
  67. package/bundled/skills/bug-planner/references/input-formats.md +53 -0
  68. package/bundled/skills/bug-planner/references/schema-validation.md +25 -0
  69. package/bundled/skills/bug-planner/references/severity-rules.md +16 -0
  70. package/bundled/skills/bug-planner/scripts/validate-bug-list.py +4 -8
  71. package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +34 -39
  72. package/bundled/skills/feature-pipeline-launcher/SKILL.md +49 -36
  73. package/bundled/skills/feature-pipeline-launcher/scripts/preflight-check.py +3 -3
  74. package/bundled/skills/feature-planner/SKILL.md +53 -142
  75. package/bundled/skills/feature-planner/assets/evaluation-guide.md +1 -1
  76. package/bundled/skills/feature-planner/assets/planning-guide.md +21 -5
  77. package/bundled/skills/feature-planner/references/browser-interaction.md +2 -4
  78. package/bundled/skills/feature-planner/references/completeness-review.md +57 -0
  79. package/bundled/skills/feature-planner/references/error-recovery.md +16 -35
  80. package/bundled/skills/feature-planner/references/incremental-feature-planning.md +1 -1
  81. package/bundled/skills/feature-planner/references/new-project-planning.md +2 -2
  82. package/bundled/skills/feature-planner/scripts/validate-and-generate.py +19 -20
  83. package/bundled/skills/feature-workflow/SKILL.md +24 -25
  84. package/bundled/skills/prizm-kit/SKILL.md +39 -49
  85. package/bundled/skills/prizmkit-code-review/SKILL.md +51 -64
  86. package/bundled/skills/prizmkit-code-review/rules/dimensions.md +85 -0
  87. package/bundled/skills/prizmkit-code-review/rules/fix-strategy.md +11 -11
  88. package/bundled/skills/prizmkit-committer/SKILL.md +3 -31
  89. package/bundled/skills/prizmkit-deploy/SKILL.md +34 -31
  90. package/bundled/skills/prizmkit-deploy/assets/deploy-template.md +1 -1
  91. package/bundled/skills/prizmkit-implement/SKILL.md +35 -68
  92. package/bundled/skills/prizmkit-init/SKILL.md +112 -65
  93. package/bundled/skills/prizmkit-init/assets/project-brief-template.md +82 -0
  94. package/bundled/skills/prizmkit-plan/SKILL.md +120 -79
  95. package/bundled/skills/prizmkit-plan/assets/plan-template.md +28 -18
  96. package/bundled/skills/prizmkit-plan/assets/spec-template.md +28 -11
  97. package/bundled/skills/prizmkit-plan/references/clarify-guide.md +3 -3
  98. package/bundled/skills/prizmkit-plan/references/verification-checklist.md +60 -0
  99. package/bundled/skills/prizmkit-prizm-docs/SKILL.md +10 -81
  100. package/bundled/skills/prizmkit-prizm-docs/assets/{PRIZM-SPEC.md → prizm-docs-format.md} +41 -526
  101. package/bundled/skills/prizmkit-prizm-docs/references/op-init.md +46 -0
  102. package/bundled/skills/prizmkit-prizm-docs/references/op-rebuild.md +16 -0
  103. package/bundled/skills/prizmkit-prizm-docs/references/op-status.md +14 -0
  104. package/bundled/skills/prizmkit-prizm-docs/references/op-update.md +19 -0
  105. package/bundled/skills/prizmkit-prizm-docs/references/op-validate.md +17 -0
  106. package/bundled/skills/prizmkit-retrospective/SKILL.md +27 -65
  107. package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +3 -4
  108. package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +7 -25
  109. package/bundled/skills/recovery-workflow/SKILL.md +22 -22
  110. package/bundled/skills/recovery-workflow/evals/evals.json +5 -5
  111. package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +43 -10
  112. package/bundled/skills/refactor-pipeline-launcher/SKILL.md +48 -40
  113. package/bundled/skills/refactor-planner/SKILL.md +43 -61
  114. package/bundled/skills/refactor-planner/scripts/validate-and-generate-refactor.py +17 -17
  115. package/bundled/skills/refactor-workflow/SKILL.md +23 -24
  116. package/bundled/team/prizm-dev-team.json +1 -1
  117. package/bundled/{skills/prizm-kit/assets → templates}/project-memory-template.md +1 -1
  118. package/package.json +1 -1
  119. package/src/clean.js +3 -4
  120. package/src/gitignore-template.js +7 -9
  121. package/src/scaffold.js +14 -5
  122. package/bundled/dev-pipeline/templates/agent-prompts/reviewer-analyze.md +0 -5
  123. package/bundled/dev-pipeline/templates/sections/phase-analyze-agent.md +0 -19
  124. package/bundled/dev-pipeline/templates/sections/phase-analyze-full.md +0 -19
  125. package/bundled/skills/app-planner/references/project-conventions.md +0 -93
  126. package/bundled/skills/prizmkit-analyze/SKILL.md +0 -207
  127. package/bundled/skills/prizmkit-code-review/rules/dimensions-bugfix.md +0 -25
  128. package/bundled/skills/prizmkit-code-review/rules/dimensions-feature.md +0 -43
  129. package/bundled/skills/prizmkit-code-review/rules/dimensions-refactor.md +0 -25
  130. package/bundled/skills/prizmkit-implement/references/deploy-guide-protocol.md +0 -69
  131. package/bundled/skills/prizmkit-verify/SKILL.md +0 -281
  132. package/bundled/skills/prizmkit-verify/scripts/verify-light.py +0 -402
@@ -38,13 +38,13 @@ def parse_args():
38
38
  parser = argparse.ArgumentParser(
39
39
  description=(
40
40
  "Generate a session-specific bootstrap prompt from a template "
41
- "and feature-list.json."
41
+ "and .prizmkit/plans/feature-list.json."
42
42
  )
43
43
  )
44
44
  parser.add_argument(
45
45
  "--feature-list",
46
46
  required=True,
47
- help="Path to feature-list.json",
47
+ help="Path to .prizmkit/plans/feature-list.json",
48
48
  )
49
49
  parser.add_argument(
50
50
  "--feature-id",
@@ -274,7 +274,12 @@ def format_global_context(global_context, project_root=None):
274
274
 
275
275
 
276
276
  def get_completed_dependencies(features, feature):
277
- """Look up dependency features and list those with status=completed."""
277
+ """Look up dependency features and list those with status=completed.
278
+
279
+ When a completed dependency has completion_notes (written by the AI
280
+ session and propagated by the pipeline runner), include them as rich
281
+ context so the downstream session knows what was built.
282
+ """
278
283
  deps = feature.get("dependencies", [])
279
284
  if not deps:
280
285
  return "- (no dependencies)"
@@ -285,17 +290,26 @@ def get_completed_dependencies(features, feature):
285
290
  if isinstance(f, dict) and "id" in f:
286
291
  feature_map[f["id"]] = f
287
292
 
288
- completed = []
293
+ sections = []
289
294
  for dep_id in deps:
290
295
  dep = feature_map.get(dep_id)
291
296
  if dep and dep.get("status") == "completed":
292
- completed.append("- {} - {} (completed)".format(
297
+ header = "- **{}** {} (completed)".format(
293
298
  dep_id, dep.get("title", "Untitled")
294
- ))
299
+ )
300
+ notes = dep.get("completion_notes", [])
301
+ if notes and isinstance(notes, list):
302
+ note_lines = "\n".join(
303
+ " - {}".format(n) for n in notes
304
+ if isinstance(n, str) and n.strip()
305
+ )
306
+ if note_lines:
307
+ header += "\n" + note_lines
308
+ sections.append(header)
295
309
 
296
- if not completed:
310
+ if not sections:
297
311
  return "- (no completed dependencies yet)"
298
- return "\n".join(completed)
312
+ return "\n".join(sections)
299
313
 
300
314
 
301
315
  def get_prev_session_status(state_dir, feature_id):
@@ -348,7 +362,7 @@ def get_prev_session_status(state_dir, feature_id):
348
362
 
349
363
 
350
364
  def _read_project_brief(project_root):
351
- """Read project-brief.md from project root if it exists.
365
+ """Read project-brief.md from new or old location with fallback.
352
366
 
353
367
  Returns the file content as a string, or a fallback message if absent.
354
368
  This brief is generated by app-planner during interactive planning and
@@ -356,13 +370,25 @@ def _read_project_brief(project_root):
356
370
  marked [ ] for pending or [x] for completed. Feature sessions should mark
357
371
  items [x] and append key file paths when implementing relevant ideas.
358
372
  """
359
- brief_path = os.path.join(project_root, "project-brief.md")
360
- if os.path.isfile(brief_path):
361
- try:
362
- with open(brief_path, "r", encoding="utf-8") as f:
363
- return f.read().strip()
364
- except IOError:
365
- return "(project-brief.md exists but could not be read)"
373
+ # Check both new and old paths for backward compatibility
374
+ new_path = os.path.join(project_root, ".prizmkit", "plans", "project-brief.md")
375
+ old_path = os.path.join(project_root, "project-brief.md")
376
+
377
+ for brief_path in [new_path, old_path]:
378
+ if os.path.isfile(brief_path):
379
+ try:
380
+ with open(brief_path, "r", encoding="utf-8") as f:
381
+ content = f.read().strip()
382
+ if brief_path == old_path:
383
+ # Warn user about old path
384
+ import sys
385
+ print("⚠️ Migration notice: project-brief.md found in root. "
386
+ "Please move to .prizmkit/plans/project-brief.md",
387
+ file=sys.stderr)
388
+ return content
389
+ except IOError:
390
+ return "(project-brief.md exists but could not be read)"
391
+
366
392
  return "(No project brief available)"
367
393
 
368
394
 
@@ -549,7 +575,6 @@ SECTION_TO_SKILL = {
549
575
  ".prizmkit/specs/{slug}/plan.md"]),
550
576
  "phase-plan": ("prizmkit-plan", "Plan & Tasks",
551
577
  [".prizmkit/specs/{slug}/plan.md"]),
552
- "phase-analyze": ("prizmkit-analyze", "Analyze", []),
553
578
  "phase-critic-plan": ("critic-plan-review", "Critic: Plan Review", []),
554
579
  "phase-implement": ("prizmkit-implement", "Implement + Test", []),
555
580
  "phase-critic-code": ("critic-code-review", "Critic: Code Review", []),
@@ -945,16 +970,6 @@ def assemble_sections(pipeline_mode, sections_dir, init_done, is_resume,
945
970
  load_section(sections_dir,
946
971
  "phase-plan-agent.md")))
947
972
 
948
- # --- Analyze (only for agent tiers) ---
949
- if pipeline_mode == "standard":
950
- sections.append(("phase-analyze",
951
- load_section(sections_dir,
952
- "phase-analyze-agent.md")))
953
- elif pipeline_mode == "full":
954
- sections.append(("phase-analyze",
955
- load_section(sections_dir,
956
- "phase-analyze-full.md")))
957
-
958
973
  # --- Critic: Plan Challenge (only if critic enabled) ---
959
974
  if critic_enabled:
960
975
  if pipeline_mode == "full":
@@ -980,12 +995,23 @@ def assemble_sections(pipeline_mode, sections_dir, init_done, is_resume,
980
995
  load_section(sections_dir,
981
996
  "phase-implement-agent.md")))
982
997
 
998
+ # --- Test Failure Recovery Protocol (all tiers) ---
999
+ sections.append(("test-failure-recovery",
1000
+ load_section(sections_dir, "test-failure-recovery.md")))
1001
+
983
1002
  # --- Critic: Code Challenge (only if critic enabled, agent tiers) ---
984
1003
  if critic_enabled and pipeline_mode in ("standard", "full"):
985
1004
  sections.append(("phase-critic-code",
986
1005
  load_section(sections_dir,
987
1006
  "phase-critic-code.md")))
988
1007
 
1008
+ # --- AC Verification Checklist (all tiers) ---
1009
+ ac_checklist_path = os.path.join(sections_dir, "ac-verification-checklist.md")
1010
+ if os.path.isfile(ac_checklist_path):
1011
+ sections.append(("ac-verification-checklist",
1012
+ load_section(sections_dir,
1013
+ "ac-verification-checklist.md")))
1014
+
989
1015
  # --- Review (only for agent tiers) ---
990
1016
  if pipeline_mode == "full":
991
1017
  sections.append(("phase-review",
@@ -1179,12 +1205,12 @@ def build_replacements(args, feature, features, global_context, script_dir):
1179
1205
  validator_scripts_dir = os.path.join(project_root, "dev-pipeline", "scripts")
1180
1206
  init_script_path = os.path.join(validator_scripts_dir, "init-dev-team.py")
1181
1207
 
1182
- # Session status path (relative to dev-pipeline/)
1208
+ # Session status path (relative to project root)
1183
1209
  session_status_path = os.path.join(
1184
- "dev-pipeline", "state", "features", args.feature_id,
1210
+ ".prizmkit", "state", "features", args.feature_id,
1185
1211
  "sessions", args.session_id, "session-status.json",
1186
1212
  )
1187
- # Make it relative from project root
1213
+ # Make it absolute from project root
1188
1214
  session_status_abs = os.path.join(project_root, session_status_path)
1189
1215
 
1190
1216
  prev_status = get_prev_session_status(args.state_dir, args.feature_id)
@@ -1221,7 +1247,7 @@ def build_replacements(args, feature, features, global_context, script_dir):
1221
1247
  critic_enabled = bool(feature.get("critic", False))
1222
1248
 
1223
1249
  # Determine critic count (from feature field, default 1)
1224
- # Multi-critic voting (3) must be explicitly set by the user in feature-list.json
1250
+ # Multi-critic voting (3) must be explicitly set by the user in .prizmkit/plans/feature-list.json
1225
1251
  critic_count = feature.get("critic_count", 1)
1226
1252
 
1227
1253
  # Guard: if critic enabled but agent file missing, force disable and warn
@@ -1309,7 +1335,6 @@ def build_replacements(args, feature, features, global_context, script_dir):
1309
1335
  "{{DEV_SUBAGENT_PATH}}": dev_subagent,
1310
1336
  "{{REVIEWER_SUBAGENT_PATH}}": reviewer_subagent,
1311
1337
  "{{CRITIC_SUBAGENT_PATH}}": critic_subagent,
1312
- "{{VALIDATOR_SCRIPTS_DIR}}": validator_scripts_dir,
1313
1338
  "{{INIT_SCRIPT_PATH}}": init_script_path,
1314
1339
  "{{SESSION_STATUS_PATH}}": session_status_abs,
1315
1340
  "{{PROJECT_ROOT}}": project_root,
@@ -1324,7 +1349,6 @@ def build_replacements(args, feature, features, global_context, script_dir):
1324
1349
  "{{INIT_DONE}}": "true" if init_done else "false",
1325
1350
  "{{HAS_SPEC}}": "true" if artifacts["has_spec"] else "false",
1326
1351
  "{{HAS_PLAN}}": "true" if artifacts["has_plan"] else "false",
1327
- "{{ARTIFACTS_COMPLETE}}": "true" if artifacts["all_complete"] else "false",
1328
1352
  "{{BROWSER_URL}}": browser_url,
1329
1353
  "{{BROWSER_SETUP_COMMAND}}": browser_setup_command,
1330
1354
  "{{BROWSER_VERIFY_STEPS}}": browser_verify_steps,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
- """Generate a session-specific bug fix bootstrap prompt from template and bug-fix-list.json.
2
+ """Generate a session-specific bug fix bootstrap prompt from template and .prizmkit/plans/bug-fix-list.json.
3
3
 
4
- Reads the bugfix-bootstrap-prompt.md template and a bug-fix-list.json, resolves all
4
+ Reads the bugfix-bootstrap-prompt.md template and a .prizmkit/plans/bug-fix-list.json, resolves all
5
5
  {{PLACEHOLDER}} variables, handles conditional blocks, and writes the rendered
6
6
  prompt to the specified output path.
7
7
 
@@ -31,18 +31,20 @@ def parse_args():
31
31
  parser = argparse.ArgumentParser(
32
32
  description=(
33
33
  "Generate a session-specific bug fix bootstrap prompt from a template "
34
- "and bug-fix-list.json."
34
+ "and .prizmkit/plans/bug-fix-list.json."
35
35
  )
36
36
  )
37
- parser.add_argument("--bug-list", required=True, help="Path to bug-fix-list.json")
37
+ parser.add_argument("--bug-list", required=True, help="Path to .prizmkit/plans/bug-fix-list.json")
38
38
  parser.add_argument("--bug-id", required=True, help="Bug ID to generate prompt for (e.g. B-001)")
39
39
  parser.add_argument("--session-id", required=True, help="Session ID for this pipeline session")
40
40
  parser.add_argument("--run-id", required=True, help="Pipeline run ID")
41
41
  parser.add_argument("--retry-count", required=True, help="Current retry count")
42
42
  parser.add_argument("--resume-phase", required=True, help='Phase to resume from, or "null" for fresh start')
43
- parser.add_argument("--state-dir", default=None, help="State directory path for reading previous session info")
43
+ parser.add_argument("--state-dir", default=None, help="State directory (default: .prizmkit/state/bugfix)")
44
44
  parser.add_argument("--output", required=True, help="Output path for the rendered prompt")
45
45
  parser.add_argument("--template", default=None, help="Custom template path. Defaults to {script_dir}/../templates/bugfix-bootstrap-prompt.md")
46
+ parser.add_argument("--mode", default=None, help="Pipeline execution mode override: lite, standard, full")
47
+ parser.add_argument("--critic", default=None, help="Enable critic agent: true/false")
46
48
  return parser.parse_args()
47
49
 
48
50
 
@@ -217,7 +219,7 @@ def build_replacements(args, bug, global_context, script_dir):
217
219
 
218
220
  # Session status path
219
221
  session_status_path = os.path.join(
220
- project_root, "dev-pipeline", "bugfix-state", "bugs", args.bug_id,
222
+ project_root, ".prizmkit", "state", "bugfix", "bugs", args.bug_id,
221
223
  "sessions", args.session_id, "session-status.json"
222
224
  )
223
225
 
@@ -254,7 +256,6 @@ def build_replacements(args, bug, global_context, script_dir):
254
256
  "{{ACCEPTANCE_CRITERIA}}": format_acceptance_criteria(
255
257
  bug.get("acceptance_criteria", [])
256
258
  ),
257
- "{{AFFECTED_FEATURE}}": bug.get("affected_feature", "N/A"),
258
259
  "{{ENVIRONMENT}}": format_environment(bug.get("environment")),
259
260
  "{{GLOBAL_CONTEXT}}": format_global_context(global_context, project_root),
260
261
  "{{TEAM_CONFIG_PATH}}": team_config_path,
@@ -263,7 +264,7 @@ def build_replacements(args, bug, global_context, script_dir):
263
264
  "{{SESSION_STATUS_PATH}}": session_status_path,
264
265
  "{{PROJECT_ROOT}}": project_root,
265
266
  "{{FIX_SCOPE}}": fix_scope,
266
- "{{TIMESTAMP}}": "", # 占位符,agent 自行填写时间戳
267
+ "{{TIMESTAMP}}": "", # Placeholder, agent fills in the timestamp
267
268
  }
268
269
 
269
270
  return replacements
@@ -280,7 +281,7 @@ def process_conditional_blocks(content, bug):
280
281
  content = content.replace("{{END_IF_VERIFICATION_MANUAL_OR_HYBRID}}\n", "")
281
282
  content = content.replace("{{END_IF_VERIFICATION_MANUAL_OR_HYBRID}}", "")
282
283
  else:
283
- # 删除整个条件块
284
+ # Remove the entire conditional block
284
285
  content = re.sub(
285
286
  r"\{\{IF_VERIFICATION_MANUAL_OR_HYBRID\}\}.*?\{\{END_IF_VERIFICATION_MANUAL_OR_HYBRID\}\}\n?",
286
287
  "", content, flags=re.DOTALL,
@@ -497,11 +498,26 @@ def main():
497
498
  json.dump(checkpoint, f, indent=2, ensure_ascii=False)
498
499
  LOGGER.info("Wrote bugfix checkpoint to %s", checkpoint_path)
499
500
 
501
+ # Resolve critic and mode
502
+ bug_critic = bug.get("critic", False)
503
+ if args.critic is not None:
504
+ critic_enabled = str(args.critic).lower() == "true"
505
+ else:
506
+ critic_enabled = bool(bug_critic)
507
+
508
+ pipeline_mode = args.mode or "standard"
509
+ agent_count = 5 if critic_enabled else 3
510
+
500
511
  # Success
512
+ bug_model = bug.get("model", "")
501
513
  output = {
502
514
  "success": True,
503
515
  "output_path": os.path.abspath(args.output),
504
516
  "checkpoint_path": checkpoint_path,
517
+ "model": bug_model,
518
+ "pipeline_mode": pipeline_mode,
519
+ "agent_count": agent_count,
520
+ "critic_enabled": critic_enabled,
505
521
  }
506
522
  print(json.dumps(output, indent=2, ensure_ascii=False))
507
523
  sys.exit(0)
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
- """Generate a session-specific refactor bootstrap prompt from template and refactor-list.json.
2
+ """Generate a session-specific refactor bootstrap prompt from template and .prizmkit/plans/refactor-list.json.
3
3
 
4
- Reads the refactor-bootstrap-prompt.md template and a refactor-list.json, resolves all
4
+ Reads the refactor-bootstrap-prompt.md template and a .prizmkit/plans/refactor-list.json, resolves all
5
5
  {{PLACEHOLDER}} variables, handles conditional blocks, and writes the rendered
6
6
  prompt to the specified output path.
7
7
 
@@ -16,6 +16,7 @@ Usage:
16
16
  import argparse
17
17
  import json
18
18
  import os
19
+ import re
19
20
  import sys
20
21
 
21
22
  from utils import enrich_global_context, load_json_file, setup_logging
@@ -30,18 +31,20 @@ def parse_args():
30
31
  parser = argparse.ArgumentParser(
31
32
  description=(
32
33
  "Generate a session-specific refactor bootstrap prompt from a template "
33
- "and refactor-list.json."
34
+ "and .prizmkit/plans/refactor-list.json."
34
35
  )
35
36
  )
36
- parser.add_argument("--refactor-list", required=True, help="Path to refactor-list.json")
37
+ parser.add_argument("--refactor-list", required=True, help="Path to .prizmkit/plans/refactor-list.json")
37
38
  parser.add_argument("--refactor-id", required=True, help="Refactor ID to generate prompt for (e.g. R-001)")
38
39
  parser.add_argument("--session-id", required=True, help="Session ID for this pipeline session")
39
40
  parser.add_argument("--run-id", required=True, help="Pipeline run ID")
40
41
  parser.add_argument("--retry-count", required=True, help="Current retry count")
41
42
  parser.add_argument("--resume-phase", required=True, help='Phase to resume from, or "null" for fresh start')
42
- parser.add_argument("--state-dir", default=None, help="State directory path for reading previous session info")
43
+ parser.add_argument("--state-dir", default=None, help="State directory (default: .prizmkit/state/refactor)")
43
44
  parser.add_argument("--output", required=True, help="Output path for the rendered prompt")
44
45
  parser.add_argument("--template", default=None, help="Custom template path. Defaults to {script_dir}/../templates/refactor-bootstrap-prompt.md")
46
+ parser.add_argument("--mode", default=None, help="Pipeline execution mode override: lite, standard, full")
47
+ parser.add_argument("--critic", default=None, help="Enable critic agent: true/false")
45
48
  return parser.parse_args()
46
49
 
47
50
 
@@ -161,15 +164,42 @@ def format_behavior_preservation(bp):
161
164
  return "\n".join(lines)
162
165
 
163
166
 
164
- def format_dependencies(dependencies):
165
- """Format dependencies list as a markdown bullet list."""
167
+ def format_dependencies(dependencies, refactors=None):
168
+ """Format dependencies list as a markdown bullet list with completion context.
169
+
170
+ When refactors list is provided, look up completed dependencies and include
171
+ their completion_notes for rich context propagation.
172
+ """
166
173
  if not dependencies or not isinstance(dependencies, list):
167
174
  return "- (none)"
168
175
  if len(dependencies) == 0:
169
176
  return "- (none)"
177
+
178
+ # Build lookup map if refactors list is provided
179
+ refactor_map = {}
180
+ if refactors:
181
+ for r in refactors:
182
+ if isinstance(r, dict) and "id" in r:
183
+ refactor_map[r["id"]] = r
184
+
170
185
  lines = []
171
186
  for dep in dependencies:
172
- lines.append("- `{}`".format(dep))
187
+ dep_info = refactor_map.get(dep)
188
+ if dep_info and dep_info.get("status") == "completed":
189
+ header = "- **{}** — {} (completed)".format(
190
+ dep, dep_info.get("title", "Untitled")
191
+ )
192
+ notes = dep_info.get("completion_notes", [])
193
+ if notes and isinstance(notes, list):
194
+ note_lines = [
195
+ " - {}".format(n) for n in notes
196
+ if isinstance(n, str) and n.strip()
197
+ ]
198
+ if note_lines:
199
+ header += "\n" + "\n".join(note_lines)
200
+ lines.append(header)
201
+ else:
202
+ lines.append("- `{}`".format(dep))
173
203
  return "\n".join(lines)
174
204
 
175
205
 
@@ -219,7 +249,7 @@ def resolve_project_root(script_dir):
219
249
  return os.path.abspath(project_root)
220
250
 
221
251
 
222
- def build_replacements(args, refactor, global_context, script_dir):
252
+ def build_replacements(args, refactor, refactors, global_context, script_dir):
223
253
  """Build the full dict of placeholder -> replacement value."""
224
254
  project_root = resolve_project_root(script_dir)
225
255
 
@@ -247,7 +277,7 @@ def build_replacements(args, refactor, global_context, script_dir):
247
277
 
248
278
  # Session status path
249
279
  session_status_path = os.path.join(
250
- project_root, "dev-pipeline", "refactor-state", "refactors", args.refactor_id,
280
+ project_root, ".prizmkit", "state", "refactor", "refactors", args.refactor_id,
251
281
  "sessions", args.session_id, "session-status.json"
252
282
  )
253
283
 
@@ -260,7 +290,11 @@ def build_replacements(args, refactor, global_context, script_dir):
260
290
  bp = refactor.get("behavior_preservation", {})
261
291
  behavior_strategy = bp.get("strategy", "test-gate") if isinstance(bp, dict) else "test-gate"
262
292
  existing_tests = bp.get("existing_tests", []) if isinstance(bp, dict) else []
293
+ if not isinstance(existing_tests, list):
294
+ existing_tests = []
263
295
  new_tests_needed = bp.get("new_tests_needed", []) if isinstance(bp, dict) else []
296
+ if not isinstance(new_tests_needed, list):
297
+ new_tests_needed = []
264
298
 
265
299
  # Format existing tests
266
300
  if existing_tests:
@@ -296,7 +330,7 @@ def build_replacements(args, refactor, global_context, script_dir):
296
330
  refactor.get("acceptance_criteria", [])
297
331
  ),
298
332
  "{{DEPENDENCIES}}": format_dependencies(
299
- refactor.get("dependencies", [])
333
+ refactor.get("dependencies", []), refactors
300
334
  ),
301
335
  "{{GLOBAL_CONTEXT}}": format_global_context(global_context, project_root),
302
336
  "{{TEAM_CONFIG_PATH}}": team_config_path,
@@ -304,17 +338,55 @@ def build_replacements(args, refactor, global_context, script_dir):
304
338
  "{{REVIEWER_SUBAGENT_PATH}}": reviewer_subagent,
305
339
  "{{SESSION_STATUS_PATH}}": session_status_path,
306
340
  "{{PROJECT_ROOT}}": project_root,
341
+ "{{CHECKPOINT_PATH}}": os.path.join(
342
+ ".prizmkit", "refactor", args.refactor_id, "workflow-checkpoint.json",
343
+ ),
307
344
  "{{TIMESTAMP}}": "", # Placeholder — agent fills in timestamp
308
345
  }
309
346
 
310
347
  return replacements
311
348
 
312
349
 
313
- def render_template(template_content, replacements):
314
- """Render the template by replacing all {{PLACEHOLDER}} variables."""
315
- content = template_content
350
+ def process_conditional_blocks(content, resume_phase):
351
+ """Handle conditional blocks based on resume_phase.
352
+
353
+ - {{IF_RESUME}}...{{END_IF_RESUME}} — include only when resuming (resume_phase != "null")
354
+ - {{IF_FRESH_START}}...{{END_IF_FRESH_START}} — include only on fresh start (resume_phase == "null")
355
+ """
356
+ is_resume = resume_phase != "null"
357
+
358
+ if is_resume:
359
+ # Keep IF_RESUME content, strip markers
360
+ content = content.replace("{{IF_RESUME}}\n", "")
361
+ content = content.replace("{{IF_RESUME}}", "")
362
+ content = content.replace("{{END_IF_RESUME}}\n", "")
363
+ content = content.replace("{{END_IF_RESUME}}", "")
364
+ # Remove IF_FRESH_START blocks entirely
365
+ content = re.sub(
366
+ r"\{\{IF_FRESH_START\}\}.*?\{\{END_IF_FRESH_START\}\}\n?",
367
+ "", content, flags=re.DOTALL,
368
+ )
369
+ else:
370
+ # Keep IF_FRESH_START content, strip markers
371
+ content = content.replace("{{IF_FRESH_START}}\n", "")
372
+ content = content.replace("{{IF_FRESH_START}}", "")
373
+ content = content.replace("{{END_IF_FRESH_START}}\n", "")
374
+ content = content.replace("{{END_IF_FRESH_START}}", "")
375
+ # Remove IF_RESUME blocks entirely
376
+ content = re.sub(
377
+ r"\{\{IF_RESUME\}\}.*?\{\{END_IF_RESUME\}\}\n?",
378
+ "", content, flags=re.DOTALL,
379
+ )
380
+
381
+ return content
382
+
316
383
 
317
- # Replace all {{PLACEHOLDER}} variables
384
+ def render_template(template_content, replacements, resume_phase):
385
+ """Render the template by processing conditionals and replacing placeholders."""
386
+ # Step 1: Process conditional blocks
387
+ content = process_conditional_blocks(template_content, resume_phase)
388
+
389
+ # Step 2: Replace all {{PLACEHOLDER}} variables
318
390
  for placeholder, value in replacements.items():
319
391
  content = content.replace(placeholder, value)
320
392
 
@@ -388,20 +460,35 @@ def main():
388
460
  global_context = {}
389
461
 
390
462
  # Build replacements
391
- replacements = build_replacements(args, refactor, global_context, script_dir)
463
+ replacements = build_replacements(args, refactor, refactors, global_context, script_dir)
392
464
 
393
465
  # Render the template
394
- rendered = render_template(template_content, replacements)
466
+ rendered = render_template(template_content, replacements, args.resume_phase)
395
467
 
396
468
  # Write the output
397
469
  err = write_output(args.output, rendered)
398
470
  if err:
399
471
  emit_failure(err)
400
472
 
473
+ # Resolve critic and mode
474
+ refactor_critic = refactor.get("critic", False)
475
+ if args.critic is not None:
476
+ critic_enabled = str(args.critic).lower() == "true"
477
+ else:
478
+ critic_enabled = bool(refactor_critic)
479
+
480
+ pipeline_mode = args.mode or "standard"
481
+ agent_count = 5 if critic_enabled else 3
482
+
401
483
  # Success
484
+ refactor_model = refactor.get("model", "")
402
485
  output = {
403
486
  "success": True,
404
487
  "output_path": os.path.abspath(args.output),
488
+ "model": refactor_model,
489
+ "pipeline_mode": pipeline_mode,
490
+ "agent_count": agent_count,
491
+ "critic_enabled": critic_enabled,
405
492
  }
406
493
  print(json.dumps(output, indent=2, ensure_ascii=False))
407
494
  sys.exit(0)
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Initialize the bug-fix pipeline state directory from a bug-fix-list.json file.
2
+ """Initialize the bug-fix pipeline state directory from a .prizmkit/plans/bug-fix-list.json file.
3
3
 
4
4
  Validates the bug fix list schema, sorts by priority/severity, and creates
5
5
  the state directory structure with pipeline and per-bug status files.
@@ -33,24 +33,28 @@ REQUIRED_BUG_FIELDS = [
33
33
  VALID_SEVERITIES = ["critical", "high", "medium", "low"]
34
34
  VALID_VERIFICATION_TYPES = ["automated", "manual", "hybrid"]
35
35
  VALID_STATUSES = [
36
- "pending", "triaging", "reproducing", "fixing",
37
- "verifying", "completed", "failed", "needs_info", "skipped",
36
+ "pending", "in_progress", "completed", "failed",
37
+ "skipped", "needs_info",
38
+ ]
39
+ TERMINAL_STATUSES = {"completed", "failed", "skipped", "needs_info"}
40
+ VALID_ERROR_SOURCE_TYPES = [
41
+ "stack_trace", "user_report", "failed_test", "log_pattern", "monitoring_alert",
38
42
  ]
39
43
 
40
44
 
41
45
  def parse_args():
42
46
  parser = argparse.ArgumentParser(
43
- description="Initialize bug-fix pipeline state from a bug-fix-list.json file."
47
+ description="Initialize bug-fix pipeline state from a .prizmkit/plans/bug-fix-list.json file."
44
48
  )
45
49
  parser.add_argument(
46
50
  "--bug-list",
47
51
  required=True,
48
- help="Path to the bug-fix-list.json file",
52
+ help="Path to the .prizmkit/plans/bug-fix-list.json file",
49
53
  )
50
54
  parser.add_argument(
51
55
  "--state-dir",
52
56
  required=True,
53
- help="Path to the state directory to create/initialize",
57
+ help="Path to the state directory (default: .prizmkit/state/bugfix)",
54
58
  )
55
59
  return parser.parse_args()
56
60
 
@@ -173,6 +177,17 @@ def validate_bugs(bugs):
173
177
  bid or "index {}".format(i)
174
178
  )
175
179
  )
180
+ else:
181
+ es_type = error_source["type"]
182
+ if es_type not in VALID_ERROR_SOURCE_TYPES:
183
+ # Warn but don't error — the pipeline can still attempt the fix
184
+ print(
185
+ "WARNING: Bug '{}' error_source.type '{}' is not one of {} "
186
+ "— pipeline will still attempt to process this bug".format(
187
+ bid or "index {}".format(i), es_type, VALID_ERROR_SOURCE_TYPES
188
+ ),
189
+ file=sys.stderr,
190
+ )
176
191
 
177
192
  # Validate acceptance_criteria is a list
178
193
  ac = bug.get("acceptance_criteria")
@@ -195,12 +210,18 @@ def create_state_directory(state_dir, bug_list_path, bugs):
195
210
  bugs_dir = os.path.join(abs_state_dir, "bugs")
196
211
 
197
212
  now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
198
- run_id = "bugfix-run-" + datetime.now(timezone.utc).strftime("%Y%m%d%H%M")
213
+ run_id = "bugfix-run-" + datetime.now(timezone.utc).strftime("%Y%m%d%H%M%S")
199
214
 
200
215
  # Create top-level state directory
201
216
  os.makedirs(abs_state_dir, exist_ok=True)
202
217
  os.makedirs(bugs_dir, exist_ok=True)
203
218
 
219
+ # Count bugs already in terminal status at init time
220
+ completed_count = sum(
221
+ 1 for b in bugs
222
+ if isinstance(b, dict) and b.get("status") in TERMINAL_STATUSES
223
+ )
224
+
204
225
  # Write pipeline.json
205
226
  pipeline_state = {
206
227
  "run_id": run_id,
@@ -209,7 +230,7 @@ def create_state_directory(state_dir, bug_list_path, bugs):
209
230
  "bug_list_path": rel_bug_list_path,
210
231
  "created_at": now,
211
232
  "total_bugs": len(bugs),
212
- "completed_bugs": 0,
233
+ "completed_bugs": completed_count,
213
234
  }
214
235
  pipeline_path = os.path.join(abs_state_dir, "pipeline.json")
215
236
  with open(pipeline_path, "w", encoding="utf-8") as f:
@@ -228,9 +249,13 @@ def create_state_directory(state_dir, bug_list_path, bugs):
228
249
  sessions_dir = os.path.join(bug_dir, "sessions")
229
250
  os.makedirs(sessions_dir, exist_ok=True)
230
251
 
252
+ # Respect existing terminal status from bug-fix-list.json
253
+ bl_status = bug.get("status", "pending")
254
+ init_status = bl_status if bl_status in TERMINAL_STATUSES else "pending"
255
+
231
256
  bug_status = {
232
257
  "bug_id": bid,
233
- "status": "pending",
258
+ "status": init_status,
234
259
  "retry_count": 0,
235
260
  "max_retries": 3,
236
261
  "sessions": [],