@claudetools/tools 0.8.11 → 0.9.0

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 (75) hide show
  1. package/dist/codedna/generators/astro.d.ts +18 -0
  2. package/dist/codedna/generators/astro.js +91 -0
  3. package/dist/codedna/generators/authjs.d.ts +18 -0
  4. package/dist/codedna/generators/authjs.js +68 -0
  5. package/dist/codedna/generators/better-auth.d.ts +18 -0
  6. package/dist/codedna/generators/better-auth.js +62 -0
  7. package/dist/codedna/generators/drizzle-orm.d.ts +18 -0
  8. package/dist/codedna/generators/drizzle-orm.js +65 -0
  9. package/dist/codedna/generators/elysia-api.d.ts +12 -0
  10. package/dist/codedna/generators/elysia-api.js +64 -0
  11. package/dist/codedna/generators/hono-api.d.ts +12 -0
  12. package/dist/codedna/generators/hono-api.js +64 -0
  13. package/dist/codedna/generators/lucia-auth.d.ts +18 -0
  14. package/dist/codedna/generators/lucia-auth.js +69 -0
  15. package/dist/codedna/generators/prisma.d.ts +18 -0
  16. package/dist/codedna/generators/prisma.js +64 -0
  17. package/dist/codedna/generators/react-router-v7.d.ts +18 -0
  18. package/dist/codedna/generators/react-router-v7.js +77 -0
  19. package/dist/codedna/generators/react19-shadcn.d.ts +21 -0
  20. package/dist/codedna/generators/react19-shadcn.js +367 -0
  21. package/dist/codedna/generators/sveltekit.d.ts +18 -0
  22. package/dist/codedna/generators/sveltekit.js +73 -0
  23. package/dist/codedna/generators/tanstack-start-drizzle.d.ts +92 -0
  24. package/dist/codedna/generators/tanstack-start-drizzle.js +824 -0
  25. package/dist/codedna/generators/trpc-api.d.ts +12 -0
  26. package/dist/codedna/generators/trpc-api.js +64 -0
  27. package/dist/codedna/index.d.ts +31 -0
  28. package/dist/codedna/index.js +39 -0
  29. package/dist/codedna/kappa-api-generator.d.ts +89 -0
  30. package/dist/codedna/kappa-api-generator.js +493 -0
  31. package/dist/codedna/kappa-ast.d.ts +552 -0
  32. package/dist/codedna/kappa-ast.js +141 -0
  33. package/dist/codedna/kappa-cli.d.ts +2 -0
  34. package/dist/codedna/kappa-cli.js +302 -0
  35. package/dist/codedna/kappa-component-generator.d.ts +47 -0
  36. package/dist/codedna/kappa-component-generator.js +295 -0
  37. package/dist/codedna/kappa-design-generator.d.ts +52 -0
  38. package/dist/codedna/kappa-design-generator.js +365 -0
  39. package/dist/codedna/kappa-drizzle-generator.d.ts +45 -0
  40. package/dist/codedna/kappa-drizzle-generator.js +355 -0
  41. package/dist/codedna/kappa-form-generator.d.ts +51 -0
  42. package/dist/codedna/kappa-form-generator.js +319 -0
  43. package/dist/codedna/kappa-lexer.d.ts +268 -0
  44. package/dist/codedna/kappa-lexer.js +757 -0
  45. package/dist/codedna/kappa-page-generator.d.ts +57 -0
  46. package/dist/codedna/kappa-page-generator.js +338 -0
  47. package/dist/codedna/kappa-parser.d.ts +261 -0
  48. package/dist/codedna/kappa-parser.js +2547 -0
  49. package/dist/codedna/kappa-provenance.d.ts +101 -0
  50. package/dist/codedna/kappa-provenance.js +199 -0
  51. package/dist/codedna/kappa-types-generator.d.ts +37 -0
  52. package/dist/codedna/kappa-types-generator.js +159 -0
  53. package/dist/codedna/kappa-validator.d.ts +86 -0
  54. package/dist/codedna/kappa-validator.js +638 -0
  55. package/dist/codedna/kappa-zod-generator.d.ts +32 -0
  56. package/dist/codedna/kappa-zod-generator.js +216 -0
  57. package/dist/handlers/kappa-handlers.d.ts +116 -0
  58. package/dist/handlers/kappa-handlers.js +465 -0
  59. package/dist/handlers/tool-handlers.js +121 -0
  60. package/dist/templates/claude-md.d.ts +1 -1
  61. package/dist/templates/claude-md.js +166 -9
  62. package/dist/tools.js +199 -0
  63. package/docs/research/2026-01-02-codedna-il-specification.md +639 -0
  64. package/docs/research/2026-01-02-codedna-v2-research.md +943 -0
  65. package/docs/research/2026-01-02-computation-foundations.md +564 -0
  66. package/docs/research/2026-01-02-hardware-description.md +814 -0
  67. package/docs/research/2026-01-02-kappa-specification.md +697 -0
  68. package/docs/research/2026-01-02-kappa-tanstack-example.md +527 -0
  69. package/docs/research/2026-01-02-kappa-v2-synthesis.md +406 -0
  70. package/docs/research/2026-01-02-kappa-v2.5-specification.md +1218 -0
  71. package/docs/research/2026-01-02-kappa-v3-specification.md +1864 -0
  72. package/docs/research/2026-01-02-kappa-whitepaper.md +662 -0
  73. package/docs/research/2026-01-02-logic-constraint.md +731 -0
  74. package/docs/research/2026-01-02-quantum-computation.md +635 -0
  75. package/package.json +4 -2
