From 9efb251ae4a4f65fb7393fbb6a592590ba4aef10 Mon Sep 17 00:00:00 2001 From: dyoshikawa <34151621+dyoshikawa@users.noreply.github.com> Date: Sun, 12 Apr 2026 21:30:18 +0900 Subject: [PATCH] fix: keep .rulesync/.aiignore exception effective in gitignore --- src/cli/commands/gitignore-entries.ts | 3 ++- src/cli/commands/gitignore.test.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/cli/commands/gitignore-entries.ts b/src/cli/commands/gitignore-entries.ts index e4531636..e218d7d0 100644 --- a/src/cli/commands/gitignore-entries.ts +++ b/src/cli/commands/gitignore-entries.ts @@ -31,7 +31,6 @@ export const GITIGNORE_ENTRY_REGISTRY: ReadonlyArray = [ }, { target: "common", feature: "general", entry: ".rulesync/rules/*.local.md" }, { target: "common", feature: "general", entry: "rulesync.local.jsonc" }, - { target: "common", feature: "general", entry: "!.rulesync/.aiignore" }, // AGENTS.local.md is placed in common scope (not rovodev-only) so that // local rule files are always gitignored regardless of which targets are enabled. // This prevents accidental commits when a user disables the rovodev target. @@ -178,6 +177,8 @@ export const GITIGNORE_ENTRY_REGISTRY: ReadonlyArray = [ { target: "kiro", feature: "subagents", entry: "**/.kiro/agents/" }, { target: "kiro", feature: "mcp", entry: "**/.kiro/settings/mcp.json" }, { target: "kiro", feature: "ignore", entry: "**/.aiignore" }, + // Keep this after ignore entries like "**/.aiignore" so the exception remains effective. + { target: "common", feature: "general", entry: "!.rulesync/.aiignore" }, // OpenCode { target: "opencode", feature: "commands", entry: "**/.opencode/command/" }, diff --git a/src/cli/commands/gitignore.test.ts b/src/cli/commands/gitignore.test.ts index eb35744c..ba410dc0 100644 --- a/src/cli/commands/gitignore.test.ts +++ b/src/cli/commands/gitignore.test.ts @@ -92,6 +92,18 @@ describe("gitignoreCommand", () => { expect(content).not.toContain("**/.agent/skills/"); }); + it("should place .rulesync/.aiignore exception after .aiignore ignore entries", async () => { + vi.mocked(fileExists).mockResolvedValue(false); + + await gitignoreCommand(mockLogger); + + const writeCall = vi.mocked(writeFileContent).mock.calls[0]; + expect(writeCall).toBeDefined(); + const content = writeCall![1]; + + expect(content.indexOf("**/.aiignore")).toBeLessThan(content.indexOf("!.rulesync/.aiignore")); + }); + it("should format content properly with newline at end", async () => { vi.mocked(fileExists).mockResolvedValue(false);