@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
JavaScript
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;