directory-indexer 0.1.7 → 0.2.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 CHANGED
@@ -7,7 +7,7 @@
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
8
8
  [![CI](https://github.com/peteretelej/directory-indexer/workflows/CI/badge.svg)](https://github.com/peteretelej/directory-indexer/actions)
9
9
 
10
- Self-hosted semantic search for local files. Enable AI assistants to search your documents using vector embeddings and MCP integration.
10
+ Self-hosted semantic search for local files. Give your AI assistant access to search, read, and work with your local files and directories. Ask about topics, patterns, or examples and get meaningful results from your actual knowledge base.
11
11
 
12
12
  ## Quick Start
13
13
 
package/dist/cli.js CHANGED
@@ -9,6 +9,7 @@ import { loadConfig } from "./config.js";
9
9
  import { getIndexStatus } from "./storage.js";
10
10
  import { startMcpServer } from "./mcp.js";
11
11
  import { validateIndexPrerequisites, validateSearchPrerequisites, getServiceStatus } from "./prerequisites.js";
12
+ import { resetEnvironment } from "./reset.js";
12
13
  const __dirname = dirname(fileURLToPath(import.meta.url));
13
14
  const packageJsonPath = join(__dirname, "../package.json");
14
15
  const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
@@ -102,6 +103,22 @@ async function main() {
102
103
  process.exit(1);
103
104
  }
104
105
  });
