From 68d09bc9150eae43e9d1c0296c0a6c386f2231c8 Mon Sep 17 00:00:00 2001 From: Wendy Smoak Date: Thu, 19 Feb 2026 19:02:20 -0500 Subject: [PATCH 1/7] feat: add CodexSkillsSetup installer for agentskills.io format New codex-skills.js writes BMAD artifacts as Agent Skills (directory per skill with SKILL.md) to .agents/skills/. Existing codex.js prompts installer marked as deprecated with preferred=false. Co-Authored-By: Claude Opus 4.6 --- tools/cli/installers/lib/ide/codex-skills.js | 469 +++++++++++++++++++ tools/cli/installers/lib/ide/codex.js | 2 +- 2 files changed, 470 insertions(+), 1 deletion(-) create mode 100644 tools/cli/installers/lib/ide/codex-skills.js diff --git a/tools/cli/installers/lib/ide/codex-skills.js b/tools/cli/installers/lib/ide/codex-skills.js new file mode 100644 index 000000000..78ca7867a --- /dev/null +++ b/tools/cli/installers/lib/ide/codex-skills.js @@ -0,0 +1,469 @@ +const path = require('node:path'); +const fs = require('fs-extra'); +const os = require('node:os'); +const { BaseIdeSetup } = require('./_base-ide'); +const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); +const { AgentCommandGenerator } = require('./shared/agent-command-generator'); +const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator'); +const { getTasksFromBmad } = require('./shared/bmad-artifacts'); +const { toDashPath, customAgentDashName } = require('./shared/path-utils'); +const yaml = require('yaml'); +const prompts = require('../../../lib/prompts'); + +/** + * Codex setup handler (CLI mode) + * Writes BMAD artifacts as Agent Skills (agentskills.io format) + * into .agents/skills/ directories. + */ +class CodexSkillsSetup extends BaseIdeSetup { + constructor() { + super('codex', 'Codex', true); // preferred IDE + } + + /** + * Collect configuration choices before installation + * @param {Object} options - Configuration options + * @returns {Object} Collected configuration + */ + async collectConfiguration(options = {}) { + // Non-interactive mode: use default (project) - recommended for real work + if (options.skipPrompts) { + return { installLocation: options.codexLocation || 'project' }; + } + + let confirmed = false; + let installLocation = 'project'; + + while (!confirmed) { + installLocation = await prompts.select({ + message: 'Where would you like to install Codex CLI skills?', + choices: [ + { + name: 'Project-specific - Recommended (/.agents/skills)', + value: 'project', + }, + { + name: 'Global - ($HOME/.agents/skills)', + value: 'global', + }, + ], + default: 'project', + }); + + // Show brief confirmation hint (detailed instructions available via verbose) + if (installLocation === 'project') { + await prompts.log.info('Skills installed to: /.agents/skills'); + } else { + await prompts.log.info('Skills installed to: $HOME/.agents/skills'); + } + + // Confirm the choice + confirmed = await prompts.confirm({ + message: 'Proceed with this installation option?', + default: true, + }); + + if (!confirmed) { + await prompts.log.warn("Let's choose a different installation option."); + } + } + + return { installLocation }; + } + + /** + * Setup Codex configuration + * @param {string} projectDir - Project directory + * @param {string} bmadDir - BMAD installation directory + * @param {Object} options - Setup options + */ + async setup(projectDir, bmadDir, options = {}) { + if (!options.silent) await prompts.log.info(`Setting up ${this.name}...`); + + // Always use CLI mode + const mode = 'cli'; + + // Get installation location from pre-collected config or default to project + const installLocation = options.preCollectedConfig?.installLocation || 'project'; + + const { artifacts, counts } = await this.collectClaudeArtifacts(projectDir, bmadDir, options); + + const destDir = this.getCodexSkillsDir(projectDir, installLocation); + await fs.ensureDir(destDir); + await this.clearOldBmadSkills(destDir, options); + + // Collect and write agent skills + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, options.selectedModules || []); + const agentCount = await this.writeSkillArtifacts(destDir, agentArtifacts, 'agent-launcher'); + + // Collect and write task skills + const tasks = await getTasksFromBmad(bmadDir, options.selectedModules || []); + const taskArtifacts = []; + for (const task of tasks) { + const content = await this.readAndProcessWithProject( + task.path, + { + module: task.module, + name: task.name, + }, + projectDir, + ); + taskArtifacts.push({ + type: 'task', + name: task.name, + displayName: task.name, + module: task.module, + path: task.path, + sourcePath: task.path, + relativePath: path.join(task.module, 'tasks', `${task.name}.md`), + content, + }); + } + + const ttGen = new TaskToolCommandGenerator(this.bmadFolderName); + const taskSkillArtifacts = taskArtifacts.map((artifact) => ({ + ...artifact, + content: ttGen.generateCommandContent(artifact, artifact.type), + })); + const tasksWritten = await this.writeSkillArtifacts(destDir, taskSkillArtifacts, 'task'); + + // Collect and write workflow skills + const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName); + const { artifacts: workflowArtifacts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir); + const workflowCount = await this.writeSkillArtifacts(destDir, workflowArtifacts, 'workflow-command'); + + const written = agentCount + workflowCount + tasksWritten; + + if (!options.silent) { + await prompts.log.success( + `${this.name} configured: ${counts.agents} agents, ${counts.workflows} workflows, ${counts.tasks} tasks, ${written} skills → ${destDir}`, + ); + } + + return { + success: true, + mode, + artifacts, + counts, + destination: destDir, + written, + installLocation, + }; + } + + /** + * Write artifacts as Agent Skills (agentskills.io format). + * Each artifact becomes a directory containing SKILL.md. + * @param {string} destDir - Base skills directory + * @param {Array} artifacts - Artifacts to write + * @param {string} artifactType - Type filter (e.g., 'agent-launcher', 'workflow-command', 'task') + * @returns {number} Number of skills written + */ + async writeSkillArtifacts(destDir, artifacts, artifactType) { + let writtenCount = 0; + + for (const artifact of artifacts) { + // Filter by type if the artifact has a type field + if (artifact.type && artifact.type !== artifactType) { + continue; + } + + // Get the dash-format name (e.g., bmad-bmm-create-prd.md) and remove .md + const flatName = toDashPath(artifact.relativePath); + const skillName = flatName.replace(/\.md$/, ''); + + // Create skill directory + const skillDir = path.join(destDir, skillName); + await fs.ensureDir(skillDir); + + // Transform content: rewrite frontmatter for skills format + const skillContent = this.transformToSkillFormat(artifact.content, skillName); + + // Write SKILL.md + await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillContent); + writtenCount++; + } + + return writtenCount; + } + + /** + * Transform artifact content from Codex prompt format to Agent Skills format. + * Removes disable-model-invocation, ensures name matches directory. + * @param {string} content - Original content with YAML frontmatter + * @param {string} skillName - Skill name (must match directory name) + * @returns {string} Transformed content + */ + transformToSkillFormat(content, skillName) { + // Parse frontmatter + const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/); + if (!fmMatch) { + // No frontmatter -- wrap with minimal frontmatter + const fm = yaml.stringify({ name: skillName, description: skillName }).trimEnd(); + return `---\n${fm}\n---\n\n${content}`; + } + + const frontmatter = fmMatch[1]; + const body = fmMatch[2]; + + // Parse frontmatter with yaml library to handle all quoting variants + let description; + try { + const parsed = yaml.parse(frontmatter); + description = parsed?.description || `${skillName} skill`; + } catch { + description = `${skillName} skill`; + } + + // Build new frontmatter with only skills-spec fields, let yaml handle quoting + const newFrontmatter = yaml.stringify({ name: skillName, description }).trimEnd(); + return `---\n${newFrontmatter}\n---\n${body}`; + } + + /** + * Detect Codex installation by checking for BMAD skills + */ + async detect(projectDir) { + // Check both global and project-specific locations + const globalDir = this.getCodexSkillsDir(null, 'global'); + const projectDir_local = projectDir || process.cwd(); + const projectSpecificDir = this.getCodexSkillsDir(projectDir_local, 'project'); + + // Check global location + if (await fs.pathExists(globalDir)) { + try { + const entries = await fs.readdir(globalDir); + if (entries && entries.some((entry) => entry && typeof entry === 'string' && entry.startsWith('bmad'))) { + return true; + } + } catch { + // Ignore errors + } + } + + // Check project-specific location + if (await fs.pathExists(projectSpecificDir)) { + try { + const entries = await fs.readdir(projectSpecificDir); + if (entries && entries.some((entry) => entry && typeof entry === 'string' && entry.startsWith('bmad'))) { + return true; + } + } catch { + // Ignore errors + } + } + + return false; + } + + /** + * Collect Claude-style artifacts for Codex export. + * Returns the normalized artifact list for further processing. + */ + async collectClaudeArtifacts(projectDir, bmadDir, options = {}) { + const selectedModules = options.selectedModules || []; + const artifacts = []; + + // Generate agent launchers + const agentGen = new AgentCommandGenerator(this.bmadFolderName); + const { artifacts: agentArtifacts } = await agentGen.collectAgentArtifacts(bmadDir, selectedModules); + + for (const artifact of agentArtifacts) { + artifacts.push({ + type: 'agent', + module: artifact.module, + sourcePath: artifact.sourcePath, + relativePath: artifact.relativePath, + content: artifact.content, + }); + } + + const tasks = await getTasksFromBmad(bmadDir, selectedModules); + for (const task of tasks) { + const content = await this.readAndProcessWithProject( + task.path, + { + module: task.module, + name: task.name, + }, + projectDir, + ); + + artifacts.push({ + type: 'task', + name: task.name, + displayName: task.name, + module: task.module, + path: task.path, + sourcePath: task.path, + relativePath: path.join(task.module, 'tasks', `${task.name}.md`), + content, + }); + } + + const workflowGenerator = new WorkflowCommandGenerator(this.bmadFolderName); + const { artifacts: workflowArtifacts, counts: workflowCounts } = await workflowGenerator.collectWorkflowArtifacts(bmadDir); + artifacts.push(...workflowArtifacts); + + return { + artifacts, + counts: { + agents: agentArtifacts.length, + tasks: tasks.length, + workflows: workflowCounts.commands, + workflowLaunchers: workflowCounts.launchers, + }, + }; + } + + getCodexSkillsDir(projectDir = null, location = 'project') { + if (location === 'project' && projectDir) { + return path.join(projectDir, '.agents', 'skills'); + } + if (location === 'project' && !projectDir) { + throw new Error('projectDir is required for project-scoped skill installation'); + } + return path.join(os.homedir(), '.agents', 'skills'); + } + + /** + * Remove existing BMAD skill directories from the skills directory. + */ + async clearOldBmadSkills(destDir, options = {}) { + if (!(await fs.pathExists(destDir))) { + return; + } + + let entries; + try { + entries = await fs.readdir(destDir); + } catch (error) { + // Directory exists but can't be read - skip cleanup + if (!options.silent) await prompts.log.warn(`Warning: Could not read directory ${destDir}: ${error.message}`); + return; + } + + if (!entries || !Array.isArray(entries)) { + return; + } + + for (const entry of entries) { + // Skip non-strings or undefined entries + if (!entry || typeof entry !== 'string') { + continue; + } + if (!entry.startsWith('bmad')) { + continue; + } + + const entryPath = path.join(destDir, entry); + try { + await fs.remove(entryPath); + } catch (error) { + if (!options.silent) { + await prompts.log.message(` Skipping ${entry}: ${error.message}`); + } + } + } + } + + async readAndProcessWithProject(filePath, metadata, projectDir) { + const rawContent = await fs.readFile(filePath, 'utf8'); + const content = rawContent.replaceAll('\r\n', '\n').replaceAll('\r', '\n'); + return super.processContent(content, metadata, projectDir); + } + + /** + * Get instructions for global installation + * @returns {string} Instructions text + */ + getGlobalInstructions(destDir) { + const lines = [ + 'IMPORTANT: Codex Configuration', + '', + 'Skills installed globally to your HOME DIRECTORY ($HOME/.agents/skills).', + '', + 'These skills reference a specific _bmad path.', + "To use with other projects, you'd need to copy the _bmad dir.", + '', + 'Skills are available in Codex CLI automatically.', + ' Use /skills to see available skills', + ' Skills can also be invoked implicitly based on task description', + ]; + return lines.join('\n'); + } + + /** + * Get instructions for project-specific installation + * @param {string} projectDir - Optional project directory + * @param {string} destDir - Optional destination directory + * @returns {string} Instructions text + */ + getProjectSpecificInstructions(projectDir = null, destDir = null) { + const lines = [ + 'Project-Specific Codex Configuration', + '', + `Skills installed to: ${destDir || '/.agents/skills'}`, + '', + 'Codex automatically discovers skills in .agents/skills/ at and above the current directory and in your home directory.', + 'No additional configuration is needed.', + ]; + + return lines.join('\n'); + } + + /** + * Cleanup Codex configuration + */ + async cleanup(projectDir = null) { + // Clean both global and project-specific locations + const globalDir = this.getCodexSkillsDir(null, 'global'); + await this.clearOldBmadSkills(globalDir); + + if (projectDir) { + const projectSpecificDir = this.getCodexSkillsDir(projectDir, 'project'); + await this.clearOldBmadSkills(projectSpecificDir); + } + } + + /** + * Install a custom agent launcher for Codex as an Agent Skill + * @param {string} projectDir - Project directory + * @param {string} agentName - Agent name (e.g., "fred-commit-poet") + * @param {string} agentPath - Path to compiled agent (relative to project root) + * @param {Object} metadata - Agent metadata + * @returns {Object|null} Info about created skill + */ + async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { + const destDir = this.getCodexSkillsDir(projectDir, 'project'); + + // Skill name from the dash name (without .md) + const skillName = customAgentDashName(agentName).replace(/\.md$/, ''); + const skillDir = path.join(destDir, skillName); + await fs.ensureDir(skillDir); + + const fm = yaml.stringify({ name: skillName, description: `${agentName} agent` }).trimEnd(); + const skillContent = + `---\n${fm}\n---\n` + + "\nYou must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command.\n" + + '\n\n' + + `1. LOAD the FULL agent file from @${agentPath}\n` + + '2. READ its entire contents - this contains the complete agent persona, menu, and instructions\n' + + '3. FOLLOW every step in the section precisely\n' + + '4. DISPLAY the welcome/greeting as instructed\n' + + '5. PRESENT the numbered menu\n' + + '6. WAIT for user input before proceeding\n' + + '\n'; + + const skillPath = path.join(skillDir, 'SKILL.md'); + await fs.writeFile(skillPath, skillContent, 'utf8'); + + return { + path: path.relative(projectDir, skillPath), + command: `$${skillName}`, + }; + } +} + +module.exports = { CodexSkillsSetup }; diff --git a/tools/cli/installers/lib/ide/codex.js b/tools/cli/installers/lib/ide/codex.js index 890d8bd3a..8124e9e2e 100644 --- a/tools/cli/installers/lib/ide/codex.js +++ b/tools/cli/installers/lib/ide/codex.js @@ -14,7 +14,7 @@ const prompts = require('../../../lib/prompts'); */ class CodexSetup extends BaseIdeSetup { constructor() { - super('codex', 'Codex', true); // preferred IDE + super('codex', 'Codex (Custom Prompts) (Deprecated)', false); } /** From 28e2b070c4ca9065e53143790bce9048d4caccfc Mon Sep 17 00:00:00 2001 From: Wendy Smoak Date: Thu, 19 Feb 2026 19:11:50 -0500 Subject: [PATCH 2/7] fix name collision --- tools/cli/installers/lib/ide/codex-skills.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cli/installers/lib/ide/codex-skills.js b/tools/cli/installers/lib/ide/codex-skills.js index 78ca7867a..c43c1c2d7 100644 --- a/tools/cli/installers/lib/ide/codex-skills.js +++ b/tools/cli/installers/lib/ide/codex-skills.js @@ -17,7 +17,7 @@ const prompts = require('../../../lib/prompts'); */ class CodexSkillsSetup extends BaseIdeSetup { constructor() { - super('codex', 'Codex', true); // preferred IDE + super('codex-skills', 'Codex', true); // preferred IDE } /** From 5679c83efe0248ff22733b55aac9de7f729f172f Mon Sep 17 00:00:00 2001 From: Wendy Smoak Date: Thu, 19 Feb 2026 19:12:35 -0500 Subject: [PATCH 3/7] Add codex-skills to manager --- tools/cli/installers/lib/ide/manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cli/installers/lib/ide/manager.js b/tools/cli/installers/lib/ide/manager.js index f83db4592..88f82181c 100644 --- a/tools/cli/installers/lib/ide/manager.js +++ b/tools/cli/installers/lib/ide/manager.js @@ -61,7 +61,7 @@ class IdeManager { */ async loadCustomInstallerFiles() { const ideDir = __dirname; - const customFiles = ['codex.js', 'github-copilot.js', 'kilo.js']; + const customFiles = ['codex.js', 'codex-skills.js', 'github-copilot.js', 'kilo.js']; for (const file of customFiles) { const filePath = path.join(ideDir, file); From a974adc8783c57d1d90e6d0a955c6f0be95c1888 Mon Sep 17 00:00:00 2001 From: Wendy Smoak Date: Thu, 19 Feb 2026 19:16:01 -0500 Subject: [PATCH 4/7] Add Codex skills to platform codes --- tools/cli/installers/lib/ide/platform-codes.yaml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/cli/installers/lib/ide/platform-codes.yaml b/tools/cli/installers/lib/ide/platform-codes.yaml index 7c2dde2cb..300a39f35 100644 --- a/tools/cli/installers/lib/ide/platform-codes.yaml +++ b/tools/cli/installers/lib/ide/platform-codes.yaml @@ -51,12 +51,19 @@ platforms: template_type: windsurf codex: - name: "Codex" + name: "Codex (Deprecated)" preferred: false category: cli - description: "OpenAI Codex integration" + description: "OpenAI Codex integration (legacy custom prompts)" # No installer config - uses custom codex.js + codex-skills: + name: "Codex" + preferred: true + category: cli + description: "OpenAI Codex integration (skills)" + # No installer config - uses custom codex-skills.js + crush: name: "Crush" preferred: false From 0f280e590ea9ec01b7732ccda1eaa5f555b757e2 Mon Sep 17 00:00:00 2001 From: Wendy Smoak Date: Thu, 19 Feb 2026 19:19:25 -0500 Subject: [PATCH 5/7] remove option to install globally. this does not exist for any other ide --- tools/cli/installers/lib/ide/codex-skills.js | 117 ++----------------- 1 file changed, 9 insertions(+), 108 deletions(-) diff --git a/tools/cli/installers/lib/ide/codex-skills.js b/tools/cli/installers/lib/ide/codex-skills.js index c43c1c2d7..dd6284749 100644 --- a/tools/cli/installers/lib/ide/codex-skills.js +++ b/tools/cli/installers/lib/ide/codex-skills.js @@ -1,6 +1,5 @@ const path = require('node:path'); const fs = require('fs-extra'); -const os = require('node:os'); const { BaseIdeSetup } = require('./_base-ide'); const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator'); const { AgentCommandGenerator } = require('./shared/agent-command-generator'); @@ -20,57 +19,6 @@ class CodexSkillsSetup extends BaseIdeSetup { super('codex-skills', 'Codex', true); // preferred IDE } - /** - * Collect configuration choices before installation - * @param {Object} options - Configuration options - * @returns {Object} Collected configuration - */ - async collectConfiguration(options = {}) { - // Non-interactive mode: use default (project) - recommended for real work - if (options.skipPrompts) { - return { installLocation: options.codexLocation || 'project' }; - } - - let confirmed = false; - let installLocation = 'project'; - - while (!confirmed) { - installLocation = await prompts.select({ - message: 'Where would you like to install Codex CLI skills?', - choices: [ - { - name: 'Project-specific - Recommended (/.agents/skills)', - value: 'project', - }, - { - name: 'Global - ($HOME/.agents/skills)', - value: 'global', - }, - ], - default: 'project', - }); - - // Show brief confirmation hint (detailed instructions available via verbose) - if (installLocation === 'project') { - await prompts.log.info('Skills installed to: /.agents/skills'); - } else { - await prompts.log.info('Skills installed to: $HOME/.agents/skills'); - } - - // Confirm the choice - confirmed = await prompts.confirm({ - message: 'Proceed with this installation option?', - default: true, - }); - - if (!confirmed) { - await prompts.log.warn("Let's choose a different installation option."); - } - } - - return { installLocation }; - } - /** * Setup Codex configuration * @param {string} projectDir - Project directory @@ -83,12 +31,9 @@ class CodexSkillsSetup extends BaseIdeSetup { // Always use CLI mode const mode = 'cli'; - // Get installation location from pre-collected config or default to project - const installLocation = options.preCollectedConfig?.installLocation || 'project'; - const { artifacts, counts } = await this.collectClaudeArtifacts(projectDir, bmadDir, options); - const destDir = this.getCodexSkillsDir(projectDir, installLocation); + const destDir = this.getCodexSkillsDir(projectDir); await fs.ensureDir(destDir); await this.clearOldBmadSkills(destDir, options); @@ -148,7 +93,6 @@ class CodexSkillsSetup extends BaseIdeSetup { counts, destination: destDir, written, - installLocation, }; } @@ -225,27 +169,11 @@ class CodexSkillsSetup extends BaseIdeSetup { * Detect Codex installation by checking for BMAD skills */ async detect(projectDir) { - // Check both global and project-specific locations - const globalDir = this.getCodexSkillsDir(null, 'global'); - const projectDir_local = projectDir || process.cwd(); - const projectSpecificDir = this.getCodexSkillsDir(projectDir_local, 'project'); + const dir = this.getCodexSkillsDir(projectDir || process.cwd()); - // Check global location - if (await fs.pathExists(globalDir)) { + if (await fs.pathExists(dir)) { try { - const entries = await fs.readdir(globalDir); - if (entries && entries.some((entry) => entry && typeof entry === 'string' && entry.startsWith('bmad'))) { - return true; - } - } catch { - // Ignore errors - } - } - - // Check project-specific location - if (await fs.pathExists(projectSpecificDir)) { - try { - const entries = await fs.readdir(projectSpecificDir); + const entries = await fs.readdir(dir); if (entries && entries.some((entry) => entry && typeof entry === 'string' && entry.startsWith('bmad'))) { return true; } @@ -317,14 +245,11 @@ class CodexSkillsSetup extends BaseIdeSetup { }; } - getCodexSkillsDir(projectDir = null, location = 'project') { - if (location === 'project' && projectDir) { - return path.join(projectDir, '.agents', 'skills'); - } - if (location === 'project' && !projectDir) { + getCodexSkillsDir(projectDir) { + if (!projectDir) { throw new Error('projectDir is required for project-scoped skill installation'); } - return path.join(os.homedir(), '.agents', 'skills'); + return path.join(projectDir, '.agents', 'skills'); } /** @@ -374,26 +299,6 @@ class CodexSkillsSetup extends BaseIdeSetup { return super.processContent(content, metadata, projectDir); } - /** - * Get instructions for global installation - * @returns {string} Instructions text - */ - getGlobalInstructions(destDir) { - const lines = [ - 'IMPORTANT: Codex Configuration', - '', - 'Skills installed globally to your HOME DIRECTORY ($HOME/.agents/skills).', - '', - 'These skills reference a specific _bmad path.', - "To use with other projects, you'd need to copy the _bmad dir.", - '', - 'Skills are available in Codex CLI automatically.', - ' Use /skills to see available skills', - ' Skills can also be invoked implicitly based on task description', - ]; - return lines.join('\n'); - } - /** * Get instructions for project-specific installation * @param {string} projectDir - Optional project directory @@ -417,13 +322,9 @@ class CodexSkillsSetup extends BaseIdeSetup { * Cleanup Codex configuration */ async cleanup(projectDir = null) { - // Clean both global and project-specific locations - const globalDir = this.getCodexSkillsDir(null, 'global'); - await this.clearOldBmadSkills(globalDir); - if (projectDir) { - const projectSpecificDir = this.getCodexSkillsDir(projectDir, 'project'); - await this.clearOldBmadSkills(projectSpecificDir); + const destDir = this.getCodexSkillsDir(projectDir); + await this.clearOldBmadSkills(destDir); } } From f0fc78407c20481370a07c862808a4f3ef510f40 Mon Sep 17 00:00:00 2001 From: Wendy Smoak Date: Fri, 20 Feb 2026 07:03:30 -0500 Subject: [PATCH 6/7] Remove extra method param --- tools/cli/installers/lib/ide/codex-skills.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cli/installers/lib/ide/codex-skills.js b/tools/cli/installers/lib/ide/codex-skills.js index dd6284749..692f3c26b 100644 --- a/tools/cli/installers/lib/ide/codex-skills.js +++ b/tools/cli/installers/lib/ide/codex-skills.js @@ -337,7 +337,7 @@ class CodexSkillsSetup extends BaseIdeSetup { * @returns {Object|null} Info about created skill */ async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) { - const destDir = this.getCodexSkillsDir(projectDir, 'project'); + const destDir = this.getCodexSkillsDir(projectDir); // Skill name from the dash name (without .md) const skillName = customAgentDashName(agentName).replace(/\.md$/, ''); From c075d1673c479e0e4bb4a615783140d772f84a50 Mon Sep 17 00:00:00 2001 From: Wendy Smoak Date: Fri, 20 Feb 2026 08:12:56 -0500 Subject: [PATCH 7/7] add skill to display name --- tools/cli/installers/lib/ide/codex-skills.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cli/installers/lib/ide/codex-skills.js b/tools/cli/installers/lib/ide/codex-skills.js index 692f3c26b..a5cb64b24 100644 --- a/tools/cli/installers/lib/ide/codex-skills.js +++ b/tools/cli/installers/lib/ide/codex-skills.js @@ -16,7 +16,7 @@ const prompts = require('../../../lib/prompts'); */ class CodexSkillsSetup extends BaseIdeSetup { constructor() { - super('codex-skills', 'Codex', true); // preferred IDE + super('codex-skills', 'Codex (Skills)', true); // preferred IDE } /**