@synergenius/flow-weaver 0.10.8 → 0.10.9

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.
@@ -47472,26 +47472,44 @@ ${fn.getText()}` : fn.getText();
47472
47472
  ports.execute = { dataType: "STEP", tsType: "boolean", label: "Execute" };
47473
47473
  }
47474
47474
  if (params.length > 1) {
47475
- const dataParam = params[1];
47476
- const dataParamType = dataParam.getType();
47477
- const properties = dataParamType.getProperties();
47478
- properties.forEach((prop) => {
47479
- const propName = prop.getName();
47480
- const propType = prop.getTypeAtLocation(dataParam);
47481
- const portType = this.inferPortType(propType);
47482
- const propTypeText = propType.getText();
47483
- const tsSchema = portType === "OBJECT" ? this.extractTypeSchema(propType) : void 0;
47484
- const startPortConfig = config2?.startPorts?.[propName];
47485
- ports[propName] = {
47486
- dataType: portType,
47487
- label: startPortConfig?.label || this.capitalize(propName),
47488
- ...startPortConfig?.metadata && { metadata: startPortConfig.metadata },
47489
- // Include original TS type for rich type display
47490
- ...propTypeText && { tsType: propTypeText },
47491
- // Include schema breakdown for complex types
47492
- ...tsSchema && Object.keys(tsSchema).length > 0 && { tsSchema }
47493
- };
47494
- });
47475
+ const dataParams = params.slice(1).filter((p) => p.getName() !== "__abortSignal__");
47476
+ const shouldExpandProperties = dataParams.length === 1 && this.isExpandableObjectType(dataParams[0].getType());
47477
+ if (shouldExpandProperties) {
47478
+ const dataParam = dataParams[0];
47479
+ const dataParamType = dataParam.getType();
47480
+ const properties = dataParamType.getProperties();
47481
+ properties.forEach((prop) => {
47482
+ const propName = prop.getName();
47483
+ const propType = prop.getTypeAtLocation(dataParam);
47484
+ const portType = this.inferPortType(propType);
47485
+ const propTypeText = propType.getText();
47486
+ const tsSchema = portType === "OBJECT" ? this.extractTypeSchema(propType) : void 0;
47487
+ const startPortConfig = config2?.startPorts?.[propName];
47488
+ ports[propName] = {
47489
+ dataType: portType,
47490
+ label: startPortConfig?.label || this.capitalize(propName),
47491
+ ...startPortConfig?.metadata && { metadata: startPortConfig.metadata },
47492
+ ...propTypeText && { tsType: propTypeText },
47493
+ ...tsSchema && Object.keys(tsSchema).length > 0 && { tsSchema }
47494
+ };
47495
+ });
47496
+ } else {
47497
+ for (const param of dataParams) {
47498
+ const paramName = param.getName();
47499
+ const paramType = param.getType();
47500
+ const portType = this.inferPortType(paramType);
47501
+ const paramTypeText = paramType.getText(param);
47502
+ const tsSchema = portType === "OBJECT" ? this.extractTypeSchema(paramType) : void 0;
47503
+ const startPortConfig = config2?.startPorts?.[paramName];
47504
+ ports[paramName] = {
47505
+ dataType: portType,
47506
+ label: startPortConfig?.label || this.capitalize(paramName),
47507
+ ...startPortConfig?.metadata && { metadata: startPortConfig.metadata },
47508
+ ...paramTypeText && { tsType: paramTypeText },
47509
+ ...tsSchema && Object.keys(tsSchema).length > 0 && { tsSchema }
47510
+ };
47511
+ }
47512
+ }
47495
47513
  }
47496
47514
  } else {
47497
47515
  throw new Error(
@@ -47578,6 +47596,18 @@ ${fn.getText()}` : fn.getText();
47578
47596
  }
47579
47597
  return Object.keys(schema2).length > 0 ? schema2 : void 0;
47580
47598
  }
