#!/usr/bin/env node import { Command } from 'commander'; import * as path from 'node:path'; import * as fs from 'node:fs/promises'; import { PromptManager } from './prompt-manager.ts'; import { CopyOptions, copyPrompts } from './prompt-copier.ts'; import { PromptConfigManager, PromptPathResolver, PromptValidator, createProgressBar, formatFileSize, formatDuration } from './prompt-utils.ts'; // Enterprise logger for this module const logger = { info: (msg: string, ...args: any[]) => console.log(`[INFO] ${msg}`, ...args), error: (msg: string, ...args: any[]) => console.error(`[ERROR] ${msg}`, ...args), warn: (msg: string, ...args: any[]) => console.warn(`[WARN] ${msg}`, ...args), debug: (msg: string, ...args: any[]) => console.log(`[DEBUG] ${msg}`, ...args) }; const program = new Command(); program .name('prompt-copier') .description('Enterprise-grade prompt copying mechanism for FlowX') .version('2.0.0'); program .command('copy') .description('Copy prompts from source to destination with enterprise features') .option('-s, --source ', 'Source directory') .option('-d, --destination ', 'Destination directory') .option('-p, --profile ', 'Configuration profile to use') .option('--no-backup', 'Disable backup creation') .option('--no-verify', 'Disable file verification') .option('--no-parallel', 'Disable parallel processing') .option('--workers ', 'Number of worker threads', parseInt) .option('--conflict ', 'Conflict resolution strategy', /^(skip|overwrite|backup|merge)$/) .option('--include ', 'Include patterns (comma-separated)') .option('--exclude ', 'Exclude patterns (comma-separated)') .option('--dry-run', 'Show what would be copied without actually copying') .option('--enhanced', 'Use enhanced copier with worker threads') .action(async (options) => { try { const configManager = new PromptConfigManager(); const config = await configManager.loadConfig(); let copyOptions: CopyOptions; if (options.profile) { const profileOptions = configManager.getProfile(options.profile); copyOptions = { source: options.source || config.sourceDirectories[0], destination: options.destination || config.destinationDirectory, ...profileOptions }; } else { copyOptions = { source: options.source || config.sourceDirectories[0], destination: options.destination || config.destinationDirectory, backup: options.backup !== false, // Default to true unless explicitly disabled verify: options.verify !== false, // Default to true unless explicitly disabled parallel: options.parallel !== false, // Default to true unless explicitly disabled maxWorkers: options.workers || config.defaultOptions.maxWorkers, conflictResolution: options.conflict || config.defaultOptions.conflictResolution, includePatterns: options.include ? options.include.split(',') : config.defaultOptions.includePatterns, excludePatterns: options.exclude ? options.exclude.split(',') : config.defaultOptions.excludePatterns, dryRun: options.dryRun, overwrite: options.conflict === 'overwrite' }; } // Create progress bar let progressBar: ReturnType | null = null; copyOptions.progressCallback = (progress) => { if (!progressBar) { progressBar = createProgressBar(progress.total); } progressBar.update(progress.completed); if (progress.completed === progress.total) { progressBar.complete(); } }; console.log('šŸš€ Starting enterprise prompt copy operation...'); console.log(`šŸ“ Source: ${copyOptions.source}`); console.log(`šŸ“ Destination: ${copyOptions.destination}`); if (options.dryRun) { console.log('šŸ” DRY RUN MODE - No files will be modified'); } const startTime = Date.now(); const result = await copyPrompts(copyOptions); console.log('\n=== Enterprise Copy Results ==='); console.log(`āœ… Success: ${result.success ? 'āœ…' : 'āŒ'}`); console.log(`šŸ“Š Total files: ${result.totalFiles}`); console.log(`šŸ“‹ Copied: ${result.copiedFiles}`); console.log(`āš ļø Failed: ${result.failedFiles}`); console.log(`ā­ļø Skipped: ${result.skippedFiles}`); console.log(`ā±ļø Duration: ${formatDuration(result.duration)}`); if (result.bytesTransferred) { console.log(`šŸ’¾ Data transferred: ${formatFileSize(result.bytesTransferred)}`); } if (result.backupLocation) { console.log(`šŸ”„ Backup created: ${result.backupLocation}`); } if (result.errors.length > 0) { console.log('\n=== āš ļø Errors ==='); result.errors.forEach(error => { const timestamp = error.timestamp.toISOString(); console.log(`āŒ [${timestamp}] ${error.file}: ${error.error} (${error.phase})`); }); } // Performance metrics const throughput = result.bytesTransferred ? (result.bytesTransferred / (result.duration / 1000) / 1024 / 1024).toFixed(2) : 'N/A'; console.log('\n=== šŸ“ˆ Performance Metrics ==='); console.log(`šŸƒ Throughput: ${throughput} MB/s`); console.log(`⚔ Files/sec: ${(result.copiedFiles / (result.duration / 1000)).toFixed(2)}`); if (!result.success) { process.exit(1); } } catch (error: any) { logger.error('Copy operation failed:', error); console.error('āŒ Enterprise copy operation failed:', error.message); process.exit(1); } }); program .command('discover') .description('Discover prompt directories in the current project') .option('-b, --base ', 'Base path to search from', process.cwd()) .option('--json', 'Output in JSON format') .action(async (options) => { try { const resolver = new PromptPathResolver(options.base); const directories = await resolver.discoverPromptDirectories(); if (options.tson) { console.log(JSON.stringify({ directories }, null, 2)); return; } console.log('šŸ“ Discovered prompt directories:'); directories.forEach((dir, index) => { console.log(` ${index + 1}. šŸ“‚ ${dir}`); }); if (directories.length === 0) { console.log(' ā„¹ļø No prompt directories found'); } else { console.log(`\nāœ… Found ${directories.length} prompt directories`); } } catch (error) { logger.error('Discovery failed:', error); console.error('āŒ Discovery failed:', error); process.exit(1); } }); program .command('validate') .description('Validate prompt files with enterprise standards') .argument('', 'Path to validate (file or directory)') .option('--recursive', 'Validate recursively') .option('--json', 'Output in JSON format') .option('--strict', 'Use strict validation rules') .action(async (filePath, options) => { try { const stats = await fs.stat(filePath); const files: string[] = []; if (stats.isFile()) { files.push(filePath); } else if (stats.isDirectory()) { // Scan directory for prompt files const scanDir = async (dir: string) => { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isFile() && ( entry.name.endsWith('.md') || entry.name.endsWith('.txt') || entry.name.endsWith('.prompt') )) { files.push(fullPath); } else if (entry.isDirectory() && options.recursive) { await scanDir(fullPath); } } }; await scanDir(filePath); } console.log(`šŸ” Validating ${files.length} files with enterprise standards...`); let validFiles = 0; let invalidFiles = 0; const results: any[] = []; for (const file of files) { const result = await PromptValidator.validatePromptFile(file); if (result.valid) { validFiles++; if (!options.tson) { console.log(`āœ… ${file}`); } } else { invalidFiles++; if (!options.tson) { console.log(`āŒ ${file}`); result.issues.forEach((issue: string) => { console.log(` āš ļø ${issue}`); }); } } if (options.tson) { results.push({ file, valid: result.valid, issues: result.issues, metadata: result.metadata }); } else if (result.metadata && Object.keys(result.metadata).length > 0) { console.log(` šŸ“Š Metadata: ${JSON.stringify(result.metadata)}`); } } if (options.tson) { console.log(JSON.stringify({ summary: { total: files.length, valid: validFiles, invalid: invalidFiles }, results }, null, 2)); } else { console.log(`\nšŸ“Š Validation complete: ${validFiles} valid, ${invalidFiles} invalid`); if (invalidFiles > 0) { console.log('āš ļø Some files failed validation. Consider fixing issues for enterprise compliance.'); process.exit(1); } else { console.log('āœ… All files meet enterprise standards!'); } } } catch (error) { logger.error('Validation failed:', error); console.error('āŒ Validation failed:', error); process.exit(1); } }); program .command('config') .description('Manage enterprise configuration') .option('--init', 'Initialize default configuration') .option('--show', 'Show current configuration') .option('--profiles', 'List available profiles') .option('--json', 'Output in JSON format') .action(async (options) => { try { const configManager = new PromptConfigManager(); if (options.init) { await configManager.saveConfig(); console.log('āœ… Enterprise configuration initialized'); } else if (options.show) { const config = await configManager.loadConfig(); if (options.tson) { console.log(JSON.stringify(config, null, 2)); } else { console.log('šŸ“‹ Current Configuration:'); console.log(JSON.stringify(config, null, 2)); } } else if (options.profiles) { const config = await configManager.loadConfig(); const profiles = configManager.listProfiles(); if (options.tson) { const profileData: Record = {}; profiles.forEach(profile => { profileData[profile] = configManager.getProfile(profile); }); console.log(JSON.stringify(profileData, null, 2)); } else { console.log('šŸ“‹ Available enterprise profiles:'); profiles.forEach(profile => { console.log(` šŸ·ļø ${profile}`); const profileOptions = configManager.getProfile(profile); Object.entries(profileOptions).forEach(([key, value]) => { console.log(` ${key}: ${JSON.stringify(value)}`); }); }); } } else { console.log('Use --init, --show, or --profiles'); } } catch (error) { logger.error('Configuration operation failed:', error); console.error('āŒ Configuration operation failed:', error); process.exit(1); } }); program .command('rollback') .description('Rollback from backup with enterprise safety') .argument('', 'Path to backup manifest file') .option('--confirm', 'Confirm rollback operation') .action(async (manifestPath, options) => { try { if (!options.confirm) { console.log('āš ļø Rollback is a destructive operation.'); console.log('Use --confirm flag to proceed with rollback.'); process.exit(1); } const { PromptCopier } = await import('./prompt-copier.ts'); const copier = new PromptCopier({ source: '', destination: '' }); console.log('šŸ”„ Starting enterprise rollback operation...'); await copier.restoreFromBackup(manifestPath); console.log('āœ… Enterprise rollback completed successfully'); } catch (error) { logger.error('Rollback failed:', error); console.error('āŒ Enterprise rollback failed:', error); process.exit(1); } }); program .command('sync') .description('Synchronize prompts between directories with enterprise features') .option('-s, --source ', 'Source directory') .option('-d, --destination ', 'Destination directory') .option('--bidirectional', 'Enable bidirectional sync') .option('--delete', 'Delete files not present in source') .option('--dry-run', 'Show what would be synchronized') .action(async (options) => { try { console.log('šŸ”„ Enterprise sync functionality'); console.log('šŸ“Š Sync options:', JSON.stringify(options, null, 2)); if (options.dryRun) { console.log('šŸ” DRY RUN: Would synchronize directories'); console.log(`šŸ“ Source: ${options.source}`); console.log(`šŸ“ Destination: ${options.destination}`); console.log(`šŸ”„ Bidirectional: ${options.bidirectional ? 'Yes' : 'No'}`); console.log(`šŸ—‘ļø Delete orphaned: ${options.delete ? 'Yes' : 'No'}`); } else { console.log('āš ļø Enterprise sync functionality is under development'); console.log('Use --dry-run to preview sync operations'); } } catch (error) { logger.error('Sync failed:', error); console.error('āŒ Enterprise sync failed:', error); process.exit(1); } }); // Enterprise error handling process.on('uncaughtException', (error) => { logger.error('Uncaught exception:', error); console.error('šŸ’„ Critical error occurred:', error.message); process.exit(1); }); process.on('unhandledRejection', (reason) => { logger.error('Unhandled rejection:', reason); console.error('šŸ’„ Unhandled promise rejection:', reason); process.exit(1); }); // Graceful shutdown process.on('SIGINT', () => { console.log('\nšŸ›‘ Received interrupt signal. Shutting down gracefully...'); process.exit(0); }); process.on('SIGTERM', () => { console.log('\nšŸ›‘ Received termination signal. Shutting down gracefully...'); process.exit(0); }); if (require.main === module) { program.parse(); } export { program };