bmalph 2.2.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +111 -30
  2. package/dist/cli.js +1 -0
  3. package/dist/commands/doctor.d.ts +14 -2
  4. package/dist/commands/doctor.js +83 -35
  5. package/dist/commands/init.d.ts +1 -0
  6. package/dist/commands/init.js +74 -7
  7. package/dist/commands/upgrade.js +8 -5
  8. package/dist/installer.d.ts +15 -4
  9. package/dist/installer.js +190 -101
  10. package/dist/platform/aider.d.ts +2 -0
  11. package/dist/platform/aider.js +71 -0
  12. package/dist/platform/claude-code.d.ts +2 -0
  13. package/dist/platform/claude-code.js +88 -0
  14. package/dist/platform/codex.d.ts +2 -0
  15. package/dist/platform/codex.js +67 -0
  16. package/dist/platform/copilot.d.ts +2 -0
  17. package/dist/platform/copilot.js +71 -0
  18. package/dist/platform/cursor.d.ts +2 -0
  19. package/dist/platform/cursor.js +71 -0
  20. package/dist/platform/detect.d.ts +7 -0
  21. package/dist/platform/detect.js +23 -0
  22. package/dist/platform/index.d.ts +4 -0
  23. package/dist/platform/index.js +3 -0
  24. package/dist/platform/registry.d.ts +4 -0
  25. package/dist/platform/registry.js +27 -0
  26. package/dist/platform/resolve.d.ts +8 -0
  27. package/dist/platform/resolve.js +24 -0
  28. package/dist/platform/types.d.ts +41 -0
  29. package/dist/platform/types.js +7 -0
  30. package/dist/platform/windsurf.d.ts +2 -0
  31. package/dist/platform/windsurf.js +71 -0
  32. package/dist/transition/artifacts.js +1 -1
  33. package/dist/transition/specs-changelog.js +4 -1
  34. package/dist/utils/config.d.ts +2 -0
  35. package/dist/utils/validate.js +16 -0
  36. package/package.json +1 -1
  37. package/ralph/drivers/claude-code.sh +118 -0
  38. package/ralph/drivers/codex.sh +81 -0
  39. package/ralph/ralph_import.sh +11 -0
  40. package/ralph/ralph_loop.sh +37 -64
  41. package/ralph/templates/ralphrc.template +7 -0
package/README.md CHANGED
@@ -27,11 +27,30 @@ bmalph provides:
27
27
  - `bmalph doctor` — Check installation health
28
28
  - `/bmalph-implement` — Transition from BMAD to Ralph
29
29
 
30
+ ## Supported Platforms
31
+
32
+ bmalph works with multiple AI coding assistants. Each platform gets BMAD planning (Phases 1-3). The Ralph autonomous loop (Phase 4) requires a CLI-based platform.
33
+
34
+ | Platform | ID | Tier | Instructions File | Commands |
35
+ | -------------- | ------------- | ----------------- | --------------------------------- | ----------------------------- |
36
+ | Claude Code | `claude-code` | full | `CLAUDE.md` | `.claude/commands/` directory |
37
+ | OpenAI Codex | `codex` | full | `AGENTS.md` | Inline in instructions file |
38
+ | Cursor | `cursor` | instructions-only | `.cursor/rules/bmad.mdc` | None |
39
+ | Windsurf | `windsurf` | instructions-only | `.windsurf/rules/bmad.md` | None |
40
+ | GitHub Copilot | `copilot` | instructions-only | `.github/copilot-instructions.md` | None |
41
+ | Aider | `aider` | instructions-only | `CONVENTIONS.md` | None |
42
+
43
+ **Tiers:**
44
+
45
+ - **full** — Phases 1-4. BMAD planning + Ralph autonomous implementation loop.
46
+ - **instructions-only** — Phases 1-3. BMAD planning only. Ralph is not available.
47
+
30
48
  ## Prerequisites
31
49
 
32
50
  - Node.js 20+
33
51
  - Bash (WSL or Git Bash on Windows)
34
- - Claude Code (`claude` in PATH) needed for Ralph loop
52
+ - A supported AI coding platform (see table above)
53
+ - For Ralph loop (Phase 4): Claude Code (`claude`) or Codex CLI (`codex`) in PATH
35
54
 
36
55
  ## Installation
37
56
 
