mira-consciousness
Version:
Memory & Intelligence Retention Archive - Preserving The Spark
1,040 lines ⢠46 kB
JavaScript
import { Command } from 'commander';
import chalk from 'chalk';
import ora from 'ora';
import fs from 'fs-extra';
import * as path from 'path';
import { execSync } from 'child_process';
import { getDirectPythonInterface } from '../core/DirectPythonInterface.js';
import { NameDetector } from '../core/NameDetector.js';
import { ProjectDetector } from '../core/ProjectDetector.js';
import { PatternAnalyzer } from '../core/PatternAnalyzer.js';
import { SecurityAnalyzer } from '../analyzers/SecurityAnalyzer.js';
import { PerformanceAnalyzer } from '../analyzers/PerformanceAnalyzer.js';
import { DocumentationAnalyzer } from '../analyzers/DocumentationAnalyzer.js';
import { CodeQualityAnalyzer } from '../analyzers/CodeQualityAnalyzer.js';
import { DependencyAnalyzer } from '../analyzers/DependencyAnalyzer.js';
import { UnifiedScoringSystem } from '../core/UnifiedScoring.js';
export function createAnalyzeCommand() {
return new Command('analyze')
.description('Comprehensive code analysis with unified scoring')
.option('-c, --code <path>', 'Analyze specific code file or directory')
.option('-i, --issue <description>', 'Analyze and solve specific issue')
.option('-g, --git', 'Analyze recent git changes for commit suggestions')
.option('-p, --pattern', 'Analyze patterns in current project')
.option('--comprehensive', 'Run all analyzers with unified scoring')
.option('--security', 'Run security analysis only')
.option('--performance', 'Run performance analysis only')
.option('--docs', 'Run documentation analysis only')
.option('--quality', 'Run code quality analysis only')
.option('--deps', 'Run dependency analysis only')
.option('--recent <count>', 'Number of recent messages to analyze', '20')
.option('--no-memory', 'Skip memory search')
.option('-d, --deep', 'Perform deep analysis with all subsystems')
.action(async (options) => {
const pythonInterface = getDirectPythonInterface();
const nameDetector = new NameDetector();
const projectDetector = new ProjectDetector(process.cwd());
const patternAnalyzer = new PatternAnalyzer(process.cwd());
console.log(chalk.cyan('\nš§ MIRA Intelligent Analysis\n'));
// Get project identity and context
const identity = await nameDetector.detect();
const developerRef = await nameDetector.getDeveloperReference();
const projectInfo = await projectDetector.detectProject();
console.log(chalk.gray(`Project: ${identity.projectName}`));
console.log(chalk.gray(`Type: ${projectInfo.type}`));
console.log(chalk.gray(`Developer: ${developerRef}\n`));
// Determine analysis focus
let analysisType = 'context';
let analysisTarget = null;
if (options.comprehensive) {
analysisType = 'comprehensive';
}
else if (options.security) {
analysisType = 'security';
}
else if (options.performance) {
analysisType = 'performance';
}
else if (options.docs) {
analysisType = 'documentation';
}
else if (options.quality) {
analysisType = 'quality';
}
else if (options.deps) {
analysisType = 'dependencies';
}
else if (options.code) {
analysisType = 'code';
analysisTarget = options.code;
}
else if (options.issue) {
analysisType = 'issue';
analysisTarget = options.issue;
}
else if (options.git) {
analysisType = 'git';
}
else if (options.pattern) {
analysisType = 'patterns';
}
const spinner = ora('Gathering analysis context...').start();
try {
// Build comprehensive analysis context
const context = {
recentMessages: [],
gitChanges: [],
relatedMemories: [],
patterns: [],
projectContext: projectInfo
};
// 1. Update conversation index and get recent context
if (!options.noMemory) {
spinner.text = 'Indexing recent conversations...';
try {
await pythonInterface.indexConversations();
// Get recent conversation context using search (with timeout)
const recentLimit = parseInt(options.recent) || 20;
try {
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Conversation context timeout')), 10000));
context.recentMessages = await Promise.race([
getRecentConversationContext(pythonInterface, recentLimit),
timeoutPromise
]);
}
catch (e) {
console.log(chalk.gray('Note: Recent conversation analysis skipped'));
context.recentMessages = [];
}
}
catch (e) {
console.log(chalk.gray('Note: Conversation analysis skipped'));
}
}
// 2. Analyze git changes if requested or relevant
if (options.git || analysisType === 'git') {
spinner.text = 'Analyzing git changes...';
context.gitChanges = await analyzeGitChanges();
}
// 3. Search for related memories based on context
if (!options.noMemory && (analysisTarget || context.recentMessages.length > 0)) {
spinner.text = 'Searching related memories...';
// Build search query from context
const searchQuery = buildContextualSearchQuery(analysisTarget, context.recentMessages, analysisType);
if (searchQuery) {
try {
const searchResult = await pythonInterface.searchConversations(searchQuery, 5);
context.relatedMemories = searchResult.success && Array.isArray(searchResult.results) ? searchResult.results : [];
}
catch (e) {
console.log(chalk.gray('Note: Memory search skipped'));
context.relatedMemories = [];
}
}
}
// 4. Analyze patterns if deep analysis requested
if (options.deep || analysisType === 'patterns') {
spinner.text = 'Analyzing development patterns...';
context.patterns = await patternAnalyzer.analyzeGitPatterns();
}
spinner.text = 'Generating intelligent insights...';
// 5. Generate analysis based on type and context
let analysis;
if (['comprehensive', 'security', 'performance', 'documentation', 'quality', 'dependencies'].includes(analysisType)) {
analysis = await generateAnalyzerBasedAnalysis(analysisType, context, options.deep);
}
else {
analysis = await generateContextualAnalysis(analysisType, analysisTarget, context, options.deep);
}
spinner.succeed('Analysis complete');
// Display results
displayAnalysisResults(analysis, context);
}
catch (error) {
spinner.fail('Analysis failed');
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
console.log(chalk.yellow('\nš” Tips:'));
console.log(chalk.gray(' ⢠Run "mira index-conversations" first to build conversation database'));
console.log(chalk.gray(' ⢠Use --no-memory to skip memory integration'));
console.log(chalk.gray(' ⢠Use -g to focus on git changes'));
console.log(chalk.gray(' ⢠Add -d for deeper pattern analysis'));
process.exit(1);
}
});
}
/**
* Get recent conversation context by searching for recent topics
*/
async function getRecentConversationContext(pythonInterface, limit) {
try {
// Search for recent development topics
const recentTopics = [
'recent work', 'current task', 'working on', 'implementing',
'fixing', 'debugging', 'feature', 'bug', 'issue'
];
const allResults = [];
for (const topic of recentTopics.slice(0, 3)) { // Limit to avoid too many calls
try {
const searchResult = await pythonInterface.searchConversations(topic, Math.ceil(limit / 3));
if (searchResult.success && Array.isArray(searchResult.results)) {
allResults.push(...searchResult.results);
}
}
catch (e) {
// Continue with next topic if one fails
}
}
// Sort by relevance and return top results
return allResults
.sort((a, b) => (b.score || 0) - (a.score || 0))
.slice(0, limit);
}
catch (error) {
return [];
}
}
/**
* Analyze recent git changes
*/
async function analyzeGitChanges() {
try {
// Check if we're in a git repository
execSync('git rev-parse --git-dir', { stdio: 'ignore' });
// Get recent changes (staged and recent commits)
const changes = [];
// Staged changes
try {
const staged = execSync('git diff --cached --numstat', { encoding: 'utf-8' });
if (staged.trim()) {
const stagedFiles = staged.trim().split('\n').map(line => {
const [additions, deletions, file] = line.split('\t');
return {
file,
status: 'staged',
additions: parseInt(additions) || 0,
deletions: parseInt(deletions) || 0
};
});
changes.push(...stagedFiles);
}
}
catch (e) {
// No staged changes
}
// Recent unstaged changes
try {
const unstaged = execSync('git diff --numstat', { encoding: 'utf-8' });
if (unstaged.trim()) {
const unstagedFiles = unstaged.trim().split('\n').map(line => {
const [additions, deletions, file] = line.split('\t');
return {
file,
status: 'modified',
additions: parseInt(additions) || 0,
deletions: parseInt(deletions) || 0
};
});
changes.push(...unstagedFiles);
}
}
catch (e) {
// No unstaged changes
}
// Get actual diff for small changes to help with commit messages
for (const change of changes) {
if (change.additions + change.deletions < 50) { // Only for small changes
try {
const diff = execSync(`git diff ${change.status === 'staged' ? '--cached' : ''} -- "${change.file}"`, { encoding: 'utf-8' });
change.diff = diff;
}
catch (e) {
// Skip if diff fails
}
}
}
return changes;
}
catch (error) {
return [];
}
}
/**
* Build contextual search query from available information
*/
function buildContextualSearchQuery(target, recentMessages, analysisType) {
let query = '';
if (target) {
query = target;
}
else if (analysisType === 'git') {
query = 'commit message git changes implementation';
}
else if (recentMessages.length > 0) {
// Extract key terms from recent messages
const recentContent = recentMessages
.slice(0, 5)
.map(msg => msg.content || '')
.join(' ')
.toLowerCase();
// Look for technical terms, patterns, and topics
const keywords = extractKeywords(recentContent);
query = keywords.slice(0, 3).join(' '); // Top 3 keywords
}
return query.trim() || null;
}
/**
* Extract keywords from text for search
*/
function extractKeywords(text) {
const commonWords = new Set([
'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with',
'by', 'is', 'are', 'was', 'were', 'be', 'been', 'have', 'has', 'had', 'do', 'does',
'did', 'will', 'would', 'could', 'should', 'can', 'may', 'might', 'this', 'that',
'these', 'those', 'i', 'you', 'he', 'she', 'it', 'we', 'they', 'me', 'him', 'her',
'us', 'them', 'my', 'your', 'his', 'her', 'its', 'our', 'their'
]);
return text
.split(/\s+/)
.map(word => word.replace(/[^\w]/g, '').toLowerCase())
.filter(word => word.length > 3 && !commonWords.has(word))
.filter((word, index, arr) => arr.indexOf(word) === index) // unique
.slice(0, 10); // top 10
}
/**
* Generate analyzer-based analysis with unified scoring
*/
async function generateAnalyzerBasedAnalysis(analysisType, context, deep) {
const projectRoot = process.cwd();
const results = {};
console.log(chalk.cyan('\nš Running Static Code Analysis\n'));
try {
if (analysisType === 'comprehensive' || analysisType === 'security') {
const securityAnalyzer = new SecurityAnalyzer(projectRoot);
const securitySpinner = ora('Analyzing security vulnerabilities...').start();
try {
const securityResult = await securityAnalyzer.fullAnalysis();
results.security = {
score: securityResult.securityScore,
issues: securityResult.vulnerabilities.map((issue) => ({
type: issue.type,
severity: issue.severity,
confidence: 'high',
impact: issue.severity === 'critical' ? 'major' : issue.severity === 'high' ? 'major' : 'moderate',
message: issue.message,
file: issue.file,
line: issue.line,
suggestion: issue.recommendation
})),
strengths: [],
recommendations: securityResult.recommendations?.map((r) => r.description) || []
};
securitySpinner.succeed(`Security analysis complete (Score: ${securityResult.securityScore}/100)`);
}
catch (error) {
securitySpinner.fail('Security analysis failed');
results.security = { score: 50, issues: [], strengths: [], recommendations: [] };
}
}
if (analysisType === 'comprehensive' || analysisType === 'performance') {
const performanceAnalyzer = new PerformanceAnalyzer(projectRoot);
const performanceSpinner = ora('Analyzing performance bottlenecks...').start();
try {
const performanceResult = await performanceAnalyzer.analyze();
results.performance = {
score: performanceResult.score,
issues: performanceResult.issues.map(issue => ({
type: issue.type,
severity: issue.impact === 'high' ? 'high' : issue.impact === 'medium' ? 'medium' : 'low',
confidence: 'high',
impact: issue.impact === 'high' ? 'major' : 'moderate',
message: issue.message,
file: issue.file,
line: issue.line,
suggestion: issue.recommendation
})),
strengths: [],
recommendations: []
};
performanceSpinner.succeed(`Performance analysis complete (Score: ${performanceResult.score}/100)`);
}
catch (error) {
performanceSpinner.fail('Performance analysis failed');
results.performance = { score: 50, issues: [], strengths: [], recommendations: [] };
}
}
if (analysisType === 'comprehensive' || analysisType === 'documentation') {
const documentationAnalyzer = new DocumentationAnalyzer(projectRoot);
const documentationSpinner = ora('Analyzing documentation quality...').start();
try {
const documentationResult = await documentationAnalyzer.analyze();
results.documentation = {
score: documentationResult.score,
issues: documentationResult.issues.map(issue => ({
type: issue.type,
severity: issue.severity,
confidence: 'high',
impact: issue.severity === 'critical' ? 'major' : 'moderate',
message: issue.message,
file: issue.file,
line: issue.line,
suggestion: issue.suggestion
})),
strengths: documentationResult.strengths || [],
recommendations: documentationResult.recommendations?.map(r => r.title) || []
};
documentationSpinner.succeed(`Documentation analysis complete (Score: ${documentationResult.score}/100)`);
}
catch (error) {
documentationSpinner.fail('Documentation analysis failed');
results.documentation = { score: 50, issues: [], strengths: [], recommendations: [] };
}
}
if (analysisType === 'comprehensive' || analysisType === 'quality') {
const qualityAnalyzer = new CodeQualityAnalyzer(projectRoot);
const qualitySpinner = ora('Analyzing code quality...').start();
try {
const qualityResult = await qualityAnalyzer.quickScan();
results.quality = {
score: qualityResult.score,
issues: qualityResult.issues.map((issue) => ({
type: issue.rule,
severity: issue.severity === 'error' ? 'high' : issue.severity === 'warning' ? 'medium' : 'low',
confidence: 'medium',
impact: issue.severity === 'error' ? 'major' : 'moderate',
message: issue.message,
file: issue.file,
line: issue.line,
suggestion: issue.message
})),
strengths: [],
recommendations: qualityResult.suggestions || []
};
qualitySpinner.succeed(`Code quality analysis complete (Score: ${qualityResult.score}/100)`);
}
catch (error) {
qualitySpinner.fail('Code quality analysis failed');
results.quality = { score: 50, issues: [], strengths: [], recommendations: [] };
}
}
if (analysisType === 'comprehensive' || analysisType === 'dependencies') {
const dependencyAnalyzer = new DependencyAnalyzer(projectRoot);
const dependencySpinner = ora('Analyzing dependencies...').start();
try {
const dependencyResult = await dependencyAnalyzer.analyze();
const depScore = Math.max(0, 100 - (dependencyResult.vulnerabilities * 20) - (dependencyResult.outdated * 5) - (dependencyResult.unused.length * 2));
const issues = [];
if (dependencyResult.vulnerabilities > 0) {
issues.push({
type: 'vulnerabilities',
severity: 'high',
confidence: 'high',
impact: 'major',
message: `${dependencyResult.vulnerabilities} vulnerable dependencies detected`,
file: 'package.json',
suggestion: 'Update vulnerable dependencies to latest secure versions'
});
}
if (dependencyResult.outdated > 0) {
issues.push({
type: 'outdated',
severity: 'medium',
confidence: 'medium',
impact: 'moderate',
message: `${dependencyResult.outdated} outdated dependencies detected`,
file: 'package.json',
suggestion: 'Update outdated dependencies for better security and features'
});
}
if (dependencyResult.unused.length > 0) {
issues.push({
type: 'unused',
severity: 'low',
confidence: 'medium',
impact: 'minor',
message: `${dependencyResult.unused.length} unused dependencies: ${dependencyResult.unused.slice(0, 3).join(', ')}`,
file: 'package.json',
suggestion: 'Remove unused dependencies to reduce bundle size'
});
}
results.dependencies = {
score: depScore,
issues,
strengths: dependencyResult.totalDependencies > 0 ? [`Managing ${dependencyResult.totalDependencies} dependencies`] : [],
recommendations: issues.map(issue => issue.suggestion)
};
dependencySpinner.succeed(`Dependency analysis complete (Score: ${depScore}/100)`);
}
catch (error) {
dependencySpinner.fail('Dependency analysis failed');
results.dependencies = { score: 50, issues: [], strengths: [], recommendations: [] };
}
}
// Calculate unified score
console.log(chalk.cyan('\nš Calculating Unified Score\n'));
const unifiedScore = UnifiedScoringSystem.calculateUnifiedScore(results);
return {
type: analysisType,
timestamp: new Date().toISOString(),
unifiedScore,
analyzerResults: results,
summary: UnifiedScoringSystem.createScoringSummary(unifiedScore)
};
}
catch (error) {
console.error(chalk.red(`Analysis failed: ${error instanceof Error ? error.message : String(error)}`));
return {
type: analysisType,
timestamp: new Date().toISOString(),
error: error instanceof Error ? error.message : String(error)
};
}
}
/**
* Generate contextual analysis based on gathered information
*/
async function generateContextualAnalysis(analysisType, target, context, deep) {
const analysis = {
type: analysisType,
target,
timestamp: new Date().toISOString(),
context: {
hasRecentMessages: context.recentMessages.length > 0,
hasGitChanges: context.gitChanges.length > 0,
hasRelatedMemories: context.relatedMemories.length > 0,
hasPatterns: context.patterns.length > 0
}
};
switch (analysisType) {
case 'git':
analysis.gitAnalysis = analyzeGitContext(context.gitChanges, context.relatedMemories);
break;
case 'code':
analysis.codeAnalysis = await analyzeCodeContext(target, context);
break;
case 'issue':
analysis.issueAnalysis = analyzeIssueContext(target, context);
break;
case 'patterns':
analysis.patternAnalysis = analyzePatternContext(context.patterns, context.relatedMemories);
break;
default:
analysis.contextAnalysis = analyzeGeneralContext(context);
}
return analysis;
}
/**
* Analyze git changes for commit message suggestions
*/
function analyzeGitContext(gitChanges, relatedMemories) {
if (gitChanges.length === 0) {
return {
message: 'No git changes detected',
suggestions: ['Make some changes first, then run analysis']
};
}
const totalAdditions = gitChanges.reduce((sum, change) => sum + change.additions, 0);
const totalDeletions = gitChanges.reduce((sum, change) => sum + change.deletions, 0);
// Analyze change patterns
const fileTypes = gitChanges.map(change => path.extname(change.file)).filter(ext => ext);
const directories = gitChanges.map(change => path.dirname(change.file));
const changeType = determineChangeType(gitChanges);
// Generate commit message suggestions
const suggestions = generateCommitMessageSuggestions(gitChanges, changeType, relatedMemories);
// Debug output removed
return {
summary: `${gitChanges.length} files changed, ${totalAdditions} insertions(+), ${totalDeletions} deletions(-)`,
changeType,
fileTypes: [...new Set(fileTypes)],
directories: [...new Set(directories)],
commitSuggestions: suggestions,
relatedContext: relatedMemories.length > 0 ?
`Found ${relatedMemories.length} related memories from past work` : null
};
}
/**
* Determine the type of changes being made
*/
function determineChangeType(gitChanges) {
const files = gitChanges.map(change => change.file.toLowerCase());
if (files.some(f => f.includes('test')))
return 'test';
if (files.some(f => f.includes('doc') || f.includes('readme')))
return 'docs';
if (files.some(f => f.includes('config') || f.includes('.json') || f.includes('.yml')))
return 'config';
if (files.some(f => f.includes('style') || f.includes('.css') || f.includes('.scss')))
return 'style';
if (gitChanges.every(change => change.additions === 0))
return 'refactor';
if (gitChanges.some(change => change.file.includes('fix') || change.file.includes('bug')))
return 'fix';
return 'feat';
}
/**
* Generate commit message suggestions based on changes and context
*/
function generateCommitMessageSuggestions(gitChanges, changeType, relatedMemories) {
const suggestions = [];
// Basic type-based suggestions
const fileCount = gitChanges.length;
const mainFiles = gitChanges.slice(0, 3).map(change => path.basename(change.file, path.extname(change.file)));
if (changeType === 'feat') {
suggestions.push(`feat: add ${mainFiles.join(', ')} implementation`);
suggestions.push(`feat: implement ${mainFiles[0]} functionality`);
}
else if (changeType === 'fix') {
suggestions.push(`fix: resolve issues in ${mainFiles.join(', ')}`);
suggestions.push(`fix: correct ${mainFiles[0]} behavior`);
}
else if (changeType === 'docs') {
suggestions.push(`docs: update ${mainFiles.join(', ')} documentation`);
suggestions.push(`docs: improve project documentation`);
}
else if (changeType === 'test') {
suggestions.push(`test: add tests for ${mainFiles.join(', ')}`);
suggestions.push(`test: improve test coverage`);
}
else if (changeType === 'refactor') {
suggestions.push(`refactor: restructure ${mainFiles.join(', ')}`);
suggestions.push(`refactor: improve code organization`);
}
else if (changeType === 'config') {
suggestions.push(`config: update ${mainFiles.join(', ')} configuration`);
suggestions.push(`chore: update project configuration`);
suggestions.push(`config: modify ${mainFiles[0]} settings`);
}
else if (changeType === 'style') {
suggestions.push(`style: update ${mainFiles.join(', ')} styling`);
suggestions.push(`style: improve visual appearance`);
}
// Context-aware suggestions from memory
if (relatedMemories.length > 0) {
const memoryContext = relatedMemories[0].content || '';
if (memoryContext.toLowerCase().includes('bug') || memoryContext.toLowerCase().includes('fix')) {
suggestions.unshift(`fix: address ${memoryContext.split(' ').slice(0, 3).join(' ')}`);
}
else if (memoryContext.toLowerCase().includes('feature') || memoryContext.toLowerCase().includes('add')) {
suggestions.unshift(`feat: ${memoryContext.split(' ').slice(0, 4).join(' ')}`);
}
}
return suggestions.slice(0, 5); // Top 5 suggestions
}
/**
* Analyze code context
*/
async function analyzeCodeContext(codePath, context) {
const fullPath = path.resolve(codePath);
if (!fs.existsSync(fullPath)) {
return {
error: `Path does not exist: ${codePath}`,
suggestions: ['Check the file path and try again']
};
}
const stats = fs.statSync(fullPath);
const isDirectory = stats.isDirectory();
let analysis = {
path: fullPath,
type: isDirectory ? 'directory' : 'file',
exists: true
};
if (isDirectory) {
// Analyze directory structure
const files = fs.readdirSync(fullPath);
analysis.fileCount = files.length;
analysis.fileTypes = [...new Set(files.map(f => path.extname(f)).filter(ext => ext))];
}
else {
// Analyze individual file
const content = fs.readFileSync(fullPath, 'utf-8');
analysis.lines = content.split('\n').length;
analysis.size = stats.size;
analysis.extension = path.extname(fullPath);
// Basic code analysis
if (['.js', '.ts', '.py', '.java', '.cpp', '.c'].includes(analysis.extension)) {
analysis.codeMetrics = analyzeCodeMetrics(content);
}
}
// Add memory context
if (context.relatedMemories.length > 0) {
analysis.relatedWork = context.relatedMemories.map(memory => ({
content: memory.content,
relevance: memory.score
}));
}
return analysis;
}
/**
* Basic code metrics analysis
*/
function analyzeCodeMetrics(code) {
const lines = code.split('\n');
const nonEmptyLines = lines.filter(line => line.trim().length > 0);
const commentLines = lines.filter(line => {
const trimmed = line.trim();
return trimmed.startsWith('//') || trimmed.startsWith('#') ||
trimmed.startsWith('/*') || trimmed.startsWith('*');
});
return {
totalLines: lines.length,
codeLines: nonEmptyLines.length - commentLines.length,
commentLines: commentLines.length,
commentRatio: Math.round((commentLines.length / nonEmptyLines.length) * 100) || 0
};
}
/**
* Analyze issue context
*/
function analyzeIssueContext(issue, context) {
const analysis = {
issue,
understanding: `Analyzing issue: "${issue}"`,
suggestions: []
};
// Basic issue classification
const issueLower = issue.toLowerCase();
if (issueLower.includes('error') || issueLower.includes('bug') || issueLower.includes('fail')) {
analysis.type = 'bug';
analysis.suggestions.push('Check logs for error details');
analysis.suggestions.push('Reproduce the issue with minimal example');
}
else if (issueLower.includes('feature') || issueLower.includes('add') || issueLower.includes('implement')) {
analysis.type = 'feature';
analysis.suggestions.push('Define clear requirements');
analysis.suggestions.push('Break down into smaller tasks');
}
else if (issueLower.includes('slow') || issueLower.includes('performance')) {
analysis.type = 'performance';
analysis.suggestions.push('Profile the application');
analysis.suggestions.push('Identify bottlenecks');
}
// Add related memory context
if (context.relatedMemories.length > 0) {
analysis.relatedExperience = context.relatedMemories.map(memory => ({
content: memory.content,
relevance: memory.score,
lesson: extractLessonFromMemory(memory.content)
}));
}
return analysis;
}
/**
* Extract potential lessons from memory content
*/
function extractLessonFromMemory(content) {
const lessonIndicators = [
'learned that', 'discovered that', 'found that', 'realized that',
'solution was', 'fix was', 'key was', 'important to'
];
for (const indicator of lessonIndicators) {
const index = content.toLowerCase().indexOf(indicator);
if (index !== -1) {
// Extract sentence containing the lesson
const sentences = content.split(/[.!?]/);
const lessonSentence = sentences.find(s => s.toLowerCase().includes(indicator));
return lessonSentence?.trim() || null;
}
}
return null;
}
/**
* Analyze pattern context
*/
function analyzePatternContext(patterns, relatedMemories) {
return {
patternCount: patterns.length,
patterns: patterns.slice(0, 5), // Top 5 patterns
memoryPatterns: relatedMemories.length > 0 ?
'Found related patterns in historical memory' : null
};
}
/**
* Analyze general context
*/
function analyzeGeneralContext(context) {
const insights = [];
if (context.recentMessages.length > 0) {
insights.push(`Found ${context.recentMessages.length} recent conversation messages`);
// Extract common themes from recent messages
const allContent = context.recentMessages.map(msg => msg.content || '').join(' ').toLowerCase();
const keywords = extractKeywords(allContent);
if (keywords.length > 0) {
insights.push(`Recent focus areas: ${keywords.slice(0, 3).join(', ')}`);
}
}
if (context.gitChanges.length > 0) {
insights.push(`${context.gitChanges.length} uncommitted changes detected`);
}
if (context.relatedMemories.length > 0) {
insights.push(`${context.relatedMemories.length} related memories found`);
}
return {
summary: 'General project context analysis',
insights,
recommendations: generateGeneralRecommendations(context)
};
}
/**
* Generate general recommendations based on context
*/
function generateGeneralRecommendations(context) {
const recommendations = [];
if (context.gitChanges.length > 0) {
recommendations.push('Consider committing your current changes');
recommendations.push('Run tests before committing');
}
if (context.recentMessages.length > 0) {
recommendations.push('Document recent decisions in project notes');
}
if (context.relatedMemories.length > 0) {
recommendations.push('Review related memories for insights');
}
else {
recommendations.push('Store key insights in memory for future reference');
}
return recommendations;
}
/**
* Display unified analysis results
*/
function displayUnifiedAnalysisResults(analysis) {
if (analysis.error) {
console.log(chalk.red(`ā ${analysis.error}`));
return;
}
const { unifiedScore, analyzerResults } = analysis;
// Display overall unified score
console.log(chalk.cyan('šÆ Unified Analysis Results'));
console.log(chalk.gray('ā'.repeat(60)));
const scoreColor = unifiedScore.overall >= 80 ? chalk.green :
unifiedScore.overall >= 60 ? chalk.yellow : chalk.red;
console.log(scoreColor(`\nš Overall Score: ${unifiedScore.overall}/100`));
console.log(chalk.gray(`Risk Level: ${unifiedScore.risk.level.toUpperCase()}`));
console.log(chalk.gray(`Time to Address: ${unifiedScore.risk.timeToAddress}`));
// Display component scores
console.log(chalk.cyan('\nš Component Scores:'));
console.log(chalk.gray(` Security: ${unifiedScore.security}/100`));
console.log(chalk.gray(` Performance: ${unifiedScore.performance}/100`));
console.log(chalk.gray(` Quality: ${unifiedScore.quality}/100`));
console.log(chalk.gray(` Maintainability: ${unifiedScore.maintainability}/100`));
console.log(chalk.gray(` Documentation: ${unifiedScore.documentation}/100`));
// Display critical issues
const allIssues = Object.values(analyzerResults).flatMap((result) => result.issues || []);
const criticalIssues = allIssues.filter((issue) => issue.severity === 'critical');
const highIssues = allIssues.filter((issue) => issue.severity === 'high');
if (criticalIssues.length > 0) {
console.log(chalk.red('\nšØ Critical Issues:'));
criticalIssues.slice(0, 5).forEach((issue, index) => {
console.log(chalk.red(` ${index + 1}. ${issue.message}`));
if (issue.file) {
console.log(chalk.gray(` File: ${issue.file}${issue.line ? `:${issue.line}` : ''}`));
}
if (issue.suggestion) {
console.log(chalk.yellow(` š” ${issue.suggestion}`));
}
});
}
if (highIssues.length > 0) {
console.log(chalk.yellow('\nā ļø High Priority Issues:'));
highIssues.slice(0, 3).forEach((issue, index) => {
console.log(chalk.yellow(` ${index + 1}. ${issue.message}`));
if (issue.file) {
console.log(chalk.gray(` File: ${issue.file}${issue.line ? `:${issue.line}` : ''}`));
}
});
}
// Display risk factors
if (unifiedScore.risk.factors && unifiedScore.risk.factors.length > 0) {
console.log(chalk.magenta('\nā” Risk Factors:'));
unifiedScore.risk.factors.slice(0, 3).forEach((factor, index) => {
console.log(chalk.magenta(` ${index + 1}. ${factor.description}`));
console.log(chalk.gray(` Type: ${factor.type}, Severity: ${factor.severity}`));
console.log(chalk.blue(` š” ${factor.mitigation}`));
});
}
// Display recommendations
if (unifiedScore.risk.recommendations && unifiedScore.risk.recommendations.length > 0) {
console.log(chalk.green('\nš Key Recommendations:'));
unifiedScore.risk.recommendations.slice(0, 5).forEach((rec, index) => {
console.log(chalk.green(` ${index + 1}. ${rec}`));
});
}
// Display strengths
const allStrengths = Object.values(analyzerResults).flatMap((result) => result.strengths || []);
if (allStrengths.length > 0) {
console.log(chalk.cyan('\nšŖ Project Strengths:'));
[...new Set(allStrengths)].slice(0, 5).forEach((strength, index) => {
console.log(chalk.cyan(` ā ${strength}`));
});
}
// Display summary
console.log(chalk.white('\nš Summary:'));
console.log(analysis.summary);
}
/**
* Display comprehensive analysis results
*/
function displayAnalysisResults(analysis, context) {
// Check if this is a unified analysis result
if (analysis.unifiedScore && analysis.analyzerResults) {
displayUnifiedAnalysisResults(analysis);
return;
}
// Display context summary
if (context.recentMessages.length > 0 || context.gitChanges.length > 0 || context.relatedMemories.length > 0) {
console.log(chalk.cyan('š Analysis Context'));
console.log(chalk.gray('ā'.repeat(50)));
if (context.recentMessages.length > 0) {
console.log(chalk.white(`š£ļø Recent conversations: ${context.recentMessages.length} messages`));
}
if (context.gitChanges.length > 0) {
console.log(chalk.white(`š Git changes: ${context.gitChanges.length} files modified`));
}
if (context.relatedMemories.length > 0) {
console.log(chalk.white(`š§ Related memories: ${context.relatedMemories.length} found`));
}
console.log('');
}
// Display type-specific analysis
if (analysis.gitAnalysis) {
displayGitAnalysis(analysis.gitAnalysis);
}
else if (analysis.codeAnalysis) {
displayCodeAnalysis(analysis.codeAnalysis);
}
else if (analysis.issueAnalysis) {
displayIssueAnalysis(analysis.issueAnalysis);
}
else if (analysis.patternAnalysis) {
displayPatternAnalysis(analysis.patternAnalysis);
}
else if (analysis.contextAnalysis) {
displayContextAnalysis(analysis.contextAnalysis);
}
// Display related memories if available
if (context.relatedMemories.length > 0) {
console.log(chalk.blue('\nš§ Related Memories'));
console.log(chalk.gray('ā'.repeat(50)));
context.relatedMemories.slice(0, 3).forEach((memory, index) => {
const content = memory.content || '';
const preview = content.length > 100 ? content.substring(0, 100) + '...' : content;
console.log(chalk.blue(`${index + 1}. ${preview}`));
if (memory.score) {
console.log(chalk.gray(` Relevance: ${Math.round(memory.score * 100)}%`));
}
});
}
}
/**
* Display git analysis results
*/
function displayGitAnalysis(gitAnalysis) {
console.log(chalk.cyan('š Git Analysis'));
console.log(chalk.gray('ā'.repeat(50)));
console.log(chalk.white(gitAnalysis.summary));
if (gitAnalysis.changeType) {
console.log(chalk.yellow(`\nš Change Type: ${gitAnalysis.changeType}`));
}
if (gitAnalysis.commitSuggestions && gitAnalysis.commitSuggestions.length > 0) {
console.log(chalk.green('\nš” Commit Message Suggestions:'));
gitAnalysis.commitSuggestions.forEach((suggestion, index) => {
console.log(chalk.green(` ${index + 1}. ${suggestion}`));
});
}
else if (gitAnalysis.message) {
console.log(chalk.yellow(`\n${gitAnalysis.message}`));
if (gitAnalysis.suggestions) {
gitAnalysis.suggestions.forEach((suggestion, index) => {
console.log(chalk.gray(` ${index + 1}. ${suggestion}`));
});
}
}
if (gitAnalysis.relatedContext) {
console.log(chalk.gray(`\n${gitAnalysis.relatedContext}`));
}
}
/**
* Display code analysis results
*/
function displayCodeAnalysis(codeAnalysis) {
console.log(chalk.cyan('š Code Analysis'));
console.log(chalk.gray('ā'.repeat(50)));
if (codeAnalysis.error) {
console.log(chalk.red(`ā ${codeAnalysis.error}`));
return;
}
console.log(chalk.white(`Path: ${codeAnalysis.path}`));
console.log(chalk.white(`Type: ${codeAnalysis.type}`));
if (codeAnalysis.codeMetrics) {
const metrics = codeAnalysis.codeMetrics;
console.log(chalk.yellow('\nš Code Metrics:'));
console.log(chalk.gray(` Lines: ${metrics.totalLines} (${metrics.codeLines} code, ${metrics.commentLines} comments)`));
console.log(chalk.gray(` Comment ratio: ${metrics.commentRatio}%`));
}
if (codeAnalysis.relatedWork && codeAnalysis.relatedWork.length > 0) {
console.log(chalk.blue('\nš Related Work:'));
codeAnalysis.relatedWork.slice(0, 2).forEach((work, index) => {
console.log(chalk.blue(` ${index + 1}. ${work.content.substring(0, 80)}...`));
});
}
}
/**
* Display issue analysis results
*/
function displayIssueAnalysis(issueAnalysis) {
console.log(chalk.cyan('š Issue Analysis'));
console.log(chalk.gray('ā'.repeat(50)));
console.log(chalk.white(issueAnalysis.understanding));
if (issueAnalysis.type) {
console.log(chalk.yellow(`\nš·ļø Type: ${issueAnalysis.type}`));
}
if (issueAnalysis.suggestions && issueAnalysis.suggestions.length > 0) {
console.log(chalk.green('\nš” Suggestions:'));
issueAnalysis.suggestions.forEach((suggestion, index) => {
console.log(chalk.green(` ${index + 1}. ${suggestion}`));
});
}
if (issueAnalysis.relatedExperience && issueAnalysis.relatedExperience.length > 0) {
console.log(chalk.blue('\nš Related Experience:'));
issueAnalysis.relatedExperience.forEach((exp, index) => {
console.log(chalk.blue(` ${index + 1}. ${exp.content.substring(0, 80)}...`));
if (exp.lesson) {
console.log(chalk.gray(` š” Lesson: ${exp.lesson}`));
}
});
}
}
/**
* Display pattern analysis results
*/
function displayPatternAnalysis(patternAnalysis) {
console.log(chalk.cyan('š® Pattern Analysis'));
console.log(chalk.gray('ā'.repeat(50)));
console.log(chalk.white(`Found ${patternAnalysis.patternCount} patterns`));
if (patternAnalysis.patterns && patternAnalysis.patterns.length > 0) {
console.log(chalk.yellow('\nš Development Patterns:'));
patternAnalysis.patterns.forEach((pattern, index) => {
console.log(chalk.yellow(` ${index + 1}. ${pattern.name || 'Pattern'}: ${pattern.description || 'No description'}`));
});
}
}
/**
* Display context analysis results
*/
function displayContextAnalysis(contextAnalysis) {
console.log(chalk.cyan('šļø Context Analysis'));
console.log(chalk.gray('ā'.repeat(50)));
console.log(chalk.white(contextAnalysis.summary));
if (contextAnalysis.insights && contextAnalysis.insights.length > 0) {
console.log(chalk.yellow('\nš Insights:'));
contextAnalysis.insights.forEach((insight) => {
console.log(chalk.yellow(` ⢠${insight}`));
});
}
if (contextAnalysis.recommendations && contextAnalysis.recommendations.length > 0) {
console.log(chalk.green('\nš” Recommendations:'));
contextAnalysis.recommendations.forEach((rec, index) => {
console.log(chalk.green(` ${index + 1}. ${rec}`));
});
}
}
//# sourceMappingURL=analyze.js.map