@specverse/engines 4.1.13 → 4.1.15

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 (68) hide show
  1. package/assets/prompts/core/standard/v9/behavior.prompt.yaml +126 -0
  2. package/dist/ai/behavior-ai-service.d.ts +65 -0
  3. package/dist/ai/behavior-ai-service.d.ts.map +1 -0
  4. package/dist/ai/behavior-ai-service.js +205 -0
  5. package/dist/ai/behavior-ai-service.js.map +1 -0
  6. package/dist/ai/index.d.ts +27 -0
  7. package/dist/ai/index.d.ts.map +1 -1
  8. package/dist/ai/index.js +30 -0
  9. package/dist/ai/index.js.map +1 -1
  10. package/dist/ai/prompt-loader.js +2 -2
  11. package/dist/inference/quint-transpiler.d.ts.map +1 -1
  12. package/dist/inference/quint-transpiler.js +204 -4
  13. package/dist/inference/quint-transpiler.js.map +1 -1
  14. package/dist/libs/instance-factories/applications/templates/generic/backend-package-json-generator.js +4 -1
  15. package/dist/libs/instance-factories/applications/templates/generic/backend-tsconfig-generator.js +2 -2
  16. package/dist/libs/instance-factories/applications/templates/react/runtime-package-json-generator.js +1 -0
  17. package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +97 -22
  18. package/dist/libs/instance-factories/communication/templates/eventemitter/bus-generator.js +31 -31
  19. package/dist/libs/instance-factories/communication/templates/eventemitter/types-generator.js +79 -0
  20. package/dist/libs/instance-factories/communication/templates/eventemitter/websocket-bridge-generator.js +96 -0
  21. package/dist/libs/instance-factories/controllers/templates/fastify/routes-generator.js +45 -9
  22. package/dist/libs/instance-factories/controllers/templates/fastify/server-generator.js +20 -2
  23. package/dist/libs/instance-factories/scaffolding/templates/generic/tsconfig-generator.js +10 -2
  24. package/dist/libs/instance-factories/services/templates/prisma/ai-behaviors-generator.js +249 -0
  25. package/dist/libs/instance-factories/services/templates/prisma/behavior-generator.js +72 -45
  26. package/dist/libs/instance-factories/services/templates/prisma/controller-generator.js +61 -54
  27. package/dist/libs/instance-factories/services/templates/prisma/service-generator.js +31 -10
  28. package/dist/libs/instance-factories/services/templates/prisma/step-conventions.js +101 -84
  29. package/dist/libs/instance-factories/views/templates/react/components-generator.js +40 -10
  30. package/dist/realize/index.d.ts.map +1 -1
  31. package/dist/realize/index.js +192 -23
  32. package/dist/realize/index.js.map +1 -1
  33. package/libs/instance-factories/applications/templates/generic/backend-package-json-generator.ts +4 -1
  34. package/libs/instance-factories/applications/templates/generic/backend-tsconfig-generator.ts +2 -2
  35. package/libs/instance-factories/applications/templates/react/runtime-package-json-generator.ts +6 -1
  36. package/libs/instance-factories/cli/templates/commander/command-generator.ts +115 -22
  37. package/libs/instance-factories/communication/event-emitter.yaml +16 -12
  38. package/libs/instance-factories/communication/templates/eventemitter/bus-generator.ts +33 -36
  39. package/libs/instance-factories/communication/templates/eventemitter/types-generator.ts +95 -0
  40. package/libs/instance-factories/communication/templates/eventemitter/websocket-bridge-generator.ts +105 -0
  41. package/libs/instance-factories/controllers/templates/fastify/routes-generator.ts +57 -11
  42. package/libs/instance-factories/controllers/templates/fastify/server-generator.ts +23 -2
  43. package/libs/instance-factories/scaffolding/templates/generic/tsconfig-generator.ts +23 -2
  44. package/libs/instance-factories/services/templates/prisma/ai-behaviors-generator.ts +376 -0
  45. package/libs/instance-factories/services/templates/prisma/behavior-generator.ts +116 -45
  46. package/libs/instance-factories/services/templates/prisma/controller-generator.ts +83 -59
  47. package/libs/instance-factories/services/templates/prisma/service-generator.ts +40 -10
  48. package/libs/instance-factories/services/templates/prisma/step-conventions.ts +169 -85
  49. package/libs/instance-factories/views/templates/react/components-generator.ts +50 -10
  50. package/package.json +1 -1
  51. package/dist/libs/instance-factories/tools/templates/mcp/static/src/controllers/MCPServerController.js +0 -232
  52. package/dist/libs/instance-factories/tools/templates/mcp/static/src/events/EventEmitter.js +0 -49
  53. package/dist/libs/instance-factories/tools/templates/mcp/static/src/index.js +0 -18
  54. package/dist/libs/instance-factories/tools/templates/mcp/static/src/interfaces/ResourceProvider.js +0 -0
  55. package/dist/libs/instance-factories/tools/templates/mcp/static/src/models/LibrarySuggestion.js +0 -97
  56. package/dist/libs/instance-factories/tools/templates/mcp/static/src/models/SpecVerseResource.js +0 -64
  57. package/dist/libs/instance-factories/tools/templates/mcp/static/src/server/mcp-server.js +0 -182
  58. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/CLIProxyService.js +0 -1210
  59. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/EmbeddedResourcesAdapter.js +0 -172
  60. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/EntityModuleService.js +0 -240
  61. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/HybridResourcesProvider.js +0 -147
  62. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/LibraryToolsService.js +0 -281
  63. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorBridge.js +0 -409
  64. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/OrchestratorToolsService.js +0 -414
  65. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/PromptToolsService.js +0 -467
  66. package/dist/libs/instance-factories/tools/templates/mcp/static/src/services/ResourcesProviderService.js +0 -135
  67. package/dist/libs/instance-factories/tools/templates/mcp/static/src/types/index.js +0 -0
  68. package/dist/libs/instance-factories/tools/templates/vscode/static/extension.js +0 -965
