#!/usr/bin/env node
/**
* Architect Sub-Agent
*
* Takes a list of repositories and designs system architecture,
* identifying patterns, dependencies, and integration points.
*
* Usage:
* node architect-agent.js hypercore hyperbee hyperswarm
* node architect-agent.js --use-case distributed-database
*/
const fs = require('fs');
// Load semantic mappings
const patternMap = JSON.parse(fs.readFileSync('pattern-map.json', 'utf8'));
const dependencyGraph = JSON.parse(fs.readFileSync('dependency-graph.json', 'utf8'));
const useCaseMap = JSON.parse(fs.readFileSync('use-case-map.json', 'utf8'));
const stabilityIndex = JSON.parse(fs.readFileSync('stability-index.json', 'utf8'));
const repoMapData = JSON.parse(fs.readFileSync('repo_map.json', 'utf8'));
// Support both old array format and new object format with _meta
const repoMapArray = repoMapData.repos || repoMapData;
// Convert repo map to object
const repoMap = {};
for (const repo of repoMapArray) {
repoMap[repo.name] = repo;
}
// Load API extracts for deeply analyzed repos
function loadAPIExtract(repoName) {
const path = `skills/${repoName}/api-extract.json`;
if (fs.existsSync(path)) {
try {
return JSON.parse(fs.readFileSync(path, 'utf8'));
} catch (e) {
return null;
}
}
return null;
}
/**
* Analyze architecture for a set of repos
*/
function analyzeArchitecture(repoNames) {
const architecture = {
repos: [],
patterns: [],
dependencies: {},
layers: {},
integrationPoints: [],
recommendations: []
};
// Analyze each repo
for (const repoName of repoNames) {
const repoInfo = analyzeRepoForArchitecture(repoName);
if (repoInfo) {
architecture.repos.push(repoInfo);
}
}
// Identify patterns
architecture.patterns = identifyPatterns(repoNames);
// Analyze dependencies
architecture.dependencies = analyzeDependencies(repoNames);
// Organize into layers
architecture.layers = organizeLayers(repoNames, architecture.dependencies);
// Identify integration points
architecture.integrationPoints = identifyIntegrationPoints(repoNames);
// Generate recommendations
architecture.recommendations = generateRecommendations(architecture);
return architecture;
}
/**
* Analyze a single repo for architecture
*/
function analyzeRepoForArchitecture(repoName) {
const info = repoMap[repoName];
if (!info) return null;
const patterns = patternMap.repoPatterns[repoName] || { implements: [], uses: [] };
const deps = dependencyGraph[repoName] || { dependencies: [], reverseDependencies: [] };
const api = loadAPIExtract(repoName);
return {
name: repoName,
description: info.description,
patternsImplemented: patterns.implements || [],
patternsUsed: patterns.uses || [],
dependencies: deps.dependencies || [],
dependents: deps.reverseDependencies || [],
hasAPIData: !!api,
apiClasses: api && api.api ? (api.api.classes || []) : [],
apiFunctions: api && api.api ? (api.api.functions || []) : []
};
}
/**
* Identify patterns across selected repos
*/
function identifyPatterns(repoNames) {
const patternsUsed = new Set();
for (const repoName of repoNames) {
const repoPatterns = patternMap.repoPatterns[repoName];
if (repoPatterns) {
for (const pattern of repoPatterns.implements || []) {
patternsUsed.add(pattern);
}
for (const pattern of repoPatterns.uses || []) {
patternsUsed.add(pattern);
}
}
}
return Array.from(patternsUsed).map(patternName => {
const pattern = patternMap.patterns[patternName];
const implementedBy = repoNames.filter(repo =>
patternMap.repoPatterns[repo] &&
(patternMap.repoPatterns[repo].implements || []).includes(patternName)
);
const usedBy = repoNames.filter(repo =>
patternMap.repoPatterns[repo] &&
(patternMap.repoPatterns[repo].uses || []).includes(patternName)
);
return {
name: patternName,
description: pattern ? pattern.description : 'Unknown pattern',
implementedBy,
usedBy
};
});
}
/**
* Analyze dependencies between selected repos
*/
function analyzeDependencies(repoNames) {
const deps = {};
const repoSet = new Set(repoNames);
for (const repoName of repoNames) {
const repoDeps = dependencyGraph[repoName];
if (repoDeps) {
// Only include dependencies that are in our selected repos
const internalDeps = (repoDeps.dependencies || []).filter(dep => repoSet.has(dep));
if (internalDeps.length > 0) {
deps[repoName] = internalDeps;
}
}
}
return deps;
}
/**
* Organize repos into architectural layers
*/
function organizeLayers(repoNames, dependencies) {
const layers = {
foundation: [],
core: [],
integration: [],
application: []
};
for (const repoName of repoNames) {
const deps = dependencies[repoName] || [];
const stability = stabilityIndex[repoName];
// Foundation layer: no dependencies on other selected repos, stable
if (deps.length === 0 && stability && stability.stability === 'stable') {
layers.foundation.push(repoName);
}
// Core layer: few dependencies, implements key patterns
else if (deps.length <= 2) {
const patterns = patternMap.repoPatterns[repoName];
if (patterns && patterns.implements && patterns.implements.length > 0) {
layers.core.push(repoName);
} else {
layers.integration.push(repoName);
}
}
// Integration/Application layer: many dependencies
else {
layers.application.push(repoName);
}
}
// Move any remaining repos to integration
for (const repoName of repoNames) {
const inAnyLayer = Object.values(layers).some(layer => layer.includes(repoName));
if (!inAnyLayer) {
layers.integration.push(repoName);
}
}
return layers;
}
/**
* Identify integration points between repos
*/
function identifyIntegrationPoints(repoNames) {
const points = [];
const repoSet = new Set(repoNames);
for (const repoName of repoNames) {
const deps = dependencyGraph[repoName];
if (deps && deps.dependencies) {
for (const dep of deps.dependencies) {
if (repoSet.has(dep)) {
points.push({
from: repoName,
to: dep,
type: 'dependency'
});
}
}
}
// Check for pattern-based integration
const patterns = patternMap.repoPatterns[repoName];
if (patterns && patterns.uses) {
for (const pattern of patterns.uses) {
const patternDef = patternMap.patterns[pattern];
if (patternDef && patternDef.implementedBy) {
for (const impl of patternDef.implementedBy) {
if (repoSet.has(impl) && impl !== repoName) {
points.push({
from: repoName,
to: impl,
type: 'pattern',
pattern: pattern
});
}
}
}
}
}
}
return points;
}
/**
* Generate architecture recommendations
*/
function generateRecommendations(architecture) {
const recommendations = [];
// Check for missing core dependencies
for (const repo of architecture.repos) {
for (const dep of repo.dependencies) {
if (!architecture.repos.find(r => r.name === dep)) {
recommendations.push({
type: 'missing-dependency',
severity: 'warning',
message: `Consider adding "${dep}" - required by ${repo.name}`
});
}
}
}
// Check for pattern coverage
const hasReplication = architecture.patterns.some(p => p.name === 'replication');
const hasPeerDiscovery = architecture.patterns.some(p => p.name === 'peer-discovery');
if (!hasReplication && architecture.repos.length > 1) {
recommendations.push({
type: 'pattern-suggestion',
severity: 'info',
message: 'Consider adding replication pattern for data sync between peers'
});
}
if (!hasPeerDiscovery) {
recommendations.push({
type: 'pattern-suggestion',
severity: 'info',
message: 'Consider adding peer-discovery (hyperswarm/hyperdht) for P2P networking'
});
}
// Check layer distribution
if (architecture.layers.foundation.length === 0) {
recommendations.push({
type: 'architecture',
severity: 'warning',
message: 'No foundation layer repos - consider adding stable core primitives'
});
}
return recommendations;
}
/**
* Format architecture as text
*/
function formatArchitecture(arch) {
let output = '';
output += '═══════════════════════════════════════════════════════\n';
output += 'ARCHITECT AGENT - SYSTEM ARCHITECTURE\n';
output += '═══════════════════════════════════════════════════════\n\n';
output += `Analyzing ${arch.repos.length} repositories\n\n`;
// Architectural layers
output += '───────────────────────────────────────────────────────\n';
output += 'ARCHITECTURAL LAYERS\n';
output += '───────────────────────────────────────────────────────\n\n';
output += '┌─ Foundation Layer (Stable, no internal deps)\n';
for (const repo of arch.layers.foundation) {
output += `│ • ${repo}\n`;
}
if (arch.layers.foundation.length === 0) output += '│ (none)\n';
output += '├─ Core Layer (Key patterns, few deps)\n';
for (const repo of arch.layers.core) {
output += `│ • ${repo}\n`;
}
if (arch.layers.core.length === 0) output += '│ (none)\n';
output += '├─ Integration Layer\n';
for (const repo of arch.layers.integration) {
output += `│ • ${repo}\n`;
}
if (arch.layers.integration.length === 0) output += '│ (none)\n';
output += '└─ Application Layer (High-level, many deps)\n';
for (const repo of arch.layers.application) {
output += ` • ${repo}\n`;
}
if (arch.layers.application.length === 0) output += ' (none)\n';
// Patterns
output += '\n───────────────────────────────────────────────────────\n';
output += 'P2P PATTERNS IN USE\n';
output += '───────────────────────────────────────────────────────\n\n';
for (const pattern of arch.patterns) {
output += `📐 ${pattern.name}\n`;
output += ` ${pattern.description}\n`;
if (pattern.implementedBy.length > 0) {
output += ` Implemented by: ${pattern.implementedBy.join(', ')}\n`;
}
if (pattern.usedBy.length > 0) {
output += ` Used by: ${pattern.usedBy.join(', ')}\n`;
}
output += '\n';
}
// Dependencies
if (Object.keys(arch.dependencies).length > 0) {
output += '───────────────────────────────────────────────────────\n';
output += 'DEPENDENCY GRAPH\n';
output += '───────────────────────────────────────────────────────\n\n';
for (const [repo, deps] of Object.entries(arch.dependencies)) {
output += `${repo}\n`;
for (const dep of deps) {
output += ` └─> ${dep}\n`;
}
}
output += '\n';
}
// Integration points
if (arch.integrationPoints.length > 0) {
output += '───────────────────────────────────────────────────────\n';
output += 'INTEGRATION POINTS\n';
output += '───────────────────────────────────────────────────────\n\n';
for (const point of arch.integrationPoints.slice(0, 10)) {
if (point.type === 'dependency') {
output += `🔗 ${point.from} → ${point.to} (direct dependency)\n`;
} else if (point.type === 'pattern') {
output += `🔗 ${point.from} → ${point.to} (via ${point.pattern} pattern)\n`;
}
}
if (arch.integrationPoints.length > 10) {
output += `\n... and ${arch.integrationPoints.length - 10} more\n`;
}
output += '\n';
}
// Recommendations
if (arch.recommendations.length > 0) {
output += '───────────────────────────────────────────────────────\n';
output += 'RECOMMENDATIONS\n';
output += '───────────────────────────────────────────────────────\n\n';
for (const rec of arch.recommendations) {
const icon = rec.severity === 'warning' ? '⚠️ ' : '💡';
output += `${icon} ${rec.message}\n`;
}
output += '\n';
}
// Repository details
output += '───────────────────────────────────────────────────────\n';
output += 'REPOSITORY DETAILS\n';
output += '───────────────────────────────────────────────────────\n\n';
for (const repo of arch.repos) {
output += `📦 ${repo.name}\n`;
output += ` ${repo.description}\n`;
if (repo.patternsImplemented.length > 0) {
output += ` Implements: ${repo.patternsImplemented.join(', ')}\n`;
}
if (repo.patternsUsed.length > 0) {
output += ` Uses: ${repo.patternsUsed.join(', ')}\n`;
}
if (repo.hasAPIData) {
output += ` API: ${repo.apiClasses.length} classes, ${repo.apiFunctions.length} functions\n`;
output += ` Documentation: skills/${repo.name}/skills.md\n`;
}
output += '\n';
}
return output;
}
/**
* Main function
*/
function main() {
const args = process.argv.slice(2);
if (args.length === 0 || args[0] === '--help') {
console.log('Architect Sub-Agent - Design system architecture\n');
console.log('Usage:');
console.log(' node architect-agent.js <repo1> <repo2> ...');
console.log(' node architect-agent.js --use-case <use-case-name>\n');
console.log('Examples:');
console.log(' node architect-agent.js hypercore hyperbee hyperswarm');
console.log(' node architect-agent.js --use-case distributed-database');
process.exit(0);
}
let repoNames = args;
// Handle --use-case option
if (args[0] === '--use-case') {
const useCaseName = args[1];
if (!useCaseName) {
console.error('Error: Please specify a use case name');
process.exit(1);
}
const useCase = useCaseMap.useCases[useCaseName];
if (!useCase) {
console.error(`Error: Use case "${useCaseName}" not found`);
process.exit(1);
}
repoNames = [...useCase.requiredRepos, ...useCase.optionalRepos];
console.log(`Analyzing architecture for use case: ${useCaseName}\n`);
}
const architecture = analyzeArchitecture(repoNames);
console.log(formatArchitecture(architecture));
}
// Run if called directly
if (require.main === module) {
main();
}
module.exports = { analyzeArchitecture };