Skip to main content
Glama

mcp-github-project-manager

PRDGenerationService.ts12.8 kB
import { AITaskProcessor } from './ai/AITaskProcessor'; import { PRDDocument, FeatureRequirement, UserPersona, ProjectScope, TechnicalRequirement, PRDDocumentSchema } from '../domain/ai-types'; import { v4 as uuidv4 } from 'uuid'; import { z } from 'zod'; /** * Service for generating and managing Product Requirements Documents (PRDs) */ export class PRDGenerationService { private aiProcessor: AITaskProcessor; constructor() { this.aiProcessor = new AITaskProcessor(); } /** * Generate a comprehensive PRD from a project idea */ async generatePRDFromIdea(params: { projectIdea: string; projectName: string; targetUsers?: string[]; timeline?: string; complexity?: 'low' | 'medium' | 'high'; author: string; stakeholders?: string[]; }): Promise<PRDDocument> { try { // Validate input if (!params.projectIdea.trim()) { throw new Error('Project idea is required'); } if (!params.projectName.trim()) { throw new Error('Project name is required'); } // Generate PRD using AI const generatedPRD = await this.aiProcessor.generatePRDFromIdea({ projectIdea: params.projectIdea, targetUsers: params.targetUsers?.join(', '), timeline: params.timeline, complexity: params.complexity }); // Enhance with provided metadata const enhancedPRD: PRDDocument = { ...generatedPRD, title: params.projectName, author: params.author, stakeholders: params.stakeholders || [], version: '1.0.0' }; // Validate the generated PRD const validatedPRD = PRDDocumentSchema.parse(enhancedPRD); return validatedPRD; } catch (error) { process.stderr.write(`Error generating PRD from idea: ${error instanceof Error ? error.message : String(error)}\n`); throw new Error(`Failed to generate PRD: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Enhance an existing PRD with AI assistance */ async enhancePRD(params: { currentPRD: PRDDocument | string; enhancementType: 'comprehensive' | 'technical' | 'user_focused' | 'business_focused'; focusAreas?: string[]; includeResearch?: boolean; }): Promise<PRDDocument> { try { const currentPRDContent = typeof params.currentPRD === 'string' ? params.currentPRD : JSON.stringify(params.currentPRD, null, 2); const enhancedPRD = await this.aiProcessor.enhancePRD({ currentPRD: currentPRDContent, enhancementType: params.enhancementType, focusAreas: params.focusAreas }); // If we started with a PRD object, preserve some original metadata if (typeof params.currentPRD === 'object') { enhancedPRD.id = params.currentPRD.id; enhancedPRD.createdAt = params.currentPRD.createdAt; enhancedPRD.author = params.currentPRD.author; enhancedPRD.version = this.incrementVersion(params.currentPRD.version); } return PRDDocumentSchema.parse(enhancedPRD); } catch (error) { process.stderr.write(`Error enhancing PRD: ${error instanceof Error ? error.message : String(error)}\n`); throw new Error(`Failed to enhance PRD: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Extract and analyze features from a PRD */ async extractFeaturesFromPRD(prd: PRDDocument | string): Promise<FeatureRequirement[]> { try { const prdContent = typeof prd === 'string' ? prd : JSON.stringify(prd, null, 2); const features = await this.aiProcessor.extractFeaturesFromPRD(prdContent); // Validate and enhance features return features.map(feature => ({ ...feature, id: feature.id || uuidv4() })); } catch (error) { process.stderr.write(`Error extracting features from PRD: ${error instanceof Error ? error.message : String(error)}\n`); throw new Error(`Failed to extract features: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Validate PRD completeness and quality */ async validatePRDCompleteness(prd: PRDDocument): Promise<{ isComplete: boolean; score: number; // 0-100 missingElements: string[]; recommendations: string[]; qualityIssues: string[]; }> { try { // Basic validation using schema const validationResult = PRDDocumentSchema.safeParse(prd); if (!validationResult.success) { return { isComplete: false, score: 0, missingElements: validationResult.error.errors.map(e => e.message), recommendations: ['Fix schema validation errors'], qualityIssues: ['PRD does not match required structure'] }; } // Content-based validation const contentScore = this.calculateContentScore(prd); const missingElements = this.identifyMissingElements(prd); const recommendations = this.generateRecommendations(prd, missingElements); return { isComplete: contentScore >= 80 && missingElements.length === 0, score: contentScore, missingElements, recommendations, qualityIssues: this.identifyQualityIssues(prd) }; } catch (error) { process.stderr.write(`Error validating PRD completeness: ${error instanceof Error ? error.message : String(error)}\n`); throw new Error(`Failed to validate PRD: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Generate user stories from PRD features */ async generateUserStoriesFromFeatures(features: FeatureRequirement[]): Promise<{ [featureId: string]: { userStories: string[]; acceptanceCriteria: string[]; } }> { try { const userStoriesMap: { [featureId: string]: { userStories: string[]; acceptanceCriteria: string[] } } = {}; for (const feature of features) { // For now, use the existing user stories and acceptance criteria // In a full implementation, you'd call AI to generate more comprehensive stories userStoriesMap[feature.id] = { userStories: feature.userStories, acceptanceCriteria: feature.acceptanceCriteria }; } return userStoriesMap; } catch (error) { process.stderr.write(`Error generating user stories: ${error instanceof Error ? error.message : String(error)}\n`); throw new Error(`Failed to generate user stories: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Create a PRD template for a specific industry or project type */ createPRDTemplate(type: 'web_app' | 'mobile_app' | 'api' | 'saas' | 'ecommerce'): Partial<PRDDocument> { const baseTemplate = { id: uuidv4(), version: '1.0.0', aiGenerated: false, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), tags: [type], stakeholders: [], milestones: [], successMetrics: [] }; switch (type) { case 'web_app': return { ...baseTemplate, title: 'Web Application PRD Template', overview: 'Template for web application development projects', objectives: [ 'Create responsive web application', 'Ensure cross-browser compatibility', 'Implement user authentication', 'Provide intuitive user experience' ], technicalRequirements: [ { id: uuidv4(), category: 'performance', requirement: 'Page load time under 3 seconds', rationale: 'User experience and SEO requirements', priority: 'high' as any }, { id: uuidv4(), category: 'security', requirement: 'HTTPS encryption for all communications', rationale: 'Data security and privacy compliance', priority: 'critical' as any } ] }; case 'mobile_app': return { ...baseTemplate, title: 'Mobile Application PRD Template', overview: 'Template for mobile application development projects', objectives: [ 'Create native or cross-platform mobile app', 'Ensure optimal performance on mobile devices', 'Implement offline functionality', 'Provide seamless user experience' ] }; case 'api': return { ...baseTemplate, title: 'API Development PRD Template', overview: 'Template for API development projects', objectives: [ 'Create RESTful API endpoints', 'Ensure proper authentication and authorization', 'Implement comprehensive error handling', 'Provide clear API documentation' ] }; default: return baseTemplate; } } /** * Calculate content completeness score */ private calculateContentScore(prd: PRDDocument): number { let score = 0; const maxScore = 100; // Overview and objectives (20 points) if (prd.overview && prd.overview.length > 50) score += 10; if (prd.objectives && prd.objectives.length > 0) score += 10; // User analysis (20 points) if (prd.targetUsers && prd.targetUsers.length > 0) score += 10; if (prd.userJourney && prd.userJourney.length > 50) score += 10; // Features (30 points) if (prd.features && prd.features.length > 0) score += 15; if (prd.features && prd.features.some(f => f.userStories.length > 0)) score += 15; // Technical requirements (15 points) if (prd.technicalRequirements && prd.technicalRequirements.length > 0) score += 15; // Project planning (15 points) if (prd.timeline && prd.timeline.length > 0) score += 5; if (prd.milestones && prd.milestones.length > 0) score += 5; if (prd.successMetrics && prd.successMetrics.length > 0) score += 5; return Math.min(score, maxScore); } /** * Identify missing elements in PRD */ private identifyMissingElements(prd: PRDDocument): string[] { const missing: string[] = []; if (!prd.overview || prd.overview.length < 50) { missing.push('Detailed project overview'); } if (!prd.targetUsers || prd.targetUsers.length === 0) { missing.push('User personas'); } if (!prd.features || prd.features.length === 0) { missing.push('Feature requirements'); } if (!prd.technicalRequirements || prd.technicalRequirements.length === 0) { missing.push('Technical requirements'); } if (!prd.successMetrics || prd.successMetrics.length === 0) { missing.push('Success metrics and KPIs'); } return missing; } /** * Generate recommendations for PRD improvement */ private generateRecommendations(prd: PRDDocument, missingElements: string[]): string[] { const recommendations: string[] = []; if (missingElements.includes('User personas')) { recommendations.push('Add detailed user personas with goals, pain points, and technical levels'); } if (missingElements.includes('Feature requirements')) { recommendations.push('Define specific features with user stories and acceptance criteria'); } if (prd.features && prd.features.some(f => !f.userStories || f.userStories.length === 0)) { recommendations.push('Add user stories for all features'); } if (missingElements.includes('Success metrics and KPIs')) { recommendations.push('Define measurable success criteria and key performance indicators'); } return recommendations; } /** * Identify quality issues in PRD */ private identifyQualityIssues(prd: PRDDocument): string[] { const issues: string[] = []; // Check for vague or unclear descriptions if (prd.overview && prd.overview.length < 100) { issues.push('Project overview is too brief and may lack important details'); } // Check for missing priorities if (prd.features && prd.features.some(f => !f.priority)) { issues.push('Some features are missing priority levels'); } // Check for missing complexity estimates if (prd.features && prd.features.some(f => !f.estimatedComplexity)) { issues.push('Some features are missing complexity estimates'); } return issues; } /** * Increment version number */ private incrementVersion(currentVersion: string): string { const parts = currentVersion.split('.'); if (parts.length === 3) { const patch = parseInt(parts[2]) + 1; return `${parts[0]}.${parts[1]}.${patch}`; } return currentVersion; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kunwarVivek/mcp-github-project-manager'

If you have feedback or need assistance with the MCP directory API, please join our Discord server