ctrlshiftleft
Version:
AI-powered toolkit for embedding QA and security testing into development workflows
141 lines (121 loc) • 6.25 kB
text/typescript
/**
* AI-Enhanced Watch Command
*
* This command provides an intelligent file watching capability that automatically
* generates tests, performs security analysis, and creates QA checklists as you code.
* It's designed to work seamlessly with code generation tools like Cursor AI.
*/
import { Command } from 'commander';
import chalk from 'chalk';
import ora from 'ora';
import path from 'path';
// Use a compatible way to import figures
const figures = {
tick: '✓',
cross: '✖',
warning: '⚠',
info: 'ℹ',
pointer: '❯',
line: '│'
};
import { EnhancedWatcher } from '../core/enhancedWatcher';
export function watchAICommand(program: Command): void {
program
.command('watch-ai')
.description('Watch files and automatically generate tests, security reports, and checklists as you code')
.argument('[directory]', 'Directory to watch', '.')
.option('-t, --tests', 'Generate tests on file changes', true)
.option('-s, --security', 'Perform security analysis on file changes', true)
.option('-c, --checklists', 'Generate QA checklists on file changes', true)
.option('-a, --ai', 'Use AI-enhanced security analysis', false)
.option('--test-output <dir>', 'Output directory for tests', './tests')
.option('--security-output <dir>', 'Output directory for security reports', './security-reports')
.option('--checklist-output <dir>', 'Output directory for checklists', './checklists')
.option('--include <patterns...>', 'Glob patterns to include')
.option('--exclude <patterns...>', 'Glob patterns to exclude')
.option('--concurrent <n>', 'Maximum concurrent tasks', '3')
.option('--silent', 'Run in silent mode (no progress reporting)', false)
.option('--test-format <format>', 'Test format (playwright or selenium)', 'playwright')
.action(async (directory, options) => {
// Normalize directory path
const dirPath = path.resolve(process.cwd(), directory);
// Display banner
console.log(chalk.cyan.bold('\n┌──────────────────────────────────────────────┐'));
console.log(chalk.cyan.bold('│ ctrl.shift.left AI-Enhanced Watcher │'));
console.log(chalk.cyan.bold('└──────────────────────────────────────────────┘'));
console.log(chalk.dim(`Version 1.0.0 | Mode: ${options.ai ? 'AI-Enhanced' : 'Standard'}\n`));
// Check for OpenAI API key if AI mode is enabled
if (options.ai && !process.env.OPENAI_API_KEY) {
console.log(chalk.yellow(`${figures.warning} AI-enhanced security analysis requested, but no OpenAI API key found.`));
console.log(chalk.yellow(`${figures.line} Set the OPENAI_API_KEY environment variable to enable AI features.`));
console.log(chalk.yellow(`${figures.line} Falling back to pattern-based analysis.\n`));
}
// Create watcher options
const watcherOptions = {
include: options.include,
exclude: options.exclude,
generateTests: options.tests,
analyzeSecurity: options.security,
useAIAnalysis: options.ai,
generateChecklists: options.checklists,
testOutputDir: options.testOutput,
securityOutputDir: options.securityOutput,
checklistOutputDir: options.checklistOutput,
maxConcurrentTasks: parseInt(options.concurrent, 10),
reportProgress: !options.silent,
testFormat: options.testFormat as 'playwright' | 'selenium',
openAIApiKey: process.env.OPENAI_API_KEY
};
// Create and start the watcher
const watcher = new EnhancedWatcher(watcherOptions);
// Setup event handlers for real-time feedback
watcher.on('ready', () => {
console.log(chalk.green(`${figures.tick} Watcher ready and monitoring ${chalk.cyan(dirPath)}`));
console.log(chalk.green(`${figures.line} AI Integration: ${options.ai ? (process.env.OPENAI_API_KEY ? 'Active' : 'Disabled (no API key)') : 'Disabled'}`));
console.log(chalk.green(`${figures.line} Press Ctrl+C to stop the watcher\n`));
});
watcher.on('file:change', (event) => {
if (!options.silent) {
console.log(chalk.blue(`${figures.pointer} File ${event.type}: ${path.basename(event.path)}`));
}
});
watcher.on('analysis:complete', (result) => {
if (!options.silent && result.success) {
const typeColor = result.type === 'test' ? chalk.magenta :
result.type === 'security' ? chalk.yellow :
chalk.blue;
console.log(`${chalk.green(figures.tick)} ${typeColor(result.type.toUpperCase())} analysis complete for ${path.basename(result.filePath)} (${result.duration}ms)`);
if (result.outputPath) {
console.log(`${chalk.dim(figures.line)} Output: ${result.outputPath}`);
}
}
});
watcher.on('error', (error) => {
console.error(chalk.red(`${figures.cross} Error: ${error.message}`));
});
// Start watching
const stopWatching = watcher.watch(dirPath);
// Handle process exit
const handleExit = async () => {
console.log(chalk.yellow('\n\nStopping watcher...'));
const spinner = ora('Cleaning up...').start();
await watcher.stop();
spinner.succeed('Watcher stopped');
console.log(chalk.green(`\n${figures.tick} Thank you for using ctrl.shift.left!`));
process.exit(0);
};
// Handle interrupts
process.on('SIGINT', handleExit);
process.on('SIGTERM', handleExit);
try {
// Keep the process running
await new Promise(() => {}); // Never resolves
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error(chalk.red(`${figures.cross} Error: ${errorMessage}`));
await watcher.stop();
process.exit(1);
}
});
}
export default watchAICommand;