#
tokens: 48314/50000 7/274 files (page 14/20)
lines: off (toggle) GitHub
raw markdown copy
This is page 14 of 20. Use http://codebase.md/tosin2013/documcp?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
├── 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
│   │   ├── 001-mcp-server-architecture.md
│   │   ├── 002-repository-analysis-engine.md
│   │   ├── 003-static-site-generator-recommendation-engine.md
│   │   ├── 004-diataxis-framework-integration.md
│   │   ├── 005-github-pages-deployment-automation.md
│   │   ├── 006-mcp-tools-api-design.md
│   │   ├── 007-mcp-prompts-and-resources-integration.md
│   │   ├── 008-intelligent-content-population-engine.md
│   │   ├── 009-content-accuracy-validation-framework.md
│   │   ├── 010-mcp-resource-pattern-redesign.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
│   ├── 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
│   │   ├── custom-domains.md
│   │   ├── documentation-freshness-tracking.md
│   │   ├── github-pages-deployment.md
│   │   ├── index.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
├── 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
│   │   ├── check-documentation-links.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
│   │   ├── 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
│   │   ├── ast-analyzer.ts
│   │   ├── code-scanner.ts
│   │   ├── content-extractor.ts
│   │   ├── drift-detector.ts
│   │   ├── freshness-tracker.ts
│   │   ├── language-parsers-simple.ts
│   │   ├── permission-checker.ts
│   │   └── sitemap-generator.ts
│   └── workflows
│       └── documentation-workflow.ts
├── test-docs-local.sh
├── tests
│   ├── api
│   │   └── mcp-responses.test.ts
│   ├── benchmarks
│   │   └── performance.test.ts
│   ├── edge-cases
│   │   └── error-handling.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-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.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
│   │   ├── 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
│       ├── ast-analyzer.test.ts
│       ├── content-extractor.test.ts
│       ├── drift-detector.test.ts
│       ├── freshness-tracker.test.ts
│       └── sitemap-generator.test.ts
├── tsconfig.json
└── typedoc.json
```

# Files

--------------------------------------------------------------------------------
/docs/api/index.html:
--------------------------------------------------------------------------------

```html
<!DOCTYPE html><html class="default" lang="en" data-base="./"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>DocuMCP API Documentation - v0.4.1</title><meta name="description" content="Documentation for DocuMCP API Documentation"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="assets/style.css"/><link rel="stylesheet" href="assets/highlight.css"/><script defer src="assets/main.js"></script><script async src="assets/icons.js" id="tsd-icons-script"></script><script async src="assets/search.js" id="tsd-search-script"></script><script async src="assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><a href="index.html" class="title">DocuMCP API Documentation - v0.4.1</a><div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/><ul role="listbox" id="tsd-search-results"></ul><div id="tsd-search-status" aria-live="polite" aria-atomic="true"><div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-menu"></use></svg></a></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><h1>DocuMCP API Documentation - v0.4.1</h1></div><div class="tsd-panel tsd-typography"><h1 id="documcp---intelligent-documentation-deployment-mcp-server" class="tsd-anchor-link">DocuMCP - Intelligent Documentation Deployment MCP Server<a href="#documcp---intelligent-documentation-deployment-mcp-server" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h1><p><a href="https://github.com/tosin2013/documcp/actions/workflows/ci.yml"><img src="https://github.com/tosin2013/documcp/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
<a href="https://github.com/tosin2013/documcp/actions/workflows/codeql.yml"><img src="https://github.com/tosin2013/documcp/actions/workflows/codeql.yml/badge.svg" alt="CodeQL"></a>
<a href="https://codecov.io/gh/tosin2013/documcp"><img src="https://codecov.io/gh/tosin2013/documcp/branch/main/graph/badge.svg" alt="Coverage"></a>
<a href="https://badge.fury.io/js/documcp"><img src="https://badge.fury.io/js/documcp.svg" alt="npm version"></a></p>
<p>DocuMCP is an intelligent Model Context Protocol (MCP) server that revolutionizes documentation deployment for open-source projects. It provides deep repository analysis, intelligent static site generator recommendations, and automated GitHub Pages deployment workflows.</p>
<h2 id="tldr" class="tsd-anchor-link">TL;DR<a href="#tldr" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>DocuMCP analyzes your repository, recommends the perfect static site generator (Jekyll, Hugo, Docusaurus, MkDocs, or Eleventy), creates professional documentation structure following Diataxis principles, and deploys it automatically to GitHub Pages. Just say &quot;analyze my repository and deploy documentation&quot; to get started.</p>
<h2 id="features" class="tsd-anchor-link">Features<a href="#features" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><h3 id="core-capabilities" class="tsd-anchor-link">Core Capabilities<a href="#core-capabilities" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ul>
<li>🔍 <strong>Repository Analysis</strong>: Deep multi-layered analysis of project structure, dependencies, and documentation needs</li>
<li>🎯 <strong>SSG Recommendations</strong>: Data-driven recommendations for Jekyll, Hugo, Docusaurus, MkDocs, or Eleventy</li>
<li>📚 <strong>Diataxis Framework</strong>: Automatic creation of well-structured documentation following proven principles</li>
<li>🚀 <strong>GitHub Pages Deployment</strong>: Automated workflow generation with SSG-specific optimizations</li>
<li>✅ <strong>Deployment Verification</strong>: Comprehensive checks and troubleshooting for successful deployments</li>
</ul>
<h3 id="intelligence--learning-phase-2" class="tsd-anchor-link">Intelligence &amp; Learning (Phase 2)<a href="#intelligence--learning-phase-2" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ul>
<li>🧠 <strong>Historical Intelligence</strong>: Learns from past deployments to improve recommendations</li>
<li>👤 <strong>User Preferences</strong>: Personalized recommendations based on your preferences and patterns</li>
<li>📊 <strong>Deployment Analytics</strong>: Comprehensive insights into deployment patterns and success rates</li>
<li>🎯 <strong>Smart Scoring</strong>: Intelligent SSG scoring based on success rates from similar projects</li>
<li>📈 <strong>Trend Analysis</strong>: Identifies deployment trends and provides health scores</li>
</ul>
<h2 id="requirements" class="tsd-anchor-link">Requirements<a href="#requirements" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><ul>
<li><strong>Node.js</strong>: 20.0.0 or higher</li>
<li><strong>npm</strong>: Latest stable version</li>
</ul>
<h2 id="installation" class="tsd-anchor-link">Installation<a href="#installation" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><pre><code class="bash"><span class="hl-0"># Clone the repository</span><br/><span class="hl-1">git</span><span class="hl-2"> </span><span class="hl-3">clone</span><span class="hl-2"> </span><span class="hl-3">https://github.com/tosin2013/documcp.git</span><br/><span class="hl-1">cd</span><span class="hl-2"> </span><span class="hl-3">documcp</span><br/><br/><span class="hl-0"># Install dependencies</span><br/><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">install</span><br/><br/><span class="hl-0"># Build the project</span><br/><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">run</span><span class="hl-2"> </span><span class="hl-3">build</span>
</code><button type="button">Copy</button></pre>

<h2 id="mcp-client-setup" class="tsd-anchor-link">MCP Client Setup<a href="#mcp-client-setup" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>DocuMCP works with various MCP-enabled clients. Here's how to configure it:</p>
<h3 id="claude-desktop" class="tsd-anchor-link">Claude Desktop<a href="#claude-desktop" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ol>
<li>
<p><strong>Locate Claude Desktop's configuration file</strong>:</p>
<ul>
<li><strong>macOS</strong>: <code>~/Library/Application Support/Claude/claude_desktop_config.json</code></li>
<li><strong>Windows</strong>: <code>%APPDATA%\Claude\claude_desktop_config.json</code></li>
<li><strong>Linux</strong>: <code>~/.config/claude/claude_desktop_config.json</code></li>
</ul>
</li>
<li>
<p><strong>Add documcp server configuration</strong>:</p>
<pre><code class="json"><span class="hl-2">{</span><br/><span class="hl-2">  </span><span class="hl-4">&quot;mcpServers&quot;</span><span class="hl-2">: {</span><br/><span class="hl-2">    </span><span class="hl-4">&quot;documcp&quot;</span><span class="hl-2">: {</span><br/><span class="hl-2">      </span><span class="hl-4">&quot;command&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;npx&quot;</span><span class="hl-2">,</span><br/><span class="hl-2">      </span><span class="hl-4">&quot;args&quot;</span><span class="hl-2">: [</span><span class="hl-3">&quot;documcp&quot;</span><span class="hl-2">]</span><br/><span class="hl-2">    }</span><br/><span class="hl-2">  }</span><br/><span class="hl-2">}</span>
</code><button type="button">Copy</button></pre>

</li>
<li>
<p><strong>Restart Claude Desktop</strong> to load the configuration.</p>
</li>
</ol>
<h3 id="vs-code-with-github-copilot" class="tsd-anchor-link">VS Code with GitHub Copilot<a href="#vs-code-with-github-copilot" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ol>
<li><strong>Install MCP extension</strong> for VS Code</li>
<li><strong>Configure in VS Code settings.json</strong>:<pre><code class="json"><span class="hl-2">{</span><br/><span class="hl-2">  </span><span class="hl-4">&quot;mcp.servers&quot;</span><span class="hl-2">: {</span><br/><span class="hl-2">    </span><span class="hl-4">&quot;documcp&quot;</span><span class="hl-2">: {</span><br/><span class="hl-2">      </span><span class="hl-4">&quot;command&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;npx&quot;</span><span class="hl-2">,</span><br/><span class="hl-2">      </span><span class="hl-4">&quot;args&quot;</span><span class="hl-2">: [</span><span class="hl-3">&quot;documcp&quot;</span><span class="hl-2">]</span><br/><span class="hl-2">    }</span><br/><span class="hl-2">  }</span><br/><span class="hl-2">}</span>
</code><button type="button">Copy</button></pre>

</li>
</ol>
<h3 id="cursor-editor" class="tsd-anchor-link">Cursor Editor<a href="#cursor-editor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ol>
<li><strong>Configure in Cursor settings</strong>:<pre><code class="json"><span class="hl-2">{</span><br/><span class="hl-2">  </span><span class="hl-4">&quot;mcpServers&quot;</span><span class="hl-2">: {</span><br/><span class="hl-2">    </span><span class="hl-4">&quot;documcp&quot;</span><span class="hl-2">: {</span><br/><span class="hl-2">      </span><span class="hl-4">&quot;command&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;npx&quot;</span><span class="hl-2">,</span><br/><span class="hl-2">      </span><span class="hl-4">&quot;args&quot;</span><span class="hl-2">: [</span><span class="hl-3">&quot;documcp&quot;</span><span class="hl-2">]</span><br/><span class="hl-2">    }</span><br/><span class="hl-2">  }</span><br/><span class="hl-2">}</span>
</code><button type="button">Copy</button></pre>

</li>
</ol>
<h3 id="gemini-code-assist" class="tsd-anchor-link">Gemini Code Assist<a href="#gemini-code-assist" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ol>
<li><strong>Check Gemini documentation</strong> for MCP server configuration</li>
<li><strong>Add similar configuration</strong> as above</li>
</ol>
<h3 id="troubleshooting" class="tsd-anchor-link">Troubleshooting<a href="#troubleshooting" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ul>
<li>Ensure <code>npx</code> is available in your PATH</li>
<li>For global installations, use the full path:<pre><code class="json"><span class="hl-2">{</span><br/><span class="hl-2">  </span><span class="hl-4">&quot;command&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;node&quot;</span><span class="hl-2">,</span><br/><span class="hl-2">  </span><span class="hl-4">&quot;args&quot;</span><span class="hl-2">: [</span><span class="hl-3">&quot;/usr/local/lib/node_modules/documcp/dist/index.js&quot;</span><span class="hl-2">]</span><br/><span class="hl-2">}</span>
</code><button type="button">Copy</button></pre>

</li>
<li>Find installation path: <code>npm list -g documcp</code></li>
</ul>
<h2 id="quick-start" class="tsd-anchor-link">Quick Start<a href="#quick-start" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Once configured with your MCP client, just prompt DocuMCP with natural language:</p>
<pre><code class="bash"><span class="hl-0"># Complete workflow</span><br/><span class="hl-1">&quot;analyze my repository and deploy documentation to GitHub Pages&quot;</span><br/><br/><span class="hl-0"># Step by step</span><br/><span class="hl-1">&quot;analyze my repository for documentation needs&quot;</span><br/><span class="hl-1">&quot;recommend the best static site generator for my project&quot;</span><br/><span class="hl-1">&quot;set up documentation structure and deploy to GitHub Pages&quot;</span>
</code><button type="button">Copy</button></pre>

<p>DocuMCP provides 30+ tools including repository analysis, intelligent SSG recommendations, content generation, deployment automation with tracking, validation, user preference management, deployment analytics, and memory-enhanced insights. See the <a href="media/index.md">complete documentation</a> for detailed tool reference.</p>
<h2 id="key-tools" class="tsd-anchor-link">Key Tools<a href="#key-tools" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><h3 id="analysis--recommendations" class="tsd-anchor-link">Analysis &amp; Recommendations<a href="#analysis--recommendations" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ul>
<li><code>analyze_repository</code> - Deep repository structure and dependency analysis</li>
<li><code>recommend_ssg</code> - Intelligent SSG recommendations with historical data and user preferences</li>
<li><code>detect_gaps</code> - Identify missing documentation sections</li>
</ul>
<h3 id="deployment--tracking" class="tsd-anchor-link">Deployment &amp; Tracking<a href="#deployment--tracking" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ul>
<li><code>deploy_pages</code> - Automated GitHub Pages deployment with outcome tracking</li>
<li><code>verify_deployment</code> - Comprehensive deployment validation</li>
<li><code>analyze_deployments</code> - Analytics and insights from deployment history</li>
</ul>
<h3 id="user-preferences--learning" class="tsd-anchor-link">User Preferences &amp; Learning<a href="#user-preferences--learning" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ul>
<li><code>manage_preferences</code> - Manage user preferences for personalized recommendations</li>
<li>View historical success rates and deployment patterns</li>
<li>Get recommendations based on similar projects' success</li>
</ul>
<h2 id="development" class="tsd-anchor-link">Development<a href="#development" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><pre><code class="bash"><span class="hl-0"># Run in development mode</span><br/><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">run</span><span class="hl-2"> </span><span class="hl-3">dev</span><br/><br/><span class="hl-0"># Run tests</span><br/><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">test</span><br/><br/><span class="hl-0"># Lint code</span><br/><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">run</span><span class="hl-2"> </span><span class="hl-3">lint</span><br/><br/><span class="hl-0"># Type check</span><br/><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">run</span><span class="hl-2"> </span><span class="hl-3">typecheck</span>
</code><button type="button">Copy</button></pre>

<h2 id="architecture" class="tsd-anchor-link">Architecture<a href="#architecture" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>DocuMCP follows a modular, stateless architecture:</p>
<ul>
<li><strong>TypeScript-based</strong> implementation using the official MCP SDK</li>
<li><strong>Stateless operation</strong> for consistency and reliability</li>
<li><strong>Modular design</strong> with clear separation of concerns</li>
<li><strong>Progressive complexity</strong> allowing users to start simple</li>
</ul>
<h2 id="documentation-structure-diataxis" class="tsd-anchor-link">Documentation Structure (Diataxis)<a href="#documentation-structure-diataxis" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>DocuMCP automatically creates documentation following the Diataxis framework:</p>
<ul>
<li><strong>Tutorials</strong>: Learning-oriented guides for newcomers</li>
<li><strong>How-To Guides</strong>: Task-oriented recipes for specific goals</li>
<li><strong>Reference</strong>: Information-oriented technical descriptions</li>
<li><strong>Explanation</strong>: Understanding-oriented conceptual discussions</li>
</ul>
<h2 id="contributing" class="tsd-anchor-link">Contributing<a href="#contributing" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>We welcome contributions! Please see our <a href="media/CONTRIBUTING.md">Contributing Guide</a> for details.</p>
<h3 id="first-time-contributors" class="tsd-anchor-link">First Time Contributors<a href="#first-time-contributors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Look for issues labeled &quot;good first issue&quot; to get started with the project. We welcome contributions from developers of all experience levels.</p>
<h3 id="reporting-issues" class="tsd-anchor-link">Reporting Issues<a href="#reporting-issues" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Please use our <a href="media/ISSUE_TEMPLATE">issue templates</a> when reporting bugs or requesting features.</p>
<h2 id="code-of-conduct" class="tsd-anchor-link">Code of Conduct<a href="#code-of-conduct" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>This project adheres to the <a href="media/CODE_OF_CONDUCT.md">Contributor Covenant Code of Conduct</a>. By participating, you are expected to uphold this code.</p>
<h2 id="security" class="tsd-anchor-link">Security<a href="#security" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Please see our <a href="media/SECURITY.md">Security Policy</a> for reporting vulnerabilities and security-related issues.</p>
<h2 id="license" class="tsd-anchor-link">License<a href="#license" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>MIT License - see <a href="media/LICENSE">LICENSE</a> for details.</p>
<h2 id="acknowledgments" class="tsd-anchor-link">Acknowledgments<a href="#acknowledgments" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><ul>
<li>Built on the <a href="https://modelcontextprotocol.io/">Model Context Protocol</a></li>
<li>Follows the <a href="https://diataxis.fr/">Diataxis Framework</a></li>
<li>Inspired by the need for better documentation in open-source projects</li>
</ul>
</div></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-chevronDown"></use></svg><h3>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div><details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-chevronDown"></use></svg><h3>On This Page</h3></summary><div class="tsd-accordion-details"><a href="#documcp---intelligent-documentation-deployment-mcp-server"><span>Docu<wbr/>MCP -<wbr/> <wbr/>Intelligent <wbr/>Documentation <wbr/>Deployment <wbr/>MCP <wbr/>Server</span></a><ul><li><a href="#tldr"><span>TL;<wbr/>DR</span></a></li><li><a href="#features"><span>Features</span></a></li><li><ul><li><a href="#core-capabilities"><span>Core <wbr/>Capabilities</span></a></li><li><a href="#intelligence--learning-phase-2"><span>Intelligence &amp; <wbr/>Learning (<wbr/>Phase 2)</span></a></li></ul></li><li><a href="#requirements"><span>Requirements</span></a></li><li><a href="#installation"><span>Installation</span></a></li><li><a href="#mcp-client-setup"><span>MCP <wbr/>Client <wbr/>Setup</span></a></li><li><ul><li><a href="#claude-desktop"><span>Claude <wbr/>Desktop</span></a></li><li><a href="#vs-code-with-github-copilot"><span>VS <wbr/>Code with <wbr/>Git<wbr/>Hub <wbr/>Copilot</span></a></li><li><a href="#cursor-editor"><span>Cursor <wbr/>Editor</span></a></li><li><a href="#gemini-code-assist"><span>Gemini <wbr/>Code <wbr/>Assist</span></a></li><li><a href="#troubleshooting"><span>Troubleshooting</span></a></li></ul></li><li><a href="#quick-start"><span>Quick <wbr/>Start</span></a></li><li><a href="#key-tools"><span>Key <wbr/>Tools</span></a></li><li><ul><li><a href="#analysis--recommendations"><span>Analysis &amp; <wbr/>Recommendations</span></a></li><li><a href="#deployment--tracking"><span>Deployment &amp; <wbr/>Tracking</span></a></li><li><a href="#user-preferences--learning"><span>User <wbr/>Preferences &amp; <wbr/>Learning</span></a></li></ul></li><li><a href="#development"><span>Development</span></a></li><li><a href="#architecture"><span>Architecture</span></a></li><li><a href="#documentation-structure-diataxis"><span>Documentation <wbr/>Structure (<wbr/>Diataxis)</span></a></li><li><a href="#contributing"><span>Contributing</span></a></li><li><ul><li><a href="#first-time-contributors"><span>First <wbr/>Time <wbr/>Contributors</span></a></li><li><a href="#reporting-issues"><span>Reporting <wbr/>Issues</span></a></li></ul></li><li><a href="#code-of-conduct"><span>Code of <wbr/>Conduct</span></a></li><li><a href="#security"><span>Security</span></a></li><li><a href="#license"><span>License</span></a></li><li><a href="#acknowledgments"><span>Acknowledgments</span></a></li></ul></div></details></div><div class="site-menu"><nav class="tsd-navigation"><a href="modules.html">DocuMCP API Documentation - v0.4.1</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer><p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer><div class="overlay"></div></body></html>

```

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

```typescript
/**
 * Memory System Performance and Load Testing
 * Tests performance, scalability, and resource usage of memory system
 * Part of Issue #57 - Memory System Performance and Load Testing
 */

