Summary
Surfaced by review of #21 ([MINOR] in src/writers/claude.ts:182).
The two writer modules expose inconsistent contracts for managing .gitignore:
A future consumer calling installCursor directly outside of cli.ts would miss the gitignore step.
Why #21 kept the inconsistency
The self-contained approach in installClaudePlugin makes its gitignore contribution unit-testable in test/writers/claude.test.ts without spinning up the interactive CLI. Moving it back out to cli.ts would lose that test coverage. The principled fix is the opposite direction — move cursor's gitignore call into installCursor to match — but that touches installCursor and its tests, which is out of scope for #21's project-scope-enablement work.
Proposed fix
Move ensureGitignore(['.cursor/mcp.json']) into installCursor. Remove the corresponding push from cli.ts main(). Update test/writers/cursor.test.ts with an equivalent assertion to the one in claude.test.ts (adds .cursor/mcp.json to project .gitignore).
After this, cli.ts no longer needs the gitignoreEntries: string[] accumulator at all — each writer owns its own gitignore contributions.
Acceptance
installCursor writes its .gitignore entry without help from the caller.
cli.ts main() no longer references gitignoreEntries or calls ensureGitignore directly.
- Direct programmatic use of
installCursor produces the same on-disk state as today via npx @aictrl/plugin.
- No behaviour change for
installClaudePlugin.
Summary
Surfaced by review of #21 ([MINOR] in
src/writers/claude.ts:182).The two writer modules expose inconsistent contracts for managing
.gitignore:installClaudePlugin(since fix: scope plugin enablement per project so multi-org developers get only their current repo's MCP + skills (#20) #21) callsensureGitignoreinternally to add.claude/settings.local.json.installCursordelegates.cursor/mcp.jsonto its caller (main()incli.ts, which builds agitignoreEntriesarray and applies them at the end).A future consumer calling
installCursordirectly outside ofcli.tswould miss the gitignore step.Why #21 kept the inconsistency
The self-contained approach in
installClaudePluginmakes its gitignore contribution unit-testable intest/writers/claude.test.tswithout spinning up the interactive CLI. Moving it back out tocli.tswould lose that test coverage. The principled fix is the opposite direction — move cursor's gitignore call intoinstallCursorto match — but that touchesinstallCursorand its tests, which is out of scope for #21's project-scope-enablement work.Proposed fix
Move
ensureGitignore(['.cursor/mcp.json'])intoinstallCursor. Remove the corresponding push fromcli.ts main(). Updatetest/writers/cursor.test.tswith an equivalent assertion to the one inclaude.test.ts(adds .cursor/mcp.json to project .gitignore).After this,
cli.tsno longer needs thegitignoreEntries: string[]accumulator at all — each writer owns its own gitignore contributions.Acceptance
installCursorwrites its.gitignoreentry without help from the caller.cli.tsmain()no longer referencesgitignoreEntriesor callsensureGitignoredirectly.installCursorproduces the same on-disk state as today vianpx @aictrl/plugin.installClaudePlugin.