codecrucible-synth
Version:
Production-Ready AI Development Platform with Multi-Voice Synthesis, Smithery MCP Integration, Enterprise Security, and Zero-Timeout Reliability
675 lines • 26.3 kB
JavaScript
/**
* Project Intelligence System - Enhanced Context Awareness
* Iteration 3: Add enhanced context awareness and project intelligence
*/
import { EventEmitter } from 'events';
import { readdir, readFile, stat } from 'fs/promises';
import { join, relative, extname, basename } from 'path';
import { Logger } from '../logger.js';
import { unifiedCache } from '../cache/unified-cache-system.js';
export class ProjectIntelligenceSystem extends EventEmitter {
logger;
analysisInProgress = new Set();
constructor() {
super();
this.logger = new Logger('ProjectIntelligenceSystem');
}
/**
* Analyze project and generate comprehensive intelligence
*/
async analyzeProject(rootPath, options = {}) {
const normalizedPath = join(rootPath);
// Check if analysis is already in progress
if (this.analysisInProgress.has(normalizedPath)) {
throw new Error(`Analysis already in progress for ${normalizedPath}`);
}
// Check cache first
const cacheKey = `project-intel:${normalizedPath}`;
if (!options.force) {
const cacheResult = await unifiedCache.get(cacheKey);
if (cacheResult?.hit) {
this.logger.info(`Using cached analysis for ${normalizedPath}`);
return cacheResult.value;
}
}
this.analysisInProgress.add(normalizedPath);
this.logger.info(`Starting comprehensive project analysis for ${normalizedPath}`);
try {
const startTime = Date.now();
// Phase 1: Structural Analysis
this.emit('analysis:phase', { phase: 'structure', progress: 0 });
const structure = await this.analyzeProjectStructure(rootPath, options);
// Phase 2: Code Analysis
this.emit('analysis:phase', { phase: 'code', progress: 20 });
const insights = await this.generateProjectInsights(structure, options);
// Phase 3: Dependency Analysis
this.emit('analysis:phase', { phase: 'dependencies', progress: 40 });
const dependencies = await this.analyzeDependencies(structure, options);
// Phase 4: Architecture Analysis
this.emit('analysis:phase', { phase: 'architecture', progress: 60 });
const patterns = await this.identifyArchitecturePatterns(structure, insights, dependencies);
// Phase 5: Metadata Extraction
this.emit('analysis:phase', { phase: 'metadata', progress: 80 });
const metadata = await this.extractProjectMetadata(structure, options);
// Phase 6: Generate Recommendations
this.emit('analysis:phase', { phase: 'recommendations', progress: 90 });
const recommendations = await this.generateRecommendations(structure, insights, dependencies, patterns, metadata);
const intelligence = {
structure,
insights,
dependencies,
patterns,
metadata,
recommendations,
};
// Cache the results with 1 hour TTL and project intelligence tags
await unifiedCache.set(cacheKey, intelligence, {
ttl: 3600000, // 1 hour
tags: ['project-intelligence', 'analysis'],
metadata: { path: normalizedPath, analysisTime: Date.now() - startTime },
});
const analysisTime = Date.now() - startTime;
this.logger.info(`Project analysis completed in ${analysisTime}ms`);
this.emit('analysis:complete', { path: normalizedPath, intelligence, time: analysisTime });
return intelligence;
}
finally {
this.analysisInProgress.delete(normalizedPath);
}
}
/**
* Analyze project structure and file organization
*/
async analyzeProjectStructure(rootPath, options) {
const files = [];
const directories = [];
const packageFiles = [];
const configFiles = [];
const documentationFiles = [];
const testFiles = [];
const buildFiles = [];
let totalSize = 0;
const scanDirectory = async (dirPath, depth = 0) => {
try {
const entries = await readdir(dirPath, { withFileTypes: true });
for (const entry of entries) {
const fullPath = join(dirPath, entry.name);
const relativePath = relative(rootPath, fullPath);
// Skip common ignore patterns
if (this.shouldIgnoreFile(relativePath))
continue;
if (entry.isDirectory()) {
const dirNode = {
path: relativePath,
name: entry.name,
depth,
fileCount: 0,
childDirectories: [],
purpose: this.determineDirectoryPurpose(entry.name, relativePath),
importance: this.assessDirectoryImportance(entry.name, relativePath),
};
directories.push(dirNode);
// Recursively scan subdirectories
if (depth < (options.maxDepth || 10)) {
await scanDirectory(fullPath, depth + 1);
}
}
else if (entry.isFile()) {
const stats = await stat(fullPath);
const fileNode = await this.analyzeFile(fullPath, relativePath, stats);
files.push(fileNode);
totalSize += stats.size;
// Categorize special files
this.categorizeSpecialFile(fileNode, packageFiles, configFiles, documentationFiles, testFiles, buildFiles);
}
}
}
catch (error) {
this.logger.warn(`Error scanning directory ${dirPath}:`, error);
}
};
await scanDirectory(rootPath);
// Update directory file counts
this.updateDirectoryFileCounts(directories, files);
return {
rootPath,
directories,
files,
packageFiles,
configFiles,
documentationFiles,
testFiles,
buildFiles,
totalFiles: files.length,
totalDirectories: directories.length,
codebaseSize: totalSize,
};
}
/**
* Generate comprehensive project insights
*/
async generateProjectInsights(structure, options) {
// Analyze languages
const languageStats = this.analyzeLanguageDistribution(structure.files);
const primaryLanguage = Object.keys(languageStats).reduce((a, b) => (languageStats[a] > languageStats[b] ? a : b), '');
// Detect frameworks
const frameworksDetected = await this.detectFrameworks(structure);
// Determine project type and architecture
const projectType = this.determineProjectType(structure, frameworksDetected);
const architecturePattern = this.identifyPrimaryArchitecture(structure, frameworksDetected);
// Assess maturity and quality
const maturityLevel = this.assessProjectMaturity(structure);
const codeQuality = await this.assessCodeQuality(structure);
const technicalDebt = await this.analyzeTechnicalDebt(structure);
// Security and performance analysis
const securityConcerns = await this.identifySecurityConcerns(structure);
const performanceIndicators = await this.analyzePerformanceIndicators(structure);
return {
primaryLanguage,
languageDistribution: languageStats,
frameworksDetected,
architecturePattern,
projectType,
maturityLevel,
codeQuality,
technicalDebt,
securityConcerns,
performanceIndicators,
};
}
/**
* Analyze project dependencies and relationships
*/
async analyzeDependencies(structure, options) {
// This is a simplified implementation - in practice, this would involve
// parsing import statements, package files, etc.
const nodes = [];
const edges = [];
const externalDependencies = [];
const internalDependencies = [];
// Analyze package files for external dependencies
for (const pkg of structure.packageFiles) {
for (const dep of pkg.dependencies) {
externalDependencies.push({
name: dep,
version: 'unknown',
type: 'production',
usage: [],
risk: 'low',
});
}
}
// Analyze internal file dependencies (simplified)
for (const file of structure.files) {
nodes.push({
id: file.path,
name: file.name,
type: 'internal',
importance: file.importance === 'critical' ? 1 : file.importance === 'high' ? 0.8 : 0.5,
});
}
return {
nodes,
edges,
cycles: [],
criticalPath: [],
modularity: {
cohesion: 0.7,
coupling: 0.3,
modularity: 0.8,
instability: 0.4,
},
externalDependencies,
internalDependencies,
};
}
/**
* Identify architecture patterns and design principles
*/
async identifyArchitecturePatterns(structure, insights, dependencies) {
// Simplified pattern detection
const primaryPattern = insights.architecturePattern || 'modular';
return {
primaryPattern,
secondaryPatterns: ['layered'],
designPrinciples: ['separation of concerns', 'single responsibility'],
antiPatterns: [],
refactoringOpportunities: [],
};
}
/**
* Extract project metadata
*/
async extractProjectMetadata(structure, options) {
let metadata = {
name: basename(structure.rootPath),
lastModified: new Date(),
};
// Try to extract from package.json or similar
for (const pkg of structure.packageFiles) {
if (pkg.path.endsWith('package.json')) {
try {
const content = await readFile(join(structure.rootPath, pkg.path), 'utf8');
const packageInfo = JSON.parse(content);
metadata = {
...metadata,
name: packageInfo.name || metadata.name,
version: packageInfo.version,
description: packageInfo.description,
author: typeof packageInfo.author === 'string'
? packageInfo.author
: packageInfo.author?.name,
license: packageInfo.license,
repository: typeof packageInfo.repository === 'string'
? packageInfo.repository
: packageInfo.repository?.url,
homepage: packageInfo.homepage,
keywords: packageInfo.keywords,
};
break;
}
catch (error) {
this.logger.warn(`Error parsing ${pkg.path}:`, error);
}
}
}
// Calculate project statistics
const stats = this.calculateProjectStats(structure);
return {
name: metadata.name || 'Unknown Project',
version: metadata.version,
description: metadata.description,
author: metadata.author,
license: metadata.license,
repository: metadata.repository,
homepage: metadata.homepage,
keywords: metadata.keywords,
lastModified: metadata.lastModified,
stats,
};
}
/**
* Generate intelligent recommendations
*/
async generateRecommendations(structure, insights, dependencies, patterns, metadata) {
return {
codeImprovement: [],
architecture: [],
performance: [],
security: [],
testing: [],
documentation: [],
maintenance: [],
};
}
// Helper methods (simplified implementations)
shouldIgnoreFile(path) {
const ignorePatterns = [
'node_modules',
'.git',
'.vscode',
'.idea',
'dist',
'build',
'coverage',
'.nyc_output',
'.DS_Store',
'Thumbs.db',
'*.log',
];
return ignorePatterns.some(pattern => path.includes(pattern));
}
determineDirectoryPurpose(name, path) {
const purposeMap = {
src: 'source',
source: 'source',
lib: 'source',
test: 'tests',
tests: 'tests',
__tests__: 'tests',
spec: 'tests',
build: 'build',
dist: 'build',
config: 'config',
docs: 'docs',
doc: 'docs',
assets: 'assets',
static: 'assets',
node_modules: 'dependencies',
};
return purposeMap[name.toLowerCase()] || 'other';
}
assessDirectoryImportance(name, path) {
if (['src', 'source', 'lib'].includes(name.toLowerCase()))
return 'high';
if (['test', 'tests', 'config', 'docs'].includes(name.toLowerCase()))
return 'medium';
return 'low';
}
async analyzeFile(fullPath, relativePath, stats) {
const ext = extname(relativePath);
const name = basename(relativePath);
const language = this.detectLanguage(ext);
let linesOfCode = 0;
let complexity = {
cyclomaticComplexity: 0,
cognitiveComplexity: 0,
nestingDepth: 0,
functionCount: 0,
classCount: 0,
};
// Read and analyze file content for code files
if (this.isCodeFile(ext)) {
try {
const content = await readFile(fullPath, 'utf8');
linesOfCode = content.split('\n').length;
complexity = this.analyzeCodeComplexity(content, language);
}
catch (error) {
this.logger.warn(`Error reading file ${fullPath}:`, error);
}
}
return {
path: relativePath,
name,
extension: ext,
language,
size: stats.size,
linesOfCode,
complexity,
dependencies: [],
exports: [],
imports: [],
functions: [],
classes: [],
interfaces: [],
purpose: this.determineFilePurpose(name, relativePath),
lastModified: stats.mtime,
importance: this.assessFileImportance(name, relativePath),
};
}
detectLanguage(extension) {
const languageMap = {
'.js': 'JavaScript',
'.ts': 'TypeScript',
'.jsx': 'JavaScript',
'.tsx': 'TypeScript',
'.py': 'Python',
'.java': 'Java',
'.cs': 'C#',
'.cpp': 'C++',
'.c': 'C',
'.go': 'Go',
'.rs': 'Rust',
'.php': 'PHP',
'.rb': 'Ruby',
'.swift': 'Swift',
'.kt': 'Kotlin',
};
return languageMap[extension.toLowerCase()] || 'Unknown';
}
isCodeFile(extension) {
const codeExtensions = [
'.js',
'.ts',
'.jsx',
'.tsx',
'.py',
'.java',
'.cs',
'.cpp',
'.c',
'.go',
'.rs',
'.php',
'.rb',
'.swift',
'.kt',
'.scala',
'.r',
'.m',
];
return codeExtensions.includes(extension.toLowerCase());
}
determineFilePurpose(name, path) {
if (name.toLowerCase().includes('test') ||
path.includes('/test/') ||
path.includes('/__tests__/'))
return 'test';
if (name.toLowerCase().includes('config') || name.includes('.config.'))
return 'config';
if (['index.js', 'index.ts', 'main.js', 'main.ts', 'app.js', 'app.ts'].includes(name.toLowerCase()))
return 'entry';
if (name.toLowerCase().includes('util') || name.toLowerCase().includes('helper'))
return 'utility';
if (['readme.md', 'readme.txt', 'changelog.md'].includes(name.toLowerCase()))
return 'documentation';
return 'core';
}
assessFileImportance(name, path) {
if (['index.js', 'index.ts', 'main.js', 'main.ts', 'app.js', 'app.ts'].includes(name.toLowerCase()))
return 'critical';
if (path.startsWith('src/') && !path.includes('/test/'))
return 'high';
if (name.toLowerCase().includes('test'))
return 'medium';
return 'low';
}
analyzeCodeComplexity(content, language) {
// Simplified complexity analysis
const lines = content.split('\n');
const functionMatches = content.match(/function\s+\w+|def\s+\w+|class\s+\w+/gi) || [];
const classMatches = content.match(/class\s+\w+/gi) || [];
return {
cyclomaticComplexity: Math.min(functionMatches.length * 2, 50),
cognitiveComplexity: Math.min(functionMatches.length * 1.5, 40),
nestingDepth: Math.min(lines.filter(line => line.trim().startsWith('if') || line.trim().startsWith('for')).length, 10),
functionCount: functionMatches.length,
classCount: classMatches.length,
};
}
categorizeSpecialFile(file, packageFiles, configFiles, documentationFiles, testFiles, buildFiles) {
const name = file.name.toLowerCase();
if (name === 'package.json' ||
name === 'requirements.txt' ||
name === 'cargo.toml' ||
name === 'pom.xml') {
packageFiles.push({
path: file.path,
type: name.includes('.json') ? 'package.json' : 'other',
dependencies: [],
devDependencies: [],
scripts: {},
});
}
if (name.includes('config') || name.includes('.config.') || name.includes('.env')) {
configFiles.push({
path: file.path,
type: file.extension,
purpose: 'configuration',
settings: {},
});
}
if (name.includes('readme') || name.includes('doc') || file.extension === '.md') {
documentationFiles.push({
path: file.path,
type: name.includes('readme') ? 'readme' : 'other',
quality: 'fair',
coverage: [],
});
}
if (file.purpose === 'test') {
testFiles.push({
path: file.path,
type: 'unit',
framework: 'unknown',
coverage: [],
});
}
if (name.includes('build') ||
name.includes('webpack') ||
name.includes('rollup') ||
name.includes('gulp')) {
buildFiles.push({
path: file.path,
type: file.extension,
commands: [],
targets: [],
});
}
}
updateDirectoryFileCounts(directories, files) {
for (const dir of directories) {
dir.fileCount = files.filter(file => file.path.startsWith(dir.path + '/')).length;
dir.childDirectories = directories
.filter(d => d.path.startsWith(dir.path + '/') && d.depth === dir.depth + 1)
.map(d => d.path);
}
}
analyzeLanguageDistribution(files) {
const distribution = {};
for (const file of files) {
if (this.isCodeFile(file.extension)) {
distribution[file.language] = (distribution[file.language] || 0) + file.linesOfCode;
}
}
return distribution;
}
async detectFrameworks(structure) {
const frameworks = [];
// Check package files for framework dependencies
for (const pkg of structure.packageFiles) {
if (pkg.dependencies.includes('react')) {
frameworks.push({ name: 'React', confidence: 0.9, evidence: ['package dependency'] });
}
if (pkg.dependencies.includes('vue')) {
frameworks.push({ name: 'Vue', confidence: 0.9, evidence: ['package dependency'] });
}
if (pkg.dependencies.includes('angular')) {
frameworks.push({ name: 'Angular', confidence: 0.9, evidence: ['package dependency'] });
}
if (pkg.dependencies.includes('express')) {
frameworks.push({ name: 'Express', confidence: 0.9, evidence: ['package dependency'] });
}
}
return frameworks;
}
determineProjectType(structure, frameworks) {
// Check for library indicators
if (structure.packageFiles.some(pkg => pkg.path.includes('package.json'))) {
return 'library';
}
// Check for application indicators
if (frameworks.some(f => ['React', 'Vue', 'Angular'].includes(f.name))) {
return 'application';
}
// Check for service indicators
if (frameworks.some(f => f.name === 'Express')) {
return 'service';
}
return 'unknown';
}
identifyPrimaryArchitecture(structure, frameworks) {
// Simple architecture detection based on directory structure
const hasControllers = structure.directories.some(d => d.name.toLowerCase().includes('controller'));
const hasModels = structure.directories.some(d => d.name.toLowerCase().includes('model'));
const hasViews = structure.directories.some(d => d.name.toLowerCase().includes('view'));
if (hasControllers && hasModels && hasViews) {
return 'mvc';
}
if (structure.directories.some(d => d.name.toLowerCase().includes('service'))) {
return 'microservices';
}
return 'modular';
}
assessProjectMaturity(structure) {
const hasTests = structure.testFiles.length > 0;
const hasDocumentation = structure.documentationFiles.length > 0;
const hasConfig = structure.configFiles.length > 0;
const hasBuildSystem = structure.buildFiles.length > 0;
const maturityScore = [hasTests, hasDocumentation, hasConfig, hasBuildSystem].filter(Boolean).length;
if (maturityScore >= 3)
return 'production';
if (maturityScore >= 2)
return 'development';
return 'prototype';
}
async assessCodeQuality(structure) {
// Simplified quality assessment
const totalFiles = structure.files.filter(f => this.isCodeFile(f.extension)).length;
const testFiles = structure.testFiles.length;
return {
maintainabilityIndex: 75,
duplication: 0.1,
testCoverage: totalFiles > 0 ? (testFiles / totalFiles) * 100 : 0,
commentDensity: 0.15,
naming: { score: 0.8, issues: [], suggestions: [] },
structure: { score: 0.75, issues: [], suggestions: [] },
consistency: { score: 0.85, issues: [], suggestions: [] },
};
}
async analyzeTechnicalDebt(structure) {
// Simplified debt analysis
return {
totalDebt: 5,
debtItems: [],
debtRatio: 0.1,
prioritizedFixes: [],
};
}
async identifySecurityConcerns(structure) {
// Simplified security analysis
return [];
}
async analyzePerformanceIndicators(structure) {
// Simplified performance analysis
return [];
}
calculateProjectStats(structure) {
const codeFiles = structure.files.filter(f => this.isCodeFile(f.extension));
const totalLines = codeFiles.reduce((sum, file) => sum + file.linesOfCode, 0);
return {
totalFiles: structure.files.length,
totalLines,
codeLines: Math.floor(totalLines * 0.8), // Estimate
commentLines: Math.floor(totalLines * 0.15), // Estimate
blankLines: Math.floor(totalLines * 0.05), // Estimate
testFiles: structure.testFiles.length,
configFiles: structure.configFiles.length,
documentFiles: structure.documentationFiles.length,
};
}
/**
* Get cached analysis if available
*/
async getCachedAnalysis(path) {
const cacheKey = `project-intel:${join(path)}`;
const cacheResult = await unifiedCache.get(cacheKey);
return cacheResult?.value || null;
}
/**
* Clear analysis cache
*/
async clearCache(path) {
if (path) {
const cacheKey = `project-intel:${join(path)}`;
await unifiedCache.delete(cacheKey);
}
else {
await unifiedCache.clearByTags(['project-intelligence']);
}
}
/**
* Get system metrics
*/
async getSystemMetrics() {
const cacheStats = await unifiedCache.getStats();
return {
cachedAnalyses: cacheStats.size || 0,
activeAnalyses: this.analysisInProgress.size,
memoryUsage: process.memoryUsage(),
};
}
}
export default ProjectIntelligenceSystem;
//# sourceMappingURL=project-intelligence-system.js.map