@mistralys/persona-builder 2.4.0 → 2.4.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/engine/partials.ts","../src/engine/conditionals.ts","../src/engine/variables.ts","../src/engine/postProcessor.ts","../src/engine/serializer.ts","../src/loaders/partials-loader.ts","../src/loaders/metadata-loader.ts","../src/loaders/content-loader.ts","../src/plugins/runner.ts","../src/targets/types.ts","../src/builders/frontmatter.ts","../src/targets/registry.ts","../src/targets/built-in.ts","../src/builders/persona-builder.ts","../src/validators/filename-validator.ts","../src/validators/strict-validator.ts","../src/utils/regex.ts","../src/index.ts"],"names":["readdir","path","readFile","yaml","existsSync","mkdir","writeFile","createRequire"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * partials.ts\r\n *\r\n * Pure template-engine function for resolving partial inclusions.\r\n * Supports {{> name}} syntax with up to depth-2 recursion to handle\r\n * partials-within-partials. No file-system I/O.\r\n */\r\n\r\n/**\r\n * Resolve partial inclusions in a template string.\r\n *\r\n * Replaces `{{> name}}` markers with the content from `partialsMap`.\r\n * Recursion is capped at depth 2 so that:\r\n * - depth 0 → 1: outer partials are expanded\r\n * - depth 1 → 2: one level of nested partials are expanded\r\n * - depth 2: recursion stops, marker is left as-is\r\n *\r\n * Each resolved partial is `trimEnd()`-ed to prevent trailing blank lines\r\n * from causing double-blank-line artefacts during concatenation.\r\n *\r\n * If a partial name is not found in `partialsMap`, the original marker is\r\n * preserved and a warning is emitted via `console.warn`.\r\n *\r\n * @param text - Template string potentially containing {{> name}} markers\r\n * @param partialsMap - Map of partial name → partial content\r\n * @param depth - **Internal recursion counter — callers must always omit this\r\n * parameter.** It exists solely so the function can track its own\r\n * nesting level across recursive calls. Passing a non-zero value\r\n * bypasses the depth guard (e.g. `resolvePartials(text, map, 5)`\r\n * expands nothing). This parameter will be removed from the public\r\n * signature and marked `@internal` in a future release.\r\n * @returns The template string with partial markers replaced\r\n */\r\nexport function resolvePartials(\r\n text: string,\r\n partialsMap: Record<string, string>,\r\n depth = 0,\r\n): string {\r\n if (depth >= 2) return text;\r\n return text.replace(/\\{\\{> ([\\w-]+)\\}\\}/g, (match, name: string) => {\r\n if (!(name in partialsMap)) {\r\n console.warn(`[WARN] Partial not found: ${match}`);\r\n return match;\r\n }\r\n // Recursively resolve nested partials (depth + 1).\r\n // trimEnd() strips trailing whitespace to avoid extra blank lines.\r\n return resolvePartials(partialsMap[name], partialsMap, depth + 1).trimEnd();\r\n });\r\n}\r\n","/**\r\n * conditionals.ts\r\n *\r\n * Pure template-engine function for resolving conditional blocks.\r\n * Handles {{#if flag}}…{{/if}}, {{#if flag}}…{{else}}…{{/if}}, and\r\n * {{#if flag}}…{{else if flag2}}…{{else}}…{{/if}} (chain) syntax,\r\n * including nested {{#if}} blocks inside {{else}} branches.\r\n * No file-system I/O.\r\n */\r\n\r\n/**\r\n * Negative-lookahead fragment that matches any character sequence containing\r\n * no `{{#if` opener. Used as a shared building-block in the regex patterns of\r\n * both `resolveElseIf` and `resolveConditionals` so the two guards stay in sync.\r\n * @internal\r\n */\r\nconst NO_NESTED_IF = String.raw`(?:(?!\\{\\{#if\\b)[\\s\\S])*?`;\r\n\r\n/**\r\n * Matches `{{else if flag}}…{{/if}}` segments whose content contains no\r\n * nested `{{#if` opener. Hoisted to module level to avoid constructing a new\r\n * `RegExp` object on every `resolveElseIf()` call.\r\n * `String.prototype.replace()` resets `lastIndex` to 0 before each call, so\r\n * the shared `g`-flag instance is safe for repeated use.\r\n * @internal\r\n */\r\nconst ELSE_IF_PATTERN = new RegExp(\r\n String.raw`\\{\\{else if (\\w+)\\}\\}(${NO_NESTED_IF})\\{\\{\\/if\\}\\}`,\r\n 'g',\r\n);\r\n\r\n/**\r\n * Pre-process `{{else if flag}}` chains by rewriting each innermost\r\n * occurrence into an equivalent nested `{{else}}{{#if flag}}` block.\r\n *\r\n * Examples:\r\n * `{{#if a}}A{{else if b}}B{{else}}C{{/if}}`\r\n * → `{{#if a}}A{{else}}{{#if b}}B{{else}}C{{/if}}{{/if}}`\r\n *\r\n * Multi-level chains are normalised iteratively, one level per pass:\r\n * `{{#if a}}A{{else if b}}B{{else if c}}C{{/if}}`\r\n * pass 1 → `{{#if a}}A{{else}}{{#if b}}B{{else if c}}C{{/if}}{{/if}}`\r\n * pass 2 → `{{#if a}}A{{else}}{{#if b}}B{{else}}{{#if c}}C{{/if}}{{/if}}{{/if}}`\r\n *\r\n * The pattern matches only segments whose content contains no nested\r\n * `{{#if` markers, mirroring the innermost-first invariant of\r\n * `resolveConditionals`. This ensures `{{else if}}` chains that are\r\n * themselves nested inside outer `{{#if}}…{{else}}…{{/if}}` blocks are\r\n * handled safely.\r\n *\r\n * @internal\r\n */\r\nfunction resolveElseIf(text: string): string {\r\n if (!text.includes('{{else if ')) {\r\n return text;\r\n }\r\n // Use the module-level ELSE_IF_PATTERN constant. replace() resets the\r\n // regex's lastIndex before each call, so sharing the instance is safe.\r\n let result = text;\r\n let prev: string;\r\n do {\r\n prev = result;\r\n result = result.replace(\r\n ELSE_IF_PATTERN,\r\n (_match: string, flag: string, content: string): string =>\r\n `{{else}}{{#if ${flag}}}${content}{{/if}}{{/if}}`,\r\n );\r\n } while (result !== prev);\r\n return result;\r\n}\r\n\r\n/**\r\n * Resolve conditional blocks in a template string.\r\n *\r\n * Syntax:\r\n * `{{#if flag}}content{{/if}}`\r\n * `{{#if flag}}truthy-content{{else}}falsy-content{{/if}}`\r\n * `{{#if flag}}truthy-content{{else if flag2}}branch2{{else}}falsy-content{{/if}}`\r\n *\r\n * Nested conditionals inside `{{else}}` branches are supported:\r\n * `{{#if a}}A{{else}}{{#if b}}B{{else}}C{{/if}}{{/if}}`\r\n *\r\n * `{{else if}}` chains are normalised into nested `{{#if}}` blocks before\r\n * resolution, so they work transparently alongside — and can be combined\r\n * with — traditional nested `{{#if}}` syntax.\r\n *\r\n * Behaviour:\r\n * - When `context[flag]` is truthy: the delimiters are stripped and the\r\n * content before `{{else}}` (or the entire inner block if no `{{else}}`)\r\n * is kept, surrounded by single `\\n` delimiters.\r\n * - When `context[flag]` is falsy and a `{{else}}` branch exists: the\r\n * content after `{{else}}` is kept, surrounded by single `\\n` delimiters.\r\n * - When `context[flag]` is falsy and no `{{else}}` branch exists: the\r\n * entire block (including surrounding newlines) is removed, leaving a\r\n * single `\\n`.\r\n * - Unknown flags (absent from context) are treated as falsy.\r\n *\r\n * Leading and trailing newlines within the kept content are trimmed so the\r\n * output does not accumulate extra blank lines.\r\n *\r\n * Nesting algorithm: the regex matches only *innermost* blocks — those\r\n * whose content contains no further `{{#if` markers. The replacement loop\r\n * repeats until the output stabilises, resolving each nesting level in\r\n * depth-first (innermost-first) order. This avoids the closing `{{/if}}`\r\n * ambiguity that arises with non-greedy single-pass matching.\r\n *\r\n * @param text - Template string potentially containing {{#if}} blocks\r\n * @param context - Key-value map used to evaluate flag truthiness\r\n * @returns The template string with conditional blocks resolved\r\n */\r\nexport function resolveConditionals(\r\n text: string,\r\n context: Record<string, unknown>,\r\n): string {\r\n // Normalise {{else if}} chains into nested {{else}}{{#if}} blocks so the\r\n // existing innermost-first resolution loop handles them transparently.\r\n const normalized = resolveElseIf(text);\r\n\r\n // Match only innermost conditional blocks — those whose truthy and falsy\r\n // content contains no nested `{{#if`. The negative lookahead\r\n // `(?!\\{\\{#if\\b)` ensures the quantifier stops before any nested opener,\r\n // so each pass resolves one depth level. Subsequent passes bubble outward\r\n // until the output stabilises (no more `{{#if` markers remain).\r\n const pattern = new RegExp(\r\n String.raw`\\n*\\{\\{#if (\\w+)\\}\\}(${NO_NESTED_IF})` +\r\n String.raw`(?:\\{\\{else\\}\\}(${NO_NESTED_IF}))?\\{\\{\\/if\\}\\}\\n*`,\r\n 'g',\r\n );\r\n\r\n const resolve = (\r\n _match: string,\r\n flag: string,\r\n inner: string,\r\n elseInner: string | undefined,\r\n ): string => {\r\n if (context[flag]) {\r\n // Truthy: keep content before {{else}} (or entire inner if no {{else}})\r\n return '\\n' + inner.replace(/^\\n+/, '').replace(/\\n+$/, '') + '\\n';\r\n }\r\n if (elseInner !== undefined) {\r\n // Falsy with {{else}}: keep content after {{else}}\r\n return '\\n' + elseInner.replace(/^\\n+/, '').replace(/\\n+$/, '') + '\\n';\r\n }\r\n // Falsy without {{else}}: remove entire block\r\n return '\\n';\r\n };\r\n\r\n let result = normalized;\r\n let prev: string;\r\n do {\r\n prev = result;\r\n result = result.replace(pattern, resolve);\r\n } while (result !== prev);\r\n return result;\r\n}\r\n","/**\r\n * variables.ts\r\n *\r\n * Pure template-engine function for resolving variable substitutions.\r\n * Handles {{varName}} syntax. No file-system I/O.\r\n */\r\n\r\n/**\r\n * Resolve variable substitutions in a template string.\r\n *\r\n * Replaces `{{varName}}` markers with `String(context[varName])`.\r\n * If a variable is not found in `context` (or its value is `undefined`),\r\n * the original marker is preserved and a warning is emitted via\r\n * `console.warn`, identifying the file by `filename` for easier debugging.\r\n *\r\n * Note: this step must run AFTER `resolvePartials` and `resolveConditionals`\r\n * so that only plain variable markers remain.\r\n *\r\n * @param text - Template string potentially containing {{varName}} markers\r\n * @param context - Key-value map of variable name → value\r\n * @param filename - Identifier used in warning messages (e.g. persona file path)\r\n * @returns The template string with variable markers substituted\r\n */\r\nexport function resolveVariables(\r\n text: string,\r\n context: Record<string, unknown>,\r\n filename: string,\r\n): string {\r\n return text.replace(/\\{\\{(\\w+)\\}\\}/g, (match, varName: string) => {\r\n if (varName in context && context[varName] !== undefined) {\r\n return String(context[varName]);\r\n }\r\n console.warn(`[WARN] Unresolved variable: ${match} in ${filename}`);\r\n return match;\r\n });\r\n}\r\n","/**\r\n * postProcessor.ts\r\n *\r\n * Pure post-processing functions for cleaning up rendered persona output.\r\n * All functions are side-effect-free and operate only on strings.\r\n * No file-system I/O.\r\n */\r\n\r\n/**\r\n * Collapse 3 or more consecutive blank lines into 2 blank lines.\r\n *\r\n * Specifically converts 4 or more consecutive `\\n` characters into `\\n\\n\\n`\r\n * (which equals 2 blank lines between paragraphs).\r\n *\r\n * @param text - Rendered output string\r\n * @returns String with excessive blank lines collapsed\r\n */\r\nexport function collapseBlankLines(text: string): string {\r\n return text.replace(/\\n{4,}/g, '\\n\\n\\n');\r\n}\r\n\r\n/**\r\n * Ensure every Markdown heading has a blank line immediately before it.\r\n *\r\n * Also ensures horizontal rules (`---`) have a blank line before and after\r\n * them. This corrects spacing gaps caused by partial concatenation where\r\n * `trimEnd()` strips trailing newlines and conditionals add only a single\r\n * `\\n` delimiter.\r\n *\r\n * @param text - Rendered output string\r\n * @returns String with blank lines inserted before headings and rules\r\n */\r\nexport function ensureBlankLineBeforeHeadings(text: string): string {\r\n // Blank line before headings\r\n let result = text.replace(/([^\\n])\\n(#{1,6} )/g, '$1\\n\\n$2');\r\n // Blank line before horizontal rules (---)\r\n result = result.replace(/([^\\n])\\n(---)\\n/g, '$1\\n\\n$2\\n');\r\n // Blank line after horizontal rules (---)\r\n result = result.replace(/\\n(---)\\n([^\\n])/g, '\\n$1\\n\\n$2');\r\n return result;\r\n}\r\n\r\n/**\r\n * Normalize line endings to LF (`\\n`) for OS-agnostic output.\r\n *\r\n * Converts CRLF (`\\r\\n`) first, then strips any remaining stray CR (`\\r`).\r\n *\r\n * @param text - String potentially containing CRLF or CR line endings\r\n * @returns String with all line endings normalized to LF\r\n */\r\nexport function normalizeNewlines(text: string): string {\r\n return text.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\r\n}\r\n","/**\r\n * serializer.ts\r\n *\r\n * Pure serializer functions for converting tool lists to YAML-compatible\r\n * string representations. No file-system I/O.\r\n */\r\n\r\n/**\r\n * Serialize a tools array in YAML single-quote flow format WITH outer brackets.\r\n *\r\n * Output format: `['tool1', 'tool2', 'tool3']`\r\n * Used by the ledger suite to preserve byte-identical frontmatter output.\r\n *\r\n * @param tools - Array of tool name strings\r\n * @returns YAML flow-sequence string including outer brackets\r\n *\r\n * @example\r\n * serializeTools(['Bash', 'Read']) // => \"['Bash', 'Read']\"\r\n * serializeTools([]) // => \"[]\"\r\n */\r\nexport function serializeTools(tools: string[]): string {\r\n return '[' + tools.map((t) => `'${t}'`).join(', ') + ']';\r\n}\r\n\r\n/**\r\n * Serialize a tools array in YAML single-quote flow format WITHOUT outer brackets.\r\n *\r\n * Output format: `'tool1', 'tool2', 'tool3'`\r\n * Used inside standalone frontmatter templates which supply the surrounding `[ ]`.\r\n *\r\n * @param tools - Array of tool name strings\r\n * @returns Comma-separated quoted tool names (no outer brackets)\r\n *\r\n * @example\r\n * serializeToolsList(['Bash', 'Read']) // => \"'Bash', 'Read'\"\r\n * serializeToolsList([]) // => \"\"\r\n */\r\nexport function serializeToolsList(tools: string[]): string {\r\n return tools.map((t) => `'${t}'`).join(', ');\r\n}\r\n","/**\r\n * src/loaders/partials-loader.ts\r\n *\r\n * File-system loader for Handlebars-style partial snippets.\r\n *\r\n * Reads every `.md` file in `dir`, keys each entry by the filename stem\r\n * (i.e. the portion before the final `.md` extension), and returns the\r\n * map. Callers that need a two-layer (shared → suite-local override)\r\n * setup should call `loadPartials` twice and merge the results themselves,\r\n * with the suite-local result spreading last.\r\n *\r\n * All file reads are performed asynchronously. Path construction uses\r\n * `path.join` and `path.posix`-compatible operations so no path-separator\r\n * assumptions are baked in.\r\n */\r\n\r\nimport { readdir, readFile } from 'node:fs/promises';\r\nimport path from 'node:path';\r\n\r\n/**\r\n * Load all `.md` files in `dir` and return them as a `Record<string, string>`\r\n * keyed by filename stem.\r\n *\r\n * Files whose names do not end in `.md` are silently ignored.\r\n * The directory must exist; a missing directory throws an `ENOENT` error from\r\n * the underlying `readdir` call (let callers decide how to handle absence).\r\n *\r\n * @param dir Absolute (or relative) path to the directory to scan.\r\n * @returns A map from filename stem → file content string.\r\n *\r\n * @example\r\n * const partials = await loadPartials('/project/partials');\r\n * // { greeting: 'Hello, {{name}}!', footer: '---\\nEnd of file' }\r\n */\r\nexport async function loadPartials(dir: string): Promise<Record<string, string>> {\r\n const entries = await readdir(dir, { withFileTypes: true });\r\n\r\n const mdFiles = entries.filter(\r\n (entry) => entry.isFile() && entry.name.endsWith('.md'),\r\n );\r\n\r\n const pairs = await Promise.all(\r\n mdFiles.map(async (entry) => {\r\n const stem = entry.name.slice(0, -'.md'.length); // strip trailing \".md\"\r\n const filePath = path.join(dir, entry.name);\r\n const content = await readFile(filePath, 'utf8');\r\n return [stem, content] as [string, string];\r\n }),\r\n );\r\n\r\n return Object.fromEntries(pairs);\r\n}\r\n","/**\r\n * src/loaders/metadata-loader.ts\r\n *\r\n * File-system loader for persona YAML metadata files.\r\n *\r\n * Provides two exports:\r\n *\r\n * 1. `discoverPersonaYamls(root)` — recursively walks `root` and returns\r\n * absolute paths for every `*.yaml` file found, regardless of nesting\r\n * depth. Uses Node's built-in `fs.readdir` with `recursive: true`\r\n * (available since Node 18.17). No glob library is required.\r\n *\r\n * 2. `loadMetadata(yamlPath)` — reads a single YAML file and parses it\r\n * with `js-yaml` into a fully typed `PersonaMetadata` object.\r\n *\r\n * Path construction relies exclusively on `node:path` so the output is\r\n * correct on both POSIX and Windows.\r\n */\r\n\r\nimport { readdir, readFile } from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport yaml from 'js-yaml';\r\nimport type { PersonaMetadata } from '../plugins/types.js';\r\n\r\n// Re-export the type so consumers can import it directly from this module\r\nexport type { PersonaMetadata };\r\n\r\n// ---------------------------------------------------------------------------\r\n// YAML discovery\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Recursively discover all `*.yaml` files under `root` and return their\r\n * absolute paths sorted lexicographically.\r\n *\r\n * Uses `readdir` with `{ recursive: true }` (Node ≥ 18.17). Each returned\r\n * path is normalised through `path.resolve` so callers always receive\r\n * absolute, platform-consistent paths.\r\n *\r\n * @param root The directory to search (absolute or resolvable relative path).\r\n * @returns Sorted array of absolute paths to every `*.yaml` file found.\r\n *\r\n * @example\r\n * const yamls = await discoverPersonaYamls('/project/personas/ledger/src/meta');\r\n * // ['/project/personas/ledger/src/meta/alpha.yaml', ...]\r\n */\r\nexport async function discoverPersonaYamls(root: string): Promise<string[]> {\r\n const absRoot = path.resolve(root);\r\n\r\n // Node ≥ 18.17: readdir with recursive returns relative paths from root\r\n const allEntries = await readdir(absRoot, { recursive: true, withFileTypes: false });\r\n\r\n const yamlPaths = (allEntries as string[])\r\n .filter((entry) => entry.endsWith('.yaml'))\r\n .map((entry) => path.join(absRoot, entry))\r\n .sort();\r\n\r\n return yamlPaths;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// YAML parsing\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Load and parse a single persona YAML file into a typed `PersonaMetadata`\r\n * object.\r\n *\r\n * The YAML is parsed using `js-yaml`'s safe `load` function. The result\r\n * is validated to be a non-null object; if the YAML is empty or does not\r\n * parse to an object, an `Error` is thrown.\r\n *\r\n * `PersonaMetadata` requires a `name` field. If the YAML does not contain\r\n * a `name` key the function throws an `Error` with a descriptive message.\r\n *\r\n * @param yamlPath Absolute path to the YAML file.\r\n * @returns Parsed and validated `PersonaMetadata` object.\r\n * @throws `Error` when the file is unparseable, not an object, or\r\n * is missing the required `name` field.\r\n *\r\n * @example\r\n * const meta = await loadMetadata('/project/meta/my-persona.yaml');\r\n * // { name: 'my-persona', description: '...', tools: [...] }\r\n */\r\nexport async function loadMetadata(yamlPath: string): Promise<PersonaMetadata> {\r\n const raw = await readFile(yamlPath, 'utf8');\r\n\r\n const parsed: unknown = yaml.load(raw);\r\n\r\n if (parsed === null || parsed === undefined || typeof parsed !== 'object' || Array.isArray(parsed)) {\r\n throw new Error(\r\n `loadMetadata: expected a YAML object in \"${yamlPath}\", got ${\r\n Array.isArray(parsed) ? 'array' : String(parsed)\r\n }`,\r\n );\r\n }\r\n\r\n const record = parsed as Record<string, unknown>;\r\n\r\n if (typeof record['name'] !== 'string' || record['name'].trim() === '') {\r\n throw new Error(\r\n `loadMetadata: YAML file \"${yamlPath}\" is missing a required string field \"name\"`,\r\n );\r\n }\r\n\r\n return record as PersonaMetadata;\r\n}\r\n","/**\r\n * src/loaders/content-loader.ts\r\n *\r\n * File-system loader for persona Markdown content templates.\r\n *\r\n * Provides a single `loadContent` function that reads the raw string content\r\n * of a persona Markdown file from disk. The content is returned exactly as\r\n * stored — no template substitution, no post-processing. Those concerns\r\n * belong to the engine layer.\r\n *\r\n * All I/O is asynchronous. Path construction uses `node:path` so the\r\n * implementation is path-separator–agnostic.\r\n */\r\n\r\nimport { readFile } from 'node:fs/promises';\r\nimport path from 'node:path';\r\n\r\n/**\r\n * Read a persona Markdown content file and return its raw string content.\r\n *\r\n * The file is read with UTF-8 encoding. No parsing, template resolution,\r\n * or post-processing is applied — that is the engine layer's responsibility.\r\n *\r\n * @param mdPath Absolute (or resolvable relative) path to the `.md` file.\r\n * @returns Raw UTF-8 string content of the file.\r\n * @throws An `ENOENT` error (from `fs/promises`) if the file does not\r\n * exist, or any other I/O error the OS reports.\r\n *\r\n * @example\r\n * const body = await loadContent('/project/content/my-persona.md');\r\n * // '{{> greeting}}\\n\\n## About\\n\\nThis is {{name}}...'\r\n */\r\nexport async function loadContent(mdPath: string): Promise<string> {\r\n const absPath = path.resolve(mdPath);\r\n return readFile(absPath, 'utf8');\r\n}\r\n","/**\r\n * src/plugins/runner.ts\r\n *\r\n * Plugin runner — responsible for invoking plugin hooks in registration order.\r\n *\r\n * Each exported function corresponds to one lifecycle hook defined in\r\n * PersonaBuildPlugin. The runner:\r\n * - Skips plugins that do not implement the requested hook (hook is optional)\r\n * - Invokes hooks in the order plugins are registered (first-in first-called)\r\n * - For accumulating hooks (onBuildContext, onPartials, onPersonaPartials,\r\n * onPostRender), each plugin receives the output of the previous plugin\r\n * as its first argument\r\n * - For collecting hooks (onValidate), results are concatenated into a\r\n * flat array\r\n *\r\n * No file-system I/O. No async operations.\r\n */\r\n\r\nimport type {\r\n PersonaBuildPlugin,\r\n PersonaMetadata,\r\n SuiteConfig,\r\n TargetType,\r\n ValidationResult,\r\n} from './types.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// Suite-level hook\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Invoke the `onSuiteInit` hook on every registered plugin.\r\n *\r\n * Each plugin may optionally implement this hook. Plugins are called in\r\n * registration order. The hook receives the suite config and a mutable\r\n * `sharedMeta` object — plugins may mutate `sharedMeta` in place; the\r\n * same reference is passed to every subsequent plugin.\r\n *\r\n * @param plugins Ordered list of registered plugins\r\n * @param suite The suite configuration object\r\n * @param sharedMeta Mutable shared metadata object (mutated in place by plugins)\r\n */\r\nexport function runSuiteInit(\r\n plugins: PersonaBuildPlugin[],\r\n suite: SuiteConfig,\r\n sharedMeta: Record<string, unknown>,\r\n): void {\r\n for (const plugin of plugins) {\r\n if (typeof plugin.onSuiteInit === 'function') {\r\n plugin.onSuiteInit(suite, sharedMeta);\r\n }\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Per-persona context accumulation\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Invoke the `onBuildContext` hook on every registered plugin, accumulating\r\n * context mutations sequentially.\r\n *\r\n * Each plugin receives the context returned by the previous plugin. If a\r\n * plugin does not implement `onBuildContext`, the context passes through\r\n * unchanged. The final accumulated context is returned.\r\n *\r\n * @param plugins Ordered list of registered plugins\r\n * @param ctx Initial rendering context for this persona\r\n * @param persona Typed metadata for the persona being built\r\n * @param suite The suite configuration object\r\n * @param target The current build target (optional — forwarded to each plugin)\r\n * @returns Accumulated rendering context after all plugins have run\r\n */\r\nexport function runBuildContext(\r\n plugins: PersonaBuildPlugin[],\r\n ctx: Record<string, unknown>,\r\n persona: PersonaMetadata,\r\n suite: SuiteConfig,\r\n target?: TargetType,\r\n): Record<string, unknown> {\r\n let accumulated = ctx;\r\n for (const plugin of plugins) {\r\n if (typeof plugin.onBuildContext === 'function') {\r\n accumulated = plugin.onBuildContext(accumulated, persona, suite, target) ?? accumulated;\r\n }\r\n }\r\n return accumulated;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Per-persona post-render chain\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Invoke the `onPostRender` hook on every registered plugin, chaining the\r\n * output string sequentially.\r\n *\r\n * Each plugin receives the string returned by the previous plugin. If a\r\n * plugin does not implement `onPostRender`, the string passes through\r\n * unchanged. The final string is returned.\r\n *\r\n * @param plugins Ordered list of registered plugins\r\n * @param rendered Initial rendered output string\r\n * @param persona Typed metadata for the persona being built\r\n * @param target The current build target\r\n * @returns Final output string after all plugins have run\r\n */\r\nexport function runPostRender(\r\n plugins: PersonaBuildPlugin[],\r\n rendered: string,\r\n persona: PersonaMetadata,\r\n target: TargetType,\r\n): string {\r\n let output = rendered;\r\n for (const plugin of plugins) {\r\n if (typeof plugin.onPostRender === 'function') {\r\n output = plugin.onPostRender(output, persona, target) ?? output;\r\n }\r\n }\r\n return output;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Suite-level partials accumulation\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Invoke the `onPartials` hook on every registered plugin, accumulating\r\n * partials map mutations sequentially.\r\n *\r\n * Each plugin receives the partials map returned by the previous plugin. If a\r\n * plugin does not implement `onPartials`, the map passes through unchanged.\r\n * The final accumulated map is returned.\r\n *\r\n * Called once per suite after partials are loaded from disk, before any\r\n * persona is rendered.\r\n *\r\n * @param plugins Ordered list of registered plugins\r\n * @param partialsMap Initial map of partial name → partial content\r\n * @param suiteName The identifier of the current suite\r\n * @param suite The suite configuration object\r\n * @returns Accumulated partials map after all plugins have run\r\n */\r\nexport function runPartials(\r\n plugins: PersonaBuildPlugin[],\r\n partialsMap: Record<string, string>,\r\n suiteName: string,\r\n suite: SuiteConfig,\r\n): Record<string, string> {\r\n let accumulated = partialsMap;\r\n for (const plugin of plugins) {\r\n if (typeof plugin.onPartials === 'function') {\r\n accumulated = plugin.onPartials(accumulated, suiteName, suite) ?? accumulated;\r\n }\r\n }\r\n return accumulated;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Per-persona partials accumulation\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Invoke the `onPersonaPartials` hook on every registered plugin, accumulating\r\n * partials map mutations sequentially.\r\n *\r\n * Each plugin receives the partials map returned by the previous plugin. If a\r\n * plugin does not implement `onPersonaPartials`, the map passes through\r\n * unchanged. The final accumulated map is returned.\r\n *\r\n * Called for each persona (and target) after `onBuildContext`, before\r\n * template rendering.\r\n *\r\n * @param plugins Ordered list of registered plugins\r\n * @param partialsMap Initial map of partial name → partial content\r\n * @param persona Typed metadata for the persona being built\r\n * @param context The rendering context built by `onBuildContext`\r\n * @param suite The suite configuration object\r\n * @param target The current build target (optional)\r\n * @returns Accumulated partials map after all plugins have run\r\n */\r\nexport function runPersonaPartials(\r\n plugins: PersonaBuildPlugin[],\r\n partialsMap: Record<string, string>,\r\n persona: PersonaMetadata,\r\n context: Record<string, unknown>,\r\n suite: SuiteConfig,\r\n target?: TargetType,\r\n): Record<string, string> {\r\n let accumulated = partialsMap;\r\n for (const plugin of plugins) {\r\n if (typeof plugin.onPersonaPartials === 'function') {\r\n accumulated = plugin.onPersonaPartials(accumulated, persona, context, suite, target) ?? accumulated;\r\n }\r\n }\r\n return accumulated;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Per-persona validation collection\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Invoke the `onValidate` hook on every registered plugin and collect all\r\n * returned ValidationResult objects into a single flat array.\r\n *\r\n * Plugins that do not implement `onValidate` contribute nothing to the result.\r\n * The return value is always an array (never null/undefined).\r\n *\r\n * @param plugins Ordered list of registered plugins\r\n * @param persona Typed metadata for the persona being built\r\n * @param suite The suite configuration object\r\n * @param target The current build target (optional — forwarded to each plugin)\r\n * @returns Flat array of all ValidationResult objects from all plugins\r\n */\r\nexport function runValidate(\r\n plugins: PersonaBuildPlugin[],\r\n persona: PersonaMetadata,\r\n suite: SuiteConfig,\r\n target?: TargetType,\r\n): ValidationResult[] {\r\n const results: ValidationResult[] = [];\r\n for (const plugin of plugins) {\r\n if (typeof plugin.onValidate === 'function') {\r\n const pluginResults = plugin.onValidate(persona, suite, target);\r\n results.push(...pluginResults);\r\n }\r\n }\r\n return results;\r\n}\r\n","/**\r\n * src/targets/types.ts\r\n *\r\n * Target type definitions for @mistralys/persona-builder.\r\n *\r\n * Defines the TargetDefinition interface and well-known target name constants.\r\n */\r\n\r\n// ---------------------------------------------------------------------------\r\n// TargetDefinition interface\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Describes a build target — maps a target name to its output directory key,\r\n * filename context key, default frontmatter template, and optional\r\n * auto-injected context flags.\r\n */\r\nexport interface TargetDefinition {\r\n /** Unique target identifier (e.g. 'vscode', 'claude-code'). */\r\n name: string;\r\n\r\n /**\r\n * Key used to look up the output directory in the suite's output dir map.\r\n * For built-in targets this matches the target name.\r\n */\r\n outputDirKey: string;\r\n\r\n /**\r\n * Context field name that holds a custom output filename for this target.\r\n * When present and non-empty in the rendered context, it overrides the\r\n * default filename derived from the persona name.\r\n */\r\n filenameContextKey?: string;\r\n\r\n /**\r\n * Default frontmatter template string for this target.\r\n * Used when neither a plugin nor a BuildConfig template is provided.\r\n */\r\n defaultFrontmatter: string;\r\n\r\n /**\r\n * Declarative context flags merged into the build context when this target\r\n * is rendered. Useful for `{{#if target_vscode}}` guards.\r\n *\r\n * All entries are spread into the context by `buildContext()` via a\r\n * registry lookup — `registry.get(target).contextFlags`. The fallback path\r\n * (for targets absent from the registry) injects a single boolean flag\r\n * derived from the target name: `target_${name.replace(/-/g, '_')} = true`.\r\n */\r\n contextFlags?: Record<string, unknown>;\r\n\r\n /**\r\n * Whether this target is included in the default build when no explicit\r\n * `targets` array is configured. Defaults to `true` when omitted.\r\n *\r\n * Set to `false` for opt-in targets (e.g. `'deep-agents'`) that should\r\n * only be built when explicitly requested via `config.targets`.\r\n */\r\n defaultEnabled?: boolean;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Well-known target name constants\r\n// ---------------------------------------------------------------------------\r\n\r\n/** Well-known name constant for the VS Code target. */\r\nexport const TARGET_VSCODE = 'vscode' as const;\r\n\r\n/** Well-known name constant for the Claude Code target. */\r\nexport const TARGET_CLAUDE_CODE = 'claude-code' as const;\r\n\r\n/** Well-known name constant for the Deep Agents target. */\r\nexport const TARGET_DEEP_AGENTS = 'deep-agents' as const;\r\n\r\n// ---------------------------------------------------------------------------\r\n// Default frontmatter templates (owned by the targets layer)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Default VS Code frontmatter template.\r\n *\r\n * Minimal fields that work for standalone personas. Projects using numbered\r\n * workflows (e.g. ledger) should inject a richer template via a plugin.\r\n */\r\nexport const DEFAULT_FRONTMATTER_VSCODE = `---\r\nname: '{{name}} v{{version}}'\r\ndescription: '{{description}}'\r\ntools: [{{tools_list}}]\r\n---`;\r\n\r\n/**\r\n * Default Claude Code frontmatter template.\r\n *\r\n * Minimal fields that work for standalone personas. Projects using numbered\r\n * workflows should inject a richer template via a plugin.\r\n */\r\nexport const DEFAULT_FRONTMATTER_CLAUDE_CODE = `---\r\nname: {{cc_file_name_stem}}\r\npermissionMode: {{cc_permission_mode}}\r\nmodel: {{cc_model}}\r\nmemory: {{cc_memory}}\r\nallowedTools: [{{cc_tools_list}}]\r\n---`;\r\n\r\n/**\r\n * Default Deep Agents frontmatter template.\r\n *\r\n * Minimal fields — no IDE-specific properties. Suitable for headless\r\n * LangGraph / Deep Agents pipeline executors that consume persona files\r\n * without an IDE host.\r\n */\r\nexport const DEFAULT_FRONTMATTER_DEEP_AGENTS = `---\r\nname: {{name}}\r\ndescription: {{description}}\r\n---`;\r\n","/**\r\n * src/builders/frontmatter.ts\r\n *\r\n * Frontmatter template registry for @mistralys/persona-builder.\r\n *\r\n * Ships three minimal default templates — one per built-in target — that work for the\r\n * \"standalone\" persona mode (simple personas without numbered workflows or\r\n * MCP server blocks). Projects needing richer frontmatter register custom\r\n * templates via the `PersonaBuildPlugin.frontmatterTemplates` property.\r\n *\r\n * Template rendering follows the same two-step sequence as body rendering:\r\n * 1. resolveConditionals() — resolve {{#if flag}} blocks\r\n * 2. resolveVariables() — substitute {{varName}} markers\r\n *\r\n * No partials in frontmatter — frontmatter is kept deliberately simple.\r\n */\r\n\r\nimport { resolveConditionals } from '../engine/conditionals.js';\r\nimport { resolveVariables } from '../engine/variables.js';\r\nimport type { PersonaBuildPlugin } from '../plugins/types.js';\r\nimport type { TargetRegistry } from '../targets/registry.js';\r\n\r\n// Default templates are owned by the targets layer; re-exported here so\r\n// the public API surface (src/builders/index.ts) is unchanged.\r\nimport {\r\n DEFAULT_FRONTMATTER_VSCODE,\r\n DEFAULT_FRONTMATTER_CLAUDE_CODE,\r\n DEFAULT_FRONTMATTER_DEEP_AGENTS,\r\n} from '../targets/types.js';\r\nexport { DEFAULT_FRONTMATTER_VSCODE, DEFAULT_FRONTMATTER_CLAUDE_CODE, DEFAULT_FRONTMATTER_DEEP_AGENTS };\r\n\r\n// ---------------------------------------------------------------------------\r\n// Template resolution\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Resolve frontmatter template precedence.\r\n *\r\n * Precedence order (highest wins):\r\n * 1. Plugin `frontmatterTemplates` — plugins are checked in registration\r\n * order; the first plugin with a matching key wins.\r\n * 2. `configTemplates` — templates passed via `BuildConfig.frontmatter`\r\n * 3. Registry default — `TargetDefinition.defaultFrontmatter` for the target\r\n * 4. Library default (`DEFAULT_FRONTMATTER_VSCODE`) — safety fallback only\r\n *\r\n * @param target The build target name (e.g. `'vscode'`, `'claude-code'`, or a custom target)\r\n * @param plugins Registered plugins (searched in order; first match wins)\r\n * @param configTemplates Optional caller-supplied overrides from BuildConfig\r\n * @param registry Optional TargetRegistry for resolving the built-in default template\r\n * @returns The resolved template string\r\n */\r\nexport function resolveFrontmatterTemplate(\r\n target: string,\r\n plugins: PersonaBuildPlugin[],\r\n configTemplates?: Record<string, string>,\r\n registry?: TargetRegistry,\r\n): string {\r\n // Check plugins in registration order — first plugin with a matching\r\n // frontmatterTemplates entry wins.\r\n for (const plugin of plugins) {\r\n if (plugin.frontmatterTemplates && target in plugin.frontmatterTemplates) {\r\n const tpl = plugin.frontmatterTemplates[target];\r\n if (tpl !== undefined) return tpl;\r\n }\r\n }\r\n\r\n // Caller-supplied config templates\r\n if (configTemplates && target in configTemplates) {\r\n const tpl = configTemplates[target];\r\n if (tpl !== undefined) return tpl;\r\n }\r\n\r\n // Registry default (covers all registered targets, including custom ones)\r\n if (registry && registry.has(target)) {\r\n return registry.get(target).defaultFrontmatter;\r\n }\r\n\r\n // Absolute fallback — should not be reached in normal usage\r\n return DEFAULT_FRONTMATTER_VSCODE;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Frontmatter rendering\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Render a frontmatter template string against the given context.\r\n *\r\n * Applies the standard two-step template resolution:\r\n * 1. `resolveConditionals` — `{{#if flag}}` blocks\r\n * 2. `resolveVariables` — `{{varName}}` substitution\r\n *\r\n * @param template The raw frontmatter template string (may contain markers)\r\n * @param context Key-value context for variable substitution\r\n * @param filename Source filename used in warning messages\r\n * @returns Rendered frontmatter string (ready to prepend to body)\r\n */\r\nexport function renderFrontmatter(\r\n template: string,\r\n context: Record<string, unknown>,\r\n filename: string,\r\n): string {\r\n let rendered = resolveConditionals(template, context);\r\n rendered = resolveVariables(rendered, context, filename);\r\n return rendered;\r\n}\r\n","/**\r\n * src/targets/registry.ts\r\n *\r\n * TargetRegistry — holds TargetDefinition entries and allows consumers to\r\n * register custom build targets alongside (or instead of) the built-in ones.\r\n */\r\n\r\nimport type { TargetDefinition } from './types.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// TargetRegistry class\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Registry that maps target names to their TargetDefinition objects.\r\n *\r\n * Consumers can extend the default build system by calling `register()` with\r\n * a custom TargetDefinition before invoking `build()`.\r\n *\r\n * @example\r\n * ```ts\r\n * import { defaultRegistry } from '@mistralys/persona-builder';\r\n *\r\n * defaultRegistry.register({\r\n * name: 'my-target',\r\n * outputDirKey: 'my-target',\r\n * defaultFrontmatter: '---\\nmy: frontmatter\\n---',\r\n * contextFlags: { target_my_target: true },\r\n * });\r\n * ```\r\n */\r\nexport class TargetRegistry {\r\n // Map preserves insertion order — names() and allDefinitions() are\r\n // therefore deterministic and match registration sequence. This is\r\n // intentional: the built-in registry guarantees ['vscode', 'claude-code']\r\n // ordering for the default targets (AC-2).\r\n private readonly _definitions = new Map<string, TargetDefinition>();\r\n\r\n /**\r\n * Register a new target definition.\r\n *\r\n * @param definition The target descriptor to register.\r\n * @throws {Error} If a target with the same `name` is already registered.\r\n */\r\n register(definition: TargetDefinition): void {\r\n if (this._definitions.has(definition.name)) {\r\n throw new Error(\r\n `TargetRegistry: target \"${definition.name}\" is already registered. ` +\r\n `Use a unique name or remove the existing registration first.`,\r\n );\r\n }\r\n this._definitions.set(definition.name, definition);\r\n }\r\n\r\n /**\r\n * Retrieve a registered target definition by name.\r\n *\r\n * Returns a shallow copy — mutating the returned object does not affect\r\n * the registry's internal state.\r\n *\r\n * @param name The target name to look up.\r\n * @returns A shallow copy of the matching TargetDefinition.\r\n * @throws {Error} If no target with the given name is registered.\r\n */\r\n get(name: string): TargetDefinition {\r\n const def = this._definitions.get(name);\r\n if (!def) {\r\n const known = this.names().join(', ') || '(none)';\r\n throw new Error(\r\n `TargetRegistry: target \"${name}\" is not registered. ` +\r\n `Registered targets: ${known}.`,\r\n );\r\n }\r\n return { ...def };\r\n }\r\n\r\n /**\r\n * Returns `true` if a target with the given name is registered.\r\n *\r\n * @param name The target name to check.\r\n */\r\n has(name: string): boolean {\r\n return this._definitions.has(name);\r\n }\r\n\r\n /**\r\n * Returns the names of all registered targets, in registration order.\r\n */\r\n names(): string[] {\r\n return Array.from(this._definitions.keys());\r\n }\r\n\r\n /**\r\n * Returns all registered TargetDefinition objects, in registration order.\r\n *\r\n * Returns shallow copies — mutating a returned definition does not affect\r\n * the registry's internal state.\r\n */\r\n allDefinitions(): TargetDefinition[] {\r\n return Array.from(this._definitions.values()).map(def => ({ ...def }));\r\n }\r\n\r\n /**\r\n * Returns a new TargetRegistry pre-populated with the same definitions.\r\n *\r\n * Useful for test isolation: clone the `defaultRegistry` to get an\r\n * independent copy that can be mutated without affecting the singleton.\r\n */\r\n clone(): TargetRegistry {\r\n const copy = new TargetRegistry();\r\n for (const def of this._definitions.values()) {\r\n copy.register({ ...def });\r\n }\r\n return copy;\r\n }\r\n}\r\n","/**\r\n * src/targets/built-in.ts\r\n *\r\n * Creates and exports the defaultRegistry — a TargetRegistry pre-populated\r\n * with the three built-in targets: 'vscode', 'claude-code', and 'deep-agents'.\r\n *\r\n * Consumers import defaultRegistry when they need to register additional\r\n * custom targets or query the built-in target definitions.\r\n */\r\n\r\nimport { TargetRegistry } from './registry.js';\r\nimport {\r\n DEFAULT_FRONTMATTER_CLAUDE_CODE,\r\n DEFAULT_FRONTMATTER_DEEP_AGENTS,\r\n DEFAULT_FRONTMATTER_VSCODE,\r\n TARGET_CLAUDE_CODE,\r\n TARGET_DEEP_AGENTS,\r\n TARGET_VSCODE,\r\n} from './types.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// Default registry\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Singleton TargetRegistry pre-populated with the three built-in targets:\r\n * `'vscode'`, `'claude-code'`, and `'deep-agents'`.\r\n *\r\n * Import and call `register()` on this instance to add custom targets before\r\n * invoking `build()`.\r\n *\r\n * **Warning:** This is a module-level singleton. Calling `register()` on it\r\n * in tests mutates shared state that persists across test cases. Use\r\n * `defaultRegistry.clone()` to obtain an isolated copy for test scenarios\r\n * that need to register additional targets.\r\n */\r\nexport const defaultRegistry = new TargetRegistry();\r\n\r\ndefaultRegistry.register({\r\n name: TARGET_VSCODE,\r\n outputDirKey: 'vscode',\r\n filenameContextKey: 'vs_file_name',\r\n defaultFrontmatter: DEFAULT_FRONTMATTER_VSCODE,\r\n contextFlags: { target_vscode: true },\r\n defaultEnabled: true,\r\n});\r\n\r\ndefaultRegistry.register({\r\n name: TARGET_CLAUDE_CODE,\r\n outputDirKey: 'claude-code',\r\n filenameContextKey: 'cc_file_name',\r\n defaultFrontmatter: DEFAULT_FRONTMATTER_CLAUDE_CODE,\r\n contextFlags: { target_claude_code: true },\r\n defaultEnabled: true,\r\n});\r\n\r\ndefaultRegistry.register({\r\n name: TARGET_DEEP_AGENTS,\r\n outputDirKey: 'deep-agents',\r\n filenameContextKey: 'da_file_name',\r\n defaultFrontmatter: DEFAULT_FRONTMATTER_DEEP_AGENTS,\r\n contextFlags: { target_deep_agents: true },\r\n defaultEnabled: false,\r\n});\r\n","/**\r\n * src/builders/persona-builder.ts\r\n *\r\n * Core build orchestrator for @mistralys/persona-builder.\r\n *\r\n * Exports three public functions:\r\n *\r\n * 1. buildPersona(personaYamlPath, suiteName, suiteConfig, sharedMeta,\r\n * partialsMap, config, plugins, target, agentMap?)\r\n * — Builds a single persona for a single target. Returns a BuildResult.\r\n *\r\n * 2. buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap?)\r\n * — Discovers all persona YAMLs for a suite, fires onSuiteInit, maps\r\n * buildPersona() over each, and returns BuildResult[].\r\n *\r\n * 3. build(config)\r\n * — Top-level entry point. Pre-scans all suites to build a cross-suite\r\n * agent name map, then iterates all suites × targets, calls\r\n * buildSuite() for each combination, and returns a BuildSummary.\r\n * Respects --check (no writes) and --strict (fail on warnings/errors).\r\n *\r\n * Template variable injection (7-layer merge order, later layers win):\r\n *\r\n * 1. BuildConfig.variables — global defaults; available to every suite\r\n * 2. SuiteConfig.variables — suite-level overrides\r\n * 3. _shared.yaml fields — shared metadata for the suite\r\n * 4. Per-persona YAML fields — per-persona metadata\r\n * 5. Derived fields — version fallback, tools serialisation, etc.\r\n * 6. Cross-suite agent map — agent_<slug> / agent_slug_<slug> entries\r\n * 7. Target flags — target_<name> booleans; always highest precedence\r\n *\r\n * Callers inject global or suite-scoped variables via `BuildConfig.variables`\r\n * and `SuiteConfig.variables` respectively. Both fields are forwarded by\r\n * buildPersona() to the internal buildContext() function as the two\r\n * lowest-priority layers in the merge chain.\r\n */\r\n\r\nimport { readdir, readFile, mkdir, writeFile } from 'node:fs/promises';\r\nimport { existsSync } from 'node:fs';\r\nimport path from 'node:path';\r\nimport yaml from 'js-yaml';\r\n\r\nimport { resolvePartials } from '../engine/partials.js';\r\nimport { resolveConditionals } from '../engine/conditionals.js';\r\nimport { resolveVariables } from '../engine/variables.js';\r\nimport {\r\n collapseBlankLines,\r\n ensureBlankLineBeforeHeadings,\r\n normalizeNewlines,\r\n} from '../engine/postProcessor.js';\r\nimport { serializeTools, serializeToolsList } from '../engine/serializer.js';\r\nimport { loadPartials } from '../loaders/partials-loader.js';\r\nimport {\r\n runSuiteInit,\r\n runPartials,\r\n runBuildContext,\r\n runPersonaPartials,\r\n runPostRender,\r\n runValidate,\r\n} from '../plugins/runner.js';\r\n\r\nimport { resolveFrontmatterTemplate, renderFrontmatter } from './frontmatter.js';\r\nimport type { BuildConfig, BuildResult, BuildSummary } from './types.js';\r\nimport type { PersonaBuildPlugin, PersonaMetadata, SuiteConfig, TargetType, ValidationResult } from '../plugins/types.js';\r\nimport { defaultRegistry } from '../targets/built-in.js';\r\nimport type { TargetDefinition } from '../targets/types.js';\r\nimport type { TargetRegistry } from '../targets/registry.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// Internal helpers\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Discover all persona YAML files in the `meta/` subdirectory of a suite.\r\n *\r\n * Excludes files whose names start with `_` (shared metadata files such as\r\n * `_shared.yaml`). Results are sorted lexicographically.\r\n *\r\n * @param suiteConfig Suite configuration (used to locate `metaSubdir`)\r\n * @returns Absolute paths to each persona YAML file, sorted.\r\n */\r\nasync function discoverSuitePersonaYamls(suiteConfig: SuiteConfig): Promise<string[]> {\r\n const metaSubdir = suiteConfig.metaSubdir ?? 'meta';\r\n const metaDir = path.join(suiteConfig.srcDir, metaSubdir);\r\n\r\n const entries = await readdir(metaDir, { withFileTypes: true });\r\n\r\n return entries\r\n .filter((e) => e.isFile() && e.name.endsWith('.yaml') && !e.name.startsWith('_'))\r\n .map((e) => path.join(metaDir, e.name))\r\n .sort();\r\n}\r\n\r\n/**\r\n * Load and parse a raw YAML file into a plain object.\r\n * Used for `_shared.yaml` which does not conform to PersonaMetadata's\r\n * `name` requirement.\r\n *\r\n * @param filePath Absolute path to the YAML file\r\n * @returns Parsed object, or {} when the file is empty/absent\r\n */\r\nasync function loadRawYaml(filePath: string): Promise<Record<string, unknown>> {\r\n if (!existsSync(filePath)) return {};\r\n const raw = await readFile(filePath, 'utf8');\r\n const parsed: unknown = yaml.load(raw);\r\n if (parsed === null || parsed === undefined) return {};\r\n if (typeof parsed !== 'object' || Array.isArray(parsed)) return {};\r\n return parsed as Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Load a persona YAML file and return it as a plain metadata record.\r\n * The `name` field is derived from the filename stem when absent.\r\n *\r\n * @param yamlPath Absolute path to the persona YAML file\r\n * @returns Merged metadata record ready for context building\r\n */\r\nasync function loadPersonaYaml(yamlPath: string): Promise<Record<string, unknown>> {\r\n const raw = await readFile(yamlPath, 'utf8');\r\n const parsed: unknown = yaml.load(raw);\r\n\r\n if (parsed === null || parsed === undefined || typeof parsed !== 'object' || Array.isArray(parsed)) {\r\n throw new Error(`buildPersona: expected a YAML object in \"${yamlPath}\"`);\r\n }\r\n\r\n const record = parsed as Record<string, unknown>;\r\n\r\n // Derive name from filename stem if not present in YAML\r\n if (!record['name']) {\r\n record['name'] = path.basename(yamlPath, '.yaml');\r\n }\r\n\r\n return record;\r\n}\r\n\r\n/**\r\n * Resolve the output directory for a given target from a suite configuration.\r\n *\r\n * Resolution order (first match wins):\r\n * 1. `suiteConfig.outputDirs[definition.outputDirKey]` — generic map, takes precedence.\r\n * 2. `suiteConfig.outVscode` — deprecated fallback for the built-in 'vscode' key.\r\n * 3. `suiteConfig.outClaudeCode` — deprecated fallback for the built-in 'claude-code' key.\r\n *\r\n * The lookup key is `definition.outputDirKey` when a registry definition is\r\n * provided, falling back to the raw `target` name for unregistered targets\r\n * (where `outputDirKey` equals the name by convention).\r\n *\r\n * @param target The build target name.\r\n * @param suiteConfig The suite configuration to resolve from.\r\n * @param definition Optional registry definition for the target. When present,\r\n * `definition.outputDirKey` is used as the output dir map key.\r\n * @returns Resolved output directory path.\r\n * @throws {Error} When no output directory is configured for the target.\r\n */\r\nfunction resolveOutputDir(\r\n target: string,\r\n suiteConfig: SuiteConfig,\r\n definition?: TargetDefinition,\r\n): string {\r\n // Build a merged map: deprecated named fields provide the base, outputDirs overrides.\r\n const merged: Record<string, string> = {};\r\n if (suiteConfig.outVscode) merged['vscode'] = suiteConfig.outVscode;\r\n if (suiteConfig.outClaudeCode) merged['claude-code'] = suiteConfig.outClaudeCode;\r\n if (suiteConfig.outputDirs) Object.assign(merged, suiteConfig.outputDirs);\r\n\r\n // Use outputDirKey from the registry definition when available, falling back\r\n // to the target name for unregistered targets where outputDirKey === name.\r\n const lookupKey = definition?.outputDirKey ?? target;\r\n const dir = merged[lookupKey];\r\n if (dir) return dir;\r\n\r\n throw new Error(\r\n `buildPersona: no output directory configured for target \"${target}\". ` +\r\n `Add outputDirs['${lookupKey}'] to the suite config, or, for the built-in ` +\r\n `targets, provide the outVscode / outClaudeCode fields.`,\r\n );\r\n}\r\n\r\n/**\r\n * Pre-scan all suites and build a cross-suite agent name map.\r\n *\r\n * For each persona across all configured suites, creates a context variable:\r\n * key: `agent_` + slug (hyphens → underscores)\r\n * value: `\"<name> v<version>\"`\r\n *\r\n * Slug is taken from the persona YAML's `slug` field, falling back to the\r\n * filename stem. Version falls back to the suite's `default_version`, then\r\n * to `'0.0.0'`.\r\n *\r\n * @param config Top-level BuildConfig with all suite definitions\r\n * @returns Map of agent variable keys to display strings\r\n */\r\nasync function buildAgentNameMap(\r\n config: BuildConfig,\r\n): Promise<Record<string, string>> {\r\n const agentMap: Record<string, string> = {};\r\n\r\n for (const [, suiteConfig] of Object.entries(config.suites)) {\r\n const metaSubdir = suiteConfig.metaSubdir ?? 'meta';\r\n const sharedYamlPath = path.join(suiteConfig.srcDir, metaSubdir, '_shared.yaml');\r\n const sharedMeta = await loadRawYaml(sharedYamlPath);\r\n const defaultVersion =\r\n typeof sharedMeta['default_version'] === 'string'\r\n ? sharedMeta['default_version']\r\n : '0.0.0';\r\n\r\n const personaYamls = await discoverSuitePersonaYamls(suiteConfig);\r\n\r\n for (const yamlPath of personaYamls) {\r\n const persona = await loadPersonaYaml(yamlPath);\r\n\r\n const slug =\r\n typeof persona['slug'] === 'string'\r\n ? persona['slug']\r\n : path.basename(yamlPath, '.yaml');\r\n\r\n const name =\r\n typeof persona['name'] === 'string'\r\n ? persona['name']\r\n : slug;\r\n\r\n const version =\r\n typeof persona['version'] === 'string'\r\n ? persona['version']\r\n : defaultVersion;\r\n\r\n const underscoredSlug = slug.replace(/-/g, '_');\r\n const key = `agent_${underscoredSlug}`;\r\n agentMap[key] = `${name} v${version}`;\r\n\r\n const slugKey = `agent_slug_${underscoredSlug}`;\r\n agentMap[slugKey] = slug;\r\n }\r\n }\r\n\r\n return agentMap;\r\n}\r\n\r\n/**\r\n * Build the merged template context for a single persona.\r\n *\r\n * Merge order (later values win):\r\n * 1. configVariables (lowest priority — global BuildConfig.variables)\r\n * 2. suiteVariables (suite-level SuiteConfig.variables)\r\n * 3. sharedMeta (parsed _shared.yaml fields)\r\n * 4. personaMeta (per-persona YAML fields)\r\n * 5. derived/computed fields (version fallback, tools serialisation, etc.)\r\n * 6. agentMap entries (only for keys not already present)\r\n * 7. target flags (injected last; always win)\r\n *\r\n * @param options.personaMeta Per-persona YAML as a plain record\r\n * @param options.sharedMeta Parsed `_shared.yaml` fields\r\n * @param options.agentMap Cross-suite agent name map (injected by build())\r\n * @param options.target The current build target (forwarded to plugins)\r\n * @param options.registry Target registry for context-flag injection\r\n * @param options.configVariables Optional global variables from BuildConfig.variables\r\n * (lowest precedence; overridden by all other layers)\r\n * @param options.suiteVariables Optional suite-level variables from SuiteConfig.variables\r\n * (overrides configVariables; overridden by sharedMeta and personaMeta)\r\n * @returns Merged rendering context\r\n */\r\ninterface BuildContextOptions {\r\n personaMeta: Record<string, unknown>;\r\n sharedMeta: Record<string, unknown>;\r\n agentMap?: Record<string, string>;\r\n target?: TargetType;\r\n registry?: TargetRegistry;\r\n configVariables?: Record<string, unknown>;\r\n suiteVariables?: Record<string, unknown>;\r\n}\r\n\r\nfunction buildContext(options: BuildContextOptions): Record<string, unknown> {\r\n const {\r\n personaMeta,\r\n sharedMeta,\r\n agentMap = {},\r\n target,\r\n registry,\r\n configVariables,\r\n suiteVariables,\r\n } = options;\r\n const version =\r\n typeof personaMeta['version'] === 'string'\r\n ? personaMeta['version']\r\n : typeof sharedMeta['default_version'] === 'string'\r\n ? sharedMeta['default_version']\r\n : '0.0.0';\r\n\r\n // Merge base: configVariables first (lowest priority), then suiteVariables,\r\n // then sharedMeta, then personaMeta (highest priority among YAML sources).\r\n const merged: Record<string, unknown> = {\r\n ...(configVariables ?? {}),\r\n ...(suiteVariables ?? {}),\r\n ...sharedMeta,\r\n ...personaMeta,\r\n version,\r\n };\r\n\r\n // ── Derived convenience fields (only set when not already provided) ───────\r\n // tools_list / tools_json — serialized from the `tools` array if present\r\n const tools = Array.isArray(merged['tools']) ? (merged['tools'] as string[]) : [];\r\n if (!('tools_list' in merged)) {\r\n merged['tools_list'] = serializeToolsList(tools);\r\n }\r\n if (!('tools_json' in merged)) {\r\n merged['tools_json'] = serializeTools(tools);\r\n }\r\n\r\n // cc_tools_list / cc_tools_json — from `cc_tools` or fall back to `tools`\r\n const ccTools = Array.isArray(merged['cc_tools']) ? (merged['cc_tools'] as string[]) : tools;\r\n if (!('cc_tools_list' in merged)) {\r\n merged['cc_tools_list'] = serializeToolsList(ccTools);\r\n }\r\n if (!('cc_tools_json' in merged)) {\r\n merged['cc_tools_json'] = serializeTools(ccTools);\r\n }\r\n\r\n // cc_file_name_stem — stem of cc_file_name (for default CC frontmatter template)\r\n if (!('cc_file_name_stem' in merged) && typeof merged['cc_file_name'] === 'string') {\r\n const ccFileName = merged['cc_file_name'] as string;\r\n merged['cc_file_name_stem'] = ccFileName.replace(/\\.md$/, '');\r\n }\r\n\r\n // da_file_name_stem — stem of da_file_name (for deep-agents output)\r\n if (!('da_file_name_stem' in merged) && typeof merged['da_file_name'] === 'string') {\r\n const daFileName = merged['da_file_name'] as string;\r\n merged['da_file_name_stem'] = daFileName.replace(/\\.md$/, '');\r\n }\r\n\r\n // da_tools_list / da_tools_json — from `da_tools` or fall back to `tools`\r\n // Intentionally gated on da_file_name: unlike cc_tools_list/cc_tools_json (always emitted for\r\n // every persona), da_* fields are absent when the persona has no deep-agents output file (AC-4).\r\n if (typeof merged['da_file_name'] === 'string') {\r\n const daTools = Array.isArray(merged['da_tools']) ? (merged['da_tools'] as string[]) : tools;\r\n if (!('da_tools_list' in merged)) {\r\n merged['da_tools_list'] = serializeToolsList(daTools);\r\n }\r\n if (!('da_tools_json' in merged)) {\r\n merged['da_tools_json'] = serializeTools(daTools);\r\n }\r\n }\r\n\r\n // ── Cross-suite agent name variables ──────────────────────────────────────\r\n for (const [key, value] of Object.entries(agentMap)) {\r\n if (!(key in merged)) {\r\n merged[key] = value;\r\n }\r\n }\r\n\r\n // ── Target flag injection ─────────────────────────────────────────────────\r\n if (target !== undefined) {\r\n if (registry && registry.has(target)) {\r\n // Inject all contextFlags declared in the target's registry definition.\r\n const flags = registry.get(target).contextFlags ?? {};\r\n for (const [key, value] of Object.entries(flags)) {\r\n merged[key] = value;\r\n }\r\n } else {\r\n // Fallback for targets not present in the registry.\r\n merged[`target_${target.replace(/-/g, '_')}`] = true;\r\n }\r\n }\r\n\r\n return merged;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// buildPersona — single persona × single target\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Build a single persona for a single output target.\r\n *\r\n * Pipeline:\r\n * 1. Load sharedMeta + personaMeta (callers supply pre-loaded values)\r\n * 2. Build merged context\r\n * 3. Run onBuildContext plugin hooks (context accumulation)\r\n * 4. Run onPersonaPartials plugin hooks (shallow-copy partials map, persona-scoped)\r\n * 5. Render frontmatter template → render frontmatter\r\n * 6. Load content template\r\n * 7. Render body: partials → conditionals → variables → post-process\r\n * 8. Assemble final output (frontmatter + body)\r\n * 9. Run onPostRender plugin hooks (output chain)\r\n * 10. Run onValidate plugin hooks (validation collection)\r\n * 11. Determine output file path\r\n * 12. Write output file (unless check mode)\r\n * 13. Return BuildResult\r\n *\r\n * @param personaYamlPath Absolute path to the persona YAML source file\r\n * @param suiteName Identifier for the suite this persona belongs to\r\n * @param suiteConfig Suite configuration object. `suiteConfig.variables`\r\n * is forwarded to `buildContext()` as the\r\n * `suiteVariables` layer (layer 2 of 7) — these values\r\n * override any `config.variables` but are themselves\r\n * overridden by `_shared.yaml` and per-persona fields.\r\n * @param sharedMeta Pre-loaded `_shared.yaml` contents\r\n * @param partialsMap Pre-loaded partials map (shared + suite-local merged).\r\n * This map is **not** passed directly to rendering.\r\n * Instead, a shallow copy (`{ ...partialsMap }`) is\r\n * created at step 3b and passed to the `onPersonaPartials`\r\n * plugin hooks — the hooks' accumulated output map is what\r\n * reaches `resolvePartials`, not `partialsMap` itself.\r\n * This ensures that persona-level overrides or injections\r\n * do not leak back into the caller's reference or into\r\n * subsequent personas in the same suite.\r\n * @param config Top-level BuildConfig. `config.variables` is\r\n * forwarded to `buildContext()` as the\r\n * `configVariables` layer (layer 1 of 7, lowest\r\n * priority) — global defaults available to every\r\n * persona across all suites.\r\n * @param plugins Registered plugins\r\n * @param target Target output format\r\n * @param agentMap Pre-built cross-suite agent name map\r\n * @param registry Target registry to use. Defaults to `defaultRegistry`.\r\n * **Two-registry limitation:** If you pass a custom `TargetRegistry` only\r\n * to `build()` (via `config.targetRegistry`) and call `buildPersona()`\r\n * directly without also passing that registry here, your custom targets\r\n * will not be visible — `defaultRegistry` will be used instead. Either\r\n * pass the same registry instance explicitly, or call `build()` to have\r\n * the registry forwarded automatically.\r\n * @returns BuildResult for this persona × target combination\r\n */\r\nexport async function buildPersona(\r\n personaYamlPath: string,\r\n suiteName: string,\r\n suiteConfig: SuiteConfig,\r\n sharedMeta: Record<string, unknown>,\r\n partialsMap: Record<string, string>,\r\n config: BuildConfig,\r\n plugins: PersonaBuildPlugin[],\r\n target: string,\r\n agentMap: Record<string, string> = {},\r\n registry: TargetRegistry = defaultRegistry,\r\n): Promise<BuildResult> {\r\n // ── 1. Load persona metadata ──────────────────────────────────────────────\r\n const personaMeta = await loadPersonaYaml(personaYamlPath);\r\n\r\n // ── 2. Build merged context ───────────────────────────────────────────────\r\n let context = buildContext({\r\n personaMeta,\r\n sharedMeta,\r\n agentMap,\r\n target,\r\n registry,\r\n configVariables: config.variables,\r\n suiteVariables: suiteConfig.variables,\r\n });\r\n\r\n // ── 3. Plugin onBuildContext ──────────────────────────────────────────────\r\n // Cast context to PersonaMetadata for the plugin runner (it requires a\r\n // name field which is guaranteed by loadPersonaYaml above).\r\n const personaMetaTyped = personaMeta as PersonaMetadata;\r\n context = runBuildContext(plugins, context, personaMetaTyped, suiteConfig, target);\r\n\r\n // ── 4. Plugin onPersonaPartials ──────────────────────────────────────────────\r\n // Create a shallow copy of the suite-level partials map so that any\r\n // persona-level overrides or injections do not leak to other personas.\r\n // The copy is then mutated (accumulated) by onPersonaPartials plugins and\r\n // used exclusively for this persona's rendering pass.\r\n const personaPartialsMap = runPersonaPartials(\r\n plugins,\r\n { ...partialsMap },\r\n personaMetaTyped,\r\n context,\r\n suiteConfig,\r\n target,\r\n );\r\n\r\n // ── 5. Render frontmatter ─────────────────────────────────────────────────\r\n const fmTemplate = resolveFrontmatterTemplate(target, plugins, config.frontmatter, registry);\r\n const contentBasename = path.basename(personaYamlPath, '.yaml') + '.md';\r\n const frontmatter = renderFrontmatter(fmTemplate, context, contentBasename);\r\n\r\n // ── 6. Load content template ──────────────────────────────────────────────\r\n const contentSubdir = suiteConfig.contentSubdir ?? 'content';\r\n const contentPath = path.join(suiteConfig.srcDir, contentSubdir, contentBasename);\r\n const bodyTemplate = normalizeNewlines(await readFile(contentPath, 'utf8'));\r\n\r\n // ── 7. Render body ────────────────────────────────────────────────────────\r\n let body = resolvePartials(bodyTemplate, personaPartialsMap);\r\n body = resolveConditionals(body, context);\r\n body = resolveVariables(body, context, contentBasename);\r\n body = collapseBlankLines(body);\r\n body = ensureBlankLineBeforeHeadings(body);\r\n body = body.trimEnd();\r\n\r\n // ── 8. Assemble output ────────────────────────────────────────────────────\r\n let output = normalizeNewlines(`${frontmatter}\\n\\n${body}\\n`);\r\n\r\n // ── 9. Plugin onPostRender ────────────────────────────────────────────────\r\n output = runPostRender(plugins, output, personaMetaTyped, target);\r\n\r\n // ── 10. Plugin onValidate ──────────────────────────────────────────────────\r\n const validationResults: ValidationResult[] = runValidate(plugins, personaMetaTyped, suiteConfig, target);\r\n\r\n // ── 11. Determine output file path ────────────────────────────────────────\r\n // Resolve the registry definition once — used for both outputDirKey (map\r\n // lookup) and filenameContextKey (output filename override).\r\n const def = registry.has(target) ? registry.get(target) : undefined;\r\n const outputDir = resolveOutputDir(target, suiteConfig, def);\r\n // Use the filename context key declared in the target's registry definition,\r\n // falling back to the content basename when absent or unset in context.\r\n const fnKey = def?.filenameContextKey;\r\n const outputBasename =\r\n fnKey && typeof context[fnKey] === 'string'\r\n ? (context[fnKey] as string)\r\n : contentBasename;\r\n const outputPath = path.join(outputDir, outputBasename);\r\n\r\n // ── 12. Write (unless check mode) ─────────────────────────────────────────\r\n const check = config.check ?? false;\r\n let written = false;\r\n\r\n if (!check) {\r\n await mkdir(outputDir, { recursive: true });\r\n await writeFile(outputPath, output, 'utf8');\r\n written = true;\r\n }\r\n\r\n return {\r\n suite: suiteName,\r\n target,\r\n personaYamlPath,\r\n outputPath,\r\n content: output,\r\n validationResults,\r\n written,\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// buildSuite — all personas in one suite × one target\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Build all personas in a suite for a single output target.\r\n *\r\n * Pipeline:\r\n * 1. Load `_shared.yaml` for the suite\r\n * 2. Load merged partials (config.partials → shared → suite-local)\r\n * 3. Run `onSuiteInit` on all plugins\r\n * 4. Run `onPartials` on all plugins (highest priority: may override any file-based partial)\r\n * 5. Discover all persona YAML files\r\n * 6. Call `buildPersona()` for each\r\n *\r\n * @param suiteName Identifier for this suite\r\n * @param suiteConfig Suite configuration\r\n * @param config Top-level BuildConfig\r\n * @param plugins Registered plugins\r\n * @param target Target output format\r\n * @param agentMap Pre-built cross-suite agent name map\r\n * @param registry Target registry to use. Defaults to `defaultRegistry`.\r\n * **Two-registry limitation:** If you pass a custom `TargetRegistry` only\r\n * to `build()` (via `config.targetRegistry`) and call `buildSuite()`\r\n * directly without also passing that registry here, your custom targets\r\n * will not be visible — `defaultRegistry` will be used instead. Either\r\n * pass the same registry instance explicitly, or call `build()` to have\r\n * the registry forwarded automatically.\r\n * @returns Array of BuildResult objects, one per persona\r\n */\r\nexport async function buildSuite(\r\n suiteName: string,\r\n suiteConfig: SuiteConfig,\r\n config: BuildConfig,\r\n plugins: PersonaBuildPlugin[],\r\n target: string,\r\n agentMap: Record<string, string> = {},\r\n registry: TargetRegistry = defaultRegistry,\r\n): Promise<BuildResult[]> {\r\n // ── 1. Load shared metadata ───────────────────────────────────────────────\r\n const metaSubdir = suiteConfig.metaSubdir ?? 'meta';\r\n const sharedYamlPath = path.join(suiteConfig.srcDir, metaSubdir, '_shared.yaml');\r\n const sharedMeta = await loadRawYaml(sharedYamlPath);\r\n\r\n // ── 2. Load partials (three-layer: config.partials → shared → suite-local) ─\r\n // Start with config.partials as the lowest-priority base layer.\r\n let partialsMap: Record<string, string> = { ...(config.partials ?? {}) };\r\n\r\n if (config.sharedPartialsDir && existsSync(config.sharedPartialsDir)) {\r\n partialsMap = { ...partialsMap, ...(await loadPartials(config.sharedPartialsDir)) };\r\n }\r\n\r\n const partialsSubdir = suiteConfig.partialsSubdir ?? 'partials';\r\n const suitePartialsDir = path.join(suiteConfig.srcDir, partialsSubdir);\r\n if (existsSync(suitePartialsDir)) {\r\n partialsMap = { ...partialsMap, ...(await loadPartials(suitePartialsDir)) };\r\n }\r\n\r\n // ── 3. Plugin onSuiteInit ─────────────────────────────────────────────────\r\n runSuiteInit(plugins, suiteConfig, sharedMeta);\r\n\r\n // ── 4. Plugin onPartials ────────────────────────────────────────────────────\r\n // Invoked after onSuiteInit and after all file-based partials are loaded.\r\n // Plugins may inject new partials or override any file-based entry.\r\n partialsMap = runPartials(plugins, partialsMap, suiteName, suiteConfig);\r\n\r\n // ── 5. Discover persona YAML files ───────────────────────────────────────\r\n const personaYamlPaths = await discoverSuitePersonaYamls(suiteConfig);\r\n\r\n // ── 6. Build each persona ────────────────────────────────────────────────────\r\n const results: BuildResult[] = [];\r\n for (const yamlPath of personaYamlPaths) {\r\n const result = await buildPersona(\r\n yamlPath,\r\n suiteName,\r\n suiteConfig,\r\n sharedMeta,\r\n partialsMap,\r\n config,\r\n plugins,\r\n target,\r\n agentMap,\r\n registry,\r\n );\r\n results.push(result);\r\n }\r\n\r\n return results;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// build — top-level entry point\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Top-level build orchestrator.\r\n *\r\n * Iterates all `config.suites × config.targets` combinations, calls\r\n * `buildSuite()` for each, and aggregates the results into a `BuildSummary`.\r\n *\r\n * Modes:\r\n * - Normal: renders and writes all personas.\r\n * - `check: true`: renders without writing; useful for CI staleness checks.\r\n * - `strict: true`: throws when any ValidationResult has severity `'error'`\r\n * or `'warning'`. All suites are processed before the throw, so output\r\n * files **will** be written to disk even when the build ultimately fails.\r\n * **For CI usage, combine `strict: true` with `check: true`** to avoid\r\n * leaving partial artefacts on disk when validation fails.\r\n *\r\n * @param config Typed build configuration\r\n * @returns Aggregated BuildSummary\r\n * @throws `Error` when `strict: true` and validation failures exist\r\n */\r\nexport async function build(config: BuildConfig): Promise<BuildSummary> {\r\n const plugins = config.plugins ?? [];\r\n const registry = config.targetRegistry ?? defaultRegistry;\r\n // When a custom registry is supplied and targets are not explicit, build all\r\n // registered targets. When using the default registry without an explicit\r\n // targets list, build only targets with defaultEnabled !== false. This\r\n // preserves the historical two-target default (vscode + claude-code) for\r\n // existing suite configs that do not configure deep-agents output.\r\n const targets = config.targets ?? registry.names().filter(n => registry.get(n).defaultEnabled !== false);\r\n const allResults: BuildResult[] = [];\r\n\r\n // Pre-scan: build cross-suite agent name map\r\n const agentMap = await buildAgentNameMap(config);\r\n\r\n for (const [suiteName, suiteConfig] of Object.entries(config.suites)) {\r\n for (const target of targets) {\r\n const suiteResults = await buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap, registry);\r\n allResults.push(...suiteResults);\r\n }\r\n }\r\n\r\n // Collect strict failures (error + warning severity)\r\n const strictFailures: ValidationResult[] = config.strict\r\n ? allResults.flatMap((r) =>\r\n r.validationResults.filter(\r\n (v) => v.severity === 'error' || v.severity === 'warning',\r\n ),\r\n )\r\n : [];\r\n\r\n const success = !config.strict || strictFailures.length === 0;\r\n\r\n const summary: BuildSummary = {\r\n success,\r\n results: allResults,\r\n strictFailures,\r\n totalBuilt: allResults.length,\r\n totalWritten: allResults.filter((r) => r.written).length,\r\n };\r\n\r\n if (config.strict && !success) {\r\n const messages = strictFailures.map((f) => `[${f.severity}] ${f.message}`).join('\\n');\r\n throw new Error(\r\n `Build failed in strict mode — ${strictFailures.length} validation issue(s):\\n${messages}`,\r\n );\r\n }\r\n\r\n return summary;\r\n}\r\n","/**\r\n * src/validators/filename-validator.ts\r\n *\r\n * Validates persona output filenames against the project naming convention.\r\n *\r\n * Convention: kebab-case only — lowercase letters, digits, and hyphens.\r\n * No spaces, no uppercase letters, no special characters other than hyphens\r\n * and dots (for the file extension).\r\n *\r\n * This is a pure function: no file I/O, no process.exit, no side effects.\r\n * It depends only on `ValidationResult` from `src/plugins/types.ts`.\r\n */\r\n\r\nimport type { ValidationResult } from '../plugins/types.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// Validation rule definitions\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface FilenameRule {\r\n /** Human-readable description of the rule (used in error messages) */\r\n description: string;\r\n /** Returns true when the filename is *invalid* (i.e. the rule is violated) */\r\n violated: (basename: string) => boolean;\r\n /** Message factory — receives the offending basename */\r\n message: (basename: string) => string;\r\n}\r\n\r\nconst FILENAME_RULES: FilenameRule[] = [\r\n {\r\n description: 'no uppercase letters',\r\n violated: (name) => /[A-Z]/.test(name),\r\n message: (name) =>\r\n `Filename \"${name}\" contains uppercase letters. Use lowercase kebab-case (e.g. \"my-persona.md\").`,\r\n },\r\n {\r\n description: 'no spaces',\r\n violated: (name) => /\\s/.test(name),\r\n message: (name) =>\r\n `Filename \"${name}\" contains spaces. Use hyphens to separate words (e.g. \"my-persona.md\").`,\r\n },\r\n {\r\n description: 'kebab-case characters only',\r\n violated: (name) => {\r\n // A valid filename consists of one or more dot-separated segments.\r\n // Each segment must be a non-empty kebab-case token:\r\n // - starts and ends with a lowercase letter or digit\r\n // - may contain hyphens, but not consecutive hyphens\r\n // Examples of valid names: \"my-persona.md\", \"1-developer.agent.md\"\r\n // Examples of invalid names: \"My_Persona.md\", \"--bad.md\", \"foo..bar.md\"\r\n const segments = name.split('.');\r\n if (segments.length === 1) {\r\n // No extension — treat the whole name as a kebab stem\r\n return !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name);\r\n }\r\n // All segments (stem + extension parts) must be valid kebab tokens\r\n return !segments.every((seg) => /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(seg));\r\n },\r\n message: (name) =>\r\n `Filename \"${name}\" does not conform to kebab-case naming. ` +\r\n `Use lowercase letters, digits, and hyphens only (e.g. \"my-persona.md\").`,\r\n },\r\n];\r\n\r\n// ---------------------------------------------------------------------------\r\n// Public API\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Validate a persona filename against the project naming convention.\r\n *\r\n * Accepts either a bare filename (`my-persona.md`) or a full/relative path\r\n * — only the basename (last path segment) is evaluated.\r\n *\r\n * @param filePath Filename or path to validate (only the basename is checked)\r\n * @returns Empty array when the filename conforms; one ValidationResult\r\n * per violated rule otherwise. Each result has severity \"error\".\r\n *\r\n * @example\r\n * validateFileName('my-persona.md'); // []\r\n * validateFileName('My Persona.md'); // [{severity:'error', message:'...'}]\r\n * validateFileName('/abs/path/my-persona.md');// []\r\n */\r\nexport function validateFileName(filePath: string): ValidationResult[] {\r\n const basename = filePath.includes('/')\r\n ? filePath.split('/').pop() ?? filePath\r\n : filePath.includes('\\\\')\r\n ? filePath.split('\\\\').pop() ?? filePath\r\n : filePath;\r\n\r\n const errors: ValidationResult[] = [];\r\n\r\n for (const rule of FILENAME_RULES) {\r\n if (rule.violated(basename)) {\r\n errors.push({\r\n severity: 'error',\r\n message: rule.message(basename),\r\n });\r\n }\r\n }\r\n\r\n return errors;\r\n}\r\n","/**\r\n * src/validators/strict-validator.ts\r\n *\r\n * Validates that a set of required marker strings are present in a rendered\r\n * persona output string.\r\n *\r\n * \"Strict\" mode in the build pipeline guards against incomplete renders —\r\n * e.g. a required section marker (e.g. \"{{ROLE}}\") that was never resolved.\r\n * This validator generalises that concept: callers supply the list of marker\r\n * strings that *must* appear in the final rendered content.\r\n *\r\n * This is a pure function: no file I/O, no side effects.\r\n * It depends only on `ValidationResult` from `src/plugins/types.ts`.\r\n */\r\n\r\nimport type { ValidationResult } from '../plugins/types.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// Public API\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Validate that every required marker string is present in the rendered output.\r\n *\r\n * Each absent marker produces one `ValidationResult` entry with severity\r\n * `\"error\"` and a descriptive message identifying the missing marker.\r\n *\r\n * @param renderedContent The final rendered output string to inspect\r\n * @param requiredMarkers Array of marker strings that must appear verbatim in\r\n * `renderedContent`. An empty array always returns `[]`.\r\n * @returns Empty array when all markers are found; one entry per\r\n * absent marker otherwise. Each entry has severity \"error\".\r\n *\r\n * @example\r\n * validateStrictMarkers('Hello world', ['Hello', 'world']); // []\r\n * validateStrictMarkers('Hello world', ['{{MISSING}}']);\r\n * // [{severity:'error', message:'Required marker \"{{MISSING}}\" is missing from the rendered output.'}]\r\n */\r\nexport function validateStrictMarkers(\r\n renderedContent: string,\r\n requiredMarkers: string[],\r\n): ValidationResult[] {\r\n const errors: ValidationResult[] = [];\r\n\r\n for (const marker of requiredMarkers) {\r\n if (!renderedContent.includes(marker)) {\r\n errors.push({\r\n severity: 'error',\r\n message: `Required marker \"${marker}\" is missing from the rendered output.`,\r\n });\r\n }\r\n }\r\n\r\n return errors;\r\n}\r\n","/**\r\n * src/utils/regex.ts\r\n *\r\n * Shared regex utilities.\r\n *\r\n * Pure functions — no I/O, no side effects.\r\n */\r\n\r\n/**\r\n * Escape a string for safe use inside a `new RegExp(...)` constructor.\r\n * Escapes all regex special characters.\r\n *\r\n * @param str Raw string to escape\r\n * @returns String with all special regex characters escaped\r\n *\r\n * @example\r\n * escapeRegExp('tool.name+extra')\r\n * // => 'tool\\\\.name\\\\+extra'\r\n *\r\n * new RegExp(`\\\\|\\\\s*\\`${escapeRegExp(toolName)}\\`\\\\s*\\\\|`)\r\n * // Safe regex that matches | `<toolName>` | in rendered Markdown tables\r\n */\r\nexport function escapeRegExp(str: string): string {\r\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n}\r\n","/**\r\n * @mistralys/persona-builder\r\n *\r\n * Public API barrel export.\r\n */\r\n\r\nimport { createRequire } from 'node:module';\r\n\r\nexport * from './engine/index.js';\r\nexport * from './loaders/index.js';\r\nexport * from './plugins/index.js';\r\nexport * from './builders/index.js';\r\nexport * from './validators/index.js';\r\nexport * from './utils/index.js';\r\nexport * from './targets/index.js';\r\n\r\n/** Package version — sourced from package.json (single source of truth). */\r\nconst _pkgRequire = createRequire(import.meta.url);\r\nexport const VERSION = (_pkgRequire('../package.json') as { version: string }).version;\r\n"]}
1
+ {"version":3,"sources":["../src/engine/partials.ts","../src/engine/conditionals.ts","../src/engine/variables.ts","../src/engine/postProcessor.ts","../src/engine/serializer.ts","../src/loaders/partials-loader.ts","../src/loaders/metadata-loader.ts","../src/loaders/content-loader.ts","../src/plugins/runner.ts","../src/targets/types.ts","../src/builders/frontmatter.ts","../src/targets/registry.ts","../src/targets/built-in.ts","../src/builders/persona-builder.ts","../src/validators/filename-validator.ts","../src/validators/strict-validator.ts","../src/utils/regex.ts","../src/index.ts"],"names":["readdir","path","readFile","yaml","existsSync","mkdir","writeFile","createRequire"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * partials.ts\r\n *\r\n * Pure template-engine function for resolving partial inclusions.\r\n * Supports {{> name}} syntax with up to depth-2 recursion to handle\r\n * partials-within-partials. No file-system I/O.\r\n */\r\n\r\n/**\r\n * Resolve partial inclusions in a template string.\r\n *\r\n * Replaces `{{> name}}` markers with the content from `partialsMap`.\r\n * Recursion is capped at depth 2 so that:\r\n * - depth 0 → 1: outer partials are expanded\r\n * - depth 1 → 2: one level of nested partials are expanded\r\n * - depth 2: recursion stops, marker is left as-is\r\n *\r\n * Each resolved partial is `trimEnd()`-ed to prevent trailing blank lines\r\n * from causing double-blank-line artefacts during concatenation.\r\n *\r\n * If a partial name is not found in `partialsMap`, the original marker is\r\n * preserved and a warning is emitted via `console.warn`.\r\n *\r\n * @param text - Template string potentially containing {{> name}} markers\r\n * @param partialsMap - Map of partial name → partial content\r\n * @param depth - **Internal recursion counter — callers must always omit this\r\n * parameter.** It exists solely so the function can track its own\r\n * nesting level across recursive calls. Passing a non-zero value\r\n * bypasses the depth guard (e.g. `resolvePartials(text, map, 5)`\r\n * expands nothing). This parameter will be removed from the public\r\n * signature and marked `@internal` in a future release.\r\n * @returns The template string with partial markers replaced\r\n */\r\nexport function resolvePartials(\r\n text: string,\r\n partialsMap: Record<string, string>,\r\n depth = 0,\r\n): string {\r\n if (depth >= 2) return text;\r\n return text.replace(/\\{\\{> ([\\w-]+)\\}\\}/g, (match, name: string) => {\r\n if (!(name in partialsMap)) {\r\n console.warn(`[WARN] Partial not found: ${match}`);\r\n return match;\r\n }\r\n // Recursively resolve nested partials (depth + 1).\r\n // trimEnd() strips trailing whitespace to avoid extra blank lines.\r\n return resolvePartials(partialsMap[name], partialsMap, depth + 1).trimEnd();\r\n });\r\n}\r\n","/**\r\n * conditionals.ts\r\n *\r\n * Pure template-engine function for resolving conditional blocks.\r\n * Handles {{#if flag}}…{{/if}}, {{#if flag}}…{{else}}…{{/if}}, and\r\n * {{#if flag}}…{{else if flag2}}…{{else}}…{{/if}} (chain) syntax,\r\n * including nested {{#if}} blocks inside {{else}} branches.\r\n * No file-system I/O.\r\n */\r\n\r\n/**\r\n * Negative-lookahead fragment that matches any character sequence containing\r\n * no `{{#if` opener. Used as a shared building-block in the regex patterns of\r\n * both `resolveElseIf` and `resolveConditionals` so the two guards stay in sync.\r\n * @internal\r\n */\r\nconst NO_NESTED_IF = String.raw`(?:(?!\\{\\{#if\\b)[\\s\\S])*?`;\r\n\r\n/**\r\n * Matches `{{else if flag}}…{{/if}}` segments whose content contains no\r\n * nested `{{#if` opener. Hoisted to module level to avoid constructing a new\r\n * `RegExp` object on every `resolveElseIf()` call.\r\n * `String.prototype.replace()` resets `lastIndex` to 0 before each call, so\r\n * the shared `g`-flag instance is safe for repeated use.\r\n * @internal\r\n */\r\nconst ELSE_IF_PATTERN = new RegExp(\r\n String.raw`\\{\\{else if (\\w+)\\}\\}(${NO_NESTED_IF})\\{\\{\\/if\\}\\}`,\r\n 'g',\r\n);\r\n\r\n/**\r\n * Pre-process `{{else if flag}}` chains by rewriting each innermost\r\n * occurrence into an equivalent nested `{{else}}{{#if flag}}` block.\r\n *\r\n * Examples:\r\n * `{{#if a}}A{{else if b}}B{{else}}C{{/if}}`\r\n * → `{{#if a}}A{{else}}{{#if b}}B{{else}}C{{/if}}{{/if}}`\r\n *\r\n * Multi-level chains are normalised iteratively, one level per pass:\r\n * `{{#if a}}A{{else if b}}B{{else if c}}C{{/if}}`\r\n * pass 1 → `{{#if a}}A{{else}}{{#if b}}B{{else if c}}C{{/if}}{{/if}}`\r\n * pass 2 → `{{#if a}}A{{else}}{{#if b}}B{{else}}{{#if c}}C{{/if}}{{/if}}{{/if}}`\r\n *\r\n * The pattern matches only segments whose content contains no nested\r\n * `{{#if` markers, mirroring the innermost-first invariant of\r\n * `resolveConditionals`. This ensures `{{else if}}` chains that are\r\n * themselves nested inside outer `{{#if}}…{{else}}…{{/if}}` blocks are\r\n * handled safely.\r\n *\r\n * @internal\r\n */\r\nfunction resolveElseIf(text: string): string {\r\n if (!text.includes('{{else if ')) {\r\n return text;\r\n }\r\n // Use the module-level ELSE_IF_PATTERN constant. replace() resets the\r\n // regex's lastIndex before each call, so sharing the instance is safe.\r\n let result = text;\r\n let prev: string;\r\n do {\r\n prev = result;\r\n result = result.replace(\r\n ELSE_IF_PATTERN,\r\n (_match: string, flag: string, content: string): string =>\r\n `{{else}}{{#if ${flag}}}${content}{{/if}}{{/if}}`,\r\n );\r\n } while (result !== prev);\r\n return result;\r\n}\r\n\r\n/**\r\n * Resolve conditional blocks in a template string.\r\n *\r\n * Syntax:\r\n * `{{#if flag}}content{{/if}}`\r\n * `{{#if flag}}truthy-content{{else}}falsy-content{{/if}}`\r\n * `{{#if flag}}truthy-content{{else if flag2}}branch2{{else}}falsy-content{{/if}}`\r\n *\r\n * Nested conditionals inside `{{else}}` branches are supported:\r\n * `{{#if a}}A{{else}}{{#if b}}B{{else}}C{{/if}}{{/if}}`\r\n *\r\n * `{{else if}}` chains are normalised into nested `{{#if}}` blocks before\r\n * resolution, so they work transparently alongside — and can be combined\r\n * with — traditional nested `{{#if}}` syntax.\r\n *\r\n * Behaviour:\r\n * - When `context[flag]` is truthy: the delimiters are stripped and the\r\n * content before `{{else}}` (or the entire inner block if no `{{else}}`)\r\n * is kept, surrounded by single `\\n` delimiters.\r\n * - When `context[flag]` is falsy and a `{{else}}` branch exists: the\r\n * content after `{{else}}` is kept, surrounded by single `\\n` delimiters.\r\n * - When `context[flag]` is falsy and no `{{else}}` branch exists: the\r\n * entire block (including surrounding newlines) is removed, leaving a\r\n * single `\\n`.\r\n * - Unknown flags (absent from context) are treated as falsy.\r\n *\r\n * Leading and trailing newlines within the kept content are trimmed so the\r\n * output does not accumulate extra blank lines.\r\n *\r\n * Nesting algorithm: the regex matches only *innermost* blocks — those\r\n * whose content contains no further `{{#if` markers. The replacement loop\r\n * repeats until the output stabilises, resolving each nesting level in\r\n * depth-first (innermost-first) order. This avoids the closing `{{/if}}`\r\n * ambiguity that arises with non-greedy single-pass matching.\r\n *\r\n * @param text - Template string potentially containing {{#if}} blocks\r\n * @param context - Key-value map used to evaluate flag truthiness\r\n * @returns The template string with conditional blocks resolved\r\n */\r\nexport function resolveConditionals(\r\n text: string,\r\n context: Record<string, unknown>,\r\n): string {\r\n // Normalise {{else if}} chains into nested {{else}}{{#if}} blocks so the\r\n // existing innermost-first resolution loop handles them transparently.\r\n const normalized = resolveElseIf(text);\r\n\r\n // Match only innermost conditional blocks — those whose truthy and falsy\r\n // content contains no nested `{{#if`. The negative lookahead\r\n // `(?!\\{\\{#if\\b)` ensures the quantifier stops before any nested opener,\r\n // so each pass resolves one depth level. Subsequent passes bubble outward\r\n // until the output stabilises (no more `{{#if` markers remain).\r\n const pattern = new RegExp(\r\n String.raw`\\n*\\{\\{#if (\\w+)\\}\\}(${NO_NESTED_IF})` +\r\n String.raw`(?:\\{\\{else\\}\\}(${NO_NESTED_IF}))?\\{\\{\\/if\\}\\}\\n*`,\r\n 'g',\r\n );\r\n\r\n const resolve = (\r\n _match: string,\r\n flag: string,\r\n inner: string,\r\n elseInner: string | undefined,\r\n ): string => {\r\n if (context[flag]) {\r\n // Truthy: keep content before {{else}} (or entire inner if no {{else}})\r\n return '\\n' + inner.replace(/^\\n+/, '').replace(/\\n+$/, '') + '\\n';\r\n }\r\n if (elseInner !== undefined) {\r\n // Falsy with {{else}}: keep content after {{else}}\r\n return '\\n' + elseInner.replace(/^\\n+/, '').replace(/\\n+$/, '') + '\\n';\r\n }\r\n // Falsy without {{else}}: remove entire block\r\n return '\\n';\r\n };\r\n\r\n let result = normalized;\r\n let prev: string;\r\n do {\r\n prev = result;\r\n result = result.replace(pattern, resolve);\r\n } while (result !== prev);\r\n return result;\r\n}\r\n","/**\r\n * variables.ts\r\n *\r\n * Pure template-engine function for resolving variable substitutions.\r\n * Handles {{varName}} syntax. No file-system I/O.\r\n */\r\n\r\n/**\r\n * Resolve variable substitutions in a template string.\r\n *\r\n * Replaces `{{varName}}` markers with `String(context[varName])`.\r\n * If a variable is not found in `context` (or its value is `undefined`),\r\n * the original marker is preserved and a warning is emitted via\r\n * `console.warn`, identifying the file by `filename` for easier debugging.\r\n *\r\n * Note: this step must run AFTER `resolvePartials` and `resolveConditionals`\r\n * so that only plain variable markers remain.\r\n *\r\n * @param text - Template string potentially containing {{varName}} markers\r\n * @param context - Key-value map of variable name → value\r\n * @param filename - Identifier used in warning messages (e.g. persona file path)\r\n * @returns The template string with variable markers substituted\r\n */\r\nexport function resolveVariables(\r\n text: string,\r\n context: Record<string, unknown>,\r\n filename: string,\r\n): string {\r\n return text.replace(/\\{\\{(\\w+)\\}\\}/g, (match, varName: string) => {\r\n if (varName in context && context[varName] !== undefined) {\r\n return String(context[varName]);\r\n }\r\n console.warn(`[WARN] Unresolved variable: ${match} in ${filename}`);\r\n return match;\r\n });\r\n}\r\n","/**\r\n * postProcessor.ts\r\n *\r\n * Pure post-processing functions for cleaning up rendered persona output.\r\n * All functions are side-effect-free and operate only on strings.\r\n * No file-system I/O.\r\n */\r\n\r\n/**\r\n * Collapse 3 or more consecutive blank lines into 2 blank lines.\r\n *\r\n * Specifically converts 4 or more consecutive `\\n` characters into `\\n\\n\\n`\r\n * (which equals 2 blank lines between paragraphs).\r\n *\r\n * @param text - Rendered output string\r\n * @returns String with excessive blank lines collapsed\r\n */\r\nexport function collapseBlankLines(text: string): string {\r\n return text.replace(/\\n{4,}/g, '\\n\\n\\n');\r\n}\r\n\r\n/**\r\n * Ensure every Markdown heading has a blank line immediately before it.\r\n *\r\n * Also ensures horizontal rules (`---`) have a blank line before and after\r\n * them. This corrects spacing gaps caused by partial concatenation where\r\n * `trimEnd()` strips trailing newlines and conditionals add only a single\r\n * `\\n` delimiter.\r\n *\r\n * @param text - Rendered output string\r\n * @returns String with blank lines inserted before headings and rules\r\n */\r\nexport function ensureBlankLineBeforeHeadings(text: string): string {\r\n // Blank line before headings\r\n let result = text.replace(/([^\\n])\\n(#{1,6} )/g, '$1\\n\\n$2');\r\n // Blank line before horizontal rules (---)\r\n result = result.replace(/([^\\n])\\n(---)\\n/g, '$1\\n\\n$2\\n');\r\n // Blank line after horizontal rules (---)\r\n result = result.replace(/\\n(---)\\n([^\\n])/g, '\\n$1\\n\\n$2');\r\n return result;\r\n}\r\n\r\n/**\r\n * Normalize line endings to LF (`\\n`) for OS-agnostic output.\r\n *\r\n * Converts CRLF (`\\r\\n`) first, then strips any remaining stray CR (`\\r`).\r\n *\r\n * @param text - String potentially containing CRLF or CR line endings\r\n * @returns String with all line endings normalized to LF\r\n */\r\nexport function normalizeNewlines(text: string): string {\r\n return text.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\r\n}\r\n","/**\r\n * serializer.ts\r\n *\r\n * Pure serializer functions for converting tool lists to YAML-compatible\r\n * string representations. No file-system I/O.\r\n */\r\n\r\n/**\r\n * Serialize a tools array in YAML single-quote flow format WITH outer brackets.\r\n *\r\n * Output format: `['tool1', 'tool2', 'tool3']`\r\n * Used by the ledger suite to preserve byte-identical frontmatter output.\r\n *\r\n * @param tools - Array of tool name strings\r\n * @returns YAML flow-sequence string including outer brackets\r\n *\r\n * @example\r\n * serializeTools(['Bash', 'Read']) // => \"['Bash', 'Read']\"\r\n * serializeTools([]) // => \"[]\"\r\n */\r\nexport function serializeTools(tools: string[]): string {\r\n return '[' + tools.map((t) => `'${t}'`).join(', ') + ']';\r\n}\r\n\r\n/**\r\n * Serialize a tools array in YAML single-quote flow format WITHOUT outer brackets.\r\n *\r\n * Output format: `'tool1', 'tool2', 'tool3'`\r\n * Used inside standalone frontmatter templates which supply the surrounding `[ ]`.\r\n *\r\n * @param tools - Array of tool name strings\r\n * @returns Comma-separated quoted tool names (no outer brackets)\r\n *\r\n * @example\r\n * serializeToolsList(['Bash', 'Read']) // => \"'Bash', 'Read'\"\r\n * serializeToolsList([]) // => \"\"\r\n */\r\nexport function serializeToolsList(tools: string[]): string {\r\n return tools.map((t) => `'${t}'`).join(', ');\r\n}\r\n","/**\r\n * src/loaders/partials-loader.ts\r\n *\r\n * File-system loader for Handlebars-style partial snippets.\r\n *\r\n * Reads every `.md` file in `dir`, keys each entry by the filename stem\r\n * (i.e. the portion before the final `.md` extension), and returns the\r\n * map. Callers that need a two-layer (shared → suite-local override)\r\n * setup should call `loadPartials` twice and merge the results themselves,\r\n * with the suite-local result spreading last.\r\n *\r\n * All file reads are performed asynchronously. Path construction uses\r\n * `path.join` and `path.posix`-compatible operations so no path-separator\r\n * assumptions are baked in.\r\n */\r\n\r\nimport { readdir, readFile } from 'node:fs/promises';\r\nimport path from 'node:path';\r\n\r\n/**\r\n * Load all `.md` files in `dir` and return them as a `Record<string, string>`\r\n * keyed by filename stem.\r\n *\r\n * Files whose names do not end in `.md` are silently ignored.\r\n * The directory must exist; a missing directory throws an `ENOENT` error from\r\n * the underlying `readdir` call (let callers decide how to handle absence).\r\n *\r\n * @param dir Absolute (or relative) path to the directory to scan.\r\n * @returns A map from filename stem → file content string.\r\n *\r\n * @example\r\n * const partials = await loadPartials('/project/partials');\r\n * // { greeting: 'Hello, {{name}}!', footer: '---\\nEnd of file' }\r\n */\r\nexport async function loadPartials(dir: string): Promise<Record<string, string>> {\r\n const entries = await readdir(dir, { withFileTypes: true });\r\n\r\n const mdFiles = entries.filter(\r\n (entry) => entry.isFile() && entry.name.endsWith('.md'),\r\n );\r\n\r\n const pairs = await Promise.all(\r\n mdFiles.map(async (entry) => {\r\n const stem = entry.name.slice(0, -'.md'.length); // strip trailing \".md\"\r\n const filePath = path.join(dir, entry.name);\r\n const content = await readFile(filePath, 'utf8');\r\n return [stem, content] as [string, string];\r\n }),\r\n );\r\n\r\n return Object.fromEntries(pairs);\r\n}\r\n","/**\r\n * src/loaders/metadata-loader.ts\r\n *\r\n * File-system loader for persona YAML metadata files.\r\n *\r\n * Provides two exports:\r\n *\r\n * 1. `discoverPersonaYamls(root)` — recursively walks `root` and returns\r\n * absolute paths for every `*.yaml` file found, regardless of nesting\r\n * depth. Uses Node's built-in `fs.readdir` with `recursive: true`\r\n * (available since Node 18.17). No glob library is required.\r\n *\r\n * 2. `loadMetadata(yamlPath)` — reads a single YAML file and parses it\r\n * with `js-yaml` into a fully typed `PersonaMetadata` object.\r\n *\r\n * Path construction relies exclusively on `node:path` so the output is\r\n * correct on both POSIX and Windows.\r\n */\r\n\r\nimport { readdir, readFile } from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport yaml from 'js-yaml';\r\nimport type { PersonaMetadata } from '../plugins/types.js';\r\n\r\n// Re-export the type so consumers can import it directly from this module\r\nexport type { PersonaMetadata };\r\n\r\n// ---------------------------------------------------------------------------\r\n// YAML discovery\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Recursively discover all `*.yaml` files under `root` and return their\r\n * absolute paths sorted lexicographically.\r\n *\r\n * Uses `readdir` with `{ recursive: true }` (Node ≥ 18.17). Each returned\r\n * path is normalised through `path.resolve` so callers always receive\r\n * absolute, platform-consistent paths.\r\n *\r\n * @param root The directory to search (absolute or resolvable relative path).\r\n * @returns Sorted array of absolute paths to every `*.yaml` file found.\r\n *\r\n * @example\r\n * const yamls = await discoverPersonaYamls('/project/personas/ledger/src/meta');\r\n * // ['/project/personas/ledger/src/meta/alpha.yaml', ...]\r\n */\r\nexport async function discoverPersonaYamls(root: string): Promise<string[]> {\r\n const absRoot = path.resolve(root);\r\n\r\n // Node ≥ 18.17: readdir with recursive returns relative paths from root\r\n const allEntries = await readdir(absRoot, { recursive: true, withFileTypes: false });\r\n\r\n const yamlPaths = (allEntries as string[])\r\n .filter((entry) => entry.endsWith('.yaml'))\r\n .map((entry) => path.join(absRoot, entry))\r\n .sort();\r\n\r\n return yamlPaths;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// YAML parsing\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Load and parse a single persona YAML file into a typed `PersonaMetadata`\r\n * object.\r\n *\r\n * The YAML is parsed using `js-yaml`'s safe `load` function. The result\r\n * is validated to be a non-null object; if the YAML is empty or does not\r\n * parse to an object, an `Error` is thrown.\r\n *\r\n * `PersonaMetadata` requires a `name` field. If the YAML does not contain\r\n * a `name` key the function throws an `Error` with a descriptive message.\r\n *\r\n * @param yamlPath Absolute path to the YAML file.\r\n * @returns Parsed and validated `PersonaMetadata` object.\r\n * @throws `Error` when the file is unparseable, not an object, or\r\n * is missing the required `name` field.\r\n *\r\n * @example\r\n * const meta = await loadMetadata('/project/meta/my-persona.yaml');\r\n * // { name: 'my-persona', description: '...', tools: [...] }\r\n */\r\nexport async function loadMetadata(yamlPath: string): Promise<PersonaMetadata> {\r\n const raw = await readFile(yamlPath, 'utf8');\r\n\r\n const parsed: unknown = yaml.load(raw);\r\n\r\n if (parsed === null || parsed === undefined || typeof parsed !== 'object' || Array.isArray(parsed)) {\r\n throw new Error(\r\n `loadMetadata: expected a YAML object in \"${yamlPath}\", got ${\r\n Array.isArray(parsed) ? 'array' : String(parsed)\r\n }`,\r\n );\r\n }\r\n\r\n const record = parsed as Record<string, unknown>;\r\n\r\n if (typeof record['name'] !== 'string' || record['name'].trim() === '') {\r\n throw new Error(\r\n `loadMetadata: YAML file \"${yamlPath}\" is missing a required string field \"name\"`,\r\n );\r\n }\r\n\r\n return record as PersonaMetadata;\r\n}\r\n","/**\r\n * src/loaders/content-loader.ts\r\n *\r\n * File-system loader for persona Markdown content templates.\r\n *\r\n * Provides a single `loadContent` function that reads the raw string content\r\n * of a persona Markdown file from disk. The content is returned exactly as\r\n * stored — no template substitution, no post-processing. Those concerns\r\n * belong to the engine layer.\r\n *\r\n * All I/O is asynchronous. Path construction uses `node:path` so the\r\n * implementation is path-separator–agnostic.\r\n */\r\n\r\nimport { readFile } from 'node:fs/promises';\r\nimport path from 'node:path';\r\n\r\n/**\r\n * Read a persona Markdown content file and return its raw string content.\r\n *\r\n * The file is read with UTF-8 encoding. No parsing, template resolution,\r\n * or post-processing is applied — that is the engine layer's responsibility.\r\n *\r\n * @param mdPath Absolute (or resolvable relative) path to the `.md` file.\r\n * @returns Raw UTF-8 string content of the file.\r\n * @throws An `ENOENT` error (from `fs/promises`) if the file does not\r\n * exist, or any other I/O error the OS reports.\r\n *\r\n * @example\r\n * const body = await loadContent('/project/content/my-persona.md');\r\n * // '{{> greeting}}\\n\\n## About\\n\\nThis is {{name}}...'\r\n */\r\nexport async function loadContent(mdPath: string): Promise<string> {\r\n const absPath = path.resolve(mdPath);\r\n return readFile(absPath, 'utf8');\r\n}\r\n","/**\r\n * src/plugins/runner.ts\r\n *\r\n * Plugin runner — responsible for invoking plugin hooks in registration order.\r\n *\r\n * Each exported function corresponds to one lifecycle hook defined in\r\n * PersonaBuildPlugin. The runner:\r\n * - Skips plugins that do not implement the requested hook (hook is optional)\r\n * - Invokes hooks in the order plugins are registered (first-in first-called)\r\n * - For accumulating hooks (onBuildContext, onPartials, onPersonaPartials,\r\n * onPostRender), each plugin receives the output of the previous plugin\r\n * as its first argument\r\n * - For collecting hooks (onValidate), results are concatenated into a\r\n * flat array\r\n *\r\n * No file-system I/O. No async operations.\r\n */\r\n\r\nimport type {\r\n PersonaBuildPlugin,\r\n PersonaMetadata,\r\n SuiteConfig,\r\n TargetType,\r\n ValidationResult,\r\n} from './types.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// Suite-level hook\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Invoke the `onSuiteInit` hook on every registered plugin.\r\n *\r\n * Each plugin may optionally implement this hook. Plugins are called in\r\n * registration order. The hook receives the suite config and a mutable\r\n * `sharedMeta` object — plugins may mutate `sharedMeta` in place; the\r\n * same reference is passed to every subsequent plugin.\r\n *\r\n * @param plugins Ordered list of registered plugins\r\n * @param suite The suite configuration object\r\n * @param sharedMeta Mutable shared metadata object (mutated in place by plugins)\r\n */\r\nexport function runSuiteInit(\r\n plugins: PersonaBuildPlugin[],\r\n suite: SuiteConfig,\r\n sharedMeta: Record<string, unknown>,\r\n): void {\r\n for (const plugin of plugins) {\r\n if (typeof plugin.onSuiteInit === 'function') {\r\n plugin.onSuiteInit(suite, sharedMeta);\r\n }\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Per-persona context accumulation\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Invoke the `onBuildContext` hook on every registered plugin, accumulating\r\n * context mutations sequentially.\r\n *\r\n * Each plugin receives the context returned by the previous plugin. If a\r\n * plugin does not implement `onBuildContext`, the context passes through\r\n * unchanged. The final accumulated context is returned.\r\n *\r\n * @param plugins Ordered list of registered plugins\r\n * @param ctx Initial rendering context for this persona\r\n * @param persona Typed metadata for the persona being built\r\n * @param suite The suite configuration object\r\n * @param target The current build target (optional — forwarded to each plugin)\r\n * @returns Accumulated rendering context after all plugins have run\r\n */\r\nexport function runBuildContext(\r\n plugins: PersonaBuildPlugin[],\r\n ctx: Record<string, unknown>,\r\n persona: PersonaMetadata,\r\n suite: SuiteConfig,\r\n target?: TargetType,\r\n): Record<string, unknown> {\r\n let accumulated = ctx;\r\n for (const plugin of plugins) {\r\n if (typeof plugin.onBuildContext === 'function') {\r\n accumulated = plugin.onBuildContext(accumulated, persona, suite, target) ?? accumulated;\r\n }\r\n }\r\n return accumulated;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Per-persona post-render chain\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Invoke the `onPostRender` hook on every registered plugin, chaining the\r\n * output string sequentially.\r\n *\r\n * Each plugin receives the string returned by the previous plugin. If a\r\n * plugin does not implement `onPostRender`, the string passes through\r\n * unchanged. The final string is returned.\r\n *\r\n * @param plugins Ordered list of registered plugins\r\n * @param rendered Initial rendered output string\r\n * @param persona Typed metadata for the persona being built\r\n * @param target The current build target\r\n * @returns Final output string after all plugins have run\r\n */\r\nexport function runPostRender(\r\n plugins: PersonaBuildPlugin[],\r\n rendered: string,\r\n persona: PersonaMetadata,\r\n target: TargetType,\r\n): string {\r\n let output = rendered;\r\n for (const plugin of plugins) {\r\n if (typeof plugin.onPostRender === 'function') {\r\n output = plugin.onPostRender(output, persona, target) ?? output;\r\n }\r\n }\r\n return output;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Suite-level partials accumulation\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Invoke the `onPartials` hook on every registered plugin, accumulating\r\n * partials map mutations sequentially.\r\n *\r\n * Each plugin receives the partials map returned by the previous plugin. If a\r\n * plugin does not implement `onPartials`, the map passes through unchanged.\r\n * The final accumulated map is returned.\r\n *\r\n * Called once per suite after partials are loaded from disk, before any\r\n * persona is rendered.\r\n *\r\n * @param plugins Ordered list of registered plugins\r\n * @param partialsMap Initial map of partial name → partial content\r\n * @param suiteName The identifier of the current suite\r\n * @param suite The suite configuration object\r\n * @returns Accumulated partials map after all plugins have run\r\n */\r\nexport function runPartials(\r\n plugins: PersonaBuildPlugin[],\r\n partialsMap: Record<string, string>,\r\n suiteName: string,\r\n suite: SuiteConfig,\r\n): Record<string, string> {\r\n let accumulated = partialsMap;\r\n for (const plugin of plugins) {\r\n if (typeof plugin.onPartials === 'function') {\r\n accumulated = plugin.onPartials(accumulated, suiteName, suite) ?? accumulated;\r\n }\r\n }\r\n return accumulated;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Per-persona partials accumulation\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Invoke the `onPersonaPartials` hook on every registered plugin, accumulating\r\n * partials map mutations sequentially.\r\n *\r\n * Each plugin receives the partials map returned by the previous plugin. If a\r\n * plugin does not implement `onPersonaPartials`, the map passes through\r\n * unchanged. The final accumulated map is returned.\r\n *\r\n * Called for each persona (and target) after `onBuildContext`, before\r\n * template rendering.\r\n *\r\n * @param plugins Ordered list of registered plugins\r\n * @param partialsMap Initial map of partial name → partial content\r\n * @param persona Typed metadata for the persona being built\r\n * @param context The rendering context built by `onBuildContext`\r\n * @param suite The suite configuration object\r\n * @param target The current build target (optional)\r\n * @returns Accumulated partials map after all plugins have run\r\n */\r\nexport function runPersonaPartials(\r\n plugins: PersonaBuildPlugin[],\r\n partialsMap: Record<string, string>,\r\n persona: PersonaMetadata,\r\n context: Record<string, unknown>,\r\n suite: SuiteConfig,\r\n target?: TargetType,\r\n): Record<string, string> {\r\n let accumulated = partialsMap;\r\n for (const plugin of plugins) {\r\n if (typeof plugin.onPersonaPartials === 'function') {\r\n accumulated = plugin.onPersonaPartials(accumulated, persona, context, suite, target) ?? accumulated;\r\n }\r\n }\r\n return accumulated;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Per-persona validation collection\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Invoke the `onValidate` hook on every registered plugin and collect all\r\n * returned ValidationResult objects into a single flat array.\r\n *\r\n * Plugins that do not implement `onValidate` contribute nothing to the result.\r\n * The return value is always an array (never null/undefined).\r\n *\r\n * @param plugins Ordered list of registered plugins\r\n * @param persona Typed metadata for the persona being built\r\n * @param suite The suite configuration object\r\n * @param target The current build target (optional — forwarded to each plugin)\r\n * @returns Flat array of all ValidationResult objects from all plugins\r\n */\r\nexport function runValidate(\r\n plugins: PersonaBuildPlugin[],\r\n persona: PersonaMetadata,\r\n suite: SuiteConfig,\r\n target?: TargetType,\r\n): ValidationResult[] {\r\n const results: ValidationResult[] = [];\r\n for (const plugin of plugins) {\r\n if (typeof plugin.onValidate === 'function') {\r\n const pluginResults = plugin.onValidate(persona, suite, target);\r\n results.push(...pluginResults);\r\n }\r\n }\r\n return results;\r\n}\r\n","/**\r\n * src/targets/types.ts\r\n *\r\n * Target type definitions for @mistralys/persona-builder.\r\n *\r\n * Defines the TargetDefinition interface and well-known target name constants.\r\n */\r\n\r\n// ---------------------------------------------------------------------------\r\n// TargetDefinition interface\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Describes a build target — maps a target name to its output directory key,\r\n * filename context key, default frontmatter template, and optional\r\n * auto-injected context flags.\r\n */\r\nexport interface TargetDefinition {\r\n /** Unique target identifier (e.g. 'vscode', 'claude-code'). */\r\n name: string;\r\n\r\n /**\r\n * Key used to look up the output directory in the suite's output dir map.\r\n * For built-in targets this matches the target name.\r\n */\r\n outputDirKey: string;\r\n\r\n /**\r\n * Context field name that holds a custom output filename for this target.\r\n * When present and non-empty in the rendered context, it overrides the\r\n * default filename derived from the persona name.\r\n */\r\n filenameContextKey?: string;\r\n\r\n /**\r\n * Default frontmatter template string for this target.\r\n * Used when neither a plugin nor a BuildConfig template is provided.\r\n */\r\n defaultFrontmatter: string;\r\n\r\n /**\r\n * Declarative context flags merged into the build context when this target\r\n * is rendered. Useful for `{{#if target_vscode}}` guards.\r\n *\r\n * All entries are spread into the context by `buildContext()` via a\r\n * registry lookup — `registry.get(target).contextFlags`. The fallback path\r\n * (for targets absent from the registry) injects a single boolean flag\r\n * derived from the target name: `target_${name.replace(/-/g, '_')} = true`.\r\n */\r\n contextFlags?: Record<string, unknown>;\r\n\r\n /**\r\n * Whether this target is included in the default build when no explicit\r\n * `targets` array is configured. Defaults to `true` when omitted.\r\n *\r\n * Set to `false` for opt-in targets (e.g. `'deep-agents'`) that should\r\n * only be built when explicitly requested via `config.targets`.\r\n */\r\n defaultEnabled?: boolean;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Well-known target name constants\r\n// ---------------------------------------------------------------------------\r\n\r\n/** Well-known name constant for the VS Code target. */\r\nexport const TARGET_VSCODE = 'vscode' as const;\r\n\r\n/** Well-known name constant for the Claude Code target. */\r\nexport const TARGET_CLAUDE_CODE = 'claude-code' as const;\r\n\r\n/** Well-known name constant for the Deep Agents target. */\r\nexport const TARGET_DEEP_AGENTS = 'deep-agents' as const;\r\n\r\n// ---------------------------------------------------------------------------\r\n// Default frontmatter templates (owned by the targets layer)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Default VS Code frontmatter template.\r\n *\r\n * Minimal fields that work for standalone personas. Projects using numbered\r\n * workflows (e.g. ledger) should inject a richer template via a plugin.\r\n */\r\nexport const DEFAULT_FRONTMATTER_VSCODE = `---\r\nname: '{{name}} v{{version}}'\r\ndescription: '{{description}}'\r\ntools: [{{tools_list}}]\r\n---`;\r\n\r\n/**\r\n * Default Claude Code frontmatter template.\r\n *\r\n * Minimal fields that work for standalone personas. Projects using numbered\r\n * workflows should inject a richer template via a plugin.\r\n */\r\nexport const DEFAULT_FRONTMATTER_CLAUDE_CODE = `---\r\nname: {{cc_file_name_stem}}\r\npermissionMode: {{cc_permission_mode}}\r\nmodel: {{cc_model}}\r\nmemory: {{cc_memory}}\r\nallowedTools: [{{cc_tools_list}}]\r\n---`;\r\n\r\n/**\r\n * Default Deep Agents frontmatter template.\r\n *\r\n * Minimal fields — no IDE-specific properties. Suitable for headless\r\n * LangGraph / Deep Agents pipeline executors that consume persona files\r\n * without an IDE host.\r\n */\r\nexport const DEFAULT_FRONTMATTER_DEEP_AGENTS = `---\r\nname: {{name}}\r\ndescription: {{description}}\r\n---`;\r\n","/**\r\n * src/builders/frontmatter.ts\r\n *\r\n * Frontmatter template registry for @mistralys/persona-builder.\r\n *\r\n * Ships three minimal default templates — one per built-in target — that work for the\r\n * \"standalone\" persona mode (simple personas without numbered workflows or\r\n * MCP server blocks). Projects needing richer frontmatter register custom\r\n * templates via the `PersonaBuildPlugin.frontmatterTemplates` property.\r\n *\r\n * Template rendering follows the same two-step sequence as body rendering:\r\n * 1. resolveConditionals() — resolve {{#if flag}} blocks\r\n * 2. resolveVariables() — substitute {{varName}} markers\r\n *\r\n * No partials in frontmatter — frontmatter is kept deliberately simple.\r\n */\r\n\r\nimport { resolveConditionals } from '../engine/conditionals.js';\r\nimport { resolveVariables } from '../engine/variables.js';\r\nimport type { PersonaBuildPlugin } from '../plugins/types.js';\r\nimport type { TargetRegistry } from '../targets/registry.js';\r\n\r\n// Default templates are owned by the targets layer; re-exported here so\r\n// the public API surface (src/builders/index.ts) is unchanged.\r\nimport {\r\n DEFAULT_FRONTMATTER_VSCODE,\r\n DEFAULT_FRONTMATTER_CLAUDE_CODE,\r\n DEFAULT_FRONTMATTER_DEEP_AGENTS,\r\n} from '../targets/types.js';\r\nexport { DEFAULT_FRONTMATTER_VSCODE, DEFAULT_FRONTMATTER_CLAUDE_CODE, DEFAULT_FRONTMATTER_DEEP_AGENTS };\r\n\r\n// ---------------------------------------------------------------------------\r\n// Template resolution\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Resolve frontmatter template precedence.\r\n *\r\n * Precedence order (highest wins):\r\n * 1. Plugin `frontmatterTemplates` — plugins are checked in registration\r\n * order; the first plugin with a matching key wins.\r\n * 2. `configTemplates` — templates passed via `BuildConfig.frontmatter`\r\n * 3. Registry default — `TargetDefinition.defaultFrontmatter` for the target\r\n * 4. Library default (`DEFAULT_FRONTMATTER_VSCODE`) — safety fallback only\r\n *\r\n * @param target The build target name (e.g. `'vscode'`, `'claude-code'`, or a custom target)\r\n * @param plugins Registered plugins (searched in order; first match wins)\r\n * @param configTemplates Optional caller-supplied overrides from BuildConfig\r\n * @param registry Optional TargetRegistry for resolving the built-in default template\r\n * @returns The resolved template string\r\n */\r\nexport function resolveFrontmatterTemplate(\r\n target: string,\r\n plugins: PersonaBuildPlugin[],\r\n configTemplates?: Record<string, string>,\r\n registry?: TargetRegistry,\r\n): string {\r\n // Check plugins in registration order — first plugin with a matching\r\n // frontmatterTemplates entry wins.\r\n for (const plugin of plugins) {\r\n if (plugin.frontmatterTemplates && target in plugin.frontmatterTemplates) {\r\n const tpl = plugin.frontmatterTemplates[target];\r\n if (tpl !== undefined) return tpl;\r\n }\r\n }\r\n\r\n // Caller-supplied config templates\r\n if (configTemplates && target in configTemplates) {\r\n const tpl = configTemplates[target];\r\n if (tpl !== undefined) return tpl;\r\n }\r\n\r\n // Registry default (covers all registered targets, including custom ones)\r\n if (registry && registry.has(target)) {\r\n return registry.get(target).defaultFrontmatter;\r\n }\r\n\r\n // Absolute fallback — should not be reached in normal usage\r\n return DEFAULT_FRONTMATTER_VSCODE;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Frontmatter rendering\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Render a frontmatter template string against the given context.\r\n *\r\n * Applies the standard two-step template resolution:\r\n * 1. `resolveConditionals` — `{{#if flag}}` blocks\r\n * 2. `resolveVariables` — `{{varName}}` substitution\r\n *\r\n * @param template The raw frontmatter template string (may contain markers)\r\n * @param context Key-value context for variable substitution\r\n * @param filename Source filename used in warning messages\r\n * @returns Rendered frontmatter string (ready to prepend to body)\r\n */\r\nexport function renderFrontmatter(\r\n template: string,\r\n context: Record<string, unknown>,\r\n filename: string,\r\n): string {\r\n let rendered = resolveConditionals(template, context);\r\n rendered = resolveVariables(rendered, context, filename);\r\n return rendered;\r\n}\r\n","/**\r\n * src/targets/registry.ts\r\n *\r\n * TargetRegistry — holds TargetDefinition entries and allows consumers to\r\n * register custom build targets alongside (or instead of) the built-in ones.\r\n */\r\n\r\nimport type { TargetDefinition } from './types.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// TargetRegistry class\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Registry that maps target names to their TargetDefinition objects.\r\n *\r\n * Consumers can extend the default build system by calling `register()` with\r\n * a custom TargetDefinition before invoking `build()`.\r\n *\r\n * @example\r\n * ```ts\r\n * import { defaultRegistry } from '@mistralys/persona-builder';\r\n *\r\n * defaultRegistry.register({\r\n * name: 'my-target',\r\n * outputDirKey: 'my-target',\r\n * defaultFrontmatter: '---\\nmy: frontmatter\\n---',\r\n * contextFlags: { target_my_target: true },\r\n * });\r\n * ```\r\n */\r\nexport class TargetRegistry {\r\n // Map preserves insertion order — names() and allDefinitions() are\r\n // therefore deterministic and match registration sequence. This is\r\n // intentional: the built-in registry guarantees ['vscode', 'claude-code']\r\n // ordering for the default targets (AC-2).\r\n private readonly _definitions = new Map<string, TargetDefinition>();\r\n\r\n /**\r\n * Register a new target definition.\r\n *\r\n * @param definition The target descriptor to register.\r\n * @throws {Error} If a target with the same `name` is already registered.\r\n */\r\n register(definition: TargetDefinition): void {\r\n if (this._definitions.has(definition.name)) {\r\n throw new Error(\r\n `TargetRegistry: target \"${definition.name}\" is already registered. ` +\r\n `Use a unique name or remove the existing registration first.`,\r\n );\r\n }\r\n this._definitions.set(definition.name, definition);\r\n }\r\n\r\n /**\r\n * Retrieve a registered target definition by name.\r\n *\r\n * Returns a shallow copy — mutating the returned object does not affect\r\n * the registry's internal state.\r\n *\r\n * @param name The target name to look up.\r\n * @returns A shallow copy of the matching TargetDefinition.\r\n * @throws {Error} If no target with the given name is registered.\r\n */\r\n get(name: string): TargetDefinition {\r\n const def = this._definitions.get(name);\r\n if (!def) {\r\n const known = this.names().join(', ') || '(none)';\r\n throw new Error(\r\n `TargetRegistry: target \"${name}\" is not registered. ` +\r\n `Registered targets: ${known}.`,\r\n );\r\n }\r\n return { ...def };\r\n }\r\n\r\n /**\r\n * Returns `true` if a target with the given name is registered.\r\n *\r\n * @param name The target name to check.\r\n */\r\n has(name: string): boolean {\r\n return this._definitions.has(name);\r\n }\r\n\r\n /**\r\n * Returns the names of all registered targets, in registration order.\r\n */\r\n names(): string[] {\r\n return Array.from(this._definitions.keys());\r\n }\r\n\r\n /**\r\n * Returns all registered TargetDefinition objects, in registration order.\r\n *\r\n * Returns shallow copies — mutating a returned definition does not affect\r\n * the registry's internal state.\r\n */\r\n allDefinitions(): TargetDefinition[] {\r\n return Array.from(this._definitions.values()).map(def => ({ ...def }));\r\n }\r\n\r\n /**\r\n * Returns a new TargetRegistry pre-populated with the same definitions.\r\n *\r\n * Useful for test isolation: clone the `defaultRegistry` to get an\r\n * independent copy that can be mutated without affecting the singleton.\r\n */\r\n clone(): TargetRegistry {\r\n const copy = new TargetRegistry();\r\n for (const def of this._definitions.values()) {\r\n copy.register({ ...def });\r\n }\r\n return copy;\r\n }\r\n}\r\n","/**\r\n * src/targets/built-in.ts\r\n *\r\n * Creates and exports the defaultRegistry — a TargetRegistry pre-populated\r\n * with the three built-in targets: 'vscode', 'claude-code', and 'deep-agents'.\r\n *\r\n * Consumers import defaultRegistry when they need to register additional\r\n * custom targets or query the built-in target definitions.\r\n */\r\n\r\nimport { TargetRegistry } from './registry.js';\r\nimport {\r\n DEFAULT_FRONTMATTER_CLAUDE_CODE,\r\n DEFAULT_FRONTMATTER_DEEP_AGENTS,\r\n DEFAULT_FRONTMATTER_VSCODE,\r\n TARGET_CLAUDE_CODE,\r\n TARGET_DEEP_AGENTS,\r\n TARGET_VSCODE,\r\n} from './types.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// Default registry\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Singleton TargetRegistry pre-populated with the three built-in targets:\r\n * `'vscode'`, `'claude-code'`, and `'deep-agents'`.\r\n *\r\n * Import and call `register()` on this instance to add custom targets before\r\n * invoking `build()`.\r\n *\r\n * **Warning:** This is a module-level singleton. Calling `register()` on it\r\n * in tests mutates shared state that persists across test cases. Use\r\n * `defaultRegistry.clone()` to obtain an isolated copy for test scenarios\r\n * that need to register additional targets.\r\n */\r\nexport const defaultRegistry = new TargetRegistry();\r\n\r\ndefaultRegistry.register({\r\n name: TARGET_VSCODE,\r\n outputDirKey: 'vscode',\r\n filenameContextKey: 'vs_file_name',\r\n defaultFrontmatter: DEFAULT_FRONTMATTER_VSCODE,\r\n contextFlags: { target_vscode: true },\r\n defaultEnabled: true,\r\n});\r\n\r\ndefaultRegistry.register({\r\n name: TARGET_CLAUDE_CODE,\r\n outputDirKey: 'claude-code',\r\n filenameContextKey: 'cc_file_name',\r\n defaultFrontmatter: DEFAULT_FRONTMATTER_CLAUDE_CODE,\r\n contextFlags: { target_claude_code: true },\r\n defaultEnabled: true,\r\n});\r\n\r\ndefaultRegistry.register({\r\n name: TARGET_DEEP_AGENTS,\r\n outputDirKey: 'deep-agents',\r\n filenameContextKey: 'da_file_name',\r\n defaultFrontmatter: DEFAULT_FRONTMATTER_DEEP_AGENTS,\r\n contextFlags: { target_deep_agents: true },\r\n defaultEnabled: false,\r\n});\r\n","/**\r\n * src/builders/persona-builder.ts\r\n *\r\n * Core build orchestrator for @mistralys/persona-builder.\r\n *\r\n * Exports three public functions:\r\n *\r\n * 1. buildPersona(personaYamlPath, suiteName, suiteConfig, sharedMeta,\r\n * partialsMap, config, plugins, target, agentMap?)\r\n * — Builds a single persona for a single target. Returns a BuildResult.\r\n *\r\n * 2. buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap?)\r\n * — Discovers all persona YAMLs for a suite, fires onSuiteInit, maps\r\n * buildPersona() over each, and returns BuildResult[].\r\n *\r\n * 3. build(config)\r\n * — Top-level entry point. Pre-scans all suites to build a cross-suite\r\n * agent name map, then iterates all suites × targets, calls\r\n * buildSuite() for each combination, and returns a BuildSummary.\r\n * Respects --check (no writes) and --strict (fail on warnings/errors).\r\n *\r\n * Template variable injection (7-layer merge order, later layers win):\r\n *\r\n * 1. BuildConfig.variables — global defaults; available to every suite\r\n * 2. SuiteConfig.variables — suite-level overrides\r\n * 3. _shared.yaml fields — shared metadata for the suite\r\n * 4. Per-persona YAML fields — per-persona metadata\r\n * 5. Derived fields — version fallback, tools serialisation, etc.\r\n * 6. Cross-suite agent map — agent_<slug> / agent_slug_<slug> entries\r\n * 7. Target flags — target_<name> booleans; always highest precedence\r\n *\r\n * Callers inject global or suite-scoped variables via `BuildConfig.variables`\r\n * and `SuiteConfig.variables` respectively. Both fields are forwarded by\r\n * buildPersona() to the internal buildContext() function as the two\r\n * lowest-priority layers in the merge chain.\r\n */\r\n\r\nimport { readdir, readFile, mkdir, writeFile } from 'node:fs/promises';\r\nimport { existsSync } from 'node:fs';\r\nimport path from 'node:path';\r\nimport yaml from 'js-yaml';\r\n\r\nimport { resolvePartials } from '../engine/partials.js';\r\nimport { resolveConditionals } from '../engine/conditionals.js';\r\nimport { resolveVariables } from '../engine/variables.js';\r\nimport {\r\n collapseBlankLines,\r\n ensureBlankLineBeforeHeadings,\r\n normalizeNewlines,\r\n} from '../engine/postProcessor.js';\r\nimport { serializeTools, serializeToolsList } from '../engine/serializer.js';\r\nimport { loadPartials } from '../loaders/partials-loader.js';\r\nimport {\r\n runSuiteInit,\r\n runPartials,\r\n runBuildContext,\r\n runPersonaPartials,\r\n runPostRender,\r\n runValidate,\r\n} from '../plugins/runner.js';\r\n\r\nimport { resolveFrontmatterTemplate, renderFrontmatter } from './frontmatter.js';\r\nimport type { BuildConfig, BuildResult, BuildSummary } from './types.js';\r\nimport type { PersonaBuildPlugin, PersonaMetadata, SuiteConfig, TargetType, ValidationResult } from '../plugins/types.js';\r\nimport { defaultRegistry } from '../targets/built-in.js';\r\nimport type { TargetDefinition } from '../targets/types.js';\r\nimport type { TargetRegistry } from '../targets/registry.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// Internal helpers\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Discover all persona YAML files in the `meta/` subdirectory of a suite.\r\n *\r\n * Excludes files whose names start with `_` (shared metadata files such as\r\n * `_shared.yaml`). Results are sorted lexicographically.\r\n *\r\n * @param suiteConfig Suite configuration (used to locate `metaSubdir`)\r\n * @returns Absolute paths to each persona YAML file, sorted.\r\n */\r\nasync function discoverSuitePersonaYamls(suiteConfig: SuiteConfig): Promise<string[]> {\r\n const metaSubdir = suiteConfig.metaSubdir ?? 'meta';\r\n const metaDir = path.join(suiteConfig.srcDir, metaSubdir);\r\n\r\n const entries = await readdir(metaDir, { withFileTypes: true });\r\n\r\n return entries\r\n .filter((e) => e.isFile() && e.name.endsWith('.yaml') && !e.name.startsWith('_'))\r\n .map((e) => path.join(metaDir, e.name))\r\n .sort();\r\n}\r\n\r\n/**\r\n * Load and parse a raw YAML file into a plain object.\r\n * Used for `_shared.yaml` which does not conform to PersonaMetadata's\r\n * `name` requirement.\r\n *\r\n * @param filePath Absolute path to the YAML file\r\n * @returns Parsed object, or {} when the file is empty/absent\r\n */\r\nasync function loadRawYaml(filePath: string): Promise<Record<string, unknown>> {\r\n if (!existsSync(filePath)) return {};\r\n const raw = await readFile(filePath, 'utf8');\r\n const parsed: unknown = yaml.load(raw);\r\n if (parsed === null || parsed === undefined) return {};\r\n if (typeof parsed !== 'object' || Array.isArray(parsed)) return {};\r\n return parsed as Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Load a persona YAML file and return it as a plain metadata record.\r\n * The `name` field is derived from the filename stem when absent.\r\n *\r\n * @param yamlPath Absolute path to the persona YAML file\r\n * @returns Merged metadata record ready for context building\r\n */\r\nasync function loadPersonaYaml(yamlPath: string): Promise<Record<string, unknown>> {\r\n const raw = await readFile(yamlPath, 'utf8');\r\n const parsed: unknown = yaml.load(raw);\r\n\r\n if (parsed === null || parsed === undefined || typeof parsed !== 'object' || Array.isArray(parsed)) {\r\n throw new Error(`buildPersona: expected a YAML object in \"${yamlPath}\"`);\r\n }\r\n\r\n const record = parsed as Record<string, unknown>;\r\n\r\n // Derive name from filename stem if not present in YAML\r\n if (!record['name']) {\r\n record['name'] = path.basename(yamlPath, '.yaml');\r\n }\r\n\r\n return record;\r\n}\r\n\r\n/**\r\n * Resolve the output directory for a given target from a suite configuration.\r\n *\r\n * Resolution order (first match wins):\r\n * 1. `suiteConfig.outputDirs[definition.outputDirKey]` — generic map, takes precedence.\r\n * 2. `suiteConfig.outVscode` — deprecated fallback for the built-in 'vscode' key.\r\n * 3. `suiteConfig.outClaudeCode` — deprecated fallback for the built-in 'claude-code' key.\r\n *\r\n * The lookup key is `definition.outputDirKey` when a registry definition is\r\n * provided, falling back to the raw `target` name for unregistered targets\r\n * (where `outputDirKey` equals the name by convention).\r\n *\r\n * @param target The build target name.\r\n * @param suiteConfig The suite configuration to resolve from.\r\n * @param definition Optional registry definition for the target. When present,\r\n * `definition.outputDirKey` is used as the output dir map key.\r\n * @returns Resolved output directory path.\r\n * @throws {Error} When no output directory is configured for the target.\r\n */\r\nfunction resolveOutputDir(\r\n target: string,\r\n suiteConfig: SuiteConfig,\r\n definition?: TargetDefinition,\r\n): string {\r\n // Build a merged map: deprecated named fields provide the base, outputDirs overrides.\r\n const merged: Record<string, string> = {};\r\n if (suiteConfig.outVscode) merged['vscode'] = suiteConfig.outVscode;\r\n if (suiteConfig.outClaudeCode) merged['claude-code'] = suiteConfig.outClaudeCode;\r\n if (suiteConfig.outputDirs) Object.assign(merged, suiteConfig.outputDirs);\r\n\r\n // Use outputDirKey from the registry definition when available, falling back\r\n // to the target name for unregistered targets where outputDirKey === name.\r\n const lookupKey = definition?.outputDirKey ?? target;\r\n const dir = merged[lookupKey];\r\n if (dir) return dir;\r\n\r\n throw new Error(\r\n `buildPersona: no output directory configured for target \"${target}\". ` +\r\n `Add outputDirs['${lookupKey}'] to the suite config, or, for the built-in ` +\r\n `targets, provide the outVscode / outClaudeCode fields.`,\r\n );\r\n}\r\n\r\n/**\r\n * Pre-scan all suites and build a cross-suite agent name map.\r\n *\r\n * For each persona across all configured suites, creates a context variable:\r\n * key: `agent_` + slug (hyphens → underscores)\r\n * value: `\"<name> v<version>\"`\r\n *\r\n * Slug is taken from the persona YAML's `slug` field, falling back to the\r\n * filename stem. Version falls back to the suite's `default_version`, then\r\n * to `'0.0.0'`.\r\n *\r\n * @param config Top-level BuildConfig with all suite definitions\r\n * @returns Map of agent variable keys to display strings\r\n */\r\nasync function buildAgentNameMap(\r\n config: BuildConfig,\r\n): Promise<Record<string, string>> {\r\n const agentMap: Record<string, string> = {};\r\n\r\n for (const [, suiteConfig] of Object.entries(config.suites)) {\r\n const metaSubdir = suiteConfig.metaSubdir ?? 'meta';\r\n const sharedYamlPath = path.join(suiteConfig.srcDir, metaSubdir, '_shared.yaml');\r\n const sharedMeta = await loadRawYaml(sharedYamlPath);\r\n const defaultVersion =\r\n typeof sharedMeta['default_version'] === 'string'\r\n ? sharedMeta['default_version']\r\n : '0.0.0';\r\n\r\n const personaYamls = await discoverSuitePersonaYamls(suiteConfig);\r\n\r\n for (const yamlPath of personaYamls) {\r\n const persona = await loadPersonaYaml(yamlPath);\r\n\r\n const slug =\r\n typeof persona['slug'] === 'string'\r\n ? persona['slug']\r\n : path.basename(yamlPath, '.yaml');\r\n\r\n const name =\r\n typeof persona['name'] === 'string'\r\n ? persona['name']\r\n : slug;\r\n\r\n const version =\r\n typeof persona['version'] === 'string'\r\n ? persona['version']\r\n : defaultVersion;\r\n\r\n const underscoredSlug = slug.replace(/-/g, '_');\r\n const key = `agent_${underscoredSlug}`;\r\n agentMap[key] = `${name} v${version}`;\r\n\r\n const slugKey = `agent_slug_${underscoredSlug}`;\r\n agentMap[slugKey] = slug;\r\n }\r\n }\r\n\r\n return agentMap;\r\n}\r\n\r\n/**\r\n * Build the merged template context for a single persona.\r\n *\r\n * Merge order (later values win):\r\n * 1. configVariables (lowest priority — global BuildConfig.variables)\r\n * 2. suiteVariables (suite-level SuiteConfig.variables)\r\n * 3. sharedMeta (parsed _shared.yaml fields)\r\n * 4. personaMeta (per-persona YAML fields)\r\n * 5. derived/computed fields (version fallback, tools serialisation, etc.)\r\n * 6. agentMap entries (only for keys not already present)\r\n * 7. target flags (injected last; always win)\r\n *\r\n * @param options.personaMeta Per-persona YAML as a plain record\r\n * @param options.sharedMeta Parsed `_shared.yaml` fields\r\n * @param options.agentMap Cross-suite agent name map (injected by build())\r\n * @param options.target The current build target (forwarded to plugins)\r\n * @param options.registry Target registry for context-flag injection\r\n * @param options.configVariables Optional global variables from BuildConfig.variables\r\n * (lowest precedence; overridden by all other layers)\r\n * @param options.suiteVariables Optional suite-level variables from SuiteConfig.variables\r\n * (overrides configVariables; overridden by sharedMeta and personaMeta)\r\n * @returns Merged rendering context\r\n */\r\ninterface BuildContextOptions {\r\n personaMeta: Record<string, unknown>;\r\n sharedMeta: Record<string, unknown>;\r\n agentMap?: Record<string, string>;\r\n target?: TargetType;\r\n registry?: TargetRegistry;\r\n configVariables?: Record<string, unknown>;\r\n suiteVariables?: Record<string, unknown>;\r\n}\r\n\r\nfunction buildContext(options: BuildContextOptions): Record<string, unknown> {\r\n const {\r\n personaMeta,\r\n sharedMeta,\r\n agentMap = {},\r\n target,\r\n registry,\r\n configVariables,\r\n suiteVariables,\r\n } = options;\r\n const version =\r\n typeof personaMeta['version'] === 'string'\r\n ? personaMeta['version']\r\n : typeof sharedMeta['default_version'] === 'string'\r\n ? sharedMeta['default_version']\r\n : '0.0.0';\r\n\r\n // Merge base: configVariables first (lowest priority), then suiteVariables,\r\n // then sharedMeta, then personaMeta (highest priority among YAML sources).\r\n const merged: Record<string, unknown> = {\r\n ...(configVariables ?? {}),\r\n ...(suiteVariables ?? {}),\r\n ...sharedMeta,\r\n ...personaMeta,\r\n version,\r\n };\r\n\r\n // ── Derived convenience fields (only set when not already provided) ───────\r\n // tools_list / tools_json — serialized from the `tools` array if present\r\n const tools = Array.isArray(merged['tools']) ? (merged['tools'] as string[]) : [];\r\n if (!('tools_list' in merged)) {\r\n merged['tools_list'] = serializeToolsList(tools);\r\n }\r\n if (!('tools_json' in merged)) {\r\n merged['tools_json'] = serializeTools(tools);\r\n }\r\n\r\n // cc_tools_list / cc_tools_json — from `cc_tools` or fall back to `tools`\r\n const ccTools = Array.isArray(merged['cc_tools']) ? (merged['cc_tools'] as string[]) : tools;\r\n if (!('cc_tools_list' in merged)) {\r\n merged['cc_tools_list'] = serializeToolsList(ccTools);\r\n }\r\n if (!('cc_tools_json' in merged)) {\r\n merged['cc_tools_json'] = serializeTools(ccTools);\r\n }\r\n\r\n // cc_file_name_stem — stem of cc_file_name (for default CC frontmatter template)\r\n if (!('cc_file_name_stem' in merged) && typeof merged['cc_file_name'] === 'string') {\r\n const ccFileName = merged['cc_file_name'] as string;\r\n merged['cc_file_name_stem'] = ccFileName.replace(/\\.md$/, '');\r\n }\r\n\r\n // da_file_name_stem — stem of da_file_name (for deep-agents output)\r\n if (!('da_file_name_stem' in merged) && typeof merged['da_file_name'] === 'string') {\r\n const daFileName = merged['da_file_name'] as string;\r\n merged['da_file_name_stem'] = daFileName.replace(/\\.md$/, '');\r\n }\r\n\r\n // da_tools_list / da_tools_json — from `da_tools` or fall back to `tools`\r\n // Intentionally gated on da_file_name: unlike cc_tools_list/cc_tools_json (always emitted for\r\n // every persona), da_* fields are absent when the persona has no deep-agents output file (AC-4).\r\n if (typeof merged['da_file_name'] === 'string') {\r\n const daTools = Array.isArray(merged['da_tools']) ? (merged['da_tools'] as string[]) : tools;\r\n if (!('da_tools_list' in merged)) {\r\n merged['da_tools_list'] = serializeToolsList(daTools);\r\n }\r\n if (!('da_tools_json' in merged)) {\r\n merged['da_tools_json'] = serializeTools(daTools);\r\n }\r\n }\r\n\r\n // ── Cross-suite agent name variables ──────────────────────────────────────\r\n for (const [key, value] of Object.entries(agentMap)) {\r\n if (!(key in merged)) {\r\n merged[key] = value;\r\n }\r\n }\r\n\r\n // ── Target flag injection ─────────────────────────────────────────────────\r\n if (target !== undefined) {\r\n if (registry && registry.has(target)) {\r\n // Inject all contextFlags declared in the target's registry definition.\r\n const flags = registry.get(target).contextFlags ?? {};\r\n for (const [key, value] of Object.entries(flags)) {\r\n merged[key] = value;\r\n }\r\n } else {\r\n // Fallback for targets not present in the registry.\r\n merged[`target_${target.replace(/-/g, '_')}`] = true;\r\n }\r\n }\r\n\r\n return merged;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// buildPersona — single persona × single target\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Validate that all slugs declared in a persona's `subagents` field exist\r\n * in the cross-suite agent map.\r\n *\r\n * The agent map contains keys in the form `agent_slug_{underscored_slug}`\r\n * (hyphens replaced by underscores). A declared slug is considered valid\r\n * when its corresponding key is present in the map.\r\n *\r\n * @param persona Typed persona metadata (may or may not have `subagents`)\r\n * @param agentMap Cross-suite agent name map built by `buildAgentNameMap()`\r\n * @returns `ValidationResult[]` — one error per unknown slug, or `[]`\r\n */\r\nfunction validateSubagentRefs(\r\n persona: PersonaMetadata,\r\n agentMap: Record<string, string>,\r\n): ValidationResult[] {\r\n const subagents = persona.subagents;\r\n if (!Array.isArray(subagents) || subagents.length === 0) return [];\r\n\r\n const results: ValidationResult[] = [];\r\n\r\n for (const slug of subagents) {\r\n const key = `agent_slug_${slug.replace(/-/g, '_')}`;\r\n if (!(key in agentMap)) {\r\n results.push({\r\n severity: 'error',\r\n message:\r\n `Persona '${persona.name}' declares subagent '${slug}' but no persona ` +\r\n `with that slug exists in any configured suite.`,\r\n });\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * Build a single persona for a single output target.\r\n *\r\n * Pipeline:\r\n * 1. Load sharedMeta + personaMeta (callers supply pre-loaded values)\r\n * 2. Build merged context\r\n * 3. Run onBuildContext plugin hooks (context accumulation)\r\n * 4. Run onPersonaPartials plugin hooks (shallow-copy partials map, persona-scoped)\r\n * 5. Render frontmatter template → render frontmatter\r\n * 6. Load content template\r\n * 7. Render body: partials → conditionals → variables → post-process\r\n * 8. Assemble final output (frontmatter + body)\r\n * 9. Run onPostRender plugin hooks (output chain)\r\n * 10. Run onValidate plugin hooks (validation collection)\r\n * 11. Determine output file path\r\n * 12. Write output file (unless check mode)\r\n * 13. Return BuildResult\r\n *\r\n * @param personaYamlPath Absolute path to the persona YAML source file\r\n * @param suiteName Identifier for the suite this persona belongs to\r\n * @param suiteConfig Suite configuration object. `suiteConfig.variables`\r\n * is forwarded to `buildContext()` as the\r\n * `suiteVariables` layer (layer 2 of 7) — these values\r\n * override any `config.variables` but are themselves\r\n * overridden by `_shared.yaml` and per-persona fields.\r\n * @param sharedMeta Pre-loaded `_shared.yaml` contents\r\n * @param partialsMap Pre-loaded partials map (shared + suite-local merged).\r\n * This map is **not** passed directly to rendering.\r\n * Instead, a shallow copy (`{ ...partialsMap }`) is\r\n * created at step 3b and passed to the `onPersonaPartials`\r\n * plugin hooks — the hooks' accumulated output map is what\r\n * reaches `resolvePartials`, not `partialsMap` itself.\r\n * This ensures that persona-level overrides or injections\r\n * do not leak back into the caller's reference or into\r\n * subsequent personas in the same suite.\r\n * @param config Top-level BuildConfig. `config.variables` is\r\n * forwarded to `buildContext()` as the\r\n * `configVariables` layer (layer 1 of 7, lowest\r\n * priority) — global defaults available to every\r\n * persona across all suites.\r\n * @param plugins Registered plugins\r\n * @param target Target output format\r\n * @param agentMap Pre-built cross-suite agent name map\r\n * @param registry Target registry to use. Defaults to `defaultRegistry`.\r\n * **Two-registry limitation:** If you pass a custom `TargetRegistry` only\r\n * to `build()` (via `config.targetRegistry`) and call `buildPersona()`\r\n * directly without also passing that registry here, your custom targets\r\n * will not be visible — `defaultRegistry` will be used instead. Either\r\n * pass the same registry instance explicitly, or call `build()` to have\r\n * the registry forwarded automatically.\r\n * @returns BuildResult for this persona × target combination\r\n */\r\nexport async function buildPersona(\r\n personaYamlPath: string,\r\n suiteName: string,\r\n suiteConfig: SuiteConfig,\r\n sharedMeta: Record<string, unknown>,\r\n partialsMap: Record<string, string>,\r\n config: BuildConfig,\r\n plugins: PersonaBuildPlugin[],\r\n target: string,\r\n agentMap: Record<string, string> = {},\r\n registry: TargetRegistry = defaultRegistry,\r\n): Promise<BuildResult> {\r\n // ── 1. Load persona metadata ──────────────────────────────────────────────\r\n const personaMeta = await loadPersonaYaml(personaYamlPath);\r\n\r\n // ── 2. Build merged context ───────────────────────────────────────────────\r\n let context = buildContext({\r\n personaMeta,\r\n sharedMeta,\r\n agentMap,\r\n target,\r\n registry,\r\n configVariables: config.variables,\r\n suiteVariables: suiteConfig.variables,\r\n });\r\n\r\n // ── 3. Plugin onBuildContext ──────────────────────────────────────────────\r\n // Cast context to PersonaMetadata for the plugin runner (it requires a\r\n // name field which is guaranteed by loadPersonaYaml above).\r\n const personaMetaTyped = personaMeta as PersonaMetadata;\r\n context = runBuildContext(plugins, context, personaMetaTyped, suiteConfig, target);\r\n\r\n // ── 4. Plugin onPersonaPartials ──────────────────────────────────────────────\r\n // Create a shallow copy of the suite-level partials map so that any\r\n // persona-level overrides or injections do not leak to other personas.\r\n // The copy is then mutated (accumulated) by onPersonaPartials plugins and\r\n // used exclusively for this persona's rendering pass.\r\n const personaPartialsMap = runPersonaPartials(\r\n plugins,\r\n { ...partialsMap },\r\n personaMetaTyped,\r\n context,\r\n suiteConfig,\r\n target,\r\n );\r\n\r\n // ── 5. Render frontmatter ─────────────────────────────────────────────────\r\n const fmTemplate = resolveFrontmatterTemplate(target, plugins, config.frontmatter, registry);\r\n const contentBasename = path.basename(personaYamlPath, '.yaml') + '.md';\r\n const frontmatter = renderFrontmatter(fmTemplate, context, contentBasename);\r\n\r\n // ── 6. Load content template ──────────────────────────────────────────────\r\n const contentSubdir = suiteConfig.contentSubdir ?? 'content';\r\n const contentPath = path.join(suiteConfig.srcDir, contentSubdir, contentBasename);\r\n const bodyTemplate = normalizeNewlines(await readFile(contentPath, 'utf8'));\r\n\r\n // ── 7. Render body ────────────────────────────────────────────────────────\r\n let body = resolvePartials(bodyTemplate, personaPartialsMap);\r\n body = resolveConditionals(body, context);\r\n body = resolveVariables(body, context, contentBasename);\r\n body = collapseBlankLines(body);\r\n body = ensureBlankLineBeforeHeadings(body);\r\n body = body.trimEnd();\r\n\r\n // ── 8. Assemble output ────────────────────────────────────────────────────\r\n let output = normalizeNewlines(`${frontmatter}\\n\\n${body}\\n`);\r\n\r\n // ── 9. Plugin onPostRender ────────────────────────────────────────────────\r\n output = runPostRender(plugins, output, personaMetaTyped, target);\r\n\r\n // ── 10. Plugin onValidate + subagent ref validation ─────────────────────\r\n const validationResults: ValidationResult[] = [\r\n ...runValidate(plugins, personaMetaTyped, suiteConfig, target),\r\n ...validateSubagentRefs(personaMetaTyped, agentMap),\r\n ];\r\n\r\n // ── 11. Determine output file path ────────────────────────────────────────\r\n // Resolve the registry definition once — used for both outputDirKey (map\r\n // lookup) and filenameContextKey (output filename override).\r\n const def = registry.has(target) ? registry.get(target) : undefined;\r\n const outputDir = resolveOutputDir(target, suiteConfig, def);\r\n // Use the filename context key declared in the target's registry definition,\r\n // falling back to the content basename when absent or unset in context.\r\n const fnKey = def?.filenameContextKey;\r\n const outputBasename =\r\n fnKey && typeof context[fnKey] === 'string'\r\n ? (context[fnKey] as string)\r\n : contentBasename;\r\n const outputPath = path.join(outputDir, outputBasename);\r\n\r\n // ── 12. Write (unless check mode) ─────────────────────────────────────────\r\n const check = config.check ?? false;\r\n let written = false;\r\n\r\n if (!check) {\r\n await mkdir(outputDir, { recursive: true });\r\n await writeFile(outputPath, output, 'utf8');\r\n written = true;\r\n }\r\n\r\n return {\r\n suite: suiteName,\r\n target,\r\n personaYamlPath,\r\n outputPath,\r\n content: output,\r\n validationResults,\r\n written,\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// buildSuite — all personas in one suite × one target\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Build all personas in a suite for a single output target.\r\n *\r\n * Pipeline:\r\n * 1. Load `_shared.yaml` for the suite\r\n * 2. Load merged partials (config.partials → shared → suite-local)\r\n * 3. Run `onSuiteInit` on all plugins\r\n * 4. Run `onPartials` on all plugins (highest priority: may override any file-based partial)\r\n * 5. Discover all persona YAML files\r\n * 6. Call `buildPersona()` for each\r\n *\r\n * @param suiteName Identifier for this suite\r\n * @param suiteConfig Suite configuration\r\n * @param config Top-level BuildConfig\r\n * @param plugins Registered plugins\r\n * @param target Target output format\r\n * @param agentMap Pre-built cross-suite agent name map\r\n * @param registry Target registry to use. Defaults to `defaultRegistry`.\r\n * **Two-registry limitation:** If you pass a custom `TargetRegistry` only\r\n * to `build()` (via `config.targetRegistry`) and call `buildSuite()`\r\n * directly without also passing that registry here, your custom targets\r\n * will not be visible — `defaultRegistry` will be used instead. Either\r\n * pass the same registry instance explicitly, or call `build()` to have\r\n * the registry forwarded automatically.\r\n * @returns Array of BuildResult objects, one per persona\r\n */\r\nexport async function buildSuite(\r\n suiteName: string,\r\n suiteConfig: SuiteConfig,\r\n config: BuildConfig,\r\n plugins: PersonaBuildPlugin[],\r\n target: string,\r\n agentMap: Record<string, string> = {},\r\n registry: TargetRegistry = defaultRegistry,\r\n): Promise<BuildResult[]> {\r\n // ── 1. Load shared metadata ───────────────────────────────────────────────\r\n const metaSubdir = suiteConfig.metaSubdir ?? 'meta';\r\n const sharedYamlPath = path.join(suiteConfig.srcDir, metaSubdir, '_shared.yaml');\r\n const sharedMeta = await loadRawYaml(sharedYamlPath);\r\n\r\n // ── 2. Load partials (three-layer: config.partials → shared → suite-local) ─\r\n // Start with config.partials as the lowest-priority base layer.\r\n let partialsMap: Record<string, string> = { ...(config.partials ?? {}) };\r\n\r\n if (config.sharedPartialsDir && existsSync(config.sharedPartialsDir)) {\r\n partialsMap = { ...partialsMap, ...(await loadPartials(config.sharedPartialsDir)) };\r\n }\r\n\r\n const partialsSubdir = suiteConfig.partialsSubdir ?? 'partials';\r\n const suitePartialsDir = path.join(suiteConfig.srcDir, partialsSubdir);\r\n if (existsSync(suitePartialsDir)) {\r\n partialsMap = { ...partialsMap, ...(await loadPartials(suitePartialsDir)) };\r\n }\r\n\r\n // ── 3. Plugin onSuiteInit ─────────────────────────────────────────────────\r\n runSuiteInit(plugins, suiteConfig, sharedMeta);\r\n\r\n // ── 4. Plugin onPartials ────────────────────────────────────────────────────\r\n // Invoked after onSuiteInit and after all file-based partials are loaded.\r\n // Plugins may inject new partials or override any file-based entry.\r\n partialsMap = runPartials(plugins, partialsMap, suiteName, suiteConfig);\r\n\r\n // ── 5. Discover persona YAML files ───────────────────────────────────────\r\n const personaYamlPaths = await discoverSuitePersonaYamls(suiteConfig);\r\n\r\n // ── 6. Build each persona ────────────────────────────────────────────────────\r\n const results: BuildResult[] = [];\r\n for (const yamlPath of personaYamlPaths) {\r\n const result = await buildPersona(\r\n yamlPath,\r\n suiteName,\r\n suiteConfig,\r\n sharedMeta,\r\n partialsMap,\r\n config,\r\n plugins,\r\n target,\r\n agentMap,\r\n registry,\r\n );\r\n results.push(result);\r\n }\r\n\r\n return results;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// build — top-level entry point\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Top-level build orchestrator.\r\n *\r\n * Iterates all `config.suites × config.targets` combinations, calls\r\n * `buildSuite()` for each, and aggregates the results into a `BuildSummary`.\r\n *\r\n * Modes:\r\n * - Normal: renders and writes all personas.\r\n * - `check: true`: renders without writing; useful for CI staleness checks.\r\n * - `strict: true`: throws when any ValidationResult has severity `'error'`\r\n * or `'warning'`. All suites are processed before the throw, so output\r\n * files **will** be written to disk even when the build ultimately fails.\r\n * **For CI usage, combine `strict: true` with `check: true`** to avoid\r\n * leaving partial artefacts on disk when validation fails.\r\n *\r\n * @param config Typed build configuration\r\n * @returns Aggregated BuildSummary\r\n * @throws `Error` when `strict: true` and validation failures exist\r\n */\r\nexport async function build(config: BuildConfig): Promise<BuildSummary> {\r\n const plugins = config.plugins ?? [];\r\n const registry = config.targetRegistry ?? defaultRegistry;\r\n // When a custom registry is supplied and targets are not explicit, build all\r\n // registered targets. When using the default registry without an explicit\r\n // targets list, build only targets with defaultEnabled !== false. This\r\n // preserves the historical two-target default (vscode + claude-code) for\r\n // existing suite configs that do not configure deep-agents output.\r\n const targets = config.targets ?? registry.names().filter(n => registry.get(n).defaultEnabled !== false);\r\n const allResults: BuildResult[] = [];\r\n\r\n // Pre-scan: build cross-suite agent name map\r\n const agentMap = await buildAgentNameMap(config);\r\n\r\n for (const [suiteName, suiteConfig] of Object.entries(config.suites)) {\r\n for (const target of targets) {\r\n const suiteResults = await buildSuite(suiteName, suiteConfig, config, plugins, target, agentMap, registry);\r\n allResults.push(...suiteResults);\r\n }\r\n }\r\n\r\n // Collect strict failures (error + warning severity)\r\n const strictFailures: ValidationResult[] = config.strict\r\n ? allResults.flatMap((r) =>\r\n r.validationResults.filter(\r\n (v) => v.severity === 'error' || v.severity === 'warning',\r\n ),\r\n )\r\n : [];\r\n\r\n const success = !config.strict || strictFailures.length === 0;\r\n\r\n const summary: BuildSummary = {\r\n success,\r\n results: allResults,\r\n strictFailures,\r\n totalBuilt: allResults.length,\r\n totalWritten: allResults.filter((r) => r.written).length,\r\n };\r\n\r\n if (config.strict && !success) {\r\n const messages = strictFailures.map((f) => `[${f.severity}] ${f.message}`).join('\\n');\r\n throw new Error(\r\n `Build failed in strict mode — ${strictFailures.length} validation issue(s):\\n${messages}`,\r\n );\r\n }\r\n\r\n return summary;\r\n}\r\n","/**\r\n * src/validators/filename-validator.ts\r\n *\r\n * Validates persona output filenames against the project naming convention.\r\n *\r\n * Convention: kebab-case only — lowercase letters, digits, and hyphens.\r\n * No spaces, no uppercase letters, no special characters other than hyphens\r\n * and dots (for the file extension).\r\n *\r\n * This is a pure function: no file I/O, no process.exit, no side effects.\r\n * It depends only on `ValidationResult` from `src/plugins/types.ts`.\r\n */\r\n\r\nimport type { ValidationResult } from '../plugins/types.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// Validation rule definitions\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface FilenameRule {\r\n /** Human-readable description of the rule (used in error messages) */\r\n description: string;\r\n /** Returns true when the filename is *invalid* (i.e. the rule is violated) */\r\n violated: (basename: string) => boolean;\r\n /** Message factory — receives the offending basename */\r\n message: (basename: string) => string;\r\n}\r\n\r\nconst FILENAME_RULES: FilenameRule[] = [\r\n {\r\n description: 'no uppercase letters',\r\n violated: (name) => /[A-Z]/.test(name),\r\n message: (name) =>\r\n `Filename \"${name}\" contains uppercase letters. Use lowercase kebab-case (e.g. \"my-persona.md\").`,\r\n },\r\n {\r\n description: 'no spaces',\r\n violated: (name) => /\\s/.test(name),\r\n message: (name) =>\r\n `Filename \"${name}\" contains spaces. Use hyphens to separate words (e.g. \"my-persona.md\").`,\r\n },\r\n {\r\n description: 'kebab-case characters only',\r\n violated: (name) => {\r\n // A valid filename consists of one or more dot-separated segments.\r\n // Each segment must be a non-empty kebab-case token:\r\n // - starts and ends with a lowercase letter or digit\r\n // - may contain hyphens, but not consecutive hyphens\r\n // Examples of valid names: \"my-persona.md\", \"1-developer.agent.md\"\r\n // Examples of invalid names: \"My_Persona.md\", \"--bad.md\", \"foo..bar.md\"\r\n const segments = name.split('.');\r\n if (segments.length === 1) {\r\n // No extension — treat the whole name as a kebab stem\r\n return !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name);\r\n }\r\n // All segments (stem + extension parts) must be valid kebab tokens\r\n return !segments.every((seg) => /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(seg));\r\n },\r\n message: (name) =>\r\n `Filename \"${name}\" does not conform to kebab-case naming. ` +\r\n `Use lowercase letters, digits, and hyphens only (e.g. \"my-persona.md\").`,\r\n },\r\n];\r\n\r\n// ---------------------------------------------------------------------------\r\n// Public API\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Validate a persona filename against the project naming convention.\r\n *\r\n * Accepts either a bare filename (`my-persona.md`) or a full/relative path\r\n * — only the basename (last path segment) is evaluated.\r\n *\r\n * @param filePath Filename or path to validate (only the basename is checked)\r\n * @returns Empty array when the filename conforms; one ValidationResult\r\n * per violated rule otherwise. Each result has severity \"error\".\r\n *\r\n * @example\r\n * validateFileName('my-persona.md'); // []\r\n * validateFileName('My Persona.md'); // [{severity:'error', message:'...'}]\r\n * validateFileName('/abs/path/my-persona.md');// []\r\n */\r\nexport function validateFileName(filePath: string): ValidationResult[] {\r\n const basename = filePath.includes('/')\r\n ? filePath.split('/').pop() ?? filePath\r\n : filePath.includes('\\\\')\r\n ? filePath.split('\\\\').pop() ?? filePath\r\n : filePath;\r\n\r\n const errors: ValidationResult[] = [];\r\n\r\n for (const rule of FILENAME_RULES) {\r\n if (rule.violated(basename)) {\r\n errors.push({\r\n severity: 'error',\r\n message: rule.message(basename),\r\n });\r\n }\r\n }\r\n\r\n return errors;\r\n}\r\n","/**\r\n * src/validators/strict-validator.ts\r\n *\r\n * Validates that a set of required marker strings are present in a rendered\r\n * persona output string.\r\n *\r\n * \"Strict\" mode in the build pipeline guards against incomplete renders —\r\n * e.g. a required section marker (e.g. \"{{ROLE}}\") that was never resolved.\r\n * This validator generalises that concept: callers supply the list of marker\r\n * strings that *must* appear in the final rendered content.\r\n *\r\n * This is a pure function: no file I/O, no side effects.\r\n * It depends only on `ValidationResult` from `src/plugins/types.ts`.\r\n */\r\n\r\nimport type { ValidationResult } from '../plugins/types.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// Public API\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Validate that every required marker string is present in the rendered output.\r\n *\r\n * Each absent marker produces one `ValidationResult` entry with severity\r\n * `\"error\"` and a descriptive message identifying the missing marker.\r\n *\r\n * @param renderedContent The final rendered output string to inspect\r\n * @param requiredMarkers Array of marker strings that must appear verbatim in\r\n * `renderedContent`. An empty array always returns `[]`.\r\n * @returns Empty array when all markers are found; one entry per\r\n * absent marker otherwise. Each entry has severity \"error\".\r\n *\r\n * @example\r\n * validateStrictMarkers('Hello world', ['Hello', 'world']); // []\r\n * validateStrictMarkers('Hello world', ['{{MISSING}}']);\r\n * // [{severity:'error', message:'Required marker \"{{MISSING}}\" is missing from the rendered output.'}]\r\n */\r\nexport function validateStrictMarkers(\r\n renderedContent: string,\r\n requiredMarkers: string[],\r\n): ValidationResult[] {\r\n const errors: ValidationResult[] = [];\r\n\r\n for (const marker of requiredMarkers) {\r\n if (!renderedContent.includes(marker)) {\r\n errors.push({\r\n severity: 'error',\r\n message: `Required marker \"${marker}\" is missing from the rendered output.`,\r\n });\r\n }\r\n }\r\n\r\n return errors;\r\n}\r\n","/**\r\n * src/utils/regex.ts\r\n *\r\n * Shared regex utilities.\r\n *\r\n * Pure functions — no I/O, no side effects.\r\n */\r\n\r\n/**\r\n * Escape a string for safe use inside a `new RegExp(...)` constructor.\r\n * Escapes all regex special characters.\r\n *\r\n * @param str Raw string to escape\r\n * @returns String with all special regex characters escaped\r\n *\r\n * @example\r\n * escapeRegExp('tool.name+extra')\r\n * // => 'tool\\\\.name\\\\+extra'\r\n *\r\n * new RegExp(`\\\\|\\\\s*\\`${escapeRegExp(toolName)}\\`\\\\s*\\\\|`)\r\n * // Safe regex that matches | `<toolName>` | in rendered Markdown tables\r\n */\r\nexport function escapeRegExp(str: string): string {\r\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n}\r\n","/**\r\n * @mistralys/persona-builder\r\n *\r\n * Public API barrel export.\r\n */\r\n\r\nimport { createRequire } from 'node:module';\r\n\r\nexport * from './engine/index.js';\r\nexport * from './loaders/index.js';\r\nexport * from './plugins/index.js';\r\nexport * from './builders/index.js';\r\nexport * from './validators/index.js';\r\nexport * from './utils/index.js';\r\nexport * from './targets/index.js';\r\n\r\n/** Package version — sourced from package.json (single source of truth). */\r\nconst _pkgRequire = createRequire(import.meta.url);\r\nexport const VERSION = (_pkgRequire('../package.json') as { version: string }).version;\r\n"]}
package/dist/index.d.cts CHANGED
@@ -256,6 +256,14 @@ interface PersonaMetadata {
256
256
  version?: string;
257
257
  /** Ordered list of tool identifiers */
258
258
  tools?: string[];
259
+ /**
260
+ * Optional list of persona slugs this persona delegates to as sub-agents.
261
+ *
262
+ * Each entry is a kebab-case slug referencing another persona in any
263
+ * configured suite. Validated during build against the cross-suite agent
264
+ * map — an unknown slug produces an error-severity `ValidationResult`.
265
+ */
266
+ subagents?: string[];
259
267
  /** Free-form context variables available during template rendering */
260
268
  [key: string]: unknown;
261
269
  }
package/dist/index.d.ts CHANGED
@@ -256,6 +256,14 @@ interface PersonaMetadata {
256
256
  version?: string;
257
257
  /** Ordered list of tool identifiers */
258
258
  tools?: string[];
259
+ /**
260
+ * Optional list of persona slugs this persona delegates to as sub-agents.
261
+ *
262
+ * Each entry is a kebab-case slug referencing another persona in any
263
+ * configured suite. Validated during build against the cross-suite agent
264
+ * map — an unknown slug produces an error-severity `ValidationResult`.
265
+ */
266
+ subagents?: string[];
259
267
  /** Free-form context variables available during template rendering */
260
268
  [key: string]: unknown;
261
269
  }