Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/commands/upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
isInitialized,
copyBundledAssets,
mergeInstructionsFile,
updateGitignore,
previewUpgrade,
getBundledVersions,
} from "../installer.js";
Expand Down Expand Up @@ -63,6 +64,9 @@ async function runUpgrade(options: UpgradeOptions): Promise<void> {

const result = await copyBundledAssets(projectDir, platform);
await mergeInstructionsFile(projectDir, platform);
// Migrate .gitignore so installs created before a new managed entry was added
// (e.g. bmalph/state/) pick it up on upgrade instead of failing doctor.
await updateGitignore(projectDir);

// Update upstreamVersions in config to match bundled versions
const config = await readConfig(projectDir);
Expand Down
1 change: 1 addition & 0 deletions src/installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { generateManifests } from "./installer/bmad-assets.js";

export {
mergeInstructionsFile,
updateGitignore,
isInitialized,
previewInstall,
previewUpgrade,
Expand Down
7 changes: 6 additions & 1 deletion src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,12 @@ export const RALPH_STATUS_MAP = {
// =============================================================================

/** Entries bmalph adds to .gitignore during init and checks during doctor */
export const GITIGNORE_ENTRIES = [".ralph/logs/", "_bmad-output/", ".swarm/"] as const;
export const GITIGNORE_ENTRIES = [
".ralph/logs/",
"_bmad-output/",
".swarm/",
"bmalph/state/",
] as const;

// =============================================================================
// Swarm constants
Expand Down
13 changes: 12 additions & 1 deletion tests/commands/doctor-health-checks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ beforeEach(() => {

describe("checkGitignore", () => {
it("passes when .gitignore contains all required entries", async () => {
mockReadFile.mockResolvedValue("node_modules/\n.ralph/logs/\n_bmad-output/\n.swarm/\ndist/\n");
mockReadFile.mockResolvedValue(
"node_modules/\n.ralph/logs/\n_bmad-output/\n.swarm/\nbmalph/state/\ndist/\n"
);

const result = await checkGitignore("/projects/webapp");

Expand All @@ -84,6 +86,15 @@ describe("checkGitignore", () => {
expect(result.passed).toBe(false);
});

it("fails when bmalph/state/ (mutable runtime state) is missing from .gitignore", async () => {
mockReadFile.mockResolvedValue("node_modules/\n.ralph/logs/\n_bmad-output/\n.swarm/\n");

const result = await checkGitignore("/projects/webapp");

expect(result.passed).toBe(false);
expect(result.detail).toContain("bmalph/state/");
});

it("reports which entries are missing", async () => {
mockReadFile.mockResolvedValue("node_modules/\ndist/\n");

Expand Down
5 changes: 4 additions & 1 deletion tests/commands/doctor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,10 @@ describe("doctor command", { timeout: 15000 }, () => {
join(testDir, "CLAUDE.md"),
"# Project\n\n## BMAD-METHOD Integration\n\nSome content"
);
await writeFile(join(testDir, ".gitignore"), ".ralph/logs/\n_bmad-output/\n.swarm/\n");
await writeFile(
join(testDir, ".gitignore"),
".ralph/logs/\n_bmad-output/\n.swarm/\nbmalph/state/\n"
);
}

describe("Node version check", () => {
Expand Down
14 changes: 14 additions & 0 deletions tests/commands/upgrade.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ vi.mock("../../src/installer.js", () => ({
isInitialized: vi.fn(),
copyBundledAssets: vi.fn(),
mergeInstructionsFile: vi.fn(),
updateGitignore: vi.fn(),
previewUpgrade: vi.fn(),
getBundledVersions: vi.fn(),
}));
Expand Down Expand Up @@ -136,6 +137,19 @@ describe("upgrade command", () => {
expect(mergeInstructionsFile).toHaveBeenCalled();
});

it("migrates .gitignore so older installs pick up newly managed entries", async () => {
const { isInitialized, copyBundledAssets, mergeInstructionsFile, updateGitignore } =
await import("../../src/installer.js");
vi.mocked(isInitialized).mockResolvedValue(true);
vi.mocked(copyBundledAssets).mockResolvedValue({ updatedPaths: ["_bmad/"] });
vi.mocked(mergeInstructionsFile).mockResolvedValue(undefined);

const { upgradeCommand } = await import("../../src/commands/upgrade.js");
await upgradeCommand({ force: true, projectDir: process.cwd() });

expect(updateGitignore).toHaveBeenCalledWith(expect.any(String));
});

it("displays upgrading message", async () => {
const { isInitialized, copyBundledAssets, mergeInstructionsFile } =
await import("../../src/installer.js");
Expand Down