#
tokens: 45662/50000 10/274 files (page 9/20)
lines: off (toggle) GitHub
raw markdown copy
This is page 9 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

--------------------------------------------------------------------------------
/src/memory/enhanced-manager.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Enhanced Memory Manager with Learning and Knowledge Graph Integration
 * Combines Issues #47 and #48 for intelligent memory management
 */

import { MemoryManager } from "./manager.js";
import { MemoryEntry } from "./storage.js";
import IncrementalLearningSystem, {
  ProjectFeatures,
  LearningInsight,
} from "./learning.js";
import KnowledgeGraph, { RecommendationPath } from "./knowledge-graph.js";

export interface EnhancedRecommendation {
  baseRecommendation: any;
  learningEnhanced: any;
  graphBased: RecommendationPath[];
  insights: LearningInsight[];
  confidence: number;
  reasoning: string[];
  metadata: {
    usedLearning: boolean;
    usedKnowledgeGraph: boolean;
    patternsFound: number;
    similarProjects: number;
  };
}

export interface IntelligentAnalysis {
  analysis: any;
  patterns: string[];
  predictions: Array<{
    type: "success_rate" | "optimal_ssg" | "potential_issues";
    prediction: string;
    confidence: number;
  }>;
  recommendations: string[];
  learningData: {
    similarProjects: number;
    confidenceLevel: number;
    dataQuality: "low" | "medium" | "high";
  };
}

export class EnhancedMemoryManager extends MemoryManager {
  private learningSystem: IncrementalLearningSystem;
  private knowledgeGraph: KnowledgeGraph;
  private initialized: boolean = false;

  constructor(storageDir?: string) {
    super(storageDir);
    this.learningSystem = new IncrementalLearningSystem(this);
    this.knowledgeGraph = new KnowledgeGraph(this);
  }

  async initialize(): Promise<void> {
    await super.initialize();

    if (!this.initialized) {
      await this.learningSystem.initialize();
      await this.knowledgeGraph.initialize();
      this.initialized = true;

      // Set up automatic learning from new memories
      this.on("memory-created", this.handleNewMemory.bind(this));
    }
  }

  /**
   * Enhanced recommendation that combines base analysis with learning and graph intelligence
   */
  async getEnhancedRecommendation(
    projectPath: string,
    baseRecommendation: any,
    projectFeatures: ProjectFeatures,
  ): Promise<EnhancedRecommendation> {
    await this.initialize();

    // Get learning-enhanced recommendation
    const learningResult = await this.learningSystem.getImprovedRecommendation(
      projectFeatures,
      baseRecommendation,
    );

    // Get knowledge graph-based recommendations
    const candidateSSGs = this.extractCandidateSSGs(baseRecommendation);
    const graphRecommendations =
      await this.knowledgeGraph.getGraphBasedRecommendation(
        projectFeatures,
        candidateSSGs,
      );

    // Combine insights and reasoning
    const allInsights = [...learningResult.insights];
    const reasoning: string[] = [];

    // Add graph-based reasoning
    if (graphRecommendations.length > 0) {
      const topRecommendation = graphRecommendations[0];
      reasoning.push(...topRecommendation.reasoning);

      allInsights.push({
        type: "recommendation",
        message: `Knowledge graph analysis suggests ${topRecommendation.to.label} based on similar successful projects`,
        confidence: topRecommendation.confidence,
        actionable: true,
        data: { graphPath: topRecommendation.path },
      });
    }

    // Calculate combined confidence
    const combinedConfidence = this.calculateCombinedConfidence(
      baseRecommendation.confidence,
      learningResult.confidence,
      graphRecommendations[0]?.confidence || 0,
    );

    // Determine final recommendation
    const finalRecommendation = learningResult.recommendation;
    if (
      graphRecommendations.length > 0 &&
      graphRecommendations[0].confidence > 0.8
    ) {
      const graphChoice = graphRecommendations[0].to.label;
      if (graphChoice !== finalRecommendation.recommended) {
        finalRecommendation.graphSuggestion = graphChoice;
        finalRecommendation.conflictDetected = true;
        reasoning.push(
          `Knowledge graph suggests ${graphChoice} while learning system suggests ${finalRecommendation.recommended}`,
        );
      }
    }

    return {
      baseRecommendation,
      learningEnhanced: learningResult.recommendation,
      graphBased: graphRecommendations,
      insights: allInsights,
      confidence: combinedConfidence,
      reasoning,
      metadata: {
        usedLearning: learningResult.insights.length > 0,
        usedKnowledgeGraph: graphRecommendations.length > 0,
        patternsFound: await this.countRelevantPatterns(projectFeatures),
        similarProjects: graphRecommendations.length,
      },
    };
  }

  /**
   * Enhanced analysis that provides intelligent insights
   */
  async getIntelligentAnalysis(
    projectPath: string,
    baseAnalysis: any,
  ): Promise<IntelligentAnalysis> {
    await this.initialize();

    const projectFeatures = this.extractProjectFeatures(baseAnalysis);

    // Find patterns from similar projects
    const patterns = await this.findProjectPatterns(projectFeatures);

    // Generate predictions
    const predictions = await this.generatePredictions(
      projectFeatures,
      patterns,
    );

    // Generate recommendations
    const recommendations = await this.generateIntelligentRecommendations(
      projectFeatures,
      patterns,
      predictions,
    );

    // Assess learning data quality
    const learningData = await this.assessLearningData(projectFeatures);

    return {
      analysis: baseAnalysis,
      patterns: patterns.map((p) => p.description),
      predictions,
      recommendations,
      learningData,
    };
  }

  /**
   * Learn from new memory entries automatically
   */
  private async handleNewMemory(memory: MemoryEntry): Promise<void> {
    try {
      // Determine outcome for learning
      const outcome = this.inferOutcome(memory);

      if (outcome) {
        await this.learningSystem.learn(memory, outcome);
      }

      // Update knowledge graph
      await this.knowledgeGraph.buildFromMemories();

      // Periodically save graph to persistent storage
      if (Math.random() < 0.1) {
        // 10% chance to save
        await this.knowledgeGraph.saveToMemory();
      }
    } catch (error) {
      console.error("Error in automatic learning:", error);
    }
  }

  /**
   * Extract project features from analysis data
   */
  private extractProjectFeatures(analysis: any): ProjectFeatures {
    return {
      language: analysis.language?.primary || "unknown",
      framework: analysis.framework?.name,
      size: this.categorizeProjectSize(analysis.stats?.files || 0),
      complexity: this.categorizeProjectComplexity(analysis),
      hasTests: Boolean(analysis.testing?.hasTests),
      hasCI: Boolean(analysis.ci?.hasCI),
      hasDocs: Boolean(analysis.documentation?.exists),
      teamSize: analysis.team?.size,
      isOpenSource: Boolean(analysis.repository?.isPublic),
    };
  }

  private categorizeProjectSize(
    fileCount: number,
  ): "small" | "medium" | "large" {
    if (fileCount < 50) return "small";
    if (fileCount < 200) return "medium";
    return "large";
  }

  private categorizeProjectComplexity(
    analysis: any,
  ): "simple" | "moderate" | "complex" {
    let complexity = 0;

    if (analysis.dependencies?.count > 20) complexity++;
    if (analysis.framework?.name) complexity++;
    if (analysis.testing?.frameworks?.length > 1) complexity++;
    if (analysis.ci?.workflows?.length > 2) complexity++;
    if (analysis.architecture?.patterns?.length > 3) complexity++;

    if (complexity <= 1) return "simple";
    if (complexity <= 3) return "moderate";
    return "complex";
  }

  /**
   * Extract candidate SSGs from base recommendation
   */
  private extractCandidateSSGs(baseRecommendation: any): string[] {
    const candidates = [baseRecommendation.recommended];

    if (baseRecommendation.alternatives) {
      candidates.push(
        ...baseRecommendation.alternatives.map((alt: any) => alt.name || alt),
      );
    }

    return [...new Set(candidates)].filter(Boolean);
  }

  /**
   * Calculate combined confidence from multiple sources
   */
  private calculateCombinedConfidence(
    baseConfidence: number,
    learningConfidence: number,
    graphConfidence: number,
  ): number {
    // Weighted average with emphasis on learning and graph data when available
    const weights = {
      base: 0.4,
      learning: learningConfidence > 0 ? 0.4 : 0,
      graph: graphConfidence > 0 ? 0.2 : 0,
    };

    // Redistribute weights if some sources are unavailable
    const totalWeight = weights.base + weights.learning + weights.graph;
    const normalizedWeights = {
      base: weights.base / totalWeight,
      learning: weights.learning / totalWeight,
      graph: weights.graph / totalWeight,
    };

    return (
      baseConfidence * normalizedWeights.base +
      learningConfidence * normalizedWeights.learning +
      graphConfidence * normalizedWeights.graph
    );
  }

  /**
   * Count patterns relevant to project features
   */
  private async countRelevantPatterns(
    features: ProjectFeatures,
  ): Promise<number> {
    const tags = [features.language, features.framework, features.size].filter(
      (tag): tag is string => Boolean(tag),
    );
    const memories = await this.search({
      tags,
    });

    return memories.length;
  }

  /**
   * Find patterns from similar projects
   */
  private async findProjectPatterns(features: ProjectFeatures): Promise<
    Array<{
      type: string;
      description: string;
      confidence: number;
      frequency: number;
    }>
  > {
    const patterns: Array<{
      type: string;
      description: string;
      confidence: number;
      frequency: number;
    }> = [];

    // Find similar projects based on features
    const similarMemories = await this.search({
      tags: [features.language],
    });

    if (similarMemories.length >= 3) {
      // Pattern: Most common SSG for this language
      const ssgCounts = new Map<string, number>();
      for (const memory of similarMemories) {
        if (memory.metadata.ssg) {
          ssgCounts.set(
            memory.metadata.ssg,
            (ssgCounts.get(memory.metadata.ssg) || 0) + 1,
          );
        }
      }

      if (ssgCounts.size > 0) {
        const topSSG = Array.from(ssgCounts.entries()).sort(
          ([, a], [, b]) => b - a,
        )[0];

        patterns.push({
          type: "ssg_preference",
          description: `${topSSG[0]} is commonly used with ${features.language} (${topSSG[1]}/${similarMemories.length} projects)`,
          confidence: topSSG[1] / similarMemories.length,
          frequency: topSSG[1],
        });
      }

      // Pattern: Success rate analysis
      const deploymentMemories = similarMemories.filter(
        (m) => m.type === "deployment",
      );
      if (deploymentMemories.length >= 2) {
        const successRate =
          deploymentMemories.filter((m) => m.data.status === "success").length /
          deploymentMemories.length;

        patterns.push({
          type: "success_rate",
          description: `Similar ${features.language} projects have ${(
            successRate * 100
          ).toFixed(0)}% deployment success rate`,
          confidence: Math.min(deploymentMemories.length / 10, 1.0),
          frequency: deploymentMemories.length,
        });
      }
    }

    return patterns;
  }

  /**
   * Generate predictions based on patterns and features
   */
  private async generatePredictions(
    features: ProjectFeatures,
    patterns: Array<{ type: string; description: string; confidence: number }>,
  ): Promise<
    Array<{
      type: "success_rate" | "optimal_ssg" | "potential_issues";
      prediction: string;
      confidence: number;
    }>
  > {
    const predictions: Array<{
      type: "success_rate" | "optimal_ssg" | "potential_issues";
      prediction: string;
      confidence: number;
    }> = [];

    // Predict success rate
    const successPattern = patterns.find((p) => p.type === "success_rate");
    if (successPattern) {
      predictions.push({
        type: "success_rate",
        prediction: `Expected deployment success rate: ${
          successPattern.description.match(/(\d+)%/)?.[1] || "unknown"
        }%`,
        confidence: successPattern.confidence,
      });
    }

    // Predict optimal SSG
    const ssgPattern = patterns.find((p) => p.type === "ssg_preference");
    if (ssgPattern) {
      const ssg = ssgPattern.description.split(" ")[0];
      predictions.push({
        type: "optimal_ssg",
        prediction: `${ssg} is likely the optimal choice based on similar projects`,
        confidence: ssgPattern.confidence,
      });
    }

    // Predict potential issues
    if (!features.hasTests && features.size !== "small") {
      predictions.push({
        type: "potential_issues",
        prediction:
          "Deployment issues likely due to lack of tests in medium/large project",
        confidence: 0.7,
      });
    }

    if (!features.hasCI && features.complexity === "complex") {
      predictions.push({
        type: "potential_issues",
        prediction:
          "Complex project without CI/CD may face integration challenges",
        confidence: 0.6,
      });
    }

    return predictions;
  }

  /**
   * Generate intelligent recommendations
   */
  private async generateIntelligentRecommendations(
    features: ProjectFeatures,
    patterns: Array<{ type: string; description: string; confidence: number }>,
    predictions: Array<{
      type: string;
      prediction: string;
      confidence: number;
    }>,
  ): Promise<string[]> {
    const recommendations: string[] = [];

    // Recommendations based on patterns
    const ssgPattern = patterns.find((p) => p.type === "ssg_preference");
    if (ssgPattern && ssgPattern.confidence > 0.7) {
      const ssg = ssgPattern.description.split(" ")[0];
      recommendations.push(
        `Consider ${ssg} - it's proven successful for similar ${features.language} projects`,
      );
    }

    // Recommendations based on predictions
    const issuesPrediction = predictions.find(
      (p) => p.type === "potential_issues",
    );
    if (issuesPrediction && issuesPrediction.confidence > 0.6) {
      if (issuesPrediction.prediction.includes("tests")) {
        recommendations.push(
          "Set up automated testing before deployment to improve success rate",
        );
      }
      if (issuesPrediction.prediction.includes("CI/CD")) {
        recommendations.push(
          "Implement CI/CD pipeline to handle project complexity",
        );
      }
    }

    // Recommendations based on features
    if (features.complexity === "complex" && !features.hasDocs) {
      recommendations.push(
        "Invest in comprehensive documentation for this complex project",
      );
    }

    if (features.isOpenSource && features.size === "large") {
      recommendations.push(
        "Consider community-friendly documentation tools for large open-source project",
      );
    }

    return recommendations;
  }

  /**
   * Assess quality of learning data
   */
  private async assessLearningData(features: ProjectFeatures): Promise<{
    similarProjects: number;
    confidenceLevel: number;
    dataQuality: "low" | "medium" | "high";
  }> {
    const tags = [features.language, features.framework].filter(
      (tag): tag is string => Boolean(tag),
    );
    const similarMemories = await this.search({
      tags,
    });

    const similarProjects = similarMemories.length;
    let confidenceLevel = 0;

    if (similarProjects >= 10) {
      confidenceLevel = 0.9;
    } else if (similarProjects >= 5) {
      confidenceLevel = 0.7;
    } else if (similarProjects >= 2) {
      confidenceLevel = 0.5;
    } else {
      confidenceLevel = 0.2;
    }

    let dataQuality: "low" | "medium" | "high";
    if (similarProjects >= 8 && confidenceLevel >= 0.8) {
      dataQuality = "high";
    } else if (similarProjects >= 3 && confidenceLevel >= 0.5) {
      dataQuality = "medium";
    } else {
      dataQuality = "low";
    }

    return {
      similarProjects,
      confidenceLevel,
      dataQuality,
    };
  }

  /**
   * Infer outcome from memory entry
   */
  private inferOutcome(
    memory: MemoryEntry,
  ): "success" | "failure" | "neutral" | null {
    if (memory.type === "deployment") {
      if (memory.data.status === "success") return "success";
      if (memory.data.status === "failed") return "failure";
    }

    if (memory.type === "recommendation" && memory.data.feedback) {
      const rating = memory.data.feedback.rating || memory.data.feedback.score;
      if (rating > 3) return "success";
      if (rating < 3) return "failure";
    }

    return "neutral";
  }

  /**
   * Get comprehensive learning statistics
   */
  async getLearningStatistics(): Promise<{
    learning: any;
    knowledgeGraph: any;
    combined: {
      totalMemories: number;
      enhancedRecommendations: number;
      accuracyImprovement: number;
      systemMaturity: "nascent" | "developing" | "mature";
    };
  }> {
    const learningStats = await this.learningSystem.getStatistics();
    const graphStats = this.knowledgeGraph.getStatistics();

    const totalMemories = (await this.search("")).length;
    const graphStatsResult = await graphStats;
    const enhancedRecommendations =
      learningStats.totalPatterns + graphStatsResult.nodeCount;

    // Estimate accuracy improvement based on data volume
    let accuracyImprovement = 0;
    if (totalMemories >= 50) {
      accuracyImprovement = Math.min(0.3, totalMemories / 200);
    }

    // Determine system maturity
    let systemMaturity: "nascent" | "developing" | "mature";
    if (totalMemories >= 100 && learningStats.totalPatterns >= 20) {
      systemMaturity = "mature";
    } else if (totalMemories >= 20 && learningStats.totalPatterns >= 5) {
      systemMaturity = "developing";
    } else {
      systemMaturity = "nascent";
    }

    return {
      learning: learningStats,
      knowledgeGraph: graphStats,
      combined: {
        totalMemories,
        enhancedRecommendations,
        accuracyImprovement,
        systemMaturity,
      },
    };
  }

  /**
   * Close and cleanup
   */
  async close(): Promise<void> {
    await this.knowledgeGraph.saveToMemory();
    await super.close();
  }
}

export default EnhancedMemoryManager;

```

--------------------------------------------------------------------------------
/tests/integration/memory-mcp-tools.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Memory MCP Tools Integration Tests
 * Tests integration between memory system and MCP tools
 * Part of Issue #56 - Memory MCP Tools Integration Tests
 */

import { promises as fs } from "fs";
import path from "path";
import os from "os";
import {
  initializeMemory,
  rememberAnalysis,
  rememberRecommendation,
  rememberDeployment,
  rememberConfiguration,
  recallProjectHistory,
  getProjectInsights,
  getSimilarProjects,
  getMemoryStatistics,
} from "../../src/memory/integration.js";
import { analyzeRepository } from "../../src/tools/analyze-repository.js";
import { recommendSSG } from "../../src/tools/recommend-ssg.js";

describe("Memory MCP Tools Integration", () => {
  let tempDir: string;
  let testProjectDir: string;

  beforeEach(async () => {
    // Create unique temp directory for each test
    tempDir = path.join(
      os.tmpdir(),
      `memory-mcp-integration-${Date.now()}-${Math.random()
        .toString(36)
        .substr(2, 9)}`,
    );
    await fs.mkdir(tempDir, { recursive: true });

    // Create a mock project structure for testing
    testProjectDir = path.join(tempDir, "test-project");
    await createMockProject(testProjectDir);
  });

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

  async function createMockProject(projectPath: string) {
    await fs.mkdir(projectPath, { recursive: true });

    // Create package.json
    await fs.writeFile(
      path.join(projectPath, "package.json"),
      JSON.stringify(
        {
          name: "test-project",
          version: "1.0.0",
          dependencies: {
            react: "^18.0.0",
            typescript: "^5.0.0",
          },
          devDependencies: {
            jest: "^29.0.0",
          },
        },
        null,
        2,
      ),
    );

    // Create README.md
    await fs.writeFile(
      path.join(projectPath, "README.md"),
      `# Test Project

A test project for memory integration testing.

