@hustle-together/api-dev-tools 3.10.1 → 3.11.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.
- package/.claude/api-dev-state.json +159 -0
- package/.claude/commands/README.md +185 -0
- package/.claude/commands/add-command.md +209 -0
- package/.claude/commands/api-create.md +499 -0
- package/.claude/commands/api-env.md +50 -0
- package/.claude/commands/api-interview.md +331 -0
- package/.claude/commands/api-research.md +331 -0
- package/.claude/commands/api-status.md +259 -0
- package/.claude/commands/api-verify.md +231 -0
- package/.claude/commands/beepboop.md +97 -0
- package/.claude/commands/busycommit.md +112 -0
- package/.claude/commands/commit.md +83 -0
- package/.claude/commands/cycle.md +142 -0
- package/.claude/commands/gap.md +86 -0
- package/.claude/commands/green.md +142 -0
- package/.claude/commands/issue.md +192 -0
- package/.claude/commands/plan.md +168 -0
- package/.claude/commands/pr.md +122 -0
- package/.claude/commands/red.md +142 -0
- package/.claude/commands/refactor.md +142 -0
- package/.claude/commands/spike.md +142 -0
- package/.claude/commands/summarize.md +94 -0
- package/.claude/commands/tdd.md +144 -0
- package/.claude/commands/worktree-add.md +315 -0
- package/.claude/commands/worktree-cleanup.md +281 -0
- package/.claude/hooks/api-workflow-check.py +227 -0
- package/.claude/hooks/enforce-deep-research.py +185 -0
- package/.claude/hooks/enforce-disambiguation.py +155 -0
- package/.claude/hooks/enforce-documentation.py +192 -0
- package/.claude/hooks/enforce-environment.py +253 -0
- package/.claude/hooks/enforce-external-research.py +328 -0
- package/.claude/hooks/enforce-interview.py +421 -0
- package/.claude/hooks/enforce-refactor.py +189 -0
- package/.claude/hooks/enforce-research.py +159 -0
- package/.claude/hooks/enforce-schema.py +186 -0
- package/.claude/hooks/enforce-scope.py +160 -0
- package/.claude/hooks/enforce-tdd-red.py +250 -0
- package/.claude/hooks/enforce-verify.py +186 -0
- package/.claude/hooks/periodic-reground.py +154 -0
- package/.claude/hooks/session-startup.py +151 -0
- package/.claude/hooks/track-tool-use.py +626 -0
- package/.claude/hooks/verify-after-green.py +282 -0
- package/.claude/hooks/verify-implementation.py +225 -0
- package/.claude/research/index.json +6 -0
- package/.claude/settings.json +93 -0
- package/.claude/settings.local.json +11 -0
- package/.claude-plugin/marketplace.json +112 -0
- package/.skills/README.md +291 -0
- package/.skills/_shared/convert-commands.py +192 -0
- package/.skills/_shared/hooks/api-workflow-check.py +227 -0
- package/.skills/_shared/hooks/enforce-deep-research.py +185 -0
- package/.skills/_shared/hooks/enforce-disambiguation.py +155 -0
- package/.skills/_shared/hooks/enforce-documentation.py +192 -0
- package/.skills/_shared/hooks/enforce-environment.py +253 -0
- package/.skills/_shared/hooks/enforce-external-research.py +328 -0
- package/.skills/_shared/hooks/enforce-interview.py +421 -0
- package/.skills/_shared/hooks/enforce-refactor.py +189 -0
- package/.skills/_shared/hooks/enforce-research.py +159 -0
- package/.skills/_shared/hooks/enforce-schema.py +186 -0
- package/.skills/_shared/hooks/enforce-scope.py +160 -0
- package/.skills/_shared/hooks/enforce-tdd-red.py +250 -0
- package/.skills/_shared/hooks/enforce-verify.py +186 -0
- package/.skills/_shared/hooks/periodic-reground.py +154 -0
- package/.skills/_shared/hooks/session-startup.py +151 -0
- package/.skills/_shared/hooks/track-tool-use.py +626 -0
- package/.skills/_shared/hooks/verify-after-green.py +282 -0
- package/.skills/_shared/hooks/verify-implementation.py +225 -0
- package/.skills/_shared/install.sh +114 -0
- package/.skills/_shared/settings.json +93 -0
- package/.skills/add-command/SKILL.md +222 -0
- package/.skills/api-create/SKILL.md +512 -0
- package/.skills/api-env/SKILL.md +63 -0
- package/.skills/api-interview/SKILL.md +344 -0
- package/.skills/api-research/SKILL.md +344 -0
- package/.skills/api-status/SKILL.md +272 -0
- package/.skills/api-verify/SKILL.md +244 -0
- package/.skills/beepboop/SKILL.md +110 -0
- package/.skills/busycommit/SKILL.md +125 -0
- package/.skills/commit/SKILL.md +96 -0
- package/.skills/cycle/SKILL.md +155 -0
- package/.skills/gap/SKILL.md +99 -0
- package/.skills/green/SKILL.md +155 -0
- package/.skills/issue/SKILL.md +205 -0
- package/.skills/plan/SKILL.md +181 -0
- package/.skills/pr/SKILL.md +135 -0
- package/.skills/red/SKILL.md +155 -0
- package/.skills/refactor/SKILL.md +155 -0
- package/.skills/spike/SKILL.md +155 -0
- package/.skills/summarize/SKILL.md +107 -0
- package/.skills/tdd/SKILL.md +157 -0
- package/.skills/update-todos/SKILL.md +228 -0
- package/.skills/worktree-add/SKILL.md +328 -0
- package/.skills/worktree-cleanup/SKILL.md +294 -0
- package/CHANGELOG.md +97 -0
- package/README.md +58 -17
- package/package.json +22 -11
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Hook: PreToolUse for Write/Edit (and Stop)
|
|
4
|
+
Purpose: Block completion until documentation confirmed WITH USER REVIEW
|
|
5
|
+
|
|
6
|
+
Phase 21 (Documentation) requires:
|
|
7
|
+
1. Update api-tests-manifest.json
|
|
8
|
+
2. Cache research to .claude/research/
|
|
9
|
+
3. Update OpenAPI spec if applicable
|
|
10
|
+
4. SHOW documentation checklist to user
|
|
11
|
+
5. USE AskUserQuestion: "Documentation complete? [Y/n]"
|
|
12
|
+
6. Only allow completion when user confirms
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
- {"permissionDecision": "allow"} - Let the tool run
|
|
16
|
+
- {"permissionDecision": "deny", "reason": "..."} - Block with explanation
|
|
17
|
+
"""
|
|
18
|
+
import json
|
|
19
|
+
import sys
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
STATE_FILE = Path(__file__).parent.parent / "api-dev-state.json"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def main():
|
|
26
|
+
try:
|
|
27
|
+
input_data = json.load(sys.stdin)
|
|
28
|
+
except json.JSONDecodeError:
|
|
29
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
30
|
+
sys.exit(0)
|
|
31
|
+
|
|
32
|
+
tool_input = input_data.get("tool_input", {})
|
|
33
|
+
file_path = tool_input.get("file_path", "")
|
|
34
|
+
|
|
35
|
+
# Only enforce for documentation files or when trying to mark workflow complete
|
|
36
|
+
is_manifest = "api-tests-manifest.json" in file_path
|
|
37
|
+
is_openapi = "openapi" in file_path.lower() and file_path.endswith((".json", ".yaml", ".yml"))
|
|
38
|
+
is_readme = file_path.endswith("README.md") and "/api/" in file_path
|
|
39
|
+
|
|
40
|
+
# Also check for state file updates (marking complete)
|
|
41
|
+
is_state_update = "api-dev-state.json" in file_path
|
|
42
|
+
|
|
43
|
+
if not is_manifest and not is_openapi and not is_readme and not is_state_update:
|
|
44
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
45
|
+
sys.exit(0)
|
|
46
|
+
|
|
47
|
+
if not STATE_FILE.exists():
|
|
48
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
49
|
+
sys.exit(0)
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
state = json.loads(STATE_FILE.read_text())
|
|
53
|
+
except json.JSONDecodeError:
|
|
54
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
55
|
+
sys.exit(0)
|
|
56
|
+
|
|
57
|
+
endpoint = state.get("endpoint", "unknown")
|
|
58
|
+
phases = state.get("phases", {})
|
|
59
|
+
tdd_refactor = phases.get("tdd_refactor", {})
|
|
60
|
+
documentation = phases.get("documentation", {})
|
|
61
|
+
|
|
62
|
+
# Only enforce after refactor is complete
|
|
63
|
+
if tdd_refactor.get("status") != "complete":
|
|
64
|
+
# Allow documentation updates during development
|
|
65
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
66
|
+
sys.exit(0)
|
|
67
|
+
|
|
68
|
+
status = documentation.get("status", "not_started")
|
|
69
|
+
|
|
70
|
+
phase_exit_confirmed = documentation.get("phase_exit_confirmed", False)
|
|
71
|
+
|
|
72
|
+
if status != "complete" or not phase_exit_confirmed:
|
|
73
|
+
user_question_asked = documentation.get("user_question_asked", False)
|
|
74
|
+
user_confirmed = documentation.get("user_confirmed", False)
|
|
75
|
+
checklist_shown = documentation.get("checklist_shown", False)
|
|
76
|
+
manifest_updated = documentation.get("manifest_updated", False)
|
|
77
|
+
research_cached = documentation.get("research_cached", False)
|
|
78
|
+
openapi_updated = documentation.get("openapi_updated", False)
|
|
79
|
+
|
|
80
|
+
missing = []
|
|
81
|
+
if not manifest_updated:
|
|
82
|
+
missing.append("api-tests-manifest.json not updated")
|
|
83
|
+
if not research_cached:
|
|
84
|
+
missing.append("Research not cached to .claude/research/")
|
|
85
|
+
if not checklist_shown:
|
|
86
|
+
missing.append("Documentation checklist not shown to user")
|
|
87
|
+
if not user_question_asked:
|
|
88
|
+
missing.append("User confirmation question (AskUserQuestion not used)")
|
|
89
|
+
if not user_confirmed:
|
|
90
|
+
missing.append("User hasn't confirmed documentation complete")
|
|
91
|
+
if not phase_exit_confirmed:
|
|
92
|
+
missing.append("Phase exit confirmation (user must explicitly approve to complete)")
|
|
93
|
+
|
|
94
|
+
print(json.dumps({
|
|
95
|
+
"permissionDecision": "deny",
|
|
96
|
+
"reason": f"""❌ BLOCKED: Documentation (Phase 21) not complete.
|
|
97
|
+
|
|
98
|
+
Status: {status}
|
|
99
|
+
Manifest updated: {manifest_updated}
|
|
100
|
+
Research cached: {research_cached}
|
|
101
|
+
OpenAPI updated: {openapi_updated}
|
|
102
|
+
Checklist shown: {checklist_shown}
|
|
103
|
+
User question asked: {user_question_asked}
|
|
104
|
+
User confirmed: {user_confirmed}
|
|
105
|
+
Phase exit confirmed: {phase_exit_confirmed}
|
|
106
|
+
|
|
107
|
+
MISSING:
|
|
108
|
+
{chr(10).join(f" • {m}" for m in missing)}
|
|
109
|
+
|
|
110
|
+
═══════════════════════════════════════════════════════════
|
|
111
|
+
⚠️ GET USER CONFIRMATION FOR DOCUMENTATION
|
|
112
|
+
═══════════════════════════════════════════════════════════
|
|
113
|
+
|
|
114
|
+
REQUIRED STEPS:
|
|
115
|
+
|
|
116
|
+
1. Update api-tests-manifest.json with:
|
|
117
|
+
• Endpoint path, method, description
|
|
118
|
+
• Request/response schemas
|
|
119
|
+
• Test coverage info
|
|
120
|
+
• Code examples
|
|
121
|
+
• Testing notes
|
|
122
|
+
|
|
123
|
+
2. Cache research to .claude/research/{endpoint}/:
|
|
124
|
+
• sources.json - URLs and summaries
|
|
125
|
+
• interview.json - User decisions
|
|
126
|
+
• schema.json - Final Zod schemas
|
|
127
|
+
|
|
128
|
+
3. Update OpenAPI spec (if applicable):
|
|
129
|
+
• Add endpoint definition
|
|
130
|
+
• Document parameters
|
|
131
|
+
• Document responses
|
|
132
|
+
|
|
133
|
+
4. SHOW documentation checklist to user:
|
|
134
|
+
┌───────────────────────────────────────────────────────┐
|
|
135
|
+
│ DOCUMENTATION CHECKLIST │
|
|
136
|
+
│ │
|
|
137
|
+
│ ✓ api-tests-manifest.json │
|
|
138
|
+
│ • Added {endpoint} entry │
|
|
139
|
+
│ • 8 test scenarios documented │
|
|
140
|
+
│ • Code examples included │
|
|
141
|
+
│ │
|
|
142
|
+
│ ✓ Research Cache │
|
|
143
|
+
│ • .claude/research/{endpoint}/sources.json │
|
|
144
|
+
│ • .claude/research/{endpoint}/interview.json │
|
|
145
|
+
│ │
|
|
146
|
+
│ {'✓' if openapi_updated else '⏭'} OpenAPI Spec │
|
|
147
|
+
│ • {'Updated' if openapi_updated else 'Skipped (internal API)'} │
|
|
148
|
+
│ │
|
|
149
|
+
│ All documentation complete? [Y] │
|
|
150
|
+
│ Need to add something? [n] ____ │
|
|
151
|
+
└───────────────────────────────────────────────────────┘
|
|
152
|
+
|
|
153
|
+
5. USE AskUserQuestion:
|
|
154
|
+
question: "Documentation checklist complete?"
|
|
155
|
+
options: [
|
|
156
|
+
{{"value": "confirm", "label": "Yes, all documentation is done"}},
|
|
157
|
+
{{"value": "add", "label": "No, I need to add [what]"}},
|
|
158
|
+
{{"value": "skip", "label": "Skip docs for now (not recommended)"}}
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
6. If user says "add":
|
|
162
|
+
• Ask what documentation is missing
|
|
163
|
+
• Update the relevant files
|
|
164
|
+
• LOOP BACK and show updated checklist
|
|
165
|
+
|
|
166
|
+
7. If user says "confirm":
|
|
167
|
+
• Set documentation.user_confirmed = true
|
|
168
|
+
• Set documentation.user_question_asked = true
|
|
169
|
+
• Set documentation.checklist_shown = true
|
|
170
|
+
• Set documentation.manifest_updated = true
|
|
171
|
+
• Set documentation.research_cached = true
|
|
172
|
+
• Set documentation.status = "complete"
|
|
173
|
+
• Mark entire workflow as complete!
|
|
174
|
+
|
|
175
|
+
WHY: Documentation ensures next developer (or future Claude) has context."""
|
|
176
|
+
}))
|
|
177
|
+
sys.exit(0)
|
|
178
|
+
|
|
179
|
+
# Documentation complete
|
|
180
|
+
print(json.dumps({
|
|
181
|
+
"permissionDecision": "allow",
|
|
182
|
+
"message": f"""✅ Documentation complete for {endpoint}.
|
|
183
|
+
Manifest updated: {manifest_updated}
|
|
184
|
+
Research cached: {research_cached}
|
|
185
|
+
OpenAPI updated: {openapi_updated}
|
|
186
|
+
User confirmed documentation is complete."""
|
|
187
|
+
}))
|
|
188
|
+
sys.exit(0)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
if __name__ == "__main__":
|
|
192
|
+
main()
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Hook: PreToolUse for Write/Edit
|
|
4
|
+
Purpose: Block writing if environment not verified WITH USER READINESS CONFIRMATION
|
|
5
|
+
|
|
6
|
+
Phase 7 requires:
|
|
7
|
+
1. Check required API keys based on endpoint/interview
|
|
8
|
+
2. Report found/missing keys to user
|
|
9
|
+
3. USE AskUserQuestion: "Ready for testing? [Y/n]"
|
|
10
|
+
4. Only proceed to TDD when user confirms readiness
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
- {"permissionDecision": "allow"} - Let the tool run
|
|
14
|
+
- {"permissionDecision": "deny", "reason": "..."} - Block with explanation
|
|
15
|
+
"""
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
# State file is in .claude/ directory (sibling to hooks/)
|
|
22
|
+
STATE_FILE = Path(__file__).parent.parent / "api-dev-state.json"
|
|
23
|
+
|
|
24
|
+
# Common API key patterns to check
|
|
25
|
+
COMMON_KEY_PATTERNS = {
|
|
26
|
+
"openai": ["OPENAI_API_KEY", "NEXT_PUBLIC_OPENAI_API_KEY"],
|
|
27
|
+
"anthropic": ["ANTHROPIC_API_KEY", "NEXT_PUBLIC_ANTHROPIC_API_KEY"],
|
|
28
|
+
"google": ["GOOGLE_API_KEY", "GOOGLE_GENERATIVE_AI_API_KEY"],
|
|
29
|
+
"brandfetch": ["BRANDFETCH_API_KEY"],
|
|
30
|
+
"firecrawl": ["FIRECRAWL_API_KEY"],
|
|
31
|
+
"brave": ["BRAVE_SEARCH_API_KEY"],
|
|
32
|
+
"perplexity": ["PERPLEXITY_API_KEY"],
|
|
33
|
+
"exa": ["EXA_API_KEY"],
|
|
34
|
+
"cartesia": ["CARTESIA_API_KEY"],
|
|
35
|
+
"elevenlabs": ["ELEVENLABS_API_KEY"],
|
|
36
|
+
"unsplash": ["UNSPLASH_ACCESS_KEY"],
|
|
37
|
+
"pexels": ["PEXELS_API_KEY"],
|
|
38
|
+
"supabase": ["SUPABASE_URL", "SUPABASE_ANON_KEY", "SUPABASE_SERVICE_ROLE_KEY"],
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def check_env_keys(required_keys: list) -> tuple[list, list]:
|
|
43
|
+
"""Check which keys exist and which are missing."""
|
|
44
|
+
found = []
|
|
45
|
+
missing = []
|
|
46
|
+
|
|
47
|
+
for key in required_keys:
|
|
48
|
+
if os.environ.get(key):
|
|
49
|
+
found.append(key)
|
|
50
|
+
else:
|
|
51
|
+
missing.append(key)
|
|
52
|
+
|
|
53
|
+
return found, missing
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def infer_required_keys(endpoint: str, external_services: list) -> list:
|
|
57
|
+
"""Infer required API keys from endpoint name and external services."""
|
|
58
|
+
required = []
|
|
59
|
+
|
|
60
|
+
# Check endpoint name against common patterns
|
|
61
|
+
endpoint_lower = endpoint.lower()
|
|
62
|
+
for service, keys in COMMON_KEY_PATTERNS.items():
|
|
63
|
+
if service in endpoint_lower:
|
|
64
|
+
required.extend(keys)
|
|
65
|
+
|
|
66
|
+
# Check external services list
|
|
67
|
+
for service in external_services:
|
|
68
|
+
service_lower = service.lower()
|
|
69
|
+
for pattern, keys in COMMON_KEY_PATTERNS.items():
|
|
70
|
+
if pattern in service_lower:
|
|
71
|
+
required.extend(keys)
|
|
72
|
+
|
|
73
|
+
return list(set(required)) # Deduplicate
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def main():
|
|
77
|
+
# Read hook input from stdin
|
|
78
|
+
try:
|
|
79
|
+
input_data = json.load(sys.stdin)
|
|
80
|
+
except json.JSONDecodeError:
|
|
81
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
82
|
+
sys.exit(0)
|
|
83
|
+
|
|
84
|
+
tool_input = input_data.get("tool_input", {})
|
|
85
|
+
file_path = tool_input.get("file_path", "")
|
|
86
|
+
|
|
87
|
+
# Only enforce for API route files (not tests - tests should fail if keys missing)
|
|
88
|
+
is_api_file = "/api/" in file_path and file_path.endswith(".ts")
|
|
89
|
+
is_route_file = file_path.endswith("route.ts")
|
|
90
|
+
|
|
91
|
+
if not is_api_file or not is_route_file:
|
|
92
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
93
|
+
sys.exit(0)
|
|
94
|
+
|
|
95
|
+
# Skip test files
|
|
96
|
+
if ".test." in file_path or "/__tests__/" in file_path or ".spec." in file_path:
|
|
97
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
98
|
+
sys.exit(0)
|
|
99
|
+
|
|
100
|
+
# Check if state file exists
|
|
101
|
+
if not STATE_FILE.exists():
|
|
102
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
103
|
+
sys.exit(0)
|
|
104
|
+
|
|
105
|
+
# Load state
|
|
106
|
+
try:
|
|
107
|
+
state = json.loads(STATE_FILE.read_text())
|
|
108
|
+
except json.JSONDecodeError:
|
|
109
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
110
|
+
sys.exit(0)
|
|
111
|
+
|
|
112
|
+
endpoint = state.get("endpoint")
|
|
113
|
+
if not endpoint:
|
|
114
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
115
|
+
sys.exit(0)
|
|
116
|
+
|
|
117
|
+
phases = state.get("phases", {})
|
|
118
|
+
schema_creation = phases.get("schema_creation", {})
|
|
119
|
+
environment_check = phases.get("environment_check", {})
|
|
120
|
+
|
|
121
|
+
# Only enforce after schema creation
|
|
122
|
+
if schema_creation.get("status") != "complete":
|
|
123
|
+
# Let earlier hooks handle this
|
|
124
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
125
|
+
sys.exit(0)
|
|
126
|
+
|
|
127
|
+
env_status = environment_check.get("status", "not_started")
|
|
128
|
+
keys_required = environment_check.get("keys_required", [])
|
|
129
|
+
keys_found = environment_check.get("keys_found", [])
|
|
130
|
+
keys_missing = environment_check.get("keys_missing", [])
|
|
131
|
+
user_question_asked = environment_check.get("user_question_asked", False)
|
|
132
|
+
user_ready = environment_check.get("user_ready", False)
|
|
133
|
+
env_shown = environment_check.get("env_shown", False)
|
|
134
|
+
phase_exit_confirmed = environment_check.get("phase_exit_confirmed", False)
|
|
135
|
+
|
|
136
|
+
# Check if environment check is complete
|
|
137
|
+
if env_status != "complete" or not phase_exit_confirmed:
|
|
138
|
+
# Infer required keys if not already set
|
|
139
|
+
if not keys_required:
|
|
140
|
+
interview = phases.get("interview", {})
|
|
141
|
+
decisions = interview.get("decisions", {})
|
|
142
|
+
external_services = decisions.get("external_services", {}).get("value", [])
|
|
143
|
+
if isinstance(external_services, str):
|
|
144
|
+
external_services = [external_services]
|
|
145
|
+
keys_required = infer_required_keys(endpoint, external_services)
|
|
146
|
+
|
|
147
|
+
# Check current environment
|
|
148
|
+
if keys_required:
|
|
149
|
+
found, missing = check_env_keys(keys_required)
|
|
150
|
+
else:
|
|
151
|
+
found, missing = [], []
|
|
152
|
+
|
|
153
|
+
# Check what's missing for user checkpoint
|
|
154
|
+
missing_steps = []
|
|
155
|
+
if not env_shown:
|
|
156
|
+
missing_steps.append("Environment status not shown to user")
|
|
157
|
+
if not user_question_asked:
|
|
158
|
+
missing_steps.append("User readiness question (AskUserQuestion not used)")
|
|
159
|
+
if not user_ready:
|
|
160
|
+
missing_steps.append("User hasn't confirmed readiness for TDD")
|
|
161
|
+
if not phase_exit_confirmed:
|
|
162
|
+
missing_steps.append("Phase exit confirmation (user must explicitly approve to proceed)")
|
|
163
|
+
|
|
164
|
+
print(json.dumps({
|
|
165
|
+
"permissionDecision": "deny",
|
|
166
|
+
"reason": f"""❌ BLOCKED: Environment check (Phase 7) not complete.
|
|
167
|
+
|
|
168
|
+
Current status: {env_status}
|
|
169
|
+
Required keys: {len(keys_required)}
|
|
170
|
+
Found: {len(found)}
|
|
171
|
+
Missing: {len(missing)}
|
|
172
|
+
User shown env: {env_shown}
|
|
173
|
+
User question asked: {user_question_asked}
|
|
174
|
+
User ready: {user_ready}
|
|
175
|
+
Phase exit confirmed: {phase_exit_confirmed}
|
|
176
|
+
|
|
177
|
+
MISSING:
|
|
178
|
+
{chr(10).join(f" • {m}" for m in missing_steps)}
|
|
179
|
+
|
|
180
|
+
═══════════════════════════════════════════════════════════
|
|
181
|
+
⚠️ GET USER READINESS CONFIRMATION
|
|
182
|
+
═══════════════════════════════════════════════════════════
|
|
183
|
+
|
|
184
|
+
REQUIRED STEPS:
|
|
185
|
+
|
|
186
|
+
1. Check API keys and SHOW status to user:
|
|
187
|
+
┌───────────────────────────────────────────────────────┐
|
|
188
|
+
│ ENVIRONMENT CHECK │
|
|
189
|
+
│ │
|
|
190
|
+
│ Required for {endpoint}: │
|
|
191
|
+
│ │
|
|
192
|
+
│ API Keys: │
|
|
193
|
+
{chr(10).join(f" │ {'✓' if k in found else '❌'} {k:<40} │" for k in keys_required) if keys_required else " │ No API keys required │"}
|
|
194
|
+
│ │
|
|
195
|
+
│ Testing Setup: │
|
|
196
|
+
│ • Schema file ready │
|
|
197
|
+
│ • Test patterns defined │
|
|
198
|
+
│ • Mock data prepared (if needed) │
|
|
199
|
+
│ │
|
|
200
|
+
│ Ready to begin TDD? [Y] │
|
|
201
|
+
│ Need to fix something? [n] │
|
|
202
|
+
└───────────────────────────────────────────────────────┘
|
|
203
|
+
|
|
204
|
+
2. USE AskUserQuestion:
|
|
205
|
+
question: "Environment looks ready. Start TDD?"
|
|
206
|
+
options: [
|
|
207
|
+
{{"value": "ready", "label": "Yes, ready to write tests"}},
|
|
208
|
+
{{"value": "fix_keys", "label": "No, need to set up API keys first"}},
|
|
209
|
+
{{"value": "fix_other", "label": "No, need to fix something else"}}
|
|
210
|
+
]
|
|
211
|
+
|
|
212
|
+
3. If user says "fix_keys" or "fix_other":
|
|
213
|
+
• Help them resolve the issue
|
|
214
|
+
• Re-check environment
|
|
215
|
+
• LOOP BACK and show updated status
|
|
216
|
+
|
|
217
|
+
4. If user says "ready":
|
|
218
|
+
• Set environment_check.user_ready = true
|
|
219
|
+
• Set environment_check.user_question_asked = true
|
|
220
|
+
• Set environment_check.env_shown = true
|
|
221
|
+
• Set environment_check.keys_found = [list]
|
|
222
|
+
• Set environment_check.keys_missing = [list]
|
|
223
|
+
• Set environment_check.status = "complete"
|
|
224
|
+
|
|
225
|
+
{'API KEY ISSUES:' if missing else ''}
|
|
226
|
+
{chr(10).join(f" ❌ {k}" for k in missing) if missing else ''}
|
|
227
|
+
|
|
228
|
+
WHY: Verify environment before writing tests that depend on it."""
|
|
229
|
+
}))
|
|
230
|
+
sys.exit(0)
|
|
231
|
+
|
|
232
|
+
# Environment check complete
|
|
233
|
+
if keys_missing and not user_ready:
|
|
234
|
+
print(json.dumps({
|
|
235
|
+
"permissionDecision": "deny",
|
|
236
|
+
"reason": f"""❌ Missing keys noted but user hasn't confirmed readiness.
|
|
237
|
+
Use AskUserQuestion to confirm user is ready to proceed with missing keys:
|
|
238
|
+
{chr(10).join(f" ⚠️ {k}" for k in keys_missing[:3])}"""
|
|
239
|
+
}))
|
|
240
|
+
sys.exit(0)
|
|
241
|
+
|
|
242
|
+
print(json.dumps({
|
|
243
|
+
"permissionDecision": "allow",
|
|
244
|
+
"message": f"""✅ Environment check complete.
|
|
245
|
+
User confirmed ready for TDD.
|
|
246
|
+
Keys found: {len(keys_found)}
|
|
247
|
+
Keys missing (acknowledged): {len(keys_missing)}"""
|
|
248
|
+
}))
|
|
249
|
+
sys.exit(0)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
if __name__ == "__main__":
|
|
253
|
+
main()
|