ctrlshiftleft
Version:
AI-powered toolkit for embedding QA and security testing into development workflows
71 lines (62 loc) • 3.02 kB
text/typescript
import { Command } from 'commander';
import chalk from 'chalk';
import path from 'path';
import { Watcher } from '../core/watcher';
import { isValidSourcePath } from '../utils/fileUtils';
export function watchCommand(program: Command): void {
program
.command('watch')
.description('Watch source files and auto-generate tests on changes')
.argument('<source>', 'Source file or directory to watch')
.option('-o, --output <directory>', 'Output directory for generated tests', './tests')
.option('-i, --ignore <patterns>', 'Comma-separated glob patterns to ignore', 'node_modules,dist,build,*.test.*')
.option('-d, --delay <ms>', 'Debounce delay in milliseconds', '1000')
.option('-r, --run-tests', 'Auto-run tests after generation', true)
.option('-c, --generate-checklists', 'Generate security and QA checklists', true)
.option('-s, --show-diffs', 'Show file diff when files change', true)
.option('-b, --browser <browser>', 'Browser to use for tests', 'chromium')
.option('--headless', 'Run tests in headless mode', true)
.option('-w, --workers <count>', 'Number of parallel test workers', '1')
.action(async (source: string, options) => {
try {
// Validate source path
if (!await isValidSourcePath(source)) {
console.error(chalk.red(`Invalid source path: ${source}`));
return;
}
const absoluteSourcePath = path.resolve(process.cwd(), source);
const outputDir = path.resolve(process.cwd(), options.output);
const ignorePatterns = options.ignore.split(',').map((pattern: string) => pattern.trim());
// Initialize watcher
const watcher = new Watcher({
debounceMs: parseInt(options.delay, 10),
ignorePatterns,
runTests: options.runTests !== false,
generateChecklists: options.generateChecklists !== false,
showDiffs: options.showDiffs !== false,
browser: options.browser,
headless: options.headless !== false,
workers: parseInt(options.workers, 10)
});
console.log(chalk.blue(`Watching ${chalk.bold(source)} for changes...`));
console.log(chalk.gray(`Test output directory: ${outputDir}`));
console.log(chalk.gray(`Ignored patterns: ${ignorePatterns.join(', ')}`));
// Start watching
await watcher.watch(absoluteSourcePath, outputDir);
// Prevent the process from exiting
process.stdin.resume();
// Handle graceful shutdown
const shutdown = () => {
console.log(chalk.yellow('\nStopping watcher...'));
watcher.stop();
process.exit(0);
};
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
} catch (error) {
console.error(chalk.red(`Watch error: ${(error as Error).message}`));
console.error(chalk.red((error as Error).stack));
process.exit(1);
}
});
}