## Features
- TypeScript support
- React components
- Jest testing
`,
    );

    // Create src directory with TypeScript files
    await fs.mkdir(path.join(projectPath, "src"));
    await fs.writeFile(
      path.join(projectPath, "src/index.ts"),
      'export const hello = "world";',
    );
    await fs.writeFile(
      path.join(projectPath, "src/component.tsx"),
      'import React from "react"; export const Component = () => <div>Hello</div>;',
    );

    // Create tests directory
    await fs.mkdir(path.join(projectPath, "__tests__"));
    await fs.writeFile(
      path.join(projectPath, "__tests__/index.test.ts"),
      'test("hello world", () => { expect(true).toBe(true); });',
    );

    // Create docs directory
    await fs.mkdir(path.join(projectPath, "docs"));
    await fs.writeFile(
      path.join(projectPath, "docs/setup.md"),
      "# Setup Guide\n\nHow to set up the project.",
    );
  }

  describe("Memory Integration Initialization", () => {
    test("should initialize memory system for MCP tools", async () => {
      const memoryManager = await initializeMemory();

      expect(memoryManager).toBeDefined();
      expect(memoryManager.constructor.name).toBe("MemoryManager");
    });

    test("should handle memory system events", async () => {
      const memoryManager = await initializeMemory();
      let eventsFired = 0;

      memoryManager.on("memory-created", () => {
        eventsFired++;
      });

      // Create a memory entry
      await memoryManager.remember("analysis", { test: true });

      // Give events time to fire
      await new Promise((resolve) => setTimeout(resolve, 50));

      expect(eventsFired).toBeGreaterThanOrEqual(1);
    });
  });

  describe("Repository Analysis Integration", () => {
    test("should integrate repository analysis with memory system", async () => {
      // Run repository analysis tool
      const analysisResult = await analyzeRepository({
        path: testProjectDir,
        depth: "standard",
      });

      expect(analysisResult.isError).toBeFalsy();
      expect(analysisResult.content).toBeDefined();

      // Extract analysis data from MCP response
      const analysisContent = analysisResult.content.find(
        (c) => c.type === "text" && c.text.includes("Analysis Complete"),
      );
      expect(analysisContent).toBeDefined();

      // Remember analysis in memory system
      const analysisData = {
        projectId: "test-project",
        language: { primary: "typescript" },
        framework: { name: "react" },
        stats: { files: 5 },
        repository: { url: "github.com/test/project" },
      };

      const memoryId = await rememberAnalysis(testProjectDir, analysisData);
      expect(memoryId).toBeDefined();
      expect(typeof memoryId).toBe("string");

      // Verify memory was stored
      const memoryManager = await initializeMemory();
      const recalled = await memoryManager.recall(memoryId);

      expect(recalled).not.toBeNull();
      expect(recalled?.data.language.primary).toBe("typescript");
      expect(recalled?.data.framework.name).toBe("react");
    });

    test("should store analysis metadata correctly", async () => {
      const analysisData = {
        projectId: "metadata-test",
        language: { primary: "javascript" },
        framework: { name: "vue" },
        stats: { files: 25 },
        repository: { url: "github.com/test/vue-project" },
      };

      const memoryId = await rememberAnalysis(
        "/test/vue-project",
        analysisData,
      );

      const memoryManager = await initializeMemory();
      const recalled = await memoryManager.recall(memoryId);

      expect(recalled?.metadata.repository).toBe("github.com/test/vue-project");
      expect(recalled?.metadata.tags).toContain("analysis");
      expect(recalled?.metadata.tags).toContain("javascript");
      expect(recalled?.metadata.tags).toContain("vue");
    });
  });

  describe("SSG Recommendation Integration", () => {
    test("should integrate SSG recommendation with memory system", async () => {
      // First create an analysis
      const analysisData = {
        projectId: "ssg-test",
        language: { primary: "typescript" },
        framework: { name: "react" },
      };

      const analysisId = await rememberAnalysis(
        "/test/ssg-project",
        analysisData,
      );

      // Run SSG recommendation tool
      const recommendationResult = await recommendSSG({
        analysisId,
        preferences: {
          priority: "features",
          ecosystem: "javascript",
        },
      });

      expect(recommendationResult.content).toBeDefined();

      // Extract recommendation data
      const recommendationData = {
        recommended: "docusaurus",
        confidence: 0.85,
        reasoning: ["React-based", "TypeScript support"],
        analysisId,
      };

      const memoryId = await rememberRecommendation(
        analysisId,
        recommendationData,
      );
      expect(memoryId).toBeDefined();

      // Verify memory linking
      const memoryManager = await initializeMemory();
      const recalled = await memoryManager.recall(memoryId);

      expect(recalled?.data.recommended).toBe("docusaurus");
      expect(recalled?.metadata.ssg).toBe("docusaurus");
      expect(recalled?.metadata.tags).toContain("recommendation");
      expect(recalled?.metadata.tags).toContain("docusaurus");
    });

    test("should link recommendations to analysis", async () => {
      // Create analysis
      const analysisData = {
        projectId: "linked-test",
        language: { primary: "python" },
      };
      const analysisId = await rememberAnalysis(
        "/test/python-project",
        analysisData,
      );

      // Create recommendation
      const recommendationData = {
        recommended: "mkdocs",
        confidence: 0.9,
      };
      const recommendationId = await rememberRecommendation(
        analysisId,
        recommendationData,
      );

      // Verify linking
      const memoryManager = await initializeMemory();
      const recommendation = await memoryManager.recall(recommendationId);
      const analysis = await memoryManager.recall(analysisId);

      expect(recommendation?.metadata.projectId).toBe(
        analysis?.metadata.projectId,
      );
      expect(recommendation?.metadata.projectId).toBe("linked-test");
    });
  });

  describe("Deployment Memory Integration", () => {
    test("should store deployment results in memory", async () => {
      const deploymentData = {
        ssg: "hugo",
        status: "success",
        duration: 120,
        url: "https://test-project.github.io",
        branch: "gh-pages",
      };

      const memoryId = await rememberDeployment(
        "github.com/test/project",
        deploymentData,
      );

      const memoryManager = await initializeMemory();
      const recalled = await memoryManager.recall(memoryId);

      expect(recalled?.data.ssg).toBe("hugo");
      expect(recalled?.data.status).toBe("success");
      expect(recalled?.metadata.repository).toBe("github.com/test/project");
      expect(recalled?.metadata.tags).toContain("deployment");
      expect(recalled?.metadata.tags).toContain("success");
      expect(recalled?.metadata.tags).toContain("hugo");
    });

    test("should track deployment failures", async () => {
      const failedDeployment = {
        ssg: "jekyll",
        status: "failed",
        error: "Build failed: missing dependency",
        duration: 45,
      };

      const memoryId = await rememberDeployment(
        "github.com/test/failed-project",
        failedDeployment,
      );

      const memoryManager = await initializeMemory();
      const recalled = await memoryManager.recall(memoryId);

      expect(recalled?.data.status).toBe("failed");
      expect(recalled?.data.error).toContain("Build failed");
      expect(recalled?.metadata.tags).toContain("failed");
    });
  });

  describe("Configuration Memory Integration", () => {
    test("should store configuration data in memory", async () => {
      const configData = {
        title: "Test Documentation",
        theme: "material",
        plugins: ["search", "navigation"],
        build: {
          outputDir: "_site",
          baseUrl: "/docs/",
        },
      };

      const memoryId = await rememberConfiguration(
        "test-docs",
        "mkdocs",
        configData,
      );

      const memoryManager = await initializeMemory();
      const recalled = await memoryManager.recall(memoryId);

      expect(recalled?.data.title).toBe("Test Documentation");
      expect(recalled?.data.theme).toBe("material");
      expect(recalled?.metadata.ssg).toBe("mkdocs");
      expect(recalled?.metadata.tags).toContain("configuration");
      expect(recalled?.metadata.tags).toContain("mkdocs");
      expect(recalled?.metadata.tags).toContain("test-docs");
    });
  });

  describe("Project History and Insights", () => {
    test("should recall comprehensive project history", async () => {
      const projectId = "history-test";

      // Create a complete project workflow in memory
      const analysisData = {
        projectId,
        language: { primary: "typescript" },
        framework: { name: "react" },
      };
      await rememberAnalysis("/test/history-project", analysisData);

      const recommendationData = {
        recommended: "docusaurus",
        confidence: 0.8,
      };
      await rememberRecommendation("analysis-id", recommendationData);

      const deploymentData = {
        ssg: "docusaurus",
        status: "success",
        duration: 90,
      };
      await rememberDeployment(
        "github.com/test/history-project",
        deploymentData,
      );

      // Recall project history
      const history = await recallProjectHistory(projectId);

      expect(history.projectId).toBe(projectId);
      expect(history.history).toBeDefined();
      expect(history.insights).toBeDefined();
      expect(Array.isArray(history.insights)).toBe(true);
    });

    test("should generate meaningful project insights", async () => {
      const projectId = "insights-test";

      // Create deployment history
      const successfulDeployment = {
        ssg: "hugo",
        status: "success",
        duration: 60,
      };
      await rememberDeployment(
        "github.com/test/insights-project",
        successfulDeployment,
      );

      const failedDeployment = {
        ssg: "hugo",
        status: "failed",
        error: "Build timeout",
      };
      await rememberDeployment(
        "github.com/test/insights-project",
        failedDeployment,
      );

      const insights = await getProjectInsights(projectId);

      expect(Array.isArray(insights)).toBe(true);

      // Insights may be empty if memories don't meet criteria, that's ok
      if (insights.length > 0) {
        // Should include deployment success rate if deployments exist
        const successRateInsight = insights.find((i) =>
          i.includes("success rate"),
        );
        expect(successRateInsight).toBeDefined();
      }
    });
  });

  describe("Similar Projects Discovery", () => {
    test("should find similar projects based on characteristics", async () => {
      // Create multiple projects with different characteristics
      await rememberAnalysis("/project1", {
        projectId: "project1",
        language: { primary: "typescript" },
        framework: { name: "react" },
      });

      await rememberAnalysis("/project2", {
        projectId: "project2",
        language: { primary: "typescript" },
        framework: { name: "vue" },
      });

      await rememberAnalysis("/project3", {
        projectId: "project3",
        language: { primary: "python" },
        framework: { name: "django" },
      });

      // Search for similar projects
      const targetProject = {
        language: { primary: "typescript" },
        framework: { name: "react" },
        stats: { files: 100 },
      };

      const similarProjects = await getSimilarProjects(targetProject, 3);

      expect(Array.isArray(similarProjects)).toBe(true);

      // Similar projects may be empty if no matches found, that's ok
      if (similarProjects.length > 0) {
        // Should find TypeScript projects first
        const tsProjects = similarProjects.filter((p) => p.similarity > 0);
        expect(tsProjects.length).toBeGreaterThan(0);
      }
    });

    test("should calculate similarity scores correctly", async () => {
      // Create projects with known characteristics
      await rememberAnalysis("/exact-match", {
        projectId: "exact-match",
        language: { primary: "javascript" },
        framework: { name: "vue" },
        stats: { files: 50 },
        documentation: { type: "api" },
      });

      await rememberAnalysis("/partial-match", {
        projectId: "partial-match",
        language: { primary: "javascript" },
        framework: { name: "react" },
        stats: { files: 45 },
      });

      const targetProject = {
        language: { primary: "javascript" },
        framework: { name: "vue" },
        stats: { files: 52 },
        documentation: { type: "api" },
      };

      const similarProjects = await getSimilarProjects(targetProject, 5);

      expect(Array.isArray(similarProjects)).toBe(true);

      // Similar projects may be empty, but if found should have similarity scores
      if (similarProjects.length > 0) {
        const exactMatch = similarProjects.find(
          (p) => p.projectId === "exact-match",
        );
        const partialMatch = similarProjects.find(
          (p) => p.projectId === "partial-match",
        );

        if (exactMatch && partialMatch) {
          expect(exactMatch.similarity).toBeGreaterThan(
            partialMatch.similarity,
          );
        }
      }
    });
  });

  describe("Memory Statistics and Analytics", () => {
    test("should provide memory statistics for tools", async () => {
      // Create some test data
      await rememberAnalysis("/stats-test", {
        projectId: "stats-test",
        language: { primary: "go" },
      });

      await rememberDeployment("github.com/test/stats", {
        ssg: "hugo",
        status: "success",
      });

      const stats = await getMemoryStatistics();

      expect(stats).toBeDefined();
      expect(typeof stats).toBe("object");
    });
  });

  describe("Error Handling and Edge Cases", () => {
    test("should handle malformed analysis data gracefully", async () => {
      const malformedData = {
        // Missing required fields
        language: null,
        framework: undefined,
      };

      // Should not throw but handle gracefully
      const memoryId = await rememberAnalysis("/malformed", malformedData);
      expect(memoryId).toBeDefined();

      const memoryManager = await initializeMemory();
      const recalled = await memoryManager.recall(memoryId);
      expect(recalled).not.toBeNull();
    });

    test("should handle missing analysis when creating recommendations", async () => {
      const recommendationData = {
        recommended: "jekyll",
        confidence: 0.7,
      };

      // Reference non-existent analysis
      const memoryId = await rememberRecommendation(
        "non-existent-analysis",
        recommendationData,
      );
      expect(memoryId).toBeDefined();

      const memoryManager = await initializeMemory();
      const recalled = await memoryManager.recall(memoryId);
      expect(recalled?.data.recommended).toBe("jekyll");
    });

    test("should handle empty project history gracefully", async () => {
      const history = await recallProjectHistory("non-existent-project");

      expect(history.projectId).toBe("non-existent-project");
      expect(history.history).toBeDefined();
      expect(Array.isArray(history.insights)).toBe(true);
    });

    test("should handle similar projects search with no matches", async () => {
      const uniqueProject = {
        language: { primary: "rust" },
        framework: { name: "actix" },
        stats: { files: 500 },
      };

      const similarProjects = await getSimilarProjects(uniqueProject, 5);

      expect(Array.isArray(similarProjects)).toBe(true);
      // Should return empty array or minimal matches
      expect(similarProjects.length).toBeGreaterThanOrEqual(0);
    });
  });
});

```

--------------------------------------------------------------------------------
/tests/utils/content-extractor.test.ts:
--------------------------------------------------------------------------------

