codesearch-mcp 0.0.15-alpha → 0.0.17-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 (39) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +74 -33
  3. package/dist/app.d.ts +1 -1
  4. package/dist/app.js +67 -16
  5. package/dist/src/commands/BackfillCommand.js +15 -5
  6. package/dist/src/commands/ConfigCommand.d.ts +12 -0
  7. package/dist/src/commands/ConfigCommand.js +98 -0
  8. package/dist/src/commands/IndexCommand.js +1 -1
  9. package/dist/src/config-store.d.ts +53 -0
  10. package/dist/src/config-store.js +197 -0
  11. package/dist/src/config.d.ts +22 -8
  12. package/dist/src/config.js +103 -58
  13. package/dist/src/container.d.ts +1 -1
  14. package/dist/src/container.js +8 -7
  15. package/dist/src/db/index.d.ts +2 -3
  16. package/dist/src/db/index.js +6 -7
  17. package/dist/src/db/schema.d.ts +577 -0
  18. package/dist/src/db/schema.js +47 -0
  19. package/dist/src/help.js +28 -12
  20. package/dist/src/interfaces/IAstParser.d.ts +19 -0
  21. package/dist/src/interfaces/IEmbedder.d.ts +1 -0
  22. package/dist/src/project-selector.d.ts +14 -5
  23. package/dist/src/project-selector.js +74 -60
  24. package/dist/src/services/AstParserService.d.ts +7 -0
  25. package/dist/src/services/AstParserService.js +191 -28
  26. package/dist/src/services/DatabaseService.d.ts +11 -4
  27. package/dist/src/services/DatabaseService.js +107 -48
  28. package/dist/src/services/IndexerService.d.ts +9 -0
  29. package/dist/src/services/IndexerService.js +224 -60
  30. package/dist/src/services/LoggerService.d.ts +5 -1
  31. package/dist/src/services/LoggerService.js +8 -5
  32. package/dist/src/services/McpServer.d.ts +1 -0
  33. package/dist/src/services/McpServer.js +74 -4
  34. package/dist/src/services/OllamaEmbedder.d.ts +2 -0
  35. package/dist/src/services/OllamaEmbedder.js +29 -0
  36. package/dist/src/services/SearcherService.d.ts +13 -0
  37. package/dist/src/services/SearcherService.js +118 -22
  38. package/dist/src/setup.js +57 -62
  39. package/package.json +4 -5
package/CHANGELOG.md CHANGED
@@ -3,6 +3,59 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  Format based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
5
 
