From 79e49aa413bd23971d308ee769687d64be2c6c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=82CHES?= Date: Sat, 21 Mar 2026 20:52:30 -0600 Subject: [PATCH] fix(worktree): resolve 8.3 short paths and use shell mode for .bat hooks on Windows (#1956) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve Windows 8.3 short paths (RUNNER~1 → runneradmin) via realpathSync.native() and use shell mode for .bat/.cmd files in worktree post-create hooks. Fixes pre-existing windows-portability CI failure. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/resources/extensions/gsd/auto-worktree.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/resources/extensions/gsd/auto-worktree.ts b/src/resources/extensions/gsd/auto-worktree.ts index 6e3d2815d..0d928fb12 100644 --- a/src/resources/extensions/gsd/auto-worktree.ts +++ b/src/resources/extensions/gsd/auto-worktree.ts @@ -471,13 +471,21 @@ export function runWorktreePostCreateHook( } if (!hookPath) return null; - // Resolve relative paths against the source project root - const resolved = isAbsolute(hookPath) ? hookPath : join(sourceDir, hookPath); + // Resolve relative paths against the source project root. + // On Windows, convert 8.3 short paths (e.g. RUNNER~1) to long paths + // so execFileSync can locate the file correctly. + let resolved = isAbsolute(hookPath) ? hookPath : join(sourceDir, hookPath); if (!existsSync(resolved)) { return `Worktree post-create hook not found: ${resolved}`; } + if (process.platform === "win32") { + try { resolved = realpathSync.native(resolved); } catch { /* keep original */ } + } try { + // .bat/.cmd files on Windows require shell mode — execFileSync cannot + // spawn them directly (EINVAL). + const needsShell = process.platform === "win32" && /\.(bat|cmd)$/i.test(resolved); execFileSync(resolved, [], { cwd: worktreeDir, env: { @@ -488,6 +496,7 @@ export function runWorktreePostCreateHook( stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8", timeout: 30_000, // 30 second timeout + shell: needsShell, }); return null; } catch (err) {