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

# Directory Structure

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

# Files

--------------------------------------------------------------------------------
/tests/memory/kg-storage-validation.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Tests for uncovered branches in KGStorage
  3 |  * Covers: Error handling (lines 197, 276), backup restoration with timestamp (lines 453-455),
  4 |  * validation errors (lines 496, 510), and other edge cases
  5 |  */
  6 | 
  7 | import { describe, it, expect, beforeEach, afterEach } from "@jest/globals";
  8 | import { promises as fs } from "fs";
  9 | import { join } from "path";
 10 | import { KGStorage } from "../../src/memory/kg-storage.js";
 11 | import { GraphNode, GraphEdge } from "../../src/memory/knowledge-graph.js";
 12 | import { tmpdir } from "os";
 13 | 
 14 | describe("KGStorage - Validation and Error Handling", () => {
 15 |   let storage: KGStorage;
 16 |   let testDir: string;
 17 | 
 18 |   beforeEach(async () => {
 19 |     testDir = join(tmpdir(), `kg-storage-validation-test-${Date.now()}`);
 20 |     await fs.mkdir(testDir, { recursive: true });
 21 | 
 22 |     storage = new KGStorage({
 23 |       storageDir: testDir,
 24 |       backupOnWrite: true,
 25 |       validateOnRead: true,
 26 |     });
 27 | 
 28 |     await storage.initialize();
 29 |   });
 30 | 
 31 |   afterEach(async () => {
 32 |     try {
 33 |       await fs.rm(testDir, { recursive: true, force: true });
 34 |     } catch (error) {
 35 |       // Ignore cleanup errors
 36 |     }
 37 |   });
 38 | 
 39 |   describe("Load Error Handling", () => {
 40 |     it("should handle non-JSON lines in loadEntities gracefully (line 188)", async () => {
 41 |       const entityFile = join(testDir, "knowledge-graph-entities.jsonl");
 42 | 
 43 |       // Write marker + valid entity + invalid JSON + another valid entity
 44 |       await fs.writeFile(
 45 |         entityFile,
 46 |         "# DOCUMCP_KNOWLEDGE_GRAPH_ENTITIES v1.0.0\n" +
 47 |           '{"id":"e1","type":"project","label":"Project 1","properties":{},"weight":1.0,"lastUpdated":"2024-01-01"}\n' +
 48 |           "invalid json line {this is not valid}\n" +
 49 |           '{"id":"e2","type":"project","label":"Project 2","properties":{},"weight":1.0,"lastUpdated":"2024-01-01"}\n',
 50 |         "utf-8",
 51 |       );
 52 | 
 53 |       // Should load valid entities and skip invalid line
 54 |       const entities = await storage.loadEntities();
 55 | 
 56 |       expect(entities.length).toBe(2);
 57 |       expect(entities[0].id).toBe("e1");
 58 |       expect(entities[1].id).toBe("e2");
 59 |     });
 60 | 
 61 |     it("should handle non-JSON lines in loadRelationships gracefully (line 267)", async () => {
 62 |       const relationshipFile = join(
 63 |         testDir,
 64 |         "knowledge-graph-relationships.jsonl",
 65 |       );
 66 | 
 67 |       // Write marker + valid relationship + invalid JSON + another valid relationship
 68 |       await fs.writeFile(
 69 |         relationshipFile,
 70 |         "# DOCUMCP_KNOWLEDGE_GRAPH_RELATIONSHIPS v1.0.0\n" +
 71 |           '{"id":"r1","source":"s1","target":"t1","type":"uses","label":"Uses","properties":{},"weight":1.0,"lastUpdated":"2024-01-01"}\n' +
 72 |           "corrupted json {missing quotes and brackets\n" +
 73 |           '{"id":"r2","source":"s2","target":"t2","type":"uses","label":"Uses","properties":{},"weight":1.0,"lastUpdated":"2024-01-01"}\n',
 74 |         "utf-8",
 75 |       );
 76 | 
 77 |       // Should load valid relationships and skip invalid line
 78 |       const relationships = await storage.loadRelationships();
 79 | 
 80 |       expect(relationships.length).toBe(2);
 81 |       expect(relationships[0].id).toBe("r1");
 82 |       expect(relationships[1].id).toBe("r2");
 83 |     });
 84 | 
 85 |     it("should throw error when loadEntities encounters non-ENOENT error (line 197-201)", async () => {
 86 |       const entityFile = join(testDir, "knowledge-graph-entities.jsonl");
 87 | 
 88 |       // Create file with proper marker
 89 |       await fs.writeFile(
 90 |         entityFile,
 91 |         "# DOCUMCP_KNOWLEDGE_GRAPH_ENTITIES v1.0.0\n",
 92 |         "utf-8",
 93 |       );
 94 | 
 95 |       // Make file unreadable by changing permissions (Unix-like systems)
 96 |       if (process.platform !== "win32") {
 97 |         await fs.chmod(entityFile, 0o000);
 98 | 
 99 |         await expect(storage.loadEntities()).rejects.toThrow();
100 | 
101 |         // Restore permissions for cleanup
102 |         await fs.chmod(entityFile, 0o644);
103 |       }
104 |     });
105 | 
106 |     it("should throw error when loadRelationships encounters non-ENOENT error (line 276-280)", async () => {
107 |       const relationshipFile = join(
108 |         testDir,
109 |         "knowledge-graph-relationships.jsonl",
110 |       );
111 | 
112 |       // Create file with proper marker
113 |       await fs.writeFile(
114 |         relationshipFile,
115 |         "# DOCUMCP_KNOWLEDGE_GRAPH_RELATIONSHIPS v1.0.0\n",
116 |         "utf-8",
117 |       );
118 | 
119 |       // Make file unreadable (Unix-like systems)
120 |       if (process.platform !== "win32") {
121 |         await fs.chmod(relationshipFile, 0o000);
122 | 
123 |         await expect(storage.loadRelationships()).rejects.toThrow();
124 | 
125 |         // Restore permissions for cleanup
126 |         await fs.chmod(relationshipFile, 0o644);
127 |       }
128 |     });
129 |   });
130 | 
131 |   describe("Validation Errors", () => {
132 |     it("should validate entity structure and throw on invalid entity (line 496)", async () => {
133 |       const invalidEntity = {
134 |         // Missing required 'type' and 'label' fields
135 |         id: "invalid-entity",
136 |         properties: {},
137 |         weight: 1.0,
138 |         lastUpdated: "2024-01-01",
139 |       } as unknown as GraphNode;
140 | 
141 |       // Create a storage with validation enabled
142 |       const validatingStorage = new KGStorage({
143 |         storageDir: testDir,
144 |         validateOnRead: true,
145 |       });
146 |       await validatingStorage.initialize();
147 | 
148 |       // Write invalid entity to file
149 |       const entityFile = join(testDir, "knowledge-graph-entities.jsonl");
150 |       await fs.writeFile(
151 |         entityFile,
152 |         "# DOCUMCP_KNOWLEDGE_GRAPH_ENTITIES v1.0.0\n" +
153 |           '{"id":"invalid-entity","properties":{},"weight":1.0,"lastUpdated":"2024-01-01"}\n',
154 |         "utf-8",
155 |       );
156 | 
157 |       // Loading should skip the invalid entity (caught and logged)
158 |       const entities = await validatingStorage.loadEntities();
159 |       expect(entities.length).toBe(0); // Invalid entity skipped
160 |     });
161 | 
162 |     it("should validate relationship structure and throw on invalid relationship (line 510)", async () => {
163 |       // Create storage with validation enabled
164 |       const validatingStorage = new KGStorage({
165 |         storageDir: testDir,
166 |         validateOnRead: true,
167 |       });
168 |       await validatingStorage.initialize();
169 | 
170 |       // Write invalid relationship (missing 'type' field)
171 |       const relationshipFile = join(
172 |         testDir,
173 |         "knowledge-graph-relationships.jsonl",
174 |       );
175 |       await fs.writeFile(
176 |         relationshipFile,
177 |         "# DOCUMCP_KNOWLEDGE_GRAPH_RELATIONSHIPS v1.0.0\n" +
178 |           '{"id":"r1","source":"s1","target":"t1","label":"Invalid","properties":{},"weight":1.0}\n',
179 |         "utf-8",
180 |       );
181 | 
182 |       // Loading should skip the invalid relationship
183 |       const relationships = await validatingStorage.loadRelationships();
184 |       expect(relationships.length).toBe(0); // Invalid relationship skipped
185 |     });
186 | 
187 |     it("should validate entity has required fields: id, type, label (line 495-497)", async () => {
188 |       const validatingStorage = new KGStorage({
189 |         storageDir: testDir,
190 |         validateOnRead: true,
191 |       });
192 |       await validatingStorage.initialize();
193 | 
194 |       const entityFile = join(testDir, "knowledge-graph-entities.jsonl");
195 | 
196 |       // Test missing 'id'
197 |       await fs.writeFile(
198 |         entityFile,
199 |         "# DOCUMCP_KNOWLEDGE_GRAPH_ENTITIES v1.0.0\n" +
200 |           '{"type":"project","label":"No ID","properties":{},"weight":1.0,"lastUpdated":"2024-01-01"}\n',
201 |         "utf-8",
202 |       );
203 | 
204 |       let entities = await validatingStorage.loadEntities();
205 |       expect(entities.length).toBe(0);
206 | 
207 |       // Test missing 'type'
208 |       await fs.writeFile(
209 |         entityFile,
210 |         "# DOCUMCP_KNOWLEDGE_GRAPH_ENTITIES v1.0.0\n" +
211 |           '{"id":"e1","label":"No Type","properties":{},"weight":1.0,"lastUpdated":"2024-01-01"}\n',
212 |         "utf-8",
213 |       );
214 | 
215 |       entities = await validatingStorage.loadEntities();
216 |       expect(entities.length).toBe(0);
217 | 
218 |       // Test missing 'label'
219 |       await fs.writeFile(
220 |         entityFile,
221 |         "# DOCUMCP_KNOWLEDGE_GRAPH_ENTITIES v1.0.0\n" +
222 |           '{"id":"e1","type":"project","properties":{},"weight":1.0,"lastUpdated":"2024-01-01"}\n',
223 |         "utf-8",
224 |       );
225 | 
226 |       entities = await validatingStorage.loadEntities();
227 |       expect(entities.length).toBe(0);
228 |     });
229 | 
230 |     it("should validate relationship has required fields: id, source, target, type (line 504-512)", async () => {
231 |       const validatingStorage = new KGStorage({
232 |         storageDir: testDir,
233 |         validateOnRead: true,
234 |       });
235 |       await validatingStorage.initialize();
236 | 
237 |       const relationshipFile = join(
238 |         testDir,
239 |         "knowledge-graph-relationships.jsonl",
240 |       );
241 | 
242 |       // Test missing 'id'
243 |       await fs.writeFile(
244 |         relationshipFile,
245 |         "# DOCUMCP_KNOWLEDGE_GRAPH_RELATIONSHIPS v1.0.0\n" +
246 |           '{"source":"s1","target":"t1","type":"uses","label":"Uses","properties":{},"weight":1.0}\n',
247 |         "utf-8",
248 |       );
249 | 
250 |       let relationships = await validatingStorage.loadRelationships();
251 |       expect(relationships.length).toBe(0);
252 | 
253 |       // Test missing 'source'
254 |       await fs.writeFile(
255 |         relationshipFile,
256 |         "# DOCUMCP_KNOWLEDGE_GRAPH_RELATIONSHIPS v1.0.0\n" +
257 |           '{"id":"r1","target":"t1","type":"uses","label":"Uses","properties":{},"weight":1.0}\n',
258 |         "utf-8",
259 |       );
260 | 
261 |       relationships = await validatingStorage.loadRelationships();
262 |       expect(relationships.length).toBe(0);
263 | 
264 |       // Test missing 'target'
265 |       await fs.writeFile(
266 |         relationshipFile,
267 |         "# DOCUMCP_KNOWLEDGE_GRAPH_RELATIONSHIPS v1.0.0\n" +
268 |           '{"id":"r1","source":"s1","type":"uses","label":"Uses","properties":{},"weight":1.0}\n',
269 |         "utf-8",
270 |       );
271 | 
272 |       relationships = await validatingStorage.loadRelationships();
273 |       expect(relationships.length).toBe(0);
274 | 
275 |       // Test missing 'type'
276 |       await fs.writeFile(
277 |         relationshipFile,
278 |         "# DOCUMCP_KNOWLEDGE_GRAPH_RELATIONSHIPS v1.0.0\n" +
279 |           '{"id":"r1","source":"s1","target":"t1","label":"Uses","properties":{},"weight":1.0}\n',
280 |         "utf-8",
281 |       );
282 | 
283 |       relationships = await validatingStorage.loadRelationships();
284 |       expect(relationships.length).toBe(0);
285 |     });
286 | 
287 |     it("should not validate when validateOnRead is false", async () => {
288 |       const nonValidatingStorage = new KGStorage({
289 |         storageDir: testDir,
290 |         validateOnRead: false,
291 |       });
292 |       await nonValidatingStorage.initialize();
293 | 
294 |       const entityFile = join(testDir, "knowledge-graph-entities.jsonl");
295 | 
296 |       // Write entity missing required fields
297 |       await fs.writeFile(
298 |         entityFile,
299 |         "# DOCUMCP_KNOWLEDGE_GRAPH_ENTITIES v1.0.0\n" +
300 |           '{"id":"e1","properties":{}}\n',
301 |         "utf-8",
302 |       );
303 | 
304 |       // Should load without validation (parse as-is)
305 |       const entities = await nonValidatingStorage.loadEntities();
306 |       expect(entities.length).toBe(1);
307 |       expect(entities[0].id).toBe("e1");
308 |     });
309 |   });
310 | 
311 |   describe("Backup Restoration with Timestamp", () => {
312 |     // TODO: Fix timing issue with backup file creation
313 |     it.skip("should restore from backup with specific timestamp (lines 451-455)", async () => {
314 |       const entities: GraphNode[] = [
315 |         {
316 |           id: "project:backup1",
317 |           type: "project",
318 |           label: "Backup Test 1",
319 |           properties: {},
320 |           weight: 1.0,
321 |           lastUpdated: "2024-01-01",
322 |         },
323 |       ];
324 | 
325 |       // Save first version
326 |       await storage.saveEntities(entities);
327 | 
328 |       // Get list of backups to find the timestamp
329 |       const backupDir = join(testDir, "backups");
330 |       const backups = await fs.readdir(backupDir);
331 |       const entityBackups = backups.filter((f) => f.startsWith("entities-"));
332 | 
333 |       expect(entityBackups.length).toBeGreaterThan(0);
334 | 
335 |       // Extract timestamp from backup filename (format: entities-YYYY-MM-DDTHH-MM-SS-MMMZ.jsonl)
336 |       const backupFilename = entityBackups[0];
337 |       const timestampMatch = backupFilename.match(/entities-(.*?)\.jsonl/);
338 |       expect(timestampMatch).not.toBeNull();
339 | 
340 |       const timestamp = timestampMatch![1];
341 | 
342 |       // Modify entities
343 |       const modifiedEntities: GraphNode[] = [
344 |         {
345 |           id: "project:backup2",
346 |           type: "project",
347 |           label: "Modified",
348 |           properties: {},
349 |           weight: 1.0,
350 |           lastUpdated: "2024-01-02",
351 |         },
352 |       ];
353 |       await storage.saveEntities(modifiedEntities);
354 | 
355 |       // Verify current state
356 |       let current = await storage.loadEntities();
357 |       expect(current.length).toBe(1);
358 |       expect(current[0].id).toBe("project:backup2");
359 | 
360 |       // Restore from backup using specific timestamp
361 |       await storage.restoreFromBackup("entities", timestamp);
362 | 
363 |       // Verify restored state
364 |       current = await storage.loadEntities();
365 |       expect(current.length).toBe(1);
366 |       expect(current[0].id).toBe("project:backup1");
367 |     });
368 | 
369 |     it("should throw error when backup with timestamp not found (line 454-456)", async () => {
370 |       const entities: GraphNode[] = [
371 |         {
372 |           id: "project:test",
373 |           type: "project",
374 |           label: "Test",
375 |           properties: {},
376 |           weight: 1.0,
377 |           lastUpdated: "2024-01-01",
378 |         },
379 |       ];
380 | 
381 |       await storage.saveEntities(entities);
382 | 
383 |       // Try to restore with non-existent timestamp
384 |       await expect(
385 |         storage.restoreFromBackup("entities", "2099-12-31T23-59-59-999Z"),
386 |       ).rejects.toThrow("Backup with timestamp");
387 |     });
388 | 
389 |     it("should restore most recent backup when no timestamp specified (line 458-467)", async () => {
390 |       const entities1: GraphNode[] = [
391 |         {
392 |           id: "project:v1",
393 |           type: "project",
394 |           label: "Version 1",
395 |           properties: {},
396 |           weight: 1.0,
397 |           lastUpdated: "2024-01-01",
398 |         },
399 |       ];
400 | 
401 |       await storage.saveEntities(entities1);
402 | 
403 |       // Wait a bit to ensure different timestamps
404 |       await new Promise((resolve) => setTimeout(resolve, 100));
405 | 
406 |       const entities2: GraphNode[] = [
407 |         {
408 |           id: "project:v2",
409 |           type: "project",
410 |           label: "Version 2",
411 |           properties: {},
412 |           weight: 1.0,
413 |           lastUpdated: "2024-01-02",
414 |         },
415 |       ];
416 | 
417 |       await storage.saveEntities(entities2);
418 | 
419 |       await new Promise((resolve) => setTimeout(resolve, 100));
420 | 
421 |       const entities3: GraphNode[] = [
422 |         {
423 |           id: "project:v3",
424 |           type: "project",
425 |           label: "Version 3 (current)",
426 |           properties: {},
427 |           weight: 1.0,
428 |           lastUpdated: "2024-01-03",
429 |         },
430 |       ];
431 | 
432 |       await storage.saveEntities(entities3);
433 | 
434 |       // Current state should be v3
435 |       let current = await storage.loadEntities();
436 |       expect(current[0].id).toBe("project:v3");
437 | 
438 |       // Restore without timestamp (should get most recent backup = v2)
439 |       await storage.restoreFromBackup("entities");
440 | 
441 |       current = await storage.loadEntities();
442 |       expect(current[0].id).toBe("project:v2");
443 |     });
444 | 
445 |     // TODO: Fix timing issue with backup file creation
446 |     it.skip("should restore relationships with timestamp", async () => {
447 |       const relationships1: GraphEdge[] = [
448 |         {
449 |           id: "rel:v1",
450 |           source: "s1",
451 |           target: "t1",
452 |           type: "uses",
453 |           properties: {},
454 |           weight: 1.0,
455 |           confidence: 1.0,
456 |           lastUpdated: "2024-01-01",
457 |         },
458 |       ];
459 | 
460 |       await storage.saveRelationships(relationships1);
461 | 
462 |       // Get backup timestamp
463 |       const backupDir = join(testDir, "backups");
464 |       const backups = await fs.readdir(backupDir);
465 |       const relBackups = backups.filter((f) => f.startsWith("relationships-"));
466 | 
467 |       expect(relBackups.length).toBeGreaterThan(0);
468 | 
469 |       const timestampMatch = relBackups[0].match(/relationships-(.*?)\.jsonl/);
470 |       const timestamp = timestampMatch![1];
471 | 
472 |       // Modify relationships
473 |       const relationships2: GraphEdge[] = [
474 |         {
475 |           id: "rel:v2",
476 |           source: "s2",
477 |           target: "t2",
478 |           type: "uses",
479 |           properties: {},
480 |           weight: 1.0,
481 |           confidence: 1.0,
482 |           lastUpdated: "2024-01-02",
483 |         },
484 |       ];
485 | 
486 |       await storage.saveRelationships(relationships2);
487 | 
488 |       // Restore from backup using timestamp
489 |       await storage.restoreFromBackup("relationships", timestamp);
490 | 
491 |       const restored = await storage.loadRelationships();
492 |       expect(restored[0].id).toBe("rel:v1");
493 |     });
494 | 
495 |     it("should throw error when no backups exist (line 445-447)", async () => {
496 |       // Try to restore when no backups exist
497 |       await expect(storage.restoreFromBackup("entities")).rejects.toThrow(
498 |         "No backups found",
499 |       );
500 |     });
501 | 
502 |     it("should log restoration in debug mode (line 478-481)", async () => {
503 |       const entities: GraphNode[] = [
504 |         {
505 |           id: "project:debug",
506 |           type: "project",
507 |           label: "Debug Test",
508 |           properties: {},
509 |           weight: 1.0,
510 |           lastUpdated: "2024-01-01",
511 |         },
512 |       ];
513 | 
514 |       await storage.saveEntities(entities);
515 | 
516 |       // Verify initial save worked
517 |       const initial = await storage.loadEntities();
518 |       expect(initial).toHaveLength(1);
519 |       expect(initial[0].id).toBe("project:debug");
520 | 
521 |       // Set DEBUG env var
522 |       const originalDebug = process.env.DEBUG;
523 |       process.env.DEBUG = "true";
524 | 
525 |       // Modify - this will create a backup of the original entities
526 |       const modifiedEntities: GraphNode[] = [
527 |         {
528 |           id: "project:modified",
529 |           type: "project",
530 |           label: "Modified",
531 |           properties: {},
532 |           weight: 1.0,
533 |           lastUpdated: "2024-01-02",
534 |         },
535 |       ];
536 |       await storage.saveEntities(modifiedEntities);
537 | 
538 |       // Verify backup was created by checking backups directory
539 |       const backupDir = join(testDir, "backups");
540 |       const backupFiles = await fs.readdir(backupDir);
541 |       const entityBackups = backupFiles.filter((f) => f.startsWith("entities"));
542 |       expect(entityBackups.length).toBeGreaterThan(0);
543 | 
544 |       // Restore (should log in debug mode)
545 |       await storage.restoreFromBackup("entities");
546 | 
547 |       // Restore original DEBUG setting
548 |       if (originalDebug !== undefined) {
549 |         process.env.DEBUG = originalDebug;
550 |       } else {
551 |         delete process.env.DEBUG;
552 |       }
553 | 
554 |       // Verify restoration worked
555 |       const restored = await storage.loadEntities();
556 |       expect(restored).toHaveLength(1);
557 |       expect(restored[0].id).toBe("project:debug");
558 |     });
559 |   });
560 | 
561 |   describe("Error Handling Edge Cases", () => {
562 |     it("should handle backup file access errors gracefully (line 337-340)", async () => {
563 |       // This tests the warning path when backup fails due to file access issues
564 |       const storage2 = new KGStorage({
565 |         storageDir: testDir,
566 |         backupOnWrite: true,
567 |       });
568 | 
569 |       await storage2.initialize();
570 | 
571 |       // Save initial entities to create a file
572 |       const initialEntities: GraphNode[] = [
573 |         {
574 |           id: "project:initial",
575 |           type: "project",
576 |           label: "Initial",
577 |           properties: {},
578 |           weight: 1.0,
579 |           lastUpdated: "2024-01-01",
580 |         },
581 |       ];
582 |       await storage2.saveEntities(initialEntities);
583 | 
584 |       // Make entity file unreadable (Unix-like systems only) to trigger backup error
585 |       const entityFile = join(testDir, "knowledge-graph-entities.jsonl");
586 |       if (process.platform !== "win32") {
587 |         try {
588 |           await fs.chmod(entityFile, 0o000);
589 |         } catch (e) {
590 |           // Skip test if chmod not supported
591 |           return;
592 |         }
593 |       }
594 | 
595 |       // Saving should still attempt even if backup fails with non-ENOENT error
596 |       const newEntities: GraphNode[] = [
597 |         {
598 |           id: "project:no-backup",
599 |           type: "project",
600 |           label: "No Backup",
601 |           properties: {},
602 |           weight: 1.0,
603 |           lastUpdated: "2024-01-02",
604 |         },
605 |       ];
606 | 
607 |       // Will fail during backup read, but should warn and continue
608 |       // This tests line 337-339: if (error.code !== "ENOENT")
609 |       try {
610 |         await storage2.saveEntities(newEntities);
611 |       } catch (error) {
612 |         // Might throw due to unreadable file
613 |       }
614 | 
615 |       // Restore permissions for cleanup
616 |       if (process.platform !== "win32") {
617 |         try {
618 |           await fs.chmod(entityFile, 0o644);
619 |         } catch (e) {
620 |           // Ignore
621 |         }
622 |       }
623 |     });
624 | 
625 |     it("should handle cleanup of backups when file is deleted during iteration (line 369-371)", async () => {
626 |       // Create multiple backups
627 |       const entities: GraphNode[] = [
628 |         {
629 |           id: "project:cleanup",
630 |           type: "project",
631 |           label: "Cleanup Test",
632 |           properties: {},
633 |           weight: 1.0,
634 |           lastUpdated: "2024-01-01",
635 |         },
636 |       ];
637 | 
638 |       // Create many backups (more than keepCount of 10)
639 |       for (let i = 0; i < 15; i++) {
640 |         await storage.saveEntities([
641 |           {
642 |             ...entities[0],
643 |             id: `project:cleanup-${i}`,
644 |             label: `Cleanup Test ${i}`,
645 |           },
646 |         ]);
647 |         await new Promise((resolve) => setTimeout(resolve, 10));
648 |       }
649 | 
650 |       // Old backups should be cleaned up automatically
651 |       const backupDir = join(testDir, "backups");
652 |       const backups = await fs.readdir(backupDir);
653 |       const entityBackups = backups.filter((f) => f.startsWith("entities-"));
654 | 
655 |       // Should keep last 10 backups
656 |       expect(entityBackups.length).toBeLessThanOrEqual(10);
657 |     });
658 | 
659 |     it("should handle missing backup directory gracefully (line 388-391)", async () => {
660 |       // Create storage without creating backups first
661 |       const testDir2 = join(tmpdir(), `kg-no-backup-${Date.now()}`);
662 |       await fs.mkdir(testDir2, { recursive: true });
663 | 
664 |       const storage2 = new KGStorage({
665 |         storageDir: testDir2,
666 |         backupOnWrite: false, // Disable backups
667 |       });
668 | 
669 |       await storage2.initialize();
670 | 
671 |       const entities: GraphNode[] = [
672 |         {
673 |           id: "project:no-backup-dir",
674 |           type: "project",
675 |           label: "No Backup Dir",
676 |           properties: {},
677 |           weight: 1.0,
678 |           lastUpdated: "2024-01-01",
679 |         },
680 |       ];
681 | 
682 |       // Should work fine without backup directory
683 |       await storage2.saveEntities(entities);
684 | 
685 |       const loaded = await storage2.loadEntities();
686 |       expect(loaded.length).toBe(1);
687 | 
688 |       await fs.rm(testDir2, { recursive: true, force: true });
689 |     });
690 |   });
691 | 
692 |   describe("Verify Integrity Coverage", () => {
693 |     it("should detect orphaned relationships - missing source (line 535-538)", async () => {
694 |       const entities: GraphNode[] = [
695 |         {
696 |           id: "project:exists",
697 |           type: "project",
698 |           label: "Exists",
699 |           properties: {},
700 |           weight: 1.0,
701 |           lastUpdated: "2024-01-01",
702 |         },
703 |       ];
704 | 
705 |       const relationships: GraphEdge[] = [
706 |         {
707 |           id: "rel:orphan",
708 |           source: "project:missing",
709 |           target: "project:exists",
710 |           type: "uses",
711 |           properties: {},
712 |           weight: 1.0,
713 |           confidence: 1.0,
714 |           lastUpdated: "2024-01-01",
715 |         },
716 |       ];
717 | 
718 |       await storage.saveEntities(entities);
719 |       await storage.saveRelationships(relationships);
720 | 
721 |       const result = await storage.verifyIntegrity();
722 | 
723 |       expect(result.warnings.length).toBeGreaterThan(0);
724 |       expect(
725 |         result.warnings.some((w) => w.includes("missing source entity")),
726 |       ).toBe(true);
727 |     });
728 | 
729 |     it("should detect orphaned relationships - missing target (line 540-544)", async () => {
730 |       const entities: GraphNode[] = [
731 |         {
732 |           id: "project:exists",
733 |           type: "project",
734 |           label: "Exists",
735 |           properties: {},
736 |           weight: 1.0,
737 |           lastUpdated: "2024-01-01",
738 |         },
739 |       ];
740 | 
741 |       const relationships: GraphEdge[] = [
742 |         {
743 |           id: "rel:orphan",
744 |           source: "project:exists",
745 |           target: "project:missing",
746 |           type: "uses",
747 |           properties: {},
748 |           weight: 1.0,
749 |           confidence: 1.0,
750 |           lastUpdated: "2024-01-01",
751 |         },
752 |       ];
753 | 
754 |       await storage.saveEntities(entities);
755 |       await storage.saveRelationships(relationships);
756 | 
757 |       const result = await storage.verifyIntegrity();
758 | 
759 |       expect(result.warnings.length).toBeGreaterThan(0);
760 |       expect(
761 |         result.warnings.some((w) => w.includes("missing target entity")),
762 |       ).toBe(true);
763 |     });
764 | 
765 |     // TODO: Fix - validation prevents corrupted data from being loaded
766 |     it.skip("should catch errors during integrity check (lines 564-570)", async () => {
767 |       // Save valid data
768 |       const entities: GraphNode[] = [
769 |         {
770 |           id: "project:test",
771 |           type: "project",
772 |           label: "Test",
773 |           properties: {},
774 |           weight: 1.0,
775 |           lastUpdated: "2024-01-01",
776 |         },
777 |       ];
778 | 
779 |       await storage.saveEntities(entities);
780 | 
781 |       // Corrupt the entity file to cause a parse error
782 |       const entityFile = join(testDir, "knowledge-graph-entities.jsonl");
783 |       await fs.writeFile(
784 |         entityFile,
785 |         "# DOCUMCP_KNOWLEDGE_GRAPH_ENTITIES v1.0.0\nthis is not valid json\n",
786 |         "utf-8",
787 |       );
788 | 
789 |       // Integrity check should catch the error
790 |       const result = await storage.verifyIntegrity();
791 | 
792 |       expect(result.valid).toBe(false);
793 |       expect(result.errors.length).toBeGreaterThan(0);
794 |       expect(result.errors[0]).toContain("Integrity check failed");
795 |     });
796 |   });
797 | });
798 | 
```

--------------------------------------------------------------------------------
/src/memory/contextual-retrieval.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Contextual Memory Retrieval System for DocuMCP
  3 |  * Implements Issue #49: Contextual Memory Retrieval
  4 |  *
  5 |  * Provides intelligent, context-aware memory retrieval using semantic similarity,
  6 |  * temporal relevance, and user intent analysis for enhanced recommendation accuracy.
  7 |  */
  8 | 
  9 | import { MemoryManager } from "./manager.js";
 10 | import { MemoryEntry } from "./storage.js";
 11 | import { KnowledgeGraph } from "./knowledge-graph.js";
 12 | 
 13 | export interface RetrievalContext {
 14 |   currentProject?: {
 15 |     path: string;
 16 |     language: string;
 17 |     framework?: string;
 18 |     domain?: string;
 19 |     size?: "small" | "medium" | "large";
 20 |   };
 21 |   userIntent?: {
 22 |     action: "analyze" | "recommend" | "deploy" | "troubleshoot" | "learn";
 23 |     urgency: "low" | "medium" | "high";
 24 |     experience: "novice" | "intermediate" | "expert";
 25 |   };
 26 |   sessionContext?: {
 27 |     recentActions: string[];
 28 |     focusAreas: string[];
 29 |     timeConstraints?: number; // minutes
 30 |   };
 31 |   temporalContext?: {
 32 |     timeRange?: { start: string; end: string };
 33 |     recency: "recent" | "all" | "historical";
 34 |     seasonality?: boolean;
 35 |   };
 36 | }
 37 | 
 38 | export interface SemanticEmbedding {
 39 |   vector: number[];
 40 |   metadata: {
 41 |     source: string;
 42 |     confidence: number;
 43 |     generatedAt: string;
 44 |   };
 45 | }
 46 | 
 47 | export interface ContextualMatch {
 48 |   memory: MemoryEntry;
 49 |   relevanceScore: number;
 50 |   contextualFactors: {
 51 |     semantic: number;
 52 |     temporal: number;
 53 |     structural: number;
 54 |     intentional: number;
 55 |   };
 56 |   reasoning: string[];
 57 |   confidence: number;
 58 | }
 59 | 
 60 | export interface RetrievalResult {
 61 |   matches: ContextualMatch[];
 62 |   metadata: {
 63 |     queryContext: RetrievalContext;
 64 |     totalCandidates: number;
 65 |     processingTime: number;
 66 |     fallbackUsed: boolean;
 67 |   };
 68 |   insights: {
 69 |     patterns: string[];
 70 |     recommendations: string[];
 71 |     gaps: string[];
 72 |   };
 73 | }
 74 | 
 75 | export class ContextualMemoryRetrieval {
 76 |   private memoryManager: MemoryManager;
 77 |   private knowledgeGraph: KnowledgeGraph;
 78 |   private embeddingCache: Map<string, SemanticEmbedding>;
 79 |   private readonly maxCacheSize = 1000;
 80 |   private readonly similarityThreshold = 0.6;
 81 | 
 82 |   constructor(memoryManager: MemoryManager, knowledgeGraph: KnowledgeGraph) {
 83 |     this.memoryManager = memoryManager;
 84 |     this.knowledgeGraph = knowledgeGraph;
 85 |     this.embeddingCache = new Map();
 86 |   }
 87 | 
 88 |   /**
 89 |    * Retrieve contextually relevant memories
 90 |    */
 91 |   async retrieve(
 92 |     query: string,
 93 |     context: RetrievalContext,
 94 |     options?: {
 95 |       maxResults?: number;
 96 |       minRelevance?: number;
 97 |       includeReasoning?: boolean;
 98 |     },
 99 |   ): Promise<RetrievalResult> {
100 |     const startTime = Date.now();
101 |     const maxResults = options?.maxResults || 10;
102 |     const minRelevance = options?.minRelevance || 0.3;
103 | 
104 |     // Get candidate memories based on basic filtering
105 |     const candidates = await this.getCandidateMemories(query, context);
106 | 
107 |     // Score and rank candidates
108 |     const scoredMatches = await this.scoreAndRankCandidates(
109 |       candidates,
110 |       query,
111 |       context,
112 |     );
113 | 
114 |     // Filter by relevance threshold
115 |     const relevantMatches = scoredMatches
116 |       .filter((match) => match.relevanceScore >= minRelevance)
117 |       .slice(0, maxResults);
118 | 
119 |     // Generate insights from matches
120 |     const insights = await this.generateInsights(relevantMatches, context);
121 | 
122 |     const processingTime = Date.now() - startTime;
123 | 
124 |     return {
125 |       matches: relevantMatches,
126 |       metadata: {
127 |         queryContext: context,
128 |         totalCandidates: candidates.length,
129 |         processingTime,
130 |         fallbackUsed: relevantMatches.length === 0 && candidates.length > 0,
131 |       },
132 |       insights,
133 |     };
134 |   }
135 | 
136 |   /**
137 |    * Get candidate memories using multiple retrieval strategies
138 |    */
139 |   private async getCandidateMemories(
140 |     query: string,
141 |     context: RetrievalContext,
142 |   ): Promise<MemoryEntry[]> {
143 |     const candidates = new Map<string, MemoryEntry>();
144 | 
145 |     // Strategy 1: Text-based search
146 |     const textMatches = await this.memoryManager.search(query, {
147 |       sortBy: "timestamp",
148 |     });
149 |     textMatches.forEach((memory) => candidates.set(memory.id, memory));
150 | 
151 |     // Strategy 2: Context-based filtering
152 |     if (context.currentProject) {
153 |       const contextMatches = await this.getContextBasedCandidates(
154 |         context.currentProject,
155 |       );
156 |       contextMatches.forEach((memory) => candidates.set(memory.id, memory));
157 |     }
158 | 
159 |     // Strategy 3: Intent-based retrieval
160 |     if (context.userIntent) {
161 |       const intentMatches = await this.getIntentBasedCandidates(
162 |         context.userIntent,
163 |       );
164 |       intentMatches.forEach((memory) => candidates.set(memory.id, memory));
165 |     }
166 | 
167 |     // Strategy 4: Temporal filtering
168 |     if (context.temporalContext) {
169 |       const temporalMatches = await this.getTemporalCandidates(
170 |         context.temporalContext,
171 |       );
172 |       temporalMatches.forEach((memory) => candidates.set(memory.id, memory));
173 |     }
174 | 
175 |     // Strategy 5: Knowledge graph traversal
176 |     const graphMatches = await this.getGraphBasedCandidates(query, context);
177 |     graphMatches.forEach((memory) => candidates.set(memory.id, memory));
178 | 
179 |     return Array.from(candidates.values());
180 |   }
181 | 
182 |   /**
183 |    * Get candidates based on current project context
184 |    */
185 |   private async getContextBasedCandidates(
186 |     project: NonNullable<RetrievalContext["currentProject"]>,
187 |   ): Promise<MemoryEntry[]> {
188 |     const searchCriteria = [];
189 | 
190 |     // Language-based search
191 |     searchCriteria.push(
192 |       this.memoryManager
193 |         .search("", { sortBy: "timestamp" })
194 |         .then((memories) =>
195 |           memories.filter(
196 |             (m) =>
197 |               m.data.language?.primary === project.language ||
198 |               m.metadata.tags?.includes(project.language),
199 |           ),
200 |         ),
201 |     );
202 | 
203 |     // Framework-based search
204 |     if (project.framework) {
205 |       searchCriteria.push(
206 |         this.memoryManager
207 |           .search("", { sortBy: "timestamp" })
208 |           .then((memories) =>
209 |             memories.filter(
210 |               (m) =>
211 |                 m.data.framework?.name === project.framework ||
212 |                 (project.framework &&
213 |                   m.metadata.tags?.includes(project.framework)),
214 |             ),
215 |           ),
216 |       );
217 |     }
218 | 
219 |     // Project size similarity
220 |     if (project.size) {
221 |       searchCriteria.push(
222 |         this.memoryManager
223 |           .search("", { sortBy: "timestamp" })
224 |           .then((memories) =>
225 |             memories.filter(
226 |               (m) =>
227 |                 this.categorizeProjectSize(m.data.stats?.files || 0) ===
228 |                 project.size,
229 |             ),
230 |           ),
231 |       );
232 |     }
233 | 
234 |     const results = await Promise.all(searchCriteria);
235 |     const allMatches = results.flat();
236 | 
237 |     // Deduplicate
238 |     const unique = new Map<string, MemoryEntry>();
239 |     allMatches.forEach((memory) => unique.set(memory.id, memory));
240 | 
241 |     return Array.from(unique.values());
242 |   }
243 | 
244 |   /**
245 |    * Get candidates based on user intent
246 |    */
247 |   private async getIntentBasedCandidates(
248 |     intent: NonNullable<RetrievalContext["userIntent"]>,
249 |   ): Promise<MemoryEntry[]> {
250 |     const intentTypeMap = {
251 |       analyze: ["analysis", "evaluation", "assessment"],
252 |       recommend: ["recommendation", "suggestion", "advice"],
253 |       deploy: ["deployment", "publish", "release"],
254 |       troubleshoot: ["error", "issue", "problem", "debug"],
255 |       learn: ["tutorial", "guide", "example", "pattern"],
256 |     };
257 | 
258 |     const searchTerms = intentTypeMap[intent.action] || [intent.action];
259 |     const searches = searchTerms.map((term) =>
260 |       this.memoryManager.search(term, { sortBy: "timestamp" }),
261 |     );
262 | 
263 |     const results = await Promise.all(searches);
264 |     const allMatches = results.flat();
265 | 
266 |     // Filter by experience level
267 |     return allMatches.filter((memory) => {
268 |       if (intent.experience === "novice") {
269 |         return (
270 |           !memory.metadata.tags?.includes("advanced") &&
271 |           !memory.metadata.tags?.includes("expert")
272 |         );
273 |       } else if (intent.experience === "expert") {
274 |         return (
275 |           memory.metadata.tags?.includes("advanced") ||
276 |           memory.metadata.tags?.includes("expert") ||
277 |           memory.data.complexity === "complex"
278 |         );
279 |       }
280 |       return true; // intermediate gets all
281 |     });
282 |   }
283 | 
284 |   /**
285 |    * Get candidates based on temporal context
286 |    */
287 |   private async getTemporalCandidates(
288 |     temporal: NonNullable<RetrievalContext["temporalContext"]>,
289 |   ): Promise<MemoryEntry[]> {
290 |     const searchOptions: any = { sortBy: "timestamp" };
291 | 
292 |     if (temporal.timeRange) {
293 |       // Use memory manager's built-in time filtering
294 |       const allMemories = await this.memoryManager.search("", searchOptions);
295 | 
296 |       return allMemories.filter((memory) => {
297 |         const memoryTime = new Date(memory.timestamp);
298 |         const start = new Date(temporal.timeRange!.start);
299 |         const end = new Date(temporal.timeRange!.end);
300 |         return memoryTime >= start && memoryTime <= end;
301 |       });
302 |     }
303 | 
304 |     if (temporal.recency === "recent") {
305 |       const cutoff = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); // Last 7 days
306 |       const allMemories = await this.memoryManager.search("", searchOptions);
307 | 
308 |       return allMemories.filter(
309 |         (memory) => new Date(memory.timestamp) > cutoff,
310 |       );
311 |     }
312 | 
313 |     if (temporal.recency === "historical") {
314 |       const cutoff = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000); // Older than 90 days
315 |       const allMemories = await this.memoryManager.search("", searchOptions);
316 | 
317 |       return allMemories.filter(
318 |         (memory) => new Date(memory.timestamp) < cutoff,
319 |       );
320 |     }
321 | 
322 |     return this.memoryManager.search("", searchOptions);
323 |   }
324 | 
325 |   /**
326 |    * Get candidates using knowledge graph traversal
327 |    */
328 |   private async getGraphBasedCandidates(
329 |     query: string,
330 |     context: RetrievalContext,
331 |   ): Promise<MemoryEntry[]> {
332 |     if (!context.currentProject) return [];
333 | 
334 |     // Find relevant nodes in the knowledge graph
335 |     const graphQuery = {
336 |       nodeTypes: ["project", "technology"],
337 |       properties: context.currentProject.language
338 |         ? {
339 |             language: context.currentProject.language,
340 |           }
341 |         : undefined,
342 |       maxDepth: 2,
343 |     };
344 | 
345 |     const graphResult = this.knowledgeGraph.query(graphQuery);
346 |     const relevantNodeIds = graphResult.nodes.map((node) => node.id);
347 | 
348 |     // Find memories associated with these nodes
349 |     const memories: MemoryEntry[] = [];
350 |     const allMemories = await this.memoryManager.search("", {
351 |       sortBy: "timestamp",
352 |     });
353 | 
354 |     for (const memory of allMemories) {
355 |       const projectNodeId = `project:${memory.metadata.projectId}`;
356 |       const techNodeId = memory.metadata.ssg
357 |         ? `tech:${memory.metadata.ssg}`
358 |         : null;
359 | 
360 |       if (
361 |         relevantNodeIds.includes(projectNodeId) ||
362 |         (techNodeId && relevantNodeIds.includes(techNodeId))
363 |       ) {
364 |         memories.push(memory);
365 |       }
366 |     }
367 | 
368 |     return memories;
369 |   }
370 | 
371 |   /**
372 |    * Score and rank candidates based on contextual relevance
373 |    */
374 |   private async scoreAndRankCandidates(
375 |     candidates: MemoryEntry[],
376 |     query: string,
377 |     context: RetrievalContext,
378 |   ): Promise<ContextualMatch[]> {
379 |     const matches: ContextualMatch[] = [];
380 | 
381 |     for (const memory of candidates) {
382 |       const contextualFactors = await this.calculateContextualFactors(
383 |         memory,
384 |         query,
385 |         context,
386 |       );
387 | 
388 |       const relevanceScore = this.calculateOverallRelevance(contextualFactors);
389 |       const reasoning = this.generateReasoning(
390 |         memory,
391 |         contextualFactors,
392 |         context,
393 |       );
394 |       const confidence = this.calculateConfidence(contextualFactors, memory);
395 | 
396 |       matches.push({
397 |         memory,
398 |         relevanceScore,
399 |         contextualFactors,
400 |         reasoning,
401 |         confidence,
402 |       });
403 |     }
404 | 
405 |     // Sort by relevance score (descending)
406 |     return matches.sort((a, b) => b.relevanceScore - a.relevanceScore);
407 |   }
408 | 
409 |   /**
410 |    * Calculate contextual factors for scoring
411 |    */
412 |   private async calculateContextualFactors(
413 |     memory: MemoryEntry,
414 |     query: string,
415 |     context: RetrievalContext,
416 |   ): Promise<ContextualMatch["contextualFactors"]> {
417 |     const semantic = await this.calculateSemanticSimilarity(memory, query);
418 |     const temporal = await this.calculateTemporalRelevance(memory, context);
419 |     const structural = this.calculateStructuralRelevance(memory, context);
420 |     const intentional = this.calculateIntentionalRelevance(memory, context);
421 | 
422 |     return { semantic, temporal, structural, intentional };
423 |   }
424 | 
425 |   /**
426 |    * Calculate semantic similarity using simple text matching
427 |    * (In a production system, this would use embeddings)
428 |    */
429 |   private async calculateSemanticSimilarity(
430 |     memory: MemoryEntry,
431 |     query: string,
432 |   ): Promise<number> {
433 |     const queryTerms = query.toLowerCase().split(/\s+/);
434 |     const memoryText = JSON.stringify(memory.data).toLowerCase();
435 |     const metadataText = JSON.stringify(memory.metadata).toLowerCase();
436 | 
437 |     let matches = 0;
438 |     for (const term of queryTerms) {
439 |       if (memoryText.includes(term) || metadataText.includes(term)) {
440 |         matches++;
441 |       }
442 |     }
443 | 
444 |     return queryTerms.length > 0 ? matches / queryTerms.length : 0;
445 |   }
446 | 
447 |   /**
448 |    * Calculate temporal relevance based on recency and context
449 |    */
450 |   private calculateTemporalRelevance(
451 |     memory: MemoryEntry,
452 |     context: RetrievalContext,
453 |   ): Promise<number> {
454 |     const memoryDate = new Date(memory.timestamp);
455 |     const now = new Date();
456 |     const daysSince =
457 |       (now.getTime() - memoryDate.getTime()) / (1000 * 60 * 60 * 24);
458 | 
459 |     // Base score decreases with age
460 |     let score = Math.exp(-daysSince / 30); // Half-life of 30 days
461 | 
462 |     // Boost for explicit temporal preferences
463 |     if (context.temporalContext?.recency === "recent" && daysSince <= 7) {
464 |       score *= 1.5;
465 |     } else if (
466 |       context.temporalContext?.recency === "historical" &&
467 |       daysSince >= 90
468 |     ) {
469 |       score *= 1.3;
470 |     }
471 | 
472 |     // Consider time constraints
473 |     if (context.sessionContext?.timeConstraints) {
474 |       const urgencyMultiplier =
475 |         context.userIntent?.urgency === "high" ? 1.2 : 1.0;
476 |       score *= urgencyMultiplier;
477 |     }
478 | 
479 |     return Promise.resolve(Math.min(score, 1.0));
480 |   }
481 | 
482 |   /**
483 |    * Calculate structural relevance based on project similarity
484 |    */
485 |   private calculateStructuralRelevance(
486 |     memory: MemoryEntry,
487 |     context: RetrievalContext,
488 |   ): number {
489 |     if (!context.currentProject) return 0.5; // Neutral when no project context
490 | 
491 |     let score = 0;
492 |     let factors = 0;
493 | 
494 |     // Language match
495 |     if (memory.data.language?.primary === context.currentProject.language) {
496 |       score += 0.4;
497 |     }
498 |     factors++;
499 | 
500 |     // Framework match
501 |     if (
502 |       context.currentProject.framework &&
503 |       memory.data.framework?.name === context.currentProject.framework
504 |     ) {
505 |       score += 0.3;
506 |     }
507 |     factors++;
508 | 
509 |     // Size similarity
510 |     if (context.currentProject.size) {
511 |       const memorySize = this.categorizeProjectSize(
512 |         memory.data.stats?.files || 0,
513 |       );
514 |       if (memorySize === context.currentProject.size) {
515 |         score += 0.2;
516 |       }
517 |     }
518 |     factors++;
519 | 
520 |     // Type relevance
521 |     if (
522 |       memory.type === "analysis" &&
523 |       context.userIntent?.action === "analyze"
524 |     ) {
525 |       score += 0.1;
526 |     } else if (
527 |       memory.type === "recommendation" &&
528 |       context.userIntent?.action === "recommend"
529 |     ) {
530 |       score += 0.1;
531 |     }
532 |     factors++;
533 | 
534 |     return factors > 0 ? score / factors : 0;
535 |   }
536 | 
537 |   /**
538 |    * Calculate intentional relevance based on user intent
539 |    */
540 |   private calculateIntentionalRelevance(
541 |     memory: MemoryEntry,
542 |     context: RetrievalContext,
543 |   ): number {
544 |     if (!context.userIntent) return 0.5; // Neutral when no intent
545 | 
546 |     let score = 0;
547 | 
548 |     // Action alignment
549 |     const actionTypeMap = {
550 |       analyze: ["analysis", "evaluation"],
551 |       recommend: ["recommendation"],
552 |       deploy: ["deployment"],
553 |       troubleshoot: ["deployment", "configuration"],
554 |       learn: ["analysis", "recommendation"],
555 |     };
556 | 
557 |     const relevantTypes = actionTypeMap[context.userIntent.action] || [];
558 |     if (relevantTypes.includes(memory.type)) {
559 |       score += 0.5;
560 |     }
561 | 
562 |     // Experience level alignment
563 |     if (context.userIntent.experience === "novice") {
564 |       // Prefer simpler, more successful cases
565 |       if (
566 |         memory.data.status === "success" ||
567 |         memory.data.complexity !== "complex"
568 |       ) {
569 |         score += 0.3;
570 |       }
571 |     } else if (context.userIntent.experience === "expert") {
572 |       // Prefer complex or edge cases
573 |       if (
574 |         memory.data.complexity === "complex" ||
575 |         memory.metadata.tags?.includes("advanced")
576 |       ) {
577 |         score += 0.3;
578 |       }
579 |     }
580 | 
581 |     // Urgency consideration
582 |     if (context.userIntent.urgency === "high") {
583 |       // Prefer recent, successful cases
584 |       const daysSince =
585 |         (Date.now() - new Date(memory.timestamp).getTime()) /
586 |         (1000 * 60 * 60 * 24);
587 |       if (daysSince <= 7 && memory.data.status === "success") {
588 |         score += 0.2;
589 |       }
590 |     }
591 | 
592 |     return Math.min(score, 1.0);
593 |   }
594 | 
595 |   /**
596 |    * Calculate overall relevance score
597 |    */
598 |   private calculateOverallRelevance(
599 |     factors: ContextualMatch["contextualFactors"],
600 |   ): number {
601 |     // Weighted combination of factors
602 |     const weights = {
603 |       semantic: 0.3,
604 |       temporal: 0.2,
605 |       structural: 0.3,
606 |       intentional: 0.2,
607 |     };
608 | 
609 |     return (
610 |       factors.semantic * weights.semantic +
611 |       factors.temporal * weights.temporal +
612 |       factors.structural * weights.structural +
613 |       factors.intentional * weights.intentional
614 |     );
615 |   }
616 | 
617 |   /**
618 |    * Generate reasoning for why a memory was selected
619 |    */
620 |   private generateReasoning(
621 |     memory: MemoryEntry,
622 |     factors: ContextualMatch["contextualFactors"],
623 |     context: RetrievalContext,
624 |   ): string[] {
625 |     const reasoning: string[] = [];
626 | 
627 |     if (factors.semantic > 0.7) {
628 |       reasoning.push("High semantic similarity to query");
629 |     }
630 | 
631 |     if (factors.temporal > 0.8) {
632 |       reasoning.push("Recently relevant information");
633 |     }
634 | 
635 |     if (factors.structural > 0.6) {
636 |       reasoning.push(
637 |         `Similar project structure (${
638 |           memory.data.language?.primary || "unknown"
639 |         })`,
640 |       );
641 |     }
642 | 
643 |     if (factors.intentional > 0.7) {
644 |       reasoning.push(
645 |         `Matches user intent for ${
646 |           context.userIntent?.action || "general"
647 |         } action`,
648 |       );
649 |     }
650 | 
651 |     if (
652 |       memory.data.status === "success" &&
653 |       context.userIntent?.urgency === "high"
654 |     ) {
655 |       reasoning.push("Proven successful approach for urgent needs");
656 |     }
657 | 
658 |     if (memory.metadata.ssg && context.currentProject?.framework) {
659 |       reasoning.push(
660 |         `Experience with ${memory.metadata.ssg} for similar projects`,
661 |       );
662 |     }
663 | 
664 |     return reasoning.length > 0 ? reasoning : ["General relevance to query"];
665 |   }
666 | 
667 |   /**
668 |    * Calculate confidence in the match
669 |    */
670 |   private calculateConfidence(
671 |     factors: ContextualMatch["contextualFactors"],
672 |     memory: MemoryEntry,
673 |   ): number {
674 |     let confidence = (factors.semantic + factors.structural) / 2;
675 | 
676 |     // Boost confidence for successful outcomes
677 |     if (memory.data.status === "success") {
678 |       confidence *= 1.2;
679 |     }
680 | 
681 |     // Boost confidence for recent data
682 |     const daysSince =
683 |       (Date.now() - new Date(memory.timestamp).getTime()) /
684 |       (1000 * 60 * 60 * 24);
685 |     if (daysSince <= 30) {
686 |       confidence *= 1.1;
687 |     }
688 | 
689 |     // Boost confidence for rich metadata
690 |     if (memory.metadata.tags && memory.metadata.tags.length > 2) {
691 |       confidence *= 1.05;
692 |     }
693 | 
694 |     return Math.min(confidence, 1.0);
695 |   }
696 | 
697 |   /**
698 |    * Generate insights from retrieved matches
699 |    */
700 |   private async generateInsights(
701 |     matches: ContextualMatch[],
702 |     context: RetrievalContext,
703 |   ): Promise<RetrievalResult["insights"]> {
704 |     const patterns: string[] = [];
705 |     const recommendations: string[] = [];
706 |     const gaps: string[] = [];
707 | 
708 |     if (matches.length === 0) {
709 |       gaps.push("No relevant memories found for current context");
710 |       recommendations.push(
711 |         "Consider expanding search criteria or building more experience",
712 |       );
713 |       return { patterns, recommendations, gaps };
714 |     }
715 | 
716 |     // Analyze patterns in successful matches
717 |     const successfulMatches = matches.filter(
718 |       (m) => m.memory.data.status === "success" && m.relevanceScore > 0.6,
719 |     );
720 | 
721 |     if (successfulMatches.length >= 2) {
722 |       // Find common SSGs
723 |       const ssgs = new Map<string, number>();
724 |       successfulMatches.forEach((match) => {
725 |         if (match.memory.metadata.ssg) {
726 |           ssgs.set(
727 |             match.memory.metadata.ssg,
728 |             (ssgs.get(match.memory.metadata.ssg) || 0) + 1,
729 |           );
730 |         }
731 |       });
732 | 
733 |       if (ssgs.size > 0) {
734 |         const topSSG = Array.from(ssgs.entries()).sort(
735 |           ([, a], [, b]) => b - a,
736 |         )[0];
737 |         patterns.push(
738 |           `${topSSG[0]} appears in ${topSSG[1]} successful similar projects`,
739 |         );
740 |         recommendations.push(
741 |           `Consider ${topSSG[0]} based on successful precedents`,
742 |         );
743 |       }
744 | 
745 |       // Find common success factors
746 |       const commonFactors = this.findCommonSuccessFactors(successfulMatches);
747 |       patterns.push(...commonFactors);
748 |     }
749 | 
750 |     // Identify gaps
751 |     if (
752 |       context.userIntent?.action === "deploy" &&
753 |       matches.filter((m) => m.memory.type === "deployment").length === 0
754 |     ) {
755 |       gaps.push("Limited deployment experience for similar projects");
756 |       recommendations.push(
757 |         "Proceed cautiously with deployment and document the process",
758 |       );
759 |     }
760 | 
761 |     if (
762 |       context.userIntent?.experience === "novice" &&
763 |       matches.every((m) => m.confidence < 0.7)
764 |     ) {
765 |       gaps.push("Limited beginner-friendly resources for this context");
766 |       recommendations.push(
767 |         "Consider consulting documentation or seeking expert guidance",
768 |       );
769 |     }
770 | 
771 |     return { patterns, recommendations, gaps };
772 |   }
773 | 
774 |   /**
775 |    * Find common success factors across matches
776 |    */
777 |   private findCommonSuccessFactors(matches: ContextualMatch[]): string[] {
778 |     const factors: string[] = [];
779 | 
780 |     const hasTests = matches.filter(
781 |       (m) => m.memory.data.testing?.hasTests,
782 |     ).length;
783 |     if (hasTests / matches.length > 0.7) {
784 |       factors.push("Projects with testing have higher success rates");
785 |     }
786 | 
787 |     const hasCI = matches.filter((m) => m.memory.data.ci?.hasCI).length;
788 |     if (hasCI / matches.length > 0.6) {
789 |       factors.push("CI/CD adoption correlates with deployment success");
790 |     }
791 | 
792 |     const simpleProjects = matches.filter(
793 |       (m) => m.memory.data.complexity !== "complex",
794 |     ).length;
795 |     if (simpleProjects / matches.length > 0.8) {
796 |       factors.push("Simpler project structures show more reliable outcomes");
797 |     }
798 | 
799 |     return factors;
800 |   }
801 | 
802 |   /**
803 |    * Categorize project size for comparison
804 |    */
805 |   private categorizeProjectSize(
806 |     fileCount: number,
807 |   ): "small" | "medium" | "large" {
808 |     if (fileCount < 50) return "small";
809 |     if (fileCount < 200) return "medium";
810 |     return "large";
811 |   }
812 | 
813 |   /**
814 |    * Get contextual suggestions for improving retrieval
815 |    */
816 |   async getSuggestions(context: RetrievalContext): Promise<{
817 |     queryImprovements: string[];
818 |     contextEnhancements: string[];
819 |     learningOpportunities: string[];
820 |   }> {
821 |     const suggestions = {
822 |       queryImprovements: [] as string[],
823 |       contextEnhancements: [] as string[],
824 |       learningOpportunities: [] as string[],
825 |     };
826 | 
827 |     // Analyze current context completeness
828 |     if (!context.currentProject) {
829 |       suggestions.contextEnhancements.push(
830 |         "Provide current project information for better matches",
831 |       );
832 |     }
833 | 
834 |     if (!context.userIntent) {
835 |       suggestions.contextEnhancements.push(
836 |         "Specify your intent (analyze, recommend, deploy, etc.) for targeted results",
837 |       );
838 |     }
839 | 
840 |     if (!context.temporalContext) {
841 |       suggestions.contextEnhancements.push(
842 |         "Set temporal preferences (recent vs. historical) for relevance",
843 |       );
844 |     }
845 | 
846 |     // Analyze retrieval patterns
847 |     const recentSearches = await this.memoryManager.search("search", {
848 |       sortBy: "timestamp",
849 |     });
850 |     if (recentSearches.length < 5) {
851 |       suggestions.learningOpportunities.push(
852 |         "System will improve with more usage and data",
853 |       );
854 |     }
855 | 
856 |     // Check for data gaps
857 |     if (context.currentProject?.language) {
858 |       const languageMemories = await this.memoryManager.search(
859 |         context.currentProject.language,
860 |       );
861 |       if (languageMemories.length < 3) {
862 |         suggestions.learningOpportunities.push(
863 |           `More experience needed with ${context.currentProject.language} projects`,
864 |         );
865 |       }
866 |     }
867 | 
868 |     return suggestions;
869 |   }
870 | 
871 |   /**
872 |    * Clear embedding cache
873 |    */
874 |   clearCache(): void {
875 |     this.embeddingCache.clear();
876 |   }
877 | 
878 |   /**
879 |    * Get retrieval statistics
880 |    */
881 |   getStatistics(): {
882 |     cacheSize: number;
883 |     cacheHitRate: number;
884 |     averageRetrievalTime: number;
885 |     commonContextTypes: Record<string, number>;
886 |   } {
887 |     // This would track actual usage statistics in a real implementation
888 |     return {
889 |       cacheSize: this.embeddingCache.size,
890 |       cacheHitRate: 0.85, // Placeholder
891 |       averageRetrievalTime: 150, // ms
892 |       commonContextTypes: {
893 |         project_analysis: 45,
894 |         ssg_recommendation: 38,
895 |         deployment_troubleshooting: 12,
896 |         learning_assistance: 5,
897 |       },
898 |     };
899 |   }
900 | }
901 | 
902 | export default ContextualMemoryRetrieval;
903 | 
```

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