6
+ ## [0.0.17-alpha] - 2026-05-13
7
+
8
+ ### Added
9
+
10
+ - **`get_callers` MCP tool** — finds all call sites of a function or method by exact name. Uses the `code_call` table populated by `rag-index backfill --calls`. Requires both `symbol` and `project`; optional `callee_file` to disambiguate overloaded names. Returns empty if backfill has not been run.
11
+
12
+ - **Call graph indexing** — `code_call` table records per-call-site entries `(project, fromFile, fromSymbol, calleeName, calleeFile, calleeSymbol, line, col)`. Populated during normal indexing (`ingestFile`) and via `rag-index backfill --calls` for existing installations. `AstParserService` extracts call expressions using ts-morph `CallExpression` nodes.
13
+
14
+ - **`analyzeProject` `skipDependencyResolution` option** — `IAstParser.analyzeProject` now accepts `opts?: { skipDependencyResolution?: boolean }`. `AstParserService.createMorphProject` propagates the flag to ts-morph `Project` options. Used by `IndexerService` for large projects to avoid OOM.
15
+
16
+ - **Configurable AST batch size (`indexing.astBatchSize`)** — `IndexerService` splits large projects into ts-morph batches. Threshold and batch size are now controlled by `indexing.astBatchSize` (default `3000`, configurable via `rag-config set indexing.astBatchSize <N>`). Projects above the threshold use `skipDependencyResolution: true` and print a console warning about potentially incomplete cross-batch dependency edges.
17
+
18
+ - **`include_graph_context` parameter for `search_codebase`** — appends direct imports and dependents inline with each result (`Imports: X · Y` / `Used by: Z`), eliminating separate `get_dependencies` / `get_dependents` calls for basic context. Single batch SQL query (UNION ALL + VALUES) for all result files — no N+1. Deduplicates via Sets; limits to 50 entries per direction per file.
19
+
20
+ - **Cross-encoder reranking** — optional second-pass via `POST /api/rerank` (requires Ollama ≥ 0.6). Controlled by `reranker.enabled`, `reranker.model`, `reranker.url` (defaults to `${ollama.host}/api/rerank`), `reranker.overfetch`. When enabled, `search_codebase` overfetches `limit × reranker.overfetch` vector candidates and reranks before returning `limit` results. Falls back gracefully to vector order on error (`warn` log). `rerank?` is an optional method on `IEmbedder` — `OllamaEmbedder` assigns it only when both `reranker.enabled=true` and `reranker.model` is set.
21
+
22
+ - **`ProjectSelector` unit tests** — full test coverage for project selection workflows including first-run auto-select, `--select` flag, `.ragprojects` / `.ragskip` persistence
23
+
24
+ - **`rag-config` command** — manage `~/.config/codesearch-mcp/config.json` from the CLI: `rag-config show`, `rag-config get <key>`, `rag-config set <key> <value>`, `rag-config reset <key>`. Supports all config keys with type coercion (string / number / boolean).
25
+
26
+ ### Changed
27
+
28
+ - **Config migrated from `.env` to `config.json`** — configuration is now stored in `~/.config/codesearch-mcp/config.json` instead of a `.env` file. `rag-setup` writes JSON; `dotenv` dependency removed. Existing `.env` files are no longer read — run `rag-setup` once to migrate.
29
+
30
+ - **`Config` class replaces module-level singleton** — `src/config.ts` now exports a `Config` class instantiated in `createContainer()`. Reads `config.json` via `readConfigJson()`, Zod-validates, throws `ConfigError` on missing required fields (caught in `app.ts` with a user-friendly message).
31
+
32
+ - **`LoggerService.fromConfig(cfg, name?)`** replaces `fromEnv()` — takes the `logging` sub-object from `Config` directly instead of reading `LOG_LEVEL` / `LOG_DIR` from `process.env`.
33
+
34
+ - **`reranker.*` and `search.*` config keys** — `RERANKER_ENABLED` and `RERANKER_OVERFETCH` in `config.search`; `config.ollama` retains only `rerankerModel` and `rerankerUrl`
35
+ - **`fetchLimit` capped at 200** — prevents runaway DB queries when `reranker.overfetch` is high
36
+ - **Reranker index validation** — indices from cross-encoder filtered for bounds before mapping
37
+ - **30s timeout + truncated error body on rerank HTTP call** — `AbortSignal.timeout(30_000)`; error body sliced to 200 chars
38
+ - **Enriched embedding context** — chunks now include `Project`, `Role` (inferred from file path), `Symbols` (overlapping definitions), and `Dependencies` in the embedding prompt. Improves semantic search quality for architectural queries. **Requires full re-index** (`rag-index --reindex`).
39
+ - **`ProjectSelector` refactored** — improved modularity and maintainability; extracted logic into smaller focused methods
40
+ - **`IndexCommand` argument validation** — enhanced error handling and validation across CLI modes
41
+
42
+ ## [0.0.16-alpha] - 2026-05-02
43
+
44
+ ### Added
45
+
46
+ - **Hybrid incremental indexing (mtime + SHA-256)** — `rag-index` now uses a two-step skip check per file: fast `mtime` comparison first (no file read), then SHA-256 content hash if `mtime` changed. Files with identical content but reset timestamps (after `git clone`, DB transfer, or `touch`) are skipped without re-embedding. `lastModified` is refreshed in a single batch SQL `UPDATE` after each project scan. `--reindex` clears both fields.
47
+ - `contentHash varchar(64)` column added to `code_chunk`; applied automatically via `ALTER TABLE ... ADD COLUMN IF NOT EXISTS` on startup — no manual migration needed.
48
+
49
+ ### Changed
50
+
51
+ - **Migrated from TypeORM to Drizzle ORM** — schema management and queries rewritten with Drizzle API; TypeORM decorators and metadata removed
52
+ - **Batch AST parsing (`analyzeProject`)** — `IndexerService` now parses all project files in a single `MorphProject` instance instead of one per file, reducing ts-morph overhead on large projects
53
+ - **Enriched embedding context** — chunks now include `[Symbols: class Foo, function bar]` tag when the chunk overlaps known symbol definitions, improving semantic search quality for partial/truncated chunks. **Requires full re-index** (`rag-index /path --reindex`) — existing embeddings were built without symbol context and will produce degraded similarity scores against new chunks if mixed.
54
+ - **`endLine` added to `SymbolEntry`** — enables accurate chunk-to-symbol overlap detection used by enriched prompts
55
+ - **`analyzeProject` error isolation** — parse failures are caught per-file; one broken file no longer aborts the entire project scan
56
+ - **Memory cleanup in `analyzeProject`** — source files are explicitly removed from `MorphProject` after parsing to release memory
57
+ - **Logging improvements** — `AstParser: analyzeProject done` debug log reports total files, parsed count, and elapsed ms; per-file `depsMs` renamed to `astMs` (now ~0 since AST work is done upfront)
58
+
6
59
  ## [0.0.15-alpha] - 2026-05-02
