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.
Files changed (24) hide show
  1. package/README.md +62 -95
  2. package/dist/app/indexer/index.d.ts +27 -2
  3. package/dist/cli/main.js +967 -604
  4. package/dist/cli/main.js.map +18 -17
  5. package/dist/{introspection/conventions/types.d.ts → domain/entities/conventions.d.ts} +6 -5
  6. package/dist/domain/entities/index.d.ts +2 -0
  7. package/dist/{introspection → domain/services}/conventions/configFiles.d.ts +1 -1
  8. package/dist/{introspection → domain/services}/conventions/entryPoints.d.ts +1 -1
  9. package/dist/{introspection → domain/services}/conventions/frameworks/convex.d.ts +1 -1
  10. package/dist/{introspection → domain/services}/conventions/frameworks/index.d.ts +1 -1
  11. package/dist/{introspection → domain/services}/conventions/frameworks/nextjs.d.ts +1 -1
  12. package/dist/{introspection → domain/services}/conventions/index.d.ts +5 -5
  13. package/dist/domain/services/introspection.d.ts +31 -0
  14. package/dist/index.js +671 -474
  15. package/dist/index.js.map +16 -16
  16. package/dist/{introspection/index.d.ts → infrastructure/introspection/IntrospectionIndex.d.ts} +3 -14
  17. package/dist/infrastructure/introspection/index.d.ts +9 -0
  18. package/dist/{introspection → infrastructure/introspection}/projectDetector.d.ts +3 -12
  19. package/dist/types.d.ts +4 -4
  20. package/package.json +1 -1
  21. package/dist/introspection/fileIntrospector.d.ts +0 -14
  22. /package/dist/{introspection/types.d.ts → domain/entities/introspection.d.ts} +0 -0
  23. /package/dist/{introspection → domain/services}/conventions/conventions.test.d.ts +0 -0
  24. /package/dist/{introspection → domain/services}/introspection.test.d.ts +0 -0
package/dist/index.js CHANGED
@@ -270,247 +270,11 @@ function normalizeScore(score, midpoint = 5) {
270
270
  }
271
271
  var BM25_K1 = 1.5, BM25_B = 0.75;
272
272
 
273
- // src/introspection/projectDetector.ts
273
+ // src/domain/services/conventions/entryPoints.ts
274
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())
305
- continue;
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";
329
- }
330
- const hasWorkspaces = Boolean(pkg.workspaces);
331
- return { name, relativePath, type, hasWorkspaces };
332
- } catch {
333
- return null;
334
- }
335
- }
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";
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);
430
- }
431
- }
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
- };
450
- }
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
275
  function getParentFolder(filepath) {
512
- const dir = path3.dirname(filepath);
513
- return path3.basename(dir);
276
+ const dir = path2.dirname(filepath);
277
+ return path2.basename(dir);
514
278
  }
515
279
  var entryPointConventions;
516
280
  var init_entryPoints = __esm(() => {
@@ -595,11 +359,75 @@ var init_entryPoints = __esm(() => {
595
359
  return filename === "lib.rs" || filename === "main.rs";
596
360
  },
597
361
  keywords: ["entry", "crate", "rust", "module"]
362
+ },
363
+ {
364
+ id: "go-main",
365
+ name: "Go Main Entry",
366
+ description: "Go application main entry point",
367
+ category: "entry-point",
368
+ match: (filepath, filename) => {
369
+ return filename === "main.go";
370
+ },
371
+ keywords: ["entry", "main", "go", "golang", "entrypoint"],
372
+ dynamicKeywords: (filepath) => {
373
+ const parent = getParentFolder(filepath);
374
+ if (parent && !["cmd", "src", ".", ""].includes(parent)) {
375
+ return [parent.toLowerCase()];
376
+ }
377
+ return [];
378
+ }
379
+ },
380
+ {
381
+ id: "python-main",
382
+ name: "Python Main Module",
383
+ description: "Python package main entry point",
384
+ category: "entry-point",
385
+ match: (filepath, filename) => {
386
+ return filename === "__main__.py";
387
+ },
388
+ keywords: ["entry", "main", "python", "entrypoint", "cli"],
389
+ dynamicKeywords: (filepath) => {
390
+ const parent = getParentFolder(filepath);
391
+ if (["src", "lib", ".", ""].includes(parent)) {
392
+ return [];
393
+ }
394
+ return [parent.toLowerCase()];
395
+ }
396
+ },
397
+ {
398
+ id: "python-app",
399
+ name: "Python App Entry",
400
+ description: "Common Python application entry points",
401
+ category: "entry-point",
402
+ match: (filepath, filename) => {
403
+ return filename === "app.py" || filename === "main.py" || filename === "run.py";
404
+ },
405
+ keywords: ["entry", "main", "python", "app", "entrypoint"]
406
+ },
407
+ {
408
+ id: "python-manage",
409
+ name: "Django Manage",
410
+ description: "Django management script",
411
+ category: "entry-point",
412
+ match: (filepath, filename) => {
413
+ return filename === "manage.py";
414
+ },
415
+ keywords: ["entry", "django", "python", "manage", "cli", "admin"]
416
+ },
417
+ {
418
+ id: "python-wsgi",
419
+ name: "Python WSGI Entry",
420
+ description: "Python WSGI application entry point",
421
+ category: "entry-point",
422
+ match: (filepath, filename) => {
423
+ return filename === "wsgi.py" || filename === "asgi.py";
424
+ },
425
+ keywords: ["entry", "wsgi", "asgi", "python", "server", "web"]
598
426
  }
599
427
  ];
600
428
  });
601
429
 
602
- // src/introspection/conventions/configFiles.ts
430
+ // src/domain/services/conventions/configFiles.ts
603
431
  var configFileConventions;
