This is page 5 of 20. Use http://codebase.md/tosin2013/documcp?lines=false&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
--------------------------------------------------------------------------------
/docs/reference/configuration.md:
--------------------------------------------------------------------------------
```markdown
---
documcp:
last_updated: "2025-11-20T00:46:21.960Z"
last_validated: "2025-11-20T00:46:21.960Z"
auto_updated: false
update_frequency: monthly
---
# Configuration Options
This reference guide covers all configuration options available in DocuMCP and the static site generators it supports.
## DocuMCP Configuration
### Environment Variables
DocuMCP supports the following environment variables:
| Variable | Default | Description |
| --------------------- | ----------------- | ----------------------------------- |
| `DOCUMCP_STORAGE_DIR` | `.documcp/memory` | Directory for memory system storage |
| `DEBUG` | `false` | Enable debug logging |
| `NODE_ENV` | `development` | Node.js environment |
### Memory System Configuration
The memory system stores analysis results and learning patterns:
```bash
# Default storage location (relative to project)
.documcp/memory/
├── analysis/ # Repository analysis results
├── recommendations/ # SSG recommendations
├── patterns/ # Learning patterns
└── metadata.json # System metadata
```
#### Memory Cleanup Options
```javascript
// Cleanup configuration
{
"daysToKeep": 30, // Days to retain memories
"maxEntries": 1000, // Maximum memory entries
"compressionEnabled": true
}
```
## Static Site Generator Configurations
### Jekyll Configuration
**\_config.yml:**
```yaml
title: "Your Documentation Site"
description: "Project documentation"
baseurl: "/repository-name"
url: "https://username.github.io"
markdown: kramdown
highlighter: rouge
theme: minima
plugins:
- jekyll-feed
- jekyll-sitemap
- jekyll-seo-tag
collections:
tutorials:
output: true
permalink: /:collection/:name/
how-to-guides:
output: true
permalink: /:collection/:name/
defaults:
- scope:
path: ""
values:
layout: "default"
- scope:
path: "_tutorials"
values:
layout: "tutorial"
```
**Gemfile:**
```ruby
source 'https://rubygems.org'
gem 'jekyll', '~> 4.3.0'
gem 'jekyll-feed', '~> 0.17'
gem 'jekyll-sitemap', '~> 1.4'
gem 'jekyll-seo-tag', '~> 2.8'
gem 'minima', '~> 2.5'
group :jekyll_plugins do
gem 'jekyll-timeago', '~> 0.13.1'
end
```
### Hugo Configuration
**config.yml:**
```yaml
baseURL: "https://username.github.io/repository-name"
languageCode: "en-us"
title: "Documentation Site"
theme: "docsy"
params:
github_repo: "https://github.com/username/repository"
github_branch: "main"
edit_page: true
search:
enabled: true
menu:
main:
- name: "Tutorials"
url: "/tutorials/"
weight: 10
- name: "How-to Guides"
url: "/how-to/"
weight: 20
- name: "Reference"
url: "/reference/"
weight: 30
- name: "Explanation"
url: "/explanation/"
weight: 40
markup:
goldmark:
renderer:
unsafe: true
highlight:
style: github
lineNos: true
codeFences: true
security:
funcs:
getenv:
- ^HUGO_
- ^CI$
```
**go.mod:**
```go
module github.com/username/repository
go 1.19
require (
github.com/google/docsy v0.6.0 // indirect
github.com/google/docsy/dependencies v0.6.0 // indirect
)
```
### Docusaurus Configuration
**docusaurus.config.js:**
For GitHub Pages deployment, ensure you configure `organizationName`, `projectName`, and `deploymentBranch`:
```javascript
const config = {
title: "Documentation Site",
tagline: "Comprehensive project documentation",
url: "https://yourusername.github.io", // Your GitHub Pages URL
baseUrl: "/repository-name/", // Repository name (or "/" for user/organization pages)
organizationName: "yourusername", // GitHub username or organization
projectName: "repository-name", // Repository name
deploymentBranch: "gh-pages", // Branch for deployment (default: gh-pages)
trailingSlash: false, // Set to true if using trailing slashes
onBrokenLinks: "throw",
onBrokenMarkdownLinks: "warn",
i18n: {
defaultLocale: "en",
locales: ["en"],
},
presets: [
[
"classic",
{
docs: {
routeBasePath: "/",
sidebarPath: require.resolve("./sidebars.js"),
editUrl: "https://github.com/username/repository/tree/main/",
},
theme: {
customCss: require.resolve("./src/css/custom.css"),
},
gtag: {
trackingID: "G-XXXXXXXXXX",
anonymizeIP: true,
},
},
],
],
themeConfig: {
navbar: {
title: "Documentation",
items: [
{
type: "doc",
docId: "tutorials/index",
position: "left",
label: "Tutorials",
},
{
type: "doc",
docId: "how-to/index",
position: "left",
label: "How-to",
},
{
type: "doc",
docId: "reference/index",
position: "left",
label: "Reference",
},
{
href: "https://github.com/username/repository",
label: "GitHub",
position: "right",
},
],
},
footer: {
style: "dark",
copyright: `Copyright © ${new Date().getFullYear()} Your Project Name.`,
},
prism: {
theme: require("prism-react-renderer/themes/github"),
darkTheme: require("prism-react-renderer/themes/dracula"),
},
},
};
module.exports = config;
```
**sidebars.js:**
```javascript
const sidebars = {
tutorialSidebar: [
"index",
{
type: "category",
label: "Tutorials",
items: [
"tutorials/getting-started",
"tutorials/first-deployment",
"tutorials/development-setup",
],
},
{
type: "category",
label: "How-to Guides",
items: [
"how-to/prompting-guide",
"how-to/repository-analysis",
"how-to/github-pages-deployment",
"how-to/troubleshooting",
],
},
{
type: "category",
label: "Reference",
items: [
"reference/mcp-tools",
"reference/configuration",
"reference/cli",
],
},
],
};
module.exports = sidebars;
```
### MkDocs Configuration
**mkdocs.yml:**
```yaml
site_name: Documentation Site
site_url: https://username.github.io/repository-name
site_description: Comprehensive project documentation
repo_name: username/repository
repo_url: https://github.com/username/repository
edit_uri: edit/main/docs/
theme:
name: material
palette:
- scheme: default
primary: blue
accent: blue
toggle:
icon: material/brightness-7
name: Switch to dark mode
- scheme: slate
primary: blue
accent: blue
toggle:
icon: material/brightness-4
name: Switch to light mode
features:
- navigation.tabs
- navigation.sections
- navigation.expand
- navigation.top
- search.highlight
- content.code.copy
nav:
- Home: index.md
- Tutorials:
- tutorials/index.md
- Getting Started: tutorials/getting-started.md
- First Deployment: tutorials/first-deployment.md
- How-to Guides:
- how-to/index.md
- Prompting Guide: how-to/prompting-guide.md
- Repository Analysis: how-to/repository-analysis.md
- Reference:
- reference/index.md
- MCP Tools: reference/mcp-tools.md
- Configuration: reference/configuration.md
- Explanation:
- explanation/index.md
- Architecture: explanation/architecture.md
plugins:
- search
- git-revision-date-localized:
enable_creation_date: true
markdown_extensions:
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.superfences
- admonition
- pymdownx.details
- pymdownx.tabbed:
alternate_style: true
- attr_list
- md_in_html
extra:
social:
- icon: fontawesome/brands/github
link: https://github.com/username/repository
```
**requirements.txt:**
```txt
mkdocs>=1.5.0
mkdocs-material>=9.0.0
mkdocs-git-revision-date-localized-plugin>=1.2.0
```
### Eleventy Configuration
**.eleventy.js:**
```javascript
const { EleventyHtmlBasePlugin } = require("@11ty/eleventy");
const markdownIt = require("markdown-it");
const markdownItAnchor = require("markdown-it-anchor");
module.exports = function (eleventyConfig) {
// Add plugins
eleventyConfig.addPlugin(EleventyHtmlBasePlugin);
// Configure Markdown
let markdownLibrary = markdownIt({
html: true,
breaks: true,
linkify: true,
}).use(markdownItAnchor, {
permalink: markdownItAnchor.permalink.ariaHidden({
placement: "after",
class: "direct-link",
symbol: "#",
}),
level: [1, 2, 3, 4],
slugify: eleventyConfig.getFilter("slug"),
});
eleventyConfig.setLibrary("md", markdownLibrary);
// Copy static files
eleventyConfig.addPassthroughCopy("src/assets");
eleventyConfig.addPassthroughCopy("src/css");
// Collections for Diataxis structure
eleventyConfig.addCollection("tutorials", function (collection) {
return collection.getFilteredByGlob("src/tutorials/*.md");
});
eleventyConfig.addCollection("howto", function (collection) {
return collection.getFilteredByGlob("src/how-to/*.md");
});
eleventyConfig.addCollection("reference", function (collection) {
return collection.getFilteredByGlob("src/reference/*.md");
});
eleventyConfig.addCollection("explanation", function (collection) {
return collection.getFilteredByGlob("src/explanation/*.md");
});
return {
dir: {
input: "src",
output: "_site",
includes: "_includes",
layouts: "_layouts",
data: "_data",
},
pathPrefix: "/repository-name/",
markdownTemplateEngine: "njk",
htmlTemplateEngine: "njk",
};
};
```
**package.json additions:**
```json
{
"scripts": {
"build": "eleventy",
"serve": "eleventy --serve",
"debug": "DEBUG=Eleventy* eleventy"
},
"devDependencies": {
"@11ty/eleventy": "^2.0.0",
"markdown-it": "^13.0.0",
"markdown-it-anchor": "^8.6.0"
}
}
```
## GitHub Actions Configuration
### Common Workflow Settings
All generated workflows include these optimizations:
```yaml
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
```
### Caching Configuration
Node.js dependencies:
```yaml
- name: Cache dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
```
Ruby dependencies (Jekyll):
```yaml
- name: Cache gems
uses: actions/cache@v4
with:
path: vendor/bundle
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gems-
```
## Performance Configuration
### Build Optimization
**Docusaurus:**
```javascript
const config = {
future: {
experimental_faster: true,
},
webpack: {
jsLoader: (isServer) => ({
loader: "esbuild-loader",
options: {
loader: "tsx",
target: isServer ? "node12" : "es2017",
},
}),
},
};
```
**Hugo:**
```yaml
build:
writeStats: true
noJSConfigInAssets: true
caches:
getjson:
maxAge: "1m"
getcsv:
maxAge: "1m"
```
### SEO Configuration
All SSGs include:
- Meta tags for social sharing
- Structured data markup
- XML sitemaps
- RSS feeds
- Canonical URLs
- Open Graph tags
## Security Configuration
### Content Security Policy
Generated sites include CSP headers:
```html
<meta
http-equiv="Content-Security-Policy"
content="
default-src 'self';
script-src 'self' 'unsafe-inline' https://www.googletagmanager.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://www.google-analytics.com;
"
/>
```
### HTTPS Enforcement
All deployments force HTTPS and include HSTS headers.
## Troubleshooting Configuration Issues
### Common Problems
**BaseURL Mismatch:**
```bash
# Check your configuration matches repository name
baseURL: "https://username.github.io/repository-name/" # Must match exactly
```
**Build Failures:**
```bash
# Verify Node.js version in workflows
node-version: '20' # Must match your local version
```
**Asset Loading Issues:**
```bash
# Ensure relative paths
<img src="./images/logo.png" /> # Good
<img src="/images/logo.png" /> # May fail
```
For more troubleshooting help, see the [Troubleshooting Guide](../how-to/troubleshooting.md).
```
--------------------------------------------------------------------------------
/tests/memory/enhanced-manager.test.ts:
--------------------------------------------------------------------------------
```typescript
/**
* Advanced unit tests for Enhanced Memory Manager
* Tests intelligent memory management with learning and knowledge graph integration
* Part of Issue #55 - Advanced Memory Components Unit Tests
*/
import { promises as fs } from "fs";
import path from "path";
import os from "os";
import {
EnhancedMemoryManager,
EnhancedRecommendation,
IntelligentAnalysis,
} from "../../src/memory/enhanced-manager.js";
import { ProjectFeatures } from "../../src/memory/learning.js";
describe("EnhancedMemoryManager", () => {
let tempDir: string;
let enhancedManager: EnhancedMemoryManager;
beforeEach(async () => {
// Create unique temp directory for each test
tempDir = path.join(
os.tmpdir(),
`enhanced-memory-test-${Date.now()}-${Math.random()
.toString(36)
.substr(2, 9)}`,
);
await fs.mkdir(tempDir, { recursive: true });
enhancedManager = new EnhancedMemoryManager(tempDir);
await enhancedManager.initialize();
});
afterEach(async () => {
// Cleanup temp directory
try {
await fs.rm(tempDir, { recursive: true, force: true });
} catch (error) {
// Ignore cleanup errors
}
});
describe("Enhanced Manager Initialization", () => {
test("should create enhanced manager instance", () => {
expect(enhancedManager).toBeDefined();
expect(enhancedManager).toBeInstanceOf(EnhancedMemoryManager);
});
test("should initialize all subsystems", async () => {
// Test that the enhanced manager properly initializes
// The initialize() method should complete without throwing
await enhancedManager.initialize();
expect(true).toBe(true);
});
test("should have learning and knowledge graph capabilities", async () => {
// Test that we can get learning statistics (indicating learning system exists)
const learningStats = await enhancedManager.getLearningStatistics();
expect(learningStats).toBeDefined();
expect(learningStats.learning).toBeDefined();
expect(learningStats.knowledgeGraph).toBeDefined();
});
});
describe("Enhanced Recommendations", () => {
test("should provide enhanced recommendations with multiple data sources", async () => {
// Set up test context
enhancedManager.setContext({ projectId: "enhanced-rec-test" });
// Add some historical data
await enhancedManager.remember("analysis", {
language: { primary: "typescript" },
framework: { name: "react" },
stats: { files: 150 },
});
await enhancedManager.remember("recommendation", {
recommended: "docusaurus",
confidence: 0.9,
});
await enhancedManager.remember("deployment", {
status: "success",
ssg: "docusaurus",
});
// Test enhanced recommendation
const projectFeatures: ProjectFeatures = {
language: "typescript",
framework: "react",
size: "medium",
complexity: "moderate",
hasTests: true,
hasCI: true,
hasDocs: false,
isOpenSource: true,
};
const baseRecommendation = {
recommended: "gatsby",
confidence: 0.7,
score: 0.75,
};
const enhanced = await enhancedManager.getEnhancedRecommendation(
"/test/project",
baseRecommendation,
projectFeatures,
);
expect(enhanced).toBeDefined();
expect(enhanced.baseRecommendation).toEqual(baseRecommendation);
expect(enhanced.learningEnhanced).toBeDefined();
expect(Array.isArray(enhanced.graphBased)).toBe(true);
expect(Array.isArray(enhanced.insights)).toBe(true);
expect(typeof enhanced.confidence).toBe("number");
expect(Array.isArray(enhanced.reasoning)).toBe(true);
expect(enhanced.metadata).toBeDefined();
expect(typeof enhanced.metadata.usedLearning).toBe("boolean");
expect(typeof enhanced.metadata.usedKnowledgeGraph).toBe("boolean");
});
test("should handle recommendations with insufficient data", async () => {
const projectFeatures: ProjectFeatures = {
language: "unknown",
size: "small",
complexity: "simple",
hasTests: false,
hasCI: false,
hasDocs: false,
isOpenSource: false,
};
const baseRecommendation = {
recommended: "jekyll",
confidence: 0.5,
};
const enhanced = await enhancedManager.getEnhancedRecommendation(
"/test/project",
baseRecommendation,
projectFeatures,
);
expect(enhanced).toBeDefined();
expect(enhanced.confidence).toBeGreaterThanOrEqual(0);
expect(enhanced.confidence).toBeLessThanOrEqual(1);
});
});
describe("Intelligent Analysis", () => {
test("should provide intelligent analysis with patterns and predictions", async () => {
enhancedManager.setContext({ projectId: "intelligent-analysis-test" });
// Add analysis data
await enhancedManager.remember("analysis", {
language: { primary: "python" },
framework: { name: "flask" },
dependencies: { count: 25 },
testing: { hasTests: true },
ci: { hasCI: true },
});
const analysisData = {
language: "python",
framework: "flask",
size: "medium",
hasTests: true,
hasCI: true,
};
const intelligentAnalysis = await enhancedManager.getIntelligentAnalysis(
"/test/project",
analysisData,
);
expect(intelligentAnalysis).toBeDefined();
expect(intelligentAnalysis.analysis).toBeDefined();
expect(Array.isArray(intelligentAnalysis.patterns)).toBe(true);
expect(Array.isArray(intelligentAnalysis.predictions)).toBe(true);
expect(Array.isArray(intelligentAnalysis.recommendations)).toBe(true);
expect(intelligentAnalysis.learningData).toBeDefined();
expect(typeof intelligentAnalysis.learningData.similarProjects).toBe(
"number",
);
expect(typeof intelligentAnalysis.learningData.confidenceLevel).toBe(
"number",
);
expect(["low", "medium", "high"]).toContain(
intelligentAnalysis.learningData.dataQuality,
);
// Check prediction structure
if (intelligentAnalysis.predictions.length > 0) {
const prediction = intelligentAnalysis.predictions[0];
expect(["success_rate", "optimal_ssg", "potential_issues"]).toContain(
prediction.type,
);
expect(typeof prediction.prediction).toBe("string");
expect(typeof prediction.confidence).toBe("number");
}
});
test("should adapt analysis based on historical patterns", async () => {
enhancedManager.setContext({ projectId: "adaptive-analysis-test" });
// Create pattern with multiple similar projects
for (let i = 0; i < 3; i++) {
await enhancedManager.remember("analysis", {
language: { primary: "javascript" },
framework: { name: "vue" },
});
await enhancedManager.remember("recommendation", {
recommended: "vuepress",
confidence: 0.8 + i * 0.05,
});
await enhancedManager.remember("deployment", {
status: "success",
ssg: "vuepress",
});
}
const analysisData = {
language: "javascript",
framework: "vue",
size: "small",
};
const analysis = await enhancedManager.getIntelligentAnalysis(
"/test/project",
analysisData,
);
expect(analysis.learningData.similarProjects).toBeGreaterThan(0);
expect(analysis.learningData.dataQuality).toBe("medium");
});
});
describe("Memory Integration", () => {
test("should integrate learning feedback into knowledge graph", async () => {
enhancedManager.setContext({ projectId: "integration-test" });
// Create initial recommendation
const memoryEntry = await enhancedManager.remember("recommendation", {
recommended: "hugo",
confidence: 0.8,
language: { primary: "go" },
});
// Simulate feedback by creating a deployment success record
await enhancedManager.remember("deployment", {
status: "success",
ssg: "hugo",
feedback: {
rating: 5,
helpful: true,
comments: "Worked perfectly",
},
});
// Verify feedback was processed
const stats = await enhancedManager.getLearningStatistics();
expect(stats).toBeDefined();
expect(stats.learning).toBeDefined();
});
test("should synchronize data between subsystems", async () => {
enhancedManager.setContext({ projectId: "sync-test" });
// Add data that should propagate between systems
await enhancedManager.remember("analysis", {
language: { primary: "rust" },
framework: { name: "actix" },
});
await enhancedManager.remember("deployment", {
status: "success",
ssg: "mdbook",
});
// The subsystems should automatically sync through the enhanced manager
// Verify data exists in both systems
const learningStats = await enhancedManager.getLearningStatistics();
expect(learningStats).toBeDefined();
expect(learningStats.learning).toBeDefined();
expect(learningStats.knowledgeGraph).toBeDefined();
expect(learningStats.combined).toBeDefined();
});
});
describe("Performance and Optimization", () => {
test("should handle concurrent enhanced operations", async () => {
enhancedManager.setContext({ projectId: "concurrent-enhanced-test" });
const operations = Array.from({ length: 5 }, async (_, i) => {
const projectFeatures: ProjectFeatures = {
language: "go",
size: "medium",
complexity: "moderate",
hasTests: true,
hasCI: true,
hasDocs: true,
isOpenSource: true,
};
const baseRecommendation = {
recommended: "hugo",
confidence: 0.8 + i * 0.02,
};
return enhancedManager.getEnhancedRecommendation(
"/test/project",
baseRecommendation,
projectFeatures,
);
});
const results = await Promise.all(operations);
expect(results.length).toBe(5);
results.forEach((result) => {
expect(result).toBeDefined();
expect(result.confidence).toBeGreaterThanOrEqual(0);
});
});
test("should provide optimization insights", async () => {
enhancedManager.setContext({ projectId: "optimization-test" });
// Add some data
await enhancedManager.remember("analysis", { performanceTest: true });
// Test learning statistics as a proxy for optimization insights
const stats = await enhancedManager.getLearningStatistics();
expect(stats).toBeDefined();
expect(stats.combined).toBeDefined();
expect(typeof stats.combined.systemMaturity).toBe("string");
expect(["nascent", "developing", "mature"]).toContain(
stats.combined.systemMaturity,
);
});
});
describe("Error Handling and Edge Cases", () => {
test("should handle malformed input gracefully", async () => {
const malformedFeatures = {
language: null,
size: "invalid" as any,
complexity: undefined as any,
};
const malformedRecommendation = {
recommended: "",
confidence: -1,
};
// Should not throw, but handle gracefully
const result = await enhancedManager.getEnhancedRecommendation(
"/test/project",
malformedRecommendation,
malformedFeatures as any,
);
expect(result).toBeDefined();
expect(result.confidence).toBeGreaterThanOrEqual(0);
expect(result.confidence).toBeLessThanOrEqual(1);
});
test("should handle subsystem failures gracefully", async () => {
// Test with partial system availability
const projectFeatures: ProjectFeatures = {
language: "javascript",
size: "small",
complexity: "simple",
hasTests: false,
hasCI: false,
hasDocs: false,
isOpenSource: true,
};
const baseRecommendation = {
recommended: "gatsby",
confidence: 0.6,
};
// Should work even if some subsystems have issues
const result = await enhancedManager.getEnhancedRecommendation(
"/test/project",
baseRecommendation,
projectFeatures,
);
expect(result).toBeDefined();
expect(result.baseRecommendation).toEqual(baseRecommendation);
});
});
});
```
--------------------------------------------------------------------------------
/src/tools/setup-structure.ts:
--------------------------------------------------------------------------------
```typescript
import { promises as fs } from "fs";
import path from "path";
import { z } from "zod";
import { MCPToolResponse, formatMCPResponse } from "../types/api.js";
const inputSchema = z.object({
path: z.string(),
ssg: z.enum(["jekyll", "hugo", "docusaurus", "mkdocs", "eleventy"]),
includeExamples: z.boolean().optional().default(true),
});
// Diataxis structure based on ADR-004
const DIATAXIS_STRUCTURE = {
tutorials: {
description: "Learning-oriented guides for newcomers",
example: "getting-started.md",
},
"how-to": {
description: "Task-oriented guides for specific goals",
example: "deploy-to-production.md",
},
reference: {
description: "Information-oriented technical descriptions",
example: "api-documentation.md",
},
explanation: {
description: "Understanding-oriented conceptual discussions",
example: "architecture-overview.md",
},
};
/**
* Sets up the Diataxis-compliant documentation structure for a project.
*
* Creates a comprehensive documentation structure following the Diataxis framework,
* organizing content into four categories: tutorials (learning-oriented), how-to
* guides (problem-oriented), reference (information-oriented), and explanation
* (understanding-oriented). Includes support for different static site generators
* and optional example content.
*
* @param args - The input arguments for structure setup
* @param args.path - The root path where documentation structure should be created
* @param args.ssg - The static site generator type for structure optimization
* @param args.includeExamples - Whether to include example content (default: true)
*
* @returns Promise resolving to structure setup results
* @returns content - Array containing the setup results in MCP tool response format
*
* @throws {Error} When the output path is inaccessible or invalid
* @throws {Error} When the SSG type is unsupported
* @throws {Error} When directory structure creation fails
*
* @example
* ```typescript
* // Set up Docusaurus structure
* const result = await setupStructure({
* path: "./docs",
* ssg: "docusaurus",
* includeExamples: true
* });
*
* // Set up minimal Hugo structure
* const minimal = await setupStructure({
* path: "./site/content",
* ssg: "hugo",
* includeExamples: false
* });
* ```
*
* @since 1.0.0
*/
export async function setupStructure(
args: unknown,
): Promise<{ content: any[] }> {
const startTime = Date.now();
const { path: docsPath, ssg, includeExamples } = inputSchema.parse(args);
try {
const createdDirs: string[] = [];
const createdFiles: string[] = [];
// Create base docs directory
await fs.mkdir(docsPath, { recursive: true });
// Create Diataxis structure
for (const [category, info] of Object.entries(DIATAXIS_STRUCTURE)) {
const categoryPath = path.join(docsPath, category);
await fs.mkdir(categoryPath, { recursive: true });
createdDirs.push(categoryPath);
// Create index file for category
const indexPath = path.join(categoryPath, "index.md");
const indexContent = generateCategoryIndex(
category,
info.description,
ssg,
includeExamples,
);
await fs.writeFile(indexPath, indexContent);
createdFiles.push(indexPath);
// Create example content if requested
if (includeExamples) {
const examplePath = path.join(categoryPath, info.example);
const exampleContent = generateExampleContent(
category,
info.example,
ssg,
);
await fs.writeFile(examplePath, exampleContent);
createdFiles.push(examplePath);
}
}
// Create root index
const rootIndexPath = path.join(docsPath, "index.md");
const rootIndexContent = generateRootIndex(ssg);
await fs.writeFile(rootIndexPath, rootIndexContent);
createdFiles.push(rootIndexPath);
const structureResult = {
docsPath,
ssg,
includeExamples,
directoriesCreated: createdDirs,
filesCreated: createdFiles,
diataxisCategories: Object.keys(DIATAXIS_STRUCTURE),
totalDirectories: createdDirs.length,
totalFiles: createdFiles.length,
};
const response: MCPToolResponse<typeof structureResult> = {
success: true,
data: structureResult,
metadata: {
toolVersion: "1.0.0",
executionTime: Date.now() - startTime,
timestamp: new Date().toISOString(),
},
recommendations: [
{
type: "info",
title: "Diataxis Structure Created",
description: `Successfully created ${createdDirs.length} directories and ${createdFiles.length} files`,
},
],
nextSteps: [
{
action: "Generate Sitemap",
toolRequired: "manage_sitemap",
description:
"Create sitemap.xml as source of truth for documentation links (required for SEO)",
priority: "high",
},
{
action: "Setup GitHub Pages Deployment",
toolRequired: "deploy_pages",
description: "Create automated deployment workflow",
priority: "medium",
},
],
};
return formatMCPResponse(response);
} catch (error) {
const errorResponse: MCPToolResponse = {
success: false,
error: {
code: "STRUCTURE_SETUP_FAILED",
message: `Failed to setup structure: ${error}`,
resolution: "Ensure the documentation path is writable and accessible",
},
metadata: {
toolVersion: "1.0.0",
executionTime: Date.now() - startTime,
timestamp: new Date().toISOString(),
},
};
return formatMCPResponse(errorResponse);
}
}
function generateCategoryIndex(
category: string,
description: string,
ssg: string,
includeExamples: boolean = true,
): string {
const title =
category.charAt(0).toUpperCase() + category.slice(1).replace("-", " ");
let frontmatter = "";
switch (ssg) {
case "docusaurus":
frontmatter = `---
id: ${category}-index
title: ${title}
sidebar_label: ${title}
---\n\n`;
break;
case "mkdocs":
case "jekyll":
case "hugo":
frontmatter = `---
title: ${title}
description: ${description}
---\n\n`;
break;
}
return `${frontmatter}# ${title}
${description}
## Available Guides
This section contains ${category} documentation following the Diataxis framework.
${generateDiataxisExplanation(category)}
## Contents
${
includeExamples
? `- [Example: ${
DIATAXIS_STRUCTURE[category as keyof typeof DIATAXIS_STRUCTURE].example
}](./${
DIATAXIS_STRUCTURE[category as keyof typeof DIATAXIS_STRUCTURE].example
})`
: "- Coming soon..."
}
`;
}
function generateExampleContent(
category: string,
filename: string,
ssg: string,
): string {
const title = filename
.replace(".md", "")
.replace(/-/g, " ")
.split(" ")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
let frontmatter = "";
switch (ssg) {
case "docusaurus":
frontmatter = `---
id: ${filename.replace(".md", "")}
title: ${title}
sidebar_label: ${title}
---\n\n`;
break;
default:
frontmatter = `---
title: ${title}
---\n\n`;
break;
}
let content = "";
switch (category) {
case "tutorials":
content = `# ${title}
This tutorial will guide you through the process step by step.
## Prerequisites
Before you begin, ensure you have:
- Requirement 1
- Requirement 2
## Step 1: Initial Setup
Start by...
## Step 2: Configuration
Next, configure...
## Step 3: Verification
Finally, verify...
## Summary
In this tutorial, you learned how to:
- Achievement 1
- Achievement 2
- Achievement 3
## Next Steps
- Explore [How-To Guides](../how-to/)
- Read the [API Reference](../reference/)`;
break;
case "how-to":
content = `# ${title}
This guide shows you how to accomplish a specific task.
## Prerequisites
- Prerequisite 1
- Prerequisite 2
## Steps
### 1. Prepare your environment
\`\`\`bash
# Example command
echo "Setup environment"
\`\`\`
### 2. Execute the task
\`\`\`bash
# Main command
echo "Execute task"
\`\`\`
### 3. Verify results
\`\`\`bash
# Verification command
echo "Verify success"
\`\`\`
## Troubleshooting
If you encounter issues:
- Check condition 1
- Verify setting 2
## Related Guides
- [Another How-To Guide](./another-guide.md)
- [Reference Documentation](../reference/)`;
break;
case "reference":
content = `# ${title}
Technical reference documentation.
## Overview
This document provides complete reference information for...
## API Endpoints
### GET /api/resource
Retrieves...
**Parameters:**
- \`param1\` (string, required): Description
- \`param2\` (number, optional): Description
**Response:**
\`\`\`json
{
"field1": "value",
"field2": 123
}
\`\`\`
### POST /api/resource
Creates...
## Configuration Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| option1 | string | "default" | Description of option1 |
| option2 | boolean | false | Description of option2 |
## Error Codes
| Code | Description | Resolution |
|------|-------------|------------|
| E001 | Error description | How to fix |
| E002 | Error description | How to fix |`;
break;
case "explanation":
content = `# ${title}
This document explains the concepts and reasoning behind...
## Introduction
Understanding the architecture requires knowledge of...
## Core Concepts
### Concept 1
Explanation of the first core concept...
### Concept 2
Explanation of the second core concept...
## Design Decisions
### Why This Approach?
We chose this approach because...
### Trade-offs
The main trade-offs include:
- Trade-off 1: Benefit vs Cost
- Trade-off 2: Benefit vs Cost
## Comparison with Alternatives
| Approach | Pros | Cons |
|----------|------|------|
| Our Approach | Pro 1, Pro 2 | Con 1 |
| Alternative 1 | Pro 1 | Con 1, Con 2 |
| Alternative 2 | Pro 1, Pro 2 | Con 1 |
## Further Reading
- [Related Tutorial](../tutorials/)
- [Implementation Guide](../how-to/)`;
break;
}
return `${frontmatter}${content}`;
}
function generateRootIndex(ssg: string): string {
let frontmatter = "";
switch (ssg) {
case "docusaurus":
frontmatter = `---
id: intro
title: Documentation
sidebar_position: 1
---\n\n`;
break;
default:
frontmatter = `---
title: Documentation
---\n\n`;
break;
}
return `${frontmatter}# Documentation
Welcome to our documentation! This site follows the [Diataxis](https://diataxis.fr/) framework to provide clear, well-organized documentation.
## Documentation Structure
Our documentation is organized into four distinct sections:
### 📚 [Tutorials](./tutorials/)
Learning-oriented guides that take you through a process step by step. Perfect for newcomers who want to get started.
### 🔧 [How-To Guides](./how-to/)
Task-oriented recipes that help you accomplish specific goals. Ideal when you know what you want to do.
### 📖 [Reference](./reference/)
Information-oriented technical descriptions of the system. Essential when you need to look up specific details.
### 💡 [Explanation](./explanation/)
Understanding-oriented discussions that clarify and illuminate topics. Great for deepening your knowledge.
## Quick Start
New to this project? Start with our [Getting Started Tutorial](./tutorials/getting-started.md).
## Contributing
We welcome contributions to our documentation! Please see our [Contributing Guide](./how-to/contribute.md) for details.
`;
}
function generateDiataxisExplanation(category: string): string {
const explanations: Record<string, string> = {
tutorials: `
**Tutorials** are learning-oriented and help newcomers get started:
- Take the reader through a process step by step
- Focus on learning by doing
- Ensure the reader succeeds in accomplishing something
- Build confidence through success`,
"how-to": `
**How-To Guides** are task-oriented and help users accomplish specific goals:
- Solve specific problems
- Assume some knowledge and experience
- Provide a series of steps
- Focus on results`,
reference: `
**Reference** documentation is information-oriented:
- Describe the machinery
- Be accurate and complete
- Focus on describing, not explaining
- Structure content for finding information`,
explanation: `
**Explanation** documentation is understanding-oriented:
- Clarify and illuminate a topic
- Provide context and background
- Discuss alternatives and opinions
- Focus on understanding, not instruction`,
};
return explanations[category] || "";
}
```
--------------------------------------------------------------------------------
/docs/api/assets/icons.js:
--------------------------------------------------------------------------------
```javascript
(function () {
addIcons();
function addIcons() {
if (document.readyState === "loading")
return document.addEventListener("DOMContentLoaded", addIcons);
const svg = document.body.appendChild(
document.createElementNS("http://www.w3.org/2000/svg", "svg"),
);
svg.innerHTML = `<g id="icon-1" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-module)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">M</text></g><g id="icon-2" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-module)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">M</text></g><g id="icon-4" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-namespace)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">N</text></g><g id="icon-8" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-enum)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">E</text></g><g id="icon-16" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-property)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">P</text></g><g id="icon-32" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-variable)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">V</text></g><g id="icon-64" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-function)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">F</text></g><g id="icon-128" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-class)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">C</text></g><g id="icon-256" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-interface)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">I</text></g><g id="icon-512" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-constructor)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">C</text></g><g id="icon-1024" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-property)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">P</text></g><g id="icon-2048" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-method)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">M</text></g><g id="icon-4096" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-function)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">F</text></g><g id="icon-8192" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-property)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">P</text></g><g id="icon-16384" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-constructor)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">C</text></g><g id="icon-32768" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-property)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">P</text></g><g id="icon-65536" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-type-alias)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">T</text></g><g id="icon-131072" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-type-alias)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">T</text></g><g id="icon-262144" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-accessor)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">A</text></g><g id="icon-524288" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-accessor)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">A</text></g><g id="icon-1048576" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-accessor)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">A</text></g><g id="icon-2097152" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-type-alias)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">T</text></g><g id="icon-4194304" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-ts-reference)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="12"></rect><text fill="var(--color-icon-text)" x="50%" y="50%" dominant-baseline="central" text-anchor="middle">R</text></g><g id="icon-8388608" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-document)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><g stroke="var(--color-icon-text)" fill="none" stroke-width="1.5"><polygon points="6,5 6,19 18,19, 18,10 13,5"></polygon><line x1="9" y1="9" x2="13" y2="9"></line><line x1="9" y1="12" x2="15" y2="12"></line><line x1="9" y1="15" x2="15" y2="15"></line></g></g><g id="icon-folder" class="tsd-no-select"><rect fill="var(--color-icon-background)" stroke="var(--color-document)" stroke-width="1.5" x="1" y="1" width="22" height="22" rx="6"></rect><g stroke="var(--color-icon-text)" fill="none" stroke-width="1.5"><polygon points="5,5 10,5 12,8 19,8 19,18 5,18"></polygon></g></g><g id="icon-chevronDown" class="tsd-no-select"><path d="M4.93896 8.531L12 15.591L19.061 8.531L16.939 6.409L12 11.349L7.06098 6.409L4.93896 8.531Z" fill="var(--color-icon-text)"></path></g><g id="icon-chevronSmall" class="tsd-no-select"><path d="M1.5 5.50969L8 11.6609L14.5 5.50969L12.5466 3.66086L8 7.96494L3.45341 3.66086L1.5 5.50969Z" fill="var(--color-icon-text)"></path></g><g id="icon-checkbox" class="tsd-no-select"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></g><g id="icon-menu" class="tsd-no-select"><rect x="1" y="3" width="14" height="2" fill="var(--color-icon-text)"></rect><rect x="1" y="7" width="14" height="2" fill="var(--color-icon-text)"></rect><rect x="1" y="11" width="14" height="2" fill="var(--color-icon-text)"></rect></g><g id="icon-search" class="tsd-no-select"><path d="M15.7824 13.833L12.6666 10.7177C12.5259 10.5771 12.3353 10.499 12.1353 10.499H11.6259C12.4884 9.39596 13.001 8.00859 13.001 6.49937C13.001 2.90909 10.0914 0 6.50048 0C2.90959 0 0 2.90909 0 6.49937C0 10.0896 2.90959 12.9987 6.50048 12.9987C8.00996 12.9987 9.39756 12.4863 10.5008 11.6239V12.1332C10.5008 12.3332 10.5789 12.5238 10.7195 12.6644L13.8354 15.7797C14.1292 16.0734 14.6042 16.0734 14.8948 15.7797L15.7793 14.8954C16.0731 14.6017 16.0731 14.1267 15.7824 13.833ZM6.50048 10.499C4.29094 10.499 2.50018 8.71165 2.50018 6.49937C2.50018 4.29021 4.28781 2.49976 6.50048 2.49976C8.71001 2.49976 10.5008 4.28708 10.5008 6.49937C10.5008 8.70852 8.71314 10.499 6.50048 10.499Z" fill="var(--color-icon-text)"></path></g><g id="icon-anchor" class="tsd-no-select"><g stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M10 14a3.5 3.5 0 0 0 5 0l4 -4a3.5 3.5 0 0 0 -5 -5l-.5 .5"></path><path d="M14 10a3.5 3.5 0 0 0 -5 0l-4 4a3.5 3.5 0 0 0 5 5l.5 -.5"></path></g></g><g id="icon-alertNote" class="tsd-no-select"><path fill="var(--color-alert-note)" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></g><g id="icon-alertTip" class="tsd-no-select"><path fill="var(--color-alert-tip)" d="M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"></path></g><g id="icon-alertImportant" class="tsd-no-select"><path fill="var(--color-alert-important)" d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></g><g id="icon-alertWarning" class="tsd-no-select"><path fill="var(--color-alert-warning)" d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></g><g id="icon-alertCaution" class="tsd-no-select"><path fill="var(--color-alert-caution)" d="M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></g>`;
svg.style.display = "none";
if (location.protocol === "file:") updateUseElements();
}
function updateUseElements() {
document.querySelectorAll("use").forEach((el) => {
if (el.getAttribute("href").includes("#icon-")) {
el.setAttribute("href", el.getAttribute("href").replace(/.*#/, "#"));
}
});
}
})();
```
--------------------------------------------------------------------------------
/tests/integration/knowledge-graph-workflow.test.ts:
--------------------------------------------------------------------------------
```typescript
/**
* Integration Tests for Knowledge Graph Workflow
* Phase 1: End-to-End KG-Analysis Integration
*/
import { describe, it, expect, beforeEach, afterEach } from "@jest/globals";
import { promises as fs } from "fs";
import { join } from "path";
import { tmpdir } from "os";
import {
initializeKnowledgeGraph,
getKnowledgeGraph,
saveKnowledgeGraph,
createOrUpdateProject,
getProjectContext,
trackDeployment,
getKGStatistics,
} from "../../src/memory/kg-integration.js";
describe("Knowledge Graph Workflow Integration", () => {
let testDir: string;
let originalEnv: string | undefined;
beforeEach(async () => {
// Create temporary test directory
testDir = join(tmpdir(), `kg-workflow-test-${Date.now()}`);
await fs.mkdir(testDir, { recursive: true });
// Set environment variable for storage
originalEnv = process.env.DOCUMCP_STORAGE_DIR;
process.env.DOCUMCP_STORAGE_DIR = testDir;
// Initialize KG
await initializeKnowledgeGraph(testDir);
});
afterEach(async () => {
// Restore environment
if (originalEnv) {
process.env.DOCUMCP_STORAGE_DIR = originalEnv;
} else {
delete process.env.DOCUMCP_STORAGE_DIR;
}
// Clean up test directory
try {
await fs.rm(testDir, { recursive: true, force: true });
} catch (error) {
console.warn("Failed to clean up test directory:", error);
}
});
describe("Complete Analysis Workflow", () => {
it("should handle first-time project analysis", async () => {
const analysis = {
id: "analysis_001",
timestamp: new Date().toISOString(),
path: "/test/project",
projectName: "Test Project",
structure: {
totalFiles: 100,
totalDirectories: 10,
languages: {
typescript: 60,
javascript: 30,
json: 10,
},
hasTests: true,
hasCI: true,
hasDocs: false,
},
};
// Create project in KG
const projectNode = await createOrUpdateProject(analysis);
expect(projectNode).toBeDefined();
expect(projectNode.type).toBe("project");
expect(projectNode.properties.name).toBe("Test Project");
expect(projectNode.properties.analysisCount).toBe(1);
// Get context (should be empty for first analysis)
const context = await getProjectContext("/test/project");
expect(context.previousAnalyses).toBe(1);
expect(context.knownTechnologies).toContain("typescript");
});
it("should track returning project with historical context", async () => {
const analysis1 = {
id: "analysis_001",
timestamp: new Date().toISOString(),
path: "/test/project",
projectName: "Test Project",
structure: {
totalFiles: 100,
languages: { typescript: 60, javascript: 40 },
hasTests: true,
hasCI: false,
hasDocs: false,
},
};
const analysis2 = {
id: "analysis_002",
timestamp: new Date().toISOString(),
path: "/test/project",
projectName: "Test Project",
structure: {
totalFiles: 120,
languages: { typescript: 80, javascript: 40 },
hasTests: true,
hasCI: true,
hasDocs: true,
},
};
// First analysis
await createOrUpdateProject(analysis1);
// Second analysis
const projectNode = await createOrUpdateProject(analysis2);
expect(projectNode.properties.analysisCount).toBe(2);
// Get context
const context = await getProjectContext("/test/project");
expect(context.previousAnalyses).toBe(2);
expect(context.lastAnalyzed).toBeDefined();
});
it("should find similar projects based on technologies", async () => {
// Create multiple projects with shared technologies
const project1 = {
id: "analysis_001",
timestamp: new Date().toISOString(),
path: "/test/project1",
projectName: "React App",
structure: {
totalFiles: 50,
languages: { typescript: 30, javascript: 20 },
hasTests: true,
hasCI: false,
hasDocs: false,
},
};
const project2 = {
id: "analysis_002",
timestamp: new Date().toISOString(),
path: "/test/project2",
projectName: "Another React App",
structure: {
totalFiles: 75,
languages: { typescript: 50, javascript: 25 },
hasTests: true,
hasCI: true,
hasDocs: false,
},
};
const project3 = {
id: "analysis_003",
timestamp: new Date().toISOString(),
path: "/test/project3",
projectName: "Python Project",
structure: {
totalFiles: 40,
languages: { python: 40 },
hasTests: true,
hasCI: false,
hasDocs: false,
},
};
// Create all projects
await createOrUpdateProject(project1);
await createOrUpdateProject(project2);
await createOrUpdateProject(project3);
// Get context for project1
const context = await getProjectContext("/test/project1");
// Should find project2 as similar (shares typescript/javascript)
// Should not find project3 (uses different stack)
expect(context.similarProjects.length).toBeGreaterThan(0);
const similarProject = context.similarProjects.find(
(p) => p.properties.name === "Another React App",
);
expect(similarProject).toBeDefined();
});
});
describe("Deployment Tracking Workflow", () => {
it("should track successful deployment", async () => {
// Create project
const analysis = {
id: "project_001",
timestamp: new Date().toISOString(),
path: "/test/project",
projectName: "Test Project",
structure: {
totalFiles: 50,
languages: { typescript: 50 },
hasTests: true,
hasCI: false,
hasDocs: false,
},
};
const projectNode = await createOrUpdateProject(analysis);
// Track successful deployment
await trackDeployment(projectNode.id, "docusaurus", true, {
buildTime: 45,
deploymentUrl: "https://test.github.io",
});
// Verify deployment was tracked
const kg = await getKnowledgeGraph();
const edges = await kg.findEdges({
source: projectNode.id,
properties: { baseType: "project_deployed_with" },
});
expect(edges.length).toBeGreaterThan(0);
expect(edges[0].properties.success).toBe(true);
expect(edges[0].properties.buildTime).toBe(45);
});
it("should track failed deployment", async () => {
const analysis = {
id: "project_002",
timestamp: new Date().toISOString(),
path: "/test/project2",
projectName: "Test Project 2",
structure: {
totalFiles: 50,
languages: { javascript: 50 },
hasTests: false,
hasCI: false,
hasDocs: false,
},
};
const projectNode = await createOrUpdateProject(analysis);
// Track failed deployment
await trackDeployment(projectNode.id, "jekyll", false, {
errorMessage: "Ruby version mismatch",
});
const kg = await getKnowledgeGraph();
const edges = await kg.findEdges({
source: projectNode.id,
properties: { baseType: "project_deployed_with" },
});
expect(edges.length).toBeGreaterThan(0);
expect(edges[0].properties.success).toBe(false);
expect(edges[0].properties.errorMessage).toContain("Ruby version");
});
it("should update configuration success rate over time", async () => {
const analysis = {
id: "project_003",
timestamp: new Date().toISOString(),
path: "/test/project3",
projectName: "Test Project 3",
structure: {
totalFiles: 50,
languages: { typescript: 50 },
hasTests: false,
hasCI: false,
hasDocs: false,
},
};
const projectNode = await createOrUpdateProject(analysis);
// Track multiple deployments
await trackDeployment(projectNode.id, "hugo", true);
await trackDeployment(projectNode.id, "hugo", true);
await trackDeployment(projectNode.id, "hugo", false);
const kg = await getKnowledgeGraph();
const configNode = await kg.findNode({
type: "configuration",
properties: { ssg: "hugo" },
});
expect(configNode).toBeDefined();
expect(configNode!.properties.usageCount).toBe(3);
// Success rate: 2/3 = 0.666...
expect(configNode!.properties.deploymentSuccessRate).toBeCloseTo(
0.666,
2,
);
});
});
describe("Knowledge Graph Statistics", () => {
it("should return accurate statistics", async () => {
// Create multiple projects
for (let i = 0; i < 5; i++) {
const analysis = {
id: `project_00${i}`,
timestamp: new Date().toISOString(),
path: `/test/project${i}`,
projectName: `Project ${i}`,
structure: {
totalFiles: 50,
languages: { typescript: 30, javascript: 20 },
hasTests: true,
hasCI: false,
hasDocs: false,
},
};
await createOrUpdateProject(analysis);
}
const stats = await getKGStatistics();
expect(stats.projectCount).toBe(5);
expect(stats.technologyCount).toBeGreaterThan(0);
expect(stats.nodeCount).toBeGreaterThan(5); // Projects + technologies
expect(stats.edgeCount).toBeGreaterThan(0); // project_uses_technology edges
});
});
describe("Persistence Workflow", () => {
it("should persist data across sessions", async () => {
const analysis = {
id: "persistent_project",
timestamp: new Date().toISOString(),
path: "/test/persistent",
projectName: "Persistent Project",
structure: {
totalFiles: 50,
languages: { typescript: 50 },
hasTests: true,
hasCI: false,
hasDocs: false,
},
};
// Create project and save
await createOrUpdateProject(analysis);
await saveKnowledgeGraph();
// Reinitialize (simulating new session)
await initializeKnowledgeGraph(testDir);
// Verify data was loaded
const context = await getProjectContext("/test/persistent");
expect(context.previousAnalyses).toBe(1);
expect(context.knownTechnologies).toContain("typescript");
});
});
describe("Complex Multi-Step Workflow", () => {
it("should handle complete project lifecycle", async () => {
// Step 1: Initial analysis
const initialAnalysis = {
id: "lifecycle_project",
timestamp: new Date().toISOString(),
path: "/test/lifecycle",
projectName: "Lifecycle Project",
structure: {
totalFiles: 30,
languages: { javascript: 30 },
hasTests: false,
hasCI: false,
hasDocs: false,
},
};
const project1 = await createOrUpdateProject(initialAnalysis);
expect(project1.properties.analysisCount).toBe(1);
// Step 2: Track deployment attempt (failed)
await trackDeployment(project1.id, "jekyll", false, {
errorMessage: "Missing dependencies",
});
// Step 3: Re-analysis after fixes
const updatedAnalysis = {
...initialAnalysis,
id: "lifecycle_project_2",
timestamp: new Date().toISOString(),
structure: {
totalFiles: 35,
languages: { javascript: 30, json: 5 },
hasTests: true,
hasCI: true,
hasDocs: true,
},
};
const project2 = await createOrUpdateProject(updatedAnalysis);
expect(project2.properties.analysisCount).toBe(2);
expect(project2.properties.hasCI).toBe(true);
// Step 4: Successful deployment
await trackDeployment(project2.id, "eleventy", true, {
buildTime: 30,
deploymentUrl: "https://lifecycle.github.io",
});
// Verify complete lifecycle
const kg = await getKnowledgeGraph();
// Check project node
const projectNode = await kg.findNode({
type: "project",
properties: { path: "/test/lifecycle" },
});
expect(projectNode).toBeDefined();
expect(projectNode!.properties.analysisCount).toBe(2);
// Check deployments
const deployments = await kg.findEdges({
source: projectNode!.id,
properties: { baseType: "project_deployed_with" },
});
expect(deployments).toHaveLength(2);
// Check technologies
const techEdges = await kg.findEdges({
source: projectNode!.id,
type: "project_uses_technology",
});
expect(techEdges.length).toBeGreaterThan(0);
// Get final context
const context = await getProjectContext("/test/lifecycle");
expect(context.previousAnalyses).toBe(2);
expect(context.knownTechnologies).toContain("javascript");
});
});
});
```
--------------------------------------------------------------------------------
/docs/adrs/003-static-site-generator-recommendation-engine.md:
--------------------------------------------------------------------------------
```markdown
---
id: 003-static-site-generator-recommendation-engine
title: "ADR-003: SSG Recommendation Engine Design"
sidebar_label: "ADR-3: SSG Recommendation Engine Design"
sidebar_position: 3
documcp:
last_updated: "2025-11-20T00:46:21.937Z"
last_validated: "2025-11-20T00:46:21.937Z"
auto_updated: false
update_frequency: monthly
---
# ADR-003: Static Site Generator Recommendation Engine Design
## Status
Accepted
## Context
DocuMCP must intelligently recommend the most appropriate static site generator (SSG) for each project based on comprehensive analysis of project characteristics, team capabilities, and technical requirements. The recommendation engine needs to move beyond simple feature comparison to provide data-driven, contextual recommendations with clear justifications.
Current SSG landscape includes:
- **Jekyll**: GitHub Pages native, Ruby-based, mature ecosystem
- **Hugo**: Go-based, fast builds, extensive theming
- **Docusaurus**: React-based, modern features, Meta-backed
- **MkDocs**: Python-based, simple, Material theme
- **Eleventy**: JavaScript-based, flexible, minimal configuration
Key challenges:
- Choice paralysis for users unfamiliar with SSG ecosystem
- Technical requirements vary significantly by project type
- Performance needs differ based on content volume and update frequency
- Team capabilities and preferences affect long-term success
- Maintenance overhead varies dramatically between options
## Decision
We will implement a multi-criteria decision analysis (MCDA) framework that evaluates project characteristics against SSG capabilities to provide ranked recommendations with confidence scores and detailed justifications.
### Recommendation Engine Architecture:
#### 1. SSG Knowledge Base
- **Comprehensive SSG profiles** with quantitative and qualitative metrics
- **Performance characteristics**: build times, memory usage, scalability limits
- **Learning curve assessments**: setup complexity, maintenance requirements
- **Feature compatibility matrices**: advanced features, plugin ecosystems
- **Community metrics**: activity, support quality, ecosystem maturity
#### 2. Decision Matrix Framework
- **Multi-criteria evaluation** across weighted factors
- **Project-specific factor weighting** based on analysis results
- **Algorithmic scoring** with transparent calculation methods
- **Confidence assessment** based on factor alignment quality
#### 3. Performance Modeling
- **Build time prediction** based on content volume and complexity
- **Scalability assessment** for projected growth patterns
- **Resource requirement estimation** for different deployment scenarios
#### 4. Compatibility Assessment
- **Technical stack alignment** with existing project technologies
- **Workflow integration** with current development processes
- **CI/CD compatibility** with existing automation infrastructure
## Alternatives Considered
### Simple Rule-Based Recommendations
- **Pros**: Easy to implement, fast execution, predictable results
- **Cons**: Inflexible, doesn't handle edge cases, poor justification quality
- **Decision**: Rejected due to insufficient sophistication for quality recommendations
### Machine Learning-Based Recommendation
- **Pros**: Could learn from successful project outcomes, adaptive over time
- **Cons**: Requires training data, model maintenance, unpredictable results
- **Decision**: Deferred to future versions; insufficient training data initially
### User Survey-Based Selection
- **Pros**: Direct user input, captures preferences and constraints
- **Cons**: Requires user expertise, time-consuming, potential analysis paralysis
- **Decision**: Integrated as preference input to algorithmic recommendation
### External Service Integration (StackShare, etc.)
- **Pros**: Real-world usage data, community insights
- **Cons**: External dependency, potential bias, limited project-specific context
- **Decision**: Rejected for core logic; may integrate for validation
## Consequences
### Positive
- **Objective Recommendations**: Data-driven approach reduces bias and subjectivity
- **Clear Justifications**: Users understand why specific SSGs are recommended
- **Confidence Indicators**: Users know when recommendations are highly certain vs. uncertain
- **Contextual Intelligence**: Recommendations adapt to specific project characteristics
- **Educational Value**: Users learn about SSG capabilities and trade-offs
### Negative
- **Algorithm Complexity**: Multi-criteria analysis requires careful tuning and validation
- **Knowledge Base Maintenance**: SSG profiles need regular updates as tools evolve
- **Subjectivity in Weights**: Factor importance assignments may not match all user preferences
### Risks and Mitigations
- **Recommendation Accuracy**: Validate against known successful project combinations
- **Algorithm Bias**: Test across diverse project types and regularly audit results
- **Knowledge Staleness**: Implement automated SSG capability monitoring and updates
## Implementation Details
### Decision Criteria Framework
```typescript
interface RecommendationCriteria {
projectSize: ProjectSizeMetrics;
technicalComplexity: ComplexityAssessment;
teamCapabilities: TeamProfile;
performanceRequirements: PerformanceNeeds;
maintenancePreferences: MaintenanceProfile;
customizationNeeds: CustomizationRequirements;
}
interface SSGProfile {
name: string;
capabilities: SSGCapabilities;
performance: PerformanceProfile;
learningCurve: LearningCurveMetrics;
ecosystem: EcosystemMetrics;
maintenanceOverhead: MaintenanceMetrics;
}
```
### Scoring Algorithm
```typescript
interface ScoringWeights {
buildPerformance: number; // 0.20
setupComplexity: number; // 0.15
technicalAlignment: number; // 0.25
customizationFlexibility: number; // 0.15
maintenanceOverhead: number; // 0.15
ecosystemMaturity: number; // 0.10
}
function calculateSSGScore(
project: ProjectAnalysis,
ssg: SSGProfile,
weights: ScoringWeights,
): RecommendationScore {
// Weighted scoring across multiple criteria
// Returns score (0-100) with component breakdown
}
```
### Performance Modeling (Updated with Research 2025-01-14)
**Research Integration**: Comprehensive SSG performance analysis validates and refines our approach:
```typescript
interface PerformanceModel {
predictBuildTime(
contentVolume: number,
complexity: number,
): BuildTimeEstimate;
assessScalability(projectedGrowth: GrowthPattern): ScalabilityRating;
estimateResourceNeeds(deployment: DeploymentTarget): ResourceRequirements;
// Research-validated performance tiers
calculatePerformanceTier(
ssg: SSGType,
projectScale: ProjectScale,
): PerformanceTier;
}
// Research-validated performance characteristics
const SSG_PERFORMANCE_MATRIX = {
hugo: {
smallSites: { buildTime: "instant", scaleFactor: 1.0, overhead: "minimal" },
mediumSites: {
buildTime: "seconds",
scaleFactor: 1.1,
overhead: "minimal",
},
largeSites: { buildTime: "seconds", scaleFactor: 1.2, overhead: "minimal" },
},
gatsby: {
smallSites: { buildTime: "slow", scaleFactor: 250, overhead: "webpack" },
mediumSites: {
buildTime: "moderate",
scaleFactor: 100,
overhead: "webpack",
},
largeSites: {
buildTime: "improving",
scaleFactor: 40,
overhead: "optimized",
},
},
eleventy: {
smallSites: { buildTime: "fast", scaleFactor: 3, overhead: "node" },
mediumSites: { buildTime: "good", scaleFactor: 8, overhead: "node" },
largeSites: { buildTime: "moderate", scaleFactor: 15, overhead: "node" },
},
jekyll: {
smallSites: { buildTime: "good", scaleFactor: 2, overhead: "ruby" },
mediumSites: { buildTime: "slowing", scaleFactor: 12, overhead: "ruby" },
largeSites: {
buildTime: "poor",
scaleFactor: 25,
overhead: "ruby-bottleneck",
},
},
} as const;
// Research-validated recommendation algorithm
const calculatePerformanceScore = (
ssg: SSGType,
projectMetrics: ProjectMetrics,
): number => {
const { pageCount, updateFrequency, teamTechnicalLevel } = projectMetrics;
// Scale-based performance weighting (research-validated)
const performanceWeight =
pageCount > 1000 ? 0.8 : pageCount > 100 ? 0.6 : 0.4;
// Research-based performance scores
const baseScores = {
hugo: 100, // Fastest across all scales
eleventy: 85, // Good balance
jekyll: pageCount > 500 ? 60 : 80, // Ruby bottleneck at scale
nextjs: 70, // Framework overhead, good at scale
gatsby: pageCount > 1000 ? 65 : 45, // Severe small-site penalty
docusaurus: 75, // Optimized for documentation
};
return (
baseScores[ssg] * performanceWeight +
baseScores[ssg] * (1 - performanceWeight) * featureScore[ssg]
);
};
```
### Recommendation Output
```typescript
interface Recommendation {
ssg: SSGProfile;
score: number;
confidence: number;
justification: RecommendationJustification;
tradeoffs: Tradeoff[];
alternativeOptions: AlternativeRecommendation[];
}
interface RecommendationJustification {
primaryStrengths: string[];
concerningWeaknesses: string[];
bestFitReasons: string[];
performancePredictions: PerformancePrediction[];
}
```
### SSG Knowledge Base Structure
```typescript
const SSG_PROFILES: Record<string, SSGProfile> = {
jekyll: {
name: "Jekyll",
capabilities: {
buildSpeed: "moderate",
themingFlexibility: "high",
pluginEcosystem: "mature",
githubPagesNative: true,
contentTypes: ["markdown", "liquid"],
i18nSupport: "plugin-based",
},
performance: {
averageBuildTime: "2-5 minutes per 100 pages",
memoryUsage: "moderate",
scalabilityLimit: "1000+ pages",
},
learningCurve: {
setupComplexity: "low-moderate",
configurationComplexity: "moderate",
customizationComplexity: "moderate-high",
},
// ... additional profile data
},
// ... other SSG profiles
};
```
### Confidence Calculation
```typescript
function calculateConfidence(
scores: SSGScore[],
projectAnalysis: ProjectAnalysis,
): number {
const scoreSpread = Math.max(...scores) - Math.min(...scores);
const analysisCompleteness = assessAnalysisCompleteness(projectAnalysis);
const criteriaAlignment = assessCriteriaAlignment(scores);
// Higher confidence when:
// - Clear winner emerges (high score spread)
// - Analysis is comprehensive
// - Criteria strongly align with one option
return calculateWeightedConfidence(
scoreSpread,
analysisCompleteness,
criteriaAlignment,
);
}
```
## Quality Assurance
### Validation Strategy
- **Benchmark Projects**: Test against known successful project-SSG combinations
- **Expert Review**: Documentation experts validate recommendation logic
- **User Feedback**: Collect real-world outcomes to refine algorithms
- **A/B Testing**: Compare algorithm versions for recommendation quality
### Testing Framework
```typescript
describe("RecommendationEngine", () => {
it("should recommend Jekyll for simple documentation sites");
it("should recommend Hugo for performance-critical large sites");
it("should recommend Docusaurus for React-based projects");
it("should provide low confidence for ambiguous project profiles");
it("should justify all recommendations with specific reasons");
});
```
### Monitoring and Metrics
- Recommendation accuracy rates by project type
- User satisfaction with recommendations
- Confidence score calibration accuracy
- Algorithm performance and execution time
## Knowledge Base Maintenance
### SSG Capability Tracking
- Regular monitoring of SSG releases and capability changes
- Community feedback integration for real-world performance data
- Automated testing of SSG performance benchmarks
- Expert review cycles for knowledge base accuracy
### Update Processes
- Quarterly comprehensive review of all SSG profiles
- Monthly monitoring of major releases and capability changes
- Community contribution process for knowledge base improvements
- Automated validation of knowledge base consistency
## Future Enhancements
### Advanced Analytics
- Historical success rate tracking by recommendation
- Machine learning integration for pattern recognition
- User preference learning and personalization
- Comparative analysis across similar projects
### Extended SSG Support
- Evaluation framework for new SSG additions
- Community-contributed SSG profiles
- Specialized SSG recommendations (e.g., Sphinx for API docs)
- Custom SSG configuration for specific use cases
### Integration Features
- Direct integration with SSG documentation and examples
- Automated setup validation and testing
- Performance monitoring and optimization recommendations
- Migration assistance between SSGs
## Security and Privacy
- No collection of sensitive project information
- Anonymized analytics for algorithm improvement
- Transparent recommendation criteria and methodology
- User control over data sharing preferences
## References
- [Static Site Generator Comparison Studies](https://jamstack.org/generators/)
- [Multi-Criteria Decision Analysis](https://en.wikipedia.org/wiki/Multiple-criteria_decision_analysis)
- [Static Site Generator Performance Comparison](https://jamstack.org/generators/)
```
--------------------------------------------------------------------------------
/src/memory/user-preferences.ts:
--------------------------------------------------------------------------------
```typescript
/**
* User Preference Management Module
* Phase 2.2: User Preference Learning and Application
*
* Tracks and applies user preferences across DocuMCP operations
*/
import { getKnowledgeGraph, saveKnowledgeGraph } from "./kg-integration.js";
import { GraphNode } from "./knowledge-graph.js";
export interface UserPreferences {
userId: string;
preferredSSGs: string[];
documentationStyle: "minimal" | "comprehensive" | "tutorial-heavy";
expertiseLevel: "beginner" | "intermediate" | "advanced";
preferredTechnologies: string[];
preferredDiataxisCategories: Array<
"tutorials" | "how-to" | "reference" | "explanation"
>;
autoApplyPreferences: boolean;
lastUpdated: string;
}
export interface SSGUsageEvent {
ssg: string;
success: boolean;
timestamp: string;
projectType?: string;
}
/**
* User Preference Manager
* Handles storage, retrieval, and inference of user preferences
*/
export class UserPreferenceManager {
private userId: string;
private preferences: UserPreferences | null = null;
constructor(userId: string = "default") {
this.userId = userId;
}
/**
* Initialize and load user preferences from knowledge graph
*/
async initialize(): Promise<void> {
const kg = await getKnowledgeGraph();
// Find existing user node
const userNode = await kg.findNode({
type: "user",
properties: { userId: this.userId },
});
if (userNode) {
this.preferences = {
userId: this.userId,
preferredSSGs: userNode.properties.preferredSSGs || [],
documentationStyle:
userNode.properties.documentationStyle || "comprehensive",
expertiseLevel: userNode.properties.expertiseLevel || "intermediate",
preferredTechnologies: userNode.properties.preferredTechnologies || [],
preferredDiataxisCategories:
userNode.properties.preferredDiataxisCategories || [],
autoApplyPreferences:
userNode.properties.autoApplyPreferences !== false,
lastUpdated: userNode.properties.lastActive || new Date().toISOString(),
};
} else {
// Create default preferences
this.preferences = {
userId: this.userId,
preferredSSGs: [],
documentationStyle: "comprehensive",
expertiseLevel: "intermediate",
preferredTechnologies: [],
preferredDiataxisCategories: [],
autoApplyPreferences: true,
lastUpdated: new Date().toISOString(),
};
// Store in knowledge graph
await this.save();
}
}
/**
* Get current user preferences
*/
async getPreferences(): Promise<UserPreferences> {
if (!this.preferences) {
await this.initialize();
}
return this.preferences!;
}
/**
* Update user preferences
*/
async updatePreferences(
updates: Partial<Omit<UserPreferences, "userId" | "lastUpdated">>,
): Promise<UserPreferences> {
if (!this.preferences) {
await this.initialize();
}
this.preferences = {
...this.preferences!,
...updates,
lastUpdated: new Date().toISOString(),
};
await this.save();
return this.preferences;
}
/**
* Track SSG usage and infer preferences
*/
async trackSSGUsage(event: SSGUsageEvent): Promise<void> {
if (!this.preferences) {
await this.initialize();
}
const kg = await getKnowledgeGraph();
// Find user node
const userNodeId = `user:${this.userId}`;
let userNode = await kg.findNode({
type: "user",
properties: { userId: this.userId },
});
if (!userNode) {
userNode = kg.addNode({
id: userNodeId,
type: "user",
label: this.userId,
properties: {
userId: this.userId,
expertiseLevel: this.preferences!.expertiseLevel,
preferredSSGs: [],
preferredTechnologies: [],
documentationStyle: this.preferences!.documentationStyle,
projectCount: 0,
lastActive: new Date().toISOString(),
createdAt: new Date().toISOString(),
},
weight: 1.0,
});
}
// Find or create configuration node
const configNodeId = `configuration:${event.ssg}`;
let configNode = await kg.findNode({
type: "configuration",
properties: { ssg: event.ssg },
});
if (!configNode) {
configNode = kg.addNode({
id: configNodeId,
type: "configuration",
label: `${event.ssg} configuration`,
properties: {
ssg: event.ssg,
settings: {},
deploymentSuccessRate: event.success ? 1.0 : 0.0,
usageCount: 1,
lastUsed: event.timestamp,
},
weight: 1.0,
});
}
// Create or update preference relationship
const existingEdges = await kg.findEdges({
source: userNode.id,
target: configNode.id,
type: "user_prefers_ssg",
});
if (existingEdges.length > 0) {
// Update existing preference
const edge = existingEdges[0];
const currentCount = edge.properties.usageCount || 1;
const currentRate = edge.properties.successRate || 0.5;
edge.properties.usageCount = currentCount + 1;
edge.properties.successRate =
(currentRate * currentCount + (event.success ? 1.0 : 0.0)) /
(currentCount + 1);
edge.properties.lastUsed = event.timestamp;
edge.weight = edge.properties.successRate;
} else {
// Create new preference relationship
kg.addEdge({
source: userNode.id,
target: configNode.id,
type: "user_prefers_ssg",
weight: event.success ? 1.0 : 0.5,
confidence: 1.0,
properties: {
usageCount: 1,
lastUsed: event.timestamp,
successRate: event.success ? 1.0 : 0.0,
},
});
}
// Update user's preferred SSGs list based on success rate
await this.inferPreferredSSGs();
await saveKnowledgeGraph();
}
/**
* Infer preferred SSGs from usage history
*/
private async inferPreferredSSGs(): Promise<void> {
if (!this.preferences) {
await this.initialize();
}
const kg = await getKnowledgeGraph();
// Find user node
const userNode = await kg.findNode({
type: "user",
properties: { userId: this.userId },
});
if (!userNode) return;
// Get all SSG preference edges
const preferenceEdges = await kg.findEdges({
source: userNode.id,
type: "user_prefers_ssg",
});
// Calculate preference scores (usage count * success rate)
const ssgScores = new Map<string, number>();
for (const edge of preferenceEdges) {
const configNode = (await kg.getAllNodes()).find(
(n) => n.id === edge.target,
);
if (configNode && configNode.type === "configuration") {
const ssg = configNode.properties.ssg;
const usageCount = edge.properties.usageCount || 1;
const successRate = edge.properties.successRate || 0.5;
// Score = usage frequency * success rate
const score = usageCount * successRate;
ssgScores.set(ssg, score);
}
}
// Sort by score and take top 3
const topSSGs = Array.from(ssgScores.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 3)
.map(([ssg]) => ssg);
// Update preferences
this.preferences!.preferredSSGs = topSSGs;
this.preferences!.lastUpdated = new Date().toISOString();
// Update user node
userNode.properties.preferredSSGs = topSSGs;
}
/**
* Get SSG recommendations based on user preferences
*/
async getSSGRecommendations(): Promise<
Array<{ ssg: string; score: number; reason: string }>
> {
if (!this.preferences) {
await this.initialize();
}
const kg = await getKnowledgeGraph();
// Find user node
const userNode = await kg.findNode({
type: "user",
properties: { userId: this.userId },
});
if (!userNode) {
return [];
}
// Get all SSG preference edges
const preferenceEdges = await kg.findEdges({
source: userNode.id,
type: "user_prefers_ssg",
});
const recommendations: Array<{
ssg: string;
score: number;
reason: string;
}> = [];
for (const edge of preferenceEdges) {
const configNode = (await kg.getAllNodes()).find(
(n) => n.id === edge.target,
);
if (configNode && configNode.type === "configuration") {
const ssg = configNode.properties.ssg;
const usageCount = edge.properties.usageCount || 1;
const successRate = edge.properties.successRate || 0.5;
// Calculate recommendation score
const score = usageCount * successRate;
let reason = `Used ${usageCount} time(s)`;
if (successRate >= 0.8) {
reason += `, ${(successRate * 100).toFixed(0)}% success rate`;
} else if (successRate < 0.5) {
reason += `, only ${(successRate * 100).toFixed(0)}% success rate`;
}
recommendations.push({ ssg, score, reason });
}
}
return recommendations.sort((a, b) => b.score - a.score);
}
/**
* Apply user preferences to a recommendation
*/
applyPreferencesToRecommendation(
recommendation: string,
alternatives: string[],
): { recommended: string; adjustmentReason?: string } {
if (!this.preferences || !this.preferences.autoApplyPreferences) {
return { recommended: recommendation };
}
// Check if user has a strong preference
const preferredSSGs = this.preferences.preferredSSGs;
if (preferredSSGs.length > 0) {
// If recommended SSG is already in preferences, keep it
if (preferredSSGs.includes(recommendation)) {
return {
recommended: recommendation,
adjustmentReason: "Matches your preferred SSG",
};
}
// Check if any preferred SSG is in alternatives
for (const preferred of preferredSSGs) {
if (alternatives.includes(preferred)) {
return {
recommended: preferred,
adjustmentReason: `Switched to ${preferred} based on your usage history`,
};
}
}
}
return { recommended: recommendation };
}
/**
* Save preferences to knowledge graph
*/
private async save(): Promise<void> {
if (!this.preferences) return;
const kg = await getKnowledgeGraph();
const userNodeId = `user:${this.userId}`;
const existingNode = await kg.findNode({
type: "user",
properties: { userId: this.userId },
});
const userNode: GraphNode = {
id: existingNode?.id || userNodeId,
type: "user",
label: this.userId,
properties: {
userId: this.preferences.userId,
expertiseLevel: this.preferences.expertiseLevel,
preferredSSGs: this.preferences.preferredSSGs,
preferredTechnologies: this.preferences.preferredTechnologies,
documentationStyle: this.preferences.documentationStyle,
preferredDiataxisCategories:
this.preferences.preferredDiataxisCategories,
autoApplyPreferences: this.preferences.autoApplyPreferences,
projectCount: existingNode?.properties.projectCount || 0,
lastActive: this.preferences.lastUpdated,
createdAt:
existingNode?.properties.createdAt || this.preferences.lastUpdated,
},
weight: 1.0,
lastUpdated: this.preferences.lastUpdated,
};
kg.addNode(userNode);
await saveKnowledgeGraph();
}
/**
* Reset preferences to defaults
*/
async resetPreferences(): Promise<UserPreferences> {
this.preferences = {
userId: this.userId,
preferredSSGs: [],
documentationStyle: "comprehensive",
expertiseLevel: "intermediate",
preferredTechnologies: [],
preferredDiataxisCategories: [],
autoApplyPreferences: true,
lastUpdated: new Date().toISOString(),
};
await this.save();
return this.preferences;
}
/**
* Export preferences as JSON
*/
async exportPreferences(): Promise<string> {
if (!this.preferences) {
await this.initialize();
}
return JSON.stringify(this.preferences, null, 2);
}
/**
* Import preferences from JSON
*/
async importPreferences(json: string): Promise<UserPreferences> {
const imported = JSON.parse(json) as UserPreferences;
// Validate userId matches
if (imported.userId !== this.userId) {
throw new Error(
`User ID mismatch: expected ${this.userId}, got ${imported.userId}`,
);
}
this.preferences = {
...imported,
lastUpdated: new Date().toISOString(),
};
await this.save();
return this.preferences;
}
}
/**
* Get or create a user preference manager instance
*/
const userPreferenceManagers = new Map<string, UserPreferenceManager>();
export async function getUserPreferenceManager(
userId: string = "default",
): Promise<UserPreferenceManager> {
if (!userPreferenceManagers.has(userId)) {
const manager = new UserPreferenceManager(userId);
await manager.initialize();
userPreferenceManagers.set(userId, manager);
}
return userPreferenceManagers.get(userId)!;
}
/**
* Clear all cached preference managers (for testing)
*/
export function clearPreferenceManagerCache(): void {
userPreferenceManagers.clear();
}
```
--------------------------------------------------------------------------------
/src/workflows/documentation-workflow.ts:
--------------------------------------------------------------------------------
```typescript
// Documentation Workflow Guide for LLMs using DocuMCP MCP Server
export interface WorkflowStep {
tool: string;
description: string;
requiredInputs: string[];
outputs: string[];
optional: boolean;
alternatives?: string[];
}
export interface DocumentationWorkflow {
name: string;
description: string;
useCase: string;
steps: WorkflowStep[];
estimatedTime: string;
complexity: "simple" | "moderate" | "complex";
}
// Primary Documentation Creation Workflows
export const DOCUMENTATION_WORKFLOWS: Record<string, DocumentationWorkflow> = {
// Complete end-to-end documentation setup
"full-documentation-setup": {
name: "Complete Documentation Setup",
description:
"Analyze repository, recommend SSG, create structure, populate content, and deploy",
useCase:
"Starting from scratch with a new project that needs comprehensive documentation",
complexity: "complex",
estimatedTime: "5-10 minutes",
steps: [
{
tool: "analyze_repository",
description:
"Analyze the repository structure, dependencies, and documentation needs",
requiredInputs: ["path"],
outputs: ["analysisId", "projectMetadata", "technologyStack"],
optional: false,
},
{
tool: "recommend_ssg",
description:
"Get intelligent SSG recommendation based on project analysis",
requiredInputs: ["analysisId"],
outputs: ["recommendedSSG", "justification", "alternatives"],
optional: false,
},
{
tool: "generate_config",
description: "Generate configuration files for the recommended SSG",
requiredInputs: ["ssg", "projectName", "outputPath"],
outputs: ["configFiles", "setupInstructions"],
optional: false,
},
{
tool: "setup_structure",
description: "Create Diataxis-compliant documentation structure",
requiredInputs: ["path", "ssg"],
outputs: ["directoryStructure", "templateFiles"],
optional: false,
},
{
tool: "populate_diataxis_content",
description: "Intelligently populate content based on project analysis",
requiredInputs: ["analysisId", "docsPath"],
outputs: ["generatedContent", "populationMetrics"],
optional: false,
},
{
tool: "validate_diataxis_content",
description: "Validate generated content for accuracy and compliance",
requiredInputs: ["contentPath"],
outputs: ["validationResults", "recommendations"],
optional: true,
},
{
tool: "deploy_pages",
description: "Set up GitHub Pages deployment workflow",
requiredInputs: ["repository", "ssg"],
outputs: ["deploymentWorkflow", "deploymentInstructions"],
optional: true,
},
],
},
// Quick documentation setup for existing projects
"quick-documentation-setup": {
name: "Quick Documentation Setup",
description: "Rapid documentation creation using intelligent defaults",
useCase:
"Existing project needs documentation quickly with minimal customization",
complexity: "simple",
estimatedTime: "2-3 minutes",
steps: [
{
tool: "analyze_repository",
description: "Quick analysis of repository",
requiredInputs: ["path"],
outputs: ["analysisId"],
optional: false,
},
{
tool: "setup_structure",
description:
"Create documentation structure with intelligent SSG selection",
requiredInputs: ["path"],
outputs: ["directoryStructure"],
optional: false,
alternatives: ["Use analyze_repository output to auto-select SSG"],
},
{
tool: "populate_diataxis_content",
description: "Auto-populate with project-specific content",
requiredInputs: ["analysisId", "docsPath"],
outputs: ["generatedContent"],
optional: false,
},
],
},
// Content-focused workflow for existing documentation
"enhance-existing-documentation": {
name: "Enhance Existing Documentation",
description: "Improve and populate existing documentation structure",
useCase:
"Project already has basic documentation that needs content and validation",
complexity: "moderate",
estimatedTime: "3-5 minutes",
steps: [
{
tool: "analyze_repository",
description: "Analyze repository and existing documentation",
requiredInputs: ["path"],
outputs: ["analysisId", "existingDocsAnalysis"],
optional: false,
},
{
tool: "validate_diataxis_content",
description: "Validate existing content and identify gaps",
requiredInputs: ["contentPath", "analysisId"],
outputs: ["validationResults", "contentGaps"],
optional: false,
},
{
tool: "populate_diataxis_content",
description: "Fill content gaps with intelligent population",
requiredInputs: ["analysisId", "docsPath", "preserveExisting: true"],
outputs: ["enhancedContent"],
optional: false,
},
{
tool: "validate_diataxis_content",
description: "Final validation of enhanced content",
requiredInputs: ["contentPath"],
outputs: ["finalValidation"],
optional: true,
},
],
},
// Deployment-focused workflow
"deployment-only": {
name: "Documentation Deployment Setup",
description: "Set up deployment for existing documentation",
useCase: "Documentation exists but needs automated deployment setup",
complexity: "simple",
estimatedTime: "1-2 minutes",
steps: [
{
tool: "analyze_repository",
description:
"Analyze repository structure for deployment configuration",
requiredInputs: ["path"],
outputs: ["deploymentContext"],
optional: true,
},
{
tool: "deploy_pages",
description: "Set up GitHub Pages deployment workflow",
requiredInputs: ["repository", "ssg"],
outputs: ["deploymentWorkflow"],
optional: false,
},
{
tool: "verify_deployment",
description: "Verify deployment configuration and test",
requiredInputs: ["repository"],
outputs: ["deploymentStatus", "troubleshootingInfo"],
optional: true,
},
],
},
// Validation and quality assurance workflow
"documentation-audit": {
name: "Documentation Quality Audit",
description: "Comprehensive validation and quality assessment",
useCase:
"Existing documentation needs quality assessment and improvement recommendations",
complexity: "moderate",
estimatedTime: "2-3 minutes",
steps: [
{
tool: "analyze_repository",
description: "Analyze repository for context-aware validation",
requiredInputs: ["path"],
outputs: ["analysisId", "projectContext"],
optional: false,
},
{
tool: "validate_diataxis_content",
description: "Comprehensive content validation with all checks",
requiredInputs: [
"contentPath",
"analysisId",
"validationType: all",
"confidence: strict",
],
outputs: ["detailedValidation", "improvementPlan"],
optional: false,
},
{
tool: "verify_deployment",
description: "Validate deployment if applicable",
requiredInputs: ["repository"],
outputs: ["deploymentHealth"],
optional: true,
},
],
},
};
// Workflow Decision Helper
export interface WorkflowRecommendation {
recommendedWorkflow: string;
reason: string;
alternativeWorkflows: string[];
customizationSuggestions: string[];
}
export function recommendWorkflow(
projectStatus:
| "new"
| "existing-no-docs"
| "existing-basic-docs"
| "existing-full-docs",
requirements: {
needsDeployment?: boolean;
timeConstraint?: "minimal" | "moderate" | "comprehensive";
qualityFocus?: boolean;
customization?: "minimal" | "moderate" | "high";
},
): WorkflowRecommendation {
// New project or no documentation
if (projectStatus === "new" || projectStatus === "existing-no-docs") {
if (requirements.timeConstraint === "minimal") {
return {
recommendedWorkflow: "quick-documentation-setup",
reason:
"Fast setup with intelligent defaults for time-constrained scenario",
alternativeWorkflows: ["full-documentation-setup"],
customizationSuggestions: [
"Consider full-documentation-setup if time allows for better customization",
"Add deployment-only workflow later if needed",
],
};
} else {
return {
recommendedWorkflow: "full-documentation-setup",
reason:
"Comprehensive setup provides best foundation for new documentation",
alternativeWorkflows: ["quick-documentation-setup"],
customizationSuggestions: [
"Skip deploy_pages step if deployment not needed immediately",
"Use validate_diataxis_content for quality assurance",
],
};
}
}
// Existing documentation that needs enhancement
if (projectStatus === "existing-basic-docs") {
if (requirements.qualityFocus) {
return {
recommendedWorkflow: "documentation-audit",
reason: "Quality-focused validation and improvement recommendations",
alternativeWorkflows: ["enhance-existing-documentation"],
customizationSuggestions: [
"Follow up with enhance-existing-documentation based on audit results",
"Consider deployment-only if deployment setup is needed",
],
};
} else {
return {
recommendedWorkflow: "enhance-existing-documentation",
reason: "Improve and expand existing documentation content",
alternativeWorkflows: ["documentation-audit"],
customizationSuggestions: [
"Run documentation-audit first if quality is uncertain",
"Set preserveExisting: true in populate_diataxis_content",
],
};
}
}
// Full documentation that needs deployment or validation
if (projectStatus === "existing-full-docs") {
if (requirements.needsDeployment) {
return {
recommendedWorkflow: "deployment-only",
reason: "Focus on deployment setup for complete documentation",
alternativeWorkflows: ["documentation-audit"],
customizationSuggestions: [
"Run documentation-audit if quality validation is also needed",
"Consider verify_deployment for troubleshooting",
],
};
} else {
return {
recommendedWorkflow: "documentation-audit",
reason:
"Quality assurance and validation for existing complete documentation",
alternativeWorkflows: ["deployment-only"],
customizationSuggestions: [
"Focus on validation aspects most relevant to your concerns",
"Use strict confidence level for thorough quality checking",
],
};
}
}
// Fallback
return {
recommendedWorkflow: "full-documentation-setup",
reason: "Default comprehensive workflow for unclear requirements",
alternativeWorkflows: ["quick-documentation-setup", "documentation-audit"],
customizationSuggestions: [
"Analyze your specific needs to choose a more targeted workflow",
"Consider running analyze_repository first to understand your project better",
],
};
}
// Workflow execution guidance for LLMs
export const WORKFLOW_EXECUTION_GUIDANCE = {
description: "How LLMs should execute DocuMCP workflows",
principles: [
"Each tool can be called independently - workflows are guidance, not rigid requirements",
"Always check tool outputs before proceeding to next step",
"Adapt workflows based on user feedback and tool results",
"Skip optional steps if user has time constraints or different priorities",
"Use tool outputs (like analysisId) as inputs for subsequent tools",
"Validate critical assumptions before proceeding with deployment steps",
],
errorHandling: [
"If a tool fails, provide clear error information to user",
"Suggest alternative approaches or simplified workflows",
"Don't abandon the entire workflow - adapt and continue with available information",
"Use verify_deployment and validate_diataxis_content for troubleshooting",
],
customization: [
"Ask users about their priorities (speed vs quality, deployment needs, etc.)",
"Adapt tool parameters based on project analysis results",
"Suggest workflow modifications based on repository characteristics",
"Allow users to skip or modify steps based on their specific needs",
],
};
// Export workflow metadata for MCP resource exposure
export const WORKFLOW_METADATA = {
totalWorkflows: Object.keys(DOCUMENTATION_WORKFLOWS).length,
workflowNames: Object.keys(DOCUMENTATION_WORKFLOWS),
complexityLevels: ["simple", "moderate", "complex"],
estimatedTimeRange: "1-10 minutes depending on workflow and project size",
toolsUsed: [
"analyze_repository",
"recommend_ssg",
"generate_config",
"setup_structure",
"populate_diataxis_content",
"validate_diataxis_content",
"deploy_pages",
"verify_deployment",
],
};
```
--------------------------------------------------------------------------------
/docs/tutorials/environment-setup.md:
--------------------------------------------------------------------------------
```markdown
---
documcp:
last_updated: "2025-11-20T00:46:21.971Z"
last_validated: "2025-11-20T00:46:21.971Z"
auto_updated: false
update_frequency: monthly
---
# DocuMCP Environment Setup Guide
This guide will help you set up DocuMCP in your own environment, from basic installation to advanced configuration for team collaboration.
## 🚀 Quick Setup
### Prerequisites Check
Before installing DocuMCP, ensure you have the required software:
```bash
# Check Node.js version (requires 20.0.0+)
node --version
# Check npm version (requires 8.0.0+)
npm --version
# Check Git version
git --version
# Check GitHub CLI (optional but recommended)
gh --version
```
### Installation Methods
#### Method 1: Global Installation (Recommended)
```bash
# Install DocuMCP globally
npm install -g documcp
# Verify installation
documcp --version
# Should output: DocuMCP v0.5.0
```
#### Method 2: Local Project Installation
```bash
# Navigate to your project directory
cd /path/to/your/project
# Install DocuMCP as a dev dependency
npm install documcp --save-dev
# Add to package.json scripts
npm pkg set scripts.docs="documcp"
npm pkg set scripts.docs:analyze="documcp analyze-repository --path ."
npm pkg set scripts.docs:deploy="documcp deploy-pages --repository $(git remote get-url origin | sed 's/.*github.com[:/]\([^.]*\).*/\1/')"
```
#### Method 3: Docker Installation
```bash
# Pull the official DocuMCP Docker image
docker pull documcp/documcp:latest
# Run DocuMCP in a container
docker run -it --rm -v $(pwd):/workspace documcp/documcp:latest
```
## 🔧 Basic Configuration
### Environment Variables
Create a `.env` file in your project root:
```bash
# DocuMCP Configuration
export DOCUMCP_STORAGE_DIR="./.documcp"
export DOCUMCP_LOG_LEVEL="info"
export DOCUMCP_CACHE_ENABLED="true"
# GitHub Integration
export GITHUB_TOKEN="your_github_token_here"
export GITHUB_USERNAME="your_username"
# Optional: Custom configuration
export DOCUMCP_DEFAULT_SSG="docusaurus"
export DOCUMCP_DEFAULT_DEPTH="standard"
```
### Configuration File
Create a `documcp.config.json` file:
```json
{
"storage": {
"directory": "./.documcp",
"enableCache": true,
"maxCacheSize": "100MB"
},
"github": {
"token": "${GITHUB_TOKEN}",
"username": "${GITHUB_USERNAME}",
"defaultBranch": "main"
},
"defaults": {
"ssg": "docusaurus",
"analysisDepth": "standard",
"includeExamples": true,
"targetAudience": "community_contributors"
},
"memory": {
"enableLearning": true,
"retentionDays": 90,
"enableAnalytics": true
}
}
```
## 🏗️ Project Structure Setup
### Recommended Project Structure
```
your-project/
├── .documcp/ # DocuMCP storage and cache
│ ├── memory/ # Memory system data
│ ├── cache/ # Analysis cache
│ └── config/ # Local configuration
├── docs/ # Generated documentation
│ ├── api/ # API documentation
│ ├── tutorials/ # Tutorial content
│ ├── how-to/ # How-to guides
│ ├── reference/ # Reference documentation
│ └── explanation/ # Explanatory content
├── src/ # Source code
├── README.md # Project README
├── documcp.config.json # DocuMCP configuration
├── .env # Environment variables
└── package.json # Node.js dependencies
```
### Initialize DocuMCP in Your Project
```bash
# Initialize DocuMCP in your project
documcp init
# This creates:
# - .documcp/ directory
# - documcp.config.json
# - .env template
# - .gitignore entries
```
## 🔐 GitHub Integration Setup
### GitHub Token Setup
1. **Create a GitHub Personal Access Token:**
Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
Required permissions:
- `repo` (Full control of private repositories)
- `pages` (Write access to GitHub Pages)
- `workflow` (Update GitHub Action workflows)
- `read:org` (Read organization membership)
2. **Set the token in your environment:**
```bash
# Add to your shell profile (.bashrc, .zshrc, etc.)
export GITHUB_TOKEN="ghp_your_token_here"
# Or add to .env file
echo "GITHUB_TOKEN=ghp_your_token_here" >> .env
```
3. **Verify GitHub integration:**
```bash
# Test GitHub connection
documcp github test
# Should output: ✅ GitHub connection successful
```
### GitHub Pages Setup
1. **Enable GitHub Pages in your repository:**
Go to your repository → Settings → Pages
- Source: GitHub Actions
- Branch: main (or your preferred branch)
2. **Configure deployment:**
```bash
# Configure GitHub Pages deployment
documcp github configure-pages --repository "username/repository"
```
## 🧠 Memory System Setup
### Initialize Memory System
```bash
# Initialize memory system with custom storage
documcp memory init --storage-dir ./.documcp/memory
# Initialize with specific configuration
documcp memory init --storage-dir ./.documcp/memory --enable-learning --retention-days 90
```
### Memory System Configuration
Create a memory configuration file:
```json
{
"storage": {
"directory": "./.documcp/memory",
"enableCompression": true,
"maxSize": "500MB"
},
"learning": {
"enabled": true,
"retentionDays": 90,
"enableAnalytics": true,
"enablePatternRecognition": true
},
"userPreferences": {
"enablePersonalization": true,
"defaultUserId": "developer123"
}
}
```
### Memory System Testing
```bash
# Test memory system
documcp memory test
# Check memory statistics
documcp memory stats
# Export memories for backup
documcp memory export --format json --output ./documcp-memories-backup.json
```
## 🔧 Advanced Configuration
### Custom SSG Configuration
```bash
# Configure custom SSG settings
documcp config set --key "ssg.docusaurus.theme" --value "classic"
documcp config set --key "ssg.hugo.baseURL" --value "https://docs.example.com"
documcp config set --key "ssg.mkdocs.theme" --value "material"
```
### User Preferences Setup
```bash
# Set user preferences
documcp preferences set --user-id "developer123" --priority performance --ecosystem javascript
# Set team preferences
documcp preferences set --user-id "team" --priority simplicity --ecosystem any
# Export preferences
documcp preferences export --user-id "developer123" --output ./preferences.json
```
### Cache Configuration
```bash
# Configure caching
documcp cache config --enable --max-size "200MB" --ttl "24h"
# Clear cache
documcp cache clear
# Cache statistics
documcp cache stats
```
## 🐳 Docker Setup
### Docker Compose Configuration
Create a `docker-compose.yml` file:
```yaml
version: "3.8"
services:
documcp:
image: documcp/documcp:latest
container_name: documcp
volumes:
- ./:/workspace
- ./documcp-data:/app/.documcp
environment:
- GITHUB_TOKEN=${GITHUB_TOKEN}
- DOCUMCP_STORAGE_DIR=/app/.documcp
working_dir: /workspace
command: ["documcp", "serve", "--port", "3000", "--host", "0.0.0.0"]
ports:
- "3000:3000"
```
### Docker Usage
```bash
# Start DocuMCP with Docker Compose
docker-compose up -d
# Run specific commands
docker-compose exec documcp documcp analyze-repository --path .
# Stop services
docker-compose down
```
## 🔄 CI/CD Integration
### GitHub Actions Setup
Create `.github/workflows/docs.yml`:
```yaml
name: Documentation Deployment
on:
push:
branches: [main]
paths: ["docs/**", "src/**", "README.md"]
pull_request:
branches: [main]
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install DocuMCP
run: npm install -g documcp
- name: Analyze Repository
run: |
ANALYSIS_ID=$(documcp analyze-repository --path . --depth standard | jq -r '.data.id')
echo "analysis_id=$ANALYSIS_ID" >> $GITHUB_OUTPUT
id: analyze
- name: Validate Documentation
run: |
documcp validate-content --docs-path ./docs
deploy:
needs: analyze
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install DocuMCP
run: npm install -g documcp
- name: Deploy Documentation
run: |
documcp deploy-pages --repository ${{ github.repository }} --ssg docusaurus
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
### GitLab CI Setup
Create `.gitlab-ci.yml`:
```yaml
stages:
- analyze
- deploy
variables:
NODE_VERSION: "20"
analyze:
stage: analyze
image: node:${NODE_VERSION}-alpine
before_script:
- npm install -g documcp
script:
- documcp analyze-repository --path . --depth standard
- documcp validate-content --docs-path ./docs
artifacts:
reports:
junit: documcp-results.xml
paths:
- .documcp/
expire_in: 1 hour
deploy:
stage: deploy
image: node:${NODE_VERSION}-alpine
before_script:
- npm install -g documcp
script:
- documcp deploy-pages --repository $CI_PROJECT_PATH --ssg docusaurus
only:
- main
environment:
name: production
url: https://$CI_PROJECT_NAMESPACE.gitlab.io/$CI_PROJECT_NAME
```
## 🔍 Development Setup
### Local Development
```bash
# Clone DocuMCP repository
git clone https://github.com/tosin2013/documcp.git
cd documcp
# Install dependencies
npm install
# Build the project
npm run build
# Run in development mode
npm run dev
# Run tests
npm test
# Run linting
npm run lint
```
### IDE Configuration
#### VS Code Configuration
Create `.vscode/settings.json`:
```json
{
"typescript.preferences.importModuleSpecifier": "relative",
"typescript.suggest.autoImports": true,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"files.associations": {
"*.mcp": "json"
}
}
```
#### VS Code Extensions
Recommended extensions:
- TypeScript and JavaScript Language Features
- ESLint
- Prettier
- GitLens
- REST Client (for testing API endpoints)
## 🧪 Testing Setup
### Unit Testing
```bash
# Install testing dependencies
npm install --save-dev jest @types/jest ts-jest
# Create jest.config.js
cat > jest.config.js << EOF
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/__tests__/**/*.test.ts'],
collectCoverage: true,
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov', 'html']
};
EOF
# Run tests
npm test
```
### Integration Testing
```bash
# Create test repository
mkdir test-repo
cd test-repo
git init
echo "# Test Repository" > README.md
git add README.md
git commit -m "Initial commit"
# Test DocuMCP with test repository
cd ..
documcp analyze-repository --path ./test-repo --depth quick
```
### Performance Testing
```bash
# Run performance benchmarks
documcp benchmark run --repository ./test-repo --iterations 10
# Check performance metrics
documcp benchmark current
```
## 🔧 Troubleshooting
### Common Issues
#### Issue 1: Permission Denied
**Problem:** `Permission denied: Cannot read directory`
**Solution:**
```bash
# Check permissions
ls -la /path/to/repository
# Fix permissions
chmod -R 755 /path/to/repository
# Or run with sudo (not recommended for production)
sudo documcp analyze-repository --path /path/to/repository
```
#### Issue 2: GitHub Token Invalid
**Problem:** `GitHub authentication failed`
**Solution:**
```bash
# Check token validity
curl -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/user
# Regenerate token with correct permissions
# Go to GitHub → Settings → Developer settings → Personal access tokens
# Update environment variable
export GITHUB_TOKEN="new_token_here"
```
#### Issue 3: Memory System Errors
**Problem:** `Memory system initialization failed`
**Solution:**
```bash
# Clear memory storage
rm -rf ./.documcp/memory
# Reinitialize memory system
documcp memory init --storage-dir ./.documcp/memory
# Check memory system status
documcp memory status
```
#### Issue 4: Build Failures
**Problem:** `Documentation build failed`
**Solution:**
```bash
# Check for syntax errors
documcp validate-content --docs-path ./docs
# Test local build
documcp test-local --docs-path ./docs --ssg docusaurus
# Check SSG configuration
cat ./docs/docusaurus.config.js
```
### Debug Mode
```bash
# Enable debug logging
export DOCUMCP_LOG_LEVEL="debug"
# Run with verbose output
documcp analyze-repository --path . --depth standard --verbose
# Check logs
tail -f ./.documcp/logs/documcp.log
```
### Health Check
```bash
# Run comprehensive health check
documcp health-check
# Should output:
# ✅ Node.js version: v20.0.0
# ✅ npm version: 8.0.0
# ✅ Git version: 2.30.0
# ✅ GitHub token: valid
# ✅ Memory system: healthy
# ✅ Cache system: healthy
```
## 📚 Next Steps
After completing the environment setup:
1. **Read the [User Onboarding Guide](./user-onboarding.md)** for usage patterns
2. **Explore [Usage Examples](../how-to/usage-examples.md)** for practical examples
3. **Check the [API Reference](/api/)** for complete function documentation
4. **Join the [GitHub Issues](https://github.com/tosin2013/documcp/issues)** for community support and feature requests
## 🆘 Getting Help
- **Documentation**: Check the comprehensive documentation
- **GitHub Issues**: Report bugs and request features
- **GitHub Discussions**: Ask questions and share ideas
- **Community**: Join the DocuMCP community for support
Your DocuMCP environment is now ready! 🎉
```
--------------------------------------------------------------------------------
/src/memory/freshness-kg-integration.ts:
--------------------------------------------------------------------------------
```typescript
/**
* Documentation Freshness Knowledge Graph Integration
*
* Provides functions for storing and retrieving documentation freshness
* tracking events in the Knowledge Graph for historical analysis and insights.
*/
import { getKnowledgeGraph, getKGStorage } from "./kg-integration.js";
import type {
DocumentationFreshnessEventEntity,
ProjectHasFreshnessEventRelationship,
} from "./schemas.js";
import type { FreshnessScanReport } from "../utils/freshness-tracker.js";
import crypto from "crypto";
/**
* Generate a unique ID for a freshness event
*/
function generateFreshnessEventId(
projectPath: string,
timestamp: string,
): string {
const hash = crypto
.createHash("sha256")
.update(`${projectPath}:${timestamp}`)
.digest("hex")
.substring(0, 16);
return `freshness_event:${hash}`;
}
/**
* Generate a project ID from project path
*/
function generateProjectId(projectPath: string): string {
const hash = crypto
.createHash("sha256")
.update(projectPath)
.digest("hex")
.substring(0, 16);
return `project:${hash}`;
}
/**
* Store a documentation freshness scan event in the Knowledge Graph
*/
export async function storeFreshnessEvent(
projectPath: string,
docsPath: string,
report: FreshnessScanReport,
eventType: "scan" | "validation" | "initialization" | "update" = "scan",
): Promise<string> {
const kg = await getKnowledgeGraph();
const storage = await getKGStorage();
const timestamp = new Date().toISOString();
const eventId = generateFreshnessEventId(projectPath, timestamp);
const projectId = generateProjectId(projectPath);
// Calculate average age in days
const filesWithAge = report.files.filter((f) => f.ageInMs !== undefined);
const averageAge =
filesWithAge.length > 0
? filesWithAge.reduce((sum, f) => sum + (f.ageInMs || 0), 0) /
filesWithAge.length /
(1000 * 60 * 60 * 24)
: undefined;
// Find oldest file
const oldestFile =
filesWithAge.length > 0
? filesWithAge.reduce((oldest, current) =>
(current.ageInMs || 0) > (oldest.ageInMs || 0) ? current : oldest,
)
: undefined;
// Get most stale files (critical and stale)
const mostStaleFiles = report.files
.filter(
(f) => f.stalenessLevel === "critical" || f.stalenessLevel === "stale",
)
.sort((a, b) => (b.ageInMs || 0) - (a.ageInMs || 0))
.slice(0, 10)
.map((f) => f.relativePath);
// Create freshness event entity
const freshnessEntity: DocumentationFreshnessEventEntity = {
docsPath,
projectPath,
scannedAt: report.scannedAt,
totalFiles: report.totalFiles,
freshFiles: report.freshFiles,
warningFiles: report.warningFiles,
staleFiles: report.staleFiles,
criticalFiles: report.criticalFiles,
filesWithoutMetadata: report.filesWithoutMetadata,
thresholds: report.thresholds,
averageAge,
oldestFile: oldestFile
? {
path: oldestFile.relativePath,
ageInDays: (oldestFile.ageInMs || 0) / (1000 * 60 * 60 * 24),
}
: undefined,
mostStaleFiles,
eventType,
};
// Add entity to knowledge graph
kg.addNode({
id: eventId,
type: "documentation_freshness_event",
label: `Freshness Event ${timestamp}`,
properties: freshnessEntity,
weight: 1.0,
});
// Check if project node exists via async findNode, if not, create a minimal one
const projectNode = await kg.findNode({
type: "project",
properties: { path: projectPath },
});
if (!projectNode) {
kg.addNode({
id: projectId,
type: "project",
label: projectPath.split("/").pop() || "Unknown Project",
properties: {
name: projectPath.split("/").pop() || "Unknown",
path: projectPath,
createdAt: timestamp,
},
weight: 1.0,
});
}
// Calculate improvement score (0-1, higher is better)
const improvementScore =
report.totalFiles > 0
? (report.freshFiles +
report.warningFiles * 0.7 +
report.staleFiles * 0.3) /
report.totalFiles
: 1.0;
// Create relationship between project and freshness event
const relationship: ProjectHasFreshnessEventRelationship = {
type: "project_has_freshness_event",
eventType,
filesScanned: report.totalFiles,
freshFiles: report.freshFiles,
staleFiles: report.staleFiles,
criticalFiles: report.criticalFiles,
filesInitialized: 0, // This will be updated by validation events
filesUpdated: 0, // This will be updated by update events
averageStaleness: averageAge,
improvementScore,
weight: 1.0,
confidence: 1.0,
createdAt: timestamp,
lastUpdated: timestamp,
metadata: {
docsPath,
thresholds: report.thresholds,
},
};
kg.addEdge({
source: projectId,
target: eventId,
type: "project_has_freshness_event",
weight: 1.0,
confidence: 1.0,
properties: relationship,
});
// Persist to storage
const nodes = await kg.getAllNodes();
const edges = await kg.getAllEdges();
await storage.saveGraph(nodes, edges);
return eventId;
}
/**
* Update a freshness event with validation/update results
*/
export async function updateFreshnessEvent(
eventId: string,
updates: {
filesInitialized?: number;
filesUpdated?: number;
eventType?: "scan" | "validation" | "initialization" | "update";
},
): Promise<void> {
const kg = await getKnowledgeGraph();
const storage = await getKGStorage();
// Find event node by ID
const eventNode = await kg.getNodeById(eventId);
if (!eventNode) {
throw new Error(`Freshness event not found: ${eventId}`);
}
// Update entity properties
if (updates.eventType) {
eventNode.properties.eventType = updates.eventType;
}
eventNode.lastUpdated = new Date().toISOString();
// Find and update the relationship
const relEdges = await kg.findEdges({
target: eventId,
type: "project_has_freshness_event",
});
for (const edge of relEdges) {
const props = edge.properties as ProjectHasFreshnessEventRelationship;
if (updates.filesInitialized !== undefined) {
props.filesInitialized = updates.filesInitialized;
}
if (updates.filesUpdated !== undefined) {
props.filesUpdated = updates.filesUpdated;
}
if (updates.eventType) {
props.eventType = updates.eventType;
}
edge.lastUpdated = new Date().toISOString();
}
// Persist to storage
const allNodes = await kg.getAllNodes();
const allEdges = await kg.getAllEdges();
await storage.saveGraph(allNodes, allEdges);
}
/**
* Get freshness event history for a project
*/
export async function getFreshnessHistory(
projectPath: string,
limit: number = 10,
): Promise<
Array<{
eventId: string;
event: DocumentationFreshnessEventEntity;
relationship: ProjectHasFreshnessEventRelationship;
}>
> {
const kg = await getKnowledgeGraph();
const projectId = generateProjectId(projectPath);
const edges = await kg.findEdges({
source: projectId,
type: "project_has_freshness_event",
});
// Sort by timestamp (most recent first)
const sorted = await Promise.all(
edges.map(async (edge) => {
const eventNode = await kg.getNodeById(edge.target);
if (!eventNode || eventNode.type !== "documentation_freshness_event") {
return null;
}
return {
eventId: edge.target,
event: eventNode.properties as DocumentationFreshnessEventEntity,
relationship: edge.properties as ProjectHasFreshnessEventRelationship,
timestamp: (eventNode.properties as DocumentationFreshnessEventEntity)
.scannedAt,
};
}),
);
const filtered = sorted
.filter((item): item is NonNullable<typeof item> => item !== null)
.sort(
(a, b) =>
new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),
)
.slice(0, limit);
return filtered.map(({ eventId, event, relationship }) => ({
eventId,
event,
relationship,
}));
}
/**
* Get staleness insights for a project
*/
export async function getStalenessInsights(projectPath: string): Promise<{
totalEvents: number;
averageImprovementScore: number;
trend: "improving" | "declining" | "stable";
currentStatus: {
freshFiles: number;
staleFiles: number;
criticalFiles: number;
totalFiles: number;
} | null;
recommendations: string[];
}> {
const history = await getFreshnessHistory(projectPath, 100);
if (history.length === 0) {
return {
totalEvents: 0,
averageImprovementScore: 0,
trend: "stable",
currentStatus: null,
recommendations: [
"No freshness tracking history found. Run track_documentation_freshness to begin monitoring.",
],
};
}
// Calculate average improvement score
const avgScore =
history.reduce(
(sum, h) => sum + (h.relationship.improvementScore || 0),
0,
) / history.length;
// Determine trend (compare first half to second half)
const midpoint = Math.floor(history.length / 2);
const recentScore =
history
.slice(0, midpoint)
.reduce((sum, h) => sum + (h.relationship.improvementScore || 0), 0) /
Math.max(midpoint, 1);
const olderScore =
history
.slice(midpoint)
.reduce((sum, h) => sum + (h.relationship.improvementScore || 0), 0) /
Math.max(history.length - midpoint, 1);
let trend: "improving" | "declining" | "stable";
if (recentScore > olderScore + 0.1) {
trend = "improving";
} else if (recentScore < olderScore - 0.1) {
trend = "declining";
} else {
trend = "stable";
}
// Get current status from most recent event
const latest = history[0];
const currentStatus = {
freshFiles: latest.event.freshFiles,
staleFiles: latest.event.staleFiles,
criticalFiles: latest.event.criticalFiles,
totalFiles: latest.event.totalFiles,
};
// Generate recommendations
const recommendations: string[] = [];
if (currentStatus.criticalFiles > 0) {
recommendations.push(
`🔴 ${currentStatus.criticalFiles} files are critically stale and need immediate attention`,
);
}
if (currentStatus.staleFiles > currentStatus.totalFiles * 0.3) {
recommendations.push(
`🟠 Over 30% of documentation is stale. Consider running validate_documentation_freshness`,
);
}
if (trend === "declining") {
recommendations.push(
"📉 Documentation freshness is declining. Review update processes and automation",
);
} else if (trend === "improving") {
recommendations.push(
"📈 Documentation freshness is improving. Keep up the good work!",
);
}
if (latest.event.filesWithoutMetadata > 0) {
recommendations.push(
`⚠️ ${latest.event.filesWithoutMetadata} files lack freshness metadata. Run validate_documentation_freshness with initializeMissing=true`,
);
}
// Analyze most commonly stale files
const allStaleFiles = history.flatMap((h) => h.event.mostStaleFiles);
const staleFileCounts = new Map<string, number>();
for (const file of allStaleFiles) {
staleFileCounts.set(file, (staleFileCounts.get(file) || 0) + 1);
}
const chronicallyStale = Array.from(staleFileCounts.entries())
.filter(([_, count]) => count >= Math.min(3, history.length * 0.5))
.map(([file]) => file);
if (chronicallyStale.length > 0) {
recommendations.push(
`🔄 ${
chronicallyStale.length
} files are chronically stale: ${chronicallyStale
.slice(0, 3)
.join(", ")}${chronicallyStale.length > 3 ? "..." : ""}`,
);
}
return {
totalEvents: history.length,
averageImprovementScore: avgScore,
trend,
currentStatus,
recommendations,
};
}
/**
* Compare freshness across similar projects
*/
export async function compareFreshnessAcrossProjects(
projectPath: string,
): Promise<{
currentProject: {
path: string;
improvementScore: number;
};
similarProjects: Array<{
path: string;
improvementScore: number;
similarity: number;
}>;
ranking: number; // 1-based ranking (1 = best)
}> {
const kg = await getKnowledgeGraph();
const projectId = generateProjectId(projectPath);
// Get current project's latest score
const history = await getFreshnessHistory(projectPath, 1);
const currentScore =
history.length > 0 ? history[0].relationship.improvementScore || 0 : 0;
// Find similar projects
const similarEdges = await kg.findEdges({
source: projectId,
type: "similar_to",
});
const similarProjectsPromises = similarEdges.map(async (edge) => {
const similarProjectNode = await kg.getNodeById(edge.target);
if (!similarProjectNode || similarProjectNode.type !== "project") {
return null;
}
const similarPath = (similarProjectNode.properties as any).path || "";
const similarHistory = await getFreshnessHistory(similarPath, 1);
const similarScore =
similarHistory.length > 0
? similarHistory[0].relationship.improvementScore || 0
: 0;
return {
path: similarPath,
improvementScore: similarScore,
similarity: (edge.properties as any).similarityScore || 0,
};
});
const similarProjects = await Promise.all(similarProjectsPromises);
const validSimilarProjects = similarProjects.filter(
(p): p is NonNullable<typeof p> => p !== null,
);
// Calculate ranking
const allScores = [
currentScore,
...validSimilarProjects.map((p) => p.improvementScore),
];
const sortedScores = [...allScores].sort((a, b) => b - a);
const ranking = sortedScores.indexOf(currentScore) + 1;
return {
currentProject: {
path: projectPath,
improvementScore: currentScore,
},
similarProjects: validSimilarProjects,
ranking,
};
}
```
--------------------------------------------------------------------------------
/docs/adrs/004-diataxis-framework-integration.md:
--------------------------------------------------------------------------------
```markdown
---
id: 004-diataxis-framework-integration
title: "ADR-004: Diataxis Framework Integration"
sidebar_label: "ADR-4: Diataxis Framework Integration"
sidebar_position: 4
documcp:
last_updated: "2025-11-20T00:46:21.938Z"
last_validated: "2025-11-20T00:46:21.938Z"
auto_updated: false
update_frequency: monthly
---
# ADR-004: Diataxis Framework Integration for Documentation Structure
## Status
Accepted
## Context
DocuMCP aims to improve the quality and effectiveness of technical documentation by implementing proven information architecture principles. The Diataxis framework provides a systematic approach to organizing technical documentation into four distinct categories that serve different user needs and learning contexts.
Diataxis Framework Components:
- **Tutorials**: Learning-oriented content for skill acquisition (study context)
- **How-to Guides**: Problem-solving oriented content 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)
Current documentation challenges:
- Most projects mix different content types without clear organization
- Users struggle to find appropriate content for their current needs
- Documentation often fails to serve different user contexts effectively
- Information architecture is typically ad-hoc and inconsistent
The framework addresses fundamental differences in user intent:
- **Study vs. Work**: Different contexts require different content approaches
- **Acquisition vs. Application**: Learning new skills vs. applying existing knowledge
- **Practical vs. Theoretical**: Task completion vs. understanding concepts
## Decision
We will integrate the Diataxis framework as the foundational information architecture for all DocuMCP-generated documentation structures, with intelligent content planning and navigation generation adapted to each static site generator's capabilities.
### Integration Approach:
#### 1. Automated Structure Generation
- **Directory organization** that clearly separates Diataxis content types
- **Navigation systems** that help users understand content categorization
- **Template generation** for each content type with appropriate guidance
- **Cross-reference systems** that maintain logical relationships between content types
#### 2. Content Type Templates
- **Tutorial templates** with learning objectives, prerequisites, step-by-step instructions
- **How-to guide templates** focused on problem-solution patterns
- **Reference templates** for systematic information organization
- **Explanation templates** for conceptual and architectural content
#### 3. Content Planning Intelligence
- **Automated content suggestions** based on project analysis
- **Gap identification** for missing content types
- **User journey mapping** to appropriate content categories
- **Content relationship mapping** to ensure comprehensive coverage
#### 4. SSG-Specific Implementation
- **Adaptation to SSG capabilities** while maintaining Diataxis principles
- **Theme and plugin recommendations** that support Diataxis organization
- **Navigation configuration** optimized for each SSG's features
## Alternatives Considered
### Generic Documentation Templates
- **Pros**: Simpler implementation, fewer constraints on content organization
- **Cons**: Perpetuates existing documentation quality problems, no systematic improvement
- **Decision**: Rejected due to missed opportunity for significant quality improvement
### Custom Documentation Framework
- **Pros**: Full control over documentation approach and features
- **Cons**: Reinventing proven methodology, reduced credibility, maintenance burden
- **Decision**: Rejected in favor of proven, established framework
### Multiple Framework Options
- **Pros**: Could accommodate different project preferences and approaches
- **Cons**: Choice paralysis, inconsistent quality, complex implementation
- **Decision**: Rejected to maintain focus and ensure consistent quality outcomes
### Optional Diataxis Integration
- **Pros**: Gives users choice, accommodates existing documentation structures
- **Cons**: Reduces value proposition, complicates implementation, inconsistent results
- **Decision**: Rejected to ensure consistent quality and educational value
## Consequences
### Positive
- **Improved Documentation Quality**: Systematic application of proven principles
- **Better User Experience**: Users can find appropriate content for their context
- **Educational Value**: Projects learn proper documentation organization
- **Consistency**: All DocuMCP projects benefit from same high-quality structure
- **Maintenance Benefits**: Clear content types simplify ongoing documentation work
### Negative
- **Learning Curve**: Teams need to understand Diataxis principles for optimal results
- **Initial Overhead**: More structure requires more initial planning and content creation
- **Rigidity**: Some projects might prefer different organizational approaches
### Risks and Mitigations
- **User Resistance**: Provide clear education about benefits and implementation guidance
- **Implementation Complexity**: Start with basic structure, enhance over time
- **Content Quality**: Provide high-quality templates and examples
## Implementation Details
### Directory Structure Generation
```typescript
interface DiataxisStructure {
tutorials: DirectoryConfig;
howToGuides: DirectoryConfig;
reference: DirectoryConfig;
explanation: DirectoryConfig;
navigation: NavigationConfig;
}
const DIATAXIS_TEMPLATES: Record<SSGType, DiataxisStructure> = {
hugo: {
tutorials: { path: "content/tutorials", layout: "tutorial" },
howToGuides: { path: "content/how-to", layout: "guide" },
reference: { path: "content/reference", layout: "reference" },
explanation: { path: "content/explanation", layout: "explanation" },
navigation: { menu: "diataxis", weight: "category-based" },
},
// ... other SSG configurations
};
```
### Content Template System
```typescript
interface ContentTemplate {
frontmatter: Record<string, any>;
structure: ContentSection[];
guidance: string[];
examples: string[];
}
const TUTORIAL_TEMPLATE: ContentTemplate = {
frontmatter: {
title: "{{ tutorial_title }}",
description: "{{ tutorial_description }}",
difficulty: "{{ difficulty_level }}",
prerequisites: "{{ prerequisites }}",
estimated_time: "{{ time_estimate }}",
},
structure: [
{ section: "learning_objectives", required: true },
{ section: "prerequisites", required: true },
{ section: "step_by_step_instructions", required: true },
{ section: "verification", required: true },
{ section: "next_steps", required: false },
],
guidance: [
"Focus on learning and skill acquisition",
"Provide complete, working examples",
"Include verification steps for each major milestone",
"Assume minimal prior knowledge",
],
};
```
### Content Planning Algorithm
```typescript
interface ContentPlan {
tutorials: TutorialSuggestion[];
howToGuides: HowToSuggestion[];
reference: ReferenceSuggestion[];
explanation: ExplanationSuggestion[];
}
function generateContentPlan(projectAnalysis: ProjectAnalysis): ContentPlan {
return {
tutorials: suggestTutorials(projectAnalysis),
howToGuides: suggestHowToGuides(projectAnalysis),
reference: suggestReference(projectAnalysis),
explanation: suggestExplanation(projectAnalysis),
};
}
function suggestTutorials(analysis: ProjectAnalysis): TutorialSuggestion[] {
const suggestions: TutorialSuggestion[] = [];
// Getting started tutorial (always recommended)
suggestions.push({
title: "Getting Started",
description: "First steps with {{ project_name }}",
priority: "high",
estimated_effort: "medium",
});
// Feature-specific tutorials based on project complexity
if (analysis.complexity.apiSurface > 5) {
suggestions.push({
title: "API Integration Tutorial",
description: "Complete guide to integrating with the API",
priority: "high",
estimated_effort: "large",
});
}
return suggestions;
}
```
### Navigation Generation
```typescript
interface DiataxisNavigation {
structure: NavigationItem[];
labels: NavigationLabels;
descriptions: CategoryDescriptions;
}
const NAVIGATION_STRUCTURE: DiataxisNavigation = {
structure: [
{
category: "tutorials",
label: "Tutorials",
description: "Learning-oriented guides",
icon: "graduation-cap",
order: 1,
},
{
category: "how-to",
label: "How-to Guides",
description: "Problem-solving recipes",
icon: "tools",
order: 2,
},
{
category: "reference",
label: "Reference",
description: "Technical information",
icon: "book",
order: 3,
},
{
category: "explanation",
label: "Explanation",
description: "Understanding and context",
icon: "lightbulb",
order: 4,
},
],
labels: {
tutorials: "Learn",
howToGuides: "Solve",
reference: "Lookup",
explanation: "Understand",
},
descriptions: {
tutorials: "Step-by-step learning paths",
howToGuides: "Solutions to specific problems",
reference: "Complete technical details",
explanation: "Background and concepts",
},
};
```
### SSG-Specific Adaptations
```typescript
interface SSGDiataxisAdapter {
generateStructure(
ssg: SSGType,
project: ProjectAnalysis,
): DiataxisImplementation;
createNavigation(
ssg: SSGType,
structure: DiataxisStructure,
): NavigationConfig;
generateTemplates(ssg: SSGType, contentTypes: ContentType[]): TemplateSet;
}
class HugoDiataxisAdapter implements SSGDiataxisAdapter {
generateStructure(
ssg: SSGType,
project: ProjectAnalysis,
): DiataxisImplementation {
return {
contentDirectories: this.createHugoContentStructure(),
frontmatterSchemas: this.createHugoFrontmatter(),
taxonomies: this.createDiataxisTaxonomies(),
menuConfiguration: this.createHugoMenus(),
};
}
createHugoContentStructure(): ContentStructure {
return {
"content/tutorials/": { weight: 10, section: "tutorials" },
"content/how-to/": { weight: 20, section: "guides" },
"content/reference/": { weight: 30, section: "reference" },
"content/explanation/": { weight: 40, section: "explanation" },
};
}
}
```
## Quality Assurance
### Diataxis Compliance Validation
```typescript
interface DiataxisValidator {
validateStructure(documentation: DocumentationStructure): ValidationResult;
checkContentTypeAlignment(
content: Content,
declaredType: ContentType,
): AlignmentResult;
identifyMissingCategories(structure: DocumentationStructure): Gap[];
}
function validateDiataxisCompliance(
docs: DocumentationStructure,
): ComplianceReport {
return {
structureCompliance: checkDirectoryOrganization(docs),
contentTypeAccuracy: validateContentCategorization(docs),
navigationClarity: assessNavigationEffectiveness(docs),
crossReferenceCompleteness: checkContentRelationships(docs),
};
}
```
### Content Quality Guidelines
- **Tutorial Content**: Must include learning objectives, prerequisites, and verification steps
- **How-to Content**: Must focus on specific problems with clear solution steps
- **Reference Content**: Must be comprehensive, accurate, and systematically organized
- **Explanation Content**: Must provide context, background, and conceptual understanding
### Testing Strategy
- **Structure Tests**: Validate directory organization and navigation generation
- **Template Tests**: Ensure all content type templates are properly formatted
- **Integration Tests**: Test complete Diataxis implementation across different SSGs
- **User Experience Tests**: Validate that users can effectively navigate and find content
## Educational Integration
### User Guidance
- **Diataxis Explanation**: Clear documentation of framework benefits and principles
- **Content Type Guidelines**: Detailed guidance for creating each type of content
- **Migration Assistance**: Help converting existing documentation to Diataxis structure
- **Best Practice Examples**: Templates and examples demonstrating effective implementation
### Community Building
- **Diataxis Advocacy**: Promote framework adoption across open-source community
- **Success Story Sharing**: Highlight projects benefiting from Diataxis implementation
- **Training Resources**: Develop educational materials for technical writers and maintainers
- **Feedback Collection**: Gather community input for framework implementation improvements
## Future Enhancements
### Advanced Features
- **Content Gap Analysis**: AI-powered identification of missing content areas
- **User Journey Optimization**: Intelligent linking between content types based on user flows
- **Content Quality Scoring**: Automated assessment of content quality within each category
- **Personalized Navigation**: Adaptive navigation based on user role and experience level
### Tool Integration
- **Analytics Integration**: Track how users navigate between different content types
- **Content Management**: Tools for maintaining Diataxis compliance over time
- **Translation Support**: Multi-language implementations of Diataxis structure
- **Accessibility Features**: Ensure Diataxis implementation supports accessibility standards
## References
- [Diataxis Framework Official Documentation](https://diataxis.fr/)
- [Information Architecture Principles](https://www.usability.gov/what-and-why/information-architecture.html)
- [Technical Writing Best Practices](https://developers.google.com/tech-writing)
```
--------------------------------------------------------------------------------
/docs/how-to/troubleshooting.md:
--------------------------------------------------------------------------------
```markdown
---
documcp:
last_updated: "2025-11-20T00:46:21.956Z"
last_validated: "2025-11-20T00:46:21.956Z"
auto_updated: false
update_frequency: monthly
---
# Troubleshooting Common Issues
This guide helps you diagnose and fix common problems when using DocuMCP for documentation deployment.
## Quick Diagnostic Commands
Use these DocuMCP prompts for immediate diagnosis:
```bash
# General troubleshooting
"diagnose issues with my documentation deployment"
# Specific verification
"verify my GitHub Pages deployment and identify any problems"
# Link validation
"check all my documentation links for broken references"
# Content validation
"validate my documentation content for errors and inconsistencies"
```
## Repository Analysis Issues
### Problem: Analysis Returns Empty or Incomplete Results
**Symptoms:**
- Analysis shows 0 files or minimal structure
- Missing language detection
- No dependency information
**Solutions:**
1. **Check directory permissions:**
```bash
ls -la /path/to/your/repository
# Ensure read permissions exist
```
2. **Verify Git repository:**
```bash
git status
# Must be in a valid Git repository
```
3. **Use deeper analysis:**
```bash
"analyze my repository with deep analysis to get comprehensive results"
```
4. **Check for hidden files:**
```bash
# Include hidden files in analysis
ls -la
# Look for .gitignore excluding important files
```
### Problem: Wrong Project Type Detection
**Symptoms:**
- Library detected as application
- Wrong primary language
- Incorrect team size estimation
**Solutions:**
1. **Provide more context:**
```bash
"analyze my TypeScript library project with focus on API documentation"
```
2. **Check file extensions:**
```bash
# Ensure your main files have correct extensions
find . -name "*.ts" -o -name "*.js" -o -name "*.py" | head -20
```
3. **Update package.json:**
```json
{
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"keywords": ["library", "typescript", "api"]
}
```
## Static Site Generator Recommendation Issues
### Problem: No Recommendations or Low Confidence Scores
**Symptoms:**
- Empty recommendation list
- All SSGs have similar low scores
- Recommendation doesn't match project needs
**Solutions:**
1. **Provide preferences:**
```bash
"recommend SSG for my project with preferences for JavaScript ecosystem and feature-rich capabilities"
```
2. **Re-analyze with specific focus:**
```bash
"analyze my repository focusing on documentation needs and complexity"
```
3. **Check project characteristics:**
- Ensure sufficient code files exist
- Verify dependencies are in package.json/requirements.txt
- Add README with project description
### Problem: Recommended SSG Doesn't Match Expectations
**Symptoms:**
- Hugo recommended for React project
- MkDocs suggested for JavaScript library
- Jekyll proposed for Python project
**Solutions:**
1. **Specify ecosystem preference:**
```bash
"recommend SSG for my project with JavaScript ecosystem preference"
```
2. **Review analysis results:**
```bash
"explain why you recommended Hugo instead of Docusaurus for my React project"
```
3. **Override with specific request:**
```bash
"generate Docusaurus configuration for my project despite the Hugo recommendation"
```
## Configuration Generation Issues
### Problem: Configuration Files Not Created
**Symptoms:**
- No config files generated
- Empty configuration
- Missing dependencies
**Solutions:**
1. **Check output path:**
```bash
# Ensure output path exists and is writable
mkdir -p ./docs
chmod 755 ./docs
```
2. **Specify absolute path:**
```bash
"generate Hugo configuration files at /full/path/to/project"
```
3. **Check project name format:**
```bash
# Avoid special characters in project names
"generate config for project 'My-Simple-Docs' not 'My Project (v2.0)'"
```
### Problem: Invalid Configuration Generated
**Symptoms:**
- Build fails with config errors
- Missing required fields
- Wrong file format
**Solutions:**
1. **Validate generated config:**
```bash
# For Docusaurus
npm run docusaurus --version
# For Hugo
hugo version && hugo config
# For MkDocs
mkdocs --version && mkdocs build --strict
```
2. **Regenerate with project details:**
```bash
"generate detailed Hugo configuration with custom theme and GitHub integration"
```
3. **Fix common issues:**
**Docusaurus baseUrl fix:**
```javascript
// Fix in docusaurus.config.js
const config = {
baseUrl: "/your-repo-name/", // Must match repository name
url: "https://yourusername.github.io",
};
```
**Hugo baseURL fix:**
```yaml
# Fix in config.yml
baseURL: "https://yourusername.github.io/your-repo-name/"
```
## Documentation Structure Issues
### Problem: Diataxis Structure Not Created
**Symptoms:**
- Missing directories
- Empty folders
- No example content
**Solutions:**
1. **Check path permissions:**
```bash
ls -ld /path/to/docs
# Ensure write permissions
```
2. **Use absolute path:**
```bash
"set up Diataxis structure at /absolute/path/to/docs"
```
3. **Force recreation:**
```bash
"recreate documentation structure with examples for my SSG"
```
### Problem: Content Population Fails
**Symptoms:**
- Empty documentation files
- Generic content only
- Missing project-specific information
**Solutions:**
1. **Provide analysis context:**
```bash
"populate documentation using analysis ID analysis_abc123 with comprehensive content"
```
2. **Specify technology focus:**
```bash
"populate docs focusing on TypeScript, React, and API documentation"
```
3. **Check source code structure:**
```bash
# Ensure code has discoverable patterns
find . -name "*.ts" -exec grep -l "export" {} \;
```
## GitHub Pages Deployment Issues
### Problem: Deployment Workflow Fails
**Symptoms:**
- GitHub Actions shows red X
- Build fails with errors
- Deployment never completes
**Solutions:**
1. **Check workflow logs:**
- Go to Actions tab in GitHub
- Click on failed workflow
- Review step-by-step logs
2. **Common fixes:**
**Node.js version mismatch:**
```yaml
# Fix in .github/workflows/deploy.yml
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20" # Match your local version
```
**Missing dependencies:**
```json
# Ensure all dependencies in package.json
{
"dependencies": {
"@docusaurus/core": "^3.0.0",
"@docusaurus/preset-classic": "^3.0.0"
}
}
```
**Build command issues:**
```yaml
# Fix build command
- name: Build
run: npm run build # Ensure this command exists in package.json
```
### Problem: Site Shows 404 Error
**Symptoms:**
- GitHub Pages URL returns 404
- Site deployed but not accessible
- Some pages work, others don't
**Solutions:**
1. **Check GitHub Pages settings:**
- Repository Settings > Pages
- Source should be "GitHub Actions"
- Custom domain configured correctly (if used)
2. **Fix baseURL configuration:**
**Docusaurus:**
```javascript
const config = {
baseUrl: "/repository-name/", // Must match your repo name exactly
url: "https://username.github.io",
};
```
**Hugo:**
```yaml
baseURL: "https://username.github.io/repository-name/"
```
**MkDocs:**
```yaml
site_url: "https://username.github.io/repository-name/"
```
3. **Check file naming:**
```bash
# Ensure index.html or index.md exists
ls docs/index.*
```
### Problem: Assets Not Loading (CSS/JS/Images)
**Symptoms:**
- Site loads but no styling
- Images show as broken
- JavaScript functionality missing
**Solutions:**
1. **Check asset paths:**
```javascript
// Use relative paths
<img src="./images/logo.png" /> // Good
<img src="/images/logo.png" /> // May fail
```
2. **Configure public path:**
**Docusaurus:**
```javascript
const config = {
baseUrl: "/repo-name/",
staticDirectories: ["static"],
};
```
**Hugo:**
```yaml
# In config.yml
baseURL: "https://username.github.io/repo-name/"
canonifyURLs: true
```
3. **Verify asset directories:**
```bash
# Check assets exist in build output
ls -la build/assets/ # Docusaurus
ls -la public/css/ # Hugo
ls -la site/css/ # MkDocs
```
## Content Validation Issues
### Problem: Link Validation Shows False Positives
**Symptoms:**
- Valid links reported as broken
- External links fail intermittently
- Anchor links not found
**Solutions:**
1. **Configure link checking:**
```bash
"check documentation links with timeout of 10 seconds and ignore external domains github.com"
```
2. **Check anchor links:**
```markdown
<!-- Ensure anchors exist -->
## My Section
Link to [My Section](#my-section) <!-- Correct -->
Link to [My Section](#my_section) <!-- May fail -->
```
3. **Handle external link timeouts:**
```bash
"validate content with longer timeout for external links and retry failed checks"
```
### Problem: Code Block Validation Fails
**Symptoms:**
- Valid code marked as invalid
- Syntax highlighting not working
- Code examples cause build failures
**Solutions:**
1. **Check language tags:**
````markdown
<!-- Correct -->
```javascript
const example = "Hello World";
```
````
<!-- Incorrect - missing language -->
```
const example = "Hello World";
```
````
2. **Validate code syntax:**
```bash
# Test code blocks separately
node -e "const example = 'Hello World'; console.log(example);"
````
3. **Configure code validation:**
```bash
"validate content with permissive code validation and syntax checking disabled"
```
## Memory System Issues
### Problem: Memory System Not Initializing
**Symptoms:**
- Memory tools return errors
- No historical data available
- Analysis doesn't include insights
**Solutions:**
1. **Check storage directory:**
```bash
ls -la .documcp/memory/
# Should contain analysis files
```
2. **Initialize manually:**
```bash
"recall all memories to initialize the memory system"
```
3. **Check permissions:**
```bash
chmod -R 755 .documcp/
```
### Problem: Similar Projects Not Found
**Symptoms:**
- No similar projects in results
- Low-quality recommendations
- Missing historical patterns
**Solutions:**
1. **Build memory with more analyses:**
```bash
"analyze multiple repositories to build memory patterns"
```
2. **Export and import memory:**
```bash
"export memories in JSON format for backup"
```
3. **Clean and rebuild:**
```bash
"cleanup old memories and rebuild with recent analyses"
```
## Performance Issues
### Problem: Slow Build Times
**Symptoms:**
- Builds take too long
- GitHub Actions timeout
- Local development is slow
**Solutions:**
1. **Optimize build configuration:**
**Docusaurus:**
```javascript
const config = {
future: {
experimental_faster: true,
},
webpack: {
jsLoader: (isServer) => ({
loader: "esbuild-loader",
options: {
loader: "tsx",
target: isServer ? "node12" : "es2017",
},
}),
},
};
```
**Hugo:**
```yaml
# config.yml
build:
writeStats: false
noJSConfigInAssets: true
```
2. **Enable caching:**
```yaml
# In GitHub Actions
- name: Cache dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
```
3. **Reduce build scope:**
```bash
# Build only changed files
npm run build -- --locale en
```
### Problem: Large Bundle Sizes
**Symptoms:**
- Slow page loads
- High bandwidth usage
- Poor mobile performance
**Solutions:**
1. **Analyze bundle:**
```bash
# Docusaurus
npm run build -- --bundle-analyzer
# Check generated files
ls -lh build/assets/
```
2. **Optimize images:**
```bash
# Convert images to WebP
find docs -name "*.png" -exec cwebp {} -o {}.webp \;
```
3. **Enable code splitting:**
```javascript
// Docusaurus config
const config = {
webpack: {
splitChunks: {
chunks: "all",
cacheGroups: {
default: {
minChunks: 2,
reuseExistingChunk: true,
},
},
},
},
};
```
## Getting Help
### Diagnostic Information
When reporting issues, include:
1. **DocuMCP version:**
```bash
npm list documcp
```
2. **System information:**
```bash
node --version
npm --version
git --version
```
3. **Error logs:**
```bash
# GitHub Actions logs
# Local build output
# Browser console errors
```
### Support Channels
- **GitHub Issues**: [Report bugs and feature requests](https://github.com/tosin2013/documcp/issues)
- **Documentation**: Check other guides in this documentation
- **Community**: Search existing issues for solutions
### Self-Diagnostic Commands
```bash
# Complete health check
"verify my entire documentation setup and identify all issues"
# Performance analysis
"analyze my documentation build performance and suggest optimizations"
# Security check
"validate my GitHub Pages deployment for security best practices"
```
## Prevention Tips
### Regular Maintenance
1. **Weekly validation:**
```bash
"check all documentation links and validate content quality"
```
2. **Monthly updates:**
```bash
# Update dependencies
npm update
# Regenerate configurations if needed
```
3. **Monitor deployment:**
- Set up GitHub Actions notifications
- Check site accessibility regularly
- Monitor build times and performance
### Best Practices
1. **Always test locally before deploying**
2. **Use DocuMCP validation before committing**
3. **Keep dependencies updated**
4. **Monitor GitHub Actions for failures**
5. **Backup memory and configurations**
## Summary
Common issue categories and solutions:
✅ Repository analysis problems - permissions and context
✅ SSG recommendation issues - preferences and project type
✅ Configuration generation - paths and project details
✅ Deployment failures - workflows and settings
✅ Content validation - links and code blocks
✅ Performance optimization - builds and bundles
✅ Memory system troubleshooting - initialization and data
Most issues can be resolved by providing more context to DocuMCP or fixing configuration details!
```
--------------------------------------------------------------------------------
/src/tools/generate-readme-template.ts:
--------------------------------------------------------------------------------
```typescript
import { z } from "zod";
// Template types
export const TemplateType = z.enum([
"library",
"application",
"cli-tool",
"api",
"documentation",
]);
export type TemplateType = z.infer<typeof TemplateType>;
// Input schema
export const GenerateReadmeTemplateSchema = z.object({
projectName: z.string().min(1, "Project name is required"),
description: z.string().min(1, "Project description is required"),
templateType: TemplateType,
author: z.string().optional(),
license: z.string().default("MIT"),
includeScreenshots: z.boolean().default(false),
includeBadges: z.boolean().default(true),
includeContributing: z.boolean().default(true),
outputPath: z.string().optional(),
});
export type GenerateReadmeTemplateInput = z.infer<
typeof GenerateReadmeTemplateSchema
>;
interface TemplateSection {
title: string;
content: string;
required: boolean;
}
interface ReadmeTemplate {
sections: TemplateSection[];
badges: string[];
metadata: {
type: TemplateType;
estimatedLength: number;
};
}
export class ReadmeTemplateGenerator {
private templates: Map<TemplateType, ReadmeTemplate> = new Map();
constructor() {
this.initializeTemplates();
}
private initializeTemplates(): void {
// Library/Package Template
this.templates.set("library", {
sections: [
{
title: "Header",
content: "# {{projectName}}\n\n> {{description}}",
required: true,
},
{
title: "Badges",
content: "{{badges}}",
required: false,
},
{
title: "TL;DR",
content:
"## TL;DR\n\nWhat it does in 2-3 sentences. Who should use it.\n\n- ✅ Perfect for X use cases\n- ✅ Solves Y problems\n- ❌ Not suitable for Z (consider [alternative] instead)",
required: true,
},
{
title: "Quick Start",
content:
"## Quick Start\n\n### Install\n\n```bash\nnpm install {{projectName}}\n```\n\n### Use\n\n```javascript\nconst {{camelCaseName}} = require('{{projectName}}');\n\n// Basic usage example\nconst result = {{camelCaseName}}.doSomething();\nconsole.log(result);\n```",
required: true,
},
{
title: "API Documentation",
content:
"## API Documentation\n\n[Link to full API documentation]\n\n### Core Methods\n\n#### `methodName(param)`\n\n- **param** `{Type}` - Description\n- **Returns** `{Type}` - Description\n\nExample:\n```javascript\n// Example usage\n```",
required: true,
},
{
title: "Contributing",
content:
"## Contributing\n\nWe welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.\n\n### Development Setup\n\n```bash\ngit clone https://github.com/{{author}}/{{projectName}}.git\ncd {{projectName}}\nnpm install\nnpm test\n```",
required: false,
},
{
title: "License",
content: "## License\n\n{{license}} © {{author}}",
required: true,
},
],
badges: [
"[](https://badge.fury.io/js/{{projectName}})",
"[](https://travis-ci.org/{{author}}/{{projectName}})",
"[](https://opensource.org/licenses/{{license}})",
],
metadata: {
type: "library",
estimatedLength: 150,
},
});
// Application Template
this.templates.set("application", {
sections: [
{
title: "Header",
content: "# {{projectName}}\n\n> {{description}}",
required: true,
},
{
title: "Screenshot",
content: "{{screenshot}}",
required: false,
},
{
title: "What This Does",
content:
"## What This Does\n\n{{projectName}} helps you:\n\n- 🎯 **Feature 1** - Brief explanation\n- ⚡ **Feature 2** - Brief explanation\n- 🔧 **Feature 3** - Brief explanation",
required: true,
},
{
title: "Quick Start",
content:
"## Quick Start\n\n### Prerequisites\n\n- Node.js 18+ \n- npm or yarn\n- [Additional requirements]\n\n### Install & Run\n\n```bash\ngit clone https://github.com/{{author}}/{{projectName}}.git\ncd {{projectName}}\nnpm install\nnpm start\n```\n\nOpen http://localhost:3000 in your browser.",
required: true,
},
{
title: "Configuration",
content:
"## Configuration\n\nCreate a `.env` file in the root directory:\n\n```env\n# Required settings\nPORT=3000\nNODE_ENV=development\n\n# Optional settings\nDATABASE_URL=your_database_url\nAPI_KEY=your_api_key\n```\n\nSee [Configuration Guide](docs/configuration.md) for all options.",
required: true,
},
{
title: "Usage",
content:
"## Usage\n\n### Basic Operations\n\n1. **Step 1** - Description\n2. **Step 2** - Description\n3. **Step 3** - Description\n\n### Advanced Features\n\n[Link to advanced documentation]",
required: true,
},
{
title: "Contributing",
content:
"## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and contribution guidelines.",
required: false,
},
{
title: "License",
content: "## License\n\n{{license}} © {{author}}",
required: true,
},
],
badges: [
"[](https://github.com/{{author}}/{{projectName}}/actions)",
"[](LICENSE)",
],
metadata: {
type: "application",
estimatedLength: 200,
},
});
// CLI Tool Template
this.templates.set("cli-tool", {
sections: [
{
title: "Header",
content: "# {{projectName}}\n\n> {{description}}",
required: true,
},
{
title: "Installation",
content:
"## Installation\n\n```bash\n# Global installation\nnpm install -g {{projectName}}\n\n# Or use with npx\nnpx {{projectName}} --help\n```",
required: true,
},
{
title: "Usage",
content:
"## Usage\n\n### Basic Commands\n\n```bash\n# Basic usage\n{{projectName}} [options] [arguments]\n\n# Show help\n{{projectName}} --help\n\n# Show version\n{{projectName}} --version\n```\n\n### Examples\n\n```bash\n# Example 1\n{{projectName}} command --option value\n\n# Example 2\n{{projectName}} another-command file.txt\n```",
required: true,
},
{
title: "Options",
content:
"## Options\n\n| Option | Description | Default |\n|--------|-------------|----------|\n| `-h, --help` | Show help | |\n| `-v, --version` | Show version | |\n| `--config <path>` | Config file path | `./config.json` |\n| `--verbose` | Verbose output | `false` |",
required: true,
},
{
title: "Configuration",
content:
'## Configuration\n\nCreate a config file:\n\n```json\n{\n "setting1": "value1",\n "setting2": "value2"\n}\n```',
required: false,
},
{
title: "Contributing",
content:
"## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for development guidelines.",
required: false,
},
{
title: "License",
content: "## License\n\n{{license}} © {{author}}",
required: true,
},
],
badges: [
"[](https://www.npmjs.com/package/{{projectName}})",
"[](LICENSE)",
],
metadata: {
type: "cli-tool",
estimatedLength: 180,
},
});
}
generateTemplate(input: GenerateReadmeTemplateInput): string {
const template = this.templates.get(input.templateType);
if (!template) {
throw new Error(`Template type "${input.templateType}" not supported`);
}
let readme = "";
const camelCaseName = this.toCamelCase(input.projectName);
// Process each section
for (const section of template.sections) {
if (section.title === "Badges" && input.includeBadges) {
readme += this.processBadges(template.badges, input) + "\n\n";
} else if (section.title === "Screenshot" && input.includeScreenshots) {
readme += this.processScreenshot(input) + "\n\n";
} else if (
section.title === "Contributing" &&
!input.includeContributing
) {
continue;
} else {
readme +=
this.processSection(section.content, input, camelCaseName) + "\n\n";
}
}
return readme.trim();
}
private processBadges(
badges: string[],
input: GenerateReadmeTemplateInput,
): string {
return badges
.map((badge) => this.replaceVariables(badge, input))
.join("\n");
}
private processScreenshot(input: GenerateReadmeTemplateInput): string {
return `\n\n*Add a screenshot or demo GIF here*`;
}
private processSection(
content: string,
input: GenerateReadmeTemplateInput,
camelCaseName: string,
): string {
let processed = this.replaceVariables(content, input);
processed = processed.replace(/\{\{camelCaseName\}\}/g, camelCaseName);
return processed;
}
private replaceVariables(
content: string,
input: GenerateReadmeTemplateInput,
): string {
return content
.replace(/\{\{projectName\}\}/g, input.projectName)
.replace(/\{\{description\}\}/g, input.description)
.replace(/\{\{author\}\}/g, input.author || "your-username")
.replace(/\{\{license\}\}/g, input.license);
}
private toCamelCase(str: string): string {
return str
.replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ""))
.replace(/^./, (c) => c.toLowerCase());
}
getAvailableTemplates(): TemplateType[] {
return Array.from(this.templates.keys());
}
getTemplateInfo(type: TemplateType): ReadmeTemplate["metadata"] | null {
const template = this.templates.get(type);
return template ? template.metadata : null;
}
}
/**
* Generates standardized README templates for different project types with best practices.
*
* Creates comprehensive README templates tailored to specific project types (library,
* application, CLI tool, API, documentation) following community best practices. Includes
* customizable sections, badges, contributing guidelines, and project-specific content
* to ensure professional documentation standards.
*
* @param input - The input parameters for README template generation
* @param input.projectName - Name of the project (required)
* @param input.description - Brief description of what the project does (required)
* @param input.templateType - Type of project template to generate
* @param input.author - Optional project author/organization name
* @param input.license - Project license (default: "MIT")
* @param input.includeScreenshots - Whether to include screenshot placeholders (default: false)
* @param input.includeBadges - Whether to include status badges (default: true)
* @param input.includeContributing - Whether to include contributing section (default: true)
* @param input.outputPath - Optional path to write the generated README.md file
*
* @returns Promise resolving to README template generation results
* @returns template - The generated README template content
* @returns metadata - Template metadata including type and estimated length
* @returns filePath - Path where the README was written (if outputPath provided)
*
* @throws {Error} When required parameters are missing
* @throws {Error} When output path is inaccessible
* @throws {Error} When template generation fails
*
* @example
* ```typescript
* // Generate library README template
* const result = await generateReadmeTemplate({
* projectName: "MyAwesomeLibrary",
* description: "A powerful utility library for data processing",
* templateType: "library",
* author: "Your Name",
* license: "MIT",
* includeBadges: true
* });
*
* console.log(`Generated ${result.metadata.estimatedLength} line README`);
*
* // Generate CLI tool template with output file
* const cliTemplate = await generateReadmeTemplate({
* projectName: "my-cli-tool",
* description: "Command-line interface for project management",
* templateType: "cli-tool",
* outputPath: "./README.md"
* });
* ```
*
* @since 1.0.0
*/
export async function generateReadmeTemplate(
input: GenerateReadmeTemplateInput,
): Promise<{
content: string;
metadata: {
templateType: TemplateType;
estimatedLength: number;
sectionsIncluded: number;
};
}> {
const validatedInput = GenerateReadmeTemplateSchema.parse(input);
const generator = new ReadmeTemplateGenerator();
const content = generator.generateTemplate(validatedInput);
const templateInfo = generator.getTemplateInfo(validatedInput.templateType);
if (!templateInfo) {
throw new Error(`Template type "${validatedInput.templateType}" not found`);
}
// Write to file if output path specified
if (validatedInput.outputPath) {
const fs = await import("fs/promises");
await fs.writeFile(validatedInput.outputPath, content, "utf-8");
}
return {
content,
metadata: {
templateType: validatedInput.templateType,
estimatedLength: templateInfo.estimatedLength,
sectionsIncluded: content.split("##").length - 1,
},
};
}
```
--------------------------------------------------------------------------------
/src/tools/manage-sitemap.ts:
--------------------------------------------------------------------------------
```typescript
/**
* Manage Sitemap Tool
*
* MCP tool for generating, validating, and managing sitemap.xml files.
* Sitemap.xml serves as the source of truth for all documentation links.
*/
import { z } from "zod";
import path from "path";
import { promises as fs } from "fs";
import {
generateSitemap,
validateSitemap,
updateSitemap,
listSitemapUrls,
type SitemapUrl,
type SitemapStats,
} from "../utils/sitemap-generator.js";
import { formatMCPResponse } from "../types/api.js";
/**
* Input schema for manage_sitemap tool
*/
export const ManageSitemapInputSchema = z.object({
action: z
.enum(["generate", "validate", "update", "list"])
.describe(
"Action to perform: generate (create new), validate (check structure), update (sync with docs), list (show all URLs)",
),
docsPath: z.string().describe("Path to documentation root directory"),
baseUrl: z
.string()
.optional()
.describe(
"Base URL for the site (e.g., https://user.github.io/repo). Required for generate/update actions.",
),
includePatterns: z
.array(z.string())
.optional()
.describe(
"File patterns to include (default: **/*.md, **/*.html, **/*.mdx)",
),
excludePatterns: z
.array(z.string())
.optional()
.describe(
"File patterns to exclude (default: node_modules, .git, dist, build, .documcp)",
),
updateFrequency: z
.enum(["always", "hourly", "daily", "weekly", "monthly", "yearly", "never"])
.optional()
.describe("Default change frequency for pages"),
useGitHistory: z
.boolean()
.optional()
.default(true)
.describe("Use git history for last modified dates (default: true)"),
sitemapPath: z
.string()
.optional()
.describe("Custom path for sitemap.xml (default: docsPath/sitemap.xml)"),
});
export type ManageSitemapInput = z.input<typeof ManageSitemapInputSchema>;
/**
* Manage sitemap.xml for documentation
*/
export async function manageSitemap(
input: ManageSitemapInput,
): Promise<{ content: any[] }> {
const { action, docsPath, sitemapPath } = input;
// Resolve sitemap path
const resolvedSitemapPath = sitemapPath || path.join(docsPath, "sitemap.xml");
try {
// Verify docs directory exists
try {
await fs.access(docsPath);
} catch {
return formatMCPResponse({
success: false,
error: {
code: "DOCS_DIR_NOT_FOUND",
message: `Documentation directory not found: ${docsPath}`,
},
metadata: {
toolVersion: "1.0.0",
executionTime: Date.now(),
timestamp: new Date().toISOString(),
},
});
}
switch (action) {
case "generate":
return await generateSitemapAction(input, resolvedSitemapPath);
case "validate":
return await validateSitemapAction(resolvedSitemapPath);
case "update":
return await updateSitemapAction(input, resolvedSitemapPath);
case "list":
return await listSitemapAction(resolvedSitemapPath);
default:
return formatMCPResponse({
success: false,
error: {
code: "UNKNOWN_ACTION",
message: `Unknown action: ${action}`,
},
metadata: {
toolVersion: "1.0.0",
executionTime: Date.now(),
timestamp: new Date().toISOString(),
},
});
}
} catch (error) {
return formatMCPResponse({
success: false,
error: {
code: "SITEMAP_ERROR",
message: `Error managing sitemap: ${
error instanceof Error ? error.message : String(error)
}`,
},
metadata: {
toolVersion: "1.0.0",
executionTime: Date.now(),
timestamp: new Date().toISOString(),
},
});
}
}
/**
* Generate new sitemap.xml
*/
async function generateSitemapAction(
input: ManageSitemapInput,
sitemapPath: string,
): Promise<{ content: any[] }> {
const {
docsPath,
baseUrl,
includePatterns,
excludePatterns,
updateFrequency,
} = input;
if (!baseUrl) {
return formatMCPResponse({
success: false,
error: {
code: "BASE_URL_REQUIRED",
message: "baseUrl is required for generate action",
},
metadata: {
toolVersion: "1.0.0",
executionTime: 0,
timestamp: new Date().toISOString(),
},
});
}
// Generate sitemap
const { xml, urls, stats } = await generateSitemap({
baseUrl,
docsPath,
includePatterns,
excludePatterns,
useGitHistory: input.useGitHistory,
defaultChangeFreq: updateFrequency || "monthly",
});
// Write sitemap.xml
await fs.writeFile(sitemapPath, xml, "utf-8");
// Format output
const output = formatGenerateOutput(sitemapPath, urls, stats);
return formatMCPResponse({
success: true,
data: {
action: "generate",
sitemapPath,
totalUrls: stats.totalUrls,
categories: stats.byCategory,
output,
},
metadata: {
toolVersion: "1.0.0",
executionTime: Date.now(),
timestamp: new Date().toISOString(),
},
});
}
/**
* Validate existing sitemap.xml
*/
async function validateSitemapAction(
sitemapPath: string,
): Promise<{ content: any[] }> {
// Check if sitemap exists
try {
await fs.access(sitemapPath);
} catch {
return formatMCPResponse({
success: false,
error: {
code: "SITEMAP_NOT_FOUND",
message: `Sitemap not found: ${sitemapPath}. Use action: "generate" to create a new sitemap.`,
},
metadata: {
toolVersion: "1.0.0",
executionTime: 0,
timestamp: new Date().toISOString(),
},
});
}
// Validate sitemap
const validation = await validateSitemap(sitemapPath);
// Format output
const output = formatValidationOutput(sitemapPath, validation);
return formatMCPResponse({
success: validation.valid,
data: {
action: "validate",
valid: validation.valid,
errorCount: validation.errors.length,
warningCount: validation.warnings.length,
urlCount: validation.urlCount,
output,
},
error: validation.valid
? undefined
: {
code: "VALIDATION_FAILED",
message: `Sitemap validation failed with ${validation.errors.length} error(s)`,
},
metadata: {
toolVersion: "1.0.0",
executionTime: Date.now(),
timestamp: new Date().toISOString(),
},
});
}
/**
* Update existing sitemap.xml
*/
async function updateSitemapAction(
input: ManageSitemapInput,
sitemapPath: string,
): Promise<{ content: any[] }> {
const {
docsPath,
baseUrl,
includePatterns,
excludePatterns,
updateFrequency,
} = input;
if (!baseUrl) {
return formatMCPResponse({
success: false,
error: {
code: "BASE_URL_REQUIRED",
message: "baseUrl is required for update action",
},
metadata: {
toolVersion: "1.0.0",
executionTime: 0,
timestamp: new Date().toISOString(),
},
});
}
// Check if sitemap exists
const sitemapExists = await fs
.access(sitemapPath)
.then(() => true)
.catch(() => false);
if (!sitemapExists) {
return formatMCPResponse({
success: false,
error: {
code: "SITEMAP_NOT_FOUND",
message: `Sitemap not found: ${sitemapPath}. Run generate action first.`,
},
metadata: {
toolVersion: "1.0.0",
executionTime: 0,
timestamp: new Date().toISOString(),
},
});
}
// Update sitemap
const changes = await updateSitemap(sitemapPath, {
baseUrl,
docsPath,
includePatterns,
excludePatterns,
useGitHistory: input.useGitHistory,
defaultChangeFreq: updateFrequency || "monthly",
});
// Format output
const output = formatUpdateOutput(sitemapPath, changes);
return formatMCPResponse({
success: true,
data: {
action: "update",
added: changes.added,
removed: changes.removed,
updated: changes.updated,
total: changes.total,
output,
},
metadata: {
toolVersion: "1.0.0",
executionTime: Date.now(),
timestamp: new Date().toISOString(),
},
});
}
/**
* List all URLs from sitemap.xml
*/
async function listSitemapAction(
sitemapPath: string,
): Promise<{ content: any[] }> {
// Check if sitemap exists
try {
await fs.access(sitemapPath);
} catch {
return formatMCPResponse({
success: false,
error: {
code: "SITEMAP_NOT_FOUND",
message: `Sitemap not found: ${sitemapPath}. Use action: "generate" to create a new sitemap.`,
},
metadata: {
toolVersion: "1.0.0",
executionTime: 0,
timestamp: new Date().toISOString(),
},
});
}
// List URLs
const urls = await listSitemapUrls(sitemapPath);
// Format output
const output = formatListOutput(sitemapPath, urls);
return formatMCPResponse({
success: true,
data: {
action: "list",
totalUrls: urls.length,
urls: urls.map((u) => ({
loc: u.loc,
priority: u.priority,
category: u.category,
lastmod: u.lastmod,
})),
output,
},
metadata: {
toolVersion: "1.0.0",
executionTime: Date.now(),
timestamp: new Date().toISOString(),
},
});
}
/**
* Format generate action output
*/
function formatGenerateOutput(
sitemapPath: string,
urls: SitemapUrl[],
stats: SitemapStats,
): string {
const lines: string[] = [
"✅ Sitemap generated successfully!",
"",
`📄 Location: ${sitemapPath}`,
`📊 Total URLs: ${stats.totalUrls}`,
"",
"📋 URLs by Category:",
];
// Sort categories by count (descending)
const sortedCategories = Object.entries(stats.byCategory).sort(
([, a], [, b]) => b - a,
);
for (const [category, count] of sortedCategories) {
const percentage = ((count / stats.totalUrls) * 100).toFixed(1);
lines.push(` • ${category}: ${count} (${percentage}%)`);
}
lines.push("");
lines.push("🔄 Change Frequencies:");
// Sort change frequencies
const sortedFreqs = Object.entries(stats.byChangeFreq).sort(
([, a], [, b]) => b - a,
);
for (const [freq, count] of sortedFreqs) {
lines.push(` • ${freq}: ${count}`);
}
// Show top priority URLs
const topUrls = urls.filter((u) => (u.priority || 0) >= 0.9).slice(0, 5);
if (topUrls.length > 0) {
lines.push("");
lines.push("⭐ High Priority Pages:");
for (const url of topUrls) {
lines.push(` • [${url.priority?.toFixed(1)}] ${url.title || url.loc}`);
}
}
lines.push("");
lines.push("💡 Next Steps:");
lines.push(" → Submit sitemap to search engines (Google, Bing)");
lines.push(" → Add sitemap to robots.txt");
lines.push(" → Deploy to GitHub Pages");
return lines.join("\n");
}
/**
* Format validation output
*/
function formatValidationOutput(
sitemapPath: string,
validation: {
valid: boolean;
errors: string[];
warnings: string[];
urlCount: number;
},
): string {
const lines: string[] = [];
if (validation.valid) {
lines.push("✅ Sitemap is valid!");
} else {
lines.push("❌ Sitemap validation failed!");
}
lines.push("");
lines.push(`📄 Location: ${sitemapPath}`);
lines.push(`📊 Total URLs: ${validation.urlCount}`);
if (validation.errors.length > 0) {
lines.push("");
lines.push("🔴 Errors:");
for (const error of validation.errors) {
lines.push(` • ${error}`);
}
}
if (validation.warnings.length > 0) {
lines.push("");
lines.push("⚠️ Warnings:");
for (const warning of validation.warnings) {
lines.push(` • ${warning}`);
}
}
if (validation.valid) {
lines.push("");
lines.push("💡 Recommendations:");
lines.push(" ℹ️ Sitemap follows the Sitemaps 0.9 protocol");
lines.push(" ℹ️ Ready for search engine submission");
}
return lines.join("\n");
}
/**
* Format update output
*/
function formatUpdateOutput(
sitemapPath: string,
changes: { added: number; removed: number; updated: number; total: number },
): string {
const lines: string[] = [
"✅ Sitemap updated successfully!",
"",
`📄 Location: ${sitemapPath}`,
`📊 Total URLs: ${changes.total}`,
"",
"📝 Changes:",
];
if (changes.added > 0) {
lines.push(` ✨ Added: ${changes.added} new page(s)`);
}
if (changes.removed > 0) {
lines.push(` 🗑️ Removed: ${changes.removed} deleted page(s)`);
}
if (changes.updated > 0) {
lines.push(` 🔄 Updated: ${changes.updated} modified page(s)`);
}
if (changes.added === 0 && changes.removed === 0 && changes.updated === 0) {
lines.push(" ℹ️ No changes detected");
}
lines.push("");
lines.push("💡 Next Steps:");
lines.push(" → Review changes if needed");
lines.push(" → Redeploy to GitHub Pages");
lines.push(" → Notify search engines of updates");
return lines.join("\n");
}
/**
* Format list output
*/
function formatListOutput(sitemapPath: string, urls: SitemapUrl[]): string {
const lines: string[] = [
`📄 Sitemap URLs from: ${sitemapPath}`,
`📊 Total: ${urls.length}`,
"",
];
// Group by category
const byCategory: Record<string, SitemapUrl[]> = {};
for (const url of urls) {
const category = url.category || "default";
if (!byCategory[category]) {
byCategory[category] = [];
}
byCategory[category].push(url);
}
// Display by category
for (const [category, categoryUrls] of Object.entries(byCategory)) {
lines.push(`📂 ${category} (${categoryUrls.length}):`);
// Sort by priority
const sorted = categoryUrls.sort(
(a, b) => (b.priority || 0) - (a.priority || 0),
);
for (const url of sorted.slice(0, 10)) {
// Show first 10 per category
const priority = url.priority?.toFixed(1) || "0.5";
const title = url.title || path.basename(url.loc);
lines.push(` [${priority}] ${title}`);
lines.push(` ${url.loc}`);
}
if (categoryUrls.length > 10) {
lines.push(` ... and ${categoryUrls.length - 10} more`);
}
lines.push("");
}
return lines.join("\n");
}
```