UNPKG

ccoptimizer

Version:

šŸš€ Optimize your Claude Code experience by analyzing your conversation patterns

120 lines • 5.76 kB
#!/usr/bin/env node import chalk from 'chalk'; import ora from 'ora'; import { analyzeConversations } from './analyzer.js'; import { generateClaudeMd } from './generator.js'; import { homedir } from 'os'; import { existsSync } from 'fs'; import { join } from 'path'; async function main() { console.log(chalk.cyan.bold('\nšŸš€ CC Optimizer - Personalize your Claude experience\n')); // Handle graceful shutdown process.on('SIGINT', () => { console.log(chalk.yellow('\n\nāš ļø Analysis cancelled by user')); process.exit(0); }); // Quick check for Claude CLI try { const { execSync } = await import('child_process'); execSync('which claude', { stdio: 'ignore' }); } catch { console.log(chalk.yellow('āš ļø Claude CLI not found. Please install it first:')); console.log(chalk.gray(' npm install -g @anthropic-ai/claude-code\n')); process.exit(1); } const spinner = ora('Checking Claude projects directory...').start(); try { const projectsPath = join(homedir(), '.claude', 'projects'); if (!existsSync(projectsPath)) { spinner.fail('Claude projects directory not found'); console.log(chalk.yellow('Make sure you have used Claude Code at least once')); process.exit(1); } spinner.text = 'Scanning conversation history...'; const startTime = Date.now(); const analysis = await analyzeConversations(projectsPath, spinner); const duration = Math.round((Date.now() - startTime) / 1000); spinner.succeed(`Analyzed ${chalk.green(analysis.conversationCount)} conversations in ${chalk.cyan(duration + 's')}`); if (analysis.insights && analysis.insights.length > 0) { console.log(chalk.blue('\nšŸ“Š Insights gathered from your conversations')); } const outputSpinner = ora('\n🧠 Generating optimized CLAUDE.md...').start(); const claudeMdPath = join(homedir(), '.claude', 'CLAUDE.md'); const newContent = await generateClaudeMd(analysis.insights); // Smart merge with existing CLAUDE.md const { readFileSync, writeFileSync, copyFileSync } = await import('fs'); let finalContent = newContent; if (existsSync(claudeMdPath)) { const backupPath = claudeMdPath + '.backup'; copyFileSync(claudeMdPath, backupPath); const existingContent = readFileSync(claudeMdPath, 'utf8'); const marker = '## Auto-Generated by ccoptimizer'; if (existingContent.includes(marker)) { // Replace only the auto-generated section const markerIndex = existingContent.indexOf(marker); const beforeMarker = existingContent.substring(0, markerIndex).trimEnd(); finalContent = beforeMarker + '\n\n' + marker + '\n\n' + newContent.replace('# Optimized Claude Instructions\n\n', ''); } else { // Append as new section finalContent = existingContent.trimEnd() + '\n\n' + marker + '\n\n' + newContent.replace('# Optimized Claude Instructions\n\n', ''); } } outputSpinner.stop(); if (existsSync(claudeMdPath)) { console.log(chalk.gray('āœ“ Backed up existing CLAUDE.md')); } // Show preview console.log(chalk.blue('\nšŸ“ Preview of changes:\n')); console.log(chalk.gray('─'.repeat(50))); // Show just the new/updated section const markerIndex = finalContent.indexOf('## Auto-Generated by ccoptimizer'); if (markerIndex !== -1) { const preview = finalContent.substring(markerIndex); console.log(preview.substring(0, 500) + (preview.length > 500 ? '\n...' : '')); } else { console.log(finalContent.substring(0, 500) + (finalContent.length > 500 ? '\n...' : '')); } console.log(chalk.gray('─'.repeat(50))); // Check for --yes flag to skip prompt let shouldApply = process.argv.includes('--yes') || process.argv.includes('-y'); if (!shouldApply) { // Ask for confirmation const { createInterface } = await import('readline'); const rl = createInterface({ input: process.stdin, output: process.stdout }); const answer = await new Promise(resolve => { rl.question(chalk.yellow('\nāœ” Apply these changes to CLAUDE.md? [Y/n]: '), (ans) => { rl.close(); const response = ans.toLowerCase().trim(); // Default to yes if user just presses enter resolve(response === '' || response === 'y' || response === 'yes'); }); }); shouldApply = answer; } if (shouldApply) { writeFileSync(claudeMdPath, finalContent); console.log(chalk.green(`āœ“ Updated ${claudeMdPath}`)); console.log(chalk.green.bold('\n✨ Your personalized instructions are ready!\n')); } else { console.log(chalk.yellow('\nāŒ Changes discarded.\n')); } } catch (error) { spinner.fail('An error occurred'); console.error(chalk.red('\nError details:'), error instanceof Error ? error.message : String(error)); if (error instanceof Error && error.message.includes('claude')) { console.log(chalk.yellow('\nšŸ’” Tip: Make sure the Claude CLI is installed:')); console.log(chalk.gray(' npm install -g @anthropic-ai/claude-code')); } process.exit(1); } } main(); //# sourceMappingURL=index.js.map