bmalph 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +111 -30
  2. package/bundled-versions.json +1 -2
  3. package/dist/cli.js +3 -2
  4. package/dist/commands/check-updates.js +5 -27
  5. package/dist/commands/doctor.d.ts +14 -2
  6. package/dist/commands/doctor.js +99 -56
  7. package/dist/commands/init.d.ts +1 -0
  8. package/dist/commands/init.js +75 -9
  9. package/dist/commands/upgrade.js +8 -5
  10. package/dist/installer.d.ts +16 -5
  11. package/dist/installer.js +245 -128
  12. package/dist/platform/aider.d.ts +2 -0
  13. package/dist/platform/aider.js +71 -0
  14. package/dist/platform/claude-code.d.ts +2 -0
  15. package/dist/platform/claude-code.js +88 -0
  16. package/dist/platform/codex.d.ts +2 -0
  17. package/dist/platform/codex.js +67 -0
  18. package/dist/platform/copilot.d.ts +2 -0
  19. package/dist/platform/copilot.js +71 -0
  20. package/dist/platform/cursor.d.ts +2 -0
  21. package/dist/platform/cursor.js +71 -0
  22. package/dist/platform/detect.d.ts +7 -0
  23. package/dist/platform/detect.js +23 -0
  24. package/dist/platform/index.d.ts +4 -0
  25. package/dist/platform/index.js +3 -0
  26. package/dist/platform/registry.d.ts +4 -0
  27. package/dist/platform/registry.js +27 -0
  28. package/dist/platform/resolve.d.ts +8 -0
  29. package/dist/platform/resolve.js +24 -0
  30. package/dist/platform/types.d.ts +41 -0
  31. package/dist/platform/types.js +7 -0
  32. package/dist/platform/windsurf.d.ts +2 -0
  33. package/dist/platform/windsurf.js +71 -0
  34. package/dist/transition/artifacts.js +1 -1
  35. package/dist/transition/fix-plan.d.ts +1 -1
  36. package/dist/transition/fix-plan.js +3 -2
  37. package/dist/transition/orchestration.js +1 -1
  38. package/dist/transition/specs-changelog.js +4 -1
  39. package/dist/transition/specs-index.js +2 -3
  40. package/dist/utils/config.d.ts +2 -1
  41. package/dist/utils/errors.js +3 -0
  42. package/dist/utils/github.d.ts +0 -1
  43. package/dist/utils/github.js +1 -18
  44. package/dist/utils/json.js +2 -2
  45. package/dist/utils/state.js +7 -1
  46. package/dist/utils/validate.d.ts +1 -0
  47. package/dist/utils/validate.js +35 -4
  48. package/package.json +4 -4
  49. package/ralph/drivers/claude-code.sh +118 -0
  50. package/ralph/drivers/codex.sh +81 -0
  51. package/ralph/ralph_import.sh +11 -0
  52. package/ralph/ralph_loop.sh +37 -64
  53. package/ralph/templates/ralphrc.template +7 -0
@@ -1,13 +1,60 @@
1
1
  import chalk from "chalk";
2
2
  import inquirer from "inquirer";
3
3
  import { writeConfig } from "../utils/config.js";
4
- import { installProject, mergeClaudeMd, isInitialized, previewInstall, getBundledVersions, } from "../installer.js";
4
+ import { installProject, mergeInstructionsFile, isInitialized, hasExistingBmadDir, previewInstall, getBundledVersions, } from "../installer.js";
5
5
  import { formatDryRunSummary } from "../utils/dryrun.js";
6
6
  import { validateProjectName } from "../utils/validate.js";
7
7
  import { withErrorHandling } from "../utils/errors.js";
8
+ import { isPlatformId, getPlatform } from "../platform/registry.js";
9
+ import { detectPlatform } from "../platform/detect.js";
8
10
  export async function initCommand(options) {
9
11
  await withErrorHandling(() => runInit(options));
10
12
  }
