raggrep 0.1.3 → 0.1.5
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/dist/{indexer → app/indexer}/index.d.ts +1 -1
- package/dist/{search → app/search}/index.d.ts +1 -1
- package/dist/cli/main.js +1364 -220
- package/dist/cli/main.js.map +25 -15
- package/dist/composition.d.ts +7 -7
- package/dist/domain/entities/chunk.d.ts +1 -1
- package/dist/domain/entities/fileIndex.d.ts +1 -1
- package/dist/domain/entities/index.d.ts +1 -1
- package/dist/domain/entities/searchResult.d.ts +47 -2
- package/dist/domain/index.d.ts +5 -3
- package/dist/domain/ports/embedding.d.ts +1 -5
- package/dist/domain/ports/index.d.ts +3 -4
- package/dist/domain/services/bm25.d.ts +24 -0
- package/dist/domain/services/index.d.ts +3 -2
- package/dist/domain/services/similarity.d.ts +23 -0
- package/dist/{application → domain}/usecases/cleanupIndex.d.ts +2 -2
- package/dist/{application → domain}/usecases/indexDirectory.d.ts +2 -2
- package/dist/{application → domain}/usecases/searchIndex.d.ts +2 -2
- package/dist/index.d.ts +5 -5
- package/dist/index.js +1305 -239
- package/dist/index.js.map +25 -15
- package/dist/{utils/config.d.ts → infrastructure/config/configLoader.d.ts} +7 -4
- package/dist/infrastructure/config/index.d.ts +6 -0
- package/dist/infrastructure/embeddings/index.d.ts +3 -1
- package/dist/infrastructure/embeddings/transformersEmbedding.d.ts +16 -0
- package/dist/infrastructure/index.d.ts +4 -3
- package/dist/infrastructure/storage/index.d.ts +4 -1
- package/dist/{utils/tieredIndex.d.ts → infrastructure/storage/symbolicIndex.d.ts} +7 -18
- package/dist/introspection/fileIntrospector.d.ts +14 -0
- package/dist/introspection/index.d.ts +68 -0
- package/dist/introspection/introspection.test.d.ts +4 -0
- package/dist/introspection/projectDetector.d.ts +27 -0
- package/dist/introspection/types.d.ts +70 -0
- package/dist/modules/core/index.d.ts +69 -0
- package/dist/modules/core/symbols.d.ts +27 -0
- package/dist/modules/core/symbols.test.d.ts +4 -0
- package/dist/modules/{semantic → language/typescript}/index.d.ts +11 -12
- package/dist/types.d.ts +4 -1
- package/package.json +5 -5
- package/dist/application/index.d.ts +0 -7
- package/dist/utils/bm25.d.ts +0 -9
- package/dist/utils/embeddings.d.ts +0 -46
- /package/dist/{cli → app/cli}/main.d.ts +0 -0
- /package/dist/{indexer → app/indexer}/watcher.d.ts +0 -0
- /package/dist/{application → domain}/usecases/index.d.ts +0 -0
- /package/dist/{utils → infrastructure/embeddings}/embeddings.test.d.ts +0 -0
- /package/dist/modules/{semantic → language/typescript}/parseCode.d.ts +0 -0
- /package/dist/modules/{semantic → language/typescript}/parseCode.test.d.ts +0 -0
package/dist/index.js
CHANGED
|
@@ -21,7 +21,12 @@ function createDefaultConfig() {
|
|
|
21
21
|
ignorePaths: DEFAULT_IGNORE_PATHS,
|
|
22
22
|
modules: [
|
|
23
23
|
{
|
|
24
|
-
id: "
|
|
24
|
+
id: "core",
|
|
25
|
+
enabled: true,
|
|
26
|
+
options: {}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "language/typescript",
|
|
25
30
|
enabled: true,
|
|
26
31
|
options: {
|
|
27
32
|
embeddingModel: "all-MiniLM-L6-v2"
|
|
@@ -81,156 +86,32 @@ var init_entities = __esm(() => {
|
|
|
81
86
|
init_config();
|
|
82
87
|
});
|
|
83
88
|
|
|
84
|
-
// src/
|
|
85
|
-
import { pipeline, env } from "@xenova/transformers";
|
|
89
|
+
// src/infrastructure/config/configLoader.ts
|
|
86
90
|
import * as path from "path";
|
|
87
|
-
import * as os from "os";
|
|
88
|
-
function configureEmbeddings(config) {
|
|
89
|
-
const newConfig = { ...currentConfig, ...config };
|
|
90
|
-
if (newConfig.model !== currentConfig.model) {
|
|
91
|
-
embeddingPipeline = null;
|
|
92
|
-
currentModelName = null;
|
|
93
|
-
}
|
|
94
|
-
currentConfig = newConfig;
|
|
95
|
-
}
|
|
96
|
-
async function initializePipeline() {
|
|
97
|
-
if (embeddingPipeline && currentModelName === currentConfig.model) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
if (isInitializing && initPromise) {
|
|
101
|
-
return initPromise;
|
|
102
|
-
}
|
|
103
|
-
isInitializing = true;
|
|
104
|
-
initPromise = (async () => {
|
|
105
|
-
const modelId = EMBEDDING_MODELS[currentConfig.model];
|
|
106
|
-
if (currentConfig.showProgress) {
|
|
107
|
-
console.log(`
|
|
108
|
-
Loading embedding model: ${currentConfig.model}`);
|
|
109
|
-
console.log(` Cache: ${CACHE_DIR}`);
|
|
110
|
-
}
|
|
111
|
-
try {
|
|
112
|
-
embeddingPipeline = await pipeline("feature-extraction", modelId, {
|
|
113
|
-
progress_callback: currentConfig.showProgress ? (progress) => {
|
|
114
|
-
if (progress.status === "progress" && progress.file) {
|
|
115
|
-
const pct = progress.progress ? Math.round(progress.progress) : 0;
|
|
116
|
-
process.stdout.write(`\r Downloading ${progress.file}: ${pct}% `);
|
|
117
|
-
} else if (progress.status === "done" && progress.file) {
|
|
118
|
-
process.stdout.write(`\r Downloaded ${progress.file}
|
|
119
|
-
`);
|
|
120
|
-
} else if (progress.status === "ready") {}
|
|
121
|
-
} : undefined
|
|
122
|
-
});
|
|
123
|
-
currentModelName = currentConfig.model;
|
|
124
|
-
if (currentConfig.showProgress) {
|
|
125
|
-
console.log(` Model ready.
|
|
126
|
-
`);
|
|
127
|
-
}
|
|
128
|
-
} catch (error) {
|
|
129
|
-
embeddingPipeline = null;
|
|
130
|
-
currentModelName = null;
|
|
131
|
-
throw new Error(`Failed to load embedding model: ${error}`);
|
|
132
|
-
} finally {
|
|
133
|
-
isInitializing = false;
|
|
134
|
-
initPromise = null;
|
|
135
|
-
}
|
|
136
|
-
})();
|
|
137
|
-
return initPromise;
|
|
138
|
-
}
|
|
139
|
-
async function getEmbedding(text) {
|
|
140
|
-
await initializePipeline();
|
|
141
|
-
if (!embeddingPipeline) {
|
|
142
|
-
throw new Error("Embedding pipeline not initialized");
|
|
143
|
-
}
|
|
144
|
-
const output = await embeddingPipeline(text, {
|
|
145
|
-
pooling: "mean",
|
|
146
|
-
normalize: true
|
|
147
|
-
});
|
|
148
|
-
return Array.from(output.data);
|
|
149
|
-
}
|
|
150
|
-
async function getEmbeddings(texts) {
|
|
151
|
-
if (texts.length === 0)
|
|
152
|
-
return [];
|
|
153
|
-
await initializePipeline();
|
|
154
|
-
if (!embeddingPipeline) {
|
|
155
|
-
throw new Error("Embedding pipeline not initialized");
|
|
156
|
-
}
|
|
157
|
-
const results = [];
|
|
158
|
-
for (let i = 0;i < texts.length; i += BATCH_SIZE) {
|
|
159
|
-
const batch = texts.slice(i, i + BATCH_SIZE);
|
|
160
|
-
const outputs = await Promise.all(batch.map(async (text) => {
|
|
161
|
-
const output = await embeddingPipeline(text, {
|
|
162
|
-
pooling: "mean",
|
|
163
|
-
normalize: true
|
|
164
|
-
});
|
|
165
|
-
return Array.from(output.data);
|
|
166
|
-
}));
|
|
167
|
-
results.push(...outputs);
|
|
168
|
-
}
|
|
169
|
-
return results;
|
|
170
|
-
}
|
|
171
|
-
function cosineSimilarity(a, b) {
|
|
172
|
-
if (a.length !== b.length) {
|
|
173
|
-
throw new Error("Vectors must have the same length");
|
|
174
|
-
}
|
|
175
|
-
let dotProduct = 0;
|
|
176
|
-
let normA = 0;
|
|
177
|
-
let normB = 0;
|
|
178
|
-
for (let i = 0;i < a.length; i++) {
|
|
179
|
-
dotProduct += a[i] * b[i];
|
|
180
|
-
normA += a[i] * a[i];
|
|
181
|
-
normB += b[i] * b[i];
|
|
182
|
-
}
|
|
183
|
-
if (normA === 0 || normB === 0)
|
|
184
|
-
return 0;
|
|
185
|
-
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
186
|
-
}
|
|
187
|
-
function getEmbeddingConfig() {
|
|
188
|
-
return { ...currentConfig };
|
|
189
|
-
}
|
|
190
|
-
var CACHE_DIR, EMBEDDING_MODELS, embeddingPipeline = null, currentModelName = null, isInitializing = false, initPromise = null, DEFAULT_CONFIG, currentConfig, BATCH_SIZE = 32;
|
|
191
|
-
var init_embeddings = __esm(() => {
|
|
192
|
-
CACHE_DIR = path.join(os.homedir(), ".cache", "raggrep", "models");
|
|
193
|
-
env.cacheDir = CACHE_DIR;
|
|
194
|
-
env.allowLocalModels = true;
|
|
195
|
-
EMBEDDING_MODELS = {
|
|
196
|
-
"all-MiniLM-L6-v2": "Xenova/all-MiniLM-L6-v2",
|
|
197
|
-
"all-MiniLM-L12-v2": "Xenova/all-MiniLM-L12-v2",
|
|
198
|
-
"bge-small-en-v1.5": "Xenova/bge-small-en-v1.5",
|
|
199
|
-
"paraphrase-MiniLM-L3-v2": "Xenova/paraphrase-MiniLM-L3-v2"
|
|
200
|
-
};
|
|
201
|
-
DEFAULT_CONFIG = {
|
|
202
|
-
model: "all-MiniLM-L6-v2",
|
|
203
|
-
showProgress: true
|
|
204
|
-
};
|
|
205
|
-
currentConfig = { ...DEFAULT_CONFIG };
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
// src/utils/config.ts
|
|
209
|
-
import * as path2 from "path";
|
|
210
91
|
import * as fs from "fs/promises";
|
|
211
|
-
function getRaggrepDir(rootDir, config =
|
|
212
|
-
return
|
|
92
|
+
function getRaggrepDir(rootDir, config = DEFAULT_CONFIG) {
|
|
93
|
+
return path.join(rootDir, config.indexDir);
|
|
213
94
|
}
|
|
214
|
-
function getModuleIndexPath(rootDir, moduleId, config =
|
|
215
|
-
return
|
|
95
|
+
function getModuleIndexPath(rootDir, moduleId, config = DEFAULT_CONFIG) {
|
|
96
|
+
return path.join(rootDir, config.indexDir, "index", moduleId);
|
|
216
97
|
}
|
|
217
|
-
function getModuleManifestPath(rootDir, moduleId, config =
|
|
218
|
-
return
|
|
98
|
+
function getModuleManifestPath(rootDir, moduleId, config = DEFAULT_CONFIG) {
|
|
99
|
+
return path.join(rootDir, config.indexDir, "index", moduleId, "manifest.json");
|
|
219
100
|
}
|
|
220
|
-
function getGlobalManifestPath(rootDir, config =
|
|
221
|
-
return
|
|
101
|
+
function getGlobalManifestPath(rootDir, config = DEFAULT_CONFIG) {
|
|
102
|
+
return path.join(rootDir, config.indexDir, "manifest.json");
|
|
222
103
|
}
|
|
223
|
-
function getConfigPath(rootDir, config =
|
|
224
|
-
return
|
|
104
|
+
function getConfigPath(rootDir, config = DEFAULT_CONFIG) {
|
|
105
|
+
return path.join(rootDir, config.indexDir, "config.json");
|
|
225
106
|
}
|
|
226
107
|
async function loadConfig(rootDir) {
|
|
227
|
-
const configPath = getConfigPath(rootDir,
|
|
108
|
+
const configPath = getConfigPath(rootDir, DEFAULT_CONFIG);
|
|
228
109
|
try {
|
|
229
110
|
const content = await fs.readFile(configPath, "utf-8");
|
|
230
111
|
const savedConfig = JSON.parse(content);
|
|
231
|
-
return { ...
|
|
112
|
+
return { ...DEFAULT_CONFIG, ...savedConfig };
|
|
232
113
|
} catch {
|
|
233
|
-
return
|
|
114
|
+
return DEFAULT_CONFIG;
|
|
234
115
|
}
|
|
235
116
|
}
|
|
236
117
|
function getModuleConfig(config, moduleId) {
|
|
@@ -248,11 +129,21 @@ function getEmbeddingConfigFromModule(moduleConfig) {
|
|
|
248
129
|
showProgress: options.showProgress !== false
|
|
249
130
|
};
|
|
250
131
|
}
|
|
251
|
-
var
|
|
252
|
-
var
|
|
132
|
+
var DEFAULT_CONFIG, EMBEDDING_MODELS;
|
|
133
|
+
var init_configLoader = __esm(() => {
|
|
253
134
|
init_entities();
|
|
254
|
-
|
|
255
|
-
|
|
135
|
+
DEFAULT_CONFIG = createDefaultConfig();
|
|
136
|
+
EMBEDDING_MODELS = {
|
|
137
|
+
"all-MiniLM-L6-v2": "Xenova/all-MiniLM-L6-v2",
|
|
138
|
+
"all-MiniLM-L12-v2": "Xenova/all-MiniLM-L12-v2",
|
|
139
|
+
"bge-small-en-v1.5": "Xenova/bge-small-en-v1.5",
|
|
140
|
+
"paraphrase-MiniLM-L3-v2": "Xenova/paraphrase-MiniLM-L3-v2"
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// src/infrastructure/config/index.ts
|
|
145
|
+
var init_config2 = __esm(() => {
|
|
146
|
+
init_configLoader();
|
|
256
147
|
});
|
|
257
148
|
|
|
258
149
|
// src/domain/services/bm25.ts
|
|
@@ -326,16 +217,604 @@ class BM25Index {
|
|
|
326
217
|
this.avgDocLength = 0;
|
|
327
218
|
this.totalDocs = 0;
|
|
328
219
|
}
|
|
220
|
+
addDocument(id, tokens) {
|
|
221
|
+
this.addDocuments([{ id, content: "", tokens }]);
|
|
222
|
+
}
|
|
223
|
+
serialize() {
|
|
224
|
+
const documents = {};
|
|
225
|
+
for (const [id, { tokens }] of this.documents) {
|
|
226
|
+
documents[id] = tokens;
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
documents,
|
|
230
|
+
avgDocLength: this.avgDocLength,
|
|
231
|
+
documentFrequencies: Object.fromEntries(this.documentFrequencies),
|
|
232
|
+
totalDocs: this.totalDocs
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
static deserialize(data) {
|
|
236
|
+
const index = new BM25Index;
|
|
237
|
+
index.avgDocLength = data.avgDocLength;
|
|
238
|
+
index.totalDocs = data.totalDocs;
|
|
239
|
+
index.documentFrequencies = new Map(Object.entries(data.documentFrequencies));
|
|
240
|
+
for (const [id, tokens] of Object.entries(data.documents)) {
|
|
241
|
+
index.documents.set(id, { content: "", tokens });
|
|
242
|
+
}
|
|
243
|
+
return index;
|
|
244
|
+
}
|
|
329
245
|
}
|
|
330
246
|
function normalizeScore(score, midpoint = 5) {
|
|
331
247
|
return 1 / (1 + Math.exp(-score / midpoint + 1));
|
|
332
248
|
}
|
|
333
249
|
var BM25_K1 = 1.5, BM25_B = 0.75;
|
|
334
250
|
|
|
335
|
-
// src/
|
|
336
|
-
|
|
251
|
+
// src/modules/core/symbols.ts
|
|
252
|
+
function extractSymbols(content) {
|
|
253
|
+
const symbols = [];
|
|
254
|
+
const seenSymbols = new Set;
|
|
255
|
+
const lines = content.split(`
|
|
256
|
+
`);
|
|
257
|
+
for (const { type, pattern, exported } of SYMBOL_PATTERNS) {
|
|
258
|
+
pattern.lastIndex = 0;
|
|
259
|
+
let match;
|
|
260
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
261
|
+
const name = match[1];
|
|
262
|
+
const symbolKey = `${name}:${type}`;
|
|
263
|
+
if (seenSymbols.has(symbolKey))
|
|
264
|
+
continue;
|
|
265
|
+
seenSymbols.add(symbolKey);
|
|
266
|
+
const beforeMatch = content.substring(0, match.index);
|
|
267
|
+
const line = beforeMatch.split(`
|
|
268
|
+
`).length;
|
|
269
|
+
symbols.push({
|
|
270
|
+
name,
|
|
271
|
+
type,
|
|
272
|
+
line,
|
|
273
|
+
isExported: exported
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return symbols.sort((a, b) => a.line - b.line);
|
|
278
|
+
}
|
|
279
|
+
function symbolsToKeywords(symbols) {
|
|
280
|
+
const keywords = new Set;
|
|
281
|
+
for (const symbol of symbols) {
|
|
282
|
+
keywords.add(symbol.name.toLowerCase());
|
|
283
|
+
const parts = symbol.name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").toLowerCase().split(/\s+/);
|
|
284
|
+
for (const part of parts) {
|
|
285
|
+
if (part.length > 2) {
|
|
286
|
+
keywords.add(part);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return Array.from(keywords);
|
|
291
|
+
}
|
|
292
|
+
var SYMBOL_PATTERNS;
|
|
293
|
+
var init_symbols = __esm(() => {
|
|
294
|
+
SYMBOL_PATTERNS = [
|
|
295
|
+
{
|
|
296
|
+
type: "function",
|
|
297
|
+
pattern: /^export\s+(?:async\s+)?function\s+(\w+)/gm,
|
|
298
|
+
exported: true
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
type: "function",
|
|
302
|
+
pattern: /^export\s+(?:const|let)\s+(\w+)\s*=\s*(?:async\s*)?\(/gm,
|
|
303
|
+
exported: true
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
type: "class",
|
|
307
|
+
pattern: /^export\s+(?:abstract\s+)?class\s+(\w+)/gm,
|
|
308
|
+
exported: true
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
type: "interface",
|
|
312
|
+
pattern: /^export\s+interface\s+(\w+)/gm,
|
|
313
|
+
exported: true
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
type: "type",
|
|
317
|
+
pattern: /^export\s+type\s+(\w+)/gm,
|
|
318
|
+
exported: true
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
type: "enum",
|
|
322
|
+
pattern: /^export\s+(?:const\s+)?enum\s+(\w+)/gm,
|
|
323
|
+
exported: true
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
type: "variable",
|
|
327
|
+
pattern: /^export\s+(?:const|let|var)\s+(\w+)\s*(?::|=)/gm,
|
|
328
|
+
exported: true
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
type: "function",
|
|
332
|
+
pattern: /^export\s+default\s+(?:async\s+)?function\s+(\w+)/gm,
|
|
333
|
+
exported: true
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
type: "class",
|
|
337
|
+
pattern: /^export\s+default\s+class\s+(\w+)/gm,
|
|
338
|
+
exported: true
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
type: "function",
|
|
342
|
+
pattern: /^(?:async\s+)?function\s+(\w+)/gm,
|
|
343
|
+
exported: false
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
type: "function",
|
|
347
|
+
pattern: /^(?:const|let)\s+(\w+)\s*=\s*(?:async\s*)?\(/gm,
|
|
348
|
+
exported: false
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
type: "class",
|
|
352
|
+
pattern: /^(?:abstract\s+)?class\s+(\w+)/gm,
|
|
353
|
+
exported: false
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
type: "interface",
|
|
357
|
+
pattern: /^interface\s+(\w+)/gm,
|
|
358
|
+
exported: false
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
type: "type",
|
|
362
|
+
pattern: /^type\s+(\w+)/gm,
|
|
363
|
+
exported: false
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
type: "enum",
|
|
367
|
+
pattern: /^(?:const\s+)?enum\s+(\w+)/gm,
|
|
368
|
+
exported: false
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
type: "function",
|
|
372
|
+
pattern: /^def\s+(\w+)\s*\(/gm,
|
|
373
|
+
exported: false
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
type: "class",
|
|
377
|
+
pattern: /^class\s+(\w+)(?:\s*\(|:)/gm,
|
|
378
|
+
exported: false
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
type: "function",
|
|
382
|
+
pattern: /^func\s+(?:\([^)]+\)\s+)?(\w+)\s*\(/gm,
|
|
383
|
+
exported: false
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
type: "type",
|
|
387
|
+
pattern: /^type\s+(\w+)\s+(?:struct|interface)/gm,
|
|
388
|
+
exported: false
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
type: "function",
|
|
392
|
+
pattern: /^(?:pub\s+)?(?:async\s+)?fn\s+(\w+)/gm,
|
|
393
|
+
exported: false
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
type: "type",
|
|
397
|
+
pattern: /^(?:pub\s+)?struct\s+(\w+)/gm,
|
|
398
|
+
exported: false
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
type: "enum",
|
|
402
|
+
pattern: /^(?:pub\s+)?enum\s+(\w+)/gm,
|
|
403
|
+
exported: false
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
type: "interface",
|
|
407
|
+
pattern: /^(?:pub\s+)?trait\s+(\w+)/gm,
|
|
408
|
+
exported: false
|
|
409
|
+
}
|
|
410
|
+
];
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// src/modules/core/index.ts
|
|
414
|
+
var exports_core = {};
|
|
415
|
+
__export(exports_core, {
|
|
416
|
+
CoreModule: () => CoreModule
|
|
417
|
+
});
|
|
418
|
+
import * as path2 from "path";
|
|
419
|
+
import * as fs2 from "fs/promises";
|
|
420
|
+
|
|
421
|
+
class CoreModule {
|
|
422
|
+
id = "core";
|
|
423
|
+
name = "Core Search";
|
|
424
|
+
description = "Language-agnostic text search with symbol extraction";
|
|
425
|
+
version = "1.0.0";
|
|
426
|
+
symbolIndex = new Map;
|
|
427
|
+
bm25Index = null;
|
|
428
|
+
rootDir = "";
|
|
429
|
+
async initialize(_config) {}
|
|
430
|
+
async indexFile(filepath, content, ctx) {
|
|
431
|
+
this.rootDir = ctx.rootDir;
|
|
432
|
+
const symbols = extractSymbols(content);
|
|
433
|
+
const symbolKeywords = symbolsToKeywords(symbols);
|
|
434
|
+
const contentTokens = tokenize(content);
|
|
435
|
+
const allTokens = [...new Set([...contentTokens, ...symbolKeywords])];
|
|
436
|
+
const chunks = this.createChunks(filepath, content, symbols);
|
|
437
|
+
const stats = await ctx.getFileStats(filepath);
|
|
438
|
+
this.symbolIndex.set(filepath, {
|
|
439
|
+
filepath,
|
|
440
|
+
symbols,
|
|
441
|
+
tokens: allTokens
|
|
442
|
+
});
|
|
443
|
+
const moduleData = {
|
|
444
|
+
symbols,
|
|
445
|
+
tokens: allTokens
|
|
446
|
+
};
|
|
447
|
+
return {
|
|
448
|
+
filepath,
|
|
449
|
+
lastModified: stats.lastModified,
|
|
450
|
+
chunks,
|
|
451
|
+
moduleData
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
createChunks(filepath, content, symbols) {
|
|
455
|
+
const lines = content.split(`
|
|
456
|
+
`);
|
|
457
|
+
const chunks = [];
|
|
458
|
+
for (let start = 0;start < lines.length; start += LINES_PER_CHUNK - CHUNK_OVERLAP) {
|
|
459
|
+
const end = Math.min(start + LINES_PER_CHUNK, lines.length);
|
|
460
|
+
const chunkLines = lines.slice(start, end);
|
|
461
|
+
const chunkContent = chunkLines.join(`
|
|
462
|
+
`);
|
|
463
|
+
const chunkSymbols = symbols.filter((s) => s.line >= start + 1 && s.line <= end);
|
|
464
|
+
let chunkType = "block";
|
|
465
|
+
let chunkName;
|
|
466
|
+
let isExported = false;
|
|
467
|
+
if (chunkSymbols.length > 0) {
|
|
468
|
+
const primarySymbol = chunkSymbols[0];
|
|
469
|
+
chunkType = this.symbolTypeToChunkType(primarySymbol.type);
|
|
470
|
+
chunkName = primarySymbol.name;
|
|
471
|
+
isExported = primarySymbol.isExported;
|
|
472
|
+
}
|
|
473
|
+
const chunkId = `${filepath}:${start + 1}-${end}`;
|
|
474
|
+
chunks.push({
|
|
475
|
+
id: chunkId,
|
|
476
|
+
content: chunkContent,
|
|
477
|
+
startLine: start + 1,
|
|
478
|
+
endLine: end,
|
|
479
|
+
type: chunkType,
|
|
480
|
+
name: chunkName,
|
|
481
|
+
isExported
|
|
482
|
+
});
|
|
483
|
+
if (end >= lines.length)
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
return chunks;
|
|
487
|
+
}
|
|
488
|
+
symbolTypeToChunkType(symbolType) {
|
|
489
|
+
switch (symbolType) {
|
|
490
|
+
case "function":
|
|
491
|
+
case "method":
|
|
492
|
+
return "function";
|
|
493
|
+
case "class":
|
|
494
|
+
return "class";
|
|
495
|
+
case "interface":
|
|
496
|
+
return "interface";
|
|
497
|
+
case "type":
|
|
498
|
+
return "type";
|
|
499
|
+
case "enum":
|
|
500
|
+
return "enum";
|
|
501
|
+
case "variable":
|
|
502
|
+
return "variable";
|
|
503
|
+
default:
|
|
504
|
+
return "block";
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
async finalize(ctx) {
|
|
508
|
+
const config = ctx.config;
|
|
509
|
+
const coreDir = path2.join(getRaggrepDir(ctx.rootDir, config), "index", "core");
|
|
510
|
+
await fs2.mkdir(coreDir, { recursive: true });
|
|
511
|
+
this.bm25Index = new BM25Index;
|
|
512
|
+
for (const [filepath, entry] of this.symbolIndex) {
|
|
513
|
+
this.bm25Index.addDocument(filepath, entry.tokens);
|
|
514
|
+
}
|
|
515
|
+
const symbolIndexData = {
|
|
516
|
+
version: this.version,
|
|
517
|
+
lastUpdated: new Date().toISOString(),
|
|
518
|
+
files: Object.fromEntries(this.symbolIndex),
|
|
519
|
+
bm25Data: this.bm25Index.serialize()
|
|
520
|
+
};
|
|
521
|
+
await fs2.writeFile(path2.join(coreDir, "symbols.json"), JSON.stringify(symbolIndexData, null, 2));
|
|
522
|
+
console.log(` [Core] Symbol index built with ${this.symbolIndex.size} files`);
|
|
523
|
+
}
|
|
524
|
+
async search(query, ctx, options) {
|
|
525
|
+
const config = ctx.config;
|
|
526
|
+
const topK = options?.topK ?? DEFAULT_TOP_K;
|
|
527
|
+
const minScore = options?.minScore ?? DEFAULT_MIN_SCORE;
|
|
528
|
+
if (this.symbolIndex.size === 0) {
|
|
529
|
+
await this.loadSymbolIndex(ctx.rootDir, config);
|
|
530
|
+
}
|
|
531
|
+
if (!this.bm25Index || this.symbolIndex.size === 0) {
|
|
532
|
+
return [];
|
|
533
|
+
}
|
|
534
|
+
const queryTokens = tokenize(query);
|
|
535
|
+
const bm25Results = this.bm25Index.search(query, topK * 2);
|
|
536
|
+
const bm25Scores = new Map(bm25Results.map((r) => [r.id, r.score]));
|
|
537
|
+
const symbolMatches = this.findSymbolMatches(queryTokens);
|
|
538
|
+
const results = [];
|
|
539
|
+
for (const filepath of this.symbolIndex.keys()) {
|
|
540
|
+
const entry = this.symbolIndex.get(filepath);
|
|
541
|
+
const bm25Score = bm25Scores.get(filepath) ?? 0;
|
|
542
|
+
const symbolScore = symbolMatches.get(filepath) ?? 0;
|
|
543
|
+
if (bm25Score === 0 && symbolScore === 0)
|
|
544
|
+
continue;
|
|
545
|
+
const combinedScore = 0.6 * normalizeScore(bm25Score) + 0.4 * symbolScore;
|
|
546
|
+
if (combinedScore >= minScore) {
|
|
547
|
+
const fileIndex = await ctx.loadFileIndex(filepath);
|
|
548
|
+
if (!fileIndex)
|
|
549
|
+
continue;
|
|
550
|
+
const bestChunk = this.findBestChunk(fileIndex.chunks, queryTokens, entry.symbols);
|
|
551
|
+
results.push({
|
|
552
|
+
filepath,
|
|
553
|
+
chunk: bestChunk,
|
|
554
|
+
score: combinedScore,
|
|
555
|
+
moduleId: this.id,
|
|
556
|
+
context: {
|
|
557
|
+
bm25Score: normalizeScore(bm25Score),
|
|
558
|
+
symbolScore
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return results.sort((a, b) => b.score - a.score).slice(0, topK);
|
|
564
|
+
}
|
|
565
|
+
findSymbolMatches(queryTokens) {
|
|
566
|
+
const matches = new Map;
|
|
567
|
+
for (const [filepath, entry] of this.symbolIndex) {
|
|
568
|
+
let matchScore = 0;
|
|
569
|
+
for (const symbol of entry.symbols) {
|
|
570
|
+
const symbolName = symbol.name.toLowerCase();
|
|
571
|
+
const symbolParts = symbolsToKeywords([symbol]);
|
|
572
|
+
for (const token of queryTokens) {
|
|
573
|
+
if (symbolName === token) {
|
|
574
|
+
matchScore += symbol.isExported ? 1 : 0.8;
|
|
575
|
+
} else if (symbolName.includes(token) || token.includes(symbolName)) {
|
|
576
|
+
matchScore += symbol.isExported ? 0.5 : 0.4;
|
|
577
|
+
} else if (symbolParts.some((p) => p === token)) {
|
|
578
|
+
matchScore += symbol.isExported ? 0.3 : 0.2;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
if (matchScore > 0) {
|
|
583
|
+
matches.set(filepath, Math.min(1, matchScore / queryTokens.length));
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return matches;
|
|
587
|
+
}
|
|
588
|
+
findBestChunk(chunks, queryTokens, symbols) {
|
|
589
|
+
let bestChunk = chunks[0];
|
|
590
|
+
let bestScore = 0;
|
|
591
|
+
for (const chunk of chunks) {
|
|
592
|
+
let score = 0;
|
|
593
|
+
const chunkContent = chunk.content.toLowerCase();
|
|
594
|
+
for (const token of queryTokens) {
|
|
595
|
+
if (chunkContent.includes(token)) {
|
|
596
|
+
score += 1;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
if (chunk.name) {
|
|
600
|
+
const nameLower = chunk.name.toLowerCase();
|
|
601
|
+
for (const token of queryTokens) {
|
|
602
|
+
if (nameLower.includes(token)) {
|
|
603
|
+
score += 2;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (chunk.isExported) {
|
|
608
|
+
score += 0.5;
|
|
609
|
+
}
|
|
610
|
+
if (score > bestScore) {
|
|
611
|
+
bestScore = score;
|
|
612
|
+
bestChunk = chunk;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
return bestChunk;
|
|
616
|
+
}
|
|
617
|
+
async loadSymbolIndex(rootDir, config) {
|
|
618
|
+
const coreDir = path2.join(getRaggrepDir(rootDir, config), "index", "core");
|
|
619
|
+
const symbolsPath = path2.join(coreDir, "symbols.json");
|
|
620
|
+
try {
|
|
621
|
+
const content = await fs2.readFile(symbolsPath, "utf-8");
|
|
622
|
+
const data = JSON.parse(content);
|
|
623
|
+
this.symbolIndex = new Map(Object.entries(data.files));
|
|
624
|
+
if (data.bm25Data) {
|
|
625
|
+
this.bm25Index = BM25Index.deserialize(data.bm25Data);
|
|
626
|
+
}
|
|
627
|
+
} catch (error) {
|
|
628
|
+
this.symbolIndex = new Map;
|
|
629
|
+
this.bm25Index = null;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
async dispose() {
|
|
633
|
+
this.symbolIndex.clear();
|
|
634
|
+
this.bm25Index = null;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
var DEFAULT_MIN_SCORE = 0.1, DEFAULT_TOP_K = 20, LINES_PER_CHUNK = 50, CHUNK_OVERLAP = 10;
|
|
638
|
+
var init_core = __esm(() => {
|
|
639
|
+
init_config2();
|
|
640
|
+
init_symbols();
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
// src/infrastructure/embeddings/transformersEmbedding.ts
|
|
644
|
+
import { pipeline, env } from "@xenova/transformers";
|
|
645
|
+
import * as path3 from "path";
|
|
646
|
+
import * as os from "os";
|
|
647
|
+
|
|
648
|
+
class TransformersEmbeddingProvider {
|
|
649
|
+
pipeline = null;
|
|
650
|
+
config;
|
|
651
|
+
isInitializing = false;
|
|
652
|
+
initPromise = null;
|
|
653
|
+
constructor(config) {
|
|
654
|
+
this.config = {
|
|
655
|
+
model: config?.model ?? "all-MiniLM-L6-v2",
|
|
656
|
+
showProgress: config?.showProgress ?? true
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
async initialize(config) {
|
|
660
|
+
if (config) {
|
|
661
|
+
if (config.model !== this.config.model) {
|
|
662
|
+
this.pipeline = null;
|
|
663
|
+
}
|
|
664
|
+
this.config = { ...this.config, ...config };
|
|
665
|
+
}
|
|
666
|
+
await this.ensurePipeline();
|
|
667
|
+
}
|
|
668
|
+
async ensurePipeline() {
|
|
669
|
+
if (this.pipeline) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
if (this.isInitializing && this.initPromise) {
|
|
673
|
+
return this.initPromise;
|
|
674
|
+
}
|
|
675
|
+
this.isInitializing = true;
|
|
676
|
+
this.initPromise = (async () => {
|
|
677
|
+
const modelId = EMBEDDING_MODELS2[this.config.model];
|
|
678
|
+
if (this.config.showProgress) {
|
|
679
|
+
console.log(`
|
|
680
|
+
Loading embedding model: ${this.config.model}`);
|
|
681
|
+
console.log(` Cache: ${CACHE_DIR}`);
|
|
682
|
+
}
|
|
683
|
+
try {
|
|
684
|
+
this.pipeline = await pipeline("feature-extraction", modelId, {
|
|
685
|
+
progress_callback: this.config.showProgress ? (progress) => {
|
|
686
|
+
if (progress.status === "progress" && progress.file) {
|
|
687
|
+
const pct = progress.progress ? Math.round(progress.progress) : 0;
|
|
688
|
+
process.stdout.write(`\r Downloading ${progress.file}: ${pct}% `);
|
|
689
|
+
} else if (progress.status === "done" && progress.file) {
|
|
690
|
+
process.stdout.write(`\r Downloaded ${progress.file}
|
|
691
|
+
`);
|
|
692
|
+
}
|
|
693
|
+
} : undefined
|
|
694
|
+
});
|
|
695
|
+
if (this.config.showProgress) {
|
|
696
|
+
console.log(` Model ready.
|
|
697
|
+
`);
|
|
698
|
+
}
|
|
699
|
+
} catch (error) {
|
|
700
|
+
this.pipeline = null;
|
|
701
|
+
throw new Error(`Failed to load embedding model: ${error}`);
|
|
702
|
+
} finally {
|
|
703
|
+
this.isInitializing = false;
|
|
704
|
+
this.initPromise = null;
|
|
705
|
+
}
|
|
706
|
+
})();
|
|
707
|
+
return this.initPromise;
|
|
708
|
+
}
|
|
709
|
+
async getEmbedding(text) {
|
|
710
|
+
await this.ensurePipeline();
|
|
711
|
+
if (!this.pipeline) {
|
|
712
|
+
throw new Error("Embedding pipeline not initialized");
|
|
713
|
+
}
|
|
714
|
+
const output = await this.pipeline(text, {
|
|
715
|
+
pooling: "mean",
|
|
716
|
+
normalize: true
|
|
717
|
+
});
|
|
718
|
+
return Array.from(output.data);
|
|
719
|
+
}
|
|
720
|
+
async getEmbeddings(texts) {
|
|
721
|
+
if (texts.length === 0)
|
|
722
|
+
return [];
|
|
723
|
+
await this.ensurePipeline();
|
|
724
|
+
if (!this.pipeline) {
|
|
725
|
+
throw new Error("Embedding pipeline not initialized");
|
|
726
|
+
}
|
|
727
|
+
const results = [];
|
|
728
|
+
for (let i = 0;i < texts.length; i += BATCH_SIZE) {
|
|
729
|
+
const batch = texts.slice(i, i + BATCH_SIZE);
|
|
730
|
+
const outputs = await Promise.all(batch.map(async (text) => {
|
|
731
|
+
const output = await this.pipeline(text, {
|
|
732
|
+
pooling: "mean",
|
|
733
|
+
normalize: true
|
|
734
|
+
});
|
|
735
|
+
return Array.from(output.data);
|
|
736
|
+
}));
|
|
737
|
+
results.push(...outputs);
|
|
738
|
+
}
|
|
739
|
+
return results;
|
|
740
|
+
}
|
|
741
|
+
getDimension() {
|
|
742
|
+
return EMBEDDING_DIMENSION;
|
|
743
|
+
}
|
|
744
|
+
getModelName() {
|
|
745
|
+
return this.config.model;
|
|
746
|
+
}
|
|
747
|
+
async dispose() {
|
|
748
|
+
this.pipeline = null;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
function configureEmbeddings(config) {
|
|
752
|
+
const newConfig = { ...globalConfig, ...config };
|
|
753
|
+
if (newConfig.model !== globalConfig.model) {
|
|
754
|
+
globalProvider = null;
|
|
755
|
+
}
|
|
756
|
+
globalConfig = newConfig;
|
|
757
|
+
}
|
|
758
|
+
function getEmbeddingConfig() {
|
|
759
|
+
return { ...globalConfig };
|
|
760
|
+
}
|
|
761
|
+
async function ensureGlobalProvider() {
|
|
762
|
+
if (!globalProvider) {
|
|
763
|
+
globalProvider = new TransformersEmbeddingProvider(globalConfig);
|
|
764
|
+
await globalProvider.initialize();
|
|
765
|
+
}
|
|
766
|
+
return globalProvider;
|
|
767
|
+
}
|
|
768
|
+
async function getEmbedding(text) {
|
|
769
|
+
const provider = await ensureGlobalProvider();
|
|
770
|
+
return provider.getEmbedding(text);
|
|
771
|
+
}
|
|
772
|
+
async function getEmbeddings(texts) {
|
|
773
|
+
const provider = await ensureGlobalProvider();
|
|
774
|
+
return provider.getEmbeddings(texts);
|
|
775
|
+
}
|
|
776
|
+
var CACHE_DIR, EMBEDDING_MODELS2, EMBEDDING_DIMENSION = 384, BATCH_SIZE = 32, globalProvider = null, globalConfig;
|
|
777
|
+
var init_transformersEmbedding = __esm(() => {
|
|
778
|
+
CACHE_DIR = path3.join(os.homedir(), ".cache", "raggrep", "models");
|
|
779
|
+
env.cacheDir = CACHE_DIR;
|
|
780
|
+
env.allowLocalModels = true;
|
|
781
|
+
EMBEDDING_MODELS2 = {
|
|
782
|
+
"all-MiniLM-L6-v2": "Xenova/all-MiniLM-L6-v2",
|
|
783
|
+
"all-MiniLM-L12-v2": "Xenova/all-MiniLM-L12-v2",
|
|
784
|
+
"bge-small-en-v1.5": "Xenova/bge-small-en-v1.5",
|
|
785
|
+
"paraphrase-MiniLM-L3-v2": "Xenova/paraphrase-MiniLM-L3-v2"
|
|
786
|
+
};
|
|
787
|
+
globalConfig = {
|
|
788
|
+
model: "all-MiniLM-L6-v2",
|
|
789
|
+
showProgress: true
|
|
790
|
+
};
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
// src/infrastructure/embeddings/index.ts
|
|
794
|
+
var init_embeddings = __esm(() => {
|
|
795
|
+
init_transformersEmbedding();
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
// src/domain/services/similarity.ts
|
|
799
|
+
function cosineSimilarity(a, b) {
|
|
800
|
+
if (a.length !== b.length) {
|
|
801
|
+
throw new Error(`Vector length mismatch: ${a.length} vs ${b.length}`);
|
|
802
|
+
}
|
|
803
|
+
let dotProduct = 0;
|
|
804
|
+
let normA = 0;
|
|
805
|
+
let normB = 0;
|
|
806
|
+
for (let i = 0;i < a.length; i++) {
|
|
807
|
+
dotProduct += a[i] * b[i];
|
|
808
|
+
normA += a[i] * a[i];
|
|
809
|
+
normB += b[i] * b[i];
|
|
810
|
+
}
|
|
811
|
+
const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
|
|
812
|
+
if (magnitude === 0)
|
|
813
|
+
return 0;
|
|
814
|
+
return dotProduct / magnitude;
|
|
815
|
+
}
|
|
337
816
|
|
|
338
|
-
// src/modules/
|
|
817
|
+
// src/modules/language/typescript/parseCode.ts
|
|
339
818
|
import * as ts from "typescript";
|
|
340
819
|
function parseCode(content, filepath) {
|
|
341
820
|
const ext = filepath.split(".").pop()?.toLowerCase();
|
|
@@ -526,6 +1005,11 @@ function generateChunkId(filepath, startLine, endLine) {
|
|
|
526
1005
|
}
|
|
527
1006
|
var init_parseCode = () => {};
|
|
528
1007
|
|
|
1008
|
+
// src/infrastructure/storage/fileIndexStorage.ts
|
|
1009
|
+
var init_fileIndexStorage = __esm(() => {
|
|
1010
|
+
init_entities();
|
|
1011
|
+
});
|
|
1012
|
+
|
|
529
1013
|
// src/domain/services/keywords.ts
|
|
530
1014
|
function extractKeywords(content, name, maxKeywords = 50) {
|
|
531
1015
|
const keywords = new Set;
|
|
@@ -714,9 +1198,9 @@ var init_keywords = __esm(() => {
|
|
|
714
1198
|
};
|
|
715
1199
|
});
|
|
716
1200
|
|
|
717
|
-
// src/
|
|
718
|
-
import * as
|
|
719
|
-
import * as
|
|
1201
|
+
// src/infrastructure/storage/symbolicIndex.ts
|
|
1202
|
+
import * as fs3 from "fs/promises";
|
|
1203
|
+
import * as path4 from "path";
|
|
720
1204
|
|
|
721
1205
|
class SymbolicIndex {
|
|
722
1206
|
meta = null;
|
|
@@ -725,7 +1209,7 @@ class SymbolicIndex {
|
|
|
725
1209
|
symbolicPath;
|
|
726
1210
|
moduleId;
|
|
727
1211
|
constructor(indexDir, moduleId) {
|
|
728
|
-
this.symbolicPath =
|
|
1212
|
+
this.symbolicPath = path4.join(indexDir, "index", moduleId, "symbolic");
|
|
729
1213
|
this.moduleId = moduleId;
|
|
730
1214
|
}
|
|
731
1215
|
async initialize() {
|
|
@@ -785,18 +1269,18 @@ class SymbolicIndex {
|
|
|
785
1269
|
throw new Error("Index not initialized");
|
|
786
1270
|
this.meta.lastUpdated = new Date().toISOString();
|
|
787
1271
|
this.meta.fileCount = this.fileSummaries.size;
|
|
788
|
-
await
|
|
789
|
-
const metaPath =
|
|
790
|
-
await
|
|
1272
|
+
await fs3.mkdir(this.symbolicPath, { recursive: true });
|
|
1273
|
+
const metaPath = path4.join(this.symbolicPath, "_meta.json");
|
|
1274
|
+
await fs3.writeFile(metaPath, JSON.stringify(this.meta, null, 2));
|
|
791
1275
|
for (const [filepath, summary] of this.fileSummaries) {
|
|
792
1276
|
const summaryPath = this.getFileSummaryPath(filepath);
|
|
793
|
-
await
|
|
794
|
-
await
|
|
1277
|
+
await fs3.mkdir(path4.dirname(summaryPath), { recursive: true });
|
|
1278
|
+
await fs3.writeFile(summaryPath, JSON.stringify(summary, null, 2));
|
|
795
1279
|
}
|
|
796
1280
|
}
|
|
797
1281
|
async load() {
|
|
798
|
-
const metaPath =
|
|
799
|
-
const metaContent = await
|
|
1282
|
+
const metaPath = path4.join(this.symbolicPath, "_meta.json");
|
|
1283
|
+
const metaContent = await fs3.readFile(metaPath, "utf-8");
|
|
800
1284
|
this.meta = JSON.parse(metaContent);
|
|
801
1285
|
this.fileSummaries.clear();
|
|
802
1286
|
await this.loadFileSummariesRecursive(this.symbolicPath);
|
|
@@ -804,14 +1288,14 @@ class SymbolicIndex {
|
|
|
804
1288
|
}
|
|
805
1289
|
async loadFileSummariesRecursive(dir) {
|
|
806
1290
|
try {
|
|
807
|
-
const entries = await
|
|
1291
|
+
const entries = await fs3.readdir(dir, { withFileTypes: true });
|
|
808
1292
|
for (const entry of entries) {
|
|
809
|
-
const fullPath =
|
|
1293
|
+
const fullPath = path4.join(dir, entry.name);
|
|
810
1294
|
if (entry.isDirectory()) {
|
|
811
1295
|
await this.loadFileSummariesRecursive(fullPath);
|
|
812
1296
|
} else if (entry.name.endsWith(".json") && entry.name !== "_meta.json") {
|
|
813
1297
|
try {
|
|
814
|
-
const content = await
|
|
1298
|
+
const content = await fs3.readFile(fullPath, "utf-8");
|
|
815
1299
|
const summary = JSON.parse(content);
|
|
816
1300
|
if (summary.filepath) {
|
|
817
1301
|
this.fileSummaries.set(summary.filepath, summary);
|
|
@@ -823,18 +1307,18 @@ class SymbolicIndex {
|
|
|
823
1307
|
}
|
|
824
1308
|
getFileSummaryPath(filepath) {
|
|
825
1309
|
const jsonPath = filepath.replace(/\.[^.]+$/, ".json");
|
|
826
|
-
return
|
|
1310
|
+
return path4.join(this.symbolicPath, jsonPath);
|
|
827
1311
|
}
|
|
828
1312
|
async deleteFileSummary(filepath) {
|
|
829
1313
|
try {
|
|
830
|
-
await
|
|
1314
|
+
await fs3.unlink(this.getFileSummaryPath(filepath));
|
|
831
1315
|
} catch {}
|
|
832
1316
|
this.fileSummaries.delete(filepath);
|
|
833
1317
|
}
|
|
834
1318
|
async exists() {
|
|
835
1319
|
try {
|
|
836
|
-
const metaPath =
|
|
837
|
-
await
|
|
1320
|
+
const metaPath = path4.join(this.symbolicPath, "_meta.json");
|
|
1321
|
+
await fs3.access(metaPath);
|
|
838
1322
|
return true;
|
|
839
1323
|
} catch {
|
|
840
1324
|
return false;
|
|
@@ -856,24 +1340,29 @@ class SymbolicIndex {
|
|
|
856
1340
|
this.bm25Index = new BM25Index;
|
|
857
1341
|
}
|
|
858
1342
|
}
|
|
859
|
-
var
|
|
860
|
-
init_keywords();
|
|
1343
|
+
var init_symbolicIndex = __esm(() => {
|
|
861
1344
|
init_keywords();
|
|
862
1345
|
});
|
|
863
1346
|
|
|
864
|
-
// src/
|
|
865
|
-
var
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
DEFAULT_TOP_K: () => DEFAULT_TOP_K,
|
|
869
|
-
DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE
|
|
1347
|
+
// src/infrastructure/storage/index.ts
|
|
1348
|
+
var init_storage = __esm(() => {
|
|
1349
|
+
init_fileIndexStorage();
|
|
1350
|
+
init_symbolicIndex();
|
|
870
1351
|
});
|
|
871
|
-
import * as path4 from "path";
|
|
872
1352
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
1353
|
+
// src/modules/language/typescript/index.ts
|
|
1354
|
+
var exports_typescript = {};
|
|
1355
|
+
__export(exports_typescript, {
|
|
1356
|
+
TypeScriptModule: () => TypeScriptModule,
|
|
1357
|
+
DEFAULT_TOP_K: () => DEFAULT_TOP_K2,
|
|
1358
|
+
DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE2
|
|
1359
|
+
});
|
|
1360
|
+
import * as path5 from "path";
|
|
1361
|
+
|
|
1362
|
+
class TypeScriptModule {
|
|
1363
|
+
id = "language/typescript";
|
|
1364
|
+
name = "TypeScript Search";
|
|
1365
|
+
description = "TypeScript-aware code search with AST parsing and semantic embeddings";
|
|
877
1366
|
version = "1.0.0";
|
|
878
1367
|
embeddingConfig = null;
|
|
879
1368
|
symbolicIndex = null;
|
|
@@ -909,10 +1398,10 @@ class SemanticModule {
|
|
|
909
1398
|
}));
|
|
910
1399
|
const references = this.extractReferences(content, filepath);
|
|
911
1400
|
const stats = await ctx.getFileStats(filepath);
|
|
912
|
-
const
|
|
1401
|
+
const currentConfig = getEmbeddingConfig();
|
|
913
1402
|
const moduleData = {
|
|
914
1403
|
embeddings,
|
|
915
|
-
embeddingModel:
|
|
1404
|
+
embeddingModel: currentConfig.model
|
|
916
1405
|
};
|
|
917
1406
|
const chunkTypes = [...new Set(parsedChunks.map((pc) => pc.type))];
|
|
918
1407
|
const exports = parsedChunks.filter((pc) => pc.isExported && pc.name).map((pc) => pc.name);
|
|
@@ -958,7 +1447,7 @@ class SemanticModule {
|
|
|
958
1447
|
this.pendingSummaries.clear();
|
|
959
1448
|
}
|
|
960
1449
|
async search(query, ctx, options = {}) {
|
|
961
|
-
const { topK =
|
|
1450
|
+
const { topK = DEFAULT_TOP_K2, minScore = DEFAULT_MIN_SCORE2, filePatterns } = options;
|
|
962
1451
|
const indexDir = getRaggrepDir(ctx.rootDir, ctx.config);
|
|
963
1452
|
const symbolicIndex = new SymbolicIndex(indexDir, this.id);
|
|
964
1453
|
let candidateFiles;
|
|
@@ -1062,37 +1551,37 @@ class SemanticModule {
|
|
|
1062
1551
|
while ((match = importRegex.exec(content)) !== null) {
|
|
1063
1552
|
const importPath = match[1];
|
|
1064
1553
|
if (importPath.startsWith(".")) {
|
|
1065
|
-
const dir =
|
|
1066
|
-
const resolved =
|
|
1554
|
+
const dir = path5.dirname(filepath);
|
|
1555
|
+
const resolved = path5.normalize(path5.join(dir, importPath));
|
|
1067
1556
|
references.push(resolved);
|
|
1068
1557
|
}
|
|
1069
1558
|
}
|
|
1070
1559
|
while ((match = requireRegex.exec(content)) !== null) {
|
|
1071
1560
|
const importPath = match[1];
|
|
1072
1561
|
if (importPath.startsWith(".")) {
|
|
1073
|
-
const dir =
|
|
1074
|
-
const resolved =
|
|
1562
|
+
const dir = path5.dirname(filepath);
|
|
1563
|
+
const resolved = path5.normalize(path5.join(dir, importPath));
|
|
1075
1564
|
references.push(resolved);
|
|
1076
1565
|
}
|
|
1077
1566
|
}
|
|
1078
1567
|
return references;
|
|
1079
1568
|
}
|
|
1080
1569
|
}
|
|
1081
|
-
var
|
|
1082
|
-
var
|
|
1570
|
+
var DEFAULT_MIN_SCORE2 = 0.15, DEFAULT_TOP_K2 = 10, SEMANTIC_WEIGHT = 0.7, BM25_WEIGHT = 0.3, TIER1_CANDIDATE_MULTIPLIER = 3;
|
|
1571
|
+
var init_typescript = __esm(() => {
|
|
1083
1572
|
init_embeddings();
|
|
1084
|
-
init_bm25();
|
|
1085
1573
|
init_config2();
|
|
1086
1574
|
init_parseCode();
|
|
1087
|
-
|
|
1575
|
+
init_storage();
|
|
1576
|
+
init_keywords();
|
|
1088
1577
|
init_keywords();
|
|
1089
1578
|
});
|
|
1090
1579
|
|
|
1091
|
-
// src/indexer/index.ts
|
|
1580
|
+
// src/app/indexer/index.ts
|
|
1092
1581
|
init_config2();
|
|
1093
1582
|
import { glob } from "glob";
|
|
1094
|
-
import * as
|
|
1095
|
-
import * as
|
|
1583
|
+
import * as fs6 from "fs/promises";
|
|
1584
|
+
import * as path9 from "path";
|
|
1096
1585
|
|
|
1097
1586
|
// src/modules/registry.ts
|
|
1098
1587
|
class ModuleRegistryImpl {
|
|
@@ -1116,20 +1605,594 @@ class ModuleRegistryImpl {
|
|
|
1116
1605
|
}
|
|
1117
1606
|
var registry = new ModuleRegistryImpl;
|
|
1118
1607
|
async function registerBuiltInModules() {
|
|
1119
|
-
const {
|
|
1120
|
-
|
|
1608
|
+
const { CoreModule: CoreModule2 } = await Promise.resolve().then(() => (init_core(), exports_core));
|
|
1609
|
+
const { TypeScriptModule: TypeScriptModule2 } = await Promise.resolve().then(() => (init_typescript(), exports_typescript));
|
|
1610
|
+
registry.register(new CoreModule2);
|
|
1611
|
+
registry.register(new TypeScriptModule2);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// src/introspection/index.ts
|
|
1615
|
+
import * as path8 from "path";
|
|
1616
|
+
import * as fs5 from "fs/promises";
|
|
1617
|
+
|
|
1618
|
+
// src/introspection/projectDetector.ts
|
|
1619
|
+
import * as path6 from "path";
|
|
1620
|
+
import * as fs4 from "fs/promises";
|
|
1621
|
+
var MAX_SCAN_DEPTH = 4;
|
|
1622
|
+
var SKIP_DIRS = new Set([
|
|
1623
|
+
"node_modules",
|
|
1624
|
+
".git",
|
|
1625
|
+
"dist",
|
|
1626
|
+
"build",
|
|
1627
|
+
".next",
|
|
1628
|
+
".nuxt",
|
|
1629
|
+
"coverage",
|
|
1630
|
+
".raggrep"
|
|
1631
|
+
]);
|
|
1632
|
+
var PROJECT_PATTERNS = [
|
|
1633
|
+
{ pattern: /^apps\/([^/]+)/, type: "app", defaultScope: "unknown" },
|
|
1634
|
+
{ pattern: /^packages\/([^/]+)/, type: "library", defaultScope: "shared" },
|
|
1635
|
+
{ pattern: /^libs\/([^/]+)/, type: "library", defaultScope: "shared" },
|
|
1636
|
+
{ pattern: /^services\/([^/]+)/, type: "service", defaultScope: "backend" },
|
|
1637
|
+
{ pattern: /^scripts\/([^/]+)/, type: "script", defaultScope: "tooling" },
|
|
1638
|
+
{ pattern: /^tools\/([^/]+)/, type: "script", defaultScope: "tooling" }
|
|
1639
|
+
];
|
|
1640
|
+
var SCOPE_KEYWORDS = {
|
|
1641
|
+
frontend: [
|
|
1642
|
+
"web",
|
|
1643
|
+
"webapp",
|
|
1644
|
+
"frontend",
|
|
1645
|
+
"client",
|
|
1646
|
+
"ui",
|
|
1647
|
+
"app",
|
|
1648
|
+
"mobile",
|
|
1649
|
+
"react",
|
|
1650
|
+
"vue",
|
|
1651
|
+
"angular",
|
|
1652
|
+
"next",
|
|
1653
|
+
"nuxt"
|
|
1654
|
+
],
|
|
1655
|
+
backend: [
|
|
1656
|
+
"api",
|
|
1657
|
+
"server",
|
|
1658
|
+
"backend",
|
|
1659
|
+
"service",
|
|
1660
|
+
"worker",
|
|
1661
|
+
"lambda",
|
|
1662
|
+
"functions"
|
|
1663
|
+
],
|
|
1664
|
+
shared: ["shared", "common", "utils", "lib", "core", "types", "models"],
|
|
1665
|
+
tooling: [
|
|
1666
|
+
"scripts",
|
|
1667
|
+
"tools",
|
|
1668
|
+
"cli",
|
|
1669
|
+
"devtools",
|
|
1670
|
+
"build",
|
|
1671
|
+
"config",
|
|
1672
|
+
"infra"
|
|
1673
|
+
],
|
|
1674
|
+
unknown: []
|
|
1675
|
+
};
|
|
1676
|
+
function detectScopeFromName(name) {
|
|
1677
|
+
const nameLower = name.toLowerCase();
|
|
1678
|
+
for (const [scope, keywords] of Object.entries(SCOPE_KEYWORDS)) {
|
|
1679
|
+
if (scope === "unknown")
|
|
1680
|
+
continue;
|
|
1681
|
+
for (const keyword of keywords) {
|
|
1682
|
+
if (nameLower.includes(keyword)) {
|
|
1683
|
+
return scope;
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
return "unknown";
|
|
1688
|
+
}
|
|
1689
|
+
async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
|
|
1690
|
+
if (depth > MAX_SCAN_DEPTH)
|
|
1691
|
+
return [];
|
|
1692
|
+
const results = [];
|
|
1693
|
+
const fullDir = currentDir ? path6.join(rootDir, currentDir) : rootDir;
|
|
1694
|
+
try {
|
|
1695
|
+
const entries = await fs4.readdir(fullDir, { withFileTypes: true });
|
|
1696
|
+
const hasPackageJson = entries.some((e) => e.isFile() && e.name === "package.json");
|
|
1697
|
+
if (hasPackageJson && currentDir) {
|
|
1698
|
+
const info = await parsePackageJson(rootDir, currentDir);
|
|
1699
|
+
if (info) {
|
|
1700
|
+
results.push(info);
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
for (const entry of entries) {
|
|
1704
|
+
if (!entry.isDirectory())
|
|
1705
|
+
continue;
|
|
1706
|
+
if (SKIP_DIRS.has(entry.name))
|
|
1707
|
+
continue;
|
|
1708
|
+
const subPath = currentDir ? `${currentDir}/${entry.name}` : entry.name;
|
|
1709
|
+
const subResults = await scanForPackageJsons(rootDir, subPath, depth + 1);
|
|
1710
|
+
results.push(...subResults);
|
|
1711
|
+
}
|
|
1712
|
+
} catch {}
|
|
1713
|
+
return results;
|
|
1714
|
+
}
|
|
1715
|
+
async function parsePackageJson(rootDir, relativePath) {
|
|
1716
|
+
try {
|
|
1717
|
+
const packageJsonPath = path6.join(rootDir, relativePath, "package.json");
|
|
1718
|
+
const content = await fs4.readFile(packageJsonPath, "utf-8");
|
|
1719
|
+
const pkg = JSON.parse(content);
|
|
1720
|
+
const name = pkg.name || path6.basename(relativePath);
|
|
1721
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
1722
|
+
let type = "unknown";
|
|
1723
|
+
if (deps["next"] || deps["react"] || deps["vue"] || deps["svelte"]) {
|
|
1724
|
+
type = "app";
|
|
1725
|
+
} else if (deps["express"] || deps["fastify"] || deps["koa"] || deps["hono"]) {
|
|
1726
|
+
type = "service";
|
|
1727
|
+
} else if (pkg.main || pkg.exports) {
|
|
1728
|
+
type = "library";
|
|
1729
|
+
}
|
|
1730
|
+
const hasWorkspaces = Boolean(pkg.workspaces);
|
|
1731
|
+
return { name, relativePath, type, hasWorkspaces };
|
|
1732
|
+
} catch {
|
|
1733
|
+
return null;
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
async function detectProjectStructure(rootDir) {
|
|
1737
|
+
const projectMap = new Map;
|
|
1738
|
+
let isMonorepo = false;
|
|
1739
|
+
try {
|
|
1740
|
+
const entries = await fs4.readdir(rootDir, { withFileTypes: true });
|
|
1741
|
+
const dirNames = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
1742
|
+
const monorepoPatterns = ["apps", "packages", "libs", "services"];
|
|
1743
|
+
const hasMonorepoStructure = monorepoPatterns.some((p) => dirNames.includes(p));
|
|
1744
|
+
if (hasMonorepoStructure) {
|
|
1745
|
+
isMonorepo = true;
|
|
1746
|
+
for (const pattern of monorepoPatterns) {
|
|
1747
|
+
if (!dirNames.includes(pattern))
|
|
1748
|
+
continue;
|
|
1749
|
+
const patternDir = path6.join(rootDir, pattern);
|
|
1750
|
+
try {
|
|
1751
|
+
const subDirs = await fs4.readdir(patternDir, { withFileTypes: true });
|
|
1752
|
+
for (const subDir of subDirs) {
|
|
1753
|
+
if (!subDir.isDirectory())
|
|
1754
|
+
continue;
|
|
1755
|
+
const projectRoot = `${pattern}/${subDir.name}`;
|
|
1756
|
+
const type = getProjectType(pattern);
|
|
1757
|
+
projectMap.set(projectRoot, {
|
|
1758
|
+
name: subDir.name,
|
|
1759
|
+
root: projectRoot,
|
|
1760
|
+
type
|
|
1761
|
+
});
|
|
1762
|
+
}
|
|
1763
|
+
} catch {}
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
const packageJsons = await scanForPackageJsons(rootDir);
|
|
1767
|
+
for (const pkg of packageJsons) {
|
|
1768
|
+
if (pkg.hasWorkspaces) {
|
|
1769
|
+
isMonorepo = true;
|
|
1770
|
+
}
|
|
1771
|
+
if (packageJsons.length > 1) {
|
|
1772
|
+
isMonorepo = true;
|
|
1773
|
+
}
|
|
1774
|
+
projectMap.set(pkg.relativePath, {
|
|
1775
|
+
name: pkg.name,
|
|
1776
|
+
root: pkg.relativePath,
|
|
1777
|
+
type: pkg.type
|
|
1778
|
+
});
|
|
1779
|
+
}
|
|
1780
|
+
let rootType = "unknown";
|
|
1781
|
+
try {
|
|
1782
|
+
const rootPkgPath = path6.join(rootDir, "package.json");
|
|
1783
|
+
const rootPkg = JSON.parse(await fs4.readFile(rootPkgPath, "utf-8"));
|
|
1784
|
+
if (rootPkg.workspaces) {
|
|
1785
|
+
isMonorepo = true;
|
|
1786
|
+
}
|
|
1787
|
+
const deps = { ...rootPkg.dependencies, ...rootPkg.devDependencies };
|
|
1788
|
+
if (deps["next"] || deps["react"] || deps["vue"]) {
|
|
1789
|
+
rootType = "app";
|
|
1790
|
+
} else if (deps["express"] || deps["fastify"] || deps["koa"]) {
|
|
1791
|
+
rootType = "service";
|
|
1792
|
+
}
|
|
1793
|
+
} catch {}
|
|
1794
|
+
const projects = Array.from(projectMap.values()).sort((a, b) => a.root.length - b.root.length);
|
|
1795
|
+
return {
|
|
1796
|
+
projects,
|
|
1797
|
+
isMonorepo,
|
|
1798
|
+
rootType: isMonorepo ? undefined : rootType
|
|
1799
|
+
};
|
|
1800
|
+
} catch {
|
|
1801
|
+
return {
|
|
1802
|
+
projects: [],
|
|
1803
|
+
isMonorepo: false,
|
|
1804
|
+
rootType: "unknown"
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
function getProjectType(patternDir) {
|
|
1809
|
+
switch (patternDir) {
|
|
1810
|
+
case "apps":
|
|
1811
|
+
return "app";
|
|
1812
|
+
case "packages":
|
|
1813
|
+
case "libs":
|
|
1814
|
+
return "library";
|
|
1815
|
+
case "services":
|
|
1816
|
+
return "service";
|
|
1817
|
+
case "scripts":
|
|
1818
|
+
case "tools":
|
|
1819
|
+
return "script";
|
|
1820
|
+
default:
|
|
1821
|
+
return "unknown";
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
function findProjectForFile(filepath, structure) {
|
|
1825
|
+
const normalizedPath = filepath.replace(/\\/g, "/");
|
|
1826
|
+
const matches = [];
|
|
1827
|
+
for (const project of structure.projects) {
|
|
1828
|
+
if (normalizedPath === project.root || normalizedPath.startsWith(project.root + "/")) {
|
|
1829
|
+
matches.push(project);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
if (matches.length > 0) {
|
|
1833
|
+
return matches.reduce((best, current) => current.root.length > best.root.length ? current : best);
|
|
1834
|
+
}
|
|
1835
|
+
for (const { pattern, type } of PROJECT_PATTERNS) {
|
|
1836
|
+
const match = normalizedPath.match(pattern);
|
|
1837
|
+
if (match) {
|
|
1838
|
+
return {
|
|
1839
|
+
name: match[1],
|
|
1840
|
+
root: match[0],
|
|
1841
|
+
type
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
return {
|
|
1846
|
+
name: "root",
|
|
1847
|
+
root: "",
|
|
1848
|
+
type: structure.rootType ?? "unknown"
|
|
1849
|
+
};
|
|
1121
1850
|
}
|
|
1122
1851
|
|
|
1123
|
-
// src/
|
|
1852
|
+
// src/introspection/fileIntrospector.ts
|
|
1853
|
+
import * as path7 from "path";
|
|
1854
|
+
var LAYER_PATTERNS2 = {
|
|
1855
|
+
controller: ["controller", "api", "routes", "route", "handler"],
|
|
1856
|
+
service: ["service", "logic", "usecase", "usecases", "handler"],
|
|
1857
|
+
repository: ["repository", "repo", "dao", "store", "persistence"],
|
|
1858
|
+
model: ["model", "models", "entity", "entities", "schema", "schemas", "types", "type"],
|
|
1859
|
+
util: ["util", "utils", "helper", "helpers", "common", "lib"],
|
|
1860
|
+
config: ["config", "configuration", "settings"],
|
|
1861
|
+
middleware: ["middleware", "middlewares"],
|
|
1862
|
+
domain: ["domain"],
|
|
1863
|
+
infrastructure: ["infrastructure", "infra"],
|
|
1864
|
+
application: ["application", "app"],
|
|
1865
|
+
presentation: ["presentation", "ui", "views", "view", "component", "components"],
|
|
1866
|
+
test: ["test", "tests", "spec", "specs", "__tests__", "e2e"]
|
|
1867
|
+
};
|
|
1868
|
+
var DOMAIN_PATTERNS = [
|
|
1869
|
+
"auth",
|
|
1870
|
+
"authentication",
|
|
1871
|
+
"user",
|
|
1872
|
+
"users",
|
|
1873
|
+
"account",
|
|
1874
|
+
"accounts",
|
|
1875
|
+
"profile",
|
|
1876
|
+
"profiles",
|
|
1877
|
+
"product",
|
|
1878
|
+
"products",
|
|
1879
|
+
"item",
|
|
1880
|
+
"items",
|
|
1881
|
+
"catalog",
|
|
1882
|
+
"order",
|
|
1883
|
+
"orders",
|
|
1884
|
+
"cart",
|
|
1885
|
+
"checkout",
|
|
1886
|
+
"payment",
|
|
1887
|
+
"payments",
|
|
1888
|
+
"billing",
|
|
1889
|
+
"subscription",
|
|
1890
|
+
"subscriptions",
|
|
1891
|
+
"notification",
|
|
1892
|
+
"notifications",
|
|
1893
|
+
"email",
|
|
1894
|
+
"sms",
|
|
1895
|
+
"report",
|
|
1896
|
+
"reports",
|
|
1897
|
+
"analytics",
|
|
1898
|
+
"metrics",
|
|
1899
|
+
"dashboard",
|
|
1900
|
+
"admin",
|
|
1901
|
+
"settings",
|
|
1902
|
+
"search",
|
|
1903
|
+
"chat",
|
|
1904
|
+
"message",
|
|
1905
|
+
"messages",
|
|
1906
|
+
"feed",
|
|
1907
|
+
"post",
|
|
1908
|
+
"posts",
|
|
1909
|
+
"comment",
|
|
1910
|
+
"comments",
|
|
1911
|
+
"media",
|
|
1912
|
+
"upload",
|
|
1913
|
+
"file",
|
|
1914
|
+
"files",
|
|
1915
|
+
"storage",
|
|
1916
|
+
"cache",
|
|
1917
|
+
"session",
|
|
1918
|
+
"log",
|
|
1919
|
+
"logs",
|
|
1920
|
+
"audit"
|
|
1921
|
+
];
|
|
1922
|
+
var FRAMEWORK_INDICATORS = {
|
|
1923
|
+
nextjs: ["next", "next/"],
|
|
1924
|
+
express: ["express"],
|
|
1925
|
+
fastify: ["fastify"],
|
|
1926
|
+
react: ["react"],
|
|
1927
|
+
vue: ["vue"],
|
|
1928
|
+
angular: ["@angular/"],
|
|
1929
|
+
nestjs: ["@nestjs/"],
|
|
1930
|
+
koa: ["koa"]
|
|
1931
|
+
};
|
|
1932
|
+
var EXTENSION_TO_LANGUAGE = {
|
|
1933
|
+
".ts": "typescript",
|
|
1934
|
+
".tsx": "typescript",
|
|
1935
|
+
".js": "javascript",
|
|
1936
|
+
".jsx": "javascript",
|
|
1937
|
+
".mjs": "javascript",
|
|
1938
|
+
".cjs": "javascript",
|
|
1939
|
+
".py": "python",
|
|
1940
|
+
".go": "go",
|
|
1941
|
+
".rs": "rust",
|
|
1942
|
+
".java": "java",
|
|
1943
|
+
".kt": "kotlin",
|
|
1944
|
+
".swift": "swift",
|
|
1945
|
+
".rb": "ruby",
|
|
1946
|
+
".php": "php",
|
|
1947
|
+
".cs": "csharp",
|
|
1948
|
+
".cpp": "cpp",
|
|
1949
|
+
".c": "c",
|
|
1950
|
+
".h": "c",
|
|
1951
|
+
".hpp": "cpp",
|
|
1952
|
+
".md": "markdown",
|
|
1953
|
+
".json": "json",
|
|
1954
|
+
".yaml": "yaml",
|
|
1955
|
+
".yml": "yaml"
|
|
1956
|
+
};
|
|
1957
|
+
function introspectFile(filepath, structure, fileContent) {
|
|
1958
|
+
const normalizedPath = filepath.replace(/\\/g, "/");
|
|
1959
|
+
const segments = normalizedPath.split("/").filter((s) => s.length > 0);
|
|
1960
|
+
const filename = segments[segments.length - 1] || "";
|
|
1961
|
+
const ext = path7.extname(filename);
|
|
1962
|
+
const project = findProjectForFile(normalizedPath, structure);
|
|
1963
|
+
const language = EXTENSION_TO_LANGUAGE[ext] || "unknown";
|
|
1964
|
+
const layer = detectLayer(segments, filename);
|
|
1965
|
+
const domain = detectDomain(segments);
|
|
1966
|
+
const scope = detectScope(segments, project, layer);
|
|
1967
|
+
let framework;
|
|
1968
|
+
if (fileContent) {
|
|
1969
|
+
framework = detectFramework(fileContent);
|
|
1970
|
+
}
|
|
1971
|
+
return {
|
|
1972
|
+
filepath: normalizedPath,
|
|
1973
|
+
project,
|
|
1974
|
+
scope,
|
|
1975
|
+
layer,
|
|
1976
|
+
domain,
|
|
1977
|
+
language,
|
|
1978
|
+
framework,
|
|
1979
|
+
depth: segments.length - 1,
|
|
1980
|
+
pathSegments: segments.slice(0, -1)
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
function detectLayer(segments, filename) {
|
|
1984
|
+
const filenameLower = filename.toLowerCase();
|
|
1985
|
+
for (const [layer, patterns] of Object.entries(LAYER_PATTERNS2)) {
|
|
1986
|
+
for (const pattern of patterns) {
|
|
1987
|
+
if (filenameLower.includes(pattern)) {
|
|
1988
|
+
return layer;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
for (let i = segments.length - 2;i >= 0; i--) {
|
|
1993
|
+
const segment = segments[i].toLowerCase();
|
|
1994
|
+
for (const [layer, patterns] of Object.entries(LAYER_PATTERNS2)) {
|
|
1995
|
+
if (patterns.includes(segment)) {
|
|
1996
|
+
return layer;
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
return;
|
|
2001
|
+
}
|
|
2002
|
+
function detectDomain(segments) {
|
|
2003
|
+
const skipSegments = new Set([
|
|
2004
|
+
"src",
|
|
2005
|
+
"lib",
|
|
2006
|
+
"app",
|
|
2007
|
+
"apps",
|
|
2008
|
+
"packages",
|
|
2009
|
+
"services",
|
|
2010
|
+
"modules",
|
|
2011
|
+
"features",
|
|
2012
|
+
...Object.values(LAYER_PATTERNS2).flat()
|
|
2013
|
+
]);
|
|
2014
|
+
for (const segment of segments) {
|
|
2015
|
+
const segmentLower = segment.toLowerCase();
|
|
2016
|
+
if (skipSegments.has(segmentLower))
|
|
2017
|
+
continue;
|
|
2018
|
+
if (DOMAIN_PATTERNS.includes(segmentLower)) {
|
|
2019
|
+
return segmentLower;
|
|
2020
|
+
}
|
|
2021
|
+
for (const domain of DOMAIN_PATTERNS) {
|
|
2022
|
+
if (segmentLower.startsWith(domain) || segmentLower.endsWith(domain)) {
|
|
2023
|
+
return domain;
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
function detectScope(segments, project, layer) {
|
|
2030
|
+
const projectScope = detectScopeFromName(project.name);
|
|
2031
|
+
if (projectScope !== "unknown") {
|
|
2032
|
+
return projectScope;
|
|
2033
|
+
}
|
|
2034
|
+
if (layer) {
|
|
2035
|
+
switch (layer) {
|
|
2036
|
+
case "controller":
|
|
2037
|
+
case "repository":
|
|
2038
|
+
case "middleware":
|
|
2039
|
+
return "backend";
|
|
2040
|
+
case "presentation":
|
|
2041
|
+
return "frontend";
|
|
2042
|
+
case "util":
|
|
2043
|
+
case "model":
|
|
2044
|
+
return "shared";
|
|
2045
|
+
case "test":
|
|
2046
|
+
return "tooling";
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
for (const segment of segments) {
|
|
2050
|
+
const segmentLower = segment.toLowerCase();
|
|
2051
|
+
if (["server", "api", "backend"].includes(segmentLower)) {
|
|
2052
|
+
return "backend";
|
|
2053
|
+
}
|
|
2054
|
+
if (["client", "web", "frontend", "ui"].includes(segmentLower)) {
|
|
2055
|
+
return "frontend";
|
|
2056
|
+
}
|
|
2057
|
+
if (["shared", "common", "lib", "libs"].includes(segmentLower)) {
|
|
2058
|
+
return "shared";
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
return "unknown";
|
|
2062
|
+
}
|
|
2063
|
+
function detectFramework(content) {
|
|
2064
|
+
for (const [framework, indicators] of Object.entries(FRAMEWORK_INDICATORS)) {
|
|
2065
|
+
for (const indicator of indicators) {
|
|
2066
|
+
if (content.includes(`from '${indicator}`) || content.includes(`from "${indicator}`) || content.includes(`require('${indicator}`) || content.includes(`require("${indicator}`)) {
|
|
2067
|
+
return framework;
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
return;
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
// src/introspection/index.ts
|
|
2075
|
+
init_config2();
|
|
2076
|
+
|
|
2077
|
+
class IntrospectionIndex {
|
|
2078
|
+
rootDir;
|
|
2079
|
+
structure = null;
|
|
2080
|
+
files = new Map;
|
|
2081
|
+
config = {};
|
|
2082
|
+
constructor(rootDir) {
|
|
2083
|
+
this.rootDir = rootDir;
|
|
2084
|
+
}
|
|
2085
|
+
async initialize() {
|
|
2086
|
+
this.structure = await detectProjectStructure(this.rootDir);
|
|
2087
|
+
try {
|
|
2088
|
+
const configPath = path8.join(this.rootDir, ".raggrep", "config.json");
|
|
2089
|
+
const configContent = await fs5.readFile(configPath, "utf-8");
|
|
2090
|
+
const config = JSON.parse(configContent);
|
|
2091
|
+
this.config = config.introspection || {};
|
|
2092
|
+
} catch {}
|
|
2093
|
+
}
|
|
2094
|
+
getStructure() {
|
|
2095
|
+
return this.structure;
|
|
2096
|
+
}
|
|
2097
|
+
addFile(filepath, content) {
|
|
2098
|
+
if (!this.structure) {
|
|
2099
|
+
throw new Error("IntrospectionIndex not initialized");
|
|
2100
|
+
}
|
|
2101
|
+
const intro = introspectFile(filepath, this.structure, content);
|
|
2102
|
+
this.applyOverrides(intro);
|
|
2103
|
+
this.files.set(filepath, intro);
|
|
2104
|
+
return intro;
|
|
2105
|
+
}
|
|
2106
|
+
getFile(filepath) {
|
|
2107
|
+
return this.files.get(filepath);
|
|
2108
|
+
}
|
|
2109
|
+
getAllFiles() {
|
|
2110
|
+
return Array.from(this.files.values());
|
|
2111
|
+
}
|
|
2112
|
+
applyOverrides(intro) {
|
|
2113
|
+
if (!this.config.projects)
|
|
2114
|
+
return;
|
|
2115
|
+
for (const [projectPath, overrides] of Object.entries(this.config.projects)) {
|
|
2116
|
+
if (intro.filepath.startsWith(projectPath + "/") || intro.project.root === projectPath) {
|
|
2117
|
+
if (overrides.scope) {
|
|
2118
|
+
intro.scope = overrides.scope;
|
|
2119
|
+
}
|
|
2120
|
+
if (overrides.framework) {
|
|
2121
|
+
intro.framework = overrides.framework;
|
|
2122
|
+
}
|
|
2123
|
+
break;
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
async save(config) {
|
|
2128
|
+
const introDir = path8.join(getRaggrepDir(this.rootDir, config), "introspection");
|
|
2129
|
+
await fs5.mkdir(introDir, { recursive: true });
|
|
2130
|
+
const projectPath = path8.join(introDir, "_project.json");
|
|
2131
|
+
await fs5.writeFile(projectPath, JSON.stringify({
|
|
2132
|
+
version: "1.0.0",
|
|
2133
|
+
lastUpdated: new Date().toISOString(),
|
|
2134
|
+
structure: this.structure
|
|
2135
|
+
}, null, 2));
|
|
2136
|
+
for (const [filepath, intro] of this.files) {
|
|
2137
|
+
const introFilePath = path8.join(introDir, "files", filepath.replace(/\.[^.]+$/, ".json"));
|
|
2138
|
+
await fs5.mkdir(path8.dirname(introFilePath), { recursive: true });
|
|
2139
|
+
await fs5.writeFile(introFilePath, JSON.stringify(intro, null, 2));
|
|
2140
|
+
}
|
|
2141
|
+
console.log(` [Introspection] Saved metadata for ${this.files.size} files`);
|
|
2142
|
+
}
|
|
2143
|
+
async load(config) {
|
|
2144
|
+
const introDir = path8.join(getRaggrepDir(this.rootDir, config), "introspection");
|
|
2145
|
+
try {
|
|
2146
|
+
const projectPath = path8.join(introDir, "_project.json");
|
|
2147
|
+
const projectContent = await fs5.readFile(projectPath, "utf-8");
|
|
2148
|
+
const projectData = JSON.parse(projectContent);
|
|
2149
|
+
this.structure = projectData.structure;
|
|
2150
|
+
await this.loadFilesRecursive(path8.join(introDir, "files"), "");
|
|
2151
|
+
} catch {
|
|
2152
|
+
this.structure = null;
|
|
2153
|
+
this.files.clear();
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
async loadFilesRecursive(basePath, prefix) {
|
|
2157
|
+
try {
|
|
2158
|
+
const entries = await fs5.readdir(basePath, { withFileTypes: true });
|
|
2159
|
+
for (const entry of entries) {
|
|
2160
|
+
const entryPath = path8.join(basePath, entry.name);
|
|
2161
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
2162
|
+
if (entry.isDirectory()) {
|
|
2163
|
+
await this.loadFilesRecursive(entryPath, relativePath);
|
|
2164
|
+
} else if (entry.name.endsWith(".json")) {
|
|
2165
|
+
const content = await fs5.readFile(entryPath, "utf-8");
|
|
2166
|
+
const intro = JSON.parse(content);
|
|
2167
|
+
this.files.set(intro.filepath, intro);
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
} catch {}
|
|
2171
|
+
}
|
|
2172
|
+
clear() {
|
|
2173
|
+
this.files.clear();
|
|
2174
|
+
this.structure = null;
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
// src/app/indexer/watcher.ts
|
|
1124
2179
|
import { watch } from "chokidar";
|
|
1125
2180
|
init_config2();
|
|
1126
2181
|
|
|
1127
|
-
// src/indexer/index.ts
|
|
2182
|
+
// src/app/indexer/index.ts
|
|
1128
2183
|
async function indexDirectory(rootDir, options = {}) {
|
|
1129
2184
|
const verbose = options.verbose ?? false;
|
|
1130
|
-
rootDir =
|
|
2185
|
+
rootDir = path9.resolve(rootDir);
|
|
1131
2186
|
console.log(`Indexing directory: ${rootDir}`);
|
|
1132
2187
|
const config = await loadConfig(rootDir);
|
|
2188
|
+
const introspection = new IntrospectionIndex(rootDir);
|
|
2189
|
+
await introspection.initialize();
|
|
2190
|
+
if (verbose) {
|
|
2191
|
+
const structure = introspection.getStructure();
|
|
2192
|
+
if (structure?.isMonorepo) {
|
|
2193
|
+
console.log(`Detected monorepo with ${structure.projects.length} projects`);
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
1133
2196
|
await registerBuiltInModules();
|
|
1134
2197
|
const enabledModules = registry.getEnabled(config);
|
|
1135
2198
|
if (enabledModules.length === 0) {
|
|
@@ -1146,7 +2209,7 @@ async function indexDirectory(rootDir, options = {}) {
|
|
|
1146
2209
|
const moduleConfig = getModuleConfig(config, module.id);
|
|
1147
2210
|
if (module.initialize && moduleConfig) {
|
|
1148
2211
|
const configWithOverrides = { ...moduleConfig };
|
|
1149
|
-
if (options.model && module.id === "
|
|
2212
|
+
if (options.model && module.id === "language/typescript") {
|
|
1150
2213
|
configWithOverrides.options = {
|
|
1151
2214
|
...configWithOverrides.options,
|
|
1152
2215
|
embeddingModel: options.model
|
|
@@ -1154,7 +2217,7 @@ async function indexDirectory(rootDir, options = {}) {
|
|
|
1154
2217
|
}
|
|
1155
2218
|
await module.initialize(configWithOverrides);
|
|
1156
2219
|
}
|
|
1157
|
-
const result = await indexWithModule(rootDir, files, module, config, verbose);
|
|
2220
|
+
const result = await indexWithModule(rootDir, files, module, config, verbose, introspection);
|
|
1158
2221
|
results.push(result);
|
|
1159
2222
|
if (module.finalize) {
|
|
1160
2223
|
console.log(`[${module.name}] Building secondary indexes...`);
|
|
@@ -1162,12 +2225,12 @@ async function indexDirectory(rootDir, options = {}) {
|
|
|
1162
2225
|
rootDir,
|
|
1163
2226
|
config,
|
|
1164
2227
|
readFile: async (filepath) => {
|
|
1165
|
-
const fullPath =
|
|
1166
|
-
return
|
|
2228
|
+
const fullPath = path9.isAbsolute(filepath) ? filepath : path9.join(rootDir, filepath);
|
|
2229
|
+
return fs6.readFile(fullPath, "utf-8");
|
|
1167
2230
|
},
|
|
1168
2231
|
getFileStats: async (filepath) => {
|
|
1169
|
-
const fullPath =
|
|
1170
|
-
const stats = await
|
|
2232
|
+
const fullPath = path9.isAbsolute(filepath) ? filepath : path9.join(rootDir, filepath);
|
|
2233
|
+
const stats = await fs6.stat(fullPath);
|
|
1171
2234
|
return { lastModified: stats.mtime.toISOString() };
|
|
1172
2235
|
}
|
|
1173
2236
|
};
|
|
@@ -1175,10 +2238,11 @@ async function indexDirectory(rootDir, options = {}) {
|
|
|
1175
2238
|
}
|
|
1176
2239
|
console.log(`[${module.name}] Complete: ${result.indexed} indexed, ${result.skipped} skipped, ${result.errors} errors`);
|
|
1177
2240
|
}
|
|
2241
|
+
await introspection.save(config);
|
|
1178
2242
|
await updateGlobalManifest(rootDir, enabledModules, config);
|
|
1179
2243
|
return results;
|
|
1180
2244
|
}
|
|
1181
|
-
async function indexWithModule(rootDir, files, module, config, verbose) {
|
|
2245
|
+
async function indexWithModule(rootDir, files, module, config, verbose, introspection) {
|
|
1182
2246
|
const result = {
|
|
1183
2247
|
moduleId: module.id,
|
|
1184
2248
|
indexed: 0,
|
|
@@ -1190,19 +2254,20 @@ async function indexWithModule(rootDir, files, module, config, verbose) {
|
|
|
1190
2254
|
rootDir,
|
|
1191
2255
|
config,
|
|
1192
2256
|
readFile: async (filepath) => {
|
|
1193
|
-
const fullPath =
|
|
1194
|
-
return
|
|
2257
|
+
const fullPath = path9.isAbsolute(filepath) ? filepath : path9.join(rootDir, filepath);
|
|
2258
|
+
return fs6.readFile(fullPath, "utf-8");
|
|
1195
2259
|
},
|
|
1196
2260
|
getFileStats: async (filepath) => {
|
|
1197
|
-
const fullPath =
|
|
1198
|
-
const stats = await
|
|
2261
|
+
const fullPath = path9.isAbsolute(filepath) ? filepath : path9.join(rootDir, filepath);
|
|
2262
|
+
const stats = await fs6.stat(fullPath);
|
|
1199
2263
|
return { lastModified: stats.mtime.toISOString() };
|
|
1200
|
-
}
|
|
2264
|
+
},
|
|
2265
|
+
getIntrospection: (filepath) => introspection.getFile(filepath)
|
|
1201
2266
|
};
|
|
1202
2267
|
for (const filepath of files) {
|
|
1203
|
-
const relativePath =
|
|
2268
|
+
const relativePath = path9.relative(rootDir, filepath);
|
|
1204
2269
|
try {
|
|
1205
|
-
const stats = await
|
|
2270
|
+
const stats = await fs6.stat(filepath);
|
|
1206
2271
|
const lastModified = stats.mtime.toISOString();
|
|
1207
2272
|
const existingEntry = manifest.files[relativePath];
|
|
1208
2273
|
if (existingEntry && existingEntry.lastModified === lastModified) {
|
|
@@ -1212,7 +2277,8 @@ async function indexWithModule(rootDir, files, module, config, verbose) {
|
|
|
1212
2277
|
result.skipped++;
|
|
1213
2278
|
continue;
|
|
1214
2279
|
}
|
|
1215
|
-
const content = await
|
|
2280
|
+
const content = await fs6.readFile(filepath, "utf-8");
|
|
2281
|
+
introspection.addFile(relativePath, content);
|
|
1216
2282
|
if (verbose) {
|
|
1217
2283
|
console.log(` Processing ${relativePath}...`);
|
|
1218
2284
|
}
|
|
@@ -1256,7 +2322,7 @@ async function findFiles(rootDir, config) {
|
|
|
1256
2322
|
async function loadModuleManifest(rootDir, moduleId, config) {
|
|
1257
2323
|
const manifestPath = getModuleManifestPath(rootDir, moduleId, config);
|
|
1258
2324
|
try {
|
|
1259
|
-
const content = await
|
|
2325
|
+
const content = await fs6.readFile(manifestPath, "utf-8");
|
|
1260
2326
|
return JSON.parse(content);
|
|
1261
2327
|
} catch {
|
|
1262
2328
|
return {
|
|
@@ -1269,14 +2335,14 @@ async function loadModuleManifest(rootDir, moduleId, config) {
|
|
|
1269
2335
|
}
|
|
1270
2336
|
async function writeModuleManifest(rootDir, moduleId, manifest, config) {
|
|
1271
2337
|
const manifestPath = getModuleManifestPath(rootDir, moduleId, config);
|
|
1272
|
-
await
|
|
1273
|
-
await
|
|
2338
|
+
await fs6.mkdir(path9.dirname(manifestPath), { recursive: true });
|
|
2339
|
+
await fs6.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
|
|
1274
2340
|
}
|
|
1275
2341
|
async function writeFileIndex(rootDir, moduleId, filepath, fileIndex, config) {
|
|
1276
2342
|
const indexPath = getModuleIndexPath(rootDir, moduleId, config);
|
|
1277
|
-
const indexFilePath =
|
|
1278
|
-
await
|
|
1279
|
-
await
|
|
2343
|
+
const indexFilePath = path9.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
|
|
2344
|
+
await fs6.mkdir(path9.dirname(indexFilePath), { recursive: true });
|
|
2345
|
+
await fs6.writeFile(indexFilePath, JSON.stringify(fileIndex, null, 2));
|
|
1280
2346
|
}
|
|
1281
2347
|
async function updateGlobalManifest(rootDir, modules, config) {
|
|
1282
2348
|
const manifestPath = getGlobalManifestPath(rootDir, config);
|
|
@@ -1285,12 +2351,12 @@ async function updateGlobalManifest(rootDir, modules, config) {
|
|
|
1285
2351
|
lastUpdated: new Date().toISOString(),
|
|
1286
2352
|
modules: modules.map((m) => m.id)
|
|
1287
2353
|
};
|
|
1288
|
-
await
|
|
1289
|
-
await
|
|
2354
|
+
await fs6.mkdir(path9.dirname(manifestPath), { recursive: true });
|
|
2355
|
+
await fs6.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
|
|
1290
2356
|
}
|
|
1291
2357
|
async function cleanupIndex(rootDir, options = {}) {
|
|
1292
2358
|
const verbose = options.verbose ?? false;
|
|
1293
|
-
rootDir =
|
|
2359
|
+
rootDir = path9.resolve(rootDir);
|
|
1294
2360
|
console.log(`Cleaning up index in: ${rootDir}`);
|
|
1295
2361
|
const config = await loadConfig(rootDir);
|
|
1296
2362
|
await registerBuiltInModules();
|
|
@@ -1320,9 +2386,9 @@ async function cleanupModuleIndex(rootDir, moduleId, config, verbose) {
|
|
|
1320
2386
|
const filesToRemove = [];
|
|
1321
2387
|
const updatedFiles = {};
|
|
1322
2388
|
for (const [filepath, entry] of Object.entries(manifest.files)) {
|
|
1323
|
-
const fullPath =
|
|
2389
|
+
const fullPath = path9.join(rootDir, filepath);
|
|
1324
2390
|
try {
|
|
1325
|
-
await
|
|
2391
|
+
await fs6.access(fullPath);
|
|
1326
2392
|
updatedFiles[filepath] = entry;
|
|
1327
2393
|
result.kept++;
|
|
1328
2394
|
} catch {
|
|
@@ -1334,9 +2400,9 @@ async function cleanupModuleIndex(rootDir, moduleId, config, verbose) {
|
|
|
1334
2400
|
}
|
|
1335
2401
|
}
|
|
1336
2402
|
for (const filepath of filesToRemove) {
|
|
1337
|
-
const indexFilePath =
|
|
2403
|
+
const indexFilePath = path9.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
|
|
1338
2404
|
try {
|
|
1339
|
-
await
|
|
2405
|
+
await fs6.unlink(indexFilePath);
|
|
1340
2406
|
} catch {}
|
|
1341
2407
|
}
|
|
1342
2408
|
manifest.files = updatedFiles;
|
|
@@ -1347,16 +2413,16 @@ async function cleanupModuleIndex(rootDir, moduleId, config, verbose) {
|
|
|
1347
2413
|
}
|
|
1348
2414
|
async function cleanupEmptyDirectories(dir) {
|
|
1349
2415
|
try {
|
|
1350
|
-
const entries = await
|
|
2416
|
+
const entries = await fs6.readdir(dir, { withFileTypes: true });
|
|
1351
2417
|
for (const entry of entries) {
|
|
1352
2418
|
if (entry.isDirectory()) {
|
|
1353
|
-
const subDir =
|
|
2419
|
+
const subDir = path9.join(dir, entry.name);
|
|
1354
2420
|
await cleanupEmptyDirectories(subDir);
|
|
1355
2421
|
}
|
|
1356
2422
|
}
|
|
1357
|
-
const remainingEntries = await
|
|
2423
|
+
const remainingEntries = await fs6.readdir(dir);
|
|
1358
2424
|
if (remainingEntries.length === 0) {
|
|
1359
|
-
await
|
|
2425
|
+
await fs6.rmdir(dir);
|
|
1360
2426
|
return true;
|
|
1361
2427
|
}
|
|
1362
2428
|
return false;
|
|
@@ -1365,12 +2431,12 @@ async function cleanupEmptyDirectories(dir) {
|
|
|
1365
2431
|
}
|
|
1366
2432
|
}
|
|
1367
2433
|
|
|
1368
|
-
// src/search/index.ts
|
|
2434
|
+
// src/app/search/index.ts
|
|
1369
2435
|
init_config2();
|
|
1370
|
-
import * as
|
|
1371
|
-
import * as
|
|
2436
|
+
import * as fs7 from "fs/promises";
|
|
2437
|
+
import * as path10 from "path";
|
|
1372
2438
|
async function search(rootDir, query, options = {}) {
|
|
1373
|
-
rootDir =
|
|
2439
|
+
rootDir = path10.resolve(rootDir);
|
|
1374
2440
|
console.log(`Searching for: "${query}"`);
|
|
1375
2441
|
const config = await loadConfig(rootDir);
|
|
1376
2442
|
await registerBuiltInModules();
|
|
@@ -1411,9 +2477,9 @@ function createSearchContext(rootDir, moduleId, config) {
|
|
|
1411
2477
|
config,
|
|
1412
2478
|
loadFileIndex: async (filepath) => {
|
|
1413
2479
|
const hasExtension = /\.[^./]+$/.test(filepath);
|
|
1414
|
-
const indexFilePath = hasExtension ?
|
|
2480
|
+
const indexFilePath = hasExtension ? path10.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path10.join(indexPath, filepath + ".json");
|
|
1415
2481
|
try {
|
|
1416
|
-
const content = await
|
|
2482
|
+
const content = await fs7.readFile(indexFilePath, "utf-8");
|
|
1417
2483
|
return JSON.parse(content);
|
|
1418
2484
|
} catch {
|
|
1419
2485
|
return null;
|
|
@@ -1423,7 +2489,7 @@ function createSearchContext(rootDir, moduleId, config) {
|
|
|
1423
2489
|
const files = [];
|
|
1424
2490
|
await traverseDirectory(indexPath, files, indexPath);
|
|
1425
2491
|
return files.filter((f) => f.endsWith(".json") && !f.endsWith("manifest.json")).map((f) => {
|
|
1426
|
-
const relative3 =
|
|
2492
|
+
const relative3 = path10.relative(indexPath, f);
|
|
1427
2493
|
return relative3.replace(/\.json$/, "");
|
|
1428
2494
|
});
|
|
1429
2495
|
}
|
|
@@ -1431,9 +2497,9 @@ function createSearchContext(rootDir, moduleId, config) {
|
|
|
1431
2497
|
}
|
|
1432
2498
|
async function traverseDirectory(dir, files, basePath) {
|
|
1433
2499
|
try {
|
|
1434
|
-
const entries = await
|
|
2500
|
+
const entries = await fs7.readdir(dir, { withFileTypes: true });
|
|
1435
2501
|
for (const entry of entries) {
|
|
1436
|
-
const fullPath =
|
|
2502
|
+
const fullPath = path10.join(dir, entry.name);
|
|
1437
2503
|
if (entry.isDirectory()) {
|
|
1438
2504
|
await traverseDirectory(fullPath, files, basePath);
|
|
1439
2505
|
} else if (entry.isFile()) {
|
|
@@ -1445,7 +2511,7 @@ async function traverseDirectory(dir, files, basePath) {
|
|
|
1445
2511
|
async function loadGlobalManifest(rootDir, config) {
|
|
1446
2512
|
const manifestPath = getGlobalManifestPath(rootDir, config);
|
|
1447
2513
|
try {
|
|
1448
|
-
const content = await
|
|
2514
|
+
const content = await fs7.readFile(manifestPath, "utf-8");
|
|
1449
2515
|
return JSON.parse(content);
|
|
1450
2516
|
} catch {
|
|
1451
2517
|
return null;
|
|
@@ -1509,4 +2575,4 @@ export {
|
|
|
1509
2575
|
cleanup
|
|
1510
2576
|
};
|
|
1511
2577
|
|
|
1512
|
-
//# debugId=
|
|
2578
|
+
//# debugId=0E3E5BCA1147AB0A64756E2164756E21
|