```typescript
import { promises as fs } from "fs";
import path from "path";
import os from "os";
import { extractRepositoryContent } from "../../src/utils/content-extractor";

describe("Content Extractor", () => {
  let tempDir: string;

  beforeEach(async () => {
    tempDir = path.join(os.tmpdir(), `content-extractor-${Date.now()}`);
    await fs.mkdir(tempDir, { recursive: true });
  });

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

  describe("extractRepositoryContent", () => {
    it("should extract README.md content", async () => {
      const readmeContent = `# Test Project\n## Installation\nRun npm install\n## Usage\nUse it like this`;
      await fs.writeFile(path.join(tempDir, "README.md"), readmeContent);

      const result = await extractRepositoryContent(tempDir);

      expect(result.readme).toBeDefined();
      expect(result.readme?.content).toBe(readmeContent);
      expect(result.readme?.sections.length).toBeGreaterThan(0);
      expect(result.readme?.sections[0].title).toBe("Test Project");
    });

    it("should extract readme.md (lowercase) content", async () => {
      const readmeContent = `# Test\nContent`;
      await fs.writeFile(path.join(tempDir, "readme.md"), readmeContent);

      const result = await extractRepositoryContent(tempDir);

      expect(result.readme).toBeDefined();
      expect(result.readme?.content).toBe(readmeContent);
    });

    it("should extract Readme.md (mixed case) content", async () => {
      const readmeContent = `# Test\nContent`;
      await fs.writeFile(path.join(tempDir, "Readme.md"), readmeContent);

      const result = await extractRepositoryContent(tempDir);

      expect(result.readme).toBeDefined();
      expect(result.readme?.content).toBe(readmeContent);
    });

    it("should return undefined when no README exists", async () => {
      const result = await extractRepositoryContent(tempDir);

      expect(result.readme).toBeUndefined();
    });

    it("should extract existing documentation from docs directory", async () => {
      const docsDir = path.join(tempDir, "docs");
      await fs.mkdir(docsDir, { recursive: true });
      await fs.writeFile(
        path.join(docsDir, "guide.md"),
        "# Guide\nHow to do things",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.existingDocs.length).toBeGreaterThan(0);
      expect(result.existingDocs[0].title).toBe("Guide");
      expect(result.existingDocs[0].path).toContain("guide.md");
    });

    it("should extract documentation from documentation directory", async () => {
      const docsDir = path.join(tempDir, "documentation");
      await fs.mkdir(docsDir, { recursive: true });
      await fs.writeFile(path.join(docsDir, "test.md"), "# Test Doc\nContent");

      const result = await extractRepositoryContent(tempDir);

      expect(result.existingDocs.length).toBeGreaterThan(0);
    });

    it("should extract documentation from doc directory", async () => {
      const docsDir = path.join(tempDir, "doc");
      await fs.mkdir(docsDir, { recursive: true });
      await fs.writeFile(path.join(docsDir, "test.md"), "# Test\nContent");

      const result = await extractRepositoryContent(tempDir);

      expect(result.existingDocs.length).toBeGreaterThan(0);
    });

    it("should extract .mdx files", async () => {
      const docsDir = path.join(tempDir, "docs");
      await fs.mkdir(docsDir, { recursive: true });
      await fs.writeFile(
        path.join(docsDir, "component.mdx"),
        "# Component\nJSX content",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.existingDocs.length).toBeGreaterThan(0);
      expect(result.existingDocs[0].path).toContain("component.mdx");
    });

    it("should recursively extract docs from subdirectories", async () => {
      const docsDir = path.join(tempDir, "docs");
      const subDir = path.join(docsDir, "guides");
      await fs.mkdir(subDir, { recursive: true });
      await fs.writeFile(
        path.join(subDir, "tutorial.md"),
        "# Tutorial\nStep by step",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.existingDocs.length).toBeGreaterThan(0);
      expect(result.existingDocs[0].path).toContain("guides");
    });

    it("should skip hidden directories", async () => {
      const docsDir = path.join(tempDir, "docs");
      const hiddenDir = path.join(docsDir, ".hidden");
      await fs.mkdir(hiddenDir, { recursive: true });
      await fs.writeFile(
        path.join(hiddenDir, "secret.md"),
        "# Secret\nContent",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(
        result.existingDocs.find((d) => d.path.includes(".hidden")),
      ).toBeUndefined();
    });

    it("should categorize documents as tutorial", async () => {
      const docsDir = path.join(tempDir, "docs");
      await fs.mkdir(docsDir, { recursive: true });
      await fs.writeFile(
        path.join(docsDir, "getting-started.md"),
        "# Getting Started\n## Step 1\nFirst, do this",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.existingDocs[0].category).toBe("tutorial");
    });

    it("should categorize documents as how-to", async () => {
      const docsDir = path.join(tempDir, "docs");
      await fs.mkdir(docsDir, { recursive: true });
      await fs.writeFile(
        path.join(docsDir, "how-to-deploy.md"),
        "# How to Deploy\nYou can deploy by...",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.existingDocs[0].category).toBe("how-to");
    });

    it("should categorize documents as reference", async () => {
      const docsDir = path.join(tempDir, "docs");
      await fs.mkdir(docsDir, { recursive: true });
      await fs.writeFile(
        path.join(docsDir, "api.md"),
        "# API Reference\nEndpoints",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.existingDocs[0].category).toBe("reference");
    });

    it("should categorize documents as explanation", async () => {
      const docsDir = path.join(tempDir, "docs");
      const adrDir = path.join(docsDir, "adrs");
      await fs.mkdir(adrDir, { recursive: true });
      await fs.writeFile(
        path.join(adrDir, "001-decision.md"),
        "# Decision\nExplanation",
      );

      const result = await extractRepositoryContent(tempDir);

      const adrDocs = result.existingDocs.filter((d) => d.path.includes("adr"));
      expect(adrDocs.length).toBeGreaterThan(0);
      expect(adrDocs[0].category).toBe("explanation");
    });

    it("should extract title from first heading", async () => {
      const docsDir = path.join(tempDir, "docs");
      await fs.mkdir(docsDir, { recursive: true });
      await fs.writeFile(
        path.join(docsDir, "doc.md"),
        "Some text\n# Main Title\nContent",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.existingDocs[0].title).toBe("Main Title");
    });

    it("should use filename as title when no heading exists", async () => {
      const docsDir = path.join(tempDir, "docs");
      await fs.mkdir(docsDir, { recursive: true });
      await fs.writeFile(path.join(docsDir, "my-doc-file.md"), "No heading");

      const result = await extractRepositoryContent(tempDir);

      expect(result.existingDocs[0].title).toBe("my doc file");
    });

    it("should extract ADRs from docs/adrs", async () => {
      const adrDir = path.join(tempDir, "docs/adrs");
      await fs.mkdir(adrDir, { recursive: true });
      await fs.writeFile(
        path.join(adrDir, "001-use-typescript.md"),
        "# 1. Use TypeScript\n## Status\nAccepted\n## Decision\nWe will use TypeScript\n## Consequences\nBetter type safety",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.adrs.length).toBeGreaterThan(0);
      expect(result.adrs[0].number).toBe("001");
      expect(result.adrs[0].title).toBe("Use TypeScript");
      expect(result.adrs[0].status).toBe("Accepted");
      expect(result.adrs[0].decision).toContain("TypeScript");
      expect(result.adrs[0].consequences).toContain("type safety");
    });

    it("should extract ADRs from docs/adr", async () => {
      const adrDir = path.join(tempDir, "docs/adr");
      await fs.mkdir(adrDir, { recursive: true });
      await fs.writeFile(
        path.join(adrDir, "0001-test.md"),
        "# Test\n## Status\nDraft\n## Decision\nTest",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.adrs.length).toBeGreaterThan(0);
    });

    it("should extract ADRs from docs/decisions", async () => {
      const adrDir = path.join(tempDir, "docs/decisions");
      await fs.mkdir(adrDir, { recursive: true });
      await fs.writeFile(
        path.join(adrDir, "0001-test.md"),
        "# Test\n## Status\nDraft\n## Decision\nTest",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.adrs.length).toBeGreaterThan(0);
    });

    it("should skip ADRs without number in filename", async () => {
      const adrDir = path.join(tempDir, "docs/adrs");
      await fs.mkdir(adrDir, { recursive: true });
      await fs.writeFile(
        path.join(adrDir, "template.md"),
        "# Template\n## Status\nN/A",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.adrs.length).toBe(0);
    });

    it("should extract code examples from examples directory", async () => {
      const examplesDir = path.join(tempDir, "examples");
      await fs.mkdir(examplesDir, { recursive: true });
      await fs.writeFile(
        path.join(examplesDir, "hello.js"),
        "// Example: Hello World\nconsole.log('hello');",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.codeExamples.length).toBeGreaterThan(0);
      expect(result.codeExamples[0].language).toBe("javascript");
      expect(result.codeExamples[0].file).toBe("hello.js");
    });

    it("should extract code examples from samples directory", async () => {
      const samplesDir = path.join(tempDir, "samples");
      await fs.mkdir(samplesDir, { recursive: true });
      await fs.writeFile(
        path.join(samplesDir, "test.py"),
        "# Demo: Python example\nprint('test')",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.codeExamples.length).toBeGreaterThan(0);
      expect(result.codeExamples[0].language).toBe("python");
    });

    it("should extract code examples from demo directory", async () => {
      const demoDir = path.join(tempDir, "demo");
      await fs.mkdir(demoDir, { recursive: true });
      await fs.writeFile(path.join(demoDir, "test.ts"), "const x = 1;");

      const result = await extractRepositoryContent(tempDir);

      expect(result.codeExamples.length).toBeGreaterThan(0);
      expect(result.codeExamples[0].language).toBe("typescript");
    });

    it("should extract inline examples from @example tags", async () => {
      const srcDir = path.join(tempDir, "src");
      await fs.mkdir(srcDir, { recursive: true });
      await fs.writeFile(
        path.join(srcDir, "utils.ts"),
        "/**\n * @example\n * const result = add(1, 2);\n */\nfunction add(a, b) { return a + b; }",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.codeExamples.length).toBeGreaterThan(0);
      expect(result.codeExamples[0].code).toContain("add(1, 2)");
    });

    it("should support various programming languages", async () => {
      const examplesDir = path.join(tempDir, "examples");
      await fs.mkdir(examplesDir, { recursive: true });
      await fs.writeFile(path.join(examplesDir, "test.rb"), "puts 'hello'");
      await fs.writeFile(path.join(examplesDir, "test.go"), "package main");
      await fs.writeFile(path.join(examplesDir, "test.java"), "class Test {}");
      await fs.writeFile(path.join(examplesDir, "test.rs"), "fn main() {}");

      const result = await extractRepositoryContent(tempDir);

      const languages = result.codeExamples.map((e) => e.language);
      expect(languages).toContain("ruby");
      expect(languages).toContain("go");
      expect(languages).toContain("java");
      expect(languages).toContain("rust");
    });

    it("should extract API docs from markdown files", async () => {
      const apiContent = `## \`getUserById\` function\n\nGet user by ID\n\n### Parameters\n\n- \`id\` (string) - User ID\n\n### Returns\n\nUser object`;
      await fs.writeFile(path.join(tempDir, "api.md"), apiContent);

      const result = await extractRepositoryContent(tempDir);

      expect(result.apiDocs.length).toBeGreaterThan(0);
      expect(result.apiDocs[0].function).toBe("getUserById");
      expect(result.apiDocs[0].parameters.length).toBeGreaterThan(0);
    });

    it("should extract API docs from OpenAPI spec", async () => {
      const openApiSpec = {
        paths: {
          "/users": {
            get: {
              summary: "List users",
              parameters: [
                {
                  name: "page",
                  type: "integer",
                  description: "Page number",
                },
              ],
              responses: {
                "200": {
                  description: "Success",
                },
              },
            },
          },
        },
      };
      await fs.writeFile(
        path.join(tempDir, "openapi.json"),
        JSON.stringify(openApiSpec),
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.apiDocs.length).toBeGreaterThan(0);
      expect(result.apiDocs[0].endpoint).toContain("GET");
    });

    it("should extract JSDoc from source files", async () => {
      const srcDir = path.join(tempDir, "src");
      await fs.mkdir(srcDir, { recursive: true });
      await fs.writeFile(
        path.join(srcDir, "utils.js"),
        "/**\n * Add two numbers\n * @param {number} a - First number\n * @param {number} b - Second number\n * @returns {number} Sum of a and b\n */\nfunction add(a, b) { return a + b; }",
      );

      const result = await extractRepositoryContent(tempDir);

      // JSDoc extraction may or may not find the function depending on parsing
      // Just ensure it doesn't crash and returns valid structure
      expect(result.apiDocs).toBeDefined();
      expect(Array.isArray(result.apiDocs)).toBe(true);
    });

    it("should handle empty repository gracefully", async () => {
      const result = await extractRepositoryContent(tempDir);

      expect(result.readme).toBeUndefined();
      expect(result.existingDocs).toEqual([]);
      expect(result.adrs).toEqual([]);
      expect(result.codeExamples).toEqual([]);
      expect(result.apiDocs).toEqual([]);
    });

    it("should handle unreadable files gracefully", async () => {
      const docsDir = path.join(tempDir, "docs");
      await fs.mkdir(docsDir, { recursive: true });

      // Create a file and immediately make it unreadable (if possible)
      const filePath = path.join(docsDir, "test.md");
      await fs.writeFile(filePath, "content");

      // The function should handle errors gracefully and continue
      const result = await extractRepositoryContent(tempDir);

      expect(result).toBeDefined();
    });

    it("should parse markdown sections correctly", async () => {
      const readmeContent = `# Main\nIntro\n## Section 1\nContent 1\n### Subsection\nContent 2\n## Section 2\nContent 3`;
      await fs.writeFile(path.join(tempDir, "README.md"), readmeContent);

      const result = await extractRepositoryContent(tempDir);

      expect(result.readme?.sections.length).toBeGreaterThan(2);
      expect(result.readme?.sections[0].level).toBe(1);
      expect(result.readme?.sections[1].level).toBe(2);
    });

    it("should handle ADRs with 4-digit numbers", async () => {
      const adrDir = path.join(tempDir, "docs/adrs");
      await fs.mkdir(adrDir, { recursive: true });
      await fs.writeFile(
        path.join(adrDir, "1234-long-number.md"),
        "# Test\n## Status\nAccepted\n## Decision\nTest",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.adrs.length).toBeGreaterThan(0);
      expect(result.adrs[0].number).toBe("1234");
    });

    it("should extract example description from code comments", async () => {
      const examplesDir = path.join(tempDir, "examples");
      await fs.mkdir(examplesDir, { recursive: true });
      await fs.writeFile(
        path.join(examplesDir, "test.js"),
        "// Example: This is a demo\nconsole.log('test');",
      );

      const result = await extractRepositoryContent(tempDir);

      expect(result.codeExamples[0].description).toContain("Example:");
    });

    it("should limit code example length", async () => {
      const examplesDir = path.join(tempDir, "examples");
      await fs.mkdir(examplesDir, { recursive: true });
      const longCode = "x".repeat(1000);
      await fs.writeFile(path.join(examplesDir, "test.js"), longCode);

      const result = await extractRepositoryContent(tempDir);

      expect(result.codeExamples[0].code.length).toBeLessThanOrEqual(500);
    });

    it("should handle invalid OpenAPI spec gracefully", async () => {
      await fs.writeFile(path.join(tempDir, "openapi.json"), "invalid json{");

      const result = await extractRepositoryContent(tempDir);

      // Should not crash, just skip the invalid spec
      expect(result).toBeDefined();
    });

    it("should skip non-markdown files in docs", async () => {
      const docsDir = path.join(tempDir, "docs");
      await fs.mkdir(docsDir, { recursive: true });
      await fs.writeFile(path.join(docsDir, "image.png"), "binary data");
      await fs.writeFile(path.join(docsDir, "valid.md"), "# Valid\nContent");

      const result = await extractRepositoryContent(tempDir);

      expect(result.existingDocs.length).toBe(1);
      expect(result.existingDocs[0].path).toContain("valid.md");
    });

    it("should handle swagger.yaml files", async () => {
      await fs.writeFile(path.join(tempDir, "swagger.yaml"), "openapi: 3.0.0");

      const result = await extractRepositoryContent(tempDir);

      // Should attempt to parse it (even if it fails due to YAML parsing)
      expect(result).toBeDefined();
    });
  });
});

```

--------------------------------------------------------------------------------
/src/memory/learning.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Incremental Learning System for DocuMCP
 * Implements Issue #47: Incremental Learning System
 *
 * Enables continuous improvement of recommendations based on historical patterns,
 * success rates, and user feedback to optimize SSG suggestions and documentation strategies.
 */

import { MemoryManager } from "./manager.js";
import { MemoryEntry } from "./storage.js";

export interface LearningPattern {
  id: string;
  type:
    | "ssg_preference"
    | "deployment_success"
    | "project_similarity"
    | "user_behavior";
  pattern: Record<string, any>;
  confidence: number;
  sampleSize: number;
  lastUpdated: string;
  metadata: {
    projectTypes?: string[];
    technologies?: string[];
    successRate?: number;
    frequency?: number;
  };
}

export interface LearningInsight {
  type: "recommendation" | "warning" | "optimization";
  message: string;
  confidence: number;
  actionable: boolean;
  data: Record<string, any>;
}

export interface ProjectFeatures {
  language: string;
  framework?: string;
  size: "small" | "medium" | "large";
  complexity: "simple" | "moderate" | "complex";
  hasTests: boolean;
  hasCI: boolean;
  hasDocs: boolean;
  teamSize?: number;
  isOpenSource: boolean;
}

export class IncrementalLearningSystem {
  private memoryManager: MemoryManager;
  private patterns: Map<string, LearningPattern>;
  private learningEnabled: boolean = true;
  private readonly minSampleSize = 3;
  private readonly confidenceThreshold = 0.7;

  constructor(memoryManager: MemoryManager) {
    this.memoryManager = memoryManager;
    this.patterns = new Map();
  }

  async initialize(): Promise<void> {
    await this.loadPatterns();
    await this.updatePatterns();
  }

  /**
   * Learn from a new interaction result
   */
  async learn(
    interaction: MemoryEntry,
    outcome: "success" | "failure" | "neutral",
    feedback?: Record<string, any>,
  ): Promise<void> {
    if (!this.learningEnabled) return;

    const features = this.extractFeatures(interaction);

    // Update SSG preference patterns
    if (interaction.type === "recommendation" && interaction.metadata.ssg) {
      await this.updateSSGPattern(features, interaction.metadata.ssg, outcome);
    }

    // Update deployment success patterns
    if (interaction.type === "deployment") {
      await this.updateDeploymentPattern(features, outcome);
    }

    // Update project similarity patterns
    if (interaction.type === "analysis") {
      await this.updateSimilarityPattern(features, interaction);
    }

    // Learn from user feedback
    if (feedback) {
      await this.updateUserBehaviorPattern(features, feedback);
    }

    await this.persistPatterns();
  }

  /**
   * Get improved recommendations based on learned patterns
   */
  async getImprovedRecommendation(
    projectFeatures: ProjectFeatures,
    baseRecommendation: any,
  ): Promise<{
    recommendation: any;
    confidence: number;
    insights: LearningInsight[];
  }> {
    const insights: LearningInsight[] = [];
    const adjustedRecommendation = { ...baseRecommendation };
    let confidenceBoost = 0;

    // Apply SSG preference patterns
    const ssgPattern = await this.getSSGPreferencePattern(projectFeatures);
    if (ssgPattern && ssgPattern.confidence > this.confidenceThreshold) {
      const preferredSSG = ssgPattern.pattern.preferredSSG;
      if (preferredSSG !== baseRecommendation.recommended) {
        insights.push({
          type: "recommendation",
          message: `Based on ${
            ssgPattern.sampleSize
          } similar projects, ${preferredSSG} has a ${(
            ssgPattern.pattern.successRate * 100
          ).toFixed(0)}% success rate`,
          confidence: ssgPattern.confidence,
          actionable: true,
          data: { suggestedSSG: preferredSSG, pattern: ssgPattern },
        });

        adjustedRecommendation.recommended = preferredSSG;
        adjustedRecommendation.learningAdjusted = true;
        confidenceBoost += 0.2;
      }
    }

    // Apply deployment success patterns
    const deploymentPattern = await this.getDeploymentPattern(projectFeatures);
    if (
      deploymentPattern &&
      deploymentPattern.confidence > this.confidenceThreshold
    ) {
      const riskFactors = deploymentPattern.pattern.riskFactors || [];
      if (riskFactors.length > 0) {
        insights.push({
          type: "warning",
          message: `Projects with similar characteristics have ${riskFactors.length} common deployment issues`,
          confidence: deploymentPattern.confidence,
          actionable: true,
          data: { riskFactors, pattern: deploymentPattern },
        });

        adjustedRecommendation.deploymentWarnings = riskFactors;
      }
    }

    // Apply optimization patterns
    const optimizations = await this.getOptimizationInsights(projectFeatures);
    insights.push(...optimizations);

    const finalConfidence = Math.min(
      baseRecommendation.confidence + confidenceBoost,
      1.0,
    );

    return {
      recommendation: adjustedRecommendation,
      confidence: finalConfidence,
      insights,
    };
  }

  /**
   * Extract features from project data for pattern matching
   */
  private extractFeatures(interaction: MemoryEntry): ProjectFeatures {
    const data = interaction.data;

    return {
      language: data.language?.primary || "unknown",
      framework: data.framework?.name,
      size: this.categorizeSize(data.stats?.files || 0),
      complexity: this.categorizeComplexity(data),
      hasTests: Boolean(data.testing?.hasTests),
      hasCI: Boolean(data.ci?.hasCI),
      hasDocs: Boolean(data.documentation?.exists),
      isOpenSource: Boolean(data.repository?.isPublic),
    };
  }

  private categorizeSize(fileCount: number): "small" | "medium" | "large" {
    if (fileCount < 50) return "small";
    if (fileCount < 200) return "medium";
    return "large";
  }

  private categorizeComplexity(data: any): "simple" | "moderate" | "complex" {
    let complexity = 0;

    if (data.dependencies?.count > 20) complexity++;
    if (data.framework?.name) complexity++;
    if (data.testing?.frameworks?.length > 1) complexity++;
    if (data.ci?.workflows?.length > 2) complexity++;
    if (data.architecture?.patterns?.length > 3) complexity++;

    if (complexity <= 1) return "simple";
    if (complexity <= 3) return "moderate";
    return "complex";
  }

  /**
   * Update SSG preference patterns based on outcomes
   */
  private async updateSSGPattern(
    features: ProjectFeatures,
    ssg: string,
    outcome: "success" | "failure" | "neutral",
  ): Promise<void> {
    const patternKey = this.generatePatternKey("ssg_preference", features);
    const existing = this.patterns.get(patternKey);

    if (existing) {
      // Update existing pattern
      const totalCount = existing.sampleSize;
      const successCount = existing.pattern.successCount || 0;
      const newSuccessCount =
        outcome === "success" ? successCount + 1 : successCount;

      existing.pattern.preferredSSG = ssg;
      existing.pattern.successCount = newSuccessCount;
      existing.pattern.successRate = newSuccessCount / (totalCount + 1);
      existing.sampleSize = totalCount + 1;
      existing.confidence = Math.min(existing.sampleSize / 10, 1.0);
      existing.lastUpdated = new Date().toISOString();
    } else {
      // Create new pattern
      const pattern: LearningPattern = {
        id: patternKey,
        type: "ssg_preference",
        pattern: {
          preferredSSG: ssg,
          successCount: outcome === "success" ? 1 : 0,
          successRate: outcome === "success" ? 1.0 : 0.0,
        },
        confidence: 0.1,
        sampleSize: 1,
        lastUpdated: new Date().toISOString(),
        metadata: {
          projectTypes: [features.language],
          technologies: features.framework ? [features.framework] : [],
        },
      };

      this.patterns.set(patternKey, pattern);
    }
  }

  /**
   * Update deployment success patterns
   */
  private async updateDeploymentPattern(
    features: ProjectFeatures,
    outcome: "success" | "failure" | "neutral",
  ): Promise<void> {
    const patternKey = this.generatePatternKey("deployment_success", features);
    const existing = this.patterns.get(patternKey);

    const riskFactors: string[] = [];
    if (!features.hasTests) riskFactors.push("no_tests");
    if (!features.hasCI) riskFactors.push("no_ci");
    if (features.complexity === "complex") riskFactors.push("high_complexity");
    if (features.size === "large") riskFactors.push("large_codebase");

    if (existing) {
      const totalCount = existing.sampleSize;
      const successCount = existing.pattern.successCount || 0;
      const newSuccessCount =
        outcome === "success" ? successCount + 1 : successCount;

      existing.pattern.successCount = newSuccessCount;
      existing.pattern.successRate = newSuccessCount / (totalCount + 1);
      existing.pattern.riskFactors = riskFactors;
      existing.sampleSize = totalCount + 1;
      existing.confidence = Math.min(existing.sampleSize / 10, 1.0);
      existing.lastUpdated = new Date().toISOString();
    } else {
      const pattern: LearningPattern = {
        id: patternKey,
        type: "deployment_success",
        pattern: {
          successCount: outcome === "success" ? 1 : 0,
          successRate: outcome === "success" ? 1.0 : 0.0,
          riskFactors,
        },
        confidence: 0.1,
        sampleSize: 1,
        lastUpdated: new Date().toISOString(),
        metadata: {
          projectTypes: [features.language],
          successRate: outcome === "success" ? 1.0 : 0.0,
        },
      };

      this.patterns.set(patternKey, pattern);
    }
  }

  /**
   * Update project similarity patterns for better matching
   */
  private async updateSimilarityPattern(
    features: ProjectFeatures,
    _interaction: MemoryEntry,
  ): Promise<void> {
    const patternKey = this.generatePatternKey("project_similarity", features);
    const existing = this.patterns.get(patternKey);

    const characteristics = {
      language: features.language,
      framework: features.framework,
      size: features.size,
      complexity: features.complexity,
    };

    if (existing) {
      existing.pattern.characteristics = characteristics;
      existing.sampleSize += 1;
      existing.confidence = Math.min(existing.sampleSize / 15, 1.0);
      existing.lastUpdated = new Date().toISOString();
    } else {
      const pattern: LearningPattern = {
        id: patternKey,
        type: "project_similarity",
        pattern: {
          characteristics,
          commonPatterns: [],
        },
        confidence: 0.1,
        sampleSize: 1,
        lastUpdated: new Date().toISOString(),
        metadata: {
          projectTypes: [features.language],
        },
      };

      this.patterns.set(patternKey, pattern);
    }
  }

  /**
   * Update user behavior patterns from feedback
   */
  private async updateUserBehaviorPattern(
    features: ProjectFeatures,
    feedback: Record<string, any>,
  ): Promise<void> {
    const patternKey = this.generatePatternKey("user_behavior", features);
    const existing = this.patterns.get(patternKey);

    if (existing) {
      existing.pattern.feedback = { ...existing.pattern.feedback, ...feedback };
      existing.sampleSize += 1;
      existing.confidence = Math.min(existing.sampleSize / 5, 1.0);
      existing.lastUpdated = new Date().toISOString();
    } else {
      const pattern: LearningPattern = {
        id: patternKey,
        type: "user_behavior",
        pattern: {
          feedback,
          preferences: {},
        },
        confidence: 0.2,
        sampleSize: 1,
        lastUpdated: new Date().toISOString(),
        metadata: {},
      };

      this.patterns.set(patternKey, pattern);
    }
  }

  /**
   * Generate a consistent pattern key for grouping similar projects
   */
  private generatePatternKey(type: string, features: ProjectFeatures): string {
    const keyParts = [
      type,
      features.language,
      features.framework || "none",
      features.size,
      features.complexity,
    ];
    return keyParts.join("_").toLowerCase();
  }

  /**
   * Get SSG preference pattern for similar projects
   */
  private async getSSGPreferencePattern(
    features: ProjectFeatures,
  ): Promise<LearningPattern | null> {
    const patternKey = this.generatePatternKey("ssg_preference", features);
    const pattern = this.patterns.get(patternKey);

    if (pattern && pattern.sampleSize >= this.minSampleSize) {
      return pattern;
    }

    // Try broader matching if exact match not found
    const broaderKey = `ssg_preference_${features.language}_${features.size}`;
    return this.patterns.get(broaderKey) || null;
  }

  /**
   * Get deployment success pattern for risk assessment
   */
  private async getDeploymentPattern(
    features: ProjectFeatures,
  ): Promise<LearningPattern | null> {
    const patternKey = this.generatePatternKey("deployment_success", features);
    const pattern = this.patterns.get(patternKey);

    if (pattern && pattern.sampleSize >= this.minSampleSize) {
      return pattern;
    }

    return null;
  }

  /**
   * Generate optimization insights based on learned patterns
   */
  private async getOptimizationInsights(
    features: ProjectFeatures,
  ): Promise<LearningInsight[]> {
    const insights: LearningInsight[] = [];

    // Check for common optimization opportunities
    if (!features.hasTests) {
      insights.push({
        type: "optimization",
        message: "Adding tests could improve deployment success rate by 25%",
        confidence: 0.8,
        actionable: true,
        data: { optimization: "add_tests", impact: "deployment_success" },
      });
    }

    if (!features.hasCI && features.size !== "small") {
      insights.push({
        type: "optimization",
        message: "CI/CD setup recommended for projects of this size",
        confidence: 0.7,
        actionable: true,
        data: { optimization: "add_ci", impact: "development_velocity" },
      });
    }

    if (features.complexity === "complex" && !features.hasDocs) {
      insights.push({
        type: "optimization",
        message:
          "Complex projects benefit significantly from comprehensive documentation",
        confidence: 0.9,
        actionable: true,
        data: { optimization: "improve_docs", impact: "maintainability" },
      });
    }

    return insights;
  }

  /**
   * Load patterns from persistent storage
   */
  private async loadPatterns(): Promise<void> {
    try {
      const patternMemories =
        await this.memoryManager.search("learning_pattern");

      for (const memory of patternMemories) {
        if (memory.data.pattern) {
          this.patterns.set(memory.data.pattern.id, memory.data.pattern);
        }
      }
    } catch (error) {
      console.error("Failed to load learning patterns:", error);
    }
  }

  /**
   * Update patterns based on recent memory data
   */
  private async updatePatterns(): Promise<void> {
    // Analyze recent memories to update patterns
    const recentMemories = await this.memoryManager.search("", {
      sortBy: "timestamp",
    });

    const cutoffDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); // Last 7 days

    for (const memory of recentMemories) {
      if (new Date(memory.timestamp) > cutoffDate) {
        // Infer outcome based on memory data
        const outcome = this.inferOutcome(memory);
        if (outcome) {
          await this.learn(memory, outcome);
        }
      }
    }
  }

  /**
   * Infer outcome from memory entry data
   */
  private inferOutcome(
    memory: MemoryEntry,
  ): "success" | "failure" | "neutral" | null {
    if (memory.type === "deployment") {
      if (memory.data.status === "success") return "success";
      if (memory.data.status === "failed") return "failure";
    }

    if (memory.type === "recommendation" && memory.data.feedback) {
      if (memory.data.feedback.rating > 3) return "success";
      if (memory.data.feedback.rating < 3) return "failure";
    }

    return "neutral";
  }

  /**
   * Persist learned patterns to memory
   */
  private async persistPatterns(): Promise<void> {
    for (const [, pattern] of this.patterns) {
      if (pattern.sampleSize >= this.minSampleSize) {
        await this.memoryManager.remember(
          "interaction",
          {
            pattern,
            type: "learning_pattern",
          },
          {
            tags: ["learning", "pattern", pattern.type],
          },
        );
      }
    }
  }

  /**
   * Get all learned patterns
   */
  async getPatterns(): Promise<LearningPattern[]> {
    return Array.from(this.patterns.values());
  }

  /**
   * Get learning statistics and insights
   */
  async getStatistics(): Promise<{
    totalPatterns: number;
    patternsByType: Record<string, number>;
    averageConfidence: number;
    learningVelocity: number;
    insights: string[];
  }> {
    const stats = {
      totalPatterns: this.patterns.size,
      patternsByType: {} as Record<string, number>,
      averageConfidence: 0,
      learningVelocity: 0,
      insights: [] as string[],
    };

    let totalConfidence = 0;
    for (const pattern of this.patterns.values()) {
      stats.patternsByType[pattern.type] =
        (stats.patternsByType[pattern.type] || 0) + 1;
      totalConfidence += pattern.confidence;
    }

    stats.averageConfidence =
      stats.totalPatterns > 0 ? totalConfidence / stats.totalPatterns : 0;

    // Calculate learning velocity (patterns learned in last week)
    const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
    stats.learningVelocity = Array.from(this.patterns.values()).filter(
      (p) => new Date(p.lastUpdated) > weekAgo,
    ).length;

    // Generate insights
    if (stats.totalPatterns > 10) {
      stats.insights.push(
        `System has learned ${stats.totalPatterns} patterns with ${(
          stats.averageConfidence * 100
        ).toFixed(0)}% average confidence`,
      );
    }

    if (stats.learningVelocity > 0) {
      stats.insights.push(
        `Learning velocity: ${stats.learningVelocity} new patterns this week`,
      );
    }

    const topPatternType = Object.entries(stats.patternsByType).sort(
      ([, a], [, b]) => b - a,
    )[0];

    if (topPatternType) {
      stats.insights.push(
        `Most common pattern type: ${topPatternType[0]} (${topPatternType[1]} patterns)`,
      );
    }

    return stats;
  }

  /**
   * Enable or disable learning
   */
  setLearningEnabled(enabled: boolean): void {
    this.learningEnabled = enabled;
  }

  /**
   * Clear all learned patterns (useful for testing or reset)
   */
  async clearPatterns(): Promise<void> {
    this.patterns.clear();
  }
}

export default IncrementalLearningSystem;

```

