aios-core
Version:
Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework
762 lines (668 loc) • 24.3 kB
JavaScript
const fs = require('fs').promises;
const path = require('path');
const yaml = require('js-yaml');
const chalk = require('chalk');
/**
* Framework structure analyzer for Synkra AIOS
* Discovers and catalogs all framework components
*/
class FrameworkAnalyzer {
constructor(options = {}) {
this.rootPath = options.rootPath || process.cwd();
this.aiosCoreDir = path.join(this.rootPath, 'aios-core');
this.excludes = options.excludes || [
'node_modules',
'.git',
'.aios',
'dist',
'build',
'coverage',
'.next',
'.nuxt',
'tmp',
'temp',
];
}
/**
* Analyze complete framework structure
*/
async analyzeFrameworkStructure(scope = 'full') {
const components = {
agents: [],
tasks: [],
workflows: [],
utils: [],
templates: [],
docs: [],
tests: [],
};
try {
// Analyze each component type based on scope
if (scope === 'full' || scope === 'agents') {
components.agents = await this.discoverAgents();
}
if (scope === 'full' || scope === 'tasks') {
components.tasks = await this.discoverTasks();
}
if (scope === 'full' || scope === 'workflows') {
components.workflows = await this.discoverWorkflows();
}
if (scope === 'full' || scope === 'utils') {
components.utils = await this.discoverUtils();
}
if (scope === 'full') {
components.templates = await this.discoverTemplates();
components.docs = await this.discoverDocs();
components.tests = await this.discoverTests();
}
// Calculate summary
const totalComponents = Object.values(components).reduce((sum, arr) => sum + arr.length, 0);
return {
scope,
timestamp: new Date().toISOString(),
total_components: totalComponents,
components: this.flattenComponents(components),
breakdown: components,
agents: components.agents,
tasks: components.tasks,
workflows: components.workflows,
utils: components.utils,
templates: components.templates,
docs: components.docs,
tests: components.tests,
directory_structure: await this.analyzeDirectoryStructure(),
dependencies: await this.analyzeDependencies(components),
metrics: await this.calculateFrameworkMetrics(components),
};
} catch (error) {
console.error(chalk.red(`Framework analysis failed: ${error.message}`));
throw error;
}
}
/**
* Discover all agents in the framework
*/
async discoverAgents() {
const agents = [];
const agentsDir = path.join(this.aiosCoreDir, 'agents');
try {
await fs.access(agentsDir);
const files = await this.getMarkdownFiles(agentsDir);
for (const file of files) {
try {
const content = await fs.readFile(file, 'utf-8');
const agent = await this.parseAgentFile(file, content);
if (agent) agents.push(agent);
} catch (error) {
console.warn(chalk.yellow(`Warning: Failed to parse agent ${file}: ${error.message}`));
}
}
} catch (error) {
// Agents directory doesn't exist or not accessible
}
return agents;
}
/**
* Discover all tasks in the framework
*/
async discoverTasks() {
const tasks = [];
const tasksDir = path.join(this.aiosCoreDir, 'tasks');
try {
await fs.access(tasksDir);
const files = await this.getMarkdownFiles(tasksDir);
for (const file of files) {
try {
const content = await fs.readFile(file, 'utf-8');
const task = await this.parseTaskFile(file, content);
if (task) tasks.push(task);
} catch (error) {
console.warn(chalk.yellow(`Warning: Failed to parse task ${file}: ${error.message}`));
}
}
} catch (error) {
// Tasks directory doesn't exist or not accessible
}
return tasks;
}
/**
* Discover all workflows in the framework
*/
async discoverWorkflows() {
const workflows = [];
const workflowsDir = path.join(this.aiosCoreDir, 'workflows');
try {
await fs.access(workflowsDir);
const files = await this.getYamlFiles(workflowsDir);
for (const file of files) {
try {
const content = await fs.readFile(file, 'utf-8');
const workflow = await this.parseWorkflowFile(file, content);
if (workflow) workflows.push(workflow);
} catch (error) {
console.warn(chalk.yellow(`Warning: Failed to parse workflow ${file}: ${error.message}`));
}
}
} catch (error) {
// Workflows directory doesn't exist or not accessible
}
return workflows;
}
/**
* Discover all utilities in the framework
*/
async discoverUtils() {
const utils = [];
const utilsDir = path.join(this.aiosCoreDir, 'utils');
try {
await fs.access(utilsDir);
const files = await this.getJavaScriptFiles(utilsDir);
for (const file of files) {
try {
const content = await fs.readFile(file, 'utf-8');
const util = await this.parseUtilFile(file, content);
if (util) utils.push(util);
} catch (error) {
console.warn(chalk.yellow(`Warning: Failed to parse util ${file}: ${error.message}`));
}
}
} catch (error) {
// Utils directory doesn't exist or not accessible
}
return utils;
}
/**
* Discover all templates in the framework
*/
async discoverTemplates() {
const templates = [];
const templatesDir = path.join(this.aiosCoreDir, 'templates');
try {
await fs.access(templatesDir);
const files = await this.getAllFiles(templatesDir);
for (const file of files) {
try {
const template = await this.parseTemplateFile(file);
if (template) templates.push(template);
} catch (error) {
console.warn(chalk.yellow(`Warning: Failed to parse template ${file}: ${error.message}`));
}
}
} catch (error) {
// Templates directory doesn't exist or not accessible
}
return templates;
}
/**
* Discover documentation files
*/
async discoverDocs() {
const docs = [];
const docsDir = path.join(this.rootPath, 'docs');
try {
await fs.access(docsDir);
const files = await this.getMarkdownFiles(docsDir);
for (const file of files) {
try {
const doc = await this.parseDocFile(file);
if (doc) docs.push(doc);
} catch (error) {
console.warn(chalk.yellow(`Warning: Failed to parse doc ${file}: ${error.message}`));
}
}
} catch (error) {
// Docs directory doesn't exist or not accessible
}
return docs;
}
/**
* Discover test files
*/
async discoverTests() {
const tests = [];
const testsDir = path.join(this.rootPath, 'tests');
try {
await fs.access(testsDir);
const files = await this.getJavaScriptFiles(testsDir);
for (const file of files) {
try {
const test = await this.parseTestFile(file);
if (test) tests.push(test);
} catch (error) {
console.warn(chalk.yellow(`Warning: Failed to parse test ${file}: ${error.message}`));
}
}
} catch (error) {
// Tests directory doesn't exist or not accessible
}
return tests;
}
/**
* Parse agent file content
*/
async parseAgentFile(filePath, content) {
try {
// Extract YAML frontmatter
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
let metadata = {};
if (frontmatterMatch) {
metadata = yaml.load(frontmatterMatch[1]) || {};
}
// Extract markdown content
const markdownContent = content.replace(/^---\n[\s\S]*?\n---\n/, '');
return {
type: 'agent',
id: metadata.id || path.basename(filePath, '.md'),
name: metadata.name || path.basename(filePath, '.md'),
description: metadata.description || this.extractDescription(markdownContent),
version: metadata.version || '1.0.0',
file_path: path.relative(this.rootPath, filePath),
size: content.length,
last_modified: (await fs.stat(filePath)).mtime,
metadata,
dependencies: this.extractDependencies(content),
capabilities: metadata.capabilities || [],
complexity: this.calculateComplexity(content),
maintainability: this.calculateMaintainability(content),
};
} catch (error) {
console.warn(`Failed to parse agent ${filePath}: ${error.message}`);
return null;
}
}
/**
* Parse task file content
*/
async parseTaskFile(filePath, content) {
try {
// Tasks use different structure than agents
const nameMatch = content.match(/^# Task: (.+)$/m);
const descMatch = content.match(/## Description\n([\s\S]*?)(?=\n## |$)/);
const typeMatch = content.match(/## Type\n(.+)$/m);
const complexityMatch = content.match(/## Complexity\n(.+)$/m);
return {
type: 'task',
id: path.basename(filePath, '.md'),
name: nameMatch ? nameMatch[1].trim() : path.basename(filePath, '.md'),
description: descMatch ? descMatch[1].trim() : '',
task_type: typeMatch ? typeMatch[1].trim() : 'unknown',
complexity: complexityMatch ? complexityMatch[1].trim().toLowerCase() : 'medium',
file_path: path.relative(this.rootPath, filePath),
size: content.length,
last_modified: (await fs.stat(filePath)).mtime,
dependencies: this.extractDependencies(content),
parameters: this.extractTaskParameters(content),
implementation_status: this.analyzeImplementationStatus(content),
};
} catch (error) {
console.warn(`Failed to parse task ${filePath}: ${error.message}`);
return null;
}
}
/**
* Parse workflow file content
*/
async parseWorkflowFile(filePath, content) {
try {
const workflow = yaml.load(content);
return {
type: 'workflow',
id: workflow.id || path.basename(filePath, '.yaml'),
name: workflow.name || path.basename(filePath, '.yaml'),
description: workflow.description || '',
version: workflow.version || '1.0.0',
file_path: path.relative(this.rootPath, filePath),
size: content.length,
last_modified: (await fs.stat(filePath)).mtime,
steps: workflow.steps || [],
dependencies: workflow.dependencies || [],
triggers: workflow.triggers || [],
complexity: this.calculateWorkflowComplexity(workflow),
validation_status: await this.validateWorkflow(workflow),
};
} catch (error) {
console.warn(`Failed to parse workflow ${filePath}: ${error.message}`);
return null;
}
}
/**
* Parse utility file content
*/
async parseUtilFile(filePath, content) {
try {
const stats = await fs.stat(filePath);
return {
type: 'utility',
id: path.basename(filePath, '.js'),
name: path.basename(filePath),
description: this.extractUtilDescription(content),
file_path: path.relative(this.rootPath, filePath),
size: content.length,
last_modified: stats.mtime,
exports: this.extractExports(content),
functions: this.extractFunctions(content),
dependencies: this.extractImports(content),
complexity: this.calculateCodeComplexity(content),
test_coverage: await this.calculateTestCoverage(filePath),
};
} catch (error) {
console.warn(`Failed to parse util ${filePath}: ${error.message}`);
return null;
}
}
/**
* Parse template file
*/
async parseTemplateFile(filePath) {
try {
const stats = await fs.stat(filePath);
const content = await fs.readFile(filePath, 'utf-8');
return {
type: 'template',
id: path.basename(filePath),
name: path.basename(filePath),
description: this.extractTemplateDescription(content),
file_path: path.relative(this.rootPath, filePath),
size: content.length,
last_modified: stats.mtime,
template_type: this.detectTemplateType(filePath, content),
variables: this.extractTemplateVariables(content),
};
} catch (error) {
console.warn(`Failed to parse template ${filePath}: ${error.message}`);
return null;
}
}
/**
* Parse documentation file
*/
async parseDocFile(filePath) {
try {
const stats = await fs.stat(filePath);
const content = await fs.readFile(filePath, 'utf-8');
return {
type: 'documentation',
id: path.basename(filePath, '.md'),
name: path.basename(filePath),
description: this.extractDescription(content),
file_path: path.relative(this.rootPath, filePath),
size: content.length,
last_modified: stats.mtime,
doc_type: this.detectDocType(filePath),
sections: this.extractSections(content),
word_count: this.countWords(content),
};
} catch (error) {
console.warn(`Failed to parse doc ${filePath}: ${error.message}`);
return null;
}
}
/**
* Parse test file
*/
async parseTestFile(filePath) {
try {
const stats = await fs.stat(filePath);
const content = await fs.readFile(filePath, 'utf-8');
return {
type: 'test',
id: path.basename(filePath, '.js'),
name: path.basename(filePath),
description: this.extractTestDescription(content),
file_path: path.relative(this.rootPath, filePath),
size: content.length,
last_modified: stats.mtime,
test_framework: this.detectTestFramework(content),
test_suites: this.extractTestSuites(content),
test_count: this.countTests(content),
coverage_target: this.extractCoverageTarget(filePath),
};
} catch (error) {
console.warn(`Failed to parse test ${filePath}: ${error.message}`);
return null;
}
}
/**
* Analyze directory structure
*/
async analyzeDirectoryStructure() {
const structure = {
total_directories: 0,
total_files: 0,
depth: 0,
size_distribution: {},
file_types: {},
};
try {
await this.walkDirectory(this.rootPath, structure, 0);
} catch (error) {
console.warn(`Directory analysis failed: ${error.message}`);
}
return structure;
}
/**
* Analyze component dependencies
*/
async analyzeDependencies(components) {
const dependencies = {
internal: new Map(),
external: new Map(),
circular: [],
orphaned: [],
highly_coupled: [],
};
// Analyze dependencies for each component type
const allComponents = this.flattenComponents(components);
for (const component of allComponents) {
if (component.dependencies) {
for (const dep of component.dependencies) {
if (this.isInternalDependency(dep)) {
dependencies.internal.set(dep, (dependencies.internal.get(dep) || 0) + 1);
} else {
dependencies.external.set(dep, (dependencies.external.get(dep) || 0) + 1);
}
}
}
}
// Detect circular dependencies
dependencies.circular = await this.detectCircularDependencies(allComponents);
// Find orphaned components
dependencies.orphaned = this.findOrphanedComponents(allComponents);
// Find highly coupled components
dependencies.highly_coupled = this.findHighlyCoupledComponents(allComponents);
return {
internal_count: dependencies.internal.size,
external_count: dependencies.external.size,
internal_dependencies: Array.from(dependencies.internal.entries()).map(([name, count]) => ({ name, count })),
external_dependencies: Array.from(dependencies.external.entries()).map(([name, count]) => ({ name, count })),
circular_dependencies: dependencies.circular,
orphaned_components: dependencies.orphaned,
highly_coupled_components: dependencies.highly_coupled,
};
}
/**
* Calculate framework-wide metrics
*/
async calculateFrameworkMetrics(components) {
const allComponents = this.flattenComponents(components);
return {
total_size: allComponents.reduce((sum, comp) => sum + (comp.size || 0), 0),
average_complexity: this.calculateAverageComplexity(allComponents),
maintainability_index: this.calculateMaintainabilityIndex(allComponents),
test_coverage: await this.calculateOverallTestCoverage(components),
documentation_coverage: this.calculateDocumentationCoverage(components),
code_quality_score: this.calculateCodeQualityScore(allComponents),
technical_debt: this.calculateTechnicalDebt(allComponents),
};
}
// File discovery helper methods
async getMarkdownFiles(dir) {
return this.getFilesByExtension(dir, '.md');
}
async getYamlFiles(dir) {
return this.getFilesByExtension(dir, ['.yaml', '.yml']);
}
async getJavaScriptFiles(dir) {
return this.getFilesByExtension(dir, ['.js', '.mjs']);
}
async getAllFiles(dir) {
const files = [];
await this.walkDirectory(dir, { files }, 0);
return files;
}
async getFilesByExtension(dir, extensions) {
const files = [];
const extArray = Array.isArray(extensions) ? extensions : [extensions];
try {
const entries = await fs.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory() && !this.isExcluded(entry.name)) {
files.push(...await this.getFilesByExtension(fullPath, extensions));
} else if (entry.isFile()) {
const ext = path.extname(entry.name).toLowerCase();
if (extArray.includes(ext)) {
files.push(fullPath);
}
}
}
} catch (error) {
// Directory not accessible
}
return files;
}
async walkDirectory(dir, result, depth) {
try {
const entries = await fs.readdir(dir, { withFileTypes: true });
result.depth = Math.max(result.depth || 0, depth);
for (const entry of entries) {
if (this.isExcluded(entry.name)) continue;
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
result.total_directories = (result.total_directories || 0) + 1;
await this.walkDirectory(fullPath, result, depth + 1);
} else if (entry.isFile()) {
result.total_files = (result.total_files || 0) + 1;
if (result.files) {
result.files.push(fullPath);
}
// Track file types
const ext = path.extname(entry.name).toLowerCase();
result.file_types = result.file_types || {};
result.file_types[ext] = (result.file_types[ext] || 0) + 1;
// Track size distribution
const stats = await fs.stat(fullPath);
const sizeCategory = this.getSizeCategory(stats.size);
result.size_distribution = result.size_distribution || {};
result.size_distribution[sizeCategory] = (result.size_distribution[sizeCategory] || 0) + 1;
}
}
} catch (error) {
// Directory not accessible
}
}
// Helper methods
isExcluded(name) {
return this.excludes.some(exclude =>
name === exclude ||
name.startsWith(exclude) ||
name.startsWith('.'),
);
}
flattenComponents(components) {
return Object.values(components).flat();
}
extractDescription(content) {
// Extract first paragraph or first meaningful line
const lines = content.split('\n').filter(line => line.trim());
return lines.find(line => line.length > 20 && !line.startsWith('#')) || '';
}
extractDependencies(content) {
const deps = [];
const requireMatches = content.match(/require\(['"`]([^'"`]+)['"`]\)/g) || [];
const importMatches = content.match(/import .* from ['"`]([^'"`]+)['"`]/g) || [];
requireMatches.forEach(match => {
const dep = match.match(/['"`]([^'"`]+)['"`]/)[1];
if (!deps.includes(dep)) deps.push(dep);
});
importMatches.forEach(match => {
const dep = match.match(/from ['"`]([^'"`]+)['"`]/)[1];
if (!deps.includes(dep)) deps.push(dep);
});
return deps;
}
calculateComplexity(content) {
// Simple complexity calculation based on content patterns
const lines = content.split('\n').length;
const functions = (content.match(/function|async|=>/g) || []).length;
const conditions = (content.match(/if|while|for|switch|catch/g) || []).length;
const complexity = Math.min(10, (functions + conditions * 2) / lines * 100);
return Math.max(1, Math.round(complexity));
}
calculateMaintainability(content) {
// Basic maintainability score
const lines = content.split('\n').length;
const comments = (content.match(/\/\*[\s\S]*?\*\/|\/\/.*/g) || []).length;
const commentRatio = comments / lines;
const maintainability = Math.min(10, 5 + commentRatio * 10);
return Math.round(maintainability * 10) / 10;
}
getSizeCategory(size) {
if (size < 1024) return 'tiny';
if (size < 10240) return 'small';
if (size < 102400) return 'medium';
if (size < 1048576) return 'large';
return 'huge';
}
isInternalDependency(dep) {
return dep.startsWith('./') || dep.startsWith('../') || dep.startsWith('/');
}
// Stub methods for more complex analysis
extractTaskParameters(content) { return []; }
analyzeImplementationStatus(content) { return 'unknown'; }
calculateWorkflowComplexity(workflow) { return 1; }
async validateWorkflow(workflow) {
try {
const { WorkflowValidator } = require('../../development/scripts/workflow-validator');
const validator = new WorkflowValidator({ verbose: false });
// validateRequiredFields works on the parsed object directly
const result = validator.validateRequiredFields({ workflow }, 'inline');
if (workflow && workflow.sequence) {
const seqResult = validator.validatePhaseSequence(workflow.sequence);
result.errors.push(...(seqResult.errors || []));
result.warnings.push(...(seqResult.warnings || []));
if (seqResult.errors && seqResult.errors.length > 0) result.valid = false;
}
return result;
} catch (error) {
return { valid: false, errors: [{ code: 'VALIDATOR_LOAD_ERROR', message: error.message }], warnings: [] };
}
}
extractUtilDescription(content) { return ''; }
extractExports(content) { return []; }
extractFunctions(content) { return []; }
extractImports(content) { return []; }
calculateCodeComplexity(content) { return 1; }
calculateTestCoverage(filePath) { return 0; }
extractTemplateDescription(content) { return ''; }
detectTemplateType(filePath, content) { return 'generic'; }
extractTemplateVariables(content) { return []; }
detectDocType(filePath) { return 'general'; }
extractSections(content) { return []; }
countWords(content) { return content.split(/\s+/).length; }
extractTestDescription(content) { return ''; }
detectTestFramework(content) { return 'jest'; }
extractTestSuites(content) { return []; }
countTests(content) { return (content.match(/test\(|it\(/g) || []).length; }
extractCoverageTarget(filePath) { return null; }
detectCircularDependencies(components) { return []; }
findOrphanedComponents(components) { return []; }
findHighlyCoupledComponents(components) { return []; }
calculateAverageComplexity(components) { return 1; }
calculateMaintainabilityIndex(components) { return 80; }
calculateOverallTestCoverage(components) { return 0; }
calculateDocumentationCoverage(components) { return 0; }
calculateCodeQualityScore(components) { return 7; }
calculateTechnicalDebt(components) { return 'low'; }
}
module.exports = FrameworkAnalyzer;