fix: add git.manage_gitignore preference to opt out of .gitignore changes (#950) (#952)

When set to false in .gsd/preferences.md, GSD will not modify .gitignore
at all — no baseline patterns added, no self-healing, no untracking.

Usage in preferences.md:
  git:
    manage_gitignore: false

Files changed:
- git-service.ts: Add manage_gitignore to GitPreferences interface
- gitignore.ts: Early return when manageGitignore is false
- auto.ts: Pass manage_gitignore preference to ensureGitignore
- preferences.ts: Parse and validate manage_gitignore in git config
This commit is contained in:
Tom Boucher 2026-03-17 17:27:34 -04:00 committed by GitHub
parent aa224b5944
commit 614012f38a
4 changed files with 19 additions and 4 deletions

View file

@ -688,9 +688,11 @@ export async function startAuto(
}
// Ensure .gitignore has baseline patterns
const commitDocs = loadEffectiveGSDPreferences()?.preferences?.git?.commit_docs;
ensureGitignore(base, { commitDocs });
untrackRuntimeFiles(base);
const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git;
const commitDocs = gitPrefs?.commit_docs;
const manageGitignore = gitPrefs?.manage_gitignore;
ensureGitignore(base, { commitDocs, manageGitignore });
if (manageGitignore !== false) untrackRuntimeFiles(base);
// Bootstrap .gsd/ if it doesn't exist
const gsdDir = join(base, ".gsd");

View file

@ -53,6 +53,12 @@ export interface GitPreferences {
* Default: true (planning docs are tracked in git).
*/
commit_docs?: boolean;
/** When false, GSD will not modify .gitignore at all no baseline patterns
* are added and no self-healing occurs. Use this if you manage your own
* .gitignore and don't want GSD touching it.
* Default: true (GSD ensures baseline patterns are present).
*/
manage_gitignore?: boolean;
/** Script to run after a worktree is created (#597).
* Receives SOURCE_DIR and WORKTREE_DIR as environment variables.
* Can be an absolute path or relative to the project root.

View file

@ -85,7 +85,10 @@ const BASELINE_PATTERNS = [
* .gitignore instead of individual runtime patterns, keeping all GSD
* artifacts local-only.
*/
export function ensureGitignore(basePath: string, options?: { commitDocs?: boolean }): boolean {
export function ensureGitignore(basePath: string, options?: { commitDocs?: boolean; manageGitignore?: boolean }): boolean {
// If manage_gitignore is explicitly false, do not touch .gitignore at all
if (options?.manageGitignore === false) return false;
const gitignorePath = join(basePath, ".gitignore");
const commitDocs = options?.commitDocs !== false; // default true

View file

@ -1329,6 +1329,10 @@ export function validatePreferences(preferences: GSDPreferences): {
if (typeof g.commit_docs === "boolean") git.commit_docs = g.commit_docs;
else errors.push("git.commit_docs must be a boolean");
}
if (g.manage_gitignore !== undefined) {
if (typeof g.manage_gitignore === "boolean") git.manage_gitignore = g.manage_gitignore;
else errors.push("git.manage_gitignore must be a boolean");
}
if (g.worktree_post_create !== undefined) {
if (typeof g.worktree_post_create === "string" && g.worktree_post_create.trim()) {
git.worktree_post_create = g.worktree_post_create.trim();