diff --git a/src/services/integrations/CursorHooksInstaller.ts b/src/services/integrations/CursorHooksInstaller.ts index 311c55f6..8df99cff 100644 --- a/src/services/integrations/CursorHooksInstaller.ts +++ b/src/services/integrations/CursorHooksInstaller.ts @@ -128,7 +128,7 @@ export async function updateCursorContextForProject(projectName: string, port: n /** * Find cursor-hooks directory * Searches in order: marketplace install, source repo - * Checks for both bash (common.sh) and PowerShell (common.ps1) scripts + * Checks for hooks.json (unified CLI mode) or legacy shell scripts */ export function findCursorHooksDir(): string | null { const possiblePaths = [ @@ -141,8 +141,10 @@ export function findCursorHooksDir(): string | null { ]; for (const p of possiblePaths) { - // Check for either bash or PowerShell common script - if (existsSync(path.join(p, 'common.sh')) || existsSync(path.join(p, 'common.ps1'))) { + // Check for hooks.json (unified CLI mode) or legacy shell scripts + if (existsSync(path.join(p, 'hooks.json')) || + existsSync(path.join(p, 'common.sh')) || + existsSync(path.join(p, 'common.ps1'))) { return p; } } @@ -193,6 +195,37 @@ export function findWorkerServicePath(): string | null { return null; } +/** + * Find the Bun executable path + * Required because worker-service.cjs uses bun:sqlite which is Bun-specific + * Searches common installation locations across platforms + */ +export function findBunPath(): string { + const possiblePaths = [ + // Standard user install location (most common) + path.join(homedir(), '.bun', 'bin', 'bun'), + // Global install locations + '/usr/local/bin/bun', + '/usr/bin/bun', + // Windows locations + ...(process.platform === 'win32' ? [ + path.join(homedir(), '.bun', 'bin', 'bun.exe'), + path.join(process.env.LOCALAPPDATA || '', 'bun', 'bun.exe'), + ] : []), + ]; + + for (const p of possiblePaths) { + if (p && existsSync(p)) { + return p; + } + } + + // Fallback to 'bun' and hope it's in PATH + // This allows the installation to proceed even if we can't find bun + // The user will get a clear error when the hook runs if bun isn't available + return 'bun'; +} + /** * Get the target directory for Cursor hooks based on install target */ @@ -312,15 +345,21 @@ export async function installCursorHooks(_sourceDir: string, target: CursorInsta // Generate hooks.json with unified CLI commands const hooksJsonPath = path.join(targetDir, 'hooks.json'); + // Find bun executable - required because worker-service.cjs uses bun:sqlite + const bunPath = findBunPath(); + const escapedBunPath = bunPath.replace(/\\/g, '\\\\'); + // Use the absolute path to worker-service.cjs // Escape backslashes for JSON on Windows const escapedWorkerPath = workerServicePath.replace(/\\/g, '\\\\'); - // Helper to create hook command using unified CLI + // Helper to create hook command using unified CLI with bun runtime const makeHookCommand = (command: string) => { - return `node "${escapedWorkerPath}" hook cursor ${command}`; + return `"${escapedBunPath}" "${escapedWorkerPath}" hook cursor ${command}`; }; + console.log(` Using Bun runtime: ${bunPath}`); + const hooksJson: CursorHooksJson = { version: 1, hooks: { @@ -356,7 +395,7 @@ export async function installCursorHooks(_sourceDir: string, target: CursorInsta Installation complete! Hooks installed to: ${targetDir}/hooks.json -Using unified CLI: node worker-service.cjs hook cursor +Using unified CLI: bun worker-service.cjs hook cursor Next steps: 1. Start claude-mem worker: claude-mem start @@ -532,7 +571,7 @@ export function checkCursorHooksStatus(): number { const firstCommand = hooksContent?.hooks?.beforeSubmitPrompt?.[0]?.command || ''; if (firstCommand.includes('worker-service.cjs') && firstCommand.includes('hook cursor')) { - console.log(` Mode: Unified CLI (node worker-service.cjs)`); + console.log(` Mode: Unified CLI (bun worker-service.cjs)`); } else { // Detect legacy shell scripts const bashScripts = ['session-init.sh', 'context-inject.sh', 'save-observation.sh'];