```html
  1 | <!DOCTYPE html><html class="default" lang="en" data-base="./"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>DocuMCP API Documentation - v0.4.1</title><meta name="description" content="Documentation for DocuMCP API Documentation"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="assets/style.css"/><link rel="stylesheet" href="assets/highlight.css"/><script defer src="assets/main.js"></script><script async src="assets/icons.js" id="tsd-icons-script"></script><script async src="assets/search.js" id="tsd-search-script"></script><script async src="assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><a href="index.html" class="title">DocuMCP API Documentation - v0.4.1</a><div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/><ul role="listbox" id="tsd-search-results"></ul><div id="tsd-search-status" aria-live="polite" aria-atomic="true"><div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-menu"></use></svg></a></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><h1>DocuMCP API Documentation - v0.4.1</h1></div><div class="tsd-panel tsd-typography"><h1 id="documcp---intelligent-documentation-deployment-mcp-server" class="tsd-anchor-link">DocuMCP - Intelligent Documentation Deployment MCP Server<a href="#documcp---intelligent-documentation-deployment-mcp-server" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h1><p><a href="https://github.com/tosin2013/documcp/actions/workflows/ci.yml"><img src="https://github.com/tosin2013/documcp/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  2 | <a href="https://github.com/tosin2013/documcp/actions/workflows/codeql.yml"><img src="https://github.com/tosin2013/documcp/actions/workflows/codeql.yml/badge.svg" alt="CodeQL"></a>
  3 | <a href="https://codecov.io/gh/tosin2013/documcp"><img src="https://codecov.io/gh/tosin2013/documcp/branch/main/graph/badge.svg" alt="Coverage"></a>
  4 | <a href="https://badge.fury.io/js/documcp"><img src="https://badge.fury.io/js/documcp.svg" alt="npm version"></a></p>
  5 | <p>DocuMCP is an intelligent Model Context Protocol (MCP) server that revolutionizes documentation deployment for open-source projects. It provides deep repository analysis, intelligent static site generator recommendations, and automated GitHub Pages deployment workflows.</p>
  6 | <h2 id="tldr" class="tsd-anchor-link">TL;DR<a href="#tldr" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>DocuMCP analyzes your repository, recommends the perfect static site generator (Jekyll, Hugo, Docusaurus, MkDocs, or Eleventy), creates professional documentation structure following Diataxis principles, and deploys it automatically to GitHub Pages. Just say &quot;analyze my repository and deploy documentation&quot; to get started.</p>
  7 | <h2 id="features" class="tsd-anchor-link">Features<a href="#features" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><h3 id="core-capabilities" class="tsd-anchor-link">Core Capabilities<a href="#core-capabilities" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ul>
  8 | <li>🔍 <strong>Repository Analysis</strong>: Deep multi-layered analysis of project structure, dependencies, and documentation needs</li>
  9 | <li>🎯 <strong>SSG Recommendations</strong>: Data-driven recommendations for Jekyll, Hugo, Docusaurus, MkDocs, or Eleventy</li>
 10 | <li>📚 <strong>Diataxis Framework</strong>: Automatic creation of well-structured documentation following proven principles</li>
 11 | <li>🚀 <strong>GitHub Pages Deployment</strong>: Automated workflow generation with SSG-specific optimizations</li>
 12 | <li>✅ <strong>Deployment Verification</strong>: Comprehensive checks and troubleshooting for successful deployments</li>
 13 | </ul>
 14 | <h3 id="intelligence--learning-phase-2" class="tsd-anchor-link">Intelligence &amp; Learning (Phase 2)<a href="#intelligence--learning-phase-2" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ul>
 15 | <li>🧠 <strong>Historical Intelligence</strong>: Learns from past deployments to improve recommendations</li>
 16 | <li>👤 <strong>User Preferences</strong>: Personalized recommendations based on your preferences and patterns</li>
 17 | <li>📊 <strong>Deployment Analytics</strong>: Comprehensive insights into deployment patterns and success rates</li>
 18 | <li>🎯 <strong>Smart Scoring</strong>: Intelligent SSG scoring based on success rates from similar projects</li>
 19 | <li>📈 <strong>Trend Analysis</strong>: Identifies deployment trends and provides health scores</li>
 20 | </ul>
 21 | <h2 id="requirements" class="tsd-anchor-link">Requirements<a href="#requirements" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><ul>
 22 | <li><strong>Node.js</strong>: 20.0.0 or higher</li>
 23 | <li><strong>npm</strong>: Latest stable version</li>
 24 | </ul>
 25 | <h2 id="installation" class="tsd-anchor-link">Installation<a href="#installation" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><pre><code class="bash"><span class="hl-0"># Clone the repository</span><br/><span class="hl-1">git</span><span class="hl-2"> </span><span class="hl-3">clone</span><span class="hl-2"> </span><span class="hl-3">https://github.com/tosin2013/documcp.git</span><br/><span class="hl-1">cd</span><span class="hl-2"> </span><span class="hl-3">documcp</span><br/><br/><span class="hl-0"># Install dependencies</span><br/><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">install</span><br/><br/><span class="hl-0"># Build the project</span><br/><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">run</span><span class="hl-2"> </span><span class="hl-3">build</span>
 26 | </code><button type="button">Copy</button></pre>
 27 | 
 28 | <h2 id="mcp-client-setup" class="tsd-anchor-link">MCP Client Setup<a href="#mcp-client-setup" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>DocuMCP works with various MCP-enabled clients. Here's how to configure it:</p>
 29 | <h3 id="claude-desktop" class="tsd-anchor-link">Claude Desktop<a href="#claude-desktop" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ol>
 30 | <li>
 31 | <p><strong>Locate Claude Desktop's configuration file</strong>:</p>
 32 | <ul>
 33 | <li><strong>macOS</strong>: <code>~/Library/Application Support/Claude/claude_desktop_config.json</code></li>
 34 | <li><strong>Windows</strong>: <code>%APPDATA%\Claude\claude_desktop_config.json</code></li>
 35 | <li><strong>Linux</strong>: <code>~/.config/claude/claude_desktop_config.json</code></li>
 36 | </ul>
 37 | </li>
 38 | <li>
 39 | <p><strong>Add documcp server configuration</strong>:</p>
 40 | <pre><code class="json"><span class="hl-2">{</span><br/><span class="hl-2">  </span><span class="hl-4">&quot;mcpServers&quot;</span><span class="hl-2">: {</span><br/><span class="hl-2">    </span><span class="hl-4">&quot;documcp&quot;</span><span class="hl-2">: {</span><br/><span class="hl-2">      </span><span class="hl-4">&quot;command&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;npx&quot;</span><span class="hl-2">,</span><br/><span class="hl-2">      </span><span class="hl-4">&quot;args&quot;</span><span class="hl-2">: [</span><span class="hl-3">&quot;documcp&quot;</span><span class="hl-2">]</span><br/><span class="hl-2">    }</span><br/><span class="hl-2">  }</span><br/><span class="hl-2">}</span>
 41 | </code><button type="button">Copy</button></pre>
 42 | 
 43 | </li>
 44 | <li>
 45 | <p><strong>Restart Claude Desktop</strong> to load the configuration.</p>
 46 | </li>
 47 | </ol>
 48 | <h3 id="vs-code-with-github-copilot" class="tsd-anchor-link">VS Code with GitHub Copilot<a href="#vs-code-with-github-copilot" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ol>
 49 | <li><strong>Install MCP extension</strong> for VS Code</li>
 50 | <li><strong>Configure in VS Code settings.json</strong>:<pre><code class="json"><span class="hl-2">{</span><br/><span class="hl-2">  </span><span class="hl-4">&quot;mcp.servers&quot;</span><span class="hl-2">: {</span><br/><span class="hl-2">    </span><span class="hl-4">&quot;documcp&quot;</span><span class="hl-2">: {</span><br/><span class="hl-2">      </span><span class="hl-4">&quot;command&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;npx&quot;</span><span class="hl-2">,</span><br/><span class="hl-2">      </span><span class="hl-4">&quot;args&quot;</span><span class="hl-2">: [</span><span class="hl-3">&quot;documcp&quot;</span><span class="hl-2">]</span><br/><span class="hl-2">    }</span><br/><span class="hl-2">  }</span><br/><span class="hl-2">}</span>
 51 | </code><button type="button">Copy</button></pre>
 52 | 
 53 | </li>
 54 | </ol>
 55 | <h3 id="cursor-editor" class="tsd-anchor-link">Cursor Editor<a href="#cursor-editor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ol>
 56 | <li><strong>Configure in Cursor settings</strong>:<pre><code class="json"><span class="hl-2">{</span><br/><span class="hl-2">  </span><span class="hl-4">&quot;mcpServers&quot;</span><span class="hl-2">: {</span><br/><span class="hl-2">    </span><span class="hl-4">&quot;documcp&quot;</span><span class="hl-2">: {</span><br/><span class="hl-2">      </span><span class="hl-4">&quot;command&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;npx&quot;</span><span class="hl-2">,</span><br/><span class="hl-2">      </span><span class="hl-4">&quot;args&quot;</span><span class="hl-2">: [</span><span class="hl-3">&quot;documcp&quot;</span><span class="hl-2">]</span><br/><span class="hl-2">    }</span><br/><span class="hl-2">  }</span><br/><span class="hl-2">}</span>
 57 | </code><button type="button">Copy</button></pre>
 58 | 
 59 | </li>
 60 | </ol>
 61 | <h3 id="gemini-code-assist" class="tsd-anchor-link">Gemini Code Assist<a href="#gemini-code-assist" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ol>
 62 | <li><strong>Check Gemini documentation</strong> for MCP server configuration</li>
 63 | <li><strong>Add similar configuration</strong> as above</li>
 64 | </ol>
 65 | <h3 id="troubleshooting" class="tsd-anchor-link">Troubleshooting<a href="#troubleshooting" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ul>
 66 | <li>Ensure <code>npx</code> is available in your PATH</li>
 67 | <li>For global installations, use the full path:<pre><code class="json"><span class="hl-2">{</span><br/><span class="hl-2">  </span><span class="hl-4">&quot;command&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;node&quot;</span><span class="hl-2">,</span><br/><span class="hl-2">  </span><span class="hl-4">&quot;args&quot;</span><span class="hl-2">: [</span><span class="hl-3">&quot;/usr/local/lib/node_modules/documcp/dist/index.js&quot;</span><span class="hl-2">]</span><br/><span class="hl-2">}</span>
 68 | </code><button type="button">Copy</button></pre>
 69 | 
 70 | </li>
 71 | <li>Find installation path: <code>npm list -g documcp</code></li>
 72 | </ul>
 73 | <h2 id="quick-start" class="tsd-anchor-link">Quick Start<a href="#quick-start" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Once configured with your MCP client, just prompt DocuMCP with natural language:</p>
 74 | <pre><code class="bash"><span class="hl-0"># Complete workflow</span><br/><span class="hl-1">&quot;analyze my repository and deploy documentation to GitHub Pages&quot;</span><br/><br/><span class="hl-0"># Step by step</span><br/><span class="hl-1">&quot;analyze my repository for documentation needs&quot;</span><br/><span class="hl-1">&quot;recommend the best static site generator for my project&quot;</span><br/><span class="hl-1">&quot;set up documentation structure and deploy to GitHub Pages&quot;</span>
 75 | </code><button type="button">Copy</button></pre>
 76 | 
 77 | <p>DocuMCP provides 30+ tools including repository analysis, intelligent SSG recommendations, content generation, deployment automation with tracking, validation, user preference management, deployment analytics, and memory-enhanced insights. See the <a href="media/index.md">complete documentation</a> for detailed tool reference.</p>
 78 | <h2 id="key-tools" class="tsd-anchor-link">Key Tools<a href="#key-tools" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><h3 id="analysis--recommendations" class="tsd-anchor-link">Analysis &amp; Recommendations<a href="#analysis--recommendations" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ul>
 79 | <li><code>analyze_repository</code> - Deep repository structure and dependency analysis</li>
 80 | <li><code>recommend_ssg</code> - Intelligent SSG recommendations with historical data and user preferences</li>
 81 | <li><code>detect_gaps</code> - Identify missing documentation sections</li>
 82 | </ul>
 83 | <h3 id="deployment--tracking" class="tsd-anchor-link">Deployment &amp; Tracking<a href="#deployment--tracking" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ul>
 84 | <li><code>deploy_pages</code> - Automated GitHub Pages deployment with outcome tracking</li>
 85 | <li><code>verify_deployment</code> - Comprehensive deployment validation</li>
 86 | <li><code>analyze_deployments</code> - Analytics and insights from deployment history</li>
 87 | </ul>
 88 | <h3 id="user-preferences--learning" class="tsd-anchor-link">User Preferences &amp; Learning<a href="#user-preferences--learning" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><ul>
 89 | <li><code>manage_preferences</code> - Manage user preferences for personalized recommendations</li>
 90 | <li>View historical success rates and deployment patterns</li>
 91 | <li>Get recommendations based on similar projects' success</li>
 92 | </ul>
 93 | <h2 id="development" class="tsd-anchor-link">Development<a href="#development" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><pre><code class="bash"><span class="hl-0"># Run in development mode</span><br/><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">run</span><span class="hl-2"> </span><span class="hl-3">dev</span><br/><br/><span class="hl-0"># Run tests</span><br/><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">test</span><br/><br/><span class="hl-0"># Lint code</span><br/><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">run</span><span class="hl-2"> </span><span class="hl-3">lint</span><br/><br/><span class="hl-0"># Type check</span><br/><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">run</span><span class="hl-2"> </span><span class="hl-3">typecheck</span>
 94 | </code><button type="button">Copy</button></pre>
 95 | 
 96 | <h2 id="architecture" class="tsd-anchor-link">Architecture<a href="#architecture" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>DocuMCP follows a modular, stateless architecture:</p>
 97 | <ul>
 98 | <li><strong>TypeScript-based</strong> implementation using the official MCP SDK</li>
 99 | <li><strong>Stateless operation</strong> for consistency and reliability</li>
100 | <li><strong>Modular design</strong> with clear separation of concerns</li>
101 | <li><strong>Progressive complexity</strong> allowing users to start simple</li>
102 | </ul>
103 | <h2 id="documentation-structure-diataxis" class="tsd-anchor-link">Documentation Structure (Diataxis)<a href="#documentation-structure-diataxis" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>DocuMCP automatically creates documentation following the Diataxis framework:</p>
104 | <ul>
105 | <li><strong>Tutorials</strong>: Learning-oriented guides for newcomers</li>
106 | <li><strong>How-To Guides</strong>: Task-oriented recipes for specific goals</li>
107 | <li><strong>Reference</strong>: Information-oriented technical descriptions</li>
108 | <li><strong>Explanation</strong>: Understanding-oriented conceptual discussions</li>
109 | </ul>
110 | <h2 id="contributing" class="tsd-anchor-link">Contributing<a href="#contributing" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>We welcome contributions! Please see our <a href="media/CONTRIBUTING.md">Contributing Guide</a> for details.</p>
111 | <h3 id="first-time-contributors" class="tsd-anchor-link">First Time Contributors<a href="#first-time-contributors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Look for issues labeled &quot;good first issue&quot; to get started with the project. We welcome contributions from developers of all experience levels.</p>
112 | <h3 id="reporting-issues" class="tsd-anchor-link">Reporting Issues<a href="#reporting-issues" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Please use our <a href="media/ISSUE_TEMPLATE">issue templates</a> when reporting bugs or requesting features.</p>
113 | <h2 id="code-of-conduct" class="tsd-anchor-link">Code of Conduct<a href="#code-of-conduct" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>This project adheres to the <a href="media/CODE_OF_CONDUCT.md">Contributor Covenant Code of Conduct</a>. By participating, you are expected to uphold this code.</p>
114 | <h2 id="security" class="tsd-anchor-link">Security<a href="#security" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Please see our <a href="media/SECURITY.md">Security Policy</a> for reporting vulnerabilities and security-related issues.</p>
115 | <h2 id="license" class="tsd-anchor-link">License<a href="#license" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>MIT License - see <a href="media/LICENSE">LICENSE</a> for details.</p>
116 | <h2 id="acknowledgments" class="tsd-anchor-link">Acknowledgments<a href="#acknowledgments" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><ul>
117 | <li>Built on the <a href="https://modelcontextprotocol.io/">Model Context Protocol</a></li>
118 | <li>Follows the <a href="https://diataxis.fr/">Diataxis Framework</a></li>
119 | <li>Inspired by the need for better documentation in open-source projects</li>
120 | </ul>
121 | </div></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-chevronDown"></use></svg><h3>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div><details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="assets/icons.svg#icon-chevronDown"></use></svg><h3>On This Page</h3></summary><div class="tsd-accordion-details"><a href="#documcp---intelligent-documentation-deployment-mcp-server"><span>Docu<wbr/>MCP -<wbr/> <wbr/>Intelligent <wbr/>Documentation <wbr/>Deployment <wbr/>MCP <wbr/>Server</span></a><ul><li><a href="#tldr"><span>TL;<wbr/>DR</span></a></li><li><a href="#features"><span>Features</span></a></li><li><ul><li><a href="#core-capabilities"><span>Core <wbr/>Capabilities</span></a></li><li><a href="#intelligence--learning-phase-2"><span>Intelligence &amp; <wbr/>Learning (<wbr/>Phase 2)</span></a></li></ul></li><li><a href="#requirements"><span>Requirements</span></a></li><li><a href="#installation"><span>Installation</span></a></li><li><a href="#mcp-client-setup"><span>MCP <wbr/>Client <wbr/>Setup</span></a></li><li><ul><li><a href="#claude-desktop"><span>Claude <wbr/>Desktop</span></a></li><li><a href="#vs-code-with-github-copilot"><span>VS <wbr/>Code with <wbr/>Git<wbr/>Hub <wbr/>Copilot</span></a></li><li><a href="#cursor-editor"><span>Cursor <wbr/>Editor</span></a></li><li><a href="#gemini-code-assist"><span>Gemini <wbr/>Code <wbr/>Assist</span></a></li><li><a href="#troubleshooting"><span>Troubleshooting</span></a></li></ul></li><li><a href="#quick-start"><span>Quick <wbr/>Start</span></a></li><li><a href="#key-tools"><span>Key <wbr/>Tools</span></a></li><li><ul><li><a href="#analysis--recommendations"><span>Analysis &amp; <wbr/>Recommendations</span></a></li><li><a href="#deployment--tracking"><span>Deployment &amp; <wbr/>Tracking</span></a></li><li><a href="#user-preferences--learning"><span>User <wbr/>Preferences &amp; <wbr/>Learning</span></a></li></ul></li><li><a href="#development"><span>Development</span></a></li><li><a href="#architecture"><span>Architecture</span></a></li><li><a href="#documentation-structure-diataxis"><span>Documentation <wbr/>Structure (<wbr/>Diataxis)</span></a></li><li><a href="#contributing"><span>Contributing</span></a></li><li><ul><li><a href="#first-time-contributors"><span>First <wbr/>Time <wbr/>Contributors</span></a></li><li><a href="#reporting-issues"><span>Reporting <wbr/>Issues</span></a></li></ul></li><li><a href="#code-of-conduct"><span>Code of <wbr/>Conduct</span></a></li><li><a href="#security"><span>Security</span></a></li><li><a href="#license"><span>License</span></a></li><li><a href="#acknowledgments"><span>Acknowledgments</span></a></li></ul></div></details></div><div class="site-menu"><nav class="tsd-navigation"><a href="modules.html">DocuMCP API Documentation - v0.4.1</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer><p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer><div class="overlay"></div></body></html>
122 | 
```

