claudex-setup 0.2.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/bin/cli.js CHANGED
@@ -18,6 +18,7 @@ const HELP = `
18
18
  npx claudex-setup audit Same as above
19
19
  npx claudex-setup setup Apply recommended configuration
20
20
  npx claudex-setup setup --auto Apply all recommendations without prompts
21
+ npx claudex-setup badge Generate shields.io badge markdown
21
22
 
22
23
  Options:
23
24
  --verbose Show detailed analysis
@@ -45,7 +46,14 @@ async function main() {
45
46
  };
46
47
 
47
48
  try {
48
- if (command === 'setup') {
49
+ if (command === 'badge') {
50
+ const { getBadgeMarkdown } = require('../src/badge');
51
+ const result = await audit({ ...options, silent: true });
52
+ console.log(getBadgeMarkdown(result.score));
53
+ console.log('');
54
+ console.log('Add this to your README.md');
55
+ process.exit(0);
56
+ } else if (command === 'setup') {
49
57
  await setup(options);
50
58
  } else {
51
59
  await audit(options);
package/package.json CHANGED
@@ -1,11 +1,17 @@
1
1
  {
2
2
  "name": "claudex-setup",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Audit and optimize any project for Claude Code. Powered by 1107 verified techniques.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
7
7
  "claudex-setup": "bin/cli.js"
8
8
  },
9
+ "files": [
10
+ "bin",
11
+ "src",
12
+ "README.md",
13
+ "CHANGELOG.md"
14
+ ],
9
15
  "scripts": {
10
16
  "start": "node bin/cli.js",
11
17
  "test": "node test/run.js"
@@ -25,7 +31,7 @@
25
31
  "license": "MIT",
26
32
  "repository": {
27
33
  "type": "git",
28
- "url": "https://github.com/DnaFin/claudex"
34
+ "url": "git+https://github.com/DnaFin/claudex.git"
29
35
  },
30
36
  "engines": {
31
37
  "node": ">=18.0.0"
package/src/audit.js CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  const { TECHNIQUES, STACKS } = require('./techniques');
6
6
  const { ProjectContext } = require('./context');
7
+ const { getBadgeMarkdown } = require('./badge');
7
8
 
8
9
  const COLORS = {
9
10
  reset: '\x1b[0m',
@@ -27,7 +28,24 @@ function progressBar(score, max = 100, width = 20) {
27
28
  return colorize('█'.repeat(filled), color) + colorize('░'.repeat(empty), 'dim');
28
29
  }
29
30
 
31
+ const EFFORT_ORDER = { critical: 0, high: 1, medium: 2 };
32
+
33
+ function getQuickWins(failed) {
34
+ // Quick wins = medium impact items first (easiest), then high, sorted by name length (shorter = simpler)
35
+ return [...failed]
36
+ .sort((a, b) => {
37
+ const effortA = EFFORT_ORDER[a.impact] ?? 3;
38
+ const effortB = EFFORT_ORDER[b.impact] ?? 3;
39
+ // Prefer medium (easiest to fix), then high, then critical
40
+ if (effortA !== effortB) return effortB - effortA;
41
+ // Tie-break by fix length (shorter fix description = likely simpler)
42
+ return (a.fix || '').length - (b.fix || '').length;
43
+ })
44
+ .slice(0, 3);
45
+ }
46
+
30
47
  async function audit(options) {
48
+ const silent = options.silent || false;
31
49
  const ctx = new ProjectContext(options.dir);
32
50
  const stacks = ctx.detectStacks(STACKS);
33
51
  const results = [];
@@ -54,9 +72,14 @@ async function audit(options) {
54
72
  const earnedScore = passed.reduce((sum, r) => sum + (weights[r.impact] || 5), 0);
55
73
  const score = Math.round((earnedScore / maxScore) * 100);
56
74
 
75
+ // Silent mode: skip all output, just return result
76
+ if (silent) {
77
+ return { score, passed: passed.length, failed: failed.length, stacks, results };
78
+ }
79
+
57
80
  if (options.json) {
58
81
  console.log(JSON.stringify({ score, stacks, passed: passed.length, failed: failed.length, results }, null, 2));
59
- return;
82
+ return { score, passed: passed.length, failed: failed.length, stacks, results };
60
83
  }
61
84
 
62
85
  // Display results
@@ -115,6 +138,18 @@ async function audit(options) {
115
138
  console.log('');
116
139
  }
117
140
 
141
+ // Quick wins
142
+ if (failed.length > 0) {
143
+ const quickWins = getQuickWins(failed);
144
+ console.log(colorize(' ⚡ Quick wins (easiest fixes first)', 'magenta'));
145
+ for (let i = 0; i < quickWins.length; i++) {
146
+ const r = quickWins[i];
147
+ console.log(` ${i + 1}. ${colorize(r.name, 'bold')}`);
148
+ console.log(colorize(` → ${r.fix}`, 'dim'));
149
+ }
150
+ console.log('');
151
+ }
152
+
118
153
  // Summary
119
154
  console.log(colorize(' ─────────────────────────────────────', 'dim'));
120
155
  console.log(` ${colorize(`${passed.length}/${results.length}`, 'bold')} checks passing`);
@@ -123,12 +158,14 @@ async function audit(options) {
123
158
  console.log(` Run ${colorize('npx claudex-setup setup', 'bold')} to fix automatically`);
124
159
  }
125
160
 
161
+ console.log('');
162
+ console.log(` Add to README: ${getBadgeMarkdown(score)}`);
126
163
  console.log('');
127
164
  console.log(colorize(' Powered by CLAUDEX - 1,107 verified Claude Code techniques', 'dim'));
128
- console.log(colorize(' https://github.com/naorp/claudex-setup', 'dim'));
165
+ console.log(colorize(' https://github.com/DnaFin/claudex', 'dim'));
129
166
  console.log('');
130
167
 
131
- return { score, passed: passed.length, failed: failed.length, stacks };
168
+ return { score, passed: passed.length, failed: failed.length, stacks, results };
132
169
  }
133
170
 
134
171
  module.exports = { audit };
package/src/badge.js ADDED
@@ -0,0 +1,13 @@
1
+ function getBadgeUrl(score) {
2
+ const color = score >= 80 ? 'brightgreen' : score >= 60 ? 'yellow' : score >= 40 ? 'orange' : 'red';
3
+ const label = encodeURIComponent('Claude Code Ready');
4
+ const message = encodeURIComponent(`${score}/100`);
5
+ return `https://img.shields.io/badge/${label}-${message}-${color}`;
6
+ }
7
+
8
+ function getBadgeMarkdown(score) {
9
+ const url = getBadgeUrl(score);
10
+ return `[![Claude Code Ready](${url})](https://github.com/DnaFin/claudex)`;
11
+ }
12
+
13
+ module.exports = { getBadgeUrl, getBadgeMarkdown };
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,62 +9,349 @@ 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
252
  const stackKeys = stacks.map(s => s.key);