@@ -1,4 +1,5 @@
1
1
  import { buildTransitionMap, isAutoField } from "@specverse/types/spec-rules";
2
+ import { generateBehaviorWithHelpers } from "./behavior-generator.js";
2
3
  function generatePrismaController(context) {
3
4
  const { controller, model, spec, models: allModels } = context;
4
5
  if (!controller) {
@@ -12,10 +13,12 @@ function generatePrismaController(context) {
12
13
  const rawModelVar = modelName.charAt(0).toLowerCase() + modelName.slice(1);
13
14
  const RESERVED_WORDS = /* @__PURE__ */ new Set(["import", "export", "default", "class", "function", "return", "delete", "new", "this", "switch", "case", "break", "continue", "for", "while", "do", "if", "else", "try", "catch", "finally", "throw", "typeof", "instanceof", "in", "of", "let", "const", "var", "void", "with", "yield", "async", "await", "enum", "implements", "interface", "package", "private", "protected", "public", "static", "super", "extends"]);
14
15
  const modelVar = RESERVED_WORDS.has(rawModelVar) ? `${rawModelVar}Item` : rawModelVar;
16
+ const prismaDelegate = RESERVED_WORDS.has(rawModelVar) ? `prisma['${rawModelVar}']` : `prisma.${rawModelVar}`;
15
17
  const curedOps = controller.cured || {};
16
18
  const idAttr = (Array.isArray(model.attributes) ? model.attributes : Object.values(model.attributes || {})).find((a) => a.name === "id");
17
19
  const idType = idAttr?.type || "UUID";
18
20
  const needsIntParse = idType === "Integer" || idType === "Int" || idType === "Number";
21
+ const customActions = generateCustomActions(controller, modelName, modelVar);
19
22
  return `/**
20
23
  * ${controllerName}
21
24
  * Model-specific business logic for ${modelName}
@@ -23,7 +26,8 @@ function generatePrismaController(context) {
23
26
  */
24
27
 
25
28
  import { PrismaClient } from '@prisma/client';
26
- ${hasEventPublishing(curedOps, controller) ? `import { eventBus, EventName } from '../events/eventBus.js';` : ""}
29
+ ${hasEventPublishing(curedOps, controller) ? `import { eventBus } from '../events/eventBus.js';` : ""}
30
+ ${customActions.needsAiBehaviors ? `import * as aiBehaviors from '../behaviors/${modelName}Controller.ai.js';` : ""}
27
31
 
28
32
  const prisma = new PrismaClient();
29
33
 
@@ -37,12 +41,12 @@ function parseId(id: string): ${needsIntParse ? "number" : "string"} {
37
41
  */
38
42
  export class ${controllerName} {
39
43
  ${generateValidateMethod(model, modelName)}
40
- ${curedOps.create ? generateCreateMethod(model, modelName, modelVar, controller, allModels) : ""}
41
- ${curedOps.retrieve ? generateRetrieveMethod(model, modelName, modelVar) : ""}
42
- ${curedOps.update ? generateUpdateMethod(model, modelName, modelVar, controller, allModels) : ""}
43
- ${curedOps.evolve ? generateEvolveMethod(model, modelName, modelVar, controller) : ""}
44
- ${curedOps.delete ? generateDeleteMethod(model, modelName, modelVar, controller) : ""}
45
- ${generateCustomActions(controller, modelName, modelVar)}
44
+ ${curedOps.create ? generateCreateMethod(model, modelName, modelVar, prismaDelegate, controller, allModels) : ""}
45
+ ${curedOps.retrieve ? generateRetrieveMethod(model, modelName, modelVar, prismaDelegate) : ""}
46
+ ${curedOps.update ? generateUpdateMethod(model, modelName, modelVar, prismaDelegate, controller, allModels) : ""}
47
+ ${curedOps.evolve ? generateEvolveMethod(model, modelName, modelVar, prismaDelegate, controller) : ""}
48
+ ${curedOps.delete ? generateDeleteMethod(model, modelName, modelVar, prismaDelegate, controller) : ""}
49
+ ${customActions.code}
46
50
  }
47
51
 
48
52
  // Export singleton instance
@@ -113,9 +117,7 @@ function generateValidationLogic(model, dataParam = "_data", contextParam = "_co
113
117
  });
114
118
  return validations.join("\n") || "// No validation rules defined";
115
119
  }
116
- function generateCreateMethod(model, modelName, modelVar, controller, allModels) {
117
- const hasEvents = controller.publishes && Array.isArray(controller.publishes);
118
- const createEvent = hasEvents ? controller.publishes.find((e) => e.includes("Created")) : null;
120
+ function generateCreateMethod(model, modelName, modelVar, prismaDelegate, controller, allModels) {
119
121
  return `
120
122
  /**
121
123
  * Create a new ${modelName}
@@ -132,29 +134,24 @@ function generateCreateMethod(model, modelName, modelVar, controller, allModels)
132
134
  ${generateFKTransform(model, "prismaData", allModels)}
133
135
 
134
136
  // Create record
135
- const ${modelVar} = await prisma.${modelVar}.create({
137
+ const ${modelVar} = await ${prismaDelegate}.create({
136
138
  data: prismaData${generateIncludeRelationships(model)}
137
139
  });
138
140
 
139
- ${createEvent ? `
140
- // Publish event
141
- eventBus.publish(EventName.${createEvent}, {
142
- ${modelVar},
143
- timestamp: new Date().toISOString()
144
- });
145
- ` : ""}
141
+ // Publish CURED event
142
+ await eventBus.publish('${modelName}Created', { ...${modelVar}, timestamp: new Date().toISOString() } as any);
146
143
 
147
144
  return ${modelVar};
148
145
  }
149
146
  `;
150
147
  }
151
- function generateRetrieveMethod(model, modelName, modelVar) {
148
+ function generateRetrieveMethod(model, modelName, modelVar, prismaDelegate) {
152
149
  return `
153
150
  /**
154
151
  * Retrieve ${modelName} by ID
155
152
  */
156
153
  public async retrieve(id: string): Promise<any> {
157
- const ${modelVar} = await prisma.${modelVar}.findUnique({
154
+ const ${modelVar} = await ${prismaDelegate}.findUnique({
158
155
  where: { id: parseId(id) }${generateIncludeRelationships(model)}
159
156
  });
160
157
 
@@ -169,16 +166,14 @@ function generateRetrieveMethod(model, modelName, modelVar) {
169
166
  * Retrieve all ${modelName}s
170
167
  */
171
168
  public async retrieveAll(options: { skip?: number; take?: number } = {}): Promise<any[]> {
172
- return await prisma.${modelVar}.findMany({
169
+ return await ${prismaDelegate}.findMany({
173
170
  skip: options.skip,
174
171
  take: options.take${generateIncludeRelationships(model)}
175
172
  });
176
173
  }
177
174
  `;
178
175
  }
179
- function generateUpdateMethod(model, modelName, modelVar, controller, allModels) {
180
- const hasEvents = controller.publishes && Array.isArray(controller.publishes);
181
- const updateEvent = hasEvents ? controller.publishes.find((e) => e.includes("Updated")) : null;
176
+ function generateUpdateMethod(model, modelName, modelVar, prismaDelegate, controller, allModels) {
182
177
  return `
183
178
  /**
184
179
  * Update ${modelName}
@@ -203,24 +198,19 @@ function generateUpdateMethod(model, modelName, modelVar, controller, allModels)
203
198
  ${generateFKTransform(model, "updateData", allModels)}
204
199
 
205
200
  // Update record
206
- const ${modelVar} = await prisma.${modelVar}.update({
201
+ const ${modelVar} = await ${prismaDelegate}.update({
207
202
  where: { id: parseId(id) },
208
203
  data: updateData${generateIncludeRelationships(model)}
209
204
  });
210
205
 
211
- ${updateEvent ? `
212
- // Publish event
213
- eventBus.publish(EventName.${updateEvent}, {
214
- ${modelVar},
215
- timestamp: new Date().toISOString()
216
- });
217
- ` : ""}
206
+ // Publish CURED event
207
+ await eventBus.publish('${modelName}Updated', { ...${modelVar}, timestamp: new Date().toISOString() } as any);
218
208
 
219
209
  return ${modelVar};
220
210
  }
221
211
  `;
222
212
  }
223
- function generateEvolveMethod(model, modelName, modelVar, controller) {
213
+ function generateEvolveMethod(model, modelName, modelVar, prismaDelegate, controller) {
224
214
  const lifecycles = Array.isArray(model.lifecycles) ? model.lifecycles : model.lifecycles ? Object.entries(model.lifecycles).map(([name, lc]) => ({ name, ...lc })) : [];
225
215
  const lifecycle = lifecycles[0];
226
216
  const lifecycleName = lifecycle?.name || "status";
@@ -239,7 +229,7 @@ function generateEvolveMethod(model, modelName, modelVar, controller) {
239
229
  }
240
230
 
241
231
  // Get current record to check lifecycle state
242
- const current = await prisma.${modelVar}.findUnique({ where: { id: parseId(id) } });
232
+ const current = await ${prismaDelegate}.findUnique({ where: { id: parseId(id) } });
243
233
  if (!current) {
244
234
  throw new Error('${modelName} not found');
245
235
  }
@@ -258,61 +248,78 @@ function generateEvolveMethod(model, modelName, modelVar, controller) {
258
248
  ` : ""}
259
249
 
260
250
  // Update record
261
- const ${modelVar} = await prisma.${modelVar}.update({
251
+ const ${modelVar} = await ${prismaDelegate}.update({
262
252
  where: { id: parseId(id) },
263
253
  data${generateIncludeRelationships(model)}
264
254
  });
265
255
 
256
+ // Publish CURED event
257
+ await eventBus.publish('${modelName}Evolved', { ...${modelVar}, timestamp: new Date().toISOString() } as any);
258
+
266
259
  return ${modelVar};
267
260
  }
268
261
  `;
269
262
  }
270
- function generateDeleteMethod(model, modelName, modelVar, controller) {
271
- const hasEvents = controller.publishes && Array.isArray(controller.publishes);
272
- const deleteEvent = hasEvents ? controller.publishes.find((e) => e.includes("Deleted")) : null;
263
+ function generateDeleteMethod(model, modelName, modelVar, prismaDelegate, controller) {
273
264
  return `
274
265
  /**
275
266
  * Delete ${modelName}
276
267
  */
277
268
  public async delete(id: string): Promise<void> {
278
- ${deleteEvent ? `
279
269
  // Get record before deletion for event
280
- const ${modelVar} = await prisma.${modelVar}.findUnique({ where: { id: parseId(id) } });
281
- ` : ""}
270
+ const ${modelVar} = await ${prismaDelegate}.findUnique({ where: { id: parseId(id) } });
282
271
 
283
- await prisma.${modelVar}.delete({
272
+ await ${prismaDelegate}.delete({
284
273
  where: { id: parseId(id) }
285
274
  });
286
275
 
287
- ${deleteEvent ? `
288
- // Publish event
276
+ // Publish CURED event
289
277
  if (${modelVar}) {
290
- eventBus.publish(EventName.${deleteEvent}, {
291
- ${modelVar},
292
- timestamp: new Date().toISOString()
293
- });
278
+ await eventBus.publish('${modelName}Deleted', { ...${modelVar}, timestamp: new Date().toISOString() } as any);
294
279
  }
295
- ` : ""}
296
280
  }
297
281
  `;
298
282
  }
299
283
  function generateCustomActions(controller, modelName, modelVar) {
300
284
  if (!controller.actions || Object.keys(controller.actions).length === 0) {
301
- return "";
285
+ return { code: "", unmatchedSteps: [], needsAiBehaviors: false };
302
286
  }
303
287
  const actions = [];
288
+ const allUnmatchedSteps = [];
304
289
  Object.entries(controller.actions).forEach(([actionName, action]) => {
290
+ const behavior = {
291
+ preconditions: action.requires || action.preconditions || [],
292
+ steps: action.steps || [],
293
+ postconditions: action.ensures || action.postconditions || [],
294
+ sideEffects: action.publishes || action.events || [],
295
+ transactional: action.transactional
296
+ };
297
+ const ctx = {
298
+ modelName,
299
+ serviceName: `${modelName}Controller`,
300
+ operationName: actionName,
301
+ prismaModel: modelVar,
302
+ parameterNames: Object.keys(action.parameters || {})
303
+ };
304
+ const result = generateBehaviorWithHelpers(behavior, {}, ctx);
305
+ allUnmatchedSteps.push(...result.unmatchedSteps);
305
306
  actions.push(`
306
307
  /**
307
308
  * ${actionName}
308
309
  * ${action.description || ""}
309
310
  */
310
311
  public async ${actionName}(${generateActionParams(action)}): Promise<any> {
311
- // TODO: Implement ${actionName} logic
312
- throw new Error('${actionName} not implemented');
312
+ ${result.body}
313
313
  }`);
314
+ if (result.helperMethods.length > 0) {
315
+ actions.push(...result.helperMethods);
316
+ }
314
317
  });
315
- return actions.join("\n");
318
+ return {
319
+ code: actions.join("\n"),
320
+ unmatchedSteps: allUnmatchedSteps,
321
+ needsAiBehaviors: allUnmatchedSteps.length > 0
322
+ };
316
323
  }
317
324
  function generateActionParams(action) {
318
325
  if (!action.parameters || Object.keys(action.parameters).length === 0) {
@@ -391,7 +398,7 @@ ${includes}
391
398
  }`;
392
399
  }
393
400
  function hasEventPublishing(curedOps, controller) {
394
- return controller.publishes && Array.isArray(controller.publishes) && controller.publishes.length > 0;
401
+ return true;
395
402
  }
396
403
  export {
397
404
  generatePrismaController as default
@@ -6,23 +6,27 @@ function generatePrismaService(context) {
6
6
  }
7
7
  const serviceName = service.name;
8
8
  const hasEvents = service.publishes && service.publishes.length > 0 || service.subscribes && service.subscribes.length > 0;
9
+ const operationsCode = generateOperationsWithHelpers(service);
10
+ const hasAiBehaviors = /\baiBehaviors\./.test(operationsCode);
11
+ const usesPrisma = /\bprisma\b/.test(operationsCode);
9
12
  return `/**
10
13
  * ${serviceName}
11
14
  * Abstract business logic service
12
15
  * ${service.description || ""}
13
16
  */
14
-
15
- import { PrismaClient } from '@prisma/client';
16
- ${hasEvents ? `import { eventBus, EventName } from '../events/eventBus.js';` : ""}
17
-
18
- const prisma = new PrismaClient();
17
+ ${usesPrisma ? `
18
+ import { PrismaClient } from '@prisma/client';` : ""}
19
+ ${hasEvents ? `import { eventBus } from '../events/eventBus.js';` : ""}
20
+ ${hasAiBehaviors ? `import * as aiBehaviors from '../behaviors/${serviceName}.ai.js';` : ""}
21
+ ${usesPrisma ? `
22
+ const prisma = new PrismaClient();` : ""}
19
23
 
20
24
  /**
21
25
  * ${serviceName} class
22
26
  */
23
27
  export class ${serviceName} {
24
28
  ${generateConstructor(service)}
25
- ${generateOperationsWithHelpers(service)}
29
+ ${operationsCode}
26
30
  ${generateEventSubscriptions(service)}
27
31
  }
28
32
 
@@ -97,8 +101,10 @@ function generateOperations(service) {
97
101
  return operations.join("\n");
98
102
  }
99
103
  function generateOperation(operationName, operation, service) {
100
- const params = generateOperationParams(operation);
104
+ const rawParams = generateOperationParams(operation);
101
105
  const hasPublish = service.publishes && service.publishes.length > 0;
106
+ const body = generateOperationLogic(operation, service);
107
+ const params = renameUnusedParams(rawParams, body);
102
108
  return `
103
109
  /**
104
110
  * ${operationName}
@@ -106,7 +112,7 @@ function generateOperation(operationName, operation, service) {
106
112
  */
107
113
  public async ${operationName}(${params}): Promise<any> {
108
114
  try {
109
- ${generateOperationLogic(operation, service)}
115
+ ${body}
110
116
 
111
117
  ${hasPublish ? `
112
118
  // Publish event (example)
@@ -125,6 +131,19 @@ function generateOperation(operationName, operation, service) {
125
131
  }
126
132
  `;
127
133
  }
134
+ function renameUnusedParams(paramsString, body) {
135
+ if (!paramsString.trim()) return paramsString;
136
+ return paramsString.split(",").map((segment) => {
137
+ const trimmed = segment.trim();
138
+ const nameMatch = trimmed.match(/^(\w+)/);
139
+ if (!nameMatch) return segment;
140
+ const name = nameMatch[1];
141
+ if (name.startsWith("_")) return segment;
142
+ const re = new RegExp(`\\b${name}\\b`);
143
+ if (re.test(body)) return segment;
144
+ return segment.replace(new RegExp(`\\b${name}\\b`), `_${name}`);
145
+ }).join(", ");
146
+ }
128
147
  function generateOperationParams(operation) {
129
148
  if (!operation.parameters || Object.keys(operation.parameters).length === 0) {
130
149
  return "params: any = {}";
@@ -149,17 +168,19 @@ function generateOperationLogic(operation, service) {
149
168
  }
150
169
  if (impl.preconditions?.length || impl.postconditions?.length || impl.steps?.length || impl.transactional) {
151
170
  const modelName = inferModelFromServiceName(service.name);
171
+ const parameterNames = Object.keys(operation.parameters || {});
152
172
  const context = {
153
173
  modelName,
154
174
  serviceName: service.name,
155
175
  operationName: operation.name || "execute",
156
- prismaModel: modelName
176
+ prismaModel: modelName,
177
+ parameterNames
157
178
  };
158
179
  const behavior = {
159
180
  preconditions: impl.preconditions || [],
160
181
  postconditions: impl.postconditions || [],
161
182
  sideEffects: impl.sideEffects || [],
162
- steps: impl.steps || [],
183
+ steps: impl.steps || operation.steps || [],
163
184
  transactional: impl.transactional || false
164
185
  };
165
186
  const opMeta = {
@@ -4,15 +4,47 @@ function toVar(name) {
4
4
  function toMethod(words) {
5
5
  return words.trim().replace(/\s+(.)/g, (_, c) => c.toUpperCase()).replace(/^\w/, (c) => c.toLowerCase());
6
6
  }
7
+ function resolveValue(rawValue, ctx) {
8
+ const value = rawValue.trim().replace(/^['"]|['"]$/g, "");
9
+ if (/^(current\s*time|now|timestamp)$/i.test(value)) {
10
+ return "new Date().toISOString()";
11
+ }
12
+ if (value === "true" || value === "false") return value;
13
+ if (/^-?\d+(\.\d+)?$/.test(value)) return value;
14
+ const declared = ctx.declaredVars || /* @__PURE__ */ new Set();
15
+ const params = ctx.parameterNames || [];
16
+ const rootMatch = value.match(/^([a-zA-Z_][a-zA-Z0-9_]*)(\.[a-zA-Z0-9_.]+)?$/);
17
+ if (rootMatch) {
18
+ const root = rootMatch[1];
19
+ if (declared.has(root) || params.includes(root)) {
20
+ return value;
21
+ }
22
+ }
23
+ const tokens = value.toLowerCase().split(/\s+/);
24
+ for (const declaredName of [...declared, ...params]) {
25
+ const normalized = declaredName.toLowerCase();
26
+ if (tokens.includes(normalized)) return declaredName;
27
+ }
28
+ const calcMatch = value.match(/^(?:the\s+|calculated\s+|computed\s+|resulting\s+)(\w+)/i);
29
+ if (calcMatch) {
30
+ const field = calcMatch[1];
31
+ const stepResults = [...declared].filter((v) => /^step\d+Result$/.test(v)).sort().reverse();
32
+ if (stepResults.length > 0) {
33
+ return `(${stepResults[0]} as any).${field} /* TODO: verify field name */`;
34
+ }
35
+ }
36
+ if (/\s/.test(value)) {
37
+ return `/* TODO: resolve "${value}" \u2014 no matching declared variable */ null`;
38
+ }
39
+ return `'${value.replace(/'/g, "\\'")}'`;
40
+ }
7
41
  const STEP_CONVENTIONS = [
8
42
  // --- Validation ---
9
43
  {
10
44
  name: "validate",
11
45
  pattern: /^validate\s+(.+)/i,
12
46
  generateCall: (m, ctx) => ` // Step ${ctx.stepNum}: Validate ${m[1]}
13
- const validationResult = this.validate(params, { operation: '${ctx.operationName}' });
14
- if (!validationResult.valid) {
15
- throw new Error(\`Validation failed: \${validationResult.errors.join(', ')}\`);
47
+ // TODO: Add validation logic for ${m[1]}
16
48
  }`
17
49
  },
18
50
  // --- Check / Guard ---
@@ -23,21 +55,9 @@ const STEP_CONVENTIONS = [
23
55
  const condition = m[1];
24
56
  const methodName = toMethod("check " + condition);
25
57
  return ` // Step ${ctx.stepNum}: Check ${condition}
26
- const checkResult = await this.${methodName}(params);
27
- if (!checkResult) {
28
- throw new Error('Check failed: ${condition}');
29
- }`;
30
- },
31
- generateMethod: (m, ctx) => {
32
- const condition = m[1];
33
- const methodName = toMethod("check " + condition);
34
- return `
35
- private async ${methodName}(params: any): Promise<boolean> {
36
58
  // TODO: Implement check \u2014 ${condition}
37
- const ${toVar(ctx.prismaModel)} = await this.prisma.${toVar(ctx.prismaModel)}.findUnique({ where: { id: params.id } });
38
- if (!${toVar(ctx.prismaModel)}) return false;
39
- return true;
40
- }`;
59
+ // const checkResult = ...
60
+ // if (!checkResult) throw new Error('Check failed: ${condition}');`;
41
61
  }
42
62
  },
43
63
  // --- Find / Lookup ---
@@ -47,21 +67,29 @@ const STEP_CONVENTIONS = [
47
67
  generateCall: (m, ctx) => {
48
68
  const model = m[1];
49
69
  const field = m[2];
70
+ const modelVar = toVar(model);
71
+ const params = ctx.parameterNames || [];
72
+ const declared = ctx.declaredVars || /* @__PURE__ */ new Set();
73
+ const idParam = field === "id" ? params.find((p) => p === `${modelVar}Id`) || params.find((p) => p === "id") || `${modelVar}Id` : params.find((p) => p === field) || `params.${field}`;
74
+ if (declared.has(modelVar)) {
75
+ return ` // Step ${ctx.stepNum}: Find ${model} by ${field} (already loaded)`;
76
+ }
77
+ declared.add(modelVar);
50
78
  return ` // Step ${ctx.stepNum}: Find ${model} by ${field}
51
- const ${toVar(model)} = await this.prisma.${toVar(model)}.findUnique({ where: { ${field}: params.${field} } });
52
- if (!${toVar(model)}) {
53
- throw new Error('${model} not found by ${field}');
54
- }`;
79
+ const ${modelVar} = await prisma.${modelVar}.findUniqueOrThrow({ where: { ${field}: ${idParam} } });`;
55
80
  }
56
81
  },
57
82
  // --- Create ---
58
83
  {
59
84
  name: "create",
60
- pattern: /^create\s+(\w+)(?:\s+record)?/i,
85
+ pattern: /^create\s+(\w+)(?:\s+(?:with\s+)?(.+))?/i,
61
86
  generateCall: (m, ctx) => {
62
87
  const model = m[1];
88
+ const modelVar = toVar(model);
89
+ const paramNames = ctx.parameterNames || [];
90
+ const dataFields = paramNames.length > 0 ? `{ ${paramNames.join(", ")} }` : "data";
63
91
  return ` // Step ${ctx.stepNum}: Create ${model}
64
- const ${toVar(model)} = await this.prisma.${toVar(model)}.create({ data: params });`;
92
+ const ${modelVar} = await prisma.${modelVar}.create({ data: ${dataFields} });`;
65
93
  }
66
94
  },
67
95
  // --- Update field ---
@@ -71,12 +99,12 @@ const STEP_CONVENTIONS = [
71
99
  generateCall: (m, ctx) => {
72
100
  const model = m[1];
73
101
  const field = m[2];
74
- const value = m[3].trim().replace(/"/g, "'");
75
- const isString = /^[a-z]/.test(value) && !value.includes(".");
76
- const val = isString ? `'${value}'` : value;
77
- return ` // Step ${ctx.stepNum}: Update ${model} ${field} to ${value}
78
- await this.prisma.${toVar(model)}.update({
79
- where: { id: params.id },
102
+ const rawValue = m[3];
103
+ const modelVar = toVar(model);
104
+ const val = resolveValue(rawValue, ctx);
105
+ return ` // Step ${ctx.stepNum}: Update ${model} ${field} to ${rawValue.trim()}
106
+ await prisma.${modelVar}.update({
107
+ where: { id: ${modelVar}.id },
80
108
  data: { ${field}: ${val} },
81
109
  });`;
82
110
  }
@@ -87,9 +115,10 @@ const STEP_CONVENTIONS = [
87
115
  pattern: /^update\s+(\w+)(?:\s+(.+))?/i,
88
116
  generateCall: (m, ctx) => {
89
117
  const model = m[1];
118
+ const modelVar = toVar(model);
90
119
  return ` // Step ${ctx.stepNum}: Update ${model}
91
- await this.prisma.${toVar(model)}.update({
92
- where: { id: params.id },
120
+ await prisma.${modelVar}.update({
121
+ where: { id: ${modelVar}.id },
93
122
  data: params,
94
123
  });`;
95
124
  }
@@ -100,8 +129,9 @@ const STEP_CONVENTIONS = [
100
129
  pattern: /^delete\s+(\w+)/i,
101
130
  generateCall: (m, ctx) => {
102
131
  const model = m[1];
132
+ const modelVar = toVar(model);
103
133
  return ` // Step ${ctx.stepNum}: Delete ${model}
104
- await this.prisma.${toVar(model)}.delete({ where: { id: params.id } });`;
134
+ await prisma.${modelVar}.delete({ where: { id: ${modelVar}.id } });`;
105
135
  }
106
136
  },
107
137
  // --- Transition / Evolve ---
@@ -111,11 +141,11 @@ const STEP_CONVENTIONS = [
111
141
  generateCall: (m, ctx) => {
112
142
  const model = m[1];
113
143
  const state = m[2];
144
+ const modelVar = toVar(model);
114
145
  return ` // Step ${ctx.stepNum}: Transition ${model} to ${state}
115
- const current = await this.prisma.${toVar(model)}.findUniqueOrThrow({ where: { id: params.id } });
116
- // Lifecycle validation would check valid transition here
117
- await this.prisma.${toVar(model)}.update({
118
- where: { id: params.id },
146
+ if (${modelVar}.status === '${state}') throw new Error('${model} is already ${state}');
147
+ await prisma.${modelVar}.update({
148
+ where: { id: ${modelVar}.id },
119
149
  data: { status: '${state}' },
120
150
  });`;
121
151
  }
@@ -126,12 +156,12 @@ const STEP_CONVENTIONS = [
126
156
  pattern: /^set\s+(\w+)\s+to\s+(.+)/i,
127
157
  generateCall: (m, ctx) => {
128
158
  const field = m[1];
129
- const value = m[2].trim();
130
- const isCurrentTime = /current time|now|timestamp/i.test(value);
131
- const val = isCurrentTime ? "new Date()" : `'${value}'`;
132
- return ` // Step ${ctx.stepNum}: Set ${field} to ${value}
133
- await this.prisma.${toVar(ctx.prismaModel)}.update({
134
- where: { id: params.id },
159
+ const rawValue = m[2];
160
+ const modelVar = toVar(ctx.prismaModel);
161
+ const val = resolveValue(rawValue, ctx);
162
+ return ` // Step ${ctx.stepNum}: Set ${field} to ${rawValue.trim()}
163
+ await prisma.${modelVar}.update({
164
+ where: { id: ${modelVar}.id },
135
165
  data: { ${field}: ${val} },
136
166
  });`;
137
167
  }
@@ -143,10 +173,11 @@ const STEP_CONVENTIONS = [
143
173
  generateCall: (m, ctx) => {
144
174
  const field = m[1];
145
175
  const amount = m[2];
146
- const amountVal = /^\d+$/.test(amount) ? amount : `params.${amount}`;
176
+ const modelVar = toVar(ctx.prismaModel);
177
+ const amountVal = /^\d+$/.test(amount) ? amount : amount;
147
178
  return ` // Step ${ctx.stepNum}: Increment ${field} by ${amount}
148
- await this.prisma.${toVar(ctx.prismaModel)}.update({
149
- where: { id: params.id },
179
+ await prisma.${modelVar}.update({
180
+ where: { id: ${modelVar}.id },
150
181
  data: { ${field}: { increment: ${amountVal} } },
151
182
  });`;
152
183
  }
@@ -158,38 +189,18 @@ const STEP_CONVENTIONS = [
158
189
  generateCall: (m, ctx) => {
159
190
  const field = m[1];
160
191
  const amount = m[2];
161
- const amountVal = /^\d+$/.test(amount) ? amount : `params.${amount}`;
192
+ const modelVar = toVar(ctx.prismaModel);
193
+ const amountVal = /^\d+$/.test(amount) ? amount : amount;
162
194
  return ` // Step ${ctx.stepNum}: Decrement ${field} by ${amount}
163
- const current${field} = await this.prisma.${toVar(ctx.prismaModel)}.findUniqueOrThrow({ where: { id: params.id } });
164
- if ((current${field}.${field} || 0) < ${amountVal}) {
165
- throw new Error('Cannot decrement ${field} below zero');
166
- }
167
- await this.prisma.${toVar(ctx.prismaModel)}.update({
168
- where: { id: params.id },
195
+ await prisma.${modelVar}.update({
196
+ where: { id: ${modelVar}.id },
169
197
  data: { ${field}: { decrement: ${amountVal} } },
170
198
  });`;
171
199
  }
172
200
  },
173
- // --- Calculate ---
174
- {
175
- name: "calculate",
176
- pattern: /^calculate\s+(.+)/i,
177
- generateCall: (m, ctx) => {
178
- const metric = m[1];
179
- const methodName = toMethod("calculate " + metric);
180
- return ` // Step ${ctx.stepNum}: Calculate ${metric}
181
- const ${toVar(metric.replace(/\s+/g, ""))} = await this.${methodName}(params);`;
182
- },
183
- generateMethod: (m, ctx) => {
184
- const metric = m[1];
185
- const methodName = toMethod("calculate " + metric);
186
- return `
187
- private async ${methodName}(params: any): Promise<number> {
188
- // TODO: Implement calculation \u2014 ${metric}
189
- return 0;
190
- }`;
191
- }
192
- },
201
+ // NOTE: "calculate X" is intentionally NOT a convention — it falls through to AI
202
+ // behaviors because calculations are domain-specific pure functions that benefit
203
+ // from AI generation with the full input context.
193
204
  // --- Send event ---
194
205
  {
195
206
  name: "send-event",
@@ -197,7 +208,7 @@ const STEP_CONVENTIONS = [
197
208
  generateCall: (m, ctx) => {
198
209
  const event = m[1];
199
210
  return ` // Step ${ctx.stepNum}: Emit ${event} event
200
- this.emit('${event}', { ${toVar(ctx.prismaModel)}Id: params.id, operation: '${ctx.operationName}', timestamp: new Date().toISOString() });`;
211
+ await eventBus.publish('${event}', { ${toVar(ctx.prismaModel)}Id: ${toVar(ctx.prismaModel)}.id, operation: '${ctx.operationName}', timestamp: new Date().toISOString() });`;
201
212
  }
202
213
  },
203
214
  // --- Send notification ---
@@ -207,7 +218,7 @@ const STEP_CONVENTIONS = [
207
218
  generateCall: (m, ctx) => {
208
219
  const type = m[1];
209
220
  return ` // Step ${ctx.stepNum}: Send ${type} notification
210
- this.emit('${type}Notification', { ${toVar(ctx.prismaModel)}Id: params.id, operation: '${ctx.operationName}' });`;
221
+ await eventBus.publish('${type}Notification', { ${toVar(ctx.prismaModel)}Id: ${toVar(ctx.prismaModel)}.id, operation: '${ctx.operationName}' });`;
211
222
  }
212
223
  },
213
224
  // --- Call service ---
@@ -218,7 +229,7 @@ const STEP_CONVENTIONS = [
218
229
  const service = m[1];
219
230
  const method = m[2];
220
231
  return ` // Step ${ctx.stepNum}: Call ${service}.${method}
221
- await this.${toVar(service)}.${method}(params);`;
232
+ await ${toVar(service)}.${method}({ ${(ctx.parameterNames || []).join(", ")} });`;
222
233
  }
223
234
  },
224
235
  // --- Return ---
@@ -243,19 +254,25 @@ function matchStep(step, ctx) {
243
254
  if (match) {
244
255
  return {
245
256
  call: convention.generateCall(match, ctx),
246
- helperMethod: convention.generateMethod?.(match, ctx)
257
+ helperMethod: convention.generateMethod?.(match, ctx),
258
+ matched: true
247
259
  };
248
260
  }
249
261
  }
250
- const methodName = toMethod(step);
262
+ const functionName = toMethod(step);
263
+ const declared = Array.from(ctx.declaredVars || []);
264
+ const paramNames = ctx.parameterNames || [];
265
+ const inputs = [...paramNames, ...declared];
266
+ const resultVar = ctx.resultName || `step${ctx.stepNum}Result`;
267
+ const inputObj = inputs.length > 0 ? `{ ${inputs.join(", ")} }` : "{}";
268
+ if (ctx.declaredVars) ctx.declaredVars.add(resultVar);
251
269
  return {
252
- call: ` // Step ${ctx.stepNum}: ${step}
253
- await this.${methodName}(params);`,
254
- helperMethod: `
255
- private async ${methodName}(params: any): Promise<void> {
256
- // TODO: Implement \u2014 ${step}
257
- throw new Error('Not implemented: ${methodName}');
258
- }`
270
+ call: ` // Step ${ctx.stepNum}: ${step} [AI-generated \u2014 pure function]
271
+ const ${resultVar} = await aiBehaviors.${functionName}(${inputObj});`,
272
+ matched: false,
273
+ functionName,
274
+ inputs,
275
+ resultVar
259
276
  };
260
277
  }
261
278
  export {