@hustle-together/api-dev-tools 1.0.0
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/LICENSE +21 -0
- package/README.md +447 -0
- package/bin/cli.js +322 -0
- package/commands/README.md +312 -0
- package/commands/add-command.md +209 -0
- package/commands/api-create.md +166 -0
- package/commands/api-env.md +50 -0
- package/commands/api-interview.md +211 -0
- package/commands/api-research.md +279 -0
- package/commands/api-status.md +259 -0
- package/commands/beepboop.md +97 -0
- package/commands/busycommit.md +112 -0
- package/commands/commit.md +83 -0
- package/commands/cycle.md +142 -0
- package/commands/gap.md +86 -0
- package/commands/green.md +142 -0
- package/commands/issue.md +192 -0
- package/commands/plan.md +168 -0
- package/commands/pr.md +122 -0
- package/commands/red.md +142 -0
- package/commands/refactor.md +142 -0
- package/commands/spike.md +142 -0
- package/commands/summarize.md +94 -0
- package/commands/tdd.md +144 -0
- package/commands/worktree-add.md +315 -0
- package/commands/worktree-cleanup.md +281 -0
- package/hooks/api-workflow-check.py +116 -0
- package/hooks/enforce-research.py +112 -0
- package/hooks/track-tool-use.py +194 -0
- package/package.json +45 -0
- package/templates/api-dev-state.json +65 -0
- package/templates/settings.json +36 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Hook: Stop
|
|
4
|
+
Purpose: Check if all required phases are complete before allowing stop
|
|
5
|
+
|
|
6
|
+
This hook runs when Claude tries to stop/end the conversation.
|
|
7
|
+
It checks api-dev-state.json to ensure critical workflow phases completed.
|
|
8
|
+
|
|
9
|
+
Returns:
|
|
10
|
+
- {"decision": "approve"} - Allow stopping
|
|
11
|
+
- {"decision": "block", "reason": "..."} - Prevent stopping with explanation
|
|
12
|
+
"""
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
# State file is in .claude/ directory (sibling to hooks/)
|
|
18
|
+
STATE_FILE = Path(__file__).parent.parent / "api-dev-state.json"
|
|
19
|
+
|
|
20
|
+
# Phases that MUST be complete before stopping
|
|
21
|
+
# These are the critical phases - others are optional
|
|
22
|
+
REQUIRED_PHASES = [
|
|
23
|
+
("research_initial", "Initial research (Context7/WebSearch)"),
|
|
24
|
+
("tdd_red", "TDD Red phase (failing tests written)"),
|
|
25
|
+
("tdd_green", "TDD Green phase (tests passing)"),
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
# Phases that SHOULD be complete (warning but don't block)
|
|
29
|
+
RECOMMENDED_PHASES = [
|
|
30
|
+
("interview", "User interview"),
|
|
31
|
+
("schema_creation", "Schema creation"),
|
|
32
|
+
("documentation", "Documentation updates"),
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def main():
|
|
37
|
+
# If no state file, we're not in an API workflow - allow stop
|
|
38
|
+
if not STATE_FILE.exists():
|
|
39
|
+
print(json.dumps({"decision": "approve"}))
|
|
40
|
+
sys.exit(0)
|
|
41
|
+
|
|
42
|
+
# Load state
|
|
43
|
+
try:
|
|
44
|
+
state = json.loads(STATE_FILE.read_text())
|
|
45
|
+
except json.JSONDecodeError:
|
|
46
|
+
# Corrupted state, allow stop
|
|
47
|
+
print(json.dumps({"decision": "approve"}))
|
|
48
|
+
sys.exit(0)
|
|
49
|
+
|
|
50
|
+
phases = state.get("phases", {})
|
|
51
|
+
|
|
52
|
+
# Check if workflow was even started
|
|
53
|
+
research = phases.get("research_initial", {})
|
|
54
|
+
if research.get("status") == "not_started":
|
|
55
|
+
# Workflow not started, allow stop
|
|
56
|
+
print(json.dumps({"decision": "approve"}))
|
|
57
|
+
sys.exit(0)
|
|
58
|
+
|
|
59
|
+
# Check required phases
|
|
60
|
+
incomplete_required = []
|
|
61
|
+
for phase_key, phase_name in REQUIRED_PHASES:
|
|
62
|
+
phase = phases.get(phase_key, {})
|
|
63
|
+
status = phase.get("status", "not_started")
|
|
64
|
+
if status != "complete":
|
|
65
|
+
incomplete_required.append(f" - {phase_name} ({status})")
|
|
66
|
+
|
|
67
|
+
# Check recommended phases
|
|
68
|
+
incomplete_recommended = []
|
|
69
|
+
for phase_key, phase_name in RECOMMENDED_PHASES:
|
|
70
|
+
phase = phases.get(phase_key, {})
|
|
71
|
+
status = phase.get("status", "not_started")
|
|
72
|
+
if status != "complete":
|
|
73
|
+
incomplete_recommended.append(f" - {phase_name} ({status})")
|
|
74
|
+
|
|
75
|
+
# Block if required phases incomplete
|
|
76
|
+
if incomplete_required:
|
|
77
|
+
reason_parts = ["❌ API workflow has REQUIRED phases incomplete:\n"]
|
|
78
|
+
reason_parts.extend(incomplete_required)
|
|
79
|
+
|
|
80
|
+
if incomplete_recommended:
|
|
81
|
+
reason_parts.append("\n\n⚠️ Also recommended but not complete:")
|
|
82
|
+
reason_parts.extend(incomplete_recommended)
|
|
83
|
+
|
|
84
|
+
reason_parts.append("\n\nTo continue:")
|
|
85
|
+
reason_parts.append(" 1. Complete required phases above")
|
|
86
|
+
reason_parts.append(" 2. Use /api-status to see detailed progress")
|
|
87
|
+
reason_parts.append(" 3. Or manually mark phases complete in .claude/api-dev-state.json")
|
|
88
|
+
|
|
89
|
+
print(json.dumps({
|
|
90
|
+
"decision": "block",
|
|
91
|
+
"reason": "\n".join(reason_parts)
|
|
92
|
+
}))
|
|
93
|
+
sys.exit(0)
|
|
94
|
+
|
|
95
|
+
# Warn about recommended phases but allow
|
|
96
|
+
if incomplete_recommended:
|
|
97
|
+
# Allow but the reason will be shown to user
|
|
98
|
+
print(json.dumps({
|
|
99
|
+
"decision": "approve",
|
|
100
|
+
"message": f"""⚠️ API workflow completing with optional phases pending:
|
|
101
|
+
{chr(10).join(incomplete_recommended)}
|
|
102
|
+
|
|
103
|
+
Consider running /api-status to review what was skipped."""
|
|
104
|
+
}))
|
|
105
|
+
sys.exit(0)
|
|
106
|
+
|
|
107
|
+
# All phases complete
|
|
108
|
+
print(json.dumps({
|
|
109
|
+
"decision": "approve",
|
|
110
|
+
"message": "✅ API workflow completed successfully!"
|
|
111
|
+
}))
|
|
112
|
+
sys.exit(0)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
if __name__ == "__main__":
|
|
116
|
+
main()
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Hook: PreToolUse for Write/Edit
|
|
4
|
+
Purpose: Block writing API code if research phase not complete
|
|
5
|
+
|
|
6
|
+
This hook runs BEFORE Claude can write or edit files in /api/ directories.
|
|
7
|
+
It checks the api-dev-state.json file to ensure research was completed first.
|
|
8
|
+
|
|
9
|
+
Returns:
|
|
10
|
+
- {"permissionDecision": "allow"} - Let the tool run
|
|
11
|
+
- {"permissionDecision": "deny", "reason": "..."} - Block with explanation
|
|
12
|
+
"""
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
# State file is in .claude/ directory (sibling to hooks/)
|
|
18
|
+
STATE_FILE = Path(__file__).parent.parent / "api-dev-state.json"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main():
|
|
22
|
+
# Read hook input from stdin (Claude Code passes tool info as JSON)
|
|
23
|
+
try:
|
|
24
|
+
input_data = json.load(sys.stdin)
|
|
25
|
+
except json.JSONDecodeError:
|
|
26
|
+
# If we can't parse input, allow (fail open for safety)
|
|
27
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
28
|
+
sys.exit(0)
|
|
29
|
+
|
|
30
|
+
tool_name = input_data.get("tool_name", "")
|
|
31
|
+
tool_input = input_data.get("tool_input", {})
|
|
32
|
+
|
|
33
|
+
# Get the file path being written/edited
|
|
34
|
+
file_path = tool_input.get("file_path", "")
|
|
35
|
+
|
|
36
|
+
# Only enforce for API route files
|
|
37
|
+
# Check for both /api/ and /api-test/ patterns
|
|
38
|
+
if "/api/" not in file_path and "/api-test/" not in file_path:
|
|
39
|
+
# Not an API file, allow without checking
|
|
40
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
41
|
+
sys.exit(0)
|
|
42
|
+
|
|
43
|
+
# Also skip for test files - tests should be written before research completes
|
|
44
|
+
# (TDD Red phase)
|
|
45
|
+
if ".test." in file_path or "/__tests__/" in file_path:
|
|
46
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
47
|
+
sys.exit(0)
|
|
48
|
+
|
|
49
|
+
# Skip for documentation/config files
|
|
50
|
+
if file_path.endswith(".md") or file_path.endswith(".json"):
|
|
51
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
52
|
+
sys.exit(0)
|
|
53
|
+
|
|
54
|
+
# Check if state file exists
|
|
55
|
+
if not STATE_FILE.exists():
|
|
56
|
+
print(json.dumps({
|
|
57
|
+
"permissionDecision": "deny",
|
|
58
|
+
"reason": """❌ API development state not initialized.
|
|
59
|
+
|
|
60
|
+
Before writing API implementation code, you must:
|
|
61
|
+
1. Run /api-create [endpoint-name] to start the workflow
|
|
62
|
+
OR
|
|
63
|
+
2. Run /api-research [library-name] to research dependencies
|
|
64
|
+
|
|
65
|
+
This ensures you're working with current documentation, not outdated training data."""
|
|
66
|
+
}))
|
|
67
|
+
sys.exit(0)
|
|
68
|
+
|
|
69
|
+
# Load and check state
|
|
70
|
+
try:
|
|
71
|
+
state = json.loads(STATE_FILE.read_text())
|
|
72
|
+
except json.JSONDecodeError:
|
|
73
|
+
# Corrupted state file, allow but warn
|
|
74
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
75
|
+
sys.exit(0)
|
|
76
|
+
|
|
77
|
+
# Check research phase status
|
|
78
|
+
phases = state.get("phases", {})
|
|
79
|
+
research = phases.get("research_initial", {})
|
|
80
|
+
research_status = research.get("status", "not_started")
|
|
81
|
+
|
|
82
|
+
if research_status != "complete":
|
|
83
|
+
sources_count = len(research.get("sources", []))
|
|
84
|
+
print(json.dumps({
|
|
85
|
+
"permissionDecision": "deny",
|
|
86
|
+
"reason": f"""❌ Cannot write API implementation code yet.
|
|
87
|
+
|
|
88
|
+
RESEARCH PHASE INCOMPLETE
|
|
89
|
+
Current status: {research_status}
|
|
90
|
+
Sources consulted: {sources_count}
|
|
91
|
+
|
|
92
|
+
REQUIRED ACTIONS:
|
|
93
|
+
1. Complete research phase first
|
|
94
|
+
2. Run: /api-research [library-name]
|
|
95
|
+
3. Ensure Context7 or WebSearch has been used
|
|
96
|
+
|
|
97
|
+
WHY THIS MATTERS:
|
|
98
|
+
- Implementation must match CURRENT API documentation
|
|
99
|
+
- Training data may be outdated
|
|
100
|
+
- All parameters must be discovered before coding
|
|
101
|
+
|
|
102
|
+
Once research is complete, you can proceed with implementation."""
|
|
103
|
+
}))
|
|
104
|
+
sys.exit(0)
|
|
105
|
+
|
|
106
|
+
# Research complete, allow writing
|
|
107
|
+
print(json.dumps({"permissionDecision": "allow"}))
|
|
108
|
+
sys.exit(0)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
if __name__ == "__main__":
|
|
112
|
+
main()
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Hook: PostToolUse for WebSearch, WebFetch, Context7 MCP
|
|
4
|
+
Purpose: Track all research activity in the state file
|
|
5
|
+
|
|
6
|
+
This hook runs AFTER Claude uses research tools (WebSearch, WebFetch, Context7).
|
|
7
|
+
It logs each research action to api-dev-state.json for:
|
|
8
|
+
- Auditing what research was done
|
|
9
|
+
- Verifying prerequisites before allowing implementation
|
|
10
|
+
- Providing visibility to the user
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
- {"continue": true} - Always continues (logging only, no blocking)
|
|
14
|
+
"""
|
|
15
|
+
import json
|
|
16
|
+
import sys
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
# State file is in .claude/ directory (sibling to hooks/)
|
|
21
|
+
STATE_FILE = Path(__file__).parent.parent / "api-dev-state.json"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def main():
|
|
25
|
+
# Read hook input from stdin
|
|
26
|
+
try:
|
|
27
|
+
input_data = json.load(sys.stdin)
|
|
28
|
+
except json.JSONDecodeError:
|
|
29
|
+
# Can't parse, just continue
|
|
30
|
+
print(json.dumps({"continue": True}))
|
|
31
|
+
sys.exit(0)
|
|
32
|
+
|
|
33
|
+
tool_name = input_data.get("tool_name", "")
|
|
34
|
+
tool_input = input_data.get("tool_input", {})
|
|
35
|
+
tool_output = input_data.get("tool_output", {})
|
|
36
|
+
|
|
37
|
+
# Only track research-related tools
|
|
38
|
+
research_tools = ["WebSearch", "WebFetch", "mcp__context7"]
|
|
39
|
+
is_research_tool = any(t in tool_name for t in research_tools)
|
|
40
|
+
|
|
41
|
+
if not is_research_tool:
|
|
42
|
+
print(json.dumps({"continue": True}))
|
|
43
|
+
sys.exit(0)
|
|
44
|
+
|
|
45
|
+
# Load or create state file
|
|
46
|
+
if STATE_FILE.exists():
|
|
47
|
+
try:
|
|
48
|
+
state = json.loads(STATE_FILE.read_text())
|
|
49
|
+
except json.JSONDecodeError:
|
|
50
|
+
state = create_initial_state()
|
|
51
|
+
else:
|
|
52
|
+
state = create_initial_state()
|
|
53
|
+
|
|
54
|
+
# Get or create research phase
|
|
55
|
+
phases = state.setdefault("phases", {})
|
|
56
|
+
research = phases.setdefault("research_initial", {
|
|
57
|
+
"status": "in_progress",
|
|
58
|
+
"sources": [],
|
|
59
|
+
"started_at": datetime.now().isoformat()
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
# Update status if not started
|
|
63
|
+
if research.get("status") == "not_started":
|
|
64
|
+
research["status"] = "in_progress"
|
|
65
|
+
research["started_at"] = datetime.now().isoformat()
|
|
66
|
+
|
|
67
|
+
# Get sources list
|
|
68
|
+
sources = research.setdefault("sources", [])
|
|
69
|
+
|
|
70
|
+
# Create source entry based on tool type
|
|
71
|
+
timestamp = datetime.now().isoformat()
|
|
72
|
+
|
|
73
|
+
if "context7" in tool_name.lower():
|
|
74
|
+
source_entry = {
|
|
75
|
+
"type": "context7",
|
|
76
|
+
"tool": tool_name,
|
|
77
|
+
"input": sanitize_input(tool_input),
|
|
78
|
+
"timestamp": timestamp,
|
|
79
|
+
"success": True
|
|
80
|
+
}
|
|
81
|
+
# Extract library info if available
|
|
82
|
+
if "libraryName" in tool_input:
|
|
83
|
+
source_entry["library"] = tool_input["libraryName"]
|
|
84
|
+
if "libraryId" in tool_input:
|
|
85
|
+
source_entry["library_id"] = tool_input["libraryId"]
|
|
86
|
+
|
|
87
|
+
elif tool_name == "WebSearch":
|
|
88
|
+
source_entry = {
|
|
89
|
+
"type": "websearch",
|
|
90
|
+
"query": tool_input.get("query", ""),
|
|
91
|
+
"timestamp": timestamp,
|
|
92
|
+
"success": True
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
elif tool_name == "WebFetch":
|
|
96
|
+
source_entry = {
|
|
97
|
+
"type": "webfetch",
|
|
98
|
+
"url": tool_input.get("url", ""),
|
|
99
|
+
"timestamp": timestamp,
|
|
100
|
+
"success": True
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
else:
|
|
104
|
+
# Generic research tool
|
|
105
|
+
source_entry = {
|
|
106
|
+
"type": "other",
|
|
107
|
+
"tool": tool_name,
|
|
108
|
+
"timestamp": timestamp,
|
|
109
|
+
"success": True
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# Add to sources list
|
|
113
|
+
sources.append(source_entry)
|
|
114
|
+
|
|
115
|
+
# Update last activity timestamp
|
|
116
|
+
research["last_activity"] = timestamp
|
|
117
|
+
research["source_count"] = len(sources)
|
|
118
|
+
|
|
119
|
+
# Check if we have enough sources to consider research "complete"
|
|
120
|
+
# More robust criteria:
|
|
121
|
+
# - At least 2 sources total (prevents single accidental search from completing)
|
|
122
|
+
# - At least one of: Context7 docs fetch, WebFetch of docs page
|
|
123
|
+
# - At least one search (WebSearch or Context7 resolve)
|
|
124
|
+
context7_count = sum(1 for s in sources if s.get("type") == "context7")
|
|
125
|
+
websearch_count = sum(1 for s in sources if s.get("type") == "websearch")
|
|
126
|
+
webfetch_count = sum(1 for s in sources if s.get("type") == "webfetch")
|
|
127
|
+
total_sources = len(sources)
|
|
128
|
+
|
|
129
|
+
# Minimum threshold: 2+ sources with at least one being docs-related
|
|
130
|
+
has_docs = webfetch_count >= 1 or context7_count >= 1
|
|
131
|
+
has_search = websearch_count >= 1 or context7_count >= 1
|
|
132
|
+
sufficient = total_sources >= 2 and has_docs and has_search
|
|
133
|
+
|
|
134
|
+
# Auto-complete research if sufficient sources
|
|
135
|
+
if sufficient:
|
|
136
|
+
if research.get("status") == "in_progress":
|
|
137
|
+
research["status"] = "complete"
|
|
138
|
+
research["completed_at"] = timestamp
|
|
139
|
+
research["completion_reason"] = "sufficient_sources"
|
|
140
|
+
research["completion_summary"] = {
|
|
141
|
+
"total_sources": total_sources,
|
|
142
|
+
"context7_calls": context7_count,
|
|
143
|
+
"web_searches": websearch_count,
|
|
144
|
+
"doc_fetches": webfetch_count
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Save state file
|
|
148
|
+
STATE_FILE.write_text(json.dumps(state, indent=2))
|
|
149
|
+
|
|
150
|
+
# Return success
|
|
151
|
+
print(json.dumps({"continue": True}))
|
|
152
|
+
sys.exit(0)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def create_initial_state():
|
|
156
|
+
"""Create initial state structure"""
|
|
157
|
+
return {
|
|
158
|
+
"version": "1.0.0",
|
|
159
|
+
"created_at": datetime.now().isoformat(),
|
|
160
|
+
"phases": {
|
|
161
|
+
"scope": {"status": "not_started"},
|
|
162
|
+
"research_initial": {"status": "not_started", "sources": []},
|
|
163
|
+
"interview": {"status": "not_started"},
|
|
164
|
+
"research_deep": {"status": "not_started", "sources": []},
|
|
165
|
+
"schema_creation": {"status": "not_started"},
|
|
166
|
+
"environment_check": {"status": "not_started"},
|
|
167
|
+
"tdd_red": {"status": "not_started"},
|
|
168
|
+
"tdd_green": {"status": "not_started"},
|
|
169
|
+
"tdd_refactor": {"status": "not_started"},
|
|
170
|
+
"documentation": {"status": "not_started"}
|
|
171
|
+
},
|
|
172
|
+
"verification": {
|
|
173
|
+
"all_sources_fetched": False,
|
|
174
|
+
"schema_matches_docs": False,
|
|
175
|
+
"tests_cover_params": False,
|
|
176
|
+
"all_tests_passing": False
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def sanitize_input(tool_input):
|
|
182
|
+
"""Remove potentially sensitive data from input before logging"""
|
|
183
|
+
sanitized = {}
|
|
184
|
+
for key, value in tool_input.items():
|
|
185
|
+
# Skip API keys or tokens
|
|
186
|
+
if any(sensitive in key.lower() for sensitive in ["key", "token", "secret", "password"]):
|
|
187
|
+
sanitized[key] = "[REDACTED]"
|
|
188
|
+
else:
|
|
189
|
+
sanitized[key] = value
|
|
190
|
+
return sanitized
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
if __name__ == "__main__":
|
|
194
|
+
main()
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hustle-together/api-dev-tools",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Interview-driven API development workflow for Claude Code - Automates research, testing, and documentation",
|
|
5
|
+
"main": "bin/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"api-dev-tools": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"commands/",
|
|
12
|
+
"hooks/",
|
|
13
|
+
"templates/",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"test": "node bin/cli.js --scope=project"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"claude",
|
|
22
|
+
"claude-code",
|
|
23
|
+
"api-development",
|
|
24
|
+
"tdd",
|
|
25
|
+
"test-driven-development",
|
|
26
|
+
"interview-driven",
|
|
27
|
+
"api-testing",
|
|
28
|
+
"documentation",
|
|
29
|
+
"workflow",
|
|
30
|
+
"automation"
|
|
31
|
+
],
|
|
32
|
+
"author": "Hustle Together",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/hustle-together/api-dev-tools.git"
|
|
37
|
+
},
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/hustle-together/api-dev-tools/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/hustle-together/api-dev-tools#readme",
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=14.0.0"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0.0",
|
|
3
|
+
"created_at": null,
|
|
4
|
+
"endpoint": null,
|
|
5
|
+
"library": null,
|
|
6
|
+
"phases": {
|
|
7
|
+
"scope": {
|
|
8
|
+
"status": "not_started",
|
|
9
|
+
"description": "Initial scope understanding"
|
|
10
|
+
},
|
|
11
|
+
"research_initial": {
|
|
12
|
+
"status": "not_started",
|
|
13
|
+
"sources": [],
|
|
14
|
+
"description": "Context7/WebSearch research for live documentation"
|
|
15
|
+
},
|
|
16
|
+
"interview": {
|
|
17
|
+
"status": "not_started",
|
|
18
|
+
"questions": [],
|
|
19
|
+
"description": "Structured interview about requirements"
|
|
20
|
+
},
|
|
21
|
+
"research_deep": {
|
|
22
|
+
"status": "not_started",
|
|
23
|
+
"sources": [],
|
|
24
|
+
"description": "Deep dive based on interview answers"
|
|
25
|
+
},
|
|
26
|
+
"schema_creation": {
|
|
27
|
+
"status": "not_started",
|
|
28
|
+
"schema_file": null,
|
|
29
|
+
"description": "Zod schema creation from research"
|
|
30
|
+
},
|
|
31
|
+
"environment_check": {
|
|
32
|
+
"status": "not_started",
|
|
33
|
+
"keys_verified": [],
|
|
34
|
+
"keys_missing": [],
|
|
35
|
+
"description": "API key and environment verification"
|
|
36
|
+
},
|
|
37
|
+
"tdd_red": {
|
|
38
|
+
"status": "not_started",
|
|
39
|
+
"test_file": null,
|
|
40
|
+
"test_count": 0,
|
|
41
|
+
"description": "Write failing tests first"
|
|
42
|
+
},
|
|
43
|
+
"tdd_green": {
|
|
44
|
+
"status": "not_started",
|
|
45
|
+
"implementation_file": null,
|
|
46
|
+
"description": "Minimal implementation to pass tests"
|
|
47
|
+
},
|
|
48
|
+
"tdd_refactor": {
|
|
49
|
+
"status": "not_started",
|
|
50
|
+
"description": "Code cleanup while keeping tests green"
|
|
51
|
+
},
|
|
52
|
+
"documentation": {
|
|
53
|
+
"status": "not_started",
|
|
54
|
+
"files_updated": [],
|
|
55
|
+
"description": "Update manifests, OpenAPI, examples"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"verification": {
|
|
59
|
+
"all_sources_fetched": false,
|
|
60
|
+
"schema_matches_docs": false,
|
|
61
|
+
"tests_cover_params": false,
|
|
62
|
+
"all_tests_passing": false,
|
|
63
|
+
"coverage_percent": null
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "Write|Edit",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/enforce-research.py"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"PostToolUse": [
|
|
15
|
+
{
|
|
16
|
+
"matcher": "WebSearch|WebFetch|mcp__context7.*",
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/track-tool-use.py"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"Stop": [
|
|
26
|
+
{
|
|
27
|
+
"hooks": [
|
|
28
|
+
{
|
|
29
|
+
"type": "command",
|
|
30
|
+
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/api-workflow-check.py"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
}
|