UNPKG

ai-context-commit-tools

Version:

AI context builder with automated commit message generation and changelog maintenance for enhanced AI-assisted development

205 lines (204 loc) 7.85 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CursorClient = void 0; const tslib_1 = require("tslib"); const child_process_1 = require("child_process"); const os = tslib_1.__importStar(require("os")); const path = tslib_1.__importStar(require("path")); class CursorClient { constructor(options = {}) { this.options = { debugMode: false, timeout: 30000, model: 'sonnet-4', maxRetries: 2, retryDelay: 2000, retryMultiplier: 2, ...options, }; this.cursorPath = this.findCursorPath(); } async generateResponse(prompt) { if (!this.cursorPath) { return { success: false, error: 'Cursor CLI not found. Please install Cursor CLI for AI features.', }; } const maxRetries = this.options.maxRetries || 2; let lastError = ''; for (let attempt = 1; attempt <= maxRetries + 1; attempt++) { if (attempt > 1) { const retryMessage = this.options.debugMode ? `🔄 Retry attempt ${attempt - 1}/${maxRetries}` : `🔄 Retrying AI generation (${attempt - 1}/${maxRetries})...`; this.log(retryMessage); } const result = await this.attemptGeneration(prompt, attempt); if (result.success) { if (this.options.debugMode && attempt > 1) { this.log(`✅ Success on attempt ${attempt}`); } return result; } lastError = result.error || 'Unknown error'; if (!this.isRetryableError(lastError) || attempt === maxRetries + 1) { return result; } const baseDelay = this.options.retryDelay || 2000; const multiplier = this.options.retryMultiplier || 2; const delay = Math.min(baseDelay * Math.pow(multiplier, attempt - 1), 10000); if (this.options.debugMode) { this.log(`⏳ Waiting ${delay}ms before retry...`); } await new Promise(resolve => setTimeout(resolve, delay)); } return { success: false, error: lastError, }; } async attemptGeneration(prompt, attempt) { return new Promise(resolve => { try { const fs = require('fs'); const tempFile = `/tmp/cursor-prompt-ai-${Date.now()}-${attempt}.txt`; fs.writeFileSync(tempFile, prompt, 'utf8'); const currentModel = 'sonnet-4'; const command = this.cursorPath === 'cursor-agent' ? `cursor-agent --print --model ${currentModel} --output-format text < "${tempFile}"` : `"${this.cursorPath}" --print --model ${currentModel} --output-format text < "${tempFile}"`; if (this.options.debugMode) { this.log('=== CURSOR COMMAND ==='); this.log(command); if (attempt > 1) { this.log(`🔄 Trying model: ${currentModel} (attempt ${attempt})`); } this.log('=== PROMPT ==='); this.log(`${prompt.substring(0, 500)}...`); this.log('=== END DEBUG ===\n'); } const timeout = attempt === 1 ? this.options.timeout * 1.5 : this.options.timeout; (0, child_process_1.exec)(command, { timeout, shell: '/bin/bash', maxBuffer: 1024 * 1024, }, (error, stdout, _stderr) => { try { if (fs.existsSync(tempFile)) { fs.unlinkSync(tempFile); } } catch (cleanupError) { } if (error) { let errorMessage = `Cursor CLI execution failed: ${error.message}`; if (error.message.includes('Cannot use this model')) { errorMessage += '\n\nAvailable models: auto, sonnet-4, gpt-5, opus-4.1, grok'; errorMessage += '\nTry using --model auto or --model sonnet-4'; } resolve({ success: false, error: errorMessage, }); return; } try { const cleanResponse = stdout.replace(/\x1b\[[0-9;]*m/g, '').trim(); if (!cleanResponse) { resolve({ success: false, error: 'Cursor CLI returned empty response', }); return; } resolve({ success: true, message: cleanResponse, }); } catch (parseError) { resolve({ success: false, error: `Failed to parse Cursor response: ${parseError}`, }); } }); } catch (setupError) { resolve({ success: false, error: `Failed to setup Cursor CLI call: ${setupError instanceof Error ? setupError.message : String(setupError)}`, }); } }); } isRetryableError(errorMessage) { const retryablePatterns = [ 'timeout', 'etimedout', 'econnreset', 'econnrefused', 'enotfound', 'network', 'connection', 'temporarily unavailable', 'service unavailable', 'internal server error', 'gateway timeout', 'bad gateway', 'command failed', 'cursor cli execution failed', 'execution error', 'spawn', 'no such file', ]; const lowerErrorMessage = errorMessage.toLowerCase(); return retryablePatterns.some(pattern => lowerErrorMessage.includes(pattern)); } isAvailable() { return !!this.cursorPath; } async getVersion() { if (!this.cursorPath) { return null; } return new Promise(resolve => { (0, child_process_1.exec)(`${this.cursorPath} --version`, (error, stdout) => { if (error) { resolve(null); return; } resolve(stdout.trim()); }); }); } findCursorPath() { const possiblePaths = [ path.join(os.homedir(), '.local/bin/cursor-agent'), '/usr/local/bin/cursor', '/opt/homebrew/bin/cursor', 'cursor', 'cursor-agent', ]; for (const cursorPath of possiblePaths) { try { require('child_process').execSync(`${cursorPath} --help`, { stdio: 'ignore' }); return cursorPath; } catch (error) { continue; } } return undefined; } log(message) { if (this.options.silentMode) { return; } if (this.options.debugMode || process.env.NODE_ENV !== 'test') { console.log(message); } } } exports.CursorClient = CursorClient;