import { promises as fs } from "fs";
import path from "path";
import os from "os";
import { performance } from "perf_hooks";
import { MemoryManager } from "../../src/memory/manager.js";
import { EnhancedMemoryManager } from "../../src/memory/enhanced-manager.js";
import { IncrementalLearningSystem } from "../../src/memory/learning.js";
import { KnowledgeGraph } from "../../src/memory/knowledge-graph.js";
import {
  initializeMemory,
  rememberAnalysis,
  rememberRecommendation,
  getSimilarProjects,
} from "../../src/memory/integration.js";

interface PerformanceMetrics {
  operationTime: number;
  memoryUsed: number;
  operationsPerSecond: number;
  throughput: number;
}

describe("Memory System Performance and Load Testing", () => {
  let tempDir: string;
  let memoryManager: MemoryManager;

  beforeEach(async () => {
    tempDir = path.join(
      os.tmpdir(),
      `memory-performance-test-${Date.now()}-${Math.random()
        .toString(36)
        .substr(2, 9)}`,
    );
    await fs.mkdir(tempDir, { recursive: true });

    memoryManager = new MemoryManager(tempDir);
    await memoryManager.initialize();
  });

  afterEach(async () => {
    try {
      await fs.rm(tempDir, { recursive: true, force: true });
    } catch (error) {
      // Ignore cleanup errors
    }
  });

  function measurePerformance<T>(
    operation: () => Promise<T>,
  ): Promise<{ result: T; metrics: PerformanceMetrics }> {
    return new Promise(async (resolve) => {
      const startTime = performance.now();
      const startMemory = process.memoryUsage();

      const result = await operation();

      const endTime = performance.now();
      const endMemory = process.memoryUsage();

      const operationTime = endTime - startTime;
      const memoryUsed = endMemory.heapUsed - startMemory.heapUsed;

      resolve({
        result,
        metrics: {
          operationTime,
          memoryUsed,
          operationsPerSecond: 1000 / operationTime,
          throughput: 1000 / operationTime,
        },
      });
    });
  }

  describe("Basic Operations Performance", () => {
    test("should perform single memory operations efficiently", async () => {
      memoryManager.setContext({ projectId: "performance-single" });

      const testData = {
        projectId: "performance-single",
        language: { primary: "typescript" },
        framework: { name: "react" },
        stats: { files: 100, lines: 10000 },
      };

      const { metrics: createMetrics } = await measurePerformance(async () => {
        return await memoryManager.remember("analysis", testData);
      });

      const memoryId = (await memoryManager.remember("analysis", testData)).id;

      const { metrics: readMetrics } = await measurePerformance(async () => {
        return await memoryManager.recall(memoryId);
      });

      const { metrics: searchMetrics } = await measurePerformance(async () => {
        return await memoryManager.search({ projectId: "performance-single" });
      });

      // Performance expectations (adjust based on system capabilities)
      expect(createMetrics.operationTime).toBeLessThan(100); // 100ms
      expect(readMetrics.operationTime).toBeLessThan(50); // 50ms
      expect(searchMetrics.operationTime).toBeLessThan(100); // 100ms

      // Memory usage should be reasonable
      expect(createMetrics.memoryUsed).toBeLessThan(10 * 1024 * 1024); // 10MB
      expect(readMetrics.memoryUsed).toBeLessThan(1 * 1024 * 1024); // 1MB

      console.log("Single Operation Performance:");
      console.log(
        `Create: ${createMetrics.operationTime.toFixed(2)}ms, Memory: ${(
          createMetrics.memoryUsed / 1024
        ).toFixed(2)}KB`,
      );
      console.log(
        `Read: ${readMetrics.operationTime.toFixed(2)}ms, Memory: ${(
          readMetrics.memoryUsed / 1024
        ).toFixed(2)}KB`,
      );
      console.log(
        `Search: ${searchMetrics.operationTime.toFixed(2)}ms, Memory: ${(
          searchMetrics.memoryUsed / 1024
        ).toFixed(2)}KB`,
      );
    });

    test("should handle batch operations efficiently", async () => {
      memoryManager.setContext({ projectId: "performance-batch" });

      const batchSize = 100;
      const testData = Array.from({ length: batchSize }, (_, i) => ({
        projectId: "performance-batch",
        index: i,
        language: { primary: i % 2 === 0 ? "typescript" : "javascript" },
        framework: {
          name: i % 3 === 0 ? "react" : i % 3 === 1 ? "vue" : "angular",
        },
        stats: { files: 10 + i, lines: 1000 + i * 100 },
      }));

      const { metrics: batchCreateMetrics } = await measurePerformance(
        async () => {
          const promises = testData.map((data) =>
            memoryManager.remember("analysis", data),
          );
          return await Promise.all(promises);
        },
      );

      const { metrics: batchSearchMetrics } = await measurePerformance(
        async () => {
          return await memoryManager.search({ projectId: "performance-batch" });
        },
      );

      // Batch operations should be efficient
      expect(batchCreateMetrics.operationTime).toBeLessThan(5000); // 5 seconds for 100 items
      expect(batchSearchMetrics.operationTime).toBeLessThan(1000); // 1 second to search 100 items

      // Calculate throughput
      const createThroughput =
        batchSize / (batchCreateMetrics.operationTime / 1000);
      const searchThroughput =
        batchSize / (batchSearchMetrics.operationTime / 1000);

      expect(createThroughput).toBeGreaterThan(20); // At least 20 ops/sec
      expect(searchThroughput).toBeGreaterThan(100); // At least 100 searches/sec

      console.log("Batch Operation Performance:");
      console.log(
        `Create ${batchSize} items: ${batchCreateMetrics.operationTime.toFixed(
          2,
        )}ms (${createThroughput.toFixed(2)} ops/sec)`,
      );
      console.log(
        `Search ${batchSize} items: ${batchSearchMetrics.operationTime.toFixed(
          2,
        )}ms (${searchThroughput.toFixed(2)} ops/sec)`,
      );
    });
  });

  describe("Scalability Testing", () => {
    test("should scale linearly with data size", async () => {
      memoryManager.setContext({ projectId: "scalability-test" });

      const testSizes = [10, 50, 100, 500];
      const results: Array<{
        size: number;
        createTime: number;
        searchTime: number;
      }> = [];

      for (const size of testSizes) {
        const testData = Array.from({ length: size }, (_, i) => ({
          projectId: "scalability-test",
          index: i,
          data: `test-data-${i}`,
          timestamp: new Date().toISOString(),
        }));

        // Measure creation time
        const { metrics: createMetrics } = await measurePerformance(
          async () => {
            const promises = testData.map((data) =>
              memoryManager.remember("analysis", data),
            );
            return await Promise.all(promises);
          },
        );

        // Measure search time
        const { metrics: searchMetrics } = await measurePerformance(
          async () => {
            return await memoryManager.search({
              projectId: "scalability-test",
            });
          },
        );

        results.push({
          size,
          createTime: createMetrics.operationTime,
          searchTime: searchMetrics.operationTime,
        });
      }

      // Verify roughly linear scaling (allow for some variance)
      for (let i = 1; i < results.length; i++) {
        const prev = results[i - 1];
        const curr = results[i];

        const sizeRatio = curr.size / prev.size;
        const createTimeRatio = curr.createTime / prev.createTime;
        const searchTimeRatio = curr.searchTime / prev.searchTime;

        // Create time should scale roughly linearly (within 3x of size ratio)
        expect(createTimeRatio).toBeLessThan(sizeRatio * 3);

        // Search time should not degrade too badly (within 2x of size ratio)
        expect(searchTimeRatio).toBeLessThan(sizeRatio * 2);
      }

      console.log("Scalability Results:");
      results.forEach((result) => {
        console.log(
          `Size ${result.size}: Create ${result.createTime.toFixed(
            2,
          )}ms, Search ${result.searchTime.toFixed(2)}ms`,
        );
      });
    });

    test("should handle large individual memories efficiently", async () => {
      memoryManager.setContext({ projectId: "large-memory-test" });

      const sizes = [
        { name: "small", data: "x".repeat(1000) }, // 1KB
        { name: "medium", data: "x".repeat(10000) }, // 10KB
        { name: "large", data: "x".repeat(100000) }, // 100KB
        { name: "xlarge", data: "x".repeat(1000000) }, // 1MB
      ];

      const results: Array<{
        name: string;
        createTime: number;
        readTime: number;
      }> = [];

      for (const size of sizes) {
        const testData = {
          projectId: "large-memory-test",
          size: size.name,
          content: size.data,
          metadata: { size: size.data.length },
        };

        // Measure creation time
        const { result: memory, metrics: createMetrics } =
          await measurePerformance(async () => {
            return await memoryManager.remember("analysis", testData);
          });

        // Measure read time
        const { metrics: readMetrics } = await measurePerformance(async () => {
          return await memoryManager.recall(memory.id);
        });

        results.push({
          name: size.name,
          createTime: createMetrics.operationTime,
          readTime: readMetrics.operationTime,
        });

        // Large memories should still be handled within reasonable time
        expect(createMetrics.operationTime).toBeLessThan(5000); // 5 seconds
        expect(readMetrics.operationTime).toBeLessThan(1000); // 1 second
      }

      console.log("Large Memory Performance:");
      results.forEach((result) => {
        console.log(
          `${result.name}: Create ${result.createTime.toFixed(
            2,
          )}ms, Read ${result.readTime.toFixed(2)}ms`,
        );
      });
    });
  });

  describe("Concurrent Operations Performance", () => {
    test("should handle concurrent read/write operations", async () => {
      memoryManager.setContext({ projectId: "concurrent-test" });

      // Pre-populate with some data
      const initialData = Array.from({ length: 50 }, (_, i) => ({
        projectId: "concurrent-test",
        index: i,
        data: `initial-data-${i}`,
      }));

      const initialMemories = await Promise.all(
        initialData.map((data) => memoryManager.remember("analysis", data)),
      );

      const concurrentOperations = 20;

      const { metrics: concurrentMetrics } = await measurePerformance(
        async () => {
          const operations = Array.from(
            { length: concurrentOperations },
            async (_, i) => {
              if (i % 3 === 0) {
                // Create new memory
                return await memoryManager.remember("analysis", {
                  projectId: "concurrent-test",
                  index: 100 + i,
                  data: `concurrent-data-${i}`,
                });
              } else if (i % 3 === 1) {
                // Read existing memory
                const randomMemory =
                  initialMemories[
                    Math.floor(Math.random() * initialMemories.length)
                  ];
                return await memoryManager.recall(randomMemory.id);
              } else {
                // Search memories
                return await memoryManager.search({
                  projectId: "concurrent-test",
                });
              }
            },
          );

          return await Promise.all(operations);
        },
      );

      expect(concurrentMetrics.operationTime).toBeLessThan(3000); // 3 seconds for 20 concurrent ops

      const throughput =
        concurrentOperations / (concurrentMetrics.operationTime / 1000);
      expect(throughput).toBeGreaterThan(5); // At least 5 concurrent ops/sec

      console.log("Concurrent Operations Performance:");
      console.log(
        `${concurrentOperations} concurrent ops: ${concurrentMetrics.operationTime.toFixed(
          2,
        )}ms (${throughput.toFixed(2)} ops/sec)`,
      );
    });

    test("should maintain performance under sustained load", async () => {
      memoryManager.setContext({ projectId: "sustained-load-test" });

      const testDuration = 3000; // 3 seconds
      const operationInterval = 100; // Every 100ms
      const results: number[] = [];

      const startTime = Date.now();
      let operationCount = 0;

      while (Date.now() - startTime < testDuration) {
        const { metrics } = await measurePerformance(async () => {
          return await memoryManager.remember("analysis", {
            projectId: "sustained-load-test",
            index: operationCount++,
            timestamp: Date.now(),
            data: `sustained-load-data-${operationCount}`,
          });
        });

        results.push(metrics.operationTime);

        // Wait for next interval
        await new Promise((resolve) => setTimeout(resolve, operationInterval));
      }

      const avgTime =
        results.reduce((sum, time) => sum + time, 0) / results.length;
      const maxTime = Math.max(...results);
      const minTime = Math.min(...results);

      // Performance should remain consistent under sustained load
      expect(avgTime).toBeLessThan(200); // Average operation time < 200ms
      expect(maxTime).toBeLessThan(1000); // No single operation > 1 second

      // Performance degradation should be minimal
      const firstHalf = results.slice(0, Math.floor(results.length / 2));
      const secondHalf = results.slice(Math.floor(results.length / 2));

      const firstHalfAvg =
        firstHalf.reduce((sum, time) => sum + time, 0) / firstHalf.length;
      const secondHalfAvg =
        secondHalf.reduce((sum, time) => sum + time, 0) / secondHalf.length;

      const degradation = secondHalfAvg / firstHalfAvg;
      expect(degradation).toBeLessThan(2); // Less than 2x degradation

      console.log("Sustained Load Performance:");
      console.log(
        `Operations: ${results.length}, Avg: ${avgTime.toFixed(
          2,
        )}ms, Min: ${minTime.toFixed(2)}ms, Max: ${maxTime.toFixed(2)}ms`,
      );
      console.log(
        `Performance degradation: ${((degradation - 1) * 100).toFixed(1)}%`,
      );
    });
  });

  describe("Memory Resource Usage", () => {
    test("should manage memory usage efficiently", async () => {
      memoryManager.setContext({ projectId: "memory-usage-test" });

      const initialMemory = process.memoryUsage();
      const memorySnapshots: Array<{ count: number; heapUsed: number }> = [];

      // Add memories in batches and monitor memory usage
      for (let batch = 0; batch < 10; batch++) {
        const batchSize = 100;
        const batchData = Array.from({ length: batchSize }, (_, i) => ({
          projectId: "memory-usage-test",
          batch,
          index: i,
          data: "x".repeat(1000), // 1KB per memory
          timestamp: new Date().toISOString(),
        }));

        await Promise.all(
          batchData.map((data) => memoryManager.remember("analysis", data)),
        );

        const currentMemory = process.memoryUsage();
        memorySnapshots.push({
          count: (batch + 1) * batchSize,
          heapUsed: currentMemory.heapUsed - initialMemory.heapUsed,
        });

        // Force garbage collection if available
        if (global.gc) {
          global.gc();
        }
      }

      // Memory usage should be reasonable
      const finalMemoryUsage = memorySnapshots[memorySnapshots.length - 1];
      const memoryPerItem = finalMemoryUsage.heapUsed / finalMemoryUsage.count;

      expect(memoryPerItem).toBeLessThan(50 * 1024); // Less than 50KB per memory item (including overhead)
      expect(finalMemoryUsage.heapUsed).toBeLessThan(100 * 1024 * 1024); // Less than 100MB total

      console.log("Memory Usage Analysis:");
      console.log(
        `Total items: ${finalMemoryUsage.count}, Total memory: ${(
          finalMemoryUsage.heapUsed /
          1024 /
          1024
        ).toFixed(2)}MB`,
      );
      console.log(`Memory per item: ${(memoryPerItem / 1024).toFixed(2)}KB`);
    });

    test("should not leak memory on cleanup operations", async () => {
      memoryManager.setContext({ projectId: "memory-leak-test" });

      const initialMemory = process.memoryUsage();

      // Create and delete memories multiple times
      for (let cycle = 0; cycle < 5; cycle++) {
        const memories = [];

        // Create memories
        for (let i = 0; i < 100; i++) {
          const memory = await memoryManager.remember("analysis", {
            projectId: "memory-leak-test",
            cycle,
            index: i,
            data: "x".repeat(1000),
          });
          memories.push(memory);
        }

        // Delete all memories
        for (const memory of memories) {
          await memoryManager.forget(memory.id);
        }

        // Force garbage collection
        if (global.gc) {
          global.gc();
        }
      }

      const finalMemory = process.memoryUsage();
      const memoryDifference = finalMemory.heapUsed - initialMemory.heapUsed;

      // Memory usage should return close to initial levels
      expect(memoryDifference).toBeLessThan(15 * 1024 * 1024); // Less than 15MB difference

      console.log("Memory Leak Test:");
      console.log(
        `Memory difference: ${(memoryDifference / 1024 / 1024).toFixed(2)}MB`,
      );
    });
  });

  describe("Enhanced Components Performance", () => {
    test("should benchmark enhanced memory manager performance", async () => {
      const enhancedTempDir = path.join(tempDir, "enhanced");
      await fs.mkdir(enhancedTempDir, { recursive: true });

      const enhancedManager = new EnhancedMemoryManager(enhancedTempDir);
      await enhancedManager.initialize();

      enhancedManager.setContext({ projectId: "enhanced-performance" });

      const projectFeatures: import("../../src/memory/learning.js").ProjectFeatures =
        {
          language: "typescript",
          framework: "react",
          size: "medium",
          complexity: "moderate",
          hasTests: true,
          hasCI: true,
          hasDocs: true,
          isOpenSource: true,
        };

      const baseRecommendation = {
        recommended: "docusaurus",
        confidence: 0.8,
        score: 0.85,
      };

      // Benchmark enhanced recommendation
      const { metrics: enhancedMetrics } = await measurePerformance(
        async () => {
          return await enhancedManager.getEnhancedRecommendation(
            "/test/enhanced-performance",
            baseRecommendation,
            projectFeatures,
          );
        },
      );

      expect(enhancedMetrics.operationTime).toBeLessThan(5000); // 5 seconds

      // Benchmark intelligent analysis
      const analysisData = {
        language: "typescript",
        framework: "react",
        size: "medium",
        hasTests: true,
        hasCI: true,
      };

      const { metrics: analysisMetrics } = await measurePerformance(
        async () => {
          return await enhancedManager.getIntelligentAnalysis(
            "/test/enhanced-performance",
            analysisData,
          );
        },
      );

      expect(analysisMetrics.operationTime).toBeLessThan(3000); // 3 seconds

      console.log("Enhanced Components Performance:");
      console.log(
        `Enhanced recommendation: ${enhancedMetrics.operationTime.toFixed(
          2,
        )}ms`,
      );
      console.log(
        `Intelligent analysis: ${analysisMetrics.operationTime.toFixed(2)}ms`,
      );
    });

    test("should benchmark learning system performance", async () => {
      const learningTempDir = path.join(tempDir, "learning");
      await fs.mkdir(learningTempDir, { recursive: true });

      const tempLearningManager = new MemoryManager(learningTempDir);
      await tempLearningManager.initialize();
      const learningSystem = new IncrementalLearningSystem(tempLearningManager);
      await learningSystem.initialize();

      const projectFeatures: import("../../src/memory/learning.js").ProjectFeatures =
        {
          language: "python",
          framework: "django",
          size: "large",
          complexity: "complex",
          hasTests: true,
          hasCI: true,
          hasDocs: false,
          isOpenSource: true,
        };

      const baseRecommendation = {
        recommended: "sphinx",
        confidence: 0.7,
      };

      // Add training data through memory manager (learning system learns from stored memories)
      for (let i = 0; i < 50; i++) {
        await tempLearningManager.remember("analysis", {
          ...projectFeatures,
          index: i,
          feedback: {
            rating: 3 + (i % 3),
            helpful: i % 2 === 0,
            comments: `Training feedback ${i}`,
          },
        });
      }

      // Benchmark improved recommendation
      const { metrics: improveMetrics } = await measurePerformance(async () => {
        return await learningSystem.getImprovedRecommendation(
          projectFeatures,
          baseRecommendation,
        );
      });

      expect(improveMetrics.operationTime).toBeLessThan(1000); // 1 second

      // Benchmark pattern detection
      const { metrics: patternMetrics } = await measurePerformance(async () => {
        return await learningSystem.getPatterns();
      });

      expect(patternMetrics.operationTime).toBeLessThan(2000); // 2 seconds

      console.log("Learning System Performance:");
      console.log(
        `Improved recommendation: ${improveMetrics.operationTime.toFixed(2)}ms`,
      );
      console.log(
        `Pattern detection: ${patternMetrics.operationTime.toFixed(2)}ms`,
      );
    });

    test("should benchmark knowledge graph performance", async () => {
      const graphTempDir = path.join(tempDir, "graph");
      await fs.mkdir(graphTempDir, { recursive: true });

      const tempGraphManager = new MemoryManager(graphTempDir);
      await tempGraphManager.initialize();
      const knowledgeGraph = new KnowledgeGraph(tempGraphManager);
      await knowledgeGraph.initialize();

      // Add nodes and edges
      const nodeCount = 100;
      const edgeCount = 200;

      const { metrics: buildMetrics } = await measurePerformance(async () => {
        // Add nodes
        for (let i = 0; i < nodeCount; i++) {
          knowledgeGraph.addNode({
            id: `node-${i}`,
            type:
              i % 3 === 0 ? "project" : i % 3 === 1 ? "technology" : "pattern",
            label: `Node ${i}`,
            weight: 1.0,
            properties: {
              name: `Node ${i}`,
              category: i % 5 === 0 ? "frontend" : "backend",
            },
          });
        }

        // Add edges
        for (let i = 0; i < edgeCount; i++) {
          const sourceId = `node-${Math.floor(Math.random() * nodeCount)}`;
          const targetId = `node-${Math.floor(Math.random() * nodeCount)}`;

          if (sourceId !== targetId) {
            knowledgeGraph.addEdge({
              source: sourceId,
              target: targetId,
              type: i % 2 === 0 ? "uses" : "similar_to",
              weight: Math.random(),
              properties: {},
              confidence: Math.random(),
            });
          }
        }
      });

      expect(buildMetrics.operationTime).toBeLessThan(5000); // 5 seconds to build graph

      // Benchmark pathfinding
      const { metrics: pathMetrics } = await measurePerformance(async () => {
        return knowledgeGraph.findPath("node-0", "node-50");
      });

      expect(pathMetrics.operationTime).toBeLessThan(500); // 500ms for pathfinding

      // Benchmark node queries (using available methods)
      const { metrics: queryMetrics } = await measurePerformance(async () => {
        return knowledgeGraph.getAllNodes();
      });

      expect(queryMetrics.operationTime).toBeLessThan(1000); // 1 second for node queries

      console.log("Knowledge Graph Performance:");
      console.log(
        `Build graph (${nodeCount} nodes, ${edgeCount} edges): ${buildMetrics.operationTime.toFixed(
          2,
        )}ms`,
      );
      console.log(`Find path: ${pathMetrics.operationTime.toFixed(2)}ms`);
      console.log(`Nodes query: ${queryMetrics.operationTime.toFixed(2)}ms`);
    });
  });

  describe("Integration Performance", () => {
    test("should benchmark MCP integration performance", async () => {
      // Test memory integration functions
      const analysisData = {
        projectId: "integration-performance",
        language: { primary: "go" },
        framework: { name: "gin" },
        stats: { files: 200, lines: 50000 },
      };

      const { metrics: analysisMetrics } = await measurePerformance(
        async () => {
          return await rememberAnalysis(
            "/test/integration-performance",
            analysisData,
          );
        },
      );

      const { metrics: recommendationMetrics } = await measurePerformance(
        async () => {
          return await rememberRecommendation("analysis-id", {
            recommended: "hugo",
            confidence: 0.9,
          });
        },
      );

      const { metrics: similarMetrics } = await measurePerformance(async () => {
        return await getSimilarProjects(analysisData, 5);
      });

      expect(analysisMetrics.operationTime).toBeLessThan(1000); // 1 second
      expect(recommendationMetrics.operationTime).toBeLessThan(1000); // 1 second
      expect(similarMetrics.operationTime).toBeLessThan(2000); // 2 seconds

      console.log("MCP Integration Performance:");
      console.log(
        `Remember analysis: ${analysisMetrics.operationTime.toFixed(2)}ms`,
      );
      console.log(
        `Remember recommendation: ${recommendationMetrics.operationTime.toFixed(
          2,
        )}ms`,
      );
      console.log(
        `Get similar projects: ${similarMetrics.operationTime.toFixed(2)}ms`,
      );
    });
  });
});

