@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.
- package/.claude/adr-requests/.gitkeep +10 -0
- package/.claude/agents/adr-researcher.md +109 -0
- package/.claude/agents/visual-analyzer.md +183 -0
- package/.claude/api-dev-state.json +10 -0
- package/.claude/documentation-audit.json +114 -0
- package/.claude/registry.json +289 -0
- package/.claude/settings.json +45 -1
- package/.claude/settings.local.json +1 -7
- package/.claude/workflow-logs/None.json +49 -0
- package/.claude/workflow-logs/session-20251230-143727.json +106 -0
- package/.skills/adr-deep-research/SKILL.md +351 -0
- package/.skills/api-create/SKILL.md +34 -20
- package/.skills/api-research/SKILL.md +130 -0
- package/.skills/docs-update/SKILL.md +205 -0
- package/.skills/hustle-brand/SKILL.md +368 -0
- package/.skills/hustle-build/SKILL.md +365 -38
- package/.skills/parallel-spawn/SKILL.md +212 -0
- package/.skills/ralph-continue/SKILL.md +151 -0
- package/.skills/ralph-loop/SKILL.md +341 -0
- package/.skills/ralph-status/SKILL.md +87 -0
- package/.skills/refactor/SKILL.md +59 -0
- package/.skills/shadcn/SKILL.md +522 -0
- package/.skills/test-all/SKILL.md +210 -0
- package/.skills/test-builds/SKILL.md +208 -0
- package/.skills/test-debug/SKILL.md +212 -0
- package/.skills/test-e2e/SKILL.md +168 -0
- package/.skills/test-review/SKILL.md +707 -0
- package/.skills/test-unit/SKILL.md +143 -0
- package/.skills/test-visual/SKILL.md +301 -0
- package/.skills/token-report/SKILL.md +132 -0
- package/CHANGELOG.md +488 -0
- package/README.md +346 -53
- package/bin/cli.js +359 -123
- package/hooks/__pycache__/api-workflow-check.cpython-314.pyc +0 -0
- package/hooks/__pycache__/auto-answer.cpython-314.pyc +0 -0
- package/hooks/__pycache__/cache-research.cpython-314.pyc +0 -0
- package/hooks/__pycache__/check-api-routes.cpython-314.pyc +0 -0
- package/hooks/__pycache__/check-playwright-setup.cpython-314.pyc +0 -0
- package/hooks/__pycache__/check-storybook-setup.cpython-314.pyc +0 -0
- package/hooks/__pycache__/check-update.cpython-314.pyc +0 -0
- package/hooks/__pycache__/completion-promise-detector.cpython-314.pyc +0 -0
- package/hooks/__pycache__/context-capacity-warning.cpython-314.pyc +0 -0
- package/hooks/__pycache__/detect-interruption.cpython-314.pyc +0 -0
- package/hooks/__pycache__/docs-update-check.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-a11y-audit.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-brand-guide.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-component-type-confirm.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-deep-research.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-disambiguation.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-documentation.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-dry-run.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-environment.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-external-research.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-freshness.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-interview.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-page-components.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-page-data-schema.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-questions-sourced.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-refactor.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-research.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-schema-from-interview.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-schema.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-scope.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-tdd-red.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-ui-disambiguation.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-ui-interview.cpython-314.pyc +0 -0
- package/hooks/__pycache__/enforce-verify.cpython-314.pyc +0 -0
- package/hooks/__pycache__/generate-adr-options.cpython-314.pyc +0 -0
- package/hooks/__pycache__/generate-manifest-entry.cpython-314.pyc +0 -0
- package/hooks/__pycache__/hook_utils.cpython-314.pyc +0 -0
- package/hooks/__pycache__/notify-input-needed.cpython-314.pyc +0 -0
- package/hooks/__pycache__/notify-phase-complete.cpython-314.pyc +0 -0
- package/hooks/__pycache__/ntfy-on-question.cpython-314.pyc +0 -0
- package/hooks/__pycache__/orchestrator-completion.cpython-314.pyc +0 -0
- package/hooks/__pycache__/orchestrator-handoff.cpython-314.pyc +0 -0
- package/hooks/__pycache__/orchestrator-session-startup.cpython-314.pyc +0 -0
- package/hooks/__pycache__/parallel-orchestrator.cpython-314.pyc +0 -0
- package/hooks/__pycache__/periodic-reground.cpython-314.pyc +0 -0
- package/hooks/__pycache__/project-document-prompt.cpython-314.pyc +0 -0
- package/hooks/__pycache__/remote-question-proxy.cpython-314.pyc +0 -0
- package/hooks/__pycache__/remote-question-server.cpython-314.pyc +0 -0
- package/hooks/__pycache__/run-code-review.cpython-314.pyc +0 -0
- package/hooks/__pycache__/run-visual-qa.cpython-314.pyc +0 -0
- package/hooks/__pycache__/session-logger.cpython-314.pyc +0 -0
- package/hooks/__pycache__/session-startup.cpython-314.pyc +0 -0
- package/hooks/__pycache__/track-scope-coverage.cpython-314.pyc +0 -0
- package/hooks/__pycache__/track-token-usage.cpython-314.pyc +0 -0
- package/hooks/__pycache__/track-tool-use.cpython-314.pyc +0 -0
- package/hooks/__pycache__/update-adr-decision.cpython-314.pyc +0 -0
- package/hooks/__pycache__/update-api-showcase.cpython-314.pyc +0 -0
- package/hooks/__pycache__/update-registry.cpython-314.pyc +0 -0
- package/hooks/__pycache__/update-ui-showcase.cpython-314.pyc +0 -0
- package/hooks/__pycache__/verify-after-green.cpython-314.pyc +0 -0
- package/hooks/__pycache__/verify-implementation.cpython-314.pyc +0 -0
- package/hooks/api-workflow-check.py +34 -0
- package/hooks/auto-answer.py +97 -20
- package/{.claude/hooks → hooks}/completion-promise-detector.py +0 -0
- package/{.claude/hooks → hooks}/context-capacity-warning.py +0 -0
- package/{.claude/hooks → hooks}/docs-update-check.py +0 -0
- package/{.claude/hooks → hooks}/enforce-dry-run.py +0 -0
- package/hooks/enforce-external-research.py +25 -0
- package/hooks/enforce-interview.py +20 -0
- package/{.claude/hooks → hooks}/generate-adr-options.py +0 -0
- package/{.claude/hooks → hooks}/hook_utils.py +0 -0
- package/hooks/ntfy-on-question.py +15 -2
- package/hooks/orchestrator-handoff.py +81 -3
- package/{.claude/hooks → hooks}/parallel-orchestrator.py +0 -0
- package/hooks/periodic-reground.py +40 -0
- package/{.claude/hooks → hooks}/remote-question-server.py +0 -0
- package/hooks/run-code-review.py +176 -29
- package/{.claude/hooks → hooks}/run-visual-qa.py +0 -0
- package/hooks/session-logger.py +27 -1
- package/hooks/session-startup.py +113 -0
- package/{.claude/hooks → hooks}/update-adr-decision.py +0 -0
- package/package.json +1 -1
- package/templates/.skills/hustle-interview/SKILL.md +174 -0
- package/templates/adr-viewer/_components/ADRViewer.tsx +326 -0
- package/templates/api-dev-state.json +33 -1
- package/templates/brand-page/page.tsx +645 -0
- package/templates/component/Component.visual.spec.ts +30 -24
- package/templates/eslint-plugin-zod-schema/index.js +446 -0
- package/templates/eslint-plugin-zod-schema/package.json +26 -0
- package/templates/github-workflows/security.yml +274 -0
- package/templates/hustle-build-defaults.json +53 -1
- package/templates/page/page.e2e.test.ts +30 -26
- package/templates/performance-budgets.json +63 -5
- package/templates/registry.json +279 -3
- package/templates/review-dashboard/page.tsx +510 -0
- package/templates/settings.json +74 -7
- package/templates/ui-showcase/_components/UIShowcase.tsx +47 -0
- package/templates/ui-showcase/_components/VisualTestingDashboard.tsx +579 -0
- package/.claude/commands/hustle-combine.md +0 -1089
- package/.claude/commands/hustle-ui-create-page.md +0 -1078
- package/.claude/commands/hustle-ui-create.md +0 -1058
- package/.claude/hooks/auto-answer.py +0 -305
- package/.claude/hooks/cache-research.py +0 -337
- package/.claude/hooks/check-api-routes.py +0 -168
- package/.claude/hooks/check-playwright-setup.py +0 -103
- package/.claude/hooks/check-storybook-setup.py +0 -81
- package/.claude/hooks/check-update.py +0 -132
- package/.claude/hooks/detect-interruption.py +0 -165
- package/.claude/hooks/enforce-a11y-audit.py +0 -202
- package/.claude/hooks/enforce-brand-guide.py +0 -241
- package/.claude/hooks/enforce-component-type-confirm.py +0 -97
- package/.claude/hooks/enforce-freshness.py +0 -184
- package/.claude/hooks/enforce-page-components.py +0 -186
- package/.claude/hooks/enforce-page-data-schema.py +0 -155
- package/.claude/hooks/enforce-questions-sourced.py +0 -146
- package/.claude/hooks/enforce-schema-from-interview.py +0 -248
- package/.claude/hooks/enforce-ui-disambiguation.py +0 -108
- package/.claude/hooks/enforce-ui-interview.py +0 -130
- package/.claude/hooks/generate-manifest-entry.py +0 -1161
- package/.claude/hooks/lib/__init__.py +0 -1
- package/.claude/hooks/lib/greptile.py +0 -355
- package/.claude/hooks/lib/ntfy.py +0 -209
- package/.claude/hooks/notify-input-needed.py +0 -73
- package/.claude/hooks/notify-phase-complete.py +0 -90
- package/.claude/hooks/ntfy-on-question.py +0 -240
- package/.claude/hooks/orchestrator-completion.py +0 -313
- package/.claude/hooks/orchestrator-handoff.py +0 -267
- package/.claude/hooks/orchestrator-session-startup.py +0 -146
- package/.claude/hooks/run-code-review.py +0 -393
- package/.claude/hooks/session-logger.py +0 -323
- package/.claude/hooks/test-orchestrator-reground.py +0 -248
- package/.claude/hooks/track-scope-coverage.py +0 -220
- package/.claude/hooks/track-token-usage.py +0 -121
- package/.claude/hooks/update-api-showcase.py +0 -161
- package/.claude/hooks/update-registry.py +0 -352
- package/.claude/hooks/update-ui-showcase.py +0 -224
- package/.claude/test-auto-answer-bot.py +0 -183
- package/.claude/test-completion-detector.py +0 -263
- package/.claude/test-orchestrator-state.json +0 -20
- package/.claude/test-orchestrator.sh +0 -271
- /package/{.claude/commands → commands}/hustle-build.md +0 -0
- /package/{.claude/hooks → hooks}/lib/__pycache__/__init__.cpython-314.pyc +0 -0
- /package/{.claude/hooks → hooks}/lib/__pycache__/greptile.cpython-314.pyc +0 -0
- /package/{.claude/hooks → hooks}/lib/__pycache__/ntfy.cpython-314.pyc +0 -0
- /package/{.claude/hooks → hooks}/project-document-prompt.py +0 -0
- /package/{.claude/hooks → hooks}/remote-question-proxy.py +0 -0
- /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()
|