UNPKG

ctrlshiftleft

Version:

AI-powered toolkit for embedding QA and security testing into development workflows

152 lines (137 loc) โ€ข 5.1 kB
#!/usr/bin/env node /** * Enhanced real-time watcher for Cursor AI integration * This script provides real-time feedback during development with VS Code/Cursor integration */ // When running from the source directory let EnhancedWatcher; let analyzeSecurityWithAI; try { // Try to import from source (development mode) EnhancedWatcher = require('../src/core/enhancedWatcher').EnhancedWatcher; analyzeSecurityWithAI = require('../src/api/index').analyzeSecurityWithAI; } catch (error) { // Import from compiled package (production mode) const ctrlShiftLeft = require('ctrlshiftleft'); EnhancedWatcher = ctrlShiftLeft.EnhancedWatcher; analyzeSecurityWithAI = ctrlShiftLeft.analyzeSecurityWithAI; } const path = require('path'); const fs = require('fs'); const chalk = require('chalk'); // Parse command line arguments const argv = require('yargs') .option('dir', { alias: 'd', description: 'Directory to watch', type: 'string', default: './src' }) .option('output', { alias: 'o', description: 'Output format (console, diagnostics, json)', type: 'string', default: 'console' }) .option('ai', { description: 'Use AI-enhanced analysis', type: 'boolean', default: true }) .option('notify', { alias: 'n', description: 'Show notifications in editor', type: 'boolean', default: true }) .option('extensions', { alias: 'e', description: 'File extensions to watch', type: 'array', default: ['js', 'jsx', 'ts', 'tsx'] }) .help() .argv; // Check if OpenAI API key is available const hasOpenAIKey = process.env.OPENAI_API_KEY && process.env.OPENAI_API_KEY.length > 0; if (!hasOpenAIKey && argv.ai) { console.log(chalk.yellow('โš ๏ธ Warning: OPENAI_API_KEY not found in environment variables.')); console.log(chalk.yellow(' AI-enhanced analysis will be limited to pattern-based checks.')); console.log(chalk.yellow(' Set OPENAI_API_KEY to enable full AI capabilities.')); console.log(''); } // Configure and start the watcher console.log(chalk.blue('๐Ÿ” ctrl.shift.left cursor integration - real-time feedback')); console.log(chalk.blue(`Watching ${argv.dir} for changes to ${argv.extensions.join(', ')} files`)); console.log(chalk.blue(`AI-enhanced analysis: ${argv.ai ? 'enabled' : 'disabled'}`)); console.log(chalk.blue(`Editor notifications: ${argv.notify ? 'enabled' : 'disabled'}`)); console.log(''); // Create the enhanced watcher const watcher = new EnhancedWatcher({ useAIAnalysis: argv.ai, notifyOnChange: argv.notify, cursorIntegration: true, outputFormat: argv.output, fileExtensions: argv.extensions }); // Output diagnostics in VS Code/Cursor format function outputDiagnostics(file, issues) { const diagnostics = issues.map(issue => ({ file: file, line: issue.location?.line || 1, column: issue.location?.column || 1, message: `${issue.severity || 'Warning'}: ${issue.message}`, code: issue.code || 'SECURITY', source: 'ctrl.shift.left', severity: issue.severity === 'Critical' ? 'error' : 'warning' })); // Output in format that editors can parse if (argv.output === 'diagnostics') { diagnostics.forEach(d => { console.log(`${d.file}:${d.line}:${d.column} - ${d.severity}: ${d.message} [${d.code}]`); }); } else if (argv.output === 'json') { console.log(JSON.stringify(diagnostics, null, 2)); } else { // Console format with colors if (diagnostics.length > 0) { console.log(chalk.yellow(`\n๐Ÿ” Issues found in ${path.basename(file)}:`)); diagnostics.forEach(d => { const severity = d.severity === 'error' ? chalk.red('Error') : chalk.yellow('Warning'); console.log(` ${severity}: ${d.message}`); console.log(` ${chalk.gray(`at line ${d.line}, column ${d.column}`)}`); }); } else { console.log(chalk.green(`โœ… No issues found in ${path.basename(file)}`)); } } } // Handle file changes watcher.on('change', async (filePath) => { try { // Read file content const content = fs.readFileSync(filePath, 'utf8'); // Analyze with AI security analyzer const analysis = await analyzeSecurityWithAI(content, { filePath: filePath, context: path.extname(filePath).slice(1) }); // Output diagnostics outputDiagnostics(filePath, analysis.issues || []); // Generate tests if it's a component file if (/components?\/.*\.(jsx?|tsx?)$/.test(filePath) && argv.notify) { console.log(chalk.blue(`\n๐Ÿงช Generating tests for ${path.basename(filePath)}...`)); // This would trigger test generation, but we're just notifying here console.log(chalk.blue(`Run: ctrlshiftleft gen ${filePath} to generate tests`)); } } catch (error) { console.error(chalk.red('Error processing file:'), error); } }); // Start watching watcher.watch(argv.dir).then(() => { console.log(chalk.green('Watcher started successfully')); console.log(chalk.gray('Press Ctrl+C to stop')); }).catch(error => { console.error(chalk.red('Failed to start watcher:'), error); });