claudex-setup 0.1.0 → 0.2.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/src/setup.js CHANGED
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * Setup engine - applies recommended Claude Code configuration to a project.
3
+ * v0.3.0 - Smart CLAUDE.md generation with project analysis.
3
4
  */
4
5
 
5
6
  const fs = require('fs');
@@ -8,31 +9,376 @@ const { TECHNIQUES, STACKS } = require('./techniques');
8
9
  const { ProjectContext } = require('./context');
9
10
  const { audit } = require('./audit');
10
11
 
12
+ // ============================================================
13
+ // Helper: detect project scripts from package.json
14
+ // ============================================================
15
+ function detectScripts(ctx) {
16
+ const pkg = ctx.jsonFile('package.json');
17
+ if (!pkg || !pkg.scripts) return {};
18
+ const relevant = ['test', 'build', 'lint', 'dev', 'start', 'format', 'typecheck', 'check'];
19
+ const found = {};
20
+ for (const key of relevant) {
21
+ if (pkg.scripts[key]) {
22
+ found[key] = pkg.scripts[key];
23
+ }
24
+ }
25
+ return found;
26
+ }
27
+
28
+ // ============================================================
29
+ // Helper: detect main directories
30
+ // ============================================================
31
+ function detectMainDirs(ctx) {
32
+ const candidates = ['src', 'lib', 'app', 'pages', 'components', 'api', 'routes', 'utils', 'helpers', 'services', 'models', 'controllers', 'views', 'public', 'assets', 'config', 'tests', 'test', '__tests__', 'spec', 'scripts', 'prisma', 'db', 'middleware'];
33
+ const found = [];
34
+ for (const dir of candidates) {
35
+ if (ctx.hasDir(dir)) {
36
+ const files = ctx.dirFiles(dir);
37
+ found.push({ name: dir, fileCount: files.length, files: files.slice(0, 10) });
38
+ }
39
+ }
40
+ return found;
41
+ }
42
+
43
+ // ============================================================
44
+ // Helper: generate Mermaid diagram from directory structure
45
+ // ============================================================
46
+ function generateMermaid(dirs, stacks) {
47
+ const stackKeys = stacks.map(s => s.key);
48
+ const dirNames = dirs.map(d => d.name);
49
+
50
+ // Build nodes based on what exists
51
+ const nodes = [];
52
+ const edges = [];
53
+ let nodeId = 0;
54
+ const ids = {};
55
+
56
+ function addNode(label, shape) {
57
+ const id = String.fromCharCode(65 + nodeId++); // A, B, C...
58
+ ids[label] = id;
59
+ if (shape === 'db') return ` ${id}[(${label})]`;
60
+ if (shape === 'round') return ` ${id}(${label})`;
61
+ return ` ${id}[${label}]`;
62
+ }
63
+
64
+ // Entry point
65
+ nodes.push(addNode('Entry Point', 'round'));
66
+
67
+ // Detect layers
68
+ if (dirNames.includes('app') || dirNames.includes('pages')) {
69
+ nodes.push(addNode('Pages / Routes', 'default'));
70
+ edges.push(` ${ids['Entry Point']} --> ${ids['Pages / Routes']}`);
71
+ }
72
+
73
+ if (dirNames.includes('components')) {
74
+ nodes.push(addNode('Components', 'default'));
75
+ const parent = ids['Pages / Routes'] || ids['Entry Point'];
76
+ edges.push(` ${parent} --> ${ids['Components']}`);
77
+ }
78
+
79
+ if (dirNames.includes('src')) {
80
+ nodes.push(addNode('src/', 'default'));
81
+ const parent = ids['Pages / Routes'] || ids['Entry Point'];
82
+ edges.push(` ${parent} --> ${ids['src/']}`);
83
+ }
84
+
85
+ if (dirNames.includes('lib')) {
86
+ nodes.push(addNode('lib/', 'default'));
87
+ const parent = ids['src/'] || ids['Entry Point'];
88
+ edges.push(` ${parent} --> ${ids['lib/']}`);
89
+ }
90
+
91
+ if (dirNames.includes('api') || dirNames.includes('routes') || dirNames.includes('controllers')) {
92
+ const label = dirNames.includes('api') ? 'API Layer' : 'Routes';
93
+ nodes.push(addNode(label, 'default'));
94
+ const parent = ids['src/'] || ids['Entry Point'];
95
+ edges.push(` ${parent} --> ${ids[label]}`);
96
+ }
97
+
98
+ if (dirNames.includes('services')) {
99
+ nodes.push(addNode('Services', 'default'));
100
+ const parent = ids['API Layer'] || ids['Routes'] || ids['src/'] || ids['Entry Point'];
101
+ edges.push(` ${parent} --> ${ids['Services']}`);
102
+ }
103
+
104
+ if (dirNames.includes('models') || dirNames.includes('prisma') || dirNames.includes('db')) {
105
+ nodes.push(addNode('Data Layer', 'default'));
106
+ const parent = ids['Services'] || ids['API Layer'] || ids['Routes'] || ids['src/'] || ids['Entry Point'];
107
+ edges.push(` ${parent} --> ${ids['Data Layer']}`);
108
+ nodes.push(addNode('Database', 'db'));
109
+ edges.push(` ${ids['Data Layer']} --> ${ids['Database']}`);
110
+ }
111
+
112
+ if (dirNames.includes('utils') || dirNames.includes('helpers')) {
113
+ nodes.push(addNode('Utils', 'default'));
114
+ const parent = ids['src/'] || ids['Services'] || ids['Entry Point'];
115
+ edges.push(` ${parent} --> ${ids['Utils']}`);
116
+ }
117
+
118
+ if (dirNames.includes('middleware')) {
119
+ nodes.push(addNode('Middleware', 'default'));
120
+ const parent = ids['API Layer'] || ids['Routes'] || ids['Entry Point'];
121
+ edges.push(` ${parent} --> ${ids['Middleware']}`);
122
+ }
123
+
124
+ if (dirNames.includes('tests') || dirNames.includes('test') || dirNames.includes('__tests__') || dirNames.includes('spec')) {
125
+ nodes.push(addNode('Tests', 'round'));
126
+ const parent = ids['src/'] || ids['Entry Point'];
127
+ edges.push(` ${ids['Tests']} -.-> ${parent}`);
128
+ }
129
+
130
+ // Fallback: if we only have Entry Point, make a generic diagram
131
+ if (nodes.length <= 1) {
132
+ return `\`\`\`mermaid
133
+ graph TD
134
+ A[Entry Point] --> B[Core Logic]
135
+ B --> C[Data Layer]
136
+ B --> D[API / Routes]
137
+ C --> E[(Database)]
138
+ D --> F[External Services]
139
+ \`\`\`
140
+ <!-- Update this diagram to match your actual architecture -->`;
141
+ }
142
+
143
+ return '```mermaid\ngraph TD\n' + nodes.join('\n') + '\n' + edges.join('\n') + '\n```';
144
+ }
145
+
146
+ // ============================================================
147
+ // Helper: framework-specific instructions
148
+ // ============================================================
149
+ function getFrameworkInstructions(stacks) {
150
+ const stackKeys = stacks.map(s => s.key);
151
+ const sections = [];
152
+
153
+ if (stackKeys.includes('nextjs')) {
154
+ sections.push(`### Next.js
155
+ - Use App Router conventions (app/ directory) when applicable
156
+ - Prefer Server Components by default; add 'use client' only when needed
157
+ - Use next/image for images, next/link for navigation
158
+ - API routes go in app/api/ (App Router) or pages/api/ (Pages Router)
159
+ - Use loading.tsx, error.tsx, and not-found.tsx for route-level UX`);
160
+ } else if (stackKeys.includes('react')) {
161
+ sections.push(`### React
162
+ - Use functional components with hooks exclusively
163
+ - Prefer named exports over default exports
164
+ - Keep components under 150 lines; extract sub-components
165
+ - Use custom hooks to share stateful logic
166
+ - Colocate styles, tests, and types with components`);
167
+ }
168
+
169
+ if (stackKeys.includes('vue')) {
170
+ sections.push(`### Vue
171
+ - Use Composition API with \`<script setup>\` syntax
172
+ - Prefer defineProps/defineEmits macros
173
+ - Keep components under 200 lines
174
+ - Use composables for shared logic`);
175
+ }
176
+
177
+ if (stackKeys.includes('angular')) {
178
+ sections.push(`### Angular
179
+ - Use standalone components when possible
180
+ - Follow Angular style guide naming conventions
181
+ - Use reactive forms over template-driven forms
182
+ - Keep services focused on a single responsibility`);
183
+ }
184
+
185
+ if (stackKeys.includes('typescript')) {
186
+ sections.push(`### TypeScript
187
+ - Use \`interface\` for object shapes, \`type\` for unions/intersections
188
+ - Enable strict mode in tsconfig.json
189
+ - Avoid \`any\` — use \`unknown\` and narrow with type guards
190
+ - Prefer \`as const\` assertions over enum when practical
191
+ - Export types alongside their implementations`);
192
+ }
193
+
194
+ if (stackKeys.includes('django')) {
195
+ sections.push(`### Django
196
+ - Follow fat models, thin views pattern
197
+ - Use class-based views for complex logic, function views for simple
198
+ - Always use Django ORM; avoid raw SQL unless necessary
199
+ - Keep business logic in models or services, not views`);
200
+ } else if (stackKeys.includes('fastapi')) {
201
+ sections.push(`### FastAPI
202
+ - Use Pydantic models for request/response validation
203
+ - Use dependency injection for shared logic
204
+ - Keep route handlers thin; delegate to service functions
205
+ - Use async def for I/O-bound endpoints`);
206
+ }
207
+
208
+ if (stackKeys.includes('python') || stackKeys.includes('django') || stackKeys.includes('fastapi')) {
209
+ sections.push(`### Python
210
+ - Use type hints on all function signatures and return types
211
+ - Follow PEP 8; use f-strings for formatting
212
+ - Prefer pathlib over os.path
213
+ - Use dataclasses or pydantic for structured data
214
+ - Raise specific exceptions; never bare \`except:\``);
215
+ }
216
+
217
+ if (stackKeys.includes('rust')) {
218
+ sections.push(`### Rust
219
+ - Prefer Result<T, E> over unwrap/expect in library code
220
+ - Use clippy warnings as errors
221
+ - Derive common traits (Debug, Clone, PartialEq) where appropriate
222
+ - Use modules to organize code; keep lib.rs/main.rs thin`);
223
+ }
224
+
225
+ if (stackKeys.includes('go')) {
226
+ sections.push(`### Go
227
+ - Follow standard project layout conventions
228
+ - Handle all errors explicitly; no blank _ for errors
229
+ - Use interfaces for testability and abstraction
230
+ - Keep packages focused; avoid circular dependencies`);
231
+ }
232
+
233
+ const hasJS = stackKeys.some(k => ['react', 'vue', 'angular', 'nextjs', 'node', 'svelte'].includes(k));
234
+ if (hasJS && !stackKeys.includes('typescript')) {
235
+ sections.push(`### JavaScript
236
+ - Use \`const\` by default, \`let\` when reassignment needed; never \`var\`
237
+ - Use \`async/await\` over raw Promises
238
+ - Use named exports over default exports
239
+ - Import order: stdlib > external > internal > relative`);
240
+ }
241
+
242
+ return sections.join('\n\n');
243
+ }
244
+
245
+ // ============================================================
246
+ // TEMPLATES
247
+ // ============================================================
248
+
11
249
  const TEMPLATES = {
12
- 'claude-md': (stacks) => {
250
+ 'claude-md': (stacks, ctx) => {
13
251
  const stackNames = stacks.map(s => s.label).join(', ') || 'General';
14
- return `# Project Instructions
252
+ const stackKeys = stacks.map(s => s.key);
15
253
 
16
- ## Architecture
17
- <!-- Add a Mermaid diagram of your project structure -->
254
+ // --- Detect project details ---
255
+ const scripts = detectScripts(ctx);
256
+ const mainDirs = detectMainDirs(ctx);
257
+ const hasTS = stackKeys.includes('typescript') || ctx.files.includes('tsconfig.json');
258
+ const hasPython = stackKeys.includes('python') || stackKeys.includes('django') || stackKeys.includes('fastapi');
259
+ const hasJS = stackKeys.some(k => ['react', 'vue', 'angular', 'nextjs', 'node', 'svelte'].includes(k));
260
+
261
+ // --- Build commands section ---
262
+ let buildSection = '';
263
+ if (Object.keys(scripts).length > 0) {
264
+ const lines = [];
265
+ if (scripts.dev) lines.push(`npm run dev # ${scripts.dev}`);
266
+ if (scripts.start) lines.push(`npm start # ${scripts.start}`);
267
+ if (scripts.build) lines.push(`npm run build # ${scripts.build}`);
268
+ if (scripts.test) lines.push(`npm test # ${scripts.test}`);
269
+ if (scripts.lint) lines.push(`npm run lint # ${scripts.lint}`);
270
+ if (scripts.format) lines.push(`npm run format # ${scripts.format}`);
271
+ if (scripts.typecheck) lines.push(`npm run typecheck # ${scripts.typecheck}`);
272
+ if (scripts.check) lines.push(`npm run check # ${scripts.check}`);
273
+ buildSection = lines.join('\n');
274
+ } else if (hasPython) {
275
+ buildSection = `python -m pytest # run tests
276
+ python -m mypy . # type checking
277
+ ruff check . # lint`;
278
+ } else if (hasJS) {
279
+ buildSection = `npm run build # or: npx tsc --noEmit
280
+ npm test # or: npx jest / npx vitest
281
+ npm run lint # or: npx eslint .`;
282
+ } else {
283
+ buildSection = '# Add your build command\n# Add your test command\n# Add your lint command';
284
+ }
285
+
286
+ // --- Architecture description ---
287
+ const mermaid = generateMermaid(mainDirs, stacks);
288
+
289
+ let dirDescription = '';
290
+ if (mainDirs.length > 0) {
291
+ dirDescription = '\n### Directory Structure\n';
292
+ for (const dir of mainDirs) {
293
+ const suffix = dir.fileCount > 0 ? ` (${dir.fileCount} files)` : '';
294
+ dirDescription += `- \`${dir.name}/\`${suffix}\n`;
295
+ }
296
+ }
18
297
 
298
+ // --- Framework-specific instructions ---
299
+ const frameworkInstructions = getFrameworkInstructions(stacks);
300
+ const stackSection = frameworkInstructions
301
+ ? `\n## Stack-Specific Guidelines\n\n${frameworkInstructions}\n`
302
+ : '';
303
+
304
+ // --- TypeScript-specific additions ---
305
+ let tsSection = '';
306
+ if (hasTS) {
307
+ const tsconfig = ctx.jsonFile('tsconfig.json');
308
+ if (tsconfig) {
309
+ const strict = tsconfig.compilerOptions && tsconfig.compilerOptions.strict;
310
+ tsSection = `
311
+ ## TypeScript Configuration
312
+ - Strict mode: ${strict ? '**enabled**' : '**disabled** (consider enabling)'}
313
+ - Always fix type errors before committing — do not use \`@ts-ignore\`
314
+ - Run type checking: \`${scripts.typecheck ? 'npm run typecheck' : 'npx tsc --noEmit'}\`
315
+ `;
316
+ }
317
+ }
318
+
319
+ // --- Verification criteria based on detected commands ---
320
+ const verificationSteps = [];
321
+ verificationSteps.push('1. All existing tests still pass');
322
+ verificationSteps.push('2. New code has test coverage');
323
+ if (scripts.lint || hasPython) {
324
+ verificationSteps.push(`3. No linting errors (\`${scripts.lint ? 'npm run lint' : 'ruff check .'}\`)`);
325
+ } else if (hasJS) {
326
+ verificationSteps.push('3. No linting errors (`npx eslint .`)');
327
+ } else {
328
+ verificationSteps.push('3. No linting errors introduced');
329
+ }
330
+ if (scripts.build) {
331
+ verificationSteps.push(`4. Build succeeds (\`npm run build\`)`);
332
+ }
333
+ if (hasTS) {
334
+ verificationSteps.push(`${verificationSteps.length + 1}. No TypeScript errors (\`${scripts.typecheck ? 'npm run typecheck' : 'npx tsc --noEmit'}\`)`);
335
+ }
336
+ verificationSteps.push(`${verificationSteps.length + 1}. Changes match the requested scope (no gold-plating)`);
337
+
338
+ // --- Read package.json for project name/description ---
339
+ const pkg = ctx.jsonFile('package.json');
340
+ const projectName = (pkg && pkg.name) ? pkg.name : path.basename(ctx.dir);
341
+ const projectDesc = (pkg && pkg.description) ? ` — ${pkg.description}` : '';
342
+
343
+ // --- Assemble the final CLAUDE.md ---
344
+ return `# ${projectName}${projectDesc}
345
+
346
+ ## Architecture
347
+ ${mermaid}
348
+ ${dirDescription}
19
349
  ## Stack
20
350
  ${stackNames}
21
-
351
+ ${stackSection}${tsSection}
22
352
  ## Build & Test
23
353
  \`\`\`bash
24
- # Add your build command
25
- # Add your test command
26
- # Add your lint command
354
+ ${buildSection}
27
355
  \`\`\`
28
356
 
29
357
  ## Code Style
30
358
  - Follow existing patterns in the codebase
31
359
  - Write tests for new features
360
+ - Keep functions small and focused (< 50 lines)
361
+ - Use descriptive variable names; avoid abbreviations
362
+
363
+ <constraints>
364
+ - Never commit secrets, API keys, or .env files
365
+ - Always run tests before marking work complete
366
+ - Prefer editing existing files over creating new ones
367
+ - When uncertain about architecture, ask before implementing
368
+ ${hasTS ? '- Do not use @ts-ignore or @ts-expect-error without a tracking issue\n' : ''}\
369
+ ${hasJS ? '- Use const by default; never use var\n' : ''}\
370
+ </constraints>
371
+
372
+ <verification>
373
+ Before completing any task, confirm:
374
+ ${verificationSteps.join('\n')}
375
+ </verification>
32
376
 
33
377
  ## Workflow
34
378
  - Verify changes with tests before committing
35
- - Use descriptive commit messages
379
+ - Use descriptive commit messages (why, not what)
380
+ - Create focused PRs — one concern per PR
381
+ - Document non-obvious decisions in code comments
36
382
  `;
37
383
  },
38
384
 
@@ -42,6 +388,50 @@ ${stackNames}
42
388
  # Customize the linter command for your project
43
389
  TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
44
390
  echo "[$TIMESTAMP] File changed: $(cat -)" >> .claude/logs/changes.txt
391
+ `,
392
+ 'protect-secrets.sh': `#!/bin/bash
393
+ # PreToolUse hook - warn before touching sensitive files
394
+ # Prevents accidental reads/writes to files containing secrets
395
+
396
+ INPUT=$(cat -)
397
+ FILE=$(echo "$INPUT" | grep -oP '"file_path"\\s*:\\s*"\\K[^"]+' 2>/dev/null || echo "")
398
+
399
+ if [ -z "$FILE" ]; then
400
+ exit 0
401
+ fi
402
+
403
+ BASENAME=$(basename "$FILE")
404
+
405
+ case "$BASENAME" in
406
+ .env|.env.*|*.pem|*.key|credentials.json|secrets.yaml|secrets.yml)
407
+ echo "WARN: Attempting to access sensitive file: $BASENAME"
408
+ echo "This file may contain secrets. Proceed with caution."
409
+ ;;
410
+ esac
411
+
412
+ exit 0
413
+ `,
414
+ 'log-changes.sh': `#!/bin/bash
415
+ # PostToolUse hook - logs all file changes with timestamps
416
+ # Appends to .claude/logs/file-changes.log
417
+
418
+ INPUT=$(cat -)
419
+ TOOL_NAME=$(echo "$INPUT" | grep -oP '"tool_name"\\s*:\\s*"\\K[^"]+' 2>/dev/null || echo "unknown")
420
+ FILE_PATH=$(echo "$INPUT" | grep -oP '"file_path"\\s*:\\s*"\\K[^"]+' 2>/dev/null || echo "")
421
+
422
+ if [ -z "$FILE_PATH" ]; then
423
+ exit 0
424
+ fi
425
+
426
+ LOG_DIR=".claude/logs"
427
+ LOG_FILE="$LOG_DIR/file-changes.log"
428
+
429
+ mkdir -p "$LOG_DIR"
430
+
431
+ TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
432
+ echo "[$TIMESTAMP] $TOOL_NAME: $FILE_PATH" >> "$LOG_FILE"
433
+
434
+ exit 0
45
435
  `,
46
436
  }),
47
437
 
@@ -59,6 +449,32 @@ echo "[$TIMESTAMP] File changed: $(cat -)" >> .claude/logs/changes.txt
59
449
  1. Run \`git diff\` to see all changes
60
450
  2. Check for: bugs, security issues, missing tests, code style
61
451
  3. Provide actionable feedback
452
+ `,
453
+ 'deploy.md': `Pre-deployment checklist and deployment steps.
454
+
455
+ ## Pre-deploy checks:
456
+ 1. Run \`git status\` — working tree must be clean
457
+ 2. Run full test suite — all tests must pass
458
+ 3. Run linter — no errors allowed
459
+ 4. Check for TODO/FIXME/HACK comments in changed files
460
+ 5. Verify no secrets in staged changes (\`git diff --cached\`)
461
+
462
+ ## Deploy steps:
463
+ 1. Confirm target environment (staging vs production)
464
+ 2. Review the diff since last deploy tag
465
+ 3. Run the deployment command
466
+ 4. Verify deployment succeeded (health check / smoke test)
467
+ 5. Tag the release: \`git tag -a vX.Y.Z -m "Release vX.Y.Z"\`
468
+ `,
469
+ 'fix.md': `Fix the issue described: $ARGUMENTS
470
+
471
+ ## Steps:
472
+ 1. Understand the issue — read relevant code and error messages
473
+ 2. Identify the root cause (not just the symptom)
474
+ 3. Implement the minimal fix
475
+ 4. Write or update tests to cover the fix
476
+ 5. Run the full test suite to verify no regressions
477
+ 6. Summarize what was wrong and how the fix addresses it
62
478
  `,
