@claudetools/tools 0.6.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.
- package/dist/codedna/generators/base.d.ts +11 -2
- package/dist/codedna/generators/base.js +91 -8
- package/dist/codedna/generators/react-frontend.js +2 -1
- package/dist/codedna/parser.d.ts +6 -0
- package/dist/codedna/parser.js +7 -0
- package/dist/codedna/registry.d.ts +23 -17
- package/dist/codedna/registry.js +103 -263
- package/dist/codedna/template-engine.js +23 -0
- package/dist/codedna/types.d.ts +22 -0
- package/dist/handlers/codedna-handlers.d.ts +219 -6
- package/dist/handlers/codedna-handlers.js +379 -11
- package/dist/handlers/tool-handlers.js +45 -2
- package/dist/helpers/workers.js +60 -7
- package/dist/templates/orchestrator-prompt.js +34 -7
- package/dist/templates/worker-prompt.js +56 -31
- package/dist/tools.js +101 -2
- package/package.json +1 -1
|
@@ -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,
|
|
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:
|
|
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,
|
|
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:
|
|
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,
|
|
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:
|
|
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
|
|
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
|
}
|
package/dist/helpers/workers.js
CHANGED
|
@@ -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
|
-
|
|
41
|
-
|
|
42
|
-
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
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)) {
|