--------------------------------------------------------------------------------
/src/memory/integration.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Memory System Integration for DocuMCP
 * Connects memory capabilities to MCP tools
 */

import { MemoryManager } from "./manager.js";
import { MemoryEntry } from "./storage.js";

let memoryManager: MemoryManager | null = null;

/**
 * Initializes the DocuMCP memory system for persistent learning and context.
 *
 * Sets up the memory manager with optional custom storage directory, configures
 * event listeners for debugging in development mode, and ensures the memory
 * system is ready for storing and retrieving project analysis data, user
 * preferences, and deployment patterns.
 *
 * @param storageDir - Optional custom directory path for memory storage (defaults to .documcp/memory)
 *
 * @returns Promise resolving to the initialized MemoryManager instance
 *
 * @throws {Error} When memory system initialization fails
 * @throws {Error} When storage directory cannot be created or accessed
 *
 * @example
 * ```typescript
 * // Initialize with default storage
 * const memory = await initializeMemory();
 *
 * // Initialize with custom storage directory
 * const customMemory = await initializeMemory("/custom/memory/path");
 * ```
 *
 * @since 1.0.0
 * @version 1.2.0 - Added development mode event logging
 */
export async function initializeMemory(
  storageDir?: string,
): Promise<MemoryManager> {
  if (!memoryManager) {
    memoryManager = new MemoryManager(storageDir);
    await memoryManager.initialize();

    // Set up event listeners (debug logging disabled in production)
    if (process.env.NODE_ENV === "development") {
      memoryManager.on("memory-created", (entry: MemoryEntry) => {
        // eslint-disable-next-line no-console
        console.log(`[Memory] Created: ${entry.id} (${entry.type})`);
      });

      memoryManager.on("memory-updated", (entry: MemoryEntry) => {
        // eslint-disable-next-line no-console
        console.log(`[Memory] Updated: ${entry.id}`);
      });

      memoryManager.on("memory-deleted", (id: string) => {
        // eslint-disable-next-line no-console
        console.log(`[Memory] Deleted: ${id}`);
      });
    }
  }

  return memoryManager;
}

/**
 * Stores repository analysis data in the memory system for future reference.
 *
 * Persists comprehensive repository analysis results including structure, dependencies,
 * documentation status, and recommendations. This data is used for learning patterns,
 * improving future recommendations, and providing historical context for similar projects.
 *
 * @param projectPath - The file system path to the analyzed repository
 * @param analysisData - The complete repository analysis results to store
 *
 * @returns Promise resolving to the unique memory entry ID
 *
 * @throws {Error} When memory system is not initialized
 * @throws {Error} When analysis data cannot be stored
 *
 * @example
 * ```typescript
 * const analysisId = await rememberAnalysis("/path/to/project", {
 *   id: "analysis_123",
 *   structure: { totalFiles: 150, languages: { ".ts": 100 } },
 *   dependencies: { ecosystem: "javascript", packages: ["react"] },
 *   // ... other analysis data
 * });
 * ```
 *
 * @since 1.0.0
 */
export async function rememberAnalysis(
  projectPath: string,
  analysisData: any,
): Promise<string> {
  const manager = await initializeMemory();

  manager.setContext({
    projectId: analysisData.projectId || projectPath,
    repository: analysisData.repository?.url,
  });

  const entry = await manager.remember("analysis", analysisData, {
    repository: analysisData.repository?.url,
    tags: [
      "analysis",
      analysisData.language?.primary || "unknown",
      analysisData.framework?.name || "none",
    ],
  });

  return entry.id;
}

/**
 * Stores SSG recommendation data in the memory system for learning and pattern recognition.
 *
 * Persists recommendation results including the chosen SSG, confidence scores, reasoning,
 * and alternatives. This data is used to improve future recommendations by learning from
 * successful patterns and user choices.
 *
 * @param analysisId - The unique identifier of the associated repository analysis
 * @param recommendation - The complete SSG recommendation results to store
 *
 * @returns Promise resolving to the unique memory entry ID
 *
 * @throws {Error} When memory system is not initialized
 * @throws {Error} When recommendation data cannot be stored
 * @throws {Error} When the associated analysis cannot be found
 *
 * @example
 * ```typescript
 * const recommendationId = await rememberRecommendation("analysis_123", {
 *   recommended: "docusaurus",
 *   confidence: 0.92,
 *   reasoning: ["React-based project", "Documentation focus"],
 *   alternatives: [
 *     { name: "hugo", score: 0.85, pros: ["Performance"], cons: ["Learning curve"] }
 *   ]
 * });
 * ```
 *
 * @since 1.0.0
 */
export async function rememberRecommendation(
  analysisId: string,
  recommendation: any,
): Promise<string> {
  const manager = await initializeMemory();

  const entry = await manager.remember("recommendation", recommendation, {
    ssg: recommendation.recommended,
    tags: ["recommendation", recommendation.recommended, "ssg"],
  });

  // Link to analysis
  const analysis = await manager.recall(analysisId);
  if (analysis) {
    await manager.update(entry.id, {
      metadata: {
        ...entry.metadata,
        projectId: analysis.metadata.projectId,
      },
    });
  }

  return entry.id;
}

/**
 * Stores deployment data in the memory system for success tracking and analytics.
 *
 * Persists deployment results including success status, timing, configuration used,
 * and any issues encountered. This data enables deployment analytics, success rate
 * tracking, and identification of deployment patterns for optimization.
 *
 * @param repository - The repository URL or identifier for the deployment
 * @param deploymentData - The complete deployment results and metadata
 *
 * @returns Promise resolving to the unique memory entry ID
 *
 * @throws {Error} When memory system is not initialized
 * @throws {Error} When deployment data cannot be stored
 *
 * @example
 * ```typescript
 * const deploymentId = await rememberDeployment("https://github.com/user/repo", {
 *   success: true,
 *   ssg: "docusaurus",
 *   deploymentTime: 180000,
 *   url: "https://user.github.io/repo",
 *   issues: [],
 *   configuration: { theme: "classic", plugins: ["search"] }
 * });
 * ```
 *
 * @since 1.0.0
 */
export async function rememberDeployment(
  repository: string,
  deploymentData: any,
): Promise<string> {
  const manager = await initializeMemory();

  manager.setContext({
    projectId: repository,
    repository,
  });

  const entry = await manager.remember("deployment", deploymentData, {
    repository,
    ssg: deploymentData.ssg,
    tags: [
      "deployment",
      deploymentData.status || "unknown",
      deploymentData.ssg,
    ],
  });

  return entry.id;
}

export async function rememberConfiguration(
  projectName: string,
  ssg: string,
  configData: any,
): Promise<string> {
  const manager = await initializeMemory();

  manager.setContext({
    projectId: projectName,
  });

  const entry = await manager.remember("configuration", configData, {
    ssg,
    tags: ["configuration", ssg, projectName],
  });

  return entry.id;
}

export async function recallProjectHistory(projectId: string): Promise<any> {
  const manager = await initializeMemory();

  const memories = await manager.search(
    { projectId },
    { sortBy: "timestamp", groupBy: "type" },
  );

  return {
    projectId,
    history: memories,
    insights: await getProjectInsights(projectId),
  };
}

/**
 * Retrieves intelligent insights about a project based on historical data and patterns.
 *
 * Analyzes stored project data to provide actionable insights including technology
 * trends, successful patterns, optimization opportunities, and recommendations for
 * improvement. Uses machine learning and pattern recognition to generate contextual
 * insights.
 *
 * @param projectId - The unique identifier of the project to analyze
 *
 * @returns Promise resolving to an array of insight strings
 *
 * @throws {Error} When memory system is not initialized
 * @throws {Error} When project data cannot be retrieved
 *
 * @example
 * ```typescript
 * const insights = await getProjectInsights("project_abc123");
 * console.log(insights);
 * // Output: [
 * //   "Consider upgrading to Docusaurus v3 for better performance",
 * //   "Similar projects show 95% success rate with current configuration",
 * //   "Documentation could benefit from additional API examples"
 * // ]
 * ```
 *
 * @since 1.0.0
 */
export async function getProjectInsights(projectId: string): Promise<string[]> {
  const manager = await initializeMemory();

  const memories = await manager.search({ projectId });
  const insights: string[] = [];

  // Find patterns in SSG choices
  const ssgMemories = memories.filter((m: any) => m.metadata.ssg);
  if (ssgMemories.length > 0) {
    const lastSSG = ssgMemories[ssgMemories.length - 1].metadata.ssg;
    insights.push(`Previously used ${lastSSG} for this project`);
  }

  // Find deployment patterns
  const deployments = memories.filter((m: any) => m.type === "deployment");
  if (deployments.length > 0) {
    const successful = deployments.filter(
      (d: any) => d.data.status === "success",
    ).length;
    const rate = ((successful / deployments.length) * 100).toFixed(0);
    insights.push(`Deployment success rate: ${rate}%`);
  }

  // Find recent activity
  const lastMemory = memories[memories.length - 1];
  if (lastMemory) {
    const daysAgo = Math.floor(
      (Date.now() - new Date(lastMemory.timestamp).getTime()) /
        (1000 * 60 * 60 * 24),
    );
    insights.push(`Last activity: ${daysAgo} days ago`);
  }

  return insights;
}

/**
 * Finds similar projects based on analysis data and historical patterns.
 *
 * Uses similarity algorithms to identify projects with comparable characteristics
 * including technology stack, project structure, documentation patterns, and
 * deployment history. Useful for providing relevant examples and recommendations.
 *
 * @param analysisData - The analysis data to use for similarity comparison
 * @param limit - Maximum number of similar projects to return (default: 5)
 *
 * @returns Promise resolving to an array of similar project data
 *
 * @throws {Error} When memory system is not initialized
 * @throws {Error} When similarity analysis fails
 *
 * @example
 * ```typescript
 * const similar = await getSimilarProjects(analysisData, 3);
 * console.log(similar.map(p => p.metadata.projectId));
 * // Output: ["project_xyz", "project_abc", "project_def"]
 * ```
 *
 * @since 1.0.0
 */
export async function getSimilarProjects(
  analysisData: any,
  limit: number = 5,
): Promise<any[]> {
  const manager = await initializeMemory();

  // Search for projects with similar characteristics
  const similarProjects: any[] = [];

  // Search by language
  if (analysisData.language?.primary) {
    const languageMatches = await manager.search(
      { tags: [analysisData.language.primary] },
      { sortBy: "timestamp" },
    );
    similarProjects.push(...languageMatches);
  }

  // Search by framework
  if (analysisData.framework?.name) {
    const frameworkMatches = await manager.search(
      { tags: [analysisData.framework.name] },
      { sortBy: "timestamp" },
    );
    similarProjects.push(...frameworkMatches);
  }

  // Deduplicate and return top matches
  const unique = Array.from(
    new Map(similarProjects.map((p) => [p.metadata.projectId, p])).values(),
  );

  return unique.slice(0, limit).map((project) => ({
    projectId: project.metadata.projectId,
    similarity: calculateSimilarity(analysisData, project.data),
    recommendation: project.metadata.ssg,
    timestamp: project.timestamp,
  }));
}

function calculateSimilarity(data1: any, data2: any): number {
  let score = 0;

  // Language match
  if (data1.language?.primary === data2.language?.primary) score += 0.3;

  // Framework match
  if (data1.framework?.name === data2.framework?.name) score += 0.3;

  // Size similarity
  if (Math.abs((data1.stats?.files || 0) - (data2.stats?.files || 0)) < 100)
    score += 0.2;

  // Documentation type match
  if (data1.documentation?.type === data2.documentation?.type) score += 0.2;

  return Math.min(score, 1.0);
}

export async function cleanupOldMemories(
  daysToKeep: number = 30,
): Promise<number> {
  const manager = await initializeMemory();
  const cutoffDate = new Date(Date.now() - daysToKeep * 24 * 60 * 60 * 1000);

  return await manager.cleanup(cutoffDate);
}

export async function exportMemories(
  format: "json" | "csv" = "json",
  projectId?: string,
): Promise<string> {
  const manager = await initializeMemory();
  return await manager.export(format, projectId);
}

export async function importMemories(
  data: string,
  format: "json" | "csv" = "json",
): Promise<number> {
  const manager = await initializeMemory();
  return await manager.import(data, format);
}

export async function getMemoryStatistics(): Promise<any> {
  const manager = await initializeMemory();
  return await manager.analyze();
}

export function getMemoryManager(): MemoryManager | null {
  return memoryManager;
}

export async function resetMemoryManager(storageDir?: string): Promise<void> {
  if (memoryManager) {
    await memoryManager.close();
  }
  memoryManager = null;
  if (storageDir) {
    await initializeMemory(storageDir);
  }
}

// Memory handler functions for MCP tools
export async function handleMemoryRecall(args: {
  query: string;
  type?: string;
  limit?: number;
}): Promise<any> {
  const manager = await initializeMemory();

  const searchOptions: any = {
    sortBy: "timestamp",
    limit: args.limit || 10,
  };

  if (args.type && args.type !== "all") {
    searchOptions.type = args.type;
  }

  const memories = await manager.search({}, searchOptions);

  return {
    query: args.query,
    type: args.type || "all",
    count: memories.length,
    memories: memories.map((m: any) => ({
      id: m.id,
      type: m.type,
      timestamp: m.timestamp,
      data: m.data,
      metadata: m.metadata,
    })),
  };
}

export async function handleMemoryIntelligentAnalysis(args: {
  projectPath: string;
  baseAnalysis: any;
}): Promise<any> {
  await initializeMemory();

  // Get project history and similar projects for enhanced analysis
  const projectId = args.baseAnalysis.projectId || args.projectPath;
  const history = await recallProjectHistory(projectId);
  const similarProjects = await getSimilarProjects(args.baseAnalysis);

  // Enhance analysis with memory insights
  const enhancedAnalysis = {
    ...args.baseAnalysis,
    memoryInsights: {
      projectHistory: history,
      similarProjects,
      patterns: await extractPatterns(args.baseAnalysis, history.history),
      recommendations: await generateRecommendations(
        args.baseAnalysis,
        similarProjects,
      ),
    },
  };

  // Remember this enhanced analysis
  await rememberAnalysis(args.projectPath, enhancedAnalysis);

  return enhancedAnalysis;
}

export async function handleMemoryEnhancedRecommendation(args: {
  projectPath: string;
  baseRecommendation: any;
  projectFeatures: any;
}): Promise<any> {
  await initializeMemory();

  // Get similar projects with same features
  const similarProjects = await getSimilarProjects(args.projectFeatures);

  // Analyze success patterns
  const successPatterns = await analyzeSuccessPatterns(similarProjects);

  // Enhanced recommendation with memory insights
  const enhancedRecommendation = {
    ...args.baseRecommendation,
    memoryEnhanced: {
      similarProjects,
      successPatterns,
      confidence: calculateConfidence(args.baseRecommendation, successPatterns),
      alternativeOptions: await getAlternativeOptions(
        args.baseRecommendation,
        successPatterns,
      ),
    },
  };

  return enhancedRecommendation;
}

