diff --git a/README.md b/README.md index d4827e378..6536c5895 100644 --- a/README.md +++ b/README.md @@ -47,12 +47,12 @@ Follow the installer prompts, then open your AI IDE (Claude Code, Cursor, etc.) **Non-Interactive Installation** (for CI/CD): ```bash -npx bmad-method install --directory /path/to/project --modules bmm --tools claude-code --yes +npx bmad-method install --directory /path/to/project --modules bmm --tools codex --yes ``` [See all installation options](https://docs.bmad-method.org/how-to/non-interactive-installation/) -> **Not sure what to do?** Run `/bmad-help` — it tells you exactly what's next and what's optional. You can also ask questions like `/bmad-help I just finished the architecture, what do I do next?` +> **Not sure what to do?** Run `/bmad-help` (Codex CLI: `/prompts:bmad-help`) — it tells you exactly what's next and what's optional. You can also ask questions like `/bmad-help I just finished the architecture, what do I do next?` ## Modules diff --git a/README_CN.md b/README_CN.md index 85b42cb2f..789a07e9b 100644 --- a/README_CN.md +++ b/README_CN.md @@ -47,12 +47,12 @@ npx bmad-method install **非交互式安装**(用于 CI/CD): ```bash -npx bmad-method install --directory /path/to/project --modules bmm --tools claude-code --yes +npx bmad-method install --directory /path/to/project --modules bmm --tools codex --yes ``` [查看所有安装选项](http://docs.bmad-method.org/how-to/non-interactive-installation/) -> **不确定该做什么?** 运行 `/bmad-help` — 它会准确告诉你下一步做什么以及什么是可选的。你也可以问诸如 `/bmad-help 我刚刚完成了架构设计,接下来该做什么?` 之类的问题。 +> **不确定该做什么?** 运行 `/bmad-help`(Codex CLI 使用 `/prompts:bmad-help`)— 它会准确告诉你下一步做什么以及什么是可选的。你也可以问诸如 `/bmad-help 我刚刚完成了架构设计,接下来该做什么?` 之类的问题。 ## 模块 diff --git a/docs/how-to/non-interactive-installation.md b/docs/how-to/non-interactive-installation.md index fa7a1e7b1..71b683f1d 100644 --- a/docs/how-to/non-interactive-installation.md +++ b/docs/how-to/non-interactive-installation.md @@ -26,7 +26,7 @@ Requires [Node.js](https://nodejs.org) v20+ and `npx` (included with npm). |------|-------------|---------| | `--directory ` | Installation directory | `--directory ~/projects/myapp` | | `--modules ` | Comma-separated module IDs | `--modules bmm,bmb` | -| `--tools ` | Comma-separated tool/IDE IDs (use `none` to skip) | `--tools claude-code,cursor` or `--tools none` | +| `--tools ` | Comma-separated tool/IDE IDs (use `none` to skip) | `--tools codex,cursor` or `--tools none` | | `--custom-content ` | Comma-separated paths to custom modules | `--custom-content ~/my-module,~/another-module` | | `--action ` | Action for existing installations: `install` (default), `update`, `quick-update`, or `compile-agents` | `--action quick-update` | @@ -63,11 +63,14 @@ Available tool IDs for the `--tools` flag: Run `npx bmad-method install` interactively once to see the full current list of supported tools, or check the [platform codes configuration](https://github.com/bmad-code-org/BMAD-METHOD/blob/main/tools/cli/installers/lib/ide/platform-codes.yaml). +For `codex`, BMAD installs slash-command prompt files into `.codex/prompts` and syncs them to `~/.codex/prompts`. +Use them in Codex CLI as `/prompts:bmad-help`, `/prompts:bmad-master`, etc. + ## Installation Modes | Mode | Description | Example | |------|-------------|---------| -| Fully non-interactive | Provide all flags to skip all prompts | `npx bmad-method install --directory . --modules bmm --tools claude-code --yes` | +| Fully non-interactive | Provide all flags to skip all prompts | `npx bmad-method install --directory . --modules bmm --tools codex --yes` | | Semi-interactive | Provide some flags; BMad prompts for the rest | `npx bmad-method install --directory . --modules bmm` | | Defaults only | Accept all defaults with `-y` | `npx bmad-method install --yes` | | Without tools | Skip tool/IDE configuration | `npx bmad-method install --modules bmm --tools none` | @@ -83,7 +86,7 @@ Run `npx bmad-method install` interactively once to see the full current list of npx bmad-method install \ --directory "${GITHUB_WORKSPACE}" \ --modules bmm \ - --tools claude-code \ + --tools codex \ --user-name "CI Bot" \ --communication-language English \ --document-output-language English \ @@ -115,7 +118,7 @@ npx bmad-method install \ --directory ~/projects/myapp \ --modules bmm \ --custom-content ~/my-custom-module,~/another-module \ - --tools claude-code + --tools codex ``` ## What You Get diff --git a/docs/zh-cn/how-to/non-interactive-installation.md b/docs/zh-cn/how-to/non-interactive-installation.md index 11d57a712..9a976f6d4 100644 --- a/docs/zh-cn/how-to/non-interactive-installation.md +++ b/docs/zh-cn/how-to/non-interactive-installation.md @@ -26,7 +26,7 @@ sidebar: |------|-------------|---------| | `--directory ` | 安装目录 | `--directory ~/projects/myapp` | | `--modules ` | 逗号分隔的模块 ID | `--modules bmm,bmb` | -| `--tools ` | 逗号分隔的工具/IDE ID(使用 `none` 跳过) | `--tools claude-code,cursor` 或 `--tools none` | +| `--tools ` | 逗号分隔的工具/IDE ID(使用 `none` 跳过) | `--tools codex,cursor` 或 `--tools none` | | `--custom-content ` | 逗号分隔的自定义模块路径 | `--custom-content ~/my-module,~/another-module` | | `--action ` | 对现有安装的操作:`install`(默认)、`update`、`quick-update` 或 `compile-agents` | `--action quick-update` | @@ -63,11 +63,14 @@ sidebar: 运行一次 `npx bmad-method install` 交互式安装以查看完整的当前支持工具列表,或查看 [平台代码配置](https://github.com/bmad-code-org/BMAD-METHOD/blob/main/tools/cli/installers/lib/ide/platform-codes.yaml)。 +对于 `codex`,BMAD 会把 slash 命令提示文件安装到 `.codex/prompts`,并同步到 `~/.codex/prompts`。 +在 Codex CLI 中用 `/prompts:bmad-help`、`/prompts:bmad-master` 等方式调用。 + ## 安装模式 | 模式 | 描述 | 示例 | |------|-------------|---------| -| 完全非交互式 | 提供所有标志以跳过所有提示 | `npx bmad-method install --directory . --modules bmm --tools claude-code --yes` | +| 完全非交互式 | 提供所有标志以跳过所有提示 | `npx bmad-method install --directory . --modules bmm --tools codex --yes` | | 半交互式 | 提供部分标志;BMad 提示其余部分 | `npx bmad-method install --directory . --modules bmm` | | 仅使用默认值 | 使用 `-y` 接受所有默认值 | `npx bmad-method install --yes` | | 不包含工具 | 跳过工具/IDE 配置 | `npx bmad-method install --modules bmm --tools none` | @@ -83,7 +86,7 @@ sidebar: npx bmad-method install \ --directory "${GITHUB_WORKSPACE}" \ --modules bmm \ - --tools claude-code \ + --tools codex \ --user-name "CI Bot" \ --communication-language English \ --document-output-language English \ @@ -115,7 +118,7 @@ npx bmad-method install \ --directory ~/projects/myapp \ --modules bmm \ --custom-content ~/my-custom-module,~/another-module \ - --tools claude-code + --tools codex ``` ## 安装结果 diff --git a/test/test-installation-components.js b/test/test-installation-components.js index 1654bc110..4d4a0258f 100644 --- a/test/test-installation-components.js +++ b/test/test-installation-components.js @@ -54,6 +54,13 @@ async function createTestBmadFixture() { // Minimal workflow manifest (generators check for this) await fs.ensureDir(path.join(fixtureDir, '_config')); await fs.writeFile(path.join(fixtureDir, '_config', 'workflow-manifest.csv'), ''); + await fs.writeFile( + path.join(fixtureDir, '_config', 'skill-manifest.csv'), + [ + 'canonicalId,name,description,module,path,install_to_bmad', + '"bmad-help","bmad-help","Help workflow","core","_bmad/core/tasks/bmad-help/SKILL.md","true"', + ].join('\n'), + ); // Minimal compiled agent for core/agents (contains 0) { + await this.syncBmadArtifacts(targetPath, projectDir, config.sync_targets, options); } await this.printSummary(results, target_dir, options); @@ -638,15 +648,8 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}} async installVerbatimSkills(projectDir, bmadDir, targetPath, config) { const bmadFolderName = path.basename(bmadDir); const bmadPrefix = bmadFolderName + '/'; - const csvPath = path.join(bmadDir, '_config', 'skill-manifest.csv'); - - if (!(await fs.pathExists(csvPath))) return 0; - - const csvContent = await fs.readFile(csvPath, 'utf8'); - const records = csv.parse(csvContent, { - columns: true, - skip_empty_lines: true, - }); + const records = await this.loadSkillManifestRecords(bmadDir); + if (!records || records.length === 0) return 0; let count = 0; @@ -699,6 +702,119 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}} return count; } + /** + * Load skill-manifest CSV records. + * @param {string} bmadDir - BMAD installation directory + * @returns {Promise} Parsed CSV records + */ + async loadSkillManifestRecords(bmadDir) { + const csvPath = path.join(bmadDir, '_config', 'skill-manifest.csv'); + + if (!(await fs.pathExists(csvPath))) return []; + + const csvContent = await fs.readFile(csvPath, 'utf8'); + return csv.parse(csvContent, { + columns: true, + skip_empty_lines: true, + }); + } + + /** + * Install skill-manifest entries as flat command files. + * This is useful for platforms that support slash-command prompts but not + * native skill directory formats. + * @param {string} targetPath - Target directory path + * @param {string} bmadDir - BMAD installation directory + * @param {string} templateType - Template type to use + * @param {Object} config - Installation configuration + * @returns {Promise} Count of skills written as command files + */ + async installSkillManifestArtifacts(targetPath, bmadDir, templateType, config = {}) { + const records = await this.loadSkillManifestRecords(bmadDir); + if (!records || records.length === 0) return 0; + + const { content: template, extension } = await this.loadTemplate(templateType, 'skill', config, 'default-skill'); + + const bmadPrefix = `${this.bmadFolderName}/`; + let count = 0; + + for (const record of records) { + const canonicalId = record?.canonicalId; + if (!canonicalId) continue; + + let relativePath = String(record.path || '').replaceAll('\\', '/'); + if (relativePath.startsWith(bmadPrefix)) { + relativePath = relativePath.slice(bmadPrefix.length); + } else if (relativePath.startsWith('_bmad/')) { + relativePath = relativePath.slice(6); + } else if (relativePath.startsWith('bmad/')) { + relativePath = relativePath.slice(5); + } + + const artifact = { + type: 'skill', + name: canonicalId, + module: record.module || 'core', + description: record.description || `${canonicalId} skill`, + path: relativePath, + relativePath, + canonicalId, + }; + + const content = this.renderTemplate(template, artifact); + const filename = this.generateFilename(artifact, 'skill', extension); + const filePath = path.join(targetPath, filename); + + await this.writeFile(filePath, content); + count++; + } + + return count; + } + + /** + * Mirror generated BMAD files into additional target directories. + * Only BMAD-prefixed entries are synced to avoid touching user files. + * @param {string} sourcePath - Primary target directory + * @param {string} projectDir - Project directory + * @param {Array} syncTargets - Additional target directories + * @param {Object} options - Setup options + */ + async syncBmadArtifacts(sourcePath, projectDir, syncTargets, options = {}) { + let sourceEntries = []; + try { + sourceEntries = await fs.readdir(sourcePath); + } catch { + return; + } + + const bmadEntries = sourceEntries.filter((entry) => typeof entry === 'string' && entry.startsWith('bmad')); + if (bmadEntries.length === 0) return; + + for (const syncTarget of syncTargets) { + const targetPath = this.resolveTargetPath(projectDir, syncTarget); + if (path.resolve(targetPath) === path.resolve(sourcePath)) continue; + + await this.ensureDir(targetPath); + + // Remove stale BMAD entries before sync so mirrored directories stay exact. + const existing = await fs.readdir(targetPath); + for (const entry of existing) { + if (typeof entry === 'string' && entry.startsWith('bmad') && !entry.startsWith('bmad-os-')) { + await fs.remove(path.join(targetPath, entry)); + } + } + + for (const entry of bmadEntries) { + await fs.copy(path.join(sourcePath, entry), path.join(targetPath, entry), { overwrite: true }); + } + + if (!options.silent) { + await prompts.log.message(` Synced BMAD files to ${syncTarget}`); + } + } + } + /** * Print installation summary * @param {Object} results - Installation results @@ -766,6 +882,13 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}} } else if (this.installerConfig?.target_dir) { await this.cleanupTarget(projectDir, this.installerConfig.target_dir, options); } + + // Clean any mirrored targets used for command synchronization. + if (Array.isArray(this.installerConfig?.sync_targets)) { + for (const syncTarget of this.installerConfig.sync_targets) { + await this.cleanupTarget(projectDir, syncTarget, options); + } + } } /** @@ -777,6 +900,21 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}} return p.startsWith('~') || path.isAbsolute(p); } + /** + * Resolve a configured target path to an absolute path. + * Supports project-relative, absolute, and ~/home-relative targets. + * @param {string} projectDir - Project directory + * @param {string} targetDir - Configured target directory + * @returns {string} Absolute resolved path + */ + resolveTargetPath(projectDir, targetDir) { + if (!targetDir) return projectDir; + if (targetDir === '~') return os.homedir(); + if (targetDir.startsWith('~/')) return path.join(os.homedir(), targetDir.slice(2)); + if (path.isAbsolute(targetDir)) return targetDir; + return path.join(projectDir, targetDir); + } + /** * Warn about stale BMAD files in a global legacy directory (never auto-deletes) * @param {string} legacyDir - Legacy directory path (may start with ~) @@ -809,7 +947,7 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}} * @param {string} targetDir - Target directory to clean */ async cleanupTarget(projectDir, targetDir, options = {}) { - const targetPath = path.join(projectDir, targetDir); + const targetPath = this.resolveTargetPath(projectDir, targetDir); if (!(await fs.pathExists(targetPath))) { return; @@ -984,29 +1122,46 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}} * @returns {Promise} Path to conflicting directory, or null if clean */ async findAncestorConflict(projectDir) { - const targetDir = this.installerConfig?.target_dir; - if (!targetDir) return null; + const targetDirs = []; + if (this.installerConfig?.target_dir) { + targetDirs.push(this.installerConfig.target_dir); + } + if (Array.isArray(this.installerConfig?.targets)) { + for (const target of this.installerConfig.targets) { + if (target?.target_dir) targetDirs.push(target.target_dir); + } + } + + if (targetDirs.length === 0) return null; const resolvedProject = await fs.realpath(path.resolve(projectDir)); - let current = path.dirname(resolvedProject); - const root = path.parse(current).root; - while (current !== root && current.length > root.length) { - const candidatePath = path.join(current, targetDir); - try { - if (await fs.pathExists(candidatePath)) { - const entries = await fs.readdir(candidatePath); - const hasBmad = entries.some( - (e) => typeof e === 'string' && e.toLowerCase().startsWith('bmad') && !e.toLowerCase().startsWith('bmad-os-'), - ); - if (hasBmad) { - return candidatePath; - } - } - } catch { - // Can't read directory — skip + for (const targetDir of targetDirs) { + // Ancestor conflicts only apply to project-relative directories. + if (this.isGlobalPath(targetDir) || path.isAbsolute(targetDir)) { + continue; + } + + let current = path.dirname(resolvedProject); + const root = path.parse(current).root; + + while (current !== root && current.length > root.length) { + const candidatePath = path.join(current, targetDir); + try { + if (await fs.pathExists(candidatePath)) { + const entries = await fs.readdir(candidatePath); + const hasBmad = entries.some( + (e) => typeof e === 'string' && e.toLowerCase().startsWith('bmad') && !e.toLowerCase().startsWith('bmad-os-'), + ); + if (hasBmad) { + return candidatePath; + } + } + } catch { + // Can't read directory — skip + } + current = path.dirname(current); } - current = path.dirname(current); } return null; diff --git a/tools/cli/installers/lib/ide/platform-codes.yaml b/tools/cli/installers/lib/ide/platform-codes.yaml index 9d5f171f1..9c94f73d9 100644 --- a/tools/cli/installers/lib/ide/platform-codes.yaml +++ b/tools/cli/installers/lib/ide/platform-codes.yaml @@ -70,13 +70,14 @@ platforms: description: "OpenAI Codex integration" installer: legacy_targets: - - .codex/prompts - - ~/.codex/prompts - target_dir: .agents/skills + - .agents/skills + target_dir: .codex/prompts template_type: default - skill_format: true ancestor_conflict_check: true artifact_types: [agents, workflows, tasks] + install_skill_manifest_as_artifacts: true + sync_targets: + - ~/.codex/prompts codebuddy: name: "CodeBuddy" diff --git a/tools/cli/installers/lib/ide/templates/combined/default-skill.md b/tools/cli/installers/lib/ide/templates/combined/default-skill.md new file mode 100644 index 000000000..da32faa23 --- /dev/null +++ b/tools/cli/installers/lib/ide/templates/combined/default-skill.md @@ -0,0 +1,11 @@ +--- +name: '{{name}}' +description: '{{description}}' +--- + +# {{name}} + +Read the entire skill file at: {project-root}/{{bmadFolderName}}/{{path}} + +Follow all instructions in the skill file exactly as written. +If the skill references additional files (for example `workflow.md`), load and follow those as well. diff --git a/tools/docs/native-skills-migration-checklist.md b/tools/docs/native-skills-migration-checklist.md index 2f0f31344..64466c244 100644 --- a/tools/docs/native-skills-migration-checklist.md +++ b/tools/docs/native-skills-migration-checklist.md @@ -7,7 +7,7 @@ Scope: migrate the BMAD-supported platforms that fully support the Agent Skills Current branch status: - `Claude Code` has already been moved to `.claude/skills` -- `Codex CLI` has already been moved to `.agents/skills` +- `Codex CLI` now uses `.codex/prompts` slash commands (not Agent Skills format) This checklist now includes those completed platforms plus the remaining full-support platforms. @@ -26,15 +26,16 @@ Support assumption: full Agent Skills support. BMAD has already migrated from `. ## Codex CLI -Support assumption: full Agent Skills support. BMAD has already migrated from `.codex/prompts` to `.agents/skills`. +Support assumption: Codex custom slash commands are file-based prompts. BMAD targets `.codex/prompts` and syncs to `~/.codex/prompts` for Codex discovery. **Install:** `npm install -g @openai/codex` -- [x] Confirm current implementation still matches Codex CLI skills expectations -- [x] Confirm legacy cleanup for project and global `.codex/prompts` +- [x] Confirm current implementation matches Codex CLI prompt command expectations +- [x] Ensure skill-manifest entries (for example `bmad-help`) are also emitted as prompt commands +- [x] Confirm legacy cleanup for prior project-local `.agents/skills` output and global `~/.codex/prompts` warnings - [x] Test fresh install -- [x] Test reinstall/upgrade from legacy prompt output -- [x] Confirm ancestor conflict protection because Codex inherits parent-directory `.agents/skills` +- [x] Test reinstall/upgrade from legacy skills output +- [x] Confirm ancestor conflict protection because Codex can inherit parent-directory `.codex/prompts` - [x] Implement/extend automated tests as needed ## Cursor