@claudetools/tools 0.7.0 → 0.7.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.
@@ -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;
@@ -1697,7 +1697,7 @@ export function registerToolHandlers(server) {
1697
1697
  };
1698
1698
  }
1699
1699
  case 'codedna_list_generators': {
1700
- const result = await handleListGenerators();
1700
+ const result = await handleListGenerators(args);
1701
1701
  mcpLogger.toolResult(name, true, timer());
1702
1702
  return {
1703
1703
  content: [{
@@ -1716,6 +1716,49 @@ export function registerToolHandlers(server) {
1716
1716
  }],
1717
1717
  };
1718
1718
  }
1719
+ // =====================================================================
1720
+ // CodeDNA Pattern Library Tools
1721
+ // =====================================================================
1722
+ case 'codedna_list_patterns': {
1723
+ const result = await handleListPatterns(args);
1724
+ mcpLogger.toolResult(name, true, timer());
1725
+ return {
1726
+ content: [{
1727
+ type: 'text',
1728
+ text: JSON.stringify(result, null, 2),
1729
+ }],
1730
+ };
1731
+ }
1732
+ case 'codedna_get_pattern': {
1733
+ const result = await handleGetPattern(args);
1734
+ mcpLogger.toolResult(name, true, timer());
1735
+ return {
1736
+ content: [{
1737
+ type: 'text',
1738
+ text: JSON.stringify(result, null, 2),
1739
+ }],
1740
+ };
1741
+ }
1742
+ case 'codedna_detect_patterns': {
1743
+ const result = await handleDetectPatterns(args);
1744
+ mcpLogger.toolResult(name, true, timer());
1745
+ return {
1746
+ content: [{
1747
+ type: 'text',
1748
+ text: JSON.stringify(result, null, 2),
1749
+ }],
1750
+ };
1751
+ }
1752
+ case 'codedna_init_project': {
1753
+ const result = await handleInitProject(args);
1754
+ mcpLogger.toolResult(name, true, timer());
1755
+ return {
1756
+ content: [{
1757
+ type: 'text',
1758
+ text: JSON.stringify(result, null, 2),
1759
+ }],
1760
+ };
1761
+ }
1719
1762
  default:
1720
1763
  throw new Error(`Unknown tool: ${name}`);
1721
1764
  }
@@ -37,11 +37,17 @@ When complete, provide a concise summary of changes made.`,
37
37
  - Response formatting
38
38
  - Error handling for endpoints
39
39
 
40
- CODEDNA PRIORITY: Before writing CRUD code manually, check if CodeDNA can generate it:
41
- - Look for Entity DSL in task (e.g., "User(email:string:unique, password:string:hashed)")
42
- - Call codedna_generate_api(spec, framework="express", options={...})
43
- - This generates models, controllers, routes, validation in seconds
44
- - Only write manually for complex business logic that can't be generated
40
+ PATTERN DETECTION (for existing projects):
41
+ 1. Call codedna_detect_patterns(package_json) to find existing patterns
42
+ 2. MATCH detected patterns - don't introduce conflicting libraries
43
+ 3. Check for: zod vs yup validation, tanstack-query vs swr, etc.
44
+
45
+ CODEDNA PRIORITY: Before writing CRUD code manually:
46
+ 1. Call codedna_list_generators() to discover available generators
47
+ 2. Look for Entity DSL in task (e.g., "User(email:string:unique, password:string:hashed)")
48
+ 3. Detect framework from project (check package.json, existing patterns)
49
+ 4. Call codedna_generate_api(spec, framework="<detected>", options={...})
50
+ 5. Only write manually for complex business logic that can't be generated
45
51
 
46
52
  Focus only on API-related changes. Do not modify schema or extraction code.
47
53
  When complete, provide a concise summary of endpoints created or modified.`,
@@ -99,6 +105,40 @@ TOOLS: Use codebase_context to see file dependencies.
99
105
 
100
106
  Focus on integration without modifying core implementation logic.
