@hustle-together/api-dev-tools 3.12.16 → 4.5.3

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 (180) hide show
  1. package/.claude/adr-requests/.gitkeep +10 -0
  2. package/.claude/agents/adr-researcher.md +109 -0
  3. package/.claude/agents/visual-analyzer.md +183 -0
  4. package/.claude/api-dev-state.json +10 -0
  5. package/.claude/documentation-audit.json +114 -0
  6. package/.claude/registry.json +289 -0
  7. package/.claude/settings.json +45 -1
  8. package/.claude/settings.local.json +1 -7
  9. package/.claude/workflow-logs/None.json +49 -0
  10. package/.claude/workflow-logs/session-20251230-143727.json +106 -0
  11. package/.skills/adr-deep-research/SKILL.md +351 -0
  12. package/.skills/api-create/SKILL.md +34 -20
  13. package/.skills/api-research/SKILL.md +130 -0
  14. package/.skills/docs-update/SKILL.md +205 -0
  15. package/.skills/hustle-brand/SKILL.md +368 -0
  16. package/.skills/hustle-build/SKILL.md +365 -38
  17. package/.skills/parallel-spawn/SKILL.md +212 -0
  18. package/.skills/ralph-continue/SKILL.md +151 -0
  19. package/.skills/ralph-loop/SKILL.md +341 -0
  20. package/.skills/ralph-status/SKILL.md +87 -0
  21. package/.skills/refactor/SKILL.md +59 -0
  22. package/.skills/shadcn/SKILL.md +522 -0
  23. package/.skills/test-all/SKILL.md +210 -0
  24. package/.skills/test-builds/SKILL.md +208 -0
  25. package/.skills/test-debug/SKILL.md +212 -0
  26. package/.skills/test-e2e/SKILL.md +168 -0
  27. package/.skills/test-review/SKILL.md +707 -0
  28. package/.skills/test-unit/SKILL.md +143 -0
  29. package/.skills/test-visual/SKILL.md +301 -0
  30. package/.skills/token-report/SKILL.md +132 -0
  31. package/CHANGELOG.md +488 -0
  32. package/README.md +346 -53
  33. package/bin/cli.js +359 -123
  34. package/hooks/__pycache__/api-workflow-check.cpython-314.pyc +0 -0
  35. package/hooks/__pycache__/auto-answer.cpython-314.pyc +0 -0
  36. package/hooks/__pycache__/cache-research.cpython-314.pyc +0 -0
  37. package/hooks/__pycache__/check-api-routes.cpython-314.pyc +0 -0
  38. package/hooks/__pycache__/check-playwright-setup.cpython-314.pyc +0 -0
  39. package/hooks/__pycache__/check-storybook-setup.cpython-314.pyc +0 -0
  40. package/hooks/__pycache__/check-update.cpython-314.pyc +0 -0
  41. package/hooks/__pycache__/completion-promise-detector.cpython-314.pyc +0 -0
  42. package/hooks/__pycache__/context-capacity-warning.cpython-314.pyc +0 -0
  43. package/hooks/__pycache__/detect-interruption.cpython-314.pyc +0 -0
  44. package/hooks/__pycache__/docs-update-check.cpython-314.pyc +0 -0
  45. package/hooks/__pycache__/enforce-a11y-audit.cpython-314.pyc +0 -0
  46. package/hooks/__pycache__/enforce-brand-guide.cpython-314.pyc +0 -0
  47. package/hooks/__pycache__/enforce-component-type-confirm.cpython-314.pyc +0 -0
  48. package/hooks/__pycache__/enforce-deep-research.cpython-314.pyc +0 -0
  49. package/hooks/__pycache__/enforce-disambiguation.cpython-314.pyc +0 -0
  50. package/hooks/__pycache__/enforce-documentation.cpython-314.pyc +0 -0
  51. package/hooks/__pycache__/enforce-dry-run.cpython-314.pyc +0 -0
  52. package/hooks/__pycache__/enforce-environment.cpython-314.pyc +0 -0
  53. package/hooks/__pycache__/enforce-external-research.cpython-314.pyc +0 -0
  54. package/hooks/__pycache__/enforce-freshness.cpython-314.pyc +0 -0
  55. package/hooks/__pycache__/enforce-interview.cpython-314.pyc +0 -0
  56. package/hooks/__pycache__/enforce-page-components.cpython-314.pyc +0 -0
  57. package/hooks/__pycache__/enforce-page-data-schema.cpython-314.pyc +0 -0
  58. package/hooks/__pycache__/enforce-questions-sourced.cpython-314.pyc +0 -0
  59. package/hooks/__pycache__/enforce-refactor.cpython-314.pyc +0 -0
  60. package/hooks/__pycache__/enforce-research.cpython-314.pyc +0 -0
  61. package/hooks/__pycache__/enforce-schema-from-interview.cpython-314.pyc +0 -0
  62. package/hooks/__pycache__/enforce-schema.cpython-314.pyc +0 -0
  63. package/hooks/__pycache__/enforce-scope.cpython-314.pyc +0 -0
  64. package/hooks/__pycache__/enforce-tdd-red.cpython-314.pyc +0 -0
  65. package/hooks/__pycache__/enforce-ui-disambiguation.cpython-314.pyc +0 -0
  66. package/hooks/__pycache__/enforce-ui-interview.cpython-314.pyc +0 -0
  67. package/hooks/__pycache__/enforce-verify.cpython-314.pyc +0 -0
  68. package/hooks/__pycache__/generate-adr-options.cpython-314.pyc +0 -0
  69. package/hooks/__pycache__/generate-manifest-entry.cpython-314.pyc +0 -0
  70. package/hooks/__pycache__/hook_utils.cpython-314.pyc +0 -0
  71. package/hooks/__pycache__/notify-input-needed.cpython-314.pyc +0 -0
  72. package/hooks/__pycache__/notify-phase-complete.cpython-314.pyc +0 -0
  73. package/hooks/__pycache__/ntfy-on-question.cpython-314.pyc +0 -0
  74. package/hooks/__pycache__/orchestrator-completion.cpython-314.pyc +0 -0
  75. package/hooks/__pycache__/orchestrator-handoff.cpython-314.pyc +0 -0
  76. package/hooks/__pycache__/orchestrator-session-startup.cpython-314.pyc +0 -0
  77. package/hooks/__pycache__/parallel-orchestrator.cpython-314.pyc +0 -0
  78. package/hooks/__pycache__/periodic-reground.cpython-314.pyc +0 -0
  79. package/hooks/__pycache__/project-document-prompt.cpython-314.pyc +0 -0
  80. package/hooks/__pycache__/remote-question-proxy.cpython-314.pyc +0 -0
  81. package/hooks/__pycache__/remote-question-server.cpython-314.pyc +0 -0
  82. package/hooks/__pycache__/run-code-review.cpython-314.pyc +0 -0
  83. package/hooks/__pycache__/run-visual-qa.cpython-314.pyc +0 -0
  84. package/hooks/__pycache__/session-logger.cpython-314.pyc +0 -0
  85. package/hooks/__pycache__/session-startup.cpython-314.pyc +0 -0
  86. package/hooks/__pycache__/track-scope-coverage.cpython-314.pyc +0 -0
  87. package/hooks/__pycache__/track-token-usage.cpython-314.pyc +0 -0
  88. package/hooks/__pycache__/track-tool-use.cpython-314.pyc +0 -0
  89. package/hooks/__pycache__/update-adr-decision.cpython-314.pyc +0 -0
  90. package/hooks/__pycache__/update-api-showcase.cpython-314.pyc +0 -0
  91. package/hooks/__pycache__/update-registry.cpython-314.pyc +0 -0
  92. package/hooks/__pycache__/update-ui-showcase.cpython-314.pyc +0 -0
  93. package/hooks/__pycache__/verify-after-green.cpython-314.pyc +0 -0
  94. package/hooks/__pycache__/verify-implementation.cpython-314.pyc +0 -0
  95. package/hooks/api-workflow-check.py +34 -0
  96. package/hooks/auto-answer.py +97 -20
  97. package/{.claude/hooks → hooks}/completion-promise-detector.py +0 -0
  98. package/{.claude/hooks → hooks}/context-capacity-warning.py +0 -0
  99. package/{.claude/hooks → hooks}/docs-update-check.py +0 -0
  100. package/{.claude/hooks → hooks}/enforce-dry-run.py +0 -0
  101. package/hooks/enforce-external-research.py +25 -0
  102. package/hooks/enforce-interview.py +20 -0
  103. package/{.claude/hooks → hooks}/generate-adr-options.py +0 -0
  104. package/{.claude/hooks → hooks}/hook_utils.py +0 -0
  105. package/hooks/ntfy-on-question.py +15 -2
  106. package/hooks/orchestrator-handoff.py +81 -3
  107. package/{.claude/hooks → hooks}/parallel-orchestrator.py +0 -0
  108. package/hooks/periodic-reground.py +40 -0
  109. package/{.claude/hooks → hooks}/remote-question-server.py +0 -0
  110. package/hooks/run-code-review.py +176 -29
  111. package/{.claude/hooks → hooks}/run-visual-qa.py +0 -0
  112. package/hooks/session-logger.py +27 -1
  113. package/hooks/session-startup.py +113 -0
  114. package/{.claude/hooks → hooks}/update-adr-decision.py +0 -0
  115. package/package.json +1 -1
  116. package/templates/.skills/hustle-interview/SKILL.md +174 -0
  117. package/templates/adr-viewer/_components/ADRViewer.tsx +326 -0
  118. package/templates/api-dev-state.json +33 -1
  119. package/templates/brand-page/page.tsx +645 -0
  120. package/templates/component/Component.visual.spec.ts +30 -24
  121. package/templates/eslint-plugin-zod-schema/index.js +446 -0
  122. package/templates/eslint-plugin-zod-schema/package.json +26 -0
  123. package/templates/github-workflows/security.yml +274 -0
  124. package/templates/hustle-build-defaults.json +53 -1
  125. package/templates/page/page.e2e.test.ts +30 -26
  126. package/templates/performance-budgets.json +63 -5
  127. package/templates/registry.json +279 -3
  128. package/templates/review-dashboard/page.tsx +510 -0
  129. package/templates/settings.json +74 -7
  130. package/templates/ui-showcase/_components/UIShowcase.tsx +47 -0
  131. package/templates/ui-showcase/_components/VisualTestingDashboard.tsx +579 -0
  132. package/.claude/commands/hustle-combine.md +0 -1089
  133. package/.claude/commands/hustle-ui-create-page.md +0 -1078
  134. package/.claude/commands/hustle-ui-create.md +0 -1058
  135. package/.claude/hooks/auto-answer.py +0 -305
  136. package/.claude/hooks/cache-research.py +0 -337
  137. package/.claude/hooks/check-api-routes.py +0 -168
  138. package/.claude/hooks/check-playwright-setup.py +0 -103
  139. package/.claude/hooks/check-storybook-setup.py +0 -81
  140. package/.claude/hooks/check-update.py +0 -132
  141. package/.claude/hooks/detect-interruption.py +0 -165
  142. package/.claude/hooks/enforce-a11y-audit.py +0 -202
  143. package/.claude/hooks/enforce-brand-guide.py +0 -241
  144. package/.claude/hooks/enforce-component-type-confirm.py +0 -97
  145. package/.claude/hooks/enforce-freshness.py +0 -184
  146. package/.claude/hooks/enforce-page-components.py +0 -186
  147. package/.claude/hooks/enforce-page-data-schema.py +0 -155
  148. package/.claude/hooks/enforce-questions-sourced.py +0 -146
  149. package/.claude/hooks/enforce-schema-from-interview.py +0 -248
  150. package/.claude/hooks/enforce-ui-disambiguation.py +0 -108
  151. package/.claude/hooks/enforce-ui-interview.py +0 -130
  152. package/.claude/hooks/generate-manifest-entry.py +0 -1161
  153. package/.claude/hooks/lib/__init__.py +0 -1
  154. package/.claude/hooks/lib/greptile.py +0 -355
  155. package/.claude/hooks/lib/ntfy.py +0 -209
  156. package/.claude/hooks/notify-input-needed.py +0 -73
  157. package/.claude/hooks/notify-phase-complete.py +0 -90
  158. package/.claude/hooks/ntfy-on-question.py +0 -240
  159. package/.claude/hooks/orchestrator-completion.py +0 -313
  160. package/.claude/hooks/orchestrator-handoff.py +0 -267
  161. package/.claude/hooks/orchestrator-session-startup.py +0 -146
  162. package/.claude/hooks/run-code-review.py +0 -393
  163. package/.claude/hooks/session-logger.py +0 -323
  164. package/.claude/hooks/test-orchestrator-reground.py +0 -248
  165. package/.claude/hooks/track-scope-coverage.py +0 -220
  166. package/.claude/hooks/track-token-usage.py +0 -121
  167. package/.claude/hooks/update-api-showcase.py +0 -161
  168. package/.claude/hooks/update-registry.py +0 -352
  169. package/.claude/hooks/update-ui-showcase.py +0 -224
  170. package/.claude/test-auto-answer-bot.py +0 -183
  171. package/.claude/test-completion-detector.py +0 -263
  172. package/.claude/test-orchestrator-state.json +0 -20
  173. package/.claude/test-orchestrator.sh +0 -271
  174. /package/{.claude/commands → commands}/hustle-build.md +0 -0
  175. /package/{.claude/hooks → hooks}/lib/__pycache__/__init__.cpython-314.pyc +0 -0
  176. /package/{.claude/hooks → hooks}/lib/__pycache__/greptile.cpython-314.pyc +0 -0
  177. /package/{.claude/hooks → hooks}/lib/__pycache__/ntfy.cpython-314.pyc +0 -0
  178. /package/{.claude/hooks → hooks}/project-document-prompt.py +0 -0
  179. /package/{.claude/hooks → hooks}/remote-question-proxy.py +0 -0
  180. /package/{.claude/hooks → hooks}/update-testing-checklist.py +0 -0
