glancey 2.0.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/LICENSE +21 -0
- package/README.md +435 -0
- package/dist/__tests__/ast-chunker.test.d.ts +2 -0
- package/dist/__tests__/ast-chunker.test.d.ts.map +1 -0
- package/dist/__tests__/ast-chunker.test.js +307 -0
- package/dist/__tests__/ast-chunker.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +2 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +242 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/dashboard/beads.test.d.ts +2 -0
- package/dist/__tests__/dashboard/beads.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard/beads.test.js +151 -0
- package/dist/__tests__/dashboard/beads.test.js.map +1 -0
- package/dist/__tests__/dashboard/index.test.d.ts +2 -0
- package/dist/__tests__/dashboard/index.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard/index.test.js +116 -0
- package/dist/__tests__/dashboard/index.test.js.map +1 -0
- package/dist/__tests__/dashboard/routes.test.d.ts +2 -0
- package/dist/__tests__/dashboard/routes.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard/routes.test.js +125 -0
- package/dist/__tests__/dashboard/routes.test.js.map +1 -0
- package/dist/__tests__/dashboard/server.test.d.ts +2 -0
- package/dist/__tests__/dashboard/server.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard/server.test.js +75 -0
- package/dist/__tests__/dashboard/server.test.js.map +1 -0
- package/dist/__tests__/dashboard/state.test.d.ts +2 -0
- package/dist/__tests__/dashboard/state.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard/state.test.js +124 -0
- package/dist/__tests__/dashboard/state.test.js.map +1 -0
- package/dist/__tests__/dashboard/token-tracking.test.d.ts +2 -0
- package/dist/__tests__/dashboard/token-tracking.test.d.ts.map +1 -0
- package/dist/__tests__/dashboard/token-tracking.test.js +315 -0
- package/dist/__tests__/dashboard/token-tracking.test.js.map +1 -0
- package/dist/__tests__/embeddings/factory.test.d.ts +2 -0
- package/dist/__tests__/embeddings/factory.test.d.ts.map +1 -0
- package/dist/__tests__/embeddings/factory.test.js +157 -0
- package/dist/__tests__/embeddings/factory.test.js.map +1 -0
- package/dist/__tests__/embeddings/gemini.test.d.ts +2 -0
- package/dist/__tests__/embeddings/gemini.test.d.ts.map +1 -0
- package/dist/__tests__/embeddings/gemini.test.js +178 -0
- package/dist/__tests__/embeddings/gemini.test.js.map +1 -0
- package/dist/__tests__/embeddings/ollama.test.d.ts +2 -0
- package/dist/__tests__/embeddings/ollama.test.d.ts.map +1 -0
- package/dist/__tests__/embeddings/ollama.test.js +273 -0
- package/dist/__tests__/embeddings/ollama.test.js.map +1 -0
- package/dist/__tests__/embeddings/rate-limiter.test.d.ts +2 -0
- package/dist/__tests__/embeddings/rate-limiter.test.d.ts.map +1 -0
- package/dist/__tests__/embeddings/rate-limiter.test.js +163 -0
- package/dist/__tests__/embeddings/rate-limiter.test.js.map +1 -0
- package/dist/__tests__/embeddings/retry.test.d.ts +2 -0
- package/dist/__tests__/embeddings/retry.test.d.ts.map +1 -0
- package/dist/__tests__/embeddings/retry.test.js +334 -0
- package/dist/__tests__/embeddings/retry.test.js.map +1 -0
- package/dist/__tests__/embeddings/types.test.d.ts +2 -0
- package/dist/__tests__/embeddings/types.test.d.ts.map +1 -0
- package/dist/__tests__/embeddings/types.test.js +31 -0
- package/dist/__tests__/embeddings/types.test.js.map +1 -0
- package/dist/__tests__/mocks/embedding-backend.mock.d.ts +10 -0
- package/dist/__tests__/mocks/embedding-backend.mock.d.ts.map +1 -0
- package/dist/__tests__/mocks/embedding-backend.mock.js +39 -0
- package/dist/__tests__/mocks/embedding-backend.mock.js.map +1 -0
- package/dist/__tests__/mocks/fetch.mock.d.ts +49 -0
- package/dist/__tests__/mocks/fetch.mock.d.ts.map +1 -0
- package/dist/__tests__/mocks/fetch.mock.js +102 -0
- package/dist/__tests__/mocks/fetch.mock.js.map +1 -0
- package/dist/__tests__/mocks/lancedb.mock.d.ts +38 -0
- package/dist/__tests__/mocks/lancedb.mock.d.ts.map +1 -0
- package/dist/__tests__/mocks/lancedb.mock.js +63 -0
- package/dist/__tests__/mocks/lancedb.mock.js.map +1 -0
- package/dist/__tests__/search/clustering.test.d.ts +2 -0
- package/dist/__tests__/search/clustering.test.d.ts.map +1 -0
- package/dist/__tests__/search/clustering.test.js +230 -0
- package/dist/__tests__/search/clustering.test.js.map +1 -0
- package/dist/__tests__/search/hybrid-search.test.d.ts +2 -0
- package/dist/__tests__/search/hybrid-search.test.d.ts.map +1 -0
- package/dist/__tests__/search/hybrid-search.test.js +279 -0
- package/dist/__tests__/search/hybrid-search.test.js.map +1 -0
- package/dist/__tests__/search/indexer.test.d.ts +2 -0
- package/dist/__tests__/search/indexer.test.d.ts.map +1 -0
- package/dist/__tests__/search/indexer.test.js +1305 -0
- package/dist/__tests__/search/indexer.test.js.map +1 -0
- package/dist/__tests__/search/tree-sitter-chunker.test.d.ts +2 -0
- package/dist/__tests__/search/tree-sitter-chunker.test.d.ts.map +1 -0
- package/dist/__tests__/search/tree-sitter-chunker.test.js +228 -0
- package/dist/__tests__/search/tree-sitter-chunker.test.js.map +1 -0
- package/dist/__tests__/setup.d.ts +2 -0
- package/dist/__tests__/setup.d.ts.map +1 -0
- package/dist/__tests__/setup.js +11 -0
- package/dist/__tests__/setup.js.map +1 -0
- package/dist/__tests__/tools/clustering-handlers.test.d.ts +2 -0
- package/dist/__tests__/tools/clustering-handlers.test.d.ts.map +1 -0
- package/dist/__tests__/tools/clustering-handlers.test.js +286 -0
- package/dist/__tests__/tools/clustering-handlers.test.js.map +1 -0
- package/dist/__tests__/tools/commit-handlers.test.d.ts +2 -0
- package/dist/__tests__/tools/commit-handlers.test.d.ts.map +1 -0
- package/dist/__tests__/tools/commit-handlers.test.js +153 -0
- package/dist/__tests__/tools/commit-handlers.test.js.map +1 -0
- package/dist/__tests__/tools/index-handlers.test.d.ts +2 -0
- package/dist/__tests__/tools/index-handlers.test.d.ts.map +1 -0
- package/dist/__tests__/tools/index-handlers.test.js +184 -0
- package/dist/__tests__/tools/index-handlers.test.js.map +1 -0
- package/dist/__tests__/tools/instructions-handlers.test.d.ts +2 -0
- package/dist/__tests__/tools/instructions-handlers.test.d.ts.map +1 -0
- package/dist/__tests__/tools/instructions-handlers.test.js +53 -0
- package/dist/__tests__/tools/instructions-handlers.test.js.map +1 -0
- package/dist/__tests__/tools/memory-handlers.test.d.ts +2 -0
- package/dist/__tests__/tools/memory-handlers.test.d.ts.map +1 -0
- package/dist/__tests__/tools/memory-handlers.test.js +175 -0
- package/dist/__tests__/tools/memory-handlers.test.js.map +1 -0
- package/dist/__tests__/tools/search-handlers.test.d.ts +2 -0
- package/dist/__tests__/tools/search-handlers.test.d.ts.map +1 -0
- package/dist/__tests__/tools/search-handlers.test.js +229 -0
- package/dist/__tests__/tools/search-handlers.test.js.map +1 -0
- package/dist/__tests__/tools/symbol-handlers.test.d.ts +2 -0
- package/dist/__tests__/tools/symbol-handlers.test.d.ts.map +1 -0
- package/dist/__tests__/tools/symbol-handlers.test.js +348 -0
- package/dist/__tests__/tools/symbol-handlers.test.js.map +1 -0
- package/dist/__tests__/tools/worktree-handlers.test.d.ts +2 -0
- package/dist/__tests__/tools/worktree-handlers.test.d.ts.map +1 -0
- package/dist/__tests__/tools/worktree-handlers.test.js +297 -0
- package/dist/__tests__/tools/worktree-handlers.test.js.map +1 -0
- package/dist/__tests__/utils/cache.test.d.ts +2 -0
- package/dist/__tests__/utils/cache.test.d.ts.map +1 -0
- package/dist/__tests__/utils/cache.test.js +147 -0
- package/dist/__tests__/utils/cache.test.js.map +1 -0
- package/dist/__tests__/utils/concurrency.test.d.ts +2 -0
- package/dist/__tests__/utils/concurrency.test.d.ts.map +1 -0
- package/dist/__tests__/utils/concurrency.test.js +83 -0
- package/dist/__tests__/utils/concurrency.test.js.map +1 -0
- package/dist/__tests__/utils/errors.test.d.ts +2 -0
- package/dist/__tests__/utils/errors.test.d.ts.map +1 -0
- package/dist/__tests__/utils/errors.test.js +136 -0
- package/dist/__tests__/utils/errors.test.js.map +1 -0
- package/dist/__tests__/utils/logger.test.d.ts +2 -0
- package/dist/__tests__/utils/logger.test.d.ts.map +1 -0
- package/dist/__tests__/utils/logger.test.js +131 -0
- package/dist/__tests__/utils/logger.test.js.map +1 -0
- package/dist/__tests__/utils/type-guards.test.d.ts +2 -0
- package/dist/__tests__/utils/type-guards.test.d.ts.map +1 -0
- package/dist/__tests__/utils/type-guards.test.js +80 -0
- package/dist/__tests__/utils/type-guards.test.js.map +1 -0
- package/dist/__tests__/worktree/worktree-manager.test.d.ts +2 -0
- package/dist/__tests__/worktree/worktree-manager.test.d.ts.map +1 -0
- package/dist/__tests__/worktree/worktree-manager.test.js +403 -0
- package/dist/__tests__/worktree/worktree-manager.test.js.map +1 -0
- package/dist/config.d.ts +191 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +788 -0
- package/dist/config.js.map +1 -0
- package/dist/dashboard/beads.d.ts +35 -0
- package/dist/dashboard/beads.d.ts.map +1 -0
- package/dist/dashboard/beads.js +102 -0
- package/dist/dashboard/beads.js.map +1 -0
- package/dist/dashboard/events.d.ts +64 -0
- package/dist/dashboard/events.d.ts.map +1 -0
- package/dist/dashboard/events.js +211 -0
- package/dist/dashboard/events.js.map +1 -0
- package/dist/dashboard/index.d.ts +69 -0
- package/dist/dashboard/index.d.ts.map +1 -0
- package/dist/dashboard/index.js +93 -0
- package/dist/dashboard/index.js.map +1 -0
- package/dist/dashboard/routes.d.ts +6 -0
- package/dist/dashboard/routes.d.ts.map +1 -0
- package/dist/dashboard/routes.js +505 -0
- package/dist/dashboard/routes.js.map +1 -0
- package/dist/dashboard/server.d.ts +27 -0
- package/dist/dashboard/server.d.ts.map +1 -0
- package/dist/dashboard/server.js +72 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/dashboard/state.d.ts +172 -0
- package/dist/dashboard/state.d.ts.map +1 -0
- package/dist/dashboard/state.js +362 -0
- package/dist/dashboard/state.js.map +1 -0
- package/dist/dashboard/token-tracking.d.ts +143 -0
- package/dist/dashboard/token-tracking.d.ts.map +1 -0
- package/dist/dashboard/token-tracking.js +363 -0
- package/dist/dashboard/token-tracking.js.map +1 -0
- package/dist/dashboard/ui.d.ts +6 -0
- package/dist/dashboard/ui.d.ts.map +1 -0
- package/dist/dashboard/ui.js +2710 -0
- package/dist/dashboard/ui.js.map +1 -0
- package/dist/embeddings/gemini.d.ts +28 -0
- package/dist/embeddings/gemini.d.ts.map +1 -0
- package/dist/embeddings/gemini.js +165 -0
- package/dist/embeddings/gemini.js.map +1 -0
- package/dist/embeddings/index.d.ts +34 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +120 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/embeddings/ollama.d.ts +22 -0
- package/dist/embeddings/ollama.d.ts.map +1 -0
- package/dist/embeddings/ollama.js +179 -0
- package/dist/embeddings/ollama.js.map +1 -0
- package/dist/embeddings/rate-limiter.d.ts +75 -0
- package/dist/embeddings/rate-limiter.d.ts.map +1 -0
- package/dist/embeddings/rate-limiter.js +145 -0
- package/dist/embeddings/rate-limiter.js.map +1 -0
- package/dist/embeddings/retry.d.ts +20 -0
- package/dist/embeddings/retry.d.ts.map +1 -0
- package/dist/embeddings/retry.js +227 -0
- package/dist/embeddings/retry.js.map +1 -0
- package/dist/embeddings/types.d.ts +103 -0
- package/dist/embeddings/types.d.ts.map +1 -0
- package/dist/embeddings/types.js +23 -0
- package/dist/embeddings/types.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2028 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/index.d.ts +63 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +168 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/search/ast-chunker.d.ts +29 -0
- package/dist/search/ast-chunker.d.ts.map +1 -0
- package/dist/search/ast-chunker.js +236 -0
- package/dist/search/ast-chunker.js.map +1 -0
- package/dist/search/chunk-utils.d.ts +24 -0
- package/dist/search/chunk-utils.d.ts.map +1 -0
- package/dist/search/chunk-utils.js +41 -0
- package/dist/search/chunk-utils.js.map +1 -0
- package/dist/search/clustering.d.ts +77 -0
- package/dist/search/clustering.d.ts.map +1 -0
- package/dist/search/clustering.js +455 -0
- package/dist/search/clustering.js.map +1 -0
- package/dist/search/indexer.d.ts +377 -0
- package/dist/search/indexer.d.ts.map +1 -0
- package/dist/search/indexer.js +1557 -0
- package/dist/search/indexer.js.map +1 -0
- package/dist/search/tree-sitter-chunker.d.ts +64 -0
- package/dist/search/tree-sitter-chunker.d.ts.map +1 -0
- package/dist/search/tree-sitter-chunker.js +412 -0
- package/dist/search/tree-sitter-chunker.js.map +1 -0
- package/dist/symbols/index.d.ts +14 -0
- package/dist/symbols/index.d.ts.map +1 -0
- package/dist/symbols/index.js +19 -0
- package/dist/symbols/index.js.map +1 -0
- package/dist/symbols/name-path.d.ts +113 -0
- package/dist/symbols/name-path.d.ts.map +1 -0
- package/dist/symbols/name-path.js +194 -0
- package/dist/symbols/name-path.js.map +1 -0
- package/dist/symbols/pattern-search.d.ts +14 -0
- package/dist/symbols/pattern-search.d.ts.map +1 -0
- package/dist/symbols/pattern-search.js +224 -0
- package/dist/symbols/pattern-search.js.map +1 -0
- package/dist/symbols/reference-finder.d.ts +38 -0
- package/dist/symbols/reference-finder.d.ts.map +1 -0
- package/dist/symbols/reference-finder.js +376 -0
- package/dist/symbols/reference-finder.js.map +1 -0
- package/dist/symbols/symbol-editor.d.ts +81 -0
- package/dist/symbols/symbol-editor.d.ts.map +1 -0
- package/dist/symbols/symbol-editor.js +257 -0
- package/dist/symbols/symbol-editor.js.map +1 -0
- package/dist/symbols/symbol-extractor.d.ts +49 -0
- package/dist/symbols/symbol-extractor.d.ts.map +1 -0
- package/dist/symbols/symbol-extractor.js +593 -0
- package/dist/symbols/symbol-extractor.js.map +1 -0
- package/dist/symbols/symbol-renamer.d.ts +81 -0
- package/dist/symbols/symbol-renamer.d.ts.map +1 -0
- package/dist/symbols/symbol-renamer.js +204 -0
- package/dist/symbols/symbol-renamer.js.map +1 -0
- package/dist/symbols/types.d.ts +234 -0
- package/dist/symbols/types.d.ts.map +1 -0
- package/dist/symbols/types.js +106 -0
- package/dist/symbols/types.js.map +1 -0
- package/dist/tools/clustering-handlers.d.ts +70 -0
- package/dist/tools/clustering-handlers.d.ts.map +1 -0
- package/dist/tools/clustering-handlers.js +150 -0
- package/dist/tools/clustering-handlers.js.map +1 -0
- package/dist/tools/commit-handlers.d.ts +77 -0
- package/dist/tools/commit-handlers.d.ts.map +1 -0
- package/dist/tools/commit-handlers.js +251 -0
- package/dist/tools/commit-handlers.js.map +1 -0
- package/dist/tools/index-handlers.d.ts +31 -0
- package/dist/tools/index-handlers.d.ts.map +1 -0
- package/dist/tools/index-handlers.js +53 -0
- package/dist/tools/index-handlers.js.map +1 -0
- package/dist/tools/instructions-handlers.d.ts +25 -0
- package/dist/tools/instructions-handlers.d.ts.map +1 -0
- package/dist/tools/instructions-handlers.js +36 -0
- package/dist/tools/instructions-handlers.js.map +1 -0
- package/dist/tools/memory-handlers.d.ts +95 -0
- package/dist/tools/memory-handlers.d.ts.map +1 -0
- package/dist/tools/memory-handlers.js +126 -0
- package/dist/tools/memory-handlers.js.map +1 -0
- package/dist/tools/search-handlers.d.ts +51 -0
- package/dist/tools/search-handlers.d.ts.map +1 -0
- package/dist/tools/search-handlers.js +128 -0
- package/dist/tools/search-handlers.js.map +1 -0
- package/dist/tools/symbol-handlers.d.ts +171 -0
- package/dist/tools/symbol-handlers.d.ts.map +1 -0
- package/dist/tools/symbol-handlers.js +456 -0
- package/dist/tools/symbol-handlers.js.map +1 -0
- package/dist/tools/types.d.ts +32 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +17 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/worktree-handlers.d.ts +96 -0
- package/dist/tools/worktree-handlers.d.ts.map +1 -0
- package/dist/tools/worktree-handlers.js +186 -0
- package/dist/tools/worktree-handlers.js.map +1 -0
- package/dist/utils/cache.d.ts +77 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +134 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/concurrency.d.ts +32 -0
- package/dist/utils/concurrency.d.ts.map +1 -0
- package/dist/utils/concurrency.js +57 -0
- package/dist/utils/concurrency.js.map +1 -0
- package/dist/utils/errors.d.ts +36 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +91 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +37 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +114 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/type-guards.d.ts +17 -0
- package/dist/utils/type-guards.d.ts.map +1 -0
- package/dist/utils/type-guards.js +25 -0
- package/dist/utils/type-guards.js.map +1 -0
- package/dist/worktree/index.d.ts +6 -0
- package/dist/worktree/index.d.ts.map +1 -0
- package/dist/worktree/index.js +6 -0
- package/dist/worktree/index.js.map +1 -0
- package/dist/worktree/types.d.ts +101 -0
- package/dist/worktree/types.d.ts.map +1 -0
- package/dist/worktree/types.js +6 -0
- package/dist/worktree/types.js.map +1 -0
- package/dist/worktree/worktree-manager.d.ts +80 -0
- package/dist/worktree/worktree-manager.d.ts.map +1 -0
- package/dist/worktree/worktree-manager.js +407 -0
- package/dist/worktree/worktree-manager.js.map +1 -0
- package/package.json +87 -0
- package/scripts/postinstall.js +48 -0
package/dist/config.js
ADDED
|
@@ -0,0 +1,788 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
/**
|
|
5
|
+
* Zod schema for configuration validation
|
|
6
|
+
*/
|
|
7
|
+
const ChunkingConfigSchema = z.object({
|
|
8
|
+
maxLines: z.number().min(10).max(500).optional(),
|
|
9
|
+
overlap: z.number().min(0).max(50).optional(),
|
|
10
|
+
});
|
|
11
|
+
const SearchConfigSchema = z.object({
|
|
12
|
+
semanticWeight: z.number().min(0).max(1).optional(),
|
|
13
|
+
keywordWeight: z.number().min(0).max(1).optional(),
|
|
14
|
+
/** Automatically reindex when stale files are detected before search (default: true) */
|
|
15
|
+
autoReindex: z.boolean().optional(),
|
|
16
|
+
});
|
|
17
|
+
const EmbeddingConfigSchema = z.object({
|
|
18
|
+
backend: z.enum(['ollama', 'gemini']).optional(),
|
|
19
|
+
model: z.string().optional(),
|
|
20
|
+
/** Number of concurrent requests to Ollama (default: 10). Increase if your system has capacity. */
|
|
21
|
+
ollamaConcurrency: z.number().min(1).max(200).optional(),
|
|
22
|
+
});
|
|
23
|
+
const DashboardConfigSchema = z.object({
|
|
24
|
+
enabled: z.boolean().optional(),
|
|
25
|
+
port: z.number().min(1024).max(65535).optional(),
|
|
26
|
+
openBrowser: z.boolean().optional(),
|
|
27
|
+
});
|
|
28
|
+
const IndexingConfigSchema = z.object({
|
|
29
|
+
/** Delay in milliseconds between embedding batches (default: 0) */
|
|
30
|
+
batchDelayMs: z.number().min(0).max(10000).optional(),
|
|
31
|
+
/** Number of chunks to embed per batch (default: 32). Higher values reduce overhead but use more memory. */
|
|
32
|
+
batchSize: z.number().min(1).max(1000).optional(),
|
|
33
|
+
});
|
|
34
|
+
const ConfigSchema = z.object({
|
|
35
|
+
patterns: z.array(z.string()).optional(),
|
|
36
|
+
excludePatterns: z.array(z.string()).optional(),
|
|
37
|
+
embedding: EmbeddingConfigSchema.optional(),
|
|
38
|
+
chunking: ChunkingConfigSchema.optional(),
|
|
39
|
+
search: SearchConfigSchema.optional(),
|
|
40
|
+
dashboard: DashboardConfigSchema.optional(),
|
|
41
|
+
indexing: IndexingConfigSchema.optional(),
|
|
42
|
+
instructions: z.string().optional(),
|
|
43
|
+
});
|
|
44
|
+
const DEFAULT_PATTERNS = [
|
|
45
|
+
'**/*.ts',
|
|
46
|
+
'**/*.tsx',
|
|
47
|
+
'**/*.js',
|
|
48
|
+
'**/*.jsx',
|
|
49
|
+
'**/*.py',
|
|
50
|
+
'**/*.go',
|
|
51
|
+
'**/*.rs',
|
|
52
|
+
'**/*.java',
|
|
53
|
+
'**/*.rb',
|
|
54
|
+
'**/*.php',
|
|
55
|
+
'**/*.c',
|
|
56
|
+
'**/*.cpp',
|
|
57
|
+
'**/*.h',
|
|
58
|
+
'**/*.hpp',
|
|
59
|
+
'**/*.cs',
|
|
60
|
+
'**/*.swift',
|
|
61
|
+
'**/*.kt',
|
|
62
|
+
];
|
|
63
|
+
const DEFAULT_EXCLUDE_PATTERNS = [
|
|
64
|
+
'**/node_modules/**',
|
|
65
|
+
'**/dist/**',
|
|
66
|
+
'**/.git/**',
|
|
67
|
+
'**/build/**',
|
|
68
|
+
'**/target/**',
|
|
69
|
+
'**/__pycache__/**',
|
|
70
|
+
'**/venv/**',
|
|
71
|
+
'**/.venv/**',
|
|
72
|
+
'**/vendor/**',
|
|
73
|
+
'**/*.min.js',
|
|
74
|
+
'**/*.min.css',
|
|
75
|
+
];
|
|
76
|
+
/**
|
|
77
|
+
* Default chunking configuration
|
|
78
|
+
*/
|
|
79
|
+
export const DEFAULT_CHUNKING = {
|
|
80
|
+
maxLines: 100,
|
|
81
|
+
overlap: 20,
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Default search configuration
|
|
85
|
+
*/
|
|
86
|
+
export const DEFAULT_SEARCH = {
|
|
87
|
+
semanticWeight: 0.7,
|
|
88
|
+
keywordWeight: 0.3,
|
|
89
|
+
autoReindex: true,
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Default dashboard configuration
|
|
93
|
+
*/
|
|
94
|
+
export const DEFAULT_DASHBOARD = {
|
|
95
|
+
enabled: true,
|
|
96
|
+
port: 24300,
|
|
97
|
+
openBrowser: true,
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Default indexing configuration
|
|
101
|
+
*/
|
|
102
|
+
export const DEFAULT_INDEXING = {
|
|
103
|
+
batchDelayMs: 0,
|
|
104
|
+
batchSize: 256,
|
|
105
|
+
};
|
|
106
|
+
export const DEFAULT_CONFIG = {
|
|
107
|
+
patterns: DEFAULT_PATTERNS,
|
|
108
|
+
excludePatterns: DEFAULT_EXCLUDE_PATTERNS,
|
|
109
|
+
chunking: DEFAULT_CHUNKING,
|
|
110
|
+
search: DEFAULT_SEARCH,
|
|
111
|
+
dashboard: DEFAULT_DASHBOARD,
|
|
112
|
+
indexing: DEFAULT_INDEXING,
|
|
113
|
+
};
|
|
114
|
+
const CONFIG_FILENAMES = ['.glancey.json', 'glancey.config.json'];
|
|
115
|
+
/**
|
|
116
|
+
* Local config filename for user-specific overrides (gitignored)
|
|
117
|
+
* Settings saved from the dashboard are written here instead of the main config
|
|
118
|
+
*/
|
|
119
|
+
const LOCAL_CONFIG_FILENAME = '.glancey.local.json';
|
|
120
|
+
/**
|
|
121
|
+
* Load and validate configuration from project directory
|
|
122
|
+
*/
|
|
123
|
+
/**
|
|
124
|
+
* Format a Zod validation error into a user-friendly message
|
|
125
|
+
*/
|
|
126
|
+
function formatValidationError(error, rawConfig) {
|
|
127
|
+
const pathArray = error.path.map((p) => String(p));
|
|
128
|
+
const fieldPath = pathArray.join('.');
|
|
129
|
+
const rawValue = getValueAtPath(rawConfig, pathArray);
|
|
130
|
+
const rawValueStr = rawValue === undefined ? 'undefined' : JSON.stringify(rawValue);
|
|
131
|
+
switch (error.code) {
|
|
132
|
+
case 'invalid_type': {
|
|
133
|
+
const expected = error.expected;
|
|
134
|
+
const suggestion = getSuggestionForType(fieldPath, String(expected), rawValue);
|
|
135
|
+
return ` - ${fieldPath}: Expected ${expected}, got ${typeof rawValue} ${rawValueStr}${suggestion}`;
|
|
136
|
+
}
|
|
137
|
+
case 'too_small': {
|
|
138
|
+
const issueMin = error;
|
|
139
|
+
const suggestion = getSuggestionForRange(fieldPath, 'minimum', issueMin.minimum);
|
|
140
|
+
return ` - ${fieldPath}: Value ${rawValueStr} is below minimum ${issueMin.minimum}${suggestion}`;
|
|
141
|
+
}
|
|
142
|
+
case 'too_big': {
|
|
143
|
+
const issueMax = error;
|
|
144
|
+
const suggestion = getSuggestionForRange(fieldPath, 'maximum', issueMax.maximum);
|
|
145
|
+
return ` - ${fieldPath}: Value ${rawValueStr} exceeds maximum ${issueMax.maximum}${suggestion}`;
|
|
146
|
+
}
|
|
147
|
+
case 'invalid_value': {
|
|
148
|
+
// Handle enum validation errors
|
|
149
|
+
const issueVal = error;
|
|
150
|
+
if (issueVal.values) {
|
|
151
|
+
const options = issueVal.values.map((o) => `'${String(o)}'`).join(', ');
|
|
152
|
+
return ` - ${fieldPath}: Invalid value ${rawValueStr}. Valid options: ${options}`;
|
|
153
|
+
}
|
|
154
|
+
return ` - ${fieldPath}: Invalid value ${rawValueStr}`;
|
|
155
|
+
}
|
|
156
|
+
default:
|
|
157
|
+
return ` - ${fieldPath}: ${error.message}`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Get the value at a path in an object
|
|
162
|
+
*/
|
|
163
|
+
function getValueAtPath(obj, pathSegments) {
|
|
164
|
+
let current = obj;
|
|
165
|
+
for (const segment of pathSegments) {
|
|
166
|
+
if (current === null || typeof current !== 'object') {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
current = current[segment];
|
|
170
|
+
}
|
|
171
|
+
return current;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get suggestions for common type mistakes
|
|
175
|
+
*/
|
|
176
|
+
function getSuggestionForType(fieldPath, expected, rawValue) {
|
|
177
|
+
// Check for string that looks like a number
|
|
178
|
+
if (expected === 'number' && typeof rawValue === 'string') {
|
|
179
|
+
const num = Number(rawValue);
|
|
180
|
+
if (!isNaN(num)) {
|
|
181
|
+
return `\n Suggestion: Remove quotes to use numeric value: ${num}`;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Check for string that looks like a boolean
|
|
185
|
+
if (expected === 'boolean' && typeof rawValue === 'string') {
|
|
186
|
+
const lower = rawValue.toLowerCase();
|
|
187
|
+
if (lower === 'true' || lower === 'false') {
|
|
188
|
+
return `\n Suggestion: Remove quotes to use boolean value: ${lower}`;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Check for array expected but got single value
|
|
192
|
+
if (expected === 'array' && typeof rawValue === 'string') {
|
|
193
|
+
return `\n Suggestion: Wrap the value in an array: ["${rawValue}"]`;
|
|
194
|
+
}
|
|
195
|
+
return '';
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Get suggestions for range violations
|
|
199
|
+
*/
|
|
200
|
+
function getSuggestionForRange(fieldPath, boundType, _bound) {
|
|
201
|
+
const fieldSuggestions = {
|
|
202
|
+
'chunking.maxLines': boundType === 'minimum'
|
|
203
|
+
? '\n Suggestion: Use a value between 10 and 500 lines per chunk'
|
|
204
|
+
: '\n Suggestion: Use a value between 10 and 500 lines per chunk',
|
|
205
|
+
'chunking.overlap': boundType === 'minimum'
|
|
206
|
+
? '\n Suggestion: Use a value between 0 and 50 lines for overlap'
|
|
207
|
+
: '\n Suggestion: Use a value between 0 and 50 lines for overlap',
|
|
208
|
+
'search.semanticWeight': '\n Suggestion: Use a value between 0.0 and 1.0 for search weights',
|
|
209
|
+
'search.keywordWeight': '\n Suggestion: Use a value between 0.0 and 1.0 for search weights',
|
|
210
|
+
'dashboard.port': '\n Suggestion: Use a port number between 1024 and 65535',
|
|
211
|
+
};
|
|
212
|
+
return fieldSuggestions[fieldPath] || '';
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Print formatted validation warnings to console
|
|
216
|
+
*/
|
|
217
|
+
function printValidationWarnings(filename, errors, rawConfig) {
|
|
218
|
+
console.warn(`[glancey] Warning: Invalid config in ${filename}`);
|
|
219
|
+
for (const error of errors) {
|
|
220
|
+
console.warn(formatValidationError(error, rawConfig));
|
|
221
|
+
}
|
|
222
|
+
console.warn(' Using default values for invalid fields.');
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Extract valid configuration values from a raw config object.
|
|
226
|
+
* This allows us to use valid fields while falling back to defaults for invalid ones.
|
|
227
|
+
*/
|
|
228
|
+
function extractValidConfig(rawConfig) {
|
|
229
|
+
if (typeof rawConfig !== 'object' || rawConfig === null) {
|
|
230
|
+
return {};
|
|
231
|
+
}
|
|
232
|
+
const config = rawConfig;
|
|
233
|
+
const result = {};
|
|
234
|
+
// Try to parse each section independently
|
|
235
|
+
if (config.patterns !== undefined) {
|
|
236
|
+
const patternsResult = z.array(z.string()).safeParse(config.patterns);
|
|
237
|
+
if (patternsResult.success) {
|
|
238
|
+
result.patterns = patternsResult.data;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (config.excludePatterns !== undefined) {
|
|
242
|
+
const excludeResult = z.array(z.string()).safeParse(config.excludePatterns);
|
|
243
|
+
if (excludeResult.success) {
|
|
244
|
+
result.excludePatterns = excludeResult.data;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (config.instructions !== undefined) {
|
|
248
|
+
const instructionsResult = z.string().safeParse(config.instructions);
|
|
249
|
+
if (instructionsResult.success) {
|
|
250
|
+
result.instructions = instructionsResult.data;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (config.embedding !== undefined) {
|
|
254
|
+
const embeddingResult = EmbeddingConfigSchema.safeParse(config.embedding);
|
|
255
|
+
if (embeddingResult.success) {
|
|
256
|
+
result.embedding = embeddingResult.data;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (config.chunking !== undefined) {
|
|
260
|
+
// Try to extract valid individual fields from chunking
|
|
261
|
+
const chunkingResult = extractValidChunking(config.chunking);
|
|
262
|
+
if (Object.keys(chunkingResult).length > 0) {
|
|
263
|
+
result.chunking = chunkingResult;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (config.search !== undefined) {
|
|
267
|
+
// Try to extract valid individual fields from search
|
|
268
|
+
const searchResult = extractValidSearch(config.search);
|
|
269
|
+
if (Object.keys(searchResult).length > 0) {
|
|
270
|
+
result.search = searchResult;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (config.dashboard !== undefined) {
|
|
274
|
+
// Try to extract valid individual fields from dashboard
|
|
275
|
+
const dashboardResult = extractValidDashboard(config.dashboard);
|
|
276
|
+
if (Object.keys(dashboardResult).length > 0) {
|
|
277
|
+
result.dashboard = dashboardResult;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (config.indexing !== undefined) {
|
|
281
|
+
// Try to extract valid individual fields from indexing
|
|
282
|
+
const indexingResult = extractValidIndexing(config.indexing);
|
|
283
|
+
if (Object.keys(indexingResult).length > 0) {
|
|
284
|
+
result.indexing = indexingResult;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return result;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Extract valid chunking config fields
|
|
291
|
+
*/
|
|
292
|
+
function extractValidChunking(rawChunking) {
|
|
293
|
+
if (typeof rawChunking !== 'object' || rawChunking === null) {
|
|
294
|
+
return {};
|
|
295
|
+
}
|
|
296
|
+
const chunking = rawChunking;
|
|
297
|
+
const result = {};
|
|
298
|
+
if (chunking.maxLines !== undefined) {
|
|
299
|
+
const maxLinesResult = z.number().min(10).max(500).safeParse(chunking.maxLines);
|
|
300
|
+
if (maxLinesResult.success) {
|
|
301
|
+
result.maxLines = maxLinesResult.data;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (chunking.overlap !== undefined) {
|
|
305
|
+
const overlapResult = z.number().min(0).max(50).safeParse(chunking.overlap);
|
|
306
|
+
if (overlapResult.success) {
|
|
307
|
+
result.overlap = overlapResult.data;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Extract valid search config fields
|
|
314
|
+
*/
|
|
315
|
+
function extractValidSearch(rawSearch) {
|
|
316
|
+
if (typeof rawSearch !== 'object' || rawSearch === null) {
|
|
317
|
+
return {};
|
|
318
|
+
}
|
|
319
|
+
const search = rawSearch;
|
|
320
|
+
const result = {};
|
|
321
|
+
if (search.semanticWeight !== undefined) {
|
|
322
|
+
const semanticResult = z.number().min(0).max(1).safeParse(search.semanticWeight);
|
|
323
|
+
if (semanticResult.success) {
|
|
324
|
+
result.semanticWeight = semanticResult.data;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (search.keywordWeight !== undefined) {
|
|
328
|
+
const keywordResult = z.number().min(0).max(1).safeParse(search.keywordWeight);
|
|
329
|
+
if (keywordResult.success) {
|
|
330
|
+
result.keywordWeight = keywordResult.data;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (search.autoReindex !== undefined) {
|
|
334
|
+
const autoReindexResult = z.boolean().safeParse(search.autoReindex);
|
|
335
|
+
if (autoReindexResult.success) {
|
|
336
|
+
result.autoReindex = autoReindexResult.data;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Extract valid dashboard config fields
|
|
343
|
+
*/
|
|
344
|
+
function extractValidDashboard(rawDashboard) {
|
|
345
|
+
if (typeof rawDashboard !== 'object' || rawDashboard === null) {
|
|
346
|
+
return {};
|
|
347
|
+
}
|
|
348
|
+
const dashboard = rawDashboard;
|
|
349
|
+
const result = {};
|
|
350
|
+
if (dashboard.enabled !== undefined) {
|
|
351
|
+
const enabledResult = z.boolean().safeParse(dashboard.enabled);
|
|
352
|
+
if (enabledResult.success) {
|
|
353
|
+
result.enabled = enabledResult.data;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (dashboard.port !== undefined) {
|
|
357
|
+
const portResult = z.number().min(1024).max(65535).safeParse(dashboard.port);
|
|
358
|
+
if (portResult.success) {
|
|
359
|
+
result.port = portResult.data;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (dashboard.openBrowser !== undefined) {
|
|
363
|
+
const openBrowserResult = z.boolean().safeParse(dashboard.openBrowser);
|
|
364
|
+
if (openBrowserResult.success) {
|
|
365
|
+
result.openBrowser = openBrowserResult.data;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return result;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Extract valid indexing config fields
|
|
372
|
+
*/
|
|
373
|
+
function extractValidIndexing(rawIndexing) {
|
|
374
|
+
if (typeof rawIndexing !== 'object' || rawIndexing === null) {
|
|
375
|
+
return {};
|
|
376
|
+
}
|
|
377
|
+
const indexing = rawIndexing;
|
|
378
|
+
const result = {};
|
|
379
|
+
if (indexing.batchDelayMs !== undefined) {
|
|
380
|
+
const batchDelayResult = z.number().min(0).max(10000).safeParse(indexing.batchDelayMs);
|
|
381
|
+
if (batchDelayResult.success) {
|
|
382
|
+
result.batchDelayMs = batchDelayResult.data;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (indexing.batchSize !== undefined) {
|
|
386
|
+
const batchSizeResult = z.number().min(1).max(100).safeParse(indexing.batchSize);
|
|
387
|
+
if (batchSizeResult.success) {
|
|
388
|
+
result.batchSize = batchSizeResult.data;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Load local config overrides (user-specific settings, gitignored)
|
|
395
|
+
*/
|
|
396
|
+
async function loadLocalConfig(projectPath) {
|
|
397
|
+
const localConfigPath = path.join(projectPath, LOCAL_CONFIG_FILENAME);
|
|
398
|
+
try {
|
|
399
|
+
const content = await fs.readFile(localConfigPath, 'utf-8');
|
|
400
|
+
const rawConfig = JSON.parse(content);
|
|
401
|
+
// Validate with Zod (lenient - just extract valid parts)
|
|
402
|
+
const result = ConfigSchema.safeParse(rawConfig);
|
|
403
|
+
if (result.success) {
|
|
404
|
+
return result.data;
|
|
405
|
+
}
|
|
406
|
+
// Try to salvage valid parts
|
|
407
|
+
return extractValidConfig(rawConfig);
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
// Local config doesn't exist, return empty
|
|
411
|
+
return {};
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Deep merge two config objects, with localConfig taking precedence
|
|
416
|
+
*/
|
|
417
|
+
function mergeConfigs(baseConfig, localConfig) {
|
|
418
|
+
return {
|
|
419
|
+
patterns: localConfig.patterns || baseConfig.patterns,
|
|
420
|
+
excludePatterns: localConfig.excludePatterns || baseConfig.excludePatterns,
|
|
421
|
+
embedding: {
|
|
422
|
+
...baseConfig.embedding,
|
|
423
|
+
...localConfig.embedding,
|
|
424
|
+
},
|
|
425
|
+
chunking: {
|
|
426
|
+
...baseConfig.chunking,
|
|
427
|
+
...localConfig.chunking,
|
|
428
|
+
},
|
|
429
|
+
search: {
|
|
430
|
+
...baseConfig.search,
|
|
431
|
+
...localConfig.search,
|
|
432
|
+
},
|
|
433
|
+
dashboard: {
|
|
434
|
+
...baseConfig.dashboard,
|
|
435
|
+
...localConfig.dashboard,
|
|
436
|
+
},
|
|
437
|
+
indexing: {
|
|
438
|
+
...baseConfig.indexing,
|
|
439
|
+
...localConfig.indexing,
|
|
440
|
+
},
|
|
441
|
+
instructions: localConfig.instructions ?? baseConfig.instructions,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
export async function loadConfig(projectPath) {
|
|
445
|
+
let baseConfig = DEFAULT_CONFIG;
|
|
446
|
+
// Load project config
|
|
447
|
+
for (const filename of CONFIG_FILENAMES) {
|
|
448
|
+
const configPath = path.join(projectPath, filename);
|
|
449
|
+
try {
|
|
450
|
+
const content = await fs.readFile(configPath, 'utf-8');
|
|
451
|
+
const rawConfig = JSON.parse(content);
|
|
452
|
+
// Validate with Zod
|
|
453
|
+
const result = ConfigSchema.safeParse(rawConfig);
|
|
454
|
+
if (!result.success) {
|
|
455
|
+
// Print detailed validation warnings
|
|
456
|
+
printValidationWarnings(filename, result.error.issues, rawConfig);
|
|
457
|
+
// Try to salvage valid parts of the config by validating each section separately
|
|
458
|
+
const validConfig = extractValidConfig(rawConfig);
|
|
459
|
+
baseConfig = {
|
|
460
|
+
patterns: validConfig.patterns || DEFAULT_PATTERNS,
|
|
461
|
+
excludePatterns: validConfig.excludePatterns || DEFAULT_EXCLUDE_PATTERNS,
|
|
462
|
+
embedding: validConfig.embedding,
|
|
463
|
+
chunking: {
|
|
464
|
+
...DEFAULT_CHUNKING,
|
|
465
|
+
...validConfig.chunking,
|
|
466
|
+
},
|
|
467
|
+
search: {
|
|
468
|
+
...DEFAULT_SEARCH,
|
|
469
|
+
...validConfig.search,
|
|
470
|
+
},
|
|
471
|
+
dashboard: {
|
|
472
|
+
...DEFAULT_DASHBOARD,
|
|
473
|
+
...validConfig.dashboard,
|
|
474
|
+
},
|
|
475
|
+
indexing: {
|
|
476
|
+
...DEFAULT_INDEXING,
|
|
477
|
+
...validConfig.indexing,
|
|
478
|
+
},
|
|
479
|
+
instructions: validConfig.instructions,
|
|
480
|
+
};
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
const userConfig = result.data;
|
|
484
|
+
baseConfig = {
|
|
485
|
+
patterns: userConfig.patterns || DEFAULT_PATTERNS,
|
|
486
|
+
excludePatterns: userConfig.excludePatterns || DEFAULT_EXCLUDE_PATTERNS,
|
|
487
|
+
embedding: userConfig.embedding,
|
|
488
|
+
chunking: {
|
|
489
|
+
...DEFAULT_CHUNKING,
|
|
490
|
+
...userConfig.chunking,
|
|
491
|
+
},
|
|
492
|
+
search: {
|
|
493
|
+
...DEFAULT_SEARCH,
|
|
494
|
+
...userConfig.search,
|
|
495
|
+
},
|
|
496
|
+
dashboard: {
|
|
497
|
+
...DEFAULT_DASHBOARD,
|
|
498
|
+
...userConfig.dashboard,
|
|
499
|
+
},
|
|
500
|
+
indexing: {
|
|
501
|
+
...DEFAULT_INDEXING,
|
|
502
|
+
...userConfig.indexing,
|
|
503
|
+
},
|
|
504
|
+
instructions: userConfig.instructions,
|
|
505
|
+
};
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
catch (error) {
|
|
509
|
+
// Check if it's a JSON parse error
|
|
510
|
+
if (error instanceof SyntaxError) {
|
|
511
|
+
console.warn(`[glancey] Warning: Invalid JSON in ${filename}`);
|
|
512
|
+
console.warn(` - ${error.message}`);
|
|
513
|
+
console.warn(' Suggestion: Check for trailing commas, missing quotes, or other JSON syntax errors.');
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
// Config file doesn't exist, continue to next
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
// Load and merge local config overrides
|
|
520
|
+
const localConfig = await loadLocalConfig(projectPath);
|
|
521
|
+
return mergeConfigs(baseConfig, localConfig);
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Get default file patterns
|
|
525
|
+
*/
|
|
526
|
+
export function getDefaultPatterns() {
|
|
527
|
+
return [...DEFAULT_PATTERNS];
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Get default exclude patterns
|
|
531
|
+
*/
|
|
532
|
+
export function getDefaultExcludePatterns() {
|
|
533
|
+
return [...DEFAULT_EXCLUDE_PATTERNS];
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Get chunking config with defaults
|
|
537
|
+
*/
|
|
538
|
+
export function getChunkingConfig(config) {
|
|
539
|
+
return {
|
|
540
|
+
...DEFAULT_CHUNKING,
|
|
541
|
+
...config.chunking,
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Get search config with defaults
|
|
546
|
+
*/
|
|
547
|
+
export function getSearchConfig(config) {
|
|
548
|
+
return {
|
|
549
|
+
...DEFAULT_SEARCH,
|
|
550
|
+
...config.search,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Get dashboard config with defaults
|
|
555
|
+
*/
|
|
556
|
+
export function getDashboardConfig(config) {
|
|
557
|
+
return {
|
|
558
|
+
...DEFAULT_DASHBOARD,
|
|
559
|
+
...config.dashboard,
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Get indexing config with defaults
|
|
564
|
+
*/
|
|
565
|
+
export function getIndexingConfig(config) {
|
|
566
|
+
return {
|
|
567
|
+
...DEFAULT_INDEXING,
|
|
568
|
+
...config.indexing,
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Get project instructions from config
|
|
573
|
+
*/
|
|
574
|
+
export function getInstructions(config) {
|
|
575
|
+
return config.instructions;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Load secrets from .glancey/secrets.json
|
|
579
|
+
*/
|
|
580
|
+
export async function loadSecrets(projectPath) {
|
|
581
|
+
const secretsPath = path.join(projectPath, '.glancey', 'secrets.json');
|
|
582
|
+
try {
|
|
583
|
+
const content = await fs.readFile(secretsPath, 'utf-8');
|
|
584
|
+
return JSON.parse(content);
|
|
585
|
+
}
|
|
586
|
+
catch {
|
|
587
|
+
return {};
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Save secrets to .glancey/secrets.json
|
|
592
|
+
*/
|
|
593
|
+
export async function saveSecrets(projectPath, secrets) {
|
|
594
|
+
const lanceDir = path.join(projectPath, '.glancey');
|
|
595
|
+
const secretsPath = path.join(lanceDir, 'secrets.json');
|
|
596
|
+
// Ensure .glancey directory exists
|
|
597
|
+
await fs.mkdir(lanceDir, { recursive: true });
|
|
598
|
+
// Load existing secrets and merge
|
|
599
|
+
const existing = await loadSecrets(projectPath);
|
|
600
|
+
const merged = { ...existing, ...secrets };
|
|
601
|
+
await fs.writeFile(secretsPath, JSON.stringify(merged, null, 2));
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Save embedding settings from dashboard
|
|
605
|
+
* - Stores backend preference in .glancey.local.json (gitignored, user-specific)
|
|
606
|
+
* - Stores API key in .glancey/secrets.json (gitignored)
|
|
607
|
+
*/
|
|
608
|
+
export async function saveEmbeddingSettings(projectPath, settings) {
|
|
609
|
+
// Load existing local config (user-specific overrides)
|
|
610
|
+
const localConfigPath = path.join(projectPath, LOCAL_CONFIG_FILENAME);
|
|
611
|
+
let localConfig = {};
|
|
612
|
+
try {
|
|
613
|
+
const content = await fs.readFile(localConfigPath, 'utf-8');
|
|
614
|
+
localConfig = JSON.parse(content);
|
|
615
|
+
}
|
|
616
|
+
catch {
|
|
617
|
+
// File doesn't exist, start fresh
|
|
618
|
+
}
|
|
619
|
+
// Update embedding config
|
|
620
|
+
localConfig.embedding = {
|
|
621
|
+
...localConfig.embedding,
|
|
622
|
+
backend: settings.backend,
|
|
623
|
+
};
|
|
624
|
+
// Update ollamaConcurrency if provided
|
|
625
|
+
if (settings.ollamaConcurrency !== undefined) {
|
|
626
|
+
localConfig.embedding.ollamaConcurrency = settings.ollamaConcurrency;
|
|
627
|
+
}
|
|
628
|
+
// Update indexing batch size if provided
|
|
629
|
+
if (settings.batchSize !== undefined) {
|
|
630
|
+
localConfig.indexing = {
|
|
631
|
+
...localConfig.indexing,
|
|
632
|
+
batchSize: settings.batchSize,
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
// Save to local config (gitignored, user-specific)
|
|
636
|
+
await fs.writeFile(localConfigPath, JSON.stringify(localConfig, null, 2));
|
|
637
|
+
// Save API key to secrets if provided
|
|
638
|
+
if (settings.apiKey) {
|
|
639
|
+
if (settings.backend === 'gemini') {
|
|
640
|
+
await saveSecrets(projectPath, { geminiApiKey: settings.apiKey });
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Get current embedding settings including secrets
|
|
646
|
+
*/
|
|
647
|
+
export async function getEmbeddingSettings(projectPath) {
|
|
648
|
+
const config = await loadConfig(projectPath);
|
|
649
|
+
const secrets = await loadSecrets(projectPath);
|
|
650
|
+
const backend = config.embedding?.backend || 'ollama';
|
|
651
|
+
// Check for API key based on backend
|
|
652
|
+
let hasApiKey = false;
|
|
653
|
+
if (backend === 'gemini') {
|
|
654
|
+
hasApiKey = !!(secrets.geminiApiKey || process.env.GEMINI_API_KEY);
|
|
655
|
+
}
|
|
656
|
+
return {
|
|
657
|
+
backend,
|
|
658
|
+
hasApiKey,
|
|
659
|
+
ollamaUrl: process.env.OLLAMA_URL || 'http://localhost:11434',
|
|
660
|
+
ollamaConcurrency: config.embedding?.ollamaConcurrency || 1,
|
|
661
|
+
batchSize: config.indexing?.batchSize || DEFAULT_INDEXING.batchSize,
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Save dashboard settings to .glancey.local.json (gitignored, user-specific)
|
|
666
|
+
*/
|
|
667
|
+
export async function saveDashboardSettings(projectPath, settings) {
|
|
668
|
+
const localConfigPath = path.join(projectPath, LOCAL_CONFIG_FILENAME);
|
|
669
|
+
let localConfig = {};
|
|
670
|
+
try {
|
|
671
|
+
const content = await fs.readFile(localConfigPath, 'utf-8');
|
|
672
|
+
localConfig = JSON.parse(content);
|
|
673
|
+
}
|
|
674
|
+
catch {
|
|
675
|
+
// File doesn't exist, start fresh
|
|
676
|
+
}
|
|
677
|
+
// Update dashboard config
|
|
678
|
+
localConfig.dashboard = {
|
|
679
|
+
...localConfig.dashboard,
|
|
680
|
+
enabled: settings.enabled,
|
|
681
|
+
};
|
|
682
|
+
// Only update port if provided
|
|
683
|
+
if (settings.port !== undefined) {
|
|
684
|
+
localConfig.dashboard.port = settings.port;
|
|
685
|
+
}
|
|
686
|
+
// Only update openBrowser if provided
|
|
687
|
+
if (settings.openBrowser !== undefined) {
|
|
688
|
+
localConfig.dashboard.openBrowser = settings.openBrowser;
|
|
689
|
+
}
|
|
690
|
+
await fs.writeFile(localConfigPath, JSON.stringify(localConfig, null, 2));
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Get current dashboard settings
|
|
694
|
+
*/
|
|
695
|
+
export async function getDashboardSettings(projectPath) {
|
|
696
|
+
const config = await loadConfig(projectPath);
|
|
697
|
+
return {
|
|
698
|
+
enabled: config.dashboard?.enabled ?? DEFAULT_DASHBOARD.enabled,
|
|
699
|
+
port: config.dashboard?.port ?? DEFAULT_DASHBOARD.port,
|
|
700
|
+
openBrowser: config.dashboard?.openBrowser ?? DEFAULT_DASHBOARD.openBrowser,
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Save search weights to .glancey.local.json (gitignored, user-specific)
|
|
705
|
+
*/
|
|
706
|
+
export async function saveSearchWeights(projectPath, settings) {
|
|
707
|
+
const localConfigPath = path.join(projectPath, LOCAL_CONFIG_FILENAME);
|
|
708
|
+
let localConfig = {};
|
|
709
|
+
try {
|
|
710
|
+
const content = await fs.readFile(localConfigPath, 'utf-8');
|
|
711
|
+
localConfig = JSON.parse(content);
|
|
712
|
+
}
|
|
713
|
+
catch {
|
|
714
|
+
// File doesn't exist, start fresh
|
|
715
|
+
}
|
|
716
|
+
// Update search config
|
|
717
|
+
localConfig.search = {
|
|
718
|
+
...localConfig.search,
|
|
719
|
+
semanticWeight: settings.semanticWeight,
|
|
720
|
+
keywordWeight: settings.keywordWeight,
|
|
721
|
+
};
|
|
722
|
+
await fs.writeFile(localConfigPath, JSON.stringify(localConfig, null, 2));
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Get current search weights
|
|
726
|
+
*/
|
|
727
|
+
export async function getSearchWeights(projectPath) {
|
|
728
|
+
const config = await loadConfig(projectPath);
|
|
729
|
+
return {
|
|
730
|
+
semanticWeight: config.search?.semanticWeight ?? DEFAULT_SEARCH.semanticWeight,
|
|
731
|
+
keywordWeight: config.search?.keywordWeight ?? DEFAULT_SEARCH.keywordWeight,
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Add a pattern to include or exclude patterns in .glancey.local.json
|
|
736
|
+
*/
|
|
737
|
+
export async function addPattern(projectPath, pattern, type) {
|
|
738
|
+
const localConfigPath = path.join(projectPath, LOCAL_CONFIG_FILENAME);
|
|
739
|
+
let localConfig = {};
|
|
740
|
+
try {
|
|
741
|
+
const content = await fs.readFile(localConfigPath, 'utf-8');
|
|
742
|
+
localConfig = JSON.parse(content);
|
|
743
|
+
}
|
|
744
|
+
catch {
|
|
745
|
+
// File doesn't exist, start fresh
|
|
746
|
+
}
|
|
747
|
+
// Get effective config to check for duplicates and get current patterns
|
|
748
|
+
const effectiveConfig = await loadConfig(projectPath);
|
|
749
|
+
if (type === 'include') {
|
|
750
|
+
const currentPatterns = effectiveConfig.patterns || [];
|
|
751
|
+
if (!currentPatterns.includes(pattern)) {
|
|
752
|
+
localConfig.patterns = [...currentPatterns, pattern];
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
const currentPatterns = effectiveConfig.excludePatterns || [];
|
|
757
|
+
if (!currentPatterns.includes(pattern)) {
|
|
758
|
+
localConfig.excludePatterns = [...currentPatterns, pattern];
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
await fs.writeFile(localConfigPath, JSON.stringify(localConfig, null, 2));
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Remove a pattern from include or exclude patterns in .glancey.local.json
|
|
765
|
+
*/
|
|
766
|
+
export async function removePattern(projectPath, pattern, type) {
|
|
767
|
+
const localConfigPath = path.join(projectPath, LOCAL_CONFIG_FILENAME);
|
|
768
|
+
let localConfig = {};
|
|
769
|
+
try {
|
|
770
|
+
const content = await fs.readFile(localConfigPath, 'utf-8');
|
|
771
|
+
localConfig = JSON.parse(content);
|
|
772
|
+
}
|
|
773
|
+
catch {
|
|
774
|
+
// File doesn't exist, start fresh
|
|
775
|
+
}
|
|
776
|
+
// Get effective config to get current patterns
|
|
777
|
+
const effectiveConfig = await loadConfig(projectPath);
|
|
778
|
+
if (type === 'include') {
|
|
779
|
+
const currentPatterns = effectiveConfig.patterns || [];
|
|
780
|
+
localConfig.patterns = currentPatterns.filter((p) => p !== pattern);
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
const currentPatterns = effectiveConfig.excludePatterns || [];
|
|
784
|
+
localConfig.excludePatterns = currentPatterns.filter((p) => p !== pattern);
|
|
785
|
+
}
|
|
786
|
+
await fs.writeFile(localConfigPath, JSON.stringify(localConfig, null, 2));
|
|
787
|
+
}
|
|
788
|
+
//# sourceMappingURL=config.js.map
|