47599
+ /**
47600
+ * Check if a type should be expanded into individual ports via getProperties().
47601
+ * Returns true for object literals and interfaces, false for primitives, arrays,
47602
+ * and built-in types whose properties are prototype methods (string, number, etc.).
47603
+ */
47604
+ isExpandableObjectType(tsType) {
47605
+ const typeText = tsType.getText();
47606
+ const primitiveTypes2 = /* @__PURE__ */ new Set(["string", "number", "boolean", "any", "unknown", "never", "object", "Object"]);
47607
+ if (primitiveTypes2.has(typeText)) return false;
47608
+ if (typeText.endsWith("[]") || typeText.startsWith("Array<")) return false;
47609
+ return tsType.isObject() && tsType.getProperties().length > 0;
47610
+ }
47581
47611
  inferPortType(tsType) {
47582
47612
  const typeText = tsType.getText();
47583
47613
  return inferDataTypeFromTS(typeText);
@@ -59279,331 +59309,6 @@ async function createNodeCommand(name, file, options = {}) {
59279
59309
  import * as fs11 from "fs";
59280
59310
  import * as path11 from "path";
59281
59311
  init_validator();
59282
- function buildNodeInfo(instance, nodeType) {
59283
- return {
59284
- id: instance.id,
59285
- type: instance.nodeType,
59286
- inputs: nodeType ? Object.keys(nodeType.inputs) : [],
59287
- outputs: nodeType ? Object.keys(nodeType.outputs) : []
59288
- };
59289
- }
59290
- function buildAdjacency(ast) {
59291
- const fromStart = [];
59292
- const toExit = /* @__PURE__ */ new Set();
59293
- const edges = /* @__PURE__ */ new Map();
59294
- ast.connections.forEach((conn) => {
59295
- if (conn.from.node === "Start") {
59296
- if (!fromStart.includes(conn.to.node)) {
59297
- fromStart.push(conn.to.node);
59298
- }
59299
- } else if (conn.to.node === "Exit") {
59300
- toExit.add(conn.from.node);
59301
- } else {
59302
- const targets = edges.get(conn.from.node) || [];
59303
- if (!targets.includes(conn.to.node)) {
59304
- targets.push(conn.to.node);
59305
- }
59306
- edges.set(conn.from.node, targets);
59307
- }
59308
- });
59309
- return { fromStart, toExit, edges };
59310
- }
59311
- function enumeratePaths(ast) {
59312
- const { fromStart, toExit, edges } = buildAdjacency(ast);
59313
- const paths = [];
59314
- function dfs(current2, path42, visited) {
59315
- if (toExit.has(current2)) {
59316
- paths.push([...path42, "Exit"]);
59317
- }
59318
- const targets = edges.get(current2) || [];
59319
- for (const next of targets) {
59320
- if (!visited.has(next)) {
59321
- visited.add(next);
59322
- path42.push(next);
59323
- dfs(next, path42, visited);
59324
- path42.pop();
59325
- visited.delete(next);
59326
- }
59327
- }
59328
- }
59329
- fromStart.forEach((startNode) => {
59330
- const visited = /* @__PURE__ */ new Set([startNode]);
59331
- dfs(startNode, ["Start", startNode], visited);
59332
- });
59333
- return paths;
59334
- }
59335
- function buildGraph(ast) {
59336
- const { fromStart, toExit, edges } = buildAdjacency(ast);
59337
- const lines = [];
59338
- function dfs(current2, path42, visited) {
59339
- if (toExit.has(current2)) {
59340
- lines.push([...path42, "Exit"].join(" -> "));
59341
- }
59342
- const targets = edges.get(current2) || [];
59343
- for (const next of targets) {
59344
- if (!visited.has(next)) {
59345
- visited.add(next);
59346
- path42.push(next);
59347
- dfs(next, path42, visited);
59348
- path42.pop();
59349
- visited.delete(next);
59350
- }
59351
- }
59352
- if (targets.length === 0 && !toExit.has(current2)) {
59353
- lines.push(path42.join(" -> "));
59354
- }
59355
- }
59356
- fromStart.forEach((startNode) => {
59357
- const visited = /* @__PURE__ */ new Set([startNode]);
59358
- dfs(startNode, ["Start", startNode], visited);
59359
- });
59360
- return lines.join("\n");
59361
- }
59362
- function formatPaths(ast) {
59363
- const paths = enumeratePaths(ast);
59364
- if (paths.length === 0) return "(no complete Start-to-Exit paths found)";
59365
- return paths.map((p) => p.join(" -> ")).join("\n");
59366
- }
59367
- function generateMermaid(ast) {
59368
- const lines = ["graph LR"];
59369
- ast.instances.forEach((instance) => {
59370
- lines.push(` ${instance.id}[${instance.id}: ${instance.nodeType}]`);
59371
- });
59372
- const seenEdges = /* @__PURE__ */ new Set();
59373
- ast.connections.forEach((conn) => {
59374
- const edgeKey = `${conn.from.node}->${conn.to.node}`;
59375
- if (!seenEdges.has(edgeKey)) {
59376
- seenEdges.add(edgeKey);
59377
- const from = conn.from.node === "Start" ? "Start((Start))" : conn.from.node;
59378
- const to = conn.to.node === "Exit" ? "Exit((Exit))" : conn.to.node;
59379
- lines.push(` ${from} --> ${to}`);
59380
- }
59381
- });
59382
- return lines.join("\n");
59383
- }
59384
- function describeWorkflow(ast, options = {}) {
59385
- const { node: focusNodeId } = options;
59386
- const validation = validator.validate(ast);
59387
- const validationOutput = {
59388
- valid: validation.valid,
59389
- errors: validation.errors.map((e) => e.message),
59390
- warnings: validation.warnings.map((w) => w.message)
59391
- };
59392
- const nodeTypeMap = /* @__PURE__ */ new Map();
59393
- ast.nodeTypes.forEach((nt) => nodeTypeMap.set(nt.functionName, nt));
59394
- if (focusNodeId) {
59395
- const nodeInstance = getNode(ast, focusNodeId);
59396
- if (!nodeInstance) {
59397
- throw new Error(`Node not found: ${focusNodeId}`);
59398
- }
59399
- const nodeType = nodeTypeMap.get(nodeInstance.nodeType);
59400
- const incoming = getIncomingConnections(ast, focusNodeId).map((c) => ({
59401
- from: `${c.from.node}.${c.from.port}`,
59402
- to: `${c.to.node}.${c.to.port}`
59403
- }));
59404
- const outgoing = getOutgoingConnections(ast, focusNodeId).map((c) => ({
59405
- from: `${c.from.node}.${c.from.port}`,
59406
- to: `${c.to.node}.${c.to.port}`
59407
- }));
59408
- return {
59409
- focusNode: focusNodeId,
59410
- node: buildNodeInfo(nodeInstance, nodeType),
59411
- incoming,
59412
- outgoing,
59413
- validation: validationOutput
59414
- };
59415
- }
59416
- const nodes = ast.instances.map((instance) => {
59417
- const nodeType = nodeTypeMap.get(instance.nodeType);
59418
- return buildNodeInfo(instance, nodeType);
59419
- });
59420
- const connections = ast.connections.map((c) => ({
59421
- from: `${c.from.node}.${c.from.port}`,
59422
- to: `${c.to.node}.${c.to.port}`
59423
- }));
59424
- return {
59425
- name: ast.functionName,
59426
- description: ast.description || null,
59427
- nodes,
59428
- connections,
59429
- graph: buildGraph(ast),
59430
- validation: validationOutput
59431
- };
59432
- }
59433
- function formatTextOutput(ast, output) {
59434
- if ("focusNode" in output) {
59435
- const focused = output;
59436
- const lines2 = [];
59437
- lines2.push(`Node: ${focused.node.id} [${focused.node.type}]`);
59438
- lines2.push(` Inputs: ${focused.node.inputs.join(", ") || "(none)"}`);
59439
- lines2.push(` Outputs: ${focused.node.outputs.join(", ") || "(none)"}`);
59440
- if (focused.incoming.length > 0) {
59441
- lines2.push("");
59442
- lines2.push("Incoming:");
59443
- focused.incoming.forEach((c) => lines2.push(` ${c.from} -> ${c.to}`));
59444
- }
59445
- if (focused.outgoing.length > 0) {
59446
- lines2.push("");
59447
- lines2.push("Outgoing:");
59448
- focused.outgoing.forEach((c) => lines2.push(` ${c.from} -> ${c.to}`));
59449
- }
59450
- return lines2.join("\n");
59451
- }
59452
- const desc = output;
59453
- const lines = [];
59454
- lines.push(`Workflow: ${desc.name}`);
59455
- if (desc.description) {
59456
- lines.push(` ${desc.description}`);
59457
- }
59458
- const nodeTypeMap = /* @__PURE__ */ new Map();
59459
- ast.nodeTypes.forEach((nt) => nodeTypeMap.set(nt.functionName, nt));
59460
- lines.push("");
59461
- lines.push(`Nodes (${desc.nodes.length}):`);
59462
- const maxIdLen = Math.max(...desc.nodes.map((n) => n.id.length), 2);
59463
- const maxTypeLen = Math.max(...desc.nodes.map((n) => n.type.length), 2);
59464
- desc.nodes.forEach((node) => {
59465
- const id = node.id.padEnd(maxIdLen);
59466
- const type2 = `[${node.type}]`.padEnd(maxTypeLen + 2);
59467
- const nodeType = nodeTypeMap.get(node.type);
59468
- const isExpression = nodeType?.expression === true;
59469
- const scopeNames = nodeType?.scopes ?? (nodeType?.scope ? [nodeType.scope] : []);
59470
- const filterPorts = (ports, direction) => {
59471
- if (!isExpression || !nodeType) return ports;
59472
- return ports.filter((portName) => {
59473
- if (portName === "execute" || portName === "onSuccess" || portName === "onFailure") {
59474
- return false;
59475
- }
59476
- const portDef = direction === "input" ? nodeType.inputs[portName] : nodeType.outputs[portName];
59477
- return !(portDef?.isControlFlow || portDef?.dataType === "STEP");
59478
- });
59479
- };
59480
- if (scopeNames.length > 0 && nodeType) {
59481
- const regularInputs = [];
59482
- const regularOutputs = [];
59483
- const scopedPorts = {};
59484
- scopeNames.forEach((s) => {
59485
- scopedPorts[s] = { sends: [], receives: [], children: [] };
59486
- });
59487
- Object.entries(nodeType.inputs).forEach(([name, port]) => {
59488
- if (port.scope && scopedPorts[port.scope]) {
59489
- scopedPorts[port.scope].receives.push(name + (port.isControlFlow ? " (STEP)" : ""));
59490
- } else {
59491
- regularInputs.push(name);
59492
- }
59493
- });
59494
- Object.entries(nodeType.outputs).forEach(([name, port]) => {
59495
- if (port.scope && scopedPorts[port.scope]) {
59496
- scopedPorts[port.scope].sends.push(name + (port.isControlFlow ? " (STEP)" : ""));
59497
- } else {
59498
- regularOutputs.push(name);
59499
- }
59500
- });
59501
- if (ast.scopes) {
59502
- for (const [scopeKey, childIds] of Object.entries(ast.scopes)) {
59503
- const dotIdx = scopeKey.indexOf(".");
59504
- const scopeName = dotIdx >= 0 ? scopeKey.substring(dotIdx + 1) : scopeKey;
59505
- if (scopedPorts[scopeName]) {
59506
- scopedPorts[scopeName].children.push(...childIds);
59507
- }
59508
- }
59509
- }
59510
- const ins = regularInputs.length > 0 ? `IN: ${regularInputs.join(", ")}` : "";
59511
- const outs = regularOutputs.length > 0 ? `OUT: ${regularOutputs.join(", ")}` : "";
59512
- const ports = [ins, outs].filter(Boolean).join(" ");
59513
- lines.push(` ${id} ${type2} ${ports}`);
59514
- for (const [scopeName, info] of Object.entries(scopedPorts)) {
59515
- lines.push(` Scope "${scopeName}":`);
59516
- if (info.sends.length > 0) {
59517
- lines.push(` Sends to children: ${info.sends.join(", ")}`);
59518
- }
59519
- if (info.receives.length > 0) {
59520
- lines.push(` Receives from children: ${info.receives.join(", ")}`);
59521
- }
59522
- if (info.children.length > 0) {
59523
- lines.push(` Children: ${info.children.join(", ")}`);
59524
- }
59525
- }
59526
- } else {
59527
- const filteredInputs = filterPorts(node.inputs, "input");
59528
- const filteredOutputs = filterPorts(node.outputs, "output");
59529
- const ins = filteredInputs.length > 0 ? `IN: ${filteredInputs.join(", ")}` : "";
59530
- const outs = filteredOutputs.length > 0 ? `OUT: ${filteredOutputs.join(", ")}` : "";
59531
- const ports = [ins, outs].filter(Boolean).join(" ");
59532
- lines.push(` ${id} ${type2} ${ports}`);
59533
- }
59534
- });
59535
- if (desc.graph) {
59536
- lines.push("");
59537
- const paths = enumeratePaths(ast);
59538
- if (paths.length > 1) {
59539
- lines.push(`Paths (${paths.length}):`);
59540
- paths.forEach((p, i) => {
59541
- lines.push(` Path ${i + 1}: ${p.join(" -> ")}`);
59542
- });
59543
- } else {
59544
- lines.push("Flow:");
59545
- desc.graph.split("\n").forEach((flowLine) => {
59546
- lines.push(` ${flowLine}`);
59547
- });
59548
- }
59549
- }
59550
- lines.push("");
59551
- lines.push(
59552
- `Validation: ${desc.validation.errors.length} errors, ${desc.validation.warnings.length} warnings`
59553
- );
59554
- return lines.join("\n");
59555
- }
59556
- function formatDescribeOutput(ast, output, format) {
59557
- switch (format) {
59558
- case "text":
59559
- return formatTextOutput(ast, output);
59560
- case "mermaid":
59561
- return generateMermaid(ast);
59562
- case "paths":
59563
- return formatPaths(ast);
59564
- case "json":
59565
- default:
59566
- return JSON.stringify(output, null, 2);
59567
- }
59568
- }
59569
- async function describeCommand(input, options = {}) {
59570
- const { format = "json", node: focusNodeId, workflowName, compile = false } = options;
59571
- const filePath = path11.resolve(input);
59572
- if (!fs11.existsSync(filePath)) {
59573
- logger.error(`File not found: ${filePath}`);
59574
- process.exit(1);
59575
- }
59576
- try {
59577
- const parseResult = await parseWorkflow(filePath, { workflowName });
59578
- if (parseResult.errors.length > 0) {
59579
- logger.error(`Parse errors:`);
59580
- parseResult.errors.forEach((err) => logger.error(` ${err}`));
59581
- process.exit(1);
59582
- }
59583
- const ast = parseResult.ast;
59584
- if (compile) {
59585
- const sourceCode = fs11.readFileSync(filePath, "utf8");
59586
- const generated = generateInPlace(sourceCode, ast, { production: false });
59587
- if (generated.hasChanges) {
59588
- fs11.writeFileSync(filePath, generated.code, "utf8");
59589
- logger.info(`Updated runtime markers in ${path11.basename(filePath)}`);
59590
- }
59591
- }
59592
- const output = describeWorkflow(ast, { node: focusNodeId });
59593
- console.log(formatDescribeOutput(ast, output, format));
59594
- } catch (error2) {
59595
- if (error2 instanceof Error && error2.message.startsWith("Node not found:")) {
59596
- logger.error(error2.message);
59597
- process.exit(1);
59598
- }
59599
- logger.error(`Failed to describe workflow: ${getErrorMessage(error2)}`);
59600
- process.exit(1);
59601
- }
59602
- }
59603
-
59604
- // src/cli/commands/diagram.ts
59605
- import * as fs12 from "fs";
59606
- import * as path12 from "path";
59607
59312
 
59608
59313
  // src/diagram/geometry.ts
59609
59314
  init_constants();
@@ -61257,6 +60962,772 @@ function maxPortLabelExtent(ports) {
61257
60962
  return max;
61258
60963
  }
61259
60964
 
60965
+ // src/diagram/ascii-renderer.ts
60966
+ function groupByLayer(nodes) {
60967
+ if (nodes.length === 0) return [];
60968
+ const sorted = [...nodes].sort((a, b) => a.x - b.x);
60969
+ const layers = [];
60970
+ let currentX = sorted[0].x;
60971
+ let currentLayer = [];
60972
+ for (const node of sorted) {
60973
+ if (Math.abs(node.x - currentX) > 10) {
60974
+ layers.push(currentLayer);
60975
+ currentLayer = [];
60976
+ currentX = node.x;
60977
+ }
60978
+ currentLayer.push(node);
60979
+ }
60980
+ if (currentLayer.length > 0) layers.push(currentLayer);
60981
+ return layers;
60982
+ }
60983
+ function buildConnectedPorts(connections) {
60984
+ const s = /* @__PURE__ */ new Set();
60985
+ for (const c of connections) {
60986
+ s.add(`${c.fromNode}.${c.fromPort}`);
60987
+ s.add(`${c.toNode}.${c.toPort}`);
60988
+ }
60989
+ return s;
60990
+ }
60991
+ function portSymbol(nodeId, port, connected) {
60992
+ return connected.has(`${nodeId}.${port.name}`) ? "\u25CF" : "\u25CB";
60993
+ }
60994
+ function cornerChar(isStep, hDir, vDir) {
60995
+ if (hDir === "right" && vDir === "down") return isStep ? "\u2554" : "\u250C";
60996
+ if (hDir === "left" && vDir === "down") return isStep ? "\u2557" : "\u2510";
60997
+ if (hDir === "right" && vDir === "up") return isStep ? "\u255A" : "\u2514";
60998
+ if (hDir === "left" && vDir === "up") return isStep ? "\u255D" : "\u2518";
60999
+ return "\u253C";
61000
+ }
61001
+ var H_CHARS = new Set("\u2500\u2550");
61002
+ var V_CHARS = new Set("\u2502\u2551");
61003
+ var CharGrid = class {
61004
+ cells;
61005
+ width;
61006
+ height;
61007
+ constructor(w, h) {
61008
+ this.width = w;
61009
+ this.height = h;
61010
+ this.cells = new Array(w * h).fill(" ");
61011
+ }
61012
+ set(x, y, ch) {
61013
+ if (x < 0 || x >= this.width || y < 0 || y >= this.height) return;
61014
+ const existing = this.cells[y * this.width + x];
61015
+ if (existing !== " ") {
61016
+ if (H_CHARS.has(ch) && V_CHARS.has(existing) || V_CHARS.has(ch) && H_CHARS.has(existing)) {
61017
+ this.cells[y * this.width + x] = "\u253C";
61018
+ return;
61019
+ }
61020
+ if (H_CHARS.has(ch) && H_CHARS.has(existing) || V_CHARS.has(ch) && V_CHARS.has(existing)) {
61021
+ return;
61022
+ }
61023
+ }
61024
+ this.cells[y * this.width + x] = ch;
61025
+ }
61026
+ get(x, y) {
61027
+ if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
61028
+ return this.cells[y * this.width + x];
61029
+ }
61030
+ return " ";
61031
+ }
61032
+ /** Force-write, ignoring collision logic (for boxes and text). */
61033
+ forceSet(x, y, ch) {
61034
+ if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
61035
+ this.cells[y * this.width + x] = ch;
61036
+ }
61037
+ }
61038
+ writeStr(x, y, s) {
61039
+ for (let i = 0; i < s.length; i++) {
61040
+ this.forceSet(x + i, y, s[i]);
61041
+ }
61042
+ }
61043
+ toLines() {
61044
+ const lines = [];
61045
+ for (let y = 0; y < this.height; y++) {
61046
+ let line = "";
61047
+ for (let x = 0; x < this.width; x++) {
61048
+ line += this.cells[y * this.width + x];
61049
+ }
61050
+ lines.push(line.trimEnd());
61051
+ }
61052
+ while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
61053
+ return lines;
61054
+ }
61055
+ };
61056
+ function mergePortRows(inputs, outputs) {
61057
+ const rows = [];
61058
+ const maxLen = Math.max(inputs.length, outputs.length);
61059
+ for (let i = 0; i < maxLen; i++) {
61060
+ rows.push({
61061
+ input: i < inputs.length ? inputs[i] : null,
61062
+ output: i < outputs.length ? outputs[i] : null
61063
+ });
61064
+ }
61065
+ return rows;
61066
+ }
61067
+ function measureBox(node) {
61068
+ const portRows = mergePortRows(node.inputs, node.outputs);
61069
+ const maxInputPortLen = node.inputs.length > 0 ? Math.max(...node.inputs.map((p) => p.name.length)) : 0;
61070
+ const maxOutputPortLen = node.outputs.length > 0 ? Math.max(...node.outputs.map((p) => p.name.length)) : 0;
61071
+ const portsContentWidth = (maxInputPortLen > 0 ? maxInputPortLen + 2 : 0) + 2 + (maxOutputPortLen > 0 ? maxOutputPortLen + 2 : 0);
61072
+ const innerWidth = Math.max(node.label.length + 2, portsContentWidth, 10);
61073
+ const boxWidth = innerWidth + 2;
61074
+ const headerRows = 3;
61075
+ const portRowCount = Math.max(portRows.length, 1);
61076
+ const boxHeight = headerRows + portRowCount + 1;
61077
+ const portRowOffsets = /* @__PURE__ */ new Map();
61078
+ for (let i = 0; i < portRows.length; i++) {
61079
+ if (portRows[i].input) portRowOffsets.set(portRows[i].input.name, headerRows + i);
61080
+ if (portRows[i].output) portRowOffsets.set(portRows[i].output.name, headerRows + i);
61081
+ }
61082
+ return { node, innerWidth, boxWidth, portRows, boxHeight, portRowOffsets };
61083
+ }
61084
+ function drawBox(grid, box, bx, by, connected) {
61085
+ const { innerWidth, portRows, node } = box;
61086
+ grid.forceSet(bx, by, "\u250C");
61087
+ for (let i = 0; i < innerWidth; i++) grid.forceSet(bx + 1 + i, by, "\u2500");
61088
+ grid.forceSet(bx + innerWidth + 1, by, "\u2510");
61089
+ const labelPad = innerWidth - node.label.length;
61090
+ const padL = Math.floor(labelPad / 2);
61091
+ grid.forceSet(bx, by + 1, "\u2502");
61092
+ grid.writeStr(bx + 1 + padL, by + 1, node.label);
61093
+ grid.forceSet(bx + innerWidth + 1, by + 1, "\u2502");
61094
+ grid.forceSet(bx, by + 2, "\u251C");
61095
+ for (let i = 0; i < innerWidth; i++) grid.forceSet(bx + 1 + i, by + 2, "\u2500");
61096
+ grid.forceSet(bx + innerWidth + 1, by + 2, "\u2524");
61097
+ for (let i = 0; i < Math.max(portRows.length, 1); i++) {
61098
+ const ry = by + 3 + i;
61099
+ grid.forceSet(bx, ry, "\u2502");
61100
+ grid.forceSet(bx + innerWidth + 1, ry, "\u2502");
61101
+ if (i < portRows.length) {
61102
+ const row = portRows[i];
61103
+ if (row.input) {
61104
+ grid.writeStr(bx + 1, ry, `${portSymbol(node.id, row.input, connected)} ${row.input.name}`);
61105
+ }
61106
+ if (row.output) {
61107
+ const txt = `${row.output.name} ${portSymbol(node.id, row.output, connected)}`;
61108
+ grid.writeStr(bx + innerWidth + 1 - txt.length, ry, txt);
61109
+ }
61110
+ }
61111
+ }
61112
+ const bottomY = by + box.boxHeight - 1;
61113
+ grid.forceSet(bx, bottomY, "\u2514");
61114
+ for (let i = 0; i < innerWidth; i++) grid.forceSet(bx + 1 + i, bottomY, "\u2500");
61115
+ grid.forceSet(bx + innerWidth + 1, bottomY, "\u2518");
61116
+ }
61117
+ function renderASCII(graph) {
61118
+ const layers = groupByLayer(graph.nodes);
61119
+ if (layers.length === 0) return `${graph.workflowName}
61120
+ (empty workflow)`;
61121
+ const layerBoxes = layers.map((layer) => layer.map(measureBox));
61122
+ const colWidths = layerBoxes.map((boxes) => Math.max(...boxes.map((b) => b.boxWidth)));
61123
+ const wireGap = 14;
61124
+ const colX = [];
61125
+ let cx = 1;
61126
+ for (let c = 0; c < layerBoxes.length; c++) {
61127
+ colX.push(cx);
61128
+ cx += colWidths[c] + wireGap;
61129
+ }
61130
+ const gridWidth = cx + 1;
61131
+ const nodeColMap = /* @__PURE__ */ new Map();
61132
+ for (let c = 0; c < layerBoxes.length; c++) {
61133
+ for (const box of layerBoxes[c]) nodeColMap.set(box.node.id, c);
61134
+ }
61135
+ let spanCount = 0;
61136
+ for (const conn of graph.connections) {
61137
+ const fc = nodeColMap.get(conn.fromNode);
61138
+ const tc = nodeColMap.get(conn.toNode);
61139
+ if (fc !== void 0 && tc !== void 0 && Math.abs(tc - fc) > 1) spanCount++;
61140
+ }
61141
+ const highwayMargin = spanCount > 0 ? spanCount + 1 : 0;
61142
+ const nodeGapY = 2;
61143
+ const boxPositions = /* @__PURE__ */ new Map();
61144
+ const boxStartY = 3 + highwayMargin;
61145
+ let maxGridHeight = 0;
61146
+ for (let c = 0; c < layerBoxes.length; c++) {
61147
+ let y = boxStartY;
61148
+ for (const box of layerBoxes[c]) {
61149
+ boxPositions.set(box.node.id, { col: c, bx: colX[c], by: y, box });
61150
+ y += box.boxHeight + nodeGapY;
61151
+ }
61152
+ if (y > maxGridHeight) maxGridHeight = y;
61153
+ }
61154
+ maxGridHeight += highwayMargin + 3;
61155
+ const connected = buildConnectedPorts(graph.connections);
61156
+ const grid = new CharGrid(gridWidth, maxGridHeight);
61157
+ const titleX = Math.max(0, Math.floor((gridWidth - graph.workflowName.length) / 2));
61158
+ grid.writeStr(titleX, 1, graph.workflowName);
61159
+ for (const pos of boxPositions.values()) {
61160
+ drawBox(grid, pos.box, pos.bx, pos.by, connected);
61161
+ }
61162
+ drawConnections(grid, graph.connections, boxPositions, boxStartY, maxGridHeight);
61163
+ const scopeLines = [];
61164
+ for (const node of graph.nodes) {
61165
+ if (node.scopeChildren && node.scopeChildren.length > 0) {
61166
+ scopeLines.push("");
61167
+ scopeLines.push(` Scope [${node.label}]:`);
61168
+ if (node.scopeConnections) {
61169
+ for (const sc of node.scopeConnections) {
61170
+ const arrow = sc.isStepConnection ? "\u2550\u2550\u25B6" : "\u2500\u2500\u25B6";
61171
+ scopeLines.push(` ${sc.fromNode}.${sc.fromPort} ${arrow} ${sc.toNode}.${sc.toPort}${sc.isStepConnection ? " STEP" : ""}`);
61172
+ }
61173
+ }
61174
+ }
61175
+ }
61176
+ const lines = grid.toLines();
61177
+ lines.push(...scopeLines);
61178
+ lines.push("");
61179
+ lines.push(" \u25CF connected \u25CB not connected \u2550\u2550\u25B6 STEP \u2500\u2500\u25B6 DATA");
61180
+ return lines.join("\n");
61181
+ }
61182
+ function drawConnections(grid, connections, positions, boxAreaTop, _gridHeight) {
61183
+ let globalMinY = Infinity;
61184
+ let globalMaxY = 0;
61185
+ for (const pos of positions.values()) {
61186
+ globalMinY = Math.min(globalMinY, pos.by);
61187
+ globalMaxY = Math.max(globalMaxY, pos.by + pos.box.boxHeight);
61188
+ }
61189
+ const sorted = [...connections].sort((a, b) => {
61190
+ const fa = positions.get(a.fromNode);
61191
+ const ta = positions.get(a.toNode);
61192
+ const fb = positions.get(b.fromNode);
61193
+ const tb = positions.get(b.toNode);
61194
+ if (!fa || !ta || !fb || !tb) return 0;
61195
+ const spanA = Math.abs(ta.col - fa.col);
61196
+ const spanB = Math.abs(tb.col - fb.col);
61197
+ if (spanA !== spanB) return spanA - spanB;
61198
+ const ya = fa.by + (fa.box.portRowOffsets.get(a.fromPort) ?? 0);
61199
+ const yb = fb.by + (fb.box.portRowOffsets.get(b.fromPort) ?? 0);
61200
+ return ya - yb;
61201
+ });
61202
+ const usedTracks = /* @__PURE__ */ new Map();
61203
+ let nextAboveHighway = globalMinY - 2;
61204
+ let nextBelowHighway = globalMaxY + 1;
61205
+ for (const conn of sorted) {
61206
+ const fromPos = positions.get(conn.fromNode);
61207
+ const toPos = positions.get(conn.toNode);
61208
+ if (!fromPos || !toPos) continue;
61209
+ const fromRowOff = fromPos.box.portRowOffsets.get(conn.fromPort);
61210
+ const toRowOff = toPos.box.portRowOffsets.get(conn.toPort);
61211
+ if (fromRowOff === void 0 || toRowOff === void 0) continue;
61212
+ const y1 = fromPos.by + fromRowOff;
61213
+ const y2 = toPos.by + toRowOff;
61214
+ const x1 = fromPos.bx + fromPos.box.boxWidth;
61215
+ const x2 = toPos.bx - 1;
61216
+ const isStep = conn.isStepConnection;
61217
+ const hCh = isStep ? "\u2550" : "\u2500";
61218
+ const vCh = isStep ? "\u2551" : "\u2502";
61219
+ const isAdjacent = toPos.col === fromPos.col + 1;
61220
+ if (isAdjacent) {
61221
+ drawAdjacentRoute(grid, x1, y1, x2, y2, hCh, vCh, isStep, usedTracks, fromPos, toPos);
61222
+ } else {
61223
+ const avgY = (y1 + y2) / 2;
61224
+ const midBoxY = (globalMinY + globalMaxY) / 2;
61225
+ let highwayY;
61226
+ if (avgY <= midBoxY) {
61227
+ highwayY = nextAboveHighway;
61228
+ nextAboveHighway--;
61229
+ } else {
61230
+ highwayY = nextBelowHighway;
61231
+ nextBelowHighway++;
61232
+ }
61233
+ drawSpanningRoute(grid, x1, y1, x2, y2, highwayY, hCh, vCh, isStep, usedTracks, fromPos, toPos);
61234
+ }
61235
+ }
61236
+ }
61237
+ function drawAdjacentRoute(grid, x1, y1, x2, y2, hCh, vCh, isStep, usedTracks, fromPos, toPos) {
61238
+ if (y1 === y2) {
61239
+ for (let x = x1; x < x2; x++) grid.set(x, y1, hCh);
61240
+ grid.set(x2, y1, "\u25B6");
61241
+ return;
61242
+ }
61243
+ const gapStart = fromPos.bx + fromPos.box.boxWidth + 1;
61244
+ const gapEnd = toPos.bx - 2;
61245
+ const midX = findTrack(usedTracks, gapStart, gapEnd, y1, y2);
61246
+ markTrack(usedTracks, midX, y1, y2);
61247
+ for (let x = x1; x < midX; x++) grid.set(x, y1, hCh);
61248
+ grid.set(midX, y1, cornerChar(isStep, "left", y2 > y1 ? "down" : "up"));
61249
+ drawVerticalSegment(grid, midX, y1, y2, vCh);
61250
+ grid.set(midX, y2, cornerChar(isStep, "right", y2 > y1 ? "up" : "down"));
61251
+ for (let x = midX + 1; x < x2; x++) grid.set(x, y2, hCh);
61252
+ grid.set(x2, y2, "\u25B6");
61253
+ }
61254
+ function drawSpanningRoute(grid, x1, y1, x2, y2, highwayY, hCh, vCh, isStep, usedTracks, fromPos, toPos) {
61255
+ const srcGapStart = fromPos.bx + fromPos.box.boxWidth + 1;
61256
+ const srcGapEnd = srcGapStart + 10;
61257
+ const srcMidX = findTrack(usedTracks, srcGapStart, srcGapEnd, y1, highwayY);
61258
+ markTrack(usedTracks, srcMidX, y1, highwayY);
61259
+ const tgtGapEnd = toPos.bx - 2;
61260
+ const tgtGapStart = tgtGapEnd - 10;
61261
+ const tgtMidX = findTrack(usedTracks, tgtGapStart, tgtGapEnd, highwayY, y2);
61262
+ markTrack(usedTracks, tgtMidX, highwayY, y2);
61263
+ const goingDown = highwayY > y1;
61264
+ const goingUp = y2 < highwayY;
61265
+ for (let x = x1; x < srcMidX; x++) grid.set(x, y1, hCh);
61266
+ grid.set(srcMidX, y1, cornerChar(isStep, "left", goingDown ? "down" : "up"));
61267
+ drawVerticalSegment(grid, srcMidX, y1, highwayY, vCh);
61268
+ grid.set(srcMidX, highwayY, cornerChar(isStep, "right", goingDown ? "up" : "down"));
61269
+ for (let x = srcMidX + 1; x < tgtMidX; x++) grid.set(x, highwayY, hCh);
61270
+ grid.set(tgtMidX, highwayY, cornerChar(isStep, "left", goingUp ? "up" : "down"));
61271
+ drawVerticalSegment(grid, tgtMidX, highwayY, y2, vCh);
61272
+ grid.set(tgtMidX, y2, cornerChar(isStep, "right", goingUp ? "down" : "up"));
61273
+ for (let x = tgtMidX + 1; x < x2; x++) grid.set(x, y2, hCh);
61274
+ grid.set(x2, y2, "\u25B6");
61275
+ }
61276
+ function drawVerticalSegment(grid, x, y1, y2, vCh) {
61277
+ const minY = Math.min(y1, y2);
61278
+ const maxY = Math.max(y1, y2);
61279
+ for (let y = minY + 1; y < maxY; y++) {
61280
+ grid.set(x, y, vCh);
61281
+ }
61282
+ }
61283
+ function findTrack(usedTracks, gapStart, gapEnd, y1, y2) {
61284
+ const minY = Math.min(y1, y2);
61285
+ const maxY = Math.max(y1, y2);
61286
+ const actualStart = Math.min(gapStart, gapEnd);
61287
+ const actualEnd = Math.max(gapStart, gapEnd);
61288
+ const mid = Math.floor((actualStart + actualEnd) / 2);
61289
+ for (let offset = 0; offset <= actualEnd - actualStart; offset++) {
61290
+ for (const candidate of [mid + offset, mid - offset]) {
61291
+ if (candidate < actualStart || candidate > actualEnd) continue;
61292
+ const existing = usedTracks.get(candidate);
61293
+ if (!existing) return candidate;
61294
+ let conflict = false;
61295
+ for (let y = minY; y <= maxY; y++) {
61296
+ if (existing.has(y)) {
61297
+ conflict = true;
61298
+ break;
61299
+ }
61300
+ }
61301
+ if (!conflict) return candidate;
61302
+ }
61303
+ }
61304
+ return mid;
61305
+ }
61306
+ function markTrack(usedTracks, x, y1, y2) {
61307
+ if (!usedTracks.has(x)) usedTracks.set(x, /* @__PURE__ */ new Set());
61308
+ const s = usedTracks.get(x);
61309
+ const minY = Math.min(y1, y2);
61310
+ const maxY = Math.max(y1, y2);
61311
+ for (let y = minY; y <= maxY; y++) s.add(y);
61312
+ }
61313
+ function renderASCIICompact(graph) {
61314
+ const layers = groupByLayer(graph.nodes);
61315
+ if (layers.length === 0) return `${graph.workflowName}
61316
+ (empty workflow)`;
61317
+ const outputLines = [];
61318
+ outputLines.push(graph.workflowName);
61319
+ outputLines.push("");
61320
+ const mainChain = [];
61321
+ const parallelNodes = [];
61322
+ for (const layer of layers) {
61323
+ mainChain.push(layer[0]);
61324
+ for (let i = 1; i < layer.length; i++) parallelNodes.push(layer[i]);
61325
+ }
61326
+ const topParts = [];
61327
+ const midParts = [];
61328
+ const botParts = [];
61329
+ for (let i = 0; i < mainChain.length; i++) {
61330
+ const node = mainChain[i];
61331
+ const innerWidth = Math.max(node.label.length + 2, 5);
61332
+ const padLeft = Math.floor((innerWidth - node.label.length) / 2);
61333
+ const padRight = innerWidth - node.label.length - padLeft;
61334
+ topParts.push("\u250C" + "\u2500".repeat(innerWidth) + "\u2510");
61335
+ midParts.push("\u2502" + " ".repeat(padLeft) + node.label + " ".repeat(padRight) + "\u2502");
61336
+ botParts.push("\u2514" + "\u2500".repeat(innerWidth) + "\u2518");
61337
+ if (i < mainChain.length - 1) {
61338
+ topParts.push(" ");
61339
+ midParts.push("\u2501\u2501\u2501\u25B6");
61340
+ botParts.push(" ");
61341
+ }
61342
+ }
61343
+ outputLines.push(" " + topParts.join(""));
61344
+ outputLines.push(" " + midParts.join(""));
61345
+ outputLines.push(" " + botParts.join(""));
61346
+ if (parallelNodes.length > 0) {
61347
+ outputLines.push("");
61348
+ outputLines.push(" Parallel: " + parallelNodes.map((n) => n.label).join(", "));
61349
+ }
61350
+ for (const node of graph.nodes) {
61351
+ if (node.scopeChildren && node.scopeChildren.length > 0) {
61352
+ outputLines.push("");
61353
+ outputLines.push(` Scope [${node.label}]: ` + node.scopeChildren.map((c) => c.label).join(" \u2501\u25B6 "));
61354
+ }
61355
+ }
61356
+ return outputLines.join("\n");
61357
+ }
61358
+ function renderText(graph) {
61359
+ const lines = [];
61360
+ const connected = buildConnectedPorts(graph.connections);
61361
+ lines.push(graph.workflowName);
61362
+ lines.push("\u2550".repeat(graph.workflowName.length));
61363
+ lines.push("");
61364
+ lines.push("Nodes:");
61365
+ const maxLabelLen = Math.max(...graph.nodes.map((n) => n.label.length), 5);
61366
+ for (const node of graph.nodes) {
61367
+ const label = node.label.padEnd(maxLabelLen);
61368
+ const inputPorts = node.inputs.map((p) => `${p.name}${portSymbol(node.id, p, connected)}`);
61369
+ const outputPorts = node.outputs.map((p) => `${p.name}${portSymbol(node.id, p, connected)}`);
61370
+ const inputStr = inputPorts.length > 0 ? `[${inputPorts.join(", ")}]` : "";
61371
+ const outputStr = outputPorts.length > 0 ? `[${outputPorts.join(", ")}]` : "";
61372
+ if (inputStr && outputStr) {
61373
+ lines.push(` ${label} ${inputStr} \u2192 ${outputStr}`);
61374
+ } else if (outputStr) {
61375
+ lines.push(` ${label} ${outputStr}`);
61376
+ } else if (inputStr) {
61377
+ lines.push(` ${label} ${inputStr}`);
61378
+ } else {
61379
+ lines.push(` ${label}`);
61380
+ }
61381
+ if (node.scopeChildren && node.scopeChildren.length > 0) {
61382
+ lines.push(` ${" ".repeat(maxLabelLen)} scope: ${node.scopeChildren.map((c) => c.label).join(", ")}`);
61383
+ }
61384
+ }
61385
+ if (graph.connections.length > 0) {
61386
+ lines.push("");
61387
+ lines.push("Connections:");
61388
+ const maxFromLen = Math.max(...graph.connections.map((c) => `${c.fromNode}.${c.fromPort}`.length));
61389
+ for (const conn of graph.connections) {
61390
+ const from = `${conn.fromNode}.${conn.fromPort}`.padEnd(maxFromLen);
61391
+ const arrow = conn.isStepConnection ? "\u2501\u2501\u25B6" : "\u2500\u2500\u25B6";
61392
+ const to = `${conn.toNode}.${conn.toPort}`;
61393
+ const type2 = conn.isStepConnection ? "STEP" : "";
61394
+ lines.push(` ${from} ${arrow} ${to}${type2 ? " " + type2 : ""}`);
61395
+ }
61396
+ }
61397
+ return lines.join("\n");
61398
+ }
61399
+
61400
+ // src/cli/commands/describe.ts
61401
+ function buildNodeInfo(instance, nodeType) {
61402
+ return {
61403
+ id: instance.id,
61404
+ type: instance.nodeType,
61405
+ inputs: nodeType ? Object.keys(nodeType.inputs) : [],
61406
+ outputs: nodeType ? Object.keys(nodeType.outputs) : []
61407
+ };
61408
+ }
61409
+ function buildAdjacency(ast) {
61410
+ const fromStart = [];
61411
+ const toExit = /* @__PURE__ */ new Set();
61412
+ const edges = /* @__PURE__ */ new Map();
61413
+ ast.connections.forEach((conn) => {
61414
+ if (conn.from.node === "Start") {
61415
+ if (!fromStart.includes(conn.to.node)) {
61416
+ fromStart.push(conn.to.node);
61417
+ }
61418
+ } else if (conn.to.node === "Exit") {
61419
+ toExit.add(conn.from.node);
61420
+ } else {
61421
+ const targets = edges.get(conn.from.node) || [];
61422
+ if (!targets.includes(conn.to.node)) {
61423
+ targets.push(conn.to.node);
61424
+ }
61425
+ edges.set(conn.from.node, targets);
61426
+ }
61427
+ });
61428
+ return { fromStart, toExit, edges };
61429
+ }
61430
+ function enumeratePaths(ast) {
61431
+ const { fromStart, toExit, edges } = buildAdjacency(ast);
61432
+ const paths = [];
61433
+ function dfs(current2, path42, visited) {
61434
+ if (toExit.has(current2)) {
61435
+ paths.push([...path42, "Exit"]);
61436
+ }
61437
+ const targets = edges.get(current2) || [];
61438
+ for (const next of targets) {
61439
+ if (!visited.has(next)) {
61440
+ visited.add(next);
61441
+ path42.push(next);
61442
+ dfs(next, path42, visited);
61443
+ path42.pop();
61444
+ visited.delete(next);
61445
+ }
61446
+ }
61447
+ }
61448
+ fromStart.forEach((startNode) => {
61449
+ const visited = /* @__PURE__ */ new Set([startNode]);
61450
+ dfs(startNode, ["Start", startNode], visited);
61451
+ });
61452
+ return paths;
61453
+ }
61454
+ function buildGraph(ast) {
61455
+ const { fromStart, toExit, edges } = buildAdjacency(ast);
61456
+ const lines = [];
61457
+ function dfs(current2, path42, visited) {
61458
+ if (toExit.has(current2)) {
61459
+ lines.push([...path42, "Exit"].join(" -> "));
61460
+ }
61461
+ const targets = edges.get(current2) || [];
61462
+ for (const next of targets) {
61463
+ if (!visited.has(next)) {
61464
+ visited.add(next);
61465
+ path42.push(next);
61466
+ dfs(next, path42, visited);
61467
+ path42.pop();
61468
+ visited.delete(next);
61469
+ }
61470
+ }
61471
+ if (targets.length === 0 && !toExit.has(current2)) {
61472
+ lines.push(path42.join(" -> "));
61473
+ }
61474
+ }
61475
+ fromStart.forEach((startNode) => {
61476
+ const visited = /* @__PURE__ */ new Set([startNode]);
61477
+ dfs(startNode, ["Start", startNode], visited);
61478
+ });
61479
+ return lines.join("\n");
61480
+ }
61481
+ function formatPaths(ast) {
61482
+ const paths = enumeratePaths(ast);
61483
+ if (paths.length === 0) return "(no complete Start-to-Exit paths found)";
61484
+ return paths.map((p) => p.join(" -> ")).join("\n");
61485
+ }
61486
+ function generateMermaid(ast) {
61487
+ const lines = ["graph LR"];
61488
+ ast.instances.forEach((instance) => {
61489
+ lines.push(` ${instance.id}[${instance.id}: ${instance.nodeType}]`);
61490
+ });
61491
+ const seenEdges = /* @__PURE__ */ new Set();
61492
+ ast.connections.forEach((conn) => {
61493
+ const edgeKey = `${conn.from.node}->${conn.to.node}`;
61494
+ if (!seenEdges.has(edgeKey)) {
61495
+ seenEdges.add(edgeKey);
61496
+ const from = conn.from.node === "Start" ? "Start((Start))" : conn.from.node;
61497
+ const to = conn.to.node === "Exit" ? "Exit((Exit))" : conn.to.node;
61498
+ lines.push(` ${from} --> ${to}`);
61499
+ }
61500
+ });
61501
+ return lines.join("\n");
61502
+ }
61503
+ function describeWorkflow(ast, options = {}) {
61504
+ const { node: focusNodeId } = options;
61505
+ const validation = validator.validate(ast);
61506
+ const validationOutput = {
61507
+ valid: validation.valid,
61508
+ errors: validation.errors.map((e) => e.message),
61509
+ warnings: validation.warnings.map((w) => w.message)
61510
+ };
61511
+ const nodeTypeMap = /* @__PURE__ */ new Map();
61512
+ ast.nodeTypes.forEach((nt) => nodeTypeMap.set(nt.functionName, nt));
61513
+ if (focusNodeId) {
61514
+ const nodeInstance = getNode(ast, focusNodeId);
61515
+ if (!nodeInstance) {
61516
+ throw new Error(`Node not found: ${focusNodeId}`);
61517
+ }
61518
+ const nodeType = nodeTypeMap.get(nodeInstance.nodeType);
61519
+ const incoming = getIncomingConnections(ast, focusNodeId).map((c) => ({
61520
+ from: `${c.from.node}.${c.from.port}`,
61521
+ to: `${c.to.node}.${c.to.port}`
61522
+ }));
61523
+ const outgoing = getOutgoingConnections(ast, focusNodeId).map((c) => ({
61524
+ from: `${c.from.node}.${c.from.port}`,
61525
+ to: `${c.to.node}.${c.to.port}`
61526
+ }));
61527
+ return {
61528
+ focusNode: focusNodeId,
61529
+ node: buildNodeInfo(nodeInstance, nodeType),
61530
+ incoming,
61531
+ outgoing,
61532
+ validation: validationOutput
61533
+ };
61534
+ }
61535
+ const nodes = ast.instances.map((instance) => {
61536
+ const nodeType = nodeTypeMap.get(instance.nodeType);
61537
+ return buildNodeInfo(instance, nodeType);
61538
+ });
61539
+ const connections = ast.connections.map((c) => ({
61540
+ from: `${c.from.node}.${c.from.port}`,
61541
+ to: `${c.to.node}.${c.to.port}`
61542
+ }));
61543
+ return {
61544
+ name: ast.functionName,
61545
+ description: ast.description || null,
61546
+ nodes,
61547
+ connections,
61548
+ graph: buildGraph(ast),
61549
+ validation: validationOutput
61550
+ };
61551
+ }
61552
+ function formatTextOutput(ast, output) {
61553
+ if ("focusNode" in output) {
61554
+ const focused = output;
61555
+ const lines2 = [];
61556
+ lines2.push(`Node: ${focused.node.id} [${focused.node.type}]`);
61557
+ lines2.push(` Inputs: ${focused.node.inputs.join(", ") || "(none)"}`);
61558
+ lines2.push(` Outputs: ${focused.node.outputs.join(", ") || "(none)"}`);
61559
+ if (focused.incoming.length > 0) {
61560
+ lines2.push("");
61561
+ lines2.push("Incoming:");
61562
+ focused.incoming.forEach((c) => lines2.push(` ${c.from} -> ${c.to}`));
61563
+ }
61564
+ if (focused.outgoing.length > 0) {
61565
+ lines2.push("");
61566
+ lines2.push("Outgoing:");
61567
+ focused.outgoing.forEach((c) => lines2.push(` ${c.from} -> ${c.to}`));
61568
+ }
61569
+ return lines2.join("\n");
61570
+ }
61571
+ const desc = output;
61572
+ const lines = [];
61573
+ lines.push(`Workflow: ${desc.name}`);
61574
+ if (desc.description) {
61575
+ lines.push(` ${desc.description}`);
61576
+ }
61577
+ const nodeTypeMap = /* @__PURE__ */ new Map();
61578
+ ast.nodeTypes.forEach((nt) => nodeTypeMap.set(nt.functionName, nt));
61579
+ lines.push("");
61580
+ lines.push(`Nodes (${desc.nodes.length}):`);
61581
+ const maxIdLen = Math.max(...desc.nodes.map((n) => n.id.length), 2);
61582
+ const maxTypeLen = Math.max(...desc.nodes.map((n) => n.type.length), 2);
61583
+ desc.nodes.forEach((node) => {
61584
+ const id = node.id.padEnd(maxIdLen);
61585
+ const type2 = `[${node.type}]`.padEnd(maxTypeLen + 2);
61586
+ const nodeType = nodeTypeMap.get(node.type);
61587
+ const isExpression = nodeType?.expression === true;
61588
+ const scopeNames = nodeType?.scopes ?? (nodeType?.scope ? [nodeType.scope] : []);
61589
+ const filterPorts = (ports, direction) => {
61590
+ if (!isExpression || !nodeType) return ports;
61591
+ return ports.filter((portName) => {
61592
+ if (portName === "execute" || portName === "onSuccess" || portName === "onFailure") {
61593
+ return false;
61594
+ }
61595
+ const portDef = direction === "input" ? nodeType.inputs[portName] : nodeType.outputs[portName];
61596
+ return !(portDef?.isControlFlow || portDef?.dataType === "STEP");
61597
+ });
61598
+ };
61599
+ if (scopeNames.length > 0 && nodeType) {
61600
+ const regularInputs = [];
61601
+ const regularOutputs = [];
61602
+ const scopedPorts = {};
61603
+ scopeNames.forEach((s) => {
61604
+ scopedPorts[s] = { sends: [], receives: [], children: [] };
61605
+ });
61606
+ Object.entries(nodeType.inputs).forEach(([name, port]) => {
61607
+ if (port.scope && scopedPorts[port.scope]) {
61608
+ scopedPorts[port.scope].receives.push(name + (port.isControlFlow ? " (STEP)" : ""));
61609
+ } else {
61610
+ regularInputs.push(name);
61611
+ }
61612
+ });
61613
+ Object.entries(nodeType.outputs).forEach(([name, port]) => {
61614
+ if (port.scope && scopedPorts[port.scope]) {
61615
+ scopedPorts[port.scope].sends.push(name + (port.isControlFlow ? " (STEP)" : ""));
61616
+ } else {
61617
+ regularOutputs.push(name);
61618
+ }
61619
+ });
61620
+ if (ast.scopes) {
61621
+ for (const [scopeKey, childIds] of Object.entries(ast.scopes)) {
61622
+ const dotIdx = scopeKey.indexOf(".");
61623
+ const scopeName = dotIdx >= 0 ? scopeKey.substring(dotIdx + 1) : scopeKey;
61624
+ if (scopedPorts[scopeName]) {
61625
+ scopedPorts[scopeName].children.push(...childIds);
61626
+ }
61627
+ }
61628
+ }
61629
+ const ins = regularInputs.length > 0 ? `IN: ${regularInputs.join(", ")}` : "";
61630
+ const outs = regularOutputs.length > 0 ? `OUT: ${regularOutputs.join(", ")}` : "";
61631
+ const ports = [ins, outs].filter(Boolean).join(" ");
61632
+ lines.push(` ${id} ${type2} ${ports}`);
61633
+ for (const [scopeName, info] of Object.entries(scopedPorts)) {
61634
+ lines.push(` Scope "${scopeName}":`);
61635
+ if (info.sends.length > 0) {
61636
+ lines.push(` Sends to children: ${info.sends.join(", ")}`);
61637
+ }
61638
+ if (info.receives.length > 0) {
61639
+ lines.push(` Receives from children: ${info.receives.join(", ")}`);
61640
+ }
61641
+ if (info.children.length > 0) {
61642
+ lines.push(` Children: ${info.children.join(", ")}`);
61643
+ }
61644
+ }
61645
+ } else {
61646
+ const filteredInputs = filterPorts(node.inputs, "input");
61647
+ const filteredOutputs = filterPorts(node.outputs, "output");
61648
+ const ins = filteredInputs.length > 0 ? `IN: ${filteredInputs.join(", ")}` : "";
61649
+ const outs = filteredOutputs.length > 0 ? `OUT: ${filteredOutputs.join(", ")}` : "";
61650
+ const ports = [ins, outs].filter(Boolean).join(" ");
61651
+ lines.push(` ${id} ${type2} ${ports}`);
61652
+ }
61653
+ });
61654
+ if (desc.graph) {
61655
+ lines.push("");
61656
+ const paths = enumeratePaths(ast);
61657
+ if (paths.length > 1) {
61658
+ lines.push(`Paths (${paths.length}):`);
61659
+ paths.forEach((p, i) => {
61660
+ lines.push(` Path ${i + 1}: ${p.join(" -> ")}`);
61661
+ });
61662
+ } else {
61663
+ lines.push("Flow:");
61664
+ desc.graph.split("\n").forEach((flowLine) => {
61665
+ lines.push(` ${flowLine}`);
61666
+ });
61667
+ }
61668
+ }
61669
+ lines.push("");
61670
+ lines.push(
61671
+ `Validation: ${desc.validation.errors.length} errors, ${desc.validation.warnings.length} warnings`
61672
+ );
61673
+ return lines.join("\n");
61674
+ }
61675
+ function formatDescribeOutput(ast, output, format) {
61676
+ switch (format) {
61677
+ case "text":
61678
+ return formatTextOutput(ast, output);
61679
+ case "mermaid":
61680
+ return generateMermaid(ast);
61681
+ case "paths":
61682
+ return formatPaths(ast);
61683
+ case "ascii":
61684
+ return renderASCII(buildDiagramGraph(ast));
61685
+ case "ascii-compact":
61686
+ return renderASCIICompact(buildDiagramGraph(ast));
61687
+ case "json":
61688
+ default:
61689
+ return JSON.stringify(output, null, 2);
61690
+ }
61691
+ }
61692
+ async function describeCommand(input, options = {}) {
61693
+ const { format = "json", node: focusNodeId, workflowName, compile = false } = options;
61694
+ const filePath = path11.resolve(input);
61695
+ if (!fs11.existsSync(filePath)) {
61696
+ logger.error(`File not found: ${filePath}`);
61697
+ process.exit(1);
61698
+ }
61699
+ try {
61700
+ const parseResult = await parseWorkflow(filePath, { workflowName });
61701
+ if (parseResult.errors.length > 0) {
61702
+ logger.error(`Parse errors:`);
61703
+ parseResult.errors.forEach((err) => logger.error(` ${err}`));
61704
+ process.exit(1);
61705
+ }
61706
+ const ast = parseResult.ast;
61707
+ if (compile) {
61708
+ const sourceCode = fs11.readFileSync(filePath, "utf8");
61709
+ const generated = generateInPlace(sourceCode, ast, { production: false });
61710
+ if (generated.hasChanges) {
61711
+ fs11.writeFileSync(filePath, generated.code, "utf8");
61712
+ logger.info(`Updated runtime markers in ${path11.basename(filePath)}`);
61713
+ }
61714
+ }
61715
+ const output = describeWorkflow(ast, { node: focusNodeId });
61716
+ console.log(formatDescribeOutput(ast, output, format));
61717
+ } catch (error2) {
61718
+ if (error2 instanceof Error && error2.message.startsWith("Node not found:")) {
61719
+ logger.error(error2.message);
61720
+ process.exit(1);
61721
+ }
61722
+ logger.error(`Failed to describe workflow: ${getErrorMessage(error2)}`);
61723
+ process.exit(1);
61724
+ }
61725
+ }
61726
+
61727
+ // src/cli/commands/diagram.ts
61728
+ import * as fs12 from "fs";
61729
+ import * as path12 from "path";
61730
+
61260
61731
  // src/diagram/renderer.ts