// Helper functions for memory enhancement
async function extractPatterns(
  analysis: any,
  history: any[],
): Promise<string[]> {
  const patterns: string[] = [];

  // Analyze deployment patterns
  const deployments = history.filter((h: any) => h.type === "deployment");
  if (deployments.length > 0) {
    const successfulDeployments = deployments.filter(
      (d: any) => d.data.status === "success",
    );
    if (successfulDeployments.length > 0) {
      patterns.push("Previous successful deployments found");
    }
  }

  // Analyze SSG patterns
  const recommendations = history.filter(
    (h: any) => h.type === "recommendation",
  );
  if (recommendations.length > 0) {
    const lastSSG =
      recommendations[recommendations.length - 1].data.recommended;
    patterns.push(`Previously recommended SSG: ${lastSSG}`);
  }

  return patterns;
}

async function generateRecommendations(
  analysis: any,
  similarProjects: any[],
): Promise<string[]> {
  const recommendations: string[] = [];

  if (similarProjects.length > 0) {
    const popularSSG = findMostPopularSSG(similarProjects);
    if (popularSSG) {
      recommendations.push(
        `Consider ${popularSSG} based on similar project success`,
      );
    }
  }

  return recommendations;
}

async function analyzeSuccessPatterns(similarProjects: any[]): Promise<any> {
  const patterns = {
    ssgSuccess: {} as Record<string, number>,
    deploymentPatterns: [] as string[],
    commonFeatures: [] as string[],
  };

  // Analyze SSG success rates
  similarProjects.forEach((project: any) => {
    const ssg = project.recommendation;
    if (ssg) {
      patterns.ssgSuccess[ssg] = (patterns.ssgSuccess[ssg] || 0) + 1;
    }
  });

  return patterns;
}

function calculateConfidence(
  baseRecommendation: any,
  successPatterns: any,
): number {
  const recommended = baseRecommendation.recommended;
  const successCount = successPatterns.ssgSuccess[recommended] || 0;
  const totalProjects = Object.values(
    successPatterns.ssgSuccess as Record<string, number>,
  ).reduce((a: number, b: number) => a + b, 0);

  if (totalProjects === 0) return 0.5; // Default confidence

  return Math.min(successCount / totalProjects + 0.3, 1.0);
}

async function getAlternativeOptions(
  baseRecommendation: any,
  successPatterns: any,
): Promise<string[]> {
  const sorted = Object.entries(successPatterns.ssgSuccess)
    .sort(([, a]: [string, any], [, b]: [string, any]) => b - a)
    .map(([ssg]) => ssg);

  // Return top 2 alternatives different from the base recommendation
  return sorted
    .filter((ssg) => ssg !== baseRecommendation.recommended)
    .slice(0, 2);
}

function findMostPopularSSG(projects: any[]): string | null {
  const ssgCount: Record<string, number> = {};

  projects.forEach((project) => {
    const ssg = project.recommendation;
    if (ssg) {
      ssgCount[ssg] = (ssgCount[ssg] || 0) + 1;
    }
  });

  const sorted = Object.entries(ssgCount).sort(([, a], [, b]) => b - a);
  return sorted.length > 0 ? sorted[0][0] : null;
}

```

--------------------------------------------------------------------------------
/src/tools/check-documentation-links.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";
import { readFile, readdir, stat } from "fs/promises";
import { join, extname, resolve, relative, dirname } from "path";
import { MCPToolResponse } from "../types/api.js";

// Input validation schema
const LinkCheckInputSchema = z.object({
  documentation_path: z.string().default("./docs"),
  check_external_links: z.boolean().default(true),
  check_internal_links: z.boolean().default(true),
  check_anchor_links: z.boolean().default(true),
  timeout_ms: z.number().min(1000).max(30000).default(5000),
  max_concurrent_checks: z.number().min(1).max(20).default(5),
  allowed_domains: z.array(z.string()).default([]),
  ignore_patterns: z.array(z.string()).default([]),
  fail_on_broken_links: z.boolean().default(false),
  output_format: z.enum(["summary", "detailed", "json"]).default("detailed"),
});

type LinkCheckInput = z.infer<typeof LinkCheckInputSchema>;

interface LinkCheckResult {
  url: string;
  status: "valid" | "broken" | "warning" | "skipped";
  statusCode?: number;
  error?: string;
  responseTime?: number;
  sourceFile: string;
  lineNumber?: number;
  linkType: "internal" | "external" | "anchor" | "mailto" | "tel";
}

interface LinkCheckReport {
  summary: {
    totalLinks: number;
    validLinks: number;
    brokenLinks: number;
    warningLinks: number;
    skippedLinks: number;
    executionTime: number;
    filesScanned: number;
  };
  results: LinkCheckResult[];
  recommendations: string[];
  configuration: {
    checkExternalLinks: boolean;
    checkInternalLinks: boolean;
    checkAnchorLinks: boolean;
    timeoutMs: number;
    maxConcurrentChecks: number;
  };
}

export async function checkDocumentationLinks(
  input: Partial<LinkCheckInput>,
): Promise<MCPToolResponse<LinkCheckReport>> {
  const startTime = Date.now();

  try {
    // Validate input with defaults
    const validatedInput = LinkCheckInputSchema.parse(input);
    const {
      documentation_path,
      check_external_links,
      check_internal_links,
      check_anchor_links,
      timeout_ms,
      max_concurrent_checks,
      allowed_domains,
      ignore_patterns,
      fail_on_broken_links,
    } = validatedInput;

    // Scan documentation files
    const documentationFiles = await scanDocumentationFiles(documentation_path);

    if (documentationFiles.length === 0) {
      return {
        success: false,
        error: {
          code: "NO_DOCUMENTATION_FILES",
          message: "No documentation files found in the specified path",
          details: `Searched in: ${documentation_path}`,
          resolution:
            "Verify the documentation_path parameter points to a directory containing markdown files",
        },
        metadata: {
          toolVersion: "1.0.0",
          executionTime: Date.now() - startTime,
          timestamp: new Date().toISOString(),
        },
      };
    }

    // Extract all links from documentation files
    const allLinks = await extractLinksFromFiles(
      documentationFiles,
      documentation_path,
    );

    // Filter links based on configuration
    const filteredLinks = filterLinks(allLinks, {
      checkExternalLinks: check_external_links,
      checkInternalLinks: check_internal_links,
      checkAnchorLinks: check_anchor_links,
      ignorePatterns: ignore_patterns,
    });

    // Check links with concurrency control
    const linkResults = await checkLinksWithConcurrency(filteredLinks, {
      timeoutMs: timeout_ms,
      maxConcurrent: max_concurrent_checks,
      allowedDomains: allowed_domains,
      documentationPath: documentation_path,
    });

    // Generate report
    const report = generateLinkCheckReport(linkResults, {
      checkExternalLinks: check_external_links,
      checkInternalLinks: check_internal_links,
      checkAnchorLinks: check_anchor_links,
      timeoutMs: timeout_ms,
      maxConcurrentChecks: max_concurrent_checks,
      filesScanned: documentationFiles.length,
      executionTime: Date.now() - startTime,
    });

    // Check if we should fail on broken links
    if (fail_on_broken_links && report.summary.brokenLinks > 0) {
      return {
        success: false,
        error: {
          code: "BROKEN_LINKS_FOUND",
          message: `Found ${report.summary.brokenLinks} broken links`,
          details: `${report.summary.brokenLinks} out of ${report.summary.totalLinks} links are broken`,
          resolution:
            "Fix the broken links or set fail_on_broken_links to false",
        },
        data: report,
        metadata: {
          toolVersion: "1.0.0",
          executionTime: Date.now() - startTime,
          timestamp: new Date().toISOString(),
        },
      };
    }

    return {
      success: true,
      data: report,
      metadata: {
        toolVersion: "1.0.0",
        executionTime: Date.now() - startTime,
        timestamp: new Date().toISOString(),
      },
    };
  } catch (error) {
    return {
      success: false,
      error: {
        code: "LINK_CHECK_ERROR",
        message: "Failed to check documentation links",
        details:
          error instanceof Error ? error.message : "Unknown error occurred",
        resolution:
          "Check the documentation path and ensure files are accessible",
      },
      metadata: {
        toolVersion: "1.0.0",
        executionTime: Date.now() - startTime,
        timestamp: new Date().toISOString(),
      },
    };
  }
}

async function scanDocumentationFiles(basePath: string): Promise<string[]> {
  const files: string[] = [];

  async function scanDirectory(dirPath: string): Promise<void> {
    try {
      const entries = await readdir(dirPath);

      for (const entry of entries) {
        const fullPath = join(dirPath, entry);
        const stats = await stat(fullPath);

        if (stats.isDirectory()) {
          // Skip node_modules and hidden directories
          if (!entry.startsWith(".") && entry !== "node_modules") {
            await scanDirectory(fullPath);
          }
        } else if (stats.isFile()) {
          const ext = extname(entry).toLowerCase();
          if ([".md", ".mdx", ".markdown"].includes(ext)) {
            files.push(fullPath);
          }
        }
      }
    } catch (error) {
      // Skip directories we can't read
    }
  }

  await scanDirectory(basePath);
  return files;
}

async function extractLinksFromFiles(
  files: string[],
  basePath: string,
): Promise<
  Array<{
    url: string;
    sourceFile: string;
    lineNumber: number;
    linkType: "internal" | "external" | "anchor" | "mailto" | "tel";
  }>
> {
  const allLinks: Array<{
    url: string;
    sourceFile: string;
    lineNumber: number;
    linkType: "internal" | "external" | "anchor" | "mailto" | "tel";
  }> = [];

  // Regex patterns for different link types
  const markdownLinkRegex = /\[([^\]]*)\]\(([^)]+)\)/g;
  const htmlLinkRegex = /<a[^>]+href=["']([^"']+)["'][^>]*>/gi;
  const refLinkRegex = /\[([^\]]+)\]:\s*(.+)/g;

  for (const file of files) {
    try {
      const content = await readFile(file, "utf-8");
      const lines = content.split("\n");
      // Create proper relative file path
      const absoluteBasePath = resolve(basePath);
      const absoluteFilePath = resolve(file);
      const relativeFile = relative(absoluteBasePath, absoluteFilePath).replace(
        /\\/g,
        "/",
      );

      // Extract markdown links
      lines.forEach((line, index) => {
        let match;

        // Markdown links [text](url)
        while ((match = markdownLinkRegex.exec(line)) !== null) {
          const url = match[2].trim();
          if (url && !url.startsWith("#")) {
            // Skip empty and anchor-only links
            allLinks.push({
              url,
              sourceFile: relativeFile,
              lineNumber: index + 1,
              linkType: determineLinkType(url),
            });
          }
        }

        // HTML links
        while ((match = htmlLinkRegex.exec(line)) !== null) {
          const url = match[1].trim();
          if (url && !url.startsWith("#")) {
            allLinks.push({
              url,
              sourceFile: relativeFile,
              lineNumber: index + 1,
              linkType: determineLinkType(url),
            });
          }
        }

        // Reference links
        while ((match = refLinkRegex.exec(line)) !== null) {
          const url = match[2].trim();
          if (url && !url.startsWith("#")) {
            allLinks.push({
              url,
              sourceFile: relativeFile,
              lineNumber: index + 1,
              linkType: determineLinkType(url),
            });
          }
        }
      });
    } catch (error) {
      // Skip files we can't read
    }
  }

  return allLinks;
}

function determineLinkType(
  url: string,
): "internal" | "external" | "anchor" | "mailto" | "tel" {
  if (url.startsWith("mailto:")) return "mailto";
  if (url.startsWith("tel:")) return "tel";
  if (url.startsWith("#")) return "anchor";
  if (url.startsWith("http://") || url.startsWith("https://"))
    return "external";
  return "internal";
}

function filterLinks(
  links: Array<{
    url: string;
    sourceFile: string;
    lineNumber: number;
    linkType: "internal" | "external" | "anchor" | "mailto" | "tel";
  }>,
  options: {
    checkExternalLinks: boolean;
    checkInternalLinks: boolean;
    checkAnchorLinks: boolean;
    ignorePatterns: string[];
  },
) {
  return links.filter((link) => {
    // Check if link should be ignored based on patterns
    if (options.ignorePatterns.some((pattern) => link.url.includes(pattern))) {
      return false;
    }

    // Filter by link type
    switch (link.linkType) {
      case "external":
        return options.checkExternalLinks;
      case "internal":
        return options.checkInternalLinks;
      case "anchor":
        return options.checkAnchorLinks;
      case "mailto":
      case "tel":
        return false; // Skip these for now
      default:
        return true;
    }
  });
}

async function checkLinksWithConcurrency(
  links: Array<{
    url: string;
    sourceFile: string;
    lineNumber: number;
    linkType: "internal" | "external" | "anchor" | "mailto" | "tel";
  }>,
  options: {
    timeoutMs: number;
    maxConcurrent: number;
    allowedDomains: string[];
    documentationPath: string;
  },
): Promise<LinkCheckResult[]> {
  const results: LinkCheckResult[] = [];

  async function checkSingleLink(link: {
    url: string;
    sourceFile: string;
    lineNumber: number;
    linkType: "internal" | "external" | "anchor" | "mailto" | "tel";
  }): Promise<LinkCheckResult> {
    const startTime = Date.now();

    try {
      if (link.linkType === "internal") {
        return await checkInternalLink(link, options.documentationPath);
      } else if (link.linkType === "external") {
        return await checkExternalLink(
          link,
          options.timeoutMs,
          options.allowedDomains,
        );
      } else if (link.linkType === "anchor") {
        return await checkAnchorLink(link, options.documentationPath);
      }

      return {
        url: link.url,
        status: "skipped",
        sourceFile: link.sourceFile,
        lineNumber: link.lineNumber,
        linkType: link.linkType,
        responseTime: Date.now() - startTime,
      };
    } catch (error) {
      return {
        url: link.url,
        status: "broken",
        error: error instanceof Error ? error.message : "Unknown error",
        sourceFile: link.sourceFile,
        lineNumber: link.lineNumber,
        linkType: link.linkType,
        responseTime: Date.now() - startTime,
      };
    }
  }

  // Process links with concurrency control
  const chunks = [];
  for (let i = 0; i < links.length; i += options.maxConcurrent) {
    chunks.push(links.slice(i, i + options.maxConcurrent));
  }

  for (const chunk of chunks) {
    const chunkResults = await Promise.all(chunk.map(checkSingleLink));
    results.push(...chunkResults);
  }

  return results;
}

async function checkInternalLink(
  link: {
    url: string;
    sourceFile: string;
    lineNumber: number;
    linkType: "internal" | "external" | "anchor" | "mailto" | "tel";
  },
  documentationPath: string,
): Promise<LinkCheckResult> {
  const startTime = Date.now();

  try {
    let targetPath = link.url;

    // Remove anchor if present
    const [filePath] = targetPath.split("#");

    // Handle relative paths properly using Node.js path resolution
    const absoluteDocPath = resolve(documentationPath);
    const sourceFileAbsolutePath = resolve(absoluteDocPath, link.sourceFile);
    const sourceDir = dirname(sourceFileAbsolutePath);

    if (filePath.startsWith("./")) {
      // Current directory reference - resolve relative to source file directory
      targetPath = resolve(sourceDir, filePath.substring(2));
    } else if (filePath.startsWith("../")) {
      // Parent directory reference - resolve relative to source file directory
      targetPath = resolve(sourceDir, filePath);
    } else if (filePath.startsWith("/")) {
      // Absolute path from documentation root
      targetPath = resolve(absoluteDocPath, filePath.substring(1));
    } else {
      // Relative path - resolve relative to source file directory
      targetPath = resolve(sourceDir, filePath);
    }

    try {
      await stat(targetPath);
      return {
        url: link.url,
        status: "valid",
        sourceFile: link.sourceFile,
        lineNumber: link.lineNumber,
        linkType: link.linkType,
        responseTime: Date.now() - startTime,
      };
    } catch {
      return {
        url: link.url,
        status: "broken",
        error: "File not found",
        sourceFile: link.sourceFile,
        lineNumber: link.lineNumber,
        linkType: link.linkType,
        responseTime: Date.now() - startTime,
      };
    }
  } catch (error) {
    return {
      url: link.url,
      status: "broken",
      error: error instanceof Error ? error.message : "Unknown error",
      sourceFile: link.sourceFile,
      lineNumber: link.lineNumber,
      linkType: link.linkType,
      responseTime: Date.now() - startTime,
    };
  }
}

async function checkExternalLink(
  link: {
    url: string;
    sourceFile: string;
    lineNumber: number;
    linkType: "internal" | "external" | "anchor" | "mailto" | "tel";
  },
  timeoutMs: number,
  allowedDomains: string[],
): Promise<LinkCheckResult> {
  const startTime = Date.now();

  try {
    // Check if domain is in allowed list (if specified)
    if (allowedDomains.length > 0) {
      const url = new URL(link.url);
      const isAllowed = allowedDomains.some(
        (domain) =>
          url.hostname === domain || url.hostname.endsWith("." + domain),
      );

      if (!isAllowed) {
        return {
          url: link.url,
          status: "skipped",
          error: "Domain not in allowed list",
          sourceFile: link.sourceFile,
          lineNumber: link.lineNumber,
          linkType: link.linkType,
          responseTime: Date.now() - startTime,
        };
      }
    }

    // Simple HEAD request to check if URL is accessible
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeoutMs);

    try {
      const response = await fetch(link.url, {
        method: "HEAD",
        signal: controller.signal,
        headers: {
          "User-Agent": "DocuMCP Link Checker 1.0",
        },
      });

      clearTimeout(timeoutId);

      if (response.ok) {
        return {
          url: link.url,
          status: "valid",
          statusCode: response.status,
          sourceFile: link.sourceFile,
          lineNumber: link.lineNumber,
          linkType: link.linkType,
          responseTime: Date.now() - startTime,
        };
      } else {
        return {
          url: link.url,
          status: "broken",
          statusCode: response.status,
          error: `HTTP ${response.status}: ${response.statusText}`,
          sourceFile: link.sourceFile,
          lineNumber: link.lineNumber,
          linkType: link.linkType,
          responseTime: Date.now() - startTime,
        };
      }
    } catch (fetchError) {
      clearTimeout(timeoutId);

      if (fetchError instanceof Error && fetchError.name === "AbortError") {
        return {
          url: link.url,
          status: "warning",
          error: "Request timeout",
          sourceFile: link.sourceFile,
          lineNumber: link.lineNumber,
          linkType: link.linkType,
          responseTime: Date.now() - startTime,
        };
      }

      throw fetchError;
    }
  } catch (error) {
    return {
      url: link.url,
      status: "broken",
      error: error instanceof Error ? error.message : "Unknown error",
      sourceFile: link.sourceFile,
      lineNumber: link.lineNumber,
      linkType: link.linkType,
      responseTime: Date.now() - startTime,
    };
  }
}

async function checkAnchorLink(
  link: {
    url: string;
    sourceFile: string;
    lineNumber: number;
    linkType: "internal" | "external" | "anchor" | "mailto" | "tel";
  },
  _documentationPath: string,
): Promise<LinkCheckResult> {
  const startTime = Date.now();

  // For now, just mark anchor links as valid
  // In a more sophisticated implementation, we would parse the target file
  // and check if the anchor exists
  return {
    url: link.url,
    status: "valid",
    sourceFile: link.sourceFile,
    lineNumber: link.lineNumber,
    linkType: link.linkType,
    responseTime: Date.now() - startTime,
  };
}

function generateLinkCheckReport(
  results: LinkCheckResult[],
  config: {
    checkExternalLinks: boolean;
    checkInternalLinks: boolean;
    checkAnchorLinks: boolean;
    timeoutMs: number;
    maxConcurrentChecks: number;
    filesScanned: number;
    executionTime: number;
  },
): LinkCheckReport {
  const summary = {
    totalLinks: results.length,
    validLinks: results.filter((r) => r.status === "valid").length,
    brokenLinks: results.filter((r) => r.status === "broken").length,
    warningLinks: results.filter((r) => r.status === "warning").length,
    skippedLinks: results.filter((r) => r.status === "skipped").length,
    executionTime: config.executionTime,
    filesScanned: config.filesScanned,
  };

  const recommendations: string[] = [];

  if (summary.brokenLinks > 0) {
    recommendations.push(
      `🔴 Fix ${summary.brokenLinks} broken links to improve documentation quality`,
    );
  }

  if (summary.warningLinks > 0) {
    recommendations.push(
      `🟡 Review ${summary.warningLinks} warning links that may need attention`,
    );
  }

  if (summary.validLinks === summary.totalLinks) {
    recommendations.push(
      "✅ All links are valid - excellent documentation quality!",
    );
  }

  if (summary.totalLinks > 100) {
    recommendations.push(
      "📊 Consider implementing automated link checking in CI/CD pipeline",
    );
  }

  return {
    summary,
    results,
    recommendations,
    configuration: {
      checkExternalLinks: config.checkExternalLinks,
      checkInternalLinks: config.checkInternalLinks,
      checkAnchorLinks: config.checkAnchorLinks,
      timeoutMs: config.timeoutMs,
      maxConcurrentChecks: config.maxConcurrentChecks,
    },
  };
}

```

