#
tokens: 45082/50000 5/307 files (page 22/33)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 22 of 33. Use http://codebase.md/tosin2013/documcp?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .dockerignore
├── .eslintignore
├── .eslintrc.json
├── .github
│   ├── agents
│   │   ├── documcp-ast.md
│   │   ├── documcp-deploy.md
│   │   ├── documcp-memory.md
│   │   ├── documcp-test.md
│   │   └── documcp-tool.md
│   ├── copilot-instructions.md
│   ├── dependabot.yml
│   ├── ISSUE_TEMPLATE
│   │   ├── automated-changelog.md
│   │   ├── bug_report.md
│   │   ├── bug_report.yml
│   │   ├── documentation_issue.md
│   │   ├── feature_request.md
│   │   ├── feature_request.yml
│   │   ├── npm-publishing-fix.md
│   │   └── release_improvements.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── release-drafter.yml
│   └── workflows
│       ├── auto-merge.yml
│       ├── ci.yml
│       ├── codeql.yml
│       ├── dependency-review.yml
│       ├── deploy-docs.yml
│       ├── README.md
│       ├── release-drafter.yml
│       └── release.yml
├── .gitignore
├── .husky
│   ├── commit-msg
│   └── pre-commit
├── .linkcheck.config.json
├── .markdown-link-check.json
├── .nvmrc
├── .pre-commit-config.yaml
├── .versionrc.json
├── ARCHITECTURAL_CHANGES_SUMMARY.md
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── commitlint.config.js
├── CONTRIBUTING.md
├── docker-compose.docs.yml
├── Dockerfile.docs
├── docs
│   ├── .docusaurus
│   │   ├── docusaurus-plugin-content-docs
│   │   │   └── default
│   │   │       └── __mdx-loader-dependency.json
│   │   └── docusaurus-plugin-content-pages
│   │       └── default
│   │           └── __plugin.json
│   ├── adrs
│   │   ├── adr-0001-mcp-server-architecture.md
│   │   ├── adr-0002-repository-analysis-engine.md
│   │   ├── adr-0003-static-site-generator-recommendation-engine.md
│   │   ├── adr-0004-diataxis-framework-integration.md
│   │   ├── adr-0005-github-pages-deployment-automation.md
│   │   ├── adr-0006-mcp-tools-api-design.md
│   │   ├── adr-0007-mcp-prompts-and-resources-integration.md
│   │   ├── adr-0008-intelligent-content-population-engine.md
│   │   ├── adr-0009-content-accuracy-validation-framework.md
│   │   ├── adr-0010-mcp-resource-pattern-redesign.md
│   │   ├── adr-0011-ce-mcp-compatibility.md
│   │   ├── adr-0012-priority-scoring-system-for-documentation-drift.md
│   │   ├── adr-0013-release-pipeline-and-package-distribution.md
│   │   └── README.md
│   ├── api
│   │   ├── .nojekyll
│   │   ├── assets
│   │   │   ├── hierarchy.js
│   │   │   ├── highlight.css
│   │   │   ├── icons.js
│   │   │   ├── icons.svg
│   │   │   ├── main.js
│   │   │   ├── navigation.js
│   │   │   ├── search.js
│   │   │   └── style.css
│   │   ├── hierarchy.html
│   │   ├── index.html
│   │   ├── modules.html
│   │   └── variables
│   │       └── TOOLS.html
│   ├── assets
│   │   └── logo.svg
│   ├── CE-MCP-FINDINGS.md
│   ├── development
│   │   └── MCP_INSPECTOR_TESTING.md
│   ├── docusaurus.config.js
│   ├── explanation
│   │   ├── architecture.md
│   │   └── index.md
│   ├── guides
│   │   ├── link-validation.md
│   │   ├── playwright-integration.md
│   │   └── playwright-testing-workflow.md
│   ├── how-to
│   │   ├── analytics-setup.md
│   │   ├── change-watcher.md
│   │   ├── custom-domains.md
│   │   ├── documentation-freshness-tracking.md
│   │   ├── drift-priority-scoring.md
│   │   ├── github-pages-deployment.md
│   │   ├── index.md
│   │   ├── llm-integration.md
│   │   ├── local-testing.md
│   │   ├── performance-optimization.md
│   │   ├── prompting-guide.md
│   │   ├── repository-analysis.md
│   │   ├── seo-optimization.md
│   │   ├── site-monitoring.md
│   │   ├── troubleshooting.md
│   │   └── usage-examples.md
│   ├── index.md
│   ├── knowledge-graph.md
│   ├── package-lock.json
│   ├── package.json
│   ├── phase-2-intelligence.md
│   ├── reference
│   │   ├── api-overview.md
│   │   ├── cli.md
│   │   ├── configuration.md
│   │   ├── deploy-pages.md
│   │   ├── index.md
│   │   ├── mcp-tools.md
│   │   └── prompt-templates.md
│   ├── research
│   │   ├── cross-domain-integration
│   │   │   └── README.md
│   │   ├── domain-1-mcp-architecture
│   │   │   ├── index.md
│   │   │   └── mcp-performance-research.md
│   │   ├── domain-2-repository-analysis
│   │   │   └── README.md
│   │   ├── domain-3-ssg-recommendation
│   │   │   ├── index.md
│   │   │   └── ssg-performance-analysis.md
│   │   ├── domain-4-diataxis-integration
│   │   │   └── README.md
│   │   ├── domain-5-github-deployment
│   │   │   ├── github-pages-security-analysis.md
│   │   │   └── index.md
│   │   ├── domain-6-api-design
│   │   │   └── README.md
│   │   ├── README.md
│   │   ├── research-integration-summary-2025-01-14.md
│   │   ├── research-progress-template.md
│   │   └── research-questions-2025-01-14.md
│   ├── robots.txt
│   ├── sidebars.js
│   ├── sitemap.xml
│   ├── src
│   │   └── css
│   │       └── custom.css
│   └── tutorials
│       ├── development-setup.md
│       ├── environment-setup.md
│       ├── first-deployment.md
│       ├── getting-started.md
│       ├── index.md
│       ├── memory-workflows.md
│       └── user-onboarding.md
├── ISSUE_IMPLEMENTATION_SUMMARY.md
├── jest.config.js
├── LICENSE
├── Makefile
├── MCP_PHASE2_IMPLEMENTATION.md
├── mcp-config-example.json
├── mcp.json
├── package-lock.json
├── package.json
├── README.md
├── release.sh
├── scripts
│   └── check-package-structure.cjs
├── SECURITY.md
├── setup-precommit.sh
├── src
│   ├── benchmarks
│   │   └── performance.ts
│   ├── index.ts
│   ├── memory
│   │   ├── contextual-retrieval.ts
│   │   ├── deployment-analytics.ts
│   │   ├── enhanced-manager.ts
│   │   ├── export-import.ts
│   │   ├── freshness-kg-integration.ts
│   │   ├── index.ts
│   │   ├── integration.ts
│   │   ├── kg-code-integration.ts
│   │   ├── kg-health.ts
│   │   ├── kg-integration.ts
│   │   ├── kg-link-validator.ts
│   │   ├── kg-storage.ts
│   │   ├── knowledge-graph.ts
│   │   ├── learning.ts
│   │   ├── manager.ts
│   │   ├── multi-agent-sharing.ts
│   │   ├── pruning.ts
│   │   ├── schemas.ts
│   │   ├── storage.ts
│   │   ├── temporal-analysis.ts
│   │   ├── user-preferences.ts
│   │   └── visualization.ts
│   ├── prompts
│   │   └── technical-writer-prompts.ts
│   ├── scripts
│   │   └── benchmark.ts
│   ├── templates
│   │   └── playwright
│   │       ├── accessibility.spec.template.ts
│   │       ├── Dockerfile.template
│   │       ├── docs-e2e.workflow.template.yml
│   │       ├── link-validation.spec.template.ts
│   │       └── playwright.config.template.ts
│   ├── tools
│   │   ├── analyze-deployments.ts
│   │   ├── analyze-readme.ts
│   │   ├── analyze-repository.ts
│   │   ├── change-watcher.ts
│   │   ├── check-documentation-links.ts
│   │   ├── cleanup-agent-artifacts.ts
│   │   ├── deploy-pages.ts
│   │   ├── detect-gaps.ts
│   │   ├── evaluate-readme-health.ts
│   │   ├── generate-config.ts
│   │   ├── generate-contextual-content.ts
│   │   ├── generate-llm-context.ts
│   │   ├── generate-readme-template.ts
│   │   ├── generate-technical-writer-prompts.ts
│   │   ├── kg-health-check.ts
│   │   ├── manage-preferences.ts
│   │   ├── manage-sitemap.ts
│   │   ├── optimize-readme.ts
│   │   ├── populate-content.ts
│   │   ├── readme-best-practices.ts
│   │   ├── recommend-ssg.ts
│   │   ├── setup-playwright-tests.ts
│   │   ├── setup-structure.ts
│   │   ├── simulate-execution.ts
│   │   ├── sync-code-to-docs.ts
│   │   ├── test-local-deployment.ts
│   │   ├── track-documentation-freshness.ts
│   │   ├── update-existing-documentation.ts
│   │   ├── validate-content.ts
│   │   ├── validate-documentation-freshness.ts
│   │   ├── validate-readme-checklist.ts
│   │   └── verify-deployment.ts
│   ├── types
│   │   └── api.ts
│   ├── utils
│   │   ├── artifact-detector.ts
│   │   ├── ast-analyzer.ts
│   │   ├── change-watcher.ts
│   │   ├── code-scanner.ts
│   │   ├── content-extractor.ts
│   │   ├── drift-detector.ts
│   │   ├── execution-simulator.ts
│   │   ├── freshness-tracker.ts
│   │   ├── language-parsers-simple.ts
│   │   ├── llm-client.ts
│   │   ├── permission-checker.ts
│   │   ├── semantic-analyzer.ts
│   │   ├── sitemap-generator.ts
│   │   ├── usage-metadata.ts
│   │   └── user-feedback-integration.ts
│   └── workflows
│       └── documentation-workflow.ts
├── test-docs-local.sh
├── tests
│   ├── api
│   │   └── mcp-responses.test.ts
│   ├── benchmarks
│   │   └── performance.test.ts
│   ├── call-graph-builder.test.ts
│   ├── change-watcher-priority.integration.test.ts
│   ├── change-watcher.test.ts
│   ├── edge-cases
│   │   └── error-handling.test.ts
│   ├── execution-simulator.test.ts
│   ├── functional
│   │   └── tools.test.ts
│   ├── integration
│   │   ├── kg-documentation-workflow.test.ts
│   │   ├── knowledge-graph-workflow.test.ts
│   │   ├── mcp-readme-tools.test.ts
│   │   ├── memory-mcp-tools.test.ts
│   │   ├── readme-technical-writer.test.ts
│   │   └── workflow.test.ts
│   ├── memory
│   │   ├── contextual-retrieval.test.ts
│   │   ├── enhanced-manager.test.ts
│   │   ├── export-import.test.ts
│   │   ├── freshness-kg-integration.test.ts
│   │   ├── kg-code-integration.test.ts
│   │   ├── kg-health.test.ts
│   │   ├── kg-link-validator.test.ts
│   │   ├── kg-storage-validation.test.ts
│   │   ├── kg-storage.test.ts
│   │   ├── knowledge-graph-documentation-examples.test.ts
│   │   ├── knowledge-graph-enhanced.test.ts
│   │   ├── knowledge-graph.test.ts
│   │   ├── learning.test.ts
│   │   ├── manager-advanced.test.ts
│   │   ├── manager.test.ts
│   │   ├── mcp-resource-integration.test.ts
│   │   ├── mcp-tool-persistence.test.ts
│   │   ├── schemas-documentation-examples.test.ts
│   │   ├── schemas.test.ts
│   │   ├── storage.test.ts
│   │   ├── temporal-analysis.test.ts
│   │   └── user-preferences.test.ts
│   ├── performance
│   │   ├── memory-load-testing.test.ts
│   │   └── memory-stress-testing.test.ts
│   ├── prompts
│   │   ├── guided-workflow-prompts.test.ts
│   │   └── technical-writer-prompts.test.ts
│   ├── server.test.ts
│   ├── setup.ts
│   ├── tools
│   │   ├── all-tools.test.ts
│   │   ├── analyze-coverage.test.ts
│   │   ├── analyze-deployments.test.ts
│   │   ├── analyze-readme.test.ts
│   │   ├── analyze-repository.test.ts
│   │   ├── check-documentation-links.test.ts
│   │   ├── cleanup-agent-artifacts.test.ts
│   │   ├── deploy-pages-kg-retrieval.test.ts
│   │   ├── deploy-pages-tracking.test.ts
│   │   ├── deploy-pages.test.ts
│   │   ├── detect-gaps.test.ts
│   │   ├── evaluate-readme-health.test.ts
│   │   ├── generate-contextual-content.test.ts
│   │   ├── generate-llm-context.test.ts
│   │   ├── generate-readme-template.test.ts
│   │   ├── generate-technical-writer-prompts.test.ts
│   │   ├── kg-health-check.test.ts
│   │   ├── manage-sitemap.test.ts
│   │   ├── optimize-readme.test.ts
│   │   ├── readme-best-practices.test.ts
│   │   ├── recommend-ssg-historical.test.ts
│   │   ├── recommend-ssg-preferences.test.ts
│   │   ├── recommend-ssg.test.ts
│   │   ├── simple-coverage.test.ts
│   │   ├── sync-code-to-docs.test.ts
│   │   ├── test-local-deployment.test.ts
│   │   ├── tool-error-handling.test.ts
│   │   ├── track-documentation-freshness.test.ts
│   │   ├── validate-content.test.ts
│   │   ├── validate-documentation-freshness.test.ts
│   │   └── validate-readme-checklist.test.ts
│   ├── types
│   │   └── type-safety.test.ts
│   └── utils
│       ├── artifact-detector.test.ts
│       ├── ast-analyzer.test.ts
│       ├── content-extractor.test.ts
│       ├── drift-detector-diataxis.test.ts
│       ├── drift-detector-priority.test.ts
│       ├── drift-detector.test.ts
│       ├── freshness-tracker.test.ts
│       ├── llm-client.test.ts
│       ├── semantic-analyzer.test.ts
│       ├── sitemap-generator.test.ts
│       ├── usage-metadata.test.ts
│       └── user-feedback-integration.test.ts
├── tsconfig.json
└── typedoc.json
```

# Files

--------------------------------------------------------------------------------
/tests/performance/memory-load-testing.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Memory System Performance and Load Testing
  3 |  * Tests performance, scalability, and resource usage of memory system
  4 |  * Part of Issue #57 - Memory System Performance and Load Testing
  5 |  */
  6 | 
  7 | import { promises as fs } from "fs";
  8 | import path from "path";
  9 | import os from "os";
 10 | import { performance } from "perf_hooks";
 11 | import { MemoryManager } from "../../src/memory/manager.js";
 12 | import { EnhancedMemoryManager } from "../../src/memory/enhanced-manager.js";
 13 | import { IncrementalLearningSystem } from "../../src/memory/learning.js";
 14 | import { KnowledgeGraph } from "../../src/memory/knowledge-graph.js";
 15 | import {
 16 |   initializeMemory,
 17 |   rememberAnalysis,
 18 |   rememberRecommendation,
 19 |   getSimilarProjects,
 20 | } from "../../src/memory/integration.js";
 21 | 
 22 | interface PerformanceMetrics {
 23 |   operationTime: number;
 24 |   memoryUsed: number;
 25 |   operationsPerSecond: number;
 26 |   throughput: number;
 27 | }
 28 | 
 29 | describe("Memory System Performance and Load Testing", () => {
 30 |   let tempDir: string;
 31 |   let memoryManager: MemoryManager;
 32 | 
 33 |   beforeEach(async () => {
 34 |     tempDir = path.join(
 35 |       os.tmpdir(),
 36 |       `memory-performance-test-${Date.now()}-${Math.random()
 37 |         .toString(36)
 38 |         .substr(2, 9)}`,
 39 |     );
 40 |     await fs.mkdir(tempDir, { recursive: true });
 41 | 
 42 |     memoryManager = new MemoryManager(tempDir);
 43 |     await memoryManager.initialize();
 44 |   });
 45 | 
 46 |   afterEach(async () => {
 47 |     try {
 48 |       await fs.rm(tempDir, { recursive: true, force: true });
 49 |     } catch (error) {
 50 |       // Ignore cleanup errors
 51 |     }
 52 |   });
 53 | 
 54 |   function measurePerformance<T>(
 55 |     operation: () => Promise<T>,
 56 |   ): Promise<{ result: T; metrics: PerformanceMetrics }> {
 57 |     return new Promise(async (resolve) => {
 58 |       const startTime = performance.now();
 59 |       const startMemory = process.memoryUsage();
 60 | 
 61 |       const result = await operation();
 62 | 
 63 |       const endTime = performance.now();
 64 |       const endMemory = process.memoryUsage();
 65 | 
 66 |       const operationTime = endTime - startTime;
 67 |       const memoryUsed = endMemory.heapUsed - startMemory.heapUsed;
 68 | 
 69 |       resolve({
 70 |         result,
 71 |         metrics: {
 72 |           operationTime,
 73 |           memoryUsed,
 74 |           operationsPerSecond: 1000 / operationTime,
 75 |           throughput: 1000 / operationTime,
 76 |         },
 77 |       });
 78 |     });
 79 |   }
 80 | 
 81 |   describe("Basic Operations Performance", () => {
 82 |     test("should perform single memory operations efficiently", async () => {
 83 |       memoryManager.setContext({ projectId: "performance-single" });
 84 | 
 85 |       const testData = {
 86 |         projectId: "performance-single",
 87 |         language: { primary: "typescript" },
 88 |         framework: { name: "react" },
 89 |         stats: { files: 100, lines: 10000 },
 90 |       };
 91 | 
 92 |       const { metrics: createMetrics } = await measurePerformance(async () => {
 93 |         return await memoryManager.remember("analysis", testData);
 94 |       });
 95 | 
 96 |       const memoryId = (await memoryManager.remember("analysis", testData)).id;
 97 | 
 98 |       const { metrics: readMetrics } = await measurePerformance(async () => {
 99 |         return await memoryManager.recall(memoryId);
100 |       });
101 | 
102 |       const { metrics: searchMetrics } = await measurePerformance(async () => {
103 |         return await memoryManager.search({ projectId: "performance-single" });
104 |       });
105 | 
106 |       // Performance expectations (adjust based on system capabilities)
107 |       expect(createMetrics.operationTime).toBeLessThan(100); // 100ms
108 |       expect(readMetrics.operationTime).toBeLessThan(50); // 50ms
109 |       expect(searchMetrics.operationTime).toBeLessThan(100); // 100ms
110 | 
111 |       // Memory usage should be reasonable
112 |       expect(createMetrics.memoryUsed).toBeLessThan(10 * 1024 * 1024); // 10MB
113 |       expect(readMetrics.memoryUsed).toBeLessThan(1 * 1024 * 1024); // 1MB
114 | 
115 |       console.log("Single Operation Performance:");
116 |       console.log(
117 |         `Create: ${createMetrics.operationTime.toFixed(2)}ms, Memory: ${(
118 |           createMetrics.memoryUsed / 1024
119 |         ).toFixed(2)}KB`,
120 |       );
121 |       console.log(
122 |         `Read: ${readMetrics.operationTime.toFixed(2)}ms, Memory: ${(
123 |           readMetrics.memoryUsed / 1024
124 |         ).toFixed(2)}KB`,
125 |       );
126 |       console.log(
127 |         `Search: ${searchMetrics.operationTime.toFixed(2)}ms, Memory: ${(
128 |           searchMetrics.memoryUsed / 1024
129 |         ).toFixed(2)}KB`,
130 |       );
131 |     });
132 | 
133 |     test("should handle batch operations efficiently", async () => {
134 |       memoryManager.setContext({ projectId: "performance-batch" });
135 | 
136 |       const batchSize = 100;
137 |       const testData = Array.from({ length: batchSize }, (_, i) => ({
138 |         projectId: "performance-batch",
139 |         index: i,
140 |         language: { primary: i % 2 === 0 ? "typescript" : "javascript" },
141 |         framework: {
142 |           name: i % 3 === 0 ? "react" : i % 3 === 1 ? "vue" : "angular",
143 |         },
144 |         stats: { files: 10 + i, lines: 1000 + i * 100 },
145 |       }));
146 | 
147 |       const { metrics: batchCreateMetrics } = await measurePerformance(
148 |         async () => {
149 |           const promises = testData.map((data) =>
150 |             memoryManager.remember("analysis", data),
151 |           );
152 |           return await Promise.all(promises);
153 |         },
154 |       );
155 | 
156 |       const { metrics: batchSearchMetrics } = await measurePerformance(
157 |         async () => {
158 |           return await memoryManager.search({ projectId: "performance-batch" });
159 |         },
160 |       );
161 | 
162 |       // Batch operations should be efficient
163 |       expect(batchCreateMetrics.operationTime).toBeLessThan(5000); // 5 seconds for 100 items
164 |       expect(batchSearchMetrics.operationTime).toBeLessThan(1000); // 1 second to search 100 items
165 | 
166 |       // Calculate throughput
167 |       const createThroughput =
168 |         batchSize / (batchCreateMetrics.operationTime / 1000);
169 |       const searchThroughput =
170 |         batchSize / (batchSearchMetrics.operationTime / 1000);
171 | 
172 |       expect(createThroughput).toBeGreaterThan(20); // At least 20 ops/sec
173 |       expect(searchThroughput).toBeGreaterThan(100); // At least 100 searches/sec
174 | 
175 |       console.log("Batch Operation Performance:");
176 |       console.log(
177 |         `Create ${batchSize} items: ${batchCreateMetrics.operationTime.toFixed(
178 |           2,
179 |         )}ms (${createThroughput.toFixed(2)} ops/sec)`,
180 |       );
181 |       console.log(
182 |         `Search ${batchSize} items: ${batchSearchMetrics.operationTime.toFixed(
183 |           2,
184 |         )}ms (${searchThroughput.toFixed(2)} ops/sec)`,
185 |       );
186 |     });
187 |   });
188 | 
189 |   describe("Scalability Testing", () => {
190 |     test("should scale linearly with data size", async () => {
191 |       memoryManager.setContext({ projectId: "scalability-test" });
192 | 
193 |       const testSizes = [10, 50, 100, 500];
194 |       const results: Array<{
195 |         size: number;
196 |         createTime: number;
197 |         searchTime: number;
198 |       }> = [];
199 | 
200 |       for (const size of testSizes) {
201 |         const testData = Array.from({ length: size }, (_, i) => ({
202 |           projectId: "scalability-test",
203 |           index: i,
204 |           data: `test-data-${i}`,
205 |           timestamp: new Date().toISOString(),
206 |         }));
207 | 
208 |         // Measure creation time
209 |         const { metrics: createMetrics } = await measurePerformance(
210 |           async () => {
211 |             const promises = testData.map((data) =>
212 |               memoryManager.remember("analysis", data),
213 |             );
214 |             return await Promise.all(promises);
215 |           },
216 |         );
217 | 
218 |         // Measure search time
219 |         const { metrics: searchMetrics } = await measurePerformance(
220 |           async () => {
221 |             return await memoryManager.search({
222 |               projectId: "scalability-test",
223 |             });
224 |           },
225 |         );
226 | 
227 |         results.push({
228 |           size,
229 |           createTime: createMetrics.operationTime,
230 |           searchTime: searchMetrics.operationTime,
231 |         });
232 |       }
233 | 
234 |       // Verify roughly linear scaling (allow for some variance)
235 |       for (let i = 1; i < results.length; i++) {
236 |         const prev = results[i - 1];
237 |         const curr = results[i];
238 | 
239 |         const sizeRatio = curr.size / prev.size;
240 |         const createTimeRatio = curr.createTime / prev.createTime;
241 |         const searchTimeRatio = curr.searchTime / prev.searchTime;
242 | 
243 |         // Create time should scale roughly linearly (within 3x of size ratio)
244 |         expect(createTimeRatio).toBeLessThan(sizeRatio * 3);
245 | 
246 |         // Search time should not degrade too badly (within 2x of size ratio)
247 |         expect(searchTimeRatio).toBeLessThan(sizeRatio * 2);
248 |       }
249 | 
250 |       console.log("Scalability Results:");
251 |       results.forEach((result) => {
252 |         console.log(
253 |           `Size ${result.size}: Create ${result.createTime.toFixed(
254 |             2,
255 |           )}ms, Search ${result.searchTime.toFixed(2)}ms`,
256 |         );
257 |       });
258 |     });
259 | 
260 |     test("should handle large individual memories efficiently", async () => {
261 |       memoryManager.setContext({ projectId: "large-memory-test" });
262 | 
263 |       const sizes = [
264 |         { name: "small", data: "x".repeat(1000) }, // 1KB
265 |         { name: "medium", data: "x".repeat(10000) }, // 10KB
266 |         { name: "large", data: "x".repeat(100000) }, // 100KB
267 |         { name: "xlarge", data: "x".repeat(1000000) }, // 1MB
268 |       ];
269 | 
270 |       const results: Array<{
271 |         name: string;
272 |         createTime: number;
273 |         readTime: number;
274 |       }> = [];
275 | 
276 |       for (const size of sizes) {
277 |         const testData = {
278 |           projectId: "large-memory-test",
279 |           size: size.name,
280 |           content: size.data,
281 |           metadata: { size: size.data.length },
282 |         };
283 | 
284 |         // Measure creation time
285 |         const { result: memory, metrics: createMetrics } =
286 |           await measurePerformance(async () => {
287 |             return await memoryManager.remember("analysis", testData);
288 |           });
289 | 
290 |         // Measure read time
291 |         const { metrics: readMetrics } = await measurePerformance(async () => {
292 |           return await memoryManager.recall(memory.id);
293 |         });
294 | 
295 |         results.push({
296 |           name: size.name,
297 |           createTime: createMetrics.operationTime,
298 |           readTime: readMetrics.operationTime,
299 |         });
300 | 
301 |         // Large memories should still be handled within reasonable time
302 |         expect(createMetrics.operationTime).toBeLessThan(5000); // 5 seconds
303 |         expect(readMetrics.operationTime).toBeLessThan(1000); // 1 second
304 |       }
305 | 
306 |       console.log("Large Memory Performance:");
307 |       results.forEach((result) => {
308 |         console.log(
309 |           `${result.name}: Create ${result.createTime.toFixed(
310 |             2,
311 |           )}ms, Read ${result.readTime.toFixed(2)}ms`,
312 |         );
313 |       });
314 |     });
315 |   });
316 | 
317 |   describe("Concurrent Operations Performance", () => {
318 |     test("should handle concurrent read/write operations", async () => {
319 |       memoryManager.setContext({ projectId: "concurrent-test" });
320 | 
321 |       // Pre-populate with some data
322 |       const initialData = Array.from({ length: 50 }, (_, i) => ({
323 |         projectId: "concurrent-test",
324 |         index: i,
325 |         data: `initial-data-${i}`,
326 |       }));
327 | 
328 |       const initialMemories = await Promise.all(
329 |         initialData.map((data) => memoryManager.remember("analysis", data)),
330 |       );
331 | 
332 |       const concurrentOperations = 20;
333 | 
334 |       const { metrics: concurrentMetrics } = await measurePerformance(
335 |         async () => {
336 |           const operations = Array.from(
337 |             { length: concurrentOperations },
338 |             async (_, i) => {
339 |               if (i % 3 === 0) {
340 |                 // Create new memory
341 |                 return await memoryManager.remember("analysis", {
342 |                   projectId: "concurrent-test",
343 |                   index: 100 + i,
344 |                   data: `concurrent-data-${i}`,
345 |                 });
346 |               } else if (i % 3 === 1) {
347 |                 // Read existing memory
348 |                 const randomMemory =
349 |                   initialMemories[
350 |                     Math.floor(Math.random() * initialMemories.length)
351 |                   ];
352 |                 return await memoryManager.recall(randomMemory.id);
353 |               } else {
354 |                 // Search memories
355 |                 return await memoryManager.search({
356 |                   projectId: "concurrent-test",
357 |                 });
358 |               }
359 |             },
360 |           );
361 | 
362 |           return await Promise.all(operations);
363 |         },
364 |       );
365 | 
366 |       expect(concurrentMetrics.operationTime).toBeLessThan(3000); // 3 seconds for 20 concurrent ops
367 | 
368 |       const throughput =
369 |         concurrentOperations / (concurrentMetrics.operationTime / 1000);
370 |       expect(throughput).toBeGreaterThan(5); // At least 5 concurrent ops/sec
371 | 
372 |       console.log("Concurrent Operations Performance:");
373 |       console.log(
374 |         `${concurrentOperations} concurrent ops: ${concurrentMetrics.operationTime.toFixed(
375 |           2,
376 |         )}ms (${throughput.toFixed(2)} ops/sec)`,
377 |       );
378 |     });
379 | 
380 |     test("should maintain performance under sustained load", async () => {
381 |       memoryManager.setContext({ projectId: "sustained-load-test" });
382 | 
383 |       const testDuration = 3000; // 3 seconds
384 |       const operationInterval = 100; // Every 100ms
385 |       const results: number[] = [];
386 | 
387 |       const startTime = Date.now();
388 |       let operationCount = 0;
389 | 
390 |       while (Date.now() - startTime < testDuration) {
391 |         const { metrics } = await measurePerformance(async () => {
392 |           return await memoryManager.remember("analysis", {
393 |             projectId: "sustained-load-test",
394 |             index: operationCount++,
395 |             timestamp: Date.now(),
396 |             data: `sustained-load-data-${operationCount}`,
397 |           });
398 |         });
399 | 
400 |         results.push(metrics.operationTime);
401 | 
402 |         // Wait for next interval
403 |         await new Promise((resolve) => setTimeout(resolve, operationInterval));
404 |       }
405 | 
406 |       const avgTime =
407 |         results.reduce((sum, time) => sum + time, 0) / results.length;
408 |       const maxTime = Math.max(...results);
409 |       const minTime = Math.min(...results);
410 | 
411 |       // Performance should remain consistent under sustained load
412 |       expect(avgTime).toBeLessThan(200); // Average operation time < 200ms
413 |       expect(maxTime).toBeLessThan(1000); // No single operation > 1 second
414 | 
415 |       // Performance degradation should be minimal
416 |       const firstHalf = results.slice(0, Math.floor(results.length / 2));
417 |       const secondHalf = results.slice(Math.floor(results.length / 2));
418 | 
419 |       const firstHalfAvg =
420 |         firstHalf.reduce((sum, time) => sum + time, 0) / firstHalf.length;
421 |       const secondHalfAvg =
422 |         secondHalf.reduce((sum, time) => sum + time, 0) / secondHalf.length;
423 | 
424 |       const degradation = secondHalfAvg / firstHalfAvg;
425 |       expect(degradation).toBeLessThan(2); // Less than 2x degradation
426 | 
427 |       console.log("Sustained Load Performance:");
428 |       console.log(
429 |         `Operations: ${results.length}, Avg: ${avgTime.toFixed(
430 |           2,
431 |         )}ms, Min: ${minTime.toFixed(2)}ms, Max: ${maxTime.toFixed(2)}ms`,
432 |       );
433 |       console.log(
434 |         `Performance degradation: ${((degradation - 1) * 100).toFixed(1)}%`,
435 |       );
436 |     });
437 |   });
438 | 
439 |   describe("Memory Resource Usage", () => {
440 |     test("should manage memory usage efficiently", async () => {
441 |       memoryManager.setContext({ projectId: "memory-usage-test" });
442 | 
443 |       const initialMemory = process.memoryUsage();
444 |       const memorySnapshots: Array<{ count: number; heapUsed: number }> = [];
445 | 
446 |       // Add memories in batches and monitor memory usage
447 |       for (let batch = 0; batch < 10; batch++) {
448 |         const batchSize = 100;
449 |         const batchData = Array.from({ length: batchSize }, (_, i) => ({
450 |           projectId: "memory-usage-test",
451 |           batch,
452 |           index: i,
453 |           data: "x".repeat(1000), // 1KB per memory
454 |           timestamp: new Date().toISOString(),
455 |         }));
456 | 
457 |         await Promise.all(
458 |           batchData.map((data) => memoryManager.remember("analysis", data)),
459 |         );
460 | 
461 |         const currentMemory = process.memoryUsage();
462 |         memorySnapshots.push({
463 |           count: (batch + 1) * batchSize,
464 |           heapUsed: currentMemory.heapUsed - initialMemory.heapUsed,
465 |         });
466 | 
467 |         // Force garbage collection if available
468 |         if (global.gc) {
469 |           global.gc();
470 |         }
471 |       }
472 | 
473 |       // Memory usage should be reasonable
474 |       const finalMemoryUsage = memorySnapshots[memorySnapshots.length - 1];
475 |       const memoryPerItem = finalMemoryUsage.heapUsed / finalMemoryUsage.count;
476 | 
477 |       expect(memoryPerItem).toBeLessThan(50 * 1024); // Less than 50KB per memory item (including overhead)
478 |       expect(finalMemoryUsage.heapUsed).toBeLessThan(100 * 1024 * 1024); // Less than 100MB total
479 | 
480 |       console.log("Memory Usage Analysis:");
481 |       console.log(
482 |         `Total items: ${finalMemoryUsage.count}, Total memory: ${(
483 |           finalMemoryUsage.heapUsed /
484 |           1024 /
485 |           1024
486 |         ).toFixed(2)}MB`,
487 |       );
488 |       console.log(`Memory per item: ${(memoryPerItem / 1024).toFixed(2)}KB`);
489 |     });
490 | 
491 |     test("should not leak memory on cleanup operations", async () => {
492 |       memoryManager.setContext({ projectId: "memory-leak-test" });
493 | 
494 |       const initialMemory = process.memoryUsage();
495 | 
496 |       // Create and delete memories multiple times
497 |       for (let cycle = 0; cycle < 5; cycle++) {
498 |         const memories = [];
499 | 
500 |         // Create memories
501 |         for (let i = 0; i < 100; i++) {
502 |           const memory = await memoryManager.remember("analysis", {
503 |             projectId: "memory-leak-test",
504 |             cycle,
505 |             index: i,
506 |             data: "x".repeat(1000),
507 |           });
508 |           memories.push(memory);
509 |         }
510 | 
511 |         // Delete all memories
512 |         for (const memory of memories) {
513 |           await memoryManager.forget(memory.id);
514 |         }
515 | 
516 |         // Force garbage collection
517 |         if (global.gc) {
518 |           global.gc();
519 |         }
520 |       }
521 | 
522 |       const finalMemory = process.memoryUsage();
523 |       const memoryDifference = finalMemory.heapUsed - initialMemory.heapUsed;
524 | 
525 |       // Memory usage should return close to initial levels
526 |       expect(memoryDifference).toBeLessThan(15 * 1024 * 1024); // Less than 15MB difference
527 | 
528 |       console.log("Memory Leak Test:");
529 |       console.log(
530 |         `Memory difference: ${(memoryDifference / 1024 / 1024).toFixed(2)}MB`,
531 |       );
532 |     });
533 |   });
534 | 
535 |   describe("Enhanced Components Performance", () => {
536 |     test("should benchmark enhanced memory manager performance", async () => {
537 |       const enhancedTempDir = path.join(tempDir, "enhanced");
538 |       await fs.mkdir(enhancedTempDir, { recursive: true });
539 | 
540 |       const enhancedManager = new EnhancedMemoryManager(enhancedTempDir);
541 |       await enhancedManager.initialize();
542 | 
543 |       enhancedManager.setContext({ projectId: "enhanced-performance" });
544 | 
545 |       const projectFeatures: import("../../src/memory/learning.js").ProjectFeatures =
546 |         {
547 |           language: "typescript",
548 |           framework: "react",
549 |           size: "medium",
550 |           complexity: "moderate",
551 |           hasTests: true,
552 |           hasCI: true,
553 |           hasDocs: true,
554 |           isOpenSource: true,
555 |         };
556 | 
557 |       const baseRecommendation = {
558 |         recommended: "docusaurus",
559 |         confidence: 0.8,
560 |         score: 0.85,
561 |       };
562 | 
563 |       // Benchmark enhanced recommendation
564 |       const { metrics: enhancedMetrics } = await measurePerformance(
565 |         async () => {
566 |           return await enhancedManager.getEnhancedRecommendation(
567 |             "/test/enhanced-performance",
568 |             baseRecommendation,
569 |             projectFeatures,
570 |           );
571 |         },
572 |       );
573 | 
574 |       expect(enhancedMetrics.operationTime).toBeLessThan(5000); // 5 seconds
575 | 
576 |       // Benchmark intelligent analysis
577 |       const analysisData = {
578 |         language: "typescript",
579 |         framework: "react",
580 |         size: "medium",
581 |         hasTests: true,
582 |         hasCI: true,
583 |       };
584 | 
585 |       const { metrics: analysisMetrics } = await measurePerformance(
586 |         async () => {
587 |           return await enhancedManager.getIntelligentAnalysis(
588 |             "/test/enhanced-performance",
589 |             analysisData,
590 |           );
591 |         },
592 |       );
593 | 
594 |       expect(analysisMetrics.operationTime).toBeLessThan(3000); // 3 seconds
595 | 
596 |       console.log("Enhanced Components Performance:");
597 |       console.log(
598 |         `Enhanced recommendation: ${enhancedMetrics.operationTime.toFixed(
599 |           2,
600 |         )}ms`,
601 |       );
602 |       console.log(
603 |         `Intelligent analysis: ${analysisMetrics.operationTime.toFixed(2)}ms`,
604 |       );
605 |     });
606 | 
607 |     test("should benchmark learning system performance", async () => {
608 |       const learningTempDir = path.join(tempDir, "learning");
609 |       await fs.mkdir(learningTempDir, { recursive: true });
610 | 
611 |       const tempLearningManager = new MemoryManager(learningTempDir);
612 |       await tempLearningManager.initialize();
613 |       const learningSystem = new IncrementalLearningSystem(tempLearningManager);
614 |       await learningSystem.initialize();
615 | 
616 |       const projectFeatures: import("../../src/memory/learning.js").ProjectFeatures =
617 |         {
618 |           language: "python",
619 |           framework: "django",
620 |           size: "large",
621 |           complexity: "complex",
622 |           hasTests: true,
623 |           hasCI: true,
624 |           hasDocs: false,
625 |           isOpenSource: true,
626 |         };
627 | 
628 |       const baseRecommendation = {
629 |         recommended: "sphinx",
630 |         confidence: 0.7,
631 |       };
632 | 
633 |       // Add training data through memory manager (learning system learns from stored memories)
634 |       for (let i = 0; i < 50; i++) {
635 |         await tempLearningManager.remember("analysis", {
636 |           ...projectFeatures,
637 |           index: i,
638 |           feedback: {
639 |             rating: 3 + (i % 3),
640 |             helpful: i % 2 === 0,
641 |             comments: `Training feedback ${i}`,
642 |           },
643 |         });
644 |       }
645 | 
646 |       // Benchmark improved recommendation
647 |       const { metrics: improveMetrics } = await measurePerformance(async () => {
648 |         return await learningSystem.getImprovedRecommendation(
649 |           projectFeatures,
650 |           baseRecommendation,
651 |         );
652 |       });
653 | 
654 |       expect(improveMetrics.operationTime).toBeLessThan(1000); // 1 second
655 | 
656 |       // Benchmark pattern detection
657 |       const { metrics: patternMetrics } = await measurePerformance(async () => {
658 |         return await learningSystem.getPatterns();
659 |       });
660 | 
661 |       expect(patternMetrics.operationTime).toBeLessThan(2000); // 2 seconds
662 | 
663 |       console.log("Learning System Performance:");
664 |       console.log(
665 |         `Improved recommendation: ${improveMetrics.operationTime.toFixed(2)}ms`,
666 |       );
667 |       console.log(
668 |         `Pattern detection: ${patternMetrics.operationTime.toFixed(2)}ms`,
669 |       );
670 |     });
671 | 
672 |     test("should benchmark knowledge graph performance", async () => {
673 |       const graphTempDir = path.join(tempDir, "graph");
674 |       await fs.mkdir(graphTempDir, { recursive: true });
675 | 
676 |       const tempGraphManager = new MemoryManager(graphTempDir);
677 |       await tempGraphManager.initialize();
678 |       const knowledgeGraph = new KnowledgeGraph(tempGraphManager);
679 |       await knowledgeGraph.initialize();
680 | 
681 |       // Add nodes and edges
682 |       const nodeCount = 100;
683 |       const edgeCount = 200;
684 | 
685 |       const { metrics: buildMetrics } = await measurePerformance(async () => {
686 |         // Add nodes
687 |         for (let i = 0; i < nodeCount; i++) {
688 |           knowledgeGraph.addNode({
689 |             id: `node-${i}`,
690 |             type:
691 |               i % 3 === 0 ? "project" : i % 3 === 1 ? "technology" : "pattern",
692 |             label: `Node ${i}`,
693 |             weight: 1.0,
694 |             properties: {
695 |               name: `Node ${i}`,
696 |               category: i % 5 === 0 ? "frontend" : "backend",
697 |             },
698 |           });
699 |         }
700 | 
701 |         // Add edges
702 |         for (let i = 0; i < edgeCount; i++) {
703 |           const sourceId = `node-${Math.floor(Math.random() * nodeCount)}`;
704 |           const targetId = `node-${Math.floor(Math.random() * nodeCount)}`;
705 | 
706 |           if (sourceId !== targetId) {
707 |             knowledgeGraph.addEdge({
708 |               source: sourceId,
709 |               target: targetId,
710 |               type: i % 2 === 0 ? "uses" : "similar_to",
711 |               weight: Math.random(),
712 |               properties: {},
713 |               confidence: Math.random(),
714 |             });
715 |           }
716 |         }
717 |       });
718 | 
719 |       expect(buildMetrics.operationTime).toBeLessThan(5000); // 5 seconds to build graph
720 | 
721 |       // Benchmark pathfinding
722 |       const { metrics: pathMetrics } = await measurePerformance(async () => {
723 |         return knowledgeGraph.findPath("node-0", "node-50");
724 |       });
725 | 
726 |       expect(pathMetrics.operationTime).toBeLessThan(500); // 500ms for pathfinding
727 | 
728 |       // Benchmark node queries (using available methods)
729 |       const { metrics: queryMetrics } = await measurePerformance(async () => {
730 |         return knowledgeGraph.getAllNodes();
731 |       });
732 | 
733 |       expect(queryMetrics.operationTime).toBeLessThan(1000); // 1 second for node queries
734 | 
735 |       console.log("Knowledge Graph Performance:");
736 |       console.log(
737 |         `Build graph (${nodeCount} nodes, ${edgeCount} edges): ${buildMetrics.operationTime.toFixed(
738 |           2,
739 |         )}ms`,
740 |       );
741 |       console.log(`Find path: ${pathMetrics.operationTime.toFixed(2)}ms`);
742 |       console.log(`Nodes query: ${queryMetrics.operationTime.toFixed(2)}ms`);
743 |     });
744 |   });
745 | 
746 |   describe("Integration Performance", () => {
747 |     test("should benchmark MCP integration performance", async () => {
748 |       // Test memory integration functions
749 |       const analysisData = {
750 |         projectId: "integration-performance",
751 |         language: { primary: "go" },
752 |         framework: { name: "gin" },
753 |         stats: { files: 200, lines: 50000 },
754 |       };
755 | 
756 |       const { metrics: analysisMetrics } = await measurePerformance(
757 |         async () => {
758 |           return await rememberAnalysis(
759 |             "/test/integration-performance",
760 |             analysisData,
761 |           );
762 |         },
763 |       );
764 | 
765 |       const { metrics: recommendationMetrics } = await measurePerformance(
766 |         async () => {
767 |           return await rememberRecommendation("analysis-id", {
768 |             recommended: "hugo",
769 |             confidence: 0.9,
770 |           });
771 |         },
772 |       );
773 | 
774 |       const { metrics: similarMetrics } = await measurePerformance(async () => {
775 |         return await getSimilarProjects(analysisData, 5);
776 |       });
777 | 
778 |       expect(analysisMetrics.operationTime).toBeLessThan(1000); // 1 second
779 |       expect(recommendationMetrics.operationTime).toBeLessThan(1000); // 1 second
780 |       expect(similarMetrics.operationTime).toBeLessThan(2000); // 2 seconds
781 | 
782 |       console.log("MCP Integration Performance:");
783 |       console.log(
784 |         `Remember analysis: ${analysisMetrics.operationTime.toFixed(2)}ms`,
785 |       );
786 |       console.log(
787 |         `Remember recommendation: ${recommendationMetrics.operationTime.toFixed(
788 |           2,
789 |         )}ms`,
790 |       );
791 |       console.log(
792 |         `Get similar projects: ${similarMetrics.operationTime.toFixed(2)}ms`,
793 |       );
794 |     });
795 |   });
796 | });
797 | 
```

