This is page 10 of 20. Use http://codebase.md/tosin2013/documcp?page={x} to view the full context.
# Directory Structure
```
├── .dockerignore
├── .eslintignore
├── .eslintrc.json
├── .github
│ ├── agents
│ │ ├── documcp-ast.md
│ │ ├── documcp-deploy.md
│ │ ├── documcp-memory.md
│ │ ├── documcp-test.md
│ │ └── documcp-tool.md
│ ├── copilot-instructions.md
│ ├── dependabot.yml
│ ├── ISSUE_TEMPLATE
│ │ ├── automated-changelog.md
│ │ ├── bug_report.md
│ │ ├── bug_report.yml
│ │ ├── documentation_issue.md
│ │ ├── feature_request.md
│ │ ├── feature_request.yml
│ │ ├── npm-publishing-fix.md
│ │ └── release_improvements.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── release-drafter.yml
│ └── workflows
│ ├── auto-merge.yml
│ ├── ci.yml
│ ├── codeql.yml
│ ├── dependency-review.yml
│ ├── deploy-docs.yml
│ ├── README.md
│ ├── release-drafter.yml
│ └── release.yml
├── .gitignore
├── .husky
│ ├── commit-msg
│ └── pre-commit
├── .linkcheck.config.json
├── .markdown-link-check.json
├── .nvmrc
├── .pre-commit-config.yaml
├── .versionrc.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── commitlint.config.js
├── CONTRIBUTING.md
├── docker-compose.docs.yml
├── Dockerfile.docs
├── docs
│ ├── .docusaurus
│ │ ├── docusaurus-plugin-content-docs
│ │ │ └── default
│ │ │ └── __mdx-loader-dependency.json
│ │ └── docusaurus-plugin-content-pages
│ │ └── default
│ │ └── __plugin.json
│ ├── adrs
│ │ ├── 001-mcp-server-architecture.md
│ │ ├── 002-repository-analysis-engine.md
│ │ ├── 003-static-site-generator-recommendation-engine.md
│ │ ├── 004-diataxis-framework-integration.md
│ │ ├── 005-github-pages-deployment-automation.md
│ │ ├── 006-mcp-tools-api-design.md
│ │ ├── 007-mcp-prompts-and-resources-integration.md
│ │ ├── 008-intelligent-content-population-engine.md
│ │ ├── 009-content-accuracy-validation-framework.md
│ │ ├── 010-mcp-resource-pattern-redesign.md
│ │ └── README.md
│ ├── api
│ │ ├── .nojekyll
│ │ ├── assets
│ │ │ ├── hierarchy.js
│ │ │ ├── highlight.css
│ │ │ ├── icons.js
│ │ │ ├── icons.svg
│ │ │ ├── main.js
│ │ │ ├── navigation.js
│ │ │ ├── search.js
│ │ │ └── style.css
│ │ ├── hierarchy.html
│ │ ├── index.html
│ │ ├── modules.html
│ │ └── variables
│ │ └── TOOLS.html
│ ├── assets
│ │ └── logo.svg
│ ├── development
│ │ └── MCP_INSPECTOR_TESTING.md
│ ├── docusaurus.config.js
│ ├── explanation
│ │ ├── architecture.md
│ │ └── index.md
│ ├── guides
│ │ ├── link-validation.md
│ │ ├── playwright-integration.md
│ │ └── playwright-testing-workflow.md
│ ├── how-to
│ │ ├── analytics-setup.md
│ │ ├── custom-domains.md
│ │ ├── documentation-freshness-tracking.md
│ │ ├── github-pages-deployment.md
│ │ ├── index.md
│ │ ├── local-testing.md
│ │ ├── performance-optimization.md
│ │ ├── prompting-guide.md
│ │ ├── repository-analysis.md
│ │ ├── seo-optimization.md
│ │ ├── site-monitoring.md
│ │ ├── troubleshooting.md
│ │ └── usage-examples.md
│ ├── index.md
│ ├── knowledge-graph.md
│ ├── package-lock.json
│ ├── package.json
│ ├── phase-2-intelligence.md
│ ├── reference
│ │ ├── api-overview.md
│ │ ├── cli.md
│ │ ├── configuration.md
│ │ ├── deploy-pages.md
│ │ ├── index.md
│ │ ├── mcp-tools.md
│ │ └── prompt-templates.md
│ ├── research
│ │ ├── cross-domain-integration
│ │ │ └── README.md
│ │ ├── domain-1-mcp-architecture
│ │ │ ├── index.md
│ │ │ └── mcp-performance-research.md
│ │ ├── domain-2-repository-analysis
│ │ │ └── README.md
│ │ ├── domain-3-ssg-recommendation
│ │ │ ├── index.md
│ │ │ └── ssg-performance-analysis.md
│ │ ├── domain-4-diataxis-integration
│ │ │ └── README.md
│ │ ├── domain-5-github-deployment
│ │ │ ├── github-pages-security-analysis.md
│ │ │ └── index.md
│ │ ├── domain-6-api-design
│ │ │ └── README.md
│ │ ├── README.md
│ │ ├── research-integration-summary-2025-01-14.md
│ │ ├── research-progress-template.md
│ │ └── research-questions-2025-01-14.md
│ ├── robots.txt
│ ├── sidebars.js
│ ├── sitemap.xml
│ ├── src
│ │ └── css
│ │ └── custom.css
│ └── tutorials
│ ├── development-setup.md
│ ├── environment-setup.md
│ ├── first-deployment.md
│ ├── getting-started.md
│ ├── index.md
│ ├── memory-workflows.md
│ └── user-onboarding.md
├── jest.config.js
├── LICENSE
├── Makefile
├── MCP_PHASE2_IMPLEMENTATION.md
├── mcp-config-example.json
├── mcp.json
├── package-lock.json
├── package.json
├── README.md
├── release.sh
├── scripts
│ └── check-package-structure.cjs
├── SECURITY.md
├── setup-precommit.sh
├── src
│ ├── benchmarks
│ │ └── performance.ts
│ ├── index.ts
│ ├── memory
│ │ ├── contextual-retrieval.ts
│ │ ├── deployment-analytics.ts
│ │ ├── enhanced-manager.ts
│ │ ├── export-import.ts
│ │ ├── freshness-kg-integration.ts
│ │ ├── index.ts
│ │ ├── integration.ts
│ │ ├── kg-code-integration.ts
│ │ ├── kg-health.ts
│ │ ├── kg-integration.ts
│ │ ├── kg-link-validator.ts
│ │ ├── kg-storage.ts
│ │ ├── knowledge-graph.ts
│ │ ├── learning.ts
│ │ ├── manager.ts
│ │ ├── multi-agent-sharing.ts
│ │ ├── pruning.ts
│ │ ├── schemas.ts
│ │ ├── storage.ts
│ │ ├── temporal-analysis.ts
│ │ ├── user-preferences.ts
│ │ └── visualization.ts
│ ├── prompts
│ │ └── technical-writer-prompts.ts
│ ├── scripts
│ │ └── benchmark.ts
│ ├── templates
│ │ └── playwright
│ │ ├── accessibility.spec.template.ts
│ │ ├── Dockerfile.template
│ │ ├── docs-e2e.workflow.template.yml
│ │ ├── link-validation.spec.template.ts
│ │ └── playwright.config.template.ts
│ ├── tools
│ │ ├── analyze-deployments.ts
│ │ ├── analyze-readme.ts
│ │ ├── analyze-repository.ts
│ │ ├── check-documentation-links.ts
│ │ ├── deploy-pages.ts
│ │ ├── detect-gaps.ts
│ │ ├── evaluate-readme-health.ts
│ │ ├── generate-config.ts
│ │ ├── generate-contextual-content.ts
│ │ ├── generate-llm-context.ts
│ │ ├── generate-readme-template.ts
│ │ ├── generate-technical-writer-prompts.ts
│ │ ├── kg-health-check.ts
│ │ ├── manage-preferences.ts
│ │ ├── manage-sitemap.ts
│ │ ├── optimize-readme.ts
│ │ ├── populate-content.ts
│ │ ├── readme-best-practices.ts
│ │ ├── recommend-ssg.ts
│ │ ├── setup-playwright-tests.ts
│ │ ├── setup-structure.ts
│ │ ├── sync-code-to-docs.ts
│ │ ├── test-local-deployment.ts
│ │ ├── track-documentation-freshness.ts
│ │ ├── update-existing-documentation.ts
│ │ ├── validate-content.ts
│ │ ├── validate-documentation-freshness.ts
│ │ ├── validate-readme-checklist.ts
│ │ └── verify-deployment.ts
│ ├── types
│ │ └── api.ts
│ ├── utils
│ │ ├── ast-analyzer.ts
│ │ ├── code-scanner.ts
│ │ ├── content-extractor.ts
│ │ ├── drift-detector.ts
│ │ ├── freshness-tracker.ts
│ │ ├── language-parsers-simple.ts
│ │ ├── permission-checker.ts
│ │ └── sitemap-generator.ts
│ └── workflows
│ └── documentation-workflow.ts
├── test-docs-local.sh
├── tests
│ ├── api
│ │ └── mcp-responses.test.ts
│ ├── benchmarks
│ │ └── performance.test.ts
│ ├── edge-cases
│ │ └── error-handling.test.ts
│ ├── functional
│ │ └── tools.test.ts
│ ├── integration
│ │ ├── kg-documentation-workflow.test.ts
│ │ ├── knowledge-graph-workflow.test.ts
│ │ ├── mcp-readme-tools.test.ts
│ │ ├── memory-mcp-tools.test.ts
│ │ ├── readme-technical-writer.test.ts
│ │ └── workflow.test.ts
│ ├── memory
│ │ ├── contextual-retrieval.test.ts
│ │ ├── enhanced-manager.test.ts
│ │ ├── export-import.test.ts
│ │ ├── freshness-kg-integration.test.ts
│ │ ├── kg-code-integration.test.ts
│ │ ├── kg-health.test.ts
│ │ ├── kg-link-validator.test.ts
│ │ ├── kg-storage-validation.test.ts
│ │ ├── kg-storage.test.ts
│ │ ├── knowledge-graph-enhanced.test.ts
│ │ ├── knowledge-graph.test.ts
│ │ ├── learning.test.ts
│ │ ├── manager-advanced.test.ts
│ │ ├── manager.test.ts
│ │ ├── mcp-resource-integration.test.ts
│ │ ├── mcp-tool-persistence.test.ts
│ │ ├── schemas.test.ts
│ │ ├── storage.test.ts
│ │ ├── temporal-analysis.test.ts
│ │ └── user-preferences.test.ts
│ ├── performance
│ │ ├── memory-load-testing.test.ts
│ │ └── memory-stress-testing.test.ts
│ ├── prompts
│ │ ├── guided-workflow-prompts.test.ts
│ │ └── technical-writer-prompts.test.ts
│ ├── server.test.ts
│ ├── setup.ts
│ ├── tools
│ │ ├── all-tools.test.ts
│ │ ├── analyze-coverage.test.ts
│ │ ├── analyze-deployments.test.ts
│ │ ├── analyze-readme.test.ts
│ │ ├── analyze-repository.test.ts
│ │ ├── check-documentation-links.test.ts
│ │ ├── deploy-pages-kg-retrieval.test.ts
│ │ ├── deploy-pages-tracking.test.ts
│ │ ├── deploy-pages.test.ts
│ │ ├── detect-gaps.test.ts
│ │ ├── evaluate-readme-health.test.ts
│ │ ├── generate-contextual-content.test.ts
│ │ ├── generate-llm-context.test.ts
│ │ ├── generate-readme-template.test.ts
│ │ ├── generate-technical-writer-prompts.test.ts
│ │ ├── kg-health-check.test.ts
│ │ ├── manage-sitemap.test.ts
│ │ ├── optimize-readme.test.ts
│ │ ├── readme-best-practices.test.ts
│ │ ├── recommend-ssg-historical.test.ts
│ │ ├── recommend-ssg-preferences.test.ts
│ │ ├── recommend-ssg.test.ts
│ │ ├── simple-coverage.test.ts
│ │ ├── sync-code-to-docs.test.ts
│ │ ├── test-local-deployment.test.ts
│ │ ├── tool-error-handling.test.ts
│ │ ├── track-documentation-freshness.test.ts
│ │ ├── validate-content.test.ts
│ │ ├── validate-documentation-freshness.test.ts
│ │ └── validate-readme-checklist.test.ts
│ ├── types
│ │ └── type-safety.test.ts
│ └── utils
│ ├── ast-analyzer.test.ts
│ ├── content-extractor.test.ts
│ ├── drift-detector.test.ts
│ ├── freshness-tracker.test.ts
│ └── sitemap-generator.test.ts
├── tsconfig.json
└── typedoc.json
```
# Files
--------------------------------------------------------------------------------
/src/tools/validate-readme-checklist.ts:
--------------------------------------------------------------------------------
```typescript
import { z } from "zod";
import { promises as fs } from "fs";
// Input schema
export const ValidateReadmeChecklistSchema = z.object({
readmePath: z.string().min(1, "README path is required"),
projectPath: z.string().optional(),
strict: z.boolean().default(false),
outputFormat: z.enum(["json", "markdown", "console"]).default("console"),
});
export type ValidateReadmeChecklistInput = z.infer<
typeof ValidateReadmeChecklistSchema
>;
interface ChecklistItem {
id: string;
category: string;
name: string;
description: string;
required: boolean;
weight: number;
}
interface ValidationResult {
item: ChecklistItem;
passed: boolean;
details: string;
suggestions?: string[];
}
interface ChecklistReport {
overallScore: number;
totalItems: number;
passedItems: number;
failedItems: number;
categories: {
[category: string]: {
score: number;
passed: number;
total: number;
results: ValidationResult[];
};
};
recommendations: string[];
estimatedReadTime: number;
wordCount: number;
}
export class ReadmeChecklistValidator {
private checklist: ChecklistItem[] = [];
constructor() {
this.initializeChecklist();
}
private initializeChecklist(): void {
this.checklist = [
// Essential Sections
{
id: "title",
category: "Essential Sections",
name: "Project Title",
description: "Clear, descriptive project title as main heading",
required: true,
weight: 10,
},
{
id: "description",
category: "Essential Sections",
name: "Project Description",
description: "Brief one-liner describing what the project does",
required: true,
weight: 10,
},
{
id: "tldr",
category: "Essential Sections",
name: "TL;DR Section",
description: "2-3 sentence summary of the project",
required: true,
weight: 8,
},
{
id: "quickstart",
category: "Essential Sections",
name: "Quick Start Guide",
description: "Instructions to get running in under 5 minutes",
required: true,
weight: 10,
},
{
id: "installation",
category: "Essential Sections",
name: "Installation Instructions",
description: "Clear installation steps with code examples",
required: true,
weight: 9,
},
{
id: "usage",
category: "Essential Sections",
name: "Basic Usage Examples",
description: "Simple working code examples",
required: true,
weight: 9,
},
{
id: "license",
category: "Essential Sections",
name: "License Information",
description: "Clear license information",
required: true,
weight: 7,
},
// Community Health
{
id: "contributing",
category: "Community Health",
name: "Contributing Guidelines",
description: "Link to CONTRIBUTING.md or inline guidelines",
required: false,
weight: 6,
},
{
id: "code-of-conduct",
category: "Community Health",
name: "Code of Conduct",
description: "Link to CODE_OF_CONDUCT.md",
required: false,
weight: 4,
},
{
id: "security",
category: "Community Health",
name: "Security Policy",
description: "Link to SECURITY.md or security reporting info",
required: false,
weight: 4,
},
// Visual Elements
{
id: "badges",
category: "Visual Elements",
name: "Status Badges",
description: "Build status, version, license badges",
required: false,
weight: 3,
},
{
id: "screenshots",
category: "Visual Elements",
name: "Screenshots/Demos",
description: "Visual representation for applications/tools",
required: false,
weight: 5,
},
{
id: "formatting",
category: "Visual Elements",
name: "Consistent Formatting",
description: "Proper markdown formatting and structure",
required: true,
weight: 6,
},
// Content Quality
{
id: "working-examples",
category: "Content Quality",
name: "Working Code Examples",
description: "All code examples are functional and tested",
required: true,
weight: 8,
},
{
id: "external-links",
category: "Content Quality",
name: "Functional External Links",
description: "All external links are working",
required: true,
weight: 5,
},
{
id: "appropriate-length",
category: "Content Quality",
name: "Appropriate Length",
description: "README under 300 lines for community projects",
required: false,
weight: 4,
},
{
id: "scannable-structure",
category: "Content Quality",
name: "Scannable Structure",
description: "Good heading hierarchy and organization",
required: true,
weight: 7,
},
];
}
async validateReadme(
input: ValidateReadmeChecklistInput,
): Promise<ChecklistReport> {
const readmeContent = await fs.readFile(input.readmePath, "utf-8");
const projectFiles = input.projectPath
? await this.getProjectFiles(input.projectPath)
: [];
const results: ValidationResult[] = [];
const categories: { [key: string]: ValidationResult[] } = {};
// Run validation for each checklist item
for (const item of this.checklist) {
const result = await this.validateItem(
item,
readmeContent,
projectFiles,
input,
);
results.push(result);
if (!categories[item.category]) {
categories[item.category] = [];
}
categories[item.category].push(result);
}
return this.generateReport(results, readmeContent);
}
private async validateItem(
item: ChecklistItem,
content: string,
projectFiles: string[],
_input: ValidateReadmeChecklistInput,
): Promise<ValidationResult> {
let passed = false;
let details = "";
const suggestions: string[] = [];
switch (item.id) {
case "title": {
const titleRegex = /^#\s+.+/m;
const hasTitle = titleRegex.test(content);
passed = hasTitle;
details = passed
? "Project title found"
: "No main heading (# Title) found";
if (!passed)
suggestions.push(
"Add a clear project title as the first heading: # Your Project Name",
);
break;
}
case "description": {
const descRegex = /(^>\s+.+|^[^#\n].{20,})/m;
const hasDesc = descRegex.test(content);
passed = hasDesc;
details = passed
? "Project description found"
: "Missing project description";
if (!passed)
suggestions.push(
"Add a brief description using > quote syntax or paragraph after title",
);
break;
}
case "tldr": {
const tldrRegex = /##?\s*(tl;?dr|quick start|at a glance)/i;
const hasTldr = tldrRegex.test(content);
passed = hasTldr;
details = passed
? "TL;DR section found"
: "Missing TL;DR or quick overview";
if (!passed)
suggestions.push(
"Add a ## TL;DR section with 2-3 sentences explaining what your project does",
);
break;
}
case "quickstart":
passed = /##\s*(Quick\s*Start|Getting\s*Started|Installation)/i.test(
content,
);
details = passed
? "Quick start section found"
: "No quick start section found";
if (!passed)
suggestions.push(
"Add a ## Quick Start section with immediate setup instructions",
);
break;
case "installation": {
const installRegex = /##?\s*(install|installation|setup)/i;
const hasInstall = installRegex.test(content);
passed = hasInstall;
details = passed
? "Installation instructions found"
: "Missing installation instructions";
if (!passed)
suggestions.push(
"Add installation instructions with code blocks showing exact commands",
);
break;
}
case "usage": {
const usageRegex = /##?\s*(usage|example|getting started)/i;
const hasUsage = usageRegex.test(content);
passed = hasUsage;
details = passed ? "Usage examples found" : "Missing usage examples";
if (!passed)
suggestions.push("Add usage examples with working code snippets");
break;
}
case "license": {
const licenseRegex = /##?\s*license/i;
const hasLicense = licenseRegex.test(content);
const hasLicenseFile =
projectFiles.includes("LICENSE") ||
projectFiles.includes("LICENSE.md");
passed = hasLicense || hasLicenseFile;
details = passed
? "License information found"
: "Missing license information";
if (!passed)
suggestions.push("Add a ## License section or LICENSE file");
break;
}
case "contributing": {
const hasContributing = /##\s*Contribut/i.test(content);
const hasContributingFile = projectFiles.includes("CONTRIBUTING.md");
passed = hasContributing || hasContributingFile;
details = passed
? "Contributing guidelines found"
: "No contributing guidelines found";
if (!passed)
suggestions.push(
"Add contributing guidelines or link to CONTRIBUTING.md",
);
break;
}
case "code-of-conduct": {
const hasCodeOfConduct = /code.of.conduct/i.test(content);
const hasCodeFile = projectFiles.includes("CODE_OF_CONDUCT.md");
passed = hasCodeOfConduct || hasCodeFile;
details = passed ? "Code of conduct found" : "No code of conduct found";
break;
}
case "security": {
const hasSecurity = /security/i.test(content);
const hasSecurityFile = projectFiles.includes("SECURITY.md");
passed = hasSecurity || hasSecurityFile;
details = passed
? "Security information found"
: "No security policy found";
break;
}
case "badges": {
const badgeRegex =
/\[!\[.*?\]\(.*?\)\]\(.*?\)|!\[.*?\]\(.*?badge.*?\)/i;
const hasBadges = badgeRegex.test(content);
passed = hasBadges;
details = passed
? "Status badges found"
: "Consider adding status badges";
if (!passed)
suggestions.push(
"Consider adding badges for build status, version, license",
);
break;
}
case "screenshots": {
const imageRegex = /!\[.*?\]\(.*?\.(png|jpg|jpeg|gif|svg).*?\)/i;
const hasImages = imageRegex.test(content);
passed = hasImages;
details = passed
? "Screenshots/images found"
: "Consider adding screenshots or images";
if (!passed && content.includes("application")) {
suggestions.push(
"Consider adding screenshots or demo GIFs for visual applications",
);
}
break;
}
case "formatting": {
const hasHeaders = (content.match(/^##?\s+/gm) || []).length >= 3;
const hasProperSpacing = !/#{1,6}\s*\n\s*#{1,6}/.test(content);
passed = hasHeaders && hasProperSpacing;
details = passed
? "Good markdown formatting"
: "Improve markdown formatting and structure";
if (!passed)
suggestions.push(
"Improve markdown formatting with proper heading hierarchy and spacing",
);
break;
}
case "working-examples": {
const codeRegex = /```[\s\S]*?```/g;
const codeBlocks = content.match(codeRegex) || [];
passed = codeBlocks.length > 0;
details = `${codeBlocks.length} code examples found`;
if (!passed)
suggestions.push("Add working code examples to demonstrate usage");
break;
}
case "external-links": {
const links = content.match(/\[.*?\]\((https?:\/\/.*?)\)/g) || [];
passed = true; // Assume links work unless we can verify
details = `${links.length} external links found`;
break;
}
case "appropriate-length": {
const wordCount = content.split(/\s+/).length;
passed = wordCount <= 300;
details = `${wordCount} words (target: ≤300)`;
if (!passed)
suggestions.push(
"Consider shortening README or moving detailed content to separate docs",
);
break;
}
case "scannable-structure": {
const sections = (content.match(/^##?\s+/gm) || []).length;
const lists = (content.match(/^\s*[-*+]\s+/gm) || []).length;
passed = sections >= 3 && lists >= 2;
details = passed
? "Good scannable structure"
: "Improve structure with more sections and lists";
if (!passed)
suggestions.push(
"Improve heading structure with logical hierarchy (H1 → H2 → H3)",
);
break;
}
default:
passed = false;
details = "Validation not implemented";
}
return {
item,
passed,
details,
suggestions: suggestions.length > 0 ? suggestions : undefined,
};
}
private async getProjectFiles(projectPath: string): Promise<string[]> {
try {
const files = await fs.readdir(projectPath);
return files;
} catch {
return [];
}
}
private generateReport(
results: ValidationResult[],
content: string,
): ChecklistReport {
const categories: {
[category: string]: {
score: number;
passed: number;
total: number;
results: ValidationResult[];
};
} = {};
let totalWeight = 0;
let passedWeight = 0;
let passedItems = 0;
const totalItems = results.length;
// Group results by category and calculate scores
for (const result of results) {
const category = result.item.category;
if (!categories[category]) {
categories[category] = { score: 0, passed: 0, total: 0, results: [] };
}
categories[category].results.push(result);
categories[category].total++;
totalWeight += result.item.weight;
if (result.passed) {
categories[category].passed++;
passedWeight += result.item.weight;
passedItems++;
}
}
// Calculate category scores
for (const category in categories) {
const cat = categories[category];
cat.score = Math.round((cat.passed / cat.total) * 100);
}
const overallScore = Math.round((passedWeight / totalWeight) * 100);
const wordCount = content.split(/\s+/).length;
const estimatedReadTime = Math.ceil(wordCount / 200); // 200 words per minute
// Generate recommendations
const recommendations: string[] = [];
if (overallScore < 70) {
recommendations.push(
"README needs significant improvement to meet community standards",
);
}
if (categories["Essential Sections"]?.score < 80) {
recommendations.push("Focus on completing essential sections first");
}
if (wordCount > 2000) {
recommendations.push(
"Consider breaking up content into separate documentation files",
);
}
if (!results.find((r) => r.item.id === "badges")?.passed) {
recommendations.push("Add status badges to improve project credibility");
}
return {
overallScore,
totalItems,
passedItems,
failedItems: totalItems - passedItems,
categories,
recommendations,
estimatedReadTime,
wordCount,
};
}
formatReport(
report: ChecklistReport,
format: "json" | "markdown" | "console",
): string {
switch (format) {
case "json":
return JSON.stringify(report, null, 2);
case "markdown":
return this.formatMarkdownReport(report);
case "console":
default:
return this.formatConsoleReport(report);
}
}
private formatMarkdownReport(report: ChecklistReport): string {
let output = "# README Checklist Report\n\n";
output += `## Overall Score: ${report.overallScore}%\n\n`;
output += `- **Passed**: ${report.passedItems}/${report.totalItems} items\n`;
output += `- **Word Count**: ${report.wordCount} words\n`;
output += `- **Estimated Read Time**: ${report.estimatedReadTime} minutes\n\n`;
output += "## Category Breakdown\n\n";
for (const [categoryName, category] of Object.entries(report.categories)) {
output += `### ${categoryName} (${category.score}%)\n\n`;
for (const result of category.results) {
const status = result.passed ? "✅" : "❌";
output += `- ${status} **${result.item.name}**: ${result.details}\n`;
if (result.suggestions) {
for (const suggestion of result.suggestions) {
output += ` - 💡 ${suggestion}\n`;
}
}
}
output += "\n";
}
if (report.recommendations.length > 0) {
output += "## Recommendations\n\n";
for (const rec of report.recommendations) {
output += `- ${rec}\n`;
}
}
return output;
}
private formatConsoleReport(report: ChecklistReport): string {
let output = "\n📋 README Checklist Report\n";
output += "=".repeat(50) + "\n";
const scoreColor =
report.overallScore >= 80
? "🟢"
: report.overallScore >= 60
? "🟡"
: "🔴";
output += `${scoreColor} Overall Score: ${report.overallScore}%\n`;
output += `✅ Passed: ${report.passedItems}/${report.totalItems} items\n`;
output += `📄 Word Count: ${report.wordCount} words\n`;
output += `⏱️ Read Time: ${report.estimatedReadTime} minutes\n\n`;
for (const [categoryName, category] of Object.entries(report.categories)) {
const catColor =
category.score >= 80 ? "🟢" : category.score >= 60 ? "🟡" : "🔴";
output += `${catColor} ${categoryName} (${category.score}%)\n`;
output += "-".repeat(30) + "\n";
for (const result of category.results) {
const status = result.passed ? "✅" : "❌";
output += `${status} ${result.item.name}: ${result.details}\n`;
if (result.suggestions) {
for (const suggestion of result.suggestions) {
output += ` 💡 ${suggestion}\n`;
}
}
}
output += "\n";
}
if (report.recommendations.length > 0) {
output += "🎯 Recommendations:\n";
for (const rec of report.recommendations) {
output += `• ${rec}\n`;
}
}
return output;
}
}
export async function validateReadmeChecklist(
input: ValidateReadmeChecklistInput,
): Promise<ChecklistReport> {
const validatedInput = ValidateReadmeChecklistSchema.parse(input);
const validator = new ReadmeChecklistValidator();
return await validator.validateReadme(validatedInput);
}
```
--------------------------------------------------------------------------------
/docs/adrs/009-content-accuracy-validation-framework.md:
--------------------------------------------------------------------------------
```markdown
---
id: 009-content-accuracy-validation-framework
title: "ADR-009: Content Accuracy Validation Framework"
sidebar_label: "ADR-9: Content Accuracy Validation Framework"
sidebar_position: 9
documcp:
last_updated: "2025-11-20T00:46:21.943Z"
last_validated: "2025-11-20T00:46:21.943Z"
auto_updated: false
update_frequency: monthly
---
# ADR-009: Content Accuracy and Validation Framework for Generated Documentation
## Status
Accepted
## Context
The Intelligent Content Population Engine (ADR-008) introduces sophisticated content generation capabilities, but with this power comes the critical challenge of ensuring content accuracy and handling scenarios where generated documentation is incorrect, outdated, or missing crucial context. This represents a fundamental risk to user trust and system adoption.
**Core Problem**: Automated content generation can fail in multiple ways:
- **Analysis Misinterpretation**: Repository analysis detects Express.js but project primarily uses GraphQL
- **Outdated Patterns**: Generated content assumes current best practices for deprecated framework versions
- **Missing Context**: Analysis cannot understand business domain, team conventions, or architectural constraints
- **Code Reality Mismatch**: Generated examples don't work with actual project structure
- **Confidence Overstatement**: System appears confident about uncertain conclusions
**Real-World Scenarios**:
1. Analysis detects PostgreSQL in docker-compose but app actually uses MongoDB in production
2. TypeScript project generates JavaScript examples due to build artifact analysis
3. Monorepo analysis sees partial picture, generating incomplete architectural guidance
4. Custom framework wrappers confuse standard pattern detection
5. Legacy code patterns generate deprecated recommendation content
**Current State**: ADR-008 includes basic content validation but lacks comprehensive accuracy assurance, user correction workflows, and systematic approaches to handling uncertainty and missing information.
**Strategic Importance**: Content accuracy directly impacts:
- User trust and adoption rates
- Time savings vs. time wasted on incorrect guidance
- System credibility in professional development environments
- Long-term viability as intelligent documentation assistant
## Decision
We will implement a comprehensive Content Accuracy and Validation Framework that treats content correctness as a first-class architectural concern, with systematic approaches to uncertainty management, reality verification, and continuous accuracy improvement.
### Framework Architecture:
#### 1. Multi-Layer Validation System
**Purpose**: Systematic verification at multiple stages of content generation
**Layers**:
- **Pre-Generation Validation**: Verify analysis accuracy before content creation
- **Generation-Time Validation**: Real-time checks during content assembly
- **Post-Generation Validation**: Comprehensive verification against project reality
- **User-Guided Validation**: Interactive accuracy confirmation and correction
#### 2. Confidence-Aware Content Generation
**Purpose**: Explicit uncertainty management and confidence scoring
**Capabilities**:
- Granular confidence metrics for different content aspects
- Uncertainty flagging for areas requiring user verification
- Content degradation strategies when confidence is insufficient
- Alternative content paths for ambiguous scenarios
#### 3. Reality-Check Validation Engine
**Purpose**: Verify generated content against actual project characteristics
**Verification Types**:
- Code example compilation and execution validation
- Pattern existence verification in actual codebase
- Dependency version compatibility checking
- Framework usage pattern matching
#### 4. Interactive Accuracy Workflow
**Purpose**: User-guided accuracy improvement and correction
**Components**:
- Pre-generation clarification requests for uncertain areas
- Inline content correction and improvement interfaces
- Accuracy feedback collection and learning system
- Project-specific accuracy profile building
### Implementation Details:
#### Confidence-Aware Generation System
```typescript
interface ConfidenceAwareGenerator {
generateWithConfidence(
contentRequest: ContentRequest,
projectContext: ProjectContext,
): ConfidenceAwareContent;
handleUncertainty(
uncertainty: UncertaintyArea,
alternatives: ContentAlternative[],
): UncertaintyHandlingStrategy;
degradeContentSafely(
highRiskContent: GeneratedContent,
safetyThreshold: number,
): SaferContent;
}
interface ConfidenceAwareContent {
content: GeneratedContent;
confidence: ConfidenceMetrics;
uncertainties: UncertaintyFlag[];
validationRequests: ValidationRequest[];
alternatives: ContentAlternative[];
}
interface ConfidenceMetrics {
overall: number; // 0-100
breakdown: {
technologyDetection: number;
frameworkVersionAccuracy: number;
codeExampleRelevance: number;
architecturalAssumptions: number;
businessContextAlignment: number;
};
riskFactors: RiskFactor[];
}
interface UncertaintyFlag {
area: UncertaintyArea;
severity: "low" | "medium" | "high" | "critical";
description: string;
potentialImpact: string;
clarificationNeeded: string;
fallbackStrategy: string;
}
```
#### Reality-Check Validation Engine
```typescript
interface RealityCheckValidator {
// Validate against actual project structure and code
validateAgainstCodebase(
content: GeneratedContent,
projectPath: string,
): Promise<ValidationResult>;
// Check if generated code examples actually work
validateCodeExamples(
examples: CodeExample[],
projectContext: ProjectContext,
): Promise<CodeValidationResult>;
// Verify framework patterns exist in project
verifyFrameworkPatterns(
patterns: FrameworkPattern[],
projectFiles: ProjectFile[],
): PatternValidationResult;
// Check dependency compatibility
validateDependencyCompatibility(
suggestions: DependencySuggestion[],
projectManifest: ProjectManifest,
): CompatibilityResult;
}
interface ValidationResult {
isValid: boolean;
confidence: number;
issues: ValidationIssue[];
suggestions: ImprovementSuggestion[];
corrections: AutomaticCorrection[];
}
interface ValidationIssue {
type: IssueType;
severity: "error" | "warning" | "info";
location: ContentLocation;
description: string;
evidence: Evidence[];
suggestedFix: string;
confidence: number;
}
class TypeScriptRealityChecker implements RealityCheckValidator {
async validateCodeExamples(
examples: CodeExample[],
projectContext: ProjectContext,
): Promise<CodeValidationResult> {
const results: ExampleValidation[] = [];
for (const example of examples) {
try {
// Create temporary test file
const testFile = await this.createTestFile(example, projectContext);
// Attempt TypeScript compilation
const compileResult = await this.compileTypeScript(testFile);
// Run basic execution test if compilation succeeds
const executionResult = compileResult.success
? await this.testExecution(testFile)
: null;
results.push({
example: example.id,
compilationSuccess: compileResult.success,
executionSuccess: executionResult?.success ?? false,
issues: [...compileResult.errors, ...(executionResult?.errors ?? [])],
confidence: this.calculateExampleConfidence(
compileResult,
executionResult,
),
});
} catch (error) {
results.push({
example: example.id,
compilationSuccess: false,
executionSuccess: false,
issues: [{ type: "validation_error", message: error.message }],
confidence: 0,
});
}
}
return {
overallSuccess: results.every((r) => r.compilationSuccess),
exampleResults: results,
confidence: this.calculateOverallConfidence(results),
};
}
}
```
#### Interactive Accuracy Workflow
```typescript
interface InteractiveAccuracyWorkflow {
// Pre-generation clarification
requestClarification(
uncertainties: UncertaintyFlag[],
analysisContext: AnalysisContext,
): Promise<UserClarification>;
// Real-time accuracy feedback during generation
enableRealTimeFeedback(
generationSession: GenerationSession,
): AccuracyFeedbackInterface;
// Post-generation correction and improvement
facilitateCorrections(
generatedContent: GeneratedContent,
userContext: UserContext,
): CorrectionInterface;
// Learning from corrections
recordAccuracyLearning(
original: GeneratedContent,
corrected: GeneratedContent,
userFeedback: UserFeedback,
): AccuracyLearning;
}
interface UserClarification {
uncertaintyArea: UncertaintyArea;
userResponse: string;
confidence: number;
additionalContext?: string;
}
interface CorrectionInterface {
// Inline editing capabilities
enableInlineEditing(content: GeneratedContent): EditableContent;
// Structured feedback collection
collectStructuredFeedback(
content: GeneratedContent,
): Promise<StructuredFeedback>;
// Quick accuracy rating
requestAccuracyRating(
contentSection: ContentSection,
): Promise<AccuracyRating>;
// Pattern correction learning
identifyPatternCorrections(
corrections: ContentCorrection[],
): PatternLearning[];
}
```
#### Fallback and Recovery Strategies
```typescript
interface ContentFallbackStrategy {
// Progressive content degradation
degradeToSaferContent(
failedContent: GeneratedContent,
validationFailures: ValidationFailure[],
): SaferContent;
// Multiple alternative generation
generateAlternatives(
contentRequest: ContentRequest,
primaryFailure: GenerationFailure,
): ContentAlternative[];
// Graceful uncertainty handling
handleInsufficientInformation(
analysisGaps: AnalysisGap[],
contentRequirements: ContentRequirement[],
): PartialContent;
// Safe default content
provideSafeDefaults(
projectType: ProjectType,
framework: Framework,
confidence: number,
): DefaultContent;
}
interface SafetyThresholds {
minimumConfidenceForCodeExamples: 85;
minimumConfidenceForArchitecturalAdvice: 75;
minimumConfidenceForProductionGuidance: 90;
uncertaintyThresholdForUserConfirmation: 70;
}
const fallbackHierarchy = [
{
level: "project-specific-optimized",
confidence: 85,
description: "Highly confident project-specific content",
},
{
level: "framework-specific-validated",
confidence: 95,
description: "Framework patterns validated against project",
},
{
level: "technology-generic-safe",
confidence: 98,
description: "Generic patterns known to work",
},
{
level: "diataxis-structure-only",
confidence: 100,
description: "Structure with clear placeholders for manual completion",
},
];
```
## Alternatives Considered
### Trust-But-Verify Approach (Basic Validation Only)
- **Pros**: Simpler implementation, faster content generation, less user friction
- **Cons**: High risk of incorrect content, potential user frustration, system credibility damage
- **Decision**: Rejected - accuracy is fundamental to system value proposition
### AI-Only Validation (External LLM Review)
- **Pros**: Advanced natural language understanding, sophisticated error detection
- **Cons**: External dependencies, costs, latency, inconsistent results, black box validation
- **Decision**: Rejected for primary validation - may integrate as supplementary check
### Manual Review Required (Human-in-the-Loop Always)
- **Pros**: Maximum accuracy assurance, user control, learning opportunities
- **Cons**: Eliminates automation benefits, slows workflow, high user burden
- **Decision**: Rejected as default - integrate as optional high-accuracy mode
### Static Analysis Only (No Dynamic Validation)
- **Pros**: Fast execution, no code execution risks, consistent results
- **Cons**: Misses runtime issues, limited pattern verification, poor accuracy detection
- **Decision**: Rejected as sole approach - integrate as first-pass validation
### Crowdsourced Accuracy (Community Validation)
- **Pros**: Diverse perspectives, real-world validation, community engagement
- **Cons**: Inconsistent quality, coordination complexity, slow feedback loops
- **Decision**: Deferred to future enhancement - focus on systematic validation first
## Consequences
### Positive
- **Trust and Credibility**: Systematic accuracy assurance builds user confidence
- **Reduced Risk**: Explicit uncertainty handling prevents misleading guidance
- **Continuous Improvement**: Learning from corrections improves future accuracy
- **Professional Reliability**: Reality-check validation ensures professional-grade output
- **User Empowerment**: Interactive workflows give users control over accuracy
### Negative
- **Implementation Complexity**: Multi-layer validation requires significant engineering effort
- **Performance Impact**: Validation processes may slow content generation
- **User Experience Friction**: Clarification requests may interrupt workflow
- **Maintenance Overhead**: Validation rules require updates as technologies evolve
### Risks and Mitigations
- **Validation Accuracy**: Validate the validators through comprehensive testing
- **Performance Impact**: Implement parallel validation and smart caching
- **User Fatigue**: Balance accuracy requests with workflow efficiency
- **Technology Coverage**: Start with well-known patterns, expand methodically
## Integration Points
### Repository Analysis Integration (ADR-002)
- Use analysis confidence metrics to inform content generation confidence
- Validate analysis assumptions against actual project characteristics
- Identify analysis gaps that require user clarification
### Content Population Integration (ADR-008)
- Integrate validation framework into content generation pipeline
- Use confidence metrics to guide content generation strategies
- Apply reality-check validation to all generated content
### MCP Tools API Integration (ADR-006)
- Add validation results to MCP tool responses
- Provide user interfaces for accuracy feedback and correction
- Maintain consistency with existing error handling patterns
### Diataxis Framework Integration (ADR-004)
- Ensure validation preserves Diataxis category integrity
- Validate content type appropriateness within framework
- Maintain cross-reference accuracy across content categories
## Implementation Roadmap
### Phase 1: Core Validation Infrastructure (High Priority)
- Confidence scoring system implementation
- Basic reality-check validation for common patterns
- User clarification workflow for high-uncertainty areas
- Fallback content generation strategies
### Phase 2: Advanced Validation (Medium Priority)
- Code example compilation and execution testing
- Framework pattern existence verification
- Interactive correction interfaces
- Accuracy learning and improvement systems
### Phase 3: Intelligent Accuracy Features (Future)
- Machine learning-based accuracy prediction
- Community-driven validation and improvement
- Advanced uncertainty reasoning and handling
- Personalized accuracy preferences and thresholds
## Quality Assurance
### Validation Testing Framework
```typescript
describe("ContentAccuracyFramework", () => {
describe("Confidence Scoring", () => {
it("should correctly identify low-confidence scenarios");
it("should provide appropriate uncertainty flags");
it("should degrade content safely when confidence is insufficient");
});
describe("Reality-Check Validation", () => {
it("should detect when generated code examples fail compilation");
it("should identify pattern mismatches with actual codebase");
it("should validate dependency compatibility accurately");
});
describe("Interactive Workflows", () => {
it("should request clarification for appropriate uncertainty levels");
it("should enable effective user corrections and learning");
it("should maintain accuracy improvements across sessions");
});
});
```
### Accuracy Metrics and Monitoring
- **Content Accuracy Rate**: Percentage of generated content validated as correct
- **User Correction Rate**: Frequency of user corrections per content section
- **Confidence Calibration**: Alignment between confidence scores and actual accuracy
- **Validation Performance**: Speed and accuracy of validation processes
### Continuous Improvement Process
- Regular validation of validation systems (meta-validation)
- User feedback analysis and pattern identification
- Technology pattern database updates and maintenance
- Accuracy threshold tuning based on real-world usage
## Success Metrics
### Accuracy Metrics
- **Content Accuracy Rate**: 85%+ technical accuracy for generated content
- **Confidence Calibration**: ±10% alignment between confidence and actual accuracy
- **False Positive Rate**: <5% validation failures for actually correct content
- **User Correction Rate**: <20% of content sections require user correction
### User Experience Metrics
- **Trust Score**: 90%+ user confidence in system accuracy
- **Workflow Efficiency**: Validation processes add <15% to generation time
- **Clarification Effectiveness**: 80%+ of clarification requests improve accuracy
- **Learning Effectiveness**: 70% reduction in repeat accuracy issues
### System Reliability Metrics
- **Validation Coverage**: 95%+ of generated content passes through validation
- **Fallback Effectiveness**: 100% of failed generations provide safe alternatives
- **Error Recovery**: 90%+ of validation failures result in improved content
- **Performance Impact**: <30 seconds total for accuracy-validated content generation
## Future Enhancements
### Advanced Validation Technologies
- **Static Analysis Integration**: Deeper code analysis for pattern verification
- **Dynamic Testing**: Automated testing of generated examples in project context
- **Semantic Validation**: AI-powered understanding of content meaning and correctness
- **Cross-Project Learning**: Accuracy improvements shared across similar projects
### User Experience Improvements
- **Accuracy Preferences**: User-configurable accuracy vs. speed trade-offs
- **Domain-Specific Validation**: Specialized validation for different technical domains
- **Real-Time Collaboration**: Team-based accuracy review and improvement workflows
- **Accuracy Analytics**: Detailed insights into content accuracy patterns and trends
### Integration Expansions
- **IDE Integration**: Real-time accuracy feedback in development environments
- **CI/CD Integration**: Accuracy validation as part of documentation deployment
- **Documentation Management**: Integration with existing documentation systems
- **Quality Metrics**: Accuracy tracking as part of documentation quality scoring
## References
- [ADR-002: Multi-Layered Repository Analysis Engine Design](002-repository-analysis-engine.md)
- [ADR-008: Intelligent Content Population Engine](008-intelligent-content-population-engine.md)
- [Software Verification and Validation](https://en.wikipedia.org/wiki/Software_verification_and_validation)
- [Web Content Accessibility Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)
- [AI Documentation Best Practices](https://developers.google.com/machine-learning/guides/rules-of-ml)
```
--------------------------------------------------------------------------------
/src/utils/code-scanner.ts:
--------------------------------------------------------------------------------
```typescript
import { parse } from "@typescript-eslint/typescript-estree";
import { globby } from "globby";
import { promises as fs } from "fs";
import path from "path";
import {
MultiLanguageCodeScanner,
LanguageParseResult,
} from "./language-parsers-simple.js";
export interface CodeElement {
name: string;
type:
| "function"
| "class"
| "interface"
| "type"
| "enum"
| "variable"
| "export"
| "import";
filePath: string;
line: number;
column: number;
exported: boolean;
isAsync?: boolean;
isPrivate?: boolean;
hasJSDoc?: boolean;
jsDocDescription?: string;
parameters?: string[];
returnType?: string;
decorators?: string[];
}
export interface APIEndpoint {
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "ALL";
path: string;
filePath: string;
line: number;
handlerName?: string;
hasDocumentation?: boolean;
}
export interface CodeAnalysisResult {
functions: CodeElement[];
classes: CodeElement[];
interfaces: CodeElement[];
types: CodeElement[];
enums: CodeElement[];
exports: CodeElement[];
imports: CodeElement[];
apiEndpoints: APIEndpoint[];
constants: CodeElement[];
variables: CodeElement[];
hasTests: boolean;
testFiles: string[];
configFiles: string[];
packageManagers: string[];
frameworks: string[];
dependencies: string[];
devDependencies: string[];
supportedLanguages: string[];
}
export class CodeScanner {
private rootPath: string;
private multiLanguageScanner: MultiLanguageCodeScanner;
constructor(rootPath: string) {
this.rootPath = rootPath;
this.multiLanguageScanner = new MultiLanguageCodeScanner();
}
/**
* Performs comprehensive code analysis of the repository.
*
* Analyzes all source files in the repository to extract code elements including
* functions, classes, interfaces, API endpoints, and framework detection. Uses
* multi-language parsing to support various programming languages and provides
* detailed metadata about each code element including documentation status.
*
* @returns Promise resolving to comprehensive code analysis results
* @returns functions - Array of discovered functions with metadata
* @returns classes - Array of discovered classes with metadata
* @returns interfaces - Array of discovered interfaces with metadata
* @returns apiEndpoints - Array of discovered API endpoints
* @returns frameworks - Array of detected frameworks and libraries
* @returns hasTests - Boolean indicating if test files are present
*
* @throws {Error} When repository analysis fails
* @throws {Error} When file system access is denied
*
* @example
* ```typescript
* const scanner = new CodeScanner("/path/to/repository");
* const analysis = await scanner.analyzeRepository();
* console.log(`Found ${analysis.functions.length} functions`);
* console.log(`Detected frameworks: ${analysis.frameworks.join(', ')}`);
* ```
*
* @since 1.0.0
*/
async analyzeRepository(): Promise<CodeAnalysisResult> {
const result: CodeAnalysisResult = {
functions: [],
classes: [],
interfaces: [],
types: [],
enums: [],
exports: [],
imports: [],
apiEndpoints: [],
constants: [],
variables: [],
hasTests: false,
testFiles: [],
configFiles: [],
packageManagers: [],
frameworks: [],
dependencies: [],
devDependencies: [],
supportedLanguages: [],
};
// Find all relevant files (now including Python, Go, YAML, Bash)
const codeFiles = await this.findCodeFiles();
const configFiles = await this.findConfigFiles();
const testFiles = await this.findTestFiles();
result.configFiles = configFiles;
result.testFiles = testFiles;
result.hasTests = testFiles.length > 0;
result.supportedLanguages =
this.multiLanguageScanner.getSupportedExtensions();
// Analyze package.json for dependencies and frameworks
await this.analyzePackageJson(result);
// Analyze code files with multi-language support
for (const filePath of codeFiles) {
try {
await this.analyzeFile(filePath, result);
} catch (error) {
// Skip files that can't be parsed (e.g., invalid syntax)
console.warn(`Failed to analyze ${filePath}:`, error);
}
}
return result;
}
private async findCodeFiles(): Promise<string[]> {
const patterns = [
// TypeScript/JavaScript
"**/*.ts",
"**/*.tsx",
"**/*.js",
"**/*.jsx",
"**/*.mjs",
"**/*.cjs",
// Python
"**/*.py",
"**/*.pyi",
"**/*.pyx",
"**/*.pxd",
// Go
"**/*.go",
// YAML/Config
"**/*.yml",
"**/*.yaml",
// Shell scripts
"**/*.sh",
"**/*.bash",
"**/*.zsh",
// Exclusions
"!**/node_modules/**",
"!**/dist/**",
"!**/build/**",
"!**/.git/**",
"!**/coverage/**",
"!**/*.min.js",
"!**/venv/**",
"!**/__pycache__/**",
"!**/vendor/**",
];
return await globby(patterns, { cwd: this.rootPath, absolute: true });
}
private async findConfigFiles(): Promise<string[]> {
const patterns = [
// JavaScript/Node.js configs
"package.json",
"tsconfig.json",
"jsconfig.json",
".eslintrc*",
"prettier.config.*",
"webpack.config.*",
"vite.config.*",
"rollup.config.*",
"babel.config.*",
"next.config.*",
"nuxt.config.*",
"vue.config.*",
"svelte.config.*",
"tailwind.config.*",
"jest.config.*",
"vitest.config.*",
// Python configs
"setup.py",
"setup.cfg",
"pyproject.toml",
"requirements*.txt",
"Pipfile",
"poetry.lock",
"tox.ini",
"pytest.ini",
".flake8",
"mypy.ini",
// Go configs
"go.mod",
"go.sum",
"Makefile",
// Container configs
"dockerfile*",
"docker-compose*",
".dockerignore",
"Containerfile*",
// Kubernetes configs
"k8s/**/*.yml",
"k8s/**/*.yaml",
"kubernetes/**/*.yml",
"kubernetes/**/*.yaml",
"manifests/**/*.yml",
"manifests/**/*.yaml",
// Terraform configs
"**/*.tf",
"**/*.tfvars",
"terraform.tfstate*",
// CI/CD configs
".github/workflows/*.yml",
".github/workflows/*.yaml",
".gitlab-ci.yml",
"Jenkinsfile",
".circleci/config.yml",
// Ansible configs
"ansible.cfg",
"playbook*.yml",
"inventory*",
// Cloud configs
"serverless.yml",
"sam.yml",
"template.yml",
"cloudformation*.yml",
"pulumi*.yml",
];
return await globby(patterns, {
cwd: this.rootPath,
absolute: true,
caseSensitiveMatch: false,
});
}
private async findTestFiles(): Promise<string[]> {
const patterns = [
// JavaScript/TypeScript tests
"**/*.test.ts",
"**/*.test.tsx",
"**/*.test.js",
"**/*.test.jsx",
"**/*.spec.ts",
"**/*.spec.tsx",
"**/*.spec.js",
"**/*.spec.jsx",
"**/test/**/*.ts",
"**/test/**/*.js",
"**/tests/**/*.ts",
"**/tests/**/*.js",
"**/__tests__/**/*.ts",
"**/__tests__/**/*.js",
// Python tests
"**/*_test.py",
"**/test_*.py",
"**/tests/**/*.py",
"**/test/**/*.py",
// Go tests
"**/*_test.go",
// Shell script tests
"**/test*.sh",
"**/tests/**/*.sh",
"!**/node_modules/**",
"!**/venv/**",
"!**/vendor/**",
];
return await globby(patterns, { cwd: this.rootPath, absolute: true });
}
private async analyzePackageJson(result: CodeAnalysisResult): Promise<void> {
try {
const packageJsonPath = path.join(this.rootPath, "package.json");
const packageJsonContent = await fs.readFile(packageJsonPath, "utf-8");
const packageJson = JSON.parse(packageJsonContent);
// Extract dependencies
if (packageJson.dependencies) {
result.dependencies = Object.keys(packageJson.dependencies);
}
if (packageJson.devDependencies) {
result.devDependencies = Object.keys(packageJson.devDependencies);
}
// Detect package managers
const allDeps = [...result.dependencies, ...result.devDependencies];
if (allDeps.some((dep) => dep.startsWith("@npm/")))
result.packageManagers.push("npm");
if (allDeps.some((dep) => dep.includes("yarn")))
result.packageManagers.push("yarn");
if (allDeps.some((dep) => dep.includes("pnpm")))
result.packageManagers.push("pnpm");
// Check for scripts that might indicate package managers
if (packageJson.scripts) {
const scripts = Object.keys(packageJson.scripts).join(" ");
if (scripts.includes("yarn")) result.packageManagers.push("yarn");
if (scripts.includes("pnpm")) result.packageManagers.push("pnpm");
}
// Detect frameworks (expanded for your DevOps/Cloud stack)
const frameworkMap: Record<string, string[]> = {
// Web Frameworks
React: ["react", "@types/react"],
Vue: ["vue", "@vue/core"],
Angular: ["@angular/core"],
Svelte: ["svelte"],
"Next.js": ["next"],
Nuxt: ["nuxt"],
Express: ["express"],
Fastify: ["fastify"],
Koa: ["koa"],
NestJS: ["@nestjs/core"],
// Build Tools
Vite: ["vite"],
Webpack: ["webpack"],
Rollup: ["rollup"],
// Testing
Jest: ["jest"],
Vitest: ["vitest"],
Playwright: ["@playwright/test"],
Cypress: ["cypress"],
// Cloud/DevOps (Python)
Flask: ["flask"],
Django: ["django"],
FastAPI: ["fastapi"],
Ansible: ["ansible"],
Boto3: ["boto3"],
// Infrastructure
"AWS CDK": ["aws-cdk-lib", "@aws-cdk/core"],
Pulumi: ["@pulumi/pulumi"],
"Terraform CDK": ["cdktf"],
};
for (const [framework, deps] of Object.entries(frameworkMap)) {
if (deps.some((dep) => allDeps.includes(dep))) {
result.frameworks.push(framework);
}
}
} catch (error) {
// package.json not found or invalid
}
}
private async analyzeFile(
filePath: string,
result: CodeAnalysisResult,
): Promise<void> {
try {
const content = await fs.readFile(filePath, "utf-8");
const relativePath = path.relative(this.rootPath, filePath);
const extension = path.extname(filePath).slice(1).toLowerCase();
// Try multi-language parsing first
if (
this.multiLanguageScanner.getSupportedExtensions().includes(extension)
) {
const parseResult = await this.multiLanguageScanner.parseFile(
content,
filePath,
);
this.mergeParseResults(parseResult, result);
}
// Fall back to TypeScript/JavaScript parsing for .ts/.js files
else if (["ts", "tsx", "js", "jsx", "mjs", "cjs"].includes(extension)) {
await this.analyzeTypeScriptFile(content, relativePath, result);
}
// Otherwise skip or warn
else {
console.warn(`Unsupported file type: ${filePath}`);
}
} catch (error) {
// File parsing failed - could be due to syntax errors or unsupported syntax
console.warn(`Failed to parse file ${filePath}:`, error);
}
}
private async analyzeTypeScriptFile(
content: string,
relativePath: string,
result: CodeAnalysisResult,
): Promise<void> {
try {
// Parse with TypeScript-ESLint parser
const ast = parse(content, {
filePath: relativePath,
sourceType: "module",
ecmaVersion: 2022,
ecmaFeatures: {
jsx: true,
},
comment: true,
range: true,
loc: true,
});
// Traverse AST to extract code elements
this.traverseAST(ast, result, relativePath);
// Look for API endpoints in the content
this.findAPIEndpoints(content, result, relativePath);
} catch (error) {
throw new Error(
`TypeScript parsing failed for ${relativePath}: ${error}`,
);
}
}
private mergeParseResults(
parseResult: LanguageParseResult,
result: CodeAnalysisResult,
): void {
result.functions.push(...parseResult.functions);
result.classes.push(...parseResult.classes);
result.interfaces.push(...parseResult.interfaces);
result.types.push(...parseResult.types);
result.enums.push(...parseResult.enums);
result.exports.push(...parseResult.exports);
result.imports.push(...parseResult.imports);
result.apiEndpoints.push(...parseResult.apiEndpoints);
result.constants.push(...parseResult.constants);
result.variables.push(...parseResult.variables);
}
private traverseAST(
node: any,
result: CodeAnalysisResult,
filePath: string,
isInExport = false,
): void {
if (!node || typeof node !== "object") return;
const line = node.loc?.start?.line || 0;
const column = node.loc?.start?.column || 0;
switch (node.type) {
case "FunctionDeclaration":
if (node.id?.name) {
result.functions.push({
name: node.id.name,
type: "function",
filePath,
line,
column,
exported: isInExport || this.isExported(node),
isAsync: node.async,
hasJSDoc: this.hasJSDoc(node),
jsDocDescription: this.getJSDocDescription(node),
parameters: node.params?.map((p: any) => p.name || "param") || [],
});
}
break;
case "ClassDeclaration":
if (node.id?.name) {
result.classes.push({
name: node.id.name,
type: "class",
filePath,
line,
column,
exported: isInExport || this.isExported(node),
hasJSDoc: this.hasJSDoc(node),
jsDocDescription: this.getJSDocDescription(node),
decorators:
node.decorators?.map(
(d: any) => d.expression?.name || "decorator",
) || [],
});
}
break;
case "TSInterfaceDeclaration":
if (node.id?.name) {
result.interfaces.push({
name: node.id.name,
type: "interface",
filePath,
line,
column,
exported: isInExport || this.isExported(node),
hasJSDoc: this.hasJSDoc(node),
jsDocDescription: this.getJSDocDescription(node),
});
}
break;
case "TSTypeAliasDeclaration":
if (node.id?.name) {
result.types.push({
name: node.id.name,
type: "type",
filePath,
line,
column,
exported: isInExport || this.isExported(node),
hasJSDoc: this.hasJSDoc(node),
jsDocDescription: this.getJSDocDescription(node),
});
}
break;
case "TSEnumDeclaration":
if (node.id?.name) {
result.enums.push({
name: node.id.name,
type: "enum",
filePath,
line,
column,
exported: isInExport || this.isExported(node),
hasJSDoc: this.hasJSDoc(node),
jsDocDescription: this.getJSDocDescription(node),
});
}
break;
case "ExportNamedDeclaration":
case "ExportDefaultDeclaration":
// Handle exports
if (node.declaration) {
this.traverseAST(node.declaration, result, filePath, true);
}
if (node.specifiers) {
node.specifiers.forEach((spec: any) => {
if (spec.exported?.name) {
result.exports.push({
name: spec.exported.name,
type: "export",
filePath,
line,
column,
exported: true,
});
}
});
}
break;
case "ImportDeclaration":
if (node.source?.value) {
result.imports.push({
name: node.source.value,
type: "import",
filePath,
line,
column,
exported: false,
});
}
break;
}
// Recursively traverse child nodes
for (const key in node) {
if (key === "parent" || key === "loc" || key === "range") continue;
const child = node[key];
if (Array.isArray(child)) {
child.forEach((c) => this.traverseAST(c, result, filePath, isInExport));
} else if (child && typeof child === "object") {
this.traverseAST(child, result, filePath, isInExport);
}
}
}
private findAPIEndpoints(
content: string,
result: CodeAnalysisResult,
filePath: string,
): void {
// Common patterns for API endpoints
const patterns = [
// Express/Fastify/Koa patterns
/\.(get|post|put|delete|patch|all)\s*\(\s*['"`]([^'"`]+)['"`]/gi,
// Router patterns
/router\.(get|post|put|delete|patch|all)\s*\(\s*['"`]([^'"`]+)['"`]/gi,
// App patterns
/app\.(get|post|put|delete|patch|all)\s*\(\s*['"`]([^'"`]+)['"`]/gi,
];
const lines = content.split("\n");
patterns.forEach((pattern) => {
let match;
while ((match = pattern.exec(content)) !== null) {
const method = match[1].toUpperCase() as APIEndpoint["method"];
const path = match[2];
// Find line number
let line = 1;
let pos = 0;
for (let i = 0; i < lines.length; i++) {
if (pos + lines[i].length >= match.index!) {
line = i + 1;
break;
}
pos += lines[i].length + 1; // +1 for newline
}
result.apiEndpoints.push({
method,
path,
filePath,
line,
hasDocumentation: this.hasEndpointDocumentation(
content,
match.index!,
),
});
}
});
}
private isExported(node: any): boolean {
// Check if node is part of an export declaration
return (
node.parent?.type === "ExportNamedDeclaration" ||
node.parent?.type === "ExportDefaultDeclaration"
);
}
private hasJSDoc(node: any): boolean {
return (
node.comments?.some(
(comment: any) =>
comment.type === "Block" && comment.value.startsWith("*"),
) || false
);
}
private getJSDocDescription(node: any): string | undefined {
const jsDocComment = node.comments?.find(
(comment: any) =>
comment.type === "Block" && comment.value.startsWith("*"),
);
if (jsDocComment) {
// Extract first line of JSDoc as description
const lines = jsDocComment.value.split("\n");
for (const line of lines) {
const cleaned = line.replace(/^\s*\*\s?/, "").trim();
if (cleaned && !cleaned.startsWith("@")) {
return cleaned;
}
}
}
return undefined;
}
private hasEndpointDocumentation(
content: string,
matchIndex: number,
): boolean {
// Look for JSDoc or comments before the endpoint
const beforeMatch = content.substring(0, matchIndex);
const lines = beforeMatch.split("\n");
// Check last few lines for documentation
for (let i = Math.max(0, lines.length - 5); i < lines.length; i++) {
const line = lines[i].trim();
if (
line.startsWith("/**") ||
line.startsWith("/*") ||
line.startsWith("//")
) {
return true;
}
}
return false;
}
}
```
--------------------------------------------------------------------------------
/tests/edge-cases/error-handling.test.ts:
--------------------------------------------------------------------------------
```typescript
// Edge cases and error handling tests
import { promises as fs } from "fs";
import path from "path";
import os from "os";
import { analyzeRepository } from "../../src/tools/analyze-repository";
import { recommendSSG } from "../../src/tools/recommend-ssg";
import { generateConfig } from "../../src/tools/generate-config";
import { setupStructure } from "../../src/tools/setup-structure";
import { deployPages } from "../../src/tools/deploy-pages";
import { verifyDeployment } from "../../src/tools/verify-deployment";
describe("Edge Cases and Error Handling", () => {
let tempDir: string;
beforeAll(async () => {
tempDir = path.join(os.tmpdir(), "documcp-edge-case-tests");
await fs.mkdir(tempDir, { recursive: true });
});
afterAll(async () => {
try {
await fs.rm(tempDir, { recursive: true, force: true });
} catch (error) {
console.warn("Failed to cleanup edge case test directory:", error);
}
});
describe("Input Validation Edge Cases", () => {
it("should handle null and undefined inputs gracefully", async () => {
// Test analyze_repository with invalid inputs
const invalidInputs = [
null,
undefined,
{},
{ path: null },
{ path: undefined },
{ path: "", depth: "invalid" },
];
for (const input of invalidInputs) {
try {
const result = await analyzeRepository(input as any);
expect((result as any).isError).toBe(true);
} catch (error) {
// Catching errors is also acceptable for invalid inputs
expect(error).toBeDefined();
}
}
});
it("should validate SSG enum values strictly", async () => {
const invalidSSGs = [
"react",
"vue",
"angular",
"gatsby",
"",
null,
undefined,
];
for (const invalidSSG of invalidSSGs) {
try {
const result = await generateConfig({
ssg: invalidSSG as any,
projectName: "Test",
outputPath: tempDir,
});
expect((result as any).isError).toBe(true);
} catch (error) {
expect(error).toBeDefined();
}
}
});
it("should handle extremely long input strings", async () => {
const longString = "a".repeat(10000);
const result = await generateConfig({
ssg: "docusaurus",
projectName: longString,
projectDescription: longString,
outputPath: path.join(tempDir, "long-strings"),
});
// Should handle long strings without crashing
expect(result.content).toBeDefined();
});
it("should handle special characters in project names", async () => {
const specialNames = [
"Project with spaces",
"Project-with-hyphens",
"Project_with_underscores",
"Project.with.dots",
"Project@with#special$chars",
"Проект на русском",
"项目中文名",
"プロジェクト日本語",
];
for (const name of specialNames) {
const outputDir = path.join(
tempDir,
"special-chars",
Buffer.from(name).toString("hex"),
);
await fs.mkdir(outputDir, { recursive: true });
const result = await generateConfig({
ssg: "docusaurus",
projectName: name,
outputPath: outputDir,
});
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
}
});
});
describe("File System Edge Cases", () => {
it("should handle permission-denied scenarios", async () => {
if (process.platform === "win32") {
// Skip on Windows as permission handling is different
return;
}
// Create a directory with restricted permissions
const restrictedDir = path.join(tempDir, "no-permissions");
await fs.mkdir(restrictedDir, { recursive: true });
try {
await fs.chmod(restrictedDir, 0o000);
const result = await analyzeRepository({
path: restrictedDir,
depth: "standard",
});
expect((result as any).isError).toBe(true);
} finally {
// Restore permissions for cleanup
await fs.chmod(restrictedDir, 0o755);
}
});
it("should handle symlinks and circular references", async () => {
const symlinkTest = path.join(tempDir, "symlink-test");
await fs.mkdir(symlinkTest, { recursive: true });
// Create a file
const originalFile = path.join(symlinkTest, "original.txt");
await fs.writeFile(originalFile, "original content");
// Create symlink
const symlinkFile = path.join(symlinkTest, "link.txt");
try {
await fs.symlink(originalFile, symlinkFile);
const result = await analyzeRepository({
path: symlinkTest,
depth: "standard",
});
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
} catch (error) {
// Symlinks might not be supported on all systems
console.warn("Symlink test skipped:", error);
}
});
it("should handle very deep directory structures", async () => {
const deepTest = path.join(tempDir, "deep-structure");
let currentPath = deepTest;
// Create 20 levels deep structure
for (let i = 0; i < 20; i++) {
currentPath = path.join(currentPath, `level-${i}`);
await fs.mkdir(currentPath, { recursive: true });
await fs.writeFile(
path.join(currentPath, `file-${i}.txt`),
`Content ${i}`,
);
}
const result = await analyzeRepository({
path: deepTest,
depth: "deep",
});
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
});
it("should handle files with unusual extensions", async () => {
const unusualFiles = path.join(tempDir, "unusual-files");
await fs.mkdir(unusualFiles, { recursive: true });
const unusualExtensions = [
"file.xyz",
"file.123",
"file.",
".hidden",
"no-extension",
"file..double.dot",
"file with spaces.txt",
"file-with-émojis-🚀.md",
];
for (const filename of unusualExtensions) {
await fs.writeFile(path.join(unusualFiles, filename), "test content");
}
const result = await analyzeRepository({
path: unusualFiles,
depth: "standard",
});
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
// Should count all files (excluding hidden files that start with .)
const analysisData = JSON.parse(
result.content.find((c) => c.text.includes('"totalFiles"'))!.text,
);
// The analyze function filters out .hidden files, so we expect 7 files instead of 8
expect(analysisData.structure.totalFiles).toBe(7); // 8 files minus .hidden
});
it("should handle binary files gracefully", async () => {
const binaryTest = path.join(tempDir, "binary-files");
await fs.mkdir(binaryTest, { recursive: true });
// Create binary-like files
const binaryData = Buffer.from([0x00, 0x01, 0x02, 0xff, 0xfe, 0xfd]);
await fs.writeFile(path.join(binaryTest, "binary.bin"), binaryData);
await fs.writeFile(path.join(binaryTest, "image.png"), binaryData);
await fs.writeFile(path.join(binaryTest, "archive.zip"), binaryData);
const result = await analyzeRepository({
path: binaryTest,
depth: "standard",
});
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
});
});
describe("Memory and Performance Edge Cases", () => {
it("should handle repositories with many small files", async () => {
const manyFilesTest = path.join(tempDir, "many-small-files");
await fs.mkdir(manyFilesTest, { recursive: true });
// Create 500 small files
for (let i = 0; i < 500; i++) {
await fs.writeFile(
path.join(manyFilesTest, `small-${i}.txt`),
`content ${i}`,
);
}
const startTime = Date.now();
const result = await analyzeRepository({
path: manyFilesTest,
depth: "standard",
});
const executionTime = Date.now() - startTime;
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
expect(executionTime).toBeLessThan(10000); // Should complete within 10 seconds
});
it("should handle repositories with very large files", async () => {
const largeFilesTest = path.join(tempDir, "large-files");
await fs.mkdir(largeFilesTest, { recursive: true });
// Create large files (1MB each)
const largeContent = "x".repeat(1024 * 1024);
await fs.writeFile(path.join(largeFilesTest, "large1.txt"), largeContent);
await fs.writeFile(path.join(largeFilesTest, "large2.log"), largeContent);
const result = await analyzeRepository({
path: largeFilesTest,
depth: "quick", // Use quick to avoid timeout
});
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
});
it("should handle concurrent tool executions", async () => {
const concurrentTest = path.join(tempDir, "concurrent-test");
await fs.mkdir(concurrentTest, { recursive: true });
await fs.writeFile(
path.join(concurrentTest, "test.js"),
'console.log("test");',
);
await fs.writeFile(path.join(concurrentTest, "README.md"), "# Test");
// Run multiple analyses concurrently
const promises = Array.from({ length: 5 }, () =>
analyzeRepository({
path: concurrentTest,
depth: "quick",
}),
);
const results = await Promise.all(promises);
results.forEach((result) => {
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
});
});
});
describe("Configuration Edge Cases", () => {
it("should handle output paths with special characters", async () => {
const specialPaths = [
path.join(tempDir, "path with spaces"),
path.join(tempDir, "path-with-hyphens"),
path.join(tempDir, "path_with_underscores"),
path.join(tempDir, "path.with.dots"),
];
for (const specialPath of specialPaths) {
const result = await generateConfig({
ssg: "docusaurus",
projectName: "Special Path Test",
outputPath: specialPath,
});
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
// Verify files were actually created
const files = await fs.readdir(specialPath);
expect(files.length).toBeGreaterThan(0);
}
});
it("should handle nested output directory creation", async () => {
const nestedPath = path.join(
tempDir,
"deeply",
"nested",
"output",
"directory",
);
const result = await generateConfig({
ssg: "mkdocs",
projectName: "Nested Test",
outputPath: nestedPath,
});
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
// Verify nested directories were created
expect(
await fs
.access(nestedPath)
.then(() => true)
.catch(() => false),
).toBe(true);
});
it("should handle existing files without overwriting destructively", async () => {
const existingFiles = path.join(tempDir, "existing-files");
await fs.mkdir(existingFiles, { recursive: true });
// Create existing file
const existingContent =
"This is existing content that should not be lost";
await fs.writeFile(
path.join(existingFiles, "important.txt"),
existingContent,
);
const result = await generateConfig({
ssg: "docusaurus",
projectName: "Existing Files Test",
outputPath: existingFiles,
});
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
// Verify our important file still exists
expect(
await fs
.access(path.join(existingFiles, "important.txt"))
.then(() => true)
.catch(() => false),
).toBe(true);
});
});
describe("Deployment Edge Cases", () => {
it("should handle repositories with existing workflows", async () => {
const existingWorkflow = path.join(tempDir, "existing-workflow");
await fs.mkdir(path.join(existingWorkflow, ".github", "workflows"), {
recursive: true,
});
// Create existing workflow
await fs.writeFile(
path.join(existingWorkflow, ".github", "workflows", "existing.yml"),
"name: Existing Workflow\non: push",
);
const result = await deployPages({
repository: existingWorkflow,
ssg: "docusaurus",
});
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
// Both workflows should exist
const workflows = await fs.readdir(
path.join(existingWorkflow, ".github", "workflows"),
);
expect(workflows).toContain("existing.yml");
expect(workflows).toContain("deploy-docs.yml");
});
it("should handle custom domain validation", async () => {
const customDomains = [
"docs.example.com",
"my-docs.github.io",
"documentation.mycompany.org",
"subdomain.example.co.uk",
];
for (const domain of customDomains) {
const domainTest = path.join(
tempDir,
"domain-test",
domain.replace(/[^a-z0-9]/gi, "-"),
);
const result = await deployPages({
repository: domainTest,
ssg: "jekyll",
customDomain: domain,
});
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
// Verify CNAME file
const cnameContent = await fs.readFile(
path.join(domainTest, "CNAME"),
"utf-8",
);
expect(cnameContent.trim()).toBe(domain);
}
});
it("should handle repository URL variations", async () => {
const urlVariations = [
"https://github.com/user/repo",
"https://github.com/user/repo.git",
"[email protected]:user/repo.git",
"/absolute/local/path",
"./relative/path",
".",
];
for (const repo of urlVariations) {
const result = await verifyDeployment({
repository: repo,
});
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
}
});
});
describe("Unicode and Internationalization", () => {
it("should handle Unicode file names and content", async () => {
const unicodeTest = path.join(tempDir, "unicode-test");
await fs.mkdir(unicodeTest, { recursive: true });
const unicodeFiles = [
{ name: "中文文件.md", content: "# 中文标题\n这是中文内容。" },
{ name: "русский.txt", content: "Привет мир!" },
{
name: "日本語.js",
content: '// 日本語のコメント\nconsole.log("こんにちは");',
},
{
name: "émojis-🚀-test.py",
content: '# -*- coding: utf-8 -*-\nprint("🚀 Unicode test")',
},
];
for (const file of unicodeFiles) {
await fs.writeFile(
path.join(unicodeTest, file.name),
file.content,
"utf8",
);
}
const result = await analyzeRepository({
path: unicodeTest,
depth: "standard",
});
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
const analysisData = JSON.parse(
result.content.find((c) => c.text.includes('"totalFiles"'))!.text,
);
expect(analysisData.structure.totalFiles).toBe(unicodeFiles.length); // No README created in this test
});
it("should handle different line ending styles", async () => {
const lineEndingTest = path.join(tempDir, "line-ending-test");
await fs.mkdir(lineEndingTest, { recursive: true });
// Create files with different line endings
await fs.writeFile(
path.join(lineEndingTest, "unix.txt"),
"line1\nline2\nline3\n",
);
await fs.writeFile(
path.join(lineEndingTest, "windows.txt"),
"line1\r\nline2\r\nline3\r\n",
);
await fs.writeFile(
path.join(lineEndingTest, "mac.txt"),
"line1\rline2\rline3\r",
);
await fs.writeFile(
path.join(lineEndingTest, "mixed.txt"),
"line1\nline2\r\nline3\rline4\n",
);
const result = await analyzeRepository({
path: lineEndingTest,
depth: "standard",
});
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
});
});
describe("Recovery and Resilience", () => {
it("should recover from partial failures gracefully", async () => {
const partialFailure = path.join(tempDir, "partial-failure");
await fs.mkdir(partialFailure, { recursive: true });
// Create some valid files
await fs.writeFile(
path.join(partialFailure, "valid.js"),
'console.log("valid");',
);
await fs.writeFile(
path.join(partialFailure, "package.json"),
'{"name": "test"}',
);
// Create some problematic scenarios
await fs.mkdir(path.join(partialFailure, "empty-dir"));
const result = await analyzeRepository({
path: partialFailure,
depth: "standard",
});
expect(result.content).toBeDefined();
expect((result as any).isError).toBeFalsy();
// Should still provide useful analysis despite issues
const analysisData = JSON.parse(
result.content.find((c) => c.text.includes('"ecosystem"'))!.text,
);
expect(analysisData.dependencies.ecosystem).toBe("javascript");
});
it("should provide meaningful error messages", async () => {
const result = await analyzeRepository({
path: "/absolutely/does/not/exist/anywhere",
depth: "standard",
});
expect((result as any).isError).toBe(true);
const errorText = result.content.map((c) => c.text).join(" ");
// Error message should be helpful
expect(errorText.toLowerCase()).toContain("error");
expect(errorText.toLowerCase()).toMatch(
/resolution|solution|fix|check|ensure/,
);
});
it("should handle timeout scenarios gracefully", async () => {
// This test verifies that long-running operations don't hang indefinitely
const longOperation = analyzeRepository({
path: tempDir, // Large temp directory
depth: "deep",
});
// Set a reasonable timeout with proper cleanup
let timeoutId: NodeJS.Timeout | undefined;
const timeoutPromise = new Promise((_, reject) => {
timeoutId = setTimeout(
() => reject(new Error("Operation timed out")),
30000,
); // 30 seconds
});
try {
await Promise.race([longOperation, timeoutPromise]);
} catch (error) {
if ((error as Error).message === "Operation timed out") {
console.warn(
"Long operation test timed out - this is expected behavior",
);
} else {
throw error;
}
} finally {
// Clean up the timeout to prevent Jest hanging
if (timeoutId) {
clearTimeout(timeoutId);
}
}
});
});
});
```
--------------------------------------------------------------------------------
/src/utils/content-extractor.ts:
--------------------------------------------------------------------------------
```typescript
import { promises as fs } from "fs";
import path from "path";
export interface ExtractedContent {
readme?: {
content: string;
sections: Array<{
title: string;
content: string;
level: number;
}>;
};
existingDocs: Array<{
path: string;
title: string;
content: string;
category?: "tutorial" | "how-to" | "reference" | "explanation";
}>;
adrs: Array<{
number: string;
title: string;
status: string;
content: string;
decision: string;
consequences: string;
}>;
codeExamples: Array<{
file: string;
description: string;
code: string;
language: string;
}>;
apiDocs: Array<{
endpoint?: string;
function?: string;
description: string;
parameters: Array<{ name: string; type: string; description: string }>;
returns?: string;
}>;
}
/**
* Extracts comprehensive content from a repository for documentation generation.
*
* Performs multi-layered content extraction including README analysis, existing
* documentation discovery, ADR (Architectural Decision Record) parsing, code
* examples identification, and API documentation extraction. This function is
* the foundation for intelligent content population and documentation generation.
*
* @param repoPath - The file system path to the repository to extract content from
*
* @returns Promise resolving to comprehensive extracted content
* @returns readme - README content with structured sections
* @returns existingDocs - Array of existing documentation files found
* @returns adrs - Array of Architectural Decision Records
* @returns codeExamples - Array of code examples with descriptions
* @returns apiDocs - Array of API documentation extracted from JSDoc comments
*
* @throws {Error} When repository path is inaccessible
* @throws {Error} When content extraction fails
*
* @example
* ```typescript
* const content = await extractRepositoryContent("/path/to/repository");
* console.log(`Found ${content.existingDocs.length} existing docs`);
* console.log(`Extracted ${content.codeExamples.length} code examples`);
* ```
*
* @since 1.0.0
*/
export async function extractRepositoryContent(
repoPath: string,
): Promise<ExtractedContent> {
const content: ExtractedContent = {
existingDocs: [],
adrs: [],
codeExamples: [],
apiDocs: [],
};
// Extract README content
content.readme = await extractReadme(repoPath);
// Extract existing documentation
content.existingDocs = await extractExistingDocs(repoPath);
// Extract ADRs
content.adrs = await extractADRs(repoPath);
// Extract code examples from main source files
content.codeExamples = await extractCodeExamples(repoPath);
// Extract API documentation from code comments
content.apiDocs = await extractAPIDocs(repoPath);
return content;
}
async function extractReadme(
repoPath: string,
): Promise<ExtractedContent["readme"] | undefined> {
const readmeFiles = ["README.md", "readme.md", "Readme.md"];
for (const filename of readmeFiles) {
try {
const readmePath = path.join(repoPath, filename);
const content = await fs.readFile(readmePath, "utf-8");
const sections = parseMarkdownSections(content);
return { content, sections };
} catch {
// Continue to next potential README file
}
}
return undefined;
}
async function extractExistingDocs(
repoPath: string,
): Promise<ExtractedContent["existingDocs"]> {
const docs: ExtractedContent["existingDocs"] = [];
const docDirs = ["docs", "documentation", "doc"];
for (const dir of docDirs) {
try {
const docsPath = path.join(repoPath, dir);
await extractDocsFromDir(docsPath, docs, "");
} catch {
// Directory doesn't exist, continue
}
}
return docs;
}
async function extractDocsFromDir(
dirPath: string,
docs: ExtractedContent["existingDocs"],
relativePath: string,
): Promise<void> {
const entries = await fs.readdir(dirPath, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dirPath, entry.name);
const relPath = path.join(relativePath, entry.name);
if (entry.isDirectory() && !entry.name.startsWith(".")) {
await extractDocsFromDir(fullPath, docs, relPath);
} else if (
entry.isFile() &&
(entry.name.endsWith(".md") || entry.name.endsWith(".mdx"))
) {
try {
const content = await fs.readFile(fullPath, "utf-8");
const title = extractTitle(content, entry.name);
const category = categorizeDocument(content, relPath);
docs.push({
path: relPath,
title,
content,
category,
});
} catch {
// Skip files that can't be read
}
}
}
}
async function extractADRs(
repoPath: string,
): Promise<ExtractedContent["adrs"]> {
const adrs: ExtractedContent["adrs"] = [];
const adrPaths = [
"docs/adrs",
"docs/adr",
"docs/decisions",
"docs/architecture/decisions",
"adr",
];
for (const adrDir of adrPaths) {
try {
const dirPath = path.join(repoPath, adrDir);
const files = await fs.readdir(dirPath);
for (const file of files) {
if (file.endsWith(".md") && /\d{3,4}/.test(file)) {
const content = await fs.readFile(path.join(dirPath, file), "utf-8");
const adr = parseADR(content, file);
if (adr) {
adrs.push(adr);
}
}
}
// If we found ADRs, don't check other directories
if (adrs.length > 0) break;
} catch {
// Directory doesn't exist, continue
}
}
return adrs;
}
async function extractCodeExamples(
repoPath: string,
): Promise<ExtractedContent["codeExamples"]> {
const examples: ExtractedContent["codeExamples"] = [];
// Look for example directories
const exampleDirs = ["examples", "samples", "demo"];
for (const dir of exampleDirs) {
try {
const examplesPath = path.join(repoPath, dir);
await extractExamplesFromDir(examplesPath, examples);
} catch {
// Directory doesn't exist
}
}
// Also extract from main source files if they contain example comments
try {
const srcPath = path.join(repoPath, "src");
await extractInlineExamples(srcPath, examples);
} catch {
// No src directory
}
return examples;
}
async function extractExamplesFromDir(
dirPath: string,
examples: ExtractedContent["codeExamples"],
): Promise<void> {
const entries = await fs.readdir(dirPath, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dirPath, entry.name);
if (entry.isFile()) {
const ext = path.extname(entry.name);
const language = getLanguageFromExtension(ext);
if (language) {
try {
const code = await fs.readFile(fullPath, "utf-8");
const description = extractExampleDescription(code);
examples.push({
file: entry.name,
description: description || `Example: ${entry.name}`,
code: code.slice(0, 500), // First 500 chars
language,
});
} catch {
// Skip unreadable files
}
}
}
}
}
async function extractInlineExamples(
srcPath: string,
examples: ExtractedContent["codeExamples"],
): Promise<void> {
// Extract examples from comments marked with @example
const files = await walkSourceFiles(srcPath);
for (const file of files) {
try {
const content = await fs.readFile(file, "utf-8");
const exampleBlocks = content.match(/@example[\s\S]*?(?=@\w|$)/g);
if (exampleBlocks) {
for (const block of exampleBlocks) {
const code = block.replace(/@example\s*/, "").trim();
const language = getLanguageFromExtension(path.extname(file));
if (code && language) {
examples.push({
file: path.basename(file),
description: `Example from ${path.basename(file)}`,
code: code.slice(0, 500),
language,
});
}
}
}
} catch {
// Skip unreadable files
}
}
}
async function extractAPIDocs(
repoPath: string,
): Promise<ExtractedContent["apiDocs"]> {
const apiDocs: ExtractedContent["apiDocs"] = [];
// Look for API documentation in various formats
const apiFiles = [
"api.md",
"API.md",
"docs/api.md",
"docs/API.md",
"openapi.json",
"openapi.yaml",
"swagger.json",
"swagger.yaml",
];
for (const file of apiFiles) {
try {
const filePath = path.join(repoPath, file);
const content = await fs.readFile(filePath, "utf-8");
if (file.endsWith(".md")) {
// Parse markdown API documentation
const apis = parseMarkdownAPI(content);
apiDocs.push(...apis);
} else if (file.includes("openapi") || file.includes("swagger")) {
// Parse OpenAPI/Swagger spec
const apis = parseOpenAPISpec(content);
apiDocs.push(...apis);
}
} catch {
// File doesn't exist
}
}
// Also extract from source code comments
try {
const srcPath = path.join(repoPath, "src");
const jsDocAPIs = await extractJSDocAPIs(srcPath);
apiDocs.push(...jsDocAPIs);
} catch {
// No src directory
}
return apiDocs;
}
// Helper functions
function parseMarkdownSections(
content: string,
): Array<{ title: string; content: string; level: number }> {
const sections: Array<{ title: string; content: string; level: number }> = [];
const lines = content.split("\n");
let currentSection: { title: string; content: string; level: number } | null =
null;
for (const line of lines) {
const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
if (headerMatch) {
if (currentSection) {
sections.push(currentSection);
}
currentSection = {
title: headerMatch[2],
content: "",
level: headerMatch[1].length,
};
} else if (currentSection) {
currentSection.content += line + "\n";
}
}
if (currentSection) {
sections.push(currentSection);
}
return sections;
}
function extractTitle(content: string, filename: string): string {
const lines = content.split("\n");
for (const line of lines) {
if (line.startsWith("# ")) {
return line.replace("# ", "").trim();
}
}
return filename.replace(/\.(md|mdx)$/, "").replace(/[-_]/g, " ");
}
function categorizeDocument(
content: string,
filePath: string,
): ExtractedContent["existingDocs"][0]["category"] {
const lowerContent = content.toLowerCase();
const lowerPath = filePath.toLowerCase();
if (
lowerPath.includes("tutorial") ||
lowerPath.includes("getting-started") ||
lowerContent.includes("## getting started") ||
lowerContent.includes("# tutorial")
) {
return "tutorial";
}
if (
lowerPath.includes("how-to") ||
lowerPath.includes("guide") ||
lowerContent.includes("## how to") ||
lowerContent.includes("# guide")
) {
return "how-to";
}
if (
lowerPath.includes("api") ||
lowerPath.includes("reference") ||
lowerContent.includes("## api") ||
lowerContent.includes("# reference")
) {
return "reference";
}
if (
lowerPath.includes("concept") ||
lowerPath.includes("explanation") ||
lowerPath.includes("architecture") ||
lowerPath.includes("adr")
) {
return "explanation";
}
// Default categorization based on content patterns
if (lowerContent.includes("step 1") || lowerContent.includes("first,")) {
return "tutorial";
}
if (lowerContent.includes("to do this") || lowerContent.includes("you can")) {
return "how-to";
}
return "reference";
}
function parseADR(
content: string,
filename: string,
): ExtractedContent["adrs"][0] | null {
const lines = content.split("\n");
const adr: Partial<ExtractedContent["adrs"][0]> = {
content,
};
// Extract ADR number from filename
const numberMatch = filename.match(/(\d{3,4})/);
if (numberMatch) {
adr.number = numberMatch[1];
}
// Extract title
for (const line of lines) {
if (line.startsWith("# ")) {
adr.title = line
.replace("# ", "")
.replace(/^\d+\.?\s*/, "")
.trim();
break;
}
}
// Extract status
const statusMatch = content.match(/## Status\s*\n+([^\n]+)/i);
if (statusMatch) {
adr.status = statusMatch[1].trim();
}
// Extract decision
const decisionMatch = content.match(/## Decision\s*\n+([\s\S]*?)(?=##|$)/i);
if (decisionMatch) {
adr.decision = decisionMatch[1].trim();
}
// Extract consequences
const consequencesMatch = content.match(
/## Consequences\s*\n+([\s\S]*?)(?=##|$)/i,
);
if (consequencesMatch) {
adr.consequences = consequencesMatch[1].trim();
}
if (adr.number && adr.title) {
return adr as ExtractedContent["adrs"][0];
}
return null;
}
function getLanguageFromExtension(ext: string): string | null {
const languageMap: Record<string, string> = {
".js": "javascript",
".jsx": "javascript",
".ts": "typescript",
".tsx": "typescript",
".py": "python",
".rb": "ruby",
".go": "go",
".java": "java",
".cpp": "cpp",
".c": "c",
".rs": "rust",
".php": "php",
".swift": "swift",
".kt": "kotlin",
".scala": "scala",
".sh": "bash",
".yaml": "yaml",
".yml": "yaml",
".json": "json",
};
return languageMap[ext] || null;
}
function extractExampleDescription(code: string): string | null {
const lines = code.split("\n").slice(0, 10);
for (const line of lines) {
if (
line.includes("Example:") ||
line.includes("Demo:") ||
line.includes("Sample:")
) {
return line.replace(/[/*#-]/, "").trim();
}
}
return null;
}
async function walkSourceFiles(
dir: string,
files: string[] = [],
): Promise<string[]> {
try {
const entries = await fs.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (
entry.isDirectory() &&
!entry.name.startsWith(".") &&
entry.name !== "node_modules"
) {
await walkSourceFiles(fullPath, files);
} else if (entry.isFile()) {
const ext = path.extname(entry.name);
if (
[".js", ".ts", ".jsx", ".tsx", ".py", ".rb", ".go", ".java"].includes(
ext,
)
) {
files.push(fullPath);
}
}
}
} catch {
// Directory doesn't exist or can't be read
}
return files;
}
function parseMarkdownAPI(content: string): ExtractedContent["apiDocs"] {
const apis: ExtractedContent["apiDocs"] = [];
const sections = content.split(/^##\s+/m);
for (const section of sections) {
if (
section.includes("API") ||
section.includes("endpoint") ||
section.includes("function")
) {
const lines = section.split("\n");
const api: Partial<ExtractedContent["apiDocs"][0]> = {
parameters: [],
};
// Extract function/endpoint name
const titleMatch = lines[0].match(/`([^`]+)`/);
if (titleMatch) {
if (titleMatch[1].includes("/")) {
api.endpoint = titleMatch[1];
} else {
api.function = titleMatch[1];
}
}
// Extract description
for (let i = 1; i < lines.length; i++) {
if (lines[i] && !lines[i].startsWith("###")) {
api.description = lines[i].trim();
break;
}
}
// Extract parameters
const paramsIndex = lines.findIndex(
(l) => l.includes("Parameters") || l.includes("Arguments"),
);
if (paramsIndex !== -1) {
for (let i = paramsIndex + 1; i < lines.length; i++) {
const paramMatch = lines[i].match(
/[-*]\s*`([^`]+)`\s*\(([^)]+)\)\s*[-:]?\s*(.+)/,
);
if (paramMatch) {
api.parameters?.push({
name: paramMatch[1],
type: paramMatch[2],
description: paramMatch[3],
});
}
}
}
// Extract returns
const returnsIndex = lines.findIndex(
(l) => l.includes("Returns") || l.includes("Response"),
);
if (returnsIndex !== -1 && returnsIndex + 1 < lines.length) {
api.returns = lines[returnsIndex + 1].trim();
}
if ((api.endpoint || api.function) && api.description) {
apis.push(api as ExtractedContent["apiDocs"][0]);
}
}
}
return apis;
}
function parseOpenAPISpec(content: string): ExtractedContent["apiDocs"] {
const apis: ExtractedContent["apiDocs"] = [];
try {
const spec = JSON.parse(content);
if (spec.paths) {
for (const [path, methods] of Object.entries(spec.paths)) {
for (const [method, operation] of Object.entries(methods as any)) {
if (typeof operation === "object" && operation) {
const api: ExtractedContent["apiDocs"][0] = {
endpoint: `${method.toUpperCase()} ${path}`,
description:
(operation as any).summary ||
(operation as any).description ||
"",
parameters: [],
};
if ((operation as any).parameters) {
for (const param of (operation as any).parameters) {
api.parameters.push({
name: param.name,
type: param.type || param.schema?.type || "any",
description: param.description || "",
});
}
}
if ((operation as any).responses?.["200"]) {
api.returns = (operation as any).responses["200"].description;
}
apis.push(api);
}
}
}
}
} catch {
// Not valid JSON or doesn't follow expected structure
}
return apis;
}
async function extractJSDocAPIs(
srcPath: string,
): Promise<ExtractedContent["apiDocs"]> {
const apis: ExtractedContent["apiDocs"] = [];
const files = await walkSourceFiles(srcPath);
for (const file of files.slice(0, 20)) {
// Limit to first 20 files for performance
try {
const content = await fs.readFile(file, "utf-8");
const jsdocBlocks = content.match(/\/\*\*[\s\S]*?\*\//g);
if (jsdocBlocks) {
for (const block of jsdocBlocks) {
const api = parseJSDocBlock(block);
if (api) {
apis.push(api);
}
}
}
} catch {
// Skip unreadable files
}
}
return apis;
}
function parseJSDocBlock(block: string): ExtractedContent["apiDocs"][0] | null {
const api: Partial<ExtractedContent["apiDocs"][0]> = {
parameters: [],
};
// Extract description (first line after /^**)
const descMatch = block.match(/\/\*\*\s*\n\s*\*\s*([^@\n]+)/);
if (descMatch) {
api.description = descMatch[1].trim();
}
// Extract function name from the code following the JSDoc
const functionMatch = block.match(
/function\s+(\w+)|const\s+(\w+)\s*=|(\w+)\s*\(/,
);
if (functionMatch) {
api.function = functionMatch[1] || functionMatch[2] || functionMatch[3];
}
// Extract parameters
const paramMatches = block.matchAll(
/@param\s*{([^}]+)}\s*(\w+)\s*-?\s*(.+)/g,
);
for (const match of paramMatches) {
api.parameters?.push({
name: match[2],
type: match[1],
description: match[3].trim(),
});
}
// Extract returns
const returnsMatch = block.match(/@returns?\s*{([^}]+)}\s*(.+)/);
if (returnsMatch) {
api.returns = `${returnsMatch[1]}: ${returnsMatch[2]}`;
}
if (api.function && api.description) {
return api as ExtractedContent["apiDocs"][0];
}
return null;
}
```
--------------------------------------------------------------------------------
/docs/adrs/005-github-pages-deployment-automation.md:
--------------------------------------------------------------------------------
```markdown
---
id: 005-github-pages-deployment-automation
title: "ADR-005: GitHub Pages Deployment Automation"
sidebar_label: "ADR-5: GitHub Pages Deployment Automation"
sidebar_position: 5
documcp:
last_updated: "2025-11-20T00:46:21.939Z"
last_validated: "2025-11-20T00:46:21.939Z"
auto_updated: false
update_frequency: monthly
---
# ADR-005: GitHub Pages Deployment Automation Architecture
## Status
Accepted
## Context
DocuMCP must provide seamless, automated deployment of documentation sites to GitHub Pages. This requires sophisticated understanding of GitHub Pages capabilities, limitations, and best practices, along with intelligent generation of CI/CD workflows that adapt to different static site generators and project configurations.
GitHub Pages deployment complexity factors:
- **Multiple deployment methods**: GitHub Actions, branch-based, legacy Jekyll
- **SSG-specific requirements**: Different build tools, dependencies, and configurations
- **Security considerations**: Secrets management, workflow permissions, dependency vulnerabilities
- **Performance optimization**: Build caching, incremental builds, deployment strategies
- **Troubleshooting support**: Common failure modes, debugging guidance, health checks
Key challenges:
- Each SSG has unique deployment requirements and optimal configurations
- GitHub Actions workflows need to be maintainable and debuggable
- Repository settings and branch configurations must be properly managed
- Users need clear guidance for initial deployment and ongoing maintenance
## Decision
We will implement a comprehensive GitHub Pages deployment orchestration system that generates optimized, SSG-specific GitHub Actions workflows with intelligent configuration, error handling, and verification capabilities.
### Deployment Architecture Components:
#### 1. Workflow Generation Engine
- **SSG-specific workflow templates** optimized for each supported generator
- **Intelligent dependency management** with version pinning and security updates
- **Build optimization** including caching strategies and incremental builds
- **Error handling and debugging** with comprehensive logging and failure analysis
#### 2. Repository Configuration Management (Enhanced with Security Research)
- **Automated repository settings** for GitHub Pages configuration
- **Branch management guidance** for different deployment strategies
- **Security configuration** including workflow permissions and secrets management
- **Health check integration** for deployment verification
**Research-Validated Security Enhancements**:
- **OIDC Token Authentication**: Implements JWT-based deployment validation with branch protection
- **Minimal Permission Principle**: Generates workflows with only required `pages: write` and `id-token: write` permissions
- **Environment Protection**: Default environment rules with required reviewers for production deployments
- **Automated Security Scanning**: Integrated secret scanning and vulnerability assessment
#### 3. Deployment Strategy Selection
- **Branch-based deployment** for simple sites with minimal build requirements
- **GitHub Actions deployment** for complex builds requiring custom environments
- **Hybrid approaches** combining native Jekyll support with custom processing
#### 4. Monitoring and Troubleshooting
- **Deployment verification** with automated health checks and accessibility testing
- **Common failure diagnosis** with specific remediation guidance
- **Performance monitoring** with build time optimization recommendations
- **Maintenance guidance** for ongoing workflow and dependency management
## Alternatives Considered
### Manual Deployment Setup
- **Pros**: Full user control, educational value, flexible configuration
- **Cons**: High learning curve, error-prone, inconsistent results
- **Decision**: Rejected due to complexity and poor user experience
### Third-Party Deployment Services (Netlify, Vercel)
- **Pros**: Advanced features, excellent performance, minimal configuration
- **Cons**: Cost for advanced features, vendor lock-in, less GitHub integration
- **Decision**: Rejected to maintain GitHub-native workflow and free hosting
### Universal Deployment Workflow
- **Pros**: Simpler implementation, consistent user experience
- **Cons**: Suboptimal for specific SSGs, limited optimization opportunities
- **Decision**: Rejected in favor of SSG-optimized approaches
### Container-Based Deployment
- **Pros**: Consistent environments, advanced dependency management
- **Cons**: Complexity overhead, slower builds, GitHub Actions limitations
- **Decision**: Rejected for initial version; consider for advanced scenarios
## Consequences
### Positive
- **Optimized Performance**: SSG-specific workflows provide optimal build times and caching
- **Reliable Deployment**: Comprehensive error handling and verification reduce failure rates
- **Maintainable Workflows**: Generated workflows follow best practices and include documentation
- **Debugging Support**: Clear error messages and troubleshooting guidance reduce support burden
- **Security Best Practices**: Automated security configuration and dependency management
### Negative
- **Implementation Complexity**: Multiple SSG-specific templates require significant maintenance
- **GitHub Dependency**: Tight coupling to GitHub Actions and Pages infrastructure
- **Template Maintenance**: Regular updates needed as SSGs and GitHub features evolve
### Risks and Mitigations
- **Workflow Obsolescence**: Regular testing and updates of generated workflows
- **GitHub API Changes**: Monitoring of GitHub features and migration planning
- **Security Vulnerabilities**: Automated dependency scanning and update recommendations
## Implementation Details
### Workflow Template System
```typescript
interface WorkflowTemplate {
name: string;
triggers: WorkflowTrigger[];
jobs: WorkflowJob[];
permissions: WorkflowPermissions;
environment: EnvironmentConfig;
}
interface SSGWorkflowConfig {
buildCommand: string;
outputDirectory: string;
dependencies: DependencyConfig;
caching: CacheConfig;
environmentVariables: EnvironmentVariable[];
}
const WORKFLOW_TEMPLATES: Record<SSGType, WorkflowTemplate> = {
hugo: createHugoWorkflow(),
jekyll: createJekyllWorkflow(),
docusaurus: createDocusaurusWorkflow(),
mkdocs: createMkDocsWorkflow(),
eleventy: createEleventyWorkflow(),
};
```
### Hugo Workflow Template
```yaml
name: Deploy Hugo Documentation
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Setup Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: "{{ hugo_version }}"
extended: { { hugo_extended } }
- name: Setup Pages
id: pages
uses: actions/configure-pages@v5
- name: Build with Hugo
env:
HUGO_ENVIRONMENT: production
HUGO_ENV: production
run: |
hugo \
--gc \
--minify \
--baseURL "${{ '{{ steps.pages.outputs.base_url }}' }}/"
- name: Upload artifact
uses: actions/upload-pages-artifact@v4
with:
path: ./public
deploy:
environment:
name: github-pages
url: ${{ '{{ steps.deployment.outputs.page_url }}' }}
runs-on: ubuntu-latest
needs: build
permissions:
contents: read
pages: write
id-token: write
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
```
### Docusaurus Workflow Template
```yaml
name: Deploy Docusaurus Documentation
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "{{ node_version }}"
cache: { { package_manager } }
- name: Install dependencies
run: { { install_command } }
- name: Build website
run: { { build_command } }
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v4
with:
path: { { build_output_directory } }
deploy:
environment:
name: github-pages
url: ${{ '{{ steps.deployment.outputs.page_url }}' }}
runs-on: ubuntu-latest
needs: build
permissions:
contents: read
pages: write
id-token: write
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
```
### Workflow Generation Logic
```typescript
interface WorkflowGenerationConfig {
ssg: SSGType;
projectAnalysis: ProjectAnalysis;
deploymentPreferences: DeploymentPreferences;
securityRequirements: SecurityConfig;
}
class WorkflowGenerator {
generateWorkflow(config: WorkflowGenerationConfig): WorkflowDefinition {
const template = this.getSSGTemplate(config.ssg);
const customizations = this.analyzeCustomizations(config.projectAnalysis);
const optimizations = this.calculateOptimizations(config);
return this.mergeConfiguration(template, customizations, optimizations);
}
private getSSGTemplate(ssg: SSGType): WorkflowTemplate {
return WORKFLOW_TEMPLATES[ssg];
}
private analyzeCustomizations(
analysis: ProjectAnalysis,
): WorkflowCustomizations {
return {
nodeVersion: this.detectNodeVersion(analysis),
packageManager: this.detectPackageManager(analysis),
buildCommand: this.detectBuildCommand(analysis),
outputDirectory: this.detectOutputDirectory(analysis),
environmentVariables: this.extractEnvironmentNeeds(analysis),
};
}
private calculateOptimizations(
config: WorkflowGenerationConfig,
): WorkflowOptimizations {
return {
caching: this.calculateCachingStrategy(config),
parallelization: this.identifyParallelizationOpportunities(config),
incrementalBuild: this.assessIncrementalBuildOptions(config),
securityHardening: this.applySecurityBestPractices(config),
};
}
}
```
### Repository Configuration Management
```typescript
interface RepositoryConfiguration {
pagesSource: PagesSourceConfig;
branchProtection: BranchProtectionConfig;
secrets: SecretsConfig;
environmentSettings: EnvironmentSettings;
}
class RepositoryConfigurationManager {
async configureRepository(
repoPath: string,
config: RepositoryConfiguration,
): Promise<ConfigurationResult> {
try {
return {
pagesConfiguration: await this.configurePagesSettings(
config.pagesSource,
),
branchSetup: await this.setupBranchConfiguration(
config.branchProtection,
),
secretsManagement: await this.configureSecrets(config.secrets),
environmentSetup: await this.setupEnvironments(
config.environmentSettings,
),
};
} catch (error) {
console.error(`Failed to configure repository at ${repoPath}:`, error);
throw new Error(
`Repository configuration failed: ${
error instanceof Error ? error.message : "Unknown error"
}`,
);
}
}
private async configurePagesSettings(
config: PagesSourceConfig,
): Promise<void> {
// Configure GitHub Pages source (GitHub Actions vs. branch-based)
// Set custom domain if specified
// Configure HTTPS enforcement
}
private async setupBranchConfiguration(
config: BranchProtectionConfig,
): Promise<void> {
// Create gh-pages branch if needed
// Configure branch protection rules
// Set up required status checks
}
}
```
### Deployment Verification System
```typescript
interface DeploymentVerification {
healthChecks: HealthCheck[];
performanceTests: PerformanceTest[];
accessibilityTests: AccessibilityTest[];
linkValidation: LinkValidationConfig;
}
class DeploymentVerifier {
async verifyDeployment(
siteUrl: string,
config: DeploymentVerification,
): Promise<VerificationReport> {
try {
const results = await Promise.allSettled([
this.runHealthChecks(siteUrl, config.healthChecks),
this.runPerformanceTests(siteUrl, config.performanceTests),
this.runAccessibilityTests(siteUrl, config.accessibilityTests),
this.validateLinks(siteUrl, config.linkValidation),
]);
// Handle partial failures gracefully
const processedResults = results
.map((result, index) => {
if (result.status === "fulfilled") {
return result.value;
} else {
console.warn(`Verification step ${index} failed:`, result.reason);
return null;
}
})
.filter((result) => result !== null);
return this.generateVerificationReport(processedResults);
} catch (error) {
throw new Error(`Deployment verification failed: ${error.message}`);
}
}
private async runHealthChecks(
siteUrl: string,
checks: HealthCheck[],
): Promise<HealthCheckResult[]> {
try {
const results = await Promise.allSettled(
checks.map((check) => this.executeHealthCheck(siteUrl, check)),
);
return results
.filter(
(result): result is PromiseFulfilledResult<HealthCheckResult> =>
result.status === "fulfilled",
)
.map((result) => result.value);
} catch (error) {
throw new Error(`Health checks failed: ${error.message}`);
}
}
private async executeHealthCheck(
siteUrl: string,
check: HealthCheck,
): Promise<HealthCheckResult> {
// Verify site accessibility
// Check for broken links
// Validate content rendering
// Test mobile responsiveness
// Verify search functionality if applicable
}
}
```
### Error Handling and Troubleshooting
```typescript
interface TroubleshootingGuide {
commonErrors: ErrorPattern[];
diagnosticSteps: DiagnosticStep[];
resolutionGuides: ResolutionGuide[];
escalationPaths: EscalationPath[];
}
const COMMON_DEPLOYMENT_ERRORS: ErrorPattern[] = [
{
pattern: /ENOENT.*package\.json/,
category: "dependency",
description: "Package.json not found or missing dependencies",
resolution: "Verify package.json exists and run npm install",
preventionTips: [
"Always commit package.json",
"Use package-lock.json for version consistency",
],
},
{
pattern: /Permission denied.*write/,
category: "permissions",
description: "Insufficient permissions for GitHub Pages deployment",
resolution: "Check workflow permissions and repository settings",
preventionTips: [
"Use recommended workflow permissions",
"Verify Pages deployment source",
],
},
// ... additional error patterns
];
class DeploymentTroubleshooter {
analyzeBuildFailure(buildLog: string): TroubleshootingReport {
const detectedErrors = this.detectErrorPatterns(buildLog);
const diagnosticResults = this.runDiagnostics(detectedErrors);
const resolutionSteps = this.generateResolutionSteps(detectedErrors);
return {
detectedIssues: detectedErrors,
diagnostics: diagnosticResults,
recommendedActions: resolutionSteps,
escalationGuidance: this.getEscalationGuidance(detectedErrors),
};
}
}
```
## Security Considerations
### Workflow Security Best Practices
- **Minimal Permissions**: Use least privilege principle for workflow permissions
- **Dependency Scanning**: Automated vulnerability detection in build dependencies
- **Secrets Management**: Proper handling of sensitive configuration data
- **Supply Chain Security**: Pin action versions and verify checksums
### Security Configuration Template
```yaml
permissions:
contents: read # Read repository contents
pages: write # Deploy to GitHub Pages
id-token: write # Use OIDC token for authentication
security:
dependency-scanning:
enabled: true
auto-update: true
workflow-hardening:
pin-actions: true
verify-checksums: true
minimal-permissions: true
```
## Performance Optimization
### Build Optimization Strategies
- **Intelligent Caching**: Cache dependencies, build artifacts, and intermediate files
- **Incremental Builds**: Build only changed content when possible
- **Parallel Processing**: Utilize available CPU cores for build tasks
- **Resource Optimization**: Optimize memory usage and disk I/O
### Performance Monitoring
```typescript
interface BuildPerformanceMetrics {
totalBuildTime: number;
dependencyInstallTime: number;
compilationTime: number;
deploymentTime: number;
cacheHitRate: number;
resourceUsage: ResourceUsageMetrics;
}
class PerformanceMonitor {
trackBuildPerformance(buildLog: string): BuildPerformanceMetrics {
return {
totalBuildTime: this.extractTotalTime(buildLog),
dependencyInstallTime: this.extractDependencyTime(buildLog),
compilationTime: this.extractCompilationTime(buildLog),
deploymentTime: this.extractDeploymentTime(buildLog),
cacheHitRate: this.calculateCacheEfficiency(buildLog),
resourceUsage: this.analyzeResourceUsage(buildLog),
};
}
generateOptimizationRecommendations(
metrics: BuildPerformanceMetrics,
): OptimizationRecommendation[] {
const recommendations: OptimizationRecommendation[] = [];
if (metrics.cacheHitRate < 0.8) {
recommendations.push({
type: "caching",
priority: "high",
description: "Improve caching strategy to reduce build times",
implementation:
"Configure additional cache paths and improve cache keys",
});
}
return recommendations;
}
}
```
## Future Enhancements
### Advanced Deployment Features
- **Multi-environment deployment**: Staging and production environment management
- **Blue-green deployments**: Zero-downtime deployment strategies
- **Rollback capabilities**: Automated rollback on deployment failures
- **A/B testing support**: Deploy multiple versions for testing
### Integration Enhancements
- **CDN integration**: Automatic CDN configuration for improved performance
- **Analytics integration**: Built-in analytics and monitoring setup
- **Search integration**: Automated search index generation and deployment
- **Monitoring integration**: Health monitoring and alerting setup
## Testing Strategy
### Workflow Testing
- **Unit tests**: Individual workflow components and template generation
- **Integration tests**: Full deployment workflows across different SSGs
- **End-to-end tests**: Complete documentation site deployment and verification
- **Performance tests**: Build time and resource usage benchmarks
### Validation Framework
```typescript
describe("DeploymentWorkflows", () => {
it("should generate valid Hugo workflow for typical project");
it("should handle complex Docusaurus configuration");
it("should optimize caching for large MkDocs sites");
it("should provide meaningful error messages for common failures");
it("should verify successful deployment and site accessibility");
});
```
## References
- [GitHub Pages Documentation](https://docs.github.com/en/pages)
- [GitHub Actions Best Practices](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions)
- [Static Site Deployment Strategies](https://jamstack.org/best-practices/)
- [JAMstack Architecture Guide](https://jamstack.org/what-is-jamstack/)
```
--------------------------------------------------------------------------------
/tests/tools/all-tools.test.ts:
--------------------------------------------------------------------------------
```typescript
// Comprehensive tests for all MCP tools to achieve 80% coverage
import { promises as fs } from "fs";
import path from "path";
import os from "os";
import tmp from "tmp";
import { analyzeRepository } from "../../src/tools/analyze-repository";
import { recommendSSG } from "../../src/tools/recommend-ssg";
import { generateConfig } from "../../src/tools/generate-config";
import { setupStructure } from "../../src/tools/setup-structure";
import { deployPages } from "../../src/tools/deploy-pages";
import { verifyDeployment } from "../../src/tools/verify-deployment";
import { evaluateReadmeHealth } from "../../src/tools/evaluate-readme-health";
import { readmeBestPractices } from "../../src/tools/readme-best-practices";
describe("All MCP Tools Coverage Tests", () => {
let tempDir: string;
beforeAll(async () => {
tempDir = path.join(os.tmpdir(), "documcp-coverage-tests");
await fs.mkdir(tempDir, { recursive: true });
});
afterAll(async () => {
try {
await fs.rm(tempDir, { recursive: true, force: true });
} catch (error) {
// Ignore cleanup errors
}
});
describe("analyze_repository", () => {
it("should analyze JavaScript project", async () => {
const jsRepo = await createJSRepo();
const result = await analyzeRepository({
path: jsRepo,
depth: "standard",
});
expect(result.content).toBeDefined();
const analysisData = JSON.parse(
result.content.find((c) => c.text.includes('"ecosystem"'))!.text,
);
expect(analysisData.dependencies.ecosystem).toBe("javascript");
});
it("should analyze Python project", async () => {
const pyRepo = await createPythonRepo();
const result = await analyzeRepository({
path: pyRepo,
depth: "standard",
});
expect(result.content).toBeDefined();
const analysisData = JSON.parse(
result.content.find((c) => c.text.includes('"ecosystem"'))!.text,
);
expect(analysisData.dependencies.ecosystem).toBe("python");
});
it("should handle different depths", async () => {
const repo = await createJSRepo();
const quickResult = await analyzeRepository({
path: repo,
depth: "quick",
});
const deepResult = await analyzeRepository({ path: repo, depth: "deep" });
expect(quickResult.content).toBeDefined();
expect(deepResult.content).toBeDefined();
});
it("should detect CI/CD workflows", async () => {
const ciRepo = await createRepoWithCI();
const result = await analyzeRepository({
path: ciRepo,
depth: "standard",
});
const analysisData = JSON.parse(
result.content.find((c) => c.text.includes('"hasCI"'))!.text,
);
expect(analysisData.structure.hasCI).toBe(true);
});
it("should handle repository without dependencies", async () => {
const emptyRepo = await createEmptyRepo();
const result = await analyzeRepository({
path: emptyRepo,
depth: "standard",
});
const analysisData = JSON.parse(
result.content.find((c) => c.text.includes('"ecosystem"'))!.text,
);
expect(analysisData.dependencies.ecosystem).toBe("unknown");
});
});
describe("recommend_ssg", () => {
it("should provide recommendation with confidence", async () => {
const result = await recommendSSG({ analysisId: "test-123" });
expect(result.content).toBeDefined();
const recData = JSON.parse(
result.content.find((c) => c.text.includes('"confidence"'))!.text,
);
expect(recData.confidence).toBeGreaterThan(0);
expect(recData.confidence).toBeLessThanOrEqual(1);
});
it("should handle preferences", async () => {
const result = await recommendSSG({
analysisId: "test-456",
preferences: {
priority: "simplicity",
ecosystem: "javascript",
},
});
expect(result.content).toBeDefined();
const recData = JSON.parse(
result.content.find((c) => c.text.includes('"recommended"'))!.text,
);
expect(["jekyll", "hugo", "docusaurus", "mkdocs", "eleventy"]).toContain(
recData.recommended,
);
});
it("should provide alternatives", async () => {
const result = await recommendSSG({ analysisId: "test-789" });
const recData = JSON.parse(
result.content.find((c) => c.text.includes('"alternatives"'))!.text,
);
expect(Array.isArray(recData.alternatives)).toBe(true);
expect(recData.alternatives.length).toBeGreaterThan(0);
});
});
describe("generate_config", () => {
it("should generate Docusaurus config", async () => {
const outputDir = await createTempDir("docusaurus-config");
const result = await generateConfig({
ssg: "docusaurus",
projectName: "Test Docusaurus",
outputPath: outputDir,
});
expect(result.content).toBeDefined();
expect(
await fileExists(path.join(outputDir, "docusaurus.config.js")),
).toBe(true);
expect(await fileExists(path.join(outputDir, "package.json"))).toBe(true);
});
it("should generate MkDocs config", async () => {
const outputDir = await createTempDir("mkdocs-config");
const result = await generateConfig({
ssg: "mkdocs",
projectName: "Test MkDocs",
outputPath: outputDir,
});
expect(result.content).toBeDefined();
expect(await fileExists(path.join(outputDir, "mkdocs.yml"))).toBe(true);
expect(await fileExists(path.join(outputDir, "requirements.txt"))).toBe(
true,
);
});
it("should generate Hugo config", async () => {
const outputDir = await createTempDir("hugo-config");
const result = await generateConfig({
ssg: "hugo",
projectName: "Test Hugo",
outputPath: outputDir,
});
expect(result.content).toBeDefined();
expect(await fileExists(path.join(outputDir, "hugo.toml"))).toBe(true);
});
it("should generate Jekyll config", async () => {
const outputDir = await createTempDir("jekyll-config");
const result = await generateConfig({
ssg: "jekyll",
projectName: "Test Jekyll",
outputPath: outputDir,
});
expect(result.content).toBeDefined();
expect(await fileExists(path.join(outputDir, "_config.yml"))).toBe(true);
expect(await fileExists(path.join(outputDir, "Gemfile"))).toBe(true);
});
it("should generate Eleventy config", async () => {
const outputDir = await createTempDir("eleventy-config");
const result = await generateConfig({
ssg: "eleventy",
projectName: "Test Eleventy",
outputPath: outputDir,
});
expect(result.content).toBeDefined();
expect(await fileExists(path.join(outputDir, ".eleventy.js"))).toBe(true);
expect(await fileExists(path.join(outputDir, "package.json"))).toBe(true);
});
});
describe("setup_structure", () => {
it("should create Diataxis structure", async () => {
const docsDir = await createTempDir("diataxis-structure");
const result = await setupStructure({
path: docsDir,
ssg: "docusaurus",
includeExamples: true,
});
expect(result.content).toBeDefined();
const categories = ["tutorials", "how-to", "reference", "explanation"];
for (const category of categories) {
expect(await fileExists(path.join(docsDir, category, "index.md"))).toBe(
true,
);
}
expect(await fileExists(path.join(docsDir, "index.md"))).toBe(true);
});
it("should create structure without examples", async () => {
const docsDir = await createTempDir("no-examples");
const result = await setupStructure({
path: docsDir,
ssg: "mkdocs",
includeExamples: false,
});
expect(result.content).toBeDefined();
const tutorialsFiles = await fs.readdir(path.join(docsDir, "tutorials"));
expect(tutorialsFiles).toEqual(["index.md"]); // Only index, no examples
});
it("should handle different SSG formats", async () => {
const docusaurusDir = await createTempDir("docusaurus-format");
await setupStructure({
path: docusaurusDir,
ssg: "docusaurus",
includeExamples: true,
});
const content = await fs.readFile(
path.join(docusaurusDir, "tutorials", "index.md"),
"utf-8",
);
expect(content).toContain("id: tutorials-index");
const jekyllDir = await createTempDir("jekyll-format");
await setupStructure({
path: jekyllDir,
ssg: "jekyll",
includeExamples: true,
});
const jekyllContent = await fs.readFile(
path.join(jekyllDir, "tutorials", "index.md"),
"utf-8",
);
expect(jekyllContent).toContain("title:");
});
});
describe("deploy_pages", () => {
it("should create Docusaurus deployment workflow", async () => {
const repoDir = await createTempDir("docusaurus-deploy");
const result = await deployPages({
repository: repoDir,
ssg: "docusaurus",
});
expect(result.content).toBeDefined();
const workflowPath = path.join(
repoDir,
".github",
"workflows",
"deploy-docs.yml",
);
expect(await fileExists(workflowPath)).toBe(true);
const workflowContent = await fs.readFile(workflowPath, "utf-8");
expect(workflowContent).toContain("Deploy Docusaurus");
expect(workflowContent).toContain("npm run build");
});
it("should create MkDocs deployment workflow", async () => {
const repoDir = await createTempDir("mkdocs-deploy");
const result = await deployPages({
repository: repoDir,
ssg: "mkdocs",
});
const workflowContent = await fs.readFile(
path.join(repoDir, ".github", "workflows", "deploy-docs.yml"),
"utf-8",
);
expect(workflowContent).toContain("mkdocs gh-deploy");
});
it("should handle custom domain", async () => {
const repoDir = await createTempDir("custom-domain");
const result = await deployPages({
repository: repoDir,
ssg: "docusaurus",
customDomain: "docs.example.com",
});
expect(result.content).toBeDefined();
expect(await fileExists(path.join(repoDir, "CNAME"))).toBe(true);
const cnameContent = await fs.readFile(
path.join(repoDir, "CNAME"),
"utf-8",
);
expect(cnameContent.trim()).toBe("docs.example.com");
});
it("should handle different branches", async () => {
const repoDir = await createTempDir("custom-branch");
await deployPages({
repository: repoDir,
ssg: "hugo",
branch: "main",
});
const workflowContent = await fs.readFile(
path.join(repoDir, ".github", "workflows", "deploy-docs.yml"),
"utf-8",
);
expect(workflowContent).toContain("Deploy Hugo");
});
});
describe("verify_deployment", () => {
it("should verify complete setup", async () => {
const repoDir = await createCompleteRepo();
const result = await verifyDeployment({
repository: repoDir,
});
expect(result.content).toBeDefined();
const verification = JSON.parse(result.content[0].text);
expect(verification.overallStatus).toBe("Ready for deployment");
expect(verification.summary.passed).toBeGreaterThan(0);
});
it("should identify missing components", async () => {
const emptyDir = await createTempDir("empty-verify");
const result = await verifyDeployment({
repository: emptyDir,
});
const verification = JSON.parse(result.content[0].text);
expect(verification.overallStatus).toContain("Configuration required");
expect(verification.summary.failed).toBeGreaterThan(0);
expect(
verification.checks.some((check: any) =>
check.message.includes("No .github/workflows"),
),
).toBe(true);
});
it("should handle different repository paths", async () => {
const relativeResult = await verifyDeployment({ repository: "." });
expect(relativeResult.content).toBeDefined();
const httpResult = await verifyDeployment({
repository: "https://github.com/user/repo",
});
expect(httpResult.content).toBeDefined();
});
it("should provide recommendations", async () => {
const incompleteDir = await createTempDir("incomplete");
await fs.writeFile(path.join(incompleteDir, "README.md"), "# Test");
const result = await verifyDeployment({
repository: incompleteDir,
});
const resultText = result.content.map((c) => c.text).join("\n");
expect(resultText).toContain("→");
expect(resultText).toContain("deploy_pages tool");
});
it("should check for different config patterns", async () => {
const configDir = await createTempDir("config-check");
await fs.writeFile(
path.join(configDir, "docusaurus.config.js"),
"module.exports = {};",
);
const result = await verifyDeployment({
repository: configDir,
});
const resultText = result.content.map((c) => c.text).join("\n");
expect(resultText).toContain("SSG Configuration");
expect(resultText).toContain("docusaurus.config.js");
});
});
describe("evaluate_readme_health", () => {
it("should evaluate README health with minimal input", async () => {
const readmePath = await createReadmeFile("Basic project README");
const result = await evaluateReadmeHealth({
readme_path: readmePath,
});
expect(result.content).toBeDefined();
expect(result.isError).toBe(false);
const healthData = result.content.find((c) =>
c.text.includes("healthReport"),
);
expect(healthData).toBeDefined();
});
it("should handle different project types", async () => {
const readmePath = await createReadmeFile(
"Enterprise tool documentation",
);
const result = await evaluateReadmeHealth({
readme_path: readmePath,
project_type: "enterprise_tool",
});
expect(result.content).toBeDefined();
expect(result.isError).toBe(false);
});
it("should provide health components and scoring", async () => {
const readmePath = await createReadmeFile(`# Complete Project
## Description
Detailed description
## Installation
Installation steps
## Usage
Usage examples
## Contributing
Contributing guidelines
## License
MIT License`);
const result = await evaluateReadmeHealth({
readme_path: readmePath,
});
const dataContent = result.content.find((c) =>
c.text.includes("healthReport"),
);
const data = JSON.parse(dataContent!.text);
expect(data.healthReport.overallScore).toBeGreaterThanOrEqual(0);
expect(data.healthReport.grade).toBeDefined();
expect(data.healthReport.components).toBeDefined();
});
});
describe("readme_best_practices", () => {
it("should analyze README best practices", async () => {
const readmePath = await createReadmeFile("Basic library README");
const result = await readmeBestPractices({
readme_path: readmePath,
project_type: "library",
});
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data!.bestPracticesReport).toBeDefined();
});
it("should handle template generation", async () => {
const outputDir = await createTempDir("best-practices-template");
const result = await readmeBestPractices({
readme_path: path.join(outputDir, "missing.md"),
generate_template: true,
output_directory: outputDir,
project_type: "application",
});
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
});
it("should provide checklist and recommendations", async () => {
const readmePath = await createReadmeFile(`# Library Project
## Installation
npm install my-lib
## Usage
Basic usage here
## API
API documentation
`);
const result = await readmeBestPractices({
readme_path: readmePath,
});
expect(result.success).toBe(true);
expect(result.data!.bestPracticesReport.checklist).toBeDefined();
expect(Array.isArray(result.data!.bestPracticesReport.checklist)).toBe(
true,
);
expect(result.data!.recommendations).toBeDefined();
expect(result.data!.nextSteps).toBeDefined();
});
});
// Helper functions
async function createJSRepo(): Promise<string> {
const repoPath = path.join(tempDir, `js-repo-${Date.now()}`);
await fs.mkdir(repoPath, { recursive: true });
await fs.writeFile(
path.join(repoPath, "package.json"),
JSON.stringify(
{
name: "test-js-project",
dependencies: { express: "^4.0.0" },
devDependencies: { jest: "^29.0.0" },
},
null,
2,
),
);
await fs.writeFile(
path.join(repoPath, "index.js"),
'console.log("Hello");',
);
await fs.writeFile(path.join(repoPath, "README.md"), "# JS Project");
return repoPath;
}
async function createPythonRepo(): Promise<string> {
const repoPath = path.join(tempDir, `py-repo-${Date.now()}`);
await fs.mkdir(repoPath, { recursive: true });
await fs.writeFile(path.join(repoPath, "requirements.txt"), "flask>=2.0.0");
await fs.writeFile(path.join(repoPath, "main.py"), 'print("Hello")');
await fs.writeFile(path.join(repoPath, "README.md"), "# Python Project");
return repoPath;
}
async function createRepoWithCI(): Promise<string> {
const repoPath = path.join(tempDir, `ci-repo-${Date.now()}`);
await fs.mkdir(path.join(repoPath, ".github", "workflows"), {
recursive: true,
});
await fs.writeFile(
path.join(repoPath, ".github", "workflows", "ci.yml"),
"name: CI\non: push",
);
await fs.writeFile(path.join(repoPath, "README.md"), "# CI Project");
return repoPath;
}
async function createEmptyRepo(): Promise<string> {
const repoPath = path.join(tempDir, `empty-repo-${Date.now()}`);
await fs.mkdir(repoPath, { recursive: true });
await fs.writeFile(path.join(repoPath, "README.md"), "# Empty Project");
return repoPath;
}
async function createTempDir(suffix: string): Promise<string> {
const dirPath = path.join(tempDir, `${suffix}-${Date.now()}`);
await fs.mkdir(dirPath, { recursive: true });
return dirPath;
}
async function createCompleteRepo(): Promise<string> {
const repoPath = await createTempDir("complete-repo");
// Create workflow
await fs.mkdir(path.join(repoPath, ".github", "workflows"), {
recursive: true,
});
await fs.writeFile(
path.join(repoPath, ".github", "workflows", "deploy.yml"),
"name: Deploy\non: push",
);
// Create docs
await fs.mkdir(path.join(repoPath, "docs"), { recursive: true });
await fs.writeFile(path.join(repoPath, "docs", "index.md"), "# Docs");
// Create config
await fs.writeFile(
path.join(repoPath, "docusaurus.config.js"),
"module.exports = {};",
);
// Create build output
await fs.mkdir(path.join(repoPath, "build"), { recursive: true });
await fs.writeFile(
path.join(repoPath, "build", "index.html"),
"<html></html>",
);
return repoPath;
}
async function fileExists(filePath: string): Promise<boolean> {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
async function createReadmeFile(content: string): Promise<string> {
const file = tmp.fileSync({ postfix: ".md", keep: false });
await fs.writeFile(file.name, content);
return file.name;
}
});
```
--------------------------------------------------------------------------------
/docs/reference/mcp-tools.md:
--------------------------------------------------------------------------------
```markdown
---
documcp:
last_updated: "2025-11-20T00:46:21.963Z"
last_validated: "2025-11-20T00:46:21.963Z"
auto_updated: false
update_frequency: monthly
---
# MCP Tools API Reference
DocuMCP provides a comprehensive set of tools via the Model Context Protocol (MCP). These tools enable intelligent documentation deployment through repository analysis, SSG recommendations, and automated GitHub Pages setup.
## Implementation Details
DocuMCP implements the MCP protocol using the low-level `Server` class from `@modelcontextprotocol/sdk/server/index.js` with `StdioServerTransport` for process-based communication. Tools are registered manually using `setRequestHandler` with `CallToolRequestSchema` and `ListToolsRequestSchema`, providing full control over tool execution and response formatting.
## Core Documentation Tools
### analyze_repository
**Description**: Analyze repository structure, dependencies, and documentation needs
**Parameters**:
- `path` (string, required): Path to the repository to analyze
- `depth` (enum, optional): Analysis depth level
- `"quick"`: Fast overview focusing on basic structure
- `"standard"`: Comprehensive analysis (default)
- `"deep"`: Detailed analysis with advanced insights
**Returns**: Analysis object containing:
- `id`: Unique analysis identifier for use in other tools
- `timestamp`: Analysis execution time
- `structure`: File counts, languages, and project features
- `dependencies`: Package ecosystem and dependency analysis
- `documentation`: Existing documentation assessment
- `recommendations`: Project classification and team size estimates
**Example**:
```json
{
"path": "/path/to/repository",
"depth": "standard"
}
```
### recommend_ssg
**Description**: Recommend the best static site generator based on project analysis
**Parameters**:
- `analysisId` (string, required): ID from previous repository analysis
- `preferences` (object, optional):
- `priority`: `"simplicity"`, `"features"`, or `"performance"`
- `ecosystem`: `"javascript"`, `"python"`, `"ruby"`, `"go"`, or `"any"`
**Returns**: Recommendation object with weighted scoring and justifications
**Example**:
```json
{
"analysisId": "analysis_abc123",
"preferences": {
"priority": "simplicity",
"ecosystem": "javascript"
}
}
```
### generate_config
**Description**: Generate configuration files for the selected static site generator
**Parameters**:
- `ssg` (enum, required): `"jekyll"`, `"hugo"`, `"docusaurus"`, `"mkdocs"`, or `"eleventy"`
- `projectName` (string, required): Name of the project
- `projectDescription` (string, optional): Brief description
- `outputPath` (string, required): Where to generate config files
**Returns**: Generated configuration files and setup instructions
**Example**:
```json
{
"ssg": "hugo",
"projectName": "My Documentation Site",
"outputPath": "./docs"
}
```
### setup_structure
**Description**: Create Diataxis-compliant documentation structure
**Parameters**:
- `path` (string, required): Root path for documentation
- `ssg` (enum, required): Static site generator type
- `includeExamples` (boolean, optional, default: true): Include example content
**Returns**: Created directory structure following Diataxis framework:
- **tutorials/**: Learning-oriented guides for skill acquisition (study context)
- **how-to/**: Problem-solving guides for specific tasks (work context)
- **reference/**: Information-oriented content for lookup and verification (information context)
- **explanation/**: Understanding-oriented content for context and background (understanding context)
**Example**:
```json
{
"path": "./docs",
"ssg": "mkdocs",
"includeExamples": true
}
```
### deploy_pages
**Description**: Set up GitHub Pages deployment workflow
**Parameters**:
- `repository` (string, required): Repository path or URL
- `ssg` (enum, required): Static site generator type
- `branch` (string, optional, default: "gh-pages"): Deployment branch
- `customDomain` (string, optional): Custom domain name
**Returns**: GitHub Actions workflow files for automated deployment
**Example**:
```json
{
"repository": "username/repository",
"ssg": "docusaurus",
"customDomain": "docs.example.com"
}
```
### verify_deployment
**Description**: Verify and troubleshoot GitHub Pages deployment
**Parameters**:
- `repository` (string, required): Repository path or URL
- `url` (string, optional): Expected deployment URL
**Returns**: Deployment status and troubleshooting recommendations
**Example**:
```json
{
"repository": "username/repository",
"url": "https://username.github.io/repository"
}
```
## Content Management Tools
### populate_diataxis_content
**Description**: Intelligently populate Diataxis documentation with project-specific content
**Parameters**:
- `analysisId` (string, required): Repository analysis ID
- `docsPath` (string, required): Path to documentation directory
- `populationLevel` (enum, optional, default: "comprehensive"): Content generation level
- `includeProjectSpecific` (boolean, optional, default: true): Include project-specific content
- `preserveExisting` (boolean, optional, default: true): Preserve existing content
- `technologyFocus` (array of strings, optional): Specific technologies to emphasize
**Returns**: Populated content metrics and file creation summary
### update_existing_documentation
**Description**: Intelligently analyze and update existing documentation using memory insights
**Parameters**:
- `analysisId` (string, required): Repository analysis ID
- `docsPath` (string, required): Path to existing documentation directory
- `compareMode` (enum, optional, default: "comprehensive"): Comparison mode
- `updateStrategy` (enum, optional, default: "moderate"): Update aggressiveness
- `preserveStyle` (boolean, optional, default: true): Preserve existing style
- `focusAreas` (array of strings, optional): Specific areas to focus on
**Returns**: Update recommendations and gap analysis
### detect_documentation_gaps
**Description**: Analyze repository and existing documentation to identify missing content
**Parameters**:
- `repositoryPath` (string, required): Path to the repository
- `documentationPath` (string, optional): Path to existing documentation
- `analysisId` (string, optional): Optional existing analysis ID to reuse
- `depth` (enum, optional, default: "standard"): Analysis depth
**Returns**: Identified gaps and recommendations for improvement
## Validation Tools
### validate_diataxis_content
**Description**: Validate the accuracy, completeness, and compliance of generated Diataxis documentation
**Parameters**:
- `contentPath` (string, required): Path to documentation directory to validate
- `analysisId` (string, optional): Repository analysis ID for context
- `validationType` (enum, optional, default: "all"): Type of validation
- `includeCodeValidation` (boolean, optional, default: true): Validate code examples
- `confidence` (enum, optional, default: "moderate"): Validation confidence level
**Returns**: Validation results with issues, recommendations, and confidence scores
### validate_content
**Description**: Validate general content quality including links and code syntax
**Parameters**:
- `contentPath` (string, required): Path to content directory
- `validationType` (string, optional, default: "all"): Validation type
- `includeCodeValidation` (boolean, optional, default: true): Validate code blocks
- `followExternalLinks` (boolean, optional, default: false): Check external URLs
**Returns**: Content validation results with broken links and code errors
### check_documentation_links
**Description**: Comprehensive link checking for documentation deployment
**Parameters**:
- `documentation_path` (string, optional, default: "./docs"): Documentation directory
- `check_external_links` (boolean, optional, default: true): Validate external URLs
- `check_internal_links` (boolean, optional, default: true): Validate internal references
- `check_anchor_links` (boolean, optional, default: true): Validate anchor links
- `timeout_ms` (number, optional, default: 5000): Request timeout
- `max_concurrent_checks` (number, optional, default: 5): Concurrent check limit
**Returns**: Comprehensive link validation report
## Testing and Deployment Tools
### test_local_deployment
**Description**: Test documentation build and local server before deploying to GitHub Pages
**Parameters**:
- `repositoryPath` (string, required): Path to the repository
- `ssg` (enum, required): Static site generator type
- `port` (number, optional, default: 3000): Local server port
- `timeout` (number, optional, default: 60): Build timeout in seconds
- `skipBuild` (boolean, optional, default: false): Skip build step
**Returns**: Local testing results and server status
## README Management Tools
### evaluate_readme_health
**Description**: Evaluate README files for community health and onboarding effectiveness
**Parameters**:
- `readme_path` (string, required): Path to README file
- `project_type` (enum, optional, default: "community_library"): Project type
- `repository_path` (string, optional): Repository path for context
**Returns**: Health evaluation with scores and recommendations
### readme_best_practices
**Description**: Analyze README files against best practices checklist
**Parameters**:
- `readme_path` (string, required): Path to README file
- `project_type` (enum, optional, default: "library"): Project type
- `generate_template` (boolean, optional, default: false): Generate templates
- `target_audience` (enum, optional, default: "mixed"): Target audience
**Returns**: Best practices analysis and improvement recommendations
### generate_readme_template
**Description**: Generate standardized README templates for different project types
**Parameters**:
- `projectName` (string, required): Name of the project
- `description` (string, required): Brief project description
- `templateType` (enum, required): Project template type
- `author` (string, optional): Project author/organization
- `license` (string, optional, default: "MIT"): Project license
- `outputPath` (string, optional): Output file path
**Returns**: Generated README template content
### validate_readme_checklist
**Description**: Validate README files against community best practices checklist
**Parameters**:
- `readmePath` (string, required): Path to README file
- `projectPath` (string, optional): Project directory for context
- `strict` (boolean, optional, default: false): Use strict validation
- `outputFormat` (enum, optional, default: "console"): Output format
**Returns**: Validation report with detailed scoring
### analyze_readme
**Description**: Comprehensive README analysis with length assessment and optimization opportunities
**Parameters**:
- `project_path` (string, required): Path to project directory
- `target_audience` (enum, optional, default: "community_contributors"): Target audience
- `optimization_level` (enum, optional, default: "moderate"): Optimization level
- `max_length_target` (number, optional, default: 300): Target max length
**Returns**: README analysis with optimization recommendations
### optimize_readme
**Description**: Optimize README content by restructuring and condensing
**Parameters**:
- `readme_path` (string, required): Path to README file
- `strategy` (enum, optional, default: "community_focused"): Optimization strategy
- `max_length` (number, optional, default: 300): Target maximum length
- `include_tldr` (boolean, optional, default: true): Include TL;DR section
- `create_docs_directory` (boolean, optional, default: true): Create docs directory
**Returns**: Optimized README content and extracted documentation
## Documentation Freshness Tracking Tools
DocuMCP includes comprehensive tools for tracking and managing documentation freshness, ensuring your documentation stays up-to-date and identifying files that need attention.
### track_documentation_freshness
**Description**: Scan documentation directory for staleness markers and identify files needing updates based on configurable time thresholds (minutes, hours, days)
**Parameters**:
- `docsPath` (string, required): Path to documentation directory
- `projectPath` (string, optional): Path to project root (for knowledge graph tracking)
- `warningThreshold` (object, optional): Warning threshold (yellow flag)
- `value` (number, positive): Threshold value
- `unit` (enum): `"minutes"`, `"hours"`, or `"days"`
- `staleThreshold` (object, optional): Stale threshold (orange flag)
- `value` (number, positive): Threshold value
- `unit` (enum): `"minutes"`, `"hours"`, or `"days"`
- `criticalThreshold` (object, optional): Critical threshold (red flag)
- `value` (number, positive): Threshold value
- `unit` (enum): `"minutes"`, `"hours"`, or `"days"`
- `preset` (enum, optional): Use predefined threshold preset
- Options: `"realtime"`, `"active"`, `"recent"`, `"weekly"`, `"monthly"`, `"quarterly"`
- `includeFileList` (boolean, optional, default: true): Include detailed file list in response
- `sortBy` (enum, optional, default: "staleness"): Sort order for file list
- Options: `"age"`, `"path"`, `"staleness"`
- `storeInKG` (boolean, optional, default: true): Store tracking event in knowledge graph for historical analysis
**Returns**: Freshness report with:
- Summary statistics (total files, fresh, warning, stale, critical)
- Detailed file list with staleness levels
- Age information for each file
- Recommendations for action
**Example**:
```json
{
"docsPath": "/path/to/docs",
"preset": "monthly",
"includeFileList": true
}
```
**Default Thresholds**:
- Warning: 7 days
- Stale: 30 days
- Critical: 90 days
### validate_documentation_freshness
**Description**: Validate documentation freshness, initialize metadata for files without it, and update timestamps based on code changes
**Parameters**:
- `docsPath` (string, required): Path to documentation directory
- `projectPath` (string, required): Path to project root (for git integration)
- `initializeMissing` (boolean, optional, default: true): Initialize metadata for files without it
- `updateExisting` (boolean, optional, default: false): Update last_validated timestamp for all files
- `updateFrequency` (enum, optional, default: "monthly"): Default update frequency for new metadata
- Options: `"realtime"`, `"active"`, `"recent"`, `"weekly"`, `"monthly"`, `"quarterly"`
- `validateAgainstGit` (boolean, optional, default: true): Validate against current git commit
**Returns**: Validation report with:
- Files initialized (new metadata created)
- Files updated (existing metadata refreshed)
- Metadata structure for each file
- Recommendations for next steps
**Example**:
```json
{
"docsPath": "/path/to/docs",
"projectPath": "/path/to/project",
"initializeMissing": true,
"validateAgainstGit": true
}
```
**Use Cases**:
- First-time setup: Initialize freshness metadata for all documentation files
- Regular maintenance: Update validation timestamps
- After code changes: Sync documentation freshness with git history
## Sitemap Management Tools
### manage_sitemap
**Description**: Generate, validate, and manage sitemap.xml as the source of truth for documentation links. Sitemap.xml is used for SEO, search engine submission, and deployment tracking.
**Parameters**:
- `action` (enum, required): Action to perform
- `"generate"`: Create new sitemap.xml
- `"validate"`: Check sitemap structure
- `"update"`: Sync sitemap with documentation
- `"list"`: Show all URLs in sitemap
- `docsPath` (string, required): Path to documentation root directory
- `baseUrl` (string, required for generate/update): Base URL for the site (e.g., `https://user.github.io/repo`)
- `includePatterns` (array, optional): File patterns to include
- Default: `["**/*.md", "**/*.html", "**/*.mdx"]`
- `excludePatterns` (array, optional): File patterns to exclude
- Default: `["node_modules", ".git", "dist", "build", ".documcp"]`
- `updateFrequency` (enum, optional): Default change frequency for pages
- Options: `"always"`, `"hourly"`, `"daily"`, `"weekly"`, `"monthly"`, `"yearly"`, `"never"`
- `useGitHistory` (boolean, optional, default: true): Use git history for last modified dates
- `sitemapPath` (string, optional): Custom path for sitemap.xml (default: `docsPath/sitemap.xml`)
**Returns**: Sitemap operation result with:
- Generated/validated sitemap structure
- URL count and statistics
- Validation errors (if any)
- Recommendations for SEO optimization
**Example**:
```json
{
"action": "generate",
"docsPath": "/path/to/docs",
"baseUrl": "https://example.com/docs"
}
```
**Use Cases**:
- SEO optimization: Generate sitemap for search engines
- Link validation: Ensure all documentation pages are discoverable
- Deployment tracking: Monitor documentation changes over time
## Memory System Tools
The memory system provides intelligent learning and pattern recognition across documentation projects.
### memory_recall
**Description**: Recall memories about a project or topic
**Parameters**:
- `query` (string, required): Search query or project ID
- `type` (enum, optional): Memory type to recall
- `limit` (number, optional, default: 10): Maximum results
### memory_insights
**Description**: Get insights and patterns from memory
**Parameters**:
- `projectId` (string, optional): Project ID to analyze
- `timeRange` (object, optional): Time range for analysis
### memory_similar
**Description**: Find similar projects from memory
**Parameters**:
- `analysisId` (string, required): Analysis ID to find similar projects for
- `limit` (number, optional, default: 5): Maximum similar projects
### memory_export
**Description**: Export memories to JSON or CSV
**Parameters**:
- `filter` (object, optional): Filter memories to export
- `format` (enum, optional, default: "json"): Export format
### memory_cleanup
**Description**: Clean up old memories
**Parameters**:
- `daysToKeep` (number, optional, default: 30): Number of days to retain
- `dryRun` (boolean, optional, default: false): Preview without deleting
## Tool Chaining and Workflows
DocuMCP tools are designed to work together in workflows:
1. **Analysis → Recommendation → Implementation**:
```
analyze_repository → recommend_ssg → generate_config → setup_structure → deploy_pages
```
2. **Content Management**:
```
analyze_repository → populate_diataxis_content → validate_diataxis_content
```
3. **Documentation Maintenance**:
```
detect_documentation_gaps → update_existing_documentation → validate_content
```
4. **Freshness Tracking**:
```
validate_documentation_freshness → track_documentation_freshness → (update files as needed)
```
5. **SEO and Sitemap Management**:
```
manage_sitemap (generate) → deploy_pages → manage_sitemap (validate)
```
## Error Handling
All tools return structured responses with error information when failures occur:
```json
{
"content": [
{
"type": "text",
"text": "Error executing tool_name: error_message"
}
],
"isError": true
}
```
## Resource Storage
Tool results are automatically stored as MCP resources with URIs like:
- `documcp://analysis/{id}`: Analysis results
- `documcp://config/{ssg}/{id}`: Configuration files
- `documcp://deployment/{id}`: Deployment workflows
These resources can be accessed later for reference or further processing.
## Version Information
Current DocuMCP version: **0.5.2**
For the latest updates and detailed changelog, see the project repository.
## Recent Additions (v0.5.2)
### Documentation Freshness Tracking
- `track_documentation_freshness`: Monitor documentation staleness with configurable thresholds
- `validate_documentation_freshness`: Initialize and update freshness metadata
### Sitemap Management
- `manage_sitemap`: Generate, validate, and manage sitemap.xml for SEO and deployment tracking
These tools integrate with the knowledge graph system to provide historical analysis and intelligent recommendations.
```
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
```markdown
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### [0.5.2](https://github.com/tosin2013/documcp/compare/v0.5.1...v0.5.2) (2025-11-19)
### 🚀 Features
- add GitHub Copilot instructions and specialized agents ([3ba709f](https://github.com/tosin2013/documcp/commit/3ba709f2e209ae603f0142fa7f55a1d486f67829))
- add MCP prompts and resources for documentation freshness tracking ([2820c0e](https://github.com/tosin2013/documcp/commit/2820c0ed8fe35627e2434f78d2c172a7cdfa7370))
- implement documentation freshness tracking with KG integration and fix js-yaml vulnerability ([978aa5a](https://github.com/tosin2013/documcp/commit/978aa5a7f84049d2a1d8b2da8a30e53a7d3fbf99))
- improve branch coverage to 81.41%, add KG efficiency improvements ([e0f9641](https://github.com/tosin2013/documcp/commit/e0f96419a3b9566677e5defa54df24f0607371ae))
### 🐛 Bug Fixes
- correct sidebar reference for mcp-resource-pattern-redesign ([1168821](https://github.com/tosin2013/documcp/commit/1168821ab933ab84c4d66dcfce1caa7c15e16765)), closes [#19519888749](https://github.com/tosin2013/documcp/issues/19519888749)
- resolve KG storage issues and test failures, improve branch coverage to 81.32% ([9fd72d5](https://github.com/tosin2013/documcp/commit/9fd72d57e67faf370ac9ab13a9aa3bef0e81be49))
- update deploy-docs workflow from Jekyll to Docusaurus ([fdc363a](https://github.com/tosin2013/documcp/commit/fdc363a2af6e4f1bf4155210d4e3e2fe62304236)), closes [#19519733512](https://github.com/tosin2013/documcp/issues/19519733512)
- upgrade GitHub Actions to resolve deprecation warnings ([67fea5d](https://github.com/tosin2013/documcp/commit/67fea5ddf6e1a09907019a3c54edaca39f882936))
### [0.5.1](https://github.com/tosin2013/documcp/compare/v0.5.0...v0.5.1) (2025-11-18)
### 📚 Documentation
- comprehensive documentation validation and updates ([abc8bc5](https://github.com/tosin2013/documcp/commit/abc8bc5d6ac537f27ed83e690a99a3c668356bcd))
### 🐛 Bug Fixes
- add forceExit to Jest config for CI to prevent worker process hangs ([c5dada6](https://github.com/tosin2013/documcp/commit/c5dada607907adb6a04536accb50704435bb2516)), closes [#216](https://github.com/tosin2013/documcp/issues/216)
- remove ADR-010 from sidebar to fix Docker build ([1ce448f](https://github.com/tosin2013/documcp/commit/1ce448ff69e9184f3a48ac9f0143aabb134d7205))
- resolve GitHub Actions build failure ([a1b6b23](https://github.com/tosin2013/documcp/commit/a1b6b23e723f29b829c369feea386ebea601940a))
- update dependencies and adjust security audit workflow ([033a4f3](https://github.com/tosin2013/documcp/commit/033a4f340af903ceb84610531f3d1817964e91b9))
## [0.5.0](https://github.com/tosin2013/documcp/compare/v0.4.1...v0.5.0) (2025-10-12)
### ⚠ BREAKING CHANGES
- GitHub Actions workflow now requires Docusaurus instead of Jekyll
Fixes: Deprecated artifact actions error
Closes: Documentation completeness gaps
### ♻️ Code Refactoring
- implement MCP best practices and resource pattern redesign ([e3de334](https://github.com/tosin2013/documcp/commit/e3de334336580dc1e2e8f0e2302cc7f903a58b7d))
### 🐛 Bug Fixes
- ensure memory storage directory exists before file operations ([19961a1](https://github.com/tosin2013/documcp/commit/19961a1bb017f90931fff8957fd040723a5d0810))
- resolve sitemap test failures and type issues ([232ce57](https://github.com/tosin2013/documcp/commit/232ce57ae1d1d0537122db6fa46e2614416ad929))
### 🚀 Features
- add generate_llm_context tool for dynamic LLM reference generation ([c8e3282](https://github.com/tosin2013/documcp/commit/c8e32823e2eb5f048b98b82f2840e1b4dee61094))
- enhance ListRoots to auto-detect and list documentation directories ([d432c4c](https://github.com/tosin2013/documcp/commit/d432c4c0bf74f1944d142c24c074365c67e3bdd5))
- implement MCP Phase 1 - progress notifications and logging support ([7d0ceeb](https://github.com/tosin2013/documcp/commit/7d0ceeb5e74611ae0f8276c63fed61e58ad16789))
- implement MCP Phase 2 - roots permission system ([ba307af](https://github.com/tosin2013/documcp/commit/ba307afc3fb186db699f10d0fccf6d6935ceee4d))
- implement Phase 3 code-to-docs synchronization and sitemap.xml management ([bbde81b](https://github.com/tosin2013/documcp/commit/bbde81be27938bacd05f1c30765e673e8679e6c7))
- integrate AST analyzer into kg-code-integration for enhanced code parsing ([ef47894](https://github.com/tosin2013/documcp/commit/ef478940bf50616c672201a9c7720a7054eb0456))
### 📚 Documentation
- comprehensive documentation improvements and pipeline fixes ([be64fd2](https://github.com/tosin2013/documcp/commit/be64fd2ffbe277b87afd9bd214ee1c4d4d54b224))
### [0.4.1](https://github.com/tosin2013/documcp/compare/v0.4.0...v0.4.1) (2025-10-04)
### 📚 Documentation
- add release workflow fixes documentation ([44b8bc9](https://github.com/tosin2013/documcp/commit/44b8bc96adcedaeff70f5bdea0a8b0c7a49f1e52))
### 🚀 Features
- add Playwright testing integration and knowledge graph enhancements ([39dc058](https://github.com/tosin2013/documcp/commit/39dc058842dfcf2195ac71d0edc7d1f14077cb84))
### 🐛 Bug Fixes
- add cleanup step to CI workflow to prevent corrupted memory files ([b07692d](https://github.com/tosin2013/documcp/commit/b07692d41b6c57e4e0b8d50c84f886bd77f86adf))
- correct workflow to build Docusaurus instead of Jekyll ([246302c](https://github.com/tosin2013/documcp/commit/246302c16b779d4caa38980496d2d211e8a1f2cd))
- improve GitHub Actions release workflow for future releases ([f83e930](https://github.com/tosin2013/documcp/commit/f83e9308db3b7be4afc4d0afebb0d782269b7df8))
- remove problematic setup-playwright-tests test file ([adb20f2](https://github.com/tosin2013/documcp/commit/adb20f2f09dd58143d12767ba9da92a686ca4237)), closes [#18245578186](https://github.com/tosin2013/documcp/issues/18245578186)
- update deprecated GitHub Actions in deploy-docs workflow ([dc877d7](https://github.com/tosin2013/documcp/commit/dc877d748429ec1ccbe9873449f56315b3bae14b)), closes [#18246024667](https://github.com/tosin2013/documcp/issues/18246024667)
## [0.4.0](https://github.com/tosin2013/documcp/compare/v0.3.4...v0.4.0) (2025-10-02)
### 🚀 Features
- Complete API documentation and user onboarding system ([7e7944e](https://github.com/tosin2013/documcp/commit/7e7944e65d576d2531c627560288d61ae88717d1))
- implement Phase 2 Intelligence & Learning System ([26b3370](https://github.com/tosin2013/documcp/commit/26b3370e64796a6f02534b6e6a9170043edc0a0a))
- integrate Release Drafter for automated release notes ([d06d88a](https://github.com/tosin2013/documcp/commit/d06d88a116ee56f42c3a8bcd8adc58220fce9b95))
### 🐛 Bug Fixes
- adjust coverage threshold for kg-storage error handling ([0b3e121](https://github.com/tosin2013/documcp/commit/0b3e1210e778aa2b7a9b05e7d64403076da8eaaa))
- resolve GitHub Actions workflow build failures ([0baddff](https://github.com/tosin2013/documcp/commit/0baddff738519ca598555af4c32ab582394d98b0))
- resolve Phase 2.1 edge case test failures with nuanced logic ([52d1f32](https://github.com/tosin2013/documcp/commit/52d1f32c4c4f26eba9b5b0894cc61b7637349f26))
- resolve Phase 2.1 test failures with comprehensive fixes ([736f104](https://github.com/tosin2013/documcp/commit/736f1049c1ac1b9deb61d63d7f69ff970e4ccb49))
- resolve pre-commit shellcheck and prettier issues ([54b90bf](https://github.com/tosin2013/documcp/commit/54b90bf9753ae018ed7983c6e703415f35f71156))
- update Docusaurus sidebar configuration with correct document IDs ([b9dcd99](https://github.com/tosin2013/documcp/commit/b9dcd99fea130a69cfef205b20fa6ca0ee9b143a))
- update GitHub Actions to latest versions to resolve deprecated artifact actions ([fabbbf3](https://github.com/tosin2013/documcp/commit/fabbbf3a22fcc07b546f383c5ff67570adeab9e3))
- update GitHub Actions to use latest artifact actions ([37bdda0](https://github.com/tosin2013/documcp/commit/37bdda01ef3cdb29ab805cfcecb98067846bfa52))
- update GitHub Pages deployment from Jekyll to Docusaurus ([09d7133](https://github.com/tosin2013/documcp/commit/09d7133580c228b5cb0615228e3e7946b6e6889d))
- update GitHub Pages deployment workflow for Docusaurus ([7623671](https://github.com/tosin2013/documcp/commit/7623671d72f7fbc088dba9923f45eb01265278a1))
### [0.3.4](https://github.com/tosin2013/documcp/compare/v0.3.3...v0.3.4) (2025-09-18)
### 🐛 Bug Fixes
- exclude remaining experimental memory files from coverage ([6c436b0](https://github.com/tosin2013/documcp/commit/6c436b018d0e072f25058617fe728b39279b51fc))
### 🚀 Features
- achieve 90%+ coverage by focusing on core functionality ([561b8a5](https://github.com/tosin2013/documcp/commit/561b8a56a14ddc39387fce35a1efd2ad0c2983bc))
### [0.3.3](https://github.com/tosin2013/documcp/compare/v0.3.2...v0.3.3) (2025-09-18)
### 🚀 Features
- achieve 85%+ test coverage with comprehensive test suite ([d607514](https://github.com/tosin2013/documcp/commit/d60751449d9fdc431f4c25d1465ab8731c31d3d9))
- add comprehensive pre-commit hooks configuration ([46e71ee](https://github.com/tosin2013/documcp/commit/46e71eec6f26c8e8b560480ec75e7f8c300ec9ae))
- comprehensive documentation updates with memory-enhanced capabilities ([9b13be9](https://github.com/tosin2013/documcp/commit/9b13be938b11cafee151a071b7406d5d6fb32366))
- configure project-local storage with startup visibility ([dfe60f0](https://github.com/tosin2013/documcp/commit/dfe60f0afa4073d4e1b05a9cc569a7ad203a3716))
- implement complete MCP prompts and resources system (ADR-007) ([1c9b5c2](https://github.com/tosin2013/documcp/commit/1c9b5c2cdaf41b793ae0c956f5de59f102cf35de))
- implement comprehensive memory system with advanced AI capabilities ([e4c9d06](https://github.com/tosin2013/documcp/commit/e4c9d0608037bc6f2ff239cd2107c77972c4eaa9)), closes [#45-54](https://github.com/tosin2013/documcp/issues/45-54) [#45-46](https://github.com/tosin2013/documcp/issues/45-46) [#47-48](https://github.com/tosin2013/documcp/issues/47-48) [#49-50](https://github.com/tosin2013/documcp/issues/49-50) [#51-52](https://github.com/tosin2013/documcp/issues/51-52) [#53-54](https://github.com/tosin2013/documcp/issues/53-54)
- implement Docusaurus documentation deployment with GitHub Actions ([7b78e7b](https://github.com/tosin2013/documcp/commit/7b78e7b80deb9fb8f074c0209bd1c88e539cb329))
- implement missing memory tool handlers for DocuMCP ([576bab5](https://github.com/tosin2013/documcp/commit/576bab50545b9eb57b8c2a74e50b0c555bcb3c80))
### ♻️ Code Refactoring
- simplify documentation deployment and add GitHub Actions linting ([6996c55](https://github.com/tosin2013/documcp/commit/6996c553d35a1c7cbd473c6150a8994e00a0526c))
### 🐛 Bug Fixes
- correct Dockerfile syntax for heredocs ([7d3556d](https://github.com/tosin2013/documcp/commit/7d3556d783b9f4bb251d8c47ac8f3aed441b1764))
- deploy Docusaurus documentation instead of Jekyll ([48400ae](https://github.com/tosin2013/documcp/commit/48400ae40f2a77bb9c8e446a9db3deb726a1e252))
- **generate-config:** correct Docusaurus file paths for test compatibility ([72522b4](https://github.com/tosin2013/documcp/commit/72522b4dab07ab9f96454a32c81599119b09cfe3))
- improve error handling test for cross-environment compatibility ([676e1da](https://github.com/tosin2013/documcp/commit/676e1dafdd2cc87a267591d5c244252efdf10222))
- remove invalid exclude field from Docusaurus config ([ebac637](https://github.com/tosin2013/documcp/commit/ebac6376dfe15ef76f688e42a86c9b4e01391316))
- resolve analysis ID retrieval issues across DocuMCP tools ([37610d0](https://github.com/tosin2013/documcp/commit/37610d0c79b1e8d97dad3a87746a7533a1f27740))
- resolve analysis ID retrieval issues across DocuMCP tools ([1f141d4](https://github.com/tosin2013/documcp/commit/1f141d4de0fa97fecee27a401d7870e13b42a630))
- resolve critical memory system failures and improve functionality ([9d009dc](https://github.com/tosin2013/documcp/commit/9d009dcf8cfaa721d6163546bc919bc318e8a1ee))
- resolve ESLint errors in memory system implementation ([a500719](https://github.com/tosin2013/documcp/commit/a50071901f7ec05b4ae2fa464ec1d38feb8f670d))
- resolve ESLint unused variable errors while preserving memory functionality ([3412abe](https://github.com/tosin2013/documcp/commit/3412abe08c44766660388d9fab438a2221544eb5))
- resolve memory system import/export issues and performance bottlenecks ([7164e3d](https://github.com/tosin2013/documcp/commit/7164e3dbe00cac5d1e82d9bea79ae6ced71e2ce5))
- resolve remaining TypeScript compilation errors ([3674069](https://github.com/tosin2013/documcp/commit/3674069cf722f5bc4060af999ad3a2f1480301a2))
- resolve test failures and improve test reliability to 99.3% ([56f9bc8](https://github.com/tosin2013/documcp/commit/56f9bc842a19f7841b8a5b508daf5c8f58c0ec76))
- resolve test failures and restore MCP server functionality ([0755bd3](https://github.com/tosin2013/documcp/commit/0755bd3f4398d172ee42e571971755d8a2779412))
- resolve TypeScript build errors and add package.json protection ([315a601](https://github.com/tosin2013/documcp/commit/315a601d5375142c2f4dc15d271c1088c8a8608c))
- resolve TypeScript compilation errors from ESLint fixes ([0f628f7](https://github.com/tosin2013/documcp/commit/0f628f7f8e3788a658432dd4983be8b063ccdd08))
- resolve TypeScript compilation errors in memory system tests ([47d9afe](https://github.com/tosin2013/documcp/commit/47d9afe238f3e0723c813b6d4ef640c22c3e1659))
- resolve TypeScript compilation errors in memory system tests ([3003d0f](https://github.com/tosin2013/documcp/commit/3003d0f608f7b70d35dbddf14500fa5d91774e91))
- restore Docusaurus workflow and remove npm cache dependency ([bb2bc85](https://github.com/tosin2013/documcp/commit/bb2bc8518a00ea4a4807f7de1956956bcb4af74e))
- update GitHub Actions workflow to use Docker-based Docusaurus build ([65deb79](https://github.com/tosin2013/documcp/commit/65deb79f7e8caf8accd99183e7f91ec89b94261f))
- use Docker-based build for GitHub Actions documentation deployment ([1777268](https://github.com/tosin2013/documcp/commit/1777268ca82d8471480e6c17d4e1fb80fc45dcd4))
- use npm install instead of npm ci for dynamic package.json ([d4c9d5b](https://github.com/tosin2013/documcp/commit/d4c9d5b584b26868203badbf7e01d90cac04f02c))
### [0.3.2](https://github.com/tosin2013/documcp/compare/v0.3.1...v0.3.2) (2025-09-11)
### 🐛 Bug Fixes
- resolve documentation inconsistencies and improve type safety ([eeb5dde](https://github.com/tosin2013/documcp/commit/eeb5dde09885fdf94dd5fd91a31e7aa6dc157084))
- update deploy-docs workflow from Jekyll to Docusaurus ([26b4a30](https://github.com/tosin2013/documcp/commit/26b4a307dc2c558d15008e0f2624645b7d4b1a08))
- update deprecated GitHub Actions to latest versions ([e219410](https://github.com/tosin2013/documcp/commit/e2194109170fbb6d56513bbe8b4d93e950b98da9))
### [0.3.1](https://github.com/tosin2013/documcp/compare/v0.3.0...v0.3.1) (2025-09-11)
## [0.3.0](https://github.com/tosin2013/documcp/compare/v0.2.4...v0.3.0) (2025-09-11)
### 🐛 Bug Fixes
- add error handling for package.json parsing in project context analyzer ([0a5a3e6](https://github.com/tosin2013/documcp/commit/0a5a3e6d2802397d83bf87483a083b51fe3a1a8c))
- disable strict ESLint rules to resolve CI pipeline failures on main branch ([5a1dda4](https://github.com/tosin2013/documcp/commit/5a1dda4870472e074733b597ab3f0325a8c65d1d))
- regenerate package-lock.json to resolve CodeQL workflow failure ([613e6c0](https://github.com/tosin2013/documcp/commit/613e6c0f4319ee244e5037f1036b86085e97201a)), closes [#25](https://github.com/tosin2013/documcp/issues/25)
- resolve all failing test cases in optimize-readme.test.ts ([7353338](https://github.com/tosin2013/documcp/commit/7353338b33a5a98f6f0f87bbc090f068d38430fb))
- Resolve ESLint errors in generate-technical-writer-prompts.ts ([5a176f6](https://github.com/tosin2013/documcp/commit/5a176f672e1556450383a03c4d0f0475ca92e25d))
- resolve ESLint errors in README Technical Writer tools ([68810b0](https://github.com/tosin2013/documcp/commit/68810b0ceba74f541968f51ac6bc3ec6b8524cad))
- Resolve ESLint errors in validate-readme-checklist.ts ([0b3beab](https://github.com/tosin2013/documcp/commit/0b3beab437802b8c1393759b96ffd907683923b2))
- Update index.ts to use new Diataxis-aligned prompt API ([28dc2c0](https://github.com/tosin2013/documcp/commit/28dc2c0e727aa90219ae32f2b2036c2f9b206b3e))
### 🚀 Features
- Achieve 85%+ branch coverage for critical DocuMCP tools ([0111a1b](https://github.com/tosin2013/documcp/commit/0111a1b3aae09a27ab9db236ec1acfbe636d3361))
- add comprehensive technical writer prompts system ([7509f91](https://github.com/tosin2013/documcp/commit/7509f91de043237a528864f4b11cb485b0b2c03a))
- add Dependabot config for Docusaurus documentation dependencies and security updates ([16fbee7](https://github.com/tosin2013/documcp/commit/16fbee7fad535e4b4cc4960a88daf3062add19ba))
- Add README template generator and checklist validator tools ([4899e12](https://github.com/tosin2013/documcp/commit/4899e1217cd1fe60246f23c4d43731cc6ecbb0e6)), closes [#11](https://github.com/tosin2013/documcp/issues/11)
- Implement Diataxis-aligned technical writer prompts ([f32558a](https://github.com/tosin2013/documcp/commit/f32558a031a571579fb02da64f3e1e3bf8518664))
- implement README Technical Writer MCP tools ([728da0a](https://github.com/tosin2013/documcp/commit/728da0a21ec586b5f8361337edf42fec79dc70d0)), closes [#10](https://github.com/tosin2013/documcp/issues/10)
- improve validate-content.ts test coverage from 79.31% to 83.15% ([a51c0a7](https://github.com/tosin2013/documcp/commit/a51c0a7f1e7232db99d444fbe94ea7a74ec04ece)), closes [#7](https://github.com/tosin2013/documcp/issues/7)
- integrate main branch updates and fix merge conflicts ([6d30ddf](https://github.com/tosin2013/documcp/commit/6d30ddf63ccca01f67b90ecfef2fb438a16a369e))
## [0.2.3] - 2025-08-24
### Fixed
- Added missing `bin` field to package.json to enable npx execution (Fixes #3)
- Made dist/index.js executable with proper permissions
### Added
- CLI executable support via `npx documcp` command
- Direct command-line invocation capability
## [0.2.2] - 2025-08-24
### Added
- Version display and badges to documentation pages
- Enhanced documentation structure
## [0.2.1] - 2025-08-24
### Changed
- Minor documentation updates
## [0.2.0] - 2025-08-24
### Changed
- **BREAKING**: Updated Node.js requirements from >=18.0.0 to >=20.0.0
- Updated CI/CD pipeline to test Node.js 20.x and 22.x (removed 18.x)
- Updated all generated GitHub Actions workflows to use Node.js 20
- Updated Docker base images from node:18 to node:20 in test fixtures
- Updated @types/node references from ^18.0.0 to ^20.0.0
- Enhanced content validation capabilities and improved documentation structure
- Improved Docusaurus deployment build path configuration
- Refactored local deployment tests with better input validation and response structure
### Added
- Created .nvmrc file specifying Node.js 22 for development
- Added Node.js version requirements to README.md
- Comprehensive test suite additions with improved coverage
- Enhanced error handling and response structure improvements
- Content generation methods with consistent parameter naming
### Fixed
- Critical linting errors resolved (lexical declarations in case blocks)
- Unused variable cleanup in validation tools
- Correct build path for Docusaurus deployment
### Technical Details
- CI/CD pipeline now tests compatibility with Node.js 20.x and 22.x
- All generated deployment workflows use Node.js 20 by default
- Test coverage maintained at 82.76% (exceeds 80% requirement)
- All builds and tests pass with updated Node.js requirements
- 161 tests passing across 13 test suites
- Enhanced documentation gap detection and content validation
## [0.1.0] - 2025-08-22
### Added
- Initial release of DocuMCP
- Complete MCP Server implementation with 6 core tools
- Comprehensive CI/CD pipeline with GitHub Actions
- 82% test coverage (exceeds 80% requirement)
- Performance benchmarking system (PERF-001 compliant)
- Security scanning and dependency review
- Automated release and deployment workflows
```
--------------------------------------------------------------------------------
/tests/performance/memory-stress-testing.test.ts:
--------------------------------------------------------------------------------
```typescript
/**
* Memory System Stress Testing
* Tests memory system under extreme conditions and edge cases
* Part of Issue #57 - Memory System Performance and Load Testing
*/
import { promises as fs } from "fs";
import path from "path";
import os from "os";
import { performance } from "perf_hooks";
import { MemoryManager } from "../../src/memory/manager.js";
import { JSONLStorage } from "../../src/memory/storage.js";
describe("Memory System Stress Testing", () => {
let tempDir: string;
let memoryManager: MemoryManager;
beforeEach(async () => {
tempDir = path.join(
os.tmpdir(),
`memory-stress-test-${Date.now()}-${Math.random()
.toString(36)
.substr(2, 9)}`,
);
await fs.mkdir(tempDir, { recursive: true });
memoryManager = new MemoryManager(tempDir);
await memoryManager.initialize();
});
afterEach(async () => {
try {
await fs.rm(tempDir, { recursive: true, force: true });
} catch (error) {
// Ignore cleanup errors
}
});
describe("High Volume Stress Tests", () => {
test("should handle extremely large datasets", async () => {
memoryManager.setContext({ projectId: "extreme-volume-test" });
const largeDatasetSize = 10000; // 10K memories
const batchSize = 1000;
const startTime = performance.now();
console.log(
`Starting extreme volume test with ${largeDatasetSize} memories...`,
);
let processedCount = 0;
for (let batch = 0; batch < largeDatasetSize / batchSize; batch++) {
const batchData = Array.from({ length: batchSize }, (_, i) => ({
projectId: "extreme-volume-test",
batch,
index: i,
globalIndex: processedCount + i,
data: `stress-test-data-${processedCount + i}`,
timestamp: new Date().toISOString(),
metadata: {
batch,
processingOrder: processedCount + i,
complexity: i % 5,
},
}));
// Process batch
const batchPromises = batchData.map((data) =>
memoryManager.remember("analysis", data),
);
await Promise.all(batchPromises);
processedCount += batchSize;
// Progress update
if (batch % 2 === 0) {
const elapsed = performance.now() - startTime;
const rate = processedCount / (elapsed / 1000);
console.log(
`Processed ${processedCount}/${largeDatasetSize} memories (${rate.toFixed(
0,
)} memories/sec)`,
);
}
// Verify memory usage doesn't spiral out of control
const memUsage = process.memoryUsage();
expect(memUsage.heapUsed).toBeLessThan(500 * 1024 * 1024); // Less than 500MB
}
const endTime = performance.now();
const totalTime = endTime - startTime;
const averageRate = largeDatasetSize / (totalTime / 1000);
console.log(
`Completed ${largeDatasetSize} memories in ${(totalTime / 1000).toFixed(
2,
)}s (${averageRate.toFixed(0)} memories/sec)`,
);
// Verify all memories were stored
const allMemories = await memoryManager.search({
projectId: "extreme-volume-test",
});
expect(allMemories.length).toBe(largeDatasetSize);
// Performance expectations
expect(totalTime).toBeLessThan(300000); // Should complete within 5 minutes
expect(averageRate).toBeGreaterThan(30); // At least 30 memories per second
}, 360000); // 6 minute timeout
test("should handle rapid burst operations", async () => {
memoryManager.setContext({ projectId: "burst-test" });
const burstSize = 1000;
const burstCount = 5;
const burstResults: number[] = [];
console.log(
`Testing ${burstCount} bursts of ${burstSize} operations each...`,
);
for (let burst = 0; burst < burstCount; burst++) {
const burstStartTime = performance.now();
// Create burst data
const burstData = Array.from({ length: burstSize }, (_, i) => ({
projectId: "burst-test",
burst,
index: i,
data: `burst-${burst}-item-${i}`,
timestamp: new Date().toISOString(),
}));
// Execute burst
const burstPromises = burstData.map((data) =>
memoryManager.remember("analysis", data),
);
await Promise.all(burstPromises);
const burstEndTime = performance.now();
const burstTime = burstEndTime - burstStartTime;
burstResults.push(burstTime);
console.log(
`Burst ${burst + 1}: ${burstTime.toFixed(2)}ms (${(
burstSize /
(burstTime / 1000)
).toFixed(0)} ops/sec)`,
);
// Small delay between bursts
await new Promise((resolve) => setTimeout(resolve, 100));
}
// Analyze burst performance consistency
const avgBurstTime =
burstResults.reduce((sum, time) => sum + time, 0) / burstResults.length;
const maxBurstTime = Math.max(...burstResults);
const minBurstTime = Math.min(...burstResults);
const performanceVariation = (maxBurstTime - minBurstTime) / avgBurstTime;
console.log(
`Burst analysis: Avg ${avgBurstTime.toFixed(
2,
)}ms, Min ${minBurstTime.toFixed(2)}ms, Max ${maxBurstTime.toFixed(
2,
)}ms`,
);
console.log(
`Performance variation: ${(performanceVariation * 100).toFixed(1)}%`,
);
// Performance should be consistent across bursts
expect(avgBurstTime).toBeLessThan(10000); // Average burst < 10 seconds
expect(performanceVariation).toBeLessThan(3); // Less than 300% variation
});
});
describe("Resource Exhaustion Tests", () => {
test("should handle memory pressure gracefully", async () => {
memoryManager.setContext({ projectId: "memory-pressure-test" });
const largeItemSize = 1024 * 1024; // 1MB per item
const maxItems = 100; // 100MB of data
const memorySnapshots: Array<{
count: number;
heapUsed: number;
time: number;
}> = [];
console.log("Testing memory pressure handling...");
const startMemory = process.memoryUsage();
const startTime = performance.now();
for (let i = 0; i < maxItems; i++) {
const largeData = {
projectId: "memory-pressure-test",
index: i,
payload: "x".repeat(largeItemSize),
timestamp: new Date().toISOString(),
};
await memoryManager.remember("analysis", largeData);
if (i % 10 === 0) {
const currentMemory = process.memoryUsage();
memorySnapshots.push({
count: i + 1,
heapUsed: currentMemory.heapUsed - startMemory.heapUsed,
time: performance.now() - startTime,
});
// Force garbage collection if available
if (global.gc) {
global.gc();
}
}
// Check for memory leaks - memory shouldn't grow unbounded
const currentMemory = process.memoryUsage();
const memoryUsed = currentMemory.heapUsed - startMemory.heapUsed;
// Allow for reasonable memory growth but prevent runaway usage
const expectedMaxMemory = (i + 1) * largeItemSize * 2; // 2x overhead allowance
expect(memoryUsed).toBeLessThan(
Math.max(expectedMaxMemory, 200 * 1024 * 1024),
); // Max 200MB
}
const finalSnapshot = memorySnapshots[memorySnapshots.length - 1];
console.log(
`Memory pressure test: ${finalSnapshot.count} items, ${(
finalSnapshot.heapUsed /
1024 /
1024
).toFixed(2)}MB used`,
);
// Verify data integrity under pressure
const allMemories = await memoryManager.search({
projectId: "memory-pressure-test",
});
expect(allMemories.length).toBe(maxItems);
});
test("should handle storage device pressure", async () => {
memoryManager.setContext({ projectId: "storage-pressure-test" });
// Create many small files to stress the storage system
const fileCount = 1000;
const operationResults: boolean[] = [];
console.log(`Testing storage pressure with ${fileCount} operations...`);
for (let i = 0; i < fileCount; i++) {
try {
const data = {
projectId: "storage-pressure-test",
index: i,
data: `storage-pressure-item-${i}`,
timestamp: new Date().toISOString(),
};
await memoryManager.remember("analysis", data);
operationResults.push(true);
if (i % 100 === 0) {
console.log(`Storage operations completed: ${i + 1}/${fileCount}`);
}
} catch (error) {
operationResults.push(false);
console.error(`Storage operation ${i} failed:`, error);
}
}
const successRate =
operationResults.filter((result) => result).length /
operationResults.length;
console.log(
`Storage pressure test: ${(successRate * 100).toFixed(
1,
)}% success rate`,
);
// Should handle most operations successfully
expect(successRate).toBeGreaterThan(0.95); // At least 95% success rate
// Verify storage integrity
const storedMemories = await memoryManager.search({
projectId: "storage-pressure-test",
});
expect(storedMemories.length).toBeGreaterThan(fileCount * 0.9); // At least 90% stored
});
});
describe("Edge Case Stress Tests", () => {
test("should handle extremely large individual memories", async () => {
memoryManager.setContext({ projectId: "large-individual-test" });
const extremeSizes = [
{ name: "huge", size: 5 * 1024 * 1024 }, // 5MB
{ name: "massive", size: 10 * 1024 * 1024 }, // 10MB
{ name: "gigantic", size: 25 * 1024 * 1024 }, // 25MB
];
for (const testSize of extremeSizes) {
console.log(
`Testing ${testSize.name} memory (${(
testSize.size /
1024 /
1024
).toFixed(1)}MB)...`,
);
const startTime = performance.now();
const largeData = {
projectId: "large-individual-test",
size: testSize.name,
payload: "x".repeat(testSize.size),
metadata: { originalSize: testSize.size },
};
try {
const memory = await memoryManager.remember("analysis", largeData);
const createTime = performance.now() - startTime;
// Verify storage
const readStartTime = performance.now();
const retrieved = await memoryManager.recall(memory.id);
const readTime = performance.now() - readStartTime;
expect(retrieved).not.toBeNull();
expect(retrieved?.data.payload.length).toBe(testSize.size);
console.log(
`${testSize.name}: Create ${createTime.toFixed(
2,
)}ms, Read ${readTime.toFixed(2)}ms`,
);
// Performance should be reasonable even for large items
expect(createTime).toBeLessThan(30000); // 30 seconds max
expect(readTime).toBeLessThan(10000); // 10 seconds max
} catch (error) {
console.error(`Failed to handle ${testSize.name} memory:`, error);
throw error;
}
}
});
test("should handle deeply nested data structures", async () => {
memoryManager.setContext({ projectId: "nested-structure-test" });
// Create deeply nested object
const createNestedObject = (depth: number): any => {
if (depth === 0) {
return { value: "leaf-node", depth: 0 };
}
return {
level: depth,
children: Array.from({ length: 3 }, (_, i) => ({
id: `child-${depth}-${i}`,
data: createNestedObject(depth - 1),
metadata: { parent: depth, index: i },
})),
metadata: { depth, totalChildren: 3 },
};
};
const testDepths = [10, 15, 20];
for (const depth of testDepths) {
console.log(`Testing nested structure depth ${depth}...`);
const startTime = performance.now();
const nestedData = {
projectId: "nested-structure-test",
depth,
structure: createNestedObject(depth),
metadata: { maxDepth: depth, type: "stress-test" },
};
try {
const memory = await memoryManager.remember("analysis", nestedData);
const createTime = performance.now() - startTime;
// Verify retrieval
const readStartTime = performance.now();
const retrieved = await memoryManager.recall(memory.id);
const readTime = performance.now() - readStartTime;
expect(retrieved).not.toBeNull();
expect(retrieved?.data.depth).toBe(depth);
expect(retrieved?.data.structure.level).toBe(depth);
console.log(
`Depth ${depth}: Create ${createTime.toFixed(
2,
)}ms, Read ${readTime.toFixed(2)}ms`,
);
// Should handle complex structures efficiently
expect(createTime).toBeLessThan(5000); // 5 seconds max
expect(readTime).toBeLessThan(2000); // 2 seconds max
} catch (error) {
console.error(
`Failed to handle nested structure depth ${depth}:`,
error,
);
throw error;
}
}
});
test("should handle rapid context switching", async () => {
const contextCount = 100;
const operationsPerContext = 10;
const totalOperations = contextCount * operationsPerContext;
console.log(
`Testing rapid context switching: ${contextCount} contexts, ${operationsPerContext} ops each...`,
);
const startTime = performance.now();
const results: Array<{ context: string; operationTime: number }> = [];
for (let context = 0; context < contextCount; context++) {
const contextId = `rapid-context-${context}`;
const contextStartTime = performance.now();
memoryManager.setContext({ projectId: contextId });
// Perform operations in this context
const contextPromises = Array.from(
{ length: operationsPerContext },
async (_, i) => {
return await memoryManager.remember("analysis", {
projectId: contextId,
contextIndex: context,
operationIndex: i,
data: `context-${context}-operation-${i}`,
timestamp: new Date().toISOString(),
});
},
);
await Promise.all(contextPromises);
const contextTime = performance.now() - contextStartTime;
results.push({ context: contextId, operationTime: contextTime });
if (context % 20 === 0) {
console.log(`Completed context ${context}/${contextCount}`);
}
}
const totalTime = performance.now() - startTime;
const avgContextTime =
results.reduce((sum, r) => sum + r.operationTime, 0) / results.length;
const totalRate = totalOperations / (totalTime / 1000);
console.log(
`Context switching test: ${(totalTime / 1000).toFixed(
2,
)}s total, ${avgContextTime.toFixed(2)}ms avg per context`,
);
console.log(`Overall rate: ${totalRate.toFixed(0)} operations/sec`);
// Verify all operations completed
const allMemories = await memoryManager.search("");
expect(allMemories.length).toBeGreaterThanOrEqual(totalOperations * 0.95); // Allow for 5% loss
// Performance should remain reasonable
expect(totalTime).toBeLessThan(60000); // Complete within 1 minute
expect(totalRate).toBeGreaterThan(50); // At least 50 ops/sec overall
});
});
describe("Failure Recovery Stress Tests", () => {
test("should recover from simulated storage failures", async () => {
memoryManager.setContext({ projectId: "storage-failure-test" });
// Create initial data
const initialMemories = [];
for (let i = 0; i < 100; i++) {
const memory = await memoryManager.remember("analysis", {
projectId: "storage-failure-test",
index: i,
data: `initial-data-${i}`,
phase: "before-failure",
});
initialMemories.push(memory);
}
// Simulate storage failure recovery by creating new manager instance
const recoveryManager = new MemoryManager(tempDir);
await recoveryManager.initialize();
// Verify recovery
const recoveredMemories = await recoveryManager.search({
projectId: "storage-failure-test",
});
expect(recoveredMemories.length).toBe(100);
// Continue operations after recovery
recoveryManager.setContext({ projectId: "storage-failure-test" });
for (let i = 0; i < 50; i++) {
await recoveryManager.remember("analysis", {
projectId: "storage-failure-test",
index: 100 + i,
data: `post-recovery-data-${i}`,
phase: "after-recovery",
});
}
// Verify total state
const finalMemories = await recoveryManager.search({
projectId: "storage-failure-test",
});
expect(finalMemories.length).toBe(150);
const beforeFailure = finalMemories.filter(
(m) => m.data.phase === "before-failure",
);
const afterRecovery = finalMemories.filter(
(m) => m.data.phase === "after-recovery",
);
expect(beforeFailure.length).toBe(100);
expect(afterRecovery.length).toBe(50);
console.log("Storage failure recovery test completed successfully");
});
test("should handle concurrent access corruption scenarios", async () => {
memoryManager.setContext({ projectId: "corruption-test" });
const concurrentWorkers = 5;
const operationsPerWorker = 100;
const conflictData = Array.from(
{ length: concurrentWorkers },
(_, workerId) =>
Array.from({ length: operationsPerWorker }, (_, opId) => ({
projectId: "corruption-test",
workerId,
operationId: opId,
data: `worker-${workerId}-operation-${opId}`,
timestamp: new Date().toISOString(),
})),
);
console.log(
`Testing concurrent access with ${concurrentWorkers} workers, ${operationsPerWorker} ops each...`,
);
// Execute concurrent operations that might cause conflicts
const workerPromises = conflictData.map(async (workerData, workerId) => {
const results = [];
for (const data of workerData) {
try {
const memory = await memoryManager.remember("analysis", data);
results.push({ success: true, id: memory.id });
} catch (error) {
results.push({ success: false, error: (error as Error).message });
}
}
return { workerId, results };
});
const workerResults = await Promise.all(workerPromises);
// Analyze results
let totalOperations = 0;
let successfulOperations = 0;
workerResults.forEach(({ workerId, results }) => {
const successful = results.filter((r) => r.success).length;
totalOperations += results.length;
successfulOperations += successful;
console.log(
`Worker ${workerId}: ${successful}/${results.length} operations successful`,
);
});
const successRate = successfulOperations / totalOperations;
console.log(
`Overall concurrent access success rate: ${(successRate * 100).toFixed(
1,
)}%`,
);
// Should handle most concurrent operations successfully
expect(successRate).toBeGreaterThan(0.9); // At least 90% success rate
// Verify data integrity
const allMemories = await memoryManager.search({
projectId: "corruption-test",
});
expect(allMemories.length).toBeGreaterThanOrEqual(totalOperations * 0.85); // Allow for some conflicts
});
});
});
```