import { Command } from 'commander'; import { Configuration } from '../../config/Configuration'; import { GitAdapter } from '../../adapters/outbound/GitAdapter'; import { LLMAdapter } from '../../adapters/outbound/LLMAdapter'; import { OutputAdapter } from '../../adapters/outbound/OutputAdapter'; import { GitHubAdapter } from '../../adapters/outbound/GitHubAdapter'; import { TestGeneratorAdapter } from '../../adapters/outbound/TestGeneratorAdapter'; import { SecurityScannerAdapter } from '../../adapters/outbound/SecurityScannerAdapter'; import { DocumentationAdapter } from '../../adapters/outbound/DocumentationAdapter'; export abstract class BaseCommand extends Command { protected config!: Configuration; protected gitAdapter!: GitAdapter; protected llmAdapter!: LLMAdapter; protected outputAdapter!: OutputAdapter; protected githubAdapter?: GitHubAdapter; protected testGeneratorAdapter!: TestGeneratorAdapter; protected securityScannerAdapter!: SecurityScannerAdapter; protected documentationAdapter!: DocumentationAdapter; constructor(name: string, description: string) { super(name); this.description(description); // Add common options this.option('--base-ref ', 'Base reference for comparison') .option('--head-ref ', 'Head reference for comparison') .option('--staged', 'Include staged changes') .option('--unstaged', 'Include unstaged changes') .option('--file-patterns ', 'File patterns to include (comma-separated)') .option('--exclude-patterns ', 'File patterns to exclude (comma-separated)') .option('--output-format ', 'Output format (console, markdown, json, html, file)') .option('--output-path ', 'Output file path') .option('--verbose', 'Enable verbose output') .option('--no-color', 'Disable colored output'); // Initialize adapters in action this.action(async (options, command) => { try { await this.initializeAdapters(command); await this.execute(options, command); } catch (error) { console.error(`Command failed: ${error instanceof Error ? error.message : 'Unknown error'}`); process.exit(1); } }); } protected async initializeAdapters(command: Command): Promise { // Get config from parent command this.config = (command.parent as any).config; if (!this.config) { throw new Error('Configuration not found'); } // Initialize adapters this.gitAdapter = new GitAdapter(this.config.git.repoPath); this.llmAdapter = new LLMAdapter(this.config); this.outputAdapter = new OutputAdapter(); this.testGeneratorAdapter = new TestGeneratorAdapter(this.llmAdapter); this.securityScannerAdapter = new SecurityScannerAdapter(this.llmAdapter); this.documentationAdapter = new DocumentationAdapter(this.llmAdapter); // Initialize GitHub adapter if token is provided if (this.config.github?.token) { this.githubAdapter = new GitHubAdapter({ token: this.config.github.token, ...(this.config.github.baseUrl && { baseUrl: this.config.github.baseUrl }), ...(this.config.github.apiVersion && { apiVersion: this.config.github.apiVersion }), }); } } protected abstract execute(options: any, command: Command): Promise; protected parseFilePatterns(patterns?: string): string[] | undefined { return patterns ? patterns.split(',').map(p => p.trim()) : undefined; } protected parseExcludePatterns(patterns?: string): string[] | undefined { return patterns ? patterns.split(',').map(p => p.trim()) : undefined; } protected getOutputFormat(options: any): string { return options.outputFormat || this.config.output.format; } protected getOutputPath(options: any): string | undefined { return options.outputPath || this.config.output.outputPath; } protected isVerbose(options: any): boolean { return options.verbose || this.config.verbose || this.config.output.verbose; } protected isColorized(options: any): boolean { return !options.noColor && (this.config.output.colorize ?? true); } }