--------------------------------------------------------------------------------
/src/memory/schemas.ts:
--------------------------------------------------------------------------------

```typescript
  1 | /**
  2 |  * Knowledge Graph Schema Definitions
  3 |  * Implements Phase 1.1: Enhanced Knowledge Graph Schema Implementation
  4 |  *
  5 |  * Defines comprehensive Zod schemas for all entity types and relationships
  6 |  * in the DocuMCP knowledge graph.
  7 |  */
  8 | 
  9 | import { z } from "zod";
 10 | 
 11 | // ============================================================================
 12 | // Entity Schemas
 13 | // ============================================================================
 14 | 
 15 | /**
 16 |  * Project Entity Schema
 17 |  * Represents a software project analyzed by DocuMCP
 18 |  */
 19 | export const ProjectEntitySchema = z.object({
 20 |   name: z.string().min(1, "Project name is required"),
 21 |   path: z.string().min(1, "Project path is required"),
 22 |   technologies: z.array(z.string()).default([]),
 23 |   size: z.enum(["small", "medium", "large"]).default("medium"),
 24 |   domain: z.string().optional(),
 25 |   lastAnalyzed: z.string().datetime(),
 26 |   analysisCount: z.number().int().min(0).default(0),
 27 |   primaryLanguage: z.string().optional(),
 28 |   hasTests: z.boolean().default(false),
 29 |   hasCI: z.boolean().default(false),
 30 |   hasDocs: z.boolean().default(false),
 31 |   totalFiles: z.number().int().min(0).default(0),
 32 |   linesOfCode: z.number().int().min(0).optional(),
 33 | });
 34 | 
 35 | export type ProjectEntity = z.infer<typeof ProjectEntitySchema>;
 36 | 
 37 | /**
 38 |  * User Entity Schema
 39 |  * Represents a DocuMCP user with their preferences and behavior patterns
 40 |  */
 41 | export const UserEntitySchema = z.object({
 42 |   userId: z.string().min(1, "User ID is required"),
 43 |   expertiseLevel: z
 44 |     .enum(["beginner", "intermediate", "advanced"])
 45 |     .default("intermediate"),
 46 |   preferredTechnologies: z.array(z.string()).default([]),
 47 |   preferredSSGs: z.array(z.string()).default([]),
 48 |   documentationStyle: z
 49 |     .enum(["minimal", "comprehensive", "tutorial-heavy"])
 50 |     .default("comprehensive"),
 51 |   preferredDiataxisCategories: z
 52 |     .array(z.enum(["tutorials", "how-to", "reference", "explanation"]))
 53 |     .default([]),
 54 |   projectCount: z.number().int().min(0).default(0),
 55 |   lastActive: z.string().datetime(),
 56 |   createdAt: z.string().datetime(),
 57 | });
 58 | 
 59 | export type UserEntity = z.infer<typeof UserEntitySchema>;
 60 | 
 61 | /**
 62 |  * Configuration Entity Schema
 63 |  * Represents a deployment configuration with success metrics
 64 |  */
 65 | export const ConfigurationEntitySchema = z.object({
 66 |   ssg: z.enum(["jekyll", "hugo", "docusaurus", "mkdocs", "eleventy"]),
 67 |   settings: z.record(z.string(), z.any()).default({}),
 68 |   deploymentSuccessRate: z.number().min(0).max(1).default(1.0),
 69 |   usageCount: z.number().int().min(0).default(0),
 70 |   lastUsed: z.string().datetime(),
 71 |   buildTimeAverage: z.number().min(0).optional(), // in seconds
 72 |   failureReasons: z.array(z.string()).default([]),
 73 |   compatibleTechnologies: z.array(z.string()).default([]),
 74 | });
 75 | 
 76 | export type ConfigurationEntity = z.infer<typeof ConfigurationEntitySchema>;
 77 | 
 78 | /**
 79 |  * Documentation Entity Schema
 80 |  * Represents a documentation structure or pattern
 81 |  */
 82 | export const DocumentationEntitySchema = z.object({
 83 |   type: z.enum(["structure", "pattern", "template"]),
 84 |   framework: z.enum(["diataxis", "custom", "mixed"]).default("diataxis"),
 85 |   categories: z.array(z.string()).default([]),
 86 |   effectivenessScore: z.number().min(0).max(1).optional(),
 87 |   usageCount: z.number().int().min(0).default(0),
 88 |   lastUsed: z.string().datetime(),
 89 |   contentPatterns: z.record(z.string(), z.any()).default({}),
 90 |   suitableFor: z
 91 |     .array(z.enum(["library", "application", "tool", "framework"]))
 92 |     .default([]),
 93 | });
 94 | 
 95 | export type DocumentationEntity = z.infer<typeof DocumentationEntitySchema>;
 96 | 
 97 | /**
 98 |  * CodeFile Entity Schema
 99 |  * Represents a source code file with its structure and metadata
100 |  */
101 | export const CodeFileEntitySchema = z.object({
102 |   path: z.string().min(1, "File path is required"),
103 |   language: z.string().min(1, "Language is required"),
104 |   functions: z.array(z.string()).default([]),
105 |   classes: z.array(z.string()).default([]),
106 |   dependencies: z.array(z.string()).default([]),
107 |   imports: z.array(z.string()).default([]),
108 |   exports: z.array(z.string()).default([]),
109 |   lastModified: z.string().datetime(),
110 |   linesOfCode: z.number().int().min(0).default(0),
111 |   contentHash: z.string().min(1, "Content hash is required"),
112 |   complexity: z.enum(["low", "medium", "high"]).optional(),
113 | });
114 | 
115 | export type CodeFileEntity = z.infer<typeof CodeFileEntitySchema>;
116 | 
117 | /**
118 |  * DocumentationSection Entity Schema
119 |  * Represents a specific section of documentation
120 |  */
121 | export const DocumentationSectionEntitySchema = z.object({
122 |   filePath: z.string().min(1, "File path is required"),
123 |   sectionTitle: z.string().min(1, "Section title is required"),
124 |   contentHash: z.string().min(1, "Content hash is required"),
125 |   referencedCodeFiles: z.array(z.string()).default([]),
126 |   referencedFunctions: z.array(z.string()).default([]),
127 |   referencedClasses: z.array(z.string()).default([]),
128 |   lastUpdated: z.string().datetime(),
129 |   category: z
130 |     .enum(["tutorial", "how-to", "reference", "explanation"])
131 |     .optional(),
132 |   effectivenessScore: z.number().min(0).max(1).optional(),
133 |   wordCount: z.number().int().min(0).default(0),
134 |   hasCodeExamples: z.boolean().default(false),
135 | });
136 | 
137 | export type DocumentationSectionEntity = z.infer<
138 |   typeof DocumentationSectionEntitySchema
139 | >;
140 | 
141 | /**
142 |  * Technology Entity Schema
143 |  * Represents a technology, framework, or language
144 |  */
145 | export const TechnologyEntitySchema = z.object({
146 |   name: z.string().min(1, "Technology name is required"),
147 |   category: z.enum(["language", "framework", "library", "tool", "platform"]),
148 |   version: z.string().optional(),
149 |   ecosystem: z
150 |     .enum(["javascript", "python", "ruby", "go", "rust", "java", "other"])
151 |     .optional(),
152 |   popularityScore: z.number().min(0).max(1).optional(),
153 |   usageCount: z.number().int().min(0).default(0),
154 | });
155 | 
156 | export type TechnologyEntity = z.infer<typeof TechnologyEntitySchema>;
157 | 
158 | /**
159 |  * LinkValidation Entity Schema
160 |  * Represents link validation results for documentation
161 |  */
162 | export const LinkValidationEntitySchema = z.object({
163 |   totalLinks: z.number().int().min(0).default(0),
164 |   validLinks: z.number().int().min(0).default(0),
165 |   brokenLinks: z.number().int().min(0).default(0),
166 |   warningLinks: z.number().int().min(0).default(0),
167 |   unknownLinks: z.number().int().min(0).default(0),
168 |   healthScore: z.number().min(0).max(100).default(100),
169 |   lastValidated: z.string().datetime(),
170 |   brokenLinksList: z.array(z.string()).default([]),
171 | });
172 | 
173 | export type LinkValidationEntity = z.infer<typeof LinkValidationEntitySchema>;
174 | 
175 | /**
176 |  * Sitemap Entity Schema
177 |  * Represents a sitemap.xml file with generation and update tracking
178 |  */
179 | export const SitemapEntitySchema = z.object({
180 |   baseUrl: z.string().url("Valid base URL required"),
181 |   docsPath: z.string().min(1, "Documentation path is required"),
182 |   totalUrls: z.number().int().min(0).default(0),
183 |   lastGenerated: z.string().datetime(),
184 |   lastUpdated: z.string().datetime().optional(),
185 |   urlsByCategory: z.record(z.string(), z.number()).default({}),
186 |   urlsByPriority: z
187 |     .object({
188 |       high: z.number().int().min(0).default(0), // priority >= 0.9
189 |       medium: z.number().int().min(0).default(0), // priority 0.5-0.9
190 |       low: z.number().int().min(0).default(0), // priority < 0.5
191 |     })
192 |     .default({ high: 0, medium: 0, low: 0 }),
193 |   updateFrequency: z
194 |     .enum(["always", "hourly", "daily", "weekly", "monthly", "yearly", "never"])
195 |     .default("monthly"),
196 |   validationStatus: z
197 |     .enum(["valid", "invalid", "not_validated"])
198 |     .default("not_validated"),
199 |   validationErrors: z.array(z.string()).default([]),
200 |   sitemapPath: z.string().min(1),
201 |   ssg: z
202 |     .enum(["jekyll", "hugo", "docusaurus", "mkdocs", "eleventy"])
203 |     .optional(),
204 |   submittedToSearchEngines: z.boolean().default(false),
205 |   searchEngines: z.array(z.string()).default([]),
206 | });
207 | 
208 | export type SitemapEntity = z.infer<typeof SitemapEntitySchema>;
209 | 
210 | /**
211 |  * Documentation Freshness Event Entity Schema
212 |  * Represents a documentation freshness tracking event with staleness metrics
213 |  */
214 | export const DocumentationFreshnessEventEntitySchema = z.object({
215 |   docsPath: z.string().min(1, "Documentation path is required"),
216 |   projectPath: z.string().min(1, "Project path is required"),
217 |   scannedAt: z.string().datetime(),
218 |   totalFiles: z.number().int().min(0).default(0),
219 |   freshFiles: z.number().int().min(0).default(0),
220 |   warningFiles: z.number().int().min(0).default(0),
221 |   staleFiles: z.number().int().min(0).default(0),
222 |   criticalFiles: z.number().int().min(0).default(0),
223 |   filesWithoutMetadata: z.number().int().min(0).default(0),
224 |   thresholds: z
225 |     .object({
226 |       warning: z.object({
227 |         value: z.number().positive(),
228 |         unit: z.enum(["minutes", "hours", "days"]),
229 |       }),
230 |       stale: z.object({
231 |         value: z.number().positive(),
232 |         unit: z.enum(["minutes", "hours", "days"]),
233 |       }),
234 |       critical: z.object({
235 |         value: z.number().positive(),
236 |         unit: z.enum(["minutes", "hours", "days"]),
237 |       }),
238 |     })
239 |     .optional(),
240 |   averageAge: z.number().min(0).optional(), // Average age in days
241 |   oldestFile: z
242 |     .object({
243 |       path: z.string(),
244 |       ageInDays: z.number().min(0),
245 |     })
246 |     .optional(),
247 |   mostStaleFiles: z.array(z.string()).default([]),
248 |   validatedAgainstCommit: z.string().optional(),
249 |   eventType: z
250 |     .enum(["scan", "validation", "initialization", "update"])
251 |     .default("scan"),
252 | });
253 | 
254 | export type DocumentationFreshnessEventEntity = z.infer<
255 |   typeof DocumentationFreshnessEventEntitySchema
256 | >;
257 | 
258 | /**
259 |  * Documentation Example Entity Schema
260 |  * Represents a code example in documentation with tracking and validation
261 |  */
262 | export const DocumentationExampleEntitySchema = z.object({
263 |   sourceFile: z.string().min(1, "Source file path is required"),
264 |   language: z.string().min(1, "Programming language is required"),
265 |   code: z.string().min(1, "Code content is required"),
266 |   diataxisType: z.enum(["tutorial", "how-to", "reference", "explanation"]),
267 |   referencedSymbols: z.array(z.string()).default([]),
268 |   lastValidated: z.string().datetime().optional(),
269 |   validationStatus: z.enum(["valid", "invalid", "unknown"]).default("unknown"),
270 |   exampleId: z.string().min(1, "Example ID is required"),
271 |   contentHash: z.string().optional(),
272 |   lineStart: z.number().int().min(1).optional(),
273 |   lineEnd: z.number().int().min(1).optional(),
274 | });
275 | 
276 | export type DocumentationExampleEntity = z.infer<
277 |   typeof DocumentationExampleEntitySchema
278 | >;
279 | 
280 | /**
281 |  * Example Validation Entity Schema
282 |  * Represents validation results for a documentation example
283 |  */
284 | export const ExampleValidationEntitySchema = z.object({
285 |   exampleId: z.string().min(1, "Example ID is required"),
286 |   validatedAt: z.string().datetime(),
287 |   result: z.enum(["pass", "fail", "warning"]),
288 |   issues: z.array(z.string()).default([]),
289 |   confidenceScore: z.number().min(0).max(1),
290 |   validationMethod: z.enum(["ast", "llm", "execution"]),
291 |   errorDetails: z.record(z.string(), z.unknown()).optional(),
292 |   suggestions: z.array(z.string()).default([]),
293 | });
294 | 
295 | export type ExampleValidationEntity = z.infer<
296 |   typeof ExampleValidationEntitySchema
297 | >;
298 | 
299 | /**
300 |  * Call Graph Entity Schema
301 |  * Represents a call graph for code execution analysis
302 |  */
303 | export const CallGraphNodeSchema = z.object({
304 |   functionName: z.string().min(1),
305 |   filePath: z.string().min(1),
306 |   lineNumber: z.number().int().min(1).optional(),
307 |   callCount: z.number().int().min(0).default(1),
308 | });
309 | 
310 | export const CallGraphEdgeSchema = z.object({
311 |   from: z.string().min(1),
312 |   to: z.string().min(1),
313 |   callType: z
314 |     .enum(["direct", "indirect", "async", "callback"])
315 |     .default("direct"),
316 |   weight: z.number().min(0).default(1.0),
317 | });
318 | 
319 | export const CallGraphEntitySchema = z.object({
320 |   rootFunction: z.string().min(1, "Root function is required"),
321 |   nodes: z.array(CallGraphNodeSchema).default([]),
322 |   edges: z.array(CallGraphEdgeSchema).default([]),
323 |   depth: z.number().int().min(0),
324 |   generatedAt: z.string().datetime(),
325 |   analysisMethod: z.enum(["static", "dynamic", "hybrid"]).default("static"),
326 |   totalFunctions: z.number().int().min(0).default(0),
327 |   entryPoint: z.string().optional(),
328 | });
329 | 
330 | export type CallGraphNode = z.infer<typeof CallGraphNodeSchema>;
331 | export type CallGraphEdge = z.infer<typeof CallGraphEdgeSchema>;
332 | export type CallGraphEntity = z.infer<typeof CallGraphEntitySchema>;
333 | 
334 | // ============================================================================
335 | // Relationship Schemas
336 | // ============================================================================
337 | 
338 | /**
339 |  * Base Relationship Schema
340 |  * Common fields for all relationship types
341 |  */
342 | export const BaseRelationshipSchema = z.object({
343 |   weight: z.number().min(0).max(1).default(1.0),
344 |   confidence: z.number().min(0).max(1).default(1.0),
345 |   createdAt: z.string().datetime(),
346 |   lastUpdated: z.string().datetime(),
347 |   metadata: z.record(z.string(), z.any()).default({}),
348 | });
349 | 
350 | /**
351 |  * Project Uses Technology Relationship
352 |  */
353 | export const ProjectUsesTechnologySchema = BaseRelationshipSchema.extend({
354 |   type: z.literal("project_uses_technology"),
355 |   fileCount: z.number().int().min(0).default(0),
356 |   percentage: z.number().min(0).max(100).optional(),
357 |   isPrimary: z.boolean().default(false),
358 | });
359 | 
360 | export type ProjectUsesTechnologyRelationship = z.infer<
361 |   typeof ProjectUsesTechnologySchema
362 | >;
363 | 
364 | /**
365 |  * User Prefers SSG Relationship
366 |  */
367 | export const UserPrefersSSGSchema = BaseRelationshipSchema.extend({
368 |   type: z.literal("user_prefers_ssg"),
369 |   usageCount: z.number().int().min(0).default(0),
370 |   lastUsed: z.string().datetime(),
371 |   successRate: z.number().min(0).max(1).optional(),
372 | });
373 | 
374 | export type UserPrefersSSGRelationship = z.infer<typeof UserPrefersSSGSchema>;
375 | 
376 | /**
377 |  * Project Deployed With Configuration Relationship
378 |  */
379 | export const ProjectDeployedWithSchema = BaseRelationshipSchema.extend({
380 |   type: z.literal("project_deployed_with"),
381 |   success: z.boolean(),
382 |   timestamp: z.string().datetime(),
383 |   buildTime: z.number().min(0).optional(), // in seconds
384 |   errorMessage: z.string().optional(),
385 |   deploymentUrl: z.string().url().optional(),
386 | });
387 | 
388 | export type ProjectDeployedWithRelationship = z.infer<
389 |   typeof ProjectDeployedWithSchema
390 | >;
391 | 
392 | /**
393 |  * Similar To Relationship
394 |  */
395 | export const SimilarToSchema = BaseRelationshipSchema.extend({
396 |   type: z.literal("similar_to"),
397 |   similarityScore: z.number().min(0).max(1),
398 |   sharedTechnologies: z.array(z.string()).default([]),
399 |   sharedPatterns: z.array(z.string()).default([]),
400 |   reason: z.string().optional(),
401 | });
402 | 
403 | export type SimilarToRelationship = z.infer<typeof SimilarToSchema>;
404 | 
405 | /**
406 |  * Documents Relationship (CodeFile -> DocumentationSection)
407 |  */
408 | export const DocumentsSchema = BaseRelationshipSchema.extend({
409 |   type: z.literal("documents"),
410 |   coverage: z.enum(["partial", "complete", "comprehensive"]).default("partial"),
411 |   lastVerified: z.string().datetime(),
412 |   quality: z.enum(["low", "medium", "high"]).optional(),
413 | });
414 | 
415 | export type DocumentsRelationship = z.infer<typeof DocumentsSchema>;
416 | 
417 | /**
418 |  * References Relationship (DocumentationSection -> CodeFile)
419 |  */
420 | export const ReferencesSchema = BaseRelationshipSchema.extend({
421 |   type: z.literal("references"),
422 |   referenceType: z.enum([
423 |     "example",
424 |     "api-reference",
425 |     "tutorial",
426 |     "explanation",
427 |   ]),
428 |   isAccurate: z.boolean().optional(),
429 |   lastVerified: z.string().datetime().optional(),
430 | });
431 | 
432 | export type ReferencesRelationship = z.infer<typeof ReferencesSchema>;
433 | 
434 | /**
435 |  * Outdated For Relationship
436 |  */
437 | export const OutdatedForSchema = BaseRelationshipSchema.extend({
438 |   type: z.literal("outdated_for"),
439 |   detectedAt: z.string().datetime(),
440 |   changeType: z.enum([
441 |     "function_signature",
442 |     "class_structure",
443 |     "dependency",
444 |     "behavior",
445 |     "removed",
446 |   ]),
447 |   severity: z.enum(["low", "medium", "high", "critical"]).default("medium"),
448 |   autoFixable: z.boolean().default(false),
449 | });
450 | 
451 | export type OutdatedForRelationship = z.infer<typeof OutdatedForSchema>;
452 | 
453 | /**
454 |  * Depends On Relationship
455 |  */
456 | export const DependsOnSchema = BaseRelationshipSchema.extend({
457 |   type: z.literal("depends_on"),
458 |   dependencyType: z.enum(["import", "inheritance", "composition", "usage"]),
459 |   isRequired: z.boolean().default(true),
460 |   version: z.string().optional(),
461 | });
462 | 
463 | export type DependsOnRelationship = z.infer<typeof DependsOnSchema>;
464 | 
465 | /**
466 |  * Recommends Relationship
467 |  */
468 | export const RecommendsSchema = BaseRelationshipSchema.extend({
469 |   type: z.literal("recommends"),
470 |   reason: z.string(),
471 |   basedOn: z.array(z.string()).default([]), // IDs of supporting evidence
472 |   contextFactors: z.array(z.string()).default([]),
473 | });
474 | 
475 | export type RecommendsRelationship = z.infer<typeof RecommendsSchema>;
476 | 
477 | /**
478 |  * Results In Relationship
479 |  */
480 | export const ResultsInSchema = BaseRelationshipSchema.extend({
481 |   type: z.literal("results_in"),
482 |   outcomeType: z.enum(["success", "failure", "partial"]),
483 |   metrics: z.record(z.string(), z.number()).default({}),
484 |   notes: z.string().optional(),
485 | });
486 | 
487 | export type ResultsInRelationship = z.infer<typeof ResultsInSchema>;
488 | 
489 | /**
490 |  * Created By Relationship
491 |  */
492 | export const CreatedBySchema = BaseRelationshipSchema.extend({
493 |   type: z.literal("created_by"),
494 |   role: z.enum(["author", "contributor", "maintainer"]).default("author"),
495 |   timestamp: z.string().datetime(),
496 | });
497 | 
498 | export type CreatedByRelationship = z.infer<typeof CreatedBySchema>;
499 | 
500 | /**
501 |  * Project Has Sitemap Relationship
502 |  * Links a project to its sitemap with generation metrics
503 |  */
504 | export const ProjectHasSitemapSchema = BaseRelationshipSchema.extend({
505 |   type: z.literal("project_has_sitemap"),
506 |   generationCount: z.number().int().min(0).default(0),
507 |   lastAction: z.enum(["generate", "update", "validate"]).default("generate"),
508 |   urlsAdded: z.number().int().min(0).default(0),
509 |   urlsRemoved: z.number().int().min(0).default(0),
510 |   urlsUpdated: z.number().int().min(0).default(0),
511 |   successRate: z.number().min(0).max(1).default(1.0),
512 | });
513 | 
514 | export type ProjectHasSitemapRelationship = z.infer<
515 |   typeof ProjectHasSitemapSchema
516 | >;
517 | 
518 | /**
519 |  * Project Has Freshness Event Relationship
520 |  * Links a project to a documentation freshness tracking event
521 |  */
522 | export const ProjectHasFreshnessEventSchema = BaseRelationshipSchema.extend({
523 |   type: z.literal("project_has_freshness_event"),
524 |   eventType: z
525 |     .enum(["scan", "validation", "initialization", "update"])
526 |     .default("scan"),
527 |   filesScanned: z.number().int().min(0).default(0),
528 |   freshFiles: z.number().int().min(0).default(0),
529 |   staleFiles: z.number().int().min(0).default(0),
530 |   criticalFiles: z.number().int().min(0).default(0),
531 |   filesInitialized: z.number().int().min(0).default(0),
532 |   filesUpdated: z.number().int().min(0).default(0),
533 |   averageStaleness: z.number().min(0).optional(), // in days
534 |   improvementScore: z.number().min(0).max(1).optional(), // 0-1, higher is better
535 | });
536 | 
537 | export type ProjectHasFreshnessEventRelationship = z.infer<
538 |   typeof ProjectHasFreshnessEventSchema
539 | >;
540 | 
541 | /**
542 |  * Has Example Relationship (Document -> Documentation Example)
543 |  * Links a documentation file/section to a code example it contains
544 |  */
545 | export const HasExampleSchema = BaseRelationshipSchema.extend({
546 |   type: z.literal("has_example"),
547 |   exampleCount: z.number().int().min(0).default(1),
548 |   primaryLanguage: z.string().optional(),
549 |   exampleType: z
550 |     .enum(["inline", "reference", "embedded", "external"])
551 |     .default("inline"),
552 | });
553 | 
554 | export type HasExampleRelationship = z.infer<typeof HasExampleSchema>;
555 | 
556 | /**
557 |  * Validates Relationship (Example Validation -> Documentation Example)
558 |  * Links a validation result to the example it validates
559 |  */
560 | export const ValidatesSchema = BaseRelationshipSchema.extend({
561 |   type: z.literal("validates"),
562 |   validationRun: z.string().datetime(),
563 |   previousResult: z.enum(["pass", "fail", "warning", "none"]).optional(),
564 |   resultChanged: z.boolean().default(false),
565 | });
566 | 
567 | export type ValidatesRelationship = z.infer<typeof ValidatesSchema>;
568 | 
569 | /**
570 |  * Has Call Graph Relationship (Documentation Example -> Call Graph)
571 |  * Links an example to its execution call graph
572 |  */
573 | export const HasCallGraphSchema = BaseRelationshipSchema.extend({
574 |   type: z.literal("has_call_graph"),
575 |   graphDepth: z.number().int().min(0),
576 |   totalNodes: z.number().int().min(0),
577 |   totalEdges: z.number().int().min(0),
578 |   complexity: z.enum(["low", "medium", "high"]).optional(),
579 | });
580 | 
581 | export type HasCallGraphRelationship = z.infer<typeof HasCallGraphSchema>;
582 | 
583 | // ============================================================================
584 | // Union Types and Type Guards
585 | // ============================================================================
586 | 
587 | /**
588 |  * All Entity Types Union
589 |  */
590 | const ProjectEntityWithType = ProjectEntitySchema.extend({
591 |   type: z.literal("project"),
592 | });
593 | const UserEntityWithType = UserEntitySchema.extend({ type: z.literal("user") });
594 | const ConfigurationEntityWithType = ConfigurationEntitySchema.extend({
595 |   type: z.literal("configuration"),
596 | });
597 | const DocumentationEntityWithType = DocumentationEntitySchema.extend({
598 |   type: z.literal("documentation"),
599 | });
600 | const CodeFileEntityWithType = CodeFileEntitySchema.extend({
601 |   type: z.literal("code_file"),
602 | });
603 | const DocumentationSectionEntityWithType =
604 |   DocumentationSectionEntitySchema.extend({
605 |     type: z.literal("documentation_section"),
606 |   });
607 | const TechnologyEntityWithType = TechnologyEntitySchema.extend({
608 |   type: z.literal("technology"),
609 | });
610 | const LinkValidationEntityWithType = LinkValidationEntitySchema.extend({
611 |   type: z.literal("link_validation"),
612 | });
613 | const SitemapEntityWithType = SitemapEntitySchema.extend({
614 |   type: z.literal("sitemap"),
615 | });
616 | const DocumentationFreshnessEventEntityWithType =
617 |   DocumentationFreshnessEventEntitySchema.extend({
618 |     type: z.literal("documentation_freshness_event"),
619 |   });
620 | const DocumentationExampleEntityWithType =
621 |   DocumentationExampleEntitySchema.extend({
622 |     type: z.literal("documentation_example"),
623 |   });
624 | const ExampleValidationEntityWithType = ExampleValidationEntitySchema.extend({
625 |   type: z.literal("example_validation"),
626 | });
627 | const CallGraphEntityWithType = CallGraphEntitySchema.extend({
628 |   type: z.literal("call_graph"),
629 | });
630 | 
631 | export const EntitySchema = z.union([
632 |   ProjectEntityWithType,
633 |   UserEntityWithType,
634 |   ConfigurationEntityWithType,
635 |   DocumentationEntityWithType,
636 |   CodeFileEntityWithType,
637 |   DocumentationSectionEntityWithType,
638 |   TechnologyEntityWithType,
639 |   LinkValidationEntityWithType,
640 |   SitemapEntityWithType,
641 |   DocumentationFreshnessEventEntityWithType,
642 |   DocumentationExampleEntityWithType,
643 |   ExampleValidationEntityWithType,
644 |   CallGraphEntityWithType,
645 | ]);
646 | 
647 | export type Entity = z.infer<typeof EntitySchema>;
648 | 
649 | /**
650 |  * All Relationship Types Union
651 |  */
652 | export const RelationshipSchema = z.union([
653 |   ProjectUsesTechnologySchema,
654 |   UserPrefersSSGSchema,
655 |   ProjectDeployedWithSchema,
656 |   SimilarToSchema,
657 |   DocumentsSchema,
658 |   ReferencesSchema,
659 |   OutdatedForSchema,
660 |   DependsOnSchema,
661 |   RecommendsSchema,
662 |   ResultsInSchema,
663 |   CreatedBySchema,
664 |   ProjectHasSitemapSchema,
665 |   ProjectHasFreshnessEventSchema,
666 |   HasExampleSchema,
667 |   ValidatesSchema,
668 |   HasCallGraphSchema,
669 | ]);
670 | 
671 | export type Relationship =
672 |   | ProjectUsesTechnologyRelationship
673 |   | UserPrefersSSGRelationship
674 |   | ProjectDeployedWithRelationship
675 |   | SimilarToRelationship
676 |   | DocumentsRelationship
677 |   | ReferencesRelationship
678 |   | OutdatedForRelationship
679 |   | DependsOnRelationship
680 |   | RecommendsRelationship
681 |   | ResultsInRelationship
682 |   | CreatedByRelationship
683 |   | ProjectHasSitemapRelationship
684 |   | ProjectHasFreshnessEventRelationship
685 |   | HasExampleRelationship
686 |   | ValidatesRelationship
687 |   | HasCallGraphRelationship;
688 | 
689 | // ============================================================================
690 | // Validation Helpers
691 | // ============================================================================
692 | 
693 | /**
694 |  * Validate an entity against its schema
695 |  */
696 | export function validateEntity(entity: any): Entity {
697 |   return EntitySchema.parse(entity);
698 | }
699 | 
700 | /**
701 |  * Validate a relationship against its schema
702 |  */
703 | export function validateRelationship(relationship: any): Relationship {
704 |   return RelationshipSchema.parse(relationship);
705 | }
706 | 
707 | /**
708 |  * Type guard for specific entity types
709 |  */
710 | export function isProjectEntity(
711 |   entity: Entity,
712 | ): entity is ProjectEntity & { type: "project" } {
713 |   return entity.type === "project";
714 | }
715 | 
716 | export function isUserEntity(
717 |   entity: Entity,
718 | ): entity is UserEntity & { type: "user" } {
719 |   return entity.type === "user";
720 | }
721 | 
722 | export function isConfigurationEntity(
723 |   entity: Entity,
724 | ): entity is ConfigurationEntity & { type: "configuration" } {
725 |   return entity.type === "configuration";
726 | }
727 | 
728 | export function isCodeFileEntity(
729 |   entity: Entity,
730 | ): entity is CodeFileEntity & { type: "code_file" } {
731 |   return entity.type === "code_file";
732 | }
733 | 
734 | export function isDocumentationSectionEntity(
735 |   entity: Entity,
736 | ): entity is DocumentationSectionEntity & { type: "documentation_section" } {
737 |   return entity.type === "documentation_section";
738 | }
739 | 
740 | export function isDocumentationExampleEntity(
741 |   entity: Entity,
742 | ): entity is DocumentationExampleEntity & { type: "documentation_example" } {
743 |   return entity.type === "documentation_example";
744 | }
745 | 
746 | export function isExampleValidationEntity(
747 |   entity: Entity,
748 | ): entity is ExampleValidationEntity & { type: "example_validation" } {
749 |   return entity.type === "example_validation";
750 | }
751 | 
752 | export function isCallGraphEntity(
753 |   entity: Entity,
754 | ): entity is CallGraphEntity & { type: "call_graph" } {
755 |   return entity.type === "call_graph";
756 | }
757 | 
758 | // ============================================================================
759 | // Schema Metadata
760 | // ============================================================================
761 | 
762 | /**
763 |  * Schema version for migration support
764 |  */
765 | export const SCHEMA_VERSION = "1.1.0";
766 | 
767 | /**
768 |  * Schema metadata for documentation and validation
769 |  */
770 | export const SCHEMA_METADATA = {
771 |   version: SCHEMA_VERSION,
772 |   entityTypes: [
773 |     "project",
774 |     "user",
775 |     "configuration",
776 |     "documentation",
777 |     "code_file",
778 |     "documentation_section",
779 |     "technology",
780 |     "documentation_freshness_event",
781 |     "documentation_example",
782 |     "example_validation",
783 |     "call_graph",
784 |   ] as const,
785 |   relationshipTypes: [
786 |     "project_uses_technology",
787 |     "user_prefers_ssg",
788 |     "project_deployed_with",
789 |     "similar_to",
790 |     "documents",
791 |     "references",
792 |     "outdated_for",
793 |     "depends_on",
794 |     "recommends",
795 |     "results_in",
796 |     "created_by",
797 |     "project_has_sitemap",
798 |     "project_has_freshness_event",
799 |     "has_example",
800 |     "validates",
801 |     "has_call_graph",
802 |   ] as const,
803 |   lastUpdated: "2025-12-10",
804 | } as const;
805 | 
```