@@ -44,9 +63,9 @@ npm install -g bmalph
44
63
  ```bash
45
64
  cd my-project
46
65
  bmalph init --name my-project
47
- # Use /bmalph slash command in Claude Code to navigate phases
48
- # ... work through BMAD phases 1-3 ...
49
- # Use /bmalph-implement to transition and start Ralph
66
+
67
+ # To target a specific platform, add --platform (e.g. codex, cursor, windsurf)
68
+ # Without --platform, bmalph auto-detects or prompts interactively
50
69
  ```
51
70
 
52
71
  ## Workflow
@@ -58,17 +77,27 @@ cd my-project
58
77
  bmalph init
59
78
  ```
60
79
 
80
+ **Platform resolution:** `--platform` flag > auto-detect from project markers > interactive prompt > default `claude-code`
81
+
61
82
  This installs:
62
83
 
63
84
  - `_bmad/` — BMAD agents and workflows
64
- - `.ralph/` — Ralph loop, libs, templates
65
- - `bmalph/` — State management (config.json)
66
- - Updates `CLAUDE.md` with BMAD workflow instructions
67
- - Installs slash commands in `.claude/commands/`
85
+ - `.ralph/` — Ralph loop, libs, templates (drivers for claude-code and codex only)
86
+ - `bmalph/` — State management (config.json, stores selected platform)
87
+ - Updates the platform's instructions file with BMAD workflow instructions (e.g. `CLAUDE.md`, `AGENTS.md`, `.cursor/rules/bmad.mdc`)
88
+ - Installs slash commands for supported platforms (Claude Code: `.claude/commands/` directory; Codex: inline in `AGENTS.md`; other platforms: commands not installed)
89
+
90
+ ### Migrating from standalone BMAD
91
+
92
+ If you already have BMAD installed (a `_bmad/` directory), `bmalph init` works as a migration path:
93
+
94
+ - `_bmad/` (framework files) will be replaced with the bmalph-managed version
95
+ - `_bmad-output/` (your planning artifacts: PRDs, architecture, stories) is not touched
96
+ - If you've customized framework files inside `_bmad/`, commit first so you can review changes with `git diff`
68
97
 
69
98
  ### Step 2: Plan with BMAD (Phases 1-3)
70
99
 
71
- Work interactively in Claude Code with BMAD agents. Use the `/bmalph` slash command to see your current phase, available commands, and advance phases.
100
+ Work interactively with BMAD agents in your AI coding assistant. On Claude Code, use the `/bmalph` slash command to see your current phase and available commands. On other platforms, ask the agent about BMAD phases or run `bmalph status` in terminal.
72
101
 
73
102
  | Phase | Agent | Commands |
74
103
  | ------------- | ---------------- | ------------------ |
@@ -124,7 +153,9 @@ Available in any phase for supporting tasks:
124
153
 
125
154
  ### Step 3: Implement with Ralph (Phase 4)
126
155
 
127
- Use the `/bmalph-implement` slash command in Claude Code.
156
+ > **Note:** Ralph is only available on **full** tier platforms (Claude Code, OpenAI Codex). Instructions-only platforms (Cursor, Windsurf, Copilot, Aider) support Phases 1-3 only.
157
+
158
+ Use the `/bmalph-implement` slash command in Claude Code, or run the transition from the BMAD agents in Codex.
128
159
 
129
160
  This transitions your BMAD artifacts into Ralph's format:
130
161
 
@@ -181,10 +212,11 @@ BMAD (add Epic 2) → /bmalph-implement → Ralph sees changes + picks up Epic 2
181
212
 
182
213
  ### init options
183
214
 
184
- | Flag | Description | Default |
185
- | -------------------------- | ------------------- | -------------- |
186
- | `-n, --name <name>` | Project name | directory name |
187
- | `-d, --description <desc>` | Project description | (prompted) |
215
+ | Flag | Description | Default |
216
+ | -------------------------- | ---------------------------------------------------------------------------------- | -------------- |
217
+ | `-n, --name <name>` | Project name | directory name |
218
+ | `-d, --description <desc>` | Project description | (prompted) |
219
+ | `--platform <id>` | Target platform (`claude-code`, `codex`, `cursor`, `windsurf`, `copilot`, `aider`) | auto-detect |
188
220
 
189
221
  ### upgrade options
190
222
 
@@ -195,7 +227,13 @@ BMAD (add Epic 2) → /bmalph-implement → Ralph sees changes + picks up Epic 2
195
227
 
196
228
  ## Slash Commands
197
229
 
198
- bmalph installs 47 BMAD slash commands. Key commands:
230
+ bmalph installs 47 BMAD slash commands. Command delivery varies by platform:
231
+
232
+ - **Claude Code** — installed as files in `.claude/commands/` (invoke with `/command-name`)
233
+ - **OpenAI Codex** — inlined in `AGENTS.md` (reference agents by name)
234
+ - **Cursor, Windsurf, Copilot, Aider** — not delivered as slash commands; use agents by name in chat
235
+
236
+ Key commands (Claude Code syntax):
199
237
 
200
238
  | Command | Description |
201
239
  | ----------------------- | ----------------------------------- |
@@ -239,12 +277,15 @@ project/
239
277
  │ ├── planning-artifacts/ # PRD, architecture, stories
240
278
  │ ├── implementation-artifacts/ # Sprint plans (optional)
241
279
  │ └── brainstorming/ # Brainstorm sessions (optional)
242
- ├── .ralph/ # Ralph autonomous loop
280
+ ├── .ralph/ # Ralph autonomous loop (drivers for claude-code and codex only)
243
281
  │ ├── ralph_loop.sh # Main loop script
244
282
  │ ├── ralph_import.sh # Import requirements into Ralph
245
283
  │ ├── ralph_monitor.sh # Monitor loop progress
246
284
  │ ├── .ralphrc # Ralph configuration
247
285
  │ ├── RALPH-REFERENCE.md # Ralph usage reference
286
+ │ ├── drivers/ # Platform driver scripts
287
+ │ │ ├── claude-code.sh # Claude Code driver (uses `claude`)
288
+ │ │ └── codex.sh # OpenAI Codex driver (uses `codex exec`)
248
289
  │ ├── lib/ # Circuit breaker, response analyzer
249
290
  │ ├── specs/ # Copied from _bmad-output during transition
250
291
  │ ├── logs/ # Loop execution logs
@@ -254,16 +295,23 @@ project/
254
295
  │ ├── @AGENT.md # Agent build instructions
255
296
  │ └── @fix_plan.md # Generated task list (after /bmalph-implement)
256
297
  ├── bmalph/ # State management
257
- │ ├── config.json # Project config (name, description)
298
+ │ ├── config.json # Project config (name, description, platform)
258
299
  │ └── state/ # Phase tracking data
259
- ├── .claude/
260
- │ └── commands/ # Slash commands for Claude Code
261
- └── CLAUDE.md # Updated with BMAD instructions
300
+ ├── .claude/ # Claude Code specific
301
+ │ └── commands/ # Slash commands (claude-code only)
302
+ └── <instructions file> # Varies by platform (see Supported Platforms)
262
303
  ```
