aios-core
Version:
Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework
635 lines (553 loc) • 18.2 kB
JavaScript
const fs = require('fs').promises;
const path = require('path');
const chalk = require('chalk');
/**
* Usage pattern analyzer for Synkra AIOS framework components
* Analyzes how components are used throughout the codebase
*/
class UsageAnalytics {
constructor(options = {}) {
this.rootPath = options.rootPath || process.cwd();
this.cacheTimeout = options.cacheTimeout || 300000; // 5 minutes
this.usageCache = new Map();
this.patterns = {
imports: [
/require\(['"`]([^'"`]+)['"`]\)/g,
/import .* from ['"`]([^'"`]+)['"`]/g,
/import\(['"`]([^'"`]+)['"`]\)/g,
],
taskCalls: [
/\*([a-zA-Z-]+)/g,
/executeTask\(['"`]([^'"`]+)['"`]\)/g,
],
agentReferences: [
/aios-developer/g,
/meta-agent/g,
/@agent\s+([a-zA-Z-]+)/g,
],
utilCalls: [
/require\(['"`]\.\.\/utils\/([^'"`]+)['"`]\)/g,
/from ['"`]\.\.\/utils\/([^'"`]+)['"`]/g,
],
};
}
/**
* Analyze usage patterns for all components
*/
async analyzeUsagePatterns(components) {
const usage = {
timestamp: new Date().toISOString(),
component_usage: {},
hotspots: [],
unused_components: [],
popular_components: [],
usage_trends: {},
cross_references: {},
efficiency_score: 0,
recommendations: [],
};
try {
console.log(chalk.blue('Analyzing component usage patterns...'));
// Analyze usage for each component
for (const component of components) {
const componentUsage = await this.analyzeComponentUsage(component);
usage.component_usage[component.id] = componentUsage;
}
// Calculate derived metrics
usage.hotspots = this.identifyHotspots(usage.component_usage);
usage.unused_components = this.findUnusedComponents(usage.component_usage);
usage.popular_components = this.findPopularComponents(usage.component_usage);
usage.cross_references = await this.analyzeCrossReferences(components);
usage.efficiency_score = this.calculateEfficiencyScore(usage);
usage.recommendations = this.generateUsageRecommendations(usage);
console.log(chalk.green(`✅ Analyzed usage for ${components.length} components`));
return usage;
} catch (error) {
console.error(chalk.red(`Usage analysis failed: ${error.message}`));
throw error;
}
}
/**
* Analyze usage patterns for a single component
*/
async analyzeComponentUsage(component) {
const cacheKey = `${component.id}-${component.last_modified}`;
if (this.usageCache.has(cacheKey)) {
const cached = this.usageCache.get(cacheKey);
if (Date.now() - cached.timestamp < this.cacheTimeout) {
return cached.data;
}
}
const usage = {
component_id: component.id,
type: component.type,
direct_references: 0,
indirect_references: 0,
referencing_files: [],
usage_contexts: [],
popularity_score: 0,
last_used: null,
usage_frequency: 'never',
dependency_depth: 0,
integration_points: [],
};
try {
// Search for direct references
const directRefs = await this.findDirectReferences(component);
usage.direct_references = directRefs.count;
usage.referencing_files = directRefs.files;
// Search for indirect references
const indirectRefs = await this.findIndirectReferences(component);
usage.indirect_references = indirectRefs.count;
// Analyze usage contexts
usage.usage_contexts = await this.analyzeUsageContexts(component, directRefs.files);
// Calculate metrics
usage.popularity_score = this.calculatePopularityScore(usage);
usage.last_used = await this.findLastUsageDate(component);
usage.usage_frequency = this.calculateUsageFrequency(usage);
usage.dependency_depth = await this.calculateDependencyDepth(component);
usage.integration_points = await this.findIntegrationPoints(component);
// Cache the result
this.usageCache.set(cacheKey, {
timestamp: Date.now(),
data: usage,
});
return usage;
} catch (error) {
console.warn(chalk.yellow(`Failed to analyze usage for ${component.id}: ${error.message}`));
return usage;
}
}
/**
* Find direct references to a component
*/
async findDirectReferences(component) {
const references = {
count: 0,
files: [],
locations: [],
};
try {
const searchPaths = [
path.join(this.rootPath, 'aios-core'),
path.join(this.rootPath, 'tests'),
path.join(this.rootPath, 'docs'),
path.join(this.rootPath, 'examples'),
];
for (const searchPath of searchPaths) {
try {
await fs.access(searchPath);
await this.searchInDirectory(searchPath, component, references);
} catch (error) {
// Directory doesn't exist, skip
}
}
} catch (error) {
console.warn(`Direct reference search failed: ${error.message}`);
}
return references;
}
/**
* Search for component references in a directory
*/
async searchInDirectory(dirPath, component, references) {
try {
const entries = await fs.readdir(dirPath, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dirPath, entry.name);
if (entry.isDirectory()) {
// Skip excluded directories
if (this.isExcludedDirectory(entry.name)) continue;
await this.searchInDirectory(fullPath, component, references);
} else if (entry.isFile() && this.isSearchableFile(entry.name)) {
await this.searchInFile(fullPath, component, references);
}
}
} catch (error) {
// Directory not accessible, skip
}
}
/**
* Search for component references in a file
*/
async searchInFile(filePath, component, references) {
try {
const content = await fs.readFile(filePath, 'utf-8');
const relativePath = path.relative(this.rootPath, filePath);
let foundReferences = false;
// Search based on component type
switch (component.type) {
case 'agent':
foundReferences = this.searchAgentReferences(content, component);
break;
case 'task':
foundReferences = this.searchTaskReferences(content, component);
break;
case 'workflow':
foundReferences = this.searchWorkflowReferences(content, component);
break;
case 'utility':
foundReferences = this.searchUtilityReferences(content, component);
break;
default:
foundReferences = this.searchGenericReferences(content, component);
}
if (foundReferences.count > 0) {
references.count += foundReferences.count;
if (!references.files.includes(relativePath)) {
references.files.push(relativePath);
}
foundReferences.locations.forEach(loc => {
references.locations.push({
file: relativePath,
line: loc.line,
context: loc.context,
type: loc.type,
});
});
}
} catch (error) {
// File not readable, skip
}
}
/**
* Search for agent references
*/
searchAgentReferences(content, component) {
const results = { count: 0, locations: [] };
const lines = content.split('\n');
// Look for agent name mentions
const agentPatterns = [
new RegExp(`@agent\\s+${component.id}`, 'gi'),
new RegExp(`agent:\\s*['"\`]${component.id}['"\`]`, 'gi'),
new RegExp(`${component.name}`, 'gi'),
];
lines.forEach((line, index) => {
agentPatterns.forEach(pattern => {
const matches = line.match(pattern);
if (matches) {
results.count += matches.length;
results.locations.push({
line: index + 1,
context: line.trim(),
type: 'agent_reference',
});
}
});
});
return results;
}
/**
* Search for task references
*/
searchTaskReferences(content, component) {
const results = { count: 0, locations: [] };
const lines = content.split('\n');
// Look for task calls
const taskPatterns = [
new RegExp(`\\*${component.id}`, 'gi'),
new RegExp(`executeTask\\(['"\`]${component.id}['"\`]\\)`, 'gi'),
new RegExp(`task:\\s*['"\`]${component.id}['"\`]`, 'gi'),
];
lines.forEach((line, index) => {
taskPatterns.forEach(pattern => {
const matches = line.match(pattern);
if (matches) {
results.count += matches.length;
results.locations.push({
line: index + 1,
context: line.trim(),
type: 'task_call',
});
}
});
});
return results;
}
/**
* Search for workflow references
*/
searchWorkflowReferences(content, component) {
const results = { count: 0, locations: [] };
const lines = content.split('\n');
// Look for workflow references
const workflowPatterns = [
new RegExp(`workflow:\\s*['"\`]${component.id}['"\`]`, 'gi'),
new RegExp(`${component.id}\\.yaml`, 'gi'),
new RegExp(`${component.name}`, 'gi'),
];
lines.forEach((line, index) => {
workflowPatterns.forEach(pattern => {
const matches = line.match(pattern);
if (matches) {
results.count += matches.length;
results.locations.push({
line: index + 1,
context: line.trim(),
type: 'workflow_reference',
});
}
});
});
return results;
}
/**
* Search for utility references
*/
searchUtilityReferences(content, component) {
const results = { count: 0, locations: [] };
const lines = content.split('\n');
// Look for utility imports and calls
const utilPatterns = [
new RegExp(`require\\(['"\`][^'"\`]*${component.id}['"\`]\\)`, 'gi'),
new RegExp(`from ['"\`][^'"\`]*${component.id}['"\`]`, 'gi'),
new RegExp(`import.*${component.id}`, 'gi'),
];
lines.forEach((line, index) => {
utilPatterns.forEach(pattern => {
const matches = line.match(pattern);
if (matches) {
results.count += matches.length;
results.locations.push({
line: index + 1,
context: line.trim(),
type: 'utility_import',
});
}
});
});
return results;
}
/**
* Search for generic references
*/
searchGenericReferences(content, component) {
const results = { count: 0, locations: [] };
const lines = content.split('\n');
// Look for generic mentions
const namePattern = new RegExp(component.name || component.id, 'gi');
lines.forEach((line, index) => {
const matches = line.match(namePattern);
if (matches) {
results.count += matches.length;
results.locations.push({
line: index + 1,
context: line.trim(),
type: 'generic_mention',
});
}
});
return results;
}
/**
* Find indirect references through dependency chains
*/
async findIndirectReferences(component) {
const indirectRefs = { count: 0, files: [] };
// This would analyze dependency chains to find indirect usage
// For now, return basic implementation
return indirectRefs;
}
/**
* Analyze usage contexts for a component
*/
async analyzeUsageContexts(component, referencingFiles) {
const contexts = [];
for (const file of referencingFiles.slice(0, 10)) { // Limit analysis
try {
const fullPath = path.join(this.rootPath, file);
const content = await fs.readFile(fullPath, 'utf-8');
const context = this.extractUsageContext(content, component, file);
if (context) {
contexts.push(context);
}
} catch (error) {
// File not accessible, skip
}
}
return contexts;
}
/**
* Extract usage context from file content
*/
extractUsageContext(content, component, filePath) {
return {
file: filePath,
context_type: this.determineContextType(filePath),
usage_pattern: this.identifyUsagePattern(content, component),
complexity: this.calculateUsageComplexity(content, component),
};
}
/**
* Calculate popularity score for a component
*/
calculatePopularityScore(usage) {
const directWeight = 1.0;
const indirectWeight = 0.3;
const fileWeight = 0.1;
return Math.round(
(usage.direct_references * directWeight +
usage.indirect_references * indirectWeight +
usage.referencing_files.length * fileWeight) * 10,
) / 10;
}
/**
* Find last usage date for a component
*/
async findLastUsageDate(component) {
// This would analyze git history or file modification dates
// For now, return null
return null;
}
/**
* Calculate usage frequency
*/
calculateUsageFrequency(usage) {
if (usage.direct_references === 0) return 'never';
if (usage.direct_references < 3) return 'rare';
if (usage.direct_references < 10) return 'occasional';
if (usage.direct_references < 25) return 'frequent';
return 'very_frequent';
}
/**
* Calculate dependency depth
*/
async calculateDependencyDepth(component) {
// This would analyze how deep in the dependency tree the component is
return 0;
}
/**
* Find integration points
*/
async findIntegrationPoints(component) {
// This would identify key integration points
return [];
}
/**
* Identify usage hotspots
*/
identifyHotspots(componentUsage) {
return Object.entries(componentUsage)
.filter(([_, usage]) => usage.popularity_score > 15)
.map(([id, usage]) => ({
component_id: id,
popularity_score: usage.popularity_score,
direct_references: usage.direct_references,
referencing_files: usage.referencing_files.length,
}))
.sort((a, b) => b.popularity_score - a.popularity_score);
}
/**
* Find unused components
*/
findUnusedComponents(componentUsage) {
return Object.entries(componentUsage)
.filter(([_, usage]) => usage.direct_references === 0 && usage.indirect_references === 0)
.map(([id, usage]) => ({
component_id: id,
type: usage.type,
last_modified: usage.last_used,
}));
}
/**
* Find popular components
*/
findPopularComponents(componentUsage) {
return Object.entries(componentUsage)
.filter(([_, usage]) => usage.popularity_score > 10)
.map(([id, usage]) => ({
component_id: id,
type: usage.type,
popularity_score: usage.popularity_score,
usage_frequency: usage.usage_frequency,
}))
.sort((a, b) => b.popularity_score - a.popularity_score)
.slice(0, 10);
}
/**
* Analyze cross-references between components
*/
async analyzeCrossReferences(components) {
const crossRefs = {};
// This would analyze how components reference each other
// For now, return basic structure
return crossRefs;
}
/**
* Calculate overall efficiency score
*/
calculateEfficiencyScore(usage) {
const totalComponents = Object.keys(usage.component_usage).length;
const usedComponents = totalComponents - usage.unused_components.length;
const utilizationRate = usedComponents / totalComponents;
return Math.round(utilizationRate * 100);
}
/**
* Generate usage recommendations
*/
generateUsageRecommendations(usage) {
const recommendations = [];
// Unused components
if (usage.unused_components.length > 0) {
recommendations.push({
type: 'cleanup',
priority: 'medium',
message: `Consider reviewing ${usage.unused_components.length} unused components for potential removal`,
components: usage.unused_components.map(c => c.component_id),
});
}
// Hotspots
if (usage.hotspots.length > 0) {
const topHotspot = usage.hotspots[0];
if (topHotspot.popularity_score > 50) {
recommendations.push({
type: 'optimization',
priority: 'high',
message: `Component '${topHotspot.component_id}' is heavily used - consider optimization or caching`,
component: topHotspot.component_id,
});
}
}
// Low efficiency
if (usage.efficiency_score < 70) {
recommendations.push({
type: 'architecture',
priority: 'medium',
message: `Framework efficiency is ${usage.efficiency_score}% - consider component consolidation`,
score: usage.efficiency_score,
});
}
return recommendations;
}
// Helper methods
isExcludedDirectory(name) {
const excludes = ['node_modules', '.git', '.aios', 'dist', 'build', 'coverage'];
return excludes.includes(name) || name.startsWith('.');
}
isSearchableFile(name) {
const extensions = ['.js', '.mjs', '.ts', '.md', '.yaml', '.yml', '.json'];
return extensions.some(ext => name.endsWith(ext));
}
determineContextType(filePath) {
if (filePath.includes('/tests/')) return 'test';
if (filePath.includes('/docs/')) return 'documentation';
if (filePath.includes('/utils/')) return 'utility';
if (filePath.includes('/tasks/')) return 'task';
if (filePath.includes('/agents/')) return 'agent';
return 'general';
}
identifyUsagePattern(content, component) {
// Analyze how the component is used
if (content.includes('require(')) return 'import';
if (content.includes('*' + component.id)) return 'task_call';
if (content.includes('@agent')) return 'agent_reference';
return 'mention';
}
calculateUsageComplexity(content, component) {
// Simple complexity based on usage patterns
const lines = content.split('\n').length;
const mentions = (content.match(new RegExp(component.id, 'g')) || []).length;
return Math.min(5, Math.round(mentions / lines * 100));
}
}
module.exports = UsageAnalytics;