--------------------------------------------------------------------------------
/src/utils/execution-simulator.ts:
--------------------------------------------------------------------------------

```typescript
   1 | /**
   2 |  * Execution Simulator for LLM-based Code Tracing (Issue #73)
   3 |  *
   4 |  * Uses LLM to trace code execution paths without actually running the code.
   5 |  * Enables validation of documentation examples by simulating their behavior.
   6 |  *
   7 |  * Supports ADR-010: Executable Documentation Examples
   8 |  */
   9 | 
  10 | import { createLLMClient, LLMClient, isLLMAvailable } from "./llm-client.js";
  11 | import {
  12 |   ASTAnalyzer,
  13 |   FunctionSignature,
  14 |   ASTAnalysisResult,
  15 | } from "./ast-analyzer.js";
  16 | 
  17 | /**
  18 |  * Represents the state of a variable during execution
  19 |  */
  20 | export interface VariableState {
  21 |   name: string;
  22 |   type: string;
  23 |   value: unknown;
  24 |   definedAt: number;
  25 |   lastModifiedAt: number;
  26 |   isParameter: boolean;
  27 | }
  28 | 
  29 | /**
  30 |  * Single step in execution trace
  31 |  */
  32 | export interface ExecutionStep {
  33 |   lineNumber: number;
  34 |   operation: string;
  35 |   stateChanges: Record<string, unknown>;
  36 |   callsMade: string[];
  37 |   branchTaken?: "if" | "else" | "switch-case" | "loop-continue" | "loop-break";
  38 |   returnValue?: unknown;
  39 |   errorThrown?: string;
  40 |   confidence: number;
  41 | }
  42 | 
  43 | /**
  44 |  * Trace of simulated code execution
  45 |  */
  46 | export interface ExecutionTrace {
  47 |   exampleId: string;
  48 |   entryPoint: string;
  49 |   executionSteps: ExecutionStep[];
  50 |   variablesAccessed: Record<string, VariableState>;
  51 |   potentialIssues: PotentialIssue[];
  52 |   confidenceScore: number;
  53 |   executionPath: string[];
  54 |   reachedEnd: boolean;
  55 |   simulationDuration: number;
  56 | }
  57 | 
  58 | /**
  59 |  * Potential issue detected during execution simulation
  60 |  */
  61 | export interface PotentialIssue {
  62 |   severity: "error" | "warning" | "info";
  63 |   type:
  64 |     | "null-reference"
  65 |     | "type-mismatch"
  66 |     | "undefined-variable"
  67 |     | "unreachable-code"
  68 |     | "infinite-loop"
  69 |     | "missing-error-handling"
  70 |     | "deprecated-api"
  71 |     | "other";
  72 |   location: {
  73 |     line: number;
  74 |     column?: number;
  75 |     function?: string;
  76 |   };
  77 |   description: string;
  78 |   suggestion: string;
  79 |   codeSnippet?: string;
  80 | }
  81 | 
  82 | /**
  83 |  * Call graph node for execution path tracing
  84 |  */
  85 | export interface CallGraphNode {
  86 |   function: FunctionSignature;
  87 |   location: { file: string; line: number };
  88 |   calls: CallGraphNode[];
  89 |   conditionals: ConditionalPath[];
  90 |   raises: string[];
  91 |   depth: number;
  92 | }
  93 | 
  94 | /**
  95 |  * Conditional branch in execution path
  96 |  */
  97 | export interface ConditionalPath {
  98 |   condition: string;
  99 |   lineNumber: number;
 100 |   trueBranch: CallGraphNode[];
 101 |   falseBranch: CallGraphNode[];
 102 | }
 103 | 
 104 | /**
 105 |  * Call graph for an entry point
 106 |  */
 107 | export interface CallGraph {
 108 |   entryPoint: string;
 109 |   root: CallGraphNode;
 110 |   allFunctions: FunctionSignature[];
 111 |   maxDepthReached: number;
 112 | }
 113 | 
 114 | /**
 115 |  * Options for execution simulation
 116 |  */
 117 | export interface SimulationOptions {
 118 |   maxDepth?: number;
 119 |   maxSteps?: number;
 120 |   timeoutMs?: number;
 121 |   includeCallGraph?: boolean;
 122 |   detectNullRefs?: boolean;
 123 |   detectTypeMismatches?: boolean;
 124 |   detectUnreachableCode?: boolean;
 125 |   confidenceThreshold?: number;
 126 | }
 127 | 
 128 | /**
 129 |  * Result of validating a code example
 130 |  */
 131 | export interface ExampleValidationResult {
 132 |   exampleCode: string;
 133 |   trace: ExecutionTrace;
 134 |   isValid: boolean;
 135 |   issues: PotentialIssue[];
 136 |   matchesDocumentation: boolean;
 137 |   suggestions: string[];
 138 | }
 139 | 
 140 | /**
 141 |  * Execution Simulator using LLM for code tracing
 142 |  */
 143 | export class ExecutionSimulator {
 144 |   private llmClient: LLMClient | null;
 145 |   private astAnalyzer: ASTAnalyzer;
 146 |   private initialized: boolean = false;
 147 |   private options: SimulationOptions;
 148 | 
 149 |   constructor(options: SimulationOptions = {}) {
 150 |     this.options = {
 151 |       maxDepth: options.maxDepth || 10,
 152 |       maxSteps: options.maxSteps || 100,
 153 |       timeoutMs: options.timeoutMs || 30000,
 154 |       includeCallGraph: options.includeCallGraph !== false,
 155 |       detectNullRefs: options.detectNullRefs !== false,
 156 |       detectTypeMismatches: options.detectTypeMismatches !== false,
 157 |       detectUnreachableCode: options.detectUnreachableCode !== false,
 158 |       confidenceThreshold: options.confidenceThreshold || 0.7,
 159 |     };
 160 | 
 161 |     this.llmClient = isLLMAvailable() ? createLLMClient() : null;
 162 |     this.astAnalyzer = new ASTAnalyzer();
 163 |   }
 164 | 
 165 |   /**
 166 |    * Initialize the simulator
 167 |    */
 168 |   async initialize(): Promise<void> {
 169 |     if (this.initialized) return;
 170 |     await this.astAnalyzer.initialize();
 171 |     this.initialized = true;
 172 |   }
 173 | 
 174 |   /**
 175 |    * Check if LLM is available for simulation
 176 |    */
 177 |   isLLMAvailable(): boolean {
 178 |     return this.llmClient !== null;
 179 |   }
 180 | 
 181 |   /**
 182 |    * Simulate execution of a code example
 183 |    */
 184 |   async simulateExecution(
 185 |     exampleCode: string,
 186 |     implementationCode: string,
 187 |     entryPoint?: string,
 188 |   ): Promise<ExecutionTrace> {
 189 |     await this.initialize();
 190 | 
 191 |     const startTime = Date.now();
 192 |     const exampleId = this.generateExampleId(exampleCode);
 193 | 
 194 |     // If LLM is not available, fall back to static analysis
 195 |     if (!this.llmClient) {
 196 |       return this.staticAnalysisTrace(
 197 |         exampleCode,
 198 |         implementationCode,
 199 |         exampleId,
 200 |         startTime,
 201 |       );
 202 |     }
 203 | 
 204 |     try {
 205 |       // Use LLM to trace execution
 206 |       const trace = await this.llmTraceExecution(
 207 |         exampleCode,
 208 |         implementationCode,
 209 |         entryPoint || this.detectEntryPoint(exampleCode),
 210 |         exampleId,
 211 |         startTime,
 212 |       );
 213 | 
 214 |       return trace;
 215 |     } catch (error) {
 216 |       // Fallback to static analysis on LLM failure
 217 |       console.warn(
 218 |         "LLM execution trace failed, falling back to static analysis:",
 219 |         error,
 220 |       );
 221 |       return this.staticAnalysisTrace(
 222 |         exampleCode,
 223 |         implementationCode,
 224 |         exampleId,
 225 |         startTime,
 226 |       );
 227 |     }
 228 |   }
 229 | 
 230 |   /**
 231 |    * Validate a documentation example against implementation
 232 |    */
 233 |   async validateExample(
 234 |     exampleCode: string,
 235 |     implementationCode: string,
 236 |     expectedBehavior?: string,
 237 |   ): Promise<ExampleValidationResult> {
 238 |     await this.initialize();
 239 | 
 240 |     const trace = await this.simulateExecution(exampleCode, implementationCode);
 241 | 
 242 |     const issues = this.analyzeTraceForIssues(trace);
 243 |     const isValid = issues.filter((i) => i.severity === "error").length === 0;
 244 |     const matchesDocumentation = await this.checkBehaviorMatch(
 245 |       trace,
 246 |       expectedBehavior,
 247 |     );
 248 | 
 249 |     const suggestions: string[] = [];
 250 |     if (!isValid) {
 251 |       suggestions.push(
 252 |         "Fix the identified errors before publishing documentation",
 253 |       );
 254 |     }
 255 |     if (!matchesDocumentation && expectedBehavior) {
 256 |       suggestions.push(
 257 |         "Update documentation to match actual implementation behavior",
 258 |       );
 259 |     }
 260 |     if (trace.confidenceScore < this.options.confidenceThreshold!) {
 261 |       suggestions.push("Low confidence simulation - manual review recommended");
 262 |     }
 263 | 
 264 |     return {
 265 |       exampleCode,
 266 |       trace,
 267 |       isValid,
 268 |       issues,
 269 |       matchesDocumentation,
 270 |       suggestions,
 271 |     };
 272 |   }
 273 | 
 274 |   /**
 275 |    * Batch validate multiple examples
 276 |    */
 277 |   async validateExamples(
 278 |     examples: Array<{
 279 |       code: string;
 280 |       implementation: string;
 281 |       expectedBehavior?: string;
 282 |     }>,
 283 |   ): Promise<ExampleValidationResult[]> {
 284 |     const results: ExampleValidationResult[] = [];
 285 | 
 286 |     for (const example of examples) {
 287 |       const result = await this.validateExample(
 288 |         example.code,
 289 |         example.implementation,
 290 |         example.expectedBehavior,
 291 |       );
 292 |       results.push(result);
 293 |     }
 294 | 
 295 |     return results;
 296 |   }
 297 | 
 298 |   /**
 299 |    * Build a call graph for a function
 300 |    */
 301 |   async buildCallGraph(
 302 |     entryFunction: string,
 303 |     analysisResult: ASTAnalysisResult,
 304 |     maxDepth: number = 3,
 305 |   ): Promise<CallGraph> {
 306 |     const allFunctions = analysisResult.functions;
 307 |     const entryFunc = allFunctions.find((f) => f.name === entryFunction);
 308 | 
 309 |     if (!entryFunc) {
 310 |       return {
 311 |         entryPoint: entryFunction,
 312 |         root: this.createEmptyNode(entryFunction),
 313 |         allFunctions,
 314 |         maxDepthReached: 0,
 315 |       };
 316 |     }
 317 | 
 318 |     const visited = new Set<string>();
 319 |     const root = await this.buildCallGraphNode(
 320 |       entryFunc,
 321 |       analysisResult,
 322 |       0,
 323 |       maxDepth,
 324 |       visited,
 325 |     );
 326 | 
 327 |     return {
 328 |       entryPoint: entryFunction,
 329 |       root,
 330 |       allFunctions,
 331 |       maxDepthReached: Math.min(maxDepth, visited.size),
 332 |     };
 333 |   }
 334 | 
 335 |   /**
 336 |    * Use LLM to trace code execution
 337 |    */
 338 |   private async llmTraceExecution(
 339 |     exampleCode: string,
 340 |     implementationCode: string,
 341 |     entryPoint: string,
 342 |     exampleId: string,
 343 |     startTime: number,
 344 |   ): Promise<ExecutionTrace> {
 345 |     const prompt = this.buildTracePrompt(
 346 |       exampleCode,
 347 |       implementationCode,
 348 |       entryPoint,
 349 |     );
 350 | 
 351 |     const response = await this.llmClient!.complete(prompt);
 352 |     const parsed = this.parseTraceResponse(response);
 353 | 
 354 |     return {
 355 |       exampleId,
 356 |       entryPoint,
 357 |       executionSteps: parsed.steps,
 358 |       variablesAccessed: parsed.variables,
 359 |       potentialIssues: parsed.issues,
 360 |       confidenceScore: parsed.confidence,
 361 |       executionPath: parsed.path,
 362 |       reachedEnd: parsed.reachedEnd,
 363 |       simulationDuration: Date.now() - startTime,
 364 |     };
 365 |   }
 366 | 
 367 |   /**
 368 |    * Build LLM prompt for execution tracing
 369 |    */
 370 |   private buildTracePrompt(
 371 |     exampleCode: string,
 372 |     implementationCode: string,
 373 |     entryPoint: string,
 374 |   ): string {
 375 |     return `You are a code execution simulator. Trace the execution of the following code example without actually running it.
 376 | 
 377 | ## Example Code (to validate):
 378 | \`\`\`
 379 | ${exampleCode}
 380 | \`\`\`
 381 | 
 382 | ## Implementation Code:
 383 | \`\`\`
 384 | ${implementationCode}
 385 | \`\`\`
 386 | 
 387 | ## Entry Point: ${entryPoint}
 388 | 
 389 | Analyze the code flow step by step and respond in JSON format:
 390 | 
 391 | {
 392 |   "steps": [
 393 |     {
 394 |       "lineNumber": <number>,
 395 |       "operation": "<description of what happens>",
 396 |       "stateChanges": { "<variable>": <new_value> },
 397 |       "callsMade": ["<function_name>"],
 398 |       "branchTaken": "<if|else|switch-case|loop-continue|loop-break|null>",
 399 |       "returnValue": <value_if_returning>,
 400 |       "errorThrown": "<error_type_if_any|null>",
 401 |       "confidence": <0-1>
 402 |     }
 403 |   ],
 404 |   "variables": {
 405 |     "<name>": {
 406 |       "name": "<name>",
 407 |       "type": "<type>",
 408 |       "value": <value>,
 409 |       "definedAt": <line>,
 410 |       "lastModifiedAt": <line>,
 411 |       "isParameter": <boolean>
 412 |     }
 413 |   },
 414 |   "issues": [
 415 |     {
 416 |       "severity": "<error|warning|info>",
 417 |       "type": "<null-reference|type-mismatch|undefined-variable|unreachable-code|infinite-loop|missing-error-handling|deprecated-api|other>",
 418 |       "location": { "line": <number>, "function": "<name>" },
 419 |       "description": "<what's wrong>",
 420 |       "suggestion": "<how to fix>"
 421 |     }
 422 |   ],
 423 |   "confidence": <0-1 overall confidence>,
 424 |   "path": ["<function1>", "<function2>", ...],
 425 |   "reachedEnd": <boolean - did execution complete normally?>
 426 | }
 427 | 
 428 | Focus on:
 429 | 1. Variable initialization and modifications
 430 | 2. Function call order and arguments
 431 | 3. Conditional branch decisions
 432 | 4. Potential null/undefined access
 433 | 5. Type mismatches between example and implementation
 434 | 6. Error handling paths
 435 | 7. Return values at each step`;
 436 |   }
 437 | 
 438 |   /**
 439 |    * Parse LLM response into trace structure
 440 |    */
 441 |   private parseTraceResponse(response: string): {
 442 |     steps: ExecutionStep[];
 443 |     variables: Record<string, VariableState>;
 444 |     issues: PotentialIssue[];
 445 |     confidence: number;
 446 |     path: string[];
 447 |     reachedEnd: boolean;
 448 |   } {
 449 |     try {
 450 |       // Extract JSON from response (handle markdown code blocks)
 451 |       let jsonStr = response.trim();
 452 |       if (jsonStr.startsWith("```json")) {
 453 |         jsonStr = jsonStr.replace(/```json\n?/g, "").replace(/```\n?$/g, "");
 454 |       } else if (jsonStr.startsWith("```")) {
 455 |         jsonStr = jsonStr.replace(/```\n?/g, "");
 456 |       }
 457 | 
 458 |       const parsed = JSON.parse(jsonStr);
 459 | 
 460 |       return {
 461 |         steps: Array.isArray(parsed.steps) ? parsed.steps : [],
 462 |         variables: parsed.variables || {},
 463 |         issues: Array.isArray(parsed.issues) ? parsed.issues : [],
 464 |         confidence:
 465 |           typeof parsed.confidence === "number" ? parsed.confidence : 0.5,
 466 |         path: Array.isArray(parsed.path) ? parsed.path : [],
 467 |         reachedEnd: Boolean(parsed.reachedEnd),
 468 |       };
 469 |     } catch (error) {
 470 |       console.warn("Failed to parse LLM trace response:", error);
 471 |       return {
 472 |         steps: [],
 473 |         variables: {},
 474 |         issues: [
 475 |           {
 476 |             severity: "warning",
 477 |             type: "other",
 478 |             location: { line: 0 },
 479 |             description: "Failed to parse LLM response",
 480 |             suggestion: "Manual review required",
 481 |           },
 482 |         ],
 483 |         confidence: 0,
 484 |         path: [],
 485 |         reachedEnd: false,
 486 |       };
 487 |     }
 488 |   }
 489 | 
 490 |   /**
 491 |    * Static analysis fallback when LLM is not available
 492 |    */
 493 |   private async staticAnalysisTrace(
 494 |     exampleCode: string,
 495 |     implementationCode: string,
 496 |     exampleId: string,
 497 |     startTime: number,
 498 |   ): Promise<ExecutionTrace> {
 499 |     const issues: PotentialIssue[] = [];
 500 |     const variables: Record<string, VariableState> = {};
 501 |     const steps: ExecutionStep[] = [];
 502 | 
 503 |     // Basic static analysis
 504 |     const exampleLines = exampleCode.split("\n");
 505 |     // Note: implementationCode is used for context in detectUndefinedVariables
 506 | 
 507 |     // Detect potential null references
 508 |     if (this.options.detectNullRefs) {
 509 |       this.detectNullReferences(exampleCode, issues);
 510 |     }
 511 | 
 512 |     // Detect undefined variables
 513 |     this.detectUndefinedVariables(
 514 |       exampleCode,
 515 |       implementationCode,
 516 |       issues,
 517 |       variables,
 518 |     );
 519 | 
 520 |     // Detect unreachable code patterns
 521 |     if (this.options.detectUnreachableCode) {
 522 |       this.detectUnreachablePatterns(exampleCode, issues);
 523 |     }
 524 | 
 525 |     // Extract variable declarations
 526 |     this.extractVariableDeclarations(exampleCode, variables);
 527 | 
 528 |     // Create basic execution steps
 529 |     for (let i = 0; i < exampleLines.length; i++) {
 530 |       const line = exampleLines[i].trim();
 531 |       if (line && !line.startsWith("//") && !line.startsWith("/*")) {
 532 |         steps.push({
 533 |           lineNumber: i + 1,
 534 |           operation: this.inferOperation(line),
 535 |           stateChanges: {},
 536 |           callsMade: this.extractFunctionCalls(line),
 537 |           confidence: 0.5, // Lower confidence for static analysis
 538 |         });
 539 |       }
 540 |     }
 541 | 
 542 |     return {
 543 |       exampleId,
 544 |       entryPoint: this.detectEntryPoint(exampleCode),
 545 |       executionSteps: steps,
 546 |       variablesAccessed: variables,
 547 |       potentialIssues: issues,
 548 |       confidenceScore: 0.5, // Static analysis has lower confidence
 549 |       executionPath: [],
 550 |       reachedEnd: true,
 551 |       simulationDuration: Date.now() - startTime,
 552 |     };
 553 |   }
 554 | 
 555 |   /**
 556 |    * Detect potential null/undefined references
 557 |    */
 558 |   private detectNullReferences(code: string, issues: PotentialIssue[]): void {
 559 |     const lines = code.split("\n");
 560 | 
 561 |     lines.forEach((line, index) => {
 562 |       // Check for property access without null checks
 563 |       const propertyAccess = line.match(/(\w+)\.(\w+)/g);
 564 |       if (propertyAccess) {
 565 |         for (const access of propertyAccess) {
 566 |           // Check if there's an optional chaining or null check
 567 |           if (
 568 |             !line.includes("?.") &&
 569 |             !line.includes("&& ") &&
 570 |             !line.includes("!= null")
 571 |           ) {
 572 |             issues.push({
 573 |               severity: "warning",
 574 |               type: "null-reference",
 575 |               location: { line: index + 1 },
 576 |               description: `Potential null reference: ${access}`,
 577 |               suggestion: "Consider using optional chaining (?.) or null check",
 578 |               codeSnippet: line.trim(),
 579 |             });
 580 |           }
 581 |         }
 582 |       }
 583 |     });
 584 |   }
 585 | 
 586 |   /**
 587 |    * Detect undefined variable usage
 588 |    */
 589 |   private detectUndefinedVariables(
 590 |     exampleCode: string,
 591 |     implementationCode: string,
 592 |     issues: PotentialIssue[],
 593 |     variables: Record<string, VariableState>,
 594 |   ): void {
 595 |     // Extract declared variables from implementation
 596 |     const implVars = new Set<string>();
 597 |     const implMatches =
 598 |       implementationCode.match(/(const|let|var|function)\s+(\w+)/g) || [];
 599 |     for (const match of implMatches) {
 600 |       const name = match.split(/\s+/)[1];
 601 |       if (name) implVars.add(name);
 602 |     }
 603 | 
 604 |     // Check if example uses variables not in implementation
 605 |     const exampleLines = exampleCode.split("\n");
 606 |     exampleLines.forEach((line, index) => {
 607 |       const varUsage = line.match(/\b[a-zA-Z_]\w*\b/g) || [];
 608 |       for (const varName of varUsage) {
 609 |         // Skip keywords and common globals
 610 |         const keywords = [
 611 |           "const",
 612 |           "let",
 613 |           "var",
 614 |           "function",
 615 |           "if",
 616 |           "else",
 617 |           "for",
 618 |           "while",
 619 |           "return",
 620 |           "true",
 621 |           "false",
 622 |           "null",
 623 |           "undefined",
 624 |           "console",
 625 |           "async",
 626 |           "await",
 627 |           "import",
 628 |           "export",
 629 |         ];
 630 |         if (!keywords.includes(varName) && !implVars.has(varName)) {
 631 |           if (!variables[varName]) {
 632 |             issues.push({
 633 |               severity: "info",
 634 |               type: "undefined-variable",
 635 |               location: { line: index + 1 },
 636 |               description: `Variable '${varName}' used in example but not found in implementation`,
 637 |               suggestion: "Ensure the variable is defined or imported",
 638 |               codeSnippet: line.trim(),
 639 |             });
 640 |           }
 641 |         }
 642 |       }
 643 |     });
 644 |   }
 645 | 
 646 |   /**
 647 |    * Detect unreachable code patterns
 648 |    */
 649 |   private detectUnreachablePatterns(
 650 |     code: string,
 651 |     issues: PotentialIssue[],
 652 |   ): void {
 653 |     const lines = code.split("\n");
 654 |     let afterReturn = false;
 655 | 
 656 |     lines.forEach((line, index) => {
 657 |       const trimmed = line.trim();
 658 | 
 659 |       if (
 660 |         afterReturn &&
 661 |         trimmed &&
 662 |         !trimmed.startsWith("}") &&
 663 |         !trimmed.startsWith("//")
 664 |       ) {
 665 |         issues.push({
 666 |           severity: "warning",
 667 |           type: "unreachable-code",
 668 |           location: { line: index + 1 },
 669 |           description: "Code after return statement may be unreachable",
 670 |           suggestion: "Review control flow logic",
 671 |           codeSnippet: trimmed,
 672 |         });
 673 |       }
 674 | 
 675 |       if (trimmed.startsWith("return ") || trimmed === "return;") {
 676 |         afterReturn = true;
 677 |       }
 678 |       if (trimmed === "}") {
 679 |         afterReturn = false;
 680 |       }
 681 |     });
 682 |   }
 683 | 
 684 |   /**
 685 |    * Extract variable declarations from code
 686 |    */
 687 |   private extractVariableDeclarations(
 688 |     code: string,
 689 |     variables: Record<string, VariableState>,
 690 |   ): void {
 691 |     const lines = code.split("\n");
 692 | 
 693 |     lines.forEach((line, index) => {
 694 |       // Match const/let/var declarations
 695 |       const declMatch = line.match(
 696 |         /(const|let|var)\s+(\w+)(?:\s*:\s*(\w+))?\s*=\s*(.+)/,
 697 |       );
 698 |       if (declMatch) {
 699 |         const [, , name, type, value] = declMatch;
 700 |         variables[name] = {
 701 |           name,
 702 |           type: type || this.inferType(value),
 703 |           value: this.parseValue(value),
 704 |           definedAt: index + 1,
 705 |           lastModifiedAt: index + 1,
 706 |           isParameter: false,
 707 |         };
 708 |       }
 709 | 
 710 |       // Match function parameters
 711 |       const funcMatch = line.match(/function\s+\w+\s*\(([^)]*)\)/);
 712 |       if (funcMatch) {
 713 |         const params = funcMatch[1].split(",").map((p) => p.trim());
 714 |         for (const param of params) {
 715 |           if (param) {
 716 |             const [name, type] = param.split(":").map((s) => s.trim());
 717 |             if (name) {
 718 |               variables[name] = {
 719 |                 name,
 720 |                 type: type || "any",
 721 |                 value: undefined,
 722 |                 definedAt: index + 1,
 723 |                 lastModifiedAt: index + 1,
 724 |                 isParameter: true,
 725 |               };
 726 |             }
 727 |           }
 728 |         }
 729 |       }
 730 |     });
 731 |   }
 732 | 
 733 |   /**
 734 |    * Infer the operation from a line of code
 735 |    */
 736 |   private inferOperation(line: string): string {
 737 |     if (line.includes("=") && !line.includes("==")) {
 738 |       if (
 739 |         line.includes("const") ||
 740 |         line.includes("let") ||
 741 |         line.includes("var")
 742 |       ) {
 743 |         return "variable declaration";
 744 |       }
 745 |       return "assignment";
 746 |     }
 747 |     if (line.includes("if")) return "conditional check";
 748 |     if (line.includes("for") || line.includes("while")) return "loop iteration";
 749 |     if (line.includes("return")) return "return statement";
 750 |     if (line.includes("(") && line.includes(")")) return "function call";
 751 |     if (line.includes("await")) return "async operation";
 752 |     if (line.includes("throw")) return "throw error";
 753 |     if (line.includes("try")) return "try block start";
 754 |     if (line.includes("catch")) return "catch block";
 755 |     return "statement";
 756 |   }
 757 | 
 758 |   /**
 759 |    * Extract function calls from a line
 760 |    */
 761 |   private extractFunctionCalls(line: string): string[] {
 762 |     const calls: string[] = [];
 763 |     const callPattern = /(\w+)\s*\(/g;
 764 |     let match;
 765 | 
 766 |     while ((match = callPattern.exec(line)) !== null) {
 767 |       const name = match[1];
 768 |       // Skip keywords that look like function calls
 769 |       const keywords = ["if", "for", "while", "switch", "catch", "function"];
 770 |       if (!keywords.includes(name)) {
 771 |         calls.push(name);
 772 |       }
 773 |     }
 774 | 
 775 |     return calls;
 776 |   }
 777 | 
 778 |   /**
 779 |    * Infer type from value
 780 |    */
 781 |   private inferType(value: string): string {
 782 |     const trimmed = value.trim();
 783 |     if (
 784 |       trimmed.startsWith('"') ||
 785 |       trimmed.startsWith("'") ||
 786 |       trimmed.startsWith("`")
 787 |     )
 788 |       return "string";
 789 |     if (!isNaN(Number(trimmed))) return "number";
 790 |     if (trimmed === "true" || trimmed === "false") return "boolean";
 791 |     if (trimmed === "null") return "null";
 792 |     if (trimmed === "undefined") return "undefined";
 793 |     if (trimmed.startsWith("[")) return "array";
 794 |     if (trimmed.startsWith("{")) return "object";
 795 |     if (trimmed.includes("new ")) return "object";
 796 |     if (trimmed.includes("async") || trimmed.includes("=>")) return "function";
 797 |     return "unknown";
 798 |   }
 799 | 
 800 |   /**
 801 |    * Parse value from string
 802 |    */
 803 |   private parseValue(value: string): unknown {
 804 |     const trimmed = value.trim().replace(/;$/, "");
 805 |     try {
 806 |       return JSON.parse(trimmed);
 807 |     } catch {
 808 |       return trimmed;
 809 |     }
 810 |   }
 811 | 
 812 |   /**
 813 |    * Detect entry point from example code
 814 |    */
 815 |   private detectEntryPoint(code: string): string {
 816 |     // Look for function definitions
 817 |     const funcMatch = code.match(/(?:async\s+)?function\s+(\w+)/);
 818 |     if (funcMatch) return funcMatch[1];
 819 | 
 820 |     // Look for arrow function assignments
 821 |     const arrowMatch = code.match(
 822 |       /(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:\([^)]*\)|[^=])\s*=>/,
 823 |     );
 824 |     if (arrowMatch) return arrowMatch[1];
 825 | 
 826 |     // Look for main function call
 827 |     const mainMatch = code.match(/(\w+)\s*\(/);
 828 |     if (mainMatch) return mainMatch[1];
 829 | 
 830 |     return "main";
 831 |   }
 832 | 
 833 |   /**
 834 |    * Generate unique example ID
 835 |    */
 836 |   private generateExampleId(code: string): string {
 837 |     const hash = code.split("").reduce((a, b) => {
 838 |       a = (a << 5) - a + b.charCodeAt(0);
 839 |       return a & a;
 840 |     }, 0);
 841 |     return `example-${Math.abs(hash).toString(16)}`;
 842 |   }
 843 | 
 844 |   /**
 845 |    * Analyze trace for issues
 846 |    */
 847 |   private analyzeTraceForIssues(trace: ExecutionTrace): PotentialIssue[] {
 848 |     const issues = [...trace.potentialIssues];
 849 | 
 850 |     // Check for low confidence steps
 851 |     const lowConfidenceSteps = trace.executionSteps.filter(
 852 |       (s) => s.confidence < 0.5,
 853 |     );
 854 |     if (lowConfidenceSteps.length > trace.executionSteps.length * 0.3) {
 855 |       issues.push({
 856 |         severity: "warning",
 857 |         type: "other",
 858 |         location: { line: 0 },
 859 |         description: "Many execution steps have low confidence",
 860 |         suggestion: "Manual review recommended for this example",
 861 |       });
 862 |     }
 863 | 
 864 |     // Check if execution didn't reach the end
 865 |     if (!trace.reachedEnd) {
 866 |       issues.push({
 867 |         severity: "warning",
 868 |         type: "other",
 869 |         location: { line: 0 },
 870 |         description: "Execution simulation did not complete normally",
 871 |         suggestion: "Check for infinite loops or early termination",
 872 |       });
 873 |     }
 874 | 
 875 |     return issues;
 876 |   }
 877 | 
 878 |   /**
 879 |    * Check if trace behavior matches expected documentation
 880 |    */
 881 |   private async checkBehaviorMatch(
 882 |     trace: ExecutionTrace,
 883 |     expectedBehavior?: string,
 884 |   ): Promise<boolean> {
 885 |     if (!expectedBehavior) return true;
 886 | 
 887 |     // If LLM is available, use it for semantic comparison
 888 |     if (this.llmClient) {
 889 |       try {
 890 |         const prompt = `Compare the following execution trace with the expected behavior.
 891 | 
 892 | ## Execution Trace Summary:
 893 | - Steps: ${trace.executionSteps.length}
 894 | - Variables: ${Object.keys(trace.variablesAccessed).join(", ")}
 895 | - Issues found: ${trace.potentialIssues.length}
 896 | - Reached end: ${trace.reachedEnd}
 897 | 
 898 | ## Expected Behavior:
 899 | ${expectedBehavior}
 900 | 
 901 | Does the execution match the expected behavior? Respond with JSON:
 902 | {
 903 |   "matches": <boolean>,
 904 |   "reason": "<explanation>"
 905 | }`;
 906 | 
 907 |         const response = await this.llmClient.complete(prompt);
 908 |         const parsed = JSON.parse(
 909 |           response.replace(/```json\n?/g, "").replace(/```\n?$/g, ""),
 910 |         );
 911 |         return Boolean(parsed.matches);
 912 |       } catch {
 913 |         return true; // Default to matching if comparison fails
 914 |       }
 915 |     }
 916 | 
 917 |     return true;
 918 |   }
 919 | 
 920 |   /**
 921 |    * Build call graph node recursively
 922 |    */
 923 |   private async buildCallGraphNode(
 924 |     func: FunctionSignature,
 925 |     analysis: ASTAnalysisResult,
 926 |     depth: number,
 927 |     maxDepth: number,
 928 |     visited: Set<string>,
 929 |   ): Promise<CallGraphNode> {
 930 |     visited.add(func.name);
 931 | 
 932 |     const node: CallGraphNode = {
 933 |       function: func,
 934 |       location: {
 935 |         file: analysis.filePath,
 936 |         line: func.startLine,
 937 |       },
 938 |       calls: [],
 939 |       conditionals: [],
 940 |       raises: [],
 941 |       depth,
 942 |     };
 943 | 
 944 |     if (depth >= maxDepth) return node;
 945 | 
 946 |     // Find called functions
 947 |     for (const calledName of func.dependencies) {
 948 |       if (!visited.has(calledName)) {
 949 |         const calledFunc = analysis.functions.find(
 950 |           (f) => f.name === calledName,
 951 |         );
 952 |         if (calledFunc) {
 953 |           const childNode = await this.buildCallGraphNode(
 954 |             calledFunc,
 955 |             analysis,
 956 |             depth + 1,
 957 |             maxDepth,
 958 |             visited,
 959 |           );
 960 |           node.calls.push(childNode);
 961 |         }
 962 |       }
 963 |     }
 964 | 
 965 |     return node;
 966 |   }
 967 | 
 968 |   /**
 969 |    * Create empty call graph node
 970 |    */
 971 |   private createEmptyNode(name: string): CallGraphNode {
 972 |     return {
 973 |       function: {
 974 |         name,
 975 |         parameters: [],
 976 |         returnType: null,
 977 |         isAsync: false,
 978 |         isExported: false,
 979 |         isPublic: true,
 980 |         docComment: null,
 981 |         startLine: 0,
 982 |         endLine: 0,
 983 |         complexity: 0,
 984 |         dependencies: [],
 985 |       },
 986 |       location: { file: "unknown", line: 0 },
 987 |       calls: [],
 988 |       conditionals: [],
 989 |       raises: [],
 990 |       depth: 0,
 991 |     };
 992 |   }
 993 | }
 994 | 
 995 | /**
 996 |  * Factory function to create an execution simulator
 997 |  */
 998 | export function createExecutionSimulator(
 999 |   options?: SimulationOptions,
1000 | ): ExecutionSimulator {
1001 |   return new ExecutionSimulator(options);
1002 | }
1003 | 
```
Page 21/33FirstPrevNextLast