@synergenius/flow-weaver 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/annotation-generator.js +7 -7
  2. package/dist/api/generate-in-place.js +25 -20
  3. package/dist/api/patterns.js +12 -15
  4. package/dist/built-in-nodes/wait-for-agent.js +4 -0
  5. package/dist/chevrotain-parser/node-parser.d.ts +4 -0
  6. package/dist/chevrotain-parser/node-parser.js +24 -1
  7. package/dist/chevrotain-parser/tokens.d.ts +1 -0
  8. package/dist/chevrotain-parser/tokens.js +5 -0
  9. package/dist/cli/flow-weaver.mjs +110 -111
  10. package/dist/cli/index.js +1 -1
  11. package/dist/cli/templates/workflows/aggregator.js +3 -6
  12. package/dist/cli/templates/workflows/ai-agent-durable.js +4 -8
  13. package/dist/cli/templates/workflows/ai-agent.js +3 -6
  14. package/dist/cli/templates/workflows/ai-chat.js +3 -6
  15. package/dist/cli/templates/workflows/ai-pipeline-durable.js +4 -8
  16. package/dist/cli/templates/workflows/ai-rag.js +2 -4
  17. package/dist/cli/templates/workflows/ai-react.js +3 -6
  18. package/dist/cli/templates/workflows/conditional.js +3 -6
  19. package/dist/cli/templates/workflows/error-handler.js +2 -4
  20. package/dist/cli/templates/workflows/foreach.js +3 -6
  21. package/dist/cli/templates/workflows/sequential.js +7 -8
  22. package/dist/cli/templates/workflows/webhook.js +3 -6
  23. package/dist/doc-metadata/extractors/annotations.js +9 -6
  24. package/dist/editor-completions/jsDocAnnotations.js +5 -3
  25. package/dist/jsdoc-parser.d.ts +2 -0
  26. package/dist/jsdoc-parser.js +6 -1
  27. package/dist/parser.js +4 -3
  28. package/docs/reference/built-in-nodes.md +8 -0
  29. package/docs/reference/concepts.md +8 -7
  30. package/docs/reference/debugging.md +4 -1
  31. package/docs/reference/iterative-development.md +2 -3
  32. package/docs/reference/jsdoc-grammar.md +8 -3
  33. package/docs/reference/patterns.md +2 -4
  34. package/docs/reference/tutorial.md +8 -14
  35. package/package.json +1 -1
