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.
- package/README.md +111 -30
- package/dist/cli.js +1 -0
- package/dist/commands/doctor.d.ts +14 -2
- package/dist/commands/doctor.js +83 -35
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +74 -7
- package/dist/commands/upgrade.js +8 -5
- package/dist/installer.d.ts +15 -4
- package/dist/installer.js +190 -101
- package/dist/platform/aider.d.ts +2 -0
- package/dist/platform/aider.js +71 -0
- package/dist/platform/claude-code.d.ts +2 -0
- package/dist/platform/claude-code.js +88 -0
- package/dist/platform/codex.d.ts +2 -0
- package/dist/platform/codex.js +67 -0
- package/dist/platform/copilot.d.ts +2 -0
- package/dist/platform/copilot.js +71 -0
- package/dist/platform/cursor.d.ts +2 -0
- package/dist/platform/cursor.js +71 -0
- package/dist/platform/detect.d.ts +7 -0
- package/dist/platform/detect.js +23 -0
- package/dist/platform/index.d.ts +4 -0
- package/dist/platform/index.js +3 -0
- package/dist/platform/registry.d.ts +4 -0
- package/dist/platform/registry.js +27 -0
- package/dist/platform/resolve.d.ts +8 -0
- package/dist/platform/resolve.js +24 -0
- package/dist/platform/types.d.ts +41 -0
- package/dist/platform/types.js +7 -0
- package/dist/platform/windsurf.d.ts +2 -0
- package/dist/platform/windsurf.js +71 -0
- package/dist/transition/artifacts.js +1 -1
- package/dist/transition/specs-changelog.js +4 -1
- package/dist/utils/config.d.ts +2 -0
- package/dist/utils/validate.js +16 -0
- package/package.json +1 -1
- package/ralph/drivers/claude-code.sh +118 -0
- package/ralph/drivers/codex.sh +81 -0
- package/ralph/ralph_import.sh +11 -0
- package/ralph/ralph_loop.sh +37 -64
- 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
|
-
-
|
|
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
|
-
|
|
48
|
-
#
|
|
49
|
-
#
|
|
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
|
|
67
|
-
- Installs slash 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
|
|
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
|
-
|
|
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
|
|
185
|
-
| -------------------------- |
|
|
186
|
-
| `-n, --name <name>` | Project name
|
|
187
|
-
| `-d, --description <desc>` | Project description
|
|
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.
|
|
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
|
|
261
|
-
└──
|
|
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
|
|
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
|
|
317
|
-
|
|
|
318
|
-
| Commands fail before init
|
|
319
|
-
| Transition finds no stories
|
|
320
|
-
| Ralph stops mid-loop
|
|
321
|
-
| Doctor reports version drift
|
|
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
|
|
330
|
-
|
|
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
|
-
*
|
|
36
|
-
*
|
|
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 {};
|
package/dist/commands/doctor.js
CHANGED
|
@@ -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
|
-
//
|
|
23
|
-
const
|
|
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
|
-
//
|
|
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 -
|
|
394
|
+
// Check Registry - core checks + platform-specific checks
|
|
416
395
|
// =============================================================================
|
|
417
396
|
/**
|
|
418
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
429
|
-
|
|
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
|
+
})();
|
package/dist/commands/init.d.ts
CHANGED
package/dist/commands/init.js
CHANGED
|
@@ -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,
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import inquirer from "inquirer";
|
|
3
|
-
import { isInitialized, copyBundledAssets,
|
|
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(
|
|
45
|
-
const result = await copyBundledAssets(projectDir);
|
|
46
|
-
await
|
|
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) {
|