101
107
  When complete, provide a concise summary of integrations configured.`,
108
+ },
109
+ 'frontend-expert': {
110
+ id: 'frontend-expert',
111
+ name: 'Frontend Expert',
112
+ description: 'Specialises in React, Vue, and frontend component development',
113
+ domains: ['**/components/**', '**/pages/**', '**/app/**', '**/*.tsx', '**/*.vue'],
114
+ capabilities: ['react', 'vue', 'components', 'forms', 'tables', 'styling'],
115
+ promptTemplate: `You are a frontend development expert. Your domain is limited to:
116
+ - React/Vue components and pages
117
+ - Form and table components
118
+ - UI component libraries
119
+ - Client-side state management
120
+
121
+ PATTERN DETECTION (CRITICAL for existing projects):
122
+ 1. Call codedna_detect_patterns(package_json) to detect existing patterns
123
+ 2. RESPECT detected patterns - consistency > "better":
124
+ - Compound components? Use compound pattern
125
+ - React Hook Form? Don't introduce Formik
126
+ - Zustand? Don't add Redux
127
+ - Tailwind? Match styling approach
128
+ 3. Check codedna_list_patterns(category="anti-patterns") for code smells to avoid
129
+
130
+ CODEDNA PRIORITY: Before writing UI components manually:
131
+ 1. Call codedna_list_generators() to discover available generators
132
+ 2. Detect UI framework from project (check package.json for react, vue, etc.)
133
+ 3. Check for UI library (package.json: @shadcn/ui, @mui/material, @chakra-ui, etc.)
134
+ 4. Call codedna_generate_frontend or codedna_generate_component with detected settings
135
+ 5. DO NOT assume any specific framework/library - discover via list_generators, detect from project
136
+
137
+ TOOLS: Use codebase_find to discover existing component patterns.
138
+ TOOLS: Use memory_search for past UI decisions.
139
+
140
+ Focus on components and pages. Do not modify API or schema code.
141
+ When complete, provide a concise summary of components created.`,
102
142
  },
103
143
  'general-expert': {
104
144
  id: 'general-expert',
@@ -108,7 +148,13 @@ When complete, provide a concise summary of integrations configured.`,
108
148
  capabilities: ['general-development', 'refactoring', 'documentation', 'testing'],
109
149
  promptTemplate: `You are a general purpose development expert.
110
150
 
111
- CODEDNA: For CRUD/API tasks, use codedna_generate_api before writing code manually.
151
+ CODEDNA - DISCOVER THEN DETECT:
152
+ 1. Call codedna_list_generators() to see what's available
153
+ 2. Detect project framework from package.json/pyproject.toml
154
+ 3. Match detected framework to generator capabilities
155
+ 4. If no match, ASK the user which framework to use
156
+ 5. DO NOT assume any specific framework exists - always discover first
157
+
112
158
  TOOLS: Use memory_search and codebase_find before implementing from scratch.
113
159
 
114
160
  Handle this task with care:
@@ -267,7 +313,6 @@ export function matchTaskToWorker(task) {
267
313
  'database': 'schema-expert',
268
314
  'sql': 'schema-expert',
269
315
  'migration': 'schema-expert',
270
- 'table': 'schema-expert',
271
316
  'api': 'api-expert',
272
317
  'route': 'api-expert',
273
318
  'endpoint': 'api-expert',
@@ -282,6 +327,14 @@ export function matchTaskToWorker(task) {
282
327
  'integration': 'integration-expert',
283
328
  'config': 'integration-expert',
284
329
  'wiring': 'integration-expert',
330
+ 'component': 'frontend-expert',
331
+ 'frontend': 'frontend-expert',
332
+ 'react': 'frontend-expert',
333
+ 'vue': 'frontend-expert',
334
+ 'form': 'frontend-expert',
335
+ 'table': 'frontend-expert',
336
+ 'page': 'frontend-expert',
337
+ 'ui': 'frontend-expert',
285
338
  };
286
339
  for (const [keyword, workerId] of Object.entries(keywordMap)) {
287
340
  if (searchText.includes(keyword)) {