@@ -281,12 +281,7 @@ export class AnnotationGenerator {
281
281
  if (workflow.ui?.startNode?.x !== undefined && workflow.ui?.startNode?.y !== undefined) {
282
282
  lines.push(` * @position Start ${Math.round(workflow.ui.startNode.x)} ${Math.round(workflow.ui.startNode.y)}`);
283
283
  }
284
- // Add instance positions
285
- workflow.instances.forEach((instance) => {
286
- if (instance.config?.x !== undefined && instance.config?.y !== undefined) {
287
- lines.push(` * @position ${instance.id} ${Math.round(instance.config.x)} ${Math.round(instance.config.y)}`);
288
- }
289
- });
284
+ // Instance positions are now emitted as [position: x y] on @node lines
290
285
  // Add Exit node position if present
291
286
  if (workflow.ui?.exitNode?.x !== undefined && workflow.ui?.exitNode?.y !== undefined) {
292
287
  lines.push(` * @position Exit ${Math.round(workflow.ui.exitNode.x)} ${Math.round(workflow.ui.exitNode.y)}`);
@@ -612,7 +607,12 @@ export function generateNodeInstanceTag(instance) {
612
607
  if (instance.config?.width !== undefined && instance.config?.height !== undefined) {
613
608
  sizeAttr = ` [size: ${Math.round(instance.config.width)} ${Math.round(instance.config.height)}]`;
614
609
  }
615
- return ` * @node ${instance.id} ${instance.nodeType}${parent}${labelAttr}${portOrderAttr}${portLabelAttr}${exprAttr}${pullExecutionAttr}${minimizedAttr}${colorAttr}${iconAttr}${tagsAttr}${sizeAttr}`;
610
+ // Generate [position: x y] attribute if present
611
+ let positionAttr = '';
612
+ if (instance.config?.x !== undefined && instance.config?.y !== undefined) {
613
+ positionAttr = ` [position: ${Math.round(instance.config.x)} ${Math.round(instance.config.y)}]`;
614
+ }
615
+ return ` * @node ${instance.id} ${instance.nodeType}${parent}${labelAttr}${portOrderAttr}${portLabelAttr}${exprAttr}${pullExecutionAttr}${minimizedAttr}${colorAttr}${iconAttr}${tagsAttr}${sizeAttr}${positionAttr}`;
616
616
  }
617
617
  export const annotationGenerator = new AnnotationGenerator();
618
618
  //# sourceMappingURL=annotation-generator.js.map
@@ -1119,17 +1119,36 @@ function generateWorkflowJSDoc(ast, options = {}) {
1119
1119
  }
1120
1120
  lines.push(` * @fwImport ${npmType.name} ${actualFunctionName} from "${npmType.importSource}"`);
1121
1121
  }
1122
- // Add node instances skip synthetic MAP_ITERATOR instances, strip parent from macro children
1122
+ // Auto-position: compute default positions for nodes without explicit positions.
1123
+ // Must happen before instance tags are generated so [position:] can be emitted.
1124
+ const autoPositions = computeAutoPositions(ast);
1125
+ // Add node instances — skip synthetic MAP_ITERATOR instances, strip parent from macro children.
1126
+ // Merge auto-computed positions into instance config (without mutating the AST).
1123
1127
  for (const instance of ast.instances) {
1124
1128
  if (macroInstanceIds.has(instance.id))
1125
1129
  continue;
1126
- if (macroChildIds.has(instance.id) && instance.parent) {
1130
+ // Merge auto-position into config if not already set
1131
+ let inst = instance;
1132
+ if (inst.config?.x === undefined || inst.config?.y === undefined) {
1133
+ const autoPos = autoPositions.get(inst.id);
1134
+ if (autoPos) {
1135
+ inst = {
1136
+ ...inst,
1137
+ config: {
1138
+ ...inst.config,
1139
+ x: inst.config?.x ?? autoPos.x,
1140
+ y: inst.config?.y ?? autoPos.y,
1141
+ },
1142
+ };
1143
+ }
1144
+ }
1145
+ if (macroChildIds.has(inst.id) && inst.parent) {
1127
1146
  // Write child @node without parent scope — @map handles it
1128
- const stripped = { ...instance, parent: undefined };
1147
+ const stripped = { ...inst, parent: undefined };
1129
1148
  lines.push(generateNodeInstanceTag(stripped));
1130
1149
  }
1131
1150
  else {
1132
- lines.push(generateNodeInstanceTag(instance));
1151
+ lines.push(generateNodeInstanceTag(inst));
1133
1152
  }
1134
1153
  }
1135
1154
  // Filter stale macros (e.g. paths whose connections were deleted)
@@ -1168,27 +1187,13 @@ function generateWorkflowJSDoc(ast, options = {}) {
1168
1187
  }
1169
1188
  }
1170
1189
  }
1171
- // Auto-position: compute default positions for nodes without explicit positions.
1172
- // Uses a left-to-right layout with topological ordering when connections are available.
1173
- const autoPositions = computeAutoPositions(ast);
1174
- // Add positions - Start node
1190
+ // Add positions - Start node (virtual, standalone @position)
1175
1191
  const startX = ast.ui?.startNode?.x ?? autoPositions.get('Start')?.x;
1176
1192
  const startY = ast.ui?.startNode?.y ?? autoPositions.get('Start')?.y;
1177
1193
  if (startX !== undefined && startY !== undefined) {
1178
1194
  lines.push(` * @position Start ${Math.round(startX)} ${Math.round(startY)}`);
1179
1195
  }
