@claudetools/tools 0.9.0 → 0.9.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.
Files changed (85) hide show
  1. package/dist/cli.js +9 -1
  2. package/dist/codedna/__tests__/examples/mongoose-example.d.ts +6 -0
  3. package/dist/codedna/__tests__/examples/mongoose-example.js +163 -0
  4. package/dist/codedna/__tests__/fixtures/typeorm-production-test.d.ts +1 -0
  5. package/dist/codedna/__tests__/fixtures/typeorm-production-test.js +231 -0
  6. package/dist/codedna/__tests__/fixtures/typeorm-test.d.ts +1 -0
  7. package/dist/codedna/__tests__/fixtures/typeorm-test.js +124 -0
  8. package/dist/codedna/__tests__/laravel-output-review.d.ts +1 -0
  9. package/dist/codedna/__tests__/laravel-output-review.js +249 -0
  10. package/dist/codedna/__tests__/mongoose-output-test.d.ts +1 -0
  11. package/dist/codedna/__tests__/mongoose-output-test.js +178 -0
  12. package/dist/codedna/examples/radix-example.d.ts +2 -0
  13. package/dist/codedna/examples/radix-example.js +259 -0
  14. package/dist/codedna/index.d.ts +5 -3
  15. package/dist/codedna/index.js +6 -3
  16. package/dist/codedna/kappa-ast.d.ts +143 -5
  17. package/dist/codedna/kappa-drizzle-generator.js +8 -5
  18. package/dist/codedna/kappa-gofiber-generator.d.ts +65 -0
  19. package/dist/codedna/kappa-gofiber-generator.js +587 -0
  20. package/dist/codedna/kappa-laravel-generator.d.ts +68 -0
  21. package/dist/codedna/kappa-laravel-generator.js +741 -0
  22. package/dist/codedna/kappa-lexer.d.ts +44 -0
  23. package/dist/codedna/kappa-lexer.js +124 -0
  24. package/dist/codedna/kappa-mantine-generator.d.ts +65 -0
  25. package/dist/codedna/kappa-mantine-generator.js +518 -0
  26. package/dist/codedna/kappa-mongoose-generator.d.ts +44 -0
  27. package/dist/codedna/kappa-mongoose-generator.js +442 -0
  28. package/dist/codedna/kappa-parser.d.ts +43 -1
  29. package/dist/codedna/kappa-parser.js +601 -0
  30. package/dist/codedna/kappa-radix-generator.d.ts +61 -0
  31. package/dist/codedna/kappa-radix-generator.js +566 -0
  32. package/dist/codedna/kappa-typeorm-generator.d.ts +59 -0
  33. package/dist/codedna/kappa-typeorm-generator.js +723 -0
  34. package/dist/codedna/kappa-vitest-generator.d.ts +85 -0
  35. package/dist/codedna/kappa-vitest-generator.js +739 -0
  36. package/dist/codedna/parser.js +26 -1
  37. package/dist/codegen/cloud-client.d.ts +160 -0
  38. package/dist/codegen/cloud-client.js +195 -0
  39. package/dist/codegen/codegen-tool.d.ts +35 -0
  40. package/dist/codegen/codegen-tool.js +312 -0
  41. package/dist/codegen/field-inference.d.ts +24 -0
  42. package/dist/codegen/field-inference.js +101 -0
  43. package/dist/codegen/form-parser.d.ts +13 -0
  44. package/dist/codegen/form-parser.js +186 -0
  45. package/dist/codegen/index.d.ts +2 -0
  46. package/dist/codegen/index.js +4 -0
  47. package/dist/codegen/natural-parser.d.ts +50 -0
  48. package/dist/codegen/natural-parser.js +769 -0
  49. package/dist/handlers/codedna-handlers.d.ts +1 -1
  50. package/dist/handlers/codegen-handlers.d.ts +20 -0
  51. package/dist/handlers/codegen-handlers.js +60 -0
  52. package/dist/handlers/kappa-handlers.d.ts +97 -0
  53. package/dist/handlers/kappa-handlers.js +408 -0
  54. package/dist/handlers/tool-handlers.js +124 -221
  55. package/dist/helpers/api-client.js +48 -3
  56. package/dist/helpers/compact-formatter.d.ts +9 -2
  57. package/dist/helpers/compact-formatter.js +26 -2
  58. package/dist/helpers/config.d.ts +7 -2
  59. package/dist/helpers/config.js +25 -10
  60. package/dist/helpers/session-validation.d.ts +1 -1
  61. package/dist/helpers/session-validation.js +2 -4
  62. package/dist/helpers/tasks.d.ts +21 -0
  63. package/dist/helpers/tasks.js +52 -0
  64. package/dist/helpers/workers.d.ts +1 -1
  65. package/dist/helpers/workers.js +19 -19
  66. package/dist/setup.d.ts +1 -0
  67. package/dist/setup.js +228 -3
  68. package/dist/templates/claude-md.d.ts +1 -1
  69. package/dist/templates/claude-md.js +37 -152
  70. package/dist/templates/orchestrator-prompt.d.ts +2 -2
  71. package/dist/templates/orchestrator-prompt.js +31 -38
  72. package/dist/templates/self-critique.d.ts +50 -0
  73. package/dist/templates/self-critique.js +209 -0
  74. package/dist/templates/worker-prompt.d.ts +3 -3
  75. package/dist/templates/worker-prompt.js +18 -18
  76. package/dist/tools.js +77 -413
  77. package/docs/codedna/generator-testing-summary.md +205 -0
  78. package/docs/codedna/radix-ui-generator.md +478 -0
  79. package/docs/kappa-gofiber-generator.md +274 -0
  80. package/docs/kappa-laravel-fixes.md +172 -0
  81. package/docs/kappa-mongoose-generator.md +322 -0
  82. package/docs/kappa-vitest-generator.md +337 -0
  83. package/package.json +1 -1
  84. package/dist/context/deduplication.test.d.ts +0 -6
  85. package/dist/context/deduplication.test.js +0 -84