--------------------------------------------------------------------------------
/docs/adrs/adr-0006-mcp-tools-api-design.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | id: adr-6-mcp-tools-api-design
  3 | title: "ADR-006: MCP Tools API Design and Interface Specification"
  4 | sidebar_label: "ADR-006: MCP Tools API Design"
  5 | sidebar_position: 6
  6 | documcp:
  7 |   last_updated: "2025-01-14T00:00:00.000Z"
  8 |   last_validated: "2025-01-14T00:00:00.000Z"
  9 |   auto_updated: false
 10 |   update_frequency: monthly
 11 |   validated_against_commit: 210a274
 12 | ---
 13 | 
 14 | # ADR-006: MCP Tools API Design and Interface Specification
 15 | 
 16 | ## Status
 17 | 
 18 | Accepted
 19 | 
 20 | ## Context
 21 | 
 22 | DocuMCP must expose its functionality through a carefully designed set of MCP tools that provide comprehensive coverage of the documentation deployment workflow while maintaining clear separation of concerns, appropriate granularity, and excellent developer experience for MCP-enabled clients.
 23 | 
 24 | The MCP Tools API serves as the primary interface between DocuMCP's intelligence and client applications like GitHub Copilot, Claude Desktop, and other MCP-enabled development environments. This API must balance several competing concerns:
 25 | 
 26 | **Functional Requirements:**
 27 | 
 28 | - Comprehensive repository analysis capabilities
 29 | - Intelligent SSG recommendation with detailed justifications
 30 | - Automated configuration generation for multiple SSGs
 31 | - Diataxis-compliant documentation structure creation
 32 | - GitHub Pages deployment workflow generation
 33 | - Git integration for seamless deployment
 34 | 
 35 | **Usability Requirements:**
 36 | 
 37 | - Intuitive tool names and parameter structures
 38 | - Comprehensive input validation with clear error messages
 39 | - Consistent response formats across all tools
 40 | - Rich metadata for client presentation and user guidance
 41 | - Progressive disclosure of complexity (simple to advanced use cases)
 42 | 
 43 | **Technical Requirements:**
 44 | 
 45 | - Full MCP specification compliance
 46 | - Robust error handling and recovery
 47 | - Efficient parameter validation and sanitization
 48 | - Scalable architecture supporting complex multi-step workflows
 49 | - Extensible design for future functionality additions
 50 | 
 51 | ## Decision
 52 | 
 53 | We will implement a comprehensive MCP Tools API consisting of six core tools that cover the complete documentation deployment workflow, with additional utility tools for advanced scenarios and troubleshooting.
 54 | 
 55 | ### Core MCP Tools Architecture:
 56 | 
 57 | #### 1. Repository Analysis Tool (`analyzeRepository`)
 58 | 
 59 | **Purpose**: Comprehensive repository analysis and project characterization
 60 | **Scope**: Deep analysis of project structure, language ecosystems, existing documentation, and complexity assessment
 61 | 
 62 | #### 2. SSG Recommendation Tool (`recommendSSG`)
 63 | 
 64 | **Purpose**: Intelligent static site generator recommendation with detailed justifications
 65 | **Scope**: Multi-criteria decision analysis with confidence scoring and alternative options
 66 | 
 67 | #### 3. Configuration Generation Tool (`generateConfiguration`)
 68 | 
 69 | **Purpose**: Create customized SSG configuration files and directory structures
 70 | **Scope**: Template-based generation with project-specific customizations and validation
 71 | 
 72 | #### 4. Diataxis Structure Tool (`createDiataxisStructure`)
 73 | 
 74 | **Purpose**: Generate comprehensive Diataxis-compliant documentation frameworks
 75 | **Scope**: Information architecture generation with content planning and navigation design
 76 | 
 77 | #### 5. Deployment Workflow Tool (`generateWorkflow`)
 78 | 
 79 | **Purpose**: Create optimized GitHub Actions workflows for automated deployment
 80 | **Scope**: SSG-specific workflow generation with security best practices and performance optimization
 81 | 
 82 | #### 6. Git Integration Tool (`generateGitCommands`)
 83 | 
 84 | **Purpose**: Provide ready-to-execute Git commands for deployment and maintenance
 85 | **Scope**: Context-aware command generation with branch management and deployment verification
 86 | 
 87 | ### Supporting Tools:
 88 | 
 89 | - `validateConfiguration`: Validate generated configurations and identify issues
 90 | - `troubleshootDeployment`: Analyze deployment failures and provide remediation guidance
 91 | - `optimizePerformance`: Analyze and optimize existing documentation site performance
 92 | - `migrateDocumentation`: Assist with migration between different SSGs or frameworks
 93 | - `cleanupAgentArtifacts`: Detect, classify, and clean up artifacts generated by AI coding agents (Phase 3)
 94 | 
 95 | ## Alternatives Considered
 96 | 
 97 | ### Monolithic Single Tool Approach
 98 | 
 99 | - **Pros**: Simpler API surface, single entry point, easier client integration