1180
- // Add positions - instances (use explicit position if available, otherwise auto-computed)
1181
- for (const instance of ast.instances) {
1182
- const explicitX = instance.config?.x;
1183
- const explicitY = instance.config?.y;
1184
- const autoPos = autoPositions.get(instance.id);
1185
- const x = explicitX ?? autoPos?.x;
1186
- const y = explicitY ?? autoPos?.y;
1187
- if (x !== undefined && y !== undefined) {
1188
- lines.push(` * @position ${instance.id} ${Math.round(x)} ${Math.round(y)}`);
1189
- }
1190
- }
1191
- // Add positions - Exit node
1196
+ // Add positions - Exit node (virtual, standalone @position)
1192
1197
  const exitX = ast.ui?.exitNode?.x ?? autoPositions.get('Exit')?.x;
1193
1198
  const exitY = ast.ui?.exitNode?.y ?? autoPositions.get('Exit')?.y;
1194
1199
  if (exitX !== undefined && exitY !== undefined) {
@@ -48,8 +48,13 @@ export function applyPattern(options) {
48
48
  conflicts.push(nodeType.name);
49
49
  }
50
50
  }
51
- // ── Build @node declarations ────────────────────────────────────────
52
- const nodeDeclarations = pattern.instances.map((inst) => ` * @node ${nodePrefix}${inst.id} ${inst.nodeType}`);
51
+ // ── Build @node declarations (with inline [position:] when present) ──
52
+ const nodeDeclarations = pattern.instances.map((inst) => {
53
+ const posAttr = inst.config?.x !== undefined && inst.config?.y !== undefined
54
+ ? ` [position: ${inst.config.x} ${inst.config.y}]`
55
+ : '';
56
+ return ` * @node ${nodePrefix}${inst.id} ${inst.nodeType}${posAttr}`;
57
+ });
53
58
  // ── Build @connect declarations + wiring instructions ───────────────
54
59
  const connectDeclarations = [];
55
60
  const wiringInstructions = [];
@@ -85,10 +90,6 @@ export function applyPattern(options) {
85
90
  connectDeclarations.push(` * @connect ${fromNode}.${conn.from.port} -> ${toNode}.${conn.to.port}`);
86
91
  }
87
92
  }
88
- // ── Build @position declarations ────────────────────────────────────
89
- const positionDeclarations = pattern.instances
90
- .filter((inst) => inst.config?.x !== undefined && inst.config?.y !== undefined)
91
- .map((inst) => ` * @position ${nodePrefix}${inst.id} ${inst.config.x} ${inst.config.y}`);
92
93
  // ── Generate node type functions (only non-conflicting) ─────────────
93
94
  const nodeTypesAdded = [];
94
95
  const nodeTypeFunctions = [];
@@ -103,7 +104,6 @@ export function applyPattern(options) {
103
104
  `// --- Pattern: ${pattern.name} ${prefix ? `(prefix: ${prefix})` : ''} ---`,
104
105
  ...nodeDeclarations,
105
106
  ...connectDeclarations,
106
- ...positionDeclarations,
107
107
  ];
108
108
  // ── Insert into target content ──────────────────────────────────────
