Skip to main content
Glama

documcp

by tosin2013
update-existing-documentation.ts37 kB
import { Tool } from '@modelcontextprotocol/sdk/types.js'; import * as fs from 'fs/promises'; import * as path from 'path'; import { handleMemoryRecall, handleMemoryEnhancedRecommendation, handleMemoryIntelligentAnalysis, } from '../memory/index.js'; interface UpdateOptions { analysisId: string; docsPath: string; compareMode: 'comprehensive' | 'gap-detection' | 'accuracy-check'; updateStrategy: 'conservative' | 'moderate' | 'aggressive'; preserveStyle: boolean; focusAreas?: string[]; } interface DocumentationGap { type: 'missing' | 'outdated' | 'incorrect' | 'incomplete'; location: string; description: string; severity: 'low' | 'medium' | 'high' | 'critical'; suggestedUpdate: string; memoryEvidence?: any[]; } interface CodeDocumentationComparison { codeFeatures: any[]; documentedFeatures: any[]; gaps: DocumentationGap[]; outdatedSections: any[]; accuracyIssues: any[]; } interface UpdateRecommendation { section: string; currentContent: string; suggestedContent: string; reasoning: string; memoryEvidence: any[]; confidence: number; effort: 'low' | 'medium' | 'high'; } interface UpdateResult { success: boolean; analysisPerformed: CodeDocumentationComparison; recommendations: UpdateRecommendation[]; memoryInsights: { similarProjects: any[]; successfulUpdatePatterns: any[]; commonGapTypes: Record<string, number>; }; updateMetrics: { gapsDetected: number; recommendationsGenerated: number; confidenceScore: number; estimatedEffort: string; }; nextSteps: string[]; } class DocumentationUpdateEngine { private memoryInsights: any = null; private codeAnalysis: any = null; private existingDocs: Map<string, any> = new Map(); async updateExistingDocumentation(options: UpdateOptions): Promise<UpdateResult> { // 1. Load repository analysis and memory insights const analysis = await this.getRepositoryAnalysis(options.analysisId); this.codeAnalysis = analysis; // 2. Load memory insights for intelligent comparison await this.loadMemoryInsights(analysis, options); // 3. Analyze existing documentation structure and content const existingDocs = await this.analyzeExistingDocumentation(options.docsPath); this.existingDocs = existingDocs; // 4. Perform comprehensive code-documentation comparison const comparison = await this.performCodeDocumentationComparison( analysis, existingDocs, options, ); // 5. Generate memory-informed update recommendations const recommendations = await this.generateUpdateRecommendations(comparison, options); // 6. Calculate metrics and confidence scores const updateMetrics = this.calculateUpdateMetrics(comparison, recommendations); return { success: true, analysisPerformed: comparison, recommendations, memoryInsights: this.memoryInsights, updateMetrics, nextSteps: this.generateMemoryInformedNextSteps(comparison, recommendations), }; } private async getRepositoryAnalysis(analysisId: string): Promise<any> { // Try to get analysis from memory system first try { const memoryRecall = await handleMemoryRecall({ query: analysisId, type: 'analysis', limit: 1, }); // Handle the memory recall result structure if (memoryRecall && memoryRecall.memories && memoryRecall.memories.length > 0) { const memory = memoryRecall.memories[0]; // Handle wrapped content structure if (memory.data && memory.data.content && Array.isArray(memory.data.content)) { // Extract the JSON from the first text content const firstContent = memory.data.content[0]; if (firstContent && firstContent.type === 'text' && firstContent.text) { try { return JSON.parse(firstContent.text); } catch (parseError) { console.warn('Failed to parse analysis content from memory:', parseError); return memory.data; } } } // Try direct content access (legacy format) if (memory.content) { return memory.content; } // Try data field if (memory.data) { return memory.data; } } } catch (error) { console.warn('Failed to retrieve from memory system:', error); } // Fallback to reading from cached analysis file const analysisPath = path.join('.documcp', 'analyses', `${analysisId}.json`); try { const content = await fs.readFile(analysisPath, 'utf-8'); return JSON.parse(content); } catch { throw new Error( `Repository analysis with ID '${analysisId}' not found. Please run analyze_repository first.`, ); } } private async loadMemoryInsights(analysis: any, options: UpdateOptions): Promise<void> { try { // Get similar projects that had successful documentation updates const similarProjectsQuery = `${analysis.metadata?.primaryLanguage || ''} ${ analysis.metadata?.ecosystem || '' } documentation update`; const similarProjects = await handleMemoryRecall({ query: similarProjectsQuery, type: 'recommendation', limit: 10, }); // Get patterns for successful documentation updates const updatePatternsQuery = 'documentation update successful patterns gaps outdated'; const updatePatterns = await handleMemoryRecall({ query: updatePatternsQuery, type: 'configuration', limit: 5, }); // Get memory-enhanced analysis for this specific update task const enhancedAnalysis = await handleMemoryIntelligentAnalysis({ projectPath: analysis.projectPath || '', baseAnalysis: analysis, }); // Get memory-enhanced recommendations for update strategy const enhancedRecommendations = await handleMemoryEnhancedRecommendation({ projectPath: analysis.projectPath || '', baseRecommendation: { updateStrategy: options.updateStrategy, compareMode: options.compareMode, focusAreas: options.focusAreas || [], }, projectFeatures: { ecosystem: analysis.metadata?.ecosystem || 'unknown', primaryLanguage: analysis.metadata?.primaryLanguage || 'unknown', complexity: analysis.complexity || 'medium', hasTests: analysis.structure?.hasTests || false, hasCI: analysis.structure?.hasCI || false, docStructure: 'existing', // Indicates we're updating existing docs }, }); this.memoryInsights = { similarProjects: similarProjects.memories || [], updatePatterns: updatePatterns.memories || [], enhancedAnalysis: enhancedAnalysis, enhancedRecommendations: enhancedRecommendations, successfulUpdatePatterns: this.extractUpdatePatterns(similarProjects.memories || []), commonGapTypes: this.extractCommonGapTypes(similarProjects.memories || []), }; } catch (error) { console.warn('Failed to load memory insights:', error); this.memoryInsights = { similarProjects: [], updatePatterns: [], enhancedAnalysis: null, enhancedRecommendations: null, successfulUpdatePatterns: [], commonGapTypes: {}, }; } } private extractUpdatePatterns(projects: any[]): any[] { return projects .filter((p) => p.content?.updatePatterns || p.content?.documentationUpdates) .map((p) => p.content?.updatePatterns || p.content?.documentationUpdates) .flat() .filter(Boolean); } private extractCommonGapTypes(projects: any[]): Record<string, number> { const gapTypes: Record<string, number> = {}; projects.forEach((p) => { const gaps = p.content?.documentationGaps || []; gaps.forEach((gap: any) => { const type = gap.type || 'unknown'; gapTypes[type] = (gapTypes[type] || 0) + 1; }); }); return gapTypes; } private async analyzeExistingDocumentation(docsPath: string): Promise<Map<string, any>> { const docs = new Map<string, any>(); try { await this.recursivelyAnalyzeDocuments(docsPath, docs); } catch (error) { console.warn('Failed to analyze existing documentation:', error); } return docs; } private async recursivelyAnalyzeDocuments( dirPath: string, docs: Map<string, any>, relativePath: string = '', ): Promise<void> { try { const entries = await fs.readdir(dirPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dirPath, entry.name); const docPath = path.join(relativePath, entry.name); if (entry.isDirectory()) { await this.recursivelyAnalyzeDocuments(fullPath, docs, docPath); } else if (entry.name.endsWith('.md') || entry.name.endsWith('.mdx')) { try { const content = await fs.readFile(fullPath, 'utf-8'); const analysis = this.analyzeDocumentContent(content, docPath); docs.set(docPath, { content, analysis, lastModified: (await fs.stat(fullPath)).mtime, path: fullPath, }); } catch (error) { console.warn(`Failed to read document ${fullPath}:`, error); } } } } catch (error) { console.warn(`Failed to read directory ${dirPath}:`, error); } } private analyzeDocumentContent(content: string, filePath: string): any { return { type: this.inferDocumentType(filePath, content), sections: this.extractSections(content), codeBlocks: this.extractCodeBlocks(content), links: this.extractLinks(content), lastUpdated: this.extractLastUpdated(content), version: this.extractVersion(content), dependencies: this.extractMentionedDependencies(content), features: this.extractDocumentedFeatures(content), wordCount: content.split(/\s+/).length, headingStructure: this.extractHeadingStructure(content), }; } private inferDocumentType(filePath: string, content: string): string { const fileName = path.basename(filePath).toLowerCase(); const pathParts = filePath.toLowerCase().split(path.sep); // Diataxis categories if (pathParts.includes('tutorials')) return 'tutorial'; if (pathParts.includes('how-to') || pathParts.includes('howto')) return 'how-to'; if (pathParts.includes('reference')) return 'reference'; if (pathParts.includes('explanation')) return 'explanation'; // Common documentation types if (fileName.includes('readme')) return 'readme'; if (fileName.includes('getting-started') || fileName.includes('quickstart')) return 'getting-started'; if (fileName.includes('api')) return 'api-reference'; if (fileName.includes('install') || fileName.includes('setup')) return 'installation'; if (fileName.includes('deploy')) return 'deployment'; if (fileName.includes('config')) return 'configuration'; // Infer from content if (content.includes('# Getting Started') || content.includes('## Getting Started')) return 'getting-started'; if (content.includes('# API') || content.includes('## API')) return 'api-reference'; if (content.includes('# Installation') || content.includes('## Installation')) return 'installation'; return 'general'; } private extractSections(content: string): any[] { const sections: any[] = []; const lines = content.split('\n'); let currentSection: any = null; for (let i = 0; i < lines.length; i++) { const line = lines[i]; const headingMatch = line.match(/^(#{1,6})\s+(.+)/); if (headingMatch) { if (currentSection) { sections.push(currentSection); } currentSection = { level: headingMatch[1].length, title: headingMatch[2], startLine: i + 1, content: [], }; } else if (currentSection) { currentSection.content.push(line); } } if (currentSection) { sections.push(currentSection); } return sections.map((section) => ({ ...section, content: section.content.join('\n'), wordCount: section.content.join(' ').split(/\s+/).length, })); } private extractCodeBlocks(content: string): any[] { const codeBlocks: any[] = []; const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g; let match; while ((match = codeBlockRegex.exec(content)) !== null) { codeBlocks.push({ language: match[1] || 'text', code: match[2], startIndex: match.index, endIndex: match.index + match[0].length, }); } return codeBlocks; } private extractLinks(content: string): any[] { const links: any[] = []; const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g; let match; while ((match = linkRegex.exec(content)) !== null) { links.push({ text: match[1], url: match[2], isInternal: !match[2].startsWith('http'), startIndex: match.index, }); } return links; } private extractLastUpdated(content: string): string | null { const updateMatch = content.match(/(?:last updated|updated|modified):\s*(.+)/i); return updateMatch ? updateMatch[1] : null; } private extractVersion(content: string): string | null { const versionMatch = content.match(/(?:version|v)[\s:]+([\d.]+)/i); return versionMatch ? versionMatch[1] : null; } private extractMentionedDependencies(content: string): string[] { const dependencies: Set<string> = new Set(); // Extract from npm install commands const npmMatches = content.match(/npm install\s+([^`\n]+)/g); if (npmMatches) { npmMatches.forEach((match) => { const packages = match.replace('npm install', '').trim().split(/\s+/); packages.forEach((pkg) => { if (pkg && !pkg.startsWith('-')) { dependencies.add(pkg); } }); }); } // Extract from import statements const importMatches = content.match(/import.*from\s+['"]([^'"]+)['"]/g); if (importMatches) { importMatches.forEach((match) => { const packageMatch = match.match(/from\s+['"]([^'"]+)['"]/); if (packageMatch && !packageMatch[1].startsWith('.')) { dependencies.add(packageMatch[1]); } }); } return Array.from(dependencies); } private extractDocumentedFeatures(content: string): string[] { const features: Set<string> = new Set(); // Extract function names from code blocks const functionMatches = content.match(/(?:function|const|let|var)\s+(\w+)/g); if (functionMatches) { functionMatches.forEach((match) => { const functionMatch = match.match(/(?:function|const|let|var)\s+(\w+)/); if (functionMatch) { features.add(functionMatch[1]); } }); } // Extract API endpoints const apiMatches = content.match(/(?:GET|POST|PUT|DELETE|PATCH)\s+([/\w-]+)/g); if (apiMatches) { apiMatches.forEach((match) => { const endpointMatch = match.match(/(?:GET|POST|PUT|DELETE|PATCH)\s+([/\w-]+)/); if (endpointMatch) { features.add(endpointMatch[1]); } }); } // Extract mentioned features from headings const headings = content.match(/#{1,6}\s+(.+)/g); if (headings) { headings.forEach((heading) => { const headingText = heading.replace(/#{1,6}\s+/, '').toLowerCase(); if (headingText.includes('feature') || headingText.includes('functionality')) { features.add(headingText); } }); } return Array.from(features); } private extractHeadingStructure(content: string): any[] { const headings: any[] = []; const lines = content.split('\n'); lines.forEach((line, index) => { const headingMatch = line.match(/^(#{1,6})\s+(.+)/); if (headingMatch) { headings.push({ level: headingMatch[1].length, text: headingMatch[2], line: index + 1, }); } }); return headings; } private async performCodeDocumentationComparison( analysis: any, existingDocs: Map<string, any>, _options: UpdateOptions, ): Promise<CodeDocumentationComparison> { const codeFeatures = this.extractCodeFeatures(analysis); const documentedFeatures = this.extractAllDocumentedFeatures(existingDocs); const gaps = await this.detectDocumentationGaps(codeFeatures, documentedFeatures, _options); const outdatedSections = await this.detectOutdatedSections(analysis, existingDocs); const accuracyIssues = await this.detectAccuracyIssues(analysis, existingDocs); return { codeFeatures, documentedFeatures, gaps, outdatedSections, accuracyIssues, }; } private extractCodeFeatures(analysis: any): any[] { const features: any[] = []; // Extract from dependencies if (analysis.dependencies?.packages) { analysis.dependencies.packages.forEach((pkg: string) => { features.push({ type: 'dependency', name: pkg, source: 'package.json', }); }); } // Extract from scripts const packageJson = this.findPackageJsonInAnalysis(analysis); if (packageJson?.scripts) { Object.keys(packageJson.scripts).forEach((script) => { features.push({ type: 'script', name: script, command: packageJson.scripts[script], source: 'package.json', }); }); } // Extract from file structure if (analysis.structure) { if (analysis.structure.hasTests) { features.push({ type: 'testing', name: 'test suite', source: 'structure' }); } if (analysis.structure.hasCI) { features.push({ type: 'ci-cd', name: 'continuous integration', source: 'structure' }); } } // Extract from technologies if (analysis.technologies) { Object.entries(analysis.technologies).forEach(([key, value]) => { if (value) { features.push({ type: 'technology', name: key, value: value, source: 'analysis', }); } }); } return features; } private findPackageJsonInAnalysis(analysis: any): any { const files = analysis.files || []; const packageFile = files.find((f: any) => f.name === 'package.json'); if (packageFile?.content) { try { return JSON.parse(packageFile.content); } catch { return null; } } return null; } private extractAllDocumentedFeatures(existingDocs: Map<string, any>): any[] { const allFeatures: any[] = []; existingDocs.forEach((doc, docPath) => { const features = doc.analysis?.features || []; const dependencies = doc.analysis?.dependencies || []; features.forEach((feature: string) => { allFeatures.push({ name: feature, source: docPath, type: 'documented-feature', }); }); dependencies.forEach((dep: string) => { allFeatures.push({ name: dep, source: docPath, type: 'documented-dependency', }); }); }); return allFeatures; } private async detectDocumentationGaps( codeFeatures: any[], documentedFeatures: any[], _options: UpdateOptions, ): Promise<DocumentationGap[]> { const gaps: DocumentationGap[] = []; const memoryGapPatterns = this.memoryInsights?.commonGapTypes || {}; // Find features in code that aren't documented codeFeatures.forEach((codeFeature) => { const isDocumented = documentedFeatures.some((docFeature) => this.featuresMatch(codeFeature, docFeature), ); if (!isDocumented) { const severity = this.determineGapSeverity(codeFeature, memoryGapPatterns); const suggestedUpdate = this.generateGapSuggestion(codeFeature, _options); gaps.push({ type: 'missing', location: `${codeFeature.source} -> documentation`, description: `${codeFeature.type} '${codeFeature.name}' exists in code but is not documented`, severity, suggestedUpdate, memoryEvidence: this.findMemoryEvidenceForGap(codeFeature), }); } }); // Find documented features that no longer exist in code documentedFeatures.forEach((docFeature) => { const existsInCode = codeFeatures.some((codeFeature) => this.featuresMatch(codeFeature, docFeature), ); if (!existsInCode) { gaps.push({ type: 'outdated', location: docFeature.source, description: `Documented feature '${docFeature.name}' no longer exists in code`, severity: 'medium', suggestedUpdate: `Remove or update documentation for '${docFeature.name}'`, memoryEvidence: this.findMemoryEvidenceForOutdated(docFeature), }); } }); return gaps; } private featuresMatch(codeFeature: any, docFeature: any): boolean { // Exact name match if (codeFeature.name === docFeature.name) return true; // Type-specific matching if (codeFeature.type === 'dependency' && docFeature.type === 'documented-dependency') { return codeFeature.name === docFeature.name; } // Partial match for similar names const codeName = codeFeature.name.toLowerCase(); const docName = docFeature.name.toLowerCase(); return codeName.includes(docName) || docName.includes(codeName); } private determineGapSeverity( codeFeature: any, memoryGapPatterns: Record<string, number>, ): 'low' | 'medium' | 'high' | 'critical' { // High importance features if ( codeFeature.type === 'script' && ['start', 'dev', 'build', 'test'].includes(codeFeature.name) ) { return 'high'; } if (codeFeature.type === 'dependency' && this.isCriticalDependency(codeFeature.name)) { return 'high'; } if (codeFeature.type === 'testing' || codeFeature.type === 'ci-cd') { return 'medium'; } // Check memory patterns for common gaps const gapFrequency = memoryGapPatterns[codeFeature.type] || 0; if (gapFrequency > 5) return 'medium'; // Common gap type if (gapFrequency > 2) return 'low'; return 'low'; } private isCriticalDependency(depName: string): boolean { const criticalDeps = [ 'react', 'vue', 'angular', 'express', 'fastify', 'next', 'nuxt', 'gatsby', 'typescript', 'jest', 'mocha', 'webpack', 'vite', 'rollup', ]; return criticalDeps.some((critical) => depName.includes(critical)); } private generateGapSuggestion(codeFeature: any, _options: UpdateOptions): string { switch (codeFeature.type) { case 'script': return `Add documentation for the '${codeFeature.name}' script: \`npm run ${codeFeature.name}\``; case 'dependency': return `Document the '${codeFeature.name}' dependency and its usage`; case 'testing': return `Add testing documentation explaining how to run and write tests`; case 'ci-cd': return `Document the CI/CD pipeline and deployment process`; case 'technology': return `Add explanation for ${codeFeature.name}: ${codeFeature.value}`; default: return `Document the ${codeFeature.type} '${codeFeature.name}'`; } } private findMemoryEvidenceForGap(codeFeature: any): any[] { return ( this.memoryInsights?.similarProjects .filter((p: any) => p.content?.gaps?.some((gap: any) => gap.type === codeFeature.type)) .slice(0, 3) || [] ); } private findMemoryEvidenceForOutdated(docFeature: any): any[] { return ( this.memoryInsights?.similarProjects .filter( (p: any) => p.content?.outdatedSections?.some( (section: any) => section.feature === docFeature.name, ), ) .slice(0, 3) || [] ); } private async detectOutdatedSections( analysis: any, existingDocs: Map<string, any>, ): Promise<any[]> { const outdatedSections: any[] = []; existingDocs.forEach((doc, docPath) => { const sections = doc.analysis?.sections || []; sections.forEach((section: any) => { const isOutdated = this.checkSectionOutdated(section, analysis); if (isOutdated) { outdatedSections.push({ location: docPath, section: section.title, reason: isOutdated.reason, confidence: isOutdated.confidence, suggestedUpdate: isOutdated.suggestedUpdate, }); } }); }); return outdatedSections; } private checkSectionOutdated(section: any, analysis: any): any { const sectionContent = section.content.toLowerCase(); // Check for outdated Node.js versions const nodeVersionMatch = sectionContent.match(/node(?:\.js)?\s+(\d+)/); if (nodeVersionMatch) { const documentedVersion = parseInt(nodeVersionMatch[1], 10); const currentRecommended = 18; // Current LTS if (documentedVersion < currentRecommended - 2) { return { reason: `Documented Node.js version ${documentedVersion} is outdated`, confidence: 0.9, suggestedUpdate: `Update to recommend Node.js ${currentRecommended}+`, }; } } // Check for outdated package names const packageJson = this.findPackageJsonInAnalysis(analysis); if (packageJson?.dependencies) { const currentDeps = Object.keys(packageJson.dependencies); // Look for documented packages that are no longer dependencies for (const dep of currentDeps) { if (sectionContent.includes(dep)) { const version = packageJson.dependencies[dep]; if (sectionContent.includes(dep) && !sectionContent.includes(version)) { return { reason: `Package version information may be outdated for ${dep}`, confidence: 0.7, suggestedUpdate: `Update ${dep} version references to ${version}`, }; } } } } return null; } private async detectAccuracyIssues( analysis: any, existingDocs: Map<string, any>, ): Promise<any[]> { const accuracyIssues: any[] = []; existingDocs.forEach((doc, docPath) => { const codeBlocks = doc.analysis?.codeBlocks || []; codeBlocks.forEach((codeBlock: any, index: number) => { const issues = this.validateCodeBlock(codeBlock, analysis); issues.forEach((issue) => { accuracyIssues.push({ location: `${docPath}:code-block-${index}`, type: issue.type, description: issue.description, severity: issue.severity, suggestedFix: issue.suggestedFix, }); }); }); }); return accuracyIssues; } private validateCodeBlock(codeBlock: any, analysis: any): any[] { const issues: any[] = []; const code = codeBlock.code; // Check npm install commands against actual dependencies const npmInstallMatches = code.match(/npm install\s+([^`\n]+)/g); if (npmInstallMatches) { const packageJson = this.findPackageJsonInAnalysis(analysis); const actualDeps = packageJson ? Object.keys(packageJson.dependencies || {}) : []; npmInstallMatches.forEach((match: string) => { const packages = match.replace('npm install', '').trim().split(/\s+/); packages.forEach((pkg: string) => { if (pkg && !pkg.startsWith('-') && !actualDeps.includes(pkg)) { issues.push({ type: 'incorrect-dependency', description: `npm install command includes '${pkg}' which is not in package.json`, severity: 'medium', suggestedFix: `Remove '${pkg}' or add it to dependencies`, }); } }); }); } // Check for outdated import syntax if (code.includes('require(') && analysis.metadata?.primaryLanguage === 'TypeScript') { issues.push({ type: 'outdated-syntax', description: 'Using require() syntax in TypeScript project', severity: 'low', suggestedFix: 'Update to ES6 import syntax', }); } return issues; } private async generateUpdateRecommendations( comparison: CodeDocumentationComparison, _options: UpdateOptions, ): Promise<UpdateRecommendation[]> { const recommendations: UpdateRecommendation[] = []; // Generate recommendations for gaps for (const gap of comparison.gaps) { if ( gap.severity === 'critical' || gap.severity === 'high' || (gap.severity === 'medium' && _options.updateStrategy !== 'conservative') ) { const recommendation = await this.generateGapRecommendation(gap, _options); recommendations.push(recommendation); } } // Generate recommendations for outdated sections for (const outdated of comparison.outdatedSections) { const recommendation = await this.generateOutdatedRecommendation(outdated, _options); recommendations.push(recommendation); } // Generate recommendations for accuracy issues for (const issue of comparison.accuracyIssues) { if (issue.severity !== 'low' || _options.updateStrategy === 'aggressive') { const recommendation = await this.generateAccuracyRecommendation(issue, _options); recommendations.push(recommendation); } } return recommendations.sort((a, b) => b.confidence - a.confidence); } private async generateGapRecommendation( gap: DocumentationGap, _options: UpdateOptions, ): Promise<UpdateRecommendation> { const memoryEvidence = gap.memoryEvidence || []; const successfulPatterns = this.memoryInsights?.successfulUpdatePatterns || []; return { section: gap.location, currentContent: '', // No current content for missing items suggestedContent: this.generateContentForGap(gap, successfulPatterns), reasoning: `${gap.description}. ${memoryEvidence.length} similar projects had similar gaps.`, memoryEvidence, confidence: this.calculateGapConfidence(gap, memoryEvidence), effort: this.estimateGapEffort(gap), }; } private generateContentForGap(gap: DocumentationGap, patterns: any[]): string { // Use memory patterns to generate appropriate content const relevantPatterns = patterns.filter((p) => p.gapType === gap.type); if (relevantPatterns.length > 0) { const bestPattern = relevantPatterns[0]; return this.adaptPatternToGap(bestPattern, gap); } return gap.suggestedUpdate; } private adaptPatternToGap(pattern: any, gap: DocumentationGap): string { let content = pattern.template || pattern.content || gap.suggestedUpdate; // Replace placeholders with actual gap information content = content.replace(/\{feature\}/g, gap.description); content = content.replace(/\{location\}/g, gap.location); return content; } private calculateGapConfidence(gap: DocumentationGap, evidence: any[]): number { let confidence = 0.5; // Base confidence // Increase confidence based on severity switch (gap.severity) { case 'critical': confidence += 0.4; break; case 'high': confidence += 0.3; break; case 'medium': confidence += 0.2; break; case 'low': confidence += 0.1; break; } // Increase confidence based on memory evidence confidence += Math.min(evidence.length * 0.1, 0.3); return Math.min(confidence, 1.0); } private estimateGapEffort(gap: DocumentationGap): 'low' | 'medium' | 'high' { switch (gap.type) { case 'missing': return gap.severity === 'critical' ? 'high' : 'medium'; case 'outdated': return 'low'; case 'incorrect': return 'medium'; case 'incomplete': return 'low'; default: return 'medium'; } } private async generateOutdatedRecommendation( outdated: any, _options: UpdateOptions, ): Promise<UpdateRecommendation> { return { section: outdated.location, currentContent: outdated.section, suggestedContent: outdated.suggestedUpdate, reasoning: outdated.reason, memoryEvidence: [], confidence: outdated.confidence || 0.8, effort: 'low', }; } private async generateAccuracyRecommendation( issue: any, _options: UpdateOptions, ): Promise<UpdateRecommendation> { return { section: issue.location, currentContent: 'Code block with accuracy issues', suggestedContent: issue.suggestedFix, reasoning: issue.description, memoryEvidence: [], confidence: issue.severity === 'high' ? 0.9 : 0.7, effort: issue.severity === 'high' ? 'medium' : 'low', }; } private calculateUpdateMetrics( comparison: CodeDocumentationComparison, recommendations: UpdateRecommendation[], ): any { const totalGaps = comparison.gaps.length; const totalRecommendations = recommendations.length; const avgConfidence = recommendations.reduce((sum, r) => sum + r.confidence, 0) / recommendations.length || 0; const effortCounts = recommendations.reduce( (acc, r) => { acc[r.effort] = (acc[r.effort] || 0) + 1; return acc; }, {} as Record<string, number>, ); let estimatedEffort = 'low'; if (effortCounts.high > 0) estimatedEffort = 'high'; else if (effortCounts.medium > effortCounts.low) estimatedEffort = 'medium'; return { gapsDetected: totalGaps, recommendationsGenerated: totalRecommendations, confidenceScore: Math.round(avgConfidence * 100) / 100, estimatedEffort, }; } private generateMemoryInformedNextSteps( comparison: CodeDocumentationComparison, recommendations: UpdateRecommendation[], ): string[] { const nextSteps = []; const highConfidenceRecs = recommendations.filter((r) => r.confidence > 0.8); const criticalGaps = comparison.gaps.filter((g) => g.severity === 'critical'); if (criticalGaps.length > 0) { nextSteps.push(`Address ${criticalGaps.length} critical documentation gaps immediately`); } if (highConfidenceRecs.length > 0) { nextSteps.push( `Implement ${highConfidenceRecs.length} high-confidence recommendations first`, ); } if (comparison.accuracyIssues.length > 0) { nextSteps.push( `Fix ${comparison.accuracyIssues.length} code accuracy issues in documentation`, ); } nextSteps.push('Review and validate all recommended changes before implementation'); nextSteps.push('Test updated code examples to ensure they work correctly'); const memoryInsights = this.memoryInsights?.similarProjects?.length || 0; if (memoryInsights > 0) { nextSteps.push( `Leverage patterns from ${memoryInsights} similar projects for additional improvements`, ); } return nextSteps; } } // Export the tool implementation export const updateExistingDocumentation: Tool = { name: 'update_existing_documentation', description: 'Intelligently analyze and update existing documentation using memory insights and code comparison', inputSchema: { type: 'object', properties: { analysisId: { type: 'string', description: 'Repository analysis ID from analyze_repository tool', }, docsPath: { type: 'string', description: 'Path to existing documentation directory', }, compareMode: { type: 'string', enum: ['comprehensive', 'gap-detection', 'accuracy-check'], default: 'comprehensive', description: 'Mode of comparison between code and documentation', }, updateStrategy: { type: 'string', enum: ['conservative', 'moderate', 'aggressive'], default: 'moderate', description: 'How aggressively to suggest updates', }, preserveStyle: { type: 'boolean', default: true, description: 'Preserve existing documentation style and formatting', }, focusAreas: { type: 'array', items: { type: 'string' }, description: 'Specific areas to focus updates on (e.g., "dependencies", "scripts", "api")', }, }, required: ['analysisId', 'docsPath'], }, }; export async function handleUpdateExistingDocumentation(args: any): Promise<UpdateResult> { const engine = new DocumentationUpdateEngine(); return await engine.updateExistingDocumentation(args); }

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/tosin2013/documcp'

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