7
60
 
8
61
  ### Changed
package/README.md CHANGED
@@ -6,13 +6,15 @@ Ask Claude things like *"where is the payment retry logic?"* or *"show me how au
6
6
 
7
7
  > **Alpha** — works well locally, API may change before 1.0.
8
8
 
9
+ See `CHANGELOG.md` for version history.
10
+
9
11
  ## How it works
10
12
 
11
13
  1. `rag-index` scans your projects, chunks source files, embeds them with [bge-m3](https://huggingface.co/BAAI/bge-m3) via Ollama, and stores vectors in PostgreSQL
12
14
  2. `rag-mcp` exposes an MCP server — Claude calls it automatically when you ask about code
13
15
  3. Incremental — only changed files are re-indexed on subsequent runs
14
16
 
15
- **Stack:** TypeScript · TypeORM · PostgreSQL + pgvector · Ollama (bge-m3) · LangChain text splitters · ts-morph
17
+ **Stack:** TypeScript · Drizzle ORM · PostgreSQL + pgvector · Ollama (bge-m3) · LangChain text splitters · ts-morph
16
18
 
17
19
  ## Requirements
18
20
 
@@ -32,24 +34,24 @@ rag-setup
32
34
  ```
33
35
 
34
36
  Walks you through:
35
- - **Local Docker** — copies `docker-compose.yml`, starts PostgreSQL + Ollama, pulls `bge-m3` automatically
36
- - **Remote DB** — enter connection details for Supabase, Railway, or self-hosted; connection is verified before saving
37
+ - **Database** — enter connection details; optionally starts Docker containers automatically
38
+ - **Ollama** — configure API URL
37
39
  - **Logging** — choose log level and directory (defaults: `error` level, `~/.config/codesearch-mcp/logs/`)
38
40
 
39
- Config is saved to `~/.config/codesearch-mcp/.env`.
41
+ Config is saved to `~/.config/codesearch-mcp/config.json`.
40
42
 
41
43
  ## Index your projects
42
44
 
43
45
  ```bash
44
- rag-index /home/user/Projects
46
+ rag-index
45
47
  ```
46
48
 
47
- On first run you will be prompted to select which projects to index. Selections are saved in `.ragprojects` / `.ragskip` at the workspace root.
49
+ `workspaceRoot` (set by `rag-setup` or `rag-config set workspaceRoot /path`) determines which directory is scanned. On first run — or when no projects are selected — the selector launches automatically. Selections are saved in `.ragprojects` / `.ragskip` at the workspace root.
48
50
 
49
51
  ```bash
50
- rag-index /path --select # re-run project selection
51
- rag-index /path --reindex # force re-embed all files (use after model change)
52
- rag-index /path --select --reindex
52
+ rag-index --select # re-run project selection
53
+ rag-index --reindex # force re-embed all files (use after model change)
54
+ rag-index --select --reindex
53
55
  ```
54
56
 
55
57
  ## Register with Claude Code
@@ -64,14 +66,15 @@ Then ask Claude anything about your code — it will use the tools below automat
64
66
 
65
67
  | Tool | Description |
66
68
  |------|-------------|
67
- | `search_codebase` | Semantic search across all indexed projects |
69
+ | `search_codebase` | Semantic search across all indexed projects. Accepts `include_graph_context` to append direct imports/dependents to each result, and `include_neighbors` for surrounding chunks |
68
70
  | `list_projects` | List projects with metadata and chunk counts |
69
71
  | `find_symbol` | Find a function, class, or type by exact name — faster than semantic search |
70
72
  | `list_files` | Browse the directory tree of a project |
71
73
  | `read_file` | Read a complete file by absolute path |
72
74
  | `read_file_range` | Read a specific line range from an indexed file |
73
- | `get_dependencies` | Show what files a given file imports |
74
- | `get_dependents` | Show which files import a given file |
75
+ | `get_dependencies` | Show what files a given file imports (direct or transitive) |
76
+ | `get_dependents` | Show which files import a given file (direct or transitive) |
77
+ | `get_callers` | Find all call sites of a function or method by exact name |
75
78
 
76
79
  ## Project metadata
77
80
 
@@ -90,6 +93,39 @@ Add `.ragmeta.json` to a project root to enrich `list_projects` and generate a v
90
93
 
91
94
  After each indexing run an interactive `project-graph.html` is written to the workspace root.
92
95
 
96
+ ## Graph context
97
+
98
+ Pass `include_graph_context: true` to `search_codebase` to see direct imports and dependents inline with each result — without extra `get_dependencies` / `get_dependents` calls:
99
+
100
+ ```
101
+ **File:** src/services/InvoiceService.ts **Line:** 45
102
+ ...code...
103
+ **Relevance:** 0.32
104
+ **Imports:** src/repositories/InvoiceRepo.ts · src/dto/CreateInvoiceDto.ts
105
+ **Used by:** src/controllers/InvoiceController.ts
106
+ ```
107
+
108
+ Useful for immediately understanding a file's role in the dependency graph.
109
+
110
+ ## Cross-encoder reranking
111
+
112
+ By default, results are ordered by cosine distance (vector similarity). Enable cross-encoder reranking to reorder candidates by semantic relevance — better for queries where the exact wording differs from how the code is written.
113
+
114
+ Requires Ollama ≥ 0.6 and a reranker model:
115
+
116
+ ```bash
117
+ ollama pull qllama/bge-reranker-v2-m3
118
+ ```
119
+
120
+ ```bash
121
+ rag-config set reranker.enabled true
122
+ rag-config set reranker.model qllama/bge-reranker-v2-m3
123
+ # rag-config set reranker.url http://localhost:11434/api/rerank # default
124
+ # rag-config set reranker.overfetch 3 # fetch limit×3 candidates before reranking
125
+ ```
126
+
127
+ When enabled, `search_codebase` fetches `limit × reranker.overfetch` vector candidates, then the cross-encoder reranks them and returns the top `limit`. Falls back gracefully to vector order if the reranker is unreachable.
128
+
93
129
  ## Excluding files
94
130
 
95
131
  Create `.ragignore` in a project root — same syntax as `.gitignore`:
@@ -110,31 +146,36 @@ tail -f ~/.config/codesearch-mcp/logs/indexer/current.log
110
146
  npx pino-pretty < ~/.config/codesearch-mcp/logs/indexer/current.log
111
147
 
112
148
  # Detailed indexing diagnostics (timing per file: deps / embed / db)
113
- LOG_LEVEL=debug rag-index /path/to/workspace
149
+ rag-config set logging.level debug
114
150
 
115
151
  # Disable file logging
116
- LOG_DIR=false rag-index /path/to/workspace
152
+ rag-config set logging.dir false
117
153
  ```
118
154
 
119
- ## Environment variables
120
-
121
- All set by `rag-setup`. Override any of them in `~/.config/codesearch-mcp/.env`.
122
-
123
- | Variable | Default | Description |
124
- |----------|---------|-------------|
125
- | `DB_HOST` | — | PostgreSQL host |
126
- | `DB_PORT` | `5433` | PostgreSQL port |
127
- | `DB_USER` | — | PostgreSQL user |
128
- | `DB_PASS` | — | PostgreSQL password |
129
- | `DB_NAME` | `rag_<os-username>` | Database name — auto-created on first run |
130
- | `WORKSPACE_ROOT` | — | Root of your projects directory |
131
- | `OLLAMA_HOST` | `http://localhost:11434` | Ollama API URL |
132
- | `OLLAMA_MODEL` | `bge-m3` | Embedding model |
133
- | `SIMILARITY_THRESHOLD` | `0.6` | Max cosine distance — raise if search returns nothing |
134
- | `AUTO_TRANSLATE` | `false` | Translate non-English queries to English before embedding |
135
- | `TRANSLATE_MODEL` | `llama3.2` | Ollama model for translation (requires `ollama pull llama3.2`) |
136
- | `LOG_LEVEL` | `error` | Log level written to file: `error` \| `warn` \| `info` \| `debug` |
137
- | `LOG_DIR` | `~/.config/codesearch-mcp/logs` | Base log directory, or `false` to disable |
155
+ ## Configuration
156
+
157
+ All set by `rag-setup`. Override individual keys with `rag-config set <key> <value>`. Stored in `~/.config/codesearch-mcp/config.json`.
158
+
159
+ | Key | Default | Description |
160
+ |-----|---------|-------------|
161
+ | `db.host` | — | PostgreSQL host |
162
+ | `db.port` | `5433` | PostgreSQL port |
163
+ | `db.user` | — | PostgreSQL user |
164
+ | `db.password` | — | PostgreSQL password |
165
+ | `db.name` | `rag_<os-username>` | Database name — auto-created on first run |
166
+ | `workspaceRoot` | — | Root of your projects directory |
167
+ | `ollama.host` | `http://localhost:11434` | Ollama API URL |
168
+ | `ollama.model` | `bge-m3` | Embedding model |
169
+ | `search.similarityThreshold` | `0.6` | Max cosine distance — raise if search returns nothing |
170
+ | `search.autoTranslate` | `false` | Translate non-English queries to English before embedding |
171
+ | `search.translateModel` | `llama3.2` | Ollama model for translation (requires `ollama pull llama3.2`) |
172
+ | `indexing.astBatchSize` | `3000` | Max files per ts-morph batch — lower if indexing large projects causes OOM |
173
+ | `reranker.enabled` | `false` | Enable cross-encoder reranking (requires Ollama ≥ 0.6 and `reranker.model`) |
174
+ | `reranker.model` | — | Reranker model name (e.g. `qllama/bge-reranker-v2-m3`) |
175
+ | `reranker.url` | `${ollama.host}/api/rerank` | Override rerank endpoint URL |
176
+ | `reranker.overfetch` | `3` | Candidate multiplier before reranking (`limit × N`, max 20) |
177
+ | `logging.level` | `error` | Log level written to file: `error` \| `warn` \| `info` \| `debug` |
178
+ | `logging.dir` | `~/.config/codesearch-mcp/logs` | Base log directory, or `"false"` to disable |
138
179
 
139
180
  ## License
140
181
 
package/dist/app.d.ts CHANGED
@@ -1 +1 @@
1
- import './src/env-loader';
1
+ export {};
package/dist/app.js CHANGED
@@ -34,38 +34,89 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  };
35
35
  })();
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
- require("./src/env-loader");
38
37
  const path = __importStar(require("path"));