--------------------------------------------------------------------------------
/tests/tools/test-local-deployment.test.ts:
--------------------------------------------------------------------------------

```typescript
import { testLocalDeployment } from "../../src/tools/test-local-deployment.js";
import * as childProcess from "child_process";
import * as fs from "fs";

// Create simpler mocking approach

describe("testLocalDeployment", () => {
  const testRepoPath = process.cwd();

  afterEach(() => {
    jest.restoreAllMocks();
  });

  describe("Input validation", () => {
    it("should handle invalid SSG parameter", async () => {
      await expect(
        testLocalDeployment({
          repositoryPath: "/test/path",
          ssg: "invalid" as any,
        }),
      ).rejects.toThrow();
    });

    it("should handle missing required parameters", async () => {
      await expect(
        testLocalDeployment({
          ssg: "docusaurus",
        } as any),
      ).rejects.toThrow();
    });

    it("should handle unsupported SSG gracefully", async () => {
      // This should throw a ZodError due to input validation
      await expect(
        testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: "gatsby" as any,
        }),
      ).rejects.toThrow("Invalid enum value");
    });
  });

  describe("Basic functionality", () => {
    it("should return proper response structure", async () => {
      const result = await testLocalDeployment({
        repositoryPath: testRepoPath,
        ssg: "hugo",
        skipBuild: true,
      });

      expect(result.content).toBeDefined();
      expect(Array.isArray(result.content)).toBe(true);
      expect(result.content.length).toBeGreaterThan(0);
      expect(() => JSON.parse(result.content[0].text)).not.toThrow();
    });

    it("should use default port when not specified", async () => {
      const result = await testLocalDeployment({
        repositoryPath: testRepoPath,
        ssg: "hugo",
        skipBuild: true,
      });

      const parsedResult = JSON.parse(result.content[0].text);
      expect(parsedResult.port).toBe(3000);
    });

    it("should use custom port when specified", async () => {
      const result = await testLocalDeployment({
        repositoryPath: testRepoPath,
        ssg: "hugo",
        port: 4000,
        skipBuild: true,
      });

      const parsedResult = JSON.parse(result.content[0].text);
      expect(parsedResult.port).toBe(4000);
    });

    it("should use custom timeout when specified", async () => {
      const result = await testLocalDeployment({
        repositoryPath: testRepoPath,
        ssg: "hugo",
        timeout: 120,
        skipBuild: true,
      });

      const parsedResult = JSON.parse(result.content[0].text);
      expect(parsedResult.buildSuccess).toBeDefined();
    });
  });

  describe("SSG support", () => {
    it("should handle all supported SSG types", async () => {
      const ssgs = ["jekyll", "hugo", "docusaurus", "mkdocs", "eleventy"];

      for (const ssg of ssgs) {
        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: ssg as any,
          skipBuild: true,
        });

        const parsedResult = JSON.parse(result.content[0].text);
        expect(parsedResult.ssg).toBe(ssg);
        expect(parsedResult.buildSuccess).toBeDefined();
      }
    });

    it("should generate test script for all SSG types", async () => {
      const ssgs = ["jekyll", "hugo", "docusaurus", "mkdocs", "eleventy"];

      for (const ssg of ssgs) {
        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: ssg as any,
          skipBuild: true,
          port: 4000,
        });

        const parsedResult = JSON.parse(result.content[0].text);
        expect(parsedResult.testScript).toContain(
          `# Local Deployment Test Script for ${ssg}`,
        );
        expect(parsedResult.testScript).toContain("http://localhost:4000");
      }
    });

    it("should include install commands for Node.js-based SSGs", async () => {
      const result = await testLocalDeployment({
        repositoryPath: testRepoPath,
        ssg: "docusaurus",
        skipBuild: true,
      });

      const parsedResult = JSON.parse(result.content[0].text);
      expect(parsedResult.testScript).toContain("npm install");
    });

    it("should not include install commands for non-Node.js SSGs", async () => {
      const result = await testLocalDeployment({
        repositoryPath: testRepoPath,
        ssg: "hugo",
        skipBuild: true,
      });

      const parsedResult = JSON.parse(result.content[0].text);
      expect(parsedResult.testScript).not.toContain("npm install");
    });
  });

  describe("Configuration handling", () => {
    it("should provide recommendations when configuration is missing", async () => {
      const result = await testLocalDeployment({
        repositoryPath: testRepoPath,
        ssg: "jekyll", // Jekyll config unlikely to exist in this repo
        skipBuild: true,
      });

      const parsedResult = JSON.parse(result.content[0].text);
      expect(parsedResult.recommendations).toEqual(
        expect.arrayContaining([
          expect.stringContaining("Missing configuration file"),
        ]),
      );
    });

    it("should provide next steps for missing configuration", async () => {
      const result = await testLocalDeployment({
        repositoryPath: testRepoPath,
        ssg: "jekyll",
        skipBuild: true,
      });

      const parsedResult = JSON.parse(result.content[0].text);
      expect(parsedResult.nextSteps).toEqual(
        expect.arrayContaining([expect.stringContaining("generate_config")]),
      );
    });
  });

  describe("Error handling", () => {
    it("should handle general errors gracefully", async () => {
      jest.spyOn(process, "chdir").mockImplementation(() => {
        throw new Error("Permission denied");
      });

      const result = await testLocalDeployment({
        repositoryPath: testRepoPath,
        ssg: "hugo",
      });

      // The tool returns an error response structure instead of throwing
      const parsedResult = JSON.parse(result.content[0].text);
      expect(parsedResult.success).toBe(false);
      expect(parsedResult.error.code).toBe("LOCAL_TEST_FAILED");
      expect(parsedResult.error.message).toContain("Permission denied");
    });

    it("should handle non-existent repository path", async () => {
      const result = await testLocalDeployment({
        repositoryPath: "/non/existent/path",
        ssg: "hugo",
        skipBuild: true,
      });

      const parsedResult = JSON.parse(result.content[0].text);
      // Should still work with skipBuild, but may have warnings
      expect(parsedResult).toBeDefined();
      expect(result.content).toBeDefined();
    });
  });

  describe("Response structure validation", () => {
    it("should include all required response fields", async () => {
      const result = await testLocalDeployment({
        repositoryPath: testRepoPath,
        ssg: "hugo",
        skipBuild: true,
      });

      const parsedResult = JSON.parse(result.content[0].text);
      expect(parsedResult).toHaveProperty("buildSuccess");
      expect(parsedResult).toHaveProperty("ssg");
      expect(parsedResult).toHaveProperty("port");
      expect(parsedResult).toHaveProperty("testScript");
      expect(parsedResult).toHaveProperty("recommendations");
      expect(parsedResult).toHaveProperty("nextSteps");
    });

    it("should include tool recommendations in next steps", async () => {
      const result = await testLocalDeployment({
        repositoryPath: testRepoPath,
        ssg: "hugo",
        skipBuild: true,
      });

      const parsedResult = JSON.parse(result.content[0].text);
      expect(Array.isArray(parsedResult.nextSteps)).toBe(true);
      expect(parsedResult.nextSteps.length).toBeGreaterThan(0);
    });

    it("should validate test script content structure", async () => {
      const result = await testLocalDeployment({
        repositoryPath: testRepoPath,
        ssg: "hugo",
        port: 8080,
        skipBuild: true,
      });

      const parsedResult = JSON.parse(result.content[0].text);
      const testScript = parsedResult.testScript;

      expect(testScript).toContain("# Local Deployment Test Script for hugo");
      expect(testScript).toContain("http://localhost:8080");
      expect(testScript).toContain("hugo server");
      expect(testScript).toContain("--port 8080");
    });

    it("should handle different timeout values", async () => {
      const timeouts = [30, 60, 120, 300];

      for (const timeout of timeouts) {
        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: "hugo",
          timeout,
          skipBuild: true,
        });

        const parsedResult = JSON.parse(result.content[0].text);
        expect(parsedResult.buildSuccess).toBeDefined();
        // Timeout is not directly returned in response, but test should pass
      }
    });

    it("should provide appropriate recommendations for each SSG type", async () => {
      const ssgConfigs = {
        jekyll: "_config.yml",
        hugo: "config.toml",
        docusaurus: "docusaurus.config.js",
        mkdocs: "mkdocs.yml",
        eleventy: ".eleventy.js",
      };

      for (const [ssg, configFile] of Object.entries(ssgConfigs)) {
        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: ssg as any,
          skipBuild: true,
        });

        const parsedResult = JSON.parse(result.content[0].text);
        expect(parsedResult.recommendations).toEqual(
          expect.arrayContaining([expect.stringContaining(configFile)]),
        );
      }
    });

    it("should include comprehensive next steps", async () => {
      const result = await testLocalDeployment({
        repositoryPath: testRepoPath,
        ssg: "jekyll", // Missing config will trigger recommendations
        skipBuild: true,
      });

      const parsedResult = JSON.parse(result.content[0].text);
      const nextSteps = parsedResult.nextSteps;

      expect(Array.isArray(nextSteps)).toBe(true);
      expect(nextSteps.length).toBeGreaterThan(0);

      // Should include generate_config step for missing config
      expect(nextSteps).toEqual(
        expect.arrayContaining([expect.stringContaining("generate_config")]),
      );
    });

    it("should handle edge case with empty repository path", async () => {
      const result = await testLocalDeployment({
        repositoryPath: "",
        ssg: "hugo",
        skipBuild: true,
      });

      const parsedResult = JSON.parse(result.content[0].text);
      // Should handle gracefully and provide recommendations
      expect(parsedResult).toBeDefined();
      expect(result.content).toBeDefined();
    });

    it("should validate port range handling", async () => {
      const ports = [1000, 3000, 8080, 9000, 65535];

      for (const port of ports) {
        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: "hugo",
          port,
          skipBuild: true,
        });

        const parsedResult = JSON.parse(result.content[0].text);
        expect(parsedResult.port).toBe(port);
        expect(parsedResult.testScript).toContain(`http://localhost:${port}`);
      }
    });
  });

  describe("Advanced coverage scenarios", () => {
    beforeEach(() => {
      jest.spyOn(process, "chdir").mockImplementation(() => {});
    });

    afterEach(() => {
      jest.restoreAllMocks();
    });

    describe("Configuration file scenarios", () => {
      it("should detect existing configuration file for hugo", async () => {
        // Mock fs.access to succeed for hugo config file
        const mockFsAccess = jest
          .spyOn(fs.promises, "access")
          .mockResolvedValueOnce(undefined);

        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: "hugo",
          skipBuild: true,
        });

        const parsedResult = JSON.parse(result.content[0].text);
        // Should not recommend missing config since file exists
        expect(parsedResult.recommendations).not.toEqual(
          expect.arrayContaining([
            expect.stringContaining("Missing configuration file"),
          ]),
        );

        mockFsAccess.mockRestore();
      });

      it("should detect existing configuration file for jekyll", async () => {
        // Mock fs.access to succeed for jekyll config file
        const mockFsAccess = jest
          .spyOn(fs.promises, "access")
          .mockResolvedValueOnce(undefined);

        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: "jekyll",
          skipBuild: true,
        });

        const parsedResult = JSON.parse(result.content[0].text);
        // Should not recommend missing config since file exists
        expect(parsedResult.recommendations).not.toEqual(
          expect.arrayContaining([
            expect.stringContaining("Missing configuration file"),
          ]),
        );

        mockFsAccess.mockRestore();
      });

      it("should detect existing configuration file for docusaurus", async () => {
        // Mock fs.access to succeed for docusaurus config file
        const mockFsAccess = jest
          .spyOn(fs.promises, "access")
          .mockResolvedValueOnce(undefined);

        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: "docusaurus",
          skipBuild: true,
        });

        const parsedResult = JSON.parse(result.content[0].text);
        // Should not recommend missing config since file exists
        expect(parsedResult.recommendations).not.toEqual(
          expect.arrayContaining([
            expect.stringContaining("Missing configuration file"),
          ]),
        );

        mockFsAccess.mockRestore();
      });

      it("should detect existing configuration file for mkdocs", async () => {
        // Mock fs.access to succeed for mkdocs config file
        const mockFsAccess = jest
          .spyOn(fs.promises, "access")
          .mockResolvedValueOnce(undefined);

        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: "mkdocs",
          skipBuild: true,
        });

        const parsedResult = JSON.parse(result.content[0].text);
        // Should not recommend missing config since file exists
        expect(parsedResult.recommendations).not.toEqual(
          expect.arrayContaining([
            expect.stringContaining("Missing configuration file"),
          ]),
        );

        mockFsAccess.mockRestore();
      });

      it("should detect existing configuration file for eleventy", async () => {
        // Mock fs.access to succeed for eleventy config file
        const mockFsAccess = jest
          .spyOn(fs.promises, "access")
          .mockResolvedValueOnce(undefined);

        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: "eleventy",
          skipBuild: true,
        });

        const parsedResult = JSON.parse(result.content[0].text);
        // Should not recommend missing config since file exists
        expect(parsedResult.recommendations).not.toEqual(
          expect.arrayContaining([
            expect.stringContaining("Missing configuration file"),
          ]),
        );

        mockFsAccess.mockRestore();
      });
    });

    describe("Build scenarios with actual executions", () => {
      it("should handle successful build for eleventy without skipBuild", async () => {
        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: "eleventy",
          skipBuild: false,
          timeout: 10, // Short timeout to avoid long waits
        });

        const parsedResult = JSON.parse(result.content[0].text);
        expect(parsedResult.ssg).toBe("eleventy");
        expect(parsedResult.buildSuccess).toBeDefined();
        expect(parsedResult.testScript).toContain("npx @11ty/eleventy");
      });

      it("should handle successful build for mkdocs without skipBuild", async () => {
        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: "mkdocs",
          skipBuild: false,
          timeout: 10, // Short timeout to avoid long waits
        });

        const parsedResult = JSON.parse(result.content[0].text);
        expect(parsedResult.ssg).toBe("mkdocs");
        expect(parsedResult.buildSuccess).toBeDefined();
        expect(parsedResult.testScript).toContain("mkdocs build");
      });

      it("should exercise server start paths with short timeout", async () => {
        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: "hugo",
          skipBuild: true,
          timeout: 5, // Very short timeout to trigger timeout path
        });

        const parsedResult = JSON.parse(result.content[0].text);
        expect(parsedResult.ssg).toBe("hugo");
        expect(parsedResult.serverStarted).toBeDefined();
        // localUrl may be undefined if server doesn't start quickly enough
        expect(
          typeof parsedResult.localUrl === "string" ||
            parsedResult.localUrl === undefined,
        ).toBe(true);
      });

      it("should test port customization in serve commands", async () => {
        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: "jekyll",
          port: 4000,
          skipBuild: true,
        });

        const parsedResult = JSON.parse(result.content[0].text);
        expect(parsedResult.testScript).toContain("--port 4000");
        expect(parsedResult.testScript).toContain("http://localhost:4000");
      });

      it("should test mkdocs serve command with custom port", async () => {
        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: "mkdocs",
          port: 8000,
          skipBuild: true,
        });

        const parsedResult = JSON.parse(result.content[0].text);
        expect(parsedResult.testScript).toContain("--dev-addr localhost:8000");
        expect(parsedResult.testScript).toContain("http://localhost:8000");
      });

      it("should test eleventy serve command with custom port", async () => {
        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: "eleventy",
          port: 3001,
          skipBuild: true,
        });

        const parsedResult = JSON.parse(result.content[0].text);
        expect(parsedResult.testScript).toContain("--port 3001");
        expect(parsedResult.testScript).toContain("http://localhost:3001");
      });

      it("should provide correct next steps recommendations", async () => {
        const result = await testLocalDeployment({
          repositoryPath: testRepoPath,
          ssg: "docusaurus",
          skipBuild: true,
        });

        const parsedResult = JSON.parse(result.content[0].text);
        expect(parsedResult.nextSteps).toBeDefined();
        expect(Array.isArray(parsedResult.nextSteps)).toBe(true);
        expect(parsedResult.nextSteps.length).toBeGreaterThan(0);
      });
    });
  });
});