```

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

```typescript
/**
 * Tests for sitemap-generator utility
 */

import { promises as fs } from "fs";
import path from "path";
import { tmpdir } from "os";
import {
  generateSitemap,
  parseSitemap,
  validateSitemap,
  updateSitemap,
  listSitemapUrls,
  type SitemapUrl,
  type SitemapOptions,
} from "../../src/utils/sitemap-generator.js";

describe("sitemap-generator", () => {
  let testDir: string;
  let docsDir: string;

  beforeEach(async () => {
    // Create temporary test directory
    testDir = path.join(tmpdir(), `sitemap-test-${Date.now()}`);
    docsDir = path.join(testDir, "docs");
    await fs.mkdir(docsDir, { recursive: true });
  });

  afterEach(async () => {
    // Clean up test directory
    try {
      await fs.rm(testDir, { recursive: true, force: true });
    } catch (error) {
      // Ignore cleanup errors
    }
  });

  describe("generateSitemap", () => {
    it("should generate sitemap.xml from documentation files", async () => {
      // Create test documentation structure
      await fs.mkdir(path.join(docsDir, "tutorials"), { recursive: true });
      await fs.mkdir(path.join(docsDir, "reference"), { recursive: true });

      await fs.writeFile(
        path.join(docsDir, "index.md"),
        "# Home\n\nWelcome to the docs!",
      );
      await fs.writeFile(
        path.join(docsDir, "tutorials", "getting-started.md"),
        "# Getting Started\n\nStart here.",
      );
      await fs.writeFile(
        path.join(docsDir, "reference", "api.md"),
        "# API Reference\n\nAPI documentation.",
      );

      const options: SitemapOptions = {
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      };

      const result = await generateSitemap(options);

      expect(result.xml).toContain('<?xml version="1.0" encoding="UTF-8"?>');
      expect(result.xml).toContain(
        '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
      );
      expect(result.urls).toHaveLength(3);
      expect(result.stats.totalUrls).toBe(3);
      expect(result.stats.byCategory).toHaveProperty("home");
      expect(result.stats.byCategory).toHaveProperty("tutorial");
      expect(result.stats.byCategory).toHaveProperty("reference");
    });

    it("should generate URLs with correct priorities based on categories", async () => {
      await fs.mkdir(path.join(docsDir, "tutorials"), { recursive: true });
      await fs.mkdir(path.join(docsDir, "reference"), { recursive: true });

      await fs.writeFile(
        path.join(docsDir, "tutorials", "guide.md"),
        "# Tutorial",
      );
      await fs.writeFile(
        path.join(docsDir, "reference", "api.md"),
        "# Reference",
      );

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      const tutorialUrl = result.urls.find((u) => u.category === "tutorial");
      const referenceUrl = result.urls.find((u) => u.category === "reference");

      expect(tutorialUrl?.priority).toBe(1.0); // Highest priority
      expect(referenceUrl?.priority).toBe(0.8);
    });

    it("should handle empty documentation directory", async () => {
      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      expect(result.urls).toHaveLength(0);
      expect(result.stats.totalUrls).toBe(0);
    });

    it("should exclude node_modules and other excluded patterns", async () => {
      await fs.mkdir(path.join(docsDir, "node_modules"), { recursive: true });
      await fs.writeFile(
        path.join(docsDir, "node_modules", "package.md"),
        "# Package",
      );
      await fs.writeFile(path.join(docsDir, "guide.md"), "# Guide");

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      expect(result.urls).toHaveLength(1);
      expect(result.urls[0].loc).toContain("guide.html");
    });

    it("should convert markdown extensions to html", async () => {
      await fs.writeFile(path.join(docsDir, "page.md"), "# Page");
      await fs.writeFile(path.join(docsDir, "component.mdx"), "# Component");

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      expect(result.urls[0].loc).toContain(".html");
      expect(result.urls[1].loc).toContain(".html");
      expect(result.urls.some((u) => u.loc.endsWith(".md"))).toBe(false);
      expect(result.urls.some((u) => u.loc.endsWith(".mdx"))).toBe(false);
    });

    it("should extract title from markdown frontmatter", async () => {
      const content = `---
title: My Custom Title
---

# Main Heading

Content here.`;

      await fs.writeFile(path.join(docsDir, "page.md"), content);

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      expect(result.urls[0].title).toBe("My Custom Title");
    });

    it("should extract title from markdown heading", async () => {
      await fs.writeFile(
        path.join(docsDir, "page.md"),
        "# Page Title\n\nContent",
      );

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      expect(result.urls[0].title).toBe("Page Title");
    });

    it("should handle custom include and exclude patterns", async () => {
      await fs.writeFile(path.join(docsDir, "page.md"), "# Markdown");
      await fs.writeFile(path.join(docsDir, "page.html"), "<h1>HTML</h1>");
      await fs.writeFile(path.join(docsDir, "page.txt"), "Text");

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        includePatterns: ["**/*.md"],
        excludePatterns: [],
        useGitHistory: false,
      });

      expect(result.urls).toHaveLength(1);
      expect(result.urls[0].loc).toContain("page.html");
    });
  });

  describe("parseSitemap", () => {
    it("should parse existing sitemap.xml", async () => {
      const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://example.com/page1.html</loc>
    <lastmod>2025-01-01</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
  </url>
  <url>
    <loc>https://example.com/page2.html</loc>
    <lastmod>2025-01-02</lastmod>
    <changefreq>weekly</changefreq>
    <priority>1.0</priority>
  </url>
</urlset>`;

      const sitemapPath = path.join(testDir, "sitemap.xml");
      await fs.writeFile(sitemapPath, xml);

      const urls = await parseSitemap(sitemapPath);

      expect(urls).toHaveLength(2);
      expect(urls[0].loc).toBe("https://example.com/page1.html");
      expect(urls[0].lastmod).toBe("2025-01-01");
      expect(urls[0].changefreq).toBe("monthly");
      expect(urls[0].priority).toBe(0.8);
      expect(urls[1].loc).toBe("https://example.com/page2.html");
    });

    it("should handle XML special characters", async () => {
      const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://example.com/page?id=1&amp;type=test</loc>
  </url>
</urlset>`;

      const sitemapPath = path.join(testDir, "sitemap.xml");
      await fs.writeFile(sitemapPath, xml);

      const urls = await parseSitemap(sitemapPath);

      expect(urls[0].loc).toBe("https://example.com/page?id=1&type=test");
    });

    it("should throw error for invalid sitemap", async () => {
      const sitemapPath = path.join(testDir, "invalid.xml");
      await fs.writeFile(sitemapPath, "not xml");

      const urls = await parseSitemap(sitemapPath);
      expect(urls).toHaveLength(0); // Graceful handling of invalid XML
    });
  });

  describe("validateSitemap", () => {
    it("should validate correct sitemap", async () => {
      const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://example.com/page.html</loc>
    <lastmod>2025-01-01</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
  </url>
</urlset>`;

      const sitemapPath = path.join(testDir, "sitemap.xml");
      await fs.writeFile(sitemapPath, xml);

      const result = await validateSitemap(sitemapPath);

      expect(result.valid).toBe(true);
      expect(result.errors).toHaveLength(0);
      expect(result.urlCount).toBe(1);
    });

    it("should detect missing loc element", async () => {
      const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <lastmod>2025-01-01</lastmod>
  </url>
</urlset>`;

      const sitemapPath = path.join(testDir, "sitemap.xml");
      await fs.writeFile(sitemapPath, xml);

      const result = await validateSitemap(sitemapPath);

      expect(result.valid).toBe(false);
      expect(result.errors.some((e) => e.includes("Missing <loc>"))).toBe(true);
    });

    it("should detect invalid priority", async () => {
      const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://example.com/page.html</loc>
    <priority>1.5</priority>
  </url>
</urlset>`;

      const sitemapPath = path.join(testDir, "sitemap.xml");
      await fs.writeFile(sitemapPath, xml);

      const result = await validateSitemap(sitemapPath);

      expect(result.valid).toBe(false);
      expect(
        result.errors.some((e) =>
          e.includes("Priority must be between 0.0 and 1.0"),
        ),
      ).toBe(true);
    });

    it("should detect invalid protocol", async () => {
      const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>ftp://example.com/page.html</loc>
  </url>
</urlset>`;

      const sitemapPath = path.join(testDir, "sitemap.xml");
      await fs.writeFile(sitemapPath, xml);

      const result = await validateSitemap(sitemapPath);

      expect(result.valid).toBe(false);
      expect(result.errors.some((e) => e.includes("Invalid protocol"))).toBe(
        true,
      );
    });

    it("should return error if sitemap does not exist", async () => {
      const sitemapPath = path.join(testDir, "nonexistent.xml");

      const result = await validateSitemap(sitemapPath);

      expect(result.valid).toBe(false);
      expect(result.errors.some((e) => e.includes("does not exist"))).toBe(
        true,
      );
    });
  });

  describe("updateSitemap", () => {
    it("should update existing sitemap with new pages", async () => {
      // Create initial sitemap
      const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://example.com/page1.html</loc>
    <lastmod>2025-01-01</lastmod>
    <priority>0.8</priority>
  </url>
</urlset>`;

      const sitemapPath = path.join(testDir, "sitemap.xml");
      await fs.writeFile(sitemapPath, xml);

      // Add new documentation file
      await fs.writeFile(path.join(docsDir, "page1.md"), "# Page 1");
      await fs.writeFile(path.join(docsDir, "page2.md"), "# Page 2");

      const changes = await updateSitemap(sitemapPath, {
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      expect(changes.added).toBe(1); // page2.md is new
      expect(changes.total).toBe(2);
    });

    it("should detect removed pages", async () => {
      // Create initial sitemap with 2 URLs
      const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://example.com/page1.html</loc>
  </url>
  <url>
    <loc>https://example.com/page2.html</loc>
  </url>
</urlset>`;

      const sitemapPath = path.join(testDir, "sitemap.xml");
      await fs.writeFile(sitemapPath, xml);

      // Only create page1.md
      await fs.writeFile(path.join(docsDir, "page1.md"), "# Page 1");

      const changes = await updateSitemap(sitemapPath, {
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      expect(changes.removed).toBe(1); // page2.html was removed
      expect(changes.total).toBe(1);
    });

    it("should create new sitemap if none exists", async () => {
      const sitemapPath = path.join(testDir, "sitemap.xml");
      await fs.writeFile(path.join(docsDir, "page.md"), "# Page");

      const changes = await updateSitemap(sitemapPath, {
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      expect(changes.added).toBe(1);
      expect(changes.total).toBe(1);

      // Verify sitemap was created
      const exists = await fs
        .access(sitemapPath)
        .then(() => true)
        .catch(() => false);
      expect(exists).toBe(true);
    });
  });

  describe("listSitemapUrls", () => {
    it("should list all URLs from sitemap", async () => {
      const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://example.com/page1.html</loc>
    <priority>0.9</priority>
  </url>
  <url>
    <loc>https://example.com/page2.html</loc>
    <priority>0.8</priority>
  </url>
</urlset>`;

      const sitemapPath = path.join(testDir, "sitemap.xml");
      await fs.writeFile(sitemapPath, xml);

      const urls = await listSitemapUrls(sitemapPath);

      expect(urls).toHaveLength(2);
      expect(urls[0].loc).toBe("https://example.com/page1.html");
      expect(urls[1].loc).toBe("https://example.com/page2.html");
    });
  });

  describe("edge cases", () => {
    it("should handle deeply nested directory structures", async () => {
      const deepPath = path.join(docsDir, "a", "b", "c", "d");
      await fs.mkdir(deepPath, { recursive: true });
      await fs.writeFile(path.join(deepPath, "deep.md"), "# Deep Page");

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      expect(result.urls).toHaveLength(1);
      expect(result.urls[0].loc).toContain("a/b/c/d/deep.html");
    });

    it("should handle files with special characters in names", async () => {
      await fs.writeFile(path.join(docsDir, "my-page-2024.md"), "# Page");

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      expect(result.urls).toHaveLength(1);
      expect(result.urls[0].loc).toContain("my-page-2024.html");
    });

    it("should handle index.html correctly", async () => {
      await fs.writeFile(path.join(docsDir, "index.md"), "# Home");

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      expect(result.urls[0].loc).toBe("https://example.com/");
    });

    it("should exclude directories matching exclusion patterns", async () => {
      // Create directory structure with excluded dirs
      await fs.mkdir(path.join(docsDir, "node_modules"), { recursive: true });
      await fs.mkdir(path.join(docsDir, "valid"), { recursive: true });
      await fs.writeFile(
        path.join(docsDir, "node_modules", "package.md"),
        "# Should be excluded",
      );
      await fs.writeFile(
        path.join(docsDir, "valid", "page.md"),
        "# Valid Page",
      );

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      // Should only include valid directory, not node_modules
      expect(result.urls).toHaveLength(1);
      expect(result.urls[0].loc).toContain("valid/page");
    });

    it("should handle directory scan errors gracefully", async () => {
      // Create a valid docs directory
      await fs.writeFile(path.join(docsDir, "valid.md"), "# Valid");

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      // Should succeed despite potential permission issues
      expect(result.urls.length).toBeGreaterThanOrEqual(1);
    });

    it("should categorize explanation pages correctly", async () => {
      await fs.mkdir(path.join(docsDir, "explanation"), { recursive: true });
      await fs.writeFile(
        path.join(docsDir, "explanation", "concepts.md"),
        "# Concepts",
      );

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      expect(result.stats.byCategory).toHaveProperty("explanation");
      expect(result.stats.byCategory.explanation).toBeGreaterThan(0);
    });

    it("should fall back to file system date when git fails", async () => {
      await fs.writeFile(path.join(docsDir, "no-git.md"), "# No Git");

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: true, // Try git but will fall back
      });

      // Should still have a lastmod date from file system
      expect(result.urls[0].lastmod).toBeDefined();
      expect(result.urls[0].lastmod).toMatch(/^\d{4}-\d{2}-\d{2}$/);
    });

    it("should handle files without extensions", async () => {
      await fs.writeFile(path.join(docsDir, "README"), "# Readme");

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        includePatterns: ["**/*"], // Include all files
        useGitHistory: false,
      });

      // Should handle extensionless files
      expect(result.urls.length).toBeGreaterThanOrEqual(0);
    });

    it("should handle empty git timestamp", async () => {
      // Create file and generate sitemap with git enabled
      await fs.writeFile(path.join(docsDir, "test.md"), "# Test");

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: true,
      });

      // Should have valid dates even if git returns empty
      expect(result.urls[0].lastmod).toBeDefined();
    });

    it("should handle files in deeply excluded paths", async () => {
      await fs.mkdir(path.join(docsDir, ".git", "objects"), {
        recursive: true,
      });
      await fs.writeFile(
        path.join(docsDir, ".git", "objects", "file.md"),
        "# Git Object",
      );
      await fs.writeFile(path.join(docsDir, "valid.md"), "# Valid");

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      // Should exclude .git directory
      expect(result.urls).toHaveLength(1);
      expect(result.urls[0].loc).not.toContain(".git");
    });

    it("should extract title from HTML title tag", async () => {
      const htmlContent = `<!DOCTYPE html>
<html>
<head>
  <title>HTML Page Title</title>
</head>
<body>
  <h1>Different Heading</h1>
</body>
</html>`;

      await fs.writeFile(path.join(docsDir, "page.html"), htmlContent);

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        includePatterns: ["**/*.html"],
        useGitHistory: false,
      });

      expect(result.urls[0].title).toBe("HTML Page Title");
    });

    it("should handle files with no extractable title", async () => {
      await fs.writeFile(path.join(docsDir, "notitle.md"), "Just content");

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      expect(result.urls[0].title).toBeUndefined();
    });

    it("should handle inaccessible files gracefully", async () => {
      await fs.writeFile(path.join(docsDir, "readable.md"), "# Readable");

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: docsDir,
        useGitHistory: false,
      });

      // Should still process readable files
      expect(result.urls.length).toBeGreaterThan(0);
    });
  });

  describe("validateSitemap - additional validations", () => {
    it("should detect empty sitemap", async () => {
      const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
</urlset>`;

      const sitemapPath = path.join(testDir, "sitemap.xml");
      await fs.writeFile(sitemapPath, xml);

      const result = await validateSitemap(sitemapPath);

      expect(result.valid).toBe(true);
      expect(result.warnings.some((w) => w.includes("no URLs"))).toBe(true);
    });

    it("should detect URL exceeding 2048 characters", async () => {
      const longUrl = `https://example.com/${"a".repeat(2100)}`;
      const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>${longUrl}</loc>
  </url>
</urlset>`;

      const sitemapPath = path.join(testDir, "sitemap.xml");
      await fs.writeFile(sitemapPath, xml);

      const result = await validateSitemap(sitemapPath);

      expect(result.valid).toBe(false);
      expect(result.errors.some((e) => e.includes("exceeds 2048"))).toBe(true);
    });

    it("should warn about invalid lastmod format", async () => {
      const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://example.com/page.html</loc>
    <lastmod>invalid-date</lastmod>
  </url>
</urlset>`;

      const sitemapPath = path.join(testDir, "sitemap.xml");
      await fs.writeFile(sitemapPath, xml);

      const result = await validateSitemap(sitemapPath);

      expect(result.warnings.some((w) => w.includes("Invalid lastmod"))).toBe(
        true,
      );
    });

    it("should detect sitemap with more than 50,000 URLs", async () => {
      // Create sitemap XML with >50,000 URLs
      const urls = Array.from(
        { length: 50001 },
        (_, i) => `  <url>
    <loc>https://example.com/page${i}.html</loc>
    <lastmod>2025-01-01</lastmod>
  </url>`,
      ).join("\n");

      const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls}
</urlset>`;

      const sitemapPath = path.join(testDir, "large-sitemap.xml");
      await fs.writeFile(sitemapPath, xml);

      const result = await validateSitemap(sitemapPath);

      expect(result.valid).toBe(false);
      expect(result.errors.some((e) => e.includes("50,000"))).toBe(true);
    });

    it("should handle malformed XML gracefully", async () => {
      // The regex-based parser is lenient and extracts data where possible
      // This tests that the parser doesn't crash on malformed XML
      const malformedXml = `<?xml version="1.0"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://example.com</loc>
  </url>
  <!-- Missing closing urlset tag`;

      const sitemapPath = path.join(testDir, "malformed.xml");
      await fs.writeFile(sitemapPath, malformedXml);

      // Should parse successfully despite malformation (regex-based parsing)
      const result = await validateSitemap(sitemapPath);
      expect(result).toBeDefined();
      expect(result.urlCount).toBe(1);
    });
  });

  describe("Edge cases", () => {
    it("should handle excluded directories", async () => {
      // Create structure with node_modules
      await fs.mkdir(path.join(testDir, "node_modules"), { recursive: true });
      await fs.writeFile(
        path.join(testDir, "node_modules", "package.md"),
        "# Should be excluded",
      );
      await fs.writeFile(path.join(testDir, "included.md"), "# Included");

      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: testDir,
        includePatterns: ["**/*.md"],
        useGitHistory: false,
      });

      expect(result.urls.some((u) => u.loc.includes("node_modules"))).toBe(
        false,
      );
      expect(result.urls.some((u) => u.loc.includes("included"))).toBe(true);
    });

    it("should handle directory scan errors gracefully", async () => {
      // Test with a path that has permission issues or doesn't exist
      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: path.join(testDir, "nonexistent"),
        includePatterns: ["**/*.md"],
        useGitHistory: false,
      });

      expect(result.urls).toEqual([]);
    });

    it("should use git timestamp when available", async () => {
      // Initialize git and create a committed file
      await fs.writeFile(path.join(testDir, "test.md"), "# Test");

      try {
        const { execSync } = require("child_process");
        execSync("git init", { cwd: testDir, stdio: "ignore" });
        execSync("git config user.email '[email protected]'", {
          cwd: testDir,
          stdio: "ignore",
        });
        execSync("git config user.name 'Test'", {
          cwd: testDir,
          stdio: "ignore",
        });
        execSync("git add test.md", { cwd: testDir, stdio: "ignore" });
        execSync("git commit -m 'test'", { cwd: testDir, stdio: "ignore" });

        const result = await generateSitemap({
          baseUrl: "https://example.com",
          docsPath: testDir,
          includePatterns: ["**/*.md"],
          useGitHistory: true,
        });

        expect(result.urls.length).toBe(1);
        expect(result.urls[0].lastmod).toMatch(/\d{4}-\d{2}-\d{2}/);
      } catch (error) {
        // Git might not be available in test environment, skip
        console.log("Git test skipped:", error);
      }
    });

    it("should use current date when file doesn't exist", async () => {
      // This tests the getFileLastModified error path
      // We'll indirectly test this by ensuring dates are always returned
      const result = await generateSitemap({
        baseUrl: "https://example.com",
        docsPath: testDir,
        includePatterns: ["**/*.md"],
        useGitHistory: false,
      });

      // Even with no files, function should not crash
      expect(result).toBeDefined();
      expect(Array.isArray(result.urls)).toBe(true);
    });
  });
});

```

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

```typescript
/**
 * Knowledge Graph Health Monitoring Module
 * Implements Phase 2: KG Health Tracking
 *
 * Provides comprehensive health monitoring, issue detection, and trend analysis
 * for the DocuMCP knowledge graph to ensure data quality and performance.
 */

import { promises as fs } from "fs";
import { join } from "path";
import KnowledgeGraph, { GraphNode, GraphEdge } from "./knowledge-graph.js";
import { KGStorage } from "./kg-storage.js";

// ============================================================================
// Health Metrics Schema
// ============================================================================

export interface KGHealthMetrics {
  timestamp: string;
  overallHealth: number; // 0-100 score
  dataQuality: DataQualityMetrics;
  structureHealth: StructureHealthMetrics;
  performance: PerformanceMetrics;
  trends: HealthTrends;
  issues: HealthIssue[];
  recommendations: HealthRecommendation[];
}

export interface DataQualityMetrics {
  score: number; // 0-100
  staleNodeCount: number; // nodes not updated in 30+ days
  orphanedEdgeCount: number;
  duplicateCount: number;
  confidenceAverage: number;
  completenessScore: number; // % of expected relationships present
  totalNodes: number;
  totalEdges: number;
}

export interface StructureHealthMetrics {
  score: number; // 0-100
  isolatedNodeCount: number; // nodes with no edges
  clusteringCoefficient: number;
  averagePathLength: number;
  densityScore: number;
  connectedComponents: number;
}

export interface PerformanceMetrics {
  score: number; // 0-100
  avgQueryTime: number; // ms
  storageSize: number; // bytes
  growthRate: number; // bytes/day
  indexEfficiency: number;
}

export interface HealthTrends {
  healthTrend: "improving" | "stable" | "degrading";
  nodeGrowthRate: number; // nodes/day
  edgeGrowthRate: number; // edges/day
  errorRate: number; // errors/operations (from last 100 operations)
  qualityTrend: "improving" | "stable" | "degrading";
}

export interface HealthIssue {
  id: string;
  severity: "critical" | "high" | "medium" | "low";
  category: "integrity" | "performance" | "quality" | "structure";
  description: string;
  affectedEntities: string[];
  remediation: string;
  detectedAt: string;
  autoFixable: boolean;
}

export interface HealthRecommendation {
  id: string;
  priority: "high" | "medium" | "low";
  action: string;
  expectedImpact: number; // health score increase (0-100)
  effort: "low" | "medium" | "high";
  category: string;
}

export interface HealthHistory {
  timestamp: string;
  overallHealth: number;
  dataQuality: number;
  structureHealth: number;
  performance: number;
  nodeCount: number;
  edgeCount: number;
}

// ============================================================================
// Health Monitoring Class
// ============================================================================

export class KGHealthMonitor {
  private storageDir: string;
  private historyFilePath: string;
  private issueDetectors: IssueDetector[];
  private performanceTracking: PerformanceTracker;

  constructor(storageDir?: string) {
    this.storageDir = storageDir || `${process.cwd()}/.documcp/memory`;
    this.historyFilePath = join(this.storageDir, "health-history.jsonl");
    this.issueDetectors = createIssueDetectors();
    this.performanceTracking = new PerformanceTracker();
  }

  /**
   * Calculate comprehensive health metrics
   */
  async calculateHealth(
    kg: KnowledgeGraph,
    storage: KGStorage,
  ): Promise<KGHealthMetrics> {
    const timestamp = new Date().toISOString();

    // Calculate component metrics
    const dataQuality = await this.calculateDataQuality(kg, storage);
    const structureHealth = await this.calculateStructureHealth(kg);
    const performance = await this.calculatePerformance(storage);

    // Calculate overall health (weighted average)
    const overallHealth = Math.round(
      dataQuality.score * 0.4 +
        structureHealth.score * 0.3 +
        performance.score * 0.3,
    );

    // Detect issues
    const issues = await this.detectIssues(kg, {
      dataQuality,
      structureHealth,
      performance,
    });

    // Generate recommendations
    const recommendations = this.generateRecommendations(issues, {
      dataQuality,
      structureHealth,
      performance,
    });

    // Analyze trends
    const trends = await this.analyzeTrends(overallHealth);

    const metrics: KGHealthMetrics = {
      timestamp,
      overallHealth,
      dataQuality,
      structureHealth,
      performance,
      trends,
      issues,
      recommendations,
    };

    // Track history
    await this.trackHealthHistory(metrics);

    return metrics;
  }

  /**
   * Calculate data quality metrics
   */
  private async calculateDataQuality(
    kg: KnowledgeGraph,
    storage: KGStorage,
  ): Promise<DataQualityMetrics> {
    await kg.getStatistics();
    const integrity = await storage.verifyIntegrity();

    const now = new Date();
    const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);

    // Count stale nodes
    const allNodes = await kg.getAllNodes();
    const staleNodeCount = allNodes.filter((node) => {
      const lastUpdated = new Date(node.lastUpdated);
      return lastUpdated < thirtyDaysAgo;
    }).length;

    // Get orphaned edges from integrity check
    const orphanedEdgeCount = integrity.warnings.filter((w) =>
      w.includes("missing"),
    ).length;

    // Get duplicate count from integrity check
    const duplicateCount = integrity.errors.filter((e) =>
      e.includes("Duplicate"),
    ).length;

    // Calculate average confidence
    const allEdges = await kg.getAllEdges();
    const confidenceAverage =
      allEdges.length > 0
        ? allEdges.reduce((sum, edge) => sum + edge.confidence, 0) /
          allEdges.length
        : 1.0;

    // Calculate completeness (% of projects with expected relationships)
    const completenessScore = this.calculateCompleteness(allNodes, allEdges);

    // Calculate data quality score (0-100)
    const stalePercentage =
      (staleNodeCount / Math.max(allNodes.length, 1)) * 100;
    const orphanPercentage =
      (orphanedEdgeCount / Math.max(allEdges.length, 1)) * 100;
    const qualityDeductions =
      stalePercentage * 0.3 + orphanPercentage * 0.5 + duplicateCount * 10;

    const score = Math.max(
      0,
      Math.min(100, 100 - qualityDeductions + (completenessScore - 0.5) * 50),
    );

    return {
      score: Math.round(score),
      staleNodeCount,
      orphanedEdgeCount,
      duplicateCount,
      confidenceAverage,
      completenessScore,
      totalNodes: allNodes.length,
      totalEdges: allEdges.length,
    };
  }

  /**
   * Calculate structure health metrics
   */
  private async calculateStructureHealth(
    kg: KnowledgeGraph,
  ): Promise<StructureHealthMetrics> {
    await kg.getStatistics();
    const allNodes = await kg.getAllNodes();
    const allEdges = await kg.getAllEdges();

    // Count isolated nodes (no edges)
    const nodeConnections = new Map<string, number>();
    for (const edge of allEdges) {
      nodeConnections.set(
        edge.source,
        (nodeConnections.get(edge.source) || 0) + 1,
      );
      nodeConnections.set(
        edge.target,
        (nodeConnections.get(edge.target) || 0) + 1,
      );
    }

    const isolatedNodeCount = allNodes.filter(
      (node) => !nodeConnections.has(node.id),
    ).length;

    // Calculate clustering coefficient (simplified)
    const clusteringCoefficient = this.calculateClusteringCoefficient(
      allNodes,
      allEdges,
    );

    // Calculate average path length (simplified - using BFS on sample)
    const averagePathLength = this.calculateAveragePathLength(
      allNodes,
      allEdges,
    );

    // Calculate density score
    const maxPossibleEdges = (allNodes.length * (allNodes.length - 1)) / 2;
    const densityScore =
      maxPossibleEdges > 0 ? allEdges.length / maxPossibleEdges : 0;

    // Count connected components
    const connectedComponents = this.countConnectedComponents(
      allNodes,
      allEdges,
    );

    // Calculate structure health score
    const isolatedPercentage =
      (isolatedNodeCount / Math.max(allNodes.length, 1)) * 100;
    const score = Math.max(
      0,
      Math.min(
        100,
        100 -
          isolatedPercentage * 0.5 +
          clusteringCoefficient * 20 -
          (connectedComponents - 1) * 5,
      ),
    );

    return {
      score: Math.round(score),
      isolatedNodeCount,
      clusteringCoefficient,
      averagePathLength,
      densityScore,
      connectedComponents,
    };
  }

  /**
   * Calculate performance metrics
   */
  private async calculatePerformance(
    storage: KGStorage,
  ): Promise<PerformanceMetrics> {
    const storageStats = await storage.getStatistics();

    // Get average query time from performance tracker
    const avgQueryTime = this.performanceTracking.getAverageQueryTime();

    // Calculate storage size
    const storageSize =
      storageStats.fileSize.entities + storageStats.fileSize.relationships;

    // Calculate growth rate (bytes/day) from history
    const growthRate = await this.calculateGrowthRate();

    // Index efficiency (placeholder - would need actual indexing metrics)
    const indexEfficiency = 0.8;

    // Calculate performance score
    const queryScore =
      avgQueryTime < 10 ? 100 : Math.max(0, 100 - avgQueryTime);
    const sizeScore =
      storageSize < 10 * 1024 * 1024
        ? 100
        : Math.max(0, 100 - storageSize / (1024 * 1024));
    const score = Math.round(
      queryScore * 0.5 + sizeScore * 0.3 + indexEfficiency * 100 * 0.2,
    );

    return {
      score,
      avgQueryTime,
      storageSize,
      growthRate,
      indexEfficiency,
    };
  }

  /**
   * Detect issues in the knowledge graph
   */
  private async detectIssues(
    kg: KnowledgeGraph,
    metrics: {
      dataQuality: DataQualityMetrics;
      structureHealth: StructureHealthMetrics;
      performance: PerformanceMetrics;
    },
  ): Promise<HealthIssue[]> {
    const issues: HealthIssue[] = [];

    for (const detector of this.issueDetectors) {
      const detectedIssues = await detector.detect(kg, metrics);
      issues.push(...detectedIssues);
    }

    // Sort by severity
    issues.sort((a, b) => {
      const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
      return severityOrder[a.severity] - severityOrder[b.severity];
    });

    return issues;
  }

  /**
   * Generate recommendations based on issues and metrics
   */
  private generateRecommendations(
    issues: HealthIssue[],
    metrics: {
      dataQuality: DataQualityMetrics;
      structureHealth: StructureHealthMetrics;
      performance: PerformanceMetrics;
    },
  ): HealthRecommendation[] {
    const recommendations: HealthRecommendation[] = [];

    // Generate recommendations for critical/high severity issues
    for (const issue of issues.filter(
      (i) => i.severity === "critical" || i.severity === "high",
    )) {
      if (issue.autoFixable) {
        recommendations.push({
          id: `fix_${issue.id}`,
          priority: "high",
          action: issue.remediation,
          expectedImpact: issue.severity === "critical" ? 20 : 10,
          effort: "low",
          category: issue.category,
        });
      }
    }

    // Data quality recommendations
    if (metrics.dataQuality.score < 70) {
      if (metrics.dataQuality.staleNodeCount > 10) {
        recommendations.push({
          id: "refresh_stale_data",
          priority: "medium",
          action: `Re-analyze ${metrics.dataQuality.staleNodeCount} stale projects to refresh data`,
          expectedImpact: 15,
          effort: "medium",
          category: "data_quality",
        });
      }

      if (metrics.dataQuality.orphanedEdgeCount > 5) {
        recommendations.push({
          id: "cleanup_orphaned_edges",
          priority: "high",
          action: "Run automated cleanup to remove orphaned relationships",
          expectedImpact: 10,
          effort: "low",
          category: "data_quality",
        });
      }
    }

    // Structure health recommendations
    if (metrics.structureHealth.score < 70) {
      if (metrics.structureHealth.isolatedNodeCount > 0) {
        recommendations.push({
          id: "connect_isolated_nodes",
          priority: "medium",
          action: `Review and connect ${metrics.structureHealth.isolatedNodeCount} isolated nodes`,
          expectedImpact: 8,
          effort: "medium",
          category: "structure",
        });
      }
    }

    // Performance recommendations
    if (metrics.performance.score < 70) {
      if (metrics.performance.storageSize > 50 * 1024 * 1024) {
        recommendations.push({
          id: "optimize_storage",
          priority: "medium",
          action: "Archive or compress old knowledge graph data",
          expectedImpact: 12,
          effort: "high",
          category: "performance",
        });
      }
    }

    // Sort by priority and expected impact
    recommendations.sort((a, b) => {
      const priorityOrder = { high: 0, medium: 1, low: 2 };
      if (priorityOrder[a.priority] !== priorityOrder[b.priority]) {
        return priorityOrder[a.priority] - priorityOrder[b.priority];
      }
      return b.expectedImpact - a.expectedImpact;
    });

    return recommendations.slice(0, 5); // Top 5 recommendations
  }

  /**
   * Analyze trends from historical health data
   */
  private async analyzeTrends(currentHealth: number): Promise<HealthTrends> {
    const history = await this.getHealthHistory(7); // Last 7 days

    if (history.length < 2) {
      return {
        healthTrend: "stable",
        nodeGrowthRate: 0,
        edgeGrowthRate: 0,
        errorRate: 0,
        qualityTrend: "stable",
      };
    }

    // Calculate health trend
    const sevenDayAvg =
      history.reduce((sum, h) => sum + h.overallHealth, 0) / history.length;
    const healthDiff = currentHealth - sevenDayAvg;

    const healthTrend =
      healthDiff > 5 ? "improving" : healthDiff < -5 ? "degrading" : "stable";

    // Calculate growth rates
    const oldestEntry = history[history.length - 1];
    const newestEntry = history[0];
    const daysDiff = Math.max(
      1,
      (new Date(newestEntry.timestamp).getTime() -
        new Date(oldestEntry.timestamp).getTime()) /
        (1000 * 60 * 60 * 24),
    );

    const nodeGrowthRate =
      (newestEntry.nodeCount - oldestEntry.nodeCount) / daysDiff;
    const edgeGrowthRate =
      (newestEntry.edgeCount - oldestEntry.edgeCount) / daysDiff;

    // Quality trend
    const qualityAvg =
      history.reduce((sum, h) => sum + h.dataQuality, 0) / history.length;
    const qualityDiff = history[0].dataQuality - qualityAvg;

    const qualityTrend =
      qualityDiff > 5 ? "improving" : qualityDiff < -5 ? "degrading" : "stable";

    return {
      healthTrend,
      nodeGrowthRate: Math.round(nodeGrowthRate * 10) / 10,
      edgeGrowthRate: Math.round(edgeGrowthRate * 10) / 10,
      errorRate: 0, // TODO: Track from operations log
      qualityTrend,
    };
  }

  /**
   * Track health history to persistent storage
   */
  private async trackHealthHistory(metrics: KGHealthMetrics): Promise<void> {
    const historyEntry: HealthHistory = {
      timestamp: metrics.timestamp,
      overallHealth: metrics.overallHealth,
      dataQuality: metrics.dataQuality.score,
      structureHealth: metrics.structureHealth.score,
      performance: metrics.performance.score,
      nodeCount: metrics.dataQuality.totalNodes,
      edgeCount: metrics.dataQuality.totalEdges,
    };

    try {
      await fs.appendFile(
        this.historyFilePath,
        JSON.stringify(historyEntry) + "\n",
        "utf-8",
      );

      // Keep only last 90 days of history
      await this.pruneHistoryFile(90);
    } catch (error) {
      console.warn("Failed to track health history:", error);
    }
  }

  /**
   * Get health history for the last N days
   */
  private async getHealthHistory(days: number): Promise<HealthHistory[]> {
    try {
      const content = await fs.readFile(this.historyFilePath, "utf-8");
      const lines = content.trim().split("\n");

      const cutoffDate = new Date();
      cutoffDate.setDate(cutoffDate.getDate() - days);

      const history: HealthHistory[] = [];
      for (const line of lines) {
        if (line.trim()) {
          const entry = JSON.parse(line) as HealthHistory;
          if (new Date(entry.timestamp) >= cutoffDate) {
            history.push(entry);
          }
        }
      }

      return history.reverse(); // Most recent first
    } catch {
      return [];
    }
  }

  /**
   * Prune history file to keep only last N days
   */
  private async pruneHistoryFile(days: number): Promise<void> {
    try {
      const history = await this.getHealthHistory(days);
      const content = history.map((h) => JSON.stringify(h)).join("\n") + "\n";
      await fs.writeFile(this.historyFilePath, content, "utf-8");
    } catch (error) {
      console.warn("Failed to prune history file:", error);
    }
  }

  // Helper methods

  private calculateCompleteness(
    nodes: GraphNode[],
    edges: GraphEdge[],
  ): number {
    const projectNodes = nodes.filter((n) => n.type === "project");
    if (projectNodes.length === 0) return 1.0;

    let totalExpected = 0;
    let totalFound = 0;

    for (const project of projectNodes) {
      // Expected relationships for each project:
      // 1. At least one technology relationship
      // 2. Documentation relationship (if hasDocs = true)
      // 3. Configuration relationship (if deployed)

      totalExpected += 1; // Technology

      const projectEdges = edges.filter((e) => e.source === project.id);

      if (projectEdges.some((e) => e.type === "project_uses_technology")) {
        totalFound += 1;
      }

      if (project.properties.hasDocs) {
        totalExpected += 1;
        if (
          projectEdges.some(
            (e) =>
              e.type === "depends_on" &&
              nodes.find((n) => n.id === e.target)?.type ===
                "documentation_section",
          )
        ) {
          totalFound += 1;
        }
      }
    }

    return totalExpected > 0 ? totalFound / totalExpected : 1.0;
  }

  private calculateClusteringCoefficient(
    nodes: GraphNode[],
    edges: GraphEdge[],
  ): number {
    // Simplified clustering coefficient calculation
    if (nodes.length < 3) return 0;

    const adjacency = new Map<string, Set<string>>();
    for (const edge of edges) {
      if (!adjacency.has(edge.source)) {
        adjacency.set(edge.source, new Set());
      }
      adjacency.get(edge.source)!.add(edge.target);
    }

    let totalCoefficient = 0;
    let nodeCount = 0;

    for (const node of nodes.slice(0, 100)) {
      // Sample first 100 nodes
      const neighbors = adjacency.get(node.id);
      if (!neighbors || neighbors.size < 2) continue;

      const neighborArray = Array.from(neighbors);
      let triangles = 0;
      const possibleTriangles =
        (neighborArray.length * (neighborArray.length - 1)) / 2;

      for (let i = 0; i < neighborArray.length; i++) {
        for (let j = i + 1; j < neighborArray.length; j++) {
          const n1Neighbors = adjacency.get(neighborArray[i]);
          if (n1Neighbors?.has(neighborArray[j])) {
            triangles++;
          }
        }
      }

      if (possibleTriangles > 0) {
        totalCoefficient += triangles / possibleTriangles;
        nodeCount++;
      }
    }

    return nodeCount > 0 ? totalCoefficient / nodeCount : 0;
  }

  private calculateAveragePathLength(
    nodes: GraphNode[],
    edges: GraphEdge[],
  ): number {
    // Simplified using sample BFS
    if (nodes.length === 0) return 0;

    const adjacency = new Map<string, string[]>();
    for (const edge of edges) {
      if (!adjacency.has(edge.source)) {
        adjacency.set(edge.source, []);
      }
      adjacency.get(edge.source)!.push(edge.target);
    }

    // Sample 10 random nodes for BFS
    const sampleSize = Math.min(10, nodes.length);
    let totalPathLength = 0;
    let pathCount = 0;

    for (let i = 0; i < sampleSize; i++) {
      const startNode = nodes[i];
      const distances = new Map<string, number>();
      const queue = [startNode.id];
      distances.set(startNode.id, 0);

      while (queue.length > 0) {
        const current = queue.shift()!;
        const currentDist = distances.get(current)!;

        const neighbors = adjacency.get(current) || [];
        for (const neighbor of neighbors) {
          if (!distances.has(neighbor)) {
            distances.set(neighbor, currentDist + 1);
            queue.push(neighbor);
          }
        }
      }

      for (const dist of distances.values()) {
        if (dist > 0) {
          totalPathLength += dist;
          pathCount++;
        }
      }
    }

    return pathCount > 0 ? totalPathLength / pathCount : 0;
  }

  private countConnectedComponents(
    nodes: GraphNode[],
    edges: GraphEdge[],
  ): number {
    if (nodes.length === 0) return 0;

    const adjacency = new Map<string, Set<string>>();
    for (const edge of edges) {
      if (!adjacency.has(edge.source)) {
        adjacency.set(edge.source, new Set());
      }
      if (!adjacency.has(edge.target)) {
        adjacency.set(edge.target, new Set());
      }
      adjacency.get(edge.source)!.add(edge.target);
      adjacency.get(edge.target)!.add(edge.source);
    }

    const visited = new Set<string>();
    let components = 0;

    for (const node of nodes) {
      if (!visited.has(node.id)) {
        components++;
        const queue = [node.id];

        while (queue.length > 0) {
          const current = queue.shift()!;
          if (visited.has(current)) continue;

          visited.add(current);
          const neighbors = adjacency.get(current) || new Set();
          for (const neighbor of neighbors) {
            if (!visited.has(neighbor)) {
              queue.push(neighbor);
            }
          }
        }
      }
    }

    return components;
  }

  private async calculateGrowthRate(): Promise<number> {
    const history = await this.getHealthHistory(30);
    if (history.length < 2) return 0;

    // Calculate storage size growth (simplified)
    return 1024; // Placeholder: 1KB/day
  }
}

// ============================================================================
// Issue Detectors
// ============================================================================

interface IssueDetector {
  name: string;
  detect(
    kg: KnowledgeGraph,
    metrics: {
      dataQuality: DataQualityMetrics;
      structureHealth: StructureHealthMetrics;
      performance: PerformanceMetrics;
    },
  ): Promise<HealthIssue[]>;
}

function createIssueDetectors(): IssueDetector[] {
  return [
    {
      name: "orphaned_edges",
      async detect(kg, metrics) {
        if (metrics.dataQuality.orphanedEdgeCount > 10) {
          return [
            {
              id: "orphaned_edges_high",
              severity: "high",
              category: "integrity",
              description: `Found ${metrics.dataQuality.orphanedEdgeCount} orphaned relationships`,
              affectedEntities: [],
              remediation: "Run kg.removeOrphanedEdges() to clean up",
              detectedAt: new Date().toISOString(),
              autoFixable: true,
            },
          ];
        }
        return [];
      },
    },
    {
      name: "stale_data",
      async detect(kg, metrics) {
        if (metrics.dataQuality.staleNodeCount > 20) {
          return [
            {
              id: "stale_data_high",
              severity: "medium",
              category: "quality",
              description: `${metrics.dataQuality.staleNodeCount} nodes haven't been updated in 30+ days`,
              affectedEntities: [],
              remediation: "Re-analyze stale projects to refresh data",
              detectedAt: new Date().toISOString(),
              autoFixable: false,
            },
          ];
        }
        return [];
      },
    },
    {
      name: "low_completeness",
      async detect(kg, metrics) {
        if (metrics.dataQuality.completenessScore < 0.7) {
          return [
            {
              id: "low_completeness",
              severity: "high",
              category: "quality",
              description: `Completeness score is ${Math.round(
                metrics.dataQuality.completenessScore * 100,
              )}%`,
              affectedEntities: [],
              remediation: "Review projects for missing relationships",
              detectedAt: new Date().toISOString(),
              autoFixable: false,
            },
          ];
        }
        return [];
      },
    },
    {
      name: "isolated_nodes",
      async detect(kg, metrics) {
        const threshold = metrics.structureHealth.isolatedNodeCount;
        if (threshold > metrics.dataQuality.totalNodes * 0.05) {
          return [
            {
              id: "isolated_nodes_high",
              severity: "medium",
              category: "structure",
              description: `${threshold} nodes are isolated (no connections)`,
              affectedEntities: [],
              remediation: "Review and connect isolated nodes",
              detectedAt: new Date().toISOString(),
              autoFixable: false,
            },
          ];
        }
        return [];
      },
    },
    {
      name: "duplicate_entities",
      async detect(kg, metrics) {
        if (metrics.dataQuality.duplicateCount > 0) {
          return [
            {
              id: "duplicate_entities",
              severity: "critical",
              category: "integrity",
              description: `Found ${metrics.dataQuality.duplicateCount} duplicate entities`,
              affectedEntities: [],
              remediation: "Merge duplicate entities",
              detectedAt: new Date().toISOString(),
              autoFixable: false,
            },
          ];
        }
        return [];
      },
    },
  ];
}

