bmalph 2.3.0 → 2.4.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 CHANGED
@@ -25,7 +25,7 @@ bmalph provides:
25
25
  - `bmalph init` — Install both systems
26
26
  - `bmalph upgrade` — Update to latest versions
27
27
  - `bmalph doctor` — Check installation health
28
- - `/bmalph-implement` — Transition from BMAD to Ralph
28
+ - `bmalph implement` — Transition from BMAD to Ralph
29
29
 
30
30
  ## Supported Platforms
31
31
 
@@ -155,7 +155,7 @@ Available in any phase for supporting tasks:
155
155
 
156
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
157
 
158
- Use the `/bmalph-implement` slash command in Claude Code, or run the transition from the BMAD agents in Codex.
158
+ Run `bmalph implement` from the terminal, or use the `/bmalph-implement` slash command in Claude Code.
159
159
 
160
160
  This transitions your BMAD artifacts into Ralph's format:
161
161
 
@@ -164,10 +164,14 @@ This transitions your BMAD artifacts into Ralph's format:
164
164
  3. Copies specs to `.ralph/specs/` with changelog tracking
165
165
  4. Instructs you to start the Ralph autonomous loop
166
166
 
167
- Then start Ralph:
167
+ Then start Ralph using the driver for your platform:
168
168
 
169
169
  ```bash
170
- bash .ralph/ralph_loop.sh
170
+ # Claude Code
171
+ bash .ralph/drivers/claude-code.sh
172
+
173
+ # OpenAI Codex
174
+ bash .ralph/drivers/codex.sh
171
175
  ```
172
176
 
173
177
  Ralph picks stories one by one, implements with TDD, and commits. The loop stops when all stories are done or the circuit breaker triggers.
@@ -177,12 +181,12 @@ Ralph picks stories one by one, implements with TDD, and commits. The loop stops
177
181
  bmalph supports iterative development cycles:
178
182
 
179
183
  ```
180
- BMAD (Epic 1) → /bmalph-implement → Ralph works on Epic 1
184
+ BMAD (Epic 1) → bmalph implement → Ralph works on Epic 1
181
185
 
182
- BMAD (add Epic 2) → /bmalph-implement → Ralph sees changes + picks up Epic 2
186
+ BMAD (add Epic 2) → bmalph implement → Ralph sees changes + picks up Epic 2
183
187
  ```
184
188
 
185
- **Smart Merge**: When you run `/bmalph-implement` again after Ralph has made progress:
189
+ **Smart Merge**: When you run `bmalph implement` again after Ralph has made progress:
186
190
 
187
191
  - Completed stories (`[x]`) are preserved in the new fix_plan
188
192
  - New stories from BMAD are added as pending (`[ ]`)
@@ -198,6 +202,7 @@ BMAD (add Epic 2) → /bmalph-implement → Ralph sees changes + picks up Epic 2
198
202
  | `bmalph doctor` | Check installation health |
199
203
  | `bmalph check-updates` | Check if bundled BMAD/Ralph versions are up to date |
200
204
  | `bmalph status` | Show current project status and phase |
205
+ | `bmalph implement` | Transition BMAD planning artifacts to Ralph format |
201
206
 
202
207
  ### Global options
203
208
 
@@ -218,6 +223,24 @@ BMAD (add Epic 2) → /bmalph-implement → Ralph sees changes + picks up Epic 2
218
223
  | `-d, --description <desc>` | Project description | (prompted) |
219
224
  | `--platform <id>` | Target platform (`claude-code`, `codex`, `cursor`, `windsurf`, `copilot`, `aider`) | auto-detect |
220
225
 
226
+ ### implement options
227
+
228
+ | Flag | Description |
229
+ | --------- | ------------------------------------- |
230
+ | `--force` | Override pre-flight validation errors |
231
+
232
+ ### check-updates options
233
+
234
+ | Flag | Description |
235
+ | -------- | -------------- |
236
+ | `--json` | Output as JSON |
237
+
238
+ ### status options
239
+
240
+ | Flag | Description |
241
+ | -------- | -------------- |
242
+ | `--json` | Output as JSON |
243
+
221
244
  ### upgrade options
