raggrep 0.1.5 → 0.1.6
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 +23 -9
- package/dist/cli/main.js +2479 -1627
- package/dist/cli/main.js.map +18 -12
- package/dist/index.js +2588 -1733
- package/dist/index.js.map +18 -12
- package/dist/infrastructure/config/configLoader.d.ts +22 -3
- package/dist/infrastructure/config/index.d.ts +1 -1
- package/dist/introspection/conventions/configFiles.d.ts +10 -0
- package/dist/introspection/conventions/conventions.test.d.ts +4 -0
- package/dist/introspection/conventions/entryPoints.d.ts +10 -0
- package/dist/introspection/conventions/frameworks/convex.d.ts +11 -0
- package/dist/introspection/conventions/frameworks/index.d.ts +22 -0
- package/dist/introspection/conventions/frameworks/nextjs.d.ts +10 -0
- package/dist/introspection/conventions/index.d.ts +39 -0
- package/dist/introspection/conventions/types.d.ts +62 -0
- package/dist/tests/integration.test.d.ts +9 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -76,7 +76,8 @@ var init_config = __esm(() => {
|
|
|
76
76
|
".go",
|
|
77
77
|
".rs",
|
|
78
78
|
".java",
|
|
79
|
-
".md"
|
|
79
|
+
".md",
|
|
80
|
+
".txt"
|
|
80
81
|
];
|
|
81
82
|
});
|
|
82
83
|
|
|
@@ -89,20 +90,40 @@ var init_entities = __esm(() => {
|
|
|
89
90
|
// src/infrastructure/config/configLoader.ts
|
|
90
91
|
import * as path from "path";
|
|
91
92
|
import * as fs from "fs/promises";
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
import * as os from "os";
|
|
94
|
+
import * as crypto from "crypto";
|
|
95
|
+
function hashPath(inputPath) {
|
|
96
|
+
return crypto.createHash("sha256").update(inputPath).digest("hex").slice(0, 12);
|
|
97
|
+
}
|
|
98
|
+
function getRaggrepDir(rootDir, _config = DEFAULT_CONFIG) {
|
|
99
|
+
const absoluteRoot = path.resolve(rootDir);
|
|
100
|
+
const projectHash = hashPath(absoluteRoot);
|
|
101
|
+
return path.join(RAGGREP_TEMP_BASE, projectHash);
|
|
102
|
+
}
|
|
103
|
+
function getIndexLocation(rootDir) {
|
|
104
|
+
const absoluteRoot = path.resolve(rootDir);
|
|
105
|
+
const projectHash = hashPath(absoluteRoot);
|
|
106
|
+
return {
|
|
107
|
+
indexDir: path.join(RAGGREP_TEMP_BASE, projectHash),
|
|
108
|
+
projectRoot: absoluteRoot,
|
|
109
|
+
projectHash
|
|
110
|
+
};
|
|
94
111
|
}
|
|
95
112
|
function getModuleIndexPath(rootDir, moduleId, config = DEFAULT_CONFIG) {
|
|
96
|
-
|
|
113
|
+
const indexDir = getRaggrepDir(rootDir, config);
|
|
114
|
+
return path.join(indexDir, "index", moduleId);
|
|
97
115
|
}
|
|
98
116
|
function getModuleManifestPath(rootDir, moduleId, config = DEFAULT_CONFIG) {
|
|
99
|
-
|
|
117
|
+
const indexDir = getRaggrepDir(rootDir, config);
|
|
118
|
+
return path.join(indexDir, "index", moduleId, "manifest.json");
|
|
100
119
|
}
|
|
101
120
|
function getGlobalManifestPath(rootDir, config = DEFAULT_CONFIG) {
|
|
102
|
-
|
|
121
|
+
const indexDir = getRaggrepDir(rootDir, config);
|
|
122
|
+
return path.join(indexDir, "manifest.json");
|
|
103
123
|
}
|
|
104
124
|
function getConfigPath(rootDir, config = DEFAULT_CONFIG) {
|
|
105
|
-
|
|
125
|
+
const indexDir = getRaggrepDir(rootDir, config);
|
|
126
|
+
return path.join(indexDir, "config.json");
|
|
106
127
|
}
|
|
107
128
|
async function loadConfig(rootDir) {
|
|
108
129
|
const configPath = getConfigPath(rootDir, DEFAULT_CONFIG);
|
|
@@ -129,10 +150,11 @@ function getEmbeddingConfigFromModule(moduleConfig) {
|
|
|
129
150
|
showProgress: options.showProgress !== false
|
|
130
151
|
};
|
|
131
152
|
}
|
|
132
|
-
var DEFAULT_CONFIG, EMBEDDING_MODELS;
|
|
153
|
+
var DEFAULT_CONFIG, RAGGREP_TEMP_BASE, EMBEDDING_MODELS;
|
|
133
154
|
var init_configLoader = __esm(() => {
|
|
134
155
|
init_entities();
|
|
135
156
|
DEFAULT_CONFIG = createDefaultConfig();
|
|
157
|
+
RAGGREP_TEMP_BASE = path.join(os.tmpdir(), "raggrep-indexes");
|
|
136
158
|
EMBEDDING_MODELS = {
|
|
137
159
|
"all-MiniLM-L6-v2": "Xenova/all-MiniLM-L6-v2",
|
|
138
160
|
"all-MiniLM-L12-v2": "Xenova/all-MiniLM-L12-v2",
|
|
@@ -248,1932 +270,2763 @@ function normalizeScore(score, midpoint = 5) {
|
|
|
248
270
|
}
|
|
249
271
|
var BM25_K1 = 1.5, BM25_B = 0.75;
|
|
250
272
|
|
|
251
|
-
// src/
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
273
|
+
// src/introspection/projectDetector.ts
|
|
274
|
+
import * as path2 from "path";
|
|
275
|
+
import * as fs2 from "fs/promises";
|
|
276
|
+
function detectScopeFromName(name) {
|
|
277
|
+
const nameLower = name.toLowerCase();
|
|
278
|
+
for (const [scope, keywords] of Object.entries(SCOPE_KEYWORDS)) {
|
|
279
|
+
if (scope === "unknown")
|
|
280
|
+
continue;
|
|
281
|
+
for (const keyword of keywords) {
|
|
282
|
+
if (nameLower.includes(keyword)) {
|
|
283
|
+
return scope;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return "unknown";
|
|
288
|
+
}
|
|
289
|
+
async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
|
|
290
|
+
if (depth > MAX_SCAN_DEPTH)
|
|
291
|
+
return [];
|
|
292
|
+
const results = [];
|
|
293
|
+
const fullDir = currentDir ? path2.join(rootDir, currentDir) : rootDir;
|
|
294
|
+
try {
|
|
295
|
+
const entries = await fs2.readdir(fullDir, { withFileTypes: true });
|
|
296
|
+
const hasPackageJson = entries.some((e) => e.isFile() && e.name === "package.json");
|
|
297
|
+
if (hasPackageJson && currentDir) {
|
|
298
|
+
const info = await parsePackageJson(rootDir, currentDir);
|
|
299
|
+
if (info) {
|
|
300
|
+
results.push(info);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
for (const entry of entries) {
|
|
304
|
+
if (!entry.isDirectory())
|
|
264
305
|
continue;
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
306
|
+
if (SKIP_DIRS.has(entry.name))
|
|
307
|
+
continue;
|
|
308
|
+
const subPath = currentDir ? `${currentDir}/${entry.name}` : entry.name;
|
|
309
|
+
const subResults = await scanForPackageJsons(rootDir, subPath, depth + 1);
|
|
310
|
+
results.push(...subResults);
|
|
311
|
+
}
|
|
312
|
+
} catch {}
|
|
313
|
+
return results;
|
|
314
|
+
}
|
|
315
|
+
async function parsePackageJson(rootDir, relativePath) {
|
|
316
|
+
try {
|
|
317
|
+
const packageJsonPath = path2.join(rootDir, relativePath, "package.json");
|
|
318
|
+
const content = await fs2.readFile(packageJsonPath, "utf-8");
|
|
319
|
+
const pkg = JSON.parse(content);
|
|
320
|
+
const name = pkg.name || path2.basename(relativePath);
|
|
321
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
322
|
+
let type = "unknown";
|
|
323
|
+
if (deps["next"] || deps["react"] || deps["vue"] || deps["svelte"]) {
|
|
324
|
+
type = "app";
|
|
325
|
+
} else if (deps["express"] || deps["fastify"] || deps["koa"] || deps["hono"]) {
|
|
326
|
+
type = "service";
|
|
327
|
+
} else if (pkg.main || pkg.exports) {
|
|
328
|
+
type = "library";
|
|
275
329
|
}
|
|
330
|
+
const hasWorkspaces = Boolean(pkg.workspaces);
|
|
331
|
+
return { name, relativePath, type, hasWorkspaces };
|
|
332
|
+
} catch {
|
|
333
|
+
return null;
|
|
276
334
|
}
|
|
277
|
-
return symbols.sort((a, b) => a.line - b.line);
|
|
278
335
|
}
|
|
279
|
-
function
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
336
|
+
async function detectProjectStructure(rootDir) {
|
|
337
|
+
const projectMap = new Map;
|
|
338
|
+
let isMonorepo = false;
|
|
339
|
+
try {
|
|
340
|
+
const entries = await fs2.readdir(rootDir, { withFileTypes: true });
|
|
341
|
+
const dirNames = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
342
|
+
const monorepoPatterns = ["apps", "packages", "libs", "services"];
|
|
343
|
+
const hasMonorepoStructure = monorepoPatterns.some((p) => dirNames.includes(p));
|
|
344
|
+
if (hasMonorepoStructure) {
|
|
345
|
+
isMonorepo = true;
|
|
346
|
+
for (const pattern of monorepoPatterns) {
|
|
347
|
+
if (!dirNames.includes(pattern))
|
|
348
|
+
continue;
|
|
349
|
+
const patternDir = path2.join(rootDir, pattern);
|
|
350
|
+
try {
|
|
351
|
+
const subDirs = await fs2.readdir(patternDir, { withFileTypes: true });
|
|
352
|
+
for (const subDir of subDirs) {
|
|
353
|
+
if (!subDir.isDirectory())
|
|
354
|
+
continue;
|
|
355
|
+
const projectRoot = `${pattern}/${subDir.name}`;
|
|
356
|
+
const type = getProjectType(pattern);
|
|
357
|
+
projectMap.set(projectRoot, {
|
|
358
|
+
name: subDir.name,
|
|
359
|
+
root: projectRoot,
|
|
360
|
+
type
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
} catch {}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const packageJsons = await scanForPackageJsons(rootDir);
|
|
367
|
+
for (const pkg of packageJsons) {
|
|
368
|
+
if (pkg.hasWorkspaces) {
|
|
369
|
+
isMonorepo = true;
|
|
370
|
+
}
|
|
371
|
+
if (packageJsons.length > 1) {
|
|
372
|
+
isMonorepo = true;
|
|
373
|
+
}
|
|
374
|
+
projectMap.set(pkg.relativePath, {
|
|
375
|
+
name: pkg.name,
|
|
376
|
+
root: pkg.relativePath,
|
|
377
|
+
type: pkg.type
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
let rootType = "unknown";
|
|
381
|
+
try {
|
|
382
|
+
const rootPkgPath = path2.join(rootDir, "package.json");
|
|
383
|
+
const rootPkg = JSON.parse(await fs2.readFile(rootPkgPath, "utf-8"));
|
|
384
|
+
if (rootPkg.workspaces) {
|
|
385
|
+
isMonorepo = true;
|
|
386
|
+
}
|
|
387
|
+
const deps = { ...rootPkg.dependencies, ...rootPkg.devDependencies };
|
|
388
|
+
if (deps["next"] || deps["react"] || deps["vue"]) {
|
|
389
|
+
rootType = "app";
|
|
390
|
+
} else if (deps["express"] || deps["fastify"] || deps["koa"]) {
|
|
391
|
+
rootType = "service";
|
|
287
392
|
}
|
|
393
|
+
} catch {}
|
|
394
|
+
const projects = Array.from(projectMap.values()).sort((a, b) => a.root.length - b.root.length);
|
|
395
|
+
return {
|
|
396
|
+
projects,
|
|
397
|
+
isMonorepo,
|
|
398
|
+
rootType: isMonorepo ? undefined : rootType
|
|
399
|
+
};
|
|
400
|
+
} catch {
|
|
401
|
+
return {
|
|
402
|
+
projects: [],
|
|
403
|
+
isMonorepo: false,
|
|
404
|
+
rootType: "unknown"
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function getProjectType(patternDir) {
|
|
409
|
+
switch (patternDir) {
|
|
410
|
+
case "apps":
|
|
411
|
+
return "app";
|
|
412
|
+
case "packages":
|
|
413
|
+
case "libs":
|
|
414
|
+
return "library";
|
|
415
|
+
case "services":
|
|
416
|
+
return "service";
|
|
417
|
+
case "scripts":
|
|
418
|
+
case "tools":
|
|
419
|
+
return "script";
|
|
420
|
+
default:
|
|
421
|
+
return "unknown";
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
function findProjectForFile(filepath, structure) {
|
|
425
|
+
const normalizedPath = filepath.replace(/\\/g, "/");
|
|
426
|
+
const matches = [];
|
|
427
|
+
for (const project of structure.projects) {
|
|
428
|
+
if (normalizedPath === project.root || normalizedPath.startsWith(project.root + "/")) {
|
|
429
|
+
matches.push(project);
|
|
288
430
|
}
|
|
289
431
|
}
|
|
290
|
-
|
|
432
|
+
if (matches.length > 0) {
|
|
433
|
+
return matches.reduce((best, current) => current.root.length > best.root.length ? current : best);
|
|
434
|
+
}
|
|
435
|
+
for (const { pattern, type } of PROJECT_PATTERNS) {
|
|
436
|
+
const match = normalizedPath.match(pattern);
|
|
437
|
+
if (match) {
|
|
438
|
+
return {
|
|
439
|
+
name: match[1],
|
|
440
|
+
root: match[0],
|
|
441
|
+
type
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return {
|
|
446
|
+
name: "root",
|
|
447
|
+
root: "",
|
|
448
|
+
type: structure.rootType ?? "unknown"
|
|
449
|
+
};
|
|
291
450
|
}
|
|
292
|
-
var
|
|
293
|
-
var
|
|
294
|
-
|
|
451
|
+
var MAX_SCAN_DEPTH = 4, SKIP_DIRS, PROJECT_PATTERNS, SCOPE_KEYWORDS;
|
|
452
|
+
var init_projectDetector = __esm(() => {
|
|
453
|
+
SKIP_DIRS = new Set([
|
|
454
|
+
"node_modules",
|
|
455
|
+
".git",
|
|
456
|
+
"dist",
|
|
457
|
+
"build",
|
|
458
|
+
".next",
|
|
459
|
+
".nuxt",
|
|
460
|
+
"coverage",
|
|
461
|
+
".raggrep"
|
|
462
|
+
]);
|
|
463
|
+
PROJECT_PATTERNS = [
|
|
464
|
+
{ pattern: /^apps\/([^/]+)/, type: "app", defaultScope: "unknown" },
|
|
465
|
+
{ pattern: /^packages\/([^/]+)/, type: "library", defaultScope: "shared" },
|
|
466
|
+
{ pattern: /^libs\/([^/]+)/, type: "library", defaultScope: "shared" },
|
|
467
|
+
{ pattern: /^services\/([^/]+)/, type: "service", defaultScope: "backend" },
|
|
468
|
+
{ pattern: /^scripts\/([^/]+)/, type: "script", defaultScope: "tooling" },
|
|
469
|
+
{ pattern: /^tools\/([^/]+)/, type: "script", defaultScope: "tooling" }
|
|
470
|
+
];
|
|
471
|
+
SCOPE_KEYWORDS = {
|
|
472
|
+
frontend: [
|
|
473
|
+
"web",
|
|
474
|
+
"webapp",
|
|
475
|
+
"frontend",
|
|
476
|
+
"client",
|
|
477
|
+
"ui",
|
|
478
|
+
"app",
|
|
479
|
+
"mobile",
|
|
480
|
+
"react",
|
|
481
|
+
"vue",
|
|
482
|
+
"angular",
|
|
483
|
+
"next",
|
|
484
|
+
"nuxt"
|
|
485
|
+
],
|
|
486
|
+
backend: [
|
|
487
|
+
"api",
|
|
488
|
+
"server",
|
|
489
|
+
"backend",
|
|
490
|
+
"service",
|
|
491
|
+
"worker",
|
|
492
|
+
"lambda",
|
|
493
|
+
"functions"
|
|
494
|
+
],
|
|
495
|
+
shared: ["shared", "common", "utils", "lib", "core", "types", "models"],
|
|
496
|
+
tooling: [
|
|
497
|
+
"scripts",
|
|
498
|
+
"tools",
|
|
499
|
+
"cli",
|
|
500
|
+
"devtools",
|
|
501
|
+
"build",
|
|
502
|
+
"config",
|
|
503
|
+
"infra"
|
|
504
|
+
],
|
|
505
|
+
unknown: []
|
|
506
|
+
};
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
// src/introspection/conventions/entryPoints.ts
|
|
510
|
+
import * as path3 from "path";
|
|
511
|
+
function getParentFolder(filepath) {
|
|
512
|
+
const dir = path3.dirname(filepath);
|
|
513
|
+
return path3.basename(dir);
|
|
514
|
+
}
|
|
515
|
+
var entryPointConventions;
|
|
516
|
+
var init_entryPoints = __esm(() => {
|
|
517
|
+
entryPointConventions = [
|
|
295
518
|
{
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
519
|
+
id: "index-file",
|
|
520
|
+
name: "Index/Barrel File",
|
|
521
|
+
description: "Module entry point that typically re-exports from other files",
|
|
522
|
+
category: "entry-point",
|
|
523
|
+
match: (filepath, filename) => {
|
|
524
|
+
return /^index\.(ts|tsx|js|jsx|mjs|cjs)$/.test(filename);
|
|
525
|
+
},
|
|
526
|
+
keywords: ["entry", "barrel", "exports", "module"],
|
|
527
|
+
dynamicKeywords: (filepath) => {
|
|
528
|
+
const parent = getParentFolder(filepath);
|
|
529
|
+
if (["src", "lib", "dist", "build", ".", ""].includes(parent)) {
|
|
530
|
+
return [];
|
|
531
|
+
}
|
|
532
|
+
return [parent.toLowerCase()];
|
|
533
|
+
}
|
|
299
534
|
},
|
|
300
535
|
{
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
536
|
+
id: "main-file",
|
|
537
|
+
name: "Main Entry Point",
|
|
538
|
+
description: "Application main entry point",
|
|
539
|
+
category: "entry-point",
|
|
540
|
+
match: (filepath, filename) => {
|
|
541
|
+
return /^main\.(ts|tsx|js|jsx|mjs|cjs)$/.test(filename);
|
|
542
|
+
},
|
|
543
|
+
keywords: ["entry", "main", "entrypoint", "bootstrap", "startup"]
|
|
304
544
|
},
|
|
305
545
|
{
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
546
|
+
id: "app-component",
|
|
547
|
+
name: "Root App Component",
|
|
548
|
+
description: "Root application component (React, Vue, etc.)",
|
|
549
|
+
category: "entry-point",
|
|
550
|
+
match: (filepath, filename) => {
|
|
551
|
+
return /^App\.(tsx|jsx|vue|svelte)$/.test(filename);
|
|
552
|
+
},
|
|
553
|
+
keywords: ["root", "app", "application", "component", "main"]
|
|
309
554
|
},
|
|
310
555
|
{
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
556
|
+
id: "deno-mod",
|
|
557
|
+
name: "Deno Module Entry",
|
|
558
|
+
description: "Deno module entry point",
|
|
559
|
+
category: "entry-point",
|
|
560
|
+
match: (filepath, filename) => {
|
|
561
|
+
return filename === "mod.ts";
|
|
562
|
+
},
|
|
563
|
+
keywords: ["entry", "module", "deno", "exports"],
|
|
564
|
+
dynamicKeywords: (filepath) => {
|
|
565
|
+
const parent = getParentFolder(filepath);
|
|
566
|
+
if (["src", "lib", ".", ""].includes(parent)) {
|
|
567
|
+
return [];
|
|
568
|
+
}
|
|
569
|
+
return [parent.toLowerCase()];
|
|
570
|
+
}
|
|
314
571
|
},
|
|
315
572
|
{
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
573
|
+
id: "python-init",
|
|
574
|
+
name: "Python Package Init",
|
|
575
|
+
description: "Python package initialization file",
|
|
576
|
+
category: "entry-point",
|
|
577
|
+
match: (filepath, filename) => {
|
|
578
|
+
return filename === "__init__.py";
|
|
579
|
+
},
|
|
580
|
+
keywords: ["entry", "package", "init", "python", "module"],
|
|
581
|
+
dynamicKeywords: (filepath) => {
|
|
582
|
+
const parent = getParentFolder(filepath);
|
|
583
|
+
if (["src", "lib", ".", ""].includes(parent)) {
|
|
584
|
+
return [];
|
|
585
|
+
}
|
|
586
|
+
return [parent.toLowerCase()];
|
|
587
|
+
}
|
|
319
588
|
},
|
|
320
589
|
{
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
590
|
+
id: "rust-lib",
|
|
591
|
+
name: "Rust Library Entry",
|
|
592
|
+
description: "Rust library crate entry point",
|
|
593
|
+
category: "entry-point",
|
|
594
|
+
match: (filepath, filename) => {
|
|
595
|
+
return filename === "lib.rs" || filename === "main.rs";
|
|
596
|
+
},
|
|
597
|
+
keywords: ["entry", "crate", "rust", "module"]
|
|
598
|
+
}
|
|
599
|
+
];
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
// src/introspection/conventions/configFiles.ts
|
|
603
|
+
var configFileConventions;
|
|
604
|
+
var init_configFiles = __esm(() => {
|
|
605
|
+
configFileConventions = [
|
|
606
|
+
{
|
|
607
|
+
id: "package-json",
|
|
608
|
+
name: "Package.json",
|
|
609
|
+
description: "Node.js package manifest",
|
|
610
|
+
category: "configuration",
|
|
611
|
+
match: (filepath, filename) => filename === "package.json",
|
|
612
|
+
keywords: ["package", "dependencies", "npm", "scripts", "manifest", "node"]
|
|
324
613
|
},
|
|
325
614
|
{
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
615
|
+
id: "pnpm-workspace",
|
|
616
|
+
name: "PNPM Workspace",
|
|
617
|
+
description: "PNPM monorepo workspace configuration",
|
|
618
|
+
category: "configuration",
|
|
619
|
+
match: (filepath, filename) => filename === "pnpm-workspace.yaml" || filename === "pnpm-workspace.yml",
|
|
620
|
+
keywords: ["workspace", "monorepo", "pnpm", "packages"]
|
|
329
621
|
},
|
|
330
622
|
{
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
623
|
+
id: "yarn-lock",
|
|
624
|
+
name: "Yarn Lock",
|
|
625
|
+
description: "Yarn dependency lock file",
|
|
626
|
+
category: "configuration",
|
|
627
|
+
match: (filepath, filename) => filename === "yarn.lock",
|
|
628
|
+
keywords: ["dependencies", "lock", "yarn", "versions"]
|
|
334
629
|
},
|
|
335
630
|
{
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
631
|
+
id: "package-lock",
|
|
632
|
+
name: "Package Lock",
|
|
633
|
+
description: "NPM dependency lock file",
|
|
634
|
+
category: "configuration",
|
|
635
|
+
match: (filepath, filename) => filename === "package-lock.json",
|
|
636
|
+
keywords: ["dependencies", "lock", "npm", "versions"]
|
|
339
637
|
},
|
|
340
638
|
{
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
639
|
+
id: "bun-lockb",
|
|
640
|
+
name: "Bun Lock",
|
|
641
|
+
description: "Bun dependency lock file",
|
|
642
|
+
category: "configuration",
|
|
643
|
+
match: (filepath, filename) => filename === "bun.lockb" || filename === "bun.lock",
|
|
644
|
+
keywords: ["dependencies", "lock", "bun", "versions"]
|
|
344
645
|
},
|
|
345
646
|
{
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
647
|
+
id: "tsconfig",
|
|
648
|
+
name: "TypeScript Config",
|
|
649
|
+
description: "TypeScript compiler configuration",
|
|
650
|
+
category: "configuration",
|
|
651
|
+
match: (filepath, filename) => filename === "tsconfig.json" || filename.startsWith("tsconfig.") && filename.endsWith(".json"),
|
|
652
|
+
keywords: [
|
|
653
|
+
"typescript",
|
|
654
|
+
"config",
|
|
655
|
+
"compiler",
|
|
656
|
+
"ts",
|
|
657
|
+
"settings",
|
|
658
|
+
"paths",
|
|
659
|
+
"types"
|
|
660
|
+
]
|
|
349
661
|
},
|
|
350
662
|
{
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
663
|
+
id: "jsconfig",
|
|
664
|
+
name: "JavaScript Config",
|
|
665
|
+
description: "JavaScript project configuration",
|
|
666
|
+
category: "configuration",
|
|
667
|
+
match: (filepath, filename) => filename === "jsconfig.json",
|
|
668
|
+
keywords: ["javascript", "config", "compiler", "js", "settings", "paths"]
|
|
354
669
|
},
|
|
355
670
|
{
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
671
|
+
id: "eslint-config",
|
|
672
|
+
name: "ESLint Config",
|
|
673
|
+
description: "ESLint linting configuration",
|
|
674
|
+
category: "configuration",
|
|
675
|
+
match: (filepath, filename) => filename === ".eslintrc" || filename === ".eslintrc.js" || filename === ".eslintrc.cjs" || filename === ".eslintrc.json" || filename === ".eslintrc.yml" || filename === ".eslintrc.yaml" || filename === "eslint.config.js" || filename === "eslint.config.mjs" || filename === "eslint.config.cjs",
|
|
676
|
+
keywords: ["eslint", "linting", "lint", "rules", "code quality"]
|
|
359
677
|
},
|
|
360
678
|
{
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
679
|
+
id: "prettier-config",
|
|
680
|
+
name: "Prettier Config",
|
|
681
|
+
description: "Prettier code formatting configuration",
|
|
682
|
+
category: "configuration",
|
|
683
|
+
match: (filepath, filename) => filename === ".prettierrc" || filename === ".prettierrc.js" || filename === ".prettierrc.cjs" || filename === ".prettierrc.json" || filename === ".prettierrc.yml" || filename === ".prettierrc.yaml" || filename === "prettier.config.js" || filename === "prettier.config.cjs" || filename === "prettier.config.mjs",
|
|
684
|
+
keywords: ["prettier", "formatting", "format", "code style", "style"]
|
|
364
685
|
},
|
|
365
686
|
{
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
687
|
+
id: "biome-config",
|
|
688
|
+
name: "Biome Config",
|
|
689
|
+
description: "Biome linting and formatting configuration",
|
|
690
|
+
category: "configuration",
|
|
691
|
+
match: (filepath, filename) => filename === "biome.json" || filename === "biome.jsonc",
|
|
692
|
+
keywords: ["biome", "linting", "formatting", "lint", "format"]
|
|
369
693
|
},
|
|
370
694
|
{
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
695
|
+
id: "vite-config",
|
|
696
|
+
name: "Vite Config",
|
|
697
|
+
description: "Vite build tool configuration",
|
|
698
|
+
category: "build",
|
|
699
|
+
match: (filepath, filename) => filename === "vite.config.ts" || filename === "vite.config.js" || filename === "vite.config.mjs",
|
|
700
|
+
keywords: ["vite", "bundler", "build", "dev server", "hmr"]
|
|
374
701
|
},
|
|
375
702
|
{
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
703
|
+
id: "webpack-config",
|
|
704
|
+
name: "Webpack Config",
|
|
705
|
+
description: "Webpack bundler configuration",
|
|
706
|
+
category: "build",
|
|
707
|
+
match: (filepath, filename) => filename === "webpack.config.js" || filename === "webpack.config.ts" || filename.startsWith("webpack.") && (filename.endsWith(".js") || filename.endsWith(".ts")),
|
|
708
|
+
keywords: ["webpack", "bundler", "build", "loaders", "plugins"]
|
|
379
709
|
},
|
|
380
710
|
{
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
711
|
+
id: "rollup-config",
|
|
712
|
+
name: "Rollup Config",
|
|
713
|
+
description: "Rollup bundler configuration",
|
|
714
|
+
category: "build",
|
|
715
|
+
match: (filepath, filename) => filename === "rollup.config.js" || filename === "rollup.config.ts" || filename === "rollup.config.mjs",
|
|
716
|
+
keywords: ["rollup", "bundler", "build", "esm", "bundle"]
|
|
384
717
|
},
|
|
385
718
|
{
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
719
|
+
id: "esbuild-config",
|
|
720
|
+
name: "esbuild Config",
|
|
721
|
+
description: "esbuild bundler configuration",
|
|
722
|
+
category: "build",
|
|
723
|
+
match: (filepath, filename) => filename === "esbuild.config.js" || filename === "esbuild.config.ts" || filename === "esbuild.config.mjs",
|
|
724
|
+
keywords: ["esbuild", "bundler", "build", "fast"]
|
|
389
725
|
},
|
|
390
726
|
{
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
727
|
+
id: "jest-config",
|
|
728
|
+
name: "Jest Config",
|
|
729
|
+
description: "Jest testing framework configuration",
|
|
730
|
+
category: "test",
|
|
731
|
+
match: (filepath, filename) => filename === "jest.config.js" || filename === "jest.config.ts" || filename === "jest.config.mjs" || filename === "jest.config.cjs" || filename === "jest.config.json",
|
|
732
|
+
keywords: ["jest", "testing", "test", "unit test", "config"]
|
|
394
733
|
},
|
|
395
734
|
{
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
735
|
+
id: "vitest-config",
|
|
736
|
+
name: "Vitest Config",
|
|
737
|
+
description: "Vitest testing framework configuration",
|
|
738
|
+
category: "test",
|
|
739
|
+
match: (filepath, filename) => filename === "vitest.config.ts" || filename === "vitest.config.js" || filename === "vitest.config.mts",
|
|
740
|
+
keywords: ["vitest", "testing", "test", "unit test", "config"]
|
|
399
741
|
},
|
|
400
742
|
{
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
743
|
+
id: "playwright-config",
|
|
744
|
+
name: "Playwright Config",
|
|
745
|
+
description: "Playwright E2E testing configuration",
|
|
746
|
+
category: "test",
|
|
747
|
+
match: (filepath, filename) => filename === "playwright.config.ts" || filename === "playwright.config.js",
|
|
748
|
+
keywords: ["playwright", "testing", "e2e", "end-to-end", "browser test"]
|
|
404
749
|
},
|
|
405
750
|
{
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
751
|
+
id: "cypress-config",
|
|
752
|
+
name: "Cypress Config",
|
|
753
|
+
description: "Cypress E2E testing configuration",
|
|
754
|
+
category: "test",
|
|
755
|
+
match: (filepath, filename) => filename === "cypress.config.ts" || filename === "cypress.config.js" || filename === "cypress.json",
|
|
756
|
+
keywords: ["cypress", "testing", "e2e", "end-to-end", "browser test"]
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
id: "tailwind-config",
|
|
760
|
+
name: "Tailwind Config",
|
|
761
|
+
description: "Tailwind CSS configuration",
|
|
762
|
+
category: "configuration",
|
|
763
|
+
match: (filepath, filename) => filename === "tailwind.config.js" || filename === "tailwind.config.ts" || filename === "tailwind.config.cjs" || filename === "tailwind.config.mjs",
|
|
764
|
+
keywords: ["tailwind", "css", "styling", "utility", "design"]
|
|
765
|
+
},
|
|
766
|
+
{
|
|
767
|
+
id: "postcss-config",
|
|
768
|
+
name: "PostCSS Config",
|
|
769
|
+
description: "PostCSS configuration",
|
|
770
|
+
category: "configuration",
|
|
771
|
+
match: (filepath, filename) => filename === "postcss.config.js" || filename === "postcss.config.cjs" || filename === "postcss.config.mjs" || filename === ".postcssrc" || filename === ".postcssrc.json",
|
|
772
|
+
keywords: ["postcss", "css", "styling", "transforms"]
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
id: "env-file",
|
|
776
|
+
name: "Environment File",
|
|
777
|
+
description: "Environment variables file",
|
|
778
|
+
category: "configuration",
|
|
779
|
+
match: (filepath, filename) => filename === ".env" || filename === ".env.local" || filename === ".env.development" || filename === ".env.production" || filename === ".env.test" || filename.startsWith(".env."),
|
|
780
|
+
keywords: ["environment", "env", "variables", "secrets", "config"]
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
id: "env-example",
|
|
784
|
+
name: "Environment Example",
|
|
785
|
+
description: "Example environment variables file",
|
|
786
|
+
category: "documentation",
|
|
787
|
+
match: (filepath, filename) => filename === ".env.example" || filename === ".env.sample" || filename === ".env.template",
|
|
788
|
+
keywords: ["environment", "env", "example", "template", "setup"]
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
id: "dockerfile",
|
|
792
|
+
name: "Dockerfile",
|
|
793
|
+
description: "Docker container image definition",
|
|
794
|
+
category: "deployment",
|
|
795
|
+
match: (filepath, filename) => filename === "Dockerfile" || filename.startsWith("Dockerfile."),
|
|
796
|
+
keywords: ["docker", "container", "image", "deployment", "build"]
|
|
797
|
+
},
|
|
798
|
+
{
|
|
799
|
+
id: "docker-compose",
|
|
800
|
+
name: "Docker Compose",
|
|
801
|
+
description: "Docker Compose multi-container configuration",
|
|
802
|
+
category: "deployment",
|
|
803
|
+
match: (filepath, filename) => filename === "docker-compose.yml" || filename === "docker-compose.yaml" || filename === "compose.yml" || filename === "compose.yaml" || filename.startsWith("docker-compose."),
|
|
804
|
+
keywords: ["docker", "compose", "containers", "services", "deployment"]
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
id: "github-actions",
|
|
808
|
+
name: "GitHub Actions Workflow",
|
|
809
|
+
description: "GitHub Actions CI/CD workflow",
|
|
810
|
+
category: "deployment",
|
|
811
|
+
match: (filepath) => filepath.includes(".github/workflows/") && filepath.endsWith(".yml"),
|
|
812
|
+
keywords: ["github", "actions", "ci", "cd", "workflow", "automation"]
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
id: "vercel-config",
|
|
816
|
+
name: "Vercel Config",
|
|
817
|
+
description: "Vercel deployment configuration",
|
|
818
|
+
category: "deployment",
|
|
819
|
+
match: (filepath, filename) => filename === "vercel.json",
|
|
820
|
+
keywords: ["vercel", "deployment", "hosting", "serverless"]
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
id: "netlify-config",
|
|
824
|
+
name: "Netlify Config",
|
|
825
|
+
description: "Netlify deployment configuration",
|
|
826
|
+
category: "deployment",
|
|
827
|
+
match: (filepath, filename) => filename === "netlify.toml",
|
|
828
|
+
keywords: ["netlify", "deployment", "hosting", "functions"]
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
id: "gitignore",
|
|
832
|
+
name: "Git Ignore",
|
|
833
|
+
description: "Git ignored files configuration",
|
|
834
|
+
category: "configuration",
|
|
835
|
+
match: (filepath, filename) => filename === ".gitignore",
|
|
836
|
+
keywords: ["git", "ignore", "version control", "excluded"]
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
id: "gitattributes",
|
|
840
|
+
name: "Git Attributes",
|
|
841
|
+
description: "Git file attributes configuration",
|
|
842
|
+
category: "configuration",
|
|
843
|
+
match: (filepath, filename) => filename === ".gitattributes",
|
|
844
|
+
keywords: ["git", "attributes", "version control", "line endings"]
|
|
845
|
+
},
|
|
846
|
+
{
|
|
847
|
+
id: "readme",
|
|
848
|
+
name: "README",
|
|
849
|
+
description: "Project documentation",
|
|
850
|
+
category: "documentation",
|
|
851
|
+
match: (filepath, filename) => filename.toLowerCase() === "readme.md" || filename.toLowerCase() === "readme",
|
|
852
|
+
keywords: [
|
|
853
|
+
"readme",
|
|
854
|
+
"documentation",
|
|
855
|
+
"docs",
|
|
856
|
+
"overview",
|
|
857
|
+
"getting started"
|
|
858
|
+
]
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
id: "changelog",
|
|
862
|
+
name: "Changelog",
|
|
863
|
+
description: "Project changelog",
|
|
864
|
+
category: "documentation",
|
|
865
|
+
match: (filepath, filename) => filename.toLowerCase() === "changelog.md" || filename.toLowerCase() === "changelog",
|
|
866
|
+
keywords: ["changelog", "changes", "releases", "history", "versions"]
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
id: "contributing",
|
|
870
|
+
name: "Contributing Guide",
|
|
871
|
+
description: "Contribution guidelines",
|
|
872
|
+
category: "documentation",
|
|
873
|
+
match: (filepath, filename) => filename.toLowerCase() === "contributing.md" || filename.toLowerCase() === "contributing",
|
|
874
|
+
keywords: ["contributing", "contribution", "guidelines", "development"]
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
id: "license",
|
|
878
|
+
name: "License",
|
|
879
|
+
description: "Project license",
|
|
880
|
+
category: "documentation",
|
|
881
|
+
match: (filepath, filename) => filename.toLowerCase() === "license" || filename.toLowerCase() === "license.md" || filename.toLowerCase() === "license.txt",
|
|
882
|
+
keywords: ["license", "legal", "copyright", "terms"]
|
|
409
883
|
}
|
|
410
884
|
];
|
|
411
885
|
});
|
|
412
886
|
|
|
413
|
-
// src/
|
|
414
|
-
var
|
|
415
|
-
|
|
416
|
-
|
|
887
|
+
// src/introspection/conventions/frameworks/nextjs.ts
|
|
888
|
+
var nextjsConventions, nextjsFramework;
|
|
889
|
+
var init_nextjs = __esm(() => {
|
|
890
|
+
nextjsConventions = [
|
|
891
|
+
{
|
|
892
|
+
id: "next-config",
|
|
893
|
+
name: "Next.js Config",
|
|
894
|
+
description: "Next.js framework configuration",
|
|
895
|
+
category: "configuration",
|
|
896
|
+
match: (filepath, filename) => filename === "next.config.js" || filename === "next.config.mjs" || filename === "next.config.ts",
|
|
897
|
+
keywords: ["nextjs", "next", "config", "framework", "settings"]
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
id: "next-env",
|
|
901
|
+
name: "Next.js Environment Types",
|
|
902
|
+
description: "Next.js TypeScript environment declarations",
|
|
903
|
+
category: "types",
|
|
904
|
+
match: (filepath, filename) => filename === "next-env.d.ts",
|
|
905
|
+
keywords: ["nextjs", "types", "typescript", "declarations"]
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
id: "next-layout",
|
|
909
|
+
name: "Next.js Layout",
|
|
910
|
+
description: "Next.js layout component (App Router)",
|
|
911
|
+
category: "framework",
|
|
912
|
+
match: (filepath, filename) => (filename === "layout.tsx" || filename === "layout.js") && (filepath.includes("/app/") || filepath.startsWith("app/")),
|
|
913
|
+
keywords: ["nextjs", "layout", "wrapper", "template", "app router"],
|
|
914
|
+
dynamicKeywords: (filepath) => {
|
|
915
|
+
const match = filepath.match(/app\/(.+?)\/layout\./);
|
|
916
|
+
if (match) {
|
|
917
|
+
const segments = match[1].split("/").filter((s) => !s.startsWith("(") && !s.startsWith("["));
|
|
918
|
+
return segments.map((s) => s.toLowerCase());
|
|
919
|
+
}
|
|
920
|
+
if (filepath === "app/layout.tsx" || filepath === "app/layout.js") {
|
|
921
|
+
return ["root", "main"];
|
|
922
|
+
}
|
|
923
|
+
return [];
|
|
924
|
+
}
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
id: "next-page",
|
|
928
|
+
name: "Next.js Page",
|
|
929
|
+
description: "Next.js page component (App Router)",
|
|
930
|
+
category: "framework",
|
|
931
|
+
match: (filepath, filename) => (filename === "page.tsx" || filename === "page.js") && (filepath.includes("/app/") || filepath.startsWith("app/")),
|
|
932
|
+
keywords: ["nextjs", "page", "route", "view", "app router"],
|
|
933
|
+
dynamicKeywords: (filepath) => {
|
|
934
|
+
const match = filepath.match(/app\/(.+?)\/page\./);
|
|
935
|
+
if (match) {
|
|
936
|
+
const segments = match[1].split("/").filter((s) => !s.startsWith("(")).map((s) => s.replace(/^\[(.+?)\]$/, "$1"));
|
|
937
|
+
return segments.map((s) => s.toLowerCase());
|
|
938
|
+
}
|
|
939
|
+
if (filepath === "app/page.tsx" || filepath === "app/page.js") {
|
|
940
|
+
return ["home", "index", "root"];
|
|
941
|
+
}
|
|
942
|
+
return [];
|
|
943
|
+
}
|
|
944
|
+
},
|
|
945
|
+
{
|
|
946
|
+
id: "next-loading",
|
|
947
|
+
name: "Next.js Loading",
|
|
948
|
+
description: "Next.js loading UI component",
|
|
949
|
+
category: "framework",
|
|
950
|
+
match: (filepath, filename) => (filename === "loading.tsx" || filename === "loading.js") && (filepath.includes("/app/") || filepath.startsWith("app/")),
|
|
951
|
+
keywords: ["nextjs", "loading", "suspense", "skeleton", "spinner"]
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
id: "next-error",
|
|
955
|
+
name: "Next.js Error",
|
|
956
|
+
description: "Next.js error boundary component",
|
|
957
|
+
category: "framework",
|
|
958
|
+
match: (filepath, filename) => (filename === "error.tsx" || filename === "error.js") && (filepath.includes("/app/") || filepath.startsWith("app/")),
|
|
959
|
+
keywords: ["nextjs", "error", "boundary", "fallback", "catch"]
|
|
960
|
+
},
|
|
961
|
+
{
|
|
962
|
+
id: "next-not-found",
|
|
963
|
+
name: "Next.js Not Found",
|
|
964
|
+
description: "Next.js 404 page component",
|
|
965
|
+
category: "framework",
|
|
966
|
+
match: (filepath, filename) => (filename === "not-found.tsx" || filename === "not-found.js") && (filepath.includes("/app/") || filepath.startsWith("app/")),
|
|
967
|
+
keywords: ["nextjs", "404", "not found", "missing", "error"]
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
id: "next-template",
|
|
971
|
+
name: "Next.js Template",
|
|
972
|
+
description: "Next.js template component",
|
|
973
|
+
category: "framework",
|
|
974
|
+
match: (filepath, filename) => (filename === "template.tsx" || filename === "template.js") && (filepath.includes("/app/") || filepath.startsWith("app/")),
|
|
975
|
+
keywords: ["nextjs", "template", "wrapper", "app router"]
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
id: "next-route-handler",
|
|
979
|
+
name: "Next.js Route Handler",
|
|
980
|
+
description: "Next.js API route handler (App Router)",
|
|
981
|
+
category: "framework",
|
|
982
|
+
match: (filepath, filename) => (filename === "route.ts" || filename === "route.js") && (filepath.includes("/app/") || filepath.startsWith("app/")),
|
|
983
|
+
keywords: ["nextjs", "api", "route", "handler", "endpoint", "rest"],
|
|
984
|
+
dynamicKeywords: (filepath) => {
|
|
985
|
+
const match = filepath.match(/app\/api\/(.+?)\/route\./);
|
|
986
|
+
if (match) {
|
|
987
|
+
const segments = match[1].split("/").filter((s) => !s.startsWith("(")).map((s) => s.replace(/^\[(.+?)\]$/, "$1"));
|
|
988
|
+
return ["api", ...segments.map((s) => s.toLowerCase())];
|
|
989
|
+
}
|
|
990
|
+
return ["api"];
|
|
991
|
+
}
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
id: "next-middleware",
|
|
995
|
+
name: "Next.js Middleware",
|
|
996
|
+
description: "Next.js edge middleware",
|
|
997
|
+
category: "framework",
|
|
998
|
+
match: (filepath, filename) => filename === "middleware.ts" || filename === "middleware.js",
|
|
999
|
+
keywords: ["nextjs", "middleware", "edge", "request", "interceptor"]
|
|
1000
|
+
},
|
|
1001
|
+
{
|
|
1002
|
+
id: "next-global-error",
|
|
1003
|
+
name: "Next.js Global Error",
|
|
1004
|
+
description: "Next.js global error handler",
|
|
1005
|
+
category: "framework",
|
|
1006
|
+
match: (filepath, filename) => filename === "global-error.tsx" || filename === "global-error.js",
|
|
1007
|
+
keywords: ["nextjs", "error", "global", "boundary", "catch"]
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
id: "next-pages-api",
|
|
1011
|
+
name: "Next.js API Route (Pages)",
|
|
1012
|
+
description: "Next.js API route (Pages Router)",
|
|
1013
|
+
category: "framework",
|
|
1014
|
+
match: (filepath) => filepath.includes("/pages/api/") || filepath.startsWith("pages/api/"),
|
|
1015
|
+
keywords: ["nextjs", "api", "route", "handler", "endpoint", "pages router"],
|
|
1016
|
+
dynamicKeywords: (filepath) => {
|
|
1017
|
+
const match = filepath.match(/pages\/api\/(.+?)\.(ts|js)/);
|
|
1018
|
+
if (match) {
|
|
1019
|
+
const segments = match[1].split("/").map((s) => s.replace(/^\[(.+?)\]$/, "$1"));
|
|
1020
|
+
return ["api", ...segments.map((s) => s.toLowerCase())];
|
|
1021
|
+
}
|
|
1022
|
+
return ["api"];
|
|
1023
|
+
}
|
|
1024
|
+
},
|
|
1025
|
+
{
|
|
1026
|
+
id: "next-pages-document",
|
|
1027
|
+
name: "Next.js Document",
|
|
1028
|
+
description: "Next.js custom document (Pages Router)",
|
|
1029
|
+
category: "framework",
|
|
1030
|
+
match: (filepath, filename) => (filename === "_document.tsx" || filename === "_document.js") && (filepath.includes("/pages/") || filepath.startsWith("pages/")),
|
|
1031
|
+
keywords: ["nextjs", "document", "html", "head", "body", "pages router"]
|
|
1032
|
+
},
|
|
1033
|
+
{
|
|
1034
|
+
id: "next-pages-app",
|
|
1035
|
+
name: "Next.js App (Pages)",
|
|
1036
|
+
description: "Next.js custom app (Pages Router)",
|
|
1037
|
+
category: "framework",
|
|
1038
|
+
match: (filepath, filename) => (filename === "_app.tsx" || filename === "_app.js") && (filepath.includes("/pages/") || filepath.startsWith("pages/")),
|
|
1039
|
+
keywords: ["nextjs", "app", "wrapper", "provider", "pages router"]
|
|
1040
|
+
}
|
|
1041
|
+
];
|
|
1042
|
+
nextjsFramework = {
|
|
1043
|
+
id: "nextjs",
|
|
1044
|
+
name: "Next.js",
|
|
1045
|
+
detect: (filepath) => {
|
|
1046
|
+
return filepath === "next.config.js" || filepath === "next.config.mjs" || filepath === "next.config.ts" || filepath.includes("/app/page.") || filepath.includes("/pages/_app.");
|
|
1047
|
+
},
|
|
1048
|
+
conventions: nextjsConventions
|
|
1049
|
+
};
|
|
417
1050
|
});
|
|
418
|
-
import * as path2 from "path";
|
|
419
|
-
import * as fs2 from "fs/promises";
|
|
420
1051
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
filepath
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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;
|
|
1052
|
+
// src/introspection/conventions/frameworks/convex.ts
|
|
1053
|
+
var convexConventions, convexFramework;
|
|
1054
|
+
var init_convex = __esm(() => {
|
|
1055
|
+
convexConventions = [
|
|
1056
|
+
{
|
|
1057
|
+
id: "convex-config",
|
|
1058
|
+
name: "Convex Config",
|
|
1059
|
+
description: "Convex project configuration",
|
|
1060
|
+
category: "configuration",
|
|
1061
|
+
match: (filepath, filename) => filename === "convex.json",
|
|
1062
|
+
keywords: ["convex", "config", "backend", "settings"]
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
id: "convex-schema",
|
|
1066
|
+
name: "Convex Schema",
|
|
1067
|
+
description: "Convex database schema definition",
|
|
1068
|
+
category: "framework",
|
|
1069
|
+
match: (filepath, filename) => filename === "schema.ts" && (filepath.includes("/convex/") || filepath.startsWith("convex/")),
|
|
1070
|
+
keywords: ["convex", "schema", "database", "tables", "types", "model"]
|
|
1071
|
+
},
|
|
1072
|
+
{
|
|
1073
|
+
id: "convex-function",
|
|
1074
|
+
name: "Convex Function File",
|
|
1075
|
+
description: "Convex backend function file",
|
|
1076
|
+
category: "framework",
|
|
1077
|
+
match: (filepath, filename, extension) => (extension === ".ts" || extension === ".js") && (filepath.includes("/convex/") || filepath.startsWith("convex/")) && !filepath.includes("/_generated/") && filename !== "schema.ts" && !filename.startsWith("_"),
|
|
1078
|
+
keywords: ["convex", "function", "backend", "query", "mutation", "action"],
|
|
1079
|
+
dynamicKeywords: (filepath) => {
|
|
1080
|
+
const match = filepath.match(/convex\/(.+?)\.(ts|js)/);
|
|
1081
|
+
if (match) {
|
|
1082
|
+
const name = match[1].replace(/\//g, " ").split(" ").pop() || "";
|
|
1083
|
+
if (name && !["schema", "http", "crons"].includes(name)) {
|
|
1084
|
+
return [name.toLowerCase()];
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
return [];
|
|
472
1088
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
1089
|
+
},
|
|
1090
|
+
{
|
|
1091
|
+
id: "convex-http",
|
|
1092
|
+
name: "Convex HTTP Routes",
|
|
1093
|
+
description: "Convex HTTP endpoint definitions",
|
|
1094
|
+
category: "framework",
|
|
1095
|
+
match: (filepath, filename) => filename === "http.ts" && (filepath.includes("/convex/") || filepath.startsWith("convex/")),
|
|
1096
|
+
keywords: ["convex", "http", "routes", "api", "endpoints", "rest"]
|
|
1097
|
+
},
|
|
1098
|
+
{
|
|
1099
|
+
id: "convex-crons",
|
|
1100
|
+
name: "Convex Cron Jobs",
|
|
1101
|
+
description: "Convex scheduled function definitions",
|
|
1102
|
+
category: "framework",
|
|
1103
|
+
match: (filepath, filename) => filename === "crons.ts" && (filepath.includes("/convex/") || filepath.startsWith("convex/")),
|
|
1104
|
+
keywords: [
|
|
1105
|
+
"convex",
|
|
1106
|
+
"crons",
|
|
1107
|
+
"scheduled",
|
|
1108
|
+
"jobs",
|
|
1109
|
+
"background",
|
|
1110
|
+
"recurring"
|
|
1111
|
+
]
|
|
1112
|
+
},
|
|
1113
|
+
{
|
|
1114
|
+
id: "convex-generated",
|
|
1115
|
+
name: "Convex Generated",
|
|
1116
|
+
description: "Convex auto-generated files",
|
|
1117
|
+
category: "framework",
|
|
1118
|
+
match: (filepath) => filepath.includes("/convex/_generated/") || filepath.startsWith("convex/_generated/"),
|
|
1119
|
+
keywords: ["convex", "generated", "types", "api"]
|
|
1120
|
+
},
|
|
1121
|
+
{
|
|
1122
|
+
id: "convex-auth",
|
|
1123
|
+
name: "Convex Auth",
|
|
1124
|
+
description: "Convex authentication configuration",
|
|
1125
|
+
category: "framework",
|
|
1126
|
+
match: (filepath, filename) => filename === "auth.ts" && (filepath.includes("/convex/") || filepath.startsWith("convex/")),
|
|
1127
|
+
keywords: ["convex", "auth", "authentication", "login", "users"]
|
|
1128
|
+
},
|
|
1129
|
+
{
|
|
1130
|
+
id: "convex-auth-config",
|
|
1131
|
+
name: "Convex Auth Config",
|
|
1132
|
+
description: "Convex auth configuration file",
|
|
1133
|
+
category: "configuration",
|
|
1134
|
+
match: (filepath, filename) => filename === "auth.config.ts",
|
|
1135
|
+
keywords: ["convex", "auth", "config", "providers", "oauth"]
|
|
505
1136
|
}
|
|
1137
|
+
];
|
|
1138
|
+
convexFramework = {
|
|
1139
|
+
id: "convex",
|
|
1140
|
+
name: "Convex",
|
|
1141
|
+
detect: (filepath) => {
|
|
1142
|
+
return filepath === "convex.json" || filepath.startsWith("convex/") || filepath.includes("/convex/");
|
|
1143
|
+
},
|
|
1144
|
+
conventions: convexConventions
|
|
1145
|
+
};
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
// src/introspection/conventions/frameworks/index.ts
|
|
1149
|
+
function getAllFrameworkConventions() {
|
|
1150
|
+
return frameworkProviders.flatMap((f) => f.conventions);
|
|
1151
|
+
}
|
|
1152
|
+
var frameworkProviders;
|
|
1153
|
+
var init_frameworks = __esm(() => {
|
|
1154
|
+
init_nextjs();
|
|
1155
|
+
init_convex();
|
|
1156
|
+
init_nextjs();
|
|
1157
|
+
init_convex();
|
|
1158
|
+
frameworkProviders = [
|
|
1159
|
+
nextjsFramework,
|
|
1160
|
+
convexFramework
|
|
1161
|
+
];
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
// src/introspection/conventions/index.ts
|
|
1165
|
+
import * as path4 from "path";
|
|
1166
|
+
function getAllConventions() {
|
|
1167
|
+
return [
|
|
1168
|
+
...entryPointConventions,
|
|
1169
|
+
...configFileConventions,
|
|
1170
|
+
...getAllFrameworkConventions()
|
|
1171
|
+
];
|
|
1172
|
+
}
|
|
1173
|
+
function getConventions() {
|
|
1174
|
+
return [
|
|
1175
|
+
...getAllConventions(),
|
|
1176
|
+
...typeDefinitionConventions,
|
|
1177
|
+
...testFileConventions
|
|
1178
|
+
];
|
|
1179
|
+
}
|
|
1180
|
+
function getConventionKeywords(filepath) {
|
|
1181
|
+
const conventions = getConventions();
|
|
1182
|
+
const filename = path4.basename(filepath);
|
|
1183
|
+
const extension = path4.extname(filepath);
|
|
1184
|
+
const keywords = new Set;
|
|
1185
|
+
for (const convention of conventions) {
|
|
1186
|
+
try {
|
|
1187
|
+
if (convention.match(filepath, filename, extension)) {
|
|
1188
|
+
for (const keyword of convention.keywords) {
|
|
1189
|
+
keywords.add(keyword.toLowerCase());
|
|
1190
|
+
}
|
|
1191
|
+
if (convention.dynamicKeywords) {
|
|
1192
|
+
const dynamicKws = convention.dynamicKeywords(filepath);
|
|
1193
|
+
for (const kw of dynamicKws) {
|
|
1194
|
+
if (kw && kw.length > 1) {
|
|
1195
|
+
keywords.add(kw.toLowerCase());
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
} catch {}
|
|
506
1201
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
1202
|
+
return Array.from(keywords);
|
|
1203
|
+
}
|
|
1204
|
+
var typeDefinitionConventions, testFileConventions;
|
|
1205
|
+
var init_conventions = __esm(() => {
|
|
1206
|
+
init_entryPoints();
|
|
1207
|
+
init_configFiles();
|
|
1208
|
+
init_frameworks();
|
|
1209
|
+
init_entryPoints();
|
|
1210
|
+
init_configFiles();
|
|
1211
|
+
init_frameworks();
|
|
1212
|
+
typeDefinitionConventions = [
|
|
1213
|
+
{
|
|
1214
|
+
id: "dts-file",
|
|
1215
|
+
name: "TypeScript Declaration",
|
|
1216
|
+
description: "TypeScript type declaration file",
|
|
1217
|
+
category: "types",
|
|
1218
|
+
match: (filepath, filename) => filename.endsWith(".d.ts"),
|
|
1219
|
+
keywords: ["types", "declarations", "typescript", "definitions"]
|
|
1220
|
+
},
|
|
1221
|
+
{
|
|
1222
|
+
id: "types-file",
|
|
1223
|
+
name: "Types File",
|
|
1224
|
+
description: "TypeScript types file",
|
|
1225
|
+
category: "types",
|
|
1226
|
+
match: (filepath, filename) => filename.endsWith(".types.ts") || filename === "types.ts",
|
|
1227
|
+
keywords: ["types", "definitions", "typescript", "interfaces"],
|
|
1228
|
+
dynamicKeywords: (filepath) => {
|
|
1229
|
+
const match = filepath.match(/([^/]+)\.types\.ts$/);
|
|
1230
|
+
if (match) {
|
|
1231
|
+
return [match[1].toLowerCase()];
|
|
1232
|
+
}
|
|
1233
|
+
return [];
|
|
1234
|
+
}
|
|
1235
|
+
},
|
|
1236
|
+
{
|
|
1237
|
+
id: "types-folder",
|
|
1238
|
+
name: "Types Folder File",
|
|
1239
|
+
description: "File in a types folder",
|
|
1240
|
+
category: "types",
|
|
1241
|
+
match: (filepath) => filepath.includes("/types/") || filepath.startsWith("types/"),
|
|
1242
|
+
keywords: ["types", "definitions"]
|
|
514
1243
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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
|
-
});
|
|
1244
|
+
];
|
|
1245
|
+
testFileConventions = [
|
|
1246
|
+
{
|
|
1247
|
+
id: "test-file",
|
|
1248
|
+
name: "Test File",
|
|
1249
|
+
description: "Unit/integration test file",
|
|
1250
|
+
category: "test",
|
|
1251
|
+
match: (filepath, filename) => filename.includes(".test.") || filename.includes(".spec.") || filename.includes("_test."),
|
|
1252
|
+
keywords: ["test", "spec", "unit test"],
|
|
1253
|
+
dynamicKeywords: (filepath) => {
|
|
1254
|
+
const match = filepath.match(/([^/]+)\.(test|spec)\./);
|
|
1255
|
+
if (match) {
|
|
1256
|
+
return [match[1].toLowerCase()];
|
|
1257
|
+
}
|
|
1258
|
+
return [];
|
|
561
1259
|
}
|
|
1260
|
+
},
|
|
1261
|
+
{
|
|
1262
|
+
id: "test-folder",
|
|
1263
|
+
name: "Test Folder File",
|
|
1264
|
+
description: "File in a test folder",
|
|
1265
|
+
category: "test",
|
|
1266
|
+
match: (filepath) => filepath.includes("/__tests__/") || filepath.includes("/test/") || filepath.includes("/tests/") || filepath.startsWith("__tests__/") || filepath.startsWith("test/") || filepath.startsWith("tests/"),
|
|
1267
|
+
keywords: ["test", "testing"]
|
|
562
1268
|
}
|
|
563
|
-
|
|
1269
|
+
];
|
|
1270
|
+
});
|
|
1271
|
+
|
|
1272
|
+
// src/introspection/fileIntrospector.ts
|
|
1273
|
+
import * as path5 from "path";
|
|
1274
|
+
function introspectFile(filepath, structure, fileContent) {
|
|
1275
|
+
const normalizedPath = filepath.replace(/\\/g, "/");
|
|
1276
|
+
const segments = normalizedPath.split("/").filter((s) => s.length > 0);
|
|
1277
|
+
const filename = segments[segments.length - 1] || "";
|
|
1278
|
+
const ext = path5.extname(filename);
|
|
1279
|
+
const project = findProjectForFile(normalizedPath, structure);
|
|
1280
|
+
const language = EXTENSION_TO_LANGUAGE[ext] || "unknown";
|
|
1281
|
+
const layer = detectLayer(segments, filename);
|
|
1282
|
+
const domain = detectDomain(segments);
|
|
1283
|
+
const scope = detectScope(segments, project, layer);
|
|
1284
|
+
let framework;
|
|
1285
|
+
if (fileContent) {
|
|
1286
|
+
framework = detectFramework(fileContent);
|
|
564
1287
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
matches.set(filepath, Math.min(1, matchScore / queryTokens.length));
|
|
1288
|
+
return {
|
|
1289
|
+
filepath: normalizedPath,
|
|
1290
|
+
project,
|
|
1291
|
+
scope,
|
|
1292
|
+
layer,
|
|
1293
|
+
domain,
|
|
1294
|
+
language,
|
|
1295
|
+
framework,
|
|
1296
|
+
depth: segments.length - 1,
|
|
1297
|
+
pathSegments: segments.slice(0, -1)
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
function detectLayer(segments, filename) {
|
|
1301
|
+
const filenameLower = filename.toLowerCase();
|
|
1302
|
+
for (const [layer, patterns] of Object.entries(LAYER_PATTERNS)) {
|
|
1303
|
+
for (const pattern of patterns) {
|
|
1304
|
+
if (filenameLower.includes(pattern)) {
|
|
1305
|
+
return layer;
|
|
584
1306
|
}
|
|
585
1307
|
}
|
|
586
|
-
return matches;
|
|
587
1308
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
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;
|
|
1309
|
+
for (let i = segments.length - 2;i >= 0; i--) {
|
|
1310
|
+
const segment = segments[i].toLowerCase();
|
|
1311
|
+
for (const [layer, patterns] of Object.entries(LAYER_PATTERNS)) {
|
|
1312
|
+
if (patterns.includes(segment)) {
|
|
1313
|
+
return layer;
|
|
613
1314
|
}
|
|
614
1315
|
}
|
|
615
|
-
return bestChunk;
|
|
616
1316
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
function detectDomain(segments) {
|
|
1320
|
+
const skipSegments = new Set([
|
|
1321
|
+
"src",
|
|
1322
|
+
"lib",
|
|
1323
|
+
"app",
|
|
1324
|
+
"apps",
|
|
1325
|
+
"packages",
|
|
1326
|
+
"services",
|
|
1327
|
+
"modules",
|
|
1328
|
+
"features",
|
|
1329
|
+
...Object.values(LAYER_PATTERNS).flat()
|
|
1330
|
+
]);
|
|
1331
|
+
for (const segment of segments) {
|
|
1332
|
+
const segmentLower = segment.toLowerCase();
|
|
1333
|
+
if (skipSegments.has(segmentLower))
|
|
1334
|
+
continue;
|
|
1335
|
+
if (DOMAIN_PATTERNS.includes(segmentLower)) {
|
|
1336
|
+
return segmentLower;
|
|
1337
|
+
}
|
|
1338
|
+
for (const domain of DOMAIN_PATTERNS) {
|
|
1339
|
+
if (segmentLower.startsWith(domain) || segmentLower.endsWith(domain)) {
|
|
1340
|
+
return domain;
|
|
626
1341
|
}
|
|
627
|
-
} catch (error) {
|
|
628
|
-
this.symbolIndex = new Map;
|
|
629
|
-
this.bm25Index = null;
|
|
630
1342
|
}
|
|
631
1343
|
}
|
|
632
|
-
|
|
633
|
-
this.symbolIndex.clear();
|
|
634
|
-
this.bm25Index = null;
|
|
635
|
-
}
|
|
1344
|
+
return;
|
|
636
1345
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
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
|
-
};
|
|
1346
|
+
function detectScope(segments, project, layer) {
|
|
1347
|
+
const projectScope = detectScopeFromName(project.name);
|
|
1348
|
+
if (projectScope !== "unknown") {
|
|
1349
|
+
return projectScope;
|
|
658
1350
|
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
1351
|
+
if (layer) {
|
|
1352
|
+
switch (layer) {
|
|
1353
|
+
case "controller":
|
|
1354
|
+
case "repository":
|
|
1355
|
+
case "middleware":
|
|
1356
|
+
return "backend";
|
|
1357
|
+
case "presentation":
|
|
1358
|
+
return "frontend";
|
|
1359
|
+
case "util":
|
|
1360
|
+
case "model":
|
|
1361
|
+
return "shared";
|
|
1362
|
+
case "test":
|
|
1363
|
+
return "tooling";
|
|
665
1364
|
}
|
|
666
|
-
await this.ensurePipeline();
|
|
667
1365
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
1366
|
+
for (const segment of segments) {
|
|
1367
|
+
const segmentLower = segment.toLowerCase();
|
|
1368
|
+
if (["server", "api", "backend"].includes(segmentLower)) {
|
|
1369
|
+
return "backend";
|
|
671
1370
|
}
|
|
672
|
-
if (
|
|
673
|
-
return
|
|
1371
|
+
if (["client", "web", "frontend", "ui"].includes(segmentLower)) {
|
|
1372
|
+
return "frontend";
|
|
674
1373
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
1374
|
+
if (["shared", "common", "lib", "libs"].includes(segmentLower)) {
|
|
1375
|
+
return "shared";
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
return "unknown";
|
|
1379
|
+
}
|
|
1380
|
+
function detectFramework(content) {
|
|
1381
|
+
for (const [framework, indicators] of Object.entries(FRAMEWORK_INDICATORS)) {
|
|
1382
|
+
for (const indicator of indicators) {
|
|
1383
|
+
if (content.includes(`from '${indicator}`) || content.includes(`from "${indicator}`) || content.includes(`require('${indicator}`) || content.includes(`require("${indicator}`)) {
|
|
1384
|
+
return framework;
|
|
682
1385
|
}
|
|
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
1386
|
}
|
|
714
|
-
const output = await this.pipeline(text, {
|
|
715
|
-
pooling: "mean",
|
|
716
|
-
normalize: true
|
|
717
|
-
});
|
|
718
|
-
return Array.from(output.data);
|
|
719
1387
|
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
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;
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
function introspectionToKeywords(intro) {
|
|
1391
|
+
const keywords = [];
|
|
1392
|
+
const filename = path5.basename(intro.filepath);
|
|
1393
|
+
const filenameWithoutExt = filename.replace(/\.[^.]+$/, "");
|
|
1394
|
+
const filenameParts = filenameWithoutExt.split(/[-_.]/).flatMap((part) => part.split(/(?=[A-Z])/)).map((part) => part.toLowerCase()).filter((part) => part.length > 1);
|
|
1395
|
+
keywords.push(...filenameParts);
|
|
1396
|
+
keywords.push(filenameWithoutExt.toLowerCase());
|
|
1397
|
+
if (intro.project.name && intro.project.name !== "root") {
|
|
1398
|
+
keywords.push(intro.project.name.toLowerCase());
|
|
740
1399
|
}
|
|
741
|
-
|
|
742
|
-
|
|
1400
|
+
if (intro.scope !== "unknown") {
|
|
1401
|
+
keywords.push(intro.scope);
|
|
743
1402
|
}
|
|
744
|
-
|
|
745
|
-
|
|
1403
|
+
if (intro.layer) {
|
|
1404
|
+
keywords.push(intro.layer);
|
|
746
1405
|
}
|
|
747
|
-
|
|
748
|
-
|
|
1406
|
+
if (intro.domain) {
|
|
1407
|
+
keywords.push(intro.domain);
|
|
749
1408
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
const newConfig = { ...globalConfig, ...config };
|
|
753
|
-
if (newConfig.model !== globalConfig.model) {
|
|
754
|
-
globalProvider = null;
|
|
1409
|
+
if (intro.language !== "unknown") {
|
|
1410
|
+
keywords.push(intro.language);
|
|
755
1411
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
function getEmbeddingConfig() {
|
|
759
|
-
return { ...globalConfig };
|
|
760
|
-
}
|
|
761
|
-
async function ensureGlobalProvider() {
|
|
762
|
-
if (!globalProvider) {
|
|
763
|
-
globalProvider = new TransformersEmbeddingProvider(globalConfig);
|
|
764
|
-
await globalProvider.initialize();
|
|
1412
|
+
if (intro.framework) {
|
|
1413
|
+
keywords.push(intro.framework);
|
|
765
1414
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
return
|
|
1415
|
+
const skipSegments = new Set(["src", "lib", "index"]);
|
|
1416
|
+
for (const segment of intro.pathSegments) {
|
|
1417
|
+
if (!skipSegments.has(segment.toLowerCase()) && segment.length > 2) {
|
|
1418
|
+
keywords.push(segment.toLowerCase());
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
const conventionKeywords = getConventionKeywords(intro.filepath);
|
|
1422
|
+
keywords.push(...conventionKeywords);
|
|
1423
|
+
return [...new Set(keywords)];
|
|
775
1424
|
}
|
|
776
|
-
var
|
|
777
|
-
var
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
"
|
|
783
|
-
"
|
|
784
|
-
|
|
785
|
-
|
|
1425
|
+
var LAYER_PATTERNS, DOMAIN_PATTERNS, FRAMEWORK_INDICATORS, EXTENSION_TO_LANGUAGE;
|
|
1426
|
+
var init_fileIntrospector = __esm(() => {
|
|
1427
|
+
init_projectDetector();
|
|
1428
|
+
init_conventions();
|
|
1429
|
+
LAYER_PATTERNS = {
|
|
1430
|
+
controller: ["controller", "api", "routes", "route", "handler"],
|
|
1431
|
+
service: ["service", "logic", "usecase", "usecases", "handler"],
|
|
1432
|
+
repository: ["repository", "repo", "dao", "store", "persistence"],
|
|
1433
|
+
model: [
|
|
1434
|
+
"model",
|
|
1435
|
+
"models",
|
|
1436
|
+
"entity",
|
|
1437
|
+
"entities",
|
|
1438
|
+
"schema",
|
|
1439
|
+
"schemas",
|
|
1440
|
+
"types",
|
|
1441
|
+
"type"
|
|
1442
|
+
],
|
|
1443
|
+
util: ["util", "utils", "helper", "helpers", "common", "lib"],
|
|
1444
|
+
config: ["config", "configuration", "settings"],
|
|
1445
|
+
middleware: ["middleware", "middlewares"],
|
|
1446
|
+
domain: ["domain"],
|
|
1447
|
+
infrastructure: ["infrastructure", "infra"],
|
|
1448
|
+
application: ["application", "app"],
|
|
1449
|
+
presentation: [
|
|
1450
|
+
"presentation",
|
|
1451
|
+
"ui",
|
|
1452
|
+
"views",
|
|
1453
|
+
"view",
|
|
1454
|
+
"component",
|
|
1455
|
+
"components"
|
|
1456
|
+
],
|
|
1457
|
+
test: ["test", "tests", "spec", "specs", "__tests__", "e2e"]
|
|
786
1458
|
};
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
1459
|
+
DOMAIN_PATTERNS = [
|
|
1460
|
+
"auth",
|
|
1461
|
+
"authentication",
|
|
1462
|
+
"user",
|
|
1463
|
+
"users",
|
|
1464
|
+
"account",
|
|
1465
|
+
"accounts",
|
|
1466
|
+
"profile",
|
|
1467
|
+
"profiles",
|
|
1468
|
+
"product",
|
|
1469
|
+
"products",
|
|
1470
|
+
"item",
|
|
1471
|
+
"items",
|
|
1472
|
+
"catalog",
|
|
1473
|
+
"order",
|
|
1474
|
+
"orders",
|
|
1475
|
+
"cart",
|
|
1476
|
+
"checkout",
|
|
1477
|
+
"payment",
|
|
1478
|
+
"payments",
|
|
1479
|
+
"billing",
|
|
1480
|
+
"subscription",
|
|
1481
|
+
"subscriptions",
|
|
1482
|
+
"notification",
|
|
1483
|
+
"notifications",
|
|
1484
|
+
"email",
|
|
1485
|
+
"sms",
|
|
1486
|
+
"report",
|
|
1487
|
+
"reports",
|
|
1488
|
+
"analytics",
|
|
1489
|
+
"metrics",
|
|
1490
|
+
"dashboard",
|
|
1491
|
+
"admin",
|
|
1492
|
+
"settings",
|
|
1493
|
+
"search",
|
|
1494
|
+
"chat",
|
|
1495
|
+
"message",
|
|
1496
|
+
"messages",
|
|
1497
|
+
"feed",
|
|
1498
|
+
"post",
|
|
1499
|
+
"posts",
|
|
1500
|
+
"comment",
|
|
1501
|
+
"comments",
|
|
1502
|
+
"media",
|
|
1503
|
+
"upload",
|
|
1504
|
+
"file",
|
|
1505
|
+
"files",
|
|
1506
|
+
"storage",
|
|
1507
|
+
"cache",
|
|
1508
|
+
"session",
|
|
1509
|
+
"log",
|
|
1510
|
+
"logs",
|
|
1511
|
+
"audit"
|
|
1512
|
+
];
|
|
1513
|
+
FRAMEWORK_INDICATORS = {
|
|
1514
|
+
nextjs: ["next", "next/"],
|
|
1515
|
+
express: ["express"],
|
|
1516
|
+
fastify: ["fastify"],
|
|
1517
|
+
react: ["react"],
|
|
1518
|
+
vue: ["vue"],
|
|
1519
|
+
angular: ["@angular/"],
|
|
1520
|
+
nestjs: ["@nestjs/"],
|
|
1521
|
+
koa: ["koa"]
|
|
1522
|
+
};
|
|
1523
|
+
EXTENSION_TO_LANGUAGE = {
|
|
1524
|
+
".ts": "typescript",
|
|
1525
|
+
".tsx": "typescript",
|
|
1526
|
+
".js": "javascript",
|
|
1527
|
+
".jsx": "javascript",
|
|
1528
|
+
".mjs": "javascript",
|
|
1529
|
+
".cjs": "javascript",
|
|
1530
|
+
".py": "python",
|
|
1531
|
+
".go": "go",
|
|
1532
|
+
".rs": "rust",
|
|
1533
|
+
".java": "java",
|
|
1534
|
+
".kt": "kotlin",
|
|
1535
|
+
".swift": "swift",
|
|
1536
|
+
".rb": "ruby",
|
|
1537
|
+
".php": "php",
|
|
1538
|
+
".cs": "csharp",
|
|
1539
|
+
".cpp": "cpp",
|
|
1540
|
+
".c": "c",
|
|
1541
|
+
".h": "c",
|
|
1542
|
+
".hpp": "cpp",
|
|
1543
|
+
".md": "markdown",
|
|
1544
|
+
".json": "json",
|
|
1545
|
+
".yaml": "yaml",
|
|
1546
|
+
".yml": "yaml"
|
|
790
1547
|
};
|
|
791
1548
|
});
|
|
792
1549
|
|
|
793
|
-
// src/
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
});
|
|
1550
|
+
// src/introspection/index.ts
|
|
1551
|
+
import * as path6 from "path";
|
|
1552
|
+
import * as fs3 from "fs/promises";
|
|
797
1553
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
1554
|
+
class IntrospectionIndex {
|
|
1555
|
+
rootDir;
|
|
1556
|
+
structure = null;
|
|
1557
|
+
files = new Map;
|
|
1558
|
+
config = {};
|
|
1559
|
+
constructor(rootDir) {
|
|
1560
|
+
this.rootDir = rootDir;
|
|
802
1561
|
}
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
1562
|
+
async initialize() {
|
|
1563
|
+
this.structure = await detectProjectStructure(this.rootDir);
|
|
1564
|
+
try {
|
|
1565
|
+
const configPath = path6.join(this.rootDir, ".raggrep", "config.json");
|
|
1566
|
+
const configContent = await fs3.readFile(configPath, "utf-8");
|
|
1567
|
+
const config = JSON.parse(configContent);
|
|
1568
|
+
this.config = config.introspection || {};
|
|
1569
|
+
} catch {}
|
|
810
1570
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
return 0;
|
|
814
|
-
return dotProduct / magnitude;
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
// src/modules/language/typescript/parseCode.ts
|
|
818
|
-
import * as ts from "typescript";
|
|
819
|
-
function parseCode(content, filepath) {
|
|
820
|
-
const ext = filepath.split(".").pop()?.toLowerCase();
|
|
821
|
-
if (["ts", "tsx", "js", "jsx", "mts", "cts", "mjs", "cjs"].includes(ext || "")) {
|
|
822
|
-
return parseTypeScript(content, filepath);
|
|
1571
|
+
getStructure() {
|
|
1572
|
+
return this.structure;
|
|
823
1573
|
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
const start = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
833
|
-
const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
|
|
834
|
-
return {
|
|
835
|
-
startLine: start.line + 1,
|
|
836
|
-
endLine: end.line + 1
|
|
837
|
-
};
|
|
1574
|
+
addFile(filepath, content) {
|
|
1575
|
+
if (!this.structure) {
|
|
1576
|
+
throw new Error("IntrospectionIndex not initialized");
|
|
1577
|
+
}
|
|
1578
|
+
const intro = introspectFile(filepath, this.structure, content);
|
|
1579
|
+
this.applyOverrides(intro);
|
|
1580
|
+
this.files.set(filepath, intro);
|
|
1581
|
+
return intro;
|
|
838
1582
|
}
|
|
839
|
-
|
|
840
|
-
return
|
|
1583
|
+
getFile(filepath) {
|
|
1584
|
+
return this.files.get(filepath);
|
|
841
1585
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
return false;
|
|
845
|
-
const modifiers = ts.getModifiers(node);
|
|
846
|
-
return modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
1586
|
+
getAllFiles() {
|
|
1587
|
+
return Array.from(this.files.values());
|
|
847
1588
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
if (jsDocNodes.length === 0)
|
|
1589
|
+
applyOverrides(intro) {
|
|
1590
|
+
if (!this.config.projects)
|
|
851
1591
|
return;
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
1592
|
+
for (const [projectPath, overrides] of Object.entries(this.config.projects)) {
|
|
1593
|
+
if (intro.filepath.startsWith(projectPath + "/") || intro.project.root === projectPath) {
|
|
1594
|
+
if (overrides.scope) {
|
|
1595
|
+
intro.scope = overrides.scope;
|
|
1596
|
+
}
|
|
1597
|
+
if (overrides.framework) {
|
|
1598
|
+
intro.framework = overrides.framework;
|
|
1599
|
+
}
|
|
1600
|
+
break;
|
|
1601
|
+
}
|
|
861
1602
|
}
|
|
862
|
-
|
|
863
|
-
|
|
1603
|
+
}
|
|
1604
|
+
async save(config) {
|
|
1605
|
+
const introDir = path6.join(getRaggrepDir(this.rootDir, config), "introspection");
|
|
1606
|
+
await fs3.mkdir(introDir, { recursive: true });
|
|
1607
|
+
const projectPath = path6.join(introDir, "_project.json");
|
|
1608
|
+
await fs3.writeFile(projectPath, JSON.stringify({
|
|
1609
|
+
version: "1.0.0",
|
|
1610
|
+
lastUpdated: new Date().toISOString(),
|
|
1611
|
+
structure: this.structure
|
|
1612
|
+
}, null, 2));
|
|
1613
|
+
for (const [filepath, intro] of this.files) {
|
|
1614
|
+
const introFilePath = path6.join(introDir, "files", filepath.replace(/\.[^.]+$/, ".json"));
|
|
1615
|
+
await fs3.mkdir(path6.dirname(introFilePath), { recursive: true });
|
|
1616
|
+
await fs3.writeFile(introFilePath, JSON.stringify(intro, null, 2));
|
|
864
1617
|
}
|
|
865
|
-
|
|
1618
|
+
console.log(` [Introspection] Saved metadata for ${this.files.size} files`);
|
|
866
1619
|
}
|
|
867
|
-
|
|
868
|
-
const
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
});
|
|
879
|
-
return;
|
|
1620
|
+
async load(config) {
|
|
1621
|
+
const introDir = path6.join(getRaggrepDir(this.rootDir, config), "introspection");
|
|
1622
|
+
try {
|
|
1623
|
+
const projectPath = path6.join(introDir, "_project.json");
|
|
1624
|
+
const projectContent = await fs3.readFile(projectPath, "utf-8");
|
|
1625
|
+
const projectData = JSON.parse(projectContent);
|
|
1626
|
+
this.structure = projectData.structure;
|
|
1627
|
+
await this.loadFilesRecursive(path6.join(introDir, "files"), "");
|
|
1628
|
+
} catch {
|
|
1629
|
+
this.structure = null;
|
|
1630
|
+
this.files.clear();
|
|
880
1631
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
return;
|
|
1632
|
+
}
|
|
1633
|
+
async loadFilesRecursive(basePath, prefix) {
|
|
1634
|
+
try {
|
|
1635
|
+
const entries = await fs3.readdir(basePath, { withFileTypes: true });
|
|
1636
|
+
for (const entry of entries) {
|
|
1637
|
+
const entryPath = path6.join(basePath, entry.name);
|
|
1638
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
1639
|
+
if (entry.isDirectory()) {
|
|
1640
|
+
await this.loadFilesRecursive(entryPath, relativePath);
|
|
1641
|
+
} else if (entry.name.endsWith(".json")) {
|
|
1642
|
+
const content = await fs3.readFile(entryPath, "utf-8");
|
|
1643
|
+
const intro = JSON.parse(content);
|
|
1644
|
+
this.files.set(intro.filepath, intro);
|
|
895
1645
|
}
|
|
896
1646
|
}
|
|
897
|
-
}
|
|
898
|
-
if (ts.isClassDeclaration(node) && node.name) {
|
|
899
|
-
chunks.push({
|
|
900
|
-
content: getNodeText(node),
|
|
901
|
-
startLine,
|
|
902
|
-
endLine,
|
|
903
|
-
type: "class",
|
|
904
|
-
name: node.name.text,
|
|
905
|
-
isExported: isExported(node),
|
|
906
|
-
jsDoc: getJSDoc(node)
|
|
907
|
-
});
|
|
908
|
-
return;
|
|
909
|
-
}
|
|
910
|
-
if (ts.isInterfaceDeclaration(node)) {
|
|
911
|
-
chunks.push({
|
|
912
|
-
content: getNodeText(node),
|
|
913
|
-
startLine,
|
|
914
|
-
endLine,
|
|
915
|
-
type: "interface",
|
|
916
|
-
name: node.name.text,
|
|
917
|
-
isExported: isExported(node),
|
|
918
|
-
jsDoc: getJSDoc(node)
|
|
919
|
-
});
|
|
920
|
-
return;
|
|
921
|
-
}
|
|
922
|
-
if (ts.isTypeAliasDeclaration(node)) {
|
|
923
|
-
chunks.push({
|
|
924
|
-
content: getNodeText(node),
|
|
925
|
-
startLine,
|
|
926
|
-
endLine,
|
|
927
|
-
type: "type",
|
|
928
|
-
name: node.name.text,
|
|
929
|
-
isExported: isExported(node),
|
|
930
|
-
jsDoc: getJSDoc(node)
|
|
931
|
-
});
|
|
932
|
-
return;
|
|
933
|
-
}
|
|
934
|
-
if (ts.isEnumDeclaration(node)) {
|
|
935
|
-
chunks.push({
|
|
936
|
-
content: getNodeText(node),
|
|
937
|
-
startLine,
|
|
938
|
-
endLine,
|
|
939
|
-
type: "enum",
|
|
940
|
-
name: node.name.text,
|
|
941
|
-
isExported: isExported(node),
|
|
942
|
-
jsDoc: getJSDoc(node)
|
|
943
|
-
});
|
|
944
|
-
return;
|
|
945
|
-
}
|
|
946
|
-
if (ts.isVariableStatement(node) && isExported(node)) {
|
|
947
|
-
for (const decl of node.declarationList.declarations) {
|
|
948
|
-
if (decl.initializer && (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer))) {
|
|
949
|
-
continue;
|
|
950
|
-
}
|
|
951
|
-
const name = ts.isIdentifier(decl.name) ? decl.name.text : undefined;
|
|
952
|
-
chunks.push({
|
|
953
|
-
content: getNodeText(node),
|
|
954
|
-
startLine,
|
|
955
|
-
endLine,
|
|
956
|
-
type: "variable",
|
|
957
|
-
name,
|
|
958
|
-
isExported: true,
|
|
959
|
-
jsDoc: getJSDoc(node)
|
|
960
|
-
});
|
|
961
|
-
}
|
|
962
|
-
return;
|
|
963
|
-
}
|
|
964
|
-
ts.forEachChild(node, visit);
|
|
965
|
-
}
|
|
966
|
-
ts.forEachChild(sourceFile, visit);
|
|
967
|
-
if (chunks.length === 0) {
|
|
968
|
-
return parseGenericCode(content);
|
|
969
|
-
}
|
|
970
|
-
return chunks;
|
|
971
|
-
}
|
|
972
|
-
function parseGenericCode(content) {
|
|
973
|
-
const chunks = [];
|
|
974
|
-
const lines = content.split(`
|
|
975
|
-
`);
|
|
976
|
-
const CHUNK_SIZE = 30;
|
|
977
|
-
const OVERLAP = 5;
|
|
978
|
-
if (lines.length <= CHUNK_SIZE) {
|
|
979
|
-
return [
|
|
980
|
-
{
|
|
981
|
-
content,
|
|
982
|
-
startLine: 1,
|
|
983
|
-
endLine: lines.length,
|
|
984
|
-
type: "file"
|
|
985
|
-
}
|
|
986
|
-
];
|
|
1647
|
+
} catch {}
|
|
987
1648
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
content: lines.slice(i, endIdx).join(`
|
|
992
|
-
`),
|
|
993
|
-
startLine: i + 1,
|
|
994
|
-
endLine: endIdx,
|
|
995
|
-
type: "block"
|
|
996
|
-
});
|
|
997
|
-
if (endIdx >= lines.length)
|
|
998
|
-
break;
|
|
1649
|
+
clear() {
|
|
1650
|
+
this.files.clear();
|
|
1651
|
+
this.structure = null;
|
|
999
1652
|
}
|
|
1000
|
-
return chunks;
|
|
1001
|
-
}
|
|
1002
|
-
function generateChunkId(filepath, startLine, endLine) {
|
|
1003
|
-
const safePath = filepath.replace(/[/\\]/g, "-").replace(/\./g, "_");
|
|
1004
|
-
return `${safePath}-${startLine}-${endLine}`;
|
|
1005
1653
|
}
|
|
1006
|
-
var
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1654
|
+
var init_introspection = __esm(() => {
|
|
1655
|
+
init_projectDetector();
|
|
1656
|
+
init_fileIntrospector();
|
|
1657
|
+
init_config2();
|
|
1658
|
+
init_fileIntrospector();
|
|
1659
|
+
init_projectDetector();
|
|
1011
1660
|
});
|
|
1012
1661
|
|
|
1013
|
-
// src/
|
|
1014
|
-
function
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1662
|
+
// src/modules/core/symbols.ts
|
|
1663
|
+
function extractSymbols(content) {
|
|
1664
|
+
const symbols = [];
|
|
1665
|
+
const seenSymbols = new Set;
|
|
1666
|
+
const lines = content.split(`
|
|
1667
|
+
`);
|
|
1668
|
+
for (const { type, pattern, exported } of SYMBOL_PATTERNS) {
|
|
1669
|
+
pattern.lastIndex = 0;
|
|
1670
|
+
let match;
|
|
1671
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
1672
|
+
const name = match[1];
|
|
1673
|
+
const symbolKey = `${name}:${type}`;
|
|
1674
|
+
if (seenSymbols.has(symbolKey))
|
|
1675
|
+
continue;
|
|
1676
|
+
seenSymbols.add(symbolKey);
|
|
1677
|
+
const beforeMatch = content.substring(0, match.index);
|
|
1678
|
+
const line = beforeMatch.split(`
|
|
1679
|
+
`).length;
|
|
1680
|
+
symbols.push({
|
|
1681
|
+
name,
|
|
1682
|
+
type,
|
|
1683
|
+
line,
|
|
1684
|
+
isExported: exported
|
|
1685
|
+
});
|
|
1027
1686
|
}
|
|
1028
1687
|
}
|
|
1029
|
-
return
|
|
1030
|
-
}
|
|
1031
|
-
function splitIdentifier(str) {
|
|
1032
|
-
return str.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").split(/\s+/).map((s) => s.toLowerCase()).filter((s) => s.length > 1);
|
|
1688
|
+
return symbols.sort((a, b) => a.line - b.line);
|
|
1033
1689
|
}
|
|
1034
|
-
function
|
|
1690
|
+
function symbolsToKeywords(symbols) {
|
|
1035
1691
|
const keywords = new Set;
|
|
1036
|
-
const
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
if (segment.length < 2)
|
|
1040
|
-
continue;
|
|
1041
|
-
const lower = segment.toLowerCase();
|
|
1042
|
-
if (!COMMON_KEYWORDS.has(lower) && lower.length > 2) {
|
|
1043
|
-
keywords.add(lower);
|
|
1044
|
-
}
|
|
1045
|
-
const parts = splitIdentifier(segment);
|
|
1692
|
+
for (const symbol of symbols) {
|
|
1693
|
+
keywords.add(symbol.name.toLowerCase());
|
|
1694
|
+
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+/);
|
|
1046
1695
|
for (const part of parts) {
|
|
1047
|
-
if (
|
|
1696
|
+
if (part.length > 2) {
|
|
1048
1697
|
keywords.add(part);
|
|
1049
1698
|
}
|
|
1050
1699
|
}
|
|
1051
1700
|
}
|
|
1052
1701
|
return Array.from(keywords);
|
|
1053
1702
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1703
|
+
var SYMBOL_PATTERNS;
|
|
1704
|
+
var init_symbols = __esm(() => {
|
|
1705
|
+
SYMBOL_PATTERNS = [
|
|
1706
|
+
{
|
|
1707
|
+
type: "function",
|
|
1708
|
+
pattern: /^export\s+(?:async\s+)?function\s+(\w+)/gm,
|
|
1709
|
+
exported: true
|
|
1710
|
+
},
|
|
1711
|
+
{
|
|
1712
|
+
type: "function",
|
|
1713
|
+
pattern: /^export\s+(?:const|let)\s+(\w+)\s*=\s*(?:async\s*)?\(/gm,
|
|
1714
|
+
exported: true
|
|
1715
|
+
},
|
|
1716
|
+
{
|
|
1717
|
+
type: "class",
|
|
1718
|
+
pattern: /^export\s+(?:abstract\s+)?class\s+(\w+)/gm,
|
|
1719
|
+
exported: true
|
|
1720
|
+
},
|
|
1721
|
+
{
|
|
1722
|
+
type: "interface",
|
|
1723
|
+
pattern: /^export\s+interface\s+(\w+)/gm,
|
|
1724
|
+
exported: true
|
|
1725
|
+
},
|
|
1726
|
+
{
|
|
1727
|
+
type: "type",
|
|
1728
|
+
pattern: /^export\s+type\s+(\w+)/gm,
|
|
1729
|
+
exported: true
|
|
1730
|
+
},
|
|
1731
|
+
{
|
|
1732
|
+
type: "enum",
|
|
1733
|
+
pattern: /^export\s+(?:const\s+)?enum\s+(\w+)/gm,
|
|
1734
|
+
exported: true
|
|
1735
|
+
},
|
|
1736
|
+
{
|
|
1737
|
+
type: "variable",
|
|
1738
|
+
pattern: /^export\s+(?:const|let|var)\s+(\w+)\s*(?::|=)/gm,
|
|
1739
|
+
exported: true
|
|
1740
|
+
},
|
|
1741
|
+
{
|
|
1742
|
+
type: "function",
|
|
1743
|
+
pattern: /^export\s+default\s+(?:async\s+)?function\s+(\w+)/gm,
|
|
1744
|
+
exported: true
|
|
1745
|
+
},
|
|
1746
|
+
{
|
|
1747
|
+
type: "class",
|
|
1748
|
+
pattern: /^export\s+default\s+class\s+(\w+)/gm,
|
|
1749
|
+
exported: true
|
|
1750
|
+
},
|
|
1751
|
+
{
|
|
1752
|
+
type: "function",
|
|
1753
|
+
pattern: /^(?:async\s+)?function\s+(\w+)/gm,
|
|
1754
|
+
exported: false
|
|
1755
|
+
},
|
|
1756
|
+
{
|
|
1757
|
+
type: "function",
|
|
1758
|
+
pattern: /^(?:const|let)\s+(\w+)\s*=\s*(?:async\s*)?\(/gm,
|
|
1759
|
+
exported: false
|
|
1760
|
+
},
|
|
1761
|
+
{
|
|
1762
|
+
type: "class",
|
|
1763
|
+
pattern: /^(?:abstract\s+)?class\s+(\w+)/gm,
|
|
1764
|
+
exported: false
|
|
1765
|
+
},
|
|
1766
|
+
{
|
|
1767
|
+
type: "interface",
|
|
1768
|
+
pattern: /^interface\s+(\w+)/gm,
|
|
1769
|
+
exported: false
|
|
1770
|
+
},
|
|
1771
|
+
{
|
|
1772
|
+
type: "type",
|
|
1773
|
+
pattern: /^type\s+(\w+)/gm,
|
|
1774
|
+
exported: false
|
|
1775
|
+
},
|
|
1776
|
+
{
|
|
1777
|
+
type: "enum",
|
|
1778
|
+
pattern: /^(?:const\s+)?enum\s+(\w+)/gm,
|
|
1779
|
+
exported: false
|
|
1780
|
+
},
|
|
1781
|
+
{
|
|
1782
|
+
type: "function",
|
|
1783
|
+
pattern: /^def\s+(\w+)\s*\(/gm,
|
|
1784
|
+
exported: false
|
|
1785
|
+
},
|
|
1786
|
+
{
|
|
1787
|
+
type: "class",
|
|
1788
|
+
pattern: /^class\s+(\w+)(?:\s*\(|:)/gm,
|
|
1789
|
+
exported: false
|
|
1790
|
+
},
|
|
1791
|
+
{
|
|
1792
|
+
type: "function",
|
|
1793
|
+
pattern: /^func\s+(?:\([^)]+\)\s+)?(\w+)\s*\(/gm,
|
|
1794
|
+
exported: false
|
|
1795
|
+
},
|
|
1796
|
+
{
|
|
1797
|
+
type: "type",
|
|
1798
|
+
pattern: /^type\s+(\w+)\s+(?:struct|interface)/gm,
|
|
1799
|
+
exported: false
|
|
1800
|
+
},
|
|
1801
|
+
{
|
|
1802
|
+
type: "function",
|
|
1803
|
+
pattern: /^(?:pub\s+)?(?:async\s+)?fn\s+(\w+)/gm,
|
|
1804
|
+
exported: false
|
|
1805
|
+
},
|
|
1806
|
+
{
|
|
1807
|
+
type: "type",
|
|
1808
|
+
pattern: /^(?:pub\s+)?struct\s+(\w+)/gm,
|
|
1809
|
+
exported: false
|
|
1810
|
+
},
|
|
1811
|
+
{
|
|
1812
|
+
type: "enum",
|
|
1813
|
+
pattern: /^(?:pub\s+)?enum\s+(\w+)/gm,
|
|
1814
|
+
exported: false
|
|
1815
|
+
},
|
|
1816
|
+
{
|
|
1817
|
+
type: "interface",
|
|
1818
|
+
pattern: /^(?:pub\s+)?trait\s+(\w+)/gm,
|
|
1819
|
+
exported: false
|
|
1820
|
+
}
|
|
1821
|
+
];
|
|
1822
|
+
});
|
|
1823
|
+
|
|
1824
|
+
// src/modules/core/index.ts
|
|
1825
|
+
var exports_core = {};
|
|
1826
|
+
__export(exports_core, {
|
|
1827
|
+
CoreModule: () => CoreModule
|
|
1828
|
+
});
|
|
1829
|
+
import * as path7 from "path";
|
|
1830
|
+
import * as fs4 from "fs/promises";
|
|
1831
|
+
|
|
1832
|
+
class CoreModule {
|
|
1833
|
+
id = "core";
|
|
1834
|
+
name = "Core Search";
|
|
1835
|
+
description = "Language-agnostic text search with symbol extraction";
|
|
1836
|
+
version = "1.0.0";
|
|
1837
|
+
symbolIndex = new Map;
|
|
1838
|
+
bm25Index = null;
|
|
1839
|
+
rootDir = "";
|
|
1840
|
+
async initialize(_config) {}
|
|
1841
|
+
async indexFile(filepath, content, ctx) {
|
|
1842
|
+
this.rootDir = ctx.rootDir;
|
|
1843
|
+
const symbols = extractSymbols(content);
|
|
1844
|
+
const symbolKeywords = symbolsToKeywords(symbols);
|
|
1845
|
+
const contentTokens = tokenize(content);
|
|
1846
|
+
const intro = ctx.getIntrospection?.(filepath);
|
|
1847
|
+
const introKeywords = intro ? introspectionToKeywords(intro) : [];
|
|
1848
|
+
const allTokens = [...new Set([...contentTokens, ...symbolKeywords, ...introKeywords])];
|
|
1849
|
+
const chunks = this.createChunks(filepath, content, symbols);
|
|
1850
|
+
const stats = await ctx.getFileStats(filepath);
|
|
1851
|
+
this.symbolIndex.set(filepath, {
|
|
1852
|
+
filepath,
|
|
1853
|
+
symbols,
|
|
1854
|
+
tokens: allTokens
|
|
1855
|
+
});
|
|
1856
|
+
const moduleData = {
|
|
1857
|
+
symbols,
|
|
1858
|
+
tokens: allTokens
|
|
1859
|
+
};
|
|
1860
|
+
return {
|
|
1861
|
+
filepath,
|
|
1862
|
+
lastModified: stats.lastModified,
|
|
1863
|
+
chunks,
|
|
1864
|
+
moduleData
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
createChunks(filepath, content, symbols) {
|
|
1868
|
+
const lines = content.split(`
|
|
1869
|
+
`);
|
|
1870
|
+
const chunks = [];
|
|
1871
|
+
for (let start = 0;start < lines.length; start += LINES_PER_CHUNK - CHUNK_OVERLAP) {
|
|
1872
|
+
const end = Math.min(start + LINES_PER_CHUNK, lines.length);
|
|
1873
|
+
const chunkLines = lines.slice(start, end);
|
|
1874
|
+
const chunkContent = chunkLines.join(`
|
|
1875
|
+
`);
|
|
1876
|
+
const chunkSymbols = symbols.filter((s) => s.line >= start + 1 && s.line <= end);
|
|
1877
|
+
let chunkType = "block";
|
|
1878
|
+
let chunkName;
|
|
1879
|
+
let isExported = false;
|
|
1880
|
+
if (chunkSymbols.length > 0) {
|
|
1881
|
+
const primarySymbol = chunkSymbols[0];
|
|
1882
|
+
chunkType = this.symbolTypeToChunkType(primarySymbol.type);
|
|
1883
|
+
chunkName = primarySymbol.name;
|
|
1884
|
+
isExported = primarySymbol.isExported;
|
|
1885
|
+
}
|
|
1886
|
+
const chunkId = `${filepath}:${start + 1}-${end}`;
|
|
1887
|
+
chunks.push({
|
|
1888
|
+
id: chunkId,
|
|
1889
|
+
content: chunkContent,
|
|
1890
|
+
startLine: start + 1,
|
|
1891
|
+
endLine: end,
|
|
1892
|
+
type: chunkType,
|
|
1893
|
+
name: chunkName,
|
|
1894
|
+
isExported
|
|
1895
|
+
});
|
|
1896
|
+
if (end >= lines.length)
|
|
1897
|
+
break;
|
|
1898
|
+
}
|
|
1899
|
+
return chunks;
|
|
1900
|
+
}
|
|
1901
|
+
symbolTypeToChunkType(symbolType) {
|
|
1902
|
+
switch (symbolType) {
|
|
1903
|
+
case "function":
|
|
1904
|
+
case "method":
|
|
1905
|
+
return "function";
|
|
1906
|
+
case "class":
|
|
1907
|
+
return "class";
|
|
1908
|
+
case "interface":
|
|
1909
|
+
return "interface";
|
|
1910
|
+
case "type":
|
|
1911
|
+
return "type";
|
|
1912
|
+
case "enum":
|
|
1913
|
+
return "enum";
|
|
1914
|
+
case "variable":
|
|
1915
|
+
return "variable";
|
|
1916
|
+
default:
|
|
1917
|
+
return "block";
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
async finalize(ctx) {
|
|
1921
|
+
const config = ctx.config;
|
|
1922
|
+
const coreDir = path7.join(getRaggrepDir(ctx.rootDir, config), "index", "core");
|
|
1923
|
+
await fs4.mkdir(coreDir, { recursive: true });
|
|
1924
|
+
this.bm25Index = new BM25Index;
|
|
1925
|
+
for (const [filepath, entry] of this.symbolIndex) {
|
|
1926
|
+
this.bm25Index.addDocument(filepath, entry.tokens);
|
|
1927
|
+
}
|
|
1928
|
+
const symbolIndexData = {
|
|
1929
|
+
version: this.version,
|
|
1930
|
+
lastUpdated: new Date().toISOString(),
|
|
1931
|
+
files: Object.fromEntries(this.symbolIndex),
|
|
1932
|
+
bm25Data: this.bm25Index.serialize()
|
|
1933
|
+
};
|
|
1934
|
+
await fs4.writeFile(path7.join(coreDir, "symbols.json"), JSON.stringify(symbolIndexData, null, 2));
|
|
1935
|
+
console.log(` [Core] Symbol index built with ${this.symbolIndex.size} files`);
|
|
1936
|
+
}
|
|
1937
|
+
async search(query, ctx, options) {
|
|
1938
|
+
const config = ctx.config;
|
|
1939
|
+
const topK = options?.topK ?? DEFAULT_TOP_K;
|
|
1940
|
+
const minScore = options?.minScore ?? DEFAULT_MIN_SCORE;
|
|
1941
|
+
if (this.symbolIndex.size === 0) {
|
|
1942
|
+
await this.loadSymbolIndex(ctx.rootDir, config);
|
|
1943
|
+
}
|
|
1944
|
+
if (!this.bm25Index || this.symbolIndex.size === 0) {
|
|
1945
|
+
return [];
|
|
1946
|
+
}
|
|
1947
|
+
const queryTokens = tokenize(query);
|
|
1948
|
+
const bm25Results = this.bm25Index.search(query, topK * 2);
|
|
1949
|
+
const bm25Scores = new Map(bm25Results.map((r) => [r.id, r.score]));
|
|
1950
|
+
const symbolMatches = this.findSymbolMatches(queryTokens);
|
|
1951
|
+
const results = [];
|
|
1952
|
+
for (const filepath of this.symbolIndex.keys()) {
|
|
1953
|
+
const entry = this.symbolIndex.get(filepath);
|
|
1954
|
+
const bm25Score = bm25Scores.get(filepath) ?? 0;
|
|
1955
|
+
const symbolScore = symbolMatches.get(filepath) ?? 0;
|
|
1956
|
+
if (bm25Score === 0 && symbolScore === 0)
|
|
1957
|
+
continue;
|
|
1958
|
+
const combinedScore = 0.6 * normalizeScore(bm25Score) + 0.4 * symbolScore;
|
|
1959
|
+
if (combinedScore >= minScore) {
|
|
1960
|
+
const fileIndex = await ctx.loadFileIndex(filepath);
|
|
1961
|
+
if (!fileIndex)
|
|
1962
|
+
continue;
|
|
1963
|
+
const bestChunk = this.findBestChunk(fileIndex.chunks, queryTokens, entry.symbols);
|
|
1964
|
+
results.push({
|
|
1965
|
+
filepath,
|
|
1966
|
+
chunk: bestChunk,
|
|
1967
|
+
score: combinedScore,
|
|
1968
|
+
moduleId: this.id,
|
|
1969
|
+
context: {
|
|
1970
|
+
bm25Score: normalizeScore(bm25Score),
|
|
1971
|
+
symbolScore
|
|
1972
|
+
}
|
|
1973
|
+
});
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
return results.sort((a, b) => b.score - a.score).slice(0, topK);
|
|
1977
|
+
}
|
|
1978
|
+
findSymbolMatches(queryTokens) {
|
|
1979
|
+
const matches = new Map;
|
|
1980
|
+
for (const [filepath, entry] of this.symbolIndex) {
|
|
1981
|
+
let matchScore = 0;
|
|
1982
|
+
for (const symbol of entry.symbols) {
|
|
1983
|
+
const symbolName = symbol.name.toLowerCase();
|
|
1984
|
+
const symbolParts = symbolsToKeywords([symbol]);
|
|
1985
|
+
for (const token of queryTokens) {
|
|
1986
|
+
if (symbolName === token) {
|
|
1987
|
+
matchScore += symbol.isExported ? 1 : 0.8;
|
|
1988
|
+
} else if (symbolName.includes(token) || token.includes(symbolName)) {
|
|
1989
|
+
matchScore += symbol.isExported ? 0.5 : 0.4;
|
|
1990
|
+
} else if (symbolParts.some((p) => p === token)) {
|
|
1991
|
+
matchScore += symbol.isExported ? 0.3 : 0.2;
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
if (matchScore > 0) {
|
|
1996
|
+
matches.set(filepath, Math.min(1, matchScore / queryTokens.length));
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
return matches;
|
|
2000
|
+
}
|
|
2001
|
+
findBestChunk(chunks, queryTokens, symbols) {
|
|
2002
|
+
let bestChunk = chunks[0];
|
|
2003
|
+
let bestScore = 0;
|
|
2004
|
+
for (const chunk of chunks) {
|
|
2005
|
+
let score = 0;
|
|
2006
|
+
const chunkContent = chunk.content.toLowerCase();
|
|
2007
|
+
for (const token of queryTokens) {
|
|
2008
|
+
if (chunkContent.includes(token)) {
|
|
2009
|
+
score += 1;
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
if (chunk.name) {
|
|
2013
|
+
const nameLower = chunk.name.toLowerCase();
|
|
2014
|
+
for (const token of queryTokens) {
|
|
2015
|
+
if (nameLower.includes(token)) {
|
|
2016
|
+
score += 2;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
if (chunk.isExported) {
|
|
2021
|
+
score += 0.5;
|
|
2022
|
+
}
|
|
2023
|
+
if (score > bestScore) {
|
|
2024
|
+
bestScore = score;
|
|
2025
|
+
bestChunk = chunk;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
return bestChunk;
|
|
2029
|
+
}
|
|
2030
|
+
async loadSymbolIndex(rootDir, config) {
|
|
2031
|
+
const coreDir = path7.join(getRaggrepDir(rootDir, config), "index", "core");
|
|
2032
|
+
const symbolsPath = path7.join(coreDir, "symbols.json");
|
|
2033
|
+
try {
|
|
2034
|
+
const content = await fs4.readFile(symbolsPath, "utf-8");
|
|
2035
|
+
const data = JSON.parse(content);
|
|
2036
|
+
this.symbolIndex = new Map(Object.entries(data.files));
|
|
2037
|
+
if (data.bm25Data) {
|
|
2038
|
+
this.bm25Index = BM25Index.deserialize(data.bm25Data);
|
|
2039
|
+
}
|
|
2040
|
+
} catch (error) {
|
|
2041
|
+
this.symbolIndex = new Map;
|
|
2042
|
+
this.bm25Index = null;
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
async dispose() {
|
|
2046
|
+
this.symbolIndex.clear();
|
|
2047
|
+
this.bm25Index = null;
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
var DEFAULT_MIN_SCORE = 0.1, DEFAULT_TOP_K = 20, LINES_PER_CHUNK = 50, CHUNK_OVERLAP = 10;
|
|
2051
|
+
var init_core = __esm(() => {
|
|
2052
|
+
init_config2();
|
|
2053
|
+
init_introspection();
|
|
2054
|
+
init_symbols();
|
|
1199
2055
|
});
|
|
1200
2056
|
|
|
1201
|
-
// src/infrastructure/
|
|
1202
|
-
import
|
|
1203
|
-
import * as
|
|
2057
|
+
// src/infrastructure/embeddings/transformersEmbedding.ts
|
|
2058
|
+
import { pipeline, env } from "@xenova/transformers";
|
|
2059
|
+
import * as path8 from "path";
|
|
2060
|
+
import * as os2 from "os";
|
|
1204
2061
|
|
|
1205
|
-
class
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
2062
|
+
class TransformersEmbeddingProvider {
|
|
2063
|
+
pipeline = null;
|
|
2064
|
+
config;
|
|
2065
|
+
isInitializing = false;
|
|
2066
|
+
initPromise = null;
|
|
2067
|
+
constructor(config) {
|
|
2068
|
+
this.config = {
|
|
2069
|
+
model: config?.model ?? "all-MiniLM-L6-v2",
|
|
2070
|
+
showProgress: config?.showProgress ?? true
|
|
2071
|
+
};
|
|
1214
2072
|
}
|
|
1215
|
-
async initialize() {
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
lastUpdated: new Date().toISOString(),
|
|
1222
|
-
moduleId: this.moduleId,
|
|
1223
|
-
fileCount: 0,
|
|
1224
|
-
bm25Data: {
|
|
1225
|
-
avgDocLength: 0,
|
|
1226
|
-
documentFrequencies: {},
|
|
1227
|
-
totalDocs: 0
|
|
1228
|
-
}
|
|
1229
|
-
};
|
|
1230
|
-
this.bm25Index = new BM25Index;
|
|
2073
|
+
async initialize(config) {
|
|
2074
|
+
if (config) {
|
|
2075
|
+
if (config.model !== this.config.model) {
|
|
2076
|
+
this.pipeline = null;
|
|
2077
|
+
}
|
|
2078
|
+
this.config = { ...this.config, ...config };
|
|
1231
2079
|
}
|
|
2080
|
+
await this.ensurePipeline();
|
|
1232
2081
|
}
|
|
1233
|
-
|
|
1234
|
-
this.
|
|
2082
|
+
async ensurePipeline() {
|
|
2083
|
+
if (this.pipeline) {
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
if (this.isInitializing && this.initPromise) {
|
|
2087
|
+
return this.initPromise;
|
|
2088
|
+
}
|
|
2089
|
+
this.isInitializing = true;
|
|
2090
|
+
this.initPromise = (async () => {
|
|
2091
|
+
const modelId = EMBEDDING_MODELS2[this.config.model];
|
|
2092
|
+
if (this.config.showProgress) {
|
|
2093
|
+
console.log(`
|
|
2094
|
+
Loading embedding model: ${this.config.model}`);
|
|
2095
|
+
console.log(` Cache: ${CACHE_DIR}`);
|
|
2096
|
+
}
|
|
2097
|
+
try {
|
|
2098
|
+
this.pipeline = await pipeline("feature-extraction", modelId, {
|
|
2099
|
+
progress_callback: this.config.showProgress ? (progress) => {
|
|
2100
|
+
if (progress.status === "progress" && progress.file) {
|
|
2101
|
+
const pct = progress.progress ? Math.round(progress.progress) : 0;
|
|
2102
|
+
process.stdout.write(`\r Downloading ${progress.file}: ${pct}% `);
|
|
2103
|
+
} else if (progress.status === "done" && progress.file) {
|
|
2104
|
+
process.stdout.write(`\r Downloaded ${progress.file}
|
|
2105
|
+
`);
|
|
2106
|
+
}
|
|
2107
|
+
} : undefined
|
|
2108
|
+
});
|
|
2109
|
+
if (this.config.showProgress) {
|
|
2110
|
+
console.log(` Model ready.
|
|
2111
|
+
`);
|
|
2112
|
+
}
|
|
2113
|
+
} catch (error) {
|
|
2114
|
+
this.pipeline = null;
|
|
2115
|
+
throw new Error(`Failed to load embedding model: ${error}`);
|
|
2116
|
+
} finally {
|
|
2117
|
+
this.isInitializing = false;
|
|
2118
|
+
this.initPromise = null;
|
|
2119
|
+
}
|
|
2120
|
+
})();
|
|
2121
|
+
return this.initPromise;
|
|
1235
2122
|
}
|
|
1236
|
-
|
|
1237
|
-
|
|
2123
|
+
async getEmbedding(text) {
|
|
2124
|
+
await this.ensurePipeline();
|
|
2125
|
+
if (!this.pipeline) {
|
|
2126
|
+
throw new Error("Embedding pipeline not initialized");
|
|
2127
|
+
}
|
|
2128
|
+
const output = await this.pipeline(text, {
|
|
2129
|
+
pooling: "mean",
|
|
2130
|
+
normalize: true
|
|
2131
|
+
});
|
|
2132
|
+
return Array.from(output.data);
|
|
1238
2133
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
...extractPathKeywords(filepath)
|
|
1246
|
-
].join(" ");
|
|
1247
|
-
this.bm25Index.addDocuments([{ id: filepath, content }]);
|
|
2134
|
+
async getEmbeddings(texts) {
|
|
2135
|
+
if (texts.length === 0)
|
|
2136
|
+
return [];
|
|
2137
|
+
await this.ensurePipeline();
|
|
2138
|
+
if (!this.pipeline) {
|
|
2139
|
+
throw new Error("Embedding pipeline not initialized");
|
|
1248
2140
|
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
2141
|
+
const results = [];
|
|
2142
|
+
for (let i = 0;i < texts.length; i += BATCH_SIZE) {
|
|
2143
|
+
const batch = texts.slice(i, i + BATCH_SIZE);
|
|
2144
|
+
const outputs = await Promise.all(batch.map(async (text) => {
|
|
2145
|
+
const output = await this.pipeline(text, {
|
|
2146
|
+
pooling: "mean",
|
|
2147
|
+
normalize: true
|
|
2148
|
+
});
|
|
2149
|
+
return Array.from(output.data);
|
|
2150
|
+
}));
|
|
2151
|
+
results.push(...outputs);
|
|
1252
2152
|
}
|
|
2153
|
+
return results;
|
|
1253
2154
|
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
return Array.from(this.fileSummaries.keys());
|
|
1257
|
-
}
|
|
1258
|
-
const results = this.bm25Index.search(query, maxCandidates);
|
|
1259
|
-
return results.map((r) => r.id);
|
|
2155
|
+
getDimension() {
|
|
2156
|
+
return EMBEDDING_DIMENSION;
|
|
1260
2157
|
}
|
|
1261
|
-
|
|
1262
|
-
return
|
|
2158
|
+
getModelName() {
|
|
2159
|
+
return this.config.model;
|
|
1263
2160
|
}
|
|
1264
|
-
|
|
1265
|
-
|
|
2161
|
+
async dispose() {
|
|
2162
|
+
this.pipeline = null;
|
|
1266
2163
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
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));
|
|
1275
|
-
for (const [filepath, summary] of this.fileSummaries) {
|
|
1276
|
-
const summaryPath = this.getFileSummaryPath(filepath);
|
|
1277
|
-
await fs3.mkdir(path4.dirname(summaryPath), { recursive: true });
|
|
1278
|
-
await fs3.writeFile(summaryPath, JSON.stringify(summary, null, 2));
|
|
1279
|
-
}
|
|
2164
|
+
}
|
|
2165
|
+
function configureEmbeddings(config) {
|
|
2166
|
+
const newConfig = { ...globalConfig, ...config };
|
|
2167
|
+
if (newConfig.model !== globalConfig.model) {
|
|
2168
|
+
globalProvider = null;
|
|
1280
2169
|
}
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
2170
|
+
globalConfig = newConfig;
|
|
2171
|
+
}
|
|
2172
|
+
function getEmbeddingConfig() {
|
|
2173
|
+
return { ...globalConfig };
|
|
2174
|
+
}
|
|
2175
|
+
async function ensureGlobalProvider() {
|
|
2176
|
+
if (!globalProvider) {
|
|
2177
|
+
globalProvider = new TransformersEmbeddingProvider(globalConfig);
|
|
2178
|
+
await globalProvider.initialize();
|
|
1288
2179
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
2180
|
+
return globalProvider;
|
|
2181
|
+
}
|
|
2182
|
+
async function getEmbedding(text) {
|
|
2183
|
+
const provider = await ensureGlobalProvider();
|
|
2184
|
+
return provider.getEmbedding(text);
|
|
2185
|
+
}
|
|
2186
|
+
async function getEmbeddings(texts) {
|
|
2187
|
+
const provider = await ensureGlobalProvider();
|
|
2188
|
+
return provider.getEmbeddings(texts);
|
|
2189
|
+
}
|
|
2190
|
+
var CACHE_DIR, EMBEDDING_MODELS2, EMBEDDING_DIMENSION = 384, BATCH_SIZE = 32, globalProvider = null, globalConfig;
|
|
2191
|
+
var init_transformersEmbedding = __esm(() => {
|
|
2192
|
+
CACHE_DIR = path8.join(os2.homedir(), ".cache", "raggrep", "models");
|
|
2193
|
+
env.cacheDir = CACHE_DIR;
|
|
2194
|
+
env.allowLocalModels = true;
|
|
2195
|
+
EMBEDDING_MODELS2 = {
|
|
2196
|
+
"all-MiniLM-L6-v2": "Xenova/all-MiniLM-L6-v2",
|
|
2197
|
+
"all-MiniLM-L12-v2": "Xenova/all-MiniLM-L12-v2",
|
|
2198
|
+
"bge-small-en-v1.5": "Xenova/bge-small-en-v1.5",
|
|
2199
|
+
"paraphrase-MiniLM-L3-v2": "Xenova/paraphrase-MiniLM-L3-v2"
|
|
2200
|
+
};
|
|
2201
|
+
globalConfig = {
|
|
2202
|
+
model: "all-MiniLM-L6-v2",
|
|
2203
|
+
showProgress: true
|
|
2204
|
+
};
|
|
2205
|
+
});
|
|
2206
|
+
|
|
2207
|
+
// src/infrastructure/embeddings/index.ts
|
|
2208
|
+
var init_embeddings = __esm(() => {
|
|
2209
|
+
init_transformersEmbedding();
|
|
2210
|
+
});
|
|
2211
|
+
|
|
2212
|
+
// src/domain/services/similarity.ts
|
|
2213
|
+
function cosineSimilarity(a, b) {
|
|
2214
|
+
if (a.length !== b.length) {
|
|
2215
|
+
throw new Error(`Vector length mismatch: ${a.length} vs ${b.length}`);
|
|
1307
2216
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
2217
|
+
let dotProduct = 0;
|
|
2218
|
+
let normA = 0;
|
|
2219
|
+
let normB = 0;
|
|
2220
|
+
for (let i = 0;i < a.length; i++) {
|
|
2221
|
+
dotProduct += a[i] * b[i];
|
|
2222
|
+
normA += a[i] * a[i];
|
|
2223
|
+
normB += b[i] * b[i];
|
|
2224
|
+
}
|
|
2225
|
+
const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
|
|
2226
|
+
if (magnitude === 0)
|
|
2227
|
+
return 0;
|
|
2228
|
+
return dotProduct / magnitude;
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
// src/modules/language/typescript/parseCode.ts
|
|
2232
|
+
import * as ts from "typescript";
|
|
2233
|
+
function parseCode(content, filepath) {
|
|
2234
|
+
const ext = filepath.split(".").pop()?.toLowerCase();
|
|
2235
|
+
if (["ts", "tsx", "js", "jsx", "mts", "cts", "mjs", "cjs"].includes(ext || "")) {
|
|
2236
|
+
return parseTypeScript(content, filepath);
|
|
2237
|
+
}
|
|
2238
|
+
return parseGenericCode(content);
|
|
2239
|
+
}
|
|
2240
|
+
function parseTypeScript(content, filepath) {
|
|
2241
|
+
const chunks = [];
|
|
2242
|
+
const lines = content.split(`
|
|
2243
|
+
`);
|
|
2244
|
+
const sourceFile = ts.createSourceFile(filepath, content, ts.ScriptTarget.Latest, true, filepath.endsWith(".tsx") || filepath.endsWith(".jsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
|
|
2245
|
+
function getLineNumbers(node) {
|
|
2246
|
+
const start = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
2247
|
+
const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
|
|
2248
|
+
return {
|
|
2249
|
+
startLine: start.line + 1,
|
|
2250
|
+
endLine: end.line + 1
|
|
2251
|
+
};
|
|
1311
2252
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
await fs3.unlink(this.getFileSummaryPath(filepath));
|
|
1315
|
-
} catch {}
|
|
1316
|
-
this.fileSummaries.delete(filepath);
|
|
2253
|
+
function getNodeText(node) {
|
|
2254
|
+
return node.getText(sourceFile);
|
|
1317
2255
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
const metaPath = path4.join(this.symbolicPath, "_meta.json");
|
|
1321
|
-
await fs3.access(metaPath);
|
|
1322
|
-
return true;
|
|
1323
|
-
} catch {
|
|
2256
|
+
function isExported(node) {
|
|
2257
|
+
if (!ts.canHaveModifiers(node))
|
|
1324
2258
|
return false;
|
|
1325
|
-
|
|
2259
|
+
const modifiers = ts.getModifiers(node);
|
|
2260
|
+
return modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
1326
2261
|
}
|
|
1327
|
-
|
|
1328
|
-
|
|
2262
|
+
function getJSDoc(node) {
|
|
2263
|
+
const jsDocNodes = ts.getJSDocCommentsAndTags(node);
|
|
2264
|
+
if (jsDocNodes.length === 0)
|
|
2265
|
+
return;
|
|
2266
|
+
return jsDocNodes.map((doc) => doc.getText(sourceFile)).join(`
|
|
2267
|
+
`);
|
|
1329
2268
|
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
this.meta.fileCount = 0;
|
|
1334
|
-
this.meta.bm25Data = {
|
|
1335
|
-
avgDocLength: 0,
|
|
1336
|
-
documentFrequencies: {},
|
|
1337
|
-
totalDocs: 0
|
|
1338
|
-
};
|
|
2269
|
+
function getFunctionName(node) {
|
|
2270
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
2271
|
+
return node.name.text;
|
|
1339
2272
|
}
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
}
|
|
1343
|
-
var init_symbolicIndex = __esm(() => {
|
|
1344
|
-
init_keywords();
|
|
1345
|
-
});
|
|
1346
|
-
|
|
1347
|
-
// src/infrastructure/storage/index.ts
|
|
1348
|
-
var init_storage = __esm(() => {
|
|
1349
|
-
init_fileIndexStorage();
|
|
1350
|
-
init_symbolicIndex();
|
|
1351
|
-
});
|
|
1352
|
-
|
|
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";
|
|
1366
|
-
version = "1.0.0";
|
|
1367
|
-
embeddingConfig = null;
|
|
1368
|
-
symbolicIndex = null;
|
|
1369
|
-
pendingSummaries = new Map;
|
|
1370
|
-
rootDir = "";
|
|
1371
|
-
async initialize(config) {
|
|
1372
|
-
this.embeddingConfig = getEmbeddingConfigFromModule(config);
|
|
1373
|
-
configureEmbeddings(this.embeddingConfig);
|
|
1374
|
-
this.pendingSummaries.clear();
|
|
1375
|
-
}
|
|
1376
|
-
async indexFile(filepath, content, ctx) {
|
|
1377
|
-
this.rootDir = ctx.rootDir;
|
|
1378
|
-
const parsedChunks = parseCode(content, filepath);
|
|
1379
|
-
if (parsedChunks.length === 0) {
|
|
1380
|
-
return null;
|
|
2273
|
+
if (ts.isMethodDeclaration(node) && ts.isIdentifier(node.name)) {
|
|
2274
|
+
return node.name.text;
|
|
1381
2275
|
}
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
const chunkContents = parsedChunks.map((c) => {
|
|
1385
|
-
const namePrefix = c.name ? `${c.name}: ` : "";
|
|
1386
|
-
return `${pathPrefix} ${namePrefix}${c.content}`;
|
|
1387
|
-
});
|
|
1388
|
-
const embeddings = await getEmbeddings(chunkContents);
|
|
1389
|
-
const chunks = parsedChunks.map((pc) => ({
|
|
1390
|
-
id: generateChunkId(filepath, pc.startLine, pc.endLine),
|
|
1391
|
-
content: pc.content,
|
|
1392
|
-
startLine: pc.startLine,
|
|
1393
|
-
endLine: pc.endLine,
|
|
1394
|
-
type: pc.type,
|
|
1395
|
-
name: pc.name,
|
|
1396
|
-
isExported: pc.isExported,
|
|
1397
|
-
jsDoc: pc.jsDoc
|
|
1398
|
-
}));
|
|
1399
|
-
const references = this.extractReferences(content, filepath);
|
|
1400
|
-
const stats = await ctx.getFileStats(filepath);
|
|
1401
|
-
const currentConfig = getEmbeddingConfig();
|
|
1402
|
-
const moduleData = {
|
|
1403
|
-
embeddings,
|
|
1404
|
-
embeddingModel: currentConfig.model
|
|
1405
|
-
};
|
|
1406
|
-
const chunkTypes = [...new Set(parsedChunks.map((pc) => pc.type))];
|
|
1407
|
-
const exports = parsedChunks.filter((pc) => pc.isExported && pc.name).map((pc) => pc.name);
|
|
1408
|
-
const allKeywords = new Set;
|
|
1409
|
-
for (const pc of parsedChunks) {
|
|
1410
|
-
const keywords = extractKeywords(pc.content, pc.name);
|
|
1411
|
-
keywords.forEach((k) => allKeywords.add(k));
|
|
2276
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {
|
|
2277
|
+
return node.name.text;
|
|
1412
2278
|
}
|
|
1413
|
-
|
|
1414
|
-
const fileSummary = {
|
|
1415
|
-
filepath,
|
|
1416
|
-
chunkCount: chunks.length,
|
|
1417
|
-
chunkTypes,
|
|
1418
|
-
keywords: Array.from(allKeywords),
|
|
1419
|
-
exports,
|
|
1420
|
-
lastModified: stats.lastModified,
|
|
1421
|
-
pathContext: {
|
|
1422
|
-
segments: pathContext.segments,
|
|
1423
|
-
layer: pathContext.layer,
|
|
1424
|
-
domain: pathContext.domain,
|
|
1425
|
-
depth: pathContext.depth
|
|
1426
|
-
}
|
|
1427
|
-
};
|
|
1428
|
-
this.pendingSummaries.set(filepath, fileSummary);
|
|
1429
|
-
return {
|
|
1430
|
-
filepath,
|
|
1431
|
-
lastModified: stats.lastModified,
|
|
1432
|
-
chunks,
|
|
1433
|
-
moduleData,
|
|
1434
|
-
references
|
|
1435
|
-
};
|
|
2279
|
+
return;
|
|
1436
2280
|
}
|
|
1437
|
-
|
|
1438
|
-
const
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
2281
|
+
function visit(node) {
|
|
2282
|
+
const { startLine, endLine } = getLineNumbers(node);
|
|
2283
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
2284
|
+
chunks.push({
|
|
2285
|
+
content: getNodeText(node),
|
|
2286
|
+
startLine,
|
|
2287
|
+
endLine,
|
|
2288
|
+
type: "function",
|
|
2289
|
+
name: node.name.text,
|
|
2290
|
+
isExported: isExported(node),
|
|
2291
|
+
jsDoc: getJSDoc(node)
|
|
2292
|
+
});
|
|
2293
|
+
return;
|
|
1443
2294
|
}
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
candidateFiles = symbolicIndex.getAllFiles();
|
|
2295
|
+
if (ts.isVariableStatement(node)) {
|
|
2296
|
+
for (const decl of node.declarationList.declarations) {
|
|
2297
|
+
if (decl.initializer && (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer))) {
|
|
2298
|
+
const name = ts.isIdentifier(decl.name) ? decl.name.text : undefined;
|
|
2299
|
+
chunks.push({
|
|
2300
|
+
content: getNodeText(node),
|
|
2301
|
+
startLine,
|
|
2302
|
+
endLine,
|
|
2303
|
+
type: "function",
|
|
2304
|
+
name,
|
|
2305
|
+
isExported: isExported(node),
|
|
2306
|
+
jsDoc: getJSDoc(node)
|
|
2307
|
+
});
|
|
2308
|
+
return;
|
|
2309
|
+
}
|
|
1460
2310
|
}
|
|
1461
|
-
} catch {
|
|
1462
|
-
candidateFiles = await ctx.listIndexedFiles();
|
|
1463
2311
|
}
|
|
1464
|
-
if (
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
2312
|
+
if (ts.isClassDeclaration(node) && node.name) {
|
|
2313
|
+
chunks.push({
|
|
2314
|
+
content: getNodeText(node),
|
|
2315
|
+
startLine,
|
|
2316
|
+
endLine,
|
|
2317
|
+
type: "class",
|
|
2318
|
+
name: node.name.text,
|
|
2319
|
+
isExported: isExported(node),
|
|
2320
|
+
jsDoc: getJSDoc(node)
|
|
1473
2321
|
});
|
|
2322
|
+
return;
|
|
1474
2323
|
}
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
}
|
|
2324
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
2325
|
+
chunks.push({
|
|
2326
|
+
content: getNodeText(node),
|
|
2327
|
+
startLine,
|
|
2328
|
+
endLine,
|
|
2329
|
+
type: "interface",
|
|
2330
|
+
name: node.name.text,
|
|
2331
|
+
isExported: isExported(node),
|
|
2332
|
+
jsDoc: getJSDoc(node)
|
|
2333
|
+
});
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
if (ts.isTypeAliasDeclaration(node)) {
|
|
2337
|
+
chunks.push({
|
|
2338
|
+
content: getNodeText(node),
|
|
2339
|
+
startLine,
|
|
2340
|
+
endLine,
|
|
2341
|
+
type: "type",
|
|
2342
|
+
name: node.name.text,
|
|
2343
|
+
isExported: isExported(node),
|
|
2344
|
+
jsDoc: getJSDoc(node)
|
|
2345
|
+
});
|
|
2346
|
+
return;
|
|
1497
2347
|
}
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
2348
|
+
if (ts.isEnumDeclaration(node)) {
|
|
2349
|
+
chunks.push({
|
|
2350
|
+
content: getNodeText(node),
|
|
2351
|
+
startLine,
|
|
2352
|
+
endLine,
|
|
2353
|
+
type: "enum",
|
|
2354
|
+
name: node.name.text,
|
|
2355
|
+
isExported: isExported(node),
|
|
2356
|
+
jsDoc: getJSDoc(node)
|
|
2357
|
+
});
|
|
2358
|
+
return;
|
|
1502
2359
|
}
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
if (summary?.pathContext) {
|
|
1508
|
-
let boost = 0;
|
|
1509
|
-
const ctx2 = summary.pathContext;
|
|
1510
|
-
if (ctx2.domain && queryTerms.some((t) => ctx2.domain.includes(t) || t.includes(ctx2.domain))) {
|
|
1511
|
-
boost += 0.1;
|
|
1512
|
-
}
|
|
1513
|
-
if (ctx2.layer && queryTerms.some((t) => ctx2.layer.includes(t) || t.includes(ctx2.layer))) {
|
|
1514
|
-
boost += 0.05;
|
|
1515
|
-
}
|
|
1516
|
-
const segmentMatch = ctx2.segments.some((seg) => queryTerms.some((t) => seg.toLowerCase().includes(t) || t.includes(seg.toLowerCase())));
|
|
1517
|
-
if (segmentMatch) {
|
|
1518
|
-
boost += 0.05;
|
|
2360
|
+
if (ts.isVariableStatement(node) && isExported(node)) {
|
|
2361
|
+
for (const decl of node.declarationList.declarations) {
|
|
2362
|
+
if (decl.initializer && (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer))) {
|
|
2363
|
+
continue;
|
|
1519
2364
|
}
|
|
1520
|
-
|
|
2365
|
+
const name = ts.isIdentifier(decl.name) ? decl.name.text : undefined;
|
|
2366
|
+
chunks.push({
|
|
2367
|
+
content: getNodeText(node),
|
|
2368
|
+
startLine,
|
|
2369
|
+
endLine,
|
|
2370
|
+
type: "variable",
|
|
2371
|
+
name,
|
|
2372
|
+
isExported: true,
|
|
2373
|
+
jsDoc: getJSDoc(node)
|
|
2374
|
+
});
|
|
1521
2375
|
}
|
|
2376
|
+
return;
|
|
1522
2377
|
}
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
2378
|
+
ts.forEachChild(node, visit);
|
|
2379
|
+
}
|
|
2380
|
+
ts.forEachChild(sourceFile, visit);
|
|
2381
|
+
if (chunks.length === 0) {
|
|
2382
|
+
return parseGenericCode(content);
|
|
2383
|
+
}
|
|
2384
|
+
return chunks;
|
|
2385
|
+
}
|
|
2386
|
+
function parseGenericCode(content) {
|
|
2387
|
+
const chunks = [];
|
|
2388
|
+
const lines = content.split(`
|
|
2389
|
+
`);
|
|
2390
|
+
const CHUNK_SIZE = 30;
|
|
2391
|
+
const OVERLAP = 5;
|
|
2392
|
+
if (lines.length <= CHUNK_SIZE) {
|
|
2393
|
+
return [
|
|
2394
|
+
{
|
|
2395
|
+
content,
|
|
2396
|
+
startLine: 1,
|
|
2397
|
+
endLine: lines.length,
|
|
2398
|
+
type: "file"
|
|
1541
2399
|
}
|
|
2400
|
+
];
|
|
2401
|
+
}
|
|
2402
|
+
for (let i = 0;i < lines.length; i += CHUNK_SIZE - OVERLAP) {
|
|
2403
|
+
const endIdx = Math.min(i + CHUNK_SIZE, lines.length);
|
|
2404
|
+
chunks.push({
|
|
2405
|
+
content: lines.slice(i, endIdx).join(`
|
|
2406
|
+
`),
|
|
2407
|
+
startLine: i + 1,
|
|
2408
|
+
endLine: endIdx,
|
|
2409
|
+
type: "block"
|
|
2410
|
+
});
|
|
2411
|
+
if (endIdx >= lines.length)
|
|
2412
|
+
break;
|
|
2413
|
+
}
|
|
2414
|
+
return chunks;
|
|
2415
|
+
}
|
|
2416
|
+
function generateChunkId(filepath, startLine, endLine) {
|
|
2417
|
+
const safePath = filepath.replace(/[/\\]/g, "-").replace(/\./g, "_");
|
|
2418
|
+
return `${safePath}-${startLine}-${endLine}`;
|
|
2419
|
+
}
|
|
2420
|
+
var init_parseCode = () => {};
|
|
2421
|
+
|
|
2422
|
+
// src/infrastructure/storage/fileIndexStorage.ts
|
|
2423
|
+
var init_fileIndexStorage = __esm(() => {
|
|
2424
|
+
init_entities();
|
|
2425
|
+
});
|
|
2426
|
+
|
|
2427
|
+
// src/domain/services/keywords.ts
|
|
2428
|
+
function extractKeywords(content, name, maxKeywords = 50) {
|
|
2429
|
+
const keywords = new Set;
|
|
2430
|
+
if (name) {
|
|
2431
|
+
keywords.add(name.toLowerCase());
|
|
2432
|
+
const parts = name.split(/(?=[A-Z])/).map((p) => p.toLowerCase());
|
|
2433
|
+
parts.forEach((p) => p.length > 2 && keywords.add(p));
|
|
2434
|
+
}
|
|
2435
|
+
const identifierRegex = /\b([a-zA-Z_][a-zA-Z0-9_]{2,})\b/g;
|
|
2436
|
+
let match;
|
|
2437
|
+
while ((match = identifierRegex.exec(content)) !== null) {
|
|
2438
|
+
const word = match[1].toLowerCase();
|
|
2439
|
+
if (!COMMON_KEYWORDS.has(word) && word.length > 2) {
|
|
2440
|
+
keywords.add(word);
|
|
1542
2441
|
}
|
|
1543
|
-
results.sort((a, b) => b.score - a.score);
|
|
1544
|
-
return results.slice(0, topK);
|
|
1545
2442
|
}
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
2443
|
+
return Array.from(keywords).slice(0, maxKeywords);
|
|
2444
|
+
}
|
|
2445
|
+
function splitIdentifier(str) {
|
|
2446
|
+
return str.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").split(/\s+/).map((s) => s.toLowerCase()).filter((s) => s.length > 1);
|
|
2447
|
+
}
|
|
2448
|
+
function extractPathKeywords(filepath) {
|
|
2449
|
+
const keywords = new Set;
|
|
2450
|
+
const pathWithoutExt = filepath.replace(/\.[^.]+$/, "");
|
|
2451
|
+
const segments = pathWithoutExt.split(/[/\\]/);
|
|
2452
|
+
for (const segment of segments) {
|
|
2453
|
+
if (segment.length < 2)
|
|
2454
|
+
continue;
|
|
2455
|
+
const lower = segment.toLowerCase();
|
|
2456
|
+
if (!COMMON_KEYWORDS.has(lower) && lower.length > 2) {
|
|
2457
|
+
keywords.add(lower);
|
|
2458
|
+
}
|
|
2459
|
+
const parts = splitIdentifier(segment);
|
|
2460
|
+
for (const part of parts) {
|
|
2461
|
+
if (!COMMON_KEYWORDS.has(part) && part.length > 2) {
|
|
2462
|
+
keywords.add(part);
|
|
1557
2463
|
}
|
|
1558
2464
|
}
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
2465
|
+
}
|
|
2466
|
+
return Array.from(keywords);
|
|
2467
|
+
}
|
|
2468
|
+
function parsePathContext(filepath) {
|
|
2469
|
+
const pathWithoutExt = filepath.replace(/\.[^.]+$/, "");
|
|
2470
|
+
const allSegments = pathWithoutExt.split(/[/\\]/);
|
|
2471
|
+
const filename = allSegments[allSegments.length - 1];
|
|
2472
|
+
const dirSegments = allSegments.slice(0, -1);
|
|
2473
|
+
const keywords = extractPathKeywords(filepath);
|
|
2474
|
+
let layer;
|
|
2475
|
+
const allLower = [...dirSegments, filename].map((s) => s.toLowerCase()).join(" ");
|
|
2476
|
+
const filenameLower = filename.toLowerCase();
|
|
2477
|
+
for (const [layerName, patterns] of Object.entries(LAYER_PATTERNS2)) {
|
|
2478
|
+
for (const pattern of patterns) {
|
|
2479
|
+
if (filenameLower.includes(pattern)) {
|
|
2480
|
+
layer = layerName;
|
|
2481
|
+
break;
|
|
2482
|
+
}
|
|
2483
|
+
if (dirSegments.some((s) => s.toLowerCase() === pattern)) {
|
|
2484
|
+
layer = layerName;
|
|
2485
|
+
break;
|
|
1565
2486
|
}
|
|
1566
2487
|
}
|
|
1567
|
-
|
|
2488
|
+
if (layer)
|
|
2489
|
+
break;
|
|
2490
|
+
}
|
|
2491
|
+
let domain;
|
|
2492
|
+
const layerPatternSet = new Set(Object.values(LAYER_PATTERNS2).flat());
|
|
2493
|
+
const reversedSegments = [...dirSegments].reverse();
|
|
2494
|
+
for (const segment of reversedSegments) {
|
|
2495
|
+
const lower = segment.toLowerCase();
|
|
2496
|
+
if (["src", "lib", "app", "packages", "modules"].includes(lower))
|
|
2497
|
+
continue;
|
|
2498
|
+
if (layerPatternSet.has(lower))
|
|
2499
|
+
continue;
|
|
2500
|
+
if (lower.length > 2) {
|
|
2501
|
+
domain = lower;
|
|
2502
|
+
break;
|
|
2503
|
+
}
|
|
1568
2504
|
}
|
|
2505
|
+
return {
|
|
2506
|
+
segments: dirSegments,
|
|
2507
|
+
layer,
|
|
2508
|
+
domain,
|
|
2509
|
+
depth: dirSegments.length,
|
|
2510
|
+
keywords
|
|
2511
|
+
};
|
|
1569
2512
|
}
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
2513
|
+
function formatPathContextForEmbedding(pathContext) {
|
|
2514
|
+
const parts = [];
|
|
2515
|
+
if (pathContext.domain) {
|
|
2516
|
+
parts.push(pathContext.domain);
|
|
2517
|
+
}
|
|
2518
|
+
if (pathContext.layer) {
|
|
2519
|
+
parts.push(pathContext.layer);
|
|
2520
|
+
}
|
|
2521
|
+
const significantSegments = pathContext.segments.slice(-3).filter((s) => s.length > 2 && !["src", "lib", "app"].includes(s.toLowerCase()));
|
|
2522
|
+
if (significantSegments.length > 0) {
|
|
2523
|
+
parts.push(...significantSegments.map((s) => s.toLowerCase()));
|
|
2524
|
+
}
|
|
2525
|
+
if (parts.length === 0)
|
|
2526
|
+
return "";
|
|
2527
|
+
const unique = [...new Set(parts)];
|
|
2528
|
+
return `[${unique.join(" ")}]`;
|
|
2529
|
+
}
|
|
2530
|
+
var COMMON_KEYWORDS, LAYER_PATTERNS2;
|
|
2531
|
+
var init_keywords = __esm(() => {
|
|
2532
|
+
COMMON_KEYWORDS = new Set([
|
|
2533
|
+
"const",
|
|
2534
|
+
"let",
|
|
2535
|
+
"var",
|
|
2536
|
+
"function",
|
|
2537
|
+
"class",
|
|
2538
|
+
"interface",
|
|
2539
|
+
"type",
|
|
2540
|
+
"enum",
|
|
2541
|
+
"export",
|
|
2542
|
+
"import",
|
|
2543
|
+
"from",
|
|
2544
|
+
"return",
|
|
2545
|
+
"async",
|
|
2546
|
+
"await",
|
|
2547
|
+
"new",
|
|
2548
|
+
"this",
|
|
2549
|
+
"true",
|
|
2550
|
+
"false",
|
|
2551
|
+
"null",
|
|
2552
|
+
"undefined",
|
|
2553
|
+
"if",
|
|
2554
|
+
"else",
|
|
2555
|
+
"for",
|
|
2556
|
+
"while",
|
|
2557
|
+
"switch",
|
|
2558
|
+
"case",
|
|
2559
|
+
"break",
|
|
2560
|
+
"continue",
|
|
2561
|
+
"try",
|
|
2562
|
+
"catch",
|
|
2563
|
+
"finally",
|
|
2564
|
+
"throw",
|
|
2565
|
+
"typeof",
|
|
2566
|
+
"instanceof",
|
|
2567
|
+
"void",
|
|
2568
|
+
"delete",
|
|
2569
|
+
"in",
|
|
2570
|
+
"of",
|
|
2571
|
+
"string",
|
|
2572
|
+
"number",
|
|
2573
|
+
"boolean",
|
|
2574
|
+
"any",
|
|
2575
|
+
"unknown",
|
|
2576
|
+
"never",
|
|
2577
|
+
"object",
|
|
2578
|
+
"public",
|
|
2579
|
+
"private",
|
|
2580
|
+
"protected",
|
|
2581
|
+
"static",
|
|
2582
|
+
"readonly",
|
|
2583
|
+
"abstract",
|
|
2584
|
+
"implements",
|
|
2585
|
+
"extends",
|
|
2586
|
+
"super",
|
|
2587
|
+
"get",
|
|
2588
|
+
"set",
|
|
2589
|
+
"constructor",
|
|
2590
|
+
"the",
|
|
2591
|
+
"and",
|
|
2592
|
+
"for",
|
|
2593
|
+
"not",
|
|
2594
|
+
"with",
|
|
2595
|
+
"are",
|
|
2596
|
+
"was",
|
|
2597
|
+
"has",
|
|
2598
|
+
"have"
|
|
2599
|
+
]);
|
|
2600
|
+
LAYER_PATTERNS2 = {
|
|
2601
|
+
controller: ["controller", "controllers", "handler", "handlers", "route", "routes", "api"],
|
|
2602
|
+
service: ["service", "services", "usecase", "usecases", "application"],
|
|
2603
|
+
repository: ["repository", "repositories", "repo", "repos", "dao", "store", "storage"],
|
|
2604
|
+
model: ["model", "models", "entity", "entities", "schema", "schemas"],
|
|
2605
|
+
util: ["util", "utils", "utility", "utilities", "helper", "helpers", "common", "shared"],
|
|
2606
|
+
config: ["config", "configs", "configuration", "settings"],
|
|
2607
|
+
middleware: ["middleware", "middlewares", "interceptor", "interceptors"],
|
|
2608
|
+
domain: ["domain", "core", "business"],
|
|
2609
|
+
infrastructure: ["infrastructure", "infra", "external", "adapters"],
|
|
2610
|
+
presentation: ["presentation", "view", "views", "component", "components", "ui"],
|
|
2611
|
+
test: ["test", "tests", "spec", "specs", "__tests__", "__test__"]
|
|
2612
|
+
};
|
|
1578
2613
|
});
|
|
1579
2614
|
|
|
1580
|
-
// src/
|
|
1581
|
-
|
|
1582
|
-
import { glob } from "glob";
|
|
1583
|
-
import * as fs6 from "fs/promises";
|
|
2615
|
+
// src/infrastructure/storage/symbolicIndex.ts
|
|
2616
|
+
import * as fs5 from "fs/promises";
|
|
1584
2617
|
import * as path9 from "path";
|
|
1585
2618
|
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
this.
|
|
1594
|
-
|
|
1595
|
-
get(id) {
|
|
1596
|
-
return this.modules.get(id);
|
|
2619
|
+
class SymbolicIndex {
|
|
2620
|
+
meta = null;
|
|
2621
|
+
fileSummaries = new Map;
|
|
2622
|
+
bm25Index = null;
|
|
2623
|
+
symbolicPath;
|
|
2624
|
+
moduleId;
|
|
2625
|
+
constructor(indexDir, moduleId) {
|
|
2626
|
+
this.symbolicPath = path9.join(indexDir, "index", moduleId, "symbolic");
|
|
2627
|
+
this.moduleId = moduleId;
|
|
1597
2628
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
2629
|
+
async initialize() {
|
|
2630
|
+
try {
|
|
2631
|
+
await this.load();
|
|
2632
|
+
} catch {
|
|
2633
|
+
this.meta = {
|
|
2634
|
+
version: "1.0.0",
|
|
2635
|
+
lastUpdated: new Date().toISOString(),
|
|
2636
|
+
moduleId: this.moduleId,
|
|
2637
|
+
fileCount: 0,
|
|
2638
|
+
bm25Data: {
|
|
2639
|
+
avgDocLength: 0,
|
|
2640
|
+
documentFrequencies: {},
|
|
2641
|
+
totalDocs: 0
|
|
2642
|
+
}
|
|
2643
|
+
};
|
|
2644
|
+
this.bm25Index = new BM25Index;
|
|
2645
|
+
}
|
|
1600
2646
|
}
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
return this.list().filter((m) => enabledIds.has(m.id));
|
|
2647
|
+
addFile(summary) {
|
|
2648
|
+
this.fileSummaries.set(summary.filepath, summary);
|
|
1604
2649
|
}
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
async function registerBuiltInModules() {
|
|
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
|
-
}
|
|
2650
|
+
removeFile(filepath) {
|
|
2651
|
+
return this.fileSummaries.delete(filepath);
|
|
1686
2652
|
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
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);
|
|
2653
|
+
buildBM25Index() {
|
|
2654
|
+
this.bm25Index = new BM25Index;
|
|
2655
|
+
for (const [filepath, summary] of this.fileSummaries) {
|
|
2656
|
+
const content = [
|
|
2657
|
+
...summary.keywords,
|
|
2658
|
+
...summary.exports,
|
|
2659
|
+
...extractPathKeywords(filepath)
|
|
2660
|
+
].join(" ");
|
|
2661
|
+
this.bm25Index.addDocuments([{ id: filepath, content }]);
|
|
1711
2662
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
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";
|
|
2663
|
+
if (this.meta) {
|
|
2664
|
+
this.meta.fileCount = this.fileSummaries.size;
|
|
2665
|
+
this.meta.bm25Data.totalDocs = this.fileSummaries.size;
|
|
1729
2666
|
}
|
|
1730
|
-
const hasWorkspaces = Boolean(pkg.workspaces);
|
|
1731
|
-
return { name, relativePath, type, hasWorkspaces };
|
|
1732
|
-
} catch {
|
|
1733
|
-
return null;
|
|
1734
2667
|
}
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
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
|
-
}
|
|
2668
|
+
findCandidates(query, maxCandidates = 20) {
|
|
2669
|
+
if (!this.bm25Index) {
|
|
2670
|
+
return Array.from(this.fileSummaries.keys());
|
|
1765
2671
|
}
|
|
1766
|
-
const
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
2672
|
+
const results = this.bm25Index.search(query, maxCandidates);
|
|
2673
|
+
return results.map((r) => r.id);
|
|
2674
|
+
}
|
|
2675
|
+
getAllFiles() {
|
|
2676
|
+
return Array.from(this.fileSummaries.keys());
|
|
2677
|
+
}
|
|
2678
|
+
getFileSummary(filepath) {
|
|
2679
|
+
return this.fileSummaries.get(filepath);
|
|
2680
|
+
}
|
|
2681
|
+
async save() {
|
|
2682
|
+
if (!this.meta)
|
|
2683
|
+
throw new Error("Index not initialized");
|
|
2684
|
+
this.meta.lastUpdated = new Date().toISOString();
|
|
2685
|
+
this.meta.fileCount = this.fileSummaries.size;
|
|
2686
|
+
await fs5.mkdir(this.symbolicPath, { recursive: true });
|
|
2687
|
+
const metaPath = path9.join(this.symbolicPath, "_meta.json");
|
|
2688
|
+
await fs5.writeFile(metaPath, JSON.stringify(this.meta, null, 2));
|
|
2689
|
+
for (const [filepath, summary] of this.fileSummaries) {
|
|
2690
|
+
const summaryPath = this.getFileSummaryPath(filepath);
|
|
2691
|
+
await fs5.mkdir(path9.dirname(summaryPath), { recursive: true });
|
|
2692
|
+
await fs5.writeFile(summaryPath, JSON.stringify(summary, null, 2));
|
|
1779
2693
|
}
|
|
1780
|
-
|
|
2694
|
+
}
|
|
2695
|
+
async load() {
|
|
2696
|
+
const metaPath = path9.join(this.symbolicPath, "_meta.json");
|
|
2697
|
+
const metaContent = await fs5.readFile(metaPath, "utf-8");
|
|
2698
|
+
this.meta = JSON.parse(metaContent);
|
|
2699
|
+
this.fileSummaries.clear();
|
|
2700
|
+
await this.loadFileSummariesRecursive(this.symbolicPath);
|
|
2701
|
+
this.buildBM25Index();
|
|
2702
|
+
}
|
|
2703
|
+
async loadFileSummariesRecursive(dir) {
|
|
1781
2704
|
try {
|
|
1782
|
-
const
|
|
1783
|
-
const
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
2705
|
+
const entries = await fs5.readdir(dir, { withFileTypes: true });
|
|
2706
|
+
for (const entry of entries) {
|
|
2707
|
+
const fullPath = path9.join(dir, entry.name);
|
|
2708
|
+
if (entry.isDirectory()) {
|
|
2709
|
+
await this.loadFileSummariesRecursive(fullPath);
|
|
2710
|
+
} else if (entry.name.endsWith(".json") && entry.name !== "_meta.json") {
|
|
2711
|
+
try {
|
|
2712
|
+
const content = await fs5.readFile(fullPath, "utf-8");
|
|
2713
|
+
const summary = JSON.parse(content);
|
|
2714
|
+
if (summary.filepath) {
|
|
2715
|
+
this.fileSummaries.set(summary.filepath, summary);
|
|
2716
|
+
}
|
|
2717
|
+
} catch {}
|
|
2718
|
+
}
|
|
1792
2719
|
}
|
|
1793
2720
|
} 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
2721
|
}
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
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";
|
|
2722
|
+
getFileSummaryPath(filepath) {
|
|
2723
|
+
const jsonPath = filepath.replace(/\.[^.]+$/, ".json");
|
|
2724
|
+
return path9.join(this.symbolicPath, jsonPath);
|
|
1822
2725
|
}
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
2726
|
+
async deleteFileSummary(filepath) {
|
|
2727
|
+
try {
|
|
2728
|
+
await fs5.unlink(this.getFileSummaryPath(filepath));
|
|
2729
|
+
} catch {}
|
|
2730
|
+
this.fileSummaries.delete(filepath);
|
|
2731
|
+
}
|
|
2732
|
+
async exists() {
|
|
2733
|
+
try {
|
|
2734
|
+
const metaPath = path9.join(this.symbolicPath, "_meta.json");
|
|
2735
|
+
await fs5.access(metaPath);
|
|
2736
|
+
return true;
|
|
2737
|
+
} catch {
|
|
2738
|
+
return false;
|
|
1830
2739
|
}
|
|
1831
2740
|
}
|
|
1832
|
-
|
|
1833
|
-
return
|
|
2741
|
+
get size() {
|
|
2742
|
+
return this.fileSummaries.size;
|
|
1834
2743
|
}
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
if (
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
2744
|
+
clear() {
|
|
2745
|
+
this.fileSummaries.clear();
|
|
2746
|
+
if (this.meta) {
|
|
2747
|
+
this.meta.fileCount = 0;
|
|
2748
|
+
this.meta.bm25Data = {
|
|
2749
|
+
avgDocLength: 0,
|
|
2750
|
+
documentFrequencies: {},
|
|
2751
|
+
totalDocs: 0
|
|
1842
2752
|
};
|
|
1843
2753
|
}
|
|
2754
|
+
this.bm25Index = new BM25Index;
|
|
1844
2755
|
}
|
|
1845
|
-
return {
|
|
1846
|
-
name: "root",
|
|
1847
|
-
root: "",
|
|
1848
|
-
type: structure.rootType ?? "unknown"
|
|
1849
|
-
};
|
|
1850
2756
|
}
|
|
2757
|
+
var init_symbolicIndex = __esm(() => {
|
|
2758
|
+
init_keywords();
|
|
2759
|
+
});
|
|
1851
2760
|
|
|
1852
|
-
// src/
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
"
|
|
1870
|
-
"
|
|
1871
|
-
"
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
"
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
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);
|
|
2761
|
+
// src/infrastructure/storage/index.ts
|
|
2762
|
+
var init_storage = __esm(() => {
|
|
2763
|
+
init_fileIndexStorage();
|
|
2764
|
+
init_symbolicIndex();
|
|
2765
|
+
});
|
|
2766
|
+
|
|
2767
|
+
// src/modules/language/typescript/index.ts
|
|
2768
|
+
var exports_typescript = {};
|
|
2769
|
+
__export(exports_typescript, {
|
|
2770
|
+
TypeScriptModule: () => TypeScriptModule,
|
|
2771
|
+
DEFAULT_TOP_K: () => DEFAULT_TOP_K2,
|
|
2772
|
+
DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE2
|
|
2773
|
+
});
|
|
2774
|
+
import * as path10 from "path";
|
|
2775
|
+
|
|
2776
|
+
class TypeScriptModule {
|
|
2777
|
+
id = "language/typescript";
|
|
2778
|
+
name = "TypeScript Search";
|
|
2779
|
+
description = "TypeScript-aware code search with AST parsing and semantic embeddings";
|
|
2780
|
+
version = "1.0.0";
|
|
2781
|
+
embeddingConfig = null;
|
|
2782
|
+
symbolicIndex = null;
|
|
2783
|
+
pendingSummaries = new Map;
|
|
2784
|
+
rootDir = "";
|
|
2785
|
+
async initialize(config) {
|
|
2786
|
+
this.embeddingConfig = getEmbeddingConfigFromModule(config);
|
|
2787
|
+
configureEmbeddings(this.embeddingConfig);
|
|
2788
|
+
this.pendingSummaries.clear();
|
|
1970
2789
|
}
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
}
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
2790
|
+
async indexFile(filepath, content, ctx) {
|
|
2791
|
+
this.rootDir = ctx.rootDir;
|
|
2792
|
+
const parsedChunks = parseCode(content, filepath);
|
|
2793
|
+
if (parsedChunks.length === 0) {
|
|
2794
|
+
return null;
|
|
2795
|
+
}
|
|
2796
|
+
const pathContext = parsePathContext(filepath);
|
|
2797
|
+
const pathPrefix = formatPathContextForEmbedding(pathContext);
|
|
2798
|
+
const chunkContents = parsedChunks.map((c) => {
|
|
2799
|
+
const namePrefix = c.name ? `${c.name}: ` : "";
|
|
2800
|
+
return `${pathPrefix} ${namePrefix}${c.content}`;
|
|
2801
|
+
});
|
|
2802
|
+
const embeddings = await getEmbeddings(chunkContents);
|
|
2803
|
+
const chunks = parsedChunks.map((pc) => ({
|
|
2804
|
+
id: generateChunkId(filepath, pc.startLine, pc.endLine),
|
|
2805
|
+
content: pc.content,
|
|
2806
|
+
startLine: pc.startLine,
|
|
2807
|
+
endLine: pc.endLine,
|
|
2808
|
+
type: pc.type,
|
|
2809
|
+
name: pc.name,
|
|
2810
|
+
isExported: pc.isExported,
|
|
2811
|
+
jsDoc: pc.jsDoc
|
|
2812
|
+
}));
|
|
2813
|
+
const references = this.extractReferences(content, filepath);
|
|
2814
|
+
const stats = await ctx.getFileStats(filepath);
|
|
2815
|
+
const currentConfig = getEmbeddingConfig();
|
|
2816
|
+
const moduleData = {
|
|
2817
|
+
embeddings,
|
|
2818
|
+
embeddingModel: currentConfig.model
|
|
2819
|
+
};
|
|
2820
|
+
const chunkTypes = [...new Set(parsedChunks.map((pc) => pc.type))];
|
|
2821
|
+
const exports = parsedChunks.filter((pc) => pc.isExported && pc.name).map((pc) => pc.name);
|
|
2822
|
+
const allKeywords = new Set;
|
|
2823
|
+
for (const pc of parsedChunks) {
|
|
2824
|
+
const keywords = extractKeywords(pc.content, pc.name);
|
|
2825
|
+
keywords.forEach((k) => allKeywords.add(k));
|
|
2826
|
+
}
|
|
2827
|
+
pathContext.keywords.forEach((k) => allKeywords.add(k));
|
|
2828
|
+
const fileSummary = {
|
|
2829
|
+
filepath,
|
|
2830
|
+
chunkCount: chunks.length,
|
|
2831
|
+
chunkTypes,
|
|
2832
|
+
keywords: Array.from(allKeywords),
|
|
2833
|
+
exports,
|
|
2834
|
+
lastModified: stats.lastModified,
|
|
2835
|
+
pathContext: {
|
|
2836
|
+
segments: pathContext.segments,
|
|
2837
|
+
layer: pathContext.layer,
|
|
2838
|
+
domain: pathContext.domain,
|
|
2839
|
+
depth: pathContext.depth
|
|
1989
2840
|
}
|
|
2841
|
+
};
|
|
2842
|
+
this.pendingSummaries.set(filepath, fileSummary);
|
|
2843
|
+
return {
|
|
2844
|
+
filepath,
|
|
2845
|
+
lastModified: stats.lastModified,
|
|
2846
|
+
chunks,
|
|
2847
|
+
moduleData,
|
|
2848
|
+
references
|
|
2849
|
+
};
|
|
2850
|
+
}
|
|
2851
|
+
async finalize(ctx) {
|
|
2852
|
+
const indexDir = getRaggrepDir(ctx.rootDir, ctx.config);
|
|
2853
|
+
this.symbolicIndex = new SymbolicIndex(indexDir, this.id);
|
|
2854
|
+
await this.symbolicIndex.initialize();
|
|
2855
|
+
for (const [filepath, summary] of this.pendingSummaries) {
|
|
2856
|
+
this.symbolicIndex.addFile(summary);
|
|
1990
2857
|
}
|
|
2858
|
+
this.symbolicIndex.buildBM25Index();
|
|
2859
|
+
await this.symbolicIndex.save();
|
|
2860
|
+
console.log(` Symbolic index built with ${this.pendingSummaries.size} file summaries`);
|
|
2861
|
+
this.pendingSummaries.clear();
|
|
1991
2862
|
}
|
|
1992
|
-
|
|
1993
|
-
const
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
2863
|
+
async search(query, ctx, options = {}) {
|
|
2864
|
+
const { topK = DEFAULT_TOP_K2, minScore = DEFAULT_MIN_SCORE2, filePatterns } = options;
|
|
2865
|
+
const indexDir = getRaggrepDir(ctx.rootDir, ctx.config);
|
|
2866
|
+
const symbolicIndex = new SymbolicIndex(indexDir, this.id);
|
|
2867
|
+
let candidateFiles;
|
|
2868
|
+
try {
|
|
2869
|
+
await symbolicIndex.initialize();
|
|
2870
|
+
const maxCandidates = topK * TIER1_CANDIDATE_MULTIPLIER;
|
|
2871
|
+
candidateFiles = symbolicIndex.findCandidates(query, maxCandidates);
|
|
2872
|
+
if (candidateFiles.length === 0) {
|
|
2873
|
+
candidateFiles = symbolicIndex.getAllFiles();
|
|
2874
|
+
}
|
|
2875
|
+
} catch {
|
|
2876
|
+
candidateFiles = await ctx.listIndexedFiles();
|
|
2877
|
+
}
|
|
2878
|
+
if (filePatterns && filePatterns.length > 0) {
|
|
2879
|
+
candidateFiles = candidateFiles.filter((filepath) => {
|
|
2880
|
+
return filePatterns.some((pattern) => {
|
|
2881
|
+
if (pattern.startsWith("*.")) {
|
|
2882
|
+
const ext = pattern.slice(1);
|
|
2883
|
+
return filepath.endsWith(ext);
|
|
2884
|
+
}
|
|
2885
|
+
return filepath.includes(pattern);
|
|
2886
|
+
});
|
|
2887
|
+
});
|
|
2888
|
+
}
|
|
2889
|
+
const queryEmbedding = await getEmbedding(query);
|
|
2890
|
+
const bm25Index = new BM25Index;
|
|
2891
|
+
const allChunksData = [];
|
|
2892
|
+
for (const filepath of candidateFiles) {
|
|
2893
|
+
const fileIndex = await ctx.loadFileIndex(filepath);
|
|
2894
|
+
if (!fileIndex)
|
|
2895
|
+
continue;
|
|
2896
|
+
const moduleData = fileIndex.moduleData;
|
|
2897
|
+
if (!moduleData?.embeddings)
|
|
2898
|
+
continue;
|
|
2899
|
+
for (let i = 0;i < fileIndex.chunks.length; i++) {
|
|
2900
|
+
const chunk = fileIndex.chunks[i];
|
|
2901
|
+
const embedding = moduleData.embeddings[i];
|
|
2902
|
+
if (!embedding)
|
|
2903
|
+
continue;
|
|
2904
|
+
allChunksData.push({
|
|
2905
|
+
filepath: fileIndex.filepath,
|
|
2906
|
+
chunk,
|
|
2907
|
+
embedding
|
|
2908
|
+
});
|
|
2909
|
+
bm25Index.addDocuments([{ id: chunk.id, content: chunk.content }]);
|
|
1997
2910
|
}
|
|
1998
2911
|
}
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
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;
|
|
2912
|
+
const bm25Results = bm25Index.search(query, topK * 3);
|
|
2913
|
+
const bm25Scores = new Map;
|
|
2914
|
+
for (const result of bm25Results) {
|
|
2915
|
+
bm25Scores.set(result.id, normalizeScore(result.score, 3));
|
|
2020
2916
|
}
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2917
|
+
const queryTerms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 2);
|
|
2918
|
+
const pathBoosts = new Map;
|
|
2919
|
+
for (const filepath of candidateFiles) {
|
|
2920
|
+
const summary = symbolicIndex.getFileSummary(filepath);
|
|
2921
|
+
if (summary?.pathContext) {
|
|
2922
|
+
let boost = 0;
|
|
2923
|
+
const ctx2 = summary.pathContext;
|
|
2924
|
+
if (ctx2.domain && queryTerms.some((t) => ctx2.domain.includes(t) || t.includes(ctx2.domain))) {
|
|
2925
|
+
boost += 0.1;
|
|
2926
|
+
}
|
|
2927
|
+
if (ctx2.layer && queryTerms.some((t) => ctx2.layer.includes(t) || t.includes(ctx2.layer))) {
|
|
2928
|
+
boost += 0.05;
|
|
2929
|
+
}
|
|
2930
|
+
const segmentMatch = ctx2.segments.some((seg) => queryTerms.some((t) => seg.toLowerCase().includes(t) || t.includes(seg.toLowerCase())));
|
|
2931
|
+
if (segmentMatch) {
|
|
2932
|
+
boost += 0.05;
|
|
2933
|
+
}
|
|
2934
|
+
pathBoosts.set(filepath, boost);
|
|
2024
2935
|
}
|
|
2025
2936
|
}
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
case "test":
|
|
2046
|
-
return "tooling";
|
|
2937
|
+
const results = [];
|
|
2938
|
+
for (const { filepath, chunk, embedding } of allChunksData) {
|
|
2939
|
+
const semanticScore = cosineSimilarity(queryEmbedding, embedding);
|
|
2940
|
+
const bm25Score = bm25Scores.get(chunk.id) || 0;
|
|
2941
|
+
const pathBoost = pathBoosts.get(filepath) || 0;
|
|
2942
|
+
const hybridScore = SEMANTIC_WEIGHT * semanticScore + BM25_WEIGHT * bm25Score + pathBoost;
|
|
2943
|
+
if (hybridScore >= minScore || bm25Score > 0.3) {
|
|
2944
|
+
results.push({
|
|
2945
|
+
filepath,
|
|
2946
|
+
chunk,
|
|
2947
|
+
score: hybridScore,
|
|
2948
|
+
moduleId: this.id,
|
|
2949
|
+
context: {
|
|
2950
|
+
semanticScore,
|
|
2951
|
+
bm25Score,
|
|
2952
|
+
pathBoost
|
|
2953
|
+
}
|
|
2954
|
+
});
|
|
2955
|
+
}
|
|
2047
2956
|
}
|
|
2957
|
+
results.sort((a, b) => b.score - a.score);
|
|
2958
|
+
return results.slice(0, topK);
|
|
2048
2959
|
}
|
|
2049
|
-
|
|
2050
|
-
const
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2960
|
+
extractReferences(content, filepath) {
|
|
2961
|
+
const references = [];
|
|
2962
|
+
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
2963
|
+
const requireRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
2964
|
+
let match;
|
|
2965
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
2966
|
+
const importPath = match[1];
|
|
2967
|
+
if (importPath.startsWith(".")) {
|
|
2968
|
+
const dir = path10.dirname(filepath);
|
|
2969
|
+
const resolved = path10.normalize(path10.join(dir, importPath));
|
|
2970
|
+
references.push(resolved);
|
|
2971
|
+
}
|
|
2059
2972
|
}
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
if (content.includes(`from '${indicator}`) || content.includes(`from "${indicator}`) || content.includes(`require('${indicator}`) || content.includes(`require("${indicator}`)) {
|
|
2067
|
-
return framework;
|
|
2973
|
+
while ((match = requireRegex.exec(content)) !== null) {
|
|
2974
|
+
const importPath = match[1];
|
|
2975
|
+
if (importPath.startsWith(".")) {
|
|
2976
|
+
const dir = path10.dirname(filepath);
|
|
2977
|
+
const resolved = path10.normalize(path10.join(dir, importPath));
|
|
2978
|
+
references.push(resolved);
|
|
2068
2979
|
}
|
|
2069
2980
|
}
|
|
2981
|
+
return references;
|
|
2070
2982
|
}
|
|
2071
|
-
return;
|
|
2072
2983
|
}
|
|
2984
|
+
var DEFAULT_MIN_SCORE2 = 0.15, DEFAULT_TOP_K2 = 10, SEMANTIC_WEIGHT = 0.7, BM25_WEIGHT = 0.3, TIER1_CANDIDATE_MULTIPLIER = 3;
|
|
2985
|
+
var init_typescript = __esm(() => {
|
|
2986
|
+
init_embeddings();
|
|
2987
|
+
init_config2();
|
|
2988
|
+
init_parseCode();
|
|
2989
|
+
init_storage();
|
|
2990
|
+
init_keywords();
|
|
2991
|
+
init_keywords();
|
|
2992
|
+
});
|
|
2073
2993
|
|
|
2074
|
-
// src/
|
|
2994
|
+
// src/app/indexer/index.ts
|
|
2075
2995
|
init_config2();
|
|
2996
|
+
import { glob } from "glob";
|
|
2997
|
+
import * as fs6 from "fs/promises";
|
|
2998
|
+
import * as path11 from "path";
|
|
2076
2999
|
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
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));
|
|
3000
|
+
// src/modules/registry.ts
|
|
3001
|
+
class ModuleRegistryImpl {
|
|
3002
|
+
modules = new Map;
|
|
3003
|
+
register(module) {
|
|
3004
|
+
if (this.modules.has(module.id)) {
|
|
3005
|
+
console.warn(`Module '${module.id}' is already registered, overwriting...`);
|
|
2140
3006
|
}
|
|
2141
|
-
|
|
3007
|
+
this.modules.set(module.id, module);
|
|
2142
3008
|
}
|
|
2143
|
-
|
|
2144
|
-
|
|
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
|
-
}
|
|
3009
|
+
get(id) {
|
|
3010
|
+
return this.modules.get(id);
|
|
2155
3011
|
}
|
|
2156
|
-
|
|
2157
|
-
|
|
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 {}
|
|
3012
|
+
list() {
|
|
3013
|
+
return Array.from(this.modules.values());
|
|
2171
3014
|
}
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
this.
|
|
3015
|
+
getEnabled(config) {
|
|
3016
|
+
const enabledIds = new Set(config.modules.filter((m) => m.enabled).map((m) => m.id));
|
|
3017
|
+
return this.list().filter((m) => enabledIds.has(m.id));
|
|
2175
3018
|
}
|
|
2176
3019
|
}
|
|
3020
|
+
var registry = new ModuleRegistryImpl;
|
|
3021
|
+
async function registerBuiltInModules() {
|
|
3022
|
+
const { CoreModule: CoreModule2 } = await Promise.resolve().then(() => (init_core(), exports_core));
|
|
3023
|
+
const { TypeScriptModule: TypeScriptModule2 } = await Promise.resolve().then(() => (init_typescript(), exports_typescript));
|
|
3024
|
+
registry.register(new CoreModule2);
|
|
3025
|
+
registry.register(new TypeScriptModule2);
|
|
3026
|
+
}
|
|
3027
|
+
|
|
3028
|
+
// src/app/indexer/index.ts
|
|
3029
|
+
init_introspection();
|
|
2177
3030
|
|
|
2178
3031
|
// src/app/indexer/watcher.ts
|
|
2179
3032
|
import { watch } from "chokidar";
|
|
@@ -2182,8 +3035,10 @@ init_config2();
|
|
|
2182
3035
|
// src/app/indexer/index.ts
|
|
2183
3036
|
async function indexDirectory(rootDir, options = {}) {
|
|
2184
3037
|
const verbose = options.verbose ?? false;
|
|
2185
|
-
rootDir =
|
|
3038
|
+
rootDir = path11.resolve(rootDir);
|
|
3039
|
+
const location = getIndexLocation(rootDir);
|
|
2186
3040
|
console.log(`Indexing directory: ${rootDir}`);
|
|
3041
|
+
console.log(`Index location: ${location.indexDir}`);
|
|
2187
3042
|
const config = await loadConfig(rootDir);
|
|
2188
3043
|
const introspection = new IntrospectionIndex(rootDir);
|
|
2189
3044
|
await introspection.initialize();
|
|
@@ -2225,11 +3080,11 @@ async function indexDirectory(rootDir, options = {}) {
|
|
|
2225
3080
|
rootDir,
|
|
2226
3081
|
config,
|
|
2227
3082
|
readFile: async (filepath) => {
|
|
2228
|
-
const fullPath =
|
|
3083
|
+
const fullPath = path11.isAbsolute(filepath) ? filepath : path11.join(rootDir, filepath);
|
|
2229
3084
|
return fs6.readFile(fullPath, "utf-8");
|
|
2230
3085
|
},
|
|
2231
3086
|
getFileStats: async (filepath) => {
|
|
2232
|
-
const fullPath =
|
|
3087
|
+
const fullPath = path11.isAbsolute(filepath) ? filepath : path11.join(rootDir, filepath);
|
|
2233
3088
|
const stats = await fs6.stat(fullPath);
|
|
2234
3089
|
return { lastModified: stats.mtime.toISOString() };
|
|
2235
3090
|
}
|
|
@@ -2254,18 +3109,18 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
|
|
|
2254
3109
|
rootDir,
|
|
2255
3110
|
config,
|
|
2256
3111
|
readFile: async (filepath) => {
|
|
2257
|
-
const fullPath =
|
|
3112
|
+
const fullPath = path11.isAbsolute(filepath) ? filepath : path11.join(rootDir, filepath);
|
|
2258
3113
|
return fs6.readFile(fullPath, "utf-8");
|
|
2259
3114
|
},
|
|
2260
3115
|
getFileStats: async (filepath) => {
|
|
2261
|
-
const fullPath =
|
|
3116
|
+
const fullPath = path11.isAbsolute(filepath) ? filepath : path11.join(rootDir, filepath);
|
|
2262
3117
|
const stats = await fs6.stat(fullPath);
|
|
2263
3118
|
return { lastModified: stats.mtime.toISOString() };
|
|
2264
3119
|
},
|
|
2265
3120
|
getIntrospection: (filepath) => introspection.getFile(filepath)
|
|
2266
3121
|
};
|
|
2267
3122
|
for (const filepath of files) {
|
|
2268
|
-
const relativePath =
|
|
3123
|
+
const relativePath = path11.relative(rootDir, filepath);
|
|
2269
3124
|
try {
|
|
2270
3125
|
const stats = await fs6.stat(filepath);
|
|
2271
3126
|
const lastModified = stats.mtime.toISOString();
|
|
@@ -2335,13 +3190,13 @@ async function loadModuleManifest(rootDir, moduleId, config) {
|
|
|
2335
3190
|
}
|
|
2336
3191
|
async function writeModuleManifest(rootDir, moduleId, manifest, config) {
|
|
2337
3192
|
const manifestPath = getModuleManifestPath(rootDir, moduleId, config);
|
|
2338
|
-
await fs6.mkdir(
|
|
3193
|
+
await fs6.mkdir(path11.dirname(manifestPath), { recursive: true });
|
|
2339
3194
|
await fs6.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
|
|
2340
3195
|
}
|
|
2341
3196
|
async function writeFileIndex(rootDir, moduleId, filepath, fileIndex, config) {
|
|
2342
3197
|
const indexPath = getModuleIndexPath(rootDir, moduleId, config);
|
|
2343
|
-
const indexFilePath =
|
|
2344
|
-
await fs6.mkdir(
|
|
3198
|
+
const indexFilePath = path11.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
|
|
3199
|
+
await fs6.mkdir(path11.dirname(indexFilePath), { recursive: true });
|
|
2345
3200
|
await fs6.writeFile(indexFilePath, JSON.stringify(fileIndex, null, 2));
|
|
2346
3201
|
}
|
|
2347
3202
|
async function updateGlobalManifest(rootDir, modules, config) {
|
|
@@ -2351,12 +3206,12 @@ async function updateGlobalManifest(rootDir, modules, config) {
|
|
|
2351
3206
|
lastUpdated: new Date().toISOString(),
|
|
2352
3207
|
modules: modules.map((m) => m.id)
|
|
2353
3208
|
};
|
|
2354
|
-
await fs6.mkdir(
|
|
3209
|
+
await fs6.mkdir(path11.dirname(manifestPath), { recursive: true });
|
|
2355
3210
|
await fs6.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
|
|
2356
3211
|
}
|
|
2357
3212
|
async function cleanupIndex(rootDir, options = {}) {
|
|
2358
3213
|
const verbose = options.verbose ?? false;
|
|
2359
|
-
rootDir =
|
|
3214
|
+
rootDir = path11.resolve(rootDir);
|
|
2360
3215
|
console.log(`Cleaning up index in: ${rootDir}`);
|
|
2361
3216
|
const config = await loadConfig(rootDir);
|
|
2362
3217
|
await registerBuiltInModules();
|
|
@@ -2386,7 +3241,7 @@ async function cleanupModuleIndex(rootDir, moduleId, config, verbose) {
|
|
|
2386
3241
|
const filesToRemove = [];
|
|
2387
3242
|
const updatedFiles = {};
|
|
2388
3243
|
for (const [filepath, entry] of Object.entries(manifest.files)) {
|
|
2389
|
-
const fullPath =
|
|
3244
|
+
const fullPath = path11.join(rootDir, filepath);
|
|
2390
3245
|
try {
|
|
2391
3246
|
await fs6.access(fullPath);
|
|
2392
3247
|
updatedFiles[filepath] = entry;
|
|
@@ -2400,7 +3255,7 @@ async function cleanupModuleIndex(rootDir, moduleId, config, verbose) {
|
|
|
2400
3255
|
}
|
|
2401
3256
|
}
|
|
2402
3257
|
for (const filepath of filesToRemove) {
|
|
2403
|
-
const indexFilePath =
|
|
3258
|
+
const indexFilePath = path11.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
|
|
2404
3259
|
try {
|
|
2405
3260
|
await fs6.unlink(indexFilePath);
|
|
2406
3261
|
} catch {}
|
|
@@ -2416,7 +3271,7 @@ async function cleanupEmptyDirectories(dir) {
|
|
|
2416
3271
|
const entries = await fs6.readdir(dir, { withFileTypes: true });
|
|
2417
3272
|
for (const entry of entries) {
|
|
2418
3273
|
if (entry.isDirectory()) {
|
|
2419
|
-
const subDir =
|
|
3274
|
+
const subDir = path11.join(dir, entry.name);
|
|
2420
3275
|
await cleanupEmptyDirectories(subDir);
|
|
2421
3276
|
}
|
|
2422
3277
|
}
|
|
@@ -2434,9 +3289,9 @@ async function cleanupEmptyDirectories(dir) {
|
|
|
2434
3289
|
// src/app/search/index.ts
|
|
2435
3290
|
init_config2();
|
|
2436
3291
|
import * as fs7 from "fs/promises";
|
|
2437
|
-
import * as
|
|
3292
|
+
import * as path12 from "path";
|
|
2438
3293
|
async function search(rootDir, query, options = {}) {
|
|
2439
|
-
rootDir =
|
|
3294
|
+
rootDir = path12.resolve(rootDir);
|
|
2440
3295
|
console.log(`Searching for: "${query}"`);
|
|
2441
3296
|
const config = await loadConfig(rootDir);
|
|
2442
3297
|
await registerBuiltInModules();
|
|
@@ -2477,7 +3332,7 @@ function createSearchContext(rootDir, moduleId, config) {
|
|
|
2477
3332
|
config,
|
|
2478
3333
|
loadFileIndex: async (filepath) => {
|
|
2479
3334
|
const hasExtension = /\.[^./]+$/.test(filepath);
|
|
2480
|
-
const indexFilePath = hasExtension ?
|
|
3335
|
+
const indexFilePath = hasExtension ? path12.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path12.join(indexPath, filepath + ".json");
|
|
2481
3336
|
try {
|
|
2482
3337
|
const content = await fs7.readFile(indexFilePath, "utf-8");
|
|
2483
3338
|
return JSON.parse(content);
|
|
@@ -2489,7 +3344,7 @@ function createSearchContext(rootDir, moduleId, config) {
|
|
|
2489
3344
|
const files = [];
|
|
2490
3345
|
await traverseDirectory(indexPath, files, indexPath);
|
|
2491
3346
|
return files.filter((f) => f.endsWith(".json") && !f.endsWith("manifest.json")).map((f) => {
|
|
2492
|
-
const relative3 =
|
|
3347
|
+
const relative3 = path12.relative(indexPath, f);
|
|
2493
3348
|
return relative3.replace(/\.json$/, "");
|
|
2494
3349
|
});
|
|
2495
3350
|
}
|
|
@@ -2499,7 +3354,7 @@ async function traverseDirectory(dir, files, basePath) {
|
|
|
2499
3354
|
try {
|
|
2500
3355
|
const entries = await fs7.readdir(dir, { withFileTypes: true });
|
|
2501
3356
|
for (const entry of entries) {
|
|
2502
|
-
const fullPath =
|
|
3357
|
+
const fullPath = path12.join(dir, entry.name);
|
|
2503
3358
|
if (entry.isDirectory()) {
|
|
2504
3359
|
await traverseDirectory(fullPath, files, basePath);
|
|
2505
3360
|
} else if (entry.isFile()) {
|
|
@@ -2575,4 +3430,4 @@ export {
|
|
|
2575
3430
|
cleanup
|
|
2576
3431
|
};
|
|
2577
3432
|
|
|
2578
|
-
//# debugId=
|
|
3433
|
+
//# debugId=3C6A4139E7CDC07F64756E2164756E21
|