// ============================================================================
// Performance Tracker
// ============================================================================

class PerformanceTracker {
  private queryTimes: number[] = [];
  private maxSamples = 100;

  trackQuery(timeMs: number): void {
    this.queryTimes.push(timeMs);
    if (this.queryTimes.length > this.maxSamples) {
      this.queryTimes.shift();
    }
  }

  getAverageQueryTime(): number {
    if (this.queryTimes.length === 0) return 0;
    return (
      this.queryTimes.reduce((sum, t) => sum + t, 0) / this.queryTimes.length
    );
  }
}

```

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

```typescript
import { readFile, writeFile, mkdir } from "fs/promises";
import { join } from "path";
import { z } from "zod";
import { MCPToolResponse } from "../types/api.js";

// Input validation schema
const ReadmeBestPracticesInputSchema = z.object({
  readme_path: z.string().describe("Path to the README file to analyze"),
  project_type: z
    .enum(["library", "application", "tool", "documentation", "framework"])
    .optional()
    .default("library")
    .describe("Type of project for tailored analysis"),
  generate_template: z
    .boolean()
    .optional()
    .default(false)
    .describe("Generate README templates and community files"),
  output_directory: z
    .string()
    .optional()
    .describe("Directory to write generated templates and community files"),
  include_community_files: z
    .boolean()
    .optional()
    .default(true)
    .describe(
      "Generate community health files (CONTRIBUTING.md, CODE_OF_CONDUCT.md, etc.)",
    ),
  target_audience: z
    .enum(["beginner", "intermediate", "advanced", "mixed"])
    .optional()
    .default("mixed")
    .describe("Target audience for recommendations"),
});

