import { readFileSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; export const napcatAutomationPromptNames = [ 'upstream-audit', 'sync-candidate-review', 'runtime-release-readiness', 'remote-dev-handoff', ] as const; export type NapcatAutomationPromptName = (typeof napcatAutomationPromptNames)[number]; const promptsRoot = path.resolve( fileURLToPath(new URL('../..', import.meta.url)), 'prompts', 'napcat', ); const promptsRootBoundary = promptsRoot.endsWith(path.sep) ? promptsRoot : `${promptsRoot}${path.sep}`; const napcatAutomationPromptNameSet = new Set( napcatAutomationPromptNames, ); const napcatAutomationPromptFileNames = { 'upstream-audit': 'upstream-audit.md', 'sync-candidate-review': 'sync-candidate-review.md', 'runtime-release-readiness': 'runtime-release-readiness.md', 'remote-dev-handoff': 'remote-dev-handoff.md', } satisfies Record; /** * Parses an external prompt name into the source-controlled NapCat prompt enum. * @param value - Raw prompt name from CLI, JSON, MCP input, or a typed caller; only exact entries in `napcatAutomationPromptNames` are accepted. * @returns A stable prompt name that can be mapped to a fixed file under `prompts/napcat`. */ export function parseNapcatAutomationPromptName( value: string, ): NapcatAutomationPromptName { if (napcatAutomationPromptNameSet.has(value)) { return value as NapcatAutomationPromptName; } throw new Error(`Unsupported NapCat automation prompt: ${value}`); } /** * Loads a source-controlled NapCat automation prompt by stable prompt name. * @param name - Prompt identifier from `napcatAutomationPromptNames`; runtime callers may still pass untyped external strings, so the loader re-validates and resolves only fixed filenames. * @returns UTF-8 prompt text used by Codex CLI automation. */ export function loadNapcatAutomationPrompt( name: NapcatAutomationPromptName, ): string { const promptName = parseNapcatAutomationPromptName(String(name)); const promptPath = path.resolve( promptsRoot, napcatAutomationPromptFileNames[promptName], ); if (!promptPath.startsWith(promptsRootBoundary)) { throw new Error(`NapCat automation prompt escaped prompt root: ${name}`); } return readFileSync(promptPath, 'utf8'); }