ai-sprint-kit 1.3.1 → 2.0.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 (64) hide show
  1. package/LICENSE +35 -123
  2. package/README.md +39 -207
  3. package/bin/ai-sprint.js +105 -0
  4. package/lib/auth.js +73 -0
  5. package/lib/installer.js +59 -195
  6. package/lib/messages.js +53 -0
  7. package/package.json +15 -18
  8. package/bin/cli.js +0 -135
  9. package/lib/scanner.js +0 -321
  10. package/templates/.claude/.env.example +0 -13
  11. package/templates/.claude/agents/debugger.md +0 -668
  12. package/templates/.claude/agents/devops.md +0 -728
  13. package/templates/.claude/agents/docs.md +0 -662
  14. package/templates/.claude/agents/implementer.md +0 -288
  15. package/templates/.claude/agents/planner.md +0 -273
  16. package/templates/.claude/agents/researcher.md +0 -454
  17. package/templates/.claude/agents/reviewer.md +0 -644
  18. package/templates/.claude/agents/security.md +0 -203
  19. package/templates/.claude/agents/tester.md +0 -647
  20. package/templates/.claude/commands/ai-sprint-auto.md +0 -150
  21. package/templates/.claude/commands/ai-sprint-code.md +0 -316
  22. package/templates/.claude/commands/ai-sprint-debug.md +0 -453
  23. package/templates/.claude/commands/ai-sprint-deploy.md +0 -475
  24. package/templates/.claude/commands/ai-sprint-docs.md +0 -519
  25. package/templates/.claude/commands/ai-sprint-plan.md +0 -136
  26. package/templates/.claude/commands/ai-sprint-review.md +0 -433
  27. package/templates/.claude/commands/ai-sprint-scan.md +0 -146
  28. package/templates/.claude/commands/ai-sprint-secure.md +0 -88
  29. package/templates/.claude/commands/ai-sprint-test.md +0 -352
  30. package/templates/.claude/commands/ai-sprint-validate.md +0 -253
  31. package/templates/.claude/settings.json +0 -27
  32. package/templates/.claude/skills/codebase-context/SKILL.md +0 -68
  33. package/templates/.claude/skills/codebase-context/references/reading-context.md +0 -68
  34. package/templates/.claude/skills/codebase-context/references/refresh-triggers.md +0 -82
  35. package/templates/.claude/skills/implementation/SKILL.md +0 -70
  36. package/templates/.claude/skills/implementation/references/error-handling.md +0 -106
  37. package/templates/.claude/skills/implementation/references/security-patterns.md +0 -73
  38. package/templates/.claude/skills/implementation/references/validation-patterns.md +0 -107
  39. package/templates/.claude/skills/memory/SKILL.md +0 -67
  40. package/templates/.claude/skills/memory/references/decisions-format.md +0 -68
  41. package/templates/.claude/skills/memory/references/learning-format.md +0 -74
  42. package/templates/.claude/skills/planning/SKILL.md +0 -72
  43. package/templates/.claude/skills/planning/references/plan-templates.md +0 -81
  44. package/templates/.claude/skills/planning/references/research-phase.md +0 -62
  45. package/templates/.claude/skills/planning/references/solution-design.md +0 -66
  46. package/templates/.claude/skills/quality-assurance/SKILL.md +0 -79
  47. package/templates/.claude/skills/quality-assurance/references/review-checklist.md +0 -72
  48. package/templates/.claude/skills/quality-assurance/references/security-checklist.md +0 -70
  49. package/templates/.claude/skills/quality-assurance/references/testing-strategy.md +0 -85
  50. package/templates/.claude/skills/quality-assurance/scripts/check-size.py +0 -333
  51. package/templates/.claude/statusline.sh +0 -126
  52. package/templates/.claude/workflows/development-rules.md +0 -133
  53. package/templates/.claude/workflows/orchestration-protocol.md +0 -194
  54. package/templates/.mcp.json.example +0 -36
  55. package/templates/CLAUDE.md +0 -412
  56. package/templates/README.md +0 -331
  57. package/templates/ai_context/codebase/.gitkeep +0 -0
  58. package/templates/ai_context/memory/active.md +0 -15
  59. package/templates/ai_context/memory/decisions.md +0 -18
  60. package/templates/ai_context/memory/learning.md +0 -22
  61. package/templates/ai_context/plans/.gitkeep +0 -0
  62. package/templates/ai_context/reports/.gitkeep +0 -0
  63. package/templates/docs/user-guide-th.md +0 -454
  64. package/templates/docs/user-guide.md +0 -595
