UNPKG

claude-gemini

Version:

Global CLI tool for Claude-Gemini integration across projects

289 lines (287 loc) 10.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.findGeminiPath = findGeminiPath; exports.convertPaths = convertPaths; exports.validatePaths = validatePaths; exports.findCode2PromptPath = findCode2PromptPath; exports.runCode2Prompt = runCode2Prompt; exports.extractPathsFromQuery = extractPathsFromQuery; exports.createTempPromptFile = createTempPromptFile; exports.cleanupTempFile = cleanupTempFile; exports.formatForClaude = formatForClaude; const which_1 = __importDefault(require("which")); const path_1 = __importDefault(require("path")); const child_process_1 = require("child_process"); const fs_1 = __importDefault(require("fs")); const crypto_1 = __importDefault(require("crypto")); const os_1 = __importDefault(require("os")); async function findGeminiPath() { try { // Try to find gemini in PATH return await (0, which_1.default)('gemini'); } catch { // Try common locations const commonPaths = [ '/usr/local/bin/gemini', path_1.default.join(process.env.HOME || '', '.local/bin/gemini'), path_1.default.join(process.env.HOME || '', 'bin/gemini'), ]; // Add NVM paths try { const nvmPath = (0, child_process_1.execSync)('echo $NVM_BIN', { encoding: 'utf-8' }).trim(); if (nvmPath) { commonPaths.unshift(path_1.default.join(nvmPath, 'gemini')); } } catch { } for (const p of commonPaths) { try { (0, child_process_1.execSync)(`test -x "${p}"`, { stdio: 'ignore' }); return p; } catch { } } return null; } } function convertPaths(query, baseDir) { // Handle edge cases if (!query || typeof query !== 'string') { return ''; } // Convert @./ to absolute path query = query.replace(/@\.\//g, `@${baseDir}/`); // Convert @dir/ to absolute path - but check if it's a valid directory path query = query.replace(/@([^/@ "]+)\//g, (match, dir) => { // Skip if it looks like it might be part of the prompt text if (dir.includes(' ') || dir.length > 100) { return match; } return `@${path_1.default.join(baseDir, dir)}/`; }); // Convert @file to absolute path - with better file extension detection query = query.replace(/@([^/@ "]+\.[a-zA-Z0-9]{1,10})(?=\s|$|")/g, (match, file) => { // Skip if it looks like it might be part of the prompt text if (file.includes(' ') || file.length > 100) { return match; } return `@${path_1.default.join(baseDir, file)}`; }); return query; } function validatePaths(query) { const warnings = []; const validPaths = []; // Extract all @path references const pathMatches = query.match(/@[^\s"]+/g) || []; for (const match of pathMatches) { const pathStr = match.substring(1); // Remove @ // Skip if it's just @ followed by text (might be part of the prompt) if (!pathStr.includes('/') && !pathStr.includes('.')) { continue; } // Check if it looks like a path if (pathStr.endsWith('/')) { // Directory path if (fs_1.default.existsSync(pathStr)) { validPaths.push(pathStr); } else { warnings.push(`Directory not found: ${pathStr}`); } } else if (pathStr.includes('.')) { // File path if (fs_1.default.existsSync(pathStr)) { validPaths.push(pathStr); } else { warnings.push(`File not found: ${pathStr}`); } } else { // Assume it's a directory without trailing slash const dirPath = pathStr + '/'; if (fs_1.default.existsSync(pathStr)) { validPaths.push(pathStr); } else { warnings.push(`Path not found: ${pathStr}`); } } } return { valid: warnings.length === 0, warnings, paths: validPaths }; } async function findCode2PromptPath() { try { // Try to find code2prompt in PATH return await (0, which_1.default)('code2prompt'); } catch { // Try common locations const commonPaths = [ '/usr/local/bin/code2prompt', path_1.default.join(process.env.HOME || '', '.local/bin/code2prompt'), path_1.default.join(process.env.HOME || '', 'bin/code2prompt'), path_1.default.join(process.env.HOME || '', '.cargo/bin/code2prompt'), ]; // Add Cargo paths try { const cargoPath = (0, child_process_1.execSync)('echo $CARGO_HOME/bin', { encoding: 'utf-8' }).trim(); if (cargoPath && cargoPath !== '/bin') { commonPaths.unshift(path_1.default.join(cargoPath, 'code2prompt')); } } catch { } for (const p of commonPaths) { try { (0, child_process_1.execSync)(`test -x "${p}"`, { stdio: 'ignore' }); return p; } catch { } } return null; } } async function runCode2Prompt(basePath, options = {}) { const code2promptPath = await findCode2PromptPath(); if (!code2promptPath) { throw new Error('code2prompt not found. Install with: cargo install code2prompt'); } return new Promise((resolve, reject) => { const args = [basePath]; // Add options if (options.include?.length) { for (const pattern of options.include) { args.push('-i', pattern); } } if (options.exclude?.length) { for (const pattern of options.exclude) { args.push('-e', pattern); } } if (options.template) { args.push('-t', options.template); } if (options.lineNumbers) { args.push('-l'); } if (options.outputFormat) { args.push('-F', options.outputFormat); } if (options.tokens) { args.push('--tokens', typeof options.tokens === 'string' ? options.tokens : 'format'); } if (options.outputFile) { args.push('-O', options.outputFile); } if (options.encoding) { args.push('-c', options.encoding); } if (options.diff) { args.push('-d'); } if (options.absolutePaths) { args.push('--absolute-paths'); } if (options.noCodeblock) { args.push('--no-codeblock'); } const childProcess = (0, child_process_1.spawn)(code2promptPath, args, { stdio: ['pipe', 'pipe', 'pipe'], env: { ...process.env } }); let stdout = ''; let stderr = ''; childProcess.stdout?.on('data', (data) => { stdout += data.toString(); }); childProcess.stderr?.on('data', (data) => { stderr += data.toString(); }); childProcess.on('close', (code) => { if (code === 0) { // Parse JSON output if requested if (options.outputFormat === 'json') { try { const result = JSON.parse(stdout); resolve({ output: result.prompt || stdout, tokenCount: result.token_count }); } catch (e) { resolve({ output: stdout }); } } else { // Extract token count from stdout if tokens were requested let tokenCount; if (options.tokens) { const tokenMatch = stdout.match(/Token count: (\d+)/); if (tokenMatch) { tokenCount = parseInt(tokenMatch[1], 10); } } resolve({ output: stdout, tokenCount }); } } else { reject(new Error(`code2prompt failed with code ${code}: ${stderr}`)); } }); childProcess.on('error', (err) => { reject(err); }); }); } function extractPathsFromQuery(query) { const paths = []; const pathPattern = /@([^\s"]+)/g; let match; while ((match = pathPattern.exec(query)) !== null) { const pathStr = match[1]; // Only consider it a path if it has / or . or looks like a directory if (pathStr.includes('/') || pathStr.includes('.') || (pathStr.length < 50 && !pathStr.includes(' '))) { paths.push(pathStr); } } // Remove @paths from query to get clean prompt const cleanQuery = query.replace(/@[^\s"]+/g, '').trim(); return { paths, cleanQuery }; } function createTempPromptFile(content) { const tempDir = os_1.default.tmpdir(); const tempFile = path_1.default.join(tempDir, `claude-gemini-${crypto_1.default.randomUUID()}.md`); fs_1.default.writeFileSync(tempFile, content); return tempFile; } function cleanupTempFile(filePath) { try { if (fs_1.default.existsSync(filePath)) { fs_1.default.unlinkSync(filePath); } } catch (e) { // Ignore cleanup errors } } function formatForClaude(output) { return ` <system-message source="gemini-analysis" priority="high"> # Gemini Analysis Results **IMPORTANT**: Use these results to answer the user's question. Do not proceed with limited analysis. ${output} </system-message>`; } //# sourceMappingURL=index.js.map