From 05736fa06903275567c885aa4492f6b03dea7b26 Mon Sep 17 00:00:00 2001 From: Jonathan Borgwing Date: Sun, 31 Aug 2025 18:48:03 -0400 Subject: [PATCH] feat(installer): add Codex CLI + Codex Web modes, generate AGENTS.md, inject npm scripts, and docs (#529) --- README.md | 16 ++ docs/user-guide.md | 26 +++ tools/installer/bin/bmad.js | 2 +- tools/installer/config/install.config.yaml | 22 +++ tools/installer/lib/ide-setup.js | 175 +++++++++++++++++++++ 5 files changed, 240 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 135a2937..c22292f6 100644 --- a/README.md +++ b/README.md @@ -243,3 +243,19 @@ BMAD™ and BMAD-METHOD™ are trademarks of BMad Code, LLC. All rights reserved [![Contributors](https://contrib.rocks/image?repo=bmadcode/bmad-method)](https://github.com/bmadcode/bmad-method/graphs/contributors) Built with ❤️ for the AI-assisted development community + +#### Codex (CLI & Web) + +- Two modes are supported: + - Codex (local only): `npx bmad-method install -f -i codex -d .` — keeps `.bmad-core/` ignored via `.gitignore` for local development. + - Codex Web Enabled: `npx bmad-method install -f -i codex-web -d .` — ensures `.bmad-core/` is tracked (not ignored) so it can be committed for Codex Web. +- For Codex Web, commit both `.bmad-core/` and `AGENTS.md` to the repository. +- Codex CLI: run `codex` at your project root; reference agents naturally, e.g., “As dev, implement …”. +- Codex Web: open your repo in Codex and prompt the same way — it reads `AGENTS.md` automatically. +- Refresh after changes: rerun the appropriate install command (`codex` or `codex-web`) to regenerate the BMAD section inside `AGENTS.md`. + +If a `package.json` exists in your project, the installer will add helpful scripts: + +- `bmad:refresh` → `bmad-method install -f -i codex` +- `bmad:list` → `bmad-method list:agents` +- `bmad:validate` → `bmad-method validate` diff --git a/docs/user-guide.md b/docs/user-guide.md index 43c2daf6..117b08b4 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -187,6 +187,32 @@ If you want to do the planning on the web with Claude (Sonnet 4 or Opus), Gemini npx bmad-method install ``` +### Codex (CLI & Web) + +BMAD integrates with OpenAI Codex via `AGENTS.md` and committed core agent files. + +- Two installation modes: + - Codex (local only): keeps `.bmad-core/` ignored for local dev. + - `npx bmad-method install -f -i codex -d .` + - Codex Web Enabled: ensures `.bmad-core/` is tracked so you can commit it for Codex Web. + - `npx bmad-method install -f -i codex-web -d .` + +- What gets generated: + - `AGENTS.md` at the project root with a BMAD section containing + - How-to-use with Codex (CLI & Web) + - Agent Directory (Title, ID, When To Use) + - Detailed per‑agent sections with source path, when-to-use, activation phrasing, and YAML + - Tasks with quick usage notes + - If a `package.json` exists, helpful scripts are added: + - `bmad:refresh`, `bmad:list`, `bmad:validate` + +- Using Codex: + - CLI: run `codex` in the project root and prompt naturally, e.g., “As dev, implement …”. + - Web: commit `.bmad-core/` and `AGENTS.md`, then open the repo in Codex and prompt the same way. + +- Refresh after changes: + - Re-run the appropriate install mode (`codex` or `codex-web`) to update the BMAD block in `AGENTS.md`. + ## Special Agents There are two BMad agents — in the future they'll be consolidated into a single BMad-Master. diff --git a/tools/installer/bin/bmad.js b/tools/installer/bin/bmad.js index b8272c14..ce8ded5a 100755 --- a/tools/installer/bin/bmad.js +++ b/tools/installer/bin/bmad.js @@ -49,7 +49,7 @@ program .option('-d, --directory ', 'Installation directory') .option( '-i, --ide ', - 'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, trae, roo, kilo, cline, gemini, qwen-code, github-copilot, other)', + 'Configure for specific IDE(s) - can specify multiple (cursor, claude-code, windsurf, trae, roo, kilo, cline, gemini, qwen-code, github-copilot, codex, codex-web, other)', ) .option( '-e, --expansion-packs ', diff --git a/tools/installer/config/install.config.yaml b/tools/installer/config/install.config.yaml index 43013550..e929b722 100644 --- a/tools/installer/config/install.config.yaml +++ b/tools/installer/config/install.config.yaml @@ -121,3 +121,25 @@ ide-configurations: # 2. It concatenates all agent files into a single QWEN.md file. # 3. Simply mention the agent in your prompt (e.g., "As *dev, ..."). # 4. The Qwen Code CLI will automatically have the context for that agent. + + codex: + name: Codex CLI + format: project-memory + file: AGENTS.md + instructions: | + # To use BMAD agents with Codex CLI: + # 1. The installer updates/creates AGENTS.md at your project root with BMAD agents and tasks. + # 2. Run `codex` in your project. Codex automatically reads AGENTS.md as project memory. + # 3. Mention agents in your prompt (e.g., "As dev, please implement ...") or reference tasks. + # 4. You can further customize global Codex behavior via ~/.codex/config.toml. + + codex-web: + name: Codex Web Enabled + format: project-memory + file: AGENTS.md + instructions: | + # To enable BMAD agents for Codex Web (cloud): + # 1. The installer updates/creates AGENTS.md and ensures `.bmad-core` is NOT ignored by git. + # 2. Commit `.bmad-core/` and `AGENTS.md` to your repository. + # 3. Open the repo in Codex Web and reference agents naturally (e.g., "As dev, ..."). + # 4. Re-run this installer to refresh agent sections when the core changes. diff --git a/tools/installer/lib/ide-setup.js b/tools/installer/lib/ide-setup.js index b6944ad1..2dd33302 100644 --- a/tools/installer/lib/ide-setup.js +++ b/tools/installer/lib/ide-setup.js @@ -74,6 +74,12 @@ class IdeSetup extends BaseIdeSetup { case 'qwen-code': { return this.setupQwenCode(installDir, selectedAgent); } + case 'codex': { + return this.setupCodex(installDir, selectedAgent, { webEnabled: false }); + } + case 'codex-web': { + return this.setupCodex(installDir, selectedAgent, { webEnabled: true }); + } default: { console.log(chalk.yellow(`\nIDE ${ide} not yet supported`)); return false; @@ -81,6 +87,175 @@ class IdeSetup extends BaseIdeSetup { } } + async setupCodex(installDir, selectedAgent, options) { + options = options ?? { webEnabled: false }; + // Codex reads AGENTS.md at the project root as project memory (CLI & Web). + // Inject/update a BMAD section with guidance, directory, and details. + const filePath = path.join(installDir, 'AGENTS.md'); + const startMarker = ''; + const endMarker = ''; + + const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir); + const tasks = await this.getAllTaskIds(installDir); + + // Build BMAD section content + let section = ''; + section += `${startMarker}\n`; + section += `# BMAD-METHOD Agents and Tasks\n\n`; + section += `This section is auto-generated by BMAD-METHOD for Codex. Codex merges this AGENTS.md into context.\n\n`; + section += `## How To Use With Codex\n\n`; + section += `- Codex CLI: run \`codex\` in this project. Reference an agent naturally, e.g., "As dev, implement ...".\n`; + section += `- Codex Web: open this repo and reference roles the same way; Codex reads \`AGENTS.md\`.\n`; + section += `- Commit \`.bmad-core\` and this \`AGENTS.md\` file to your repo so Codex (Web/CLI) can read full agent definitions.\n`; + section += `- Refresh this section after agent updates: \`npx bmad-method install -f -i codex\`.\n\n`; + + section += `### Helpful Commands\n\n`; + section += `- List agents: \`npx bmad-method list:agents\`\n`; + section += `- Reinstall BMAD core and regenerate AGENTS.md: \`npx bmad-method install -f -i codex\`\n`; + section += `- Validate configuration: \`npx bmad-method validate\`\n\n`; + + // Agents directory table + section += `## Agents\n\n`; + section += `### Directory\n\n`; + section += `| Title | ID | When To Use |\n|---|---|---|\n`; + const agentSummaries = []; + for (const agentId of agents) { + const agentPath = await this.findAgentPath(agentId, installDir); + if (!agentPath) continue; + const raw = await fileManager.readFile(agentPath); + const yamlMatch = raw.match(/```ya?ml\r?\n([\s\S]*?)```/); + const yamlBlock = yamlMatch ? yamlMatch[1].trim() : null; + const title = await this.getAgentTitle(agentId, installDir); + const whenToUse = yamlBlock?.match(/whenToUse:\s*"?([^\n"]+)"?/i)?.[1]?.trim() || ''; + agentSummaries.push({ agentId, title, whenToUse, yamlBlock, raw, path: agentPath }); + section += `| ${title} | ${agentId} | ${whenToUse || '—'} |\n`; + } + section += `\n`; + + // Detailed agent sections + for (const { agentId, title, whenToUse, yamlBlock, raw, path: agentPath } of agentSummaries) { + const relativePath = path.relative(installDir, agentPath).replaceAll('\\', '/'); + section += `### ${title} (id: ${agentId})\n`; + section += `Source: ${relativePath}\n\n`; + if (whenToUse) section += `- When to use: ${whenToUse}\n`; + section += `- How to activate: Mention "As ${agentId}, ..." or "Use ${title} to ..."\n\n`; + if (yamlBlock) { + section += '```yaml\n' + yamlBlock + '\n```\n\n'; + } else { + section += '```md\n' + raw.trim() + '\n```\n\n'; + } + } + + // Tasks + if (tasks && tasks.length > 0) { + section += `## Tasks\n\n`; + section += `These are reusable task briefs you can reference directly in Codex.\n\n`; + for (const taskId of tasks) { + const taskPath = await this.findTaskPath(taskId, installDir); + if (!taskPath) continue; + const raw = await fileManager.readFile(taskPath); + const relativePath = path.relative(installDir, taskPath).replaceAll('\\', '/'); + section += `### Task: ${taskId}\n`; + section += `Source: ${relativePath}\n`; + section += `- How to use: "Use task ${taskId} with the appropriate agent" and paste relevant parts as needed.\n\n`; + section += '```md\n' + raw.trim() + '\n```\n\n'; + } + } + + section += `${endMarker}\n`; + + // Write or update AGENTS.md + let finalContent = ''; + if (await fileManager.pathExists(filePath)) { + const existing = await fileManager.readFile(filePath); + if (existing.includes(startMarker) && existing.includes(endMarker)) { + // Replace existing BMAD block + const pattern = String.raw`${startMarker}[\s\S]*?${endMarker}`; + const replaced = existing.replace(new RegExp(pattern, 'm'), section); + finalContent = replaced; + } else { + // Append BMAD block to existing file + finalContent = existing.trimEnd() + `\n\n` + section; + } + } else { + // Create fresh AGENTS.md with a small header and BMAD block + finalContent += '# Project Agents\n\n'; + finalContent += 'This file provides guidance and memory for Codex CLI.\n\n'; + finalContent += section; + } + + await fileManager.writeFile(filePath, finalContent); + console.log(chalk.green('✓ Created/updated AGENTS.md for Codex CLI integration')); + console.log( + chalk.dim( + 'Codex reads AGENTS.md automatically. Run `codex` in this project to use BMAD agents.', + ), + ); + + // Optionally add helpful npm scripts if a package.json exists + try { + const pkgPath = path.join(installDir, 'package.json'); + if (await fileManager.pathExists(pkgPath)) { + const pkgRaw = await fileManager.readFile(pkgPath); + const pkg = JSON.parse(pkgRaw); + pkg.scripts = pkg.scripts || {}; + const updated = { ...pkg.scripts }; + if (!updated['bmad:refresh']) updated['bmad:refresh'] = 'bmad-method install -f -i codex'; + if (!updated['bmad:list']) updated['bmad:list'] = 'bmad-method list:agents'; + if (!updated['bmad:validate']) updated['bmad:validate'] = 'bmad-method validate'; + const changed = JSON.stringify(updated) !== JSON.stringify(pkg.scripts); + if (changed) { + const newPkg = { ...pkg, scripts: updated }; + await fileManager.writeFile(pkgPath, JSON.stringify(newPkg, null, 2) + '\n'); + console.log(chalk.green('✓ Added npm scripts: bmad:refresh, bmad:list, bmad:validate')); + } + } + } catch { + console.log( + chalk.yellow('⚠︎ Skipped adding npm scripts (package.json not writable or invalid)'), + ); + } + + // Adjust .gitignore behavior depending on Codex mode + try { + const gitignorePath = path.join(installDir, '.gitignore'); + const ignoreLines = ['# BMAD (local only)', '.bmad-core/', '.bmad-*/']; + const exists = await fileManager.pathExists(gitignorePath); + if (options.webEnabled) { + if (exists) { + let gi = await fileManager.readFile(gitignorePath); + // Remove lines that ignore BMAD dot-folders + const updated = gi + .split(/\r?\n/) + .filter((l) => !/^\s*\.bmad-core\/?\s*$/.test(l) && !/^\s*\.bmad-\*\/?\s*$/.test(l)) + .join('\n'); + if (updated !== gi) { + await fileManager.writeFile(gitignorePath, updated.trimEnd() + '\n'); + console.log(chalk.green('✓ Updated .gitignore to include .bmad-core in commits')); + } + } + } else { + // Local-only: add ignores if missing + let base = exists ? await fileManager.readFile(gitignorePath) : ''; + const haveCore = base.includes('.bmad-core/'); + const haveStar = base.includes('.bmad-*/'); + if (!haveCore || !haveStar) { + const sep = base.endsWith('\n') || base.length === 0 ? '' : '\n'; + const add = [!haveCore || !haveStar ? ignoreLines.join('\n') : ''] + .filter(Boolean) + .join('\n'); + const out = base + sep + add + '\n'; + await fileManager.writeFile(gitignorePath, out); + console.log(chalk.green('✓ Added .bmad-core/* to .gitignore for local-only Codex setup')); + } + } + } catch { + console.log(chalk.yellow('⚠︎ Could not update .gitignore (skipping)')); + } + + return true; + } + async setupCursor(installDir, selectedAgent) { const cursorRulesDir = path.join(installDir, '.cursor', 'rules', 'bmad'); const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);