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

# Directory Structure

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

# Files

--------------------------------------------------------------------------------
/src/tools/validate-content.ts:
--------------------------------------------------------------------------------

```typescript
   1 | import { Tool } from "@modelcontextprotocol/sdk/types.js";
   2 | import * as fs from "fs/promises";
   3 | import * as path from "path";
   4 | import { exec } from "child_process";
   5 | import { promisify } from "util";
   6 | import { handleMemoryRecall } from "../memory/index.js";
   7 | import {
   8 |   createExecutionSimulator,
   9 |   ExecutionSimulator,
  10 | } from "../utils/execution-simulator.js";
  11 | // ESM-compatible dirname replacement - fallback for test environments
  12 | function getDirname(): string {
  13 |   // Use process.cwd() as fallback for all environments to avoid import.meta issues
  14 |   return process.cwd();
  15 | }
  16 | 
  17 | const currentDir = getDirname();
  18 | 
  19 | const execAsync = promisify(exec);
  20 | 
  21 | interface ValidationOptions {
  22 |   contentPath: string;
  23 |   analysisId?: string;
  24 |   validationType: "accuracy" | "completeness" | "compliance" | "all";
  25 |   includeCodeValidation: boolean;
  26 |   confidence: "strict" | "moderate" | "permissive";
  27 | }
  28 | 
  29 | interface ConfidenceMetrics {
  30 |   overall: number;
  31 |   breakdown: {
  32 |     technologyDetection: number;
  33 |     frameworkVersionAccuracy: number;
  34 |     codeExampleRelevance: number;
  35 |     architecturalAssumptions: number;
  36 |     businessContextAlignment: number;
  37 |   };
  38 |   riskFactors: RiskFactor[];
  39 | }
  40 | 
  41 | interface RiskFactor {
  42 |   type: "high" | "medium" | "low";
  43 |   category: string;
  44 |   description: string;
  45 |   impact: string;
  46 |   mitigation: string;
  47 | }
  48 | 
  49 | interface UncertaintyFlag {
  50 |   area: string;
  51 |   severity: "low" | "medium" | "high" | "critical";
  52 |   description: string;
  53 |   potentialImpact: string;
  54 |   clarificationNeeded: string;
  55 |   fallbackStrategy: string;
  56 | }
  57 | 
  58 | interface ValidationIssue {
  59 |   type: "error" | "warning" | "info";
  60 |   category: "accuracy" | "completeness" | "compliance" | "performance";
  61 |   location: {
  62 |     file: string;
  63 |     line?: number;
  64 |     section?: string;
  65 |   };
  66 |   description: string;
  67 |   evidence: string[];
  68 |   suggestedFix: string;
  69 |   confidence: number;
  70 | }
  71 | 
  72 | interface CodeValidationResult {
  73 |   overallSuccess: boolean;
  74 |   exampleResults: ExampleValidation[];
  75 |   confidence: number;
  76 | }
  77 | 
  78 | interface ExampleValidation {
  79 |   example: string;
  80 |   compilationSuccess: boolean;
  81 |   executionSuccess: boolean;
  82 |   issues: ValidationIssue[];
  83 |   confidence: number;
  84 | }
  85 | 
  86 | export interface ValidationResult {
  87 |   success: boolean;
  88 |   confidence: ConfidenceMetrics;
  89 |   issues: ValidationIssue[];
  90 |   uncertainties: UncertaintyFlag[];
  91 |   codeValidation?: CodeValidationResult;
  92 |   recommendations: string[];
  93 |   nextSteps: string[];
  94 | }
  95 | 
  96 | class ContentAccuracyValidator {
  97 |   private projectContext: any;
  98 |   private tempDir: string;
  99 |   private executionSimulator: ExecutionSimulator | null = null;
 100 |   private useExecutionSimulation: boolean;
 101 | 
 102 |   constructor(useExecutionSimulation: boolean = true) {
 103 |     this.tempDir = path.join(currentDir, ".tmp");
 104 |     this.useExecutionSimulation = useExecutionSimulation;
 105 | 
 106 |     // Initialize execution simulator if enabled
 107 |     if (useExecutionSimulation) {
 108 |       this.executionSimulator = createExecutionSimulator({
 109 |         maxDepth: 5,
 110 |         maxSteps: 50,
 111 |         timeoutMs: 15000,
 112 |         detectNullRefs: true,
 113 |         detectTypeMismatches: true,
 114 |         detectUnreachableCode: true,
 115 |         confidenceThreshold: 0.6,
 116 |       });
 117 |     }
 118 |   }
 119 | 
 120 |   async validateContent(
 121 |     options: ValidationOptions,
 122 |     context?: any,
 123 |   ): Promise<ValidationResult> {
 124 |     if (context?.meta?.progressToken) {
 125 |       await context.meta.reportProgress?.({ progress: 0, total: 100 });
 126 |     }
 127 | 
 128 |     const result: ValidationResult = {
 129 |       success: false,
 130 |       confidence: this.initializeConfidenceMetrics(),
 131 |       issues: [],
 132 |       uncertainties: [],
 133 |       recommendations: [],
 134 |       nextSteps: [],
 135 |     };
 136 | 
 137 |     // Load project context if analysis ID provided
 138 |     if (options.analysisId) {
 139 |       await context?.info?.("📊 Loading project context...");
 140 |       this.projectContext = await this.loadProjectContext(options.analysisId);
 141 |     }
 142 | 
 143 |     if (context?.meta?.progressToken) {
 144 |       await context.meta.reportProgress?.({ progress: 20, total: 100 });
 145 |     }
 146 | 
 147 |     // Determine if we should analyze application code vs documentation
 148 |     await context?.info?.("🔎 Analyzing content type...");
 149 |     const isApplicationValidation = await this.shouldAnalyzeApplicationCode(
 150 |       options.contentPath,
 151 |     );
 152 | 
 153 |     if (context?.meta?.progressToken) {
 154 |       await context.meta.reportProgress?.({ progress: 40, total: 100 });
 155 |     }
 156 | 
 157 |     // Perform different types of validation based on request
 158 |     if (
 159 |       options.validationType === "all" ||
 160 |       options.validationType === "accuracy"
 161 |     ) {
 162 |       await this.validateAccuracy(options.contentPath, result);
 163 |     }
 164 | 
 165 |     if (
 166 |       options.validationType === "all" ||
 167 |       options.validationType === "completeness"
 168 |     ) {
 169 |       await this.validateCompleteness(options.contentPath, result);
 170 |     }
 171 | 
 172 |     if (
 173 |       options.validationType === "all" ||
 174 |       options.validationType === "compliance"
 175 |     ) {
 176 |       if (isApplicationValidation) {
 177 |         await this.validateApplicationStructureCompliance(
 178 |           options.contentPath,
 179 |           result,
 180 |         );
 181 |       } else {
 182 |         await this.validateDiataxisCompliance(options.contentPath, result);
 183 |       }
 184 |     }
 185 | 
 186 |     // Code validation if requested
 187 |     if (options.includeCodeValidation) {
 188 |       result.codeValidation = await this.validateCodeExamples(
 189 |         options.contentPath,
 190 |       );
 191 |       // Set code example relevance confidence based on code validation results
 192 |       if (result.codeValidation) {
 193 |         const successRate =
 194 |           result.codeValidation.exampleResults.length > 0
 195 |             ? result.codeValidation.exampleResults.filter(
 196 |                 (e) => e.compilationSuccess,
 197 |               ).length / result.codeValidation.exampleResults.length
 198 |             : 1;
 199 |         result.confidence.breakdown.codeExampleRelevance = Math.round(
 200 |           successRate * 100,
 201 |         );
 202 |       }
 203 |     } else {
 204 |       // If code validation is skipped, assume reasonable confidence
 205 |       result.confidence.breakdown.codeExampleRelevance = 75;
 206 |     }
 207 | 
 208 |     // Set framework version accuracy based on technology detection confidence
 209 |     result.confidence.breakdown.frameworkVersionAccuracy = Math.min(
 210 |       90,
 211 |       result.confidence.breakdown.technologyDetection + 10,
 212 |     );
 213 | 
 214 |     // Set architectural assumptions confidence based on file structure and content analysis
 215 |     const filesAnalyzed = await this.getMarkdownFiles(options.contentPath);
 216 |     const hasStructuredContent = filesAnalyzed.length > 3; // Basic heuristic
 217 |     result.confidence.breakdown.architecturalAssumptions = hasStructuredContent
 218 |       ? 80
 219 |       : 60;
 220 | 
 221 |     // Calculate overall confidence and success
 222 |     this.calculateOverallMetrics(result);
 223 | 
 224 |     // Generate recommendations and next steps
 225 |     this.generateRecommendations(result, options);
 226 | 
 227 |     if (context?.meta?.progressToken) {
 228 |       await context.meta.reportProgress?.({ progress: 100, total: 100 });
 229 |     }
 230 | 
 231 |     const status = result.success ? "PASSED" : "ISSUES FOUND";
 232 |     await context?.info?.(
 233 |       `✅ Validation complete! Status: ${status} (${result.confidence.overall}% confidence, ${result.issues.length} issue(s))`,
 234 |     );
 235 | 
 236 |     return result;
 237 |   }
 238 | 
 239 |   private initializeConfidenceMetrics(): ConfidenceMetrics {
 240 |     return {
 241 |       overall: 0,
 242 |       breakdown: {
 243 |         technologyDetection: 0,
 244 |         frameworkVersionAccuracy: 0,
 245 |         codeExampleRelevance: 0,
 246 |         architecturalAssumptions: 0,
 247 |         businessContextAlignment: 0,
 248 |       },
 249 |       riskFactors: [],
 250 |     };
 251 |   }
 252 | 
 253 |   private async loadProjectContext(analysisId: string): Promise<any> {
 254 |     // Try to get analysis from memory system first
 255 |     try {
 256 |       const memoryRecall = await handleMemoryRecall({
 257 |         query: analysisId,
 258 |         type: "analysis",
 259 |         limit: 1,
 260 |       });
 261 | 
 262 |       // Handle the memory recall result structure
 263 |       if (
 264 |         memoryRecall &&
 265 |         memoryRecall.memories &&
 266 |         memoryRecall.memories.length > 0
 267 |       ) {
 268 |         const memory = memoryRecall.memories[0];
 269 | 
 270 |         // Handle wrapped content structure
 271 |         if (
 272 |           memory.data &&
 273 |           memory.data.content &&
 274 |           Array.isArray(memory.data.content)
 275 |         ) {
 276 |           // Extract the JSON from the first text content
 277 |           const firstContent = memory.data.content[0];
 278 |           if (
 279 |             firstContent &&
 280 |             firstContent.type === "text" &&
 281 |             firstContent.text
 282 |           ) {
 283 |             try {
 284 |               return JSON.parse(firstContent.text);
 285 |             } catch (parseError) {
 286 |               console.warn(
 287 |                 "Failed to parse analysis content from memory:",
 288 |                 parseError,
 289 |               );
 290 |             }
 291 |           }
 292 |         }
 293 | 
 294 |         // Try direct content or data access
 295 |         if (memory.content) {
 296 |           return memory.content;
 297 |         }
 298 |         if (memory.data) {
 299 |           return memory.data;
 300 |         }
 301 |       }
 302 |     } catch (error) {
 303 |       console.warn("Failed to retrieve from memory system:", error);
 304 |     }
 305 | 
 306 |     // Fallback to reading from cached analysis file
 307 |     try {
 308 |       const analysisPath = path.join(
 309 |         ".documcp",
 310 |         "analyses",
 311 |         `${analysisId}.json`,
 312 |       );
 313 |       const content = await fs.readFile(analysisPath, "utf-8");
 314 |       return JSON.parse(content);
 315 |     } catch {
 316 |       // Return default context if no analysis found
 317 |       return {
 318 |         metadata: { projectName: "unknown", primaryLanguage: "JavaScript" },
 319 |         technologies: {},
 320 |         dependencies: { packages: [] },
 321 |       };
 322 |     }
 323 |   }
 324 | 
 325 |   private async validateAccuracy(
 326 |     contentPath: string,
 327 |     result: ValidationResult,
 328 |   ): Promise<void> {
 329 |     const files = await this.getMarkdownFiles(contentPath);
 330 | 
 331 |     for (const file of files) {
 332 |       const content = await fs.readFile(file, "utf-8");
 333 | 
 334 |       // Check for common accuracy issues
 335 |       await this.checkTechnicalAccuracy(file, content, result);
 336 |       await this.checkFrameworkVersionCompatibility(file, content, result);
 337 |       await this.checkCommandAccuracy(file, content, result);
 338 |       await this.checkLinkValidity(file, content, result);
 339 |     }
 340 | 
 341 |     // Update confidence based on findings
 342 |     this.updateAccuracyConfidence(result);
 343 |   }
 344 | 
 345 |   private async checkTechnicalAccuracy(
 346 |     filePath: string,
 347 |     content: string,
 348 |     result: ValidationResult,
 349 |   ): Promise<void> {
 350 |     // Check for deprecated patterns
 351 |     const deprecatedPatterns = [
 352 |       {
 353 |         pattern: /npm install -g/,
 354 |         suggestion: "Use npx instead of global installs",
 355 |       },
 356 |       { pattern: /var\s+\w+/, suggestion: "Use const or let instead of var" },
 357 |       { pattern: /function\(\)/, suggestion: "Consider using arrow functions" },
 358 |       { pattern: /http:\/\//, suggestion: "Use HTTPS URLs for security" },
 359 |     ];
 360 | 
 361 |     for (const { pattern, suggestion } of deprecatedPatterns) {
 362 |       if (pattern.test(content)) {
 363 |         result.issues.push({
 364 |           type: "warning",
 365 |           category: "accuracy",
 366 |           location: { file: path.basename(filePath) },
 367 |           description: `Potentially outdated pattern detected: ${pattern.source}`,
 368 |           evidence: [content.match(pattern)?.[0] || ""],
 369 |           suggestedFix: suggestion,
 370 |           confidence: 80,
 371 |         });
 372 |       }
 373 |     }
 374 | 
 375 |     // Check for missing error handling in code examples
 376 |     const codeBlocks = this.extractCodeBlocks(content);
 377 |     for (const block of codeBlocks) {
 378 |       if (block.language === "javascript" || block.language === "typescript") {
 379 |         if (
 380 |           block.code.includes("await") &&
 381 |           !block.code.includes("try") &&
 382 |           !block.code.includes("catch")
 383 |         ) {
 384 |           result.issues.push({
 385 |             type: "warning",
 386 |             category: "accuracy",
 387 |             location: { file: path.basename(filePath) },
 388 |             description: "Async code without error handling",
 389 |             evidence: [block.code.substring(0, 100)],
 390 |             suggestedFix: "Add try-catch blocks for async operations",
 391 |             confidence: 90,
 392 |           });
 393 |         }
 394 |       }
 395 |     }
 396 |   }
 397 | 
 398 |   private async checkFrameworkVersionCompatibility(
 399 |     filePath: string,
 400 |     content: string,
 401 |     result: ValidationResult,
 402 |   ): Promise<void> {
 403 |     if (!this.projectContext) return;
 404 | 
 405 |     // Check if mentioned versions align with project dependencies
 406 |     const versionPattern = /@(\d+\.\d+\.\d+)/g;
 407 |     const matches = content.match(versionPattern);
 408 | 
 409 |     if (matches) {
 410 |       for (const match of matches) {
 411 |         const version = match.replace("@", "");
 412 | 
 413 |         result.uncertainties.push({
 414 |           area: "version-compatibility",
 415 |           severity: "medium",
 416 |           description: `Version ${version} mentioned in documentation`,
 417 |           potentialImpact: "May not match actual project dependencies",
 418 |           clarificationNeeded: "Verify version compatibility with project",
 419 |           fallbackStrategy: "Use generic version-agnostic examples",
 420 |         });
 421 |       }
 422 |     }
 423 |   }
 424 | 
 425 |   private async checkCommandAccuracy(
 426 |     filePath: string,
 427 |     content: string,
 428 |     result: ValidationResult,
 429 |   ): Promise<void> {
 430 |     const codeBlocks = this.extractCodeBlocks(content);
 431 | 
 432 |     for (const block of codeBlocks) {
 433 |       if (block.language === "bash" || block.language === "sh") {
 434 |         // Check for common command issues
 435 |         const commands = block.code
 436 |           .split("\n")
 437 |           .filter((line) => line.trim() && !line.startsWith("#"));
 438 | 
 439 |         for (const command of commands) {
 440 |           // Check for potentially dangerous commands
 441 |           const dangerousPatterns = [
 442 |             /rm -rf \//,
 443 |             /sudo rm/,
 444 |             /chmod 777/,
 445 |             /> \/dev\/null 2>&1/,
 446 |           ];
 447 | 
 448 |           for (const pattern of dangerousPatterns) {
 449 |             if (pattern.test(command)) {
 450 |               result.issues.push({
 451 |                 type: "error",
 452 |                 category: "accuracy",
 453 |                 location: { file: path.basename(filePath) },
 454 |                 description: "Potentially dangerous command in documentation",
 455 |                 evidence: [command],
 456 |                 suggestedFix: "Review and provide safer alternative",
 457 |                 confidence: 95,
 458 |               });
 459 |             }
 460 |           }
 461 | 
 462 |           // Check for non-portable commands (Windows vs Unix)
 463 |           if (command.includes("\\") && command.includes("/")) {
 464 |             result.issues.push({
 465 |               type: "warning",
 466 |               category: "accuracy",
 467 |               location: { file: path.basename(filePath) },
 468 |               description: "Mixed path separators in command",
 469 |               evidence: [command],
 470 |               suggestedFix:
 471 |                 "Use consistent path separators or provide OS-specific examples",
 472 |               confidence: 85,
 473 |             });
 474 |           }
 475 |         }
 476 |       }
 477 |     }
 478 |   }
 479 | 
 480 |   private async checkLinkValidity(
 481 |     filePath: string,
 482 |     content: string,
 483 |     result: ValidationResult,
 484 |   ): Promise<void> {
 485 |     const linkPattern = /\[([^\]]+)\]\(([^)]+)\)/g;
 486 |     const links: Array<{ text: string; url: string }> = [];
 487 |     let match;
 488 | 
 489 |     while ((match = linkPattern.exec(content)) !== null) {
 490 |       links.push({ text: match[1], url: match[2] });
 491 |     }
 492 | 
 493 |     for (const link of links) {
 494 |       // Check internal links
 495 |       if (!link.url.startsWith("http")) {
 496 |         const targetPath = path.resolve(path.dirname(filePath), link.url);
 497 |         try {
 498 |           await fs.access(targetPath);
 499 |         } catch {
 500 |           result.issues.push({
 501 |             type: "error",
 502 |             category: "accuracy",
 503 |             location: { file: path.basename(filePath) },
 504 |             description: `Broken internal link: ${link.url}`,
 505 |             evidence: [link.text],
 506 |             suggestedFix: "Fix the link path or create the missing file",
 507 |             confidence: 100,
 508 |           });
 509 |         }
 510 |       }
 511 | 
 512 |       // Flag external links for manual verification
 513 |       if (link.url.startsWith("http")) {
 514 |         result.uncertainties.push({
 515 |           area: "external-links",
 516 |           severity: "low",
 517 |           description: `External link: ${link.url}`,
 518 |           potentialImpact: "Link may become outdated or broken",
 519 |           clarificationNeeded: "Verify link is still valid",
 520 |           fallbackStrategy: "Archive important external content locally",
 521 |         });
 522 |       }
 523 |     }
 524 |   }
 525 | 
 526 |   private async validateCompleteness(
 527 |     contentPath: string,
 528 |     result: ValidationResult,
 529 |   ): Promise<void> {
 530 |     const files = await this.getMarkdownFiles(contentPath);
 531 |     const structure = await this.analyzeDiataxisStructure(contentPath);
 532 | 
 533 |     // Check for missing essential sections
 534 |     const requiredSections = [
 535 |       "tutorials",
 536 |       "how-to",
 537 |       "reference",
 538 |       "explanation",
 539 |     ];
 540 |     const missingSections = requiredSections.filter(
 541 |       (section) => !structure.sections.includes(section),
 542 |     );
 543 | 
 544 |     if (missingSections.length > 0) {
 545 |       result.issues.push({
 546 |         type: "warning",
 547 |         category: "completeness",
 548 |         location: { file: "documentation structure" },
 549 |         description: `Missing Diataxis sections: ${missingSections.join(", ")}`,
 550 |         evidence: structure.sections,
 551 |         suggestedFix:
 552 |           "Add missing Diataxis sections for complete documentation",
 553 |         confidence: 100,
 554 |       });
 555 |     }
 556 | 
 557 |     // Check content depth in each section
 558 |     for (const section of structure.sections) {
 559 |       const sectionFiles = files.filter((f) => f.includes(`/${section}/`));
 560 |       if (sectionFiles.length < 2) {
 561 |         result.issues.push({
 562 |           type: "info",
 563 |           category: "completeness",
 564 |           location: { file: section },
 565 |           description: `Limited content in ${section} section`,
 566 |           evidence: [`Only ${sectionFiles.length} files`],
 567 |           suggestedFix: "Consider adding more comprehensive coverage",
 568 |           confidence: 75,
 569 |         });
 570 |       }
 571 |     }
 572 | 
 573 |     // Update completeness confidence
 574 |     result.confidence.breakdown.businessContextAlignment = Math.max(
 575 |       0,
 576 |       100 - missingSections.length * 25,
 577 |     );
 578 |   }
 579 | 
 580 |   private async validateDiataxisCompliance(
 581 |     contentPath: string,
 582 |     result: ValidationResult,
 583 |   ): Promise<void> {
 584 |     const files = await this.getMarkdownFiles(contentPath);
 585 | 
 586 |     for (const file of files) {
 587 |       const content = await fs.readFile(file, "utf-8");
 588 |       const section = this.identifyDiataxisSection(file);
 589 | 
 590 |       if (section) {
 591 |         await this.checkSectionCompliance(file, content, section, result);
 592 |       }
 593 |     }
 594 |   }
 595 | 
 596 |   private async validateApplicationStructureCompliance(
 597 |     contentPath: string,
 598 |     result: ValidationResult,
 599 |   ): Promise<void> {
 600 |     // Analyze application source code for Diataxis compliance
 601 |     await this.validateSourceCodeDocumentation(contentPath, result);
 602 |     await this.validateApplicationArchitecture(contentPath, result);
 603 |     await this.validateInlineDocumentationPatterns(contentPath, result);
 604 |   }
 605 | 
 606 |   private async validateSourceCodeDocumentation(
 607 |     contentPath: string,
 608 |     result: ValidationResult,
 609 |   ): Promise<void> {
 610 |     const sourceFiles = await this.getSourceFiles(contentPath);
 611 | 
 612 |     for (const file of sourceFiles) {
 613 |       const content = await fs.readFile(file, "utf-8");
 614 | 
 615 |       // Check for proper JSDoc/TSDoc documentation
 616 |       await this.checkInlineDocumentationQuality(file, content, result);
 617 | 
 618 |       // Check for README files and their structure
 619 |       if (file.endsWith("README.md")) {
 620 |         await this.validateReadmeStructure(file, content, result);
 621 |       }
 622 | 
 623 |       // Check for proper module/class documentation
 624 |       await this.checkModuleDocumentation(file, content, result);
 625 |     }
 626 |   }
 627 | 
 628 |   private async validateApplicationArchitecture(
 629 |     contentPath: string,
 630 |     result: ValidationResult,
 631 |   ): Promise<void> {
 632 |     // Check if the application structure supports different types of documentation
 633 |     const hasToolsDir = await this.pathExists(path.join(contentPath, "tools"));
 634 |     const hasTypesDir = await this.pathExists(path.join(contentPath, "types"));
 635 |     // Check for workflows directory (currently not used but may be useful for future validation)
 636 |     // const hasWorkflowsDir = await this.pathExists(path.join(contentPath, 'workflows'));
 637 | 
 638 |     if (!hasToolsDir) {
 639 |       result.issues.push({
 640 |         type: "warning",
 641 |         category: "compliance",
 642 |         location: { file: "application structure" },
 643 |         description:
 644 |           "No dedicated tools directory found - may impact reference documentation organization",
 645 |         evidence: ["Missing /tools directory"],
 646 |         suggestedFix:
 647 |           "Organize tools into dedicated directory for better reference documentation",
 648 |         confidence: 80,
 649 |       });
 650 |     }
 651 | 
 652 |     if (!hasTypesDir) {
 653 |       result.issues.push({
 654 |         type: "info",
 655 |         category: "compliance",
 656 |         location: { file: "application structure" },
 657 |         description:
 658 |           "No types directory found - may impact API reference documentation",
 659 |         evidence: ["Missing /types directory"],
 660 |         suggestedFix: "Consider organizing types for better API documentation",
 661 |         confidence: 70,
 662 |       });
 663 |     }
 664 |   }
 665 | 
 666 |   private async validateInlineDocumentationPatterns(
 667 |     contentPath: string,
 668 |     result: ValidationResult,
 669 |   ): Promise<void> {
 670 |     const sourceFiles = await this.getSourceFiles(contentPath);
 671 | 
 672 |     for (const file of sourceFiles) {
 673 |       const content = await fs.readFile(file, "utf-8");
 674 | 
 675 |       // Check for proper function documentation that could support tutorials
 676 |       const functions = this.extractFunctions(content);
 677 |       for (const func of functions) {
 678 |         if (func.isExported && !func.hasDocumentation) {
 679 |           result.issues.push({
 680 |             type: "warning",
 681 |             category: "compliance",
 682 |             location: { file: path.basename(file), line: func.line },
 683 |             description: `Exported function '${func.name}' lacks documentation`,
 684 |             evidence: [func.signature],
 685 |             suggestedFix:
 686 |               "Add JSDoc/TSDoc documentation to support tutorial and reference content",
 687 |             confidence: 85,
 688 |           });
 689 |         }
 690 |       }
 691 | 
 692 |       // Check for proper error handling documentation
 693 |       const errorPatterns = content.match(/throw new \w*Error/g);
 694 |       if (errorPatterns && errorPatterns.length > 0) {
 695 |         const hasErrorDocs =
 696 |           content.includes("@throws") || content.includes("@error");
 697 |         if (!hasErrorDocs) {
 698 |           result.issues.push({
 699 |             type: "info",
 700 |             category: "compliance",
 701 |             location: { file: path.basename(file) },
 702 |             description:
 703 |               "Error throwing code found without error documentation",
 704 |             evidence: errorPatterns,
 705 |             suggestedFix:
 706 |               "Document error conditions to support troubleshooting guides",
 707 |             confidence: 75,
 708 |           });
 709 |         }
 710 |       }
 711 |     }
 712 |   }
 713 | 
 714 |   private identifyDiataxisSection(filePath: string): string | null {
 715 |     const sections = ["tutorials", "how-to", "reference", "explanation"];
 716 | 
 717 |     for (const section of sections) {
 718 |       if (filePath.includes(`/${section}/`)) {
 719 |         return section;
 720 |       }
 721 |     }
 722 | 
 723 |     return null;
 724 |   }
 725 | 
 726 |   private async checkSectionCompliance(
 727 |     filePath: string,
 728 |     content: string,
 729 |     section: string,
 730 |     result: ValidationResult,
 731 |   ): Promise<void> {
 732 |     const complianceRules = this.getDiataxisComplianceRules(section);
 733 | 
 734 |     for (const rule of complianceRules) {
 735 |       if (!rule.check(content)) {
 736 |         result.issues.push({
 737 |           type: "warning",
 738 |           category: "compliance",
 739 |           location: { file: path.basename(filePath), section },
 740 |           description: rule.message,
 741 |           evidence: [rule.evidence?.(content) || ""],
 742 |           suggestedFix: rule.fix,
 743 |           confidence: rule.confidence,
 744 |         });
 745 |       }
 746 |     }
 747 |   }
 748 | 
 749 |   private getDiataxisComplianceRules(section: string) {
 750 |     const rules: any = {
 751 |       tutorials: [
 752 |         {
 753 |           check: (content: string) =>
 754 |             content.includes("## Prerequisites") ||
 755 |             content.includes("## Requirements"),
 756 |           message: "Tutorial should include prerequisites section",
 757 |           fix: "Add a prerequisites or requirements section",
 758 |           confidence: 90,
 759 |         },
 760 |         {
 761 |           check: (content: string) => /step|Step|STEP/.test(content),
 762 |           message: "Tutorial should be organized in clear steps",
 763 |           fix: "Structure content with numbered steps or clear progression",
 764 |           confidence: 85,
 765 |         },
 766 |         {
 767 |           check: (content: string) => content.includes("```"),
 768 |           message: "Tutorial should include practical code examples",
 769 |           fix: "Add code blocks with working examples",
 770 |           confidence: 80,
 771 |         },
 772 |       ],
 773 |       "how-to": [
 774 |         {
 775 |           check: (content: string) => /how to|How to|HOW TO/.test(content),
 776 |           message: "How-to guide should focus on specific tasks",
 777 |           fix: "Frame content around achieving specific goals",
 778 |           confidence: 75,
 779 |         },
 780 |         {
 781 |           check: (content: string) => content.length > 500,
 782 |           message: "How-to guide should provide detailed guidance",
 783 |           fix: "Expand with more detailed instructions",
 784 |           confidence: 70,
 785 |         },
 786 |       ],
 787 |       reference: [
 788 |         {
 789 |           check: (content: string) => /##|###/.test(content),
 790 |           message: "Reference should be well-structured with clear sections",
 791 |           fix: "Add proper headings and organization",
 792 |           confidence: 95,
 793 |         },
 794 |         {
 795 |           check: (content: string) => /\|.*\|/.test(content),
 796 |           message: "Reference should include tables for structured information",
 797 |           fix: "Consider using tables for parameters, options, etc.",
 798 |           confidence: 60,
 799 |         },
 800 |       ],
 801 |       explanation: [
 802 |         {
 803 |           check: (content: string) =>
 804 |             content.includes("why") || content.includes("Why"),
 805 |           message: 'Explanation should address the "why" behind concepts',
 806 |           fix: "Include rationale and context for decisions/concepts",
 807 |           confidence: 80,
 808 |         },
 809 |         {
 810 |           check: (content: string) => content.length > 800,
 811 |           message: "Explanation should provide in-depth coverage",
 812 |           fix: "Expand with more comprehensive explanation",
 813 |           confidence: 70,
 814 |         },
 815 |       ],
 816 |     };
 817 | 
 818 |     return rules[section] || [];
 819 |   }
 820 | 
 821 |   private async validateCodeExamples(
 822 |     contentPath: string,
 823 |   ): Promise<CodeValidationResult> {
 824 |     const files = await this.getMarkdownFiles(contentPath);
 825 |     const allExamples: ExampleValidation[] = [];
 826 | 
 827 |     for (const file of files) {
 828 |       const content = await fs.readFile(file, "utf-8");
 829 |       const codeBlocks = this.extractCodeBlocks(content);
 830 | 
 831 |       for (const block of codeBlocks) {
 832 |         if (this.isValidatableLanguage(block.language)) {
 833 |           const validation = await this.validateCodeBlock(block, file);
 834 |           allExamples.push(validation);
 835 |         }
 836 |       }
 837 |     }
 838 | 
 839 |     return {
 840 |       overallSuccess: allExamples.every((e) => e.compilationSuccess),
 841 |       exampleResults: allExamples,
 842 |       confidence: this.calculateCodeValidationConfidence(allExamples),
 843 |     };
 844 |   }
 845 | 
 846 |   private extractCodeBlocks(
 847 |     content: string,
 848 |   ): Array<{ language: string; code: string; id: string }> {
 849 |     const codeBlockPattern = /```(\w+)?\n([\s\S]*?)```/g;
 850 |     const blocks: Array<{ language: string; code: string; id: string }> = [];
 851 |     let match;
 852 |     let index = 0;
 853 | 
 854 |     while ((match = codeBlockPattern.exec(content)) !== null) {
 855 |       blocks.push({
 856 |         language: match[1] || "text",
 857 |         code: match[2].trim(),
 858 |         id: `block-${index++}`,
 859 |       });
 860 |     }
 861 | 
 862 |     return blocks;
 863 |   }
 864 | 
 865 |   private isValidatableLanguage(language: string): boolean {
 866 |     const validatable = [
 867 |       "javascript",
 868 |       "typescript",
 869 |       "js",
 870 |       "ts",
 871 |       "json",
 872 |       "bash",
 873 |       "sh",
 874 |     ];
 875 |     return validatable.includes(language.toLowerCase());
 876 |   }
 877 | 
 878 |   private async validateCodeBlock(
 879 |     block: { language: string; code: string; id: string },
 880 |     filePath: string,
 881 |   ): Promise<ExampleValidation> {
 882 |     const validation: ExampleValidation = {
 883 |       example: block.id,
 884 |       compilationSuccess: false,
 885 |       executionSuccess: false,
 886 |       issues: [],
 887 |       confidence: 0,
 888 |     };
 889 | 
 890 |     try {
 891 |       if (block.language === "typescript" || block.language === "ts") {
 892 |         await this.validateTypeScriptCode(block.code, validation);
 893 | 
 894 |         // Use execution simulation for additional validation if available
 895 |         if (
 896 |           this.useExecutionSimulation &&
 897 |           this.executionSimulator &&
 898 |           validation.compilationSuccess
 899 |         ) {
 900 |           await this.enhanceWithExecutionSimulation(
 901 |             block.code,
 902 |             validation,
 903 |             filePath,
 904 |           );
 905 |         }
 906 |       } else if (block.language === "javascript" || block.language === "js") {
 907 |         await this.validateJavaScriptCode(block.code, validation);
 908 | 
 909 |         // Use execution simulation for additional validation if available
 910 |         if (
 911 |           this.useExecutionSimulation &&
 912 |           this.executionSimulator &&
 913 |           validation.compilationSuccess
 914 |         ) {
 915 |           await this.enhanceWithExecutionSimulation(
 916 |             block.code,
 917 |             validation,
 918 |             filePath,
 919 |           );
 920 |         }
 921 |       } else if (block.language === "json") {
 922 |         await this.validateJSONCode(block.code, validation);
 923 |       } else if (block.language === "bash" || block.language === "sh") {
 924 |         await this.validateBashCode(block.code, validation);
 925 |       }
 926 |     } catch (error: any) {
 927 |       validation.issues.push({
 928 |         type: "error",
 929 |         category: "accuracy",
 930 |         location: { file: path.basename(filePath) },
 931 |         description: `Code validation failed: ${error.message}`,
 932 |         evidence: [block.code.substring(0, 100)],
 933 |         suggestedFix: "Review and fix syntax errors",
 934 |         confidence: 95,
 935 |       });
 936 |     }
 937 | 
 938 |     return validation;
 939 |   }
 940 | 
 941 |   /**
 942 |    * Enhance validation with execution simulation results
 943 |    */
 944 |   private async enhanceWithExecutionSimulation(
 945 |     code: string,
 946 |     validation: ExampleValidation,
 947 |     filePath: string,
 948 |   ): Promise<void> {
 949 |     if (!this.executionSimulator) return;
 950 | 
 951 |     try {
 952 |       const simResult = await this.executionSimulator.validateExample(
 953 |         code,
 954 |         code,
 955 |       );
 956 | 
 957 |       // Add simulation-detected issues to validation
 958 |       for (const issue of simResult.issues) {
 959 |         validation.issues.push({
 960 |           type: issue.severity === "error" ? "error" : "warning",
 961 |           category: "accuracy",
 962 |           location: {
 963 |             file: path.basename(filePath),
 964 |             line: issue.location.line,
 965 |           },
 966 |           description: `[Simulation] ${issue.description}`,
 967 |           evidence: [issue.codeSnippet || ""],
 968 |           suggestedFix: issue.suggestion,
 969 |           confidence: Math.round(simResult.trace.confidenceScore * 100),
 970 |         });
 971 |       }
 972 | 
 973 |       // Update execution success based on simulation
 974 |       validation.executionSuccess = simResult.isValid;
 975 | 
 976 |       // Adjust confidence based on simulation confidence
 977 |       if (simResult.trace.confidenceScore > 0) {
 978 |         validation.confidence = Math.round(
 979 |           (validation.confidence + simResult.trace.confidenceScore * 100) / 2,
 980 |         );
 981 |       }
 982 |     } catch (error) {
 983 |       // Simulation is best-effort, don't fail validation on simulation errors
 984 |       console.warn("Execution simulation failed:", error);
 985 |     }
 986 |   }
 987 | 
 988 |   private async validateTypeScriptCode(
 989 |     code: string,
 990 |     validation: ExampleValidation,
 991 |   ): Promise<void> {
 992 |     // Ensure temp directory exists
 993 |     await fs.mkdir(this.tempDir, { recursive: true });
 994 | 
 995 |     const tempFile = path.join(this.tempDir, `temp-${Date.now()}.ts`);
 996 | 
 997 |     try {
 998 |       // Write code to temporary file
 999 |       await fs.writeFile(tempFile, code, "utf-8");
1000 | 
1001 |       // Try to compile with TypeScript
1002 |       const { stderr } = await execAsync(
1003 |         `npx tsc --noEmit --skipLibCheck ${tempFile}`,
1004 |       );
1005 | 
1006 |       if (stderr && stderr.includes("error")) {
1007 |         validation.issues.push({
1008 |           type: "error",
1009 |           category: "accuracy",
1010 |           location: { file: "code-example" },
1011 |           description: "TypeScript compilation error",
1012 |           evidence: [stderr],
1013 |           suggestedFix: "Fix TypeScript syntax and type errors",
1014 |           confidence: 90,
1015 |         });
1016 |       } else {
1017 |         validation.compilationSuccess = true;
1018 |         validation.confidence = 85;
1019 |       }
1020 |     } catch (error: any) {
1021 |       if (error.stderr && error.stderr.includes("error")) {
1022 |         validation.issues.push({
1023 |           type: "error",
1024 |           category: "accuracy",
1025 |           location: { file: "code-example" },
1026 |           description: "TypeScript compilation failed",
1027 |           evidence: [error.stderr],
1028 |           suggestedFix: "Fix compilation errors",
1029 |           confidence: 95,
1030 |         });
1031 |       }
1032 |     } finally {
1033 |       // Clean up temp file
1034 |       try {
1035 |         await fs.unlink(tempFile);
1036 |       } catch {
1037 |         // Ignore cleanup errors
1038 |       }
1039 |     }
1040 |   }
1041 | 
1042 |   private async validateJavaScriptCode(
1043 |     code: string,
1044 |     validation: ExampleValidation,
1045 |   ): Promise<void> {
1046 |     try {
1047 |       // Basic syntax check using Node.js
1048 |       new Function(code);
1049 |       validation.compilationSuccess = true;
1050 |       validation.confidence = 75;
1051 |     } catch (error: any) {
1052 |       validation.issues.push({
1053 |         type: "error",
1054 |         category: "accuracy",
1055 |         location: { file: "code-example" },
1056 |         description: `JavaScript syntax error: ${error.message}`,
1057 |         evidence: [code.substring(0, 100)],
1058 |         suggestedFix: "Fix JavaScript syntax errors",
1059 |         confidence: 90,
1060 |       });
1061 |     }
1062 |   }
1063 | 
1064 |   private async validateJSONCode(
1065 |     code: string,
1066 |     validation: ExampleValidation,
1067 |   ): Promise<void> {
1068 |     try {
1069 |       JSON.parse(code);
1070 |       validation.compilationSuccess = true;
1071 |       validation.confidence = 95;
1072 |     } catch (error: any) {
1073 |       validation.issues.push({
1074 |         type: "error",
1075 |         category: "accuracy",
1076 |         location: { file: "code-example" },
1077 |         description: `Invalid JSON: ${error.message}`,
1078 |         evidence: [code.substring(0, 100)],
1079 |         suggestedFix: "Fix JSON syntax errors",
1080 |         confidence: 100,
1081 |       });
1082 |     }
1083 |   }
1084 | 
1085 |   private async validateBashCode(
1086 |     code: string,
1087 |     validation: ExampleValidation,
1088 |   ): Promise<void> {
1089 |     // Basic bash syntax validation
1090 |     const lines = code
1091 |       .split("\n")
1092 |       .filter((line) => line.trim() && !line.startsWith("#"));
1093 | 
1094 |     for (const line of lines) {
1095 |       // Check for basic syntax issues
1096 |       if (line.includes("&&") && line.includes("||")) {
1097 |         validation.issues.push({
1098 |           type: "warning",
1099 |           category: "accuracy",
1100 |           location: { file: "code-example" },
1101 |           description: "Complex command chaining may be confusing",
1102 |           evidence: [line],
1103 |           suggestedFix:
1104 |             "Consider breaking into separate commands or adding explanation",
1105 |           confidence: 60,
1106 |         });
1107 |       }
1108 | 
1109 |       // Check for unquoted variables in dangerous contexts
1110 |       if (line.includes("rm") && /\$\w+/.test(line) && !/'.*\$.*'/.test(line)) {
1111 |         validation.issues.push({
1112 |           type: "warning",
1113 |           category: "accuracy",
1114 |           location: { file: "code-example" },
1115 |           description: "Unquoted variable in potentially dangerous command",
1116 |           evidence: [line],
1117 |           suggestedFix: "Quote variables to prevent word splitting",
1118 |           confidence: 80,
1119 |         });
1120 |       }
1121 |     }
1122 | 
1123 |     validation.compilationSuccess =
1124 |       validation.issues.filter((i) => i.type === "error").length === 0;
1125 |     validation.confidence = validation.compilationSuccess ? 70 : 20;
1126 |   }
1127 | 
1128 |   private calculateCodeValidationConfidence(
1129 |     examples: ExampleValidation[],
1130 |   ): number {
1131 |     if (examples.length === 0) return 0;
1132 | 
1133 |     const totalConfidence = examples.reduce(
1134 |       (sum, ex) => sum + ex.confidence,
1135 |       0,
1136 |     );
1137 |     return Math.round(totalConfidence / examples.length);
1138 |   }
1139 | 
1140 |   public async getMarkdownFiles(
1141 |     contentPath: string,
1142 |     maxDepth: number = 5,
1143 |   ): Promise<string[]> {
1144 |     const files: string[] = [];
1145 |     const excludedDirs = new Set([
1146 |       "node_modules",
1147 |       ".git",
1148 |       "dist",
1149 |       "build",
1150 |       ".next",
1151 |       ".nuxt",
1152 |       "coverage",
1153 |       ".tmp",
1154 |       "tmp",
1155 |       ".cache",
1156 |       ".vscode",
1157 |       ".idea",
1158 |       "logs",
1159 |       ".logs",
1160 |       ".npm",
1161 |       ".yarn",
1162 |     ]);
1163 | 
1164 |     const scan = async (
1165 |       dir: string,
1166 |       currentDepth: number = 0,
1167 |     ): Promise<void> => {
1168 |       if (currentDepth > maxDepth) return;
1169 | 
1170 |       try {
1171 |         const entries = await fs.readdir(dir, { withFileTypes: true });
1172 | 
1173 |         for (const entry of entries) {
1174 |           const fullPath = path.join(dir, entry.name);
1175 | 
1176 |           if (entry.isDirectory()) {
1177 |             // Skip excluded directories
1178 |             if (excludedDirs.has(entry.name) || entry.name.startsWith(".")) {
1179 |               continue;
1180 |             }
1181 | 
1182 |             // Prevent symlink loops
1183 |             try {
1184 |               const stats = await fs.lstat(fullPath);
1185 |               if (stats.isSymbolicLink()) {
1186 |                 continue;
1187 |               }
1188 |             } catch {
1189 |               continue;
1190 |             }
1191 | 
1192 |             await scan(fullPath, currentDepth + 1);
1193 |           } else if (entry.name.endsWith(".md")) {
1194 |             files.push(fullPath);
1195 | 
1196 |             // Limit total files to prevent memory issues
1197 |             if (files.length > 500) {
1198 |               console.warn("Markdown file limit reached (500), stopping scan");
1199 |               return;
1200 |             }
1201 |           }
1202 |         }
1203 |       } catch (error) {
1204 |         // Skip directories that can't be read
1205 |         console.warn(`Warning: Could not read directory ${dir}:`, error);
1206 |       }
1207 |     };
1208 | 
1209 |     try {
1210 |       await scan(contentPath);
1211 |     } catch (error) {
1212 |       console.warn("Error scanning directory:", error);
1213 |     }
1214 | 
1215 |     return files;
1216 |   }
1217 | 
1218 |   private async getSourceFiles(
1219 |     contentPath: string,
1220 |     maxDepth: number = 5,
1221 |   ): Promise<string[]> {
1222 |     const files: string[] = [];
1223 |     const excludedDirs = new Set([
1224 |       "node_modules",
1225 |       ".git",
1226 |       "dist",
1227 |       "build",
1228 |       ".next",
1229 |       ".nuxt",
1230 |       "coverage",
1231 |       ".tmp",
1232 |       "tmp",
1233 |       ".cache",
1234 |       ".vscode",
1235 |       ".idea",
1236 |       "logs",
1237 |       ".logs",
1238 |       ".npm",
1239 |       ".yarn",
1240 |     ]);
1241 | 
1242 |     const scan = async (
1243 |       dir: string,
1244 |       currentDepth: number = 0,
1245 |     ): Promise<void> => {
1246 |       if (currentDepth > maxDepth) return;
1247 | 
1248 |       try {
1249 |         const entries = await fs.readdir(dir, { withFileTypes: true });
1250 | 
1251 |         for (const entry of entries) {
1252 |           const fullPath = path.join(dir, entry.name);
1253 | 
1254 |           if (entry.isDirectory()) {
1255 |             // Skip excluded directories
1256 |             if (excludedDirs.has(entry.name) || entry.name.startsWith(".")) {
1257 |               continue;
1258 |             }
1259 | 
1260 |             // Prevent symlink loops
1261 |             try {
1262 |               const stats = await fs.lstat(fullPath);
1263 |               if (stats.isSymbolicLink()) {
1264 |                 continue;
1265 |               }
1266 |             } catch {
1267 |               continue;
1268 |             }
1269 | 
1270 |             await scan(fullPath, currentDepth + 1);
1271 |           } else if (
1272 |             entry.name.endsWith(".ts") ||
1273 |             entry.name.endsWith(".js") ||
1274 |             entry.name.endsWith(".md")
1275 |           ) {
1276 |             files.push(fullPath);
1277 | 
1278 |             // Limit total files to prevent memory issues
1279 |             if (files.length > 1000) {
1280 |               console.warn("File limit reached (1000), stopping scan");
1281 |               return;
1282 |             }
1283 |           }
1284 |         }
1285 |       } catch (error) {
1286 |         // Skip directories that can't be read
1287 |         console.warn(`Warning: Could not read directory ${dir}:`, error);
1288 |       }
1289 |     };
1290 | 
1291 |     try {
1292 |       await scan(contentPath);
1293 |     } catch (error) {
1294 |       console.warn("Error scanning directory:", error);
1295 |     }
1296 | 
1297 |     return files;
1298 |   }
1299 | 
1300 |   private async pathExists(filePath: string): Promise<boolean> {
1301 |     try {
1302 |       await fs.access(filePath);
1303 |       return true;
1304 |     } catch {
1305 |       return false;
1306 |     }
1307 |   }
1308 | 
1309 |   private extractFunctions(content: string): Array<{
1310 |     name: string;
1311 |     line: number;
1312 |     signature: string;
1313 |     isExported: boolean;
1314 |     hasDocumentation: boolean;
1315 |   }> {
1316 |     const functions: Array<{
1317 |       name: string;
1318 |       line: number;
1319 |       signature: string;
1320 |       isExported: boolean;
1321 |       hasDocumentation: boolean;
1322 |     }> = [];
1323 |     const lines = content.split("\n");
1324 | 
1325 |     for (let i = 0; i < lines.length; i++) {
1326 |       const line = lines[i];
1327 | 
1328 |       // Match function declarations and exports
1329 |       const functionMatch = line.match(
1330 |         /^(export\s+)?(async\s+)?function\s+(\w+)/,
1331 |       );
1332 |       const arrowMatch = line.match(
1333 |         /^(export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(async\s+)?\(/,
1334 |       );
1335 | 
1336 |       if (functionMatch) {
1337 |         const name = functionMatch[3];
1338 |         const isExported = !!functionMatch[1];
1339 |         const hasDocumentation = this.checkForDocumentation(lines, i);
1340 | 
1341 |         functions.push({
1342 |           name,
1343 |           line: i + 1,
1344 |           signature: line.trim(),
1345 |           isExported,
1346 |           hasDocumentation,
1347 |         });
1348 |       } else if (arrowMatch) {
1349 |         const name = arrowMatch[2];
1350 |         const isExported = !!arrowMatch[1];
1351 |         const hasDocumentation = this.checkForDocumentation(lines, i);
1352 | 
1353 |         functions.push({
1354 |           name,
1355 |           line: i + 1,
1356 |           signature: line.trim(),
1357 |           isExported,
1358 |           hasDocumentation,
1359 |         });
1360 |       }
1361 |     }
1362 | 
1363 |     return functions;
1364 |   }
1365 | 
1366 |   private async checkInlineDocumentationQuality(
1367 |     _file: string,
1368 |     _content: string,
1369 |     _result: ValidationResult,
1370 |   ): Promise<void> {
1371 |     // Implementation for checking JSDoc/TSDoc quality
1372 |     // This could check for proper parameter documentation, return types, etc.
1373 |   }
1374 | 
1375 |   private async validateReadmeStructure(
1376 |     _file: string,
1377 |     content: string,
1378 |     result: ValidationResult,
1379 |   ): Promise<void> {
1380 |     // Check if README follows good structure
1381 |     const hasTitle = /^#\s+/.test(content);
1382 |     const hasDescription =
1383 |       content.includes("## Description") || content.includes("## Overview");
1384 |     const hasInstallation =
1385 |       content.includes("## Installation") || content.includes("## Setup");
1386 |     const hasUsage =
1387 |       content.includes("## Usage") || content.includes("## Getting Started");
1388 | 
1389 |     if (!hasTitle) {
1390 |       result.issues.push({
1391 |         type: "warning",
1392 |         category: "compliance",
1393 |         location: { file: "README.md" },
1394 |         description: "README missing clear title",
1395 |         evidence: ["No H1 heading found"],
1396 |         suggestedFix: "Add clear title with # heading",
1397 |         confidence: 90,
1398 |       });
1399 |     }
1400 | 
1401 |     if (!hasDescription && !hasInstallation && !hasUsage) {
1402 |       result.issues.push({
1403 |         type: "warning",
1404 |         category: "compliance",
1405 |         location: { file: "README.md" },
1406 |         description:
1407 |           "README lacks essential sections (description, installation, usage)",
1408 |         evidence: ["Missing standard README sections"],
1409 |         suggestedFix:
1410 |           "Add sections for description, installation, and usage following Diataxis principles",
1411 |         confidence: 85,
1412 |       });
1413 |     }
1414 |   }
1415 | 
1416 |   private async checkModuleDocumentation(
1417 |     _file: string,
1418 |     _content: string,
1419 |     _result: ValidationResult,
1420 |   ): Promise<void> {
1421 |     // Implementation for checking module-level documentation
1422 |     // This could check for file-level JSDoc, proper exports documentation, etc.
1423 |   }
1424 | 
1425 |   private checkForDocumentation(
1426 |     lines: string[],
1427 |     functionLineIndex: number,
1428 |   ): boolean {
1429 |     // Look backwards from the function line to find documentation
1430 |     let checkIndex = functionLineIndex - 1;
1431 | 
1432 |     // Skip empty lines
1433 |     while (checkIndex >= 0 && lines[checkIndex].trim() === "") {
1434 |       checkIndex--;
1435 |     }
1436 | 
1437 |     // Check if we found the end of a JSDoc comment
1438 |     if (checkIndex >= 0 && lines[checkIndex].trim() === "*/") {
1439 |       // Look backwards to find the start of the JSDoc block
1440 |       let jsDocStart = checkIndex;
1441 |       while (jsDocStart >= 0) {
1442 |         const line = lines[jsDocStart].trim();
1443 |         if (line.startsWith("/**")) {
1444 |           return true; // Found complete JSDoc block
1445 |         }
1446 |         if (!line.startsWith("*") && line !== "*/") {
1447 |           break; // Not part of JSDoc block
1448 |         }
1449 |         jsDocStart--;
1450 |       }
1451 |     }
1452 | 
1453 |     // Also check for single-line JSDoc comments
1454 |     if (
1455 |       checkIndex >= 0 &&
1456 |       lines[checkIndex].trim().startsWith("/**") &&
1457 |       lines[checkIndex].includes("*/")
1458 |     ) {
1459 |       return true;
1460 |     }
1461 | 
1462 |     return false;
1463 |   }
1464 | 
1465 |   private async shouldAnalyzeApplicationCode(
1466 |     contentPath: string,
1467 |   ): Promise<boolean> {
1468 |     // Check if the path contains application source code vs documentation
1469 |     const hasSrcDir = await this.pathExists(path.join(contentPath, "src"));
1470 |     const hasPackageJson = await this.pathExists(
1471 |       path.join(contentPath, "package.json"),
1472 |     );
1473 |     const hasTypescriptFiles = (await this.getSourceFiles(contentPath)).some(
1474 |       (file) => file.endsWith(".ts"),
1475 |     );
1476 | 
1477 |     // If path ends with 'src' or is a project root with src/, analyze as application code
1478 |     if (
1479 |       contentPath.endsWith("/src") ||
1480 |       contentPath.endsWith("\\src") ||
1481 |       (hasSrcDir && hasPackageJson)
1482 |     ) {
1483 |       return true;
1484 |     }
1485 | 
1486 |     // If path contains TypeScript/JavaScript files and package.json, treat as application code
1487 |     if (hasTypescriptFiles && hasPackageJson) {
1488 |       return true;
1489 |     }
1490 | 
1491 |     // If path is specifically a documentation directory, analyze as documentation
1492 |     if (contentPath.includes("/docs") || contentPath.includes("\\docs")) {
1493 |       return false;
1494 |     }
1495 | 
1496 |     return false;
1497 |   }
1498 | 
1499 |   private async analyzeDiataxisStructure(
1500 |     contentPath: string,
1501 |   ): Promise<{ sections: string[] }> {
1502 |     const sections: string[] = [];
1503 | 
1504 |     try {
1505 |       const entries = await fs.readdir(contentPath, { withFileTypes: true });
1506 | 
1507 |       for (const entry of entries) {
1508 |         if (entry.isDirectory()) {
1509 |           const dirName = entry.name;
1510 |           if (
1511 |             ["tutorials", "how-to", "reference", "explanation"].includes(
1512 |               dirName,
1513 |             )
1514 |           ) {
1515 |             sections.push(dirName);
1516 |           }
1517 |         }
1518 |       }
1519 |     } catch {
1520 |       // Directory doesn't exist
1521 |     }
1522 | 
1523 |     return { sections };
1524 |   }
1525 | 
1526 |   private updateAccuracyConfidence(result: ValidationResult): void {
1527 |     const errorCount = result.issues.filter((i) => i.type === "error").length;
1528 |     const warningCount = result.issues.filter(
1529 |       (i) => i.type === "warning",
1530 |     ).length;
1531 | 
1532 |     // Base confidence starts high and decreases with issues
1533 |     let confidence = 95;
1534 |     confidence -= errorCount * 20;
1535 |     confidence -= warningCount * 5;
1536 |     confidence = Math.max(0, confidence);
1537 | 
1538 |     result.confidence.breakdown.technologyDetection = confidence;
1539 |   }
1540 | 
1541 |   private calculateOverallMetrics(result: ValidationResult): void {
1542 |     const breakdown = result.confidence.breakdown;
1543 |     const values = Object.values(breakdown).filter((v) => v > 0);
1544 | 
1545 |     if (values.length > 0) {
1546 |       result.confidence.overall = Math.round(
1547 |         values.reduce((a, b) => a + b, 0) / values.length,
1548 |       );
1549 |     }
1550 | 
1551 |     // Determine overall success
1552 |     const criticalIssues = result.issues.filter(
1553 |       (i) => i.type === "error",
1554 |     ).length;
1555 |     result.success = criticalIssues === 0;
1556 | 
1557 |     // Add risk factors based on issues
1558 |     if (criticalIssues > 0) {
1559 |       result.confidence.riskFactors.push({
1560 |         type: "high",
1561 |         category: "accuracy",
1562 |         description: `${criticalIssues} critical accuracy issues found`,
1563 |         impact: "Users may encounter broken examples or incorrect information",
1564 |         mitigation: "Fix all critical issues before publication",
1565 |       });
1566 |     }
1567 | 
1568 |     const uncertaintyCount = result.uncertainties.length;
1569 |     if (uncertaintyCount > 5) {
1570 |       result.confidence.riskFactors.push({
1571 |         type: "medium",
1572 |         category: "completeness",
1573 |         description: `${uncertaintyCount} areas requiring clarification`,
1574 |         impact: "Documentation may lack specificity for user context",
1575 |         mitigation: "Address high-priority uncertainties with user input",
1576 |       });
1577 |     }
1578 |   }
1579 | 
1580 |   private generateRecommendations(
1581 |     result: ValidationResult,
1582 |     _options: ValidationOptions,
1583 |   ): void {
1584 |     const recommendations: string[] = [];
1585 |     const nextSteps: string[] = [];
1586 | 
1587 |     // Generate recommendations based on issues found
1588 |     const errorCount = result.issues.filter((i) => i.type === "error").length;
1589 |     if (errorCount > 0) {
1590 |       recommendations.push(
1591 |         `Fix ${errorCount} critical accuracy issues before publication`,
1592 |       );
1593 |       nextSteps.push("Review and resolve all error-level validation issues");
1594 |     }
1595 | 
1596 |     const warningCount = result.issues.filter(
1597 |       (i) => i.type === "warning",
1598 |     ).length;
1599 |     if (warningCount > 0) {
1600 |       recommendations.push(
1601 |         `Address ${warningCount} potential accuracy concerns`,
1602 |       );
1603 |       nextSteps.push(
1604 |         "Review warning-level issues and apply fixes where appropriate",
1605 |       );
1606 |     }
1607 | 
1608 |     const uncertaintyCount = result.uncertainties.filter(
1609 |       (u) => u.severity === "high" || u.severity === "critical",
1610 |     ).length;
1611 |     if (uncertaintyCount > 0) {
1612 |       recommendations.push(
1613 |         `Clarify ${uncertaintyCount} high-uncertainty areas`,
1614 |       );
1615 |       nextSteps.push("Gather user input on areas flagged for clarification");
1616 |     }
1617 | 
1618 |     // Code validation recommendations
1619 |     if (result.codeValidation && !result.codeValidation.overallSuccess) {
1620 |       recommendations.push(
1621 |         "Fix code examples that fail compilation or execution tests",
1622 |       );
1623 |       nextSteps.push(
1624 |         "Test all code examples in appropriate development environment",
1625 |       );
1626 |     }
1627 | 
1628 |     // Completeness recommendations
1629 |     const missingCompliance = result.issues.filter(
1630 |       (i) => i.category === "compliance",
1631 |     ).length;
1632 |     if (missingCompliance > 0) {
1633 |       recommendations.push(
1634 |         "Improve Diataxis framework compliance for better user experience",
1635 |       );
1636 |       nextSteps.push(
1637 |         "Restructure content to better align with Diataxis principles",
1638 |       );
1639 |     }
1640 | 
1641 |     // General recommendations based on confidence level
1642 |     if (result.confidence.overall < 70) {
1643 |       recommendations.push(
1644 |         "Overall confidence is below recommended threshold - consider comprehensive review",
1645 |       );
1646 |       nextSteps.push(
1647 |         "Conduct manual review of generated content before publication",
1648 |       );
1649 |     }
1650 | 
1651 |     if (recommendations.length === 0) {
1652 |       recommendations.push("Content validation passed - ready for publication");
1653 |       nextSteps.push("Deploy documentation and monitor for user feedback");
1654 |     }
1655 | 
1656 |     result.recommendations = recommendations;
1657 |     result.nextSteps = nextSteps;
1658 |   }
1659 | }
1660 | 
1661 | export const validateDiataxisContent: Tool = {
1662 |   name: "validate_diataxis_content",
1663 |   description:
1664 |     "Validate the accuracy, completeness, and compliance of generated Diataxis documentation",
1665 |   inputSchema: {
1666 |     type: "object",
1667 |     properties: {
1668 |       contentPath: {
1669 |         type: "string",
1670 |         description: "Path to the documentation directory to validate",
1671 |       },
1672 |       analysisId: {
1673 |         type: "string",
1674 |         description:
1675 |           "Optional repository analysis ID for context-aware validation",
1676 |       },
1677 |       validationType: {
1678 |         type: "string",
1679 |         enum: ["accuracy", "completeness", "compliance", "all"],
1680 |         default: "all",
1681 |         description: "Type of validation to perform",
1682 |       },
1683 |       includeCodeValidation: {
1684 |         type: "boolean",
1685 |         default: true,
1686 |         description: "Whether to validate code examples for correctness",
1687 |       },
1688 |       confidence: {
1689 |         type: "string",
1690 |         enum: ["strict", "moderate", "permissive"],
1691 |         default: "moderate",
1692 |         description:
1693 |           "Validation confidence level - stricter levels catch more issues",
1694 |       },
1695 |     },
1696 |     required: ["contentPath"],
1697 |   },
1698 | };
1699 | 
1700 | /**
1701 |  * Validates Diataxis-compliant documentation content for accuracy, completeness, and compliance.
1702 |  *
1703 |  * Performs comprehensive validation of documentation content including accuracy verification,
1704 |  * completeness assessment, compliance checking, and code example validation. Uses advanced
1705 |  * confidence scoring and risk assessment to provide detailed validation results with
1706 |  * actionable recommendations.
1707 |  *
1708 |  * @param args - The validation parameters
1709 |  * @param args.contentPath - Path to the documentation content directory
1710 |  * @param args.analysisId - Optional repository analysis ID for context-aware validation
1711 |  * @param args.validationType - Type of validation to perform: "accuracy", "completeness", "compliance", or "all"
1712 |  * @param args.includeCodeValidation - Whether to validate code examples and syntax
1713 |  * @param args.confidence - Validation confidence level: "strict", "moderate", or "permissive"
1714 |  *
1715 |  * @returns Promise resolving to comprehensive validation results
1716 |  * @returns success - Whether validation passed overall
1717 |  * @returns confidence - Confidence metrics and risk assessment
1718 |  * @returns issues - Array of validation issues found
1719 |  * @returns uncertainties - Areas requiring clarification
1720 |  * @returns codeValidation - Code example validation results
1721 |  * @returns recommendations - Suggested improvements
1722 |  * @returns nextSteps - Recommended next actions
1723 |  *
1724 |  * @throws {Error} When content path is inaccessible
1725 |  * @throws {Error} When validation processing fails
1726 |  *
1727 |  * @example
1728 |  * ```typescript
1729 |  * // Comprehensive validation
1730 |  * const result = await handleValidateDiataxisContent({
1731 |  *   contentPath: "./docs",
1732 |  *   validationType: "all",
1733 |  *   includeCodeValidation: true,
1734 |  *   confidence: "moderate"
1735 |  * });
1736 |  *
1737 |  * console.log(`Validation success: ${result.success}`);
1738 |  * console.log(`Overall confidence: ${result.confidence.overall}%`);
1739 |  * console.log(`Issues found: ${result.issues.length}`);
1740 |  *
1741 |  * // Strict accuracy validation
1742 |  * const accuracy = await handleValidateDiataxisContent({
1743 |  *   contentPath: "./docs",
1744 |  *   validationType: "accuracy",
1745 |  *   confidence: "strict"
1746 |  * });
1747 |  * ```
1748 |  *
1749 |  * @since 1.0.0
1750 |  */
1751 | export async function handleValidateDiataxisContent(
1752 |   args: any,
1753 |   context?: any,
1754 | ): Promise<ValidationResult> {
1755 |   await context?.info?.("🔍 Starting Diataxis content validation...");
1756 | 
1757 |   const validator = new ContentAccuracyValidator();
1758 | 
1759 |   // Add timeout protection to prevent infinite hangs
1760 |   const timeoutMs = 120000; // 2 minutes
1761 |   let timeoutHandle: NodeJS.Timeout;
1762 |   const timeoutPromise = new Promise<ValidationResult>((_, reject) => {
1763 |     timeoutHandle = setTimeout(() => {
1764 |       reject(
1765 |         new Error(
1766 |           `Validation timed out after ${
1767 |             timeoutMs / 1000
1768 |           } seconds. This may be due to a large directory structure. Try validating a smaller subset or specific directory.`,
1769 |         ),
1770 |       );
1771 |     }, timeoutMs);
1772 |   });
1773 | 
1774 |   const validationPromise = validator.validateContent(args, context);
1775 | 
1776 |   try {
1777 |     const result = await Promise.race([validationPromise, timeoutPromise]);
1778 |     clearTimeout(timeoutHandle!);
1779 |     return result;
1780 |   } catch (error: any) {
1781 |     clearTimeout(timeoutHandle!);
1782 |     // Return a partial result with error information
1783 |     return {
1784 |       success: false,
1785 |       confidence: {
1786 |         overall: 0,
1787 |         breakdown: {
1788 |           technologyDetection: 0,
1789 |           frameworkVersionAccuracy: 0,
1790 |           codeExampleRelevance: 0,
1791 |           architecturalAssumptions: 0,
1792 |           businessContextAlignment: 0,
1793 |         },
1794 |         riskFactors: [
1795 |           {
1796 |             type: "high",
1797 |             category: "validation",
1798 |             description: "Validation process failed or timed out",
1799 |             impact: "Unable to complete content validation",
1800 |             mitigation:
1801 |               "Try validating a smaller directory or specific subset of files",
1802 |           },
1803 |         ],
1804 |       },
1805 |       issues: [],
1806 |       uncertainties: [],
1807 |       recommendations: [
1808 |         "Validation failed or timed out",
1809 |         "Consider validating smaller directory subsets",
1810 |         "Check for very large files or deep directory structures",
1811 |         `Error: ${error.message}`,
1812 |       ],
1813 |       nextSteps: [
1814 |         "Verify the content path is correct and accessible",
1815 |         "Try validating specific subdirectories instead of the entire project",
1816 |         "Check for circular symlinks or very deep directory structures",
1817 |       ],
1818 |     };
1819 |   }
1820 | }
1821 | 
1822 | interface GeneralValidationResult {
1823 |   success: boolean;
1824 |   linksChecked: number;
1825 |   brokenLinks: string[];
1826 |   codeBlocksValidated: number;
1827 |   codeErrors: string[];
1828 |   recommendations: string[];
1829 |   summary: string;
1830 | }
1831 | 
1832 | export async function validateGeneralContent(
1833 |   args: any,
1834 | ): Promise<GeneralValidationResult> {
1835 |   const {
1836 |     contentPath,
1837 |     validationType = "all",
1838 |     includeCodeValidation = true,
1839 |     followExternalLinks = false,
1840 |   } = args;
1841 | 
1842 |   const result: GeneralValidationResult = {
1843 |     success: true,
1844 |     linksChecked: 0,
1845 |     brokenLinks: [],
1846 |     codeBlocksValidated: 0,
1847 |     codeErrors: [],
1848 |     recommendations: [],
1849 |     summary: "",
1850 |   };
1851 | 
1852 |   try {
1853 |     // Get all markdown files
1854 |     const validator = new ContentAccuracyValidator();
1855 |     const files = await validator.getMarkdownFiles(contentPath);
1856 | 
1857 |     // Check links if requested
1858 |     if (validationType === "all" || validationType === "links") {
1859 |       for (const file of files) {
1860 |         const content = await fs.readFile(file, "utf-8");
1861 |         const links = extractLinksFromMarkdown(content);
1862 | 
1863 |         for (const link of links) {
1864 |           result.linksChecked++;
1865 | 
1866 |           // Skip external links unless explicitly requested
1867 |           if (link.startsWith("http") && !followExternalLinks) continue;
1868 | 
1869 |           // Check internal links
1870 |           if (!link.startsWith("http")) {
1871 |             const fullPath = path.resolve(path.dirname(file), link);
1872 |             try {
1873 |               await fs.access(fullPath);
1874 |             } catch {
1875 |               result.brokenLinks.push(`${path.basename(file)}: ${link}`);
1876 |               result.success = false;
1877 |             }
1878 |           }
1879 |         }
1880 |       }
1881 |     }
1882 | 
1883 |     // Validate code blocks if requested
1884 |     if (
1885 |       includeCodeValidation &&
1886 |       (validationType === "all" || validationType === "code")
1887 |     ) {
1888 |       for (const file of files) {
1889 |         const content = await fs.readFile(file, "utf-8");
1890 |         const codeBlocks = extractCodeBlocks(content);
1891 | 
1892 |         for (const block of codeBlocks) {
1893 |           result.codeBlocksValidated++;
1894 | 
1895 |           // Basic syntax validation
1896 |           if (block.language && block.code.trim()) {
1897 |             if (block.language === "javascript" || block.language === "js") {
1898 |               try {
1899 |                 // Basic JS syntax check - look for common issues
1900 |                 if (
1901 |                   block.code.includes("console.log") &&
1902 |                   !block.code.includes(";")
1903 |                 ) {
1904 |                   result.codeErrors.push(
1905 |                     `${path.basename(file)}: Missing semicolon in JS code`,
1906 |                   );
1907 |                 }
1908 |               } catch (error) {
1909 |                 result.codeErrors.push(
1910 |                   `${path.basename(file)}: JS syntax error - ${error}`,
1911 |                 );
1912 |                 result.success = false;
1913 |               }
1914 |             }
1915 |           }
1916 |         }
1917 |       }
1918 |     }
1919 | 
1920 |     // Generate recommendations
1921 |     if (result.brokenLinks.length > 0) {
1922 |       result.recommendations.push(
1923 |         `Fix ${result.brokenLinks.length} broken internal links`,
1924 |       );
1925 |       result.recommendations.push(
1926 |         "Run documentation build to catch additional link issues",
1927 |       );
1928 |     }
1929 | 
1930 |     if (result.codeErrors.length > 0) {
1931 |       result.recommendations.push(
1932 |         `Review and fix ${result.codeErrors.length} code syntax issues`,
1933 |       );
1934 |     }
1935 | 
1936 |     if (result.success) {
1937 |       result.recommendations.push(
1938 |         "Content validation passed - no critical issues found",
1939 |       );
1940 |     }
1941 | 
1942 |     // Create summary
1943 |     result.summary = `Validated ${files.length} files, ${
1944 |       result.linksChecked
1945 |     } links, ${result.codeBlocksValidated} code blocks. ${
1946 |       result.success
1947 |         ? "PASSED"
1948 |         : `ISSUES FOUND: ${
1949 |             result.brokenLinks.length + result.codeErrors.length
1950 |           }`
1951 |     }`;
1952 | 
1953 |     return result;
1954 |   } catch (error) {
1955 |     result.success = false;
1956 |     result.recommendations.push(`Validation failed: ${error}`);
1957 |     result.summary = `Validation error: ${error}`;
1958 |     return result;
1959 |   }
1960 | }
1961 | 
1962 | // Helper function to extract links from markdown
1963 | function extractLinksFromMarkdown(content: string): string[] {
1964 |   const linkRegex = /\[([^\]]*)\]\(([^)]+)\)/g;
1965 |   const links: string[] = [];
1966 |   let match;
1967 | 
1968 |   while ((match = linkRegex.exec(content)) !== null) {
1969 |     links.push(match[2]); // The URL part
1970 |   }
1971 | 
1972 |   return links;
1973 | }
1974 | 
1975 | // Helper function to extract code blocks from markdown
1976 | function extractCodeBlocks(
1977 |   content: string,
1978 | ): { language: string; code: string }[] {
1979 |   const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
1980 |   const blocks: { language: string; code: string }[] = [];
1981 |   let match;
1982 | 
1983 |   while ((match = codeBlockRegex.exec(content)) !== null) {
1984 |     blocks.push({
1985 |       language: match[1] || "text",
1986 |       code: match[2],
1987 |     });
1988 |   }
1989 | 
1990 |   return blocks;
1991 | }
1992 | 
```

--------------------------------------------------------------------------------
/src/utils/ast-analyzer.ts:
--------------------------------------------------------------------------------

```typescript
   1 | /**
   2 |  * AST-based Code Analyzer (Phase 3)
   3 |  *
   4 |  * Uses tree-sitter parsers for multi-language AST analysis
   5 |  * Provides deep code structure extraction for drift detection
   6 |  */
   7 | 
   8 | import { parse as parseTypeScript } from "@typescript-eslint/typescript-estree";
   9 | import { promises as fs } from "fs";
  10 | import path from "path";
  11 | import crypto from "crypto";
  12 | 
  13 | // Language configuration
  14 | const LANGUAGE_CONFIGS: Record<
  15 |   string,
  16 |   { parser: string; extensions: string[] }
  17 | > = {
  18 |   typescript: { parser: "tree-sitter-typescript", extensions: [".ts", ".tsx"] },
  19 |   javascript: {
  20 |     parser: "tree-sitter-javascript",
  21 |     extensions: [".js", ".jsx", ".mjs"],
  22 |   },
  23 |   python: { parser: "tree-sitter-python", extensions: [".py"] },
  24 |   rust: { parser: "tree-sitter-rust", extensions: [".rs"] },
  25 |   go: { parser: "tree-sitter-go", extensions: [".go"] },
  26 |   java: { parser: "tree-sitter-java", extensions: [".java"] },
  27 |   ruby: { parser: "tree-sitter-ruby", extensions: [".rb"] },
  28 |   bash: { parser: "tree-sitter-bash", extensions: [".sh", ".bash"] },
  29 | };
  30 | 
  31 | export interface FunctionSignature {
  32 |   name: string;
  33 |   parameters: ParameterInfo[];
  34 |   returnType: string | null;
  35 |   isAsync: boolean;
  36 |   isExported: boolean;
  37 |   isPublic: boolean;
  38 |   docComment: string | null;
  39 |   startLine: number;
  40 |   endLine: number;
  41 |   complexity: number;
  42 |   dependencies: string[];
  43 | }
  44 | 
  45 | export interface ParameterInfo {
  46 |   name: string;
  47 |   type: string | null;
  48 |   optional: boolean;
  49 |   defaultValue: string | null;
  50 | }
  51 | 
  52 | export interface ClassInfo {
  53 |   name: string;
  54 |   isExported: boolean;
  55 |   extends: string | null;
  56 |   implements: string[];
  57 |   methods: FunctionSignature[];
  58 |   properties: PropertyInfo[];
  59 |   docComment: string | null;
  60 |   startLine: number;
  61 |   endLine: number;
  62 | }
  63 | 
  64 | export interface PropertyInfo {
  65 |   name: string;
  66 |   type: string | null;
  67 |   isStatic: boolean;
  68 |   isReadonly: boolean;
  69 |   visibility: "public" | "private" | "protected";
  70 | }
  71 | 
  72 | export interface InterfaceInfo {
  73 |   name: string;
  74 |   isExported: boolean;
  75 |   extends: string[];
  76 |   properties: PropertyInfo[];
  77 |   methods: FunctionSignature[];
  78 |   docComment: string | null;
  79 |   startLine: number;
  80 |   endLine: number;
  81 | }
  82 | 
  83 | export interface TypeInfo {
  84 |   name: string;
  85 |   isExported: boolean;
  86 |   definition: string;
  87 |   docComment: string | null;
  88 |   startLine: number;
  89 |   endLine: number;
  90 | }
  91 | 
  92 | export interface ImportInfo {
  93 |   source: string;
  94 |   imports: Array<{ name: string; alias?: string }>;
  95 |   isDefault: boolean;
  96 |   startLine: number;
  97 | }
  98 | 
  99 | export interface ASTAnalysisResult {
 100 |   filePath: string;
 101 |   language: string;
 102 |   functions: FunctionSignature[];
 103 |   classes: ClassInfo[];
 104 |   interfaces: InterfaceInfo[];
 105 |   types: TypeInfo[];
 106 |   imports: ImportInfo[];
 107 |   exports: string[];
 108 |   contentHash: string;
 109 |   lastModified: string;
 110 |   linesOfCode: number;
 111 |   complexity: number;
 112 | }
 113 | 
 114 | export interface CodeDiff {
 115 |   type: "added" | "removed" | "modified" | "unchanged";
 116 |   category: "function" | "class" | "interface" | "type" | "import" | "export";
 117 |   name: string;
 118 |   details: string;
 119 |   oldSignature?: string;
 120 |   newSignature?: string;
 121 |   impactLevel: "breaking" | "major" | "minor" | "patch";
 122 | }
 123 | 
 124 | /**
 125 |  * Call graph node representing a function and its calls (Issue #72)
 126 |  */
 127 | export interface CallGraphNode {
 128 |   /** Function signature with full metadata */
 129 |   function: FunctionSignature;
 130 |   /** File location of this function */
 131 |   location: {
 132 |     file: string;
 133 |     line: number;
 134 |     column?: number;
 135 |   };
 136 |   /** Child function calls made by this function */
 137 |   calls: CallGraphNode[];
 138 |   /** Conditional branches (if/else, switch) with their paths */
 139 |   conditionalBranches: ConditionalPath[];
 140 |   /** Exception types that can be raised */
 141 |   exceptions: ExceptionPath[];
 142 |   /** Current recursion depth */
 143 |   depth: number;
 144 |   /** Whether this node was truncated due to maxDepth */
 145 |   truncated: boolean;
 146 |   /** Whether this is an external/imported function */
 147 |   isExternal: boolean;
 148 |   /** Source of the import if external */
 149 |   importSource?: string;
 150 | }
 151 | 
 152 | /**
 153 |  * Conditional execution path (if/else, switch, ternary)
 154 |  */
 155 | export interface ConditionalPath {
 156 |   /** Type of conditional */
 157 |   type: "if" | "else-if" | "else" | "switch-case" | "ternary";
 158 |   /** The condition expression as string */
 159 |   condition: string;
 160 |   /** Line number of the conditional */
 161 |   lineNumber: number;
 162 |   /** Functions called in the true/case branch */
 163 |   trueBranch: CallGraphNode[];
 164 |   /** Functions called in the false/else branch */
 165 |   falseBranch: CallGraphNode[];
 166 | }
 167 | 
 168 | /**
 169 |  * Exception path tracking
 170 |  */
 171 | export interface ExceptionPath {
 172 |   /** Exception type/class being thrown */
 173 |   exceptionType: string;
 174 |   /** Line number of the throw statement */
 175 |   lineNumber: number;
 176 |   /** The throw expression as string */
 177 |   expression: string;
 178 |   /** Whether this is caught within the function */
 179 |   isCaught: boolean;
 180 | }
 181 | 
 182 | /**
 183 |  * Complete call graph for an entry point (Issue #72)
 184 |  */
 185 | export interface CallGraph {
 186 |   /** Name of the entry point function */
 187 |   entryPoint: string;
 188 |   /** Root node of the call graph */
 189 |   root: CallGraphNode;
 190 |   /** All discovered functions in the call graph */
 191 |   allFunctions: Map<string, FunctionSignature>;
 192 |   /** Maximum depth that was actually reached */
 193 |   maxDepthReached: number;
 194 |   /** Files that were analyzed */
 195 |   analyzedFiles: string[];
 196 |   /** Circular references detected */
 197 |   circularReferences: Array<{ from: string; to: string }>;
 198 |   /** External calls that couldn't be resolved */
 199 |   unresolvedCalls: Array<{
 200 |     name: string;
 201 |     location: { file: string; line: number };
 202 |   }>;
 203 |   /** Build timestamp */
 204 |   buildTime: string;
 205 | }
 206 | 
 207 | /**
 208 |  * Options for building call graphs
 209 |  */
 210 | export interface CallGraphOptions {
 211 |   /** Maximum recursion depth (default: 3) */
 212 |   maxDepth?: number;
 213 |   /** Whether to resolve cross-file imports (default: true) */
 214 |   resolveImports?: boolean;
 215 |   /** Whether to extract conditional branches (default: true) */
 216 |   extractConditionals?: boolean;
 217 |   /** Whether to track exceptions (default: true) */
 218 |   trackExceptions?: boolean;
 219 |   /** File extensions to consider for import resolution */
 220 |   extensions?: string[];
 221 | }
 222 | 
 223 | /**
 224 |  * Main AST Analyzer class
 225 |  */
 226 | export class ASTAnalyzer {
 227 |   private parsers: Map<string, any> = new Map();
 228 |   private initialized = false;
 229 | 
 230 |   /**
 231 |    * Initialize tree-sitter parsers for all languages
 232 |    */
 233 |   async initialize(): Promise<void> {
 234 |     if (this.initialized) return;
 235 | 
 236 |     // Note: Tree-sitter initialization would happen here in a full implementation
 237 |     // For now, we're primarily using TypeScript/JavaScript parser
 238 |     // console.log(
 239 |     //   "AST Analyzer initialized with language support:",
 240 |     //   Object.keys(LANGUAGE_CONFIGS),
 241 |     // );
 242 |     this.initialized = true;
 243 |   }
 244 | 
 245 |   /**
 246 |    * Analyze a single file and extract AST information
 247 |    */
 248 |   async analyzeFile(filePath: string): Promise<ASTAnalysisResult | null> {
 249 |     if (!this.initialized) {
 250 |       await this.initialize();
 251 |     }
 252 | 
 253 |     const ext = path.extname(filePath);
 254 |     const language = this.detectLanguage(ext);
 255 | 
 256 |     if (!language) {
 257 |       console.warn(`Unsupported file extension: ${ext}`);
 258 |       return null;
 259 |     }
 260 | 
 261 |     const content = await fs.readFile(filePath, "utf-8");
 262 |     const stats = await fs.stat(filePath);
 263 | 
 264 |     // Use TypeScript parser for .ts/.tsx files
 265 |     if (language === "typescript" || language === "javascript") {
 266 |       return this.analyzeTypeScript(
 267 |         filePath,
 268 |         content,
 269 |         stats.mtime.toISOString(),
 270 |       );
 271 |     }
 272 | 
 273 |     // For other languages, use tree-sitter (placeholder)
 274 |     return this.analyzeWithTreeSitter(
 275 |       filePath,
 276 |       content,
 277 |       language,
 278 |       stats.mtime.toISOString(),
 279 |     );
 280 |   }
 281 | 
 282 |   /**
 283 |    * Analyze TypeScript/JavaScript using typescript-estree
 284 |    */
 285 |   private async analyzeTypeScript(
 286 |     filePath: string,
 287 |     content: string,
 288 |     lastModified: string,
 289 |   ): Promise<ASTAnalysisResult> {
 290 |     const functions: FunctionSignature[] = [];
 291 |     const classes: ClassInfo[] = [];
 292 |     const interfaces: InterfaceInfo[] = [];
 293 |     const types: TypeInfo[] = [];
 294 |     const imports: ImportInfo[] = [];
 295 |     const exports: string[] = [];
 296 | 
 297 |     try {
 298 |       const ast = parseTypeScript(content, {
 299 |         loc: true,
 300 |         range: true,
 301 |         tokens: false,
 302 |         comment: true,
 303 |         jsx: filePath.endsWith(".tsx") || filePath.endsWith(".jsx"),
 304 |       });
 305 | 
 306 |       // Extract functions
 307 |       this.extractFunctions(ast, content, functions);
 308 | 
 309 |       // Extract classes
 310 |       this.extractClasses(ast, content, classes);
 311 | 
 312 |       // Extract interfaces
 313 |       this.extractInterfaces(ast, content, interfaces);
 314 | 
 315 |       // Extract type aliases
 316 |       this.extractTypes(ast, content, types);
 317 | 
 318 |       // Extract imports
 319 |       this.extractImports(ast, imports);
 320 | 
 321 |       // Extract exports
 322 |       this.extractExports(ast, exports);
 323 |     } catch (error) {
 324 |       console.warn(`Failed to parse TypeScript file ${filePath}:`, error);
 325 |     }
 326 | 
 327 |     const contentHash = crypto
 328 |       .createHash("sha256")
 329 |       .update(content)
 330 |       .digest("hex");
 331 |     const linesOfCode = content.split("\n").length;
 332 |     const complexity = this.calculateComplexity(functions, classes);
 333 | 
 334 |     return {
 335 |       filePath,
 336 |       language:
 337 |         filePath.endsWith(".ts") || filePath.endsWith(".tsx")
 338 |           ? "typescript"
 339 |           : "javascript",
 340 |       functions,
 341 |       classes,
 342 |       interfaces,
 343 |       types,
 344 |       imports,
 345 |       exports,
 346 |       contentHash,
 347 |       lastModified,
 348 |       linesOfCode,
 349 |       complexity,
 350 |     };
 351 |   }
 352 | 
 353 |   /**
 354 |    * Analyze using tree-sitter (placeholder for other languages)
 355 |    */
 356 |   private async analyzeWithTreeSitter(
 357 |     filePath: string,
 358 |     content: string,
 359 |     language: string,
 360 |     lastModified: string,
 361 |   ): Promise<ASTAnalysisResult> {
 362 |     // Placeholder for tree-sitter analysis
 363 |     // In a full implementation, we'd parse the content using tree-sitter
 364 |     // and extract language-specific constructs
 365 | 
 366 |     const contentHash = crypto
 367 |       .createHash("sha256")
 368 |       .update(content)
 369 |       .digest("hex");
 370 |     const linesOfCode = content.split("\n").length;
 371 | 
 372 |     return {
 373 |       filePath,
 374 |       language,
 375 |       functions: [],
 376 |       classes: [],
 377 |       interfaces: [],
 378 |       types: [],
 379 |       imports: [],
 380 |       exports: [],
 381 |       contentHash,
 382 |       lastModified,
 383 |       linesOfCode,
 384 |       complexity: 0,
 385 |     };
 386 |   }
 387 | 
 388 |   /**
 389 |    * Extract function declarations from AST
 390 |    */
 391 |   private extractFunctions(
 392 |     ast: any,
 393 |     content: string,
 394 |     functions: FunctionSignature[],
 395 |   ): void {
 396 |     const lines = content.split("\n");
 397 | 
 398 |     const traverse = (node: any, isExported = false) => {
 399 |       if (!node) return;
 400 | 
 401 |       // Handle export declarations
 402 |       if (
 403 |         node.type === "ExportNamedDeclaration" ||
 404 |         node.type === "ExportDefaultDeclaration"
 405 |       ) {
 406 |         if (node.declaration) {
 407 |           traverse(node.declaration, true);
 408 |         }
 409 |         return;
 410 |       }
 411 | 
 412 |       // Function declarations
 413 |       if (node.type === "FunctionDeclaration") {
 414 |         const func = this.parseFunctionNode(node, lines, isExported);
 415 |         if (func) functions.push(func);
 416 |       }
 417 | 
 418 |       // Arrow functions assigned to variables
 419 |       if (node.type === "VariableDeclaration") {
 420 |         for (const declarator of node.declarations || []) {
 421 |           if (declarator.init?.type === "ArrowFunctionExpression") {
 422 |             const func = this.parseArrowFunction(declarator, lines, isExported);
 423 |             if (func) functions.push(func);
 424 |           }
 425 |         }
 426 |       }
 427 | 
 428 |       // Traverse children
 429 |       for (const key in node) {
 430 |         if (typeof node[key] === "object" && node[key] !== null) {
 431 |           if (Array.isArray(node[key])) {
 432 |             node[key].forEach((child: any) => traverse(child, false));
 433 |           } else {
 434 |             traverse(node[key], false);
 435 |           }
 436 |         }
 437 |       }
 438 |     };
 439 | 
 440 |     traverse(ast);
 441 |   }
 442 | 
 443 |   /**
 444 |    * Parse function node
 445 |    */
 446 |   private parseFunctionNode(
 447 |     node: any,
 448 |     lines: string[],
 449 |     isExported: boolean,
 450 |   ): FunctionSignature | null {
 451 |     if (!node.id?.name) return null;
 452 | 
 453 |     const docComment = this.extractDocComment(node.loc?.start.line - 1, lines);
 454 |     const parameters = this.extractParameters(node.params);
 455 | 
 456 |     return {
 457 |       name: node.id.name,
 458 |       parameters,
 459 |       returnType: this.extractReturnType(node),
 460 |       isAsync: node.async || false,
 461 |       isExported,
 462 |       isPublic: true,
 463 |       docComment,
 464 |       startLine: node.loc?.start.line || 0,
 465 |       endLine: node.loc?.end.line || 0,
 466 |       complexity: this.calculateFunctionComplexity(node),
 467 |       dependencies: [],
 468 |     };
 469 |   }
 470 | 
 471 |   /**
 472 |    * Parse arrow function
 473 |    */
 474 |   private parseArrowFunction(
 475 |     declarator: any,
 476 |     lines: string[],
 477 |     isExported: boolean,
 478 |   ): FunctionSignature | null {
 479 |     if (!declarator.id?.name) return null;
 480 | 
 481 |     const node = declarator.init;
 482 |     const docComment = this.extractDocComment(
 483 |       declarator.loc?.start.line - 1,
 484 |       lines,
 485 |     );
 486 |     const parameters = this.extractParameters(node.params);
 487 | 
 488 |     return {
 489 |       name: declarator.id.name,
 490 |       parameters,
 491 |       returnType: this.extractReturnType(node),
 492 |       isAsync: node.async || false,
 493 |       isExported,
 494 |       isPublic: true,
 495 |       docComment,
 496 |       startLine: declarator.loc?.start.line || 0,
 497 |       endLine: declarator.loc?.end.line || 0,
 498 |       complexity: this.calculateFunctionComplexity(node),
 499 |       dependencies: [],
 500 |     };
 501 |   }
 502 | 
 503 |   /**
 504 |    * Extract classes from AST
 505 |    */
 506 |   private extractClasses(
 507 |     ast: any,
 508 |     content: string,
 509 |     classes: ClassInfo[],
 510 |   ): void {
 511 |     const lines = content.split("\n");
 512 | 
 513 |     const traverse = (node: any, isExported = false) => {
 514 |       if (!node) return;
 515 | 
 516 |       // Handle export declarations
 517 |       if (
 518 |         node.type === "ExportNamedDeclaration" ||
 519 |         node.type === "ExportDefaultDeclaration"
 520 |       ) {
 521 |         if (node.declaration) {
 522 |           traverse(node.declaration, true);
 523 |         }
 524 |         return;
 525 |       }
 526 | 
 527 |       if (node.type === "ClassDeclaration" && node.id?.name) {
 528 |         const classInfo = this.parseClassNode(node, lines, isExported);
 529 |         if (classInfo) classes.push(classInfo);
 530 |       }
 531 | 
 532 |       for (const key in node) {
 533 |         if (typeof node[key] === "object" && node[key] !== null) {
 534 |           if (Array.isArray(node[key])) {
 535 |             node[key].forEach((child: any) => traverse(child, false));
 536 |           } else {
 537 |             traverse(node[key], false);
 538 |           }
 539 |         }
 540 |       }
 541 |     };
 542 | 
 543 |     traverse(ast);
 544 |   }
 545 | 
 546 |   /**
 547 |    * Parse class node
 548 |    */
 549 |   private parseClassNode(
 550 |     node: any,
 551 |     lines: string[],
 552 |     isExported: boolean,
 553 |   ): ClassInfo | null {
 554 |     const methods: FunctionSignature[] = [];
 555 |     const properties: PropertyInfo[] = [];
 556 | 
 557 |     // Extract methods and properties
 558 |     if (node.body?.body) {
 559 |       for (const member of node.body.body) {
 560 |         if (member.type === "MethodDefinition") {
 561 |           const method = this.parseMethodNode(member, lines);
 562 |           if (method) methods.push(method);
 563 |         } else if (member.type === "PropertyDefinition") {
 564 |           const property = this.parsePropertyNode(member);
 565 |           if (property) properties.push(property);
 566 |         }
 567 |       }
 568 |     }
 569 | 
 570 |     return {
 571 |       name: node.id.name,
 572 |       isExported,
 573 |       extends: node.superClass?.name || null,
 574 |       implements:
 575 |         node.implements?.map((i: any) => i.expression?.name || "unknown") || [],
 576 |       methods,
 577 |       properties,
 578 |       docComment: this.extractDocComment(node.loc?.start.line - 1, lines),
 579 |       startLine: node.loc?.start.line || 0,
 580 |       endLine: node.loc?.end.line || 0,
 581 |     };
 582 |   }
 583 | 
 584 |   /**
 585 |    * Parse method node
 586 |    */
 587 |   private parseMethodNode(
 588 |     node: any,
 589 |     lines: string[],
 590 |   ): FunctionSignature | null {
 591 |     if (!node.key?.name) return null;
 592 | 
 593 |     return {
 594 |       name: node.key.name,
 595 |       parameters: this.extractParameters(node.value?.params || []),
 596 |       returnType: this.extractReturnType(node.value),
 597 |       isAsync: node.value?.async || false,
 598 |       isExported: false,
 599 |       isPublic: !node.key.name.startsWith("_"),
 600 |       docComment: this.extractDocComment(node.loc?.start.line - 1, lines),
 601 |       startLine: node.loc?.start.line || 0,
 602 |       endLine: node.loc?.end.line || 0,
 603 |       complexity: this.calculateFunctionComplexity(node.value),
 604 |       dependencies: [],
 605 |     };
 606 |   }
 607 | 
 608 |   /**
 609 |    * Parse property node
 610 |    */
 611 |   private parsePropertyNode(node: any): PropertyInfo | null {
 612 |     if (!node.key?.name) return null;
 613 | 
 614 |     return {
 615 |       name: node.key.name,
 616 |       type: this.extractTypeAnnotation(node.typeAnnotation),
 617 |       isStatic: node.static || false,
 618 |       isReadonly: node.readonly || false,
 619 |       visibility: this.determineVisibility(node),
 620 |     };
 621 |   }
 622 | 
 623 |   /**
 624 |    * Extract interfaces from AST
 625 |    */
 626 |   private extractInterfaces(
 627 |     ast: any,
 628 |     content: string,
 629 |     interfaces: InterfaceInfo[],
 630 |   ): void {
 631 |     const lines = content.split("\n");
 632 | 
 633 |     const traverse = (node: any, isExported = false) => {
 634 |       if (!node) return;
 635 | 
 636 |       // Handle export declarations
 637 |       if (
 638 |         node.type === "ExportNamedDeclaration" ||
 639 |         node.type === "ExportDefaultDeclaration"
 640 |       ) {
 641 |         if (node.declaration) {
 642 |           traverse(node.declaration, true);
 643 |         }
 644 |         return;
 645 |       }
 646 | 
 647 |       if (node.type === "TSInterfaceDeclaration" && node.id?.name) {
 648 |         const interfaceInfo = this.parseInterfaceNode(node, lines, isExported);
 649 |         if (interfaceInfo) interfaces.push(interfaceInfo);
 650 |       }
 651 | 
 652 |       for (const key in node) {
 653 |         if (typeof node[key] === "object" && node[key] !== null) {
 654 |           if (Array.isArray(node[key])) {
 655 |             node[key].forEach((child: any) => traverse(child, false));
 656 |           } else {
 657 |             traverse(node[key], false);
 658 |           }
 659 |         }
 660 |       }
 661 |     };
 662 | 
 663 |     traverse(ast);
 664 |   }
 665 | 
 666 |   /**
 667 |    * Parse interface node
 668 |    */
 669 |   private parseInterfaceNode(
 670 |     node: any,
 671 |     lines: string[],
 672 |     isExported: boolean,
 673 |   ): InterfaceInfo | null {
 674 |     const properties: PropertyInfo[] = [];
 675 |     const methods: FunctionSignature[] = [];
 676 | 
 677 |     if (node.body?.body) {
 678 |       for (const member of node.body.body) {
 679 |         if (member.type === "TSPropertySignature") {
 680 |           const prop = this.parseInterfaceProperty(member);
 681 |           if (prop) properties.push(prop);
 682 |         } else if (member.type === "TSMethodSignature") {
 683 |           const method = this.parseInterfaceMethod(member);
 684 |           if (method) methods.push(method);
 685 |         }
 686 |       }
 687 |     }
 688 | 
 689 |     return {
 690 |       name: node.id.name,
 691 |       isExported,
 692 |       extends:
 693 |         node.extends?.map((e: any) => e.expression?.name || "unknown") || [],
 694 |       properties,
 695 |       methods,
 696 |       docComment: this.extractDocComment(node.loc?.start.line - 1, lines),
 697 |       startLine: node.loc?.start.line || 0,
 698 |       endLine: node.loc?.end.line || 0,
 699 |     };
 700 |   }
 701 | 
 702 |   /**
 703 |    * Parse interface property
 704 |    */
 705 |   private parseInterfaceProperty(node: any): PropertyInfo | null {
 706 |     if (!node.key?.name) return null;
 707 | 
 708 |     return {
 709 |       name: node.key.name,
 710 |       type: this.extractTypeAnnotation(node.typeAnnotation),
 711 |       isStatic: false,
 712 |       isReadonly: node.readonly || false,
 713 |       visibility: "public",
 714 |     };
 715 |   }
 716 | 
 717 |   /**
 718 |    * Parse interface method
 719 |    */
 720 |   private parseInterfaceMethod(node: any): FunctionSignature | null {
 721 |     if (!node.key?.name) return null;
 722 | 
 723 |     return {
 724 |       name: node.key.name,
 725 |       parameters: this.extractParameters(node.params || []),
 726 |       returnType: this.extractTypeAnnotation(node.returnType),
 727 |       isAsync: false,
 728 |       isExported: false,
 729 |       isPublic: true,
 730 |       docComment: null,
 731 |       startLine: node.loc?.start.line || 0,
 732 |       endLine: node.loc?.end.line || 0,
 733 |       complexity: 0,
 734 |       dependencies: [],
 735 |     };
 736 |   }
 737 | 
 738 |   /**
 739 |    * Extract type aliases from AST
 740 |    */
 741 |   private extractTypes(ast: any, content: string, types: TypeInfo[]): void {
 742 |     const lines = content.split("\n");
 743 | 
 744 |     const traverse = (node: any, isExported = false) => {
 745 |       if (!node) return;
 746 | 
 747 |       // Handle export declarations
 748 |       if (
 749 |         node.type === "ExportNamedDeclaration" ||
 750 |         node.type === "ExportDefaultDeclaration"
 751 |       ) {
 752 |         if (node.declaration) {
 753 |           traverse(node.declaration, true);
 754 |         }
 755 |         return;
 756 |       }
 757 | 
 758 |       if (node.type === "TSTypeAliasDeclaration" && node.id?.name) {
 759 |         const typeInfo = this.parseTypeNode(node, lines, isExported);
 760 |         if (typeInfo) types.push(typeInfo);
 761 |       }
 762 | 
 763 |       for (const key in node) {
 764 |         if (typeof node[key] === "object" && node[key] !== null) {
 765 |           if (Array.isArray(node[key])) {
 766 |             node[key].forEach((child: any) => traverse(child, false));
 767 |           } else {
 768 |             traverse(node[key], false);
 769 |           }
 770 |         }
 771 |       }
 772 |     };
 773 | 
 774 |     traverse(ast);
 775 |   }
 776 | 
 777 |   /**
 778 |    * Parse type alias node
 779 |    */
 780 |   private parseTypeNode(
 781 |     node: any,
 782 |     lines: string[],
 783 |     isExported: boolean,
 784 |   ): TypeInfo | null {
 785 |     return {
 786 |       name: node.id.name,
 787 |       isExported,
 788 |       definition: this.extractTypeDefinition(node.typeAnnotation),
 789 |       docComment: this.extractDocComment(node.loc?.start.line - 1, lines),
 790 |       startLine: node.loc?.start.line || 0,
 791 |       endLine: node.loc?.end.line || 0,
 792 |     };
 793 |   }
 794 | 
 795 |   /**
 796 |    * Extract imports from AST
 797 |    */
 798 |   private extractImports(ast: any, imports: ImportInfo[]): void {
 799 |     const traverse = (node: any) => {
 800 |       if (!node) return;
 801 | 
 802 |       if (node.type === "ImportDeclaration") {
 803 |         const importInfo: ImportInfo = {
 804 |           source: node.source?.value || "",
 805 |           imports: [],
 806 |           isDefault: false,
 807 |           startLine: node.loc?.start.line || 0,
 808 |         };
 809 | 
 810 |         for (const specifier of node.specifiers || []) {
 811 |           if (specifier.type === "ImportDefaultSpecifier") {
 812 |             importInfo.isDefault = true;
 813 |             importInfo.imports.push({
 814 |               name: specifier.local?.name || "default",
 815 |             });
 816 |           } else if (specifier.type === "ImportSpecifier") {
 817 |             importInfo.imports.push({
 818 |               name: specifier.imported?.name || "",
 819 |               alias:
 820 |                 specifier.local?.name !== specifier.imported?.name
 821 |                   ? specifier.local?.name
 822 |                   : undefined,
 823 |             });
 824 |           }
 825 |         }
 826 | 
 827 |         imports.push(importInfo);
 828 |       }
 829 | 
 830 |       for (const key in node) {
 831 |         if (typeof node[key] === "object" && node[key] !== null) {
 832 |           if (Array.isArray(node[key])) {
 833 |             node[key].forEach((child: any) => traverse(child));
 834 |           } else {
 835 |             traverse(node[key]);
 836 |           }
 837 |         }
 838 |       }
 839 |     };
 840 | 
 841 |     traverse(ast);
 842 |   }
 843 | 
 844 |   /**
 845 |    * Extract exports from AST
 846 |    */
 847 |   private extractExports(ast: any, exports: string[]): void {
 848 |     const traverse = (node: any) => {
 849 |       if (!node) return;
 850 | 
 851 |       // Named exports
 852 |       if (node.type === "ExportNamedDeclaration") {
 853 |         if (node.declaration) {
 854 |           if (node.declaration.id?.name) {
 855 |             exports.push(node.declaration.id.name);
 856 |           } else if (node.declaration.declarations) {
 857 |             for (const decl of node.declaration.declarations) {
 858 |               if (decl.id?.name) exports.push(decl.id.name);
 859 |             }
 860 |           }
 861 |         }
 862 |         for (const specifier of node.specifiers || []) {
 863 |           if (specifier.exported?.name) exports.push(specifier.exported.name);
 864 |         }
 865 |       }
 866 | 
 867 |       // Default export
 868 |       if (node.type === "ExportDefaultDeclaration") {
 869 |         if (node.declaration?.id?.name) {
 870 |           exports.push(node.declaration.id.name);
 871 |         } else {
 872 |           exports.push("default");
 873 |         }
 874 |       }
 875 | 
 876 |       for (const key in node) {
 877 |         if (typeof node[key] === "object" && node[key] !== null) {
 878 |           if (Array.isArray(node[key])) {
 879 |             node[key].forEach((child: any) => traverse(child));
 880 |           } else {
 881 |             traverse(node[key]);
 882 |           }
 883 |         }
 884 |       }
 885 |     };
 886 | 
 887 |     traverse(ast);
 888 |   }
 889 | 
 890 |   // Helper methods
 891 | 
 892 |   private extractParameters(params: any[]): ParameterInfo[] {
 893 |     return params.map((param) => ({
 894 |       name: param.name || param.argument?.name || param.left?.name || "unknown",
 895 |       type: this.extractTypeAnnotation(param.typeAnnotation),
 896 |       optional: param.optional || false,
 897 |       defaultValue: param.right ? this.extractDefaultValue(param.right) : null,
 898 |     }));
 899 |   }
 900 | 
 901 |   private extractReturnType(node: any): string | null {
 902 |     return this.extractTypeAnnotation(node?.returnType);
 903 |   }
 904 | 
 905 |   private extractTypeAnnotation(typeAnnotation: any): string | null {
 906 |     if (!typeAnnotation) return null;
 907 |     if (typeAnnotation.typeAnnotation)
 908 |       return this.extractTypeDefinition(typeAnnotation.typeAnnotation);
 909 |     return this.extractTypeDefinition(typeAnnotation);
 910 |   }
 911 | 
 912 |   private extractTypeDefinition(typeNode: any): string {
 913 |     if (!typeNode) return "unknown";
 914 |     if (typeNode.type === "TSStringKeyword") return "string";
 915 |     if (typeNode.type === "TSNumberKeyword") return "number";
 916 |     if (typeNode.type === "TSBooleanKeyword") return "boolean";
 917 |     if (typeNode.type === "TSAnyKeyword") return "any";
 918 |     if (typeNode.type === "TSVoidKeyword") return "void";
 919 |     if (typeNode.type === "TSTypeReference")
 920 |       return typeNode.typeName?.name || "unknown";
 921 |     return "unknown";
 922 |   }
 923 | 
 924 |   private extractDefaultValue(node: any): string | null {
 925 |     if (node.type === "Literal") return String(node.value);
 926 |     if (node.type === "Identifier") return node.name;
 927 |     return null;
 928 |   }
 929 | 
 930 |   private extractDocComment(
 931 |     lineNumber: number,
 932 |     lines: string[],
 933 |   ): string | null {
 934 |     if (lineNumber < 0 || lineNumber >= lines.length) return null;
 935 | 
 936 |     const comment: string[] = [];
 937 |     let currentLine = lineNumber;
 938 | 
 939 |     // Look backwards for JSDoc comment
 940 |     while (currentLine >= 0) {
 941 |       const line = lines[currentLine].trim();
 942 |       if (line.startsWith("*/")) {
 943 |         comment.unshift(line);
 944 |         currentLine--;
 945 |         continue;
 946 |       }
 947 |       if (line.startsWith("*") || line.startsWith("/**")) {
 948 |         comment.unshift(line);
 949 |         if (line.startsWith("/**")) break;
 950 |         currentLine--;
 951 |         continue;
 952 |       }
 953 |       if (comment.length > 0) break;
 954 |       currentLine--;
 955 |     }
 956 | 
 957 |     return comment.length > 0 ? comment.join("\n") : null;
 958 |   }
 959 | 
 960 |   private isExported(node: any): boolean {
 961 |     if (!node) return false;
 962 | 
 963 |     // Check parent for export
 964 |     let current = node;
 965 |     while (current) {
 966 |       if (
 967 |         current.type === "ExportNamedDeclaration" ||
 968 |         current.type === "ExportDefaultDeclaration"
 969 |       ) {
 970 |         return true;
 971 |       }
 972 |       current = current.parent;
 973 |     }
 974 | 
 975 |     return false;
 976 |   }
 977 | 
 978 |   private determineVisibility(node: any): "public" | "private" | "protected" {
 979 |     if (node.accessibility) return node.accessibility;
 980 |     if (node.key?.name?.startsWith("_")) return "private";
 981 |     if (node.key?.name?.startsWith("#")) return "private";
 982 |     return "public";
 983 |   }
 984 | 
 985 |   private calculateFunctionComplexity(node: any): number {
 986 |     // Simplified cyclomatic complexity
 987 |     let complexity = 1;
 988 | 
 989 |     const traverse = (n: any) => {
 990 |       if (!n) return;
 991 | 
 992 |       // Increment for control flow statements
 993 |       if (
 994 |         [
 995 |           "IfStatement",
 996 |           "ConditionalExpression",
 997 |           "ForStatement",
 998 |           "WhileStatement",
 999 |           "DoWhileStatement",
1000 |           "SwitchCase",
1001 |           "CatchClause",
1002 |         ].includes(n.type)
1003 |       ) {
1004 |         complexity++;
1005 |       }
1006 | 
1007 |       for (const key in n) {
1008 |         if (typeof n[key] === "object" && n[key] !== null) {
1009 |           if (Array.isArray(n[key])) {
1010 |             n[key].forEach((child: any) => traverse(child));
1011 |           } else {
1012 |             traverse(n[key]);
1013 |           }
1014 |         }
1015 |       }
1016 |     };
1017 | 
1018 |     traverse(node);
1019 |     return complexity;
1020 |   }
1021 | 
1022 |   private calculateComplexity(
1023 |     functions: FunctionSignature[],
1024 |     classes: ClassInfo[],
1025 |   ): number {
1026 |     const functionComplexity = functions.reduce(
1027 |       (sum, f) => sum + f.complexity,
1028 |       0,
1029 |     );
1030 |     const classComplexity = classes.reduce(
1031 |       (sum, c) =>
1032 |         sum + c.methods.reduce((methodSum, m) => methodSum + m.complexity, 0),
1033 |       0,
1034 |     );
1035 |     return functionComplexity + classComplexity;
1036 |   }
1037 | 
1038 |   private detectLanguage(ext: string): string | null {
1039 |     for (const [lang, config] of Object.entries(LANGUAGE_CONFIGS)) {
1040 |       if (config.extensions.includes(ext)) return lang;
1041 |     }
1042 |     return null;
1043 |   }
1044 | 
1045 |   /**
1046 |    * Compare two AST analysis results and detect changes
1047 |    */
1048 |   async detectDrift(
1049 |     oldAnalysis: ASTAnalysisResult,
1050 |     newAnalysis: ASTAnalysisResult,
1051 |   ): Promise<CodeDiff[]> {
1052 |     const diffs: CodeDiff[] = [];
1053 | 
1054 |     // Compare functions
1055 |     diffs.push(
1056 |       ...this.compareFunctions(oldAnalysis.functions, newAnalysis.functions),
1057 |     );
1058 | 
1059 |     // Compare classes
1060 |     diffs.push(
1061 |       ...this.compareClasses(oldAnalysis.classes, newAnalysis.classes),
1062 |     );
1063 | 
1064 |     // Compare interfaces
1065 |     diffs.push(
1066 |       ...this.compareInterfaces(oldAnalysis.interfaces, newAnalysis.interfaces),
1067 |     );
1068 | 
1069 |     // Compare types
1070 |     diffs.push(...this.compareTypes(oldAnalysis.types, newAnalysis.types));
1071 | 
1072 |     return diffs;
1073 |   }
1074 | 
1075 |   private compareFunctions(
1076 |     oldFuncs: FunctionSignature[],
1077 |     newFuncs: FunctionSignature[],
1078 |   ): CodeDiff[] {
1079 |     const diffs: CodeDiff[] = [];
1080 |     const oldMap = new Map(oldFuncs.map((f) => [f.name, f]));
1081 |     const newMap = new Map(newFuncs.map((f) => [f.name, f]));
1082 | 
1083 |     // Check for removed functions
1084 |     for (const [name, func] of oldMap) {
1085 |       if (!newMap.has(name)) {
1086 |         diffs.push({
1087 |           type: "removed",
1088 |           category: "function",
1089 |           name,
1090 |           details: `Function '${name}' was removed`,
1091 |           oldSignature: this.formatFunctionSignature(func),
1092 |           impactLevel: func.isExported ? "breaking" : "minor",
1093 |         });
1094 |       }
1095 |     }
1096 | 
1097 |     // Check for added functions
1098 |     for (const [name, func] of newMap) {
1099 |       if (!oldMap.has(name)) {
1100 |         diffs.push({
1101 |           type: "added",
1102 |           category: "function",
1103 |           name,
1104 |           details: `Function '${name}' was added`,
1105 |           newSignature: this.formatFunctionSignature(func),
1106 |           impactLevel: "patch",
1107 |         });
1108 |       }
1109 |     }
1110 | 
1111 |     // Check for modified functions
1112 |     for (const [name, newFunc] of newMap) {
1113 |       const oldFunc = oldMap.get(name);
1114 |       if (oldFunc) {
1115 |         const changes = this.detectFunctionChanges(oldFunc, newFunc);
1116 |         if (changes.length > 0) {
1117 |           diffs.push({
1118 |             type: "modified",
1119 |             category: "function",
1120 |             name,
1121 |             details: changes.join("; "),
1122 |             oldSignature: this.formatFunctionSignature(oldFunc),
1123 |             newSignature: this.formatFunctionSignature(newFunc),
1124 |             impactLevel: this.determineFunctionImpact(oldFunc, newFunc),
1125 |           });
1126 |         }
1127 |       }
1128 |     }
1129 | 
1130 |     return diffs;
1131 |   }
1132 | 
1133 |   private compareClasses(
1134 |     oldClasses: ClassInfo[],
1135 |     newClasses: ClassInfo[],
1136 |   ): CodeDiff[] {
1137 |     const diffs: CodeDiff[] = [];
1138 |     const oldMap = new Map(oldClasses.map((c) => [c.name, c]));
1139 |     const newMap = new Map(newClasses.map((c) => [c.name, c]));
1140 | 
1141 |     for (const [name, oldClass] of oldMap) {
1142 |       if (!newMap.has(name)) {
1143 |         diffs.push({
1144 |           type: "removed",
1145 |           category: "class",
1146 |           name,
1147 |           details: `Class '${name}' was removed`,
1148 |           impactLevel: oldClass.isExported ? "breaking" : "minor",
1149 |         });
1150 |       }
1151 |     }
1152 | 
1153 |     for (const [name] of newMap) {
1154 |       if (!oldMap.has(name)) {
1155 |         diffs.push({
1156 |           type: "added",
1157 |           category: "class",
1158 |           name,
1159 |           details: `Class '${name}' was added`,
1160 |           impactLevel: "patch",
1161 |         });
1162 |       }
1163 |     }
1164 | 
1165 |     return diffs;
1166 |   }
1167 | 
1168 |   private compareInterfaces(
1169 |     oldInterfaces: InterfaceInfo[],
1170 |     newInterfaces: InterfaceInfo[],
1171 |   ): CodeDiff[] {
1172 |     const diffs: CodeDiff[] = [];
1173 |     const oldMap = new Map(oldInterfaces.map((i) => [i.name, i]));
1174 |     const newMap = new Map(newInterfaces.map((i) => [i.name, i]));
1175 | 
1176 |     for (const [name, oldInterface] of oldMap) {
1177 |       if (!newMap.has(name)) {
1178 |         diffs.push({
1179 |           type: "removed",
1180 |           category: "interface",
1181 |           name,
1182 |           details: `Interface '${name}' was removed`,
1183 |           impactLevel: oldInterface.isExported ? "breaking" : "minor",
1184 |         });
1185 |       }
1186 |     }
1187 | 
1188 |     for (const [name] of newMap) {
1189 |       if (!oldMap.has(name)) {
1190 |         diffs.push({
1191 |           type: "added",
1192 |           category: "interface",
1193 |           name,
1194 |           details: `Interface '${name}' was added`,
1195 |           impactLevel: "patch",
1196 |         });
1197 |       }
1198 |     }
1199 | 
1200 |     return diffs;
1201 |   }
1202 | 
1203 |   private compareTypes(oldTypes: TypeInfo[], newTypes: TypeInfo[]): CodeDiff[] {
1204 |     const diffs: CodeDiff[] = [];
1205 |     const oldMap = new Map(oldTypes.map((t) => [t.name, t]));
1206 |     const newMap = new Map(newTypes.map((t) => [t.name, t]));
1207 | 
1208 |     for (const [name, oldType] of oldMap) {
1209 |       if (!newMap.has(name)) {
1210 |         diffs.push({
1211 |           type: "removed",
1212 |           category: "type",
1213 |           name,
1214 |           details: `Type '${name}' was removed`,
1215 |           impactLevel: oldType.isExported ? "breaking" : "minor",
1216 |         });
1217 |       }
1218 |     }
1219 | 
1220 |     for (const [name] of newMap) {
1221 |       if (!oldMap.has(name)) {
1222 |         diffs.push({
1223 |           type: "added",
1224 |           category: "type",
1225 |           name,
1226 |           details: `Type '${name}' was added`,
1227 |           impactLevel: "patch",
1228 |         });
1229 |       }
1230 |     }
1231 | 
1232 |     return diffs;
1233 |   }
1234 | 
1235 |   private detectFunctionChanges(
1236 |     oldFunc: FunctionSignature,
1237 |     newFunc: FunctionSignature,
1238 |   ): string[] {
1239 |     const changes: string[] = [];
1240 | 
1241 |     // Check parameter changes
1242 |     if (oldFunc.parameters.length !== newFunc.parameters.length) {
1243 |       changes.push(
1244 |         `Parameter count changed from ${oldFunc.parameters.length} to ${newFunc.parameters.length}`,
1245 |       );
1246 |     }
1247 | 
1248 |     // Check return type changes
1249 |     if (oldFunc.returnType !== newFunc.returnType) {
1250 |       changes.push(
1251 |         `Return type changed from '${oldFunc.returnType}' to '${newFunc.returnType}'`,
1252 |       );
1253 |     }
1254 | 
1255 |     // Check async changes
1256 |     if (oldFunc.isAsync !== newFunc.isAsync) {
1257 |       changes.push(
1258 |         newFunc.isAsync
1259 |           ? "Function became async"
1260 |           : "Function is no longer async",
1261 |       );
1262 |     }
1263 | 
1264 |     // Check export changes
1265 |     if (oldFunc.isExported !== newFunc.isExported) {
1266 |       changes.push(
1267 |         newFunc.isExported
1268 |           ? "Function is now exported"
1269 |           : "Function is no longer exported",
1270 |       );
1271 |     }
1272 | 
1273 |     return changes;
1274 |   }
1275 | 
1276 |   private determineFunctionImpact(
1277 |     oldFunc: FunctionSignature,
1278 |     newFunc: FunctionSignature,
1279 |   ): "breaking" | "major" | "minor" | "patch" {
1280 |     // Breaking changes
1281 |     if (oldFunc.isExported) {
1282 |       if (oldFunc.parameters.length !== newFunc.parameters.length)
1283 |         return "breaking";
1284 |       if (oldFunc.returnType !== newFunc.returnType) return "breaking";
1285 |       // If a function was exported and is no longer exported, that's breaking
1286 |       if (oldFunc.isExported && !newFunc.isExported) return "breaking";
1287 |     }
1288 | 
1289 |     // Major changes
1290 |     if (oldFunc.isAsync !== newFunc.isAsync) return "major";
1291 | 
1292 |     // Minor changes (new API surface)
1293 |     // If a function becomes exported, that's a minor change (new feature/API)
1294 |     if (!oldFunc.isExported && newFunc.isExported) return "minor";
1295 | 
1296 |     return "patch";
1297 |   }
1298 | 
1299 |   private formatFunctionSignature(func: FunctionSignature): string {
1300 |     const params = func.parameters
1301 |       .map((p) => `${p.name}: ${p.type || "any"}`)
1302 |       .join(", ");
1303 |     const returnType = func.returnType || "void";
1304 |     const asyncPrefix = func.isAsync ? "async " : "";
1305 |     return `${asyncPrefix}${func.name}(${params}): ${returnType}`;
1306 |   }
1307 | 
1308 |   // ============================================================================
1309 |   // Call Graph Builder (Issue #72)
1310 |   // ============================================================================
1311 | 
1312 |   /**
1313 |    * Build a call graph starting from an entry function
1314 |    *
1315 |    * @param entryFunction - Name of the function to start from
1316 |    * @param projectPath - Root path of the project for cross-file resolution
1317 |    * @param options - Call graph building options
1318 |    * @returns Complete call graph with all discovered paths
1319 |    */
1320 |   async buildCallGraph(
1321 |     entryFunction: string,
1322 |     projectPath: string,
1323 |     options: CallGraphOptions = {},
1324 |   ): Promise<CallGraph> {
1325 |     const resolvedOptions: Required<CallGraphOptions> = {
1326 |       maxDepth: options.maxDepth ?? 3,
1327 |       resolveImports: options.resolveImports ?? true,
1328 |       extractConditionals: options.extractConditionals ?? true,
1329 |       trackExceptions: options.trackExceptions ?? true,
1330 |       extensions: options.extensions ?? [".ts", ".tsx", ".js", ".jsx", ".mjs"],
1331 |     };
1332 | 
1333 |     if (!this.initialized) {
1334 |       await this.initialize();
1335 |     }
1336 | 
1337 |     // Find the entry file containing the function
1338 |     const entryFile = await this.findFunctionFile(
1339 |       entryFunction,
1340 |       projectPath,
1341 |       resolvedOptions.extensions,
1342 |     );
1343 | 
1344 |     if (!entryFile) {
1345 |       return this.createEmptyCallGraph(entryFunction);
1346 |     }
1347 | 
1348 |     // Analyze the entry file
1349 |     const entryAnalysis = await this.analyzeFile(entryFile);
1350 |     if (!entryAnalysis) {
1351 |       return this.createEmptyCallGraph(entryFunction);
1352 |     }
1353 | 
1354 |     const entryFunc = entryAnalysis.functions.find(
1355 |       (f) => f.name === entryFunction,
1356 |     );
1357 |     if (!entryFunc) {
1358 |       // Check class methods
1359 |       for (const cls of entryAnalysis.classes) {
1360 |         const method = cls.methods.find((m) => m.name === entryFunction);
1361 |         if (method) {
1362 |           return this.buildCallGraphFromFunction(
1363 |             method,
1364 |             entryFile,
1365 |             entryAnalysis,
1366 |             projectPath,
1367 |             resolvedOptions,
1368 |           );
1369 |         }
1370 |       }
1371 |       return this.createEmptyCallGraph(entryFunction);
1372 |     }
1373 | 
1374 |     return this.buildCallGraphFromFunction(
1375 |       entryFunc,
1376 |       entryFile,
1377 |       entryAnalysis,
1378 |       projectPath,
1379 |       resolvedOptions,
1380 |     );
1381 |   }
1382 | 
1383 |   /**
1384 |    * Build call graph from a specific function
1385 |    */
1386 |   private async buildCallGraphFromFunction(
1387 |     entryFunc: FunctionSignature,
1388 |     entryFile: string,
1389 |     entryAnalysis: ASTAnalysisResult,
1390 |     projectPath: string,
1391 |     options: Required<CallGraphOptions>,
1392 |   ): Promise<CallGraph> {
1393 |     const allFunctions = new Map<string, FunctionSignature>();
1394 |     const analyzedFiles: string[] = [entryFile];
1395 |     const circularReferences: Array<{ from: string; to: string }> = [];
1396 |     const unresolvedCalls: Array<{
1397 |       name: string;
1398 |       location: { file: string; line: number };
1399 |     }> = [];
1400 | 
1401 |     // Cache for analyzed files
1402 |     const analysisCache = new Map<string, ASTAnalysisResult>();
1403 |     analysisCache.set(entryFile, entryAnalysis);
1404 | 
1405 |     // Track visited functions to prevent infinite loops
1406 |     const visited = new Set<string>();
1407 |     let maxDepthReached = 0;
1408 | 
1409 |     const root = await this.buildCallGraphNode(
1410 |       entryFunc,
1411 |       entryFile,
1412 |       entryAnalysis,
1413 |       projectPath,
1414 |       options,
1415 |       0,
1416 |       visited,
1417 |       allFunctions,
1418 |       analysisCache,
1419 |       circularReferences,
1420 |       unresolvedCalls,
1421 |       analyzedFiles,
1422 |       (depth) => {
1423 |         maxDepthReached = Math.max(maxDepthReached, depth);
1424 |       },
1425 |     );
1426 | 
1427 |     return {
1428 |       entryPoint: entryFunc.name,
1429 |       root,
1430 |       allFunctions,
1431 |       maxDepthReached,
1432 |       analyzedFiles: [...new Set(analyzedFiles)],
1433 |       circularReferences,
1434 |       unresolvedCalls,
1435 |       buildTime: new Date().toISOString(),
1436 |     };
1437 |   }
1438 | 
1439 |   /**
1440 |    * Build a single call graph node recursively
1441 |    */
1442 |   private async buildCallGraphNode(
1443 |     func: FunctionSignature,
1444 |     filePath: string,
1445 |     analysis: ASTAnalysisResult,
1446 |     projectPath: string,
1447 |     options: Required<CallGraphOptions>,
1448 |     depth: number,
1449 |     visited: Set<string>,
1450 |     allFunctions: Map<string, FunctionSignature>,
1451 |     analysisCache: Map<string, ASTAnalysisResult>,
1452 |     circularReferences: Array<{ from: string; to: string }>,
1453 |     unresolvedCalls: Array<{
1454 |       name: string;
1455 |       location: { file: string; line: number };
1456 |     }>,
1457 |     analyzedFiles: string[],
1458 |     updateMaxDepth: (depth: number) => void,
1459 |   ): Promise<CallGraphNode> {
1460 |     const funcKey = `${filePath}:${func.name}`;
1461 |     updateMaxDepth(depth);
1462 | 
1463 |     // Check for circular reference
1464 |     if (visited.has(funcKey)) {
1465 |       circularReferences.push({ from: funcKey, to: func.name });
1466 |       return this.createTruncatedNode(func, filePath, depth, true);
1467 |     }
1468 | 
1469 |     // Check max depth
1470 |     if (depth >= options.maxDepth) {
1471 |       return this.createTruncatedNode(func, filePath, depth, false);
1472 |     }
1473 | 
1474 |     visited.add(funcKey);
1475 |     allFunctions.set(func.name, func);
1476 | 
1477 |     // Read the file content to extract function body
1478 |     let content: string;
1479 |     try {
1480 |       content = await fs.readFile(filePath, "utf-8");
1481 |     } catch {
1482 |       return this.createTruncatedNode(func, filePath, depth, false);
1483 |     }
1484 | 
1485 |     // Parse the function body to find calls, conditionals, and exceptions
1486 |     const functionBody = this.extractFunctionBody(content, func);
1487 |     const callExpressions = this.extractCallExpressions(
1488 |       functionBody,
1489 |       func.startLine,
1490 |     );
1491 |     const conditionalBranches: ConditionalPath[] = options.extractConditionals
1492 |       ? await this.extractConditionalPaths(
1493 |           functionBody,
1494 |           func.startLine,
1495 |           analysis,
1496 |           filePath,
1497 |           projectPath,
1498 |           options,
1499 |           depth,
1500 |           visited,
1501 |           allFunctions,
1502 |           analysisCache,
1503 |           circularReferences,
1504 |           unresolvedCalls,
1505 |           analyzedFiles,
1506 |           updateMaxDepth,
1507 |         )
1508 |       : [];
1509 |     const exceptions: ExceptionPath[] = options.trackExceptions
1510 |       ? this.extractExceptions(functionBody, func.startLine)
1511 |       : [];
1512 | 
1513 |     // Build child nodes for each call
1514 |     const calls: CallGraphNode[] = [];
1515 |     for (const call of callExpressions) {
1516 |       const childNode = await this.resolveAndBuildChildNode(
1517 |         call,
1518 |         filePath,
1519 |         analysis,
1520 |         projectPath,
1521 |         options,
1522 |         depth + 1,
1523 |         visited,
1524 |         allFunctions,
1525 |         analysisCache,
1526 |         circularReferences,
1527 |         unresolvedCalls,
1528 |         analyzedFiles,
1529 |         updateMaxDepth,
1530 |       );
1531 |       if (childNode) {
1532 |         calls.push(childNode);
1533 |       }
1534 |     }
1535 | 
1536 |     visited.delete(funcKey); // Allow revisiting from different paths
1537 | 
1538 |     return {
1539 |       function: func,
1540 |       location: {
1541 |         file: filePath,
1542 |         line: func.startLine,
1543 |       },
1544 |       calls,
1545 |       conditionalBranches,
1546 |       exceptions,
1547 |       depth,
1548 |       truncated: false,
1549 |       isExternal: false,
1550 |     };
1551 |   }
1552 | 
1553 |   /**
1554 |    * Resolve a function call and build its child node
1555 |    */
1556 |   private async resolveAndBuildChildNode(
1557 |     call: { name: string; line: number; isMethod: boolean; object?: string },
1558 |     currentFile: string,
1559 |     currentAnalysis: ASTAnalysisResult,
1560 |     projectPath: string,
1561 |     options: Required<CallGraphOptions>,
1562 |     depth: number,
1563 |     visited: Set<string>,
1564 |     allFunctions: Map<string, FunctionSignature>,
1565 |     analysisCache: Map<string, ASTAnalysisResult>,
1566 |     circularReferences: Array<{ from: string; to: string }>,
1567 |     unresolvedCalls: Array<{
1568 |       name: string;
1569 |       location: { file: string; line: number };
1570 |     }>,
1571 |     analyzedFiles: string[],
1572 |     updateMaxDepth: (depth: number) => void,
1573 |   ): Promise<CallGraphNode | null> {
1574 |     // First, try to find the function in the current file
1575 |     let targetFunc = currentAnalysis.functions.find(
1576 |       (f) => f.name === call.name,
1577 |     );
1578 |     let targetFile = currentFile;
1579 |     let targetAnalysis = currentAnalysis;
1580 | 
1581 |     // Check class methods if it's a method call
1582 |     if (!targetFunc && call.isMethod) {
1583 |       for (const cls of currentAnalysis.classes) {
1584 |         const method = cls.methods.find((m) => m.name === call.name);
1585 |         if (method) {
1586 |           targetFunc = method;
1587 |           break;
1588 |         }
1589 |       }
1590 |     }
1591 | 
1592 |     // If not found locally, try to resolve from imports
1593 |     if (!targetFunc && options.resolveImports) {
1594 |       const resolvedImport = await this.resolveImportedFunction(
1595 |         call.name,
1596 |         currentAnalysis.imports,
1597 |         currentFile,
1598 |         projectPath,
1599 |         options.extensions,
1600 |         analysisCache,
1601 |         analyzedFiles,
1602 |       );
1603 | 
1604 |       if (resolvedImport) {
1605 |         targetFunc = resolvedImport.func;
1606 |         targetFile = resolvedImport.file;
1607 |         targetAnalysis = resolvedImport.analysis;
1608 |       }
1609 |     }
1610 | 
1611 |     if (!targetFunc) {
1612 |       // Track as unresolved call (might be built-in or external library)
1613 |       if (!this.isBuiltInFunction(call.name)) {
1614 |         unresolvedCalls.push({
1615 |           name: call.name,
1616 |           location: { file: currentFile, line: call.line },
1617 |         });
1618 |       }
1619 |       return null;
1620 |     }
1621 | 
1622 |     return this.buildCallGraphNode(
1623 |       targetFunc,
1624 |       targetFile,
1625 |       targetAnalysis,
1626 |       projectPath,
1627 |       options,
1628 |       depth,
1629 |       visited,
1630 |       allFunctions,
1631 |       analysisCache,
1632 |       circularReferences,
1633 |       unresolvedCalls,
1634 |       analyzedFiles,
1635 |       updateMaxDepth,
1636 |     );
1637 |   }
1638 | 
1639 |   /**
1640 |    * Resolve an imported function to its source file
1641 |    */
1642 |   private async resolveImportedFunction(
1643 |     funcName: string,
1644 |     imports: ImportInfo[],
1645 |     currentFile: string,
1646 |     projectPath: string,
1647 |     extensions: string[],
1648 |     analysisCache: Map<string, ASTAnalysisResult>,
1649 |     analyzedFiles: string[],
1650 |   ): Promise<{
1651 |     func: FunctionSignature;
1652 |     file: string;
1653 |     analysis: ASTAnalysisResult;
1654 |   } | null> {
1655 |     // Find the import that provides this function
1656 |     for (const imp of imports) {
1657 |       const importedItem = imp.imports.find(
1658 |         (i) => i.name === funcName || i.alias === funcName,
1659 |       );
1660 | 
1661 |       if (
1662 |         importedItem ||
1663 |         (imp.isDefault && imp.imports[0]?.name === funcName)
1664 |       ) {
1665 |         // Resolve the import path
1666 |         const resolvedPath = await this.resolveImportPath(
1667 |           imp.source,
1668 |           currentFile,
1669 |           projectPath,
1670 |           extensions,
1671 |         );
1672 | 
1673 |         if (!resolvedPath) continue;
1674 | 
1675 |         // Check cache first
1676 |         let analysis: ASTAnalysisResult | undefined =
1677 |           analysisCache.get(resolvedPath);
1678 |         if (!analysis) {
1679 |           const analyzedFile = await this.analyzeFile(resolvedPath);
1680 |           if (analyzedFile) {
1681 |             analysis = analyzedFile;
1682 |             analysisCache.set(resolvedPath, analysis);
1683 |             analyzedFiles.push(resolvedPath);
1684 |           }
1685 |         }
1686 | 
1687 |         if (!analysis) continue;
1688 | 
1689 |         // Find the function in the resolved file
1690 |         const func = analysis.functions.find(
1691 |           (f) => f.name === (importedItem?.name || funcName),
1692 |         );
1693 |         if (func) {
1694 |           return { func, file: resolvedPath, analysis };
1695 |         }
1696 | 
1697 |         // Check class methods
1698 |         for (const cls of analysis.classes) {
1699 |           const method = cls.methods.find(
1700 |             (m) => m.name === (importedItem?.name || funcName),
1701 |           );
1702 |           if (method) {
1703 |             return { func: method, file: resolvedPath, analysis };
1704 |           }
1705 |         }
1706 |       }
1707 |     }
1708 | 
1709 |     return null;
1710 |   }
1711 | 
1712 |   /**
1713 |    * Resolve an import path to an absolute file path
1714 |    */
1715 |   private async resolveImportPath(
1716 |     importSource: string,
1717 |     currentFile: string,
1718 |     projectPath: string,
1719 |     extensions: string[],
1720 |   ): Promise<string | null> {
1721 |     // Skip node_modules and external packages
1722 |     if (
1723 |       !importSource.startsWith(".") &&
1724 |       !importSource.startsWith("/") &&
1725 |       !importSource.startsWith("@/")
1726 |     ) {
1727 |       return null;
1728 |     }
1729 | 
1730 |     const currentDir = path.dirname(currentFile);
1731 |     let basePath: string;
1732 | 
1733 |     if (importSource.startsWith("@/")) {
1734 |       // Handle alias imports (common in Next.js, etc.)
1735 |       basePath = path.join(projectPath, importSource.slice(2));
1736 |     } else {
1737 |       basePath = path.resolve(currentDir, importSource);
1738 |     }
1739 | 
1740 |     // Try with different extensions
1741 |     const candidates = [
1742 |       basePath,
1743 |       ...extensions.map((ext) => basePath + ext),
1744 |       path.join(basePath, "index.ts"),
1745 |       path.join(basePath, "index.tsx"),
1746 |       path.join(basePath, "index.js"),
1747 |     ];
1748 | 
1749 |     for (const candidate of candidates) {
1750 |       try {
1751 |         await fs.access(candidate);
1752 |         return candidate;
1753 |       } catch {
1754 |         // File doesn't exist, try next
1755 |       }
1756 |     }
1757 | 
1758 |     return null;
1759 |   }
1760 | 
1761 |   /**
1762 |    * Find the file containing a function
1763 |    */
1764 |   private async findFunctionFile(
1765 |     funcName: string,
1766 |     projectPath: string,
1767 |     extensions: string[],
1768 |   ): Promise<string | null> {
1769 |     // Search common source directories
1770 |     const searchDirs = ["src", "lib", "app", "."];
1771 | 
1772 |     for (const dir of searchDirs) {
1773 |       const searchPath = path.join(projectPath, dir);
1774 |       try {
1775 |         const files = await this.findFilesRecursive(searchPath, extensions);
1776 |         for (const file of files) {
1777 |           const analysis = await this.analyzeFile(file);
1778 |           if (analysis) {
1779 |             const func = analysis.functions.find((f) => f.name === funcName);
1780 |             if (func) return file;
1781 | 
1782 |             // Check class methods
1783 |             for (const cls of analysis.classes) {
1784 |               if (cls.methods.find((m) => m.name === funcName)) {
1785 |                 return file;
1786 |               }
1787 |             }
1788 |           }
1789 |         }
1790 |       } catch {
1791 |         // Directory doesn't exist, continue
1792 |       }
1793 |     }
1794 | 
1795 |     return null;
1796 |   }
1797 | 
1798 |   /**
1799 |    * Recursively find files with given extensions
1800 |    */
1801 |   private async findFilesRecursive(
1802 |     dir: string,
1803 |     extensions: string[],
1804 |     maxDepth: number = 5,
1805 |     currentDepth: number = 0,
1806 |   ): Promise<string[]> {
1807 |     if (currentDepth >= maxDepth) return [];
1808 | 
1809 |     const files: string[] = [];
1810 |     try {
1811 |       const entries = await fs.readdir(dir, { withFileTypes: true });
1812 | 
1813 |       for (const entry of entries) {
1814 |         const fullPath = path.join(dir, entry.name);
1815 | 
1816 |         // Skip node_modules and hidden directories
1817 |         if (
1818 |           entry.name === "node_modules" ||
1819 |           entry.name.startsWith(".") ||
1820 |           entry.name === "dist" ||
1821 |           entry.name === "build"
1822 |         ) {
1823 |           continue;
1824 |         }
1825 | 
1826 |         if (entry.isDirectory()) {
1827 |           files.push(
1828 |             ...(await this.findFilesRecursive(
1829 |               fullPath,
1830 |               extensions,
1831 |               maxDepth,
1832 |               currentDepth + 1,
1833 |             )),
1834 |           );
1835 |         } else if (extensions.some((ext) => entry.name.endsWith(ext))) {
1836 |           files.push(fullPath);
1837 |         }
1838 |       }
1839 |     } catch {
1840 |       // Directory access error
1841 |     }
1842 | 
1843 |     return files;
1844 |   }
1845 | 
1846 |   /**
1847 |    * Extract the body of a function from source code
1848 |    */
1849 |   private extractFunctionBody(
1850 |     content: string,
1851 |     func: FunctionSignature,
1852 |   ): string {
1853 |     const lines = content.split("\n");
1854 |     return lines.slice(func.startLine - 1, func.endLine).join("\n");
1855 |   }
1856 | 
1857 |   /**
1858 |    * Extract function call expressions from code
1859 |    */
1860 |   private extractCallExpressions(
1861 |     code: string,
1862 |     startLine: number,
1863 |   ): Array<{ name: string; line: number; isMethod: boolean; object?: string }> {
1864 |     const calls: Array<{
1865 |       name: string;
1866 |       line: number;
1867 |       isMethod: boolean;
1868 |       object?: string;
1869 |     }> = [];
1870 | 
1871 |     try {
1872 |       const ast = parseTypeScript(code, {
1873 |         loc: true,
1874 |         range: true,
1875 |         tokens: false,
1876 |         comment: false,
1877 |       });
1878 | 
1879 |       const traverse = (node: any) => {
1880 |         if (!node) return;
1881 | 
1882 |         if (node.type === "CallExpression") {
1883 |           const callee = node.callee;
1884 |           const line = (node.loc?.start.line || 0) + startLine - 1;
1885 | 
1886 |           if (callee.type === "Identifier") {
1887 |             calls.push({
1888 |               name: callee.name,
1889 |               line,
1890 |               isMethod: false,
1891 |             });
1892 |           } else if (callee.type === "MemberExpression") {
1893 |             if (callee.property?.name) {
1894 |               calls.push({
1895 |                 name: callee.property.name,
1896 |                 line,
1897 |                 isMethod: true,
1898 |                 object: callee.object?.name,
1899 |               });
1900 |             }
1901 |           }
1902 |         }
1903 | 
1904 |         for (const key in node) {
1905 |           if (typeof node[key] === "object" && node[key] !== null) {
1906 |             if (Array.isArray(node[key])) {
1907 |               node[key].forEach((child: any) => traverse(child));
1908 |             } else {
1909 |               traverse(node[key]);
1910 |             }
1911 |           }
1912 |         }
1913 |       };
1914 | 
1915 |       traverse(ast);
1916 |     } catch {
1917 |       // Parse error, return empty
1918 |     }
1919 | 
1920 |     return calls;
1921 |   }
1922 | 
1923 |   /**
1924 |    * Extract conditional paths from function body
1925 |    */
1926 |   private async extractConditionalPaths(
1927 |     code: string,
1928 |     startLine: number,
1929 |     currentAnalysis: ASTAnalysisResult,
1930 |     currentFile: string,
1931 |     projectPath: string,
1932 |     options: Required<CallGraphOptions>,
1933 |     depth: number,
1934 |     visited: Set<string>,
1935 |     allFunctions: Map<string, FunctionSignature>,
1936 |     analysisCache: Map<string, ASTAnalysisResult>,
1937 |     circularReferences: Array<{ from: string; to: string }>,
1938 |     unresolvedCalls: Array<{
1939 |       name: string;
1940 |       location: { file: string; line: number };
1941 |     }>,
1942 |     analyzedFiles: string[],
1943 |     updateMaxDepth: (depth: number) => void,
1944 |   ): Promise<ConditionalPath[]> {
1945 |     const conditionals: ConditionalPath[] = [];
1946 | 
1947 |     try {
1948 |       const ast = parseTypeScript(code, {
1949 |         loc: true,
1950 |         range: true,
1951 |         tokens: false,
1952 |         comment: false,
1953 |       });
1954 | 
1955 |       const extractConditionString = (node: any): string => {
1956 |         if (!node) return "unknown";
1957 |         if (node.type === "Identifier") return node.name;
1958 |         if (node.type === "BinaryExpression") {
1959 |           return `${extractConditionString(node.left)} ${
1960 |             node.operator
1961 |           } ${extractConditionString(node.right)}`;
1962 |         }
1963 |         if (node.type === "MemberExpression") {
1964 |           return `${extractConditionString(node.object)}.${
1965 |             node.property?.name || "?"
1966 |           }`;
1967 |         }
1968 |         if (node.type === "UnaryExpression") {
1969 |           return `${node.operator}${extractConditionString(node.argument)}`;
1970 |         }
1971 |         if (node.type === "Literal") {
1972 |           return String(node.value);
1973 |         }
1974 |         return "complex";
1975 |       };
1976 | 
1977 |       const extractBranchCalls = async (
1978 |         branchNode: any,
1979 |       ): Promise<CallGraphNode[]> => {
1980 |         if (!branchNode) return [];
1981 | 
1982 |         const branchCode =
1983 |           branchNode.type === "BlockStatement"
1984 |             ? code.slice(branchNode.range[0], branchNode.range[1])
1985 |             : code.slice(
1986 |                 branchNode.range?.[0] || 0,
1987 |                 branchNode.range?.[1] || 0,
1988 |               );
1989 | 
1990 |         const branchCalls = this.extractCallExpressions(
1991 |           branchCode,
1992 |           (branchNode.loc?.start.line || 0) + startLine - 1,
1993 |         );
1994 |         const nodes: CallGraphNode[] = [];
1995 | 
1996 |         for (const call of branchCalls) {
1997 |           const childNode = await this.resolveAndBuildChildNode(
1998 |             call,
1999 |             currentFile,
2000 |             currentAnalysis,
2001 |             projectPath,
2002 |             options,
2003 |             depth + 1,
2004 |             visited,
2005 |             allFunctions,
2006 |             analysisCache,
2007 |             circularReferences,
2008 |             unresolvedCalls,
2009 |             analyzedFiles,
2010 |             updateMaxDepth,
2011 |           );
2012 |           if (childNode) {
2013 |             nodes.push(childNode);
2014 |           }
2015 |         }
2016 | 
2017 |         return nodes;
2018 |       };
2019 | 
2020 |       const traverse = async (node: any) => {
2021 |         if (!node) return;
2022 | 
2023 |         // If statement
2024 |         if (node.type === "IfStatement") {
2025 |           const condition = extractConditionString(node.test);
2026 |           const line = (node.loc?.start.line || 0) + startLine - 1;
2027 | 
2028 |           conditionals.push({
2029 |             type: "if",
2030 |             condition,
2031 |             lineNumber: line,
2032 |             trueBranch: await extractBranchCalls(node.consequent),
2033 |             falseBranch: await extractBranchCalls(node.alternate),
2034 |           });
2035 |         }
2036 | 
2037 |         // Switch statement
2038 |         if (node.type === "SwitchStatement") {
2039 |           const discriminant = extractConditionString(node.discriminant);
2040 | 
2041 |           for (const switchCase of node.cases || []) {
2042 |             conditionals.push({
2043 |               type: "switch-case",
2044 |               condition: switchCase.test
2045 |                 ? `${discriminant} === ${extractConditionString(
2046 |                     switchCase.test,
2047 |                   )}`
2048 |                 : "default",
2049 |               lineNumber: (switchCase.loc?.start.line || 0) + startLine - 1,
2050 |               trueBranch: await extractBranchCalls(switchCase),
2051 |               falseBranch: [],
2052 |             });
2053 |           }
2054 |         }
2055 | 
2056 |         // Ternary operator
2057 |         if (node.type === "ConditionalExpression") {
2058 |           const condition = extractConditionString(node.test);
2059 |           const line = (node.loc?.start.line || 0) + startLine - 1;
2060 | 
2061 |           conditionals.push({
2062 |             type: "ternary",
2063 |             condition,
2064 |             lineNumber: line,
2065 |             trueBranch: await extractBranchCalls(node.consequent),
2066 |             falseBranch: await extractBranchCalls(node.alternate),
2067 |           });
2068 |         }
2069 | 
2070 |         for (const key in node) {
2071 |           if (typeof node[key] === "object" && node[key] !== null) {
2072 |             if (Array.isArray(node[key])) {
2073 |               for (const child of node[key]) {
2074 |                 await traverse(child);
2075 |               }
2076 |             } else {
2077 |               await traverse(node[key]);
2078 |             }
2079 |           }
2080 |         }
2081 |       };
2082 | 
2083 |       await traverse(ast);
2084 |     } catch {
2085 |       // Parse error
2086 |     }
2087 | 
2088 |     return conditionals;
2089 |   }
2090 | 
2091 |   /**
2092 |    * Extract exception paths (throw statements)
2093 |    */
2094 |   private extractExceptions(code: string, startLine: number): ExceptionPath[] {
2095 |     const exceptions: ExceptionPath[] = [];
2096 | 
2097 |     try {
2098 |       const ast = parseTypeScript(code, {
2099 |         loc: true,
2100 |         range: true,
2101 |         tokens: false,
2102 |         comment: false,
2103 |       });
2104 | 
2105 |       // Track try-catch blocks to determine if throws are caught
2106 |       const catchRanges: Array<[number, number]> = [];
2107 | 
2108 |       const collectCatchBlocks = (node: any) => {
2109 |         if (!node) return;
2110 | 
2111 |         if (node.type === "TryStatement" && node.handler) {
2112 |           const handlerRange = node.handler.range || [0, 0];
2113 |           catchRanges.push(handlerRange);
2114 |         }
2115 | 
2116 |         for (const key in node) {
2117 |           if (typeof node[key] === "object" && node[key] !== null) {
2118 |             if (Array.isArray(node[key])) {
2119 |               node[key].forEach((child: any) => collectCatchBlocks(child));
2120 |             } else {
2121 |               collectCatchBlocks(node[key]);
2122 |             }
2123 |           }
2124 |         }
2125 |       };
2126 | 
2127 |       const traverse = (node: any, inTryBlock = false) => {
2128 |         if (!node) return;
2129 | 
2130 |         if (node.type === "TryStatement") {
2131 |           traverse(node.block, true);
2132 |           if (node.handler) traverse(node.handler.body, false);
2133 |           if (node.finalizer) traverse(node.finalizer, false);
2134 |           return;
2135 |         }
2136 | 
2137 |         if (node.type === "ThrowStatement") {
2138 |           const line = (node.loc?.start.line || 0) + startLine - 1;
2139 |           const argument = node.argument;
2140 |           let exceptionType = "Error";
2141 |           let expression = "unknown";
2142 | 
2143 |           if (argument?.type === "NewExpression") {
2144 |             exceptionType = argument.callee?.name || "Error";
2145 |             expression = `new ${exceptionType}(...)`;
2146 |           } else if (argument?.type === "Identifier") {
2147 |             exceptionType = argument.name;
2148 |             expression = argument.name;
2149 |           } else if (argument?.type === "CallExpression") {
2150 |             exceptionType = argument.callee?.name || "Error";
2151 |             expression = `${exceptionType}(...)`;
2152 |           }
2153 | 
2154 |           exceptions.push({
2155 |             exceptionType,
2156 |             lineNumber: line,
2157 |             expression,
2158 |             isCaught: inTryBlock,
2159 |           });
2160 |         }
2161 | 
2162 |         for (const key in node) {
2163 |           if (
2164 |             key !== "handler" &&
2165 |             key !== "finalizer" &&
2166 |             typeof node[key] === "object" &&
2167 |             node[key] !== null
2168 |           ) {
2169 |             if (Array.isArray(node[key])) {
2170 |               node[key].forEach((child: any) => traverse(child, inTryBlock));
2171 |             } else {
2172 |               traverse(node[key], inTryBlock);
2173 |             }
2174 |           }
2175 |         }
2176 |       };
2177 | 
2178 |       collectCatchBlocks(ast);
2179 |       traverse(ast);
2180 |     } catch {
2181 |       // Parse error
2182 |     }
2183 | 
2184 |     return exceptions;
2185 |   }
2186 | 
2187 |   /**
2188 |    * Check if a function name is a built-in JavaScript function
2189 |    */
2190 |   private isBuiltInFunction(name: string): boolean {
2191 |     const builtIns = new Set([
2192 |       // Console
2193 |       "log",
2194 |       "warn",
2195 |       "error",
2196 |       "info",
2197 |       "debug",
2198 |       "trace",
2199 |       "table",
2200 |       // Array methods
2201 |       "map",
2202 |       "filter",
2203 |       "reduce",
2204 |       "forEach",
2205 |       "find",
2206 |       "findIndex",
2207 |       "some",
2208 |       "every",
2209 |       "includes",
2210 |       "indexOf",
2211 |       "push",
2212 |       "pop",
2213 |       "shift",
2214 |       "unshift",
2215 |       "slice",
2216 |       "splice",
2217 |       "concat",
2218 |       "join",
2219 |       "sort",
2220 |       "reverse",
2221 |       "flat",
2222 |       "flatMap",
2223 |       // String methods
2224 |       "split",
2225 |       "trim",
2226 |       "toLowerCase",
2227 |       "toUpperCase",
2228 |       "replace",
2229 |       "substring",
2230 |       "substr",
2231 |       "charAt",
2232 |       "startsWith",
2233 |       "endsWith",
2234 |       "padStart",
2235 |       "padEnd",
2236 |       "repeat",
2237 |       // Object methods
2238 |       "keys",
2239 |       "values",
2240 |       "entries",
2241 |       "assign",
2242 |       "freeze",
2243 |       "seal",
2244 |       "hasOwnProperty",
2245 |       // Math methods
2246 |       "max",
2247 |       "min",
2248 |       "abs",
2249 |       "floor",
2250 |       "ceil",
2251 |       "round",
2252 |       "random",
2253 |       "sqrt",
2254 |       "pow",
2255 |       // JSON
2256 |       "stringify",
2257 |       "parse",
2258 |       // Promise
2259 |       "then",
2260 |       "catch",
2261 |       "finally",
2262 |       "resolve",
2263 |       "reject",
2264 |       "all",
2265 |       "race",
2266 |       "allSettled",
2267 |       // Timers
2268 |       "setTimeout",
2269 |       "setInterval",
2270 |       "clearTimeout",
2271 |       "clearInterval",
2272 |       // Common
2273 |       "require",
2274 |       "import",
2275 |       "console",
2276 |       "Date",
2277 |       "Error",
2278 |       "Promise",
2279 |       "fetch",
2280 |     ]);
2281 |     return builtIns.has(name);
2282 |   }
2283 | 
2284 |   /**
2285 |    * Create an empty call graph for when entry function is not found
2286 |    */
2287 |   private createEmptyCallGraph(entryFunction: string): CallGraph {
2288 |     return {
2289 |       entryPoint: entryFunction,
2290 |       root: {
2291 |         function: {
2292 |           name: entryFunction,
2293 |           parameters: [],
2294 |           returnType: null,
2295 |           isAsync: false,
2296 |           isExported: false,
2297 |           isPublic: true,
2298 |           docComment: null,
2299 |           startLine: 0,
2300 |           endLine: 0,
2301 |           complexity: 0,
2302 |           dependencies: [],
2303 |         },
2304 |         location: { file: "unknown", line: 0 },
2305 |         calls: [],
2306 |         conditionalBranches: [],
2307 |         exceptions: [],
2308 |         depth: 0,
2309 |         truncated: false,
2310 |         isExternal: true,
2311 |       },
2312 |       allFunctions: new Map(),
2313 |       maxDepthReached: 0,
2314 |       analyzedFiles: [],
2315 |       circularReferences: [],
2316 |       unresolvedCalls: [
2317 |         {
2318 |           name: entryFunction,
2319 |           location: { file: "unknown", line: 0 },
2320 |         },
2321 |       ],
2322 |       buildTime: new Date().toISOString(),
2323 |     };
2324 |   }
2325 | 
2326 |   /**
2327 |    * Create a truncated node when max depth is reached or circular reference detected
2328 |    */
2329 |   private createTruncatedNode(
2330 |     func: FunctionSignature,
2331 |     filePath: string,
2332 |     depth: number,
2333 |     _isCircular: boolean,
2334 |   ): CallGraphNode {
2335 |     return {
2336 |       function: func,
2337 |       location: {
2338 |         file: filePath,
2339 |         line: func.startLine,
2340 |       },
2341 |       calls: [],
2342 |       conditionalBranches: [],
2343 |       exceptions: [],
2344 |       depth,
2345 |       truncated: true,
2346 |       isExternal: false,
2347 |     };
2348 |   }
2349 | }
2350 | 
```
Page 29/33FirstPrevNextLast