{"version":3,"file":"loader.mjs","names":[],"sources":["../../src/template/loader.ts"],"sourcesContent":["/**\n * Liquid-based template engine for live compaction prompts.\n *\n * Shared Liquid mechanics (engine defaults, filters, `{% xml %}`, and\n * template loading) live in `pi-template-kit`. This module keeps only\n * live-compaction-specific frontmatter normalization, error type,\n * and render-variable builders.\n *\n * File layout convention (per scope; project overrides global):\n *\n *   extensions/live-compaction/\n *     compaction-prompt.md           required template\n *     branch-summary-prompt.md       optional template\n *     templates/                     optional partials, layouts, etc.\n *\n * Partials and layouts resolve from <templateDir>/templates/ first, then\n * <templateDir>/. Use `{% include 'name' %}` and `{% layout 'name' %}` —\n * Liquid does the rest.\n */\n\nimport {\n\tDEFAULT_BRANCH_SUMMARY_TEMPLATE_BODY,\n\tDEFAULT_COMPACTION_TEMPLATE_BODY,\n\tnormalizeThinkingLevel,\n} from '@live-compaction/config';\nimport type {\n\tBranchSummaryRenderVars,\n\tCompactionRenderVars,\n\tCompactionTemplateFrontmatter,\n} from '@live-compaction/template/types';\nimport {\n\ttype LoadedTemplate,\n\tloadTemplate,\n\tloadTemplateFromString,\n\tTemplateKitError,\n} from 'pi-template-kit/template';\n\nexport type {\n\tBranchSummaryRenderVars,\n\tCompactionRenderVars,\n\tCompactionTemplateFrontmatter,\n} from '@live-compaction/template/types';\n\n// ---------------------------------------------------------------------------\n// Frontmatter parser\n// ---------------------------------------------------------------------------\n\nfunction parseFrontmatter(raw: unknown): CompactionTemplateFrontmatter {\n\tif (!raw || typeof raw !== 'object') {\n\t\treturn { extra: {} };\n\t}\n\tconst data = raw as Record<string, unknown>;\n\tconst fm: CompactionTemplateFrontmatter = { extra: {} };\n\n\tif (typeof data.preset === 'string' && data.preset.trim()) {\n\t\tfm.preset = data.preset.trim();\n\t}\n\tif (data.thinkingLevel !== undefined || data.thinking_level !== undefined) {\n\t\tconst candidate = data.thinkingLevel ?? data.thinking_level;\n\t\tconst normalized = normalizeThinkingLevel(candidate);\n\t\tif (normalized) fm.thinkingLevel = normalized;\n\t}\n\tif (typeof data.model === 'string' && data.model.trim()) {\n\t\tfm.model = data.model.trim();\n\t}\n\tif (typeof data.description === 'string') {\n\t\tfm.description = data.description;\n\t}\n\n\tfor (const [k, v] of Object.entries(data)) {\n\t\tif (\n\t\t\tk === 'preset' ||\n\t\t\tk === 'thinkingLevel' ||\n\t\t\tk === 'thinking_level' ||\n\t\t\tk === 'model' ||\n\t\t\tk === 'description'\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tfm.extra[k] = v;\n\t}\n\n\treturn fm;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Compiled liquid template. Render accepts any record because the same\n * engine is reused for compaction and branch-summary, which expose\n * different variable shapes. Specific call sites use\n * `buildRenderVars` / `buildBranchSummaryRenderVars` for type-safety.\n */\nexport interface CompactionTemplate {\n\ttemplatePath: string;\n\tfrontmatter: CompactionTemplateFrontmatter;\n\trender(vars: Record<string, unknown> | CompactionRenderVars | BranchSummaryRenderVars): string;\n}\n\nexport class CompactionTemplateError extends Error {\n\t// Explicit field declaration instead of a parameter property\n\t// (`constructor(public readonly x)`) so the file stays loadable under\n\t// Node's strip-mode TypeScript loader — which is what pi runs under\n\t// by default (no `--import tsx`). Strip mode rejects parameter\n\t// properties at parse time.\n\treadonly templatePath: string;\n\n\tconstructor(message: string, templatePath: string) {\n\t\tsuper(message);\n\t\tthis.name = 'CompactionTemplateError';\n\t\tthis.templatePath = templatePath;\n\t}\n}\n\nfunction asCompactionTemplate(loaded: LoadedTemplate<Record<string, unknown>>): CompactionTemplate {\n\treturn {\n\t\ttemplatePath: loaded.templatePath,\n\t\tfrontmatter: loaded.frontmatter as unknown as CompactionTemplateFrontmatter,\n\t\trender(vars: Record<string, unknown> | CompactionRenderVars | BranchSummaryRenderVars): string {\n\t\t\ttry {\n\t\t\t\treturn loaded.render(vars as Record<string, unknown>);\n\t\t\t} catch (error) {\n\t\t\t\tthrow toCompactionTemplateError(error, loaded.templatePath);\n\t\t\t}\n\t\t},\n\t};\n}\n\nfunction toCompactionTemplateError(\n\terror: unknown,\n\tfallbackTemplatePath: string,\n): CompactionTemplateError {\n\tif (error instanceof CompactionTemplateError) return error;\n\tconst templatePath =\n\t\terror instanceof TemplateKitError ? error.templatePath : fallbackTemplatePath;\n\tconst message = error instanceof Error ? error.message : String(error);\n\treturn new CompactionTemplateError(message, templatePath);\n}\n\nexport async function loadCompactionTemplate(\n\ttemplatePath: string,\n): Promise<CompactionTemplate | null> {\n\ttry {\n\t\treturn asCompactionTemplate(\n\t\t\tawait loadTemplate(templatePath, {\n\t\t\t\tparseFrontmatter: (raw) => parseFrontmatter(raw) as unknown as Record<string, unknown>,\n\t\t\t}),\n\t\t);\n\t} catch (error) {\n\t\tconst code = (error as { code?: string }).code;\n\t\tif (code === 'ENOENT') return null;\n\t\tthrow toCompactionTemplateError(error, templatePath);\n\t}\n}\n\n/**\n * Compile a template from an in-memory string instead of reading a file.\n *\n * Used by the built-in fallback templates so the runtime renders them\n * through exactly the same engine as on-disk ones. `templatePath` is\n * still required so error messages can name the source, and `templateDir`\n * controls partial / layout resolution — pass a real directory if you\n * want sibling partials to resolve, or a synthetic path (no directory\n * read) otherwise.\n */\nexport function loadCompactionTemplateFromString(\n\tbody: string,\n\toptions: { templatePath: string; templateDir: string },\n): CompactionTemplate {\n\ttry {\n\t\treturn asCompactionTemplate(\n\t\t\tloadTemplateFromString(body, {\n\t\t\t\t...options,\n\t\t\t\tparseFrontmatter: (raw) => parseFrontmatter(raw) as unknown as Record<string, unknown>,\n\t\t\t}),\n\t\t);\n\t} catch (error) {\n\t\tthrow toCompactionTemplateError(error, options.templatePath);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Built-in template singletons (lazy, reused across calls)\n// ---------------------------------------------------------------------------\n\nlet builtInCompactionTemplate: CompactionTemplate | null = null;\n\n/**\n * Built-in compaction template compiled once and reused for every\n * fallback render. The body lives in config/prompts.ts so it can also be\n * inspected by tests and the preview CLI without going through this module.\n */\nexport function getBuiltInCompactionTemplate(): CompactionTemplate {\n\tif (!builtInCompactionTemplate) {\n\t\tbuiltInCompactionTemplate = loadCompactionTemplateFromString(DEFAULT_COMPACTION_TEMPLATE_BODY, {\n\t\t\ttemplatePath: '<built-in compaction template>',\n\t\t\ttemplateDir: '/',\n\t\t});\n\t}\n\treturn builtInCompactionTemplate;\n}\n\nlet builtInBranchSummaryTemplate: CompactionTemplate | null = null;\n\nexport function getBuiltInBranchSummaryTemplate(): CompactionTemplate {\n\tif (!builtInBranchSummaryTemplate) {\n\t\tbuiltInBranchSummaryTemplate = loadCompactionTemplateFromString(\n\t\t\tDEFAULT_BRANCH_SUMMARY_TEMPLATE_BODY,\n\t\t\t{\n\t\t\t\ttemplatePath: '<built-in branch summary template>',\n\t\t\t\ttemplateDir: '/',\n\t\t\t},\n\t\t);\n\t}\n\treturn builtInBranchSummaryTemplate;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA+CA,SAAS,iBAAiB,KAA6C;CACtE,IAAI,CAAC,OAAO,OAAO,QAAQ,UAC1B,OAAO,EAAE,OAAO,CAAC,EAAE;CAEpB,MAAM,OAAO;CACb,MAAM,KAAoC,EAAE,OAAO,CAAC,EAAE;CAEtD,IAAI,OAAO,KAAK,WAAW,YAAY,KAAK,OAAO,KAAK,GACvD,GAAG,SAAS,KAAK,OAAO,KAAK;CAE9B,IAAI,KAAK,kBAAkB,KAAA,KAAa,KAAK,mBAAmB,KAAA,GAAW;EAE1E,MAAM,aAAa,uBADD,KAAK,iBAAiB,KAAK,cACM;EACnD,IAAI,YAAY,GAAG,gBAAgB;CACpC;CACA,IAAI,OAAO,KAAK,UAAU,YAAY,KAAK,MAAM,KAAK,GACrD,GAAG,QAAQ,KAAK,MAAM,KAAK;CAE5B,IAAI,OAAO,KAAK,gBAAgB,UAC/B,GAAG,cAAc,KAAK;CAGvB,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,IAAI,GAAG;EAC1C,IACC,MAAM,YACN,MAAM,mBACN,MAAM,oBACN,MAAM,WACN,MAAM,eAEN;EAED,GAAG,MAAM,KAAK;CACf;CAEA,OAAO;AACR;AAkBA,IAAa,0BAAb,cAA6C,MAAM;CAMlD;CAEA,YAAY,SAAiB,cAAsB;EAClD,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,eAAe;CACrB;AACD;AAEA,SAAS,qBAAqB,QAAqE;CAClG,OAAO;EACN,cAAc,OAAO;EACrB,aAAa,OAAO;EACpB,OAAO,MAAwF;GAC9F,IAAI;IACH,OAAO,OAAO,OAAO,IAA+B;GACrD,SAAS,OAAO;IACf,MAAM,0BAA0B,OAAO,OAAO,YAAY;GAC3D;EACD;CACD;AACD;AAEA,SAAS,0BACR,OACA,sBAC0B;CAC1B,IAAI,iBAAiB,yBAAyB,OAAO;CACrD,MAAM,eACL,iBAAiB,mBAAmB,MAAM,eAAe;CAE1D,OAAO,IAAI,wBADK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACzB,YAAY;AACzD;AAEA,eAAsB,uBACrB,cACqC;CACrC,IAAI;EACH,OAAO,qBACN,MAAM,aAAa,cAAc,EAChC,mBAAmB,QAAQ,iBAAiB,GAAG,EAChD,CAAC,CACF;CACD,SAAS,OAAO;EAEf,IADc,MAA4B,SAC7B,UAAU,OAAO;EAC9B,MAAM,0BAA0B,OAAO,YAAY;CACpD;AACD;;;;;;;;;;;AAYA,SAAgB,iCACf,MACA,SACqB;CACrB,IAAI;EACH,OAAO,qBACN,uBAAuB,MAAM;GAC5B,GAAG;GACH,mBAAmB,QAAQ,iBAAiB,GAAG;EAChD,CAAC,CACF;CACD,SAAS,OAAO;EACf,MAAM,0BAA0B,OAAO,QAAQ,YAAY;CAC5D;AACD;AAMA,IAAI,4BAAuD;;;;;;AAO3D,SAAgB,+BAAmD;CAClE,IAAI,CAAC,2BACJ,4BAA4B,iCAAiC,kCAAkC;EAC9F,cAAc;EACd,aAAa;CACd,CAAC;CAEF,OAAO;AACR;AAEA,IAAI,+BAA0D;AAE9D,SAAgB,kCAAsD;CACrE,IAAI,CAAC,8BACJ,+BAA+B,iCAC9B,sCACA;EACC,cAAc;EACd,aAAa;CACd,CACD;CAED,OAAO;AACR"}