UNPKG

codeplot

Version:

Interactive CLI tool for feature planning and ADR generation using Gemini 2.5 Pro

165 lines (144 loc) 5.57 kB
#!/usr/bin/env node import 'reflect-metadata'; import { Command } from 'commander'; import { render } from 'ink'; import React from 'react'; import App from './ui/App.jsx'; import { logger } from './utils/logger.js'; import container, { configureContainer } from './container.js'; import { FeatureArchitect } from './feature-architect.js'; interface PlanOptions { projectPath: string; apiKey?: string; outputDir: string; streaming: boolean; typingSpeed: string; debug?: boolean; logLevel?: string; } interface EnvironmentInfo { nodeVersion: string; platform: string; arch: string; cwd: string; } const program = new Command(); program .name('codeplot') .description('Interactive CLI tool for feature planning and ADR generation using Gemini 2.5 Pro') .version('1.0.0'); program .command('plan') .description('Start interactive feature planning session') .option('-p, --project-path <path>', 'Path to the project repository', process.cwd()) .option('-k, --api-key <key>', 'Gemini API key (or set GEMINI_API_KEY env var)') .option('-o, --output-dir <dir>', 'Output directory for ADRs', './doc/adr') .option('--no-streaming', 'Disable streaming responses (show all at once)') .option('--typing-speed <speed>', 'Typing speed for streaming: fast, normal, slow', 'normal') .option('--debug', 'Enable debug mode with verbose logging and stack traces') .option('--log-level <level>', 'Set log level: error, warn, info, debug, trace', 'info') .action(async (options: PlanOptions) => { try { // Set environment variables for debug mode and log level if (options.debug) { process.env.DEBUG = 'true'; } if (options.logLevel) { process.env.LOG_LEVEL = options.logLevel; } const environmentInfo: EnvironmentInfo = { nodeVersion: process.version, platform: process.platform, arch: process.arch, cwd: process.cwd(), }; logger.info('Starting codeplot application', { options: { ...options, apiKey: options.apiKey ? '[REDACTED]' : undefined }, environment: environmentInfo, }); // Validate required options if (!options.apiKey && !process.env.GEMINI_API_KEY) { const error = new Error( 'Gemini API key is required. Set GEMINI_API_KEY environment variable or use --api-key option.' ); logger.errorWithStack(error, 'Missing API key'); return; // errorWithStack will throw in debug mode } // Configure the DI container with runtime options configureContainer({ projectPath: options.projectPath, apiKey: options.apiKey || process.env.GEMINI_API_KEY || '', outputDir: options.outputDir, streaming: options.streaming, typingSpeed: options.typingSpeed, }); // Resolve the FeatureArchitect from the container const featureArchitect = container.resolve(FeatureArchitect); // Set up global error handlers setupGlobalErrorHandlers(); // Render the ink App component with the injected FeatureArchitect logger.debug('Rendering React App component'); render(React.createElement(App, { featureArchitect })); } catch (error) { logger.errorWithStack(error as Error, 'Failed to start application'); // In non-debug mode, show user-friendly error and exit if (!process.env.DEBUG) { console.error('❌ Application failed to start. Run with --debug for more details.'); console.error(`Debug log saved to: ${logger.getLogFilePath()}`); process.exit(1); } } }); program .command('init') .description('Initialize the CLI tool configuration') .action(async () => { console.log('📊 Codeplot CLI'); console.log('Plot your features with AI-powered planning and ADR generation'); console.log(); console.log('Setup:'); console.log('1. Install repomix: npm install -g repomix'); console.log('2. Install adr-tools: npm install -g adr-tools'); console.log('3. Set your Gemini API key: export GEMINI_API_KEY=your_api_key'); console.log(); console.log('Usage:'); console.log('codeplot plan --project-path /path/to/your/project'); }); // Global error handlers setup function setupGlobalErrorHandlers(): void { // Handle uncaught exceptions process.on('uncaughtException', (error: Error) => { logger.errorWithStack(error, 'Uncaught Exception'); if (!process.env.DEBUG) { console.error('❌ Unexpected error occurred. Debug log saved to:', logger.getLogFilePath()); process.exit(1); } }); // Handle unhandled promise rejections process.on('unhandledRejection', (reason: unknown, promise: Promise<unknown>) => { const error = reason instanceof Error ? reason : new Error(String(reason)); logger.errorWithStack(error, 'Unhandled Promise Rejection', { promise }); if (!process.env.DEBUG) { console.error('❌ Unexpected error occurred. Debug log saved to:', logger.getLogFilePath()); process.exit(1); } }); // Handle process warnings process.on('warning', (warning: Error) => { logger.warn('Process Warning', { name: warning.name, message: warning.message, stack: warning.stack, }); }); // Handle graceful shutdown process.on('SIGINT', () => { logger.info('Received SIGINT, shutting down gracefully'); process.exit(0); }); process.on('SIGTERM', () => { logger.info('Received SIGTERM, shutting down gracefully'); process.exit(0); }); } program.parse();