codesearch-mcp 0.0.17-alpha.1 → 0.1.0-alpha

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.
Files changed (134) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +95 -6
  3. package/dist/app.js +70 -10
  4. package/dist/src/commands/ConfigCommand.d.ts +1 -0
  5. package/dist/src/commands/ConfigCommand.js +84 -4
  6. package/dist/src/commands/IndexCommand.js +35 -7
  7. package/dist/src/commands/McpCommand.js +9 -4
  8. package/dist/src/commands/{SetupCommand.d.ts → WebCommand.d.ts} +3 -2
  9. package/dist/src/commands/WebCommand.js +56 -0
  10. package/dist/src/config-store.d.ts +85 -4
  11. package/dist/src/config-store.js +157 -32
  12. package/dist/src/config.d.ts +32 -8
  13. package/dist/src/config.js +93 -30
  14. package/dist/src/constants.d.ts +35 -0
  15. package/dist/src/constants.js +57 -4
  16. package/dist/src/container.d.ts +10 -6
  17. package/dist/src/container.js +10 -5
  18. package/dist/src/db/index.d.ts +1 -1
  19. package/dist/src/db/index.js +2 -1
  20. package/dist/src/db/schema.d.ts +111 -0
  21. package/dist/src/db/schema.js +15 -1
  22. package/dist/src/graph.d.ts +60 -0
  23. package/dist/src/graph.js +239 -120
  24. package/dist/src/help.js +20 -5
  25. package/dist/src/interfaces/IAstParser.d.ts +20 -3
  26. package/dist/src/interfaces/IChunker.d.ts +24 -0
  27. package/dist/src/interfaces/IChunker.js +2 -0
  28. package/dist/src/interfaces/IDatabase.d.ts +27 -0
  29. package/dist/src/interfaces/IDatabase.js +2 -0
  30. package/dist/src/interfaces/IEmbedder.d.ts +15 -2
  31. package/dist/src/interfaces/ILogger.d.ts +10 -0
  32. package/dist/src/interfaces/ILogger.js +2 -0
  33. package/dist/src/interfaces/IMcpToolHandler.d.ts +22 -0
  34. package/dist/src/interfaces/IMcpToolHandler.js +2 -0
  35. package/dist/src/interfaces/IMonorepoResolver.d.ts +33 -0
  36. package/dist/src/interfaces/IMonorepoResolver.js +2 -0
  37. package/dist/src/interfaces/IProjectDiscovery.d.ts +28 -0
  38. package/dist/src/interfaces/IProjectDiscovery.js +2 -0
  39. package/dist/src/interfaces/ISearcher.d.ts +37 -0
  40. package/dist/src/interfaces/ISearcher.js +2 -0
  41. package/dist/src/interfaces/index.d.ts +8 -0
  42. package/dist/src/interfaces/index.js +2 -0
  43. package/dist/src/owner-path.d.ts +10 -0
  44. package/dist/src/owner-path.js +27 -0
  45. package/dist/src/project-selector.d.ts +1 -1
  46. package/dist/src/services/AstChunker.d.ts +31 -0
  47. package/dist/src/services/AstChunker.js +114 -0
  48. package/dist/src/services/AstParserService.d.ts +10 -6
  49. package/dist/src/services/AstParserService.js +60 -15
  50. package/dist/src/services/DatabaseService.d.ts +14 -1
  51. package/dist/src/services/DatabaseService.js +70 -25
  52. package/dist/src/services/EmbedderFactory.d.ts +9 -0
  53. package/dist/src/services/EmbedderFactory.js +29 -0
  54. package/dist/src/services/IndexerService.d.ts +22 -12
  55. package/dist/src/services/IndexerService.js +106 -46
  56. package/dist/src/services/InfinityEmbedder.d.ts +79 -0
  57. package/dist/src/services/InfinityEmbedder.js +199 -0
  58. package/dist/src/services/LoggerService.d.ts +2 -1
  59. package/dist/src/services/McpServer.d.ts +16 -7
  60. package/dist/src/services/McpServer.js +54 -367
  61. package/dist/src/services/MonorepoAwareDiscoveryService.d.ts +62 -0
  62. package/dist/src/services/MonorepoAwareDiscoveryService.js +125 -0
  63. package/dist/src/services/NxMonorepoResolver.d.ts +45 -0
  64. package/dist/src/services/NxMonorepoResolver.js +308 -0
  65. package/dist/src/services/OllamaEmbedder.d.ts +19 -5
  66. package/dist/src/services/OllamaEmbedder.js +91 -36
  67. package/dist/src/services/OpenAICompatibleEmbedder.d.ts +25 -0
  68. package/dist/src/services/OpenAICompatibleEmbedder.js +75 -0
  69. package/dist/src/services/ProjectDiscoveryService.d.ts +8 -0
  70. package/dist/src/{env-loader.js → services/ProjectDiscoveryService.js} +16 -5
  71. package/dist/src/services/SearcherService.d.ts +46 -7
  72. package/dist/src/services/SearcherService.js +183 -53
  73. package/dist/src/services/VoyageEmbedder.d.ts +22 -0
  74. package/dist/src/services/VoyageEmbedder.js +72 -0
  75. package/dist/src/services/WebServerService.d.ts +77 -0
  76. package/dist/src/services/WebServerService.js +267 -0
  77. package/dist/src/services/http-retry.d.ts +19 -0
  78. package/dist/src/services/http-retry.js +54 -0
  79. package/dist/src/services/reranker.d.ts +15 -0
  80. package/dist/src/services/reranker.js +44 -0
  81. package/dist/src/setup.d.ts +6 -0
  82. package/dist/src/setup.js +249 -31
  83. package/dist/src/tools/FindSymbolTool.d.ts +8 -0
  84. package/dist/src/tools/FindSymbolTool.js +45 -0
  85. package/dist/src/tools/GetCallersTool.d.ts +8 -0
  86. package/dist/src/tools/GetCallersTool.js +42 -0
  87. package/dist/src/tools/GetDependenciesTool.d.ts +8 -0
  88. package/dist/src/tools/GetDependenciesTool.js +38 -0
  89. package/dist/src/tools/GetDependentsTool.d.ts +8 -0
  90. package/dist/src/tools/GetDependentsTool.js +57 -0
  91. package/dist/src/tools/ListFilesTool.d.ts +8 -0
  92. package/dist/src/tools/ListFilesTool.js +39 -0
  93. package/dist/src/tools/ListProjectsTool.d.ts +8 -0
  94. package/dist/src/tools/ListProjectsTool.js +44 -0
  95. package/dist/src/tools/ReadFileRangeTool.d.ts +8 -0
  96. package/dist/src/tools/ReadFileRangeTool.js +44 -0
  97. package/dist/src/tools/ReadFileTool.d.ts +8 -0
  98. package/dist/src/tools/ReadFileTool.js +42 -0
  99. package/dist/src/tools/SearchCodebaseTool.d.ts +13 -0
  100. package/dist/src/tools/SearchCodebaseTool.js +81 -0
  101. package/dist/src/tools/format-helpers.d.ts +8 -0
  102. package/dist/src/tools/format-helpers.js +58 -0
  103. package/dist/src/tools/index.d.ts +9 -0
  104. package/dist/src/tools/index.js +21 -0
  105. package/dist/src/web/dashboard.d.ts +9 -0
  106. package/dist/src/web/dashboard.js +342 -0
  107. package/docker/docker-compose.infinity-gpu.yml +19 -0
  108. package/docker/docker-compose.yml +25 -0
  109. package/docs/web-dashboard.md +187 -0
  110. package/package.json +25 -3
  111. package/dist/src/ast.d.ts +0 -2
  112. package/dist/src/ast.js +0 -110
  113. package/dist/src/commands/SetupCommand.js +0 -14
  114. package/dist/src/db/data-source.d.ts +0 -2
  115. package/dist/src/db/data-source.js +0 -13
  116. package/dist/src/db/entities/code-chunk.entity.d.ts +0 -9
  117. package/dist/src/db/entities/code-chunk.entity.js +0 -54
  118. package/dist/src/db/entities/code-dependency.entity.d.ts +0 -9
  119. package/dist/src/db/entities/code-dependency.entity.js +0 -54
  120. package/dist/src/db/entities/code-symbol.entity.d.ts +0 -10
  121. package/dist/src/db/entities/code-symbol.entity.js +0 -59
  122. package/dist/src/db/init.d.ts +0 -2
  123. package/dist/src/db/init.js +0 -62
  124. package/dist/src/env-loader.d.ts +0 -1
  125. package/dist/src/indexer.d.ts +0 -17
  126. package/dist/src/indexer.js +0 -242
  127. package/dist/src/mcp.d.ts +0 -1
  128. package/dist/src/mcp.js +0 -403
  129. package/dist/src/ollama.d.ts +0 -2
  130. package/dist/src/ollama.js +0 -6
  131. package/dist/src/searcher.d.ts +0 -74
  132. package/dist/src/searcher.js +0 -446
  133. package/dist/vitest.config.d.ts +0 -2
  134. package/dist/vitest.config.js +0 -9
