MCPcopy Index your code
hub / github.com/garrytan/gstack / processTemplate

Function processTemplate

scripts/gen-skill-docs.ts:798–870  ·  view source on GitHub ↗
(tmplPath: string, host: Host = 'claude')

Source from the content-addressed store, hash-verified

796}
797
798function processTemplate(tmplPath: string, host: Host = 'claude'): { outputPath: string; content: string; symlinkLoop?: boolean; catalogParts?: CatalogParts | null } {
799 const tmplContent = fs.readFileSync(tmplPath, 'utf-8');
800 const relTmplPath = path.relative(ROOT, tmplPath);
801 let outputPath = tmplPath.replace(/\.tmpl$/, '');
802
803 // Determine skill directory relative to ROOT
804 const skillDir = path.relative(ROOT, path.dirname(tmplPath));
805
806 // --out-dir (Claude only): mirror the skill tree into the out-dir instead of
807 // writing in place. External hosts compute their own paths below.
808 if (OUT_DIR && host === 'claude') {
809 outputPath = path.join(OUT_DIR, skillDir, path.basename(tmplPath).replace(/\.tmpl$/, ''));
810 }
811
812 // Extract name/description: name drives external skill naming + setup symlinks
813 // (and TemplateContext.skillName via buildContext); description feeds external
814 // host metadata. When frontmatter name: differs from directory name (e.g.
815 // run-tests/ with name: test), the frontmatter name wins.
816 const { name: extractedName, description: extractedDescription } = extractNameAndDescription(tmplContent);
817
818 const currentHostConfig = getHostConfig(host);
819 const ctx = buildContext(tmplContent, tmplPath, host);
820 const skillName = ctx.skillName;
821
822 // Replace placeholders + assert none remain (shared path with section generation).
823 let content = resolvePlaceholders(tmplContent, ctx, currentHostConfig, relTmplPath);
824
825 // Preprocess voice triggers: fold into description, strip field from frontmatter.
826 // Must run BEFORE transformFrontmatter so all hosts see the updated description,
827 // and BEFORE extractedDescription is used by external host metadata.
828 content = processVoiceTriggers(content);
829
830 // Re-extract description AFTER voice trigger preprocessing so Codex openai.yaml
831 // metadata gets the updated description with voice triggers included.
832 const postProcessDescription = extractNameAndDescription(content).description;
833
834 // For Claude: strip sensitive: field (only Factory uses it)
835 // For external hosts: route output, transform frontmatter, rewrite paths
836 let symlinkLoop = false;
837 if (host === 'claude') {
838 content = transformFrontmatter(content, host);
839 } else {
840 const result = processExternalHost(content, tmplContent, host, skillDir, postProcessDescription, ctx, extractedName || undefined);
841 content = result.content;
842 outputPath = result.outputPath;
843 symlinkLoop = result.symlinkLoop;
844 }
845
846 // Prepend generated header (after frontmatter)
847 const header = GENERATED_HEADER.replace('{{SOURCE}}', path.basename(tmplPath));
848 const fmEnd = content.indexOf('---', content.indexOf('---') + 3);
849 if (fmEnd !== -1) {
850 const insertAt = content.indexOf('\n', fmEnd) + 1;
851 content = content.slice(0, insertAt) + header + content.slice(insertAt);
852 } else {
853 content = header + content;
854 }
855

Callers 1

gen-skill-docs.tsFile · 0.85

Calls 9

getHostConfigFunction · 0.90
buildContextFunction · 0.85
resolvePlaceholdersFunction · 0.85
processVoiceTriggersFunction · 0.85
processExternalHostFunction · 0.85
applyCatalogTrimFunction · 0.85
rewriteSectionBaseFunction · 0.85
transformFrontmatterFunction · 0.70

Tested by

no test coverage detected