15
- const hasJS = stackKeys.some(k => ['typescript', 'react', 'vue', 'angular', 'nextjs', 'node'].includes(k));
16
- const hasPython = stackKeys.includes('python');
17
253
 
18
- let buildCommands = '# Add your build command\n# Add your test command\n# Add your lint command';
19
- if (hasJS) {
20
- buildCommands = `npm run build # or: npx tsc --noEmit
21
- npm test # or: npx jest / npx vitest
22
- npm run lint # or: npx eslint .`;
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');
23
274
  } else if (hasPython) {
24
- buildCommands = `python -m pytest # run tests
275
+ buildSection = `python -m pytest # run tests
25
276
  python -m mypy . # type checking
26
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';
27
284
  }
28
285
 
29
- let stackSection = '';
30
- if (hasJS) {
31
- stackSection = `
32
- ## Stack-Specific
33
- - Prefer functional components with hooks (React)
34
- - Use TypeScript interfaces over \`type\` where possible
35
- - Use \`async/await\` over raw Promises
36
- - Import order: stdlib > external > internal > relative
37
- `;
38
- } else if (hasPython) {
39
- stackSection = `
40
- ## Stack-Specific
41
- - Use type hints on all function signatures
42
- - Follow PEP 8 conventions; use f-strings for formatting
43
- - Prefer pathlib over os.path
44
- - Use dataclasses or pydantic for structured data
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
+ }
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'}\`
45
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\`)`);
46
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)`);
47
337
 
48
- return `# Project Instructions
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}` : '';
49
342
 
50
- ## Architecture
51
- \`\`\`mermaid
52
- graph TD
53
- A[Entry Point] --> B[Core Logic]
54
- B --> C[Data Layer]
55
- B --> D[API / Routes]
56
- C --> E[(Database)]
57
- D --> F[External Services]
58
- \`\`\`
59
- <!-- Replace with your actual project architecture -->
343
+ // --- Assemble the final CLAUDE.md ---
344
+ return `# ${projectName}${projectDesc}
60
345
 