@@ -1,333 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Design principles size checker.
4
- Detects files >500 lines and functions >50 lines.
5
- Supports: Python, JavaScript, TypeScript, Go, Java, Rust
6
- Exit code: 0 = no violations (pass), 1 = violations found (warn only)
7
- """
8
-
9
- import argparse
10
- import ast
11
- import os
12
- import re
13
- import sys
14
- from pathlib import Path
15
- from dataclasses import dataclass
16
- from typing import List
17
-
18
- @dataclass
19
- class Violation:
20
- file: str
21
- line: int
22
- type: str # 'file' or 'function'
23
- name: str
24
- actual: int
25
- limit: int
26
-
27
-
28
- def count_file_lines(path: Path) -> int:
29
- """Count non-empty, non-comment lines."""
30
- try:
31
- with open(path, 'r', encoding='utf-8', errors='ignore') as f:
32
- lines = [l for l in f.readlines() if l.strip()
33
- and not l.strip().startswith(('#', '//', '--', '/*', '*'))]
34
- return len(lines)
35
- except Exception:
36
- return 0
37
-
38
-
39
- def check_python_functions(path: Path, max_lines: int) -> List[Violation]:
40
- """Check Python function lengths using AST."""
41
- violations = []
42
- try:
43
- with open(path, 'r', encoding='utf-8') as f:
44
- tree = ast.parse(f.read())
45
- for node in ast.walk(tree):
46
- if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
47
- length = (node.end_lineno or node.lineno) - node.lineno + 1
48
- if length > max_lines:
49
- violations.append(Violation(
50
- file=str(path), line=node.lineno, type='function',
51
- name=node.name, actual=length, limit=max_lines
52
- ))
53
- except (SyntaxError, Exception):
54
- pass
55
- return violations
56
-
57
-
58
- def check_js_functions(path: Path, max_lines: int) -> List[Violation]:
59
- """Check JS/TS function lengths using brace counting."""
60
- violations = []
61
- try:
62
- with open(path, 'r', encoding='utf-8') as f:
63
- lines = f.readlines()
64
-
65
- func_pattern = re.compile(
66
- r'(function\s+(\w+)|(\w+)\s*=\s*(async\s+)?\([^)]*\)\s*=>|'
67
- r'(async\s+)?(\w+)\s*\([^)]*\)\s*\{)'
68
- )
69
-
70
- in_func = False
71
- func_start = 0
72
- func_name = 'anonymous'
73
- brace_count = 0
74
-
75
- for i, line in enumerate(lines, 1):
76
- if not in_func:
77
- match = func_pattern.search(line)
78
- if match and '{' in line:
79
- in_func = True
80
- func_start = i
81
- func_name = match.group(2) or match.group(3) or match.group(6) or 'anonymous'
82
- brace_count = line.count('{') - line.count('}')
83
- else:
84
- brace_count += line.count('{') - line.count('}')
85
- if brace_count <= 0:
86
- length = i - func_start + 1
87
- if length > max_lines:
88
- violations.append(Violation(
89
- file=str(path), line=func_start, type='function',
90
- name=func_name, actual=length, limit=max_lines
91
- ))
92
- in_func = False
93
- brace_count = 0
94
- except Exception:
95
- pass
96
- return violations
97
-
98
-
99
- def check_go_functions(path: Path, max_lines: int) -> List[Violation]:
100
- """Check Go function lengths."""
101
- violations = []
102
- try:
103
- with open(path, 'r', encoding='utf-8') as f:
104
- lines = f.readlines()
105
-
106
- func_pattern = re.compile(r'^func\s+(\w+|\([^)]+\)\s+\w+)\s*\(')
107
- in_func = False
108
- func_start = 0
109
- func_name = ''
110
- brace_count = 0
111
-
112
- for i, line in enumerate(lines, 1):
113
- if not in_func:
114
- match = func_pattern.match(line)
115
- if match:
116
- in_func = True
117
- func_start = i
118
- func_name = match.group(1).split()[-1]
119
- brace_count = line.count('{') - line.count('}')
120
- else:
121
- brace_count += line.count('{') - line.count('}')
122
- if brace_count <= 0:
123
- length = i - func_start + 1
124
- if length > max_lines:
125
- violations.append(Violation(
126
- file=str(path), line=func_start, type='function',
127
- name=func_name, actual=length, limit=max_lines
128
- ))
129
- in_func = False
130
- except Exception:
131
- pass
132
- return violations
133
-
134
-
135
- def check_java_functions(path: Path, max_lines: int) -> List[Violation]:
136
- """Check Java method lengths."""
137
- violations = []
138
- try:
139
- with open(path, 'r', encoding='utf-8') as f:
140
- lines = f.readlines()
141
-
142
- method_pattern = re.compile(
143
- r'(public|private|protected|static|\s)+[\w<>\[\]]+\s+(\w+)\s*\([^)]*\)\s*(\{|throws)'
144
- )
145
- in_method = False
146
- method_start = 0
147
- method_name = ''
148
- brace_count = 0
149
-
150
- for i, line in enumerate(lines, 1):
151
- if not in_method:
152
- match = method_pattern.search(line)
153
- if match and '{' in line:
154
- in_method = True
155
- method_start = i
156
- method_name = match.group(2)
157
- brace_count = line.count('{') - line.count('}')
158
- else:
159
- brace_count += line.count('{') - line.count('}')
160
- if brace_count <= 0:
161
- length = i - method_start + 1
162
- if length > max_lines:
163
- violations.append(Violation(
164
- file=str(path), line=method_start, type='function',
165
- name=method_name, actual=length, limit=max_lines
166
- ))
167
- in_method = False
168
- except Exception:
169
- pass
170
- return violations
171
-
172
-
173
- def check_rust_functions(path: Path, max_lines: int) -> List[Violation]:
174
- """Check Rust function lengths."""
175
- violations = []
176
- try:
177
- with open(path, 'r', encoding='utf-8') as f:
178
- lines = f.readlines()
179
-
180
- func_pattern = re.compile(r'^\s*(pub\s+)?(async\s+)?fn\s+(\w+)')
181
- in_func = False
182
- func_start = 0
183
- func_name = ''
184
- brace_count = 0
185
-
186
- for i, line in enumerate(lines, 1):
187
- if not in_func:
188
- match = func_pattern.match(line)
189
- if match and '{' in line:
190
- in_func = True
191
- func_start = i
192
- func_name = match.group(3)
193
- brace_count = line.count('{') - line.count('}')
194
- else:
195
- brace_count += line.count('{') - line.count('}')
196
- if brace_count <= 0:
197
- length = i - func_start + 1
198
- if length > max_lines:
199
- violations.append(Violation(
200
- file=str(path), line=func_start, type='function',
201
- name=func_name, actual=length, limit=max_lines
202
- ))
203
- in_func = False
204
- except Exception:
205
- pass
206
- return violations
207
-
208
-
209
- LANG_CHECKERS = {
210
- '.py': check_python_functions,
211
- '.js': check_js_functions,
212
- '.ts': check_js_functions,
213
- '.tsx': check_js_functions,
214
- '.jsx': check_js_functions,
215
- '.go': check_go_functions,
216
- '.java': check_java_functions,
217
- '.rs': check_rust_functions,
218
- }
219
-
220
- SKIP_DIRS = {'node_modules', '.git', 'dist', 'build', '__pycache__', '.venv',
221
- 'venv', 'vendor', 'target', '.next', 'coverage'}
222
-
223
-
224
- def scan_directory(path: Path, max_file: int, max_func: int) -> List[Violation]:
225
- """Scan directory for size violations."""
226
- violations = []
227
-
228
- for root, dirs, files in os.walk(path):
229
- dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
230
-
231
- for file in files:
232
- filepath = Path(root) / file
233
- ext = filepath.suffix.lower()
234
-
235
- if ext not in LANG_CHECKERS:
236
- continue
237
-
238
- # Check file length
239
- file_lines = count_file_lines(filepath)
240
- if file_lines > max_file:
241
- violations.append(Violation(
242
- file=str(filepath), line=1, type='file',
243
- name=file, actual=file_lines, limit=max_file
244
- ))
245
-
246
- # Check function lengths
247
- checker = LANG_CHECKERS.get(ext)
248
- if checker:
249
- violations.extend(checker(filepath, max_func))
250
-
251
- return violations
252
-
253
-
254
- def generate_report(violations: List[Violation]) -> str:
255
- """Generate markdown report."""
256
- if not violations:
257
- return "# Size Check Report\n\n**Status:** ✅ PASS\n\nNo design principle violations found."
258
-
259
- file_v = [v for v in violations if v.type == 'file']
260
- func_v = [v for v in violations if v.type == 'function']
261
-
262
- lines = [
263
- "# Size Check Report",
264
- "",
265
- "**Status:** ⚠️ WARNING",
266
- "",
267
- f"**Total Violations:** {len(violations)}",
268
- f"- Files exceeding limit: {len(file_v)}",
269
- f"- Functions exceeding limit: {len(func_v)}",
270
- ""
271
- ]
272
-
273
- if file_v:
274
- lines.extend([
275
- "## Files Exceeding 500 Lines",
276
- "",
277
- "| File | Lines | Limit |",
278
- "|------|-------|-------|"
279
- ])
280
- for v in sorted(file_v, key=lambda x: -x.actual):
281
- lines.append(f"| `{v.file}` | {v.actual} | {v.limit} |")
282
- lines.append("")
283
-
284
- if func_v:
285
- lines.extend([
286
- "## Functions Exceeding 50 Lines",
287
- "",
288
- "| File | Line | Function | Lines | Limit |",
289
- "|------|------|----------|-------|-------|"
290
- ])
291
- for v in sorted(func_v, key=lambda x: -x.actual):
292
- lines.append(f"| `{v.file}` | {v.line} | `{v.name}` | {v.actual} | {v.limit} |")
293
- lines.append("")
294
-
295
- lines.extend([
296
- "## Remediation",
297
- "",
298
- "### Large Files",
299
- "- Identify logical groupings",
300
- "- Extract to separate modules",
301
- "",
302
- "### Long Functions",
303
- "- Extract helper functions",
304
- "- Apply single responsibility principle"
305
- ])
306
-
307
- return "\n".join(lines)
308
-
309
-
310
- def main():
311
- parser = argparse.ArgumentParser(description='Check code size limits (warning only)')
312
- parser.add_argument('--path', default='.', help='Directory to scan')
313
- parser.add_argument('--max-file-lines', type=int, default=500)
314
- parser.add_argument('--max-function-lines', type=int, default=50)
315
- parser.add_argument('--output', help='Output file (default: stdout)')
316
-
317
- args = parser.parse_args()
318
- violations = scan_directory(Path(args.path), args.max_file_lines, args.max_function_lines)
319
- report = generate_report(violations)
320
-
321
- if args.output:
322
- with open(args.output, 'w') as f:
323
- f.write(report)
324
- print(f"Report saved to {args.output}")
325
- else:
326
- print(report)
327
-
328
- # Exit 1 if violations (for CI awareness), but documented as warning only
329
- sys.exit(1 if violations else 0)
330
-
331
-
332
- if __name__ == '__main__':
333
- main()
@@ -1,126 +0,0 @@
1
- #!/usr/bin/env bash
2
- # AI Sprint Statusline
3
- # Displays: directory, git branch, model, context usage, cost
4
- # Requires: jq (optional - graceful fallback without it)
5
-
6
- set -euo pipefail
7
-
8
- # Check for jq
9
- HAS_JQ=false
10
- command -v jq >/dev/null 2>&1 && HAS_JQ=true
11
-
12
- # Read JSON from stdin
13
- INPUT=$(cat)
14
-
15
- # Fallback without jq
16
- if ! $HAS_JQ; then
17
- echo "🚀 AI Sprint"
18
- exit 0
19
- fi
20
-
21
- # --- Helper Functions ---
22
-
23
- # Expand home directory to ~
24
- expand_home() {
25
- local path="$1"
26
- echo "${path/#$HOME/~}"
27
- }
28
-
29
- # Get git branch
30
- get_git_branch() {
31
- if git rev-parse --git-dir >/dev/null 2>&1; then
32
- git branch --show-current 2>/dev/null || git rev-parse --short HEAD 2>/dev/null || echo ""
33
- fi
34
- }
35
-
36
- # Progress bar (12 chars): ▰▰▰▱▱▱▱▱▱▱▱▱
37
- progress_bar() {
38
- local percent=${1:-0}
39
- local width=12
40
-
41
- # Clamp to 0-100
42
- (( percent < 0 )) && percent=0
43
- (( percent > 100 )) && percent=100
44
-
45
- local filled=$((percent * width / 100))
46
- local empty=$((width - filled))
47
-
48
- local bar=""
49
- for ((i=0; i<filled; i++)); do bar+="▰"; done
50
- for ((i=0; i<empty; i++)); do bar+="▱"; done
51
-
52
- echo "$bar"
53
- }
54
-
55
- # Severity emoji based on percentage
56
- severity_emoji() {
57
- local percent=${1:-0}
58
- if (( percent >= 90 )); then echo "🔴"
59
- elif (( percent >= 70 )); then echo "🟡"
60
- else echo "🟢"
61
- fi
62
- }
63
-
64
- # --- Parse JSON ---
65
-
66
- # Directory
67
- CWD=$(echo "$INPUT" | jq -r '.workspace.current_dir // .cwd // "unknown"')
68
- CWD=$(expand_home "$CWD")
69
-
70
- # Model
71
- MODEL=$(echo "$INPUT" | jq -r '.model.display_name // "Claude"')
72
-
73
- # Context window
74
- INPUT_TOKENS=$(echo "$INPUT" | jq -r '.context_window.total_input_tokens // 0')
75
- OUTPUT_TOKENS=$(echo "$INPUT" | jq -r '.context_window.total_output_tokens // 0')
76
- CONTEXT_SIZE=$(echo "$INPUT" | jq -r '.context_window.context_window_size // 0')
77
-
78
- CONTEXT_PCT=0
79
- if (( CONTEXT_SIZE > 0 )); then
80
- TOTAL_TOKENS=$((INPUT_TOKENS + OUTPUT_TOKENS))
81
- CONTEXT_PCT=$((TOTAL_TOKENS * 100 / CONTEXT_SIZE))
82
- fi
83
-
84
- # Cost (optional)
85
- COST=$(echo "$INPUT" | jq -r '.cost.total_cost_usd // empty')
86
-
87
- # Lines changed
88
- LINES_ADDED=$(echo "$INPUT" | jq -r '.cost.total_lines_added // 0')
89
- LINES_REMOVED=$(echo "$INPUT" | jq -r '.cost.total_lines_removed // 0')
90
-
91
- # --- Build Output ---
92
-
93
- OUTPUT=""
94
-
95
- # Directory
96
- OUTPUT+="📁 $CWD"
97
-
98
- # Git branch
99
- GIT_BRANCH=$(get_git_branch)
100
- if [[ -n "$GIT_BRANCH" ]]; then
101
- OUTPUT+=" 🌿 $GIT_BRANCH"
102
- fi
103
-
104
- # Model
105
- OUTPUT+=" 🤖 $MODEL"
106
-
107
- # Context usage
108
- if (( CONTEXT_PCT > 0 )); then
109
- EMOJI=$(severity_emoji "$CONTEXT_PCT")
110
- BAR=$(progress_bar "$CONTEXT_PCT")
111
- OUTPUT+=" $EMOJI $BAR ${CONTEXT_PCT}%"
112
- fi
113
-
114
- # Cost (only show if non-zero)
115
- if [[ -n "$COST" && "$COST" != "null" && "$COST" != "0" ]]; then
116
- # Format to 4 decimal places
117
- COST_FMT=$(printf "%.4f" "$COST" 2>/dev/null || echo "$COST")
118
- OUTPUT+=" 💵 \$$COST_FMT"
119
- fi
120
-
121
- # Lines changed
122
- if (( LINES_ADDED > 0 || LINES_REMOVED > 0 )); then
123
- OUTPUT+=" 📝 +$LINES_ADDED -$LINES_REMOVED"
124
- fi
125
-
126
- echo "$OUTPUT"
@@ -1,133 +0,0 @@
1
- # Development Rules
2
-
3
- Core principles enforced across all agents and commands.
4
-
5
- ## Core Principles
6
-
7
- 1. **YAGNI** - You Aren't Gonna Need It
8
- 2. **KISS** - Keep It Simple, Stupid
9
- 3. **DRY** - Don't Repeat Yourself
10
- 4. **SRP** - Single Responsibility Principle
11
- 5. **Security-First** - Security in every layer
12
-
13
- ## Design Principles Enforcement
14
-
15
- ### Size Limits (Warning level - non-blocking)
16
- | Metric | Limit | Action if Exceeded |
17
- |--------|-------|-------------------|
18
- | File lines | 500 | Split into modules |
19
- | Function lines | 50 | Extract helpers |
20
- | Parameters | 4 | Use options object |
21
- | Nesting levels | 3 | Use early returns |
22
-
23
- ### YAGNI Checklist
24
- - Only build explicitly requested features
25
- - Delete unused code immediately
26
- - No "future-proofing" abstractions
27
- - No unused parameters "for later"
28
-
29
- ### KISS Checklist
30
- - Prefer explicit over implicit
31
- - Prefer flat over nested
32
- - Prefer composition over inheritance
33
- - No clever code - readable > clever
34
-
35
- ### SRP Checklist
36
- - One file = one concept
37
- - One function = one operation
38
- - If name needs "and" → split it
39
-
40
- ### Automated Checking
41
- Run size checker (warning only):
42
- ```bash
43
- python3 .claude/skills/quality-assurance/scripts/check-size.py
44
- ```
45
-
46
- Included in `/ai-sprint-review` and `/ai-sprint-validate` workflows.
47
-
48
- ## Date Handling
49
-
50
- **CRITICAL**: Never guess dates. Always use bash:
51
- ```bash
52
- date "+%Y-%m-%d" # For reports: 2025-12-24
53
- date "+%y%m%d-%H%M" # For filenames: 251224-2115
54
- ```
55
-
56
- ## Context Engineering
57
-
58
- All AI context stored under `ai_context/`:
59
- ```
60
- ai_context/
61
- ├── plans/ # Implementation plans
62
- ├── docs/ # AI-specific documentation
63
- ├── refs/ # Reference materials
64
- ├── memory/ # Session memory
65
- │ ├── learning.md # Retrospective lessons
66
- │ ├── decisions.md # Key decisions log
67
- │ └── active.md # Current session state
68
- └── reports/ # Agent reports
69
- ```
70
-
71
- ## Memory Integration
72
-
73
- Before any task:
74
- - Check `ai_context/memory/learning.md` for past lessons
75
-
76
- After any task:
77
- - Update `ai_context/memory/learning.md` with new lessons
78
- - Record decisions in `ai_context/memory/decisions.md`
79
-
80
- ## Quality Gates
81
-
82
- ### Code Quality
83
- - [ ] All tests passing
84
- - [ ] >80% code coverage
85
- - [ ] No linting errors
86
- - [ ] Types complete (TypeScript)
87
-
88
- ### Security
89
- - [ ] No hardcoded secrets
90
- - [ ] Input validation present
91
- - [ ] OWASP Top 10 compliant
92
- - [ ] Security scan passed
93
-
94
- ### Documentation
95
- - [ ] Code comments where needed
96
- - [ ] API documented
97
- - [ ] README updated
98
-
99
- ## Agent DO NOT Directives
100
-
101
- Every agent must follow:
102
- - DO NOT guess dates - use bash date command
103
- - DO NOT hardcode secrets
104
- - DO NOT skip security checks
105
- - DO NOT leave failing tests
106
- - DO NOT modify code without reading it first
107
-
108
- ## Output Standards
109
-
110
- ### Reports
111
- - Save to `ai_context/reports/` with timestamped filename
112
- - Use format: `{type}-{YYMMDD}-{slug}.md`
113
- - Include date from bash command
114
-
115
- ### Plans
116
- - Save to `ai_context/ai-sprint-plans/` with timestamped folder
117
- - Use format: `{YYMMDD-HHMM}-{feature}/`
118
- - Include phase files if complex
119
-
120
- ## Review Before Commit
121
-
122
- All code changes must pass:
123
- 1. `/ai-sprint-test` - All tests pass
124
- 2. `/ai-sprint-review` - Code quality check
125
- 3. `/ai-sprint-secure` - Security scan
126
-
127
- ## Human-in-the-Loop Gates
128
-
129
- Require user approval for:
130
- - Production deployments
131
- - Infrastructure changes
132
- - Database migrations
133
- - Security vulnerability fixes