* Generate one on-demand section file (` /sections/ .md.tmpl` → * ` .md`). Sections are BODY FRAGMENTS — no frontmatter, no catalog trim, * no voice triggers. They resolve placeholders through the SAME path as * SKILL.md (resolvePlaceholders) using the PARENT skill's TemplateConte
( sectionTmplPath: string, skillDir: string, host: Host = 'claude', )
| 883 | * applyHostRewrites so cross-references resolve per host. |
| 884 | */ |
| 885 | function processSectionTemplate( |
| 886 | sectionTmplPath: string, |
| 887 | skillDir: string, |
| 888 | host: Host = 'claude', |
| 889 | ): { outputPath: string; content: string } { |
| 890 | const tmplContent = fs.readFileSync(sectionTmplPath, 'utf-8'); |
| 891 | const relTmplPath = path.relative(ROOT, sectionTmplPath); |
| 892 | const hostConfig = getHostConfig(host); |
| 893 | |
| 894 | // Read the owning SKILL.md.tmpl so the section inherits the parent's name + |
| 895 | // tier + benefits-from (TemplateContext parity). Fall back to the dir name. |
| 896 | const parentTmplPath = path.join(ROOT, skillDir, 'SKILL.md.tmpl'); |
| 897 | const parentContent = fs.existsSync(parentTmplPath) ? fs.readFileSync(parentTmplPath, 'utf-8') : ''; |
| 898 | const parentName = (parentContent && extractNameAndDescription(parentContent).name) || skillDir; |
| 899 | const ctx = buildContext(parentContent || tmplContent, parentTmplPath, host, parentName); |
| 900 | |
| 901 | // Resolve placeholders against the section body (shared guard catches stragglers). |
| 902 | let content = resolvePlaceholders(tmplContent, ctx, hostConfig, relTmplPath); |
| 903 | |
| 904 | // External hosts: rewrite cross-reference paths/tools (no frontmatter to transform). |
| 905 | if (host !== 'claude') { |
| 906 | content = applyHostRewrites(content, hostConfig); |
| 907 | } else { |
| 908 | // --out-dir: a section may cross-reference another section by absolute path; |
| 909 | // repoint those to the out-dir too (no-op when --out-dir is unset). |
| 910 | content = rewriteSectionBase(content); |
| 911 | } |
| 912 | |
| 913 | // Plain generated header (no frontmatter to insert after). |
| 914 | content = GENERATED_HEADER.replace('{{SOURCE}}', path.basename(sectionTmplPath)) + content; |
| 915 | |
| 916 | const fileName = path.basename(sectionTmplPath).replace(/\.tmpl$/, ''); |
| 917 | let outputPath: string; |
| 918 | if (host === 'claude') { |
| 919 | outputPath = path.join(OUT_DIR || ROOT, skillDir, 'sections', fileName); |
| 920 | } else { |
| 921 | const externalName = externalSkillName(skillDir, parentName); |
| 922 | outputPath = path.join(ROOT, hostConfig.hostSubdir, 'skills', externalName, 'sections', fileName); |
| 923 | } |
| 924 | if (!DRY_RUN) fs.mkdirSync(path.dirname(outputPath), { recursive: true }); |
| 925 | return { outputPath, content }; |
| 926 | } |
| 927 | |
| 928 | // ─── Main ─────────────────────────────────────────────────── |
| 929 |
no test coverage detected