@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
@@ -0,0 +1,249 @@
1
+ import { matchStep } from "./step-conventions.js";
2
+ import { createHash } from "crypto";
3
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
4
+ import { join } from "path";
5
+ async function validateTypeScript(code) {
6
+ try {
7
+ const esbuild = await import("esbuild");
8
+ await esbuild.transform(code, {
9
+ loader: "ts",
10
+ format: "esm",
11
+ target: "es2022"
12
+ });
13
+ return null;
14
+ } catch (err) {
15
+ const msg = err?.errors?.[0]?.text || err?.message || "unknown syntax error";
16
+ return msg;
17
+ }
18
+ }
19
+ const PROMPT_VERSION = "9.1.0";
20
+ function cacheKey(step, modelName, operationName, functionName, inputs) {
21
+ const payload = JSON.stringify({ step, modelName, operationName, functionName, inputs: [...inputs].sort(), v: PROMPT_VERSION });
22
+ return createHash("sha256").update(payload).digest("hex").slice(0, 16);
23
+ }
24
+ function cacheDir() {
25
+ const cwd = process.env.SPECVERSE_USER_CWD || process.cwd();
26
+ return join(cwd, ".specverse", "ai-cache");
27
+ }
28
+ function cacheRead(key) {
29
+ const path = join(cacheDir(), `${key}.ts`);
30
+ if (!existsSync(path)) return null;
31
+ try {
32
+ return readFileSync(path, "utf8");
33
+ } catch {
34
+ return null;
35
+ }
36
+ }
37
+ function cacheWrite(key, body) {
38
+ const dir = cacheDir();
39
+ try {
40
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
41
+ writeFileSync(join(dir, `${key}.ts`), body, "utf8");
42
+ } catch (err) {
43
+ console.warn(` [ai-cache] write failed: ${err?.message || err}`);
44
+ }
45
+ }
46
+ async function generateAiBehaviors(context) {
47
+ const { controller, model } = context;
48
+ if (!controller?.actions) return "";
49
+ const modelName = model?.name || controller.model || "Model";
50
+ const modelVar = modelName.charAt(0).toLowerCase() + modelName.slice(1);
51
+ const unmatchedFunctions = [];
52
+ for (const [actionName, action] of Object.entries(controller.actions)) {
53
+ const steps = action.steps || [];
54
+ const parameterNames = Object.keys(action.parameters || {});
55
+ const preconditions = action.requires || action.preconditions || [];
56
+ const declaredVars = /* @__PURE__ */ new Set();
57
+ for (const pc of preconditions) {
58
+ const match = pc.match(/^(\w+)\s+(?:exists|is\s+\w+)$/i);
59
+ if (match) {
60
+ const entity = match[1];
61
+ declaredVars.add(entity.charAt(0).toLowerCase() + entity.slice(1));
62
+ }
63
+ }
64
+ for (let i = 0; i < steps.length; i++) {
65
+ const stepInput = steps[i];
66
+ const stepText = typeof stepInput === "string" ? stepInput : stepInput?.step;
67
+ const stepAs = typeof stepInput === "object" ? stepInput?.as : void 0;
68
+ const stepReturns = typeof stepInput === "object" ? stepInput?.returns : void 0;
69
+ if (typeof stepText !== "string") continue;
70
+ const ctx = {
71
+ modelName,
72
+ prismaModel: modelVar,
73
+ serviceName: `${modelName}Controller`,
74
+ operationName: actionName,
75
+ stepNum: i + 1,
76
+ parameterNames,
77
+ declaredVars,
78
+ resultName: stepAs
79
+ };
80
+ const result = matchStep(stepText, ctx);
81
+ if (!result.matched && result.functionName) {
82
+ const existing = unmatchedFunctions.find((f) => f.functionName === result.functionName);
83
+ if (!existing) {
84
+ unmatchedFunctions.push({
85
+ functionName: result.functionName,
86
+ step: stepText,
87
+ operationName: actionName,
88
+ parameterNames,
89
+ inputs: result.inputs || [],
90
+ returns: stepReturns
91
+ });
92
+ }
93
+ }
94
+ }
95
+ }
96
+ if (unmatchedFunctions.length === 0) return "";
97
+ return generateAiBehaviorsFile({
98
+ ownerName: `${modelName}Controller`,
99
+ unmatchedFunctions: unmatchedFunctions.map((f) => ({
100
+ ...f,
101
+ modelName
102
+ })),
103
+ availableModels,
104
+ spec: context.spec
105
+ });
106
+ }
107
+ async function generateAiBehaviorsFile(opts) {
108
+ const { ownerName, unmatchedFunctions, availableModels: availableModels2, spec } = opts;
109
+ if (unmatchedFunctions.length === 0) return "";
110
+ let aiService = null;
111
+ try {
112
+ const { BehaviorAIService } = await import("@specverse/engines/ai");
113
+ aiService = new BehaviorAIService();
114
+ if (!aiService.isAvailable) {
115
+ aiService = null;
116
+ } else {
117
+ aiService.startSession(ownerName);
118
+ }
119
+ } catch {
120
+ aiService = null;
121
+ }
122
+ const functions = [];
123
+ let cacheHits = 0;
124
+ let cacheMisses = 0;
125
+ for (const { functionName, step, operationName, parameterNames, inputs, returns, modelName } of unmatchedFunctions) {
126
+ const signature = inputs.length > 0 ? `input: { ${inputs.map((n) => `${n}: any`).join("; ")} }` : "input: Record<string, never>";
127
+ const destructure = inputs.length > 0 ? ` const { ${inputs.join(", ")} } = input;` : "";
128
+ let returnType = "any";
129
+ if (typeof returns === "string") {
130
+ returnType = returns;
131
+ } else if (returns && typeof returns === "object") {
132
+ const fields = Object.entries(returns).map(([k, v]) => `${k}: ${v}`).join("; ");
133
+ returnType = `{ ${fields} }`;
134
+ }
135
+ const key = cacheKey(step, modelName, operationName, functionName, inputs);
136
+ let body = cacheRead(key);
137
+ let source = body ? "AI-CACHED" : "STUB";
138
+ if (body) {
139
+ const testCode = `export async function ${functionName}(input: any): Promise<any> {
140
+ ${body}
141
+ }`;
142
+ const validationError = await validateTypeScript(testCode);
143
+ if (validationError) {
144
+ console.warn(` [ai-validate] cached ${functionName} failed validation: ${validationError}`);
145
+ body = null;
146
+ source = "STUB";
147
+ } else {
148
+ cacheHits++;
149
+ }
150
+ }
151
+ if (!body && aiService) {
152
+ cacheMisses++;
153
+ try {
154
+ body = await aiService.generateBehavior({
155
+ step,
156
+ modelName,
157
+ operationName,
158
+ functionName,
159
+ parameterNames: inputs,
160
+ // the actual inputs to the pure function
161
+ availableModels: availableModels2,
162
+ spec,
163
+ returnType
164
+ // Pass declared return type to Claude
165
+ });
166
+ if (body) {
167
+ const testCode = `export async function ${functionName}(input: any): Promise<any> {
168
+ ${body}
169
+ }`;
170
+ const validationError = await validateTypeScript(testCode);
171
+ if (validationError) {
172
+ console.warn(` [ai-validate] ${functionName} has syntax error: ${validationError}`);
173
+ body = `// AI-generated code failed validation: ${validationError}
174
+ // Step: ${step}
175
+ throw new Error('AI behavior has invalid syntax \u2014 see comment above');`;
176
+ source = "AI-INVALID";
177
+ } else {
178
+ source = "AI-GENERATED";
179
+ cacheWrite(key, body);
180
+ }
181
+ }
182
+ } catch {
183
+ }
184
+ }
185
+ if (!body) {
186
+ body = ` throw new Error('Not implemented: ${functionName} \u2014 see behaviors/${modelName}Controller.ai.ts');`;
187
+ } else {
188
+ body = body.split("\n").map((line) => line ? " " + line : line).join("\n");
189
+ }
190
+ const inputsDoc = inputs.length > 0 ? ` * Inputs: ${inputs.join(", ")}
191
+ ` : "";
192
+ const returnsDoc = returnType !== "any" ? ` * Returns: ${returnType}
193
+ ` : "";
194
+ functions.push(`/**
195
+ * ${functionName}
196
+ *
197
+ * Spec step: "${step}"
198
+ * Called by: ${ownerName}.${operationName}()
199
+ ${inputsDoc}${returnsDoc} * Source: ${source}
200
+ * Generated: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
201
+ *
202
+ * PURE FUNCTION \u2014 no database access, no event publishing, no external services.
203
+ * All data comes in via \`input\`; all effects happen in the calling controller.
204
+ * ${source === "AI-GENERATED" ? "AI-generated implementation. Review and test before deploying." : source === "AI-CACHED" ? "Restored from AI cache (.specverse/ai-cache/). Delete cache entry to regenerate." : source === "AI-INVALID" ? "AI returned code with syntax errors \u2014 function throws at runtime. Fix or regenerate." : "STUB \u2014 Claude CLI unavailable. Install Claude Code or implement manually."}
205
+ */
206
+ export async function ${functionName}(${signature}): Promise<${returnType}> {
207
+ ${destructure ? destructure + "\n" : ""}${body}
208
+ }`);
209
+ }
210
+ if (aiService?.endSession) aiService.endSession();
211
+ if (cacheHits > 0 || cacheMisses > 0) {
212
+ console.log(` AI cache: ${cacheHits} hit(s), ${cacheMisses} miss(es) for ${ownerName}`);
213
+ }
214
+ return `/**
215
+ * ${ownerName} \u2014 AI-Generated Behaviors
216
+ *
217
+ * \u26A0\uFE0F THIS FILE CONTAINS STUBS FOR STEPS THAT NEED IMPLEMENTATION
218
+ *
219
+ * These functions could not be generated from convention patterns.
220
+ * They are called by ${ownerName} when executing operations.
221
+ *
222
+ * Options for each function:
223
+ * - Implement manually (recommended for business-critical logic)
224
+ * - Use AI generation: specverse ai generate <function>
225
+ * - Refactor the spec step to use a convention pattern
226
+ *
227
+ * Convention patterns that ARE auto-generated (no AI needed):
228
+ * "Find {Model} by {field}" \u2192 prisma.model.findUniqueOrThrow(...)
229
+ * "Create {Model}" \u2192 prisma.model.create(...)
230
+ * "Update {Model} {field} to {value}" \u2192 prisma.model.update(...)
231
+ * "Delete {Model}" \u2192 prisma.model.delete(...)
232
+ * "Transition {Model} to {state}" \u2192 prisma.model.update({ status: ... })
233
+ * "Count {Model}s per {Group}" \u2192 prisma.model.groupBy(...)
234
+ * See step-conventions.ts for the full list.
235
+ *
236
+ * Generated: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
237
+ */
238
+
239
+ import { PrismaClient } from '@prisma/client';
240
+
241
+ const prisma = new PrismaClient();
242
+
243
+ ${functions.join("\n\n")}
244
+ `;
245
+ }
246
+ export {
247
+ generateAiBehaviors as default,
248
+ generateAiBehaviorsFile
249
+ };
@@ -6,14 +6,17 @@ function generateBehaviorBody(behavior, opMeta, context) {
6
6
  function generateBehaviorWithHelpers(behavior, opMeta, context) {
7
7
  const parts = [];
8
8
  const helperMethods = [];
9
+ const preconditionDeclared = /* @__PURE__ */ new Set();
9
10
  const preconditions = generatePreconditionChecks(
10
11
  behavior.preconditions || [],
11
- context
12
+ context,
13
+ preconditionDeclared
12
14
  );
13
15
  if (preconditions) parts.push(preconditions);
14
- const { code, helpers } = generateStepLogic(
16
+ const { code, helpers, unmatchedSteps } = generateStepLogic(
15
17
  behavior.steps || [],
16
- context
18
+ context,
19
+ preconditionDeclared
17
20
  );
18
21
  parts.push(code);
19
22
  helperMethods.push(...helpers);
@@ -23,79 +26,86 @@ function generateBehaviorWithHelpers(behavior, opMeta, context) {
23
26
  if (postconditions) parts.push(postconditions);
24
27
  const events = generateEventPublishing(
25
28
  behavior.sideEffects || [],
26
- context.operationName
29
+ context.operationName,
30
+ context.parameterNames
27
31
  );
28
32
  if (events) parts.push(events);
29
33
  let body = parts.join("\n\n");
30
34
  if (behavior.transactional) {
31
35
  body = generateTransactionWrapper(body, context);
32
36
  }
33
- return { body, helperMethods };
37
+ return { body, helperMethods, unmatchedSteps };
34
38
  }
35
- function generatePreconditionChecks(preconditions, context) {
39
+ function generatePreconditionChecks(preconditions, context, declared) {
36
40
  if (preconditions.length === 0) return "";
37
- const checks = preconditions.map((pc) => matchPreconditionPattern(pc, context));
41
+ if (!declared) declared = /* @__PURE__ */ new Set();
42
+ const checks = preconditions.map((pc) => matchPreconditionPattern(pc, context, declared));
38
43
  return ` // === PRECONDITIONS ===
39
44
  ${checks.join("\n")}`;
40
45
  }
41
- function matchPreconditionPattern(precondition, context) {
46
+ function findIdParam(modelName, paramNames) {
47
+ const modelVar = modelName.charAt(0).toLowerCase() + modelName.slice(1);
48
+ const idParam = paramNames.find((p) => p === `${modelVar}Id`);
49
+ if (idParam) return idParam;
50
+ if (paramNames.includes("id")) return "id";
51
+ return `${modelVar}Id`;
52
+ }
53
+ function matchPreconditionPattern(precondition, context, declared) {
42
54
  const pc = precondition.toLowerCase();
43
- const prismaModel = context.prismaModel || context.modelName;
55
+ const params = context.parameterNames || [];
44
56
  const existsMatch = precondition.match(/^(\w+)\s+exists/i);
45
57
  if (existsMatch) {
46
58
  const entity = existsMatch[1];
47
59
  const entityVar = entity.charAt(0).toLowerCase() + entity.slice(1);
48
- return ` // Guard: ${precondition}
49
- const ${entityVar} = await prisma.${entityVar}.findUnique({ where: { id: params.id } });
50
- if (!${entityVar}) {
51
- throw new Error('Precondition failed: ${precondition}');
52
- }`;
60
+ const idParam = findIdParam(entity, params);
61
+ if (declared.has(entityVar)) {
62
+ return ` // Guard: ${precondition} (already fetched)`;
63
+ }
64
+ declared.add(entityVar);
65
+ return ` const ${entityVar} = await prisma.${entityVar}.findUniqueOrThrow({ where: { id: ${idParam} } });`;
53
66
  }
54
67
  if (pc.includes("is not empty") || pc.includes("is required")) {
55
68
  const fieldMatch = precondition.match(/^(\w+)\s+is/i);
56
69
  if (fieldMatch) {
57
70
  const field = fieldMatch[1];
58
- return ` // Guard: ${precondition}
59
- if (!params.${field}) {
60
- throw new Error('Precondition failed: ${precondition}');
61
- }`;
71
+ const paramRef = params.includes(field) ? field : `params.${field}`;
72
+ return ` if (!${paramRef}) throw new Error('${field} is required');`;
62
73
  }
63
74
  }
64
75
  if (pc.includes("is valid")) {
65
- return ` // Guard: ${precondition}
66
- const validation = this.validate(params, { operation: '${context.operationName}' });
67
- if (!validation.valid) {
68
- throw new Error('Precondition failed: ${precondition} \u2014 ' + validation.errors.join(', '));
69
- }`;
76
+ return ` // TODO: Implement validation: ${precondition}`;
70
77
  }
71
78
  const matchesMatch = precondition.match(/^(\w+)\s+(?:matches|equals)\s+(.+)/i);
72
79
  if (matchesMatch) {
73
- const left = matchesMatch[1];
74
- const right = matchesMatch[2];
75
- return ` // Guard: ${precondition}
76
- if (params.${left.charAt(0).toLowerCase() + left.slice(1)} !== params.${right.charAt(0).toLowerCase() + right.slice(1)}) {
77
- throw new Error('Precondition failed: ${precondition}');
78
- }`;
80
+ const left = matchesMatch[1].charAt(0).toLowerCase() + matchesMatch[1].slice(1);
81
+ const right = matchesMatch[2].charAt(0).toLowerCase() + matchesMatch[2].slice(1);
82
+ return ` if (${left} !== ${right}) throw new Error('${matchesMatch[1]} must match ${matchesMatch[2]}');`;
79
83
  }
80
84
  const stateMatch = precondition.match(/^(\w+)\s+is\s+(\w+)$/i);
81
85
  if (stateMatch) {
82
86
  const model = stateMatch[1];
83
87
  const state = stateMatch[2];
84
88
  const modelVar = model.charAt(0).toLowerCase() + model.slice(1);
85
- return ` // Guard: ${precondition}
86
- const ${modelVar}State = await prisma.${modelVar}.findUniqueOrThrow({ where: { id: params.id } });
87
- if (${modelVar}State.status !== '${state}') {
88
- throw new Error('Precondition failed: ${precondition} (current: ' + ${modelVar}State.status + ')');
89
- }`;
89
+ const idParam = findIdParam(model, params);
90
+ if (!declared.has(modelVar)) {
91
+ declared.add(modelVar);
92
+ return ` const ${modelVar} = await prisma.${modelVar}.findUniqueOrThrow({ where: { id: ${idParam} } });
93
+ if (${modelVar}.status !== '${state}') throw new Error('${model} must be ${state}, got ' + ${modelVar}.status);`;
94
+ }
95
+ return ` if (${modelVar}.status !== '${state}') throw new Error('${model} must be ${state}, got ' + ${modelVar}.status);`;
90
96
  }
91
- return ` // Guard: ${precondition}
92
- // TODO: Implement precondition check`;
97
+ return ` // TODO: Implement precondition: ${precondition}`;
93
98
  }
94
- function generateStepLogic(steps, context) {
99
+ function generateStepLogic(steps, context, preconditionDeclared) {
95
100
  const helpers = [];
101
+ const unmatchedSteps = [];
102
+ const declaredVars = preconditionDeclared || /* @__PURE__ */ new Set();
96
103
  if (steps && steps.length > 0) {
97
- const stepCode = steps.map((step, i) => {
98
- if (typeof step !== "string") {
104
+ const stepCode = steps.map((stepInput, i) => {
105
+ const stepText = typeof stepInput === "string" ? stepInput : stepInput.step;
106
+ const resultName = typeof stepInput === "object" ? stepInput.as : void 0;
107
+ const returns = typeof stepInput === "object" ? stepInput.returns : void 0;
108
+ if (typeof stepText !== "string") {
99
109
  return ` // Step ${i + 1}: Complex operation \u2014 see expanded definition`;
100
110
  }
101
111
  const ctx = {
@@ -103,24 +113,40 @@ function generateStepLogic(steps, context) {
103
113
  prismaModel: context.prismaModel || context.modelName,
104
114
  serviceName: context.serviceName,
105
115
  operationName: context.operationName,
106
- stepNum: i + 1
116
+ stepNum: i + 1,
117
+ parameterNames: context.parameterNames,
118
+ declaredVars,
119
+ resultName
120
+ // Pass through the named result
107
121
  };
108
- const result = matchStep(step, ctx);
122
+ const result = matchStep(stepText, ctx);
109
123
  if (result.helperMethod) {
110
124
  helpers.push(result.helperMethod);
111
125
  }
126
+ if (!result.matched && result.functionName) {
127
+ unmatchedSteps.push({
128
+ step: stepText,
129
+ functionName: result.functionName,
130
+ operationName: context.operationName,
131
+ inputs: result.inputs || [],
132
+ returns,
133
+ resultName: resultName || `step${i + 1}Result`
134
+ });
135
+ }
112
136
  return result.call;
113
137
  });
114
138
  return {
115
139
  code: ` // === EXECUTE ===
116
140
  ${stepCode.join("\n\n")}`,
117
- helpers
141
+ helpers,
142
+ unmatchedSteps
118
143
  };
119
144
  }
120
145
  return {
121
146
  code: ` // === EXECUTE ===
122
147
  ${inferLogicFromOperationName(context)}`,
123
- helpers
148
+ helpers,
149
+ unmatchedSteps
124
150
  };
125
151
  }
126
152
  function inferLogicFromOperationName(context) {
@@ -154,10 +180,11 @@ function generatePostconditionVerification(postconditions) {
154
180
  ${checks.join("\n")}
155
181
  }`;
156
182
  }
157
- function generateEventPublishing(sideEffects, operationName) {
183
+ function generateEventPublishing(sideEffects, operationName, parameterNames) {
158
184
  if (!sideEffects || sideEffects.length === 0) return "";
185
+ const paramFields = parameterNames?.length ? parameterNames.join(", ") + ", " : "";
159
186
  const publishes = sideEffects.map(
160
- (event) => ` this.emit('${event}', { operation: '${operationName}', timestamp: new Date().toISOString() });`
187
+ (event) => ` await eventBus.publish('${event}', { ${paramFields}timestamp: new Date().toISOString() } as any);`
161
188
  );
162
189
  return ` // === EVENTS ===
163
190
  ${publishes.join("\n")}`;