From 5eb02e9a1c091545aabce0621d6808336d4a7e31 Mon Sep 17 00:00:00 2001 From: Lex Christopherson Date: Wed, 11 Mar 2026 17:07:54 -0600 Subject: [PATCH] fix: auto-commit before branch switch and migrate legacy flat sessions ensureSliceBranch() now auto-commits dirty files before git checkout, preventing "would be overwritten" errors when doctor/STATE.md rebuild leaves uncommitted changes between slice dispatches. (closes #63) On startup, migrate any .jsonl session files from the flat ~/.gsd/sessions/ directory into the per-cwd subdirectory so /resume can find sessions created before per-directory scoping was added. (closes #64) Co-Authored-By: Claude Opus 4.6 --- src/cli.ts | 26 +++++++++++++++++++++++- src/resources/extensions/gsd/worktree.ts | 11 ++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/cli.ts b/src/cli.ts index 342cb9674..97ed54a79 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -8,7 +8,7 @@ import { InteractiveMode, runPrintMode, } from '@mariozechner/pi-coding-agent' -import { readFileSync } from 'node:fs' +import { existsSync, readdirSync, renameSync, readFileSync } from 'node:fs' import { join } from 'node:path' import { agentDir, sessionsDir, authFilePath } from './app-paths.js' import { initResources } from './resource-loader.js' @@ -180,6 +180,30 @@ if (isPrintMode) { const cwd = process.cwd() const safePath = `--${cwd.replace(/^[/\\]/, '').replace(/[/\\:]/g, '-')}--` const projectSessionsDir = join(sessionsDir, safePath) + +// Migrate legacy flat sessions: before per-directory scoping, all .jsonl session +// files lived directly in ~/.gsd/sessions/. Move them into the correct per-cwd +// subdirectory so /resume can find them. +if (existsSync(sessionsDir)) { + try { + const entries = readdirSync(sessionsDir) + const flatJsonl = entries.filter(f => f.endsWith('.jsonl')) + if (flatJsonl.length > 0) { + const { mkdirSync } = await import('node:fs') + mkdirSync(projectSessionsDir, { recursive: true }) + for (const file of flatJsonl) { + const src = join(sessionsDir, file) + const dst = join(projectSessionsDir, file) + if (!existsSync(dst)) { + renameSync(src, dst) + } + } + } + } catch { + // Non-fatal — don't block startup if migration fails + } +} + const sessionManager = SessionManager.create(cwd, projectSessionsDir) initResources(agentDir) diff --git a/src/resources/extensions/gsd/worktree.ts b/src/resources/extensions/gsd/worktree.ts index 36c153f7c..450ffc586 100644 --- a/src/resources/extensions/gsd/worktree.ts +++ b/src/resources/extensions/gsd/worktree.ts @@ -86,6 +86,17 @@ export function ensureSliceBranch(basePath: string, milestoneId: string, sliceId created = true; } + // Auto-commit dirty files before checkout to prevent "would be overwritten" errors. + // This handles cases where doctor, STATE.md rebuild, or agent work left uncommitted changes. + const status = runGit(basePath, ["status", "--short"]); + if (status.trim()) { + runGit(basePath, ["add", "-A"]); + const staged = runGit(basePath, ["diff", "--cached", "--stat"]); + if (staged.trim()) { + runGit(basePath, ["commit", "-m", `"chore: auto-commit before switching to ${branch}"`]); + } + } + runGit(basePath, ["checkout", branch]); return created; }