222
245
 
223
246
  | Flag | Description |
@@ -227,7 +250,7 @@ BMAD (add Epic 2) → /bmalph-implement → Ralph sees changes + picks up Epic 2
227
250
 
228
251
  ## Slash Commands
229
252
 
230
- bmalph installs 47 BMAD slash commands. Command delivery varies by platform:
253
+ bmalph installs 50 slash commands (45 BMAD + 5 bmalph). Command delivery varies by platform:
231
254
 
232
255
  - **Claude Code** — installed as files in `.claude/commands/` (invoke with `/command-name`)
233
256
  - **OpenAI Codex** — inlined in `AGENTS.md` (reference agents by name)
@@ -256,7 +279,7 @@ For full list, run `/bmad-help` in Claude Code.
256
279
 
257
280
  ### Transition to Ralph
258
281
 
259
- Use `/bmalph-implement` to transition from BMAD planning to Ralph implementation.
282
+ Use `bmalph implement` (or `/bmalph-implement` in Claude Code) to transition from BMAD planning to Ralph implementation.
260
283
 
261
284
  ## Project Structure (after init)
262
285
 
@@ -264,15 +287,23 @@ Use `/bmalph-implement` to transition from BMAD planning to Ralph implementation
264
287
  project/
265
288
  ├── _bmad/ # BMAD agents, workflows, core
266
289
  │ ├── _config/ # Generated configuration
267
- │ │ └── config.yaml # Platform config
290
+ │ │ ├── config.yaml # Platform config
291
+ │ │ ├── task-manifest.csv # Combined task manifest
292
+ │ │ ├── workflow-manifest.csv # Combined workflow manifest
293
+ │ │ └── bmad-help.csv # Combined help manifest
268
294
  │ ├── core/
269
295
  │ │ ├── agents/ # Master agent
270
296
  │ │ ├── tasks/ # Workflow tasks
271
- │ │ └── workflows/ # Brainstorming, party-mode, etc.
297
+ │ │ ├── workflows/ # Brainstorming, party-mode, etc.
298
+ │ │ ├── module.yaml # Core module metadata
299
+ │ │ └── module-help.csv # Core module help entries
272
300
  │ └── bmm/
273
301
  │ ├── agents/ # Analyst, PM, Architect, Dev, QA, etc.
302
+ │ ├── data/ # Templates (project-context-template.md)
274
303
  │ ├── workflows/ # Phase 1-4 workflows
275
- └── teams/ # Agent team definitions
304
+ ├── teams/ # Agent team definitions
305
+ │ ├── module.yaml # BMM module metadata
306
+ │ └── module-help.csv # BMM module help entries
276
307
  ├── _bmad-output/ # BMAD planning artifacts (generated)
277
308
  │ ├── planning-artifacts/ # PRD, architecture, stories
278
309
  │ ├── implementation-artifacts/ # Sprint plans (optional)
@@ -286,14 +317,16 @@ project/
286
317
  │ ├── drivers/ # Platform driver scripts
287
318
  │ │ ├── claude-code.sh # Claude Code driver (uses `claude`)
288
319
  │ │ └── codex.sh # OpenAI Codex driver (uses `codex exec`)
289
- │ ├── lib/ # Circuit breaker, response analyzer
320
+ │ ├── lib/ # Shell libraries
321
+ │ ├── docs/generated/ # Generated documentation
290
322
  │ ├── specs/ # Copied from _bmad-output during transition
291
323
  │ ├── logs/ # Loop execution logs
292
324
  │ ├── PROMPT.md # Iteration prompt template
