@claudetools/tools 0.7.0 → 0.7.2

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.
@@ -15,13 +15,75 @@ import { VueFrontendGenerator } from '../codedna/generators/vue-frontend.js';
15
15
  import { UIComponentGenerator } from '../codedna/generators/ui-component.js';
16
16
  import { errorTracker } from '../helpers/error-tracking.js';
17
17
  import { analytics } from '../helpers/usage-analytics.js';
18
+ import { apiRequest } from '../helpers/api-client.js';
18
19
  // Singleton registry instance
19
20
  const registry = new TemplateRegistry();
21
+ // =============================================================================
22
+ // Pattern Integration Helpers
23
+ // =============================================================================
24
+ /**
25
+ * Build PatternContext from detected patterns for use in generation
26
+ */
27
+ function buildPatternContext(detectedPatterns, confidenceScores = {}) {
28
+ const detected = detectedPatterns.map(p => ({
29
+ pattern_id: p.pattern_id,
30
+ name: p.name,
31
+ category: p.category,
32
+ confidence: confidenceScores[p.pattern_id] || 0.5,
33
+ }));
34
+ // Separate preferred from avoid (anti-patterns)
35
+ const preferred = detected
36
+ .filter(p => !detectedPatterns.find(dp => dp.pattern_id === p.pattern_id)?.is_anti_pattern)
37
+ .map(p => p.pattern_id);
38
+ const avoid = detected
39
+ .filter(p => detectedPatterns.find(dp => dp.pattern_id === p.pattern_id)?.is_anti_pattern)
40
+ .map(p => p.pattern_id);
41
+ return { detected, preferred, avoid };
42
+ }
43
+ /**
44
+ * Detect patterns from package.json and build PatternContext
45
+ */
46
+ async function detectPatternsFromPackageJson(packageJson) {
47
+ try {
48
+ // Get all patterns from registry
49
+ const patterns = await apiRequest('/api/v1/codedna/patterns');
50
+ const deps = {
51
+ ...(packageJson.dependencies || {}),
52
+ ...(packageJson.devDependencies || {}),
53
+ };
54
+ const detected = [];
55
+ const confidenceScores = {};
56
+ for (const pattern of patterns) {
57
+ let confidence = 0;
58
+ let matched = false;
59
+ for (const signal of pattern.package_signals) {
60
+ if (deps[signal]) {
61
+ matched = true;
62
+ confidence += 0.4; // Each package match adds confidence
63
+ }
64
+ }
65
+ if (matched) {
66
+ detected.push(pattern);
67
+ confidenceScores[pattern.pattern_id] = Math.min(confidence, 1.0);
68
+ }
69
+ }
70
+ return buildPatternContext(detected, confidenceScores);
71
+ }
72
+ catch (error) {
73
+ console.warn('Pattern detection failed, proceeding without patterns:', error);
74
+ return { detected: [], preferred: [], avoid: [] };
75
+ }
76
+ }
20
77
  /**
21
78
  * Handle codedna_generate_api tool call
79
+ *
80
+ * Pattern-aware generation:
81
+ * - If package_json is provided, patterns are detected automatically
82
+ * - If patterns is provided in options, those are used directly
83
+ * - Generated code will use template variants matching detected patterns
22
84
  */
