prizmkit 1.1.57 → 1.1.60

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 (188) hide show
  1. package/bin/create-prizmkit.js +8 -6
  2. package/bundled/VERSION.json +3 -3
  3. package/bundled/adapters/codex/agent-adapter.js +38 -0
  4. package/bundled/adapters/codex/paths.js +27 -0
  5. package/bundled/adapters/codex/rules-adapter.js +30 -0
  6. package/bundled/adapters/codex/settings-adapter.js +27 -0
  7. package/bundled/adapters/codex/skill-adapter.js +65 -0
  8. package/bundled/adapters/codex/team-adapter.js +37 -0
  9. package/bundled/dev-pipeline/.env.example +2 -1
  10. package/bundled/dev-pipeline/README.md +10 -7
  11. package/bundled/dev-pipeline/lib/common.sh +278 -37
  12. package/bundled/dev-pipeline/run-bugfix.sh +10 -61
  13. package/bundled/dev-pipeline/run-feature.sh +10 -78
  14. package/bundled/dev-pipeline/run-recovery.sh +10 -46
  15. package/bundled/dev-pipeline/run-refactor.sh +10 -61
  16. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +17 -7
  17. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +9 -3
  18. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +9 -3
  19. package/bundled/dev-pipeline/scripts/utils.py +6 -4
  20. package/bundled/dev-pipeline-windows/.env.example +28 -0
  21. package/bundled/dev-pipeline-windows/README.md +30 -0
  22. package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +525 -0
  23. package/bundled/dev-pipeline-windows/assets/feature-list-example.json +146 -0
  24. package/bundled/dev-pipeline-windows/assets/prizm-dev-team-integration.md +138 -0
  25. package/bundled/dev-pipeline-windows/launch-bugfix-daemon.ps1 +9 -0
  26. package/bundled/dev-pipeline-windows/launch-feature-daemon.ps1 +9 -0
  27. package/bundled/dev-pipeline-windows/launch-refactor-daemon.ps1 +9 -0
  28. package/bundled/dev-pipeline-windows/lib/common.ps1 +432 -0
  29. package/bundled/dev-pipeline-windows/lib/daemon.ps1 +140 -0
  30. package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +446 -0
  31. package/bundled/dev-pipeline-windows/lib/reset.ps1 +87 -0
  32. package/bundled/dev-pipeline-windows/reset-bug.ps1 +9 -0
  33. package/bundled/dev-pipeline-windows/reset-feature.ps1 +9 -0
  34. package/bundled/dev-pipeline-windows/reset-refactor.ps1 +9 -0
  35. package/bundled/dev-pipeline-windows/run-bugfix.ps1 +9 -0
  36. package/bundled/dev-pipeline-windows/run-feature.ps1 +9 -0
  37. package/bundled/dev-pipeline-windows/run-recovery.ps1 +76 -0
  38. package/bundled/dev-pipeline-windows/run-refactor.ps1 +9 -0
  39. package/bundled/dev-pipeline-windows/scripts/check-session-status.py +228 -0
  40. package/bundled/dev-pipeline-windows/scripts/cleanup-logs.py +192 -0
  41. package/bundled/dev-pipeline-windows/scripts/detect-stuck.py +530 -0
  42. package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +1737 -0
  43. package/bundled/dev-pipeline-windows/scripts/generate-bugfix-prompt.py +685 -0
  44. package/bundled/dev-pipeline-windows/scripts/generate-recovery-prompt.py +805 -0
  45. package/bundled/dev-pipeline-windows/scripts/generate-refactor-prompt.py +763 -0
  46. package/bundled/dev-pipeline-windows/scripts/init-bugfix-pipeline.py +316 -0
  47. package/bundled/dev-pipeline-windows/scripts/init-dev-team.py +134 -0
  48. package/bundled/dev-pipeline-windows/scripts/init-pipeline.py +380 -0
  49. package/bundled/dev-pipeline-windows/scripts/init-refactor-pipeline.py +399 -0
  50. package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +388 -0
  51. package/bundled/dev-pipeline-windows/scripts/patch-completion-notes.py +191 -0
  52. package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +864 -0
  53. package/bundled/dev-pipeline-windows/scripts/update-checkpoint.py +173 -0
  54. package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +1501 -0
  55. package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +1073 -0
  56. package/bundled/dev-pipeline-windows/scripts/utils.py +542 -0
  57. package/bundled/dev-pipeline-windows/templates/agent-prompts/critic-plan-challenge.md +7 -0
  58. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-fix.md +7 -0
  59. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +30 -0
  60. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-resume.md +5 -0
  61. package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +7 -0
  62. package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +46 -0
  63. package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +43 -0
  64. package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +43 -0
  65. package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +43 -0
  66. package/bundled/dev-pipeline-windows/templates/bug-fix-list-schema.json +263 -0
  67. package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +320 -0
  68. package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +237 -0
  69. package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +331 -0
  70. package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +270 -0
  71. package/bundled/dev-pipeline-windows/templates/sections/ac-verification-checklist.md +13 -0
  72. package/bundled/dev-pipeline-windows/templates/sections/checkpoint-system.md +91 -0
  73. package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +33 -0
  74. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-agent.md +10 -0
  75. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-full.md +12 -0
  76. package/bundled/dev-pipeline-windows/templates/sections/critical-paths-lite.md +7 -0
  77. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-agent.md +8 -0
  78. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-full.md +9 -0
  79. package/bundled/dev-pipeline-windows/templates/sections/directory-convention-lite.md +6 -0
  80. package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +21 -0
  81. package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +31 -0
  82. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +72 -0
  83. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +63 -0
  84. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +62 -0
  85. package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +71 -0
  86. package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +64 -0
  87. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +23 -0
  88. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +24 -0
  89. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +12 -0
  90. package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +53 -0
  91. package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +32 -0
  92. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +37 -0
  93. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +50 -0
  94. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +52 -0
  95. package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +27 -0
  96. package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +27 -0
  97. package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +27 -0
  98. package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +29 -0
  99. package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +77 -0
  100. package/bundled/dev-pipeline-windows/templates/sections/phase0-init.md +13 -0
  101. package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +23 -0
  102. package/bundled/dev-pipeline-windows/templates/sections/session-context.md +5 -0
  103. package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +6 -0
  104. package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-agent.md +67 -0
  105. package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-lite.md +58 -0
  106. package/bundled/dev-pipeline-windows/templates/session-status-schema.json +83 -0
  107. package/bundled/skills/_metadata.json +1 -1
  108. package/bundled/skills/app-planner/SKILL.md +26 -18
  109. package/bundled/skills/app-planner/references/architecture-decisions.md +9 -5
  110. package/bundled/skills/app-planner/references/frontend-design-guide.md +1 -1
  111. package/bundled/skills/feature-planner/SKILL.md +9 -2
  112. package/bundled/skills/prizmkit-init/SKILL.md +7 -6
  113. package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +2 -0
  114. package/bundled/skills-windows/app-planner/SKILL.md +639 -0
  115. package/bundled/skills-windows/app-planner/assets/app-design-guide.md +101 -0
  116. package/bundled/skills-windows/app-planner/references/architecture-decisions.md +52 -0
  117. package/bundled/skills-windows/app-planner/references/brainstorm-guide.md +101 -0
  118. package/bundled/skills-windows/app-planner/references/frontend-design-guide.md +71 -0
  119. package/bundled/skills-windows/app-planner/references/project-brief-guide.md +82 -0
  120. package/bundled/skills-windows/app-planner/references/red-team-checklist.md +40 -0
  121. package/bundled/skills-windows/app-planner/references/rules/backend/derivation-rules.md +609 -0
  122. package/bundled/skills-windows/app-planner/references/rules/backend/fixed-rules.md +285 -0
  123. package/bundled/skills-windows/app-planner/references/rules/backend/question-bank.md +249 -0
  124. package/bundled/skills-windows/app-planner/references/rules/backend/template.md +173 -0
  125. package/bundled/skills-windows/app-planner/references/rules/database/derivation-rules.md +373 -0
  126. package/bundled/skills-windows/app-planner/references/rules/database/fixed-rules.md +211 -0
  127. package/bundled/skills-windows/app-planner/references/rules/database/question-bank.md +184 -0
  128. package/bundled/skills-windows/app-planner/references/rules/database/template.md +158 -0
  129. package/bundled/skills-windows/app-planner/references/rules/frontend/derivation-rules.md +810 -0
  130. package/bundled/skills-windows/app-planner/references/rules/frontend/fixed-rules.md +188 -0
  131. package/bundled/skills-windows/app-planner/references/rules/frontend/question-bank.md +302 -0
  132. package/bundled/skills-windows/app-planner/references/rules/frontend/template.md +320 -0
  133. package/bundled/skills-windows/app-planner/references/rules/mobile/derivation-rules.md +639 -0
  134. package/bundled/skills-windows/app-planner/references/rules/mobile/fixed-rules.md +290 -0
  135. package/bundled/skills-windows/app-planner/references/rules/mobile/question-bank.md +232 -0
  136. package/bundled/skills-windows/app-planner/references/rules/mobile/template.md +175 -0
  137. package/bundled/skills-windows/bug-fix-workflow/SKILL.md +415 -0
  138. package/bundled/skills-windows/bug-planner/SKILL.md +395 -0
  139. package/bundled/skills-windows/bug-planner/assets/bug-confirmation-template.md +43 -0
  140. package/bundled/skills-windows/bug-planner/references/critic-and-verification.md +44 -0
  141. package/bundled/skills-windows/bug-planner/references/error-recovery.md +73 -0
  142. package/bundled/skills-windows/bug-planner/references/input-formats.md +53 -0
  143. package/bundled/skills-windows/bug-planner/references/schema-validation.md +25 -0
  144. package/bundled/skills-windows/bug-planner/references/severity-rules.md +16 -0
  145. package/bundled/skills-windows/bug-planner/scripts/validate-bug-list.py +322 -0
  146. package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +380 -0
  147. package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +441 -0
  148. package/bundled/skills-windows/feature-pipeline-launcher/scripts/preflight-check.py +462 -0
  149. package/bundled/skills-windows/feature-planner/SKILL.md +401 -0
  150. package/bundled/skills-windows/feature-planner/assets/evaluation-guide.md +64 -0
  151. package/bundled/skills-windows/feature-planner/assets/planning-guide.md +214 -0
  152. package/bundled/skills-windows/feature-planner/references/browser-interaction.md +59 -0
  153. package/bundled/skills-windows/feature-planner/references/completeness-review.md +57 -0
  154. package/bundled/skills-windows/feature-planner/references/decomposition-patterns.md +75 -0
  155. package/bundled/skills-windows/feature-planner/references/error-recovery.md +90 -0
  156. package/bundled/skills-windows/feature-planner/references/incremental-feature-planning.md +112 -0
  157. package/bundled/skills-windows/feature-planner/references/new-project-planning.md +85 -0
  158. package/bundled/skills-windows/feature-planner/scripts/validate-and-generate.py +1029 -0
  159. package/bundled/skills-windows/feature-workflow/SKILL.md +531 -0
  160. package/bundled/skills-windows/prizmkit-init/SKILL.md +356 -0
  161. package/bundled/skills-windows/prizmkit-init/assets/project-brief-template.md +82 -0
  162. package/bundled/skills-windows/prizmkit-init/references/config-schema.md +68 -0
  163. package/bundled/skills-windows/prizmkit-init/references/rules/layer-detection.md +41 -0
  164. package/bundled/skills-windows/prizmkit-init/references/tech-stack-catalog.md +13 -0
  165. package/bundled/skills-windows/prizmkit-init/references/update-supplement.md +9 -0
  166. package/bundled/skills-windows/recovery-workflow/SKILL.md +456 -0
  167. package/bundled/skills-windows/recovery-workflow/evals/evals.json +46 -0
  168. package/bundled/skills-windows/recovery-workflow/scripts/detect-recovery-state.py +544 -0
  169. package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +406 -0
  170. package/bundled/skills-windows/refactor-planner/SKILL.md +540 -0
  171. package/bundled/skills-windows/refactor-planner/assets/planning-guide.md +292 -0
  172. package/bundled/skills-windows/refactor-planner/references/behavior-preservation.md +301 -0
  173. package/bundled/skills-windows/refactor-planner/references/refactor-scoping-guide.md +221 -0
  174. package/bundled/skills-windows/refactor-planner/scripts/validate-and-generate-refactor.py +858 -0
  175. package/bundled/skills-windows/refactor-workflow/SKILL.md +503 -0
  176. package/package.json +3 -2
  177. package/src/clean.js +73 -2
  178. package/src/config.js +159 -50
  179. package/src/detect-platform.js +16 -8
  180. package/src/external-skills.js +26 -19
  181. package/src/index.js +31 -9
  182. package/src/manifest.js +6 -2
  183. package/src/metadata.js +43 -5
  184. package/src/platforms.js +36 -0
  185. package/src/prompts.js +31 -6
  186. package/src/runtimes.js +20 -0
  187. package/src/scaffold.js +314 -110
  188. package/src/upgrade.js +81 -41
