#!/usr/bin/env node
/**
* Designer Sub-Agent
*
* Takes user requirements and recommends appropriate holepunchto repositories.
*
* Usage:
* node designer-agent.js "I want to build a distributed database"
* node designer-agent.js --use-case distributed-database
* node designer-agent.js --interactive
*/
const fs = require('fs');
// Load semantic mappings
const semanticCategories = JSON.parse(fs.readFileSync('semantic-categories.json', 'utf8'));
const useCaseMap = JSON.parse(fs.readFileSync('use-case-map.json', 'utf8'));
const stabilityIndex = JSON.parse(fs.readFileSync('stability-index.json', 'utf8'));
const platformSupport = JSON.parse(fs.readFileSync('platform-support.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;
}
/**
* Match user query to use cases
*/
function matchUseCases(query) {
const q = query.toLowerCase();
const matches = [];
for (const [useCaseName, useCase] of Object.entries(useCaseMap.useCases)) {
let score = 0;
// Check if query mentions use case name
if (q.includes(useCaseName.replace(/-/g, ' '))) {
score += 10;
}
// Check description keywords
const desc = useCase.description.toLowerCase();
const queryWords = q.split(/\s+/);
for (const word of queryWords) {
if (word.length > 3 && desc.includes(word)) {
score += 2;
}
}
// Check pattern keywords
for (const pattern of useCase.patterns) {
if (q.includes(pattern.replace(/-/g, ' '))) {
score += 3;
}
}
if (score > 0) {
matches.push({ useCaseName, useCase, score });
}
}
return matches.sort((a, b) => b.score - a.score);
}
/**
* Recommend repos for a use case
*/
function recommendRepos(useCaseName) {
const useCase = useCaseMap.useCases[useCaseName];
if (!useCase) {
return null;
}
const recommendations = {
useCase: useCaseName,
description: useCase.description,
required: [],
optional: [],
patterns: useCase.patterns
};
// Analyze required repos
for (const repoName of useCase.requiredRepos) {
const repo = analyzeRepo(repoName);
if (repo) {
recommendations.required.push(repo);
}
}
// Analyze optional repos
for (const repoName of useCase.optionalRepos) {
const repo = analyzeRepo(repoName);
if (repo) {
recommendations.optional.push(repo);
}
}
return recommendations;
}
/**
* Analyze a single repo
*/
function analyzeRepo(repoName) {
const info = repoMap[repoName];
if (!info) return null;
const category = semanticCategories[repoName];
const stability = stabilityIndex[repoName];
const platform = platformSupport[repoName];
return {
name: repoName,
description: info.description,
url: info.url,
category: category ? category.category : 'Unknown',
stability: stability ? stability.stability : 'unknown',
maturity: stability ? stability.maturity : 'unknown',
recommendedFor: stability ? stability.recommendedFor : 'unknown',
stars: stability ? stability.stars : 0,
platforms: platform ? Object.entries(platform.platforms)
.filter(([_, supported]) => supported)
.map(([name]) => name) : [],
confidence: category ? category.confidence : 'low'
};
}
/**
* Format recommendations as text
*/
function formatRecommendations(recs) {
let output = '';
output += '═══════════════════════════════════════════════════════\n';
output += 'DESIGNER AGENT RECOMMENDATIONS\n';
output += '═══════════════════════════════════════════════════════\n\n';
output += `Use Case: ${recs.useCase}\n`;
output += `Description: ${recs.description}\n`;
output += `Patterns: ${recs.patterns.join(', ')}\n\n`;
output += '───────────────────────────────────────────────────────\n';
output += 'REQUIRED REPOSITORIES\n';
output += '───────────────────────────────────────────────────────\n\n';
for (const repo of recs.required) {
output += `📦 ${repo.name}\n`;
output += ` ${repo.description}\n`;
output += ` Category: ${repo.category}\n`;
output += ` Stability: ${repo.stability} (${repo.maturity})\n`;
output += ` Recommended for: ${repo.recommendedFor}\n`;
output += ` Stars: ${repo.stars}\n`;
output += ` Platforms: ${repo.platforms.join(', ') || 'unknown'}\n`;
output += ` URL: ${repo.url}\n\n`;
}
if (recs.optional.length > 0) {
output += '───────────────────────────────────────────────────────\n';
output += 'OPTIONAL REPOSITORIES (Enhance functionality)\n';
output += '───────────────────────────────────────────────────────\n\n';
for (const repo of recs.optional) {
output += `📦 ${repo.name}\n`;
output += ` ${repo.description}\n`;
output += ` Category: ${repo.category}\n`;
output += ` Stability: ${repo.stability} (${repo.maturity})\n`;
output += ` Stars: ${repo.stars}\n`;
output += ` Platforms: ${repo.platforms.join(', ') || 'unknown'}\n\n`;
}
}
output += '───────────────────────────────────────────────────────\n';
output += 'NEXT STEPS\n';
output += '───────────────────────────────────────────────────────\n\n';
output += '1. Install required repositories\n';
output += '2. Review API documentation in skills/{repo-name}/skills.md\n';
output += '3. Use Architect Agent for system design:\n';
output += ` node architect-agent.js ${recs.required.map(r => r.name).join(' ')}\n\n`;
return output;
}
/**
* Main function
*/
function main() {
const args = process.argv.slice(2);
// Handle different invocation modes
if (args.length === 0 || args[0] === '--help') {
console.log('Designer Sub-Agent - Recommend repos for P2P use cases\n');
console.log('Usage:');
console.log(' node designer-agent.js "your requirements"');
console.log(' node designer-agent.js --use-case <use-case-name>');
console.log(' node designer-agent.js --list-use-cases\n');
console.log('Examples:');
console.log(' node designer-agent.js "I want to build a distributed database"');
console.log(' node designer-agent.js --use-case p2p-file-sharing');
process.exit(0);
}
// List all use cases
if (args[0] === '--list-use-cases') {
console.log('Available Use Cases:\n');
for (const [name, useCase] of Object.entries(useCaseMap.useCases)) {
console.log(` ${name}`);
console.log(` ${useCase.description}\n`);
}
process.exit(0);
}
// Direct use case lookup
if (args[0] === '--use-case') {
const useCaseName = args[1];
if (!useCaseName) {
console.error('Error: Please specify a use case name');
process.exit(1);
}
const recs = recommendRepos(useCaseName);
if (!recs) {
console.error(`Error: Use case "${useCaseName}" not found`);
console.error('Run with --list-use-cases to see available use cases');
process.exit(1);
}
console.log(formatRecommendations(recs));
process.exit(0);
}
// Natural language query
const query = args.join(' ');
console.log(`Analyzing query: "${query}"\n`);
const matches = matchUseCases(query);
if (matches.length === 0) {
console.log('No matching use cases found.');
console.log('Run with --list-use-cases to see available use cases');
process.exit(0);
}
console.log(`Found ${matches.length} matching use case(s):\n`);
// Show top match
const topMatch = matches[0];
console.log(`Best match: ${topMatch.useCaseName} (score: ${topMatch.score})\n`);
const recs = recommendRepos(topMatch.useCaseName);
console.log(formatRecommendations(recs));
// Show other matches if any
if (matches.length > 1) {
console.log('Other possible matches:');
for (let i = 1; i < Math.min(matches.length, 3); i++) {
console.log(` • ${matches[i].useCaseName} (score: ${matches[i].score})`);
console.log(` ${matches[i].useCase.description}`);
}
console.log('\nRun with --use-case <name> to see recommendations for other matches\n');
}
}
// Run if called directly
if (require.main === module) {
main();
}
module.exports = { matchUseCases, recommendRepos, analyzeRepo };