@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.
- 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 +15 -7
- package/dist/templates/worker-prompt.js +24 -31
- package/dist/tools.js +101 -2
- package/package.json +1 -1
package/dist/codedna/registry.js
CHANGED
|
@@ -2,18 +2,29 @@
|
|
|
2
2
|
// Template Registry Client
|
|
3
3
|
// =============================================================================
|
|
4
4
|
//
|
|
5
|
-
// HTTP client for Cloudflare Workers template registry
|
|
6
|
-
//
|
|
5
|
+
// HTTP client for Cloudflare Workers template registry.
|
|
6
|
+
// Cloudflare D1 is the single source of truth for generators.
|
|
7
|
+
// Local file cache provides offline fallback for templates only.
|
|
8
|
+
//
|
|
9
|
+
// Registry data structure:
|
|
10
|
+
// - generators: flat list with domain field (api/frontend/component)
|
|
11
|
+
// - byDomain: grouped by domain for easy filtering
|
|
12
|
+
// - summary: counts by domain
|
|
7
13
|
//
|
|
8
14
|
import fs from 'fs/promises';
|
|
9
15
|
import path from 'path';
|
|
10
16
|
import { fileURLToPath } from 'url';
|
|
11
17
|
import { errorTracker } from '../helpers/error-tracking.js';
|
|
12
18
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
// Cache TTL in milliseconds (1 hour for registry, 24 hours for templates)
|
|
20
|
+
const REGISTRY_CACHE_TTL = 60 * 60 * 1000;
|
|
21
|
+
const TEMPLATE_CACHE_TTL = 24 * 60 * 60 * 1000;
|
|
13
22
|
export class TemplateRegistry {
|
|
14
23
|
baseUrl;
|
|
15
24
|
cacheDir;
|
|
16
25
|
useCache;
|
|
26
|
+
// In-memory cache for registry data
|
|
27
|
+
registryCache = null;
|
|
17
28
|
constructor(baseUrl = 'https://api.claudetools.dev/api/v1/codedna', useCache = true) {
|
|
18
29
|
this.baseUrl = baseUrl;
|
|
19
30
|
this.cacheDir = path.join(__dirname, '../../templates');
|
|
@@ -21,21 +32,54 @@ export class TemplateRegistry {
|
|
|
21
32
|
}
|
|
22
33
|
/**
|
|
23
34
|
* List all available generators
|
|
35
|
+
* Fetches from Cloudflare D1 (single source of truth)
|
|
36
|
+
* @param domain Optional domain filter: 'api' | 'frontend' | 'component'
|
|
24
37
|
*/
|
|
25
|
-
async listGenerators() {
|
|
38
|
+
async listGenerators(domain) {
|
|
39
|
+
// Check in-memory cache first
|
|
40
|
+
if (this.registryCache && Date.now() - this.registryCache.timestamp < REGISTRY_CACHE_TTL) {
|
|
41
|
+
const cached = this.registryCache.data;
|
|
42
|
+
if (domain) {
|
|
43
|
+
return cached.filter(g => g.domain === domain);
|
|
44
|
+
}
|
|
45
|
+
return cached;
|
|
46
|
+
}
|
|
26
47
|
try {
|
|
27
|
-
const
|
|
28
|
-
|
|
48
|
+
const url = domain
|
|
49
|
+
? `${this.baseUrl}/registry?domain=${domain}`
|
|
50
|
+
: `${this.baseUrl}/registry`;
|
|
51
|
+
const response = await fetch(url, {
|
|
52
|
+
signal: AbortSignal.timeout(5000),
|
|
29
53
|
});
|
|
30
54
|
if (!response.ok) {
|
|
31
55
|
throw new Error(`Registry fetch failed: ${response.statusText}`);
|
|
32
56
|
}
|
|
33
|
-
const
|
|
34
|
-
|
|
57
|
+
const result = await response.json();
|
|
58
|
+
if (!result.success || !result.data?.generators) {
|
|
59
|
+
throw new Error('Invalid registry response');
|
|
60
|
+
}
|
|
61
|
+
// Cache the full list (not filtered)
|
|
62
|
+
if (!domain) {
|
|
63
|
+
this.registryCache = {
|
|
64
|
+
data: result.data.generators,
|
|
65
|
+
timestamp: Date.now(),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return result.data.generators;
|
|
35
69
|
}
|
|
36
70
|
catch (error) {
|
|
37
|
-
console.warn('Failed to fetch from registry
|
|
38
|
-
|
|
71
|
+
console.warn('Failed to fetch from registry:', error);
|
|
72
|
+
// Try to use stale cache if available
|
|
73
|
+
if (this.registryCache) {
|
|
74
|
+
console.warn('Using stale registry cache');
|
|
75
|
+
const stale = this.registryCache.data;
|
|
76
|
+
if (domain) {
|
|
77
|
+
return stale.filter(g => g.domain === domain);
|
|
78
|
+
}
|
|
79
|
+
return stale;
|
|
80
|
+
}
|
|
81
|
+
// No cache available - throw error (Cloudflare is required)
|
|
82
|
+
throw new Error('Registry unavailable and no cache. Please check your network connection or try again later.');
|
|
39
83
|
}
|
|
40
84
|
}
|
|
41
85
|
/**
|
|
@@ -47,49 +91,64 @@ export class TemplateRegistry {
|
|
|
47
91
|
signal: AbortSignal.timeout(5000),
|
|
48
92
|
});
|
|
49
93
|
if (!response.ok) {
|
|
94
|
+
if (response.status === 404) {
|
|
95
|
+
throw new Error(`Generator '${generatorId}' not found`);
|
|
96
|
+
}
|
|
50
97
|
throw new Error(`Metadata fetch failed: ${response.statusText}`);
|
|
51
98
|
}
|
|
52
|
-
|
|
99
|
+
const result = await response.json();
|
|
100
|
+
return result.data || result;
|
|
53
101
|
}
|
|
54
102
|
catch (error) {
|
|
55
|
-
|
|
56
|
-
|
|
103
|
+
// Try to find in cached registry
|
|
104
|
+
if (this.registryCache) {
|
|
105
|
+
const cached = this.registryCache.data.find(g => g.id === generatorId);
|
|
106
|
+
if (cached) {
|
|
107
|
+
console.warn('Using cached metadata for:', generatorId);
|
|
108
|
+
return cached;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
throw error;
|
|
57
112
|
}
|
|
58
113
|
}
|
|
59
114
|
/**
|
|
60
115
|
* Get template file content
|
|
61
116
|
*/
|
|
62
117
|
async getTemplate(generatorId, templateFile) {
|
|
63
|
-
// Check cache first
|
|
118
|
+
// Check file cache first
|
|
64
119
|
if (this.useCache) {
|
|
65
120
|
const cached = await this.getCachedTemplate(generatorId, templateFile);
|
|
66
121
|
if (cached)
|
|
67
122
|
return cached;
|
|
68
123
|
}
|
|
69
|
-
// Fetch from
|
|
124
|
+
// Fetch from Cloudflare
|
|
70
125
|
try {
|
|
71
126
|
const response = await fetch(`${this.baseUrl}/templates/${generatorId}/${templateFile}`, {
|
|
72
|
-
signal: AbortSignal.timeout(10000),
|
|
127
|
+
signal: AbortSignal.timeout(10000),
|
|
73
128
|
});
|
|
74
129
|
if (!response.ok) {
|
|
130
|
+
if (response.status === 404) {
|
|
131
|
+
throw new Error(`Template '${templateFile}' not found for generator '${generatorId}'`);
|
|
132
|
+
}
|
|
75
133
|
throw new Error(`Template fetch failed: ${response.statusText}`);
|
|
76
134
|
}
|
|
77
135
|
const content = await response.text();
|
|
78
|
-
// Cache
|
|
136
|
+
// Cache locally for offline use
|
|
79
137
|
if (this.useCache) {
|
|
80
138
|
await this.cacheTemplate(generatorId, templateFile, content);
|
|
81
139
|
}
|
|
82
140
|
return content;
|
|
83
141
|
}
|
|
84
142
|
catch (error) {
|
|
85
|
-
console.warn('Failed to fetch template
|
|
86
|
-
// Track
|
|
143
|
+
console.warn('Failed to fetch template:', error);
|
|
144
|
+
// Track fetch error
|
|
87
145
|
await errorTracker.trackTemplateFetchError(generatorId, templateFile, error instanceof Error ? error : new Error(String(error)));
|
|
146
|
+
// Try local bundled fallback
|
|
88
147
|
return this.getLocalTemplate(generatorId, templateFile);
|
|
89
148
|
}
|
|
90
149
|
}
|
|
91
150
|
/**
|
|
92
|
-
* Get multiple templates at once
|
|
151
|
+
* Get multiple templates at once (parallel fetch)
|
|
93
152
|
*/
|
|
94
153
|
async getTemplates(generatorId, templateFiles) {
|
|
95
154
|
const results = {};
|
|
@@ -99,11 +158,16 @@ export class TemplateRegistry {
|
|
|
99
158
|
return results;
|
|
100
159
|
}
|
|
101
160
|
/**
|
|
102
|
-
*
|
|
161
|
+
* Get cached template from file system
|
|
103
162
|
*/
|
|
104
163
|
async getCachedTemplate(generatorId, templateFile) {
|
|
105
164
|
try {
|
|
106
165
|
const cachePath = path.join(this.cacheDir, generatorId, templateFile);
|
|
166
|
+
const stats = await fs.stat(cachePath);
|
|
167
|
+
// Check if cache is still valid
|
|
168
|
+
if (Date.now() - stats.mtimeMs > TEMPLATE_CACHE_TTL) {
|
|
169
|
+
return null; // Cache expired
|
|
170
|
+
}
|
|
107
171
|
return await fs.readFile(cachePath, 'utf-8');
|
|
108
172
|
}
|
|
109
173
|
catch {
|
|
@@ -111,7 +175,7 @@ export class TemplateRegistry {
|
|
|
111
175
|
}
|
|
112
176
|
}
|
|
113
177
|
/**
|
|
114
|
-
* Cache template
|
|
178
|
+
* Cache template to file system
|
|
115
179
|
*/
|
|
116
180
|
async cacheTemplate(generatorId, templateFile, content) {
|
|
117
181
|
try {
|
|
@@ -124,245 +188,24 @@ export class TemplateRegistry {
|
|
|
124
188
|
}
|
|
125
189
|
}
|
|
126
190
|
/**
|
|
127
|
-
* Get local
|
|
128
|
-
*/
|
|
129
|
-
async getLocalGenerators() {
|
|
130
|
-
// Return hardcoded list of bundled generators
|
|
131
|
-
return [
|
|
132
|
-
{
|
|
133
|
-
id: 'express-api',
|
|
134
|
-
name: 'Express REST API',
|
|
135
|
-
framework: 'express',
|
|
136
|
-
description: 'Generate TypeScript Express API with CRUD operations',
|
|
137
|
-
features: ['auth', 'validation', 'tests'],
|
|
138
|
-
databases: ['postgresql', 'mysql', 'mongodb'],
|
|
139
|
-
version: '1.0.0',
|
|
140
|
-
templates: [
|
|
141
|
-
'controller.ts.j2',
|
|
142
|
-
'model.ts.j2',
|
|
143
|
-
'route.ts.j2',
|
|
144
|
-
'middleware.ts.j2',
|
|
145
|
-
'validator.ts.j2',
|
|
146
|
-
],
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
id: 'fastapi-api',
|
|
150
|
-
name: 'FastAPI REST API',
|
|
151
|
-
framework: 'fastapi',
|
|
152
|
-
description: 'Generate Python FastAPI with Pydantic models and SQLAlchemy ORM',
|
|
153
|
-
features: ['auth', 'validation', 'tests'],
|
|
154
|
-
databases: ['postgresql', 'mysql', 'mongodb'],
|
|
155
|
-
version: '1.0.0',
|
|
156
|
-
templates: [
|
|
157
|
-
'model.py.j2',
|
|
158
|
-
'schemas.py.j2',
|
|
159
|
-
'router.py.j2',
|
|
160
|
-
'crud.py.j2',
|
|
161
|
-
'test_api.py.j2',
|
|
162
|
-
],
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
id: 'nestjs-api',
|
|
166
|
-
name: 'NestJS REST API',
|
|
167
|
-
framework: 'nestjs',
|
|
168
|
-
description: 'Generate TypeScript NestJS with TypeORM entities and DTOs',
|
|
169
|
-
features: ['auth', 'validation', 'tests'],
|
|
170
|
-
databases: ['postgresql', 'mysql', 'mongodb'],
|
|
171
|
-
version: '1.0.0',
|
|
172
|
-
templates: [
|
|
173
|
-
'entity.ts.j2',
|
|
174
|
-
'dto.ts.j2',
|
|
175
|
-
'controller.ts.j2',
|
|
176
|
-
'service.ts.j2',
|
|
177
|
-
'module.ts.j2',
|
|
178
|
-
'spec.ts.j2',
|
|
179
|
-
],
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
id: 'react-frontend',
|
|
183
|
-
name: 'React/Next.js Frontend',
|
|
184
|
-
framework: 'react',
|
|
185
|
-
description: 'Generate React components with ShadcnUI for forms, tables, and pages',
|
|
186
|
-
features: ['forms', 'tables', 'routing'],
|
|
187
|
-
databases: [],
|
|
188
|
-
version: '1.0.0',
|
|
189
|
-
templates: [
|
|
190
|
-
'form.tsx.j2',
|
|
191
|
-
'table.tsx.j2',
|
|
192
|
-
'list-page.tsx.j2',
|
|
193
|
-
'detail-page.tsx.j2',
|
|
194
|
-
'hooks.ts.j2',
|
|
195
|
-
],
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
id: 'vue-frontend',
|
|
199
|
-
name: 'Vue 3 Frontend',
|
|
200
|
-
framework: 'vue',
|
|
201
|
-
description: 'Generate Vue 3 components with Composition API for forms, tables, and views',
|
|
202
|
-
features: ['forms', 'tables', 'routing'],
|
|
203
|
-
databases: [],
|
|
204
|
-
version: '1.0.0',
|
|
205
|
-
templates: [
|
|
206
|
-
'Form.vue.j2',
|
|
207
|
-
'Table.vue.j2',
|
|
208
|
-
'ListView.vue.j2',
|
|
209
|
-
'DetailView.vue.j2',
|
|
210
|
-
'composables.ts.j2',
|
|
211
|
-
],
|
|
212
|
-
},
|
|
213
|
-
// Component generators
|
|
214
|
-
{
|
|
215
|
-
id: 'react-form',
|
|
216
|
-
name: 'React Form Component',
|
|
217
|
-
framework: 'react',
|
|
218
|
-
description: 'Generate React form with Zod validation and ShadcnUI',
|
|
219
|
-
features: ['validation'],
|
|
220
|
-
databases: [],
|
|
221
|
-
version: '1.0.0',
|
|
222
|
-
templates: ['form.tsx.j2'],
|
|
223
|
-
},
|
|
224
|
-
{
|
|
225
|
-
id: 'react-table',
|
|
226
|
-
name: 'React Table Component',
|
|
227
|
-
framework: 'react',
|
|
228
|
-
description: 'Generate React data table with ShadcnUI',
|
|
229
|
-
features: [],
|
|
230
|
-
databases: [],
|
|
231
|
-
version: '1.0.0',
|
|
232
|
-
templates: ['table.tsx.j2'],
|
|
233
|
-
},
|
|
234
|
-
{
|
|
235
|
-
id: 'react-card',
|
|
236
|
-
name: 'React Card Component',
|
|
237
|
-
framework: 'react',
|
|
238
|
-
description: 'Generate React card component with ShadcnUI',
|
|
239
|
-
features: [],
|
|
240
|
-
databases: [],
|
|
241
|
-
version: '1.0.0',
|
|
242
|
-
templates: ['card.tsx.j2'],
|
|
243
|
-
},
|
|
244
|
-
{
|
|
245
|
-
id: 'react-modal',
|
|
246
|
-
name: 'React Modal Component',
|
|
247
|
-
framework: 'react',
|
|
248
|
-
description: 'Generate React modal/dialog with ShadcnUI',
|
|
249
|
-
features: [],
|
|
250
|
-
databases: [],
|
|
251
|
-
version: '1.0.0',
|
|
252
|
-
templates: ['modal.tsx.j2'],
|
|
253
|
-
},
|
|
254
|
-
{
|
|
255
|
-
id: 'vue-form',
|
|
256
|
-
name: 'Vue Form Component',
|
|
257
|
-
framework: 'vue',
|
|
258
|
-
description: 'Generate Vue 3 form with Vuelidate',
|
|
259
|
-
features: ['validation'],
|
|
260
|
-
databases: [],
|
|
261
|
-
version: '1.0.0',
|
|
262
|
-
templates: ['form.vue.j2'],
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
id: 'vue-table',
|
|
266
|
-
name: 'Vue Table Component',
|
|
267
|
-
framework: 'vue',
|
|
268
|
-
description: 'Generate Vue 3 data table component',
|
|
269
|
-
features: [],
|
|
270
|
-
databases: [],
|
|
271
|
-
version: '1.0.0',
|
|
272
|
-
templates: ['table.vue.j2'],
|
|
273
|
-
},
|
|
274
|
-
{
|
|
275
|
-
id: 'vue-card',
|
|
276
|
-
name: 'Vue Card Component',
|
|
277
|
-
framework: 'vue',
|
|
278
|
-
description: 'Generate Vue 3 card component',
|
|
279
|
-
features: [],
|
|
280
|
-
databases: [],
|
|
281
|
-
version: '1.0.0',
|
|
282
|
-
templates: ['card.vue.j2'],
|
|
283
|
-
},
|
|
284
|
-
{
|
|
285
|
-
id: 'vue-modal',
|
|
286
|
-
name: 'Vue Modal Component',
|
|
287
|
-
framework: 'vue',
|
|
288
|
-
description: 'Generate Vue 3 modal with Teleport',
|
|
289
|
-
features: [],
|
|
290
|
-
databases: [],
|
|
291
|
-
version: '1.0.0',
|
|
292
|
-
templates: ['modal.vue.j2'],
|
|
293
|
-
},
|
|
294
|
-
{
|
|
295
|
-
id: 'svelte-form',
|
|
296
|
-
name: 'Svelte Form Component',
|
|
297
|
-
framework: 'svelte',
|
|
298
|
-
description: 'Generate Svelte form with reactive bindings',
|
|
299
|
-
features: ['validation'],
|
|
300
|
-
databases: [],
|
|
301
|
-
version: '1.0.0',
|
|
302
|
-
templates: ['form.svelte.j2'],
|
|
303
|
-
},
|
|
304
|
-
{
|
|
305
|
-
id: 'svelte-table',
|
|
306
|
-
name: 'Svelte Table Component',
|
|
307
|
-
framework: 'svelte',
|
|
308
|
-
description: 'Generate Svelte data table component',
|
|
309
|
-
features: [],
|
|
310
|
-
databases: [],
|
|
311
|
-
version: '1.0.0',
|
|
312
|
-
templates: ['table.svelte.j2'],
|
|
313
|
-
},
|
|
314
|
-
{
|
|
315
|
-
id: 'svelte-card',
|
|
316
|
-
name: 'Svelte Card Component',
|
|
317
|
-
framework: 'svelte',
|
|
318
|
-
description: 'Generate Svelte card component',
|
|
319
|
-
features: [],
|
|
320
|
-
databases: [],
|
|
321
|
-
version: '1.0.0',
|
|
322
|
-
templates: ['card.svelte.j2'],
|
|
323
|
-
},
|
|
324
|
-
{
|
|
325
|
-
id: 'svelte-modal',
|
|
326
|
-
name: 'Svelte Modal Component',
|
|
327
|
-
framework: 'svelte',
|
|
328
|
-
description: 'Generate Svelte modal component',
|
|
329
|
-
features: [],
|
|
330
|
-
databases: [],
|
|
331
|
-
version: '1.0.0',
|
|
332
|
-
templates: ['modal.svelte.j2'],
|
|
333
|
-
},
|
|
334
|
-
];
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* Get local metadata (fallback)
|
|
338
|
-
*/
|
|
339
|
-
async getLocalMetadata(generatorId) {
|
|
340
|
-
const generators = await this.getLocalGenerators();
|
|
341
|
-
const generator = generators.find(g => g.id === generatorId);
|
|
342
|
-
if (!generator) {
|
|
343
|
-
throw new Error(`Generator not found: ${generatorId}`);
|
|
344
|
-
}
|
|
345
|
-
return generator;
|
|
346
|
-
}
|
|
347
|
-
/**
|
|
348
|
-
* Get local template (fallback)
|
|
191
|
+
* Get template from bundled local files (fallback)
|
|
349
192
|
*/
|
|
350
193
|
async getLocalTemplate(generatorId, templateFile) {
|
|
351
|
-
// Try to load from local bundled templates
|
|
352
194
|
const localPath = path.join(__dirname, '../../../cloudflare/templates', generatorId, templateFile);
|
|
353
195
|
try {
|
|
354
196
|
return await fs.readFile(localPath, 'utf-8');
|
|
355
197
|
}
|
|
356
|
-
catch
|
|
198
|
+
catch {
|
|
357
199
|
throw new Error(`Template not found: ${generatorId}/${templateFile}. Registry unavailable and local fallback missing.`);
|
|
358
200
|
}
|
|
359
201
|
}
|
|
360
202
|
/**
|
|
361
|
-
* Clear local cache
|
|
203
|
+
* Clear local template cache
|
|
362
204
|
*/
|
|
363
205
|
async clearCache() {
|
|
364
206
|
try {
|
|
365
207
|
await fs.rm(this.cacheDir, { recursive: true, force: true });
|
|
208
|
+
this.registryCache = null;
|
|
366
209
|
}
|
|
367
210
|
catch (error) {
|
|
368
211
|
console.warn('Failed to clear cache:', error);
|
|
@@ -372,32 +215,29 @@ export class TemplateRegistry {
|
|
|
372
215
|
* Get cache statistics
|
|
373
216
|
*/
|
|
374
217
|
async getCacheStats() {
|
|
218
|
+
const stats = {
|
|
219
|
+
registryCached: !!this.registryCache,
|
|
220
|
+
registryAge: this.registryCache ? Date.now() - this.registryCache.timestamp : null,
|
|
221
|
+
cachedGenerators: [],
|
|
222
|
+
totalFiles: 0,
|
|
223
|
+
totalSize: 0,
|
|
224
|
+
};
|
|
375
225
|
try {
|
|
376
226
|
const entries = await fs.readdir(this.cacheDir, { withFileTypes: true });
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
let totalSize = 0;
|
|
380
|
-
for (const gen of generators) {
|
|
227
|
+
stats.cachedGenerators = entries.filter(e => e.isDirectory()).map(e => e.name);
|
|
228
|
+
for (const gen of stats.cachedGenerators) {
|
|
381
229
|
const genPath = path.join(this.cacheDir, gen);
|
|
382
230
|
const files = await fs.readdir(genPath);
|
|
383
|
-
totalFiles += files.length;
|
|
231
|
+
stats.totalFiles += files.length;
|
|
384
232
|
for (const file of files) {
|
|
385
|
-
const
|
|
386
|
-
totalSize +=
|
|
233
|
+
const fileStats = await fs.stat(path.join(genPath, file));
|
|
234
|
+
stats.totalSize += fileStats.size;
|
|
387
235
|
}
|
|
388
236
|
}
|
|
389
|
-
return {
|
|
390
|
-
cachedGenerators: generators,
|
|
391
|
-
totalFiles,
|
|
392
|
-
totalSize,
|
|
393
|
-
};
|
|
394
237
|
}
|
|
395
238
|
catch {
|
|
396
|
-
|
|
397
|
-
cachedGenerators: [],
|
|
398
|
-
totalFiles: 0,
|
|
399
|
-
totalSize: 0,
|
|
400
|
-
};
|
|
239
|
+
// Cache directory doesn't exist yet
|
|
401
240
|
}
|
|
241
|
+
return stats;
|
|
402
242
|
}
|
|
403
243
|
}
|
|
@@ -171,6 +171,9 @@ export class TemplateEngine {
|
|
|
171
171
|
* Helper to build template context from EntitySpec
|
|
172
172
|
*/
|
|
173
173
|
export function buildContext(entity, options = {}) {
|
|
174
|
+
// Pattern detection helpers
|
|
175
|
+
const patterns = options.patterns || { detected: [], preferred: [], avoid: [] };
|
|
176
|
+
const detectedPatternIds = patterns.detected?.map((p) => p.pattern_id) || [];
|
|
174
177
|
return {
|
|
175
178
|
entity,
|
|
176
179
|
options,
|
|
@@ -179,5 +182,25 @@ export function buildContext(entity, options = {}) {
|
|
|
179
182
|
hasUniqueFields: entity.fields.some(f => f.constraints.some(c => c.kind === 'unique')),
|
|
180
183
|
hasHashedFields: entity.fields.some(f => f.constraints.some(c => c.kind === 'hashed')),
|
|
181
184
|
hasReferences: entity.fields.some(f => f.type.kind === 'reference'),
|
|
185
|
+
// Pattern context for templates
|
|
186
|
+
patterns: {
|
|
187
|
+
detected: patterns.detected || [],
|
|
188
|
+
preferred: patterns.preferred || [],
|
|
189
|
+
avoid: patterns.avoid || [],
|
|
190
|
+
// Quick lookup helpers
|
|
191
|
+
hasPattern: (patternId) => detectedPatternIds.includes(patternId),
|
|
192
|
+
usesZod: detectedPatternIds.includes('zod-validation'),
|
|
193
|
+
usesYup: detectedPatternIds.includes('yup-validation'),
|
|
194
|
+
usesReactHookForm: detectedPatternIds.includes('react-hook-form'),
|
|
195
|
+
usesFormik: detectedPatternIds.includes('formik'),
|
|
196
|
+
usesTanstackQuery: detectedPatternIds.includes('tanstack-query'),
|
|
197
|
+
usesSwr: detectedPatternIds.includes('swr-pattern'),
|
|
198
|
+
usesZustand: detectedPatternIds.includes('zustand'),
|
|
199
|
+
usesRedux: detectedPatternIds.includes('redux-toolkit'),
|
|
200
|
+
usesTailwind: detectedPatternIds.includes('tailwind'),
|
|
201
|
+
usesShadcn: detectedPatternIds.includes('shadcn-ui'),
|
|
202
|
+
usesMui: detectedPatternIds.includes('mui-patterns'),
|
|
203
|
+
usesChakra: detectedPatternIds.includes('chakra-patterns'),
|
|
204
|
+
},
|
|
182
205
|
};
|
|
183
206
|
}
|
package/dist/codedna/types.d.ts
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
import { EntitySpec } from './parser.js';
|
|
2
|
+
/**
|
|
3
|
+
* Detected pattern from codebase analysis
|
|
4
|
+
*/
|
|
5
|
+
export interface DetectedPattern {
|
|
6
|
+
pattern_id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
category: 'components' | 'hooks' | 'forms' | 'state' | 'validation' | 'styling' | 'anti-patterns';
|
|
9
|
+
confidence: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Pattern context for generation
|
|
13
|
+
*/
|
|
14
|
+
export interface PatternContext {
|
|
15
|
+
detected: DetectedPattern[];
|
|
16
|
+
preferred: string[];
|
|
17
|
+
avoid: string[];
|
|
18
|
+
}
|
|
2
19
|
/**
|
|
3
20
|
* Options for API generation
|
|
4
21
|
*/
|
|
@@ -7,6 +24,7 @@ export interface GenerateApiOptions {
|
|
|
7
24
|
validation?: boolean;
|
|
8
25
|
tests?: boolean;
|
|
9
26
|
database?: 'postgresql' | 'mysql' | 'mongodb';
|
|
27
|
+
patterns?: PatternContext;
|
|
10
28
|
}
|
|
11
29
|
/**
|
|
12
30
|
* Options for frontend generation
|
|
@@ -16,6 +34,7 @@ export interface GenerateFrontendOptions {
|
|
|
16
34
|
forms?: boolean;
|
|
17
35
|
tables?: boolean;
|
|
18
36
|
routing?: boolean;
|
|
37
|
+
patterns?: PatternContext;
|
|
19
38
|
}
|
|
20
39
|
/**
|
|
21
40
|
* Options for component generation
|
|
@@ -23,6 +42,7 @@ export interface GenerateFrontendOptions {
|
|
|
23
42
|
export interface GenerateComponentOptions {
|
|
24
43
|
ui?: 'shadcn' | 'mui' | 'chakra';
|
|
25
44
|
validation?: boolean;
|
|
45
|
+
patterns?: PatternContext;
|
|
26
46
|
}
|
|
27
47
|
/**
|
|
28
48
|
* Generated code result
|
|
@@ -48,10 +68,12 @@ export interface GenerationMetadata {
|
|
|
48
68
|
export interface GeneratorMetadata {
|
|
49
69
|
id: string;
|
|
50
70
|
name: string;
|
|
71
|
+
domain?: 'api' | 'frontend' | 'component';
|
|
51
72
|
framework: string;
|
|
52
73
|
description: string;
|
|
53
74
|
features: string[];
|
|
54
75
|
databases?: string[];
|
|
76
|
+
uiLibraries?: string[];
|
|
55
77
|
version: string;
|
|
56
78
|
templates: string[];
|
|
57
79
|
}
|