Fix: Add cross-platform Claude path detection to feature branch

Applied v4.2.11 fix from main branch:
- Implemented explicit which/where command execution
- Unix/macOS: Uses 'which claude' command
- Windows: Uses 'where claude' command (CMD and PowerShell compatible)
- Fallback to CLAUDE_CODE_PATH environment variable
- Handles Windows multiple results (takes first match)

Technical changes:
- Added findClaudePath() helper using child_process.execSync
- Platform detection via process.platform === 'win32'
- Updated src/sdk/worker.ts with explicit path detection
- Updated src/services/worker-service.ts with explicit path detection
- Built worker-service.cjs reflects changes

This fixes SDK auto-detection failure that returned undefined path.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Alex Newman
2025-10-25 14:59:53 -04:00
parent 70ba785364
commit 7db39bb482
3 changed files with 94 additions and 27 deletions
File diff suppressed because one or more lines are too long
+34 -2
View File
@@ -13,6 +13,7 @@ declare global {
import net from 'net';
import { unlinkSync, existsSync } from 'fs';
import { execSync } from 'child_process';
import { query } from '@anthropic-ai/claude-agent-sdk';
import type { SDKUserMessage, SDKSystemMessage } from '@anthropic-ai/claude-agent-sdk';
import { SessionStore } from '../services/sqlite/SessionStore.js';
@@ -24,6 +25,35 @@ import type { SDKSession } from './prompts.js';
const MODEL = 'claude-sonnet-4-5';
const DISALLOWED_TOOLS = ['Glob', 'Grep', 'ListMcpResourcesTool', 'WebSearch'];
/**
* Find Claude Code executable path using which (Unix/Mac) or where (Windows)
*/
function findClaudePath(): string {
try {
// Try environment variable first
if (process.env.CLAUDE_CODE_PATH) {
return process.env.CLAUDE_CODE_PATH;
}
// Use which on Unix/Mac, where on Windows
const command = process.platform === 'win32' ? 'where claude' : 'which claude';
const result = execSync(command, { encoding: 'utf8' }).trim();
// On Windows, 'where' returns multiple lines if there are multiple matches, take the first
const path = result.split('\n')[0].trim();
if (!path) {
throw new Error('Claude executable not found in PATH');
}
console.error(`[SDK Worker] Found Claude executable: ${path}`);
return path;
} catch (error: any) {
console.error('[SDK Worker] Failed to find Claude executable:', error.message);
throw new Error('Claude Code executable not found. Please ensure claude is in your PATH or set CLAUDE_CODE_PATH environment variable.');
}
}
interface ObservationMessage {
type: 'observation';
tool_name: string;
@@ -282,14 +312,16 @@ class SDKWorker {
* Run SDK agent with streaming input mode
*/
private async runSDKAgent(): Promise<void> {
// Find Claude Code executable
const claudePath = findClaudePath();
console.error(`[SDK Worker DEBUG] Using Claude executable: ${claudePath}`);
const queryResult = query({
prompt: this.createMessageGenerator(),
options: {
model: MODEL,
disallowedTools: DISALLOWED_TOOLS,
abortController: this.abortController
abortController: this.abortController,
pathToClaudeCodeExecutable: claudePath
}
});
+35 -1
View File
@@ -12,11 +12,41 @@ import { parseObservations, parseSummary } from '../sdk/parser.js';
import type { SDKSession } from '../sdk/prompts.js';
import { logger } from '../utils/logger.js';
import { ensureAllDataDirs } from '../shared/paths.js';
import { execSync } from 'child_process';
const MODEL = process.env.CLAUDE_MEM_MODEL || 'claude-sonnet-4-5';
const DISALLOWED_TOOLS = ['Glob', 'Grep', 'ListMcpResourcesTool', 'WebSearch'];
const FIXED_PORT = parseInt(process.env.CLAUDE_MEM_WORKER_PORT || '37777', 10);
/**
* Find Claude Code executable path using which (Unix/Mac) or where (Windows)
*/
function findClaudePath(): string {
try {
// Try environment variable first
if (process.env.CLAUDE_CODE_PATH) {
return process.env.CLAUDE_CODE_PATH;
}
// Use which on Unix/Mac, where on Windows
const command = process.platform === 'win32' ? 'where claude' : 'which claude';
const result = execSync(command, { encoding: 'utf8' }).trim();
// On Windows, 'where' returns multiple lines if there are multiple matches, take the first
const path = result.split('\n')[0].trim();
if (!path) {
throw new Error('Claude executable not found in PATH');
}
logger.info('SYSTEM', `Found Claude executable: ${path}`);
return path;
} catch (error: any) {
logger.failure('SYSTEM', 'Failed to find Claude executable', {}, error);
throw new Error('Claude Code executable not found. Please ensure claude is in your PATH or set CLAUDE_CODE_PATH environment variable.');
}
}
interface ObservationMessage {
type: 'observation';
tool_name: string;
@@ -345,13 +375,17 @@ class WorkerService {
private async runSDKAgent(session: ActiveSession): Promise<void> {
logger.info('SDK', 'Agent starting', { sessionId: session.sessionDbId });
const claudePath = findClaudePath();
logger.info('SDK', `Using Claude executable: ${claudePath}`, { sessionId: session.sessionDbId });
try {
const queryResult = query({
prompt: this.createMessageGenerator(session),
options: {
model: MODEL,
disallowedTools: DISALLOWED_TOOLS,
abortController: session.abortController
abortController: session.abortController,
pathToClaudeCodeExecutable: claudePath
}
});