diff --git a/changelog.d/claude-native-build.md b/changelog.d/claude-native-build.md new file mode 100644 index 0000000000..193991e613 --- /dev/null +++ b/changelog.d/claude-native-build.md @@ -0,0 +1,3 @@ +### Fixed +- A Claude Code pane on macOS that was started as `Claude` (capital C) shows the conversation + instead of terminal text. diff --git a/server/claude-store.test.ts b/server/claude-store.test.ts index 876bac9914..3918bbf7e9 100644 --- a/server/claude-store.test.ts +++ b/server/claude-store.test.ts @@ -8,6 +8,24 @@ import type { ProcessRow } from "./windows-processes.ts"; const NATIVE = process.platform === "linux" || process.platform === "darwin"; const SESSION = "0b8e6f0e-8d3f-4c1a-9a53-6c2b7a1d9e42"; +describe("isClaudeProcess", () => { + it("knows the native build, whose process is titled Claude", () => { + expect(isClaudeProcess({ name: "2.1.302", argv0: "Claude", argv: ["Claude"] })).toBe(true); + expect(isClaudeProcess({ name: "Claude Helper", argv: ["/Applications/Claude.app/Contents/MacOS/Claude Helper"] })).toBe(false); + // one field alone is not that shape + expect(isClaudeProcess({ name: "2.1.302", argv0: "Claude", argv: ["Other"] })).toBe(false); + expect(isClaudeProcess({ name: "2.1.302", argv0: "Other", argv: ["Claude"] })).toBe(false); + // the native build as herdr reports it run from its lowercase `claude` launcher: the executable match + expect(isClaudeProcess({ name: "2.1.306", argv0: "claude", argv: ["claude"] })).toBe(true); + }); + + it("knows the native build typed as Claude on a case-insensitive Mac disk", () => { + // recorded on macOS 27 (case-insensitive APFS): `Claude` finds the ~/.local/bin/claude link, so + // herdr's name is the versioned binary behind it and argv0/argv[0] keep the typed `Claude` + expect(isClaudeProcess({ name: "2.1.999", argv0: "Claude", argv: ["Claude", "42"] })).toBe(true); + }); +}); + describe("claudeProcessSession", () => { const roots: string[] = []; afterEach(() => { for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true }); }); diff --git a/server/claude-store.ts b/server/claude-store.ts index 395628cce7..3ee616c01b 100644 --- a/server/claude-store.ts +++ b/server/claude-store.ts @@ -32,7 +32,10 @@ function isClaudeExecutable(text = ""): boolean { /** A Claude Code process in herdr's process info. */ export function isClaudeProcess(entry: { name?: string; argv0?: string; argv?: readonly string[] }): boolean { // macOS keeps the executable name "node" for npm installs; the process title is argv0. - return isClaudeExecutable(entry.name) || isClaudeExecutable(entry.argv0) || isClaudeExecutable(entry.argv?.[0]); + if (isClaudeExecutable(entry.name) || isClaudeExecutable(entry.argv0) || isClaudeExecutable(entry.argv?.[0])) return true; + // the native build runs under its version as the name and titles itself "Claude": both its + // argv0 and its argv[0] say so, which one field alone does not prove + return /^\d+\.\d+\.\d+$/.test(entry.name ?? "") && entry.argv0 === "Claude" && entry.argv?.[0] === "Claude"; } /** Java's String.hashCode, which Claude Code uses for the suffix of a long name. */