293
- │ ├── PROJECT_CONTEXT.md # Extracted project context (after /bmalph-implement)
294
- │ ├── SPECS_CHANGELOG.md # Spec diff since last run (after /bmalph-implement)
325
+ │ ├── PROJECT_CONTEXT.md # Extracted project context (after bmalph implement)
326
+ │ ├── SPECS_CHANGELOG.md # Spec diff since last run (after bmalph implement)
327
+ │ ├── SPECS_INDEX.md # Prioritized spec file index (after bmalph implement)
295
328
  │ ├── @AGENT.md # Agent build instructions
296
- │ └── @fix_plan.md # Generated task list (after /bmalph-implement)
329
+ │ └── @fix_plan.md # Generated task list (after bmalph implement)
297
330
  ├── bmalph/ # State management
298
331
  │ ├── config.json # Project config (name, description, platform)
299
332
  │ └── state/ # Phase tracking data
@@ -352,8 +385,8 @@ wsl --install
352
385
  If you get permission errors:
353
386
 
354
387
  ```bash
355
- # Unix/Mac: Make ralph_loop.sh executable
356
- chmod +x .ralph/ralph_loop.sh
388
+ # Unix/Mac: Make driver scripts executable
389
+ chmod +x .ralph/drivers/*.sh
357
390
 
358
391
  # Check file ownership
359
392
  ls -la .ralph/
@@ -448,10 +481,10 @@ claude
448
481
  # Phase 3: /architect → create architecture and stories
449
482
 
450
483
  # 4. Transition to Ralph
451
- # Use /bmalph-implement to generate @fix_plan.md
484
+ # Run: bmalph implement
452
485
 
453
486
  # 5. Start autonomous implementation
454
- bash .ralph/ralph_loop.sh
487
+ bash .ralph/drivers/claude-code.sh
455
488
  ```
456
489
 
457
490
  **Other platforms:**
@@ -465,9 +498,9 @@ bash .ralph/ralph_loop.sh
465
498
  # 3. Reference BMAD agents by name (analyst, pm, architect)
466
499
  # Follow phases: Analysis → Planning → Solutioning
467
500
 