106
+ program.command("reset").description("Reset directory-indexer data (database and vector collection)").option("--force", "Skip confirmation prompt").option("-v, --verbose", "Enable verbose logging").action(async (options) => {
107
+ try {
108
+ const config = await loadConfig({ verbose: options.verbose });
109
+ await resetEnvironment(config, {
110
+ force: options.force,
111
+ verbose: options.verbose
112
+ });
113
+ } catch (error) {
114
+ if (error instanceof Error && error.message === "Reset cancelled by user") {
115
+ console.log("\nReset cancelled.");
116
+ process.exit(0);
117
+ }
118
+ console.error("Error during reset:", error);
119
+ process.exit(1);
120
+ }
121
+ });
105
122
  program.command("status").description("Show indexing status").option("-v, --verbose", "Enable verbose logging").action(async (options) => {
106
123
  try {
107
124
  const config = await loadConfig({ verbose: options.verbose });
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 { 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, cleaned up ${result.deleted} deleted 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 // Show workspace health information\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 } 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":[],"mappings":"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"}
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 { 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\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, cleaned up ${result.deleted} deleted 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('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 const config = await loadConfig({ verbose: options.verbose });\n await resetEnvironment(config, { \n force: options.force, \n verbose: options.verbose \n });\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 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 // Show workspace health information\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 } 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":[],"mappings":"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"}
package/dist/config.js CHANGED
@@ -13,7 +13,8 @@ const ConfigSchema = z.object({
13
13
  storage: z.object({
14
14
  sqlitePath: z.string(),
15
15
  qdrantEndpoint: z.string().url(),
16
- qdrantCollection: z.string()
16
+ qdrantCollection: z.string(),
17
+ qdrantApiKey: z.string().optional()
17
18
  }),
18
19
  embedding: z.object({
19
20
  provider: z.enum(["ollama", "openai", "mock"]),
@@ -84,7 +85,8 @@ function loadConfig(options = {}) {
84
85
  storage: {
85
86
  sqlitePath: join(dataDir, dbFileName),
86
87
  qdrantEndpoint: process.env.QDRANT_ENDPOINT || "http://127.0.0.1:6333",
87
- qdrantCollection: process.env.DIRECTORY_INDEXER_QDRANT_COLLECTION || defaultCollection
88
+ qdrantCollection: process.env.DIRECTORY_INDEXER_QDRANT_COLLECTION || defaultCollection,
89
+ qdrantApiKey: process.env.QDRANT_API_KEY
88
90
  },
89
91
  embedding: {
90
92
  provider: process.env.EMBEDDING_PROVIDER || "ollama",
@@ -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.string().url(),\n qdrantCollection: z.string(),\n }),\n embedding: z.object({\n provider: z.enum(['ollama', 'openai', 'mock']),\n model: z.string(),\n endpoint: z.string().url(),\n }),\n indexing: z.object({\n chunkSize: z.number().positive(),\n chunkOverlap: z.number().nonnegative(),\n maxFileSize: z.number().positive(),\n ignorePatterns: z.array(z.string()),\n }),\n dataDir: z.string(),\n verbose: z.boolean(),\n workspaces: z.record(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 },\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.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 },\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.errors.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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"}
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.string().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.string().url(),\n }),\n indexing: z.object({\n chunkSize: z.number().positive(),\n chunkOverlap: z.number().nonnegative(),\n maxFileSize: z.number().positive(),\n ignorePatterns: z.array(z.string()),\n }),\n dataDir: z.string(),\n verbose: z.boolean(),\n workspaces: z.record(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.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 },\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.errors.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/reset.js ADDED
@@ -0,0 +1,84 @@
1
+ import { readlineSync } from "./utils.js";
2
+ import { getResetPreview, clearDatabase, StorageError, clearVectorCollection } from "./storage.js";
3
+ async function resetEnvironment(config, options = {}) {
4
+ const stats = await getResetPreview(config);
5
+ if (!options.force) {
6
+ await showConfirmation(stats, config);
7
+ }
8
+ await performReset(config, options);
9
+ }
10
+ async function showConfirmation(stats, config) {
11
+ console.log("\nThe following directory-indexer data will be reset:");
12
+ if (stats.sqliteExists) {
13
+ console.log(` • SQLite database: ${config.storage.sqlitePath} (${stats.sqliteSize})`);
14
+ } else {
15
+ console.log(` • SQLite database: ${config.storage.sqlitePath} (not found)`);
16
+ }
17
+ if (stats.qdrantCollectionExists) {
18
+ const vectorText = stats.qdrantVectorCount === 1 ? "vector" : "vectors";
19
+ console.log(` • Qdrant collection: ${config.storage.qdrantCollection} (${stats.qdrantVectorCount} ${vectorText})`);
20
+ } else {
21
+ console.log(` • Qdrant collection: ${config.storage.qdrantCollection} (not found)`);
22
+ }
23
+ if (config.storage.qdrantEndpoint !== "http://localhost:6333") {
24
+ console.log(` • Qdrant endpoint: ${config.storage.qdrantEndpoint}`);
25
+ }
26
+ console.log("\nYour original files will not be touched.");
27
+ const answer = await readlineSync("\nContinue? (y/N): ");
28
+ if (!answer || !["y", "yes"].includes(answer.toLowerCase().trim())) {
29
+ throw new Error("Reset cancelled by user");
30
+ }
31
+ }
32
+ async function performReset(config, options) {
33
+ let sqliteDeleted = false;
34
+ let qdrantDeleted = false;
35
+ const warnings = [];
36
+ if (options.verbose) {
37
+ console.log("\nResetting directory-indexer data...");
38
+ }
39
+ try {
40
+ if (options.verbose) {
41
+ console.log(` ✓ Deleting SQLite database: ${config.storage.sqlitePath}`);
42
+ }
43
+ sqliteDeleted = await clearDatabase(config);
44
+ if (options.verbose && sqliteDeleted) {
45
+ console.log(` ✓ SQLite database cleared`);
46
+ }
47
+ } catch (error) {
48
+ const message = error instanceof StorageError ? error.message : `Failed to clear database: ${error}`;
49
+ warnings.push(message);
50
+ if (options.verbose) {
51
+ console.log(` ⚠ ${message}`);
52
+ }
53
+ }
54
+ try {
55
+ if (options.verbose) {
56
+ console.log(` ✓ Deleting Qdrant collection: ${config.storage.qdrantCollection}`);
57
+ }
58
+ qdrantDeleted = await clearVectorCollection(config);
59
+ if (options.verbose && qdrantDeleted) {
60
+ console.log(` ✓ Qdrant collection cleared`);
61
+ }
62
+ } catch (error) {
63
+ const message = error instanceof StorageError ? error.message : `Failed to clear collection: ${error}`;
64
+ warnings.push(message);
65
+ if (options.verbose) {
66
+ console.log(` ⚠ ${message}`);
67
+ }
68
+ }
69
+ if (warnings.length > 0) {
70
+ console.log("\nReset completed with warnings:");
71
+ warnings.forEach((warning) => console.log(` ⚠ ${warning}`));
72
+ if (sqliteDeleted || qdrantDeleted) {
73
+ console.log("\nPartial reset successful. Directory-indexer is ready for fresh indexing.");
74
+ } else {
75
+ console.log("\nReset had issues but you can try again or check your configuration.");
76
+ }
77
+ } else {
78
+ console.log("\nReset complete. Directory-indexer is ready for fresh indexing.");
79
+ }
80
+ }
81
+ export {
82
+ resetEnvironment
83
+ };
84
+ //# sourceMappingURL=reset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reset.js","sources":["../src/reset.ts"],"sourcesContent":["import { readlineSync } from './utils.js';\nimport { getResetPreview, clearDatabase, clearVectorCollection, StorageError, type ResetStats } from './storage.js';\nimport type { Config } from './config.js';\n\nexport interface ResetOptions {\n force?: boolean;\n verbose?: boolean;\n}\n\nexport async function resetEnvironment(config: Config, options: ResetOptions = {}): Promise<void> {\n const stats = await getResetPreview(config);\n \n if (!options.force) {\n await showConfirmation(stats, config);\n }\n \n await performReset(config, options);\n}\n\n\nasync function showConfirmation(stats: ResetStats, config: Config): Promise<void> {\n console.log('\\nThe following directory-indexer data will be reset:');\n \n if (stats.sqliteExists) {\n console.log(` • SQLite database: ${config.storage.sqlitePath} (${stats.sqliteSize})`);\n } else {\n console.log(` • SQLite database: ${config.storage.sqlitePath} (not found)`);\n }\n \n if (stats.qdrantCollectionExists) {\n const vectorText = stats.qdrantVectorCount === 1 ? 'vector' : 'vectors';\n console.log(` • Qdrant collection: ${config.storage.qdrantCollection} (${stats.qdrantVectorCount} ${vectorText})`);\n } else {\n console.log(` • Qdrant collection: ${config.storage.qdrantCollection} (not found)`);\n }\n \n if (config.storage.qdrantEndpoint !== 'http://localhost:6333') {\n console.log(` • Qdrant endpoint: ${config.storage.qdrantEndpoint}`);\n }\n \n console.log('\\nYour original files will not be touched.');\n \n const answer = await readlineSync('\\nContinue? (y/N): ');\n if (!answer || !['y', 'yes'].includes(answer.toLowerCase().trim())) {\n throw new Error('Reset cancelled by user');\n }\n}\n\nasync function performReset(config: Config, options: ResetOptions): Promise<void> {\n let sqliteDeleted = false;\n let qdrantDeleted = false;\n const warnings: string[] = [];\n\n if (options.verbose) {\n console.log('\\nResetting directory-indexer data...');\n }\n\n // Reset SQLite database\n try {\n if (options.verbose) {\n console.log(` ✓ Deleting SQLite database: ${config.storage.sqlitePath}`);\n }\n sqliteDeleted = await clearDatabase(config);\n if (options.verbose && sqliteDeleted) {\n console.log(` ✓ SQLite database cleared`);\n }\n } catch (error) {\n const message = error instanceof StorageError ? error.message : `Failed to clear database: ${error}`;\n warnings.push(message);\n if (options.verbose) {\n console.log(` ⚠ ${message}`);\n }\n }\n\n // Reset Qdrant collection\n try {\n if (options.verbose) {\n console.log(` ✓ Deleting Qdrant collection: ${config.storage.qdrantCollection}`);\n }\n qdrantDeleted = await clearVectorCollection(config);\n if (options.verbose && qdrantDeleted) {\n console.log(` ✓ Qdrant collection cleared`);\n }\n } catch (error) {\n const message = error instanceof StorageError ? error.message : `Failed to clear collection: ${error}`;\n warnings.push(message);\n if (options.verbose) {\n console.log(` ⚠ ${message}`);\n }\n }\n\n // Show results\n if (warnings.length > 0) {\n console.log('\\nReset completed with warnings:');\n warnings.forEach(warning => console.log(` ⚠ ${warning}`));\n \n if (sqliteDeleted || qdrantDeleted) {\n console.log('\\nPartial reset successful. Directory-indexer is ready for fresh indexing.');\n } else {\n console.log('\\nReset had issues but you can try again or check your configuration.');\n }\n } else {\n console.log('\\nReset complete. Directory-indexer is ready for fresh indexing.');\n }\n}"],"names":[],"mappings":";;AASA,eAAsB,iBAAiB,QAAgB,UAAwB,IAAmB;AAChG,QAAM,QAAQ,MAAM,gBAAgB,MAAM;AAE1C,MAAI,CAAC,QAAQ,OAAO;AAClB,UAAM,iBAAiB,OAAO,MAAM;AAAA,EACtC;AAEA,QAAM,aAAa,QAAQ,OAAO;AACpC;AAGA,eAAe,iBAAiB,OAAmB,QAA+B;AAChF,UAAQ,IAAI,uDAAuD;AAEnE,MAAI,MAAM,cAAc;AACtB,YAAQ,IAAI,wBAAwB,OAAO,QAAQ,UAAU,KAAK,MAAM,UAAU,GAAG;AAAA,EACvF,OAAO;AACL,YAAQ,IAAI,wBAAwB,OAAO,QAAQ,UAAU,cAAc;AAAA,EAC7E;AAEA,MAAI,MAAM,wBAAwB;AAChC,UAAM,aAAa,MAAM,sBAAsB,IAAI,WAAW;AAC9D,YAAQ,IAAI,0BAA0B,OAAO,QAAQ,gBAAgB,KAAK,MAAM,iBAAiB,IAAI,UAAU,GAAG;AAAA,EACpH,OAAO;AACL,YAAQ,IAAI,0BAA0B,OAAO,QAAQ,gBAAgB,cAAc;AAAA,EACrF;AAEA,MAAI,OAAO,QAAQ,mBAAmB,yBAAyB;AAC7D,YAAQ,IAAI,wBAAwB,OAAO,QAAQ,cAAc,EAAE;AAAA,EACrE;AAEA,UAAQ,IAAI,4CAA4C;AAExD,QAAM,SAAS,MAAM,aAAa,qBAAqB;AACvD,MAAI,CAAC,UAAU,CAAC,CAAC,KAAK,KAAK,EAAE,SAAS,OAAO,cAAc,KAAA,CAAM,GAAG;AAClE,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AACF;AAEA,eAAe,aAAa,QAAgB,SAAsC;AAChF,MAAI,gBAAgB;AACpB,MAAI,gBAAgB;AACpB,QAAM,WAAqB,CAAA;AAE3B,MAAI,QAAQ,SAAS;AACnB,YAAQ,IAAI,uCAAuC;AAAA,EACrD;AAGA,MAAI;AACF,QAAI,QAAQ,SAAS;AACnB,cAAQ,IAAI,iCAAiC,OAAO,QAAQ,UAAU,EAAE;AAAA,IAC1E;AACA,oBAAgB,MAAM,cAAc,MAAM;AAC1C,QAAI,QAAQ,WAAW,eAAe;AACpC,cAAQ,IAAI,6BAA6B;AAAA,IAC3C;AAAA,EACF,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,eAAe,MAAM,UAAU,6BAA6B,KAAK;AAClG,aAAS,KAAK,OAAO;AACrB,QAAI,QAAQ,SAAS;AACnB,cAAQ,IAAI,OAAO,OAAO,EAAE;AAAA,IAC9B;AAAA,EACF;AAGA,MAAI;AACF,QAAI,QAAQ,SAAS;AACnB,cAAQ,IAAI,mCAAmC,OAAO,QAAQ,gBAAgB,EAAE;AAAA,IAClF;AACA,oBAAgB,MAAM,sBAAsB,MAAM;AAClD,QAAI,QAAQ,WAAW,eAAe;AACpC,cAAQ,IAAI,+BAA+B;AAAA,IAC7C;AAAA,EACF,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,eAAe,MAAM,UAAU,+BAA+B,KAAK;AACpG,aAAS,KAAK,OAAO;AACrB,QAAI,QAAQ,SAAS;AACnB,cAAQ,IAAI,OAAO,OAAO,EAAE;AAAA,IAC9B;AAAA,EACF;AAGA,MAAI,SAAS,SAAS,GAAG;AACvB,YAAQ,IAAI,kCAAkC;AAC9C,aAAS,QAAQ,CAAA,YAAW,QAAQ,IAAI,OAAO,OAAO,EAAE,CAAC;AAEzD,QAAI,iBAAiB,eAAe;AAClC,cAAQ,IAAI,4EAA4E;AAAA,IAC1F,OAAO;AACL,cAAQ,IAAI,uEAAuE;AAAA,IACrF;AAAA,EACF,OAAO;AACL,YAAQ,IAAI,kEAAkE;AAAA,EAChF;AACF;"}
package/dist/storage.js CHANGED
@@ -182,6 +182,40 @@ class QdrantClient {
182
182
  throw new StorageError(`Failed to scroll points in Qdrant`, error);
183
183
  }
184
184
  }
185
+ async getCollectionInfo() {
186
+ const collectionName = this.config.storage.qdrantCollection;
187
+ try {
188
+ const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`);
189
+ if (!response.ok) {
190
+ if (response.status === 404) {
191
+ return null;
192
+ }
193
+ throw new Error(`Failed to get collection info: ${response.statusText}`);
194
+ }
195
+ const data = await response.json();
196
+ return {
197
+ vectors_count: data.result?.points_count || data.result?.vectors_count || 0
198
+ };
199
+ } catch (error) {
200
+ throw new StorageError(`Failed to get collection info from Qdrant`, error);
201
+ }
202
+ }
203
+ async deleteCollection() {
204
+ const collectionName = this.config.storage.qdrantCollection;
205
+ try {
206
+ const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`, {
207
+ method: "DELETE",
208
+ headers: this.config.storage.qdrantApiKey ? {
209
+ "api-key": this.config.storage.qdrantApiKey
210
+ } : {}
211
+ });
212
+ if (!response.ok && response.status !== 404) {
213
+ throw new Error(`Failed to delete collection: ${response.statusText}`);
214
+ }
215
+ } catch (error) {
216
+ throw new StorageError(`Failed to delete collection from Qdrant`, error);
217
+ }
218
+ }
185
219
  }
186
220
  class SQLiteStorage {
187
221
  constructor(config) {
@@ -324,6 +358,62 @@ async function initializeStorage(config) {
324
358
  async function initDatabase(dbPath) {
325
359
  return new Database(dbPath);
326
360
  }
361
+ async function getResetPreview(config) {
362
+ const stats = {
363
+ sqliteExists: false,
364
+ qdrantCollectionExists: false
365
+ };
366
+ if (await import("fs").then((fs) => fs.existsSync(config.storage.sqlitePath))) {
367
+ stats.sqliteExists = true;
368
+ try {
369
+ const fileStats = await import("fs").then((fs) => fs.statSync(config.storage.sqlitePath));
370
+ const sizeInMB = (fileStats.size / (1024 * 1024)).toFixed(1);
371
+ stats.sqliteSize = `${sizeInMB} MB`;
372
+ } catch {
373
+ stats.sqliteSize = "unknown size";
374
+ }
375
+ }
376
+ try {
377
+ const qdrant = new QdrantClient(config);
378
+ const isHealthy = await qdrant.healthCheck();
379
+ if (isHealthy) {
380
+ const collectionInfo = await qdrant.getCollectionInfo();
381
+ if (collectionInfo) {
382
+ stats.qdrantCollectionExists = true;
383
+ stats.qdrantVectorCount = collectionInfo.vectors_count || 0;
384
+ }
385
+ }
386
+ } catch {
387
+ }
388
+ return stats;
389
+ }
390
+ async function clearDatabase(config) {
391
+ try {
392
+ if (!await import("fs").then((fs) => fs.existsSync(config.storage.sqlitePath))) {
393
+ return true;
394
+ }
395
+ await import("fs/promises").then((fs) => fs.unlink(config.storage.sqlitePath));
396
+ return true;
397
+ } catch (error) {
398
+ throw new StorageError(`Failed to delete SQLite database: ${error instanceof Error ? error.message : "Unknown error"}`, error);
399
+ }
400
+ }
401
+ async function clearVectorCollection(config) {
402
+ try {
403
+ const qdrant = new QdrantClient(config);
404
+ const isHealthy = await qdrant.healthCheck();
405
+ if (!isHealthy) {
406
+ throw new StorageError(`Qdrant unavailable at ${config.storage.qdrantEndpoint}`);
407
+ }
408
+ const collectionInfo = await qdrant.getCollectionInfo();
409
+ if (collectionInfo) {
410
+ await qdrant.deleteCollection();
411
+ }
412
+ return true;
413
+ } catch (error) {
414
+ throw new StorageError(`Failed to reset Qdrant collection: ${error instanceof Error ? error.message : "Unknown error"}`, error);
415
+ }
416
+ }
327
417
  async function calculateWorkspaceStatistics(sqlite, config) {
328
418
  const { getAvailableWorkspaces, getWorkspacePaths } = await import("./config.js");
329
419
  const workspaces = [];
@@ -585,7 +675,10 @@ export {
585
675
  QdrantClient,
586
676
  SQLiteStorage,
587
677
  StorageError,
678
+ clearDatabase,
679
+ clearVectorCollection,
588
680
  getIndexStatus,
681
+ getResetPreview,
589
682
  initDatabase,
590
683
  initializeStorage
591
684
  };
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","sources":["../src/storage.ts"],"sourcesContent":["import Database from 'better-sqlite3';\nimport { Config } from './config.js';\nimport { FileInfo, ChunkInfo, ensureDirectory } from './utils.js';\nimport { dirname } from 'path';\n\nexport interface DirectoryRecord {\n id: number;\n path: string;\n status: 'pending' | 'indexing' | 'completed' | 'failed';\n indexedAt: Date;\n}\n\nexport interface FileRecord {\n id: number;\n path: string;\n size: number;\n modifiedTime: Date;\n hash: string;\n parentDirs: string[];\n chunks: ChunkInfo[];\n errors?: string[];\n}\n\nexport interface QdrantPoint {\n id: string | number;\n vector: number[];\n payload: {\n filePath: string;\n chunkId: string;\n fileHash: string;\n content: string;\n parentDirectories: string[];\n };\n score?: number;\n}\n\nexport class StorageError extends Error {\n constructor(message: string, public override cause?: Error) {\n super(message);\n this.name = 'StorageError';\n }\n}\n\nexport class QdrantClient {\n constructor(private config: Config) {}\n\n async healthCheck(): Promise<boolean> {\n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/healthz`);\n return response.ok;\n } catch {\n return false;\n }\n }\n\n async createCollection(): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const checkResponse = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`);\n if (checkResponse.ok) {\n return;\n }\n\n const createResponse = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n vectors: {\n size: 768,\n distance: 'Cosine'\n }\n })\n });\n\n if (!createResponse.ok) {\n throw new Error(`Failed to create collection: ${createResponse.statusText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to create Qdrant collection`, error as Error);\n }\n }\n\n async upsertPoints(points: QdrantPoint[]): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ points })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to upsert points: ${response.status} ${response.statusText} - ${errorText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to upsert points to Qdrant`, error as Error);\n }\n }\n\n async searchPoints(vector: number[], limit: number = 10, filter?: Record<string, unknown>): Promise<QdrantPoint[]> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const searchBody: Record<string, unknown> = {\n vector,\n limit,\n with_payload: true\n };\n \n if (filter) {\n searchBody.filter = filter;\n }\n \n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/search`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(searchBody)\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`Failed to search points: ${response.statusText} - ${errorBody}`);\n }\n\n const data = await response.json();\n return data.result.map((item: { id: string | number; vector: number[]; payload: Record<string, unknown>; score: number }) => ({\n id: item.id,\n vector: item.vector,\n payload: item.payload,\n score: item.score\n }));\n } catch (error) {\n throw new StorageError(`Failed to search points in Qdrant`, error as Error);\n }\n }\n\n async deletePoints(ids: (string | number)[]): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/delete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ points: ids })\n });\n\n if (!response.ok) {\n throw new Error(`Failed to delete points: ${response.statusText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to delete points from Qdrant`, error as Error);\n }\n }\n\n async deletePointsByFilePath(filePath: string): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/delete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n filter: {\n must: [\n {\n key: 'filePath',\n match: { value: filePath }\n }\n ]\n }\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to delete points by file path: ${response.status} ${response.statusText} - ${errorText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to delete points by file path from Qdrant`, error as Error);\n }\n }\n\n async countPoints(filter?: Record<string, unknown>): Promise<number> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const countBody: Record<string, unknown> = { exact: true };\n if (filter) {\n countBody.filter = filter;\n }\n\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/count`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(countBody)\n });\n\n if (!response.ok) {\n throw new Error(`Failed to count points: ${response.statusText}`);\n }\n\n const data = await response.json();\n return data.result.count;\n } catch (error) {\n throw new StorageError(`Failed to count points in Qdrant`, error as Error);\n }\n }\n\n async scrollPoints(filter?: Record<string, unknown>, limit: number = 1000): Promise<QdrantPoint[]> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const scrollBody: Record<string, unknown> = {\n limit,\n with_payload: true,\n with_vector: false\n };\n if (filter) {\n scrollBody.filter = filter;\n }\n\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/scroll`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(scrollBody)\n });\n\n if (!response.ok) {\n throw new Error(`Failed to scroll points: ${response.statusText}`);\n }\n\n const data = await response.json();\n return data.result.points.map((item: { id: string | number; payload: Record<string, unknown> }) => ({\n id: item.id,\n vector: [],\n payload: item.payload,\n score: 0\n }));\n } catch (error) {\n throw new StorageError(`Failed to scroll points in Qdrant`, error as Error);\n }\n }\n}\n\nexport class SQLiteStorage {\n public db: Database.Database;\n\n constructor(private config: Config) {\n this.db = this.initializeDatabase();\n }\n\n private initializeDatabase(): Database.Database {\n try {\n ensureDirectory(dirname(this.config.storage.sqlitePath));\n \n const db = new Database(this.config.storage.sqlitePath);\n \n db.exec(`\n CREATE TABLE IF NOT EXISTS directories (\n id INTEGER PRIMARY KEY,\n path TEXT UNIQUE NOT NULL,\n status TEXT DEFAULT 'pending',\n indexed_at INTEGER DEFAULT 0\n );\n\n CREATE TABLE IF NOT EXISTS files (\n id INTEGER PRIMARY KEY,\n path TEXT UNIQUE NOT NULL,\n size INTEGER NOT NULL,\n modified_time INTEGER NOT NULL,\n hash TEXT NOT NULL,\n parent_dirs TEXT NOT NULL,\n chunks_json TEXT,\n errors_json TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_files_path ON files(path);\n CREATE INDEX IF NOT EXISTS idx_files_hash ON files(hash);\n CREATE INDEX IF NOT EXISTS idx_directories_path ON directories(path);\n `);\n\n return db;\n } catch (error) {\n throw new StorageError(`Failed to initialize SQLite database`, error as Error);\n }\n }\n\n async getDirectory(path: string): Promise<DirectoryRecord | null> {\n try {\n const stmt = this.db.prepare('SELECT * FROM directories WHERE path = ?');\n const row = stmt.get(path) as { id: number; path: string; status: 'pending' | 'indexing' | 'completed' | 'failed'; indexed_at: number } | undefined;\n \n if (!row) return null;\n \n return {\n id: row.id,\n path: row.path,\n status: row.status,\n indexedAt: new Date(row.indexed_at)\n };\n } catch (error) {\n throw new StorageError(`Failed to get directory record`, error as Error);\n }\n }\n\n async upsertDirectory(path: string, status: DirectoryRecord['status']): Promise<void> {\n try {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO directories (path, status, indexed_at)\n VALUES (?, ?, ?)\n `);\n \n stmt.run(path, status, Date.now());\n } catch (error) {\n throw new StorageError(`Failed to upsert directory record`, error as Error);\n }\n }\n\n async getFile(path: string): Promise<FileRecord | null> {\n try {\n const stmt = this.db.prepare('SELECT * FROM files WHERE path = ?');\n const row = stmt.get(path) as { id: number; path: string; size: number; modified_time: number; hash: string; parent_dirs: string; chunks_json: string | null; errors_json: string | null } | undefined;\n \n if (!row) return null;\n \n return {\n id: row.id,\n path: row.path,\n size: row.size,\n modifiedTime: new Date(row.modified_time * 1000),\n hash: row.hash,\n parentDirs: JSON.parse(row.parent_dirs),\n chunks: row.chunks_json ? JSON.parse(row.chunks_json) : [],\n errors: row.errors_json ? JSON.parse(row.errors_json) : undefined\n };\n } catch (error) {\n throw new StorageError(`Failed to get file record`, error as Error);\n }\n }\n\n async upsertFile(fileInfo: FileInfo, chunks: ChunkInfo[] = [], errors: string[] = []): Promise<void> {\n try {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO files (path, size, modified_time, hash, parent_dirs, chunks_json, errors_json)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n \n stmt.run(\n fileInfo.path,\n fileInfo.size,\n fileInfo.modifiedTime.getTime(),\n fileInfo.hash,\n JSON.stringify(fileInfo.parentDirs),\n chunks.length > 0 ? JSON.stringify(chunks) : null,\n errors.length > 0 ? JSON.stringify(errors) : null\n );\n } catch (error) {\n throw new StorageError(`Failed to upsert file record`, error as Error);\n }\n }\n\n async deleteFile(path: string): Promise<void> {\n try {\n const stmt = this.db.prepare('DELETE FROM files WHERE path = ?');\n stmt.run(path);\n } catch (error) {\n throw new StorageError(`Failed to delete file record`, error as Error);\n }\n }\n\n async getFilesByDirectory(directoryPath: string): Promise<FileRecord[]> {\n try {\n const stmt = this.db.prepare('SELECT * FROM files WHERE path LIKE ?');\n const rows = stmt.all(`${directoryPath}%`) as { id: number; path: string; size: number; modified_time: number; hash: string; parent_dirs: string; chunks_json: string | null; errors_json: string | null }[];\n \n return rows.map(row => ({\n id: row.id,\n path: row.path,\n size: row.size,\n modifiedTime: new Date(row.modified_time * 1000),\n hash: row.hash,\n parentDirs: JSON.parse(row.parent_dirs),\n chunks: row.chunks_json ? JSON.parse(row.chunks_json) : [],\n errors: row.errors_json ? JSON.parse(row.errors_json) : undefined\n }));\n } catch (error) {\n throw new StorageError(`Failed to get files by directory`, error as Error);\n }\n }\n\n close(): void {\n this.db.close();\n }\n}\n\nexport async function initializeStorage(config: Config): Promise<{ sqlite: SQLiteStorage; qdrant: QdrantClient }> {\n const sqlite = new SQLiteStorage(config);\n const qdrant = new QdrantClient(config);\n \n await qdrant.createCollection();\n \n return { sqlite, qdrant };\n}\n\nexport async function initDatabase(dbPath: string): Promise<Database.Database> {\n return new Database(dbPath);\n}\n\nexport interface DirectoryStatus {\n path: string;\n status: string;\n filesCount: number;\n chunksCount: number;\n lastIndexed: string | null;\n errors: string[];\n}\n\nexport interface WorkspaceStatus {\n name: string;\n paths: string[];\n isValid: boolean;\n filesCount: number;\n chunksCount: number;\n health: {\n status: 'healthy' | 'warning' | 'error';\n issues: string[];\n recommendations: string[];\n };\n}\n\nexport interface IndexStatus {\n directoriesIndexed: number;\n filesIndexed: number;\n chunksIndexed: number;\n databaseSize: string;\n lastIndexed: string | null;\n errors: string[];\n directories: DirectoryStatus[];\n workspaces: WorkspaceStatus[];\n workspaceHealth: {\n healthy: number;\n warnings: number;\n errors: number;\n criticalIssues: string[];\n recommendations: string[];\n };\n qdrantConsistency: {\n isConsistent: boolean;\n issues: string[];\n };\n}\n\nasync function calculateWorkspaceStatistics(sqlite: SQLiteStorage, config: Config): Promise<WorkspaceStatus[]> {\n const { getAvailableWorkspaces, getWorkspacePaths } = await import('./config.js');\n const workspaces: WorkspaceStatus[] = [];\n \n // Get all indexed directories for comparison\n const directoriesStmt = sqlite.db.prepare('SELECT path, status FROM directories');\n const indexedDirectories = directoriesStmt.all() as { path: string; status: string }[];\n const indexedDirectoryPaths = indexedDirectories.map(d => d.path);\n \n // Get Qdrant client for efficient workspace filtering\n const qdrant = new QdrantClient(config);\n \n for (const workspaceName of getAvailableWorkspaces(config)) {\n const workspacePaths = getWorkspacePaths(config, workspaceName);\n const workspaceConfig = config.workspaces[workspaceName];\n \n let filesCount = 0;\n let chunksCount = 0;\n \n if (workspacePaths.length > 0) {\n try {\n // Build workspace filter using same logic as search\n const workspaceFilter = {\n must: [\n {\n key: \"parentDirectories\",\n match: { any: workspacePaths }\n }\n ]\n };\n \n // Get chunk count efficiently using Qdrant\n chunksCount = await qdrant.countPoints(workspaceFilter);\n \n // Get unique file paths using Qdrant scroll\n const points = await qdrant.scrollPoints(workspaceFilter, 10000);\n const uniqueFilePaths = new Set(points.map(p => p.payload.filePath));\n filesCount = uniqueFilePaths.size;\n \n } catch {\n // Fallback to 0 if Qdrant is unavailable\n filesCount = 0;\n chunksCount = 0;\n }\n }\n \n // Check workspace health\n const health = analyzeWorkspaceHealth(workspacePaths, workspaceConfig.isValid, filesCount, indexedDirectoryPaths);\n \n workspaces.push({\n name: workspaceName,\n paths: workspacePaths,\n isValid: workspaceConfig.isValid,\n filesCount,\n chunksCount,\n health\n });\n }\n \n return workspaces;\n}\n\nfunction analyzeWorkspaceHealth(\n workspacePaths: string[], \n isValid: boolean, \n filesCount: number, \n indexedDirectoryPaths: string[]\n): { status: 'healthy' | 'warning' | 'error'; issues: string[]; recommendations: string[] } {\n const issues: string[] = [];\n const recommendations: string[] = [];\n \n // Check if workspace paths exist and are valid\n if (!isValid) {\n issues.push('One or more workspace directories do not exist on the filesystem');\n recommendations.push('Verify workspace directory paths exist and are accessible');\n }\n \n // Check if workspace paths are indexed\n const unindexedPaths: string[] = [];\n const partiallyIndexedPaths: string[] = [];\n \n for (const workspacePath of workspacePaths) {\n // Check if this exact path is indexed\n const exactMatch = indexedDirectoryPaths.includes(workspacePath);\n \n if (exactMatch) {\n // Perfect match - workspace directory is directly indexed\n continue;\n }\n \n // Check if workspace path is covered by a parent directory that is indexed\n const isChildOfIndexed = indexedDirectoryPaths.some(indexedPath => {\n // Workspace path must start with indexed path and be a subdirectory\n return workspacePath.startsWith(indexedPath + '/') || workspacePath.startsWith(indexedPath + '\\\\');\n });\n \n if (isChildOfIndexed) {\n partiallyIndexedPaths.push(workspacePath);\n } else {\n unindexedPaths.push(workspacePath);\n }\n }\n \n // Report unindexed paths\n if (unindexedPaths.length > 0) {\n issues.push(`Workspace directories not indexed: ${unindexedPaths.join(', ')}`);\n recommendations.push(`Run indexing on these directories: ${unindexedPaths.join(', ')}`);\n }\n \n // Report partially indexed paths (where parent directory is indexed)\n if (partiallyIndexedPaths.length > 0) {\n issues.push(`Workspace directories indexed as part of parent directory: ${partiallyIndexedPaths.join(', ')}`);\n recommendations.push('Consider indexing workspace directories directly for better organization');\n }\n \n // Check if workspace is empty (no files found)\n if (isValid && filesCount === 0 && unindexedPaths.length === 0) {\n issues.push('Workspace contains no indexed files');\n recommendations.push('Verify the workspace directory contains files and re-index if necessary');\n }\n \n // Determine overall health status\n let status: 'healthy' | 'warning' | 'error';\n if (!isValid || unindexedPaths.length > 0) {\n status = 'error';\n } else if (issues.length > 0) {\n status = 'warning';\n } else {\n status = 'healthy';\n }\n \n return { status, issues, recommendations };\n}\n\nfunction calculateWorkspaceHealthSummary(workspaces: WorkspaceStatus[]): {\n healthy: number;\n warnings: number; \n errors: number;\n criticalIssues: string[];\n recommendations: string[];\n} {\n let healthy = 0;\n let warnings = 0;\n let errors = 0;\n const criticalIssues: string[] = [];\n const recommendations: string[] = [];\n \n for (const workspace of workspaces) {\n switch (workspace.health.status) {\n case 'healthy':\n healthy++;\n break;\n case 'warning':\n warnings++;\n break;\n case 'error':\n errors++;\n criticalIssues.push(`${workspace.name}: ${workspace.health.issues.join(', ')}`);\n break;\n }\n \n // Collect unique recommendations\n for (const rec of workspace.health.recommendations) {\n if (!recommendations.includes(rec)) {\n recommendations.push(rec);\n }\n }\n }\n \n return {\n healthy,\n warnings,\n errors,\n criticalIssues,\n recommendations\n };\n}\n\nasync function checkQdrantConsistency(sqlite: SQLiteStorage, config: Config): Promise<{ isConsistent: boolean; issues: string[] }> {\n const issues: string[] = [];\n \n try {\n const qdrant = new QdrantClient(config);\n const isHealthy = await qdrant.healthCheck();\n \n if (!isHealthy) {\n issues.push('Qdrant vector database is not running or accessible');\n return { isConsistent: false, issues };\n }\n \n const filesWithChunksStmt = sqlite.db.prepare('SELECT COUNT(*) as count FROM files WHERE chunks_json IS NOT NULL');\n const filesWithChunks = filesWithChunksStmt.get() as { count: number };\n \n const totalChunksStmt = sqlite.db.prepare('SELECT SUM(json_array_length(chunks_json)) as count FROM files WHERE chunks_json IS NOT NULL');\n const totalChunks = totalChunksStmt.get() as { count: number | null };\n \n if (filesWithChunks.count > 0 && (totalChunks.count || 0) === 0) {\n issues.push('Files exist but no chunks found - possible data corruption');\n }\n \n const collectionName = config.storage.qdrantCollection;\n try {\n const response = await fetch(`${config.storage.qdrantEndpoint}/collections/${collectionName}`);\n if (!response.ok) {\n issues.push(`Vector collection '${collectionName}' not found (normal during first-time setup)`);\n return { isConsistent: false, issues };\n }\n \n const collectionInfo = await response.json();\n const qdrantPointCount = collectionInfo.result?.points_count || 0;\n const sqliteChunkCount = totalChunks.count || 0;\n \n if (Math.abs(qdrantPointCount - sqliteChunkCount) > 0) {\n if (qdrantPointCount > sqliteChunkCount) {\n issues.push(`Extra vectors in database: ${qdrantPointCount} vectors vs ${sqliteChunkCount} indexed chunks (normal during cleanup)`);\n } else {\n issues.push(`Missing vectors: ${sqliteChunkCount} indexed chunks vs ${qdrantPointCount} vectors (normal during indexing)`);\n }\n }\n } catch (error) {\n issues.push(`Cannot verify vector database status: ${error}`);\n }\n \n } catch (error) {\n issues.push(`Database status check failed: ${error}`);\n }\n \n return {\n isConsistent: issues.length === 0,\n issues\n };\n}\n\nexport async function getIndexStatus(): Promise<IndexStatus> {\n const config = await import('./config.js').then(m => m.loadConfig());\n const sqlite = new SQLiteStorage(config);\n \n try {\n const directoriesStmt = sqlite.db.prepare('SELECT COUNT(*) as count FROM directories WHERE status = ?');\n const directoriesCount = directoriesStmt.get('completed') as { count: number };\n \n const filesStmt = sqlite.db.prepare('SELECT COUNT(*) as count FROM files');\n const filesCount = filesStmt.get() as { count: number };\n \n const chunksStmt = sqlite.db.prepare('SELECT SUM(json_array_length(chunks_json)) as count FROM files WHERE chunks_json IS NOT NULL');\n const chunksCount = chunksStmt.get() as { count: number | null };\n \n const lastIndexedStmt = sqlite.db.prepare('SELECT MAX(indexed_at) as last_indexed FROM directories WHERE indexed_at > 0');\n const lastIndexedResult = lastIndexedStmt.get() as { last_indexed: number | null };\n \n const errorsStmt = sqlite.db.prepare('SELECT errors_json FROM files WHERE errors_json IS NOT NULL');\n const errorRows = errorsStmt.all() as { errors_json: string }[];\n \n const allErrors: string[] = [];\n errorRows.forEach(row => {\n try {\n const errors = JSON.parse(row.errors_json);\n allErrors.push(...errors);\n } catch {\n allErrors.push('Failed to parse error JSON');\n }\n });\n \n const directoriesDetailStmt = sqlite.db.prepare(`\n SELECT \n d.path,\n d.status,\n d.indexed_at,\n (SELECT COUNT(*) FROM files f WHERE f.path LIKE d.path || '%') as files_count,\n (SELECT COALESCE(SUM(json_array_length(f.chunks_json)), 0) FROM files f WHERE f.path LIKE d.path || '%' AND f.chunks_json IS NOT NULL) as chunks_count\n FROM directories d\n ORDER BY d.indexed_at DESC\n `);\n const directoryDetails = directoriesDetailStmt.all() as { id: number; path: string; status: 'pending' | 'indexing' | 'completed' | 'failed'; indexed_at: number; files_count: number; chunks_count: number }[];\n \n const directories: DirectoryStatus[] = directoryDetails.map(row => {\n const errorsByDirStmt = sqlite.db.prepare(`\n SELECT errors_json FROM files \n WHERE path LIKE ? || '%' AND errors_json IS NOT NULL\n `);\n const dirErrors = errorsByDirStmt.all(row.path) as { errors_json: string }[];\n \n const dirErrorsList: string[] = [];\n dirErrors.forEach(errorRow => {\n try {\n const errors = JSON.parse(errorRow.errors_json);\n dirErrorsList.push(...errors);\n } catch {\n dirErrorsList.push('Failed to parse error JSON');\n }\n });\n \n return {\n path: row.path,\n status: row.status,\n filesCount: row.files_count,\n chunksCount: row.chunks_count,\n lastIndexed: row.indexed_at && row.indexed_at > 0 ? new Date(row.indexed_at).toISOString() : null,\n errors: dirErrorsList\n };\n });\n \n const qdrantConsistency = await checkQdrantConsistency(sqlite, config);\n const workspaces = await calculateWorkspaceStatistics(sqlite, config);\n \n // Calculate workspace health summary\n const workspaceHealth = calculateWorkspaceHealthSummary(workspaces);\n \n const fs = await import('fs');\n let databaseSize = '0 KB';\n try {\n const stats = fs.statSync(config.storage.sqlitePath);\n const sizeInBytes = stats.size;\n if (sizeInBytes > 1024 * 1024) {\n databaseSize = `${(sizeInBytes / (1024 * 1024)).toFixed(2)} MB`;\n } else if (sizeInBytes > 1024) {\n databaseSize = `${(sizeInBytes / 1024).toFixed(2)} KB`;\n } else {\n databaseSize = `${sizeInBytes} bytes`;\n }\n } catch {\n databaseSize = 'Unknown';\n }\n \n return {\n directoriesIndexed: directoriesCount.count,\n filesIndexed: filesCount.count,\n chunksIndexed: chunksCount.count || 0,\n databaseSize,\n lastIndexed: lastIndexedResult.last_indexed ? new Date(lastIndexedResult.last_indexed).toISOString() : null,\n errors: allErrors,\n directories,\n workspaces,\n workspaceHealth,\n qdrantConsistency\n };\n } finally {\n sqlite.close();\n }\n}"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"storage.js","sources":["../src/storage.ts"],"sourcesContent":["import Database from 'better-sqlite3';\nimport { Config } from './config.js';\nimport { FileInfo, ChunkInfo, ensureDirectory } from './utils.js';\nimport { dirname } from 'path';\n\nexport interface DirectoryRecord {\n id: number;\n path: string;\n status: 'pending' | 'indexing' | 'completed' | 'failed';\n indexedAt: Date;\n}\n\nexport interface FileRecord {\n id: number;\n path: string;\n size: number;\n modifiedTime: Date;\n hash: string;\n parentDirs: string[];\n chunks: ChunkInfo[];\n errors?: string[];\n}\n\nexport interface QdrantPoint {\n id: string | number;\n vector: number[];\n payload: {\n filePath: string;\n chunkId: string;\n fileHash: string;\n content: string;\n parentDirectories: string[];\n };\n score?: number;\n}\n\nexport class StorageError extends Error {\n constructor(message: string, public override cause?: Error) {\n super(message);\n this.name = 'StorageError';\n }\n}\n\nexport class QdrantClient {\n constructor(private config: Config) {}\n\n async healthCheck(): Promise<boolean> {\n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/healthz`);\n return response.ok;\n } catch {\n return false;\n }\n }\n\n async createCollection(): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const checkResponse = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`);\n if (checkResponse.ok) {\n return;\n }\n\n const createResponse = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n vectors: {\n size: 768,\n distance: 'Cosine'\n }\n })\n });\n\n if (!createResponse.ok) {\n throw new Error(`Failed to create collection: ${createResponse.statusText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to create Qdrant collection`, error as Error);\n }\n }\n\n async upsertPoints(points: QdrantPoint[]): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ points })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to upsert points: ${response.status} ${response.statusText} - ${errorText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to upsert points to Qdrant`, error as Error);\n }\n }\n\n async searchPoints(vector: number[], limit: number = 10, filter?: Record<string, unknown>): Promise<QdrantPoint[]> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const searchBody: Record<string, unknown> = {\n vector,\n limit,\n with_payload: true\n };\n \n if (filter) {\n searchBody.filter = filter;\n }\n \n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/search`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(searchBody)\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`Failed to search points: ${response.statusText} - ${errorBody}`);\n }\n\n const data = await response.json();\n return data.result.map((item: { id: string | number; vector: number[]; payload: Record<string, unknown>; score: number }) => ({\n id: item.id,\n vector: item.vector,\n payload: item.payload,\n score: item.score\n }));\n } catch (error) {\n throw new StorageError(`Failed to search points in Qdrant`, error as Error);\n }\n }\n\n async deletePoints(ids: (string | number)[]): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/delete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ points: ids })\n });\n\n if (!response.ok) {\n throw new Error(`Failed to delete points: ${response.statusText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to delete points from Qdrant`, error as Error);\n }\n }\n\n async deletePointsByFilePath(filePath: string): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/delete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n filter: {\n must: [\n {\n key: 'filePath',\n match: { value: filePath }\n }\n ]\n }\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to delete points by file path: ${response.status} ${response.statusText} - ${errorText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to delete points by file path from Qdrant`, error as Error);\n }\n }\n\n async countPoints(filter?: Record<string, unknown>): Promise<number> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const countBody: Record<string, unknown> = { exact: true };\n if (filter) {\n countBody.filter = filter;\n }\n\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/count`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(countBody)\n });\n\n if (!response.ok) {\n throw new Error(`Failed to count points: ${response.statusText}`);\n }\n\n const data = await response.json();\n return data.result.count;\n } catch (error) {\n throw new StorageError(`Failed to count points in Qdrant`, error as Error);\n }\n }\n\n async scrollPoints(filter?: Record<string, unknown>, limit: number = 1000): Promise<QdrantPoint[]> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const scrollBody: Record<string, unknown> = {\n limit,\n with_payload: true,\n with_vector: false\n };\n if (filter) {\n scrollBody.filter = filter;\n }\n\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/scroll`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(scrollBody)\n });\n\n if (!response.ok) {\n throw new Error(`Failed to scroll points: ${response.statusText}`);\n }\n\n const data = await response.json();\n return data.result.points.map((item: { id: string | number; payload: Record<string, unknown> }) => ({\n id: item.id,\n vector: [],\n payload: item.payload,\n score: 0\n }));\n } catch (error) {\n throw new StorageError(`Failed to scroll points in Qdrant`, error as Error);\n }\n }\n\n async getCollectionInfo(): Promise<{ vectors_count?: number } | null> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`);\n \n if (!response.ok) {\n if (response.status === 404) {\n return null;\n }\n throw new Error(`Failed to get collection info: ${response.statusText}`);\n }\n\n const data = await response.json();\n return {\n vectors_count: data.result?.points_count || data.result?.vectors_count || 0\n };\n } catch (error) {\n throw new StorageError(`Failed to get collection info from Qdrant`, error as Error);\n }\n }\n\n async deleteCollection(): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`, {\n method: 'DELETE',\n headers: this.config.storage.qdrantApiKey ? {\n 'api-key': this.config.storage.qdrantApiKey\n } : {}\n });\n\n if (!response.ok && response.status !== 404) {\n throw new Error(`Failed to delete collection: ${response.statusText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to delete collection from Qdrant`, error as Error);\n }\n }\n}\n\nexport class SQLiteStorage {\n public db: Database.Database;\n\n constructor(private config: Config) {\n this.db = this.initializeDatabase();\n }\n\n private initializeDatabase(): Database.Database {\n try {\n ensureDirectory(dirname(this.config.storage.sqlitePath));\n \n const db = new Database(this.config.storage.sqlitePath);\n \n db.exec(`\n CREATE TABLE IF NOT EXISTS directories (\n id INTEGER PRIMARY KEY,\n path TEXT UNIQUE NOT NULL,\n status TEXT DEFAULT 'pending',\n indexed_at INTEGER DEFAULT 0\n );\n\n CREATE TABLE IF NOT EXISTS files (\n id INTEGER PRIMARY KEY,\n path TEXT UNIQUE NOT NULL,\n size INTEGER NOT NULL,\n modified_time INTEGER NOT NULL,\n hash TEXT NOT NULL,\n parent_dirs TEXT NOT NULL,\n chunks_json TEXT,\n errors_json TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_files_path ON files(path);\n CREATE INDEX IF NOT EXISTS idx_files_hash ON files(hash);\n CREATE INDEX IF NOT EXISTS idx_directories_path ON directories(path);\n `);\n\n return db;\n } catch (error) {\n throw new StorageError(`Failed to initialize SQLite database`, error as Error);\n }\n }\n\n async getDirectory(path: string): Promise<DirectoryRecord | null> {\n try {\n const stmt = this.db.prepare('SELECT * FROM directories WHERE path = ?');\n const row = stmt.get(path) as { id: number; path: string; status: 'pending' | 'indexing' | 'completed' | 'failed'; indexed_at: number } | undefined;\n \n if (!row) return null;\n \n return {\n id: row.id,\n path: row.path,\n status: row.status,\n indexedAt: new Date(row.indexed_at)\n };\n } catch (error) {\n throw new StorageError(`Failed to get directory record`, error as Error);\n }\n }\n\n async upsertDirectory(path: string, status: DirectoryRecord['status']): Promise<void> {\n try {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO directories (path, status, indexed_at)\n VALUES (?, ?, ?)\n `);\n \n stmt.run(path, status, Date.now());\n } catch (error) {\n throw new StorageError(`Failed to upsert directory record`, error as Error);\n }\n }\n\n async getFile(path: string): Promise<FileRecord | null> {\n try {\n const stmt = this.db.prepare('SELECT * FROM files WHERE path = ?');\n const row = stmt.get(path) as { id: number; path: string; size: number; modified_time: number; hash: string; parent_dirs: string; chunks_json: string | null; errors_json: string | null } | undefined;\n \n if (!row) return null;\n \n return {\n id: row.id,\n path: row.path,\n size: row.size,\n modifiedTime: new Date(row.modified_time * 1000),\n hash: row.hash,\n parentDirs: JSON.parse(row.parent_dirs),\n chunks: row.chunks_json ? JSON.parse(row.chunks_json) : [],\n errors: row.errors_json ? JSON.parse(row.errors_json) : undefined\n };\n } catch (error) {\n throw new StorageError(`Failed to get file record`, error as Error);\n }\n }\n\n async upsertFile(fileInfo: FileInfo, chunks: ChunkInfo[] = [], errors: string[] = []): Promise<void> {\n try {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO files (path, size, modified_time, hash, parent_dirs, chunks_json, errors_json)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n \n stmt.run(\n fileInfo.path,\n fileInfo.size,\n fileInfo.modifiedTime.getTime(),\n fileInfo.hash,\n JSON.stringify(fileInfo.parentDirs),\n chunks.length > 0 ? JSON.stringify(chunks) : null,\n errors.length > 0 ? JSON.stringify(errors) : null\n );\n } catch (error) {\n throw new StorageError(`Failed to upsert file record`, error as Error);\n }\n }\n\n async deleteFile(path: string): Promise<void> {\n try {\n const stmt = this.db.prepare('DELETE FROM files WHERE path = ?');\n stmt.run(path);\n } catch (error) {\n throw new StorageError(`Failed to delete file record`, error as Error);\n }\n }\n\n async getFilesByDirectory(directoryPath: string): Promise<FileRecord[]> {\n try {\n const stmt = this.db.prepare('SELECT * FROM files WHERE path LIKE ?');\n const rows = stmt.all(`${directoryPath}%`) as { id: number; path: string; size: number; modified_time: number; hash: string; parent_dirs: string; chunks_json: string | null; errors_json: string | null }[];\n \n return rows.map(row => ({\n id: row.id,\n path: row.path,\n size: row.size,\n modifiedTime: new Date(row.modified_time * 1000),\n hash: row.hash,\n parentDirs: JSON.parse(row.parent_dirs),\n chunks: row.chunks_json ? JSON.parse(row.chunks_json) : [],\n errors: row.errors_json ? JSON.parse(row.errors_json) : undefined\n }));\n } catch (error) {\n throw new StorageError(`Failed to get files by directory`, error as Error);\n }\n }\n\n close(): void {\n this.db.close();\n }\n}\n\nexport async function initializeStorage(config: Config): Promise<{ sqlite: SQLiteStorage; qdrant: QdrantClient }> {\n const sqlite = new SQLiteStorage(config);\n const qdrant = new QdrantClient(config);\n \n await qdrant.createCollection();\n \n return { sqlite, qdrant };\n}\n\nexport async function initDatabase(dbPath: string): Promise<Database.Database> {\n return new Database(dbPath);\n}\n\nexport interface ResetStats {\n sqliteExists: boolean;\n sqliteSize?: string;\n qdrantCollectionExists: boolean;\n qdrantVectorCount?: number;\n}\n\nexport interface ResetResult {\n sqliteDeleted: boolean;\n qdrantDeleted: boolean;\n warnings: string[];\n}\n\nexport async function getResetPreview(config: Config): Promise<ResetStats> {\n const stats: ResetStats = {\n sqliteExists: false,\n qdrantCollectionExists: false\n };\n\n if (await import('fs').then(fs => fs.existsSync(config.storage.sqlitePath))) {\n stats.sqliteExists = true;\n try {\n const fileStats = await import('fs').then(fs => fs.statSync(config.storage.sqlitePath));\n const sizeInMB = (fileStats.size / (1024 * 1024)).toFixed(1);\n stats.sqliteSize = `${sizeInMB} MB`;\n } catch {\n stats.sqliteSize = 'unknown size';\n }\n }\n\n try {\n const qdrant = new QdrantClient(config);\n const isHealthy = await qdrant.healthCheck();\n \n if (isHealthy) {\n const collectionInfo = await qdrant.getCollectionInfo();\n if (collectionInfo) {\n stats.qdrantCollectionExists = true;\n stats.qdrantVectorCount = collectionInfo.vectors_count || 0;\n }\n }\n } catch {\n // Qdrant unavailable\n }\n\n return stats;\n}\n\nexport async function clearDatabase(config: Config): Promise<boolean> {\n try {\n if (!await import('fs').then(fs => fs.existsSync(config.storage.sqlitePath))) {\n return true; // Already clean\n }\n \n await import('fs/promises').then(fs => fs.unlink(config.storage.sqlitePath));\n return true;\n } catch (error) {\n throw new StorageError(`Failed to delete SQLite database: ${error instanceof Error ? error.message : 'Unknown error'}`, error as Error);\n }\n}\n\nexport async function clearVectorCollection(config: Config): Promise<boolean> {\n try {\n const qdrant = new QdrantClient(config);\n const isHealthy = await qdrant.healthCheck();\n \n if (!isHealthy) {\n throw new StorageError(`Qdrant unavailable at ${config.storage.qdrantEndpoint}`);\n }\n \n const collectionInfo = await qdrant.getCollectionInfo();\n if (collectionInfo) {\n await qdrant.deleteCollection();\n }\n \n return true;\n } catch (error) {\n throw new StorageError(`Failed to reset Qdrant collection: ${error instanceof Error ? error.message : 'Unknown error'}`, error as Error);\n }\n}\n\nexport interface DirectoryStatus {\n path: string;\n status: string;\n filesCount: number;\n chunksCount: number;\n lastIndexed: string | null;\n errors: string[];\n}\n\nexport interface WorkspaceStatus {\n name: string;\n paths: string[];\n isValid: boolean;\n filesCount: number;\n chunksCount: number;\n health: {\n status: 'healthy' | 'warning' | 'error';\n issues: string[];\n recommendations: string[];\n };\n}\n\nexport interface IndexStatus {\n directoriesIndexed: number;\n filesIndexed: number;\n chunksIndexed: number;\n databaseSize: string;\n lastIndexed: string | null;\n errors: string[];\n directories: DirectoryStatus[];\n workspaces: WorkspaceStatus[];\n workspaceHealth: {\n healthy: number;\n warnings: number;\n errors: number;\n criticalIssues: string[];\n recommendations: string[];\n };\n qdrantConsistency: {\n isConsistent: boolean;\n issues: string[];\n };\n}\n\nasync function calculateWorkspaceStatistics(sqlite: SQLiteStorage, config: Config): Promise<WorkspaceStatus[]> {\n const { getAvailableWorkspaces, getWorkspacePaths } = await import('./config.js');\n const workspaces: WorkspaceStatus[] = [];\n \n // Get all indexed directories for comparison\n const directoriesStmt = sqlite.db.prepare('SELECT path, status FROM directories');\n const indexedDirectories = directoriesStmt.all() as { path: string; status: string }[];\n const indexedDirectoryPaths = indexedDirectories.map(d => d.path);\n \n // Get Qdrant client for efficient workspace filtering\n const qdrant = new QdrantClient(config);\n \n for (const workspaceName of getAvailableWorkspaces(config)) {\n const workspacePaths = getWorkspacePaths(config, workspaceName);\n const workspaceConfig = config.workspaces[workspaceName];\n \n let filesCount = 0;\n let chunksCount = 0;\n \n if (workspacePaths.length > 0) {\n try {\n // Build workspace filter using same logic as search\n const workspaceFilter = {\n must: [\n {\n key: \"parentDirectories\",\n match: { any: workspacePaths }\n }\n ]\n };\n \n // Get chunk count efficiently using Qdrant\n chunksCount = await qdrant.countPoints(workspaceFilter);\n \n // Get unique file paths using Qdrant scroll\n const points = await qdrant.scrollPoints(workspaceFilter, 10000);\n const uniqueFilePaths = new Set(points.map(p => p.payload.filePath));\n filesCount = uniqueFilePaths.size;\n \n } catch {\n // Fallback to 0 if Qdrant is unavailable\n filesCount = 0;\n chunksCount = 0;\n }\n }\n \n // Check workspace health\n const health = analyzeWorkspaceHealth(workspacePaths, workspaceConfig.isValid, filesCount, indexedDirectoryPaths);\n \n workspaces.push({\n name: workspaceName,\n paths: workspacePaths,\n isValid: workspaceConfig.isValid,\n filesCount,\n chunksCount,\n health\n });\n }\n \n return workspaces;\n}\n\nfunction analyzeWorkspaceHealth(\n workspacePaths: string[], \n isValid: boolean, \n filesCount: number, \n indexedDirectoryPaths: string[]\n): { status: 'healthy' | 'warning' | 'error'; issues: string[]; recommendations: string[] } {\n const issues: string[] = [];\n const recommendations: string[] = [];\n \n // Check if workspace paths exist and are valid\n if (!isValid) {\n issues.push('One or more workspace directories do not exist on the filesystem');\n recommendations.push('Verify workspace directory paths exist and are accessible');\n }\n \n // Check if workspace paths are indexed\n const unindexedPaths: string[] = [];\n const partiallyIndexedPaths: string[] = [];\n \n for (const workspacePath of workspacePaths) {\n // Check if this exact path is indexed\n const exactMatch = indexedDirectoryPaths.includes(workspacePath);\n \n if (exactMatch) {\n // Perfect match - workspace directory is directly indexed\n continue;\n }\n \n // Check if workspace path is covered by a parent directory that is indexed\n const isChildOfIndexed = indexedDirectoryPaths.some(indexedPath => {\n // Workspace path must start with indexed path and be a subdirectory\n return workspacePath.startsWith(indexedPath + '/') || workspacePath.startsWith(indexedPath + '\\\\');\n });\n \n if (isChildOfIndexed) {\n partiallyIndexedPaths.push(workspacePath);\n } else {\n unindexedPaths.push(workspacePath);\n }\n }\n \n // Report unindexed paths\n if (unindexedPaths.length > 0) {\n issues.push(`Workspace directories not indexed: ${unindexedPaths.join(', ')}`);\n recommendations.push(`Run indexing on these directories: ${unindexedPaths.join(', ')}`);\n }\n \n // Report partially indexed paths (where parent directory is indexed)\n if (partiallyIndexedPaths.length > 0) {\n issues.push(`Workspace directories indexed as part of parent directory: ${partiallyIndexedPaths.join(', ')}`);\n recommendations.push('Consider indexing workspace directories directly for better organization');\n }\n \n // Check if workspace is empty (no files found)\n if (isValid && filesCount === 0 && unindexedPaths.length === 0) {\n issues.push('Workspace contains no indexed files');\n recommendations.push('Verify the workspace directory contains files and re-index if necessary');\n }\n \n // Determine overall health status\n let status: 'healthy' | 'warning' | 'error';\n if (!isValid || unindexedPaths.length > 0) {\n status = 'error';\n } else if (issues.length > 0) {\n status = 'warning';\n } else {\n status = 'healthy';\n }\n \n return { status, issues, recommendations };\n}\n\nfunction calculateWorkspaceHealthSummary(workspaces: WorkspaceStatus[]): {\n healthy: number;\n warnings: number; \n errors: number;\n criticalIssues: string[];\n recommendations: string[];\n} {\n let healthy = 0;\n let warnings = 0;\n let errors = 0;\n const criticalIssues: string[] = [];\n const recommendations: string[] = [];\n \n for (const workspace of workspaces) {\n switch (workspace.health.status) {\n case 'healthy':\n healthy++;\n break;\n case 'warning':\n warnings++;\n break;\n case 'error':\n errors++;\n criticalIssues.push(`${workspace.name}: ${workspace.health.issues.join(', ')}`);\n break;\n }\n \n // Collect unique recommendations\n for (const rec of workspace.health.recommendations) {\n if (!recommendations.includes(rec)) {\n recommendations.push(rec);\n }\n }\n }\n \n return {\n healthy,\n warnings,\n errors,\n criticalIssues,\n recommendations\n };\n}\n\nasync function checkQdrantConsistency(sqlite: SQLiteStorage, config: Config): Promise<{ isConsistent: boolean; issues: string[] }> {\n const issues: string[] = [];\n \n try {\n const qdrant = new QdrantClient(config);\n const isHealthy = await qdrant.healthCheck();\n \n if (!isHealthy) {\n issues.push('Qdrant vector database is not running or accessible');\n return { isConsistent: false, issues };\n }\n \n const filesWithChunksStmt = sqlite.db.prepare('SELECT COUNT(*) as count FROM files WHERE chunks_json IS NOT NULL');\n const filesWithChunks = filesWithChunksStmt.get() as { count: number };\n \n const totalChunksStmt = sqlite.db.prepare('SELECT SUM(json_array_length(chunks_json)) as count FROM files WHERE chunks_json IS NOT NULL');\n const totalChunks = totalChunksStmt.get() as { count: number | null };\n \n if (filesWithChunks.count > 0 && (totalChunks.count || 0) === 0) {\n issues.push('Files exist but no chunks found - possible data corruption');\n }\n \n const collectionName = config.storage.qdrantCollection;\n try {\n const response = await fetch(`${config.storage.qdrantEndpoint}/collections/${collectionName}`);\n if (!response.ok) {\n issues.push(`Vector collection '${collectionName}' not found (normal during first-time setup)`);\n return { isConsistent: false, issues };\n }\n \n const collectionInfo = await response.json();\n const qdrantPointCount = collectionInfo.result?.points_count || 0;\n const sqliteChunkCount = totalChunks.count || 0;\n \n if (Math.abs(qdrantPointCount - sqliteChunkCount) > 0) {\n if (qdrantPointCount > sqliteChunkCount) {\n issues.push(`Extra vectors in database: ${qdrantPointCount} vectors vs ${sqliteChunkCount} indexed chunks (normal during cleanup)`);\n } else {\n issues.push(`Missing vectors: ${sqliteChunkCount} indexed chunks vs ${qdrantPointCount} vectors (normal during indexing)`);\n }\n }\n } catch (error) {\n issues.push(`Cannot verify vector database status: ${error}`);\n }\n \n } catch (error) {\n issues.push(`Database status check failed: ${error}`);\n }\n \n return {\n isConsistent: issues.length === 0,\n issues\n };\n}\n\nexport async function getIndexStatus(): Promise<IndexStatus> {\n const config = await import('./config.js').then(m => m.loadConfig());\n const sqlite = new SQLiteStorage(config);\n \n try {\n const directoriesStmt = sqlite.db.prepare('SELECT COUNT(*) as count FROM directories WHERE status = ?');\n const directoriesCount = directoriesStmt.get('completed') as { count: number };\n \n const filesStmt = sqlite.db.prepare('SELECT COUNT(*) as count FROM files');\n const filesCount = filesStmt.get() as { count: number };\n \n const chunksStmt = sqlite.db.prepare('SELECT SUM(json_array_length(chunks_json)) as count FROM files WHERE chunks_json IS NOT NULL');\n const chunksCount = chunksStmt.get() as { count: number | null };\n \n const lastIndexedStmt = sqlite.db.prepare('SELECT MAX(indexed_at) as last_indexed FROM directories WHERE indexed_at > 0');\n const lastIndexedResult = lastIndexedStmt.get() as { last_indexed: number | null };\n \n const errorsStmt = sqlite.db.prepare('SELECT errors_json FROM files WHERE errors_json IS NOT NULL');\n const errorRows = errorsStmt.all() as { errors_json: string }[];\n \n const allErrors: string[] = [];\n errorRows.forEach(row => {\n try {\n const errors = JSON.parse(row.errors_json);\n allErrors.push(...errors);\n } catch {\n allErrors.push('Failed to parse error JSON');\n }\n });\n \n const directoriesDetailStmt = sqlite.db.prepare(`\n SELECT \n d.path,\n d.status,\n d.indexed_at,\n (SELECT COUNT(*) FROM files f WHERE f.path LIKE d.path || '%') as files_count,\n (SELECT COALESCE(SUM(json_array_length(f.chunks_json)), 0) FROM files f WHERE f.path LIKE d.path || '%' AND f.chunks_json IS NOT NULL) as chunks_count\n FROM directories d\n ORDER BY d.indexed_at DESC\n `);\n const directoryDetails = directoriesDetailStmt.all() as { id: number; path: string; status: 'pending' | 'indexing' | 'completed' | 'failed'; indexed_at: number; files_count: number; chunks_count: number }[];\n \n const directories: DirectoryStatus[] = directoryDetails.map(row => {\n const errorsByDirStmt = sqlite.db.prepare(`\n SELECT errors_json FROM files \n WHERE path LIKE ? || '%' AND errors_json IS NOT NULL\n `);\n const dirErrors = errorsByDirStmt.all(row.path) as { errors_json: string }[];\n \n const dirErrorsList: string[] = [];\n dirErrors.forEach(errorRow => {\n try {\n const errors = JSON.parse(errorRow.errors_json);\n dirErrorsList.push(...errors);\n } catch {\n dirErrorsList.push('Failed to parse error JSON');\n }\n });\n \n return {\n path: row.path,\n status: row.status,\n filesCount: row.files_count,\n chunksCount: row.chunks_count,\n lastIndexed: row.indexed_at && row.indexed_at > 0 ? new Date(row.indexed_at).toISOString() : null,\n errors: dirErrorsList\n };\n });\n \n const qdrantConsistency = await checkQdrantConsistency(sqlite, config);\n const workspaces = await calculateWorkspaceStatistics(sqlite, config);\n \n // Calculate workspace health summary\n const workspaceHealth = calculateWorkspaceHealthSummary(workspaces);\n \n const fs = await import('fs');\n let databaseSize = '0 KB';\n try {\n const stats = fs.statSync(config.storage.sqlitePath);\n const sizeInBytes = stats.size;\n if (sizeInBytes > 1024 * 1024) {\n databaseSize = `${(sizeInBytes / (1024 * 1024)).toFixed(2)} MB`;\n } else if (sizeInBytes > 1024) {\n databaseSize = `${(sizeInBytes / 1024).toFixed(2)} KB`;\n } else {\n databaseSize = `${sizeInBytes} bytes`;\n }\n } catch {\n databaseSize = 'Unknown';\n }\n \n return {\n directoriesIndexed: directoriesCount.count,\n filesIndexed: filesCount.count,\n chunksIndexed: chunksCount.count || 0,\n databaseSize,\n lastIndexed: lastIndexedResult.last_indexed ? new Date(lastIndexedResult.last_indexed).toISOString() : null,\n errors: allErrors,\n directories,\n workspaces,\n workspaceHealth,\n qdrantConsistency\n };\n } finally {\n sqlite.close();\n }\n}"],"names":[],"mappings":"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"}
package/dist/utils.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "crypto";
2
2
  import { promises } from "fs";
3
3
  import { normalize, resolve, sep } from "path";
4
+ import { createInterface } from "readline";
4
5
  class FileError extends Error {
5
6
  constructor(message, filePath, cause) {
6
7
  super(message);
@@ -109,6 +110,18 @@ function isSupportedFileType(filePath) {
109
110
  ];
110
111
  return supportedExtensions.some((ext) => filePath.toLowerCase().endsWith(ext));
111
112
  }
113
+ async function readlineSync(prompt) {
114
+ const rl = createInterface({
115
+ input: process.stdin,
116
+ output: process.stdout
117
+ });
118
+ return new Promise((resolve2) => {
119
+ rl.question(prompt, (answer) => {
120
+ rl.close();
121
+ resolve2(answer);
122
+ });
123
+ });
124
+ }
112
125
  export {
113
126
  FileError,
114
127
  calculateHash,
@@ -121,6 +134,7 @@ export {
121
134
  isFile,
122
135
  isSupportedFileType,
123
136
  normalizePath,
137
+ readlineSync,
124
138
  shouldIgnoreFile
125
139
  };
126
140
  //# sourceMappingURL=utils.js.map
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sources":["../src/utils.ts"],"sourcesContent":["import { createHash } from 'crypto';\nimport { promises as fs } from 'fs';\nimport { resolve, normalize, sep } from 'path';\n\nexport interface FileInfo {\n path: string;\n size: number;\n modifiedTime: Date;\n hash: string;\n parentDirs: string[];\n}\n\nexport interface ChunkInfo {\n id: string;\n content: string;\n startIndex: number;\n endIndex: number;\n}\n\nexport class FileError extends Error {\n constructor(message: string, public filePath: string, public override cause?: Error) {\n super(message);\n this.name = 'FileError';\n }\n}\n\nexport function normalizePath(path: string): string {\n return normalize(resolve(path));\n}\n\nexport function getParentDirectories(filePath: string): string[] {\n const normalizedPath = normalizePath(filePath);\n const parts = normalizedPath.split(sep);\n const parents: string[] = [];\n \n for (let i = 1; i < parts.length; i++) {\n parents.push(parts.slice(0, i + 1).join(sep));\n }\n \n return parents;\n}\n\nexport async function getFileHash(filePath: string): Promise<string> {\n try {\n const content = await fs.readFile(filePath);\n return createHash('sha256').update(content).digest('hex');\n } catch (error) {\n throw new FileError(`Failed to hash file`, filePath, error as Error);\n }\n}\n\nexport function calculateHash(content: string): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\nexport async function getFileInfo(filePath: string): Promise<FileInfo> {\n try {\n const stats = await fs.stat(filePath);\n const hash = await getFileHash(filePath);\n const parentDirs = getParentDirectories(filePath);\n \n return {\n path: normalizePath(filePath),\n size: stats.size,\n modifiedTime: stats.mtime,\n hash,\n parentDirs,\n };\n } catch (error) {\n throw new FileError(`Failed to get file info`, filePath, error as Error);\n }\n}\n\nexport async function isDirectory(path: string): Promise<boolean> {\n try {\n const stats = await fs.stat(path);\n return stats.isDirectory();\n } catch {\n return false;\n }\n}\n\nexport async function isFile(path: string): Promise<boolean> {\n try {\n const stats = await fs.stat(path);\n return stats.isFile();\n } catch {\n return false;\n }\n}\n\nexport async function fileExists(path: string): Promise<boolean> {\n try {\n await fs.access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function ensureDirectory(dirPath: string): Promise<void> {\n try {\n await fs.mkdir(dirPath, { recursive: true });\n } catch (error) {\n throw new FileError(`Failed to create directory`, dirPath, error as Error);\n }\n}\n\nexport function shouldIgnoreFile(filePath: string, ignorePatterns: string[]): boolean {\n const normalizedPath = normalizePath(filePath);\n return ignorePatterns.some(pattern => normalizedPath.includes(pattern));\n}\n\nexport function isSupportedFileType(filePath: string): boolean {\n const supportedExtensions = [\n '.md', '.txt', '.rst',\n '.rs', '.py', '.js', '.ts', '.go', '.java', '.cpp', '.c',\n '.json', '.yaml', '.yml', '.toml', '.csv',\n '.env', '.conf', '.ini',\n '.html', '.xml'\n ];\n \n return supportedExtensions.some(ext => filePath.toLowerCase().endsWith(ext));\n}"],"names":["fs"],"mappings":";;;AAmBO,MAAM,kBAAkB,MAAM;AAAA,EACnC,YAAY,SAAwB,UAAkC,OAAe;AACnF,UAAM,OAAO;AADqB,SAAA,WAAA;AAAkC,SAAA,QAAA;AAEpE,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,cAAc,MAAsB;AAClD,SAAO,UAAU,QAAQ,IAAI,CAAC;AAChC;AAEO,SAAS,qBAAqB,UAA4B;AAC/D,QAAM,iBAAiB,cAAc,QAAQ;AAC7C,QAAM,QAAQ,eAAe,MAAM,GAAG;AACtC,QAAM,UAAoB,CAAA;AAE1B,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAQ,KAAK,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,KAAK,GAAG,CAAC;AAAA,EAC9C;AAEA,SAAO;AACT;AAEA,eAAsB,YAAY,UAAmC;AACnE,MAAI;AACF,UAAM,UAAU,MAAMA,SAAG,SAAS,QAAQ;AAC1C,WAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAAA,EAC1D,SAAS,OAAO;AACd,UAAM,IAAI,UAAU,uBAAuB,UAAU,KAAc;AAAA,EACrE;AACF;AAEO,SAAS,cAAc,SAAyB;AACrD,SAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAC1D;AAEA,eAAsB,YAAY,UAAqC;AACrE,MAAI;AACF,UAAM,QAAQ,MAAMA,SAAG,KAAK,QAAQ;AACpC,UAAM,OAAO,MAAM,YAAY,QAAQ;AACvC,UAAM,aAAa,qBAAqB,QAAQ;AAEhD,WAAO;AAAA,MACL,MAAM,cAAc,QAAQ;AAAA,MAC5B,MAAM,MAAM;AAAA,MACZ,cAAc,MAAM;AAAA,MACpB;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ,SAAS,OAAO;AACd,UAAM,IAAI,UAAU,2BAA2B,UAAU,KAAc;AAAA,EACzE;AACF;AAEA,eAAsB,YAAY,MAAgC;AAChE,MAAI;AACF,UAAM,QAAQ,MAAMA,SAAG,KAAK,IAAI;AAChC,WAAO,MAAM,YAAA;AAAA,EACf,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,OAAO,MAAgC;AAC3D,MAAI;AACF,UAAM,QAAQ,MAAMA,SAAG,KAAK,IAAI;AAChC,WAAO,MAAM,OAAA;AAAA,EACf,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,WAAW,MAAgC;AAC/D,MAAI;AACF,UAAMA,SAAG,OAAO,IAAI;AACpB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,gBAAgB,SAAgC;AACpE,MAAI;AACF,UAAMA,SAAG,MAAM,SAAS,EAAE,WAAW,MAAM;AAAA,EAC7C,SAAS,OAAO;AACd,UAAM,IAAI,UAAU,8BAA8B,SAAS,KAAc;AAAA,EAC3E;AACF;AAEO,SAAS,iBAAiB,UAAkB,gBAAmC;AACpF,QAAM,iBAAiB,cAAc,QAAQ;AAC7C,SAAO,eAAe,KAAK,CAAA,YAAW,eAAe,SAAS,OAAO,CAAC;AACxE;AAEO,SAAS,oBAAoB,UAA2B;AAC7D,QAAM,sBAAsB;AAAA,IAC1B;AAAA,IAAO;AAAA,IAAQ;AAAA,IACf;AAAA,IAAO;AAAA,IAAO;AAAA,IAAO;AAAA,IAAO;AAAA,IAAO;AAAA,IAAS;AAAA,IAAQ;AAAA,IACpD;AAAA,IAAS;AAAA,IAAS;AAAA,IAAQ;AAAA,IAAS;AAAA,IACnC;AAAA,IAAQ;AAAA,IAAS;AAAA,IACjB;AAAA,IAAS;AAAA,EAAA;AAGX,SAAO,oBAAoB,KAAK,CAAA,QAAO,SAAS,cAAc,SAAS,GAAG,CAAC;AAC7E;"}
1
+ {"version":3,"file":"utils.js","sources":["../src/utils.ts"],"sourcesContent":["import { createHash } from 'crypto';\nimport { promises as fs } from 'fs';\nimport { resolve, normalize, sep } from 'path';\nimport { createInterface } from 'readline';\n\nexport interface FileInfo {\n path: string;\n size: number;\n modifiedTime: Date;\n hash: string;\n parentDirs: string[];\n}\n\nexport interface ChunkInfo {\n id: string;\n content: string;\n startIndex: number;\n endIndex: number;\n}\n\nexport class FileError extends Error {\n constructor(message: string, public filePath: string, public override cause?: Error) {\n super(message);\n this.name = 'FileError';\n }\n}\n\nexport function normalizePath(path: string): string {\n return normalize(resolve(path));\n}\n\nexport function getParentDirectories(filePath: string): string[] {\n const normalizedPath = normalizePath(filePath);\n const parts = normalizedPath.split(sep);\n const parents: string[] = [];\n \n for (let i = 1; i < parts.length; i++) {\n parents.push(parts.slice(0, i + 1).join(sep));\n }\n \n return parents;\n}\n\nexport async function getFileHash(filePath: string): Promise<string> {\n try {\n const content = await fs.readFile(filePath);\n return createHash('sha256').update(content).digest('hex');\n } catch (error) {\n throw new FileError(`Failed to hash file`, filePath, error as Error);\n }\n}\n\nexport function calculateHash(content: string): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\nexport async function getFileInfo(filePath: string): Promise<FileInfo> {\n try {\n const stats = await fs.stat(filePath);\n const hash = await getFileHash(filePath);\n const parentDirs = getParentDirectories(filePath);\n \n return {\n path: normalizePath(filePath),\n size: stats.size,\n modifiedTime: stats.mtime,\n hash,\n parentDirs,\n };\n } catch (error) {\n throw new FileError(`Failed to get file info`, filePath, error as Error);\n }\n}\n\nexport async function isDirectory(path: string): Promise<boolean> {\n try {\n const stats = await fs.stat(path);\n return stats.isDirectory();\n } catch {\n return false;\n }\n}\n\nexport async function isFile(path: string): Promise<boolean> {\n try {\n const stats = await fs.stat(path);\n return stats.isFile();\n } catch {\n return false;\n }\n}\n\nexport async function fileExists(path: string): Promise<boolean> {\n try {\n await fs.access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function ensureDirectory(dirPath: string): Promise<void> {\n try {\n await fs.mkdir(dirPath, { recursive: true });\n } catch (error) {\n throw new FileError(`Failed to create directory`, dirPath, error as Error);\n }\n}\n\nexport function shouldIgnoreFile(filePath: string, ignorePatterns: string[]): boolean {\n const normalizedPath = normalizePath(filePath);\n return ignorePatterns.some(pattern => normalizedPath.includes(pattern));\n}\n\nexport function isSupportedFileType(filePath: string): boolean {\n const supportedExtensions = [\n '.md', '.txt', '.rst',\n '.rs', '.py', '.js', '.ts', '.go', '.java', '.cpp', '.c',\n '.json', '.yaml', '.yml', '.toml', '.csv',\n '.env', '.conf', '.ini',\n '.html', '.xml'\n ];\n \n return supportedExtensions.some(ext => filePath.toLowerCase().endsWith(ext));\n}\n\nexport async function readlineSync(prompt: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout\n });\n\n return new Promise((resolve) => {\n rl.question(prompt, (answer) => {\n rl.close();\n resolve(answer);\n });\n });\n}"],"names":["fs","resolve"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "directory-indexer",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "description": "AI-powered directory indexing with semantic search for MCP servers",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {