directory-indexer 0.3.0 → 1.1.0
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 +4 -1
- package/dist/cli-handlers.js +5 -2
- package/dist/cli-handlers.js.map +1 -1
- package/dist/cli.js +9 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.js +13 -5
- package/dist/config.js.map +1 -1
- package/dist/embedding.js +2 -2
- package/dist/embedding.js.map +1 -1
- package/dist/indexing.js +53 -27
- package/dist/indexing.js.map +1 -1
- package/dist/mcp-handlers.js +91 -101
- package/dist/mcp-handlers.js.map +1 -1
- package/dist/mcp.js +3 -3
- package/dist/mcp.js.map +1 -1
- package/dist/path-validation.js +2 -1
- package/dist/path-validation.js.map +1 -1
- package/dist/prerequisites.js +5 -8
- package/dist/prerequisites.js.map +1 -1
- package/dist/reset.js +21 -31
- package/dist/reset.js.map +1 -1
- package/dist/search.js +14 -3
- package/dist/search.js.map +1 -1
- package/dist/storage.js +151 -70
- package/dist/storage.js.map +1 -1
- package/dist/utils.js +10 -1
- package/dist/utils.js.map +1 -1
- package/package.json +16 -22
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Self-hosted semantic search for local files. Give your AI assistant access to se
|
|
|
14
14
|
**Prerequisites:**
|
|
15
15
|
|
|
16
16
|
- **[Docker](https://docs.docker.com/get-docker/)** - For running Qdrant and Ollama _(skip if you already have them running natively)_
|
|
17
|
-
- **[Node.js 18+](https://nodejs.org/en/download/)** - Required for running directory-indexer
|
|
17
|
+
- **[Node.js 18+](https://nodejs.org/en/download/)** - Required for running directory-indexer (the v1.x line targets Node 18 LTS compatibility)
|
|
18
18
|
|
|
19
19
|
_Note: For native Qdrant and Ollama installation without Docker, see [Setup section](#setup)._
|
|
20
20
|
|
|
@@ -274,6 +274,9 @@ export DIRECTORY_INDEXER_DATA_DIR="/opt/ai-knowledge-base"
|
|
|
274
274
|
export QDRANT_ENDPOINT="http://localhost:6333"
|
|
275
275
|
export OLLAMA_ENDPOINT="http://localhost:11434"
|
|
276
276
|
|
|
277
|
+
# OpenAI provider: custom OpenAI-compatible endpoint (default: https://api.openai.com/v1)
|
|
278
|
+
export OPENAI_ENDPOINT="https://api.openai.com/v1"
|
|
279
|
+
|
|
277
280
|
# Optional API keys
|
|
278
281
|
export OPENAI_API_KEY="your-key-here"
|
|
279
282
|
export QDRANT_API_KEY="your-key-here"
|
package/dist/cli-handlers.js
CHANGED
|
@@ -9,13 +9,16 @@ async function handleIndex(paths, options) {
|
|
|
9
9
|
const config = await loadConfig({ verbose: options.verbose });
|
|
10
10
|
await validateIndexPrerequisites(config);
|
|
11
11
|
console.log(`Indexing ${paths.length} ${paths.length === 1 ? "directory" : "directories"}: ${paths.join(", ")}`);
|
|
12
|
-
if (!
|
|
12
|
+
if (!config.verbose) {
|
|
13
13
|
console.log("Run with --verbose for detailed per-file indexing reports");
|
|
14
14
|
console.log("Indexing can be safely stopped and resumed - progress is automatically saved");
|
|
15
15
|
console.log("You can start using the MCP server while indexing continues");
|
|
16
16
|
console.log("Indexing may take time due to embedding generation - see project README for performance tips");
|
|
17
17
|
}
|
|
18
|
-
const result = await indexDirectories(paths, config
|
|
18
|
+
const result = await indexDirectories(paths, config, {
|
|
19
|
+
progress: (m) => console.log(m),
|
|
20
|
+
error: (m) => console.error(m)
|
|
21
|
+
});
|
|
19
22
|
console.log(`Indexed ${result.indexed} files, skipped ${result.skipped} files, cleaned up ${result.deleted} deleted files, ${result.failed} failed`);
|
|
20
23
|
if (result.errors.length > 0) {
|
|
21
24
|
console.log(`Errors: [`);
|
package/dist/cli-handlers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-handlers.js","sources":["../src/cli-handlers.ts"],"sourcesContent":["import { 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';\nimport { resetEnvironment } from './reset.js';\n\nexport interface IndexOptions {\n verbose?: boolean;\n}\n\nexport interface SearchOptions {\n limit?: number;\n showChunks?: boolean;\n verbose?: boolean;\n}\n\nexport interface SimilarOptions {\n limit?: number;\n verbose?: boolean;\n}\n\nexport interface GetOptions {\n chunks?: string;\n verbose?: boolean;\n}\n\nexport interface ServeOptions {\n verbose?: boolean;\n}\n\nexport interface ResetOptions {\n force?: boolean;\n verbose?: boolean;\n}\n\nexport interface StatusOptions {\n verbose?: boolean;\n}\n\nexport async function handleIndex(paths: string[], options: IndexOptions): Promise<void> {\n const config = await loadConfig({ verbose: options.verbose });\n await validateIndexPrerequisites(config);\n \n console.log(`Indexing ${paths.length} ${paths.length === 1 ? 'directory' : 'directories'}: ${paths.join(', ')}`);\n if (!options.verbose) {\n console.log('Run with --verbose for detailed per-file indexing reports');\n console.log('Indexing can be safely stopped and resumed - progress is automatically saved');\n console.log('You can start using the MCP server while indexing continues');\n console.log('Indexing may take time due to embedding generation - see project README for performance tips');\n }\n \n const result = await indexDirectories(paths, config);\n console.log(`Indexed ${result.indexed} files, skipped ${result.skipped} files, cleaned up ${result.deleted} deleted files, ${result.failed} failed`);\n \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}\n\nexport async function handleSearch(query: string, options: SearchOptions): Promise<void> {\n const config = await loadConfig({ verbose: options.verbose });\n await validateSearchPrerequisites(config);\n \n const results = await searchContent(query, { limit: options.limit || 10 });\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}\n\nexport async function handleSimilar(filePath: string, options: SimilarOptions): Promise<void> {\n const config = await loadConfig({ verbose: options.verbose });\n await validateSearchPrerequisites(config);\n \n const results = await findSimilarFiles(filePath, options.limit || 10);\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}\n\nexport async function handleGet(filePath: string, options: GetOptions): Promise<void> {\n await loadConfig({ verbose: options.verbose });\n const content = await getFileContent(filePath, options.chunks);\n console.log(content);\n}\n\nexport async function handleServe(options: ServeOptions): Promise<void> {\n const config = await loadConfig({ verbose: options.verbose });\n await startMcpServer(config);\n}\n\nexport async function handleReset(options: ResetOptions): Promise<void> {\n const config = await loadConfig({ verbose: options.verbose });\n await resetEnvironment(config, { \n force: options.force, \n verbose: options.verbose \n });\n}\n\nexport async function handleStatus(options: StatusOptions): Promise<void> {\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.workspaces.length > 0) {\n console.log('');\n console.log('WORKSPACES:');\n console.log(` • ${status.workspaceHealth.healthy} healthy, ${status.workspaceHealth.warnings} warnings, ${status.workspaceHealth.errors} errors`);\n \n if (status.workspaceHealth.errors > 0) {\n console.log('');\n console.log('WORKSPACE ERRORS:');\n status.workspaceHealth.criticalIssues.forEach(issue => {\n console.log(` ❌ ${issue}`);\n });\n }\n \n if (status.workspaceHealth.recommendations.length > 0) {\n console.log('');\n console.log('WORKSPACE RECOMMENDATIONS:');\n status.workspaceHealth.recommendations.forEach(rec => {\n console.log(` 💡 ${rec}`);\n });\n }\n \n if (options.verbose) {\n console.log('');\n console.log('WORKSPACE DETAILS:');\n status.workspaces.forEach(workspace => {\n console.log('');\n console.log(` Workspace: ${workspace.name}`);\n console.log(` • Status: ${workspace.health.status}`);\n console.log(` • Paths: ${workspace.paths.join(', ')}`);\n console.log(` • Files: ${workspace.filesCount}, Chunks: ${workspace.chunksCount}`);\n if (workspace.health.issues.length > 0) {\n console.log(` • Issues: ${workspace.health.issues.join('; ')}`);\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}"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli-handlers.js","sources":["../src/cli-handlers.ts"],"sourcesContent":["import { 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';\nimport { resetEnvironment } from './reset.js';\n\nexport interface IndexOptions {\n verbose?: boolean;\n}\n\nexport interface SearchOptions {\n limit?: number;\n showChunks?: boolean;\n verbose?: boolean;\n}\n\nexport interface SimilarOptions {\n limit?: number;\n verbose?: boolean;\n}\n\nexport interface GetOptions {\n chunks?: string;\n verbose?: boolean;\n}\n\nexport interface ServeOptions {\n verbose?: boolean;\n}\n\nexport interface ResetOptions {\n force?: boolean;\n verbose?: boolean;\n}\n\nexport interface StatusOptions {\n verbose?: boolean;\n}\n\nexport async function handleIndex(paths: string[], options: IndexOptions): Promise<void> {\n const config = await loadConfig({ verbose: options.verbose });\n await validateIndexPrerequisites(config);\n \n console.log(`Indexing ${paths.length} ${paths.length === 1 ? 'directory' : 'directories'}: ${paths.join(', ')}`);\n if (!config.verbose) {\n console.log('Run with --verbose for detailed per-file indexing reports');\n console.log('Indexing can be safely stopped and resumed - progress is automatically saved');\n console.log('You can start using the MCP server while indexing continues');\n console.log('Indexing may take time due to embedding generation - see project README for performance tips');\n }\n \n const result = await indexDirectories(paths, config, {\n progress: m => console.log(m),\n error: m => console.error(m)\n });\n console.log(`Indexed ${result.indexed} files, skipped ${result.skipped} files, cleaned up ${result.deleted} deleted files, ${result.failed} failed`);\n \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}\n\nexport async function handleSearch(query: string, options: SearchOptions): Promise<void> {\n const config = await loadConfig({ verbose: options.verbose });\n await validateSearchPrerequisites(config);\n \n const results = await searchContent(query, { limit: options.limit || 10 });\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}\n\nexport async function handleSimilar(filePath: string, options: SimilarOptions): Promise<void> {\n const config = await loadConfig({ verbose: options.verbose });\n await validateSearchPrerequisites(config);\n \n const results = await findSimilarFiles(filePath, options.limit || 10);\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}\n\nexport async function handleGet(filePath: string, options: GetOptions): Promise<void> {\n await loadConfig({ verbose: options.verbose });\n const content = await getFileContent(filePath, options.chunks);\n console.log(content);\n}\n\nexport async function handleServe(options: ServeOptions): Promise<void> {\n const config = await loadConfig({ verbose: options.verbose });\n await startMcpServer(config);\n}\n\nexport async function handleReset(options: ResetOptions): Promise<void> {\n const config = await loadConfig({ verbose: options.verbose });\n await resetEnvironment(config, { \n force: options.force, \n verbose: options.verbose \n });\n}\n\nexport async function handleStatus(options: StatusOptions): Promise<void> {\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.workspaces.length > 0) {\n console.log('');\n console.log('WORKSPACES:');\n console.log(` • ${status.workspaceHealth.healthy} healthy, ${status.workspaceHealth.warnings} warnings, ${status.workspaceHealth.errors} errors`);\n \n if (status.workspaceHealth.errors > 0) {\n console.log('');\n console.log('WORKSPACE ERRORS:');\n status.workspaceHealth.criticalIssues.forEach(issue => {\n console.log(` ❌ ${issue}`);\n });\n }\n \n if (status.workspaceHealth.recommendations.length > 0) {\n console.log('');\n console.log('WORKSPACE RECOMMENDATIONS:');\n status.workspaceHealth.recommendations.forEach(rec => {\n console.log(` 💡 ${rec}`);\n });\n }\n \n if (options.verbose) {\n console.log('');\n console.log('WORKSPACE DETAILS:');\n status.workspaces.forEach(workspace => {\n console.log('');\n console.log(` Workspace: ${workspace.name}`);\n console.log(` • Status: ${workspace.health.status}`);\n console.log(` • Paths: ${workspace.paths.join(', ')}`);\n console.log(` • Files: ${workspace.filesCount}, Chunks: ${workspace.chunksCount}`);\n if (workspace.health.issues.length > 0) {\n console.log(` • Issues: ${workspace.health.issues.join('; ')}`);\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}"],"names":[],"mappings":";;;;;;;AAyCA,eAAsB,YAAY,OAAiB,SAAsC;AACvF,QAAM,SAAS,MAAM,WAAW,EAAE,SAAS,QAAQ,SAAS;AAC5D,QAAM,2BAA2B,MAAM;AAEvC,UAAQ,IAAI,YAAY,MAAM,MAAM,IAAI,MAAM,WAAW,IAAI,cAAc,aAAa,KAAK,MAAM,KAAK,IAAI,CAAC,EAAE;AAC/G,MAAI,CAAC,OAAO,SAAS;AACnB,YAAQ,IAAI,2DAA2D;AACvE,YAAQ,IAAI,8EAA8E;AAC1F,YAAQ,IAAI,6DAA6D;AACzE,YAAQ,IAAI,8FAA8F;AAAA,EAC5G;AAEA,QAAM,SAAS,MAAM,iBAAiB,OAAO,QAAQ;AAAA,IACnD,UAAU,CAAA,MAAK,QAAQ,IAAI,CAAC;AAAA,IAC5B,OAAO,CAAA,MAAK,QAAQ,MAAM,CAAC;AAAA,EAAA,CAC5B;AACD,UAAQ,IAAI,WAAW,OAAO,OAAO,mBAAmB,OAAO,OAAO,sBAAsB,OAAO,OAAO,mBAAmB,OAAO,MAAM,SAAS;AAEnJ,MAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,YAAQ,IAAI,WAAW;AACvB,WAAO,OAAO,QAAQ,CAAA,UAAS;AAC7B,cAAQ,IAAI,MAAM,KAAK,GAAG;AAAA,IAC5B,CAAC;AACD,YAAQ,IAAI,GAAG;AAAA,EACjB;AACF;AAEA,eAAsB,aAAa,OAAe,SAAuC;AACvF,QAAM,SAAS,MAAM,WAAW,EAAE,SAAS,QAAQ,SAAS;AAC5D,QAAM,4BAA4B,MAAM;AAExC,QAAM,UAAU,MAAM,cAAc,OAAO,EAAE,OAAO,QAAQ,SAAS,IAAI;AAEzE,MAAI,QAAQ,WAAW,GAAG;AACxB,YAAQ,IAAI,kBAAkB;AAC9B;AAAA,EACF;AAEA,UAAQ,IAAI,SAAS,QAAQ,MAAM;AAAA,CAAa;AAChD,UAAQ,QAAQ,CAAC,QAAQ,UAAU;AACjC,YAAQ,IAAI,GAAG,QAAQ,CAAC,KAAK,OAAO,QAAQ,EAAE;AAC9C,YAAQ,IAAI,aAAa,OAAO,MAAM,QAAQ,CAAC,CAAC,KAAK,OAAO,cAAc,UAAU;AAEpF,QAAI,QAAQ,cAAc,OAAO,OAAO,SAAS,GAAG;AAClD,cAAQ,IAAI,YAAY;AACxB,aAAO,OAAO,QAAQ,CAAA,UAAS;AAC7B,gBAAQ,IAAI,gBAAgB,MAAM,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE;AAAA,MACxE,CAAC;AAAA,IACH;AAEA,YAAQ,IAAA;AAAA,EACV,CAAC;AACH;AAEA,eAAsB,cAAc,UAAkB,SAAwC;AAC5F,QAAM,SAAS,MAAM,WAAW,EAAE,SAAS,QAAQ,SAAS;AAC5D,QAAM,4BAA4B,MAAM;AAExC,QAAM,UAAU,MAAM,iBAAiB,UAAU,QAAQ,SAAS,EAAE;AAEpE,MAAI,QAAQ,WAAW,GAAG;AACxB,YAAQ,IAAI,wBAAwB;AACpC;AAAA,EACF;AAEA,UAAQ,IAAI,SAAS,QAAQ,MAAM;AAAA,CAAmB;AACtD,UAAQ,QAAQ,CAAC,QAAQ,UAAU;AACjC,YAAQ,IAAI,GAAG,QAAQ,CAAC,KAAK,OAAO,QAAQ,EAAE;AAC9C,YAAQ,IAAI,kBAAkB,OAAO,MAAM,QAAQ,CAAC,CAAC,EAAE;AACvD,YAAQ,IAAA;AAAA,EACV,CAAC;AACH;AAEA,eAAsB,UAAU,UAAkB,SAAoC;AACpF,QAAM,WAAW,EAAE,SAAS,QAAQ,SAAS;AAC7C,QAAM,UAAU,MAAM,eAAe,UAAU,QAAQ,MAAM;AAC7D,UAAQ,IAAI,OAAO;AACrB;AAEA,eAAsB,YAAY,SAAsC;AACtE,QAAM,SAAS,MAAM,WAAW,EAAE,SAAS,QAAQ,SAAS;AAC5D,QAAM,eAAe,MAAM;AAC7B;AAEA,eAAsB,YAAY,SAAsC;AACtE,QAAM,SAAS,MAAM,WAAW,EAAE,SAAS,QAAQ,SAAS;AAC5D,QAAM,iBAAiB,QAAQ;AAAA,IAC7B,OAAO,QAAQ;AAAA,IACf,SAAS,QAAQ;AAAA,EAAA,CAClB;AACH;AAEA,eAAsB,aAAa,SAAuC;AACxE,QAAM,SAAS,MAAM,WAAW,EAAE,SAAS,QAAQ,SAAS;AAC5D,QAAM,CAAC,QAAQ,aAAa,IAAI,MAAM,QAAQ,IAAI;AAAA,IAChD,eAAA;AAAA,IACA,iBAAiB,MAAM;AAAA,EAAA,CACxB;AAED,UAAQ,IAAI,iCAAiC;AAC7C,UAAQ,IAAI,uCAAuC;AACnD,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,iBAAiB;AAC7B,UAAQ,IAAI,wBAAwB,cAAc,SAAS,cAAc,cAAc,EAAE;AACzF,UAAQ,IAAI,0BAA0B,cAAc,iBAAiB,MAAM,cAAc,YAAY,cAAc,cAAc,EAAE;AACnI,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,WAAW;AACvB,UAAQ,IAAI,OAAO,OAAO,kBAAkB,gCAAgC;AAC5E,UAAQ,IAAI,OAAO,OAAO,YAAY,sCAAsC;AAC5E,UAAQ,IAAI,OAAO,OAAO,aAAa,sCAAsC;AAC7E,UAAQ,IAAI,yBAAyB,OAAO,YAAY,EAAE;AAC1D,UAAQ,IAAI,6BAA6B,OAAO,eAAe,2BAA2B,EAAE;AAE5F,MAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,YAAQ,IAAI,sCAAsC,OAAO,OAAO,MAAM,EAAE;AACxE,QAAI,QAAQ,SAAS;AACnB,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,gBAAgB;AAC5B,aAAO,OAAO,MAAM,GAAG,CAAC,EAAE,QAAQ,CAAA,UAAS;AACzC,gBAAQ,IAAI,OAAO,KAAK,EAAE;AAAA,MAC5B,CAAC;AAAA,IACH;AAAA,EACF;AAEA,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,sBAAsB;AAClC,MAAI,OAAO,YAAY,WAAW,GAAG;AACnC,YAAQ,IAAI,yCAAyC;AACrD,YAAQ,IAAI,2DAA2D;AAAA,EACzE,OAAO;AACL,WAAO,YAAY,QAAQ,CAAA,QAAO;AAChC,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,gBAAgB,IAAI,IAAI,EAAE;AACtC,cAAQ,IAAI,0BAA0B,IAAI,MAAM,EAAE;AAClD,cAAQ,IAAI,0BAA0B,IAAI,UAAU,EAAE;AACtD,cAAQ,IAAI,4BAA4B,IAAI,WAAW,EAAE;AACzD,cAAQ,IAAI,uBAAuB,IAAI,eAAe,iBAAiB,EAAE;AACzE,UAAI,IAAI,OAAO,SAAS,GAAG;AACzB,gBAAQ,IAAI,4BAA4B,IAAI,OAAO,MAAM,EAAE;AAC3D,YAAI,QAAQ,SAAS;AACnB,kBAAQ,IAAI,sBAAsB;AAClC,cAAI,OAAO,MAAM,GAAG,CAAC,EAAE,QAAQ,CAAA,UAAS;AACtC,oBAAQ,IAAI,WAAW,KAAK,EAAE;AAAA,UAChC,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,OAAO,WAAW,SAAS,GAAG;AAChC,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,aAAa;AACzB,YAAQ,IAAI,OAAO,OAAO,gBAAgB,OAAO,aAAa,OAAO,gBAAgB,QAAQ,cAAc,OAAO,gBAAgB,MAAM,SAAS;AAEjJ,QAAI,OAAO,gBAAgB,SAAS,GAAG;AACrC,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,mBAAmB;AAC/B,aAAO,gBAAgB,eAAe,QAAQ,CAAA,UAAS;AACrD,gBAAQ,IAAI,OAAO,KAAK,EAAE;AAAA,MAC5B,CAAC;AAAA,IACH;AAEA,QAAI,OAAO,gBAAgB,gBAAgB,SAAS,GAAG;AACrD,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,4BAA4B;AACxC,aAAO,gBAAgB,gBAAgB,QAAQ,CAAA,QAAO;AACpD,gBAAQ,IAAI,QAAQ,GAAG,EAAE;AAAA,MAC3B,CAAC;AAAA,IACH;AAEA,QAAI,QAAQ,SAAS;AACnB,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,oBAAoB;AAChC,aAAO,WAAW,QAAQ,CAAA,cAAa;AACrC,gBAAQ,IAAI,EAAE;AACd,gBAAQ,IAAI,gBAAgB,UAAU,IAAI,EAAE;AAC5C,gBAAQ,IAAI,iBAAiB,UAAU,OAAO,MAAM,EAAE;AACtD,gBAAQ,IAAI,gBAAgB,UAAU,MAAM,KAAK,IAAI,CAAC,EAAE;AACxD,gBAAQ,IAAI,gBAAgB,UAAU,UAAU,aAAa,UAAU,WAAW,EAAE;AACpF,YAAI,UAAU,OAAO,OAAO,SAAS,GAAG;AACtC,kBAAQ,IAAI,iBAAiB,UAAU,OAAO,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,QACnE;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,kBAAkB,cAAc;AAC1C,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,gBAAgB;AAC5B,WAAO,kBAAkB,OAAO,QAAQ,CAAA,UAAS;AAC/C,cAAQ,IAAI,OAAO,KAAK,EAAE;AAAA,IAC5B,CAAC;AACD,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,4EAA4E;AAAA,EAC1F,OAAO;AACL,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,gBAAgB;AAC5B,YAAQ,IAAI,2DAA2D;AAAA,EACzE;AACF;"}
|
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,13 @@ const __dirname$1 = dirname(fileURLToPath(import.meta.url));
|
|
|
8
8
|
const packageJsonPath = join(__dirname$1, "../package.json");
|
|
9
9
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
10
10
|
const VERSION = packageJson.version;
|
|
11
|
+
function parseLimit(value) {
|
|
12
|
+
const limit = Number.parseInt(value);
|
|
13
|
+
if (Number.isNaN(limit) || limit < 1) {
|
|
14
|
+
throw new Error("Invalid --limit: must be a positive integer");
|
|
15
|
+
}
|
|
16
|
+
return limit;
|
|
17
|
+
}
|
|
11
18
|
async function main() {
|
|
12
19
|
const program = new Command();
|
|
13
20
|
program.name("directory-indexer").description("AI-powered directory indexing with semantic search").version(VERSION);
|
|
@@ -22,7 +29,7 @@ async function main() {
|
|
|
22
29
|
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) => {
|
|
23
30
|
try {
|
|
24
31
|
await handleSearch(query, {
|
|
25
|
-
limit:
|
|
32
|
+
limit: parseLimit(options.limit),
|
|
26
33
|
showChunks: options.showChunks,
|
|
27
34
|
verbose: options.verbose
|
|
28
35
|
});
|
|
@@ -34,7 +41,7 @@ async function main() {
|
|
|
34
41
|
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) => {
|
|
35
42
|
try {
|
|
36
43
|
await handleSimilar(filePath, {
|
|
37
|
-
limit:
|
|
44
|
+
limit: parseLimit(options.limit),
|
|
38
45
|
verbose: options.verbose
|
|
39
46
|
});
|
|
40
47
|
} catch (error) {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport { fileURLToPath } from 'url';\nimport { readFileSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { \n handleIndex, \n handleSearch, \n handleSimilar, \n handleGet, \n handleServe, \n handleReset, \n handleStatus \n} from './cli-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\nexport async function main() {\n const program = new Command();\n
|
|
1
|
+
{"version":3,"file":"cli.js","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport { fileURLToPath } from 'url';\nimport { readFileSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { \n handleIndex, \n handleSearch, \n handleSimilar, \n handleGet, \n handleServe, \n handleReset, \n handleStatus \n} from './cli-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\nfunction parseLimit(value: string): number {\n const limit = Number.parseInt(value);\n if (Number.isNaN(limit) || limit < 1) {\n throw new Error('Invalid --limit: must be a positive integer');\n }\n return limit;\n}\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 await handleIndex(paths, options);\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 handleSearch(query, {\n limit: parseLimit(options.limit),\n showChunks: options.showChunks,\n verbose: options.verbose\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 handleSimilar(filePath, {\n limit: parseLimit(options.limit),\n verbose: options.verbose\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 handleGet(filePath, options);\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 await handleServe(options);\n } catch (error) {\n console.error('Error starting MCP server:', error);\n process.exit(1);\n }\n });\n\n program\n .command('reset')\n .description('Reset directory-indexer data (database and vector collection)')\n .option('--force', 'Skip confirmation prompt')\n .option('-v, --verbose', 'Enable verbose logging')\n .action(async (options) => {\n try {\n await handleReset(options);\n } catch (error) {\n if (error instanceof Error && error.message === 'Reset cancelled by user') {\n console.log('\\nReset cancelled.');\n process.exit(0);\n }\n console.error('Error during reset:', 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 handleStatus(options);\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"],"mappings":"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"}
|
package/dist/config.js
CHANGED
|
@@ -12,14 +12,14 @@ const WorkspaceSchema = z.object({
|
|
|
12
12
|
const ConfigSchema = z.object({
|
|
13
13
|
storage: z.object({
|
|
14
14
|
sqlitePath: z.string(),
|
|
15
|
-
qdrantEndpoint: z.
|
|
15
|
+
qdrantEndpoint: z.url(),
|
|
16
16
|
qdrantCollection: z.string(),
|
|
17
17
|
qdrantApiKey: z.string().optional()
|
|
18
18
|
}),
|
|
19
19
|
embedding: z.object({
|
|
20
20
|
provider: z.enum(["ollama", "openai", "mock"]),
|
|
21
21
|
model: z.string(),
|
|
22
|
-
endpoint: z.
|
|
22
|
+
endpoint: z.url()
|
|
23
23
|
}),
|
|
24
24
|
indexing: z.object({
|
|
25
25
|
chunkSize: z.number().positive(),
|
|
@@ -27,10 +27,18 @@ const ConfigSchema = z.object({
|
|
|
27
27
|
maxFileSize: z.number().positive(),
|
|
28
28
|
ignorePatterns: z.array(z.string()),
|
|
29
29
|
respectGitignore: z.boolean()
|
|
30
|
+
}).superRefine((indexing, ctx) => {
|
|
31
|
+
if (indexing.chunkOverlap >= indexing.chunkSize) {
|
|
32
|
+
ctx.addIssue({
|
|
33
|
+
code: "custom",
|
|
34
|
+
path: ["chunkOverlap"],
|
|
35
|
+
message: "chunkOverlap must be smaller than chunkSize"
|
|
36
|
+
});
|
|
37
|
+
}
|
|
30
38
|
}),
|
|
31
39
|
dataDir: z.string(),
|
|
32
40
|
verbose: z.boolean(),
|
|
33
|
-
workspaces: z.record(WorkspaceSchema)
|
|
41
|
+
workspaces: z.record(z.string(), WorkspaceSchema)
|
|
34
42
|
});
|
|
35
43
|
class ConfigError extends Error {
|
|
36
44
|
constructor(message, cause) {
|
|
@@ -92,7 +100,7 @@ function loadConfig(options = {}) {
|
|
|
92
100
|
embedding: {
|
|
93
101
|
provider: process.env.EMBEDDING_PROVIDER || "ollama",
|
|
94
102
|
model: process.env.EMBEDDING_MODEL || "nomic-embed-text",
|
|
95
|
-
endpoint: process.env.OLLAMA_ENDPOINT || "http://127.0.0.1:11434"
|
|
103
|
+
endpoint: process.env.EMBEDDING_PROVIDER === "openai" ? process.env.OPENAI_ENDPOINT || "https://api.openai.com/v1" : process.env.OLLAMA_ENDPOINT || "http://127.0.0.1:11434"
|
|
96
104
|
},
|
|
97
105
|
indexing: {
|
|
98
106
|
chunkSize: parseInt(process.env.CHUNK_SIZE || "512"),
|
|
@@ -109,7 +117,7 @@ function loadConfig(options = {}) {
|
|
|
109
117
|
return ConfigSchema.parse(config);
|
|
110
118
|
} catch (error) {
|
|
111
119
|
if (error instanceof z.ZodError) {
|
|
112
|
-
const messages = error.
|
|
120
|
+
const messages = error.issues.map((e) => `${e.path.join(".")}: ${e.message}`);
|
|
113
121
|
throw new ConfigError(`Configuration validation failed: ${messages.join(", ")}`, error);
|
|
114
122
|
}
|
|
115
123
|
throw new ConfigError("Failed to load configuration", error);
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sources":["../src/config.ts"],"sourcesContent":["import { homedir } from 'os';\nimport { join } from 'path';\nimport { existsSync, statSync } from 'fs';\nimport { z } from 'zod';\nimport { normalizePath } from './utils';\n\nconst WorkspaceSchema = z.object({\n paths: z.array(z.string()),\n isValid: z.boolean(),\n filesCount: z.number().optional(),\n chunksCount: z.number().optional(),\n});\n\nconst ConfigSchema = z.object({\n storage: z.object({\n sqlitePath: z.string(),\n qdrantEndpoint: z.
|
|
1
|
+
{"version":3,"file":"config.js","sources":["../src/config.ts"],"sourcesContent":["import { homedir } from 'os';\nimport { join } from 'path';\nimport { existsSync, statSync } from 'fs';\nimport { z } from 'zod';\nimport { normalizePath } from './utils';\n\nconst WorkspaceSchema = z.object({\n paths: z.array(z.string()),\n isValid: z.boolean(),\n filesCount: z.number().optional(),\n chunksCount: z.number().optional(),\n});\n\nconst ConfigSchema = z.object({\n storage: z.object({\n sqlitePath: z.string(),\n qdrantEndpoint: z.url(),\n qdrantCollection: z.string(),\n qdrantApiKey: z.string().optional(),\n }),\n embedding: z.object({\n provider: z.enum(['ollama', 'openai', 'mock']),\n model: z.string(),\n endpoint: z.url(),\n }),\n indexing: z\n .object({\n chunkSize: z.number().positive(),\n chunkOverlap: z.number().nonnegative(),\n maxFileSize: z.number().positive(),\n ignorePatterns: z.array(z.string()),\n respectGitignore: z.boolean(),\n })\n .superRefine((indexing, ctx) => {\n if (indexing.chunkOverlap >= indexing.chunkSize) {\n ctx.addIssue({\n code: 'custom',\n path: ['chunkOverlap'],\n message: 'chunkOverlap must be smaller than chunkSize',\n });\n }\n }),\n dataDir: z.string(),\n verbose: z.boolean(),\n workspaces: z.record(z.string(), WorkspaceSchema),\n});\n\nexport type Config = z.infer<typeof ConfigSchema>;\nexport type WorkspaceConfig = z.infer<typeof WorkspaceSchema>;\n\nexport class ConfigError extends Error {\n constructor(message: string, public override cause?: Error) {\n super(message);\n this.name = 'ConfigError';\n }\n}\n\nfunction parseWorkspaces(env: Record<string, string | undefined>): Record<string, WorkspaceConfig> {\n const workspaces: Record<string, WorkspaceConfig> = {};\n \n for (const [key, value] of Object.entries(env)) {\n if (key.startsWith('WORKSPACE_') && value) {\n const name = key.replace('WORKSPACE_', '').toLowerCase();\n \n // Parse paths from comma-separated string or JSON array\n let paths: string[];\n try {\n // Try parsing as JSON array first\n paths = JSON.parse(value);\n if (!Array.isArray(paths)) {\n throw new Error('Not an array');\n }\n } catch {\n // Fall back to comma-separated string\n paths = value.split(',').map(p => p.trim()).filter(p => p.length > 0);\n }\n \n // Normalize paths for consistent comparison\n const normalizedPaths = paths.map(normalizePath);\n \n // Validate that paths exist and are directories\n const isValid = normalizedPaths.every(path => {\n try {\n return existsSync(path) && statSync(path).isDirectory();\n } catch {\n return false;\n }\n });\n \n workspaces[name] = {\n paths: normalizedPaths,\n isValid,\n };\n }\n }\n \n return workspaces;\n}\n\nexport function getWorkspacePaths(config: Config, workspace: string): string[] {\n const workspaceConfig = config.workspaces[workspace];\n return workspaceConfig?.paths || [];\n}\n\nexport function getAvailableWorkspaces(config: Config): string[] {\n return Object.keys(config.workspaces);\n}\n\nexport function loadConfig(options: { verbose?: boolean } = {}): Config {\n const dataDir = process.env.DIRECTORY_INDEXER_DATA_DIR || join(homedir(), '.directory-indexer');\n \n // Use separate database file and collection for tests to avoid contaminating main data\n const isTest = process.env.NODE_ENV === 'test' || process.env.VITEST === 'true';\n const dbFileName = isTest ? 'test-data.db' : 'data.db';\n const defaultCollection = isTest ? 'directory-indexer-test' : 'directory-indexer';\n \n // Parse workspace configurations from environment variables\n const workspaces = parseWorkspaces(process.env);\n \n const config = {\n storage: {\n sqlitePath: join(dataDir, dbFileName),\n qdrantEndpoint: process.env.QDRANT_ENDPOINT || 'http://127.0.0.1:6333',\n qdrantCollection: process.env.DIRECTORY_INDEXER_QDRANT_COLLECTION || defaultCollection,\n qdrantApiKey: process.env.QDRANT_API_KEY,\n },\n embedding: {\n provider: (process.env.EMBEDDING_PROVIDER as Config['embedding']['provider']) || 'ollama',\n model: process.env.EMBEDDING_MODEL || 'nomic-embed-text',\n endpoint: process.env.EMBEDDING_PROVIDER === 'openai'\n ? (process.env.OPENAI_ENDPOINT || 'https://api.openai.com/v1')\n : (process.env.OLLAMA_ENDPOINT || 'http://127.0.0.1:11434'),\n },\n indexing: {\n chunkSize: parseInt(process.env.CHUNK_SIZE || '512'),\n chunkOverlap: parseInt(process.env.CHUNK_OVERLAP || '50'),\n maxFileSize: parseInt(process.env.MAX_FILE_SIZE || '10485760'),\n ignorePatterns: ['.git', 'node_modules', 'target', '.DS_Store'],\n respectGitignore: process.env.RESPECT_GITIGNORE !== 'false',\n },\n dataDir,\n verbose: options.verbose ?? (process.env.VERBOSE === 'true'),\n workspaces,\n };\n\n try {\n return ConfigSchema.parse(config);\n } catch (error) {\n if (error instanceof z.ZodError) {\n const messages = error.issues.map(e => `${e.path.join('.')}: ${e.message}`);\n throw new ConfigError(`Configuration validation failed: ${messages.join(', ')}`, error);\n }\n throw new ConfigError('Failed to load configuration', error as Error);\n }\n}"],"names":[],"mappings":"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"}
|
package/dist/embedding.js
CHANGED
|
@@ -86,7 +86,7 @@ class OpenAIEmbeddingProvider {
|
|
|
86
86
|
dimensions = 1536;
|
|
87
87
|
async generateEmbedding(text) {
|
|
88
88
|
try {
|
|
89
|
-
const response = await fetch(
|
|
89
|
+
const response = await fetch(`${this.config.endpoint}/embeddings`, {
|
|
90
90
|
method: "POST",
|
|
91
91
|
headers: {
|
|
92
92
|
"Content-Type": "application/json",
|
|
@@ -108,7 +108,7 @@ class OpenAIEmbeddingProvider {
|
|
|
108
108
|
}
|
|
109
109
|
async generateEmbeddings(texts) {
|
|
110
110
|
try {
|
|
111
|
-
const response = await fetch(
|
|
111
|
+
const response = await fetch(`${this.config.endpoint}/embeddings`, {
|
|
112
112
|
method: "POST",
|
|
113
113
|
headers: {
|
|
114
114
|
"Content-Type": "application/json",
|
package/dist/embedding.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedding.js","sources":["../src/embedding.ts"],"sourcesContent":["import { Config } from './config.js';\n\nexport interface EmbeddingProvider {\n name: string;\n dimensions: number;\n generateEmbedding(text: string): Promise<number[]>;\n generateEmbeddings(texts: string[]): Promise<number[][]>;\n}\n\nexport interface EmbeddingConfig {\n model: string;\n endpoint: string;\n dimensions: number;\n}\n\nexport class EmbeddingError extends Error {\n constructor(message: string, public override cause?: Error) {\n super(message);\n this.name = 'EmbeddingError';\n }\n}\n\nclass MockEmbeddingProvider implements EmbeddingProvider {\n name = 'mock';\n \n constructor(private config: EmbeddingConfig) {}\n \n get dimensions(): number {\n return this.config.dimensions;\n }\n \n async generateEmbedding(text: string): Promise<number[]> {\n const hash = this.hashString(text);\n return Array.from({ length: this.dimensions }, (_, i) => \n Math.sin(hash + i) * 0.1\n );\n }\n \n async generateEmbeddings(texts: string[]): Promise<number[][]> {\n return Promise.all(texts.map(text => this.generateEmbedding(text)));\n }\n \n private hashString(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return Math.abs(hash);\n }\n}\n\nclass OllamaEmbeddingProvider implements EmbeddingProvider {\n name = 'ollama';\n dimensions = 768;\n \n constructor(private config: EmbeddingConfig) {}\n \n async generateEmbedding(text: string): Promise<number[]> {\n try {\n const response = await fetch(`${this.config.endpoint}/api/embed`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: this.config.model,\n input: text\n })\n });\n \n if (!response.ok) {\n throw new Error(`Ollama API error: ${response.statusText}`);\n }\n \n const data = await response.json();\n return data.embeddings[0];\n } catch (error) {\n throw new EmbeddingError(`Failed to generate Ollama embedding`, error as Error);\n }\n }\n \n async generateEmbeddings(texts: string[]): Promise<number[][]> {\n try {\n const response = await fetch(`${this.config.endpoint}/api/embed`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: this.config.model,\n input: texts\n })\n });\n \n if (!response.ok) {\n throw new Error(`Ollama API error: ${response.statusText}`);\n }\n \n const data = await response.json();\n return data.embeddings;\n } catch (error) {\n throw new EmbeddingError(`Failed to generate Ollama embeddings`, error as Error);\n }\n }\n}\n\nclass OpenAIEmbeddingProvider implements EmbeddingProvider {\n name = 'openai';\n dimensions = 1536;\n \n constructor(private config: EmbeddingConfig) {}\n \n async generateEmbedding(text: string): Promise<number[]> {\n try {\n const response = await fetch(
|
|
1
|
+
{"version":3,"file":"embedding.js","sources":["../src/embedding.ts"],"sourcesContent":["import { Config } from './config.js';\n\nexport interface EmbeddingProvider {\n name: string;\n dimensions: number;\n generateEmbedding(text: string): Promise<number[]>;\n generateEmbeddings(texts: string[]): Promise<number[][]>;\n}\n\nexport interface EmbeddingConfig {\n model: string;\n endpoint: string;\n dimensions: number;\n}\n\nexport class EmbeddingError extends Error {\n constructor(message: string, public override cause?: Error) {\n super(message);\n this.name = 'EmbeddingError';\n }\n}\n\nclass MockEmbeddingProvider implements EmbeddingProvider {\n name = 'mock';\n \n constructor(private config: EmbeddingConfig) {}\n \n get dimensions(): number {\n return this.config.dimensions;\n }\n \n async generateEmbedding(text: string): Promise<number[]> {\n const hash = this.hashString(text);\n return Array.from({ length: this.dimensions }, (_, i) => \n Math.sin(hash + i) * 0.1\n );\n }\n \n async generateEmbeddings(texts: string[]): Promise<number[][]> {\n return Promise.all(texts.map(text => this.generateEmbedding(text)));\n }\n \n private hashString(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return Math.abs(hash);\n }\n}\n\nclass OllamaEmbeddingProvider implements EmbeddingProvider {\n name = 'ollama';\n dimensions = 768;\n \n constructor(private config: EmbeddingConfig) {}\n \n async generateEmbedding(text: string): Promise<number[]> {\n try {\n const response = await fetch(`${this.config.endpoint}/api/embed`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: this.config.model,\n input: text\n })\n });\n \n if (!response.ok) {\n throw new Error(`Ollama API error: ${response.statusText}`);\n }\n \n const data = await response.json();\n return data.embeddings[0];\n } catch (error) {\n throw new EmbeddingError(`Failed to generate Ollama embedding`, error as Error);\n }\n }\n \n async generateEmbeddings(texts: string[]): Promise<number[][]> {\n try {\n const response = await fetch(`${this.config.endpoint}/api/embed`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n model: this.config.model,\n input: texts\n })\n });\n \n if (!response.ok) {\n throw new Error(`Ollama API error: ${response.statusText}`);\n }\n \n const data = await response.json();\n return data.embeddings;\n } catch (error) {\n throw new EmbeddingError(`Failed to generate Ollama embeddings`, error as Error);\n }\n }\n}\n\nclass OpenAIEmbeddingProvider implements EmbeddingProvider {\n name = 'openai';\n dimensions = 1536;\n \n constructor(private config: EmbeddingConfig) {}\n \n async generateEmbedding(text: string): Promise<number[]> {\n try {\n const response = await fetch(`${this.config.endpoint}/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: this.config.model,\n input: text\n })\n });\n \n if (!response.ok) {\n throw new Error(`OpenAI API error: ${response.statusText}`);\n }\n \n const data = await response.json();\n return data.data[0].embedding;\n } catch (error) {\n throw new EmbeddingError(`Failed to generate OpenAI embedding`, error as Error);\n }\n }\n \n async generateEmbeddings(texts: string[]): Promise<number[][]> {\n try {\n const response = await fetch(`${this.config.endpoint}/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: this.config.model,\n input: texts\n })\n });\n \n if (!response.ok) {\n throw new Error(`OpenAI API error: ${response.statusText}`);\n }\n \n const data = await response.json();\n return data.data.map((item: { embedding: number[] }) => item.embedding);\n } catch (error) {\n throw new EmbeddingError(`Failed to generate OpenAI embeddings`, error as Error);\n }\n }\n}\n\nexport function createEmbeddingProvider(provider: string, config: EmbeddingConfig): EmbeddingProvider {\n switch (provider) {\n case 'mock':\n return new MockEmbeddingProvider(config);\n case 'ollama':\n return new OllamaEmbeddingProvider(config);\n case 'openai':\n return new OpenAIEmbeddingProvider(config);\n default:\n throw new EmbeddingError(`Unknown embedding provider: ${provider}`);\n }\n}\n\nexport async function generateEmbedding(text: string, config: Config): Promise<number[]> {\n const provider = createEmbeddingProvider(config.embedding.provider, {\n model: config.embedding.model,\n endpoint: config.embedding.endpoint,\n dimensions: config.embedding.provider === 'mock' ? 384 : (config.embedding.provider === 'ollama' ? 768 : 1536)\n });\n \n return provider.generateEmbedding(text);\n}"],"names":[],"mappings":"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"}
|
package/dist/indexing.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { promises } from "fs";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import { join } from "path";
|
|
3
4
|
import { getFileInfo, normalizePath, shouldIgnoreFile, isDirectory, isFile, isSupportedFileType } from "./utils.js";
|
|
4
5
|
import { loadGitignoreRules } from "./gitignore.js";
|
|
5
6
|
import { generateEmbedding } from "./embedding.js";
|
|
6
|
-
import { initializeStorage } from "./storage.js";
|
|
7
|
+
import { initializeStorage, ensureCollectionDimensions } from "./storage.js";
|
|
7
8
|
import { log } from "./logger.js";
|
|
8
9
|
class IndexingError extends Error {
|
|
9
10
|
constructor(message, cause) {
|
|
@@ -12,6 +13,10 @@ class IndexingError extends Error {
|
|
|
12
13
|
this.name = "IndexingError";
|
|
13
14
|
}
|
|
14
15
|
}
|
|
16
|
+
function pointIdFor(filePath, chunkId) {
|
|
17
|
+
const hex = createHash("sha256").update(`${filePath}:${chunkId}`).digest("hex").slice(0, 32);
|
|
18
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
19
|
+
}
|
|
15
20
|
function chunkText(content, chunkSize, overlap) {
|
|
16
21
|
if (content.length <= chunkSize) {
|
|
17
22
|
return [{
|
|
@@ -51,10 +56,16 @@ async function scanDirectory(dirPath, options) {
|
|
|
51
56
|
const gitignoreFilter = options.respectGitignore ? await loadGitignoreRules(dirPath) : null;
|
|
52
57
|
async function walkDirectory(currentPath) {
|
|
53
58
|
const normalizedPath = normalizePath(currentPath);
|
|
54
|
-
|
|
59
|
+
let realPath = normalizedPath;
|
|
60
|
+
try {
|
|
61
|
+
realPath = normalizePath(await promises.realpath(normalizedPath));
|
|
62
|
+
} catch {
|
|
63
|
+
realPath = normalizedPath;
|
|
64
|
+
}
|
|
65
|
+
if (visited.has(realPath)) {
|
|
55
66
|
return;
|
|
56
67
|
}
|
|
57
|
-
visited.add(
|
|
68
|
+
visited.add(realPath);
|
|
58
69
|
try {
|
|
59
70
|
const relativePath = normalizedPath.startsWith(basePath) ? normalizedPath.slice(basePath.length + 1) : normalizedPath;
|
|
60
71
|
if (shouldIgnoreFile(normalizedPath, relativePath, options.ignorePatterns, gitignoreFilter)) {
|
|
@@ -96,31 +107,38 @@ async function shouldReprocessFile(filePath, existingRecord, config) {
|
|
|
96
107
|
const fs2 = await import("fs/promises");
|
|
97
108
|
const currentStats = await fs2.stat(filePath);
|
|
98
109
|
const existingModTime = new Date(existingRecord.modifiedTime);
|
|
99
|
-
if (currentStats.mtime
|
|
110
|
+
if (currentStats.mtime.getTime() === existingModTime.getTime()) {
|
|
100
111
|
return false;
|
|
101
112
|
}
|
|
102
113
|
const currentFileInfo = await getFileInfo(filePath);
|
|
103
114
|
return currentFileInfo.hash !== existingRecord.hash;
|
|
104
115
|
} catch (modtimeError) {
|
|
105
116
|
if (config.verbose) {
|
|
106
|
-
|
|
117
|
+
log("warning", `Could not check modification time for ${filePath}`, {
|
|
118
|
+
error: modtimeError instanceof Error ? modtimeError.message : String(modtimeError)
|
|
119
|
+
});
|
|
107
120
|
}
|
|
108
121
|
try {
|
|
109
122
|
const currentFileInfo = await getFileInfo(filePath);
|
|
110
123
|
return currentFileInfo.hash !== existingRecord.hash;
|
|
111
124
|
} catch (hashError) {
|
|
112
125
|
if (config.verbose) {
|
|
113
|
-
|
|
126
|
+
log("warning", `Could not compute hash for ${filePath}`, {
|
|
127
|
+
error: hashError instanceof Error ? hashError.message : String(hashError)
|
|
128
|
+
});
|
|
114
129
|
}
|
|
115
130
|
return true;
|
|
116
131
|
}
|
|
117
132
|
}
|
|
118
133
|
}
|
|
119
|
-
async function indexDirectories(paths, config) {
|
|
134
|
+
async function indexDirectories(paths, config, reporters = {}) {
|
|
135
|
+
const progress = reporters.progress ?? ((message) => log("info", message));
|
|
136
|
+
const reportError = reporters.error ?? ((message) => log("error", message));
|
|
120
137
|
let indexed = 0;
|
|
121
138
|
let skipped = 0;
|
|
122
139
|
let failed = 0;
|
|
123
140
|
let deleted = 0;
|
|
141
|
+
let collectionEnsured = false;
|
|
124
142
|
const errors = [];
|
|
125
143
|
const scanOptions = {
|
|
126
144
|
ignorePatterns: config.indexing.ignorePatterns,
|
|
@@ -132,19 +150,19 @@ async function indexDirectories(paths, config) {
|
|
|
132
150
|
for (const path of paths) {
|
|
133
151
|
try {
|
|
134
152
|
if (config.verbose) {
|
|
135
|
-
|
|
153
|
+
progress(`Scanning directory: ${path}`);
|
|
136
154
|
}
|
|
137
155
|
const files = await scanDirectory(path, scanOptions);
|
|
138
156
|
totalFiles += files.length;
|
|
139
157
|
if (config.verbose) {
|
|
140
|
-
|
|
158
|
+
progress(`Found ${files.length} files to process in ${path}`);
|
|
141
159
|
}
|
|
142
160
|
} catch {
|
|
143
161
|
}
|
|
144
162
|
}
|
|
145
163
|
if (!config.verbose && totalFiles > 0) {
|
|
146
|
-
|
|
147
|
-
|
|
164
|
+
progress("");
|
|
165
|
+
progress(`Found ${totalFiles} files to process (checking for changes...)`);
|
|
148
166
|
}
|
|
149
167
|
for (const path of paths) {
|
|
150
168
|
try {
|
|
@@ -153,19 +171,22 @@ async function indexDirectories(paths, config) {
|
|
|
153
171
|
const files = await scanDirectory(path, scanOptions);
|
|
154
172
|
const dirStartIndexed = indexed;
|
|
155
173
|
const dirStartSkipped = skipped;
|
|
174
|
+
let dirHadFailure = false;
|
|
156
175
|
const progressInterval = totalFiles > 1e3 ? 50 : 10;
|
|
157
176
|
for (const file of files) {
|
|
177
|
+
let chunks = [];
|
|
158
178
|
try {
|
|
159
179
|
const existingFile = await sqlite.getFile(file.path);
|
|
160
180
|
if (existingFile) {
|
|
161
|
-
const
|
|
181
|
+
const hasStoredFailure = existingFile.errors?.some((error) => error.startsWith("Failed to process")) ?? false;
|
|
182
|
+
const needsReprocessing = hasStoredFailure || await shouldReprocessFile(file.path, existingFile, config);
|
|
162
183
|
if (!needsReprocessing) {
|
|
163
184
|
skipped++;
|
|
164
185
|
if (config.verbose) {
|
|
165
|
-
|
|
186
|
+
progress(` Skipped: ${file.path} (unchanged)`);
|
|
166
187
|
}
|
|
167
188
|
if (!config.verbose && (indexed + skipped) % progressInterval === 0) {
|
|
168
|
-
|
|
189
|
+
progress(`Progress: ${indexed + skipped}/${totalFiles} files (${skipped} skipped as unchanged)...`);
|
|
169
190
|
}
|
|
170
191
|
continue;
|
|
171
192
|
}
|
|
@@ -179,15 +200,17 @@ async function indexDirectories(paths, config) {
|
|
|
179
200
|
continue;
|
|
180
201
|
}
|
|
181
202
|
const content = rawContent.replace(/\r\n/g, "\n");
|
|
182
|
-
|
|
203
|
+
chunks = chunkText(content, config.indexing.chunkSize, config.indexing.chunkOverlap);
|
|
183
204
|
await sqlite.upsertFile(file, chunks);
|
|
184
205
|
for (let i = 0; i < chunks.length; i++) {
|
|
185
206
|
const chunk = chunks[i];
|
|
186
207
|
const embedding = await generateEmbedding(chunk.content, config);
|
|
187
|
-
|
|
188
|
-
|
|
208
|
+
if (!collectionEnsured) {
|
|
209
|
+
await ensureCollectionDimensions(sqlite, qdrant, config, embedding.length);
|
|
210
|
+
collectionEnsured = true;
|
|
211
|
+
}
|
|
189
212
|
const point = {
|
|
190
|
-
id:
|
|
213
|
+
id: pointIdFor(file.path, chunk.id),
|
|
191
214
|
vector: embedding,
|
|
192
215
|
payload: {
|
|
193
216
|
filePath: file.path,
|
|
@@ -201,10 +224,10 @@ async function indexDirectories(paths, config) {
|
|
|
201
224
|
}
|
|
202
225
|
indexed++;
|
|
203
226
|
if (config.verbose) {
|
|
204
|
-
|
|
227
|
+
progress(` Indexed: ${file.path} (${chunks.length} chunks)`);
|
|
205
228
|
}
|
|
206
229
|
if (!config.verbose && (indexed + skipped) % progressInterval === 0) {
|
|
207
|
-
|
|
230
|
+
progress(`Progress: ${indexed + skipped}/${totalFiles} files (${skipped} skipped as unchanged)...`);
|
|
208
231
|
}
|
|
209
232
|
} catch (error) {
|
|
210
233
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -212,7 +235,9 @@ async function indexDirectories(paths, config) {
|
|
|
212
235
|
const fullError = `Failed to process ${file.path}: ${errorMessage}${causeMessage}`;
|
|
213
236
|
errors.push(fullError);
|
|
214
237
|
failed++;
|
|
215
|
-
|
|
238
|
+
dirHadFailure = true;
|
|
239
|
+
reportError(`❌ ${fullError}`);
|
|
240
|
+
await sqlite.upsertFile(file, chunks, [fullError]);
|
|
216
241
|
}
|
|
217
242
|
}
|
|
218
243
|
const indexedFiles = await sqlite.getFilesByDirectory(normalizedPath);
|
|
@@ -224,26 +249,26 @@ async function indexDirectories(paths, config) {
|
|
|
224
249
|
await sqlite.deleteFile(deletedFile.path);
|
|
225
250
|
deleted++;
|
|
226
251
|
if (config.verbose) {
|
|
227
|
-
|
|
252
|
+
progress(` Cleaned up deleted file: ${deletedFile.path}`);
|
|
228
253
|
}
|
|
229
254
|
} catch (error) {
|
|
230
255
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
231
256
|
const fullError = `Failed to clean up deleted file ${deletedFile.path}: ${errorMessage}`;
|
|
232
257
|
errors.push(fullError);
|
|
233
258
|
failed++;
|
|
234
|
-
|
|
259
|
+
dirHadFailure = true;
|
|
260
|
+
reportError(`❌ ${fullError}`);
|
|
235
261
|
}
|
|
236
262
|
}
|
|
237
|
-
const
|
|
238
|
-
const directoryStatus = directoryErrors.length > 0 ? "failed" : "completed";
|
|
263
|
+
const directoryStatus = dirHadFailure ? "failed" : "completed";
|
|
239
264
|
await sqlite.upsertDirectory(normalizedPath, directoryStatus);
|
|
240
265
|
const dirFiles = files.length;
|
|
241
266
|
const dirIndexed = indexed - dirStartIndexed;
|
|
242
267
|
const dirSkipped = skipped - dirStartSkipped;
|
|
243
268
|
if (config.verbose) {
|
|
244
|
-
|
|
269
|
+
progress(`Directory ${path} completed: ${dirIndexed} indexed, ${dirSkipped} skipped`);
|
|
245
270
|
} else {
|
|
246
|
-
|
|
271
|
+
progress(`Directory ${path} completed: ${dirFiles} files processed`);
|
|
247
272
|
}
|
|
248
273
|
} catch (error) {
|
|
249
274
|
const normalizedPath = normalizePath(path);
|
|
@@ -258,6 +283,7 @@ export {
|
|
|
258
283
|
chunkText,
|
|
259
284
|
getFileMetadata,
|
|
260
285
|
indexDirectories,
|
|
286
|
+
pointIdFor,
|
|
261
287
|
scanDirectory
|
|
262
288
|
};
|
|
263
289
|
//# sourceMappingURL=indexing.js.map
|