468
- # 4. For full tier platforms (Codex), transition via BMAD agents
469
- # then start Ralph:
470
- bash .ralph/ralph_loop.sh
501
+ # 4. For full tier platforms (Codex), transition to Ralph:
502
+ # Run: bmalph implement
503
+ bash .ralph/drivers/codex.sh
471
504
  ```
472
505
 
473
506
  ## Contributing
package/dist/cli.js CHANGED
@@ -6,6 +6,8 @@ import { upgradeCommand } from "./commands/upgrade.js";
6
6
  import { doctorCommand } from "./commands/doctor.js";
7
7
  import { checkUpdatesCommand } from "./commands/check-updates.js";
8
8
  import { statusCommand } from "./commands/status.js";
9
+ import { implementCommand } from "./commands/implement.js";
10
+ import { resetCommand } from "./commands/reset.js";
9
11
  import { setVerbose, setQuiet } from "./utils/logger.js";
10
12
  import { getPackageVersion } from "./installer.js";
11
13
  import { isEnoent } from "./utils/errors.js";
@@ -79,4 +81,15 @@ program
79
81
  .description("Show current project status and phase")
80
82
  .option("--json", "Output as JSON")
81
83
  .action(async (opts) => statusCommand({ ...opts, projectDir: await resolveAndValidateProjectDir() }));
84
+ program
85
+ .command("implement")
86
+ .description("Transition BMAD planning artifacts to Ralph implementation format")
87
+ .option("--force", "Override pre-flight validation errors")
88
+ .action(async (opts) => implementCommand({ ...opts, projectDir: await resolveAndValidateProjectDir() }));
89
+ program
90
+ .command("reset")
91
+ .description("Remove all bmalph files from the project")
92
+ .option("--dry-run", "Preview changes without removing files")
93
+ .option("--force", "Skip confirmation prompt")
94
+ .action(async (opts) => resetCommand({ ...opts, projectDir: await resolveAndValidateProjectDir() }));
82
95
  void program.parseAsync();
@@ -64,10 +64,10 @@ export async function runDoctor(options) {
64
64
  }
65
65
  return { passed, failed };
66
66
  }
67
- async function checkBashAvailable() {
67
+ async function checkCommandAvailable(command) {
68
68
  const { execSync } = await import("child_process");
69
69
  try {
70
- const cmd = process.platform === "win32" ? "where bash" : "which bash";
70
+ const cmd = process.platform === "win32" ? `where ${command}` : `which ${command}`;
71
71
  execSync(cmd, { stdio: "ignore" });
72
72
  return true;
73
73
  }
@@ -88,18 +88,33 @@ async function checkNodeVersion(_projectDir) {
88
88
  };
89
89
  }
90
90
  async function checkBash(_projectDir) {
91
- const bashAvailable = await checkBashAvailable();
91
+ const available = await checkCommandAvailable("bash");
92
92
  return {
93
93
  label: "bash available",
94
- passed: bashAvailable,
95
- detail: bashAvailable ? undefined : "bash not found in PATH",
96
- hint: bashAvailable
94
+ passed: available,
95
+ detail: available ? undefined : "bash not found in PATH",
96
+ hint: available
97
97
  ? undefined
98
98
  : process.platform === "win32"
99
99
  ? "Install Git Bash or WSL: https://git-scm.com/downloads"
100
100
  : "Install bash via your package manager (apt, brew, etc.)",
101
101
  };
102
102
  }
103
+ async function checkJq(_projectDir) {
104
+ const available = await checkCommandAvailable("jq");
105
+ return {
106
+ label: "jq available",
107
+ passed: available,
108
+ detail: available ? undefined : "jq not found in PATH",
109
+ hint: available
110
+ ? undefined
111
+ : process.platform === "win32"
112
+ ? "Install jq: choco install jq (or: winget install jqlang.jq)"
113
+ : process.platform === "darwin"
114
+ ? "Install jq: brew install jq"
115
+ : "Install jq: sudo apt-get install jq",
116
+ };
117
+ }
103
118
  async function checkBmadDir(projectDir) {
104
119
  return checkDir(join(projectDir, "_bmad"), "_bmad/ directory present", "Run: bmalph init");
105
120
  }
@@ -399,6 +414,7 @@ async function checkUpstreamGitHubStatus(_projectDir) {
399
414
  const CORE_CHECKS = [
400
415
  { id: "node-version", run: checkNodeVersion },
401
416
  { id: "bash-available", run: checkBash },
417
+ { id: "jq-available", run: checkJq },
402
418
  { id: "config-valid", run: checkConfig },
403
419
  { id: "bmad-dir", run: checkBmadDir },
404
420
  { id: "ralph-loop", run: checkRalphLoop },
@@ -0,0 +1,6 @@
1
+ interface ImplementOptions {
2
+ force?: boolean;
3
+ projectDir: string;
4
+ }
5
+ export declare function implementCommand(options: ImplementOptions): Promise<void>;
6
+ export {};
@@ -0,0 +1,82 @@
1
+ import chalk from "chalk";
2
+ import { access } from "fs/promises";
3
+ import { join } from "path";
4
+ import { runTransition } from "../transition/orchestration.js";
5
+ import { withErrorHandling } from "../utils/errors.js";
6
+ import { resolveProjectPlatform } from "../platform/resolve.js";
7
+ export async function implementCommand(options) {
8
+ await withErrorHandling(() => runImplement(options));
9
+ }
10
+ async function runImplement(options) {
11
+ const { projectDir, force } = options;
12
+ // Re-run protection: warn if implement was already run
13
+ try {
14
+ await access(join(projectDir, ".ralph/@fix_plan.md"));
15
+ if (!force) {
16
+ console.log(chalk.yellow("Warning: bmalph implement has already been run."));
17
+ console.log("Re-running will overwrite PROMPT.md, PROJECT_CONTEXT.md, @AGENT.md, and SPECS_INDEX.md.");
18
+ console.log("Fix plan progress will be preserved.\n");
19
+ console.log(`Use ${chalk.bold("--force")} to proceed anyway.`);
20
+ process.exitCode = 1;
21
+ return;
22
+ }
23
+ }
24
+ catch {
25
+ // fix_plan doesn't exist — first run, proceed
26
+ }
27
+ const platform = await resolveProjectPlatform(projectDir);
28
+ const result = await runTransition(projectDir, { force });
29
+ // Print preflight issues with severity icons
30
+ if (result.preflightIssues && result.preflightIssues.length > 0) {
31
+ console.log(chalk.bold("\nPre-flight checks\n"));
32
+ for (const issue of result.preflightIssues) {
33
+ console.log(` ${severityIcon(issue)} ${issue.message}`);
34
+ if (issue.suggestion) {
35
+ console.log(chalk.dim(` ${issue.suggestion}`));
36
+ }
37
+ }
38
+ console.log("");
39
+ }
40
+ // Print warnings
41
+ if (result.warnings.length > 0) {
42
+ for (const warning of result.warnings) {
43
+ console.log(chalk.yellow(` ! ${warning}`));
44
+ }
45
+ console.log("");
46
+ }
47
+ // Generated files summary
48
+ if (result.generatedFiles.length > 0) {
49
+ console.log(chalk.bold("\nGenerated files\n"));
50
+ for (const file of result.generatedFiles) {
51
+ const icon = file.action === "created" ? chalk.green("+") : chalk.cyan("~");
52
+ console.log(` ${icon} ${file.path}`);
53
+ }
54
+ console.log("");
55
+ }
56
+ // Summary
57
+ const preserved = result.fixPlanPreserved ? chalk.dim(" (progress preserved)") : "";
58
+ console.log(chalk.green(`Transition complete: ${result.storiesCount} stories`) + preserved);
59
+ if (result.warnings.length > 0) {
60
+ console.log(chalk.yellow(` ${result.warnings.length} warning(s)`));
61
+ }
62
+ // Driver instructions
63
+ console.log("");
64
+ if (platform.tier === "full") {
65
+ console.log(`Start the Ralph loop:\n`);
66
+ console.log(` bash .ralph/drivers/${platform.id}.sh`);
67
+ }
68
+ else {
69
+ console.log(`Ralph requires a full-tier platform (claude-code or codex). ` +
70
+ `Current platform: ${platform.displayName}`);
71
+ }
72
+ }
73
+ function severityIcon(issue) {
74
+ switch (issue.severity) {
75
+ case "error":
76
+ return chalk.red("\u2717");
77
+ case "warning":
78
+ return chalk.yellow("!");
79
+ case "info":
80
+ return chalk.dim("i");
81
+ }
82
+ }
@@ -0,0 +1,7 @@
1
+ interface ResetOptions {
2
+ dryRun?: boolean;
3
+ force?: boolean;
4
+ projectDir: string;
5
+ }
6
+ export declare function resetCommand(options: ResetOptions): Promise<void>;
7
+ export {};
@@ -0,0 +1,81 @@
1
+ import chalk from "chalk";
2
+ import inquirer from "inquirer";
3
+ import { isInitialized } from "../installer.js";
4
+ import { buildResetPlan, executeResetPlan, planToDryRunActions } from "../reset.js";
5
+ import { formatDryRunSummary } from "../utils/dryrun.js";
6
+ import { withErrorHandling } from "../utils/errors.js";
7
+ import { resolveProjectPlatform } from "../platform/resolve.js";
8
+ export async function resetCommand(options) {
9
+ await withErrorHandling(() => runReset(options));
10
+ }
11
+ async function runReset(options) {
12
+ const projectDir = options.projectDir;
13
+ if (!(await isInitialized(projectDir))) {
14
+ console.log(chalk.red("bmalph is not initialized. Nothing to reset."));
15
+ return;
16
+ }
17
+ const platform = await resolveProjectPlatform(projectDir);
18
+ const plan = await buildResetPlan(projectDir, platform);
19
+ // Preview
20
+ const actions = planToDryRunActions(plan);
21
+ if (actions.length === 0) {
22
+ console.log(chalk.dim("Nothing to reset."));
23
+ return;
24
+ }
25
+ // Dry-run mode
26
+ if (options.dryRun) {
27
+ console.log(formatDryRunSummary(actions));
28
+ return;
29
+ }
30
+ // Show preview before confirmation
31
+ for (const action of actions) {
32
+ if (action.type === "delete") {
33
+ console.log(` ${chalk.red("delete")} ${action.path}`);
34
+ }
35
+ else if (action.type === "modify") {
36
+ console.log(` ${chalk.yellow("modify")} ${action.path}`);
37
+ }
38
+ else if (action.type === "warn") {
39
+ console.log(` ${chalk.yellow("warn")} ${action.path}${action.reason ? ` (${action.reason})` : ""}`);
40
+ }
41
+ }
42
+ // Confirm unless --force or non-interactive
43
+ if (!options.force) {
44
+ if (!process.stdin.isTTY) {
45
+ throw new Error("Non-interactive mode requires --force flag for reset");
46
+ }
47
+ const { confirm } = await inquirer.prompt([
48
+ {
49
+ type: "confirm",
50
+ name: "confirm",
51
+ message: "This will remove all bmalph files from the project. Continue?",
52
+ default: false,
53
+ },
54
+ ]);
55
+ if (!confirm) {
56
+ console.log("Aborted.");
57
+ return;
58
+ }
59
+ }
60
+ // Execute
61
+ console.log("\nResetting...");
62
+ await executeResetPlan(projectDir, plan);
63
+ // Summary
64
+ console.log(chalk.green("\nReset complete."));
65
+ if (plan.directories.length > 0) {
66
+ console.log(chalk.dim("\nRemoved:"));
67
+ for (const dir of plan.directories) {
68
+ console.log(` ${dir}/`);
69
+ }
70
+ }
71
+ if (plan.commandFiles.length > 0) {
72
+ console.log(chalk.dim("\nRemoved commands:"));
73
+ for (const file of plan.commandFiles) {
74
+ console.log(` ${file}`);
75
+ }
76
+ }
77
+ // Show warnings
78
+ for (const warning of plan.warnings) {
79
+ console.log(chalk.yellow(`\nNote: ${warning.path} ${warning.message}`));
80
+ }
81
+ }
@@ -2,6 +2,8 @@ import chalk from "chalk";
2
2
  import { readConfig } from "../utils/config.js";
3
3
  import { readState, readRalphStatus, getPhaseLabel, getPhaseInfo } from "../utils/state.js";
4
4
  import { withErrorHandling } from "../utils/errors.js";
5
+ import { resolveProjectPlatform } from "../platform/resolve.js";
6
+ import { scanProjectArtifacts } from "../transition/artifact-scan.js";
5
7
  export async function statusCommand(options) {
6
8
  await withErrorHandling(() => runStatus(options));
7
9
  }
@@ -15,17 +17,32 @@ export async function runStatus(options) {
15
17
  }
16
18
  // Read current state
17
19
  const state = await readState(projectDir);
18
- const phase = state?.currentPhase ?? 1;
20
+ const storedPhase = state?.currentPhase ?? 1;
19
21
  const status = state?.status ?? "planning";
20
- const phaseName = getPhaseLabel(phase);
21
- const phaseInfo = getPhaseInfo(phase);
22
22
  // Read Ralph status if in implementation phase
23
23
  let ralphStatus = null;
24
- if (phase === 4) {
24
+ if (storedPhase === 4) {
25
25
  ralphStatus = await readRalphStatus(projectDir);
26
26
  }
27
- // Determine next action
28
- const nextAction = getNextAction(phase, status, ralphStatus);
27
+ // Scan artifacts for phases 1-3 to detect actual progress
28
+ let artifactScan = null;
29
+ let phase = storedPhase;
30
+ let phaseDetected = false;
31
+ if (phase < 4) {
32
+ artifactScan = await scanProjectArtifacts(projectDir);
33
+ if (artifactScan && artifactScan.detectedPhase > phase) {
34
+ phase = artifactScan.detectedPhase;
35
+ phaseDetected = true;
36
+ }
37
+ }
38
+ const phaseName = getPhaseLabel(phase);
39
+ const phaseInfo = getPhaseInfo(phase);
40
+ // Resolve platform for next action hints
41
+ const platform = await resolveProjectPlatform(projectDir);
42
+ // Determine next action — use artifact-based suggestion when available
43
+ const nextAction = artifactScan && phaseDetected
44
+ ? artifactScan.nextAction
45
+ : getNextAction(phase, status, ralphStatus, platform);
29
46
  if (options.json) {
30
47
  const output = {
31
48
  phase,
@@ -40,6 +57,14 @@ export async function runStatus(options) {
40
57
  tasksTotal: ralphStatus.tasksTotal,
41
58
  };
42
59
  }
60
+ if (artifactScan) {
61
+ output.artifacts = {
62
+ directory: artifactScan.directory,
63
+ found: artifactScan.found,
64
+ detectedPhase: artifactScan.detectedPhase,
65
+ missing: artifactScan.missing,
66
+ };
67
+ }
43
68
  if (nextAction) {
44
69
  output.nextAction = nextAction;
45
70
  }
@@ -48,9 +73,18 @@ export async function runStatus(options) {
48
73
  }
49
74
  // Human-readable output
50
75
  console.log(chalk.bold("bmalph status\n"));
51
- console.log(` ${chalk.cyan("Phase:")} ${phase} - ${phaseName}`);
76
+ const phaseLabel = phaseDetected
77
+ ? `${phase} - ${phaseName} (detected from artifacts)`
78
+ : `${phase} - ${phaseName}`;
79
+ console.log(` ${chalk.cyan("Phase:")} ${phaseLabel}`);
52
80
  console.log(` ${chalk.cyan("Agent:")} ${phaseInfo.agent}`);
53
81
  console.log(` ${chalk.cyan("Status:")} ${formatStatus(status)}`);
82
+ // Show artifact checklist for phases 1-3
83
+ if (artifactScan) {
84
+ console.log("");
85
+ console.log(chalk.bold(` Artifacts (${artifactScan.directory})`));
86
+ printArtifactChecklist(artifactScan);
87
+ }
54
88
  if (phase === 4 && ralphStatus) {
55
89
  console.log("");
56
90
  console.log(chalk.bold(" Ralph Loop"));
@@ -68,6 +102,45 @@ export async function runStatus(options) {
68
102
  console.log(` ${chalk.cyan("Next:")} ${nextAction}`);
69
103
  }
70
104
  }
105
+ const ARTIFACT_DEFINITIONS = [
106
+ { phase: 1, name: "Product Brief", required: false },
107
+ { phase: 1, name: "Market Research", required: false },
108
+ { phase: 1, name: "Domain Research", required: false },
109
+ { phase: 1, name: "Technical Research", required: false },
110
+ { phase: 2, name: "PRD", required: true },
111
+ { phase: 2, name: "UX Design", required: false },
112
+ { phase: 3, name: "Architecture", required: true },
113
+ { phase: 3, name: "Epics & Stories", required: true },
114
+ { phase: 3, name: "Readiness Report", required: true },
115
+ ];
116
+ const PHASE_LABELS = {
117
+ 1: "Phase 1 - Analysis",
118
+ 2: "Phase 2 - Planning",
119
+ 3: "Phase 3 - Solutioning",
120
+ };
121
+ function printArtifactChecklist(scan) {
122
+ const foundByName = new Map();
123
+ for (const artifacts of [scan.phases[1], scan.phases[2], scan.phases[3]]) {
124
+ for (const artifact of artifacts) {
125
+ foundByName.set(artifact.name, artifact);
126
+ }
127
+ }
128
+ let currentPhase = 0;
129
+ for (const def of ARTIFACT_DEFINITIONS) {
130
+ if (def.phase !== currentPhase) {
131
+ currentPhase = def.phase;
132
+ console.log(` ${PHASE_LABELS[currentPhase]}`);
133
+ }
134
+ const found = foundByName.get(def.name);
135
+ if (found) {
136
+ console.log(` ${chalk.green("*")} ${def.name} (${found.filename})`);
137
+ }
138
+ else {
139
+ const suffix = def.required ? " (required)" : "";
140
+ console.log(` ${chalk.dim("-")} ${def.name}${suffix}`);
141
+ }
142
+ }
143
+ }
71
144
  function formatStatus(status) {
72
145
  switch (status) {
73
146
  case "planning":
@@ -94,7 +167,7 @@ function formatRalphStatus(status) {
94
167
  return status;
95
168
  }
96
169
  }
97
- function getNextAction(phase, status, ralphStatus) {
170
+ function getNextAction(phase, status, ralphStatus, platform) {
98
171
  if (status === "completed") {
99
172
  return null;
100
173
  }
@@ -104,10 +177,13 @@ function getNextAction(phase, status, ralphStatus) {
104
177
  case 2:
105
178
  return "Run /pm to create PRD";
106
179
  case 3:
107
- return "Run /bmalph-implement when ready for implementation";
180
+ return "Run: bmalph implement";
108
181
  case 4:
109
182
  if (!ralphStatus || ralphStatus.status === "not_started") {
110
- return "Start Ralph loop with: bash .ralph/ralph_loop.sh";
183
+ if (platform.tier === "full") {
184
+ return `Start Ralph loop with: bash .ralph/drivers/${platform.id}.sh`;
185
+ }
186
+ return "Ralph requires a full-tier platform (Claude Code or Codex)";
111
187
  }
112
188
  if (ralphStatus.status === "blocked") {
113
189
  return "Review Ralph logs: bmalph doctor";
@@ -36,7 +36,6 @@ Use \`/bmalph\` to navigate phases. Use \`/bmad-help\` to discover all commands.
36
36
  | \`/bmalph-implement\` | Transition planning artifacts → prepare Ralph loop |
37
37
  | \`/bmalph-upgrade\` | Update bundled assets to match current bmalph version |
38
38
  | \`/bmalph-doctor\` | Check project health and report issues |
39
- | \`/bmalph-reset\` | Reset state (soft or hard reset with confirmation) |
40
39
 
41
40
  ### Available Agents
42
41
 
@@ -0,0 +1,18 @@
1
+ import type { Platform } from "./platform/types.js";
2
+ import type { DryRunAction } from "./utils/dryrun.js";
3
+ export interface ResetPlan {
4
+ directories: string[];
5
+ commandFiles: string[];
6
+ instructionsCleanup: {
7
+ path: string;
8
+ sectionsToRemove: string[];
9
+ } | null;
10
+ gitignoreLines: string[];
11
+ warnings: Array<{
12
+ path: string;
13
+ message: string;
14
+ }>;
15
+ }
16
+ export declare function buildResetPlan(projectDir: string, platform: Platform): Promise<ResetPlan>;
17
+ export declare function executeResetPlan(projectDir: string, plan: ResetPlan): Promise<void>;
18
+ export declare function planToDryRunActions(plan: ResetPlan): DryRunAction[];