This is page 6 of 29. 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
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── commitlint.config.js
├── CONTRIBUTING.md
├── docker-compose.docs.yml
├── Dockerfile.docs
├── docs
│ ├── .docusaurus
│ │ ├── docusaurus-plugin-content-docs
│ │ │ └── default
│ │ │ └── __mdx-loader-dependency.json
│ │ └── docusaurus-plugin-content-pages
│ │ └── default
│ │ └── __plugin.json
│ ├── adrs
│ │ ├── 001-mcp-server-architecture.md
│ │ ├── 002-repository-analysis-engine.md
│ │ ├── 003-static-site-generator-recommendation-engine.md
│ │ ├── 004-diataxis-framework-integration.md
│ │ ├── 005-github-pages-deployment-automation.md
│ │ ├── 006-mcp-tools-api-design.md
│ │ ├── 007-mcp-prompts-and-resources-integration.md
│ │ ├── 008-intelligent-content-population-engine.md
│ │ ├── 009-content-accuracy-validation-framework.md
│ │ ├── 010-mcp-resource-pattern-redesign.md
│ │ └── README.md
│ ├── api
│ │ ├── .nojekyll
│ │ ├── assets
│ │ │ ├── hierarchy.js
│ │ │ ├── highlight.css
│ │ │ ├── icons.js
│ │ │ ├── icons.svg
│ │ │ ├── main.js
│ │ │ ├── navigation.js
│ │ │ ├── search.js
│ │ │ └── style.css
│ │ ├── hierarchy.html
│ │ ├── index.html
│ │ ├── modules.html
│ │ └── variables
│ │ └── TOOLS.html
│ ├── assets
│ │ └── logo.svg
│ ├── development
│ │ └── MCP_INSPECTOR_TESTING.md
│ ├── docusaurus.config.js
│ ├── explanation
│ │ ├── architecture.md
│ │ └── index.md
│ ├── guides
│ │ ├── link-validation.md
│ │ ├── playwright-integration.md
│ │ └── playwright-testing-workflow.md
│ ├── how-to
│ │ ├── analytics-setup.md
│ │ ├── custom-domains.md
│ │ ├── documentation-freshness-tracking.md
│ │ ├── github-pages-deployment.md
│ │ ├── index.md
│ │ ├── local-testing.md
│ │ ├── performance-optimization.md
│ │ ├── prompting-guide.md
│ │ ├── repository-analysis.md
│ │ ├── seo-optimization.md
│ │ ├── site-monitoring.md
│ │ ├── troubleshooting.md
│ │ └── usage-examples.md
│ ├── index.md
│ ├── knowledge-graph.md
│ ├── package-lock.json
│ ├── package.json
│ ├── phase-2-intelligence.md
│ ├── reference
│ │ ├── api-overview.md
│ │ ├── cli.md
│ │ ├── configuration.md
│ │ ├── deploy-pages.md
│ │ ├── index.md
│ │ ├── mcp-tools.md
│ │ └── prompt-templates.md
│ ├── research
│ │ ├── cross-domain-integration
│ │ │ └── README.md
│ │ ├── domain-1-mcp-architecture
│ │ │ ├── index.md
│ │ │ └── mcp-performance-research.md
│ │ ├── domain-2-repository-analysis
│ │ │ └── README.md
│ │ ├── domain-3-ssg-recommendation
│ │ │ ├── index.md
│ │ │ └── ssg-performance-analysis.md
│ │ ├── domain-4-diataxis-integration
│ │ │ └── README.md
│ │ ├── domain-5-github-deployment
│ │ │ ├── github-pages-security-analysis.md
│ │ │ └── index.md
│ │ ├── domain-6-api-design
│ │ │ └── README.md
│ │ ├── README.md
│ │ ├── research-integration-summary-2025-01-14.md
│ │ ├── research-progress-template.md
│ │ └── research-questions-2025-01-14.md
│ ├── robots.txt
│ ├── sidebars.js
│ ├── sitemap.xml
│ ├── src
│ │ └── css
│ │ └── custom.css
│ └── tutorials
│ ├── development-setup.md
│ ├── environment-setup.md
│ ├── first-deployment.md
│ ├── getting-started.md
│ ├── index.md
│ ├── memory-workflows.md
│ └── user-onboarding.md
├── jest.config.js
├── LICENSE
├── Makefile
├── MCP_PHASE2_IMPLEMENTATION.md
├── mcp-config-example.json
├── mcp.json
├── package-lock.json
├── package.json
├── README.md
├── release.sh
├── scripts
│ └── check-package-structure.cjs
├── SECURITY.md
├── setup-precommit.sh
├── src
│ ├── benchmarks
│ │ └── performance.ts
│ ├── index.ts
│ ├── memory
│ │ ├── contextual-retrieval.ts
│ │ ├── deployment-analytics.ts
│ │ ├── enhanced-manager.ts
│ │ ├── export-import.ts
│ │ ├── freshness-kg-integration.ts
│ │ ├── index.ts
│ │ ├── integration.ts
│ │ ├── kg-code-integration.ts
│ │ ├── kg-health.ts
│ │ ├── kg-integration.ts
│ │ ├── kg-link-validator.ts
│ │ ├── kg-storage.ts
│ │ ├── knowledge-graph.ts
│ │ ├── learning.ts
│ │ ├── manager.ts
│ │ ├── multi-agent-sharing.ts
│ │ ├── pruning.ts
│ │ ├── schemas.ts
│ │ ├── storage.ts
│ │ ├── temporal-analysis.ts
│ │ ├── user-preferences.ts
│ │ └── visualization.ts
│ ├── prompts
│ │ └── technical-writer-prompts.ts
│ ├── scripts
│ │ └── benchmark.ts
│ ├── templates
│ │ └── playwright
│ │ ├── accessibility.spec.template.ts
│ │ ├── Dockerfile.template
│ │ ├── docs-e2e.workflow.template.yml
│ │ ├── link-validation.spec.template.ts
│ │ └── playwright.config.template.ts
│ ├── tools
│ │ ├── analyze-deployments.ts
│ │ ├── analyze-readme.ts
│ │ ├── analyze-repository.ts
│ │ ├── check-documentation-links.ts
│ │ ├── deploy-pages.ts
│ │ ├── detect-gaps.ts
│ │ ├── evaluate-readme-health.ts
│ │ ├── generate-config.ts
│ │ ├── generate-contextual-content.ts
│ │ ├── generate-llm-context.ts
│ │ ├── generate-readme-template.ts
│ │ ├── generate-technical-writer-prompts.ts
│ │ ├── kg-health-check.ts
│ │ ├── manage-preferences.ts
│ │ ├── manage-sitemap.ts
│ │ ├── optimize-readme.ts
│ │ ├── populate-content.ts
│ │ ├── readme-best-practices.ts
│ │ ├── recommend-ssg.ts
│ │ ├── setup-playwright-tests.ts
│ │ ├── setup-structure.ts
│ │ ├── sync-code-to-docs.ts
│ │ ├── test-local-deployment.ts
│ │ ├── track-documentation-freshness.ts
│ │ ├── update-existing-documentation.ts
│ │ ├── validate-content.ts
│ │ ├── validate-documentation-freshness.ts
│ │ ├── validate-readme-checklist.ts
│ │ └── verify-deployment.ts
│ ├── types
│ │ └── api.ts
│ ├── utils
│ │ ├── ast-analyzer.ts
│ │ ├── code-scanner.ts
│ │ ├── content-extractor.ts
│ │ ├── drift-detector.ts
│ │ ├── freshness-tracker.ts
│ │ ├── language-parsers-simple.ts
│ │ ├── permission-checker.ts
│ │ └── sitemap-generator.ts
│ └── workflows
│ └── documentation-workflow.ts
├── test-docs-local.sh
├── tests
│ ├── api
│ │ └── mcp-responses.test.ts
│ ├── benchmarks
│ │ └── performance.test.ts
│ ├── edge-cases
│ │ └── error-handling.test.ts
│ ├── functional
│ │ └── tools.test.ts
│ ├── integration
│ │ ├── kg-documentation-workflow.test.ts
│ │ ├── knowledge-graph-workflow.test.ts
│ │ ├── mcp-readme-tools.test.ts
│ │ ├── memory-mcp-tools.test.ts
│ │ ├── readme-technical-writer.test.ts
│ │ └── workflow.test.ts
│ ├── memory
│ │ ├── contextual-retrieval.test.ts
│ │ ├── enhanced-manager.test.ts
│ │ ├── export-import.test.ts
│ │ ├── freshness-kg-integration.test.ts
│ │ ├── kg-code-integration.test.ts
│ │ ├── kg-health.test.ts
│ │ ├── kg-link-validator.test.ts
│ │ ├── kg-storage-validation.test.ts
│ │ ├── kg-storage.test.ts
│ │ ├── knowledge-graph-enhanced.test.ts
│ │ ├── knowledge-graph.test.ts
│ │ ├── learning.test.ts
│ │ ├── manager-advanced.test.ts
│ │ ├── manager.test.ts
│ │ ├── mcp-resource-integration.test.ts
│ │ ├── mcp-tool-persistence.test.ts
│ │ ├── schemas.test.ts
│ │ ├── storage.test.ts
│ │ ├── temporal-analysis.test.ts
│ │ └── user-preferences.test.ts
│ ├── performance
│ │ ├── memory-load-testing.test.ts
│ │ └── memory-stress-testing.test.ts
│ ├── prompts
│ │ ├── guided-workflow-prompts.test.ts
│ │ └── technical-writer-prompts.test.ts
│ ├── server.test.ts
│ ├── setup.ts
│ ├── tools
│ │ ├── all-tools.test.ts
│ │ ├── analyze-coverage.test.ts
│ │ ├── analyze-deployments.test.ts
│ │ ├── analyze-readme.test.ts
│ │ ├── analyze-repository.test.ts
│ │ ├── check-documentation-links.test.ts
│ │ ├── deploy-pages-kg-retrieval.test.ts
│ │ ├── deploy-pages-tracking.test.ts
│ │ ├── deploy-pages.test.ts
│ │ ├── detect-gaps.test.ts
│ │ ├── evaluate-readme-health.test.ts
│ │ ├── generate-contextual-content.test.ts
│ │ ├── generate-llm-context.test.ts
│ │ ├── generate-readme-template.test.ts
│ │ ├── generate-technical-writer-prompts.test.ts
│ │ ├── kg-health-check.test.ts
│ │ ├── manage-sitemap.test.ts
│ │ ├── optimize-readme.test.ts
│ │ ├── readme-best-practices.test.ts
│ │ ├── recommend-ssg-historical.test.ts
│ │ ├── recommend-ssg-preferences.test.ts
│ │ ├── recommend-ssg.test.ts
│ │ ├── simple-coverage.test.ts
│ │ ├── sync-code-to-docs.test.ts
│ │ ├── test-local-deployment.test.ts
│ │ ├── tool-error-handling.test.ts
│ │ ├── track-documentation-freshness.test.ts
│ │ ├── validate-content.test.ts
│ │ ├── validate-documentation-freshness.test.ts
│ │ └── validate-readme-checklist.test.ts
│ ├── types
│ │ └── type-safety.test.ts
│ └── utils
│ ├── ast-analyzer.test.ts
│ ├── content-extractor.test.ts
│ ├── drift-detector.test.ts
│ ├── freshness-tracker.test.ts
│ └── sitemap-generator.test.ts
├── tsconfig.json
└── typedoc.json
```
# Files
--------------------------------------------------------------------------------
/tests/memory/schemas.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Tests for Knowledge Graph Schemas
3 | * Phase 1: Core Knowledge Graph Integration
4 | */
5 |
6 | import { describe, it, expect } from "@jest/globals";
7 | import {
8 | ProjectEntitySchema,
9 | UserEntitySchema,
10 | ConfigurationEntitySchema,
11 | CodeFileEntitySchema,
12 | DocumentationSectionEntitySchema,
13 | TechnologyEntitySchema,
14 | ProjectUsesTechnologySchema,
15 | UserPrefersSSGSchema,
16 | ProjectDeployedWithSchema,
17 | SimilarToSchema,
18 | DocumentsSchema,
19 | ReferencesSchema,
20 | OutdatedForSchema,
21 | validateEntity,
22 | validateRelationship,
23 | isProjectEntity,
24 | isUserEntity,
25 | SCHEMA_METADATA,
26 | } from "../../src/memory/schemas.js";
27 |
28 | describe("Entity Schemas", () => {
29 | describe("ProjectEntitySchema", () => {
30 | it("should validate a valid project entity", () => {
31 | const validProject = {
32 | name: "test-project",
33 | path: "/path/to/project",
34 | technologies: ["typescript", "javascript"],
35 | size: "medium" as const,
36 | lastAnalyzed: new Date().toISOString(),
37 | analysisCount: 1,
38 | hasTests: true,
39 | hasCI: true,
40 | hasDocs: false,
41 | totalFiles: 100,
42 | };
43 |
44 | const result = ProjectEntitySchema.parse(validProject);
45 | expect(result).toBeDefined();
46 | expect(result.name).toBe("test-project");
47 | expect(result.technologies).toHaveLength(2);
48 | });
49 |
50 | it("should apply defaults for optional fields", () => {
51 | const minimalProject = {
52 | name: "minimal-project",
53 | path: "/path/to/minimal",
54 | lastAnalyzed: new Date().toISOString(),
55 | };
56 |
57 | const result = ProjectEntitySchema.parse(minimalProject);
58 | expect(result.technologies).toEqual([]);
59 | expect(result.size).toBe("medium");
60 | expect(result.analysisCount).toBe(0);
61 | expect(result.hasTests).toBe(false);
62 | });
63 |
64 | it("should reject invalid size values", () => {
65 | const invalidProject = {
66 | name: "test-project",
67 | path: "/path/to/project",
68 | size: "huge", // Invalid
69 | lastAnalyzed: new Date().toISOString(),
70 | };
71 |
72 | expect(() => ProjectEntitySchema.parse(invalidProject)).toThrow();
73 | });
74 |
75 | it("should require name and path", () => {
76 | const missingName = {
77 | path: "/path/to/project",
78 | lastAnalyzed: new Date().toISOString(),
79 | };
80 |
81 | expect(() => ProjectEntitySchema.parse(missingName)).toThrow();
82 | });
83 | });
84 |
85 | describe("UserEntitySchema", () => {
86 | it("should validate a valid user entity", () => {
87 | const validUser = {
88 | userId: "user123",
89 | expertiseLevel: "intermediate" as const,
90 | preferredTechnologies: ["react", "typescript"],
91 | preferredSSGs: ["docusaurus"],
92 | lastActive: new Date().toISOString(),
93 | createdAt: new Date().toISOString(),
94 | };
95 |
96 | const result = UserEntitySchema.parse(validUser);
97 | expect(result.userId).toBe("user123");
98 | expect(result.expertiseLevel).toBe("intermediate");
99 | });
100 |
101 | it("should apply defaults", () => {
102 | const minimalUser = {
103 | userId: "user456",
104 | lastActive: new Date().toISOString(),
105 | createdAt: new Date().toISOString(),
106 | };
107 |
108 | const result = UserEntitySchema.parse(minimalUser);
109 | expect(result.expertiseLevel).toBe("intermediate");
110 | expect(result.preferredTechnologies).toEqual([]);
111 | expect(result.documentationStyle).toBe("comprehensive");
112 | });
113 | });
114 |
115 | describe("ConfigurationEntitySchema", () => {
116 | it("should validate a valid configuration entity", () => {
117 | const validConfig = {
118 | ssg: "docusaurus" as const,
119 | settings: { theme: "classic" },
120 | deploymentSuccessRate: 0.95,
121 | usageCount: 10,
122 | lastUsed: new Date().toISOString(),
123 | };
124 |
125 | const result = ConfigurationEntitySchema.parse(validConfig);
126 | expect(result.ssg).toBe("docusaurus");
127 | expect(result.deploymentSuccessRate).toBe(0.95);
128 | });
129 |
130 | it("should reject invalid SSG values", () => {
131 | const invalidConfig = {
132 | ssg: "gatsby", // Not in enum
133 | lastUsed: new Date().toISOString(),
134 | };
135 |
136 | expect(() => ConfigurationEntitySchema.parse(invalidConfig)).toThrow();
137 | });
138 |
139 | it("should validate success rate bounds", () => {
140 | const invalidRate = {
141 | ssg: "jekyll" as const,
142 | deploymentSuccessRate: 1.5, // > 1.0
143 | lastUsed: new Date().toISOString(),
144 | };
145 |
146 | expect(() => ConfigurationEntitySchema.parse(invalidRate)).toThrow();
147 | });
148 | });
149 |
150 | describe("CodeFileEntitySchema", () => {
151 | it("should validate a valid code file entity", () => {
152 | const validCodeFile = {
153 | path: "/src/index.ts",
154 | language: "typescript",
155 | functions: ["main", "helper"],
156 | classes: ["App"],
157 | dependencies: ["express", "zod"],
158 | lastModified: new Date().toISOString(),
159 | contentHash: "abc123",
160 | linesOfCode: 150,
161 | };
162 |
163 | const result = CodeFileEntitySchema.parse(validCodeFile);
164 | expect(result.language).toBe("typescript");
165 | expect(result.functions).toHaveLength(2);
166 | });
167 | });
168 |
169 | describe("DocumentationSectionEntitySchema", () => {
170 | it("should validate a valid documentation section", () => {
171 | const validSection = {
172 | filePath: "/docs/api.md",
173 | sectionTitle: "API Reference",
174 | contentHash: "def456",
175 | referencedCodeFiles: ["/src/api.ts"],
176 | lastUpdated: new Date().toISOString(),
177 | category: "reference" as const,
178 | };
179 |
180 | const result = DocumentationSectionEntitySchema.parse(validSection);
181 | expect(result.category).toBe("reference");
182 | expect(result.referencedCodeFiles).toHaveLength(1);
183 | });
184 | });
185 | });
186 |
187 | describe("Relationship Schemas", () => {
188 | describe("ProjectUsesTechnologySchema", () => {
189 | it("should validate a valid project-technology relationship", () => {
190 | const validRelationship = {
191 | type: "project_uses_technology" as const,
192 | weight: 0.8,
193 | confidence: 1.0,
194 | createdAt: new Date().toISOString(),
195 | lastUpdated: new Date().toISOString(),
196 | fileCount: 50,
197 | percentage: 80,
198 | isPrimary: true,
199 | metadata: {},
200 | };
201 |
202 | const result = ProjectUsesTechnologySchema.parse(validRelationship);
203 | expect(result.type).toBe("project_uses_technology");
204 | expect(result.isPrimary).toBe(true);
205 | });
206 | });
207 |
208 | describe("ProjectDeployedWithSchema", () => {
209 | it("should validate a successful deployment relationship", () => {
210 | const validDeployment = {
211 | type: "project_deployed_with" as const,
212 | weight: 1.0,
213 | confidence: 1.0,
214 | createdAt: new Date().toISOString(),
215 | lastUpdated: new Date().toISOString(),
216 | success: true,
217 | timestamp: new Date().toISOString(),
218 | buildTime: 45,
219 | deploymentUrl: "https://example.com",
220 | metadata: {},
221 | };
222 |
223 | const result = ProjectDeployedWithSchema.parse(validDeployment);
224 | expect(result.success).toBe(true);
225 | expect(result.buildTime).toBe(45);
226 | });
227 |
228 | it("should validate a failed deployment relationship", () => {
229 | const failedDeployment = {
230 | type: "project_deployed_with" as const,
231 | weight: 0.5,
232 | confidence: 1.0,
233 | createdAt: new Date().toISOString(),
234 | lastUpdated: new Date().toISOString(),
235 | success: false,
236 | timestamp: new Date().toISOString(),
237 | errorMessage: "Build failed",
238 | metadata: {},
239 | };
240 |
241 | const result = ProjectDeployedWithSchema.parse(failedDeployment);
242 | expect(result.success).toBe(false);
243 | expect(result.errorMessage).toBe("Build failed");
244 | });
245 | });
246 |
247 | describe("OutdatedForSchema", () => {
248 | it("should validate an outdated documentation relationship", () => {
249 | const validOutdated = {
250 | type: "outdated_for" as const,
251 | weight: 1.0,
252 | confidence: 0.9,
253 | createdAt: new Date().toISOString(),
254 | lastUpdated: new Date().toISOString(),
255 | detectedAt: new Date().toISOString(),
256 | changeType: "function_signature" as const,
257 | severity: "high" as const,
258 | autoFixable: false,
259 | metadata: {},
260 | };
261 |
262 | const result = OutdatedForSchema.parse(validOutdated);
263 | expect(result.changeType).toBe("function_signature");
264 | expect(result.severity).toBe("high");
265 | });
266 | });
267 | });
268 |
269 | describe("Validation Functions", () => {
270 | describe("validateEntity", () => {
271 | it("should validate a complete entity", () => {
272 | const entity = {
273 | type: "project",
274 | name: "test-project",
275 | path: "/test",
276 | lastAnalyzed: new Date().toISOString(),
277 | };
278 |
279 | const result = validateEntity(entity);
280 | expect(result).toBeDefined();
281 | expect(result.type).toBe("project");
282 | });
283 |
284 | it("should throw on invalid entity", () => {
285 | const invalidEntity = {
286 | type: "invalid_type",
287 | name: "test",
288 | };
289 |
290 | expect(() => validateEntity(invalidEntity)).toThrow();
291 | });
292 | });
293 |
294 | describe("validateRelationship", () => {
295 | it("should validate a complete relationship", () => {
296 | const relationship = {
297 | type: "similar_to",
298 | weight: 0.85,
299 | confidence: 0.9,
300 | createdAt: new Date().toISOString(),
301 | lastUpdated: new Date().toISOString(),
302 | similarityScore: 0.85,
303 | sharedTechnologies: ["typescript"],
304 | metadata: {},
305 | };
306 |
307 | const result = validateRelationship(relationship);
308 | expect(result).toBeDefined();
309 | });
310 | });
311 | });
312 |
313 | describe("Type Guards", () => {
314 | describe("isProjectEntity", () => {
315 | it("should return true for project entities", () => {
316 | const entity = {
317 | type: "project" as const,
318 | name: "test",
319 | path: "/test",
320 | technologies: ["typescript"],
321 | size: "medium" as const,
322 | lastAnalyzed: new Date().toISOString(),
323 | analysisCount: 1,
324 | hasTests: false,
325 | hasCI: false,
326 | hasDocs: false,
327 | totalFiles: 10,
328 | };
329 |
330 | expect(isProjectEntity(entity)).toBe(true);
331 | });
332 |
333 | it("should return false for non-project entities", () => {
334 | const entity = {
335 | type: "user" as const,
336 | userId: "user123",
337 | expertiseLevel: "intermediate" as const,
338 | preferredTechnologies: [],
339 | preferredSSGs: [],
340 | documentationStyle: "comprehensive" as const,
341 | preferredDiataxisCategories: [],
342 | projectCount: 0,
343 | lastActive: new Date().toISOString(),
344 | createdAt: new Date().toISOString(),
345 | };
346 |
347 | expect(isProjectEntity(entity as any)).toBe(false);
348 | });
349 | });
350 |
351 | describe("isUserEntity", () => {
352 | it("should return true for user entities", () => {
353 | const entity = {
354 | type: "user" as const,
355 | userId: "user123",
356 | expertiseLevel: "intermediate" as const,
357 | preferredTechnologies: [],
358 | preferredSSGs: [],
359 | documentationStyle: "comprehensive" as const,
360 | preferredDiataxisCategories: [],
361 | projectCount: 0,
362 | lastActive: new Date().toISOString(),
363 | createdAt: new Date().toISOString(),
364 | };
365 |
366 | expect(isUserEntity(entity)).toBe(true);
367 | });
368 | });
369 | });
370 |
371 | describe("Schema Metadata", () => {
372 | it("should have correct version", () => {
373 | expect(SCHEMA_METADATA.version).toBe("1.0.0");
374 | });
375 |
376 | it("should list all entity types", () => {
377 | expect(SCHEMA_METADATA.entityTypes).toContain("project");
378 | expect(SCHEMA_METADATA.entityTypes).toContain("user");
379 | expect(SCHEMA_METADATA.entityTypes).toContain("configuration");
380 | expect(SCHEMA_METADATA.entityTypes).toContain("code_file");
381 | expect(SCHEMA_METADATA.entityTypes).toContain(
382 | "documentation_freshness_event",
383 | );
384 | expect(SCHEMA_METADATA.entityTypes).toHaveLength(8);
385 | });
386 |
387 | it("should list all relationship types", () => {
388 | expect(SCHEMA_METADATA.relationshipTypes).toContain(
389 | "project_uses_technology",
390 | );
391 | expect(SCHEMA_METADATA.relationshipTypes).toContain("outdated_for");
392 | expect(SCHEMA_METADATA.relationshipTypes).toContain("project_has_sitemap");
393 | expect(SCHEMA_METADATA.relationshipTypes).toContain(
394 | "project_has_freshness_event",
395 | );
396 | expect(SCHEMA_METADATA.relationshipTypes).toHaveLength(13);
397 | });
398 | });
399 |
```
--------------------------------------------------------------------------------
/docs/tutorials/user-onboarding.md:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | documcp:
3 | last_updated: "2025-11-20T00:46:21.973Z"
4 | last_validated: "2025-11-20T00:46:21.973Z"
5 | auto_updated: false
6 | update_frequency: monthly
7 | ---
8 |
9 | # DocuMCP User Onboarding Guide
10 |
11 | Welcome to DocuMCP! This comprehensive guide will help you get started with DocuMCP in your own environment, from initial setup to advanced usage patterns.
12 |
13 | ## 🚀 Quick Start
14 |
15 | ### Prerequisites
16 |
17 | - **Node.js**: Version 20.0.0 or higher
18 | - **npm**: Version 8.0.0 or higher
19 | - **Git**: For repository analysis
20 | - **GitHub Account**: For GitHub Pages deployment
21 |
22 | ### Installation
23 |
24 | ```bash
25 | # Install DocuMCP globally
26 | npm install -g documcp
27 |
28 | # Or install locally in your project
29 | npm install documcp --save-dev
30 | ```
31 |
32 | ### Verify Installation
33 |
34 | ```bash
35 | # Check if DocuMCP is installed correctly
36 | documcp --version
37 |
38 | # Should output: DocuMCP v0.5.0
39 | ```
40 |
41 | ## 📋 Basic Usage Patterns
42 |
43 | ### Pattern 1: Repository Analysis
44 |
45 | Start by analyzing your repository to understand its structure and documentation needs.
46 |
47 | ```bash
48 | # Basic repository analysis
49 | documcp analyze-repository --path ./my-project --depth standard
50 |
51 | # Quick analysis for large repositories
52 | documcp analyze-repository --path ./large-project --depth quick
53 |
54 | # Deep analysis for comprehensive documentation
55 | documcp analyze-repository --path ./complex-project --depth deep
56 | ```
57 |
58 | **Example Output:**
59 |
60 | ```json
61 | {
62 | "success": true,
63 | "data": {
64 | "id": "analysis_abc123_def456",
65 | "structure": {
66 | "totalFiles": 150,
67 | "totalDirectories": 25,
68 | "languages": { ".ts": 100, ".md": 20, ".json": 10 },
69 | "hasTests": true,
70 | "hasCI": true,
71 | "hasDocs": false
72 | },
73 | "recommendations": {
74 | "primaryLanguage": "TypeScript",
75 | "projectType": "Library",
76 | "teamSize": "small"
77 | }
78 | }
79 | }
80 | ```
81 |
82 | ### Pattern 2: SSG Recommendation
83 |
84 | Get intelligent recommendations for the best static site generator for your project.
85 |
86 | ```bash
87 | # Get SSG recommendation based on analysis
88 | documcp recommend-ssg --analysis-id analysis_abc123_def456
89 |
90 | # With user preferences
91 | documcp recommend-ssg --analysis-id analysis_abc123_def456 --priority performance --ecosystem javascript
92 |
93 | # For enterprise users
94 | documcp recommend-ssg --analysis-id analysis_abc123_def456 --priority simplicity
95 | ```
96 |
97 | **Example Output:**
98 |
99 | ```json
100 | {
101 | "success": true,
102 | "data": {
103 | "recommended": "docusaurus",
104 | "confidence": 0.92,
105 | "reasoning": [
106 | "React-based project detected",
107 | "Documentation focus identified",
108 | "Team size suitable for Docusaurus"
109 | ],
110 | "alternatives": [
111 | {
112 | "name": "hugo",
113 | "score": 0.85,
114 | "pros": ["Performance", "Fast builds"],
115 | "cons": ["Learning curve", "Go templates"]
116 | }
117 | ]
118 | }
119 | }
120 | ```
121 |
122 | ### Pattern 3: Documentation Structure Setup
123 |
124 | Create a Diataxis-compliant documentation structure.
125 |
126 | ```bash
127 | # Set up documentation structure
128 | documcp setup-structure --path ./docs --ssg docusaurus --include-examples
129 |
130 | # Minimal structure for existing projects
131 | documcp setup-structure --path ./site --ssg hugo --include-examples false
132 | ```
133 |
134 | ### Pattern 4: Configuration Generation
135 |
136 | Generate configuration files for your chosen SSG.
137 |
138 | ```bash
139 | # Generate Docusaurus configuration
140 | documcp generate-config --ssg docusaurus --project-name "My Project" --output-path ./docs
141 |
142 | # Generate Hugo configuration
143 | documcp generate-config --ssg hugo --project-name "My Site" --output-path ./site
144 | ```
145 |
146 | ### Pattern 5: Content Population
147 |
148 | Populate your documentation with intelligent content based on your repository.
149 |
150 | ```bash
151 | # Populate documentation content
152 | documcp populate-content --analysis-id analysis_abc123_def456 --docs-path ./docs
153 |
154 | # With specific focus areas
155 | documcp populate-content --analysis-id analysis_abc123_def456 --docs-path ./docs --focus-areas api,examples
156 | ```
157 |
158 | ### Pattern 6: GitHub Pages Deployment
159 |
160 | Deploy your documentation to GitHub Pages.
161 |
162 | ```bash
163 | # Deploy to GitHub Pages
164 | documcp deploy-pages --repository "user/repo" --ssg docusaurus
165 |
166 | # With custom domain
167 | documcp deploy-pages --repository "user/repo" --ssg docusaurus --custom-domain "docs.example.com"
168 | ```
169 |
170 | ## 🎯 Common Use Cases
171 |
172 | ### Use Case 1: New Open Source Project
173 |
174 | For a new open source project, follow this workflow:
175 |
176 | ```bash
177 | # 1. Analyze your repository
178 | ANALYSIS_ID=$(documcp analyze-repository --path . --depth standard | jq -r '.data.id')
179 |
180 | # 2. Get SSG recommendation
181 | documcp recommend-ssg --analysis-id $ANALYSIS_ID --priority community_focused
182 |
183 | # 3. Set up documentation structure
184 | documcp setup-structure --path ./docs --ssg docusaurus --include-examples
185 |
186 | # 4. Generate configuration
187 | documcp generate-config --ssg docusaurus --project-name "My Open Source Project" --output-path ./docs
188 |
189 | # 5. Populate content
190 | documcp populate-content --analysis-id $ANALYSIS_ID --docs-path ./docs
191 |
192 | # 6. Deploy to GitHub Pages
193 | documcp deploy-pages --repository "$(git remote get-url origin | sed 's/.*github.com[:/]\([^.]*\).*/\1/')" --ssg docusaurus
194 | ```
195 |
196 | ### Use Case 2: Enterprise Documentation
197 |
198 | For enterprise documentation with specific requirements:
199 |
200 | ```bash
201 | # 1. Analyze with enterprise focus
202 | ANALYSIS_ID=$(documcp analyze-repository --path . --depth deep | jq -r '.data.id')
203 |
204 | # 2. Get enterprise-focused recommendation
205 | documcp recommend-ssg --analysis-id $ANALYSIS_ID --priority enterprise_focused
206 |
207 | # 3. Set up minimal structure
208 | documcp setup-structure --path ./enterprise-docs --ssg hugo --include-examples false
209 |
210 | # 4. Generate enterprise configuration
211 | documcp generate-config --ssg hugo --project-name "Enterprise Documentation" --output-path ./enterprise-docs
212 |
213 | # 5. Populate with enterprise focus
214 | documcp populate-content --analysis-id $ANALYSIS_ID --docs-path ./enterprise-docs --focus-areas security,compliance,api
215 | ```
216 |
217 | ### Use Case 3: API Documentation
218 |
219 | For API-focused projects:
220 |
221 | ```bash
222 | # 1. Analyze API project
223 | ANALYSIS_ID=$(documcp analyze-repository --path . --depth standard | jq -r '.data.id')
224 |
225 | # 2. Get API-focused recommendation
226 | documcp recommend-ssg --analysis-id $ANALYSIS_ID --priority features
227 |
228 | # 3. Set up API documentation structure
229 | documcp setup-structure --path ./api-docs --ssg docusaurus --include-examples
230 |
231 | # 4. Generate API documentation configuration
232 | documcp generate-config --ssg docusaurus --project-name "API Documentation" --output-path ./api-docs
233 |
234 | # 5. Populate with API focus
235 | documcp populate-content --analysis-id $ANALYSIS_ID --docs-path ./api-docs --focus-areas api,examples,integration
236 | ```
237 |
238 | ## 🔧 Advanced Configuration
239 |
240 | ### Environment Variables
241 |
242 | Set up environment variables for advanced configuration:
243 |
244 | ```bash
245 | # GitHub token for deployment
246 | export GITHUB_TOKEN="your_github_token"
247 |
248 | # Custom storage directory for memory
249 | export DOCUMCP_STORAGE_DIR="./.documcp"
250 |
251 | # Development mode for debugging
252 | export NODE_ENV="development"
253 | ```
254 |
255 | ### Memory System Configuration
256 |
257 | Configure the memory system for learning and pattern recognition:
258 |
259 | ```bash
260 | # Initialize memory system
261 | documcp memory initialize --storage-dir ./.documcp
262 |
263 | # Export memories for backup
264 | documcp memory export --format json --output ./documcp-memories.json
265 |
266 | # Import memories from backup
267 | documcp memory import --format json --input ./documcp-memories.json
268 | ```
269 |
270 | ### User Preferences
271 |
272 | Set up user preferences for personalized recommendations:
273 |
274 | ```bash
275 | # Set user preferences
276 | documcp preferences set --user-id "developer123" --priority performance --ecosystem javascript
277 |
278 | # Get personalized recommendations
279 | documcp recommend-ssg --analysis-id $ANALYSIS_ID --user-id "developer123"
280 |
281 | # Export preferences
282 | documcp preferences export --user-id "developer123" --output ./preferences.json
283 | ```
284 |
285 | ## 🚨 Troubleshooting
286 |
287 | ### Common Issues
288 |
289 | #### Issue 1: Repository Analysis Fails
290 |
291 | **Problem:** `Permission denied: Cannot read directory`
292 |
293 | **Solution:**
294 |
295 | ```bash
296 | # Check directory permissions
297 | ls -la /path/to/repository
298 |
299 | # Fix permissions if needed
300 | chmod -R 755 /path/to/repository
301 |
302 | # Run analysis again
303 | documcp analyze-repository --path /path/to/repository --depth standard
304 | ```
305 |
306 | #### Issue 2: SSG Recommendation Returns Low Confidence
307 |
308 | **Problem:** Low confidence scores in recommendations
309 |
310 | **Solution:**
311 |
312 | ```bash
313 | # Try deeper analysis
314 | documcp analyze-repository --path . --depth deep
315 |
316 | # Use specific preferences
317 | documcp recommend-ssg --analysis-id $ANALYSIS_ID --priority simplicity --ecosystem any
318 |
319 | # Check for similar projects in memory
320 | documcp memory similar --analysis-id $ANALYSIS_ID
321 | ```
322 |
323 | #### Issue 3: GitHub Pages Deployment Fails
324 |
325 | **Problem:** Deployment fails with permission errors
326 |
327 | **Solution:**
328 |
329 | ```bash
330 | # Check GitHub token permissions
331 | curl -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/user
332 |
333 | # Ensure token has repo and pages permissions
334 | # Regenerate token with correct permissions if needed
335 |
336 | # Try deployment again
337 | documcp deploy-pages --repository "user/repo" --ssg docusaurus
338 | ```
339 |
340 | #### Issue 4: Content Population Generates Empty Content
341 |
342 | **Problem:** No content is generated during population
343 |
344 | **Solution:**
345 |
346 | ```bash
347 | # Check if repository has sufficient content
348 | documcp analyze-repository --path . --depth deep
349 |
350 | # Ensure documentation structure exists
351 | documcp setup-structure --path ./docs --ssg docusaurus
352 |
353 | # Try with different population level
354 | documcp populate-content --analysis-id $ANALYSIS_ID --docs-path ./docs --population-level comprehensive
355 | ```
356 |
357 | ## 📚 Best Practices
358 |
359 | ### 1. Repository Organization
360 |
361 | - Keep your repository well-organized with clear directory structure
362 | - Include a comprehensive README.md file
363 | - Use consistent naming conventions
364 | - Include package.json or equivalent dependency files
365 |
366 | ### 2. Documentation Structure
367 |
368 | - Follow Diataxis framework principles
369 | - Use clear, descriptive headings
370 | - Include code examples and use cases
371 | - Keep documentation up-to-date with code changes
372 |
373 | ### 3. Memory System Usage
374 |
375 | - Regularly export memories for backup
376 | - Use consistent user IDs for preference tracking
377 | - Clean up old memories periodically
378 | - Share memories across team members for better recommendations
379 |
380 | ### 4. Deployment Strategy
381 |
382 | - Test documentation locally before deployment
383 | - Use staging environments for testing
384 | - Monitor deployment success rates
385 | - Keep deployment configurations in version control
386 |
387 | ## 🔗 Integration Examples
388 |
389 | ### GitHub Actions Integration
390 |
391 | ```yaml
392 | name: Deploy Documentation
393 |
394 | on:
395 | push:
396 | branches: [main]
397 | paths: ["docs/**", "src/**"]
398 |
399 | jobs:
400 | deploy:
401 | runs-on: ubuntu-latest
402 | steps:
403 | - uses: actions/checkout@v4
404 |
405 | - name: Setup Node.js
406 | uses: actions/setup-node@v4
407 | with:
408 | node-version: "20"
409 |
410 | - name: Install DocuMCP
411 | run: npm install -g documcp
412 |
413 | - name: Analyze Repository
414 | id: analyze
415 | run: |
416 | ANALYSIS_ID=$(documcp analyze-repository --path . --depth standard | jq -r '.data.id')
417 | echo "analysis_id=$ANALYSIS_ID" >> $GITHUB_OUTPUT
418 |
419 | - name: Deploy Documentation
420 | run: |
421 | documcp deploy-pages --repository ${{ github.repository }} --ssg docusaurus
422 | env:
423 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
424 | ```
425 |
426 | ### Docker Integration
427 |
428 | ```dockerfile
429 | FROM node:20-alpine
430 |
431 | # Install DocuMCP
432 | RUN npm install -g documcp
433 |
434 | # Set working directory
435 | WORKDIR /app
436 |
437 | # Copy project files
438 | COPY . .
439 |
440 | # Analyze and deploy documentation
441 | RUN documcp analyze-repository --path . --depth standard && \
442 | documcp recommend-ssg --analysis-id $(documcp analyze-repository --path . | jq -r '.data.id') && \
443 | documcp deploy-pages --repository $REPOSITORY --ssg docusaurus
444 |
445 | EXPOSE 3000
446 | CMD ["documcp", "serve", "--port", "3000"]
447 | ```
448 |
449 | ## 📖 Additional Resources
450 |
451 | - [API Reference](../api/) - Complete API documentation
452 | - [Configuration Guide](../reference/configuration.md) - Detailed configuration options
453 | - [MCP Tools Reference](../reference/mcp-tools.md) - MCP tool specifications
454 | - [GitHub Pages Deployment](../how-to/github-pages-deployment.md) - Deployment guide
455 | - [Troubleshooting Guide](../how-to/troubleshooting.md) - Common issues and solutions
456 |
457 | ## 🤝 Getting Help
458 |
459 | - **GitHub Issues**: Report bugs and request features
460 | - **GitHub Discussions**: Ask questions and share ideas
461 | - **Documentation**: Check the comprehensive documentation
462 | - **API Reference**: Explore the complete API documentation
463 |
464 | Welcome to the DocuMCP community! 🎉
465 |
```
--------------------------------------------------------------------------------
/docs/api/assets/icons.svg:
--------------------------------------------------------------------------------
```
1 | <svg xmlns="http://www.w3.org/2000/svg"><g id="icon-1" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-module)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">M</text></g><g id="icon-2" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-module)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">M</text></g><g id="icon-4" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-namespace)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">N</text></g><g id="icon-8" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-enum)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">E</text></g><g id="icon-16" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-property)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">P</text></g><g id="icon-32" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-variable)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">V</text></g><g id="icon-64" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-function)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">F</text></g><g id="icon-128" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-class)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">C</text></g><g id="icon-256" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-interface)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">I</text></g><g id="icon-512" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-constructor)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">C</text></g><g id="icon-1024" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-property)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">P</text></g><g id="icon-2048" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-method)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">M</text></g><g id="icon-4096" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-function)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">F</text></g><g id="icon-8192" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-property)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">P</text></g><g id="icon-16384" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-constructor)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">C</text></g><g id="icon-32768" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-property)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">P</text></g><g id="icon-65536" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-type-alias)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">T</text></g><g id="icon-131072" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-type-alias)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">T</text></g><g id="icon-262144" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-accessor)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">A</text></g><g id="icon-524288" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-accessor)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">A</text></g><g id="icon-1048576" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-accessor)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">A</text></g><g id="icon-2097152" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-type-alias)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">T</text></g><g id="icon-4194304" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-reference)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">R</text></g><g id="icon-8388608" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-document)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><g stroke="var(--color-icon-text)" fill="none" stroke-width="1.5"><polygon points="6,5 6,19 18,19, 18,10 13,5"></polygon><line x1="9" y1="9" x2="13" y2="9"></line><line x1="9" y1="12" x2="15" y2="12"></line><line x1="9" y1="15" x2="15" y2="15"></line></g></g><g id="icon-folder" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-document)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><g stroke="var(--color-icon-text)" fill="none" stroke-width="1.5"><polygon points="5,5 10,5 12,8 19,8 19,18 5,18"></polygon></g></g><g id="icon-chevronDown" class="tsd-no-select"><path d="M4.93896 8.531L12 15.591L19.061 8.531L16.939 6.409L12 11.349L7.06098 6.409L4.93896 8.531Z" fill="var(--color-icon-text)"></path></g><g id="icon-chevronSmall" class="tsd-no-select"><path d="M1.5 5.50969L8 11.6609L14.5 5.50969L12.5466 3.66086L8 7.96494L3.45341 3.66086L1.5 5.50969Z" fill="var(--color-icon-text)"></path></g><g id="icon-checkbox" class="tsd-no-select"><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></g><g id="icon-menu" class="tsd-no-select"><rect x="1" y="3" width="14" height="2" fill="var(--color-icon-text)"></rect><rect x="1" y="7" width="14" height="2" fill="var(--color-icon-text)"></rect><rect x="1" y="11" width="14" height="2" fill="var(--color-icon-text)"></rect></g><g id="icon-search" class="tsd-no-select"><path d="M15.7824 13.833L12.6666 10.7177C12.5259 10.5771 12.3353 10.499 12.1353 10.499H11.6259C12.4884 9.39596 13.001 8.00859 13.001 6.49937C13.001 2.90909 10.0914 0 6.50048 0C2.90959 0 0 2.90909 0 6.49937C0 10.0896 2.90959 12.9987 6.50048 12.9987C8.00996 12.9987 9.39756 12.4863 10.5008 11.6239V12.1332C10.5008 12.3332 10.5789 12.5238 10.7195 12.6644L13.8354 15.7797C14.1292 16.0734 14.6042 16.0734 14.8948 15.7797L15.7793 14.8954C16.0731 14.6017 16.0731 14.1267 15.7824 13.833ZM6.50048 10.499C4.29094 10.499 2.50018 8.71165 2.50018 6.49937C2.50018 4.29021 4.28781 2.49976 6.50048 2.49976C8.71001 2.49976 10.5008 4.28708 10.5008 6.49937C10.5008 8.70852 8.71314 10.499 6.50048 10.499Z" fill="var(--color-icon-text)"></path></g><g id="icon-anchor" class="tsd-no-select"><g stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M10 14a3.5 3.5 0 0 0 5 0l4 -4a3.5 3.5 0 0 0 -5 -5l-.5 .5"></path><path d="M14 10a3.5 3.5 0 0 0 -5 0l-4 4a3.5 3.5 0 0 0 5 5l.5 -.5"></path></g></g><g id="icon-alertNote" class="tsd-no-select"><path fill="var(--color-alert-note)" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></g><g id="icon-alertTip" class="tsd-no-select"><path fill="var(--color-alert-tip)" d="M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"></path></g><g id="icon-alertImportant" class="tsd-no-select"><path fill="var(--color-alert-important)" d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></g><g id="icon-alertWarning" class="tsd-no-select"><path fill="var(--color-alert-warning)" d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></g><g id="icon-alertCaution" class="tsd-no-select"><path fill="var(--color-alert-caution)" d="M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></g></svg>
2 |
```
--------------------------------------------------------------------------------
/src/memory/manager.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Memory Management Module for DocuMCP
3 | * Implements Issue #46: Memory Management Module
4 | */
5 |
6 | import { JSONLStorage, MemoryEntry } from "./storage.js";
7 | import { EventEmitter } from "events";
8 |
9 | export interface MemoryContext {
10 | projectId: string;
11 | repository?: string;
12 | branch?: string;
13 | user?: string;
14 | session?: string;
15 | }
16 |
17 | export interface MemorySearchOptions {
18 | semantic?: boolean;
19 | fuzzy?: boolean;
20 | sortBy?: "relevance" | "timestamp" | "type";
21 | groupBy?: "type" | "project" | "date";
22 | }
23 |
24 | export class MemoryManager extends EventEmitter {
25 | private storage: JSONLStorage;
26 | private context: MemoryContext | null = null;
27 | private cache: Map<string, MemoryEntry>;
28 | private readonly maxCacheSize = 200; // Reduced cache size for better memory efficiency
29 |
30 | constructor(storageDir?: string) {
31 | super();
32 | this.storage = new JSONLStorage(storageDir);
33 | this.cache = new Map();
34 | }
35 |
36 | async initialize(): Promise<void> {
37 | await this.storage.initialize();
38 | this.emit("initialized");
39 | }
40 |
41 | setContext(context: MemoryContext): void {
42 | this.context = context;
43 | this.emit("context-changed", context);
44 | }
45 |
46 | async remember(
47 | type: MemoryEntry["type"],
48 | data: Record<string, any>,
49 | metadata?: Partial<MemoryEntry["metadata"]>,
50 | ): Promise<MemoryEntry> {
51 | const entry = await this.storage.append({
52 | type,
53 | timestamp: new Date().toISOString(),
54 | data,
55 | metadata: {
56 | ...metadata,
57 | projectId: this.context?.projectId,
58 | repository: this.context?.repository || metadata?.repository,
59 | },
60 | });
61 |
62 | this.addToCache(entry);
63 | this.emit("memory-created", entry);
64 |
65 | return entry;
66 | }
67 |
68 | async recall(id: string): Promise<MemoryEntry | null> {
69 | if (this.cache.has(id)) {
70 | return this.cache.get(id)!;
71 | }
72 |
73 | const entry = await this.storage.get(id);
74 | if (entry) {
75 | this.addToCache(entry);
76 | }
77 |
78 | return entry;
79 | }
80 |
81 | async search(
82 | query: string | Partial<MemoryEntry["metadata"]>,
83 | options?: MemorySearchOptions,
84 | ): Promise<MemoryEntry[]> {
85 | let filter: any = {};
86 |
87 | if (typeof query === "string") {
88 | // Text-based search - search in multiple fields
89 | // Try to match projectId first, then tags
90 | const results: MemoryEntry[] = [];
91 |
92 | // Search by projectId
93 | const projectResults = await this.storage.query({ projectId: query });
94 | results.push(...projectResults);
95 |
96 | // Search by tags (excluding already found entries)
97 | const tagResults = await this.storage.query({ tags: [query] });
98 | const existingIds = new Set(results.map((r) => r.id));
99 | results.push(...tagResults.filter((r) => !existingIds.has(r.id)));
100 |
101 | // Apply sorting and grouping if requested
102 | let finalResults = results;
103 | if (options?.sortBy) {
104 | finalResults = this.sortResults(finalResults, options.sortBy);
105 | }
106 |
107 | if (options?.groupBy) {
108 | return this.groupResults(finalResults, options.groupBy);
109 | }
110 |
111 | return finalResults;
112 | } else {
113 | filter = { ...query };
114 | }
115 |
116 | if (this.context) {
117 | filter.projectId = filter.projectId || this.context.projectId;
118 | filter.repository = filter.repository || this.context.repository;
119 | }
120 |
121 | let results = await this.storage.query(filter);
122 |
123 | if (options?.sortBy) {
124 | results = this.sortResults(results, options.sortBy);
125 | }
126 |
127 | if (options?.groupBy) {
128 | return this.groupResults(results, options.groupBy);
129 | }
130 |
131 | return results;
132 | }
133 |
134 | async update(
135 | id: string,
136 | updates: Partial<MemoryEntry>,
137 | ): Promise<MemoryEntry | null> {
138 | const existing = await this.recall(id);
139 | if (!existing) return null;
140 |
141 | const updated: MemoryEntry = {
142 | ...existing,
143 | ...updates,
144 | id: existing.id,
145 | timestamp: new Date().toISOString(),
146 | };
147 |
148 | await this.storage.delete(id);
149 | const newEntry = await this.storage.append(updated);
150 |
151 | this.cache.delete(id);
152 | this.addToCache(newEntry);
153 |
154 | this.emit("memory-updated", newEntry);
155 | return newEntry;
156 | }
157 |
158 | async forget(id: string): Promise<boolean> {
159 | const result = await this.storage.delete(id);
160 | if (result) {
161 | this.cache.delete(id);
162 | this.emit("memory-deleted", id);
163 | }
164 | return result;
165 | }
166 |
167 | async getRelated(
168 | entry: MemoryEntry,
169 | limit: number = 10,
170 | ): Promise<MemoryEntry[]> {
171 | const related: MemoryEntry[] = [];
172 |
173 | // Find by same project
174 | if (entry.metadata.projectId) {
175 | const projectMemories = await this.search({
176 | projectId: entry.metadata.projectId,
177 | });
178 | related.push(...projectMemories.filter((m: any) => m.id !== entry.id));
179 | }
180 |
181 | // Find by same type
182 | const typeMemories = await this.storage.query({
183 | type: entry.type,
184 | limit: limit * 2,
185 | });
186 | related.push(...typeMemories.filter((m: any) => m.id !== entry.id));
187 |
188 | // Find by overlapping tags
189 | if (entry.metadata.tags && entry.metadata.tags.length > 0) {
190 | const tagMemories = await this.storage.query({
191 | tags: entry.metadata.tags,
192 | limit: limit * 2,
193 | });
194 | related.push(...tagMemories.filter((m: any) => m.id !== entry.id));
195 | }
196 |
197 | // Deduplicate and limit
198 | const uniqueRelated = Array.from(
199 | new Map(related.map((m: any) => [m.id, m])).values(),
200 | ).slice(0, limit);
201 |
202 | return uniqueRelated;
203 | }
204 |
205 | async analyze(timeRange?: { start: string; end: string }): Promise<{
206 | patterns: Record<string, any>;
207 | insights: string[];
208 | statistics: any;
209 | }> {
210 | const stats = await this.storage.getStatistics();
211 | const memories = await this.storage.query({
212 | startDate: timeRange?.start,
213 | endDate: timeRange?.end,
214 | });
215 |
216 | const patterns = this.extractPatterns(memories);
217 | const insights = this.generateInsights(patterns, stats);
218 |
219 | return {
220 | patterns,
221 | insights,
222 | statistics: stats,
223 | };
224 | }
225 |
226 | private extractPatterns(memories: MemoryEntry[]): Record<string, any> {
227 | const patterns: Record<string, any> = {
228 | mostCommonSSG: {},
229 | projectTypes: {},
230 | deploymentSuccess: { success: 0, failed: 0 },
231 | timeDistribution: {},
232 | };
233 |
234 | for (const memory of memories) {
235 | // SSG patterns
236 | if (memory.metadata.ssg) {
237 | patterns.mostCommonSSG[memory.metadata.ssg] =
238 | (patterns.mostCommonSSG[memory.metadata.ssg] || 0) + 1;
239 | }
240 |
241 | // Deployment patterns
242 | if (memory.type === "deployment") {
243 | if (memory.data.status === "success") {
244 | patterns.deploymentSuccess.success++;
245 | } else if (memory.data.status === "failed") {
246 | patterns.deploymentSuccess.failed++;
247 | }
248 | }
249 |
250 | // Time patterns
251 | const hour = new Date(memory.timestamp).getHours();
252 | patterns.timeDistribution[hour] =
253 | (patterns.timeDistribution[hour] || 0) + 1;
254 | }
255 |
256 | return patterns;
257 | }
258 |
259 | private generateInsights(patterns: any, stats: any): string[] {
260 | const insights: string[] = [];
261 |
262 | // SSG preference insight
263 | if (Object.keys(patterns.mostCommonSSG).length > 0) {
264 | const topSSG = Object.entries(patterns.mostCommonSSG).sort(
265 | ([, a]: any, [, b]: any) => b - a,
266 | )[0];
267 | insights.push(
268 | `Most frequently used SSG: ${topSSG[0]} (${topSSG[1]} projects)`,
269 | );
270 | }
271 |
272 | // Deployment success rate
273 | const total =
274 | patterns.deploymentSuccess.success + patterns.deploymentSuccess.failed;
275 | if (total > 0) {
276 | const successRate = (
277 | (patterns.deploymentSuccess.success / total) *
278 | 100
279 | ).toFixed(1);
280 | insights.push(`Deployment success rate: ${successRate}%`);
281 | }
282 |
283 | // Activity patterns
284 | if (Object.keys(patterns.timeDistribution).length > 0) {
285 | const peakHour = Object.entries(patterns.timeDistribution).sort(
286 | ([, a]: any, [, b]: any) => b - a,
287 | )[0];
288 | insights.push(`Peak activity hour: ${peakHour[0]}:00`);
289 | }
290 |
291 | // Storage insights
292 | const sizeMB = (stats.totalSize / 1024 / 1024).toFixed(2);
293 | insights.push(
294 | `Total memory storage: ${sizeMB} MB across ${stats.totalEntries} entries`,
295 | );
296 |
297 | return insights;
298 | }
299 |
300 | private sortResults(
301 | results: MemoryEntry[],
302 | sortBy: "relevance" | "timestamp" | "type",
303 | ): MemoryEntry[] {
304 | switch (sortBy) {
305 | case "timestamp":
306 | return results.sort(
307 | (a, b) =>
308 | new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),
309 | );
310 | case "type":
311 | return results.sort((a, b) => a.type.localeCompare(b.type));
312 | default:
313 | return results;
314 | }
315 | }
316 |
317 | private groupResults(
318 | results: MemoryEntry[],
319 | groupBy: "type" | "project" | "date",
320 | ): any {
321 | const grouped: Record<string, MemoryEntry[]> = {};
322 |
323 | for (const entry of results) {
324 | let key: string;
325 | switch (groupBy) {
326 | case "type":
327 | key = entry.type;
328 | break;
329 | case "project":
330 | key = entry.metadata.projectId || "unknown";
331 | break;
332 | case "date":
333 | key = entry.timestamp.split("T")[0];
334 | break;
335 | default:
336 | key = "all";
337 | }
338 |
339 | if (!grouped[key]) {
340 | grouped[key] = [];
341 | }
342 | grouped[key].push(entry);
343 | }
344 |
345 | return grouped;
346 | }
347 |
348 | private addToCache(entry: MemoryEntry): void {
349 | // More aggressive cache eviction to prevent memory growth
350 | while (this.cache.size >= this.maxCacheSize) {
351 | const firstKey = this.cache.keys().next().value;
352 | if (firstKey) {
353 | this.cache.delete(firstKey);
354 | }
355 | }
356 |
357 | // Store a shallow copy to avoid retaining large objects
358 | const cacheEntry = {
359 | id: entry.id,
360 | timestamp: entry.timestamp,
361 | type: entry.type,
362 | data: entry.data,
363 | metadata: entry.metadata,
364 | tags: entry.tags,
365 | };
366 |
367 | this.cache.set(entry.id, cacheEntry as MemoryEntry);
368 | }
369 |
370 | async export(
371 | format: "json" | "csv" = "json",
372 | projectId?: string,
373 | ): Promise<string> {
374 | const filter = projectId ? { projectId } : {};
375 | const allMemories = await this.storage.query(filter);
376 |
377 | if (format === "json") {
378 | return JSON.stringify(allMemories, null, 2);
379 | } else {
380 | // CSV export
381 | const headers = [
382 | "id",
383 | "timestamp",
384 | "type",
385 | "projectId",
386 | "repository",
387 | "ssg",
388 | ];
389 | const rows = allMemories.map((m: any) => [
390 | m.id,
391 | m.timestamp,
392 | m.type,
393 | m.metadata?.projectId || "",
394 | m.metadata?.repository || "",
395 | m.metadata?.ssg || "",
396 | ]);
397 |
398 | return [headers, ...rows].map((r: any) => r.join(",")).join("\n");
399 | }
400 | }
401 |
402 | async import(data: string, format: "json" | "csv" = "json"): Promise<number> {
403 | let entries: MemoryEntry[] = [];
404 |
405 | if (format === "json") {
406 | entries = JSON.parse(data);
407 | } else {
408 | // CSV import - simplified for now
409 | const lines = data.split("\n");
410 | const headers = lines[0].split(",");
411 |
412 | for (let i = 1; i < lines.length; i++) {
413 | const values = lines[i].split(",");
414 | if (values.length === headers.length) {
415 | entries.push({
416 | id: values[0],
417 | timestamp: values[1],
418 | type: values[2] as MemoryEntry["type"],
419 | data: {},
420 | metadata: {
421 | projectId: values[3],
422 | repository: values[4],
423 | ssg: values[5],
424 | },
425 | });
426 | }
427 | }
428 | }
429 |
430 | let imported = 0;
431 | for (const entry of entries) {
432 | // Use store to preserve the original ID when importing
433 | await this.storage.store(entry);
434 | imported++;
435 | }
436 |
437 | this.emit("import-complete", imported);
438 | return imported;
439 | }
440 |
441 | async cleanup(olderThan?: Date): Promise<number> {
442 | const cutoff = olderThan || new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); // 30 days
443 | const oldMemories = await this.storage.query({
444 | endDate: cutoff.toISOString(),
445 | });
446 |
447 | let deleted = 0;
448 | for (const memory of oldMemories) {
449 | if (await this.storage.delete(memory.id)) {
450 | deleted++;
451 | }
452 | }
453 |
454 | await this.storage.compact();
455 | this.emit("cleanup-complete", deleted);
456 | return deleted;
457 | }
458 |
459 | async close(): Promise<void> {
460 | await this.storage.close();
461 | this.cache.clear();
462 | this.emit("closed");
463 | }
464 |
465 | /**
466 | * Get the storage instance for use with other systems
467 | */
468 | getStorage(): JSONLStorage {
469 | return this.storage;
470 | }
471 | }
472 |
473 | export default MemoryManager;
474 |
```
--------------------------------------------------------------------------------
/tests/tools/analyze-readme.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { describe, it, expect, beforeEach, afterEach } from "@jest/globals";
2 | import { promises as fs } from "fs";
3 | import { join } from "path";
4 | import { analyzeReadme } from "../../src/tools/analyze-readme.js";
5 | import { tmpdir } from "os";
6 |
7 | describe("analyze_readme", () => {
8 | let testDir: string;
9 | let readmePath: string;
10 |
11 | beforeEach(async () => {
12 | // Create temporary test directory
13 | testDir = join(tmpdir(), `test-readme-${Date.now()}`);
14 | await fs.mkdir(testDir, { recursive: true });
15 | readmePath = join(testDir, "README.md");
16 | });
17 |
18 | afterEach(async () => {
19 | // Cleanup test directory
20 | try {
21 | await fs.rm(testDir, { recursive: true, force: true });
22 | } catch {
23 | // Ignore cleanup errors
24 | }
25 | });
26 |
27 | describe("input validation", () => {
28 | it("should require project_path parameter", async () => {
29 | const result = await analyzeReadme({});
30 |
31 | expect(result.success).toBe(false);
32 | expect(result.error?.code).toBe("ANALYSIS_FAILED");
33 | });
34 |
35 | it("should handle non-existent project directory", async () => {
36 | const result = await analyzeReadme({
37 | project_path: "/non/existent/path",
38 | });
39 |
40 | expect(result.success).toBe(false);
41 | expect(result.error?.code).toBe("README_NOT_FOUND");
42 | });
43 | });
44 |
45 | describe("README detection", () => {
46 | it("should find README.md file", async () => {
47 | const readmeContent = `# Test Project\n\n> A simple test project\n\n## Installation\n\n\`\`\`bash\nnpm install\n\`\`\`\n\n## Usage\n\nExample usage here.`;
48 | await fs.writeFile(readmePath, readmeContent);
49 |
50 | const result = await analyzeReadme({
51 | project_path: testDir,
52 | });
53 |
54 | expect(result.success).toBe(true);
55 | expect(result.data?.analysis).toBeDefined();
56 | });
57 |
58 | it("should find alternative README file names", async () => {
59 | const readmeContent = `# Test Project\n\nBasic content`;
60 | await fs.writeFile(join(testDir, "readme.md"), readmeContent);
61 |
62 | const result = await analyzeReadme({
63 | project_path: testDir,
64 | });
65 |
66 | expect(result.success).toBe(true);
67 | });
68 | });
69 |
70 | describe("length analysis", () => {
71 | it("should analyze README length correctly", async () => {
72 | const longReadme = Array(400)
73 | .fill("# Section\n\nContent here.\n")
74 | .join("\n");
75 | await fs.writeFile(readmePath, longReadme);
76 |
77 | const result = await analyzeReadme({
78 | project_path: testDir,
79 | max_length_target: 300,
80 | });
81 |
82 | expect(result.success).toBe(true);
83 | expect(result.data?.analysis.lengthAnalysis.exceedsTarget).toBe(true);
84 | expect(
85 | result.data?.analysis.lengthAnalysis.reductionNeeded,
86 | ).toBeGreaterThan(0);
87 | });
88 |
89 | it("should handle README within target length", async () => {
90 | const shortReadme = `# Project\n\n## Quick Start\n\nInstall and use.`;
91 | await fs.writeFile(readmePath, shortReadme);
92 |
93 | const result = await analyzeReadme({
94 | project_path: testDir,
95 | max_length_target: 300,
96 | });
97 |
98 | expect(result.success).toBe(true);
99 | expect(result.data?.analysis.lengthAnalysis.exceedsTarget).toBe(false);
100 | expect(result.data?.analysis.lengthAnalysis.reductionNeeded).toBe(0);
101 | });
102 | });
103 |
104 | describe("structure analysis", () => {
105 | it("should evaluate scannability score", async () => {
106 | const wellStructuredReadme = `# Project Title
107 |
108 | > Clear description
109 |
110 | ## Installation
111 |
112 | \`\`\`bash
113 | npm install
114 | \`\`\`
115 |
116 | ## Usage
117 |
118 | - Feature 1
119 | - Feature 2
120 | - Feature 3
121 |
122 | ### Advanced Usage
123 |
124 | More details here.
125 |
126 | ## Contributing
127 |
128 | Guidelines here.`;
129 |
130 | await fs.writeFile(readmePath, wellStructuredReadme);
131 |
132 | const result = await analyzeReadme({
133 | project_path: testDir,
134 | });
135 |
136 | expect(result.success).toBe(true);
137 | expect(
138 | result.data?.analysis.structureAnalysis.scannabilityScore,
139 | ).toBeGreaterThan(50);
140 | expect(
141 | result.data?.analysis.structureAnalysis.headingHierarchy.length,
142 | ).toBeGreaterThan(0);
143 | });
144 |
145 | it("should detect poor structure", async () => {
146 | const poorStructure = `ProjectTitle\nSome text without proper headings or spacing.More text.Even more text without breaks.`;
147 | await fs.writeFile(readmePath, poorStructure);
148 |
149 | const result = await analyzeReadme({
150 | project_path: testDir,
151 | });
152 |
153 | expect(result.success).toBe(true);
154 | expect(
155 | result.data?.analysis.structureAnalysis.scannabilityScore,
156 | ).toBeLessThan(50);
157 | });
158 | });
159 |
160 | describe("content analysis", () => {
161 | it("should detect TL;DR section", async () => {
162 | const readmeWithTldr = `# Project\n\n## TL;DR\n\nQuick overview here.\n\n## Details\n\nMore info.`;
163 | await fs.writeFile(readmePath, readmeWithTldr);
164 |
165 | const result = await analyzeReadme({
166 | project_path: testDir,
167 | });
168 |
169 | expect(result.success).toBe(true);
170 | expect(result.data?.analysis.contentAnalysis.hasTldr).toBe(true);
171 | });
172 |
173 | it("should detect quick start section", async () => {
174 | const readmeWithQuickStart = `# Project\n\n## Quick Start\n\nGet started quickly.\n\n## Installation\n\nDetailed setup.`;
175 | await fs.writeFile(readmePath, readmeWithQuickStart);
176 |
177 | const result = await analyzeReadme({
178 | project_path: testDir,
179 | });
180 |
181 | expect(result.success).toBe(true);
182 | expect(result.data?.analysis.contentAnalysis.hasQuickStart).toBe(true);
183 | });
184 |
185 | it("should count code blocks and links", async () => {
186 | const readmeWithCodeAndLinks = `# Project
187 |
188 | ## Installation
189 |
190 | \`\`\`bash
191 | npm install
192 | \`\`\`
193 |
194 | ## Usage
195 |
196 | \`\`\`javascript
197 | const lib = require('lib');
198 | \`\`\`
199 |
200 | See [documentation](https://example.com) and [API reference](https://api.example.com).`;
201 |
202 | await fs.writeFile(readmePath, readmeWithCodeAndLinks);
203 |
204 | const result = await analyzeReadme({
205 | project_path: testDir,
206 | });
207 |
208 | expect(result.success).toBe(true);
209 | expect(result.data?.analysis.contentAnalysis.codeBlockCount).toBe(2);
210 | expect(result.data?.analysis.contentAnalysis.linkCount).toBe(2);
211 | });
212 | });
213 |
214 | describe("community readiness", () => {
215 | it("should detect community files", async () => {
216 | const readmeContent = `# Project\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).`;
217 | await fs.writeFile(readmePath, readmeContent);
218 | await fs.writeFile(
219 | join(testDir, "CONTRIBUTING.md"),
220 | "Contributing guidelines",
221 | );
222 | await fs.writeFile(
223 | join(testDir, "CODE_OF_CONDUCT.md"),
224 | "Code of conduct",
225 | );
226 |
227 | const result = await analyzeReadme({
228 | project_path: testDir,
229 | });
230 |
231 | expect(result.success).toBe(true);
232 | expect(result.data?.analysis.communityReadiness.hasContributing).toBe(
233 | true,
234 | );
235 | expect(result.data?.analysis.communityReadiness.hasCodeOfConduct).toBe(
236 | true,
237 | );
238 | });
239 |
240 | it("should count badges", async () => {
241 | const readmeWithBadges = `# Project
242 |
243 | [](https://travis-ci.org/user/repo)
244 | [](https://badge.fury.io/js/package)
245 |
246 | Description here.`;
247 |
248 | await fs.writeFile(readmePath, readmeWithBadges);
249 |
250 | const result = await analyzeReadme({
251 | project_path: testDir,
252 | });
253 |
254 | expect(result.success).toBe(true);
255 | expect(result.data?.analysis.communityReadiness.badgeCount).toBe(2);
256 | });
257 | });
258 |
259 | describe("optimization opportunities", () => {
260 | it("should identify length reduction opportunities", async () => {
261 | const longReadme = Array(500)
262 | .fill("# Section\n\nLong content here that exceeds target length.\n")
263 | .join("\n");
264 | await fs.writeFile(readmePath, longReadme);
265 |
266 | const result = await analyzeReadme({
267 | project_path: testDir,
268 | max_length_target: 200,
269 | optimization_level: "aggressive",
270 | });
271 |
272 | expect(result.success).toBe(true);
273 | expect(
274 | result.data?.analysis.optimizationOpportunities.length,
275 | ).toBeGreaterThan(0);
276 | expect(
277 | result.data?.analysis.optimizationOpportunities.some(
278 | (op) => op.type === "length_reduction",
279 | ),
280 | ).toBe(true);
281 | });
282 |
283 | it("should identify content enhancement opportunities", async () => {
284 | const basicReadme = `# Project\n\nBasic description.\n\n## Installation\n\nnpm install`;
285 | await fs.writeFile(readmePath, basicReadme);
286 |
287 | const result = await analyzeReadme({
288 | project_path: testDir,
289 | target_audience: "community_contributors",
290 | });
291 |
292 | expect(result.success).toBe(true);
293 | expect(
294 | result.data?.analysis.optimizationOpportunities.some(
295 | (op) => op.type === "content_enhancement",
296 | ),
297 | ).toBe(true);
298 | });
299 | });
300 |
301 | describe("scoring system", () => {
302 | it("should calculate overall score", async () => {
303 | const goodReadme = `# Excellent Project
304 |
305 | > Clear, concise description of what this project does
306 |
307 | [](https://travis-ci.org/user/repo)
308 | [](https://opensource.org/licenses/MIT)
309 |
310 | ## TL;DR
311 |
312 | This project solves X problem for Y users. Perfect for Z use cases.
313 |
314 | ## Quick Start
315 |
316 | \`\`\`bash
317 | npm install excellent-project
318 | \`\`\`
319 |
320 | \`\`\`javascript
321 | const project = require('excellent-project');
322 | project.doSomething();
323 | \`\`\`
324 |
325 | ## Prerequisites
326 |
327 | - Node.js 16+
328 | - npm or yarn
329 |
330 | ## Usage
331 |
332 | Detailed usage examples here.
333 |
334 | ## Contributing
335 |
336 | See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
337 |
338 | ## License
339 |
340 | MIT © Author`;
341 |
342 | await fs.writeFile(readmePath, goodReadme);
343 | await fs.writeFile(join(testDir, "CONTRIBUTING.md"), "Guidelines");
344 |
345 | const result = await analyzeReadme({
346 | project_path: testDir,
347 | });
348 |
349 | expect(result.success).toBe(true);
350 | expect(result.data?.analysis.overallScore).toBeGreaterThan(70);
351 | });
352 |
353 | it("should provide lower score for poor README", async () => {
354 | const poorReadme = `ProjectName\nSome description\nInstall it\nUse it`;
355 | await fs.writeFile(readmePath, poorReadme);
356 |
357 | const result = await analyzeReadme({
358 | project_path: testDir,
359 | });
360 |
361 | expect(result.success).toBe(true);
362 | expect(result.data?.analysis.overallScore).toBeLessThan(50);
363 | });
364 | });
365 |
366 | describe("recommendations and next steps", () => {
367 | it("should provide relevant recommendations", async () => {
368 | const basicReadme = `# Project\n\nDescription`;
369 | await fs.writeFile(readmePath, basicReadme);
370 |
371 | const result = await analyzeReadme({
372 | project_path: testDir,
373 | target_audience: "community_contributors",
374 | optimization_level: "moderate",
375 | });
376 |
377 | expect(result.success).toBe(true);
378 | expect(result.data?.analysis.recommendations.length).toBeGreaterThan(0);
379 | expect(result.data?.nextSteps.length).toBeGreaterThan(0);
380 | });
381 |
382 | it("should tailor recommendations to target audience", async () => {
383 | const readmeContent = `# Enterprise Tool\n\nBasic description`;
384 | await fs.writeFile(readmePath, readmeContent);
385 |
386 | const result = await analyzeReadme({
387 | project_path: testDir,
388 | target_audience: "enterprise_users",
389 | });
390 |
391 | expect(result.success).toBe(true);
392 | expect(
393 | result.data?.analysis.recommendations.some(
394 | (rec) =>
395 | rec.includes("enterprise") ||
396 | rec.includes("security") ||
397 | rec.includes("support"),
398 | ),
399 | ).toBe(true);
400 | });
401 | });
402 |
403 | describe("project context detection", () => {
404 | it("should detect JavaScript project", async () => {
405 | const readmeContent = `# JS Project\n\nA JavaScript project`;
406 | await fs.writeFile(readmePath, readmeContent);
407 | await fs.writeFile(join(testDir, "package.json"), '{"name": "test"}');
408 |
409 | const result = await analyzeReadme({
410 | project_path: testDir,
411 | });
412 |
413 | expect(result.success).toBe(true);
414 | // Should analyze successfully with project context
415 | expect(result.data?.analysis).toBeDefined();
416 | });
417 |
418 | it("should handle projects without specific type indicators", async () => {
419 | const readmeContent = `# Generic Project\n\nSome project`;
420 | await fs.writeFile(readmePath, readmeContent);
421 |
422 | const result = await analyzeReadme({
423 | project_path: testDir,
424 | });
425 |
426 | expect(result.success).toBe(true);
427 | expect(result.data?.analysis).toBeDefined();
428 | });
429 | });
430 | });
431 |
```
--------------------------------------------------------------------------------
/docs/sitemap.xml:
--------------------------------------------------------------------------------
```
1 | <?xml version="1.0" encoding="UTF-8"?>
2 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
3 | <url>
4 | <loc>https://tosin2013.github.io/documcp/</loc>
5 | <lastmod>2025-10-01</lastmod>
6 | <changefreq>weekly</changefreq>
7 | <priority>1.0</priority>
8 | </url>
9 | <url>
10 | <loc>https://tosin2013.github.io/documcp/tutorials/</loc>
11 | <lastmod>2025-10-02</lastmod>
12 | <changefreq>monthly</changefreq>
13 | <priority>1.0</priority>
14 | </url>
15 | <url>
16 | <loc>https://tosin2013.github.io/documcp/tutorials/development-setup.html</loc>
17 | <lastmod>2025-10-01</lastmod>
18 | <changefreq>monthly</changefreq>
19 | <priority>1.0</priority>
20 | </url>
21 | <url>
22 | <loc>https://tosin2013.github.io/documcp/tutorials/environment-setup.html</loc>
23 | <lastmod>2025-10-02</lastmod>
24 | <changefreq>monthly</changefreq>
25 | <priority>1.0</priority>
26 | </url>
27 | <url>
28 | <loc>https://tosin2013.github.io/documcp/tutorials/first-deployment.html</loc>
29 | <lastmod>2025-10-01</lastmod>
30 | <changefreq>monthly</changefreq>
31 | <priority>1.0</priority>
32 | </url>
33 | <url>
34 | <loc>https://tosin2013.github.io/documcp/tutorials/getting-started.html</loc>
35 | <lastmod>2025-10-12</lastmod>
36 | <changefreq>monthly</changefreq>
37 | <priority>1.0</priority>
38 | </url>
39 | <url>
40 | <loc>https://tosin2013.github.io/documcp/tutorials/memory-workflows.html</loc>
41 | <lastmod>2025-10-02</lastmod>
42 | <changefreq>monthly</changefreq>
43 | <priority>1.0</priority>
44 | </url>
45 | <url>
46 | <loc>https://tosin2013.github.io/documcp/tutorials/user-onboarding.html</loc>
47 | <lastmod>2025-10-12</lastmod>
48 | <changefreq>monthly</changefreq>
49 | <priority>1.0</priority>
50 | </url>
51 | <url>
52 | <loc>https://tosin2013.github.io/documcp/how-to/</loc>
53 | <lastmod>2025-10-02</lastmod>
54 | <changefreq>monthly</changefreq>
55 | <priority>0.9</priority>
56 | </url>
57 | <url>
58 | <loc>https://tosin2013.github.io/documcp/how-to/analytics-setup.html</loc>
59 | <lastmod>2025-10-02</lastmod>
60 | <changefreq>monthly</changefreq>
61 | <priority>0.9</priority>
62 | </url>
63 | <url>
64 | <loc>https://tosin2013.github.io/documcp/how-to/custom-domains.html</loc>
65 | <lastmod>2025-10-02</lastmod>
66 | <changefreq>monthly</changefreq>
67 | <priority>0.9</priority>
68 | </url>
69 | <url>
70 | <loc>https://tosin2013.github.io/documcp/how-to/github-pages-deployment.html</loc>
71 | <lastmod>2025-10-02</lastmod>
72 | <changefreq>monthly</changefreq>
73 | <priority>0.9</priority>
74 | </url>
75 | <url>
76 | <loc>https://tosin2013.github.io/documcp/how-to/local-testing.html</loc>
77 | <lastmod>2025-10-12</lastmod>
78 | <changefreq>monthly</changefreq>
79 | <priority>0.9</priority>
80 | </url>
81 | <url>
82 | <loc>https://tosin2013.github.io/documcp/how-to/performance-optimization.html</loc>
83 | <lastmod>2025-10-02</lastmod>
84 | <changefreq>monthly</changefreq>
85 | <priority>0.9</priority>
86 | </url>
87 | <url>
88 | <loc>https://tosin2013.github.io/documcp/how-to/prompting-guide.html</loc>
89 | <lastmod>2025-10-12</lastmod>
90 | <changefreq>monthly</changefreq>
91 | <priority>0.9</priority>
92 | </url>
93 | <url>
94 | <loc>https://tosin2013.github.io/documcp/how-to/repository-analysis.html</loc>
95 | <lastmod>2025-09-30</lastmod>
96 | <changefreq>monthly</changefreq>
97 | <priority>0.9</priority>
98 | </url>
99 | <url>
100 | <loc>https://tosin2013.github.io/documcp/how-to/seo-optimization.html</loc>
101 | <lastmod>2025-10-02</lastmod>
102 | <changefreq>monthly</changefreq>
103 | <priority>0.9</priority>
104 | </url>
105 | <url>
106 | <loc>https://tosin2013.github.io/documcp/how-to/site-monitoring.html</loc>
107 | <lastmod>2025-10-02</lastmod>
108 | <changefreq>monthly</changefreq>
109 | <priority>0.9</priority>
110 | </url>
111 | <url>
112 | <loc>https://tosin2013.github.io/documcp/how-to/troubleshooting.html</loc>
113 | <lastmod>2025-10-01</lastmod>
114 | <changefreq>monthly</changefreq>
115 | <priority>0.9</priority>
116 | </url>
117 | <url>
118 | <loc>https://tosin2013.github.io/documcp/how-to/usage-examples.html</loc>
119 | <lastmod>2025-10-02</lastmod>
120 | <changefreq>monthly</changefreq>
121 | <priority>0.9</priority>
122 | </url>
123 | <url>
124 | <loc>https://tosin2013.github.io/documcp/research/domain-1-mcp-architecture/</loc>
125 | <lastmod>2025-10-02</lastmod>
126 | <changefreq>weekly</changefreq>
127 | <priority>0.9</priority>
128 | </url>
129 | <url>
130 | <loc>https://tosin2013.github.io/documcp/research/domain-3-ssg-recommendation/</loc>
131 | <lastmod>2025-10-02</lastmod>
132 | <changefreq>weekly</changefreq>
133 | <priority>0.9</priority>
134 | </url>
135 | <url>
136 | <loc>https://tosin2013.github.io/documcp/research/domain-5-github-deployment/</loc>
137 | <lastmod>2025-10-02</lastmod>
138 | <changefreq>weekly</changefreq>
139 | <priority>0.9</priority>
140 | </url>
141 | <url>
142 | <loc>https://tosin2013.github.io/documcp/adrs/006-mcp-tools-api-design.html</loc>
143 | <lastmod>2025-10-01</lastmod>
144 | <changefreq>weekly</changefreq>
145 | <priority>0.8</priority>
146 | </url>
147 | <url>
148 | <loc>https://tosin2013.github.io/documcp/api/</loc>
149 | <lastmod>2025-10-12</lastmod>
150 | <changefreq>weekly</changefreq>
151 | <priority>0.8</priority>
152 | </url>
153 | <url>
154 | <loc>https://tosin2013.github.io/documcp/api/hierarchy.html</loc>
155 | <lastmod>2025-10-12</lastmod>
156 | <changefreq>weekly</changefreq>
157 | <priority>0.8</priority>
158 | </url>
159 | <url>
160 | <loc>https://tosin2013.github.io/documcp/api/modules.html</loc>
161 | <lastmod>2025-10-12</lastmod>
162 | <changefreq>weekly</changefreq>
163 | <priority>0.8</priority>
164 | </url>
165 | <url>
166 | <loc>https://tosin2013.github.io/documcp/api/variables/TOOLS.html</loc>
167 | <lastmod>2025-10-12</lastmod>
168 | <changefreq>weekly</changefreq>
169 | <priority>0.8</priority>
170 | </url>
171 | <url>
172 | <loc>https://tosin2013.github.io/documcp/reference/</loc>
173 | <lastmod>2025-10-12</lastmod>
174 | <changefreq>weekly</changefreq>
175 | <priority>0.8</priority>
176 | </url>
177 | <url>
178 | <loc>https://tosin2013.github.io/documcp/reference/api-overview.html</loc>
179 | <lastmod>2025-10-12</lastmod>
180 | <changefreq>weekly</changefreq>
181 | <priority>0.8</priority>
182 | </url>
183 | <url>
184 | <loc>https://tosin2013.github.io/documcp/reference/cli.html</loc>
185 | <lastmod>2025-10-01</lastmod>
186 | <changefreq>weekly</changefreq>
187 | <priority>0.8</priority>
188 | </url>
189 | <url>
190 | <loc>https://tosin2013.github.io/documcp/reference/configuration.html</loc>
191 | <lastmod>2025-10-01</lastmod>
192 | <changefreq>weekly</changefreq>
193 | <priority>0.8</priority>
194 | </url>
195 | <url>
196 | <loc>https://tosin2013.github.io/documcp/reference/mcp-tools.html</loc>
197 | <lastmod>2025-09-30</lastmod>
198 | <changefreq>weekly</changefreq>
199 | <priority>0.8</priority>
200 | </url>
201 | <url>
202 | <loc>https://tosin2013.github.io/documcp/reference/prompt-templates.html</loc>
203 | <lastmod>2025-09-30</lastmod>
204 | <changefreq>weekly</changefreq>
205 | <priority>0.8</priority>
206 | </url>
207 | <url>
208 | <loc>https://tosin2013.github.io/documcp/research/domain-6-api-design/README.html</loc>
209 | <lastmod>2025-10-02</lastmod>
210 | <changefreq>weekly</changefreq>
211 | <priority>0.8</priority>
212 | </url>
213 | <url>
214 | <loc>https://tosin2013.github.io/documcp/explanation/</loc>
215 | <lastmod>2025-10-12</lastmod>
216 | <changefreq>monthly</changefreq>
217 | <priority>0.7</priority>
218 | </url>
219 | <url>
220 | <loc>https://tosin2013.github.io/documcp/explanation/architecture.html</loc>
221 | <lastmod>2025-10-01</lastmod>
222 | <changefreq>monthly</changefreq>
223 | <priority>0.7</priority>
224 | </url>
225 | <url>
226 | <loc>https://tosin2013.github.io/documcp/adrs/001-mcp-server-architecture.html</loc>
227 | <lastmod>2025-10-01</lastmod>
228 | <changefreq>monthly</changefreq>
229 | <priority>0.5</priority>
230 | </url>
231 | <url>
232 | <loc>https://tosin2013.github.io/documcp/adrs/002-repository-analysis-engine.html</loc>
233 | <lastmod>2025-10-02</lastmod>
234 | <changefreq>monthly</changefreq>
235 | <priority>0.5</priority>
236 | </url>
237 | <url>
238 | <loc>https://tosin2013.github.io/documcp/adrs/003-static-site-generator-recommendation-engine.html</loc>
239 | <lastmod>2025-10-02</lastmod>
240 | <changefreq>monthly</changefreq>
241 | <priority>0.5</priority>
242 | </url>
243 | <url>
244 | <loc>https://tosin2013.github.io/documcp/adrs/004-diataxis-framework-integration.html</loc>
245 | <lastmod>2025-10-02</lastmod>
246 | <changefreq>monthly</changefreq>
247 | <priority>0.5</priority>
248 | </url>
249 | <url>
250 | <loc>https://tosin2013.github.io/documcp/adrs/005-github-pages-deployment-automation.html</loc>
251 | <lastmod>2025-10-02</lastmod>
252 | <changefreq>monthly</changefreq>
253 | <priority>0.5</priority>
254 | </url>
255 | <url>
256 | <loc>https://tosin2013.github.io/documcp/adrs/007-mcp-prompts-and-resources-integration.html</loc>
257 | <lastmod>2025-10-02</lastmod>
258 | <changefreq>monthly</changefreq>
259 | <priority>0.5</priority>
260 | </url>
261 | <url>
262 | <loc>https://tosin2013.github.io/documcp/adrs/008-intelligent-content-population-engine.html</loc>
263 | <lastmod>2025-10-02</lastmod>
264 | <changefreq>monthly</changefreq>
265 | <priority>0.5</priority>
266 | </url>
267 | <url>
268 | <loc>https://tosin2013.github.io/documcp/adrs/009-content-accuracy-validation-framework.html</loc>
269 | <lastmod>2025-10-02</lastmod>
270 | <changefreq>monthly</changefreq>
271 | <priority>0.5</priority>
272 | </url>
273 | <url>
274 | <loc>https://tosin2013.github.io/documcp/adrs/010-mcp-resource-pattern-redesign.html</loc>
275 | <lastmod>2025-10-09</lastmod>
276 | <changefreq>monthly</changefreq>
277 | <priority>0.5</priority>
278 | </url>
279 | <url>
280 | <loc>https://tosin2013.github.io/documcp/adrs/README.html</loc>
281 | <lastmod>2025-10-01</lastmod>
282 | <changefreq>monthly</changefreq>
283 | <priority>0.5</priority>
284 | </url>
285 | <url>
286 | <loc>https://tosin2013.github.io/documcp/development/MCP_INSPECTOR_TESTING.html</loc>
287 | <lastmod>2025-10-09</lastmod>
288 | <changefreq>monthly</changefreq>
289 | <priority>0.5</priority>
290 | </url>
291 | <url>
292 | <loc>https://tosin2013.github.io/documcp/guides/link-validation.html</loc>
293 | <lastmod>2025-10-12</lastmod>
294 | <changefreq>monthly</changefreq>
295 | <priority>0.5</priority>
296 | </url>
297 | <url>
298 | <loc>https://tosin2013.github.io/documcp/guides/playwright-integration.html</loc>
299 | <lastmod>2025-10-04</lastmod>
300 | <changefreq>monthly</changefreq>
301 | <priority>0.5</priority>
302 | </url>
303 | <url>
304 | <loc>https://tosin2013.github.io/documcp/guides/playwright-testing-workflow.html</loc>
305 | <lastmod>2025-10-12</lastmod>
306 | <changefreq>monthly</changefreq>
307 | <priority>0.5</priority>
308 | </url>
309 | <url>
310 | <loc>https://tosin2013.github.io/documcp/phase-2-intelligence.html</loc>
311 | <lastmod>2025-10-02</lastmod>
312 | <changefreq>monthly</changefreq>
313 | <priority>0.5</priority>
314 | </url>
315 | <url>
316 | <loc>https://tosin2013.github.io/documcp/research/cross-domain-integration/README.html</loc>
317 | <lastmod>2025-10-02</lastmod>
318 | <changefreq>monthly</changefreq>
319 | <priority>0.5</priority>
320 | </url>
321 | <url>
322 | <loc>https://tosin2013.github.io/documcp/research/domain-1-mcp-architecture/mcp-performance-research.html</loc>
323 | <lastmod>2025-10-01</lastmod>
324 | <changefreq>monthly</changefreq>
325 | <priority>0.5</priority>
326 | </url>
327 | <url>
328 | <loc>https://tosin2013.github.io/documcp/research/domain-2-repository-analysis/README.html</loc>
329 | <lastmod>2025-10-02</lastmod>
330 | <changefreq>monthly</changefreq>
331 | <priority>0.5</priority>
332 | </url>
333 | <url>
334 | <loc>https://tosin2013.github.io/documcp/research/domain-3-ssg-recommendation/ssg-performance-analysis.html</loc>
335 | <lastmod>2025-10-01</lastmod>
336 | <changefreq>monthly</changefreq>
337 | <priority>0.5</priority>
338 | </url>
339 | <url>
340 | <loc>https://tosin2013.github.io/documcp/research/domain-4-diataxis-integration/README.html</loc>
341 | <lastmod>2025-10-02</lastmod>
342 | <changefreq>monthly</changefreq>
343 | <priority>0.5</priority>
344 | </url>
345 | <url>
346 | <loc>https://tosin2013.github.io/documcp/research/domain-5-github-deployment/github-pages-security-analysis.html</loc>
347 | <lastmod>2025-10-01</lastmod>
348 | <changefreq>monthly</changefreq>
349 | <priority>0.5</priority>
350 | </url>
351 | <url>
352 | <loc>https://tosin2013.github.io/documcp/research/README.html</loc>
353 | <lastmod>2025-10-01</lastmod>
354 | <changefreq>monthly</changefreq>
355 | <priority>0.5</priority>
356 | </url>
357 | <url>
358 | <loc>https://tosin2013.github.io/documcp/research/research-integration-summary-2025-01-14.html</loc>
359 | <lastmod>2025-10-01</lastmod>
360 | <changefreq>monthly</changefreq>
361 | <priority>0.5</priority>
362 | </url>
363 | <url>
364 | <loc>https://tosin2013.github.io/documcp/research/research-progress-template.html</loc>
365 | <lastmod>2025-10-01</lastmod>
366 | <changefreq>monthly</changefreq>
367 | <priority>0.5</priority>
368 | </url>
369 | <url>
370 | <loc>https://tosin2013.github.io/documcp/research/research-questions-2025-01-14.html</loc>
371 | <lastmod>2025-10-01</lastmod>
372 | <changefreq>monthly</changefreq>
373 | <priority>0.5</priority>
374 | </url>
375 | </urlset>
376 |
```
--------------------------------------------------------------------------------
/docs/reference/configuration.md:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | documcp:
3 | last_updated: "2025-11-20T00:46:21.960Z"
4 | last_validated: "2025-11-20T00:46:21.960Z"
5 | auto_updated: false
6 | update_frequency: monthly
7 | ---
8 |
9 | # Configuration Options
10 |
11 | This reference guide covers all configuration options available in DocuMCP and the static site generators it supports.
12 |
13 | ## DocuMCP Configuration
14 |
15 | ### Environment Variables
16 |
17 | DocuMCP supports the following environment variables:
18 |
19 | | Variable | Default | Description |
20 | | --------------------- | ----------------- | ----------------------------------- |
21 | | `DOCUMCP_STORAGE_DIR` | `.documcp/memory` | Directory for memory system storage |
22 | | `DEBUG` | `false` | Enable debug logging |
23 | | `NODE_ENV` | `development` | Node.js environment |
24 |
25 | ### Memory System Configuration
26 |
27 | The memory system stores analysis results and learning patterns:
28 |
29 | ```bash
30 | # Default storage location (relative to project)
31 | .documcp/memory/
32 | ├── analysis/ # Repository analysis results
33 | ├── recommendations/ # SSG recommendations
34 | ├── patterns/ # Learning patterns
35 | └── metadata.json # System metadata
36 | ```
37 |
38 | #### Memory Cleanup Options
39 |
40 | ```javascript
41 | // Cleanup configuration
42 | {
43 | "daysToKeep": 30, // Days to retain memories
44 | "maxEntries": 1000, // Maximum memory entries
45 | "compressionEnabled": true
46 | }
47 | ```
48 |
49 | ## Static Site Generator Configurations
50 |
51 | ### Jekyll Configuration
52 |
53 | **\_config.yml:**
54 |
55 | ```yaml
56 | title: "Your Documentation Site"
57 | description: "Project documentation"
58 | baseurl: "/repository-name"
59 | url: "https://username.github.io"
60 |
61 | markdown: kramdown
62 | highlighter: rouge
63 | theme: minima
64 |
65 | plugins:
66 | - jekyll-feed
67 | - jekyll-sitemap
68 | - jekyll-seo-tag
69 |
70 | collections:
71 | tutorials:
72 | output: true
73 | permalink: /:collection/:name/
74 | how-to-guides:
75 | output: true
76 | permalink: /:collection/:name/
77 |
78 | defaults:
79 | - scope:
80 | path: ""
81 | values:
82 | layout: "default"
83 | - scope:
84 | path: "_tutorials"
85 | values:
86 | layout: "tutorial"
87 | ```
88 |
89 | **Gemfile:**
90 |
91 | ```ruby
92 | source 'https://rubygems.org'
93 |
94 | gem 'jekyll', '~> 4.3.0'
95 | gem 'jekyll-feed', '~> 0.17'
96 | gem 'jekyll-sitemap', '~> 1.4'
97 | gem 'jekyll-seo-tag', '~> 2.8'
98 | gem 'minima', '~> 2.5'
99 |
100 | group :jekyll_plugins do
101 | gem 'jekyll-timeago', '~> 0.13.1'
102 | end
103 | ```
104 |
105 | ### Hugo Configuration
106 |
107 | **config.yml:**
108 |
109 | ```yaml
110 | baseURL: "https://username.github.io/repository-name"
111 | languageCode: "en-us"
112 | title: "Documentation Site"
113 | theme: "docsy"
114 |
115 | params:
116 | github_repo: "https://github.com/username/repository"
117 | github_branch: "main"
118 | edit_page: true
119 | search:
120 | enabled: true
121 |
122 | menu:
123 | main:
124 | - name: "Tutorials"
125 | url: "/tutorials/"
126 | weight: 10
127 | - name: "How-to Guides"
128 | url: "/how-to/"
129 | weight: 20
130 | - name: "Reference"
131 | url: "/reference/"
132 | weight: 30
133 | - name: "Explanation"
134 | url: "/explanation/"
135 | weight: 40
136 |
137 | markup:
138 | goldmark:
139 | renderer:
140 | unsafe: true
141 | highlight:
142 | style: github
143 | lineNos: true
144 | codeFences: true
145 |
146 | security:
147 | funcs:
148 | getenv:
149 | - ^HUGO_
150 | - ^CI$
151 | ```
152 |
153 | **go.mod:**
154 |
155 | ```go
156 | module github.com/username/repository
157 |
158 | go 1.19
159 |
160 | require (
161 | github.com/google/docsy v0.6.0 // indirect
162 | github.com/google/docsy/dependencies v0.6.0 // indirect
163 | )
164 | ```
165 |
166 | ### Docusaurus Configuration
167 |
168 | **docusaurus.config.js:**
169 |
170 | For GitHub Pages deployment, ensure you configure `organizationName`, `projectName`, and `deploymentBranch`:
171 |
172 | ```javascript
173 | const config = {
174 | title: "Documentation Site",
175 | tagline: "Comprehensive project documentation",
176 | url: "https://yourusername.github.io", // Your GitHub Pages URL
177 | baseUrl: "/repository-name/", // Repository name (or "/" for user/organization pages)
178 | organizationName: "yourusername", // GitHub username or organization
179 | projectName: "repository-name", // Repository name
180 | deploymentBranch: "gh-pages", // Branch for deployment (default: gh-pages)
181 | trailingSlash: false, // Set to true if using trailing slashes
182 |
183 | onBrokenLinks: "throw",
184 | onBrokenMarkdownLinks: "warn",
185 |
186 | i18n: {
187 | defaultLocale: "en",
188 | locales: ["en"],
189 | },
190 |
191 | presets: [
192 | [
193 | "classic",
194 | {
195 | docs: {
196 | routeBasePath: "/",
197 | sidebarPath: require.resolve("./sidebars.js"),
198 | editUrl: "https://github.com/username/repository/tree/main/",
199 | },
200 | theme: {
201 | customCss: require.resolve("./src/css/custom.css"),
202 | },
203 | gtag: {
204 | trackingID: "G-XXXXXXXXXX",
205 | anonymizeIP: true,
206 | },
207 | },
208 | ],
209 | ],
210 |
211 | themeConfig: {
212 | navbar: {
213 | title: "Documentation",
214 | items: [
215 | {
216 | type: "doc",
217 | docId: "tutorials/index",
218 | position: "left",
219 | label: "Tutorials",
220 | },
221 | {
222 | type: "doc",
223 | docId: "how-to/index",
224 | position: "left",
225 | label: "How-to",
226 | },
227 | {
228 | type: "doc",
229 | docId: "reference/index",
230 | position: "left",
231 | label: "Reference",
232 | },
233 | {
234 | href: "https://github.com/username/repository",
235 | label: "GitHub",
236 | position: "right",
237 | },
238 | ],
239 | },
240 | footer: {
241 | style: "dark",
242 | copyright: `Copyright © ${new Date().getFullYear()} Your Project Name.`,
243 | },
244 | prism: {
245 | theme: require("prism-react-renderer/themes/github"),
246 | darkTheme: require("prism-react-renderer/themes/dracula"),
247 | },
248 | },
249 | };
250 |
251 | module.exports = config;
252 | ```
253 |
254 | **sidebars.js:**
255 |
256 | ```javascript
257 | const sidebars = {
258 | tutorialSidebar: [
259 | "index",
260 | {
261 | type: "category",
262 | label: "Tutorials",
263 | items: [
264 | "tutorials/getting-started",
265 | "tutorials/first-deployment",
266 | "tutorials/development-setup",
267 | ],
268 | },
269 | {
270 | type: "category",
271 | label: "How-to Guides",
272 | items: [
273 | "how-to/prompting-guide",
274 | "how-to/repository-analysis",
275 | "how-to/github-pages-deployment",
276 | "how-to/troubleshooting",
277 | ],
278 | },
279 | {
280 | type: "category",
281 | label: "Reference",
282 | items: [
283 | "reference/mcp-tools",
284 | "reference/configuration",
285 | "reference/cli",
286 | ],
287 | },
288 | ],
289 | };
290 |
291 | module.exports = sidebars;
292 | ```
293 |
294 | ### MkDocs Configuration
295 |
296 | **mkdocs.yml:**
297 |
298 | ```yaml
299 | site_name: Documentation Site
300 | site_url: https://username.github.io/repository-name
301 | site_description: Comprehensive project documentation
302 |
303 | repo_name: username/repository
304 | repo_url: https://github.com/username/repository
305 | edit_uri: edit/main/docs/
306 |
307 | theme:
308 | name: material
309 | palette:
310 | - scheme: default
311 | primary: blue
312 | accent: blue
313 | toggle:
314 | icon: material/brightness-7
315 | name: Switch to dark mode
316 | - scheme: slate
317 | primary: blue
318 | accent: blue
319 | toggle:
320 | icon: material/brightness-4
321 | name: Switch to light mode
322 | features:
323 | - navigation.tabs
324 | - navigation.sections
325 | - navigation.expand
326 | - navigation.top
327 | - search.highlight
328 | - content.code.copy
329 |
330 | nav:
331 | - Home: index.md
332 | - Tutorials:
333 | - tutorials/index.md
334 | - Getting Started: tutorials/getting-started.md
335 | - First Deployment: tutorials/first-deployment.md
336 | - How-to Guides:
337 | - how-to/index.md
338 | - Prompting Guide: how-to/prompting-guide.md
339 | - Repository Analysis: how-to/repository-analysis.md
340 | - Reference:
341 | - reference/index.md
342 | - MCP Tools: reference/mcp-tools.md
343 | - Configuration: reference/configuration.md
344 | - Explanation:
345 | - explanation/index.md
346 | - Architecture: explanation/architecture.md
347 |
348 | plugins:
349 | - search
350 | - git-revision-date-localized:
351 | enable_creation_date: true
352 |
353 | markdown_extensions:
354 | - pymdownx.highlight:
355 | anchor_linenums: true
356 | - pymdownx.inlinehilite
357 | - pymdownx.snippets
358 | - pymdownx.superfences
359 | - admonition
360 | - pymdownx.details
361 | - pymdownx.tabbed:
362 | alternate_style: true
363 | - attr_list
364 | - md_in_html
365 |
366 | extra:
367 | social:
368 | - icon: fontawesome/brands/github
369 | link: https://github.com/username/repository
370 | ```
371 |
372 | **requirements.txt:**
373 |
374 | ```txt
375 | mkdocs>=1.5.0
376 | mkdocs-material>=9.0.0
377 | mkdocs-git-revision-date-localized-plugin>=1.2.0
378 | ```
379 |
380 | ### Eleventy Configuration
381 |
382 | **.eleventy.js:**
383 |
384 | ```javascript
385 | const { EleventyHtmlBasePlugin } = require("@11ty/eleventy");
386 | const markdownIt = require("markdown-it");
387 | const markdownItAnchor = require("markdown-it-anchor");
388 |
389 | module.exports = function (eleventyConfig) {
390 | // Add plugins
391 | eleventyConfig.addPlugin(EleventyHtmlBasePlugin);
392 |
393 | // Configure Markdown
394 | let markdownLibrary = markdownIt({
395 | html: true,
396 | breaks: true,
397 | linkify: true,
398 | }).use(markdownItAnchor, {
399 | permalink: markdownItAnchor.permalink.ariaHidden({
400 | placement: "after",
401 | class: "direct-link",
402 | symbol: "#",
403 | }),
404 | level: [1, 2, 3, 4],
405 | slugify: eleventyConfig.getFilter("slug"),
406 | });
407 |
408 | eleventyConfig.setLibrary("md", markdownLibrary);
409 |
410 | // Copy static files
411 | eleventyConfig.addPassthroughCopy("src/assets");
412 | eleventyConfig.addPassthroughCopy("src/css");
413 |
414 | // Collections for Diataxis structure
415 | eleventyConfig.addCollection("tutorials", function (collection) {
416 | return collection.getFilteredByGlob("src/tutorials/*.md");
417 | });
418 |
419 | eleventyConfig.addCollection("howto", function (collection) {
420 | return collection.getFilteredByGlob("src/how-to/*.md");
421 | });
422 |
423 | eleventyConfig.addCollection("reference", function (collection) {
424 | return collection.getFilteredByGlob("src/reference/*.md");
425 | });
426 |
427 | eleventyConfig.addCollection("explanation", function (collection) {
428 | return collection.getFilteredByGlob("src/explanation/*.md");
429 | });
430 |
431 | return {
432 | dir: {
433 | input: "src",
434 | output: "_site",
435 | includes: "_includes",
436 | layouts: "_layouts",
437 | data: "_data",
438 | },
439 | pathPrefix: "/repository-name/",
440 | markdownTemplateEngine: "njk",
441 | htmlTemplateEngine: "njk",
442 | };
443 | };
444 | ```
445 |
446 | **package.json additions:**
447 |
448 | ```json
449 | {
450 | "scripts": {
451 | "build": "eleventy",
452 | "serve": "eleventy --serve",
453 | "debug": "DEBUG=Eleventy* eleventy"
454 | },
455 | "devDependencies": {
456 | "@11ty/eleventy": "^2.0.0",
457 | "markdown-it": "^13.0.0",
458 | "markdown-it-anchor": "^8.6.0"
459 | }
460 | }
461 | ```
462 |
463 | ## GitHub Actions Configuration
464 |
465 | ### Common Workflow Settings
466 |
467 | All generated workflows include these optimizations:
468 |
469 | ```yaml
470 | permissions:
471 | contents: read
472 | pages: write
473 | id-token: write
474 |
475 | concurrency:
476 | group: "pages"
477 | cancel-in-progress: false
478 |
479 | environment:
480 | name: github-pages
481 | url: ${{ steps.deployment.outputs.page_url }}
482 | ```
483 |
484 | ### Caching Configuration
485 |
486 | Node.js dependencies:
487 |
488 | ```yaml
489 | - name: Cache dependencies
490 | uses: actions/cache@v4
491 | with:
492 | path: ~/.npm
493 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
494 | restore-keys: |
495 | ${{ runner.os }}-node-
496 | ```
497 |
498 | Ruby dependencies (Jekyll):
499 |
500 | ```yaml
501 | - name: Cache gems
502 | uses: actions/cache@v4
503 | with:
504 | path: vendor/bundle
505 | key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
506 | restore-keys: |
507 | ${{ runner.os }}-gems-
508 | ```
509 |
510 | ## Performance Configuration
511 |
512 | ### Build Optimization
513 |
514 | **Docusaurus:**
515 |
516 | ```javascript
517 | const config = {
518 | future: {
519 | experimental_faster: true,
520 | },
521 | webpack: {
522 | jsLoader: (isServer) => ({
523 | loader: "esbuild-loader",
524 | options: {
525 | loader: "tsx",
526 | target: isServer ? "node12" : "es2017",
527 | },
528 | }),
529 | },
530 | };
531 | ```
532 |
533 | **Hugo:**
534 |
535 | ```yaml
536 | build:
537 | writeStats: true
538 | noJSConfigInAssets: true
539 |
540 | caches:
541 | getjson:
542 | maxAge: "1m"
543 | getcsv:
544 | maxAge: "1m"
545 | ```
546 |
547 | ### SEO Configuration
548 |
549 | All SSGs include:
550 |
551 | - Meta tags for social sharing
552 | - Structured data markup
553 | - XML sitemaps
554 | - RSS feeds
555 | - Canonical URLs
556 | - Open Graph tags
557 |
558 | ## Security Configuration
559 |
560 | ### Content Security Policy
561 |
562 | Generated sites include CSP headers:
563 |
564 | ```html
565 | <meta
566 | http-equiv="Content-Security-Policy"
567 | content="
568 | default-src 'self';
569 | script-src 'self' 'unsafe-inline' https://www.googletagmanager.com;
570 | style-src 'self' 'unsafe-inline';
571 | img-src 'self' data: https:;
572 | connect-src 'self' https://www.google-analytics.com;
573 | "
574 | />
575 | ```
576 |
577 | ### HTTPS Enforcement
578 |
579 | All deployments force HTTPS and include HSTS headers.
580 |
581 | ## Troubleshooting Configuration Issues
582 |
583 | ### Common Problems
584 |
585 | **BaseURL Mismatch:**
586 |
587 | ```bash
588 | # Check your configuration matches repository name
589 | baseURL: "https://username.github.io/repository-name/" # Must match exactly
590 | ```
591 |
592 | **Build Failures:**
593 |
594 | ```bash
595 | # Verify Node.js version in workflows
596 | node-version: '20' # Must match your local version
597 | ```
598 |
599 | **Asset Loading Issues:**
600 |
601 | ```bash
602 | # Ensure relative paths
603 | <img src="./images/logo.png" /> # Good
604 | <img src="/images/logo.png" /> # May fail
605 | ```
606 |
607 | For more troubleshooting help, see the [Troubleshooting Guide](../how-to/troubleshooting.md).
608 |
```
--------------------------------------------------------------------------------
/tests/memory/enhanced-manager.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Advanced unit tests for Enhanced Memory Manager
3 | * Tests intelligent memory management with learning and knowledge graph integration
4 | * Part of Issue #55 - Advanced Memory Components Unit Tests
5 | */
6 |
7 | import { promises as fs } from "fs";
8 | import path from "path";
9 | import os from "os";
10 | import {
11 | EnhancedMemoryManager,
12 | EnhancedRecommendation,
13 | IntelligentAnalysis,
14 | } from "../../src/memory/enhanced-manager.js";
15 | import { ProjectFeatures } from "../../src/memory/learning.js";
16 |
17 | describe("EnhancedMemoryManager", () => {
18 | let tempDir: string;
19 | let enhancedManager: EnhancedMemoryManager;
20 |
21 | beforeEach(async () => {
22 | // Create unique temp directory for each test
23 | tempDir = path.join(
24 | os.tmpdir(),
25 | `enhanced-memory-test-${Date.now()}-${Math.random()
26 | .toString(36)
27 | .substr(2, 9)}`,
28 | );
29 | await fs.mkdir(tempDir, { recursive: true });
30 |
31 | enhancedManager = new EnhancedMemoryManager(tempDir);
32 | await enhancedManager.initialize();
33 | });
34 |
35 | afterEach(async () => {
36 | // Cleanup temp directory
37 | try {
38 | await fs.rm(tempDir, { recursive: true, force: true });
39 | } catch (error) {
40 | // Ignore cleanup errors
41 | }
42 | });
43 |
44 | describe("Enhanced Manager Initialization", () => {
45 | test("should create enhanced manager instance", () => {
46 | expect(enhancedManager).toBeDefined();
47 | expect(enhancedManager).toBeInstanceOf(EnhancedMemoryManager);
48 | });
49 |
50 | test("should initialize all subsystems", async () => {
51 | // Test that the enhanced manager properly initializes
52 | // The initialize() method should complete without throwing
53 | await enhancedManager.initialize();
54 | expect(true).toBe(true);
55 | });
56 |
57 | test("should have learning and knowledge graph capabilities", async () => {
58 | // Test that we can get learning statistics (indicating learning system exists)
59 | const learningStats = await enhancedManager.getLearningStatistics();
60 | expect(learningStats).toBeDefined();
61 | expect(learningStats.learning).toBeDefined();
62 | expect(learningStats.knowledgeGraph).toBeDefined();
63 | });
64 | });
65 |
66 | describe("Enhanced Recommendations", () => {
67 | test("should provide enhanced recommendations with multiple data sources", async () => {
68 | // Set up test context
69 | enhancedManager.setContext({ projectId: "enhanced-rec-test" });
70 |
71 | // Add some historical data
72 | await enhancedManager.remember("analysis", {
73 | language: { primary: "typescript" },
74 | framework: { name: "react" },
75 | stats: { files: 150 },
76 | });
77 |
78 | await enhancedManager.remember("recommendation", {
79 | recommended: "docusaurus",
80 | confidence: 0.9,
81 | });
82 |
83 | await enhancedManager.remember("deployment", {
84 | status: "success",
85 | ssg: "docusaurus",
86 | });
87 |
88 | // Test enhanced recommendation
89 | const projectFeatures: ProjectFeatures = {
90 | language: "typescript",
91 | framework: "react",
92 | size: "medium",
93 | complexity: "moderate",
94 | hasTests: true,
95 | hasCI: true,
96 | hasDocs: false,
97 | isOpenSource: true,
98 | };
99 |
100 | const baseRecommendation = {
101 | recommended: "gatsby",
102 | confidence: 0.7,
103 | score: 0.75,
104 | };
105 |
106 | const enhanced = await enhancedManager.getEnhancedRecommendation(
107 | "/test/project",
108 | baseRecommendation,
109 | projectFeatures,
110 | );
111 |
112 | expect(enhanced).toBeDefined();
113 | expect(enhanced.baseRecommendation).toEqual(baseRecommendation);
114 | expect(enhanced.learningEnhanced).toBeDefined();
115 | expect(Array.isArray(enhanced.graphBased)).toBe(true);
116 | expect(Array.isArray(enhanced.insights)).toBe(true);
117 | expect(typeof enhanced.confidence).toBe("number");
118 | expect(Array.isArray(enhanced.reasoning)).toBe(true);
119 | expect(enhanced.metadata).toBeDefined();
120 | expect(typeof enhanced.metadata.usedLearning).toBe("boolean");
121 | expect(typeof enhanced.metadata.usedKnowledgeGraph).toBe("boolean");
122 | });
123 |
124 | test("should handle recommendations with insufficient data", async () => {
125 | const projectFeatures: ProjectFeatures = {
126 | language: "unknown",
127 | size: "small",
128 | complexity: "simple",
129 | hasTests: false,
130 | hasCI: false,
131 | hasDocs: false,
132 | isOpenSource: false,
133 | };
134 |
135 | const baseRecommendation = {
136 | recommended: "jekyll",
137 | confidence: 0.5,
138 | };
139 |
140 | const enhanced = await enhancedManager.getEnhancedRecommendation(
141 | "/test/project",
142 | baseRecommendation,
143 | projectFeatures,
144 | );
145 |
146 | expect(enhanced).toBeDefined();
147 | expect(enhanced.confidence).toBeGreaterThanOrEqual(0);
148 | expect(enhanced.confidence).toBeLessThanOrEqual(1);
149 | });
150 | });
151 |
152 | describe("Intelligent Analysis", () => {
153 | test("should provide intelligent analysis with patterns and predictions", async () => {
154 | enhancedManager.setContext({ projectId: "intelligent-analysis-test" });
155 |
156 | // Add analysis data
157 | await enhancedManager.remember("analysis", {
158 | language: { primary: "python" },
159 | framework: { name: "flask" },
160 | dependencies: { count: 25 },
161 | testing: { hasTests: true },
162 | ci: { hasCI: true },
163 | });
164 |
165 | const analysisData = {
166 | language: "python",
167 | framework: "flask",
168 | size: "medium",
169 | hasTests: true,
170 | hasCI: true,
171 | };
172 |
173 | const intelligentAnalysis = await enhancedManager.getIntelligentAnalysis(
174 | "/test/project",
175 | analysisData,
176 | );
177 |
178 | expect(intelligentAnalysis).toBeDefined();
179 | expect(intelligentAnalysis.analysis).toBeDefined();
180 | expect(Array.isArray(intelligentAnalysis.patterns)).toBe(true);
181 | expect(Array.isArray(intelligentAnalysis.predictions)).toBe(true);
182 | expect(Array.isArray(intelligentAnalysis.recommendations)).toBe(true);
183 | expect(intelligentAnalysis.learningData).toBeDefined();
184 | expect(typeof intelligentAnalysis.learningData.similarProjects).toBe(
185 | "number",
186 | );
187 | expect(typeof intelligentAnalysis.learningData.confidenceLevel).toBe(
188 | "number",
189 | );
190 | expect(["low", "medium", "high"]).toContain(
191 | intelligentAnalysis.learningData.dataQuality,
192 | );
193 |
194 | // Check prediction structure
195 | if (intelligentAnalysis.predictions.length > 0) {
196 | const prediction = intelligentAnalysis.predictions[0];
197 | expect(["success_rate", "optimal_ssg", "potential_issues"]).toContain(
198 | prediction.type,
199 | );
200 | expect(typeof prediction.prediction).toBe("string");
201 | expect(typeof prediction.confidence).toBe("number");
202 | }
203 | });
204 |
205 | test("should adapt analysis based on historical patterns", async () => {
206 | enhancedManager.setContext({ projectId: "adaptive-analysis-test" });
207 |
208 | // Create pattern with multiple similar projects
209 | for (let i = 0; i < 3; i++) {
210 | await enhancedManager.remember("analysis", {
211 | language: { primary: "javascript" },
212 | framework: { name: "vue" },
213 | });
214 |
215 | await enhancedManager.remember("recommendation", {
216 | recommended: "vuepress",
217 | confidence: 0.8 + i * 0.05,
218 | });
219 |
220 | await enhancedManager.remember("deployment", {
221 | status: "success",
222 | ssg: "vuepress",
223 | });
224 | }
225 |
226 | const analysisData = {
227 | language: "javascript",
228 | framework: "vue",
229 | size: "small",
230 | };
231 |
232 | const analysis = await enhancedManager.getIntelligentAnalysis(
233 | "/test/project",
234 | analysisData,
235 | );
236 |
237 | expect(analysis.learningData.similarProjects).toBeGreaterThan(0);
238 | expect(analysis.learningData.dataQuality).toBe("medium");
239 | });
240 | });
241 |
242 | describe("Memory Integration", () => {
243 | test("should integrate learning feedback into knowledge graph", async () => {
244 | enhancedManager.setContext({ projectId: "integration-test" });
245 |
246 | // Create initial recommendation
247 | const memoryEntry = await enhancedManager.remember("recommendation", {
248 | recommended: "hugo",
249 | confidence: 0.8,
250 | language: { primary: "go" },
251 | });
252 |
253 | // Simulate feedback by creating a deployment success record
254 | await enhancedManager.remember("deployment", {
255 | status: "success",
256 | ssg: "hugo",
257 | feedback: {
258 | rating: 5,
259 | helpful: true,
260 | comments: "Worked perfectly",
261 | },
262 | });
263 |
264 | // Verify feedback was processed
265 | const stats = await enhancedManager.getLearningStatistics();
266 | expect(stats).toBeDefined();
267 | expect(stats.learning).toBeDefined();
268 | });
269 |
270 | test("should synchronize data between subsystems", async () => {
271 | enhancedManager.setContext({ projectId: "sync-test" });
272 |
273 | // Add data that should propagate between systems
274 | await enhancedManager.remember("analysis", {
275 | language: { primary: "rust" },
276 | framework: { name: "actix" },
277 | });
278 |
279 | await enhancedManager.remember("deployment", {
280 | status: "success",
281 | ssg: "mdbook",
282 | });
283 |
284 | // The subsystems should automatically sync through the enhanced manager
285 | // Verify data exists in both systems
286 | const learningStats = await enhancedManager.getLearningStatistics();
287 |
288 | expect(learningStats).toBeDefined();
289 | expect(learningStats.learning).toBeDefined();
290 | expect(learningStats.knowledgeGraph).toBeDefined();
291 | expect(learningStats.combined).toBeDefined();
292 | });
293 | });
294 |
295 | describe("Performance and Optimization", () => {
296 | test("should handle concurrent enhanced operations", async () => {
297 | enhancedManager.setContext({ projectId: "concurrent-enhanced-test" });
298 |
299 | const operations = Array.from({ length: 5 }, async (_, i) => {
300 | const projectFeatures: ProjectFeatures = {
301 | language: "go",
302 | size: "medium",
303 | complexity: "moderate",
304 | hasTests: true,
305 | hasCI: true,
306 | hasDocs: true,
307 | isOpenSource: true,
308 | };
309 |
310 | const baseRecommendation = {
311 | recommended: "hugo",
312 | confidence: 0.8 + i * 0.02,
313 | };
314 |
315 | return enhancedManager.getEnhancedRecommendation(
316 | "/test/project",
317 | baseRecommendation,
318 | projectFeatures,
319 | );
320 | });
321 |
322 | const results = await Promise.all(operations);
323 | expect(results.length).toBe(5);
324 | results.forEach((result) => {
325 | expect(result).toBeDefined();
326 | expect(result.confidence).toBeGreaterThanOrEqual(0);
327 | });
328 | });
329 |
330 | test("should provide optimization insights", async () => {
331 | enhancedManager.setContext({ projectId: "optimization-test" });
332 |
333 | // Add some data
334 | await enhancedManager.remember("analysis", { performanceTest: true });
335 |
336 | // Test learning statistics as a proxy for optimization insights
337 | const stats = await enhancedManager.getLearningStatistics();
338 | expect(stats).toBeDefined();
339 | expect(stats.combined).toBeDefined();
340 | expect(typeof stats.combined.systemMaturity).toBe("string");
341 | expect(["nascent", "developing", "mature"]).toContain(
342 | stats.combined.systemMaturity,
343 | );
344 | });
345 | });
346 |
347 | describe("Error Handling and Edge Cases", () => {
348 | test("should handle malformed input gracefully", async () => {
349 | const malformedFeatures = {
350 | language: null,
351 | size: "invalid" as any,
352 | complexity: undefined as any,
353 | };
354 |
355 | const malformedRecommendation = {
356 | recommended: "",
357 | confidence: -1,
358 | };
359 |
360 | // Should not throw, but handle gracefully
361 | const result = await enhancedManager.getEnhancedRecommendation(
362 | "/test/project",
363 | malformedRecommendation,
364 | malformedFeatures as any,
365 | );
366 |
367 | expect(result).toBeDefined();
368 | expect(result.confidence).toBeGreaterThanOrEqual(0);
369 | expect(result.confidence).toBeLessThanOrEqual(1);
370 | });
371 |
372 | test("should handle subsystem failures gracefully", async () => {
373 | // Test with partial system availability
374 | const projectFeatures: ProjectFeatures = {
375 | language: "javascript",
376 | size: "small",
377 | complexity: "simple",
378 | hasTests: false,
379 | hasCI: false,
380 | hasDocs: false,
381 | isOpenSource: true,
382 | };
383 |
384 | const baseRecommendation = {
385 | recommended: "gatsby",
386 | confidence: 0.6,
387 | };
388 |
389 | // Should work even if some subsystems have issues
390 | const result = await enhancedManager.getEnhancedRecommendation(
391 | "/test/project",
392 | baseRecommendation,
393 | projectFeatures,
394 | );
395 |
396 | expect(result).toBeDefined();
397 | expect(result.baseRecommendation).toEqual(baseRecommendation);
398 | });
399 | });
400 | });
401 |
```
--------------------------------------------------------------------------------
/src/tools/setup-structure.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { promises as fs } from "fs";
2 | import path from "path";
3 | import { z } from "zod";
4 | import { MCPToolResponse, formatMCPResponse } from "../types/api.js";
5 |
6 | const inputSchema = z.object({
7 | path: z.string(),
8 | ssg: z.enum(["jekyll", "hugo", "docusaurus", "mkdocs", "eleventy"]),
9 | includeExamples: z.boolean().optional().default(true),
10 | });
11 |
12 | // Diataxis structure based on ADR-004
13 | const DIATAXIS_STRUCTURE = {
14 | tutorials: {
15 | description: "Learning-oriented guides for newcomers",
16 | example: "getting-started.md",
17 | },
18 | "how-to": {
19 | description: "Task-oriented guides for specific goals",
20 | example: "deploy-to-production.md",
21 | },
22 | reference: {
23 | description: "Information-oriented technical descriptions",
24 | example: "api-documentation.md",
25 | },
26 | explanation: {
27 | description: "Understanding-oriented conceptual discussions",
28 | example: "architecture-overview.md",
29 | },
30 | };
31 |
32 | /**
33 | * Sets up the Diataxis-compliant documentation structure for a project.
34 | *
35 | * Creates a comprehensive documentation structure following the Diataxis framework,
36 | * organizing content into four categories: tutorials (learning-oriented), how-to
37 | * guides (problem-oriented), reference (information-oriented), and explanation
38 | * (understanding-oriented). Includes support for different static site generators
39 | * and optional example content.
40 | *
41 | * @param args - The input arguments for structure setup
42 | * @param args.path - The root path where documentation structure should be created
43 | * @param args.ssg - The static site generator type for structure optimization
44 | * @param args.includeExamples - Whether to include example content (default: true)
45 | *
46 | * @returns Promise resolving to structure setup results
47 | * @returns content - Array containing the setup results in MCP tool response format
48 | *
49 | * @throws {Error} When the output path is inaccessible or invalid
50 | * @throws {Error} When the SSG type is unsupported
51 | * @throws {Error} When directory structure creation fails
52 | *
53 | * @example
54 | * ```typescript
55 | * // Set up Docusaurus structure
56 | * const result = await setupStructure({
57 | * path: "./docs",
58 | * ssg: "docusaurus",
59 | * includeExamples: true
60 | * });
61 | *
62 | * // Set up minimal Hugo structure
63 | * const minimal = await setupStructure({
64 | * path: "./site/content",
65 | * ssg: "hugo",
66 | * includeExamples: false
67 | * });
68 | * ```
69 | *
70 | * @since 1.0.0
71 | */
72 | export async function setupStructure(
73 | args: unknown,
74 | ): Promise<{ content: any[] }> {
75 | const startTime = Date.now();
76 | const { path: docsPath, ssg, includeExamples } = inputSchema.parse(args);
77 |
78 | try {
79 | const createdDirs: string[] = [];
80 | const createdFiles: string[] = [];
81 |
82 | // Create base docs directory
83 | await fs.mkdir(docsPath, { recursive: true });
84 |
85 | // Create Diataxis structure
86 | for (const [category, info] of Object.entries(DIATAXIS_STRUCTURE)) {
87 | const categoryPath = path.join(docsPath, category);
88 | await fs.mkdir(categoryPath, { recursive: true });
89 | createdDirs.push(categoryPath);
90 |
91 | // Create index file for category
92 | const indexPath = path.join(categoryPath, "index.md");
93 | const indexContent = generateCategoryIndex(
94 | category,
95 | info.description,
96 | ssg,
97 | includeExamples,
98 | );
99 | await fs.writeFile(indexPath, indexContent);
100 | createdFiles.push(indexPath);
101 |
102 | // Create example content if requested
103 | if (includeExamples) {
104 | const examplePath = path.join(categoryPath, info.example);
105 | const exampleContent = generateExampleContent(
106 | category,
107 | info.example,
108 | ssg,
109 | );
110 | await fs.writeFile(examplePath, exampleContent);
111 | createdFiles.push(examplePath);
112 | }
113 | }
114 |
115 | // Create root index
116 | const rootIndexPath = path.join(docsPath, "index.md");
117 | const rootIndexContent = generateRootIndex(ssg);
118 | await fs.writeFile(rootIndexPath, rootIndexContent);
119 | createdFiles.push(rootIndexPath);
120 |
121 | const structureResult = {
122 | docsPath,
123 | ssg,
124 | includeExamples,
125 | directoriesCreated: createdDirs,
126 | filesCreated: createdFiles,
127 | diataxisCategories: Object.keys(DIATAXIS_STRUCTURE),
128 | totalDirectories: createdDirs.length,
129 | totalFiles: createdFiles.length,
130 | };
131 |
132 | const response: MCPToolResponse<typeof structureResult> = {
133 | success: true,
134 | data: structureResult,
135 | metadata: {
136 | toolVersion: "1.0.0",
137 | executionTime: Date.now() - startTime,
138 | timestamp: new Date().toISOString(),
139 | },
140 | recommendations: [
141 | {
142 | type: "info",
143 | title: "Diataxis Structure Created",
144 | description: `Successfully created ${createdDirs.length} directories and ${createdFiles.length} files`,
145 | },
146 | ],
147 | nextSteps: [
148 | {
149 | action: "Generate Sitemap",
150 | toolRequired: "manage_sitemap",
151 | description:
152 | "Create sitemap.xml as source of truth for documentation links (required for SEO)",
153 | priority: "high",
154 | },
155 | {
156 | action: "Setup GitHub Pages Deployment",
157 | toolRequired: "deploy_pages",
158 | description: "Create automated deployment workflow",
159 | priority: "medium",
160 | },
161 | ],
162 | };
163 |
164 | return formatMCPResponse(response);
165 | } catch (error) {
166 | const errorResponse: MCPToolResponse = {
167 | success: false,
168 | error: {
169 | code: "STRUCTURE_SETUP_FAILED",
170 | message: `Failed to setup structure: ${error}`,
171 | resolution: "Ensure the documentation path is writable and accessible",
172 | },
173 | metadata: {
174 | toolVersion: "1.0.0",
175 | executionTime: Date.now() - startTime,
176 | timestamp: new Date().toISOString(),
177 | },
178 | };
179 | return formatMCPResponse(errorResponse);
180 | }
181 | }
182 |
183 | function generateCategoryIndex(
184 | category: string,
185 | description: string,
186 | ssg: string,
187 | includeExamples: boolean = true,
188 | ): string {
189 | const title =
190 | category.charAt(0).toUpperCase() + category.slice(1).replace("-", " ");
191 |
192 | let frontmatter = "";
193 | switch (ssg) {
194 | case "docusaurus":
195 | frontmatter = `---
196 | id: ${category}-index
197 | title: ${title}
198 | sidebar_label: ${title}
199 | ---\n\n`;
200 | break;
201 | case "mkdocs":
202 | case "jekyll":
203 | case "hugo":
204 | frontmatter = `---
205 | title: ${title}
206 | description: ${description}
207 | ---\n\n`;
208 | break;
209 | }
210 |
211 | return `${frontmatter}# ${title}
212 |
213 | ${description}
214 |
215 | ## Available Guides
216 |
217 | This section contains ${category} documentation following the Diataxis framework.
218 |
219 | ${generateDiataxisExplanation(category)}
220 |
221 | ## Contents
222 |
223 | ${
224 | includeExamples
225 | ? `- [Example: ${
226 | DIATAXIS_STRUCTURE[category as keyof typeof DIATAXIS_STRUCTURE].example
227 | }](./${
228 | DIATAXIS_STRUCTURE[category as keyof typeof DIATAXIS_STRUCTURE].example
229 | })`
230 | : "- Coming soon..."
231 | }
232 | `;
233 | }
234 |
235 | function generateExampleContent(
236 | category: string,
237 | filename: string,
238 | ssg: string,
239 | ): string {
240 | const title = filename
241 | .replace(".md", "")
242 | .replace(/-/g, " ")
243 | .split(" ")
244 | .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
245 | .join(" ");
246 |
247 | let frontmatter = "";
248 | switch (ssg) {
249 | case "docusaurus":
250 | frontmatter = `---
251 | id: ${filename.replace(".md", "")}
252 | title: ${title}
253 | sidebar_label: ${title}
254 | ---\n\n`;
255 | break;
256 | default:
257 | frontmatter = `---
258 | title: ${title}
259 | ---\n\n`;
260 | break;
261 | }
262 |
263 | let content = "";
264 | switch (category) {
265 | case "tutorials":
266 | content = `# ${title}
267 |
268 | This tutorial will guide you through the process step by step.
269 |
270 | ## Prerequisites
271 |
272 | Before you begin, ensure you have:
273 | - Requirement 1
274 | - Requirement 2
275 |
276 | ## Step 1: Initial Setup
277 |
278 | Start by...
279 |
280 | ## Step 2: Configuration
281 |
282 | Next, configure...
283 |
284 | ## Step 3: Verification
285 |
286 | Finally, verify...
287 |
288 | ## Summary
289 |
290 | In this tutorial, you learned how to:
291 | - Achievement 1
292 | - Achievement 2
293 | - Achievement 3
294 |
295 | ## Next Steps
296 |
297 | - Explore [How-To Guides](../how-to/)
298 | - Read the [API Reference](../reference/)`;
299 | break;
300 |
301 | case "how-to":
302 | content = `# ${title}
303 |
304 | This guide shows you how to accomplish a specific task.
305 |
306 | ## Prerequisites
307 |
308 | - Prerequisite 1
309 | - Prerequisite 2
310 |
311 | ## Steps
312 |
313 | ### 1. Prepare your environment
314 |
315 | \`\`\`bash
316 | # Example command
317 | echo "Setup environment"
318 | \`\`\`
319 |
320 | ### 2. Execute the task
321 |
322 | \`\`\`bash
323 | # Main command
324 | echo "Execute task"
325 | \`\`\`
326 |
327 | ### 3. Verify results
328 |
329 | \`\`\`bash
330 | # Verification command
331 | echo "Verify success"
332 | \`\`\`
333 |
334 | ## Troubleshooting
335 |
336 | If you encounter issues:
337 | - Check condition 1
338 | - Verify setting 2
339 |
340 | ## Related Guides
341 |
342 | - [Another How-To Guide](./another-guide.md)
343 | - [Reference Documentation](../reference/)`;
344 | break;
345 |
346 | case "reference":
347 | content = `# ${title}
348 |
349 | Technical reference documentation.
350 |
351 | ## Overview
352 |
353 | This document provides complete reference information for...
354 |
355 | ## API Endpoints
356 |
357 | ### GET /api/resource
358 |
359 | Retrieves...
360 |
361 | **Parameters:**
362 | - \`param1\` (string, required): Description
363 | - \`param2\` (number, optional): Description
364 |
365 | **Response:**
366 | \`\`\`json
367 | {
368 | "field1": "value",
369 | "field2": 123
370 | }
371 | \`\`\`
372 |
373 | ### POST /api/resource
374 |
375 | Creates...
376 |
377 | ## Configuration Options
378 |
379 | | Option | Type | Default | Description |
380 | |--------|------|---------|-------------|
381 | | option1 | string | "default" | Description of option1 |
382 | | option2 | boolean | false | Description of option2 |
383 |
384 | ## Error Codes
385 |
386 | | Code | Description | Resolution |
387 | |------|-------------|------------|
388 | | E001 | Error description | How to fix |
389 | | E002 | Error description | How to fix |`;
390 | break;
391 |
392 | case "explanation":
393 | content = `# ${title}
394 |
395 | This document explains the concepts and reasoning behind...
396 |
397 | ## Introduction
398 |
399 | Understanding the architecture requires knowledge of...
400 |
401 | ## Core Concepts
402 |
403 | ### Concept 1
404 |
405 | Explanation of the first core concept...
406 |
407 | ### Concept 2
408 |
409 | Explanation of the second core concept...
410 |
411 | ## Design Decisions
412 |
413 | ### Why This Approach?
414 |
415 | We chose this approach because...
416 |
417 | ### Trade-offs
418 |
419 | The main trade-offs include:
420 | - Trade-off 1: Benefit vs Cost
421 | - Trade-off 2: Benefit vs Cost
422 |
423 | ## Comparison with Alternatives
424 |
425 | | Approach | Pros | Cons |
426 | |----------|------|------|
427 | | Our Approach | Pro 1, Pro 2 | Con 1 |
428 | | Alternative 1 | Pro 1 | Con 1, Con 2 |
429 | | Alternative 2 | Pro 1, Pro 2 | Con 1 |
430 |
431 | ## Further Reading
432 |
433 | - [Related Tutorial](../tutorials/)
434 | - [Implementation Guide](../how-to/)`;
435 | break;
436 | }
437 |
438 | return `${frontmatter}${content}`;
439 | }
440 |
441 | function generateRootIndex(ssg: string): string {
442 | let frontmatter = "";
443 | switch (ssg) {
444 | case "docusaurus":
445 | frontmatter = `---
446 | id: intro
447 | title: Documentation
448 | sidebar_position: 1
449 | ---\n\n`;
450 | break;
451 | default:
452 | frontmatter = `---
453 | title: Documentation
454 | ---\n\n`;
455 | break;
456 | }
457 |
458 | return `${frontmatter}# Documentation
459 |
460 | Welcome to our documentation! This site follows the [Diataxis](https://diataxis.fr/) framework to provide clear, well-organized documentation.
461 |
462 | ## Documentation Structure
463 |
464 | Our documentation is organized into four distinct sections:
465 |
466 | ### 📚 [Tutorials](./tutorials/)
467 | Learning-oriented guides that take you through a process step by step. Perfect for newcomers who want to get started.
468 |
469 | ### 🔧 [How-To Guides](./how-to/)
470 | Task-oriented recipes that help you accomplish specific goals. Ideal when you know what you want to do.
471 |
472 | ### 📖 [Reference](./reference/)
473 | Information-oriented technical descriptions of the system. Essential when you need to look up specific details.
474 |
475 | ### 💡 [Explanation](./explanation/)
476 | Understanding-oriented discussions that clarify and illuminate topics. Great for deepening your knowledge.
477 |
478 | ## Quick Start
479 |
480 | New to this project? Start with our [Getting Started Tutorial](./tutorials/getting-started.md).
481 |
482 | ## Contributing
483 |
484 | We welcome contributions to our documentation! Please see our [Contributing Guide](./how-to/contribute.md) for details.
485 | `;
486 | }
487 |
488 | function generateDiataxisExplanation(category: string): string {
489 | const explanations: Record<string, string> = {
490 | tutorials: `
491 | **Tutorials** are learning-oriented and help newcomers get started:
492 | - Take the reader through a process step by step
493 | - Focus on learning by doing
494 | - Ensure the reader succeeds in accomplishing something
495 | - Build confidence through success`,
496 | "how-to": `
497 | **How-To Guides** are task-oriented and help users accomplish specific goals:
498 | - Solve specific problems
499 | - Assume some knowledge and experience
500 | - Provide a series of steps
501 | - Focus on results`,
502 | reference: `
503 | **Reference** documentation is information-oriented:
504 | - Describe the machinery
505 | - Be accurate and complete
506 | - Focus on describing, not explaining
507 | - Structure content for finding information`,
508 | explanation: `
509 | **Explanation** documentation is understanding-oriented:
510 | - Clarify and illuminate a topic
511 | - Provide context and background
512 | - Discuss alternatives and opinions
513 | - Focus on understanding, not instruction`,
514 | };
515 |
516 | return explanations[category] || "";
517 | }
518 |
```
--------------------------------------------------------------------------------
/docs/api/assets/icons.js:
--------------------------------------------------------------------------------
```javascript
1 | (function () {
2 | addIcons();
3 | function addIcons() {
4 | if (document.readyState === "loading")
5 | return document.addEventListener("DOMContentLoaded", addIcons);
6 | const svg = document.body.appendChild(
7 | document.createElementNS("http://www.w3.org/2000/svg", "svg"),
8 | );
9 | svg.innerHTML = `<g id="icon-1" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-module)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">M</text></g><g id="icon-2" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-module)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">M</text></g><g id="icon-4" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-namespace)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">N</text></g><g id="icon-8" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-enum)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">E</text></g><g id="icon-16" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-property)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">P</text></g><g id="icon-32" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-variable)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">V</text></g><g id="icon-64" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-function)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">F</text></g><g id="icon-128" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-class)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">C</text></g><g id="icon-256" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-interface)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">I</text></g><g id="icon-512" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-constructor)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">C</text></g><g id="icon-1024" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-property)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">P</text></g><g id="icon-2048" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-method)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">M</text></g><g id="icon-4096" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-function)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">F</text></g><g id="icon-8192" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-property)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">P</text></g><g id="icon-16384" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-constructor)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">C</text></g><g id="icon-32768" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-property)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">P</text></g><g id="icon-65536" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-type-alias)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">T</text></g><g id="icon-131072" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-type-alias)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">T</text></g><g id="icon-262144" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-accessor)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">A</text></g><g id="icon-524288" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-accessor)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">A</text></g><g id="icon-1048576" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-accessor)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">A</text></g><g id="icon-2097152" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-type-alias)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">T</text></g><g id="icon-4194304" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-reference)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">R</text></g><g id="icon-8388608" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-document)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><g stroke="var(--color-icon-text)" fill="none" stroke-width="1.5"><polygon points="6,5 6,19 18,19, 18,10 13,5"></polygon><line x1="9" y1="9" x2="13" y2="9"></line><line x1="9" y1="12" x2="15" y2="12"></line><line x1="9" y1="15" x2="15" y2="15"></line></g></g><g id="icon-folder" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-document)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><g stroke="var(--color-icon-text)" fill="none" stroke-width="1.5"><polygon points="5,5 10,5 12,8 19,8 19,18 5,18"></polygon></g></g><g id="icon-chevronDown" class="tsd-no-select"><path d="M4.93896 8.531L12 15.591L19.061 8.531L16.939 6.409L12 11.349L7.06098 6.409L4.93896 8.531Z" fill="var(--color-icon-text)"></path></g><g id="icon-chevronSmall" class="tsd-no-select"><path d="M1.5 5.50969L8 11.6609L14.5 5.50969L12.5466 3.66086L8 7.96494L3.45341 3.66086L1.5 5.50969Z" fill="var(--color-icon-text)"></path></g><g id="icon-checkbox" class="tsd-no-select"><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></g><g id="icon-menu" class="tsd-no-select"><rect x="1" y="3" width="14" height="2" fill="var(--color-icon-text)"></rect><rect x="1" y="7" width="14" height="2" fill="var(--color-icon-text)"></rect><rect x="1" y="11" width="14" height="2" fill="var(--color-icon-text)"></rect></g><g id="icon-search" class="tsd-no-select"><path d="M15.7824 13.833L12.6666 10.7177C12.5259 10.5771 12.3353 10.499 12.1353 10.499H11.6259C12.4884 9.39596 13.001 8.00859 13.001 6.49937C13.001 2.90909 10.0914 0 6.50048 0C2.90959 0 0 2.90909 0 6.49937C0 10.0896 2.90959 12.9987 6.50048 12.9987C8.00996 12.9987 9.39756 12.4863 10.5008 11.6239V12.1332C10.5008 12.3332 10.5789 12.5238 10.7195 12.6644L13.8354 15.7797C14.1292 16.0734 14.6042 16.0734 14.8948 15.7797L15.7793 14.8954C16.0731 14.6017 16.0731 14.1267 15.7824 13.833ZM6.50048 10.499C4.29094 10.499 2.50018 8.71165 2.50018 6.49937C2.50018 4.29021 4.28781 2.49976 6.50048 2.49976C8.71001 2.49976 10.5008 4.28708 10.5008 6.49937C10.5008 8.70852 8.71314 10.499 6.50048 10.499Z" fill="var(--color-icon-text)"></path></g><g id="icon-anchor" class="tsd-no-select"><g stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M10 14a3.5 3.5 0 0 0 5 0l4 -4a3.5 3.5 0 0 0 -5 -5l-.5 .5"></path><path d="M14 10a3.5 3.5 0 0 0 -5 0l-4 4a3.5 3.5 0 0 0 5 5l.5 -.5"></path></g></g><g id="icon-alertNote" class="tsd-no-select"><path fill="var(--color-alert-note)" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></g><g id="icon-alertTip" class="tsd-no-select"><path fill="var(--color-alert-tip)" d="M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"></path></g><g id="icon-alertImportant" class="tsd-no-select"><path fill="var(--color-alert-important)" d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></g><g id="icon-alertWarning" class="tsd-no-select"><path fill="var(--color-alert-warning)" d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></g><g id="icon-alertCaution" class="tsd-no-select"><path fill="var(--color-alert-caution)" d="M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></g>`;
10 | svg.style.display = "none";
11 | if (location.protocol === "file:") updateUseElements();
12 | }
13 |
14 | function updateUseElements() {
15 | document.querySelectorAll("use").forEach((el) => {
16 | if (el.getAttribute("href").includes("#icon-")) {
17 | el.setAttribute("href", el.getAttribute("href").replace(/.*#/, "#"));
18 | }
19 | });
20 | }
21 | })();
22 |
```