{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../../src/agents/frontmatter.ts"],"names":[],"mappings":"AAyCA;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,GAAG,SAAS,CAWlF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA0FvG","sourcesContent":["/**\n * Escape regex special characters for use in a RegExp constructor.\n */\nfunction escapeRegex(s: string): string {\n\treturn s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Fold a YAML folded block scalar while preserving more-indented lines and\n * every blank-line separator. Trailing whitespace is trimmed.\n */\nfunction foldBlock(block: string): string {\n\tlet folded = \"\";\n\tlet hasContent = false;\n\tlet previousIsMoreIndented = false;\n\tlet blankLines = 0;\n\n\tfor (const line of block.split(\"\\n\")) {\n\t\tconst current = line.trimEnd();\n\t\tif (current.trim() === \"\") {\n\t\t\tif (hasContent) blankLines++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst currentIsMoreIndented = current.length > current.trimStart().length;\n\t\tif (hasContent) {\n\t\t\tif (blankLines > 0) {\n\t\t\t\tfolded += \"\\n\".repeat(blankLines + (previousIsMoreIndented || currentIsMoreIndented ? 1 : 0));\n\t\t\t} else {\n\t\t\t\tfolded += previousIsMoreIndented || currentIsMoreIndented ? \"\\n\" : \" \";\n\t\t\t}\n\t\t}\n\t\tfolded += current;\n\t\thasContent = true;\n\t\tpreviousIsMoreIndented = currentIsMoreIndented;\n\t\tblankLines = 0;\n\t}\n\n\treturn folded.trim();\n}\n\n/**\n * Normalize a simple-scalar frontmatter list from comma-separated or block-list syntax.\n * Only the standard `- item` marker is removed; ordinary hyphenated values stay intact.\n */\nexport function parseFrontmatterList(raw: string | undefined): string[] | undefined {\n\tif (raw === undefined) return undefined;\n\treturn raw\n\t\t.split(\"\\n\")\n\t\t.flatMap((line) => {\n\t\t\tconst value = line.trim();\n\t\t\tconst listItem = value.match(/^-\\s+(.+)$/);\n\t\t\treturn (listItem?.[1] ?? value).split(\",\");\n\t\t})\n\t\t.map((value) => value.trim())\n\t\t.filter(Boolean);\n}\n\n/**\n * Parse YAML frontmatter from agent/chain files.\n * Handles both flat (key: value) and nested block (key: \\n  sub: val) values.\n * Block values are stored as single strings with embedded newlines.\n * The indentation of the block content is preserved relative to the key.\n */\nexport function parseFrontmatter(content: string): { frontmatter: Record<string, string>; body: string } {\n\tconst frontmatter: Record<string, string> = {};\n\tconst normalized = content.replace(/\\r\\n/g, \"\\n\");\n\n\tif (!normalized.startsWith(\"---\")) {\n\t\treturn { frontmatter, body: normalized };\n\t}\n\n\tconst endIndex = normalized.indexOf(\"\\n---\", 3);\n\tif (endIndex === -1) {\n\t\treturn { frontmatter, body: normalized };\n\t}\n\n\tconst frontmatterBlock = normalized.slice(4, endIndex);\n\tconst body = normalized.slice(endIndex + 4).trim();\n\n\tconst lines = frontmatterBlock.split(\"\\n\");\n\tlet currentKey: string | null = null;\n\tlet currentBlockLines: string[] | null = null;\n\tlet currentIndent: number | null = null;\n\tlet currentFolded = false;\n\n\tfor (const line of lines) {\n\t\tconst indent = line.search(/\\S|$/); // position of first non-whitespace char\n\t\tconst trimmed = line.trim();\n\n\t\tif (\n\t\t\tcurrentKey !== null &&\n\t\t\tcurrentBlockLines !== null &&\n\t\t\t(indent > (currentIndent ?? 0) || (currentFolded && trimmed === \"\"))\n\t\t) {\n\t\t\t// This line is part of the current block value\n\t\t\tcurrentBlockLines.push(line);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Flush any pending block value\n\t\tif (currentKey !== null && currentBlockLines !== null) {\n\t\t\t// Strip the common leading whitespace from the block so the\n\t\t\t// serializer can add its own indentation level.\n\t\t\tconst rawBlock = currentBlockLines.join(\"\\n\");\n\t\t\tconst leadingSpaces = rawBlock.match(/^[ \\t]+(?=\\S)/m);\n\t\t\tconst prefix = leadingSpaces?.[0] ?? \"\";\n\t\t\tconst stripped = prefix\n\t\t\t\t? rawBlock.replace(new RegExp(`^${escapeRegex(prefix)}`, \"gm\"), \"\").replace(/^\\n/, \"\")\n\t\t\t\t: rawBlock;\n\t\t\tfrontmatter[currentKey] = currentFolded ? foldBlock(stripped) : stripped;\n\t\t\tcurrentKey = null;\n\t\t\tcurrentBlockLines = null;\n\t\t\tcurrentIndent = null;\n\t\t\tcurrentFolded = false;\n\t\t}\n\n\t\tconst match = line.match(/^([\\w-]+):\\s*(.*)$/);\n\t\tif (match) {\n\t\t\tconst [key, rawValueValue] = match.slice(1);\n\t\t\tif (key === undefined || rawValueValue === undefined) continue;\n\t\t\tconst rawValue = rawValueValue.trim();\n\t\t\tconst isQuoted =\n\t\t\t\t(rawValue.startsWith('\"') && rawValue.endsWith('\"')) ||\n\t\t\t\t(rawValue.startsWith(\"'\") && rawValue.endsWith(\"'\"));\n\t\t\tconst value = isQuoted ? rawValue.slice(1, -1) : rawValue;\n\t\t\tconst isFolded = !isQuoted && (rawValue === \">\" || rawValue === \">-\");\n\n\t\t\tif (value === \"\" || isFolded) {\n\t\t\t\t// Key with empty value or folded block indicator — defer storing until we see indent\n\t\t\t\tcurrentKey = key;\n\t\t\t\tcurrentBlockLines = [];\n\t\t\t\tcurrentIndent = indent;\n\t\t\t\tcurrentFolded = isFolded;\n\t\t\t} else {\n\t\t\t\t// Simple key: value\n\t\t\t\tfrontmatter[key] = value;\n\t\t\t}\n\t\t}\n\t\t// Lines that don't match a key pattern (e.g., comments, empty lines) are ignored\n\t}\n\n\t// Flush final block value\n\tif (currentKey !== null && currentBlockLines !== null) {\n\t\tconst rawBlock = currentBlockLines.join(\"\\n\");\n\t\tconst leadingSpaces = rawBlock.match(/^[ \\t]+(?=\\S)/m);\n\t\tconst prefix = leadingSpaces?.[0] ?? \"\";\n\t\tconst stripped = prefix\n\t\t\t? rawBlock.replace(new RegExp(`^${escapeRegex(prefix)}`, \"gm\"), \"\").replace(/^\\n/, \"\")\n\t\t\t: rawBlock;\n\t\tfrontmatter[currentKey] = currentFolded ? foldBlock(stripped) : stripped;\n\t}\n\n\treturn { frontmatter, body };\n}\n"]}