From 870d65b1e3946a62d1a21cf4e873d655c3ca5945 Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Thu, 5 Mar 2026 10:18:52 -0800 Subject: [PATCH] Constrain audit/critique to only suggest real commands via {{available_commands}} Adds a dynamic {{available_commands}} placeholder that the build system replaces with the actual list of user-invokable skill names. Audit and critique now explicitly instruct the LLM to only suggest from this list, preventing hallucinated commands like /redesign. Co-Authored-By: Claude Opus 4.6 --- scripts/lib/transformers/agents.js | 3 ++- scripts/lib/transformers/claude-code.js | 3 ++- scripts/lib/transformers/codex.js | 3 ++- scripts/lib/transformers/cursor.js | 3 ++- scripts/lib/transformers/gemini.js | 3 ++- scripts/lib/utils.js | 8 ++++++-- source/skills/audit/SKILL.md | 12 +++++++----- source/skills/critique/SKILL.md | 2 +- 8 files changed, 24 insertions(+), 13 deletions(-) diff --git a/scripts/lib/transformers/agents.js b/scripts/lib/transformers/agents.js index 00e8e3cc9..80c141d66 100644 --- a/scripts/lib/transformers/agents.js +++ b/scripts/lib/transformers/agents.js @@ -21,6 +21,7 @@ export function transformAgents(skills, distDir, patterns = null, options = {}) ensureDir(skillsDir); const allSkillNames = skills.map(s => s.name); + const commandNames = skills.filter(s => s.userInvokable).map(s => `${prefix}${s.name}`); let refCount = 0; for (const skill of skills) { const skillName = `${prefix}${skill.name}`; @@ -42,7 +43,7 @@ export function transformAgents(skills, distDir, patterns = null, options = {}) } const frontmatter = generateYamlFrontmatter(frontmatterObj); - let skillBody = replacePlaceholders(skill.body, 'agents'); + let skillBody = replacePlaceholders(skill.body, 'agents', commandNames); if (prefix) skillBody = prefixSkillReferences(skillBody, prefix, allSkillNames); const content = `${frontmatter}\n\n${skillBody}`; const outputPath = path.join(skillDir, 'SKILL.md'); diff --git a/scripts/lib/transformers/claude-code.js b/scripts/lib/transformers/claude-code.js index 767584737..b7db1df40 100644 --- a/scripts/lib/transformers/claude-code.js +++ b/scripts/lib/transformers/claude-code.js @@ -23,6 +23,7 @@ export function transformClaudeCode(skills, distDir, patterns = null, options = ensureDir(skillsDir); const allSkillNames = skills.map(s => s.name); + const commandNames = skills.filter(s => s.userInvokable).map(s => `${prefix}${s.name}`); let refCount = 0; for (const skill of skills) { const skillName = `${prefix}${skill.name}`; @@ -41,7 +42,7 @@ export function transformClaudeCode(skills, distDir, patterns = null, options = if (skill.allowedTools) frontmatterObj['allowed-tools'] = skill.allowedTools; const frontmatter = generateYamlFrontmatter(frontmatterObj); - let skillBody = replacePlaceholders(skill.body, 'claude-code'); + let skillBody = replacePlaceholders(skill.body, 'claude-code', commandNames); if (prefix) skillBody = prefixSkillReferences(skillBody, prefix, allSkillNames); const content = `${frontmatter}\n\n${skillBody}`; const outputPath = path.join(skillDir, 'SKILL.md'); diff --git a/scripts/lib/transformers/codex.js b/scripts/lib/transformers/codex.js index a3f59d0b4..e96c49dbc 100644 --- a/scripts/lib/transformers/codex.js +++ b/scripts/lib/transformers/codex.js @@ -24,6 +24,7 @@ export function transformCodex(skills, distDir, patterns = null, options = {}) { ensureDir(skillsDir); const allSkillNames = skills.map(s => s.name); + const commandNames = skills.filter(s => s.userInvokable).map(s => `${prefix}${s.name}`); let refCount = 0; for (const skill of skills) { const skillName = `${prefix}${skill.name}`; @@ -45,7 +46,7 @@ export function transformCodex(skills, distDir, patterns = null, options = {}) { const frontmatter = generateYamlFrontmatter(frontmatterObj); - let skillBody = replacePlaceholders(skill.body, 'codex'); + let skillBody = replacePlaceholders(skill.body, 'codex', commandNames); if (prefix) skillBody = prefixSkillReferences(skillBody, prefix, allSkillNames); // For user-invokable skills, transform remaining {{argname}} to $ARGNAME if (skill.userInvokable) { diff --git a/scripts/lib/transformers/cursor.js b/scripts/lib/transformers/cursor.js index 3ad31547c..f5123f1d3 100644 --- a/scripts/lib/transformers/cursor.js +++ b/scripts/lib/transformers/cursor.js @@ -23,6 +23,7 @@ export function transformCursor(skills, distDir, patterns = null, options = {}) ensureDir(skillsDir); const allSkillNames = skills.map(s => s.name); + const commandNames = skills.filter(s => s.userInvokable).map(s => `${prefix}${s.name}`); let refCount = 0; for (const skill of skills) { const skillName = `${prefix}${skill.name}`; @@ -35,7 +36,7 @@ export function transformCursor(skills, distDir, patterns = null, options = {}) if (skill.license) frontmatterObj.license = skill.license; const frontmatter = generateYamlFrontmatter(frontmatterObj); - let skillBody = replacePlaceholders(skill.body, 'cursor'); + let skillBody = replacePlaceholders(skill.body, 'cursor', commandNames); if (prefix) skillBody = prefixSkillReferences(skillBody, prefix, allSkillNames); const content = `${frontmatter}\n\n${skillBody}`; const outputPath = path.join(skillDir, 'SKILL.md'); diff --git a/scripts/lib/transformers/gemini.js b/scripts/lib/transformers/gemini.js index 74ac45613..ead1b1223 100644 --- a/scripts/lib/transformers/gemini.js +++ b/scripts/lib/transformers/gemini.js @@ -24,6 +24,7 @@ export function transformGemini(skills, distDir, patterns = null, options = {}) ensureDir(skillsDir); const allSkillNames = skills.map(s => s.name); + const commandNames = skills.filter(s => s.userInvokable).map(s => `${prefix}${s.name}`); let refCount = 0; for (const skill of skills) { const skillName = `${prefix}${skill.name}`; @@ -34,7 +35,7 @@ export function transformGemini(skills, distDir, patterns = null, options = {}) description: skill.description, }); - let skillBody = replacePlaceholders(skill.body, 'gemini'); + let skillBody = replacePlaceholders(skill.body, 'gemini', commandNames); if (prefix) skillBody = prefixSkillReferences(skillBody, prefix, allSkillNames); // For user-invokable skills, replace remaining {{arg}} placeholders with {{args}} if (skill.userInvokable) { diff --git a/scripts/lib/utils.js b/scripts/lib/utils.js index 753cab24f..3621dbd15 100644 --- a/scripts/lib/utils.js +++ b/scripts/lib/utils.js @@ -326,13 +326,17 @@ function escapeRegex(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } -export function replacePlaceholders(content, provider) { +export function replacePlaceholders(content, provider, commandNames = []) { const placeholders = PROVIDER_PLACEHOLDERS[provider] || PROVIDER_PLACEHOLDERS['cursor']; + const commandList = commandNames.length > 0 + ? commandNames.map(n => `/${n}`).join(', ') + : ''; return content .replace(/\{\{model\}\}/g, placeholders.model) .replace(/\{\{config_file\}\}/g, placeholders.config_file) - .replace(/\{\{ask_instruction\}\}/g, placeholders.ask_instruction); + .replace(/\{\{ask_instruction\}\}/g, placeholders.ask_instruction) + .replace(/\{\{available_commands\}\}/g, commandList); } /** diff --git a/source/skills/audit/SKILL.md b/source/skills/audit/SKILL.md index 8dd7f8373..37accd069 100644 --- a/source/skills/audit/SKILL.md +++ b/source/skills/audit/SKILL.md @@ -71,7 +71,7 @@ For each issue, document: - **Impact**: How it affects users - **WCAG/Standard**: Which standard it violates (if applicable) - **Recommendation**: How to fix it -- **Suggested command**: Which command to use (e.g., `/normalize`, `/optimize`, `/harden`) +- **Suggested command**: Which command to use (ONLY from: {{available_commands}}) #### Critical Issues [Issues that block core functionality or violate WCAG A] @@ -108,10 +108,12 @@ Create actionable plan: ### Suggested Commands for Fixes -Map issues to appropriate commands: -- "Use `/normalize` to align components with design system (addresses 23 theming issues)" -- "Use `/optimize` to improve performance (addresses 12 performance issues)" -- "Use `/harden` to improve i18n and text handling (addresses 8 edge cases)" +Map issues to the available commands. **Only suggest commands from this list**: {{available_commands}}. Never invent commands that aren't listed. + +Examples: +- "Use `/normalize` to align with design system (addresses N theming issues)" +- "Use `/optimize` to improve performance (addresses N performance issues)" +- "Use `/harden` to improve resilience (addresses N edge cases)" **IMPORTANT**: Be thorough but actionable. Too many low-priority issues creates noise. Focus on what actually matters. diff --git a/source/skills/critique/SKILL.md b/source/skills/critique/SKILL.md index ad0d6af4e..e9dd53fc6 100644 --- a/source/skills/critique/SKILL.md +++ b/source/skills/critique/SKILL.md @@ -98,7 +98,7 @@ For each issue: - **What**: Name the problem clearly - **Why it matters**: How this hurts users or undermines goals - **Fix**: What to do about it (be concrete) -- **Command**: Which command to use (`/polish`, `/distill`, `/bolder`, `/quieter`, etc.) +- **Command**: Which command to use (ONLY from: {{available_commands}}) ### Minor Observations Quick notes on smaller issues worth addressing.