100 | - **Cons**: Complex parameter structures, poor separation of concerns, difficult error handling
101 | - **Decision**: Rejected due to poor usability and maintainability
102 | 
103 | ### Micro-Tool Architecture (15+ Small Tools)
104 | 
105 | - **Pros**: Maximum granularity, precise control, composable workflows
106 | - **Cons**: Complex orchestration, cognitive overhead, fragmented user experience
107 | - **Decision**: Rejected due to complexity and poor user experience
108 | 
109 | ### Stateful Session-Based API
110 | 
111 | - **Pros**: Could maintain context across tool calls, simplified parameter passing
112 | - **Cons**: Session management complexity, state synchronization issues, harder client integration
113 | - **Decision**: Rejected to maintain MCP stateless principles
114 | 
115 | ### External API Integration (REST/GraphQL)
116 | 
117 | - **Pros**: Standard web technologies, extensive tooling ecosystem
118 | - **Cons**: Not MCP-compliant, additional infrastructure requirements, authentication complexity
119 | - **Decision**: Rejected due to MCP specification requirements
120 | 
121 | ## Consequences
122 | 
123 | ### Positive
124 | 
125 | - **Clear Separation of Concerns**: Each tool has well-defined responsibility and scope
126 | - **Progressive Complexity**: Users can start simple and add sophistication as needed
127 | - **Excellent Error Handling**: Tool-specific validation and error reporting
128 | - **Client-Friendly**: Rich metadata and consistent response formats enhance client UX
129 | - **Extensible Architecture**: Easy to add new tools without breaking existing functionality
130 | 
131 | ### Negative
132 | 
133 | - **API Surface Complexity**: Six core tools plus supporting tools require comprehensive documentation
134 | - **Inter-Tool Coordination**: Some workflows require multiple tool calls with parameter passing
135 | - **Validation Overhead**: Each tool requires comprehensive input validation and error handling
136 | 
137 | ### Risks and Mitigations
138 | 
139 | - **API Complexity**: Provide comprehensive documentation and usage examples
140 | - **Parameter Evolution**: Use versioned schemas with backward compatibility
141 | - **Client Integration**: Offer reference implementations and integration guides
142 | 
143 | ## Implementation Details
144 | 
145 | ### Tool Parameter Schemas
146 | 
147 | ```typescript
148 | // Core tool parameter interfaces
149 | interface AnalyzeRepositoryParams {
150 |   repositoryPath: string;
151 |   analysisDepth?: "basic" | "comprehensive" | "deep";
152 |   focusAreas?: ("structure" | "languages" | "documentation" | "complexity")[];
153 |   excludePatterns?: string[];
154 | }
155 | 
156 | interface RecommendSSGParams {
157 |   projectAnalysis: ProjectAnalysis;
158 |   teamCapabilities?: TeamCapabilities;
159 |   performanceRequirements?: PerformanceRequirements;
160 |   customizationNeeds?: CustomizationNeeds;
161 |   existingConstraints?: ProjectConstraints;
162 | }
163 | 
164 | interface GenerateConfigurationParams {
165 |   selectedSSG: SSGType;
166 |   projectAnalysis: ProjectAnalysis;
167 |   customizations?: SSGCustomizations;
168 |   deploymentTarget?: DeploymentTarget;
169 |   advancedOptions?: AdvancedConfigOptions;
170 | }
171 | 
172 | interface CreateDiataxisStructureParams {
173 |   selectedSSG: SSGType;
174 |   projectType: ProjectType;
175 |   existingContent?: ExistingContentAnalysis;
176 |   contentComplexity?: "minimal" | "standard" | "comprehensive";
177 |   navigationPreferences?: NavigationPreferences;
178 | }
179 | 
180 | interface GenerateWorkflowParams {
181 |   ssgType: SSGType;
182 |   deploymentStrategy: "github-actions" | "branch-based" | "hybrid";
183 |   securityRequirements?: SecurityRequirements;
184 |   performanceOptimizations?: PerformanceOptions;
185 |   environmentConfiguration?: EnvironmentConfig;
186 | }
187 | 
188 | interface GenerateGitCommandsParams {
189 |   deploymentStrategy: DeploymentStrategy;
190 |   repositoryState: RepositoryState;
191 |   branchConfiguration: BranchConfiguration;
192 |   commitPreferences?: CommitPreferences;
193 | }
194 | ```
195 | 
196 | ### Response Format Standardization
197 | 
198 | ```typescript
199 | // Standardized response structure for all tools
200 | interface MCPToolResponse<T> {
201 |   success: boolean;
202 |   data?: T;
203 |   error?: ErrorDetails;
204 |   metadata: ResponseMetadata;
205 |   recommendations?: Recommendation[];
206 |   nextSteps?: NextStep[];
207 | }
208 | 
209 | interface ResponseMetadata {
210 |   toolVersion: string;
211 |   executionTime: number;
212 |   confidenceScore?: number;
213 |   analysisDepth: string;
214 |   timestamp: string;
215 |   correlationId: string;
216 | }
217 | 
218 | interface ErrorDetails {
219 |   code: string;
220 |   message: string;
221 |   details: string;
222 |   resolution?: string;
223 |   documentation?: string;
224 | }
225 | 
226 | interface Recommendation {
227 |   type: "optimization" | "alternative" | "enhancement";
228 |   priority: "low" | "medium" | "high";
229 |   description: string;
230 |   implementation?: string;
231 |   resources?: string[];
232 | }
233 | 
234 | interface NextStep {
235 |   action: string;
236 |   description: string;
237 |   toolRequired?: string;
238 |   parameters?: Record<string, any>;
239 |   estimated_time?: string;
240 | }
241 | ```
242 | 
243 | ### analyzeRepository Tool Implementation
244 | 
245 | ```typescript
246 | const analyzeRepositoryTool: MCPTool = {
247 |   name: "analyzeRepository",
248 |   description: "Comprehensive repository analysis for documentation planning",
249 |   inputSchema: {
250 |     type: "object",
251 |     properties: {
252 |       repositoryPath: {
253 |         type: "string",
254 |         description: "Path to the repository to analyze",
255 |       },
256 |       analysisDepth: {
257 |         type: "string",
258 |         enum: ["basic", "comprehensive", "deep"],
259 |         default: "comprehensive",
260 |         description: "Depth of analysis to perform",
261 |       },
262 |       focusAreas: {
263 |         type: "array",
264 |         items: {
265 |           type: "string",
266 |           enum: ["structure", "languages", "documentation", "complexity"],
267 |         },
268 |         description: "Specific areas to focus analysis on",
269 |       },
270 |       excludePatterns: {
271 |         type: "array",
272 |         items: { type: "string" },
273 |         description: "File patterns to exclude from analysis",
274 |       },
275 |     },
276 |     required: ["repositoryPath"],
277 |   },
278 | };
279 | 
280 | async function handleAnalyzeRepository(
281 |   params: AnalyzeRepositoryParams,
282 | ): Promise<MCPToolResponse<RepositoryAnalysis>> {
283 |   try {
284 |     const analysis = await repositoryAnalyzer.analyze(params);
285 | 
286 |     return {
287 |       success: true,
288 |       data: analysis,
289 |       metadata: {
290 |         toolVersion: "1.0.0",
291 |         executionTime: analysis.executionTime,
292 |         analysisDepth: params.analysisDepth || "comprehensive",
293 |         timestamp: new Date().toISOString(),
294 |         correlationId: generateCorrelationId(),
295 |       },
296 |       recommendations: generateAnalysisRecommendations(analysis),
297 |       nextSteps: [
298 |         {
299 |           action: "Get SSG Recommendation",
300 |           description:
301 |             "Use analysis results to get intelligent SSG recommendations",
302 |           toolRequired: "recommendSSG",
303 |           parameters: { projectAnalysis: analysis },
304 |           estimated_time: "< 1 minute",
305 |         },
306 |       ],
307 |     };
308 |   } catch (error) {
309 |     return {
310 |       success: false,
311 |       error: {
312 |         code: "ANALYSIS_FAILED",
313 |         message: "Repository analysis failed",
314 |         details: error.message,
315 |         resolution: "Verify repository path and permissions",
316 |         documentation: "https://documcp.dev/troubleshooting#analysis-errors",
317 |       },
318 |       metadata: {
319 |         toolVersion: "1.0.0",
320 |         executionTime: 0,
321 |         analysisDepth: params.analysisDepth || "comprehensive",
322 |         timestamp: new Date().toISOString(),
323 |         correlationId: generateCorrelationId(),
324 |       },
325 |     };
326 |   }
327 | }
328 | ```
329 | 
330 | ### recommendSSG Tool Implementation
331 | 
332 | ```typescript
333 | const recommendSSGTool: MCPTool = {
334 |   name: "recommendSSG",
335 |   description:
336 |     "Intelligent static site generator recommendation with detailed justifications",
337 |   inputSchema: {
338 |     type: "object",
339 |     properties: {
340 |       projectAnalysis: {
341 |         type: "object",
342 |         description: "Repository analysis results from analyzeRepository tool",
343 |       },
344 |       teamCapabilities: {
345 |         type: "object",
346 |         properties: {
347 |           technicalSkills: { type: "array", items: { type: "string" } },
348 |           maintenanceCapacity: {
349 |             type: "string",
350 |             enum: ["minimal", "moderate", "extensive"],
351 |           },
352 |           learningAppetite: { type: "string", enum: ["low", "medium", "high"] },
353 |         },
354 |       },
355 |       performanceRequirements: {
356 |         type: "object",
357 |         properties: {
358 |           buildTimeImportance: {
359 |             type: "string",
360 |             enum: ["low", "medium", "high"],
361 |           },
362 |           siteSpeedPriority: {
363 |             type: "string",
364 |             enum: ["standard", "fast", "ultra-fast"],
365 |           },
366 |           scalabilityNeeds: {
367 |             type: "string",
368 |             enum: ["small", "medium", "large", "enterprise"],
369 |           },
370 |         },
371 |       },
372 |     },
373 |     required: ["projectAnalysis"],
374 |   },
375 | };
376 | 
377 | async function handleRecommendSSG(
378 |   params: RecommendSSGParams,
379 | ): Promise<MCPToolResponse<SSGRecommendation>> {
380 |   try {
381 |     const recommendation = await ssgRecommendationEngine.analyze(params);
382 | 
383 |     return {
384 |       success: true,
385 |       data: recommendation,
386 |       metadata: {
387 |         toolVersion: "1.0.0",
388 |         executionTime: recommendation.analysisTime,
389 |         confidenceScore: recommendation.confidence,
390 |         analysisDepth: "comprehensive",
391 |         timestamp: new Date().toISOString(),
392 |         correlationId: generateCorrelationId(),
393 |       },
394 |       recommendations: [
395 |         {
396 |           type: "optimization",
397 |           priority: "medium",
398 |           description: "Consider performance optimization strategies",
399 |           implementation: "Review build caching and incremental build options",
400 |         },
401 |       ],
402 |       nextSteps: [
403 |         {
404 |           action: "Generate Configuration",
405 |           description: "Create customized configuration for recommended SSG",
406 |           toolRequired: "generateConfiguration",
407 |           parameters: {
408 |             selectedSSG: recommendation.primaryRecommendation.ssg,
409 |             projectAnalysis: params.projectAnalysis,
410 |           },
411 |           estimated_time: "2-3 minutes",
412 |         },
413 |       ],
414 |     };
415 |   } catch (error) {
416 |     console.error("SSG recommendation analysis failed:", error);
417 |     return {
418 |       success: false,
419 |       error: {
420 |         code: "SSG_RECOMMENDATION_FAILED",
421 |         message: `Failed to analyze SSG recommendations: ${
422 |           error instanceof Error ? error.message : "Unknown error"
423 |         }`,
424 |         resolution:
425 |           "Check project analysis data and retry with valid parameters",
426 |       },
427 |       metadata: {
428 |         toolVersion: "1.0.0",
429 |         timestamp: new Date().toISOString(),
430 |         correlationId: generateCorrelationId(),
431 |       },
432 |     };
433 |   }
434 | }
435 | ```
436 | 
437 | ### Input Validation System
438 | 
439 | ```typescript
440 | interface ValidationRule {
441 |   field: string;
442 |   validator: (value: any) => ValidationResult;
443 |   required: boolean;
444 |   errorMessage: string;
445 | }
446 | 
447 | class MCPToolValidator {
448 |   validateParameters<T>(params: T, schema: JSONSchema): ValidationResult {
449 |     const results = this.runSchemaValidation(params, schema);
450 |     const semanticResults = this.runSemanticValidation(params);
451 | 
452 |     return this.combineValidationResults(results, semanticResults);
453 |   }
454 | 
455 |   private runSemanticValidation(params: any): ValidationResult {
456 |     const issues: ValidationIssue[] = [];
457 | 
458 |     // Repository path validation
459 |     if (
460 |       params.repositoryPath &&
461 |       !this.isValidRepositoryPath(params.repositoryPath)
462 |     ) {
463 |       issues.push({
464 |         field: "repositoryPath",
465 |         message: "Repository path does not exist or is not accessible",
466 |         severity: "error",
467 |         resolution: "Verify the path exists and you have read permissions",
468 |       });
469 |     }
470 | 
471 |     // Cross-parameter validation
472 |     if (params.analysisDepth === "deep" && params.focusAreas?.length > 2) {
473 |       issues.push({
474 |         field: "analysisDepth",
475 |         message: "Deep analysis with multiple focus areas may be slow",
476 |         severity: "warning",
477 |         resolution:
478 |           "Consider using comprehensive analysis or fewer focus areas",
479 |       });
480 |     }
481 | 
482 |     return { valid: issues.length === 0, issues };
483 |   }
484 | }
485 | ```
486 | 
487 | ## Tool Orchestration Patterns
488 | 
489 | ### Sequential Workflow Pattern
490 | 
491 | ```typescript
492 | // Common workflow: Analysis → Recommendation → Configuration → Deployment
493 | class DocumentationWorkflow {
494 |   async executeCompleteWorkflow(
495 |     repositoryPath: string,
496 |   ): Promise<WorkflowResult> {
497 |     try {
498 |       // Step 1: Analyze repository
499 |       const analysisResult = await this.callTool("analyzeRepository", {
500 |         repositoryPath,
501 |       });
502 |       if (!analysisResult.success) {
503 |         throw new Error(`Analysis failed: ${analysisResult.error?.message}`);
504 |       }
505 | 
506 |       // Step 2: Get SSG recommendation
507 |       const recommendationResult = await this.callTool("recommendSSG", {
508 |         projectAnalysis: analysisResult.data,
509 |       });
510 |       if (!recommendationResult.success) {
511 |         throw new Error(
512 |           `Recommendation failed: ${recommendationResult.error?.message}`,
513 |         );
514 |       }
515 | 
516 |       // Step 3: Generate configuration
517 |       const configResult = await this.callTool("generateConfiguration", {
518 |         selectedSSG: recommendationResult.data.primaryRecommendation.ssg,
519 |         projectAnalysis: analysisResult.data,
520 |       });
521 |       if (!configResult.success) {
522 |         throw new Error(
523 |           `Configuration generation failed: ${configResult.error?.message}`,
524 |         );
525 |       }
526 | 
527 |       // Step 4: Create Diataxis structure
528 |       const structureResult = await this.callTool("createDiataxisStructure", {
529 |         selectedSSG: recommendationResult.data.primaryRecommendation.ssg,
530 |         projectType: analysisResult.data.projectType,
531 |       });
532 |       if (!structureResult.success) {
533 |         console.warn(
534 |           `Diataxis structure creation failed: ${structureResult.error?.message}`,
535 |         );
536 |       }
537 | 
538 |       // Step 5: Generate deployment workflow
539 |       const workflowResult = await this.callTool("generateWorkflow", {
540 |         ssgType: recommendationResult.data.primaryRecommendation.ssg,
541 |         deploymentStrategy: "github-actions",
542 |       });
543 |       if (!workflowResult.success) {
544 |         console.warn(
545 |           `Workflow generation failed: ${workflowResult.error?.message}`,
546 |         );
547 |       }
548 | 
549 |       return this.combineResults([
550 |         analysisResult,
551 |         recommendationResult,
552 |         configResult,
553 |         structureResult,
554 |         workflowResult,
555 |       ]);
556 |     } catch (error) {
557 |       throw new Error(`Complete workflow failed: ${error.message}`);
558 |     }
559 |   }
560 | }
561 | ```
562 | 
563 | ## Error Handling and Recovery
564 | 
565 | ### Comprehensive Error Classification
566 | 
567 | ```typescript
568 | enum ErrorCategory {
569 |   VALIDATION = "validation",
570 |   FILESYSTEM = "filesystem",
571 |   ANALYSIS = "analysis",
572 |   GENERATION = "generation",
573 |   CONFIGURATION = "configuration",
574 |   DEPLOYMENT = "deployment",
575 |   NETWORK = "network",
576 |   PERMISSION = "permission",
577 | }
578 | 
579 | interface ErrorContext {
580 |   tool: string;
581 |   operation: string;
582 |   parameters: Record<string, any>;
583 |   environment: EnvironmentInfo;
584 | }
585 | 
586 | class MCPErrorHandler {
587 |   handleError(error: Error, context: ErrorContext): MCPToolResponse<null> {
588 |     const classification = this.classifyError(error);
589 |     const resolution = this.generateResolution(classification, context);
590 | 
591 |     return {
592 |       success: false,
593 |       error: {
594 |         code: this.generateErrorCode(classification),
595 |         message: this.formatUserMessage(error, classification),
596 |         details: error.message,
597 |         resolution: resolution.guidance,
598 |         documentation: resolution.documentationUrl,
599 |       },
600 |       metadata: this.generateErrorMetadata(context),
601 |       nextSteps: resolution.suggestedActions,
602 |     };
603 |   }
604 | 
605 |   private generateResolution(
606 |     classification: ErrorClassification,
607 |     context: ErrorContext,
608 |   ): ErrorResolution {
609 |     switch (classification.category) {
610 |       case ErrorCategory.FILESYSTEM:
611 |         return {
612 |           guidance: "Verify file paths and permissions",
613 |           documentationUrl:
614 |             "https://documcp.dev/troubleshooting#filesystem-errors",
615 |           suggestedActions: [
616 |             {
617 |               action: "Check file exists",
618 |               description: `Verify ${context.parameters.repositoryPath} exists`,
619 |             },
620 |             {
621 |               action: "Check permissions",
622 |               description: "Ensure read access to repository directory",
623 |             },
624 |           ],
625 |         };
626 |       // ... other error categories
627 |     }
628 |   }
629 | }
630 | ```
631 | 
632 | ## Performance Optimization
633 | 
634 | ### Response Caching Strategy
635 | 
636 | ```typescript
637 | interface CacheConfiguration {
638 |   analyzeRepository: {
639 |     ttl: 300;
640 |     keyFields: ["repositoryPath", "analysisDepth"];
641 |   };
642 |   recommendSSG: { ttl: 3600; keyFields: ["projectAnalysis.signature"] };
643 |   generateConfiguration: {
644 |     ttl: 1800;
645 |     keyFields: ["selectedSSG", "projectAnalysis.signature"];
646 |   };
647 | }
648 | 
649 | class MCPToolCache {
650 |   async getCachedResponse<T>(
651 |     toolName: string,
652 |     parameters: any,
653 |   ): Promise<MCPToolResponse<T> | null> {
654 |     const cacheKey = this.generateCacheKey(toolName, parameters);
655 |     const cached = await this.cache.get(cacheKey);
656 | 
657 |     if (cached && !this.isExpired(cached)) {
658 |       return {
659 |         ...cached,
660 |         metadata: {
661 |           ...cached.metadata,
662 |           fromCache: true,
663 |           cacheAge: Date.now() - cached.metadata.timestamp,
664 |         },
665 |       };
666 |     }
667 | 
668 |     return null;
669 |   }
670 | }
671 | ```
672 | 
673 | ## Testing Strategy
674 | 
675 | ### Tool Testing Framework
676 | 
677 | ```typescript
678 | describe("MCP Tools API", () => {
679 |   describe("analyzeRepository", () => {
680 |     it("should analyze JavaScript project correctly");
681 |     it("should handle missing repository gracefully");
682 |     it("should respect analysis depth parameters");
683 |     it("should exclude specified patterns");
684 |   });
685 | 
686 |   describe("recommendSSG", () => {
687 |     it("should recommend Hugo for large documentation sites");
688 |     it("should recommend Jekyll for GitHub Pages simple sites");
689 |     it("should provide confidence scores for all recommendations");
690 |     it("should handle incomplete project analysis");
691 |   });
692 | 
693 |   describe("Tool Integration", () => {
694 |     it("should support complete workflow from analysis to deployment");
695 |     it("should maintain parameter consistency across tool calls");
696 |     it("should provide appropriate next steps guidance");
697 |   });
698 | });
699 | ```
700 | 
701 | ### Integration Testing
702 | 
703 | ```typescript
704 | class MCPToolIntegrationTests {
705 |   async testCompleteWorkflow(): Promise<void> {
706 |     const testRepo = await this.createTestRepository();
707 | 
708 |     // Test full workflow
709 |     const analysis = await this.callTool("analyzeRepository", {
710 |       repositoryPath: testRepo,
711 |     });
712 |     expect(analysis.success).toBe(true);
713 | 
714 |     const recommendation = await this.callTool("recommendSSG", {
715 |       projectAnalysis: analysis.data,
716 |     });
717 |     expect(recommendation.success).toBe(true);
718 |     expect(recommendation.data.primaryRecommendation).toBeDefined();
719 | 
720 |     const config = await this.callTool("generateConfiguration", {
721 |       selectedSSG: recommendation.data.primaryRecommendation.ssg,
722 |       projectAnalysis: analysis.data,
723 |     });
724 |     expect(config.success).toBe(true);
725 | 
726 |     // Validate generated configuration
727 |     await this.validateGeneratedFiles(config.data.files);
728 |   }
729 | }
730 | ```
731 | 
732 | ## Documentation and Examples
733 | 
734 | ### Tool Usage Examples
735 | 
736 | ```typescript
737 | // Example: Complete documentation setup workflow
738 | const examples = {
739 |   basicSetup: {
740 |     description: "Basic documentation setup for a JavaScript project",
741 |     steps: [
742 |       {
743 |         tool: "analyzeRepository",
744 |         parameters: { repositoryPath: "./my-project" },
745 |         expectedResult: "Project analysis with language ecosystem detection",
746 |       },
747 |       {
748 |         tool: "recommendSSG",
749 |         parameters: { projectAnalysis: "${analysis_result}" },
750 |         expectedResult: "SSG recommendation with justification",
751 |       },
752 |     ],
753 |   },
754 |   advancedSetup: {
755 |     description: "Advanced setup with custom requirements",
756 |     steps: [
757 |       // ... detailed workflow steps
758 |     ],
759 |   },
760 | };
761 | ```
762 | 
763 | ## Code Execution with MCP (CE-MCP) Compatibility (2025-12-09)
764 | 
765 | ### Tool Design Validation
766 | 
767 | **Research Findings**: Our tool API design is fully compatible with Code Mode clients:
768 | 
769 | 1. **Composable Tools**: Each tool is independent and can be orchestrated via generated code
770 | 2. **Clear Separation**: Well-defined responsibilities enable clean code generation
771 | 3. **Zod Validation**: Provides excellent type information for LLM code generation
772 | 
773 | ### Best Practices for Code Mode
774 | 
775 | **Tool Description Optimization**:
776 | 
777 | ```typescript
778 | // ✅ GOOD: Concise, focused descriptions
779 | {
780 |   name: "analyze_repository",
781 |   description: "Analyze project structure, languages, and documentation",
782 |   // ~60 tokens, clear purpose
783 | }
784 | 
785 | // ❌ AVOID: Verbose descriptions that bloat context
786 | {
787 |   name: "analyze_repository",
788 |   description: "This tool performs a comprehensive multi-layered analysis...",
789 |   // 200+ tokens, excessive detail
790 | }
791 | ```
792 | 
793 | **Tool Organization Metadata** (Optional Enhancement):
794 | 
795 | ```typescript
796 | interface ToolMetadata {
797 |   category: "analysis" | "generation" | "deployment" | "validation";
798 |   complexity: "simple" | "moderate" | "complex";
799 |   estimatedTokens: number;
800 |   codeModeFriendly: boolean;
801 | }
802 | ```
803 | 
804 | **Result Summarization for Large Outputs**:
805 | 
806 | ```typescript
807 | function formatResponse(data: AnalysisResult): MCPToolResponse {
808 |   if (data.size > 10_000) {
809 |     return {
810 |       summary: extractKeyMetrics(data),
811 |       resourceUri: storeAsResource(data), // Full data via MCP resource
812 |       nextSteps: [...] // Guidance without bloating context
813 |     };
814 |   }
815 |   return { data }; // Small results returned directly
816 | }
817 | ```
818 | 
819 | ### Performance Benefits in Code Mode
820 | 
821 | When documcp tools are orchestrated via Code Mode clients:
822 | 
823 | - **Token Efficiency**: Only needed tool definitions loaded (not all 25+)
824 | - **Parallel Execution**: Multiple tools run concurrently in sandbox
825 | - **Context Preservation**: Intermediate results stay in sandbox, not LLM context
826 | - **Cost Reduction**: ~75x savings on complex workflows
827 | 
828 | For detailed analysis, see [ADR-011: CE-MCP Compatibility](adr-0011-ce-mcp-compatibility.md).
829 | 
830 | ## Future Enhancements
831 | 
832 | ### Planned Tool Additions
833 | 
834 | - `analyzeExistingDocs`: Deep analysis of existing documentation quality and structure
835 | - `generateMigrationPlan`: Create migration plans between different documentation systems
836 | - `optimizeContent`: AI-powered content optimization and gap analysis
837 | - `validateAccessibility`: Comprehensive accessibility testing and recommendations
838 | 
839 | ### API Evolution Strategy
840 | 
841 | - Versioned tool schemas with backward compatibility
842 | - Deprecation notices and migration guidance
843 | - Feature flags for experimental functionality
844 | - Community feedback integration for API improvements
845 | - Code Mode optimization: Tool categorization and metadata
846 | 
847 | ## References
848 | 
849 | - [Model Context Protocol Specification](https://spec.modelcontextprotocol.io/)
850 | - Commit: 210a274 - feat: Add agent artifact detection and cleanup tool (#80)
851 | - GitHub Issue: #80 - Agent artifact detection and cleanup tool
852 | - [JSON Schema Validation](https://json-schema.org/)
853 | - [API Design Best Practices](https://swagger.io/resources/articles/best-practices-in-api-design/)
854 | 
```

--------------------------------------------------------------------------------
/tests/utils/sitemap-generator.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Tests for sitemap-generator utility
  3 |  */
  4 | 
  5 | import { promises as fs } from "fs";
  6 | import path from "path";
  7 | import { tmpdir } from "os";
  8 | import {
  9 |   generateSitemap,
 10 |   parseSitemap,
 11 |   validateSitemap,
 12 |   updateSitemap,
 13 |   listSitemapUrls,
 14 |   type SitemapUrl,
 15 |   type SitemapOptions,
 16 | } from "../../src/utils/sitemap-generator.js";
 17 | 
 18 | describe("sitemap-generator", () => {
 19 |   let testDir: string;
 20 |   let docsDir: string;
 21 | 
 22 |   beforeEach(async () => {
 23 |     // Create temporary test directory
 24 |     testDir = path.join(tmpdir(), `sitemap-test-${Date.now()}`);
 25 |     docsDir = path.join(testDir, "docs");
 26 |     await fs.mkdir(docsDir, { recursive: true });
 27 |   });
 28 | 
 29 |   afterEach(async () => {
 30 |     // Clean up test directory
 31 |     try {
 32 |       await fs.rm(testDir, { recursive: true, force: true });
 33 |     } catch (error) {
 34 |       // Ignore cleanup errors
 35 |     }
 36 |   });
 37 | 
 38 |   describe("generateSitemap", () => {
 39 |     it("should generate sitemap.xml from documentation files", async () => {
 40 |       // Create test documentation structure
 41 |       await fs.mkdir(path.join(docsDir, "tutorials"), { recursive: true });
 42 |       await fs.mkdir(path.join(docsDir, "reference"), { recursive: true });
 43 | 
 44 |       await fs.writeFile(
 45 |         path.join(docsDir, "index.md"),
 46 |         "# Home\n\nWelcome to the docs!",
 47 |       );
 48 |       await fs.writeFile(
 49 |         path.join(docsDir, "tutorials", "getting-started.md"),
 50 |         "# Getting Started\n\nStart here.",
 51 |       );
 52 |       await fs.writeFile(
 53 |         path.join(docsDir, "reference", "api.md"),
 54 |         "# API Reference\n\nAPI documentation.",
 55 |       );
 56 | 
 57 |       const options: SitemapOptions = {
 58 |         baseUrl: "https://example.com",
 59 |         docsPath: docsDir,
 60 |         useGitHistory: false,
 61 |       };
 62 | 
 63 |       const result = await generateSitemap(options);
 64 | 
 65 |       expect(result.xml).toContain('<?xml version="1.0" encoding="UTF-8"?>');
 66 |       expect(result.xml).toContain(
 67 |         '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
 68 |       );
 69 |       expect(result.urls).toHaveLength(3);
 70 |       expect(result.stats.totalUrls).toBe(3);
 71 |       expect(result.stats.byCategory).toHaveProperty("home");
 72 |       expect(result.stats.byCategory).toHaveProperty("tutorial");
 73 |       expect(result.stats.byCategory).toHaveProperty("reference");
 74 |     });
 75 | 
 76 |     it("should generate URLs with correct priorities based on categories", async () => {
 77 |       await fs.mkdir(path.join(docsDir, "tutorials"), { recursive: true });
 78 |       await fs.mkdir(path.join(docsDir, "reference"), { recursive: true });
 79 | 
 80 |       await fs.writeFile(
 81 |         path.join(docsDir, "tutorials", "guide.md"),
 82 |         "# Tutorial",
 83 |       );
 84 |       await fs.writeFile(
 85 |         path.join(docsDir, "reference", "api.md"),
 86 |         "# Reference",
 87 |       );
 88 | 
 89 |       const result = await generateSitemap({
 90 |         baseUrl: "https://example.com",
 91 |         docsPath: docsDir,
 92 |         useGitHistory: false,
 93 |       });
 94 | 
 95 |       const tutorialUrl = result.urls.find((u) => u.category === "tutorial");
 96 |       const referenceUrl = result.urls.find((u) => u.category === "reference");
 97 | 
 98 |       expect(tutorialUrl?.priority).toBe(1.0); // Highest priority
 99 |       expect(referenceUrl?.priority).toBe(0.8);
100 |     });
101 | 
102 |     it("should handle empty documentation directory", async () => {
103 |       const result = await generateSitemap({
104 |         baseUrl: "https://example.com",
105 |         docsPath: docsDir,
106 |         useGitHistory: false,
107 |       });
108 | 
109 |       expect(result.urls).toHaveLength(0);
110 |       expect(result.stats.totalUrls).toBe(0);
111 |     });
112 | 
113 |     it("should exclude node_modules and other excluded patterns", async () => {
114 |       await fs.mkdir(path.join(docsDir, "node_modules"), { recursive: true });
115 |       await fs.writeFile(
116 |         path.join(docsDir, "node_modules", "package.md"),
117 |         "# Package",
118 |       );
119 |       await fs.writeFile(path.join(docsDir, "guide.md"), "# Guide");
120 | 
121 |       const result = await generateSitemap({
122 |         baseUrl: "https://example.com",
123 |         docsPath: docsDir,
124 |         useGitHistory: false,
125 |       });
126 | 
127 |       expect(result.urls).toHaveLength(1);
128 |       expect(result.urls[0].loc).toContain("guide.html");
129 |     });
130 | 
131 |     it("should convert markdown extensions to html", async () => {
132 |       await fs.writeFile(path.join(docsDir, "page.md"), "# Page");
133 |       await fs.writeFile(path.join(docsDir, "component.mdx"), "# Component");
134 | 
135 |       const result = await generateSitemap({
136 |         baseUrl: "https://example.com",
137 |         docsPath: docsDir,
138 |         useGitHistory: false,
139 |       });
140 | 
141 |       expect(result.urls[0].loc).toContain(".html");
142 |       expect(result.urls[1].loc).toContain(".html");
143 |       expect(result.urls.some((u) => u.loc.endsWith(".md"))).toBe(false);
144 |       expect(result.urls.some((u) => u.loc.endsWith(".mdx"))).toBe(false);
145 |     });
146 | 
147 |     it("should extract title from markdown frontmatter", async () => {
148 |       const content = `---
149 | title: My Custom Title
150 | ---
151 | 
152 | # Main Heading
153 | 
154 | Content here.`;
155 | 
156 |       await fs.writeFile(path.join(docsDir, "page.md"), content);
157 | 
158 |       const result = await generateSitemap({
159 |         baseUrl: "https://example.com",
160 |         docsPath: docsDir,
161 |         useGitHistory: false,
162 |       });
163 | 
164 |       expect(result.urls[0].title).toBe("My Custom Title");
165 |     });
166 | 
167 |     it("should extract title from markdown heading", async () => {
168 |       await fs.writeFile(
169 |         path.join(docsDir, "page.md"),
170 |         "# Page Title\n\nContent",
171 |       );
172 | 
173 |       const result = await generateSitemap({
174 |         baseUrl: "https://example.com",
175 |         docsPath: docsDir,
176 |         useGitHistory: false,
177 |       });
178 | 
179 |       expect(result.urls[0].title).toBe("Page Title");
180 |     });
181 | 
182 |     it("should handle custom include and exclude patterns", async () => {
183 |       await fs.writeFile(path.join(docsDir, "page.md"), "# Markdown");
184 |       await fs.writeFile(path.join(docsDir, "page.html"), "<h1>HTML</h1>");
185 |       await fs.writeFile(path.join(docsDir, "page.txt"), "Text");
186 | 
187 |       const result = await generateSitemap({
188 |         baseUrl: "https://example.com",
189 |         docsPath: docsDir,
190 |         includePatterns: ["**/*.md"],
191 |         excludePatterns: [],
192 |         useGitHistory: false,
193 |       });
194 | 
195 |       expect(result.urls).toHaveLength(1);
196 |       expect(result.urls[0].loc).toContain("page.html");
197 |     });
198 |   });
199 | 
200 |   describe("parseSitemap", () => {
201 |     it("should parse existing sitemap.xml", async () => {
202 |       const xml = `<?xml version="1.0" encoding="UTF-8"?>
203 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
204 |   <url>
205 |     <loc>https://example.com/page1.html</loc>
206 |     <lastmod>2025-01-01</lastmod>
207 |     <changefreq>monthly</changefreq>
208 |     <priority>0.8</priority>
209 |   </url>
210 |   <url>
211 |     <loc>https://example.com/page2.html</loc>
212 |     <lastmod>2025-01-02</lastmod>
213 |     <changefreq>weekly</changefreq>
214 |     <priority>1.0</priority>
215 |   </url>
216 | </urlset>`;
217 | 
218 |       const sitemapPath = path.join(testDir, "sitemap.xml");
219 |       await fs.writeFile(sitemapPath, xml);
220 | 
221 |       const urls = await parseSitemap(sitemapPath);
222 | 
223 |       expect(urls).toHaveLength(2);
224 |       expect(urls[0].loc).toBe("https://example.com/page1.html");
225 |       expect(urls[0].lastmod).toBe("2025-01-01");
226 |       expect(urls[0].changefreq).toBe("monthly");
227 |       expect(urls[0].priority).toBe(0.8);
228 |       expect(urls[1].loc).toBe("https://example.com/page2.html");
229 |     });
230 | 
231 |     it("should handle XML special characters", async () => {
232 |       const xml = `<?xml version="1.0" encoding="UTF-8"?>
233 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
234 |   <url>
235 |     <loc>https://example.com/page?id=1&amp;type=test</loc>
236 |   </url>
237 | </urlset>`;
238 | 
239 |       const sitemapPath = path.join(testDir, "sitemap.xml");
240 |       await fs.writeFile(sitemapPath, xml);
241 | 
242 |       const urls = await parseSitemap(sitemapPath);
243 | 
244 |       expect(urls[0].loc).toBe("https://example.com/page?id=1&type=test");
245 |     });
246 | 
247 |     it("should throw error for invalid sitemap", async () => {
248 |       const sitemapPath = path.join(testDir, "invalid.xml");
249 |       await fs.writeFile(sitemapPath, "not xml");
250 | 
251 |       const urls = await parseSitemap(sitemapPath);
252 |       expect(urls).toHaveLength(0); // Graceful handling of invalid XML
253 |     });
254 |   });
255 | 
256 |   describe("validateSitemap", () => {
257 |     it("should validate correct sitemap", async () => {
258 |       const xml = `<?xml version="1.0" encoding="UTF-8"?>
259 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
260 |   <url>
261 |     <loc>https://example.com/page.html</loc>
262 |     <lastmod>2025-01-01</lastmod>
263 |     <changefreq>monthly</changefreq>
264 |     <priority>0.8</priority>
265 |   </url>
266 | </urlset>`;
267 | 
268 |       const sitemapPath = path.join(testDir, "sitemap.xml");
269 |       await fs.writeFile(sitemapPath, xml);
270 | 
271 |       const result = await validateSitemap(sitemapPath);
272 | 
273 |       expect(result.valid).toBe(true);
274 |       expect(result.errors).toHaveLength(0);
275 |       expect(result.urlCount).toBe(1);
276 |     });
277 | 
278 |     it("should detect missing loc element", async () => {
279 |       const xml = `<?xml version="1.0" encoding="UTF-8"?>
280 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
281 |   <url>
282 |     <lastmod>2025-01-01</lastmod>
283 |   </url>
284 | </urlset>`;
285 | 
286 |       const sitemapPath = path.join(testDir, "sitemap.xml");
287 |       await fs.writeFile(sitemapPath, xml);
288 | 
289 |       const result = await validateSitemap(sitemapPath);
290 | 
291 |       expect(result.valid).toBe(false);
292 |       expect(result.errors.some((e) => e.includes("Missing <loc>"))).toBe(true);
293 |     });
294 | 
295 |     it("should detect invalid priority", async () => {
296 |       const xml = `<?xml version="1.0" encoding="UTF-8"?>
297 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
298 |   <url>
299 |     <loc>https://example.com/page.html</loc>
300 |     <priority>1.5</priority>
301 |   </url>
302 | </urlset>`;
303 | 
304 |       const sitemapPath = path.join(testDir, "sitemap.xml");
305 |       await fs.writeFile(sitemapPath, xml);
306 | 
307 |       const result = await validateSitemap(sitemapPath);
308 | 
309 |       expect(result.valid).toBe(false);
310 |       expect(
311 |         result.errors.some((e) =>
312 |           e.includes("Priority must be between 0.0 and 1.0"),
313 |         ),
314 |       ).toBe(true);
315 |     });
316 | 
317 |     it("should detect invalid protocol", async () => {
318 |       const xml = `<?xml version="1.0" encoding="UTF-8"?>
319 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
320 |   <url>
321 |     <loc>ftp://example.com/page.html</loc>
322 |   </url>
323 | </urlset>`;
324 | 
325 |       const sitemapPath = path.join(testDir, "sitemap.xml");
326 |       await fs.writeFile(sitemapPath, xml);
327 | 
328 |       const result = await validateSitemap(sitemapPath);
329 | 
330 |       expect(result.valid).toBe(false);
331 |       expect(result.errors.some((e) => e.includes("Invalid protocol"))).toBe(
332 |         true,
333 |       );
334 |     });
335 | 
336 |     it("should return error if sitemap does not exist", async () => {
337 |       const sitemapPath = path.join(testDir, "nonexistent.xml");
338 | 
339 |       const result = await validateSitemap(sitemapPath);
340 | 
341 |       expect(result.valid).toBe(false);
342 |       expect(result.errors.some((e) => e.includes("does not exist"))).toBe(
343 |         true,
344 |       );
345 |     });
346 |   });
347 | 
348 |   describe("updateSitemap", () => {
349 |     it("should update existing sitemap with new pages", async () => {
350 |       // Create initial sitemap
351 |       const xml = `<?xml version="1.0" encoding="UTF-8"?>
352 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
353 |   <url>
354 |     <loc>https://example.com/page1.html</loc>
355 |     <lastmod>2025-01-01</lastmod>
356 |     <priority>0.8</priority>
357 |   </url>
358 | </urlset>`;
359 | 
360 |       const sitemapPath = path.join(testDir, "sitemap.xml");
361 |       await fs.writeFile(sitemapPath, xml);
362 | 
363 |       // Add new documentation file
364 |       await fs.writeFile(path.join(docsDir, "page1.md"), "# Page 1");
365 |       await fs.writeFile(path.join(docsDir, "page2.md"), "# Page 2");
366 | 
367 |       const changes = await updateSitemap(sitemapPath, {
368 |         baseUrl: "https://example.com",
369 |         docsPath: docsDir,
370 |         useGitHistory: false,
371 |       });
372 | 
373 |       expect(changes.added).toBe(1); // page2.md is new
374 |       expect(changes.total).toBe(2);
375 |     });
376 | 
377 |     it("should detect removed pages", async () => {
378 |       // Create initial sitemap with 2 URLs
379 |       const xml = `<?xml version="1.0" encoding="UTF-8"?>
380 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
381 |   <url>
382 |     <loc>https://example.com/page1.html</loc>
383 |   </url>
384 |   <url>
385 |     <loc>https://example.com/page2.html</loc>
386 |   </url>
387 | </urlset>`;
388 | 
389 |       const sitemapPath = path.join(testDir, "sitemap.xml");
390 |       await fs.writeFile(sitemapPath, xml);
391 | 
392 |       // Only create page1.md
393 |       await fs.writeFile(path.join(docsDir, "page1.md"), "# Page 1");
394 | 
395 |       const changes = await updateSitemap(sitemapPath, {
396 |         baseUrl: "https://example.com",
397 |         docsPath: docsDir,
398 |         useGitHistory: false,
399 |       });
400 | 
401 |       expect(changes.removed).toBe(1); // page2.html was removed
402 |       expect(changes.total).toBe(1);
403 |     });
404 | 
405 |     it("should create new sitemap if none exists", async () => {
406 |       const sitemapPath = path.join(testDir, "sitemap.xml");
407 |       await fs.writeFile(path.join(docsDir, "page.md"), "# Page");
408 | 
409 |       const changes = await updateSitemap(sitemapPath, {
410 |         baseUrl: "https://example.com",
411 |         docsPath: docsDir,
412 |         useGitHistory: false,
413 |       });
414 | 
415 |       expect(changes.added).toBe(1);
416 |       expect(changes.total).toBe(1);
417 | 
418 |       // Verify sitemap was created
419 |       const exists = await fs
420 |         .access(sitemapPath)
421 |         .then(() => true)
422 |         .catch(() => false);
423 |       expect(exists).toBe(true);
424 |     });
425 |   });
426 | 
427 |   describe("listSitemapUrls", () => {
428 |     it("should list all URLs from sitemap", async () => {
429 |       const xml = `<?xml version="1.0" encoding="UTF-8"?>
430 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
431 |   <url>
432 |     <loc>https://example.com/page1.html</loc>
433 |     <priority>0.9</priority>
434 |   </url>
435 |   <url>
436 |     <loc>https://example.com/page2.html</loc>
437 |     <priority>0.8</priority>
438 |   </url>
439 | </urlset>`;
440 | 
441 |       const sitemapPath = path.join(testDir, "sitemap.xml");
442 |       await fs.writeFile(sitemapPath, xml);
443 | 
444 |       const urls = await listSitemapUrls(sitemapPath);
445 | 
446 |       expect(urls).toHaveLength(2);
447 |       expect(urls[0].loc).toBe("https://example.com/page1.html");
448 |       expect(urls[1].loc).toBe("https://example.com/page2.html");
449 |     });
450 |   });
451 | 
452 |   describe("edge cases", () => {
453 |     it("should handle deeply nested directory structures", async () => {
454 |       const deepPath = path.join(docsDir, "a", "b", "c", "d");
455 |       await fs.mkdir(deepPath, { recursive: true });
456 |       await fs.writeFile(path.join(deepPath, "deep.md"), "# Deep Page");
457 | 
458 |       const result = await generateSitemap({
459 |         baseUrl: "https://example.com",
460 |         docsPath: docsDir,
461 |         useGitHistory: false,
462 |       });
463 | 
464 |       expect(result.urls).toHaveLength(1);
465 |       expect(result.urls[0].loc).toContain("a/b/c/d/deep.html");
466 |     });
467 | 
468 |     it("should handle files with special characters in names", async () => {
469 |       await fs.writeFile(path.join(docsDir, "my-page-2024.md"), "# Page");
470 | 
471 |       const result = await generateSitemap({
472 |         baseUrl: "https://example.com",
473 |         docsPath: docsDir,
474 |         useGitHistory: false,
475 |       });
476 | 
477 |       expect(result.urls).toHaveLength(1);
478 |       expect(result.urls[0].loc).toContain("my-page-2024.html");
479 |     });
480 | 
481 |     it("should handle index.html correctly", async () => {
482 |       await fs.writeFile(path.join(docsDir, "index.md"), "# Home");
483 | 
484 |       const result = await generateSitemap({
485 |         baseUrl: "https://example.com",
486 |         docsPath: docsDir,
487 |         useGitHistory: false,
488 |       });
489 | 
490 |       expect(result.urls[0].loc).toBe("https://example.com/");
491 |     });
492 | 
493 |     it("should exclude directories matching exclusion patterns", async () => {
494 |       // Create directory structure with excluded dirs
495 |       await fs.mkdir(path.join(docsDir, "node_modules"), { recursive: true });
496 |       await fs.mkdir(path.join(docsDir, "valid"), { recursive: true });
497 |       await fs.writeFile(
498 |         path.join(docsDir, "node_modules", "package.md"),
499 |         "# Should be excluded",
500 |       );
501 |       await fs.writeFile(
502 |         path.join(docsDir, "valid", "page.md"),
503 |         "# Valid Page",
504 |       );
505 | 
506 |       const result = await generateSitemap({
507 |         baseUrl: "https://example.com",
508 |         docsPath: docsDir,
509 |         useGitHistory: false,
510 |       });
511 | 
512 |       // Should only include valid directory, not node_modules
513 |       expect(result.urls).toHaveLength(1);
514 |       expect(result.urls[0].loc).toContain("valid/page");
515 |     });
516 | 
517 |     it("should handle directory scan errors gracefully", async () => {
518 |       // Create a valid docs directory
519 |       await fs.writeFile(path.join(docsDir, "valid.md"), "# Valid");
520 | 
521 |       const result = await generateSitemap({
522 |         baseUrl: "https://example.com",
523 |         docsPath: docsDir,
524 |         useGitHistory: false,
525 |       });
526 | 
527 |       // Should succeed despite potential permission issues
528 |       expect(result.urls.length).toBeGreaterThanOrEqual(1);
529 |     });
530 | 
531 |     it("should categorize explanation pages correctly", async () => {
532 |       await fs.mkdir(path.join(docsDir, "explanation"), { recursive: true });
533 |       await fs.writeFile(
534 |         path.join(docsDir, "explanation", "concepts.md"),
535 |         "# Concepts",
536 |       );
537 | 
538 |       const result = await generateSitemap({
539 |         baseUrl: "https://example.com",
540 |         docsPath: docsDir,
541 |         useGitHistory: false,
542 |       });
543 | 
544 |       expect(result.stats.byCategory).toHaveProperty("explanation");
545 |       expect(result.stats.byCategory.explanation).toBeGreaterThan(0);
546 |     });
547 | 
548 |     it("should fall back to file system date when git fails", async () => {
549 |       await fs.writeFile(path.join(docsDir, "no-git.md"), "# No Git");
550 | 
551 |       const result = await generateSitemap({
552 |         baseUrl: "https://example.com",
553 |         docsPath: docsDir,
554 |         useGitHistory: true, // Try git but will fall back
555 |       });
556 | 
557 |       // Should still have a lastmod date from file system
558 |       expect(result.urls[0].lastmod).toBeDefined();
559 |       expect(result.urls[0].lastmod).toMatch(/^\d{4}-\d{2}-\d{2}$/);
560 |     });
561 | 
562 |     it("should handle files without extensions", async () => {
563 |       await fs.writeFile(path.join(docsDir, "README"), "# Readme");
564 | 
565 |       const result = await generateSitemap({
566 |         baseUrl: "https://example.com",
567 |         docsPath: docsDir,
568 |         includePatterns: ["**/*"], // Include all files
569 |         useGitHistory: false,
570 |       });
571 | 
572 |       // Should handle extensionless files
573 |       expect(result.urls.length).toBeGreaterThanOrEqual(0);
574 |     });
575 | 
576 |     it("should handle empty git timestamp", async () => {
577 |       // Create file and generate sitemap with git enabled
578 |       await fs.writeFile(path.join(docsDir, "test.md"), "# Test");
579 | 
580 |       const result = await generateSitemap({
581 |         baseUrl: "https://example.com",
582 |         docsPath: docsDir,
583 |         useGitHistory: true,
584 |       });
585 | 
586 |       // Should have valid dates even if git returns empty
587 |       expect(result.urls[0].lastmod).toBeDefined();
588 |     });
589 | 
590 |     it("should handle files in deeply excluded paths", async () => {
591 |       await fs.mkdir(path.join(docsDir, ".git", "objects"), {
592 |         recursive: true,
593 |       });
594 |       await fs.writeFile(
595 |         path.join(docsDir, ".git", "objects", "file.md"),
596 |         "# Git Object",
597 |       );
598 |       await fs.writeFile(path.join(docsDir, "valid.md"), "# Valid");
599 | 
600 |       const result = await generateSitemap({
601 |         baseUrl: "https://example.com",
602 |         docsPath: docsDir,
603 |         useGitHistory: false,
604 |       });
605 | 
606 |       // Should exclude .git directory
607 |       expect(result.urls).toHaveLength(1);
608 |       expect(result.urls[0].loc).not.toContain(".git");
609 |     });
610 | 
611 |     it("should extract title from HTML title tag", async () => {
612 |       const htmlContent = `<!DOCTYPE html>
613 | <html>
614 | <head>
615 |   <title>HTML Page Title</title>
616 | </head>
617 | <body>
618 |   <h1>Different Heading</h1>
619 | </body>
620 | </html>`;
621 | 
622 |       await fs.writeFile(path.join(docsDir, "page.html"), htmlContent);
623 | 
624 |       const result = await generateSitemap({
625 |         baseUrl: "https://example.com",
626 |         docsPath: docsDir,
627 |         includePatterns: ["**/*.html"],
628 |         useGitHistory: false,
629 |       });
630 | 
631 |       expect(result.urls[0].title).toBe("HTML Page Title");
632 |     });
633 | 
634 |     it("should handle files with no extractable title", async () => {
635 |       await fs.writeFile(path.join(docsDir, "notitle.md"), "Just content");
636 | 
637 |       const result = await generateSitemap({
638 |         baseUrl: "https://example.com",
639 |         docsPath: docsDir,
640 |         useGitHistory: false,
641 |       });
642 | 
643 |       expect(result.urls[0].title).toBeUndefined();
644 |     });
645 | 
646 |     it("should handle inaccessible files gracefully", async () => {
647 |       await fs.writeFile(path.join(docsDir, "readable.md"), "# Readable");
648 | 
649 |       const result = await generateSitemap({
650 |         baseUrl: "https://example.com",
651 |         docsPath: docsDir,
652 |         useGitHistory: false,
653 |       });
654 | 
655 |       // Should still process readable files
656 |       expect(result.urls.length).toBeGreaterThan(0);
657 |     });
658 |   });
659 | 
660 |   describe("validateSitemap - additional validations", () => {
661 |     it("should detect empty sitemap", async () => {
662 |       const xml = `<?xml version="1.0" encoding="UTF-8"?>
663 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
664 | </urlset>`;
665 | 
666 |       const sitemapPath = path.join(testDir, "sitemap.xml");
667 |       await fs.writeFile(sitemapPath, xml);
668 | 
669 |       const result = await validateSitemap(sitemapPath);
670 | 
671 |       expect(result.valid).toBe(true);
672 |       expect(result.warnings.some((w) => w.includes("no URLs"))).toBe(true);
673 |     });
674 | 
675 |     it("should detect URL exceeding 2048 characters", async () => {
676 |       const longUrl = `https://example.com/${"a".repeat(2100)}`;
677 |       const xml = `<?xml version="1.0" encoding="UTF-8"?>
678 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
679 |   <url>
680 |     <loc>${longUrl}</loc>
681 |   </url>
682 | </urlset>`;
683 | 
684 |       const sitemapPath = path.join(testDir, "sitemap.xml");
685 |       await fs.writeFile(sitemapPath, xml);
686 | 
687 |       const result = await validateSitemap(sitemapPath);
688 | 
689 |       expect(result.valid).toBe(false);
690 |       expect(result.errors.some((e) => e.includes("exceeds 2048"))).toBe(true);
691 |     });
692 | 
693 |     it("should warn about invalid lastmod format", async () => {
694 |       const xml = `<?xml version="1.0" encoding="UTF-8"?>
695 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
696 |   <url>
697 |     <loc>https://example.com/page.html</loc>
698 |     <lastmod>invalid-date</lastmod>
699 |   </url>
700 | </urlset>`;
701 | 
702 |       const sitemapPath = path.join(testDir, "sitemap.xml");
703 |       await fs.writeFile(sitemapPath, xml);
704 | 
705 |       const result = await validateSitemap(sitemapPath);
706 | 
707 |       expect(result.warnings.some((w) => w.includes("Invalid lastmod"))).toBe(
708 |         true,
709 |       );
710 |     });
711 | 
712 |     it("should detect sitemap with more than 50,000 URLs", async () => {
713 |       // Create sitemap XML with >50,000 URLs
714 |       const urls = Array.from(
715 |         { length: 50001 },
716 |         (_, i) => `  <url>
717 |     <loc>https://example.com/page${i}.html</loc>
718 |     <lastmod>2025-01-01</lastmod>
719 |   </url>`,
720 |       ).join("\n");
721 | 
722 |       const xml = `<?xml version="1.0" encoding="UTF-8"?>
723 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
724 | ${urls}
725 | </urlset>`;
726 | 
727 |       const sitemapPath = path.join(testDir, "large-sitemap.xml");
728 |       await fs.writeFile(sitemapPath, xml);
729 | 
730 |       const result = await validateSitemap(sitemapPath);
731 | 
732 |       expect(result.valid).toBe(false);
733 |       expect(result.errors.some((e) => e.includes("50,000"))).toBe(true);
734 |     });
735 | 
736 |     it("should handle malformed XML gracefully", async () => {
737 |       // The regex-based parser is lenient and extracts data where possible
738 |       // This tests that the parser doesn't crash on malformed XML
739 |       const malformedXml = `<?xml version="1.0"?>
740 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
741 |   <url>
742 |     <loc>https://example.com</loc>
743 |   </url>
744 |   <!-- Missing closing urlset tag`;
745 | 
746 |       const sitemapPath = path.join(testDir, "malformed.xml");
747 |       await fs.writeFile(sitemapPath, malformedXml);
748 | 
749 |       // Should parse successfully despite malformation (regex-based parsing)
750 |       const result = await validateSitemap(sitemapPath);
751 |       expect(result).toBeDefined();
752 |       expect(result.urlCount).toBe(1);
753 |     });
754 |   });
755 | 
756 |   describe("Edge cases", () => {
757 |     it("should handle excluded directories", async () => {
758 |       // Create structure with node_modules
759 |       await fs.mkdir(path.join(testDir, "node_modules"), { recursive: true });
760 |       await fs.writeFile(
761 |         path.join(testDir, "node_modules", "package.md"),
762 |         "# Should be excluded",
763 |       );
764 |       await fs.writeFile(path.join(testDir, "included.md"), "# Included");
765 | 
766 |       const result = await generateSitemap({
767 |         baseUrl: "https://example.com",
768 |         docsPath: testDir,
769 |         includePatterns: ["**/*.md"],
770 |         useGitHistory: false,
771 |       });
772 | 
773 |       expect(result.urls.some((u) => u.loc.includes("node_modules"))).toBe(
774 |         false,
775 |       );
776 |       expect(result.urls.some((u) => u.loc.includes("included"))).toBe(true);
777 |     });
778 | 
779 |     it("should handle directory scan errors gracefully", async () => {
780 |       // Test with a path that has permission issues or doesn't exist
781 |       const result = await generateSitemap({
782 |         baseUrl: "https://example.com",
783 |         docsPath: path.join(testDir, "nonexistent"),
784 |         includePatterns: ["**/*.md"],
785 |         useGitHistory: false,
786 |       });
787 | 
788 |       expect(result.urls).toEqual([]);
789 |     });
790 | 
791 |     it("should use git timestamp when available", async () => {
792 |       // Initialize git and create a committed file
793 |       await fs.writeFile(path.join(testDir, "test.md"), "# Test");
794 | 
795 |       try {
796 |         const { execSync } = require("child_process");
797 |         execSync("git init", { cwd: testDir, stdio: "ignore" });
798 |         execSync("git config user.email '[email protected]'", {
799 |           cwd: testDir,
800 |           stdio: "ignore",
801 |         });
802 |         execSync("git config user.name 'Test'", {
803 |           cwd: testDir,
804 |           stdio: "ignore",
805 |         });
806 |         execSync("git add test.md", { cwd: testDir, stdio: "ignore" });
807 |         execSync("git commit -m 'test'", { cwd: testDir, stdio: "ignore" });
808 | 
809 |         const result = await generateSitemap({
810 |           baseUrl: "https://example.com",
811 |           docsPath: testDir,
812 |           includePatterns: ["**/*.md"],
813 |           useGitHistory: true,
814 |         });
815 | 
816 |         expect(result.urls.length).toBe(1);
817 |         expect(result.urls[0].lastmod).toMatch(/\d{4}-\d{2}-\d{2}/);
818 |       } catch (error) {
819 |         // Git might not be available in test environment, skip
820 |         console.log("Git test skipped:", error);
821 |       }
822 |     });
823 | 
824 |     it("should use current date when file doesn't exist", async () => {
825 |       // This tests the getFileLastModified error path
826 |       // We'll indirectly test this by ensuring dates are always returned
827 |       const result = await generateSitemap({
828 |         baseUrl: "https://example.com",
829 |         docsPath: testDir,
830 |         includePatterns: ["**/*.md"],
831 |         useGitHistory: false,
832 |       });
833 | 
834 |       // Even with no files, function should not crash
835 |       expect(result).toBeDefined();
836 |       expect(Array.isArray(result.urls)).toBe(true);
837 |     });
838 |   });
839 | });
840 | 
```

--------------------------------------------------------------------------------
/src/memory/kg-health.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Knowledge Graph Health Monitoring Module
  3 |  * Implements Phase 2: KG Health Tracking
  4 |  *
  5 |  * Provides comprehensive health monitoring, issue detection, and trend analysis
  6 |  * for the DocuMCP knowledge graph to ensure data quality and performance.
  7 |  */
  8 | 
  9 | import { promises as fs } from "fs";
 10 | import { join } from "path";
 11 | import KnowledgeGraph, { GraphNode, GraphEdge } from "./knowledge-graph.js";
 12 | import { KGStorage } from "./kg-storage.js";
 13 | 
 14 | // ============================================================================
 15 | // Health Metrics Schema
 16 | // ============================================================================
 17 | 
 18 | export interface KGHealthMetrics {
 19 |   timestamp: string;
 20 |   overallHealth: number; // 0-100 score
 21 |   dataQuality: DataQualityMetrics;
 22 |   structureHealth: StructureHealthMetrics;
 23 |   performance: PerformanceMetrics;
 24 |   trends: HealthTrends;
 25 |   issues: HealthIssue[];
 26 |   recommendations: HealthRecommendation[];
 27 | }
 28 | 
 29 | export interface DataQualityMetrics {
 30 |   score: number; // 0-100
 31 |   staleNodeCount: number; // nodes not updated in 30+ days
 32 |   orphanedEdgeCount: number;
 33 |   duplicateCount: number;
 34 |   confidenceAverage: number;
 35 |   completenessScore: number; // % of expected relationships present
 36 |   totalNodes: number;
 37 |   totalEdges: number;
 38 | }
 39 | 
 40 | export interface StructureHealthMetrics {
 41 |   score: number; // 0-100
 42 |   isolatedNodeCount: number; // nodes with no edges
 43 |   clusteringCoefficient: number;
 44 |   averagePathLength: number;
 45 |   densityScore: number;
 46 |   connectedComponents: number;
 47 | }
 48 | 
 49 | export interface PerformanceMetrics {
 50 |   score: number; // 0-100
 51 |   avgQueryTime: number; // ms
 52 |   storageSize: number; // bytes
 53 |   growthRate: number; // bytes/day
 54 |   indexEfficiency: number;
 55 | }
 56 | 
 57 | export interface HealthTrends {
 58 |   healthTrend: "improving" | "stable" | "degrading";
 59 |   nodeGrowthRate: number; // nodes/day
 60 |   edgeGrowthRate: number; // edges/day
 61 |   errorRate: number; // errors/operations (from last 100 operations)
 62 |   qualityTrend: "improving" | "stable" | "degrading";
 63 | }
 64 | 
 65 | export interface HealthIssue {
 66 |   id: string;
 67 |   severity: "critical" | "high" | "medium" | "low";
 68 |   category: "integrity" | "performance" | "quality" | "structure";
 69 |   description: string;
 70 |   affectedEntities: string[];
 71 |   remediation: string;
 72 |   detectedAt: string;
 73 |   autoFixable: boolean;
 74 | }
 75 | 
 76 | export interface HealthRecommendation {
 77 |   id: string;
 78 |   priority: "high" | "medium" | "low";
 79 |   action: string;
 80 |   expectedImpact: number; // health score increase (0-100)
 81 |   effort: "low" | "medium" | "high";
 82 |   category: string;
 83 | }
 84 | 
 85 | export interface HealthHistory {
 86 |   timestamp: string;
 87 |   overallHealth: number;
 88 |   dataQuality: number;
 89 |   structureHealth: number;
 90 |   performance: number;
 91 |   nodeCount: number;
 92 |   edgeCount: number;
 93 | }
 94 | 
 95 | // ============================================================================
 96 | // Health Monitoring Class
 97 | // ============================================================================
 98 | 
 99 | export class KGHealthMonitor {
100 |   private storageDir: string;
101 |   private historyFilePath: string;
102 |   private issueDetectors: IssueDetector[];
103 |   private performanceTracking: PerformanceTracker;
104 | 
105 |   constructor(storageDir?: string) {
106 |     this.storageDir = storageDir || `${process.cwd()}/.documcp/memory`;
107 |     this.historyFilePath = join(this.storageDir, "health-history.jsonl");
108 |     this.issueDetectors = createIssueDetectors();
109 |     this.performanceTracking = new PerformanceTracker();
110 |   }
111 | 
112 |   /**
113 |    * Calculate comprehensive health metrics
114 |    */
115 |   async calculateHealth(
116 |     kg: KnowledgeGraph,
117 |     storage: KGStorage,
118 |   ): Promise<KGHealthMetrics> {
119 |     const timestamp = new Date().toISOString();
120 | 
121 |     // Calculate component metrics
122 |     const dataQuality = await this.calculateDataQuality(kg, storage);
123 |     const structureHealth = await this.calculateStructureHealth(kg);
124 |     const performance = await this.calculatePerformance(storage);
125 | 
126 |     // Calculate overall health (weighted average)
127 |     const overallHealth = Math.round(
128 |       dataQuality.score * 0.4 +
129 |         structureHealth.score * 0.3 +
130 |         performance.score * 0.3,
131 |     );
132 | 
133 |     // Detect issues
134 |     const issues = await this.detectIssues(kg, {
135 |       dataQuality,
136 |       structureHealth,
137 |       performance,
138 |     });
139 | 
140 |     // Generate recommendations
141 |     const recommendations = this.generateRecommendations(issues, {
142 |       dataQuality,
143 |       structureHealth,
144 |       performance,
145 |     });
146 | 
147 |     // Analyze trends
148 |     const trends = await this.analyzeTrends(overallHealth);
149 | 
150 |     const metrics: KGHealthMetrics = {
151 |       timestamp,
152 |       overallHealth,
153 |       dataQuality,
154 |       structureHealth,
155 |       performance,
156 |       trends,
157 |       issues,
158 |       recommendations,
159 |     };
160 | 
161 |     // Track history
162 |     await this.trackHealthHistory(metrics);
163 | 
164 |     return metrics;
165 |   }
166 | 
167 |   /**
168 |    * Calculate data quality metrics
169 |    */
170 |   private async calculateDataQuality(
171 |     kg: KnowledgeGraph,
172 |     storage: KGStorage,
173 |   ): Promise<DataQualityMetrics> {
174 |     await kg.getStatistics();
175 |     const integrity = await storage.verifyIntegrity();
176 | 
177 |     const now = new Date();
178 |     const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
179 | 
180 |     // Count stale nodes
181 |     const allNodes = await kg.getAllNodes();
182 |     const staleNodeCount = allNodes.filter((node) => {
183 |       const lastUpdated = new Date(node.lastUpdated);
184 |       return lastUpdated < thirtyDaysAgo;
185 |     }).length;
186 | 
187 |     // Get orphaned edges from integrity check
188 |     const orphanedEdgeCount = integrity.warnings.filter((w) =>
189 |       w.includes("missing"),
190 |     ).length;
191 | 
192 |     // Get duplicate count from integrity check
193 |     const duplicateCount = integrity.errors.filter((e) =>
194 |       e.includes("Duplicate"),
195 |     ).length;
196 | 
197 |     // Calculate average confidence
198 |     const allEdges = await kg.getAllEdges();
199 |     const confidenceAverage =
200 |       allEdges.length > 0
201 |         ? allEdges.reduce((sum, edge) => sum + edge.confidence, 0) /
202 |           allEdges.length
203 |         : 1.0;
204 | 
205 |     // Calculate completeness (% of projects with expected relationships)
206 |     const completenessScore = this.calculateCompleteness(allNodes, allEdges);
207 | 
208 |     // Calculate data quality score (0-100)
209 |     const stalePercentage =
210 |       (staleNodeCount / Math.max(allNodes.length, 1)) * 100;
211 |     const orphanPercentage =
212 |       (orphanedEdgeCount / Math.max(allEdges.length, 1)) * 100;
213 |     const qualityDeductions =
214 |       stalePercentage * 0.3 + orphanPercentage * 0.5 + duplicateCount * 10;
215 | 
216 |     const score = Math.max(
217 |       0,
218 |       Math.min(100, 100 - qualityDeductions + (completenessScore - 0.5) * 50),
219 |     );
220 | 
221 |     return {
222 |       score: Math.round(score),
223 |       staleNodeCount,
224 |       orphanedEdgeCount,
225 |       duplicateCount,
226 |       confidenceAverage,
227 |       completenessScore,
228 |       totalNodes: allNodes.length,
229 |       totalEdges: allEdges.length,
230 |     };
231 |   }
232 | 
233 |   /**
234 |    * Calculate structure health metrics
235 |    */
236 |   private async calculateStructureHealth(
237 |     kg: KnowledgeGraph,
238 |   ): Promise<StructureHealthMetrics> {
239 |     await kg.getStatistics();
240 |     const allNodes = await kg.getAllNodes();
241 |     const allEdges = await kg.getAllEdges();
242 | 
243 |     // Count isolated nodes (no edges)
244 |     const nodeConnections = new Map<string, number>();
245 |     for (const edge of allEdges) {
246 |       nodeConnections.set(
247 |         edge.source,
248 |         (nodeConnections.get(edge.source) || 0) + 1,
249 |       );
250 |       nodeConnections.set(
251 |         edge.target,
252 |         (nodeConnections.get(edge.target) || 0) + 1,
253 |       );
254 |     }
255 | 
256 |     const isolatedNodeCount = allNodes.filter(
257 |       (node) => !nodeConnections.has(node.id),
258 |     ).length;
259 | 
260 |     // Calculate clustering coefficient (simplified)
261 |     const clusteringCoefficient = this.calculateClusteringCoefficient(
262 |       allNodes,
263 |       allEdges,
264 |     );
265 | 
266 |     // Calculate average path length (simplified - using BFS on sample)
267 |     const averagePathLength = this.calculateAveragePathLength(
268 |       allNodes,
269 |       allEdges,
270 |     );
271 | 
272 |     // Calculate density score
273 |     const maxPossibleEdges = (allNodes.length * (allNodes.length - 1)) / 2;
274 |     const densityScore =
275 |       maxPossibleEdges > 0 ? allEdges.length / maxPossibleEdges : 0;
276 | 
277 |     // Count connected components
278 |     const connectedComponents = this.countConnectedComponents(
279 |       allNodes,
280 |       allEdges,
281 |     );
282 | 
283 |     // Calculate structure health score
284 |     const isolatedPercentage =
285 |       (isolatedNodeCount / Math.max(allNodes.length, 1)) * 100;
286 |     const score = Math.max(
287 |       0,
288 |       Math.min(
289 |         100,
290 |         100 -
291 |           isolatedPercentage * 0.5 +
292 |           clusteringCoefficient * 20 -
293 |           (connectedComponents - 1) * 5,
294 |       ),
295 |     );
296 | 
297 |     return {
298 |       score: Math.round(score),
299 |       isolatedNodeCount,
300 |       clusteringCoefficient,
301 |       averagePathLength,
302 |       densityScore,
303 |       connectedComponents,
304 |     };
305 |   }
306 | 
307 |   /**
308 |    * Calculate performance metrics
309 |    */
310 |   private async calculatePerformance(
311 |     storage: KGStorage,
312 |   ): Promise<PerformanceMetrics> {
313 |     const storageStats = await storage.getStatistics();
314 | 
315 |     // Get average query time from performance tracker
316 |     const avgQueryTime = this.performanceTracking.getAverageQueryTime();
317 | 
318 |     // Calculate storage size
319 |     const storageSize =
320 |       storageStats.fileSize.entities + storageStats.fileSize.relationships;
321 | 
322 |     // Calculate growth rate (bytes/day) from history
323 |     const growthRate = await this.calculateGrowthRate();
324 | 
325 |     // Index efficiency (placeholder - would need actual indexing metrics)
326 |     const indexEfficiency = 0.8;
327 | 
328 |     // Calculate performance score
329 |     const queryScore =
330 |       avgQueryTime < 10 ? 100 : Math.max(0, 100 - avgQueryTime);
331 |     const sizeScore =
332 |       storageSize < 10 * 1024 * 1024
333 |         ? 100
334 |         : Math.max(0, 100 - storageSize / (1024 * 1024));
335 |     const score = Math.round(
336 |       queryScore * 0.5 + sizeScore * 0.3 + indexEfficiency * 100 * 0.2,
337 |     );
338 | 
339 |     return {
340 |       score,
341 |       avgQueryTime,
342 |       storageSize,
343 |       growthRate,
344 |       indexEfficiency,
345 |     };
346 |   }
347 | 
348 |   /**
349 |    * Detect issues in the knowledge graph
350 |    */
351 |   private async detectIssues(
352 |     kg: KnowledgeGraph,
353 |     metrics: {
354 |       dataQuality: DataQualityMetrics;
355 |       structureHealth: StructureHealthMetrics;
356 |       performance: PerformanceMetrics;
357 |     },
358 |   ): Promise<HealthIssue[]> {
359 |     const issues: HealthIssue[] = [];
360 | 
361 |     for (const detector of this.issueDetectors) {
362 |       const detectedIssues = await detector.detect(kg, metrics);
363 |       issues.push(...detectedIssues);
364 |     }
365 | 
366 |     // Sort by severity
367 |     issues.sort((a, b) => {
368 |       const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
369 |       return severityOrder[a.severity] - severityOrder[b.severity];
370 |     });
371 | 
372 |     return issues;
373 |   }
374 | 
375 |   /**
376 |    * Generate recommendations based on issues and metrics
377 |    */
378 |   private generateRecommendations(
379 |     issues: HealthIssue[],
380 |     metrics: {
381 |       dataQuality: DataQualityMetrics;
382 |       structureHealth: StructureHealthMetrics;
383 |       performance: PerformanceMetrics;
384 |     },
385 |   ): HealthRecommendation[] {
386 |     const recommendations: HealthRecommendation[] = [];
387 | 
388 |     // Generate recommendations for critical/high severity issues
389 |     for (const issue of issues.filter(
390 |       (i) => i.severity === "critical" || i.severity === "high",
391 |     )) {
392 |       if (issue.autoFixable) {
393 |         recommendations.push({
394 |           id: `fix_${issue.id}`,
395 |           priority: "high",
396 |           action: issue.remediation,
397 |           expectedImpact: issue.severity === "critical" ? 20 : 10,
398 |           effort: "low",
399 |           category: issue.category,
400 |         });
401 |       }
402 |     }
403 | 
404 |     // Data quality recommendations
405 |     if (metrics.dataQuality.score < 70) {
406 |       if (metrics.dataQuality.staleNodeCount > 10) {
407 |         recommendations.push({
408 |           id: "refresh_stale_data",
409 |           priority: "medium",
410 |           action: `Re-analyze ${metrics.dataQuality.staleNodeCount} stale projects to refresh data`,
411 |           expectedImpact: 15,
412 |           effort: "medium",
413 |           category: "data_quality",
414 |         });
415 |       }
416 | 
417 |       if (metrics.dataQuality.orphanedEdgeCount > 5) {
418 |         recommendations.push({
419 |           id: "cleanup_orphaned_edges",
420 |           priority: "high",
421 |           action: "Run automated cleanup to remove orphaned relationships",
422 |           expectedImpact: 10,
423 |           effort: "low",
424 |           category: "data_quality",
425 |         });
426 |       }
427 |     }
428 | 
429 |     // Structure health recommendations
430 |     if (metrics.structureHealth.score < 70) {
431 |       if (metrics.structureHealth.isolatedNodeCount > 0) {
432 |         recommendations.push({
433 |           id: "connect_isolated_nodes",
434 |           priority: "medium",
435 |           action: `Review and connect ${metrics.structureHealth.isolatedNodeCount} isolated nodes`,
436 |           expectedImpact: 8,
437 |           effort: "medium",
438 |           category: "structure",
439 |         });
440 |       }
441 |     }
442 | 
443 |     // Performance recommendations
444 |     if (metrics.performance.score < 70) {
445 |       if (metrics.performance.storageSize > 50 * 1024 * 1024) {
446 |         recommendations.push({
447 |           id: "optimize_storage",
448 |           priority: "medium",
449 |           action: "Archive or compress old knowledge graph data",
450 |           expectedImpact: 12,
451 |           effort: "high",
452 |           category: "performance",
453 |         });
454 |       }
455 |     }
456 | 
457 |     // Sort by priority and expected impact
458 |     recommendations.sort((a, b) => {
459 |       const priorityOrder = { high: 0, medium: 1, low: 2 };
460 |       if (priorityOrder[a.priority] !== priorityOrder[b.priority]) {
461 |         return priorityOrder[a.priority] - priorityOrder[b.priority];
462 |       }
463 |       return b.expectedImpact - a.expectedImpact;
464 |     });
465 | 
466 |     return recommendations.slice(0, 5); // Top 5 recommendations
467 |   }
468 | 
469 |   /**
470 |    * Analyze trends from historical health data
471 |    */
472 |   private async analyzeTrends(currentHealth: number): Promise<HealthTrends> {
473 |     const history = await this.getHealthHistory(7); // Last 7 days
474 | 
475 |     if (history.length < 2) {
476 |       return {
477 |         healthTrend: "stable",
478 |         nodeGrowthRate: 0,
479 |         edgeGrowthRate: 0,
480 |         errorRate: 0,
481 |         qualityTrend: "stable",
482 |       };
483 |     }
484 | 
485 |     // Calculate health trend
486 |     const sevenDayAvg =
487 |       history.reduce((sum, h) => sum + h.overallHealth, 0) / history.length;
488 |     const healthDiff = currentHealth - sevenDayAvg;
489 | 
490 |     const healthTrend =
491 |       healthDiff > 5 ? "improving" : healthDiff < -5 ? "degrading" : "stable";
492 | 
493 |     // Calculate growth rates
494 |     const oldestEntry = history[history.length - 1];
495 |     const newestEntry = history[0];
496 |     const daysDiff = Math.max(
497 |       1,
498 |       (new Date(newestEntry.timestamp).getTime() -
499 |         new Date(oldestEntry.timestamp).getTime()) /
500 |         (1000 * 60 * 60 * 24),
501 |     );
502 | 
503 |     const nodeGrowthRate =
504 |       (newestEntry.nodeCount - oldestEntry.nodeCount) / daysDiff;
505 |     const edgeGrowthRate =
506 |       (newestEntry.edgeCount - oldestEntry.edgeCount) / daysDiff;
507 | 
508 |     // Quality trend
509 |     const qualityAvg =
510 |       history.reduce((sum, h) => sum + h.dataQuality, 0) / history.length;
511 |     const qualityDiff = history[0].dataQuality - qualityAvg;
512 | 
513 |     const qualityTrend =
514 |       qualityDiff > 5 ? "improving" : qualityDiff < -5 ? "degrading" : "stable";
515 | 
516 |     return {
517 |       healthTrend,
518 |       nodeGrowthRate: Math.round(nodeGrowthRate * 10) / 10,
519 |       edgeGrowthRate: Math.round(edgeGrowthRate * 10) / 10,
520 |       errorRate: 0, // TODO: Track from operations log
521 |       qualityTrend,
522 |     };
523 |   }
524 | 
525 |   /**
526 |    * Track health history to persistent storage
527 |    */
528 |   private async trackHealthHistory(metrics: KGHealthMetrics): Promise<void> {
529 |     const historyEntry: HealthHistory = {
530 |       timestamp: metrics.timestamp,
531 |       overallHealth: metrics.overallHealth,
532 |       dataQuality: metrics.dataQuality.score,
533 |       structureHealth: metrics.structureHealth.score,
534 |       performance: metrics.performance.score,
535 |       nodeCount: metrics.dataQuality.totalNodes,
536 |       edgeCount: metrics.dataQuality.totalEdges,
537 |     };
538 | 
539 |     try {
540 |       await fs.appendFile(
541 |         this.historyFilePath,
542 |         JSON.stringify(historyEntry) + "\n",
543 |         "utf-8",
544 |       );
545 | 
546 |       // Keep only last 90 days of history
547 |       await this.pruneHistoryFile(90);
548 |     } catch (error) {
549 |       console.warn("Failed to track health history:", error);
550 |     }
551 |   }
552 | 
553 |   /**
554 |    * Get health history for the last N days
555 |    */
556 |   private async getHealthHistory(days: number): Promise<HealthHistory[]> {
557 |     try {
558 |       const content = await fs.readFile(this.historyFilePath, "utf-8");
559 |       const lines = content.trim().split("\n");
560 | 
561 |       const cutoffDate = new Date();
562 |       cutoffDate.setDate(cutoffDate.getDate() - days);
563 | 
564 |       const history: HealthHistory[] = [];
565 |       for (const line of lines) {
566 |         if (line.trim()) {
567 |           const entry = JSON.parse(line) as HealthHistory;
568 |           if (new Date(entry.timestamp) >= cutoffDate) {
569 |             history.push(entry);
570 |           }
571 |         }
572 |       }
573 | 
574 |       return history.reverse(); // Most recent first
575 |     } catch {
576 |       return [];
577 |     }
578 |   }
579 | 
580 |   /**
581 |    * Prune history file to keep only last N days
582 |    */
583 |   private async pruneHistoryFile(days: number): Promise<void> {
584 |     try {
585 |       const history = await this.getHealthHistory(days);
586 |       const content = history.map((h) => JSON.stringify(h)).join("\n") + "\n";
587 |       await fs.writeFile(this.historyFilePath, content, "utf-8");
588 |     } catch (error) {
589 |       console.warn("Failed to prune history file:", error);
590 |     }
591 |   }
592 | 
593 |   // Helper methods
594 | 
595 |   private calculateCompleteness(
596 |     nodes: GraphNode[],
597 |     edges: GraphEdge[],
598 |   ): number {
599 |     const projectNodes = nodes.filter((n) => n.type === "project");
600 |     if (projectNodes.length === 0) return 1.0;
601 | 
602 |     let totalExpected = 0;
603 |     let totalFound = 0;
604 | 
605 |     for (const project of projectNodes) {
606 |       // Expected relationships for each project:
607 |       // 1. At least one technology relationship
608 |       // 2. Documentation relationship (if hasDocs = true)
609 |       // 3. Configuration relationship (if deployed)
610 | 
611 |       totalExpected += 1; // Technology
612 | 
613 |       const projectEdges = edges.filter((e) => e.source === project.id);
614 | 
615 |       if (projectEdges.some((e) => e.type === "project_uses_technology")) {
616 |         totalFound += 1;
617 |       }
618 | 
619 |       if (project.properties.hasDocs) {
620 |         totalExpected += 1;
621 |         if (
622 |           projectEdges.some(
623 |             (e) =>
624 |               e.type === "depends_on" &&
625 |               nodes.find((n) => n.id === e.target)?.type ===
626 |                 "documentation_section",
627 |           )
628 |         ) {
629 |           totalFound += 1;
630 |         }
631 |       }
632 |     }
633 | 
634 |     return totalExpected > 0 ? totalFound / totalExpected : 1.0;
635 |   }
636 | 
637 |   private calculateClusteringCoefficient(
638 |     nodes: GraphNode[],
639 |     edges: GraphEdge[],
640 |   ): number {
641 |     // Simplified clustering coefficient calculation
642 |     if (nodes.length < 3) return 0;
643 | 
644 |     const adjacency = new Map<string, Set<string>>();
645 |     for (const edge of edges) {
646 |       if (!adjacency.has(edge.source)) {
647 |         adjacency.set(edge.source, new Set());
648 |       }
649 |       adjacency.get(edge.source)!.add(edge.target);
650 |     }
651 | 
652 |     let totalCoefficient = 0;
653 |     let nodeCount = 0;
654 | 
655 |     for (const node of nodes.slice(0, 100)) {
656 |       // Sample first 100 nodes
657 |       const neighbors = adjacency.get(node.id);
658 |       if (!neighbors || neighbors.size < 2) continue;
659 | 
660 |       const neighborArray = Array.from(neighbors);
661 |       let triangles = 0;
662 |       const possibleTriangles =
663 |         (neighborArray.length * (neighborArray.length - 1)) / 2;
664 | 
665 |       for (let i = 0; i < neighborArray.length; i++) {
666 |         for (let j = i + 1; j < neighborArray.length; j++) {
667 |           const n1Neighbors = adjacency.get(neighborArray[i]);
668 |           if (n1Neighbors?.has(neighborArray[j])) {
669 |             triangles++;
670 |           }
671 |         }
672 |       }
673 | 
674 |       if (possibleTriangles > 0) {
675 |         totalCoefficient += triangles / possibleTriangles;
676 |         nodeCount++;
677 |       }
678 |     }
679 | 
680 |     return nodeCount > 0 ? totalCoefficient / nodeCount : 0;
681 |   }
682 | 
683 |   private calculateAveragePathLength(
684 |     nodes: GraphNode[],
685 |     edges: GraphEdge[],
686 |   ): number {
687 |     // Simplified using sample BFS
688 |     if (nodes.length === 0) return 0;
689 | 
690 |     const adjacency = new Map<string, string[]>();
691 |     for (const edge of edges) {
692 |       if (!adjacency.has(edge.source)) {
693 |         adjacency.set(edge.source, []);
694 |       }
695 |       adjacency.get(edge.source)!.push(edge.target);
696 |     }
697 | 
698 |     // Sample 10 random nodes for BFS
699 |     const sampleSize = Math.min(10, nodes.length);
700 |     let totalPathLength = 0;
701 |     let pathCount = 0;
702 | 
703 |     for (let i = 0; i < sampleSize; i++) {
704 |       const startNode = nodes[i];
705 |       const distances = new Map<string, number>();
706 |       const queue = [startNode.id];
707 |       distances.set(startNode.id, 0);
708 | 
709 |       while (queue.length > 0) {
710 |         const current = queue.shift()!;
711 |         const currentDist = distances.get(current)!;
712 | 
713 |         const neighbors = adjacency.get(current) || [];
714 |         for (const neighbor of neighbors) {
715 |           if (!distances.has(neighbor)) {
716 |             distances.set(neighbor, currentDist + 1);
717 |             queue.push(neighbor);
718 |           }
719 |         }
720 |       }
721 | 
722 |       for (const dist of distances.values()) {
723 |         if (dist > 0) {
724 |           totalPathLength += dist;
725 |           pathCount++;
726 |         }
727 |       }
728 |     }
729 | 
730 |     return pathCount > 0 ? totalPathLength / pathCount : 0;
731 |   }
732 | 
733 |   private countConnectedComponents(
734 |     nodes: GraphNode[],
735 |     edges: GraphEdge[],
736 |   ): number {
737 |     if (nodes.length === 0) return 0;
738 | 
739 |     const adjacency = new Map<string, Set<string>>();
740 |     for (const edge of edges) {
741 |       if (!adjacency.has(edge.source)) {
742 |         adjacency.set(edge.source, new Set());
743 |       }
744 |       if (!adjacency.has(edge.target)) {
745 |         adjacency.set(edge.target, new Set());
746 |       }
747 |       adjacency.get(edge.source)!.add(edge.target);
748 |       adjacency.get(edge.target)!.add(edge.source);
749 |     }
750 | 
751 |     const visited = new Set<string>();
752 |     let components = 0;
753 | 
754 |     for (const node of nodes) {
755 |       if (!visited.has(node.id)) {
756 |         components++;
757 |         const queue = [node.id];
758 | 
759 |         while (queue.length > 0) {
760 |           const current = queue.shift()!;
761 |           if (visited.has(current)) continue;
762 | 
763 |           visited.add(current);
764 |           const neighbors = adjacency.get(current) || new Set();
765 |           for (const neighbor of neighbors) {
766 |             if (!visited.has(neighbor)) {
767 |               queue.push(neighbor);
768 |             }
769 |           }
770 |         }
771 |       }
772 |     }
773 | 
774 |     return components;
775 |   }
776 | 
777 |   private async calculateGrowthRate(): Promise<number> {
778 |     const history = await this.getHealthHistory(30);
779 |     if (history.length < 2) return 0;
780 | 
781 |     // Calculate storage size growth (simplified)
782 |     return 1024; // Placeholder: 1KB/day
783 |   }
784 | }
785 | 
786 | // ============================================================================
787 | // Issue Detectors
788 | // ============================================================================
789 | 
790 | interface IssueDetector {
791 |   name: string;
792 |   detect(
793 |     kg: KnowledgeGraph,
794 |     metrics: {
795 |       dataQuality: DataQualityMetrics;
796 |       structureHealth: StructureHealthMetrics;
797 |       performance: PerformanceMetrics;
798 |     },
799 |   ): Promise<HealthIssue[]>;
800 | }
801 | 
802 | function createIssueDetectors(): IssueDetector[] {
803 |   return [
804 |     {
805 |       name: "orphaned_edges",
806 |       async detect(kg, metrics) {
807 |         if (metrics.dataQuality.orphanedEdgeCount > 10) {
808 |           return [
809 |             {
810 |               id: "orphaned_edges_high",
811 |               severity: "high",
812 |               category: "integrity",
813 |               description: `Found ${metrics.dataQuality.orphanedEdgeCount} orphaned relationships`,
814 |               affectedEntities: [],
815 |               remediation: "Run kg.removeOrphanedEdges() to clean up",
816 |               detectedAt: new Date().toISOString(),
817 |               autoFixable: true,
818 |             },
819 |           ];
820 |         }
821 |         return [];
822 |       },
823 |     },
824 |     {
825 |       name: "stale_data",
826 |       async detect(kg, metrics) {
827 |         if (metrics.dataQuality.staleNodeCount > 20) {
828 |           return [
829 |             {
830 |               id: "stale_data_high",
831 |               severity: "medium",
832 |               category: "quality",
833 |               description: `${metrics.dataQuality.staleNodeCount} nodes haven't been updated in 30+ days`,
834 |               affectedEntities: [],
835 |               remediation: "Re-analyze stale projects to refresh data",
836 |               detectedAt: new Date().toISOString(),
837 |               autoFixable: false,
838 |             },
839 |           ];
840 |         }
841 |         return [];
842 |       },
843 |     },
844 |     {
845 |       name: "low_completeness",
846 |       async detect(kg, metrics) {
847 |         if (metrics.dataQuality.completenessScore < 0.7) {
848 |           return [
849 |             {
850 |               id: "low_completeness",
851 |               severity: "high",
852 |               category: "quality",
853 |               description: `Completeness score is ${Math.round(
854 |                 metrics.dataQuality.completenessScore * 100,
855 |               )}%`,
856 |               affectedEntities: [],
857 |               remediation: "Review projects for missing relationships",
858 |               detectedAt: new Date().toISOString(),
859 |               autoFixable: false,
860 |             },
861 |           ];
862 |         }
863 |         return [];
864 |       },
865 |     },
866 |     {
867 |       name: "isolated_nodes",
868 |       async detect(kg, metrics) {
869 |         const threshold = metrics.structureHealth.isolatedNodeCount;
870 |         if (threshold > metrics.dataQuality.totalNodes * 0.05) {
871 |           return [
872 |             {
873 |               id: "isolated_nodes_high",
874 |               severity: "medium",
875 |               category: "structure",
876 |               description: `${threshold} nodes are isolated (no connections)`,
877 |               affectedEntities: [],
878 |               remediation: "Review and connect isolated nodes",
879 |               detectedAt: new Date().toISOString(),
880 |               autoFixable: false,
881 |             },
882 |           ];
883 |         }
884 |         return [];
885 |       },
886 |     },
887 |     {
888 |       name: "duplicate_entities",
889 |       async detect(kg, metrics) {
890 |         if (metrics.dataQuality.duplicateCount > 0) {
891 |           return [
892 |             {
893 |               id: "duplicate_entities",
894 |               severity: "critical",
895 |               category: "integrity",
896 |               description: `Found ${metrics.dataQuality.duplicateCount} duplicate entities`,
897 |               affectedEntities: [],
898 |               remediation: "Merge duplicate entities",
899 |               detectedAt: new Date().toISOString(),
900 |               autoFixable: false,
901 |             },
902 |           ];
903 |         }
904 |         return [];
905 |       },
906 |     },
907 |   ];
908 | }
909 | 
910 | // ============================================================================
911 | // Performance Tracker
912 | // ============================================================================
913 | 
914 | class PerformanceTracker {
915 |   private queryTimes: number[] = [];
916 |   private maxSamples = 100;
917 | 
918 |   trackQuery(timeMs: number): void {
919 |     this.queryTimes.push(timeMs);
920 |     if (this.queryTimes.length > this.maxSamples) {
921 |       this.queryTimes.shift();
922 |     }
923 |   }
924 | 
925 |   getAverageQueryTime(): number {
926 |     if (this.queryTimes.length === 0) return 0;
927 |     return (
928 |       this.queryTimes.reduce((sum, t) => sum + t, 0) / this.queryTimes.length
929 |     );
930 |   }
931 | }
932 | 
```

--------------------------------------------------------------------------------
/src/tools/readme-best-practices.ts:
--------------------------------------------------------------------------------

```typescript
   1 | import { readFile, writeFile, mkdir } from "fs/promises";
   2 | import { join } from "path";
   3 | import { z } from "zod";
   4 | import { MCPToolResponse } from "../types/api.js";
   5 | 
   6 | // Input validation schema
   7 | const ReadmeBestPracticesInputSchema = z.object({
   8 |   readme_path: z.string().describe("Path to the README file to analyze"),
   9 |   project_type: z
  10 |     .enum(["library", "application", "tool", "documentation", "framework"])
  11 |     .optional()
  12 |     .default("library")
  13 |     .describe("Type of project for tailored analysis"),
  14 |   generate_template: z
  15 |     .boolean()
  16 |     .optional()
  17 |     .default(false)
  18 |     .describe("Generate README templates and community files"),
  19 |   output_directory: z
  20 |     .string()
  21 |     .optional()
  22 |     .describe("Directory to write generated templates and community files"),
  23 |   include_community_files: z
  24 |     .boolean()
  25 |     .optional()
  26 |     .default(true)
  27 |     .describe(
  28 |       "Generate community health files (CONTRIBUTING.md, CODE_OF_CONDUCT.md, etc.)",
  29 |     ),
  30 |   target_audience: z
  31 |     .enum(["beginner", "intermediate", "advanced", "mixed"])
  32 |     .optional()
  33 |     .default("mixed")
  34 |     .describe("Target audience for recommendations"),
  35 | });
  36 | 
  37 | type ReadmeBestPracticesInput = z.infer<typeof ReadmeBestPracticesInputSchema>;
  38 | 
  39 | interface ChecklistItem {
  40 |   category: string;
  41 |   item: string;
  42 |   present: boolean;
  43 |   severity: "critical" | "important" | "recommended";
  44 |   description: string;
  45 |   example?: string;
  46 | }
  47 | 
  48 | interface BestPracticesReport {
  49 |   overallScore: number;
  50 |   grade: string;
  51 |   checklist: ChecklistItem[];
  52 |   recommendations: string[];
  53 |   templates: Record<string, string>;
  54 |   communityFiles: Record<string, string>;
  55 |   summary: {
  56 |     criticalIssues: number;
  57 |     importantIssues: number;
  58 |     recommendedImprovements: number;
  59 |     sectionsPresent: number;
  60 |     totalSections: number;
  61 |     estimatedImprovementTime: string;
  62 |   };
  63 | }
  64 | 
  65 | export async function readmeBestPractices(
  66 |   input: Partial<ReadmeBestPracticesInput>,
  67 | ): Promise<
  68 |   MCPToolResponse<{
  69 |     bestPracticesReport: BestPracticesReport;
  70 |     recommendations: string[];
  71 |     nextSteps: string[];
  72 |   }>
  73 | > {
  74 |   const startTime = Date.now();
  75 | 
  76 |   try {
  77 |     // Validate input with defaults
  78 |     const validatedInput = ReadmeBestPracticesInputSchema.parse(input);
  79 |     const {
  80 |       readme_path,
  81 |       project_type,
  82 |       generate_template,
  83 |       output_directory,
  84 |       include_community_files,
  85 |       target_audience,
  86 |     } = validatedInput;
  87 | 
  88 |     // Read README content
  89 |     let readmeContent = "";
  90 |     try {
  91 |       readmeContent = await readFile(readme_path, "utf-8");
  92 |     } catch (error) {
  93 |       if (!generate_template) {
  94 |         return {
  95 |           success: false,
  96 |           error: {
  97 |             code: "README_NOT_FOUND",
  98 |             message:
  99 |               "README file not found. Use generate_template: true to create a new README.",
 100 |             details: error instanceof Error ? error.message : "Unknown error",
 101 |             resolution:
 102 |               "Set generate_template: true to create a new README from template",
 103 |           },
 104 |           metadata: {
 105 |             toolVersion: "1.0.0",
 106 |             executionTime: Date.now() - startTime,
 107 |             timestamp: new Date().toISOString(),
 108 |           },
 109 |         };
 110 |       }
 111 |     }
 112 | 
 113 |     // Generate checklist based on project type and content
 114 |     const checklist = generateChecklist(
 115 |       readmeContent,
 116 |       project_type,
 117 |       target_audience,
 118 |     );
 119 | 
 120 |     // Calculate overall score
 121 |     const { score, grade } = calculateOverallScore(checklist);
 122 | 
 123 |     // Generate recommendations
 124 |     const recommendations = generateRecommendations(
 125 |       checklist,
 126 |       project_type,
 127 |       target_audience,
 128 |     );
 129 | 
 130 |     // Generate templates if requested
 131 |     const templates = generate_template
 132 |       ? generateTemplates(project_type, generate_template)
 133 |       : {};
 134 | 
 135 |     // Generate community files if requested
 136 |     const communityFiles = include_community_files
 137 |       ? generateCommunityFiles(project_type)
 138 |       : {};
 139 | 
 140 |     // Calculate summary metrics
 141 |     const summary = calculateSummaryMetrics(checklist);
 142 | 
 143 |     // Write files if output directory specified
 144 |     if (output_directory && generate_template) {
 145 |       await writeGeneratedFiles(
 146 |         templates,
 147 |         communityFiles,
 148 |         output_directory,
 149 |         readme_path,
 150 |       );
 151 |     }
 152 | 
 153 |     const report: BestPracticesReport = {
 154 |       overallScore: score,
 155 |       grade,
 156 |       checklist,
 157 |       recommendations,
 158 |       templates,
 159 |       communityFiles,
 160 |       summary,
 161 |     };
 162 | 
 163 |     const nextSteps = generateNextSteps(
 164 |       report.checklist,
 165 |       true,
 166 |       output_directory,
 167 |     );
 168 | 
 169 |     return {
 170 |       success: true,
 171 |       data: {
 172 |         bestPracticesReport: report,
 173 |         recommendations,
 174 |         nextSteps,
 175 |       },
 176 |       metadata: {
 177 |         toolVersion: "1.0.0",
 178 |         executionTime: Date.now() - startTime,
 179 |         timestamp: new Date().toISOString(),
 180 |         analysisId: `readme-best-practices-${Date.now()}`,
 181 |       },
 182 |     };
 183 |   } catch (error) {
 184 |     return {
 185 |       success: false,
 186 |       error: {
 187 |         code: "ANALYSIS_FAILED",
 188 |         message: "Failed to analyze README best practices",
 189 |         details: error instanceof Error ? error.message : "Unknown error",
 190 |         resolution:
 191 |           "Check README file path and permissions, ensure valid project type",
 192 |       },
 193 |       metadata: {
 194 |         toolVersion: "1.0.0",
 195 |         executionTime: Date.now() - startTime,
 196 |         timestamp: new Date().toISOString(),
 197 |       },
 198 |     };
 199 |   }
 200 | }
 201 | 
 202 | function generateChecklist(
 203 |   content: string,
 204 |   projectType: string,
 205 |   _targetAudience: string,
 206 | ): ChecklistItem[] {
 207 |   const checklist: ChecklistItem[] = [];
 208 |   const lines = content.split("\n");
 209 |   const lowerContent = content.toLowerCase();
 210 | 
 211 |   // Essential Sections
 212 |   checklist.push({
 213 |     category: "Essential Sections",
 214 |     item: "Project Title",
 215 |     present: /^#\s+.+/m.test(content),
 216 |     severity: "critical",
 217 |     description: "Clear, descriptive project title as main heading",
 218 |     example: "# My Awesome Project",
 219 |   });
 220 | 
 221 |   checklist.push({
 222 |     category: "Essential Sections",
 223 |     item: "One-line Description",
 224 |     present:
 225 |       />\s*.+/.test(content) ||
 226 |       lines.some(
 227 |         (line) =>
 228 |           line.trim().length > 20 &&
 229 |           line.trim().length < 100 &&
 230 |           !line.startsWith("#"),
 231 |       ),
 232 |     severity: "critical",
 233 |     description: "Brief one-line description of what the project does",
 234 |     example:
 235 |       "> A fast, lightweight JavaScript framework for building web applications",
 236 |   });
 237 | 
 238 |   checklist.push({
 239 |     category: "Essential Sections",
 240 |     item: "Installation Instructions",
 241 |     present:
 242 |       /install/i.test(lowerContent) &&
 243 |       /npm|yarn|pip|cargo|go get|git clone/i.test(lowerContent),
 244 |     severity: "critical",
 245 |     description: "Clear installation or setup instructions",
 246 |     example: "```bash\nnpm install package-name\n```",
 247 |   });
 248 | 
 249 |   checklist.push({
 250 |     category: "Essential Sections",
 251 |     item: "Basic Usage Example",
 252 |     present:
 253 |       /usage|example|quick start|getting started/i.test(lowerContent) &&
 254 |       /```/.test(content),
 255 |     severity: "critical",
 256 |     description: "Working code example showing basic usage",
 257 |     example:
 258 |       '```javascript\nconst lib = require("package-name");\nlib.doSomething();\n```',
 259 |   });
 260 | 
 261 |   // Important Sections
 262 |   checklist.push({
 263 |     category: "Important Sections",
 264 |     item: "Prerequisites/Requirements",
 265 |     present:
 266 |       /prerequisite|requirement|dependencies|node|python|java|version/i.test(
 267 |         lowerContent,
 268 |       ),
 269 |     severity: "important",
 270 |     description: "Clear system requirements and dependencies",
 271 |     example: "- Node.js 16+\n- Docker (optional)",
 272 |   });
 273 | 
 274 |   checklist.push({
 275 |     category: "Important Sections",
 276 |     item: "License Information",
 277 |     present:
 278 |       /license/i.test(lowerContent) || /mit|apache|gpl|bsd/i.test(lowerContent),
 279 |     severity: "important",
 280 |     description: "Clear license information",
 281 |     example: "## License\n\nMIT License - see [LICENSE](LICENSE) file",
 282 |   });
 283 | 
 284 |   checklist.push({
 285 |     category: "Important Sections",
 286 |     item: "Contributing Guidelines",
 287 |     present: /contribut/i.test(lowerContent),
 288 |     severity: "important",
 289 |     description: "Information on how to contribute to the project",
 290 |     example: "See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines",
 291 |   });
 292 | 
 293 |   // Community Health
 294 |   checklist.push({
 295 |     category: "Community Health",
 296 |     item: "Code of Conduct",
 297 |     present: /code of conduct/i.test(lowerContent),
 298 |     severity: "recommended",
 299 |     description: "Link to code of conduct for community projects",
 300 |     example: "Please read our [Code of Conduct](CODE_OF_CONDUCT.md)",
 301 |   });
 302 | 
 303 |   checklist.push({
 304 |     category: "Community Health",
 305 |     item: "Issue Templates",
 306 |     present: /issue template|bug report|feature request/i.test(lowerContent),
 307 |     severity: "recommended",
 308 |     description: "Reference to issue templates for better bug reports",
 309 |     example:
 310 |       "Use our [issue templates](.github/ISSUE_TEMPLATE/) when reporting bugs",
 311 |   });
 312 | 
 313 |   // Visual Elements
 314 |   checklist.push({
 315 |     category: "Visual Elements",
 316 |     item: "Badges",
 317 |     present:
 318 |       /\[!\[.*\]\(.*\)\]\(.*\)/.test(content) || /badge/i.test(lowerContent),
 319 |     severity: "recommended",
 320 |     description: "Status badges for build, version, license, etc.",
 321 |     example: "[![Build Status](badge-url)](link-url)",
 322 |   });
 323 | 
 324 |   checklist.push({
 325 |     category: "Visual Elements",
 326 |     item: "Screenshots/Demo",
 327 |     present:
 328 |       /!\[.*\]\(.*\.(png|jpg|jpeg|gif|webp)\)/i.test(content) ||
 329 |       /screenshot|demo|gif/i.test(lowerContent),
 330 |     severity:
 331 |       projectType === "application" || projectType === "tool"
 332 |         ? "important"
 333 |         : "recommended",
 334 |     description:
 335 |       "Visual demonstration of the project (especially for applications)",
 336 |     example: "![Demo](demo.gif)",
 337 |   });
 338 | 
 339 |   // Content Quality
 340 |   checklist.push({
 341 |     category: "Content Quality",
 342 |     item: "Appropriate Length",
 343 |     present: lines.length >= 20 && lines.length <= 300,
 344 |     severity: "important",
 345 |     description:
 346 |       "README length appropriate for project complexity (20-300 lines)",
 347 |     example: "Keep main README focused, link to detailed docs",
 348 |   });
 349 | 
 350 |   checklist.push({
 351 |     category: "Content Quality",
 352 |     item: "Clear Section Headers",
 353 |     present: (content.match(/^##\s+/gm) || []).length >= 3,
 354 |     severity: "important",
 355 |     description: "Well-organized content with clear section headers",
 356 |     example: "## Installation\n## Usage\n## Contributing",
 357 |   });
 358 | 
 359 |   checklist.push({
 360 |     category: "Content Quality",
 361 |     item: "Working Links",
 362 |     present: !/\[.*\]\(\)/.test(content) && !/\[.*\]\(#\)/.test(content),
 363 |     severity: "important",
 364 |     description:
 365 |       "All links should be functional (no empty or placeholder links)",
 366 |     example: "[Documentation](https://example.com/docs)",
 367 |   });
 368 | 
 369 |   // Project-specific checks
 370 |   if (projectType === "library" || projectType === "framework") {
 371 |     checklist.push({
 372 |       category: "Library Specific",
 373 |       item: "API Documentation",
 374 |       present: /api|methods|functions|reference/i.test(lowerContent),
 375 |       severity: "important",
 376 |       description: "API documentation or link to detailed API reference",
 377 |       example:
 378 |         "See [API Documentation](docs/api.md) for detailed method reference",
 379 |     });
 380 |   }
 381 | 
 382 |   if (projectType === "application" || projectType === "tool") {
 383 |     checklist.push({
 384 |       category: "Application Specific",
 385 |       item: "Configuration Options",
 386 |       present: /config|settings|options|environment/i.test(lowerContent),
 387 |       severity: "important",
 388 |       description: "Configuration and customization options",
 389 |       example: "See [Configuration Guide](docs/configuration.md)",
 390 |     });
 391 |   }
 392 | 
 393 |   return checklist;
 394 | }
 395 | 
 396 | function calculateOverallScore(checklist: ChecklistItem[]): {
 397 |   score: number;
 398 |   grade: string;
 399 | } {
 400 |   const weights = { critical: 3, important: 2, recommended: 1 };
 401 |   let totalScore = 0;
 402 |   let maxScore = 0;
 403 | 
 404 |   checklist.forEach((item) => {
 405 |     const weight = weights[item.severity];
 406 |     maxScore += weight;
 407 |     if (item.present) {
 408 |       totalScore += weight;
 409 |     }
 410 |   });
 411 | 
 412 |   const percentage =
 413 |     maxScore > 0 ? Math.round((totalScore / maxScore) * 100) : 0;
 414 | 
 415 |   let grade: string;
 416 |   if (percentage >= 90) grade = "A";
 417 |   else if (percentage >= 80) grade = "B";
 418 |   else if (percentage >= 70) grade = "C";
 419 |   else if (percentage >= 60) grade = "D";
 420 |   else grade = "F";
 421 | 
 422 |   return { score: percentage, grade };
 423 | }
 424 | 
 425 | function generateRecommendations(
 426 |   checklist: ChecklistItem[],
 427 |   projectType: string,
 428 |   targetAudience: string,
 429 | ): string[] {
 430 |   const recommendations: string[] = [];
 431 |   const missing = checklist.filter((item) => !item.present);
 432 | 
 433 |   // Critical issues first
 434 |   const critical = missing.filter((item) => item.severity === "critical");
 435 |   if (critical.length > 0) {
 436 |     recommendations.push(
 437 |       `🚨 Critical: Fix ${critical.length} essential sections: ${critical
 438 |         .map((item) => item.item)
 439 |         .join(", ")}`,
 440 |     );
 441 |   }
 442 | 
 443 |   // Important issues
 444 |   const important = missing.filter((item) => item.severity === "important");
 445 |   if (important.length > 0) {
 446 |     recommendations.push(
 447 |       `⚠️ Important: Add ${important.length} key sections: ${important
 448 |         .map((item) => item.item)
 449 |         .join(", ")}`,
 450 |     );
 451 |   }
 452 | 
 453 |   // Project-specific recommendations
 454 |   if (projectType === "library") {
 455 |     recommendations.push(
 456 |       "📚 Library Focus: Emphasize installation, basic usage, and API documentation",
 457 |     );
 458 |   } else if (projectType === "application") {
 459 |     recommendations.push(
 460 |       "🖥️ Application Focus: Include screenshots, configuration options, and deployment guides",
 461 |     );
 462 |   }
 463 | 
 464 |   // Target audience specific recommendations
 465 |   if (targetAudience === "beginner") {
 466 |     recommendations.push(
 467 |       "👶 Beginner-Friendly: Use simple language, provide detailed examples, include troubleshooting",
 468 |     );
 469 |   } else if (targetAudience === "advanced") {
 470 |     recommendations.push(
 471 |       "🎯 Advanced Users: Focus on technical details, performance notes, and extensibility",
 472 |     );
 473 |   }
 474 | 
 475 |   // General improvements
 476 |   const recommended = missing.filter((item) => item.severity === "recommended");
 477 |   if (recommended.length > 0) {
 478 |     recommendations.push(
 479 |       `✨ Enhancement: Consider adding ${recommended
 480 |         .map((item) => item.item)
 481 |         .join(", ")}`,
 482 |     );
 483 |   }
 484 | 
 485 |   return recommendations;
 486 | }
 487 | 
 488 | function generateTemplates(
 489 |   projectType: string,
 490 |   _generateTemplate: boolean,
 491 | ): Record<string, string> {
 492 |   const templates: Record<string, string> = {};
 493 | 
 494 |   if (projectType === "library") {
 495 |     templates["README-library.md"] = `# Project Name
 496 | 
 497 | > One-line description of what this library does
 498 | 
 499 | [![Build Status][build-badge]][build-link]
 500 | [![npm version][npm-badge]][npm-link]
 501 | [![License][license-badge]][license-link]
 502 | 
 503 | ## TL;DR
 504 | 
 505 | What it does in 2-3 sentences. Who should use it.
 506 | 
 507 | ## Quick Start
 508 | 
 509 | ### Install
 510 | \`\`\`bash
 511 | npm install package-name
 512 | \`\`\`
 513 | 
 514 | ### Use
 515 | \`\`\`javascript
 516 | const lib = require('package-name');
 517 | 
 518 | // Basic usage example
 519 | const result = lib.doSomething();
 520 | console.log(result);
 521 | \`\`\`
 522 | 
 523 | ## When to Use This
 524 | 
 525 | - ✅ When you need X functionality
 526 | - ✅ When you want Y capability
 527 | - ❌ When you need Z (use [alternative] instead)
 528 | 
 529 | ## API Reference
 530 | 
 531 | ### \`doSomething(options)\`
 532 | 
 533 | Description of the main method.
 534 | 
 535 | **Parameters:**
 536 | - \`options\` (Object): Configuration options
 537 |   - \`param1\` (string): Description of parameter
 538 |   - \`param2\` (boolean, optional): Description of optional parameter
 539 | 
 540 | **Returns:** Description of return value
 541 | 
 542 | **Example:**
 543 | \`\`\`javascript
 544 | const result = lib.doSomething({
 545 |   param1: 'value',
 546 |   param2: true
 547 | });
 548 | \`\`\`
 549 | 
 550 | ## Full Documentation
 551 | 
 552 | [Link to full documentation](docs/)
 553 | 
 554 | ## Contributing
 555 | 
 556 | We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
 557 | 
 558 | ## License
 559 | 
 560 | MIT License - see [LICENSE](LICENSE) file for details.
 561 | 
 562 | [build-badge]: https://github.com/username/repo/workflows/CI/badge.svg
 563 | [build-link]: https://github.com/username/repo/actions
 564 | [npm-badge]: https://img.shields.io/npm/v/package-name.svg
 565 | [npm-link]: https://www.npmjs.com/package/package-name
 566 | [license-badge]: https://img.shields.io/badge/license-MIT-blue.svg
 567 | [license-link]: LICENSE
 568 | `;
 569 |   }
 570 | 
 571 |   if (projectType === "application" || projectType === "tool") {
 572 |     templates["README-application.md"] = `# Project Name
 573 | 
 574 | > One-line description of what this application does
 575 | 
 576 | ![Demo](demo.gif)
 577 | 
 578 | ## What This Does
 579 | 
 580 | Brief explanation of the application's purpose and key features:
 581 | 
 582 | - 🚀 Feature 1: Description
 583 | - 📊 Feature 2: Description
 584 | - 🔧 Feature 3: Description
 585 | 
 586 | ## Quick Start
 587 | 
 588 | ### Prerequisites
 589 | - Node.js 16+
 590 | - Docker (optional)
 591 | - Other requirements
 592 | 
 593 | ### Install & Run
 594 | \`\`\`bash
 595 | git clone https://github.com/username/repo.git
 596 | cd project-name
 597 | npm install
 598 | npm start
 599 | \`\`\`
 600 | 
 601 | Visit \`http://localhost:3000\` to see the application.
 602 | 
 603 | ## Configuration
 604 | 
 605 | ### Environment Variables
 606 | \`\`\`bash
 607 | # Copy example config
 608 | cp .env.example .env
 609 | 
 610 | # Edit configuration
 611 | nano .env
 612 | \`\`\`
 613 | 
 614 | ### Key Settings
 615 | - \`PORT\`: Server port (default: 3000)
 616 | - \`DATABASE_URL\`: Database connection string
 617 | - \`API_KEY\`: External service API key
 618 | 
 619 | ## Usage Examples
 620 | 
 621 | ### Basic Usage
 622 | \`\`\`bash
 623 | npm run command -- --option value
 624 | \`\`\`
 625 | 
 626 | ### Advanced Usage
 627 | \`\`\`bash
 628 | npm run command -- --config custom.json --verbose
 629 | \`\`\`
 630 | 
 631 | ## Deployment
 632 | 
 633 | See [Deployment Guide](docs/deployment.md) for production setup.
 634 | 
 635 | ## Troubleshooting
 636 | 
 637 | ### Common Issues
 638 | 
 639 | **Issue 1: Error message**
 640 | - Solution: Steps to resolve
 641 | 
 642 | **Issue 2: Another error**
 643 | - Solution: Steps to resolve
 644 | 
 645 | See [FAQ](docs/FAQ.md) for more help.
 646 | 
 647 | ## Contributing
 648 | 
 649 | We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
 650 | 
 651 | ## License
 652 | 
 653 | MIT License - see [LICENSE](LICENSE) file for details.
 654 | `;
 655 |   }
 656 | 
 657 |   return templates;
 658 | }
 659 | 
 660 | function generateCommunityFiles(_projectType: string): Record<string, string> {
 661 |   const files: Record<string, string> = {};
 662 | 
 663 |   files["CONTRIBUTING.md"] = `# Contributing to Project Name
 664 | 
 665 | Thank you for your interest in contributing! This document provides guidelines for contributing to this project.
 666 | 
 667 | ## Getting Started
 668 | 
 669 | 1. Fork the repository
 670 | 2. Clone your fork: \`git clone https://github.com/yourusername/repo.git\`
 671 | 3. Create a feature branch: \`git checkout -b feature-name\`
 672 | 4. Make your changes
 673 | 5. Test your changes: \`npm test\`
 674 | 6. Commit your changes: \`git commit -m "Description of changes"\`
 675 | 7. Push to your fork: \`git push origin feature-name\`
 676 | 8. Create a Pull Request
 677 | 
 678 | ## Development Setup
 679 | 
 680 | \`\`\`bash
 681 | npm install
 682 | npm run dev
 683 | \`\`\`
 684 | 
 685 | ## Code Style
 686 | 
 687 | - Use TypeScript for new code
 688 | - Follow existing code formatting
 689 | - Run \`npm run lint\` before committing
 690 | - Add tests for new features
 691 | 
 692 | ## Pull Request Guidelines
 693 | 
 694 | - Keep PRs focused and small
 695 | - Include tests for new functionality
 696 | - Update documentation as needed
 697 | - Ensure CI passes
 698 | - Link to relevant issues
 699 | 
 700 | ## Reporting Issues
 701 | 
 702 | Use our [issue templates](.github/ISSUE_TEMPLATE/) when reporting bugs or requesting features.
 703 | 
 704 | ## Code of Conduct
 705 | 
 706 | Please read and follow our [Code of Conduct](CODE_OF_CONDUCT.md).
 707 | `;
 708 | 
 709 |   files["CODE_OF_CONDUCT.md"] = `# Code of Conduct
 710 | 
 711 | ## Our Pledge
 712 | 
 713 | We pledge to make participation in our project a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
 714 | 
 715 | ## Our Standards
 716 | 
 717 | Examples of behavior that contributes to creating a positive environment include:
 718 | 
 719 | - Using welcoming and inclusive language
 720 | - Being respectful of differing viewpoints and experiences
 721 | - Gracefully accepting constructive criticism
 722 | - Focusing on what is best for the community
 723 | - Showing empathy towards other community members
 724 | 
 725 | Examples of unacceptable behavior include:
 726 | 
 727 | - The use of sexualized language or imagery and unwelcome sexual attention or advances
 728 | - Trolling, insulting/derogatory comments, and personal or political attacks
 729 | - Public or private harassment
 730 | - Publishing others' private information without explicit permission
 731 | - Other conduct which could reasonably be considered inappropriate in a professional setting
 732 | 
 733 | ## Enforcement
 734 | 
 735 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
 736 | 
 737 | ## Attribution
 738 | 
 739 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 1.4.
 740 | `;
 741 | 
 742 |   files["SECURITY.md"] = `# Security Policy
 743 | 
 744 | ## Supported Versions
 745 | 
 746 | | Version | Supported          |
 747 | | ------- | ------------------ |
 748 | | 1.x.x   | :white_check_mark: |
 749 | | < 1.0   | :x:                |
 750 | 
 751 | ## Reporting a Vulnerability
 752 | 
 753 | If you discover a security vulnerability, please report it privately:
 754 | 
 755 | 1. **Do not** create a public issue
 756 | 2. Email [email protected] with details
 757 | 3. Include steps to reproduce if possible
 758 | 4. We will respond within 48 hours
 759 | 
 760 | ## Security Best Practices
 761 | 
 762 | When using this project:
 763 | 
 764 | - Keep dependencies updated
 765 | - Use environment variables for secrets
 766 | - Follow principle of least privilege
 767 | - Regularly audit your setup
 768 | 
 769 | Thank you for helping keep our project secure!
 770 | `;
 771 | 
 772 |   return files;
 773 | }
 774 | 
 775 | async function writeGeneratedFiles(
 776 |   templates: Record<string, string>,
 777 |   communityFiles: Record<string, string>,
 778 |   outputDirectory: string,
 779 |   _originalReadmePath: string,
 780 | ): Promise<void> {
 781 |   try {
 782 |     // Create output directory
 783 |     await mkdir(outputDirectory, { recursive: true });
 784 | 
 785 |     // Write templates
 786 |     for (const [filename, content] of Object.entries(templates)) {
 787 |       const filePath = join(outputDirectory, filename);
 788 |       await writeFile(filePath, content, "utf-8");
 789 |     }
 790 | 
 791 |     // Write community files
 792 |     for (const [filename, content] of Object.entries(communityFiles)) {
 793 |       const filePath = join(outputDirectory, filename);
 794 |       await writeFile(filePath, content, "utf-8");
 795 |     }
 796 | 
 797 |     // Create .github directory structure
 798 |     const githubDir = join(outputDirectory, ".github");
 799 |     await mkdir(githubDir, { recursive: true });
 800 | 
 801 |     const issueTemplateDir = join(githubDir, "ISSUE_TEMPLATE");
 802 |     await mkdir(issueTemplateDir, { recursive: true });
 803 | 
 804 |     // Bug report template
 805 |     const bugReportTemplate = `---
 806 | name: Bug report
 807 | about: Create a report to help us improve
 808 | title: '[BUG] '
 809 | labels: bug
 810 | assignees: ''
 811 | ---
 812 | 
 813 | **Describe the bug**
 814 | A clear and concise description of what the bug is.
 815 | 
 816 | **To Reproduce**
 817 | Steps to reproduce the behavior:
 818 | 1. Go to '...'
 819 | 2. Click on '....'
 820 | 3. Scroll down to '....'
 821 | 4. See error
 822 | 
 823 | **Expected behavior**
 824 | A clear and concise description of what you expected to happen.
 825 | 
 826 | **Screenshots**
 827 | If applicable, add screenshots to help explain your problem.
 828 | 
 829 | **Environment:**
 830 |  - OS: [e.g. iOS]
 831 |  - Browser [e.g. chrome, safari]
 832 |  - Version [e.g. 22]
 833 | 
 834 | **Additional context**
 835 | Add any other context about the problem here.
 836 | `;
 837 | 
 838 |     await writeFile(
 839 |       join(issueTemplateDir, "bug_report.yml"),
 840 |       bugReportTemplate,
 841 |       "utf-8",
 842 |     );
 843 | 
 844 |     // Feature request template
 845 |     const featureRequestTemplate = `---
 846 | name: Feature request
 847 | about: Suggest an idea for this project
 848 | title: '[FEATURE] '
 849 | labels: enhancement
 850 | assignees: ''
 851 | ---
 852 | 
 853 | **Is your feature request related to a problem? Please describe.**
 854 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
 855 | 
 856 | **Describe the solution you'd like**
 857 | A clear and concise description of what you want to happen.
 858 | 
 859 | **Describe alternatives you've considered**
 860 | A clear and concise description of any alternative solutions or features you've considered.
 861 | 
 862 | **Additional context**
 863 | Add any other context or screenshots about the feature request here.
 864 | `;
 865 | 
 866 |     await writeFile(
 867 |       join(issueTemplateDir, "feature_request.yml"),
 868 |       featureRequestTemplate,
 869 |       "utf-8",
 870 |     );
 871 | 
 872 |     // Pull request template
 873 |     const prTemplate = `## Description
 874 | Brief description of changes made.
 875 | 
 876 | ## Type of Change
 877 | - [ ] Bug fix (non-breaking change which fixes an issue)
 878 | - [ ] New feature (non-breaking change which adds functionality)
 879 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
 880 | - [ ] Documentation update
 881 | 
 882 | ## Testing
 883 | - [ ] Tests pass locally
 884 | - [ ] New tests added for new functionality
 885 | - [ ] Manual testing completed
 886 | 
 887 | ## Checklist
 888 | - [ ] Code follows project style guidelines
 889 | - [ ] Self-review completed
 890 | - [ ] Documentation updated
 891 | - [ ] No new warnings introduced
 892 | `;
 893 | 
 894 |     await writeFile(
 895 |       join(githubDir, "PULL_REQUEST_TEMPLATE.md"),
 896 |       prTemplate,
 897 |       "utf-8",
 898 |     );
 899 |   } catch (error) {
 900 |     throw new Error(
 901 |       `Failed to write generated files: ${
 902 |         error instanceof Error ? error.message : "Unknown error"
 903 |       }`,
 904 |     );
 905 |   }
 906 | }
 907 | 
 908 | function calculateSummaryMetrics(checklist: ChecklistItem[]) {
 909 |   const criticalIssues = checklist.filter(
 910 |     (item) => !item.present && item.severity === "critical",
 911 |   ).length;
 912 |   const importantIssues = checklist.filter(
 913 |     (item) => !item.present && item.severity === "important",
 914 |   ).length;
 915 |   const recommendedImprovements = checklist.filter(
 916 |     (item) => !item.present && item.severity === "recommended",
 917 |   ).length;
 918 |   const sectionsPresent = checklist.filter((item) => item.present).length;
 919 |   const totalSections = checklist.length;
 920 | 
 921 |   // Estimate improvement time based on missing items
 922 |   const totalMissing =
 923 |     criticalIssues + importantIssues + recommendedImprovements;
 924 |   let estimatedTime = "";
 925 |   if (totalMissing === 0) {
 926 |     estimatedTime = "No improvements needed";
 927 |   } else if (totalMissing <= 3) {
 928 |     estimatedTime = "30 minutes - 1 hour";
 929 |   } else if (totalMissing <= 6) {
 930 |     estimatedTime = "1-2 hours";
 931 |   } else if (totalMissing <= 10) {
 932 |     estimatedTime = "2-4 hours";
 933 |   } else {
 934 |     estimatedTime = "4+ hours (consider phased approach)";
 935 |   }
 936 | 
 937 |   return {
 938 |     criticalIssues,
 939 |     importantIssues,
 940 |     recommendedImprovements,
 941 |     sectionsPresent,
 942 |     totalSections,
 943 |     estimatedImprovementTime: estimatedTime,
 944 |   };
 945 | }
 946 | 
 947 | function generateNextSteps(
 948 |   checklist: ChecklistItem[],
 949 |   generateTemplate: boolean,
 950 |   outputDirectory?: string,
 951 | ): string[] {
 952 |   const nextSteps: string[] = [];
 953 |   const missing = checklist.filter((item) => !item.present);
 954 | 
 955 |   if (missing.length === 0) {
 956 |     nextSteps.push(
 957 |       "✅ README follows all best practices - no immediate action needed",
 958 |     );
 959 |     nextSteps.push(
 960 |       "📊 Consider periodic reviews to maintain quality as project evolves",
 961 |     );
 962 |     return nextSteps;
 963 |   }
 964 | 
 965 |   // Critical issues first
 966 |   const critical = missing.filter((item) => item.severity === "critical");
 967 |   if (critical.length > 0) {
 968 |     nextSteps.push(
 969 |       `🚨 Priority 1: Address ${critical.length} critical issues immediately`,
 970 |     );
 971 |     critical.forEach((item) => {
 972 |       nextSteps.push(`   • Add ${item.item}: ${item.description}`);
 973 |     });
 974 |   }
 975 | 
 976 |   // Important issues
 977 |   const important = missing.filter((item) => item.severity === "important");
 978 |   if (important.length > 0) {
 979 |     nextSteps.push(
 980 |       `⚠️ Priority 2: Address ${important.length} important sections within 1 week`,
 981 |     );
 982 |   }
 983 | 
 984 |   // Template usage
 985 |   if (generateTemplate && outputDirectory) {
 986 |     nextSteps.push(`📝 Review generated templates in ${outputDirectory}/`);
 987 |     nextSteps.push("🔄 Customize templates to match your project specifics");
 988 |     nextSteps.push(
 989 |       "📋 Use community files (.github templates, CONTRIBUTING.md) to improve project health",
 990 |     );
 991 |   }
 992 | 
 993 |   // General improvements
 994 |   nextSteps.push(
 995 |     "🔍 Run this analysis periodically to maintain README quality",
 996 |   );
 997 |   nextSteps.push(
 998 |     "👥 Consider getting feedback from new users on README clarity",
 999 |   );
1000 | 
1001 |   return nextSteps;
1002 | }
1003 | 
```
Page 22/33FirstPrevNextLast