agent-team-composer
Version:
Transform README files into GitHub project plans with AI-powered agent teams
232 lines • 9.26 kB
JavaScript
export class ReadmeParser {
/**
* Parse README content and extract project information
*/
async parse(content) {
return {
title: this.extractTitle(content),
description: this.extractDescription(content),
domain: this.identifyDomain(content),
features: this.extractFeatures(content),
techStack: this.extractTechStack(content),
complexity: this.assessComplexity(content),
userTypes: this.identifyUserTypes(content),
integrations: this.identifyIntegrations(content)
};
}
extractTitle(content) {
const titleMatch = content.match(/^#\s+(.+?)$/m);
return titleMatch ? titleMatch[1].trim() : 'Untitled Project';
}
extractDescription(content) {
const lines = content.split('\n');
let description = '';
let inDescription = false;
for (const line of lines) {
if (line.match(/^#\s+/)) {
if (inDescription)
break;
inDescription = true;
continue;
}
if (line.match(/^##\s+/) && inDescription)
break;
if (inDescription && line.trim()) {
description += line.trim() + ' ';
}
}
return description.trim() || 'No description provided';
}
extractFeatures(content) {
const features = [];
const featurePatterns = [
/## Features?\n((?:[-*] .+?(?:\n|$))+)/i,
/## What (does|can) .+?\n((?:[-*] .+?(?:\n|$))+)/i,
/## Functionality\n((?:[-*] .+?(?:\n|$))+)/i,
/## Key Features?\n((?:[-*] .+?(?:\n|$))+)/i
];
for (const pattern of featurePatterns) {
const match = content.match(pattern);
if (match) {
const featureList = match[match.length - 1];
features.push(...featureList.split('\n')
.filter(line => line.trim().match(/^[-*]/))
.map(line => line.trim().replace(/^[-*]\s+/, '')));
break;
}
}
return features;
}
extractTechStack(content) {
const techStack = [];
const techPatterns = [
/## Tech(?:nology)? Stack\n((?:[-*] .+?(?:\n|$))+)/i,
/## Built With\n((?:[-*] .+?(?:\n|$))+)/i,
/## Technologies\n((?:[-*] .+?(?:\n|$))+)/i
];
for (const pattern of techPatterns) {
const match = content.match(pattern);
if (match) {
techStack.push(...match[1].split('\n')
.filter(line => line.trim().match(/^[-*]/))
.map(line => line.trim().replace(/^[-*]\s+/, '')));
break;
}
}
// Also look for common tech keywords throughout
const techKeywords = [
'React', 'Vue', 'Angular', 'Node.js', 'Express', 'Django', 'Flask',
'PostgreSQL', 'MongoDB', 'MySQL', 'Redis', 'Docker', 'Kubernetes',
'AWS', 'Azure', 'GCP', 'TypeScript', 'JavaScript', 'Python', 'Java',
'Go', 'Rust', 'GraphQL', 'REST API', 'WebSocket', 'gRPC'
];
techKeywords.forEach(tech => {
if (content.includes(tech) && !techStack.includes(tech)) {
techStack.push(tech);
}
});
return techStack;
}
assessComplexity(content) {
let score = 0;
// Feature count
const features = this.extractFeatures(content);
if (features.length > 10)
score += 3;
else if (features.length > 5)
score += 2;
else if (features.length > 0)
score += 1;
// Tech stack diversity
const techStack = this.extractTechStack(content);
if (techStack.length > 8)
score += 3;
else if (techStack.length > 4)
score += 2;
else if (techStack.length > 0)
score += 1;
// Keywords indicating complexity
const complexityKeywords = [
'microservices', 'distributed', 'scalable', 'enterprise',
'real-time', 'machine learning', 'AI', 'blockchain',
'multi-tenant', 'high availability', 'load balancing'
];
complexityKeywords.forEach(keyword => {
if (content.toLowerCase().includes(keyword.toLowerCase())) {
score += 1;
}
});
// Integration count
const integrations = this.identifyIntegrations(content);
if (integrations.length > 5)
score += 2;
else if (integrations.length > 2)
score += 1;
// Determine complexity
if (score >= 8)
return 'complex';
if (score >= 4)
return 'moderate';
return 'simple';
}
identifyDomain(content) {
const domainPatterns = {
'e-commerce': /e-?commerce|shop|store|cart|checkout|payment|product catalog/i,
'social-media': /social|feed|post|comment|like|follow|share|profile/i,
'productivity': /task|todo|project|manage|workflow|collaborate|team/i,
'healthcare': /health|medical|patient|doctor|appointment|prescription/i,
'education': /learn|course|student|teacher|quiz|lesson|curriculum/i,
'finance': /finance|bank|payment|transaction|wallet|budget|invoice/i,
'entertainment': /video|music|stream|media|content|playlist|watch/i,
'communication': /chat|message|call|video conference|email|notification/i,
'analytics': /analytics|dashboard|metric|report|visualization|insight/i,
'devtools': /developer|api|sdk|cli|debug|deploy|monitor|log/i,
'iot': /iot|sensor|device|embedded|arduino|raspberry|mqtt/i,
'gaming': /game|player|score|level|achievement|multiplayer/i
};
const scores = {};
for (const [domain, pattern] of Object.entries(domainPatterns)) {
const matches = content.match(pattern);
if (matches) {
scores[domain] = matches.length;
}
}
// Find domain with highest score
let maxScore = 0;
let detectedDomain = 'general';
for (const [domain, score] of Object.entries(scores)) {
if (score > maxScore) {
maxScore = score;
detectedDomain = domain;
}
}
return detectedDomain;
}
identifyUserTypes(content) {
const userTypes = [];
const userPatterns = {
'developers': /developer|programmer|engineer|coder|api user/i,
'administrators': /admin|administrator|operator|devops/i,
'end-users': /user|customer|client|consumer|visitor/i,
'business-users': /business|manager|executive|stakeholder/i,
'analysts': /analyst|data scientist|researcher/i,
'designers': /designer|ux|ui|creative/i
};
for (const [userType, pattern] of Object.entries(userPatterns)) {
if (pattern.test(content)) {
userTypes.push(userType);
}
}
return userTypes.length > 0 ? userTypes : ['end-users'];
}
identifyIntegrations(content) {
const integrations = [];
const integrationPatterns = [
'GitHub', 'GitLab', 'Bitbucket', 'Slack', 'Discord', 'Teams',
'Jira', 'Trello', 'Asana', 'Stripe', 'PayPal', 'Square',
'AWS', 'Azure', 'Google Cloud', 'Twilio', 'SendGrid', 'Mailgun',
'OAuth', 'SAML', 'LDAP', 'Salesforce', 'HubSpot', 'Zendesk',
'Datadog', 'New Relic', 'Sentry', 'Prometheus', 'Grafana'
];
integrationPatterns.forEach(integration => {
if (content.includes(integration)) {
integrations.push(integration);
}
});
return integrations;
}
}
export async function parseReadme(content) {
const parser = new ReadmeParser();
const parsed = await parser.parse(content);
// Get repository info from git if available
let repository = 'unknown/unknown';
let branch = 'main';
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { execSync } = require('child_process');
const remoteUrl = execSync('git config --get remote.origin.url', { encoding: 'utf8' }).trim();
// Extract owner/repo from various Git URL formats
const match = remoteUrl.match(/(?:git@|https:\/\/)(?:github\.com)[/:](.+?)(?:\.git)?$/);
if (match) {
repository = match[1];
}
branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim();
}
catch (error) {
// Ignore git errors, use defaults
}
return {
title: parsed.title,
description: parsed.description,
domain: parsed.domain,
repository,
branch,
features: parsed.features,
techStack: parsed.techStack,
complexity: parsed.complexity,
userTypes: parsed.userTypes,
integrations: parsed.integrations
};
}
//# sourceMappingURL=readme-parser.js.map