61261
61732
  init_theme();
61262
61733
  function escapeXml(str2) {
@@ -62555,10 +63026,20 @@ function workflowToSVG(ast, options = {}) {
62555
63026
  const graph = buildDiagramGraph(ast, options);
62556
63027
  return renderSVG(graph, options);
62557
63028
  }
63029
+ function sourceToSVG(code, options = {}) {
63030
+ const result = parser.parseFromString(code);
63031
+ return pickAndRender(result.workflows, options);
63032
+ }
62558
63033
  function fileToSVG(filePath, options = {}) {
62559
63034
  const result = parser.parse(filePath);
62560
63035
  return pickAndRender(result.workflows, options);
62561
63036
  }
63037
+ function sourceToHTML(code, options = {}) {
63038
+ const result = parser.parseFromString(code);
63039
+ const ast = pickWorkflow(result.workflows, options);
63040
+ const svg = workflowToSVG(ast, options);
63041
+ return wrapSVGInHTML(svg, { title: options.workflowName ?? ast.name, theme: options.theme, nodeSources: buildNodeSourceMap(ast) });
63042
+ }
62562
63043
  function fileToHTML(filePath, options = {}) {
62563
63044
  const result = parser.parse(filePath);
62564
63045
  const ast = pickWorkflow(result.workflows, options);
@@ -62612,8 +63093,31 @@ function pickWorkflow(workflows, options) {
62612
63093
  function pickAndRender(workflows, options) {
62613
63094
  return workflowToSVG(pickWorkflow(workflows, options), options);
62614
63095
  }
63096
+ function renderByFormat(graph, format) {
63097
+ switch (format) {
63098
+ case "ascii":
63099
+ return renderASCII(graph);
63100
+ case "ascii-compact":
63101
+ return renderASCIICompact(graph);
63102
+ case "text":
63103
+ return renderText(graph);
63104
+ }
63105
+ }
63106
+ function sourceToASCII(code, options = {}) {
63107
+ const result = parser.parseFromString(code);
63108
+ const ast = pickWorkflow(result.workflows, options);
63109
+ const graph = buildDiagramGraph(ast, options);
63110
+ return renderByFormat(graph, options.format ?? "ascii");
63111
+ }
63112
+ function fileToASCII(filePath, options = {}) {
63113
+ const result = parser.parse(filePath);
63114
+ const ast = pickWorkflow(result.workflows, options);
63115
+ const graph = buildDiagramGraph(ast, options);
63116
+ return renderByFormat(graph, options.format ?? "ascii");
63117
+ }
62615
63118
 
62616
63119
  // src/cli/commands/diagram.ts
63120
+ var ASCII_FORMATS = /* @__PURE__ */ new Set(["ascii", "ascii-compact", "text"]);
62617
63121
  async function diagramCommand(input, options = {}) {
62618
63122
  const { output, format = "svg", ...diagramOptions } = options;
62619
63123
  const filePath = path12.resolve(input);
@@ -62622,8 +63126,14 @@ async function diagramCommand(input, options = {}) {
62622
63126
  process.exit(1);
62623
63127
  }
62624
63128
  try {
62625
- const isHtml = format === "html";
62626
- const result = isHtml ? fileToHTML(filePath, diagramOptions) : fileToSVG(filePath, diagramOptions);
63129
+ let result;
63130
+ if (ASCII_FORMATS.has(format)) {
63131
+ result = fileToASCII(filePath, { ...diagramOptions, format });
63132
+ } else if (format === "html") {
63133
+ result = fileToHTML(filePath, diagramOptions);
63134
+ } else {
63135
+ result = fileToSVG(filePath, diagramOptions);
63136
+ }
62627
63137
  if (output) {
62628
63138
  const outputPath = path12.resolve(output);
62629
63139
  fs12.writeFileSync(outputPath, result, "utf-8");
@@ -86027,7 +86537,7 @@ function registerQueryTools(mcp) {
86027
86537
  "Describe a workflow in LLM-friendly format (nodes, connections, graph, validation).",
86028
86538
  {
86029
86539
  filePath: external_exports.string().describe("Path to the workflow .ts file"),
86030
- format: external_exports.enum(["json", "text", "mermaid", "paths"]).optional().describe("Output format (default: json)"),
86540
+ format: external_exports.enum(["json", "text", "mermaid", "paths", "ascii", "ascii-compact"]).optional().describe("Output format (default: json). ascii/ascii-compact produce terminal-readable diagrams."),
86031
86541
  node: external_exports.string().optional().describe("Focus on a specific node ID"),
86032
86542
  workflowName: external_exports.string().optional().describe("Specific workflow if file has multiple")
86033
86543
  },
@@ -93200,31 +93710,54 @@ function resolvePackageName(spec) {
93200
93710
  // src/mcp/tools-diagram.ts
93201
93711
  import * as fs26 from "fs";
93202
93712
  import * as path29 from "path";
93713
+ var ASCII_FORMATS2 = /* @__PURE__ */ new Set(["ascii", "ascii-compact", "text"]);
93203
93714
  function registerDiagramTools(mcp) {
93204
93715
  mcp.tool(
93205
93716
  "fw_diagram",
93206
- "Generate an SVG diagram of a workflow. Returns SVG string or writes to a file.",
93717
+ "Generate a diagram of a workflow. Formats: svg/html produce visual markup, ascii/ascii-compact/text produce plain text readable in terminal. Provide either filePath (workflow .ts file) or source (inline code).",
93207
93718
  {
93208
- filePath: external_exports.string().describe("Path to the workflow .ts file"),
93209
- outputPath: external_exports.string().optional().describe("Output file path for the SVG. If omitted, returns SVG as text."),
93719
+ filePath: external_exports.string().optional().describe("Path to the workflow .ts file (required if source is not provided)"),
93720
+ source: external_exports.string().optional().describe("Inline workflow source code (required if filePath is not provided)"),
93721
+ outputPath: external_exports.string().optional().describe("Output file path. If omitted, returns content as text."),
93210
93722
  workflowName: external_exports.string().optional().describe("Specific workflow name if file has multiple"),
93211
93723
  theme: external_exports.enum(["dark", "light"]).optional().describe("Color theme (default: dark)"),
93212
93724
  showPortLabels: external_exports.boolean().optional().describe("Show port labels on diagram (default: true)"),
93213
- format: external_exports.enum(["svg", "html"]).optional().describe("Output format: svg (default) or html (interactive viewer)")
93725
+ format: external_exports.enum(["svg", "html", "ascii", "ascii-compact", "text"]).optional().describe("Output format: svg (default), html (interactive viewer), ascii (port-level detail), ascii-compact (compact boxes), text (structured list)")
93214
93726
  },
93215
93727
  async (args) => {
93216
93728
  try {
93217
- const resolvedPath = path29.resolve(args.filePath);
93218
- if (!fs26.existsSync(resolvedPath)) {
93219
- return makeErrorResult("FILE_NOT_FOUND", `File not found: ${resolvedPath}`);
93729
+ if (!args.filePath && !args.source) {
93730
+ return makeErrorResult("MISSING_PARAM", "Provide either filePath or source parameter");
93220
93731
  }
93732
+ const format = args.format ?? "svg";
93221
93733
  const diagramOptions = {
93222
93734
  workflowName: args.workflowName,
93223
93735
  theme: args.theme,
93224
- showPortLabels: args.showPortLabels
93736
+ showPortLabels: args.showPortLabels,
93737
+ format
93225
93738
  };
93226
- const isHtml = args.format === "html";
93227
- const result = isHtml ? fileToHTML(resolvedPath, diagramOptions) : fileToSVG(resolvedPath, diagramOptions);
93739
+ let result;
93740
+ if (args.source) {
93741
+ if (ASCII_FORMATS2.has(format)) {
93742
+ result = sourceToASCII(args.source, diagramOptions);
93743
+ } else if (format === "html") {
93744
+ result = sourceToHTML(args.source, diagramOptions);
93745
+ } else {
93746
+ result = sourceToSVG(args.source, diagramOptions);
93747
+ }
93748
+ } else {
93749
+ const resolvedPath = path29.resolve(args.filePath);
93750
+ if (!fs26.existsSync(resolvedPath)) {
93751
+ return makeErrorResult("FILE_NOT_FOUND", `File not found: ${resolvedPath}`);
93752
+ }
93753
+ if (ASCII_FORMATS2.has(format)) {
93754
+ result = fileToASCII(resolvedPath, diagramOptions);
93755
+ } else if (format === "html") {
93756
+ result = fileToHTML(resolvedPath, diagramOptions);
93757
+ } else {
93758
+ result = fileToSVG(resolvedPath, diagramOptions);
93759
+ }
93760
+ }
93228
93761
  if (args.outputPath) {
93229
93762
  const outputResolved = path29.resolve(args.outputPath);
93230
93763
  fs26.writeFileSync(outputResolved, result, "utf-8");
@@ -93821,6 +94354,75 @@ function registerResources(mcp, connection, buffer) {
93821
94354
  );
93822
94355
  }
93823
94356
 
94357
+ // src/mcp/prompts.ts
94358
+ var NOCODE_SYSTEM_PROMPT = `You are a workflow developer powered by Flow Weaver. You write real, production-quality TypeScript behind the scenes, but you present everything to the user as plain-language step descriptions, connections, and diagrams. The user never needs to see code unless they ask for it.
94359
+
94360
+ ## How you work
94361
+
94362
+ When the user describes a workflow:
94363
+
94364
+ 1. Break their description into discrete steps (nodes), each with typed inputs and outputs.
94365
+ 2. Call fw_create_model to generate the workflow skeleton with declare stubs.
94366
+ 3. Call fw_implement_node for each step, writing real TypeScript function bodies that do what the user described. Write actual working code, not placeholder comments.
94367
+ 4. Call fw_validate to check the result. If there are errors you can fix (missing connections, type mismatches), fix them silently with fw_modify. Only tell the user about problems you genuinely cannot resolve.
94368
+ 5. Show the result as:
94369
+ - A numbered list of steps with one-sentence descriptions
94370
+ - An ASCII diagram (call fw_diagram with format "ascii-compact")
94371
+ Never show TypeScript code in this summary.
94372
+
94373
+ When the user asks to modify an existing workflow:
94374
+ - Use fw_modify or fw_modify_batch for structural changes (add/remove nodes, connections).
94375
+ - Use fw_implement_node to update a step's logic.
94376
+ - Re-validate after every change.
94377
+
94378
+ ## When to show code
94379
+
94380
+ Only reveal TypeScript when the user explicitly asks: "show me the code", "let me see the implementation", "what does step X look like", etc. When showing code, use fw_describe with format "text" or read the file directly.
94381
+
94382
+ ## Tool usage patterns
94383
+
94384
+ - fw_create_model: Create new workflows from a structured description (steps, inputs/outputs, flow path).
94385
+ - fw_implement_node: Replace a declare stub with a real function body.
94386
+ - fw_modify / fw_modify_batch: Add/remove nodes and connections, rename nodes, reposition.
94387
+ - fw_validate: Always run after changes. Fix what you can, report what you cannot.
94388
+ - fw_describe: Inspect workflow structure. Use format "text" for human-readable, "json" for programmatic.
94389
+ - fw_diagram: Generate visual representation. Prefer format "ascii-compact" for chat.
94390
+ - fw_workflow_status: Check which steps are implemented vs still stubs.
94391
+ - fw_scaffold / fw_list_templates: Bootstrap from templates when the user's request matches a known pattern.
94392
+ - fw_docs: Look up Flow Weaver features (scoped ports, branching, iteration, agents) when you need specifics.
94393
+ - fw_find_workflows: Locate existing workflow files in a directory.
94394
+ - fw_compile: Generate executable JavaScript from the workflow source.
94395
+
94396
+ ## Interaction style
94397
+
94398
+ - Ask 1-2 clarifying questions when the request is vague, not a long interrogation list.
94399
+ - Default to sensible choices for things the user did not specify (file names, port types, node names).
94400
+ - After creating or modifying a workflow, always show the step summary and ASCII diagram.
94401
+ - Use fw_docs to look up advanced features before guessing at syntax.
94402
+
94403
+ ## File conventions
94404
+
94405
+ - Workflow files use .ts extension.
94406
+ - Default location is the current working directory.
94407
+ - Node names use camelCase (e.g., validateEmail, sendNotification).
94408
+ - Workflow names use PascalCase (e.g., EmailValidation, OrderProcessing).`;
94409
+ function registerPrompts(mcp) {
94410
+ mcp.registerPrompt("flow-weaver-nocode", {
94411
+ title: "Flow Weaver No-Code Mode",
94412
+ description: "Describe workflows in plain language. The agent writes real TypeScript behind the scenes and presents results as step summaries and diagrams. Code is available on request."
94413
+ }, () => ({
94414
+ messages: [
94415
+ {
94416
+ role: "assistant",
94417
+ content: {
94418
+ type: "text",
94419
+ text: NOCODE_SYSTEM_PROMPT
94420
+ }
94421
+ }
94422
+ ]
94423
+ }));
94424
+ }
94425
+
93824
94426
  // src/mcp/server.ts
93825
94427
  function parseEventFilterFromEnv() {
93826
94428
  const filter3 = {};
@@ -93870,6 +94472,7 @@ async function startMcpServer(options) {
93870
94472
  registerDocsTools(mcp);
93871
94473
  registerModelTools(mcp);
93872
94474
  registerResources(mcp, connection, buffer);
94475
+ registerPrompts(mcp);
93873
94476
  if (!options._testDeps && options.stdio) {
93874
94477
  const transport = new StdioServerTransport();
93875
94478
  await mcp.connect(transport);
@@ -97168,7 +97771,7 @@ function displayInstalledPackage(pkg) {
97168
97771
  }
97169
97772
 
97170
97773
  // src/cli/index.ts
97171
- var version2 = true ? "0.10.8" : "0.0.0-dev";
97774
+ var version2 = true ? "0.10.9" : "0.0.0-dev";
97172
97775
  var program2 = new Command();
97173
97776
  program2.name("flow-weaver").description("Flow Weaver Annotations - Compile and validate workflow files").version(version2, "-v, --version", "Output the current version");
97174
97777
  program2.configureOutput({
@@ -97196,7 +97799,7 @@ program2.command("strip <input>").description("Remove generated code from compil
97196
97799
  process.exit(1);
97197
97800
  }
97198
97801
  });
97199
- program2.command("describe <input>").description("Output workflow structure in LLM-friendly formats (JSON, text, mermaid)").option("-f, --format <format>", "Output format: json (default), text, mermaid", "json").option("-n, --node <id>", "Focus on a specific node").option("--compile", "Also update runtime markers in the source file").option("-w, --workflow-name <name>", "Specific workflow name to describe").action(async (input, options) => {
97802
+ program2.command("describe <input>").description("Output workflow structure in LLM-friendly formats (JSON, text, mermaid)").option("-f, --format <format>", "Output format: json (default), text, mermaid, paths, ascii, ascii-compact", "json").option("-n, --node <id>", "Focus on a specific node").option("--compile", "Also update runtime markers in the source file").option("-w, --workflow-name <name>", "Specific workflow name to describe").action(async (input, options) => {
97200
97803
  try {
97201
97804
  await describeCommand(input, options);
97202
97805
  } catch (error2) {
@@ -97204,7 +97807,7 @@ program2.command("describe <input>").description("Output workflow structure in L
97204
97807
  process.exit(1);
97205
97808
  }
97206
97809
  });
97207
- program2.command("diagram <input>").description("Generate SVG or interactive HTML diagram of a workflow").option("-t, --theme <theme>", "Color theme: dark (default), light", "dark").option("-w, --width <pixels>", "SVG width in pixels").option("-p, --padding <pixels>", "Canvas padding in pixels").option("--no-port-labels", "Hide data type labels on ports").option("--workflow-name <name>", "Specific workflow to render").option("-f, --format <format>", "Output format: svg (default), html (interactive viewer)", "svg").option("-o, --output <file>", "Write output to file instead of stdout").action(async (input, options) => {
97810
+ program2.command("diagram <input>").description("Generate SVG or interactive HTML diagram of a workflow").option("-t, --theme <theme>", "Color theme: dark (default), light", "dark").option("-w, --width <pixels>", "SVG width in pixels").option("-p, --padding <pixels>", "Canvas padding in pixels").option("--no-port-labels", "Hide data type labels on ports").option("--workflow-name <name>", "Specific workflow to render").option("-f, --format <format>", "Output format: svg (default), html, ascii, ascii-compact, text", "svg").option("-o, --output <file>", "Write output to file instead of stdout").action(async (input, options) => {
97208
97811
  try {
97209
97812
  if (options.width) options.width = Number(options.width);
97210
97813
  if (options.padding) options.padding = Number(options.padding);