109
109
  const workflowMatch = targetContent.match(/\/\*\*[\s\S]*?@flowWeaver\s+workflow[\s\S]*?\*\//);
@@ -250,9 +250,12 @@ export function extractPattern(options) {
250
250
  lines.push('/**');
251
251
  lines.push(` * @flowWeaver pattern`);
252
252
  lines.push(` * @name ${patternName}`);
253
- // Node declarations
253
+ // Node declarations (with inline [position:] when present)
254
254
  for (const inst of extractedInstances) {
255
- lines.push(` * @node ${inst.id} ${inst.nodeType}`);
255
+ const posAttr = inst.config?.x !== undefined && inst.config?.y !== undefined
256
+ ? ` [position: ${inst.config.x} ${inst.config.y}]`
257
+ : '';
258
+ lines.push(` * @node ${inst.id} ${inst.nodeType}${posAttr}`);
256
259
  }
257
260
  // Internal connections
258
261
  for (const conn of internalConnections) {
@@ -280,12 +283,6 @@ export function extractPattern(options) {
280
283
  for (const port of [...new Set(outputPorts)]) {
281
284
  lines.push(` * @port OUT.${port}`);
282
285
  }
283
- // Positions
284
- for (const inst of extractedInstances) {
285
- if (inst.config?.x !== undefined && inst.config?.y !== undefined) {
286
- lines.push(` * @position ${inst.id} ${inst.config.x} ${inst.config.y}`);
287
- }
288
- }
289
286
  lines.push(' */');
290
287
  lines.push('function patternPlaceholder() {}');
291
288
  // Add node type functions
@@ -15,6 +15,10 @@ export async function waitForAgent(execute, agentId, context, prompt) {
15
15
  if (mockResult !== undefined) {
16
16
  return { onSuccess: true, onFailure: false, agentResult: mockResult };
17
17
  }
18
+ // Mocks section exists but key not found — fail like waitForEvent/invokeWorkflow
19
+ if (mocks?.agents) {
20
+ return { onSuccess: false, onFailure: true, agentResult: {} };
21
+ }
18
22
  // 2. Check agent channel (set by executor for pause/resume)
19
23
  const channel = globalThis.__fw_agent_channel__;
20
24
  if (channel) {
@@ -17,6 +17,10 @@ export interface NodeParseResult {
17
17
  width: number;
18
18
  height: number;
19
19
  };
20
+ position?: {
21
+ x: number;
22
+ y: number;
23
+ };
20
24
  color?: string;
21
25
  icon?: string;
22
26
  tags?: Array<{
@@ -4,7 +4,7 @@
4
4
  * Parser for @node declarations using Chevrotain.
5
5
  */
6
6
  import { CstParser } from 'chevrotain';
7
- import { JSDocLexer, NodeTag, Identifier, Dot, Integer, LabelPrefix, ExprPrefix, PortOrderPrefix, PortLabelPrefix, MinimizedKeyword, PullExecutionPrefix, SizePrefix, ColorPrefix, IconPrefix, TagsPrefix, StringLiteral, LBracket, RBracket, Comma, Equals, EventEq, CronEq, MatchEq, TimeoutEq, LimitEq, PeriodEq, allTokens, } from './tokens.js';
7
+ import { JSDocLexer, NodeTag, Identifier, Dot, Integer, LabelPrefix, ExprPrefix, PortOrderPrefix, PortLabelPrefix, MinimizedKeyword, PullExecutionPrefix, SizePrefix, PositionPrefix, ColorPrefix, IconPrefix, TagsPrefix, StringLiteral, LBracket, RBracket, Comma, Equals, EventEq, CronEq, MatchEq, TimeoutEq, LimitEq, PeriodEq, allTokens, } from './tokens.js';
8
8
  // =============================================================================
9
9
  // Parser Definition
10
10
  // =============================================================================
@@ -48,6 +48,7 @@ class NodeParser extends CstParser {
48
48
  { ALT: () => this.SUBRULE(this.minimizedAttr) },
49
49
  { ALT: () => this.SUBRULE(this.pullExecutionAttr) },
50
50
  { ALT: () => this.SUBRULE(this.sizeAttr) },
51
+ { ALT: () => this.SUBRULE(this.positionAttr) },
51
52
  { ALT: () => this.SUBRULE(this.colorAttr) },
52
53
  { ALT: () => this.SUBRULE(this.iconAttr) },
53
54
  { ALT: () => this.SUBRULE(this.tagsAttr) },
@@ -140,6 +141,12 @@ class NodeParser extends CstParser {
140
141
  this.CONSUME(Integer, { LABEL: 'widthValue' });
141
142
  this.CONSUME2(Integer, { LABEL: 'heightValue' });
142
143
  });
144
+ // position: x y
145
+ positionAttr = this.RULE('positionAttr', () => {
146
+ this.CONSUME(PositionPrefix);
147
+ this.CONSUME(Integer, { LABEL: 'xValue' });
148
+ this.CONSUME2(Integer, { LABEL: 'yValue' });
149
+ });
143
150
  // color: "value"
144
151
  colorAttr = this.RULE('colorAttr', () => {
145
152
  this.CONSUME(ColorPrefix);
@@ -192,6 +199,7 @@ class NodeVisitor extends BaseVisitor {
192
199
  let minimized;
193
200
  let pullExecution;
194
201
  let size;
202
+ let position;
195
203
  let color;
196
204
  let icon;
197
205
  let tags;
@@ -215,6 +223,8 @@ class NodeVisitor extends BaseVisitor {
215
223
  pullExecution = attrs.pullExecution;
216
224
  if (attrs.size)
217
225
  size = attrs.size;
226
+ if (attrs.position)
227
+ position = attrs.position;
218
228
  if (attrs.color)
219
229
  color = attrs.color;
220
230
  if (attrs.icon)
@@ -234,6 +244,7 @@ class NodeVisitor extends BaseVisitor {
234
244
  ...(minimized && { minimized }),
235
245
  ...(pullExecution && { pullExecution }),
236
246
  ...(size && { size }),
247
+ ...(position && { position }),
237
248
  ...(color && { color }),
238
249
  ...(icon && { icon }),
239
250
  ...(tags && { tags }),
@@ -252,6 +263,7 @@ class NodeVisitor extends BaseVisitor {
252
263
  let minimized;
253
264
  let pullExecution;
254
265
  let size;
266
+ let position;
255
267
  let color;
256
268
  let icon;
257
269
  let tags;
@@ -291,6 +303,11 @@ class NodeVisitor extends BaseVisitor {
291
303
  size = this.visit(attr);
292
304
  }
293
305
  }
306
+ if (ctx.positionAttr) {
307
+ for (const attr of ctx.positionAttr) {
308
+ position = this.visit(attr);
309
+ }
310
+ }
294
311
  if (ctx.colorAttr) {
295
312
  for (const attr of ctx.colorAttr) {
296
313
  color = this.visit(attr);
@@ -315,6 +332,7 @@ class NodeVisitor extends BaseVisitor {
315
332
  minimized,
316
333
  pullExecution,
317
334
  size,
335
+ position,
318
336
  color,
319
337
  icon,
320
338
  tags,
@@ -397,6 +415,11 @@ class NodeVisitor extends BaseVisitor {
397
415
  const height = parseInt(ctx.heightValue[0].image, 10);
398
416
  return { width, height };
399
417
  }
418
+ positionAttr(ctx) {
419
+ const x = parseInt(ctx.xValue[0].image, 10);
420
+ const y = parseInt(ctx.yValue[0].image, 10);
421
+ return { x, y };
422
+ }
400
423
  colorAttr(ctx) {
401
424
  return this.unescapeString(ctx.colorValue[0].image);
402
425
  }
@@ -44,6 +44,7 @@ export declare const MergeStrategyPrefix: import("chevrotain").TokenType;
44
44
  export declare const PullExecutionPrefix: import("chevrotain").TokenType;
45
45
  export declare const MinimizedKeyword: import("chevrotain").TokenType;
46
46
  export declare const SizePrefix: import("chevrotain").TokenType;
47
+ export declare const PositionPrefix: import("chevrotain").TokenType;
47
48
  export declare const ColorPrefix: import("chevrotain").TokenType;
48
49
  export declare const IconPrefix: import("chevrotain").TokenType;
49
50
  export declare const TagsPrefix: import("chevrotain").TokenType;
@@ -172,6 +172,10 @@ export const SizePrefix = createToken({
172
172
  name: 'SizePrefix',
173
173
  pattern: /size:/,
174
174
  });
175
+ export const PositionPrefix = createToken({
176
+ name: 'PositionPrefix',
177
+ pattern: /position:/,
178
+ });
175
179
  export const ColorPrefix = createToken({
176
180
  name: 'ColorPrefix',
177
181
  pattern: /color:/,
@@ -356,6 +360,7 @@ export const allTokens = [
356
360
  MergeStrategyPrefix,
357
361
  PullExecutionPrefix,
358
362
  SizePrefix,
363
+ PositionPrefix,
359
364
  ColorPrefix,
360
365
  IconPrefix,
361
366
  TagsPrefix,