Files
claude-mem/src/npx-cli/commands/ide-detection.ts
T
Alex Newman 76a27296f0 fix: wire up Cursor integration in installer (#1605)
* fix: wire up Cursor integration in installer — was incorrectly marked "coming soon"

CursorHooksInstaller.ts was fully built but never connected to the
installer. Set supported: true in IDE detection and call installCursorHooks
in the setup flow, matching the pattern used by other integrations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: wire up Cursor MCP configuration during install

PR review flagged that the hint says "hooks + MCP integration" but
configureCursorMcp() was never called during install. Now invoked
after hooks install with graceful fallback if MCP setup fails.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 22:44:49 -07:00

174 lines
4.8 KiB
TypeScript

/**
* IDE Auto-Detection
*
* Detects which AI coding IDEs / tools are installed on the system by
* probing known config directories and checking for binaries in PATH.
*
* Pure Node.js — no Bun APIs used.
*/
import { execSync } from 'child_process';
import { existsSync, readdirSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';
import { IS_WINDOWS } from '../utils/paths.js';
// ---------------------------------------------------------------------------
// IDE type and metadata
// ---------------------------------------------------------------------------
export interface IDEInfo {
/** Machine-readable identifier. */
id: string;
/** Human-readable label for display in prompts. */
label: string;
/** Whether the IDE was detected on this system. */
detected: boolean;
/** Whether claude-mem has implemented setup for this IDE. */
supported: boolean;
/** Short hint text shown in the multi-select. */
hint?: string;
}
// ---------------------------------------------------------------------------
// PATH helper
// ---------------------------------------------------------------------------
function isCommandInPath(command: string): boolean {
try {
const whichCommand = IS_WINDOWS ? 'where' : 'which';
execSync(`${whichCommand} ${command}`, { stdio: 'pipe' });
return true;
} catch {
return false;
}
}
// ---------------------------------------------------------------------------
// VS Code extension directory scanner
// ---------------------------------------------------------------------------
function hasVscodeExtension(extensionNameFragment: string): boolean {
const extensionsDirectory = join(homedir(), '.vscode', 'extensions');
if (!existsSync(extensionsDirectory)) return false;
try {
const entries = readdirSync(extensionsDirectory);
return entries.some((entry) => entry.toLowerCase().includes(extensionNameFragment.toLowerCase()));
} catch {
return false;
}
}
// ---------------------------------------------------------------------------
// Detection map
// ---------------------------------------------------------------------------
/**
* Detect all known IDEs and return an array of `IDEInfo` objects.
* Each entry indicates whether the IDE was found and whether claude-mem
* currently supports setting it up.
*/
export function detectInstalledIDEs(): IDEInfo[] {
const home = homedir();
return [
{
id: 'claude-code',
label: 'Claude Code',
detected: existsSync(join(home, '.claude')),
supported: true,
hint: 'recommended',
},
{
id: 'gemini-cli',
label: 'Gemini CLI',
detected: existsSync(join(home, '.gemini')),
supported: true,
},
{
id: 'opencode',
label: 'OpenCode',
detected:
existsSync(join(home, '.config', 'opencode')) || isCommandInPath('opencode'),
supported: true,
hint: 'plugin-based integration',
},
{
id: 'openclaw',
label: 'OpenClaw',
detected: existsSync(join(home, '.openclaw')),
supported: true,
hint: 'plugin-based integration',
},
{
id: 'windsurf',
label: 'Windsurf',
detected: existsSync(join(home, '.codeium', 'windsurf')),
supported: true,
},
{
id: 'codex-cli',
label: 'Codex CLI',
detected: existsSync(join(home, '.codex')),
supported: true,
hint: 'transcript-based integration',
},
{
id: 'cursor',
label: 'Cursor',
detected: existsSync(join(home, '.cursor')),
supported: true,
hint: 'hooks + MCP integration',
},
{
id: 'copilot-cli',
label: 'Copilot CLI',
detected: isCommandInPath('copilot'),
supported: true,
hint: 'MCP-based integration',
},
{
id: 'antigravity',
label: 'Antigravity',
detected: existsSync(join(home, '.gemini', 'antigravity')),
supported: true,
hint: 'MCP-based integration',
},
{
id: 'goose',
label: 'Goose',
detected:
existsSync(join(home, '.config', 'goose')) || isCommandInPath('goose'),
supported: true,
hint: 'MCP-based integration',
},
{
id: 'crush',
label: 'Crush',
detected: isCommandInPath('crush'),
supported: true,
hint: 'MCP-based integration',
},
{
id: 'roo-code',
label: 'Roo Code',
detected: hasVscodeExtension('roo-code'),
supported: true,
hint: 'MCP-based integration',
},
{
id: 'warp',
label: 'Warp',
detected: existsSync(join(home, '.warp')) || isCommandInPath('warp'),
supported: true,
hint: 'MCP-based integration',
},
];
}
/**
* Return only the IDEs that were detected on this system.
*/
export function getDetectedIDEs(): IDEInfo[] {
return detectInstalledIDEs().filter((ide) => ide.detected);
}