Conversation
…nored (swamp-club#250) The managed gitignore section wrote `.claude/` (the entire directory) for Claude repos, while every other tool only ignored its specific skills subdirectory (e.g. `.cursor/skills/`). This meant `.claude/skills/` was gitignored, so extension authors could not commit skill source files to their extension repo — making `swamp extension push` fail from any fresh CI/CD clone that didn't already have the skills installed locally. Replace the broad `.claude/` entry with four specific lines that mirror what the swamp repo's own .gitignore already does: .claude/worktrees/ .claude/settings.local.json .claude/scheduled_tasks.lock .claude/scheduled_tasks.json `.claude/skills/` is now untracked by the managed section, allowing extension authors to commit skill sources and have them packaged correctly by `swamp extension push`. The pull and remove behaviour (installing to `.claude/skills/<name>/`) is unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
Clean, well-motivated bug fix. The four specific .claude/ entries exactly mirror the swamp repo's own .gitignore (lines 17, 21–23), and the legacy migration pattern at repo_service.ts:1051 correctly retains .claude/ recognition for migrating old gitignore formats.
Blocking Issues
None.
Suggestions
None — test coverage is thorough (positive assertions for all four entries, negative assertion for the broad .claude/ pattern, tool-switch cleanup verification, and a dedicated Claude-specific test block). The change is minimal and well-scoped.
There was a problem hiding this comment.
Adversarial Review
Critical / High
None.
Medium
src/domain/repo/repo_service_test.ts:1072-1073— Tool-switch test only verifies 2 of 4 Claude entries are removed. The "replaces managed section on tool switch" test asserts that.claude/worktrees/and.claude/settings.local.jsonare absent after switching to cursor, but does not verify.claude/scheduled_tasks.lockor.claude/scheduled_tasks.jsonare also gone. In practice this is fine becausereplaceManagedSectionatomically replaces the entire block between markers, so partial removal is impossible — but the test doesn't fully prove the contract for the new four-entry set. Not blocking since the mechanism is sound.
Low
src/domain/repo/repo_service.ts:75— Future.claude/files created by Claude Code won't be gitignored. The old.claude/glob caught everything; the new four entries are specific. If Claude Code adds new ephemeral files (e.g..claude/todos.json,.claude/cache/), they'll appear as untracked until swamp adds them. This is the intended tradeoff (unblock.claude/skills/), and the entries match the repo's own.gitignore, so this is a conscious design decision rather than an oversight.
Verdict
PASS — Minimal, well-scoped change (1 production line, thorough test updates). The four specific ignore entries match the repo's own .gitignore. Legacy migration correctly recognizes the old .claude/ pattern (line 1051) to transition existing repos. The buildGitignoreSection / replaceManagedSection logic handles all code paths (create, update, migrate, tool-switch) and tests cover each.
Summary
.gitignoresection wrote.claude/(entire directory) for Claude repos, but all other tools only ignored their specific skills subdirectory (e.g..cursor/skills/,.kiro/skills/).claude/skills/to be gitignored, so extension authors could not commit skill source files — makingswamp extension pushfail from any fresh CI/CD clone that didn't already have skills installed locally.claude/entry with four specific lines that mirror what the swamp repo's own.gitignorealready does:.claude/worktrees/,.claude/settings.local.json,.claude/scheduled_tasks.lock,.claude/scheduled_tasks.json.claude/skills/is now untracked by the managed section. Extension authors can commit skill sources,swamp extension pushpackages them correctly, andswamp extension pullinstalls them to.claude/skills/<name>/where Claude Code loads them — unchanged.Test Plan
repo_service_test.ts: replacedassertStringIncludes(content, ".claude/")substring assertions with precise checks for each specific entry, addedassertEquals(content.split("\n").includes(".claude/"), false)to confirm the broad ignore is absent, added a dedicated Claude block verifying all four entries and that.claude/skills/is not blockedrepo_service_test.tsandintegration/cli_test.tsdeno check,deno lint,deno fmtall clean🤖 Generated with Claude Code