diff --git a/.changeset/doctor-shell-detection.md b/.changeset/doctor-shell-detection.md new file mode 100644 index 00000000..456bd881 --- /dev/null +++ b/.changeset/doctor-shell-detection.md @@ -0,0 +1,5 @@ +--- +"clerk": patch +--- + +Fix shell detection for fish users whose login shell is zsh. `clerk doctor` now correctly identifies fish via `FISH_VERSION`, and `clerk update` no longer shows an irrelevant `hash -r` hint. diff --git a/packages/cli-core/src/commands/doctor/checks.ts b/packages/cli-core/src/commands/doctor/checks.ts index 5cf086ef..b8bf9afa 100644 --- a/packages/cli-core/src/commands/doctor/checks.ts +++ b/packages/cli-core/src/commands/doctor/checks.ts @@ -362,8 +362,11 @@ export async function checkCliVersion(): Promise { type DetectedShell = "bash" | "zsh" | "fish"; function detectShell(): DetectedShell | null { + // FISH_VERSION is set by fish itself, so it's a stronger signal than $SHELL — which only reflects + // the login shell and won't follow an interactive `chsh`-less switch into fish. + if (process.env.FISH_VERSION) return "fish"; const name = process.env.SHELL?.split("/").pop(); - if (name === "zsh" || name === "bash" || name === "fish") return name; + if (name === "bash" || name === "zsh" || name === "fish") return name; return null; } diff --git a/packages/cli-core/src/commands/doctor/doctor.test.ts b/packages/cli-core/src/commands/doctor/doctor.test.ts index 09b6860d..bad4c48b 100644 --- a/packages/cli-core/src/commands/doctor/doctor.test.ts +++ b/packages/cli-core/src/commands/doctor/doctor.test.ts @@ -156,6 +156,7 @@ beforeEach(async () => { tempDir = await mkdtemp(join(tmpdir(), "clerk-doctor-test-")); process.cwd = () => tempDir; process.env = { ...originalEnv }; + delete process.env.FISH_VERSION; process.env.CLERK_PLATFORM_API_KEY = "test_key"; mockUserInfo = null; @@ -739,6 +740,43 @@ describe("checkShellCompletion", () => { }); }); + test("detects fish via FISH_VERSION when SHELL is unset", async () => { + process.env.SHELL = ""; + process.env.FISH_VERSION = "3.7.0"; + process.env.HOME = tempDir; + const result = await checkShellCompletion(); + expectCheck(result, { + name: "Shell completion", + status: "warn", + message: "fish", + remedy: "clerk completion fish", + }); + }); + + test("prefers FISH_VERSION over SHELL when both are set", async () => { + process.env.SHELL = "/bin/zsh"; + process.env.FISH_VERSION = "3.7.0"; + process.env.HOME = tempDir; + const result = await checkShellCompletion(); + expectCheck(result, { + name: "Shell completion", + status: "warn", + message: "fish", + remedy: "clerk completion fish", + }); + }); + + test("skips when SHELL points to an unrecognized shell", async () => { + process.env.SHELL = "/bin/csh"; + process.env.HOME = tempDir; + const result = await checkShellCompletion(); + expectCheck(result, { + name: "Shell completion", + status: "pass", + message: "skipped", + }); + }); + test("no fix action attached (completions are not auto-fixable)", async () => { process.env.SHELL = "/bin/zsh"; process.env.HOME = tempDir; diff --git a/packages/cli-core/src/commands/update/index.ts b/packages/cli-core/src/commands/update/index.ts index 99f6222e..8876fcb6 100644 --- a/packages/cli-core/src/commands/update/index.ts +++ b/packages/cli-core/src/commands/update/index.ts @@ -207,7 +207,7 @@ function hashHint(): string | null { // but return early to be explicit. if (process.platform === "win32") return null; const shell = (process.env.SHELL ?? "").toLowerCase(); - if (shell.endsWith("/fish")) return null; // auto-rehashes + if (shell.endsWith("/fish") || process.env.FISH_VERSION) return null; // auto-rehashes // pwsh can run on Linux/macOS via $SHELL=/usr/bin/pwsh; no command-hash cache. if (shell.endsWith("/pwsh")) return null; if (shell.endsWith("/tcsh") || shell.endsWith("/csh")) {