bmalph 2.8.0 → 2.9.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 +51 -28
- package/dist/cli.js +3 -2
- package/dist/commands/doctor-checks.js +1 -1
- package/dist/commands/doctor-health-checks.js +2 -1
- package/dist/commands/init.js +3 -1
- package/dist/commands/reset.js +1 -1
- package/dist/commands/run.js +36 -1
- package/dist/commands/status.js +0 -9
- package/dist/commands/upgrade.js +1 -1
- package/dist/installer/metadata.js +1 -1
- package/dist/installer/project-files.js +2 -3
- package/dist/installer/ralph-assets.js +8 -0
- package/dist/installer/template-files.js +25 -0
- package/dist/reset.js +2 -3
- package/dist/run/ralph-process.js +8 -3
- package/dist/run/run-dashboard.js +6 -4
- package/dist/transition/artifact-scan.js +3 -6
- package/dist/transition/context.js +1 -1
- package/dist/utils/constants.js +22 -0
- package/dist/utils/github.js +4 -3
- package/dist/utils/ralph-runtime-state.js +3 -13
- package/dist/utils/validate.js +4 -10
- package/dist/watch/dashboard.js +1 -0
- package/dist/watch/renderer.js +23 -0
- package/dist/watch/state-reader.js +20 -1
- package/package.json +8 -2
- package/ralph/ralph_loop.sh +168 -0
- package/ralph/templates/PROMPT.md +12 -0
- package/ralph/templates/REVIEW_PROMPT.md +60 -0
- package/ralph/templates/ralphrc.template +13 -0
- package/dist/cli.js.map +0 -1
- package/dist/commands/check-updates.js.map +0 -1
- package/dist/commands/doctor-checks.js.map +0 -1
- package/dist/commands/doctor-health-checks.js.map +0 -1
- package/dist/commands/doctor-runtime-checks.js.map +0 -1
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/implement.js.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/reset.js.map +0 -1
- package/dist/commands/run.js.map +0 -1
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/upgrade.js.map +0 -1
- package/dist/commands/watch.js.map +0 -1
- package/dist/installer/bmad-assets.js.map +0 -1
- package/dist/installer/commands.js.map +0 -1
- package/dist/installer/install.js.map +0 -1
- package/dist/installer/metadata.js.map +0 -1
- package/dist/installer/project-files.js.map +0 -1
- package/dist/installer/ralph-assets.js.map +0 -1
- package/dist/installer/template-files.js.map +0 -1
- package/dist/installer/types.js.map +0 -1
- package/dist/installer.js.map +0 -1
- package/dist/platform/aider.js.map +0 -1
- package/dist/platform/claude-code.js.map +0 -1
- package/dist/platform/codex.js.map +0 -1
- package/dist/platform/copilot.js.map +0 -1
- package/dist/platform/cursor-runtime-checks.js.map +0 -1
- package/dist/platform/cursor.js.map +0 -1
- package/dist/platform/detect.js.map +0 -1
- package/dist/platform/doctor-checks.js.map +0 -1
- package/dist/platform/guidance.js.map +0 -1
- package/dist/platform/instructions-snippet.js.map +0 -1
- package/dist/platform/opencode.js.map +0 -1
- package/dist/platform/registry.js.map +0 -1
- package/dist/platform/resolve.js.map +0 -1
- package/dist/platform/types.js.map +0 -1
- package/dist/platform/windsurf.js.map +0 -1
- package/dist/reset.js.map +0 -1
- package/dist/run/ralph-process.js.map +0 -1
- package/dist/run/run-dashboard.js.map +0 -1
- package/dist/run/types.js.map +0 -1
- package/dist/transition/artifact-collection.js.map +0 -1
- package/dist/transition/artifact-loading.js.map +0 -1
- package/dist/transition/artifact-scan.js.map +0 -1
- package/dist/transition/artifacts.js.map +0 -1
- package/dist/transition/context-output.js.map +0 -1
- package/dist/transition/context.js.map +0 -1
- package/dist/transition/fix-plan-sync.js.map +0 -1
- package/dist/transition/fix-plan.js.map +0 -1
- package/dist/transition/index.js.map +0 -1
- package/dist/transition/orchestration.js.map +0 -1
- package/dist/transition/preflight.js.map +0 -1
- package/dist/transition/section-patterns.js.map +0 -1
- package/dist/transition/specs-changelog.js.map +0 -1
- package/dist/transition/specs-index.js.map +0 -1
- package/dist/transition/specs-sync.js.map +0 -1
- package/dist/transition/sprint-status.js.map +0 -1
- package/dist/transition/story-id.js.map +0 -1
- package/dist/transition/story-parsing.js.map +0 -1
- package/dist/transition/tech-stack.js.map +0 -1
- package/dist/transition/types.js.map +0 -1
- package/dist/utils/artifact-definitions.js.map +0 -1
- package/dist/utils/config.js.map +0 -1
- package/dist/utils/constants.js.map +0 -1
- package/dist/utils/dryrun.js.map +0 -1
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/file-system.js.map +0 -1
- package/dist/utils/format-status.js.map +0 -1
- package/dist/utils/github.js.map +0 -1
- package/dist/utils/json.js.map +0 -1
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/ralph-runtime-state.js.map +0 -1
- package/dist/utils/state.js.map +0 -1
- package/dist/utils/validate.js.map +0 -1
- package/dist/watch/dashboard.js.map +0 -1
- package/dist/watch/file-watcher.js.map +0 -1
- package/dist/watch/frame-writer.js.map +0 -1
- package/dist/watch/renderer.js.map +0 -1
- package/dist/watch/state-reader.js.map +0 -1
- package/dist/watch/types.js.map +0 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
[BMAD-METHOD](https://github.com/bmad-code-org/BMAD-METHOD) planning + [Ralph](https://github.com/snarktank/ralph) autonomous implementation, wired through platform-specific instructions, skills, and command indexes.
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
13
|
-
<img src="docs/bmalph-diagram
|
|
13
|
+
<img src="docs/bmalph-diagram.png" alt="bmalph workflow diagram" width="800" />
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
16
|
## What is bmalph?
|
|
@@ -36,14 +36,15 @@ bmalph provides:
|
|
|
36
36
|
|
|
37
37
|
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.
|
|
38
38
|
|
|
39
|
-
| Platform | ID | Tier | Instructions File | Commands
|
|
40
|
-
| -------------- | ------------- | ------------------- | --------------------------------- |
|
|
41
|
-
| Claude Code | `claude-code` | full | `CLAUDE.md` | `.claude/commands/` directory
|
|
42
|
-
| OpenAI Codex | `codex` | full | `AGENTS.md` | Codex Skills (`.agents/skills/`)
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
39
|
+
| Platform | ID | Tier | Instructions File | Commands |
|
|
40
|
+
| -------------- | ------------- | ------------------- | --------------------------------- | ------------------------------------- |
|
|
41
|
+
| Claude Code | `claude-code` | full | `CLAUDE.md` | `.claude/commands/` directory |
|
|
42
|
+
| OpenAI Codex | `codex` | full | `AGENTS.md` | Codex Skills (`.agents/skills/`) |
|
|
43
|
+
| OpenCode | `opencode` | full | `AGENTS.md` | OpenCode Skills (`.opencode/skills/`) |
|
|
44
|
+
| Cursor | `cursor` | full (experimental) | `.cursor/rules/bmad.mdc` | `_bmad/COMMANDS.md` |
|
|
45
|
+
| Windsurf | `windsurf` | instructions-only | `.windsurf/rules/bmad.md` | `_bmad/COMMANDS.md` |
|
|
46
|
+
| GitHub Copilot | `copilot` | full (experimental) | `.github/copilot-instructions.md` | `_bmad/COMMANDS.md` |
|
|
47
|
+
| Aider | `aider` | instructions-only | `CONVENTIONS.md` | `_bmad/COMMANDS.md` |
|
|
47
48
|
|
|
48
49
|
**Tiers:**
|
|
49
50
|
|
|
@@ -55,7 +56,7 @@ bmalph works with multiple AI coding assistants. Each platform gets BMAD plannin
|
|
|
55
56
|
- Node.js 20+
|
|
56
57
|
- Bash (WSL or Git Bash on Windows)
|
|
57
58
|
- A supported AI coding platform (see table above)
|
|
58
|
-
- For Ralph loop (Phase 4): Claude Code (`claude`), Codex CLI (`codex`), Copilot CLI (`copilot`), or Cursor CLI (`cursor-agent`; older `agent` installs are also supported)
|
|
59
|
+
- For Ralph loop (Phase 4): Claude Code (`claude`), Codex CLI (`codex`), OpenCode (`opencode`), Copilot CLI (`copilot`), or Cursor CLI (`cursor-agent`; older `agent` installs are also supported)
|
|
59
60
|
|
|
60
61
|
## Installation
|
|
61
62
|
|
|
@@ -85,15 +86,15 @@ bmalph init
|
|
|
85
86
|
|
|
86
87
|
**Platform resolution:** `--platform` flag > auto-detect from project markers > interactive prompt > default `claude-code`
|
|
87
88
|
|
|
88
|
-
Strong markers such as `.cursor/`, `.claude/`, `.windsurf/`, `.github/copilot-instructions.md`, and `.aider.conf.yml` are auto-detected directly. Root-only `AGENTS.md` and `CLAUDE.md` are treated as weak hints and may still trigger the interactive platform prompt.
|
|
89
|
+
Strong markers such as `.cursor/`, `.claude/`, `.opencode/`, `.windsurf/`, `.github/copilot-instructions.md`, and `.aider.conf.yml` are auto-detected directly. Root-only `AGENTS.md` and `CLAUDE.md` are treated as weak hints and may still trigger the interactive platform prompt.
|
|
89
90
|
|
|
90
91
|
This installs:
|
|
91
92
|
|
|
92
93
|
- `_bmad/` — BMAD agents and workflows
|
|
93
|
-
- `.ralph/` — Ralph loop, libs, templates (drivers for claude-code, codex, copilot, and cursor)
|
|
94
|
+
- `.ralph/` — Ralph loop, libs, templates (drivers for claude-code, codex, opencode, copilot, and cursor)
|
|
94
95
|
- `bmalph/` — State management (config.json, stores selected platform)
|
|
95
96
|
- Updates the platform's instructions file with BMAD workflow instructions (e.g. `CLAUDE.md`, `AGENTS.md`, `.cursor/rules/bmad.mdc`)
|
|
96
|
-
- Delivers BMAD commands using the platform's native mechanism (Claude Code: `.claude/commands/`; Codex: `.agents/skills/`; Cursor, Windsurf, Copilot, and Aider: `_bmad/COMMANDS.md`)
|
|
97
|
+
- Delivers BMAD commands using the platform's native mechanism (Claude Code: `.claude/commands/`; Codex: `.agents/skills/`; OpenCode: `.opencode/skills/`; Cursor, Windsurf, Copilot, and Aider: `_bmad/COMMANDS.md`)
|
|
97
98
|
|
|
98
99
|
### Migrating from standalone BMAD
|
|
99
100
|
|
|
@@ -166,7 +167,7 @@ Available in any phase for supporting tasks:
|
|
|
166
167
|
|
|
167
168
|
### Step 3: Implement with Ralph (Phase 4)
|
|
168
169
|
|
|
169
|
-
> **Note:** Ralph is only available on **full** tier platforms (Claude Code, OpenAI Codex, GitHub Copilot, Cursor). Instructions-only platforms (Windsurf, Aider) support Phases 1-3 only. GitHub Copilot and Cursor support is experimental.
|
|
170
|
+
> **Note:** Ralph is only available on **full** tier platforms (Claude Code, OpenAI Codex, OpenCode, GitHub Copilot, Cursor). Instructions-only platforms (Windsurf, Aider) support Phases 1-3 only. GitHub Copilot and Cursor support is experimental.
|
|
170
171
|
|
|
171
172
|
Run `bmalph implement` from the terminal, or use the `/bmalph-implement` slash command in Claude Code.
|
|
172
173
|
|
|
@@ -231,12 +232,12 @@ BMAD (add Epic 2) → bmalph implement → Ralph sees changes + picks up Epic 2
|
|
|
231
232
|
|
|
232
233
|
### init options
|
|
233
234
|
|
|
234
|
-
| Flag | Description
|
|
235
|
-
| -------------------------- |
|
|
236
|
-
| `-n, --name <name>` | Project name
|
|
237
|
-
| `-d, --description <desc>` | Project description
|
|
238
|
-
| `--platform <id>` | Target platform (`claude-code`, `codex`, `cursor`, `windsurf`, `copilot`, `aider`) | auto-detect |
|
|
239
|
-
| `--dry-run` | Preview changes without writing files
|
|
235
|
+
| Flag | Description | Default |
|
|
236
|
+
| -------------------------- | ---------------------------------------------------------------------------------------------- | -------------- |
|
|
237
|
+
| `-n, --name <name>` | Project name | directory name |
|
|
238
|
+
| `-d, --description <desc>` | Project description | (prompted) |
|
|
239
|
+
| `--platform <id>` | Target platform (`claude-code`, `codex`, `opencode`, `cursor`, `windsurf`, `copilot`, `aider`) | auto-detect |
|
|
240
|
+
| `--dry-run` | Preview changes without writing files | |
|
|
240
241
|
|
|
241
242
|
### implement options
|
|
242
243
|
|
|
@@ -278,11 +279,12 @@ BMAD (add Epic 2) → bmalph implement → Ralph sees changes + picks up Epic 2
|
|
|
278
279
|
|
|
279
280
|
### run options
|
|
280
281
|
|
|
281
|
-
| Flag
|
|
282
|
-
|
|
|
283
|
-
| `--driver <platform>`
|
|
284
|
-
| `--
|
|
285
|
-
| `--
|
|
282
|
+
| Flag | Description |
|
|
283
|
+
| ---------------------- | --------------------------------------------------------------------------- |
|
|
284
|
+
| `--driver <platform>` | Override platform driver (claude-code, codex, opencode, copilot, cursor) |
|
|
285
|
+
| `--review/--no-review` | Enable/disable periodic code review (Claude Code only, prompted by default) |
|
|
286
|
+
| `--interval <ms>` | Dashboard refresh interval in milliseconds (default: 2000) |
|
|
287
|
+
| `--no-dashboard` | Run Ralph without the dashboard overlay |
|
|
286
288
|
|
|
287
289
|
### watch options
|
|
288
290
|
|
|
@@ -294,10 +296,11 @@ BMAD (add Epic 2) → bmalph implement → Ralph sees changes + picks up Epic 2
|
|
|
294
296
|
|
|
295
297
|
## Command Delivery
|
|
296
298
|
|
|
297
|
-
bmalph bundles
|
|
299
|
+
bmalph bundles 54 BMAD and bmalph command definitions. Delivery varies by platform:
|
|
298
300
|
|
|
299
301
|
- **Claude Code** — installed as files in `.claude/commands/` (invoke with `/command-name`)
|
|
300
302
|
- **OpenAI Codex** — delivered as Codex Skills in `.agents/skills/` (invoke with `$command-name`)
|
|
303
|
+
- **OpenCode** — delivered as OpenCode Skills in `.opencode/skills/`
|
|
301
304
|
- **Cursor** — discoverable via `_bmad/COMMANDS.md`; ask Cursor to run the BMAD master agent
|
|
302
305
|
- **Windsurf, Copilot, Aider** — discoverable via `_bmad/COMMANDS.md` reference index
|
|
303
306
|
|
|
@@ -324,6 +327,7 @@ For the full list:
|
|
|
324
327
|
|
|
325
328
|
- Claude Code: run `/bmad-help`
|
|
326
329
|
- OpenAI Codex: inspect `.agents/skills/`
|
|
330
|
+
- OpenCode: inspect `.opencode/skills/`
|
|
327
331
|
- Cursor, Windsurf, Copilot, Aider: open `_bmad/COMMANDS.md`
|
|
328
332
|
|
|
329
333
|
### Transition to Ralph
|
|
@@ -358,7 +362,7 @@ project/
|
|
|
358
362
|
│ ├── planning-artifacts/ # PRD, architecture, stories
|
|
359
363
|
│ ├── implementation-artifacts/ # Sprint plans (optional)
|
|
360
364
|
│ └── brainstorming/ # Brainstorm sessions (optional)
|
|
361
|
-
├── .ralph/ # Ralph autonomous loop (drivers for claude-code, codex, copilot, and cursor)
|
|
365
|
+
├── .ralph/ # Ralph autonomous loop (drivers for claude-code, codex, opencode, copilot, and cursor)
|
|
362
366
|
│ ├── ralph_loop.sh # Main loop script
|
|
363
367
|
│ ├── ralph_import.sh # Import requirements into Ralph
|
|
364
368
|
│ ├── ralph_monitor.sh # Monitor loop progress
|
|
@@ -367,6 +371,7 @@ project/
|
|
|
367
371
|
│ ├── drivers/ # Platform driver scripts
|
|
368
372
|
│ │ ├── claude-code.sh # Claude Code driver (uses `claude`)
|
|
369
373
|
│ │ ├── codex.sh # OpenAI Codex driver (uses `codex exec`)
|
|
374
|
+
│ │ ├── opencode.sh # OpenCode driver (uses `opencode run`)
|
|
370
375
|
│ │ ├── copilot.sh # GitHub Copilot driver (uses `copilot`, experimental)
|
|
371
376
|
│ │ ├── cursor.sh # Cursor driver (uses `cursor-agent`/`agent`, experimental)
|
|
372
377
|
│ │ └── cursor-agent-wrapper.sh # Wrapper for Windows .cmd Cursor installs
|
|
@@ -396,6 +401,7 @@ Ralph is a bash loop that spawns fresh AI coding sessions using a **platform dri
|
|
|
396
401
|
|
|
397
402
|
- **Claude Code driver** — invokes `claude` with `--output-format json`, `--permission-mode bypassPermissions`, `--allowedTools`, and explicit `--resume <session_id>`
|
|
398
403
|
- **Codex driver** — invokes `codex exec --json --sandbox workspace-write` with explicit `--resume <session_id>`
|
|
404
|
+
- **OpenCode driver** — invokes `opencode run --agent build --format json` with optional `--continue --session <session_id>`
|
|
399
405
|
- **Copilot driver** _(experimental)_ — invokes `copilot --autopilot --yolo` with plain-text output
|
|
400
406
|
- **Cursor driver** _(experimental)_ — invokes `cursor-agent -p --force --output-format json`, persists `session_id` for `--resume`, and switches to `stream-json` only for live output
|
|
401
407
|
|
|
@@ -410,6 +416,7 @@ Safety mechanisms:
|
|
|
410
416
|
|
|
411
417
|
- **Circuit breaker** — prevents infinite loops on failing stories
|
|
412
418
|
- **Response analyzer** — detects stuck or repeating outputs
|
|
419
|
+
- **Code review** — optional periodic review every 5 loops (`--review`, Claude Code only). A read-only session analyzes git diffs and feeds structured findings into the next implementation loop
|
|
413
420
|
- **Completion** — loop exits when all `@fix_plan.md` items are checked off
|
|
414
421
|
|
|
415
422
|
Cursor-specific runtime checks:
|
|
@@ -478,7 +485,7 @@ Notes:
|
|
|
478
485
|
| Ralph stops mid-loop | Circuit breaker detected stagnation. Check `.ralph/logs/` |
|
|
479
486
|
| Doctor reports version drift | Run `bmalph upgrade` to update bundled assets |
|
|
480
487
|
| Wrong platform detected | Re-run `bmalph init --platform <id>` with the correct platform |
|
|
481
|
-
| Ralph unavailable on platform | Ralph requires a full tier platform (claude-code, codex, copilot, or cursor)
|
|
488
|
+
| Ralph unavailable on platform | Ralph requires a full tier platform (claude-code, codex, opencode, copilot, or cursor) |
|
|
482
489
|
|
|
483
490
|
### Windows: Cursor Driver
|
|
484
491
|
|
|
@@ -513,6 +520,7 @@ Then remove the bmalph-managed sections from your instructions file. The file de
|
|
|
513
520
|
|
|
514
521
|
- **Claude Code** — remove `.claude/commands/` and bmalph section from `CLAUDE.md`
|
|
515
522
|
- **Codex** — remove bmalph sections from `AGENTS.md`
|
|
523
|
+
- **OpenCode** — remove `.opencode/skills/bmad-*/` and bmalph sections from `AGENTS.md`
|
|
516
524
|
- **Cursor** — remove `.cursor/rules/bmad.mdc`
|
|
517
525
|
- **Windsurf** — remove `.windsurf/rules/bmad.md`
|
|
518
526
|
- **Copilot** — remove bmalph sections from `.github/copilot-instructions.md`
|
|
@@ -598,6 +606,21 @@ bmalph run
|
|
|
598
606
|
# Then: bmalph run
|
|
599
607
|
```
|
|
600
608
|
|
|
609
|
+
**OpenCode:**
|
|
610
|
+
|
|
611
|
+
```bash
|
|
612
|
+
# 1. Open your project in your AI coding assistant
|
|
613
|
+
|
|
614
|
+
# 2. Use OpenCode Skills such as $analyst, $create-prd, and $architect
|
|
615
|
+
# See .opencode/skills/ and _bmad/COMMANDS.md for the full catalog
|
|
616
|
+
|
|
617
|
+
# 3. Follow phases: Analysis -> Planning -> Solutioning
|
|
618
|
+
|
|
619
|
+
# 4. Transition to Ralph
|
|
620
|
+
# Run: bmalph implement
|
|
621
|
+
# Then: bmalph run
|
|
622
|
+
```
|
|
623
|
+
|
|
601
624
|
**Cursor, Copilot, Windsurf, Aider:**
|
|
602
625
|
|
|
603
626
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -59,7 +59,7 @@ program
|
|
|
59
59
|
.description("Initialize bmalph in the current project")
|
|
60
60
|
.option("-n, --name <name>", "Project name")
|
|
61
61
|
.option("-d, --description <desc>", "Project description")
|
|
62
|
-
.option("--platform <id>", "Target platform (claude-code, codex, cursor, windsurf, copilot, aider)")
|
|
62
|
+
.option("--platform <id>", "Target platform (claude-code, codex, opencode, cursor, windsurf, copilot, aider)")
|
|
63
63
|
.option("--dry-run", "Preview changes without writing files")
|
|
64
64
|
.action(async (opts) => initCommand({ ...opts, projectDir: await resolveAndValidateProjectDir() }));
|
|
65
65
|
program
|
|
@@ -102,9 +102,10 @@ program
|
|
|
102
102
|
program
|
|
103
103
|
.command("run")
|
|
104
104
|
.description("Start Ralph loop with live dashboard")
|
|
105
|
-
.option("--driver <platform>", "Override platform driver (claude-code, codex, copilot, cursor)")
|
|
105
|
+
.option("--driver <platform>", "Override platform driver (claude-code, codex, opencode, copilot, cursor)")
|
|
106
106
|
.option("--interval <ms>", "Dashboard refresh interval in milliseconds (default: 2000)")
|
|
107
107
|
.option("--no-dashboard", "Run Ralph without the dashboard overlay")
|
|
108
|
+
.option("--review", "Enable periodic code review loop (~10-14% more tokens)")
|
|
108
109
|
.action(async (opts) => runCommand({ ...opts, projectDir: await resolveAndValidateProjectDir() }));
|
|
109
110
|
void program.parseAsync();
|
|
110
111
|
//# sourceMappingURL=cli.js.map
|
|
@@ -15,7 +15,7 @@ export async function checkCommandAvailable(command) {
|
|
|
15
15
|
return false;
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
export
|
|
18
|
+
export function checkNodeVersion(_projectDir) {
|
|
19
19
|
const major = parseInt(process.versions.node.split(".")[0]);
|
|
20
20
|
return {
|
|
21
21
|
label: "Node version >= 20",
|
|
@@ -4,9 +4,10 @@ import { readConfig } from "../utils/config.js";
|
|
|
4
4
|
import { parseGitignoreLines } from "../utils/file-system.js";
|
|
5
5
|
import { getBundledVersions } from "../installer.js";
|
|
6
6
|
import { isEnoent, formatError } from "../utils/errors.js";
|
|
7
|
+
import { GITIGNORE_ENTRIES } from "../utils/constants.js";
|
|
7
8
|
export async function checkGitignore(projectDir) {
|
|
8
9
|
const label = ".gitignore has required entries";
|
|
9
|
-
const required = [
|
|
10
|
+
const required = [...GITIGNORE_ENTRIES];
|
|
10
11
|
try {
|
|
11
12
|
const content = await readFile(join(projectDir, ".gitignore"), "utf-8");
|
|
12
13
|
const existingLines = parseGitignoreLines(content);
|
package/dist/commands/init.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import
|
|
2
|
+
import select from "@inquirer/select";
|
|
3
|
+
import input from "@inquirer/input";
|
|
3
4
|
import { writeConfig } from "../utils/config.js";
|
|
4
5
|
import { installProject, mergeInstructionsFile, isInitialized, previewInstall, getBundledVersions, } from "../installer.js";
|
|
5
6
|
import { formatDryRunSummary } from "../utils/dryrun.js";
|
|
@@ -101,6 +102,7 @@ async function runInit(options) {
|
|
|
101
102
|
const bundledVersions = await getBundledVersions();
|
|
102
103
|
const config = {
|
|
103
104
|
name: validatedName,
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- defensive against future refactors
|
|
104
106
|
description: description ?? "",
|
|
105
107
|
createdAt: new Date().toISOString(),
|
|
106
108
|
platform: platform.id,
|
package/dist/commands/reset.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import
|
|
2
|
+
import confirm from "@inquirer/confirm";
|
|
3
3
|
import { isInitialized } from "../installer.js";
|
|
4
4
|
import { buildResetPlan, executeResetPlan, planToDryRunActions } from "../reset.js";
|
|
5
5
|
import { formatDryRunSummary } from "../utils/dryrun.js";
|
package/dist/commands/run.js
CHANGED
|
@@ -24,6 +24,10 @@ async function executeRun(options) {
|
|
|
24
24
|
if (platform.experimental) {
|
|
25
25
|
console.log(chalk.yellow(`Warning: ${platform.displayName} support is experimental`));
|
|
26
26
|
}
|
|
27
|
+
const reviewEnabled = await resolveReviewMode(options.review, platform);
|
|
28
|
+
if (reviewEnabled) {
|
|
29
|
+
console.log(chalk.cyan("Enhanced mode: code review every 5 implementation loops"));
|
|
30
|
+
}
|
|
27
31
|
const interval = parseInterval(options.interval);
|
|
28
32
|
let useDashboard = dashboard;
|
|
29
33
|
if (useDashboard) {
|
|
@@ -39,9 +43,10 @@ async function executeRun(options) {
|
|
|
39
43
|
}
|
|
40
44
|
const ralph = spawnRalphLoop(projectDir, platform.id, {
|
|
41
45
|
inheritStdio: !useDashboard,
|
|
46
|
+
...(reviewEnabled && { reviewEnabled }),
|
|
42
47
|
});
|
|
43
48
|
if (useDashboard) {
|
|
44
|
-
await startRunDashboard({ projectDir, interval, ralph });
|
|
49
|
+
await startRunDashboard({ projectDir, interval, ralph, reviewEnabled });
|
|
45
50
|
if (ralph.state === "stopped") {
|
|
46
51
|
applyRalphExitCode(ralph.exitCode);
|
|
47
52
|
}
|
|
@@ -65,4 +70,34 @@ function resolvePlatform(driverOverride, configPlatform) {
|
|
|
65
70
|
}
|
|
66
71
|
return getPlatform(id);
|
|
67
72
|
}
|
|
73
|
+
async function resolveReviewMode(reviewFlag, platform) {
|
|
74
|
+
if (reviewFlag === true) {
|
|
75
|
+
if (platform.id !== "claude-code") {
|
|
76
|
+
throw new Error("--review requires Claude Code (other drivers lack read-only enforcement)");
|
|
77
|
+
}
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
if (reviewFlag === false) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
if (platform.id !== "claude-code") {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if (!process.stdin.isTTY) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const { default: select } = await import("@inquirer/select");
|
|
90
|
+
const mode = await select({
|
|
91
|
+
message: "Quality mode:",
|
|
92
|
+
choices: [
|
|
93
|
+
{ name: "Standard — current behavior (no extra cost)", value: "standard" },
|
|
94
|
+
{
|
|
95
|
+
name: "Enhanced — periodic code review every 5 loops (~10-14% more tokens)",
|
|
96
|
+
value: "enhanced",
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
default: "standard",
|
|
100
|
+
});
|
|
101
|
+
return mode === "enhanced";
|
|
102
|
+
}
|
|
68
103
|
//# sourceMappingURL=run.js.map
|
package/dist/commands/status.js
CHANGED
|
@@ -8,9 +8,6 @@ import { resolveProjectPlatform } from "../platform/resolve.js";
|
|
|
8
8
|
import { getFullTierPlatformNames } from "../platform/registry.js";
|
|
9
9
|
import { getPlatformAnalysisHint, getPlatformPrdHint } from "../platform/guidance.js";
|
|
10
10
|
import { scanProjectArtifacts } from "../transition/artifact-scan.js";
|
|
11
|
-
function getCursorNextAction() {
|
|
12
|
-
return "Read _bmad/COMMANDS.md and ask Cursor to run the BMAD master agent";
|
|
13
|
-
}
|
|
14
11
|
export async function statusCommand(options) {
|
|
15
12
|
await withErrorHandling(() => runStatus(options));
|
|
16
13
|
}
|
|
@@ -151,14 +148,8 @@ function getNextAction(phase, status, ralphStatus, platform) {
|
|
|
151
148
|
}
|
|
152
149
|
switch (phase) {
|
|
153
150
|
case 1:
|
|
154
|
-
if (platform.id === "cursor") {
|
|
155
|
-
return getCursorNextAction();
|
|
156
|
-
}
|
|
157
151
|
return getPlatformAnalysisHint(platform);
|
|
158
152
|
case 2:
|
|
159
|
-
if (platform.id === "cursor") {
|
|
160
|
-
return getCursorNextAction();
|
|
161
|
-
}
|
|
162
153
|
return getPlatformPrdHint(platform);
|
|
163
154
|
case 3:
|
|
164
155
|
return "Run: bmalph implement";
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import
|
|
2
|
+
import confirm from "@inquirer/confirm";
|
|
3
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";
|
|
@@ -22,7 +22,7 @@ export async function getBundledVersions() {
|
|
|
22
22
|
const versionsPath = join(__dirname, "..", "..", "bundled-versions.json");
|
|
23
23
|
try {
|
|
24
24
|
const versions = JSON.parse(await readFile(versionsPath, "utf-8"));
|
|
25
|
-
if (
|
|
25
|
+
if (typeof versions.bmadCommit !== "string") {
|
|
26
26
|
throw new Error("Invalid bundled-versions.json structure: missing bmadCommit");
|
|
27
27
|
}
|
|
28
28
|
return {
|
|
@@ -2,7 +2,7 @@ import { mkdir, readFile } from "node:fs/promises";
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { atomicWriteFile, exists, parseGitignoreLines, replaceSection, } from "../utils/file-system.js";
|
|
4
4
|
import { isEnoent } from "../utils/errors.js";
|
|
5
|
-
import { CONFIG_FILE, STATE_DIR } from "../utils/constants.js";
|
|
5
|
+
import { CONFIG_FILE, GITIGNORE_ENTRIES, STATE_DIR } from "../utils/constants.js";
|
|
6
6
|
import { getDefaultPlatform } from "./metadata.js";
|
|
7
7
|
import { isTemplateCustomized } from "./template-files.js";
|
|
8
8
|
export async function updateGitignore(projectDir) {
|
|
@@ -16,8 +16,7 @@ export async function updateGitignore(projectDir) {
|
|
|
16
16
|
throw err;
|
|
17
17
|
}
|
|
18
18
|
const existingLines = parseGitignoreLines(existing);
|
|
19
|
-
const
|
|
20
|
-
const newEntries = entries.filter((e) => !existingLines.has(e));
|
|
19
|
+
const newEntries = GITIGNORE_ENTRIES.filter((e) => !existingLines.has(e));
|
|
21
20
|
if (newEntries.length === 0)
|
|
22
21
|
return;
|
|
23
22
|
const suffix = existing.length > 0 && !existing.endsWith("\n")
|
|
@@ -8,8 +8,10 @@ export async function installRalphAssets(projectDir, ralphDir, platform, package
|
|
|
8
8
|
await mkdir(join(projectDir, ".ralph"), { recursive: true });
|
|
9
9
|
const promptPath = join(projectDir, ".ralph/PROMPT.md");
|
|
10
10
|
const agentPath = join(projectDir, ".ralph/@AGENT.md");
|
|
11
|
+
const reviewPromptPath = join(projectDir, ".ralph/REVIEW_PROMPT.md");
|
|
11
12
|
const promptCustomized = await isTemplateCustomized(promptPath, "PROMPT.md");
|
|
12
13
|
const agentCustomized = await isTemplateCustomized(agentPath, "AGENT.md");
|
|
14
|
+
const reviewPromptCustomized = await isTemplateCustomized(reviewPromptPath, "REVIEW_PROMPT.md");
|
|
13
15
|
if (!promptCustomized) {
|
|
14
16
|
await cp(join(ralphDir, "templates/PROMPT.md"), promptPath, {
|
|
15
17
|
dereference: false,
|
|
@@ -20,6 +22,11 @@ export async function installRalphAssets(projectDir, ralphDir, platform, package
|
|
|
20
22
|
dereference: false,
|
|
21
23
|
});
|
|
22
24
|
}
|
|
25
|
+
if (!reviewPromptCustomized) {
|
|
26
|
+
await cp(join(ralphDir, "templates/REVIEW_PROMPT.md"), reviewPromptPath, {
|
|
27
|
+
dereference: false,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
23
30
|
await cp(join(ralphDir, "RALPH-REFERENCE.md"), join(projectDir, ".ralph/RALPH-REFERENCE.md"), {
|
|
24
31
|
dereference: false,
|
|
25
32
|
});
|
|
@@ -83,6 +90,7 @@ export async function installRalphAssets(projectDir, ralphDir, platform, package
|
|
|
83
90
|
".ralph/lib/",
|
|
84
91
|
...(!promptCustomized ? [".ralph/PROMPT.md"] : []),
|
|
85
92
|
...(!agentCustomized ? [".ralph/@AGENT.md"] : []),
|
|
93
|
+
...(!reviewPromptCustomized ? [".ralph/REVIEW_PROMPT.md"] : []),
|
|
86
94
|
...(!ralphrcCustomized && currentRalphrc !== renderedRalphrc ? [".ralph/.ralphrc"] : []),
|
|
87
95
|
".ralph/RALPH-REFERENCE.md",
|
|
88
96
|
],
|
|
@@ -5,6 +5,7 @@ import { getBundledRalphDir } from "./metadata.js";
|
|
|
5
5
|
const TEMPLATE_PLACEHOLDERS = {
|
|
6
6
|
"PROMPT.md": "[YOUR PROJECT NAME]",
|
|
7
7
|
"AGENT.md": "pip install -r requirements.txt",
|
|
8
|
+
"REVIEW_PROMPT.md": "[YOUR PROJECT NAME]",
|
|
8
9
|
};
|
|
9
10
|
const RALPHRC_TEMPLATE_NAME = "RALPHRC";
|
|
10
11
|
const CLAUDE_ALLOWED_TOOLS_TEMPLATE_LINE = 'ALLOWED_TOOLS="Write,Read,Edit,MultiEdit,Glob,Grep,Task,TodoWrite,WebFetch,WebSearch,EnterPlanMode,ExitPlanMode,NotebookEdit,Bash"';
|
|
@@ -49,6 +50,20 @@ QUALITY_GATE_TIMEOUT="\${QUALITY_GATE_TIMEOUT:-120}"
|
|
|
49
50
|
# Only run gates when the agent signals completion (EXIT_SIGNAL=true)
|
|
50
51
|
QUALITY_GATE_ON_COMPLETION_ONLY="\${QUALITY_GATE_ON_COMPLETION_ONLY:-false}"
|
|
51
52
|
|
|
53
|
+
`;
|
|
54
|
+
const REVIEW_TEMPLATE_BLOCK = `# =============================================================================
|
|
55
|
+
# PERIODIC CODE REVIEW
|
|
56
|
+
# =============================================================================
|
|
57
|
+
|
|
58
|
+
# Enable periodic code review loops (set via 'bmalph run --review' or manually)
|
|
59
|
+
# When enabled, Ralph runs a read-only review session every REVIEW_INTERVAL loops.
|
|
60
|
+
# The review agent analyzes git diffs and outputs findings for the next implementation loop.
|
|
61
|
+
# Currently supported on Claude Code only.
|
|
62
|
+
REVIEW_ENABLED="\${REVIEW_ENABLED:-false}"
|
|
63
|
+
|
|
64
|
+
# Number of implementation loops between review sessions (default: 5)
|
|
65
|
+
REVIEW_INTERVAL="\${REVIEW_INTERVAL:-5}"
|
|
66
|
+
|
|
52
67
|
`;
|
|
53
68
|
const LEGACY_RALPHRC_TEMPLATE = `# .ralphrc - Ralph project configuration
|
|
54
69
|
# Generated by: ralph enable
|
|
@@ -209,6 +224,16 @@ async function isRalphrcCustomized(filePath, platformId) {
|
|
|
209
224
|
if (matchesManagedPermissionVariants(content, templateWithoutQG)) {
|
|
210
225
|
return false;
|
|
211
226
|
}
|
|
227
|
+
// Check variants without review block (pre-review installs)
|
|
228
|
+
const templateWithoutReview = currentTemplate.replace(REVIEW_TEMPLATE_BLOCK, "");
|
|
229
|
+
if (matchesManagedPermissionVariants(content, templateWithoutReview)) {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
// Check variants without both quality gates and review blocks
|
|
233
|
+
const templateWithoutQGAndReview = templateWithoutQG.replace(REVIEW_TEMPLATE_BLOCK, "");
|
|
234
|
+
if (matchesManagedPermissionVariants(content, templateWithoutQGAndReview)) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
212
237
|
const legacyTemplate = normalizeManagedRalphrcContent(renderLegacyRalphrcTemplate(platformId));
|
|
213
238
|
return content !== legacyTemplate;
|
|
214
239
|
}
|
package/dist/reset.js
CHANGED
|
@@ -3,7 +3,7 @@ import { join, posix } from "node:path";
|
|
|
3
3
|
import { getSlashCommandsDir } from "./installer.js";
|
|
4
4
|
import { exists, atomicWriteFile, parseGitignoreLines, replaceSection, } from "./utils/file-system.js";
|
|
5
5
|
import { isEnoent } from "./utils/errors.js";
|
|
6
|
-
import { BMAD_DIR, RALPH_DIR, BMALPH_DIR, BMAD_OUTPUT_DIR, SKILLS_PREFIX, } from "./utils/constants.js";
|
|
6
|
+
import { BMAD_DIR, RALPH_DIR, BMALPH_DIR, BMAD_OUTPUT_DIR, GITIGNORE_ENTRIES, SKILLS_PREFIX, } from "./utils/constants.js";
|
|
7
7
|
export async function buildResetPlan(projectDir, platform) {
|
|
8
8
|
const plan = {
|
|
9
9
|
directories: [],
|
|
@@ -74,8 +74,7 @@ export async function buildResetPlan(projectDir, platform) {
|
|
|
74
74
|
try {
|
|
75
75
|
const content = await readFile(join(projectDir, ".gitignore"), "utf-8");
|
|
76
76
|
const existingLines = parseGitignoreLines(content);
|
|
77
|
-
const
|
|
78
|
-
for (const entry of bmalpEntries) {
|
|
77
|
+
for (const entry of GITIGNORE_ENTRIES) {
|
|
79
78
|
if (existingLines.has(entry)) {
|
|
80
79
|
plan.gitignoreLines.push(entry);
|
|
81
80
|
}
|
|
@@ -74,10 +74,10 @@ export async function runBashCommand(command, options = {}) {
|
|
|
74
74
|
}
|
|
75
75
|
finish(() => reject(new Error(`bash command timed out: ${command}`)));
|
|
76
76
|
}, timeoutMs);
|
|
77
|
-
child.stdout
|
|
77
|
+
child.stdout.on("data", (chunk) => {
|
|
78
78
|
stdout += chunk.toString();
|
|
79
79
|
});
|
|
80
|
-
child.stderr
|
|
80
|
+
child.stderr.on("data", (chunk) => {
|
|
81
81
|
stderr += chunk.toString();
|
|
82
82
|
});
|
|
83
83
|
child.on("close", (exitCode) => finish(() => resolve({
|
|
@@ -124,9 +124,14 @@ export async function validateRalphLoop(projectDir) {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
export function spawnRalphLoop(projectDir, platformId, options) {
|
|
127
|
+
const env = { ...process.env, PLATFORM_DRIVER: platformId };
|
|
128
|
+
if (options.reviewEnabled) {
|
|
129
|
+
env.REVIEW_ENABLED = "true";
|
|
130
|
+
env.REVIEW_INTERVAL = "5";
|
|
131
|
+
}
|
|
127
132
|
const child = spawn(cachedBashCommand ?? "bash", [BASH_RALPH_LOOP_PATH], {
|
|
128
133
|
cwd: projectDir,
|
|
129
|
-
env
|
|
134
|
+
env,
|
|
130
135
|
stdio: options.inheritStdio ? "inherit" : ["ignore", "pipe", "pipe"],
|
|
131
136
|
detached: process.platform !== "win32",
|
|
132
137
|
windowsHide: true,
|
|
@@ -2,11 +2,12 @@ import { createRefreshCallback } from "../watch/dashboard.js";
|
|
|
2
2
|
import { createTerminalFrameWriter } from "../watch/frame-writer.js";
|
|
3
3
|
import { FileWatcher } from "../watch/file-watcher.js";
|
|
4
4
|
import { renderFooterLine } from "../watch/renderer.js";
|
|
5
|
-
export function renderStatusBar(ralph) {
|
|
5
|
+
export function renderStatusBar(ralph, reviewEnabled) {
|
|
6
6
|
const pid = ralph.child.pid ?? "?";
|
|
7
|
+
const badge = reviewEnabled ? " [review]" : "";
|
|
7
8
|
switch (ralph.state) {
|
|
8
9
|
case "running":
|
|
9
|
-
return `Ralph: running (PID ${pid}) | q: stop/detach`;
|
|
10
|
+
return `Ralph: running (PID ${pid})${badge} | q: stop/detach`;
|
|
10
11
|
case "stopped":
|
|
11
12
|
return `Ralph: stopped (exit ${ralph.exitCode ?? "?"}) | q: quit`;
|
|
12
13
|
case "detached":
|
|
@@ -17,12 +18,12 @@ export function renderQuitPrompt() {
|
|
|
17
18
|
return "Stop (s) | Detach (d) | Cancel (c)";
|
|
18
19
|
}
|
|
19
20
|
export async function startRunDashboard(options) {
|
|
20
|
-
const { projectDir, interval, ralph } = options;
|
|
21
|
+
const { projectDir, interval, ralph, reviewEnabled } = options;
|
|
21
22
|
const frameWriter = createTerminalFrameWriter();
|
|
22
23
|
let showingPrompt = false;
|
|
23
24
|
let stopped = false;
|
|
24
25
|
const footerRenderer = (lastUpdated, cols) => {
|
|
25
|
-
const leftText = showingPrompt ? renderQuitPrompt() : renderStatusBar(ralph);
|
|
26
|
+
const leftText = showingPrompt ? renderQuitPrompt() : renderStatusBar(ralph, reviewEnabled);
|
|
26
27
|
return renderFooterLine(leftText, `Updated: ${lastUpdated.toISOString().slice(11, 19)}`, cols);
|
|
27
28
|
};
|
|
28
29
|
const refresh = createRefreshCallback(projectDir, (frame) => {
|
|
@@ -100,6 +101,7 @@ export async function startRunDashboard(options) {
|
|
|
100
101
|
}
|
|
101
102
|
resolve();
|
|
102
103
|
};
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- setRawMode absent in pseudo-TTY
|
|
103
105
|
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
104
106
|
process.stdin.setRawMode(true);
|
|
105
107
|
process.stdin.resume();
|
|
@@ -3,10 +3,7 @@ import { relative } from "node:path";
|
|
|
3
3
|
import { findArtifactsDir } from "./artifacts.js";
|
|
4
4
|
import { ARTIFACT_DEFINITIONS } from "../utils/artifact-definitions.js";
|
|
5
5
|
import { getPlatform } from "../platform/registry.js";
|
|
6
|
-
import { getPlatformAnalysisHint, getPlatformArchitectureHint, getPlatformEpicsStoriesHint, getPlatformPrdHint, getPlatformReadinessHint, } from "../platform/guidance.js";
|
|
7
|
-
function getCursorNextAction() {
|
|
8
|
-
return "Read _bmad/COMMANDS.md and ask Cursor to run the BMAD master agent for the next BMAD workflow";
|
|
9
|
-
}
|
|
6
|
+
import { getPlatformAnalysisHint, getPlatformArchitectureHint, getPlatformEpicsStoriesHint, getPlatformMasterAgentHint, getPlatformPrdHint, getPlatformReadinessHint, } from "../platform/guidance.js";
|
|
10
7
|
export function classifyArtifact(filename) {
|
|
11
8
|
for (const rule of ARTIFACT_DEFINITIONS) {
|
|
12
9
|
if (rule.pattern.test(filename)) {
|
|
@@ -47,13 +44,13 @@ export function getMissing(phases) {
|
|
|
47
44
|
export function suggestNext(phases, detectedPhase, platformId) {
|
|
48
45
|
const foundNames = new Set([...phases[1], ...phases[2], ...phases[3]].map((a) => a.name));
|
|
49
46
|
const platform = platformId ? getPlatform(platformId) : null;
|
|
50
|
-
if (
|
|
47
|
+
if (platform && platform.commandDelivery.kind === "index") {
|
|
51
48
|
const allPlanningArtifactsPresent = foundNames.has("PRD") &&
|
|
52
49
|
foundNames.has("Architecture") &&
|
|
53
50
|
foundNames.has("Epics & Stories") &&
|
|
54
51
|
foundNames.has("Readiness Report");
|
|
55
52
|
if (!allPlanningArtifactsPresent) {
|
|
56
|
-
return
|
|
53
|
+
return getPlatformMasterAgentHint(platform);
|
|
57
54
|
}
|
|
58
55
|
}
|
|
59
56
|
if (detectedPhase <= 1 && phases[1].length === 0) {
|
|
@@ -11,7 +11,7 @@ export function extractSectionWithInfo(content, headingPattern, maxLength = SECT
|
|
|
11
11
|
// Determine heading level from the match
|
|
12
12
|
const headingLevelMatch = match[0].match(/^(#{1,6})\s/);
|
|
13
13
|
const level = headingLevelMatch ? headingLevelMatch[1].length : 2;
|
|
14
|
-
const startIndex =
|
|
14
|
+
const startIndex = match.index + match[0].length;
|
|
15
15
|
const rest = content.slice(startIndex);
|
|
16
16
|
// Find next heading of same or higher level
|
|
17
17
|
const nextHeadingPattern = new RegExp(`^#{1,${level}}\\s`, "m");
|
package/dist/utils/constants.js
CHANGED
|
@@ -53,6 +53,28 @@ export const CONFIG_FILE = "bmalph/config.json";
|
|
|
53
53
|
/** Ralph status file path */
|
|
54
54
|
export const RALPH_STATUS_FILE = ".ralph/status.json";
|
|
55
55
|
// =============================================================================
|
|
56
|
+
// Ralph status mapping
|
|
57
|
+
// =============================================================================
|
|
58
|
+
/**
|
|
59
|
+
* Maps raw Ralph bash status strings to normalized status values.
|
|
60
|
+
* Single source of truth — used by both validate.ts and ralph-runtime-state.ts.
|
|
61
|
+
*/
|
|
62
|
+
export const RALPH_STATUS_MAP = {
|
|
63
|
+
running: "running",
|
|
64
|
+
halted: "blocked",
|
|
65
|
+
stopped: "blocked",
|
|
66
|
+
completed: "completed",
|
|
67
|
+
success: "completed",
|
|
68
|
+
graceful_exit: "completed",
|
|
69
|
+
paused: "blocked",
|
|
70
|
+
error: "blocked",
|
|
71
|
+
};
|
|
72
|
+
// =============================================================================
|
|
73
|
+
// Gitignore entries managed by bmalph
|
|
74
|
+
// =============================================================================
|
|
75
|
+
/** Entries bmalph adds to .gitignore during init and checks during doctor */
|
|
76
|
+
export const GITIGNORE_ENTRIES = [".ralph/logs/", "_bmad-output/"];
|
|
77
|
+
// =============================================================================
|
|
56
78
|
// Dashboard constants
|
|
57
79
|
// =============================================================================
|
|
58
80
|
/** Default dashboard refresh interval in milliseconds */
|
package/dist/utils/github.js
CHANGED
|
@@ -132,9 +132,10 @@ export class GitHubClient {
|
|
|
132
132
|
typeof data.sha !== "string" ||
|
|
133
133
|
!("commit" in data) ||
|
|
134
134
|
!data.commit ||
|
|
135
|
-
typeof data.commit
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
typeof data.commit.message !==
|
|
136
|
+
"string" ||
|
|
137
|
+
!data.commit.author ||
|
|
138
|
+
typeof data.commit.author.date !== "string") {
|
|
138
139
|
return {
|
|
139
140
|
success: false,
|
|
140
141
|
error: {
|