From ed2f03b0e9ce80892e75afe55bcc60e691e0930f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Nov 2025 06:06:57 +0000 Subject: [PATCH 01/11] docs: document CLI lifecycle and improve error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add comprehensive CLI documentation to README - Document docset lifecycle (DEFINE β†’ CREATE β†’ INITIALIZE β†’ USE β†’ REFRESH) - Add detailed documentation for all CLI commands (create, init, status, refresh) - Include practical examples and complete workflow - Enhance MCP server error messages with CLI usage instructions - Add setup instructions when config file not found - Include CLI commands in "docset not found" error - Add initialization check for git_repo sources with helpful CLI guidance This makes the CLI more discoverable and helps users understand the proper workflow for managing docsets. --- README.md | 196 ++++++++++++++++++++++++++++++ packages/mcp-server/src/server.ts | 35 +++++- 2 files changed, 229 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 97ea332..d8d688a 100644 --- a/README.md +++ b/README.md @@ -278,6 +278,202 @@ docsets: url: "https://github.com/external/docs.git" ``` +## πŸ› οΈ CLI Commands & Docset Lifecycle + +The `agentic-knowledge` CLI provides commands to manage your documentation lifecycle. When you run `agentic-knowledge` without arguments, it starts the MCP server. With arguments, it executes CLI commands. + +### Understanding the Docset Lifecycle + +A docset goes through the following phases: + +``` +1. DEFINE β†’ Configure docset in .knowledge/config.yaml +2. CREATE β†’ Use CLI to create configuration from presets +3. INITIALIZE β†’ Download and prepare documentation files +4. USE β†’ Search and navigate via MCP server +5. REFRESH β†’ Update documentation as needed +``` + +### CLI Commands + +#### `create` - Create New Docset Configuration + +Create docset configurations quickly using presets: + +**Git Repository Preset:** +```bash +agentic-knowledge create \ + --preset git-repo \ + --id mcp-sdk \ + --name "MCP TypeScript SDK" \ + --url https://github.com/modelcontextprotocol/typescript-sdk.git \ + --branch main +``` + +**Local Folder Preset:** +```bash +agentic-knowledge create \ + --preset local-folder \ + --id my-docs \ + --name "My Documentation" \ + --path ./docs +``` + +**Options:** +- `--preset `: Choose preset (`git-repo` or `local-folder`) +- `--id `: Unique identifier for the docset +- `--name `: Human-readable name +- `--url `: Git repository URL (for git-repo preset) +- `--branch `: Git branch (optional, defaults to main) +- `--path `: Local directory path (for local-folder preset) + +The `create` command: +- βœ… Creates or updates `.knowledge/config.yaml` +- βœ… Validates docset ID uniqueness +- βœ… For local folders, creates symlinks immediately +- βœ… For git repos, prepares configuration for initialization + +#### `init` - Initialize Docset Sources + +Initialize a configured docset by downloading and preparing documentation: + +```bash +# Initialize a specific docset +agentic-knowledge init mcp-sdk + +# Force re-initialization +agentic-knowledge init mcp-sdk --force + +# Use custom config path +agentic-knowledge init mcp-sdk --config /path/to/config.yaml +``` + +**What happens during initialization:** + +1. **For Git Repositories:** + - Clones repository to temporary directory + - Extracts specified paths (if configured) + - Applies smart filtering (excludes `node_modules/`, build artifacts, etc.) + - Copies documentation to `.knowledge/docsets/{id}/` + - Creates metadata files for change tracking + +2. **For Local Folders:** + - Creates symlinks in `.knowledge/docsets/{id}/` + - No file duplication + - Changes are immediately visible + +3. **Creates Metadata:** + - `.agentic-metadata.json` - Overall docset information + - `.agentic-source-{index}.json` - Per-source tracking with content hashes + +**Directory structure after init:** +``` +.knowledge/ +β”œβ”€β”€ config.yaml +β”œβ”€β”€ .gitignore (auto-created) +└── docsets/ + └── mcp-sdk/ + β”œβ”€β”€ .agentic-metadata.json + β”œβ”€β”€ .agentic-source-0.json + └── [documentation files...] +``` + +#### `status` - Check Docset Status + +View the status of all docsets and their sources: + +```bash +# Basic status +agentic-knowledge status + +# Detailed status with source information +agentic-knowledge status --verbose + +# Use custom config +agentic-knowledge status --config /path/to/config.yaml +``` + +**Status indicators:** +- βœ… Green: Updated within 24 hours +- ⚠️ Yellow: Updated 1-7 days ago +- πŸ”„ Red: Updated >7 days ago or not initialized + +**Example output:** +``` +πŸ“Š Docset Status + +βœ… mcp-sdk (MCP TypeScript SDK) + Initialized | 45 files | 2 source(s) loaded + Last refreshed: 2 hours ago + +⚠️ react-docs (React Documentation) + Initialized | 120 files | 1 source(s) loaded + Last refreshed: 3 days ago + + πŸ’‘ Consider running: agentic-knowledge refresh react-docs + +πŸ”„ api-docs (API Documentation) + Not initialized | 1 source(s) configured + + πŸ’‘ Run: agentic-knowledge init api-docs +``` + +#### `refresh` - Update Documentation + +Refresh docset sources to get the latest content: + +```bash +# Refresh all docsets +agentic-knowledge refresh + +# Refresh specific docset +agentic-knowledge refresh mcp-sdk + +# Force refresh (ignore cache) +agentic-knowledge refresh mcp-sdk --force + +# Use custom config +agentic-knowledge refresh --config /path/to/config.yaml +``` + +**Smart refresh logic:** +- Checks Git commit hash to detect changes +- Skips refresh if updated within 1 hour (unless `--force`) +- Skips refresh if no changes detected +- Creates backup before refresh +- Updates metadata with new timestamp + +**When to refresh:** +- Git repository has new commits +- It's been several days since last update +- You want to ensure latest content + +### Complete Workflow Example + +Here's a complete workflow from scratch: + +```bash +# 1. Create docset for a Git repository +agentic-knowledge create \ + --preset git-repo \ + --id react-docs \ + --name "React Documentation" \ + --url https://github.com/facebook/react.git \ + --branch main + +# 2. Initialize the docset (downloads docs) +agentic-knowledge init react-docs + +# 3. Check status +agentic-knowledge status + +# 4. Start MCP server (for AI assistant) +agentic-knowledge + +# Later: Update documentation +agentic-knowledge refresh react-docs +``` + ## 🎯 How to Use ### Step 1: Set Up Your Documentation diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index 83cf08b..dd66bba 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -18,6 +18,8 @@ import { createStructuredResponse, type KnowledgeConfig, } from "@codemcp/knowledge-core"; +import { existsSync } from "node:fs"; +import { resolve, dirname } from "node:path"; /** * Create an agentic knowledge MCP server @@ -59,7 +61,12 @@ export function createAgenticKnowledgeServer() { const configPath = await findConfigPath(); if (!configPath) { throw new Error( - "No configuration file found. Please create a .knowledge/config.yaml file in your project.", + "No configuration file found.\n\n" + + "To get started:\n" + + "1. Create a docset: agentic-knowledge create --preset git-repo --id my-docs --name \"My Docs\" --url \n" + + "2. Initialize it: agentic-knowledge init my-docs\n" + + "3. Start the server: agentic-knowledge\n\n" + + "Or manually create a .knowledge/config.yaml file in your project.", ); } @@ -222,13 +229,37 @@ Use the path and search terms with your text search tools (grep, rg, ripgrep, fi if (!docset) { const availableIds = config.docsets.map((d) => d.id).join(", "); throw new Error( - `Docset '${docset_id}' not found. Available docsets: ${availableIds}`, + `Docset '${docset_id}' not found.\n\n` + + `Available docsets: ${availableIds}\n\n` + + `To create a new docset:\n` + + `agentic-knowledge create --preset git-repo --id ${docset_id} --name "My Docs" --url \n` + + `agentic-knowledge init ${docset_id}`, ); } // Calculate local path const localPath = calculateLocalPath(docset, configPath); + // Check if docset is initialized (for git_repo sources) + const primarySource = docset.sources?.[0]; + if (primarySource?.type === "git_repo") { + // For git repos, the path should be absolute or relative to project root + const configDir = dirname(configPath); + const projectRoot = dirname(configDir); + const absolutePath = resolve(projectRoot, localPath); + + if (!existsSync(absolutePath)) { + throw new Error( + `Docset '${docset_id}' is not initialized.\n\n` + + `The docset is configured but hasn't been initialized yet.\n\n` + + `To initialize this docset:\n` + + `agentic-knowledge init ${docset_id}\n\n` + + `To check status of all docsets:\n` + + `agentic-knowledge status`, + ); + } + } + // Create template context with proper function signature const templateContext = createTemplateContext( localPath, From 47c8952b0830da00d4241c4e38d418eff5e5fabd Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Nov 2025 06:12:36 +0000 Subject: [PATCH 02/11] refactor: simplify lifecycle and allow server start without config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Simplify docset lifecycle from 5 to 4 phases - Remove redundant DEFINE/CREATE distinction - CREATE now covers both CLI and manual config editing - Clearer progression: CREATE β†’ INITIALIZE β†’ USE β†’ REFRESH - Allow MCP server to start without configuration file - Server starts successfully even if no .knowledge/config.yaml exists - Tools advertise helpful setup instructions in their descriptions - Errors only occur when tools are actually invoked without config - Enhance tool descriptions for unconfigured state - search_docs shows complete setup guide when no docsets configured - list_docsets provides CLI and manual configuration examples - Both options (CLI and manual) clearly explained This improves the getting-started experience by allowing users to see the tools and their documentation before configuring docsets. --- README.md | 11 +- packages/mcp-server/src/server.ts | 243 +++++++++++++++++++----------- 2 files changed, 162 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index d8d688a..03d46ff 100644 --- a/README.md +++ b/README.md @@ -287,18 +287,17 @@ The `agentic-knowledge` CLI provides commands to manage your documentation lifec A docset goes through the following phases: ``` -1. DEFINE β†’ Configure docset in .knowledge/config.yaml -2. CREATE β†’ Use CLI to create configuration from presets -3. INITIALIZE β†’ Download and prepare documentation files -4. USE β†’ Search and navigate via MCP server -5. REFRESH β†’ Update documentation as needed +1. CREATE β†’ Configure docset (manually edit config.yaml or use CLI presets) +2. INITIALIZE β†’ Download and prepare documentation files +3. USE β†’ Search and navigate via MCP server +4. REFRESH β†’ Update documentation as needed ``` ### CLI Commands #### `create` - Create New Docset Configuration -Create docset configurations quickly using presets: +Create docset configurations quickly using presets. Alternatively, you can manually edit `.knowledge/config.yaml` - this command is just a convenience tool that does it for you. **Git Repository Preset:** ```bash diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index dd66bba..304e7d1 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -45,12 +45,12 @@ export function createAgenticKnowledgeServer() { const CONFIG_CACHE_TTL = 60000; // 1 minute cache /** - * Load configuration with caching + * Load configuration with caching (returns null if no config found) */ async function getConfiguration(): Promise<{ config: KnowledgeConfig; configPath: string; - }> { + } | null> { const now = Date.now(); if (configCache && now - configLoadTime < CONFIG_CACHE_TTL) { return configCache; @@ -60,14 +60,7 @@ export function createAgenticKnowledgeServer() { // Find configuration file path const configPath = await findConfigPath(); if (!configPath) { - throw new Error( - "No configuration file found.\n\n" + - "To get started:\n" + - "1. Create a docset: agentic-knowledge create --preset git-repo --id my-docs --name \"My Docs\" --url \n" + - "2. Initialize it: agentic-knowledge init my-docs\n" + - "3. Start the server: agentic-knowledge\n\n" + - "Or manually create a .knowledge/config.yaml file in your project.", - ); + return null; // No config file found - server can still start } // Load configuration @@ -81,62 +74,75 @@ export function createAgenticKnowledgeServer() { // Clear cache on error to force retry next time configCache = null; configLoadTime = 0; - throw error; + // Return null instead of throwing - allow server to start + console.error("Error loading configuration:", error); + return null; } } // Register tool handlers server.setRequestHandler(ListToolsRequestSchema, async () => { - try { - // Load configuration to get available docsets - const { config } = await getConfiguration(); - - // Build rich description with available docsets - const docsetInfo = config.docsets - .map((docset) => { - const description = docset.description - ? ` - ${docset.description}` - : ""; - return `β€’ **${docset.id}** (${docset.name})${description}`; - }) - .join("\n"); - - const searchDocsDescription = `Search for documentation in available docsets. Returns structured response with search instructions and parameters. - -πŸ“š **AVAILABLE DOCSETS:** -${docsetInfo} - -πŸ” **STRUCTURED RESPONSE:** -Returns JSON object with: -- instructions: Search guidance text -- search_terms: Primary keywords to search for -- generalized_search_terms: Broader terms for context -- path: Local directory path to search in - -Use the path and search terms with your text search tools (grep, rg, ripgrep, find).`; + // Load configuration to get available docsets + const configData = await getConfiguration(); + // If no configuration, return tools with setup instructions + if (!configData) { return { tools: [ { name: "search_docs", - description: searchDocsDescription, + description: `Search for documentation in configured docsets. Returns structured response with search instructions and parameters. + +⚠️ **NO DOCSETS CONFIGURED** + +To configure docsets and use this tool: + +**Option 1: Use CLI (recommended)** +\`\`\`bash +# Create a docset for a Git repository +agentic-knowledge create \\ + --preset git-repo \\ + --id my-docs \\ + --name "My Documentation" \\ + --url https://github.com/user/repo.git + +# Initialize it (downloads the docs) +agentic-knowledge init my-docs + +# Restart the MCP server +agentic-knowledge +\`\`\` + +**Option 2: Manual configuration** +Create \`.knowledge/config.yaml\`: +\`\`\`yaml +version: "1.0" +docsets: + - id: my-docs + name: My Documentation + sources: + - type: local_folder + paths: ["./docs"] +\`\`\` + +After configuring, the tool will show available docsets here.`, inputSchema: { type: "object", properties: { docset_id: { type: "string", - description: "Choose the docset to search in.", - enum: config.docsets.map((d) => d.id), + description: + "The identifier of the docset to search in. (No docsets configured - see tool description for setup instructions)", }, keywords: { type: "string", description: - 'Primary search terms or concepts you\'re looking for. Be specific about what you want to find (e.g., "authentication middleware", "user validation", "API rate limiting"). Include the exact terms you expect to appear in the documentation.', + 'Primary search terms or concepts you\'re looking for. Be specific about what you want to find (e.g., "authentication middleware", "user validation", "API rate limiting").', }, generalized_keywords: { type: "string", description: - 'Related terms, synonyms, or contextual keywords that may appear alongside your primary keywords but are not your main target. These help broaden the search context and catch relevant content that might use different terminology (e.g., for "authentication" you might include "login, signin, oauth, credentials, tokens"). Think of terms that would appear in the same sections or discussions as your main keywords.', + 'Related terms, synonyms, or contextual keywords that may appear alongside your primary keywords but are not your main target.', }, }, required: ["docset_id", "keywords"], @@ -146,7 +152,7 @@ Use the path and search terms with your text search tools (grep, rg, ripgrep, fi { name: "list_docsets", description: - "List all available documentation sets (docsets) with detailed information. Note: The search_docs tool already shows available docsets in its description, so this tool is mainly for getting additional metadata.", + "List all available documentation sets (docsets) with detailed information. (Currently no docsets configured - see search_docs description for setup instructions)", inputSchema: { type: "object", properties: {}, @@ -155,50 +161,71 @@ Use the path and search terms with your text search tools (grep, rg, ripgrep, fi }, ], }; - } catch (error) { - // Fallback to basic tools if configuration fails - return { - tools: [ - { - name: "search_docs", - description: - "Search for documentation guidance based on keywords and context. Returns intelligent navigation instructions to help you find relevant information in a specific docset. (Configuration error - use list_docsets to see available options)", - inputSchema: { - type: "object", - properties: { - docset_id: { - type: "string", - description: - "The identifier of the docset to search in. Use list_docsets to see available options.", - }, - keywords: { - type: "string", - description: - 'Primary search terms or concepts you\'re looking for. Be specific about what you want to find (e.g., "authentication middleware", "user validation", "API rate limiting"). Include the exact terms you expect to appear in the documentation.', - }, - generalized_keywords: { - type: "string", - description: - 'Related terms, synonyms, or contextual keywords that may appear alongside your primary keywords but are not your main target. These help broaden the search context and catch relevant content that might use different terminology (e.g., for "authentication" you might include "login, signin, oauth, credentials, tokens"). Think of terms that would appear in the same sections or discussions as your main keywords.', - }, + } + + // Configuration exists - build rich description with available docsets + const { config } = configData; + const docsetInfo = config.docsets + .map((docset) => { + const description = docset.description ? ` - ${docset.description}` : ""; + return `β€’ **${docset.id}** (${docset.name})${description}`; + }) + .join("\n"); + + const searchDocsDescription = `Search for documentation in available docsets. Returns structured response with search instructions and parameters. + +πŸ“š **AVAILABLE DOCSETS:** +${docsetInfo} + +πŸ” **STRUCTURED RESPONSE:** +Returns JSON object with: +- instructions: Search guidance text +- search_terms: Primary keywords to search for +- generalized_search_terms: Broader terms for context +- path: Local directory path to search in + +Use the path and search terms with your text search tools (grep, rg, ripgrep, find).`; + + return { + tools: [ + { + name: "search_docs", + description: searchDocsDescription, + inputSchema: { + type: "object", + properties: { + docset_id: { + type: "string", + description: "Choose the docset to search in.", + enum: config.docsets.map((d) => d.id), + }, + keywords: { + type: "string", + description: + 'Primary search terms or concepts you\'re looking for. Be specific about what you want to find (e.g., "authentication middleware", "user validation", "API rate limiting"). Include the exact terms you expect to appear in the documentation.', + }, + generalized_keywords: { + type: "string", + description: + 'Related terms, synonyms, or contextual keywords that may appear alongside your primary keywords but are not your main target. These help broaden the search context and catch relevant content that might use different terminology (e.g., for "authentication" you might include "login, signin, oauth, credentials, tokens"). Think of terms that would appear in the same sections or discussions as your main keywords.', }, - required: ["docset_id", "keywords"], - additionalProperties: false, }, + required: ["docset_id", "keywords"], + additionalProperties: false, }, - { - name: "list_docsets", - description: - "List all available documentation sets (docsets) that can be searched. Each docset represents a specific project, library, or knowledge base.", - inputSchema: { - type: "object", - properties: {}, - additionalProperties: false, - }, + }, + { + name: "list_docsets", + description: + "List all available documentation sets (docsets) with detailed information. Note: The search_docs tool already shows available docsets in its description, so this tool is mainly for getting additional metadata.", + inputSchema: { + type: "object", + properties: {}, + additionalProperties: false, }, - ], - }; - } + }, + ], + }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { @@ -222,7 +249,21 @@ Use the path and search terms with your text search tools (grep, rg, ripgrep, fi } // Load configuration - const { config, configPath } = await getConfiguration(); + const configData = await getConfiguration(); + if (!configData) { + throw new Error( + "No configuration file found.\n\n" + + "To configure docsets:\n\n" + + "**Option 1: Use CLI (recommended)**\n" + + "agentic-knowledge create --preset git-repo --id my-docs --name \"My Docs\" --url \n" + + "agentic-knowledge init my-docs\n\n" + + "**Option 2: Manual configuration**\n" + + "Create .knowledge/config.yaml in your project root.\n" + + "See the search_docs tool description for example configuration.", + ); + } + + const { config, configPath } = configData; // Find the requested docset const docset = config.docsets.find((d) => d.id === docset_id); @@ -293,7 +334,37 @@ Use the path and search terms with your text search tools (grep, rg, ripgrep, fi case "list_docsets": { // Load configuration - const { config, configPath } = await getConfiguration(); + const configData = await getConfiguration(); + if (!configData) { + return { + content: [ + { + type: "text", + text: + "No docsets configured.\n\n" + + "To configure docsets:\n\n" + + "**Option 1: Use CLI (recommended)**\n" + + "```bash\n" + + "agentic-knowledge create --preset git-repo --id my-docs --name \"My Docs\" --url \n" + + "agentic-knowledge init my-docs\n" + + "```\n\n" + + "**Option 2: Manual configuration**\n" + + "Create `.knowledge/config.yaml`:\n" + + "```yaml\n" + + "version: \"1.0\"\n" + + "docsets:\n" + + " - id: my-docs\n" + + " name: My Documentation\n" + + " sources:\n" + + " - type: local_folder\n" + + " paths: [\"./docs\"]\n" + + "```", + }, + ], + }; + } + + const { config, configPath } = configData; // Return list of available docsets with calculated paths const docsets = await Promise.all( From c51408c07cb992cc19a982faac930e4124c9830b Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Nov 2025 06:26:11 +0000 Subject: [PATCH 03/11] docs: clarify init vs refresh and simplify status display - Add clear explanation of when to use init vs refresh - init: First-time setup or complete reset (destructive) - refresh: Smart incremental updates (preserves state) - Highlight key difference in both command sections - Remove recency indicators from status documentation - No more green/yellow/red color coding - Show initialization date instead of "last refreshed" - Simpler, clearer status output This addresses common confusion about when to use each command and simplifies the status display to focus on initialization state. --- README.md | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 03d46ff..ea2dda2 100644 --- a/README.md +++ b/README.md @@ -334,19 +334,23 @@ The `create` command: #### `init` - Initialize Docset Sources -Initialize a configured docset by downloading and preparing documentation: +Initialize a configured docset by downloading and preparing documentation. Use this for **first-time setup**. ```bash # Initialize a specific docset agentic-knowledge init mcp-sdk -# Force re-initialization +# Force re-initialization (start completely fresh) agentic-knowledge init mcp-sdk --force # Use custom config path agentic-knowledge init mcp-sdk --config /path/to/config.yaml ``` +**When to use:** +- Setting up a docset for the first time +- With `--force`: Completely reset a docset (deletes everything and re-downloads) + **What happens during initialization:** 1. **For Git Repositories:** @@ -392,26 +396,19 @@ agentic-knowledge status --verbose agentic-knowledge status --config /path/to/config.yaml ``` -**Status indicators:** -- βœ… Green: Updated within 24 hours -- ⚠️ Yellow: Updated 1-7 days ago -- πŸ”„ Red: Updated >7 days ago or not initialized - **Example output:** ``` πŸ“Š Docset Status -βœ… mcp-sdk (MCP TypeScript SDK) +mcp-sdk (MCP TypeScript SDK) Initialized | 45 files | 2 source(s) loaded - Last refreshed: 2 hours ago + Initialized: 2024-11-20 -⚠️ react-docs (React Documentation) +react-docs (React Documentation) Initialized | 120 files | 1 source(s) loaded - Last refreshed: 3 days ago - - πŸ’‘ Consider running: agentic-knowledge refresh react-docs + Initialized: 2024-11-15 -πŸ”„ api-docs (API Documentation) +api-docs (API Documentation) Not initialized | 1 source(s) configured πŸ’‘ Run: agentic-knowledge init api-docs @@ -419,7 +416,7 @@ agentic-knowledge status --config /path/to/config.yaml #### `refresh` - Update Documentation -Refresh docset sources to get the latest content: +Update already-initialized docsets with latest content. This is a **smart, incremental update**. ```bash # Refresh all docsets @@ -428,7 +425,7 @@ agentic-knowledge refresh # Refresh specific docset agentic-knowledge refresh mcp-sdk -# Force refresh (ignore cache) +# Force refresh (ignore throttle) agentic-knowledge refresh mcp-sdk --force # Use custom config @@ -437,15 +434,18 @@ agentic-knowledge refresh --config /path/to/config.yaml **Smart refresh logic:** - Checks Git commit hash to detect changes -- Skips refresh if updated within 1 hour (unless `--force`) - Skips refresh if no changes detected -- Creates backup before refresh -- Updates metadata with new timestamp +- Skips refresh if updated within 1 hour (unless `--force`) +- Updates in place (preserves metadata) + +**When to use:** +- Getting latest updates from git repositories +- Routine maintenance/updates +- Checking for new content -**When to refresh:** -- Git repository has new commits -- It's been several days since last update -- You want to ensure latest content +**Key difference from `init --force`:** +- `init --force`: Deletes everything and starts fresh (destructive) +- `refresh`: Checks for changes and updates incrementally (smart) ### Complete Workflow Example From 5fe4746a69d6fe82fae0c231b6407cafa280acaa Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Nov 2025 06:29:23 +0000 Subject: [PATCH 04/11] feat: simplify status output to show dates instead of recency - Remove color-coded recency indicators (green/yellow/red) - Show initialization date in YYYY-MM-DD format instead of "X ago" - Match simpler format documented in README - Cleaner, less judgmental status display Output now shows: - Docset name and ID - File count and source count - Initialization date - For non-initialized: helpful init command This removes the arbitrary recency thresholds and focuses on factual information about when docsets were initialized. --- packages/cli/src/commands/status.ts | 41 ++++++++++------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/packages/cli/src/commands/status.ts b/packages/cli/src/commands/status.ts index f8e639f..60488da 100644 --- a/packages/cli/src/commands/status.ts +++ b/packages/cli/src/commands/status.ts @@ -179,10 +179,14 @@ function displaySummary(statuses: DocsetStatus[]) { if (!initialized) { console.log( - `${chalk.yellow("⚠️")} ${chalk.bold(docset.id)} - ${chalk.yellow("Not initialized")}`, + `${chalk.bold(docset.id)} (${docset.name})`, ); console.log( - chalk.gray(` ${docset.sources?.length || 0} source(s) configured`), + chalk.gray(` Not initialized | ${docset.sources?.length || 0} source(s) configured`), + ); + console.log(); + console.log( + chalk.blue(` πŸ’‘ Run: agentic-knowledge init ${docset.id}`), ); continue; } @@ -194,38 +198,21 @@ function displaySummary(statuses: DocsetStatus[]) { continue; } - // Calculate status - const lastActivity = metadata.last_refreshed || metadata.initialized_at; - const lastActivityTime = new Date(lastActivity); - const timeSince = Date.now() - lastActivityTime.getTime(); - const hoursSince = timeSince / (1000 * 60 * 60); - const daysSince = timeSince / (1000 * 60 * 60 * 24); - - let timeDisplay; - let statusIcon; - - if (hoursSince < 1) { - timeDisplay = `${Math.round(hoursSince * 60)} minutes ago`; - statusIcon = chalk.green("βœ…"); - } else if (hoursSince < 24) { - timeDisplay = `${Math.round(hoursSince)} hours ago`; - statusIcon = chalk.green("βœ…"); - } else if (daysSince < 7) { - timeDisplay = `${Math.round(daysSince)} days ago`; - statusIcon = chalk.yellow("⚠️"); - } else { - timeDisplay = `${Math.round(daysSince)} days ago`; - statusIcon = chalk.red("πŸ”„"); - } + // Format initialization date + const initDate = new Date(metadata.initialized_at); + const dateDisplay = initDate.toISOString().split("T")[0]; // YYYY-MM-DD format console.log( - `${statusIcon} ${chalk.bold(docset.id)} - ${chalk.gray(metadata.total_files)} files`, + `${chalk.bold(docset.id)} (${docset.name})`, ); console.log( chalk.gray( - ` Last updated: ${timeDisplay} | ${sources.length}/${metadata.sources_count} sources loaded`, + ` Initialized | ${metadata.total_files} files | ${sources.length}/${metadata.sources_count} source(s) loaded`, ), ); + console.log( + chalk.gray(` Initialized: ${dateDisplay}`), + ); } } From b5c9a01ed3036c0f451ce611617fb2c169276337 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Nov 2025 06:36:15 +0000 Subject: [PATCH 05/11] docs: refactor documentation into concise README and detailed user guide - Streamline README to focus on value proposition and quick start - New tagline: "Search any documentation as if you had written it yourself" - Clear "What Is This For?" section with use cases - Minimal quick start (4 simple steps) - Move philosophy/RAG comparison to "How and Why It Works" section - Create comprehensive USER_GUIDE.md with detailed usage - Complete CLI command documentation - Full docset lifecycle explanation - Configuration guide with all options - Complete workflow examples - MCP integration details - Troubleshooting section - Remove redundant content from README - No more 500+ line README mixing everything - Philosophy kept but moved to appropriate section - Essential CLI commands with link to user guide This makes the README scannable and approachable while preserving all detailed documentation in a dedicated guide. --- README.md | 716 +++++++------------------------------------------- USER_GUIDE.md | 540 +++++++++++++++++++++++++++++++++++++ 2 files changed, 639 insertions(+), 617 deletions(-) create mode 100644 USER_GUIDE.md diff --git a/README.md b/README.md index ea2dda2..eb5b319 100644 --- a/README.md +++ b/README.md @@ -1,639 +1,166 @@ # 🧠 Agentic Knowledge -A way to handle knowledge cut-off: Provide all sources you and your development team need to your agents. +**Search any documentation as if you had written it yourself** -
-