263
304
 
305
+ The instructions file and command directory depend on the configured platform. See the [Supported Platforms](#supported-platforms) table for details.
306
+
264
307
  ## How Ralph Works
265
308
 
266
- Ralph is a bash loop that spawns fresh Claude Code instances:
309
+ Ralph is a bash loop that spawns fresh AI coding sessions using a **platform driver** matching the configured platform:
310
+
311
+ - **Claude Code driver** — invokes `claude` with `--allowedTools` and session resume
312
+ - **Codex driver** — invokes `codex exec` with `--sandbox workspace-write`
313
+
314
+ Each iteration:
267
315
 
268
316
  1. Pick the next unchecked story from `@fix_plan.md`
269
317
  2. Implement with TDD (tests first, then code)
@@ -313,12 +361,14 @@ ls -la .ralph/
313
361
 
314
362
  ### Common Issues
315
363
 
316
- | Scenario | Solution |
317
- | ---------------------------- | --------------------------------------------------------- |
318
- | Commands fail before init | Run `bmalph init` first |
319
- | Transition finds no stories | Create stories in Phase 3 with `/create-epics-stories` |
320
- | Ralph stops mid-loop | Circuit breaker detected stagnation. Check `.ralph/logs/` |
321
- | Doctor reports version drift | Run `bmalph upgrade` to update bundled assets |
364
+ | Scenario | Solution |
365
+ | ----------------------------- | -------------------------------------------------------------- |
366
+ | Commands fail before init | Run `bmalph init` first |
367
+ | Transition finds no stories | Create stories in Phase 3 with `/create-epics-stories` |
368
+ | Ralph stops mid-loop | Circuit breaker detected stagnation. Check `.ralph/logs/` |
369
+ | Doctor reports version drift | Run `bmalph upgrade` to update bundled assets |
370
+ | Wrong platform detected | Re-run `bmalph init --platform <id>` with the correct platform |
371
+ | Ralph unavailable on platform | Ralph requires a full tier platform (claude-code or codex) |
322
372
 
323
373
  ### Reset Installation
324
374
 
@@ -326,8 +376,16 @@ If something goes wrong, you can manually reset:
326
376
 
327
377
  ```bash
328
378
  # Remove bmalph directories (preserves your project code)
329
- rm -rf _bmad .ralph bmalph .claude/commands/
330
- # Note: manually remove the bmalph section from CLAUDE.md and .gitignore entries
379
+ rm -rf _bmad .ralph bmalph
380
+
381
+ # Also remove platform-specific files:
382
+ # Claude Code: rm -rf .claude/commands/ and remove bmalph section from CLAUDE.md
383
+ # Codex: remove bmalph sections from AGENTS.md
384
+ # Cursor: rm .cursor/rules/bmad.mdc
385
+ # Windsurf: rm .windsurf/rules/bmad.md
386
+ # Copilot: remove bmalph sections from .github/copilot-instructions.md
387
+ # Aider: remove bmalph sections from CONVENTIONS.md
388
+ # See the Supported Platforms table for your platform's files.
331
389
 
332
390
  # Reinitialize
333
391
  bmalph init
@@ -338,12 +396,17 @@ bmalph init
338
396
  ### Initialize a new project
339
397
 
340
398
  ```bash
341
- # Interactive mode (prompts for name/description)
399
+ # Interactive mode (prompts for name/description, auto-detects platform)
342
400
  bmalph init
343
401
 
344
402
  # Non-interactive mode
345
403
  bmalph init --name my-app --description "My awesome app"
346
404
 
405
+ # Specify platform explicitly
406
+ bmalph init --name my-app --platform codex
407
+ bmalph init --name my-app --platform cursor
408
+ bmalph init --name my-app --platform windsurf
409
+
347
410
  # Preview what would be created
348
411
  bmalph init --dry-run
349
412
  ```
@@ -370,6 +433,8 @@ bmalph upgrade --dry-run
370
433
 
371
434
  ### After init: Next steps
372
435
 
436
+ **Claude Code:**
437
+
373
438
  ```bash
374
439
  # 1. Open Claude Code in your project
375
440
  claude
@@ -389,6 +454,22 @@ claude
389
454
  bash .ralph/ralph_loop.sh
390
455
  ```
391
456
 
457
+ **Other platforms:**
458
+
459
+ ```bash
460
+ # 1. Open your project in your AI coding assistant
461
+
462
+ # 2. Ask the agent about BMAD phases to start planning
463
+ # Or check status from terminal: bmalph status
464
+
465
+ # 3. Reference BMAD agents by name (analyst, pm, architect)
466
+ # Follow phases: Analysis → Planning → Solutioning
467
+
468
+ # 4. For full tier platforms (Codex), transition via BMAD agents
469
+ # then start Ralph:
470
+ bash .ralph/ralph_loop.sh
471
+ ```
472
+
392
473
  ## Contributing
393
474
 
394
475
  See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, test workflow, and commit guidelines.
package/dist/cli.js CHANGED
@@ -55,6 +55,7 @@ program
55
55
  .description("Initialize bmalph in the current project")
56
56
  .option("-n, --name <name>", "Project name")
57
57
  .option("-d, --description <desc>", "Project description")
58
+ .option("--platform <id>", "Target platform (claude-code, codex, cursor, windsurf, copilot, aider)")
58
59
  .option("--dry-run", "Preview changes without writing files")
59
60
  .action(async (opts) => initCommand({ ...opts, projectDir: await resolveAndValidateProjectDir() }));
60
61
  program
@@ -1,3 +1,4 @@
1
+ import type { Platform } from "../platform/types.js";
1
2
  /**
2
3
  * Result of a single doctor check.
3
4
  */
@@ -32,8 +33,19 @@ interface DoctorResult {
32
33
  }
33
34
  export declare function runDoctor(options: DoctorOptions): Promise<DoctorResult>;
34
35
  /**
35
- * Registry of all doctor checks in execution order.
36
- * Each check has a unique ID and a run function that takes a project directory.
36
+ * Build the full check registry for a given platform.
37
+ * Core checks + platform doctor checks + trailing checks.
38
+ */
39
+ export declare function buildCheckRegistry(platform: Platform): CheckDefinition[];
40
+ /**
41
+ * Static registry for backward compatibility with existing tests.
42
+ * Uses claude-code platform checks (slash-command + claude-md).
43
+ *
44
+ * Note: The check ids here ("slash-command", "claude-md") intentionally differ
45
+ * from the live buildCheckRegistry path which uses platform-provided ids
46
+ * ("instructions-file"). This is for test backward compatibility only.
47
+ *
48
+ * @deprecated Use `buildCheckRegistry(platform)` for platform-aware checks.
37
49
  */
38
50
  export declare const CHECK_REGISTRY: CheckDefinition[];
39
51
  export {};
@@ -9,6 +9,7 @@ import { checkUpstream, getSkipReason } from "../utils/github.js";
9
9
  import { isEnoent, formatError, withErrorHandling } from "../utils/errors.js";
10
10
  import { validateCircuitBreakerState, validateRalphSession, validateRalphApiStatus, } from "../utils/validate.js";
11
11
  import { SESSION_AGE_WARNING_MS, API_USAGE_WARNING_PERCENT, CONFIG_FILE, RALPH_STATUS_FILE, } from "../utils/constants.js";
12
+ import { resolveProjectPlatform } from "../platform/resolve.js";
12
13
  export async function doctorCommand(options) {
13
14
  await withErrorHandling(async () => {
14
15
  const { failed } = await runDoctor(options);
@@ -19,8 +20,12 @@ export async function doctorCommand(options) {
19
20
  }
20
21
  export async function runDoctor(options) {
21
22
  const projectDir = options.projectDir;
22
- // Run all checks from the registry
23
- const results = await Promise.all(CHECK_REGISTRY.map((check) => check.run(projectDir)));
23
+ // Resolve platform for this project
24
+ const platform = await resolveProjectPlatform(projectDir);
25
+ // Build the check list: core checks + platform-specific checks
26
+ const checks = buildCheckRegistry(platform);
27
+ // Run all checks
28
+ const results = await Promise.all(checks.map((check) => check.run(projectDir)));
24
29
  // Output
25
30
  const passed = results.filter((r) => r.passed).length;
26
31
  const failed = results.filter((r) => !r.passed).length;
@@ -71,7 +76,7 @@ async function checkBashAvailable() {
71
76
  }
72
77
  }
73
78
  // =============================================================================
74
- // Check functions - each conforms to CheckFunction signature
79
+ // Core check functions - platform-independent
75
80
  // =============================================================================
76
81
  async function checkNodeVersion(_projectDir) {
77
82
  const major = parseInt(process.versions.node.split(".")[0]);
@@ -104,9 +109,6 @@ async function checkRalphLoop(projectDir) {
104
109
  async function checkRalphLib(projectDir) {
105
110
  return checkDir(join(projectDir, ".ralph/lib"), ".ralph/lib/ directory present", "Run: bmalph upgrade");
106
111
  }
107
- async function checkSlashCommand(projectDir) {
108
- return checkFileExists(join(projectDir, ".claude/commands/bmalph.md"), ".claude/commands/bmalph.md present", "Run: bmalph init");
109
- }
110
112
  async function checkConfig(projectDir) {
111
113
  const label = "bmalph/config.json exists and valid";
112
114
  const hint = "Run: bmalph init";
@@ -135,12 +137,6 @@ async function checkDir(dirPath, label, hint) {
135
137
  return { label, passed: false, detail: `error: ${formatError(err)}`, hint };
136
138
  }
137
139
  }
138
- async function checkFileExists(filePath, label, hint) {
139
- if (await exists(filePath)) {
140
- return { label, passed: true };
141
- }
142
- return { label, passed: false, detail: "not found", hint };
143
- }
144
140
  async function checkFileHasContent(filePath, label, hint) {
145
141
  try {
146
142
  const content = await readFile(filePath, "utf-8");
@@ -153,23 +149,6 @@ async function checkFileHasContent(filePath, label, hint) {
153
149
  return { label, passed: false, detail: `error: ${formatError(err)}`, hint };
154
150
  }
155
151
  }
156
- async function checkClaudeMd(projectDir) {
157
- const label = "CLAUDE.md contains BMAD snippet";
158
- const hint = "Run: bmalph init";
159
- try {
160
- const content = await readFile(join(projectDir, "CLAUDE.md"), "utf-8");
161
- if (content.includes("BMAD-METHOD Integration")) {
162
- return { label, passed: true };
163
- }
164
- return { label, passed: false, detail: "missing BMAD-METHOD Integration section", hint };
165
- }
166
- catch (err) {
167
- if (isEnoent(err)) {
168
- return { label, passed: false, detail: "CLAUDE.md not found", hint };
169
- }
170
- return { label, passed: false, detail: `error: ${formatError(err)}`, hint };
171
- }
172
- }
173
152
  async function checkGitignore(projectDir) {
174
153
  const label = ".gitignore has required entries";
175
154
  const required = [".ralph/logs/", "_bmad-output/"];
@@ -412,21 +391,23 @@ async function checkUpstreamGitHubStatus(_projectDir) {
412
391
  }
413
392
  }
414
393
  // =============================================================================
415
- // Check Registry - defines all checks in execution order
394
+ // Check Registry - core checks + platform-specific checks
416
395
  // =============================================================================
417
396
  /**
418
- * Registry of all doctor checks in execution order.
419
- * Each check has a unique ID and a run function that takes a project directory.
397
+ * Core checks that apply to every platform.
420
398
  */
421
- export const CHECK_REGISTRY = [
399
+ const CORE_CHECKS = [
422
400
  { id: "node-version", run: checkNodeVersion },
423
401
  { id: "bash-available", run: checkBash },
424
402
  { id: "config-valid", run: checkConfig },
425
403
  { id: "bmad-dir", run: checkBmadDir },
426
404
  { id: "ralph-loop", run: checkRalphLoop },
427
405
  { id: "ralph-lib", run: checkRalphLib },
428
- { id: "slash-command", run: checkSlashCommand },
429
- { id: "claude-md", run: checkClaudeMd },
406
+ ];
407
+ /**
408
+ * Checks that run after platform-specific checks.
409
+ */
410
+ const TRAILING_CHECKS = [
430
411
  { id: "gitignore", run: checkGitignore },
431
412
  { id: "version-marker", run: checkVersionMarker },
432
413
  { id: "upstream-versions", run: checkUpstreamVersions },
@@ -435,3 +416,70 @@ export const CHECK_REGISTRY = [
435
416
  { id: "api-calls", run: checkApiCalls },
436
417
  { id: "upstream-github", run: checkUpstreamGitHubStatus },
437
418
  ];
419
+ /**
420
+ * Build the full check registry for a given platform.
421
+ * Core checks + platform doctor checks + trailing checks.
422
+ */
423
+ export function buildCheckRegistry(platform) {
424
+ const platformChecks = platform.getDoctorChecks().map((pc) => ({
425
+ id: pc.id,
426
+ run: async (projectDir) => {
427
+ const result = await pc.check(projectDir);
428
+ return {
429
+ label: pc.label,
430
+ passed: result.passed,
431
+ detail: result.detail,
432
+ hint: result.hint,
433
+ };
434
+ },
435
+ }));
436
+ return [...CORE_CHECKS, ...platformChecks, ...TRAILING_CHECKS];
437
+ }
438
+ /**
439
+ * Static registry for backward compatibility with existing tests.
440
+ * Uses claude-code platform checks (slash-command + claude-md).
441
+ *
442
+ * Note: The check ids here ("slash-command", "claude-md") intentionally differ
443
+ * from the live buildCheckRegistry path which uses platform-provided ids
444
+ * ("instructions-file"). This is for test backward compatibility only.
445
+ *
446
+ * @deprecated Use `buildCheckRegistry(platform)` for platform-aware checks.
447
+ */
448
+ export const CHECK_REGISTRY = (() => {
449
+ // Inline claude-code platform checks to avoid async import at module level
450
+ const slashCommandCheck = {
451
+ id: "slash-command",
452
+ run: async (projectDir) => {
453
+ if (await exists(join(projectDir, ".claude/commands/bmalph.md"))) {
454
+ return { label: ".claude/commands/bmalph.md present", passed: true };
455
+ }
456
+ return {
457
+ label: ".claude/commands/bmalph.md present",
458
+ passed: false,
459
+ detail: "not found",
460
+ hint: "Run: bmalph init",
461
+ };
462
+ },
463
+ };
464
+ const claudeMdCheck = {
465
+ id: "claude-md",
466
+ run: async (projectDir) => {
467
+ const label = "CLAUDE.md contains BMAD snippet";
468
+ const hint = "Run: bmalph init";
469
+ try {
470
+ const content = await readFile(join(projectDir, "CLAUDE.md"), "utf-8");
471
+ if (content.includes("BMAD-METHOD Integration")) {
472
+ return { label, passed: true };
473
+ }
474
+ return { label, passed: false, detail: "missing BMAD-METHOD Integration section", hint };
475
+ }
476
+ catch (err) {
477
+ if (isEnoent(err)) {
478
+ return { label, passed: false, detail: "CLAUDE.md not found", hint };
479
+ }
480
+ return { label, passed: false, detail: `error: ${formatError(err)}`, hint };
481
+ }
482
+ },
483
+ };
484
+ return [...CORE_CHECKS, slashCommandCheck, claudeMdCheck, ...TRAILING_CHECKS];
485
+ })();
@@ -1,6 +1,7 @@
1
1
  interface InitOptions {
2
2
  name?: string;
3
3
  description?: string;
4
+ platform?: string;
4
5
  dryRun?: boolean;
5
6
  projectDir: string;
6
7
  }
@@ -1,13 +1,60 @@
1
1
  import chalk from "chalk";
2
2
  import inquirer from "inquirer";
3
3
  import { writeConfig } from "../utils/config.js";
4
- import { installProject, mergeClaudeMd, isInitialized, previewInstall, getBundledVersions, } from "../installer.js";
4
+ import { installProject, mergeInstructionsFile, isInitialized, hasExistingBmadDir, previewInstall, getBundledVersions, } from "../installer.js";
5
5
  import { formatDryRunSummary } from "../utils/dryrun.js";
6
6
  import { validateProjectName } from "../utils/validate.js";
7
7
  import { withErrorHandling } from "../utils/errors.js";
8
+ import { isPlatformId, getPlatform } from "../platform/registry.js";
9
+ import { detectPlatform } from "../platform/detect.js";
8
10
  export async function initCommand(options) {
9
11
  await withErrorHandling(() => runInit(options));
10
12
  }
13
+ /**
14
+ * Resolve which platform to use:
15
+ * 1. Explicit --platform flag
16
+ * 2. Auto-detect from filesystem markers
17
+ * 3. Interactive prompt (if TTY)
18
+ * 4. Default to claude-code (non-interactive)
19
+ */
20
+ async function resolvePlatform(projectDir, explicit) {
21
+ // 1. Explicit flag
22
+ if (explicit) {
23
+ if (!isPlatformId(explicit)) {
24
+ throw new Error(`Unknown platform: "${explicit}". ` +
25
+ `Valid platforms: claude-code, codex, cursor, windsurf, copilot, aider`);
26
+ }
27
+ return getPlatform(explicit);
28
+ }
29
+ // 2. Auto-detect
30
+ const detection = await detectPlatform(projectDir);
31
+ if (detection.detected) {
32
+ return getPlatform(detection.detected);
33
+ }
34
+ // 3. Interactive prompt if multiple candidates or none detected
35
+ if (process.stdin.isTTY) {
36
+ const choices = [
37
+ { name: "Claude Code", value: "claude-code" },
38
+ { name: "OpenAI Codex", value: "codex" },
39
+ { name: "Cursor", value: "cursor" },
40
+ { name: "Windsurf", value: "windsurf" },
41
+ { name: "GitHub Copilot", value: "copilot" },
42
+ { name: "Aider", value: "aider" },
43
+ ];
44
+ const { platformId } = await inquirer.prompt([
45
+ {
46
+ type: "list",
47
+ name: "platformId",
48
+ message: "Which platform are you using?",
49
+ choices,
50
+ default: detection.candidates[0] ?? "claude-code",
51
+ },
52
+ ]);
53
+ return getPlatform(platformId);
54
+ }
55
+ // 4. Non-interactive default
56
+ return getPlatform("claude-code");
57
+ }
11
58
  async function runInit(options) {
12
59
  const projectDir = options.projectDir;
13
60
  if (await isInitialized(projectDir)) {
@@ -15,9 +62,16 @@ async function runInit(options) {
15
62
  console.log("Use 'bmalph upgrade' to update bundled assets to the latest version.");
16
63
  return;
17
64
  }
65
+ if (await hasExistingBmadDir(projectDir)) {
66
+ console.log(chalk.cyan("Existing BMAD installation detected."));
67
+ console.log("Framework files in _bmad/ will be replaced with the managed version.");
68
+ console.log("Planning artifacts in _bmad-output/ will not be modified.\n");
69
+ }
70
+ // Resolve platform
71
+ const platform = await resolvePlatform(projectDir, options.platform);
18
72
  // Handle dry-run mode
19
73
  if (options.dryRun) {
20
- const preview = await previewInstall(projectDir);
74
+ const preview = await previewInstall(projectDir, platform);
21
75
  const actions = [
22
76
  ...preview.wouldCreate.map((p) => ({ type: "create", path: p })),
23
77
  ...preview.wouldModify.map((p) => ({ type: "modify", path: p })),
@@ -64,18 +118,19 @@ async function runInit(options) {
64
118
  throw new Error("Project name cannot be empty");
65
119
  }
66
120
  const validatedName = validateProjectName(name);
67
- console.log(chalk.blue("\nInstalling BMAD + Ralph..."));
68
- await installProject(projectDir);
121
+ console.log(chalk.blue(`\nInstalling BMAD + Ralph for ${platform.displayName}...`));
122
+ await installProject(projectDir, platform);
69
123
  const bundledVersions = getBundledVersions();
70
124
  const config = {
71
125
  name: validatedName,
72
126
  description: description ?? "",
73
127
  createdAt: new Date().toISOString(),
128
+ platform: platform.id,
74
129
  upstreamVersions: bundledVersions,
75
130
  };
76
131
  try {
77
132
  await writeConfig(projectDir, config);
78
- await mergeClaudeMd(projectDir);
133
+ await mergeInstructionsFile(projectDir, platform);
79
134
  }
80
135
  catch (err) {
81
136
  throw new Error(`Partial installation: files were copied but configuration failed. ` +
@@ -83,11 +138,23 @@ async function runInit(options) {
83
138
  }
84
139
  console.log(chalk.green("\nbmalph initialized successfully!"));
85
140
  console.log(`\n Project: ${chalk.bold(config.name)}`);
141
+ console.log(` Platform: ${chalk.bold(platform.displayName)}`);
86
142
  console.log(`\nInstalled:`);
87
143
  console.log(` _bmad/ BMAD agents and workflows`);
88
144
  console.log(` .ralph/ Ralph loop and templates`);
89
- console.log(` .claude/commands/ Slash command (/bmalph)`);
145
+ if (platform.commandDelivery.kind === "directory") {
146
+ console.log(` ${platform.commandDelivery.dir}/ Slash commands`);
147
+ }
90
148
  console.log(` bmalph/ State management`);
149
+ // Platform-specific next step guidance
91
150
  console.log(`\nNext step:`);
92
- console.log(` Use ${chalk.cyan("/bmalph")} in Claude Code to see your current phase and commands.`);
151
+ if (platform.id === "claude-code") {
152
+ console.log(` Use ${chalk.cyan("/bmalph")} in Claude Code to see your current phase and commands.`);
153
+ }
154
+ else if (platform.id === "codex") {
155
+ console.log(` Ask Codex to ${chalk.cyan("run the BMAD master agent")} to navigate phases.`);
156
+ }
157
+ else {
158
+ console.log(` Ask your AI assistant to ${chalk.cyan("use the BMAD agents")} defined in ${chalk.cyan(platform.instructionsFile)}.`);
159
+ }
93
160
  }
@@ -1,9 +1,10 @@
1
1
  import chalk from "chalk";
2
2
  import inquirer from "inquirer";
3
- import { isInitialized, copyBundledAssets, mergeClaudeMd, previewUpgrade, getBundledVersions, } from "../installer.js";
3
+ import { isInitialized, copyBundledAssets, mergeInstructionsFile, previewUpgrade, getBundledVersions, } from "../installer.js";
4
4
  import { readConfig, writeConfig } from "../utils/config.js";
5
5
  import { formatDryRunSummary } from "../utils/dryrun.js";
6
6
  import { withErrorHandling } from "../utils/errors.js";
7
+ import { resolveProjectPlatform } from "../platform/resolve.js";
7
8
  export async function upgradeCommand(options) {
8
9
  await withErrorHandling(() => runUpgrade(options));
9
10
  }
@@ -13,9 +14,11 @@ async function runUpgrade(options) {
13
14
  console.log(chalk.red("bmalph is not initialized. Run 'bmalph init' first."));
14
15
  return;
15
16
  }
17
+ // Read platform from existing config
18
+ const platform = await resolveProjectPlatform(projectDir);
16
19
  // Handle dry-run mode
17
20
  if (options.dryRun) {
18
- const preview = await previewUpgrade(projectDir);
21
+ const preview = await previewUpgrade(projectDir, platform);
19
22
  const actions = [
20
23
  ...preview.wouldUpdate.map((p) => ({ type: "modify", path: p })),
21
24
  ...preview.wouldCreate.map((p) => ({ type: "create", path: p })),
@@ -41,9 +44,9 @@ async function runUpgrade(options) {
41
44
  return;
42
45
  }
43
46
  }
44
- console.log(chalk.blue("Upgrading bundled assets..."));
45
- const result = await copyBundledAssets(projectDir);
46
- await mergeClaudeMd(projectDir);
47
+ console.log(chalk.blue(`Upgrading bundled assets for ${platform.displayName}...`));
48
+ const result = await copyBundledAssets(projectDir, platform);
49
+ await mergeInstructionsFile(projectDir, platform);
47
50
  // Update upstreamVersions in config to match bundled versions
48
51
  const config = await readConfig(projectDir);
49
52
  if (config) {