```

--------------------------------------------------------------------------------
/tests/tools/recommend-ssg.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Test suite for SSG Recommendation Tool
 */

import { jest } from "@jest/globals";
import { recommendSSG } from "../../src/tools/recommend-ssg.js";

// Mock the memory and KG integration
jest.mock("../../src/memory/kg-integration.js", () => ({
  getMemoryManager: jest.fn(),
  getKnowledgeGraph: jest.fn(),
  getUserPreferenceManager: jest.fn(),
  getProjectContext: jest.fn(),
  saveKnowledgeGraph: (jest.fn() as any).mockResolvedValue(undefined),
}));

describe("recommendSSG", () => {
  let mockManager: any;
  let mockKG: any;
  let mockPreferenceManager: any;

  beforeEach(() => {
    mockManager = {
      recall: jest.fn() as any,
    } as any;

    mockKG = {
      findNode: (jest.fn() as any).mockResolvedValue(null),
      findNodes: (jest.fn() as any).mockResolvedValue([]),
      findEdges: (jest.fn() as any).mockResolvedValue([]),
      getAllNodes: (jest.fn() as any).mockResolvedValue([]),
      addNode: (jest.fn() as any).mockImplementation((node: any) => node),
      addEdge: (jest.fn() as any).mockReturnValue(undefined),
    } as any;

    mockPreferenceManager = {
      getPreference: (jest.fn() as any).mockResolvedValue(null),
    } as any;

    const {
      getMemoryManager,
      getKnowledgeGraph,
      getUserPreferenceManager,
      getProjectContext,
    } = require("../../src/memory/kg-integration.js");

    getMemoryManager.mockResolvedValue(mockManager);
    getKnowledgeGraph.mockResolvedValue(mockKG);
    getUserPreferenceManager.mockResolvedValue(mockPreferenceManager);
    getProjectContext.mockResolvedValue({
      previousAnalyses: 0,
      lastAnalyzed: null,
      knownTechnologies: [],
      similarProjects: [],
    });
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

  describe("Input Validation", () => {
    it("should validate required analysisId parameter", async () => {
      await expect(recommendSSG({})).rejects.toThrow();
    });

    it("should validate analysisId as string", async () => {
      await expect(recommendSSG({ analysisId: 123 })).rejects.toThrow();
    });

    it("should accept valid preferences", async () => {
      mockManager.recall.mockResolvedValue(null);

      const result = await recommendSSG({
        analysisId: "test-id",
        preferences: {
          priority: "simplicity",
          ecosystem: "javascript",
        },
      });

      expect(result.content).toBeDefined();
    });

    it("should reject invalid priority preference", async () => {
      await expect(
        recommendSSG({
          analysisId: "test-id",
          preferences: { priority: "invalid" },
        }),
      ).rejects.toThrow();
    });

    it("should reject invalid ecosystem preference", async () => {
      await expect(
        recommendSSG({
          analysisId: "test-id",
          preferences: { ecosystem: "invalid" },
        }),
      ).rejects.toThrow();
    });
  });

  describe("Memory Integration", () => {
    it("should retrieve analysis from memory when available", async () => {
      const mockAnalysis = {
        data: {
          content: [
            {
              type: "text",
              text: JSON.stringify({
                repository: { language: "JavaScript" },
                complexity: "low",
                size: "small",
              }),
            },
          ],
        },
      };

      mockManager.recall.mockResolvedValue(mockAnalysis);

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      expect(mockManager.recall).toHaveBeenCalledWith("test-id");
      expect(result.content).toBeDefined();
    });

    it("should handle missing analysis gracefully", async () => {
      mockManager.recall.mockResolvedValue(null);

      const result = await recommendSSG({
        analysisId: "non-existent-id",
      });

      expect(result.content).toBeDefined();
      expect(result.content[0].type).toBe("text");
    });

    it("should handle analysis with direct data structure", async () => {
      const mockAnalysis = {
        data: {
          repository: { language: "Python" },
          complexity: "medium",
        },
      };

      mockManager.recall.mockResolvedValue(mockAnalysis);

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      expect(result.content).toBeDefined();
    });

    it("should handle corrupted analysis data", async () => {
      const mockAnalysis = {
        data: {
          content: [
            {
              type: "text",
              text: "invalid json",
            },
          ],
        },
      };

      mockManager.recall.mockResolvedValue(mockAnalysis);

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      expect(result.content).toBeDefined();
    });
  });

  describe("SSG Recommendations", () => {
    it("should recommend Jekyll for Ruby projects", async () => {
      const mockAnalysis = {
        data: {
          dependencies: {
            ecosystem: "ruby",
          },
          complexity: "low",
        },
      };

      mockManager.recall.mockResolvedValue(mockAnalysis);

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(recommendation.recommended).toBe("jekyll");
    });

    it("should recommend Hugo for Go projects", async () => {
      const mockAnalysis = {
        data: {
          dependencies: {
            ecosystem: "go",
          },
          complexity: "medium",
        },
      };

      mockManager.recall.mockResolvedValue(mockAnalysis);

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(recommendation.recommended).toBe("hugo");
    });

    it("should recommend Docusaurus for JavaScript projects", async () => {
      const mockAnalysis = {
        data: {
          dependencies: {
            ecosystem: "javascript",
          },
          documentation: {
            estimatedComplexity: "complex",
          },
          recommendations: {
            teamSize: "large",
          },
        },
      };

      mockManager.recall.mockResolvedValue(mockAnalysis);

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(recommendation.recommended).toBe("docusaurus");
    });

    it("should recommend MkDocs for Python projects", async () => {
      const mockAnalysis = {
        data: {
          dependencies: {
            ecosystem: "python",
          },
          complexity: "medium",
        },
      };

      mockManager.recall.mockResolvedValue(mockAnalysis);

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(recommendation.recommended).toBe("mkdocs");
    });

    it("should recommend Eleventy for simple JavaScript projects with simplicity priority", async () => {
      const mockAnalysis = {
        data: {
          dependencies: {
            ecosystem: "javascript",
          },
          documentation: {
            estimatedComplexity: "simple",
          },
          recommendations: {
            teamSize: "small",
          },
        },
      };

      mockManager.recall.mockResolvedValue(mockAnalysis);

      const result = await recommendSSG({
        analysisId: "test-id",
        preferences: { priority: "simplicity" },
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(recommendation.recommended).toBe("eleventy");
    });
  });

  describe("Preference-based Recommendations", () => {
    it("should prioritize simplicity when requested", async () => {
      mockManager.recall.mockResolvedValue(null);

      const result = await recommendSSG({
        analysisId: "test-id",
        preferences: { priority: "simplicity" },
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(["jekyll", "eleventy"]).toContain(recommendation.recommended);
    });

    it("should consider ecosystem preferences", async () => {
      mockManager.recall.mockResolvedValue(null);

      const result = await recommendSSG({
        analysisId: "test-id",
        preferences: { ecosystem: "javascript" },
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(["docusaurus", "eleventy"]).toContain(recommendation.recommended);
    });

    it("should handle performance preference with fallback", async () => {
      mockManager.recall.mockResolvedValue(null);

      const result = await recommendSSG({
        analysisId: "test-id",
        preferences: { priority: "performance" },
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(recommendation.recommended).toBe("docusaurus");
    });

    it("should handle features preference with fallback", async () => {
      mockManager.recall.mockResolvedValue(null);

      const result = await recommendSSG({
        analysisId: "test-id",
        preferences: { priority: "features" },
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(recommendation.recommended).toBe("docusaurus");
    });
  });

  describe("Scoring and Alternatives", () => {
    it("should provide confidence scores", async () => {
      mockManager.recall.mockResolvedValue({
        data: {
          dependencies: {
            ecosystem: "javascript",
          },
          complexity: "medium",
        },
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(recommendation.confidence).toBeGreaterThan(0);
      expect(recommendation.confidence).toBeLessThanOrEqual(1);
    });

    it("should provide alternative recommendations", async () => {
      mockManager.recall.mockResolvedValue({
        data: {
          dependencies: {
            ecosystem: "javascript",
          },
          complexity: "medium",
        },
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(recommendation.alternatives).toBeDefined();
      expect(Array.isArray(recommendation.alternatives)).toBe(true);
      expect(recommendation.alternatives.length).toBeGreaterThan(0);
    });

    it("should include pros and cons for alternatives", async () => {
      mockManager.recall.mockResolvedValue({
        data: {
          dependencies: {
            ecosystem: "python",
          },
          complexity: "medium",
        },
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      const recommendation = JSON.parse(result.content[0].text);
      const alternative = recommendation.alternatives[0];

      expect(alternative.name).toBeDefined();
      expect(alternative.score).toBeDefined();
      expect(Array.isArray(alternative.pros)).toBe(true);
      expect(Array.isArray(alternative.cons)).toBe(true);
    });

    it("should sort alternatives by score", async () => {
      mockManager.recall.mockResolvedValue({
        data: {
          dependencies: {
            ecosystem: "javascript",
          },
          documentation: {
            estimatedComplexity: "complex",
          },
        },
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      const recommendation = JSON.parse(result.content[0].text);
      const scores = recommendation.alternatives.map((alt: any) => alt.score);

      for (let i = 1; i < scores.length; i++) {
        expect(scores[i]).toBeLessThanOrEqual(scores[i - 1]);
      }
    });
  });

  describe("Complex Project Analysis", () => {
    it("should handle projects with React packages", async () => {
      mockManager.recall.mockResolvedValue({
        data: {
          dependencies: {
            ecosystem: "javascript",
            packages: ["react", "next"],
          },
        },
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(recommendation.recommended).toBe("docusaurus");
      expect(recommendation.reasoning.length).toBeGreaterThan(0);
    });

    it("should consider project size in recommendations", async () => {
      mockManager.recall.mockResolvedValue({
        data: {
          dependencies: {
            ecosystem: "javascript",
          },
          structure: {
            totalFiles: 150, // Large project
          },
          documentation: {
            estimatedComplexity: "complex",
            hasReadme: true,
            hasDocs: true,
          },
        },
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(recommendation.confidence).toBeGreaterThan(0.85); // Should have higher confidence with more data
    });

    it("should handle missing ecosystem information", async () => {
      mockManager.recall.mockResolvedValue({
        data: {
          dependencies: {
            ecosystem: "unknown",
          },
          documentation: {
            estimatedComplexity: "moderate",
          },
        },
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(recommendation.recommended).toBeDefined();
    });

    it("should consider existing documentation structure", async () => {
      mockManager.recall.mockResolvedValue({
        data: {
          dependencies: {
            ecosystem: "javascript",
          },
          documentation: {
            hasReadme: true,
            hasDocs: true,
            estimatedComplexity: "moderate",
          },
        },
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(recommendation.confidence).toBeGreaterThan(0.85); // Higher confidence with documentation
    });
  });

  describe("Memory Error Handling", () => {
    it("should handle memory initialization failure", async () => {
      const {
        getMemoryManager,
      } = require("../../src/memory/kg-integration.js");
      getMemoryManager.mockRejectedValue(new Error("Memory failed"));

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      expect(result.content).toBeDefined();
      expect(result.content[0].type).toBe("text");

      // Reset the mock
      getMemoryManager.mockResolvedValue(mockManager);
    });

    it("should handle memory recall failure", async () => {
      mockManager.recall.mockRejectedValue(new Error("Recall failed"));

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      expect(result.content).toBeDefined();
    });

    it("should handle corrupted memory data", async () => {
      mockManager.recall.mockResolvedValue({
        data: {
          content: [
            {
              type: "text",
              text: '{"invalid": json}',
            },
          ],
        },
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      expect(result.content).toBeDefined();
    });
  });

  describe("Performance and Timing", () => {
    it("should complete recommendation in reasonable time", async () => {
      mockManager.recall.mockResolvedValue({
        data: {
          repository: { language: "JavaScript" },
          complexity: "medium",
        },
      });

      const start = Date.now();
      await recommendSSG({
        analysisId: "test-id",
      });
      const duration = Date.now() - start;

      expect(duration).toBeLessThan(5000); // Should complete within 5 seconds
    });

    it("should include execution time in response", async () => {
      mockManager.recall.mockResolvedValue({
        data: {
          repository: { language: "JavaScript" },
        },
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      expect(result.content[1].text).toContain("Execution completed in");
    });
  });

  describe("Edge Cases", () => {
    it("should handle null analysis data", async () => {
      mockManager.recall.mockResolvedValue({
        data: null,
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      expect(result.content).toBeDefined();
    });

    it("should handle empty analysis data", async () => {
      mockManager.recall.mockResolvedValue({
        data: {},
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      expect(result.content).toBeDefined();
    });

    it("should handle analysis without dependency data", async () => {
      mockManager.recall.mockResolvedValue({
        data: {
          documentation: {
            estimatedComplexity: "moderate",
          },
        },
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(recommendation.recommended).toBeDefined();
    });

    it("should handle unknown programming languages", async () => {
      mockManager.recall.mockResolvedValue({
        data: {
          dependencies: {
            ecosystem: "unknown",
          },
          documentation: {
            estimatedComplexity: "moderate",
          },
        },
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      const recommendation = JSON.parse(result.content[0].text);
      expect(recommendation.recommended).toBeDefined();
    });
  });

  describe("Response Format", () => {
    it("should return properly formatted MCP response", async () => {
      mockManager.recall.mockResolvedValue({
        data: {
          repository: { language: "JavaScript" },
        },
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      expect(result).toHaveProperty("content");
      expect(Array.isArray(result.content)).toBe(true);
      expect(result.content[0]).toHaveProperty("type");
      expect(result.content[0]).toHaveProperty("text");
    });

    it("should include all required recommendation fields", async () => {
      mockManager.recall.mockResolvedValue({
        data: {
          repository: { language: "Python" },
        },
      });

      const result = await recommendSSG({
        analysisId: "test-id",
      });

      const recommendation = JSON.parse(result.content[0].text);

      expect(recommendation).toHaveProperty("recommended");
      expect(recommendation).toHaveProperty("confidence");
      expect(recommendation).toHaveProperty("reasoning");
      expect(recommendation).toHaveProperty("alternatives");
      expect(result.content[1].text).toContain("Execution completed in");
    });
  });
});

```

--------------------------------------------------------------------------------
/tests/tools/track-documentation-freshness.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Integration Tests for track_documentation_freshness Tool
 */

import { describe, it, expect, beforeEach, afterEach } from "@jest/globals";
import fs from "fs/promises";
import path from "path";
import os from "os";
import {
  trackDocumentationFreshness,
  type TrackDocumentationFreshnessInput,
} from "../../src/tools/track-documentation-freshness.js";

// Example git SHA for testing
const SHA_EXAMPLE = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0";

describe("track_documentation_freshness Tool", () => {
  let tempDir: string;

  beforeEach(async () => {
    tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "track-freshness-test-"));
  });

  afterEach(async () => {
    await fs.rm(tempDir, { recursive: true, force: true });
  });

  describe("Basic Functionality", () => {
    it("should track freshness with preset thresholds", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      // Create test files
      const now = Date.now();
      await fs.writeFile(
        path.join(docsPath, "fresh.md"),
        `---
documcp:
  last_updated: "${new Date(now - 1 * 24 * 60 * 60 * 1000).toISOString()}"
---
# Fresh Doc`,
      );

      await fs.writeFile(
        path.join(docsPath, "old.md"),
        `---
documcp:
  last_updated: "${new Date(now - 60 * 24 * 60 * 60 * 1000).toISOString()}"
---
# Old Doc`,
      );

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.success).toBe(true);
      expect(result.data).toBeDefined();
      expect(result.data.report.totalFiles).toBe(2);
      expect(result.data.report.freshFiles).toBeGreaterThan(0);
      expect(result.metadata.executionTime).toBeGreaterThan(0);
    });

    it("should track freshness with custom thresholds", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      await fs.writeFile(
        path.join(docsPath, "test.md"),
        `---
documcp:
  last_updated: "${new Date(Date.now() - 45 * 60 * 1000).toISOString()}"
---
# Test`,
      );

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        warningThreshold: { value: 30, unit: "minutes" },
        staleThreshold: { value: 1, unit: "hours" },
        criticalThreshold: { value: 2, unit: "hours" },
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.success).toBe(true);
      expect(result.data.report).toBeDefined();
      expect(result.data.thresholds.warning).toEqual({
        value: 30,
        unit: "minutes",
      });
    });

    it("should identify files without metadata", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      await fs.writeFile(
        path.join(docsPath, "no-metadata.md"),
        "# No Metadata",
      );

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.success).toBe(true);
      expect(result.data.report.filesWithoutMetadata).toBe(1);
      expect(result.data.report.totalFiles).toBe(1);
    });
  });

  describe("Staleness Levels", () => {
    it("should correctly categorize fresh files", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      await fs.writeFile(
        path.join(docsPath, "fresh.md"),
        `---
documcp:
  last_updated: "${new Date(
    Date.now() - 2 * 24 * 60 * 60 * 1000,
  ).toISOString()}"
---
# Fresh`,
      );

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.data.report.freshFiles).toBe(1);
      expect(result.data.report.staleFiles).toBe(0);
      expect(result.data.report.criticalFiles).toBe(0);
    });

    it("should correctly categorize stale files", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      await fs.writeFile(
        path.join(docsPath, "stale.md"),
        `---
documcp:
  last_updated: "${new Date(
    Date.now() - 70 * 24 * 60 * 60 * 1000,
  ).toISOString()}"
---
# Stale`,
      );

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.data.report.staleFiles).toBeGreaterThan(0);
    });

    it("should correctly categorize critical files", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      await fs.writeFile(
        path.join(docsPath, "critical.md"),
        `---
documcp:
  last_updated: "${new Date(
    Date.now() - 100 * 24 * 60 * 60 * 1000,
  ).toISOString()}"
---
# Critical`,
      );

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.data.report.criticalFiles).toBe(1);
    });
  });

  describe("File Listing Options", () => {
    it("should include file list when requested", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      await fs.writeFile(path.join(docsPath, "test.md"), "# Test");

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
        includeFileList: true,
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.data.report.files).toBeDefined();
      expect(result.data.report.files.length).toBe(1);
      expect(result.data.formattedReport).toContain("File Details");
    });

    it("should exclude file list when not requested", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      await fs.writeFile(path.join(docsPath, "test.md"), "# Test");

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
        includeFileList: false,
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.data.formattedReport).not.toContain("File Details");
    });
  });

  describe("Sorting Options", () => {
    it("should sort files by staleness", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      const now = Date.now();
      await fs.writeFile(
        path.join(docsPath, "fresh.md"),
        `---
documcp:
  last_updated: "${new Date(now - 1 * 24 * 60 * 60 * 1000).toISOString()}"
---
# Fresh`,
      );

      await fs.writeFile(
        path.join(docsPath, "stale.md"),
        `---
documcp:
  last_updated: "${new Date(now - 60 * 24 * 60 * 60 * 1000).toISOString()}"
---
# Stale`,
      );

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
        sortBy: "staleness",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.success).toBe(true);
      // Stale files should appear first when sorted by staleness
      const formattedReport = result.data.formattedReport;
      const staleIndex = formattedReport.indexOf("stale.md");
      const freshIndex = formattedReport.indexOf("fresh.md");

      if (staleIndex !== -1 && freshIndex !== -1) {
        expect(staleIndex).toBeLessThan(freshIndex);
      }
    });

    it("should sort files by age", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      const now = Date.now();
      await fs.writeFile(
        path.join(docsPath, "newer.md"),
        `---
documcp:
  last_updated: "${new Date(now - 10 * 24 * 60 * 60 * 1000).toISOString()}"
---
# Newer`,
      );

      await fs.writeFile(
        path.join(docsPath, "older.md"),
        `---
documcp:
  last_updated: "${new Date(now - 50 * 24 * 60 * 60 * 1000).toISOString()}"
---
# Older`,
      );

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
        sortBy: "age",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.success).toBe(true);
    });
  });

  describe("Nested Directories", () => {
    it("should scan nested directories recursively", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);
      await fs.mkdir(path.join(docsPath, "api"));
      await fs.mkdir(path.join(docsPath, "guides"));

      await fs.writeFile(path.join(docsPath, "index.md"), "# Index");
      await fs.writeFile(path.join(docsPath, "api", "endpoints.md"), "# API");
      await fs.writeFile(
        path.join(docsPath, "guides", "tutorial.md"),
        "# Guide",
      );

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.data.report.totalFiles).toBe(3);
    });

    it("should skip common ignored directories", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);
      await fs.mkdir(path.join(docsPath, "node_modules"));
      await fs.mkdir(path.join(docsPath, ".git"));

      await fs.writeFile(path.join(docsPath, "index.md"), "# Index");
      await fs.writeFile(
        path.join(docsPath, "node_modules", "skip.md"),
        "# Skip",
      );
      await fs.writeFile(path.join(docsPath, ".git", "skip.md"), "# Skip");

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.data.report.totalFiles).toBe(1);
    });
  });

  describe("Error Handling", () => {
    it("should handle non-existent directory", async () => {
      const input: TrackDocumentationFreshnessInput = {
        docsPath: "/nonexistent/path",
        preset: "monthly",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.success).toBe(false);
      expect(result.error).toBeDefined();
      expect(result.error?.code).toBe("FRESHNESS_TRACKING_FAILED");
    });

    it("should handle empty directory", async () => {
      const docsPath = path.join(tempDir, "empty-docs");
      await fs.mkdir(docsPath);

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.success).toBe(true);
      expect(result.data.report.totalFiles).toBe(0);
    });
  });

  describe("Preset Thresholds", () => {
    const presets: Array<
      keyof typeof import("../../src/utils/freshness-tracker.js").STALENESS_PRESETS
    > = ["realtime", "active", "recent", "weekly", "monthly", "quarterly"];

    presets.forEach((preset) => {
      it(`should work with ${preset} preset`, async () => {
        const docsPath = path.join(tempDir, `docs-${preset}`);
        await fs.mkdir(docsPath);

        await fs.writeFile(path.join(docsPath, "test.md"), "# Test");

        const input: TrackDocumentationFreshnessInput = {
          docsPath,
          preset,
        };

        const result = await trackDocumentationFreshness(input);

        expect(result.success).toBe(true);
        expect(result.data.thresholds).toBeDefined();
      });
    });
  });

  describe("Output Format", () => {
    it("should include formatted report in response", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      await fs.writeFile(path.join(docsPath, "test.md"), "# Test");

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.data.formattedReport).toBeDefined();
      expect(result.data.formattedReport).toContain(
        "Documentation Freshness Report",
      );
      expect(result.data.formattedReport).toContain("Summary Statistics");
      expect(result.data.formattedReport).toContain("Freshness Breakdown");
    });

    it("should include summary in response", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      await fs.writeFile(path.join(docsPath, "test.md"), "# Test");

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.data.summary).toBeDefined();
      expect(result.data.summary).toContain("Scanned");
      expect(result.data.summary).toContain("files");
    });

    it("should include metadata in response", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      await fs.writeFile(path.join(docsPath, "test.md"), "# Test");

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.metadata).toBeDefined();
      expect(result.metadata.toolVersion).toBe("1.0.0");
      expect(result.metadata.timestamp).toBeDefined();
      expect(result.metadata.executionTime).toBeGreaterThanOrEqual(0);
    });

    it("should handle KG storage disabled", async () => {
      const docsPath = path.join(tempDir, "docs");
      const projectPath = tempDir;
      await fs.mkdir(docsPath);

      await fs.writeFile(path.join(docsPath, "test.md"), "# Test");

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        projectPath,
        preset: "monthly",
        storeInKG: false,
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.success).toBe(true);
      expect(result.data.kgInsights).toBeUndefined();
    });

    it("should handle projectPath without KG storage", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      await fs.writeFile(path.join(docsPath, "test.md"), "# Test");

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        // No projectPath provided
        preset: "monthly",
        storeInKG: true, // Won't store because projectPath is missing
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.success).toBe(true);
    });

    it("should handle error gracefully", async () => {
      const input: TrackDocumentationFreshnessInput = {
        docsPath: "/nonexistent/path/that/does/not/exist",
        preset: "monthly",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.success).toBe(false);
      expect(result.error).toBeDefined();
      expect(result.error?.code).toBe("FRESHNESS_TRACKING_FAILED");
      expect(result.metadata).toBeDefined();
    });

    it("should sort files by age", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      const now = Date.now();
      await fs.writeFile(
        path.join(docsPath, "newer.md"),
        `---
documcp:
  last_updated: "${new Date(now - 1 * 24 * 60 * 60 * 1000).toISOString()}"
---
# Newer`,
      );

      await fs.writeFile(
        path.join(docsPath, "older.md"),
        `---
documcp:
  last_updated: "${new Date(now - 10 * 24 * 60 * 60 * 1000).toISOString()}"
---
# Older`,
      );

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
        sortBy: "age",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.success).toBe(true);
      expect(result.data.report.files.length).toBe(2);
    });

    it("should sort files by path", async () => {
      const docsPath = path.join(tempDir, "docs");
      await fs.mkdir(docsPath);

      await fs.writeFile(path.join(docsPath, "z.md"), "# Z");
      await fs.writeFile(path.join(docsPath, "a.md"), "# A");

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        preset: "monthly",
        sortBy: "path",
      };

      const result = await trackDocumentationFreshness(input);

      expect(result.success).toBe(true);
    });

    it("should display commit hash for files validated against commits", async () => {
      const docsPath = path.join(tempDir, "docs");
      const projectPath = tempDir;
      await fs.mkdir(docsPath);

      // Create file with validated_against_commit metadata
      const fileContent = `---
documcp:
  last_updated: ${new Date().toISOString()}
  last_validated: ${new Date().toISOString()}
  validated_against_commit: ${SHA_EXAMPLE}
---
# Test Document
Content`;

      await fs.writeFile(path.join(docsPath, "test.md"), fileContent);

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        projectPath,
        preset: "monthly",
        includeFileList: true,
      };

      const result = await trackDocumentationFreshness(input);
      expect(result.success).toBe(true);
      expect(result.data.formattedReport).toContain(
        SHA_EXAMPLE.substring(0, 7),
      );
    });

    it("should format warning recommendations correctly", async () => {
      const docsPath = path.join(tempDir, "docs");
      const projectPath = tempDir;
      await fs.mkdir(docsPath);

      // Create a file with warning-level staleness
      const warnDate = new Date();
      warnDate.setDate(warnDate.getDate() - 45); // 45 days ago (monthly preset: warning=30d, stale=60d, critical=90d)

      const fileContent = `---
documcp:
  last_updated: ${warnDate.toISOString()}
  last_validated: ${warnDate.toISOString()}
---
# Test Document`;

      await fs.writeFile(path.join(docsPath, "warn.md"), fileContent);

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        projectPath,
        preset: "monthly",
        storeInKG: true,
      };

      const result = await trackDocumentationFreshness(input);
      expect(result.success).toBe(true);
      expect(result.data.report.warningFiles).toBeGreaterThan(0);
    });

    it("should format critical recommendations correctly", async () => {
      const docsPath = path.join(tempDir, "docs");
      const projectPath = tempDir;
      await fs.mkdir(docsPath);

      // Create a file with critical-level staleness
      const criticalDate = new Date();
      criticalDate.setDate(criticalDate.getDate() - 100); // 100 days ago (critical for monthly preset)

      const fileContent = `---
documcp:
  last_updated: ${criticalDate.toISOString()}
  last_validated: ${criticalDate.toISOString()}
---
# Old Document`;

      await fs.writeFile(path.join(docsPath, "critical.md"), fileContent);

      const input: TrackDocumentationFreshnessInput = {
        docsPath,
        projectPath,
        preset: "monthly",
        storeInKG: true,
      };

      const result = await trackDocumentationFreshness(input);
      expect(result.success).toBe(true);
      expect(result.data.report.criticalFiles).toBeGreaterThan(0);
    });
  });
});