@@ -34,10 +34,10 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.Config = exports.ConfigError = void 0;
37
+ exports.resolveEnvRef = resolveEnvRef;
37
38
  const os = __importStar(require("os"));
38
39
  const zod_1 = require("zod");
39
40
  const config_store_1 = require("./config-store");
40
- const constants_1 = require("./constants");
41
41
  class ConfigError extends Error {
42
42
  constructor(message) {
43
43
  super(message);
@@ -45,37 +45,80 @@ class ConfigError extends Error {
45
45
  }
46
46
  }
47
47
  exports.ConfigError = ConfigError;
48
+ /**
49
+ * Resolve a config value that may reference an environment variable. A value of
50
+ * the form `env:VAR_NAME` resolves to `process.env.VAR_NAME`; any other string
51
+ * (including empty) is returned verbatim. A referenced-but-unset variable throws
52
+ * a clear error naming the variable and the `.env` file. Pure aside from the
53
+ * injected env map. See `src/app.ts` for how `.env` is loaded into the process.
54
+ */
55
+ function resolveEnvRef(value, env = process.env) {
56
+ if (!value.startsWith('env:'))
57
+ return value;
58
+ const name = value.slice(4).trim();
59
+ const resolved = name ? env[name] : undefined;
60
+ if (resolved == null || resolved === '') {
61
+ throw new ConfigError(`Config references environment variable "${name}" (as "env:${name}") but it is not set. ` +
62
+ `Define ${name} in ${config_store_1.ENV_FILE} or export it in the environment.`);
63
+ }
64
+ return resolved;
65
+ }
66
+ const DEFAULT_BASE_URLS = {
67
+ 'ollama': 'http://localhost:11434',
68
+ 'openai-compatible': 'https://api.openai.com/v1',
69
+ 'voyage': 'https://api.voyageai.com/v1',
70
+ 'infinity': 'http://localhost:7997',
71
+ };
72
+ function defaultBaseURL(provider) {
73
+ return DEFAULT_BASE_URLS[provider] ?? DEFAULT_BASE_URLS.ollama;
74
+ }
75
+ // Reranking is served only by OpenAI-compatible `/rerank` endpoints (Infinity,
76
+ // TEI, vLLM, Jina/Cohere-shape). Ollama has no rerank API, so there is a single
77
+ // endpoint shape. Normalize a trailing `/embeddings` or `/v1` to the server root
78
+ // so a baseURL written with an OpenAI-style suffix still yields `<root>/rerank`.
79
+ function deriveRerankUrl(baseURL) {
80
+ const root = baseURL.replace(/\/+$/, '').replace(/\/(embeddings|v1)$/, '');
81
+ return `${root}/rerank`;
82
+ }
48
83
  const resolvedSchema = zod_1.z.object({
49
84
  db: zod_1.z.object({
50
85
  host: zod_1.z.string().min(1, 'required — run rag-setup'),
51
- port: zod_1.z.number().int().positive().default(5433),
86
+ port: zod_1.z.number().int().positive().default(config_store_1.CONFIG_DEFAULTS['db.port']),
52
87
  username: zod_1.z.string().min(1, 'required — run rag-setup'),
53
88
  password: zod_1.z.string().min(1, 'required — run rag-setup'),
54
89
  database: zod_1.z.string().min(1),
55
90
  }),
56
- ollama: zod_1.z.object({
57
- host: zod_1.z.string().min(1).default('http://localhost:11434'),
58
- model: zod_1.z.string().min(1).default(constants_1.DEFAULT_EMBED_MODEL),
59
- rerankerModel: zod_1.z.string().default(''),
60
- rerankerUrl: zod_1.z.string().min(1),
91
+ embedding: zod_1.z.object({
92
+ provider: zod_1.z.enum(['ollama', 'openai-compatible', 'voyage', 'infinity']).default(config_store_1.CONFIG_DEFAULTS['embedding.provider']),
93
+ baseURL: zod_1.z.string().min(1),
94
+ apiKey: zod_1.z.string().default(config_store_1.CONFIG_DEFAULTS['embedding.apiKey']),
95
+ model: zod_1.z.string().min(1).default(config_store_1.CONFIG_DEFAULTS['embedding.model']),
96
+ dimensions: zod_1.z.number().int().positive().nullable().default(null),
97
+ batchSize: zod_1.z.number().int().positive().default(config_store_1.CONFIG_DEFAULTS['embedding.batchSize']),
98
+ }),
99
+ reranker: zod_1.z.object({
100
+ enabled: zod_1.z.boolean().default(config_store_1.CONFIG_DEFAULTS['reranker.enabled']),
101
+ provider: zod_1.z.enum(['openai-compatible']).default(config_store_1.CONFIG_DEFAULTS['reranker.provider']),
102
+ baseURL: zod_1.z.string().min(1),
103
+ apiKey: zod_1.z.string().default(config_store_1.CONFIG_DEFAULTS['reranker.apiKey']),
104
+ model: zod_1.z.string().default(config_store_1.CONFIG_DEFAULTS['reranker.model']),
105
+ url: zod_1.z.string().min(1),
106
+ overfetch: zod_1.z.number().int().min(1).max(20).default(config_store_1.CONFIG_DEFAULTS['reranker.overfetch']),
61
107
  }),
62
108
  indexer: zod_1.z.object({
63
- chunkSize: zod_1.z.number().int().positive().default(1500),
64
- chunkOverlap: zod_1.z.number().int().min(0).default(200),
65
- batchSize: zod_1.z.number().int().positive().default(5),
66
- astBatchSize: zod_1.z.number().int().positive().default(3000),
109
+ chunkSize: zod_1.z.number().int().positive().default(config_store_1.CONFIG_DEFAULTS['indexing.chunkSize']),
110
+ chunkOverlap: zod_1.z.number().int().min(0).default(config_store_1.CONFIG_DEFAULTS['indexing.chunkOverlap']),
111
+ batchSize: zod_1.z.number().int().positive().default(config_store_1.CONFIG_DEFAULTS['indexing.embedBatchSize']),
112
+ astBatchSize: zod_1.z.number().int().positive().default(config_store_1.CONFIG_DEFAULTS['indexing.astBatchSize']),
113
+ chunkStrategy: zod_1.z.enum(['ast', 'character']).default(config_store_1.CONFIG_DEFAULTS['indexing.chunkStrategy']),
67
114
  }),
68
115
  search: zod_1.z.object({
69
- similarityThreshold: zod_1.z.number().min(0).max(1).default(0.6),
70
- defaultLimit: zod_1.z.number().int().positive().default(5),
71
- maxLimit: zod_1.z.number().int().positive().default(20),
72
- autoTranslate: zod_1.z.boolean().default(false),
73
- translateModel: zod_1.z.string().default('llama3.2'),
74
- rerankerEnabled: zod_1.z.boolean().default(false),
75
- rerankerOverfetch: zod_1.z.number().int().min(1).max(20).default(3),
116
+ similarityThreshold: zod_1.z.number().min(0).max(1).default(config_store_1.CONFIG_DEFAULTS['search.similarityThreshold']),
117
+ defaultLimit: zod_1.z.number().int().positive().default(config_store_1.CONFIG_DEFAULTS['search.defaultLimit']),
118
+ maxLimit: zod_1.z.number().int().positive().default(config_store_1.CONFIG_DEFAULTS['search.maxLimit']),
76
119
  }),
77
120
  logging: zod_1.z.object({
78
- level: zod_1.z.enum(['debug', 'info', 'warn', 'error']).default('error'),
121
+ level: zod_1.z.enum(['debug', 'info', 'warn', 'error']).default(config_store_1.CONFIG_DEFAULTS['logging.level']),
79
122
  dir: zod_1.z.string().optional(),
80
123
  }),
81
124
  workspaceRoot: zod_1.z.string().default(''),
@@ -83,7 +126,16 @@ const resolvedSchema = zod_1.z.object({
83
126
  class Config {
84
127
  data;
85
128
  get db() { return this.data.db; }
86
- get ollama() { return this.data.ollama; }
129
+ /**
130
+ * Embedding config. `apiKey` is returned **raw** (possibly an `env:VAR` ref) —
131
+ * callers resolve it lazily via `resolveEnvRef` at the moment they build a
132
+ * request, so an unset referenced variable fails only when the key is actually
133
+ * needed (during embed/search) rather than crashing container construction and
134
+ * locking the user out of `rag-web`/`rag-mcp`.
135
+ */
136
+ get embedding() { return this.data.embedding; }
137
+ /** Reranker config. `apiKey` is raw — resolve lazily with `resolveEnvRef`. */
138
+ get reranker() { return this.data.reranker; }
87
139
  get indexer() { return this.data.indexer; }
88
140
  get search() { return this.data.search; }
89
141
  get logging() { return this.data.logging; }
@@ -92,7 +144,10 @@ class Config {
92
144
  const raw = (0, config_store_1.readConfigJson)();
93
145
  if (!raw)
94
146
  throw new ConfigError('Config not found. Run rag-setup.');
95
- const ollamaHost = raw.ollama?.host ?? 'http://localhost:11434';
147
+ const embProvider = (raw.embedding?.provider ?? config_store_1.CONFIG_DEFAULTS['embedding.provider']);
148
+ const embBaseURL = raw.embedding?.baseURL ?? defaultBaseURL(embProvider);
149
+ const rerankBaseURL = raw.reranker?.baseURL ?? embBaseURL;
150
+ const rerankUrl = raw.reranker?.url ?? deriveRerankUrl(rerankBaseURL);
96
151
  const result = resolvedSchema.safeParse({
97
152
  db: {
98
153
  host: raw.db?.host,
@@ -101,26 +156,34 @@ class Config {
101
156
  password: raw.db?.password,
102
157
  database: raw.db?.name ?? this.deriveDbName(),
103
158
  },
104
- ollama: {
105
- host: raw.ollama?.host,
106
- model: raw.ollama?.model,
107
- rerankerModel: raw.reranker?.model,
108
- rerankerUrl: raw.reranker?.url ?? `${ollamaHost}/api/rerank`,
159
+ embedding: {
160
+ provider: raw.embedding?.provider,
161
+ baseURL: embBaseURL,
162
+ apiKey: raw.embedding?.apiKey,
163
+ model: raw.embedding?.model,
164
+ dimensions: raw.embedding?.dimensions,
165
+ batchSize: raw.embedding?.batchSize,
166
+ },
167
+ reranker: {
168
+ enabled: raw.reranker?.enabled,
169
+ provider: raw.reranker?.provider,
170
+ baseURL: rerankBaseURL,
171
+ apiKey: raw.reranker?.apiKey,
172
+ model: raw.reranker?.model,
173
+ url: rerankUrl,
174
+ overfetch: raw.reranker?.overfetch,
109
175
  },
110
176
  indexer: {
111
177
  chunkSize: raw.indexing?.chunkSize,
112
178
  chunkOverlap: raw.indexing?.chunkOverlap,
113
179
  batchSize: raw.indexing?.embedBatchSize,
114
180
  astBatchSize: raw.indexing?.astBatchSize,
181
+ chunkStrategy: raw.indexing?.chunkStrategy,
115
182
  },
116
183
  search: {
117
184
  similarityThreshold: raw.search?.similarityThreshold,
118
185
  defaultLimit: raw.search?.defaultLimit,
119
186
  maxLimit: raw.search?.maxLimit,
120
- autoTranslate: raw.search?.autoTranslate,
121
- translateModel: raw.search?.translateModel,
122
- rerankerEnabled: raw.reranker?.enabled,
123
- rerankerOverfetch: raw.reranker?.overfetch,
124
187
  },
125
188
  logging: {
126
189
  level: raw.logging?.level,
@@ -1 +1,36 @@
1
+ export type EmbeddingProvider = 'ollama' | 'openai-compatible' | 'voyage' | 'infinity';
2
+ /**
3
+ * pgvector's HNSW / IVFFlat index hard limit. The effective embedding dimension
4
+ * must not exceed this; larger cloud models must reduce via the Matryoshka
5
+ * `dimensions` request parameter (`embedding.dimensions`).
6
+ */
7
+ export declare const MAX_EMBED_DIM = 2000;
8
+ export interface ModelInfo {
9
+ provider: EmbeddingProvider;
10
+ /** Native / default output width. */
11
+ dimensions: number;
12
+ /** Model supports Matryoshka dimension reduction via a request parameter. */
13
+ dimConfigurable?: boolean;
14
+ /** Max width that can be indexed under the pgvector cap (advisory). */
15
+ maxIndexable?: number;
16
+ /** Max width the model itself can emit (advisory). */
17
+ maxDim?: number;
18
+ }
19
+ /**
20
+ * Advisory (NOT exhaustive) registry of known embedding models. Used to resolve
21
+ * a model's dimension without a runtime probe and to suggest models in the
22
+ * dashboard. Models absent here still work — their dimension is probed at index
23
+ * time (see `resolveDimension`).
24
+ */
25
+ export declare const MODEL_REGISTRY: Record<string, ModelInfo>;
1
26
  export declare const DEFAULT_EMBED_MODEL = "bge-m3";
27
+ /** Advisory dimension for a model, or `undefined` when the model is unknown. */
28
+ export declare function registryDimension(model: string): number | undefined;
29
+ /**
30
+ * Resolve the effective vector dimension: explicit override → registry → probe.
31
+ * Validates the result is ≤ MAX_EMBED_DIM, throwing an error that names
32
+ * `embedding.dimensions` as the fix. `probeFn` embeds a short string once and
33
+ * returns the observed length; only called when neither override nor registry
34
+ * yields a value. Pure aside from the injected probe.
35
+ */
36
+ export declare function resolveDimension(override: number | null | undefined, model: string, probeFn: () => Promise<number>, registry?: Record<string, ModelInfo>): Promise<number>;
@@ -1,7 +1,60 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_EMBED_MODEL = void 0;
4
- // Embedding model is tied to the vector column dimension (bge-m3 → 1024).
5
- // Change both together: update this constant, drop/recreate the `embedding` column,
6
- // and re-run indexing.
3
+ exports.DEFAULT_EMBED_MODEL = exports.MODEL_REGISTRY = exports.MAX_EMBED_DIM = void 0;
4
+ exports.registryDimension = registryDimension;
5
+ exports.resolveDimension = resolveDimension;
6
+ /**
7
+ * pgvector's HNSW / IVFFlat index hard limit. The effective embedding dimension
8
+ * must not exceed this; larger cloud models must reduce via the Matryoshka
9
+ * `dimensions` request parameter (`embedding.dimensions`).
10
+ */
11
+ exports.MAX_EMBED_DIM = 2000;
12
+ /**
13
+ * Advisory (NOT exhaustive) registry of known embedding models. Used to resolve
14
+ * a model's dimension without a runtime probe and to suggest models in the
15
+ * dashboard. Models absent here still work — their dimension is probed at index
16
+ * time (see `resolveDimension`).
17
+ */
18
+ exports.MODEL_REGISTRY = {
19
+ 'bge-m3': { provider: 'ollama', dimensions: 1024 },
20
+ 'nomic-embed-text': { provider: 'ollama', dimensions: 768 },
21
+ 'mxbai-embed-large': { provider: 'ollama', dimensions: 1024 },
22
+ 'text-embedding-3-small': { provider: 'openai-compatible', dimensions: 1536, dimConfigurable: true },
23
+ 'text-embedding-3-large': { provider: 'openai-compatible', dimensions: 3072, dimConfigurable: true, maxIndexable: 2000 },
24
+ 'text-embedding-ada-002': { provider: 'openai-compatible', dimensions: 1536 },
25
+ 'voyage-code-3': { provider: 'voyage', dimensions: 1024, dimConfigurable: true, maxDim: 2048 },
26
+ 'voyage-3': { provider: 'voyage', dimensions: 1024, dimConfigurable: true },
27
+ 'voyage-3-lite': { provider: 'voyage', dimensions: 512 },
28
+ 'BAAI/bge-m3': { provider: 'infinity', dimensions: 1024 },
29
+ 'jinaai/jina-embeddings-v2-base-code': { provider: 'infinity', dimensions: 768 },
30
+ };
7
31
  exports.DEFAULT_EMBED_MODEL = 'bge-m3';
32
+ /** Advisory dimension for a model, or `undefined` when the model is unknown. */
33
+ function registryDimension(model) {
34
+ return exports.MODEL_REGISTRY[model]?.dimensions;
35
+ }
36
+ /**
37
+ * Resolve the effective vector dimension: explicit override → registry → probe.
38
+ * Validates the result is ≤ MAX_EMBED_DIM, throwing an error that names
39
+ * `embedding.dimensions` as the fix. `probeFn` embeds a short string once and
40
+ * returns the observed length; only called when neither override nor registry
41
+ * yields a value. Pure aside from the injected probe.
42
+ */
43
+ async function resolveDimension(override, model, probeFn, registry = exports.MODEL_REGISTRY) {
44
+ let dim;
45
+ if (override != null) {
46
+ dim = override;
47
+ }
48
+ else {
49
+ const known = registry[model]?.dimensions;
50
+ dim = known != null ? known : await probeFn();
51
+ }
52
+ if (!Number.isInteger(dim) || dim <= 0) {
53
+ throw new Error(`Could not resolve a valid embedding dimension for model "${model}" (got ${dim}). Set embedding.dimensions explicitly.`);
54
+ }
55
+ if (dim > exports.MAX_EMBED_DIM) {
56
+ throw new Error(`Embedding dimension ${dim} for model "${model}" exceeds the pgvector limit of ${exports.MAX_EMBED_DIM}. ` +
57
+ `Set embedding.dimensions to a value ≤ ${exports.MAX_EMBED_DIM} to request dimension-reduced (Matryoshka) vectors.`);
58
+ }
59
+ return dim;
60
+ }
@@ -1,15 +1,19 @@
1
1
  import { Config } from './config';
2
- import { DatabaseService } from './services/DatabaseService';
3
2
  import { IndexerService } from './services/IndexerService';
4
- import { SearcherService } from './services/SearcherService';
5
3
  import { McpServer } from './services/McpServer';
6
- import { LoggerService } from './services/LoggerService';
4
+ import type { IDatabase } from './interfaces';
5
+ import type { IEmbedder } from './interfaces';
6
+ import type { ISearcher } from './interfaces';
7
+ import type { ILogger } from './interfaces';
8
+ import type { IProjectDiscovery } from './interfaces';
7
9
  export interface Container {
8
10
  config: Config;
9
- db: DatabaseService;
11
+ db: IDatabase;
12
+ embedder: IEmbedder;
10
13
  indexer: IndexerService;
11
- searcher: SearcherService;
14
+ searcher: ISearcher;
12
15
  mcp: McpServer;
13
- logger: LoggerService;
16
+ logger: ILogger;
17
+ discovery: IProjectDiscovery;
14
18
  }
15
19
  export declare function createContainer(loggerName?: string): Container;
@@ -3,20 +3,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createContainer = createContainer;
4
4
  const config_1 = require("./config");
5
5
  const DatabaseService_1 = require("./services/DatabaseService");
6
- const OllamaEmbedder_1 = require("./services/OllamaEmbedder");
6
+ const EmbedderFactory_1 = require("./services/EmbedderFactory");
7
7
  const AstParserService_1 = require("./services/AstParserService");
8
+ const AstChunker_1 = require("./services/AstChunker");
8
9
  const IndexerService_1 = require("./services/IndexerService");
9
10
  const SearcherService_1 = require("./services/SearcherService");
10
11
  const McpServer_1 = require("./services/McpServer");
11
12
  const LoggerService_1 = require("./services/LoggerService");
13
+ const MonorepoAwareDiscoveryService_1 = require("./services/MonorepoAwareDiscoveryService");
14
+ const NxMonorepoResolver_1 = require("./services/NxMonorepoResolver");
12
15
  function createContainer(loggerName) {
13
16
  const config = new config_1.Config();
14
17
  const logger = LoggerService_1.LoggerService.fromConfig(config.logging, loggerName);
15
18
  const db = new DatabaseService_1.DatabaseService(config);
16
- const embedder = new OllamaEmbedder_1.OllamaEmbedder(config);
19
+ const embedder = (0, EmbedderFactory_1.createEmbedder)(config, logger);
17
20
  const ast = new AstParserService_1.AstParserService(logger);
18
- const indexer = new IndexerService_1.IndexerService(db, embedder, ast, config, logger);
19
- const searcher = new SearcherService_1.SearcherService(db, embedder, config, logger);
21
+ const chunker = new AstChunker_1.AstChunker(config);
22
+ const discovery = new MonorepoAwareDiscoveryService_1.MonorepoAwareDiscoveryService([new NxMonorepoResolver_1.NxMonorepoResolver(logger)], logger);
23
+ const indexer = new IndexerService_1.IndexerService(db, embedder, ast, chunker, config, logger);
24
+ const searcher = new SearcherService_1.SearcherService(db, embedder, discovery, config, logger);
20
25
  const mcp = new McpServer_1.McpServer(searcher, config, logger);
21
- return { config, db, indexer, searcher, mcp, logger };
26
+ return { config, db, embedder, indexer, searcher, mcp, logger, discovery };
22
27
  }
@@ -1,2 +1,2 @@
1
- export { codeChunk, codeDependency, codeSymbol, codeCall } from './schema';
1
+ export { codeChunk, codeDependency, codeSymbol, codeCall, indexMeta } from './schema';
2
2
  export type { CodeChunkInsert } from './schema';
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.codeCall = exports.codeSymbol = exports.codeDependency = exports.codeChunk = void 0;
3
+ exports.indexMeta = exports.codeCall = exports.codeSymbol = exports.codeDependency = exports.codeChunk = void 0;
4
4
  var schema_1 = require("./schema");
5
5
  Object.defineProperty(exports, "codeChunk", { enumerable: true, get: function () { return schema_1.codeChunk; } });
6
6
  Object.defineProperty(exports, "codeDependency", { enumerable: true, get: function () { return schema_1.codeDependency; } });
7
7
  Object.defineProperty(exports, "codeSymbol", { enumerable: true, get: function () { return schema_1.codeSymbol; } });
8
8
  Object.defineProperty(exports, "codeCall", { enumerable: true, get: function () { return schema_1.codeCall; } });
9
+ Object.defineProperty(exports, "indexMeta", { enumerable: true, get: function () { return schema_1.indexMeta; } });
@@ -142,6 +142,25 @@ export declare const codeChunk: import("drizzle-orm/pg-core").PgTableWithColumns
142
142
  }, {}, {
143
143
  length: 64;
144
144
  }>;
145
+ ownerPath: import("drizzle-orm/pg-core").PgColumn<{
146
+ name: "ownerPath";
147
+ tableName: "code_chunk";
148
+ dataType: "string";
149
+ columnType: "PgVarchar";
150
+ data: string;
151
+ driverParam: string;
152
+ notNull: false;
153
+ hasDefault: false;
154
+ isPrimaryKey: false;
155
+ isAutoincrement: false;
156
+ hasRuntimeDefault: false;
157
+ enumValues: [string, ...string[]];
158
+ baseColumn: never;
159
+ identity: undefined;
160
+ generated: undefined;
161
+ }, {}, {
162
+ length: number | undefined;
163
+ }>;
145
164
  };
146
165
  dialect: "pg";
147
166
  }>;
@@ -574,4 +593,96 @@ export declare const codeCall: import("drizzle-orm/pg-core").PgTableWithColumns<
574
593
  };
575
594
  dialect: "pg";
576
595
  }>;
596
+ export declare const indexMeta: import("drizzle-orm/pg-core").PgTableWithColumns<{
597
+ name: "index_meta";
598
+ schema: undefined;
599
+ columns: {
600
+ id: import("drizzle-orm/pg-core").PgColumn<{
601
+ name: "id";
602
+ tableName: "index_meta";
603
+ dataType: "boolean";
604
+ columnType: "PgBoolean";
605
+ data: boolean;
606
+ driverParam: boolean;
607
+ notNull: true;
608
+ hasDefault: true;
609
+ isPrimaryKey: true;
610
+ isAutoincrement: false;
611
+ hasRuntimeDefault: false;
612
+ enumValues: undefined;
613
+ baseColumn: never;
614
+ identity: undefined;
615
+ generated: undefined;
616
+ }, {}, {}>;
617
+ provider: import("drizzle-orm/pg-core").PgColumn<{
618
+ name: "provider";
619
+ tableName: "index_meta";
620
+ dataType: "string";
621
+ columnType: "PgText";
622
+ data: string;
623
+ driverParam: string;
624
+ notNull: true;
625
+ hasDefault: false;
626
+ isPrimaryKey: false;
627
+ isAutoincrement: false;
628
+ hasRuntimeDefault: false;
629
+ enumValues: [string, ...string[]];
630
+ baseColumn: never;
631
+ identity: undefined;
632
+ generated: undefined;
633
+ }, {}, {}>;
634
+ model: import("drizzle-orm/pg-core").PgColumn<{
635
+ name: "model";
636
+ tableName: "index_meta";
637
+ dataType: "string";
638
+ columnType: "PgText";
639
+ data: string;
640
+ driverParam: string;
641
+ notNull: true;
642
+ hasDefault: false;
643
+ isPrimaryKey: false;
644
+ isAutoincrement: false;
645
+ hasRuntimeDefault: false;
646
+ enumValues: [string, ...string[]];
647
+ baseColumn: never;
648
+ identity: undefined;
649
+ generated: undefined;
650
+ }, {}, {}>;
651
+ dimensions: import("drizzle-orm/pg-core").PgColumn<{
652
+ name: "dimensions";
653
+ tableName: "index_meta";
654
+ dataType: "number";
655
+ columnType: "PgInteger";
656
+ data: number;
657
+ driverParam: string | number;
658
+ notNull: true;
659
+ hasDefault: false;
660
+ isPrimaryKey: false;
661
+ isAutoincrement: false;
662
+ hasRuntimeDefault: false;
663
+ enumValues: undefined;
664
+ baseColumn: never;
665
+ identity: undefined;
666
+ generated: undefined;
667
+ }, {}, {}>;
668
+ indexedAt: import("drizzle-orm/pg-core").PgColumn<{
669
+ name: "indexedAt";
670
+ tableName: "index_meta";
671
+ dataType: "date";
672
+ columnType: "PgTimestamp";
673
+ data: Date;
674
+ driverParam: string;
675
+ notNull: true;
676
+ hasDefault: true;
677
+ isPrimaryKey: false;
678
+ isAutoincrement: false;
679
+ hasRuntimeDefault: false;
680
+ enumValues: undefined;
681
+ baseColumn: never;
682
+ identity: undefined;
683
+ generated: undefined;
684
+ }, {}, {}>;
685
+ };
686
+ dialect: "pg";
687
+ }>;
577
688
  export type CodeChunkInsert = typeof codeChunk.$inferInsert;
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.codeCall = exports.codeSymbol = exports.codeDependency = exports.codeChunk = void 0;
3
+ exports.indexMeta = exports.codeCall = exports.codeSymbol = exports.codeDependency = exports.codeChunk = void 0;
4
4
  const pg_core_1 = require("drizzle-orm/pg-core");
5
+ // NOTE: the `dimensions` literal below is nominal only. `dataType()` is invoked
6
+ // solely by drizzle-kit DDL generation, which this project does not use — the
7
+ // `code_chunk` table is created by raw SQL in DatabaseService at the dimension
8
+ // resolved from the active model, and the real width is recorded in index_meta.
5
9
  const vector = (0, pg_core_1.customType)({
6
10
  dataType(config) { return `vector(${config?.dimensions ?? 1024})`; },
7
11
  });
@@ -14,6 +18,7 @@ exports.codeChunk = (0, pg_core_1.pgTable)('code_chunk', {
14
18
  embedding: vector('embedding', { dimensions: 1024 }).notNull(),
15
19
  lastModified: (0, pg_core_1.doublePrecision)('lastModified'),
16
20
  contentHash: (0, pg_core_1.varchar)('contentHash', { length: 64 }),
21
+ ownerPath: (0, pg_core_1.varchar)('ownerPath'),
17
22
  });
18
23
  exports.codeDependency = (0, pg_core_1.pgTable)('code_dependency', {
19
24
  id: (0, pg_core_1.uuid)('id').primaryKey().defaultRandom(),
@@ -45,3 +50,12 @@ exports.codeCall = (0, pg_core_1.pgTable)('code_call', {
45
50
  line: (0, pg_core_1.integer)('line'),
46
51
  col: (0, pg_core_1.integer)('col'),
47
52
  });
53
+ // Single-row source of truth for which model built the current index. The
54
+ // `id` column is a boolean pinned to `true` so at most one row can exist.
55
+ exports.indexMeta = (0, pg_core_1.pgTable)('index_meta', {
56
+ id: (0, pg_core_1.boolean)('id').primaryKey().default(true),
57
+ provider: (0, pg_core_1.text)('provider').notNull(),
58
+ model: (0, pg_core_1.text)('model').notNull(),
59
+ dimensions: (0, pg_core_1.integer)('dimensions').notNull(),
60
+ indexedAt: (0, pg_core_1.timestamp)('indexedAt', { withTimezone: true }).defaultNow().notNull(),
61
+ });
@@ -1,4 +1,64 @@
1
+ import { type ProjectMeta } from './file-utils';
2
+ type ProjectNode = {
3
+ name: string;
4
+ nxRepo?: boolean;
5
+ } & ProjectMeta;
6
+ type Edge = {
7
+ from: string;
8
+ to: string;
9
+ label: string;
10
+ };
11
+ export interface GraphData {
12
+ nodes: ProjectNode[];
13
+ edges: Edge[];
14
+ }
15
+ /** A single named graph tab (Workspace, or one per nested Nx repo). */
16
+ export interface GraphView {
17
+ name: string;
18
+ nodes: ProjectNode[];
19
+ edges: Edge[];
20
+ }
21
+ /**
22
+ * Build the project graph data (nodes + relation edges) from a project list.
23
+ * Pure apart from reading each project's `.ragmeta.json`. Shared by the console
24
+ * graph, the graph views, and the web dashboard's `/api/graph`.
25
+ */
26
+ export declare function buildGraphData(projects: {
27
+ name: string;
28
+ path: string;
29
+ }[]): GraphData;
30
+ /**
31
+ * Split a project list into graph tabs. A project whose name contains a `/`
32
+ * belongs to a nested Nx repo (`<repo>/<sub-project>`); everything else is a
33
+ * flat top-level project.
34
+ *
35
+ * - **Workspace** tab: flat projects + one node per Nx repo (collapsed to a
36
+ * single `nxRepo` node); edges from `.ragmeta.json` relations.
37
+ * - **One tab per Nx repo**: its sub-projects as nodes, with edges between them
38
+ * taken from real import dependencies (`projectEdges`, from `code_dependency`).
39
+ *
40
+ * Returns the Workspace view first, then repo views sorted by name.
41
+ */
42
+ export declare function buildGraphViews(projects: {
43
+ name: string;
44
+ path: string;
45
+ }[], projectEdges: {
46
+ from: string;
47
+ to: string;
48
+ }[], workspaceRoot: string): GraphView[];
49
+ /**
50
+ * Render the full interactive graph page (vis-network + inner tabs, edge-type
51
+ * filters, legend, project toggle list). Used for the standalone
52
+ * `project-graph.html` and served by the web dashboard at `GET /graph` (embedded
53
+ * via iframe) so both share one implementation. Tab 0 is the Workspace view;
54
+ * double-clicking an Nx-repo node opens that repo's tab.
55
+ */
56
+ export declare function buildHtml(views: GraphView[]): string;
1
57
  export declare function generateProjectGraph(projects: {
2
58
  name: string;
3
59
  path: string;
60
+ }[], projectEdges: {
61
+ from: string;
62
+ to: string;
4
63
  }[], workspacePath: string): void;
64
+ export {};