type ReadmeBestPracticesInput = z.infer<typeof ReadmeBestPracticesInputSchema>;

interface ChecklistItem {
  category: string;
  item: string;
  present: boolean;
  severity: "critical" | "important" | "recommended";
  description: string;
  example?: string;
}

interface BestPracticesReport {
  overallScore: number;
  grade: string;
  checklist: ChecklistItem[];
  recommendations: string[];
  templates: Record<string, string>;
  communityFiles: Record<string, string>;
  summary: {
    criticalIssues: number;
    importantIssues: number;
    recommendedImprovements: number;
    sectionsPresent: number;
    totalSections: number;
    estimatedImprovementTime: string;
  };
}

export async function readmeBestPractices(
  input: Partial<ReadmeBestPracticesInput>,
): Promise<
  MCPToolResponse<{
    bestPracticesReport: BestPracticesReport;
    recommendations: string[];
    nextSteps: string[];
  }>
> {
  const startTime = Date.now();

  try {
    // Validate input with defaults
    const validatedInput = ReadmeBestPracticesInputSchema.parse(input);
    const {
      readme_path,
      project_type,
      generate_template,
      output_directory,
      include_community_files,
      target_audience,
    } = validatedInput;

    // Read README content
    let readmeContent = "";
    try {
      readmeContent = await readFile(readme_path, "utf-8");
    } catch (error) {
      if (!generate_template) {
        return {
          success: false,
          error: {
            code: "README_NOT_FOUND",
            message:
              "README file not found. Use generate_template: true to create a new README.",
            details: error instanceof Error ? error.message : "Unknown error",
            resolution:
              "Set generate_template: true to create a new README from template",
          },
          metadata: {
            toolVersion: "1.0.0",
            executionTime: Date.now() - startTime,
            timestamp: new Date().toISOString(),
          },
        };
      }
    }

    // Generate checklist based on project type and content
    const checklist = generateChecklist(
      readmeContent,
      project_type,
      target_audience,
    );

    // Calculate overall score
    const { score, grade } = calculateOverallScore(checklist);

    // Generate recommendations
    const recommendations = generateRecommendations(
      checklist,
      project_type,
      target_audience,
    );

    // Generate templates if requested
    const templates = generate_template
      ? generateTemplates(project_type, generate_template)
      : {};

    // Generate community files if requested
    const communityFiles = include_community_files
      ? generateCommunityFiles(project_type)
      : {};

    // Calculate summary metrics
    const summary = calculateSummaryMetrics(checklist);

    // Write files if output directory specified
    if (output_directory && generate_template) {
      await writeGeneratedFiles(
        templates,
        communityFiles,
        output_directory,
        readme_path,
      );
    }

    const report: BestPracticesReport = {
      overallScore: score,
      grade,
      checklist,
      recommendations,
      templates,
      communityFiles,
      summary,
    };

    const nextSteps = generateNextSteps(
      report.checklist,
      true,
      output_directory,
    );

    return {
      success: true,
      data: {
        bestPracticesReport: report,
        recommendations,
        nextSteps,
      },
      metadata: {
        toolVersion: "1.0.0",
        executionTime: Date.now() - startTime,
        timestamp: new Date().toISOString(),
        analysisId: `readme-best-practices-${Date.now()}`,
      },
    };
  } catch (error) {
    return {
      success: false,
      error: {
        code: "ANALYSIS_FAILED",
        message: "Failed to analyze README best practices",
        details: error instanceof Error ? error.message : "Unknown error",
        resolution:
          "Check README file path and permissions, ensure valid project type",
      },
      metadata: {
        toolVersion: "1.0.0",
        executionTime: Date.now() - startTime,
        timestamp: new Date().toISOString(),
      },
    };
  }
}