@@ -1,393 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Phase 11: AI Code Review Hook (Ralph Wiggum Loop Pattern)
4
-
5
- Triggers Greptile AI code review and LOOPS until all issues are fixed.
6
- This ensures code quality before proceeding to refactor phase.
7
-
8
- Hook Type: PostToolUse (triggers after tests pass)
9
-
10
- Ralph Wiggum Pattern:
11
- 1. Run Greptile review
12
- 2. If issues found → inject context for agent to fix
13
- 3. Agent fixes issues
14
- 4. Tests re-run → hook triggers again
15
- 5. Re-review with Greptile
16
- 6. Loop until clean OR max iterations
17
- 7. Emit <promise>REVIEW_CLEAN</promise>
18
-
19
- Environment Variables:
20
- GREPTILE_API_KEY: Your Greptile API key (get from https://app.greptile.com)
21
- GITHUB_TOKEN: GitHub Personal Access Token with repo access
22
- CODE_REVIEW_ENABLED: Set to 'true' to enable (default: true)
23
- CODE_REVIEW_MAX_ITERATIONS: Max review cycles (default: 5)
24
-
25
- Version: 2.0.0
26
- """
27
- import os
28
- import sys
29
- import json
30
- import subprocess
31
- from pathlib import Path
32
- from datetime import datetime
33
-
34
- # Add lib directory to path for imports
35
- HOOK_DIR = Path(__file__).parent
36
- LIB_DIR = HOOK_DIR / "lib"
37
- sys.path.insert(0, str(LIB_DIR))
38
-
39
- try:
40
- from greptile import (
41
- is_configured,
42
- review_changes,
43
- get_review_summary,
44
- format_review_for_display,
45
- get_status
46
- )
47
- GREPTILE_AVAILABLE = True
48
- except ImportError:
49
- GREPTILE_AVAILABLE = False
50
-
51
- # State file for tracking review loops
52
- REVIEW_STATE_FILE = ".claude/code-review-state.json"
53
- MAX_ITERATIONS = int(os.environ.get("CODE_REVIEW_MAX_ITERATIONS", "5"))
54
-
55
-
56
- def get_git_diff() -> tuple:
57
- """Get the current git diff and changed files."""
58
- try:
59
- # Get list of changed files
60
- files_result = subprocess.run(
61
- ["git", "diff", "--name-only", "HEAD~1"],
62
- capture_output=True,
63
- text=True,
64
- timeout=30
65
- )
66
- files_changed = files_result.stdout.strip().split("\n") if files_result.stdout else []
67
-
68
- # Get full diff
69
- diff_result = subprocess.run(
70
- ["git", "diff", "HEAD~1"],
71
- capture_output=True,
72
- text=True,
73
- timeout=30
74
- )
75
- diff_content = diff_result.stdout
76
-
77
- return files_changed, diff_content
78
- except (subprocess.TimeoutExpired, FileNotFoundError):
79
- return [], ""
80
-
81
-
82
- def get_repo_info() -> tuple:
83
- """Get repository owner and name from git remote."""
84
- try:
85
- result = subprocess.run(
86
- ["git", "remote", "get-url", "origin"],
87
- capture_output=True,
88
- text=True,
89
- timeout=10
90
- )
91
- if result.returncode == 0:
92
- url = result.stdout.strip()
93
- # Parse GitHub URL (handles both HTTPS and SSH)
94
- if "github.com" in url:
95
- if url.startswith("git@"):
96
- # SSH format: git@github.com:owner/repo.git
97
- parts = url.split(":")[-1].replace(".git", "").split("/")
98
- else:
99
- # HTTPS format: https://github.com/owner/repo.git
100
- parts = url.replace(".git", "").split("/")[-2:]
101
-
102
- if len(parts) >= 2:
103
- return parts[-2], parts[-1]
104
- except (subprocess.TimeoutExpired, FileNotFoundError):
105
- pass
106
- return None, None
107
-
108
-
109
- def load_review_state() -> dict:
110
- """Load code review loop state."""
111
- state_file = Path.cwd() / REVIEW_STATE_FILE
112
- if state_file.exists():
113
- try:
114
- return json.loads(state_file.read_text())
115
- except (json.JSONDecodeError, IOError):
116
- pass
117
- return {
118
- "iteration": 0,
119
- "issues_found": [],
120
- "status": "pending",
121
- "started_at": None,
122
- "last_review_at": None
123
- }
124
-
125
-
126
- def save_review_state(state: dict):
127
- """Save code review loop state."""
128
- state_file = Path.cwd() / REVIEW_STATE_FILE
129
- state_file.parent.mkdir(parents=True, exist_ok=True)
130
- try:
131
- state_file.write_text(json.dumps(state, indent=2))
132
- except IOError:
133
- pass
134
-
135
-
136
- def clear_review_state():
137
- """Clear review state after successful completion."""
138
- state_file = Path.cwd() / REVIEW_STATE_FILE
139
- if state_file.exists():
140
- try:
141
- state_file.unlink()
142
- except IOError:
143
- pass
144
-
145
-
146
- def load_workflow_state() -> dict:
147
- """Load current workflow state."""
148
- state_file = Path.cwd() / ".claude" / "api-dev-state.json"
149
- if state_file.exists():
150
- try:
151
- return json.loads(state_file.read_text())
152
- except (json.JSONDecodeError, IOError):
153
- pass
154
- return {}
155
-
156
-
157
- def update_workflow_state_with_review(review_summary: dict, iteration: int):
158
- """Update workflow state file with code review results."""
159
- state_file = Path.cwd() / ".claude" / "api-dev-state.json"
160
- state = load_workflow_state()
161
-
162
- # Add or update code_review phase
163
- if "phases" not in state:
164
- state["phases"] = {}
165
-
166
- state["phases"]["code_review"] = {
167
- "status": "in_progress" if review_summary.get("issue_count", 0) > 0 else "complete",
168
- "iteration": iteration,
169
- "score": review_summary.get("score", 0),
170
- "issues_found": review_summary.get("issue_count", 0),
171
- "suggestions": review_summary.get("suggestion_count", 0),
172
- "reviewed_at": datetime.now().isoformat()
173
- }
174
-
175
- try:
176
- state_file.write_text(json.dumps(state, indent=2))
177
- except IOError:
178
- pass
179
-
180
-
181
- def should_run_review(hook_input: dict) -> bool:
182
- """Determine if code review should run based on hook context."""
183
- # Check if code review is enabled
184
- if os.environ.get("CODE_REVIEW_ENABLED", "true").lower() == "false":
185
- return False
186
-
187
- tool_name = hook_input.get("tool_name", "")
188
-
189
- # Run after tests pass (Phase 9/10)
190
- if tool_name == "Bash":
191
- tool_input = hook_input.get("tool_input", {})
192
- command = tool_input.get("command", "")
193
- tool_result = hook_input.get("tool_result", {})
194
- stdout = tool_result.get("stdout", "")
195
-
196
- # Check if tests just passed
197
- if ("pnpm test" in command or "npm test" in command or "vitest" in command):
198
- # Only run if tests passed (look for success indicators)
199
- if "pass" in stdout.lower() or "✓" in stdout or "PASS" in stdout:
200
- return True
201
-
202
- # Also run if verify-after-green hook triggered
203
- if "verify" in tool_name.lower():
204
- return True
205
-
206
- return False
207
-
208
-
209
- def format_issues_for_context(issues: list) -> str:
210
- """Format issues as context for the agent to fix."""
211
- if not issues:
212
- return ""
213
-
214
- lines = [
215
- "",
216
- "=" * 60,
217
- "CODE REVIEW ISSUES TO FIX (Ralph Wiggum Loop)",
218
- "=" * 60,
219
- "",
220
- "The following issues were found by Greptile code review.",
221
- "Please fix ALL issues, then run tests again.",
222
- "The review will re-run automatically after tests pass.",
223
- "",
224
- "ISSUES:",
225
- ]
226
-
227
- for i, issue in enumerate(issues, 1):
228
- lines.append(f" {i}. {issue}")
229
-
230
- lines.extend([
231
- "",
232
- "After fixing all issues, run: pnpm test",
233
- "Review will loop until all issues are resolved.",
234
- "=" * 60,
235
- ""
236
- ])
237
-
238
- return "\n".join(lines)
239
-
240
-
241
- def main():
242
- """Main hook entry point with Ralph Wiggum loop pattern."""
243
- # Read hook input
244
- try:
245
- hook_input = json.loads(sys.stdin.read())
246
- except json.JSONDecodeError:
247
- hook_input = {}
248
-
249
- # Check if we should run
250
- if not should_run_review(hook_input):
251
- # Pass through - no review needed
252
- print(json.dumps({"continue": True}))
253
- return
254
-
255
- # Check if Greptile is available and configured
256
- if not GREPTILE_AVAILABLE:
257
- print(json.dumps({
258
- "continue": True,
259
- "message": "Greptile library not found - skipping code review"
260
- }))
261
- return
262
-
263
- if not is_configured():
264
- status = get_status()
265
- print(json.dumps({
266
- "continue": True,
267
- "message": f"Phase 11 Code Review skipped: {status['message']}"
268
- }))
269
- return
270
-
271
- # Load current review loop state
272
- review_state = load_review_state()
273
-
274
- # Increment iteration
275
- review_state["iteration"] += 1
276
- iteration = review_state["iteration"]
277
-
278
- # Check max iterations
279
- if iteration > MAX_ITERATIONS:
280
- print(json.dumps({
281
- "continue": True,
282
- "message": f"Code review max iterations ({MAX_ITERATIONS}) reached. Proceeding with warnings.\n\n<promise>REVIEW_CLEAN</promise>"
283
- }))
284
- clear_review_state()
285
- return
286
-
287
- # Track start time on first iteration
288
- if iteration == 1:
289
- review_state["started_at"] = datetime.now().isoformat()
290
-
291
- review_state["last_review_at"] = datetime.now().isoformat()
292
-
293
- # Get repository info
294
- repo_owner, repo_name = get_repo_info()
295
- if not repo_owner or not repo_name:
296
- print(json.dumps({
297
- "continue": True,
298
- "message": "Could not determine repository info - skipping code review"
299
- }))
300
- return
301
-
302
- # Get changes to review
303
- files_changed, diff_content = get_git_diff()
304
- if not diff_content:
305
- print(json.dumps({
306
- "continue": True,
307
- "message": "No changes detected - code review complete.\n\n<promise>REVIEW_CLEAN</promise>"
308
- }))
309
- clear_review_state()
310
- return
311
-
312
- # Run the Greptile review
313
- result = review_changes(
314
- repo_owner=repo_owner,
315
- repo_name=repo_name,
316
- files_changed=files_changed,
317
- diff_content=diff_content
318
- )
319
-
320
- if not result:
321
- print(json.dumps({
322
- "continue": True,
323
- "message": "Greptile review failed - check API credentials"
324
- }))
325
- return
326
-
327
- # Parse results
328
- summary = get_review_summary(result)
329
- update_workflow_state_with_review(summary, iteration)
330
-
331
- # Format for display
332
- display_output = format_review_for_display(summary)
333
-
334
- # Check if issues found
335
- issue_count = summary.get("issue_count", 0)
336
- issues = summary.get("issues", [])
337
-
338
- if issue_count == 0:
339
- # All clean! Emit promise and proceed
340
- review_state["status"] = "complete"
341
- save_review_state(review_state)
342
-
343
- output = f"""
344
- {display_output}
345
-
346
- ================================================================================
347
- REVIEW LOOP COMPLETE (Iteration {iteration}/{MAX_ITERATIONS})
348
- ================================================================================
349
- All code review checks passed!
350
- Proceeding to next phase.
351
-
352
- <promise>REVIEW_CLEAN</promise>
353
- """
354
- print(json.dumps({
355
- "continue": True,
356
- "message": output
357
- }))
358
- clear_review_state()
359
- return
360
-
361
- # Issues found - save state and block for fixes
362
- review_state["status"] = "needs_fixing"
363
- review_state["issues_found"] = issues
364
- save_review_state(review_state)
365
-
366
- # Format issues as context for agent
367
- issues_context = format_issues_for_context(issues)
368
-
369
- output = f"""
370
- {display_output}
371
-
372
- ================================================================================
373
- REVIEW LOOP - ITERATION {iteration}/{MAX_ITERATIONS}
374
- ================================================================================
375
- {issue_count} issue(s) found. Fix them and run tests again.
376
- Review will re-run automatically after tests pass.
377
- {issues_context}
378
- """
379
-
380
- # Block workflow - agent needs to fix issues
381
- print(json.dumps({
382
- "continue": False, # Block until fixed
383
- "message": output,
384
- "review_score": summary.get("score", 0),
385
- "issues_count": issue_count,
386
- "iteration": iteration,
387
- "action_required": True,
388
- "next_action": "Fix the issues above, then run tests again"
389
- }))
390
-
391
-
392
- if __name__ == "__main__":
393
- main()