39
- const container_1 = require("./src/container");
40
38
  const help_1 = require("./src/help");
39
+ const container_1 = require("./src/container");
40
+ const config_1 = require("./src/config");
41
41
  const IndexCommand_1 = require("./src/commands/IndexCommand");
42
42
  const McpCommand_1 = require("./src/commands/McpCommand");
43
- const SetupCommand_1 = require("./src/commands/SetupCommand");
44
43
  const BackfillCommand_1 = require("./src/commands/BackfillCommand");
44
+ const ConfigCommand_1 = require("./src/commands/ConfigCommand");
45
+ const setup_1 = require("./src/setup");
45
46
  async function main() {
46
47
  const argv1 = path.basename(process.argv[1] ?? '');
47
- const isIndex = argv1 === 'rag-index' || process.argv[2] === 'index';
48
- const isMcp = argv1 === 'rag-mcp' || process.argv[2] === 'mcp';
49
- const isSetup = argv1 === 'rag-setup' || process.argv[2] === 'setup';
50
- const isBackfill = (argv1 === 'rag-index' || process.argv[2] === 'backfill') && process.argv[2] === 'backfill';
51
- const mode = isBackfill ? 'backfill' : isIndex ? 'index' : isMcp ? 'mcp' : isSetup ? 'setup' : (process.argv[2] ?? '');
48
+ const isIndex = argv1 === 'rag-index' || process.argv[2] === 'index' || process.argv[2] === 'rag-index';
49
+ const isMcp = argv1 === 'rag-mcp' || process.argv[2] === 'mcp' || process.argv[2] === 'rag-mcp';
50
+ const isSetup = argv1 === 'rag-setup' || process.argv[2] === 'setup' || process.argv[2] === 'rag-setup';
51
+ const isConfig = argv1 === 'rag-config' || process.argv[2] === 'config' || process.argv[2] === 'rag-config';
52
+ const isBackfill = process.argv[2] === 'backfill' ||
53
+ (process.argv[2] === 'rag-index' && process.argv[3] === 'backfill');
54
+ const mode = isBackfill ? 'backfill' : isIndex ? 'index' : isMcp ? 'mcp' : isSetup ? 'setup' : isConfig ? 'config' : (process.argv[2] ?? '');
55
+ const isBinary = ['rag-index', 'rag-mcp', 'rag-setup', 'rag-config'].includes(argv1);
52
56
  if (process.argv.includes('--help') || process.argv.includes('-h')) {
53
- (0, help_1.printHelp)(argv1 !== 'app.js' ? argv1 : mode);
57
+ (0, help_1.printHelp)(isBinary ? argv1 : mode);
54
58
  process.exit(0);
55
59
  }
56
- const loggerName = mode === 'index' || mode === 'backfill' ? 'indexer' : mode === 'mcp' ? 'mcp' : mode === 'setup' ? 'setup' : undefined;
57
- const container = (0, container_1.createContainer)(loggerName);
58
- if (!container.config.workspaceRoot && mode !== 'setup') {
60
+ const effectiveArgs = process.argv.slice(isBinary ? 2 : 3);
61
+ // Arg validation
62
+ if (mode === 'index') {
63
+ const knownFlags = new Set(['--reindex', '--select']);
64
+ const unknown = effectiveArgs.filter(a => !a.startsWith('-') || !knownFlags.has(a));
65
+ if (unknown.length > 0) {
66
+ console.error(`Unexpected argument: ${unknown[0]}\n`);
67
+ (0, help_1.printHelp)(isBinary ? argv1 : 'rag-index');
68
+ process.exit(1);
69
+ }
70
+ }
71
+ else if (mode === 'mcp' || mode === 'setup') {
72
+ if (effectiveArgs.length > 0) {
73
+ console.error(`Unexpected argument: ${effectiveArgs[0]}\n`);
74
+ (0, help_1.printHelp)(isBinary ? argv1 : `rag-${mode}`);
75
+ process.exit(1);
76
+ }
77
+ }
78
+ else if (mode === 'backfill') {
79
+ const knownBackfillFlags = new Set(['--calls']);
80
+ const backfillIdx = process.argv.indexOf('backfill');
81
+ const backfillArgs = backfillIdx >= 0 ? process.argv.slice(backfillIdx + 1) : [];
82
+ const unknown = backfillArgs.filter(a => !knownBackfillFlags.has(a));
83
+ if (unknown.length > 0) {
84
+ console.error(`Unexpected argument: ${unknown[0]}\n`);
85
+ (0, help_1.printHelp)('rag-index');
86
+ process.exit(1);
87
+ }
88
+ }
89
+ if (mode === 'setup') {
90
+ await (0, setup_1.runSetup)();
91
+ process.exit(0);
92
+ }
93
+ if (mode === 'config') {
94
+ await new ConfigCommand_1.ConfigCommand(effectiveArgs).execute();
95
+ process.exit(0);
96
+ }
97
+ // All other modes require a valid config.json
98
+ const loggerName = mode === 'index' || mode === 'backfill' ? 'indexer' : mode === 'mcp' ? 'mcp' : undefined;
99
+ let container;
100
+ try {
101
+ container = (0, container_1.createContainer)(loggerName);
102
+ }
103
+ catch (e) {
104
+ if (e instanceof config_1.ConfigError) {
105
+ console.error(e.message);
106
+ process.exit(1);
107
+ }
108
+ throw e;
109
+ }
110
+ if (!container.config.workspaceRoot) {
59
111
  const hint = argv1 === 'rag-mcp'
60
- ? 'Set WORKSPACE_ROOT in ~/.config/codesearch-mcp/.env or run `rag-setup`.'
61
- : 'Run `rag-setup` to configure WORKSPACE_ROOT.';
62
- console.error(`WORKSPACE_ROOT is not configured. ${hint}`);
112
+ ? 'Run rag-setup or: rag-config set workspaceRoot /path/to/projects'
113
+ : 'Run rag-setup to configure workspaceRoot.';
114
+ console.error(`workspaceRoot is not configured. ${hint}`);
63
115
  process.exit(1);
64
116
  }
65
117
  const commands = {
66
118
  index: new IndexCommand_1.IndexCommand(container, argv1),
67
119
  mcp: new McpCommand_1.McpCommand(container),
68
- setup: new SetupCommand_1.SetupCommand(container),
69
120
  backfill: new BackfillCommand_1.BackfillCommand(container),
70
121
  };
71
122
  const cmd = commands[mode];
@@ -10,14 +10,24 @@ class BackfillCommand {
10
10
  async execute() {
11
11
  const { db, indexer } = this.container;
12
12
  await db.initDbWithSchema();
13
+ const doCalls = process.argv.includes('--calls');
13
14
  const controller = new AbortController();
14
15
  const teardown = (0, interrupt_1.setupInterrupt)(controller);
15
16
  try {
16
- console.log('Backfilling symbol index...\n');
17
- const stats = await indexer.backfillSymbols(controller.signal);
18
- if (controller.signal.aborted)
19
- console.log('\nBackfill interrupted.');
20
- console.log(`\nDone. Total: ${stats.total}, Processed: ${stats.processed}, Skipped (not on disk): ${stats.skipped}, Failed: ${stats.failed}`);
17
+ if (doCalls) {
18
+ console.log('Backfilling call graph...\n');
19
+ const stats = await indexer.backfillCalls(controller.signal);
20
+ if (controller.signal.aborted)
21
+ console.log('\nBackfill interrupted.');
22
+ console.log(`\nDone. Total: ${stats.total}, Processed: ${stats.processed}, Skipped (not on disk): ${stats.skipped}, Failed: ${stats.failed}`);
23
+ }
24
+ else {
25
+ console.log('Backfilling symbol index...\n');
26
+ const stats = await indexer.backfillSymbols(controller.signal);
27
+ if (controller.signal.aborted)
28
+ console.log('\nBackfill interrupted.');
29
+ console.log(`\nDone. Total: ${stats.total}, Processed: ${stats.processed}, Skipped (not on disk): ${stats.skipped}, Failed: ${stats.failed}`);
30
+ }
21
31
  }
22
32
  finally {
23
33
  teardown();
@@ -0,0 +1,12 @@
1
+ import { ALL_KEYS } from '../config-store';
2
+ import type { ICommand } from './ICommand';
3
+ export declare class ConfigCommand implements ICommand {
4
+ private readonly args;
5
+ constructor(args: string[]);
6
+ execute(): Promise<void>;
7
+ private show;
8
+ private get;
9
+ private set;
10
+ private reset;
11
+ }
12
+ export { ALL_KEYS };
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ALL_KEYS = exports.ConfigCommand = void 0;
4
+ const config_store_1 = require("../config-store");
5
+ Object.defineProperty(exports, "ALL_KEYS", { enumerable: true, get: function () { return config_store_1.ALL_KEYS; } });
6
+ const help_1 = require("../help");
7
+ class ConfigCommand {
8
+ args;
9
+ constructor(args) {
10
+ this.args = args;
11
+ }
12
+ async execute() {
13
+ const [sub, key, value] = this.args;
14
+ switch (sub) {
15
+ case 'show': return this.show();
16
+ case 'get': return this.get(key);
17
+ case 'set': return this.set(key, value);
18
+ case 'reset': return this.reset(key);
19
+ default:
20
+ if (sub)
21
+ console.error(`Unknown subcommand: ${sub}\n`);
22
+ (0, help_1.printHelp)('rag-config');
23
+ process.exit(sub ? 1 : 0);
24
+ }
25
+ }
26
+ show() {
27
+ let cfg = null;
28
+ try {
29
+ cfg = (0, config_store_1.readConfigJson)();
30
+ }
31
+ catch (e) {
32
+ console.error(e.message);
33
+ process.exit(1);
34
+ }
35
+ if (!cfg) {
36
+ console.log(`No config found at ${config_store_1.CONFIG_FILE}. Run rag-setup.`);
37
+ return;
38
+ }
39
+ // redact password for display
40
+ const display = JSON.parse(JSON.stringify(cfg));
41
+ if (display.db?.password !== undefined)
42
+ display.db.password = '***';
43
+ console.log(JSON.stringify(display, null, 2));
44
+ }
45
+ get(key) {
46
+ if (!key) {
47
+ console.error('Usage: rag-config get <key>');
48
+ process.exit(1);
49
+ }
50
+ let cfg = null;
51
+ try {
52
+ cfg = (0, config_store_1.readConfigJson)();
53
+ }
54
+ catch (e) {
55
+ console.error(e.message);
56
+ process.exit(1);
57
+ }
58
+ if (!cfg) {
59
+ console.log('(no config)');
60
+ return;
61
+ }
62
+ const val = (0, config_store_1.getNestedValue)(cfg, key);
63
+ console.log(val === undefined ? '(not set)' : String(val));
64
+ }
65
+ set(key, value) {
66
+ if (!key || value === undefined) {
67
+ console.error('Usage: rag-config set <key> <value>');
68
+ process.exit(1);
69
+ }
70
+ try {
71
+ const cfg = (0, config_store_1.readConfigJson)() ?? {};
72
+ const updated = (0, config_store_1.setNestedValue)(cfg, key, value);
73
+ (0, config_store_1.writeConfigJson)(updated);
74
+ console.log(`${key} = ${value}`);
75
+ }
76
+ catch (e) {
77
+ console.error(e.message);
78
+ process.exit(1);
79
+ }
80
+ }
81
+ reset(key) {
82
+ if (!key) {
83
+ console.error('Usage: rag-config reset <key>');
84
+ process.exit(1);
85
+ }
86
+ try {
87
+ const cfg = (0, config_store_1.readConfigJson)() ?? {};
88
+ const updated = (0, config_store_1.deleteNestedValue)(cfg, key);
89
+ (0, config_store_1.writeConfigJson)(updated);
90
+ console.log(`${key} reset to default`);
91
+ }
92
+ catch (e) {
93
+ console.error(e.message);
94
+ process.exit(1);
95
+ }
96
+ }
97
+ }
98
+ exports.ConfigCommand = ConfigCommand;
@@ -22,7 +22,7 @@ class IndexCommand {
22
22
  const teardown = (0, interrupt_1.setupInterrupt)(controller);
23
23
  try {
24
24
  const discovered = indexer.discoverProjects(config.workspaceRoot);
25
- const projects = await (0, project_selector_1.selectProjects)(discovered, config.workspaceRoot, forceSelect);
25
+ const projects = await new project_selector_1.ProjectSelector(config.workspaceRoot).select(discovered, forceSelect);
26
26
  if (projects.length === 0)
27
27
  return;
28
28
  const filesPerProject = projects.map(p => (0, file_utils_1.collectFiles)(p.path, (0, file_utils_1.loadIgnore)(p.path), p.path));
@@ -0,0 +1,53 @@
1
+ import { z } from 'zod';
2
+ export declare const CONFIG_DIR: string;
3
+ export declare const CONFIG_FILE: string;
4
+ declare const jsonSchema: z.ZodObject<{
5
+ db: z.ZodOptional<z.ZodObject<{
6
+ host: z.ZodOptional<z.ZodString>;
7
+ port: z.ZodOptional<z.ZodNumber>;
8
+ user: z.ZodOptional<z.ZodString>;
9
+ password: z.ZodOptional<z.ZodString>;
10
+ name: z.ZodOptional<z.ZodString>;
11
+ }, z.core.$strip>>;
12
+ ollama: z.ZodOptional<z.ZodObject<{
13
+ host: z.ZodOptional<z.ZodString>;
14
+ model: z.ZodOptional<z.ZodString>;
15
+ }, z.core.$strip>>;
16
+ indexing: z.ZodOptional<z.ZodObject<{
17
+ chunkSize: z.ZodOptional<z.ZodNumber>;
18
+ chunkOverlap: z.ZodOptional<z.ZodNumber>;
19
+ embedBatchSize: z.ZodOptional<z.ZodNumber>;
20
+ astBatchSize: z.ZodOptional<z.ZodNumber>;
21
+ }, z.core.$strip>>;
22
+ search: z.ZodOptional<z.ZodObject<{
23
+ similarityThreshold: z.ZodOptional<z.ZodNumber>;
24
+ defaultLimit: z.ZodOptional<z.ZodNumber>;
25
+ maxLimit: z.ZodOptional<z.ZodNumber>;
26
+ autoTranslate: z.ZodOptional<z.ZodBoolean>;
27
+ translateModel: z.ZodOptional<z.ZodString>;
28
+ }, z.core.$strip>>;
29
+ reranker: z.ZodOptional<z.ZodObject<{
30
+ enabled: z.ZodOptional<z.ZodBoolean>;
31
+ model: z.ZodOptional<z.ZodString>;
32
+ url: z.ZodOptional<z.ZodString>;
33
+ overfetch: z.ZodOptional<z.ZodNumber>;
34
+ }, z.core.$strip>>;
35
+ logging: z.ZodOptional<z.ZodObject<{
36
+ level: z.ZodOptional<z.ZodEnum<{
37
+ error: "error";
38
+ debug: "debug";
39
+ info: "info";
40
+ warn: "warn";
41
+ }>>;
42
+ dir: z.ZodOptional<z.ZodString>;
43
+ }, z.core.$strip>>;
44
+ workspaceRoot: z.ZodOptional<z.ZodString>;
45
+ }, z.core.$strip>;
46
+ export type JsonConfig = z.infer<typeof jsonSchema>;
47
+ export declare const ALL_KEYS: string[];
48
+ export declare function readConfigJson(): JsonConfig | null;
49
+ export declare function writeConfigJson(data: JsonConfig): void;
50
+ export declare function getNestedValue(cfg: JsonConfig, key: string): unknown;
51
+ export declare function setNestedValue(cfg: JsonConfig, key: string, raw: string): JsonConfig;
52
+ export declare function deleteNestedValue(cfg: JsonConfig, key: string): JsonConfig;
53
+ export {};