ctrlshiftleft
Version:
AI-powered toolkit for embedding QA and security testing into development workflows
152 lines (137 loc) โข 5.1 kB
JavaScript
/**
* 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);
});