```

--------------------------------------------------------------------------------
/tests/utils/ast-analyzer.test.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * AST Analyzer Tests (Phase 3)
 */

import {
  ASTAnalyzer,
  FunctionSignature,
  ClassInfo,
} from "../../src/utils/ast-analyzer.js";
import { promises as fs } from "fs";
import { tmpdir } from "os";
import { join } from "path";
import { mkdtemp, rm } from "fs/promises";

describe("ASTAnalyzer", () => {
  let analyzer: ASTAnalyzer;
  let tempDir: string;

  beforeAll(async () => {
    analyzer = new ASTAnalyzer();
    await analyzer.initialize();
    tempDir = await mkdtemp(join(tmpdir(), "ast-test-"));
  });

  afterAll(async () => {
    await rm(tempDir, { recursive: true, force: true });
  });

  describe("TypeScript/JavaScript Analysis", () => {
    test("should extract function declarations", async () => {
      const code = `
export async function testFunction(param1: string, param2: number): Promise<void> {
  console.log(param1, param2);
}

export function syncFunction(name: string): string {
  return name.toUpperCase();
}
      `.trim();

      const filePath = join(tempDir, "test-functions.ts");
      await fs.writeFile(filePath, code);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();
      expect(result?.functions).toHaveLength(2);

      const asyncFunc = result?.functions.find(
        (f) => f.name === "testFunction",
      );
      expect(asyncFunc).toBeDefined();
      expect(asyncFunc?.isAsync).toBe(true);
      expect(asyncFunc?.isExported).toBe(true);
      expect(asyncFunc?.parameters).toHaveLength(2);
      expect(asyncFunc?.returnType).toBe("Promise");

      const syncFunc = result?.functions.find((f) => f.name === "syncFunction");
      expect(syncFunc).toBeDefined();
      expect(syncFunc?.isAsync).toBe(false);
      expect(syncFunc?.returnType).toBe("string");
    });

    test("should extract arrow function declarations", async () => {
      const code = `
export const arrowFunc = async (x: number, y: number): Promise<number> => {
  return x + y;
};

const privateFunc = (name: string) => {
  return name.toLowerCase();
};
      `.trim();

      const filePath = join(tempDir, "test-arrow.ts");
      await fs.writeFile(filePath, code);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();
      expect(result?.functions).toHaveLength(2);

      const exportedArrow = result?.functions.find(
        (f) => f.name === "arrowFunc",
      );
      expect(exportedArrow).toBeDefined();
      expect(exportedArrow?.isAsync).toBe(true);
      expect(exportedArrow?.parameters).toHaveLength(2);
    });

    test("should extract class information", async () => {
      const code = `
/**
 * Test class documentation
 */
export class TestClass extends BaseClass {
  private value: number;
  public readonly name: string;

  constructor(name: string) {
    super();
    this.name = name;
    this.value = 0;
  }

  /**
   * Public method
   */
  public async getValue(): Promise<number> {
    return this.value;
  }

  private setValue(val: number): void {
    this.value = val;
  }
}
      `.trim();

      const filePath = join(tempDir, "test-class.ts");
      await fs.writeFile(filePath, code);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();
      expect(result?.classes).toHaveLength(1);

      const testClass = result?.classes[0];
      expect(testClass?.name).toBe("TestClass");
      expect(testClass?.isExported).toBe(true);
      expect(testClass?.extends).toBe("BaseClass");
      expect(testClass?.properties).toHaveLength(2);
      expect(testClass?.methods.length).toBeGreaterThan(0);

      const publicMethod = testClass?.methods.find(
        (m) => m.name === "getValue",
      );
      expect(publicMethod).toBeDefined();
      expect(publicMethod?.isAsync).toBe(true);
      expect(publicMethod?.isPublic).toBe(true);
    });

    test("should extract interface information", async () => {
      const code = `
/**
 * User interface
 */
export interface User {
  id: string;
  name: string;
  age: number;
  readonly email: string;
  getProfile(): Promise<Profile>;
}

interface Profile {
  bio: string;
}
      `.trim();

      const filePath = join(tempDir, "test-interface.ts");
      await fs.writeFile(filePath, code);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();
      expect(result?.interfaces).toHaveLength(2);

      const userInterface = result?.interfaces.find((i) => i.name === "User");
      expect(userInterface).toBeDefined();
      expect(userInterface?.isExported).toBe(true);
      expect(userInterface?.properties).toHaveLength(4);
      expect(userInterface?.methods).toHaveLength(1);

      const emailProp = userInterface?.properties.find(
        (p) => p.name === "email",
      );
      expect(emailProp?.isReadonly).toBe(true);
    });

    test("should extract type aliases", async () => {
      const code = `
export type ID = string | number;
export type Status = "pending" | "active" | "inactive";
type PrivateType = { x: number; y: number };
      `.trim();

      const filePath = join(tempDir, "test-types.ts");
      await fs.writeFile(filePath, code);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();
      expect(result?.types).toHaveLength(3);

      const idType = result?.types.find((t) => t.name === "ID");
      expect(idType?.isExported).toBe(true);
    });

    test("should extract imports and exports", async () => {
      const code = `
import { func1, func2 } from "./module1";
import type { Type1 } from "./types";
import defaultExport from "./default";

export { func1, func2 };
export default class MyClass {}
      `.trim();

      const filePath = join(tempDir, "test-imports.ts");
      await fs.writeFile(filePath, code);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();
      expect(result?.imports.length).toBeGreaterThan(0);
      expect(result?.exports).toContain("func1");
      expect(result?.exports).toContain("func2");
    });

    test("should calculate complexity metrics", async () => {
      const code = `
export function complexFunction(x: number): number {
  if (x > 10) {
    for (let i = 0; i < x; i++) {
      if (i % 2 === 0) {
        try {
          return i;
        } catch (error) {
          continue;
        }
      }
    }
  } else {
    return 0;
  }
  return -1;
}
      `.trim();

      const filePath = join(tempDir, "test-complexity.ts");
      await fs.writeFile(filePath, code);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();
      const func = result?.functions[0];
      expect(func?.complexity).toBeGreaterThan(1);
    });

    test("should extract JSDoc comments", async () => {
      const code = `
/**
 * This function adds two numbers
 * @param a First number
 * @param b Second number
 * @returns The sum
 */
export function add(a: number, b: number): number {
  return a + b;
}
      `.trim();

      const filePath = join(tempDir, "test-jsdoc.ts");
      await fs.writeFile(filePath, code);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();
      const func = result?.functions[0];
      expect(func?.docComment).toBeTruthy();
      expect(func?.docComment).toContain("adds two numbers");
    });
  });

  describe("Drift Detection", () => {
    test("should detect function signature changes", async () => {
      const oldCode = `
export function processData(data: string): void {
  console.log(data);
}
      `.trim();

      const newCode = `
export function processData(data: string, options: object): Promise<string> {
  console.log(data, options);
  return Promise.resolve("done");
}
      `.trim();

      const oldFile = join(tempDir, "old-file.ts");
      const newFile = join(tempDir, "new-file.ts");

      await fs.writeFile(oldFile, oldCode);
      await fs.writeFile(newFile, newCode);

      const oldAnalysis = await analyzer.analyzeFile(oldFile);
      const newAnalysis = await analyzer.analyzeFile(newFile);

      expect(oldAnalysis).not.toBeNull();
      expect(newAnalysis).not.toBeNull();

      const diffs = await analyzer.detectDrift(oldAnalysis!, newAnalysis!);

      expect(diffs.length).toBeGreaterThan(0);
      const funcDiff = diffs.find(
        (d) => d.category === "function" && d.name === "processData",
      );
      expect(funcDiff).toBeDefined();
      expect(funcDiff?.type).toBe("modified");
      expect(funcDiff?.impactLevel).toBe("breaking");
    });

    test("should detect removed functions", async () => {
      const oldCode = `
export function oldFunction(): void {}
export function keepFunction(): void {}
      `.trim();

      const newCode = `
export function keepFunction(): void {}
      `.trim();

      const oldFile = join(tempDir, "old-removed.ts");
      const newFile = join(tempDir, "new-removed.ts");

      await fs.writeFile(oldFile, oldCode);
      await fs.writeFile(newFile, newCode);

      const oldAnalysis = await analyzer.analyzeFile(oldFile);
      const newAnalysis = await analyzer.analyzeFile(newFile);

      const diffs = await analyzer.detectDrift(oldAnalysis!, newAnalysis!);

      const removedDiff = diffs.find((d) => d.name === "oldFunction");
      expect(removedDiff).toBeDefined();
      expect(removedDiff?.type).toBe("removed");
      expect(removedDiff?.impactLevel).toBe("breaking");
    });

    test("should detect added functions", async () => {
      const oldCode = `
export function existingFunction(): void {}
      `.trim();

      const newCode = `
export function existingFunction(): void {}
export function newFunction(): void {}
      `.trim();

      const oldFile = join(tempDir, "old-added.ts");
      const newFile = join(tempDir, "new-added.ts");

      await fs.writeFile(oldFile, oldCode);
      await fs.writeFile(newFile, newCode);

      const oldAnalysis = await analyzer.analyzeFile(oldFile);
      const newAnalysis = await analyzer.analyzeFile(newFile);

      const diffs = await analyzer.detectDrift(oldAnalysis!, newAnalysis!);

      const addedDiff = diffs.find((d) => d.name === "newFunction");
      expect(addedDiff).toBeDefined();
      expect(addedDiff?.type).toBe("added");
      expect(addedDiff?.impactLevel).toBe("patch");
    });

    test("should detect minor changes", async () => {
      const oldCode = `
function internalFunction(): void {}
      `.trim();

      const newCode = `
export function internalFunction(): void {}
      `.trim();

      const oldFile = join(tempDir, "old-minor.ts");
      const newFile = join(tempDir, "new-minor.ts");

      await fs.writeFile(oldFile, oldCode);
      await fs.writeFile(newFile, newCode);

      const oldAnalysis = await analyzer.analyzeFile(oldFile);
      const newAnalysis = await analyzer.analyzeFile(newFile);

      const diffs = await analyzer.detectDrift(oldAnalysis!, newAnalysis!);

      const minorDiff = diffs.find((d) => d.name === "internalFunction");
      expect(minorDiff).toBeDefined();
      expect(minorDiff?.type).toBe("modified");
      expect(minorDiff?.impactLevel).toBe("minor");
    });
  });

  describe("Edge Cases", () => {
    test("should handle empty files", async () => {
      const filePath = join(tempDir, "empty.ts");
      await fs.writeFile(filePath, "");

      const result = await analyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();
      expect(result?.functions).toHaveLength(0);
      expect(result?.classes).toHaveLength(0);
    });

    test("should handle files with only comments", async () => {
      const code = `
// This is a comment
/* Multi-line
   comment */
      `.trim();

      const filePath = join(tempDir, "comments-only.ts");
      await fs.writeFile(filePath, code);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();
      expect(result?.functions).toHaveLength(0);
    });

    test("should handle syntax errors gracefully", async () => {
      const code = `
export function broken(
  // Missing closing paren and body
      `.trim();

      const filePath = join(tempDir, "syntax-error.ts");
      await fs.writeFile(filePath, code);

      const result = await analyzer.analyzeFile(filePath);

      // Should still return a result, even if incomplete
      expect(result).not.toBeNull();
    });

    test("should return null for unsupported file types", async () => {
      const filePath = join(tempDir, "test.txt");
      await fs.writeFile(filePath, "Some text content");

      const result = await analyzer.analyzeFile(filePath);

      expect(result).toBeNull();
    });
  });

  describe("Content Hashing", () => {
    test("should generate consistent content hashes", async () => {
      const code = `export function test(): void {}`;

      const file1 = join(tempDir, "hash1.ts");
      const file2 = join(tempDir, "hash2.ts");

      await fs.writeFile(file1, code);
      await fs.writeFile(file2, code);

      const result1 = await analyzer.analyzeFile(file1);
      const result2 = await analyzer.analyzeFile(file2);

      expect(result1?.contentHash).toBe(result2?.contentHash);
    });

    test("should generate different hashes for different content", async () => {
      const code1 = `export function test1(): void {}`;
      const code2 = `export function test2(): void {}`;

      const file1 = join(tempDir, "diff1.ts");
      const file2 = join(tempDir, "diff2.ts");

      await fs.writeFile(file1, code1);
      await fs.writeFile(file2, code2);

      const result1 = await analyzer.analyzeFile(file1);
      const result2 = await analyzer.analyzeFile(file2);

      expect(result1?.contentHash).not.toBe(result2?.contentHash);
    });
  });

  describe("Multi-Language Support", () => {
    test("should handle Python files with tree-sitter", async () => {
      const pythonCode = `
def hello_world():
    print("Hello, World!")

class MyClass:
    def __init__(self):
        self.value = 42
      `.trim();

      const filePath = join(tempDir, "test.py");
      await fs.writeFile(filePath, pythonCode);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).toBeDefined();
      expect(result?.language).toBe("python");
      expect(result?.filePath).toBe(filePath);
      expect(result?.linesOfCode).toBeGreaterThan(0);
    });

    test("should handle Go files with tree-sitter", async () => {
      const goCode = `
package main

func main() {
    println("Hello, World!")
}
      `.trim();

      const filePath = join(tempDir, "test.go");
      await fs.writeFile(filePath, goCode);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).toBeDefined();
      expect(result?.language).toBe("go");
    });

    test("should handle Rust files with tree-sitter", async () => {
      const rustCode = `
fn main() {
    println!("Hello, World!");
}
      `.trim();

      const filePath = join(tempDir, "test.rs");
      await fs.writeFile(filePath, rustCode);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).toBeDefined();
      expect(result?.language).toBe("rust");
    });
  });

  describe("Advanced TypeScript Features", () => {
    test("should extract default values from parameters", async () => {
      const code = `
export function withDefaults(
  name: string = "default",
  count: number = 42,
  flag: boolean = true
): void {
  console.log(name, count, flag);
}
      `.trim();

      const filePath = join(tempDir, "defaults.ts");
      await fs.writeFile(filePath, code);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();
      const func = result?.functions.find((f) => f.name === "withDefaults");
      expect(func).toBeDefined();
      expect(func?.parameters.length).toBe(3);

      const nameParam = func?.parameters.find((p) => p.name === "name");
      expect(nameParam?.defaultValue).toBeTruthy();
    });

    test("should detect private methods with underscore prefix", async () => {
      const code = `
export class TestClass {
  public publicMethod(): void {}

  private _privateMethod(): void {}

  #reallyPrivate(): void {}
}
      `.trim();

      const filePath = join(tempDir, "private-methods.ts");
      await fs.writeFile(filePath, code);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();
      const testClass = result?.classes[0];
      expect(testClass).toBeDefined();
      expect(testClass?.methods.length).toBeGreaterThanOrEqual(1);
    });

    test("should detect exported declarations correctly", async () => {
      const code = `
export function exportedFunc(): void {}

function nonExportedFunc(): void {}

export const exportedConst = () => {};

const nonExportedConst = () => {};
      `.trim();

      const filePath = join(tempDir, "exports.ts");
      await fs.writeFile(filePath, code);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();

      const exportedFunc = result?.functions.find(
        (f) => f.name === "exportedFunc",
      );
      expect(exportedFunc?.isExported).toBe(true);

      const exportedArrow = result?.functions.find(
        (f) => f.name === "exportedConst",
      );
      expect(exportedArrow?.isExported).toBe(true);
    });

    test("should handle files without initialization", async () => {
      const newAnalyzer = new ASTAnalyzer();
      // Don't call initialize() - should auto-initialize

      const code = `export function test(): void {}`;
      const filePath = join(tempDir, "auto-init.ts");
      await fs.writeFile(filePath, code);

      const result = await newAnalyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();
      expect(result?.functions.length).toBeGreaterThan(0);
    });
  });

  describe("Interface and Type Detection", () => {
    test("should detect interface vs type differences", async () => {
      const code = `
export interface UserInterface {
  id: string;
  name: string;
}

export type UserType = {
  id: string;
  name: string;
};

export type StatusType = "active" | "inactive";
      `.trim();

      const filePath = join(tempDir, "types-vs-interfaces.ts");
      await fs.writeFile(filePath, code);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();
      expect(result?.interfaces.length).toBe(1);
      expect(result?.types.length).toBe(2);

      const userInterface = result?.interfaces.find(
        (i) => i.name === "UserInterface",
      );
      expect(userInterface?.isExported).toBe(true);

      const statusType = result?.types.find((t) => t.name === "StatusType");
      expect(statusType?.isExported).toBe(true);
    });

    test("should handle interface methods", async () => {
      const code = `
export interface Repository {
  save(data: string): Promise<void>;
  load(): Promise<string>;
  delete(id: string): boolean;
}
      `.trim();

      const filePath = join(tempDir, "interface-methods.ts");
      await fs.writeFile(filePath, code);

      const result = await analyzer.analyzeFile(filePath);

      expect(result).not.toBeNull();
      const repo = result?.interfaces.find((i) => i.name === "Repository");
      expect(repo?.methods.length).toBe(3);
    });
  });
});

```
Page 9/20FirstPrevNextLast