Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement โ†’
Sign In

devcompass

Package Overview
Dependencies
Maintainers
1
Versions
37
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

devcompass - npm Package Compare versions

Comparing version
3.2.1
to
3.2.2
+443
src/ai/context-builder.js
// 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 @@ [![npm version](https://img.shields.io/npm/v/devcompass.svg)](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.**
[![npm version](https://img.shields.io/npm/v/devcompass.svg)](https://www.npmjs.com/package/devcompass)
[![npm downloads](https://img.shields.io/npm/dm/devcompass.svg)](https://www.npmjs.com/package/devcompass)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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)