From d67d69d3422257b08e26a71fae25babc9c95fddb Mon Sep 17 00:00:00 2001 From: Gabi Date: Sun, 15 Mar 2026 21:08:22 +0100 Subject: [PATCH] fix: Codex was showing / instead of $ for command references The build system hardcoded `/` as the command prefix for every provider, but Codex CLI uses `$`. Added command_prefix to PROVIDER_PLACEHOLDERS so replacePlaceholders and prefixSkillReferences use the right one. Now `$normalize` shows up in Codex output instead of `/normalize`. --- scripts/lib/transformers/codex.js | 6 ++-- scripts/lib/utils.js | 53 ++++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/scripts/lib/transformers/codex.js b/scripts/lib/transformers/codex.js index 2192e3ee3..9019cbb40 100644 --- a/scripts/lib/transformers/codex.js +++ b/scripts/lib/transformers/codex.js @@ -46,8 +46,8 @@ export function transformCodex(skills, distDir, patterns = null, options = {}) { const frontmatter = generateYamlFrontmatter(frontmatterObj); - let skillBody = replacePlaceholders(skill.body, 'codex', commandNames); - if (prefix) skillBody = prefixSkillReferences(skillBody, prefix, allSkillNames); + let skillBody = replacePlaceholders(skill.body, 'codex', commandNames, allSkillNames); + if (prefix) skillBody = prefixSkillReferences(skillBody, prefix, allSkillNames, '$'); // For user-invocable skills, transform remaining {{argname}} to $ARGNAME if (skill.userInvocable) { skillBody = skillBody.replace(/\{\{([^}]+)\}\}/g, (match, argName) => { @@ -65,7 +65,7 @@ export function transformCodex(skills, distDir, patterns = null, options = {}) { ensureDir(refDir); for (const ref of skill.references) { const refOutputPath = path.join(refDir, `${ref.name}.md`); - const refContent = replacePlaceholders(ref.content, 'codex'); + const refContent = replacePlaceholders(ref.content, 'codex', [], allSkillNames); writeFile(refOutputPath, refContent); refCount++; } diff --git a/scripts/lib/utils.js b/scripts/lib/utils.js index 50086ece7..ba2b12519 100644 --- a/scripts/lib/utils.js +++ b/scripts/lib/utils.js @@ -268,41 +268,49 @@ export const PROVIDER_PLACEHOLDERS = { model: 'Claude', config_file: 'CLAUDE.md', ask_instruction: 'STOP and call the AskUserQuestion tool to clarify.' + command_prefix: '/' }, 'cursor': { model: 'the model', config_file: '.cursorrules', - ask_instruction: 'ask the user directly to clarify what you cannot infer.' + ask_instruction: 'ask the user directly to clarify what you cannot infer.', + command_prefix: '/' }, 'gemini': { model: 'Gemini', config_file: 'GEMINI.md', - ask_instruction: 'ask the user directly to clarify what you cannot infer.' + ask_instruction: 'ask the user directly to clarify what you cannot infer.', + command_prefix: '/' }, 'codex': { model: 'GPT', config_file: 'AGENTS.md', - ask_instruction: 'ask the user directly to clarify what you cannot infer.' + ask_instruction: 'ask the user directly to clarify what you cannot infer.', + command_prefix: '$' }, 'agents': { model: 'the model', config_file: '.github/copilot-instructions.md', - ask_instruction: 'ask the user directly to clarify what you cannot infer.' + ask_instruction: 'ask the user directly to clarify what you cannot infer.', + command_prefix: '/' }, 'kiro': { model: 'Claude', config_file: '.kiro/settings.json', - ask_instruction: 'ask the user directly to clarify what you cannot infer.' + ask_instruction: 'ask the user directly to clarify what you cannot infer.', + command_prefix: '/' }, opencode: { model: 'Claude', config_file: 'AGENTS.md', ask_instruction: 'STOP and call the `question` tool to clarify.', + command_prefix: '/' }, 'pi': { model: 'the model', config_file: 'AGENTS.md', - ask_instruction: 'ask the user directly to clarify what you cannot infer.' + ask_instruction: 'ask the user directly to clarify what you cannot infer.', + command_prefix: '/' } }; @@ -316,8 +324,9 @@ export const PROVIDER_PLACEHOLDERS = { * @param {string} content - The skill body text * @param {string} prefix - The prefix to add (e.g., 'i-') * @param {string[]} skillNames - Array of all skill names + * @param {string} commandPrefix - The command invocation prefix (e.g., '/' or '$') */ -export function prefixSkillReferences(content, prefix, skillNames) { +export function prefixSkillReferences(content, prefix, skillNames, commandPrefix = '/') { if (!prefix || !skillNames || skillNames.length === 0) return content; let result = content; @@ -327,8 +336,12 @@ export function prefixSkillReferences(content, prefix, skillNames) { for (const name of sorted) { const prefixed = `${prefix}${name}`; - // Replace `/skillname` references (command invocations) - result = result.replace(new RegExp(`\\/(?=${escapeRegex(name)}(?:[^a-zA-Z0-9_-]|$))`, 'g'), `/${prefix}`); + // Replace command invocations (e.g., `/skillname` or `$skillname`) with prefixed versions + const escapedPrefix = escapeRegex(commandPrefix); + result = result.replace( + new RegExp(`${escapedPrefix}(?=${escapeRegex(name)}(?:[^a-zA-Z0-9_-]|$))`, 'g'), + `${commandPrefix}${prefix}` + ); // Replace `the skillname skill` references result = result.replace( @@ -346,18 +359,34 @@ function escapeRegex(str) { const EXCLUDED_FROM_SUGGESTIONS = new Set(['teach-impeccable', 'i-teach-impeccable']); -export function replacePlaceholders(content, provider, commandNames = []) { +export function replacePlaceholders(content, provider, commandNames = [], allSkillNames = []) { const placeholders = PROVIDER_PLACEHOLDERS[provider] || PROVIDER_PLACEHOLDERS['cursor']; + const cmdPrefix = placeholders.command_prefix || '/'; const commandList = commandNames .filter(n => !EXCLUDED_FROM_SUGGESTIONS.has(n)) - .map(n => `/${n}`) + .map(n => `${cmdPrefix}${n}`) .join(', '); - return content + let result = content .replace(/\{\{model\}\}/g, placeholders.model) .replace(/\{\{config_file\}\}/g, placeholders.config_file) .replace(/\{\{ask_instruction\}\}/g, placeholders.ask_instruction) + .replace(/\{\{command_prefix\}\}/g, cmdPrefix) .replace(/\{\{available_commands\}\}/g, commandList); + + // Replace `/skillname` invocations with the correct command prefix for this provider + // (e.g., `/normalize` → `$normalize` for Codex) + if (cmdPrefix !== '/' && allSkillNames.length > 0) { + const sorted = [...allSkillNames].sort((a, b) => b.length - a.length); + for (const name of sorted) { + result = result.replace( + new RegExp(`\\/(?=${escapeRegex(name)}(?:[^a-zA-Z0-9_-]|$))`, 'g'), + cmdPrefix + ); + } + } + + return result; } /**