63
479
  }),
64
480
 
@@ -83,19 +499,33 @@ Fix the GitHub issue: $ARGUMENTS
83
499
  const hasPython = stacks.some(s => s.key === 'python');
84
500
 
85
501
  if (hasTS || stacks.some(s => ['react', 'vue', 'angular', 'nextjs', 'node'].includes(s.key))) {
86
- rules['frontend.md'] = `When editing frontend files (*.tsx, *.jsx, *.vue):
87
- - Use functional components with hooks
88
- - Follow existing component patterns
89
- - Add prop types or TypeScript interfaces
502
+ rules['frontend.md'] = `When editing JavaScript/TypeScript files (*.ts, *.tsx, *.js, *.jsx, *.vue):
503
+ - Use functional components with hooks (React/Vue 3)
504
+ - Add TypeScript interfaces for all props and function params
505
+ - Prefer \`const\` over \`let\`; never use \`var\`
506
+ - Use named exports over default exports
507
+ - Handle errors explicitly — no empty catch blocks
508
+ - Keep component files under 200 lines; extract sub-components
90
509
  `;
91
510
  }
92
511
  if (hasPython) {
93
- rules['python.md'] = `When editing Python files:
94
- - Use type hints for function signatures
95
- - Follow PEP 8 conventions
96
- - Use f-strings for formatting
512
+ rules['python.md'] = `When editing Python files (*.py):
513
+ - Use type hints for all function signatures and return types
514
+ - Follow PEP 8 conventions; max line length 88 (black default)
515
+ - Use f-strings for string formatting
516
+ - Prefer pathlib.Path over os.path
517
+ - Use \`if __name__ == "__main__":\` guard in scripts
518
+ - Raise specific exceptions, never bare \`except:\`
97
519
  `;
98
520
  }
