fix: add Windows support for Claude CLI detection

Previously, the Claude CLI detection failed on Windows due to:

1. Shell command incompatibility
   - Used 'which claude || where claude 2>/dev/null' which fails on Windows
   - 'which' doesn't exist on Windows
   - '2>/dev/null' is Unix syntax (Windows uses '2>nul')
   - Now uses platform-specific commands: 'where' on Windows, 'which' on Unix

2. Missing Windows fallback paths
   - Only checked Unix paths like ~/.local/bin/claude
   - Added Windows-specific paths:
     * %USERPROFILE%\.local\bin\claude.exe
     * %APPDATA%\npm\claude.cmd
     * %USERPROFILE%\.npm-global\bin\claude.cmd

3. Credentials file detection
   - Only checked for 'credentials.json'
   - Claude CLI on Windows uses '.credentials.json' (hidden file)
   - Now checks both '.credentials.json' and 'credentials.json'

Additional improvements:
- Handle 'where' command returning multiple paths (takes first match)
- Maintains full backward compatibility with Linux and macOS
This commit is contained in:
Leon van Zyl
2025-12-16 17:16:09 +02:00
parent 2ee4ec65b4
commit 81444d5603

View File

@@ -17,12 +17,15 @@ export async function getClaudeStatus() {
let cliPath = ""; let cliPath = "";
let method = "none"; let method = "none";
// Try to find Claude CLI const isWindows = process.platform === "win32";
// Try to find Claude CLI using platform-specific command
try { try {
const { stdout } = await execAsync( // Use 'where' on Windows, 'which' on Unix-like systems
"which claude || where claude 2>/dev/null" const findCommand = isWindows ? "where claude" : "which claude";
); const { stdout } = await execAsync(findCommand);
cliPath = stdout.trim(); // 'where' on Windows can return multiple paths - take the first one
cliPath = stdout.trim().split(/\r?\n/)[0];
installed = true; installed = true;
method = "path"; method = "path";
@@ -34,8 +37,18 @@ export async function getClaudeStatus() {
// Version command might not be available // Version command might not be available
} }
} catch { } catch {
// Not in PATH, try common locations // Not in PATH, try common locations based on platform
const commonPaths = [ const commonPaths = isWindows
? [
// Windows-specific paths
path.join(os.homedir(), ".local", "bin", "claude.exe"),
path.join(os.homedir(), "AppData", "Roaming", "npm", "claude.cmd"),
path.join(os.homedir(), "AppData", "Roaming", "npm", "claude"),
path.join(os.homedir(), ".npm-global", "bin", "claude.cmd"),
path.join(os.homedir(), ".npm-global", "bin", "claude"),
]
: [
// Unix (Linux/macOS) paths
path.join(os.homedir(), ".local", "bin", "claude"), path.join(os.homedir(), ".local", "bin", "claude"),
path.join(os.homedir(), ".claude", "local", "claude"), path.join(os.homedir(), ".claude", "local", "claude"),
"/usr/local/bin/claude", "/usr/local/bin/claude",
@@ -124,8 +137,14 @@ export async function getClaudeStatus() {
// Settings file doesn't exist // Settings file doesn't exist
} }
// Check for credentials file (OAuth tokens from claude login) - legacy/alternative auth // Check for credentials file (OAuth tokens from claude login)
const credentialsPath = path.join(claudeDir, "credentials.json"); // Note: Claude CLI may use ".credentials.json" (hidden) or "credentials.json" depending on version/platform
const credentialsPaths = [
path.join(claudeDir, ".credentials.json"),
path.join(claudeDir, "credentials.json"),
];
for (const credentialsPath of credentialsPaths) {
try { try {
const credentialsContent = await fs.readFile(credentialsPath, "utf-8"); const credentialsContent = await fs.readFile(credentialsPath, "utf-8");
const credentials = JSON.parse(credentialsContent); const credentials = JSON.parse(credentialsContent);
@@ -142,8 +161,10 @@ export async function getClaudeStatus() {
auth.authenticated = true; auth.authenticated = true;
auth.method = "api_key"; // Stored API key in credentials file auth.method = "api_key"; // Stored API key in credentials file
} }
break; // Found and processed credentials file
} catch { } catch {
// No credentials file or invalid format // No credentials file at this path or invalid format
}
} }
// Environment variables override stored credentials (higher priority) // Environment variables override stored credentials (higher priority)