galangal-orchestrate 0.13.0__py3-none-any.whl

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 (79) hide show
  1. galangal/__init__.py +36 -0
  2. galangal/__main__.py +6 -0
  3. galangal/ai/__init__.py +167 -0
  4. galangal/ai/base.py +159 -0
  5. galangal/ai/claude.py +352 -0
  6. galangal/ai/codex.py +370 -0
  7. galangal/ai/gemini.py +43 -0
  8. galangal/ai/subprocess.py +254 -0
  9. galangal/cli.py +371 -0
  10. galangal/commands/__init__.py +27 -0
  11. galangal/commands/complete.py +367 -0
  12. galangal/commands/github.py +355 -0
  13. galangal/commands/init.py +177 -0
  14. galangal/commands/init_wizard.py +762 -0
  15. galangal/commands/list.py +20 -0
  16. galangal/commands/pause.py +34 -0
  17. galangal/commands/prompts.py +89 -0
  18. galangal/commands/reset.py +41 -0
  19. galangal/commands/resume.py +30 -0
  20. galangal/commands/skip.py +62 -0
  21. galangal/commands/start.py +530 -0
  22. galangal/commands/status.py +44 -0
  23. galangal/commands/switch.py +28 -0
  24. galangal/config/__init__.py +15 -0
  25. galangal/config/defaults.py +183 -0
  26. galangal/config/loader.py +163 -0
  27. galangal/config/schema.py +330 -0
  28. galangal/core/__init__.py +33 -0
  29. galangal/core/artifacts.py +136 -0
  30. galangal/core/state.py +1097 -0
  31. galangal/core/tasks.py +454 -0
  32. galangal/core/utils.py +116 -0
  33. galangal/core/workflow/__init__.py +68 -0
  34. galangal/core/workflow/core.py +789 -0
  35. galangal/core/workflow/engine.py +781 -0
  36. galangal/core/workflow/pause.py +35 -0
  37. galangal/core/workflow/tui_runner.py +1322 -0
  38. galangal/exceptions.py +36 -0
  39. galangal/github/__init__.py +31 -0
  40. galangal/github/client.py +427 -0
  41. galangal/github/images.py +324 -0
  42. galangal/github/issues.py +298 -0
  43. galangal/logging.py +364 -0
  44. galangal/prompts/__init__.py +5 -0
  45. galangal/prompts/builder.py +527 -0
  46. galangal/prompts/defaults/benchmark.md +34 -0
  47. galangal/prompts/defaults/contract.md +35 -0
  48. galangal/prompts/defaults/design.md +54 -0
  49. galangal/prompts/defaults/dev.md +89 -0
  50. galangal/prompts/defaults/docs.md +104 -0
  51. galangal/prompts/defaults/migration.md +59 -0
  52. galangal/prompts/defaults/pm.md +110 -0
  53. galangal/prompts/defaults/pm_questions.md +53 -0
  54. galangal/prompts/defaults/preflight.md +32 -0
  55. galangal/prompts/defaults/qa.md +65 -0
  56. galangal/prompts/defaults/review.md +90 -0
  57. galangal/prompts/defaults/review_codex.md +99 -0
  58. galangal/prompts/defaults/security.md +84 -0
  59. galangal/prompts/defaults/test.md +91 -0
  60. galangal/results.py +176 -0
  61. galangal/ui/__init__.py +5 -0
  62. galangal/ui/console.py +126 -0
  63. galangal/ui/tui/__init__.py +56 -0
  64. galangal/ui/tui/adapters.py +168 -0
  65. galangal/ui/tui/app.py +902 -0
  66. galangal/ui/tui/entry.py +24 -0
  67. galangal/ui/tui/mixins.py +196 -0
  68. galangal/ui/tui/modals.py +339 -0
  69. galangal/ui/tui/styles/app.tcss +86 -0
  70. galangal/ui/tui/styles/modals.tcss +197 -0
  71. galangal/ui/tui/types.py +107 -0
  72. galangal/ui/tui/widgets.py +263 -0
  73. galangal/validation/__init__.py +5 -0
  74. galangal/validation/runner.py +1072 -0
  75. galangal_orchestrate-0.13.0.dist-info/METADATA +985 -0
  76. galangal_orchestrate-0.13.0.dist-info/RECORD +79 -0
  77. galangal_orchestrate-0.13.0.dist-info/WHEEL +4 -0
  78. galangal_orchestrate-0.13.0.dist-info/entry_points.txt +2 -0
  79. galangal_orchestrate-0.13.0.dist-info/licenses/LICENSE +674 -0