@@ -0,0 +1,638 @@
1
+ // =============================================================================
2
+ // Kappa v2.5 Semantic Validator
3
+ // =============================================================================
4
+ //
5
+ // Validates Kappa AST for semantic correctness:
6
+ // - Entity references point to defined entities
7
+ // - Field types are valid
8
+ // - Relationship targets exist
9
+ // - API operations reference valid entities
10
+ //
11
+ // =============================================================================
12
+ // Validator Class
13
+ // =============================================================================
14
+ export class KappaValidator {
15
+ errors = [];
16
+ warnings = [];
17
+ /** Set of all defined entity names */
18
+ entityNames = new Set();
19
+ /** Set of all defined API names */
20
+ apiNames = new Set();
21
+ /** Set of all defined page names */
22
+ pageNames = new Set();
23
+ /** Set of all defined form names */
24
+ formNames = new Set();
25
+ /** Set of all defined journey names */
26
+ journeyNames = new Set();
27
+ /**
28
+ * Validate a Kappa specification AST
29
+ */
30
+ validate(spec) {
31
+ this.reset();
32
+ // First pass: collect all defined names
33
+ this.collectDefinedNames(spec);
34
+ // Second pass: validate references
35
+ this.validateEntities(spec.entities);
36
+ this.validateAPIs(spec.apis);
37
+ this.validateJourneys(spec.journeys);
38
+ this.validatePages(spec.pages);
39
+ this.validateForms(spec.forms);
40
+ return {
41
+ valid: this.errors.length === 0,
42
+ errors: this.errors,
43
+ warnings: this.warnings,
44
+ };
45
+ }
46
+ reset() {
47
+ this.errors = [];
48
+ this.warnings = [];
49
+ this.entityNames.clear();
50
+ this.apiNames.clear();
51
+ this.pageNames.clear();
52
+ this.formNames.clear();
53
+ this.journeyNames.clear();
54
+ }
55
+ // ===========================================================================
56
+ // Name Collection (First Pass)
57
+ // ===========================================================================
58
+ collectDefinedNames(spec) {
59
+ // Collect entity names
60
+ for (const entity of spec.entities) {
61
+ if (this.entityNames.has(entity.name)) {
62
+ this.addError({
63
+ code: 'DUPLICATE_ENTITY_NAME',
64
+ message: `Duplicate entity name: '${entity.name}'`,
65
+ location: entity.loc,
66
+ context: { entityName: entity.name },
67
+ });
68
+ }
69
+ this.entityNames.add(entity.name);
70
+ }
71
+ // Collect API names
72
+ for (const api of spec.apis) {
73
+ if (this.apiNames.has(api.name)) {
74
+ this.addError({
75
+ code: 'DUPLICATE_API_NAME',
76
+ message: `Duplicate API name: '${api.name}'`,
77
+ location: api.loc,
78
+ });
79
+ }
80
+ this.apiNames.add(api.name);
81
+ }
82
+ // Collect page names
83
+ for (const page of spec.pages) {
84
+ this.pageNames.add(page.name);
85
+ }
86
+ // Collect form names
87
+ for (const form of spec.forms) {
88
+ this.formNames.add(form.name);
89
+ }
90
+ // Collect journey names
91
+ for (const journey of spec.journeys) {
92
+ this.journeyNames.add(journey.name);
93
+ }
94
+ }
95
+ // ===========================================================================
96
+ // Entity Validation
97
+ // ===========================================================================
98
+ validateEntities(entities) {
99
+ for (const entity of entities) {
100
+ this.validateEntity(entity);
101
+ }
102
+ }
103
+ validateEntity(entity) {
104
+ const fieldNames = new Set();
105
+ let hasPrimaryKey = false;
106
+ // Validate fields
107
+ for (const field of entity.fields) {
108
+ // Check for duplicate field names
109
+ if (fieldNames.has(field.name)) {
110
+ this.addError({
111
+ code: 'DUPLICATE_FIELD_NAME',
112
+ message: `Duplicate field name '${field.name}' in entity '${entity.name}'`,
113
+ location: field.loc,
114
+ context: { entityName: entity.name, fieldName: field.name },
115
+ });
116
+ }
117
+ fieldNames.add(field.name);
118
+ // Check for multiple primary keys
119
+ if (field.modifiers.includes('primary')) {
120
+ if (hasPrimaryKey) {
121
+ this.addError({
122
+ code: 'DUPLICATE_PRIMARY_KEY',
123
+ message: `Entity '${entity.name}' has multiple primary keys`,
124
+ location: field.loc,
125
+ context: { entityName: entity.name, fieldName: field.name },
126
+ });
127
+ }
128
+ hasPrimaryKey = true;
129
+ }
130
+ // Validate field type references
131
+ this.validateFieldType(field.type, field.loc, entity.name, field.name);
132
+ // Validate modifiers for type
133
+ this.validateModifiersForType(field, entity.name);
134
+ }
135
+ // Validate relationships
136
+ for (const relationship of entity.relationships) {
137
+ this.validateRelationship(relationship, entity.name);
138
+ }
139
+ // Validate capabilities
140
+ for (const capability of entity.capabilities) {
141
+ this.validateCapability(capability, entity.name);
142
+ }
143
+ }
144
+ validateFieldType(type, location, entityName, fieldName) {
145
+ switch (type.kind) {
146
+ case 'reference':
147
+ if (!this.entityNames.has(type.entity)) {
148
+ this.addError({
149
+ code: 'UNDEFINED_ENTITY_REFERENCE',
150
+ message: `Field '${fieldName}' references undefined entity '${type.entity}'`,
151
+ location,
152
+ context: { entityName, fieldName, referencedEntity: type.entity },
153
+ });
154
+ }
155
+ break;
156
+ case 'enum':
157
+ // Check for empty enum
158
+ if (type.values.length === 0) {
159
+ this.addError({
160
+ code: 'EMPTY_ENUM',
161
+ message: `Enum field '${fieldName}' has no values`,
162
+ location,
163
+ context: { entityName, fieldName },
164
+ });
165
+ }
166
+ // Check for duplicate enum values
167
+ const enumValues = new Set();
168
+ for (const value of type.values) {
169
+ if (enumValues.has(value)) {
170
+ this.addError({
171
+ code: 'DUPLICATE_ENUM_VALUE',
172
+ message: `Enum field '${fieldName}' has duplicate value '${value}'`,
173
+ location,
174
+ context: { entityName, fieldName },
175
+ });
176
+ }
177
+ enumValues.add(value);
178
+ }
179
+ break;
180
+ case 'primitive':
181
+ // Validate range constraints
182
+ if (type.range) {
183
+ const { min, max } = type.range;
184
+ if (min !== undefined && max !== undefined && min > max) {
185
+ this.addError({
186
+ code: 'INVALID_RANGE',
187
+ message: `Field '${fieldName}' has invalid range: min (${min}) > max (${max})`,
188
+ location,
189
+ context: { entityName, fieldName },
190
+ });
191
+ }
192
+ if (min !== undefined && min < 0 && type.type === 'string') {
193
+ this.addError({
194
+ code: 'INVALID_RANGE',
195
+ message: `String field '${fieldName}' cannot have negative min length (${min})`,
196
+ location,
197
+ context: { entityName, fieldName },
198
+ });
199
+ }
200
+ }
201
+ break;
202
+ case 'array':
203
+ // Recursively validate array item type
204
+ this.validateFieldType(type.itemType, location, entityName, fieldName);
205
+ break;
206
+ }
207
+ }
208
+ validateModifiersForType(field, entityName) {
209
+ const { type, modifiers, loc, name } = field;
210
+ // 'hashed' is only valid on string types
211
+ if (modifiers.includes('hashed')) {
212
+ if (type.kind !== 'primitive' || type.type !== 'string') {
213
+ this.addError({
214
+ code: 'INVALID_MODIFIER_FOR_TYPE',
215
+ message: `Modifier 'hashed' is only valid on string fields, but '${name}' is not a string`,
216
+ location: loc,
217
+ context: { entityName, fieldName: name },
218
+ });
219
+ }
220
+ }
221
+ // 'auto' is only valid on uuid, timestamp, or int (for auto-increment)
222
+ if (modifiers.includes('auto')) {
223
+ const validAutoTypes = ['uuid', 'timestamp', 'int'];
224
+ if (type.kind !== 'primitive' || !validAutoTypes.includes(type.type)) {
225
+ this.addWarning({
226
+ code: 'INVALID_MODIFIER_FOR_TYPE',
227
+ message: `Modifier 'auto' on '${name}' may not work as expected for type '${type.kind === 'primitive' ? type.type : type.kind}'`,
228
+ location: loc,
229
+ context: { entityName, fieldName: name },
230
+ });
231
+ }
232
+ }
233
+ // 'primary' and 'optional' are conflicting
234
+ if (modifiers.includes('primary') && modifiers.includes('optional')) {
235
+ this.addError({
236
+ code: 'CONFLICTING_MODIFIERS',
237
+ message: `Field '${name}' cannot be both 'primary' and 'optional'`,
238
+ location: loc,
239
+ context: { entityName, fieldName: name },
240
+ });
241
+ }
242
+ // 'unique' on reference types is usually a warning (might be intentional)
243
+ if (modifiers.includes('unique') && type.kind === 'reference') {
244
+ this.addWarning({
245
+ code: 'INVALID_MODIFIER_FOR_TYPE',
246
+ message: `Modifier 'unique' on reference field '${name}' creates a one-to-one relationship`,
247
+ location: loc,
248
+ context: { entityName, fieldName: name },
249
+ });
250
+ }
251
+ // 'immutable' and 'auto' with 'on_update' is conflicting
252
+ if (modifiers.includes('immutable') && field.updateTrigger) {
253
+ this.addError({
254
+ code: 'CONFLICTING_MODIFIERS',
255
+ message: `Field '${name}' cannot be 'immutable' with update trigger`,
256
+ location: loc,
257
+ context: { entityName, fieldName: name },
258
+ });
259
+ }
260
+ // 'internal' is only useful on fields that would otherwise be exposed
261
+ if (modifiers.includes('internal') && modifiers.includes('hashed')) {
262
+ // This is fine - password fields are commonly internal + hashed
263
+ }
264
+ }
265
+ validateRelationship(relationship, entityName) {
266
+ // Check if target entity exists
267
+ if (!this.entityNames.has(relationship.entity)) {
268
+ this.addError({
269
+ code: 'UNDEFINED_ENTITY_REFERENCE',
270
+ message: `Relationship references undefined entity '${relationship.entity}'`,
271
+ location: relationship.loc,
272
+ context: { entityName, referencedEntity: relationship.entity },
273
+ });
274
+ }
275
+ // Check if through entity exists (for many-to-many)
276
+ if (relationship.through && !this.entityNames.has(relationship.through)) {
277
+ this.addError({
278
+ code: 'UNDEFINED_THROUGH_ENTITY',
279
+ message: `Relationship 'through' references undefined entity '${relationship.through}'`,
280
+ location: relationship.loc,
281
+ context: { entityName, referencedEntity: relationship.through },
282
+ });
283
+ }
284
+ }
285
+ validateCapability(capability, entityName) {
286
+ // Check if target entity exists (when specified)
287
+ if (capability.target && !this.entityNames.has(capability.target)) {
288
+ this.addError({
289
+ code: 'UNDEFINED_CAPABILITY_TARGET',
290
+ message: `Capability references undefined entity '${capability.target}'`,
291
+ location: capability.loc,
292
+ context: { entityName, referencedEntity: capability.target },
293
+ });
294
+ }
295
+ }
296
+ // ===========================================================================
297
+ // API Validation
298
+ // ===========================================================================
299
+ validateAPIs(apis) {
300
+ for (const api of apis) {
301
+ this.validateAPI(api);
302
+ }
303
+ }
304
+ validateAPI(api) {
305
+ // Validate CRUD shorthands
306
+ for (const crud of api.crud) {
307
+ this.validateCRUD(crud, api.name);
308
+ }
309
+ // Validate operations
310
+ for (const operation of api.operations) {
311
+ this.validateAPIOperation(operation, api.name);
312
+ }
313
+ }
314
+ validateCRUD(crud, apiName) {
315
+ // Check if CRUD target entity exists
316
+ if (!this.entityNames.has(crud.entity)) {
317
+ this.addError({
318
+ code: 'UNDEFINED_CRUD_ENTITY',
319
+ message: `CRUD shorthand references undefined entity '${crud.entity}'`,
320
+ location: crud.loc,
321
+ context: { referencedEntity: crud.entity },
322
+ });
323
+ }
324
+ }
325
+ validateAPIOperation(operation, apiName) {
326
+ const operationContext = `${apiName}.${operation.name}`;
327
+ // Validate return type entity reference
328
+ const returnType = operation.returnType.type;
329
+ // Skip validation for void and object types
330
+ if (returnType !== 'void' && !returnType.startsWith('{')) {
331
+ // Check if it's an entity reference
332
+ if (!this.entityNames.has(returnType)) {
333
+ // Could be a primitive or inline type - just warn for now
334
+ this.addWarning({
335
+ code: 'UNDEFINED_RETURN_TYPE_ENTITY',
336
+ message: `API operation '${operation.name}' returns unknown type '${returnType}'`,
337
+ location: operation.loc,
338
+ });
339
+ }
340
+ }
341
+ // Validate joined entities in return type
342
+ if (operation.returnType.joined) {
343
+ for (const join of operation.returnType.joined) {
344
+ if (!this.entityNames.has(join.entity)) {
345
+ this.addError({
346
+ code: 'UNDEFINED_JOINED_ENTITY',
347
+ message: `API operation '${operation.name}' joins undefined entity '${join.entity}'`,
348
+ location: operation.returnType.loc,
349
+ context: { referencedEntity: join.entity },
350
+ });
351
+ }
352
+ }
353
+ }
354
+ // Validate effect annotations
355
+ const validEffects = new Set(['DB', 'Auth', 'Email', 'Audit', 'Queue', 'Cache', 'External']);
356
+ for (const effect of operation.effects) {
357
+ if (!validEffects.has(effect)) {
358
+ this.addError({
359
+ code: 'INVALID_EFFECT_TYPE',
360
+ message: `API operation '${operation.name}' has invalid effect '${effect}'`,
361
+ location: operation.loc,
362
+ });
363
+ }
364
+ }
365
+ // Validate parameter types and check for duplicates
366
+ const paramNames = new Set();
367
+ for (const param of operation.parameters) {
368
+ // Check for duplicate parameter names
369
+ if (paramNames.has(param.name)) {
370
+ this.addError({
371
+ code: 'DUPLICATE_PARAMETER_NAME',
372
+ message: `API operation '${operation.name}' has duplicate parameter '${param.name}'`,
373
+ location: param.loc,
374
+ });
375
+ }
376
+ paramNames.add(param.name);
377
+ // Validate parameter type
378
+ this.validateFieldType(param.type, param.loc, operationContext, param.name);
379
+ }
380
+ }
381
+ // ===========================================================================
382
+ // Journey Validation
383
+ // ===========================================================================
384
+ validateJourneys(journeys) {
385
+ for (const journey of journeys) {
386
+ this.validateJourney(journey);
387
+ }
388
+ }
389
+ validateJourney(journey) {
390
+ const stepNames = new Set();
391
+ for (const step of journey.steps) {
392
+ stepNames.add(step.name);
393
+ }
394
+ // Validate step references
395
+ for (const step of journey.steps) {
396
+ // Check if 'next' step exists
397
+ if (step.next && !stepNames.has(step.next)) {
398
+ this.addError({
399
+ code: 'UNDEFINED_PAGE_REFERENCE',
400
+ message: `Journey step '${step.name}' references undefined next step '${step.next}'`,
401
+ location: step.loc,
402
+ });
403
+ }
404
+ // Check if page exists
405
+ if (step.page && !this.pageNames.has(step.page) && !this.formNames.has(step.page)) {
406
+ this.addWarning({
407
+ code: 'UNDEFINED_PAGE_REFERENCE',
408
+ message: `Journey step '${step.name}' references undefined page '${step.page}'`,
409
+ location: step.loc,
410
+ });
411
+ }
412
+ }
413
+ // Validate recovery handlers
414
+ for (const recovery of journey.recovery) {
415
+ if (!stepNames.has(recovery.step)) {
416
+ this.addError({
417
+ code: 'UNDEFINED_PAGE_REFERENCE',
418
+ message: `Recovery handler references undefined step '${recovery.step}'`,
419
+ location: recovery.loc,
420
+ });
421
+ }
422
+ }
423
+ }
424
+ // ===========================================================================
425
+ // Page Validation
426
+ // ===========================================================================
427
+ validatePages(pages) {
428
+ for (const page of pages) {
429
+ this.validatePage(page);
430
+ }
431
+ }
432
+ validatePage(page) {
433
+ // Validate component reference
434
+ if (page.component && !this.formNames.has(page.component)) {
435
+ // Component might be an external component - just warn
436
+ this.addWarning({
437
+ code: 'UNDEFINED_PAGE_REFERENCE',
438
+ message: `Page '${page.name}' references component '${page.component}' which is not defined in the spec`,
439
+ location: page.loc,
440
+ });
441
+ }
442
+ }
443
+ // ===========================================================================
444
+ // Form Validation
445
+ // ===========================================================================
446
+ validateForms(forms) {
447
+ for (const form of forms) {
448
+ this.validateForm(form);
449
+ }
450
+ }
451
+ validateForm(form) {
452
+ const fieldNames = new Set();
453
+ for (const field of form.fields) {
454
+ // Check for duplicate field names
455
+ if (fieldNames.has(field.name)) {
456
+ this.addError({
457
+ code: 'DUPLICATE_FIELD_NAME',
458
+ message: `Duplicate field name '${field.name}' in form '${form.name}'`,
459
+ location: field.loc,
460
+ });
461
+ }
462
+ fieldNames.add(field.name);
463
+ }
464
+ // Validate layout references
465
+ for (const row of form.layout) {
466
+ for (const fieldRef of row.fields) {
467
+ if (!fieldNames.has(fieldRef)) {
468
+ this.addError({
469
+ code: 'UNDEFINED_PAGE_REFERENCE',
470
+ message: `Form layout references undefined field '${fieldRef}'`,
471
+ location: row.loc,
472
+ });
473
+ }
474
+ }
475
+ }
476
+ }
477
+ // ===========================================================================
478
+ // Helper Methods
479
+ // ===========================================================================
480
+ addError(error) {
481
+ this.errors.push(error);
482
+ }
483
+ addWarning(warning) {
484
+ this.warnings.push(warning);
485
+ }
486
+ }
487
+ // =============================================================================
488
+ // Convenience Function
489
+ // =============================================================================
490
+ /**
491
+ * Validate a Kappa specification AST
492
+ */
493
+ export function validateKappaSpec(spec) {
494
+ const validator = new KappaValidator();
495
+ return validator.validate(spec);
496
+ }
497
+ /**
498
+ * Format a validation error for display (simple format)
499
+ */
500
+ export function formatValidationError(error) {
501
+ const { code, message, location, context } = error;
502
+ const locStr = `${location.startLine}:${location.startColumn}`;
503
+ let contextStr = '';
504
+ if (context) {
505
+ const parts = [];
506
+ if (context.entityName)
507
+ parts.push(`entity: ${context.entityName}`);
508
+ if (context.fieldName)
509
+ parts.push(`field: ${context.fieldName}`);
510
+ if (context.referencedEntity)
511
+ parts.push(`references: ${context.referencedEntity}`);
512
+ if (parts.length > 0) {
513
+ contextStr = ` (${parts.join(', ')})`;
514
+ }
515
+ }
516
+ return `[${code}] ${locStr}: ${message}${contextStr}`;
517
+ }
518
+ /**
519
+ * Format a validation error with code snippet and visual indicators
520
+ */
521
+ export function formatValidationErrorRich(error, options = {}) {
522
+ const { code, message, location, context } = error;
523
+ const { source, colors = false, contextLines = 1 } = options;
524
+ const lines = [];
525
+ // Error header with location
526
+ const locStr = `${location.startLine}:${location.startColumn}`;
527
+ const header = colors
528
+ ? `\x1b[31merror\x1b[0m[\x1b[33m${code}\x1b[0m]: ${message}`
529
+ : `error[${code}]: ${message}`;
530
+ lines.push(header);
531
+ // Location pointer
532
+ const locationLine = colors
533
+ ? ` \x1b[36m-->\x1b[0m ${locStr}`
534
+ : ` --> ${locStr}`;
535
+ lines.push(locationLine);
536
+ // Code snippet if source is provided
537
+ if (source) {
538
+ const sourceLines = source.split('\n');
539
+ const errorLine = location.startLine;
540
+ const startLine = Math.max(1, errorLine - contextLines);
541
+ const endLine = Math.min(sourceLines.length, errorLine + contextLines);
542
+ // Calculate gutter width based on line numbers
543
+ const gutterWidth = String(endLine).length;
544
+ lines.push(' |');
545
+ for (let i = startLine; i <= endLine; i++) {
546
+ const lineContent = sourceLines[i - 1] || '';
547
+ const lineNum = String(i).padStart(gutterWidth, ' ');
548
+ if (i === errorLine) {
549
+ // Error line - highlight it
550
+ const prefix = colors ? `\x1b[31m${lineNum}\x1b[0m |` : `${lineNum} |`;
551
+ lines.push(`${prefix} ${lineContent}`);
552
+ // Add caret pointer
553
+ const caretPadding = ' '.repeat(location.startColumn - 1);
554
+ const caretLength = Math.max(1, Math.min(location.endColumn - location.startColumn, lineContent.length - location.startColumn + 1));
555
+ const caret = '^'.repeat(caretLength);
556
+ const caretLine = colors
557
+ ? `${''.padStart(gutterWidth, ' ')} | ${caretPadding}\x1b[31m${caret}\x1b[0m`
558
+ : `${''.padStart(gutterWidth, ' ')} | ${caretPadding}${caret}`;
559
+ lines.push(caretLine);
560
+ }
561
+ else {
562
+ // Context line
563
+ const prefix = colors
564
+ ? `\x1b[90m${lineNum}\x1b[0m |`
565
+ : `${lineNum} |`;
566
+ const content = colors ? `\x1b[90m${lineContent}\x1b[0m` : lineContent;
567
+ lines.push(`${prefix} ${content}`);
568
+ }
569
+ }
570
+ lines.push(' |');
571
+ }
572
+ // Add context info if available
573
+ if (context) {
574
+ const parts = [];
575
+ if (context.entityName)
576
+ parts.push(`entity: ${context.entityName}`);
577
+ if (context.fieldName)
578
+ parts.push(`field: ${context.fieldName}`);
579
+ if (context.referencedEntity)
580
+ parts.push(`references: ${context.referencedEntity}`);
581
+ if (parts.length > 0) {
582
+ const contextLine = colors
583
+ ? ` \x1b[90m= note: ${parts.join(', ')}\x1b[0m`
584
+ : ` = note: ${parts.join(', ')}`;
585
+ lines.push(contextLine);
586
+ }
587
+ }
588
+ return lines.join('\n');
589
+ }
590
+ /**
591
+ * Format all validation results for display
592
+ */
593
+ export function formatValidationResults(result, options = {}) {
594
+ const sections = [];
595
+ // Format errors
596
+ if (result.errors.length > 0) {
597
+ for (const error of result.errors) {
598
+ sections.push(formatValidationErrorRich(error, options));
599
+ }
600
+ }
601
+ // Format warnings
602
+ if (result.warnings.length > 0) {
603
+ const warningOptions = { ...options };
604
+ for (const warning of result.warnings) {
605
+ // Modify message to indicate it's a warning
606
+ const warningError = {
607
+ ...warning,
608
+ message: `(warning) ${warning.message}`,
609
+ };
610
+ sections.push(formatValidationErrorRich(warningError, warningOptions));
611
+ }
612
+ }
613
+ // Summary
614
+ const errorCount = result.errors.length;
615
+ const warningCount = result.warnings.length;
616
+ const summaryParts = [];
617
+ if (errorCount > 0) {
618
+ const errorText = errorCount === 1 ? 'error' : 'errors';
619
+ summaryParts.push(options.colors
620
+ ? `\x1b[31m${errorCount} ${errorText}\x1b[0m`
621
+ : `${errorCount} ${errorText}`);
622
+ }
623
+ if (warningCount > 0) {
624
+ const warningText = warningCount === 1 ? 'warning' : 'warnings';
625
+ summaryParts.push(options.colors
626
+ ? `\x1b[33m${warningCount} ${warningText}\x1b[0m`
627
+ : `${warningCount} ${warningText}`);
628
+ }
629
+ if (summaryParts.length > 0) {
630
+ sections.push(`\nFound ${summaryParts.join(' and ')}`);
631
+ }
632
+ else {
633
+ sections.push(options.colors
634
+ ? `\n\x1b[32m✓ Validation passed\x1b[0m`
635
+ : '\n✓ Validation passed');
636
+ }
637
+ return sections.join('\n\n');
638
+ }
@@ -0,0 +1,32 @@
1
+ import type { EntityBlock } from './kappa-ast.js';
2
+ export interface ZodGeneratorOptions {
3
+ /** Add provenance comments (default: true) */
4
+ provenance?: boolean;
5
+ /** Generate update schemas with partial fields (default: true) */
6
+ updateSchemas?: boolean;
7
+ /** Generate form schemas (default: false) */
8
+ formSchemas?: boolean;
9
+ }
10
+ export interface GeneratedZodSchemas {
11
+ /** Main schemas file content */
12
+ schemas: string;
13
+ }
14
+ export declare class KappaZodGenerator {
15
+ private provenance;
16
+ private updateSchemas;
17
+ private formSchemas;
18
+ constructor(options?: ZodGeneratorOptions);
19
+ /**
20
+ * Generate Zod schemas from entity blocks
21
+ */
22
+ generate(entities: EntityBlock[]): GeneratedZodSchemas;
23
+ private generateSchemasFile;
24
+ private generateEntitySchemas;
25
+ private generateFieldSchema;
26
+ private typeToZodSchema;
27
+ private primitiveToZod;
28
+ }
29
+ /**
30
+ * Generate Zod schemas from Kappa entities
31
+ */
32
+ export declare function generateZodSchemas(entities: EntityBlock[], options?: ZodGeneratorOptions): GeneratedZodSchemas;