claude-gemini
Version:
Global CLI tool for Claude-Gemini integration across projects
289 lines (287 loc) • 10.1 kB
JavaScript
;
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