Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion Sources/CodexBarCore/PathEnvironment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,22 @@ public enum BinaryLocator {
loginPATH: loginPATH,
commandV: commandV,
aliasResolver: aliasResolver,
wellKnownPaths: self.claudeWellKnownPaths(home: home),
fileManager: fileManager,
home: home)
}

/// Well-known installation paths for the Claude CLI binary.
/// Covers the macOS Terminal installer (cmux.app), ~/.claude/bin, and Homebrew.
static func claudeWellKnownPaths(home: String) -> [String] {
[
"/Applications/cmux.app/Contents/Resources/bin/claude",
"\(home)/.claude/bin/claude",
"/usr/local/bin/claude",
"/opt/homebrew/bin/claude",
]
}

public static func resolveCodexBinary(
env: [String: String] = ProcessInfo.processInfo.environment,
loginPATH: [String]? = LoginShellPathCache.shared.current,
Expand Down Expand Up @@ -124,6 +136,7 @@ public enum BinaryLocator {
loginPATH: [String]?,
commandV: (String, String?, TimeInterval, FileManager) -> String?,
aliasResolver: (String, String?, TimeInterval, FileManager, String) -> String?,
wellKnownPaths: [String] = [],
fileManager: FileManager,
home: String) -> String?
{
Expand Down Expand Up @@ -164,7 +177,14 @@ public enum BinaryLocator {
return aliasHit
}

// 5) Minimal fallback
// 5) Well-known installation paths (e.g. cmux.app bundle, ~/.claude/bin)
// macOS apps launched from Finder may not inherit the user's shell PATH,
// so check common install locations that the shell-based lookups above may miss.
for candidate in wellKnownPaths where fileManager.isExecutableFile(atPath: candidate) {
return candidate
}

// 6) Minimal fallback
let fallback = ["/usr/bin", "/bin", "/usr/sbin", "/sbin"]
if let pathHit = self.find(name, in: fallback, fileManager: fileManager) {
return pathHit
Expand Down
50 changes: 50 additions & 0 deletions Tests/CodexBarTests/PathBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,56 @@ struct PathBuilderTests {
#expect(resolved == aliasPath)
}

@Test
func `resolves claude from well-known cmux path when shell lookups fail`() {
let cmuxPath = "/Applications/cmux.app/Contents/Resources/bin/claude"
let fm = MockFileManager(executables: [cmuxPath])
let commandV: (String, String?, TimeInterval, FileManager) -> String? = { _, _, _, _ in nil }
let aliasResolver: (String, String?, TimeInterval, FileManager, String) -> String? = { _, _, _, _, _ in nil }

let resolved = BinaryLocator.resolveClaudeBinary(
env: ["SHELL": "/bin/zsh"],
loginPATH: nil,
commandV: commandV,
aliasResolver: aliasResolver,
fileManager: fm,
home: "/Users/test")
#expect(resolved == cmuxPath)
}

@Test
func `resolves claude from well-known home dir path`() {
let homePath = "/Users/test/.claude/bin/claude"
let fm = MockFileManager(executables: [homePath])
let commandV: (String, String?, TimeInterval, FileManager) -> String? = { _, _, _, _ in nil }
let aliasResolver: (String, String?, TimeInterval, FileManager, String) -> String? = { _, _, _, _, _ in nil }

let resolved = BinaryLocator.resolveClaudeBinary(
env: ["SHELL": "/bin/zsh"],
loginPATH: nil,
commandV: commandV,
aliasResolver: aliasResolver,
fileManager: fm,
home: "/Users/test")
#expect(resolved == homePath)
}

@Test
func `prefers shell PATH over well-known paths`() {
let shellPath = "/custom/bin/claude"
let cmuxPath = "/Applications/cmux.app/Contents/Resources/bin/claude"
let fm = MockFileManager(executables: [shellPath, cmuxPath])
let commandV: (String, String?, TimeInterval, FileManager) -> String? = { _, _, _, _ in shellPath }

let resolved = BinaryLocator.resolveClaudeBinary(
env: ["SHELL": "/bin/zsh"],
loginPATH: nil,
commandV: commandV,
fileManager: fm,
home: "/Users/test")
#expect(resolved == shellPath)
}

@Test
func `skips alias when command V resolves`() {
let path = "/shell/bin/claude"
Expand Down