@hustle-together/api-dev-tools 3.12.3 → 4.5.1

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 (159) 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 +7 -463
  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/workflow-logs/None.json +49 -0
  9. package/.claude/workflow-logs/session-20251230-143727.json +106 -0
  10. package/.skills/adr-deep-research/SKILL.md +351 -0
  11. package/.skills/api-create/SKILL.md +116 -17
  12. package/.skills/api-research/SKILL.md +130 -0
  13. package/.skills/docs-sync/SKILL.md +260 -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 +786 -0
  17. package/.skills/hustle-build-review/SKILL.md +518 -0
  18. package/.skills/parallel-spawn/SKILL.md +212 -0
  19. package/.skills/ralph-continue/SKILL.md +151 -0
  20. package/.skills/ralph-loop/SKILL.md +341 -0
  21. package/.skills/ralph-status/SKILL.md +87 -0
  22. package/.skills/refactor/SKILL.md +59 -0
  23. package/.skills/shadcn/SKILL.md +522 -0
  24. package/.skills/test-all/SKILL.md +210 -0
  25. package/.skills/test-builds/SKILL.md +208 -0
  26. package/.skills/test-debug/SKILL.md +212 -0
  27. package/.skills/test-e2e/SKILL.md +168 -0
  28. package/.skills/test-review/SKILL.md +707 -0
  29. package/.skills/test-unit/SKILL.md +143 -0
  30. package/.skills/test-visual/SKILL.md +301 -0
  31. package/.skills/token-report/SKILL.md +132 -0
  32. package/CHANGELOG.md +575 -0
  33. package/README.md +426 -56
  34. package/bin/cli.js +1538 -88
  35. package/commands/hustle-api-create.md +22 -0
  36. package/commands/hustle-build.md +259 -0
  37. package/commands/hustle-combine.md +81 -2
  38. package/commands/hustle-ui-create-page.md +84 -2
  39. package/commands/hustle-ui-create.md +82 -2
  40. package/hooks/__pycache__/api-workflow-check.cpython-314.pyc +0 -0
  41. package/hooks/__pycache__/auto-answer.cpython-314.pyc +0 -0
  42. package/hooks/__pycache__/cache-research.cpython-314.pyc +0 -0
  43. package/hooks/__pycache__/check-api-routes.cpython-314.pyc +0 -0
  44. package/hooks/__pycache__/check-playwright-setup.cpython-314.pyc +0 -0
  45. package/hooks/__pycache__/check-storybook-setup.cpython-314.pyc +0 -0
  46. package/hooks/__pycache__/check-update.cpython-314.pyc +0 -0
  47. package/hooks/__pycache__/completion-promise-detector.cpython-314.pyc +0 -0
  48. package/hooks/__pycache__/context-capacity-warning.cpython-314.pyc +0 -0
  49. package/hooks/__pycache__/detect-interruption.cpython-314.pyc +0 -0
  50. package/hooks/__pycache__/docs-update-check.cpython-314.pyc +0 -0
  51. package/hooks/__pycache__/enforce-a11y-audit.cpython-314.pyc +0 -0
  52. package/hooks/__pycache__/enforce-brand-guide.cpython-314.pyc +0 -0
  53. package/hooks/__pycache__/enforce-component-type-confirm.cpython-314.pyc +0 -0
  54. package/hooks/__pycache__/enforce-deep-research.cpython-314.pyc +0 -0
  55. package/hooks/__pycache__/enforce-disambiguation.cpython-314.pyc +0 -0
  56. package/hooks/__pycache__/enforce-documentation.cpython-314.pyc +0 -0
  57. package/hooks/__pycache__/enforce-dry-run.cpython-314.pyc +0 -0
  58. package/hooks/__pycache__/enforce-environment.cpython-314.pyc +0 -0
  59. package/hooks/__pycache__/enforce-external-research.cpython-314.pyc +0 -0
  60. package/hooks/__pycache__/enforce-freshness.cpython-314.pyc +0 -0
  61. package/hooks/__pycache__/enforce-interview.cpython-314.pyc +0 -0
  62. package/hooks/__pycache__/enforce-page-components.cpython-314.pyc +0 -0
  63. package/hooks/__pycache__/enforce-page-data-schema.cpython-314.pyc +0 -0
  64. package/hooks/__pycache__/enforce-questions-sourced.cpython-314.pyc +0 -0
  65. package/hooks/__pycache__/enforce-refactor.cpython-314.pyc +0 -0
  66. package/hooks/__pycache__/enforce-research.cpython-314.pyc +0 -0
  67. package/hooks/__pycache__/enforce-schema-from-interview.cpython-314.pyc +0 -0
  68. package/hooks/__pycache__/enforce-schema.cpython-314.pyc +0 -0
  69. package/hooks/__pycache__/enforce-scope.cpython-314.pyc +0 -0
  70. package/hooks/__pycache__/enforce-tdd-red.cpython-314.pyc +0 -0
  71. package/hooks/__pycache__/enforce-ui-disambiguation.cpython-314.pyc +0 -0
  72. package/hooks/__pycache__/enforce-ui-interview.cpython-314.pyc +0 -0
  73. package/hooks/__pycache__/enforce-verify.cpython-314.pyc +0 -0
  74. package/hooks/__pycache__/generate-adr-options.cpython-314.pyc +0 -0
  75. package/hooks/__pycache__/generate-manifest-entry.cpython-314.pyc +0 -0
  76. package/hooks/__pycache__/hook_utils.cpython-314.pyc +0 -0
  77. package/hooks/__pycache__/notify-input-needed.cpython-314.pyc +0 -0
  78. package/hooks/__pycache__/notify-phase-complete.cpython-314.pyc +0 -0
  79. package/hooks/__pycache__/ntfy-on-question.cpython-314.pyc +0 -0
  80. package/hooks/__pycache__/orchestrator-completion.cpython-314.pyc +0 -0
  81. package/hooks/__pycache__/orchestrator-handoff.cpython-314.pyc +0 -0
  82. package/hooks/__pycache__/orchestrator-session-startup.cpython-314.pyc +0 -0
  83. package/hooks/__pycache__/parallel-orchestrator.cpython-314.pyc +0 -0
  84. package/hooks/__pycache__/periodic-reground.cpython-314.pyc +0 -0
  85. package/hooks/__pycache__/project-document-prompt.cpython-314.pyc +0 -0
  86. package/hooks/__pycache__/remote-question-proxy.cpython-314.pyc +0 -0
  87. package/hooks/__pycache__/remote-question-server.cpython-314.pyc +0 -0
  88. package/hooks/__pycache__/run-code-review.cpython-314.pyc +0 -0
  89. package/hooks/__pycache__/run-visual-qa.cpython-314.pyc +0 -0
  90. package/hooks/__pycache__/session-logger.cpython-314.pyc +0 -0
  91. package/hooks/__pycache__/session-startup.cpython-314.pyc +0 -0
  92. package/hooks/__pycache__/track-scope-coverage.cpython-314.pyc +0 -0
  93. package/hooks/__pycache__/track-token-usage.cpython-314.pyc +0 -0
  94. package/hooks/__pycache__/track-tool-use.cpython-314.pyc +0 -0
  95. package/hooks/__pycache__/update-adr-decision.cpython-314.pyc +0 -0
  96. package/hooks/__pycache__/update-api-showcase.cpython-314.pyc +0 -0
  97. package/hooks/__pycache__/update-registry.cpython-314.pyc +0 -0
  98. package/hooks/__pycache__/update-ui-showcase.cpython-314.pyc +0 -0
  99. package/hooks/__pycache__/verify-after-green.cpython-314.pyc +0 -0
  100. package/hooks/__pycache__/verify-implementation.cpython-314.pyc +0 -0
  101. package/hooks/api-workflow-check.py +34 -0
  102. package/hooks/auto-answer.py +305 -0
  103. package/hooks/check-update.py +132 -0
  104. package/hooks/completion-promise-detector.py +293 -0
  105. package/hooks/context-capacity-warning.py +171 -0
  106. package/hooks/docs-update-check.py +120 -0
  107. package/hooks/enforce-dry-run.py +134 -0
  108. package/hooks/enforce-external-research.py +25 -0
  109. package/hooks/enforce-interview.py +20 -0
  110. package/hooks/generate-adr-options.py +282 -0
  111. package/hooks/hook_utils.py +609 -0
  112. package/hooks/lib/__pycache__/__init__.cpython-314.pyc +0 -0
  113. package/hooks/lib/__pycache__/greptile.cpython-314.pyc +0 -0
  114. package/hooks/lib/__pycache__/ntfy.cpython-314.pyc +0 -0
  115. package/hooks/ntfy-on-question.py +240 -0
  116. package/hooks/orchestrator-completion.py +313 -0
  117. package/hooks/orchestrator-handoff.py +267 -0
  118. package/hooks/orchestrator-session-startup.py +146 -0
  119. package/hooks/parallel-orchestrator.py +451 -0
  120. package/hooks/periodic-reground.py +270 -67
  121. package/hooks/project-document-prompt.py +302 -0
  122. package/hooks/remote-question-proxy.py +284 -0
  123. package/hooks/remote-question-server.py +1224 -0
  124. package/hooks/run-code-review.py +176 -29
  125. package/hooks/run-visual-qa.py +338 -0
  126. package/hooks/session-logger.py +27 -1
  127. package/hooks/session-startup.py +113 -0
  128. package/hooks/update-adr-decision.py +236 -0
  129. package/hooks/update-api-showcase.py +13 -1
  130. package/hooks/update-testing-checklist.py +195 -0
  131. package/hooks/update-ui-showcase.py +13 -1
  132. package/package.json +7 -3
  133. package/scripts/extract-schema-docs.cjs +322 -0
  134. package/templates/.skills/hustle-interview/SKILL.md +174 -0
  135. package/templates/CLAUDE-SECTION.md +89 -64
  136. package/templates/adr-viewer/_components/ADRViewer.tsx +326 -0
  137. package/templates/api-dev-state.json +33 -1
  138. package/templates/api-showcase/_components/APIModal.tsx +100 -8
  139. package/templates/api-showcase/_components/APIShowcase.tsx +36 -4
  140. package/templates/api-showcase/_components/APITester.tsx +367 -58
  141. package/templates/brand-page/page.tsx +645 -0
  142. package/templates/component/Component.visual.spec.ts +30 -24
  143. package/templates/docs/page.tsx +230 -0
  144. package/templates/eslint-plugin-zod-schema/index.js +446 -0
  145. package/templates/eslint-plugin-zod-schema/package.json +26 -0
  146. package/templates/github-workflows/security.yml +274 -0
  147. package/templates/hustle-build-defaults.json +136 -0
  148. package/templates/hustle-dev-dashboard/page.tsx +365 -0
  149. package/templates/page/page.e2e.test.ts +30 -26
  150. package/templates/performance-budgets.json +63 -5
  151. package/templates/playwright-report/page.tsx +258 -0
  152. package/templates/registry.json +279 -3
  153. package/templates/review-dashboard/page.tsx +510 -0
  154. package/templates/settings.json +155 -7
  155. package/templates/test-results/page.tsx +237 -0
  156. package/templates/typedoc.json +19 -0
  157. package/templates/ui-showcase/_components/UIShowcase.tsx +48 -1
  158. package/templates/ui-showcase/_components/VisualTestingDashboard.tsx +579 -0
  159. package/templates/ui-showcase/page.tsx +1 -1