346
+ ## Architecture
347
+ ${mermaid}
348
+ ${dirDescription}
61
349
  ## Stack
62
350
  ${stackNames}
63
- ${stackSection}
351
+ ${stackSection}${tsSection}
64
352
  ## Build & Test
65
353
  \`\`\`bash
66
- ${buildCommands}
354
+ ${buildSection}
67
355
  \`\`\`
68
356
 
69
357
  ## Code Style
@@ -72,21 +360,18 @@ ${buildCommands}
72
360
  - Keep functions small and focused (< 50 lines)
73
361
  - Use descriptive variable names; avoid abbreviations
74
362
 
75
- ## Constraints
76
363
  <constraints>
77
364
  - Never commit secrets, API keys, or .env files
78
365
  - Always run tests before marking work complete
79
366
  - Prefer editing existing files over creating new ones
80
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' : ''}\
81
370
  </constraints>
82
371
 
83
- ## Verification
84
372
  <verification>
85
373
  Before completing any task, confirm:
86
- 1. All existing tests still pass
87
- 2. New code has test coverage
88
- 3. No linting errors introduced
89
- 4. Changes match the requested scope (no gold-plating)
374
+ ${verificationSteps.join('\n')}
90
375
  </verification>
91
376
 
92
377
  ## Workflow
@@ -124,6 +409,28 @@ case "$BASENAME" in
124
409
  ;;
125
410
  esac
126
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
+
127
434
  exit 0
128
435
  `,
129
436
  }),
@@ -158,6 +465,16 @@ exit 0
158
465
  3. Run the deployment command
159
466
  4. Verify deployment succeeded (health check / smoke test)
160
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
161
478
  `,
162
479
  }),
163
480
 
@@ -260,7 +577,8 @@ async function setup(options) {
260
577
  const template = TEMPLATES[technique.template];
261
578
  if (!template) continue;
262
579
 
263
- const result = template(stacks);
580
+ // Pass ctx as second argument — only claude-md uses it
581
+ const result = template(stacks, ctx);
264
582
 
265
583
  if (typeof result === 'string') {
266
584
  // Single file template (like CLAUDE.md)
@@ -1,11 +0,0 @@
1
- ---
2
- name: security-reviewer
3
- description: Reviews code for security vulnerabilities
4
- tools: [Read, Grep, Glob]
5
- model: sonnet
6
- ---
7
- Review code for security issues:
8
- - Injection vulnerabilities (SQL, XSS, command injection)
9
- - Authentication and authorization flaws
10
- - Secrets or credentials in code
11
- - Insecure data handling
@@ -1,6 +0,0 @@
1
- Review the current changes for quality and correctness.
2
-
3
- ## Steps:
4
- 1. Run `git diff` to see all changes
5
- 2. Check for: bugs, security issues, missing tests, code style
6
- 3. Provide actionable feedback
@@ -1,6 +0,0 @@
1
- Run the test suite and report results.
2
-
3
- ## Steps:
4
- 1. Run the project's test command
5
- 2. If tests fail, analyze the failures
6
- 3. Report: total, passed, failed, and any error details
@@ -1,5 +0,0 @@
1
- #!/bin/bash
2
- # PostToolUse hook - auto-check after file edits
3
- # Customize the linter command for your project
4
- TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
5
- echo "[$TIMESTAMP] File changed: $(cat -)" >> .claude/logs/changes.txt
@@ -1,4 +0,0 @@
1
- When editing frontend files (*.tsx, *.jsx, *.vue):
2
- - Use functional components with hooks
3
- - Follow existing component patterns
4
- - Add prop types or TypeScript interfaces
@@ -1,11 +0,0 @@
1
- ---
2
- name: fix-issue
3
- description: Fix a GitHub issue by number
4
- ---
5
- Fix the GitHub issue: $ARGUMENTS
6
-
7
- 1. Read the issue details
8
- 2. Search the codebase for relevant files
9
- 3. Implement the fix
10
- 4. Write tests
11
- 5. Create a descriptive commit