ai-sprint-kit 1.2.1 → 1.3.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/lib/installer.js CHANGED
@@ -17,64 +17,72 @@ async function checkExisting(targetDir) {
17
17
  */
18
18
  async function install(targetDir, options = {}) {
19
19
  const { force = false, skipInstall = false } = options;
20
-
21
20
  const templateDir = path.join(__dirname, '../templates');
22
21
  const claudeDir = path.join(targetDir, '.claude');
23
22
 
24
- // Remove existing if force flag
25
- if (force && await fs.pathExists(claudeDir)) {
26
- const spinner = ora('Removing existing .claude/ directory...').start();
27
- await fs.remove(claudeDir);
28
- spinner.succeed('Removed existing .claude/ directory');
29
- }
23
+ await removeExistingIfForce(claudeDir, force);
30
24
 
31
- // Copy template directory
32
25
  const spinner = ora('Installing framework...').start();
33
-
34
26
  try {
35
- // Copy .claude/ directory
36
- await fs.copy(
37
- path.join(templateDir, '.claude'),
38
- claudeDir,
39
- { overwrite: force }
40
- );
41
-
42
- // Copy root files (including MCP config template)
43
- const rootFiles = ['CLAUDE.md', 'README.md', '.mcp.json.example'];
44
- for (const file of rootFiles) {
45
- const srcPath = path.join(templateDir, file);
46
- const destPath = path.join(targetDir, file);
47
-
48
- if (await fs.pathExists(srcPath)) {
49
- // Only copy if doesn't exist or force flag
50
- if (!await fs.pathExists(destPath) || force) {
51
- await fs.copy(srcPath, destPath);
52
- }
53
- }
54
- }
55
-
56
- // Copy docs directory (user guide)
57
- const docsDir = path.join(templateDir, 'docs');
58
- if (await fs.pathExists(docsDir)) {
59
- await fs.copy(docsDir, path.join(targetDir, 'docs'), { overwrite: force });
60
- }
61
-
27
+ await copyTemplateFiles(templateDir, targetDir, claudeDir, force);
62
28
  spinner.succeed('Framework installed');
63
29
 
64
- // Install skill dependencies
65
30
  if (!skipInstall) {
66
31
  await installSkillDependencies(claudeDir);
67
32
  }
68
-
69
- // Create initial directories
70
33
  await createInitialDirs(targetDir);
71
-
72
34
  } catch (error) {
73
35
  spinner.fail('Installation failed');
74
36
  throw error;
75
37
  }
76
38
  }
77
39
 
40
+ /**
41
+ * Remove existing .claude/ directory if force flag is set
42
+ */
43
+ async function removeExistingIfForce(claudeDir, force) {
44
+ if (force && await fs.pathExists(claudeDir)) {
45
+ const spinner = ora('Removing existing .claude/ directory...').start();
46
+ await fs.remove(claudeDir);
47
+ spinner.succeed('Removed existing .claude/ directory');
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Copy template files to target directory
53
+ */
54
+ async function copyTemplateFiles(templateDir, targetDir, claudeDir, force) {
55
+ // Copy .claude/ directory
56
+ await fs.copy(path.join(templateDir, '.claude'), claudeDir, { overwrite: force });
57
+
58
+ // Copy root files
59
+ await copyRootFiles(templateDir, targetDir, force);
60
+
61
+ // Copy user guide to .claude/docs (avoid overwriting user's docs/)
62
+ const docsDir = path.join(templateDir, 'docs');
63
+ if (await fs.pathExists(docsDir)) {
64
+ await fs.copy(docsDir, path.join(claudeDir, 'docs'), { overwrite: force });
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Copy root template files (CLAUDE.md, README.md, etc.)
70
+ */
71
+ async function copyRootFiles(templateDir, targetDir, force) {
72
+ const rootFiles = ['CLAUDE.md', 'README.md', '.mcp.json.example'];
73
+
74
+ for (const file of rootFiles) {
75
+ const srcPath = path.join(templateDir, file);
76
+ const destPath = path.join(targetDir, file);
77
+
78
+ if (await fs.pathExists(srcPath)) {
79
+ if (!await fs.pathExists(destPath) || force) {
80
+ await fs.copy(srcPath, destPath);
81
+ }
82
+ }
83
+ }
84
+ }
85
+
78
86
  /**
79
87
  * Install Python dependencies for skills
80
88
  */
@@ -129,19 +137,42 @@ async function createInitialDirs(targetDir) {
129
137
  'ai_context/plans',
130
138
  'ai_context/docs',
131
139
  'ai_context/reports',
132
- 'ai_context/memory',
133
- 'docs',
134
- 'plans',
135
- 'plans/reports'
140
+ 'ai_context/reports/research',
141
+ 'ai_context/reports/review',
142
+ 'ai_context/reports/security',
143
+ 'ai_context/reports/test',
144
+ 'ai_context/reports/debug',
145
+ 'ai_context/reports/deploy',
146
+ 'ai_context/reports/docs',
147
+ 'ai_context/memory'
136
148
  ];
137
149
 
138
150
  for (const dir of dirs) {
139
- const dirPath = path.join(targetDir, dir);
140
- await fs.ensureDir(dirPath);
151
+ await fs.ensureDir(path.join(targetDir, dir));
152
+ }
153
+
154
+ await createMemoryFiles(targetDir);
155
+ }
156
+
157
+ /**
158
+ * Create initial memory template files
159
+ */
160
+ async function createMemoryFiles(targetDir) {
161
+ const memoryFiles = getMemoryFileTemplates();
162
+
163
+ for (const [file, content] of Object.entries(memoryFiles)) {
164
+ const filePath = path.join(targetDir, file);
165
+ if (!await fs.pathExists(filePath)) {
166
+ await fs.writeFile(filePath, content);
167
+ }
141
168
  }
169
+ }
142
170
 
143
- // Create initial memory files
144
- const memoryFiles = {
171
+ /**
172
+ * Get memory file templates
173
+ */
174
+ function getMemoryFileTemplates() {
175
+ return {
145
176
  'ai_context/memory/learning.md': `# Learning Log
146
177
 
147
178
  Lessons learned from past development sessions.
@@ -190,13 +221,6 @@ _None_
190
221
  _None_
191
222
  `
192
223
  };
193
-
194
- for (const [file, content] of Object.entries(memoryFiles)) {
195
- const filePath = path.join(targetDir, file);
196
- if (!await fs.pathExists(filePath)) {
197
- await fs.writeFile(filePath, content);
198
- }
199
- }
200
224
  }
201
225
 
202
226
  module.exports = {
package/lib/scanner.js CHANGED
@@ -65,60 +65,43 @@ async function checkRepomix() {
65
65
 
66
66
  /**
67
67
  * Run repomix scan on target directory
68
- * @param {string} targetDir - Directory to scan
69
- * @param {string} outputDir - Output directory for scan results
70
- * @param {object} options - Scan options
71
- * @returns {Promise<object>} - Scan results
72
68
  */
73
69
  async function runRepomixScan(targetDir, outputDir, options = {}) {
74
70
  const { useNpx = false } = options;
75
-
76
- // Create output directory
77
71
  await fs.ensureDir(outputDir);
78
72
 
79
73
  const xmlOutput = path.join(outputDir, 'repomix-output.xml');
80
74
  const mdOutput = path.join(outputDir, 'overview.md');
81
75
 
82
- // Build base command
83
- const baseCmd = useNpx ? 'npx' : 'repomix';
84
- const baseArgs = useNpx ? ['repomix'] : [];
76
+ await runRepomixCommand(targetDir, xmlOutput, 'xml', useNpx);
77
+ await runRepomixCommand(targetDir, mdOutput, 'markdown', useNpx);
85
78
 
86
- // Generate XML output (for AI consumption)
87
- const xmlArgs = [
88
- ...baseArgs,
89
- '--compress',
90
- '--style', 'xml',
91
- '-o', xmlOutput
92
- ];
79
+ return parseRepomixStats(xmlOutput);
80
+ }
93
81
 
94
- await execa(baseCmd, xmlArgs, {
95
- cwd: targetDir,
96
- timeout: 300000 // 5 minute timeout
97
- });
82
+ /**
83
+ * Execute repomix command with specified style
84
+ */
85
+ async function runRepomixCommand(targetDir, outputPath, style, useNpx) {
86
+ const baseCmd = useNpx ? 'npx' : 'repomix';
87
+ const baseArgs = useNpx ? ['repomix'] : [];
98
88
 
99
- // Generate Markdown output (for human reading)
100
- const mdArgs = [
101
- ...baseArgs,
102
- '--compress',
103
- '--style', 'markdown',
104
- '-o', mdOutput
105
- ];
89
+ const args = [...baseArgs, '--compress', '--style', style, '-o', outputPath];
106
90
 
107
- await execa(baseCmd, mdArgs, {
91
+ await execa(baseCmd, args, {
108
92
  cwd: targetDir,
109
93
  timeout: 300000
110
94
  });
95
+ }
111
96
 
112
- // Parse XML to get stats
113
- let stats = {
114
- totalFiles: 0,
115
- totalTokens: 0,
116
- compressedTokens: 0
117
- };
97
+ /**
98
+ * Parse repomix XML output for statistics
99
+ */
100
+ async function parseRepomixStats(xmlPath) {
101
+ const stats = { totalFiles: 0, totalTokens: 0, compressedTokens: 0 };
118
102
 
119
103
  try {
120
- const xmlContent = await fs.readFile(xmlOutput, 'utf-8');
121
- // Count file entries in XML
104
+ const xmlContent = await fs.readFile(xmlPath, 'utf-8');
122
105
  const fileMatches = xmlContent.match(/<file path="/g);
123
106
  if (fileMatches) {
124
107
  stats.totalFiles = fileMatches.length;
@@ -256,25 +239,36 @@ async function writeMetadata(outputDir, stats) {
256
239
 
257
240
  /**
258
241
  * Main entry point for codebase scanning
259
- * @param {string} targetDir - Directory to scan
260
- * @param {object} options - Scan options
261
- * @returns {Promise<object>} - Scan results
262
242
  */
263
243
  async function scanCodebase(targetDir, options = {}) {
264
244
  const { silent = false } = options;
265
245
  const outputDir = path.join(targetDir, 'ai_context', 'codebase');
266
- const startTime = Date.now();
267
246
 
268
- // Check for source code
247
+ // Pre-flight checks
248
+ const preflight = await runPreflightChecks(targetDir, silent);
249
+ if (preflight.skipped) return preflight;
250
+
251
+ const spinner = silent ? null : ora('Scanning codebase...').start();
252
+
253
+ try {
254
+ const result = await executeScan(targetDir, outputDir, preflight.command, spinner);
255
+ if (spinner) spinner.succeed(`Codebase scanned (${result.stats.totalFiles} files, ${result.stats.duration}s)`);
256
+ return result;
257
+ } catch (error) {
258
+ return handleScanError(error, spinner, silent);
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Run pre-flight checks before scanning
264
+ */
265
+ async function runPreflightChecks(targetDir, silent) {
269
266
  const hasSource = await detectSourceCode(targetDir);
270
267
  if (!hasSource) {
271
- if (!silent) {
272
- console.log(chalk.gray(' No source code detected. Skipping scan.'));
273
- }
268
+ if (!silent) console.log(chalk.gray(' No source code detected. Skipping scan.'));
274
269
  return { skipped: true, reason: 'no-source' };
275
270
  }
276
271
 
277
- // Check repomix availability
278
272
  const { available, command } = await checkRepomix();
279
273
  if (!available) {
280
274
  if (!silent) {
@@ -284,54 +278,40 @@ async function scanCodebase(targetDir, options = {}) {
284
278
  return { skipped: true, reason: 'no-repomix' };
285
279
  }
286
280
 
287
- const spinner = silent ? null : ora('Scanning codebase...').start();
288
-
289
- try {
290
- // Create output directory
291
- await fs.ensureDir(outputDir);
292
-
293
- // Create default ignore file
294
- await createRepomixIgnore(outputDir);
295
-
296
- // Run repomix scan
297
- const useNpx = command.includes('npx');
298
- const stats = await runRepomixScan(targetDir, outputDir, { useNpx });
299
-
300
- // Generate structure
301
- if (spinner) spinner.text = 'Generating structure...';
302
- await generateStructure(targetDir, outputDir);
281
+ return { skipped: false, command };
282
+ }
303
283
 
304
- // Calculate duration
305
- stats.duration = Math.round((Date.now() - startTime) / 1000 * 10) / 10;
284
+ /**
285
+ * Execute the actual scan operation
286
+ */
287
+ async function executeScan(targetDir, outputDir, command, spinner) {
288
+ const startTime = Date.now();
306
289
 
307
- // Write metadata
308
- const metadata = await writeMetadata(outputDir, stats);
290
+ await fs.ensureDir(outputDir);
291
+ await createRepomixIgnore(outputDir);
309
292
 
310
- if (spinner) {
311
- spinner.succeed(`Codebase scanned (${stats.totalFiles} files, ${stats.duration}s)`);
312
- }
293
+ const useNpx = command.includes('npx');
294
+ const stats = await runRepomixScan(targetDir, outputDir, { useNpx });
313
295
 
314
- return {
315
- success: true,
316
- outputDir,
317
- stats: metadata
318
- };
296
+ if (spinner) spinner.text = 'Generating structure...';
297
+ await generateStructure(targetDir, outputDir);
319
298
 
320
- } catch (error) {
321
- if (spinner) {
322
- spinner.fail('Codebase scan failed');
323
- }
299
+ stats.duration = Math.round((Date.now() - startTime) / 1000 * 10) / 10;
300
+ const metadata = await writeMetadata(outputDir, stats);
324
301
 
325
- if (!silent) {
326
- console.log(chalk.yellow(` ⚠️ ${error.message}`));
327
- console.log(chalk.gray(' Run /scan manually after fixing the issue.'));
328
- }
302
+ return { success: true, outputDir, stats: metadata };
303
+ }
329
304
 
330
- return {
331
- success: false,
332
- error: error.message
333
- };
305
+ /**
306
+ * Handle scan errors
307
+ */
308
+ function handleScanError(error, spinner, silent) {
309
+ if (spinner) spinner.fail('Codebase scan failed');
310
+ if (!silent) {
311
+ console.log(chalk.yellow(` ⚠️ ${error.message}`));
312
+ console.log(chalk.gray(' Run /scan manually after fixing the issue.'));
334
313
  }
314
+ return { success: false, error: error.message };
335
315
  }
336
316
 
337
317
  module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-sprint-kit",
3
- "version": "1.2.1",
3
+ "version": "1.3.1",
4
4
  "description": "CLI installer for autonomous coding agent framework - security-first, production-grade Claude Code setup",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {
@@ -80,7 +80,8 @@ ai_context/
80
80
  │ ├── learning.md # Bug patterns to watch for
81
81
  │ └── decisions.md # Fix decisions log
82
82
  └── reports/
83
- └── debug-251224-login-bug.md
83
+ └── debug/
84
+ └── debug-251224-login-bug.md
84
85
  ```
85
86
 
86
87
  ## Workflow
@@ -111,7 +112,7 @@ ai_context/
111
112
 
112
113
  ### Phase 4: Document
113
114
  ```
114
- 1. Call Write: ai_context/reports/ai-sprint-debug-{timestamp}-{slug}.md
115
+ 1. Call Write: ai_context/reports/debug/ai-sprint-debug-{timestamp}-{slug}.md
115
116
  2. Update ai_context/memory/learning.md with new pattern
116
117
  ```
117
118
 
@@ -76,7 +76,8 @@ ai_context/
76
76
  │ ├── learning.md # DevOps lessons learned
77
77
  │ └── decisions.md # Infrastructure decisions
78
78
  └── reports/
79
- └── deploy-251224.md
79
+ └── deploy/
80
+ └── deploy-251224.md
80
81
  ```
81
82
 
82
83
  ## Workflow
@@ -108,7 +109,7 @@ ai_context/
108
109
 
109
110
  ### Phase 4: Documentation
110
111
  ```
111
- 1. Call Write: ai_context/reports/ai-sprint-deploy-{timestamp}.md
112
+ 1. Call Write: ai_context/reports/deploy/ai-sprint-deploy-{timestamp}.md
112
113
  2. Document rollback procedures
113
114
  3. Update ai_context/memory/decisions.md
114
115
  ```
@@ -77,7 +77,8 @@ ai_context/
77
77
  ├── memory/
78
78
  │ └── learning.md # Documentation lessons
79
79
  └── reports/
80
- └── docs-audit-251224.md
80
+ └── docs/
81
+ └── docs-audit-251224.md
81
82
  ```
82
83
 
83
84
  ## Workflow
@@ -107,7 +108,7 @@ ai_context/
107
108
 
108
109
  ### Phase 4: Report
109
110
  ```
110
- 1. Call Write: ai_context/reports/ai-sprint-docs-audit-{timestamp}.md
111
+ 1. Call Write: ai_context/reports/docs/ai-sprint-docs-audit-{timestamp}.md
111
112
  2. Document what was updated
112
113
  ```
113
114
 
@@ -20,9 +20,62 @@ You are an **expert software developer** specializing in production-grade code i
20
20
  - **YAGNI** - Don't build what you don't need
21
21
  - **KISS** - Simple solutions are better
22
22
  - **DRY** - Eliminate duplication
23
+ - **SRP** - One function = one operation
23
24
  - **Security-First** - Validate all inputs, no secrets in code
24
25
  - **Test-Driven** - Write tests alongside code
25
26
 
27
+ ## Code Generation Rules
28
+
29
+ ### Size Limits (Warning thresholds)
30
+ - **500 lines max per file** - Split if exceeded
31
+ - **50 lines max per function** - Extract if exceeded
32
+ - **4 parameters max per function** - Use object if exceeded
33
+ - **3 levels max nesting** - Flatten with early returns
34
+
35
+ ### YAGNI Rules
36
+ ```typescript
37
+ // ❌ Unused parameter "for future use"
38
+ function createUser(name: string, options?: UserOptions) {}
39
+
40
+ // ✅ Only what's needed now
41
+ function createUser(name: string) {}
42
+
43
+ // ❌ Abstract class with single implementation
44
+ abstract class BaseRepository<T> { ... }
45
+ class UserRepository extends BaseRepository<User> { ... }
46
+
47
+ // ✅ Concrete implementation
48
+ class UserRepository { ... }
49
+ ```
50
+
51
+ ### KISS Rules
52
+ ```typescript
53
+ // ❌ Clever code
54
+ const result = arr.reduce((a, b) => ({...a, [b.id]: b}), {});
55
+
56
+ // ✅ Readable code
57
+ const result = {};
58
+ for (const item of arr) {
59
+ result[item.id] = item;
60
+ }
61
+ ```
62
+
63
+ ### SRP Rules
64
+ ```typescript
65
+ // ❌ Function does multiple things
66
+ function processUserAndSendEmail(user: User) {
67
+ validateUser(user);
68
+ saveToDatabase(user);
69
+ sendWelcomeEmail(user);
70
+ logAnalytics(user);
71
+ }
72
+
73
+ // ✅ Single responsibility
74
+ function validateUser(user: User): ValidationResult {}
75
+ function saveUser(user: User): Promise<User> {}
76
+ function sendWelcomeEmail(user: User): Promise<void> {}
77
+ ```
78
+
26
79
  ## Tool Usage
27
80
 
28
81
  ### Allowed Tools
@@ -20,9 +20,39 @@ You are an **expert software architect** with deep expertise in system design, t
20
20
  - **YAGNI** - You Aren't Gonna Need It
21
21
  - **KISS** - Keep It Simple, Stupid
22
22
  - **DRY** - Don't Repeat Yourself
23
+ - **SRP** - Single Responsibility Principle
23
24
  - **Security-First** - Security in every decision
24
25
  - **Token Efficiency** - Concise, actionable output
25
26
 
27
+ ## Design Principles Anti-Patterns
28
+
29
+ **Reject these patterns during planning:**
30
+
31
+ ### YAGNI Violations
32
+ - "We might need X later" → Don't plan for hypothetical requirements
33
+ - "Let's add a config option" → Only if currently needed
34
+ - "This should be extensible" → Extensibility isn't free
35
+ - Abstract base classes for single implementation → Premature abstraction
36
+
37
+ ### KISS Violations
38
+ - Abstractions before concrete use cases
39
+ - Generic solutions for specific problems
40
+ - Multiple inheritance hierarchies upfront
41
+ - Over-engineered patterns (Factory of Factories)
42
+
43
+ ### SRP Violations
44
+ - God modules handling multiple domains
45
+ - Utility files with unrelated functions
46
+ - Components mixing UI/logic/data access
47
+ - Files planned to exceed 500 lines
48
+
49
+ ### Planning Checklist
50
+ Before finalizing any plan, ask:
51
+ 1. Is this feature explicitly requested? (YAGNI)
52
+ 2. Does a simpler alternative exist? (KISS)
53
+ 3. Does each module have one clear purpose? (SRP)
54
+ 4. Will any file exceed 500 lines? (Split it)
55
+
26
56
  ## Tool Usage
27
57
 
28
58
  ### Allowed Tools
@@ -82,7 +82,8 @@ ai_context/
82
82
  ├── memory/
83
83
  │ └── learning.md # Research lessons learned
84
84
  └── reports/
85
- └── research-{topic}-251224.md
85
+ └── research/
86
+ └── research-{topic}-251224.md
86
87
  ```
87
88
 
88
89
  ## Workflow
@@ -110,7 +111,7 @@ ai_context/
110
111
 
111
112
  ### Phase 4: Report
112
113
  ```
113
- 1. Call Write: ai_context/reports/research-{topic}-{timestamp}.md
114
+ 1. Call Write: ai_context/reports/research/research-{topic}-{timestamp}.md
114
115
  2. Include all citations with links
115
116
  3. Provide actionable recommendations
116
117
  ```
@@ -18,10 +18,43 @@ You are an **expert code reviewer** specializing in code quality, security analy
18
18
  ## Core Principles
19
19
 
20
20
  - **Security-First** - Every review includes security analysis
21
- - **YAGNI, KISS, DRY** - Simplicity over complexity
21
+ - **YAGNI, KISS, DRY, SRP** - Simplicity over complexity
22
22
  - **Constructive** - Specific, actionable suggestions
23
23
  - **No Nitpicking** - Focus on meaningful improvements
24
24
 
25
+ ## Design Principles Check
26
+
27
+ ### Size Limits (Warning level)
28
+ - [ ] Files < 500 lines
29
+ - [ ] Functions < 50 lines
30
+ - [ ] Parameters < 5 per function
31
+ - [ ] Nesting < 4 levels
32
+
33
+ ### YAGNI Violations to Flag
34
+ - [ ] Unused function parameters
35
+ - [ ] Abstract classes with single implementation
36
+ - [ ] Commented-out code "for reference"
37
+ - [ ] Configuration options without current use
38
+ - [ ] Generic solutions without concrete requirements
39
+
40
+ ### KISS Violations to Flag
41
+ - [ ] Deep inheritance hierarchies (>2 levels)
42
+ - [ ] Overly abstract patterns (Factory of Factories)
43
+ - [ ] Complex conditionals (>3 conditions)
44
+ - [ ] Clever code over readable code
45
+
46
+ ### SRP Violations to Flag
47
+ - [ ] Classes with >7 public methods
48
+ - [ ] Functions with "and" in name/purpose
49
+ - [ ] Mixed concerns (UI+logic, data+formatting)
50
+ - [ ] Utility files with unrelated functions
51
+
52
+ ### Remediation Guidance
53
+ When flagging violations, suggest:
54
+ 1. **YAGNI**: What to remove/simplify
55
+ 2. **KISS**: How to make it simpler
56
+ 3. **SRP**: How to split responsibilities
57
+
25
58
  ## Tool Usage
26
59
 
27
60
  ### Allowed Tools
@@ -74,7 +107,8 @@ ai_context/
74
107
  │ ├── learning.md # Review lessons learned
75
108
  │ └── decisions.md # Code decisions log
76
109
  └── reports/
77
- └── review-251224.md
110
+ └── review/
111
+ └── review-251224.md
78
112
  ```
79
113
 
80
114
  ## Workflow
@@ -98,7 +132,7 @@ ai_context/
98
132
 
99
133
  ### Phase 3: Reporting
100
134
  ```
101
- 1. Call Write: ai_context/reports/ai-sprint-review-{timestamp}.md
135
+ 1. Call Write: ai_context/reports/review/ai-sprint-review-{timestamp}.md
102
136
  2. Categorize by severity (Critical/High/Medium/Low)
103
137
  3. Provide before/after code examples
104
138
  4. Include rationale for each suggestion
@@ -77,7 +77,8 @@ ai_context/
77
77
  │ ├── learning.md # Past security issues to watch for
78
78
  │ └── decisions.md # Security decisions log
79
79
  └── reports/
80
- └── security-251224-2115.md # Security scan results
80
+ └── security/
81
+ └── security-251224-2115.md # Security scan results
81
82
  ```
82
83
 
83
84
  ## Workflow
@@ -100,7 +101,7 @@ ai_context/
100
101
 
101
102
  ### Phase 3: Reporting
102
103
  ```
103
- 1. Call Write: ai_context/reports/security-{timestamp}.md
104
+ 1. Call Write: ai_context/reports/security/security-{timestamp}.md
104
105
  2. Include severity ratings and fixes
105
106
  3. Update ai_context/memory/learning.md if new patterns found
106
107
  ```
@@ -21,6 +21,48 @@ You are an **expert QA engineer** specializing in test generation, coverage anal
21
21
  - **Test Pyramid** - 70% unit, 20% integration, 10% E2E
22
22
  - **Security-Focused** - Test auth, input validation, XSS, SQL injection
23
23
  - **Fast Feedback** - Tests run quickly
24
+ - **Test Simplicity** - Tests should be obvious, not clever
25
+
26
+ ## Test Simplicity Rules
27
+
28
+ ### Size Limits
29
+ - **20 lines max per test function** - Split if exceeded
30
+ - **One assertion concept per test** - Focus on single behavior
31
+ - **Max 3 mocks per test** - Too many = testing wrong abstraction
32
+
33
+ ### YAGNI in Tests
34
+ ```typescript
35
+ // ❌ Over-engineered test helper
36
+ class TestUserFactory {
37
+ static create(overrides?: Partial<User>) { ... }
38
+ static createMany(count: number) { ... }
39
+ static createWithPermissions(...) { ... }
40
+ }
41
+
42
+ // ✅ Simple inline data
43
+ const user = { id: 1, name: 'Test' };
44
+ ```
45
+
46
+ ### KISS in Tests
47
+ ```typescript
48
+ // ❌ Clever shared setup
49
+ beforeEach(() => {
50
+ jest.spyOn(module, 'method').mockImplementation(...)
51
+ // 20 lines of setup
52
+ });
53
+
54
+ // ✅ Explicit per-test setup
55
+ it('does X', () => {
56
+ const mock = jest.fn().mockReturnValue('result');
57
+ expect(someFunction(mock)).toBe('expected');
58
+ });
59
+ ```
60
+
61
+ ### Test Smells to Avoid
62
+ - [ ] Tests longer than code they test
63
+ - [ ] Complex test helpers that need tests
64
+ - [ ] Shared mutable state between tests
65
+ - [ ] Testing implementation details
24
66
 
25
67
  ## Tool Usage
26
68
 
@@ -77,7 +119,8 @@ ai_context/
77
119
  ├── memory/
78
120
  │ └── learning.md # Testing lessons learned
79
121
  └── reports/
80
- └── test-coverage-251224.md
122
+ └── test/
123
+ └── test-coverage-251224.md
81
124
  ```
82
125
 
83
126
  ## Workflow
@@ -108,7 +151,7 @@ ai_context/
108
151
 
109
152
  ### Phase 4: Reporting
110
153
  ```
111
- 1. Call Write: ai_context/reports/ai-sprint-test-coverage-{timestamp}.md
154
+ 1. Call Write: ai_context/reports/test/ai-sprint-test-coverage-{timestamp}.md
112
155
  2. Document coverage metrics
113
156
  3. Note gaps and recommendations
114
157
  ```
@@ -252,11 +252,11 @@ Before debugging:
252
252
 
253
253
  After debugging:
254
254
  - Update `ai_context/memory/learning.md` with new bug pattern
255
- - Save report to `ai_context/reports/ai-sprint-debug-{timestamp}-{slug}.md`
255
+ - Save report to `ai_context/reports/debug/ai-sprint-debug-{timestamp}-{slug}.md`
256
256
 
257
257
  ## Debug Report
258
258
 
259
- Save to: `ai_context/reports/ai-sprint-debug-YYMMDD-slug.md`
259
+ Save to: `ai_context/reports/debug/ai-sprint-debug-YYMMDD-slug.md`
260
260
 
261
261
  ```markdown
262
262
  # Bug Fix Report
@@ -27,6 +27,17 @@ Perform comprehensive code quality review focusing on security, maintainability,
27
27
  - Identify security issues
28
28
  - Analyze performance
29
29
 
30
+ ### 1.5. Design Principles Check (Warning)
31
+ Run size checker:
32
+ ```bash
33
+ python3 .claude/skills/quality-assurance/scripts/check-size.py --path $SCOPE
34
+ ```
35
+ Flag (warning only):
36
+ - Files >500 lines
37
+ - Functions >50 lines
38
+ - YAGNI violations (unused abstractions)
39
+ - SRP violations (mixed concerns)
40
+
30
41
  ### 2. Security Review (Critical)
31
42
  - OWASP Top 10 compliance
32
43
  - SQL injection vulnerabilities
@@ -73,6 +84,12 @@ Perform comprehensive code quality review focusing on security, maintainability,
73
84
  - Minor optimizations
74
85
  - Naming suggestions
75
86
 
87
+ ### 🟡 Design Principle Warnings
88
+ - Files exceeding 500 lines
89
+ - Functions exceeding 50 lines
90
+ - Over-engineered abstractions
91
+ - Mixed responsibilities (SRP)
92
+
76
93
  ## Example Review Report
77
94
 
78
95
  ```markdown
@@ -36,6 +36,15 @@ Check `ai_context/memory/learning.md` for known validation issues.
36
36
  - Analyze complexity
37
37
  - Identify code smells
38
38
 
39
+ ### 2.5. Design Principles Check (Warning)
40
+ Run size checker:
41
+ ```bash
42
+ python3 .claude/skills/quality-assurance/scripts/check-size.py
43
+ ```
44
+ Flag as warnings (non-blocking):
45
+ - Files >500 lines
46
+ - Functions >50 lines
47
+
39
48
  ### 3. Security Scan
40
49
  - SAST scanning
41
50
  - Secret detection
@@ -57,10 +66,15 @@ Save to: `ai_context/reports/ai-sprint-validate-{timestamp}.md`
57
66
  ### Code Quality (Reviewer Agent)
58
67
  - [ ] No linting errors
59
68
  - [ ] Types complete
60
- - [ ] Functions < 50 lines
61
69
  - [ ] No code smells
62
70
  - [ ] Documentation present
63
71
 
72
+ ### Design Principles (Warning only)
73
+ - [ ] Files < 500 lines
74
+ - [ ] Functions < 50 lines
75
+ - [ ] No YAGNI violations
76
+ - [ ] No SRP violations
77
+
64
78
  ### Security (Security Agent)
65
79
  - [ ] No hardcoded secrets
66
80
  - [ ] Input validation present
@@ -85,6 +99,7 @@ Save to: `ai_context/reports/ai-sprint-validate-{timestamp}.md`
85
99
  | Tests | ✅/❌ | X |
86
100
  | Coverage | ✅/❌ | X% |
87
101
  | Quality | ✅/❌ | X |
102
+ | Design | ⚠️/✅ | X |
88
103
  | Security | ✅/❌ | X |
89
104
 
90
105
  ## Test Results
@@ -0,0 +1,333 @@
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()
@@ -7,7 +7,43 @@ Core principles enforced across all agents and commands.
7
7
  1. **YAGNI** - You Aren't Gonna Need It
8
8
  2. **KISS** - Keep It Simple, Stupid
9
9
  3. **DRY** - Don't Repeat Yourself
10
- 4. **Security-First** - Security in every layer
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.
11
47
 
12
48
  ## Date Handling
13
49
 
@@ -118,11 +118,14 @@ project/
118
118
  ├── ai_context/ # AI artifacts (not project code)
119
119
  │ ├── plans/ # Implementation plans
120
120
  │ ├── docs/ # AI-generated documentation
121
- │ ├── reports/ # Agent outputs
122
- │ │ ├── review-*.md # Code review reports
123
- │ │ ├── test-*.md # Test coverage reports
124
- │ │ ├── security-*.md # Security scan results
125
- │ │ └── debug-*.md # Debugging analysis
121
+ │ ├── reports/ # Agent outputs (organized by type)
122
+ │ │ ├── research/ # Research reports
123
+ │ │ ├── review/ # Code review reports
124
+ │ │ ├── security/ # Security scan results
125
+ │ │ ├── test/ # Test coverage reports
126
+ │ │ ├── debug/ # Debugging analysis
127
+ │ │ ├── deploy/ # Deployment reports
128
+ │ │ └── docs/ # Documentation audit reports
126
129
  │ └── memory/
127
130
  │ │ ├── learning.md # Lessons learned
128
131
  │ │ ├── decisions.md # Key decisions
@@ -337,11 +337,11 @@ git commit -m "เพิ่มฟีเจอร์ใหม่"
337
337
 
338
338
  ### 3. อ่านรายงานที่ AI สร้าง
339
339
 
340
- AI จะบันทึกผลการทำงานไว้ใน `ai_context/reports/`
340
+ AI จะบันทึกผลการทำงานไว้ใน `ai_context/reports/` (แยกโฟลเดอร์ตามประเภท)
341
341
 
342
342
  ```bash
343
343
  # ดูรายงานความปลอดภัย
344
- cat ai_context/reports/security-*.md
344
+ cat ai_context/reports/security/
345
345
  ```
346
346
 
347
347
  ### 4. ใช้ /ai-sprint-auto สำหรับงานง่ายๆ
@@ -391,7 +391,7 @@ cat ai_context/reports/security-*.md
391
391
 
392
392
  ```bash
393
393
  # ดูรายงาน
394
- cat ai_context/reports/security-*.md
394
+ cat ai_context/reports/security/
395
395
 
396
396
  # แก้ไข
397
397
  /ai-sprint-code "แก้ไขปัญหาความปลอดภัยตามรายงาน"
@@ -105,7 +105,7 @@ For more control over each phase:
105
105
  ```bash
106
106
  # Step 1: Investigate the issue
107
107
  /ai-sprint-debug "users getting 500 error when submitting registration form"
108
- # Review the analysis in ai_context/reports/
108
+ # Review the analysis in ai_context/reports/debug/
109
109
 
110
110
  # Step 2: Implement the fix
111
111
  /ai-sprint-code "fix the registration error based on debug analysis"
@@ -124,7 +124,7 @@ For more control over each phase:
124
124
  # Scan specific directory
125
125
  /ai-sprint-secure src/auth/
126
126
 
127
- # Review findings in ai_context/reports/security-*.md
127
+ # Review findings in ai_context/reports/security/
128
128
  ```
129
129
 
130
130
  ### Tutorial 5: Pre-Commit Validation
@@ -211,7 +211,7 @@ Generates tests and runs them with coverage.
211
211
  /ai-sprint-test "add integration tests for payment API"
212
212
  ```
213
213
 
214
- **Output:** `ai_context/reports/test-*.md`
214
+ **Output:** `ai_context/reports/test/`
215
215
 
216
216
  **Requirements:** 80%+ coverage required.
217
217
 
@@ -230,7 +230,7 @@ Analyzes code quality and best practices.
230
230
  /ai-sprint-review src/auth/login.ts
231
231
  ```
232
232
 
233
- **Output:** `ai_context/reports/review-*.md`
233
+ **Output:** `ai_context/reports/review/`
234
234
 
235
235
  **Checks:**
236
236
  - Code quality and patterns
@@ -254,7 +254,7 @@ Comprehensive security analysis.
254
254
  /ai-sprint-secure src/auth/
255
255
  ```
256
256
 
257
- **Output:** `ai_context/reports/security-*.md`
257
+ **Output:** `ai_context/reports/security/`
258
258
 
259
259
  **Scans:**
260
260
  - SAST (static analysis)
@@ -310,7 +310,7 @@ Investigates issues and bugs.
310
310
  /ai-sprint-debug "slow API response times on /users endpoint"
311
311
  ```
312
312
 
313
- **Output:** `ai_context/reports/debug-*.md`
313
+ **Output:** `ai_context/reports/debug/`
314
314
 
315
315
  **Provides:**
316
316
  - Root cause analysis
@@ -475,8 +475,8 @@ After every `/ai-sprint-secure` scan:
475
475
 
476
476
  ```bash
477
477
  # Check reports
478
- ls ai_context/reports/security-*.md
479
- cat ai_context/reports/security-latest.md
478
+ ls ai_context/reports/security/
479
+ cat ai_context/reports/security/security-*.md
480
480
  ```
481
481
 
482
482
  ### 5. Configure MCP Tools
@@ -532,7 +532,7 @@ For complex features, maintain control:
532
532
 
533
533
  ```bash
534
534
  # View the report
535
- cat ai_context/reports/security-*.md
535
+ cat ai_context/reports/security/
536
536
 
537
537
  # Fix issues
538
538
  /ai-sprint-code "fix security issues from the security scan"