521
+ rules['tests.md'] = `When writing or editing test files:
522
+ - Each test must have a clear, descriptive name (test_should_X_when_Y)
523
+ - Follow Arrange-Act-Assert (AAA) pattern
524
+ - One assertion per test when practical
525
+ - Never skip or disable tests without a tracking issue
526
+ - Mock external dependencies, not internal logic
527
+ - Include both happy path and edge case tests
528
+ `;
99
529
  return rules;
100
530
  },
101
531
 
@@ -113,6 +543,16 @@ Review code for security issues:
113
543
  - Insecure data handling
114
544
  `,
115
545
  }),
546
+
547
+ 'mermaid': () => `\`\`\`mermaid
548
+ graph TD
549
+ A[Entry Point] --> B[Core Logic]
550
+ B --> C[Data Layer]
551
+ B --> D[API / Routes]
552
+ C --> E[(Database)]
553
+ D --> F[External Services]
554
+ \`\`\`
555
+ `,
116
556
  };
117
557
 
118
558
  async function setup(options) {
@@ -137,7 +577,8 @@ async function setup(options) {
137
577
  const template = TEMPLATES[technique.template];
138
578
  if (!template) continue;
139
579
 
140
- const result = template(stacks);
580
+ // Pass ctx as second argument — only claude-md uses it
581
+ const result = template(stacks, ctx);
141
582
 
142
583
  if (typeof result === 'string') {
143
584
  // Single file template (like CLAUDE.md)