directory-indexer 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -1
- package/dist/cli.js +122 -5
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,11 +34,12 @@ docker run -d --name ollama -p 127.0.0.1:11434:11434 -v ollama:/root/.ollama oll
|
|
|
34
34
|
# Pull the embedding model
|
|
35
35
|
docker exec ollama ollama pull nomic-embed-text
|
|
36
36
|
```
|
|
37
|
+
**Note:** Make sure the embedding model is pulled before you start indexing.
|
|
37
38
|
|
|
38
39
|
**3. Index your directories**
|
|
39
40
|
|
|
40
41
|
```bash
|
|
41
|
-
npx directory-indexer@latest index
|
|
42
|
+
npx directory-indexer@latest index ./WorkNotes ./Projects
|
|
42
43
|
```
|
|
43
44
|
|
|
44
45
|
**4. Configure AI assistant** _(Claude Desktop, Cursor, Cline, Roo Code, Zed etc.)_
|
|
@@ -56,6 +57,23 @@ Add to your MCP configuration:
|
|
|
56
57
|
}
|
|
57
58
|
```
|
|
58
59
|
|
|
60
|
+
If you experience issues on windows adding this MCP. You can install the package globally using
|
|
61
|
+
```
|
|
62
|
+
npm install -g directory-indexer@latest
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Then use the following MCP configuration
|
|
66
|
+
```
|
|
67
|
+
{
|
|
68
|
+
"mcpServers": {
|
|
69
|
+
"directory-indexer": {
|
|
70
|
+
"command": "directory-indexer",
|
|
71
|
+
"args": [ "serve" ]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
59
77
|
Your AI assistant will automatically start the MCP server and can now search your indexed files.
|
|
60
78
|
|
|
61
79
|
**Advanced:** For organizing content into focused search areas, see [Workspace Support](#workspace-support).
|
package/dist/cli.js
CHANGED
|
@@ -430,6 +430,113 @@ async function startMcpServer(config) {
|
|
|
430
430
|
console.error("MCP server started successfully");
|
|
431
431
|
}
|
|
432
432
|
}
|
|
433
|
+
class PrerequisiteError extends Error {
|
|
434
|
+
constructor(message, cause) {
|
|
435
|
+
super(message);
|
|
436
|
+
this.cause = cause;
|
|
437
|
+
this.name = "PrerequisiteError";
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
async function checkQdrant(config) {
|
|
441
|
+
try {
|
|
442
|
+
const response = await fetch(`${config.storage.qdrantEndpoint}/healthz`);
|
|
443
|
+
return response.ok;
|
|
444
|
+
} catch {
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
async function checkOllama(config) {
|
|
449
|
+
try {
|
|
450
|
+
const response = await fetch(`${config.embedding.endpoint}/api/tags`);
|
|
451
|
+
return response.ok;
|
|
452
|
+
} catch {
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
async function checkOllamaModel(config) {
|
|
457
|
+
try {
|
|
458
|
+
const response = await fetch(`${config.embedding.endpoint}/api/tags`);
|
|
459
|
+
if (!response.ok) return false;
|
|
460
|
+
const data = await response.json();
|
|
461
|
+
const models = data.models || [];
|
|
462
|
+
return models.some((model) => model.name.includes(config.embedding.model));
|
|
463
|
+
} catch {
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
async function checkOpenAI(config) {
|
|
468
|
+
if (!process.env.OPENAI_API_KEY) return false;
|
|
469
|
+
try {
|
|
470
|
+
const response = await fetch("https://api.openai.com/v1/embeddings", {
|
|
471
|
+
method: "POST",
|
|
472
|
+
headers: {
|
|
473
|
+
"Content-Type": "application/json",
|
|
474
|
+
"Authorization": `Bearer ${process.env.OPENAI_API_KEY}`
|
|
475
|
+
},
|
|
476
|
+
body: JSON.stringify({
|
|
477
|
+
model: config.embedding.model,
|
|
478
|
+
input: "test"
|
|
479
|
+
})
|
|
480
|
+
});
|
|
481
|
+
return response.ok;
|
|
482
|
+
} catch {
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
function createErrorMessage(qdrantOk, embeddingOk, provider) {
|
|
487
|
+
const errors = [];
|
|
488
|
+
if (!qdrantOk) {
|
|
489
|
+
errors.push("Qdrant database is inaccessible");
|
|
490
|
+
}
|
|
491
|
+
if (!embeddingOk) {
|
|
492
|
+
if (provider === "ollama") {
|
|
493
|
+
errors.push("Ollama embedding service is inaccessible or model unavailable");
|
|
494
|
+
} else if (provider === "openai") {
|
|
495
|
+
errors.push("OpenAI API is inaccessible or key invalid");
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
errors.push("");
|
|
499
|
+
errors.push("For setup instructions, see: https://github.com/peteretelej/directory-indexer#setup");
|
|
500
|
+
return errors.join("\n");
|
|
501
|
+
}
|
|
502
|
+
async function validateIndexPrerequisites(config) {
|
|
503
|
+
const [qdrantOk, embeddingOk] = await Promise.all([
|
|
504
|
+
checkQdrant(config),
|
|
505
|
+
checkEmbeddingService(config)
|
|
506
|
+
]);
|
|
507
|
+
if (!qdrantOk || !embeddingOk) {
|
|
508
|
+
throw new PrerequisiteError(createErrorMessage(qdrantOk, embeddingOk, config.embedding.provider));
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
async function validateSearchPrerequisites(config) {
|
|
512
|
+
const qdrantOk = await checkQdrant(config);
|
|
513
|
+
if (!qdrantOk) {
|
|
514
|
+
throw new PrerequisiteError(createErrorMessage(false, true, config.embedding.provider));
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
async function checkEmbeddingService(config) {
|
|
518
|
+
switch (config.embedding.provider) {
|
|
519
|
+
case "ollama":
|
|
520
|
+
return await checkOllama(config) && await checkOllamaModel(config);
|
|
521
|
+
case "openai":
|
|
522
|
+
return await checkOpenAI(config);
|
|
523
|
+
case "mock":
|
|
524
|
+
return true;
|
|
525
|
+
default:
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
async function getServiceStatus(config) {
|
|
530
|
+
const [qdrantOk, embeddingOk] = await Promise.all([
|
|
531
|
+
checkQdrant(config),
|
|
532
|
+
checkEmbeddingService(config)
|
|
533
|
+
]);
|
|
534
|
+
return {
|
|
535
|
+
qdrant: qdrantOk,
|
|
536
|
+
embedding: embeddingOk,
|
|
537
|
+
embeddingProvider: config.embedding.provider
|
|
538
|
+
};
|
|
539
|
+
}
|
|
433
540
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
434
541
|
const packageJsonPath = join(__dirname, "../package.json");
|
|
435
542
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
@@ -440,6 +547,7 @@ async function main() {
|
|
|
440
547
|
program.command("index").description("Index directories for semantic search").argument("<paths...>", "Directory paths to index").option("-v, --verbose", "Enable verbose logging").action(async (paths, options) => {
|
|
441
548
|
try {
|
|
442
549
|
const config = await loadConfig({ verbose: options.verbose });
|
|
550
|
+
await validateIndexPrerequisites(config);
|
|
443
551
|
console.log(`Indexing ${paths.length} ${paths.length === 1 ? "directory" : "directories"}: ${paths.join(", ")}`);
|
|
444
552
|
const result = await indexDirectories(paths, config);
|
|
445
553
|
console.log(`Indexed ${result.indexed} files, skipped ${result.skipped} files, ${result.failed} failed`);
|
|
@@ -457,7 +565,8 @@ async function main() {
|
|
|
457
565
|
});
|
|
458
566
|
program.command("search").description("Search indexed content semantically").argument("<query>", "Search query").option("-l, --limit <number>", "Maximum number of results", "10").option("-c, --show-chunks", "Show individual chunk scores and IDs").option("-v, --verbose", "Enable verbose logging").action(async (query, options) => {
|
|
459
567
|
try {
|
|
460
|
-
await loadConfig({ verbose: options.verbose });
|
|
568
|
+
const config = await loadConfig({ verbose: options.verbose });
|
|
569
|
+
await validateSearchPrerequisites(config);
|
|
461
570
|
const results = await searchContent(query, { limit: parseInt(options.limit) });
|
|
462
571
|
if (results.length === 0) {
|
|
463
572
|
console.log("No results found");
|
|
@@ -483,7 +592,8 @@ async function main() {
|
|
|
483
592
|
});
|
|
484
593
|
program.command("similar").description("Find files similar to a given file").argument("<file>", "File path to find similar files for").option("-l, --limit <number>", "Maximum number of results", "10").option("-v, --verbose", "Enable verbose logging").action(async (filePath, options) => {
|
|
485
594
|
try {
|
|
486
|
-
await loadConfig({ verbose: options.verbose });
|
|
595
|
+
const config = await loadConfig({ verbose: options.verbose });
|
|
596
|
+
await validateSearchPrerequisites(config);
|
|
487
597
|
const results = await findSimilarFiles(filePath, parseInt(options.limit));
|
|
488
598
|
if (results.length === 0) {
|
|
489
599
|
console.log("No similar files found");
|
|
@@ -522,11 +632,18 @@ async function main() {
|
|
|
522
632
|
});
|
|
523
633
|
program.command("status").description("Show indexing status").option("-v, --verbose", "Enable verbose logging").action(async (options) => {
|
|
524
634
|
try {
|
|
525
|
-
await loadConfig({ verbose: options.verbose });
|
|
526
|
-
const status = await
|
|
635
|
+
const config = await loadConfig({ verbose: options.verbose });
|
|
636
|
+
const [status, serviceStatus] = await Promise.all([
|
|
637
|
+
getIndexStatus(),
|
|
638
|
+
getServiceStatus(config)
|
|
639
|
+
]);
|
|
527
640
|
console.log("Directory Indexer Status Report");
|
|
528
641
|
console.log("=====================================");
|
|
529
642
|
console.log("");
|
|
643
|
+
console.log("SERVICE STATUS:");
|
|
644
|
+
console.log(` • Qdrant database: ${serviceStatus.qdrant ? "Connected" : "Disconnected"}`);
|
|
645
|
+
console.log(` • Embedding service (${serviceStatus.embeddingProvider}): ${serviceStatus.embedding ? "Connected" : "Disconnected"}`);
|
|
646
|
+
console.log("");
|
|
530
647
|
console.log("OVERVIEW:");
|
|
531
648
|
console.log(` • ${status.directoriesIndexed} directories have been indexed`);
|
|
532
649
|
console.log(` • ${status.filesIndexed} files processed for semantic search`);
|
|
@@ -574,7 +691,7 @@ async function main() {
|
|
|
574
691
|
console.log(` • ${issue}`);
|
|
575
692
|
});
|
|
576
693
|
console.log("");
|
|
577
|
-
console.log("
|
|
694
|
+
console.log("Note: Status messages above may be normal during setup or active indexing.");
|
|
578
695
|
} else {
|
|
579
696
|
console.log("");
|
|
580
697
|
console.log("SYSTEM STATUS:");
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sources":["../src/mcp-handlers.ts","../src/mcp.ts","../src/cli.ts"],"sourcesContent":["import { Config } from './config.js';\nimport { indexDirectories } from './indexing.js';\nimport { searchContent, findSimilarFiles, getFileContent, getChunkContent } from './search.js';\nimport { getIndexStatus } from './storage.js';\nimport { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\n\n// Type-safe interfaces for MCP tool arguments\ninterface IndexToolArgs {\n directory_path: string;\n}\n\ninterface SearchToolArgs {\n query: string;\n limit?: number;\n workspace?: string;\n}\n\ninterface SimilarFilesToolArgs {\n file_path: string;\n limit?: number;\n workspace?: string;\n}\n\ninterface GetContentToolArgs {\n file_path: string;\n chunks?: string;\n}\n\ninterface GetChunkToolArgs {\n file_path: string;\n chunk_id: string;\n}\n\n// Type guard functions\nfunction isIndexToolArgs(args: unknown): args is IndexToolArgs {\n return typeof args === 'object' && args !== null && \n typeof (args as IndexToolArgs).directory_path === 'string';\n}\n\nfunction isSearchToolArgs(args: unknown): args is SearchToolArgs {\n return typeof args === 'object' && args !== null && \n typeof (args as SearchToolArgs).query === 'string';\n}\n\nfunction isSimilarFilesToolArgs(args: unknown): args is SimilarFilesToolArgs {\n return typeof args === 'object' && args !== null && \n typeof (args as SimilarFilesToolArgs).file_path === 'string';\n}\n\nfunction isGetContentToolArgs(args: unknown): args is GetContentToolArgs {\n return typeof args === 'object' && args !== null && \n typeof (args as GetContentToolArgs).file_path === 'string';\n}\n\nfunction isGetChunkToolArgs(args: unknown): args is GetChunkToolArgs {\n return typeof args === 'object' && args !== null && \n typeof (args as GetChunkToolArgs).file_path === 'string' &&\n typeof (args as GetChunkToolArgs).chunk_id === 'string';\n}\n\nexport async function handleIndexTool(args: unknown, config: Config): Promise<CallToolResult> {\n if (!isIndexToolArgs(args)) {\n throw new Error('directory_path is required');\n }\n \n const paths = args.directory_path.split(',').map((p: string) => p.trim());\n const result = await indexDirectories(paths, config);\n \n let responseText = `Indexed ${result.indexed} files, skipped ${result.skipped} files, ${result.failed} failed`;\n \n if (result.errors.length > 0) {\n responseText += `\\nErrors: [\\n`;\n result.errors.forEach(error => {\n responseText += ` '${error}'\\n`;\n });\n responseText += `]`;\n }\n \n return {\n content: [\n {\n type: 'text',\n text: responseText\n }\n ]\n };\n}\n\nasync function validateWorkspace(workspace?: string): Promise<{ workspace?: string; message?: string }> {\n if (!workspace) return { workspace };\n \n const config = (await import('./config.js')).loadConfig();\n const { getAvailableWorkspaces } = await import('./config.js');\n const availableWorkspaces = getAvailableWorkspaces(config);\n \n if (availableWorkspaces.includes(workspace)) {\n return { workspace };\n }\n \n // Invalid workspace - search all content with informative message\n const message = availableWorkspaces.length > 0\n ? `Note: Workspace '${workspace}' not found. Searching all content instead. Available workspaces: ${availableWorkspaces.join(', ')}. Use server_info tool to see workspace details.`\n : `Note: Workspace '${workspace}' not found and no workspaces are configured. Searching all indexed content.`;\n \n return { workspace: undefined, message };\n}\n\nexport async function handleSearchTool(args: unknown): Promise<CallToolResult> {\n if (!isSearchToolArgs(args)) {\n throw new Error('query is required');\n }\n \n const { workspace, message } = await validateWorkspace(args.workspace);\n const results = await searchContent(args.query, { limit: args.limit || 10, workspace });\n \n const response = message \n ? `${message}\\n\\n${JSON.stringify(results, null, 2)}`\n : JSON.stringify(results, null, 2);\n \n return {\n content: [{ type: 'text', text: response }]\n };\n}\n\nexport async function handleSimilarFilesTool(args: unknown): Promise<CallToolResult> {\n if (!isSimilarFilesToolArgs(args)) {\n throw new Error('file_path is required');\n }\n \n const { workspace, message } = await validateWorkspace(args.workspace);\n const results = await findSimilarFiles(args.file_path, args.limit || 10, workspace);\n \n const response = message \n ? `${message}\\n\\n${JSON.stringify(results, null, 2)}`\n : JSON.stringify(results, null, 2);\n \n return {\n content: [{ type: 'text', text: response }]\n };\n}\n\nexport async function handleGetContentTool(args: unknown): Promise<CallToolResult> {\n if (!isGetContentToolArgs(args)) {\n throw new Error('file_path is required');\n }\n \n const content = await getFileContent(args.file_path, args.chunks);\n \n return {\n content: [\n {\n type: 'text',\n text: content\n }\n ]\n };\n}\n\nexport async function handleGetChunkTool(args: unknown): Promise<CallToolResult> {\n if (!isGetChunkToolArgs(args)) {\n throw new Error('file_path and chunk_id are required');\n }\n \n const content = await getChunkContent(args.file_path, args.chunk_id);\n \n return {\n content: [\n {\n type: 'text',\n text: content\n }\n ]\n };\n}\n\nexport async function handleServerInfoTool(version: string): Promise<CallToolResult> {\n const status = await getIndexStatus();\n \n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n name: 'directory-indexer',\n version: version,\n status: status\n }, null, 2)\n }\n ]\n };\n}\n\nexport function formatErrorResponse(error: unknown): CallToolResult {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${errorMessage}`\n }\n ],\n isError: true\n };\n}","import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { \n CallToolRequestSchema, \n ListToolsRequestSchema,\n Tool\n} from '@modelcontextprotocol/sdk/types.js';\nimport { readFileSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport { Config } from './config.js';\nimport { \n handleIndexTool, \n handleSearchTool, \n handleSimilarFilesTool, \n handleGetContentTool, \n handleGetChunkTool, \n handleServerInfoTool,\n formatErrorResponse\n} from './mcp-handlers.js';\n\n// Read version from package.json\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst packageJsonPath = join(__dirname, '../package.json');\nconst packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\nconst VERSION = packageJson.version;\n\nconst MCP_TOOLS: Tool[] = [\n {\n name: 'index',\n description: `Index directories to make their files searchable. Processes files to create vector embeddings for semantic search.\n\nWhen to use this tool:\n- User specifically requests indexing a directory as a knowledge base\n- Adding new documentation, code repositories, or file collections to search\n- Updating index when many files have changed\n\nHow it works:\n- Recursively scans directories for supported file types\n- Extracts text content and splits into chunks\n- Generates vector embeddings for semantic similarity\n- Stores in database for fast retrieval\n\nExamples:\n- Index documentation: \"/home/user/docs/project-wiki\"\n- Index codebase: \"/home/user/projects/api-server\"\n- Index multiple directories: \"/home/user/docs,/home/user/configs\"\n\nIndexing can take several minutes for large directories. Most users will already have directories indexed and can directly use search tool. Use server_info to check current indexing status first.`,\n inputSchema: {\n type: 'object',\n properties: {\n directory_path: {\n type: 'string',\n description: 'Comma-separated list of absolute directory paths to index. Must be absolute paths since MCP server runs independently. Examples: \"/home/user/projects\" (Unix) or \"C:\\\\Users\\\\user\\\\projects\" (Windows)'\n }\n },\n required: ['directory_path']\n }\n },\n {\n name: 'search',\n description: `Search indexed files using natural language queries. Finds files containing content semantically similar to the query.\n\nWhen to use this tool:\n- Find documentation, guides, or explanations about specific topics\n- Locate code files implementing certain functionality or patterns\n- Discover configuration files, scripts, or settings related to a topic\n- Search for files covering specific concepts or technologies\n\nHow it works:\n- Converts query to vector embedding using semantic similarity\n- Searches all indexed file chunks for relevant content\n- Groups results by file and calculates average relevance scores\n- Returns files ranked by relevance score\n\nExamples:\n- \"database configuration and connection pooling setup\" - finds config files, documentation about DB setup\n- \"comprehensive error handling patterns and exception management\" - finds code files with exception handling\n- \"JWT authentication implementation and session management\" - finds auth-related code and docs\n- \"REST API documentation and endpoint specifications\" - finds API guides, endpoint definitions\n- \"Docker deployment scripts and CI/CD pipeline configuration\" - finds deployment automation\n\nReturns files with similarity scores and chunk information. Use get_content to retrieve full file content or get_chunk to retrieve specific chunk content by chunk ID.\n- Groups results by file to avoid duplicates from multiple matching sections\n\nResponse format:\n- Returns lightweight metadata including file paths, relevance scores, and chunk IDs\n- Use 'get_chunk' or 'get_content' tools to fetch actual content from search results\n- Chunks are sorted by relevance score within each file\n- Average similarity score calculated across all matching chunks per file\n\nExample queries:\n- \"error handling patterns and exception management strategies\" (finds try/catch, error classes, logging)\n- \"database migration scripts and schema versioning approaches\" (finds SQL, schema changes, migration files)\n- \"authentication middleware and JWT token validation logic\" (finds auth logic, JWT handling, middleware functions)`,\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Natural language search query describing what you are looking for. Can be concepts, functionality, or specific technical terms.'\n },\n limit: {\n type: 'number',\n description: 'Maximum number of files to return (default: 10). Each file may contain multiple matching chunks.',\n default: 10\n },\n workspace: {\n type: 'string',\n description: 'Optional workspace name to filter search results. Only files within the workspace directories will be searched. IMPORTANT: Use server_info tool first to discover available workspace names - using invalid workspace names will result in empty results.'\n }\n },\n required: ['query']\n }\n },\n {\n name: 'similar_files',\n description: `Find files with content similar to a reference file. Uses semantic similarity to find related documents, code files, or any text content.\n\nWhen to use this tool:\n- Find documentation similar to a specific guide or README\n- Locate related code files, configuration files, or scripts\n- Discover alternative implementations or approaches\n- Find files covering similar topics or concepts\n\nHow it works:\n- Analyzes the semantic content of the reference file\n- Compares against all indexed files using vector similarity\n- Returns files ranked by content similarity score\n\nExamples:\n- Given \"deployment-guide.md\" - finds other deployment docs, CI/CD guides, infrastructure setup\n- Given \"troubleshooting.md\" - finds other troubleshooting guides, FAQ files, error documentation\n- Given \"config.yaml\" - finds other configuration files, settings, environment setups\n- Given \"auth.py\" - finds other authentication modules, security code, middleware\n\nReturns file paths with similarity scores. Use get_content to read full files or get_chunk for specific sections.`,\n inputSchema: {\n type: 'object',\n properties: {\n file_path: {\n type: 'string',\n description: 'Absolute or relative path to the reference file. This file must have been previously indexed.'\n },\n limit: {\n type: 'number',\n description: 'Maximum number of similar files to return (default: 10). Results are sorted by similarity score.',\n default: 10\n },\n workspace: {\n type: 'string',\n description: 'Optional workspace name to filter results. Only files within the workspace directories will be considered. IMPORTANT: Use server_info tool first to discover available workspace names - using invalid workspace names will result in empty results.'\n }\n },\n required: ['file_path']\n }\n },\n {\n name: 'get_content',\n description: `Retrieve the full content of a file or specific chunks. Reads files directly from the filesystem.\n\nWhen to use this tool:\n- Get complete file content after finding files through search\n- Read documentation, code files, or configuration files for analysis\n- Extract specific sections of large files using chunk ranges\n- Access any text-based file content\n\nHow it works:\n- Reads files directly from filesystem (not from search index)\n- Returns entire file by default\n- Can return specific chunk ranges for indexed files\n- Preserves original formatting and content\n\nExamples:\n- Get full file: file_path=\"/home/user/docs/api.md\"\n- Get specific chunks: file_path=\"/home/user/code/main.py\", chunks=\"2-5\"\n- Get single chunk: file_path=\"/home/user/config.json\", chunks=\"1\"\n\nReturns file content as text. Use this after search or similar_files to read actual content.`,\n inputSchema: {\n type: 'object',\n properties: {\n file_path: {\n type: 'string',\n description: 'Absolute or relative path to the file to retrieve. File must be readable and text-based.'\n },\n chunks: {\n type: 'string',\n description: 'Optional chunk range specification. Examples: \"3\" (single chunk), \"2-5\" (chunks 2 through 5), \"1-3\" (first three chunks). Only works for indexed files.'\n }\n },\n required: ['file_path']\n }\n },\n {\n name: 'get_chunk',\n description: `Retrieve content of a specific chunk from an indexed file. Gets exact text segments identified during search.\n\nWhen to use this tool:\n- Get specific relevant sections after performing a search\n- Access only the most pertinent parts of large files\n- Retrieve content from high-scoring chunks identified in search results\n- Avoid reading entire files when only specific sections are needed\n\nHow it works:\n- Files are split into overlapping text chunks during indexing\n- Each chunk has a sequential ID (\"0\", \"1\", \"2\", etc.)\n- Search results include chunk IDs for relevant sections\n- Returns the exact content that was semantically matched\n\nExamples:\n- After search returns chunk \"3\" from \"api-docs.md\" with high score\n- Get chunk content: file_path=\"/docs/api-docs.md\", chunk_id=\"3\"\n- Returns the specific text segment that matched your query\n\nReturns chunk content as text. Use this with chunk IDs from search results to get precise content sections.`,\n inputSchema: {\n type: 'object',\n properties: {\n file_path: {\n type: 'string',\n description: 'Absolute or relative path to the indexed file containing the desired chunk.'\n },\n chunk_id: {\n type: 'string',\n description: 'ID of the specific chunk to retrieve. This is typically obtained from search results and is a sequential string like \"0\", \"1\", \"2\", etc.'\n }\n },\n required: ['file_path', 'chunk_id']\n }\n },\n {\n name: 'server_info',\n description: `Get information about server status and indexed content. Shows what directories and files are available for search.\n\nWhen to use this tool:\n- REQUIRED: Check available workspace names before using workspace parameter in search or similar_files tools\n- Check what content is already indexed before performing searches\n- Verify system is working properly\n- See indexing statistics and status\n- Understand scope of available searchable content\n\nHow it works:\n- Reports total indexed directories, files, and chunks\n- Shows database size and last indexing time\n- Lists all indexed directories with file counts\n- Lists all configured workspaces with their paths and file counts\n- Reports any errors or issues\n\nExamples:\n- Check workspaces before searching: \"What workspaces are available?\"\n- Check before searching: \"What content is indexed?\"\n- Verify after indexing: \"Did the indexing complete successfully?\"\n- Monitor system: \"How many files are searchable?\"\n\nReturns server version, indexing statistics, directory list, workspace information, and any errors. IMPORTANT: Always use this tool first to discover available workspace names when you need to search within specific workspaces.`,\n inputSchema: {\n type: 'object',\n properties: {},\n additionalProperties: false\n }\n }\n];\n\nexport async function startMcpServer(config: Config): Promise<void> {\n const server = new Server(\n {\n name: 'directory-indexer',\n version: VERSION\n },\n {\n capabilities: {\n tools: {}\n }\n }\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: MCP_TOOLS\n };\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n switch (name) {\n case 'index':\n return await handleIndexTool(args, config);\n \n case 'search':\n return await handleSearchTool(args);\n \n case 'similar_files':\n return await handleSimilarFilesTool(args);\n \n case 'get_content':\n return await handleGetContentTool(args);\n \n case 'get_chunk':\n return await handleGetChunkTool(args);\n \n case 'server_info':\n return await handleServerInfoTool(VERSION);\n \n default:\n throw new Error(`Unknown tool: ${name}`);\n }\n } catch (error) {\n return formatErrorResponse(error);\n }\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n \n if (config.verbose) {\n console.error('MCP server started successfully');\n }\n}","#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport { fileURLToPath } from 'url';\nimport { readFileSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { indexDirectories } from './indexing.js';\nimport { searchContent, findSimilarFiles, getFileContent } from './search.js';\nimport { loadConfig } from './config.js';\nimport { getIndexStatus } from './storage.js';\nimport { startMcpServer } from './mcp.js';\n\n// Read version from package.json\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst packageJsonPath = join(__dirname, '../package.json');\nconst packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\nconst VERSION = packageJson.version;\n\nexport async function main() {\n const program = new Command();\n \n program\n .name('directory-indexer')\n .description('AI-powered directory indexing with semantic search')\n .version(VERSION);\n\n program\n .command('index')\n .description('Index directories for semantic search')\n .argument('<paths...>', 'Directory paths to index')\n .option('-v, --verbose', 'Enable verbose logging')\n .action(async (paths: string[], options) => {\n try {\n const config = await loadConfig({ verbose: options.verbose });\n console.log(`Indexing ${paths.length} ${paths.length === 1 ? 'directory' : 'directories'}: ${paths.join(', ')}`);\n const result = await indexDirectories(paths, config);\n console.log(`Indexed ${result.indexed} files, skipped ${result.skipped} files, ${result.failed} failed`);\n if (result.errors.length > 0) {\n console.log(`Errors: [`);\n result.errors.forEach(error => {\n console.log(` '${error}'`);\n });\n console.log(`]`);\n }\n } catch (error) {\n console.error('Error indexing directories:', error);\n process.exit(1);\n }\n });\n\n program\n .command('search')\n .description('Search indexed content semantically')\n .argument('<query>', 'Search query')\n .option('-l, --limit <number>', 'Maximum number of results', '10')\n .option('-c, --show-chunks', 'Show individual chunk scores and IDs')\n .option('-v, --verbose', 'Enable verbose logging')\n .action(async (query: string, options) => {\n try {\n await loadConfig({ verbose: options.verbose });\n const results = await searchContent(query, { limit: parseInt(options.limit) });\n \n if (results.length === 0) {\n console.log('No results found');\n return;\n }\n\n console.log(`Found ${results.length} results:\\n`);\n results.forEach((result, index) => {\n console.log(`${index + 1}. ${result.filePath}`);\n console.log(` Score: ${result.score.toFixed(3)} (${result.matchingChunks} chunks)`);\n \n if (options.showChunks && result.chunks.length > 0) {\n console.log(` Chunks:`);\n result.chunks.forEach(chunk => {\n console.log(` - Chunk ${chunk.chunkId}: ${chunk.score.toFixed(3)}`);\n });\n }\n \n console.log();\n });\n } catch (error) {\n console.error('Error searching content:', error);\n process.exit(1);\n }\n });\n\n program\n .command('similar')\n .description('Find files similar to a given file')\n .argument('<file>', 'File path to find similar files for')\n .option('-l, --limit <number>', 'Maximum number of results', '10')\n .option('-v, --verbose', 'Enable verbose logging')\n .action(async (filePath: string, options) => {\n try {\n await loadConfig({ verbose: options.verbose });\n const results = await findSimilarFiles(filePath, parseInt(options.limit));\n \n if (results.length === 0) {\n console.log('No similar files found');\n return;\n }\n\n console.log(`Found ${results.length} similar files:\\n`);\n results.forEach((result, index) => {\n console.log(`${index + 1}. ${result.filePath}`);\n console.log(` Similarity: ${result.score.toFixed(3)}`);\n console.log();\n });\n } catch (error) {\n console.error('Error finding similar files:', error);\n process.exit(1);\n }\n });\n\n program\n .command('get')\n .description('Get file content')\n .argument('<file>', 'File path to retrieve')\n .option('-c, --chunks <range>', 'Chunk range (e.g., \"2-5\")')\n .option('-v, --verbose', 'Enable verbose logging')\n .action(async (filePath: string, options) => {\n try {\n await loadConfig({ verbose: options.verbose });\n const content = await getFileContent(filePath, options.chunks);\n console.log(content);\n } catch (error) {\n console.error('Error getting file content:', error);\n process.exit(1);\n }\n });\n\n program\n .command('serve')\n .description('Start MCP server')\n .option('-v, --verbose', 'Enable verbose logging')\n .action(async (options) => {\n try {\n const config = await loadConfig({ verbose: options.verbose });\n await startMcpServer(config);\n } catch (error) {\n console.error('Error starting MCP server:', error);\n process.exit(1);\n }\n });\n\n program\n .command('status')\n .description('Show indexing status')\n .option('-v, --verbose', 'Enable verbose logging')\n .action(async (options) => {\n try {\n await loadConfig({ verbose: options.verbose });\n const status = await getIndexStatus();\n \n console.log('Directory Indexer Status Report');\n console.log('=====================================');\n console.log('');\n console.log('OVERVIEW:');\n console.log(` • ${status.directoriesIndexed} directories have been indexed`);\n console.log(` • ${status.filesIndexed} files processed for semantic search`);\n console.log(` • ${status.chunksIndexed} text chunks available for AI search`);\n console.log(` • Database storage: ${status.databaseSize}`);\n console.log(` • Most recent indexing: ${status.lastIndexed || 'No indexing performed yet'}`);\n \n if (status.errors.length > 0) {\n console.log(` • Processing errors encountered: ${status.errors.length}`);\n if (options.verbose) {\n console.log('');\n console.log('RECENT ERRORS:');\n status.errors.slice(0, 5).forEach(error => {\n console.log(` - ${error}`);\n });\n }\n }\n \n console.log('');\n console.log('INDEXED DIRECTORIES:');\n if (status.directories.length === 0) {\n console.log(' No directories have been indexed yet.');\n console.log(' Run \"directory-indexer index <path>\" to start indexing.');\n } else {\n status.directories.forEach(dir => {\n console.log('');\n console.log(` Directory: ${dir.path}`);\n console.log(` • Indexing status: ${dir.status}`);\n console.log(` • Files processed: ${dir.filesCount}`);\n console.log(` • Searchable chunks: ${dir.chunksCount}`);\n console.log(` • Last indexed: ${dir.lastIndexed || 'Never completed'}`);\n if (dir.errors.length > 0) {\n console.log(` • Files with errors: ${dir.errors.length}`);\n if (options.verbose) {\n console.log(' • Recent errors:');\n dir.errors.slice(0, 3).forEach(error => {\n console.log(` - ${error}`);\n });\n }\n }\n });\n }\n \n if (!status.qdrantConsistency.isConsistent) {\n console.log('');\n console.log('SYSTEM STATUS:');\n status.qdrantConsistency.issues.forEach(issue => {\n console.log(` • ${issue}`);\n });\n console.log('');\n console.log('ℹ️ Note: Status messages above may be normal during setup or active indexing.');\n } else {\n console.log('');\n console.log('SYSTEM STATUS:');\n console.log(' • All systems operational - ready for AI-powered search');\n }\n } catch (error) {\n console.error('Error getting status:', error);\n process.exit(1);\n }\n });\n\n await program.parseAsync();\n}\n\n// Main function is already exported above"],"names":["__dirname","packageJsonPath","packageJson","VERSION"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","sources":["../src/mcp-handlers.ts","../src/mcp.ts","../src/prerequisites.ts","../src/cli.ts"],"sourcesContent":["import { Config } from './config.js';\nimport { indexDirectories } from './indexing.js';\nimport { searchContent, findSimilarFiles, getFileContent, getChunkContent } from './search.js';\nimport { getIndexStatus } from './storage.js';\nimport { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\n\n// Type-safe interfaces for MCP tool arguments\ninterface IndexToolArgs {\n directory_path: string;\n}\n\ninterface SearchToolArgs {\n query: string;\n limit?: number;\n workspace?: string;\n}\n\ninterface SimilarFilesToolArgs {\n file_path: string;\n limit?: number;\n workspace?: string;\n}\n\ninterface GetContentToolArgs {\n file_path: string;\n chunks?: string;\n}\n\ninterface GetChunkToolArgs {\n file_path: string;\n chunk_id: string;\n}\n\n// Type guard functions\nfunction isIndexToolArgs(args: unknown): args is IndexToolArgs {\n return typeof args === 'object' && args !== null && \n typeof (args as IndexToolArgs).directory_path === 'string';\n}\n\nfunction isSearchToolArgs(args: unknown): args is SearchToolArgs {\n return typeof args === 'object' && args !== null && \n typeof (args as SearchToolArgs).query === 'string';\n}\n\nfunction isSimilarFilesToolArgs(args: unknown): args is SimilarFilesToolArgs {\n return typeof args === 'object' && args !== null && \n typeof (args as SimilarFilesToolArgs).file_path === 'string';\n}\n\nfunction isGetContentToolArgs(args: unknown): args is GetContentToolArgs {\n return typeof args === 'object' && args !== null && \n typeof (args as GetContentToolArgs).file_path === 'string';\n}\n\nfunction isGetChunkToolArgs(args: unknown): args is GetChunkToolArgs {\n return typeof args === 'object' && args !== null && \n typeof (args as GetChunkToolArgs).file_path === 'string' &&\n typeof (args as GetChunkToolArgs).chunk_id === 'string';\n}\n\nexport async function handleIndexTool(args: unknown, config: Config): Promise<CallToolResult> {\n if (!isIndexToolArgs(args)) {\n throw new Error('directory_path is required');\n }\n \n const paths = args.directory_path.split(',').map((p: string) => p.trim());\n const result = await indexDirectories(paths, config);\n \n let responseText = `Indexed ${result.indexed} files, skipped ${result.skipped} files, ${result.failed} failed`;\n \n if (result.errors.length > 0) {\n responseText += `\\nErrors: [\\n`;\n result.errors.forEach(error => {\n responseText += ` '${error}'\\n`;\n });\n responseText += `]`;\n }\n \n return {\n content: [\n {\n type: 'text',\n text: responseText\n }\n ]\n };\n}\n\nasync function validateWorkspace(workspace?: string): Promise<{ workspace?: string; message?: string }> {\n if (!workspace) return { workspace };\n \n const config = (await import('./config.js')).loadConfig();\n const { getAvailableWorkspaces } = await import('./config.js');\n const availableWorkspaces = getAvailableWorkspaces(config);\n \n if (availableWorkspaces.includes(workspace)) {\n return { workspace };\n }\n \n // Invalid workspace - search all content with informative message\n const message = availableWorkspaces.length > 0\n ? `Note: Workspace '${workspace}' not found. Searching all content instead. Available workspaces: ${availableWorkspaces.join(', ')}. Use server_info tool to see workspace details.`\n : `Note: Workspace '${workspace}' not found and no workspaces are configured. Searching all indexed content.`;\n \n return { workspace: undefined, message };\n}\n\nexport async function handleSearchTool(args: unknown): Promise<CallToolResult> {\n if (!isSearchToolArgs(args)) {\n throw new Error('query is required');\n }\n \n const { workspace, message } = await validateWorkspace(args.workspace);\n const results = await searchContent(args.query, { limit: args.limit || 10, workspace });\n \n const response = message \n ? `${message}\\n\\n${JSON.stringify(results, null, 2)}`\n : JSON.stringify(results, null, 2);\n \n return {\n content: [{ type: 'text', text: response }]\n };\n}\n\nexport async function handleSimilarFilesTool(args: unknown): Promise<CallToolResult> {\n if (!isSimilarFilesToolArgs(args)) {\n throw new Error('file_path is required');\n }\n \n const { workspace, message } = await validateWorkspace(args.workspace);\n const results = await findSimilarFiles(args.file_path, args.limit || 10, workspace);\n \n const response = message \n ? `${message}\\n\\n${JSON.stringify(results, null, 2)}`\n : JSON.stringify(results, null, 2);\n \n return {\n content: [{ type: 'text', text: response }]\n };\n}\n\nexport async function handleGetContentTool(args: unknown): Promise<CallToolResult> {\n if (!isGetContentToolArgs(args)) {\n throw new Error('file_path is required');\n }\n \n const content = await getFileContent(args.file_path, args.chunks);\n \n return {\n content: [\n {\n type: 'text',\n text: content\n }\n ]\n };\n}\n\nexport async function handleGetChunkTool(args: unknown): Promise<CallToolResult> {\n if (!isGetChunkToolArgs(args)) {\n throw new Error('file_path and chunk_id are required');\n }\n \n const content = await getChunkContent(args.file_path, args.chunk_id);\n \n return {\n content: [\n {\n type: 'text',\n text: content\n }\n ]\n };\n}\n\nexport async function handleServerInfoTool(version: string): Promise<CallToolResult> {\n const status = await getIndexStatus();\n \n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n name: 'directory-indexer',\n version: version,\n status: status\n }, null, 2)\n }\n ]\n };\n}\n\nexport function formatErrorResponse(error: unknown): CallToolResult {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${errorMessage}`\n }\n ],\n isError: true\n };\n}","import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { \n CallToolRequestSchema, \n ListToolsRequestSchema,\n Tool\n} from '@modelcontextprotocol/sdk/types.js';\nimport { readFileSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport { Config } from './config.js';\nimport { \n handleIndexTool, \n handleSearchTool, \n handleSimilarFilesTool, \n handleGetContentTool, \n handleGetChunkTool, \n handleServerInfoTool,\n formatErrorResponse\n} from './mcp-handlers.js';\n\n// Read version from package.json\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst packageJsonPath = join(__dirname, '../package.json');\nconst packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\nconst VERSION = packageJson.version;\n\nconst MCP_TOOLS: Tool[] = [\n {\n name: 'index',\n description: `Index directories to make their files searchable. Processes files to create vector embeddings for semantic search.\n\nWhen to use this tool:\n- User specifically requests indexing a directory as a knowledge base\n- Adding new documentation, code repositories, or file collections to search\n- Updating index when many files have changed\n\nHow it works:\n- Recursively scans directories for supported file types\n- Extracts text content and splits into chunks\n- Generates vector embeddings for semantic similarity\n- Stores in database for fast retrieval\n\nExamples:\n- Index documentation: \"/home/user/docs/project-wiki\"\n- Index codebase: \"/home/user/projects/api-server\"\n- Index multiple directories: \"/home/user/docs,/home/user/configs\"\n\nIndexing can take several minutes for large directories. Most users will already have directories indexed and can directly use search tool. Use server_info to check current indexing status first.`,\n inputSchema: {\n type: 'object',\n properties: {\n directory_path: {\n type: 'string',\n description: 'Comma-separated list of absolute directory paths to index. Must be absolute paths since MCP server runs independently. Examples: \"/home/user/projects\" (Unix) or \"C:\\\\Users\\\\user\\\\projects\" (Windows)'\n }\n },\n required: ['directory_path']\n }\n },\n {\n name: 'search',\n description: `Search indexed files using natural language queries. Finds files containing content semantically similar to the query.\n\nWhen to use this tool:\n- Find documentation, guides, or explanations about specific topics\n- Locate code files implementing certain functionality or patterns\n- Discover configuration files, scripts, or settings related to a topic\n- Search for files covering specific concepts or technologies\n\nHow it works:\n- Converts query to vector embedding using semantic similarity\n- Searches all indexed file chunks for relevant content\n- Groups results by file and calculates average relevance scores\n- Returns files ranked by relevance score\n\nExamples:\n- \"database configuration and connection pooling setup\" - finds config files, documentation about DB setup\n- \"comprehensive error handling patterns and exception management\" - finds code files with exception handling\n- \"JWT authentication implementation and session management\" - finds auth-related code and docs\n- \"REST API documentation and endpoint specifications\" - finds API guides, endpoint definitions\n- \"Docker deployment scripts and CI/CD pipeline configuration\" - finds deployment automation\n\nReturns files with similarity scores and chunk information. Use get_content to retrieve full file content or get_chunk to retrieve specific chunk content by chunk ID.\n- Groups results by file to avoid duplicates from multiple matching sections\n\nResponse format:\n- Returns lightweight metadata including file paths, relevance scores, and chunk IDs\n- Use 'get_chunk' or 'get_content' tools to fetch actual content from search results\n- Chunks are sorted by relevance score within each file\n- Average similarity score calculated across all matching chunks per file\n\nExample queries:\n- \"error handling patterns and exception management strategies\" (finds try/catch, error classes, logging)\n- \"database migration scripts and schema versioning approaches\" (finds SQL, schema changes, migration files)\n- \"authentication middleware and JWT token validation logic\" (finds auth logic, JWT handling, middleware functions)`,\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Natural language search query describing what you are looking for. Can be concepts, functionality, or specific technical terms.'\n },\n limit: {\n type: 'number',\n description: 'Maximum number of files to return (default: 10). Each file may contain multiple matching chunks.',\n default: 10\n },\n workspace: {\n type: 'string',\n description: 'Optional workspace name to filter search results. Only files within the workspace directories will be searched. IMPORTANT: Use server_info tool first to discover available workspace names - using invalid workspace names will result in empty results.'\n }\n },\n required: ['query']\n }\n },\n {\n name: 'similar_files',\n description: `Find files with content similar to a reference file. Uses semantic similarity to find related documents, code files, or any text content.\n\nWhen to use this tool:\n- Find documentation similar to a specific guide or README\n- Locate related code files, configuration files, or scripts\n- Discover alternative implementations or approaches\n- Find files covering similar topics or concepts\n\nHow it works:\n- Analyzes the semantic content of the reference file\n- Compares against all indexed files using vector similarity\n- Returns files ranked by content similarity score\n\nExamples:\n- Given \"deployment-guide.md\" - finds other deployment docs, CI/CD guides, infrastructure setup\n- Given \"troubleshooting.md\" - finds other troubleshooting guides, FAQ files, error documentation\n- Given \"config.yaml\" - finds other configuration files, settings, environment setups\n- Given \"auth.py\" - finds other authentication modules, security code, middleware\n\nReturns file paths with similarity scores. Use get_content to read full files or get_chunk for specific sections.`,\n inputSchema: {\n type: 'object',\n properties: {\n file_path: {\n type: 'string',\n description: 'Absolute or relative path to the reference file. This file must have been previously indexed.'\n },\n limit: {\n type: 'number',\n description: 'Maximum number of similar files to return (default: 10). Results are sorted by similarity score.',\n default: 10\n },\n workspace: {\n type: 'string',\n description: 'Optional workspace name to filter results. Only files within the workspace directories will be considered. IMPORTANT: Use server_info tool first to discover available workspace names - using invalid workspace names will result in empty results.'\n }\n },\n required: ['file_path']\n }\n },\n {\n name: 'get_content',\n description: `Retrieve the full content of a file or specific chunks. Reads files directly from the filesystem.\n\nWhen to use this tool:\n- Get complete file content after finding files through search\n- Read documentation, code files, or configuration files for analysis\n- Extract specific sections of large files using chunk ranges\n- Access any text-based file content\n\nHow it works:\n- Reads files directly from filesystem (not from search index)\n- Returns entire file by default\n- Can return specific chunk ranges for indexed files\n- Preserves original formatting and content\n\nExamples:\n- Get full file: file_path=\"/home/user/docs/api.md\"\n- Get specific chunks: file_path=\"/home/user/code/main.py\", chunks=\"2-5\"\n- Get single chunk: file_path=\"/home/user/config.json\", chunks=\"1\"\n\nReturns file content as text. Use this after search or similar_files to read actual content.`,\n inputSchema: {\n type: 'object',\n properties: {\n file_path: {\n type: 'string',\n description: 'Absolute or relative path to the file to retrieve. File must be readable and text-based.'\n },\n chunks: {\n type: 'string',\n description: 'Optional chunk range specification. Examples: \"3\" (single chunk), \"2-5\" (chunks 2 through 5), \"1-3\" (first three chunks). Only works for indexed files.'\n }\n },\n required: ['file_path']\n }\n },\n {\n name: 'get_chunk',\n description: `Retrieve content of a specific chunk from an indexed file. Gets exact text segments identified during search.\n\nWhen to use this tool:\n- Get specific relevant sections after performing a search\n- Access only the most pertinent parts of large files\n- Retrieve content from high-scoring chunks identified in search results\n- Avoid reading entire files when only specific sections are needed\n\nHow it works:\n- Files are split into overlapping text chunks during indexing\n- Each chunk has a sequential ID (\"0\", \"1\", \"2\", etc.)\n- Search results include chunk IDs for relevant sections\n- Returns the exact content that was semantically matched\n\nExamples:\n- After search returns chunk \"3\" from \"api-docs.md\" with high score\n- Get chunk content: file_path=\"/docs/api-docs.md\", chunk_id=\"3\"\n- Returns the specific text segment that matched your query\n\nReturns chunk content as text. Use this with chunk IDs from search results to get precise content sections.`,\n inputSchema: {\n type: 'object',\n properties: {\n file_path: {\n type: 'string',\n description: 'Absolute or relative path to the indexed file containing the desired chunk.'\n },\n chunk_id: {\n type: 'string',\n description: 'ID of the specific chunk to retrieve. This is typically obtained from search results and is a sequential string like \"0\", \"1\", \"2\", etc.'\n }\n },\n required: ['file_path', 'chunk_id']\n }\n },\n {\n name: 'server_info',\n description: `Get information about server status and indexed content. Shows what directories and files are available for search.\n\nWhen to use this tool:\n- REQUIRED: Check available workspace names before using workspace parameter in search or similar_files tools\n- Check what content is already indexed before performing searches\n- Verify system is working properly\n- See indexing statistics and status\n- Understand scope of available searchable content\n\nHow it works:\n- Reports total indexed directories, files, and chunks\n- Shows database size and last indexing time\n- Lists all indexed directories with file counts\n- Lists all configured workspaces with their paths and file counts\n- Reports any errors or issues\n\nExamples:\n- Check workspaces before searching: \"What workspaces are available?\"\n- Check before searching: \"What content is indexed?\"\n- Verify after indexing: \"Did the indexing complete successfully?\"\n- Monitor system: \"How many files are searchable?\"\n\nReturns server version, indexing statistics, directory list, workspace information, and any errors. IMPORTANT: Always use this tool first to discover available workspace names when you need to search within specific workspaces.`,\n inputSchema: {\n type: 'object',\n properties: {},\n additionalProperties: false\n }\n }\n];\n\nexport async function startMcpServer(config: Config): Promise<void> {\n const server = new Server(\n {\n name: 'directory-indexer',\n version: VERSION\n },\n {\n capabilities: {\n tools: {}\n }\n }\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: MCP_TOOLS\n };\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n switch (name) {\n case 'index':\n return await handleIndexTool(args, config);\n \n case 'search':\n return await handleSearchTool(args);\n \n case 'similar_files':\n return await handleSimilarFilesTool(args);\n \n case 'get_content':\n return await handleGetContentTool(args);\n \n case 'get_chunk':\n return await handleGetChunkTool(args);\n \n case 'server_info':\n return await handleServerInfoTool(VERSION);\n \n default:\n throw new Error(`Unknown tool: ${name}`);\n }\n } catch (error) {\n return formatErrorResponse(error);\n }\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n \n if (config.verbose) {\n console.error('MCP server started successfully');\n }\n}","import { Config } from './config.js';\n\nexport class PrerequisiteError extends Error {\n constructor(message: string, public override cause?: Error) {\n super(message);\n this.name = 'PrerequisiteError';\n }\n}\n\n/**\n * Check if Qdrant is accessible\n */\nexport async function checkQdrant(config: Config): Promise<boolean> {\n try {\n const response = await fetch(`${config.storage.qdrantEndpoint}/healthz`);\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Check if Ollama is accessible\n */\nexport async function checkOllama(config: Config): Promise<boolean> {\n try {\n const response = await fetch(`${config.embedding.endpoint}/api/tags`);\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Check if Ollama model is available\n */\nexport async function checkOllamaModel(config: Config): Promise<boolean> {\n try {\n const response = await fetch(`${config.embedding.endpoint}/api/tags`);\n if (!response.ok) return false;\n \n const data = await response.json();\n const models = data.models || [];\n return models.some((model: { name: string }) => model.name.includes(config.embedding.model));\n } catch {\n return false;\n }\n}\n\n/**\n * Check if OpenAI is accessible\n */\nexport async function checkOpenAI(config: Config): Promise<boolean> {\n if (!process.env.OPENAI_API_KEY) return false;\n \n try {\n const response = await fetch('https://api.openai.com/v1/embeddings', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`\n },\n body: JSON.stringify({\n model: config.embedding.model,\n input: 'test'\n })\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Generate error message for failed prerequisites\n */\nfunction createErrorMessage(qdrantOk: boolean, embeddingOk: boolean, provider: string): string {\n const errors: string[] = [];\n \n if (!qdrantOk) {\n errors.push('Qdrant database is inaccessible');\n }\n \n if (!embeddingOk) {\n if (provider === 'ollama') {\n errors.push('Ollama embedding service is inaccessible or model unavailable');\n } else if (provider === 'openai') {\n errors.push('OpenAI API is inaccessible or key invalid');\n }\n }\n \n errors.push('');\n errors.push('For setup instructions, see: https://github.com/peteretelej/directory-indexer#setup');\n \n return errors.join('\\n');\n}\n\n/**\n * Validate all prerequisites for indexing (needs both Qdrant and embedding service)\n */\nexport async function validateIndexPrerequisites(config: Config): Promise<void> {\n const [qdrantOk, embeddingOk] = await Promise.all([\n checkQdrant(config),\n checkEmbeddingService(config)\n ]);\n \n if (!qdrantOk || !embeddingOk) {\n throw new PrerequisiteError(createErrorMessage(qdrantOk, embeddingOk, config.embedding.provider));\n }\n}\n\n/**\n * Validate prerequisites for search (needs only Qdrant)\n */\nexport async function validateSearchPrerequisites(config: Config): Promise<void> {\n const qdrantOk = await checkQdrant(config);\n \n if (!qdrantOk) {\n throw new PrerequisiteError(createErrorMessage(false, true, config.embedding.provider));\n }\n}\n\n/**\n * Check embedding service based on provider\n */\nasync function checkEmbeddingService(config: Config): Promise<boolean> {\n switch (config.embedding.provider) {\n case 'ollama':\n return (await checkOllama(config)) && (await checkOllamaModel(config));\n case 'openai':\n return await checkOpenAI(config);\n case 'mock':\n return true;\n default:\n return false;\n }\n}\n\n/**\n * Get status of all services (for status command)\n */\nexport async function getServiceStatus(config: Config): Promise<{\n qdrant: boolean;\n embedding: boolean;\n embeddingProvider: string;\n}> {\n const [qdrantOk, embeddingOk] = await Promise.all([\n checkQdrant(config),\n checkEmbeddingService(config)\n ]);\n \n return {\n qdrant: qdrantOk,\n embedding: embeddingOk,\n embeddingProvider: config.embedding.provider\n };\n}","#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport { fileURLToPath } from 'url';\nimport { readFileSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { indexDirectories } from './indexing.js';\nimport { searchContent, findSimilarFiles, getFileContent } from './search.js';\nimport { loadConfig } from './config.js';\nimport { getIndexStatus } from './storage.js';\nimport { startMcpServer } from './mcp.js';\nimport { validateIndexPrerequisites, validateSearchPrerequisites, getServiceStatus } from './prerequisites.js';\n\n// Read version from package.json\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst packageJsonPath = join(__dirname, '../package.json');\nconst packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\nconst VERSION = packageJson.version;\n\nexport async function main() {\n const program = new Command();\n \n program\n .name('directory-indexer')\n .description('AI-powered directory indexing with semantic search')\n .version(VERSION);\n\n program\n .command('index')\n .description('Index directories for semantic search')\n .argument('<paths...>', 'Directory paths to index')\n .option('-v, --verbose', 'Enable verbose logging')\n .action(async (paths: string[], options) => {\n try {\n const config = await loadConfig({ verbose: options.verbose });\n await validateIndexPrerequisites(config);\n console.log(`Indexing ${paths.length} ${paths.length === 1 ? 'directory' : 'directories'}: ${paths.join(', ')}`);\n const result = await indexDirectories(paths, config);\n console.log(`Indexed ${result.indexed} files, skipped ${result.skipped} files, ${result.failed} failed`);\n if (result.errors.length > 0) {\n console.log(`Errors: [`);\n result.errors.forEach(error => {\n console.log(` '${error}'`);\n });\n console.log(`]`);\n }\n } catch (error) {\n console.error('Error indexing directories:', error);\n process.exit(1);\n }\n });\n\n program\n .command('search')\n .description('Search indexed content semantically')\n .argument('<query>', 'Search query')\n .option('-l, --limit <number>', 'Maximum number of results', '10')\n .option('-c, --show-chunks', 'Show individual chunk scores and IDs')\n .option('-v, --verbose', 'Enable verbose logging')\n .action(async (query: string, options) => {\n try {\n const config = await loadConfig({ verbose: options.verbose });\n await validateSearchPrerequisites(config);\n const results = await searchContent(query, { limit: parseInt(options.limit) });\n \n if (results.length === 0) {\n console.log('No results found');\n return;\n }\n\n console.log(`Found ${results.length} results:\\n`);\n results.forEach((result, index) => {\n console.log(`${index + 1}. ${result.filePath}`);\n console.log(` Score: ${result.score.toFixed(3)} (${result.matchingChunks} chunks)`);\n \n if (options.showChunks && result.chunks.length > 0) {\n console.log(` Chunks:`);\n result.chunks.forEach(chunk => {\n console.log(` - Chunk ${chunk.chunkId}: ${chunk.score.toFixed(3)}`);\n });\n }\n \n console.log();\n });\n } catch (error) {\n console.error('Error searching content:', error);\n process.exit(1);\n }\n });\n\n program\n .command('similar')\n .description('Find files similar to a given file')\n .argument('<file>', 'File path to find similar files for')\n .option('-l, --limit <number>', 'Maximum number of results', '10')\n .option('-v, --verbose', 'Enable verbose logging')\n .action(async (filePath: string, options) => {\n try {\n const config = await loadConfig({ verbose: options.verbose });\n await validateSearchPrerequisites(config);\n const results = await findSimilarFiles(filePath, parseInt(options.limit));\n \n if (results.length === 0) {\n console.log('No similar files found');\n return;\n }\n\n console.log(`Found ${results.length} similar files:\\n`);\n results.forEach((result, index) => {\n console.log(`${index + 1}. ${result.filePath}`);\n console.log(` Similarity: ${result.score.toFixed(3)}`);\n console.log();\n });\n } catch (error) {\n console.error('Error finding similar files:', error);\n process.exit(1);\n }\n });\n\n program\n .command('get')\n .description('Get file content')\n .argument('<file>', 'File path to retrieve')\n .option('-c, --chunks <range>', 'Chunk range (e.g., \"2-5\")')\n .option('-v, --verbose', 'Enable verbose logging')\n .action(async (filePath: string, options) => {\n try {\n await loadConfig({ verbose: options.verbose });\n const content = await getFileContent(filePath, options.chunks);\n console.log(content);\n } catch (error) {\n console.error('Error getting file content:', error);\n process.exit(1);\n }\n });\n\n program\n .command('serve')\n .description('Start MCP server')\n .option('-v, --verbose', 'Enable verbose logging')\n .action(async (options) => {\n try {\n const config = await loadConfig({ verbose: options.verbose });\n await startMcpServer(config);\n } catch (error) {\n console.error('Error starting MCP server:', error);\n process.exit(1);\n }\n });\n\n program\n .command('status')\n .description('Show indexing status')\n .option('-v, --verbose', 'Enable verbose logging')\n .action(async (options) => {\n try {\n const config = await loadConfig({ verbose: options.verbose });\n const [status, serviceStatus] = await Promise.all([\n getIndexStatus(),\n getServiceStatus(config)\n ]);\n \n console.log('Directory Indexer Status Report');\n console.log('=====================================');\n console.log('');\n console.log('SERVICE STATUS:');\n console.log(` • Qdrant database: ${serviceStatus.qdrant ? 'Connected' : 'Disconnected'}`);\n console.log(` • Embedding service (${serviceStatus.embeddingProvider}): ${serviceStatus.embedding ? 'Connected' : 'Disconnected'}`);\n console.log('');\n console.log('OVERVIEW:');\n console.log(` • ${status.directoriesIndexed} directories have been indexed`);\n console.log(` • ${status.filesIndexed} files processed for semantic search`);\n console.log(` • ${status.chunksIndexed} text chunks available for AI search`);\n console.log(` • Database storage: ${status.databaseSize}`);\n console.log(` • Most recent indexing: ${status.lastIndexed || 'No indexing performed yet'}`);\n \n if (status.errors.length > 0) {\n console.log(` • Processing errors encountered: ${status.errors.length}`);\n if (options.verbose) {\n console.log('');\n console.log('RECENT ERRORS:');\n status.errors.slice(0, 5).forEach(error => {\n console.log(` - ${error}`);\n });\n }\n }\n \n console.log('');\n console.log('INDEXED DIRECTORIES:');\n if (status.directories.length === 0) {\n console.log(' No directories have been indexed yet.');\n console.log(' Run \"directory-indexer index <path>\" to start indexing.');\n } else {\n status.directories.forEach(dir => {\n console.log('');\n console.log(` Directory: ${dir.path}`);\n console.log(` • Indexing status: ${dir.status}`);\n console.log(` • Files processed: ${dir.filesCount}`);\n console.log(` • Searchable chunks: ${dir.chunksCount}`);\n console.log(` • Last indexed: ${dir.lastIndexed || 'Never completed'}`);\n if (dir.errors.length > 0) {\n console.log(` • Files with errors: ${dir.errors.length}`);\n if (options.verbose) {\n console.log(' • Recent errors:');\n dir.errors.slice(0, 3).forEach(error => {\n console.log(` - ${error}`);\n });\n }\n }\n });\n }\n \n if (!status.qdrantConsistency.isConsistent) {\n console.log('');\n console.log('SYSTEM STATUS:');\n status.qdrantConsistency.issues.forEach(issue => {\n console.log(` • ${issue}`);\n });\n console.log('');\n console.log('Note: Status messages above may be normal during setup or active indexing.');\n } else {\n console.log('');\n console.log('SYSTEM STATUS:');\n console.log(' • All systems operational - ready for AI-powered search');\n }\n } catch (error) {\n console.error('Error getting status:', error);\n process.exit(1);\n }\n });\n\n await program.parseAsync();\n}\n\n// Main function is already exported above"],"names":["__dirname","packageJsonPath","packageJson","VERSION"],"mappings":"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"}
|