Skip to main content
Glama

documcp

by tosin2013
update-existing-documentation.ts38.1 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