From 9200401e55567b6a55d6b20f1568bcc42756f36d Mon Sep 17 00:00:00 2001 From: Lex Christopherson Date: Thu, 12 Mar 2026 14:01:10 -0600 Subject: [PATCH] chore: auto-commit before switching to gsd/M002/S01 --- .claude/worktrees/agent-aa26d13d | 1 - .claude/worktrees/agent-aca4a27a | 1 - .claude/worktrees/agent-af52432f | 1 - src/resources/extensions/gsd/auto.ts | 5 +++-- .../extensions/gsd/docs/preferences-reference.md | 1 + src/resources/extensions/gsd/git-service.ts | 9 +++++++++ src/resources/extensions/gsd/gitignore.ts | 1 + src/resources/extensions/gsd/guided-flow.ts | 6 ++++-- src/resources/extensions/gsd/preferences.ts | 8 ++++++++ 9 files changed, 26 insertions(+), 7 deletions(-) delete mode 160000 .claude/worktrees/agent-aa26d13d delete mode 160000 .claude/worktrees/agent-aca4a27a delete mode 160000 .claude/worktrees/agent-af52432f diff --git a/.claude/worktrees/agent-aa26d13d b/.claude/worktrees/agent-aa26d13d deleted file mode 160000 index afcbdfa95..000000000 --- a/.claude/worktrees/agent-aa26d13d +++ /dev/null @@ -1 +0,0 @@ -Subproject commit afcbdfa956c83b3bb6f5476bb02437d9edeeda10 diff --git a/.claude/worktrees/agent-aca4a27a b/.claude/worktrees/agent-aca4a27a deleted file mode 160000 index 00d9aed6b..000000000 --- a/.claude/worktrees/agent-aca4a27a +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 00d9aed6b03298b902452da0a29d9a8ff7f7f7c7 diff --git a/.claude/worktrees/agent-af52432f b/.claude/worktrees/agent-af52432f deleted file mode 160000 index 5f7e04025..000000000 --- a/.claude/worktrees/agent-af52432f +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5f7e040254970b356e4e70d9d1a307fe07e2209a diff --git a/src/resources/extensions/gsd/auto.ts b/src/resources/extensions/gsd/auto.ts index 7027bafde..1ce41c346 100644 --- a/src/resources/extensions/gsd/auto.ts +++ b/src/resources/extensions/gsd/auto.ts @@ -56,7 +56,7 @@ import { } from "./metrics.js"; import { join } from "node:path"; import { readdirSync, readFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs"; -import { execSync } from "node:child_process"; +import { execSync, execFileSync } from "node:child_process"; import { autoCommitCurrentBranch, ensureSliceBranch, @@ -373,7 +373,8 @@ export async function startAuto( try { execSync("git rev-parse --git-dir", { cwd: base, stdio: "pipe" }); } catch { - execSync("git init", { cwd: base, stdio: "pipe" }); + const mainBranch = loadEffectiveGSDPreferences()?.preferences?.git?.main_branch || "main"; + execFileSync("git", ["init", "-b", mainBranch], { cwd: base, stdio: "pipe" }); } // Ensure .gitignore has baseline patterns diff --git a/src/resources/extensions/gsd/docs/preferences-reference.md b/src/resources/extensions/gsd/docs/preferences-reference.md index 3d382138a..5f31bea18 100644 --- a/src/resources/extensions/gsd/docs/preferences-reference.md +++ b/src/resources/extensions/gsd/docs/preferences-reference.md @@ -47,6 +47,7 @@ Full documentation for `~/.gsd/preferences.md` (global) and `.gsd/preferences.md - `snapshots`: boolean — create snapshot commits (WIP saves) during long-running tasks. Default: `false`. - `pre_merge_check`: boolean or `"auto"` — run pre-merge checks before merging a slice branch. `true` always runs, `false` never runs, `"auto"` runs when CI is detected. Default: `false`. - `commit_type`: string — override the conventional commit type prefix. Must be one of: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `perf`, `ci`, `build`, `style`. Default: inferred from diff content. + - `main_branch`: string — the primary branch name for new git repos (e.g., `"main"`, `"master"`, `"trunk"`). Also used by `getMainBranch()` as the preferred branch when auto-detection is ambiguous. Default: `"main"`. --- diff --git a/src/resources/extensions/gsd/git-service.ts b/src/resources/extensions/gsd/git-service.ts index e9ffac44b..a32522bcb 100644 --- a/src/resources/extensions/gsd/git-service.ts +++ b/src/resources/extensions/gsd/git-service.ts @@ -27,8 +27,11 @@ export interface GitPreferences { snapshots?: boolean; pre_merge_check?: boolean | string; commit_type?: string; + main_branch?: string; } +export const VALID_BRANCH_NAME = /^[a-zA-Z0-9_\-\/.]+$/; + export interface CommitOptions { message: string; allowEmpty?: boolean; @@ -192,6 +195,12 @@ export class GitServiceImpl { return this.git(["branch", "--show-current"]); } + // Explicit preference takes priority over auto-detection + const configured = this.prefs.main_branch; + if (configured && VALID_BRANCH_NAME.test(configured)) { + return configured; + } + const symbolic = this.git(["symbolic-ref", "refs/remotes/origin/HEAD"], { allowFailure: true }); if (symbolic) { const match = symbolic.match(/refs\/remotes\/origin\/(.+)$/); diff --git a/src/resources/extensions/gsd/gitignore.ts b/src/resources/extensions/gsd/gitignore.ts index bd4847c12..db9d40d91 100644 --- a/src/resources/extensions/gsd/gitignore.ts +++ b/src/resources/extensions/gsd/gitignore.ts @@ -145,6 +145,7 @@ See \`~/.gsd/agent/extensions/gsd/docs/preferences-reference.md\` for full field - \`models\`: Model preferences for specific task types - \`skill_discovery\`: Automatic skill detection preferences - \`auto_supervisor\`: Supervision and gating rules for autonomous modes +- \`git\`: Git preferences — \`main_branch\` (default branch name for new repos, e.g., "main", "master", "trunk"), \`auto_push\`, \`snapshots\`, etc. ## Examples diff --git a/src/resources/extensions/gsd/guided-flow.ts b/src/resources/extensions/gsd/guided-flow.ts index 775d14fd0..5e3224652 100644 --- a/src/resources/extensions/gsd/guided-flow.ts +++ b/src/resources/extensions/gsd/guided-flow.ts @@ -20,8 +20,9 @@ import { } from "./paths.js"; import { join } from "node:path"; import { readFileSync, existsSync, mkdirSync, readdirSync } from "node:fs"; -import { execSync } from "node:child_process"; +import { execSync, execFileSync } from "node:child_process"; import { ensureGitignore, ensurePreferences } from "./gitignore.js"; +import { loadEffectiveGSDPreferences } from "./preferences.js"; // ─── Auto-start after discuss ───────────────────────────────────────────────── @@ -444,7 +445,8 @@ export async function showSmartEntry( try { execSync("git rev-parse --git-dir", { cwd: basePath, stdio: "pipe" }); } catch { - execSync("git init", { cwd: basePath, stdio: "pipe" }); + const mainBranch = loadEffectiveGSDPreferences()?.preferences?.git?.main_branch || "main"; + execFileSync("git", ["init", "-b", mainBranch], { cwd: basePath, stdio: "pipe" }); } // ── Ensure .gitignore has baseline patterns ────────────────────────── diff --git a/src/resources/extensions/gsd/preferences.ts b/src/resources/extensions/gsd/preferences.ts index 273f1acd8..2857478c3 100644 --- a/src/resources/extensions/gsd/preferences.ts +++ b/src/resources/extensions/gsd/preferences.ts @@ -3,6 +3,7 @@ import { homedir } from "node:os"; import { isAbsolute, join } from "node:path"; import { getAgentDir } from "@mariozechner/pi-coding-agent"; import type { GitPreferences } from "./git-service.ts"; +import { VALID_BRANCH_NAME } from "./git-service.ts"; const GLOBAL_PREFERENCES_PATH = join(homedir(), ".gsd", "preferences.md"); const LEGACY_GLOBAL_PREFERENCES_PATH = join(homedir(), ".pi", "agent", "gsd-preferences.md"); @@ -639,6 +640,13 @@ function validatePreferences(preferences: GSDPreferences): { errors.push(`git.commit_type must be one of: feat, fix, refactor, docs, test, chore, perf, ci, build, style`); } } + if (g.main_branch !== undefined) { + if (typeof g.main_branch === "string" && g.main_branch.trim() !== "" && VALID_BRANCH_NAME.test(g.main_branch)) { + git.main_branch = g.main_branch; + } else { + errors.push("git.main_branch must be a valid branch name (alphanumeric, _, -, /, .)"); + } + } if (Object.keys(git).length > 0) { validated.git = git as GitPreferences;