@@ -0,0 +1,527 @@
1
+ """
2
+ Prompt building with project override support.
3
+ """
4
+
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from galangal.config.loader import get_config, get_project_root, get_prompts_dir
9
+ from galangal.core.artifacts import artifact_exists, read_artifact
10
+ from galangal.core.state import Stage, WorkflowState
11
+
12
+
13
+ class PromptBuilder:
14
+ """
15
+ Build prompts for workflow stages with project override support.
16
+
17
+ This class constructs prompts by merging:
18
+ 1. Base prompts from `galangal/prompts/defaults/` (built into package)
19
+ 2. Project prompts from `.galangal/prompts/` (project-specific)
20
+ 3. Task context (description, artifacts, state)
21
+ 4. Config context (prompt_context, stage_context)
22
+
23
+ Project prompts can either:
24
+ - Supplement: Include `# BASE` marker where default prompt is inserted
25
+ - Override: No marker means complete replacement of base prompt
26
+
27
+ Example supplement prompt:
28
+ ```markdown
29
+ # Project-Specific Instructions
30
+ Follow our coding style guide.
31
+
32
+ # BASE
33
+
34
+ # Additional Notes
35
+ Use our custom test framework.
36
+ ```
37
+ """
38
+
39
+ def __init__(self) -> None:
40
+ self.config = get_config()
41
+ self.project_root = get_project_root()
42
+ self.override_dir = get_prompts_dir()
43
+ self.defaults_dir = Path(__file__).parent / "defaults"
44
+
45
+ def _merge_with_base(self, project_prompt: str, base_prompt: str) -> str:
46
+ """Merge project prompt with base using # BASE marker.
47
+
48
+ If project_prompt contains '# BASE', splits around it and inserts
49
+ the base_prompt at that location. Content before the marker becomes
50
+ a header, content after becomes a footer.
51
+
52
+ Args:
53
+ project_prompt: Project-specific prompt that may contain # BASE marker.
54
+ base_prompt: Default/base prompt to insert at marker location.
55
+
56
+ Returns:
57
+ Merged prompt with base inserted at marker, or project_prompt unchanged
58
+ if no marker present.
59
+ """
60
+ if "# BASE" not in project_prompt:
61
+ return project_prompt
62
+
63
+ parts = project_prompt.split("# BASE", 1)
64
+ header = parts[0].rstrip()
65
+ footer = parts[1].lstrip() if len(parts) > 1 else ""
66
+
67
+ result_parts = []
68
+ if header:
69
+ result_parts.append(header)
70
+ if base_prompt:
71
+ result_parts.append(base_prompt)
72
+ if footer:
73
+ result_parts.append(footer)
74
+
75
+ return "\n\n".join(result_parts)
76
+
77
+ def get_prompt_by_name(self, name: str) -> str:
78
+ """Get a prompt by filename (without .md extension).
79
+
80
+ Supports project override/supplement like get_stage_prompt.
81
+ Used for non-stage prompts like 'pm_questions'.
82
+
83
+ Args:
84
+ name: Prompt name, e.g., 'pm_questions'
85
+
86
+ Returns:
87
+ Prompt content with project overrides applied.
88
+ """
89
+ # Get base prompt
90
+ default_path = self.defaults_dir / f"{name}.md"
91
+ base_prompt = ""
92
+ if default_path.exists():
93
+ base_prompt = default_path.read_text()
94
+
95
+ # Check for project prompt
96
+ project_path = self.override_dir / f"{name}.md"
97
+ if not project_path.exists():
98
+ return base_prompt or f"Execute {name}."
99
+
100
+ project_prompt = project_path.read_text()
101
+
102
+ # Merge with base (returns project_prompt unchanged if no # BASE marker)
103
+ merged = self._merge_with_base(project_prompt, base_prompt)
104
+ if merged != project_prompt:
105
+ return merged
106
+
107
+ # No marker = full override
108
+ return project_prompt
109
+
110
+ def build_discovery_prompt(
111
+ self, state: WorkflowState, qa_history: list[dict[str, Any]] | None = None
112
+ ) -> str:
113
+ """Build the prompt for PM discovery questions.
114
+
115
+ Args:
116
+ state: Current workflow state with task info.
117
+ qa_history: Previous Q&A rounds, if any.
118
+
119
+ Returns:
120
+ Complete prompt for generating discovery questions.
121
+ """
122
+ base_prompt = self.get_prompt_by_name("pm_questions")
123
+ task_name = state.task_name
124
+
125
+ # Build context
126
+ context_parts = [
127
+ f"# Task: {task_name}",
128
+ f"# Task Type: {state.task_type.display_name()}",
129
+ f"# Brief\n{state.task_description}",
130
+ ]
131
+
132
+ # Add screenshot context if available
133
+ context_parts.extend(self._get_screenshot_context(state))
134
+
135
+ # Add previous Q&A history
136
+ if qa_history:
137
+ qa_text = self._format_qa_history(qa_history)
138
+ context_parts.append(f"\n# Previous Q&A Rounds\n{qa_text}")
139
+ else:
140
+ context_parts.append("\n# Previous Q&A Rounds\nNone - this is the first round.")
141
+
142
+ # Add global prompt context from config
143
+ if self.config.prompt_context:
144
+ context_parts.append(f"\n# Project Context\n{self.config.prompt_context}")
145
+
146
+ context = "\n".join(context_parts)
147
+ return f"{context}\n\n---\n\n{base_prompt}"
148
+
149
+ def _get_screenshot_context(self, state: WorkflowState) -> list[str]:
150
+ """
151
+ Get screenshot context for inclusion in prompts.
152
+
153
+ When screenshots are available from a GitHub issue, instructs the AI
154
+ to read them for visual context (bug reports, designs, etc.).
155
+
156
+ Args:
157
+ state: Workflow state containing screenshot paths.
158
+
159
+ Returns:
160
+ List of context strings to include in prompt.
161
+ """
162
+ if not state.screenshots:
163
+ return []
164
+
165
+ parts = ["\n# Screenshots from GitHub Issue"]
166
+ parts.append(
167
+ "The following screenshots were attached to the GitHub issue. "
168
+ "Use the Read tool to view these images for visual context "
169
+ "(e.g., bug screenshots, design mockups, UI references):"
170
+ )
171
+ for i, path in enumerate(state.screenshots, 1):
172
+ parts.append(f" {i}. {path}")
173
+
174
+ return ["\n".join(parts)]
175
+
176
+ def _format_qa_history(self, qa_history: list[dict[str, Any]]) -> str:
177
+ """Format Q&A history for prompt inclusion."""
178
+ parts = []
179
+ for i, round_data in enumerate(qa_history, 1):
180
+ parts.append(f"## Round {i}")
181
+ parts.append("### Questions")
182
+ for j, q in enumerate(round_data.get("questions", []), 1):
183
+ parts.append(f"{j}. {q}")
184
+ parts.append("### Answers")
185
+ for j, a in enumerate(round_data.get("answers", []), 1):
186
+ parts.append(f"{j}. {a}")
187
+ parts.append("")
188
+ return "\n".join(parts)
189
+
190
+ def get_stage_prompt(self, stage: Stage, backend_name: str | None = None) -> str:
191
+ """Get the prompt for a stage, with project override/supplement support.
192
+
193
+ Project prompts in .galangal/prompts/ can either:
194
+ - Supplement the base: Include '# BASE' marker where base prompt should be inserted
195
+ - Override entirely: No marker = full replacement of base prompt
196
+
197
+ Backend-specific prompts are supported. If backend_name is provided,
198
+ the system will first look for {stage}_{backend}.md (e.g., review_codex.md)
199
+ before falling back to the generic {stage}.md prompt.
200
+
201
+ Args:
202
+ stage: The workflow stage
203
+ backend_name: Optional backend name for backend-specific prompts
204
+ """
205
+ stage_lower = stage.value.lower()
206
+
207
+ # For backend-specific prompts, try {stage}_{backend}.md first
208
+ prompt_names = [stage_lower]
209
+ if backend_name:
210
+ # Insert backend-specific name at the front
211
+ prompt_names.insert(0, f"{stage_lower}_{backend_name}")
212
+
213
+ base_prompt = ""
214
+ for prompt_name in prompt_names:
215
+ default_path = self.defaults_dir / f"{prompt_name}.md"
216
+ if default_path.exists():
217
+ base_prompt = default_path.read_text()
218
+ break
219
+
220
+ # Check for project prompts (also supports backend-specific)
221
+ for prompt_name in prompt_names:
222
+ project_path = self.override_dir / f"{prompt_name}.md"
223
+ if project_path.exists():
224
+ project_prompt = project_path.read_text()
225
+
226
+ # Merge with base (returns project_prompt unchanged if no # BASE marker)
227
+ merged = self._merge_with_base(project_prompt, base_prompt)
228
+ if merged != project_prompt:
229
+ return merged
230
+
231
+ # No marker = full override
232
+ return project_prompt
233
+
234
+ return base_prompt or f"Execute the {stage.value} stage for the task."
235
+
236
+ def build_full_prompt(
237
+ self, stage: Stage, state: WorkflowState, backend_name: str | None = None
238
+ ) -> str:
239
+ """
240
+ Build the complete prompt for a stage execution.
241
+
242
+ Assembles a full prompt by combining:
243
+ 1. Task metadata (name, type, description, attempt)
244
+ 2. Relevant artifacts (SPEC.md, PLAN.md, ROLLBACK.md, etc.)
245
+ 3. Global prompt_context from config
246
+ 4. Stage-specific stage_context from config
247
+ 5. Documentation config (for DOCS and SECURITY stages)
248
+ 6. The stage prompt (from get_stage_prompt)
249
+
250
+ The artifacts included vary by stage - later stages receive more
251
+ context from earlier artifacts.
252
+
253
+ Args:
254
+ stage: The workflow stage to build prompt for.
255
+ state: Current workflow state with task info and history.
256
+ backend_name: Optional backend name for backend-specific prompts
257
+ (e.g., "codex" to use review_codex.md instead of review.md).
258
+
259
+ Returns:
260
+ Complete prompt string ready for AI invocation.
261
+ """
262
+ base_prompt = self.get_stage_prompt(stage, backend_name)
263
+ task_name = state.task_name
264
+
265
+ # Build context
266
+ context_parts = [
267
+ f"# Task: {task_name}",
268
+ f"# Task Type: {state.task_type.display_name()}",
269
+ f"# Description\n{state.task_description}",
270
+ f"\n# Current Stage: {stage.value}",
271
+ f"\n# Attempt: {state.attempt}",
272
+ f"\n# Artifacts Directory: {self.config.tasks_dir}/{task_name}/",
273
+ ]
274
+
275
+ # Add screenshot context if available (especially useful for PM and early stages)
276
+ if stage in [Stage.PM, Stage.DESIGN, Stage.DEV]:
277
+ context_parts.extend(self._get_screenshot_context(state))
278
+
279
+ # Add failure context
280
+ if state.last_failure:
281
+ context_parts.append(f"\n# Previous Failure\n{state.last_failure}")
282
+
283
+ # Add relevant artifacts based on stage
284
+ context_parts.extend(self._get_artifact_context(stage, task_name))
285
+
286
+ # Add global prompt context from config
287
+ if self.config.prompt_context:
288
+ context_parts.append(f"\n# Project Context\n{self.config.prompt_context}")
289
+
290
+ # Add stage-specific context from config
291
+ stage_context = self.config.stage_context.get(stage.value, "")
292
+ if stage_context:
293
+ context_parts.append(f"\n# Stage Context\n{stage_context}")
294
+
295
+ # Add documentation config for DOCS and SECURITY stages
296
+ if stage in [Stage.DOCS, Stage.SECURITY]:
297
+ docs_config = self.config.docs
298
+ context_parts.append(f"""
299
+ # Documentation Configuration
300
+ ## Paths
301
+ - Changelog Directory: {docs_config.changelog_dir}
302
+ - Security Audit Directory: {docs_config.security_audit}
303
+ - General Documentation: {docs_config.general}
304
+
305
+ ## What to Update
306
+ - Update Changelog: {"YES" if docs_config.update_changelog else "NO - Skip changelog updates"}
307
+ - Update Security Audit: {"YES" if docs_config.update_security_audit else "NO - Skip security audit docs"}
308
+ - Update General Docs: {"YES" if docs_config.update_general_docs else "NO - Skip general documentation"}
309
+
310
+ Only update documentation types marked as YES above.""")
311
+
312
+ context = "\n".join(context_parts)
313
+
314
+ # Add decision file info at the END of the prompt for emphasis
315
+ # This ensures the AI sees the critical file creation requirement last
316
+ from galangal.core.state import get_decision_info_for_prompt
317
+
318
+ decision_info = get_decision_info_for_prompt(stage)
319
+ decision_suffix = f"\n\n---\n\n{decision_info}" if decision_info else ""
320
+
321
+ return f"{context}\n\n---\n\n{base_prompt}{decision_suffix}"
322
+
323
+ def _get_artifact_context(self, stage: Stage, task_name: str) -> list[str]:
324
+ """
325
+ Get relevant artifact content for inclusion in the stage prompt.
326
+
327
+ Each stage receives context from earlier artifacts based on what it needs:
328
+ - DESIGN.md supersedes PLAN.md when present
329
+ - If DESIGN was skipped, PLAN.md is included instead
330
+ - Only includes artifacts that exist
331
+
332
+ Key inclusion rules:
333
+ - PM: DISCOVERY_LOG.md (Q&A to incorporate into SPEC)
334
+ - DESIGN: SPEC.md only (creates the authoritative implementation plan)
335
+ - DEV+: SPEC.md + DESIGN.md (or PLAN.md if design was skipped)
336
+ - DEV: + DEVELOPMENT.md (resume), ROLLBACK.md (issues to fix)
337
+ - TEST: + TEST_PLAN.md, ROLLBACK.md
338
+ - REVIEW: + QA_REPORT.md, SECURITY_CHECKLIST.md (verify addressed)
339
+
340
+ Args:
341
+ stage: Current stage to get context for.
342
+ task_name: Task name for artifact lookups.
343
+
344
+ Returns:
345
+ List of formatted artifact sections (e.g., "# SPEC.md\\n{content}").
346
+ """
347
+ parts = []
348
+
349
+ # PM stage: only needs discovery Q&A to incorporate into SPEC
350
+ if stage == Stage.PM:
351
+ if artifact_exists("DISCOVERY_LOG.md", task_name):
352
+ parts.append(
353
+ f"\n# DISCOVERY_LOG.md (User Q&A - use these answers!)\n{read_artifact('DISCOVERY_LOG.md', task_name)}"
354
+ )
355
+ return parts
356
+
357
+ # All stages after PM need SPEC (core requirements)
358
+ if artifact_exists("SPEC.md", task_name):
359
+ parts.append(f"\n# SPEC.md\n{read_artifact('SPEC.md', task_name)}")
360
+
361
+ # Stages after DESIGN: include DESIGN.md if it exists, otherwise fall back to PLAN.md
362
+ # (DESIGN.md supersedes PLAN.md, but some task types skip DESIGN)
363
+ if stage not in [Stage.PM, Stage.DESIGN]:
364
+ if artifact_exists("DESIGN.md", task_name):
365
+ parts.append(f"\n# DESIGN.md\n{read_artifact('DESIGN.md', task_name)}")
366
+ elif artifact_exists("DESIGN_SKIP.md", task_name):
367
+ parts.append(
368
+ f"\n# Note: Design stage was skipped\n{read_artifact('DESIGN_SKIP.md', task_name)}"
369
+ )
370
+ # Include PLAN.md as the implementation guide when design was skipped
371
+ if artifact_exists("PLAN.md", task_name):
372
+ parts.append(f"\n# PLAN.md\n{read_artifact('PLAN.md', task_name)}")
373
+
374
+ # DEV stage: progress tracking and rollback issues
375
+ if stage == Stage.DEV:
376
+ if artifact_exists("DEVELOPMENT.md", task_name):
377
+ parts.append(
378
+ f"\n# DEVELOPMENT.md (Previous progress - continue from here)\n{read_artifact('DEVELOPMENT.md', task_name)}"
379
+ )
380
+ if artifact_exists("ROLLBACK.md", task_name):
381
+ parts.append(
382
+ f"\n# ROLLBACK.md (PRIORITY - Fix these issues first!)\n{read_artifact('ROLLBACK.md', task_name)}"
383
+ )
384
+
385
+ # TEST stage: test plan and rollback issues
386
+ if stage == Stage.TEST:
387
+ if artifact_exists("TEST_PLAN.md", task_name):
388
+ parts.append(f"\n# TEST_PLAN.md\n{read_artifact('TEST_PLAN.md', task_name)}")
389
+ if artifact_exists("ROLLBACK.md", task_name):
390
+ parts.append(
391
+ f"\n# ROLLBACK.md (Issues to address in tests)\n{read_artifact('ROLLBACK.md', task_name)}"
392
+ )
393
+
394
+ # CONTRACT stage: needs test plan for context
395
+ if stage == Stage.CONTRACT:
396
+ if artifact_exists("TEST_PLAN.md", task_name):
397
+ parts.append(f"\n# TEST_PLAN.md\n{read_artifact('TEST_PLAN.md', task_name)}")
398
+
399
+ # QA stage: include test summary and test gate results for context
400
+ if stage == Stage.QA:
401
+ if artifact_exists("TEST_SUMMARY.md", task_name):
402
+ parts.append(
403
+ f"\n# TEST_SUMMARY.md (Test results summary)\n{read_artifact('TEST_SUMMARY.md', task_name)}"
404
+ )
405
+ # Include TEST_GATE_RESULTS.md if test gate ran
406
+ if artifact_exists("TEST_GATE_RESULTS.md", task_name):
407
+ test_gate_content = read_artifact("TEST_GATE_RESULTS.md", task_name)
408
+ parts.append(
409
+ f"\n# TEST_GATE_RESULTS.md (Automated tests already verified)\n"
410
+ f"**IMPORTANT:** The following tests have already been run and passed in the TEST_GATE stage. "
411
+ f"Do NOT re-run these tests - focus on exploratory testing, edge cases, and code quality.\n\n"
412
+ f"{test_gate_content}"
413
+ )
414
+
415
+ # SECURITY stage: include test summary for coverage context
416
+ if stage == Stage.SECURITY:
417
+ if artifact_exists("TEST_SUMMARY.md", task_name):
418
+ parts.append(
419
+ f"\n# TEST_SUMMARY.md (Test results)\n{read_artifact('TEST_SUMMARY.md', task_name)}"
420
+ )
421
+
422
+ # REVIEW stage: needs QA and Security reports to verify they were addressed
423
+ if stage == Stage.REVIEW:
424
+ if artifact_exists("TEST_SUMMARY.md", task_name):
425
+ parts.append(
426
+ f"\n# TEST_SUMMARY.md (Test results)\n{read_artifact('TEST_SUMMARY.md', task_name)}"
427
+ )
428
+ if artifact_exists("QA_REPORT.md", task_name):
429
+ parts.append(f"\n# QA_REPORT.md\n{read_artifact('QA_REPORT.md', task_name)}")
430
+ if artifact_exists("SECURITY_CHECKLIST.md", task_name):
431
+ parts.append(
432
+ f"\n# SECURITY_CHECKLIST.md\n{read_artifact('SECURITY_CHECKLIST.md', task_name)}"
433
+ )
434
+
435
+ # DOCS stage: include test summary for documentation context
436
+ if stage == Stage.DOCS:
437
+ if artifact_exists("TEST_SUMMARY.md", task_name):
438
+ parts.append(
439
+ f"\n# TEST_SUMMARY.md (Test results)\n{read_artifact('TEST_SUMMARY.md', task_name)}"
440
+ )
441
+
442
+ return parts
443
+
444
+ def build_minimal_review_prompt(self, state: WorkflowState, backend_name: str) -> str:
445
+ """
446
+ Build a minimal prompt for independent code review.
447
+
448
+ Used for secondary review backends (like Codex) that should give an
449
+ unbiased opinion without being influenced by Claude's interpretations.
450
+
451
+ Only includes:
452
+ - Original task description
453
+ - List of changed files with stats
454
+ - Instructions to read files as needed
455
+
456
+ Does NOT include:
457
+ - Full git diff (too large for big changes)
458
+ - SPEC.md (Claude's interpretation of requirements)
459
+ - QA_REPORT.md, SECURITY_CHECKLIST.md (previous findings)
460
+ - Any other artifacts from the workflow
461
+
462
+ Args:
463
+ state: Workflow state with task info
464
+ backend_name: Backend name for prompt selection (e.g., "codex")
465
+
466
+ Returns:
467
+ Minimal prompt for independent review
468
+ """
469
+ import subprocess
470
+
471
+ base_branch = self.config.pr.base_branch
472
+
473
+ # Get the review prompt (may be backend-specific like review_codex.md)
474
+ review_prompt = self.get_stage_prompt(Stage.REVIEW, backend_name)
475
+
476
+ def run_git(*args: str) -> str:
477
+ """Run a git command and return stdout."""
478
+ try:
479
+ result = subprocess.run(
480
+ ["git", *args],
481
+ cwd=self.project_root,
482
+ capture_output=True,
483
+ text=True,
484
+ timeout=30,
485
+ )
486
+ return result.stdout.strip() if result.returncode == 0 else ""
487
+ except Exception:
488
+ return ""
489
+
490
+ # Get file names for the list
491
+ committed_files = run_git("diff", "--name-only", f"{base_branch}...HEAD")
492
+ staged_files = run_git("diff", "--name-only", "--cached")
493
+ unstaged_files = run_git("diff", "--name-only")
494
+
495
+ # Combine and deduplicate file names
496
+ all_files = set()
497
+ for files in [committed_files, staged_files, unstaged_files]:
498
+ if files:
499
+ all_files.update(files.split("\n"))
500
+ all_files.discard("")
501
+
502
+ files_list = (
503
+ "\n".join(f"- {f}" for f in sorted(all_files)) if all_files else "(No files changed)"
504
+ )
505
+
506
+ # Build minimal context - instruct to READ files, not dump diff
507
+ context = f"""# Independent Code Review
508
+
509
+ ## Task
510
+ {state.task_description}
511
+
512
+ ## Changed Files
513
+ The following files have been modified and need review:
514
+
515
+ {files_list}
516
+
517
+ ## Instructions
518
+ 1. Read each changed file listed above to understand the implementation
519
+ 2. Use `git diff {base_branch}...HEAD -- <file>` to see specific changes if needed
520
+ 3. Review for code quality, bugs, security issues, and best practices
521
+ 4. Provide your independent assessment
522
+
523
+ ---
524
+
525
+ {review_prompt}"""
526
+
527
+ return context
@@ -0,0 +1,34 @@
1
+ # Benchmark Stage
2
+
3
+ You are running performance benchmarks for this task. Focus ONLY on performance validation.
4
+
5
+ ## Scope
6
+
7
+ **DO:**
8
+ - Run performance-specific benchmarks if configured
9
+ - Compare metrics against baseline if available
10
+ - Identify performance regressions
11
+ - Document benchmark results
12
+
13
+ **DO NOT:**
14
+ - Run the full test suite
15
+ - Make code changes
16
+ - Run linting or other QA checks
17
+ - Optimize code (just measure)
18
+
19
+ ## Process
20
+
21
+ 1. **Identify benchmarks** - Find relevant performance tests
22
+ 2. **Run benchmarks** - Execute performance measurements
23
+ 3. **Compare** - Check against baselines if available
24
+ 4. **Document** - Create BENCHMARK_REPORT.md with results
25
+
26
+ ## Output
27
+
28
+ Create `BENCHMARK_REPORT.md` in the task artifacts directory with:
29
+ - Benchmarks executed
30
+ - Performance metrics
31
+ - Comparison to baseline (if available)
32
+ - Any regressions identified
33
+
34
+ If no benchmarks are configured for this project, note that in the report and complete the stage.
@@ -0,0 +1,35 @@
1
+ # Contract Stage
2
+
3
+ You are validating API contracts for this task. Focus ONLY on contract-related work.
4
+
5
+ ## Scope
6
+
7
+ **DO:**
8
+ - Review API endpoint changes (new/modified routes)
9
+ - Validate request/response schemas match documentation
10
+ - Check OpenAPI/Swagger specs are updated if applicable
11
+ - Verify breaking changes are documented
12
+ - Run contract-specific tests if available
13
+
14
+ **DO NOT:**
15
+ - Run the full test suite
16
+ - Make code changes unrelated to API contracts
17
+ - Run linting or other QA checks
18
+ - Modify business logic
19
+
20
+ ## Process
21
+
22
+ 1. **Identify API changes** - Find new/modified endpoints in this task
23
+ 2. **Review contracts** - Check request/response shapes are correct
24
+ 3. **Validate specs** - Ensure OpenAPI/Swagger is updated if present
25
+ 4. **Document** - Create CONTRACT_REPORT.md with findings
26
+
27
+ ## Output
28
+
29
+ Create `CONTRACT_REPORT.md` in the task artifacts directory with:
30
+ - List of API endpoints reviewed
31
+ - Schema validation results
32
+ - Breaking changes identified (if any)
33
+ - Spec file updates needed
34
+
35
+ If no API changes exist for this task, note that in the report and complete the stage.
@@ -0,0 +1,54 @@
1
+ # DESIGN Stage - Architecture Design
2
+
3
+ You are a Software Architect designing the implementation for a feature. Create a detailed technical design document.
4
+
5
+ ## Your Output
6
+
7
+ Create DESIGN.md in the task's artifacts directory with these sections:
8
+
9
+ ```markdown
10
+ # Technical Design: [Task Title]
11
+
12
+ ## Architecture Overview
13
+ [High-level description of the approach]
14
+ [How this fits into the existing system]
15
+
16
+ ## Data Model
17
+ [Any new or modified data structures]
18
+ [Database schema changes if applicable]
19
+
20
+ ## API Impact
21
+ [New or modified endpoints]
22
+ [Request/response formats]
23
+
24
+ ## Sequence Diagram
25
+ [Use mermaid or text-based diagram showing the flow]
26
+
27
+ ## Edge Cases
28
+ - [Edge case 1 and how it's handled]
29
+ - [Edge case 2 and how it's handled]
30
+
31
+ ## Migration Plan
32
+ [How to deploy this without breaking existing functionality]
33
+ [Rollback strategy if needed]
34
+ ```
35
+
36
+ ## Process
37
+
38
+ 1. Read SPEC.md and PLAN.md from context
39
+ 2. Analyze the codebase to understand:
40
+ - Current architecture
41
+ - Integration points
42
+ - Patterns to follow
43
+ 3. Design the solution considering:
44
+ - Scalability
45
+ - Maintainability
46
+ - Backward compatibility
47
+ 4. Write DESIGN.md
48
+
49
+ ## Important Rules
50
+
51
+ - Consider all edge cases
52
+ - Plan for failure scenarios
53
+ - Keep the design focused on the task scope
54
+ - Do NOT implement - only design