@@ -0,0 +1,685 @@
1
+ #!/usr/bin/env python3
2
+ """Generate a session-specific bug fix bootstrap prompt from template and .prizmkit/plans/bug-fix-list.json.
3
+
4
+ Reads the bugfix-bootstrap-prompt.md template and a .prizmkit/plans/bug-fix-list.json, resolves all
5
+ {{PLACEHOLDER}} variables, handles conditional blocks, and writes the rendered
6
+ prompt to the specified output path.
7
+
8
+ Usage:
9
+ python3 generate-bugfix-prompt.py \
10
+ --bug-list <path> --bug-id <id> \
11
+ --session-id <id> --run-id <id> \
12
+ --retry-count <n> --resume-phase <n|null> \
13
+ --state-dir <path> --output <path>
14
+ """
15
+
16
+ import argparse
17
+ import json
18
+ import os
19
+ import re
20
+ import sys
21
+
22
+ from utils import enrich_global_context, load_json_file, read_platform_conventions, setup_logging
23
+
24
+
25
+ DEFAULT_MAX_RETRIES = 3
26
+
27
+ LOGGER = setup_logging("generate-bugfix-prompt")
28
+
29
+
30
+ def parse_args():
31
+ parser = argparse.ArgumentParser(
32
+ description=(
33
+ "Generate a session-specific bug fix bootstrap prompt from a template "
34
+ "and .prizmkit/plans/bug-fix-list.json."
35
+ )
36
+ )
37
+ parser.add_argument("--bug-list", required=True, help="Path to .prizmkit/plans/bug-fix-list.json")
38
+ parser.add_argument("--bug-id", required=True, help="Bug ID to generate prompt for (e.g. B-001)")
39
+ parser.add_argument("--session-id", required=True, help="Session ID for this pipeline session")
40
+ parser.add_argument("--run-id", required=True, help="Pipeline run ID")
41
+ parser.add_argument("--retry-count", required=True, help="Current retry count")
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 (default: .prizmkit/state/bugfix)")
44
+ parser.add_argument("--output", required=True, help="Output path for the rendered prompt")
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")
48
+ parser.add_argument("--no-checkpoint", action="store_true", help="Do not write workflow-checkpoint.json (used by pipeline dry-run)")
49
+ return parser.parse_args()
50
+
51
+
52
+ def read_text_file(path):
53
+ """Read and return the text content of a file."""
54
+ abs_path = os.path.abspath(path)
55
+ if not os.path.isfile(abs_path):
56
+ return None, "File not found: {}".format(abs_path)
57
+ try:
58
+ with open(abs_path, "r", encoding="utf-8") as f:
59
+ return f.read(), None
60
+ except IOError as e:
61
+ return None, "Cannot read file: {}".format(str(e))
62
+
63
+
64
+ def find_bug(bugs, bug_id):
65
+ """Find and return the bug dict matching the given ID."""
66
+ for bug in bugs:
67
+ if isinstance(bug, dict) and bug.get("id") == bug_id:
68
+ return bug
69
+ return None
70
+
71
+
72
+ def format_acceptance_criteria(criteria):
73
+ """Format acceptance criteria as a markdown bullet list."""
74
+ if not criteria:
75
+ return "- (none specified)"
76
+ lines = []
77
+ for item in criteria:
78
+ lines.append("- {}".format(item))
79
+ return "\n".join(lines)
80
+
81
+
82
+ def format_global_context(global_context, project_root=None):
83
+ """Format global_context dict as a key-value list.
84
+
85
+ If global_context is empty/sparse and project_root is provided,
86
+ auto-detect tech stack from project files to fill gaps.
87
+ """
88
+ if project_root:
89
+ enrich_global_context(global_context, project_root)
90
+
91
+ if not global_context:
92
+ return "- (none specified)"
93
+ lines = []
94
+ for key, value in sorted(global_context.items()):
95
+ lines.append("- **{}**: {}".format(key, value))
96
+ return "\n".join(lines)
97
+
98
+
99
+ def format_error_source_details(error_source):
100
+ """Format error_source fields into markdown detail lines."""
101
+ if not error_source or not isinstance(error_source, dict):
102
+ return "- (no error source details)"
103
+ lines = []
104
+ etype = error_source.get("type", "unknown")
105
+
106
+ if etype == "stack_trace" and error_source.get("stack_trace"):
107
+ lines.append("- **Stack Trace**:")
108
+ lines.append("```")
109
+ lines.append(error_source["stack_trace"])
110
+ lines.append("```")
111
+ if error_source.get("error_message"):
112
+ lines.append("- **Error Message**: {}".format(error_source["error_message"]))
113
+ if etype == "log_pattern" and error_source.get("log_snippet"):
114
+ lines.append("- **Log Snippet**:")
115
+ lines.append("```")
116
+ lines.append(error_source["log_snippet"])
117
+ lines.append("```")
118
+ if etype == "failed_test" and error_source.get("failed_test_path"):
119
+ lines.append("- **Failed Test**: `{}`".format(error_source["failed_test_path"]))
120
+ if etype == "user_report" and error_source.get("reproduction_steps"):
121
+ lines.append("- **Reproduction Steps**:")
122
+ for i, step in enumerate(error_source["reproduction_steps"], 1):
123
+ lines.append(" {}. {}".format(i, step))
124
+
125
+ if not lines:
126
+ lines.append("- (no additional details)")
127
+ return "\n".join(lines)
128
+
129
+
130
+ def format_user_context(user_context):
131
+ """Format user_context array as a markdown section.
132
+
133
+ Returns empty string if user_context is empty or absent,
134
+ so the template placeholder resolves to nothing.
135
+ """
136
+ if not user_context or not isinstance(user_context, list):
137
+ return ""
138
+ items = [item for item in user_context if isinstance(item, str) and item.strip()]
139
+ if not items:
140
+ return ""
141
+ lines = [
142
+ "### User-Provided Context (HIGHEST PRIORITY)",
143
+ "",
144
+ "> The following materials were provided by the user. "
145
+ "They take precedence over AI inference.",
146
+ "",
147
+ ]
148
+ for item in items:
149
+ lines.append("- {}".format(item))
150
+ return "\n".join(lines)
151
+
152
+
153
+ def format_environment(env):
154
+ """Format environment dict as a key-value list."""
155
+ if not env or not isinstance(env, dict):
156
+ return "- (not specified)"
157
+ lines = []
158
+ for key, value in sorted(env.items()):
159
+ if value:
160
+ lines.append("- **{}**: {}".format(key, value))
161
+ if not lines:
162
+ return "- (not specified)"
163
+ return "\n".join(lines)
164
+
165
+
166
+ def get_prev_session_status(state_dir, bug_id):
167
+ """Read previous session status from state dir if available."""
168
+ if not state_dir:
169
+ return "N/A (first run)"
170
+
171
+ bug_status_path = os.path.join(state_dir, "bugs", bug_id, "status.json")
172
+ if not os.path.isfile(bug_status_path):
173
+ return "N/A (first run)"
174
+
175
+ try:
176
+ with open(bug_status_path, "r", encoding="utf-8") as f:
177
+ bug_status = json.load(f)
178
+ except (json.JSONDecodeError, IOError):
179
+ return "N/A (could not read bug status)"
180
+
181
+ last_session_id = bug_status.get("last_session_id")
182
+ if not last_session_id:
183
+ return "N/A (first run)"
184
+
185
+ session_status_path = os.path.join(
186
+ state_dir, "bugs", bug_id, "sessions",
187
+ last_session_id, "session-status.json"
188
+ )
189
+ if not os.path.isfile(session_status_path):
190
+ return "N/A (previous session status file not found)"
191
+
192
+ try:
193
+ with open(session_status_path, "r", encoding="utf-8") as f:
194
+ session_data = json.load(f)
195
+ except (json.JSONDecodeError, IOError):
196
+ return "N/A (could not read previous session status)"
197
+
198
+ status = session_data.get("status", "unknown")
199
+ checkpoint = session_data.get("checkpoint_reached", "none")
200
+ current_phase = session_data.get("current_phase", "unknown")
201
+ errors = session_data.get("errors", [])
202
+
203
+ result = "{} (checkpoint: {}, last phase: {})".format(
204
+ status, checkpoint, current_phase
205
+ )
206
+ if errors:
207
+ result += " — errors: {}".format("; ".join(str(e) for e in errors))
208
+ return result
209
+
210
+
211
+ def resolve_project_root(script_dir):
212
+ """Resolve project root. Layout-aware:
213
+ <project>/.prizmkit/dev-pipeline/scripts/ → <project>
214
+ <repo>/dev-pipeline/scripts/ → <repo>
215
+ """
216
+ pipeline_dir = os.path.dirname(script_dir)
217
+ pipeline_parent = os.path.dirname(pipeline_dir)
218
+ if os.path.basename(pipeline_parent) == ".prizmkit":
219
+ return os.path.abspath(os.path.dirname(pipeline_parent))
220
+ return os.path.abspath(pipeline_parent)
221
+
222
+
223
+ def build_replacements(args, bug, global_context, script_dir):
224
+ """Build the full dict of placeholder -> replacement value."""
225
+ project_root = resolve_project_root(script_dir)
226
+
227
+ # Platform-aware agent/team path resolution
228
+ platform = os.environ.get("PRIZMKIT_PLATFORM", "")
229
+ home_dir = os.path.expanduser("~")
230
+
231
+ if not platform:
232
+ if os.path.isdir(os.path.join(project_root, ".codex", "agents")):
233
+ platform = "codex"
234
+ elif os.path.isdir(os.path.join(project_root, ".claude", "agents")):
235
+ platform = "claude"
236
+ else:
237
+ platform = "codebuddy"
238
+
239
+ if platform == "claude":
240
+ agents_dir = os.path.join(project_root, ".claude", "agents")
241
+ team_config_path = os.path.join(project_root, ".claude", "team-info.json")
242
+ elif platform == "codex":
243
+ agents_dir = os.path.join(project_root, ".codex", "agents")
244
+ team_config_path = os.path.join(project_root, ".codex", "team-info.json")
245
+ else:
246
+ agents_dir = os.path.join(project_root, ".codebuddy", "agents")
247
+ team_config_path = os.path.join(
248
+ home_dir, ".codebuddy", "teams", "prizm-dev-team", "config.json"
249
+ )
250
+
251
+ agent_ext = ".toml" if platform == "codex" else ".md"
252
+ dev_subagent = os.path.join(agents_dir, f"prizm-dev-team-dev{agent_ext}")
253
+ reviewer_subagent = os.path.join(agents_dir, f"prizm-dev-team-reviewer{agent_ext}")
254
+
255
+ # Session status path
256
+ session_status_path = os.path.join(
257
+ project_root, ".prizmkit", "state", "bugfix", "bugs", args.bug_id,
258
+ "sessions", args.session_id, "session-status.json"
259
+ )
260
+
261
+ # Error source
262
+ error_source = bug.get("error_source", {})
263
+ error_type = error_source.get("type", "unknown") if isinstance(error_source, dict) else "unknown"
264
+
265
+ # Determine fix scope from affected_modules or title
266
+ affected_modules = bug.get("affected_modules", [])
267
+ if affected_modules:
268
+ fix_scope = affected_modules[0]
269
+ else:
270
+ fix_scope = bug.get("title", "unknown").split()[0].lower() if bug.get("title") else "unknown"
271
+
272
+ # Determine verification type
273
+ vtype = bug.get("verification_type", "automated")
274
+
275
+ # Browser interaction - extract from bug if present
276
+ browser_interaction = bug.get("browser_interaction")
277
+ browser_enabled = False
278
+ browser_verify_steps = ""
279
+ browser_tool = "auto"
280
+
281
+ # Environment override
282
+ browser_verify_env = os.environ.get("BROWSER_VERIFY", "").lower()
283
+ if browser_verify_env == "false":
284
+ browser_interaction = None
285
+
286
+ # Extract browser config (same logic as feature pipeline)
287
+ if browser_interaction and isinstance(browser_interaction, bool):
288
+ browser_enabled = True
289
+ browser_tool = "auto"
290
+ browser_verify_steps = " # (no specific verify goals — reproduce the bug and verify it's fixed)"
291
+ elif browser_interaction and isinstance(browser_interaction, dict):
292
+ browser_tool = browser_interaction.get("tool", "auto")
293
+ if browser_tool not in ("playwright-cli", "opencli", "auto"):
294
+ LOGGER.warning("Unknown browser_interaction.tool '%s', defaulting to 'auto'", browser_tool)
295
+ browser_tool = "auto"
296
+
297
+ steps = browser_interaction.get("verify_steps", [])
298
+ if steps:
299
+ browser_enabled = True
300
+ browser_verify_steps = "\n".join(
301
+ " # Step {}: {}".format(i + 1, step)
302
+ for i, step in enumerate(steps)
303
+ )
304
+ elif browser_interaction.get("url") or browser_interaction.get("enabled", True):
305
+ browser_enabled = True
306
+ browser_verify_steps = " # (reproduce bug and verify fix)"
307
+
308
+ replacements = {
309
+ "{{RUN_ID}}": args.run_id,
310
+ "{{SESSION_ID}}": args.session_id,
311
+ "{{BUG_ID}}": args.bug_id,
312
+ "{{BUG_TITLE}}": bug.get("title", ""),
313
+ "{{SEVERITY}}": bug.get("severity", "medium"),
314
+ "{{VERIFICATION_TYPE}}": vtype,
315
+ "{{BUG_DESCRIPTION}}": bug.get("description", ""),
316
+ "{{USER_CONTEXT}}": format_user_context(bug.get("user_context", [])),
317
+ "{{ERROR_SOURCE_TYPE}}": error_type,
318
+ "{{ERROR_SOURCE_DETAILS}}": format_error_source_details(error_source),
319
+ "{{ACCEPTANCE_CRITERIA}}": format_acceptance_criteria(
320
+ bug.get("acceptance_criteria", [])
321
+ ),
322
+ "{{ENVIRONMENT}}": format_environment(bug.get("environment")),
323
+ "{{GLOBAL_CONTEXT}}": format_global_context(global_context, project_root),
324
+ "{{PLATFORM_CONVENTIONS}}": read_platform_conventions(project_root),
325
+ "{{TEAM_CONFIG_PATH}}": team_config_path,
326
+ "{{DEV_SUBAGENT_PATH}}": dev_subagent,
327
+ "{{REVIEWER_SUBAGENT_PATH}}": reviewer_subagent,
328
+ "{{SESSION_STATUS_PATH}}": session_status_path,
329
+ "{{PROJECT_ROOT}}": project_root,
330
+ "{{PIPELINE_DIR}}": ".prizmkit\\dev-pipeline",
331
+ "{{FIX_SCOPE}}": fix_scope,
332
+ "{{TIMESTAMP}}": "", # Placeholder, agent fills in the timestamp
333
+ "{{BROWSER_ENABLED}}": "true" if browser_enabled else "false",
334
+ "{{BROWSER_TOOL}}": browser_tool,
335
+ "{{BROWSER_VERIFY_STEPS}}": browser_verify_steps,
336
+ }
337
+
338
+ return replacements
339
+
340
+
341
+ def process_conditional_blocks(content, bug):
342
+ """Handle conditional blocks based on verification_type and browser_interaction."""
343
+ # Handle verification type blocks
344
+ vtype = bug.get("verification_type", "automated")
345
+ is_manual_or_hybrid = vtype in ("manual", "hybrid")
346
+
347
+ if is_manual_or_hybrid:
348
+ content = content.replace("{{IF_VERIFICATION_MANUAL_OR_HYBRID}}\n", "")
349
+ content = content.replace("{{IF_VERIFICATION_MANUAL_OR_HYBRID}}", "")
350
+ content = content.replace("{{END_IF_VERIFICATION_MANUAL_OR_HYBRID}}\n", "")
351
+ content = content.replace("{{END_IF_VERIFICATION_MANUAL_OR_HYBRID}}", "")
352
+ else:
353
+ # Remove the entire conditional block
354
+ content = re.sub(
355
+ r"\{\{IF_VERIFICATION_MANUAL_OR_HYBRID\}\}.*?\{\{END_IF_VERIFICATION_MANUAL_OR_HYBRID\}\}\n?",
356
+ "", content, flags=re.DOTALL,
357
+ )
358
+
359
+ # Handle browser interaction blocks
360
+ browser_interaction = bug.get("browser_interaction")
361
+ browser_enabled = False
362
+ browser_tool = "auto"
363
+
364
+ # Check environment override
365
+ browser_verify_env = os.environ.get("BROWSER_VERIFY", "").lower()
366
+ if browser_verify_env == "false":
367
+ browser_interaction = None
368
+
369
+ # Determine if browser is enabled
370
+ if browser_interaction:
371
+ if isinstance(browser_interaction, bool):
372
+ browser_enabled = True
373
+ elif isinstance(browser_interaction, dict):
374
+ steps = browser_interaction.get("verify_steps", [])
375
+ if steps or browser_interaction.get("url") or browser_interaction.get("enabled", True):
376
+ browser_enabled = True
377
+ browser_tool = browser_interaction.get("tool", "auto")
378
+
379
+ # Process browser interaction blocks
380
+ browser_open = "{{IF_BROWSER_INTERACTION}}"
381
+ browser_close = "{{END_IF_BROWSER_INTERACTION}}"
382
+
383
+ if browser_enabled:
384
+ # Keep content, remove tags
385
+ content = content.replace(browser_open + "\n", "")
386
+ content = content.replace(browser_open, "")
387
+ content = content.replace(browser_close + "\n", "")
388
+ content = content.replace(browser_close, "")
389
+ else:
390
+ # Remove entire block
391
+ pattern = re.escape(browser_open) + r".*?" + re.escape(browser_close) + r"\n?"
392
+ content = re.sub(pattern, "", content, flags=re.DOTALL)
393
+
394
+ # Process browser tool selection blocks (nested inside browser interaction)
395
+ tool_variants = ["PLAYWRIGHT", "OPENCLI", "AUTO"]
396
+ active_variant = {
397
+ "playwright-cli": "PLAYWRIGHT",
398
+ "opencli": "OPENCLI",
399
+ "auto": "AUTO",
400
+ }.get(browser_tool, "AUTO")
401
+
402
+ for variant in tool_variants:
403
+ tool_open = "{{{{IF_BROWSER_TOOL_{}}}}}".format(variant)
404
+ tool_close = "{{{{END_IF_BROWSER_TOOL_{}}}}}".format(variant)
405
+
406
+ if variant == active_variant and browser_enabled:
407
+ # Keep content, remove tags
408
+ content = content.replace(tool_open + "\n", "")
409
+ content = content.replace(tool_open, "")
410
+ content = content.replace(tool_close + "\n", "")
411
+ content = content.replace(tool_close, "")
412
+ else:
413
+ # Remove entire block
414
+ pat = re.escape(tool_open) + r".*?" + re.escape(tool_close) + r"\n?"
415
+ content = re.sub(pat, "", content, flags=re.DOTALL)
416
+
417
+ return content
418
+
419
+
420
+ def render_template(template_content, replacements, bug):
421
+ """Render the template by processing conditionals and replacing placeholders."""
422
+ # Step 1: Process conditional blocks
423
+ content = process_conditional_blocks(template_content, bug)
424
+
425
+ # Step 2: Replace all {{PLACEHOLDER}} variables
426
+ for placeholder, value in replacements.items():
427
+ content = content.replace(placeholder, value)
428
+
429
+ return content
430
+
431
+
432
+ def write_output(output_path, content):
433
+ """Write the rendered content to the output file."""
434
+ abs_path = os.path.abspath(output_path)
435
+ output_dir = os.path.dirname(abs_path)
436
+ if output_dir and not os.path.isdir(output_dir):
437
+ try:
438
+ os.makedirs(output_dir, exist_ok=True)
439
+ except OSError as e:
440
+ return "Cannot create output directory: {}".format(str(e))
441
+ try:
442
+ with open(abs_path, "w", encoding="utf-8") as f:
443
+ f.write(content)
444
+ except IOError as e:
445
+ return "Cannot write output file: {}".format(str(e))
446
+ return None
447
+
448
+
449
+ def emit_failure(message):
450
+ """Emit standardized failure JSON and exit."""
451
+ print(json.dumps({"success": False, "error": message}, indent=2, ensure_ascii=False))
452
+ sys.exit(1)
453
+
454
+
455
+ # ============================================================
456
+ # Checkpoint generation for bugfix pipeline
457
+ # ============================================================
458
+
459
+ BUGFIX_STEPS = [
460
+ ("prizmkit-init", "Initialize", []),
461
+ ("bug-diagnosis-and-plan", "Diagnose & Plan",
462
+ [".prizmkit/bugfix/{slug}/spec.md",
463
+ ".prizmkit/bugfix/{slug}/plan.md"]),
464
+ ("prizmkit-implement", "Implement Fix", []),
465
+ ("prizmkit-code-review", "Code Review", []),
466
+ ("prizmkit-committer", "Commit", ["--headless"]),
467
+ ("bug-report", "Generate Fix Report",
468
+ [".prizmkit/bugfix/{slug}/fix-report.md"]),
469
+ ]
470
+
471
+
472
+ def generate_bugfix_checkpoint(bug_id, session_id):
473
+ """Generate a checkpoint definition for bugfix pipeline.
474
+
475
+ Returns a dict suitable for writing as workflow-checkpoint.json.
476
+ """
477
+ steps = []
478
+ prev_id = None
479
+ for i, (skill, name, artifacts) in enumerate(BUGFIX_STEPS, 1):
480
+ step_id = "S{:02d}".format(i)
481
+ steps.append({
482
+ "id": step_id,
483
+ "skill": skill,
484
+ "name": name,
485
+ "status": "pending",
486
+ "required_artifacts": [a.replace("{slug}", bug_id) for a in artifacts],
487
+ "depends_on": prev_id,
488
+ })
489
+ prev_id = step_id
490
+
491
+ return {
492
+ "version": 1,
493
+ "workflow_type": "bugfix-pipeline",
494
+ "pipeline_mode": "single",
495
+ "item_id": bug_id,
496
+ "item_slug": bug_id,
497
+ "session_id": session_id,
498
+ "steps": steps,
499
+ }
500
+
501
+
502
+ def merge_bugfix_checkpoint_state(existing, fresh, project_root):
503
+ """Merge existing bugfix checkpoint state into fresh definition.
504
+
505
+ Same logic as feature pipeline: validate artifacts, break chain on
506
+ first invalid step.
507
+ """
508
+ existing_status = {}
509
+ existing_artifacts = {}
510
+ for step in existing.get("steps", []):
511
+ existing_status[step["skill"]] = step["status"]
512
+ existing_artifacts[step["skill"]] = step.get("required_artifacts", [])
513
+
514
+ valid_completed = set()
515
+ for skill_key, status in existing_status.items():
516
+ if status == "completed":
517
+ artifacts = existing_artifacts.get(skill_key, [])
518
+ if all(os.path.exists(os.path.join(project_root, a))
519
+ for a in artifacts):
520
+ valid_completed.add(skill_key)
521
+ else:
522
+ LOGGER.warning(
523
+ "Step '%s' was completed but artifacts missing — "
524
+ "resetting to pending", skill_key
525
+ )
526
+ elif status == "skipped":
527
+ valid_completed.add(skill_key)
528
+
529
+ chain_broken = False
530
+ for step in fresh["steps"]:
531
+ if chain_broken:
532
+ step["status"] = "pending"
533
+ continue
534
+ prev = existing_status.get(step["skill"])
535
+ if step["skill"] in valid_completed:
536
+ step["status"] = prev
537
+ else:
538
+ chain_broken = True
539
+ step["status"] = "pending"
540
+
541
+ return fresh
542
+
543
+
544
+ def main():
545
+ args = parse_args()
546
+
547
+ # Resolve script directory
548
+ script_dir = os.path.dirname(os.path.abspath(__file__))
549
+
550
+ # Resolve template path
551
+ if args.template:
552
+ template_path = args.template
553
+ else:
554
+ template_path = os.path.join(
555
+ script_dir, "..", "templates", "bugfix-bootstrap-prompt.md"
556
+ )
557
+
558
+ # Load template
559
+ template_content, err = read_text_file(template_path)
560
+ if err:
561
+ emit_failure("Template error: {}".format(err))
562
+
563
+ # Load bug fix list
564
+ bug_list_data, err = load_json_file(args.bug_list)
565
+ if err:
566
+ emit_failure("Bug list error: {}".format(err))
567
+
568
+ # Extract bugs array
569
+ bugs = bug_list_data.get("bugs")
570
+ if not isinstance(bugs, list):
571
+ emit_failure("Bug fix list does not contain a 'bugs' array")
572
+
573
+ # Find the target bug
574
+ bug = find_bug(bugs, args.bug_id)
575
+ if bug is None:
576
+ emit_failure("Bug '{}' not found in bug fix list".format(args.bug_id))
577
+
578
+ # Extract global context
579
+ global_context = bug_list_data.get("global_context", {})
580
+ if not isinstance(global_context, dict):
581
+ global_context = {}
582
+
583
+ # Build replacements
584
+ replacements = build_replacements(args, bug, global_context, script_dir)
585
+
586
+ # Add checkpoint path to replacements
587
+ checkpoint_rel = os.path.join(
588
+ ".prizmkit", "bugfix", args.bug_id, "workflow-checkpoint.json",
589
+ )
590
+ replacements["{{CHECKPOINT_PATH}}"] = checkpoint_rel
591
+
592
+ # Render the template
593
+ rendered = render_template(template_content, replacements, bug)
594
+
595
+ # Write the output
596
+ err = write_output(args.output, rendered)
597
+ if err:
598
+ emit_failure(err)
599
+
600
+ # Generate checkpoint file
601
+ project_root = resolve_project_root(script_dir)
602
+ checkpoint_path = os.path.join(project_root, checkpoint_rel)
603
+ if not args.no_checkpoint:
604
+ checkpoint_dir = os.path.dirname(checkpoint_path)
605
+ os.makedirs(checkpoint_dir, exist_ok=True)
606
+
607
+ checkpoint = generate_bugfix_checkpoint(args.bug_id, args.session_id)
608
+
609
+ is_resume = args.resume_phase != "null"
610
+ if is_resume and os.path.exists(checkpoint_path):
611
+ try:
612
+ with open(checkpoint_path, "r", encoding="utf-8") as f:
613
+ existing = json.load(f)
614
+ checkpoint = merge_bugfix_checkpoint_state(
615
+ existing, checkpoint, project_root,
616
+ )
617
+ LOGGER.info("Merged existing bugfix checkpoint from %s",
618
+ checkpoint_path)
619
+ except (json.JSONDecodeError, KeyError) as exc:
620
+ LOGGER.warning(
621
+ "Existing bugfix checkpoint corrupted (%s) — generating fresh",
622
+ exc,
623
+ )
624
+
625
+ with open(checkpoint_path, "w", encoding="utf-8") as f:
626
+ json.dump(checkpoint, f, indent=2, ensure_ascii=False)
627
+ LOGGER.info("Wrote bugfix checkpoint to %s", checkpoint_path)
628
+
629
+ # Resolve critic and mode
630
+ bug_critic = bug.get("critic", False)
631
+ if args.critic is not None:
632
+ critic_enabled = str(args.critic).lower() == "true"
633
+ else:
634
+ critic_enabled = bool(bug_critic)
635
+
636
+ pipeline_mode = args.mode or "standard"
637
+ agent_count = 5 if critic_enabled else 3
638
+
639
+ # Success
640
+ bug_model = bug.get("model", "")
641
+ # Extract browser interaction state for output
642
+ browser_interaction = bug.get("browser_interaction")
643
+ browser_enabled = False
644
+ browser_tool = "auto"
645
+
646
+ browser_verify_env = os.environ.get("BROWSER_VERIFY", "").lower()
647
+ if browser_verify_env == "false":
648
+ browser_interaction = None
649
+
650
+ if browser_interaction:
651
+ if isinstance(browser_interaction, bool):
652
+ browser_enabled = True
653
+ elif isinstance(browser_interaction, dict):
654
+ steps = browser_interaction.get("verify_steps", [])
655
+ if steps or browser_interaction.get("url") or browser_interaction.get("enabled", True):
656
+ browser_enabled = True
657
+ browser_tool = browser_interaction.get("tool", "auto")
658
+ if browser_tool not in ("playwright-cli", "opencli", "auto"):
659
+ browser_tool = "auto"
660
+
661
+ output = {
662
+ "success": True,
663
+ "output_path": os.path.abspath(args.output),
664
+ "checkpoint_path": checkpoint_path,
665
+ "model": bug_model,
666
+ "pipeline_mode": pipeline_mode,
667
+ "agent_count": agent_count,
668
+ "critic_enabled": critic_enabled,
669
+ "browser_enabled": browser_enabled,
670
+ "browser_tool": browser_tool
671
+ }
672
+ print(json.dumps(output, indent=2, ensure_ascii=False))
673
+ sys.exit(0)
674
+
675
+
676
+ if __name__ == "__main__":
677
+ try:
678
+ main()
679
+ except KeyboardInterrupt:
680
+ emit_failure("generate-bugfix-prompt interrupted")
681
+ except SystemExit:
682
+ raise
683
+ except Exception as exc:
684
+ LOGGER.exception("Unhandled exception in generate-bugfix-prompt")
685
+ emit_failure("Unexpected error: {}".format(str(exc)))