@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,183 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Auto-Answer Bot for Test Orchestration
4
-
5
- Monitors .claude/current-question.json in test directory and automatically
6
- answers questions by writing to .claude/pending-answer.json.
7
-
8
- This enables autonomous testing by auto-selecting "comprehensive" options
9
- without manual intervention.
10
-
11
- Usage:
12
- python test-auto-answer-bot.py <test_directory>
13
-
14
- Example:
15
- python test-auto-answer-bot.py ~/test-api-dev-tools-auto
16
-
17
- Version: 1.0.0
18
- """
19
-
20
- import json
21
- import sys
22
- import time
23
- from pathlib import Path
24
- from datetime import datetime
25
-
26
- # Configuration
27
- POLL_INTERVAL = 2 # seconds
28
- MAX_WAIT_TIME = 3600 # 1 hour max
29
- AFFIRMATIVE_KEYWORDS = [
30
- "comprehensive", "all", "yes", "proceed", "continue",
31
- "recommended", "auto", "defaults", "use auto", "use defaults"
32
- ]
33
-
34
-
35
- def find_best_answer(question_data):
36
- """
37
- Find the best answer for a question.
38
-
39
- Strategy:
40
- 1. Look for options with affirmative keywords (comprehensive, all, yes, etc.)
41
- 2. If multiple match, prefer first one
42
- 3. If none match, select first option
43
-
44
- Args:
45
- question_data: Parsed question data from current-question.json
46
-
47
- Returns:
48
- dict: Answer data with selected option
49
- """
50
- questions = question_data.get("questions", [])
51
-
52
- if not questions:
53
- return None
54
-
55
- # For now, handle first question (can extend to handle multiple)
56
- first_q = questions[0]
57
- options = first_q.get("options", [])
58
-
59
- if not options:
60
- return None
61
-
62
- # Find option with affirmative keyword
63
- selected_index = 0
64
- selected_label = None
65
-
66
- for i, opt in enumerate(options):
67
- label = opt.get("label", "") if isinstance(opt, dict) else str(opt)
68
- label_lower = label.lower()
69
-
70
- # Check for affirmative keywords
71
- for keyword in AFFIRMATIVE_KEYWORDS:
72
- if keyword in label_lower:
73
- selected_index = i
74
- selected_label = label
75
- break
76
-
77
- if selected_label:
78
- break
79
-
80
- # If no affirmative found, use first option
81
- if not selected_label:
82
- first_opt = options[0]
83
- selected_label = first_opt.get("label", "") if isinstance(first_opt, dict) else str(first_opt)
84
-
85
- return {
86
- "question_id": first_q.get("id", "question"),
87
- "question": first_q.get("question", ""),
88
- "header": first_q.get("header", "Question"),
89
- "answer": selected_label,
90
- "option_index": selected_index,
91
- "phase": question_data.get("phase", "unknown"),
92
- "status": "submitted",
93
- "submitted_at": datetime.now().isoformat(),
94
- "auto_answered": True,
95
- "answers": {
96
- first_q.get("header", "Question"): selected_label
97
- }
98
- }
99
-
100
-
101
- def watch_and_answer(test_dir):
102
- """
103
- Watch for questions and auto-answer them.
104
-
105
- Args:
106
- test_dir: Path to test directory
107
- """
108
- test_path = Path(test_dir).expanduser()
109
- question_file = test_path / ".claude" / "current-question.json"
110
- answer_file = test_path / ".claude" / "pending-answer.json"
111
-
112
- print(f"Auto-Answer Bot started")
113
- print(f"Monitoring: {question_file}")
114
- print(f"Writing answers to: {answer_file}")
115
- print(f"Poll interval: {POLL_INTERVAL}s")
116
- print()
117
-
118
- start_time = time.time()
119
- answered_count = 0
120
-
121
- while True:
122
- # Check timeout
123
- if time.time() - start_time > MAX_WAIT_TIME:
124
- print(f"Max wait time ({MAX_WAIT_TIME}s) exceeded. Exiting.")
125
- break
126
-
127
- # Check for question
128
- if question_file.exists():
129
- try:
130
- # Read question
131
- question_data = json.loads(question_file.read_text())
132
-
133
- # Skip if already answered recently
134
- if answer_file.exists():
135
- answer_data = json.loads(answer_file.read_text())
136
- answer_time = answer_data.get("submitted_at", "")
137
- question_time = question_data.get("created_at", "")
138
-
139
- # If answer is newer than question, skip
140
- if answer_time > question_time:
141
- time.sleep(POLL_INTERVAL)
142
- continue
143
-
144
- # Find best answer
145
- answer_data = find_best_answer(question_data)
146
-
147
- if answer_data:
148
- # Write answer
149
- answer_file.parent.mkdir(parents=True, exist_ok=True)
150
- answer_file.write_text(json.dumps(answer_data, indent=2))
151
-
152
- answered_count += 1
153
- print(f"[{datetime.now().strftime('%H:%M:%S')}] Answered question #{answered_count}")
154
- print(f" Question: {answer_data.get('question', '')[:80]}...")
155
- print(f" Answer: {answer_data.get('answer', '')}")
156
- print(f" Phase: {answer_data.get('phase', 'unknown')}")
157
- print()
158
-
159
- # Clear the question file
160
- question_file.unlink()
161
-
162
- except Exception as e:
163
- print(f"Error processing question: {e}")
164
-
165
- time.sleep(POLL_INTERVAL)
166
-
167
- print(f"Bot finished. Answered {answered_count} questions.")
168
-
169
-
170
- def main():
171
- if len(sys.argv) < 2:
172
- print("Usage: python test-auto-answer-bot.py <test_directory>")
173
- print()
174
- print("Example:")
175
- print(" python test-auto-answer-bot.py ~/test-api-dev-tools-auto")
176
- sys.exit(1)
177
-
178
- test_dir = sys.argv[1]
179
- watch_and_answer(test_dir)
180
-
181
-
182
- if __name__ == "__main__":
183
- main()
@@ -1,263 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Test Completion Detector
4
-
5
- Programmatically verifies that a workflow completed successfully by checking:
6
- 1. All 14 phases marked as "complete" in api-dev-state.json
7
- 2. All expected artifacts exist (route.ts, schema.ts, tests, README)
8
- 3. All hooks logged in workflow-logs/
9
- 4. Tests pass (if test files exist)
10
-
11
- Usage:
12
- python test-completion-detector.py <test_directory> <command_type>
13
-
14
- Example:
15
- python test-completion-detector.py ~/test-api-dev-tools-auto api-create
16
-
17
- Returns:
18
- Exit code 0 if complete, 1 if incomplete, 2 if error
19
- Prints JSON with detailed status
20
-
21
- Version: 1.0.0
22
- """
23
-
24
- import json
25
- import sys
26
- from pathlib import Path
27
- from datetime import datetime
28
-
29
-
30
- def load_state(test_dir):
31
- """Load api-dev-state.json."""
32
- state_file = Path(test_dir) / ".claude" / "api-dev-state.json"
33
-
34
- if not state_file.exists():
35
- return None
36
-
37
- try:
38
- return json.loads(state_file.read_text())
39
- except Exception as e:
40
- print(f"Error loading state: {e}", file=sys.stderr)
41
- return None
42
-
43
-
44
- def check_phases_complete(state):
45
- """Check if all 14 phases are marked complete."""
46
- required_phases = [
47
- "disambiguation",
48
- "scope",
49
- "research_initial",
50
- "interview",
51
- "research_deep",
52
- "schema_creation",
53
- "environment_check",
54
- "tdd_red",
55
- "tdd_green",
56
- "verify",
57
- "code_review",
58
- "tdd_refactor",
59
- "documentation",
60
- "completion"
61
- ]
62
-
63
- phases = state.get("phases", {})
64
- incomplete_phases = []
65
-
66
- for phase_name in required_phases:
67
- phase_data = phases.get(phase_name, {})
68
- status = phase_data.get("status", "not_started")
69
-
70
- if status != "complete":
71
- # Special case: research_deep can be skipped if comprehensive docs found
72
- if phase_name == "research_deep" and status == "skipped":
73
- continue
74
-
75
- # Special case: code_review can be partial if no API key
76
- if phase_name == "code_review" and status == "partial":
77
- continue
78
-
79
- incomplete_phases.append({
80
- "phase": phase_name,
81
- "status": status,
82
- "reason": phase_data.get("incomplete_reason", "Unknown")
83
- })
84
-
85
- return {
86
- "complete": len(incomplete_phases) == 0,
87
- "total_phases": len(required_phases),
88
- "complete_phases": len(required_phases) - len(incomplete_phases),
89
- "incomplete": incomplete_phases
90
- }
91
-
92
-
93
- def check_artifacts_exist(test_dir, command_type, state):
94
- """Check if all expected artifacts exist."""
95
- endpoint = state.get("endpoint", "unknown")
96
- missing_artifacts = []
97
- found_artifacts = []
98
-
99
- # Define expected artifacts by command type
100
- if command_type == "api-create":
101
- expected = {
102
- f"src/app/api/v2/{endpoint}/route.ts": "API route handler",
103
- f"src/app/api/v2/{endpoint}/schema.ts": "Zod schema definitions",
104
- f"src/app/api/v2/{endpoint}/__tests__/{endpoint}.api.test.ts": "API tests",
105
- f"src/app/api/v2/{endpoint}/README.md": "API documentation"
106
- }
107
- elif command_type == "hustle-ui-create":
108
- component_name = state.get("component_name", endpoint)
109
- expected = {
110
- f"src/components/{component_name}/{component_name}.tsx": "Component file",
111
- f"src/components/{component_name}/{component_name}.test.tsx": "Component tests",
112
- f"src/components/{component_name}/{component_name}.stories.tsx": "Storybook story"
113
- }
114
- elif command_type == "hustle-ui-create-page":
115
- page_route = state.get("page_route", endpoint)
116
- expected = {
117
- f"src/app/{page_route}/page.tsx": "Page file",
118
- f"e2e/{page_route}.spec.ts": "E2E tests"
119
- }
120
- elif command_type == "hustle-combine":
121
- expected = {
122
- f"src/app/api/v2/{endpoint}/route.ts": "Combined API route",
123
- f"src/app/api/v2/{endpoint}/schema.ts": "Combined schema",
124
- f"src/app/api/v2/{endpoint}/__tests__/{endpoint}.api.test.ts": "Integration tests"
125
- }
126
- elif command_type == "hustle-build":
127
- # Build creates multiple artifacts - check decomposition
128
- expected = {} # Will be checked differently
129
- else:
130
- return {
131
- "complete": False,
132
- "error": f"Unknown command type: {command_type}"
133
- }
134
-
135
- # Check each artifact
136
- test_path = Path(test_dir)
137
- for artifact_path, description in expected.items():
138
- full_path = test_path / artifact_path
139
- if full_path.exists():
140
- found_artifacts.append({"path": artifact_path, "description": description})
141
- else:
142
- missing_artifacts.append({"path": artifact_path, "description": description})
143
-
144
- return {
145
- "complete": len(missing_artifacts) == 0,
146
- "found": found_artifacts,
147
- "missing": missing_artifacts
148
- }
149
-
150
-
151
- def check_registry_updated(test_dir, command_type):
152
- """Check if registry.json was updated."""
153
- registry_file = Path(test_dir) / ".claude" / "registry.json"
154
-
155
- if not registry_file.exists():
156
- return {"complete": False, "reason": "Registry file missing"}
157
-
158
- try:
159
- registry = json.loads(registry_file.read_text())
160
-
161
- # Check if there are entries
162
- if command_type == "api-create":
163
- apis = registry.get("apis", [])
164
- if len(apis) == 0:
165
- return {"complete": False, "reason": "No APIs in registry"}
166
- elif command_type == "hustle-ui-create":
167
- components = registry.get("components", [])
168
- if len(components) == 0:
169
- return {"complete": False, "reason": "No components in registry"}
170
- elif command_type == "hustle-ui-create-page":
171
- pages = registry.get("pages", [])
172
- if len(pages) == 0:
173
- return {"complete": False, "reason": "No pages in registry"}
174
-
175
- return {"complete": True}
176
-
177
- except Exception as e:
178
- return {"complete": False, "reason": f"Error reading registry: {e}"}
179
-
180
-
181
- def check_workflow_logs(test_dir):
182
- """Check if workflow events were logged."""
183
- logs_dir = Path(test_dir) / ".claude" / "workflow-logs"
184
-
185
- if not logs_dir.exists():
186
- return {"complete": False, "reason": "Workflow logs directory missing"}
187
-
188
- log_files = list(logs_dir.glob("*.json"))
189
-
190
- if len(log_files) == 0:
191
- return {"complete": False, "reason": "No workflow log files found"}
192
-
193
- return {
194
- "complete": True,
195
- "log_files": [str(f.name) for f in log_files]
196
- }
197
-
198
-
199
- def main():
200
- if len(sys.argv) < 3:
201
- print(json.dumps({
202
- "error": "Usage: python test-completion-detector.py <test_directory> <command_type>"
203
- }))
204
- sys.exit(2)
205
-
206
- test_dir = Path(sys.argv[1]).expanduser()
207
- command_type = sys.argv[2]
208
-
209
- if not test_dir.exists():
210
- print(json.dumps({
211
- "error": f"Test directory does not exist: {test_dir}"
212
- }))
213
- sys.exit(2)
214
-
215
- # Load state
216
- state = load_state(test_dir)
217
-
218
- if not state:
219
- print(json.dumps({
220
- "complete": False,
221
- "error": "Could not load api-dev-state.json"
222
- }))
223
- sys.exit(1)
224
-
225
- # Run all checks
226
- phases_check = check_phases_complete(state)
227
- artifacts_check = check_artifacts_exist(test_dir, command_type, state)
228
- registry_check = check_registry_updated(test_dir, command_type)
229
- logs_check = check_workflow_logs(test_dir)
230
-
231
- # Overall result
232
- all_complete = (
233
- phases_check["complete"] and
234
- artifacts_check["complete"] and
235
- registry_check["complete"] and
236
- logs_check["complete"]
237
- )
238
-
239
- result = {
240
- "complete": all_complete,
241
- "timestamp": datetime.now().isoformat(),
242
- "test_directory": str(test_dir),
243
- "command_type": command_type,
244
- "endpoint": state.get("endpoint", "unknown"),
245
- "checks": {
246
- "phases": phases_check,
247
- "artifacts": artifacts_check,
248
- "registry": registry_check,
249
- "logs": logs_check
250
- }
251
- }
252
-
253
- print(json.dumps(result, indent=2))
254
-
255
- # Exit code based on result
256
- if all_complete:
257
- sys.exit(0)
258
- else:
259
- sys.exit(1)
260
-
261
-
262
- if __name__ == "__main__":
263
- main()
@@ -1,20 +0,0 @@
1
- {
2
- "turn_count": 0,
3
- "started_at": "2025-12-31T19:42:00.000Z",
4
- "test_directory": "/Users/alfonso/test-api-dev-tools-auto",
5
- "current_command": "/api-create",
6
- "current_endpoint": "weather-api",
7
- "current_phase": "initialization",
8
- "commands_tested": {
9
- "/api-create": {
10
- "status": "IN PROGRESS",
11
- "endpoint": "weather-api",
12
- "started_at": "2025-12-31T19:42:00.000Z",
13
- "phases_complete": 0,
14
- "retries": 0
15
- }
16
- },
17
- "total_retries": 0,
18
- "reground_history": [],
19
- "auto_answer_bot_pid": null
20
- }