function generateChecklist(
  content: string,
  projectType: string,
  _targetAudience: string,
): ChecklistItem[] {
  const checklist: ChecklistItem[] = [];
  const lines = content.split("\n");
  const lowerContent = content.toLowerCase();

  // Essential Sections
  checklist.push({
    category: "Essential Sections",
    item: "Project Title",
    present: /^#\s+.+/m.test(content),
    severity: "critical",
    description: "Clear, descriptive project title as main heading",
    example: "# My Awesome Project",
  });

  checklist.push({
    category: "Essential Sections",
    item: "One-line Description",
    present:
      />\s*.+/.test(content) ||
      lines.some(
        (line) =>
          line.trim().length > 20 &&
          line.trim().length < 100 &&
          !line.startsWith("#"),
      ),
    severity: "critical",
    description: "Brief one-line description of what the project does",
    example:
      "> A fast, lightweight JavaScript framework for building web applications",
  });

  checklist.push({
    category: "Essential Sections",
    item: "Installation Instructions",
    present:
      /install/i.test(lowerContent) &&
      /npm|yarn|pip|cargo|go get|git clone/i.test(lowerContent),
    severity: "critical",
    description: "Clear installation or setup instructions",
    example: "```bash\nnpm install package-name\n```",
  });

  checklist.push({
    category: "Essential Sections",
    item: "Basic Usage Example",
    present:
      /usage|example|quick start|getting started/i.test(lowerContent) &&
      /```/.test(content),
    severity: "critical",
    description: "Working code example showing basic usage",
    example:
      '```javascript\nconst lib = require("package-name");\nlib.doSomething();\n```',
  });

  // Important Sections
  checklist.push({
    category: "Important Sections",
    item: "Prerequisites/Requirements",
    present:
      /prerequisite|requirement|dependencies|node|python|java|version/i.test(
        lowerContent,
      ),
    severity: "important",
    description: "Clear system requirements and dependencies",
    example: "- Node.js 16+\n- Docker (optional)",
  });

  checklist.push({
    category: "Important Sections",
    item: "License Information",
    present:
      /license/i.test(lowerContent) || /mit|apache|gpl|bsd/i.test(lowerContent),
    severity: "important",
    description: "Clear license information",
    example: "## License\n\nMIT License - see [LICENSE](LICENSE) file",
  });

  checklist.push({
    category: "Important Sections",
    item: "Contributing Guidelines",
    present: /contribut/i.test(lowerContent),
    severity: "important",
    description: "Information on how to contribute to the project",
    example: "See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines",
  });

  // Community Health
  checklist.push({
    category: "Community Health",
    item: "Code of Conduct",
    present: /code of conduct/i.test(lowerContent),
    severity: "recommended",
    description: "Link to code of conduct for community projects",
    example: "Please read our [Code of Conduct](CODE_OF_CONDUCT.md)",
  });

  checklist.push({
    category: "Community Health",
    item: "Issue Templates",
    present: /issue template|bug report|feature request/i.test(lowerContent),
    severity: "recommended",
    description: "Reference to issue templates for better bug reports",
    example:
      "Use our [issue templates](.github/ISSUE_TEMPLATE/) when reporting bugs",
  });

  // Visual Elements
  checklist.push({
    category: "Visual Elements",
    item: "Badges",
    present:
      /\[!\[.*\]\(.*\)\]\(.*\)/.test(content) || /badge/i.test(lowerContent),
    severity: "recommended",
    description: "Status badges for build, version, license, etc.",
    example: "[![Build Status](badge-url)](link-url)",
  });

  checklist.push({
    category: "Visual Elements",
    item: "Screenshots/Demo",
    present:
      /!\[.*\]\(.*\.(png|jpg|jpeg|gif|webp)\)/i.test(content) ||
      /screenshot|demo|gif/i.test(lowerContent),
    severity:
      projectType === "application" || projectType === "tool"
        ? "important"
        : "recommended",
    description:
      "Visual demonstration of the project (especially for applications)",
    example: "![Demo](demo.gif)",
  });

  // Content Quality
  checklist.push({
    category: "Content Quality",
    item: "Appropriate Length",
    present: lines.length >= 20 && lines.length <= 300,
    severity: "important",
    description:
      "README length appropriate for project complexity (20-300 lines)",
    example: "Keep main README focused, link to detailed docs",
  });

  checklist.push({
    category: "Content Quality",
    item: "Clear Section Headers",
    present: (content.match(/^##\s+/gm) || []).length >= 3,
    severity: "important",
    description: "Well-organized content with clear section headers",
    example: "## Installation\n## Usage\n## Contributing",
  });

  checklist.push({
    category: "Content Quality",
    item: "Working Links",
    present: !/\[.*\]\(\)/.test(content) && !/\[.*\]\(#\)/.test(content),
    severity: "important",
    description:
      "All links should be functional (no empty or placeholder links)",
    example: "[Documentation](https://example.com/docs)",
  });

  // Project-specific checks
  if (projectType === "library" || projectType === "framework") {
    checklist.push({
      category: "Library Specific",
      item: "API Documentation",
      present: /api|methods|functions|reference/i.test(lowerContent),
      severity: "important",
      description: "API documentation or link to detailed API reference",
      example:
        "See [API Documentation](docs/api.md) for detailed method reference",
    });
  }

  if (projectType === "application" || projectType === "tool") {
    checklist.push({
      category: "Application Specific",
      item: "Configuration Options",
      present: /config|settings|options|environment/i.test(lowerContent),
      severity: "important",
      description: "Configuration and customization options",
      example: "See [Configuration Guide](docs/configuration.md)",
    });
  }

  return checklist;
}

function calculateOverallScore(checklist: ChecklistItem[]): {
  score: number;
  grade: string;
} {
  const weights = { critical: 3, important: 2, recommended: 1 };
  let totalScore = 0;
  let maxScore = 0;

  checklist.forEach((item) => {
    const weight = weights[item.severity];
    maxScore += weight;
    if (item.present) {
      totalScore += weight;
    }
  });

  const percentage =
    maxScore > 0 ? Math.round((totalScore / maxScore) * 100) : 0;

  let grade: string;
  if (percentage >= 90) grade = "A";
  else if (percentage >= 80) grade = "B";
  else if (percentage >= 70) grade = "C";
  else if (percentage >= 60) grade = "D";
  else grade = "F";

  return { score: percentage, grade };
}

function generateRecommendations(
  checklist: ChecklistItem[],
  projectType: string,
  targetAudience: string,
): string[] {
  const recommendations: string[] = [];
  const missing = checklist.filter((item) => !item.present);

  // Critical issues first
  const critical = missing.filter((item) => item.severity === "critical");
  if (critical.length > 0) {
    recommendations.push(
      `🚨 Critical: Fix ${critical.length} essential sections: ${critical
        .map((item) => item.item)
        .join(", ")}`,
    );
  }

  // Important issues
  const important = missing.filter((item) => item.severity === "important");
  if (important.length > 0) {
    recommendations.push(
      `⚠️ Important: Add ${important.length} key sections: ${important
        .map((item) => item.item)
        .join(", ")}`,
    );
  }

  // Project-specific recommendations
  if (projectType === "library") {
    recommendations.push(
      "📚 Library Focus: Emphasize installation, basic usage, and API documentation",
    );
  } else if (projectType === "application") {
    recommendations.push(
      "🖥️ Application Focus: Include screenshots, configuration options, and deployment guides",
    );
  }

  // Target audience specific recommendations
  if (targetAudience === "beginner") {
    recommendations.push(
      "👶 Beginner-Friendly: Use simple language, provide detailed examples, include troubleshooting",
    );
  } else if (targetAudience === "advanced") {
    recommendations.push(
      "🎯 Advanced Users: Focus on technical details, performance notes, and extensibility",
    );
  }

  // General improvements
  const recommended = missing.filter((item) => item.severity === "recommended");
  if (recommended.length > 0) {
    recommendations.push(
      `✨ Enhancement: Consider adding ${recommended
        .map((item) => item.item)
        .join(", ")}`,
    );
  }

  return recommendations;
}

function generateTemplates(
  projectType: string,
  _generateTemplate: boolean,
): Record<string, string> {
  const templates: Record<string, string> = {};

  if (projectType === "library") {
    templates["README-library.md"] = `# Project Name

> One-line description of what this library does

[![Build Status][build-badge]][build-link]
[![npm version][npm-badge]][npm-link]
[![License][license-badge]][license-link]

## TL;DR

What it does in 2-3 sentences. Who should use it.

## Quick Start

### Install
\`\`\`bash
npm install package-name
\`\`\`

### Use
\`\`\`javascript
const lib = require('package-name');

// Basic usage example
const result = lib.doSomething();
console.log(result);
\`\`\`

## When to Use This

- ✅ When you need X functionality
- ✅ When you want Y capability
- ❌ When you need Z (use [alternative] instead)

## API Reference

### \`doSomething(options)\`

Description of the main method.

**Parameters:**
- \`options\` (Object): Configuration options
  - \`param1\` (string): Description of parameter
  - \`param2\` (boolean, optional): Description of optional parameter

**Returns:** Description of return value

**Example:**
\`\`\`javascript
const result = lib.doSomething({
  param1: 'value',
  param2: true
});
\`\`\`

## Full Documentation

[Link to full documentation](docs/)

## Contributing

We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.

## License

MIT License - see [LICENSE](LICENSE) file for details.

[build-badge]: https://github.com/username/repo/workflows/CI/badge.svg
[build-link]: https://github.com/username/repo/actions
[npm-badge]: https://img.shields.io/npm/v/package-name.svg
[npm-link]: https://www.npmjs.com/package/package-name
[license-badge]: https://img.shields.io/badge/license-MIT-blue.svg
[license-link]: LICENSE
`;
  }

  if (projectType === "application" || projectType === "tool") {
    templates["README-application.md"] = `# Project Name

> One-line description of what this application does

![Demo](demo.gif)

## What This Does

Brief explanation of the application's purpose and key features:

- 🚀 Feature 1: Description
- 📊 Feature 2: Description
- 🔧 Feature 3: Description

## Quick Start

### Prerequisites
- Node.js 16+
- Docker (optional)
- Other requirements

### Install & Run
\`\`\`bash
git clone https://github.com/username/repo.git
cd project-name
npm install
npm start
\`\`\`

Visit \`http://localhost:3000\` to see the application.

## Configuration

### Environment Variables
\`\`\`bash
# Copy example config
cp .env.example .env

# Edit configuration
nano .env
\`\`\`

### Key Settings
- \`PORT\`: Server port (default: 3000)
- \`DATABASE_URL\`: Database connection string
- \`API_KEY\`: External service API key

## Usage Examples

### Basic Usage
\`\`\`bash
npm run command -- --option value
\`\`\`

### Advanced Usage
\`\`\`bash
npm run command -- --config custom.json --verbose
\`\`\`

## Deployment

See [Deployment Guide](docs/deployment.md) for production setup.

## Troubleshooting

### Common Issues

**Issue 1: Error message**
- Solution: Steps to resolve

**Issue 2: Another error**
- Solution: Steps to resolve

See [FAQ](docs/FAQ.md) for more help.

## Contributing

We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.

## License

MIT License - see [LICENSE](LICENSE) file for details.
`;
  }

  return templates;
}

function generateCommunityFiles(_projectType: string): Record<string, string> {
  const files: Record<string, string> = {};

  files["CONTRIBUTING.md"] = `# Contributing to Project Name

Thank you for your interest in contributing! This document provides guidelines for contributing to this project.

## Getting Started

1. Fork the repository
2. Clone your fork: \`git clone https://github.com/yourusername/repo.git\`
3. Create a feature branch: \`git checkout -b feature-name\`
4. Make your changes
5. Test your changes: \`npm test\`
6. Commit your changes: \`git commit -m "Description of changes"\`
7. Push to your fork: \`git push origin feature-name\`
8. Create a Pull Request

## Development Setup

\`\`\`bash
npm install
npm run dev
\`\`\`

## Code Style

- Use TypeScript for new code
- Follow existing code formatting
- Run \`npm run lint\` before committing
- Add tests for new features

## Pull Request Guidelines

- Keep PRs focused and small
- Include tests for new functionality
- Update documentation as needed
- Ensure CI passes
- Link to relevant issues

## Reporting Issues

Use our [issue templates](.github/ISSUE_TEMPLATE/) when reporting bugs or requesting features.

## Code of Conduct

Please read and follow our [Code of Conduct](CODE_OF_CONDUCT.md).
`;

  files["CODE_OF_CONDUCT.md"] = `# Code of Conduct

## Our Pledge

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.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members

Examples of unacceptable behavior include:

- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting

## Enforcement

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.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 1.4.
`;

  files["SECURITY.md"] = `# Security Policy

## Supported Versions

| Version | Supported          |
| ------- | ------------------ |
| 1.x.x   | :white_check_mark: |
| < 1.0   | :x:                |

## Reporting a Vulnerability

If you discover a security vulnerability, please report it privately:

1. **Do not** create a public issue
2. Email [email protected] with details
3. Include steps to reproduce if possible
4. We will respond within 48 hours

## Security Best Practices

When using this project:

- Keep dependencies updated
- Use environment variables for secrets
- Follow principle of least privilege
- Regularly audit your setup

Thank you for helping keep our project secure!
`;

  return files;
}

async function writeGeneratedFiles(
  templates: Record<string, string>,
  communityFiles: Record<string, string>,
  outputDirectory: string,
  _originalReadmePath: string,
): Promise<void> {
  try {
    // Create output directory
    await mkdir(outputDirectory, { recursive: true });

    // Write templates
    for (const [filename, content] of Object.entries(templates)) {
      const filePath = join(outputDirectory, filename);
      await writeFile(filePath, content, "utf-8");
    }

    // Write community files
    for (const [filename, content] of Object.entries(communityFiles)) {
      const filePath = join(outputDirectory, filename);
      await writeFile(filePath, content, "utf-8");
    }

    // Create .github directory structure
    const githubDir = join(outputDirectory, ".github");
    await mkdir(githubDir, { recursive: true });

    const issueTemplateDir = join(githubDir, "ISSUE_TEMPLATE");
    await mkdir(issueTemplateDir, { recursive: true });

    // Bug report template
    const bugReportTemplate = `---
name: Bug report
about: Create a report to help us improve
title: '[BUG] '
labels: bug
assignees: ''
---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Environment:**
 - OS: [e.g. iOS]
 - Browser [e.g. chrome, safari]
 - Version [e.g. 22]

**Additional context**
Add any other context about the problem here.
`;

    await writeFile(
      join(issueTemplateDir, "bug_report.yml"),
      bugReportTemplate,
      "utf-8",
    );

    // Feature request template
    const featureRequestTemplate = `---
name: Feature request
about: Suggest an idea for this project
title: '[FEATURE] '
labels: enhancement
assignees: ''
---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.
`;

    await writeFile(
      join(issueTemplateDir, "feature_request.yml"),
      featureRequestTemplate,
      "utf-8",
    );

    // Pull request template
    const prTemplate = `## Description
Brief description of changes made.

## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update

## Testing
- [ ] Tests pass locally
- [ ] New tests added for new functionality
- [ ] Manual testing completed

## Checklist
- [ ] Code follows project style guidelines
- [ ] Self-review completed
- [ ] Documentation updated
- [ ] No new warnings introduced
`;

    await writeFile(
      join(githubDir, "PULL_REQUEST_TEMPLATE.md"),
      prTemplate,
      "utf-8",
    );
  } catch (error) {
    throw new Error(
      `Failed to write generated files: ${
        error instanceof Error ? error.message : "Unknown error"
      }`,
    );
  }
}

function calculateSummaryMetrics(checklist: ChecklistItem[]) {
  const criticalIssues = checklist.filter(
    (item) => !item.present && item.severity === "critical",
  ).length;
  const importantIssues = checklist.filter(
    (item) => !item.present && item.severity === "important",
  ).length;
  const recommendedImprovements = checklist.filter(
    (item) => !item.present && item.severity === "recommended",
  ).length;
  const sectionsPresent = checklist.filter((item) => item.present).length;
  const totalSections = checklist.length;

  // Estimate improvement time based on missing items
  const totalMissing =
    criticalIssues + importantIssues + recommendedImprovements;
  let estimatedTime = "";
  if (totalMissing === 0) {
    estimatedTime = "No improvements needed";
  } else if (totalMissing <= 3) {
    estimatedTime = "30 minutes - 1 hour";
  } else if (totalMissing <= 6) {
    estimatedTime = "1-2 hours";
  } else if (totalMissing <= 10) {
    estimatedTime = "2-4 hours";
  } else {
    estimatedTime = "4+ hours (consider phased approach)";
  }

  return {
    criticalIssues,
    importantIssues,
    recommendedImprovements,
    sectionsPresent,
    totalSections,
    estimatedImprovementTime: estimatedTime,
  };
}

function generateNextSteps(
  checklist: ChecklistItem[],
  generateTemplate: boolean,
  outputDirectory?: string,
): string[] {
  const nextSteps: string[] = [];
  const missing = checklist.filter((item) => !item.present);

  if (missing.length === 0) {
    nextSteps.push(
      "✅ README follows all best practices - no immediate action needed",
    );
    nextSteps.push(
      "📊 Consider periodic reviews to maintain quality as project evolves",
    );
    return nextSteps;
  }

  // Critical issues first
  const critical = missing.filter((item) => item.severity === "critical");
  if (critical.length > 0) {
    nextSteps.push(
      `🚨 Priority 1: Address ${critical.length} critical issues immediately`,
    );
    critical.forEach((item) => {
      nextSteps.push(`   • Add ${item.item}: ${item.description}`);
    });
  }

  // Important issues
  const important = missing.filter((item) => item.severity === "important");
  if (important.length > 0) {
    nextSteps.push(
      `⚠️ Priority 2: Address ${important.length} important sections within 1 week`,
    );
  }

  // Template usage
  if (generateTemplate && outputDirectory) {
    nextSteps.push(`📝 Review generated templates in ${outputDirectory}/`);
    nextSteps.push("🔄 Customize templates to match your project specifics");
    nextSteps.push(
      "📋 Use community files (.github templates, CONTRIBUTING.md) to improve project health",
    );
  }

  // General improvements
  nextSteps.push(
    "🔍 Run this analysis periodically to maintain README quality",
  );
  nextSteps.push(
    "👥 Consider getting feedback from new users on README clarity",
  );

  return nextSteps;
}

```

--------------------------------------------------------------------------------
/src/tools/recommend-ssg.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";
import { MCPToolResponse, formatMCPResponse } from "../types/api.js";
import {
  getKnowledgeGraph,
  getProjectContext,
  getMemoryManager,
} from "../memory/kg-integration.js";
import { getUserPreferenceManager } from "../memory/user-preferences.js";

// SSG scoring matrix based on ADR-003
export interface SSGRecommendation {
  recommended: "jekyll" | "hugo" | "docusaurus" | "mkdocs" | "eleventy";
  confidence: number;
  reasoning: string[];
  alternatives: Array<{
    name: string;
    score: number;
    pros: string[];
    cons: string[];
  }>;
  historicalData?: {
    similarProjectCount: number;
    successRates: Record<string, { rate: number; sampleSize: number }>;
    topPerformer?: {
      ssg: string;
      successRate: number;
      deploymentCount: number;
    };
  };
}

const inputSchema = z.object({
  analysisId: z.string(),
  userId: z.string().optional().default("default"),
  preferences: z
    .object({
      priority: z.enum(["simplicity", "features", "performance"]).optional(),
      ecosystem: z
        .enum(["javascript", "python", "ruby", "go", "any"])
        .optional(),
    })
    .optional(),
});

/**
 * Phase 2.1: Retrieve historical deployment data from knowledge graph
 */
async function getHistoricalDeploymentData(
  projectPath?: string,
  technologies?: string[],
): Promise<{
  similarProjectCount: number;
  successRates: Record<string, { rate: number; sampleSize: number }>;
  topPerformer?: {
    ssg: string;
    successRate: number;
    deploymentCount: number;
  };
  globalTopPerformer?: {
    ssg: string;
    successRate: number;
    deploymentCount: number;
  };
}> {
  try {
    const kg = await getKnowledgeGraph();

    // Get ALL projects for finding global top performers
    const allProjects = await kg.findNodes({ type: "project" });

    // Find similar projects (either by path or by shared technologies)
    let similarProjects = allProjects;

    if (projectPath) {
      // Get context for current project
      const context = await getProjectContext(projectPath);

      // If project exists in KG, use its similar projects
      if (context.similarProjects.length > 0) {
        similarProjects = context.similarProjects;
      } else if (technologies && technologies.length > 0) {
        // Project doesn't exist yet, but we have technologies - find similar by tech
        const techSet = new Set(technologies.map((t) => t.toLowerCase()));
        const projectsWithTech = [] as typeof similarProjects;

        for (const project of allProjects) {
          const projectTechs = project.properties.technologies || [];
          const hasShared = projectTechs.some((t: string) =>
            techSet.has(t.toLowerCase()),
          );
          if (hasShared) {
            projectsWithTech.push(project);
          }
        }
        similarProjects = projectsWithTech;
      } else {
        // No project found and no technologies provided
        similarProjects = [];
      }
    } else if (technologies && technologies.length > 0) {
      // Filter by shared technologies
      const techSet = new Set(technologies.map((t) => t.toLowerCase()));
      const projectsWithTech = [] as typeof similarProjects;

      for (const project of allProjects) {
        const projectTechs = project.properties.technologies || [];
        const hasShared = projectTechs.some((t: string) =>
          techSet.has(t.toLowerCase()),
        );
        if (hasShared) {
          projectsWithTech.push(project);
        }
      }
      similarProjects = projectsWithTech;
    } else {
      // No criteria provided
      similarProjects = [];
    }

    // Aggregate deployment data by SSG for similar projects
    const ssgStats: Record<
      string,
      { successes: number; failures: number; total: number }
    > = {};

    // Also track global stats across ALL projects for finding top performers
    const globalSSGStats: Record<
      string,
      { successes: number; failures: number; total: number }
    > = {};

    // Helper function to aggregate stats for a set of projects
    const aggregateStats = async (projects: typeof allProjects) => {
      const stats: Record<
        string,
        { successes: number; failures: number; total: number }
      > = {};

      for (const project of projects) {
        const allEdges = await kg.findEdges({ source: project.id });
        const deployments = allEdges.filter(
          (e) =>
            e.type.startsWith("project_deployed_with") ||
            e.properties.baseType === "project_deployed_with",
        );

        for (const deployment of deployments) {
          const allNodes = await kg.getAllNodes();
          const configNode = allNodes.find((n) => n.id === deployment.target);

          if (configNode && configNode.type === "configuration") {
            const ssg = configNode.properties.ssg;
            if (!stats[ssg]) {
              stats[ssg] = { successes: 0, failures: 0, total: 0 };
            }

            stats[ssg].total++;
            if (deployment.properties.success) {
              stats[ssg].successes++;
            } else {
              stats[ssg].failures++;
            }
          }
        }
      }
      return stats;
    };

    // Aggregate for similar projects
    Object.assign(ssgStats, await aggregateStats(similarProjects));

    // Aggregate for ALL projects (for global top performer)
    Object.assign(globalSSGStats, await aggregateStats(allProjects));

    // Calculate success rates for similar projects
    const successRates: Record<string, { rate: number; sampleSize: number }> =
      {};
    let topPerformer:
      | { ssg: string; successRate: number; deploymentCount: number }
      | undefined;
    let maxRate = 0;

    for (const [ssg, stats] of Object.entries(ssgStats)) {
      if (stats.total > 0) {
        const rate = stats.successes / stats.total;
        successRates[ssg] = {
          rate,
          sampleSize: stats.total,
        };

        // Track top performer in similar projects (require at least 2 deployments)
        if (stats.total >= 2 && rate > maxRate) {
          maxRate = rate;
          topPerformer = {
            ssg,
            successRate: rate,
            deploymentCount: stats.total,
          };
        }
      }
    }

    // Calculate global top performer from ALL projects
    let globalTopPerformer:
      | { ssg: string; successRate: number; deploymentCount: number }
      | undefined;
    let globalMaxRate = 0;

    for (const [ssg, stats] of Object.entries(globalSSGStats)) {
      if (stats.total >= 2) {
        const rate = stats.successes / stats.total;
        if (rate > globalMaxRate) {
          globalMaxRate = rate;
          globalTopPerformer = {
            ssg,
            successRate: rate,
            deploymentCount: stats.total,
          };
        }
      }
    }

    return {
      similarProjectCount: similarProjects.length,
      successRates,
      topPerformer,
      globalTopPerformer,
    };
  } catch (error) {
    console.warn("Failed to retrieve historical deployment data:", error);
    return {
      similarProjectCount: 0,
      successRates: {},
    };
  }
}

/**
 * Recommends the optimal static site generator (SSG) for a project based on analysis and historical data.
 *
 * This function provides intelligent SSG recommendations by analyzing project characteristics,
 * considering user preferences, and leveraging historical deployment data from the knowledge graph.
 * It uses a multi-criteria decision analysis approach to score different SSGs and provide
 * confidence-weighted recommendations with detailed reasoning.
 *
 * @param args - The input arguments for SSG recommendation
 * @param args.analysisId - Unique identifier from a previous repository analysis
 * @param args.userId - User identifier for personalized recommendations (defaults to "default")
 * @param args.preferences - Optional user preferences for recommendation weighting
 * @param args.preferences.priority - Priority focus: "simplicity", "features", or "performance"
 * @param args.preferences.ecosystem - Preferred technology ecosystem: "javascript", "python", "ruby", "go", or "any"
 *
 * @returns Promise resolving to SSG recommendation results
 * @returns content - Array containing the recommendation results in MCP tool response format
 *
 * @throws {Error} When the analysis ID is invalid or not found
 * @throws {Error} When historical data cannot be retrieved
 * @throws {Error} When recommendation scoring fails
 *
 * @example
 * ```typescript
 * // Basic recommendation
 * const recommendation = await recommendSSG({
 *   analysisId: "analysis_abc123_def456",
 *   userId: "user123"
 * });
 *
 * // With preferences
 * const personalized = await recommendSSG({
 *   analysisId: "analysis_abc123_def456",
 *   userId: "user123",
 *   preferences: {
 *     priority: "performance",
 *     ecosystem: "javascript"
 *   }
 * });
 * ```
 *
 * @since 1.0.0
 * @version 1.2.0 - Added historical data integration and user preferences
 */
export async function recommendSSG(
  args: unknown,
  context?: any,
): Promise<{ content: any[] }> {
  const startTime = Date.now();
  const { analysisId, userId, preferences } = inputSchema.parse(args);

  const prioritizeSimplicity = preferences?.priority === "simplicity";
  const ecosystemPreference = preferences?.ecosystem;

  // Report initial progress
  if (context?.meta?.progressToken) {
    await context.meta.reportProgress?.({
      progress: 0,
      total: 100,
    });
  }

  await context?.info?.("🔍 Starting SSG recommendation engine...");

  // Phase 2.2: Get user preference manager
  await context?.info?.(`👤 Loading preferences for user: ${userId}...`);
  const userPreferenceManager = await getUserPreferenceManager(userId);

  if (context?.meta?.progressToken) {
    await context.meta.reportProgress?.({
      progress: 15,
      total: 100,
    });
  }

  try {
    // Try to retrieve analysis from memory
    await context?.info?.(`📊 Retrieving analysis: ${analysisId}...`);
    let analysisData = null;
    try {
      const manager = await getMemoryManager();
      const analysis = await manager.recall(analysisId);
      if (analysis && analysis.data) {
        // Handle the wrapped content structure
        if (analysis.data.content && Array.isArray(analysis.data.content)) {
          // Extract the JSON from the first text content
          const firstContent = analysis.data.content[0];
          if (
            firstContent &&
            firstContent.type === "text" &&
            firstContent.text
          ) {
            try {
              analysisData = JSON.parse(firstContent.text);
            } catch (parseError) {
              // If parse fails, try the direct data
              analysisData = analysis.data;
            }
          }
        } else {
          // Direct data structure
          analysisData = analysis.data;
        }
      }
    } catch (error) {
      // If memory retrieval fails, continue with fallback logic
      console.warn(
        `Could not retrieve analysis ${analysisId} from memory:`,
        error,
      );
    }

    if (context?.meta?.progressToken) {
      await context.meta.reportProgress?.({
        progress: 30,
        total: 100,
      });
    }

    // Phase 2.1: Retrieve historical deployment data
    await context?.info?.("📈 Analyzing historical deployment data...");
    let historicalData:
      | {
          similarProjectCount: number;
          successRates: Record<string, { rate: number; sampleSize: number }>;
          topPerformer?: {
            ssg: string;
            successRate: number;
            deploymentCount: number;
          };
          globalTopPerformer?: {
            ssg: string;
            successRate: number;
            deploymentCount: number;
          };
        }
      | undefined;

    if (analysisData) {
      const projectPath = analysisData.path;
      const technologies = analysisData.dependencies?.languages || [];
      historicalData = await getHistoricalDeploymentData(
        projectPath,
        technologies,
      );

      if (historicalData && historicalData.similarProjectCount > 0) {
        await context?.info?.(
          `✨ Found ${historicalData.similarProjectCount} similar project(s) with deployment history`,
        );
      }
    }

    if (context?.meta?.progressToken) {
      await context.meta.reportProgress?.({
        progress: 50,
        total: 100,
      });
    }

    await context?.info?.("🤔 Calculating SSG recommendations...");

    // Determine recommendation based on analysis data if available
    let finalRecommendation:
      | "jekyll"
      | "hugo"
      | "docusaurus"
      | "mkdocs"
      | "eleventy";
    let reasoning: string[] = [];
    let confidence = 0.85;

    if (analysisData) {
      // Use actual analysis data to make informed recommendation
      const ecosystem = analysisData.dependencies?.ecosystem || "unknown";
      const hasReact = analysisData.dependencies?.packages?.some(
        (p: string) => p.includes("react") || p.includes("next"),
      );
      const complexity =
        analysisData.documentation?.estimatedComplexity || "moderate";
      const teamSize = analysisData.recommendations?.teamSize || "small";

      // Logic based on real analysis
      if (ecosystem === "python") {
        finalRecommendation = "mkdocs";
        reasoning = [
          "Python ecosystem detected - MkDocs integrates naturally",
          "Simple configuration with YAML",
          "Material theme provides excellent UI out of the box",
          "Strong Python community support",
        ];
      } else if (ecosystem === "ruby") {
        finalRecommendation = "jekyll";
        reasoning = [
          "Ruby ecosystem detected - Jekyll is the native choice",
          "GitHub Pages native support",
          "Simple static site generation",
          "Extensive theme ecosystem",
        ];
      } else if (hasReact || ecosystem === "javascript") {
        if (complexity === "complex" || teamSize === "large") {
          finalRecommendation = "docusaurus";
          reasoning = [
            "JavaScript/TypeScript ecosystem with React detected",
            "Complex project structure benefits from Docusaurus features",
            "Built-in versioning and internationalization",
            "MDX support for interactive documentation",
          ];
        } else if (prioritizeSimplicity) {
          finalRecommendation = "eleventy";
          reasoning = [
            "JavaScript ecosystem with simplicity priority",
            "Minimal configuration required",
            "Fast build times",
            "Flexible templating options",
          ];
        } else {
          finalRecommendation = "docusaurus";
          reasoning = [
            "JavaScript/TypeScript ecosystem detected",
            "Modern React-based framework",
            "Active community and regular updates",
            "Great developer experience",
          ];
        }
      } else if (ecosystem === "go") {
        finalRecommendation = "hugo";
        reasoning = [
          "Go ecosystem detected - Hugo is written in Go",
          "Extremely fast build times",
          "No runtime dependencies",
          "Excellent for large documentation sites",
        ];
      } else {
        // Default logic when ecosystem is unknown
        if (prioritizeSimplicity) {
          finalRecommendation = "jekyll";
          reasoning = [
            "Simple setup and configuration",
            "GitHub Pages native support",
            "Extensive documentation and community",
            "Mature and stable platform",
          ];
        } else {
          finalRecommendation = "docusaurus";
          reasoning = [
            "Modern documentation framework",
            "Rich feature set out of the box",
            "Great for technical documentation",
            "Active development and support",
          ];
        }
      }

      // Apply preference overrides
      if (ecosystemPreference && ecosystemPreference !== "any") {
        if (ecosystemPreference === "python") {
          finalRecommendation = "mkdocs";
          reasoning.unshift("Python ecosystem explicitly requested");
        } else if (ecosystemPreference === "ruby") {
          finalRecommendation = "jekyll";
          reasoning.unshift("Ruby ecosystem explicitly requested");
        } else if (ecosystemPreference === "go") {
          finalRecommendation = "hugo";
          reasoning.unshift("Go ecosystem explicitly requested");
        } else if (ecosystemPreference === "javascript") {
          if (
            finalRecommendation !== "docusaurus" &&
            finalRecommendation !== "eleventy"
          ) {
            finalRecommendation = prioritizeSimplicity
              ? "eleventy"
              : "docusaurus";
            reasoning.unshift("JavaScript ecosystem explicitly requested");
          }
        }
      }

      // Adjust confidence based on data quality
      if (analysisData.structure?.totalFiles > 100) {
        confidence = Math.min(0.95, confidence + 0.05);
      }
      if (
        analysisData.documentation?.hasReadme &&
        analysisData.documentation?.hasDocs
      ) {
        confidence = Math.min(0.95, confidence + 0.05);
      }

      // Phase 2.1: Adjust recommendation and confidence based on historical data
      if (historicalData && historicalData.similarProjectCount >= 0) {
        const recommendedSuccessRate =
          historicalData.successRates[finalRecommendation];

        if (recommendedSuccessRate) {
          // Boost confidence if historically successful
          if (recommendedSuccessRate.rate >= 1.0) {
            // Perfect success rate - maximum boost
            confidence = Math.min(0.98, confidence + 0.2);
            reasoning.unshift(
              `✅ 100% success rate in ${recommendedSuccessRate.sampleSize} similar project(s)`,
            );
          } else if (
            recommendedSuccessRate.rate > 0.8 &&
            recommendedSuccessRate.sampleSize >= 2
          ) {
            // High success rate - good boost
            confidence = Math.min(0.98, confidence + 0.15);
            reasoning.unshift(
              `✅ ${(recommendedSuccessRate.rate * 100).toFixed(
                0,
              )}% success rate in ${
                recommendedSuccessRate.sampleSize
              } similar project(s)`,
            );
          } else if (
            recommendedSuccessRate.rate < 0.5 &&
            recommendedSuccessRate.sampleSize >= 2
          ) {
            // Reduce confidence if historically problematic
            confidence = Math.max(0.5, confidence - 0.15);
            reasoning.unshift(
              `⚠️ Only ${(recommendedSuccessRate.rate * 100).toFixed(
                0,
              )}% success rate in ${
                recommendedSuccessRate.sampleSize
              } similar project(s)`,
            );
          }
        } else {
          // No deployment history for recommended SSG
          // Check if similar projects had poor outcomes with OTHER SSGs
          // This indicates general deployment challenges
          const allSuccessRates = Object.values(historicalData.successRates);
          if (allSuccessRates.length > 0) {
            const avgSuccessRate =
              allSuccessRates.reduce((sum, data) => sum + data.rate, 0) /
              allSuccessRates.length;
            const totalSamples = allSuccessRates.reduce(
              (sum, data) => sum + data.sampleSize,
              0,
            );

            // If similar projects had poor deployment success overall, reduce confidence
            if (avgSuccessRate < 0.5 && totalSamples >= 2) {
              confidence = Math.max(0.6, confidence - 0.2);
              // Find the SSG with worst performance to mention
              const worstSSG = Object.entries(
                historicalData.successRates,
              ).reduce(
                (worst, [ssg, data]) =>
                  data.rate < worst.rate ? { ssg, rate: data.rate } : worst,
                { ssg: "", rate: 1.0 },
              );
              reasoning.unshift(
                `⚠️ Similar projects had deployment challenges (${
                  worstSSG.ssg
                }: ${(worstSSG.rate * 100).toFixed(0)}% success rate)`,
              );
            }
          }
        }

        // Consider switching to top performer if significantly better
        // Prefer similar project top performer, fall back to global top performer
        const performerToConsider =
          historicalData.topPerformer || historicalData.globalTopPerformer;

        if (
          performerToConsider &&
          performerToConsider.ssg !== finalRecommendation
        ) {
          const topPerformer = performerToConsider;
          const currentRate = recommendedSuccessRate?.rate || 0.5;
          const isFromSimilarProjects = !!historicalData.topPerformer;

          // Only switch if from similar projects (same ecosystem/technologies)
          // For cross-ecosystem recommendations, just mention as alternative
          const shouldSwitch =
            isFromSimilarProjects &&
            topPerformer.successRate > currentRate + 0.2 &&
            topPerformer.deploymentCount >= 2;

          const shouldMention =
            !shouldSwitch &&
            topPerformer.successRate >= 0.8 &&
            topPerformer.deploymentCount >= 2;

          if (shouldSwitch) {
            reasoning.unshift(
              `📊 Switching to ${topPerformer.ssg} based on ${(
                topPerformer.successRate * 100
              ).toFixed(0)}% success rate across ${
                topPerformer.deploymentCount
              } deployments`,
            );
            finalRecommendation = topPerformer.ssg as
              | "jekyll"
              | "hugo"
              | "docusaurus"
              | "mkdocs"
              | "eleventy";
            confidence = Math.min(0.95, topPerformer.successRate + 0.1);
          } else if (shouldMention) {
            // Mention as alternative if it has good success rate
            const projectScope = isFromSimilarProjects
              ? "similar projects"
              : "all projects";
            reasoning.push(
              `💡 Alternative: ${topPerformer.ssg} has ${(
                topPerformer.successRate * 100
              ).toFixed(0)}% success rate in ${projectScope}`,
            );
          }
        }

        // Add general historical context
        if (historicalData.similarProjectCount >= 2) {
          const totalDeployments = Object.values(
            historicalData.successRates,
          ).reduce((sum, data) => sum + data.sampleSize, 0);
          reasoning.push(
            `📚 Based on ${totalDeployments} deployment(s) across ${historicalData.similarProjectCount} similar project(s)`,
          );
        }
      }
    } else {
      // Fallback logic when no analysis data is available
      const baseRecommendation = prioritizeSimplicity ? "jekyll" : "docusaurus";
      finalRecommendation =
        ecosystemPreference === "python" ? "mkdocs" : baseRecommendation;
      reasoning = [
        "Recommendation based on preferences without full analysis",
        "Consider running analyze_repository for more accurate recommendation",
      ];
      confidence = 0.65; // Lower confidence without analysis data
    }

    // Phase 2.2: Apply user preferences to recommendation
    // For preference checking, include all SSGs except the current recommendation
    // This ensures user preferences can override even if their preferred SSG isn't in top alternatives
    const allSSGs: Array<
      "jekyll" | "hugo" | "docusaurus" | "mkdocs" | "eleventy"
    > = ["jekyll", "hugo", "docusaurus", "mkdocs", "eleventy"];
    const alternativeNames = allSSGs.filter(
      (ssg) => ssg !== finalRecommendation,
    );

    const preferenceAdjustment =
      userPreferenceManager.applyPreferencesToRecommendation(
        finalRecommendation,
        alternativeNames,
      );

    if (preferenceAdjustment.adjustmentReason) {
      // User preferences led to a different recommendation
      finalRecommendation = preferenceAdjustment.recommended as
        | "jekyll"
        | "hugo"
        | "docusaurus"
        | "mkdocs"
        | "eleventy";
      reasoning.unshift(`🎯 ${preferenceAdjustment.adjustmentReason}`);
      confidence = Math.min(0.95, confidence + 0.05);
    }

    const recommendation: SSGRecommendation = {
      recommended: finalRecommendation,
      confidence,
      reasoning,
      alternatives: getAlternatives(finalRecommendation, prioritizeSimplicity),
      historicalData,
    };

    if (context?.meta?.progressToken) {
      await context.meta.reportProgress?.({
        progress: 100,
        total: 100,
      });
    }

    const executionTime = Date.now() - startTime;
    await context?.info?.(
      `✅ Recommendation complete! Suggesting ${recommendation.recommended.toUpperCase()} with ${(
        recommendation.confidence * 100
      ).toFixed(0)}% confidence (${Math.round(executionTime / 1000)}s)`,
    );

    const response: MCPToolResponse<SSGRecommendation> = {
      success: true,
      data: recommendation,
      metadata: {
        toolVersion: "1.0.0",
        executionTime,
        timestamp: new Date().toISOString(),
        analysisId,
      },
      recommendations: [
        {
          type: "info",
          title: "SSG Recommendation",
          description: `${recommendation.recommended} recommended with ${(
            recommendation.confidence * 100
          ).toFixed(0)}% confidence`,
        },
      ],
      nextSteps: [
        {
          action: "Generate Configuration",
          toolRequired: "generate_config",
          description: `Create ${recommendation.recommended} configuration files`,
          priority: "high",
        },
      ],
    };

    return formatMCPResponse(response);
  } catch (error) {
    const errorResponse: MCPToolResponse = {
      success: false,
      error: {
        code: "RECOMMENDATION_FAILED",
        message: `Failed to generate SSG recommendation: ${error}`,
        resolution:
          "Ensure analysis ID is valid and preferences are correctly formatted",
      },
      metadata: {
        toolVersion: "1.0.0",
        executionTime: Date.now() - startTime,
        timestamp: new Date().toISOString(),
        analysisId,
      },
    };
    return formatMCPResponse(errorResponse);
  }
}

function getAlternatives(
  recommended: string,
  prioritizeSimplicity: boolean,
): SSGRecommendation["alternatives"] {
  const allSSGs = [
    {
      name: "Jekyll",
      score: prioritizeSimplicity ? 0.85 : 0.7,
      pros: [
        "Simple setup",
        "GitHub Pages native",
        "Extensive themes",
        "Ruby ecosystem",
      ],
      cons: [
        "Ruby dependency",
        "Slower builds for large sites",
        "Limited dynamic features",
      ],
    },
    {
      name: "Hugo",
      score: prioritizeSimplicity ? 0.65 : 0.75,
      pros: [
        "Extremely fast builds",
        "No dependencies",
        "Go templating",
        "Great for large sites",
      ],
      cons: [
        "Steeper learning curve",
        "Go templating may be unfamiliar",
        "Less flexible themes",
      ],
    },
    {
      name: "Docusaurus",
      score: prioritizeSimplicity ? 0.7 : 0.9,
      pros: [
        "React-based",
        "Rich features",
        "MDX support",
        "Built-in versioning",
      ],
      cons: [
        "More complex setup",
        "Node.js dependency",
        "Heavier than static generators",
      ],
    },
    {
      name: "MkDocs",
      score: prioritizeSimplicity ? 0.8 : 0.75,
      pros: [
        "Simple setup",
        "Python-based",
        "Great themes",
        "Easy configuration",
      ],
      cons: [
        "Python dependency",
        "Less flexible than React-based",
        "Limited customization",
      ],
    },
    {
      name: "Eleventy",
      score: prioritizeSimplicity ? 0.75 : 0.7,
      pros: [
        "Minimal config",
        "Fast builds",
        "Flexible templates",
        "JavaScript ecosystem",
      ],
      cons: [
        "Less opinionated",
        "Fewer built-in features",
        "Requires more setup for complex sites",
      ],
    },
  ];

  // Filter out the recommended SSG and sort by score
  return allSSGs
    .filter((ssg) => ssg.name.toLowerCase() !== recommended.toLowerCase())
    .sort((a, b) => b.score - a.score)
    .slice(0, 2); // Return top 2 alternatives
}

```

--------------------------------------------------------------------------------
/tests/functional/tools.test.ts:
--------------------------------------------------------------------------------

```typescript
// Functional tests for all MCP tools with real repository scenarios
import { promises as fs } from "fs";
import path from "path";
import os from "os";
import { analyzeRepository } from "../../src/tools/analyze-repository";
import { recommendSSG } from "../../src/tools/recommend-ssg";
import { generateConfig } from "../../src/tools/generate-config";
import { setupStructure } from "../../src/tools/setup-structure";
import { deployPages } from "../../src/tools/deploy-pages";
import { verifyDeployment } from "../../src/tools/verify-deployment";

describe("Functional Testing - MCP Tools", () => {
  let tempDir: string;
  let testRepos: {
    javascript: string;
    python: string;
    ruby: string;
    go: string;
    mixed: string;
    large: string;
    empty: string;
  };

  beforeAll(async () => {
    tempDir = path.join(os.tmpdir(), "documcp-functional-tests");
    await fs.mkdir(tempDir, { recursive: true });

    testRepos = {
      javascript: await createJavaScriptRepo(),
      python: await createPythonRepo(),
      ruby: await createRubyRepo(),
      go: await createGoRepo(),
      mixed: await createMixedLanguageRepo(),
      large: await createLargeRepo(),
      empty: await createEmptyRepo(),
    };
  });

  afterAll(async () => {
    try {
      await fs.rm(tempDir, { recursive: true, force: true });
    } catch (error) {
      console.warn("Failed to cleanup test directory:", error);
    }
  });

  describe("analyze_repository Tool", () => {
    it("should analyze JavaScript/TypeScript repository correctly", async () => {
      const result = await analyzeRepository({
        path: testRepos.javascript,
        depth: "standard",
      });

      expect(result.content).toBeDefined();
      expect(result.content.length).toBeGreaterThan(0);

      // Parse the JSON response to validate structure
      const analysisText = result.content.find((c) => c.text.includes('"id"'));
      expect(analysisText).toBeDefined();

      const analysis = JSON.parse(analysisText!.text);
      expect(analysis.dependencies.ecosystem).toBe("javascript");
      expect(analysis.structure.languages[".js"]).toBeGreaterThan(0);
      expect(analysis.documentation.hasReadme).toBe(true);
      expect(analysis.recommendations.primaryLanguage).toBe("javascript");
    });

    it("should analyze Python repository correctly", async () => {
      const result = await analyzeRepository({
        path: testRepos.python,
        depth: "standard",
      });

      const analysisText = result.content.find((c) =>
        c.text.includes('"ecosystem"'),
      );
      const analysis = JSON.parse(analysisText!.text);

      expect(analysis.dependencies.ecosystem).toBe("python");
      expect(analysis.structure.languages[".py"]).toBeGreaterThan(0);
      expect(analysis.dependencies.packages.length).toBeGreaterThan(0);
    });

    it("should analyze Ruby repository correctly", async () => {
      const result = await analyzeRepository({
        path: testRepos.ruby,
        depth: "standard",
      });

      const analysisText = result.content.find((c) =>
        c.text.includes('"ecosystem"'),
      );
      const analysis = JSON.parse(analysisText!.text);

      expect(analysis.dependencies.ecosystem).toBe("ruby");
      expect(analysis.structure.languages[".rb"]).toBeGreaterThan(0);
    });

    it("should analyze Go repository correctly", async () => {
      const result = await analyzeRepository({
        path: testRepos.go,
        depth: "standard",
      });

      const analysisText = result.content.find((c) =>
        c.text.includes('"ecosystem"'),
      );
      const analysis = JSON.parse(analysisText!.text);

      expect(analysis.dependencies.ecosystem).toBe("go");
      expect(analysis.structure.languages[".go"]).toBeGreaterThan(0);
    });

    it("should handle different analysis depths", async () => {
      const quickResult = await analyzeRepository({
        path: testRepos.javascript,
        depth: "quick",
      });

      const deepResult = await analyzeRepository({
        path: testRepos.javascript,
        depth: "deep",
      });

      expect(quickResult.content).toBeDefined();
      expect(deepResult.content).toBeDefined();

      // Both should return valid results but potentially different detail levels
      const quickAnalysis = JSON.parse(
        quickResult.content.find((c) => c.text.includes('"id"'))!.text,
      );
      const deepAnalysis = JSON.parse(
        deepResult.content.find((c) => c.text.includes('"id"'))!.text,
      );

      expect(quickAnalysis.id).toBeDefined();
      expect(deepAnalysis.id).toBeDefined();
    });

    it("should handle empty repository gracefully", async () => {
      const result = await analyzeRepository({
        path: testRepos.empty,
        depth: "standard",
      });

      const analysisText = result.content.find((c) =>
        c.text.includes('"totalFiles"'),
      );
      const analysis = JSON.parse(analysisText!.text);

      expect(analysis.structure.totalFiles).toBe(1); // Only README.md
      expect(analysis.dependencies.ecosystem).toBe("unknown");
    });

    it("should handle non-existent repository path", async () => {
      const nonExistentPath = path.join(tempDir, "does-not-exist");

      const result = await analyzeRepository({
        path: nonExistentPath,
        depth: "standard",
      });

      expect((result as any).isError).toBe(true);
      expect(result.content[0].text).toContain("Error:");
    });
  });

  describe("recommend_ssg Tool", () => {
    it("should recommend SSG based on analysis", async () => {
      const result = await recommendSSG({
        analysisId: "test-analysis-123",
      });

      expect(result.content).toBeDefined();
      expect(result.content.length).toBeGreaterThan(0);

      // Should contain recommendation data
      const recommendationText = result.content.find((c) =>
        c.text.includes('"recommended"'),
      );
      expect(recommendationText).toBeDefined();

      const recommendation = JSON.parse(recommendationText!.text);
      expect(recommendation.recommended).toBeDefined();
      expect(recommendation.confidence).toBeGreaterThan(0);
      expect(recommendation.reasoning).toBeDefined();
      expect(recommendation.alternatives).toBeDefined();
    });

    it("should handle preferences parameter", async () => {
      const result = await recommendSSG({
        analysisId: "test-analysis-456",
        preferences: {
          priority: "simplicity",
          ecosystem: "javascript",
        },
      });

      expect(result.content).toBeDefined();
      const recommendationText = result.content.find((c) =>
        c.text.includes('"recommended"'),
      );
      const recommendation = JSON.parse(recommendationText!.text);

      expect(["jekyll", "hugo", "docusaurus", "mkdocs", "eleventy"]).toContain(
        recommendation.recommended,
      );
    });
  });

  describe("generate_config Tool", () => {
    let configOutputDir: string;

    beforeEach(async () => {
      configOutputDir = path.join(
        tempDir,
        "config-output",
        Date.now().toString(),
      );
      await fs.mkdir(configOutputDir, { recursive: true });
    });

    it("should generate Docusaurus configuration", async () => {
      const result = await generateConfig({
        ssg: "docusaurus",
        projectName: "Test Docusaurus Project",
        projectDescription: "A test project for Docusaurus",
        outputPath: configOutputDir,
      });

      expect(result.content).toBeDefined();

      // Verify files were created
      const docusaurusConfig = path.join(
        configOutputDir,
        "docusaurus.config.js",
      );
      const packageJson = path.join(configOutputDir, "package.json");

      expect(
        await fs
          .access(docusaurusConfig)
          .then(() => true)
          .catch(() => false),
      ).toBe(true);
      expect(
        await fs
          .access(packageJson)
          .then(() => true)
          .catch(() => false),
      ).toBe(true);

      // Verify file contents
      const configContent = await fs.readFile(docusaurusConfig, "utf-8");
      expect(configContent).toContain("Test Docusaurus Project");
      expect(configContent).toContain("classic");
    });

    it("should generate MkDocs configuration", async () => {
      const result = await generateConfig({
        ssg: "mkdocs",
        projectName: "Test MkDocs Project",
        outputPath: configOutputDir,
      });

      expect(result.content).toBeDefined();

      const mkdocsConfig = path.join(configOutputDir, "mkdocs.yml");
      const requirements = path.join(configOutputDir, "requirements.txt");

      expect(
        await fs
          .access(mkdocsConfig)
          .then(() => true)
          .catch(() => false),
      ).toBe(true);
      expect(
        await fs
          .access(requirements)
          .then(() => true)
          .catch(() => false),
      ).toBe(true);

      const configContent = await fs.readFile(mkdocsConfig, "utf-8");
      expect(configContent).toContain("Test MkDocs Project");
      expect(configContent).toContain("material");
    });

    it("should generate Hugo configuration", async () => {
      const result = await generateConfig({
        ssg: "hugo",
        projectName: "Test Hugo Project",
        outputPath: configOutputDir,
      });

      const hugoConfig = path.join(configOutputDir, "hugo.toml");
      expect(
        await fs
          .access(hugoConfig)
          .then(() => true)
          .catch(() => false),
      ).toBe(true);

      const configContent = await fs.readFile(hugoConfig, "utf-8");
      expect(configContent).toContain("Test Hugo Project");
    });

    it("should generate Jekyll configuration", async () => {
      const result = await generateConfig({
        ssg: "jekyll",
        projectName: "Test Jekyll Project",
        outputPath: configOutputDir,
      });

      const jekyllConfig = path.join(configOutputDir, "_config.yml");
      const gemfile = path.join(configOutputDir, "Gemfile");

      expect(
        await fs
          .access(jekyllConfig)
          .then(() => true)
          .catch(() => false),
      ).toBe(true);
      expect(
        await fs
          .access(gemfile)
          .then(() => true)
          .catch(() => false),
      ).toBe(true);
    });

    it("should generate Eleventy configuration", async () => {
      const result = await generateConfig({
        ssg: "eleventy",
        projectName: "Test Eleventy Project",
        outputPath: configOutputDir,
      });

      const eleventyConfig = path.join(configOutputDir, ".eleventy.js");
      const packageJson = path.join(configOutputDir, "package.json");

      expect(
        await fs
          .access(eleventyConfig)
          .then(() => true)
          .catch(() => false),
      ).toBe(true);
      expect(
        await fs
          .access(packageJson)
          .then(() => true)
          .catch(() => false),
      ).toBe(true);
    });
  });

  describe("setup_structure Tool", () => {
    let structureOutputDir: string;

    beforeEach(async () => {
      structureOutputDir = path.join(
        tempDir,
        "structure-output",
        Date.now().toString(),
      );
    });

    it("should create Diataxis structure with examples", async () => {
      const result = await setupStructure({
        path: structureOutputDir,
        ssg: "docusaurus",
        includeExamples: true,
      });

      expect(result.content).toBeDefined();

      // Verify directory structure
      const categories = ["tutorials", "how-to", "reference", "explanation"];
      for (const category of categories) {
        const categoryDir = path.join(structureOutputDir, category);
        expect(
          await fs
            .access(categoryDir)
            .then(() => true)
            .catch(() => false),
        ).toBe(true);

        // Check for index.md
        const indexFile = path.join(categoryDir, "index.md");
        expect(
          await fs
            .access(indexFile)
            .then(() => true)
            .catch(() => false),
        ).toBe(true);

        // Check for example file
        const files = await fs.readdir(categoryDir);
        expect(files.length).toBeGreaterThan(1); // index.md + example file
      }

      // Check root index
      const rootIndex = path.join(structureOutputDir, "index.md");
      expect(
        await fs
          .access(rootIndex)
          .then(() => true)
          .catch(() => false),
      ).toBe(true);

      const rootContent = await fs.readFile(rootIndex, "utf-8");
      expect(rootContent).toContain("Diataxis");
      expect(rootContent).toContain("Tutorials");
      expect(rootContent).toContain("How-To Guides");
    });

    it("should create structure without examples", async () => {
      const result = await setupStructure({
        path: structureOutputDir,
        ssg: "mkdocs",
        includeExamples: false,
      });

      expect(result.content).toBeDefined();

      // Verify only index files exist (no examples)
      const tutorialsDir = path.join(structureOutputDir, "tutorials");
      const files = await fs.readdir(tutorialsDir);
      expect(files).toEqual(["index.md"]); // Only index, no example
    });

    it("should handle different SSG formats correctly", async () => {
      // Test Docusaurus format
      await setupStructure({
        path: path.join(structureOutputDir, "docusaurus"),
        ssg: "docusaurus",
        includeExamples: true,
      });

      const docusaurusFile = path.join(
        structureOutputDir,
        "docusaurus",
        "tutorials",
        "index.md",
      );
      const docusaurusContent = await fs.readFile(docusaurusFile, "utf-8");
      expect(docusaurusContent).toContain("id: tutorials-index");
      expect(docusaurusContent).toContain("sidebar_label:");

      // Test Jekyll format
      await setupStructure({
        path: path.join(structureOutputDir, "jekyll"),
        ssg: "jekyll",
        includeExamples: true,
      });

      const jekyllFile = path.join(
        structureOutputDir,
        "jekyll",
        "tutorials",
        "index.md",
      );
      const jekyllContent = await fs.readFile(jekyllFile, "utf-8");
      expect(jekyllContent).toContain("title:");
      expect(jekyllContent).toContain("description:");
    });
  });

  describe("deploy_pages Tool", () => {
    let deploymentRepoDir: string;

    beforeEach(async () => {
      deploymentRepoDir = path.join(
        tempDir,
        "deployment-repo",
        Date.now().toString(),
      );
      await fs.mkdir(deploymentRepoDir, { recursive: true });
    });

    it("should create GitHub Actions workflow for Docusaurus", async () => {
      const result = await deployPages({
        repository: deploymentRepoDir,
        ssg: "docusaurus",
        branch: "gh-pages",
      });

      expect(result.content).toBeDefined();

      const workflowPath = path.join(
        deploymentRepoDir,
        ".github",
        "workflows",
        "deploy-docs.yml",
      );
      expect(
        await fs
          .access(workflowPath)
          .then(() => true)
          .catch(() => false),
      ).toBe(true);

      const workflowContent = await fs.readFile(workflowPath, "utf-8");
      expect(workflowContent).toContain("Deploy Docusaurus");
      expect(workflowContent).toContain("npm run build");
      expect(workflowContent).toContain("actions/upload-pages-artifact");
      expect(workflowContent).toContain("actions/deploy-pages");

      // Verify security compliance (OIDC tokens)
      expect(workflowContent).toContain("id-token: write");
      expect(workflowContent).toContain("pages: write");
      expect(workflowContent).not.toContain("GITHUB_TOKEN: ${{ secrets.");
    });

    it("should create workflow for MkDocs", async () => {
      const result = await deployPages({
        repository: deploymentRepoDir,
        ssg: "mkdocs",
      });

      const workflowPath = path.join(
        deploymentRepoDir,
        ".github",
        "workflows",
        "deploy-docs.yml",
      );
      const workflowContent = await fs.readFile(workflowPath, "utf-8");

      expect(workflowContent).toContain("Deploy MkDocs");
      expect(workflowContent).toContain("mkdocs gh-deploy");
      expect(workflowContent).toContain("python");
    });

    it("should create workflow for Hugo", async () => {
      const result = await deployPages({
        repository: deploymentRepoDir,
        ssg: "hugo",
      });

      const workflowContent = await fs.readFile(
        path.join(deploymentRepoDir, ".github", "workflows", "deploy-docs.yml"),
        "utf-8",
      );

      expect(workflowContent).toContain("Deploy Hugo");
      expect(workflowContent).toContain("peaceiris/actions-hugo");
      expect(workflowContent).toContain("hugo --minify");
    });

    it("should handle custom domain configuration", async () => {
      const result = await deployPages({
        repository: deploymentRepoDir,
        ssg: "jekyll",
        customDomain: "docs.example.com",
      });

      // Check CNAME file creation
      const cnamePath = path.join(deploymentRepoDir, "CNAME");
      expect(
        await fs
          .access(cnamePath)
          .then(() => true)
          .catch(() => false),
      ).toBe(true);

      const cnameContent = await fs.readFile(cnamePath, "utf-8");
      expect(cnameContent.trim()).toBe("docs.example.com");

      // Verify result indicates custom domain was configured
      const resultText = result.content.map((c) => c.text).join(" ");
      expect(resultText).toContain("docs.example.com");
    });
  });

  describe("verify_deployment Tool", () => {
    let verificationRepoDir: string;

    beforeEach(async () => {
      verificationRepoDir = path.join(
        tempDir,
        "verification-repo",
        Date.now().toString(),
      );
      await fs.mkdir(verificationRepoDir, { recursive: true });
    });

    it("should verify complete deployment setup", async () => {
      // Set up a complete deployment scenario
      await fs.mkdir(path.join(verificationRepoDir, ".github", "workflows"), {
        recursive: true,
      });
      await fs.mkdir(path.join(verificationRepoDir, "docs"), {
        recursive: true,
      });
      await fs.mkdir(path.join(verificationRepoDir, "build"), {
        recursive: true,
      });

      // Create workflow file
      await fs.writeFile(
        path.join(
          verificationRepoDir,
          ".github",
          "workflows",
          "deploy-docs.yml",
        ),
        "name: Deploy Docs\non: push\njobs:\n  deploy:\n    runs-on: ubuntu-latest",
      );

      // Create documentation files
      await fs.writeFile(
        path.join(verificationRepoDir, "docs", "index.md"),
        "# Documentation",
      );
      await fs.writeFile(
        path.join(verificationRepoDir, "docs", "guide.md"),
        "# Guide",
      );

      // Create config file
      await fs.writeFile(
        path.join(verificationRepoDir, "docusaurus.config.js"),
        'module.exports = { title: "Test" };',
      );

      // Create build directory
      await fs.writeFile(
        path.join(verificationRepoDir, "build", "index.html"),
        "<h1>Built Site</h1>",
      );

      const result = await verifyDeployment({
        repository: verificationRepoDir,
        url: "https://example.github.io/test-repo",
      });

      expect(result.content).toBeDefined();

      // Parse the verification result
      const verification = JSON.parse(result.content[0].text);
      expect(verification.summary.passed).toBeGreaterThan(0); // Should have passing checks
      expect(
        verification.checks.some((check: any) =>
          check.message.includes("deployment workflow"),
        ),
      ).toBe(true);
      expect(
        verification.checks.some((check: any) =>
          check.message.includes("documentation files"),
        ),
      ).toBe(true);
      expect(
        verification.checks.some((check: any) =>
          check.message.includes("configuration"),
        ),
      ).toBe(true);
      expect(
        verification.checks.some((check: any) =>
          check.message.includes("build output"),
        ),
      ).toBe(true);
    });

    it("should identify missing components", async () => {
      // Create minimal repo without deployment setup
      await fs.writeFile(
        path.join(verificationRepoDir, "README.md"),
        "# Test Repo",
      );

      const result = await verifyDeployment({
        repository: verificationRepoDir,
      });

      const verification = JSON.parse(result.content[0].text);
      expect(verification.summary.failed).toBeGreaterThan(0); // Should have failing checks
      expect(
        verification.checks.some((check: any) =>
          check.message.includes("No .github/workflows"),
        ),
      ).toBe(true);
      expect(
        verification.checks.some((check: any) =>
          check.message.includes("No documentation files"),
        ),
      ).toBe(true);
      expect(
        verification.checks.some((check: any) =>
          check.message.includes("No static site generator configuration"),
        ),
      ).toBe(true);
    });

    it("should provide actionable recommendations", async () => {
      const result = await verifyDeployment({
        repository: verificationRepoDir,
      });

      const resultText = result.content.map((c) => c.text).join("\n");
      expect(resultText).toContain("→"); // Should contain recommendation arrows
      expect(resultText).toContain("deploy_pages tool");
      expect(resultText).toContain("setup_structure tool");
      expect(resultText).toContain("generate_config tool");
    });

    it("should handle repository path variations", async () => {
      // Test with relative path
      const relativeResult = await verifyDeployment({
        repository: ".",
      });
      expect(relativeResult.content).toBeDefined();

      // Test with absolute path
      const absoluteResult = await verifyDeployment({
        repository: verificationRepoDir,
      });
      expect(absoluteResult.content).toBeDefined();

      // Test with HTTP URL (should default to current directory)
      const urlResult = await verifyDeployment({
        repository: "https://github.com/user/repo",
      });
      expect(urlResult.content).toBeDefined();
    });
  });

  // Helper functions to create test repositories
  async function createJavaScriptRepo(): Promise<string> {
    const repoPath = path.join(tempDir, "javascript-repo");
    await fs.mkdir(repoPath, { recursive: true });

    // package.json
    const packageJson = {
      name: "test-js-project",
      version: "1.0.0",
      description: "Test JavaScript project",
      scripts: {
        start: "node index.js",
        test: "jest",
      },
      dependencies: {
        express: "^4.18.0",
        lodash: "^4.17.21",
      },
      devDependencies: {
        jest: "^29.0.0",
        "@types/node": "^20.0.0",
      },
    };
    await fs.writeFile(
      path.join(repoPath, "package.json"),
      JSON.stringify(packageJson, null, 2),
    );

    // Source files
    await fs.writeFile(
      path.join(repoPath, "index.js"),
      'console.log("Hello World");',
    );
    await fs.writeFile(
      path.join(repoPath, "utils.js"),
      "module.exports = { helper: () => {} };",
    );
    await fs.writeFile(
      path.join(repoPath, "app.ts"),
      'const app: string = "TypeScript";',
    );

    // Test directory
    await fs.mkdir(path.join(repoPath, "test"), { recursive: true });
    await fs.writeFile(
      path.join(repoPath, "test", "app.test.js"),
      'test("example", () => {});',
    );

    // Documentation
    await fs.writeFile(
      path.join(repoPath, "README.md"),
      "# JavaScript Test Project\nA test project for JavaScript analysis.",
    );
    await fs.writeFile(
      path.join(repoPath, "CONTRIBUTING.md"),
      "# Contributing\nHow to contribute.",
    );
    await fs.writeFile(path.join(repoPath, "LICENSE"), "MIT License");

    // CI/CD
    await fs.mkdir(path.join(repoPath, ".github", "workflows"), {
      recursive: true,
    });
    await fs.writeFile(
      path.join(repoPath, ".github", "workflows", "ci.yml"),
      "name: CI\non: push\njobs:\n  test:\n    runs-on: ubuntu-latest",
    );

    return repoPath;
  }

  async function createPythonRepo(): Promise<string> {
    const repoPath = path.join(tempDir, "python-repo");
    await fs.mkdir(repoPath, { recursive: true });

    // requirements.txt
    await fs.writeFile(
      path.join(repoPath, "requirements.txt"),
      "flask>=2.0.0\nrequests>=2.25.0\nnumpy>=1.21.0",
    );

    // Python files
    await fs.writeFile(
      path.join(repoPath, "main.py"),
      "import flask\napp = flask.Flask(__name__)",
    );
    await fs.writeFile(
      path.join(repoPath, "utils.py"),
      "def helper():\n    pass",
    );

    // Tests
    await fs.mkdir(path.join(repoPath, "tests"), { recursive: true });
    await fs.writeFile(
      path.join(repoPath, "tests", "test_main.py"),
      "def test_app():\n    assert True",
    );

    await fs.writeFile(
      path.join(repoPath, "README.md"),
      "# Python Test Project",
    );

    return repoPath;
  }

  async function createRubyRepo(): Promise<string> {
    const repoPath = path.join(tempDir, "ruby-repo");
    await fs.mkdir(repoPath, { recursive: true });

    // Gemfile
    await fs.writeFile(
      path.join(repoPath, "Gemfile"),
      'source "https://rubygems.org"\ngem "rails"',
    );

    // Ruby files
    await fs.writeFile(path.join(repoPath, "app.rb"), "class App\nend");
    await fs.writeFile(path.join(repoPath, "helper.rb"), "module Helper\nend");

    await fs.writeFile(path.join(repoPath, "README.md"), "# Ruby Test Project");

    return repoPath;
  }

  async function createGoRepo(): Promise<string> {
    const repoPath = path.join(tempDir, "go-repo");
    await fs.mkdir(repoPath, { recursive: true });

    // go.mod
    await fs.writeFile(
      path.join(repoPath, "go.mod"),
      "module test-go-project\ngo 1.19",
    );

    // Go files
    await fs.writeFile(
      path.join(repoPath, "main.go"),
      "package main\nfunc main() {}",
    );
    await fs.writeFile(
      path.join(repoPath, "utils.go"),
      "package main\nfunc helper() {}",
    );

    await fs.writeFile(path.join(repoPath, "README.md"), "# Go Test Project");

    return repoPath;
  }

  async function createMixedLanguageRepo(): Promise<string> {
    const repoPath = path.join(tempDir, "mixed-repo");
    await fs.mkdir(repoPath, { recursive: true });

    // Multiple language files
    await fs.writeFile(
      path.join(repoPath, "package.json"),
      '{"name": "mixed-project"}',
    );
    await fs.writeFile(path.join(repoPath, "requirements.txt"), "flask>=2.0.0");
    await fs.writeFile(path.join(repoPath, "Gemfile"), 'gem "rails"');

    await fs.writeFile(path.join(repoPath, "app.js"), 'console.log("JS");');
    await fs.writeFile(path.join(repoPath, "script.py"), 'print("Python")');
    await fs.writeFile(path.join(repoPath, "server.rb"), 'puts "Ruby"');

    await fs.writeFile(
      path.join(repoPath, "README.md"),
      "# Mixed Language Project",
    );

    return repoPath;
  }

  async function createLargeRepo(): Promise<string> {
    const repoPath = path.join(tempDir, "large-repo");
    await fs.mkdir(repoPath, { recursive: true });

    // Create many files to simulate a large repository
    for (let i = 0; i < 150; i++) {
      const fileName = `file-${i.toString().padStart(3, "0")}.js`;
      await fs.writeFile(
        path.join(repoPath, fileName),
        `// File ${i}\nconsole.log(${i});`,
      );
    }

    // Create nested directories
    for (let i = 0; i < 10; i++) {
      const dirPath = path.join(repoPath, `dir-${i}`);
      await fs.mkdir(dirPath, { recursive: true });

      for (let j = 0; j < 20; j++) {
        const fileName = `nested-${j}.js`;
        await fs.writeFile(
          path.join(dirPath, fileName),
          `// Nested file ${i}-${j}`,
        );
      }
    }

    await fs.writeFile(
      path.join(repoPath, "package.json"),
      '{"name": "large-project"}',
    );
    await fs.writeFile(
      path.join(repoPath, "README.md"),
      "# Large Test Project",
    );

    return repoPath;
  }

  async function createEmptyRepo(): Promise<string> {
    const repoPath = path.join(tempDir, "empty-repo");
    await fs.mkdir(repoPath, { recursive: true });

    // Only a README file
    await fs.writeFile(
      path.join(repoPath, "README.md"),
      "# Empty Project\nMinimal repository for testing.",
    );

    return repoPath;
  }
});

```
Page 14/20FirstPrevNextLast