UNPKG

@cirrusinvicta/ai-commit-toolkit

Version:

AI-powered conventional commit generation with centralized configuration, OpenAI integration, and automated deployment workflows

178 lines (149 loc) • 6.85 kB
#!/usr/bin/env node const { program } = require('commander'); const chalk = require('chalk'); const { version } = require('./package.json'); const AICommitToolkitSetup = require('./lib/setup'); /** * AI Commit Toolkit - Main CLI entry point */ program .name('ai-commit-toolkit') .description('AI-powered conventional commit generation toolkit') .version(version); program .command('setup') .description('Setup AI commit toolkit in current project') .option('--type <type>', 'Project type (ansible, vue, react, generic)', 'generic') .option('--commit-style <style>', 'Commit message style: user-centric (default) or developer-centric', 'user-centric') .option('--no-hooks', 'Skip husky hooks setup') .option('--no-semantic-release', 'Skip semantic-release setup') .option('--silent', 'Run without interactive prompts') .option('--force', 'Overwrite existing configuration files') .option('--backup', 'Create backups of existing files before overwriting') .option('--no-skip-existing', 'Don\'t skip existing configuration files (default: skip)') .action(async (options) => { console.log(chalk.blue('šŸ¤– AI Commit Toolkit Setup')); console.log(chalk.gray('==============================')); if (options.force && options.backup) { console.log(chalk.yellow('šŸ”„ Update mode: Overwriting with backups')); } else if (options.force) { console.log(chalk.yellow('āš ļø Force mode: Overwriting existing files')); } else { console.log(chalk.blue('šŸ›”ļø Safe mode: Preserving existing configurations')); } const setup = new AICommitToolkitSetup(options); const success = await setup.install(options); if (success) { console.log(chalk.green('\nāœ… Setup complete! Try: git add . && git commit')); console.log(chalk.yellow('šŸ’” Don\'t forget to set OPENAI_API_KEY environment variable')); if (!options.force) { console.log(chalk.gray('\nšŸ“‹ Tip: Use --force --backup to update configurations on package updates')); } } else { process.exit(1); } }); program .command('scopes') .description('Display available commit scopes and their descriptions') .option('--category <category>', 'Show scopes for specific category (features, technical, concerns, examples)') .action(async (options) => { const fs = require('fs'); const path = require('path'); // Check if config exists const configPath = path.join(process.cwd(), 'config', 'ai-prompts.conf'); if (!fs.existsSync(configPath)) { console.log(chalk.red('āŒ AI commit configuration not found')); console.log(chalk.yellow('šŸ’” Run "ai-commit-toolkit setup" first')); process.exit(1); } try { const config = fs.readFileSync(configPath, 'utf8'); const parseScopes = (line) => { const match = line.match(/^SCOPE_MAP_\w+="([^"]*)"$/); if (!match) return []; return match[1].split('|').map(scope => { const [name, desc] = scope.split(':'); return { name: name.trim(), description: desc.trim() }; }); }; const categories = { features: { title: 'šŸŽÆ Feature Areas', pattern: /^SCOPE_MAP_FEATURES=/ }, technical: { title: 'āš™ļø Technical Areas', pattern: /^SCOPE_MAP_TECHNICAL=/ }, concerns: { title: 'šŸ”„ Cross-cutting Concerns', pattern: /^SCOPE_MAP_CONCERNS=/ }, examples: { title: 'šŸ“ Sub-scope Examples', pattern: /^SCOPE_MAP_EXAMPLES=/ } }; console.log(chalk.blue('šŸ·ļø Available Commit Scopes')); console.log(chalk.gray('============================\n')); const lines = config.split('\n'); if (options.category) { const cat = categories[options.category]; if (!cat) { console.log(chalk.red(`āŒ Unknown category: ${options.category}`)); console.log(chalk.yellow('Available categories: features, technical, concerns, examples')); process.exit(1); } const line = lines.find(l => cat.pattern.test(l)); if (line) { console.log(chalk.cyan(cat.title)); console.log(chalk.gray('─'.repeat(cat.title.length - 2)) + '\n'); const scopes = parseScopes(line); scopes.forEach(scope => { console.log(chalk.green(` ${scope.name}`) + chalk.gray(` - ${scope.description}`)); }); } } else { Object.entries(categories).forEach(([key, cat]) => { const line = lines.find(l => cat.pattern.test(l)); if (line) { console.log(chalk.cyan(cat.title)); console.log(chalk.gray('─'.repeat(cat.title.length - 2))); const scopes = parseScopes(line); scopes.forEach(scope => { console.log(chalk.green(` ${scope.name}`) + chalk.gray(` - ${scope.description}`)); }); console.log(''); } }); console.log(chalk.yellow('šŸ’” Examples:')); console.log(chalk.gray(' feat(mail.allow-block-lists): add search functionality')); console.log(chalk.gray(' chore(a11y): add aria labels to form inputs')); console.log(chalk.gray(' fix(dns.policies): resolve custom rule validation')); console.log(chalk.gray('\nšŸ“– Use "ai-commit-toolkit scopes --category <name>" for specific categories')); } } catch (error) { console.log(chalk.red('āŒ Error reading configuration:', error.message)); process.exit(1); } }); program .command('test') .description('Test the AI commit setup') .option('--create-test-file', 'Create test files for full integration test') .action(async (options) => { const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); // Check if we're in a project with AI commit setup const testScriptPath = path.join(process.cwd(), 'scripts', 'test-ai-setup.sh'); if (!fs.existsSync(testScriptPath)) { console.log(chalk.red('āŒ AI commit setup not found in current directory')); console.log(chalk.yellow('šŸ’” Run "ai-commit-toolkit setup" first')); process.exit(1); } const testScript = options.createTestFile ? `bash ${testScriptPath} --create-test-file` : `bash ${testScriptPath}`; try { execSync(testScript, { stdio: 'inherit', cwd: process.cwd() }); } catch (error) { console.error(chalk.red('āŒ Test failed:'), error.message || error); console.error(chalk.gray('Exit code:'), error.status || 'unknown'); process.exit(1); } }); if (require.main === module) { program.parse(); } // Export for programmatic use module.exports = AICommitToolkitSetup;