@@ -0,0 +1,267 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Orchestrator handoff hook.
4
+
5
+ When a Skill is invoked, this hook checks if we're in an orchestrated build
6
+ and injects shared_decisions into the sub-workflow's state.
7
+
8
+ Hook Type: PreToolUse (matcher: Skill)
9
+ """
10
+
11
+ import json
12
+ import os
13
+ from pathlib import Path
14
+ from datetime import datetime
15
+
16
+
17
+ def load_build_state():
18
+ """Load hustle-build orchestration state"""
19
+ project_dir = os.environ.get("CLAUDE_PROJECT_DIR", ".")
20
+ state_file = Path(project_dir) / ".claude" / "hustle-build-state.json"
21
+
22
+ if state_file.exists():
23
+ try:
24
+ return json.loads(state_file.read_text())
25
+ except Exception:
26
+ pass
27
+ return None
28
+
29
+
30
+ def load_api_state():
31
+ """Load api-dev state"""
32
+ project_dir = os.environ.get("CLAUDE_PROJECT_DIR", ".")
33
+ state_file = Path(project_dir) / ".claude" / "api-dev-state.json"
34
+
35
+ if state_file.exists():
36
+ try:
37
+ return json.loads(state_file.read_text())
38
+ except Exception:
39
+ pass
40
+ return {}
41
+
42
+
43
+ def save_api_state(state):
44
+ """Save api-dev state with shared decisions"""
45
+ project_dir = os.environ.get("CLAUDE_PROJECT_DIR", ".")
46
+ state_file = Path(project_dir) / ".claude" / "api-dev-state.json"
47
+
48
+ try:
49
+ state_file.write_text(json.dumps(state, indent=2))
50
+ return True
51
+ except Exception:
52
+ return False
53
+
54
+
55
+ def get_skill_name(tool_input):
56
+ """Extract skill name from tool input"""
57
+ try:
58
+ data = json.loads(tool_input)
59
+ return data.get("skill", "")
60
+ except Exception:
61
+ return ""
62
+
63
+
64
+ def main():
65
+ tool_input = os.environ.get("CLAUDE_TOOL_INPUT", "{}")
66
+
67
+ # Get skill being invoked
68
+ skill_name = get_skill_name(tool_input)
69
+
70
+ # Check if this is a workflow skill
71
+ workflow_skills = [
72
+ "api-create", "hustle-ui-create", "hustle-ui-create-page",
73
+ "hustle-combine", "red", "green", "refactor", "cycle"
74
+ ]
75
+
76
+ if skill_name not in workflow_skills:
77
+ print(json.dumps({"continue": True}))
78
+ return
79
+
80
+ # Check if we're in an orchestrated build
81
+ build_state = load_build_state()
82
+
83
+ if not build_state or build_state.get("status") != "in_progress":
84
+ print(json.dumps({"continue": True}))
85
+ return
86
+
87
+ # Get shared decisions
88
+ shared_decisions = build_state.get("shared_decisions", {})
89
+ mode = build_state.get("mode", "interactive")
90
+
91
+ if not shared_decisions and mode != "auto":
92
+ print(json.dumps({"continue": True}))
93
+ return
94
+
95
+ # Load current api-dev state
96
+ api_state = load_api_state()
97
+
98
+ # Inject shared decisions
99
+ api_state["orchestrated"] = True
100
+ api_state["build_id"] = build_state.get("build_id")
101
+ api_state["mode"] = mode
102
+
103
+ # Pre-fill interview decisions from shared decisions
104
+ if "phases" not in api_state:
105
+ api_state["phases"] = {}
106
+
107
+ if "interview" not in api_state["phases"]:
108
+ api_state["phases"]["interview"] = {"status": "not_started", "decisions": {}}
109
+
110
+ # Map shared decisions to interview decisions
111
+ decision_mappings = {
112
+ "auth_required": "authentication",
113
+ "error_handling": "error_strategy",
114
+ "brand_guide": "use_brand_guide",
115
+ "testing_level": "testing_thoroughness",
116
+ "caching_strategy": "caching"
117
+ }
118
+
119
+ for shared_key, interview_key in decision_mappings.items():
120
+ if shared_key in shared_decisions:
121
+ api_state["phases"]["interview"]["decisions"][interview_key] = shared_decisions[shared_key]
122
+
123
+ # Mark which decisions are from orchestrator (so sub-workflow knows not to re-ask)
124
+ api_state["shared_decisions_applied"] = list(shared_decisions.keys())
125
+
126
+ # Save updated state
127
+ save_api_state(api_state)
128
+
129
+ # Update build state with current active workflow
130
+ decomposition = build_state.get("decomposition", {})
131
+
132
+ # Find the workflow being started
133
+ for wf_type in ["apis", "components", "combined_apis", "pages"]:
134
+ workflows = decomposition.get(wf_type, [])
135
+ for wf in workflows:
136
+ if wf.get("status") == "pending":
137
+ # This might be the one being started
138
+ # We'll rely on the skill to update status
139
+ break
140
+
141
+ # Log the handoff
142
+ project_dir = os.environ.get("CLAUDE_PROJECT_DIR", ".")
143
+ logs_dir = Path(project_dir) / ".claude" / "workflow-logs"
144
+ logs_dir.mkdir(parents=True, exist_ok=True)
145
+
146
+ log_file = logs_dir / f"{build_state.get('build_id', 'unknown')}.json"
147
+
148
+ try:
149
+ if log_file.exists():
150
+ log = json.loads(log_file.read_text())
151
+ else:
152
+ log = {"handoffs": []}
153
+
154
+ log["handoffs"].append({
155
+ "timestamp": datetime.now().isoformat(),
156
+ "skill": skill_name,
157
+ "shared_decisions_applied": list(shared_decisions.keys()),
158
+ "mode": mode
159
+ })
160
+
161
+ log_file.write_text(json.dumps(log, indent=2))
162
+ except Exception:
163
+ pass
164
+
165
+ # Build context about orchestration
166
+ context_parts = [f"""
167
+ ## Orchestrated Workflow
168
+
169
+ This workflow is part of a larger build: **{build_state.get('build_id')}**
170
+
171
+ ### Pre-Filled Decisions (from orchestrator):
172
+ {json.dumps(shared_decisions, indent=2)}
173
+
174
+ These decisions are already applied. **Do not re-ask** questions about:
175
+ {', '.join(shared_decisions.keys())}
176
+
177
+ Only ask workflow-specific questions not covered above.
178
+ """]
179
+
180
+ # Check for project_spec and inject relevant portion
181
+ project_spec = build_state.get("project_spec", {})
182
+ extracted = project_spec.get("extracted", {})
183
+
184
+ if extracted:
185
+ # Try to find the relevant spec for this workflow
186
+ relevant_spec = None
187
+ spec_type = None
188
+
189
+ # Get the element name from tool input
190
+ try:
191
+ data = json.loads(tool_input)
192
+ args = data.get("args", "")
193
+ element_name = args.split()[0] if args else ""
194
+ except Exception:
195
+ element_name = ""
196
+
197
+ # Search in extracted elements
198
+ for api in extracted.get("apis", []):
199
+ if api.get("name", "").lower() == element_name.lower():
200
+ relevant_spec = api
201
+ spec_type = "API"
202
+ break
203
+
204
+ if not relevant_spec:
205
+ for comp in extracted.get("components", []):
206
+ if comp.get("name", "").lower() == element_name.lower():
207
+ relevant_spec = comp
208
+ spec_type = "Component"
209
+ break
210
+
211
+ if not relevant_spec:
212
+ for page in extracted.get("pages", []):
213
+ if page.get("name", "").lower() == element_name.lower():
214
+ relevant_spec = page
215
+ spec_type = "Page"
216
+ break
217
+
218
+ # Inject relevant spec if found
219
+ if relevant_spec:
220
+ context_parts.append(f"""
221
+ ### Project Spec ({spec_type})
222
+
223
+ This element was extracted from the project document. Use this as the primary source of truth:
224
+
225
+ ```json
226
+ {json.dumps(relevant_spec, indent=2)}
227
+ ```
228
+
229
+ **Important:** Implement according to this specification. If you need to deviate, ask the user first.
230
+ """)
231
+
232
+ # Also inject high-level summary if available
233
+ summary = extracted.get("summary", "")
234
+ if summary:
235
+ context_parts.append(f"""
236
+ ### Project Summary
237
+
238
+ {summary}
239
+ """)
240
+
241
+ # Inject related elements for context
242
+ uses_apis = relevant_spec.get("uses_apis", []) if relevant_spec else []
243
+ uses_components = relevant_spec.get("uses_components", []) if relevant_spec else []
244
+
245
+ if uses_apis or uses_components:
246
+ context_parts.append(f"""
247
+ ### Related Elements
248
+
249
+ This element depends on:
250
+ - APIs: {', '.join(uses_apis) if uses_apis else 'none'}
251
+ - Components: {', '.join(uses_components) if uses_components else 'none'}
252
+
253
+ Ensure types and interfaces align with these dependencies.
254
+ """)
255
+
256
+ context = "\n".join(context_parts)
257
+
258
+ result = {
259
+ "continue": True,
260
+ "additionalContext": context
261
+ }
262
+
263
+ print(json.dumps(result))
264
+
265
+
266
+ if __name__ == "__main__":
267
+ main()
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Orchestrator session startup hook.
4
+
5
+ Injects hustle-build orchestration state into context at session start.
6
+ This ensures Claude has awareness of multi-workflow builds in progress.
7
+
8
+ Hook Type: SessionStart
9
+ """
10
+
11
+ import json
12
+ import os
13
+ from pathlib import Path
14
+ from datetime import datetime
15
+
16
+
17
+ def load_build_state():
18
+ """Load hustle-build orchestration state if exists"""
19
+ project_dir = os.environ.get("CLAUDE_PROJECT_DIR", ".")
20
+ state_file = Path(project_dir) / ".claude" / "hustle-build-state.json"
21
+
22
+ if state_file.exists():
23
+ try:
24
+ return json.loads(state_file.read_text())
25
+ except Exception:
26
+ pass
27
+ return None
28
+
29
+
30
+ def format_workflow_status(workflows):
31
+ """Format workflow list for context injection"""
32
+ if not workflows:
33
+ return "No sub-workflows defined yet."
34
+
35
+ lines = []
36
+ for wf in workflows:
37
+ status_emoji = {
38
+ "complete": "✅",
39
+ "in_progress": "🔄",
40
+ "pending": "⏳",
41
+ "failed": "❌"
42
+ }.get(wf.get("status", "pending"), "⏳")
43
+
44
+ wf_type = wf.get("type", "unknown")
45
+ name = wf.get("name", "unnamed")
46
+ deps = wf.get("depends_on", [])
47
+
48
+ line = f" {status_emoji} [{wf_type}] {name}"
49
+ if deps:
50
+ line += f" (depends on: {', '.join(deps)})"
51
+ lines.append(line)
52
+
53
+ return "\n".join(lines)
54
+
55
+
56
+ def format_shared_decisions(decisions):
57
+ """Format shared decisions for context injection"""
58
+ if not decisions:
59
+ return "No shared decisions configured."
60
+
61
+ lines = []
62
+ for key, value in decisions.items():
63
+ lines.append(f" - {key}: {value}")
64
+
65
+ return "\n".join(lines)
66
+
67
+
68
+ def main():
69
+ state = load_build_state()
70
+
71
+ if not state:
72
+ # No active build, continue normally
73
+ print(json.dumps({"continue": True}))
74
+ return
75
+
76
+ # Check if build is in progress
77
+ status = state.get("status", "unknown")
78
+
79
+ if status not in ["in_progress", "paused"]:
80
+ print(json.dumps({"continue": True}))
81
+ return
82
+
83
+ # Build context for injection
84
+ build_id = state.get("build_id", "unknown")
85
+ mode = state.get("mode", "interactive")
86
+ request = state.get("request", {}).get("original", "Unknown request")
87
+
88
+ # Get workflow statuses
89
+ decomposition = state.get("decomposition", {})
90
+ all_workflows = []
91
+
92
+ for wf_type in ["apis", "components", "combined_apis", "pages"]:
93
+ workflows = decomposition.get(wf_type, [])
94
+ for wf in workflows:
95
+ wf["type"] = wf_type.rstrip("s")
96
+ all_workflows.append(wf)
97
+
98
+ # Count progress
99
+ completed = len([w for w in all_workflows if w.get("status") == "complete"])
100
+ total = len(all_workflows)
101
+ in_progress = [w for w in all_workflows if w.get("status") == "in_progress"]
102
+
103
+ # Get active sub-workflow
104
+ active = state.get("active_sub_workflow", {})
105
+ active_name = active.get("name", "None")
106
+ active_type = active.get("type", "unknown")
107
+
108
+ # Format shared decisions
109
+ shared_decisions = state.get("shared_decisions", {})
110
+
111
+ context = f"""
112
+ ## Hustle Build In Progress
113
+
114
+ **Build ID:** {build_id}
115
+ **Mode:** {mode}
116
+ **Original Request:** "{request}"
117
+
118
+ ### Progress: {completed}/{total} workflows complete
119
+
120
+ **Currently Active:** [{active_type}] {active_name}
121
+
122
+ ### Sub-Workflows:
123
+ {format_workflow_status(all_workflows)}
124
+
125
+ ### Shared Decisions (applied to all):
126
+ {format_shared_decisions(shared_decisions)}
127
+
128
+ ---
129
+
130
+ **Commands:**
131
+ - Continue current workflow
132
+ - `/hustle-build-review {build_id}` - View build log
133
+ - Set `mode: "paused"` in state to pause
134
+
135
+ """
136
+
137
+ result = {
138
+ "continue": True,
139
+ "additionalContext": context
140
+ }
141
+
142
+ print(json.dumps(result))
143
+
144
+
145
+ if __name__ == "__main__":
146
+ main()