Skip to content

Commit 607a342

Browse files
committed
fix: Resolve claude binary path, accounting for command aliasing
We resolve the path by running 'which claude' using the user's shell, which accounts for shell aliases. Fixes #96
1 parent d69d663 commit 607a342

File tree

1 file changed

+61
-3
lines changed

1 file changed

+61
-3
lines changed

config/config.go

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@ import (
55
"encoding/json"
66
"fmt"
77
"os"
8+
"os/exec"
89
"os/user"
910
"path/filepath"
11+
"regexp"
1012
"strings"
1113
)
1214

13-
const ConfigFileName = "config.json"
15+
const (
16+
ConfigFileName = "config.json"
17+
defaultProgram = "claude"
18+
)
1419

1520
// GetConfigDir returns the path to the application's configuration directory
1621
func GetConfigDir() (string, error) {
@@ -35,8 +40,14 @@ type Config struct {
3540

3641
// DefaultConfig returns the default configuration
3742
func DefaultConfig() *Config {
43+
program, err := GetClaudeCommand()
44+
if err != nil {
45+
log.ErrorLog.Printf("failed to get claude command: %v", err)
46+
program = defaultProgram
47+
}
48+
3849
return &Config{
39-
DefaultProgram: "claude",
50+
DefaultProgram: program,
4051
AutoYes: false,
4152
DaemonPollInterval: 1000,
4253
BranchPrefix: func() string {
@@ -50,7 +61,54 @@ func DefaultConfig() *Config {
5061
}
5162
}
5263

53-
// LoadConfig loads the configuration from disk. If it cannot be done, we return the default configuration.
64+
// GetClaudeCommand attempts to find the "claude" command in the user's shell
65+
// It checks in the following order:
66+
// 1. Shell alias resolution: using "which" command
67+
// 2. PATH lookup
68+
//
69+
// If both fail, it returns an error.
70+
func GetClaudeCommand() (string, error) {
71+
shell := os.Getenv("SHELL")
72+
if shell == "" {
73+
shell = "/bin/bash" // Default to bash if SHELL is not set
74+
}
75+
76+
// Force the shell to load the user's profile and then run the command
77+
// For zsh, source .zshrc; for bash, source .bashrc
78+
var shellCmd string
79+
if strings.Contains(shell, "zsh") {
80+
shellCmd = "source ~/.zshrc 2>/dev/null || true; which claude"
81+
} else if strings.Contains(shell, "bash") {
82+
shellCmd = "source ~/.bashrc 2>/dev/null || true; which claude"
83+
} else {
84+
shellCmd = "which claude"
85+
}
86+
87+
cmd := exec.Command(shell, "-c", shellCmd)
88+
output, err := cmd.Output()
89+
if err == nil && len(output) > 0 {
90+
path := strings.TrimSpace(string(output))
91+
if path != "" {
92+
// Check if the output is an alias definition and extract the actual path
93+
// Handle formats like "claude: aliased to /path/to/claude" or other shell-specific formats
94+
aliasRegex := regexp.MustCompile(`(?:aliased to|->|=)\s*([^\s]+)`)
95+
matches := aliasRegex.FindStringSubmatch(path)
96+
if len(matches) > 1 {
97+
path = matches[1]
98+
}
99+
return path, nil
100+
}
101+
}
102+
103+
// Otherwise, try to find in PATH directly
104+
claudePath, err := exec.LookPath("claude")
105+
if err == nil {
106+
return claudePath, nil
107+
}
108+
109+
return "", fmt.Errorf("claude command not found in aliases or PATH")
110+
}
111+
54112
func LoadConfig() *Config {
55113
configDir, err := GetConfigDir()
56114
if err != nil {

0 commit comments

Comments
 (0)