raggrep 0.1.6 → 0.2.0
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 +62 -95
- package/dist/app/indexer/index.d.ts +27 -2
- package/dist/cli/main.js +967 -604
- package/dist/cli/main.js.map +18 -17
- package/dist/{introspection/conventions/types.d.ts → domain/entities/conventions.d.ts} +6 -5
- package/dist/domain/entities/index.d.ts +2 -0
- package/dist/{introspection → domain/services}/conventions/configFiles.d.ts +1 -1
- package/dist/{introspection → domain/services}/conventions/entryPoints.d.ts +1 -1
- package/dist/{introspection → domain/services}/conventions/frameworks/convex.d.ts +1 -1
- package/dist/{introspection → domain/services}/conventions/frameworks/index.d.ts +1 -1
- package/dist/{introspection → domain/services}/conventions/frameworks/nextjs.d.ts +1 -1
- package/dist/{introspection → domain/services}/conventions/index.d.ts +5 -5
- package/dist/domain/services/introspection.d.ts +31 -0
- package/dist/index.js +671 -474
- package/dist/index.js.map +16 -16
- package/dist/{introspection/index.d.ts → infrastructure/introspection/IntrospectionIndex.d.ts} +3 -14
- package/dist/infrastructure/introspection/index.d.ts +9 -0
- package/dist/{introspection → infrastructure/introspection}/projectDetector.d.ts +3 -12
- package/dist/types.d.ts +4 -4
- package/package.json +1 -1
- package/dist/introspection/fileIntrospector.d.ts +0 -14
- /package/dist/{introspection/types.d.ts → domain/entities/introspection.d.ts} +0 -0
- /package/dist/{introspection → domain/services}/conventions/conventions.test.d.ts +0 -0
- /package/dist/{introspection → domain/services}/introspection.test.d.ts +0 -0
package/dist/cli/main.js
CHANGED
|
@@ -429,247 +429,11 @@ function normalizeScore(score, midpoint = 5) {
|
|
|
429
429
|
}
|
|
430
430
|
var BM25_K1 = 1.5, BM25_B = 0.75;
|
|
431
431
|
|
|
432
|
-
// src/
|
|
432
|
+
// src/domain/services/conventions/entryPoints.ts
|
|
433
433
|
import * as path3 from "path";
|
|
434
|
-
import * as fs2 from "fs/promises";
|
|
435
|
-
function detectScopeFromName(name) {
|
|
436
|
-
const nameLower = name.toLowerCase();
|
|
437
|
-
for (const [scope, keywords] of Object.entries(SCOPE_KEYWORDS)) {
|
|
438
|
-
if (scope === "unknown")
|
|
439
|
-
continue;
|
|
440
|
-
for (const keyword of keywords) {
|
|
441
|
-
if (nameLower.includes(keyword)) {
|
|
442
|
-
return scope;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
return "unknown";
|
|
447
|
-
}
|
|
448
|
-
async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
|
|
449
|
-
if (depth > MAX_SCAN_DEPTH)
|
|
450
|
-
return [];
|
|
451
|
-
const results = [];
|
|
452
|
-
const fullDir = currentDir ? path3.join(rootDir, currentDir) : rootDir;
|
|
453
|
-
try {
|
|
454
|
-
const entries = await fs2.readdir(fullDir, { withFileTypes: true });
|
|
455
|
-
const hasPackageJson = entries.some((e) => e.isFile() && e.name === "package.json");
|
|
456
|
-
if (hasPackageJson && currentDir) {
|
|
457
|
-
const info = await parsePackageJson(rootDir, currentDir);
|
|
458
|
-
if (info) {
|
|
459
|
-
results.push(info);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
for (const entry of entries) {
|
|
463
|
-
if (!entry.isDirectory())
|
|
464
|
-
continue;
|
|
465
|
-
if (SKIP_DIRS.has(entry.name))
|
|
466
|
-
continue;
|
|
467
|
-
const subPath = currentDir ? `${currentDir}/${entry.name}` : entry.name;
|
|
468
|
-
const subResults = await scanForPackageJsons(rootDir, subPath, depth + 1);
|
|
469
|
-
results.push(...subResults);
|
|
470
|
-
}
|
|
471
|
-
} catch {}
|
|
472
|
-
return results;
|
|
473
|
-
}
|
|
474
|
-
async function parsePackageJson(rootDir, relativePath) {
|
|
475
|
-
try {
|
|
476
|
-
const packageJsonPath = path3.join(rootDir, relativePath, "package.json");
|
|
477
|
-
const content = await fs2.readFile(packageJsonPath, "utf-8");
|
|
478
|
-
const pkg = JSON.parse(content);
|
|
479
|
-
const name = pkg.name || path3.basename(relativePath);
|
|
480
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
481
|
-
let type = "unknown";
|
|
482
|
-
if (deps["next"] || deps["react"] || deps["vue"] || deps["svelte"]) {
|
|
483
|
-
type = "app";
|
|
484
|
-
} else if (deps["express"] || deps["fastify"] || deps["koa"] || deps["hono"]) {
|
|
485
|
-
type = "service";
|
|
486
|
-
} else if (pkg.main || pkg.exports) {
|
|
487
|
-
type = "library";
|
|
488
|
-
}
|
|
489
|
-
const hasWorkspaces = Boolean(pkg.workspaces);
|
|
490
|
-
return { name, relativePath, type, hasWorkspaces };
|
|
491
|
-
} catch {
|
|
492
|
-
return null;
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
async function detectProjectStructure(rootDir) {
|
|
496
|
-
const projectMap = new Map;
|
|
497
|
-
let isMonorepo = false;
|
|
498
|
-
try {
|
|
499
|
-
const entries = await fs2.readdir(rootDir, { withFileTypes: true });
|
|
500
|
-
const dirNames = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
501
|
-
const monorepoPatterns = ["apps", "packages", "libs", "services"];
|
|
502
|
-
const hasMonorepoStructure = monorepoPatterns.some((p) => dirNames.includes(p));
|
|
503
|
-
if (hasMonorepoStructure) {
|
|
504
|
-
isMonorepo = true;
|
|
505
|
-
for (const pattern of monorepoPatterns) {
|
|
506
|
-
if (!dirNames.includes(pattern))
|
|
507
|
-
continue;
|
|
508
|
-
const patternDir = path3.join(rootDir, pattern);
|
|
509
|
-
try {
|
|
510
|
-
const subDirs = await fs2.readdir(patternDir, { withFileTypes: true });
|
|
511
|
-
for (const subDir of subDirs) {
|
|
512
|
-
if (!subDir.isDirectory())
|
|
513
|
-
continue;
|
|
514
|
-
const projectRoot = `${pattern}/${subDir.name}`;
|
|
515
|
-
const type = getProjectType(pattern);
|
|
516
|
-
projectMap.set(projectRoot, {
|
|
517
|
-
name: subDir.name,
|
|
518
|
-
root: projectRoot,
|
|
519
|
-
type
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
} catch {}
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
const packageJsons = await scanForPackageJsons(rootDir);
|
|
526
|
-
for (const pkg of packageJsons) {
|
|
527
|
-
if (pkg.hasWorkspaces) {
|
|
528
|
-
isMonorepo = true;
|
|
529
|
-
}
|
|
530
|
-
if (packageJsons.length > 1) {
|
|
531
|
-
isMonorepo = true;
|
|
532
|
-
}
|
|
533
|
-
projectMap.set(pkg.relativePath, {
|
|
534
|
-
name: pkg.name,
|
|
535
|
-
root: pkg.relativePath,
|
|
536
|
-
type: pkg.type
|
|
537
|
-
});
|
|
538
|
-
}
|
|
539
|
-
let rootType = "unknown";
|
|
540
|
-
try {
|
|
541
|
-
const rootPkgPath = path3.join(rootDir, "package.json");
|
|
542
|
-
const rootPkg = JSON.parse(await fs2.readFile(rootPkgPath, "utf-8"));
|
|
543
|
-
if (rootPkg.workspaces) {
|
|
544
|
-
isMonorepo = true;
|
|
545
|
-
}
|
|
546
|
-
const deps = { ...rootPkg.dependencies, ...rootPkg.devDependencies };
|
|
547
|
-
if (deps["next"] || deps["react"] || deps["vue"]) {
|
|
548
|
-
rootType = "app";
|
|
549
|
-
} else if (deps["express"] || deps["fastify"] || deps["koa"]) {
|
|
550
|
-
rootType = "service";
|
|
551
|
-
}
|
|
552
|
-
} catch {}
|
|
553
|
-
const projects = Array.from(projectMap.values()).sort((a, b) => a.root.length - b.root.length);
|
|
554
|
-
return {
|
|
555
|
-
projects,
|
|
556
|
-
isMonorepo,
|
|
557
|
-
rootType: isMonorepo ? undefined : rootType
|
|
558
|
-
};
|
|
559
|
-
} catch {
|
|
560
|
-
return {
|
|
561
|
-
projects: [],
|
|
562
|
-
isMonorepo: false,
|
|
563
|
-
rootType: "unknown"
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
function getProjectType(patternDir) {
|
|
568
|
-
switch (patternDir) {
|
|
569
|
-
case "apps":
|
|
570
|
-
return "app";
|
|
571
|
-
case "packages":
|
|
572
|
-
case "libs":
|
|
573
|
-
return "library";
|
|
574
|
-
case "services":
|
|
575
|
-
return "service";
|
|
576
|
-
case "scripts":
|
|
577
|
-
case "tools":
|
|
578
|
-
return "script";
|
|
579
|
-
default:
|
|
580
|
-
return "unknown";
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
function findProjectForFile(filepath, structure) {
|
|
584
|
-
const normalizedPath = filepath.replace(/\\/g, "/");
|
|
585
|
-
const matches = [];
|
|
586
|
-
for (const project of structure.projects) {
|
|
587
|
-
if (normalizedPath === project.root || normalizedPath.startsWith(project.root + "/")) {
|
|
588
|
-
matches.push(project);
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
if (matches.length > 0) {
|
|
592
|
-
return matches.reduce((best, current) => current.root.length > best.root.length ? current : best);
|
|
593
|
-
}
|
|
594
|
-
for (const { pattern, type } of PROJECT_PATTERNS) {
|
|
595
|
-
const match = normalizedPath.match(pattern);
|
|
596
|
-
if (match) {
|
|
597
|
-
return {
|
|
598
|
-
name: match[1],
|
|
599
|
-
root: match[0],
|
|
600
|
-
type
|
|
601
|
-
};
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
return {
|
|
605
|
-
name: "root",
|
|
606
|
-
root: "",
|
|
607
|
-
type: structure.rootType ?? "unknown"
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
var MAX_SCAN_DEPTH = 4, SKIP_DIRS, PROJECT_PATTERNS, SCOPE_KEYWORDS;
|
|
611
|
-
var init_projectDetector = __esm(() => {
|
|
612
|
-
SKIP_DIRS = new Set([
|
|
613
|
-
"node_modules",
|
|
614
|
-
".git",
|
|
615
|
-
"dist",
|
|
616
|
-
"build",
|
|
617
|
-
".next",
|
|
618
|
-
".nuxt",
|
|
619
|
-
"coverage",
|
|
620
|
-
".raggrep"
|
|
621
|
-
]);
|
|
622
|
-
PROJECT_PATTERNS = [
|
|
623
|
-
{ pattern: /^apps\/([^/]+)/, type: "app", defaultScope: "unknown" },
|
|
624
|
-
{ pattern: /^packages\/([^/]+)/, type: "library", defaultScope: "shared" },
|
|
625
|
-
{ pattern: /^libs\/([^/]+)/, type: "library", defaultScope: "shared" },
|
|
626
|
-
{ pattern: /^services\/([^/]+)/, type: "service", defaultScope: "backend" },
|
|
627
|
-
{ pattern: /^scripts\/([^/]+)/, type: "script", defaultScope: "tooling" },
|
|
628
|
-
{ pattern: /^tools\/([^/]+)/, type: "script", defaultScope: "tooling" }
|
|
629
|
-
];
|
|
630
|
-
SCOPE_KEYWORDS = {
|
|
631
|
-
frontend: [
|
|
632
|
-
"web",
|
|
633
|
-
"webapp",
|
|
634
|
-
"frontend",
|
|
635
|
-
"client",
|
|
636
|
-
"ui",
|
|
637
|
-
"app",
|
|
638
|
-
"mobile",
|
|
639
|
-
"react",
|
|
640
|
-
"vue",
|
|
641
|
-
"angular",
|
|
642
|
-
"next",
|
|
643
|
-
"nuxt"
|
|
644
|
-
],
|
|
645
|
-
backend: [
|
|
646
|
-
"api",
|
|
647
|
-
"server",
|
|
648
|
-
"backend",
|
|
649
|
-
"service",
|
|
650
|
-
"worker",
|
|
651
|
-
"lambda",
|
|
652
|
-
"functions"
|
|
653
|
-
],
|
|
654
|
-
shared: ["shared", "common", "utils", "lib", "core", "types", "models"],
|
|
655
|
-
tooling: [
|
|
656
|
-
"scripts",
|
|
657
|
-
"tools",
|
|
658
|
-
"cli",
|
|
659
|
-
"devtools",
|
|
660
|
-
"build",
|
|
661
|
-
"config",
|
|
662
|
-
"infra"
|
|
663
|
-
],
|
|
664
|
-
unknown: []
|
|
665
|
-
};
|
|
666
|
-
});
|
|
667
|
-
|
|
668
|
-
// src/introspection/conventions/entryPoints.ts
|
|
669
|
-
import * as path4 from "path";
|
|
670
434
|
function getParentFolder(filepath) {
|
|
671
|
-
const dir =
|
|
672
|
-
return
|
|
435
|
+
const dir = path3.dirname(filepath);
|
|
436
|
+
return path3.basename(dir);
|
|
673
437
|
}
|
|
674
438
|
var entryPointConventions;
|
|
675
439
|
var init_entryPoints = __esm(() => {
|
|
@@ -754,11 +518,75 @@ var init_entryPoints = __esm(() => {
|
|
|
754
518
|
return filename === "lib.rs" || filename === "main.rs";
|
|
755
519
|
},
|
|
756
520
|
keywords: ["entry", "crate", "rust", "module"]
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
id: "go-main",
|
|
524
|
+
name: "Go Main Entry",
|
|
525
|
+
description: "Go application main entry point",
|
|
526
|
+
category: "entry-point",
|
|
527
|
+
match: (filepath, filename) => {
|
|
528
|
+
return filename === "main.go";
|
|
529
|
+
},
|
|
530
|
+
keywords: ["entry", "main", "go", "golang", "entrypoint"],
|
|
531
|
+
dynamicKeywords: (filepath) => {
|
|
532
|
+
const parent = getParentFolder(filepath);
|
|
533
|
+
if (parent && !["cmd", "src", ".", ""].includes(parent)) {
|
|
534
|
+
return [parent.toLowerCase()];
|
|
535
|
+
}
|
|
536
|
+
return [];
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
id: "python-main",
|
|
541
|
+
name: "Python Main Module",
|
|
542
|
+
description: "Python package main entry point",
|
|
543
|
+
category: "entry-point",
|
|
544
|
+
match: (filepath, filename) => {
|
|
545
|
+
return filename === "__main__.py";
|
|
546
|
+
},
|
|
547
|
+
keywords: ["entry", "main", "python", "entrypoint", "cli"],
|
|
548
|
+
dynamicKeywords: (filepath) => {
|
|
549
|
+
const parent = getParentFolder(filepath);
|
|
550
|
+
if (["src", "lib", ".", ""].includes(parent)) {
|
|
551
|
+
return [];
|
|
552
|
+
}
|
|
553
|
+
return [parent.toLowerCase()];
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
id: "python-app",
|
|
558
|
+
name: "Python App Entry",
|
|
559
|
+
description: "Common Python application entry points",
|
|
560
|
+
category: "entry-point",
|
|
561
|
+
match: (filepath, filename) => {
|
|
562
|
+
return filename === "app.py" || filename === "main.py" || filename === "run.py";
|
|
563
|
+
},
|
|
564
|
+
keywords: ["entry", "main", "python", "app", "entrypoint"]
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
id: "python-manage",
|
|
568
|
+
name: "Django Manage",
|
|
569
|
+
description: "Django management script",
|
|
570
|
+
category: "entry-point",
|
|
571
|
+
match: (filepath, filename) => {
|
|
572
|
+
return filename === "manage.py";
|
|
573
|
+
},
|
|
574
|
+
keywords: ["entry", "django", "python", "manage", "cli", "admin"]
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
id: "python-wsgi",
|
|
578
|
+
name: "Python WSGI Entry",
|
|
579
|
+
description: "Python WSGI application entry point",
|
|
580
|
+
category: "entry-point",
|
|
581
|
+
match: (filepath, filename) => {
|
|
582
|
+
return filename === "wsgi.py" || filename === "asgi.py";
|
|
583
|
+
},
|
|
584
|
+
keywords: ["entry", "wsgi", "asgi", "python", "server", "web"]
|
|
757
585
|
}
|
|
758
586
|
];
|
|
759
587
|
});
|
|
760
588
|
|
|
761
|
-
// src/
|
|
589
|
+
// src/domain/services/conventions/configFiles.ts
|
|
762
590
|
var configFileConventions;
|
|
763
591
|
var init_configFiles = __esm(() => {
|
|
764
592
|
configFileConventions = [
|
|
@@ -767,40 +595,191 @@ var init_configFiles = __esm(() => {
|
|
|
767
595
|
name: "Package.json",
|
|
768
596
|
description: "Node.js package manifest",
|
|
769
597
|
category: "configuration",
|
|
770
|
-
match: (filepath, filename) => filename === "package.json",
|
|
771
|
-
keywords: ["package", "dependencies", "npm", "scripts", "manifest", "node"]
|
|
598
|
+
match: (filepath, filename) => filename === "package.json",
|
|
599
|
+
keywords: ["package", "dependencies", "npm", "scripts", "manifest", "node"]
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
id: "pnpm-workspace",
|
|
603
|
+
name: "PNPM Workspace",
|
|
604
|
+
description: "PNPM monorepo workspace configuration",
|
|
605
|
+
category: "configuration",
|
|
606
|
+
match: (filepath, filename) => filename === "pnpm-workspace.yaml" || filename === "pnpm-workspace.yml",
|
|
607
|
+
keywords: ["workspace", "monorepo", "pnpm", "packages"]
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
id: "yarn-lock",
|
|
611
|
+
name: "Yarn Lock",
|
|
612
|
+
description: "Yarn dependency lock file",
|
|
613
|
+
category: "configuration",
|
|
614
|
+
match: (filepath, filename) => filename === "yarn.lock",
|
|
615
|
+
keywords: ["dependencies", "lock", "yarn", "versions"]
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
id: "package-lock",
|
|
619
|
+
name: "Package Lock",
|
|
620
|
+
description: "NPM dependency lock file",
|
|
621
|
+
category: "configuration",
|
|
622
|
+
match: (filepath, filename) => filename === "package-lock.json",
|
|
623
|
+
keywords: ["dependencies", "lock", "npm", "versions"]
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
id: "bun-lockb",
|
|
627
|
+
name: "Bun Lock",
|
|
628
|
+
description: "Bun dependency lock file",
|
|
629
|
+
category: "configuration",
|
|
630
|
+
match: (filepath, filename) => filename === "bun.lockb" || filename === "bun.lock",
|
|
631
|
+
keywords: ["dependencies", "lock", "bun", "versions"]
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
id: "go-mod",
|
|
635
|
+
name: "Go Module",
|
|
636
|
+
description: "Go module definition file",
|
|
637
|
+
category: "configuration",
|
|
638
|
+
match: (filepath, filename) => filename === "go.mod",
|
|
639
|
+
keywords: [
|
|
640
|
+
"go",
|
|
641
|
+
"golang",
|
|
642
|
+
"module",
|
|
643
|
+
"dependencies",
|
|
644
|
+
"package",
|
|
645
|
+
"workspace"
|
|
646
|
+
]
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
id: "go-sum",
|
|
650
|
+
name: "Go Sum",
|
|
651
|
+
description: "Go module checksum file",
|
|
652
|
+
category: "configuration",
|
|
653
|
+
match: (filepath, filename) => filename === "go.sum",
|
|
654
|
+
keywords: ["go", "golang", "dependencies", "checksum", "lock", "versions"]
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
id: "go-work",
|
|
658
|
+
name: "Go Workspace",
|
|
659
|
+
description: "Go workspace configuration for multi-module development",
|
|
660
|
+
category: "configuration",
|
|
661
|
+
match: (filepath, filename) => filename === "go.work" || filename === "go.work.sum",
|
|
662
|
+
keywords: ["go", "golang", "workspace", "monorepo", "modules"]
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
id: "makefile",
|
|
666
|
+
name: "Makefile",
|
|
667
|
+
description: "Make build automation file",
|
|
668
|
+
category: "build",
|
|
669
|
+
match: (filepath, filename) => filename === "Makefile" || filename === "makefile" || filename === "GNUmakefile",
|
|
670
|
+
keywords: ["make", "build", "automation", "tasks", "compile"]
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
id: "requirements-txt",
|
|
674
|
+
name: "Python Requirements",
|
|
675
|
+
description: "Python pip requirements file",
|
|
676
|
+
category: "configuration",
|
|
677
|
+
match: (filepath, filename) => filename === "requirements.txt" || filename.startsWith("requirements-") || filename.startsWith("requirements_"),
|
|
678
|
+
keywords: ["python", "pip", "dependencies", "packages", "requirements"]
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
id: "pyproject-toml",
|
|
682
|
+
name: "Python Project",
|
|
683
|
+
description: "Python project configuration (PEP 518/621)",
|
|
684
|
+
category: "configuration",
|
|
685
|
+
match: (filepath, filename) => filename === "pyproject.toml",
|
|
686
|
+
keywords: [
|
|
687
|
+
"python",
|
|
688
|
+
"project",
|
|
689
|
+
"config",
|
|
690
|
+
"poetry",
|
|
691
|
+
"build",
|
|
692
|
+
"dependencies",
|
|
693
|
+
"package"
|
|
694
|
+
]
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
id: "setup-py",
|
|
698
|
+
name: "Python Setup",
|
|
699
|
+
description: "Python package setup script",
|
|
700
|
+
category: "configuration",
|
|
701
|
+
match: (filepath, filename) => filename === "setup.py",
|
|
702
|
+
keywords: ["python", "setup", "package", "install", "distribution"]
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
id: "setup-cfg",
|
|
706
|
+
name: "Python Setup Config",
|
|
707
|
+
description: "Python setup configuration file",
|
|
708
|
+
category: "configuration",
|
|
709
|
+
match: (filepath, filename) => filename === "setup.cfg",
|
|
710
|
+
keywords: ["python", "setup", "config", "package", "metadata"]
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
id: "pipfile",
|
|
714
|
+
name: "Pipfile",
|
|
715
|
+
description: "Pipenv dependency file",
|
|
716
|
+
category: "configuration",
|
|
717
|
+
match: (filepath, filename) => filename === "Pipfile" || filename === "Pipfile.lock",
|
|
718
|
+
keywords: ["python", "pipenv", "dependencies", "packages", "virtualenv"]
|
|
719
|
+
},
|
|
720
|
+
{
|
|
721
|
+
id: "poetry-lock",
|
|
722
|
+
name: "Poetry Lock",
|
|
723
|
+
description: "Poetry dependency lock file",
|
|
724
|
+
category: "configuration",
|
|
725
|
+
match: (filepath, filename) => filename === "poetry.lock",
|
|
726
|
+
keywords: ["python", "poetry", "dependencies", "lock", "versions"]
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
id: "tox-ini",
|
|
730
|
+
name: "Tox Config",
|
|
731
|
+
description: "Tox testing automation configuration",
|
|
732
|
+
category: "test",
|
|
733
|
+
match: (filepath, filename) => filename === "tox.ini",
|
|
734
|
+
keywords: ["python", "tox", "testing", "automation", "environments"]
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
id: "pytest-ini",
|
|
738
|
+
name: "Pytest Config",
|
|
739
|
+
description: "Pytest configuration file",
|
|
740
|
+
category: "test",
|
|
741
|
+
match: (filepath, filename) => filename === "pytest.ini" || filename === "conftest.py",
|
|
742
|
+
keywords: ["python", "pytest", "testing", "test", "fixtures"]
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
id: "mypy-ini",
|
|
746
|
+
name: "Mypy Config",
|
|
747
|
+
description: "Mypy type checker configuration",
|
|
748
|
+
category: "configuration",
|
|
749
|
+
match: (filepath, filename) => filename === "mypy.ini" || filename === ".mypy.ini",
|
|
750
|
+
keywords: ["python", "mypy", "types", "type checking", "static analysis"]
|
|
772
751
|
},
|
|
773
752
|
{
|
|
774
|
-
id: "
|
|
775
|
-
name: "
|
|
776
|
-
description: "
|
|
753
|
+
id: "flake8",
|
|
754
|
+
name: "Flake8 Config",
|
|
755
|
+
description: "Flake8 linter configuration",
|
|
777
756
|
category: "configuration",
|
|
778
|
-
match: (filepath, filename) => filename === "
|
|
779
|
-
keywords: ["
|
|
757
|
+
match: (filepath, filename) => filename === ".flake8",
|
|
758
|
+
keywords: ["python", "flake8", "linting", "lint", "style"]
|
|
780
759
|
},
|
|
781
760
|
{
|
|
782
|
-
id: "
|
|
783
|
-
name: "
|
|
784
|
-
description: "
|
|
761
|
+
id: "pylintrc",
|
|
762
|
+
name: "Pylint Config",
|
|
763
|
+
description: "Pylint linter configuration",
|
|
785
764
|
category: "configuration",
|
|
786
|
-
match: (filepath, filename) => filename === "
|
|
787
|
-
keywords: ["
|
|
765
|
+
match: (filepath, filename) => filename === ".pylintrc" || filename === "pylintrc" || filename === "pylint.toml",
|
|
766
|
+
keywords: ["python", "pylint", "linting", "lint", "code quality"]
|
|
788
767
|
},
|
|
789
768
|
{
|
|
790
|
-
id: "
|
|
791
|
-
name: "
|
|
792
|
-
description: "
|
|
769
|
+
id: "ruff-toml",
|
|
770
|
+
name: "Ruff Config",
|
|
771
|
+
description: "Ruff linter/formatter configuration",
|
|
793
772
|
category: "configuration",
|
|
794
|
-
match: (filepath, filename) => filename === "
|
|
795
|
-
keywords: ["
|
|
773
|
+
match: (filepath, filename) => filename === "ruff.toml" || filename === ".ruff.toml",
|
|
774
|
+
keywords: ["python", "ruff", "linting", "formatting", "fast"]
|
|
796
775
|
},
|
|
797
776
|
{
|
|
798
|
-
id: "
|
|
799
|
-
name: "
|
|
800
|
-
description: "
|
|
777
|
+
id: "black-toml",
|
|
778
|
+
name: "Black Config",
|
|
779
|
+
description: "Black formatter configuration",
|
|
801
780
|
category: "configuration",
|
|
802
|
-
match: (filepath, filename) => filename === "
|
|
803
|
-
keywords: ["
|
|
781
|
+
match: (filepath, filename) => filename === ".black.toml",
|
|
782
|
+
keywords: ["python", "black", "formatting", "format", "style"]
|
|
804
783
|
},
|
|
805
784
|
{
|
|
806
785
|
id: "tsconfig",
|
|
@@ -1043,7 +1022,7 @@ var init_configFiles = __esm(() => {
|
|
|
1043
1022
|
];
|
|
1044
1023
|
});
|
|
1045
1024
|
|
|
1046
|
-
// src/
|
|
1025
|
+
// src/domain/services/conventions/frameworks/nextjs.ts
|
|
1047
1026
|
var nextjsConventions, nextjsFramework;
|
|
1048
1027
|
var init_nextjs = __esm(() => {
|
|
1049
1028
|
nextjsConventions = [
|
|
@@ -1208,7 +1187,7 @@ var init_nextjs = __esm(() => {
|
|
|
1208
1187
|
};
|
|
1209
1188
|
});
|
|
1210
1189
|
|
|
1211
|
-
// src/
|
|
1190
|
+
// src/domain/services/conventions/frameworks/convex.ts
|
|
1212
1191
|
var convexConventions, convexFramework;
|
|
1213
1192
|
var init_convex = __esm(() => {
|
|
1214
1193
|
convexConventions = [
|
|
@@ -1304,7 +1283,7 @@ var init_convex = __esm(() => {
|
|
|
1304
1283
|
};
|
|
1305
1284
|
});
|
|
1306
1285
|
|
|
1307
|
-
// src/
|
|
1286
|
+
// src/domain/services/conventions/frameworks/index.ts
|
|
1308
1287
|
function getAllFrameworkConventions() {
|
|
1309
1288
|
return frameworkProviders.flatMap((f) => f.conventions);
|
|
1310
1289
|
}
|
|
@@ -1320,26 +1299,21 @@ var init_frameworks = __esm(() => {
|
|
|
1320
1299
|
];
|
|
1321
1300
|
});
|
|
1322
1301
|
|
|
1323
|
-
// src/
|
|
1324
|
-
import * as
|
|
1325
|
-
function
|
|
1302
|
+
// src/domain/services/conventions/index.ts
|
|
1303
|
+
import * as path4 from "path";
|
|
1304
|
+
function getConventions() {
|
|
1326
1305
|
return [
|
|
1327
1306
|
...entryPointConventions,
|
|
1328
1307
|
...configFileConventions,
|
|
1329
|
-
...getAllFrameworkConventions()
|
|
1330
|
-
];
|
|
1331
|
-
}
|
|
1332
|
-
function getConventions() {
|
|
1333
|
-
return [
|
|
1334
|
-
...getAllConventions(),
|
|
1308
|
+
...getAllFrameworkConventions(),
|
|
1335
1309
|
...typeDefinitionConventions,
|
|
1336
1310
|
...testFileConventions
|
|
1337
1311
|
];
|
|
1338
1312
|
}
|
|
1339
1313
|
function getConventionKeywords(filepath) {
|
|
1340
1314
|
const conventions = getConventions();
|
|
1341
|
-
const filename =
|
|
1342
|
-
const extension =
|
|
1315
|
+
const filename = path4.basename(filepath);
|
|
1316
|
+
const extension = path4.extname(filepath);
|
|
1343
1317
|
const keywords = new Set;
|
|
1344
1318
|
for (const convention of conventions) {
|
|
1345
1319
|
try {
|
|
@@ -1386,9 +1360,8 @@ var init_conventions = __esm(() => {
|
|
|
1386
1360
|
keywords: ["types", "definitions", "typescript", "interfaces"],
|
|
1387
1361
|
dynamicKeywords: (filepath) => {
|
|
1388
1362
|
const match = filepath.match(/([^/]+)\.types\.ts$/);
|
|
1389
|
-
if (match)
|
|
1363
|
+
if (match)
|
|
1390
1364
|
return [match[1].toLowerCase()];
|
|
1391
|
-
}
|
|
1392
1365
|
return [];
|
|
1393
1366
|
}
|
|
1394
1367
|
},
|
|
@@ -1411,9 +1384,8 @@ var init_conventions = __esm(() => {
|
|
|
1411
1384
|
keywords: ["test", "spec", "unit test"],
|
|
1412
1385
|
dynamicKeywords: (filepath) => {
|
|
1413
1386
|
const match = filepath.match(/([^/]+)\.(test|spec)\./);
|
|
1414
|
-
if (match)
|
|
1387
|
+
if (match)
|
|
1415
1388
|
return [match[1].toLowerCase()];
|
|
1416
|
-
}
|
|
1417
1389
|
return [];
|
|
1418
1390
|
}
|
|
1419
1391
|
},
|
|
@@ -1428,22 +1400,19 @@ var init_conventions = __esm(() => {
|
|
|
1428
1400
|
];
|
|
1429
1401
|
});
|
|
1430
1402
|
|
|
1431
|
-
// src/introspection
|
|
1432
|
-
import * as
|
|
1403
|
+
// src/domain/services/introspection.ts
|
|
1404
|
+
import * as path5 from "path";
|
|
1433
1405
|
function introspectFile(filepath, structure, fileContent) {
|
|
1434
1406
|
const normalizedPath = filepath.replace(/\\/g, "/");
|
|
1435
1407
|
const segments = normalizedPath.split("/").filter((s) => s.length > 0);
|
|
1436
1408
|
const filename = segments[segments.length - 1] || "";
|
|
1437
|
-
const ext =
|
|
1409
|
+
const ext = path5.extname(filename);
|
|
1438
1410
|
const project = findProjectForFile(normalizedPath, structure);
|
|
1439
1411
|
const language = EXTENSION_TO_LANGUAGE[ext] || "unknown";
|
|
1440
1412
|
const layer = detectLayer(segments, filename);
|
|
1441
1413
|
const domain = detectDomain(segments);
|
|
1442
1414
|
const scope = detectScope(segments, project, layer);
|
|
1443
|
-
|
|
1444
|
-
if (fileContent) {
|
|
1445
|
-
framework = detectFramework(fileContent);
|
|
1446
|
-
}
|
|
1415
|
+
const framework = fileContent ? detectFramework(fileContent) : undefined;
|
|
1447
1416
|
return {
|
|
1448
1417
|
filepath: normalizedPath,
|
|
1449
1418
|
project,
|
|
@@ -1456,21 +1425,81 @@ function introspectFile(filepath, structure, fileContent) {
|
|
|
1456
1425
|
pathSegments: segments.slice(0, -1)
|
|
1457
1426
|
};
|
|
1458
1427
|
}
|
|
1428
|
+
function introspectionToKeywords(intro) {
|
|
1429
|
+
const keywords = [];
|
|
1430
|
+
const filename = path5.basename(intro.filepath);
|
|
1431
|
+
const filenameWithoutExt = filename.replace(/\.[^.]+$/, "");
|
|
1432
|
+
const filenameParts = filenameWithoutExt.split(/[-_.]/).flatMap((part) => part.split(/(?=[A-Z])/)).map((part) => part.toLowerCase()).filter((part) => part.length > 1);
|
|
1433
|
+
keywords.push(...filenameParts);
|
|
1434
|
+
keywords.push(filenameWithoutExt.toLowerCase());
|
|
1435
|
+
if (intro.project.name && intro.project.name !== "root") {
|
|
1436
|
+
keywords.push(intro.project.name.toLowerCase());
|
|
1437
|
+
}
|
|
1438
|
+
if (intro.scope !== "unknown")
|
|
1439
|
+
keywords.push(intro.scope);
|
|
1440
|
+
if (intro.layer)
|
|
1441
|
+
keywords.push(intro.layer);
|
|
1442
|
+
if (intro.domain)
|
|
1443
|
+
keywords.push(intro.domain);
|
|
1444
|
+
if (intro.language !== "unknown")
|
|
1445
|
+
keywords.push(intro.language);
|
|
1446
|
+
if (intro.framework)
|
|
1447
|
+
keywords.push(intro.framework);
|
|
1448
|
+
const skipSegments = new Set(["src", "lib", "index"]);
|
|
1449
|
+
for (const segment of intro.pathSegments) {
|
|
1450
|
+
if (!skipSegments.has(segment.toLowerCase()) && segment.length > 2) {
|
|
1451
|
+
keywords.push(segment.toLowerCase());
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
const conventionKeywords = getConventionKeywords(intro.filepath);
|
|
1455
|
+
keywords.push(...conventionKeywords);
|
|
1456
|
+
return [...new Set(keywords)];
|
|
1457
|
+
}
|
|
1458
|
+
function detectScopeFromName(name) {
|
|
1459
|
+
const nameLower = name.toLowerCase();
|
|
1460
|
+
for (const [scope, keywords] of Object.entries(SCOPE_KEYWORDS)) {
|
|
1461
|
+
if (scope === "unknown")
|
|
1462
|
+
continue;
|
|
1463
|
+
for (const keyword of keywords) {
|
|
1464
|
+
if (nameLower.includes(keyword)) {
|
|
1465
|
+
return scope;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return "unknown";
|
|
1470
|
+
}
|
|
1471
|
+
function findProjectForFile(filepath, structure) {
|
|
1472
|
+
const normalizedPath = filepath.replace(/\\/g, "/");
|
|
1473
|
+
const matches = [];
|
|
1474
|
+
for (const project of structure.projects) {
|
|
1475
|
+
if (normalizedPath === project.root || normalizedPath.startsWith(project.root + "/")) {
|
|
1476
|
+
matches.push(project);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
if (matches.length > 0) {
|
|
1480
|
+
return matches.reduce((best, current) => current.root.length > best.root.length ? current : best);
|
|
1481
|
+
}
|
|
1482
|
+
for (const { pattern, type } of PROJECT_PATTERNS) {
|
|
1483
|
+
const match = normalizedPath.match(pattern);
|
|
1484
|
+
if (match) {
|
|
1485
|
+
return { name: match[1], root: match[0], type };
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
return { name: "root", root: "", type: structure.rootType ?? "unknown" };
|
|
1489
|
+
}
|
|
1459
1490
|
function detectLayer(segments, filename) {
|
|
1460
1491
|
const filenameLower = filename.toLowerCase();
|
|
1461
1492
|
for (const [layer, patterns] of Object.entries(LAYER_PATTERNS)) {
|
|
1462
1493
|
for (const pattern of patterns) {
|
|
1463
|
-
if (filenameLower.includes(pattern))
|
|
1494
|
+
if (filenameLower.includes(pattern))
|
|
1464
1495
|
return layer;
|
|
1465
|
-
}
|
|
1466
1496
|
}
|
|
1467
1497
|
}
|
|
1468
1498
|
for (let i = segments.length - 2;i >= 0; i--) {
|
|
1469
1499
|
const segment = segments[i].toLowerCase();
|
|
1470
1500
|
for (const [layer, patterns] of Object.entries(LAYER_PATTERNS)) {
|
|
1471
|
-
if (patterns.includes(segment))
|
|
1501
|
+
if (patterns.includes(segment))
|
|
1472
1502
|
return layer;
|
|
1473
|
-
}
|
|
1474
1503
|
}
|
|
1475
1504
|
}
|
|
1476
1505
|
return;
|
|
@@ -1491,9 +1520,8 @@ function detectDomain(segments) {
|
|
|
1491
1520
|
const segmentLower = segment.toLowerCase();
|
|
1492
1521
|
if (skipSegments.has(segmentLower))
|
|
1493
1522
|
continue;
|
|
1494
|
-
if (DOMAIN_PATTERNS.includes(segmentLower))
|
|
1523
|
+
if (DOMAIN_PATTERNS.includes(segmentLower))
|
|
1495
1524
|
return segmentLower;
|
|
1496
|
-
}
|
|
1497
1525
|
for (const domain of DOMAIN_PATTERNS) {
|
|
1498
1526
|
if (segmentLower.startsWith(domain) || segmentLower.endsWith(domain)) {
|
|
1499
1527
|
return domain;
|
|
@@ -1504,9 +1532,8 @@ function detectDomain(segments) {
|
|
|
1504
1532
|
}
|
|
1505
1533
|
function detectScope(segments, project, layer) {
|
|
1506
1534
|
const projectScope = detectScopeFromName(project.name);
|
|
1507
|
-
if (projectScope !== "unknown")
|
|
1535
|
+
if (projectScope !== "unknown")
|
|
1508
1536
|
return projectScope;
|
|
1509
|
-
}
|
|
1510
1537
|
if (layer) {
|
|
1511
1538
|
switch (layer) {
|
|
1512
1539
|
case "controller":
|
|
@@ -1524,15 +1551,12 @@ function detectScope(segments, project, layer) {
|
|
|
1524
1551
|
}
|
|
1525
1552
|
for (const segment of segments) {
|
|
1526
1553
|
const segmentLower = segment.toLowerCase();
|
|
1527
|
-
if (["server", "api", "backend"].includes(segmentLower))
|
|
1554
|
+
if (["server", "api", "backend"].includes(segmentLower))
|
|
1528
1555
|
return "backend";
|
|
1529
|
-
|
|
1530
|
-
if (["client", "web", "frontend", "ui"].includes(segmentLower)) {
|
|
1556
|
+
if (["client", "web", "frontend", "ui"].includes(segmentLower))
|
|
1531
1557
|
return "frontend";
|
|
1532
|
-
|
|
1533
|
-
if (["shared", "common", "lib", "libs"].includes(segmentLower)) {
|
|
1558
|
+
if (["shared", "common", "lib", "libs"].includes(segmentLower))
|
|
1534
1559
|
return "shared";
|
|
1535
|
-
}
|
|
1536
1560
|
}
|
|
1537
1561
|
return "unknown";
|
|
1538
1562
|
}
|
|
@@ -1546,44 +1570,8 @@ function detectFramework(content) {
|
|
|
1546
1570
|
}
|
|
1547
1571
|
return;
|
|
1548
1572
|
}
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
const filename = path6.basename(intro.filepath);
|
|
1552
|
-
const filenameWithoutExt = filename.replace(/\.[^.]+$/, "");
|
|
1553
|
-
const filenameParts = filenameWithoutExt.split(/[-_.]/).flatMap((part) => part.split(/(?=[A-Z])/)).map((part) => part.toLowerCase()).filter((part) => part.length > 1);
|
|
1554
|
-
keywords.push(...filenameParts);
|
|
1555
|
-
keywords.push(filenameWithoutExt.toLowerCase());
|
|
1556
|
-
if (intro.project.name && intro.project.name !== "root") {
|
|
1557
|
-
keywords.push(intro.project.name.toLowerCase());
|
|
1558
|
-
}
|
|
1559
|
-
if (intro.scope !== "unknown") {
|
|
1560
|
-
keywords.push(intro.scope);
|
|
1561
|
-
}
|
|
1562
|
-
if (intro.layer) {
|
|
1563
|
-
keywords.push(intro.layer);
|
|
1564
|
-
}
|
|
1565
|
-
if (intro.domain) {
|
|
1566
|
-
keywords.push(intro.domain);
|
|
1567
|
-
}
|
|
1568
|
-
if (intro.language !== "unknown") {
|
|
1569
|
-
keywords.push(intro.language);
|
|
1570
|
-
}
|
|
1571
|
-
if (intro.framework) {
|
|
1572
|
-
keywords.push(intro.framework);
|
|
1573
|
-
}
|
|
1574
|
-
const skipSegments = new Set(["src", "lib", "index"]);
|
|
1575
|
-
for (const segment of intro.pathSegments) {
|
|
1576
|
-
if (!skipSegments.has(segment.toLowerCase()) && segment.length > 2) {
|
|
1577
|
-
keywords.push(segment.toLowerCase());
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
const conventionKeywords = getConventionKeywords(intro.filepath);
|
|
1581
|
-
keywords.push(...conventionKeywords);
|
|
1582
|
-
return [...new Set(keywords)];
|
|
1583
|
-
}
|
|
1584
|
-
var LAYER_PATTERNS, DOMAIN_PATTERNS, FRAMEWORK_INDICATORS, EXTENSION_TO_LANGUAGE;
|
|
1585
|
-
var init_fileIntrospector = __esm(() => {
|
|
1586
|
-
init_projectDetector();
|
|
1573
|
+
var LAYER_PATTERNS, DOMAIN_PATTERNS, FRAMEWORK_INDICATORS, EXTENSION_TO_LANGUAGE, SCOPE_KEYWORDS, PROJECT_PATTERNS;
|
|
1574
|
+
var init_introspection = __esm(() => {
|
|
1587
1575
|
init_conventions();
|
|
1588
1576
|
LAYER_PATTERNS = {
|
|
1589
1577
|
controller: ["controller", "api", "routes", "route", "handler"],
|
|
@@ -1702,120 +1690,45 @@ var init_fileIntrospector = __esm(() => {
|
|
|
1702
1690
|
".md": "markdown",
|
|
1703
1691
|
".json": "json",
|
|
1704
1692
|
".yaml": "yaml",
|
|
1705
|
-
".yml": "yaml"
|
|
1693
|
+
".yml": "yaml",
|
|
1694
|
+
".txt": "text"
|
|
1706
1695
|
};
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
return this.files.get(filepath);
|
|
1744
|
-
}
|
|
1745
|
-
getAllFiles() {
|
|
1746
|
-
return Array.from(this.files.values());
|
|
1747
|
-
}
|
|
1748
|
-
applyOverrides(intro) {
|
|
1749
|
-
if (!this.config.projects)
|
|
1750
|
-
return;
|
|
1751
|
-
for (const [projectPath, overrides] of Object.entries(this.config.projects)) {
|
|
1752
|
-
if (intro.filepath.startsWith(projectPath + "/") || intro.project.root === projectPath) {
|
|
1753
|
-
if (overrides.scope) {
|
|
1754
|
-
intro.scope = overrides.scope;
|
|
1755
|
-
}
|
|
1756
|
-
if (overrides.framework) {
|
|
1757
|
-
intro.framework = overrides.framework;
|
|
1758
|
-
}
|
|
1759
|
-
break;
|
|
1760
|
-
}
|
|
1761
|
-
}
|
|
1762
|
-
}
|
|
1763
|
-
async save(config) {
|
|
1764
|
-
const introDir = path7.join(getRaggrepDir(this.rootDir, config), "introspection");
|
|
1765
|
-
await fs3.mkdir(introDir, { recursive: true });
|
|
1766
|
-
const projectPath = path7.join(introDir, "_project.json");
|
|
1767
|
-
await fs3.writeFile(projectPath, JSON.stringify({
|
|
1768
|
-
version: "1.0.0",
|
|
1769
|
-
lastUpdated: new Date().toISOString(),
|
|
1770
|
-
structure: this.structure
|
|
1771
|
-
}, null, 2));
|
|
1772
|
-
for (const [filepath, intro] of this.files) {
|
|
1773
|
-
const introFilePath = path7.join(introDir, "files", filepath.replace(/\.[^.]+$/, ".json"));
|
|
1774
|
-
await fs3.mkdir(path7.dirname(introFilePath), { recursive: true });
|
|
1775
|
-
await fs3.writeFile(introFilePath, JSON.stringify(intro, null, 2));
|
|
1776
|
-
}
|
|
1777
|
-
console.log(` [Introspection] Saved metadata for ${this.files.size} files`);
|
|
1778
|
-
}
|
|
1779
|
-
async load(config) {
|
|
1780
|
-
const introDir = path7.join(getRaggrepDir(this.rootDir, config), "introspection");
|
|
1781
|
-
try {
|
|
1782
|
-
const projectPath = path7.join(introDir, "_project.json");
|
|
1783
|
-
const projectContent = await fs3.readFile(projectPath, "utf-8");
|
|
1784
|
-
const projectData = JSON.parse(projectContent);
|
|
1785
|
-
this.structure = projectData.structure;
|
|
1786
|
-
await this.loadFilesRecursive(path7.join(introDir, "files"), "");
|
|
1787
|
-
} catch {
|
|
1788
|
-
this.structure = null;
|
|
1789
|
-
this.files.clear();
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
async loadFilesRecursive(basePath, prefix) {
|
|
1793
|
-
try {
|
|
1794
|
-
const entries = await fs3.readdir(basePath, { withFileTypes: true });
|
|
1795
|
-
for (const entry of entries) {
|
|
1796
|
-
const entryPath = path7.join(basePath, entry.name);
|
|
1797
|
-
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
1798
|
-
if (entry.isDirectory()) {
|
|
1799
|
-
await this.loadFilesRecursive(entryPath, relativePath);
|
|
1800
|
-
} else if (entry.name.endsWith(".json")) {
|
|
1801
|
-
const content = await fs3.readFile(entryPath, "utf-8");
|
|
1802
|
-
const intro = JSON.parse(content);
|
|
1803
|
-
this.files.set(intro.filepath, intro);
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1806
|
-
} catch {}
|
|
1807
|
-
}
|
|
1808
|
-
clear() {
|
|
1809
|
-
this.files.clear();
|
|
1810
|
-
this.structure = null;
|
|
1811
|
-
}
|
|
1812
|
-
}
|
|
1813
|
-
var init_introspection = __esm(() => {
|
|
1814
|
-
init_projectDetector();
|
|
1815
|
-
init_fileIntrospector();
|
|
1816
|
-
init_config2();
|
|
1817
|
-
init_fileIntrospector();
|
|
1818
|
-
init_projectDetector();
|
|
1696
|
+
SCOPE_KEYWORDS = {
|
|
1697
|
+
frontend: [
|
|
1698
|
+
"web",
|
|
1699
|
+
"webapp",
|
|
1700
|
+
"frontend",
|
|
1701
|
+
"client",
|
|
1702
|
+
"ui",
|
|
1703
|
+
"app",
|
|
1704
|
+
"mobile",
|
|
1705
|
+
"react",
|
|
1706
|
+
"vue",
|
|
1707
|
+
"angular",
|
|
1708
|
+
"next",
|
|
1709
|
+
"nuxt"
|
|
1710
|
+
],
|
|
1711
|
+
backend: [
|
|
1712
|
+
"api",
|
|
1713
|
+
"server",
|
|
1714
|
+
"backend",
|
|
1715
|
+
"service",
|
|
1716
|
+
"worker",
|
|
1717
|
+
"lambda",
|
|
1718
|
+
"functions"
|
|
1719
|
+
],
|
|
1720
|
+
shared: ["shared", "common", "utils", "lib", "core", "types", "models"],
|
|
1721
|
+
tooling: ["scripts", "tools", "cli", "devtools", "build", "config", "infra"],
|
|
1722
|
+
unknown: []
|
|
1723
|
+
};
|
|
1724
|
+
PROJECT_PATTERNS = [
|
|
1725
|
+
{ pattern: /^apps\/([^/]+)/, type: "app", defaultScope: "unknown" },
|
|
1726
|
+
{ pattern: /^packages\/([^/]+)/, type: "library", defaultScope: "shared" },
|
|
1727
|
+
{ pattern: /^libs\/([^/]+)/, type: "library", defaultScope: "shared" },
|
|
1728
|
+
{ pattern: /^services\/([^/]+)/, type: "service", defaultScope: "backend" },
|
|
1729
|
+
{ pattern: /^scripts\/([^/]+)/, type: "script", defaultScope: "tooling" },
|
|
1730
|
+
{ pattern: /^tools\/([^/]+)/, type: "script", defaultScope: "tooling" }
|
|
1731
|
+
];
|
|
1819
1732
|
});
|
|
1820
1733
|
|
|
1821
1734
|
// src/modules/core/symbols.ts
|
|
@@ -1985,8 +1898,8 @@ var exports_core = {};
|
|
|
1985
1898
|
__export(exports_core, {
|
|
1986
1899
|
CoreModule: () => CoreModule
|
|
1987
1900
|
});
|
|
1988
|
-
import * as
|
|
1989
|
-
import * as
|
|
1901
|
+
import * as path6 from "path";
|
|
1902
|
+
import * as fs2 from "fs/promises";
|
|
1990
1903
|
|
|
1991
1904
|
class CoreModule {
|
|
1992
1905
|
id = "core";
|
|
@@ -2004,7 +1917,9 @@ class CoreModule {
|
|
|
2004
1917
|
const contentTokens = tokenize(content);
|
|
2005
1918
|
const intro = ctx.getIntrospection?.(filepath);
|
|
2006
1919
|
const introKeywords = intro ? introspectionToKeywords(intro) : [];
|
|
2007
|
-
const allTokens = [
|
|
1920
|
+
const allTokens = [
|
|
1921
|
+
...new Set([...contentTokens, ...symbolKeywords, ...introKeywords])
|
|
1922
|
+
];
|
|
2008
1923
|
const chunks = this.createChunks(filepath, content, symbols);
|
|
2009
1924
|
const stats = await ctx.getFileStats(filepath);
|
|
2010
1925
|
this.symbolIndex.set(filepath, {
|
|
@@ -2078,8 +1993,8 @@ class CoreModule {
|
|
|
2078
1993
|
}
|
|
2079
1994
|
async finalize(ctx) {
|
|
2080
1995
|
const config = ctx.config;
|
|
2081
|
-
const coreDir =
|
|
2082
|
-
await
|
|
1996
|
+
const coreDir = path6.join(getRaggrepDir(ctx.rootDir, config), "index", "core");
|
|
1997
|
+
await fs2.mkdir(coreDir, { recursive: true });
|
|
2083
1998
|
this.bm25Index = new BM25Index;
|
|
2084
1999
|
for (const [filepath, entry] of this.symbolIndex) {
|
|
2085
2000
|
this.bm25Index.addDocument(filepath, entry.tokens);
|
|
@@ -2090,7 +2005,7 @@ class CoreModule {
|
|
|
2090
2005
|
files: Object.fromEntries(this.symbolIndex),
|
|
2091
2006
|
bm25Data: this.bm25Index.serialize()
|
|
2092
2007
|
};
|
|
2093
|
-
await
|
|
2008
|
+
await fs2.writeFile(path6.join(coreDir, "symbols.json"), JSON.stringify(symbolIndexData, null, 2));
|
|
2094
2009
|
console.log(` [Core] Symbol index built with ${this.symbolIndex.size} files`);
|
|
2095
2010
|
}
|
|
2096
2011
|
async search(query, ctx, options) {
|
|
@@ -2187,10 +2102,10 @@ class CoreModule {
|
|
|
2187
2102
|
return bestChunk;
|
|
2188
2103
|
}
|
|
2189
2104
|
async loadSymbolIndex(rootDir, config) {
|
|
2190
|
-
const coreDir =
|
|
2191
|
-
const symbolsPath =
|
|
2105
|
+
const coreDir = path6.join(getRaggrepDir(rootDir, config), "index", "core");
|
|
2106
|
+
const symbolsPath = path6.join(coreDir, "symbols.json");
|
|
2192
2107
|
try {
|
|
2193
|
-
const content = await
|
|
2108
|
+
const content = await fs2.readFile(symbolsPath, "utf-8");
|
|
2194
2109
|
const data = JSON.parse(content);
|
|
2195
2110
|
this.symbolIndex = new Map(Object.entries(data.files));
|
|
2196
2111
|
if (data.bm25Data) {
|
|
@@ -2617,8 +2532,8 @@ var init_keywords = __esm(() => {
|
|
|
2617
2532
|
});
|
|
2618
2533
|
|
|
2619
2534
|
// src/infrastructure/storage/symbolicIndex.ts
|
|
2620
|
-
import * as
|
|
2621
|
-
import * as
|
|
2535
|
+
import * as fs3 from "fs/promises";
|
|
2536
|
+
import * as path7 from "path";
|
|
2622
2537
|
|
|
2623
2538
|
class SymbolicIndex {
|
|
2624
2539
|
meta = null;
|
|
@@ -2627,7 +2542,7 @@ class SymbolicIndex {
|
|
|
2627
2542
|
symbolicPath;
|
|
2628
2543
|
moduleId;
|
|
2629
2544
|
constructor(indexDir, moduleId) {
|
|
2630
|
-
this.symbolicPath =
|
|
2545
|
+
this.symbolicPath = path7.join(indexDir, "index", moduleId, "symbolic");
|
|
2631
2546
|
this.moduleId = moduleId;
|
|
2632
2547
|
}
|
|
2633
2548
|
async initialize() {
|
|
@@ -2687,18 +2602,18 @@ class SymbolicIndex {
|
|
|
2687
2602
|
throw new Error("Index not initialized");
|
|
2688
2603
|
this.meta.lastUpdated = new Date().toISOString();
|
|
2689
2604
|
this.meta.fileCount = this.fileSummaries.size;
|
|
2690
|
-
await
|
|
2691
|
-
const metaPath =
|
|
2692
|
-
await
|
|
2605
|
+
await fs3.mkdir(this.symbolicPath, { recursive: true });
|
|
2606
|
+
const metaPath = path7.join(this.symbolicPath, "_meta.json");
|
|
2607
|
+
await fs3.writeFile(metaPath, JSON.stringify(this.meta, null, 2));
|
|
2693
2608
|
for (const [filepath, summary] of this.fileSummaries) {
|
|
2694
2609
|
const summaryPath = this.getFileSummaryPath(filepath);
|
|
2695
|
-
await
|
|
2696
|
-
await
|
|
2610
|
+
await fs3.mkdir(path7.dirname(summaryPath), { recursive: true });
|
|
2611
|
+
await fs3.writeFile(summaryPath, JSON.stringify(summary, null, 2));
|
|
2697
2612
|
}
|
|
2698
2613
|
}
|
|
2699
2614
|
async load() {
|
|
2700
|
-
const metaPath =
|
|
2701
|
-
const metaContent = await
|
|
2615
|
+
const metaPath = path7.join(this.symbolicPath, "_meta.json");
|
|
2616
|
+
const metaContent = await fs3.readFile(metaPath, "utf-8");
|
|
2702
2617
|
this.meta = JSON.parse(metaContent);
|
|
2703
2618
|
this.fileSummaries.clear();
|
|
2704
2619
|
await this.loadFileSummariesRecursive(this.symbolicPath);
|
|
@@ -2706,14 +2621,14 @@ class SymbolicIndex {
|
|
|
2706
2621
|
}
|
|
2707
2622
|
async loadFileSummariesRecursive(dir) {
|
|
2708
2623
|
try {
|
|
2709
|
-
const entries = await
|
|
2624
|
+
const entries = await fs3.readdir(dir, { withFileTypes: true });
|
|
2710
2625
|
for (const entry of entries) {
|
|
2711
|
-
const fullPath =
|
|
2626
|
+
const fullPath = path7.join(dir, entry.name);
|
|
2712
2627
|
if (entry.isDirectory()) {
|
|
2713
2628
|
await this.loadFileSummariesRecursive(fullPath);
|
|
2714
2629
|
} else if (entry.name.endsWith(".json") && entry.name !== "_meta.json") {
|
|
2715
2630
|
try {
|
|
2716
|
-
const content = await
|
|
2631
|
+
const content = await fs3.readFile(fullPath, "utf-8");
|
|
2717
2632
|
const summary = JSON.parse(content);
|
|
2718
2633
|
if (summary.filepath) {
|
|
2719
2634
|
this.fileSummaries.set(summary.filepath, summary);
|
|
@@ -2725,18 +2640,18 @@ class SymbolicIndex {
|
|
|
2725
2640
|
}
|
|
2726
2641
|
getFileSummaryPath(filepath) {
|
|
2727
2642
|
const jsonPath = filepath.replace(/\.[^.]+$/, ".json");
|
|
2728
|
-
return
|
|
2643
|
+
return path7.join(this.symbolicPath, jsonPath);
|
|
2729
2644
|
}
|
|
2730
2645
|
async deleteFileSummary(filepath) {
|
|
2731
2646
|
try {
|
|
2732
|
-
await
|
|
2647
|
+
await fs3.unlink(this.getFileSummaryPath(filepath));
|
|
2733
2648
|
} catch {}
|
|
2734
2649
|
this.fileSummaries.delete(filepath);
|
|
2735
2650
|
}
|
|
2736
2651
|
async exists() {
|
|
2737
2652
|
try {
|
|
2738
|
-
const metaPath =
|
|
2739
|
-
await
|
|
2653
|
+
const metaPath = path7.join(this.symbolicPath, "_meta.json");
|
|
2654
|
+
await fs3.access(metaPath);
|
|
2740
2655
|
return true;
|
|
2741
2656
|
} catch {
|
|
2742
2657
|
return false;
|
|
@@ -2775,7 +2690,7 @@ __export(exports_typescript, {
|
|
|
2775
2690
|
DEFAULT_TOP_K: () => DEFAULT_TOP_K2,
|
|
2776
2691
|
DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE2
|
|
2777
2692
|
});
|
|
2778
|
-
import * as
|
|
2693
|
+
import * as path8 from "path";
|
|
2779
2694
|
|
|
2780
2695
|
class TypeScriptModule {
|
|
2781
2696
|
id = "language/typescript";
|
|
@@ -2918,112 +2833,376 @@ class TypeScriptModule {
|
|
|
2918
2833
|
for (const result of bm25Results) {
|
|
2919
2834
|
bm25Scores.set(result.id, normalizeScore(result.score, 3));
|
|
2920
2835
|
}
|
|
2921
|
-
const queryTerms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 2);
|
|
2922
|
-
const pathBoosts = new Map;
|
|
2923
|
-
for (const filepath of candidateFiles) {
|
|
2924
|
-
const summary = symbolicIndex.getFileSummary(filepath);
|
|
2925
|
-
if (summary?.pathContext) {
|
|
2926
|
-
let boost = 0;
|
|
2927
|
-
const ctx2 = summary.pathContext;
|
|
2928
|
-
if (ctx2.domain && queryTerms.some((t) => ctx2.domain.includes(t) || t.includes(ctx2.domain))) {
|
|
2929
|
-
boost += 0.1;
|
|
2930
|
-
}
|
|
2931
|
-
if (ctx2.layer && queryTerms.some((t) => ctx2.layer.includes(t) || t.includes(ctx2.layer))) {
|
|
2932
|
-
boost += 0.05;
|
|
2836
|
+
const queryTerms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 2);
|
|
2837
|
+
const pathBoosts = new Map;
|
|
2838
|
+
for (const filepath of candidateFiles) {
|
|
2839
|
+
const summary = symbolicIndex.getFileSummary(filepath);
|
|
2840
|
+
if (summary?.pathContext) {
|
|
2841
|
+
let boost = 0;
|
|
2842
|
+
const ctx2 = summary.pathContext;
|
|
2843
|
+
if (ctx2.domain && queryTerms.some((t) => ctx2.domain.includes(t) || t.includes(ctx2.domain))) {
|
|
2844
|
+
boost += 0.1;
|
|
2845
|
+
}
|
|
2846
|
+
if (ctx2.layer && queryTerms.some((t) => ctx2.layer.includes(t) || t.includes(ctx2.layer))) {
|
|
2847
|
+
boost += 0.05;
|
|
2848
|
+
}
|
|
2849
|
+
const segmentMatch = ctx2.segments.some((seg) => queryTerms.some((t) => seg.toLowerCase().includes(t) || t.includes(seg.toLowerCase())));
|
|
2850
|
+
if (segmentMatch) {
|
|
2851
|
+
boost += 0.05;
|
|
2852
|
+
}
|
|
2853
|
+
pathBoosts.set(filepath, boost);
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
const results = [];
|
|
2857
|
+
for (const { filepath, chunk, embedding } of allChunksData) {
|
|
2858
|
+
const semanticScore = cosineSimilarity(queryEmbedding, embedding);
|
|
2859
|
+
const bm25Score = bm25Scores.get(chunk.id) || 0;
|
|
2860
|
+
const pathBoost = pathBoosts.get(filepath) || 0;
|
|
2861
|
+
const hybridScore = SEMANTIC_WEIGHT * semanticScore + BM25_WEIGHT * bm25Score + pathBoost;
|
|
2862
|
+
if (hybridScore >= minScore || bm25Score > 0.3) {
|
|
2863
|
+
results.push({
|
|
2864
|
+
filepath,
|
|
2865
|
+
chunk,
|
|
2866
|
+
score: hybridScore,
|
|
2867
|
+
moduleId: this.id,
|
|
2868
|
+
context: {
|
|
2869
|
+
semanticScore,
|
|
2870
|
+
bm25Score,
|
|
2871
|
+
pathBoost
|
|
2872
|
+
}
|
|
2873
|
+
});
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
results.sort((a, b) => b.score - a.score);
|
|
2877
|
+
return results.slice(0, topK);
|
|
2878
|
+
}
|
|
2879
|
+
extractReferences(content, filepath) {
|
|
2880
|
+
const references = [];
|
|
2881
|
+
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
2882
|
+
const requireRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
2883
|
+
let match;
|
|
2884
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
2885
|
+
const importPath = match[1];
|
|
2886
|
+
if (importPath.startsWith(".")) {
|
|
2887
|
+
const dir = path8.dirname(filepath);
|
|
2888
|
+
const resolved = path8.normalize(path8.join(dir, importPath));
|
|
2889
|
+
references.push(resolved);
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
while ((match = requireRegex.exec(content)) !== null) {
|
|
2893
|
+
const importPath = match[1];
|
|
2894
|
+
if (importPath.startsWith(".")) {
|
|
2895
|
+
const dir = path8.dirname(filepath);
|
|
2896
|
+
const resolved = path8.normalize(path8.join(dir, importPath));
|
|
2897
|
+
references.push(resolved);
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
return references;
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
var DEFAULT_MIN_SCORE2 = 0.15, DEFAULT_TOP_K2 = 10, SEMANTIC_WEIGHT = 0.7, BM25_WEIGHT = 0.3, TIER1_CANDIDATE_MULTIPLIER = 3;
|
|
2904
|
+
var init_typescript = __esm(() => {
|
|
2905
|
+
init_embeddings();
|
|
2906
|
+
init_config2();
|
|
2907
|
+
init_parseCode();
|
|
2908
|
+
init_storage();
|
|
2909
|
+
init_keywords();
|
|
2910
|
+
init_keywords();
|
|
2911
|
+
});
|
|
2912
|
+
|
|
2913
|
+
// src/modules/registry.ts
|
|
2914
|
+
class ModuleRegistryImpl {
|
|
2915
|
+
modules = new Map;
|
|
2916
|
+
register(module) {
|
|
2917
|
+
if (this.modules.has(module.id)) {
|
|
2918
|
+
console.warn(`Module '${module.id}' is already registered, overwriting...`);
|
|
2919
|
+
}
|
|
2920
|
+
this.modules.set(module.id, module);
|
|
2921
|
+
}
|
|
2922
|
+
get(id) {
|
|
2923
|
+
return this.modules.get(id);
|
|
2924
|
+
}
|
|
2925
|
+
list() {
|
|
2926
|
+
return Array.from(this.modules.values());
|
|
2927
|
+
}
|
|
2928
|
+
getEnabled(config) {
|
|
2929
|
+
const enabledIds = new Set(config.modules.filter((m) => m.enabled).map((m) => m.id));
|
|
2930
|
+
return this.list().filter((m) => enabledIds.has(m.id));
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
async function registerBuiltInModules() {
|
|
2934
|
+
const { CoreModule: CoreModule2 } = await Promise.resolve().then(() => (init_core(), exports_core));
|
|
2935
|
+
const { TypeScriptModule: TypeScriptModule2 } = await Promise.resolve().then(() => (init_typescript(), exports_typescript));
|
|
2936
|
+
registry.register(new CoreModule2);
|
|
2937
|
+
registry.register(new TypeScriptModule2);
|
|
2938
|
+
}
|
|
2939
|
+
var registry;
|
|
2940
|
+
var init_registry = __esm(() => {
|
|
2941
|
+
registry = new ModuleRegistryImpl;
|
|
2942
|
+
});
|
|
2943
|
+
|
|
2944
|
+
// src/infrastructure/introspection/projectDetector.ts
|
|
2945
|
+
import * as path9 from "path";
|
|
2946
|
+
import * as fs4 from "fs/promises";
|
|
2947
|
+
async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
|
|
2948
|
+
if (depth > MAX_SCAN_DEPTH)
|
|
2949
|
+
return [];
|
|
2950
|
+
const results = [];
|
|
2951
|
+
const fullDir = currentDir ? path9.join(rootDir, currentDir) : rootDir;
|
|
2952
|
+
try {
|
|
2953
|
+
const entries = await fs4.readdir(fullDir, { withFileTypes: true });
|
|
2954
|
+
const hasPackageJson = entries.some((e) => e.isFile() && e.name === "package.json");
|
|
2955
|
+
if (hasPackageJson && currentDir) {
|
|
2956
|
+
const info = await parsePackageJson(rootDir, currentDir);
|
|
2957
|
+
if (info)
|
|
2958
|
+
results.push(info);
|
|
2959
|
+
}
|
|
2960
|
+
for (const entry of entries) {
|
|
2961
|
+
if (!entry.isDirectory())
|
|
2962
|
+
continue;
|
|
2963
|
+
if (SKIP_DIRS.has(entry.name))
|
|
2964
|
+
continue;
|
|
2965
|
+
const subPath = currentDir ? `${currentDir}/${entry.name}` : entry.name;
|
|
2966
|
+
const subResults = await scanForPackageJsons(rootDir, subPath, depth + 1);
|
|
2967
|
+
results.push(...subResults);
|
|
2968
|
+
}
|
|
2969
|
+
} catch {}
|
|
2970
|
+
return results;
|
|
2971
|
+
}
|
|
2972
|
+
async function parsePackageJson(rootDir, relativePath) {
|
|
2973
|
+
try {
|
|
2974
|
+
const packageJsonPath = path9.join(rootDir, relativePath, "package.json");
|
|
2975
|
+
const content = await fs4.readFile(packageJsonPath, "utf-8");
|
|
2976
|
+
const pkg = JSON.parse(content);
|
|
2977
|
+
const name = pkg.name || path9.basename(relativePath);
|
|
2978
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2979
|
+
let type = "unknown";
|
|
2980
|
+
if (deps["next"] || deps["react"] || deps["vue"] || deps["svelte"]) {
|
|
2981
|
+
type = "app";
|
|
2982
|
+
} else if (deps["express"] || deps["fastify"] || deps["koa"] || deps["hono"]) {
|
|
2983
|
+
type = "service";
|
|
2984
|
+
} else if (pkg.main || pkg.exports) {
|
|
2985
|
+
type = "library";
|
|
2986
|
+
}
|
|
2987
|
+
const hasWorkspaces = Boolean(pkg.workspaces);
|
|
2988
|
+
return { name, relativePath, type, hasWorkspaces };
|
|
2989
|
+
} catch {
|
|
2990
|
+
return null;
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
function getProjectType(patternDir) {
|
|
2994
|
+
switch (patternDir) {
|
|
2995
|
+
case "apps":
|
|
2996
|
+
return "app";
|
|
2997
|
+
case "packages":
|
|
2998
|
+
case "libs":
|
|
2999
|
+
return "library";
|
|
3000
|
+
case "services":
|
|
3001
|
+
return "service";
|
|
3002
|
+
case "scripts":
|
|
3003
|
+
case "tools":
|
|
3004
|
+
return "script";
|
|
3005
|
+
default:
|
|
3006
|
+
return "unknown";
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
async function detectProjectStructure(rootDir) {
|
|
3010
|
+
const projectMap = new Map;
|
|
3011
|
+
let isMonorepo = false;
|
|
3012
|
+
try {
|
|
3013
|
+
const entries = await fs4.readdir(rootDir, { withFileTypes: true });
|
|
3014
|
+
const dirNames = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
3015
|
+
const monorepoPatterns = ["apps", "packages", "libs", "services"];
|
|
3016
|
+
const hasMonorepoStructure = monorepoPatterns.some((p) => dirNames.includes(p));
|
|
3017
|
+
if (hasMonorepoStructure) {
|
|
3018
|
+
isMonorepo = true;
|
|
3019
|
+
for (const pattern of monorepoPatterns) {
|
|
3020
|
+
if (!dirNames.includes(pattern))
|
|
3021
|
+
continue;
|
|
3022
|
+
const patternDir = path9.join(rootDir, pattern);
|
|
3023
|
+
try {
|
|
3024
|
+
const subDirs = await fs4.readdir(patternDir, { withFileTypes: true });
|
|
3025
|
+
for (const subDir of subDirs) {
|
|
3026
|
+
if (!subDir.isDirectory())
|
|
3027
|
+
continue;
|
|
3028
|
+
const projectRoot = `${pattern}/${subDir.name}`;
|
|
3029
|
+
const type = getProjectType(pattern);
|
|
3030
|
+
projectMap.set(projectRoot, {
|
|
3031
|
+
name: subDir.name,
|
|
3032
|
+
root: projectRoot,
|
|
3033
|
+
type
|
|
3034
|
+
});
|
|
3035
|
+
}
|
|
3036
|
+
} catch {}
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
const packageJsons = await scanForPackageJsons(rootDir);
|
|
3040
|
+
for (const pkg of packageJsons) {
|
|
3041
|
+
if (pkg.hasWorkspaces)
|
|
3042
|
+
isMonorepo = true;
|
|
3043
|
+
if (packageJsons.length > 1)
|
|
3044
|
+
isMonorepo = true;
|
|
3045
|
+
projectMap.set(pkg.relativePath, {
|
|
3046
|
+
name: pkg.name,
|
|
3047
|
+
root: pkg.relativePath,
|
|
3048
|
+
type: pkg.type
|
|
3049
|
+
});
|
|
3050
|
+
}
|
|
3051
|
+
let rootType = "unknown";
|
|
3052
|
+
try {
|
|
3053
|
+
const rootPkgPath = path9.join(rootDir, "package.json");
|
|
3054
|
+
const rootPkg = JSON.parse(await fs4.readFile(rootPkgPath, "utf-8"));
|
|
3055
|
+
if (rootPkg.workspaces)
|
|
3056
|
+
isMonorepo = true;
|
|
3057
|
+
const deps = { ...rootPkg.dependencies, ...rootPkg.devDependencies };
|
|
3058
|
+
if (deps["next"] || deps["react"] || deps["vue"]) {
|
|
3059
|
+
rootType = "app";
|
|
3060
|
+
} else if (deps["express"] || deps["fastify"] || deps["koa"]) {
|
|
3061
|
+
rootType = "service";
|
|
3062
|
+
}
|
|
3063
|
+
} catch {}
|
|
3064
|
+
const projects = Array.from(projectMap.values()).sort((a, b) => a.root.length - b.root.length);
|
|
3065
|
+
return {
|
|
3066
|
+
projects,
|
|
3067
|
+
isMonorepo,
|
|
3068
|
+
rootType: isMonorepo ? undefined : rootType
|
|
3069
|
+
};
|
|
3070
|
+
} catch {
|
|
3071
|
+
return {
|
|
3072
|
+
projects: [],
|
|
3073
|
+
isMonorepo: false,
|
|
3074
|
+
rootType: "unknown"
|
|
3075
|
+
};
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
var MAX_SCAN_DEPTH = 4, SKIP_DIRS;
|
|
3079
|
+
var init_projectDetector = __esm(() => {
|
|
3080
|
+
SKIP_DIRS = new Set([
|
|
3081
|
+
"node_modules",
|
|
3082
|
+
".git",
|
|
3083
|
+
"dist",
|
|
3084
|
+
"build",
|
|
3085
|
+
".next",
|
|
3086
|
+
".nuxt",
|
|
3087
|
+
"coverage",
|
|
3088
|
+
".raggrep"
|
|
3089
|
+
]);
|
|
3090
|
+
});
|
|
3091
|
+
|
|
3092
|
+
// src/infrastructure/introspection/IntrospectionIndex.ts
|
|
3093
|
+
import * as path10 from "path";
|
|
3094
|
+
import * as fs5 from "fs/promises";
|
|
3095
|
+
|
|
3096
|
+
class IntrospectionIndex {
|
|
3097
|
+
rootDir;
|
|
3098
|
+
structure = null;
|
|
3099
|
+
files = new Map;
|
|
3100
|
+
config = {};
|
|
3101
|
+
constructor(rootDir) {
|
|
3102
|
+
this.rootDir = rootDir;
|
|
3103
|
+
}
|
|
3104
|
+
async initialize() {
|
|
3105
|
+
this.structure = await detectProjectStructure(this.rootDir);
|
|
3106
|
+
try {
|
|
3107
|
+
const configPath = path10.join(this.rootDir, ".raggrep", "config.json");
|
|
3108
|
+
const configContent = await fs5.readFile(configPath, "utf-8");
|
|
3109
|
+
const config = JSON.parse(configContent);
|
|
3110
|
+
this.config = config.introspection || {};
|
|
3111
|
+
} catch {}
|
|
3112
|
+
}
|
|
3113
|
+
getStructure() {
|
|
3114
|
+
return this.structure;
|
|
3115
|
+
}
|
|
3116
|
+
addFile(filepath, content) {
|
|
3117
|
+
if (!this.structure) {
|
|
3118
|
+
throw new Error("IntrospectionIndex not initialized");
|
|
3119
|
+
}
|
|
3120
|
+
const intro = introspectFile(filepath, this.structure, content);
|
|
3121
|
+
this.applyOverrides(intro);
|
|
3122
|
+
this.files.set(filepath, intro);
|
|
3123
|
+
return intro;
|
|
3124
|
+
}
|
|
3125
|
+
getFile(filepath) {
|
|
3126
|
+
return this.files.get(filepath);
|
|
3127
|
+
}
|
|
3128
|
+
getAllFiles() {
|
|
3129
|
+
return Array.from(this.files.values());
|
|
3130
|
+
}
|
|
3131
|
+
applyOverrides(intro) {
|
|
3132
|
+
if (!this.config.projects)
|
|
3133
|
+
return;
|
|
3134
|
+
for (const [projectPath, overrides] of Object.entries(this.config.projects)) {
|
|
3135
|
+
if (intro.filepath.startsWith(projectPath + "/") || intro.project.root === projectPath) {
|
|
3136
|
+
if (overrides.scope) {
|
|
3137
|
+
intro.scope = overrides.scope;
|
|
2933
3138
|
}
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
boost += 0.05;
|
|
3139
|
+
if (overrides.framework) {
|
|
3140
|
+
intro.framework = overrides.framework;
|
|
2937
3141
|
}
|
|
2938
|
-
|
|
3142
|
+
break;
|
|
2939
3143
|
}
|
|
2940
3144
|
}
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
bm25Score,
|
|
2956
|
-
pathBoost
|
|
2957
|
-
}
|
|
2958
|
-
});
|
|
2959
|
-
}
|
|
3145
|
+
}
|
|
3146
|
+
async save(config) {
|
|
3147
|
+
const introDir = path10.join(getRaggrepDir(this.rootDir, config), "introspection");
|
|
3148
|
+
await fs5.mkdir(introDir, { recursive: true });
|
|
3149
|
+
const projectPath = path10.join(introDir, "_project.json");
|
|
3150
|
+
await fs5.writeFile(projectPath, JSON.stringify({
|
|
3151
|
+
version: "1.0.0",
|
|
3152
|
+
lastUpdated: new Date().toISOString(),
|
|
3153
|
+
structure: this.structure
|
|
3154
|
+
}, null, 2));
|
|
3155
|
+
for (const [filepath, intro] of this.files) {
|
|
3156
|
+
const introFilePath = path10.join(introDir, "files", filepath.replace(/\.[^.]+$/, ".json"));
|
|
3157
|
+
await fs5.mkdir(path10.dirname(introFilePath), { recursive: true });
|
|
3158
|
+
await fs5.writeFile(introFilePath, JSON.stringify(intro, null, 2));
|
|
2960
3159
|
}
|
|
2961
|
-
|
|
2962
|
-
return results.slice(0, topK);
|
|
3160
|
+
console.log(` [Introspection] Saved metadata for ${this.files.size} files`);
|
|
2963
3161
|
}
|
|
2964
|
-
|
|
2965
|
-
const
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
}
|
|
3162
|
+
async load(config) {
|
|
3163
|
+
const introDir = path10.join(getRaggrepDir(this.rootDir, config), "introspection");
|
|
3164
|
+
try {
|
|
3165
|
+
const projectPath = path10.join(introDir, "_project.json");
|
|
3166
|
+
const projectContent = await fs5.readFile(projectPath, "utf-8");
|
|
3167
|
+
const projectData = JSON.parse(projectContent);
|
|
3168
|
+
this.structure = projectData.structure;
|
|
3169
|
+
await this.loadFilesRecursive(path10.join(introDir, "files"), "");
|
|
3170
|
+
} catch {
|
|
3171
|
+
this.structure = null;
|
|
3172
|
+
this.files.clear();
|
|
2976
3173
|
}
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
3174
|
+
}
|
|
3175
|
+
async loadFilesRecursive(basePath, prefix) {
|
|
3176
|
+
try {
|
|
3177
|
+
const entries = await fs5.readdir(basePath, { withFileTypes: true });
|
|
3178
|
+
for (const entry of entries) {
|
|
3179
|
+
const entryPath = path10.join(basePath, entry.name);
|
|
3180
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
3181
|
+
if (entry.isDirectory()) {
|
|
3182
|
+
await this.loadFilesRecursive(entryPath, relativePath);
|
|
3183
|
+
} else if (entry.name.endsWith(".json")) {
|
|
3184
|
+
const content = await fs5.readFile(entryPath, "utf-8");
|
|
3185
|
+
const intro = JSON.parse(content);
|
|
3186
|
+
this.files.set(intro.filepath, intro);
|
|
3187
|
+
}
|
|
2983
3188
|
}
|
|
2984
|
-
}
|
|
2985
|
-
|
|
3189
|
+
} catch {}
|
|
3190
|
+
}
|
|
3191
|
+
clear() {
|
|
3192
|
+
this.files.clear();
|
|
3193
|
+
this.structure = null;
|
|
2986
3194
|
}
|
|
2987
3195
|
}
|
|
2988
|
-
var
|
|
2989
|
-
|
|
2990
|
-
|
|
3196
|
+
var init_IntrospectionIndex = __esm(() => {
|
|
3197
|
+
init_projectDetector();
|
|
3198
|
+
init_introspection();
|
|
2991
3199
|
init_config2();
|
|
2992
|
-
init_parseCode();
|
|
2993
|
-
init_storage();
|
|
2994
|
-
init_keywords();
|
|
2995
|
-
init_keywords();
|
|
2996
3200
|
});
|
|
2997
3201
|
|
|
2998
|
-
// src/
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
if (this.modules.has(module.id)) {
|
|
3003
|
-
console.warn(`Module '${module.id}' is already registered, overwriting...`);
|
|
3004
|
-
}
|
|
3005
|
-
this.modules.set(module.id, module);
|
|
3006
|
-
}
|
|
3007
|
-
get(id) {
|
|
3008
|
-
return this.modules.get(id);
|
|
3009
|
-
}
|
|
3010
|
-
list() {
|
|
3011
|
-
return Array.from(this.modules.values());
|
|
3012
|
-
}
|
|
3013
|
-
getEnabled(config) {
|
|
3014
|
-
const enabledIds = new Set(config.modules.filter((m) => m.enabled).map((m) => m.id));
|
|
3015
|
-
return this.list().filter((m) => enabledIds.has(m.id));
|
|
3016
|
-
}
|
|
3017
|
-
}
|
|
3018
|
-
async function registerBuiltInModules() {
|
|
3019
|
-
const { CoreModule: CoreModule2 } = await Promise.resolve().then(() => (init_core(), exports_core));
|
|
3020
|
-
const { TypeScriptModule: TypeScriptModule2 } = await Promise.resolve().then(() => (init_typescript(), exports_typescript));
|
|
3021
|
-
registry.register(new CoreModule2);
|
|
3022
|
-
registry.register(new TypeScriptModule2);
|
|
3023
|
-
}
|
|
3024
|
-
var registry;
|
|
3025
|
-
var init_registry = __esm(() => {
|
|
3026
|
-
registry = new ModuleRegistryImpl;
|
|
3202
|
+
// src/infrastructure/introspection/index.ts
|
|
3203
|
+
var init_introspection2 = __esm(() => {
|
|
3204
|
+
init_IntrospectionIndex();
|
|
3205
|
+
init_projectDetector();
|
|
3027
3206
|
});
|
|
3028
3207
|
|
|
3029
3208
|
// src/app/indexer/watcher.ts
|
|
@@ -3204,6 +3383,7 @@ __export(exports_indexer, {
|
|
|
3204
3383
|
watchDirectory: () => watchDirectory,
|
|
3205
3384
|
indexDirectory: () => indexDirectory,
|
|
3206
3385
|
getIndexStatus: () => getIndexStatus,
|
|
3386
|
+
ensureIndexFresh: () => ensureIndexFresh,
|
|
3207
3387
|
cleanupIndex: () => cleanupIndex
|
|
3208
3388
|
});
|
|
3209
3389
|
import { glob } from "glob";
|
|
@@ -3211,10 +3391,13 @@ import * as fs6 from "fs/promises";
|
|
|
3211
3391
|
import * as path12 from "path";
|
|
3212
3392
|
async function indexDirectory(rootDir, options = {}) {
|
|
3213
3393
|
const verbose = options.verbose ?? false;
|
|
3394
|
+
const quiet = options.quiet ?? false;
|
|
3214
3395
|
rootDir = path12.resolve(rootDir);
|
|
3215
3396
|
const location = getIndexLocation(rootDir);
|
|
3216
|
-
|
|
3217
|
-
|
|
3397
|
+
if (!quiet) {
|
|
3398
|
+
console.log(`Indexing directory: ${rootDir}`);
|
|
3399
|
+
console.log(`Index location: ${location.indexDir}`);
|
|
3400
|
+
}
|
|
3218
3401
|
const config = await loadConfig(rootDir);
|
|
3219
3402
|
const introspection = new IntrospectionIndex(rootDir);
|
|
3220
3403
|
await introspection.initialize();
|
|
@@ -3227,16 +3410,24 @@ async function indexDirectory(rootDir, options = {}) {
|
|
|
3227
3410
|
await registerBuiltInModules();
|
|
3228
3411
|
const enabledModules = registry.getEnabled(config);
|
|
3229
3412
|
if (enabledModules.length === 0) {
|
|
3230
|
-
|
|
3413
|
+
if (!quiet) {
|
|
3414
|
+
console.log("No modules enabled. Check your configuration.");
|
|
3415
|
+
}
|
|
3231
3416
|
return [];
|
|
3232
3417
|
}
|
|
3233
|
-
|
|
3418
|
+
if (!quiet) {
|
|
3419
|
+
console.log(`Enabled modules: ${enabledModules.map((m) => m.id).join(", ")}`);
|
|
3420
|
+
}
|
|
3234
3421
|
const files = await findFiles(rootDir, config);
|
|
3235
|
-
|
|
3422
|
+
if (!quiet) {
|
|
3423
|
+
console.log(`Found ${files.length} files to index`);
|
|
3424
|
+
}
|
|
3236
3425
|
const results = [];
|
|
3237
3426
|
for (const module of enabledModules) {
|
|
3238
|
-
|
|
3427
|
+
if (!quiet) {
|
|
3428
|
+
console.log(`
|
|
3239
3429
|
[${module.name}] Starting indexing...`);
|
|
3430
|
+
}
|
|
3240
3431
|
const moduleConfig = getModuleConfig(config, module.id);
|
|
3241
3432
|
if (module.initialize && moduleConfig) {
|
|
3242
3433
|
const configWithOverrides = { ...moduleConfig };
|
|
@@ -3251,7 +3442,9 @@ async function indexDirectory(rootDir, options = {}) {
|
|
|
3251
3442
|
const result = await indexWithModule(rootDir, files, module, config, verbose, introspection);
|
|
3252
3443
|
results.push(result);
|
|
3253
3444
|
if (module.finalize) {
|
|
3254
|
-
|
|
3445
|
+
if (!quiet) {
|
|
3446
|
+
console.log(`[${module.name}] Building secondary indexes...`);
|
|
3447
|
+
}
|
|
3255
3448
|
const ctx = {
|
|
3256
3449
|
rootDir,
|
|
3257
3450
|
config,
|
|
@@ -3267,12 +3460,167 @@ async function indexDirectory(rootDir, options = {}) {
|
|
|
3267
3460
|
};
|
|
3268
3461
|
await module.finalize(ctx);
|
|
3269
3462
|
}
|
|
3270
|
-
|
|
3463
|
+
if (!quiet) {
|
|
3464
|
+
console.log(`[${module.name}] Complete: ${result.indexed} indexed, ${result.skipped} skipped, ${result.errors} errors`);
|
|
3465
|
+
}
|
|
3271
3466
|
}
|
|
3272
3467
|
await introspection.save(config);
|
|
3273
3468
|
await updateGlobalManifest(rootDir, enabledModules, config);
|
|
3274
3469
|
return results;
|
|
3275
3470
|
}
|
|
3471
|
+
async function isIndexVersionCompatible(rootDir) {
|
|
3472
|
+
const config = await loadConfig(rootDir);
|
|
3473
|
+
const globalManifestPath = getGlobalManifestPath(rootDir, config);
|
|
3474
|
+
try {
|
|
3475
|
+
const content = await fs6.readFile(globalManifestPath, "utf-8");
|
|
3476
|
+
const manifest = JSON.parse(content);
|
|
3477
|
+
return manifest.version === INDEX_SCHEMA_VERSION;
|
|
3478
|
+
} catch {
|
|
3479
|
+
return false;
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
async function deleteIndex(rootDir) {
|
|
3483
|
+
const indexDir = getRaggrepDir(rootDir);
|
|
3484
|
+
try {
|
|
3485
|
+
await fs6.rm(indexDir, { recursive: true, force: true });
|
|
3486
|
+
} catch {}
|
|
3487
|
+
}
|
|
3488
|
+
async function ensureIndexFresh(rootDir, options = {}) {
|
|
3489
|
+
const verbose = options.verbose ?? false;
|
|
3490
|
+
const quiet = options.quiet ?? false;
|
|
3491
|
+
rootDir = path12.resolve(rootDir);
|
|
3492
|
+
const status = await getIndexStatus(rootDir);
|
|
3493
|
+
if (!status.exists) {
|
|
3494
|
+
if (!quiet) {
|
|
3495
|
+
console.log(`No index found. Creating index...
|
|
3496
|
+
`);
|
|
3497
|
+
}
|
|
3498
|
+
const results = await indexDirectory(rootDir, { ...options, quiet });
|
|
3499
|
+
const totalIndexed2 = results.reduce((sum, r) => sum + r.indexed, 0);
|
|
3500
|
+
return { indexed: totalIndexed2, removed: 0, unchanged: 0 };
|
|
3501
|
+
}
|
|
3502
|
+
const versionCompatible = await isIndexVersionCompatible(rootDir);
|
|
3503
|
+
if (!versionCompatible) {
|
|
3504
|
+
if (!quiet) {
|
|
3505
|
+
console.log(`Index version incompatible. Rebuilding...
|
|
3506
|
+
`);
|
|
3507
|
+
}
|
|
3508
|
+
await deleteIndex(rootDir);
|
|
3509
|
+
const results = await indexDirectory(rootDir, { ...options, quiet });
|
|
3510
|
+
const totalIndexed2 = results.reduce((sum, r) => sum + r.indexed, 0);
|
|
3511
|
+
return { indexed: totalIndexed2, removed: 0, unchanged: 0 };
|
|
3512
|
+
}
|
|
3513
|
+
const config = await loadConfig(rootDir);
|
|
3514
|
+
await registerBuiltInModules();
|
|
3515
|
+
const enabledModules = registry.getEnabled(config);
|
|
3516
|
+
if (enabledModules.length === 0) {
|
|
3517
|
+
return { indexed: 0, removed: 0, unchanged: 0 };
|
|
3518
|
+
}
|
|
3519
|
+
const introspection = new IntrospectionIndex(rootDir);
|
|
3520
|
+
await introspection.initialize();
|
|
3521
|
+
const currentFiles = await findFiles(rootDir, config);
|
|
3522
|
+
const currentFileSet = new Set(currentFiles.map((f) => path12.relative(rootDir, f)));
|
|
3523
|
+
let totalIndexed = 0;
|
|
3524
|
+
let totalRemoved = 0;
|
|
3525
|
+
let totalUnchanged = 0;
|
|
3526
|
+
for (const module of enabledModules) {
|
|
3527
|
+
const moduleConfig = getModuleConfig(config, module.id);
|
|
3528
|
+
if (module.initialize && moduleConfig) {
|
|
3529
|
+
const configWithOverrides = { ...moduleConfig };
|
|
3530
|
+
if (options.model && module.id === "language/typescript") {
|
|
3531
|
+
configWithOverrides.options = {
|
|
3532
|
+
...configWithOverrides.options,
|
|
3533
|
+
embeddingModel: options.model
|
|
3534
|
+
};
|
|
3535
|
+
}
|
|
3536
|
+
await module.initialize(configWithOverrides);
|
|
3537
|
+
}
|
|
3538
|
+
const manifest = await loadModuleManifest(rootDir, module.id, config);
|
|
3539
|
+
const indexPath = getModuleIndexPath(rootDir, module.id, config);
|
|
3540
|
+
const filesToRemove = [];
|
|
3541
|
+
for (const filepath of Object.keys(manifest.files)) {
|
|
3542
|
+
if (!currentFileSet.has(filepath)) {
|
|
3543
|
+
filesToRemove.push(filepath);
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
for (const filepath of filesToRemove) {
|
|
3547
|
+
if (verbose) {
|
|
3548
|
+
console.log(` Removing stale: ${filepath}`);
|
|
3549
|
+
}
|
|
3550
|
+
const indexFilePath = path12.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
|
|
3551
|
+
try {
|
|
3552
|
+
await fs6.unlink(indexFilePath);
|
|
3553
|
+
} catch {}
|
|
3554
|
+
delete manifest.files[filepath];
|
|
3555
|
+
totalRemoved++;
|
|
3556
|
+
}
|
|
3557
|
+
const ctx = {
|
|
3558
|
+
rootDir,
|
|
3559
|
+
config,
|
|
3560
|
+
readFile: async (filepath) => {
|
|
3561
|
+
const fullPath = path12.isAbsolute(filepath) ? filepath : path12.join(rootDir, filepath);
|
|
3562
|
+
return fs6.readFile(fullPath, "utf-8");
|
|
3563
|
+
},
|
|
3564
|
+
getFileStats: async (filepath) => {
|
|
3565
|
+
const fullPath = path12.isAbsolute(filepath) ? filepath : path12.join(rootDir, filepath);
|
|
3566
|
+
const stats = await fs6.stat(fullPath);
|
|
3567
|
+
return { lastModified: stats.mtime.toISOString() };
|
|
3568
|
+
},
|
|
3569
|
+
getIntrospection: (filepath) => introspection.getFile(filepath)
|
|
3570
|
+
};
|
|
3571
|
+
for (const filepath of currentFiles) {
|
|
3572
|
+
const relativePath = path12.relative(rootDir, filepath);
|
|
3573
|
+
try {
|
|
3574
|
+
const stats = await fs6.stat(filepath);
|
|
3575
|
+
const lastModified = stats.mtime.toISOString();
|
|
3576
|
+
const existingEntry = manifest.files[relativePath];
|
|
3577
|
+
if (existingEntry && existingEntry.lastModified === lastModified) {
|
|
3578
|
+
totalUnchanged++;
|
|
3579
|
+
continue;
|
|
3580
|
+
}
|
|
3581
|
+
if (verbose) {
|
|
3582
|
+
console.log(` Indexing: ${relativePath}`);
|
|
3583
|
+
}
|
|
3584
|
+
const content = await fs6.readFile(filepath, "utf-8");
|
|
3585
|
+
introspection.addFile(relativePath, content);
|
|
3586
|
+
const fileIndex = await module.indexFile(relativePath, content, ctx);
|
|
3587
|
+
if (fileIndex) {
|
|
3588
|
+
await writeFileIndex(rootDir, module.id, relativePath, fileIndex, config);
|
|
3589
|
+
manifest.files[relativePath] = {
|
|
3590
|
+
lastModified,
|
|
3591
|
+
chunkCount: fileIndex.chunks.length
|
|
3592
|
+
};
|
|
3593
|
+
totalIndexed++;
|
|
3594
|
+
}
|
|
3595
|
+
} catch (error) {
|
|
3596
|
+
if (verbose) {
|
|
3597
|
+
console.error(` Error indexing ${relativePath}:`, error);
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
if (totalIndexed > 0 || totalRemoved > 0) {
|
|
3602
|
+
manifest.lastUpdated = new Date().toISOString();
|
|
3603
|
+
await writeModuleManifest(rootDir, module.id, manifest, config);
|
|
3604
|
+
if (module.finalize) {
|
|
3605
|
+
await module.finalize(ctx);
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
if (totalRemoved > 0) {
|
|
3609
|
+
await cleanupEmptyDirectories(indexPath);
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
3612
|
+
if (totalIndexed > 0) {
|
|
3613
|
+
await introspection.save(config);
|
|
3614
|
+
}
|
|
3615
|
+
if (totalIndexed > 0 || totalRemoved > 0) {
|
|
3616
|
+
await updateGlobalManifest(rootDir, enabledModules, config);
|
|
3617
|
+
}
|
|
3618
|
+
return {
|
|
3619
|
+
indexed: totalIndexed,
|
|
3620
|
+
removed: totalRemoved,
|
|
3621
|
+
unchanged: totalUnchanged
|
|
3622
|
+
};
|
|
3623
|
+
}
|
|
3276
3624
|
async function indexWithModule(rootDir, files, module, config, verbose, introspection) {
|
|
3277
3625
|
const result = {
|
|
3278
3626
|
moduleId: module.id,
|
|
@@ -3378,7 +3726,7 @@ async function writeFileIndex(rootDir, moduleId, filepath, fileIndex, config) {
|
|
|
3378
3726
|
async function updateGlobalManifest(rootDir, modules, config) {
|
|
3379
3727
|
const manifestPath = getGlobalManifestPath(rootDir, config);
|
|
3380
3728
|
const manifest = {
|
|
3381
|
-
version:
|
|
3729
|
+
version: INDEX_SCHEMA_VERSION,
|
|
3382
3730
|
lastUpdated: new Date().toISOString(),
|
|
3383
3731
|
modules: modules.map((m) => m.id)
|
|
3384
3732
|
};
|
|
@@ -3518,10 +3866,11 @@ async function getIndexStatus(rootDir) {
|
|
|
3518
3866
|
}
|
|
3519
3867
|
return status;
|
|
3520
3868
|
}
|
|
3869
|
+
var INDEX_SCHEMA_VERSION = "1.0.0";
|
|
3521
3870
|
var init_indexer = __esm(() => {
|
|
3522
3871
|
init_config2();
|
|
3523
3872
|
init_registry();
|
|
3524
|
-
|
|
3873
|
+
init_introspection2();
|
|
3525
3874
|
init_watcher();
|
|
3526
3875
|
});
|
|
3527
3876
|
|
|
@@ -3615,6 +3964,20 @@ async function loadGlobalManifest(rootDir, config) {
|
|
|
3615
3964
|
return null;
|
|
3616
3965
|
}
|
|
3617
3966
|
}
|
|
3967
|
+
function formatModuleName(moduleId) {
|
|
3968
|
+
switch (moduleId) {
|
|
3969
|
+
case "core":
|
|
3970
|
+
return "Core";
|
|
3971
|
+
case "language/typescript":
|
|
3972
|
+
return "TypeScript";
|
|
3973
|
+
default:
|
|
3974
|
+
if (moduleId.startsWith("language/")) {
|
|
3975
|
+
const lang = moduleId.replace("language/", "");
|
|
3976
|
+
return lang.charAt(0).toUpperCase() + lang.slice(1);
|
|
3977
|
+
}
|
|
3978
|
+
return moduleId;
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3618
3981
|
function formatSearchResults(results) {
|
|
3619
3982
|
if (results.length === 0) {
|
|
3620
3983
|
return "No results found.";
|
|
@@ -3630,6 +3993,7 @@ function formatSearchResults(results) {
|
|
|
3630
3993
|
output += `${i + 1}. ${location}${nameInfo}
|
|
3631
3994
|
`;
|
|
3632
3995
|
output += ` Score: ${(result.score * 100).toFixed(1)}% | Type: ${chunk.type}`;
|
|
3996
|
+
output += ` | via ${formatModuleName(result.moduleId)}`;
|
|
3633
3997
|
if (chunk.isExported) {
|
|
3634
3998
|
output += " | exported";
|
|
3635
3999
|
}
|
|
@@ -3657,7 +4021,7 @@ init_embeddings();
|
|
|
3657
4021
|
// package.json
|
|
3658
4022
|
var package_default = {
|
|
3659
4023
|
name: "raggrep",
|
|
3660
|
-
version: "0.
|
|
4024
|
+
version: "0.2.0",
|
|
3661
4025
|
description: "Local filesystem-based RAG system for codebases - semantic search using local embeddings",
|
|
3662
4026
|
type: "module",
|
|
3663
4027
|
main: "./dist/index.js",
|
|
@@ -3899,8 +4263,11 @@ Options:
|
|
|
3899
4263
|
-h, --help Show this help message
|
|
3900
4264
|
|
|
3901
4265
|
Note:
|
|
3902
|
-
|
|
3903
|
-
|
|
4266
|
+
The index is managed automatically like a cache:
|
|
4267
|
+
- First query creates the index
|
|
4268
|
+
- Changed files are re-indexed automatically
|
|
4269
|
+
- Deleted files are cleaned up automatically
|
|
4270
|
+
- Unchanged files use the cached index (instant)
|
|
3904
4271
|
|
|
3905
4272
|
Examples:
|
|
3906
4273
|
raggrep query "user authentication"
|
|
@@ -3911,7 +4278,7 @@ Examples:
|
|
|
3911
4278
|
process.exit(0);
|
|
3912
4279
|
}
|
|
3913
4280
|
const { search: search2, formatSearchResults: formatSearchResults2 } = await Promise.resolve().then(() => (init_search(), exports_search));
|
|
3914
|
-
const {
|
|
4281
|
+
const { ensureIndexFresh: ensureIndexFresh2 } = await Promise.resolve().then(() => (init_indexer(), exports_indexer));
|
|
3915
4282
|
const query = flags.remaining[0];
|
|
3916
4283
|
if (!query) {
|
|
3917
4284
|
console.error("Usage: raggrep query <search query>");
|
|
@@ -3919,24 +4286,20 @@ Examples:
|
|
|
3919
4286
|
process.exit(1);
|
|
3920
4287
|
}
|
|
3921
4288
|
try {
|
|
3922
|
-
const
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
model: flags.model,
|
|
3931
|
-
verbose: false
|
|
3932
|
-
});
|
|
3933
|
-
console.log(`
|
|
3934
|
-
================`);
|
|
3935
|
-
console.log("Summary:");
|
|
3936
|
-
for (const result of indexResults) {
|
|
3937
|
-
console.log(` ${result.moduleId}: ${result.indexed} indexed, ${result.skipped} skipped, ${result.errors} errors`);
|
|
4289
|
+
const freshStats = await ensureIndexFresh2(process.cwd(), {
|
|
4290
|
+
model: flags.model,
|
|
4291
|
+
quiet: true
|
|
4292
|
+
});
|
|
4293
|
+
if (freshStats.indexed > 0 || freshStats.removed > 0) {
|
|
4294
|
+
const parts = [];
|
|
4295
|
+
if (freshStats.indexed > 0) {
|
|
4296
|
+
parts.push(`${freshStats.indexed} indexed`);
|
|
3938
4297
|
}
|
|
3939
|
-
|
|
4298
|
+
if (freshStats.removed > 0) {
|
|
4299
|
+
parts.push(`${freshStats.removed} removed`);
|
|
4300
|
+
}
|
|
4301
|
+
console.log(`Index updated: ${parts.join(", ")}
|
|
4302
|
+
`);
|
|
3940
4303
|
}
|
|
3941
4304
|
console.log("RAGgrep Search");
|
|
3942
4305
|
console.log(`==============
|
|
@@ -4092,4 +4455,4 @@ Run 'raggrep <command> --help' for more information.
|
|
|
4092
4455
|
}
|
|
4093
4456
|
main();
|
|
4094
4457
|
|
|
4095
|
-
//# debugId=
|
|
4458
|
+
//# debugId=4B6F0EA9EEB7164864756E2164756E21
|