13
+ /**
14
+ * Resolve which platform to use:
15
+ * 1. Explicit --platform flag
16
+ * 2. Auto-detect from filesystem markers
17
+ * 3. Interactive prompt (if TTY)
18
+ * 4. Default to claude-code (non-interactive)
19
+ */
20
+ async function resolvePlatform(projectDir, explicit) {
21
+ // 1. Explicit flag
22
+ if (explicit) {
23
+ if (!isPlatformId(explicit)) {
24
+ throw new Error(`Unknown platform: "${explicit}". ` +
25
+ `Valid platforms: claude-code, codex, cursor, windsurf, copilot, aider`);
26
+ }
27
+ return getPlatform(explicit);
28
+ }
29
+ // 2. Auto-detect
30
+ const detection = await detectPlatform(projectDir);
31
+ if (detection.detected) {
32
+ return getPlatform(detection.detected);
33
+ }
34
+ // 3. Interactive prompt if multiple candidates or none detected
35
+ if (process.stdin.isTTY) {
36
+ const choices = [
37
+ { name: "Claude Code", value: "claude-code" },
38
+ { name: "OpenAI Codex", value: "codex" },
39
+ { name: "Cursor", value: "cursor" },
40
+ { name: "Windsurf", value: "windsurf" },
41
+ { name: "GitHub Copilot", value: "copilot" },
42
+ { name: "Aider", value: "aider" },
43
+ ];
44
+ const { platformId } = await inquirer.prompt([
45
+ {
46
+ type: "list",
47
+ name: "platformId",
48
+ message: "Which platform are you using?",
49
+ choices,
50
+ default: detection.candidates[0] ?? "claude-code",
51
+ },
52
+ ]);
53
+ return getPlatform(platformId);
54
+ }
55
+ // 4. Non-interactive default
56
+ return getPlatform("claude-code");
57
+ }
11
58
  async function runInit(options) {
12
59
  const projectDir = options.projectDir;
13
60
  if (await isInitialized(projectDir)) {
@@ -15,9 +62,16 @@ async function runInit(options) {
15
62
  console.log("Use 'bmalph upgrade' to update bundled assets to the latest version.");
16
63
  return;
17
64
  }
65
+ if (await hasExistingBmadDir(projectDir)) {
66
+ console.log(chalk.cyan("Existing BMAD installation detected."));
67
+ console.log("Framework files in _bmad/ will be replaced with the managed version.");
68
+ console.log("Planning artifacts in _bmad-output/ will not be modified.\n");
69
+ }
70
+ // Resolve platform
71
+ const platform = await resolvePlatform(projectDir, options.platform);
18
72
  // Handle dry-run mode
19
73
  if (options.dryRun) {
20
- const preview = await previewInstall(projectDir);
74
+ const preview = await previewInstall(projectDir, platform);
21
75
  const actions = [
22
76
  ...preview.wouldCreate.map((p) => ({ type: "create", path: p })),
23
77
  ...preview.wouldModify.map((p) => ({ type: "modify", path: p })),
@@ -64,31 +118,43 @@ async function runInit(options) {
64
118
  throw new Error("Project name cannot be empty");
65
119
  }
66
120
  const validatedName = validateProjectName(name);
67
- console.log(chalk.blue("\nInstalling BMAD + Ralph..."));
68
- await installProject(projectDir);
121
+ console.log(chalk.blue(`\nInstalling BMAD + Ralph for ${platform.displayName}...`));
122
+ await installProject(projectDir, platform);
69
123
  const bundledVersions = getBundledVersions();
70
124
  const config = {
71
125
  name: validatedName,
72
126
  description: description ?? "",
73
127
  createdAt: new Date().toISOString(),
128
+ platform: platform.id,
74
129
  upstreamVersions: bundledVersions,
75
130
  };
76
131
  try {
77
132
  await writeConfig(projectDir, config);
78
- await mergeClaudeMd(projectDir);
133
+ await mergeInstructionsFile(projectDir, platform);
79
134
  }
80
135
  catch (err) {
81
136
  throw new Error(`Partial installation: files were copied but configuration failed. ` +
82
- `Run 'bmalph init' again to retry. ` +
83
- `Cause: ${err instanceof Error ? err.message : String(err)}`);
137
+ `Run 'bmalph init' again to retry.`, { cause: err });
84
138
  }
85
139
  console.log(chalk.green("\nbmalph initialized successfully!"));
86
140
  console.log(`\n Project: ${chalk.bold(config.name)}`);
141
+ console.log(` Platform: ${chalk.bold(platform.displayName)}`);
87
142
  console.log(`\nInstalled:`);
88
143
  console.log(` _bmad/ BMAD agents and workflows`);
89
144
  console.log(` .ralph/ Ralph loop and templates`);
90
- console.log(` .claude/commands/ Slash command (/bmalph)`);
145
+ if (platform.commandDelivery.kind === "directory") {
146
+ console.log(` ${platform.commandDelivery.dir}/ Slash commands`);
147
+ }
91
148
  console.log(` bmalph/ State management`);
149
+ // Platform-specific next step guidance
92
150
  console.log(`\nNext step:`);
93
- console.log(` Use ${chalk.cyan("/bmalph")} in Claude Code to see your current phase and commands.`);
151
+ if (platform.id === "claude-code") {
152
+ console.log(` Use ${chalk.cyan("/bmalph")} in Claude Code to see your current phase and commands.`);
153
+ }
154
+ else if (platform.id === "codex") {
155
+ console.log(` Ask Codex to ${chalk.cyan("run the BMAD master agent")} to navigate phases.`);
156
+ }
157
+ else {
158
+ console.log(` Ask your AI assistant to ${chalk.cyan("use the BMAD agents")} defined in ${chalk.cyan(platform.instructionsFile)}.`);
159
+ }
94
160
  }
@@ -1,9 +1,10 @@
1
1
  import chalk from "chalk";
2
2
  import inquirer from "inquirer";
3
- import { isInitialized, copyBundledAssets, mergeClaudeMd, previewUpgrade, getBundledVersions, } from "../installer.js";
3
+ import { isInitialized, copyBundledAssets, mergeInstructionsFile, previewUpgrade, getBundledVersions, } from "../installer.js";
4
4
  import { readConfig, writeConfig } from "../utils/config.js";
5
5
  import { formatDryRunSummary } from "../utils/dryrun.js";
6
6
  import { withErrorHandling } from "../utils/errors.js";
7
+ import { resolveProjectPlatform } from "../platform/resolve.js";
7
8
  export async function upgradeCommand(options) {
8
9
  await withErrorHandling(() => runUpgrade(options));
9
10
  }
@@ -13,9 +14,11 @@ async function runUpgrade(options) {
13
14
  console.log(chalk.red("bmalph is not initialized. Run 'bmalph init' first."));
14
15
  return;
15
16
  }
17
+ // Read platform from existing config
18
+ const platform = await resolveProjectPlatform(projectDir);
16
19
  // Handle dry-run mode
17
20
  if (options.dryRun) {
18
- const preview = await previewUpgrade(projectDir);
21
+ const preview = await previewUpgrade(projectDir, platform);
19
22
  const actions = [
20
23
  ...preview.wouldUpdate.map((p) => ({ type: "modify", path: p })),
21
24
  ...preview.wouldCreate.map((p) => ({ type: "create", path: p })),
@@ -41,9 +44,9 @@ async function runUpgrade(options) {
41
44
  return;
42
45
  }
43
46
  }
44
- console.log(chalk.blue("Upgrading bundled assets..."));
45
- const result = await copyBundledAssets(projectDir);
46
- await mergeClaudeMd(projectDir);
47
+ console.log(chalk.blue(`Upgrading bundled assets for ${platform.displayName}...`));
48
+ const result = await copyBundledAssets(projectDir, platform);
49
+ await mergeInstructionsFile(projectDir, platform);
47
50
  // Update upstreamVersions in config to match bundled versions
48
51
  const config = await readConfig(projectDir);
49
52
  if (config) {
@@ -1,7 +1,7 @@
1
+ import type { Platform } from "./platform/types.js";
1
2
  export declare function getPackageVersion(): string;
2
3
  export interface BundledVersions {
3
4
  bmadCommit: string;
4
- ralphCommit: string;
5
5
  }
6
6
  export declare function getBundledVersions(): BundledVersions;
7
7
  export declare function getBundledBmadDir(): string;
@@ -18,11 +18,22 @@ export interface PreviewInstallResult {
18
18
  export interface PreviewUpgradeResult {
19
19
  wouldUpdate: string[];
20
20
  wouldCreate: string[];
21
+ wouldPreserve: string[];
21
22
  }
22
- export declare function copyBundledAssets(projectDir: string): Promise<UpgradeResult>;
23
- export declare function installProject(projectDir: string): Promise<void>;
23
+ export declare function copyBundledAssets(projectDir: string, platform?: Platform): Promise<UpgradeResult>;
24
+ export declare function installProject(projectDir: string, platform?: Platform): Promise<void>;
24
25
  export declare function generateManifests(projectDir: string): Promise<void>;
26
+ /**
27
+ * Merge the BMAD instructions snippet into the platform's instructions file.
28
+ * Creates the file if it doesn't exist, replaces an existing BMAD section on upgrade.
29
+ */
30
+ export declare function mergeInstructionsFile(projectDir: string, platform?: Platform): Promise<void>;
31
+ /**
32
+ * @deprecated Use `mergeInstructionsFile(projectDir)` instead.
33
+ * Kept for backward compatibility during migration.
34
+ */
25
35
  export declare function mergeClaudeMd(projectDir: string): Promise<void>;
26
36
  export declare function isInitialized(projectDir: string): Promise<boolean>;
27
- export declare function previewInstall(projectDir: string): Promise<PreviewInstallResult>;
28
- export declare function previewUpgrade(projectDir: string): Promise<PreviewUpgradeResult>;
37
+ export declare function hasExistingBmadDir(projectDir: string): Promise<boolean>;
38
+ export declare function previewInstall(projectDir: string, platform?: Platform): Promise<PreviewInstallResult>;
39
+ export declare function previewUpgrade(projectDir: string, platform?: Platform): Promise<PreviewUpgradeResult>;