From 69e853d843297c025a1a9205e03c44535b3fb09c Mon Sep 17 00:00:00 2001 From: Emerson Lopes <24904209+emerson-d-lopes@users.noreply.github.com> Date: Wed, 22 Jul 2026 18:45:20 -0300 Subject: [PATCH] Don't pass duplicate /d flag in Windows cmd use-on-cd hook The generated cd.cmd always prepended /d, so a user-supplied /d was passed twice and `cd /d D:\` failed with "The syntax of the command is incorrect". Only add /d when the first argument isn't already /d. The two guarded single-line ifs are deliberate: parenthesized blocks break on paths containing ")" (e.g. "C:\Program Files (x86)"), goto labels misbehave with LF line endings (the file is stored with LF and embedded via include_bytes!), and setlocal would revert the directory change on endlocal. Fixes #1556 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01U5TQ1y3yBEDx73tYTHoUBj --- .changeset/witty-drives-turn.md | 10 ++++++++++ e2e/repros/issue-1556.test.ts | 22 ++++++++++++++++++++++ src/shell/windows_cmd/cd.cmd | 6 +++++- 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 .changeset/witty-drives-turn.md create mode 100644 e2e/repros/issue-1556.test.ts diff --git a/.changeset/witty-drives-turn.md b/.changeset/witty-drives-turn.md new file mode 100644 index 000000000..48e746168 --- /dev/null +++ b/.changeset/witty-drives-turn.md @@ -0,0 +1,10 @@ +--- +"fnm": patch +--- + +fix `cd /d ` failing in Windows Command Prompt when use-on-cd is enabled: the generated `cd.cmd` always prepended `/d`, so a user-supplied `/d` was passed twice and `cd` failed with "The syntax of the command is incorrect". Fixes #1556 + +```sh-session +> cd /d D:\ +The syntax of the command is incorrect. +``` diff --git a/e2e/repros/issue-1556.test.ts b/e2e/repros/issue-1556.test.ts new file mode 100644 index 000000000..edbf81532 --- /dev/null +++ b/e2e/repros/issue-1556.test.ts @@ -0,0 +1,22 @@ +import { writeFile, mkdir } from "node:fs/promises" +import { join } from "node:path" +import { script } from "../shellcode/script.js" +import { WinCmd } from "../shellcode/shells.js" +import testCwd from "../shellcode/test-cwd.js" +import testNodeVersion from "../shellcode/test-node-version.js" +import describe from "../describe.js" + +describe(WinCmd, () => { + test(`issue #1556: cd /d works with use-on-cd`, async () => { + const subdir = join(testCwd(), "subdir") + await mkdir(subdir, { recursive: true }) + await writeFile(join(subdir, ".node-version"), "v12.22.12") + await script(WinCmd) + .then(WinCmd.env({ useOnCd: true })) + .then(WinCmd.call("fnm", ["install", "v8.11.3"])) + .then(WinCmd.call("fnm", ["install", "v12.22.12"])) + .then(WinCmd.call("cd", ["/d", subdir])) + .then(testNodeVersion(WinCmd, "v12.22.12")) + .execute(WinCmd) + }) +}) diff --git a/src/shell/windows_cmd/cd.cmd b/src/shell/windows_cmd/cd.cmd index ef7a9fc73..0adc7b14b 100644 --- a/src/shell/windows_cmd/cd.cmd +++ b/src/shell/windows_cmd/cd.cmd @@ -1,5 +1,9 @@ @echo off -cd /d %* +rem Add /d only when the user didn't pass it themselves, so `cd /d D:\` works (#1556). +rem No if/else parentheses or setlocal here: %* may contain ")" (e.g. "C:\Program Files (x86)"), +rem and endlocal would revert the directory change. +if /i "%~1" == "/d" cd %* +if /i not "%~1" == "/d" cd /d %* if "%FNM_VERSION_FILE_STRATEGY%" == "recursive" ( fnm use --silent-if-unchanged ) else (