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

Function processExternalHost

scripts/gen-skill-docs.ts:742–796  ·  view source on GitHub ↗

* Process external host output: routing, frontmatter, path rewrites, metadata. * Shared between Codex and Factory (and future external hosts).

(
  content: string,
  tmplContent: string,
  host: Host,
  skillDir: string,
  extractedDescription: string,
  ctx: TemplateContext,
  frontmatterName?: string,
)

Source from the content-addressed store, hash-verified

740 * Shared between Codex and Factory (and future external hosts).
741 */
742function processExternalHost(
743 content: string,
744 tmplContent: string,
745 host: Host,
746 skillDir: string,
747 extractedDescription: string,
748 ctx: TemplateContext,
749 frontmatterName?: string,
750): { content: string; outputPath: string; outputDir: string; symlinkLoop: boolean } {
751 const hostConfig = getHostConfig(host);
752
753 const name = externalSkillName(skillDir === '.' ? '' : skillDir, frontmatterName);
754 const outputDir = path.join(ROOT, hostConfig.hostSubdir, 'skills', name);
755 fs.mkdirSync(outputDir, { recursive: true });
756 const outputPath = path.join(outputDir, 'SKILL.md');
757
758 // Guard against symlink loops
759 let symlinkLoop = false;
760 const claudePath = ctx.tmplPath.replace(/\.tmpl$/, '');
761 try {
762 const resolvedClaude = fs.realpathSync(claudePath);
763 const resolvedExternal = fs.realpathSync(path.dirname(outputPath)) + '/' + path.basename(outputPath);
764 if (resolvedClaude === resolvedExternal) {
765 symlinkLoop = true;
766 }
767 } catch {
768 // realpathSync fails if file doesn't exist yet — no symlink loop
769 }
770
771 // Extract hook safety prose BEFORE transforming frontmatter (which strips hooks)
772 const safetyProse = extractHookSafetyProse(tmplContent);
773
774 // Transform frontmatter (host-aware)
775 let result = transformFrontmatter(content, host);
776
777 // Insert safety advisory at the top of the body (after frontmatter)
778 if (safetyProse) {
779 const bodyStart = result.indexOf('\n---') + 4;
780 result = result.slice(0, bodyStart) + '\n' + safetyProse + '\n' + result.slice(bodyStart);
781 }
782
783 // Config-driven path + tool rewrites (shared with processSectionTemplate so
784 // section cross-references get the same per-host treatment as SKILL.md).
785 result = applyHostRewrites(result, hostConfig);
786
787 // Config-driven: generate metadata (e.g., openai.yaml for Codex)
788 if (hostConfig.generation.generateMetadata && !symlinkLoop) {
789 const agentsDir = path.join(outputDir, 'agents');
790 fs.mkdirSync(agentsDir, { recursive: true });
791 const shortDescription = condenseOpenAIShortDescription(extractedDescription);
792 fs.writeFileSync(path.join(agentsDir, 'openai.yaml'), generateOpenAIYaml(name, shortDescription));
793 }
794
795 return { content: result, outputPath, outputDir, symlinkLoop };
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');

Callers 1

processTemplateFunction · 0.85

Calls 7

getHostConfigFunction · 0.90
externalSkillNameFunction · 0.90
extractHookSafetyProseFunction · 0.90
generateOpenAIYamlFunction · 0.90
applyHostRewritesFunction · 0.85
transformFrontmatterFunction · 0.70

Tested by

no test coverage detected