react-agentic 0.0.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.
package/dist/index.js ADDED
@@ -0,0 +1,2800 @@
1
+ import {
2
+ RuntimeMarkdownEmitter,
3
+ TranspileError,
4
+ buildRuntimeFile,
5
+ bundleCodeSplit,
6
+ bundleSingleEntryRuntime,
7
+ createProject,
8
+ createRuntimeContext,
9
+ detectRuntime,
10
+ emit,
11
+ emitAgent,
12
+ emitDocument,
13
+ emitRuntime,
14
+ emitSettings,
15
+ emitSkill,
16
+ emitSkillFile,
17
+ extractExportedFunctionNames,
18
+ extractExternalComponentDeclarations,
19
+ extractFunctions,
20
+ extractInlineText,
21
+ extractInputObjectLiteral,
22
+ extractInterfaceProperties,
23
+ extractLocalComponentDeclarations,
24
+ extractPromptPlaceholders,
25
+ extractRuntimeFnDeclarations,
26
+ extractRuntimeVarDeclarations,
27
+ extractText,
28
+ extractTypeArguments,
29
+ extractVariableDeclarations,
30
+ findRootJsxElement,
31
+ generateRuntime,
32
+ getArrayAttributeValue,
33
+ getAttributeValue,
34
+ getElementName,
35
+ getJsxChildren,
36
+ getNodeLocation,
37
+ getRuntimeFunctionNames,
38
+ getRuntimeImportPaths,
39
+ getSourceCode,
40
+ hasRuntimeImports,
41
+ init_parser,
42
+ init_project,
43
+ isRuntimeFile,
44
+ isWhitespaceOnlyText,
45
+ mergeSettings,
46
+ normalizeWhitespace,
47
+ parseFile,
48
+ parseSource,
49
+ resolveComponentImport,
50
+ resolveSpreadAttribute,
51
+ resolveTypeImport,
52
+ transformAgent,
53
+ transformMCPConfig,
54
+ transformRuntimeBlockChildren,
55
+ transformRuntimeCommand,
56
+ transformSkill,
57
+ transformState,
58
+ transformToRuntimeBlock
59
+ } from "./chunk-263OAOFB.js";
60
+ import "./chunk-OO3V32L6.js";
61
+
62
+ // src/components/markdown.ts
63
+ function Markdown(_props) {
64
+ return null;
65
+ }
66
+ function XmlBlock(_props) {
67
+ return null;
68
+ }
69
+ function Indent(_props) {
70
+ return null;
71
+ }
72
+
73
+ // src/components/structured.ts
74
+ function Table(_props) {
75
+ return null;
76
+ }
77
+ function List(_props) {
78
+ return null;
79
+ }
80
+
81
+ // src/components/Command.ts
82
+ function Command(_props) {
83
+ return null;
84
+ }
85
+
86
+ // src/components/Agent.ts
87
+ function defineAgent(config) {
88
+ return {
89
+ name: config.name,
90
+ path: config.path,
91
+ __isAgentRef: true
92
+ };
93
+ }
94
+ function isAgentRef(value) {
95
+ if (!value || typeof value !== "object") return false;
96
+ return value.__isAgentRef === true;
97
+ }
98
+ function getAgentName(agent) {
99
+ return isAgentRef(agent) ? agent.name : agent;
100
+ }
101
+ function getAgentPath(agent) {
102
+ return isAgentRef(agent) ? agent.path : void 0;
103
+ }
104
+ function useOutput(agent) {
105
+ return {
106
+ agent,
107
+ field: (name) => `{${agent}.${String(name)}}`
108
+ };
109
+ }
110
+ function Agent(_props) {
111
+ return null;
112
+ }
113
+ function SpawnAgent(_props) {
114
+ return null;
115
+ }
116
+ function OnStatus(_props) {
117
+ return null;
118
+ }
119
+
120
+ // src/components/runtime-var.ts
121
+ var RUNTIME_VAR_MARKER = /* @__PURE__ */ Symbol.for("react-agentic:runtime-var");
122
+ function createRuntimeVarProxy(varName, path) {
123
+ const target = {
124
+ __varName: varName,
125
+ __path: path,
126
+ [RUNTIME_VAR_MARKER]: true
127
+ };
128
+ return new Proxy(target, {
129
+ get(_target, prop) {
130
+ if (prop === "__varName") return varName;
131
+ if (prop === "__path") return path;
132
+ if (prop === RUNTIME_VAR_MARKER) return true;
133
+ if (typeof prop === "symbol") return void 0;
134
+ return createRuntimeVarProxy(varName, [...path, prop]);
135
+ }
136
+ });
137
+ }
138
+ function useRuntimeVar(name) {
139
+ return createRuntimeVarProxy(name, []);
140
+ }
141
+ function isRuntimeVar(value) {
142
+ if (!value || typeof value !== "object") return false;
143
+ return value[RUNTIME_VAR_MARKER] === true;
144
+ }
145
+ function getRuntimeVarInfo(runtimeVar) {
146
+ return {
147
+ varName: runtimeVar.__varName,
148
+ path: runtimeVar.__path
149
+ };
150
+ }
151
+ function toJqExpression(runtimeVar) {
152
+ const { varName, path } = getRuntimeVarInfo(runtimeVar);
153
+ const jqPath = path.length === 0 ? "." : "." + path.join(".");
154
+ return `$(echo "$${varName}" | jq -r '${jqPath}')`;
155
+ }
156
+ function toJqPath(runtimeVar) {
157
+ const { path } = getRuntimeVarInfo(runtimeVar);
158
+ return path.length === 0 ? "." : "." + path.join(".");
159
+ }
160
+
161
+ // src/components/runtime-fn.ts
162
+ var RUNTIME_FN_MARKER = /* @__PURE__ */ Symbol.for("react-agentic:runtime-fn");
163
+ var runtimeFnRegistry = /* @__PURE__ */ new Map();
164
+ function runtimeFn(fn) {
165
+ const fnName = fn.name;
166
+ if (!fnName) {
167
+ throw new Error(
168
+ "runtimeFn requires a named function. Anonymous functions cannot be extracted to runtime.js."
169
+ );
170
+ }
171
+ runtimeFnRegistry.set(fnName, fn);
172
+ const Call = (_props) => {
173
+ return null;
174
+ };
175
+ const wrapper = {
176
+ Call,
177
+ fnName,
178
+ fn,
179
+ __isRuntimeFn: true,
180
+ [RUNTIME_FN_MARKER]: true
181
+ };
182
+ return wrapper;
183
+ }
184
+ function isRuntimeFn(value) {
185
+ if (!value || typeof value !== "object") return false;
186
+ return value.__isRuntimeFn === true;
187
+ }
188
+ function getRuntimeFnRegistry() {
189
+ return new Map(runtimeFnRegistry);
190
+ }
191
+ function clearRuntimeFnRegistry() {
192
+ runtimeFnRegistry.clear();
193
+ }
194
+ function getRuntimeFn(name) {
195
+ return runtimeFnRegistry.get(name);
196
+ }
197
+
198
+ // src/components/control.ts
199
+ function If(_props) {
200
+ return null;
201
+ }
202
+ function Else(_props) {
203
+ return null;
204
+ }
205
+ function Loop(_props) {
206
+ return null;
207
+ }
208
+ function Break(_props) {
209
+ return null;
210
+ }
211
+ function Return(_props) {
212
+ return null;
213
+ }
214
+ var IF_MARKER = /* @__PURE__ */ Symbol.for("react-agentic:if");
215
+ var ELSE_MARKER = /* @__PURE__ */ Symbol.for("react-agentic:else");
216
+ var LOOP_MARKER = /* @__PURE__ */ Symbol.for("react-agentic:loop");
217
+ var BREAK_MARKER = /* @__PURE__ */ Symbol.for("react-agentic:break");
218
+ var RETURN_MARKER = /* @__PURE__ */ Symbol.for("react-agentic:return");
219
+ Object.defineProperty(If, IF_MARKER, { value: true });
220
+ Object.defineProperty(Else, ELSE_MARKER, { value: true });
221
+ Object.defineProperty(Loop, LOOP_MARKER, { value: true });
222
+ Object.defineProperty(Break, BREAK_MARKER, { value: true });
223
+ Object.defineProperty(Return, RETURN_MARKER, { value: true });
224
+
225
+ // src/components/ask-user.ts
226
+ function AskUser(_props) {
227
+ return null;
228
+ }
229
+ var ASK_USER_MARKER = /* @__PURE__ */ Symbol.for("react-agentic:ask-user");
230
+ Object.defineProperty(AskUser, ASK_USER_MARKER, { value: true });
231
+
232
+ // src/ir/nodes.ts
233
+ function assertNever(x) {
234
+ throw new Error(`Unexpected node: ${JSON.stringify(x)}`);
235
+ }
236
+
237
+ // src/ir/runtime-nodes.ts
238
+ function isRuntimeNode(node) {
239
+ if (!node || typeof node !== "object") return false;
240
+ const kind = node.kind;
241
+ return [
242
+ "runtimeVarDecl",
243
+ "runtimeCall",
244
+ "if",
245
+ "else",
246
+ "loop",
247
+ "break",
248
+ "return",
249
+ "askUser",
250
+ "spawnAgent"
251
+ ].includes(kind ?? "");
252
+ }
253
+ function isDocument(node) {
254
+ if (!node || typeof node !== "object") return false;
255
+ return node.kind === "document";
256
+ }
257
+
258
+ // src/index.ts
259
+ init_project();
260
+
261
+ // src/parser/index.ts
262
+ init_parser();
263
+
264
+ // src/parser/transformer.ts
265
+ import {
266
+ Node
267
+ } from "ts-morph";
268
+ init_parser();
269
+ function toSnakeCase(name) {
270
+ return name.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
271
+ }
272
+ var HTML_ELEMENTS = /* @__PURE__ */ new Set([
273
+ "h1",
274
+ "h2",
275
+ "h3",
276
+ "h4",
277
+ "h5",
278
+ "h6",
279
+ "p",
280
+ "div",
281
+ "span",
282
+ "ul",
283
+ "ol",
284
+ "li",
285
+ "a",
286
+ "b",
287
+ "i",
288
+ "strong",
289
+ "em",
290
+ "code",
291
+ "pre",
292
+ "blockquote",
293
+ "br",
294
+ "hr"
295
+ ]);
296
+ var INLINE_ELEMENTS = /* @__PURE__ */ new Set([
297
+ "a",
298
+ "b",
299
+ "i",
300
+ "strong",
301
+ "em",
302
+ "code",
303
+ "span",
304
+ "br"
305
+ ]);
306
+ function isInlineElement(tagName) {
307
+ return INLINE_ELEMENTS.has(tagName);
308
+ }
309
+ var SPECIAL_COMPONENTS = /* @__PURE__ */ new Set([
310
+ "Command",
311
+ "Markdown",
312
+ "XmlBlock",
313
+ "Agent",
314
+ "SpawnAgent",
315
+ "Assign",
316
+ "AssignGroup",
317
+ "If",
318
+ "Else",
319
+ "Loop",
320
+ "OnStatus",
321
+ "Skill",
322
+ "SkillFile",
323
+ "SkillStatic",
324
+ "ReadState",
325
+ "WriteState",
326
+ "MCPServer",
327
+ "MCPStdioServer",
328
+ "MCPHTTPServer",
329
+ "MCPConfig",
330
+ "State",
331
+ "Operation",
332
+ "Table",
333
+ "List",
334
+ // Semantic workflow components
335
+ "ExecutionContext",
336
+ "SuccessCriteria",
337
+ "OfferNext",
338
+ "XmlSection",
339
+ "DeviationRules",
340
+ "CommitRules",
341
+ "WaveExecution",
342
+ "CheckpointHandling",
343
+ // Step workflow primitive
344
+ "Step",
345
+ // Code block primitives
346
+ "Bash",
347
+ // File reading
348
+ "ReadFiles",
349
+ // Template primitives
350
+ "PromptTemplate"
351
+ ]);
352
+ function isCustomComponent(tagName) {
353
+ if (HTML_ELEMENTS.has(tagName)) return false;
354
+ if (SPECIAL_COMPONENTS.has(tagName)) return false;
355
+ return /^[A-Z]/.test(tagName);
356
+ }
357
+ var XML_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_.\-]*$/;
358
+ function isValidXmlName(name) {
359
+ if (!name) return false;
360
+ if (!XML_NAME_REGEX.test(name)) return false;
361
+ if (name.toLowerCase().startsWith("xml")) return false;
362
+ return true;
363
+ }
364
+ var Transformer = class {
365
+ /** Source file for component resolution (optional - only needed for composition) */
366
+ sourceFile;
367
+ /** Visited paths for circular import detection */
368
+ visitedPaths = /* @__PURE__ */ new Set();
369
+ /** Extracted useVariable declarations from source file */
370
+ variables = /* @__PURE__ */ new Map();
371
+ /** Extracted useOutput declarations: identifier name -> agent name */
372
+ outputs = /* @__PURE__ */ new Map();
373
+ /** Extracted useStateRef declarations: identifier name -> state key */
374
+ stateRefs = /* @__PURE__ */ new Map();
375
+ /** Current render props context for interpolation (set during Command/Agent transformation) */
376
+ renderPropsContext;
377
+ /**
378
+ * Create a TranspileError with source location context from a node
379
+ */
380
+ createError(message, node) {
381
+ const location = getNodeLocation(node);
382
+ const sourceCode = getSourceCode(node.getSourceFile());
383
+ return new TranspileError(message, location, sourceCode);
384
+ }
385
+ /**
386
+ * Build a TransformContext from instance state for delegation to document transformers
387
+ */
388
+ buildContext() {
389
+ return {
390
+ sourceFile: this.sourceFile,
391
+ visitedPaths: this.visitedPaths,
392
+ variables: this.variables,
393
+ outputs: this.outputs,
394
+ stateRefs: this.stateRefs,
395
+ renderPropsContext: this.renderPropsContext,
396
+ createError: this.createError.bind(this),
397
+ // Provide V1 transformBlockChildren - ignores ctx since we use instance state
398
+ transformBlockChildren: (children, _ctx) => this.transformBlockChildren(children)
399
+ };
400
+ }
401
+ /**
402
+ * Transform a root JSX element/fragment into AgentDocumentNode, SkillDocumentNode, MCPConfigDocumentNode, or StateDocumentNode
403
+ *
404
+ * Note: Command documents use the runtime transformer (transformRuntimeCommand) for runtime feature support.
405
+ *
406
+ * @param node - The root JSX element/fragment to transform
407
+ * @param sourceFile - Optional source file for component composition resolution
408
+ */
409
+ transform(node, sourceFile) {
410
+ this.sourceFile = sourceFile;
411
+ this.visitedPaths = /* @__PURE__ */ new Set();
412
+ this.variables = /* @__PURE__ */ new Map();
413
+ this.outputs = /* @__PURE__ */ new Map();
414
+ this.stateRefs = /* @__PURE__ */ new Map();
415
+ if (sourceFile) {
416
+ this.visitedPaths.add(sourceFile.getFilePath());
417
+ this.variables = extractVariableDeclarations(sourceFile);
418
+ this.outputs = this.extractOutputDeclarations(sourceFile);
419
+ this.stateRefs = this.extractStateRefDeclarations(sourceFile);
420
+ }
421
+ if (Node.isJsxElement(node) || Node.isJsxSelfClosingElement(node)) {
422
+ const name = getElementName(node);
423
+ if (name === "Command") {
424
+ return this.transformCommand(node);
425
+ }
426
+ if (name === "Agent") {
427
+ return this.transformAgent(node);
428
+ }
429
+ if (name === "Skill") {
430
+ return this.transformSkill(node);
431
+ }
432
+ if (name === "MCPConfig") {
433
+ return this.transformMCPConfig(node);
434
+ }
435
+ if (name === "State") {
436
+ return this.transformState(node);
437
+ }
438
+ }
439
+ if (Node.isJsxFragment(node)) {
440
+ throw new Error(
441
+ "JSX Fragment not supported. Use a document wrapper: <Agent>, <Skill>, <MCPConfig>, <State>, or use runtime transformer for <Command>."
442
+ );
443
+ }
444
+ throw new Error(
445
+ `Unknown root element. Use a document wrapper: <Agent>, <Skill>, <MCPConfig>, <State>, or use runtime transformer for <Command>.`
446
+ );
447
+ }
448
+ /**
449
+ * Merge Command props from spread attributes and explicit attributes
450
+ *
451
+ * Processes attributes in order - later props override earlier ones.
452
+ * Supports spread attributes: {...baseProps}
453
+ * Supports explicit attributes: name="value" or name={"value"} or name={["a", "b"]}
454
+ */
455
+ mergeCommandProps(opening) {
456
+ const merged = {};
457
+ for (const attr of opening.getAttributes()) {
458
+ if (Node.isJsxSpreadAttribute(attr)) {
459
+ const spreadProps = resolveSpreadAttribute(attr);
460
+ Object.assign(merged, spreadProps);
461
+ } else if (Node.isJsxAttribute(attr)) {
462
+ const attrName = attr.getNameNode().getText();
463
+ const stringValue = getAttributeValue(opening, attrName);
464
+ if (stringValue !== void 0) {
465
+ merged[attrName] = stringValue;
466
+ continue;
467
+ }
468
+ const arrayValue = getArrayAttributeValue(opening, attrName);
469
+ if (arrayValue !== void 0) {
470
+ merged[attrName] = arrayValue;
471
+ }
472
+ }
473
+ }
474
+ return merged;
475
+ }
476
+ /**
477
+ * Transform a Command element
478
+ *
479
+ * NOTE: This transformer is deprecated for Commands. Commands should use the runtime transformer
480
+ * which supports runtime features (useRuntimeVar, If/Else/Loop, etc.).
481
+ */
482
+ transformCommand(node) {
483
+ throw this.createError(
484
+ "This transformer is deprecated for Commands. Use the runtime transformer (transformRuntimeCommand) instead. Commands should import from react-agentic and use runtime features (useRuntimeVar, If, Else, Loop).",
485
+ node
486
+ );
487
+ }
488
+ /**
489
+ * Transform an Agent element to AgentDocumentNode with frontmatter
490
+ * Delegates to document.ts transformAgent()
491
+ */
492
+ transformAgent(node) {
493
+ return transformAgent(node, this.buildContext());
494
+ }
495
+ /**
496
+ * Transform a Skill element to SkillDocumentNode with frontmatter, body, files, and statics
497
+ * Delegates to document.ts transformSkill()
498
+ */
499
+ transformSkill(node) {
500
+ return transformSkill(node, this.buildContext());
501
+ }
502
+ transformFragmentChildren(node) {
503
+ return this.transformBlockChildren(node.getJsxChildren());
504
+ }
505
+ /**
506
+ * Transform arrow function body to IR blocks
507
+ * Handles both block body { return ... } and expression body
508
+ */
509
+ transformArrowFunctionBody(arrowFn) {
510
+ const body = arrowFn.getBody();
511
+ if (Node.isBlock(body)) {
512
+ const returnStmt = body.getStatements().find((stmt) => Node.isReturnStatement(stmt));
513
+ if (returnStmt && Node.isReturnStatement(returnStmt)) {
514
+ const returnExpr = returnStmt.getExpression();
515
+ if (returnExpr) {
516
+ if (Node.isJsxElement(returnExpr) || Node.isJsxSelfClosingElement(returnExpr)) {
517
+ const block = this.transformToBlock(returnExpr);
518
+ return block ? [block] : [];
519
+ }
520
+ if (Node.isJsxFragment(returnExpr)) {
521
+ return this.transformFragmentChildren(returnExpr);
522
+ }
523
+ if (Node.isParenthesizedExpression(returnExpr)) {
524
+ const inner = returnExpr.getExpression();
525
+ if (Node.isJsxElement(inner) || Node.isJsxSelfClosingElement(inner)) {
526
+ const block = this.transformToBlock(inner);
527
+ return block ? [block] : [];
528
+ }
529
+ if (Node.isJsxFragment(inner)) {
530
+ return this.transformFragmentChildren(inner);
531
+ }
532
+ }
533
+ }
534
+ }
535
+ return [];
536
+ }
537
+ if (Node.isJsxElement(body) || Node.isJsxSelfClosingElement(body)) {
538
+ const block = this.transformToBlock(body);
539
+ return block ? [block] : [];
540
+ }
541
+ if (Node.isJsxFragment(body)) {
542
+ return this.transformFragmentChildren(body);
543
+ }
544
+ if (Node.isParenthesizedExpression(body)) {
545
+ const inner = body.getExpression();
546
+ if (Node.isJsxElement(inner) || Node.isJsxSelfClosingElement(inner)) {
547
+ const block = this.transformToBlock(inner);
548
+ return block ? [block] : [];
549
+ }
550
+ if (Node.isJsxFragment(inner)) {
551
+ return this.transformFragmentChildren(inner);
552
+ }
553
+ }
554
+ return [];
555
+ }
556
+ transformToBlock(node) {
557
+ if (Node.isJsxText(node)) {
558
+ const text = extractText(node);
559
+ if (!text) return null;
560
+ return { kind: "paragraph", children: [{ kind: "text", value: text }] };
561
+ }
562
+ if (Node.isJsxElement(node) || Node.isJsxSelfClosingElement(node)) {
563
+ const name = getElementName(node);
564
+ return this.transformElement(name, node);
565
+ }
566
+ return null;
567
+ }
568
+ transformElement(name, node) {
569
+ const headingMatch = name.match(/^h([1-6])$/);
570
+ if (headingMatch) {
571
+ const level = parseInt(headingMatch[1], 10);
572
+ const children = Node.isJsxElement(node) ? this.transformInlineChildren(node) : [];
573
+ return { kind: "heading", level, children };
574
+ }
575
+ if (name === "p") {
576
+ const children = Node.isJsxElement(node) ? this.transformInlineChildren(node) : [];
577
+ return { kind: "paragraph", children };
578
+ }
579
+ if (name === "hr") {
580
+ return { kind: "thematicBreak" };
581
+ }
582
+ if (name === "ul") {
583
+ return this.transformList(node, false);
584
+ }
585
+ if (name === "ol") {
586
+ return this.transformList(node, true);
587
+ }
588
+ if (name === "blockquote") {
589
+ return this.transformBlockquote(node);
590
+ }
591
+ if (name === "pre") {
592
+ return this.transformCodeBlock(node);
593
+ }
594
+ if (name === "div") {
595
+ return this.transformDiv(node);
596
+ }
597
+ if (name === "XmlBlock") {
598
+ return this.transformXmlBlock(node);
599
+ }
600
+ if (name === "SpawnAgent") {
601
+ return this.transformSpawnAgent(node);
602
+ }
603
+ if (name === "Assign") {
604
+ return this.transformAssign(node);
605
+ }
606
+ if (name === "AssignGroup") {
607
+ return this.transformAssignGroup(node);
608
+ }
609
+ if (name === "If") {
610
+ return this.transformIf(node);
611
+ }
612
+ if (name === "Else") {
613
+ throw this.createError("<Else> must follow <If> as sibling", node);
614
+ }
615
+ if (name === "Loop") {
616
+ return this.transformLoop(node);
617
+ }
618
+ if (name === "OnStatus") {
619
+ return this.transformOnStatus(node);
620
+ }
621
+ if (name === "ReadState") {
622
+ return this.transformReadState(node);
623
+ }
624
+ if (name === "WriteState") {
625
+ return this.transformWriteState(node);
626
+ }
627
+ if (name === "Table") {
628
+ return this.transformTable(node);
629
+ }
630
+ if (name === "List") {
631
+ return this.transformPropList(node);
632
+ }
633
+ if (name === "ExecutionContext") {
634
+ return this.transformExecutionContext(node);
635
+ }
636
+ if (name === "SuccessCriteria") {
637
+ return this.transformSuccessCriteria(node);
638
+ }
639
+ if (name === "OfferNext") {
640
+ return this.transformOfferNext(node);
641
+ }
642
+ if (name === "XmlSection") {
643
+ return this.transformXmlSection(node);
644
+ }
645
+ if (name === "DeviationRules" || name === "CommitRules" || name === "WaveExecution" || name === "CheckpointHandling") {
646
+ return this.transformXmlWrapper(name, node);
647
+ }
648
+ if (name === "Step") {
649
+ return this.transformStep(node);
650
+ }
651
+ if (name === "Bash") {
652
+ return this.transformBash(node);
653
+ }
654
+ if (name === "ReadFiles") {
655
+ return this.transformReadFiles(node);
656
+ }
657
+ if (name === "PromptTemplate") {
658
+ return this.transformPromptTemplate(node);
659
+ }
660
+ if (name === "Markdown") {
661
+ return this.transformMarkdown(node);
662
+ }
663
+ if (isCustomComponent(name)) {
664
+ return this.transformCustomComponent(name, node);
665
+ }
666
+ throw this.createError(`Unsupported block element: <${name}>`, node);
667
+ }
668
+ transformList(node, ordered) {
669
+ if (Node.isJsxSelfClosingElement(node)) {
670
+ return { kind: "list", ordered, items: [] };
671
+ }
672
+ const items = [];
673
+ for (const child of node.getJsxChildren()) {
674
+ if (Node.isJsxElement(child)) {
675
+ const childName = getElementName(child);
676
+ if (childName === "li") {
677
+ items.push(this.transformListItem(child));
678
+ } else {
679
+ throw this.createError(`Expected <li> inside list, got <${childName}>`, child);
680
+ }
681
+ } else if (Node.isJsxText(child) && !child.containsOnlyTriviaWhiteSpaces()) {
682
+ throw this.createError("Lists can only contain <li> elements", child);
683
+ }
684
+ }
685
+ return { kind: "list", ordered, items };
686
+ }
687
+ transformListItem(node) {
688
+ const children = [];
689
+ const jsxChildren = node.getJsxChildren();
690
+ const isBlockContent = (child) => {
691
+ if (Node.isJsxElement(child) || Node.isJsxSelfClosingElement(child)) {
692
+ const name = getElementName(child);
693
+ return name === "ul" || name === "ol" || name === "p";
694
+ }
695
+ return false;
696
+ };
697
+ let inlineSequence = [];
698
+ const flushInlineSequence = () => {
699
+ if (inlineSequence.length === 0) return;
700
+ const inlines = [];
701
+ for (const child of inlineSequence) {
702
+ const inline = this.transformToInline(child);
703
+ if (inline) inlines.push(inline);
704
+ }
705
+ this.trimBoundaryTextNodes(inlines);
706
+ if (inlines.length > 0) {
707
+ const lastChild = children[children.length - 1];
708
+ if (lastChild?.kind === "paragraph") {
709
+ lastChild.children.push(...inlines);
710
+ } else {
711
+ children.push({ kind: "paragraph", children: inlines });
712
+ }
713
+ }
714
+ inlineSequence = [];
715
+ };
716
+ for (const child of jsxChildren) {
717
+ if (isBlockContent(child)) {
718
+ flushInlineSequence();
719
+ const childName = getElementName(child);
720
+ if (childName === "ul" || childName === "ol") {
721
+ const nestedList = this.transformElement(childName, child);
722
+ if (nestedList) children.push(nestedList);
723
+ } else if (childName === "p") {
724
+ const para = this.transformElement(childName, child);
725
+ if (para) children.push(para);
726
+ }
727
+ } else {
728
+ inlineSequence.push(child);
729
+ }
730
+ }
731
+ flushInlineSequence();
732
+ return { kind: "listItem", children };
733
+ }
734
+ transformBlockquote(node) {
735
+ if (Node.isJsxSelfClosingElement(node)) {
736
+ return { kind: "blockquote", children: [] };
737
+ }
738
+ const children = this.transformBlockChildren(node.getJsxChildren());
739
+ return { kind: "blockquote", children };
740
+ }
741
+ transformCodeBlock(node) {
742
+ if (Node.isJsxSelfClosingElement(node)) {
743
+ return { kind: "codeBlock", content: "" };
744
+ }
745
+ const children = node.getJsxChildren();
746
+ for (const child of children) {
747
+ if (Node.isJsxElement(child) && getElementName(child) === "code") {
748
+ const language = getAttributeValue(
749
+ child.getOpeningElement(),
750
+ "className"
751
+ )?.replace(/^language-/, "");
752
+ const content2 = this.extractCodeContent(child);
753
+ return { kind: "codeBlock", language, content: content2 };
754
+ }
755
+ }
756
+ const content = this.extractCodeContent(node);
757
+ return { kind: "codeBlock", content };
758
+ }
759
+ extractCodeContent(node) {
760
+ const parts = [];
761
+ for (const child of node.getJsxChildren()) {
762
+ if (Node.isJsxText(child)) {
763
+ parts.push(child.getText());
764
+ } else if (Node.isJsxExpression(child)) {
765
+ const expr = child.getExpression();
766
+ if (expr) {
767
+ if (Node.isStringLiteral(expr)) {
768
+ parts.push(expr.getLiteralValue());
769
+ } else if (Node.isNoSubstitutionTemplateLiteral(expr)) {
770
+ parts.push(expr.getLiteralValue());
771
+ } else if (Node.isTemplateExpression(expr)) {
772
+ parts.push(this.extractTemplateText(expr));
773
+ }
774
+ }
775
+ }
776
+ }
777
+ return parts.join("").trim();
778
+ }
779
+ transformInlineChildren(node) {
780
+ const children = node.getJsxChildren();
781
+ const inlines = [];
782
+ for (const child of children) {
783
+ const inline = this.transformToInline(child);
784
+ if (inline) inlines.push(inline);
785
+ }
786
+ this.trimBoundaryTextNodes(inlines);
787
+ return inlines;
788
+ }
789
+ trimBoundaryTextNodes(inlines) {
790
+ if (inlines.length === 0) return;
791
+ const first = inlines[0];
792
+ if (first.kind === "text") {
793
+ first.value = first.value.trimStart();
794
+ if (!first.value) {
795
+ inlines.shift();
796
+ }
797
+ }
798
+ if (inlines.length === 0) return;
799
+ const last = inlines[inlines.length - 1];
800
+ if (last.kind === "text") {
801
+ last.value = last.value.trimEnd();
802
+ if (!last.value) {
803
+ inlines.pop();
804
+ }
805
+ }
806
+ }
807
+ transformToInline(node) {
808
+ if (Node.isJsxText(node)) {
809
+ const text = extractInlineText(node);
810
+ if (!text) return null;
811
+ return { kind: "text", value: text };
812
+ }
813
+ if (Node.isJsxSelfClosingElement(node)) {
814
+ const name = getElementName(node);
815
+ if (name === "br") {
816
+ return { kind: "lineBreak" };
817
+ }
818
+ throw this.createError(`Unsupported inline self-closing element: <${name}>`, node);
819
+ }
820
+ if (Node.isJsxElement(node)) {
821
+ const name = getElementName(node);
822
+ return this.transformInlineElement(name, node);
823
+ }
824
+ if (Node.isJsxExpression(node)) {
825
+ const expr = node.getExpression();
826
+ if (!expr) return null;
827
+ if (Node.isStringLiteral(expr)) {
828
+ const value = expr.getLiteralValue();
829
+ if (value) {
830
+ return { kind: "text", value };
831
+ }
832
+ return null;
833
+ }
834
+ if (Node.isCallExpression(expr)) {
835
+ const propAccess = expr.getExpression();
836
+ if (Node.isPropertyAccessExpression(propAccess)) {
837
+ const methodName = propAccess.getName();
838
+ const objExpr = propAccess.getExpression();
839
+ if (methodName === "field" && Node.isIdentifier(objExpr)) {
840
+ const outputName = objExpr.getText();
841
+ if (this.outputs.has(outputName)) {
842
+ const args = expr.getArguments();
843
+ if (args.length >= 1) {
844
+ const keyArg = args[0];
845
+ if (Node.isStringLiteral(keyArg)) {
846
+ const fieldKey = keyArg.getLiteralValue();
847
+ return { kind: "text", value: `{output.${fieldKey}}` };
848
+ }
849
+ }
850
+ }
851
+ }
852
+ }
853
+ }
854
+ if (Node.isPropertyAccessExpression(expr)) {
855
+ const objExpr = expr.getExpression();
856
+ const propName = expr.getName();
857
+ if (Node.isIdentifier(objExpr) && this.renderPropsContext) {
858
+ const objName = objExpr.getText();
859
+ if (objName === this.renderPropsContext.paramName) {
860
+ const value = this.renderPropsContext.values[propName];
861
+ if (value !== void 0) {
862
+ return { kind: "text", value };
863
+ }
864
+ }
865
+ }
866
+ }
867
+ return null;
868
+ }
869
+ return null;
870
+ }
871
+ transformInlineElement(name, node) {
872
+ if (name === "b" || name === "strong") {
873
+ return { kind: "bold", children: this.transformInlineChildren(node) };
874
+ }
875
+ if (name === "i" || name === "em") {
876
+ return { kind: "italic", children: this.transformInlineChildren(node) };
877
+ }
878
+ if (name === "code") {
879
+ const text = this.extractAllText(node);
880
+ return { kind: "inlineCode", value: text };
881
+ }
882
+ if (name === "a") {
883
+ return this.transformLink(node);
884
+ }
885
+ throw this.createError(`Unsupported inline element: <${name}>`, node);
886
+ }
887
+ extractAllText(node) {
888
+ const parts = [];
889
+ for (const child of node.getJsxChildren()) {
890
+ if (Node.isJsxText(child)) {
891
+ const text = extractText(child);
892
+ if (text) parts.push(text);
893
+ } else if (Node.isJsxExpression(child)) {
894
+ const expr = child.getExpression();
895
+ if (expr) {
896
+ if (Node.isStringLiteral(expr)) {
897
+ parts.push(expr.getLiteralValue());
898
+ } else if (Node.isNoSubstitutionTemplateLiteral(expr)) {
899
+ parts.push(expr.getLiteralValue());
900
+ } else if (Node.isTemplateExpression(expr)) {
901
+ parts.push(this.extractTemplateText(expr));
902
+ }
903
+ }
904
+ }
905
+ }
906
+ return parts.join("");
907
+ }
908
+ transformLink(node) {
909
+ const href = getAttributeValue(node.getOpeningElement(), "href");
910
+ if (!href) {
911
+ throw this.createError("<a> element requires href attribute", node);
912
+ }
913
+ const children = this.transformInlineChildren(node);
914
+ return { kind: "link", url: href, children };
915
+ }
916
+ transformDiv(node) {
917
+ const openingElement = Node.isJsxElement(node) ? node.getOpeningElement() : node;
918
+ const nameAttr = getAttributeValue(openingElement, "name");
919
+ const children = Node.isJsxElement(node) ? this.transformMixedChildren(node.getJsxChildren()) : [];
920
+ if (!nameAttr) {
921
+ return {
922
+ kind: "group",
923
+ children
924
+ };
925
+ }
926
+ if (!isValidXmlName(nameAttr)) {
927
+ throw this.createError(
928
+ `Invalid XML tag name '${nameAttr}' - must start with letter/underscore, contain only letters, digits, underscores, hyphens, or periods, and not start with 'xml'`,
929
+ node
930
+ );
931
+ }
932
+ const attributes = {};
933
+ for (const attr of openingElement.getAttributes()) {
934
+ if (Node.isJsxAttribute(attr)) {
935
+ const attrName = attr.getNameNode().getText();
936
+ if (attrName !== "name") {
937
+ const value = getAttributeValue(openingElement, attrName);
938
+ if (value !== void 0) {
939
+ attributes[attrName] = value;
940
+ }
941
+ }
942
+ }
943
+ }
944
+ return {
945
+ kind: "xmlBlock",
946
+ name: nameAttr,
947
+ attributes: Object.keys(attributes).length > 0 ? attributes : void 0,
948
+ children
949
+ };
950
+ }
951
+ /**
952
+ * Transform mixed children (inline + block elements)
953
+ * Consecutive inline elements and text are wrapped in a single paragraph
954
+ * Block elements are transformed normally
955
+ */
956
+ transformMixedChildren(jsxChildren) {
957
+ const blocks = [];
958
+ let inlineAccumulator = [];
959
+ const flushInline = () => {
960
+ if (inlineAccumulator.length > 0) {
961
+ const inlineNodes = this.transformInlineNodes(inlineAccumulator);
962
+ if (inlineNodes.length > 0) {
963
+ blocks.push({ kind: "paragraph", children: inlineNodes });
964
+ }
965
+ inlineAccumulator = [];
966
+ }
967
+ };
968
+ for (const child of jsxChildren) {
969
+ if (Node.isJsxText(child)) {
970
+ const text = extractText(child);
971
+ if (text) {
972
+ inlineAccumulator.push(child);
973
+ }
974
+ continue;
975
+ }
976
+ if (Node.isJsxElement(child) || Node.isJsxSelfClosingElement(child)) {
977
+ const name = getElementName(child);
978
+ if (isInlineElement(name)) {
979
+ inlineAccumulator.push(child);
980
+ } else {
981
+ flushInline();
982
+ const block = this.transformToBlock(child);
983
+ if (block) blocks.push(block);
984
+ }
985
+ } else if (Node.isJsxExpression(child)) {
986
+ inlineAccumulator.push(child);
987
+ }
988
+ }
989
+ flushInline();
990
+ return blocks;
991
+ }
992
+ /**
993
+ * Transform a list of nodes to inline nodes
994
+ * Used by transformMixedChildren for inline accumulation
995
+ */
996
+ transformInlineNodes(nodes) {
997
+ const result = [];
998
+ for (const node of nodes) {
999
+ if (Node.isJsxText(node)) {
1000
+ const text = extractText(node);
1001
+ if (text) {
1002
+ result.push({ kind: "text", value: text });
1003
+ }
1004
+ } else if (Node.isJsxElement(node)) {
1005
+ const name = getElementName(node);
1006
+ const inlineNode = this.transformInlineElement(name, node);
1007
+ if (inlineNode) result.push(inlineNode);
1008
+ } else if (Node.isJsxSelfClosingElement(node)) {
1009
+ const name = getElementName(node);
1010
+ if (name === "br") {
1011
+ result.push({ kind: "lineBreak" });
1012
+ }
1013
+ } else if (Node.isJsxExpression(node)) {
1014
+ const expr = node.getExpression();
1015
+ if (expr) {
1016
+ const text = expr.getText();
1017
+ const cleaned = text.replace(/^['"`]|['"`]$/g, "");
1018
+ if (cleaned) {
1019
+ result.push({ kind: "text", value: cleaned });
1020
+ }
1021
+ }
1022
+ }
1023
+ }
1024
+ return result;
1025
+ }
1026
+ transformXmlBlock(node) {
1027
+ const openingElement = Node.isJsxElement(node) ? node.getOpeningElement() : node;
1028
+ const nameAttr = getAttributeValue(openingElement, "name");
1029
+ if (!nameAttr) {
1030
+ throw this.createError("XmlBlock requires name prop", node);
1031
+ }
1032
+ if (!isValidXmlName(nameAttr)) {
1033
+ throw this.createError(
1034
+ `Invalid XML tag name '${nameAttr}' - must start with letter/underscore, contain only letters, digits, underscores, hyphens, or periods, and not start with 'xml'`,
1035
+ node
1036
+ );
1037
+ }
1038
+ const children = Node.isJsxElement(node) ? this.transformBlockChildren(node.getJsxChildren()) : [];
1039
+ return {
1040
+ kind: "xmlBlock",
1041
+ name: nameAttr,
1042
+ children
1043
+ };
1044
+ }
1045
+ /**
1046
+ * Transform Table component to TableNode IR
1047
+ */
1048
+ transformTable(node) {
1049
+ const opening = Node.isJsxElement(node) ? node.getOpeningElement() : node;
1050
+ const headers = getArrayAttributeValue(opening, "headers");
1051
+ const rows = this.parseRowsAttribute(opening);
1052
+ const alignRaw = getArrayAttributeValue(opening, "align");
1053
+ const emptyCell = getAttributeValue(opening, "emptyCell");
1054
+ const align = alignRaw?.map((a) => {
1055
+ if (a === "left" || a === "center" || a === "right") return a;
1056
+ return "left";
1057
+ });
1058
+ return {
1059
+ kind: "table",
1060
+ headers: headers?.length ? headers : void 0,
1061
+ rows,
1062
+ align,
1063
+ emptyCell: emptyCell || void 0
1064
+ };
1065
+ }
1066
+ /**
1067
+ * Parse rows attribute (array of arrays)
1068
+ */
1069
+ parseRowsAttribute(opening) {
1070
+ const attr = opening.getAttribute("rows");
1071
+ if (!attr || !Node.isJsxAttribute(attr)) return [];
1072
+ const init = attr.getInitializer();
1073
+ if (!init || !Node.isJsxExpression(init)) return [];
1074
+ const expr = init.getExpression();
1075
+ if (!expr || !Node.isArrayLiteralExpression(expr)) return [];
1076
+ const rows = [];
1077
+ for (const element of expr.getElements()) {
1078
+ if (Node.isArrayLiteralExpression(element)) {
1079
+ const row = [];
1080
+ for (const cell of element.getElements()) {
1081
+ if (Node.isStringLiteral(cell)) {
1082
+ row.push(cell.getLiteralValue());
1083
+ } else if (Node.isNumericLiteral(cell)) {
1084
+ row.push(cell.getLiteralValue().toString());
1085
+ } else if (Node.isPropertyAccessExpression(cell)) {
1086
+ const interpolated = this.interpolatePropertyAccess(cell);
1087
+ row.push(interpolated ?? cell.getText());
1088
+ } else {
1089
+ row.push(cell.getText());
1090
+ }
1091
+ }
1092
+ rows.push(row);
1093
+ }
1094
+ }
1095
+ return rows;
1096
+ }
1097
+ /**
1098
+ * Interpolate a PropertyAccessExpression if it references render props context
1099
+ * Returns the interpolated value or null if not a context access
1100
+ */
1101
+ interpolatePropertyAccess(expr) {
1102
+ const objExpr = expr.getExpression();
1103
+ const propName = expr.getName();
1104
+ if (Node.isIdentifier(objExpr) && this.renderPropsContext) {
1105
+ const objName = objExpr.getText();
1106
+ if (objName === this.renderPropsContext.paramName) {
1107
+ const value = this.renderPropsContext.values[propName];
1108
+ if (value !== void 0) {
1109
+ return value;
1110
+ }
1111
+ }
1112
+ }
1113
+ return null;
1114
+ }
1115
+ /**
1116
+ * Transform List component (prop-based) to ListNode IR
1117
+ * This is separate from HTML <ul>/<ol> transformation
1118
+ */
1119
+ transformPropList(node) {
1120
+ const opening = Node.isJsxElement(node) ? node.getOpeningElement() : node;
1121
+ const items = getArrayAttributeValue(opening, "items") ?? [];
1122
+ const ordered = getAttributeValue(opening, "ordered") === "true" || opening.getAttribute("ordered") !== void 0;
1123
+ let start = void 0;
1124
+ const startAttr = opening.getAttribute("start");
1125
+ if (startAttr && Node.isJsxAttribute(startAttr)) {
1126
+ const init = startAttr.getInitializer();
1127
+ if (init && Node.isJsxExpression(init)) {
1128
+ const expr = init.getExpression();
1129
+ if (expr && Node.isNumericLiteral(expr)) {
1130
+ start = expr.getLiteralValue();
1131
+ }
1132
+ }
1133
+ }
1134
+ const listItems = items.map((item) => ({
1135
+ kind: "listItem",
1136
+ children: [{
1137
+ kind: "paragraph",
1138
+ children: [{ kind: "text", value: String(item) }]
1139
+ }]
1140
+ }));
1141
+ return {
1142
+ kind: "list",
1143
+ ordered,
1144
+ items: listItems,
1145
+ start
1146
+ };
1147
+ }
1148
+ // ============================================================================
1149
+ // Semantic Workflow Components
1150
+ // ============================================================================
1151
+ transformExecutionContext(node) {
1152
+ const opening = Node.isJsxElement(node) ? node.getOpeningElement() : node;
1153
+ const paths = getArrayAttributeValue(opening, "paths") ?? [];
1154
+ const prefix = getAttributeValue(opening, "prefix") ?? "@";
1155
+ const children = [];
1156
+ if (Node.isJsxElement(node)) {
1157
+ for (const child of node.getJsxChildren()) {
1158
+ const block = this.transformToBlock(child);
1159
+ if (block) children.push(block);
1160
+ }
1161
+ }
1162
+ return {
1163
+ kind: "executionContext",
1164
+ paths,
1165
+ prefix,
1166
+ children
1167
+ };
1168
+ }
1169
+ transformSuccessCriteria(node) {
1170
+ const opening = Node.isJsxElement(node) ? node.getOpeningElement() : node;
1171
+ const items = this.parseSuccessCriteriaItems(opening);
1172
+ return {
1173
+ kind: "successCriteria",
1174
+ items
1175
+ };
1176
+ }
1177
+ /**
1178
+ * Parse items attribute for SuccessCriteria
1179
+ * Handles both string shorthand and {text, checked} objects
1180
+ */
1181
+ parseSuccessCriteriaItems(opening) {
1182
+ const attr = opening.getAttribute("items");
1183
+ if (!attr || !Node.isJsxAttribute(attr)) return [];
1184
+ const init = attr.getInitializer();
1185
+ if (!init || !Node.isJsxExpression(init)) return [];
1186
+ const expr = init.getExpression();
1187
+ if (!expr || !Node.isArrayLiteralExpression(expr)) return [];
1188
+ const items = [];
1189
+ for (const element of expr.getElements()) {
1190
+ if (Node.isStringLiteral(element)) {
1191
+ items.push({ text: element.getLiteralValue(), checked: false });
1192
+ } else if (Node.isObjectLiteralExpression(element)) {
1193
+ let text = "";
1194
+ let checked = false;
1195
+ for (const prop of element.getProperties()) {
1196
+ if (Node.isPropertyAssignment(prop)) {
1197
+ const propName = prop.getName();
1198
+ const propInit = prop.getInitializer();
1199
+ if (propName === "text" && propInit && Node.isStringLiteral(propInit)) {
1200
+ text = propInit.getLiteralValue();
1201
+ } else if (propName === "checked" && propInit) {
1202
+ if (propInit.getKind() === 112) {
1203
+ checked = true;
1204
+ } else if (propInit.getKind() === 97) {
1205
+ checked = false;
1206
+ }
1207
+ }
1208
+ }
1209
+ }
1210
+ items.push({ text, checked });
1211
+ }
1212
+ }
1213
+ return items;
1214
+ }
1215
+ transformOfferNext(node) {
1216
+ const opening = Node.isJsxElement(node) ? node.getOpeningElement() : node;
1217
+ const routes = this.parseOfferNextRoutes(opening);
1218
+ return {
1219
+ kind: "offerNext",
1220
+ routes
1221
+ };
1222
+ }
1223
+ /**
1224
+ * Parse routes attribute for OfferNext
1225
+ * Each route is an object with name, path, and optional description
1226
+ */
1227
+ parseOfferNextRoutes(opening) {
1228
+ const attr = opening.getAttribute("routes");
1229
+ if (!attr || !Node.isJsxAttribute(attr)) return [];
1230
+ const init = attr.getInitializer();
1231
+ if (!init || !Node.isJsxExpression(init)) return [];
1232
+ const expr = init.getExpression();
1233
+ if (!expr || !Node.isArrayLiteralExpression(expr)) return [];
1234
+ const routes = [];
1235
+ for (const element of expr.getElements()) {
1236
+ if (Node.isObjectLiteralExpression(element)) {
1237
+ let name = "";
1238
+ let path = "";
1239
+ let description = void 0;
1240
+ for (const prop of element.getProperties()) {
1241
+ if (Node.isPropertyAssignment(prop)) {
1242
+ const propName = prop.getName();
1243
+ const propInit = prop.getInitializer();
1244
+ if (propInit && Node.isStringLiteral(propInit)) {
1245
+ const value = propInit.getLiteralValue();
1246
+ if (propName === "name") {
1247
+ name = value;
1248
+ } else if (propName === "path") {
1249
+ path = value;
1250
+ } else if (propName === "description") {
1251
+ description = value;
1252
+ }
1253
+ }
1254
+ }
1255
+ }
1256
+ if (name && path) {
1257
+ routes.push({ name, path, description });
1258
+ }
1259
+ }
1260
+ }
1261
+ return routes;
1262
+ }
1263
+ /**
1264
+ * Transform Step component to StepNode IR
1265
+ */
1266
+ transformStep(node) {
1267
+ const openingElement = Node.isJsxElement(node) ? node.getOpeningElement() : node;
1268
+ let stepNumber = void 0;
1269
+ const numberAttr = openingElement.getAttribute("number");
1270
+ if (numberAttr && Node.isJsxAttribute(numberAttr)) {
1271
+ const init = numberAttr.getInitializer();
1272
+ if (init) {
1273
+ if (Node.isStringLiteral(init)) {
1274
+ stepNumber = init.getLiteralValue();
1275
+ } else if (Node.isJsxExpression(init)) {
1276
+ const expr = init.getExpression();
1277
+ if (expr) {
1278
+ if (Node.isNumericLiteral(expr)) {
1279
+ stepNumber = String(expr.getLiteralValue());
1280
+ } else if (Node.isStringLiteral(expr)) {
1281
+ stepNumber = expr.getLiteralValue();
1282
+ }
1283
+ }
1284
+ }
1285
+ }
1286
+ }
1287
+ const name = getAttributeValue(openingElement, "name");
1288
+ if (!stepNumber) {
1289
+ throw this.createError("Step requires number prop", openingElement);
1290
+ }
1291
+ if (!name) {
1292
+ throw this.createError("Step requires name prop", openingElement);
1293
+ }
1294
+ const variantAttr = getAttributeValue(openingElement, "variant");
1295
+ let variant = "heading";
1296
+ if (variantAttr === "heading" || variantAttr === "bold" || variantAttr === "xml") {
1297
+ variant = variantAttr;
1298
+ }
1299
+ const children = Node.isJsxElement(node) ? this.transformBlockChildren(node.getJsxChildren()) : [];
1300
+ return {
1301
+ kind: "step",
1302
+ number: stepNumber,
1303
+ name,
1304
+ variant,
1305
+ children
1306
+ };
1307
+ }
1308
+ /**
1309
+ * Transform <Bash> to CodeBlockNode with language 'bash'
1310
+ *
1311
+ * <Bash>ls -la</Bash>
1312
+ * becomes:
1313
+ * ```bash
1314
+ * ls -la
1315
+ * ```
1316
+ */
1317
+ transformBash(node) {
1318
+ if (Node.isJsxSelfClosingElement(node)) {
1319
+ return { kind: "codeBlock", language: "bash", content: "" };
1320
+ }
1321
+ const content = this.extractCodeContent(node);
1322
+ return {
1323
+ kind: "codeBlock",
1324
+ language: "bash",
1325
+ content
1326
+ };
1327
+ }
1328
+ /**
1329
+ * Transform <ReadFiles> to ReadFilesNode
1330
+ *
1331
+ * Extracts the files prop (which should be a defineFiles() result)
1332
+ * and creates a ReadFilesNode with file entries.
1333
+ */
1334
+ transformReadFiles(node) {
1335
+ const opening = Node.isJsxElement(node) ? node.getOpeningElement() : node;
1336
+ const filesAttr = opening.getAttribute("files");
1337
+ if (!filesAttr || !Node.isJsxAttribute(filesAttr)) {
1338
+ throw this.createError("ReadFiles requires files prop", node);
1339
+ }
1340
+ const init = filesAttr.getInitializer();
1341
+ if (!init || !Node.isJsxExpression(init)) {
1342
+ throw this.createError("ReadFiles files prop must be a JSX expression", node);
1343
+ }
1344
+ const expr = init.getExpression();
1345
+ if (!expr) {
1346
+ throw this.createError("ReadFiles files prop expression is empty", node);
1347
+ }
1348
+ const files = [];
1349
+ if (Node.isIdentifier(expr)) {
1350
+ const varName = expr.getText();
1351
+ const sourceFile = this.sourceFile;
1352
+ if (sourceFile) {
1353
+ const statements = sourceFile.getStatements();
1354
+ for (const stmt of statements) {
1355
+ if (Node.isVariableStatement(stmt)) {
1356
+ for (const decl of stmt.getDeclarationList().getDeclarations()) {
1357
+ if (decl.getName() === varName) {
1358
+ const initializer = decl.getInitializer();
1359
+ if (initializer && Node.isCallExpression(initializer)) {
1360
+ const callee = initializer.getExpression();
1361
+ if (Node.isIdentifier(callee) && callee.getText() === "defineFiles") {
1362
+ const args = initializer.getArguments();
1363
+ if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
1364
+ this.extractFilesFromSchema(args[0], files);
1365
+ }
1366
+ }
1367
+ }
1368
+ }
1369
+ }
1370
+ }
1371
+ }
1372
+ }
1373
+ } else if (Node.isCallExpression(expr)) {
1374
+ const callee = expr.getExpression();
1375
+ if (Node.isIdentifier(callee) && callee.getText() === "defineFiles") {
1376
+ const args = expr.getArguments();
1377
+ if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
1378
+ this.extractFilesFromSchema(args[0], files);
1379
+ }
1380
+ }
1381
+ }
1382
+ if (files.length === 0) {
1383
+ throw this.createError("ReadFiles: could not extract files from defineFiles schema", node);
1384
+ }
1385
+ return {
1386
+ kind: "readFiles",
1387
+ files
1388
+ };
1389
+ }
1390
+ /**
1391
+ * Extract file entries from defineFiles schema object literal
1392
+ */
1393
+ extractFilesFromSchema(obj, files) {
1394
+ for (const prop of obj.getProperties()) {
1395
+ if (Node.isPropertyAssignment(prop)) {
1396
+ const key = prop.getName();
1397
+ const value = prop.getInitializer();
1398
+ if (value && Node.isObjectLiteralExpression(value)) {
1399
+ let path;
1400
+ let required = true;
1401
+ for (const fileProp of value.getProperties()) {
1402
+ if (Node.isPropertyAssignment(fileProp)) {
1403
+ const propName = fileProp.getName();
1404
+ const propValue = fileProp.getInitializer();
1405
+ if (propName === "path" && propValue) {
1406
+ if (Node.isStringLiteral(propValue)) {
1407
+ path = propValue.getLiteralValue();
1408
+ } else if (Node.isNoSubstitutionTemplateLiteral(propValue)) {
1409
+ path = propValue.getLiteralValue();
1410
+ } else if (Node.isTemplateExpression(propValue)) {
1411
+ path = this.extractTemplatePath(propValue);
1412
+ }
1413
+ } else if (propName === "required" && propValue) {
1414
+ if (propValue.getText() === "false") {
1415
+ required = false;
1416
+ }
1417
+ }
1418
+ }
1419
+ }
1420
+ if (path) {
1421
+ const varName = key.replace(/([a-z])([A-Z])/g, "$1_$2").toUpperCase() + "_CONTENT";
1422
+ files.push({ varName, path, required });
1423
+ }
1424
+ }
1425
+ }
1426
+ }
1427
+ }
1428
+ /**
1429
+ * Extract path from template expression, preserving ${} for shell
1430
+ */
1431
+ extractTemplatePath(tmpl) {
1432
+ let result = tmpl.getHead().getLiteralText();
1433
+ for (const span of tmpl.getTemplateSpans()) {
1434
+ const spanExpr = span.getExpression();
1435
+ result += "${" + spanExpr.getText() + "}";
1436
+ result += span.getLiteral().getLiteralText();
1437
+ }
1438
+ return result;
1439
+ }
1440
+ /**
1441
+ * Transform <PromptTemplate> to PromptTemplateNode
1442
+ *
1443
+ * <PromptTemplate>
1444
+ * <XmlBlock name="objective">...</XmlBlock>
1445
+ * </PromptTemplate>
1446
+ *
1447
+ * Becomes:
1448
+ * ```markdown
1449
+ * <objective>
1450
+ * ...
1451
+ * </objective>
1452
+ * ```
1453
+ */
1454
+ transformPromptTemplate(node) {
1455
+ if (Node.isJsxSelfClosingElement(node)) {
1456
+ return { kind: "promptTemplate", children: [] };
1457
+ }
1458
+ const children = this.transformBlockChildren(node.getJsxChildren());
1459
+ return {
1460
+ kind: "promptTemplate",
1461
+ children
1462
+ };
1463
+ }
1464
+ transformXmlSection(node) {
1465
+ const opening = Node.isJsxElement(node) ? node.getOpeningElement() : node;
1466
+ const name = getAttributeValue(opening, "name") ?? "section";
1467
+ const children = [];
1468
+ if (Node.isJsxElement(node)) {
1469
+ for (const child of node.getJsxChildren()) {
1470
+ const block = this.transformToBlock(child);
1471
+ if (block) children.push(block);
1472
+ }
1473
+ }
1474
+ return {
1475
+ kind: "xmlBlock",
1476
+ name,
1477
+ children
1478
+ };
1479
+ }
1480
+ transformXmlWrapper(componentName, node) {
1481
+ const tagName = toSnakeCase(componentName);
1482
+ const children = [];
1483
+ if (Node.isJsxElement(node)) {
1484
+ for (const child of node.getJsxChildren()) {
1485
+ const block = this.transformToBlock(child);
1486
+ if (block) children.push(block);
1487
+ }
1488
+ }
1489
+ return {
1490
+ kind: "xmlBlock",
1491
+ name: tagName,
1492
+ children
1493
+ };
1494
+ }
1495
+ transformMarkdown(node) {
1496
+ if (Node.isJsxSelfClosingElement(node)) {
1497
+ return { kind: "raw", content: "" };
1498
+ }
1499
+ const parts = [];
1500
+ const jsxChildren = node.getJsxChildren();
1501
+ for (let i = 0; i < jsxChildren.length; i++) {
1502
+ const child = jsxChildren[i];
1503
+ const prev = parts[parts.length - 1];
1504
+ if (Node.isJsxText(child)) {
1505
+ let text = child.getText();
1506
+ if (text === "" && i > 0 && i < jsxChildren.length - 1) {
1507
+ const prevChild = jsxChildren[i - 1];
1508
+ const nextChild = jsxChildren[i + 1];
1509
+ if (Node.isJsxExpression(prevChild) && Node.isJsxExpression(nextChild)) {
1510
+ parts.push("\n");
1511
+ continue;
1512
+ }
1513
+ }
1514
+ if (prev && !/\s$/.test(prev) && !/^\s/.test(text) && text !== "") {
1515
+ if (/^#{1,6}\s/.test(text) || /^```/.test(text)) {
1516
+ parts.push("\n\n");
1517
+ } else if (/^\*\*/.test(text)) {
1518
+ parts.push("\n");
1519
+ } else if (/^[-*]\s/.test(text) || /^\d+\.\s/.test(text)) {
1520
+ parts.push("\n");
1521
+ } else if (/^[|]/.test(text)) {
1522
+ if (/[|]\s*$/.test(prev)) {
1523
+ parts.push("\n");
1524
+ } else {
1525
+ parts.push(" ");
1526
+ }
1527
+ } else if (!/^[.,;:!?)}\]>`"'/]/.test(text)) {
1528
+ parts.push(" ");
1529
+ }
1530
+ }
1531
+ parts.push(text);
1532
+ } else if (Node.isJsxExpression(child)) {
1533
+ const expr = child.getExpression();
1534
+ if (expr) {
1535
+ if (Node.isStringLiteral(expr)) {
1536
+ parts.push(expr.getLiteralValue());
1537
+ } else if (Node.isNoSubstitutionTemplateLiteral(expr)) {
1538
+ parts.push(expr.getLiteralValue());
1539
+ } else if (Node.isTemplateExpression(expr)) {
1540
+ let result = expr.getHead().getLiteralText();
1541
+ for (const span of expr.getTemplateSpans()) {
1542
+ const spanExpr = span.getExpression();
1543
+ if (Node.isIdentifier(spanExpr)) {
1544
+ result += `\${${spanExpr.getText()}}`;
1545
+ } else if (Node.isStringLiteral(spanExpr)) {
1546
+ result += spanExpr.getLiteralValue();
1547
+ } else {
1548
+ result += `\${${spanExpr.getText()}}`;
1549
+ }
1550
+ const literal = span.getLiteral();
1551
+ if (Node.isTemplateMiddle(literal)) {
1552
+ result += literal.getLiteralText();
1553
+ } else if (Node.isTemplateTail(literal)) {
1554
+ result += literal.getLiteralText();
1555
+ }
1556
+ }
1557
+ parts.push(result);
1558
+ } else if (Node.isIdentifier(expr)) {
1559
+ parts.push(`{${expr.getText()}}`);
1560
+ } else if (Node.isBinaryExpression(expr)) {
1561
+ const concat = this.evaluateStringConcatenation(expr);
1562
+ if (concat !== null) {
1563
+ parts.push(concat);
1564
+ }
1565
+ } else if (Node.isPropertyAccessExpression(expr)) {
1566
+ const value = this.resolvePropertyAccess(expr);
1567
+ if (value !== null) {
1568
+ parts.push(value);
1569
+ }
1570
+ }
1571
+ }
1572
+ }
1573
+ }
1574
+ const content = parts.join("").trim();
1575
+ return { kind: "raw", content };
1576
+ }
1577
+ /**
1578
+ * Transform a custom component by resolving its import and inlining its JSX
1579
+ *
1580
+ * Custom components are user-defined TSX fragments that get inlined at
1581
+ * transpile time. Component props are NOT supported in v1 - only parameterless
1582
+ * composition.
1583
+ */
1584
+ transformCustomComponent(name, node) {
1585
+ const openingElement = Node.isJsxElement(node) ? node.getOpeningElement() : node;
1586
+ const attributes = openingElement.getAttributes();
1587
+ if (attributes.length > 0) {
1588
+ throw this.createError(`Component props not supported: <${name}> has ${attributes.length} prop(s)`, node);
1589
+ }
1590
+ if (!this.sourceFile) {
1591
+ throw this.createError(
1592
+ `Cannot resolve component '${name}': no source file context. Pass sourceFile to transformer.transform() for component composition.`,
1593
+ node
1594
+ );
1595
+ }
1596
+ const resolved = resolveComponentImport(name, this.sourceFile, this.visitedPaths);
1597
+ this.visitedPaths = resolved.visitedPaths;
1598
+ const previousSourceFile = this.sourceFile;
1599
+ this.sourceFile = resolved.sourceFile;
1600
+ let result = null;
1601
+ if (Node.isJsxFragment(resolved.jsx)) {
1602
+ const blocks = this.transformFragmentChildren(resolved.jsx);
1603
+ result = blocks[0] ?? null;
1604
+ } else {
1605
+ result = this.transformToBlock(resolved.jsx);
1606
+ }
1607
+ this.sourceFile = previousSourceFile;
1608
+ return result;
1609
+ }
1610
+ /**
1611
+ * Transform a SpawnAgent element to SpawnAgentNode
1612
+ * SpawnAgent is a block-level element that emits Task() syntax
1613
+ *
1614
+ * Supports two modes:
1615
+ * 1. prompt prop (deprecated): Manual prompt string
1616
+ * 2. input prop (preferred): Typed input - VariableRef or object literal
1617
+ *
1618
+ * Also supports:
1619
+ * - agent={AgentRef} for type-safe agent references
1620
+ * - loadFromFile prop for "load from file" pattern
1621
+ */
1622
+ transformSpawnAgent(node) {
1623
+ const openingElement = Node.isJsxElement(node) ? node.getOpeningElement() : node;
1624
+ const { agentName, agentPath } = this.extractAgentProp(openingElement);
1625
+ const model = getAttributeValue(openingElement, "model");
1626
+ const description = getAttributeValue(openingElement, "description");
1627
+ const loadFromFile = this.extractLoadFromFileProp(openingElement, agentPath);
1628
+ const prompt = this.extractPromptProp(openingElement);
1629
+ const promptVariable = getAttributeValue(openingElement, "promptVariable");
1630
+ const input = this.extractInputProp(openingElement);
1631
+ const extraInstructions = Node.isJsxElement(node) ? this.extractExtraInstructions(node) : void 0;
1632
+ if (!agentName) {
1633
+ throw this.createError("SpawnAgent requires agent prop", openingElement);
1634
+ }
1635
+ if (!model) {
1636
+ throw this.createError("SpawnAgent requires model prop", openingElement);
1637
+ }
1638
+ if (!description) {
1639
+ throw this.createError("SpawnAgent requires description prop", openingElement);
1640
+ }
1641
+ const promptProps = [prompt, promptVariable, input].filter(Boolean).length;
1642
+ if (promptProps > 1) {
1643
+ throw this.createError(
1644
+ "Cannot use multiple prompt props on SpawnAgent. Use one of: prompt, promptVariable, or input.",
1645
+ openingElement
1646
+ );
1647
+ }
1648
+ if (promptProps === 0) {
1649
+ throw this.createError(
1650
+ "SpawnAgent requires either prompt, promptVariable, or input prop",
1651
+ openingElement
1652
+ );
1653
+ }
1654
+ const typeArgs = extractTypeArguments(node);
1655
+ let inputType;
1656
+ const typeParam = typeArgs && typeArgs.length > 0 ? typeArgs[0] : void 0;
1657
+ if (typeParam) {
1658
+ inputType = {
1659
+ kind: "typeReference",
1660
+ name: typeParam,
1661
+ resolved: false
1662
+ // Will be resolved in validation phase
1663
+ };
1664
+ }
1665
+ if (input) {
1666
+ this.validateInputAgainstInterface(input, typeParam, openingElement);
1667
+ }
1668
+ return {
1669
+ kind: "spawnAgent",
1670
+ agent: agentName,
1671
+ model,
1672
+ description,
1673
+ ...prompt && { prompt },
1674
+ ...promptVariable && { promptVariable },
1675
+ ...input && { input },
1676
+ ...extraInstructions && { extraInstructions },
1677
+ ...inputType && { inputType },
1678
+ ...loadFromFile && { loadFromFile }
1679
+ };
1680
+ }
1681
+ /**
1682
+ * Extract agent prop - handles string OR AgentRef identifier
1683
+ *
1684
+ * Returns:
1685
+ * - agentName: The agent name string (required)
1686
+ * - agentPath: The agent's file path (if AgentRef with path)
1687
+ */
1688
+ extractAgentProp(element) {
1689
+ const attr = element.getAttribute("agent");
1690
+ if (!attr || !Node.isJsxAttribute(attr)) {
1691
+ return { agentName: void 0, agentPath: void 0 };
1692
+ }
1693
+ const init = attr.getInitializer();
1694
+ if (init && Node.isStringLiteral(init)) {
1695
+ return { agentName: init.getLiteralValue(), agentPath: void 0 };
1696
+ }
1697
+ if (init && Node.isJsxExpression(init)) {
1698
+ const expr = init.getExpression();
1699
+ if (expr && Node.isIdentifier(expr)) {
1700
+ const identName = expr.getText();
1701
+ const agentRef = this.resolveAgentRef(identName);
1702
+ if (agentRef) {
1703
+ return { agentName: agentRef.name, agentPath: agentRef.path };
1704
+ }
1705
+ return { agentName: identName, agentPath: void 0 };
1706
+ }
1707
+ if (expr && Node.isStringLiteral(expr)) {
1708
+ return { agentName: expr.getLiteralValue(), agentPath: void 0 };
1709
+ }
1710
+ }
1711
+ return { agentName: void 0, agentPath: void 0 };
1712
+ }
1713
+ /**
1714
+ * Try to resolve an identifier to an AgentRef definition
1715
+ *
1716
+ * Looks for:
1717
+ * 1. Imported AgentRef (from defineAgent call in source file)
1718
+ * 2. Local AgentRef constant
1719
+ */
1720
+ resolveAgentRef(identName) {
1721
+ if (!this.sourceFile) return void 0;
1722
+ const symbol = this.sourceFile.getLocal(identName);
1723
+ if (!symbol) return void 0;
1724
+ const declarations = symbol.getDeclarations();
1725
+ if (!declarations || declarations.length === 0) return void 0;
1726
+ for (const decl of declarations) {
1727
+ if (Node.isImportSpecifier(decl)) {
1728
+ const resolved = this.resolveImportedAgentRef(decl, identName);
1729
+ if (resolved) return resolved;
1730
+ continue;
1731
+ }
1732
+ if (Node.isVariableDeclaration(decl)) {
1733
+ const init = decl.getInitializer();
1734
+ if (init && Node.isCallExpression(init)) {
1735
+ const callExpr = init.getExpression();
1736
+ if (callExpr && callExpr.getText() === "defineAgent") {
1737
+ const args = init.getArguments();
1738
+ if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
1739
+ return this.extractAgentRefFromObject(args[0]);
1740
+ }
1741
+ }
1742
+ }
1743
+ }
1744
+ }
1745
+ return void 0;
1746
+ }
1747
+ /**
1748
+ * Resolve an imported AgentRef by tracing to its source file
1749
+ */
1750
+ resolveImportedAgentRef(importSpec, identName) {
1751
+ let current = importSpec;
1752
+ while (current && !Node.isImportDeclaration(current)) {
1753
+ current = current.getParent();
1754
+ }
1755
+ if (!current || !Node.isImportDeclaration(current)) {
1756
+ return void 0;
1757
+ }
1758
+ const importDecl = current;
1759
+ const resolvedSourceFile = importDecl.getModuleSpecifierSourceFile();
1760
+ if (!resolvedSourceFile) {
1761
+ return void 0;
1762
+ }
1763
+ const exportedVar = resolvedSourceFile.getVariableDeclaration(identName);
1764
+ if (!exportedVar) {
1765
+ return void 0;
1766
+ }
1767
+ const init = exportedVar.getInitializer();
1768
+ if (init && Node.isCallExpression(init)) {
1769
+ const callExpr = init.getExpression();
1770
+ if (callExpr && callExpr.getText() === "defineAgent") {
1771
+ const args = init.getArguments();
1772
+ if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
1773
+ return this.extractAgentRefFromObject(args[0]);
1774
+ }
1775
+ }
1776
+ }
1777
+ return void 0;
1778
+ }
1779
+ /**
1780
+ * Extract AgentRef properties from defineAgent config object
1781
+ */
1782
+ extractAgentRefFromObject(obj) {
1783
+ let name;
1784
+ let path;
1785
+ for (const prop of obj.getProperties()) {
1786
+ if (Node.isPropertyAssignment(prop)) {
1787
+ const propName = prop.getName();
1788
+ const init = prop.getInitializer();
1789
+ if (propName === "name" && init && Node.isStringLiteral(init)) {
1790
+ name = init.getLiteralValue();
1791
+ }
1792
+ if (propName === "path" && init && Node.isStringLiteral(init)) {
1793
+ path = init.getLiteralValue();
1794
+ }
1795
+ }
1796
+ }
1797
+ if (name) {
1798
+ return { name, path };
1799
+ }
1800
+ return void 0;
1801
+ }
1802
+ /**
1803
+ * Extract loadFromFile prop
1804
+ *
1805
+ * Supports:
1806
+ * - loadFromFile (boolean true shorthand)
1807
+ * - loadFromFile={true}
1808
+ * - loadFromFile="explicit/path.md"
1809
+ *
1810
+ * When true, uses agentPath from AgentRef.
1811
+ * Returns resolved path string or undefined.
1812
+ */
1813
+ extractLoadFromFileProp(element, agentPath) {
1814
+ const attr = element.getAttribute("loadFromFile");
1815
+ if (!attr || !Node.isJsxAttribute(attr)) {
1816
+ return void 0;
1817
+ }
1818
+ const init = attr.getInitializer();
1819
+ if (!init) {
1820
+ if (!agentPath) {
1821
+ throw this.createError(
1822
+ 'loadFromFile={true} requires an AgentRef with a path property. Either use agent={AgentRef} where AgentRef has a path, or provide an explicit path: loadFromFile="path/to/agent.md"',
1823
+ element
1824
+ );
1825
+ }
1826
+ return agentPath;
1827
+ }
1828
+ if (Node.isStringLiteral(init)) {
1829
+ return init.getLiteralValue();
1830
+ }
1831
+ if (Node.isJsxExpression(init)) {
1832
+ const expr = init.getExpression();
1833
+ if (expr && expr.getText() === "true") {
1834
+ if (!agentPath) {
1835
+ throw this.createError(
1836
+ 'loadFromFile={true} requires an AgentRef with a path property. Either use agent={AgentRef} where AgentRef has a path, or provide an explicit path: loadFromFile="path/to/agent.md"',
1837
+ element
1838
+ );
1839
+ }
1840
+ return agentPath;
1841
+ }
1842
+ if (expr && expr.getText() === "false") {
1843
+ return void 0;
1844
+ }
1845
+ if (expr && Node.isStringLiteral(expr)) {
1846
+ return expr.getLiteralValue();
1847
+ }
1848
+ if (expr && Node.isPropertyAccessExpression(expr)) {
1849
+ const resolvedPath = this.resolvePropertyAccess(expr);
1850
+ if (resolvedPath) {
1851
+ return resolvedPath;
1852
+ }
1853
+ throw this.createError(
1854
+ `Cannot resolve property access ${expr.getText()} for loadFromFile. Make sure the object is a const with string literal values.`,
1855
+ element
1856
+ );
1857
+ }
1858
+ }
1859
+ throw this.createError(
1860
+ "loadFromFile must be a boolean or string path",
1861
+ element
1862
+ );
1863
+ }
1864
+ /**
1865
+ * Extract input prop - handles VariableRef identifier or object literal
1866
+ *
1867
+ * Supports:
1868
+ * - input={varRef} - Reference to useVariable result
1869
+ * - input={{ key: "value" }} - Object literal with properties
1870
+ */
1871
+ extractInputProp(element) {
1872
+ const attr = element.getAttribute("input");
1873
+ if (!attr || !Node.isJsxAttribute(attr)) return void 0;
1874
+ const init = attr.getInitializer();
1875
+ if (!init || !Node.isJsxExpression(init)) return void 0;
1876
+ const expr = init.getExpression();
1877
+ if (!expr) return void 0;
1878
+ if (Node.isIdentifier(expr)) {
1879
+ const variable = this.variables.get(expr.getText());
1880
+ if (variable) {
1881
+ return { type: "variable", varName: variable.envName };
1882
+ }
1883
+ throw this.createError(
1884
+ `Input '${expr.getText()}' not found. Use useVariable() or object literal.`,
1885
+ element
1886
+ );
1887
+ }
1888
+ if (Node.isObjectLiteralExpression(expr)) {
1889
+ const properties = extractInputObjectLiteral(expr, this.variables);
1890
+ return { type: "object", properties };
1891
+ }
1892
+ throw this.createError("Input must be a VariableRef or object literal", element);
1893
+ }
1894
+ /**
1895
+ * Extract extra instructions from SpawnAgent children
1896
+ *
1897
+ * Treats children as raw text content (like Markdown component).
1898
+ * Returns undefined if no children or only whitespace.
1899
+ */
1900
+ extractExtraInstructions(node) {
1901
+ const parts = [];
1902
+ for (const child of node.getJsxChildren()) {
1903
+ if (Node.isJsxText(child)) {
1904
+ const text = child.getText();
1905
+ if (text.trim()) {
1906
+ parts.push(text);
1907
+ }
1908
+ } else if (Node.isJsxExpression(child)) {
1909
+ const expr = child.getExpression();
1910
+ if (expr) {
1911
+ if (Node.isStringLiteral(expr)) {
1912
+ parts.push(expr.getLiteralValue());
1913
+ } else if (Node.isNoSubstitutionTemplateLiteral(expr)) {
1914
+ parts.push(expr.getLiteralValue());
1915
+ } else if (Node.isTemplateExpression(expr)) {
1916
+ parts.push(this.extractTemplateText(expr));
1917
+ }
1918
+ }
1919
+ }
1920
+ }
1921
+ const content = parts.join("").trim();
1922
+ return content || void 0;
1923
+ }
1924
+ /**
1925
+ * Extract type argument from SpawnAgent<T> syntax
1926
+ *
1927
+ * Returns the type name string (e.g., "ResearcherInput") or undefined
1928
+ * if no type argument is present.
1929
+ */
1930
+ extractSpawnAgentTypeParam(element) {
1931
+ const parent = element.getParent();
1932
+ if (!parent) return void 0;
1933
+ if (Node.isJsxElement(parent)) {
1934
+ const typeArgs = extractTypeArguments(parent);
1935
+ return typeArgs && typeArgs.length > 0 ? typeArgs[0] : void 0;
1936
+ }
1937
+ if (Node.isJsxSelfClosingElement(element)) {
1938
+ const typeArgs = extractTypeArguments(element);
1939
+ return typeArgs && typeArgs.length > 0 ? typeArgs[0] : void 0;
1940
+ }
1941
+ return void 0;
1942
+ }
1943
+ /**
1944
+ * Validate input object properties against SpawnAgent<T> type parameter.
1945
+ *
1946
+ * Throws compile error if required interface properties are missing.
1947
+ * Only validates object literal inputs (VariableRef is runtime-checked).
1948
+ *
1949
+ * @param input - The parsed SpawnAgentInput (may be variable or object)
1950
+ * @param typeParam - The type parameter name (e.g., "ResearcherInput")
1951
+ * @param element - The JSX element for error reporting
1952
+ */
1953
+ validateInputAgainstInterface(input, typeParam, element) {
1954
+ if (input.type !== "object") return;
1955
+ if (!typeParam) return;
1956
+ if (!this.sourceFile) {
1957
+ return;
1958
+ }
1959
+ const resolved = resolveTypeImport(typeParam, this.sourceFile);
1960
+ if (!resolved?.interface) {
1961
+ return;
1962
+ }
1963
+ const interfaceProps = extractInterfaceProperties(resolved.interface);
1964
+ const requiredProps = interfaceProps.filter((p) => p.required);
1965
+ const inputPropNames = input.properties.map((p) => p.name);
1966
+ const missing = requiredProps.filter((p) => !inputPropNames.includes(p.name));
1967
+ if (missing.length > 0) {
1968
+ const missingNames = missing.map((p) => p.name).join(", ");
1969
+ const requiredNames = requiredProps.map((p) => p.name).join(", ");
1970
+ throw this.createError(
1971
+ `SpawnAgent input missing required properties: ${missingNames}. Interface '${typeParam}' requires: ${requiredNames}`,
1972
+ element
1973
+ );
1974
+ }
1975
+ }
1976
+ /**
1977
+ * Transform an If element to IfNode
1978
+ *
1979
+ * NOTE: V1 control flow is deprecated. Use V3 transformer with useRuntimeVar and condition-based If.
1980
+ */
1981
+ transformIf(node) {
1982
+ throw this.createError(
1983
+ "V1 If control flow is deprecated. Use V3 transformer with useRuntimeVar and condition-based If.",
1984
+ node
1985
+ );
1986
+ }
1987
+ /**
1988
+ * Transform an Else element to ElseNode
1989
+ *
1990
+ * NOTE: V1 control flow is deprecated. Use V3 transformer with useRuntimeVar and condition-based If/Else.
1991
+ */
1992
+ transformElse(node) {
1993
+ throw this.createError(
1994
+ "V1 Else control flow is deprecated. Use V3 transformer with useRuntimeVar and condition-based Else.",
1995
+ node
1996
+ );
1997
+ }
1998
+ /**
1999
+ * Transform Loop component to LoopNode IR
2000
+ *
2001
+ * NOTE: V1 control flow is deprecated. Use V3 transformer with useRuntimeVar and max-based Loop.
2002
+ */
2003
+ transformLoop(node) {
2004
+ throw this.createError(
2005
+ "V1 Loop control flow is deprecated. Use V3 transformer with useRuntimeVar and max-based Loop.",
2006
+ node
2007
+ );
2008
+ }
2009
+ /**
2010
+ * Extract useOutput declarations from source file
2011
+ * Returns map of identifier name -> agent name
2012
+ *
2013
+ * Uses forEachDescendant to find declarations inside function bodies,
2014
+ * following the same pattern as extractVariableDeclarations in parser.ts
2015
+ */
2016
+ extractOutputDeclarations(sourceFile) {
2017
+ const outputs = /* @__PURE__ */ new Map();
2018
+ sourceFile.forEachDescendant((node) => {
2019
+ if (!Node.isVariableDeclaration(node)) return;
2020
+ const init = node.getInitializer();
2021
+ if (!init || !Node.isCallExpression(init)) return;
2022
+ const expr = init.getExpression();
2023
+ if (!Node.isIdentifier(expr) || expr.getText() !== "useOutput") return;
2024
+ const args = init.getArguments();
2025
+ if (args.length < 1) return;
2026
+ const agentArg = args[0];
2027
+ if (Node.isStringLiteral(agentArg)) {
2028
+ const agentName = agentArg.getLiteralValue();
2029
+ const identName = node.getName();
2030
+ outputs.set(identName, agentName);
2031
+ }
2032
+ });
2033
+ return outputs;
2034
+ }
2035
+ /**
2036
+ * Extract useStateRef declarations from source file
2037
+ * Returns map of identifier name -> state key
2038
+ *
2039
+ * Uses forEachDescendant to find declarations inside function bodies,
2040
+ * following the same pattern as extractOutputDeclarations
2041
+ */
2042
+ extractStateRefDeclarations(sourceFile) {
2043
+ const stateRefs = /* @__PURE__ */ new Map();
2044
+ sourceFile.forEachDescendant((node) => {
2045
+ if (!Node.isVariableDeclaration(node)) return;
2046
+ const init = node.getInitializer();
2047
+ if (!init || !Node.isCallExpression(init)) return;
2048
+ const expr = init.getExpression();
2049
+ if (!Node.isIdentifier(expr) || expr.getText() !== "useStateRef") return;
2050
+ const args = init.getArguments();
2051
+ if (args.length < 1) return;
2052
+ const keyArg = args[0];
2053
+ if (Node.isStringLiteral(keyArg)) {
2054
+ const stateKey = keyArg.getLiteralValue();
2055
+ const identName = node.getName();
2056
+ stateRefs.set(identName, stateKey);
2057
+ }
2058
+ });
2059
+ return stateRefs;
2060
+ }
2061
+ /**
2062
+ * Transform ReadState JSX element into IR node
2063
+ *
2064
+ * Extracts:
2065
+ * - state: StateRef with key property
2066
+ * - into: VariableRef with name property
2067
+ * - field: optional nested path string
2068
+ */
2069
+ transformReadState(node) {
2070
+ const openingElement = Node.isJsxElement(node) ? node.getOpeningElement() : node;
2071
+ const stateAttr = openingElement.getAttribute("state");
2072
+ if (!stateAttr || !Node.isJsxAttribute(stateAttr)) {
2073
+ throw this.createError("ReadState requires state prop", openingElement);
2074
+ }
2075
+ const stateInit = stateAttr.getInitializer();
2076
+ if (!stateInit || !Node.isJsxExpression(stateInit)) {
2077
+ throw this.createError("ReadState state prop must be JSX expression", openingElement);
2078
+ }
2079
+ const stateExpr = stateInit.getExpression();
2080
+ if (!stateExpr) {
2081
+ throw this.createError("ReadState state prop expression is empty", openingElement);
2082
+ }
2083
+ const stateKey = this.extractStateKey(stateExpr, openingElement);
2084
+ const intoAttr = openingElement.getAttribute("into");
2085
+ if (!intoAttr || !Node.isJsxAttribute(intoAttr)) {
2086
+ throw this.createError("ReadState requires into prop", openingElement);
2087
+ }
2088
+ const intoInit = intoAttr.getInitializer();
2089
+ if (!intoInit || !Node.isJsxExpression(intoInit)) {
2090
+ throw this.createError("ReadState into prop must be JSX expression", openingElement);
2091
+ }
2092
+ const intoExpr = intoInit.getExpression();
2093
+ if (!intoExpr) {
2094
+ throw this.createError("ReadState into prop expression is empty", openingElement);
2095
+ }
2096
+ const variableName = this.extractVariableName(intoExpr, openingElement);
2097
+ const fieldAttr = openingElement.getAttribute("field");
2098
+ let field;
2099
+ if (fieldAttr && Node.isJsxAttribute(fieldAttr)) {
2100
+ const fieldInit = fieldAttr.getInitializer();
2101
+ if (fieldInit && Node.isStringLiteral(fieldInit)) {
2102
+ field = fieldInit.getLiteralText();
2103
+ }
2104
+ }
2105
+ return {
2106
+ kind: "readState",
2107
+ stateKey,
2108
+ variableName,
2109
+ field
2110
+ };
2111
+ }
2112
+ /**
2113
+ * Extract state key from StateRef expression
2114
+ * Handles: identifier pointing to useStateRef result
2115
+ */
2116
+ extractStateKey(expr, element) {
2117
+ if (Node.isIdentifier(expr)) {
2118
+ const name = expr.getText();
2119
+ const tracked = this.stateRefs.get(name);
2120
+ if (tracked) return tracked;
2121
+ throw this.createError(
2122
+ `State reference '${name}' not found. Did you declare it with useStateRef()?`,
2123
+ element
2124
+ );
2125
+ }
2126
+ throw this.createError(`Cannot extract state key from: ${expr.getText()}`, element);
2127
+ }
2128
+ /**
2129
+ * Extract variable name from VariableRef expression
2130
+ * Handles: identifier pointing to useVariable result
2131
+ */
2132
+ extractVariableName(expr, element) {
2133
+ if (Node.isIdentifier(expr)) {
2134
+ const name = expr.getText();
2135
+ const tracked = this.variables.get(name);
2136
+ if (tracked) return tracked.envName;
2137
+ throw this.createError(
2138
+ `Variable '${name}' not found. Did you declare it with useVariable()?`,
2139
+ element
2140
+ );
2141
+ }
2142
+ throw this.createError(`Cannot extract variable name from: ${expr.getText()}`, element);
2143
+ }
2144
+ /**
2145
+ * Transform WriteState JSX element into IR node
2146
+ *
2147
+ * Two modes:
2148
+ * 1. Field mode: field="path" value={val}
2149
+ * 2. Merge mode: merge={partial}
2150
+ */
2151
+ transformWriteState(node) {
2152
+ const openingElement = Node.isJsxElement(node) ? node.getOpeningElement() : node;
2153
+ const stateAttr = openingElement.getAttribute("state");
2154
+ if (!stateAttr || !Node.isJsxAttribute(stateAttr)) {
2155
+ throw this.createError("WriteState requires state prop", openingElement);
2156
+ }
2157
+ const stateInit = stateAttr.getInitializer();
2158
+ if (!stateInit || !Node.isJsxExpression(stateInit)) {
2159
+ throw this.createError("WriteState state prop must be JSX expression", openingElement);
2160
+ }
2161
+ const stateExpr = stateInit.getExpression();
2162
+ if (!stateExpr) {
2163
+ throw this.createError("WriteState state prop expression is empty", openingElement);
2164
+ }
2165
+ const stateKey = this.extractStateKey(stateExpr, openingElement);
2166
+ const fieldAttr = openingElement.getAttribute("field");
2167
+ const mergeAttr = openingElement.getAttribute("merge");
2168
+ if (fieldAttr && Node.isJsxAttribute(fieldAttr)) {
2169
+ const fieldInit = fieldAttr.getInitializer();
2170
+ if (!fieldInit || !Node.isStringLiteral(fieldInit)) {
2171
+ throw this.createError("WriteState field prop must be string literal", openingElement);
2172
+ }
2173
+ const field = fieldInit.getLiteralText();
2174
+ const valueAttr = openingElement.getAttribute("value");
2175
+ if (!valueAttr || !Node.isJsxAttribute(valueAttr)) {
2176
+ throw this.createError("WriteState with field requires value prop", openingElement);
2177
+ }
2178
+ const valueInit = valueAttr.getInitializer();
2179
+ if (!valueInit) {
2180
+ throw this.createError("WriteState value prop is empty", openingElement);
2181
+ }
2182
+ let value;
2183
+ if (Node.isStringLiteral(valueInit)) {
2184
+ value = { type: "literal", content: valueInit.getLiteralText() };
2185
+ } else if (Node.isJsxExpression(valueInit)) {
2186
+ const valueExpr = valueInit.getExpression();
2187
+ if (!valueExpr) {
2188
+ throw this.createError("WriteState value expression is empty", openingElement);
2189
+ }
2190
+ if (Node.isIdentifier(valueExpr)) {
2191
+ const varName = valueExpr.getText();
2192
+ const tracked = this.variables.get(varName);
2193
+ if (tracked) {
2194
+ value = { type: "variable", content: tracked.envName };
2195
+ } else {
2196
+ value = { type: "literal", content: valueExpr.getText() };
2197
+ }
2198
+ } else {
2199
+ value = { type: "literal", content: valueExpr.getText() };
2200
+ }
2201
+ } else {
2202
+ throw this.createError("WriteState value must be string or expression", openingElement);
2203
+ }
2204
+ return {
2205
+ kind: "writeState",
2206
+ stateKey,
2207
+ mode: "field",
2208
+ field,
2209
+ value
2210
+ };
2211
+ } else if (mergeAttr && Node.isJsxAttribute(mergeAttr)) {
2212
+ const mergeInit = mergeAttr.getInitializer();
2213
+ if (!mergeInit || !Node.isJsxExpression(mergeInit)) {
2214
+ throw this.createError("WriteState merge prop must be JSX expression", openingElement);
2215
+ }
2216
+ const mergeExpr = mergeInit.getExpression();
2217
+ if (!mergeExpr) {
2218
+ throw this.createError("WriteState merge expression is empty", openingElement);
2219
+ }
2220
+ const content = mergeExpr.getText();
2221
+ return {
2222
+ kind: "writeState",
2223
+ stateKey,
2224
+ mode: "merge",
2225
+ value: { type: "literal", content }
2226
+ };
2227
+ } else {
2228
+ throw this.createError("WriteState requires either field+value or merge prop", openingElement);
2229
+ }
2230
+ }
2231
+ /**
2232
+ * Transform an OnStatus element to OnStatusNode
2233
+ * OnStatus is a block-level element that emits status-based conditionals
2234
+ */
2235
+ transformOnStatus(node) {
2236
+ const openingElement = Node.isJsxElement(node) ? node.getOpeningElement() : node;
2237
+ const outputAttr = openingElement.getAttribute("output");
2238
+ if (!outputAttr || !Node.isJsxAttribute(outputAttr)) {
2239
+ throw this.createError("OnStatus requires output prop", openingElement);
2240
+ }
2241
+ const outputInit = outputAttr.getInitializer();
2242
+ if (!outputInit || !Node.isJsxExpression(outputInit)) {
2243
+ throw this.createError("OnStatus output must be a JSX expression: output={outputRef}", openingElement);
2244
+ }
2245
+ const outputExpr = outputInit.getExpression();
2246
+ if (!outputExpr || !Node.isIdentifier(outputExpr)) {
2247
+ throw this.createError("OnStatus output must reference a useOutput result", openingElement);
2248
+ }
2249
+ const outputIdentifier = outputExpr.getText();
2250
+ const agentName = this.outputs.get(outputIdentifier);
2251
+ if (!agentName) {
2252
+ throw this.createError(
2253
+ `Output '${outputIdentifier}' not found. Did you declare it with useOutput()?`,
2254
+ openingElement
2255
+ );
2256
+ }
2257
+ const status = getAttributeValue(openingElement, "status");
2258
+ if (!status) {
2259
+ throw this.createError("OnStatus requires status prop", openingElement);
2260
+ }
2261
+ const validStatuses = ["SUCCESS", "BLOCKED", "NOT_FOUND", "ERROR", "CHECKPOINT"];
2262
+ if (!validStatuses.includes(status)) {
2263
+ throw this.createError(
2264
+ `OnStatus status must be one of: ${validStatuses.join(", ")}. Got: ${status}`,
2265
+ openingElement
2266
+ );
2267
+ }
2268
+ const children = Node.isJsxElement(node) ? this.transformBlockChildren(node.getJsxChildren()) : [];
2269
+ return {
2270
+ kind: "onStatus",
2271
+ outputRef: {
2272
+ kind: "outputReference",
2273
+ agent: agentName
2274
+ },
2275
+ status,
2276
+ children
2277
+ };
2278
+ }
2279
+ // ============================================================================
2280
+ // MCP Configuration Transformation
2281
+ // ============================================================================
2282
+ /**
2283
+ * Transform an MCPConfig element to MCPConfigDocumentNode
2284
+ * MCPConfig wraps multiple MCPServer elements into a single document
2285
+ * Delegates to document.ts transformMCPConfig()
2286
+ */
2287
+ transformMCPConfig(node) {
2288
+ return transformMCPConfig(node, this.buildContext());
2289
+ }
2290
+ /**
2291
+ * Transform JSX children to BlockNodes, handling If/Else sibling pairs
2292
+ */
2293
+ transformBlockChildren(jsxChildren) {
2294
+ const blocks = [];
2295
+ let i = 0;
2296
+ while (i < jsxChildren.length) {
2297
+ const child = jsxChildren[i];
2298
+ if (Node.isJsxText(child)) {
2299
+ const text = extractText(child);
2300
+ if (!text) {
2301
+ i++;
2302
+ continue;
2303
+ }
2304
+ }
2305
+ if (Node.isJsxElement(child) || Node.isJsxSelfClosingElement(child)) {
2306
+ const childName = getElementName(child);
2307
+ if (childName === "If") {
2308
+ const ifNode = this.transformIf(child);
2309
+ blocks.push(ifNode);
2310
+ let nextIndex = i + 1;
2311
+ while (nextIndex < jsxChildren.length) {
2312
+ const sibling = jsxChildren[nextIndex];
2313
+ if (Node.isJsxText(sibling)) {
2314
+ const text = extractText(sibling);
2315
+ if (!text) {
2316
+ nextIndex++;
2317
+ continue;
2318
+ }
2319
+ }
2320
+ if ((Node.isJsxElement(sibling) || Node.isJsxSelfClosingElement(sibling)) && getElementName(sibling) === "Else") {
2321
+ const elseNode = this.transformElse(sibling);
2322
+ blocks.push(elseNode);
2323
+ i = nextIndex;
2324
+ }
2325
+ break;
2326
+ }
2327
+ } else {
2328
+ const block = this.transformToBlock(child);
2329
+ if (block) blocks.push(block);
2330
+ }
2331
+ } else {
2332
+ const block = this.transformToBlock(child);
2333
+ if (block) blocks.push(block);
2334
+ }
2335
+ i++;
2336
+ }
2337
+ return blocks;
2338
+ }
2339
+ /**
2340
+ * Transform an Assign element to AssignNode
2341
+ * Assign emits a bash code block with variable assignment
2342
+ *
2343
+ * Supports three assignment types (exactly one required):
2344
+ * - bash: VAR=$(command)
2345
+ * - value: VAR=value (quoted if spaces)
2346
+ * - env: VAR=$ENV_VAR
2347
+ */
2348
+ transformAssign(node) {
2349
+ const openingElement = Node.isJsxElement(node) ? node.getOpeningElement() : node;
2350
+ const varAttr = openingElement.getAttribute("var");
2351
+ if (!varAttr || !Node.isJsxAttribute(varAttr)) {
2352
+ throw this.createError("Assign requires var prop", openingElement);
2353
+ }
2354
+ const init = varAttr.getInitializer();
2355
+ if (!init || !Node.isJsxExpression(init)) {
2356
+ throw this.createError("Assign var must be a JSX expression: var={variableName}", openingElement);
2357
+ }
2358
+ const expr = init.getExpression();
2359
+ if (!expr) {
2360
+ throw this.createError("Assign var must reference a useVariable or defineVars result", openingElement);
2361
+ }
2362
+ let localName;
2363
+ if (Node.isIdentifier(expr)) {
2364
+ localName = expr.getText();
2365
+ } else if (Node.isPropertyAccessExpression(expr)) {
2366
+ localName = expr.getText();
2367
+ } else {
2368
+ throw this.createError("Assign var must reference a useVariable or defineVars result", openingElement);
2369
+ }
2370
+ const variable = this.variables.get(localName);
2371
+ if (!variable) {
2372
+ throw this.createError(
2373
+ `Variable '${localName}' not found. Did you declare it with useVariable() or defineVars()?`,
2374
+ openingElement
2375
+ );
2376
+ }
2377
+ const bashProp = this.extractAssignPropValue(openingElement, "bash");
2378
+ const valueProp = this.extractAssignPropValue(openingElement, "value");
2379
+ const envProp = this.extractAssignPropValue(openingElement, "env");
2380
+ const propCount = [bashProp, valueProp, envProp].filter((p) => p !== void 0).length;
2381
+ if (propCount === 0) {
2382
+ throw this.createError(
2383
+ "Assign requires one of: bash, value, or env prop",
2384
+ openingElement
2385
+ );
2386
+ }
2387
+ if (propCount > 1) {
2388
+ throw this.createError(
2389
+ "Assign accepts only one of: bash, value, or env prop",
2390
+ openingElement
2391
+ );
2392
+ }
2393
+ let assignment;
2394
+ if (bashProp !== void 0) {
2395
+ assignment = { type: "bash", content: bashProp };
2396
+ } else if (valueProp !== void 0) {
2397
+ assignment = { type: "value", content: valueProp };
2398
+ } else {
2399
+ assignment = { type: "env", content: envProp };
2400
+ }
2401
+ const commentProp = this.extractAssignPropValue(openingElement, "comment");
2402
+ return {
2403
+ kind: "assign",
2404
+ variableName: variable.envName,
2405
+ assignment,
2406
+ ...commentProp && { comment: commentProp }
2407
+ };
2408
+ }
2409
+ /**
2410
+ * Transform an AssignGroup element to AssignGroupNode
2411
+ * AssignGroup collects Assign children into a single bash code block
2412
+ */
2413
+ transformAssignGroup(node) {
2414
+ if (Node.isJsxSelfClosingElement(node)) {
2415
+ throw this.createError("AssignGroup must have Assign children", node);
2416
+ }
2417
+ const children = node.getJsxChildren();
2418
+ const assignments = [];
2419
+ let pendingBlankBefore = false;
2420
+ for (const child of children) {
2421
+ if (Node.isJsxText(child)) {
2422
+ const text = child.getText().trim();
2423
+ if (text === "") continue;
2424
+ throw this.createError("AssignGroup can only contain Assign or br elements, not text", child);
2425
+ }
2426
+ if (!Node.isJsxElement(child) && !Node.isJsxSelfClosingElement(child)) {
2427
+ throw this.createError("AssignGroup can only contain Assign or br elements", child);
2428
+ }
2429
+ const opening = Node.isJsxElement(child) ? child.getOpeningElement() : child;
2430
+ const tagNameNode = opening.getTagNameNode();
2431
+ const name = tagNameNode.getText();
2432
+ if (name === "br") {
2433
+ pendingBlankBefore = true;
2434
+ continue;
2435
+ }
2436
+ if (name !== "Assign") {
2437
+ throw this.createError(`AssignGroup can only contain Assign or br elements, found: ${name}`, child);
2438
+ }
2439
+ const assignNode = this.transformAssign(child);
2440
+ if (pendingBlankBefore) {
2441
+ assignNode.blankBefore = true;
2442
+ pendingBlankBefore = false;
2443
+ }
2444
+ assignments.push(assignNode);
2445
+ }
2446
+ if (assignments.length === 0) {
2447
+ throw this.createError("AssignGroup must contain at least one Assign element", node);
2448
+ }
2449
+ return {
2450
+ kind: "assignGroup",
2451
+ assignments
2452
+ };
2453
+ }
2454
+ /**
2455
+ * Extract assignment prop value from Assign element
2456
+ * Handles string literals, JSX expressions with strings, and template literals
2457
+ */
2458
+ extractAssignPropValue(element, propName) {
2459
+ const attr = element.getAttribute(propName);
2460
+ if (!attr || !Node.isJsxAttribute(attr)) return void 0;
2461
+ const init = attr.getInitializer();
2462
+ if (!init) return void 0;
2463
+ if (Node.isStringLiteral(init)) {
2464
+ return init.getLiteralValue();
2465
+ }
2466
+ if (Node.isJsxExpression(init)) {
2467
+ const expr = init.getExpression();
2468
+ if (!expr) return void 0;
2469
+ if (Node.isStringLiteral(expr)) {
2470
+ return expr.getLiteralValue();
2471
+ }
2472
+ if (Node.isNoSubstitutionTemplateLiteral(expr)) {
2473
+ return expr.getLiteralValue();
2474
+ }
2475
+ if (Node.isTemplateExpression(expr)) {
2476
+ return this.extractBashTemplate(expr);
2477
+ }
2478
+ }
2479
+ return void 0;
2480
+ }
2481
+ /**
2482
+ * Extract template literal content preserving ${VAR} syntax for bash
2483
+ */
2484
+ extractBashTemplate(expr) {
2485
+ const parts = [];
2486
+ parts.push(expr.getHead().getLiteralText());
2487
+ for (const span of expr.getTemplateSpans()) {
2488
+ const spanExpr = span.getExpression();
2489
+ parts.push(`\${${spanExpr.getText()}}`);
2490
+ parts.push(span.getLiteral().getLiteralText());
2491
+ }
2492
+ return parts.join("");
2493
+ }
2494
+ /**
2495
+ * Extract prompt prop value, preserving multi-line content and {variable} placeholders
2496
+ * Supports: prompt="string", prompt={"string"}, prompt={`template`}
2497
+ */
2498
+ extractPromptProp(element) {
2499
+ const attr = element.getAttribute("prompt");
2500
+ if (!attr || !Node.isJsxAttribute(attr)) {
2501
+ return void 0;
2502
+ }
2503
+ const init = attr.getInitializer();
2504
+ if (!init) {
2505
+ return void 0;
2506
+ }
2507
+ if (Node.isStringLiteral(init)) {
2508
+ return init.getLiteralValue();
2509
+ }
2510
+ if (Node.isJsxExpression(init)) {
2511
+ const expr = init.getExpression();
2512
+ if (!expr) {
2513
+ return void 0;
2514
+ }
2515
+ if (Node.isStringLiteral(expr)) {
2516
+ return expr.getLiteralValue();
2517
+ }
2518
+ if (Node.isNoSubstitutionTemplateLiteral(expr)) {
2519
+ return expr.getLiteralValue();
2520
+ }
2521
+ if (Node.isTemplateExpression(expr)) {
2522
+ return this.extractTemplateText(expr);
2523
+ }
2524
+ }
2525
+ return void 0;
2526
+ }
2527
+ /**
2528
+ * Extract text from a template expression, converting ${var} to {var}
2529
+ * This preserves GSD's {variable} placeholder syntax
2530
+ */
2531
+ extractTemplateText(expr) {
2532
+ const parts = [];
2533
+ parts.push(expr.getHead().getLiteralText());
2534
+ for (const span of expr.getTemplateSpans()) {
2535
+ const spanExpr = span.getExpression();
2536
+ parts.push(`{${spanExpr.getText()}}`);
2537
+ parts.push(span.getLiteral().getLiteralText());
2538
+ }
2539
+ return parts.join("");
2540
+ }
2541
+ // ============================================================================
2542
+ // State Document Transformation
2543
+ // ============================================================================
2544
+ /**
2545
+ * Transform a State component into StateDocumentNode
2546
+ * Delegates to document.ts transformState()
2547
+ */
2548
+ transformState(node) {
2549
+ return transformState(node, this.buildContext());
2550
+ }
2551
+ /**
2552
+ * Evaluate a binary expression that represents string concatenation.
2553
+ * Handles chains like: `text ` + AGENT_PATHS.researcher + ` more`
2554
+ * Returns the concatenated string or null if not evaluable.
2555
+ */
2556
+ evaluateStringConcatenation(expr) {
2557
+ const operator = expr.getOperatorToken().getText();
2558
+ if (operator !== "+") {
2559
+ return null;
2560
+ }
2561
+ const left = expr.getLeft();
2562
+ const right = expr.getRight();
2563
+ const leftValue = this.evaluateStringExpression(left);
2564
+ const rightValue = this.evaluateStringExpression(right);
2565
+ if (leftValue === null || rightValue === null) {
2566
+ return null;
2567
+ }
2568
+ return leftValue + rightValue;
2569
+ }
2570
+ /**
2571
+ * Evaluate an expression that should resolve to a string value.
2572
+ * Handles: string literals, template literals, property access, binary expressions.
2573
+ */
2574
+ evaluateStringExpression(expr) {
2575
+ if (Node.isStringLiteral(expr)) {
2576
+ return expr.getLiteralValue();
2577
+ }
2578
+ if (Node.isNoSubstitutionTemplateLiteral(expr)) {
2579
+ return expr.getLiteralValue();
2580
+ }
2581
+ if (Node.isTemplateExpression(expr)) {
2582
+ let result = expr.getHead().getLiteralText();
2583
+ for (const span of expr.getTemplateSpans()) {
2584
+ const spanExpr = span.getExpression();
2585
+ const spanValue = this.evaluateStringExpression(spanExpr);
2586
+ if (spanValue !== null) {
2587
+ result += spanValue;
2588
+ } else if (Node.isIdentifier(spanExpr)) {
2589
+ result += `\${${spanExpr.getText()}}`;
2590
+ } else {
2591
+ result += `\${${spanExpr.getText()}}`;
2592
+ }
2593
+ const literal = span.getLiteral();
2594
+ if (Node.isTemplateMiddle(literal)) {
2595
+ result += literal.getLiteralText();
2596
+ } else if (Node.isTemplateTail(literal)) {
2597
+ result += literal.getLiteralText();
2598
+ }
2599
+ }
2600
+ return result;
2601
+ }
2602
+ if (Node.isPropertyAccessExpression(expr)) {
2603
+ return this.resolvePropertyAccess(expr);
2604
+ }
2605
+ if (Node.isBinaryExpression(expr)) {
2606
+ return this.evaluateStringConcatenation(expr);
2607
+ }
2608
+ if (Node.isParenthesizedExpression(expr)) {
2609
+ return this.evaluateStringExpression(expr.getExpression());
2610
+ }
2611
+ return null;
2612
+ }
2613
+ /**
2614
+ * Resolve a property access expression (e.g., AGENT_PATHS.researcher) to its value.
2615
+ * Only works for const declarations with object literals.
2616
+ */
2617
+ resolvePropertyAccess(expr) {
2618
+ const objectExpr = expr.getExpression();
2619
+ const propertyName = expr.getName();
2620
+ if (!Node.isIdentifier(objectExpr)) {
2621
+ return null;
2622
+ }
2623
+ const objectName = objectExpr.getText();
2624
+ const sourceFile = expr.getSourceFile();
2625
+ const varDecls = sourceFile.getVariableDeclarations();
2626
+ for (const varDecl of varDecls) {
2627
+ if (varDecl.getName() === objectName) {
2628
+ let initializer = varDecl.getInitializer();
2629
+ if (initializer && Node.isAsExpression(initializer)) {
2630
+ initializer = initializer.getExpression();
2631
+ }
2632
+ if (initializer && Node.isObjectLiteralExpression(initializer)) {
2633
+ for (const prop of initializer.getProperties()) {
2634
+ if (Node.isPropertyAssignment(prop) && prop.getName() === propertyName) {
2635
+ const propInit = prop.getInitializer();
2636
+ if (propInit && Node.isStringLiteral(propInit)) {
2637
+ return propInit.getLiteralValue();
2638
+ }
2639
+ if (propInit && Node.isNoSubstitutionTemplateLiteral(propInit)) {
2640
+ return propInit.getLiteralValue();
2641
+ }
2642
+ }
2643
+ }
2644
+ }
2645
+ break;
2646
+ }
2647
+ }
2648
+ return null;
2649
+ }
2650
+ };
2651
+ function transform(node, sourceFile) {
2652
+ const transformer = new Transformer();
2653
+ return transformer.transform(node, sourceFile);
2654
+ }
2655
+
2656
+ // src/primitives/schema.ts
2657
+ function defineVars(schema) {
2658
+ const result = {};
2659
+ for (const key of Object.keys(schema)) {
2660
+ result[key] = { name: key, ref: key };
2661
+ }
2662
+ return result;
2663
+ }
2664
+ function defineFiles(schema) {
2665
+ const result = {};
2666
+ const refs = [];
2667
+ for (const key of Object.keys(schema)) {
2668
+ const def = schema[key];
2669
+ const varName = key.replace(/([a-z])([A-Z])/g, "$1_$2").toUpperCase() + "_CONTENT";
2670
+ const pathValue = typeof def.path === "function" ? def.path({}) : def.path;
2671
+ const fileRef = {
2672
+ varName,
2673
+ key,
2674
+ path: pathValue,
2675
+ required: def.required !== false
2676
+ // Default true
2677
+ };
2678
+ result[key] = fileRef;
2679
+ refs.push(fileRef);
2680
+ }
2681
+ return {
2682
+ ...result,
2683
+ _refs: refs
2684
+ };
2685
+ }
2686
+ function defineContext(def) {
2687
+ const agents = {};
2688
+ if (def.agents) {
2689
+ for (const [key, value] of Object.entries(def.agents)) {
2690
+ if (typeof value === "string") {
2691
+ agents[key] = { path: value };
2692
+ } else {
2693
+ agents[key] = value;
2694
+ }
2695
+ }
2696
+ }
2697
+ return {
2698
+ agents,
2699
+ vars: def.vars || {},
2700
+ files: def.files || { _refs: [] }
2701
+ };
2702
+ }
2703
+
2704
+ // src/workflow/sections/semantic.ts
2705
+ function ExecutionContext(_props) {
2706
+ return null;
2707
+ }
2708
+ export {
2709
+ ASK_USER_MARKER,
2710
+ Agent,
2711
+ AskUser,
2712
+ BREAK_MARKER,
2713
+ Break,
2714
+ Command,
2715
+ ELSE_MARKER,
2716
+ Else,
2717
+ ExecutionContext,
2718
+ IF_MARKER,
2719
+ If,
2720
+ Indent,
2721
+ LOOP_MARKER,
2722
+ List,
2723
+ Loop,
2724
+ Markdown,
2725
+ OnStatus,
2726
+ RETURN_MARKER,
2727
+ Return,
2728
+ RuntimeMarkdownEmitter,
2729
+ SpawnAgent,
2730
+ Table,
2731
+ Transformer,
2732
+ XmlBlock,
2733
+ assertNever,
2734
+ buildRuntimeFile,
2735
+ bundleCodeSplit,
2736
+ bundleSingleEntryRuntime,
2737
+ clearRuntimeFnRegistry,
2738
+ createProject,
2739
+ createRuntimeContext,
2740
+ defineAgent,
2741
+ defineContext,
2742
+ defineFiles,
2743
+ defineVars,
2744
+ detectRuntime,
2745
+ emit,
2746
+ emitAgent,
2747
+ emitDocument,
2748
+ emitRuntime,
2749
+ emitSettings,
2750
+ emitSkill,
2751
+ emitSkillFile,
2752
+ extractExportedFunctionNames,
2753
+ extractExternalComponentDeclarations,
2754
+ extractFunctions,
2755
+ extractInputObjectLiteral,
2756
+ extractInterfaceProperties,
2757
+ extractLocalComponentDeclarations,
2758
+ extractPromptPlaceholders,
2759
+ extractRuntimeFnDeclarations,
2760
+ extractRuntimeVarDeclarations,
2761
+ extractText,
2762
+ extractTypeArguments,
2763
+ extractVariableDeclarations,
2764
+ findRootJsxElement,
2765
+ generateRuntime,
2766
+ getAgentName,
2767
+ getAgentPath,
2768
+ getArrayAttributeValue,
2769
+ getAttributeValue,
2770
+ getElementName,
2771
+ getJsxChildren,
2772
+ getRuntimeFn,
2773
+ getRuntimeFnRegistry,
2774
+ getRuntimeFunctionNames,
2775
+ getRuntimeImportPaths,
2776
+ getRuntimeVarInfo,
2777
+ hasRuntimeImports,
2778
+ isAgentRef,
2779
+ isDocument,
2780
+ isRuntimeFile,
2781
+ isRuntimeFn,
2782
+ isRuntimeNode,
2783
+ isRuntimeVar,
2784
+ isWhitespaceOnlyText,
2785
+ mergeSettings,
2786
+ normalizeWhitespace,
2787
+ parseFile,
2788
+ parseSource,
2789
+ resolveTypeImport,
2790
+ runtimeFn,
2791
+ toJqExpression,
2792
+ toJqPath,
2793
+ transform,
2794
+ transformRuntimeBlockChildren,
2795
+ transformRuntimeCommand,
2796
+ transformToRuntimeBlock,
2797
+ useOutput,
2798
+ useRuntimeVar
2799
+ };
2800
+ //# sourceMappingURL=index.js.map