@@ -0,0 +1,587 @@
1
+ // =============================================================================
2
+ // Kappa v2.5 Go/Fiber Generator
3
+ // =============================================================================
4
+ //
5
+ // Generates Go code with Fiber framework from Kappa specifications.
6
+ // Supports: API routes, GORM models, validation.
7
+ //
8
+ // =============================================================================
9
+ // Type Mappings
10
+ // =============================================================================
11
+ const PRIMITIVE_GO_MAP = {
12
+ string: 'string',
13
+ int: 'int',
14
+ float: 'float64',
15
+ bool: 'bool',
16
+ timestamp: 'time.Time',
17
+ date: 'time.Time',
18
+ time: 'time.Time',
19
+ duration: 'time.Duration',
20
+ uuid: 'uuid.UUID',
21
+ email: 'string',
22
+ url: 'string',
23
+ phone: 'string',
24
+ slug: 'string',
25
+ markdown: 'string',
26
+ json: 'json.RawMessage',
27
+ };
28
+ const CRUD_METHOD_MAP = {
29
+ create: 'Post',
30
+ read: 'Get',
31
+ update: 'Patch',
32
+ delete: 'Delete',
33
+ list: 'Get',
34
+ };
35
+ const CRUD_PATH_SUFFIX = {
36
+ create: '',
37
+ read: '/:id',
38
+ update: '/:id',
39
+ delete: '/:id',
40
+ list: '',
41
+ };
42
+ const GORM_TAGS = {
43
+ string: '',
44
+ int: '',
45
+ float: '',
46
+ bool: '',
47
+ timestamp: '',
48
+ date: 'type:date',
49
+ time: 'type:time',
50
+ duration: '',
51
+ uuid: 'type:uuid',
52
+ email: '',
53
+ url: '',
54
+ phone: '',
55
+ slug: 'index',
56
+ markdown: 'type:text',
57
+ json: 'type:jsonb',
58
+ };
59
+ // =============================================================================
60
+ // Generator Class
61
+ // =============================================================================
62
+ export class KappaGoFiberGenerator {
63
+ packageName;
64
+ provenance;
65
+ gorm;
66
+ validation;
67
+ basePath;
68
+ modulePath;
69
+ constructor(options = {}) {
70
+ this.packageName = options.packageName ?? 'main';
71
+ this.provenance = options.provenance ?? true;
72
+ this.gorm = options.gorm ?? true;
73
+ this.validation = options.validation ?? true;
74
+ this.basePath = options.basePath ?? '/api';
75
+ this.modulePath = options.modulePath ?? 'app';
76
+ }
77
+ // ===========================================================================
78
+ // Main Generation Methods
79
+ // ===========================================================================
80
+ generateFromAPIs(apis) {
81
+ return {
82
+ routes: this.generateRoutes(apis),
83
+ handlers: this.generateHandlers(apis),
84
+ types: this.generateRequestTypes(apis),
85
+ };
86
+ }
87
+ generateFromEntities(entities) {
88
+ return {
89
+ routes: '',
90
+ models: this.generateModels(entities),
91
+ };
92
+ }
93
+ generateAll(apis, entities) {
94
+ return {
95
+ routes: this.generateRoutes(apis),
96
+ handlers: this.generateHandlers(apis),
97
+ types: this.generateRequestTypes(apis),
98
+ models: this.generateModels(entities),
99
+ };
100
+ }
101
+ // ===========================================================================
102
+ // Route Generation
103
+ // ===========================================================================
104
+ generateRoutes(apis) {
105
+ const lines = [];
106
+ const routes = this.extractRoutes(apis);
107
+ // Header
108
+ if (this.provenance) {
109
+ lines.push('// Generated by Kappa v2.5 CodeDNA');
110
+ lines.push('// Framework: Go/Fiber');
111
+ lines.push('');
112
+ }
113
+ lines.push(`package ${this.packageName}`);
114
+ lines.push('');
115
+ // Imports
116
+ lines.push('import (');
117
+ lines.push('\t"github.com/gofiber/fiber/v2"');
118
+ if (routes.some((r) => r.requiresAuth)) {
119
+ lines.push(`\t"${this.modulePath}/middleware"`);
120
+ }
121
+ lines.push(')');
122
+ lines.push('');
123
+ // Setup function
124
+ lines.push('// SetupRoutes configures all API routes');
125
+ lines.push('func SetupRoutes(app *fiber.App) {');
126
+ lines.push(`\tapi := app.Group("${this.basePath}")`);
127
+ lines.push('');
128
+ // Group routes by API
129
+ for (const api of apis) {
130
+ const apiRoutes = routes.filter((r) => this.isRouteFromAPI(r, api));
131
+ if (apiRoutes.length > 0) {
132
+ lines.push(`\t// ${api.name}`);
133
+ for (const route of apiRoutes) {
134
+ const middleware = route.requiresAuth ? 'middleware.RequireAuth, ' : '';
135
+ lines.push(`\tapi.${route.method}("${route.path}", ${middleware}${route.handlerName})`);
136
+ }
137
+ lines.push('');
138
+ }
139
+ }
140
+ lines.push('}');
141
+ lines.push('');
142
+ return lines.join('\n');
143
+ }
144
+ isRouteFromAPI(route, api) {
145
+ // Check explicit operations
146
+ if (api.operations?.some((op) => op.name === route.operationName)) {
147
+ return true;
148
+ }
149
+ // Check CRUD operations
150
+ for (const crud of api.crud || []) {
151
+ if (route.operationName.toLowerCase().endsWith(crud.entity.toLowerCase())) {
152
+ return true;
153
+ }
154
+ }
155
+ return false;
156
+ }
157
+ extractRoutes(apis) {
158
+ const routes = [];
159
+ for (const api of apis) {
160
+ const apiPath = `/${this.toKebabCase(api.name)}`;
161
+ // Convert CRUD shorthand (crud is an array)
162
+ for (const crud of api.crud || []) {
163
+ routes.push(...this.crudToRoutes(crud, apiPath));
164
+ }
165
+ // Convert explicit operations
166
+ if (api.operations) {
167
+ for (const op of api.operations) {
168
+ routes.push(this.operationToRoute(op, apiPath));
169
+ }
170
+ }
171
+ }
172
+ return routes;
173
+ }
174
+ operationToRoute(op, apiPath) {
175
+ const method = this.inferMethod(op.name);
176
+ const path = `${apiPath}/${this.toKebabCase(op.name)}`;
177
+ return {
178
+ method,
179
+ path,
180
+ handlerName: `Handle${this.toPascalCase(op.name)}`,
181
+ operationName: op.name,
182
+ effects: op.effects,
183
+ requiresAuth: op.effects.includes('Auth'),
184
+ parameters: op.parameters,
185
+ };
186
+ }
187
+ crudToRoutes(crud, apiPath) {
188
+ const routes = [];
189
+ const entityPath = `${apiPath}/${this.toKebabCase(crud.entity)}s`;
190
+ for (const action of crud.actions) {
191
+ const method = CRUD_METHOD_MAP[action.action] ?? 'Get';
192
+ const pathSuffix = CRUD_PATH_SUFFIX[action.action] ?? '';
193
+ routes.push({
194
+ method,
195
+ path: `${entityPath}${pathSuffix}`,
196
+ handlerName: `Handle${this.toPascalCase(action.action)}${crud.entity}`,
197
+ operationName: `${action.action}${crud.entity}`,
198
+ effects: action.effects,
199
+ requiresAuth: action.effects.includes('Auth'),
200
+ parameters: [],
201
+ });
202
+ }
203
+ return routes;
204
+ }
205
+ inferMethod(name) {
206
+ const lower = name.toLowerCase();
207
+ if (lower.startsWith('get') ||
208
+ lower.startsWith('list') ||
209
+ lower.startsWith('find') ||
210
+ lower.startsWith('fetch')) {
211
+ return 'Get';
212
+ }
213
+ if (lower.startsWith('create') || lower.startsWith('add') || lower.startsWith('register')) {
214
+ return 'Post';
215
+ }
216
+ if (lower.startsWith('update') || lower.startsWith('edit')) {
217
+ return 'Patch';
218
+ }
219
+ if (lower.startsWith('delete') || lower.startsWith('remove')) {
220
+ return 'Delete';
221
+ }
222
+ return 'Post';
223
+ }
224
+ // ===========================================================================
225
+ // Handler Generation
226
+ // ===========================================================================
227
+ generateHandlers(apis) {
228
+ const lines = [];
229
+ const routes = this.extractRoutes(apis);
230
+ if (this.provenance) {
231
+ lines.push('// Generated by Kappa v2.5 CodeDNA');
232
+ lines.push('// Framework: Go/Fiber');
233
+ lines.push('');
234
+ }
235
+ lines.push(`package ${this.packageName}`);
236
+ lines.push('');
237
+ lines.push('import (');
238
+ lines.push('\t"github.com/gofiber/fiber/v2"');
239
+ lines.push('\t"gorm.io/gorm"');
240
+ lines.push(')');
241
+ lines.push('');
242
+ lines.push('// db is the database instance (inject or initialize)');
243
+ lines.push('var db *gorm.DB');
244
+ lines.push('');
245
+ for (const route of routes) {
246
+ lines.push(this.generateHandler(route));
247
+ lines.push('');
248
+ }
249
+ return lines.join('\n');
250
+ }
251
+ generateHandler(route) {
252
+ const lines = [];
253
+ lines.push(`// ${route.handlerName} handles ${route.method} ${route.path}`);
254
+ if (route.effects.length > 0) {
255
+ lines.push(`// Effects: ${route.effects.join(', ')}`);
256
+ }
257
+ lines.push(`func ${route.handlerName}(c *fiber.Ctx) error {`);
258
+ // Check if this is a CRUD operation and generate proper implementation
259
+ const opLower = route.operationName.toLowerCase();
260
+ if (opLower.startsWith('list')) {
261
+ lines.push(...this.generateListHandler(route));
262
+ }
263
+ else if (opLower.startsWith('create')) {
264
+ lines.push(...this.generateCreateHandler(route));
265
+ }
266
+ else if (opLower.startsWith('read') || opLower.startsWith('get')) {
267
+ lines.push(...this.generateReadHandler(route));
268
+ }
269
+ else if (opLower.startsWith('update')) {
270
+ lines.push(...this.generateUpdateHandler(route));
271
+ }
272
+ else if (opLower.startsWith('delete')) {
273
+ lines.push(...this.generateDeleteHandler(route));
274
+ }
275
+ else {
276
+ // Custom operation - generate TODO stub
277
+ lines.push(`\t// TODO: Implement ${route.operationName}`);
278
+ lines.push('\treturn c.Status(501).JSON(fiber.Map{');
279
+ lines.push('\t\t"error": "Not implemented",');
280
+ lines.push('\t})');
281
+ }
282
+ lines.push('}');
283
+ return lines.join('\n');
284
+ }
285
+ generateListHandler(route) {
286
+ const entityName = this.extractEntityName(route.operationName);
287
+ return [
288
+ '\tvar items []' + entityName,
289
+ '\t',
290
+ '\t// TODO: Add pagination, filtering, sorting',
291
+ '\tif err := db.Find(&items).Error; err != nil {',
292
+ '\t\treturn c.Status(500).JSON(fiber.Map{"error": err.Error()})',
293
+ '\t}',
294
+ '\t',
295
+ '\treturn c.JSON(items)',
296
+ ];
297
+ }
298
+ generateCreateHandler(route) {
299
+ const entityName = this.extractEntityName(route.operationName);
300
+ return [
301
+ '\tvar item ' + entityName,
302
+ '\t',
303
+ '\tif err := c.BodyParser(&item); err != nil {',
304
+ '\t\treturn c.Status(400).JSON(fiber.Map{"error": "Invalid request body"})',
305
+ '\t}',
306
+ '\t',
307
+ '\t// TODO: Add validation',
308
+ '\tif err := db.Create(&item).Error; err != nil {',
309
+ '\t\treturn c.Status(500).JSON(fiber.Map{"error": err.Error()})',
310
+ '\t}',
311
+ '\t',
312
+ '\treturn c.Status(201).JSON(item)',
313
+ ];
314
+ }
315
+ generateReadHandler(route) {
316
+ const entityName = this.extractEntityName(route.operationName);
317
+ return [
318
+ '\tid := c.Params("id")',
319
+ '\tvar item ' + entityName,
320
+ '\t',
321
+ '\tif err := db.First(&item, "id = ?", id).Error; err != nil {',
322
+ '\t\treturn c.Status(404).JSON(fiber.Map{"error": "Not found"})',
323
+ '\t}',
324
+ '\t',
325
+ '\treturn c.JSON(item)',
326
+ ];
327
+ }
328
+ generateUpdateHandler(route) {
329
+ const entityName = this.extractEntityName(route.operationName);
330
+ return [
331
+ '\tid := c.Params("id")',
332
+ '\tvar item ' + entityName,
333
+ '\t',
334
+ '\tif err := db.First(&item, "id = ?", id).Error; err != nil {',
335
+ '\t\treturn c.Status(404).JSON(fiber.Map{"error": "Not found"})',
336
+ '\t}',
337
+ '\t',
338
+ '\tif err := c.BodyParser(&item); err != nil {',
339
+ '\t\treturn c.Status(400).JSON(fiber.Map{"error": "Invalid request body"})',
340
+ '\t}',
341
+ '\t',
342
+ '\t// TODO: Add validation',
343
+ '\tif err := db.Save(&item).Error; err != nil {',
344
+ '\t\treturn c.Status(500).JSON(fiber.Map{"error": err.Error()})',
345
+ '\t}',
346
+ '\t',
347
+ '\treturn c.JSON(item)',
348
+ ];
349
+ }
350
+ generateDeleteHandler(route) {
351
+ const entityName = this.extractEntityName(route.operationName);
352
+ return [
353
+ '\tid := c.Params("id")',
354
+ '\tvar item ' + entityName,
355
+ '\t',
356
+ '\tif err := db.First(&item, "id = ?", id).Error; err != nil {',
357
+ '\t\treturn c.Status(404).JSON(fiber.Map{"error": "Not found"})',
358
+ '\t}',
359
+ '\t',
360
+ '\tif err := db.Delete(&item).Error; err != nil {',
361
+ '\t\treturn c.Status(500).JSON(fiber.Map{"error": err.Error()})',
362
+ '\t}',
363
+ '\t',
364
+ '\treturn c.SendStatus(204)',
365
+ ];
366
+ }
367
+ extractEntityName(operationName) {
368
+ // Extract entity name from operation name like "listUser" -> "User" or "createPost" -> "Post"
369
+ const match = operationName.match(/(?:list|create|read|update|delete|get)(.+)/i);
370
+ return match ? match[1] : 'Entity';
371
+ }
372
+ // ===========================================================================
373
+ // Model Generation (GORM)
374
+ // ===========================================================================
375
+ generateModels(entities) {
376
+ const lines = [];
377
+ if (this.provenance) {
378
+ lines.push('// Generated by Kappa v2.5 CodeDNA');
379
+ lines.push('// ORM: GORM');
380
+ lines.push('');
381
+ }
382
+ lines.push(`package ${this.packageName}`);
383
+ lines.push('');
384
+ // Collect required imports
385
+ const imports = new Set();
386
+ imports.add('"time"');
387
+ imports.add('"github.com/google/uuid"');
388
+ imports.add('"gorm.io/gorm"');
389
+ imports.add('"encoding/json"');
390
+ lines.push('import (');
391
+ for (const imp of imports) {
392
+ lines.push(`\t${imp}`);
393
+ }
394
+ lines.push(')');
395
+ lines.push('');
396
+ // Suppress unused import warning
397
+ lines.push('var (');
398
+ lines.push('\t_ = time.Now');
399
+ lines.push('\t_ = uuid.New');
400
+ lines.push('\t_ json.RawMessage');
401
+ lines.push(')');
402
+ lines.push('');
403
+ for (const entity of entities) {
404
+ lines.push(this.generateModel(entity));
405
+ lines.push('');
406
+ }
407
+ return lines.join('\n');
408
+ }
409
+ generateModel(entity) {
410
+ const lines = [];
411
+ lines.push(`// ${entity.name} represents the ${entity.name.toLowerCase()} entity`);
412
+ lines.push(`type ${entity.name} struct {`);
413
+ // GORM base model if using UUID
414
+ const hasUUID = entity.fields.some((f) => f.type.kind === 'primitive' && f.type.type === 'uuid' && f.modifiers.includes('primary'));
415
+ if (!hasUUID) {
416
+ lines.push('\tgorm.Model');
417
+ }
418
+ for (const field of entity.fields) {
419
+ lines.push(this.generateModelField(field));
420
+ }
421
+ lines.push('}');
422
+ // TableName method
423
+ lines.push('');
424
+ lines.push(`// TableName returns the table name for ${entity.name}`);
425
+ lines.push(`func (${entity.name}) TableName() string {`);
426
+ lines.push(`\treturn "${this.pluralizeTableName(entity.name)}"`);
427
+ lines.push('}');
428
+ return lines.join('\n');
429
+ }
430
+ generateModelField(field) {
431
+ const goType = this.fieldToGoType(field.type);
432
+ const fieldName = this.toPascalCase(field.name);
433
+ const tags = this.generateFieldTags(field);
434
+ return `\t${fieldName} ${goType} ${tags}`;
435
+ }
436
+ fieldToGoType(type) {
437
+ switch (type.kind) {
438
+ case 'primitive':
439
+ return PRIMITIVE_GO_MAP[type.type] ?? 'string';
440
+ case 'array':
441
+ const elementType = this.fieldToGoType(type.itemType);
442
+ return `[]${elementType}`;
443
+ case 'reference':
444
+ return type.entity;
445
+ case 'enum':
446
+ return 'string';
447
+ default:
448
+ return 'interface{}';
449
+ }
450
+ }
451
+ generateFieldTags(field) {
452
+ const tags = [];
453
+ // JSON tag
454
+ const jsonName = this.toSnakeCase(field.name);
455
+ tags.push(`json:"${jsonName}"`);
456
+ // GORM tags
457
+ if (this.gorm) {
458
+ const gormParts = [];
459
+ gormParts.push(`column:${jsonName}`);
460
+ if (field.modifiers.includes('primary')) {
461
+ gormParts.push('primaryKey');
462
+ }
463
+ if (field.modifiers.includes('unique')) {
464
+ gormParts.push('uniqueIndex');
465
+ }
466
+ if (!field.modifiers.includes('optional')) {
467
+ gormParts.push('not null');
468
+ }
469
+ // Type-specific GORM tags
470
+ if (field.type.kind === 'primitive') {
471
+ const gormTypeTag = GORM_TAGS[field.type.type];
472
+ if (gormTypeTag) {
473
+ gormParts.push(gormTypeTag);
474
+ }
475
+ }
476
+ tags.push(`gorm:"${gormParts.join(';')}"`);
477
+ }
478
+ // Validation tags
479
+ if (this.validation) {
480
+ const validParts = [];
481
+ if (!field.modifiers.includes('optional')) {
482
+ validParts.push('required');
483
+ }
484
+ if (field.type.kind === 'primitive') {
485
+ if (field.type.type === 'email') {
486
+ validParts.push('email');
487
+ }
488
+ if (field.type.type === 'url') {
489
+ validParts.push('url');
490
+ }
491
+ if (field.type.type === 'uuid') {
492
+ validParts.push('uuid');
493
+ }
494
+ }
495
+ if (validParts.length > 0) {
496
+ tags.push(`validate:"${validParts.join(',')}"`);
497
+ }
498
+ }
499
+ return '`' + tags.join(' ') + '`';
500
+ }
501
+ // ===========================================================================
502
+ // Request/Response Types
503
+ // ===========================================================================
504
+ generateRequestTypes(apis) {
505
+ const lines = [];
506
+ const routes = this.extractRoutes(apis);
507
+ if (this.provenance) {
508
+ lines.push('// Generated by Kappa v2.5 CodeDNA');
509
+ lines.push('// Request/Response types for Go/Fiber');
510
+ lines.push('');
511
+ }
512
+ lines.push(`package ${this.packageName}`);
513
+ lines.push('');
514
+ for (const route of routes) {
515
+ if (route.parameters.length > 0) {
516
+ lines.push(this.generateRequestType(route));
517
+ lines.push('');
518
+ }
519
+ }
520
+ return lines.join('\n');
521
+ }
522
+ generateRequestType(route) {
523
+ const lines = [];
524
+ const typeName = `${this.toPascalCase(route.operationName)}Request`;
525
+ lines.push(`// ${typeName} represents the request body for ${route.operationName}`);
526
+ lines.push(`type ${typeName} struct {`);
527
+ for (const param of route.parameters) {
528
+ const goType = this.fieldToGoType(param.type);
529
+ const fieldName = this.toPascalCase(param.name);
530
+ const jsonName = this.toSnakeCase(param.name);
531
+ const optional = param.optional ? ',omitempty' : '';
532
+ lines.push(`\t${fieldName} ${goType} \`json:"${jsonName}${optional}"\``);
533
+ }
534
+ lines.push('}');
535
+ return lines.join('\n');
536
+ }
537
+ // ===========================================================================
538
+ // Utility Methods
539
+ // ===========================================================================
540
+ toPascalCase(str) {
541
+ return str
542
+ .replace(/[-_](.)/g, (_, c) => c.toUpperCase())
543
+ .replace(/^(.)/, (_, c) => c.toUpperCase());
544
+ }
545
+ toKebabCase(str) {
546
+ return str
547
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
548
+ .replace(/[_\s]+/g, '-')
549
+ .toLowerCase();
550
+ }
551
+ toSnakeCase(str) {
552
+ return str
553
+ .replace(/([a-z])([A-Z])/g, '$1_$2')
554
+ .replace(/[-\s]+/g, '_')
555
+ .toLowerCase();
556
+ }
557
+ pluralizeTableName(entityName) {
558
+ const snakeCase = this.toSnakeCase(entityName);
559
+ // Simple pluralization rules for table names
560
+ // If already plural (ends with 's'), don't add another 's'
561
+ if (snakeCase.endsWith('s')) {
562
+ return snakeCase;
563
+ }
564
+ if (snakeCase.endsWith('x') || snakeCase.endsWith('ch') || snakeCase.endsWith('sh')) {
565
+ return `${snakeCase}es`;
566
+ }
567
+ if (snakeCase.endsWith('y') && !/[aeiou]y$/.test(snakeCase)) {
568
+ return `${snakeCase.slice(0, -1)}ies`;
569
+ }
570
+ return `${snakeCase}s`;
571
+ }
572
+ }
573
+ // =============================================================================
574
+ // Convenience Functions
575
+ // =============================================================================
576
+ export function generateGoFiberAPI(apis, options) {
577
+ const generator = new KappaGoFiberGenerator(options);
578
+ return generator.generateFromAPIs(apis);
579
+ }
580
+ export function generateGoFiberModels(entities, options) {
581
+ const generator = new KappaGoFiberGenerator(options);
582
+ return generator.generateFromEntities(entities).models ?? '';
583
+ }
584
+ export function generateGoFiber(apis, entities, options) {
585
+ const generator = new KappaGoFiberGenerator(options);
586
+ return generator.generateAll(apis, entities);
587
+ }
@@ -0,0 +1,68 @@
1
+ import type { APIBlock, EntityBlock } from './kappa-ast.js';
2
+ export interface LaravelGeneratorOptions {
3
+ /** Namespace for models (default: 'App\\Models') */
4
+ modelNamespace?: string;
5
+ /** Namespace for controllers (default: 'App\\Http\\Controllers') */
6
+ controllerNamespace?: string;
7
+ /** Add provenance comments (default: true) */
8
+ provenance?: boolean;
9
+ /** Generate migrations (default: true) */
10
+ migrations?: boolean;
11
+ /** Generate Form Requests (default: true) */
12
+ formRequests?: boolean;
13
+ /** Use API resources (default: true) */
14
+ apiResources?: boolean;
15
+ /** Base path for routes (default: '/api') */
16
+ basePath?: string;
17
+ }
18
+ export interface GeneratedLaravelFiles {
19
+ /** API routes file */
20
+ routes: string;
21
+ /** Controller files (keyed by controller name) */
22
+ controllers: Record<string, string>;
23
+ /** Model files (keyed by model name) */
24
+ models: Record<string, string>;
25
+ /** Migration files (keyed by migration name) */
26
+ migrations?: Record<string, string>;
27
+ /** Form Request files */
28
+ formRequests?: Record<string, string>;
29
+ /** API Resource files */
30
+ resources?: Record<string, string>;
31
+ }
32
+ export declare class KappaLaravelGenerator {
33
+ private modelNamespace;
34
+ private controllerNamespace;
35
+ private provenance;
36
+ private migrations;
37
+ private formRequests;
38
+ private apiResources;
39
+ private basePath;
40
+ private entities;
41
+ constructor(options?: LaravelGeneratorOptions);
42
+ generateFromAPIs(apis: APIBlock[]): GeneratedLaravelFiles;
43
+ generateFromEntities(entities: EntityBlock[]): GeneratedLaravelFiles;
44
+ generateAll(apis: APIBlock[], entities: EntityBlock[]): GeneratedLaravelFiles;
45
+ private generateRoutes;
46
+ private generateRouteDefinition;
47
+ private extractRoutes;
48
+ private operationToRoute;
49
+ private crudToRoutes;
50
+ private inferMethod;
51
+ private groupByController;
52
+ private generateController;
53
+ private generateControllerMethod;
54
+ private generateModel;
55
+ private generateCasts;
56
+ private generateMigration;
57
+ private generateMigrationColumn;
58
+ private generateFormRequest;
59
+ private generateValidationRules;
60
+ private generateResource;
61
+ private toPascalCase;
62
+ private toCamelCase;
63
+ private toKebabCase;
64
+ private toSnakeCase;
65
+ }
66
+ export declare function generateLaravelAPI(apis: APIBlock[], options?: LaravelGeneratorOptions): GeneratedLaravelFiles;
67
+ export declare function generateLaravelModels(entities: EntityBlock[], options?: LaravelGeneratorOptions): GeneratedLaravelFiles;
68
+ export declare function generateLaravel(apis: APIBlock[], entities: EntityBlock[], options?: LaravelGeneratorOptions): GeneratedLaravelFiles;