codesearch-mcp 0.0.1-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.
- package/README.md +129 -0
- package/dist/app.d.ts +1 -0
- package/dist/app.js +124 -0
- package/dist/src/ast.d.ts +2 -0
- package/dist/src/ast.js +60 -0
- package/dist/src/config.d.ts +26 -0
- package/dist/src/config.js +60 -0
- package/dist/src/db/data-source.d.ts +2 -0
- package/dist/src/db/data-source.js +13 -0
- package/dist/src/db/entities/code-chunk.entity.d.ts +9 -0
- package/dist/src/db/entities/code-chunk.entity.js +54 -0
- package/dist/src/db/entities/code-dependency.entity.d.ts +9 -0
- package/dist/src/db/entities/code-dependency.entity.js +54 -0
- package/dist/src/db/index.d.ts +4 -0
- package/dist/src/db/index.js +12 -0
- package/dist/src/db/init.d.ts +2 -0
- package/dist/src/db/init.js +50 -0
- package/dist/src/env-loader.d.ts +1 -0
- package/dist/src/env-loader.js +41 -0
- package/dist/src/file-utils.d.ts +12 -0
- package/dist/src/file-utils.js +97 -0
- package/dist/src/graph.d.ts +4 -0
- package/dist/src/graph.js +259 -0
- package/dist/src/indexer.d.ts +13 -0
- package/dist/src/indexer.js +217 -0
- package/dist/src/interrupt.d.ts +3 -0
- package/dist/src/interrupt.js +27 -0
- package/dist/src/mcp.d.ts +1 -0
- package/dist/src/mcp.js +248 -0
- package/dist/src/project-selector.d.ts +7 -0
- package/dist/src/project-selector.js +116 -0
- package/dist/src/searcher.d.ts +50 -0
- package/dist/src/searcher.js +205 -0
- package/dist/src/setup.d.ts +1 -0
- package/dist/src/setup.js +145 -0
- package/dist/vitest.config.d.ts +2 -0
- package/dist/vitest.config.js +9 -0
- package/docker/docker-compose.yml +19 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# codesearch-mcp
|
|
2
|
+
|
|
3
|
+
Semantic codebase search for Claude. Indexes your local projects into a PostgreSQL vector database and exposes search via the Model Context Protocol (MCP).
|
|
4
|
+
|
|
5
|
+
Ask Claude things like *"where is the payment retry logic?"* or *"show me how auth tokens are validated"* — across all your projects at once.
|
|
6
|
+
|
|
7
|
+
> **Alpha** — works well locally, API may change before 1.0.
|
|
8
|
+
|
|
9
|
+
## How it works
|
|
10
|
+
|
|
11
|
+
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
|
+
2. `rag-mcp` exposes an MCP server — Claude calls it automatically when you ask about code
|
|
13
|
+
3. Incremental — only changed files are re-indexed on subsequent runs
|
|
14
|
+
|
|
15
|
+
**Stack:** TypeScript · TypeORM · PostgreSQL + pgvector · Ollama (bge-m3) · LangChain text splitters · ts-morph
|
|
16
|
+
|
|
17
|
+
## Requirements
|
|
18
|
+
|
|
19
|
+
- Node.js 18+
|
|
20
|
+
- Docker (for local DB + Ollama) — or bring your own PostgreSQL with pgvector + Ollama
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g codesearch-mcp
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Setup
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
rag-setup
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
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
|
+
|
|
38
|
+
Config is saved to `~/.config/codesearch-mcp/.env`.
|
|
39
|
+
|
|
40
|
+
## Index your projects
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
rag-index /home/user/Projects
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
On first run you will be prompted for each discovered project:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Found 5 new project(s). Select which to index:
|
|
50
|
+
y = yes | n = skip this time | s = always skip
|
|
51
|
+
|
|
52
|
+
myapp [y/n/s]: y
|
|
53
|
+
legacy-app [y/n/s]: s
|
|
54
|
+
→ added to .ragskip
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Selections are saved in `.ragprojects` / `.ragskip` at the workspace root. Use `--select` to revisit them.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
rag-index /path --select # re-run project selection
|
|
61
|
+
rag-index /path --reindex # force re-embed all files (use after model change)
|
|
62
|
+
rag-index /path --select --reindex
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Register with Claude Code
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
claude mcp add codesearch-mcp -s user -- rag-mcp
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Then ask Claude anything about your code — it will use `search_codebase`, `list_projects`, `read_file_range`, `get_dependencies`, and `get_dependents` automatically.
|
|
72
|
+
|
|
73
|
+
## MCP Tools
|
|
74
|
+
|
|
75
|
+
| Tool | Description |
|
|
76
|
+
|------|-------------|
|
|
77
|
+
| `search_codebase` | Semantic search across all indexed projects |
|
|
78
|
+
| `list_projects` | List projects with metadata and chunk counts |
|
|
79
|
+
| `read_file_range` | Read a specific line range from an indexed file |
|
|
80
|
+
| `get_dependencies` | Show what files a given file imports |
|
|
81
|
+
| `get_dependents` | Show which files import a given file |
|
|
82
|
+
|
|
83
|
+
## Project metadata
|
|
84
|
+
|
|
85
|
+
Add `.ragmeta.json` to a project root to enrich `list_projects` and generate a visual dependency graph:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"description": "Main API server",
|
|
90
|
+
"stack": "NestJS, GraphQL, PostgreSQL",
|
|
91
|
+
"relations": {
|
|
92
|
+
"consumed_by": ["crm-frontend"],
|
|
93
|
+
"consumes": ["billing-service", "auth-service"]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
After each indexing run an interactive `project-graph.html` is written to the workspace root.
|
|
99
|
+
|
|
100
|
+
## Excluding files
|
|
101
|
+
|
|
102
|
+
Create `.ragignore` in a project root — same syntax as `.gitignore`:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
src/gql/graphql.ts
|
|
106
|
+
**/__snapshots__/**
|
|
107
|
+
src/generated/
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Environment variables
|
|
111
|
+
|
|
112
|
+
All set by `rag-setup`. Override any of them in `~/.config/codesearch-mcp/.env`.
|
|
113
|
+
|
|
114
|
+
| Variable | Default | Description |
|
|
115
|
+
|----------|---------|-------------|
|
|
116
|
+
| `DB_HOST` | — | PostgreSQL host |
|
|
117
|
+
| `DB_PORT` | `5433` | PostgreSQL port |
|
|
118
|
+
| `DB_USER` | — | PostgreSQL user |
|
|
119
|
+
| `DB_PASS` | — | PostgreSQL password |
|
|
120
|
+
| `DB_NAME` | — | Database name |
|
|
121
|
+
| `WORKSPACE_ROOT` | — | Root of your projects directory |
|
|
122
|
+
| `OLLAMA_HOST` | `http://localhost:11434` | Ollama API URL |
|
|
123
|
+
| `OLLAMA_MODEL` | `bge-m3` | Embedding model |
|
|
124
|
+
| `SIMILARITY_THRESHOLD` | `0.6` | Max cosine distance — raise if search returns nothing |
|
|
125
|
+
| `AUTO_TRANSLATE` | `false` | Translate non-English queries to English before embedding |
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
MIT
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './src/env-loader';
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
require("./src/env-loader");
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const db_1 = require("./src/db");
|
|
40
|
+
const indexer_1 = require("./src/indexer");
|
|
41
|
+
const interrupt_1 = require("./src/interrupt");
|
|
42
|
+
const project_selector_1 = require("./src/project-selector");
|
|
43
|
+
const mcp_1 = require("./src/mcp");
|
|
44
|
+
const graph_1 = require("./src/graph");
|
|
45
|
+
function printSummary(allStats) {
|
|
46
|
+
const cols = { name: 30, num: 8 };
|
|
47
|
+
const line = '─'.repeat(cols.name + cols.num * 5 + 5);
|
|
48
|
+
console.log('\n=== Indexing Summary ===');
|
|
49
|
+
console.log('Service'.padEnd(cols.name), 'Indexed'.padStart(cols.num), 'Skipped'.padStart(cols.num), 'Failed'.padStart(cols.num), 'Chunks'.padStart(cols.num), 'Time(s)'.padStart(cols.num));
|
|
50
|
+
console.log(line);
|
|
51
|
+
for (const s of allStats) {
|
|
52
|
+
console.log(s.serviceName.padEnd(cols.name), String(s.indexed).padStart(cols.num), String(s.skipped).padStart(cols.num), String(s.failed).padStart(cols.num), String(s.chunks).padStart(cols.num), String(s.elapsedSec).padStart(cols.num));
|
|
53
|
+
}
|
|
54
|
+
console.log(line);
|
|
55
|
+
const total = allStats.reduce((acc, s) => ({
|
|
56
|
+
indexed: acc.indexed + s.indexed,
|
|
57
|
+
skipped: acc.skipped + s.skipped,
|
|
58
|
+
failed: acc.failed + s.failed,
|
|
59
|
+
chunks: acc.chunks + s.chunks,
|
|
60
|
+
elapsedSec: acc.elapsedSec + s.elapsedSec,
|
|
61
|
+
}), { indexed: 0, skipped: 0, failed: 0, chunks: 0, elapsedSec: 0 });
|
|
62
|
+
console.log('TOTAL'.padEnd(cols.name), String(total.indexed).padStart(cols.num), String(total.skipped).padStart(cols.num), String(total.failed).padStart(cols.num), String(total.chunks).padStart(cols.num), String(total.elapsedSec.toFixed(1)).padStart(cols.num));
|
|
63
|
+
}
|
|
64
|
+
async function main() {
|
|
65
|
+
const argv1 = path.basename(process.argv[1] ?? '');
|
|
66
|
+
const isMcp = argv1 === 'rag-mcp' || process.argv[2] === 'mcp';
|
|
67
|
+
const isSetup = argv1 === 'rag-setup' || process.argv[2] === 'setup';
|
|
68
|
+
const mode = isMcp ? 'mcp' : isSetup ? 'setup' : (process.argv[2] ?? '');
|
|
69
|
+
if (mode === 'index') {
|
|
70
|
+
const args = process.argv.slice(argv1 === 'rag-index' ? 2 : 3);
|
|
71
|
+
const workspacePath = args.find(a => !a.startsWith('--'));
|
|
72
|
+
const force = args.includes('--reindex');
|
|
73
|
+
const forceSelect = args.includes('--select');
|
|
74
|
+
if (!workspacePath) {
|
|
75
|
+
console.error('Usage: rag-index <path> [--reindex] [--select]');
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
process.env.WORKSPACE_ROOT ??= workspacePath;
|
|
79
|
+
await (0, db_1.initDbWithSchema)();
|
|
80
|
+
(0, interrupt_1.setupInterrupt)();
|
|
81
|
+
const discovered = (0, indexer_1.discoverProjects)(workspacePath);
|
|
82
|
+
const projects = await (0, project_selector_1.selectProjects)(discovered, workspacePath, forceSelect);
|
|
83
|
+
if (projects.length === 0) {
|
|
84
|
+
(0, interrupt_1.teardownInterrupt)();
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
const allStats = [];
|
|
88
|
+
if (force)
|
|
89
|
+
console.log('Reindex mode: all files will be re-indexed.');
|
|
90
|
+
for (const proj of projects) {
|
|
91
|
+
if (interrupt_1.signal.aborted)
|
|
92
|
+
break;
|
|
93
|
+
const stats = await (0, indexer_1.scanAndIndex)(proj.path, proj.name, force, interrupt_1.signal);
|
|
94
|
+
allStats.push(stats);
|
|
95
|
+
}
|
|
96
|
+
(0, interrupt_1.teardownInterrupt)();
|
|
97
|
+
if (interrupt_1.signal.aborted)
|
|
98
|
+
console.log('\nIndexing interrupted.');
|
|
99
|
+
printSummary(allStats);
|
|
100
|
+
if (!interrupt_1.signal.aborted)
|
|
101
|
+
(0, graph_1.generateProjectGraph)(projects, workspacePath);
|
|
102
|
+
process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
else if (mode === 'mcp') {
|
|
105
|
+
if (!process.env.WORKSPACE_ROOT) {
|
|
106
|
+
console.error('WORKSPACE_ROOT is required for MCP mode. Set it in ~/.config/codesearch-mcp/.env');
|
|
107
|
+
console.error('Run `rag-setup` to configure.');
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
await (0, db_1.initDb)();
|
|
111
|
+
await (0, mcp_1.startMCP)();
|
|
112
|
+
}
|
|
113
|
+
else if (mode === 'setup') {
|
|
114
|
+
const { runSetup } = await Promise.resolve().then(() => __importStar(require('./src/setup')));
|
|
115
|
+
await runSetup();
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
console.log('Usage: rag-index <path> [--reindex] [--select]');
|
|
119
|
+
console.log(' rag-mcp');
|
|
120
|
+
console.log(' rag-setup');
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
main().catch(err => { console.error(err); process.exit(1); });
|
package/dist/src/ast.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractImportEdges = extractImportEdges;
|
|
4
|
+
const ts_morph_1 = require("ts-morph");
|
|
5
|
+
// Single ts-morph Project instance for import graph extraction.
|
|
6
|
+
// skipFileDependencyResolution: false is required so getModuleSpecifierSourceFile() resolves relative imports.
|
|
7
|
+
const morphProject = new ts_morph_1.Project({
|
|
8
|
+
skipAddingFilesFromTsConfig: true,
|
|
9
|
+
skipFileDependencyResolution: false,
|
|
10
|
+
compilerOptions: { allowJs: true, noEmit: true },
|
|
11
|
+
});
|
|
12
|
+
// Extract import edges from a TS/JS file using ts-morph.
|
|
13
|
+
// Only resolves relative imports (external packages are skipped).
|
|
14
|
+
// Removes the source file from morphProject after parsing to prevent memory accumulation.
|
|
15
|
+
async function extractImportEdges(filePath, serviceName) {
|
|
16
|
+
// Only process JS/TS files; skip declaration files
|
|
17
|
+
if (!/\.(ts|tsx|js|jsx)$/.test(filePath) || /\.d\.ts$/.test(filePath))
|
|
18
|
+
return [];
|
|
19
|
+
// Remove stale cached version before re-adding
|
|
20
|
+
const existing = morphProject.getSourceFile(filePath);
|
|
21
|
+
if (existing)
|
|
22
|
+
morphProject.removeSourceFile(existing);
|
|
23
|
+
let sourceFile;
|
|
24
|
+
try {
|
|
25
|
+
sourceFile = morphProject.addSourceFileAtPath(filePath);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return []; // file disappeared between stat and parse
|
|
29
|
+
}
|
|
30
|
+
const edges = [];
|
|
31
|
+
for (const decl of sourceFile.getImportDeclarations()) {
|
|
32
|
+
const resolved = decl.getModuleSpecifierSourceFile();
|
|
33
|
+
if (!resolved)
|
|
34
|
+
continue; // external package or unresolvable
|
|
35
|
+
const toFile = resolved.getFilePath();
|
|
36
|
+
if (toFile.includes('/node_modules/'))
|
|
37
|
+
continue; // skip package type declarations
|
|
38
|
+
const namedImports = decl.getNamedImports();
|
|
39
|
+
const defaultImport = decl.getDefaultImport();
|
|
40
|
+
const namespaceImport = decl.getNamespaceImport();
|
|
41
|
+
if (namedImports.length > 0) {
|
|
42
|
+
for (const named of namedImports) {
|
|
43
|
+
edges.push({ project: serviceName, fromFile: filePath, fromSymbol: null, toFile, toSymbol: named.getName(), depType: 'import' });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else if (defaultImport) {
|
|
47
|
+
edges.push({ project: serviceName, fromFile: filePath, fromSymbol: null, toFile, toSymbol: null, depType: 'import-default' });
|
|
48
|
+
}
|
|
49
|
+
else if (namespaceImport) {
|
|
50
|
+
edges.push({ project: serviceName, fromFile: filePath, fromSymbol: null, toFile, toSymbol: null, depType: 'import-namespace' });
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// Side-effect import: import './foo'
|
|
54
|
+
edges.push({ project: serviceName, fromFile: filePath, fromSymbol: null, toFile, toSymbol: null, depType: 'import' });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Remove from memory — prevents accumulation across large workspaces
|
|
58
|
+
morphProject.removeSourceFile(sourceFile);
|
|
59
|
+
return edges;
|
|
60
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const config: {
|
|
2
|
+
db: {
|
|
3
|
+
host: string;
|
|
4
|
+
port: number;
|
|
5
|
+
username: string;
|
|
6
|
+
password: string;
|
|
7
|
+
database: string;
|
|
8
|
+
};
|
|
9
|
+
ollama: {
|
|
10
|
+
host: string;
|
|
11
|
+
model: string;
|
|
12
|
+
};
|
|
13
|
+
indexer: {
|
|
14
|
+
chunkSize: number;
|
|
15
|
+
chunkOverlap: number;
|
|
16
|
+
batchSize: number;
|
|
17
|
+
};
|
|
18
|
+
search: {
|
|
19
|
+
similarityThreshold: number;
|
|
20
|
+
defaultLimit: number;
|
|
21
|
+
maxLimit: number;
|
|
22
|
+
autoTranslate: boolean;
|
|
23
|
+
translateModel: string;
|
|
24
|
+
};
|
|
25
|
+
workspaceRoot: string;
|
|
26
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.config = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const schema = zod_1.z.object({
|
|
6
|
+
// required — no defaults, process exits if missing
|
|
7
|
+
DB_HOST: zod_1.z.string(),
|
|
8
|
+
DB_PORT: zod_1.z.coerce.number().default(5433),
|
|
9
|
+
DB_USER: zod_1.z.string(),
|
|
10
|
+
DB_PASS: zod_1.z.string(),
|
|
11
|
+
DB_NAME: zod_1.z.string(),
|
|
12
|
+
// optional with defaults
|
|
13
|
+
OLLAMA_HOST: zod_1.z.string().url().default('http://localhost:11434'),
|
|
14
|
+
OLLAMA_MODEL: zod_1.z.string().default('bge-m3'),
|
|
15
|
+
CHUNK_SIZE: zod_1.z.coerce.number().default(1500),
|
|
16
|
+
CHUNK_OVERLAP: zod_1.z.coerce.number().default(200),
|
|
17
|
+
EMBED_BATCH_SIZE: zod_1.z.coerce.number().default(5),
|
|
18
|
+
SIMILARITY_THRESHOLD: zod_1.z.coerce.number().default(0.6),
|
|
19
|
+
SEARCH_DEFAULT_LIMIT: zod_1.z.coerce.number().default(5),
|
|
20
|
+
SEARCH_MAX_LIMIT: zod_1.z.coerce.number().default(20),
|
|
21
|
+
// auto-translate non-English queries to English before embedding
|
|
22
|
+
AUTO_TRANSLATE: zod_1.z.string().default('false').transform(v => v === 'true'),
|
|
23
|
+
TRANSLATE_MODEL: zod_1.z.string().default('llama3.2'),
|
|
24
|
+
WORKSPACE_ROOT: zod_1.z.string().optional(),
|
|
25
|
+
});
|
|
26
|
+
const parsed = schema.safeParse(process.env);
|
|
27
|
+
if (!parsed.success) {
|
|
28
|
+
console.error('Invalid or missing config:');
|
|
29
|
+
for (const [key, errors] of Object.entries(parsed.error.flatten().fieldErrors)) {
|
|
30
|
+
console.error(` ${key}: ${errors.join(', ')}`);
|
|
31
|
+
}
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
const env = parsed.data;
|
|
35
|
+
exports.config = {
|
|
36
|
+
db: {
|
|
37
|
+
host: env.DB_HOST,
|
|
38
|
+
port: env.DB_PORT,
|
|
39
|
+
username: env.DB_USER,
|
|
40
|
+
password: env.DB_PASS,
|
|
41
|
+
database: env.DB_NAME,
|
|
42
|
+
},
|
|
43
|
+
ollama: {
|
|
44
|
+
host: env.OLLAMA_HOST,
|
|
45
|
+
model: env.OLLAMA_MODEL,
|
|
46
|
+
},
|
|
47
|
+
indexer: {
|
|
48
|
+
chunkSize: env.CHUNK_SIZE,
|
|
49
|
+
chunkOverlap: env.CHUNK_OVERLAP,
|
|
50
|
+
batchSize: env.EMBED_BATCH_SIZE,
|
|
51
|
+
},
|
|
52
|
+
search: {
|
|
53
|
+
similarityThreshold: env.SIMILARITY_THRESHOLD,
|
|
54
|
+
defaultLimit: env.SEARCH_DEFAULT_LIMIT,
|
|
55
|
+
maxLimit: env.SEARCH_MAX_LIMIT,
|
|
56
|
+
autoTranslate: env.AUTO_TRANSLATE,
|
|
57
|
+
translateModel: env.TRANSLATE_MODEL,
|
|
58
|
+
},
|
|
59
|
+
workspaceRoot: env.WORKSPACE_ROOT ?? '',
|
|
60
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.db = void 0;
|
|
4
|
+
const typeorm_1 = require("typeorm");
|
|
5
|
+
const config_1 = require("../config");
|
|
6
|
+
const code_chunk_entity_1 = require("./entities/code-chunk.entity");
|
|
7
|
+
const code_dependency_entity_1 = require("./entities/code-dependency.entity");
|
|
8
|
+
exports.db = new typeorm_1.DataSource({
|
|
9
|
+
type: 'postgres',
|
|
10
|
+
...config_1.config.db,
|
|
11
|
+
entities: [code_chunk_entity_1.CodeChunk, code_dependency_entity_1.CodeDependency],
|
|
12
|
+
synchronize: false,
|
|
13
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.CodeChunk = void 0;
|
|
13
|
+
const typeorm_1 = require("typeorm");
|
|
14
|
+
let CodeChunk = class CodeChunk {
|
|
15
|
+
id;
|
|
16
|
+
serviceName;
|
|
17
|
+
filePath;
|
|
18
|
+
startLine;
|
|
19
|
+
content;
|
|
20
|
+
embedding;
|
|
21
|
+
lastModified;
|
|
22
|
+
};
|
|
23
|
+
exports.CodeChunk = CodeChunk;
|
|
24
|
+
__decorate([
|
|
25
|
+
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
|
|
26
|
+
__metadata("design:type", String)
|
|
27
|
+
], CodeChunk.prototype, "id", void 0);
|
|
28
|
+
__decorate([
|
|
29
|
+
(0, typeorm_1.Column)(),
|
|
30
|
+
__metadata("design:type", String)
|
|
31
|
+
], CodeChunk.prototype, "serviceName", void 0);
|
|
32
|
+
__decorate([
|
|
33
|
+
(0, typeorm_1.Column)(),
|
|
34
|
+
__metadata("design:type", String)
|
|
35
|
+
], CodeChunk.prototype, "filePath", void 0);
|
|
36
|
+
__decorate([
|
|
37
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
38
|
+
__metadata("design:type", Number)
|
|
39
|
+
], CodeChunk.prototype, "startLine", void 0);
|
|
40
|
+
__decorate([
|
|
41
|
+
(0, typeorm_1.Column)('text'),
|
|
42
|
+
__metadata("design:type", String)
|
|
43
|
+
], CodeChunk.prototype, "content", void 0);
|
|
44
|
+
__decorate([
|
|
45
|
+
(0, typeorm_1.Column)({ type: 'vector', length: 1024 }),
|
|
46
|
+
__metadata("design:type", String)
|
|
47
|
+
], CodeChunk.prototype, "embedding", void 0);
|
|
48
|
+
__decorate([
|
|
49
|
+
(0, typeorm_1.Column)({ type: 'double precision', nullable: true }),
|
|
50
|
+
__metadata("design:type", Number)
|
|
51
|
+
], CodeChunk.prototype, "lastModified", void 0);
|
|
52
|
+
exports.CodeChunk = CodeChunk = __decorate([
|
|
53
|
+
(0, typeorm_1.Entity)()
|
|
54
|
+
], CodeChunk);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.CodeDependency = void 0;
|
|
13
|
+
const typeorm_1 = require("typeorm");
|
|
14
|
+
let CodeDependency = class CodeDependency {
|
|
15
|
+
id;
|
|
16
|
+
project;
|
|
17
|
+
fromFile;
|
|
18
|
+
fromSymbol;
|
|
19
|
+
toFile;
|
|
20
|
+
toSymbol;
|
|
21
|
+
depType; // 'import' | 'import-default' | 'import-namespace'
|
|
22
|
+
};
|
|
23
|
+
exports.CodeDependency = CodeDependency;
|
|
24
|
+
__decorate([
|
|
25
|
+
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
|
|
26
|
+
__metadata("design:type", String)
|
|
27
|
+
], CodeDependency.prototype, "id", void 0);
|
|
28
|
+
__decorate([
|
|
29
|
+
(0, typeorm_1.Column)(),
|
|
30
|
+
__metadata("design:type", String)
|
|
31
|
+
], CodeDependency.prototype, "project", void 0);
|
|
32
|
+
__decorate([
|
|
33
|
+
(0, typeorm_1.Column)(),
|
|
34
|
+
__metadata("design:type", String)
|
|
35
|
+
], CodeDependency.prototype, "fromFile", void 0);
|
|
36
|
+
__decorate([
|
|
37
|
+
(0, typeorm_1.Column)({ type: 'text', nullable: true }),
|
|
38
|
+
__metadata("design:type", Object)
|
|
39
|
+
], CodeDependency.prototype, "fromSymbol", void 0);
|
|
40
|
+
__decorate([
|
|
41
|
+
(0, typeorm_1.Column)(),
|
|
42
|
+
__metadata("design:type", String)
|
|
43
|
+
], CodeDependency.prototype, "toFile", void 0);
|
|
44
|
+
__decorate([
|
|
45
|
+
(0, typeorm_1.Column)({ type: 'text', nullable: true }),
|
|
46
|
+
__metadata("design:type", Object)
|
|
47
|
+
], CodeDependency.prototype, "toSymbol", void 0);
|
|
48
|
+
__decorate([
|
|
49
|
+
(0, typeorm_1.Column)(),
|
|
50
|
+
__metadata("design:type", String)
|
|
51
|
+
], CodeDependency.prototype, "depType", void 0);
|
|
52
|
+
exports.CodeDependency = CodeDependency = __decorate([
|
|
53
|
+
(0, typeorm_1.Entity)()
|
|
54
|
+
], CodeDependency);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.initDbWithSchema = exports.initDb = exports.db = exports.CodeDependency = exports.CodeChunk = void 0;
|
|
4
|
+
var code_chunk_entity_1 = require("./entities/code-chunk.entity");
|
|
5
|
+
Object.defineProperty(exports, "CodeChunk", { enumerable: true, get: function () { return code_chunk_entity_1.CodeChunk; } });
|
|
6
|
+
var code_dependency_entity_1 = require("./entities/code-dependency.entity");
|
|
7
|
+
Object.defineProperty(exports, "CodeDependency", { enumerable: true, get: function () { return code_dependency_entity_1.CodeDependency; } });
|
|
8
|
+
var data_source_1 = require("./data-source");
|
|
9
|
+
Object.defineProperty(exports, "db", { enumerable: true, get: function () { return data_source_1.db; } });
|
|
10
|
+
var init_1 = require("./init");
|
|
11
|
+
Object.defineProperty(exports, "initDb", { enumerable: true, get: function () { return init_1.initDb; } });
|
|
12
|
+
Object.defineProperty(exports, "initDbWithSchema", { enumerable: true, get: function () { return init_1.initDbWithSchema; } });
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.initDb = initDb;
|
|
4
|
+
exports.initDbWithSchema = initDbWithSchema;
|
|
5
|
+
const data_source_1 = require("./data-source");
|
|
6
|
+
// For MCP server — just connect, no schema changes
|
|
7
|
+
async function initDb() {
|
|
8
|
+
await data_source_1.db.initialize();
|
|
9
|
+
}
|
|
10
|
+
// For indexer — connect + create schema only if tables don't exist yet
|
|
11
|
+
async function initDbWithSchema() {
|
|
12
|
+
await data_source_1.db.initialize();
|
|
13
|
+
// code_chunk table
|
|
14
|
+
const [{ exists }] = await data_source_1.db.query(`
|
|
15
|
+
SELECT EXISTS (
|
|
16
|
+
SELECT 1 FROM information_schema.tables
|
|
17
|
+
WHERE table_schema = 'public' AND table_name = 'code_chunk'
|
|
18
|
+
)
|
|
19
|
+
`);
|
|
20
|
+
if (!exists) {
|
|
21
|
+
await data_source_1.db.synchronize();
|
|
22
|
+
await data_source_1.db.query(`
|
|
23
|
+
CREATE INDEX idx_code_chunk_embedding
|
|
24
|
+
ON code_chunk USING hnsw (embedding vector_cosine_ops)
|
|
25
|
+
`);
|
|
26
|
+
}
|
|
27
|
+
// code_dependency table
|
|
28
|
+
const [{ existsDep }] = await data_source_1.db.query(`
|
|
29
|
+
SELECT EXISTS (
|
|
30
|
+
SELECT 1 FROM information_schema.tables
|
|
31
|
+
WHERE table_schema = 'public' AND table_name = 'code_dependency'
|
|
32
|
+
) AS "existsDep"
|
|
33
|
+
`);
|
|
34
|
+
if (!existsDep) {
|
|
35
|
+
await data_source_1.db.query(`
|
|
36
|
+
CREATE TABLE code_dependency (
|
|
37
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
38
|
+
project TEXT NOT NULL,
|
|
39
|
+
"fromFile" TEXT NOT NULL,
|
|
40
|
+
"fromSymbol" TEXT,
|
|
41
|
+
"toFile" TEXT NOT NULL,
|
|
42
|
+
"toSymbol" TEXT,
|
|
43
|
+
"depType" TEXT NOT NULL
|
|
44
|
+
)
|
|
45
|
+
`);
|
|
46
|
+
await data_source_1.db.query(`CREATE INDEX idx_dep_from ON code_dependency ("fromFile")`);
|
|
47
|
+
await data_source_1.db.query(`CREATE INDEX idx_dep_to ON code_dependency ("toFile")`);
|
|
48
|
+
await data_source_1.db.query(`CREATE INDEX idx_dep_project ON code_dependency (project)`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|