diff --git a/src/cli/commands/repo_init.ts b/src/cli/commands/repo_init.ts index 263a69d..10ba3c2 100644 --- a/src/cli/commands/repo_init.ts +++ b/src/cli/commands/repo_init.ts @@ -105,6 +105,7 @@ export const repoUpgradeCommand = new Command() upgradedAt: result.upgradedAt, skillsUpdated: result.skillsUpdated, settingsUpdated: result.settingsUpdated, + gitignoreCreated: result.gitignoreCreated, tool: result.tool, }; diff --git a/src/domain/repo/repo_service.ts b/src/domain/repo/repo_service.ts index e72afea..5682e1a 100644 --- a/src/domain/repo/repo_service.ts +++ b/src/domain/repo/repo_service.ts @@ -83,6 +83,7 @@ export interface RepoUpgradeResult { upgradedAt: string; skillsUpdated: string[]; settingsUpdated: boolean; + gitignoreCreated: boolean; tool: AiTool; } @@ -231,6 +232,12 @@ export class RepoService { settingsUpdated = await this.updateClaudeSettings(repoPath); } + // Create .gitignore if it doesn't exist + const gitignoreCreated = await this.createGitignoreIfNotExists( + repoPath, + tool, + ); + // createUpgradeMarker always sets upgradedAt, but TypeScript doesn't know this if (!updatedMarker.upgradedAt) { throw new Error( @@ -245,6 +252,7 @@ export class RepoService { upgradedAt: updatedMarker.upgradedAt, skillsUpdated, settingsUpdated, + gitignoreCreated, tool, }; } diff --git a/src/domain/repo/repo_service_test.ts b/src/domain/repo/repo_service_test.ts index 7a61339..7698236 100644 --- a/src/domain/repo/repo_service_test.ts +++ b/src/domain/repo/repo_service_test.ts @@ -722,6 +722,45 @@ Deno.test("RepoService.upgrade skips settings for non-claude tools", async () => }); }); +Deno.test("RepoService.upgrade creates .gitignore if missing", async () => { + await withTempDir(async (tempDir) => { + const service = new RepoService("0.1.0"); + const repoPath = RepoPath.create(tempDir); + + // Init (creates .gitignore), then delete it + await service.init(repoPath); + const gitignorePath = join(tempDir, ".gitignore"); + await Deno.remove(gitignorePath); + + // Upgrade should recreate .gitignore + const upgradeService = new RepoService("0.2.0"); + const result = await upgradeService.upgrade(repoPath); + + assertEquals(result.gitignoreCreated, true); + + const content = await Deno.readTextFile(gitignorePath); + assertStringIncludes(content, ".swamp/telemetry/"); + assertStringIncludes(content, ".swamp/secrets/keyfile"); + assertStringIncludes(content, ".claude/"); + }); +}); + +Deno.test("RepoService.upgrade does not overwrite existing .gitignore", async () => { + await withTempDir(async (tempDir) => { + const service = new RepoService("0.1.0"); + const repoPath = RepoPath.create(tempDir); + + // Init creates .gitignore + await service.init(repoPath); + + // Upgrade should leave .gitignore unchanged + const upgradeService = new RepoService("0.2.0"); + const result = await upgradeService.upgrade(repoPath); + + assertEquals(result.gitignoreCreated, false); + }); +}); + Deno.test("RepoService.init cursor instructions have MDC frontmatter", async () => { await withTempDir(async (tempDir) => { const service = new RepoService("0.1.0"); diff --git a/src/presentation/output/repo_output.ts b/src/presentation/output/repo_output.ts index eff67e0..c2f0879 100644 --- a/src/presentation/output/repo_output.ts +++ b/src/presentation/output/repo_output.ts @@ -46,6 +46,7 @@ export interface RepoUpgradeData { upgradedAt: string; skillsUpdated: string[]; settingsUpdated: boolean; + gitignoreCreated: boolean; tool: string; }