fix: auto-detect git-bash path on Windows (closes #23)

Add findGitBash() to platform.ts that checks env var, common install
paths, and derives path from `where git`. Used in claude-client.ts
to set CLAUDE_CODE_GIT_BASH_PATH when not already configured.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
7418andClaude Opus 4.6 committed 2026-02-08 20:54:03 +08:00
1 parent f2e7731766
commit 81125a233b
2 files changed
+71 -4

No files matched your search

+23 -4
View File
@@ -18,7 +18,7 @@ import type {
import type { ClaudeStreamOptions, SSEEvent, TokenUsage, MCPServerConfig, PermissionRequestEvent } from '@/types';
import { registerPendingPermission } from './permission-registry';
import { getSetting, getActiveProvider } from './db';
import { findClaudeBinary, getExpandedPath } from './platform';
import { findClaudeBinary, findGitBash, getExpandedPath } from './platform';
import os from 'os';
let cachedClaudePath: string | null | undefined;
@@ -157,6 +157,14 @@ export function streamClaude(options: ClaudeStreamOptions): ReadableStream<strin
// Ensure SDK subprocess has expanded PATH (consistent with Electron mode)
sdkEnv.PATH = getExpandedPath();
// On Windows, auto-detect Git Bash if not already configured
if (process.platform === 'win32' && !process.env.CLAUDE_CODE_GIT_BASH_PATH) {
const gitBashPath = findGitBash();
if (gitBashPath) {
sdkEnv.CLAUDE_CODE_GIT_BASH_PATH = gitBashPath;
}
}
// Try to get config from active provider first
const activeProvider = getActiveProvider();
@@ -192,11 +200,22 @@ export function streamClaude(options: ClaudeStreamOptions): ReadableStream<strin
// ignore malformed extra_env
}
} else {
// Fall back to legacy settings
// No active provider — check legacy DB settings first, then fall back to
// environment variables already present in process.env (copied into sdkEnv above).
// This allows users who set ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN / ANTHROPIC_BASE_URL
// in their shell environment to use them without configuring a provider in the UI.
const appToken = getSetting('anthropic_auth_token');
const appBaseUrl = getSetting('anthropic_base_url');
if (appToken) sdkEnv.ANTHROPIC_AUTH_TOKEN = appToken;
if (appBaseUrl) sdkEnv.ANTHROPIC_BASE_URL = appBaseUrl;
if (appToken) {
sdkEnv.ANTHROPIC_AUTH_TOKEN = appToken;
}
if (appBaseUrl) {
sdkEnv.ANTHROPIC_BASE_URL = appBaseUrl;
}
// If neither legacy settings nor env vars provide a key, log a warning
if (!appToken && !sdkEnv.ANTHROPIC_API_KEY && !sdkEnv.ANTHROPIC_AUTH_TOKEN) {
console.warn('[claude-client] No API key found: no active provider, no legacy settings, and no ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN in environment');
}
}
const queryOptions: Options = {
+48
View File
@@ -1,4 +1,5 @@
import { execFileSync, execFile } from 'child_process';
import fs from 'fs';
import { promisify } from 'util';
import os from 'os';
import path from 'path';
@@ -162,3 +163,50 @@ export async function getClaudeVersion(claudePath: string): Promise<string | nul
return null;
}
}
/**
* Find Git Bash (bash.exe) on Windows.
* Returns the path to bash.exe or null if not found.
*/
export function findGitBash(): string | null {
// 1. Check user-specified environment variable
const envPath = process.env.CLAUDE_CODE_GIT_BASH_PATH;
if (envPath && fs.existsSync(envPath)) {
return envPath;
}
// 2. Check common installation paths
const commonPaths = [
'C:\\Program Files\\Git\\bin\\bash.exe',
'C:\\Program Files (x86)\\Git\\bin\\bash.exe',
];
for (const p of commonPaths) {
if (fs.existsSync(p)) {
return p;
}
}
// 3. Try to locate git.exe via `where git` and derive bash.exe path
try {
const result = execFileSync('where', ['git'], {
timeout: 3000,
stdio: 'pipe',
shell: true,
});
const lines = result.toString().trim().split(/\r?\n/);
for (const line of lines) {
const gitExe = line.trim();
if (!gitExe) continue;
// git.exe is typically at <GitDir>\cmd\git.exe or <GitDir>\bin\git.exe
const gitDir = path.dirname(path.dirname(gitExe));
const bashPath = path.join(gitDir, 'bin', 'bash.exe');
if (fs.existsSync(bashPath)) {
return bashPath;
}
}
} catch {
// where git failed or timed out
}
return null;
}