23
85
  export async function handleGenerateApi(args) {
24
- const { spec, framework, options = {} } = args;
86
+ const { spec, framework, options = {}, package_json } = args;
25
87
  // Validate Entity DSL specification
26
88
  const validation = validateSpec(spec);
27
89
  if (!validation.valid) {
@@ -51,8 +113,18 @@ export async function handleGenerateApi(args) {
51
113
  }
52
114
  try {
53
115
  const startTime = Date.now();
116
+ // Detect patterns if package.json provided
117
+ let patternContext = options.patterns;
118
+ if (!patternContext && package_json) {
119
+ patternContext = await detectPatternsFromPackageJson(package_json);
120
+ }
121
+ // Merge pattern context into options
122
+ const generationOptions = {
123
+ ...options,
124
+ patterns: patternContext,
125
+ };
54
126
  // Generate code
55
- const result = await generator.generate(validation.parsed, options);
127
+ const result = await generator.generate(validation.parsed, generationOptions);
56
128
  const executionTime = Date.now() - startTime;
57
129
  // Track successful generation
58
130
  await analytics.trackGeneration({
@@ -65,13 +137,16 @@ export async function handleGenerateApi(args) {
65
137
  filesGenerated: result.metadata.filesGenerated,
66
138
  linesOfCode: result.metadata.linesOfCode,
67
139
  tokensSaved: result.metadata.estimatedTokensSaved,
68
- options: options,
140
+ options: generationOptions,
69
141
  executionTimeMs: executionTime,
70
142
  });
71
143
  return {
72
144
  success: true,
73
145
  files: result.files,
74
146
  metadata: result.metadata,
147
+ patternsApplied: patternContext?.detected?.length
148
+ ? patternContext.detected.map(p => p.pattern_id)
149
+ : undefined,
75
150
  tokenSavings: {
76
151
  traditional: result.metadata.linesOfCode * 25, // ~25 tokens per line
77
152
  codedna: 150, // ~150 tokens for MCP call
@@ -91,9 +166,14 @@ export async function handleGenerateApi(args) {
91
166
  }
92
167
  /**
93
168
  * Handle codedna_generate_frontend tool call
169
+ *
170
+ * Pattern-aware generation:
171
+ * - If package_json is provided, patterns are detected automatically
172
+ * - Detects React Hook Form, Zod, TanStack Query, etc.
173
+ * - Uses template variants matching detected patterns
94
174
  */
95
175
  export async function handleGenerateFrontend(args) {
96
- const { spec, framework, options = {} } = args;
176
+ const { spec, framework, options = {}, package_json } = args;
97
177
  // Validate Entity DSL specification
98
178
  const validation = validateSpec(spec);
99
179
  if (!validation.valid) {
@@ -118,8 +198,18 @@ export async function handleGenerateFrontend(args) {
118
198
  }
119
199
  try {
120
200
  const startTime = Date.now();
201
+ // Detect patterns if package.json provided
202
+ let patternContext = options.patterns;
203
+ if (!patternContext && package_json) {
204
+ patternContext = await detectPatternsFromPackageJson(package_json);
205
+ }
206
+ // Merge pattern context into options
207
+ const generationOptions = {
208
+ ...options,
209
+ patterns: patternContext,
210
+ };
121
211
  // Generate code
122
- const result = await generator.generate(validation.parsed, options);
212
+ const result = await generator.generate(validation.parsed, generationOptions);
123
213
  const executionTime = Date.now() - startTime;
124
214
  // Track successful generation
125
215
  await analytics.trackGeneration({
@@ -132,13 +222,16 @@ export async function handleGenerateFrontend(args) {
132
222
  filesGenerated: result.metadata.filesGenerated,
133
223
  linesOfCode: result.metadata.linesOfCode,
134
224
  tokensSaved: result.metadata.estimatedTokensSaved,
135
- options: options,
225
+ options: generationOptions,
136
226
  executionTimeMs: executionTime,
137
227
  });
138
228
  return {
139
229
  success: true,
140
230
  files: result.files,
141
231
  metadata: result.metadata,
232
+ patternsApplied: patternContext?.detected?.length
233
+ ? patternContext.detected.map(p => p.pattern_id)
234
+ : undefined,
142
235
  tokenSavings: {
143
236
  traditional: result.metadata.linesOfCode * 25,
144
237
  codedna: 150,
@@ -157,9 +250,13 @@ export async function handleGenerateFrontend(args) {
157
250
  }
158
251
  /**
159
252
  * Handle codedna_generate_component tool call
253
+ *
254
+ * Pattern-aware generation:
255
+ * - If package_json is provided, patterns are detected automatically
256
+ * - Uses matching patterns for form libraries, validation, etc.
160
257
  */
161
258
  export async function handleGenerateComponent(args) {
162
- const { spec, type, framework, options = {} } = args;
259
+ const { spec, type, framework, options = {}, package_json } = args;
163
260
  // Validate Entity DSL specification
164
261
  const validation = validateSpec(spec);
165
262
  if (!validation.valid) {
@@ -186,8 +283,18 @@ export async function handleGenerateComponent(args) {
186
283
  }
187
284
  try {
188
285
  const startTime = Date.now();
286
+ // Detect patterns if package.json provided
287
+ let patternContext = options.patterns;
288
+ if (!patternContext && package_json) {
289
+ patternContext = await detectPatternsFromPackageJson(package_json);
290
+ }
291
+ // Merge pattern context into options
292
+ const generationOptions = {
293
+ ...options,
294
+ patterns: patternContext,
295
+ };
189
296
  const generator = new UIComponentGenerator(registry, type, framework);
190
- const result = await generator.generate(validation.parsed, options);
297
+ const result = await generator.generate(validation.parsed, generationOptions);
191
298
  const executionTime = Date.now() - startTime;
192
299
  await analytics.trackGeneration({
193
300
  operation: 'generate_component',
@@ -199,13 +306,16 @@ export async function handleGenerateComponent(args) {
199
306
  filesGenerated: result.metadata.filesGenerated,
200
307
  linesOfCode: result.metadata.linesOfCode,
201
308
  tokensSaved: result.metadata.estimatedTokensSaved,
202
- options: options,
309
+ options: generationOptions,
203
310
  executionTimeMs: executionTime,
204
311
  });
205
312
  return {
206
313
  success: true,
207
314
  files: result.files,
208
315
  metadata: result.metadata,
316
+ patternsApplied: patternContext?.detected?.length
317
+ ? patternContext.detected.map(p => p.pattern_id)
318
+ : undefined,
209
319
  tokenSavings: {
210
320
  traditional: result.metadata.linesOfCode * 25,
211
321
  codedna: 100,
@@ -224,18 +334,47 @@ export async function handleGenerateComponent(args) {
224
334
  }
225
335
  /**
226
336
  * Handle codedna_list_generators tool call
337
+ * @param args.domain Optional domain filter: 'api' | 'frontend' | 'component'
227
338
  */
228
- export async function handleListGenerators() {
339
+ export async function handleListGenerators(args) {
229
340
  try {
230
- const generators = await registry.listGenerators();
341
+ const domain = args?.domain;
342
+ const generators = await registry.listGenerators(domain);
343
+ // Collect unique UI libraries across all generators
344
+ const allUiLibraries = new Set();
345
+ generators.forEach(gen => {
346
+ gen.uiLibraries?.forEach(lib => allUiLibraries.add(lib));
347
+ });
348
+ // Group by domain for easier discovery (only when not filtered)
349
+ const byDomain = domain ? undefined : generators.reduce((acc, gen) => {
350
+ const d = gen.domain || 'unknown';
351
+ if (!acc[d])
352
+ acc[d] = [];
353
+ acc[d].push(gen);
354
+ return acc;
355
+ }, {});
231
356
  return {
232
357
  generators,
358
+ ...(byDomain && { byDomain }),
233
359
  summary: {
234
360
  total: generators.length,
361
+ ...(domain && { filteredByDomain: domain }),
235
362
  byFramework: generators.reduce((acc, gen) => {
236
363
  acc[gen.framework] = (acc[gen.framework] || 0) + 1;
237
364
  return acc;
238
365
  }, {}),
366
+ byDomain: domain ? undefined : generators.reduce((acc, gen) => {
367
+ const d = gen.domain || 'unknown';
368
+ acc[d] = (acc[d] || 0) + 1;
369
+ return acc;
370
+ }, {}),
371
+ availableUiLibraries: Array.from(allUiLibraries),
372
+ },
373
+ usage: {
374
+ api: 'codedna_generate_api(spec, framework, options)',
375
+ frontend: 'codedna_generate_frontend(spec, framework, options)',
376
+ component: 'codedna_generate_component(spec, type, framework, options)',
377
+ note: 'Check generator.uiLibraries for available UI options. Pass ui: "shadcn"|"mui"|"chakra" in options.',
239
378
  },
240
379
  };
241
380
  }
@@ -281,3 +420,232 @@ export async function handleValidateSpec(args) {
281
420
  errors: validation.errors,
282
421
  };
283
422
  }
423
+ // =============================================================================
424
+ // Pattern Library Handlers
425
+ // =============================================================================
426
+ /**
427
+ * Handle codedna_list_patterns tool call
428
+ * Lists patterns from the library, optionally filtered by category
429
+ */
430
+ export async function handleListPatterns(args) {
431
+ try {
432
+ const { category, recommended_only, include_anti_patterns = true } = args || {};
433
+ // Build query parameters
434
+ const params = new URLSearchParams();
435
+ if (category)
436
+ params.set('category', category);
437
+ if (recommended_only)
438
+ params.set('recommended_only', 'true');
439
+ if (!include_anti_patterns)
440
+ params.set('exclude_anti_patterns', 'true');
441
+ const patterns = await apiRequest(`/api/v1/codedna/patterns?${params.toString()}`);
442
+ // Group by category for easier discovery
443
+ const byCategory = patterns.reduce((acc, p) => {
444
+ if (!acc[p.category])
445
+ acc[p.category] = [];
446
+ acc[p.category].push(p);
447
+ return acc;
448
+ }, {});
449
+ const recommended = patterns.filter(p => p.is_recommended);
450
+ const antiPatterns = patterns.filter(p => p.is_anti_pattern);
451
+ return {
452
+ patterns,
453
+ byCategory,
454
+ summary: {
455
+ total: patterns.length,
456
+ recommended: recommended.length,
457
+ antiPatterns: antiPatterns.length,
458
+ categories: Object.keys(byCategory),
459
+ },
460
+ usage: {
461
+ getDetails: 'codedna_get_pattern(pattern_id) - Get full pattern documentation',
462
+ detect: 'codedna_detect_patterns(project_path) - Detect patterns in codebase',
463
+ init: 'codedna_init_project(patterns) - Initialize project with chosen patterns',
464
+ },
465
+ };
466
+ }
467
+ catch (error) {
468
+ return {
469
+ error: 'Failed to list patterns',
470
+ message: error instanceof Error ? error.message : String(error),
471
+ };
472
+ }
473
+ }
474
+ /**
475
+ * Handle codedna_get_pattern tool call
476
+ * Gets detailed pattern information including full markdown documentation
477
+ */
478
+ export async function handleGetPattern(args) {
479
+ try {
480
+ const { pattern_id } = args;
481
+ if (!pattern_id) {
482
+ return { error: 'pattern_id is required' };
483
+ }
484
+ const pattern = await apiRequest(`/api/v1/codedna/patterns/${pattern_id}`);
485
+ return {
486
+ pattern,
487
+ sections: {
488
+ description: pattern.description,
489
+ whenToUse: pattern.conditions,
490
+ whenNotToUse: pattern.anti_conditions,
491
+ relatedPatterns: pattern.related_patterns,
492
+ migratesFrom: pattern.migrates_from,
493
+ },
494
+ detection: {
495
+ codeSignals: pattern.code_signals,
496
+ packageSignals: pattern.package_signals,
497
+ },
498
+ content: pattern.content, // Full markdown from R2
499
+ };
500
+ }
501
+ catch (error) {
502
+ return {
503
+ error: 'Failed to get pattern',
504
+ message: error instanceof Error ? error.message : String(error),
505
+ };
506
+ }
507
+ }
508
+ /**
509
+ * Handle codedna_detect_patterns tool call
510
+ * Detects patterns in a project based on code and package.json signals
511
+ */
512
+ export async function handleDetectPatterns(args) {
513
+ try {
514
+ const { project_path, package_json, code_samples } = args;
515
+ // Get all patterns with their detection signals
516
+ const patterns = await apiRequest('/api/v1/codedna/patterns');
517
+ const detected = [];
518
+ for (const pattern of patterns) {
519
+ const matchedSignals = [];
520
+ let confidence = 0;
521
+ // Check package.json signals
522
+ if (package_json?.dependencies || package_json?.devDependencies) {
523
+ const deps = {
524
+ ...(package_json.dependencies || {}),
525
+ ...(package_json.devDependencies || {}),
526
+ };
527
+ for (const signal of pattern.package_signals) {
528
+ if (deps[signal]) {
529
+ matchedSignals.push(`package:${signal}`);
530
+ confidence += 0.4; // Package match is strong signal
531
+ }
532
+ }
533
+ }
534
+ // Check code signals in samples
535
+ if (code_samples) {
536
+ const allCode = code_samples.join('\n');
537
+ for (const signal of pattern.code_signals) {
538
+ if (allCode.includes(signal)) {
539
+ matchedSignals.push(`code:${signal}`);
540
+ confidence += 0.2; // Code match is moderate signal
541
+ }
542
+ }
543
+ }
544
+ // Add to detected if any signals matched
545
+ if (matchedSignals.length > 0) {
546
+ detected.push({
547
+ pattern,
548
+ confidence: Math.min(confidence, 1.0),
549
+ signals: matchedSignals,
550
+ });
551
+ }
552
+ }
553
+ // Sort by confidence
554
+ detected.sort((a, b) => b.confidence - a.confidence);
555
+ // Separate recommended vs detected anti-patterns
556
+ const goodPatterns = detected.filter(d => !d.pattern.is_anti_pattern);
557
+ const antiPatterns = detected.filter(d => d.pattern.is_anti_pattern);
558
+ return {
559
+ detected: goodPatterns,
560
+ warnings: antiPatterns.length > 0 ? {
561
+ message: 'Anti-patterns detected in codebase',
562
+ patterns: antiPatterns,
563
+ } : undefined,
564
+ summary: {
565
+ totalDetected: detected.length,
566
+ goodPatterns: goodPatterns.length,
567
+ antiPatterns: antiPatterns.length,
568
+ highConfidence: detected.filter(d => d.confidence >= 0.6).length,
569
+ },
570
+ recommendations: patterns
571
+ .filter(p => p.is_recommended && !detected.find(d => d.pattern.pattern_id === p.pattern_id))
572
+ .slice(0, 5)
573
+ .map(p => ({
574
+ pattern_id: p.pattern_id,
575
+ name: p.name,
576
+ description: p.description,
577
+ reason: 'Recommended pattern not yet detected in codebase',
578
+ })),
579
+ };
580
+ }
581
+ catch (error) {
582
+ return {
583
+ error: 'Failed to detect patterns',
584
+ message: error instanceof Error ? error.message : String(error),
585
+ };
586
+ }
587
+ }
588
+ /**
589
+ * Handle codedna_init_project tool call
590
+ * Initializes a new project with recommended patterns
591
+ */
592
+ export async function handleInitProject(args) {
593
+ try {
594
+ const { project_id, patterns: selectedPatterns, auto_detect = true, project_type = 'new' } = args;
595
+ if (!project_id) {
596
+ return { error: 'project_id is required' };
597
+ }
598
+ // Get all patterns
599
+ const allPatterns = await apiRequest('/api/v1/codedna/patterns');
600
+ let patternsToApply;
601
+ if (project_type === 'new' && !selectedPatterns) {
602
+ // For new projects, use recommended patterns
603
+ patternsToApply = allPatterns
604
+ .filter(p => p.is_recommended && !p.is_anti_pattern)
605
+ .map(p => p.pattern_id);
606
+ }
607
+ else if (selectedPatterns) {
608
+ // Use explicitly selected patterns
609
+ patternsToApply = selectedPatterns;
610
+ }
611
+ else {
612
+ // No patterns specified
613
+ patternsToApply = [];
614
+ }
615
+ // Store pattern associations in memory
616
+ const stored = await apiRequest(`/api/v1/codedna/projects/${project_id}/patterns`, 'POST', {
617
+ patterns: patternsToApply,
618
+ detection_source: selectedPatterns ? 'user_selected' : 'recommended',
619
+ });
620
+ const appliedPatterns = allPatterns.filter(p => patternsToApply.includes(p.pattern_id));
621
+ return {
622
+ success: stored.success,
623
+ project_id,
624
+ project_type,
625
+ patterns: appliedPatterns.map(p => ({
626
+ pattern_id: p.pattern_id,
627
+ name: p.name,
628
+ category: p.category,
629
+ description: p.description,
630
+ })),
631
+ summary: {
632
+ totalPatterns: patternsToApply.length,
633
+ byCategory: appliedPatterns.reduce((acc, p) => {
634
+ acc[p.category] = (acc[p.category] || 0) + 1;
635
+ return acc;
636
+ }, {}),
637
+ },
638
+ nextSteps: [
639
+ 'Patterns are now associated with this project',
640
+ 'CodeDNA generators will use these patterns for template selection',
641
+ 'Use codedna_detect_patterns to update as codebase evolves',
642
+ ],
643
+ };
644
+ }
645
+ catch (error) {
646
+ return {
647
+ error: 'Failed to initialize project patterns',
648
+ message: error instanceof Error ? error.message : String(error),
649
+ };
650
+ }
651
+ }
@@ -13,7 +13,7 @@ import { formatContextForClaude } from '../helpers/formatter.js';
13
13
  import { createTask, listTasks, getTask, claimTask, releaseTask, updateTaskStatus, addTaskContext, getTaskSummary, heartbeatTask, parseJsonArray, getDispatchableTasks, getExecutionContext, resolveTaskDependencies, getEpicStatus, getActiveTaskCount, } from '../helpers/tasks.js';
14
14
  import { detectTimedOutTasks, retryTask, failTask, autoRetryTimedOutTasks, } from '../helpers/tasks-retry.js';
15
15
  import { detectLibrariesFromPlan } from '../helpers/library-detection.js';
16
- import { handleGenerateApi, handleGenerateFrontend, handleGenerateComponent, handleListGenerators, handleValidateSpec, } from './codedna-handlers.js';
16
+ import { handleGenerateApi, handleGenerateFrontend, handleGenerateComponent, handleListGenerators, handleValidateSpec, handleListPatterns, handleGetPattern, handleDetectPatterns, handleInitProject, } from './codedna-handlers.js';
17
17
  export function registerToolHandlers(server) {
18
18
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
19
19
  const { name, arguments: args } = request.params;
@@ -758,7 +758,7 @@ export function registerToolHandlers(server) {
758
758
  blocked: '🚫', review: '👀', done: '✅', cancelled: '❌'
759
759
  };
760
760
  const emoji = statusEmoji[t.status] || '📝';
761
- let line = `- ${emoji} **${t.title}** (${t.id.slice(0, 8)}...)`;
761
+ let line = `- ${emoji} **${t.title}** (\`${t.id}\`)`;
762
762
  line += ` [${t.status}]`;
763
763
  if (t.priority !== 'medium')
764
764
  line += ` [${t.priority}]`;
@@ -817,12 +817,12 @@ export function registerToolHandlers(server) {
817
817
  }
818
818
  if (data.parent) {
819
819
  output += `\n## Parent\n`;
820
- output += `- **${data.parent.title}** (${data.parent.id.slice(0, 8)}...) [${data.parent.status}]\n`;
820
+ output += `- **${data.parent.title}** (\`${data.parent.id}\`) [${data.parent.status}]\n`;
821
821
  }
822
822
  if (data.subtasks?.length) {
823
823
  output += `\n## Subtasks (${data.subtasks.length})\n`;
824
824
  data.subtasks.forEach(s => {
825
- output += `- ${s.title} (${s.id.slice(0, 8)}...) [${s.status}]\n`;
825
+ output += `- ${s.title} (\`${s.id}\`) [${s.status}]\n`;
826
826
  });
827
827
  }
828
828
  if (data.context?.length) {
@@ -1430,14 +1430,20 @@ export function registerToolHandlers(server) {
1430
1430
  // =========================================================================
1431
1431
  case 'codebase_map': {
1432
1432
  // Use raw fetch since this endpoint returns markdown, not JSON
1433
+ // But we still need auth header
1434
+ const { getConfig } = await import('../helpers/config-manager.js');
1435
+ const config = getConfig();
1436
+ const apiKey = config.apiKey || process.env.CLAUDETOOLS_API_KEY || process.env.MEMORY_API_KEY;
1433
1437
  const url = `${API_BASE_URL}/api/v1/codebase/${projectId}/map`;
1434
- const response = await fetch(url);
1438
+ const response = await fetch(url, {
1439
+ headers: apiKey ? { 'Authorization': `Bearer ${apiKey}` } : {},
1440
+ });
1435
1441
  mcpLogger.toolResult(name, response.ok, timer());
1436
1442
  if (!response.ok) {
1437
1443
  return {
1438
1444
  content: [{
1439
1445
  type: 'text',
1440
- text: `# No Codebase Map Found\n\nThe codebase map hasn't been generated yet. Use the file watcher or API to generate one.`,
1446
+ text: `# No Codebase Map Found\n\nThe codebase map hasn't been generated yet. Use the file watcher or API to generate one.\n\n**Debug:** \`${url}\` returned ${response.status}`,
1441
1447
  }],
1442
1448
  };
1443
1449
  }
@@ -1697,7 +1703,7 @@ export function registerToolHandlers(server) {
1697
1703
  };
1698
1704
  }
1699
1705
  case 'codedna_list_generators': {
1700
- const result = await handleListGenerators();
1706
+ const result = await handleListGenerators(args);
1701
1707
  mcpLogger.toolResult(name, true, timer());
1702
1708
  return {
1703
1709
  content: [{
@@ -1716,6 +1722,49 @@ export function registerToolHandlers(server) {
1716
1722
  }],
1717
1723
  };
1718
1724
  }
1725
+ // =====================================================================
1726
+ // CodeDNA Pattern Library Tools
1727
+ // =====================================================================
1728
+ case 'codedna_list_patterns': {
1729
+ const result = await handleListPatterns(args);
1730
+ mcpLogger.toolResult(name, true, timer());
1731
+ return {
1732
+ content: [{
1733
+ type: 'text',
1734
+ text: JSON.stringify(result, null, 2),
1735
+ }],
1736
+ };
1737
+ }
1738
+ case 'codedna_get_pattern': {
1739
+ const result = await handleGetPattern(args);
1740
+ mcpLogger.toolResult(name, true, timer());
1741
+ return {
1742
+ content: [{
1743
+ type: 'text',
1744
+ text: JSON.stringify(result, null, 2),
1745
+ }],
1746
+ };
1747
+ }
1748
+ case 'codedna_detect_patterns': {
1749
+ const result = await handleDetectPatterns(args);
1750
+ mcpLogger.toolResult(name, true, timer());
1751
+ return {
1752
+ content: [{
1753
+ type: 'text',
1754
+ text: JSON.stringify(result, null, 2),
1755
+ }],
1756
+ };
1757
+ }
1758
+ case 'codedna_init_project': {
1759
+ const result = await handleInitProject(args);
1760
+ mcpLogger.toolResult(name, true, timer());
1761
+ return {
1762
+ content: [{
1763
+ type: 'text',
1764
+ text: JSON.stringify(result, null, 2),
1765
+ }],
1766
+ };
1767
+ }
1719
1768
  default:
1720
1769
  throw new Error(`Unknown tool: ${name}`);
1721
1770
  }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Codebase Mapper
3
+ *
4
+ * Client-side AST analysis to generate codebase maps.
5
+ * Runs in the MCP server and uploads results to the API.
6
+ */
7
+ export interface FileSymbol {
8
+ name: string;
9
+ type: 'function' | 'class' | 'interface' | 'type' | 'const' | 'enum' | 'component';
10
+ exported: boolean;
11
+ line: number;
12
+ signature?: string;
13
+ description?: string;
14
+ }
15
+ export interface FileEntry {
16
+ path: string;
17
+ relativePath: string;
18
+ role: FileRole;
19
+ symbols: FileSymbol[];
20
+ imports: string[];
21
+ exports: string[];
22
+ dependencies: string[];
23
+ dependents: string[];
24
+ linesOfCode: number;
25
+ }
26
+ export type FileRole = 'entry-point' | 'route-handler' | 'api-endpoint' | 'mcp-tool' | 'component' | 'hook' | 'utility' | 'type-definitions' | 'config' | 'test' | 'model' | 'service' | 'extractor' | 'unknown';
27
+ export interface DirectoryEntry {
28
+ path: string;
29
+ role: string;
30
+ fileCount: number;
31
+ primaryLanguage: string;
32
+ keyFiles: string[];
33
+ }
34
+ export interface CodebaseIndex {
35
+ version: string;
36
+ generatedAt: string;
37
+ projectRoot: string;
38
+ summary: {
39
+ totalFiles: number;
40
+ totalSymbols: number;
41
+ languages: Record<string, number>;
42
+ frameworks: string[];
43
+ };
44
+ directories: Record<string, DirectoryEntry>;
45
+ files: Record<string, FileEntry>;
46
+ symbols: Record<string, {
47
+ file: string;
48
+ line: number;
49
+ type: string;
50
+ exported: boolean;
51
+ }[]>;
52
+ callGraph: Record<string, string[]>;
53
+ reverseCallGraph: Record<string, string[]>;
54
+ }
55
+ export declare function generateCodebaseMap(projectRoot: string, projectId: string, apiKey: string): Promise<{
56
+ success: boolean;
57
+ error?: string;
58
+ }>;
59
+ /**
60
+ * Generate a codebase map locally without uploading
61
+ * Useful for previewing or debugging
62
+ */
63
+ export declare function generateCodebaseMapLocal(projectRoot: string): {
64
+ index: CodebaseIndex;
65
+ markdown: string;
66
+ };