604
432
  var init_configFiles = __esm(() => {
605
433
  configFileConventions = [
@@ -643,6 +471,157 @@ var init_configFiles = __esm(() => {
643
471
  match: (filepath, filename) => filename === "bun.lockb" || filename === "bun.lock",
644
472
  keywords: ["dependencies", "lock", "bun", "versions"]
645
473
  },
474
+ {
475
+ id: "go-mod",
476
+ name: "Go Module",
477
+ description: "Go module definition file",
478
+ category: "configuration",
479
+ match: (filepath, filename) => filename === "go.mod",
480
+ keywords: [
481
+ "go",
482
+ "golang",
483
+ "module",
484
+ "dependencies",
485
+ "package",
486
+ "workspace"
487
+ ]
488
+ },
489
+ {
490
+ id: "go-sum",
491
+ name: "Go Sum",
492
+ description: "Go module checksum file",
493
+ category: "configuration",
494
+ match: (filepath, filename) => filename === "go.sum",
495
+ keywords: ["go", "golang", "dependencies", "checksum", "lock", "versions"]
496
+ },
497
+ {
498
+ id: "go-work",
499
+ name: "Go Workspace",
500
+ description: "Go workspace configuration for multi-module development",
501
+ category: "configuration",
502
+ match: (filepath, filename) => filename === "go.work" || filename === "go.work.sum",
503
+ keywords: ["go", "golang", "workspace", "monorepo", "modules"]
504
+ },
505
+ {
506
+ id: "makefile",
507
+ name: "Makefile",
508
+ description: "Make build automation file",
509
+ category: "build",
510
+ match: (filepath, filename) => filename === "Makefile" || filename === "makefile" || filename === "GNUmakefile",
511
+ keywords: ["make", "build", "automation", "tasks", "compile"]
512
+ },
513
+ {
514
+ id: "requirements-txt",
515
+ name: "Python Requirements",
516
+ description: "Python pip requirements file",
517
+ category: "configuration",
518
+ match: (filepath, filename) => filename === "requirements.txt" || filename.startsWith("requirements-") || filename.startsWith("requirements_"),
519
+ keywords: ["python", "pip", "dependencies", "packages", "requirements"]
520
+ },
521
+ {
522
+ id: "pyproject-toml",
523
+ name: "Python Project",
524
+ description: "Python project configuration (PEP 518/621)",
525
+ category: "configuration",
526
+ match: (filepath, filename) => filename === "pyproject.toml",
527
+ keywords: [
528
+ "python",
529
+ "project",
530
+ "config",
531
+ "poetry",
532
+ "build",
533
+ "dependencies",
534
+ "package"
535
+ ]
536
+ },
537
+ {
538
+ id: "setup-py",
539
+ name: "Python Setup",
540
+ description: "Python package setup script",
541
+ category: "configuration",
542
+ match: (filepath, filename) => filename === "setup.py",
543
+ keywords: ["python", "setup", "package", "install", "distribution"]
544
+ },
545
+ {
546
+ id: "setup-cfg",
547
+ name: "Python Setup Config",
548
+ description: "Python setup configuration file",
549
+ category: "configuration",
550
+ match: (filepath, filename) => filename === "setup.cfg",
551
+ keywords: ["python", "setup", "config", "package", "metadata"]
552
+ },
553
+ {
554
+ id: "pipfile",
555
+ name: "Pipfile",
556
+ description: "Pipenv dependency file",
557
+ category: "configuration",
558
+ match: (filepath, filename) => filename === "Pipfile" || filename === "Pipfile.lock",
559
+ keywords: ["python", "pipenv", "dependencies", "packages", "virtualenv"]
560
+ },
561
+ {
562
+ id: "poetry-lock",
563
+ name: "Poetry Lock",
564
+ description: "Poetry dependency lock file",
565
+ category: "configuration",
566
+ match: (filepath, filename) => filename === "poetry.lock",
567
+ keywords: ["python", "poetry", "dependencies", "lock", "versions"]
568
+ },
569
+ {
570
+ id: "tox-ini",
571
+ name: "Tox Config",
572
+ description: "Tox testing automation configuration",
573
+ category: "test",
574
+ match: (filepath, filename) => filename === "tox.ini",
575
+ keywords: ["python", "tox", "testing", "automation", "environments"]
576
+ },
577
+ {
578
+ id: "pytest-ini",
579
+ name: "Pytest Config",
580
+ description: "Pytest configuration file",
581
+ category: "test",
582
+ match: (filepath, filename) => filename === "pytest.ini" || filename === "conftest.py",
583
+ keywords: ["python", "pytest", "testing", "test", "fixtures"]
584
+ },
585
+ {
586
+ id: "mypy-ini",
587
+ name: "Mypy Config",
588
+ description: "Mypy type checker configuration",
589
+ category: "configuration",
590
+ match: (filepath, filename) => filename === "mypy.ini" || filename === ".mypy.ini",
591
+ keywords: ["python", "mypy", "types", "type checking", "static analysis"]
592
+ },
593
+ {
594
+ id: "flake8",
595
+ name: "Flake8 Config",
596
+ description: "Flake8 linter configuration",
597
+ category: "configuration",
598
+ match: (filepath, filename) => filename === ".flake8",
599
+ keywords: ["python", "flake8", "linting", "lint", "style"]
600
+ },
601
+ {
602
+ id: "pylintrc",
603
+ name: "Pylint Config",
604
+ description: "Pylint linter configuration",
605
+ category: "configuration",
606
+ match: (filepath, filename) => filename === ".pylintrc" || filename === "pylintrc" || filename === "pylint.toml",
607
+ keywords: ["python", "pylint", "linting", "lint", "code quality"]
608
+ },
609
+ {
610
+ id: "ruff-toml",
611
+ name: "Ruff Config",
612
+ description: "Ruff linter/formatter configuration",
613
+ category: "configuration",
614
+ match: (filepath, filename) => filename === "ruff.toml" || filename === ".ruff.toml",
615
+ keywords: ["python", "ruff", "linting", "formatting", "fast"]
616
+ },
617
+ {
618
+ id: "black-toml",
619
+ name: "Black Config",
620
+ description: "Black formatter configuration",
621
+ category: "configuration",
622
+ match: (filepath, filename) => filename === ".black.toml",
623
+ keywords: ["python", "black", "formatting", "format", "style"]
624
+ },
646
625
  {
647
626
  id: "tsconfig",
648
627
  name: "TypeScript Config",
@@ -884,7 +863,7 @@ var init_configFiles = __esm(() => {
884
863
  ];
885
864
  });
886
865
 
887
- // src/introspection/conventions/frameworks/nextjs.ts
866
+ // src/domain/services/conventions/frameworks/nextjs.ts
888
867
  var nextjsConventions, nextjsFramework;
889
868
  var init_nextjs = __esm(() => {
890
869
  nextjsConventions = [
@@ -1049,7 +1028,7 @@ var init_nextjs = __esm(() => {
1049
1028
  };
1050
1029
  });
1051
1030
 
1052
- // src/introspection/conventions/frameworks/convex.ts
1031
+ // src/domain/services/conventions/frameworks/convex.ts
1053
1032
  var convexConventions, convexFramework;
1054
1033
  var init_convex = __esm(() => {
1055
1034
  convexConventions = [
@@ -1145,7 +1124,7 @@ var init_convex = __esm(() => {
1145
1124
  };
1146
1125
  });
1147
1126
 
1148
- // src/introspection/conventions/frameworks/index.ts
1127
+ // src/domain/services/conventions/frameworks/index.ts
1149
1128
  function getAllFrameworkConventions() {
1150
1129
  return frameworkProviders.flatMap((f) => f.conventions);
1151
1130
  }
@@ -1161,26 +1140,21 @@ var init_frameworks = __esm(() => {
1161
1140
  ];
1162
1141
  });
1163
1142
 
1164
- // src/introspection/conventions/index.ts
1165
- import * as path4 from "path";
1166
- function getAllConventions() {
1143
+ // src/domain/services/conventions/index.ts
1144
+ import * as path3 from "path";
1145
+ function getConventions() {
1167
1146
  return [
1168
1147
  ...entryPointConventions,
1169
1148
  ...configFileConventions,
1170
- ...getAllFrameworkConventions()
1171
- ];
1172
- }
1173
- function getConventions() {
1174
- return [
1175
- ...getAllConventions(),
1149
+ ...getAllFrameworkConventions(),
1176
1150
  ...typeDefinitionConventions,
1177
1151
  ...testFileConventions
1178
1152
  ];
1179
1153
  }
1180
1154
  function getConventionKeywords(filepath) {
1181
1155
  const conventions = getConventions();
1182
- const filename = path4.basename(filepath);
1183
- const extension = path4.extname(filepath);
1156
+ const filename = path3.basename(filepath);
1157
+ const extension = path3.extname(filepath);
1184
1158
  const keywords = new Set;
1185
1159
  for (const convention of conventions) {
1186
1160
  try {
@@ -1227,9 +1201,8 @@ var init_conventions = __esm(() => {
1227
1201
  keywords: ["types", "definitions", "typescript", "interfaces"],
1228
1202
  dynamicKeywords: (filepath) => {
1229
1203
  const match = filepath.match(/([^/]+)\.types\.ts$/);
1230
- if (match) {
1204
+ if (match)
1231
1205
  return [match[1].toLowerCase()];
1232
- }
1233
1206
  return [];
1234
1207
  }
1235
1208
  },
@@ -1252,9 +1225,8 @@ var init_conventions = __esm(() => {
1252
1225
  keywords: ["test", "spec", "unit test"],
1253
1226
  dynamicKeywords: (filepath) => {
1254
1227
  const match = filepath.match(/([^/]+)\.(test|spec)\./);
1255
- if (match) {
1228
+ if (match)
1256
1229
  return [match[1].toLowerCase()];
1257
- }
1258
1230
  return [];
1259
1231
  }
1260
1232
  },
@@ -1269,22 +1241,19 @@ var init_conventions = __esm(() => {
1269
1241
  ];
1270
1242
  });
1271
1243
 
1272
- // src/introspection/fileIntrospector.ts
1273
- import * as path5 from "path";
1244
+ // src/domain/services/introspection.ts
1245
+ import * as path4 from "path";
1274
1246
  function introspectFile(filepath, structure, fileContent) {
1275
1247
  const normalizedPath = filepath.replace(/\\/g, "/");
1276
1248
  const segments = normalizedPath.split("/").filter((s) => s.length > 0);
1277
1249
  const filename = segments[segments.length - 1] || "";
1278
- const ext = path5.extname(filename);
1250
+ const ext = path4.extname(filename);
1279
1251
  const project = findProjectForFile(normalizedPath, structure);
1280
1252
  const language = EXTENSION_TO_LANGUAGE[ext] || "unknown";
1281
1253
  const layer = detectLayer(segments, filename);
1282
1254
  const domain = detectDomain(segments);
1283
1255
  const scope = detectScope(segments, project, layer);
1284
- let framework;
1285
- if (fileContent) {
1286
- framework = detectFramework(fileContent);
1287
- }
1256
+ const framework = fileContent ? detectFramework(fileContent) : undefined;
1288
1257
  return {
1289
1258
  filepath: normalizedPath,
1290
1259
  project,
@@ -1297,21 +1266,81 @@ function introspectFile(filepath, structure, fileContent) {
1297
1266
  pathSegments: segments.slice(0, -1)
1298
1267
  };
1299
1268
  }
1269
+ function introspectionToKeywords(intro) {
1270
+ const keywords = [];
1271
+ const filename = path4.basename(intro.filepath);
1272
+ const filenameWithoutExt = filename.replace(/\.[^.]+$/, "");
1273
+ const filenameParts = filenameWithoutExt.split(/[-_.]/).flatMap((part) => part.split(/(?=[A-Z])/)).map((part) => part.toLowerCase()).filter((part) => part.length > 1);
1274
+ keywords.push(...filenameParts);
1275
+ keywords.push(filenameWithoutExt.toLowerCase());
1276
+ if (intro.project.name && intro.project.name !== "root") {
1277
+ keywords.push(intro.project.name.toLowerCase());
1278
+ }
1279
+ if (intro.scope !== "unknown")
1280
+ keywords.push(intro.scope);
1281
+ if (intro.layer)
1282
+ keywords.push(intro.layer);
1283
+ if (intro.domain)
1284
+ keywords.push(intro.domain);
1285
+ if (intro.language !== "unknown")
1286
+ keywords.push(intro.language);
1287
+ if (intro.framework)
1288
+ keywords.push(intro.framework);
1289
+ const skipSegments = new Set(["src", "lib", "index"]);
1290
+ for (const segment of intro.pathSegments) {
1291
+ if (!skipSegments.has(segment.toLowerCase()) && segment.length > 2) {
1292
+ keywords.push(segment.toLowerCase());
1293
+ }
1294
+ }
1295
+ const conventionKeywords = getConventionKeywords(intro.filepath);
1296
+ keywords.push(...conventionKeywords);
1297
+ return [...new Set(keywords)];
1298
+ }
1299
+ function detectScopeFromName(name) {
1300
+ const nameLower = name.toLowerCase();
1301
+ for (const [scope, keywords] of Object.entries(SCOPE_KEYWORDS)) {
1302
+ if (scope === "unknown")
1303
+ continue;
1304
+ for (const keyword of keywords) {
1305
+ if (nameLower.includes(keyword)) {
1306
+ return scope;
1307
+ }
1308
+ }
1309
+ }
1310
+ return "unknown";
1311
+ }
1312
+ function findProjectForFile(filepath, structure) {
1313
+ const normalizedPath = filepath.replace(/\\/g, "/");
1314
+ const matches = [];
1315
+ for (const project of structure.projects) {
1316
+ if (normalizedPath === project.root || normalizedPath.startsWith(project.root + "/")) {
1317
+ matches.push(project);
1318
+ }
1319
+ }
1320
+ if (matches.length > 0) {
1321
+ return matches.reduce((best, current) => current.root.length > best.root.length ? current : best);
1322
+ }
1323
+ for (const { pattern, type } of PROJECT_PATTERNS) {
1324
+ const match = normalizedPath.match(pattern);
1325
+ if (match) {
1326
+ return { name: match[1], root: match[0], type };
1327
+ }
1328
+ }
1329
+ return { name: "root", root: "", type: structure.rootType ?? "unknown" };
1330
+ }
1300
1331
  function detectLayer(segments, filename) {
1301
1332
  const filenameLower = filename.toLowerCase();
1302
1333
  for (const [layer, patterns] of Object.entries(LAYER_PATTERNS)) {
1303
1334
  for (const pattern of patterns) {
1304
- if (filenameLower.includes(pattern)) {
1335
+ if (filenameLower.includes(pattern))
1305
1336
  return layer;
1306
- }
1307
1337
  }
1308
1338
  }
1309
1339
  for (let i = segments.length - 2;i >= 0; i--) {
1310
1340
  const segment = segments[i].toLowerCase();
1311
1341
  for (const [layer, patterns] of Object.entries(LAYER_PATTERNS)) {
1312
- if (patterns.includes(segment)) {
1342
+ if (patterns.includes(segment))
1313
1343
  return layer;
1314
- }
1315
1344
  }
1316
1345
  }
1317
1346
  return;
@@ -1332,9 +1361,8 @@ function detectDomain(segments) {
1332
1361
  const segmentLower = segment.toLowerCase();
1333
1362
  if (skipSegments.has(segmentLower))
1334
1363
  continue;
1335
- if (DOMAIN_PATTERNS.includes(segmentLower)) {
1364
+ if (DOMAIN_PATTERNS.includes(segmentLower))
1336
1365
  return segmentLower;
1337
- }
1338
1366
  for (const domain of DOMAIN_PATTERNS) {
1339
1367
  if (segmentLower.startsWith(domain) || segmentLower.endsWith(domain)) {
1340
1368
  return domain;
@@ -1345,9 +1373,8 @@ function detectDomain(segments) {
1345
1373
  }
1346
1374
  function detectScope(segments, project, layer) {
1347
1375
  const projectScope = detectScopeFromName(project.name);
1348
- if (projectScope !== "unknown") {
1376
+ if (projectScope !== "unknown")
1349
1377
  return projectScope;
1350
- }
1351
1378
  if (layer) {
1352
1379
  switch (layer) {
1353
1380
  case "controller":
@@ -1365,15 +1392,12 @@ function detectScope(segments, project, layer) {
1365
1392
  }
1366
1393
  for (const segment of segments) {
1367
1394
  const segmentLower = segment.toLowerCase();
1368
- if (["server", "api", "backend"].includes(segmentLower)) {
1395
+ if (["server", "api", "backend"].includes(segmentLower))
1369
1396
  return "backend";
1370
- }
1371
- if (["client", "web", "frontend", "ui"].includes(segmentLower)) {
1397
+ if (["client", "web", "frontend", "ui"].includes(segmentLower))
1372
1398
  return "frontend";
1373
- }
1374
- if (["shared", "common", "lib", "libs"].includes(segmentLower)) {
1399
+ if (["shared", "common", "lib", "libs"].includes(segmentLower))
1375
1400
  return "shared";
1376
- }
1377
1401
  }
1378
1402
  return "unknown";
1379
1403
  }
@@ -1387,44 +1411,8 @@ function detectFramework(content) {
1387
1411
  }
1388
1412
  return;
1389
1413
  }
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());
1399
- }
1400
- if (intro.scope !== "unknown") {
1401
- keywords.push(intro.scope);
1402
- }
1403
- if (intro.layer) {
1404
- keywords.push(intro.layer);
1405
- }
1406
- if (intro.domain) {
1407
- keywords.push(intro.domain);
1408
- }
1409
- if (intro.language !== "unknown") {
1410
- keywords.push(intro.language);
1411
- }
1412
- if (intro.framework) {
1413
- keywords.push(intro.framework);
1414
- }
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)];
1424
- }
1425
- var LAYER_PATTERNS, DOMAIN_PATTERNS, FRAMEWORK_INDICATORS, EXTENSION_TO_LANGUAGE;
1426
- var init_fileIntrospector = __esm(() => {
1427
- init_projectDetector();
1414
+ var LAYER_PATTERNS, DOMAIN_PATTERNS, FRAMEWORK_INDICATORS, EXTENSION_TO_LANGUAGE, SCOPE_KEYWORDS, PROJECT_PATTERNS;
1415
+ var init_introspection = __esm(() => {
1428
1416
  init_conventions();
1429
1417
  LAYER_PATTERNS = {
1430
1418
  controller: ["controller", "api", "routes", "route", "handler"],
@@ -1543,120 +1531,45 @@ var init_fileIntrospector = __esm(() => {
1543
1531
  ".md": "markdown",
1544
1532
  ".json": "json",
1545
1533
  ".yaml": "yaml",
1546
- ".yml": "yaml"
1534
+ ".yml": "yaml",
1535
+ ".txt": "text"
1547
1536
  };
1548
- });
1549
-
1550
- // src/introspection/index.ts
1551
- import * as path6 from "path";
1552
- import * as fs3 from "fs/promises";
1553
-
1554
- class IntrospectionIndex {
1555
- rootDir;
1556
- structure = null;
1557
- files = new Map;
1558
- config = {};
1559
- constructor(rootDir) {
1560
- this.rootDir = rootDir;
1561
- }
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 {}
1570
- }
1571
- getStructure() {
1572
- return this.structure;
1573
- }
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;
1582
- }
1583
- getFile(filepath) {
1584
- return this.files.get(filepath);
1585
- }
1586
- getAllFiles() {
1587
- return Array.from(this.files.values());
1588
- }
1589
- applyOverrides(intro) {
1590
- if (!this.config.projects)
1591
- return;
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
- }
1602
- }
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));
1617
- }
1618
- console.log(` [Introspection] Saved metadata for ${this.files.size} files`);
1619
- }
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();
1631
- }
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);
1645
- }
1646
- }
1647
- } catch {}
1648
- }
1649
- clear() {
1650
- this.files.clear();
1651
- this.structure = null;
1652
- }
1653
- }
1654
- var init_introspection = __esm(() => {
1655
- init_projectDetector();
1656
- init_fileIntrospector();
1657
- init_config2();
1658
- init_fileIntrospector();
1659
- init_projectDetector();
1537
+ SCOPE_KEYWORDS = {
1538
+ frontend: [
1539
+ "web",
1540
+ "webapp",
1541
+ "frontend",
1542
+ "client",
1543
+ "ui",
1544
+ "app",
1545
+ "mobile",
1546
+ "react",
1547
+ "vue",
1548
+ "angular",
1549
+ "next",
1550
+ "nuxt"
1551
+ ],
1552
+ backend: [
1553
+ "api",
1554
+ "server",
1555
+ "backend",
1556
+ "service",
1557
+ "worker",
1558
+ "lambda",
1559
+ "functions"
1560
+ ],
1561
+ shared: ["shared", "common", "utils", "lib", "core", "types", "models"],
1562
+ tooling: ["scripts", "tools", "cli", "devtools", "build", "config", "infra"],
1563
+ unknown: []
1564
+ };
1565
+ PROJECT_PATTERNS = [
1566
+ { pattern: /^apps\/([^/]+)/, type: "app", defaultScope: "unknown" },
1567
+ { pattern: /^packages\/([^/]+)/, type: "library", defaultScope: "shared" },
1568
+ { pattern: /^libs\/([^/]+)/, type: "library", defaultScope: "shared" },
1569
+ { pattern: /^services\/([^/]+)/, type: "service", defaultScope: "backend" },
1570
+ { pattern: /^scripts\/([^/]+)/, type: "script", defaultScope: "tooling" },
1571
+ { pattern: /^tools\/([^/]+)/, type: "script", defaultScope: "tooling" }
1572
+ ];
1660
1573
  });
1661
1574
 
1662
1575
  // src/modules/core/symbols.ts
@@ -1826,8 +1739,8 @@ var exports_core = {};
1826
1739
  __export(exports_core, {
1827
1740
  CoreModule: () => CoreModule
1828
1741
  });
1829
- import * as path7 from "path";
1830
- import * as fs4 from "fs/promises";
1742
+ import * as path5 from "path";
1743
+ import * as fs2 from "fs/promises";
1831
1744
 
1832
1745
  class CoreModule {
1833
1746
  id = "core";
@@ -1845,7 +1758,9 @@ class CoreModule {
1845
1758
  const contentTokens = tokenize(content);
1846
1759
  const intro = ctx.getIntrospection?.(filepath);
1847
1760
  const introKeywords = intro ? introspectionToKeywords(intro) : [];
1848
- const allTokens = [...new Set([...contentTokens, ...symbolKeywords, ...introKeywords])];
1761
+ const allTokens = [
1762
+ ...new Set([...contentTokens, ...symbolKeywords, ...introKeywords])
1763
+ ];
1849
1764
  const chunks = this.createChunks(filepath, content, symbols);
1850
1765
  const stats = await ctx.getFileStats(filepath);
1851
1766
  this.symbolIndex.set(filepath, {
@@ -1919,8 +1834,8 @@ class CoreModule {
1919
1834
  }
1920
1835
  async finalize(ctx) {
1921
1836
  const config = ctx.config;
1922
- const coreDir = path7.join(getRaggrepDir(ctx.rootDir, config), "index", "core");
1923
- await fs4.mkdir(coreDir, { recursive: true });
1837
+ const coreDir = path5.join(getRaggrepDir(ctx.rootDir, config), "index", "core");
1838
+ await fs2.mkdir(coreDir, { recursive: true });
1924
1839
  this.bm25Index = new BM25Index;
1925
1840
  for (const [filepath, entry] of this.symbolIndex) {
1926
1841
  this.bm25Index.addDocument(filepath, entry.tokens);
@@ -1931,7 +1846,7 @@ class CoreModule {
1931
1846
  files: Object.fromEntries(this.symbolIndex),
1932
1847
  bm25Data: this.bm25Index.serialize()
1933
1848
  };
1934
- await fs4.writeFile(path7.join(coreDir, "symbols.json"), JSON.stringify(symbolIndexData, null, 2));
1849
+ await fs2.writeFile(path5.join(coreDir, "symbols.json"), JSON.stringify(symbolIndexData, null, 2));
1935
1850
  console.log(` [Core] Symbol index built with ${this.symbolIndex.size} files`);
1936
1851
  }
1937
1852
  async search(query, ctx, options) {
@@ -2028,10 +1943,10 @@ class CoreModule {
2028
1943
  return bestChunk;
2029
1944
  }
2030
1945
  async loadSymbolIndex(rootDir, config) {
2031
- const coreDir = path7.join(getRaggrepDir(rootDir, config), "index", "core");
2032
- const symbolsPath = path7.join(coreDir, "symbols.json");
1946
+ const coreDir = path5.join(getRaggrepDir(rootDir, config), "index", "core");
1947
+ const symbolsPath = path5.join(coreDir, "symbols.json");
2033
1948
  try {
2034
- const content = await fs4.readFile(symbolsPath, "utf-8");
1949
+ const content = await fs2.readFile(symbolsPath, "utf-8");
2035
1950
  const data = JSON.parse(content);
2036
1951
  this.symbolIndex = new Map(Object.entries(data.files));
2037
1952
  if (data.bm25Data) {
@@ -2056,7 +1971,7 @@ var init_core = __esm(() => {
2056
1971
 
2057
1972
  // src/infrastructure/embeddings/transformersEmbedding.ts
2058
1973
  import { pipeline, env } from "@xenova/transformers";
2059
- import * as path8 from "path";
1974
+ import * as path6 from "path";
2060
1975
  import * as os2 from "os";
2061
1976
 
2062
1977
  class TransformersEmbeddingProvider {
@@ -2189,7 +2104,7 @@ async function getEmbeddings(texts) {
2189
2104
  }
2190
2105
  var CACHE_DIR, EMBEDDING_MODELS2, EMBEDDING_DIMENSION = 384, BATCH_SIZE = 32, globalProvider = null, globalConfig;
2191
2106
  var init_transformersEmbedding = __esm(() => {
2192
- CACHE_DIR = path8.join(os2.homedir(), ".cache", "raggrep", "models");
2107
+ CACHE_DIR = path6.join(os2.homedir(), ".cache", "raggrep", "models");
2193
2108
  env.cacheDir = CACHE_DIR;
2194
2109
  env.allowLocalModels = true;
2195
2110
  EMBEDDING_MODELS2 = {
@@ -2613,8 +2528,8 @@ var init_keywords = __esm(() => {
2613
2528
  });
2614
2529
 
2615
2530
  // src/infrastructure/storage/symbolicIndex.ts
2616
- import * as fs5 from "fs/promises";
2617
- import * as path9 from "path";
2531
+ import * as fs3 from "fs/promises";
2532
+ import * as path7 from "path";
2618
2533
 
2619
2534
  class SymbolicIndex {
2620
2535
  meta = null;
@@ -2623,7 +2538,7 @@ class SymbolicIndex {
2623
2538
  symbolicPath;
2624
2539
  moduleId;
2625
2540
  constructor(indexDir, moduleId) {
2626
- this.symbolicPath = path9.join(indexDir, "index", moduleId, "symbolic");
2541
+ this.symbolicPath = path7.join(indexDir, "index", moduleId, "symbolic");
2627
2542
  this.moduleId = moduleId;
2628
2543
  }
2629
2544
  async initialize() {
@@ -2683,18 +2598,18 @@ class SymbolicIndex {
2683
2598
  throw new Error("Index not initialized");
2684
2599
  this.meta.lastUpdated = new Date().toISOString();
2685
2600
  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));
2601
+ await fs3.mkdir(this.symbolicPath, { recursive: true });
2602
+ const metaPath = path7.join(this.symbolicPath, "_meta.json");
2603
+ await fs3.writeFile(metaPath, JSON.stringify(this.meta, null, 2));
2689
2604
  for (const [filepath, summary] of this.fileSummaries) {
2690
2605
  const summaryPath = this.getFileSummaryPath(filepath);
2691
- await fs5.mkdir(path9.dirname(summaryPath), { recursive: true });
2692
- await fs5.writeFile(summaryPath, JSON.stringify(summary, null, 2));
2606
+ await fs3.mkdir(path7.dirname(summaryPath), { recursive: true });
2607
+ await fs3.writeFile(summaryPath, JSON.stringify(summary, null, 2));
2693
2608
  }
2694
2609
  }
2695
2610
  async load() {
2696
- const metaPath = path9.join(this.symbolicPath, "_meta.json");
2697
- const metaContent = await fs5.readFile(metaPath, "utf-8");
2611
+ const metaPath = path7.join(this.symbolicPath, "_meta.json");
2612
+ const metaContent = await fs3.readFile(metaPath, "utf-8");
2698
2613
  this.meta = JSON.parse(metaContent);
2699
2614
  this.fileSummaries.clear();
2700
2615
  await this.loadFileSummariesRecursive(this.symbolicPath);
@@ -2702,14 +2617,14 @@ class SymbolicIndex {
2702
2617
  }
2703
2618
  async loadFileSummariesRecursive(dir) {
2704
2619
  try {
2705
- const entries = await fs5.readdir(dir, { withFileTypes: true });
2620
+ const entries = await fs3.readdir(dir, { withFileTypes: true });
2706
2621
  for (const entry of entries) {
2707
- const fullPath = path9.join(dir, entry.name);
2622
+ const fullPath = path7.join(dir, entry.name);
2708
2623
  if (entry.isDirectory()) {
2709
2624
  await this.loadFileSummariesRecursive(fullPath);
2710
2625
  } else if (entry.name.endsWith(".json") && entry.name !== "_meta.json") {
2711
2626
  try {
2712
- const content = await fs5.readFile(fullPath, "utf-8");
2627
+ const content = await fs3.readFile(fullPath, "utf-8");
2713
2628
  const summary = JSON.parse(content);
2714
2629
  if (summary.filepath) {
2715
2630
  this.fileSummaries.set(summary.filepath, summary);
@@ -2721,18 +2636,18 @@ class SymbolicIndex {
2721
2636
  }
2722
2637
  getFileSummaryPath(filepath) {
2723
2638
  const jsonPath = filepath.replace(/\.[^.]+$/, ".json");
2724
- return path9.join(this.symbolicPath, jsonPath);
2639
+ return path7.join(this.symbolicPath, jsonPath);
2725
2640
  }
2726
2641
  async deleteFileSummary(filepath) {
2727
2642
  try {
2728
- await fs5.unlink(this.getFileSummaryPath(filepath));
2643
+ await fs3.unlink(this.getFileSummaryPath(filepath));
2729
2644
  } catch {}
2730
2645
  this.fileSummaries.delete(filepath);
2731
2646
  }
2732
2647
  async exists() {
2733
2648
  try {
2734
- const metaPath = path9.join(this.symbolicPath, "_meta.json");
2735
- await fs5.access(metaPath);
2649
+ const metaPath = path7.join(this.symbolicPath, "_meta.json");
2650
+ await fs3.access(metaPath);
2736
2651
  return true;
2737
2652
  } catch {
2738
2653
  return false;
@@ -2771,7 +2686,7 @@ __export(exports_typescript, {
2771
2686
  DEFAULT_TOP_K: () => DEFAULT_TOP_K2,
2772
2687
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE2
2773
2688
  });
2774
- import * as path10 from "path";
2689
+ import * as path8 from "path";
2775
2690
 
2776
2691
  class TypeScriptModule {
2777
2692
  id = "language/typescript";
@@ -2965,16 +2880,16 @@ class TypeScriptModule {
2965
2880
  while ((match = importRegex.exec(content)) !== null) {
2966
2881
  const importPath = match[1];
2967
2882
  if (importPath.startsWith(".")) {
2968
- const dir = path10.dirname(filepath);
2969
- const resolved = path10.normalize(path10.join(dir, importPath));
2883
+ const dir = path8.dirname(filepath);
2884
+ const resolved = path8.normalize(path8.join(dir, importPath));
2970
2885
  references.push(resolved);
2971
2886
  }
2972
2887
  }
2973
2888
  while ((match = requireRegex.exec(content)) !== null) {
2974
2889
  const importPath = match[1];
2975
2890
  if (importPath.startsWith(".")) {
2976
- const dir = path10.dirname(filepath);
2977
- const resolved = path10.normalize(path10.join(dir, importPath));
2891
+ const dir = path8.dirname(filepath);
2892
+ const resolved = path8.normalize(path8.join(dir, importPath));
2978
2893
  references.push(resolved);
2979
2894
  }
2980
2895
  }
@@ -3025,20 +2940,275 @@ async function registerBuiltInModules() {
3025
2940
  registry.register(new TypeScriptModule2);
3026
2941
  }
3027
2942
 
3028
- // src/app/indexer/index.ts
2943
+ // src/infrastructure/introspection/IntrospectionIndex.ts
2944
+ import * as path10 from "path";
2945
+ import * as fs5 from "fs/promises";
2946
+
2947
+ // src/infrastructure/introspection/projectDetector.ts
2948
+ import * as path9 from "path";
2949
+ import * as fs4 from "fs/promises";
2950
+ var MAX_SCAN_DEPTH = 4;
2951
+ var SKIP_DIRS = new Set([
2952
+ "node_modules",
2953
+ ".git",
2954
+ "dist",
2955
+ "build",
2956
+ ".next",
2957
+ ".nuxt",
2958
+ "coverage",
2959
+ ".raggrep"
2960
+ ]);
2961
+ async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
2962
+ if (depth > MAX_SCAN_DEPTH)
2963
+ return [];
2964
+ const results = [];
2965
+ const fullDir = currentDir ? path9.join(rootDir, currentDir) : rootDir;
2966
+ try {
2967
+ const entries = await fs4.readdir(fullDir, { withFileTypes: true });
2968
+ const hasPackageJson = entries.some((e) => e.isFile() && e.name === "package.json");
2969
+ if (hasPackageJson && currentDir) {
2970
+ const info = await parsePackageJson(rootDir, currentDir);
2971
+ if (info)
2972
+ results.push(info);
2973
+ }
2974
+ for (const entry of entries) {
2975
+ if (!entry.isDirectory())
2976
+ continue;
2977
+ if (SKIP_DIRS.has(entry.name))
2978
+ continue;
2979
+ const subPath = currentDir ? `${currentDir}/${entry.name}` : entry.name;
2980
+ const subResults = await scanForPackageJsons(rootDir, subPath, depth + 1);
2981
+ results.push(...subResults);
2982
+ }
2983
+ } catch {}
2984
+ return results;
2985
+ }
2986
+ async function parsePackageJson(rootDir, relativePath) {
2987
+ try {
2988
+ const packageJsonPath = path9.join(rootDir, relativePath, "package.json");
2989
+ const content = await fs4.readFile(packageJsonPath, "utf-8");
2990
+ const pkg = JSON.parse(content);
2991
+ const name = pkg.name || path9.basename(relativePath);
2992
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
2993
+ let type = "unknown";
2994
+ if (deps["next"] || deps["react"] || deps["vue"] || deps["svelte"]) {
2995
+ type = "app";
2996
+ } else if (deps["express"] || deps["fastify"] || deps["koa"] || deps["hono"]) {
2997
+ type = "service";
2998
+ } else if (pkg.main || pkg.exports) {
2999
+ type = "library";
3000
+ }
3001
+ const hasWorkspaces = Boolean(pkg.workspaces);
3002
+ return { name, relativePath, type, hasWorkspaces };
3003
+ } catch {
3004
+ return null;
3005
+ }
3006
+ }
3007
+ function getProjectType(patternDir) {
3008
+ switch (patternDir) {
3009
+ case "apps":
3010
+ return "app";
3011
+ case "packages":
3012
+ case "libs":
3013
+ return "library";
3014
+ case "services":
3015
+ return "service";
3016
+ case "scripts":
3017
+ case "tools":
3018
+ return "script";
3019
+ default:
3020
+ return "unknown";
3021
+ }
3022
+ }
3023
+ async function detectProjectStructure(rootDir) {
3024
+ const projectMap = new Map;
3025
+ let isMonorepo = false;
3026
+ try {
3027
+ const entries = await fs4.readdir(rootDir, { withFileTypes: true });
3028
+ const dirNames = entries.filter((e) => e.isDirectory()).map((e) => e.name);
3029
+ const monorepoPatterns = ["apps", "packages", "libs", "services"];
3030
+ const hasMonorepoStructure = monorepoPatterns.some((p) => dirNames.includes(p));
3031
+ if (hasMonorepoStructure) {
3032
+ isMonorepo = true;
3033
+ for (const pattern of monorepoPatterns) {
3034
+ if (!dirNames.includes(pattern))
3035
+ continue;
3036
+ const patternDir = path9.join(rootDir, pattern);
3037
+ try {
3038
+ const subDirs = await fs4.readdir(patternDir, { withFileTypes: true });
3039
+ for (const subDir of subDirs) {
3040
+ if (!subDir.isDirectory())
3041
+ continue;
3042
+ const projectRoot = `${pattern}/${subDir.name}`;
3043
+ const type = getProjectType(pattern);
3044
+ projectMap.set(projectRoot, {
3045
+ name: subDir.name,
3046
+ root: projectRoot,
3047
+ type
3048
+ });
3049
+ }
3050
+ } catch {}
3051
+ }
3052
+ }
3053
+ const packageJsons = await scanForPackageJsons(rootDir);
3054
+ for (const pkg of packageJsons) {
3055
+ if (pkg.hasWorkspaces)
3056
+ isMonorepo = true;
3057
+ if (packageJsons.length > 1)
3058
+ isMonorepo = true;
3059
+ projectMap.set(pkg.relativePath, {
3060
+ name: pkg.name,
3061
+ root: pkg.relativePath,
3062
+ type: pkg.type
3063
+ });
3064
+ }
3065
+ let rootType = "unknown";
3066
+ try {
3067
+ const rootPkgPath = path9.join(rootDir, "package.json");
3068
+ const rootPkg = JSON.parse(await fs4.readFile(rootPkgPath, "utf-8"));
3069
+ if (rootPkg.workspaces)
3070
+ isMonorepo = true;
3071
+ const deps = { ...rootPkg.dependencies, ...rootPkg.devDependencies };
3072
+ if (deps["next"] || deps["react"] || deps["vue"]) {
3073
+ rootType = "app";
3074
+ } else if (deps["express"] || deps["fastify"] || deps["koa"]) {
3075
+ rootType = "service";
3076
+ }
3077
+ } catch {}
3078
+ const projects = Array.from(projectMap.values()).sort((a, b) => a.root.length - b.root.length);
3079
+ return {
3080
+ projects,
3081
+ isMonorepo,
3082
+ rootType: isMonorepo ? undefined : rootType
3083
+ };
3084
+ } catch {
3085
+ return {
3086
+ projects: [],
3087
+ isMonorepo: false,
3088
+ rootType: "unknown"
3089
+ };
3090
+ }
3091
+ }
3092
+
3093
+ // src/infrastructure/introspection/IntrospectionIndex.ts
3029
3094
  init_introspection();
3095
+ init_config2();
3030
3096
 
3097
+ class IntrospectionIndex {
3098
+ rootDir;
3099
+ structure = null;
3100
+ files = new Map;
3101
+ config = {};
3102
+ constructor(rootDir) {
3103
+ this.rootDir = rootDir;
3104
+ }
3105
+ async initialize() {
3106
+ this.structure = await detectProjectStructure(this.rootDir);
3107
+ try {
3108
+ const configPath = path10.join(this.rootDir, ".raggrep", "config.json");
3109
+ const configContent = await fs5.readFile(configPath, "utf-8");
3110
+ const config = JSON.parse(configContent);
3111
+ this.config = config.introspection || {};
3112
+ } catch {}
3113
+ }
3114
+ getStructure() {
3115
+ return this.structure;
3116
+ }
3117
+ addFile(filepath, content) {
3118
+ if (!this.structure) {
3119
+ throw new Error("IntrospectionIndex not initialized");
3120
+ }
3121
+ const intro = introspectFile(filepath, this.structure, content);
3122
+ this.applyOverrides(intro);
3123
+ this.files.set(filepath, intro);
3124
+ return intro;
3125
+ }
3126
+ getFile(filepath) {
3127
+ return this.files.get(filepath);
3128
+ }
3129
+ getAllFiles() {
3130
+ return Array.from(this.files.values());
3131
+ }
3132
+ applyOverrides(intro) {
3133
+ if (!this.config.projects)
3134
+ return;
3135
+ for (const [projectPath, overrides] of Object.entries(this.config.projects)) {
3136
+ if (intro.filepath.startsWith(projectPath + "/") || intro.project.root === projectPath) {
3137
+ if (overrides.scope) {
3138
+ intro.scope = overrides.scope;
3139
+ }
3140
+ if (overrides.framework) {
3141
+ intro.framework = overrides.framework;
3142
+ }
3143
+ break;
3144
+ }
3145
+ }
3146
+ }
3147
+ async save(config) {
3148
+ const introDir = path10.join(getRaggrepDir(this.rootDir, config), "introspection");
3149
+ await fs5.mkdir(introDir, { recursive: true });
3150
+ const projectPath = path10.join(introDir, "_project.json");
3151
+ await fs5.writeFile(projectPath, JSON.stringify({
3152
+ version: "1.0.0",
3153
+ lastUpdated: new Date().toISOString(),
3154
+ structure: this.structure
3155
+ }, null, 2));
3156
+ for (const [filepath, intro] of this.files) {
3157
+ const introFilePath = path10.join(introDir, "files", filepath.replace(/\.[^.]+$/, ".json"));
3158
+ await fs5.mkdir(path10.dirname(introFilePath), { recursive: true });
3159
+ await fs5.writeFile(introFilePath, JSON.stringify(intro, null, 2));
3160
+ }
3161
+ console.log(` [Introspection] Saved metadata for ${this.files.size} files`);
3162
+ }
3163
+ async load(config) {
3164
+ const introDir = path10.join(getRaggrepDir(this.rootDir, config), "introspection");
3165
+ try {
3166
+ const projectPath = path10.join(introDir, "_project.json");
3167
+ const projectContent = await fs5.readFile(projectPath, "utf-8");
3168
+ const projectData = JSON.parse(projectContent);
3169
+ this.structure = projectData.structure;
3170
+ await this.loadFilesRecursive(path10.join(introDir, "files"), "");
3171
+ } catch {
3172
+ this.structure = null;
3173
+ this.files.clear();
3174
+ }
3175
+ }
3176
+ async loadFilesRecursive(basePath, prefix) {
3177
+ try {
3178
+ const entries = await fs5.readdir(basePath, { withFileTypes: true });
3179
+ for (const entry of entries) {
3180
+ const entryPath = path10.join(basePath, entry.name);
3181
+ const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
3182
+ if (entry.isDirectory()) {
3183
+ await this.loadFilesRecursive(entryPath, relativePath);
3184
+ } else if (entry.name.endsWith(".json")) {
3185
+ const content = await fs5.readFile(entryPath, "utf-8");
3186
+ const intro = JSON.parse(content);
3187
+ this.files.set(intro.filepath, intro);
3188
+ }
3189
+ }
3190
+ } catch {}
3191
+ }
3192
+ clear() {
3193
+ this.files.clear();
3194
+ this.structure = null;
3195
+ }
3196
+ }
3031
3197
  // src/app/indexer/watcher.ts
3032
3198
  import { watch } from "chokidar";
3033
3199
  init_config2();
3034
3200
 
3035
3201
  // src/app/indexer/index.ts
3202
+ var INDEX_SCHEMA_VERSION = "1.0.0";
3036
3203
  async function indexDirectory(rootDir, options = {}) {
3037
3204
  const verbose = options.verbose ?? false;
3205
+ const quiet = options.quiet ?? false;
3038
3206
  rootDir = path11.resolve(rootDir);
3039
3207
  const location = getIndexLocation(rootDir);
3040
- console.log(`Indexing directory: ${rootDir}`);
3041
- console.log(`Index location: ${location.indexDir}`);
3208
+ if (!quiet) {
3209
+ console.log(`Indexing directory: ${rootDir}`);
3210
+ console.log(`Index location: ${location.indexDir}`);
3211
+ }
3042
3212
  const config = await loadConfig(rootDir);
3043
3213
  const introspection = new IntrospectionIndex(rootDir);
3044
3214
  await introspection.initialize();
@@ -3051,16 +3221,24 @@ async function indexDirectory(rootDir, options = {}) {
3051
3221
  await registerBuiltInModules();
3052
3222
  const enabledModules = registry.getEnabled(config);
3053
3223
  if (enabledModules.length === 0) {
3054
- console.log("No modules enabled. Check your configuration.");
3224
+ if (!quiet) {
3225
+ console.log("No modules enabled. Check your configuration.");
3226
+ }
3055
3227
  return [];
3056
3228
  }
3057
- console.log(`Enabled modules: ${enabledModules.map((m) => m.id).join(", ")}`);
3229
+ if (!quiet) {
3230
+ console.log(`Enabled modules: ${enabledModules.map((m) => m.id).join(", ")}`);
3231
+ }
3058
3232
  const files = await findFiles(rootDir, config);
3059
- console.log(`Found ${files.length} files to index`);
3233
+ if (!quiet) {
3234
+ console.log(`Found ${files.length} files to index`);
3235
+ }
3060
3236
  const results = [];
3061
3237
  for (const module of enabledModules) {
3062
- console.log(`
3238
+ if (!quiet) {
3239
+ console.log(`
3063
3240
  [${module.name}] Starting indexing...`);
3241
+ }
3064
3242
  const moduleConfig = getModuleConfig(config, module.id);
3065
3243
  if (module.initialize && moduleConfig) {
3066
3244
  const configWithOverrides = { ...moduleConfig };
@@ -3075,7 +3253,9 @@ async function indexDirectory(rootDir, options = {}) {
3075
3253
  const result = await indexWithModule(rootDir, files, module, config, verbose, introspection);
3076
3254
  results.push(result);
3077
3255
  if (module.finalize) {
3078
- console.log(`[${module.name}] Building secondary indexes...`);
3256
+ if (!quiet) {
3257
+ console.log(`[${module.name}] Building secondary indexes...`);
3258
+ }
3079
3259
  const ctx = {
3080
3260
  rootDir,
3081
3261
  config,
@@ -3091,7 +3271,9 @@ async function indexDirectory(rootDir, options = {}) {
3091
3271
  };
3092
3272
  await module.finalize(ctx);
3093
3273
  }
3094
- console.log(`[${module.name}] Complete: ${result.indexed} indexed, ${result.skipped} skipped, ${result.errors} errors`);
3274
+ if (!quiet) {
3275
+ console.log(`[${module.name}] Complete: ${result.indexed} indexed, ${result.skipped} skipped, ${result.errors} errors`);
3276
+ }
3095
3277
  }
3096
3278
  await introspection.save(config);
3097
3279
  await updateGlobalManifest(rootDir, enabledModules, config);
@@ -3202,7 +3384,7 @@ async function writeFileIndex(rootDir, moduleId, filepath, fileIndex, config) {
3202
3384
  async function updateGlobalManifest(rootDir, modules, config) {
3203
3385
  const manifestPath = getGlobalManifestPath(rootDir, config);
3204
3386
  const manifest = {
3205
- version: config.version,
3387
+ version: INDEX_SCHEMA_VERSION,
3206
3388
  lastUpdated: new Date().toISOString(),
3207
3389
  modules: modules.map((m) => m.id)
3208
3390
  };
@@ -3372,6 +3554,20 @@ async function loadGlobalManifest(rootDir, config) {
3372
3554
  return null;
3373
3555
  }
3374
3556
  }
3557
+ function formatModuleName(moduleId) {
3558
+ switch (moduleId) {
3559
+ case "core":
3560
+ return "Core";
3561
+ case "language/typescript":
3562
+ return "TypeScript";
3563
+ default:
3564
+ if (moduleId.startsWith("language/")) {
3565
+ const lang = moduleId.replace("language/", "");
3566
+ return lang.charAt(0).toUpperCase() + lang.slice(1);
3567
+ }
3568
+ return moduleId;
3569
+ }
3570
+ }
3375
3571
  function formatSearchResults(results) {
3376
3572
  if (results.length === 0) {
3377
3573
  return "No results found.";
@@ -3387,6 +3583,7 @@ function formatSearchResults(results) {
3387
3583
  output += `${i + 1}. ${location}${nameInfo}
3388
3584
  `;
3389
3585
  output += ` Score: ${(result.score * 100).toFixed(1)}% | Type: ${chunk.type}`;
3586
+ output += ` | via ${formatModuleName(result.moduleId)}`;
3390
3587
  if (chunk.isExported) {
3391
3588
  output += " | exported";
3392
3589
  }
@@ -3430,4 +3627,4 @@ export {
3430
3627
  cleanup
3431
3628
  };
3432
3629
 
3433
- //# debugId=3C6A4139E7CDC07F64756E2164756E21
3630
+ //# debugId=DEB2F8AAF72AF0A164756E2164756E21