From 212f08ad0311a7ea0d3426802b054388b4d8c083 Mon Sep 17 00:00:00 2001 From: Bo Date: Sun, 25 Jan 2026 21:01:24 -0500 Subject: [PATCH] fix(polecat): detect when shell is cd'd into worktree before nuke (#969) When nuking a polecat while your shell is cd'd into its worktree, the shell becomes completely broken (even `echo hello` fails). Add a check before deletion that detects if the current working directory is inside the polecat's worktree. If so, return an error with helpful instructions to cd elsewhere first. The check runs ALWAYS, even with --force, because this is about shell safety not data loss. You can't bypass it - just cd out first. Fixes #942 --- internal/polecat/manager.go | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/internal/polecat/manager.go b/internal/polecat/manager.go index daba8fb7..12c8111c 100644 --- a/internal/polecat/manager.go +++ b/internal/polecat/manager.go @@ -22,10 +22,11 @@ import ( // Common errors var ( - ErrPolecatExists = errors.New("polecat already exists") - ErrPolecatNotFound = errors.New("polecat not found") - ErrHasChanges = errors.New("polecat has uncommitted changes") + ErrPolecatExists = errors.New("polecat already exists") + ErrPolecatNotFound = errors.New("polecat not found") + ErrHasChanges = errors.New("polecat has uncommitted changes") ErrHasUncommittedWork = errors.New("polecat has uncommitted work") + ErrShellInWorktree = errors.New("shell working directory is inside polecat worktree") ) // UncommittedWorkError provides details about uncommitted work. @@ -528,6 +529,23 @@ func (m *Manager) RemoveWithOptions(name string, force, nuclear bool) error { } } + // Check if user's shell is cd'd into the worktree (prevents broken shell) + // This check ALWAYS runs, even with --force/nuclear, because deleting your + // shell's cwd breaks the shell completely (all commands fail with exit 1). + // See: https://github.com/steveyegge/gastown/issues/942 + cwd, cwdErr := os.Getwd() + if cwdErr == nil { + // Normalize paths for comparison + cwdAbs, _ := filepath.Abs(cwd) + cloneAbs, _ := filepath.Abs(clonePath) + polecatAbs, _ := filepath.Abs(polecatDir) + + if strings.HasPrefix(cwdAbs, cloneAbs) || strings.HasPrefix(cwdAbs, polecatAbs) { + return fmt.Errorf("%w: your shell is in %s\n\nPlease cd elsewhere first, then retry:\n cd ~/gt\n gt polecat nuke %s/%s --force", + ErrShellInWorktree, cwd, m.rig.Name, name) + } + } + // Get repo base to remove the worktree properly repoGit, err := m.repoBase() if err != nil {