The End of RAG. The Dawn of Agentic Search. Maybe.

-

Intelligent navigation instructions that guide AI assistants through documentation using filesystem-like exploration instead of traditional retrieval

- - - License - - - LinkedIn - -
+An MCP server that guides AI assistants to navigate documentation using their built-in tools (grep, file reading) instead of traditional RAG. Leverages massive context windows and agentic search patterns for precise, intelligent documentation discovery. --- -## 🎯 What Is This? - -**Agentic Knowledge** represents a fundamental paradigm shift away from traditional Retrieval-Augmented Generation (RAG) toward **agentic search patterns**. Instead of chunking documents, computing embeddings, and retrieving fragments, this system provides **intelligent navigation instructions** that leverage the AI agent's existing tools (grep, ripgrep, file reading) and the explosion of context windows. - -### The Core Insight - -Modern AI assistants are **context-rich** (200K+ tokens) and equipped with powerful filesystem tools. Rather than building complex search infrastructure, we can guide them to navigate documentation intelligentlyβ€”just like Claude Code revolutionized code analysis by ditching RAG for direct filesystem exploration. - -## πŸͺ¦ Why RAG is Dead - -_Inspired by [The RAG Obituary](https://www.nicolasbustamante.com/p/the-rag-obituary-killed-by-agents) by Nicolas Bustamante_ - -Traditional RAG was a brilliant workaround for the **context-poor era** (GPT-4's 8K tokens). But it came with fundamental limitations: - -### The RAG Problem Stack - -``` -❌ Chunking destroys document relationships -❌ Embeddings fail on precise terminology -❌ Similarity search misses exact matches -❌ Reranking adds latency and complexity -❌ Context fragmentation loses coherence -❌ Infrastructure burden is massive -``` - -### The Agentic Solution - -``` -βœ… Direct filesystem navigation -βœ… Intelligent reference following -βœ… Complete document context -βœ… Zero infrastructure overhead -βœ… Sub-10ms response times -βœ… Deterministic, precise results -``` - -## πŸ”„ From Retrieval to Navigation - -Traditional RAG says: _"Here are 50 fragments that mention your keywords"_ - -Agentic Knowledge says: _"Search for 'useState' in `./docs/react-18.2/hooks/`. If that doesn't help, try 'state management' in `./docs/patterns/`. Follow any 'See also' references you find."_ - -The difference? **Guidance over fragments. Investigation over retrieval.** - -## πŸ— How It Works - -### MCP Server Integration - -Implements the [Model Context Protocol](https://modelcontextprotocol.io/) with two core tools: - -```typescript -// Get navigation guidance for specific queries -search_docs({ - docset: "react-docs", - keywords: ["useEffect", "cleanup"], - generalized_keywords: ["lifecycle", "memory"], -}); - -// Discover available documentation sets -list_docsets(); -``` - -### Configuration-Driven Intelligence - -Simple `.knowledge/config.yaml` pattern: - -```yaml -version: "1.0" -docsets: - - id: react-docs - name: React Documentation - description: "React framework documentation" - sources: - - type: local_folder - paths: ["./docs/react-18.2"] - template: | - Search for '{{keywords}}' in {{local_path}}/hooks/. - If not found, try '{{generalized_keywords}}' in {{local_path}}/patterns/. - Follow any cross-references you discover. -``` - -### The Navigation Response - -Instead of document fragments, you get **actionable instructions**: - -``` -Based on your React useEffect cleanup query: - -1. Start with `./docs/react-18.2/hooks/effect.md` - contains useEffect fundamentals -2. Search for "cleanup function" patterns in `./docs/react-18.2/patterns/` -3. Check `./examples/lifecycle/` for practical cleanup implementations -4. Review `./docs/react-18.2/performance/memory.md` for memory leak prevention - -Focus on the cleanup function return pattern and dependency array management. -``` - -## πŸš€ Why This Matters - -### The Context Revolution - -- **2022**: GPT-4 had 8K tokens (~12 pages) -- **2025**: Claude Sonnet has 200K tokens (~700 pages) -- **Future**: Heading toward 2M+ tokens (~6,000 pages) - -### The Tool Evolution - -AI assistants now have sophisticated filesystem tools: - -- **Grep/Ripgrep**: Lightning-fast regex search through files -- **Glob**: Direct file discovery by patterns -- **Direct File Access**: Read complete documents in context -- **Reference Following**: Navigate cross-references naturally +## 🎯 What Is This For? -### The Infrastructure Shift +Give your AI assistant access to any documentationβ€”yours or third-partyβ€”so it can find answers as naturally as you would. No embeddings, no vector databases, no complex infrastructure. -- **RAG**: Elasticsearch clusters, embedding models, rerankers, vector databases -- **Agentic**: Simple YAML config, zero infrastructure, filesystem tools - -## 🎯 Core Principles - -### 1. **Guidance Over Search** - -Provide intelligent navigation instructions instead of search results - -### 2. **Context Abundance** - -Leverage massive context windows instead of working around limitations - -### 3. **Tool Evolution Compatibility** - -Instructions remain stable as agent capabilities evolve (grep β†’ AST parsing β†’ future tools) - -### 4. **Zero AI Dependency** - -Pure logic-based guidance for reliability and speed - -### 5. **Investigation Over Retrieval** - -Agents follow references and build understanding incrementally +**Perfect for:** +- πŸ“š **Project documentation** - Your team's internal docs, APIs, guides +- πŸ”§ **Framework references** - React, TypeScript, MCP SDK, any library +- 🏒 **Enterprise knowledge** - Company wikis, architecture docs, runbooks +- 🌐 **Open source projects** - Clone any repo's docs for instant access ## πŸš€ Quick Start -### Installation +### 1. Install ```bash npm install -g agentic-knowledge -# or -npx agentic-knowledge -``` - -### Basic Setup - -1. **Create configuration directory**: - -```bash -mkdir .knowledge ``` -2. **Add configuration** (`.knowledge/config.yaml`): +### 2. Configure an MCP Client -```yaml -version: "1.0" -docsets: - - id: my-docs - name: My Project Documentation - description: "Local project documentation" - sources: - - type: local_folder - paths: ["./docs"] - - - id: react-docs - name: React Documentation - description: "Official React documentation from GitHub" - sources: - - type: git_repo - url: "https://github.com/facebook/react.git" - branch: "main" - paths: ["docs/"] -``` - -3. **Start the MCP server**: - -```bash -agentic-knowledge -``` - -4. **Connect your AI assistant** using MCP protocol - -## πŸ“‹ Configuration Guide - -### Local Folder Sources - -For documentation stored locally in your project: - -```yaml -docsets: - - id: my-project - name: My Project Docs - sources: - - type: local_folder - paths: - - "./docs" # Single directory - - "./guides" # Multiple directories - - "./api/README.md" # Specific files -``` - -**Benefits:** - -- βœ… **No file duplication** - creates symlinks to original locations -- βœ… **Real-time updates** - changes immediately visible -- βœ… **Relative paths** - returns clean relative paths for LLM navigation - -### Git Repository Sources - -For documentation from remote repositories: - -```yaml -docsets: - - id: external-docs - name: External Documentation - sources: - - type: git_repo - url: "https://github.com/owner/repo.git" - branch: "main" # Optional, defaults to main - paths: ["docs/", "README.md"] # Optional, extracts specific paths -``` - -**Benefits:** - -- βœ… **Automatic downloads** - fetches latest documentation -- βœ… **Selective extraction** - only downloads specified paths -- βœ… **Branch selection** - target specific branches or tags - -### Mixed Configuration - -Combine local and remote sources in one configuration: - -```yaml -version: "1.0" -docsets: - - id: local-guides - name: Local User Guides - sources: - - type: local_folder - paths: ["./docs/guides"] - - - id: api-reference - name: API Reference - sources: - - type: git_repo - url: "https://github.com/company/api-docs.git" - paths: ["reference/"] - - - id: mixed-sources - name: Combined Documentation - sources: - - type: local_folder - paths: ["./internal-docs"] - - type: git_repo - url: "https://github.com/external/docs.git" -``` - -## πŸ› οΈ CLI Commands & Docset Lifecycle - -The `agentic-knowledge` CLI provides commands to manage your documentation lifecycle. When you run `agentic-knowledge` without arguments, it starts the MCP server. With arguments, it executes CLI commands. - -### Understanding the Docset Lifecycle - -A docset goes through the following phases: - -``` -1. CREATE β†’ Configure docset (manually edit config.yaml or use CLI presets) -2. INITIALIZE β†’ Download and prepare documentation files -3. USE β†’ Search and navigate via MCP server -4. REFRESH β†’ Update documentation as needed -``` - -### CLI Commands - -#### `create` - Create New Docset Configuration +Add to your Claude Desktop or other MCP client configuration: -Create docset configurations quickly using presets. Alternatively, you can manually edit `.knowledge/config.yaml` - this command is just a convenience tool that does it for you. - -**Git Repository Preset:** -```bash -agentic-knowledge create \ - --preset git-repo \ - --id mcp-sdk \ - --name "MCP TypeScript SDK" \ - --url https://github.com/modelcontextprotocol/typescript-sdk.git \ - --branch main -``` - -**Local Folder Preset:** -```bash -agentic-knowledge create \ - --preset local-folder \ - --id my-docs \ - --name "My Documentation" \ - --path ./docs -``` - -**Options:** -- `--preset `: Choose preset (`git-repo` or `local-folder`) -- `--id `: Unique identifier for the docset -- `--name `: Human-readable name -- `--url `: Git repository URL (for git-repo preset) -- `--branch `: Git branch (optional, defaults to main) -- `--path `: Local directory path (for local-folder preset) - -The `create` command: -- βœ… Creates or updates `.knowledge/config.yaml` -- βœ… Validates docset ID uniqueness -- βœ… For local folders, creates symlinks immediately -- βœ… For git repos, prepares configuration for initialization - -#### `init` - Initialize Docset Sources - -Initialize a configured docset by downloading and preparing documentation. Use this for **first-time setup**. - -```bash -# Initialize a specific docset -agentic-knowledge init mcp-sdk - -# Force re-initialization (start completely fresh) -agentic-knowledge init mcp-sdk --force - -# Use custom config path -agentic-knowledge init mcp-sdk --config /path/to/config.yaml -``` - -**When to use:** -- Setting up a docset for the first time -- With `--force`: Completely reset a docset (deletes everything and re-downloads) - -**What happens during initialization:** - -1. **For Git Repositories:** - - Clones repository to temporary directory - - Extracts specified paths (if configured) - - Applies smart filtering (excludes `node_modules/`, build artifacts, etc.) - - Copies documentation to `.knowledge/docsets/{id}/` - - Creates metadata files for change tracking - -2. **For Local Folders:** - - Creates symlinks in `.knowledge/docsets/{id}/` - - No file duplication - - Changes are immediately visible - -3. **Creates Metadata:** - - `.agentic-metadata.json` - Overall docset information - - `.agentic-source-{index}.json` - Per-source tracking with content hashes - -**Directory structure after init:** -``` -.knowledge/ -β”œβ”€β”€ config.yaml -β”œβ”€β”€ .gitignore (auto-created) -└── docsets/ - └── mcp-sdk/ - β”œβ”€β”€ .agentic-metadata.json - β”œβ”€β”€ .agentic-source-0.json - └── [documentation files...] -``` - -#### `status` - Check Docset Status - -View the status of all docsets and their sources: - -```bash -# Basic status -agentic-knowledge status - -# Detailed status with source information -agentic-knowledge status --verbose - -# Use custom config -agentic-knowledge status --config /path/to/config.yaml -``` - -**Example output:** -``` -πŸ“Š Docset Status - -mcp-sdk (MCP TypeScript SDK) - Initialized | 45 files | 2 source(s) loaded - Initialized: 2024-11-20 - -react-docs (React Documentation) - Initialized | 120 files | 1 source(s) loaded - Initialized: 2024-11-15 - -api-docs (API Documentation) - Not initialized | 1 source(s) configured - - πŸ’‘ Run: agentic-knowledge init api-docs -``` - -#### `refresh` - Update Documentation - -Update already-initialized docsets with latest content. This is a **smart, incremental update**. - -```bash -# Refresh all docsets -agentic-knowledge refresh - -# Refresh specific docset -agentic-knowledge refresh mcp-sdk - -# Force refresh (ignore throttle) -agentic-knowledge refresh mcp-sdk --force - -# Use custom config -agentic-knowledge refresh --config /path/to/config.yaml +```json +{ + "mcpServers": { + "agentic-knowledge": { + "command": "agentic-knowledge" + } + } +} ``` -**Smart refresh logic:** -- Checks Git commit hash to detect changes -- Skips refresh if no changes detected -- Skips refresh if updated within 1 hour (unless `--force`) -- Updates in place (preserves metadata) +### 3. Set Up Your First Docset -**When to use:** -- Getting latest updates from git repositories -- Routine maintenance/updates -- Checking for new content - -**Key difference from `init --force`:** -- `init --force`: Deletes everything and starts fresh (destructive) -- `refresh`: Checks for changes and updates incrementally (smart) - -### Complete Workflow Example - -Here's a complete workflow from scratch: +**Option A: Use the CLI (Recommended)** ```bash -# 1. Create docset for a Git repository +# For a Git repository agentic-knowledge create \ --preset git-repo \ --id react-docs \ --name "React Documentation" \ - --url https://github.com/facebook/react.git \ - --branch main + --url https://github.com/facebook/react.git -# 2. Initialize the docset (downloads docs) +# Initialize (downloads the docs) agentic-knowledge init react-docs -# 3. Check status -agentic-knowledge status - -# 4. Start MCP server (for AI assistant) +# Start the MCP server agentic-knowledge - -# Later: Update documentation -agentic-knowledge refresh react-docs ``` -## 🎯 How to Use +**Option B: Manual Configuration** -### Step 1: Set Up Your Documentation - -Create a `.knowledge/config.yaml` file in your project root: +Create `.knowledge/config.yaml`: ```yaml version: "1.0" docsets: - - id: my-project + - id: my-docs name: My Project Documentation - description: "Main project documentation" sources: - type: local_folder - paths: ["./docs", "./README.md"] -``` - -### Step 2: Start the MCP Server - -```bash -# Install globally -npm install -g agentic-knowledge - -# Start the server -agentic-knowledge + paths: ["./docs"] ``` -The server will: - -- βœ… Create symlinks for local folders in `.knowledge/docsets/` -- βœ… Validate your configuration -- βœ… Start listening for MCP requests - -### Step 3: Connect Your AI Assistant - -Configure your AI assistant (Claude Desktop, etc.) to use the MCP server: - -```json -{ - "mcpServers": { - "agentic-knowledge": { - "command": "agentic-knowledge" - } - } -} -``` +Then start: `agentic-knowledge` -### Step 4: Search Your Documentation +### 4. Use It -Use the `search_docs` tool in your AI assistant: +Your AI assistant now has access to `search_docs` and `list_docsets` tools. Ask questions naturally: ``` -search_docs({ - docset_id: "my-project", - keywords: "authentication setup", - generalized_keywords: "login, auth, security" -}) +"How do I implement a cleanup function in React useEffect?" +"Show me the authentication setup in our docs" +"Find examples of rate limiting in the API docs" ``` -**Response:** +The assistant will receive intelligent navigation instructions and use grep/file reading to find the exact information. -``` -# πŸ“š Search My Project Documentation +## πŸ“– Documentation -**Primary terms:** authentication setup -**Related terms:** login, auth, security -**Location:** docs +- **[User Guide](./USER_GUIDE.md)** - Detailed CLI commands, lifecycle, configuration +- **[Examples](./examples/)** - Configuration examples and integration guides +- **[Testing Guide](./TESTING.md)** - Comprehensive testing documentation -## πŸ” Search Strategy +## πŸ’‘ How and Why It Works -1. **Start with Specific Terms** - Use your text search tools (grep, rg, ripgrep) to search for: `authentication setup` +### The Paradigm Shift -2. **Expand to Related Terms** - If initial search doesn't yield results, try: `login, auth, security` +Traditional RAG (Retrieval-Augmented Generation) was built for the **context-poor era** when models had 8K token limits. It: +- Chunks documents (losing relationships) +- Computes embeddings (missing precise terminology) +- Retrieves fragments (losing context) +- Requires massive infrastructure (vector DBs, rerankers) -3. **What to Avoid** - Skip these directories: `node_modules/`, `.git/`, `.knowledge/` -``` +**Agentic Knowledge** leverages modern AI capabilities: +- βœ… **200K+ token context windows** - Can read entire documentation sets +- βœ… **Powerful filesystem tools** - grep, ripgrep, file reading built-in +- βœ… **Intelligent navigation** - Provides search strategies, not fragments +- βœ… **Zero infrastructure** - Just a config file and your docs -### Step 5: Follow the Guidance +### From Retrieval to Navigation -Your AI assistant will use the provided search strategy to: +**Traditional RAG says:** +*"Here are 50 fragments that mention your keywords"* -1. πŸ” Search your documentation with the suggested terms -2. πŸ“‚ Navigate to the right files and directories -3. 🎯 Find exactly what you're looking for -4. πŸ”— Follow cross-references and related content +**Agentic Knowledge says:** +*"Search for 'useState' in `./docs/react-18.2/hooks/`. If that doesn't help, try 'state management' in `./docs/patterns/`. Follow any 'See also' references you find."* -## πŸ’‘ Pro Tips +**The difference?** Guidance over fragments. Investigation over retrieval. -### Local Development Workflow +### How It Actually Works -```yaml -# Perfect for active development -docsets: - - id: current-project - name: Current Project - sources: - - type: local_folder - paths: ["./docs", "./README.md", "./CHANGELOG.md"] -``` +1. **Configure docsets** - Point to local folders or Git repositories +2. **Initialize** - Downloads/symlinks documentation to `.knowledge/docsets/` +3. **MCP server** - Exposes `search_docs` and `list_docsets` tools +4. **AI searches** - Gets navigation instructions, uses grep/file tools +5. **Finds answers** - Reads complete documents with full context -**Benefits:** +**Performance:** +- **Setup**: Seconds (vs hours for RAG indexing) +- **Response**: <10ms (vs 300-2000ms for RAG) +- **Infrastructure**: None (vs Elasticsearch + Vector DB) +- **Accuracy**: Complete context (vs fragment-based) -- Changes in your docs are immediately available -- No copying or syncing needed -- Works with any file type +### Inspired By -### Multi-Repository Setup +This approach is inspired by [The RAG Obituary](https://www.nicolasbustamante.com/p/the-rag-obituary-killed-by-agents) by Nicolas Bustamante and how Claude Code revolutionized code analysis by ditching RAG for direct filesystem exploration. -```yaml -# Combine multiple sources -docsets: - - id: frontend-docs - name: Frontend Documentation - sources: - - type: local_folder - paths: ["./frontend/docs"] - - type: git_repo - url: "https://github.com/company/design-system.git" - paths: ["docs/"] +## πŸ›  Essential CLI Commands - - id: backend-docs - name: Backend Documentation - sources: - - type: git_repo - url: "https://github.com/company/api-docs.git" - branch: "main" +**Lifecycle:** +``` +CREATE β†’ INITIALIZE β†’ USE β†’ REFRESH ``` -### Advanced Search Strategies +**Commands:** +```bash +# Create a docset configuration +agentic-knowledge create --preset git-repo --id my-docs --name "My Docs" --url -Use specific and generalized keywords for better results: +# Initialize (download docs) +agentic-knowledge init my-docs -```javascript -// βœ… Good: Specific + General -search_docs({ - docset_id: "react-docs", - keywords: "useEffect cleanup function", - generalized_keywords: "hooks, lifecycle, memory management", -}); +# Check status +agentic-knowledge status -// ❌ Too vague -search_docs({ - docset_id: "react-docs", - keywords: "react", - generalized_keywords: "javascript", -}); -``` +# Update docs +agentic-knowledge refresh my-docs -## πŸ“Š Performance vs RAG +# Start MCP server +agentic-knowledge +``` -| Metric | Traditional RAG | Agentic Knowledge | -| ------------------ | ------------------------- | ----------------- | -| **Setup Time** | Hours (indexing) | Seconds (config) | -| **Response Time** | 300-2000ms | <10ms | -| **Infrastructure** | Elasticsearch + Vector DB | Zero | -| **Maintenance** | High (reindexing) | None | -| **Accuracy** | Fragment-based | Complete context | -| **Cost** | High (compute) | Minimal | +See the [User Guide](./USER_GUIDE.md) for complete command documentation. ## πŸ”¬ The Future of Knowledge Systems @@ -641,44 +168,17 @@ We're entering the **post-retrieval age**. The winners won't be those with the b **RAG was training wheels**β€”useful, necessary, but temporary. The future belongs to systems that read, navigate, and reason end-to-end. -## πŸš€ Local Development & Installation - -### Installing from Source (Before NPM Publication) - -Since the packages aren't published to npm yet, you can install them locally: - -1. **Build all packages:** - - ```bash - pnpm install - pnpm build - ``` - -2. **Create local installation packages:** - - ```bash - pnpm run pack:local - ``` - - This creates `dist-local/` directory with packages that have workspace dependencies converted to relative file paths. - -3. **Install the MCP server locally:** - - ```bash - # Option 1: Install from tarball - cd dist-local/mcp-server && npm pack - npm install -g codemcp-knowledge-mcp-server-0.1.0.tgz +## πŸ§ͺ Development Status - # Option 2: Install directly from directory - npm install -g ./dist-local/mcp-server/ - ``` +**Current Phase**: Finalization βœ… -4. **Verify installation:** - ```bash - agentic-knowledge --help - ``` +- βœ… Core implementation complete (107 tests passing) +- βœ… MCP protocol compliance verified +- βœ… Performance validated (0.47ms response time) +- βœ… Full documentation and examples +- ⚠️ Ready for community feedback and real-world testing -### Development +## πŸš€ Local Development ```bash # Install dependencies @@ -692,27 +192,9 @@ pnpm test # Build all packages pnpm build - -# Format and lint -pnpm format -pnpm lint ``` -## πŸ§ͺ Development Status - -**Current Phase**: Finalization βœ… - -- βœ… Core implementation complete (107 tests passing) -- βœ… MCP protocol compliance verified -- βœ… Performance validated (0.47ms response time) -- βœ… Full documentation and examples -- ⚠️ Ready for community feedback and real-world testing - -## πŸ“š Examples & Documentation - -- [`examples/`](./examples/) - Configuration examples and integration guides -- [`TESTING.md`](./TESTING.md) - Comprehensive testing documentation -- [Architecture docs](./.vibe/docs/) - Detailed technical specifications +See [User Guide](./USER_GUIDE.md) for installation from source. ## 🀝 Contributing diff --git a/USER_GUIDE.md b/USER_GUIDE.md new file mode 100644 index 0000000..5b5b35d --- /dev/null +++ b/USER_GUIDE.md @@ -0,0 +1,540 @@ +# Agentic Knowledge User Guide + +Complete guide to using Agentic Knowledge for managing and searching documentation. + +## Table of Contents + +- [Installation](#installation) +- [Docset Lifecycle](#docset-lifecycle) +- [CLI Commands](#cli-commands) +- [Configuration Guide](#configuration-guide) +- [Complete Workflows](#complete-workflows) +- [MCP Integration](#mcp-integration) +- [Troubleshooting](#troubleshooting) + +## Installation + +### From NPM (Recommended) + +```bash +npm install -g agentic-knowledge +``` + +### From Source + +Since the packages aren't published to npm yet, you can install them locally: + +1. **Build all packages:** + + ```bash + pnpm install + pnpm build + ``` + +2. **Create local installation packages:** + + ```bash + pnpm run pack:local + ``` + + This creates `dist-local/` directory with packages that have workspace dependencies converted to relative file paths. + +3. **Install the MCP server locally:** + + ```bash + # Option 1: Install from tarball + cd dist-local/mcp-server && npm pack + npm install -g codemcp-knowledge-mcp-server-0.1.0.tgz + + # Option 2: Install directly from directory + npm install -g ./dist-local/mcp-server/ + ``` + +4. **Verify installation:** + ```bash + agentic-knowledge --help + ``` + +## Docset Lifecycle + +A docset goes through the following phases: + +``` +1. CREATE β†’ Configure docset (manually edit config.yaml or use CLI presets) +2. INITIALIZE β†’ Download and prepare documentation files +3. USE β†’ Search and navigate via MCP server +4. REFRESH β†’ Update documentation as needed +``` + +### Phase 1: CREATE + +Define a docset in `.knowledge/config.yaml` either manually or using the CLI `create` command. + +### Phase 2: INITIALIZE + +Download and prepare documentation files to make them searchable. For git repos, this clones and filters the content. For local folders, this creates symlinks. + +### Phase 3: USE + +The MCP server exposes the docsets to AI assistants via the `search_docs` and `list_docsets` tools. + +### Phase 4: REFRESH + +Update already-initialized docsets with the latest content from their sources. + +## CLI Commands + +The `agentic-knowledge` CLI provides commands to manage your documentation lifecycle. When you run `agentic-knowledge` without arguments, it starts the MCP server. With arguments, it executes CLI commands. + +### `create` - Create New Docset Configuration + +Create docset configurations quickly using presets. Alternatively, you can manually edit `.knowledge/config.yaml` - this command is just a convenience tool that does it for you. + +**Git Repository Preset:** +```bash +agentic-knowledge create \ + --preset git-repo \ + --id mcp-sdk \ + --name "MCP TypeScript SDK" \ + --url https://github.com/modelcontextprotocol/typescript-sdk.git \ + --branch main +``` + +**Local Folder Preset:** +```bash +agentic-knowledge create \ + --preset local-folder \ + --id my-docs \ + --name "My Documentation" \ + --path ./docs +``` + +**Options:** +- `--preset `: Choose preset (`git-repo` or `local-folder`) +- `--id `: Unique identifier for the docset +- `--name `: Human-readable name +- `--url `: Git repository URL (for git-repo preset) +- `--branch `: Git branch (optional, defaults to main) +- `--path `: Local directory path (for local-folder preset) + +The `create` command: +- βœ… Creates or updates `.knowledge/config.yaml` +- βœ… Validates docset ID uniqueness +- βœ… For local folders, creates symlinks immediately +- βœ… For git repos, prepares configuration for initialization + +### `init` - Initialize Docset Sources + +Initialize a configured docset by downloading and preparing documentation. Use this for **first-time setup**. + +```bash +# Initialize a specific docset +agentic-knowledge init mcp-sdk + +# Force re-initialization (start completely fresh) +agentic-knowledge init mcp-sdk --force + +# Use custom config path +agentic-knowledge init mcp-sdk --config /path/to/config.yaml +``` + +**When to use:** +- Setting up a docset for the first time +- With `--force`: Completely reset a docset (deletes everything and re-downloads) + +**What happens during initialization:** + +1. **For Git Repositories:** + - Clones repository to temporary directory + - Extracts specified paths (if configured) + - Applies smart filtering (excludes `node_modules/`, build artifacts, etc.) + - Copies documentation to `.knowledge/docsets/{id}/` + - Creates metadata files for change tracking + +2. **For Local Folders:** + - Creates symlinks in `.knowledge/docsets/{id}/` + - No file duplication + - Changes are immediately visible + +3. **Creates Metadata:** + - `.agentic-metadata.json` - Overall docset information + - `.agentic-source-{index}.json` - Per-source tracking with content hashes + +**Directory structure after init:** +``` +.knowledge/ +β”œβ”€β”€ config.yaml +β”œβ”€β”€ .gitignore (auto-created) +└── docsets/ + └── mcp-sdk/ + β”œβ”€β”€ .agentic-metadata.json + β”œβ”€β”€ .agentic-source-0.json + └── [documentation files...] +``` + +### `status` - Check Docset Status + +View the status of all docsets and their sources: + +```bash +# Basic status +agentic-knowledge status + +# Detailed status with source information +agentic-knowledge status --verbose + +# Use custom config +agentic-knowledge status --config /path/to/config.yaml +``` + +**Example output:** +``` +πŸ“Š Docset Status + +mcp-sdk (MCP TypeScript SDK) + Initialized | 45 files | 2 source(s) loaded + Initialized: 2024-11-20 + +react-docs (React Documentation) + Initialized | 120 files | 1 source(s) loaded + Initialized: 2024-11-15 + +api-docs (API Documentation) + Not initialized | 1 source(s) configured + + πŸ’‘ Run: agentic-knowledge init api-docs +``` + +### `refresh` - Update Documentation + +Update already-initialized docsets with latest content. This is a **smart, incremental update**. + +```bash +# Refresh all docsets +agentic-knowledge refresh + +# Refresh specific docset +agentic-knowledge refresh mcp-sdk + +# Force refresh (ignore throttle) +agentic-knowledge refresh mcp-sdk --force + +# Use custom config +agentic-knowledge refresh --config /path/to/config.yaml +``` + +**Smart refresh logic:** +- Checks Git commit hash to detect changes +- Skips refresh if no changes detected +- Skips refresh if updated within 1 hour (unless `--force`) +- Updates in place (preserves metadata) + +**When to use:** +- Getting latest updates from git repositories +- Routine maintenance/updates +- Checking for new content + +**Key difference from `init --force`:** +- `init --force`: Deletes everything and starts fresh (destructive) +- `refresh`: Checks for changes and updates incrementally (smart) + +## Configuration Guide + +### Configuration File Location + +Place your configuration file at `.knowledge/config.yaml` in your project root. + +### Local Folder Sources + +For documentation stored locally in your project: + +```yaml +docsets: + - id: my-project + name: My Project Docs + sources: + - type: local_folder + paths: + - "./docs" # Single directory + - "./guides" # Multiple directories + - "./api/README.md" # Specific files +``` + +**Benefits:** + +- βœ… **No file duplication** - creates symlinks to original locations +- βœ… **Real-time updates** - changes immediately visible +- βœ… **Relative paths** - returns clean relative paths for LLM navigation + +### Git Repository Sources + +For documentation from remote repositories: + +```yaml +docsets: + - id: external-docs + name: External Documentation + sources: + - type: git_repo + url: "https://github.com/owner/repo.git" + branch: "main" # Optional, defaults to main + paths: ["docs/", "README.md"] # Optional, extracts specific paths +``` + +**Benefits:** + +- βœ… **Automatic downloads** - fetches latest documentation +- βœ… **Selective extraction** - only downloads specified paths +- βœ… **Branch selection** - target specific branches or tags + +### Mixed Configuration + +Combine local and remote sources in one configuration: + +```yaml +version: "1.0" +docsets: + - id: local-guides + name: Local User Guides + sources: + - type: local_folder + paths: ["./docs/guides"] + + - id: api-reference + name: API Reference + sources: + - type: git_repo + url: "https://github.com/company/api-docs.git" + paths: ["reference/"] + + - id: mixed-sources + name: Combined Documentation + sources: + - type: local_folder + paths: ["./internal-docs"] + - type: git_repo + url: "https://github.com/external/docs.git" +``` + +### Advanced: Custom Search Templates + +You can customize the search instructions provided to AI assistants: + +```yaml +version: "1.0" +docsets: + - id: react-docs + name: React Documentation + description: "React framework documentation" + sources: + - type: local_folder + paths: ["./docs/react-18.2"] + template: | + Search for '{{keywords}}' in {{local_path}}/hooks/. + If not found, try '{{generalized_keywords}}' in {{local_path}}/patterns/. + Follow any cross-references you discover. +``` + +**Template variables:** +- `{{keywords}}` - Primary search terms +- `{{generalized_keywords}}` - Broader context terms +- `{{local_path}}` - Path to the docset + +## Complete Workflows + +### Workflow 1: Local Project Documentation + +```bash +# 1. Create config for local docs +agentic-knowledge create \ + --preset local-folder \ + --id my-project \ + --name "My Project Docs" \ + --path ./docs + +# 2. Check status +agentic-knowledge status + +# 3. Start MCP server +agentic-knowledge +``` + +No initialization needed - local folders use symlinks! + +### Workflow 2: External Git Repository + +```bash +# 1. Create docset for a Git repository +agentic-knowledge create \ + --preset git-repo \ + --id react-docs \ + --name "React Documentation" \ + --url https://github.com/facebook/react.git \ + --branch main + +# 2. Initialize the docset (downloads docs) +agentic-knowledge init react-docs + +# 3. Check status +agentic-knowledge status + +# 4. Start MCP server (for AI assistant) +agentic-knowledge + +# Later: Update documentation +agentic-knowledge refresh react-docs +``` + +### Workflow 3: Multi-Repository Setup + +```bash +# Set up multiple docsets +agentic-knowledge create --preset git-repo --id frontend-docs --name "Frontend Docs" --url https://github.com/company/frontend.git +agentic-knowledge create --preset git-repo --id backend-docs --name "Backend Docs" --url https://github.com/company/backend.git +agentic-knowledge create --preset local-folder --id internal-docs --name "Internal Docs" --path ./docs + +# Initialize git repos +agentic-knowledge init frontend-docs +agentic-knowledge init backend-docs + +# Check all statuses +agentic-knowledge status --verbose + +# Start server +agentic-knowledge +``` + +## MCP Integration + +### MCP Server + +When you run `agentic-knowledge` without arguments, it starts an MCP server that exposes two tools: + +#### `search_docs` Tool + +Get navigation guidance for specific queries: + +```typescript +search_docs({ + docset_id: "react-docs", + keywords: "useEffect cleanup", + generalized_keywords: "hooks lifecycle memory", +}); +``` + +**Returns:** +```json +{ + "instructions": "Search for 'useEffect cleanup' in .knowledge/docsets/react-docs/hooks/...", + "search_terms": "useEffect cleanup", + "generalized_search_terms": "hooks lifecycle memory", + "path": ".knowledge/docsets/react-docs" +} +``` + +#### `list_docsets` Tool + +Discover available documentation sets: + +```typescript +list_docsets(); +``` + +**Returns:** +``` +Found 2 available docset(s): + +**react-docs** (React Documentation) + Description: Official React documentation + Path: .knowledge/docsets/react-docs + +**my-docs** (My Project Documentation) + Description: Internal project documentation + Path: docs +``` + +### Configuring MCP Clients + +#### Claude Desktop + +Add to `~/Library/Application Support/Claude/claude_desktop_config.json`: + +```json +{ + "mcpServers": { + "agentic-knowledge": { + "command": "agentic-knowledge" + } + } +} +``` + +#### Other MCP Clients + +Provide the command `agentic-knowledge` as the MCP server command. The server uses stdio transport. + +### Using in AI Conversations + +Once configured, simply ask questions: + +``` +"How do I implement authentication in our API?" +"Show me examples of React hooks cleanup" +"Find the rate limiting configuration" +``` + +The AI assistant will: +1. Call `search_docs` with appropriate keywords +2. Receive navigation instructions +3. Use grep/ripgrep to search the documentation +4. Read relevant files with full context +5. Provide accurate answers + +## Troubleshooting + +### MCP Server Won't Start + +**Error**: "No configuration file found" + +**Solution**: Create `.knowledge/config.yaml` or the server will start with no docsets (shows setup instructions in tool descriptions). + +### Docset Not Initialized + +**Error**: "Docset 'X' is not initialized" + +**Solution**: Run `agentic-knowledge init X` + +### Git Clone Failures + +**Error**: "Failed to clone repository" + +**Solutions:** +- Check internet connection +- Verify repository URL is correct +- Ensure you have access to private repositories +- Try with `--branch` flag if default branch isn't `main` + +### Status Shows Old Data + +**Solution**: Run `agentic-knowledge refresh ` to update + +### Symlinks Not Working + +**Issue**: Local folder changes not reflected + +**Solutions:** +- Verify the source paths exist +- Check file permissions +- Re-run `agentic-knowledge create` with the local folder preset + +### Search Not Finding Results + +**Tips:** +- Try broader keywords with `generalized_keywords` +- Check the docset is initialized: `agentic-knowledge status` +- Verify the documentation actually contains the terms +- Use verbose status to see which files are included + +--- + +For more information, see the [README](./README.md) or check the [examples](./examples/) directory. From 92c4297b379a37fbda0b27b116f715d712b14f38 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Nov 2025 06:44:06 +0000 Subject: [PATCH 06/11] docs: update to use npx instead of global installation - Change all CLI examples to use npx instead of global install - Installation: npm install (local) or npx (no install) - All commands: npx agentic-knowledge - More aligned with modern MCP server practices - Add comprehensive Claude Desktop configuration - Show config file locations (macOS, Windows, Linux) - Option 1: npx (recommended, no installation needed) - Option 2: Project-specific with cwd - Option 3: Global installation (legacy) - Include -y flag to auto-confirm npx prompts - Update workflows to reflect automatic server startup - Server runs automatically via Claude Desktop - No need to manually start server - Clearer separation of CLI usage vs MCP server MCP servers are typically not installed globally - they run via npx in separate threads managed by the MCP client. --- README.md | 42 ++++++++++++------ USER_GUIDE.md | 120 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 116 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index eb5b319..d374152 100644 --- a/README.md +++ b/README.md @@ -21,18 +21,35 @@ Give your AI assistant access to any documentationβ€”yours or third-partyβ€”so i ### 1. Install ```bash -npm install -g agentic-knowledge +npm install agentic-knowledge +# or +npx agentic-knowledge ``` ### 2. Configure an MCP Client -Add to your Claude Desktop or other MCP client configuration: +Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS): ```json { "mcpServers": { "agentic-knowledge": { - "command": "agentic-knowledge" + "command": "npx", + "args": ["-y", "agentic-knowledge"] + } + } +} +``` + +Or for a local project installation: + +```json +{ + "mcpServers": { + "agentic-knowledge": { + "command": "npx", + "args": ["-y", "agentic-knowledge"], + "cwd": "/path/to/your/project" } } } @@ -44,17 +61,16 @@ Add to your Claude Desktop or other MCP client configuration: ```bash # For a Git repository -agentic-knowledge create \ +npx agentic-knowledge create \ --preset git-repo \ --id react-docs \ --name "React Documentation" \ --url https://github.com/facebook/react.git # Initialize (downloads the docs) -agentic-knowledge init react-docs +npx agentic-knowledge init react-docs -# Start the MCP server -agentic-knowledge +# The MCP server starts automatically when Claude Desktop launches ``` **Option B: Manual Configuration** @@ -145,19 +161,19 @@ CREATE β†’ INITIALIZE β†’ USE β†’ REFRESH **Commands:** ```bash # Create a docset configuration -agentic-knowledge create --preset git-repo --id my-docs --name "My Docs" --url +npx agentic-knowledge create --preset git-repo --id my-docs --name "My Docs" --url # Initialize (download docs) -agentic-knowledge init my-docs +npx agentic-knowledge init my-docs # Check status -agentic-knowledge status +npx agentic-knowledge status # Update docs -agentic-knowledge refresh my-docs +npx agentic-knowledge refresh my-docs -# Start MCP server -agentic-knowledge +# MCP server runs automatically via Claude Desktop configuration +# Or run manually: npx agentic-knowledge ``` See the [User Guide](./USER_GUIDE.md) for complete command documentation. diff --git a/USER_GUIDE.md b/USER_GUIDE.md index 5b5b35d..a54702d 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -17,7 +17,11 @@ Complete guide to using Agentic Knowledge for managing and searching documentati ### From NPM (Recommended) ```bash -npm install -g agentic-knowledge +# Install in your project +npm install agentic-knowledge + +# Or use directly with npx (no installation needed) +npx agentic-knowledge --help ``` ### From Source @@ -92,7 +96,7 @@ Create docset configurations quickly using presets. Alternatively, you can manua **Git Repository Preset:** ```bash -agentic-knowledge create \ +npx agentic-knowledge create \ --preset git-repo \ --id mcp-sdk \ --name "MCP TypeScript SDK" \ @@ -102,7 +106,7 @@ agentic-knowledge create \ **Local Folder Preset:** ```bash -agentic-knowledge create \ +npx agentic-knowledge create \ --preset local-folder \ --id my-docs \ --name "My Documentation" \ @@ -129,13 +133,13 @@ Initialize a configured docset by downloading and preparing documentation. Use t ```bash # Initialize a specific docset -agentic-knowledge init mcp-sdk +npx agentic-knowledge init mcp-sdk # Force re-initialization (start completely fresh) -agentic-knowledge init mcp-sdk --force +npx agentic-knowledge init mcp-sdk --force # Use custom config path -agentic-knowledge init mcp-sdk --config /path/to/config.yaml +npx agentic-knowledge init mcp-sdk --config /path/to/config.yaml ``` **When to use:** @@ -178,13 +182,13 @@ View the status of all docsets and their sources: ```bash # Basic status -agentic-knowledge status +npx agentic-knowledge status # Detailed status with source information -agentic-knowledge status --verbose +npx agentic-knowledge status --verbose # Use custom config -agentic-knowledge status --config /path/to/config.yaml +npx agentic-knowledge status --config /path/to/config.yaml ``` **Example output:** @@ -211,16 +215,16 @@ Update already-initialized docsets with latest content. This is a **smart, incre ```bash # Refresh all docsets -agentic-knowledge refresh +npx agentic-knowledge refresh # Refresh specific docset -agentic-knowledge refresh mcp-sdk +npx agentic-knowledge refresh mcp-sdk # Force refresh (ignore throttle) -agentic-knowledge refresh mcp-sdk --force +npx agentic-knowledge refresh mcp-sdk --force # Use custom config -agentic-knowledge refresh --config /path/to/config.yaml +npx agentic-knowledge refresh --config /path/to/config.yaml ``` **Smart refresh logic:** @@ -346,17 +350,17 @@ docsets: ```bash # 1. Create config for local docs -agentic-knowledge create \ +npx agentic-knowledge create \ --preset local-folder \ --id my-project \ --name "My Project Docs" \ --path ./docs # 2. Check status -agentic-knowledge status +npx agentic-knowledge status -# 3. Start MCP server -agentic-knowledge +# 3. Configure Claude Desktop (see MCP Integration section) +# The server runs automatically when Claude launches ``` No initialization needed - local folders use symlinks! @@ -365,7 +369,7 @@ No initialization needed - local folders use symlinks! ```bash # 1. Create docset for a Git repository -agentic-knowledge create \ +npx agentic-knowledge create \ --preset git-repo \ --id react-docs \ --name "React Documentation" \ @@ -373,35 +377,35 @@ agentic-knowledge create \ --branch main # 2. Initialize the docset (downloads docs) -agentic-knowledge init react-docs +npx agentic-knowledge init react-docs # 3. Check status -agentic-knowledge status +npx agentic-knowledge status -# 4. Start MCP server (for AI assistant) -agentic-knowledge +# 4. Configure Claude Desktop (see MCP Integration section) +# The server runs automatically when Claude launches # Later: Update documentation -agentic-knowledge refresh react-docs +npx agentic-knowledge refresh react-docs ``` ### Workflow 3: Multi-Repository Setup ```bash # Set up multiple docsets -agentic-knowledge create --preset git-repo --id frontend-docs --name "Frontend Docs" --url https://github.com/company/frontend.git -agentic-knowledge create --preset git-repo --id backend-docs --name "Backend Docs" --url https://github.com/company/backend.git -agentic-knowledge create --preset local-folder --id internal-docs --name "Internal Docs" --path ./docs +npx agentic-knowledge create --preset git-repo --id frontend-docs --name "Frontend Docs" --url https://github.com/company/frontend.git +npx agentic-knowledge create --preset git-repo --id backend-docs --name "Backend Docs" --url https://github.com/company/backend.git +npx agentic-knowledge create --preset local-folder --id internal-docs --name "Internal Docs" --path ./docs # Initialize git repos -agentic-knowledge init frontend-docs -agentic-knowledge init backend-docs +npx agentic-knowledge init frontend-docs +npx agentic-knowledge init backend-docs # Check all statuses -agentic-knowledge status --verbose +npx agentic-knowledge status --verbose -# Start server -agentic-knowledge +# Configure Claude Desktop (see MCP Integration section) +# The server runs automatically when Claude launches ``` ## MCP Integration @@ -457,7 +461,47 @@ Found 2 available docset(s): #### Claude Desktop -Add to `~/Library/Application Support/Claude/claude_desktop_config.json`: +**Configuration file location:** +- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` +- Windows: `%APPDATA%\Claude\claude_desktop_config.json` +- Linux: `~/.config/Claude/claude_desktop_config.json` + +**Option 1: Using npx (recommended)** + +```json +{ + "mcpServers": { + "agentic-knowledge": { + "command": "npx", + "args": ["-y", "agentic-knowledge"] + } + } +} +``` + +The `-y` flag automatically confirms the installation prompt from npx. + +**Option 2: Project-specific installation** + +If you have agentic-knowledge installed in a specific project: + +```json +{ + "mcpServers": { + "agentic-knowledge": { + "command": "npx", + "args": ["-y", "agentic-knowledge"], + "cwd": "/absolute/path/to/your/project" + } + } +} +``` + +This runs the server in your project directory, making it use your project's `.knowledge/config.yaml`. + +**Option 3: Global npm installation** + +If you installed globally with `npm install -g agentic-knowledge`: ```json { @@ -469,9 +513,19 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`: } ``` +**After configuration:** +1. Restart Claude Desktop +2. The server starts automatically in the background +3. Look for the πŸ”Œ icon in Claude Desktop to verify the connection + #### Other MCP Clients -Provide the command `agentic-knowledge` as the MCP server command. The server uses stdio transport. +For other MCP clients, use: +- **Command**: `npx` +- **Args**: `["-y", "agentic-knowledge"]` +- **Transport**: stdio + +The server will start automatically when the MCP client launches. ### Using in AI Conversations From 5f921af67bde44dc97f74550c3b5e02afadf80fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Wed, 26 Nov 2025 07:49:05 +0100 Subject: [PATCH 07/11] README.md aktualisieren --- README.md | 80 +++++-------------------------------------------------- 1 file changed, 7 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index d374152..07cdfc6 100644 --- a/README.md +++ b/README.md @@ -18,57 +18,35 @@ Give your AI assistant access to any documentationβ€”yours or third-partyβ€”so i ## πŸš€ Quick Start -### 1. Install - -```bash -npm install agentic-knowledge -# or -npx agentic-knowledge -``` - -### 2. Configure an MCP Client - -Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS): +### 1. Configure an MCP Client +Add to your coding agent config something along the lines of ```json { "mcpServers": { "agentic-knowledge": { "command": "npx", - "args": ["-y", "agentic-knowledge"] + "args": ["-y", "agentic-knowledge-mcp"] } } } ``` -Or for a local project installation: -```json -{ - "mcpServers": { - "agentic-knowledge": { - "command": "npx", - "args": ["-y", "agentic-knowledge"], - "cwd": "/path/to/your/project" - } - } -} -``` - -### 3. Set Up Your First Docset +### 2. Set Up Your First Docset **Option A: Use the CLI (Recommended)** ```bash # For a Git repository -npx agentic-knowledge create \ +npx agentic-knowledge-mcp create \ --preset git-repo \ --id react-docs \ --name "React Documentation" \ --url https://github.com/facebook/react.git # Initialize (downloads the docs) -npx agentic-knowledge init react-docs +npx agentic-knowledge-mcp init react-docs # The MCP server starts automatically when Claude Desktop launches ``` @@ -87,9 +65,8 @@ docsets: paths: ["./docs"] ``` -Then start: `agentic-knowledge` -### 4. Use It +### 3. Use It Your AI assistant now has access to `search_docs` and `list_docsets` tools. Ask questions naturally: @@ -151,49 +128,6 @@ Traditional RAG (Retrieval-Augmented Generation) was built for the **context-poo This approach is inspired by [The RAG Obituary](https://www.nicolasbustamante.com/p/the-rag-obituary-killed-by-agents) by Nicolas Bustamante and how Claude Code revolutionized code analysis by ditching RAG for direct filesystem exploration. -## πŸ›  Essential CLI Commands - -**Lifecycle:** -``` -CREATE β†’ INITIALIZE β†’ USE β†’ REFRESH -``` - -**Commands:** -```bash -# Create a docset configuration -npx agentic-knowledge create --preset git-repo --id my-docs --name "My Docs" --url - -# Initialize (download docs) -npx agentic-knowledge init my-docs - -# Check status -npx agentic-knowledge status - -# Update docs -npx agentic-knowledge refresh my-docs - -# MCP server runs automatically via Claude Desktop configuration -# Or run manually: npx agentic-knowledge -``` - -See the [User Guide](./USER_GUIDE.md) for complete command documentation. - -## πŸ”¬ The Future of Knowledge Systems - -We're entering the **post-retrieval age**. The winners won't be those with the biggest vector databases, but those who design the smartest navigation systems for abundant context. - -**RAG was training wheels**β€”useful, necessary, but temporary. The future belongs to systems that read, navigate, and reason end-to-end. - -## πŸ§ͺ Development Status - -**Current Phase**: Finalization βœ… - -- βœ… Core implementation complete (107 tests passing) -- βœ… MCP protocol compliance verified -- βœ… Performance validated (0.47ms response time) -- βœ… Full documentation and examples -- ⚠️ Ready for community feedback and real-world testing - ## πŸš€ Local Development ```bash From 678c8b5cff1fda1549165737261ad626bac69d4c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Nov 2025 06:57:34 +0000 Subject: [PATCH 08/11] fix: resolve linting warnings - Prefix unused test variables with underscore - Remove unused import (execSync) - Remove unused catch variable - Prefix unused ErrorType enum values with underscore - Update all references to use prefixed enum values All tests passing: 179/179 (100%) Linting clean (2 false positive warnings for constructor parameters) --- .../cli/src/__tests__/create-command.test.ts | 1 - packages/cli/src/commands/create.ts | 2 +- .../core/src/__tests__/error-handling.test.ts | 40 +++++++++---------- packages/core/src/__tests__/loader.test.ts | 18 ++++++--- .../src/__tests__/template-processor.test.ts | 4 +- packages/core/src/config/loader.ts | 16 ++++---- packages/core/src/config/manager.ts | 16 ++++---- packages/core/src/paths/calculator.ts | 4 +- packages/core/src/paths/symlinks.ts | 2 +- packages/core/src/templates/processor.ts | 8 ++-- packages/core/src/types.ts | 12 +++--- .../src/__tests__/integration.test.ts | 2 +- 12 files changed, 65 insertions(+), 60 deletions(-) diff --git a/packages/cli/src/__tests__/create-command.test.ts b/packages/cli/src/__tests__/create-command.test.ts index 4127b24..6873fc2 100644 --- a/packages/cli/src/__tests__/create-command.test.ts +++ b/packages/cli/src/__tests__/create-command.test.ts @@ -6,7 +6,6 @@ import { describe, it, expect, beforeEach, afterEach } from "vitest"; import { promises as fs } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { execSync } from "node:child_process"; describe("create command", () => { let testDir: string; diff --git a/packages/cli/src/commands/create.ts b/packages/cli/src/commands/create.ts index bf13983..2d6f3a5 100644 --- a/packages/cli/src/commands/create.ts +++ b/packages/cli/src/commands/create.ts @@ -137,7 +137,7 @@ async function createLocalFolderDocset(options: any): Promise { if (!stat.isDirectory()) { throw new Error(`Path is not a directory: ${options.path}`); } - } catch (error) { + } catch { throw new Error(`Path does not exist: ${options.path}`); } diff --git a/packages/core/src/__tests__/error-handling.test.ts b/packages/core/src/__tests__/error-handling.test.ts index ceef86c..329a4c3 100644 --- a/packages/core/src/__tests__/error-handling.test.ts +++ b/packages/core/src/__tests__/error-handling.test.ts @@ -9,13 +9,13 @@ describe("Error Handling", () => { describe("KnowledgeError", () => { test("should create error with type and message", () => { const error = new KnowledgeError( - ErrorType.CONFIG_NOT_FOUND, + ErrorType._CONFIG_NOT_FOUND, "Configuration file not found", ); expect(error).toBeInstanceOf(Error); expect(error).toBeInstanceOf(KnowledgeError); - expect(error.type).toBe(ErrorType.CONFIG_NOT_FOUND); + expect(error.type).toBe(ErrorType._CONFIG_NOT_FOUND); expect(error.message).toBe("Configuration file not found"); expect(error.name).toBe("KnowledgeError"); }); @@ -23,7 +23,7 @@ describe("Error Handling", () => { test("should create error with context", () => { const context = { configPath: "/path/to/config.yaml", line: 5 }; const error = new KnowledgeError( - ErrorType.YAML_PARSE_ERROR, + ErrorType._YAML_PARSE_ERROR, "Invalid YAML syntax", context, ); @@ -35,7 +35,7 @@ describe("Error Handling", () => { test("should handle error without context", () => { const error = new KnowledgeError( - ErrorType.CONFIG_INVALID, + ErrorType._CONFIG_INVALID, "Invalid configuration structure", ); @@ -44,7 +44,7 @@ describe("Error Handling", () => { test("should preserve error stack trace", () => { const error = new KnowledgeError( - ErrorType.TEMPLATE_ERROR, + ErrorType._TEMPLATE_ERROR, "Template processing failed", ); @@ -55,12 +55,12 @@ describe("Error Handling", () => { describe("ErrorType enum", () => { test("should have all expected error types", () => { - expect(ErrorType.CONFIG_NOT_FOUND).toBe("CONFIG_NOT_FOUND"); - expect(ErrorType.CONFIG_INVALID).toBe("CONFIG_INVALID"); - expect(ErrorType.DOCSET_NOT_FOUND).toBe("DOCSET_NOT_FOUND"); - expect(ErrorType.PATH_INVALID).toBe("PATH_INVALID"); - expect(ErrorType.TEMPLATE_ERROR).toBe("TEMPLATE_ERROR"); - expect(ErrorType.YAML_PARSE_ERROR).toBe("YAML_PARSE_ERROR"); + expect(ErrorType._CONFIG_NOT_FOUND).toBe("CONFIG_NOT_FOUND"); + expect(ErrorType._CONFIG_INVALID).toBe("CONFIG_INVALID"); + expect(ErrorType._DOCSET_NOT_FOUND).toBe("DOCSET_NOT_FOUND"); + expect(ErrorType._PATH_INVALID).toBe("PATH_INVALID"); + expect(ErrorType._TEMPLATE_ERROR).toBe("TEMPLATE_ERROR"); + expect(ErrorType._YAML_PARSE_ERROR).toBe("YAML_PARSE_ERROR"); }); test("should have string values for all error types", () => { @@ -92,7 +92,7 @@ describe("Error Handling", () => { }; const error = new KnowledgeError( - ErrorType.CONFIG_INVALID, + ErrorType._CONFIG_INVALID, "Complex error scenario", complexContext, ); @@ -112,7 +112,7 @@ describe("Error Handling", () => { }; const error = new KnowledgeError( - ErrorType.PATH_INVALID, + ErrorType._PATH_INVALID, "Error with null values", contextWithNulls, ); @@ -128,7 +128,7 @@ describe("Error Handling", () => { describe("error serialization", () => { test("should have accessible error properties", () => { const error = new KnowledgeError( - ErrorType.DOCSET_NOT_FOUND, + ErrorType._DOCSET_NOT_FOUND, "Docset not found", { docsetId: "missing-docs", searchPath: "/project" }, ); @@ -136,7 +136,7 @@ describe("Error Handling", () => { // Test that properties are accessible (Error serialization is complex) expect(error.name).toBe("KnowledgeError"); expect(error.message).toBe("Docset not found"); - expect(error.type).toBe(ErrorType.DOCSET_NOT_FOUND); + expect(error.type).toBe(ErrorType._DOCSET_NOT_FOUND); expect(error.context).toEqual({ docsetId: "missing-docs", searchPath: "/project", @@ -152,7 +152,7 @@ describe("Error Handling", () => { expect(manualSerialized.name).toBe("KnowledgeError"); expect(manualSerialized.message).toBe("Docset not found"); - expect(manualSerialized.type).toBe(ErrorType.DOCSET_NOT_FOUND); + expect(manualSerialized.type).toBe(ErrorType._DOCSET_NOT_FOUND); expect(manualSerialized.context).toEqual({ docsetId: "missing-docs", searchPath: "/project", @@ -164,7 +164,7 @@ describe("Error Handling", () => { circularObj.self = circularObj; const error = new KnowledgeError( - ErrorType.TEMPLATE_ERROR, + ErrorType._TEMPLATE_ERROR, "Circular reference error", { circular: circularObj }, ); @@ -179,17 +179,17 @@ describe("Error Handling", () => { test("should format error messages consistently", () => { const testCases = [ { - type: ErrorType.CONFIG_NOT_FOUND, + type: ErrorType._CONFIG_NOT_FOUND, message: "Configuration file not found: /path/to/config.yaml", expectedPattern: /Configuration file not found:/, }, { - type: ErrorType.YAML_PARSE_ERROR, + type: ErrorType._YAML_PARSE_ERROR, message: "Failed to parse YAML configuration: Unexpected token", expectedPattern: /Failed to parse YAML configuration:/, }, { - type: ErrorType.PATH_INVALID, + type: ErrorType._PATH_INVALID, message: "Failed to calculate local path for docset 'react-docs': Path resolution error", expectedPattern: /Failed to calculate local path for docset/, diff --git a/packages/core/src/__tests__/loader.test.ts b/packages/core/src/__tests__/loader.test.ts index cb3b210..7575c34 100644 --- a/packages/core/src/__tests__/loader.test.ts +++ b/packages/core/src/__tests__/loader.test.ts @@ -90,7 +90,9 @@ template: "Global only"`; await loadConfig(nonExistentPath); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe(ErrorType.CONFIG_NOT_FOUND); + expect((error as KnowledgeError).type).toBe( + ErrorType._CONFIG_NOT_FOUND, + ); expect((error as KnowledgeError).context?.configPath).toBe( nonExistentPath, ); @@ -106,7 +108,7 @@ template: "Global only"`; await loadConfig(invalidConfigPath); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe(ErrorType.CONFIG_INVALID); + expect((error as KnowledgeError).type).toBe(ErrorType._CONFIG_INVALID); } }); @@ -119,7 +121,9 @@ template: "Global only"`; await loadConfig(malformedConfigPath); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe(ErrorType.YAML_PARSE_ERROR); + expect((error as KnowledgeError).type).toBe( + ErrorType._YAML_PARSE_ERROR, + ); } }); @@ -144,7 +148,7 @@ template: "Global template with {{keywords}} and {{invalid_variable}}"`; await loadConfig(invalidTemplateConfigPath); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe(ErrorType.TEMPLATE_ERROR); + expect((error as KnowledgeError).type).toBe(ErrorType._TEMPLATE_ERROR); expect((error as KnowledgeError).message).toContain( "Invalid global template", ); @@ -179,7 +183,7 @@ docsets: await loadConfig(invalidDocsetTemplateConfigPath); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe(ErrorType.TEMPLATE_ERROR); + expect((error as KnowledgeError).type).toBe(ErrorType._TEMPLATE_ERROR); expect((error as KnowledgeError).message).toContain( "Invalid template for docset 'test-docs'", ); @@ -227,7 +231,9 @@ template: "Global: {{keywords}} in {{local_path}}"`; loadConfigSync(nonExistentPath); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe(ErrorType.CONFIG_NOT_FOUND); + expect((error as KnowledgeError).type).toBe( + ErrorType._CONFIG_NOT_FOUND, + ); } }); }); diff --git a/packages/core/src/__tests__/template-processor.test.ts b/packages/core/src/__tests__/template-processor.test.ts index fdaa856..1c28391 100644 --- a/packages/core/src/__tests__/template-processor.test.ts +++ b/packages/core/src/__tests__/template-processor.test.ts @@ -72,7 +72,7 @@ describe("Template Processing", () => { processTemplate(template, sampleContext); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe(ErrorType.TEMPLATE_ERROR); + expect((error as KnowledgeError).type).toBe(ErrorType._TEMPLATE_ERROR); expect((error as KnowledgeError).message).toContain( "invalid variables", ); @@ -112,7 +112,7 @@ describe("Template Processing", () => { validateTemplate(invalidTemplate); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe(ErrorType.TEMPLATE_ERROR); + expect((error as KnowledgeError).type).toBe(ErrorType._TEMPLATE_ERROR); expect((error as KnowledgeError).message).toContain( "invalid variables: invalid_variable", ); diff --git a/packages/core/src/config/loader.ts b/packages/core/src/config/loader.ts index 9eec497..4e7b54d 100644 --- a/packages/core/src/config/loader.ts +++ b/packages/core/src/config/loader.ts @@ -21,7 +21,7 @@ export async function loadConfig(configPath: string): Promise { if (!validateConfig(parsed)) { throw new KnowledgeError( - ErrorType.CONFIG_INVALID, + ErrorType._CONFIG_INVALID, "Configuration file contains invalid structure", { configPath, parsed }, ); @@ -38,14 +38,14 @@ export async function loadConfig(configPath: string): Promise { if ((error as NodeJS.ErrnoException).code === "ENOENT") { throw new KnowledgeError( - ErrorType.CONFIG_NOT_FOUND, + ErrorType._CONFIG_NOT_FOUND, `Configuration file not found: ${configPath}`, { configPath }, ); } throw new KnowledgeError( - ErrorType.YAML_PARSE_ERROR, + ErrorType._YAML_PARSE_ERROR, `Failed to parse YAML configuration: ${(error as Error).message}`, { configPath, error }, ); @@ -64,7 +64,7 @@ export function loadConfigSync(configPath: string): KnowledgeConfig { if (!validateConfig(parsed)) { throw new KnowledgeError( - ErrorType.CONFIG_INVALID, + ErrorType._CONFIG_INVALID, "Configuration file contains invalid structure", { configPath, parsed }, ); @@ -81,14 +81,14 @@ export function loadConfigSync(configPath: string): KnowledgeConfig { if ((error as NodeJS.ErrnoException).code === "ENOENT") { throw new KnowledgeError( - ErrorType.CONFIG_NOT_FOUND, + ErrorType._CONFIG_NOT_FOUND, `Configuration file not found: ${configPath}`, { configPath }, ); } throw new KnowledgeError( - ErrorType.YAML_PARSE_ERROR, + ErrorType._YAML_PARSE_ERROR, `Failed to parse YAML configuration: ${(error as Error).message}`, { configPath, error }, ); @@ -110,7 +110,7 @@ function validateAllTemplates( validateTemplate(config.template); } catch (error) { throw new KnowledgeError( - ErrorType.TEMPLATE_ERROR, + ErrorType._TEMPLATE_ERROR, `Invalid global template in configuration: ${(error as Error).message}`, { configPath, template: config.template, originalError: error }, ); @@ -124,7 +124,7 @@ function validateAllTemplates( validateTemplate(docset.template); } catch (error) { throw new KnowledgeError( - ErrorType.TEMPLATE_ERROR, + ErrorType._TEMPLATE_ERROR, `Invalid template for docset '${docset.id}': ${(error as Error).message}`, { configPath, diff --git a/packages/core/src/config/manager.ts b/packages/core/src/config/manager.ts index 66bafcf..8522987 100644 --- a/packages/core/src/config/manager.ts +++ b/packages/core/src/config/manager.ts @@ -45,7 +45,7 @@ export class ConfigManager { const configPath = await findConfigPath(startDir); if (!configPath) { throw new KnowledgeError( - ErrorType.CONFIG_NOT_FOUND, + ErrorType._CONFIG_NOT_FOUND, "No configuration file found. Please ensure .knowledge/config.yaml exists in your project.", { searchPath: startDir || process.cwd() }, ); @@ -75,7 +75,7 @@ export class ConfigManager { if (!validateConfig(parsed)) { throw new KnowledgeError( - ErrorType.CONFIG_INVALID, + ErrorType._CONFIG_INVALID, "Configuration file contains invalid structure", { configPath, parsed }, ); @@ -89,14 +89,14 @@ export class ConfigManager { if ((error as NodeJS.ErrnoException).code === "ENOENT") { throw new KnowledgeError( - ErrorType.CONFIG_NOT_FOUND, + ErrorType._CONFIG_NOT_FOUND, `Configuration file not found: ${configPath}`, { configPath }, ); } throw new KnowledgeError( - ErrorType.CONFIG_INVALID, + ErrorType._CONFIG_INVALID, `Failed to parse configuration file: ${error instanceof Error ? error.message : String(error)}`, { configPath, originalError: error }, ); @@ -115,7 +115,7 @@ export class ConfigManager { const targetPath = configPath || this.configCache?.configPath; if (!targetPath) { throw new KnowledgeError( - ErrorType.CONFIG_INVALID, + ErrorType._CONFIG_INVALID, "No configuration path available. Load config first or provide explicit path.", { configPath }, ); @@ -124,7 +124,7 @@ export class ConfigManager { // Validate config before saving if (!validateConfig(config)) { throw new KnowledgeError( - ErrorType.CONFIG_INVALID, + ErrorType._CONFIG_INVALID, "Cannot save invalid configuration", { config }, ); @@ -143,7 +143,7 @@ export class ConfigManager { this.configCache = null; } catch (error) { throw new KnowledgeError( - ErrorType.CONFIG_INVALID, + ErrorType._CONFIG_INVALID, `Failed to save configuration: ${error instanceof Error ? error.message : String(error)}`, { configPath: targetPath, originalError: error }, ); @@ -166,7 +166,7 @@ export class ConfigManager { const docset = config.docsets.find((d) => d.id === docsetId); if (!docset) { throw new KnowledgeError( - ErrorType.CONFIG_INVALID, + ErrorType._CONFIG_INVALID, `Docset '${docsetId}' not found in configuration`, { docsetId, availableDocsets: config.docsets.map((d) => d.id) }, ); diff --git a/packages/core/src/paths/calculator.ts b/packages/core/src/paths/calculator.ts index b24d7aa..999b173 100644 --- a/packages/core/src/paths/calculator.ts +++ b/packages/core/src/paths/calculator.ts @@ -73,7 +73,7 @@ export function calculateLocalPath( throw new Error(`Unsupported source type: ${(primarySource as any).type}`); } catch (error) { throw new KnowledgeError( - ErrorType.PATH_INVALID, + ErrorType._PATH_INVALID, `Failed to calculate local path for docset '${docset.id}': ${(error as Error).message}`, { docset, configPath, error }, ); @@ -119,7 +119,7 @@ export async function calculateLocalPathWithSymlinks( return relative(projectRoot, symlinkDir) || "."; } catch (error) { throw new KnowledgeError( - ErrorType.PATH_INVALID, + ErrorType._PATH_INVALID, `Failed to create symlinks for docset '${docset.id}': ${(error as Error).message}`, { docset, configPath, error }, ); diff --git a/packages/core/src/paths/symlinks.ts b/packages/core/src/paths/symlinks.ts index bc2a45c..591942e 100644 --- a/packages/core/src/paths/symlinks.ts +++ b/packages/core/src/paths/symlinks.ts @@ -50,7 +50,7 @@ export async function createSymlinks( } } catch (error) { throw new KnowledgeError( - ErrorType.PATH_INVALID, + ErrorType._PATH_INVALID, `Failed to create symlinks: ${(error as Error).message}`, { sourcePaths, targetDir, projectRoot, error }, ); diff --git a/packages/core/src/templates/processor.ts b/packages/core/src/templates/processor.ts index 22d117e..e344c38 100644 --- a/packages/core/src/templates/processor.ts +++ b/packages/core/src/templates/processor.ts @@ -45,7 +45,7 @@ export function processTemplate( const unreplacedMatches = processed.match(/\{\{[^}]+\}\}/g); if (unreplacedMatches) { throw new KnowledgeError( - ErrorType.TEMPLATE_ERROR, + ErrorType._TEMPLATE_ERROR, `Template contains invalid variables: ${unreplacedMatches.join(", ")}`, { template, unreplacedMatches }, ); @@ -57,7 +57,7 @@ export function processTemplate( throw error; } throw new KnowledgeError( - ErrorType.TEMPLATE_ERROR, + ErrorType._TEMPLATE_ERROR, `Failed to process template: ${(error as Error).message}`, { template, context, error }, ); @@ -95,7 +95,7 @@ export function validateTemplate(template: string): boolean { if (invalidVariables.length > 0) { throw new KnowledgeError( - ErrorType.TEMPLATE_ERROR, + ErrorType._TEMPLATE_ERROR, `Template contains invalid variables: ${invalidVariables.join(", ")}. Allowed variables: ${ALLOWED_TEMPLATE_VARIABLES.join(", ")}`, { template, @@ -112,7 +112,7 @@ export function validateTemplate(template: string): boolean { if (missingRequired.length > 0) { throw new KnowledgeError( - ErrorType.TEMPLATE_ERROR, + ErrorType._TEMPLATE_ERROR, `Template missing required variables: ${missingRequired.join(", ")}. Required variables: ${REQUIRED_TEMPLATE_VARIABLES.join(", ")}`, { template, diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index edce012..e7a3161 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -121,12 +121,12 @@ export interface TemplateContext { * Error types that can occur in the core system */ export enum ErrorType { - CONFIG_NOT_FOUND = "CONFIG_NOT_FOUND", - CONFIG_INVALID = "CONFIG_INVALID", - DOCSET_NOT_FOUND = "DOCSET_NOT_FOUND", - PATH_INVALID = "PATH_INVALID", - TEMPLATE_ERROR = "TEMPLATE_ERROR", - YAML_PARSE_ERROR = "YAML_PARSE_ERROR", + _CONFIG_NOT_FOUND = "CONFIG_NOT_FOUND", + _CONFIG_INVALID = "CONFIG_INVALID", + _DOCSET_NOT_FOUND = "DOCSET_NOT_FOUND", + _PATH_INVALID = "PATH_INVALID", + _TEMPLATE_ERROR = "TEMPLATE_ERROR", + _YAML_PARSE_ERROR = "YAML_PARSE_ERROR", } /** diff --git a/packages/mcp-server/src/__tests__/integration.test.ts b/packages/mcp-server/src/__tests__/integration.test.ts index d755bd9..bbcf6cc 100644 --- a/packages/mcp-server/src/__tests__/integration.test.ts +++ b/packages/mcp-server/src/__tests__/integration.test.ts @@ -91,7 +91,7 @@ docsets: const server = createAgenticKnowledgeServer(); // Create a mock request handler to capture tool descriptions - let toolsResponse: any = null; + let _toolsResponse: any = null; // We can't easily test the actual ListToolsRequestSchema handler directly, // but we can verify the server creates without errors and our configuration loads From 160608417636ad5387864e4a0cef3930a12a3495 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Nov 2025 07:02:31 +0000 Subject: [PATCH 09/11] refactor: remove underscore prefixes from ErrorType enum The underscore prefixing was an ugly workaround for false positive linter warnings. Enum values ARE used throughout the codebase via ErrorType.CONFIG_NOT_FOUND, etc. Added comment explaining that linter warnings are false positives. All tests still passing (179/179). --- .../core/src/__tests__/error-handling.test.ts | 40 +++++++++---------- packages/core/src/__tests__/loader.test.ts | 18 +++------ .../src/__tests__/template-processor.test.ts | 4 +- packages/core/src/config/loader.ts | 16 ++++---- packages/core/src/config/manager.ts | 16 ++++---- packages/core/src/paths/calculator.ts | 4 +- packages/core/src/paths/symlinks.ts | 2 +- packages/core/src/templates/processor.ts | 8 ++-- packages/core/src/types.ts | 14 ++++--- 9 files changed, 59 insertions(+), 63 deletions(-) diff --git a/packages/core/src/__tests__/error-handling.test.ts b/packages/core/src/__tests__/error-handling.test.ts index 329a4c3..ceef86c 100644 --- a/packages/core/src/__tests__/error-handling.test.ts +++ b/packages/core/src/__tests__/error-handling.test.ts @@ -9,13 +9,13 @@ describe("Error Handling", () => { describe("KnowledgeError", () => { test("should create error with type and message", () => { const error = new KnowledgeError( - ErrorType._CONFIG_NOT_FOUND, + ErrorType.CONFIG_NOT_FOUND, "Configuration file not found", ); expect(error).toBeInstanceOf(Error); expect(error).toBeInstanceOf(KnowledgeError); - expect(error.type).toBe(ErrorType._CONFIG_NOT_FOUND); + expect(error.type).toBe(ErrorType.CONFIG_NOT_FOUND); expect(error.message).toBe("Configuration file not found"); expect(error.name).toBe("KnowledgeError"); }); @@ -23,7 +23,7 @@ describe("Error Handling", () => { test("should create error with context", () => { const context = { configPath: "/path/to/config.yaml", line: 5 }; const error = new KnowledgeError( - ErrorType._YAML_PARSE_ERROR, + ErrorType.YAML_PARSE_ERROR, "Invalid YAML syntax", context, ); @@ -35,7 +35,7 @@ describe("Error Handling", () => { test("should handle error without context", () => { const error = new KnowledgeError( - ErrorType._CONFIG_INVALID, + ErrorType.CONFIG_INVALID, "Invalid configuration structure", ); @@ -44,7 +44,7 @@ describe("Error Handling", () => { test("should preserve error stack trace", () => { const error = new KnowledgeError( - ErrorType._TEMPLATE_ERROR, + ErrorType.TEMPLATE_ERROR, "Template processing failed", ); @@ -55,12 +55,12 @@ describe("Error Handling", () => { describe("ErrorType enum", () => { test("should have all expected error types", () => { - expect(ErrorType._CONFIG_NOT_FOUND).toBe("CONFIG_NOT_FOUND"); - expect(ErrorType._CONFIG_INVALID).toBe("CONFIG_INVALID"); - expect(ErrorType._DOCSET_NOT_FOUND).toBe("DOCSET_NOT_FOUND"); - expect(ErrorType._PATH_INVALID).toBe("PATH_INVALID"); - expect(ErrorType._TEMPLATE_ERROR).toBe("TEMPLATE_ERROR"); - expect(ErrorType._YAML_PARSE_ERROR).toBe("YAML_PARSE_ERROR"); + expect(ErrorType.CONFIG_NOT_FOUND).toBe("CONFIG_NOT_FOUND"); + expect(ErrorType.CONFIG_INVALID).toBe("CONFIG_INVALID"); + expect(ErrorType.DOCSET_NOT_FOUND).toBe("DOCSET_NOT_FOUND"); + expect(ErrorType.PATH_INVALID).toBe("PATH_INVALID"); + expect(ErrorType.TEMPLATE_ERROR).toBe("TEMPLATE_ERROR"); + expect(ErrorType.YAML_PARSE_ERROR).toBe("YAML_PARSE_ERROR"); }); test("should have string values for all error types", () => { @@ -92,7 +92,7 @@ describe("Error Handling", () => { }; const error = new KnowledgeError( - ErrorType._CONFIG_INVALID, + ErrorType.CONFIG_INVALID, "Complex error scenario", complexContext, ); @@ -112,7 +112,7 @@ describe("Error Handling", () => { }; const error = new KnowledgeError( - ErrorType._PATH_INVALID, + ErrorType.PATH_INVALID, "Error with null values", contextWithNulls, ); @@ -128,7 +128,7 @@ describe("Error Handling", () => { describe("error serialization", () => { test("should have accessible error properties", () => { const error = new KnowledgeError( - ErrorType._DOCSET_NOT_FOUND, + ErrorType.DOCSET_NOT_FOUND, "Docset not found", { docsetId: "missing-docs", searchPath: "/project" }, ); @@ -136,7 +136,7 @@ describe("Error Handling", () => { // Test that properties are accessible (Error serialization is complex) expect(error.name).toBe("KnowledgeError"); expect(error.message).toBe("Docset not found"); - expect(error.type).toBe(ErrorType._DOCSET_NOT_FOUND); + expect(error.type).toBe(ErrorType.DOCSET_NOT_FOUND); expect(error.context).toEqual({ docsetId: "missing-docs", searchPath: "/project", @@ -152,7 +152,7 @@ describe("Error Handling", () => { expect(manualSerialized.name).toBe("KnowledgeError"); expect(manualSerialized.message).toBe("Docset not found"); - expect(manualSerialized.type).toBe(ErrorType._DOCSET_NOT_FOUND); + expect(manualSerialized.type).toBe(ErrorType.DOCSET_NOT_FOUND); expect(manualSerialized.context).toEqual({ docsetId: "missing-docs", searchPath: "/project", @@ -164,7 +164,7 @@ describe("Error Handling", () => { circularObj.self = circularObj; const error = new KnowledgeError( - ErrorType._TEMPLATE_ERROR, + ErrorType.TEMPLATE_ERROR, "Circular reference error", { circular: circularObj }, ); @@ -179,17 +179,17 @@ describe("Error Handling", () => { test("should format error messages consistently", () => { const testCases = [ { - type: ErrorType._CONFIG_NOT_FOUND, + type: ErrorType.CONFIG_NOT_FOUND, message: "Configuration file not found: /path/to/config.yaml", expectedPattern: /Configuration file not found:/, }, { - type: ErrorType._YAML_PARSE_ERROR, + type: ErrorType.YAML_PARSE_ERROR, message: "Failed to parse YAML configuration: Unexpected token", expectedPattern: /Failed to parse YAML configuration:/, }, { - type: ErrorType._PATH_INVALID, + type: ErrorType.PATH_INVALID, message: "Failed to calculate local path for docset 'react-docs': Path resolution error", expectedPattern: /Failed to calculate local path for docset/, diff --git a/packages/core/src/__tests__/loader.test.ts b/packages/core/src/__tests__/loader.test.ts index 7575c34..cb3b210 100644 --- a/packages/core/src/__tests__/loader.test.ts +++ b/packages/core/src/__tests__/loader.test.ts @@ -90,9 +90,7 @@ template: "Global only"`; await loadConfig(nonExistentPath); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe( - ErrorType._CONFIG_NOT_FOUND, - ); + expect((error as KnowledgeError).type).toBe(ErrorType.CONFIG_NOT_FOUND); expect((error as KnowledgeError).context?.configPath).toBe( nonExistentPath, ); @@ -108,7 +106,7 @@ template: "Global only"`; await loadConfig(invalidConfigPath); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe(ErrorType._CONFIG_INVALID); + expect((error as KnowledgeError).type).toBe(ErrorType.CONFIG_INVALID); } }); @@ -121,9 +119,7 @@ template: "Global only"`; await loadConfig(malformedConfigPath); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe( - ErrorType._YAML_PARSE_ERROR, - ); + expect((error as KnowledgeError).type).toBe(ErrorType.YAML_PARSE_ERROR); } }); @@ -148,7 +144,7 @@ template: "Global template with {{keywords}} and {{invalid_variable}}"`; await loadConfig(invalidTemplateConfigPath); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe(ErrorType._TEMPLATE_ERROR); + expect((error as KnowledgeError).type).toBe(ErrorType.TEMPLATE_ERROR); expect((error as KnowledgeError).message).toContain( "Invalid global template", ); @@ -183,7 +179,7 @@ docsets: await loadConfig(invalidDocsetTemplateConfigPath); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe(ErrorType._TEMPLATE_ERROR); + expect((error as KnowledgeError).type).toBe(ErrorType.TEMPLATE_ERROR); expect((error as KnowledgeError).message).toContain( "Invalid template for docset 'test-docs'", ); @@ -231,9 +227,7 @@ template: "Global: {{keywords}} in {{local_path}}"`; loadConfigSync(nonExistentPath); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe( - ErrorType._CONFIG_NOT_FOUND, - ); + expect((error as KnowledgeError).type).toBe(ErrorType.CONFIG_NOT_FOUND); } }); }); diff --git a/packages/core/src/__tests__/template-processor.test.ts b/packages/core/src/__tests__/template-processor.test.ts index 1c28391..fdaa856 100644 --- a/packages/core/src/__tests__/template-processor.test.ts +++ b/packages/core/src/__tests__/template-processor.test.ts @@ -72,7 +72,7 @@ describe("Template Processing", () => { processTemplate(template, sampleContext); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe(ErrorType._TEMPLATE_ERROR); + expect((error as KnowledgeError).type).toBe(ErrorType.TEMPLATE_ERROR); expect((error as KnowledgeError).message).toContain( "invalid variables", ); @@ -112,7 +112,7 @@ describe("Template Processing", () => { validateTemplate(invalidTemplate); } catch (error) { expect(error).toBeInstanceOf(KnowledgeError); - expect((error as KnowledgeError).type).toBe(ErrorType._TEMPLATE_ERROR); + expect((error as KnowledgeError).type).toBe(ErrorType.TEMPLATE_ERROR); expect((error as KnowledgeError).message).toContain( "invalid variables: invalid_variable", ); diff --git a/packages/core/src/config/loader.ts b/packages/core/src/config/loader.ts index 4e7b54d..9eec497 100644 --- a/packages/core/src/config/loader.ts +++ b/packages/core/src/config/loader.ts @@ -21,7 +21,7 @@ export async function loadConfig(configPath: string): Promise { if (!validateConfig(parsed)) { throw new KnowledgeError( - ErrorType._CONFIG_INVALID, + ErrorType.CONFIG_INVALID, "Configuration file contains invalid structure", { configPath, parsed }, ); @@ -38,14 +38,14 @@ export async function loadConfig(configPath: string): Promise { if ((error as NodeJS.ErrnoException).code === "ENOENT") { throw new KnowledgeError( - ErrorType._CONFIG_NOT_FOUND, + ErrorType.CONFIG_NOT_FOUND, `Configuration file not found: ${configPath}`, { configPath }, ); } throw new KnowledgeError( - ErrorType._YAML_PARSE_ERROR, + ErrorType.YAML_PARSE_ERROR, `Failed to parse YAML configuration: ${(error as Error).message}`, { configPath, error }, ); @@ -64,7 +64,7 @@ export function loadConfigSync(configPath: string): KnowledgeConfig { if (!validateConfig(parsed)) { throw new KnowledgeError( - ErrorType._CONFIG_INVALID, + ErrorType.CONFIG_INVALID, "Configuration file contains invalid structure", { configPath, parsed }, ); @@ -81,14 +81,14 @@ export function loadConfigSync(configPath: string): KnowledgeConfig { if ((error as NodeJS.ErrnoException).code === "ENOENT") { throw new KnowledgeError( - ErrorType._CONFIG_NOT_FOUND, + ErrorType.CONFIG_NOT_FOUND, `Configuration file not found: ${configPath}`, { configPath }, ); } throw new KnowledgeError( - ErrorType._YAML_PARSE_ERROR, + ErrorType.YAML_PARSE_ERROR, `Failed to parse YAML configuration: ${(error as Error).message}`, { configPath, error }, ); @@ -110,7 +110,7 @@ function validateAllTemplates( validateTemplate(config.template); } catch (error) { throw new KnowledgeError( - ErrorType._TEMPLATE_ERROR, + ErrorType.TEMPLATE_ERROR, `Invalid global template in configuration: ${(error as Error).message}`, { configPath, template: config.template, originalError: error }, ); @@ -124,7 +124,7 @@ function validateAllTemplates( validateTemplate(docset.template); } catch (error) { throw new KnowledgeError( - ErrorType._TEMPLATE_ERROR, + ErrorType.TEMPLATE_ERROR, `Invalid template for docset '${docset.id}': ${(error as Error).message}`, { configPath, diff --git a/packages/core/src/config/manager.ts b/packages/core/src/config/manager.ts index 8522987..66bafcf 100644 --- a/packages/core/src/config/manager.ts +++ b/packages/core/src/config/manager.ts @@ -45,7 +45,7 @@ export class ConfigManager { const configPath = await findConfigPath(startDir); if (!configPath) { throw new KnowledgeError( - ErrorType._CONFIG_NOT_FOUND, + ErrorType.CONFIG_NOT_FOUND, "No configuration file found. Please ensure .knowledge/config.yaml exists in your project.", { searchPath: startDir || process.cwd() }, ); @@ -75,7 +75,7 @@ export class ConfigManager { if (!validateConfig(parsed)) { throw new KnowledgeError( - ErrorType._CONFIG_INVALID, + ErrorType.CONFIG_INVALID, "Configuration file contains invalid structure", { configPath, parsed }, ); @@ -89,14 +89,14 @@ export class ConfigManager { if ((error as NodeJS.ErrnoException).code === "ENOENT") { throw new KnowledgeError( - ErrorType._CONFIG_NOT_FOUND, + ErrorType.CONFIG_NOT_FOUND, `Configuration file not found: ${configPath}`, { configPath }, ); } throw new KnowledgeError( - ErrorType._CONFIG_INVALID, + ErrorType.CONFIG_INVALID, `Failed to parse configuration file: ${error instanceof Error ? error.message : String(error)}`, { configPath, originalError: error }, ); @@ -115,7 +115,7 @@ export class ConfigManager { const targetPath = configPath || this.configCache?.configPath; if (!targetPath) { throw new KnowledgeError( - ErrorType._CONFIG_INVALID, + ErrorType.CONFIG_INVALID, "No configuration path available. Load config first or provide explicit path.", { configPath }, ); @@ -124,7 +124,7 @@ export class ConfigManager { // Validate config before saving if (!validateConfig(config)) { throw new KnowledgeError( - ErrorType._CONFIG_INVALID, + ErrorType.CONFIG_INVALID, "Cannot save invalid configuration", { config }, ); @@ -143,7 +143,7 @@ export class ConfigManager { this.configCache = null; } catch (error) { throw new KnowledgeError( - ErrorType._CONFIG_INVALID, + ErrorType.CONFIG_INVALID, `Failed to save configuration: ${error instanceof Error ? error.message : String(error)}`, { configPath: targetPath, originalError: error }, ); @@ -166,7 +166,7 @@ export class ConfigManager { const docset = config.docsets.find((d) => d.id === docsetId); if (!docset) { throw new KnowledgeError( - ErrorType._CONFIG_INVALID, + ErrorType.CONFIG_INVALID, `Docset '${docsetId}' not found in configuration`, { docsetId, availableDocsets: config.docsets.map((d) => d.id) }, ); diff --git a/packages/core/src/paths/calculator.ts b/packages/core/src/paths/calculator.ts index 999b173..b24d7aa 100644 --- a/packages/core/src/paths/calculator.ts +++ b/packages/core/src/paths/calculator.ts @@ -73,7 +73,7 @@ export function calculateLocalPath( throw new Error(`Unsupported source type: ${(primarySource as any).type}`); } catch (error) { throw new KnowledgeError( - ErrorType._PATH_INVALID, + ErrorType.PATH_INVALID, `Failed to calculate local path for docset '${docset.id}': ${(error as Error).message}`, { docset, configPath, error }, ); @@ -119,7 +119,7 @@ export async function calculateLocalPathWithSymlinks( return relative(projectRoot, symlinkDir) || "."; } catch (error) { throw new KnowledgeError( - ErrorType._PATH_INVALID, + ErrorType.PATH_INVALID, `Failed to create symlinks for docset '${docset.id}': ${(error as Error).message}`, { docset, configPath, error }, ); diff --git a/packages/core/src/paths/symlinks.ts b/packages/core/src/paths/symlinks.ts index 591942e..bc2a45c 100644 --- a/packages/core/src/paths/symlinks.ts +++ b/packages/core/src/paths/symlinks.ts @@ -50,7 +50,7 @@ export async function createSymlinks( } } catch (error) { throw new KnowledgeError( - ErrorType._PATH_INVALID, + ErrorType.PATH_INVALID, `Failed to create symlinks: ${(error as Error).message}`, { sourcePaths, targetDir, projectRoot, error }, ); diff --git a/packages/core/src/templates/processor.ts b/packages/core/src/templates/processor.ts index e344c38..22d117e 100644 --- a/packages/core/src/templates/processor.ts +++ b/packages/core/src/templates/processor.ts @@ -45,7 +45,7 @@ export function processTemplate( const unreplacedMatches = processed.match(/\{\{[^}]+\}\}/g); if (unreplacedMatches) { throw new KnowledgeError( - ErrorType._TEMPLATE_ERROR, + ErrorType.TEMPLATE_ERROR, `Template contains invalid variables: ${unreplacedMatches.join(", ")}`, { template, unreplacedMatches }, ); @@ -57,7 +57,7 @@ export function processTemplate( throw error; } throw new KnowledgeError( - ErrorType._TEMPLATE_ERROR, + ErrorType.TEMPLATE_ERROR, `Failed to process template: ${(error as Error).message}`, { template, context, error }, ); @@ -95,7 +95,7 @@ export function validateTemplate(template: string): boolean { if (invalidVariables.length > 0) { throw new KnowledgeError( - ErrorType._TEMPLATE_ERROR, + ErrorType.TEMPLATE_ERROR, `Template contains invalid variables: ${invalidVariables.join(", ")}. Allowed variables: ${ALLOWED_TEMPLATE_VARIABLES.join(", ")}`, { template, @@ -112,7 +112,7 @@ export function validateTemplate(template: string): boolean { if (missingRequired.length > 0) { throw new KnowledgeError( - ErrorType._TEMPLATE_ERROR, + ErrorType.TEMPLATE_ERROR, `Template missing required variables: ${missingRequired.join(", ")}. Required variables: ${REQUIRED_TEMPLATE_VARIABLES.join(", ")}`, { template, diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index e7a3161..e6dc344 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -119,14 +119,16 @@ export interface TemplateContext { /** * Error types that can occur in the core system + * Note: Linter may warn about "unused" enum values, but these are used throughout + * the codebase as ErrorType.CONFIG_NOT_FOUND, etc. The warnings are false positives. */ export enum ErrorType { - _CONFIG_NOT_FOUND = "CONFIG_NOT_FOUND", - _CONFIG_INVALID = "CONFIG_INVALID", - _DOCSET_NOT_FOUND = "DOCSET_NOT_FOUND", - _PATH_INVALID = "PATH_INVALID", - _TEMPLATE_ERROR = "TEMPLATE_ERROR", - _YAML_PARSE_ERROR = "YAML_PARSE_ERROR", + CONFIG_NOT_FOUND = "CONFIG_NOT_FOUND", + CONFIG_INVALID = "CONFIG_INVALID", + DOCSET_NOT_FOUND = "DOCSET_NOT_FOUND", + PATH_INVALID = "PATH_INVALID", + TEMPLATE_ERROR = "TEMPLATE_ERROR", + YAML_PARSE_ERROR = "YAML_PARSE_ERROR", } /** From fdca2c2749cc743e1e49a16605bc22d388361d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Wed, 26 Nov 2025 08:14:50 +0100 Subject: [PATCH 10/11] formatting --- packages/cli/src/commands/status.ts | 20 +++++++------------- packages/mcp-server/src/server.ts | 14 ++++++++------ 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/packages/cli/src/commands/status.ts b/packages/cli/src/commands/status.ts index 60488da..0cf4890 100644 --- a/packages/cli/src/commands/status.ts +++ b/packages/cli/src/commands/status.ts @@ -178,16 +178,14 @@ function displaySummary(statuses: DocsetStatus[]) { } if (!initialized) { + console.log(`${chalk.bold(docset.id)} (${docset.name})`); console.log( - `${chalk.bold(docset.id)} (${docset.name})`, - ); - console.log( - chalk.gray(` Not initialized | ${docset.sources?.length || 0} source(s) configured`), + chalk.gray( + ` Not initialized | ${docset.sources?.length || 0} source(s) configured`, + ), ); console.log(); - console.log( - chalk.blue(` πŸ’‘ Run: agentic-knowledge init ${docset.id}`), - ); + console.log(chalk.blue(` πŸ’‘ Run: agentic-knowledge init ${docset.id}`)); continue; } @@ -202,17 +200,13 @@ function displaySummary(statuses: DocsetStatus[]) { const initDate = new Date(metadata.initialized_at); const dateDisplay = initDate.toISOString().split("T")[0]; // YYYY-MM-DD format - console.log( - `${chalk.bold(docset.id)} (${docset.name})`, - ); + console.log(`${chalk.bold(docset.id)} (${docset.name})`); console.log( chalk.gray( ` Initialized | ${metadata.total_files} files | ${sources.length}/${metadata.sources_count} source(s) loaded`, ), ); - console.log( - chalk.gray(` Initialized: ${dateDisplay}`), - ); + console.log(chalk.gray(` Initialized: ${dateDisplay}`)); } } diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index 304e7d1..dd45c19 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -142,7 +142,7 @@ After configuring, the tool will show available docsets here.`, generalized_keywords: { type: "string", description: - 'Related terms, synonyms, or contextual keywords that may appear alongside your primary keywords but are not your main target.', + "Related terms, synonyms, or contextual keywords that may appear alongside your primary keywords but are not your main target.", }, }, required: ["docset_id", "keywords"], @@ -167,7 +167,9 @@ After configuring, the tool will show available docsets here.`, const { config } = configData; const docsetInfo = config.docsets .map((docset) => { - const description = docset.description ? ` - ${docset.description}` : ""; + const description = docset.description + ? ` - ${docset.description}` + : ""; return `β€’ **${docset.id}** (${docset.name})${description}`; }) .join("\n"); @@ -255,7 +257,7 @@ Use the path and search terms with your text search tools (grep, rg, ripgrep, fi "No configuration file found.\n\n" + "To configure docsets:\n\n" + "**Option 1: Use CLI (recommended)**\n" + - "agentic-knowledge create --preset git-repo --id my-docs --name \"My Docs\" --url \n" + + 'agentic-knowledge create --preset git-repo --id my-docs --name "My Docs" --url \n' + "agentic-knowledge init my-docs\n\n" + "**Option 2: Manual configuration**\n" + "Create .knowledge/config.yaml in your project root.\n" + @@ -345,19 +347,19 @@ Use the path and search terms with your text search tools (grep, rg, ripgrep, fi "To configure docsets:\n\n" + "**Option 1: Use CLI (recommended)**\n" + "```bash\n" + - "agentic-knowledge create --preset git-repo --id my-docs --name \"My Docs\" --url \n" + + 'agentic-knowledge create --preset git-repo --id my-docs --name "My Docs" --url \n' + "agentic-knowledge init my-docs\n" + "```\n\n" + "**Option 2: Manual configuration**\n" + "Create `.knowledge/config.yaml`:\n" + "```yaml\n" + - "version: \"1.0\"\n" + + 'version: "1.0"\n' + "docsets:\n" + " - id: my-docs\n" + " name: My Documentation\n" + " sources:\n" + " - type: local_folder\n" + - " paths: [\"./docs\"]\n" + + ' paths: ["./docs"]\n' + "```", }, ], From 65da5ff6f7e4e62e02e1becfb0573b84d661ffa2 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Nov 2025 07:30:13 +0000 Subject: [PATCH 11/11] fix: prevent search on uninitialized docsets Previously, search_docs would succeed even when a docset was created but not initialized, returning search instructions for an empty directory. Changes: - Check for .agentic-metadata.json file instead of just directory existence - Add test case for uninitialized docset search (should fail with helpful error) - Update web-sources tests to include metadata file in setup The fix ensures that docsets with git_repo sources must be initialized (via 'agentic-knowledge init') before they can be searched, providing clear error messages with CLI instructions when attempting to search an uninitialized docset. Fixes bug where docsets created via CLI but not initialized would still return successful search results. --- .../src/__tests__/integration.test.ts | 50 +++++++++++++++++++ .../src/__tests__/web-sources.test.ts | 13 +++++ packages/mcp-server/src/server.ts | 10 ++-- 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/packages/mcp-server/src/__tests__/integration.test.ts b/packages/mcp-server/src/__tests__/integration.test.ts index bbcf6cc..fc92213 100644 --- a/packages/mcp-server/src/__tests__/integration.test.ts +++ b/packages/mcp-server/src/__tests__/integration.test.ts @@ -149,5 +149,55 @@ docsets: // Server creation should succeed even if config loading will fail later // This tests that server initialization is robust }); + + it("should fail when searching uninitialized docset", async () => { + // Create a docset with git_repo source that is NOT initialized + const uninitializedConfig = ` +version: "1.0" +docsets: + - id: "uninitialized-docs" + name: "Uninitialized Documentation" + description: "A docset that hasn't been initialized yet" + sources: + - type: git_repo + url: "https://github.com/example/repo.git" + local_path: ".knowledge/docsets/uninitialized-docs" +`; + await fs.writeFile(tempConfigPath, uninitializedConfig); + + const server = createAgenticKnowledgeServer(); + + // Create the docset directory (simulating what the create command does) + // but don't create the .agentic-metadata.json file (which init command creates) + const docsetDir = join( + tempDir, + ".knowledge", + "docsets", + "uninitialized-docs", + ); + await fs.mkdir(docsetDir, { recursive: true }); + + // Try to search the uninitialized docset + const callToolHandler = (server as any)._requestHandlers.get( + "tools/call", + ); + expect(callToolHandler).toBeDefined(); + + const result = await callToolHandler({ + method: "tools/call", + params: { + name: "search_docs", + arguments: { + docset_id: "uninitialized-docs", + keywords: "test", + }, + }, + }); + + // Should return an error + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain("not initialized"); + expect(result.content[0].text).toContain("agentic-knowledge init"); + }); }); }); diff --git a/packages/mcp-server/src/__tests__/web-sources.test.ts b/packages/mcp-server/src/__tests__/web-sources.test.ts index 57feeee..52b1b97 100644 --- a/packages/mcp-server/src/__tests__/web-sources.test.ts +++ b/packages/mcp-server/src/__tests__/web-sources.test.ts @@ -55,6 +55,19 @@ template: "Search for '{{keywords}}' in {{local_path}}. Also consider: {{general "# Test Documentation\n\nThis simulates downloaded web content.", ); + // Create metadata file (simulating what init command creates) + const metadata = { + docset_id: "web-source-docs", + docset_name: "Web Source Documentation", + initialized_at: new Date().toISOString(), + total_files: 1, + sources_count: 1, + }; + await fs.writeFile( + join(webSourceDir, ".agentic-metadata.json"), + JSON.stringify(metadata, null, 2), + ); + // Mock process.cwd to return our temp directory vi.spyOn(process, "cwd").mockReturnValue(tempDir); diff --git a/packages/mcp-server/src/server.ts b/packages/mcp-server/src/server.ts index dd45c19..e4a4421 100644 --- a/packages/mcp-server/src/server.ts +++ b/packages/mcp-server/src/server.ts @@ -283,15 +283,19 @@ Use the path and search terms with your text search tools (grep, rg, ripgrep, fi // Calculate local path const localPath = calculateLocalPath(docset, configPath); - // Check if docset is initialized (for git_repo sources) + // Check if docset is initialized by checking for metadata file const primarySource = docset.sources?.[0]; if (primarySource?.type === "git_repo") { - // For git repos, the path should be absolute or relative to project root + // For git repos, check if .agentic-metadata.json exists const configDir = dirname(configPath); const projectRoot = dirname(configDir); const absolutePath = resolve(projectRoot, localPath); + const metadataPath = resolve( + absolutePath, + ".agentic-metadata.json", + ); - if (!existsSync(absolutePath)) { + if (!existsSync(metadataPath)) { throw new Error( `Docset '${docset_id}' is not initialized.\n\n` + `The docset is configured but hasn't been initialized yet.\n\n` +