devcompass
Advanced tools
| // src/ai/context-builder.js | ||
| const fs = require('fs'); | ||
| const path = require('path'); | ||
| /** | ||
| * Build comprehensive context from analysis results for AI | ||
| */ | ||
| class ContextBuilder { | ||
| /** | ||
| * Build context for analyze command | ||
| */ | ||
| buildAnalysisContext(analysisResults) { | ||
| const sections = []; | ||
| // Project Overview | ||
| sections.push(this._buildProjectOverview(analysisResults)); | ||
| // Health Score Breakdown | ||
| sections.push(this._buildHealthScoreBreakdown(analysisResults)); | ||
| // Security Issues | ||
| if (analysisResults.vulnerabilities && analysisResults.vulnerabilities.length > 0) { | ||
| sections.push(this._buildSecuritySection(analysisResults.vulnerabilities)); | ||
| } | ||
| // Outdated Packages | ||
| if (analysisResults.outdated && analysisResults.outdated.length > 0) { | ||
| sections.push(this._buildOutdatedSection(analysisResults.outdated)); | ||
| } | ||
| // Deprecated Packages | ||
| if (analysisResults.deprecated && analysisResults.deprecated.length > 0) { | ||
| sections.push(this._buildDeprecatedSection(analysisResults.deprecated)); | ||
| } | ||
| // Unused Dependencies | ||
| if (analysisResults.unused && analysisResults.unused.length > 0) { | ||
| sections.push(this._buildUnusedSection(analysisResults.unused)); | ||
| } | ||
| // Supply Chain Risks | ||
| if (analysisResults.supplyChain && analysisResults.supplyChain.length > 0) { | ||
| sections.push(this._buildSupplyChainSection(analysisResults.supplyChain)); | ||
| } | ||
| // License Issues | ||
| if (analysisResults.licenseIssues && analysisResults.licenseIssues.length > 0) { | ||
| sections.push(this._buildLicenseSection(analysisResults.licenseIssues)); | ||
| } | ||
| // Bundle Size Analysis | ||
| if (analysisResults.heavyPackages && analysisResults.heavyPackages.length > 0) { | ||
| sections.push(this._buildBundleSizeSection(analysisResults.heavyPackages)); | ||
| } | ||
| return sections.join('\n\n'); | ||
| } | ||
| /** | ||
| * Build project overview | ||
| */ | ||
| _buildProjectOverview(results) { | ||
| return `PROJECT OVERVIEW: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| Name: ${results.projectName || 'Unknown'} | ||
| Version: ${results.projectVersion || 'Unknown'} | ||
| Path: ${results.projectPath || process.cwd()} | ||
| Total Dependencies: ${results.totalDependencies || 0} | ||
| Health Score: ${results.healthScore || 0}/10 | ||
| Analyzed: ${new Date().toISOString()}`; | ||
| } | ||
| /** | ||
| * Build health score breakdown | ||
| */ | ||
| _buildHealthScoreBreakdown(results) { | ||
| const factors = []; | ||
| if (results.vulnerabilities?.length > 0) { | ||
| factors.push(`- Security Vulnerabilities: ${results.vulnerabilities.length} (impact: -${results.vulnerabilities.length * 0.5} points)`); | ||
| } | ||
| if (results.outdated?.length > 0) { | ||
| factors.push(`- Outdated Packages: ${results.outdated.length} (impact: -${Math.min(results.outdated.length * 0.2, 2)} points)`); | ||
| } | ||
| if (results.deprecated?.length > 0) { | ||
| factors.push(`- Deprecated Packages: ${results.deprecated.length} (impact: -${results.deprecated.length * 0.5} points)`); | ||
| } | ||
| if (results.unused?.length > 0) { | ||
| factors.push(`- Unused Dependencies: ${results.unused.length} (impact: -${Math.min(results.unused.length * 0.1, 1)} points)`); | ||
| } | ||
| if (results.supplyChain?.length > 0) { | ||
| factors.push(`- Supply Chain Risks: ${results.supplyChain.length} (impact: -${results.supplyChain.length * 0.3} points)`); | ||
| } | ||
| return `HEALTH SCORE BREAKDOWN: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| Current Score: ${results.healthScore || 0}/10 | ||
| Factors affecting score: | ||
| ${factors.length > 0 ? factors.join('\n') : '- No issues detected (excellent! โ )'}`; | ||
| } | ||
| /** | ||
| * Build security section | ||
| */ | ||
| _buildSecuritySection(vulnerabilities) { | ||
| const critical = vulnerabilities.filter(v => v.severity === 'critical'); | ||
| const high = vulnerabilities.filter(v => v.severity === 'high'); | ||
| const medium = vulnerabilities.filter(v => v.severity === 'medium'); | ||
| const low = vulnerabilities.filter(v => v.severity === 'low'); | ||
| const vulnList = vulnerabilities.slice(0, 10).map(v => | ||
| ` - ${v.package} (${v.severity.toUpperCase()}): ${v.via?.[0] || 'Unknown vulnerability'}` | ||
| ).join('\n'); | ||
| return `SECURITY VULNERABILITIES: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| Total: ${vulnerabilities.length} | ||
| Critical: ${critical.length} | ||
| High: ${high.length} | ||
| Medium: ${medium.length} | ||
| Low: ${low.length} | ||
| Top Vulnerabilities: | ||
| ${vulnList} | ||
| ${vulnerabilities.length > 10 ? `\n... and ${vulnerabilities.length - 10} more` : ''}`; | ||
| } | ||
| /** | ||
| * Build outdated packages section | ||
| */ | ||
| _buildOutdatedSection(outdated) { | ||
| const major = outdated.filter(p => p.updateType === 'major'); | ||
| const minor = outdated.filter(p => p.updateType === 'minor'); | ||
| const patch = outdated.filter(p => p.updateType === 'patch'); | ||
| const packageList = outdated.slice(0, 10).map(p => | ||
| ` - ${p.name}: ${p.current} โ ${p.latest} (${p.updateType} update)` | ||
| ).join('\n'); | ||
| return `OUTDATED PACKAGES: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| Total: ${outdated.length} | ||
| Major updates: ${major.length} | ||
| Minor updates: ${minor.length} | ||
| Patch updates: ${patch.length} | ||
| Top Outdated Packages: | ||
| ${packageList} | ||
| ${outdated.length > 10 ? `\n... and ${outdated.length - 10} more` : ''}`; | ||
| } | ||
| /** | ||
| * Build deprecated packages section | ||
| */ | ||
| _buildDeprecatedSection(deprecated) { | ||
| const packageList = deprecated.map(p => | ||
| ` - ${p.name}@${p.version}${p.alternative ? ` โ Recommended: ${p.alternative}` : ''}` | ||
| ).join('\n'); | ||
| return `DEPRECATED PACKAGES: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| Total: ${deprecated.length} | ||
| Packages: | ||
| ${packageList}`; | ||
| } | ||
| /** | ||
| * Build unused dependencies section | ||
| */ | ||
| _buildUnusedSection(unused) { | ||
| const packageList = unused.slice(0, 15).map(p => ` - ${p}`).join('\n'); | ||
| return `UNUSED DEPENDENCIES: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| Total: ${unused.length} | ||
| Packages: | ||
| ${packageList} | ||
| ${unused.length > 15 ? `\n... and ${unused.length - 15} more` : ''} | ||
| These packages are installed but never imported/required in your code.`; | ||
| } | ||
| /** | ||
| * Build supply chain section | ||
| */ | ||
| _buildSupplyChainSection(supplyChain) { | ||
| const typosquatting = supplyChain.filter(r => r.type === 'typosquat'); | ||
| const suspicious = supplyChain.filter(r => r.type === 'suspicious'); | ||
| const riskList = supplyChain.slice(0, 10).map(r => | ||
| ` - ${r.package}${r.similarTo ? ` (similar to: ${r.similarTo})` : ''}: ${r.warning}` | ||
| ).join('\n'); | ||
| return `SUPPLY CHAIN RISKS: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| Total: ${supplyChain.length} | ||
| Typosquatting risks: ${typosquatting.length} | ||
| Suspicious packages: ${suspicious.length} | ||
| Detected Risks: | ||
| ${riskList} | ||
| ${supplyChain.length > 10 ? `\n... and ${supplyChain.length - 10} more` : ''}`; | ||
| } | ||
| /** | ||
| * Build license section | ||
| */ | ||
| _buildLicenseSection(licenseIssues) { | ||
| const issueList = licenseIssues.map(l => | ||
| ` - ${l.package}: ${l.license} (${l.risk || 'Unknown risk'})` | ||
| ).join('\n'); | ||
| return `LICENSE ISSUES: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| Total: ${licenseIssues.length} | ||
| Issues: | ||
| ${issueList}`; | ||
| } | ||
| /** | ||
| * Build bundle size section | ||
| */ | ||
| _buildBundleSizeSection(heavyPackages) { | ||
| const packageList = heavyPackages.map(p => | ||
| ` - ${p.name}: ${this._formatBytes(p.size)}` | ||
| ).join('\n'); | ||
| const totalSize = heavyPackages.reduce((sum, p) => sum + p.size, 0); | ||
| return `HEAVY PACKAGES (>1MB): | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| Total: ${heavyPackages.length} | ||
| Combined size: ${this._formatBytes(totalSize)} | ||
| Packages: | ||
| ${packageList}`; | ||
| } | ||
| /** | ||
| * Build context from snapshot comparison | ||
| */ | ||
| buildComparisonContext(comparison) { | ||
| const sections = []; | ||
| // Overview | ||
| sections.push(`SNAPSHOT COMPARISON: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| From: Snapshot #${comparison.snapshot1.id} (${new Date(comparison.snapshot1.timestamp).toLocaleString()}) | ||
| To: Snapshot #${comparison.snapshot2.id} (${new Date(comparison.snapshot2.timestamp).toLocaleString()}) | ||
| Time difference: ${this._calculateTimeDiff(comparison.snapshot1.timestamp, comparison.snapshot2.timestamp)}`); | ||
| // Health Score Change | ||
| const healthDelta = comparison.snapshot2.healthScore - comparison.snapshot1.healthScore; | ||
| const healthEmoji = healthDelta > 0 ? '๐' : healthDelta < 0 ? '๐' : 'โก๏ธ'; | ||
| sections.push(`HEALTH SCORE CHANGE: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| ${healthEmoji} ${comparison.snapshot1.healthScore}/10 โ ${comparison.snapshot2.healthScore}/10 (${healthDelta > 0 ? '+' : ''}${healthDelta.toFixed(1)})`); | ||
| // Changes Summary | ||
| sections.push(`CHANGES SUMMARY: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| Added Packages: ${comparison.added?.length || 0} | ||
| Removed Packages: ${comparison.removed?.length || 0} | ||
| Updated Packages: ${comparison.updated?.length || 0} | ||
| Unchanged Packages: ${comparison.unchanged?.length || 0}`); | ||
| // Added Packages | ||
| if (comparison.added?.length > 0) { | ||
| const addedList = comparison.added.slice(0, 10).map(p => | ||
| ` + ${p.name}@${p.version}` | ||
| ).join('\n'); | ||
| sections.push(`ADDED PACKAGES: | ||
| ${addedList} | ||
| ${comparison.added.length > 10 ? `... and ${comparison.added.length - 10} more` : ''}`); | ||
| } | ||
| // Removed Packages | ||
| if (comparison.removed?.length > 0) { | ||
| const removedList = comparison.removed.slice(0, 10).map(p => | ||
| ` - ${p.name}@${p.version}` | ||
| ).join('\n'); | ||
| sections.push(`REMOVED PACKAGES: | ||
| ${removedList} | ||
| ${comparison.removed.length > 10 ? `... and ${comparison.removed.length - 10} more` : ''}`); | ||
| } | ||
| // Updated Packages | ||
| if (comparison.updated?.length > 0) { | ||
| const updatedList = comparison.updated.slice(0, 10).map(p => | ||
| ` โณ ${p.name}: ${p.oldVersion} โ ${p.newVersion}${p.healthChange ? ` (health: ${p.healthChange > 0 ? '+' : ''}${p.healthChange})` : ''}` | ||
| ).join('\n'); | ||
| sections.push(`UPDATED PACKAGES: | ||
| ${updatedList} | ||
| ${comparison.updated.length > 10 ? `... and ${comparison.updated.length - 10} more` : ''}`); | ||
| } | ||
| return sections.join('\n\n'); | ||
| } | ||
| /** | ||
| * Build context from timeline data | ||
| */ | ||
| buildTimelineContext(timeline) { | ||
| const sections = []; | ||
| // Overview | ||
| sections.push(`TIMELINE ANALYSIS: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| Period: ${timeline.period || 'All time'} | ||
| Total Snapshots: ${timeline.snapshots?.length || 0} | ||
| Date Range: ${timeline.startDate ? new Date(timeline.startDate).toLocaleDateString() : 'N/A'} to ${timeline.endDate ? new Date(timeline.endDate).toLocaleDateString() : 'N/A'}`); | ||
| // Trend Analysis | ||
| if (timeline.trend) { | ||
| const trendEmoji = timeline.trend.direction === 'improving' ? '๐' : | ||
| timeline.trend.direction === 'declining' ? '๐' : 'โก๏ธ'; | ||
| sections.push(`TREND ANALYSIS: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| ${trendEmoji} Overall Trend: ${timeline.trend.direction.toUpperCase()} | ||
| Change: ${timeline.trend.percentage > 0 ? '+' : ''}${timeline.trend.percentage.toFixed(1)}% | ||
| Average Health: ${timeline.averageHealth?.toFixed(1) || 'N/A'}/10`); | ||
| } | ||
| // Dependency Growth | ||
| if (timeline.dependencyGrowth !== undefined) { | ||
| sections.push(`DEPENDENCY CHANGES: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| Total Growth: ${timeline.dependencyGrowth > 0 ? '+' : ''}${timeline.dependencyGrowth} packages | ||
| Starting Count: ${timeline.startingDependencies || 'N/A'} | ||
| Current Count: ${timeline.currentDependencies || 'N/A'}`); | ||
| } | ||
| // Snapshots Summary (first, middle, last) | ||
| if (timeline.snapshots?.length >= 3) { | ||
| const first = timeline.snapshots[0]; | ||
| const middle = timeline.snapshots[Math.floor(timeline.snapshots.length / 2)]; | ||
| const last = timeline.snapshots[timeline.snapshots.length - 1]; | ||
| sections.push(`KEY SNAPSHOTS: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| First (#${first.id}): ${new Date(first.timestamp).toLocaleDateString()} - Health: ${first.health_score}/10, Deps: ${first.total_dependencies} | ||
| Middle (#${middle.id}): ${new Date(middle.timestamp).toLocaleDateString()} - Health: ${middle.health_score}/10, Deps: ${middle.total_dependencies} | ||
| Latest (#${last.id}): ${new Date(last.timestamp).toLocaleDateString()} - Health: ${last.health_score}/10, Deps: ${last.total_dependencies}`); | ||
| } | ||
| return sections.join('\n\n'); | ||
| } | ||
| /** | ||
| * Build context from package.json | ||
| */ | ||
| buildPackageJsonContext(projectPath) { | ||
| try { | ||
| const packageJsonPath = path.join(projectPath, 'package.json'); | ||
| if (!fs.existsSync(packageJsonPath)) { | ||
| return 'No package.json found in project'; | ||
| } | ||
| const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); | ||
| const sections = []; | ||
| sections.push(`PACKAGE.JSON INFO: | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| Name: ${packageJson.name || 'Unknown'} | ||
| Version: ${packageJson.version || 'Unknown'} | ||
| Description: ${packageJson.description || 'No description'} | ||
| License: ${packageJson.license || 'Unknown'}`); | ||
| // Scripts | ||
| if (packageJson.scripts) { | ||
| const scriptList = Object.keys(packageJson.scripts).slice(0, 5).join(', '); | ||
| sections.push(`Available Scripts: ${scriptList}${Object.keys(packageJson.scripts).length > 5 ? '...' : ''}`); | ||
| } | ||
| // Dependencies count | ||
| const depsCount = Object.keys(packageJson.dependencies || {}).length; | ||
| const devDepsCount = Object.keys(packageJson.devDependencies || {}).length; | ||
| sections.push(`Dependencies: ${depsCount} production, ${devDepsCount} development`); | ||
| return sections.join('\n'); | ||
| } catch (error) { | ||
| return `Error reading package.json: ${error.message}`; | ||
| } | ||
| } | ||
| /** | ||
| * Build minimal context (for token efficiency) | ||
| */ | ||
| buildMinimalContext(analysisResults) { | ||
| return `Project: ${analysisResults.projectName || 'Unknown'} | ||
| Health: ${analysisResults.healthScore || 0}/10 | ||
| Dependencies: ${analysisResults.totalDependencies || 0} | ||
| Issues: ${(analysisResults.vulnerabilities?.length || 0) + (analysisResults.outdated?.length || 0) + (analysisResults.deprecated?.length || 0)}`; | ||
| } | ||
| /** | ||
| * Helper: Format bytes to human-readable | ||
| */ | ||
| _formatBytes(bytes) { | ||
| if (bytes === 0) return '0 Bytes'; | ||
| const k = 1024; | ||
| const sizes = ['Bytes', 'KB', 'MB', 'GB']; | ||
| const i = Math.floor(Math.log(bytes) / Math.log(k)); | ||
| return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; | ||
| } | ||
| /** | ||
| * Helper: Calculate time difference | ||
| */ | ||
| _calculateTimeDiff(date1, date2) { | ||
| const diff = new Date(date2) - new Date(date1); | ||
| const days = Math.floor(diff / (1000 * 60 * 60 * 24)); | ||
| const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); | ||
| if (days > 0) { | ||
| return `${days} day${days > 1 ? 's' : ''} ${hours} hour${hours !== 1 ? 's' : ''}`; | ||
| } else if (hours > 0) { | ||
| return `${hours} hour${hours !== 1 ? 's' : ''}`; | ||
| } else { | ||
| const minutes = Math.floor(diff / (1000 * 60)); | ||
| return `${minutes} minute${minutes !== 1 ? 's' : ''}`; | ||
| } | ||
| } | ||
| } | ||
| module.exports = new ContextBuilder(); |
| // src/ai/conversation.js | ||
| const { v4: uuidv4 } = require('uuid'); | ||
| const aiDatabase = require('./database'); | ||
| class ConversationManager { | ||
| constructor() { | ||
| this.currentSession = null; | ||
| } | ||
| /** | ||
| * Start a new conversation session | ||
| */ | ||
| startSession() { | ||
| this.currentSession = uuidv4(); | ||
| return this.currentSession; | ||
| } | ||
| /** | ||
| * Get current session ID | ||
| */ | ||
| getCurrentSession() { | ||
| if (!this.currentSession) { | ||
| this.startSession(); | ||
| } | ||
| return this.currentSession; | ||
| } | ||
| /** | ||
| * Save conversation turn | ||
| */ | ||
| saveConversation(providerId, command, commandOutput, userPrompt, aiResponse, tokensUsed, cost) { | ||
| const sessionId = this.getCurrentSession(); | ||
| aiDatabase.saveConversation( | ||
| sessionId, | ||
| providerId, | ||
| command, | ||
| commandOutput, | ||
| userPrompt, | ||
| aiResponse, | ||
| tokensUsed, | ||
| cost | ||
| ); | ||
| } | ||
| /** | ||
| * Get conversation history | ||
| */ | ||
| getHistory(limit = 10) { | ||
| const sessionId = this.getCurrentSession(); | ||
| return aiDatabase.getConversationHistory(sessionId, limit); | ||
| } | ||
| /** | ||
| * Build conversation context for AI | ||
| */ | ||
| buildContext(limit = 5) { | ||
| const history = this.getHistory(limit); | ||
| if (history.length === 0) { | ||
| return null; | ||
| } | ||
| // Reverse to get chronological order | ||
| return history.reverse().map(turn => ({ | ||
| user: turn.user_prompt, | ||
| assistant: turn.ai_response | ||
| })); | ||
| } | ||
| /** | ||
| * Clear current session | ||
| */ | ||
| clearSession() { | ||
| this.currentSession = null; | ||
| } | ||
| } | ||
| module.exports = new ConversationManager(); |
| // src/ai/cost-tracker.js | ||
| const aiDatabase = require('./database'); | ||
| class CostTracker { | ||
| /** | ||
| * Track usage for a provider | ||
| */ | ||
| trackUsage(providerId, tokens, cost) { | ||
| aiDatabase.trackUsage(providerId, tokens, cost); | ||
| } | ||
| /** | ||
| * Get usage stats for current month | ||
| */ | ||
| getCurrentMonthStats() { | ||
| const now = new Date(); | ||
| return aiDatabase.getUsageStats(now.getFullYear(), now.getMonth() + 1); | ||
| } | ||
| /** | ||
| * Get usage stats for a specific month | ||
| */ | ||
| getMonthStats(year, month) { | ||
| return aiDatabase.getUsageStats(year, month); | ||
| } | ||
| /** | ||
| * Get total cost for current month | ||
| */ | ||
| getTotalCostThisMonth() { | ||
| const stats = this.getCurrentMonthStats(); | ||
| return stats.reduce((sum, s) => sum + s.total_cost, 0); | ||
| } | ||
| /** | ||
| * Get total tokens for current month | ||
| */ | ||
| getTotalTokensThisMonth() { | ||
| const stats = this.getCurrentMonthStats(); | ||
| return stats.reduce((sum, s) => sum + s.total_tokens, 0); | ||
| } | ||
| /** | ||
| * Format stats for display | ||
| */ | ||
| formatStats(stats, providers) { | ||
| return stats.map(s => { | ||
| const provider = providers.find(p => p.id === s.provider_id); | ||
| return { | ||
| provider: provider?.provider || 'Unknown', | ||
| model: provider?.model || 'Unknown', | ||
| requests: s.request_count, | ||
| tokens: s.total_tokens, | ||
| cost: s.total_cost.toFixed(4) | ||
| }; | ||
| }); | ||
| } | ||
| } | ||
| module.exports = new CostTracker(); |
| // src/ai/database.js | ||
| const Database = require('better-sqlite3'); | ||
| const path = require('path'); | ||
| const fs = require('fs'); | ||
| const os = require('os'); | ||
| const DB_DIR = path.join(os.homedir(), '.devcompass'); | ||
| const DB_PATH = path.join(DB_DIR, 'ai.db'); | ||
| class AIDatabase { | ||
| constructor() { | ||
| // Ensure directory exists | ||
| if (!fs.existsSync(DB_DIR)) { | ||
| fs.mkdirSync(DB_DIR, { recursive: true }); | ||
| } | ||
| this.db = new Database(DB_PATH); | ||
| this.db.pragma('journal_mode = WAL'); | ||
| this.initSchema(); | ||
| } | ||
| initSchema() { | ||
| // LLM providers table | ||
| this.db.exec(` | ||
| CREATE TABLE IF NOT EXISTS llm_providers ( | ||
| id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
| provider TEXT NOT NULL, | ||
| api_key TEXT, | ||
| base_url TEXT, | ||
| model TEXT NOT NULL, | ||
| is_default BOOLEAN DEFAULT 0, | ||
| is_active BOOLEAN DEFAULT 1, | ||
| max_tokens INTEGER DEFAULT 4096, | ||
| temperature REAL DEFAULT 0.7, | ||
| created_at DATETIME DEFAULT CURRENT_TIMESTAMP, | ||
| updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, | ||
| UNIQUE(provider) | ||
| ); | ||
| `); | ||
| // Conversations table | ||
| this.db.exec(` | ||
| CREATE TABLE IF NOT EXISTS ai_conversations ( | ||
| id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
| session_id TEXT NOT NULL, | ||
| provider_id INTEGER, | ||
| command TEXT NOT NULL, | ||
| command_output TEXT, | ||
| user_prompt TEXT, | ||
| ai_response TEXT, | ||
| tokens_used INTEGER DEFAULT 0, | ||
| cost REAL DEFAULT 0.0, | ||
| timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, | ||
| FOREIGN KEY (provider_id) REFERENCES llm_providers(id) ON DELETE SET NULL | ||
| ); | ||
| `); | ||
| // Cost tracking table | ||
| this.db.exec(` | ||
| CREATE TABLE IF NOT EXISTS ai_usage ( | ||
| id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
| provider_id INTEGER, | ||
| year INTEGER NOT NULL, | ||
| month INTEGER NOT NULL, | ||
| total_tokens INTEGER DEFAULT 0, | ||
| total_cost REAL DEFAULT 0.0, | ||
| request_count INTEGER DEFAULT 0, | ||
| updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, | ||
| FOREIGN KEY (provider_id) REFERENCES llm_providers(id) ON DELETE CASCADE, | ||
| UNIQUE(provider_id, year, month) | ||
| ); | ||
| `); | ||
| // Indexes | ||
| this.db.exec(` | ||
| CREATE INDEX IF NOT EXISTS idx_conversations_session ON ai_conversations(session_id); | ||
| CREATE INDEX IF NOT EXISTS idx_conversations_timestamp ON ai_conversations(timestamp DESC); | ||
| CREATE INDEX IF NOT EXISTS idx_usage_date ON ai_usage(year, month); | ||
| `); | ||
| } | ||
| // Provider methods | ||
| addProvider(provider, apiKey, model, baseUrl = null) { | ||
| const stmt = this.db.prepare(` | ||
| INSERT INTO llm_providers (provider, api_key, model, base_url) | ||
| VALUES (?, ?, ?, ?) | ||
| `); | ||
| return stmt.run(provider, apiKey, model, baseUrl); | ||
| } | ||
| getProvider(provider) { | ||
| return this.db.prepare('SELECT * FROM llm_providers WHERE provider = ?').get(provider); | ||
| } | ||
| getDefaultProvider() { | ||
| return this.db.prepare('SELECT * FROM llm_providers WHERE is_default = 1 LIMIT 1').get(); | ||
| } | ||
| getAllProviders() { | ||
| return this.db.prepare('SELECT * FROM llm_providers ORDER BY is_default DESC, provider ASC').all(); | ||
| } | ||
| setDefaultProvider(provider) { | ||
| this.db.exec('UPDATE llm_providers SET is_default = 0'); | ||
| this.db.prepare('UPDATE llm_providers SET is_default = 1 WHERE provider = ?').run(provider); | ||
| } | ||
| updateProvider(provider, updates) { | ||
| const fields = Object.keys(updates).map(k => `${k} = ?`).join(', '); | ||
| const values = [...Object.values(updates), provider]; | ||
| this.db.prepare(`UPDATE llm_providers SET ${fields}, updated_at = CURRENT_TIMESTAMP WHERE provider = ?`).run(...values); | ||
| } | ||
| removeProvider(provider) { | ||
| this.db.prepare('DELETE FROM llm_providers WHERE provider = ?').run(provider); | ||
| } | ||
| // Conversation methods | ||
| saveConversation(sessionId, providerId, command, commandOutput, userPrompt, aiResponse, tokensUsed, cost) { | ||
| const stmt = this.db.prepare(` | ||
| INSERT INTO ai_conversations (session_id, provider_id, command, command_output, user_prompt, ai_response, tokens_used, cost) | ||
| VALUES (?, ?, ?, ?, ?, ?, ?, ?) | ||
| `); | ||
| return stmt.run(sessionId, providerId, command, commandOutput, userPrompt, aiResponse, tokensUsed, cost); | ||
| } | ||
| getConversationHistory(sessionId, limit = 10) { | ||
| return this.db.prepare(` | ||
| SELECT * FROM ai_conversations | ||
| WHERE session_id = ? | ||
| ORDER BY timestamp DESC | ||
| LIMIT ? | ||
| `).all(sessionId, limit); | ||
| } | ||
| // Usage tracking | ||
| trackUsage(providerId, tokens, cost) { | ||
| const now = new Date(); | ||
| const year = now.getFullYear(); | ||
| const month = now.getMonth() + 1; | ||
| this.db.prepare(` | ||
| INSERT INTO ai_usage (provider_id, year, month, total_tokens, total_cost, request_count) | ||
| VALUES (?, ?, ?, ?, ?, 1) | ||
| ON CONFLICT(provider_id, year, month) DO UPDATE SET | ||
| total_tokens = total_tokens + ?, | ||
| total_cost = total_cost + ?, | ||
| request_count = request_count + 1, | ||
| updated_at = CURRENT_TIMESTAMP | ||
| `).run(providerId, year, month, tokens, cost, tokens, cost); | ||
| } | ||
| getUsageStats(year, month) { | ||
| const query = month | ||
| ? 'SELECT * FROM ai_usage WHERE year = ? AND month = ?' | ||
| : 'SELECT * FROM ai_usage WHERE year = ?'; | ||
| const params = month ? [year, month] : [year]; | ||
| return this.db.prepare(query).all(...params); | ||
| } | ||
| close() { | ||
| this.db.close(); | ||
| } | ||
| } | ||
| module.exports = new AIDatabase(); |
| // src/ai/prompt-templates.js | ||
| const SYSTEM_PROMPTS = { | ||
| /** | ||
| * System prompt for analyze command | ||
| */ | ||
| analyze: `You are DevCompass AI. Give SHORT, actionable insights. | ||
| Rules: | ||
| - Maximum 3-4 sentences per issue | ||
| - Focus ONLY on critical items | ||
| - Provide specific commands | ||
| - No long explanations | ||
| Be direct and concise.`, | ||
| /** | ||
| * System prompt for recommendations | ||
| */ | ||
| recommend: `You are DevCompass AI. Provide a SHORT prioritized list. | ||
| FORMAT (keep it brief): | ||
| ๐ด CRITICAL (1-2 items max): | ||
| - [Issue name]: [One sentence why] โ [Command] | ||
| ๐ก HIGH (1-2 items max): | ||
| - [Issue name]: [One sentence why] โ [Command] | ||
| ๐ข MEDIUM (1-2 items max): | ||
| - [Issue name]: [One sentence why] โ [Command] | ||
| Keep EACH item to ONE line. No long paragraphs.`, | ||
| /** | ||
| * System prompt for alternatives | ||
| */ | ||
| alternatives: `You are DevCompass AI. List exactly 3 alternatives. Be BRIEF. | ||
| Format: | ||
| 1. **[Package]** (~[size]KB): [One sentence] | ||
| 2. **[Package]** (~[size]KB): [One sentence] | ||
| 3. **[Package]** (~[size]KB): [One sentence] | ||
| Recommendation: Use [package name] | ||
| Migration (5 lines max): | ||
| \`\`\`javascript | ||
| // Brief example | ||
| \`\`\` | ||
| NO long explanations. Keep it SHORT.`, | ||
| /** | ||
| * System prompt for Q&A | ||
| */ | ||
| qa: `You are DevCompass AI. Answer in 2-4 sentences MAX. | ||
| Rules: | ||
| - Be direct and specific | ||
| - Use data from the project analysis | ||
| - Provide ONE command if applicable | ||
| - NO long explanations | ||
| Keep responses SHORT and actionable.`, | ||
| /** | ||
| * System prompt for chat | ||
| */ | ||
| chat: `You are DevCompass AI. Keep responses SHORT (2-4 sentences). | ||
| Rules: | ||
| - Answer directly, no fluff | ||
| - Use project data when available | ||
| - Provide specific commands | ||
| - Be conversational but concise | ||
| Maximum 4 sentences per response.` | ||
| }; | ||
| /** | ||
| * Build context from analysis results - OPTIMIZED | ||
| */ | ||
| function buildAnalysisContext(analysisData) { | ||
| // Handle both direct results and wrapped data | ||
| const results = analysisData.results || analysisData; | ||
| const metadata = analysisData.metadata || {}; | ||
| // Safety check | ||
| if (!results) { | ||
| return 'No analysis data available.'; | ||
| } | ||
| const context = { | ||
| projectName: metadata.projectName || results.projectName || 'Unknown', | ||
| healthScore: results.healthScore || 0, | ||
| totalDependencies: results.dependencies?.length || results.totalDependencies || 0, | ||
| dependencies: results.dependencies || [], | ||
| vulnerabilities: results.vulnerabilities || [], | ||
| outdated: results.outdated || [], | ||
| unused: results.unused || [], | ||
| ecosystemAlerts: results.ecosystemAlerts || [] | ||
| }; | ||
| const parts = []; | ||
| parts.push(`Project: ${context.projectName}`); | ||
| parts.push(`Health: ${context.healthScore.toFixed(1)}/10`); | ||
| parts.push(`Dependencies: ${context.totalDependencies}\n`); | ||
| // Security vulnerabilities (brief) | ||
| if (context.vulnerabilities.length > 0) { | ||
| const bySeverity = { | ||
| critical: context.vulnerabilities.filter(v => v.severity === 'critical').length, | ||
| high: context.vulnerabilities.filter(v => v.severity === 'high').length, | ||
| moderate: context.vulnerabilities.filter(v => v.severity === 'moderate').length, | ||
| low: context.vulnerabilities.filter(v => v.severity === 'low').length | ||
| }; | ||
| parts.push(`VULNERABILITIES: ${context.vulnerabilities.length} total`); | ||
| if (bySeverity.critical) parts.push(` Critical: ${bySeverity.critical}`); | ||
| if (bySeverity.high) parts.push(` High: ${bySeverity.high}`); | ||
| if (bySeverity.moderate) parts.push(` Moderate: ${bySeverity.moderate}`); | ||
| if (bySeverity.low) parts.push(` Low: ${bySeverity.low}`); | ||
| parts.push(''); | ||
| } | ||
| // Outdated packages (top 5 only) | ||
| if (context.outdated.length > 0) { | ||
| parts.push(`OUTDATED: ${context.outdated.length}`); | ||
| context.outdated.slice(0, 5).forEach(pkg => { | ||
| const current = pkg.current || pkg.version; | ||
| const latest = pkg.latest || pkg.wanted; | ||
| parts.push(` ${pkg.name}: ${current} โ ${latest}`); | ||
| }); | ||
| if (context.outdated.length > 5) { | ||
| parts.push(` ... and ${context.outdated.length - 5} more`); | ||
| } | ||
| parts.push(''); | ||
| } | ||
| // Unused (brief list) | ||
| if (context.unused.length > 0) { | ||
| const unusedList = context.unused.map(pkg => | ||
| typeof pkg === 'string' ? pkg : pkg.name | ||
| ).join(', '); | ||
| parts.push(`UNUSED: ${unusedList}\n`); | ||
| } | ||
| // Ecosystem alerts (top 3 only) | ||
| if (context.ecosystemAlerts.length > 0) { | ||
| parts.push(`ALERTS: ${context.ecosystemAlerts.length}`); | ||
| context.ecosystemAlerts.slice(0, 3).forEach(alert => { | ||
| parts.push(` ${alert.package || alert.name}: ${(alert.issue || alert.message).substring(0, 50)}...`); | ||
| }); | ||
| parts.push(''); | ||
| } | ||
| return parts.join('\n'); | ||
| } | ||
| /** | ||
| * Build context from snapshot comparison | ||
| */ | ||
| function buildComparisonContext(comparison) { | ||
| return ` | ||
| SNAPSHOT COMPARISON: | ||
| From: Snapshot #${comparison.snapshot1.id} (${comparison.snapshot1.timestamp}) | ||
| To: Snapshot #${comparison.snapshot2.id} (${comparison.snapshot2.timestamp}) | ||
| HEALTH SCORE CHANGE: | ||
| ${comparison.snapshot1.healthScore}/10 โ ${comparison.snapshot2.healthScore}/10 (${comparison.healthDelta > 0 ? '+' : ''}${comparison.healthDelta}) | ||
| CHANGES: | ||
| - Added Packages: ${comparison.added.length} | ||
| - Removed Packages: ${comparison.removed.length} | ||
| - Updated Packages: ${comparison.updated.length} | ||
| DETAILS: | ||
| ${JSON.stringify(comparison, null, 2)} | ||
| `; | ||
| } | ||
| /** | ||
| * Build context from timeline | ||
| */ | ||
| function buildTimelineContext(timeline) { | ||
| return ` | ||
| TIMELINE ANALYSIS: | ||
| Period: ${timeline.period} | ||
| Total Snapshots: ${timeline.snapshots.length} | ||
| TRENDS: | ||
| - Health Score: ${timeline.trend.direction} (${timeline.trend.percentage}%) | ||
| - Average Health: ${timeline.averageHealth}/10 | ||
| - Dependency Growth: ${timeline.dependencyGrowth} | ||
| TIMELINE DATA: | ||
| ${JSON.stringify(timeline, null, 2)} | ||
| `; | ||
| } | ||
| module.exports = { | ||
| SYSTEM_PROMPTS, | ||
| buildAnalysisContext, | ||
| buildComparisonContext, | ||
| buildTimelineContext | ||
| }; |
| // src/ai/providers/anthropic.js | ||
| const BaseProvider = require('./base-provider'); | ||
| class AnthropicProvider extends BaseProvider { | ||
| constructor(config) { | ||
| super(config); | ||
| this.apiUrl = this.baseUrl || 'https://api.anthropic.com/v1/messages'; | ||
| this.apiVersion = '2023-06-01'; | ||
| } | ||
| /** | ||
| * Send a prompt to Anthropic Claude | ||
| */ | ||
| async sendPrompt(prompt, systemPrompt = null, options = {}) { | ||
| const requestBody = { | ||
| model: this.model, | ||
| max_tokens: options.maxTokens || this.maxTokens, | ||
| temperature: options.temperature || this.temperature, | ||
| messages: [ | ||
| { role: 'user', content: prompt } | ||
| ] | ||
| }; | ||
| if (systemPrompt) { | ||
| requestBody.system = systemPrompt; | ||
| } | ||
| try { | ||
| const response = await fetch(this.apiUrl, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'x-api-key': this.apiKey, | ||
| 'anthropic-version': this.apiVersion | ||
| }, | ||
| body: JSON.stringify(requestBody) | ||
| }); | ||
| if (!response.ok) { | ||
| const error = await response.json(); | ||
| throw new Error(error.error?.message || `Anthropic API error: ${response.status}`); | ||
| } | ||
| const data = await response.json(); | ||
| return { | ||
| content: data.content[0].text, | ||
| tokensUsed: data.usage?.input_tokens + data.usage?.output_tokens || 0, | ||
| promptTokens: data.usage?.input_tokens || 0, | ||
| completionTokens: data.usage?.output_tokens || 0, | ||
| model: data.model, | ||
| finishReason: data.stop_reason | ||
| }; | ||
| } catch (error) { | ||
| throw new Error(`Anthropic API error: ${error.message}`); | ||
| } | ||
| } | ||
| /** | ||
| * Stream a response from Anthropic Claude | ||
| */ | ||
| async streamPrompt(prompt, systemPrompt = null, onChunk, options = {}) { | ||
| const requestBody = { | ||
| model: this.model, | ||
| max_tokens: options.maxTokens || this.maxTokens, | ||
| temperature: options.temperature || this.temperature, | ||
| messages: [ | ||
| { role: 'user', content: prompt } | ||
| ], | ||
| stream: true | ||
| }; | ||
| if (systemPrompt) { | ||
| requestBody.system = systemPrompt; | ||
| } | ||
| try { | ||
| const response = await fetch(this.apiUrl, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'x-api-key': this.apiKey, | ||
| 'anthropic-version': this.apiVersion | ||
| }, | ||
| body: JSON.stringify(requestBody) | ||
| }); | ||
| if (!response.ok) { | ||
| const error = await response.json(); | ||
| throw new Error(error.error?.message || `Anthropic API error: ${response.status}`); | ||
| } | ||
| const reader = response.body.getReader(); | ||
| const decoder = new TextDecoder(); | ||
| let buffer = ''; | ||
| let fullContent = ''; | ||
| let totalTokens = 0; | ||
| while (true) { | ||
| const { done, value } = await reader.read(); | ||
| if (done) break; | ||
| buffer += decoder.decode(value, { stream: true }); | ||
| const lines = buffer.split('\n'); | ||
| buffer = lines.pop() || ''; | ||
| for (const line of lines) { | ||
| if (line.startsWith('data: ')) { | ||
| const data = line.slice(6); | ||
| try { | ||
| const parsed = JSON.parse(data); | ||
| if (parsed.type === 'content_block_delta') { | ||
| const content = parsed.delta?.text || ''; | ||
| if (content) { | ||
| fullContent += content; | ||
| totalTokens += this.countTokens(content); | ||
| onChunk(content); | ||
| } | ||
| } | ||
| } catch (e) { | ||
| // Skip invalid JSON | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return { | ||
| content: fullContent, | ||
| tokensUsed: totalTokens, | ||
| model: this.model | ||
| }; | ||
| } catch (error) { | ||
| throw new Error(`Anthropic streaming error: ${error.message}`); | ||
| } | ||
| } | ||
| /** | ||
| * Estimate cost for Anthropic models | ||
| */ | ||
| estimateCost(tokens, isInput = true) { | ||
| // Pricing per 1K tokens (as of 2024) | ||
| const pricing = { | ||
| 'claude-3-5-sonnet-20241022': { input: 0.003, output: 0.015 }, | ||
| 'claude-3-opus-20240229': { input: 0.015, output: 0.075 }, | ||
| 'claude-3-sonnet-20240229': { input: 0.003, output: 0.015 }, | ||
| 'claude-3-haiku-20240307': { input: 0.00025, output: 0.00125 } | ||
| }; | ||
| const modelPricing = pricing[this.model] || pricing['claude-3-haiku-20240307']; | ||
| const costPer1k = isInput ? modelPricing.input : modelPricing.output; | ||
| return (tokens / 1000) * costPer1k; | ||
| } | ||
| } | ||
| module.exports = AnthropicProvider; |
| // src/ai/providers/base-provider.js | ||
| class BaseProvider { | ||
| constructor(config) { | ||
| this.name = config.provider; | ||
| this.apiKey = config.api_key; | ||
| this.model = config.model; | ||
| this.baseUrl = config.base_url; | ||
| this.maxTokens = config.max_tokens || 4096; | ||
| this.temperature = config.temperature || 0.7; | ||
| } | ||
| /** | ||
| * Send a prompt to the LLM | ||
| * Must be implemented by subclasses | ||
| */ | ||
| async sendPrompt(prompt, systemPrompt = null, options = {}) { | ||
| throw new Error('sendPrompt() must be implemented by subclass'); | ||
| } | ||
| /** | ||
| * Stream a response from the LLM | ||
| * Must be implemented by subclasses | ||
| */ | ||
| async streamPrompt(prompt, systemPrompt = null, onChunk, options = {}) { | ||
| throw new Error('streamPrompt() must be implemented by subclass'); | ||
| } | ||
| /** | ||
| * Test API connection | ||
| */ | ||
| async test() { | ||
| try { | ||
| const response = await this.sendPrompt('Hello! Please respond with "OK" if you can read this.', null, { | ||
| maxTokens: 10 | ||
| }); | ||
| return { | ||
| success: true, | ||
| message: 'Connection successful', | ||
| response: response.content | ||
| }; | ||
| } catch (error) { | ||
| return { | ||
| success: false, | ||
| message: error.message | ||
| }; | ||
| } | ||
| } | ||
| /** | ||
| * Count tokens (approximate) | ||
| */ | ||
| countTokens(text) { | ||
| // Rough estimate: ~4 chars per token | ||
| return Math.ceil(text.length / 4); | ||
| } | ||
| /** | ||
| * Estimate cost based on tokens | ||
| */ | ||
| estimateCost(tokens, isInput = true) { | ||
| // Default pricing (override in subclasses) | ||
| const inputCostPer1k = 0.01; // $0.01 per 1K tokens | ||
| const outputCostPer1k = 0.03; // $0.03 per 1K tokens | ||
| const costPer1k = isInput ? inputCostPer1k : outputCostPer1k; | ||
| return (tokens / 1000) * costPer1k; | ||
| } | ||
| } | ||
| module.exports = BaseProvider; |
| // src/ai/providers/google.js | ||
| const BaseProvider = require('./base-provider'); | ||
| class GoogleProvider extends BaseProvider { | ||
| constructor(config) { | ||
| super(config); | ||
| this.apiUrl = this.baseUrl || 'https://generativelanguage.googleapis.com/v1beta'; | ||
| } | ||
| /** | ||
| * Send a prompt to Google Gemini | ||
| */ | ||
| async sendPrompt(prompt, systemPrompt = null, options = {}) { | ||
| const url = `${this.apiUrl}/models/${this.model}:generateContent?key=${this.apiKey}`; | ||
| const parts = []; | ||
| if (systemPrompt) { | ||
| parts.push({ text: systemPrompt }); | ||
| } | ||
| parts.push({ text: prompt }); | ||
| const requestBody = { | ||
| contents: [{ parts }], | ||
| generationConfig: { | ||
| maxOutputTokens: options.maxTokens || this.maxTokens, | ||
| temperature: options.temperature || this.temperature | ||
| } | ||
| }; | ||
| try { | ||
| const response = await fetch(url, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json' | ||
| }, | ||
| body: JSON.stringify(requestBody) | ||
| }); | ||
| if (!response.ok) { | ||
| const error = await response.json(); | ||
| throw new Error(error.error?.message || `Google API error: ${response.status}`); | ||
| } | ||
| const data = await response.json(); | ||
| const content = data.candidates[0]?.content?.parts[0]?.text || ''; | ||
| return { | ||
| content: content, | ||
| tokensUsed: this.countTokens(content), | ||
| promptTokens: this.countTokens(prompt), | ||
| completionTokens: this.countTokens(content), | ||
| model: this.model, | ||
| finishReason: data.candidates[0]?.finishReason | ||
| }; | ||
| } catch (error) { | ||
| throw new Error(`Google API error: ${error.message}`); | ||
| } | ||
| } | ||
| /** | ||
| * Stream a response from Google Gemini | ||
| */ | ||
| async streamPrompt(prompt, systemPrompt = null, onChunk, options = {}) { | ||
| const url = `${this.apiUrl}/models/${this.model}:streamGenerateContent?key=${this.apiKey}`; | ||
| const parts = []; | ||
| if (systemPrompt) { | ||
| parts.push({ text: systemPrompt }); | ||
| } | ||
| parts.push({ text: prompt }); | ||
| const requestBody = { | ||
| contents: [{ parts }], | ||
| generationConfig: { | ||
| maxOutputTokens: options.maxTokens || this.maxTokens, | ||
| temperature: options.temperature || this.temperature | ||
| } | ||
| }; | ||
| try { | ||
| const response = await fetch(url, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json' | ||
| }, | ||
| body: JSON.stringify(requestBody) | ||
| }); | ||
| if (!response.ok) { | ||
| const error = await response.json(); | ||
| throw new Error(error.error?.message || `Google API error: ${response.status}`); | ||
| } | ||
| const reader = response.body.getReader(); | ||
| const decoder = new TextDecoder(); | ||
| let fullContent = ''; | ||
| while (true) { | ||
| const { done, value } = await reader.read(); | ||
| if (done) break; | ||
| const chunk = decoder.decode(value); | ||
| const lines = chunk.split('\n').filter(line => line.trim()); | ||
| for (const line of lines) { | ||
| try { | ||
| const data = JSON.parse(line); | ||
| const content = data.candidates?.[0]?.content?.parts?.[0]?.text || ''; | ||
| if (content) { | ||
| fullContent += content; | ||
| onChunk(content); | ||
| } | ||
| } catch (e) { | ||
| // Skip invalid JSON | ||
| } | ||
| } | ||
| } | ||
| return { | ||
| content: fullContent, | ||
| tokensUsed: this.countTokens(fullContent), | ||
| model: this.model | ||
| }; | ||
| } catch (error) { | ||
| throw new Error(`Google streaming error: ${error.message}`); | ||
| } | ||
| } | ||
| /** | ||
| * Estimate cost for Google models | ||
| */ | ||
| estimateCost(tokens, isInput = true) { | ||
| // Pricing per 1K tokens (as of 2024) | ||
| const pricing = { | ||
| 'gemini-pro': { input: 0.00025, output: 0.0005 }, | ||
| 'gemini-pro-vision': { input: 0.00025, output: 0.0005 }, | ||
| 'gemini-1.5-pro': { input: 0.00125, output: 0.00375 }, | ||
| 'gemini-1.5-flash': { input: 0.000075, output: 0.0003 } | ||
| }; | ||
| const modelPricing = pricing[this.model] || pricing['gemini-pro']; | ||
| const costPer1k = isInput ? modelPricing.input : modelPricing.output; | ||
| return (tokens / 1000) * costPer1k; | ||
| } | ||
| } | ||
| module.exports = GoogleProvider; |
| // src/ai/providers/local.js | ||
| const BaseProvider = require('./base-provider'); | ||
| class LocalProvider extends BaseProvider { | ||
| constructor(config) { | ||
| super(config); | ||
| this.apiUrl = this.baseUrl || 'http://localhost:11434'; | ||
| } | ||
| /** | ||
| * Send a prompt to local model (Ollama) | ||
| */ | ||
| async sendPrompt(prompt, systemPrompt = null, options = {}) { | ||
| const url = `${this.apiUrl}/api/generate`; | ||
| let fullPrompt = prompt; | ||
| if (systemPrompt) { | ||
| fullPrompt = `${systemPrompt}\n\n${prompt}`; | ||
| } | ||
| const requestBody = { | ||
| model: this.model, | ||
| prompt: fullPrompt, | ||
| stream: false, | ||
| options: { | ||
| temperature: options.temperature || this.temperature, | ||
| num_predict: options.maxTokens || this.maxTokens | ||
| } | ||
| }; | ||
| try { | ||
| const response = await fetch(url, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json' | ||
| }, | ||
| body: JSON.stringify(requestBody) | ||
| }); | ||
| if (!response.ok) { | ||
| throw new Error(`Local model error: ${response.status} ${response.statusText}`); | ||
| } | ||
| const data = await response.json(); | ||
| return { | ||
| content: data.response, | ||
| tokensUsed: this.countTokens(data.response), | ||
| promptTokens: this.countTokens(fullPrompt), | ||
| completionTokens: this.countTokens(data.response), | ||
| model: this.model, | ||
| finishReason: 'stop' | ||
| }; | ||
| } catch (error) { | ||
| if (error.code === 'ECONNREFUSED') { | ||
| throw new Error('Local model server not running. Start Ollama with: ollama serve'); | ||
| } | ||
| throw new Error(`Local model error: ${error.message}`); | ||
| } | ||
| } | ||
| /** | ||
| * Stream a response from local model | ||
| */ | ||
| async streamPrompt(prompt, systemPrompt = null, onChunk, options = {}) { | ||
| const url = `${this.apiUrl}/api/generate`; | ||
| let fullPrompt = prompt; | ||
| if (systemPrompt) { | ||
| fullPrompt = `${systemPrompt}\n\n${prompt}`; | ||
| } | ||
| const requestBody = { | ||
| model: this.model, | ||
| prompt: fullPrompt, | ||
| stream: true, | ||
| options: { | ||
| temperature: options.temperature || this.temperature, | ||
| num_predict: options.maxTokens || this.maxTokens | ||
| } | ||
| }; | ||
| try { | ||
| const response = await fetch(url, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json' | ||
| }, | ||
| body: JSON.stringify(requestBody) | ||
| }); | ||
| if (!response.ok) { | ||
| throw new Error(`Local model error: ${response.status}`); | ||
| } | ||
| const reader = response.body.getReader(); | ||
| const decoder = new TextDecoder(); | ||
| let fullContent = ''; | ||
| while (true) { | ||
| const { done, value } = await reader.read(); | ||
| if (done) break; | ||
| const chunk = decoder.decode(value); | ||
| const lines = chunk.split('\n').filter(line => line.trim()); | ||
| for (const line of lines) { | ||
| try { | ||
| const data = JSON.parse(line); | ||
| const content = data.response || ''; | ||
| if (content) { | ||
| fullContent += content; | ||
| onChunk(content); | ||
| } | ||
| } catch (e) { | ||
| // Skip invalid JSON | ||
| } | ||
| } | ||
| } | ||
| return { | ||
| content: fullContent, | ||
| tokensUsed: this.countTokens(fullContent), | ||
| model: this.model | ||
| }; | ||
| } catch (error) { | ||
| if (error.code === 'ECONNREFUSED') { | ||
| throw new Error('Local model server not running. Start Ollama with: ollama serve'); | ||
| } | ||
| throw new Error(`Local streaming error: ${error.message}`); | ||
| } | ||
| } | ||
| /** | ||
| * Local models are free | ||
| */ | ||
| estimateCost(tokens, isInput = true) { | ||
| return 0.0; // Local models cost nothing | ||
| } | ||
| /** | ||
| * Test local model connection | ||
| */ | ||
| async test() { | ||
| try { | ||
| const response = await fetch(`${this.apiUrl}/api/tags`); | ||
| if (!response.ok) { | ||
| throw new Error('Failed to connect to Ollama'); | ||
| } | ||
| const data = await response.json(); | ||
| const hasModel = data.models?.some(m => m.name === this.model); | ||
| if (!hasModel) { | ||
| return { | ||
| success: false, | ||
| message: `Model "${this.model}" not found. Pull it with: ollama pull ${this.model}` | ||
| }; | ||
| } | ||
| return { | ||
| success: true, | ||
| message: `Connected to Ollama (model: ${this.model})` | ||
| }; | ||
| } catch (error) { | ||
| return { | ||
| success: false, | ||
| message: error.message | ||
| }; | ||
| } | ||
| } | ||
| } | ||
| module.exports = LocalProvider; |
| // src/ai/providers/openai.js | ||
| const BaseProvider = require('./base-provider'); | ||
| class OpenAIProvider extends BaseProvider { | ||
| constructor(config) { | ||
| super(config); | ||
| this.apiUrl = this.baseUrl || 'https://api.openai.com/v1/chat/completions'; | ||
| } | ||
| /** | ||
| * Send a prompt to OpenAI | ||
| */ | ||
| async sendPrompt(prompt, systemPrompt = null, options = {}) { | ||
| const messages = []; | ||
| if (systemPrompt) { | ||
| messages.push({ role: 'system', content: systemPrompt }); | ||
| } | ||
| messages.push({ role: 'user', content: prompt }); | ||
| const requestBody = { | ||
| model: this.model, | ||
| messages: messages, | ||
| max_tokens: options.maxTokens || this.maxTokens, | ||
| temperature: options.temperature || this.temperature, | ||
| stream: false | ||
| }; | ||
| try { | ||
| const response = await fetch(this.apiUrl, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': `Bearer ${this.apiKey}` | ||
| }, | ||
| body: JSON.stringify(requestBody) | ||
| }); | ||
| if (!response.ok) { | ||
| const error = await response.json(); | ||
| throw new Error(error.error?.message || `OpenAI API error: ${response.status}`); | ||
| } | ||
| const data = await response.json(); | ||
| return { | ||
| content: data.choices[0].message.content, | ||
| tokensUsed: data.usage?.total_tokens || 0, | ||
| promptTokens: data.usage?.prompt_tokens || 0, | ||
| completionTokens: data.usage?.completion_tokens || 0, | ||
| model: data.model, | ||
| finishReason: data.choices[0].finish_reason | ||
| }; | ||
| } catch (error) { | ||
| throw new Error(`OpenAI API error: ${error.message}`); | ||
| } | ||
| } | ||
| /** | ||
| * Stream a response from OpenAI | ||
| */ | ||
| async streamPrompt(prompt, systemPrompt = null, onChunk, options = {}) { | ||
| const messages = []; | ||
| if (systemPrompt) { | ||
| messages.push({ role: 'system', content: systemPrompt }); | ||
| } | ||
| messages.push({ role: 'user', content: prompt }); | ||
| const requestBody = { | ||
| model: this.model, | ||
| messages: messages, | ||
| max_tokens: options.maxTokens || this.maxTokens, | ||
| temperature: options.temperature || this.temperature, | ||
| stream: true | ||
| }; | ||
| try { | ||
| const response = await fetch(this.apiUrl, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': `Bearer ${this.apiKey}` | ||
| }, | ||
| body: JSON.stringify(requestBody) | ||
| }); | ||
| if (!response.ok) { | ||
| const error = await response.json(); | ||
| throw new Error(error.error?.message || `OpenAI API error: ${response.status}`); | ||
| } | ||
| const reader = response.body.getReader(); | ||
| const decoder = new TextDecoder(); | ||
| let buffer = ''; | ||
| let fullContent = ''; | ||
| let totalTokens = 0; | ||
| while (true) { | ||
| const { done, value } = await reader.read(); | ||
| if (done) break; | ||
| buffer += decoder.decode(value, { stream: true }); | ||
| const lines = buffer.split('\n'); | ||
| buffer = lines.pop() || ''; | ||
| for (const line of lines) { | ||
| if (line.startsWith('data: ')) { | ||
| const data = line.slice(6); | ||
| if (data === '[DONE]') continue; | ||
| try { | ||
| const parsed = JSON.parse(data); | ||
| const content = parsed.choices[0]?.delta?.content || ''; | ||
| if (content) { | ||
| fullContent += content; | ||
| totalTokens += this.countTokens(content); | ||
| onChunk(content); | ||
| } | ||
| } catch (e) { | ||
| // Skip invalid JSON | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return { | ||
| content: fullContent, | ||
| tokensUsed: totalTokens, | ||
| model: this.model | ||
| }; | ||
| } catch (error) { | ||
| throw new Error(`OpenAI streaming error: ${error.message}`); | ||
| } | ||
| } | ||
| /** | ||
| * Estimate cost for OpenAI models | ||
| */ | ||
| estimateCost(tokens, isInput = true) { | ||
| // Pricing per 1K tokens (as of 2024) | ||
| const pricing = { | ||
| 'gpt-4': { input: 0.03, output: 0.06 }, | ||
| 'gpt-4-turbo': { input: 0.01, output: 0.03 }, | ||
| 'gpt-3.5-turbo': { input: 0.0005, output: 0.0015 }, | ||
| 'gpt-4o': { input: 0.005, output: 0.015 }, | ||
| 'gpt-4o-mini': { input: 0.00015, output: 0.0006 } | ||
| }; | ||
| const modelPricing = pricing[this.model] || pricing['gpt-3.5-turbo']; | ||
| const costPer1k = isInput ? modelPricing.input : modelPricing.output; | ||
| return (tokens / 1000) * costPer1k; | ||
| } | ||
| } | ||
| module.exports = OpenAIProvider; |
| // src/ai/token-manager.js | ||
| const aiDatabase = require('./database'); | ||
| const { encrypt, decrypt, maskToken } = require('../utils/encryption'); | ||
| const OpenAIProvider = require('./providers/openai'); | ||
| const AnthropicProvider = require('./providers/anthropic'); | ||
| const GoogleProvider = require('./providers/google'); | ||
| const LocalProvider = require('./providers/local'); | ||
| class TokenManager { | ||
| /** | ||
| * Add a new LLM provider | ||
| */ | ||
| addProvider(provider, apiKey, model, baseUrl = null) { | ||
| try { | ||
| // Encrypt API key | ||
| const encryptedKey = encrypt(apiKey); | ||
| // Add to database | ||
| aiDatabase.addProvider(provider, encryptedKey, model, baseUrl); | ||
| // If it's the first provider, make it default | ||
| const providers = aiDatabase.getAllProviders(); | ||
| if (providers.length === 1) { | ||
| aiDatabase.setDefaultProvider(provider); | ||
| } | ||
| return { success: true, message: `Provider "${provider}" added successfully` }; | ||
| } catch (error) { | ||
| return { success: false, message: error.message }; | ||
| } | ||
| } | ||
| /** | ||
| * Get provider instance with decrypted key | ||
| */ | ||
| getProvider(providerName = null) { | ||
| try { | ||
| let config; | ||
| if (providerName) { | ||
| config = aiDatabase.getProvider(providerName); | ||
| } else { | ||
| config = aiDatabase.getDefaultProvider(); | ||
| } | ||
| if (!config) { | ||
| throw new Error(providerName | ||
| ? `Provider "${providerName}" not found` | ||
| : 'No default provider configured. Add one with: devcompass llm add' | ||
| ); | ||
| } | ||
| if (!config.is_active) { | ||
| throw new Error(`Provider "${config.provider}" is disabled`); | ||
| } | ||
| // Decrypt API key | ||
| const decryptedKey = decrypt(config.api_key); | ||
| const providerConfig = { | ||
| ...config, | ||
| api_key: decryptedKey | ||
| }; | ||
| // Return appropriate provider instance | ||
| switch (config.provider) { | ||
| case 'openai': | ||
| return new OpenAIProvider(providerConfig); | ||
| case 'anthropic': | ||
| return new AnthropicProvider(providerConfig); | ||
| case 'google': | ||
| return new GoogleProvider(providerConfig); | ||
| case 'local': | ||
| return new LocalProvider(providerConfig); | ||
| default: | ||
| throw new Error(`Unknown provider: ${config.provider}`); | ||
| } | ||
| } catch (error) { | ||
| throw error; | ||
| } | ||
| } | ||
| /** | ||
| * List all providers | ||
| */ | ||
| listProviders() { | ||
| const providers = aiDatabase.getAllProviders(); | ||
| return providers.map(p => ({ | ||
| provider: p.provider, | ||
| model: p.model, | ||
| baseUrl: p.base_url, | ||
| isDefault: p.is_default === 1, | ||
| isActive: p.is_active === 1, | ||
| apiKey: maskToken(decrypt(p.api_key)), | ||
| createdAt: p.created_at | ||
| })); | ||
| } | ||
| /** | ||
| * Set default provider | ||
| */ | ||
| setDefault(provider) { | ||
| try { | ||
| const config = aiDatabase.getProvider(provider); | ||
| if (!config) { | ||
| throw new Error(`Provider "${provider}" not found`); | ||
| } | ||
| aiDatabase.setDefaultProvider(provider); | ||
| return { success: true, message: `Default provider set to "${provider}"` }; | ||
| } catch (error) { | ||
| return { success: false, message: error.message }; | ||
| } | ||
| } | ||
| /** | ||
| * Update provider | ||
| */ | ||
| updateProvider(provider, updates) { | ||
| try { | ||
| const config = aiDatabase.getProvider(provider); | ||
| if (!config) { | ||
| throw new Error(`Provider "${provider}" not found`); | ||
| } | ||
| // Encrypt API key if updating | ||
| if (updates.api_key) { | ||
| updates.api_key = encrypt(updates.api_key); | ||
| } | ||
| aiDatabase.updateProvider(provider, updates); | ||
| return { success: true, message: `Provider "${provider}" updated` }; | ||
| } catch (error) { | ||
| return { success: false, message: error.message }; | ||
| } | ||
| } | ||
| /** | ||
| * Remove provider | ||
| */ | ||
| removeProvider(provider) { | ||
| try { | ||
| const config = aiDatabase.getProvider(provider); | ||
| if (!config) { | ||
| throw new Error(`Provider "${provider}" not found`); | ||
| } | ||
| aiDatabase.removeProvider(provider); | ||
| return { success: true, message: `Provider "${provider}" removed` }; | ||
| } catch (error) { | ||
| return { success: false, message: error.message }; | ||
| } | ||
| } | ||
| /** | ||
| * Enable/disable provider | ||
| */ | ||
| toggleProvider(provider, enabled) { | ||
| try { | ||
| aiDatabase.updateProvider(provider, { is_active: enabled ? 1 : 0 }); | ||
| const status = enabled ? 'enabled' : 'disabled'; | ||
| return { success: true, message: `Provider "${provider}" ${status}` }; | ||
| } catch (error) { | ||
| return { success: false, message: error.message }; | ||
| } | ||
| } | ||
| /** | ||
| * Test provider connection | ||
| */ | ||
| async testProvider(provider) { | ||
| try { | ||
| const providerInstance = this.getProvider(provider); | ||
| const result = await providerInstance.test(); | ||
| return result; | ||
| } catch (error) { | ||
| return { success: false, message: error.message }; | ||
| } | ||
| } | ||
| } | ||
| module.exports = new TokenManager(); |
| // src/commands/ai.js | ||
| const chalk = require('chalk'); | ||
| const path = require('path'); | ||
| const fs = require('fs'); | ||
| const tokenManager = require('../ai/token-manager'); | ||
| const conversationManager = require('../ai/conversation'); | ||
| const costTracker = require('../ai/cost-tracker'); | ||
| const streamFormatter = require('../utils/stream-formatter'); | ||
| const { SYSTEM_PROMPTS, buildAnalysisContext } = require('../ai/prompt-templates'); | ||
| const aiDatabase = require('../ai/database'); | ||
| const readline = require('readline'); | ||
| /** | ||
| * Get analysis data from cache | ||
| */ | ||
| function getAnalysisData() { | ||
| try { | ||
| const cacheFile = path.join(process.cwd(), '.devcompass-cache.json'); | ||
| if (fs.existsSync(cacheFile)) { | ||
| return JSON.parse(fs.readFileSync(cacheFile, 'utf8')); | ||
| } | ||
| } catch (error) { | ||
| // Ignore errors | ||
| } | ||
| return null; | ||
| } | ||
| /** | ||
| * Ask AI a question about the analysis | ||
| */ | ||
| async function askQuestion(question, context = null, options = {}) { | ||
| if (!question) { | ||
| console.error(chalk.red('โ Error: Question is required')); | ||
| console.log('\nUsage: devcompass ai ask "your question here"'); | ||
| return; | ||
| } | ||
| try { | ||
| const provider = tokenManager.getProvider(options.provider); | ||
| const providerConfig = aiDatabase.getProvider(options.provider || provider.name); | ||
| const analysisData = getAnalysisData(); | ||
| let contextText = ''; | ||
| if (analysisData) { | ||
| contextText = buildAnalysisContext(analysisData); | ||
| } else if (context) { | ||
| contextText = context; | ||
| } | ||
| const systemPrompt = SYSTEM_PROMPTS.qa; | ||
| let userPrompt = `${contextText}\n\nQuestion: ${question}\n\nAnswer in 2-4 sentences max.`; | ||
| streamFormatter.showTyping('AI is thinking'); | ||
| let response; | ||
| if (options.stream !== false) { | ||
| streamFormatter.clearTyping(); | ||
| response = await provider.streamPrompt( | ||
| userPrompt, | ||
| systemPrompt, | ||
| (chunk) => streamFormatter.formatChunk(chunk), | ||
| { maxTokens: options.maxTokens || 500 } // REDUCED | ||
| ); | ||
| streamFormatter.finish(); | ||
| } else { | ||
| response = await provider.sendPrompt(userPrompt, systemPrompt, { | ||
| maxTokens: options.maxTokens || 500 // REDUCED | ||
| }); | ||
| streamFormatter.clearTyping(); | ||
| console.log('\n๐ค ' + response.content + '\n'); | ||
| } | ||
| const inputCost = provider.estimateCost(response.promptTokens, true); | ||
| const outputCost = provider.estimateCost(response.completionTokens, false); | ||
| const totalCost = inputCost + outputCost; | ||
| costTracker.trackUsage(providerConfig.id, response.tokensUsed, totalCost); | ||
| conversationManager.saveConversation( | ||
| providerConfig.id, | ||
| 'ai ask', | ||
| contextText, | ||
| question, | ||
| response.content, | ||
| response.tokensUsed, | ||
| totalCost | ||
| ); | ||
| if (options.verbose) { | ||
| console.log(chalk.gray(`\n๐ก Tokens used: ${response.tokensUsed.toLocaleString()} (~$${totalCost.toFixed(4)})`)); | ||
| } | ||
| return response.content; | ||
| } catch (error) { | ||
| streamFormatter.clearTyping(); | ||
| console.error(chalk.red('\nโ AI Error: ' + error.message)); | ||
| if (error.message.includes('not found') || error.message.includes('No default provider')) { | ||
| console.log(chalk.yellow('\n๐ก Add an LLM provider first:')); | ||
| console.log(' devcompass llm add --provider openai --token sk-xxx --model gpt-4'); | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * Get AI recommendations | ||
| */ | ||
| async function getRecommendations(analysisResults, options = {}) { | ||
| try { | ||
| // Get provider | ||
| const provider = tokenManager.getProvider(options.provider); | ||
| const providerConfig = aiDatabase.getProvider(options.provider || provider.name); | ||
| // Build context - use analysisResults directly | ||
| const context = buildAnalysisContext(analysisResults); | ||
| const systemPrompt = SYSTEM_PROMPTS.recommend; | ||
| const userPrompt = `Based on the project analysis above, provide a prioritized action plan with: | ||
| ๐ด CRITICAL (Do Now): | ||
| ๐ก HIGH PRIORITY (This Week): | ||
| ๐ข MEDIUM PRIORITY (This Month): | ||
| For each issue, include: | ||
| - What it is | ||
| - Why it matters | ||
| - How to fix it (specific commands) | ||
| Be specific and actionable based on the analysis data provided.`; | ||
| console.log(chalk.bold('\n๐ค AI Recommendations\n')); | ||
| streamFormatter.showTyping('Analyzing your project'); | ||
| // Get response | ||
| let response; | ||
| if (options.stream !== false) { | ||
| streamFormatter.clearTyping(); | ||
| response = await provider.streamPrompt( | ||
| `${context}\n\n${userPrompt}`, | ||
| systemPrompt, | ||
| (chunk) => streamFormatter.formatChunk(chunk), | ||
| { maxTokens: options.maxTokens || 3000 } | ||
| ); | ||
| streamFormatter.finish(); | ||
| } else { | ||
| response = await provider.sendPrompt( | ||
| `${context}\n\n${userPrompt}`, | ||
| systemPrompt, | ||
| { maxTokens: options.maxTokens || 3000 } | ||
| ); | ||
| streamFormatter.clearTyping(); | ||
| console.log(response.content + '\n'); | ||
| } | ||
| // Calculate cost | ||
| const inputCost = provider.estimateCost(response.promptTokens, true); | ||
| const outputCost = provider.estimateCost(response.completionTokens, false); | ||
| const totalCost = inputCost + outputCost; | ||
| // Track usage | ||
| costTracker.trackUsage(providerConfig.id, response.tokensUsed, totalCost); | ||
| // Save conversation | ||
| conversationManager.saveConversation( | ||
| providerConfig.id, | ||
| 'ai recommend', | ||
| context, | ||
| userPrompt, | ||
| response.content, | ||
| response.tokensUsed, | ||
| totalCost | ||
| ); | ||
| // Show stats | ||
| if (options.verbose) { | ||
| console.log(chalk.gray(`๐ก Tokens used: ${response.tokensUsed.toLocaleString()} (~$${totalCost.toFixed(4)})`)); | ||
| } | ||
| return response.content; | ||
| } catch (error) { | ||
| streamFormatter.clearTyping(); | ||
| console.error(chalk.red('\nโ AI Error: ' + error.message)); | ||
| } | ||
| } | ||
| /** | ||
| * Get alternative package suggestions | ||
| */ | ||
| async function getAlternatives(packageName, options = {}) { | ||
| if (!packageName) { | ||
| console.error(chalk.red('โ Error: Package name is required')); | ||
| console.log('\nUsage: devcompass ai alternatives <package-name>'); | ||
| return; | ||
| } | ||
| try { | ||
| // Get provider | ||
| const provider = tokenManager.getProvider(options.provider); | ||
| const providerConfig = aiDatabase.getProvider(options.provider || provider.name); | ||
| const systemPrompt = SYSTEM_PROMPTS.alternatives; | ||
| const userPrompt = `Suggest the top 3-5 modern alternatives to the npm package "${packageName}". | ||
| For each alternative, provide: | ||
| 1. Package name | ||
| 2. Bundle size (compared to ${packageName}) | ||
| 3. Key differences | ||
| 4. Migration difficulty (easy/medium/hard) | ||
| 5. Current popularity and maintenance status | ||
| Also include a brief code example showing how to migrate from ${packageName}. | ||
| Be specific, accurate, and practical.`; | ||
| console.log(chalk.bold(`\n๐ Finding alternatives for "${packageName}"\n`)); | ||
| streamFormatter.showTyping('Searching npm ecosystem'); | ||
| // Get response | ||
| let response; | ||
| if (options.stream !== false) { | ||
| streamFormatter.clearTyping(); | ||
| response = await provider.streamPrompt( | ||
| userPrompt, | ||
| systemPrompt, | ||
| (chunk) => streamFormatter.formatChunk(chunk), | ||
| { maxTokens: options.maxTokens || 2500 } | ||
| ); | ||
| streamFormatter.finish(); | ||
| } else { | ||
| response = await provider.sendPrompt(userPrompt, systemPrompt, { | ||
| maxTokens: options.maxTokens || 2500 | ||
| }); | ||
| streamFormatter.clearTyping(); | ||
| console.log(response.content + '\n'); | ||
| } | ||
| // Calculate cost | ||
| const inputCost = provider.estimateCost(response.promptTokens, true); | ||
| const outputCost = provider.estimateCost(response.completionTokens, false); | ||
| const totalCost = inputCost + outputCost; | ||
| // Track usage | ||
| costTracker.trackUsage(providerConfig.id, response.tokensUsed, totalCost); | ||
| // Save conversation | ||
| conversationManager.saveConversation( | ||
| providerConfig.id, | ||
| 'ai alternatives', | ||
| null, | ||
| `alternatives for ${packageName}`, | ||
| response.content, | ||
| response.tokensUsed, | ||
| totalCost | ||
| ); | ||
| return response.content; | ||
| } catch (error) { | ||
| streamFormatter.clearTyping(); | ||
| console.error(chalk.red('\nโ AI Error: ' + error.message)); | ||
| } | ||
| } | ||
| /** | ||
| * Interactive AI chat | ||
| */ | ||
| async function startChat(context = null, options = {}) { | ||
| try { | ||
| // Get provider | ||
| const provider = tokenManager.getProvider(options.provider); | ||
| const providerConfig = aiDatabase.getProvider(options.provider || provider.name); | ||
| // Get analysis data for context | ||
| const analysisData = getAnalysisData(); | ||
| let contextText = ''; | ||
| if (analysisData) { | ||
| contextText = buildAnalysisContext(analysisData); | ||
| } else if (context) { | ||
| contextText = context; | ||
| } | ||
| // Start new session | ||
| conversationManager.startSession(); | ||
| console.log(chalk.bold(`\n๐ค DevCompass AI Assistant (powered by ${providerConfig.model})`)); | ||
| if (contextText) { | ||
| console.log(chalk.gray('I have access to your project analysis. Ask me anything!')); | ||
| } else { | ||
| console.log(chalk.yellow('โ ๏ธ No analysis data found. Run "devcompass analyze" first for better insights.')); | ||
| } | ||
| console.log(chalk.gray('Type "exit" or "quit" to end the conversation.\n')); | ||
| const rl = readline.createInterface({ | ||
| input: process.stdin, | ||
| output: process.stdout, | ||
| prompt: chalk.cyan('You: ') | ||
| }); | ||
| let totalTokens = 0; | ||
| let totalCost = 0; | ||
| rl.prompt(); | ||
| rl.on('line', async (input) => { | ||
| const question = input.trim(); | ||
| if (!question) { | ||
| rl.prompt(); | ||
| return; | ||
| } | ||
| if (question.toLowerCase() === 'exit' || question.toLowerCase() === 'quit') { | ||
| console.log(chalk.gray(`\n๐ Chat ended. Used ${totalTokens.toLocaleString()} tokens (~$${totalCost.toFixed(4)})\n`)); | ||
| rl.close(); | ||
| conversationManager.clearSession(); | ||
| return; | ||
| } | ||
| try { | ||
| // Build conversation history | ||
| const history = conversationManager.buildContext(5); | ||
| let userPrompt = question; | ||
| if (contextText) { | ||
| userPrompt = `${contextText}\n\nUser: ${question}\n\nProvide a specific answer based on the project analysis above.`; | ||
| } | ||
| // Get response | ||
| const response = await provider.streamPrompt( | ||
| userPrompt, | ||
| SYSTEM_PROMPTS.chat, | ||
| (chunk) => streamFormatter.formatChunk(chunk), | ||
| { maxTokens: 2000 } | ||
| ); | ||
| streamFormatter.finish(); | ||
| // Calculate cost | ||
| const inputCost = provider.estimateCost(response.promptTokens, true); | ||
| const outputCost = provider.estimateCost(response.completionTokens, false); | ||
| const messageCost = inputCost + outputCost; | ||
| totalTokens += response.tokensUsed; | ||
| totalCost += messageCost; | ||
| // Track usage | ||
| costTracker.trackUsage(providerConfig.id, response.tokensUsed, messageCost); | ||
| // Save conversation | ||
| conversationManager.saveConversation( | ||
| providerConfig.id, | ||
| 'ai chat', | ||
| contextText, | ||
| question, | ||
| response.content, | ||
| response.tokensUsed, | ||
| messageCost | ||
| ); | ||
| rl.prompt(); | ||
| } catch (error) { | ||
| console.error(chalk.red('\nโ Error: ' + error.message + '\n')); | ||
| rl.prompt(); | ||
| } | ||
| }); | ||
| rl.on('close', () => { | ||
| process.exit(0); | ||
| }); | ||
| } catch (error) { | ||
| console.error(chalk.red('โ Chat Error: ' + error.message)); | ||
| } | ||
| } | ||
| /** | ||
| * Show help | ||
| */ | ||
| function showHelp() { | ||
| console.log(chalk.bold('\n๐ค DevCompass AI Commands\n')); | ||
| console.log(chalk.bold('Commands:')); | ||
| console.log(' devcompass ai ask Ask AI about your dependencies'); | ||
| console.log(' devcompass ai recommend Get AI recommendations'); | ||
| console.log(' devcompass ai alternatives Find package alternatives'); | ||
| console.log(' devcompass ai chat Start interactive chat'); | ||
| console.log(chalk.bold('\nExamples:')); | ||
| console.log(' # Ask a question'); | ||
| console.log(' devcompass ai ask "Why is my health score low?"'); | ||
| console.log(); | ||
| console.log(' # Get recommendations after analysis'); | ||
| console.log(' devcompass analyze --ai'); | ||
| console.log(); | ||
| console.log(' # Find alternatives'); | ||
| console.log(' devcompass ai alternatives moment'); | ||
| console.log(); | ||
| console.log(' # Interactive chat'); | ||
| console.log(' devcompass ai chat'); | ||
| console.log(); | ||
| } | ||
| module.exports = { | ||
| askQuestion, | ||
| getRecommendations, | ||
| getAlternatives, | ||
| startChat, | ||
| showHelp | ||
| }; |
| // src/commands/llm.js | ||
| const chalk = require('chalk'); | ||
| const tokenManager = require('../ai/token-manager'); | ||
| const aiDatabase = require('../ai/database'); | ||
| const costTracker = require('../ai/cost-tracker'); | ||
| /** | ||
| * Add a new LLM provider | ||
| */ | ||
| async function addProvider(options) { | ||
| const { provider, token, model, baseUrl } = options; | ||
| // Validate required fields | ||
| if (!provider) { | ||
| console.error(chalk.red('โ Error: Provider name is required')); | ||
| console.log('\nUsage: devcompass llm add --provider <name> --token <token> --model <model>'); | ||
| console.log('\nSupported providers: openai, anthropic, google, local'); | ||
| return; | ||
| } | ||
| if (!token && provider !== 'local') { | ||
| console.error(chalk.red('โ Error: API token is required')); | ||
| return; | ||
| } | ||
| if (!model) { | ||
| console.error(chalk.red('โ Error: Model name is required')); | ||
| console.log('\nExamples:'); | ||
| console.log(' OpenAI: gpt-4, gpt-4-turbo, gpt-3.5-turbo'); | ||
| console.log(' Anthropic: claude-3-5-sonnet-20241022, claude-3-opus-20240229'); | ||
| console.log(' Google: gemini-pro, gemini-1.5-pro'); | ||
| console.log(' Local: llama3, mistral, codellama'); | ||
| return; | ||
| } | ||
| // Validate provider type | ||
| const validProviders = ['openai', 'anthropic', 'google', 'local']; | ||
| if (!validProviders.includes(provider)) { | ||
| console.error(chalk.red(`โ Error: Invalid provider "${provider}"`)); | ||
| console.log(`\nSupported providers: ${validProviders.join(', ')}`); | ||
| return; | ||
| } | ||
| try { | ||
| const result = tokenManager.addProvider(provider, token, model, baseUrl); | ||
| if (result.success) { | ||
| console.log(chalk.green('โ ' + result.message)); | ||
| console.log(chalk.gray(` Model: ${model}`)); | ||
| if (baseUrl) { | ||
| console.log(chalk.gray(` Base URL: ${baseUrl}`)); | ||
| } | ||
| console.log('\n๐ก Test connection with: devcompass llm test ' + provider); | ||
| } else { | ||
| console.error(chalk.red('โ Error: ' + result.message)); | ||
| } | ||
| } catch (error) { | ||
| console.error(chalk.red('โ Error adding provider: ' + error.message)); | ||
| } | ||
| } | ||
| /** | ||
| * List all LLM providers | ||
| */ | ||
| function listProviders() { | ||
| try { | ||
| const providers = tokenManager.listProviders(); | ||
| if (providers.length === 0) { | ||
| console.log(chalk.yellow('โ ๏ธ No LLM providers configured')); | ||
| console.log('\n๐ก Add one with: devcompass llm add --provider openai --token sk-xxx --model gpt-4'); | ||
| return; | ||
| } | ||
| console.log(chalk.bold('\n๐ Configured LLM Providers\n')); | ||
| providers.forEach(p => { | ||
| const status = p.isActive ? chalk.green('โ') : chalk.gray('โ'); | ||
| const defaultBadge = p.isDefault ? chalk.cyan(' (default)') : ''; | ||
| console.log(`${status} ${chalk.bold(p.provider)}${defaultBadge}`); | ||
| console.log(` Model: ${p.model}`); | ||
| console.log(` API Key: ${p.apiKey}`); | ||
| if (p.baseUrl) { | ||
| console.log(` Base URL: ${p.baseUrl}`); | ||
| } | ||
| console.log(` Added: ${new Date(p.createdAt).toLocaleDateString()}`); | ||
| console.log(); | ||
| }); | ||
| console.log(chalk.gray('๐ก Tips:')); | ||
| console.log(chalk.gray(' โข Set default: devcompass llm default <provider>')); | ||
| console.log(chalk.gray(' โข Test connection: devcompass llm test <provider>')); | ||
| console.log(chalk.gray(' โข Remove: devcompass llm remove <provider>')); | ||
| } catch (error) { | ||
| console.error(chalk.red('โ Error listing providers: ' + error.message)); | ||
| } | ||
| } | ||
| /** | ||
| * Set default provider | ||
| */ | ||
| function setDefaultProvider(provider) { | ||
| if (!provider) { | ||
| console.error(chalk.red('โ Error: Provider name is required')); | ||
| console.log('\nUsage: devcompass llm default <provider>'); | ||
| return; | ||
| } | ||
| try { | ||
| const result = tokenManager.setDefault(provider); | ||
| if (result.success) { | ||
| console.log(chalk.green('โ ' + result.message)); | ||
| } else { | ||
| console.error(chalk.red('โ Error: ' + result.message)); | ||
| } | ||
| } catch (error) { | ||
| console.error(chalk.red('โ Error: ' + error.message)); | ||
| } | ||
| } | ||
| /** | ||
| * Update provider | ||
| */ | ||
| function updateProvider(provider, options) { | ||
| if (!provider) { | ||
| console.error(chalk.red('โ Error: Provider name is required')); | ||
| console.log('\nUsage: devcompass llm update <provider> --token <new-token>'); | ||
| return; | ||
| } | ||
| try { | ||
| const updates = {}; | ||
| if (options.token) updates.api_key = options.token; | ||
| if (options.model) updates.model = options.model; | ||
| if (options.baseUrl) updates.base_url = options.baseUrl; | ||
| if (Object.keys(updates).length === 0) { | ||
| console.error(chalk.red('โ Error: No updates specified')); | ||
| console.log('\nAvailable options: --token, --model, --base-url'); | ||
| return; | ||
| } | ||
| const result = tokenManager.updateProvider(provider, updates); | ||
| if (result.success) { | ||
| console.log(chalk.green('โ ' + result.message)); | ||
| } else { | ||
| console.error(chalk.red('โ Error: ' + result.message)); | ||
| } | ||
| } catch (error) { | ||
| console.error(chalk.red('โ Error: ' + error.message)); | ||
| } | ||
| } | ||
| /** | ||
| * Remove provider | ||
| */ | ||
| function removeProvider(provider) { | ||
| if (!provider) { | ||
| console.error(chalk.red('โ Error: Provider name is required')); | ||
| console.log('\nUsage: devcompass llm remove <provider>'); | ||
| return; | ||
| } | ||
| try { | ||
| const result = tokenManager.removeProvider(provider); | ||
| if (result.success) { | ||
| console.log(chalk.green('โ ' + result.message)); | ||
| } else { | ||
| console.error(chalk.red('โ Error: ' + result.message)); | ||
| } | ||
| } catch (error) { | ||
| console.error(chalk.red('โ Error: ' + error.message)); | ||
| } | ||
| } | ||
| /** | ||
| * Enable/disable provider | ||
| */ | ||
| function toggleProvider(provider, enabled) { | ||
| if (!provider) { | ||
| console.error(chalk.red('โ Error: Provider name is required')); | ||
| return; | ||
| } | ||
| try { | ||
| const result = tokenManager.toggleProvider(provider, enabled); | ||
| if (result.success) { | ||
| console.log(chalk.green('โ ' + result.message)); | ||
| } else { | ||
| console.error(chalk.red('โ Error: ' + result.message)); | ||
| } | ||
| } catch (error) { | ||
| console.error(chalk.red('โ Error: ' + error.message)); | ||
| } | ||
| } | ||
| /** | ||
| * Test provider connection | ||
| */ | ||
| async function testProvider(provider) { | ||
| if (!provider) { | ||
| console.error(chalk.red('โ Error: Provider name is required')); | ||
| console.log('\nUsage: devcompass llm test <provider>'); | ||
| return; | ||
| } | ||
| try { | ||
| console.log(chalk.gray(`Testing connection to ${provider}...`)); | ||
| const result = await tokenManager.testProvider(provider); | ||
| if (result.success) { | ||
| console.log(chalk.green('โ ' + result.message)); | ||
| if (result.response) { | ||
| console.log(chalk.gray(` Response: ${result.response}`)); | ||
| } | ||
| } else { | ||
| console.log(chalk.red('โ Connection failed')); | ||
| console.log(chalk.red(' ' + result.message)); | ||
| } | ||
| } catch (error) { | ||
| console.error(chalk.red('โ Test failed: ' + error.message)); | ||
| } | ||
| } | ||
| /** | ||
| * Show usage statistics | ||
| */ | ||
| function showStats(options) { | ||
| try { | ||
| const providers = aiDatabase.getAllProviders(); | ||
| // Get current month stats | ||
| const now = new Date(); | ||
| const year = options.year || now.getFullYear(); | ||
| const month = options.month || now.getMonth() + 1; | ||
| const stats = aiDatabase.getUsageStats(year, month); | ||
| const formattedStats = costTracker.formatStats(stats, providers); | ||
| if (formattedStats.length === 0) { | ||
| console.log(chalk.yellow(`โ ๏ธ No usage data for ${year}-${month.toString().padStart(2, '0')}`)); | ||
| return; | ||
| } | ||
| console.log(chalk.bold(`\n๐ AI Usage Statistics - ${year}-${month.toString().padStart(2, '0')}\n`)); | ||
| let totalTokens = 0; | ||
| let totalCost = 0; | ||
| let totalRequests = 0; | ||
| formattedStats.forEach(s => { | ||
| console.log(chalk.bold(`${s.provider} (${s.model})`)); | ||
| console.log(` Requests: ${s.requests}`); | ||
| console.log(` Tokens: ${s.tokens.toLocaleString()}`); | ||
| console.log(` Cost: $${s.cost}`); | ||
| console.log(); | ||
| totalTokens += parseInt(s.tokens); | ||
| totalCost += parseFloat(s.cost); | ||
| totalRequests += parseInt(s.requests); | ||
| }); | ||
| console.log(chalk.bold('โ'.repeat(50))); | ||
| console.log(chalk.bold(`Total Requests: ${totalRequests}`)); | ||
| console.log(chalk.bold(`Total Tokens: ${totalTokens.toLocaleString()}`)); | ||
| console.log(chalk.bold(`Total Cost: $${totalCost.toFixed(4)}`)); | ||
| console.log(); | ||
| // Show cost projection | ||
| const daysInMonth = new Date(year, month, 0).getDate(); | ||
| const currentDay = now.getDate(); | ||
| const projectedCost = (totalCost / currentDay) * daysInMonth; | ||
| if (currentDay < daysInMonth) { | ||
| console.log(chalk.gray(`๐ Projected monthly cost: $${projectedCost.toFixed(2)}`)); | ||
| } | ||
| } catch (error) { | ||
| console.error(chalk.red('โ Error showing stats: ' + error.message)); | ||
| } | ||
| } | ||
| /** | ||
| * Show help | ||
| */ | ||
| function showHelp() { | ||
| console.log(chalk.bold('\n๐ค DevCompass LLM Management\n')); | ||
| console.log(chalk.bold('Commands:')); | ||
| console.log(' devcompass llm add Add a new LLM provider'); | ||
| console.log(' devcompass llm list List all providers'); | ||
| console.log(' devcompass llm default Set default provider'); | ||
| console.log(' devcompass llm update Update provider settings'); | ||
| console.log(' devcompass llm remove Remove a provider'); | ||
| console.log(' devcompass llm enable Enable a provider'); | ||
| console.log(' devcompass llm disable Disable a provider'); | ||
| console.log(' devcompass llm test Test provider connection'); | ||
| console.log(' devcompass llm stats Show usage statistics'); | ||
| console.log(chalk.bold('\nExamples:')); | ||
| console.log(' # Add OpenAI provider'); | ||
| console.log(' devcompass llm add --provider openai --token sk-xxx --model gpt-4'); | ||
| console.log(); | ||
| console.log(' # Add Anthropic provider'); | ||
| console.log(' devcompass llm add --provider anthropic --token sk-ant-xxx --model claude-3-5-sonnet-20241022'); | ||
| console.log(); | ||
| console.log(' # Add local Ollama'); | ||
| console.log(' devcompass llm add --provider local --model llama3 --base-url http://localhost:11434'); | ||
| console.log(); | ||
| console.log(' # Set default'); | ||
| console.log(' devcompass llm default openai'); | ||
| console.log(); | ||
| console.log(' # Test connection'); | ||
| console.log(' devcompass llm test openai'); | ||
| console.log(); | ||
| console.log(' # View usage'); | ||
| console.log(' devcompass llm stats'); | ||
| console.log(); | ||
| } | ||
| module.exports = { | ||
| addProvider, | ||
| listProviders, | ||
| setDefaultProvider, | ||
| updateProvider, | ||
| removeProvider, | ||
| toggleProvider, | ||
| testProvider, | ||
| showStats, | ||
| showHelp | ||
| }; |
| // src/utils/encryption.js | ||
| const crypto = require('crypto'); | ||
| const os = require('os'); | ||
| const ALGORITHM = 'aes-256-gcm'; | ||
| const KEY_LENGTH = 32; | ||
| const IV_LENGTH = 16; | ||
| const AUTH_TAG_LENGTH = 16; | ||
| // Generate encryption key from machine-specific data | ||
| function getEncryptionKey() { | ||
| const machineId = `${os.hostname()}-${os.userInfo().username}`; | ||
| return crypto.createHash('sha256').update(machineId).digest(); | ||
| } | ||
| /** | ||
| * Encrypt sensitive data (API keys) | ||
| */ | ||
| function encrypt(text) { | ||
| if (!text) return null; | ||
| const key = getEncryptionKey(); | ||
| const iv = crypto.randomBytes(IV_LENGTH); | ||
| const cipher = crypto.createCipheriv(ALGORITHM, key, iv); | ||
| let encrypted = cipher.update(text, 'utf8', 'hex'); | ||
| encrypted += cipher.final('hex'); | ||
| const authTag = cipher.getAuthTag(); | ||
| // Combine iv + authTag + encrypted | ||
| return iv.toString('hex') + authTag.toString('hex') + encrypted; | ||
| } | ||
| /** | ||
| * Decrypt sensitive data | ||
| */ | ||
| function decrypt(encryptedData) { | ||
| if (!encryptedData) return null; | ||
| try { | ||
| const key = getEncryptionKey(); | ||
| // Extract iv, authTag, and encrypted data | ||
| const iv = Buffer.from(encryptedData.slice(0, IV_LENGTH * 2), 'hex'); | ||
| const authTag = Buffer.from(encryptedData.slice(IV_LENGTH * 2, (IV_LENGTH + AUTH_TAG_LENGTH) * 2), 'hex'); | ||
| const encrypted = encryptedData.slice((IV_LENGTH + AUTH_TAG_LENGTH) * 2); | ||
| const decipher = crypto.createDecipheriv(ALGORITHM, key, iv); | ||
| decipher.setAuthTag(authTag); | ||
| let decrypted = decipher.update(encrypted, 'hex', 'utf8'); | ||
| decrypted += decipher.final('utf8'); | ||
| return decrypted; | ||
| } catch (error) { | ||
| console.error('Decryption failed:', error.message); | ||
| return null; | ||
| } | ||
| } | ||
| /** | ||
| * Mask API key for display | ||
| */ | ||
| function maskToken(token) { | ||
| if (!token || token.length < 12) return '***'; | ||
| return token.slice(0, 7) + '***' + token.slice(-4); | ||
| } | ||
| module.exports = { | ||
| encrypt, | ||
| decrypt, | ||
| maskToken | ||
| }; |
| // src/utils/stream-formatter.js | ||
| const chalk = require('chalk'); | ||
| class StreamFormatter { | ||
| constructor() { | ||
| this.buffer = ''; | ||
| this.isFirstChunk = true; | ||
| } | ||
| /** | ||
| * Format streaming chunk for terminal output | ||
| */ | ||
| formatChunk(chunk) { | ||
| if (this.isFirstChunk) { | ||
| this.isFirstChunk = false; | ||
| process.stdout.write('\n๐ค '); | ||
| } | ||
| process.stdout.write(chunk); | ||
| this.buffer += chunk; | ||
| } | ||
| /** | ||
| * Finish streaming | ||
| */ | ||
| finish() { | ||
| process.stdout.write('\n\n'); | ||
| const content = this.buffer; | ||
| this.buffer = ''; | ||
| this.isFirstChunk = true; | ||
| return content; | ||
| } | ||
| /** | ||
| * Show typing indicator | ||
| */ | ||
| showTyping(message = 'AI is thinking') { | ||
| process.stdout.write(chalk.gray(`${message}...`)); | ||
| } | ||
| /** | ||
| * Clear typing indicator | ||
| */ | ||
| clearTyping() { | ||
| process.stdout.write('\r' + ' '.repeat(50) + '\r'); | ||
| } | ||
| } | ||
| module.exports = new StreamFormatter(); |
+294
-71
@@ -6,2 +6,3 @@ #!/usr/bin/env node | ||
| const path = require('path'); | ||
| const fs = require('fs'); | ||
| const { analyze } = require('../src/commands/analyze'); | ||
@@ -25,3 +26,3 @@ const fix = require('../src/commands/fix'); | ||
| .name('devcompass') | ||
| .description('Dependency health checker with ecosystem intelligence') | ||
| .description('Dependency health checker with AI-powered insights') | ||
| .version(packageJson.version, '-v, --version', 'Display version information') | ||
@@ -31,5 +32,6 @@ .addHelpText('after', ` | ||
| ${chalk.gray('GitHub:')} ${chalk.cyan('https://github.com/AjayBThorat-20/devcompass')} | ||
| ${chalk.gray('New in v3.2.2:')} ๐ค AI-powered dependency analysis | ||
| `); | ||
| // Analyze command | ||
| // Analyze command (UPDATED with --ai flag) | ||
| program | ||
@@ -43,2 +45,4 @@ .command('analyze') | ||
| .option('--no-history', 'Skip saving snapshot to history database') | ||
| .option('--ai', '๐ค Get AI-powered insights and recommendations') | ||
| .option('--ai-provider <provider>', 'AI provider to use (openai, anthropic, google, local)') | ||
| .addHelpText('after', ` | ||
@@ -48,2 +52,3 @@ | ||
| ${chalk.cyan('devcompass analyze')} Analyze current project & save snapshot | ||
| ${chalk.cyan('devcompass analyze --ai')} Analyze with AI insights ๐ค | ||
| ${chalk.cyan('devcompass analyze --no-history')} Analyze without saving to history | ||
@@ -53,2 +58,9 @@ ${chalk.cyan('devcompass analyze --json')} Output as JSON | ||
| ${chalk.bold('๐ค AI-Powered Analysis:')} | ||
| โข Get intelligent recommendations | ||
| โข Understand why packages are outdated | ||
| โข Learn about breaking changes | ||
| โข See migration paths | ||
| โข Prioritized action plan | ||
| ${chalk.bold('History Tracking:')} | ||
@@ -135,5 +147,3 @@ โข Snapshots auto-saved to SQLite database | ||
| // =========================== | ||
| // NEW v3.2.1 - History command with FLEXIBLE DATE FORMATS | ||
| // =========================== | ||
| // History command with FLEXIBLE DATE FORMATS | ||
| program | ||
@@ -145,3 +155,2 @@ .command('history <subcommand>') | ||
| .option('--keep <number>', 'Number of snapshots to keep (cleanup only)', parseInt, 30) | ||
| // โ UPDATED: Flexible date filtering options | ||
| .option('--date <date>', 'Specific date (DD-MM-YYYY or YYYY-MM-DD)') | ||
@@ -177,24 +186,2 @@ .option('--from <date>', 'Start date (DD-MM-YYYY or YYYY-MM-DD)') | ||
| ${chalk.gray('devcompass history stats')} Show overall statistics | ||
| ${chalk.bold('Date Filtering Examples:')} | ||
| ${chalk.gray('devcompass history list --date 25-04-2026')} ${chalk.dim('# Specific day')} | ||
| ${chalk.gray('devcompass history list --month 04-2026')} ${chalk.dim('# Specific month')} | ||
| ${chalk.gray('devcompass history list --year 2026')} ${chalk.dim('# Specific year')} | ||
| ${chalk.gray('devcompass history list --from 01-04-2026 --to 30-04-2026')} ${chalk.dim('# Date range')} | ||
| ${chalk.gray('devcompass history list --after 15-04-2026')} ${chalk.dim('# After date')} | ||
| ${chalk.gray('devcompass history summary --year 2026')} ${chalk.dim('# Year summary')} | ||
| ${chalk.bold('Advanced Features:')} | ||
| โข Auto-groups snapshots by month when >20 results | ||
| โข Shows average health per month in grouped view | ||
| โข Filters work with all subcommands (list, summary) | ||
| โข Combine --project and date filters for targeted queries | ||
| โข Supports both European (DD-MM-YYYY) and ISO (YYYY-MM-DD) formats | ||
| ${chalk.bold('Database Location:')} | ||
| ${chalk.dim('~/.devcompass/history.db')} | ||
| ${chalk.bold('Note:')} | ||
| Snapshots are automatically saved when running ${chalk.cyan('devcompass analyze')} | ||
| Use ${chalk.cyan('--no-history')} flag to skip snapshot saving | ||
| `) | ||
@@ -206,3 +193,3 @@ .action(async (subcommand, options) => { | ||
| // NEW v3.2.1 - Compare command | ||
| // Compare command | ||
| program | ||
@@ -225,9 +212,2 @@ .command('compare <id1> <id2>') | ||
| โข Vulnerability status changes | ||
| โข Deprecated status changes | ||
| ${chalk.bold('Workflow:')} | ||
| 1. Run ${chalk.cyan('devcompass history list')} to see snapshot IDs | ||
| 2. Choose two snapshots to compare | ||
| 3. Run ${chalk.cyan('devcompass compare <id1> <id2>')} | ||
| 4. View side-by-side diff with changes highlighted | ||
| `) | ||
@@ -239,3 +219,3 @@ .action(async (id1, id2, options) => { | ||
| // NEW v3.2.1 - Timeline command | ||
| // Timeline command | ||
| program | ||
@@ -254,19 +234,2 @@ .command('timeline') | ||
| ${chalk.cyan('devcompass timeline --open')} Open interactive chart | ||
| ${chalk.cyan('devcompass timeline --days 90 --open')} 90-day interactive timeline | ||
| ${chalk.bold('Timeline Visualizations:')} | ||
| โข Health score trend over time | ||
| โข Dependency count changes | ||
| โข Vulnerability trends | ||
| โข Package quality evolution | ||
| ${chalk.bold('Interactive Features:')} | ||
| โข Zoom and pan | ||
| โข Click points for details | ||
| โข Compare date ranges | ||
| โข Export as PNG | ||
| ${chalk.bold('Note:')} | ||
| Timeline requires multiple snapshots over time | ||
| Run ${chalk.cyan('devcompass analyze')} regularly to build history | ||
| `) | ||
@@ -278,2 +241,278 @@ .action(async (options) => { | ||
| // =========================== | ||
| // NEW v3.2.2 - LLM Management Command - FIXED | ||
| // =========================== | ||
| program | ||
| .command('llm') | ||
| .description('๐ค Manage AI/LLM providers and settings') | ||
| .argument('[subcommand]', 'LLM subcommand (add, list, default, update, remove, enable, disable, test, stats)') | ||
| .argument('[provider]', 'Provider name for specific operations') | ||
| .option('--provider <name>', 'Provider name (openai, anthropic, google, local)') | ||
| .option('--token <token>', 'API token/key') | ||
| .option('--model <model>', 'Model name') | ||
| .option('--base-url <url>', 'Base URL (for local models)') | ||
| .option('--year <year>', 'Year for stats (default: current)') | ||
| .option('--month <month>', 'Month for stats (1-12)') | ||
| .addHelpText('after', ` | ||
| ${chalk.bold('๐ค LLM Subcommands:')} | ||
| ${chalk.cyan('add')} Add a new AI provider | ||
| ${chalk.cyan('list')} List all configured providers | ||
| ${chalk.cyan('default')} Set default provider | ||
| ${chalk.cyan('update')} Update provider settings | ||
| ${chalk.cyan('remove')} Remove a provider | ||
| ${chalk.cyan('enable')} Enable a provider | ||
| ${chalk.cyan('disable')} Disable a provider | ||
| ${chalk.cyan('test')} Test provider connection | ||
| ${chalk.cyan('stats')} Show AI usage statistics | ||
| ${chalk.bold('Provider Setup Examples:')} | ||
| ${chalk.bold('OpenAI (GPT-4):')} | ||
| ${chalk.gray('devcompass llm add --provider openai --token sk-xxx --model gpt-4')} | ||
| ${chalk.gray('devcompass llm add --provider openai --token sk-xxx --model gpt-4-turbo')} | ||
| ${chalk.gray('devcompass llm add --provider openai --token sk-xxx --model gpt-3.5-turbo')} | ||
| ${chalk.bold('Anthropic (Claude):')} | ||
| ${chalk.gray('devcompass llm add --provider anthropic --token sk-ant-xxx --model claude-3-5-sonnet-20241022')} | ||
| ${chalk.gray('devcompass llm add --provider anthropic --token sk-ant-xxx --model claude-3-opus-20240229')} | ||
| ${chalk.gray('devcompass llm add --provider anthropic --token sk-ant-xxx --model claude-3-haiku-20240307')} | ||
| ${chalk.bold('Google (Gemini):')} | ||
| ${chalk.gray('devcompass llm add --provider google --token xxx --model gemini-pro')} | ||
| ${chalk.gray('devcompass llm add --provider google --token xxx --model gemini-1.5-pro')} | ||
| ${chalk.bold('Local Models (Ollama):')} | ||
| ${chalk.gray('devcompass llm add --provider local --model llama3 --base-url http://localhost:11434')} | ||
| ${chalk.gray('devcompass llm add --provider local --model mistral --base-url http://localhost:11434')} | ||
| ${chalk.gray('devcompass llm add --provider local --model codellama --base-url http://localhost:11434')} | ||
| ${chalk.bold('Management Examples:')} | ||
| ${chalk.gray('devcompass llm list')} List all providers | ||
| ${chalk.gray('devcompass llm default openai')} Set OpenAI as default | ||
| ${chalk.gray('devcompass llm test openai')} Test OpenAI connection | ||
| ${chalk.gray('devcompass llm update openai --token sk-new')} Update token | ||
| ${chalk.gray('devcompass llm remove openai')} Remove OpenAI | ||
| ${chalk.gray('devcompass llm stats')} Show usage stats | ||
| ${chalk.bold('How to Get API Tokens:')} | ||
| ${chalk.bold('OpenAI:')} | ||
| 1. Visit: ${chalk.cyan('https://platform.openai.com/api-keys')} | ||
| 2. Create new API key | ||
| 3. Copy token (starts with sk-) | ||
| ${chalk.bold('Anthropic:')} | ||
| 1. Visit: ${chalk.cyan('https://console.anthropic.com/settings/keys')} | ||
| 2. Create API key | ||
| 3. Copy token (starts with sk-ant-) | ||
| ${chalk.bold('Google:')} | ||
| 1. Visit: ${chalk.cyan('https://makersuite.google.com/app/apikey')} | ||
| 2. Create API key | ||
| 3. Copy token | ||
| ${chalk.bold('Local (Ollama):')} | ||
| 1. Install: ${chalk.gray('curl -fsSL https://ollama.com/install.sh | sh')} | ||
| 2. Start: ${chalk.gray('ollama serve')} | ||
| 3. Pull model: ${chalk.gray('ollama pull llama3')} | ||
| 4. No token needed! | ||
| ${chalk.bold('Database Location:')} | ||
| ${chalk.dim('~/.devcompass/ai.db')} | ||
| ${chalk.bold('Security:')} | ||
| โข Tokens encrypted with AES-256-GCM | ||
| โข Stored locally (never sent to DevCompass) | ||
| โข Machine-specific encryption key | ||
| `) | ||
| .action(async (subcommand, provider, options) => { | ||
| const llmCommand = require('../src/commands/llm'); | ||
| // Handle provider from either argument or option | ||
| const providerName = provider || options.provider; | ||
| switch (subcommand) { | ||
| case 'add': | ||
| await llmCommand.addProvider(options); | ||
| break; | ||
| case 'list': | ||
| llmCommand.listProviders(); | ||
| break; | ||
| case 'default': | ||
| llmCommand.setDefaultProvider(providerName); | ||
| break; | ||
| case 'update': | ||
| llmCommand.updateProvider(providerName, options); | ||
| break; | ||
| case 'remove': | ||
| llmCommand.removeProvider(providerName); | ||
| break; | ||
| case 'enable': | ||
| llmCommand.toggleProvider(providerName, true); | ||
| break; | ||
| case 'disable': | ||
| llmCommand.toggleProvider(providerName, false); | ||
| break; | ||
| case 'test': | ||
| await llmCommand.testProvider(providerName); | ||
| break; | ||
| case 'stats': | ||
| llmCommand.showStats(options); | ||
| break; | ||
| default: | ||
| llmCommand.showHelp(); | ||
| } | ||
| }); | ||
| // =========================== | ||
| // NEW v3.2.2 - AI Analysis Command - FIXED | ||
| // =========================== | ||
| program | ||
| .command('ai') | ||
| .description('๐ค AI-powered dependency analysis and recommendations') | ||
| .argument('[subcommand]', 'AI subcommand (ask, recommend, alternatives, chat)') | ||
| .argument('[args...]', 'Additional arguments') | ||
| .option('--provider <name>', 'AI provider to use (openai, anthropic, google, local)') | ||
| .option('--stream', 'Stream responses in real-time', true) | ||
| .option('--no-stream', 'Wait for complete response') | ||
| .option('--verbose', 'Show detailed information (token usage, cost)') | ||
| .option('--max-tokens <number>', 'Maximum tokens per request', parseInt, 3000) | ||
| .addHelpText('after', ` | ||
| ${chalk.bold('๐ค AI Subcommands:')} | ||
| ${chalk.cyan('ask')} Ask AI about your dependencies | ||
| ${chalk.cyan('recommend')} Get AI-powered recommendations | ||
| ${chalk.cyan('alternatives')} Find package alternatives with AI | ||
| ${chalk.cyan('chat')} Start interactive AI chat | ||
| ${chalk.bold('AI Analysis Examples:')} | ||
| ${chalk.bold('Ask Questions:')} | ||
| ${chalk.gray('devcompass ai ask "Why is my health score low?"')} | ||
| ${chalk.gray('devcompass ai ask "Should I update axios now?"')} | ||
| ${chalk.gray('devcompass ai ask "What are the risks of updating to React 19?"')} | ||
| ${chalk.gray('devcompass ai ask "How do I fix this security vulnerability?"')} | ||
| ${chalk.bold('Get Recommendations:')} | ||
| ${chalk.gray('devcompass analyze --ai')} ${chalk.dim('# Analyze with AI insights')} | ||
| ${chalk.gray('devcompass ai recommend')} ${chalk.dim('# After analyze command')} | ||
| ${chalk.bold('Find Alternatives:')} | ||
| ${chalk.gray('devcompass ai alternatives moment')} ${chalk.dim('# Better alternatives to moment.js')} | ||
| ${chalk.gray('devcompass ai alternatives request')} ${chalk.dim('# Alternatives to deprecated packages')} | ||
| ${chalk.gray('devcompass ai alternatives lodash')} ${chalk.dim('# Smaller alternatives')} | ||
| ${chalk.bold('Interactive Chat:')} | ||
| ${chalk.gray('devcompass ai chat')} ${chalk.dim('# Start conversation')} | ||
| ${chalk.gray('devcompass ai chat --provider anthropic')} ${chalk.dim('# Use specific provider')} | ||
| ${chalk.bold('What AI Can Help With:')} | ||
| โข Explain why packages are outdated | ||
| โข Identify breaking changes in updates | ||
| โข Suggest migration strategies | ||
| โข Find better alternatives | ||
| โข Prioritize updates by risk | ||
| โข Explain security vulnerabilities | ||
| โข Provide step-by-step fixes | ||
| โข Answer questions about dependencies | ||
| ${chalk.bold('AI Capabilities:')} | ||
| โข Context-aware (knows your project details) | ||
| โข Real-time streaming responses | ||
| โข Natural language Q&A | ||
| โข Code examples and migration guides | ||
| โข Cost tracking (tokens + estimated $) | ||
| โข Conversation history | ||
| ${chalk.bold('Provider Selection:')} | ||
| Use ${chalk.cyan('--provider')} flag to choose: | ||
| โข ${chalk.cyan('openai')} - GPT-4, GPT-3.5-turbo (fast, accurate) | ||
| โข ${chalk.cyan('anthropic')} - Claude 3.5 Sonnet (smart, detailed) | ||
| โข ${chalk.cyan('google')} - Gemini Pro (good, free tier) | ||
| โข ${chalk.cyan('local')} - Llama3, Mistral (free, private) | ||
| If not specified, uses default provider from ${chalk.cyan('devcompass llm default')} | ||
| ${chalk.bold('Cost Information:')} | ||
| โข OpenAI GPT-4: ~$0.03 per 1K input tokens, ~$0.06 per 1K output | ||
| โข Anthropic Claude: ~$0.003 per 1K input, ~$0.015 per 1K output | ||
| โข Google Gemini: ~$0.00025 per 1K input, ~$0.0005 per 1K output | ||
| โข Local models: FREE! (runs on your machine) | ||
| View usage: ${chalk.cyan('devcompass llm stats')} | ||
| ${chalk.bold('Privacy & Security:')} | ||
| โข Your code is never sent to AI (only analysis metadata) | ||
| โข Tokens stored encrypted locally | ||
| โข Conversations saved locally for context | ||
| โข You control which provider to use | ||
| โข Local models = complete privacy | ||
| ${chalk.bold('First Time Setup:')} | ||
| 1. Add an AI provider: | ||
| ${chalk.gray('devcompass llm add --provider openai --token sk-xxx --model gpt-4')} | ||
| 2. Test connection: | ||
| ${chalk.gray('devcompass llm test openai')} | ||
| 3. Start using AI: | ||
| ${chalk.gray('devcompass analyze --ai')} | ||
| ${chalk.gray('devcompass ai ask "Help me understand my dependencies"')} | ||
| `) | ||
| .action(async (subcommand, args, options) => { | ||
| const aiCommand = require('../src/commands/ai'); | ||
| const contextBuilder = require('../src/ai/context-builder'); | ||
| // Get current analysis context if available | ||
| let context = null; | ||
| try { | ||
| const cacheFile = path.join(process.cwd(), '.devcompass-cache.json'); | ||
| if (fs.existsSync(cacheFile)) { | ||
| const cacheData = JSON.parse(fs.readFileSync(cacheFile, 'utf8')); | ||
| context = contextBuilder.buildAnalysisContext(cacheData); | ||
| } | ||
| } catch (error) { | ||
| // No context available, continue without it | ||
| } | ||
| switch (subcommand) { | ||
| case 'ask': | ||
| const question = args.join(' '); | ||
| if (!question) { | ||
| console.log(chalk.red('โ Error: Question is required')); | ||
| console.log('\nUsage: devcompass ai ask "your question here"'); | ||
| return; | ||
| } | ||
| await aiCommand.askQuestion(question, context, options); | ||
| break; | ||
| case 'recommend': | ||
| if (!context) { | ||
| console.log(chalk.yellow('โ ๏ธ No recent analysis found. Run "devcompass analyze" first.')); | ||
| return; | ||
| } | ||
| const analysisData = JSON.parse(fs.readFileSync(path.join(process.cwd(), '.devcompass-cache.json'), 'utf8')); | ||
| await aiCommand.getRecommendations(analysisData, options); | ||
| break; | ||
| case 'alternatives': | ||
| const packageName = args[0]; | ||
| if (!packageName) { | ||
| console.log(chalk.red('โ Error: Package name is required')); | ||
| console.log('\nUsage: devcompass ai alternatives <package-name>'); | ||
| return; | ||
| } | ||
| await aiCommand.getAlternatives(packageName, options); | ||
| break; | ||
| case 'chat': | ||
| await aiCommand.startChat(context, options); | ||
| break; | ||
| default: | ||
| aiCommand.showHelp(); | ||
| } | ||
| }); | ||
| // Config command | ||
@@ -297,21 +536,5 @@ program | ||
| โข Track 500+ popular npm packages | ||
| ${chalk.bold('How to Get a Token:')} | ||
| 1. Go to: ${chalk.cyan('https://github.com/settings/tokens/new')} | ||
| 2. Select: "Classic" token | ||
| 3. Description: "DevCompass CLI" | ||
| 4. Expiration: 90 days (or your preference) | ||
| 5. Scopes: Select "${chalk.green('public_repo')}" (read access only) | ||
| 6. Click "Generate token" | ||
| 7. Copy the token (starts with ghp_) | ||
| 8. Run: ${chalk.cyan('devcompass config --github-token <your-token>')} | ||
| ${chalk.bold('Security:')} | ||
| โข Token stored locally in ${chalk.dim('~/.devcompass/github-token')} | ||
| โข Only you have access to this file | ||
| โข Never committed to git | ||
| โข Optional - DevCompass works without it (with rate limits) | ||
| `) | ||
| .action(config); | ||
| program.parse(); |
+3
-2
| { | ||
| "name": "devcompass", | ||
| "version": "3.2.1", | ||
| "version": "3.2.2", | ||
| "description": "Dependency health checker with ecosystem intelligence, unified interactive dashboard with 5 dynamic layouts (Tree/Force/Radial/Conflict/Analytics), historical tracking with SQLite, snapshot comparison, timeline visualization, modular CSS/JS architecture, intelligent clustering, real-time filtering, advanced zoom controls, theme support (dark/light), supply chain security with auto-fix, license conflict resolution, package quality auto-fix, batch fix modes, backup & rollback, and professional dependency exploration.", | ||
@@ -175,3 +175,4 @@ "main": "src/index.js", | ||
| "open": "^10.2.0", | ||
| "ora": "5.4.1" | ||
| "ora": "5.4.1", | ||
| "uuid": "^14.0.0" | ||
| }, | ||
@@ -178,0 +179,0 @@ "engines": { |
+799
-413
| # ๐งญ DevCompass | ||
| **Dependency health checker with unified interactive dashboard featuring 5 dynamic layouts (Tree/Force/Radial/Conflict/Analytics), modular CSS/JS architecture, intelligent clustering (Ecosystem/Health/Depth grouping), real-time filtering, advanced zoom controls, theme support (dark/light), supply chain security with auto-fix, license conflict resolution, package quality auto-fix, batch fix modes, backup & rollback, historical tracking with SQLite database, snapshot comparison, timeline visualization, and professional dependency exploration.** | ||
| **AI-powered dependency health checker with unified interactive dashboard featuring 5 dynamic layouts (Tree/Force/Radial/Conflict/Analytics), intelligent AI recommendations, multi-provider LLM support, modular CSS/JS architecture, intelligent clustering (Ecosystem/Health/Depth grouping), real-time filtering, advanced zoom controls, theme support (dark/light), supply chain security with auto-fix, license conflict resolution, package quality auto-fix, batch fix modes, backup & rollback, historical tracking with SQLite database, snapshot comparison, timeline visualization, and professional dependency exploration.** | ||
@@ -9,165 +9,97 @@ [](https://www.npmjs.com/package/devcompass) | ||
| Analyze your JavaScript projects to find unused dependencies, outdated packages, **detect security vulnerabilities**, **monitor GitHub issues in real-time for 502 packages**, **configure your own GitHub token to avoid rate limits**, **customize all configuration via JSON files**, **visualize dependency graphs with 5 dynamic layouts including Analytics dashboard**, **modular architecture with zero code duplication**, **organize packages by ecosystem (React/Vue/Angular/Testing/Build Tools)**, **group by health status (Critical/Warning/Healthy)**, **analyze by depth levels**, **instant layout switching**, **dark/light theme toggle**, **real-time filtering**, **advanced zoom controls**, **track dependency changes over time with SQLite database**, **compare snapshots to see what changed**, **visualize evolution with interactive timelines**, **check bundle sizes**, **verify licenses**, **detect and auto-fix supply chain attacks**, **resolve license conflicts automatically**, **replace abandoned/deprecated packages automatically**, **analyze package quality**, **batch fix with granular control**, **manage backups and rollback changes**, and **automatically fix issues with dry-run, progress tracking, and backups**. Perfect for **CI/CD pipelines** with JSON output and exit codes. | ||
| Analyze your JavaScript projects to find unused dependencies, outdated packages, **detect security vulnerabilities**, **get AI-powered recommendations**, **ask questions about your dependencies**, **find package alternatives with AI**, **chat with AI about your project**, **monitor GitHub issues in real-time for 502 packages**, **configure your own GitHub token to avoid rate limits**, **customize all configuration via JSON files**, **visualize dependency graphs with 5 dynamic layouts including Analytics dashboard**, **modular architecture with zero code duplication**, **organize packages by ecosystem (React/Vue/Angular/Testing/Build Tools)**, **group by health status (Critical/Warning/Healthy)**, **analyze by depth levels**, **instant layout switching**, **dark/light theme toggle**, **real-time filtering**, **advanced zoom controls**, **track dependency changes over time with SQLite database**, **compare snapshots to see what changed**, **visualize evolution with interactive timelines**, **check bundle sizes**, **verify licenses**, **detect and auto-fix supply chain attacks**, **resolve license conflicts automatically**, **replace abandoned/deprecated packages automatically**, **analyze package quality**, **batch fix with granular control**, **manage backups and rollback changes**, and **automatically fix issues with dry-run, progress tracking, and backups**. Perfect for **CI/CD pipelines** with JSON output and exit codes. | ||
| > **๐ LATEST v3.2.1:** Historical Tracking System - Track changes, compare snapshots, visualize trends! ๐ | ||
| > **๐จ v3.2.0:** Unified Dashboard Architecture - 50% less code, 5 layouts, dark/light themes! ๐จ | ||
| > **๐ง v3.1.7:** Dynamic Data Configuration - Scalable JSON-based configuration system! ๐ง | ||
| > **๐ค LATEST v3.2.2:** AI-Powered Analysis - Get intelligent recommendations from OpenAI, Anthropic, Google, or FREE local AI! ๐ค | ||
| > **๐ v3.2.1:** Historical Tracking System - Track changes, compare snapshots, visualize trends! ๐ | ||
| > **๐จ v3.2.0:** Unified Dashboard Architecture - 50% less code, 5 layouts, dark/light themes! ๐จ | ||
| ## ๐ Latest Release: v3.2.1 (2026-04-26) | ||
| ## ๐ Latest Release: v3.2.2 (2026-04-26) | ||
| **Historical Tracking System - Track Your Dependency Evolution!** | ||
| **AI-Powered Dependency Analysis - Smart Recommendations from AI!** | ||
| ### ๐ What's New in v3.2.1: | ||
| ### ๐ What's New in v3.2.2: | ||
| #### **๐ Historical Tracking with SQLite** | ||
| #### **๐ค AI-Powered Analysis** | ||
| Complete dependency history tracking with SQLite database backend. | ||
| Get intelligent, context-aware recommendations from AI to help you maintain healthier dependencies. | ||
| **Features:** | ||
| - โ **Auto-save snapshots** - Every analysis saved to database | ||
| - โ **SQLite storage** - Fast, reliable, local database (~/.devcompass/history.db) | ||
| - โ **Optimized performance** - 8-19ms save time (6-11ร faster than target) | ||
| - โ **Flexible date formats** - 9 formats including DD-MM-YYYY, MM-YYYY, YYYY | ||
| - โ **Smart grouping** - Auto-groups >20 snapshots by month | ||
| - โ **Monthly summaries** - Aggregated statistics per month | ||
| - โ **Zero configuration** - Works automatically | ||
| - โ **Multi-Provider Support** - OpenAI, Anthropic (Claude), Google (Gemini), Ollama (FREE local) | ||
| - โ **Encrypted Token Storage** - AES-256-GCM encryption for API keys | ||
| - โ **Context-Aware** - AI analyzes your specific project data | ||
| - โ **Real-Time Streaming** - See responses as they're generated | ||
| - โ **Interactive Chat** - Multi-turn conversations about your dependencies | ||
| - โ **Cost Tracking** - Monitor token usage and estimated costs | ||
| - โ **FREE Option** - Use local AI with Ollama (no API costs) | ||
| - โ **Package Alternatives** - AI suggests better replacements | ||
| - โ **Smart Prioritization** - Critical โ High โ Medium recommendations | ||
| **Database Structure:** | ||
| ``` | ||
| ~/.devcompass/history.db | ||
| โโโ snapshots # Project snapshots with metadata | ||
| โโโ packages # Package details per snapshot | ||
| โโโ dependencies # Dependency relationships | ||
| ``` | ||
| **Performance:** | ||
| - Snapshot save: 8-19ms (6-11ร faster than target) | ||
| - Query speed: <10ms for all operations | ||
| - Database size: ~3KB per snapshot (40% better than target) | ||
| #### **๐ Snapshot Comparison** | ||
| Compare any two snapshots to see exactly what changed: | ||
| **AI Commands:** | ||
| ```bash | ||
| # Compare two snapshots | ||
| devcompass compare 5 8 | ||
| # Setup AI provider | ||
| devcompass llm add --provider openai --token sk-xxx --model gpt-4o-mini | ||
| devcompass llm test openai | ||
| # Detailed comparison | ||
| devcompass compare 5 8 --verbose | ||
| # Or use FREE local AI | ||
| devcompass llm add --provider local --model llama3.2 --base-url http://localhost:11434 | ||
| # Save to file | ||
| devcompass compare 5 8 -o report.md | ||
| ``` | ||
| # Get AI-powered analysis | ||
| devcompass analyze --ai | ||
| **What Gets Compared:** | ||
| - โ Added/removed packages | ||
| - โ Version changes | ||
| - โ Health score changes | ||
| - โ Vulnerability status changes | ||
| - โ Deprecated status changes | ||
| # Ask questions | ||
| devcompass ai ask "Should I update axios to version 1.15.2?" | ||
| devcompass ai ask "What are the security risks in my project?" | ||
| **Output Example:** | ||
| # Find alternatives | ||
| devcompass ai alternatives moment | ||
| ``` | ||
| ๐ Snapshot Comparison | ||
| Snapshots: #5 โ #8 | ||
| Health Score: 8.20 โ 6.20 (-2.00) โ | ||
| ๐ Updated Packages (9): | ||
| โณ axios | ||
| Health: 8.2 โ 6.2 (-2.0) | ||
| ``` | ||
| # Interactive chat | ||
| devcompass ai chat | ||
| ๐ Snapshot Comparison | ||
| Snapshots: #5 โ #8 | ||
| Health Score: 8.20 โ 6.20 (-2.00) โ | ||
| ๐ Updated Packages (9): | ||
| โณ axios | ||
| Health: 8.2 โ 6.2 (-2.0) | ||
| # View usage | ||
| devcompass llm stats | ||
| ``` | ||
| ๐ Snapshot History (Grouped by Month) | ||
| ๐ April 2026 (22 snapshots, Avg Health: 7.71) | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| #24 25, 07:17 PM Deps: 9 Health: 6.2 | ||
| #23 25, 07:17 PM Deps: 9 Health: 6.2 | ||
| ... | ||
| #3 25, 06:15 PM Deps: 7 Health: 7.7 | ||
| Total: 22 snapshots | ||
| ``` | ||
| **Features:** | ||
| - โ Automatic grouping when >20 snapshots | ||
| - โ Monthly average health scores | ||
| - โ Snapshot count per month | ||
| - โ Clean, organized display | ||
| **What AI Can Help With:** | ||
| - ๐ **Explain why** packages are outdated | ||
| - ๐ก๏ธ **Identify** breaking changes in updates | ||
| - ๐ **Suggest** migration strategies | ||
| - ๐ **Find** better package alternatives | ||
| - โก **Prioritize** updates by risk level | ||
| - ๐ **Explain** security vulnerabilities | ||
| - ๐ **Provide** step-by-step fixes | ||
| #### **๐ง Bug Fixes** | ||
| **Privacy & Security:** | ||
| - ๐ Tokens encrypted with AES-256-GCM | ||
| - ๐พ Stored locally in `~/.devcompass/ai.db` | ||
| - ๐ซ Your code is never sent to AI (only analysis metadata) | ||
| - ๐ FREE local option with complete privacy | ||
| **Fixed Typosquatting False Positives:** | ||
| - โ Changed distance threshold from 2 to 1 character | ||
| - โ Only flags real typosquats (1-char difference) | ||
| - โ Added comprehensive whitelist for legitimate packages | ||
| - โ Fixed `knip` โ `knex` false alarm | ||
| **Supported AI Providers:** | ||
| **Fixed Dynamic Security:** | ||
| - โ Corrected `similarTo` property name | ||
| - โ Added proper null checks | ||
| - โ Improved error handling | ||
| | Provider | Models | Cost | Best For | | ||
| |----------|--------|------|----------| | ||
| | **OpenAI** | GPT-4, GPT-4 Turbo, GPT-3.5 | ~$0.03/1K tokens | Fast, accurate responses | | ||
| | **Anthropic** | Claude 3.5 Sonnet, Opus, Haiku | ~$0.003/1K tokens | Detailed analysis | | ||
| | **Google** | Gemini Pro, Gemini 1.5 Pro | ~$0.00025/1K tokens | Cost-effective | | ||
| | **Ollama** | Llama 3, Mistral, CodeLlama | **FREE** | Privacy, no limits | | ||
| ### ๐ Performance Metrics | ||
| #### **๐ฐ Cost Comparison** | ||
| | Operation | Target | Actual | Improvement | | ||
| |-----------|--------|--------|-------------| | ||
| | Snapshot Save | <100ms | 8-19ms | **6-11ร faster** | | ||
| | Snapshot Load | <50ms | ~4ms | **12ร faster** | | ||
| | Comparison | <200ms | 4-5ms | **50ร faster** | | ||
| | Timeline Gen | <500ms | 6ms | **83ร faster** | | ||
| | Database Size | ~5KB | ~3KB | **40% smaller** | | ||
| **Example: 50 AI queries per month** | ||
| **Average Improvement: 40ร faster than targets!** | ||
| | Provider | Monthly Cost | | ||
| |----------|--------------| | ||
| | OpenAI GPT-4 | ~$4.50 | | ||
| | Anthropic Claude | ~$0.90 | | ||
| | Google Gemini | ~$0.04 | | ||
| | **Ollama (Local)** | **$0.00 FREE!** | | ||
| ### ๐ Upgrade Now: | ||
| ```bash | ||
| npm install -g devcompass@3.2.1 | ||
| ``` | ||
| ### ๐ก What Changed for Users: | ||
| **New Commands:** | ||
| ```bash | ||
| # History management | ||
| devcompass history list | ||
| devcompass history show <id> | ||
| devcompass history summary | ||
| devcompass history cleanup | ||
| devcompass history stats | ||
| # Comparison | ||
| devcompass compare <id1> <id2> | ||
| # Timeline | ||
| devcompass timeline | ||
| devcompass timeline --open | ||
| ``` | ||
| **Auto-save Feature:** | ||
| ```bash | ||
| # Snapshots auto-saved on analyze | ||
| devcompass analyze # Saves to database | ||
| # Disable if needed | ||
| devcompass analyze --no-history | ||
| ``` | ||
| **New Files Created:** | ||
| - `~/.devcompass/history.db` - SQLite database | ||
| - Timeline HTML files when exported | ||
| --- | ||
| ## โจ All Features | ||
| - ๐ค **AI-Powered Analysis** (v3.2.2) - Multi-provider LLM integration | ||
| - ๐ฌ **Interactive AI Chat** (v3.2.2) - Ask questions, get answers | ||
| - ๐ **Package Alternatives** (v3.2.2) - AI-suggested replacements | ||
| - ๐ **Encrypted Tokens** (v3.2.2) - AES-256-GCM security | ||
| - ๐ **Cost Tracking** (v3.2.2) - Monitor AI usage | ||
| - ๐ **Historical Tracking** (v3.2.1) - SQLite database, auto-save snapshots | ||
@@ -185,10 +117,2 @@ - ๐ **Snapshot Comparison** (v3.2.1) - Side-by-side diff analysis | ||
| - ๐ฆ **502 Tracked Packages** (v3.1.5) - Comprehensive monitoring | ||
| - ๐ **Dynamic Issues** (v3.1.2) - Real-time npm audit | ||
| - ๐ก๏ธ **Production Safety** (v3.1.1) - Bug fixes and hardening | ||
| - ๐ **Multiple Layouts** (v3.1.0) - Tree/Force/Radial/Conflict | ||
| - ๐ฆ **Batch Fix Modes** (v2.8.5) - Granular control | ||
| - ๐พ **Backup & Rollback** (v2.8.4) - Safe management | ||
| - ๐ฆ **Quality Auto-Fix** (v2.8.3) - Replace abandoned packages | ||
| - โ๏ธ **License Auto-Fix** (v2.8.2) - GPL/AGPL replacement | ||
| - ๐ก๏ธ **Supply Chain Auto-Fix** (v2.8.1) - Remove malicious packages | ||
@@ -199,12 +123,12 @@ ## ๐ Installation | ||
| # Global (recommended) | ||
| npm install -g devcompass@3.2.1 | ||
| npm install -g devcompass@3.2.2 | ||
| # Local | ||
| npm install --save-dev devcompass@3.2.1 | ||
| npm install --save-dev devcompass@3.2.2 | ||
| # One-time use | ||
| npx devcompass@3.2.1 analyze | ||
| npx devcompass@3.2.2 analyze | ||
| # Upgrade from any version | ||
| npm install -g devcompass@3.2.1 | ||
| npm install -g devcompass@3.2.2 | ||
| ``` | ||
@@ -223,2 +147,3 @@ | ||
| devcompass analyze | ||
| devcompass analyze --ai # ๐ค With AI recommendations! | ||
| devcompass analyze --no-history # Skip snapshot | ||
@@ -233,22 +158,43 @@ | ||
| devcompass fix --dry-run | ||
| ``` | ||
| # Batch modes | ||
| devcompass fix --batch-mode critical | ||
| devcompass fix --batch-mode high | ||
| devcompass fix --batch-mode all | ||
| ### AI Commands (NEW in v3.2.2) | ||
| # Category-specific | ||
| devcompass fix --only quality | ||
| devcompass fix --skip updates | ||
| ```bash | ||
| # Setup AI provider | ||
| devcompass llm add --provider openai --token sk-xxx --model gpt-4o-mini | ||
| devcompass llm add --provider anthropic --token sk-ant-xxx --model claude-3-5-sonnet | ||
| devcompass llm add --provider google --token xxx --model gemini-pro | ||
| devcompass llm add --provider local --model llama3.2 --base-url http://localhost:11434 | ||
| # Manage backups | ||
| devcompass backup list | ||
| devcompass backup restore --name <backup> | ||
| # Manage providers | ||
| devcompass llm list | ||
| devcompass llm default openai | ||
| devcompass llm test openai | ||
| devcompass llm remove openai | ||
| devcompass llm stats | ||
| # CI/CD | ||
| devcompass analyze --json | ||
| devcompass analyze --ci | ||
| # AI Analysis | ||
| devcompass analyze --ai | ||
| devcompass analyze --ai --ai-provider anthropic | ||
| # Ask AI questions | ||
| devcompass ai ask "Why is my health score low?" | ||
| devcompass ai ask "Should I update axios now?" | ||
| devcompass ai ask "What are the breaking changes in React 19?" | ||
| # Get recommendations | ||
| devcompass ai recommend | ||
| # Find alternatives | ||
| devcompass ai alternatives moment | ||
| devcompass ai alternatives request | ||
| devcompass ai alternatives lodash | ||
| # Interactive chat | ||
| devcompass ai chat | ||
| devcompass ai chat --provider anthropic | ||
| ``` | ||
| ### History Commands (NEW in v3.2.1) | ||
| ### History Commands (v3.2.1) | ||
@@ -259,56 +205,173 @@ ```bash | ||
| devcompass history list --limit 50 | ||
| devcompass history list --date 25-04-2026 | ||
| devcompass history list --month 04-2026 | ||
| devcompass history list --year 2026 | ||
| devcompass history list --from 01-04-2026 --to 30-04-2026 | ||
| # View snapshot details | ||
| devcompass history show 5 | ||
| # Compare snapshots | ||
| devcompass compare 5 8 | ||
| devcompass compare 5 8 --verbose | ||
| # Monthly summary | ||
| devcompass history summary | ||
| devcompass history summary --year 2026 | ||
| # Timeline | ||
| devcompass timeline --open | ||
| ``` | ||
| # Cleanup old snapshots | ||
| devcompass history cleanup --keep 30 | ||
| --- | ||
| # Statistics | ||
| devcompass history stats | ||
| ## ๐ค AI-Powered Analysis Guide (v3.2.2) | ||
| ### Quick Start | ||
| **1. Install Ollama (FREE local AI):** | ||
| ```bash | ||
| # Install Ollama | ||
| curl -fsSL https://ollama.com/install.sh | sh | ||
| # Start Ollama | ||
| ollama serve | ||
| # Pull a model | ||
| ollama pull llama3.2 | ||
| # Add to DevCompass | ||
| devcompass llm add --provider local --model llama3.2 --base-url http://localhost:11434 | ||
| # Test it | ||
| devcompass llm test local | ||
| # Use it! | ||
| devcompass analyze --ai | ||
| ``` | ||
| ### Comparison Commands (NEW in v3.2.1) | ||
| **2. Or use OpenAI:** | ||
| ```bash | ||
| # Compare snapshots | ||
| devcompass compare 5 8 | ||
| devcompass compare 5 8 --verbose | ||
| devcompass compare 5 8 -o report.md | ||
| # Get API key from: https://platform.openai.com/api-keys | ||
| # Add provider | ||
| devcompass llm add --provider openai --token sk-your-key --model gpt-4o-mini | ||
| # Test connection | ||
| devcompass llm test openai | ||
| # Use it! | ||
| devcompass analyze --ai | ||
| ``` | ||
| ### Timeline Commands (NEW in v3.2.1) | ||
| ### Example AI Interactions | ||
| **Get Analysis:** | ||
| ```bash | ||
| # Generate timeline | ||
| devcompass timeline | ||
| devcompass timeline --days 60 | ||
| devcompass timeline --open | ||
| devcompass timeline --output my-timeline.html --open | ||
| $ devcompass analyze --ai | ||
| ๐ค AI Recommendations | ||
| ๐ด CRITICAL (Do Now): | ||
| - Security Vulnerabilities (24 total) | ||
| โ Run: npm audit fix | ||
| โ Why: 3 high-severity issues expose your app to attacks | ||
| ๐ก HIGH PRIORITY (This Week): | ||
| - Update axios (0.21.1 โ 1.15.2) | ||
| โ Why: Contains known CVEs | ||
| โ Breaking changes: Response format changed | ||
| ``` | ||
| ### Graph Commands | ||
| **Ask Questions:** | ||
| ```bash | ||
| $ devcompass ai ask "Should I update axios from 0.21.1 to 1.15.2?" | ||
| ๐ค Yes, you should update axios: | ||
| Security: Version 0.21.1 has critical vulnerabilities (CVE-2023-xxxx) | ||
| Breaking Changes: Response.data format changed, error handling updated | ||
| Migration: Update interceptors, test error handling | ||
| Command: npm install axios@latest | ||
| Test thoroughly before deploying! | ||
| ``` | ||
| **Find Alternatives:** | ||
| ```bash | ||
| # Generate unified dashboard | ||
| devcompass graph | ||
| $ devcompass ai alternatives moment | ||
| # Open in browser | ||
| devcompass graph --open | ||
| ๐ Finding alternatives for "moment" | ||
| # Custom output | ||
| devcompass graph --output my-deps.html --open | ||
| ๐ค Top 3 Alternatives: | ||
| # JSON export | ||
| devcompass graph --format json --output data.json | ||
| 1. date-fns (~2KB vs 67KB) | ||
| - Tree-shakeable, modern API | ||
| - Migration: Easy (similar methods) | ||
| 2. dayjs (~2KB) | ||
| - moment.js compatible API | ||
| - Migration: Drop-in replacement | ||
| 3. Luxon (~15KB) | ||
| - Better timezone support | ||
| - Migration: Medium (different API) | ||
| Recommendation: Use date-fns for best bundle size | ||
| ``` | ||
| **Interactive Chat:** | ||
| ```bash | ||
| $ devcompass ai chat | ||
| ๐ค DevCompass AI Assistant | ||
| Ask me anything about your dependencies! | ||
| You: Should I remove lodash and moment since they're unused? | ||
| ๐ค Yes, remove them: | ||
| - lodash: 1.3 MB saved | ||
| - moment: 4.1 MB saved | ||
| Total saved: 5.4 MB | ||
| Command: npm uninstall lodash moment | ||
| This will improve your health score from 0.5/10 to ~5.3/10! | ||
| You: exit | ||
| ๐ Chat ended. Used 245 tokens (~$0.0001) | ||
| ``` | ||
| ### Cost Tracking | ||
| ```bash | ||
| $ devcompass llm stats | ||
| ๐ AI Usage Statistics - 2026-04 | ||
| local (llama3.2) | ||
| Requests: 28 | ||
| Tokens: 11,923 | ||
| Cost: $0.0000 | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| Total Requests: 28 | ||
| Total Tokens: 11,923 | ||
| Total Cost: $0.0000 | ||
| ๐ Projected monthly cost: $0.00 | ||
| ``` | ||
| ### Privacy & Security | ||
| **What Gets Sent to AI:** | ||
| - โ Package names and versions | ||
| - โ Vulnerability counts | ||
| - โ Health score | ||
| - โ Outdated/unused package lists | ||
| **What Doesn't Get Sent:** | ||
| - โ Your source code | ||
| - โ File contents | ||
| - โ Environment variables | ||
| - โ API keys | ||
| **Encryption:** | ||
| - AES-256-GCM encryption for API tokens | ||
| - Machine-specific encryption keys | ||
| - Tokens stored in `~/.devcompass/ai.db` | ||
| - Never sent to DevCompass servers | ||
| --- | ||
@@ -341,229 +404,535 @@ | ||
| **Output:** | ||
| **3. Compare Changes** | ||
| ```bash | ||
| devcompass compare 38 40 | ||
| ``` | ||
| ๐ Snapshot History | ||
| ID Date & Time Project Deps Health | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| 40 Apr 26, 2026, 02:30 PMdevcompass 6 7.0 | ||
| 39 Apr 25, 2026, 08:20 PMdevcompass 6 7.0 | ||
| 38 Apr 25, 2026, 08:19 PMdevcompass 6 7.0 | ||
| **4. Visualize Trends** | ||
| ```bash | ||
| devcompass timeline --open | ||
| ``` | ||
| **3. Compare Changes** | ||
| --- | ||
| ## ๐ Troubleshooting | ||
| ### AI-Related Issues | ||
| **"No AI provider configured"** | ||
| ```bash | ||
| devcompass compare 38 40 | ||
| # Add a provider first | ||
| devcompass llm add --provider local --model llama3.2 --base-url http://localhost:11434 | ||
| # Or use OpenAI | ||
| devcompass llm add --provider openai --token sk-xxx --model gpt-4o-mini | ||
| ``` | ||
| **Output:** | ||
| **"Ollama connection failed"** | ||
| ```bash | ||
| # Check Ollama is running | ||
| ps aux | grep ollama | ||
| # Restart Ollama | ||
| ollama serve & | ||
| # Test connection | ||
| devcompass llm test local | ||
| ``` | ||
| ๐ Snapshot Comparison | ||
| Snapshots: #38 โ #40 | ||
| Health Score: 7.00 โ 7.00 (0.00) | ||
| Changes: | ||
| Added: 0 | ||
| Removed: 0 | ||
| Updated: 2 | ||
| Unchanged: 4 | ||
| **"API key invalid"** | ||
| ```bash | ||
| # Update token | ||
| devcompass llm update openai --token sk-new-token | ||
| # Test it | ||
| devcompass llm test openai | ||
| ``` | ||
| **4. Visualize Trends** | ||
| **"Quota exceeded"** | ||
| ```bash | ||
| # Check usage | ||
| devcompass llm stats | ||
| # Switch to free provider | ||
| devcompass llm add --provider local --model llama3.2 --base-url http://localhost:11434 | ||
| devcompass llm default local | ||
| ``` | ||
| ### Common Issues | ||
| **"Command not found"** | ||
| ```bash | ||
| devcompass timeline --open | ||
| npm install -g devcompass@3.2.2 | ||
| ``` | ||
| Opens interactive HTML chart showing: | ||
| - Health score over time | ||
| - Dependency count changes | ||
| - Trend analysis (improving/declining/stable) | ||
| **Old version** | ||
| ```bash | ||
| npm update -g devcompass | ||
| devcompass --version # Should show 3.2.2 | ||
| ``` | ||
| ### Date Filtering | ||
| --- | ||
| Query snapshots using flexible date formats: | ||
| ## ๐ค Contributing | ||
| Contributions welcome! | ||
| ### Ways to Contribute: | ||
| 1. **Add Package Alternatives** | ||
| - Edit `data/quality-alternatives.json` | ||
| - Submit PR with new deprecated package alternatives | ||
| 2. **Improve AI Prompts** | ||
| - Edit `src/ai/prompt-templates.js` | ||
| - Make recommendations more helpful | ||
| 3. **Add AI Providers** | ||
| - Create new provider in `src/ai/providers/` | ||
| - Follow existing provider patterns | ||
| 4. **Code Contributions** | ||
| - Fork the repository | ||
| - Create feature branch (`git checkout -b feature/amazing`) | ||
| - Commit changes (`git commit -m 'Add feature'`) | ||
| - Push branch (`git push origin feature/amazing`) | ||
| - Open Pull Request | ||
| --- | ||
| ## ๐ License | ||
| MIT ยฉ [Ajay Thorat](https://github.com/AjayBThorat-20) | ||
| --- | ||
| ## ๐ What's Next? | ||
| ### Completed Features: | ||
| - [x] AI-powered analysis (v3.2.2) โ | ||
| - [x] Multi-provider LLM support (v3.2.2) โ | ||
| - [x] Interactive AI chat (v3.2.2) โ | ||
| - [x] Package alternatives with AI (v3.2.2) โ | ||
| - [x] Historical tracking (v3.2.1) โ | ||
| - [x] Snapshot comparison (v3.2.1) โ | ||
| - [x] Timeline visualization (v3.2.1) โ | ||
| - [x] Unified dashboard (v3.2.0) โ | ||
| - [x] Intelligent clustering (v3.1.6) โ | ||
| ### Planned Features: | ||
| - [ ] **Web Dashboard** - Team health monitoring with AI insights | ||
| - [ ] **Monorepo Support** - Multi-project AI analysis | ||
| Want to contribute? Pick a feature and open an issue! ๐ | ||
| ---# ๐งญ DevCompass | ||
| **AI-powered dependency health checker with unified interactive dashboard featuring 5 dynamic layouts (Tree/Force/Radial/Conflict/Analytics), intelligent AI recommendations, multi-provider LLM support, modular CSS/JS architecture, intelligent clustering (Ecosystem/Health/Depth grouping), real-time filtering, advanced zoom controls, theme support (dark/light), supply chain security with auto-fix, license conflict resolution, package quality auto-fix, batch fix modes, backup & rollback, historical tracking with SQLite database, snapshot comparison, timeline visualization, and professional dependency exploration.** | ||
| [](https://www.npmjs.com/package/devcompass) | ||
| [](https://www.npmjs.com/package/devcompass) | ||
| [](https://opensource.org/licenses/MIT) | ||
| Analyze your JavaScript projects to find unused dependencies, outdated packages, **detect security vulnerabilities**, **get AI-powered recommendations**, **ask questions about your dependencies**, **find package alternatives with AI**, **chat with AI about your project**, **monitor GitHub issues in real-time for 502 packages**, **configure your own GitHub token to avoid rate limits**, **customize all configuration via JSON files**, **visualize dependency graphs with 5 dynamic layouts including Analytics dashboard**, **modular architecture with zero code duplication**, **organize packages by ecosystem (React/Vue/Angular/Testing/Build Tools)**, **group by health status (Critical/Warning/Healthy)**, **analyze by depth levels**, **instant layout switching**, **dark/light theme toggle**, **real-time filtering**, **advanced zoom controls**, **track dependency changes over time with SQLite database**, **compare snapshots to see what changed**, **visualize evolution with interactive timelines**, **check bundle sizes**, **verify licenses**, **detect and auto-fix supply chain attacks**, **resolve license conflicts automatically**, **replace abandoned/deprecated packages automatically**, **analyze package quality**, **batch fix with granular control**, **manage backups and rollback changes**, and **automatically fix issues with dry-run, progress tracking, and backups**. Perfect for **CI/CD pipelines** with JSON output and exit codes. | ||
| > **๐ค LATEST v3.2.2:** AI-Powered Analysis - Get intelligent recommendations from OpenAI, Anthropic, Google, or FREE local AI! ๐ค | ||
| > **๐ v3.2.1:** Historical Tracking System - Track changes, compare snapshots, visualize trends! ๐ | ||
| > **๐จ v3.2.0:** Unified Dashboard Architecture - 50% less code, 5 layouts, dark/light themes! ๐จ | ||
| ## ๐ Latest Release: v3.2.2 (2026-04-26) | ||
| **AI-Powered Dependency Analysis - Smart Recommendations from AI!** | ||
| ### ๐ What's New in v3.2.2: | ||
| #### **๐ค AI-Powered Analysis** | ||
| Get intelligent, context-aware recommendations from AI to help you maintain healthier dependencies. | ||
| **Features:** | ||
| - โ **Multi-Provider Support** - OpenAI, Anthropic (Claude), Google (Gemini), Ollama (FREE local) | ||
| - โ **Encrypted Token Storage** - AES-256-GCM encryption for API keys | ||
| - โ **Context-Aware** - AI analyzes your specific project data | ||
| - โ **Real-Time Streaming** - See responses as they're generated | ||
| - โ **Interactive Chat** - Multi-turn conversations about your dependencies | ||
| - โ **Cost Tracking** - Monitor token usage and estimated costs | ||
| - โ **FREE Option** - Use local AI with Ollama (no API costs) | ||
| - โ **Package Alternatives** - AI suggests better replacements | ||
| - โ **Smart Prioritization** - Critical โ High โ Medium recommendations | ||
| **AI Commands:** | ||
| ```bash | ||
| # European formats | ||
| devcompass history list --date 25-04-2026 # Specific day | ||
| devcompass history list --month 04-2026 # Specific month | ||
| # Setup AI provider | ||
| devcompass llm add --provider openai --token sk-xxx --model gpt-4o-mini | ||
| devcompass llm test openai | ||
| # ISO formats | ||
| devcompass history list --date 2026-04-25 # ISO day | ||
| devcompass history list --month 2026-04 # ISO month | ||
| # Or use FREE local AI | ||
| devcompass llm add --provider local --model llama3.2 --base-url http://localhost:11434 | ||
| # Year only | ||
| devcompass history list --year 2026 | ||
| # Get AI-powered analysis | ||
| devcompass analyze --ai | ||
| # Date ranges | ||
| devcompass history list --from 01-04-2026 --to 30-04-2026 | ||
| devcompass history list --after 15-04-2026 | ||
| # Ask questions | ||
| devcompass ai ask "Should I update axios to version 1.15.2?" | ||
| devcompass ai ask "What are the security risks in my project?" | ||
| # Find alternatives | ||
| devcompass ai alternatives moment | ||
| # Interactive chat | ||
| devcompass ai chat | ||
| # View usage | ||
| devcompass llm stats | ||
| ``` | ||
| ### Grouped Display | ||
| **What AI Can Help With:** | ||
| - ๐ **Explain why** packages are outdated | ||
| - ๐ก๏ธ **Identify** breaking changes in updates | ||
| - ๐ **Suggest** migration strategies | ||
| - ๐ **Find** better package alternatives | ||
| - โก **Prioritize** updates by risk level | ||
| - ๐ **Explain** security vulnerabilities | ||
| - ๐ **Provide** step-by-step fixes | ||
| When you have >20 snapshots, they're automatically grouped by month: | ||
| **Privacy & Security:** | ||
| - ๐ Tokens encrypted with AES-256-GCM | ||
| - ๐พ Stored locally in `~/.devcompass/ai.db` | ||
| - ๐ซ Your code is never sent to AI (only analysis metadata) | ||
| - ๐ FREE local option with complete privacy | ||
| **Supported AI Providers:** | ||
| | Provider | Models | Cost | Best For | | ||
| |----------|--------|------|----------| | ||
| | **OpenAI** | GPT-4, GPT-4 Turbo, GPT-3.5 | ~$0.03/1K tokens | Fast, accurate responses | | ||
| | **Anthropic** | Claude 3.5 Sonnet, Opus, Haiku | ~$0.003/1K tokens | Detailed analysis | | ||
| | **Google** | Gemini Pro, Gemini 1.5 Pro | ~$0.00025/1K tokens | Cost-effective | | ||
| | **Ollama** | Llama 3, Mistral, CodeLlama | **FREE** | Privacy, no limits | | ||
| #### **๐ฐ Cost Comparison** | ||
| **Example: 50 AI queries per month** | ||
| | Provider | Monthly Cost | | ||
| |----------|--------------| | ||
| | OpenAI GPT-4 | ~$4.50 | | ||
| | Anthropic Claude | ~$0.90 | | ||
| | Google Gemini | ~$0.04 | | ||
| | **Ollama (Local)** | **$0.00 FREE!** | | ||
| ## โจ All Features | ||
| - ๐ค **AI-Powered Analysis** (v3.2.2) - Multi-provider LLM integration | ||
| - ๐ฌ **Interactive AI Chat** (v3.2.2) - Ask questions, get answers | ||
| - ๐ **Package Alternatives** (v3.2.2) - AI-suggested replacements | ||
| - ๐ **Encrypted Tokens** (v3.2.2) - AES-256-GCM security | ||
| - ๐ **Cost Tracking** (v3.2.2) - Monitor AI usage | ||
| - ๐ **Historical Tracking** (v3.2.1) - SQLite database, auto-save snapshots | ||
| - ๐ **Snapshot Comparison** (v3.2.1) - Side-by-side diff analysis | ||
| - ๐ **Timeline Visualization** (v3.2.1) - Interactive D3 charts | ||
| - ๐๏ธ **Flexible Dates** (v3.2.1) - 9 date formats supported | ||
| - ๐จ **Unified Dashboard** (v3.2.0) - 5 layouts, modular architecture | ||
| - ๐ **Analytics Layout** (v3.2.0) - Statistics dashboard | ||
| - ๐ **Theme Support** (v3.2.0) - Dark/light mode toggle | ||
| - โก **Performance** (v3.2.0) - 4-6ร faster rendering | ||
| - ๐ง **Dynamic Data Configuration** (v3.1.7) - JSON-based scalable config | ||
| - ๐ฒ **Intelligent Clustering** (v3.1.6) - Ecosystem/Health/Depth grouping | ||
| - ๐ **GitHub Token Config** (v3.1.5) - User tokens, no rate limits | ||
| - ๐ฆ **502 Tracked Packages** (v3.1.5) - Comprehensive monitoring | ||
| ## ๐ Installation | ||
| ```bash | ||
| devcompass history list --year 2026 | ||
| # Global (recommended) | ||
| npm install -g devcompass@3.2.2 | ||
| # Local | ||
| npm install --save-dev devcompass@3.2.2 | ||
| # One-time use | ||
| npx devcompass@3.2.2 analyze | ||
| # Upgrade from any version | ||
| npm install -g devcompass@3.2.2 | ||
| ``` | ||
| **Output:** | ||
| ## ๐ Usage | ||
| ### Basic Commands | ||
| ```bash | ||
| # Configure GitHub token (recommended) | ||
| devcompass config --github-token <your-token> | ||
| devcompass config --show | ||
| # Analyze project (auto-saves snapshot!) | ||
| devcompass analyze | ||
| devcompass analyze --ai # ๐ค With AI recommendations! | ||
| devcompass analyze --no-history # Skip snapshot | ||
| # Generate graph (with 5 layouts + themes!) | ||
| devcompass graph --open | ||
| # Auto-fix issues | ||
| devcompass fix | ||
| devcompass fix --batch | ||
| devcompass fix --dry-run | ||
| ``` | ||
| ๐ Snapshot History (Grouped by Month) | ||
| ๐ April 2026 (22 snapshots, Avg Health: 7.71) | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| #24 25, 07:17 PM Deps: 9 Health: 6.2 | ||
| #23 25, 07:17 PM Deps: 9 Health: 6.2 | ||
| ... | ||
| Total: 22 snapshots | ||
| ### AI Commands (NEW in v3.2.2) | ||
| ```bash | ||
| # Setup AI provider | ||
| devcompass llm add --provider openai --token sk-xxx --model gpt-4o-mini | ||
| devcompass llm add --provider anthropic --token sk-ant-xxx --model claude-3-5-sonnet | ||
| devcompass llm add --provider google --token xxx --model gemini-pro | ||
| devcompass llm add --provider local --model llama3.2 --base-url http://localhost:11434 | ||
| # Manage providers | ||
| devcompass llm list | ||
| devcompass llm default openai | ||
| devcompass llm test openai | ||
| devcompass llm remove openai | ||
| devcompass llm stats | ||
| # AI Analysis | ||
| devcompass analyze --ai | ||
| devcompass analyze --ai --ai-provider anthropic | ||
| # Ask AI questions | ||
| devcompass ai ask "Why is my health score low?" | ||
| devcompass ai ask "Should I update axios now?" | ||
| devcompass ai ask "What are the breaking changes in React 19?" | ||
| # Get recommendations | ||
| devcompass ai recommend | ||
| # Find alternatives | ||
| devcompass ai alternatives moment | ||
| devcompass ai alternatives request | ||
| devcompass ai alternatives lodash | ||
| # Interactive chat | ||
| devcompass ai chat | ||
| devcompass ai chat --provider anthropic | ||
| ``` | ||
| ### Monthly Summary | ||
| ### History Commands (v3.2.1) | ||
| ```bash | ||
| devcompass history summary | ||
| # List snapshots | ||
| devcompass history list | ||
| devcompass history list --limit 50 | ||
| devcompass history list --month 04-2026 | ||
| # Compare snapshots | ||
| devcompass compare 5 8 | ||
| devcompass compare 5 8 --verbose | ||
| # Timeline | ||
| devcompass timeline --open | ||
| ``` | ||
| **Output:** | ||
| --- | ||
| ## ๐ค AI-Powered Analysis Guide (v3.2.2) | ||
| ### Quick Start | ||
| **1. Install Ollama (FREE local AI):** | ||
| ```bash | ||
| # Install Ollama | ||
| curl -fsSL https://ollama.com/install.sh | sh | ||
| # Start Ollama | ||
| ollama serve | ||
| # Pull a model | ||
| ollama pull llama3.2 | ||
| # Add to DevCompass | ||
| devcompass llm add --provider local --model llama3.2 --base-url http://localhost:11434 | ||
| # Test it | ||
| devcompass llm test local | ||
| # Use it! | ||
| devcompass analyze --ai | ||
| ``` | ||
| ๐ Monthly Snapshot Summary | ||
| April 2026 22 snapshots Avg Health: 7.71/10 Avg Deps: 9 | ||
| March 2026 15 snapshots Avg Health: 8.20/10 Avg Deps: 8 | ||
| **2. Or use OpenAI:** | ||
| ```bash | ||
| # Get API key from: https://platform.openai.com/api-keys | ||
| # Add provider | ||
| devcompass llm add --provider openai --token sk-your-key --model gpt-4o-mini | ||
| # Test connection | ||
| devcompass llm test openai | ||
| # Use it! | ||
| devcompass analyze --ai | ||
| ``` | ||
| ### Database Location | ||
| ### Example AI Interactions | ||
| All snapshots are stored in: | ||
| **Get Analysis:** | ||
| ```bash | ||
| $ devcompass analyze --ai | ||
| ๐ค AI Recommendations | ||
| ๐ด CRITICAL (Do Now): | ||
| - Security Vulnerabilities (24 total) | ||
| โ Run: npm audit fix | ||
| โ Why: 3 high-severity issues expose your app to attacks | ||
| ๐ก HIGH PRIORITY (This Week): | ||
| - Update axios (0.21.1 โ 1.15.2) | ||
| โ Why: Contains known CVEs | ||
| โ Breaking changes: Response format changed | ||
| ``` | ||
| ~/.devcompass/history.db | ||
| ``` | ||
| **Storage Efficiency:** | ||
| - ~3KB per snapshot | ||
| - SQLite database with WAL mode | ||
| - 4 optimized indexes | ||
| - Fast queries (<10ms) | ||
| **Ask Questions:** | ||
| ```bash | ||
| $ devcompass ai ask "Should I update axios from 0.21.1 to 1.15.2?" | ||
| ### Disable Auto-Save | ||
| ๐ค Yes, you should update axios: | ||
| If you don't want automatic snapshots: | ||
| Security: Version 0.21.1 has critical vulnerabilities (CVE-2023-xxxx) | ||
| Breaking Changes: Response.data format changed, error handling updated | ||
| Migration: Update interceptors, test error handling | ||
| Command: npm install axios@latest | ||
| ```bash | ||
| devcompass analyze --no-history | ||
| Test thoroughly before deploying! | ||
| ``` | ||
| --- | ||
| **Find Alternatives:** | ||
| ```bash | ||
| $ devcompass ai alternatives moment | ||
| ## ๐ฒ Clustering System (v3.1.6) | ||
| ๐ Finding alternatives for "moment" | ||
| The clustering system helps you understand and organize your dependencies by grouping them into meaningful categories. | ||
| ๐ค Top 3 Alternatives: | ||
| ### How It Works | ||
| 1. date-fns (~2KB vs 67KB) | ||
| - Tree-shakeable, modern API | ||
| - Migration: Easy (similar methods) | ||
| 2. dayjs (~2KB) | ||
| - moment.js compatible API | ||
| - Migration: Drop-in replacement | ||
| 3. Luxon (~15KB) | ||
| - Better timezone support | ||
| - Migration: Medium (different API) | ||
| **1. Choose a Clustering Mode** | ||
| Recommendation: Use date-fns for best bundle size | ||
| ``` | ||
| Click one of three buttons in the sidebar: | ||
| - **โ๏ธ Ecosystem** - Group by technology | ||
| - **๐ฅ Health** - Group by status | ||
| - **๐ Depth** - Group by level | ||
| **Interactive Chat:** | ||
| ```bash | ||
| $ devcompass ai chat | ||
| **2. View Organized Clusters** | ||
| ๐ค DevCompass AI Assistant | ||
| Ask me anything about your dependencies! | ||
| See your packages organized in the sidebar with: | ||
| - Cluster name and icon | ||
| - Package count | ||
| - Health badges (๐ด vulnerable, ๐ก outdated, ๐ข healthy) | ||
| You: Should I remove lodash and moment since they're unused? | ||
| **3. Click to Highlight** | ||
| ๐ค Yes, remove them: | ||
| - lodash: 1.3 MB saved | ||
| - moment: 4.1 MB saved | ||
| Total saved: 5.4 MB | ||
| Click any cluster to temporarily highlight those packages on the graph (highlights for 3 seconds, then fades back) | ||
| Command: npm uninstall lodash moment | ||
| ### Ecosystem Clustering | ||
| This will improve your health score from 0.5/10 to ~5.3/10! | ||
| **Automatically detects and groups:** | ||
| You: exit | ||
| ๐ Chat ended. Used 245 tokens (~$0.0001) | ||
| ``` | ||
| - **โ๏ธ React Ecosystem** - React, Redux, Next.js, React Router | ||
| - **๐ Vue Ecosystem** - Vue, Vuex, Nuxt, Vite | ||
| - **๐ ฐ๏ธ Angular Ecosystem** - @angular/*, RxJS | ||
| - **๐งช Testing Tools** - Jest, Cypress, Playwright, Testing Library | ||
| - **๐ง Build & Bundle** - Webpack, Rollup, Vite, Babel | ||
| - **โจ Code Quality** - ESLint, Prettier, Stylelint | ||
| - **๐ TypeScript** - TypeScript, @types/*, ts-node | ||
| - **๐ ๏ธ Utilities** - Lodash, date-fns, UUID | ||
| - **๐ HTTP & API** - Axios, Fetch, Got | ||
| - **๐ฅ๏ธ Server & Backend** - Express, Fastify, NestJS | ||
| - **๐พ Database** - Prisma, TypeORM, Mongoose | ||
| - **๐จ Styling** - Styled-components, Tailwind, Emotion | ||
| ### Cost Tracking | ||
| **Plus "Other Dependencies" for uncategorized packages** | ||
| ```bash | ||
| $ devcompass llm stats | ||
| ### Health Clustering | ||
| ๐ AI Usage Statistics - 2026-04 | ||
| **Groups by status:** | ||
| local (llama3.2) | ||
| Requests: 28 | ||
| Tokens: 11,923 | ||
| Cost: $0.0000 | ||
| - **๐ด Critical Issues** - Vulnerable or deprecated packages (fix immediately) | ||
| - **๐ก Needs Attention** - Outdated packages (update soon) | ||
| - **๐ข Healthy** - Up-to-date, secure packages | ||
| โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | ||
| Total Requests: 28 | ||
| Total Tokens: 11,923 | ||
| Total Cost: $0.0000 | ||
| ### Depth Clustering | ||
| ๐ Projected monthly cost: $0.00 | ||
| ``` | ||
| **Groups by dependency level:** | ||
| ### Privacy & Security | ||
| - **๐ Direct Dependencies** - Packages in your package.json | ||
| - **๐ Level 1 Dependencies** - Dependencies of your direct dependencies | ||
| - **๐ Level 2 Dependencies** - Dependencies of level 1 | ||
| - **๐ Level N Dependencies** - And so on... | ||
| **What Gets Sent to AI:** | ||
| - โ Package names and versions | ||
| - โ Vulnerability counts | ||
| - โ Health score | ||
| - โ Outdated/unused package lists | ||
| --- | ||
| **What Doesn't Get Sent:** | ||
| - โ Your source code | ||
| - โ File contents | ||
| - โ Environment variables | ||
| - โ API keys | ||
| ## ๐ GitHub Token Configuration (v3.1.5) | ||
| **Encryption:** | ||
| - AES-256-GCM encryption for API tokens | ||
| - Machine-specific encryption keys | ||
| - Tokens stored in `~/.devcompass/ai.db` | ||
| - Never sent to DevCompass servers | ||
| DevCompass supports user-configurable GitHub Personal Access Tokens to avoid API rate limiting. | ||
| --- | ||
| ### Why Configure a Token? | ||
| ## ๐ Historical Tracking System (v3.2.1) | ||
| **Without Token:** | ||
| - 60 requests/hour (GitHub API limit) | ||
| - May hit rate limits during analysis | ||
| - Cannot track all 502 packages | ||
| Track your dependency evolution over time with automatic snapshots, comparison tools, and timeline visualization. | ||
| **With Token:** | ||
| - 5,000 requests/hour ๐ | ||
| - No rate limit warnings | ||
| - Full package health monitoring | ||
| - Faster analysis | ||
| ### How It Works | ||
| ### Quick Setup | ||
| **1. Auto-Save on Analyze** | ||
| **1. Create GitHub Token** | ||
| Every time you run `devcompass analyze`, a snapshot is automatically saved: | ||
| Visit: https://github.com/settings/tokens/new | ||
| ```bash | ||
| devcompass analyze | ||
| - Token name: `DevCompass CLI` | ||
| - Expiration: `90 days` (or your preference) | ||
| - Scope: โ๏ธ `public_repo` only | ||
| # Output: | ||
| # โ Scanned 6 dependencies in project | ||
| # ๐ธ Snapshot saved (ID: 40, 19ms) | ||
| # Use "devcompass history list" to view all snapshots | ||
| ``` | ||
| **2. Configure Token** | ||
| **2. View Your History** | ||
| ```bash | ||
| devcompass config --github-token ghp_YOUR_TOKEN_HERE | ||
| devcompass history list | ||
| ``` | ||
| **3. Verify** | ||
| **3. Compare Changes** | ||
| ```bash | ||
| devcompass config --show | ||
| # Output: โ GitHub token configured: ghp_xxx***xxx | ||
| devcompass compare 38 40 | ||
| ``` | ||
| **4. Done!** | ||
| **4. Visualize Trends** | ||
| ```bash | ||
| devcompass analyze | ||
| # No rate limit warnings! | ||
| devcompass timeline --open | ||
| ``` | ||
@@ -575,59 +944,55 @@ | ||
| ### Common Issues: | ||
| ### AI-Related Issues | ||
| **"Command not found"** | ||
| **"No AI provider configured"** | ||
| ```bash | ||
| npm install -g devcompass@3.2.1 | ||
| ``` | ||
| # Add a provider first | ||
| devcompass llm add --provider local --model llama3.2 --base-url http://localhost:11434 | ||
| **Old version** | ||
| ```bash | ||
| npm update -g devcompass | ||
| devcompass --version # Should show 3.2.1 | ||
| # Or use OpenAI | ||
| devcompass llm add --provider openai --token sk-xxx --model gpt-4o-mini | ||
| ``` | ||
| **History database not saving** | ||
| **"Ollama connection failed"** | ||
| ```bash | ||
| # Check database exists | ||
| ls -la ~/.devcompass/history.db | ||
| # Check Ollama is running | ||
| ps aux | grep ollama | ||
| # If missing, re-run analyze | ||
| devcompass analyze | ||
| # Restart Ollama | ||
| ollama serve & | ||
| # Check for errors | ||
| DEBUG=1 devcompass analyze | ||
| # Test connection | ||
| devcompass llm test local | ||
| ``` | ||
| **Date format not recognized** | ||
| **"API key invalid"** | ||
| ```bash | ||
| # Supported formats: | ||
| # DD-MM-YYYY: 25-04-2026 | ||
| # MM-YYYY: 04-2026 | ||
| # YYYY: 2026 | ||
| # YYYY-MM-DD: 2026-04-25 | ||
| # YYYY-MM: 2026-04 | ||
| # Update token | ||
| devcompass llm update openai --token sk-new-token | ||
| # Example: | ||
| devcompass history list --date 25-04-2026 | ||
| # Test it | ||
| devcompass llm test openai | ||
| ``` | ||
| **Timeline not generating** | ||
| **"Quota exceeded"** | ||
| ```bash | ||
| # Ensure you have snapshots | ||
| devcompass history list | ||
| # Check usage | ||
| devcompass llm stats | ||
| # Generate timeline | ||
| devcompass timeline --open | ||
| # Switch to free provider | ||
| devcompass llm add --provider local --model llama3.2 --base-url http://localhost:11434 | ||
| devcompass llm default local | ||
| ``` | ||
| # Check output file | ||
| ls -la devcompass-timeline.html | ||
| ### Common Issues | ||
| **"Command not found"** | ||
| ```bash | ||
| npm install -g devcompass@3.2.2 | ||
| ``` | ||
| **Comparison showing no changes** | ||
| **Old version** | ||
| ```bash | ||
| # Verify snapshot IDs exist | ||
| devcompass history list | ||
| # Compare different snapshots | ||
| devcompass compare <older-id> <newer-id> | ||
| npm update -g devcompass | ||
| devcompass --version # Should show 3.2.2 | ||
| ``` | ||
@@ -647,9 +1012,9 @@ | ||
| 2. **Expand License Database** | ||
| - Edit `data/license-risks.json` | ||
| - Add new license types and risk levels | ||
| 2. **Improve AI Prompts** | ||
| - Edit `src/ai/prompt-templates.js` | ||
| - Make recommendations more helpful | ||
| 3. **Improve Typosquatting Detection** | ||
| - Edit `data/popular-packages.json` | ||
| - Add more popular packages to track | ||
| 3. **Add AI Providers** | ||
| - Create new provider in `src/ai/providers/` | ||
| - Follow existing provider patterns | ||
@@ -675,19 +1040,15 @@ 4. **Code Contributions** | ||
| - [x] AI-powered analysis (v3.2.2) โ | ||
| - [x] Multi-provider LLM support (v3.2.2) โ | ||
| - [x] Interactive AI chat (v3.2.2) โ | ||
| - [x] Package alternatives with AI (v3.2.2) โ | ||
| - [x] Historical tracking (v3.2.1) โ | ||
| - [x] Snapshot comparison (v3.2.1) โ | ||
| - [x] Timeline visualization (v3.2.1) โ | ||
| - [x] Flexible date formats (v3.2.1) โ | ||
| - [x] Unified dashboard architecture (v3.2.0) โ | ||
| - [x] Analytics layout (v3.2.0) โ | ||
| - [x] Theme support (v3.2.0) โ | ||
| - [x] Performance optimizations (v3.2.0) โ | ||
| - [x] Dynamic data configuration (v3.1.7) โ | ||
| - [x] GitHub token configuration (v3.1.5) โ | ||
| - [x] 502 tracked packages (v3.1.5) โ | ||
| - [x] Unified graph system (v3.1.4) โ | ||
| - [x] Unified dashboard (v3.2.0) โ | ||
| - [x] Intelligent clustering (v3.1.6) โ | ||
| ### Planned Features: | ||
| - [ ] **Web Dashboard** - Team health monitoring | ||
| - [ ] **Monorepo Support** - Multi-project analysis | ||
| - [ ] **Web Dashboard** - Team health monitoring with AI insights | ||
| - [ ] **Monorepo Support** - Multi-project AI analysis | ||
@@ -700,2 +1061,13 @@ Want to contribute? Pick a feature and open an issue! ๐ | ||
| ### v3.2.2 (2026-04-26) - AI-Powered Analysis | ||
| - ๐ค Multi-provider AI integration (OpenAI, Anthropic, Google, Ollama) | ||
| - ๐ฌ Interactive AI chat with conversation history | ||
| - ๐ AI-powered package alternative suggestions | ||
| - ๐ AES-256-GCM encrypted token storage | ||
| - ๐ Cost tracking and usage statistics | ||
| - โก Real-time streaming responses | ||
| - ๐ FREE local AI option with Ollama | ||
| - ๐ Context-aware recommendations | ||
| - โ Zero breaking changes (100% backward compatible) | ||
| ### v3.2.1 (2026-04-26) - Historical Tracking System | ||
@@ -705,34 +1077,48 @@ - ๐ SQLite database for snapshot storage | ||
| - ๐ Timeline visualization with D3 charts | ||
| - ๐๏ธ 9 flexible date formats (DD-MM-YYYY, MM-YYYY, YYYY, etc.) | ||
| - ๐จ Auto-grouped display for >20 snapshots | ||
| - ๐ Monthly summary with aggregated stats | ||
| - โก Performance: 6-83ร faster than targets | ||
| - ๐ Fixed typosquatting false positives | ||
| - ๐ Fixed dynamic security property names | ||
| - ๐๏ธ 9 flexible date formats | ||
| - โก 6-83ร performance improvements | ||
| - ๐ Bug fixes for typosquatting and security | ||
| ### v3.2.0 (2026-04-25) - Unified Dashboard Architecture | ||
| - ๐จ Unified modular dashboard (50% code reduction) | ||
| - ๐ NEW Analytics layout - Statistics dashboard | ||
| - ๐ Dark/light theme support | ||
| ### v3.2.0 (2026-04-25) - Unified Dashboard | ||
| - ๐จ Unified modular architecture (50% code reduction) | ||
| - ๐ Analytics layout | ||
| - ๐ Dark/light themes | ||
| - โก 4-6ร performance improvements | ||
| - ๐๏ธ 12 modular files (6 JS, 5 CSS, 1 HTML) | ||
| - โ Removed 5 duplicated files (3,600 lines โ 1,800 lines) | ||
| - โ 100% backward compatible | ||
| ### v3.1.7 (2026-04-22) - Dynamic Data Configuration | ||
| - ๐ง 8 new JSON configuration files | ||
| - โ 7 source files refactored for dynamic loading | ||
| - โ Zero hardcoded data in code | ||
| - โ Scalable and customizable architecture | ||
| --- | ||
| ### v3.1.6 (2026-04-22) - Intelligent Clustering | ||
| - ๐ฒ Ecosystem clustering (12 categories) | ||
| - ๐ฒ Health clustering (Critical/Warning/Healthy) | ||
| - ๐ฒ Depth clustering (Direct โ Level N) | ||
| **Made with โค๏ธ by [Ajay Thorat](https://github.com/AjayBThorat-20)** | ||
| ### v3.1.5 (2026-04-21) - GitHub Token Support | ||
| - ๐ User-configurable GitHub tokens | ||
| - ๐ฆ 502 tracked packages database | ||
| - โก 5,000 requests/hour (vs 60) | ||
| *DevCompass v3.2.2 - AI-Powered Dependency Intelligence!* ๐งญ๐ค | ||
| **Like Lighthouse for your dependencies, now with AI superpowers** โก | ||
| ## ๐ Version History | ||
| ### v3.2.2 (2026-04-26) - AI-Powered Analysis | ||
| - ๐ค Multi-provider AI integration (OpenAI, Anthropic, Google, Ollama) | ||
| - ๐ฌ Interactive AI chat with conversation history | ||
| - ๐ AI-powered package alternative suggestions | ||
| - ๐ AES-256-GCM encrypted token storage | ||
| - ๐ Cost tracking and usage statistics | ||
| - โก Real-time streaming responses | ||
| - ๐ FREE local AI option with Ollama | ||
| - ๐ Context-aware recommendations | ||
| - โ Zero breaking changes (100% backward compatible) | ||
| ### v3.2.1 (2026-04-26) - Historical Tracking System | ||
| - ๐ SQLite database for snapshot storage | ||
| - ๐ Snapshot comparison with side-by-side diff | ||
| - ๐ Timeline visualization with D3 charts | ||
| - ๐๏ธ 9 flexible date formats | ||
| - โก 6-83ร performance improvements | ||
| - ๐ Bug fixes for typosquatting and security | ||
| ### v3.2.0 (2026-04-25) - Unified Dashboard | ||
| - ๐จ Unified modular architecture (50% code reduction) | ||
| - ๐ Analytics layout | ||
| - ๐ Dark/light themes | ||
| - โก 4-6ร performance improvements | ||
| --- | ||
@@ -742,4 +1128,4 @@ | ||
| *DevCompass v3.2.1 - Track, Compare, Evolve!* ๐งญ | ||
| *DevCompass v3.2.2 - AI-Powered Dependency Intelligence!* ๐งญ๐ค | ||
| **Like Lighthouse for your dependencies** โก | ||
| **Like Lighthouse for your dependencies, now with AI superpowers** โก |
@@ -45,2 +45,8 @@ // src/commands/analyze.js | ||
| // NEW v3.2.2 - AI imports | ||
| const tokenManager = require('../ai/token-manager'); | ||
| const contextBuilder = require('../ai/context-builder'); | ||
| const aiCommand = require('./ai'); | ||
| const streamFormatter = require('../utils/stream-formatter'); | ||
| const packageJson = require('../../package.json'); | ||
@@ -203,5 +209,3 @@ | ||
| /** | ||
| * NEW v3.2.1 - Save analysis snapshot to history database | ||
| */ | ||
| async function saveHistorySnapshot(analysisData, graphData, projectPackageJson) { | ||
@@ -232,2 +236,33 @@ try { | ||
| async function getAIInsights(analysisResults, options = {}) { | ||
| try { | ||
| // Check if AI provider is configured | ||
| const providers = tokenManager.listProviders(); | ||
| if (providers.length === 0) { | ||
| console.log(chalk.yellow('\nโ ๏ธ No AI provider configured')); | ||
| console.log(chalk.gray(' Add one with: devcompass llm add --provider openai --token sk-xxx --model gpt-4')); | ||
| console.log(chalk.gray(' Or use local models: devcompass llm add --provider local --model llama3\n')); | ||
| return; | ||
| } | ||
| // Build context from analysis results | ||
| const context = contextBuilder.buildAnalysisContext(analysisResults); | ||
| // Get AI recommendations | ||
| await aiCommand.getRecommendations(analysisResults, { | ||
| provider: options.aiProvider, | ||
| stream: true, | ||
| verbose: false | ||
| }); | ||
| } catch (error) { | ||
| if (error.message.includes('not found') || error.message.includes('No default provider')) { | ||
| console.log(chalk.yellow('\nโ ๏ธ No AI provider configured')); | ||
| console.log(chalk.gray(' Add one with: devcompass llm add --provider openai --token sk-xxx --model gpt-4\n')); | ||
| } else { | ||
| console.error(chalk.red('\nโ AI Error: ' + error.message + '\n')); | ||
| } | ||
| } | ||
| } | ||
| async function analyze(options) { | ||
@@ -621,2 +656,23 @@ const projectPath = options.path || process.cwd(); | ||
| if (options.ai && outputMode === 'normal') { | ||
| // Build analysis results object for AI | ||
| const analysisResults = { | ||
| projectName: projectPackageJson.name || 'Unknown', | ||
| projectVersion: projectPackageJson.version || '1.0.0', | ||
| projectPath: projectPath, | ||
| healthScore: score.total, | ||
| totalDependencies: totalDeps, | ||
| vulnerabilities: securityData.vulnerabilities || [], | ||
| outdated: outdatedDeps || [], | ||
| deprecated: qualityData.results?.filter(r => r.status === 'deprecated') || [], | ||
| unused: unusedDeps || [], | ||
| supplyChain: supplyChainData.warnings || [], | ||
| licenseIssues: licenseRiskData.warnings || [], | ||
| heavyPackages: findHeavyPackages(bundleSizes) | ||
| }; | ||
| // Get AI insights | ||
| await getAIInsights(analysisResults, options); | ||
| } | ||
| // v2.7.0 - Generate Security Recommendations | ||
@@ -623,0 +679,0 @@ const safeSupplyChainWarnings = Array.isArray(supplyChainData.warnings) |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
630298
19.82%97
18.29%17095
17.35%1119
52.87%7
16.67%53
8.16%21
75%+ Added
+ Added