raggrep 0.1.5 → 0.1.7

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 (27) hide show
  1. package/README.md +23 -9
  2. package/dist/app/indexer/index.d.ts +2 -2
  3. package/dist/cli/main.js +1964 -918
  4. package/dist/cli/main.js.map +21 -14
  5. package/dist/domain/entities/conventions.d.ts +63 -0
  6. package/dist/domain/entities/index.d.ts +2 -0
  7. package/dist/domain/services/conventions/configFiles.d.ts +10 -0
  8. package/dist/domain/services/conventions/conventions.test.d.ts +4 -0
  9. package/dist/domain/services/conventions/entryPoints.d.ts +10 -0
  10. package/dist/domain/services/conventions/frameworks/convex.d.ts +11 -0
  11. package/dist/domain/services/conventions/frameworks/index.d.ts +22 -0
  12. package/dist/domain/services/conventions/frameworks/nextjs.d.ts +10 -0
  13. package/dist/domain/services/conventions/index.d.ts +39 -0
  14. package/dist/domain/services/introspection.d.ts +31 -0
  15. package/dist/index.js +1452 -416
  16. package/dist/index.js.map +20 -14
  17. package/dist/infrastructure/config/configLoader.d.ts +22 -3
  18. package/dist/infrastructure/config/index.d.ts +1 -1
  19. package/dist/{introspection/index.d.ts → infrastructure/introspection/IntrospectionIndex.d.ts} +3 -14
  20. package/dist/infrastructure/introspection/index.d.ts +9 -0
  21. package/dist/{introspection → infrastructure/introspection}/projectDetector.d.ts +3 -12
  22. package/dist/tests/integration.test.d.ts +9 -0
  23. package/dist/types.d.ts +4 -4
  24. package/package.json +1 -1
  25. package/dist/introspection/fileIntrospector.d.ts +0 -14
  26. /package/dist/{introspection/types.d.ts → domain/entities/introspection.d.ts} +0 -0
  27. /package/dist/{introspection → domain/services}/introspection.test.d.ts +0 -0
package/dist/cli/main.js CHANGED
@@ -235,7 +235,8 @@ var init_config = __esm(() => {
235
235
  ".go",
236
236
  ".rs",
237
237
  ".java",
238
- ".md"
238
+ ".md",
239
+ ".txt"
239
240
  ];
240
241
  });
241
242
 
@@ -248,20 +249,40 @@ var init_entities = __esm(() => {
248
249
  // src/infrastructure/config/configLoader.ts
249
250
  import * as path2 from "path";
250
251
  import * as fs from "fs/promises";
251
- function getRaggrepDir(rootDir, config = DEFAULT_CONFIG) {
252
- return path2.join(rootDir, config.indexDir);
252
+ import * as os2 from "os";
253
+ import * as crypto from "crypto";
254
+ function hashPath(inputPath) {
255
+ return crypto.createHash("sha256").update(inputPath).digest("hex").slice(0, 12);
256
+ }
257
+ function getRaggrepDir(rootDir, _config = DEFAULT_CONFIG) {
258
+ const absoluteRoot = path2.resolve(rootDir);
259
+ const projectHash = hashPath(absoluteRoot);
260
+ return path2.join(RAGGREP_TEMP_BASE, projectHash);
261
+ }
262
+ function getIndexLocation(rootDir) {
263
+ const absoluteRoot = path2.resolve(rootDir);
264
+ const projectHash = hashPath(absoluteRoot);
265
+ return {
266
+ indexDir: path2.join(RAGGREP_TEMP_BASE, projectHash),
267
+ projectRoot: absoluteRoot,
268
+ projectHash
269
+ };
253
270
  }
254
271
  function getModuleIndexPath(rootDir, moduleId, config = DEFAULT_CONFIG) {
255
- return path2.join(rootDir, config.indexDir, "index", moduleId);
272
+ const indexDir = getRaggrepDir(rootDir, config);
273
+ return path2.join(indexDir, "index", moduleId);
256
274
  }
257
275
  function getModuleManifestPath(rootDir, moduleId, config = DEFAULT_CONFIG) {
258
- return path2.join(rootDir, config.indexDir, "index", moduleId, "manifest.json");
276
+ const indexDir = getRaggrepDir(rootDir, config);
277
+ return path2.join(indexDir, "index", moduleId, "manifest.json");
259
278
  }
260
279
  function getGlobalManifestPath(rootDir, config = DEFAULT_CONFIG) {
261
- return path2.join(rootDir, config.indexDir, "manifest.json");
280
+ const indexDir = getRaggrepDir(rootDir, config);
281
+ return path2.join(indexDir, "manifest.json");
262
282
  }
263
283
  function getConfigPath(rootDir, config = DEFAULT_CONFIG) {
264
- return path2.join(rootDir, config.indexDir, "config.json");
284
+ const indexDir = getRaggrepDir(rootDir, config);
285
+ return path2.join(indexDir, "config.json");
265
286
  }
266
287
  async function loadConfig(rootDir) {
267
288
  const configPath = getConfigPath(rootDir, DEFAULT_CONFIG);
@@ -288,10 +309,11 @@ function getEmbeddingConfigFromModule(moduleConfig) {
288
309
  showProgress: options.showProgress !== false
289
310
  };
290
311
  }
291
- var DEFAULT_CONFIG, EMBEDDING_MODELS2;
312
+ var DEFAULT_CONFIG, RAGGREP_TEMP_BASE, EMBEDDING_MODELS2;
292
313
  var init_configLoader = __esm(() => {
293
314
  init_entities();
294
315
  DEFAULT_CONFIG = createDefaultConfig();
316
+ RAGGREP_TEMP_BASE = path2.join(os2.tmpdir(), "raggrep-indexes");
295
317
  EMBEDDING_MODELS2 = {
296
318
  "all-MiniLM-L6-v2": "Xenova/all-MiniLM-L6-v2",
297
319
  "all-MiniLM-L12-v2": "Xenova/all-MiniLM-L12-v2",
@@ -407,405 +429,1712 @@ function normalizeScore(score, midpoint = 5) {
407
429
  }
408
430
  var BM25_K1 = 1.5, BM25_B = 0.75;
409
431
 
410
- // src/modules/core/symbols.ts
411
- function extractSymbols(content) {
412
- const symbols = [];
413
- const seenSymbols = new Set;
414
- const lines = content.split(`
415
- `);
416
- for (const { type, pattern, exported } of SYMBOL_PATTERNS) {
417
- pattern.lastIndex = 0;
418
- let match;
419
- while ((match = pattern.exec(content)) !== null) {
420
- const name = match[1];
421
- const symbolKey = `${name}:${type}`;
422
- if (seenSymbols.has(symbolKey))
423
- continue;
424
- seenSymbols.add(symbolKey);
425
- const beforeMatch = content.substring(0, match.index);
426
- const line = beforeMatch.split(`
427
- `).length;
428
- symbols.push({
429
- name,
430
- type,
431
- line,
432
- isExported: exported
433
- });
434
- }
435
- }
436
- return symbols.sort((a, b) => a.line - b.line);
432
+ // src/domain/services/conventions/entryPoints.ts
433
+ import * as path3 from "path";
434
+ function getParentFolder(filepath) {
435
+ const dir = path3.dirname(filepath);
436
+ return path3.basename(dir);
437
437
  }
438
- function symbolsToKeywords(symbols) {
439
- const keywords = new Set;
440
- for (const symbol of symbols) {
441
- keywords.add(symbol.name.toLowerCase());
442
- const parts = symbol.name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").toLowerCase().split(/\s+/);
443
- for (const part of parts) {
444
- if (part.length > 2) {
445
- keywords.add(part);
438
+ var entryPointConventions;
439
+ var init_entryPoints = __esm(() => {
440
+ entryPointConventions = [
441
+ {
442
+ id: "index-file",
443
+ name: "Index/Barrel File",
444
+ description: "Module entry point that typically re-exports from other files",
445
+ category: "entry-point",
446
+ match: (filepath, filename) => {
447
+ return /^index\.(ts|tsx|js|jsx|mjs|cjs)$/.test(filename);
448
+ },
449
+ keywords: ["entry", "barrel", "exports", "module"],
450
+ dynamicKeywords: (filepath) => {
451
+ const parent = getParentFolder(filepath);
452
+ if (["src", "lib", "dist", "build", ".", ""].includes(parent)) {
453
+ return [];
454
+ }
455
+ return [parent.toLowerCase()];
456
+ }
457
+ },
458
+ {
459
+ id: "main-file",
460
+ name: "Main Entry Point",
461
+ description: "Application main entry point",
462
+ category: "entry-point",
463
+ match: (filepath, filename) => {
464
+ return /^main\.(ts|tsx|js|jsx|mjs|cjs)$/.test(filename);
465
+ },
466
+ keywords: ["entry", "main", "entrypoint", "bootstrap", "startup"]
467
+ },
468
+ {
469
+ id: "app-component",
470
+ name: "Root App Component",
471
+ description: "Root application component (React, Vue, etc.)",
472
+ category: "entry-point",
473
+ match: (filepath, filename) => {
474
+ return /^App\.(tsx|jsx|vue|svelte)$/.test(filename);
475
+ },
476
+ keywords: ["root", "app", "application", "component", "main"]
477
+ },
478
+ {
479
+ id: "deno-mod",
480
+ name: "Deno Module Entry",
481
+ description: "Deno module entry point",
482
+ category: "entry-point",
483
+ match: (filepath, filename) => {
484
+ return filename === "mod.ts";
485
+ },
486
+ keywords: ["entry", "module", "deno", "exports"],
487
+ dynamicKeywords: (filepath) => {
488
+ const parent = getParentFolder(filepath);
489
+ if (["src", "lib", ".", ""].includes(parent)) {
490
+ return [];
491
+ }
492
+ return [parent.toLowerCase()];
493
+ }
494
+ },
495
+ {
496
+ id: "python-init",
497
+ name: "Python Package Init",
498
+ description: "Python package initialization file",
499
+ category: "entry-point",
500
+ match: (filepath, filename) => {
501
+ return filename === "__init__.py";
502
+ },
503
+ keywords: ["entry", "package", "init", "python", "module"],
504
+ dynamicKeywords: (filepath) => {
505
+ const parent = getParentFolder(filepath);
506
+ if (["src", "lib", ".", ""].includes(parent)) {
507
+ return [];
508
+ }
509
+ return [parent.toLowerCase()];
510
+ }
511
+ },
512
+ {
513
+ id: "rust-lib",
514
+ name: "Rust Library Entry",
515
+ description: "Rust library crate entry point",
516
+ category: "entry-point",
517
+ match: (filepath, filename) => {
518
+ return filename === "lib.rs" || filename === "main.rs";
519
+ },
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()];
446
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"]
447
585
  }
448
- }
449
- return Array.from(keywords);
450
- }
451
- var SYMBOL_PATTERNS;
452
- var init_symbols = __esm(() => {
453
- SYMBOL_PATTERNS = [
586
+ ];
587
+ });
588
+
589
+ // src/domain/services/conventions/configFiles.ts
590
+ var configFileConventions;
591
+ var init_configFiles = __esm(() => {
592
+ configFileConventions = [
454
593
  {
455
- type: "function",
456
- pattern: /^export\s+(?:async\s+)?function\s+(\w+)/gm,
457
- exported: true
594
+ id: "package-json",
595
+ name: "Package.json",
596
+ description: "Node.js package manifest",
597
+ category: "configuration",
598
+ match: (filepath, filename) => filename === "package.json",
599
+ keywords: ["package", "dependencies", "npm", "scripts", "manifest", "node"]
458
600
  },
459
601
  {
460
- type: "function",
461
- pattern: /^export\s+(?:const|let)\s+(\w+)\s*=\s*(?:async\s*)?\(/gm,
462
- exported: true
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"]
463
608
  },
464
609
  {
465
- type: "class",
466
- pattern: /^export\s+(?:abstract\s+)?class\s+(\w+)/gm,
467
- exported: true
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"]
468
616
  },
469
617
  {
470
- type: "interface",
471
- pattern: /^export\s+interface\s+(\w+)/gm,
472
- exported: true
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"]
473
624
  },
474
625
  {
475
- type: "type",
476
- pattern: /^export\s+type\s+(\w+)/gm,
477
- exported: true
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"]
478
632
  },
479
633
  {
480
- type: "enum",
481
- pattern: /^export\s+(?:const\s+)?enum\s+(\w+)/gm,
482
- exported: true
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
+ ]
483
647
  },
484
648
  {
485
- type: "variable",
486
- pattern: /^export\s+(?:const|let|var)\s+(\w+)\s*(?::|=)/gm,
487
- exported: true
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"]
488
655
  },
489
656
  {
490
- type: "function",
491
- pattern: /^export\s+default\s+(?:async\s+)?function\s+(\w+)/gm,
492
- exported: true
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"]
493
663
  },
494
664
  {
495
- type: "class",
496
- pattern: /^export\s+default\s+class\s+(\w+)/gm,
497
- exported: true
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"]
498
671
  },
499
672
  {
500
- type: "function",
501
- pattern: /^(?:async\s+)?function\s+(\w+)/gm,
502
- exported: false
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"]
503
679
  },
504
680
  {
505
- type: "function",
506
- pattern: /^(?:const|let)\s+(\w+)\s*=\s*(?:async\s*)?\(/gm,
507
- exported: false
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
+ ]
508
695
  },
509
696
  {
510
- type: "class",
511
- pattern: /^(?:abstract\s+)?class\s+(\w+)/gm,
512
- exported: false
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"]
513
703
  },
514
704
  {
515
- type: "interface",
516
- pattern: /^interface\s+(\w+)/gm,
517
- exported: false
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"]
518
711
  },
519
712
  {
520
- type: "type",
521
- pattern: /^type\s+(\w+)/gm,
522
- exported: false
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"]
523
719
  },
524
720
  {
525
- type: "enum",
526
- pattern: /^(?:const\s+)?enum\s+(\w+)/gm,
527
- exported: false
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"]
528
727
  },
529
728
  {
530
- type: "function",
531
- pattern: /^def\s+(\w+)\s*\(/gm,
532
- exported: false
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"]
533
735
  },
534
736
  {
535
- type: "class",
536
- pattern: /^class\s+(\w+)(?:\s*\(|:)/gm,
537
- exported: false
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"]
538
743
  },
539
744
  {
540
- type: "function",
541
- pattern: /^func\s+(?:\([^)]+\)\s+)?(\w+)\s*\(/gm,
542
- exported: false
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"]
543
751
  },
544
752
  {
545
- type: "type",
546
- pattern: /^type\s+(\w+)\s+(?:struct|interface)/gm,
547
- exported: false
753
+ id: "flake8",
754
+ name: "Flake8 Config",
755
+ description: "Flake8 linter configuration",
756
+ category: "configuration",
757
+ match: (filepath, filename) => filename === ".flake8",
758
+ keywords: ["python", "flake8", "linting", "lint", "style"]
548
759
  },
549
760
  {
550
- type: "function",
551
- pattern: /^(?:pub\s+)?(?:async\s+)?fn\s+(\w+)/gm,
552
- exported: false
761
+ id: "pylintrc",
762
+ name: "Pylint Config",
763
+ description: "Pylint linter configuration",
764
+ category: "configuration",
765
+ match: (filepath, filename) => filename === ".pylintrc" || filename === "pylintrc" || filename === "pylint.toml",
766
+ keywords: ["python", "pylint", "linting", "lint", "code quality"]
553
767
  },
554
768
  {
555
- type: "type",
556
- pattern: /^(?:pub\s+)?struct\s+(\w+)/gm,
557
- exported: false
769
+ id: "ruff-toml",
770
+ name: "Ruff Config",
771
+ description: "Ruff linter/formatter configuration",
772
+ category: "configuration",
773
+ match: (filepath, filename) => filename === "ruff.toml" || filename === ".ruff.toml",
774
+ keywords: ["python", "ruff", "linting", "formatting", "fast"]
558
775
  },
559
776
  {
560
- type: "enum",
561
- pattern: /^(?:pub\s+)?enum\s+(\w+)/gm,
562
- exported: false
777
+ id: "black-toml",
778
+ name: "Black Config",
779
+ description: "Black formatter configuration",
780
+ category: "configuration",
781
+ match: (filepath, filename) => filename === ".black.toml",
782
+ keywords: ["python", "black", "formatting", "format", "style"]
563
783
  },
564
784
  {
565
- type: "interface",
566
- pattern: /^(?:pub\s+)?trait\s+(\w+)/gm,
567
- exported: false
785
+ id: "tsconfig",
786
+ name: "TypeScript Config",
787
+ description: "TypeScript compiler configuration",
788
+ category: "configuration",
789
+ match: (filepath, filename) => filename === "tsconfig.json" || filename.startsWith("tsconfig.") && filename.endsWith(".json"),
790
+ keywords: [
791
+ "typescript",
792
+ "config",
793
+ "compiler",
794
+ "ts",
795
+ "settings",
796
+ "paths",
797
+ "types"
798
+ ]
799
+ },
800
+ {
801
+ id: "jsconfig",
802
+ name: "JavaScript Config",
803
+ description: "JavaScript project configuration",
804
+ category: "configuration",
805
+ match: (filepath, filename) => filename === "jsconfig.json",
806
+ keywords: ["javascript", "config", "compiler", "js", "settings", "paths"]
807
+ },
808
+ {
809
+ id: "eslint-config",
810
+ name: "ESLint Config",
811
+ description: "ESLint linting configuration",
812
+ category: "configuration",
813
+ match: (filepath, filename) => filename === ".eslintrc" || filename === ".eslintrc.js" || filename === ".eslintrc.cjs" || filename === ".eslintrc.json" || filename === ".eslintrc.yml" || filename === ".eslintrc.yaml" || filename === "eslint.config.js" || filename === "eslint.config.mjs" || filename === "eslint.config.cjs",
814
+ keywords: ["eslint", "linting", "lint", "rules", "code quality"]
815
+ },
816
+ {
817
+ id: "prettier-config",
818
+ name: "Prettier Config",
819
+ description: "Prettier code formatting configuration",
820
+ category: "configuration",
821
+ match: (filepath, filename) => filename === ".prettierrc" || filename === ".prettierrc.js" || filename === ".prettierrc.cjs" || filename === ".prettierrc.json" || filename === ".prettierrc.yml" || filename === ".prettierrc.yaml" || filename === "prettier.config.js" || filename === "prettier.config.cjs" || filename === "prettier.config.mjs",
822
+ keywords: ["prettier", "formatting", "format", "code style", "style"]
823
+ },
824
+ {
825
+ id: "biome-config",
826
+ name: "Biome Config",
827
+ description: "Biome linting and formatting configuration",
828
+ category: "configuration",
829
+ match: (filepath, filename) => filename === "biome.json" || filename === "biome.jsonc",
830
+ keywords: ["biome", "linting", "formatting", "lint", "format"]
831
+ },
832
+ {
833
+ id: "vite-config",
834
+ name: "Vite Config",
835
+ description: "Vite build tool configuration",
836
+ category: "build",
837
+ match: (filepath, filename) => filename === "vite.config.ts" || filename === "vite.config.js" || filename === "vite.config.mjs",
838
+ keywords: ["vite", "bundler", "build", "dev server", "hmr"]
839
+ },
840
+ {
841
+ id: "webpack-config",
842
+ name: "Webpack Config",
843
+ description: "Webpack bundler configuration",
844
+ category: "build",
845
+ match: (filepath, filename) => filename === "webpack.config.js" || filename === "webpack.config.ts" || filename.startsWith("webpack.") && (filename.endsWith(".js") || filename.endsWith(".ts")),
846
+ keywords: ["webpack", "bundler", "build", "loaders", "plugins"]
847
+ },
848
+ {
849
+ id: "rollup-config",
850
+ name: "Rollup Config",
851
+ description: "Rollup bundler configuration",
852
+ category: "build",
853
+ match: (filepath, filename) => filename === "rollup.config.js" || filename === "rollup.config.ts" || filename === "rollup.config.mjs",
854
+ keywords: ["rollup", "bundler", "build", "esm", "bundle"]
855
+ },
856
+ {
857
+ id: "esbuild-config",
858
+ name: "esbuild Config",
859
+ description: "esbuild bundler configuration",
860
+ category: "build",
861
+ match: (filepath, filename) => filename === "esbuild.config.js" || filename === "esbuild.config.ts" || filename === "esbuild.config.mjs",
862
+ keywords: ["esbuild", "bundler", "build", "fast"]
863
+ },
864
+ {
865
+ id: "jest-config",
866
+ name: "Jest Config",
867
+ description: "Jest testing framework configuration",
868
+ category: "test",
869
+ match: (filepath, filename) => filename === "jest.config.js" || filename === "jest.config.ts" || filename === "jest.config.mjs" || filename === "jest.config.cjs" || filename === "jest.config.json",
870
+ keywords: ["jest", "testing", "test", "unit test", "config"]
871
+ },
872
+ {
873
+ id: "vitest-config",
874
+ name: "Vitest Config",
875
+ description: "Vitest testing framework configuration",
876
+ category: "test",
877
+ match: (filepath, filename) => filename === "vitest.config.ts" || filename === "vitest.config.js" || filename === "vitest.config.mts",
878
+ keywords: ["vitest", "testing", "test", "unit test", "config"]
879
+ },
880
+ {
881
+ id: "playwright-config",
882
+ name: "Playwright Config",
883
+ description: "Playwright E2E testing configuration",
884
+ category: "test",
885
+ match: (filepath, filename) => filename === "playwright.config.ts" || filename === "playwright.config.js",
886
+ keywords: ["playwright", "testing", "e2e", "end-to-end", "browser test"]
887
+ },
888
+ {
889
+ id: "cypress-config",
890
+ name: "Cypress Config",
891
+ description: "Cypress E2E testing configuration",
892
+ category: "test",
893
+ match: (filepath, filename) => filename === "cypress.config.ts" || filename === "cypress.config.js" || filename === "cypress.json",
894
+ keywords: ["cypress", "testing", "e2e", "end-to-end", "browser test"]
895
+ },
896
+ {
897
+ id: "tailwind-config",
898
+ name: "Tailwind Config",
899
+ description: "Tailwind CSS configuration",
900
+ category: "configuration",
901
+ match: (filepath, filename) => filename === "tailwind.config.js" || filename === "tailwind.config.ts" || filename === "tailwind.config.cjs" || filename === "tailwind.config.mjs",
902
+ keywords: ["tailwind", "css", "styling", "utility", "design"]
903
+ },
904
+ {
905
+ id: "postcss-config",
906
+ name: "PostCSS Config",
907
+ description: "PostCSS configuration",
908
+ category: "configuration",
909
+ match: (filepath, filename) => filename === "postcss.config.js" || filename === "postcss.config.cjs" || filename === "postcss.config.mjs" || filename === ".postcssrc" || filename === ".postcssrc.json",
910
+ keywords: ["postcss", "css", "styling", "transforms"]
911
+ },
912
+ {
913
+ id: "env-file",
914
+ name: "Environment File",
915
+ description: "Environment variables file",
916
+ category: "configuration",
917
+ match: (filepath, filename) => filename === ".env" || filename === ".env.local" || filename === ".env.development" || filename === ".env.production" || filename === ".env.test" || filename.startsWith(".env."),
918
+ keywords: ["environment", "env", "variables", "secrets", "config"]
919
+ },
920
+ {
921
+ id: "env-example",
922
+ name: "Environment Example",
923
+ description: "Example environment variables file",
924
+ category: "documentation",
925
+ match: (filepath, filename) => filename === ".env.example" || filename === ".env.sample" || filename === ".env.template",
926
+ keywords: ["environment", "env", "example", "template", "setup"]
927
+ },
928
+ {
929
+ id: "dockerfile",
930
+ name: "Dockerfile",
931
+ description: "Docker container image definition",
932
+ category: "deployment",
933
+ match: (filepath, filename) => filename === "Dockerfile" || filename.startsWith("Dockerfile."),
934
+ keywords: ["docker", "container", "image", "deployment", "build"]
935
+ },
936
+ {
937
+ id: "docker-compose",
938
+ name: "Docker Compose",
939
+ description: "Docker Compose multi-container configuration",
940
+ category: "deployment",
941
+ match: (filepath, filename) => filename === "docker-compose.yml" || filename === "docker-compose.yaml" || filename === "compose.yml" || filename === "compose.yaml" || filename.startsWith("docker-compose."),
942
+ keywords: ["docker", "compose", "containers", "services", "deployment"]
943
+ },
944
+ {
945
+ id: "github-actions",
946
+ name: "GitHub Actions Workflow",
947
+ description: "GitHub Actions CI/CD workflow",
948
+ category: "deployment",
949
+ match: (filepath) => filepath.includes(".github/workflows/") && filepath.endsWith(".yml"),
950
+ keywords: ["github", "actions", "ci", "cd", "workflow", "automation"]
951
+ },
952
+ {
953
+ id: "vercel-config",
954
+ name: "Vercel Config",
955
+ description: "Vercel deployment configuration",
956
+ category: "deployment",
957
+ match: (filepath, filename) => filename === "vercel.json",
958
+ keywords: ["vercel", "deployment", "hosting", "serverless"]
959
+ },
960
+ {
961
+ id: "netlify-config",
962
+ name: "Netlify Config",
963
+ description: "Netlify deployment configuration",
964
+ category: "deployment",
965
+ match: (filepath, filename) => filename === "netlify.toml",
966
+ keywords: ["netlify", "deployment", "hosting", "functions"]
967
+ },
968
+ {
969
+ id: "gitignore",
970
+ name: "Git Ignore",
971
+ description: "Git ignored files configuration",
972
+ category: "configuration",
973
+ match: (filepath, filename) => filename === ".gitignore",
974
+ keywords: ["git", "ignore", "version control", "excluded"]
975
+ },
976
+ {
977
+ id: "gitattributes",
978
+ name: "Git Attributes",
979
+ description: "Git file attributes configuration",
980
+ category: "configuration",
981
+ match: (filepath, filename) => filename === ".gitattributes",
982
+ keywords: ["git", "attributes", "version control", "line endings"]
983
+ },
984
+ {
985
+ id: "readme",
986
+ name: "README",
987
+ description: "Project documentation",
988
+ category: "documentation",
989
+ match: (filepath, filename) => filename.toLowerCase() === "readme.md" || filename.toLowerCase() === "readme",
990
+ keywords: [
991
+ "readme",
992
+ "documentation",
993
+ "docs",
994
+ "overview",
995
+ "getting started"
996
+ ]
997
+ },
998
+ {
999
+ id: "changelog",
1000
+ name: "Changelog",
1001
+ description: "Project changelog",
1002
+ category: "documentation",
1003
+ match: (filepath, filename) => filename.toLowerCase() === "changelog.md" || filename.toLowerCase() === "changelog",
1004
+ keywords: ["changelog", "changes", "releases", "history", "versions"]
1005
+ },
1006
+ {
1007
+ id: "contributing",
1008
+ name: "Contributing Guide",
1009
+ description: "Contribution guidelines",
1010
+ category: "documentation",
1011
+ match: (filepath, filename) => filename.toLowerCase() === "contributing.md" || filename.toLowerCase() === "contributing",
1012
+ keywords: ["contributing", "contribution", "guidelines", "development"]
1013
+ },
1014
+ {
1015
+ id: "license",
1016
+ name: "License",
1017
+ description: "Project license",
1018
+ category: "documentation",
1019
+ match: (filepath, filename) => filename.toLowerCase() === "license" || filename.toLowerCase() === "license.md" || filename.toLowerCase() === "license.txt",
1020
+ keywords: ["license", "legal", "copyright", "terms"]
568
1021
  }
569
1022
  ];
570
1023
  });
571
1024
 
572
- // src/modules/core/index.ts
573
- var exports_core = {};
574
- __export(exports_core, {
575
- CoreModule: () => CoreModule
576
- });
577
- import * as path3 from "path";
578
- import * as fs2 from "fs/promises";
579
-
580
- class CoreModule {
581
- id = "core";
582
- name = "Core Search";
583
- description = "Language-agnostic text search with symbol extraction";
584
- version = "1.0.0";
585
- symbolIndex = new Map;
586
- bm25Index = null;
587
- rootDir = "";
588
- async initialize(_config) {}
589
- async indexFile(filepath, content, ctx) {
590
- this.rootDir = ctx.rootDir;
591
- const symbols = extractSymbols(content);
592
- const symbolKeywords = symbolsToKeywords(symbols);
593
- const contentTokens = tokenize(content);
594
- const allTokens = [...new Set([...contentTokens, ...symbolKeywords])];
595
- const chunks = this.createChunks(filepath, content, symbols);
596
- const stats = await ctx.getFileStats(filepath);
597
- this.symbolIndex.set(filepath, {
598
- filepath,
599
- symbols,
600
- tokens: allTokens
601
- });
602
- const moduleData = {
603
- symbols,
604
- tokens: allTokens
605
- };
606
- return {
607
- filepath,
608
- lastModified: stats.lastModified,
609
- chunks,
610
- moduleData
611
- };
612
- }
613
- createChunks(filepath, content, symbols) {
614
- const lines = content.split(`
615
- `);
616
- const chunks = [];
617
- for (let start = 0;start < lines.length; start += LINES_PER_CHUNK - CHUNK_OVERLAP) {
618
- const end = Math.min(start + LINES_PER_CHUNK, lines.length);
619
- const chunkLines = lines.slice(start, end);
620
- const chunkContent = chunkLines.join(`
621
- `);
622
- const chunkSymbols = symbols.filter((s) => s.line >= start + 1 && s.line <= end);
623
- let chunkType = "block";
624
- let chunkName;
625
- let isExported = false;
626
- if (chunkSymbols.length > 0) {
627
- const primarySymbol = chunkSymbols[0];
628
- chunkType = this.symbolTypeToChunkType(primarySymbol.type);
629
- chunkName = primarySymbol.name;
630
- isExported = primarySymbol.isExported;
1025
+ // src/domain/services/conventions/frameworks/nextjs.ts
1026
+ var nextjsConventions, nextjsFramework;
1027
+ var init_nextjs = __esm(() => {
1028
+ nextjsConventions = [
1029
+ {
1030
+ id: "next-config",
1031
+ name: "Next.js Config",
1032
+ description: "Next.js framework configuration",
1033
+ category: "configuration",
1034
+ match: (filepath, filename) => filename === "next.config.js" || filename === "next.config.mjs" || filename === "next.config.ts",
1035
+ keywords: ["nextjs", "next", "config", "framework", "settings"]
1036
+ },
1037
+ {
1038
+ id: "next-env",
1039
+ name: "Next.js Environment Types",
1040
+ description: "Next.js TypeScript environment declarations",
1041
+ category: "types",
1042
+ match: (filepath, filename) => filename === "next-env.d.ts",
1043
+ keywords: ["nextjs", "types", "typescript", "declarations"]
1044
+ },
1045
+ {
1046
+ id: "next-layout",
1047
+ name: "Next.js Layout",
1048
+ description: "Next.js layout component (App Router)",
1049
+ category: "framework",
1050
+ match: (filepath, filename) => (filename === "layout.tsx" || filename === "layout.js") && (filepath.includes("/app/") || filepath.startsWith("app/")),
1051
+ keywords: ["nextjs", "layout", "wrapper", "template", "app router"],
1052
+ dynamicKeywords: (filepath) => {
1053
+ const match = filepath.match(/app\/(.+?)\/layout\./);
1054
+ if (match) {
1055
+ const segments = match[1].split("/").filter((s) => !s.startsWith("(") && !s.startsWith("["));
1056
+ return segments.map((s) => s.toLowerCase());
1057
+ }
1058
+ if (filepath === "app/layout.tsx" || filepath === "app/layout.js") {
1059
+ return ["root", "main"];
1060
+ }
1061
+ return [];
631
1062
  }
632
- const chunkId = `${filepath}:${start + 1}-${end}`;
633
- chunks.push({
634
- id: chunkId,
635
- content: chunkContent,
636
- startLine: start + 1,
637
- endLine: end,
638
- type: chunkType,
639
- name: chunkName,
640
- isExported
641
- });
642
- if (end >= lines.length)
643
- break;
644
- }
645
- return chunks;
646
- }
647
- symbolTypeToChunkType(symbolType) {
648
- switch (symbolType) {
649
- case "function":
650
- case "method":
651
- return "function";
652
- case "class":
653
- return "class";
654
- case "interface":
655
- return "interface";
656
- case "type":
657
- return "type";
658
- case "enum":
659
- return "enum";
660
- case "variable":
661
- return "variable";
662
- default:
663
- return "block";
664
- }
665
- }
666
- async finalize(ctx) {
667
- const config = ctx.config;
668
- const coreDir = path3.join(getRaggrepDir(ctx.rootDir, config), "index", "core");
669
- await fs2.mkdir(coreDir, { recursive: true });
670
- this.bm25Index = new BM25Index;
671
- for (const [filepath, entry] of this.symbolIndex) {
672
- this.bm25Index.addDocument(filepath, entry.tokens);
673
- }
674
- const symbolIndexData = {
675
- version: this.version,
676
- lastUpdated: new Date().toISOString(),
677
- files: Object.fromEntries(this.symbolIndex),
678
- bm25Data: this.bm25Index.serialize()
679
- };
680
- await fs2.writeFile(path3.join(coreDir, "symbols.json"), JSON.stringify(symbolIndexData, null, 2));
681
- console.log(` [Core] Symbol index built with ${this.symbolIndex.size} files`);
682
- }
683
- async search(query, ctx, options) {
684
- const config = ctx.config;
685
- const topK = options?.topK ?? DEFAULT_TOP_K;
686
- const minScore = options?.minScore ?? DEFAULT_MIN_SCORE;
687
- if (this.symbolIndex.size === 0) {
688
- await this.loadSymbolIndex(ctx.rootDir, config);
689
- }
690
- if (!this.bm25Index || this.symbolIndex.size === 0) {
691
- return [];
692
- }
693
- const queryTokens = tokenize(query);
694
- const bm25Results = this.bm25Index.search(query, topK * 2);
695
- const bm25Scores = new Map(bm25Results.map((r) => [r.id, r.score]));
696
- const symbolMatches = this.findSymbolMatches(queryTokens);
697
- const results = [];
698
- for (const filepath of this.symbolIndex.keys()) {
699
- const entry = this.symbolIndex.get(filepath);
700
- const bm25Score = bm25Scores.get(filepath) ?? 0;
701
- const symbolScore = symbolMatches.get(filepath) ?? 0;
702
- if (bm25Score === 0 && symbolScore === 0)
703
- continue;
704
- const combinedScore = 0.6 * normalizeScore(bm25Score) + 0.4 * symbolScore;
705
- if (combinedScore >= minScore) {
706
- const fileIndex = await ctx.loadFileIndex(filepath);
707
- if (!fileIndex)
708
- continue;
709
- const bestChunk = this.findBestChunk(fileIndex.chunks, queryTokens, entry.symbols);
710
- results.push({
711
- filepath,
712
- chunk: bestChunk,
713
- score: combinedScore,
714
- moduleId: this.id,
715
- context: {
716
- bm25Score: normalizeScore(bm25Score),
717
- symbolScore
718
- }
719
- });
1063
+ },
1064
+ {
1065
+ id: "next-page",
1066
+ name: "Next.js Page",
1067
+ description: "Next.js page component (App Router)",
1068
+ category: "framework",
1069
+ match: (filepath, filename) => (filename === "page.tsx" || filename === "page.js") && (filepath.includes("/app/") || filepath.startsWith("app/")),
1070
+ keywords: ["nextjs", "page", "route", "view", "app router"],
1071
+ dynamicKeywords: (filepath) => {
1072
+ const match = filepath.match(/app\/(.+?)\/page\./);
1073
+ if (match) {
1074
+ const segments = match[1].split("/").filter((s) => !s.startsWith("(")).map((s) => s.replace(/^\[(.+?)\]$/, "$1"));
1075
+ return segments.map((s) => s.toLowerCase());
1076
+ }
1077
+ if (filepath === "app/page.tsx" || filepath === "app/page.js") {
1078
+ return ["home", "index", "root"];
1079
+ }
1080
+ return [];
720
1081
  }
721
- }
722
- return results.sort((a, b) => b.score - a.score).slice(0, topK);
723
- }
724
- findSymbolMatches(queryTokens) {
725
- const matches = new Map;
726
- for (const [filepath, entry] of this.symbolIndex) {
727
- let matchScore = 0;
728
- for (const symbol of entry.symbols) {
729
- const symbolName = symbol.name.toLowerCase();
730
- const symbolParts = symbolsToKeywords([symbol]);
731
- for (const token of queryTokens) {
732
- if (symbolName === token) {
733
- matchScore += symbol.isExported ? 1 : 0.8;
734
- } else if (symbolName.includes(token) || token.includes(symbolName)) {
735
- matchScore += symbol.isExported ? 0.5 : 0.4;
736
- } else if (symbolParts.some((p) => p === token)) {
737
- matchScore += symbol.isExported ? 0.3 : 0.2;
738
- }
1082
+ },
1083
+ {
1084
+ id: "next-loading",
1085
+ name: "Next.js Loading",
1086
+ description: "Next.js loading UI component",
1087
+ category: "framework",
1088
+ match: (filepath, filename) => (filename === "loading.tsx" || filename === "loading.js") && (filepath.includes("/app/") || filepath.startsWith("app/")),
1089
+ keywords: ["nextjs", "loading", "suspense", "skeleton", "spinner"]
1090
+ },
1091
+ {
1092
+ id: "next-error",
1093
+ name: "Next.js Error",
1094
+ description: "Next.js error boundary component",
1095
+ category: "framework",
1096
+ match: (filepath, filename) => (filename === "error.tsx" || filename === "error.js") && (filepath.includes("/app/") || filepath.startsWith("app/")),
1097
+ keywords: ["nextjs", "error", "boundary", "fallback", "catch"]
1098
+ },
1099
+ {
1100
+ id: "next-not-found",
1101
+ name: "Next.js Not Found",
1102
+ description: "Next.js 404 page component",
1103
+ category: "framework",
1104
+ match: (filepath, filename) => (filename === "not-found.tsx" || filename === "not-found.js") && (filepath.includes("/app/") || filepath.startsWith("app/")),
1105
+ keywords: ["nextjs", "404", "not found", "missing", "error"]
1106
+ },
1107
+ {
1108
+ id: "next-template",
1109
+ name: "Next.js Template",
1110
+ description: "Next.js template component",
1111
+ category: "framework",
1112
+ match: (filepath, filename) => (filename === "template.tsx" || filename === "template.js") && (filepath.includes("/app/") || filepath.startsWith("app/")),
1113
+ keywords: ["nextjs", "template", "wrapper", "app router"]
1114
+ },
1115
+ {
1116
+ id: "next-route-handler",
1117
+ name: "Next.js Route Handler",
1118
+ description: "Next.js API route handler (App Router)",
1119
+ category: "framework",
1120
+ match: (filepath, filename) => (filename === "route.ts" || filename === "route.js") && (filepath.includes("/app/") || filepath.startsWith("app/")),
1121
+ keywords: ["nextjs", "api", "route", "handler", "endpoint", "rest"],
1122
+ dynamicKeywords: (filepath) => {
1123
+ const match = filepath.match(/app\/api\/(.+?)\/route\./);
1124
+ if (match) {
1125
+ const segments = match[1].split("/").filter((s) => !s.startsWith("(")).map((s) => s.replace(/^\[(.+?)\]$/, "$1"));
1126
+ return ["api", ...segments.map((s) => s.toLowerCase())];
739
1127
  }
1128
+ return ["api"];
740
1129
  }
741
- if (matchScore > 0) {
742
- matches.set(filepath, Math.min(1, matchScore / queryTokens.length));
1130
+ },
1131
+ {
1132
+ id: "next-middleware",
1133
+ name: "Next.js Middleware",
1134
+ description: "Next.js edge middleware",
1135
+ category: "framework",
1136
+ match: (filepath, filename) => filename === "middleware.ts" || filename === "middleware.js",
1137
+ keywords: ["nextjs", "middleware", "edge", "request", "interceptor"]
1138
+ },
1139
+ {
1140
+ id: "next-global-error",
1141
+ name: "Next.js Global Error",
1142
+ description: "Next.js global error handler",
1143
+ category: "framework",
1144
+ match: (filepath, filename) => filename === "global-error.tsx" || filename === "global-error.js",
1145
+ keywords: ["nextjs", "error", "global", "boundary", "catch"]
1146
+ },
1147
+ {
1148
+ id: "next-pages-api",
1149
+ name: "Next.js API Route (Pages)",
1150
+ description: "Next.js API route (Pages Router)",
1151
+ category: "framework",
1152
+ match: (filepath) => filepath.includes("/pages/api/") || filepath.startsWith("pages/api/"),
1153
+ keywords: ["nextjs", "api", "route", "handler", "endpoint", "pages router"],
1154
+ dynamicKeywords: (filepath) => {
1155
+ const match = filepath.match(/pages\/api\/(.+?)\.(ts|js)/);
1156
+ if (match) {
1157
+ const segments = match[1].split("/").map((s) => s.replace(/^\[(.+?)\]$/, "$1"));
1158
+ return ["api", ...segments.map((s) => s.toLowerCase())];
1159
+ }
1160
+ return ["api"];
743
1161
  }
1162
+ },
1163
+ {
1164
+ id: "next-pages-document",
1165
+ name: "Next.js Document",
1166
+ description: "Next.js custom document (Pages Router)",
1167
+ category: "framework",
1168
+ match: (filepath, filename) => (filename === "_document.tsx" || filename === "_document.js") && (filepath.includes("/pages/") || filepath.startsWith("pages/")),
1169
+ keywords: ["nextjs", "document", "html", "head", "body", "pages router"]
1170
+ },
1171
+ {
1172
+ id: "next-pages-app",
1173
+ name: "Next.js App (Pages)",
1174
+ description: "Next.js custom app (Pages Router)",
1175
+ category: "framework",
1176
+ match: (filepath, filename) => (filename === "_app.tsx" || filename === "_app.js") && (filepath.includes("/pages/") || filepath.startsWith("pages/")),
1177
+ keywords: ["nextjs", "app", "wrapper", "provider", "pages router"]
744
1178
  }
745
- return matches;
746
- }
747
- findBestChunk(chunks, queryTokens, symbols) {
748
- let bestChunk = chunks[0];
749
- let bestScore = 0;
750
- for (const chunk of chunks) {
751
- let score = 0;
752
- const chunkContent = chunk.content.toLowerCase();
753
- for (const token of queryTokens) {
754
- if (chunkContent.includes(token)) {
755
- score += 1;
1179
+ ];
1180
+ nextjsFramework = {
1181
+ id: "nextjs",
1182
+ name: "Next.js",
1183
+ detect: (filepath) => {
1184
+ return filepath === "next.config.js" || filepath === "next.config.mjs" || filepath === "next.config.ts" || filepath.includes("/app/page.") || filepath.includes("/pages/_app.");
1185
+ },
1186
+ conventions: nextjsConventions
1187
+ };
1188
+ });
1189
+
1190
+ // src/domain/services/conventions/frameworks/convex.ts
1191
+ var convexConventions, convexFramework;
1192
+ var init_convex = __esm(() => {
1193
+ convexConventions = [
1194
+ {
1195
+ id: "convex-config",
1196
+ name: "Convex Config",
1197
+ description: "Convex project configuration",
1198
+ category: "configuration",
1199
+ match: (filepath, filename) => filename === "convex.json",
1200
+ keywords: ["convex", "config", "backend", "settings"]
1201
+ },
1202
+ {
1203
+ id: "convex-schema",
1204
+ name: "Convex Schema",
1205
+ description: "Convex database schema definition",
1206
+ category: "framework",
1207
+ match: (filepath, filename) => filename === "schema.ts" && (filepath.includes("/convex/") || filepath.startsWith("convex/")),
1208
+ keywords: ["convex", "schema", "database", "tables", "types", "model"]
1209
+ },
1210
+ {
1211
+ id: "convex-function",
1212
+ name: "Convex Function File",
1213
+ description: "Convex backend function file",
1214
+ category: "framework",
1215
+ match: (filepath, filename, extension) => (extension === ".ts" || extension === ".js") && (filepath.includes("/convex/") || filepath.startsWith("convex/")) && !filepath.includes("/_generated/") && filename !== "schema.ts" && !filename.startsWith("_"),
1216
+ keywords: ["convex", "function", "backend", "query", "mutation", "action"],
1217
+ dynamicKeywords: (filepath) => {
1218
+ const match = filepath.match(/convex\/(.+?)\.(ts|js)/);
1219
+ if (match) {
1220
+ const name = match[1].replace(/\//g, " ").split(" ").pop() || "";
1221
+ if (name && !["schema", "http", "crons"].includes(name)) {
1222
+ return [name.toLowerCase()];
1223
+ }
756
1224
  }
1225
+ return [];
757
1226
  }
758
- if (chunk.name) {
759
- const nameLower = chunk.name.toLowerCase();
760
- for (const token of queryTokens) {
761
- if (nameLower.includes(token)) {
762
- score += 2;
1227
+ },
1228
+ {
1229
+ id: "convex-http",
1230
+ name: "Convex HTTP Routes",
1231
+ description: "Convex HTTP endpoint definitions",
1232
+ category: "framework",
1233
+ match: (filepath, filename) => filename === "http.ts" && (filepath.includes("/convex/") || filepath.startsWith("convex/")),
1234
+ keywords: ["convex", "http", "routes", "api", "endpoints", "rest"]
1235
+ },
1236
+ {
1237
+ id: "convex-crons",
1238
+ name: "Convex Cron Jobs",
1239
+ description: "Convex scheduled function definitions",
1240
+ category: "framework",
1241
+ match: (filepath, filename) => filename === "crons.ts" && (filepath.includes("/convex/") || filepath.startsWith("convex/")),
1242
+ keywords: [
1243
+ "convex",
1244
+ "crons",
1245
+ "scheduled",
1246
+ "jobs",
1247
+ "background",
1248
+ "recurring"
1249
+ ]
1250
+ },
1251
+ {
1252
+ id: "convex-generated",
1253
+ name: "Convex Generated",
1254
+ description: "Convex auto-generated files",
1255
+ category: "framework",
1256
+ match: (filepath) => filepath.includes("/convex/_generated/") || filepath.startsWith("convex/_generated/"),
1257
+ keywords: ["convex", "generated", "types", "api"]
1258
+ },
1259
+ {
1260
+ id: "convex-auth",
1261
+ name: "Convex Auth",
1262
+ description: "Convex authentication configuration",
1263
+ category: "framework",
1264
+ match: (filepath, filename) => filename === "auth.ts" && (filepath.includes("/convex/") || filepath.startsWith("convex/")),
1265
+ keywords: ["convex", "auth", "authentication", "login", "users"]
1266
+ },
1267
+ {
1268
+ id: "convex-auth-config",
1269
+ name: "Convex Auth Config",
1270
+ description: "Convex auth configuration file",
1271
+ category: "configuration",
1272
+ match: (filepath, filename) => filename === "auth.config.ts",
1273
+ keywords: ["convex", "auth", "config", "providers", "oauth"]
1274
+ }
1275
+ ];
1276
+ convexFramework = {
1277
+ id: "convex",
1278
+ name: "Convex",
1279
+ detect: (filepath) => {
1280
+ return filepath === "convex.json" || filepath.startsWith("convex/") || filepath.includes("/convex/");
1281
+ },
1282
+ conventions: convexConventions
1283
+ };
1284
+ });
1285
+
1286
+ // src/domain/services/conventions/frameworks/index.ts
1287
+ function getAllFrameworkConventions() {
1288
+ return frameworkProviders.flatMap((f) => f.conventions);
1289
+ }
1290
+ var frameworkProviders;
1291
+ var init_frameworks = __esm(() => {
1292
+ init_nextjs();
1293
+ init_convex();
1294
+ init_nextjs();
1295
+ init_convex();
1296
+ frameworkProviders = [
1297
+ nextjsFramework,
1298
+ convexFramework
1299
+ ];
1300
+ });
1301
+
1302
+ // src/domain/services/conventions/index.ts
1303
+ import * as path4 from "path";
1304
+ function getConventions() {
1305
+ return [
1306
+ ...entryPointConventions,
1307
+ ...configFileConventions,
1308
+ ...getAllFrameworkConventions(),
1309
+ ...typeDefinitionConventions,
1310
+ ...testFileConventions
1311
+ ];
1312
+ }
1313
+ function getConventionKeywords(filepath) {
1314
+ const conventions = getConventions();
1315
+ const filename = path4.basename(filepath);
1316
+ const extension = path4.extname(filepath);
1317
+ const keywords = new Set;
1318
+ for (const convention of conventions) {
1319
+ try {
1320
+ if (convention.match(filepath, filename, extension)) {
1321
+ for (const keyword of convention.keywords) {
1322
+ keywords.add(keyword.toLowerCase());
1323
+ }
1324
+ if (convention.dynamicKeywords) {
1325
+ const dynamicKws = convention.dynamicKeywords(filepath);
1326
+ for (const kw of dynamicKws) {
1327
+ if (kw && kw.length > 1) {
1328
+ keywords.add(kw.toLowerCase());
1329
+ }
763
1330
  }
764
1331
  }
765
1332
  }
766
- if (chunk.isExported) {
767
- score += 0.5;
768
- }
769
- if (score > bestScore) {
770
- bestScore = score;
771
- bestChunk = chunk;
1333
+ } catch {}
1334
+ }
1335
+ return Array.from(keywords);
1336
+ }
1337
+ var typeDefinitionConventions, testFileConventions;
1338
+ var init_conventions = __esm(() => {
1339
+ init_entryPoints();
1340
+ init_configFiles();
1341
+ init_frameworks();
1342
+ init_entryPoints();
1343
+ init_configFiles();
1344
+ init_frameworks();
1345
+ typeDefinitionConventions = [
1346
+ {
1347
+ id: "dts-file",
1348
+ name: "TypeScript Declaration",
1349
+ description: "TypeScript type declaration file",
1350
+ category: "types",
1351
+ match: (filepath, filename) => filename.endsWith(".d.ts"),
1352
+ keywords: ["types", "declarations", "typescript", "definitions"]
1353
+ },
1354
+ {
1355
+ id: "types-file",
1356
+ name: "Types File",
1357
+ description: "TypeScript types file",
1358
+ category: "types",
1359
+ match: (filepath, filename) => filename.endsWith(".types.ts") || filename === "types.ts",
1360
+ keywords: ["types", "definitions", "typescript", "interfaces"],
1361
+ dynamicKeywords: (filepath) => {
1362
+ const match = filepath.match(/([^/]+)\.types\.ts$/);
1363
+ if (match)
1364
+ return [match[1].toLowerCase()];
1365
+ return [];
772
1366
  }
1367
+ },
1368
+ {
1369
+ id: "types-folder",
1370
+ name: "Types Folder File",
1371
+ description: "File in a types folder",
1372
+ category: "types",
1373
+ match: (filepath) => filepath.includes("/types/") || filepath.startsWith("types/"),
1374
+ keywords: ["types", "definitions"]
773
1375
  }
774
- return bestChunk;
775
- }
776
- async loadSymbolIndex(rootDir, config) {
777
- const coreDir = path3.join(getRaggrepDir(rootDir, config), "index", "core");
778
- const symbolsPath = path3.join(coreDir, "symbols.json");
779
- try {
780
- const content = await fs2.readFile(symbolsPath, "utf-8");
781
- const data = JSON.parse(content);
782
- this.symbolIndex = new Map(Object.entries(data.files));
783
- if (data.bm25Data) {
784
- this.bm25Index = BM25Index.deserialize(data.bm25Data);
1376
+ ];
1377
+ testFileConventions = [
1378
+ {
1379
+ id: "test-file",
1380
+ name: "Test File",
1381
+ description: "Unit/integration test file",
1382
+ category: "test",
1383
+ match: (filepath, filename) => filename.includes(".test.") || filename.includes(".spec.") || filename.includes("_test."),
1384
+ keywords: ["test", "spec", "unit test"],
1385
+ dynamicKeywords: (filepath) => {
1386
+ const match = filepath.match(/([^/]+)\.(test|spec)\./);
1387
+ if (match)
1388
+ return [match[1].toLowerCase()];
1389
+ return [];
785
1390
  }
786
- } catch (error) {
787
- this.symbolIndex = new Map;
788
- this.bm25Index = null;
1391
+ },
1392
+ {
1393
+ id: "test-folder",
1394
+ name: "Test Folder File",
1395
+ description: "File in a test folder",
1396
+ category: "test",
1397
+ match: (filepath) => filepath.includes("/__tests__/") || filepath.includes("/test/") || filepath.includes("/tests/") || filepath.startsWith("__tests__/") || filepath.startsWith("test/") || filepath.startsWith("tests/"),
1398
+ keywords: ["test", "testing"]
789
1399
  }
790
- }
791
- async dispose() {
792
- this.symbolIndex.clear();
793
- this.bm25Index = null;
794
- }
795
- }
796
- var DEFAULT_MIN_SCORE = 0.1, DEFAULT_TOP_K = 20, LINES_PER_CHUNK = 50, CHUNK_OVERLAP = 10;
797
- var init_core = __esm(() => {
798
- init_config2();
799
- init_symbols();
1400
+ ];
800
1401
  });
801
1402
 
802
- // src/domain/services/similarity.ts
803
- function cosineSimilarity(a, b) {
804
- if (a.length !== b.length) {
805
- throw new Error(`Vector length mismatch: ${a.length} vs ${b.length}`);
806
- }
807
- let dotProduct = 0;
808
- let normA = 0;
1403
+ // src/domain/services/introspection.ts
1404
+ import * as path5 from "path";
1405
+ function introspectFile(filepath, structure, fileContent) {
1406
+ const normalizedPath = filepath.replace(/\\/g, "/");
1407
+ const segments = normalizedPath.split("/").filter((s) => s.length > 0);
1408
+ const filename = segments[segments.length - 1] || "";
1409
+ const ext = path5.extname(filename);
1410
+ const project = findProjectForFile(normalizedPath, structure);
1411
+ const language = EXTENSION_TO_LANGUAGE[ext] || "unknown";
1412
+ const layer = detectLayer(segments, filename);
1413
+ const domain = detectDomain(segments);
1414
+ const scope = detectScope(segments, project, layer);
1415
+ const framework = fileContent ? detectFramework(fileContent) : undefined;
1416
+ return {
1417
+ filepath: normalizedPath,
1418
+ project,
1419
+ scope,
1420
+ layer,
1421
+ domain,
1422
+ language,
1423
+ framework,
1424
+ depth: segments.length - 1,
1425
+ pathSegments: segments.slice(0, -1)
1426
+ };
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
+ }
1490
+ function detectLayer(segments, filename) {
1491
+ const filenameLower = filename.toLowerCase();
1492
+ for (const [layer, patterns] of Object.entries(LAYER_PATTERNS)) {
1493
+ for (const pattern of patterns) {
1494
+ if (filenameLower.includes(pattern))
1495
+ return layer;
1496
+ }
1497
+ }
1498
+ for (let i = segments.length - 2;i >= 0; i--) {
1499
+ const segment = segments[i].toLowerCase();
1500
+ for (const [layer, patterns] of Object.entries(LAYER_PATTERNS)) {
1501
+ if (patterns.includes(segment))
1502
+ return layer;
1503
+ }
1504
+ }
1505
+ return;
1506
+ }
1507
+ function detectDomain(segments) {
1508
+ const skipSegments = new Set([
1509
+ "src",
1510
+ "lib",
1511
+ "app",
1512
+ "apps",
1513
+ "packages",
1514
+ "services",
1515
+ "modules",
1516
+ "features",
1517
+ ...Object.values(LAYER_PATTERNS).flat()
1518
+ ]);
1519
+ for (const segment of segments) {
1520
+ const segmentLower = segment.toLowerCase();
1521
+ if (skipSegments.has(segmentLower))
1522
+ continue;
1523
+ if (DOMAIN_PATTERNS.includes(segmentLower))
1524
+ return segmentLower;
1525
+ for (const domain of DOMAIN_PATTERNS) {
1526
+ if (segmentLower.startsWith(domain) || segmentLower.endsWith(domain)) {
1527
+ return domain;
1528
+ }
1529
+ }
1530
+ }
1531
+ return;
1532
+ }
1533
+ function detectScope(segments, project, layer) {
1534
+ const projectScope = detectScopeFromName(project.name);
1535
+ if (projectScope !== "unknown")
1536
+ return projectScope;
1537
+ if (layer) {
1538
+ switch (layer) {
1539
+ case "controller":
1540
+ case "repository":
1541
+ case "middleware":
1542
+ return "backend";
1543
+ case "presentation":
1544
+ return "frontend";
1545
+ case "util":
1546
+ case "model":
1547
+ return "shared";
1548
+ case "test":
1549
+ return "tooling";
1550
+ }
1551
+ }
1552
+ for (const segment of segments) {
1553
+ const segmentLower = segment.toLowerCase();
1554
+ if (["server", "api", "backend"].includes(segmentLower))
1555
+ return "backend";
1556
+ if (["client", "web", "frontend", "ui"].includes(segmentLower))
1557
+ return "frontend";
1558
+ if (["shared", "common", "lib", "libs"].includes(segmentLower))
1559
+ return "shared";
1560
+ }
1561
+ return "unknown";
1562
+ }
1563
+ function detectFramework(content) {
1564
+ for (const [framework, indicators] of Object.entries(FRAMEWORK_INDICATORS)) {
1565
+ for (const indicator of indicators) {
1566
+ if (content.includes(`from '${indicator}`) || content.includes(`from "${indicator}`) || content.includes(`require('${indicator}`) || content.includes(`require("${indicator}`)) {
1567
+ return framework;
1568
+ }
1569
+ }
1570
+ }
1571
+ return;
1572
+ }
1573
+ var LAYER_PATTERNS, DOMAIN_PATTERNS, FRAMEWORK_INDICATORS, EXTENSION_TO_LANGUAGE, SCOPE_KEYWORDS, PROJECT_PATTERNS;
1574
+ var init_introspection = __esm(() => {
1575
+ init_conventions();
1576
+ LAYER_PATTERNS = {
1577
+ controller: ["controller", "api", "routes", "route", "handler"],
1578
+ service: ["service", "logic", "usecase", "usecases", "handler"],
1579
+ repository: ["repository", "repo", "dao", "store", "persistence"],
1580
+ model: [
1581
+ "model",
1582
+ "models",
1583
+ "entity",
1584
+ "entities",
1585
+ "schema",
1586
+ "schemas",
1587
+ "types",
1588
+ "type"
1589
+ ],
1590
+ util: ["util", "utils", "helper", "helpers", "common", "lib"],
1591
+ config: ["config", "configuration", "settings"],
1592
+ middleware: ["middleware", "middlewares"],
1593
+ domain: ["domain"],
1594
+ infrastructure: ["infrastructure", "infra"],
1595
+ application: ["application", "app"],
1596
+ presentation: [
1597
+ "presentation",
1598
+ "ui",
1599
+ "views",
1600
+ "view",
1601
+ "component",
1602
+ "components"
1603
+ ],
1604
+ test: ["test", "tests", "spec", "specs", "__tests__", "e2e"]
1605
+ };
1606
+ DOMAIN_PATTERNS = [
1607
+ "auth",
1608
+ "authentication",
1609
+ "user",
1610
+ "users",
1611
+ "account",
1612
+ "accounts",
1613
+ "profile",
1614
+ "profiles",
1615
+ "product",
1616
+ "products",
1617
+ "item",
1618
+ "items",
1619
+ "catalog",
1620
+ "order",
1621
+ "orders",
1622
+ "cart",
1623
+ "checkout",
1624
+ "payment",
1625
+ "payments",
1626
+ "billing",
1627
+ "subscription",
1628
+ "subscriptions",
1629
+ "notification",
1630
+ "notifications",
1631
+ "email",
1632
+ "sms",
1633
+ "report",
1634
+ "reports",
1635
+ "analytics",
1636
+ "metrics",
1637
+ "dashboard",
1638
+ "admin",
1639
+ "settings",
1640
+ "search",
1641
+ "chat",
1642
+ "message",
1643
+ "messages",
1644
+ "feed",
1645
+ "post",
1646
+ "posts",
1647
+ "comment",
1648
+ "comments",
1649
+ "media",
1650
+ "upload",
1651
+ "file",
1652
+ "files",
1653
+ "storage",
1654
+ "cache",
1655
+ "session",
1656
+ "log",
1657
+ "logs",
1658
+ "audit"
1659
+ ];
1660
+ FRAMEWORK_INDICATORS = {
1661
+ nextjs: ["next", "next/"],
1662
+ express: ["express"],
1663
+ fastify: ["fastify"],
1664
+ react: ["react"],
1665
+ vue: ["vue"],
1666
+ angular: ["@angular/"],
1667
+ nestjs: ["@nestjs/"],
1668
+ koa: ["koa"]
1669
+ };
1670
+ EXTENSION_TO_LANGUAGE = {
1671
+ ".ts": "typescript",
1672
+ ".tsx": "typescript",
1673
+ ".js": "javascript",
1674
+ ".jsx": "javascript",
1675
+ ".mjs": "javascript",
1676
+ ".cjs": "javascript",
1677
+ ".py": "python",
1678
+ ".go": "go",
1679
+ ".rs": "rust",
1680
+ ".java": "java",
1681
+ ".kt": "kotlin",
1682
+ ".swift": "swift",
1683
+ ".rb": "ruby",
1684
+ ".php": "php",
1685
+ ".cs": "csharp",
1686
+ ".cpp": "cpp",
1687
+ ".c": "c",
1688
+ ".h": "c",
1689
+ ".hpp": "cpp",
1690
+ ".md": "markdown",
1691
+ ".json": "json",
1692
+ ".yaml": "yaml",
1693
+ ".yml": "yaml",
1694
+ ".txt": "text"
1695
+ };
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
+ ];
1732
+ });
1733
+
1734
+ // src/modules/core/symbols.ts
1735
+ function extractSymbols(content) {
1736
+ const symbols = [];
1737
+ const seenSymbols = new Set;
1738
+ const lines = content.split(`
1739
+ `);
1740
+ for (const { type, pattern, exported } of SYMBOL_PATTERNS) {
1741
+ pattern.lastIndex = 0;
1742
+ let match;
1743
+ while ((match = pattern.exec(content)) !== null) {
1744
+ const name = match[1];
1745
+ const symbolKey = `${name}:${type}`;
1746
+ if (seenSymbols.has(symbolKey))
1747
+ continue;
1748
+ seenSymbols.add(symbolKey);
1749
+ const beforeMatch = content.substring(0, match.index);
1750
+ const line = beforeMatch.split(`
1751
+ `).length;
1752
+ symbols.push({
1753
+ name,
1754
+ type,
1755
+ line,
1756
+ isExported: exported
1757
+ });
1758
+ }
1759
+ }
1760
+ return symbols.sort((a, b) => a.line - b.line);
1761
+ }
1762
+ function symbolsToKeywords(symbols) {
1763
+ const keywords = new Set;
1764
+ for (const symbol of symbols) {
1765
+ keywords.add(symbol.name.toLowerCase());
1766
+ const parts = symbol.name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").toLowerCase().split(/\s+/);
1767
+ for (const part of parts) {
1768
+ if (part.length > 2) {
1769
+ keywords.add(part);
1770
+ }
1771
+ }
1772
+ }
1773
+ return Array.from(keywords);
1774
+ }
1775
+ var SYMBOL_PATTERNS;
1776
+ var init_symbols = __esm(() => {
1777
+ SYMBOL_PATTERNS = [
1778
+ {
1779
+ type: "function",
1780
+ pattern: /^export\s+(?:async\s+)?function\s+(\w+)/gm,
1781
+ exported: true
1782
+ },
1783
+ {
1784
+ type: "function",
1785
+ pattern: /^export\s+(?:const|let)\s+(\w+)\s*=\s*(?:async\s*)?\(/gm,
1786
+ exported: true
1787
+ },
1788
+ {
1789
+ type: "class",
1790
+ pattern: /^export\s+(?:abstract\s+)?class\s+(\w+)/gm,
1791
+ exported: true
1792
+ },
1793
+ {
1794
+ type: "interface",
1795
+ pattern: /^export\s+interface\s+(\w+)/gm,
1796
+ exported: true
1797
+ },
1798
+ {
1799
+ type: "type",
1800
+ pattern: /^export\s+type\s+(\w+)/gm,
1801
+ exported: true
1802
+ },
1803
+ {
1804
+ type: "enum",
1805
+ pattern: /^export\s+(?:const\s+)?enum\s+(\w+)/gm,
1806
+ exported: true
1807
+ },
1808
+ {
1809
+ type: "variable",
1810
+ pattern: /^export\s+(?:const|let|var)\s+(\w+)\s*(?::|=)/gm,
1811
+ exported: true
1812
+ },
1813
+ {
1814
+ type: "function",
1815
+ pattern: /^export\s+default\s+(?:async\s+)?function\s+(\w+)/gm,
1816
+ exported: true
1817
+ },
1818
+ {
1819
+ type: "class",
1820
+ pattern: /^export\s+default\s+class\s+(\w+)/gm,
1821
+ exported: true
1822
+ },
1823
+ {
1824
+ type: "function",
1825
+ pattern: /^(?:async\s+)?function\s+(\w+)/gm,
1826
+ exported: false
1827
+ },
1828
+ {
1829
+ type: "function",
1830
+ pattern: /^(?:const|let)\s+(\w+)\s*=\s*(?:async\s*)?\(/gm,
1831
+ exported: false
1832
+ },
1833
+ {
1834
+ type: "class",
1835
+ pattern: /^(?:abstract\s+)?class\s+(\w+)/gm,
1836
+ exported: false
1837
+ },
1838
+ {
1839
+ type: "interface",
1840
+ pattern: /^interface\s+(\w+)/gm,
1841
+ exported: false
1842
+ },
1843
+ {
1844
+ type: "type",
1845
+ pattern: /^type\s+(\w+)/gm,
1846
+ exported: false
1847
+ },
1848
+ {
1849
+ type: "enum",
1850
+ pattern: /^(?:const\s+)?enum\s+(\w+)/gm,
1851
+ exported: false
1852
+ },
1853
+ {
1854
+ type: "function",
1855
+ pattern: /^def\s+(\w+)\s*\(/gm,
1856
+ exported: false
1857
+ },
1858
+ {
1859
+ type: "class",
1860
+ pattern: /^class\s+(\w+)(?:\s*\(|:)/gm,
1861
+ exported: false
1862
+ },
1863
+ {
1864
+ type: "function",
1865
+ pattern: /^func\s+(?:\([^)]+\)\s+)?(\w+)\s*\(/gm,
1866
+ exported: false
1867
+ },
1868
+ {
1869
+ type: "type",
1870
+ pattern: /^type\s+(\w+)\s+(?:struct|interface)/gm,
1871
+ exported: false
1872
+ },
1873
+ {
1874
+ type: "function",
1875
+ pattern: /^(?:pub\s+)?(?:async\s+)?fn\s+(\w+)/gm,
1876
+ exported: false
1877
+ },
1878
+ {
1879
+ type: "type",
1880
+ pattern: /^(?:pub\s+)?struct\s+(\w+)/gm,
1881
+ exported: false
1882
+ },
1883
+ {
1884
+ type: "enum",
1885
+ pattern: /^(?:pub\s+)?enum\s+(\w+)/gm,
1886
+ exported: false
1887
+ },
1888
+ {
1889
+ type: "interface",
1890
+ pattern: /^(?:pub\s+)?trait\s+(\w+)/gm,
1891
+ exported: false
1892
+ }
1893
+ ];
1894
+ });
1895
+
1896
+ // src/modules/core/index.ts
1897
+ var exports_core = {};
1898
+ __export(exports_core, {
1899
+ CoreModule: () => CoreModule
1900
+ });
1901
+ import * as path6 from "path";
1902
+ import * as fs2 from "fs/promises";
1903
+
1904
+ class CoreModule {
1905
+ id = "core";
1906
+ name = "Core Search";
1907
+ description = "Language-agnostic text search with symbol extraction";
1908
+ version = "1.0.0";
1909
+ symbolIndex = new Map;
1910
+ bm25Index = null;
1911
+ rootDir = "";
1912
+ async initialize(_config) {}
1913
+ async indexFile(filepath, content, ctx) {
1914
+ this.rootDir = ctx.rootDir;
1915
+ const symbols = extractSymbols(content);
1916
+ const symbolKeywords = symbolsToKeywords(symbols);
1917
+ const contentTokens = tokenize(content);
1918
+ const intro = ctx.getIntrospection?.(filepath);
1919
+ const introKeywords = intro ? introspectionToKeywords(intro) : [];
1920
+ const allTokens = [
1921
+ ...new Set([...contentTokens, ...symbolKeywords, ...introKeywords])
1922
+ ];
1923
+ const chunks = this.createChunks(filepath, content, symbols);
1924
+ const stats = await ctx.getFileStats(filepath);
1925
+ this.symbolIndex.set(filepath, {
1926
+ filepath,
1927
+ symbols,
1928
+ tokens: allTokens
1929
+ });
1930
+ const moduleData = {
1931
+ symbols,
1932
+ tokens: allTokens
1933
+ };
1934
+ return {
1935
+ filepath,
1936
+ lastModified: stats.lastModified,
1937
+ chunks,
1938
+ moduleData
1939
+ };
1940
+ }
1941
+ createChunks(filepath, content, symbols) {
1942
+ const lines = content.split(`
1943
+ `);
1944
+ const chunks = [];
1945
+ for (let start = 0;start < lines.length; start += LINES_PER_CHUNK - CHUNK_OVERLAP) {
1946
+ const end = Math.min(start + LINES_PER_CHUNK, lines.length);
1947
+ const chunkLines = lines.slice(start, end);
1948
+ const chunkContent = chunkLines.join(`
1949
+ `);
1950
+ const chunkSymbols = symbols.filter((s) => s.line >= start + 1 && s.line <= end);
1951
+ let chunkType = "block";
1952
+ let chunkName;
1953
+ let isExported = false;
1954
+ if (chunkSymbols.length > 0) {
1955
+ const primarySymbol = chunkSymbols[0];
1956
+ chunkType = this.symbolTypeToChunkType(primarySymbol.type);
1957
+ chunkName = primarySymbol.name;
1958
+ isExported = primarySymbol.isExported;
1959
+ }
1960
+ const chunkId = `${filepath}:${start + 1}-${end}`;
1961
+ chunks.push({
1962
+ id: chunkId,
1963
+ content: chunkContent,
1964
+ startLine: start + 1,
1965
+ endLine: end,
1966
+ type: chunkType,
1967
+ name: chunkName,
1968
+ isExported
1969
+ });
1970
+ if (end >= lines.length)
1971
+ break;
1972
+ }
1973
+ return chunks;
1974
+ }
1975
+ symbolTypeToChunkType(symbolType) {
1976
+ switch (symbolType) {
1977
+ case "function":
1978
+ case "method":
1979
+ return "function";
1980
+ case "class":
1981
+ return "class";
1982
+ case "interface":
1983
+ return "interface";
1984
+ case "type":
1985
+ return "type";
1986
+ case "enum":
1987
+ return "enum";
1988
+ case "variable":
1989
+ return "variable";
1990
+ default:
1991
+ return "block";
1992
+ }
1993
+ }
1994
+ async finalize(ctx) {
1995
+ const config = ctx.config;
1996
+ const coreDir = path6.join(getRaggrepDir(ctx.rootDir, config), "index", "core");
1997
+ await fs2.mkdir(coreDir, { recursive: true });
1998
+ this.bm25Index = new BM25Index;
1999
+ for (const [filepath, entry] of this.symbolIndex) {
2000
+ this.bm25Index.addDocument(filepath, entry.tokens);
2001
+ }
2002
+ const symbolIndexData = {
2003
+ version: this.version,
2004
+ lastUpdated: new Date().toISOString(),
2005
+ files: Object.fromEntries(this.symbolIndex),
2006
+ bm25Data: this.bm25Index.serialize()
2007
+ };
2008
+ await fs2.writeFile(path6.join(coreDir, "symbols.json"), JSON.stringify(symbolIndexData, null, 2));
2009
+ console.log(` [Core] Symbol index built with ${this.symbolIndex.size} files`);
2010
+ }
2011
+ async search(query, ctx, options) {
2012
+ const config = ctx.config;
2013
+ const topK = options?.topK ?? DEFAULT_TOP_K;
2014
+ const minScore = options?.minScore ?? DEFAULT_MIN_SCORE;
2015
+ if (this.symbolIndex.size === 0) {
2016
+ await this.loadSymbolIndex(ctx.rootDir, config);
2017
+ }
2018
+ if (!this.bm25Index || this.symbolIndex.size === 0) {
2019
+ return [];
2020
+ }
2021
+ const queryTokens = tokenize(query);
2022
+ const bm25Results = this.bm25Index.search(query, topK * 2);
2023
+ const bm25Scores = new Map(bm25Results.map((r) => [r.id, r.score]));
2024
+ const symbolMatches = this.findSymbolMatches(queryTokens);
2025
+ const results = [];
2026
+ for (const filepath of this.symbolIndex.keys()) {
2027
+ const entry = this.symbolIndex.get(filepath);
2028
+ const bm25Score = bm25Scores.get(filepath) ?? 0;
2029
+ const symbolScore = symbolMatches.get(filepath) ?? 0;
2030
+ if (bm25Score === 0 && symbolScore === 0)
2031
+ continue;
2032
+ const combinedScore = 0.6 * normalizeScore(bm25Score) + 0.4 * symbolScore;
2033
+ if (combinedScore >= minScore) {
2034
+ const fileIndex = await ctx.loadFileIndex(filepath);
2035
+ if (!fileIndex)
2036
+ continue;
2037
+ const bestChunk = this.findBestChunk(fileIndex.chunks, queryTokens, entry.symbols);
2038
+ results.push({
2039
+ filepath,
2040
+ chunk: bestChunk,
2041
+ score: combinedScore,
2042
+ moduleId: this.id,
2043
+ context: {
2044
+ bm25Score: normalizeScore(bm25Score),
2045
+ symbolScore
2046
+ }
2047
+ });
2048
+ }
2049
+ }
2050
+ return results.sort((a, b) => b.score - a.score).slice(0, topK);
2051
+ }
2052
+ findSymbolMatches(queryTokens) {
2053
+ const matches = new Map;
2054
+ for (const [filepath, entry] of this.symbolIndex) {
2055
+ let matchScore = 0;
2056
+ for (const symbol of entry.symbols) {
2057
+ const symbolName = symbol.name.toLowerCase();
2058
+ const symbolParts = symbolsToKeywords([symbol]);
2059
+ for (const token of queryTokens) {
2060
+ if (symbolName === token) {
2061
+ matchScore += symbol.isExported ? 1 : 0.8;
2062
+ } else if (symbolName.includes(token) || token.includes(symbolName)) {
2063
+ matchScore += symbol.isExported ? 0.5 : 0.4;
2064
+ } else if (symbolParts.some((p) => p === token)) {
2065
+ matchScore += symbol.isExported ? 0.3 : 0.2;
2066
+ }
2067
+ }
2068
+ }
2069
+ if (matchScore > 0) {
2070
+ matches.set(filepath, Math.min(1, matchScore / queryTokens.length));
2071
+ }
2072
+ }
2073
+ return matches;
2074
+ }
2075
+ findBestChunk(chunks, queryTokens, symbols) {
2076
+ let bestChunk = chunks[0];
2077
+ let bestScore = 0;
2078
+ for (const chunk of chunks) {
2079
+ let score = 0;
2080
+ const chunkContent = chunk.content.toLowerCase();
2081
+ for (const token of queryTokens) {
2082
+ if (chunkContent.includes(token)) {
2083
+ score += 1;
2084
+ }
2085
+ }
2086
+ if (chunk.name) {
2087
+ const nameLower = chunk.name.toLowerCase();
2088
+ for (const token of queryTokens) {
2089
+ if (nameLower.includes(token)) {
2090
+ score += 2;
2091
+ }
2092
+ }
2093
+ }
2094
+ if (chunk.isExported) {
2095
+ score += 0.5;
2096
+ }
2097
+ if (score > bestScore) {
2098
+ bestScore = score;
2099
+ bestChunk = chunk;
2100
+ }
2101
+ }
2102
+ return bestChunk;
2103
+ }
2104
+ async loadSymbolIndex(rootDir, config) {
2105
+ const coreDir = path6.join(getRaggrepDir(rootDir, config), "index", "core");
2106
+ const symbolsPath = path6.join(coreDir, "symbols.json");
2107
+ try {
2108
+ const content = await fs2.readFile(symbolsPath, "utf-8");
2109
+ const data = JSON.parse(content);
2110
+ this.symbolIndex = new Map(Object.entries(data.files));
2111
+ if (data.bm25Data) {
2112
+ this.bm25Index = BM25Index.deserialize(data.bm25Data);
2113
+ }
2114
+ } catch (error) {
2115
+ this.symbolIndex = new Map;
2116
+ this.bm25Index = null;
2117
+ }
2118
+ }
2119
+ async dispose() {
2120
+ this.symbolIndex.clear();
2121
+ this.bm25Index = null;
2122
+ }
2123
+ }
2124
+ var DEFAULT_MIN_SCORE = 0.1, DEFAULT_TOP_K = 20, LINES_PER_CHUNK = 50, CHUNK_OVERLAP = 10;
2125
+ var init_core = __esm(() => {
2126
+ init_config2();
2127
+ init_introspection();
2128
+ init_symbols();
2129
+ });
2130
+
2131
+ // src/domain/services/similarity.ts
2132
+ function cosineSimilarity(a, b) {
2133
+ if (a.length !== b.length) {
2134
+ throw new Error(`Vector length mismatch: ${a.length} vs ${b.length}`);
2135
+ }
2136
+ let dotProduct = 0;
2137
+ let normA = 0;
809
2138
  let normB = 0;
810
2139
  for (let i = 0;i < a.length; i++) {
811
2140
  dotProduct += a[i] * b[i];
@@ -1064,7 +2393,7 @@ function parsePathContext(filepath) {
1064
2393
  let layer;
1065
2394
  const allLower = [...dirSegments, filename].map((s) => s.toLowerCase()).join(" ");
1066
2395
  const filenameLower = filename.toLowerCase();
1067
- for (const [layerName, patterns] of Object.entries(LAYER_PATTERNS)) {
2396
+ for (const [layerName, patterns] of Object.entries(LAYER_PATTERNS2)) {
1068
2397
  for (const pattern of patterns) {
1069
2398
  if (filenameLower.includes(pattern)) {
1070
2399
  layer = layerName;
@@ -1079,7 +2408,7 @@ function parsePathContext(filepath) {
1079
2408
  break;
1080
2409
  }
1081
2410
  let domain;
1082
- const layerPatternSet = new Set(Object.values(LAYER_PATTERNS).flat());
2411
+ const layerPatternSet = new Set(Object.values(LAYER_PATTERNS2).flat());
1083
2412
  const reversedSegments = [...dirSegments].reverse();
1084
2413
  for (const segment of reversedSegments) {
1085
2414
  const lower = segment.toLowerCase();
@@ -1117,7 +2446,7 @@ function formatPathContextForEmbedding(pathContext) {
1117
2446
  const unique = [...new Set(parts)];
1118
2447
  return `[${unique.join(" ")}]`;
1119
2448
  }
1120
- var COMMON_KEYWORDS, LAYER_PATTERNS;
2449
+ var COMMON_KEYWORDS, LAYER_PATTERNS2;
1121
2450
  var init_keywords = __esm(() => {
1122
2451
  COMMON_KEYWORDS = new Set([
1123
2452
  "const",
@@ -1187,7 +2516,7 @@ var init_keywords = __esm(() => {
1187
2516
  "has",
1188
2517
  "have"
1189
2518
  ]);
1190
- LAYER_PATTERNS = {
2519
+ LAYER_PATTERNS2 = {
1191
2520
  controller: ["controller", "controllers", "handler", "handlers", "route", "routes", "api"],
1192
2521
  service: ["service", "services", "usecase", "usecases", "application"],
1193
2522
  repository: ["repository", "repositories", "repo", "repos", "dao", "store", "storage"],
@@ -1204,7 +2533,7 @@ var init_keywords = __esm(() => {
1204
2533
 
1205
2534
  // src/infrastructure/storage/symbolicIndex.ts
1206
2535
  import * as fs3 from "fs/promises";
1207
- import * as path4 from "path";
2536
+ import * as path7 from "path";
1208
2537
 
1209
2538
  class SymbolicIndex {
1210
2539
  meta = null;
@@ -1213,7 +2542,7 @@ class SymbolicIndex {
1213
2542
  symbolicPath;
1214
2543
  moduleId;
1215
2544
  constructor(indexDir, moduleId) {
1216
- this.symbolicPath = path4.join(indexDir, "index", moduleId, "symbolic");
2545
+ this.symbolicPath = path7.join(indexDir, "index", moduleId, "symbolic");
1217
2546
  this.moduleId = moduleId;
1218
2547
  }
1219
2548
  async initialize() {
@@ -1274,16 +2603,16 @@ class SymbolicIndex {
1274
2603
  this.meta.lastUpdated = new Date().toISOString();
1275
2604
  this.meta.fileCount = this.fileSummaries.size;
1276
2605
  await fs3.mkdir(this.symbolicPath, { recursive: true });
1277
- const metaPath = path4.join(this.symbolicPath, "_meta.json");
2606
+ const metaPath = path7.join(this.symbolicPath, "_meta.json");
1278
2607
  await fs3.writeFile(metaPath, JSON.stringify(this.meta, null, 2));
1279
2608
  for (const [filepath, summary] of this.fileSummaries) {
1280
2609
  const summaryPath = this.getFileSummaryPath(filepath);
1281
- await fs3.mkdir(path4.dirname(summaryPath), { recursive: true });
2610
+ await fs3.mkdir(path7.dirname(summaryPath), { recursive: true });
1282
2611
  await fs3.writeFile(summaryPath, JSON.stringify(summary, null, 2));
1283
2612
  }
1284
2613
  }
1285
2614
  async load() {
1286
- const metaPath = path4.join(this.symbolicPath, "_meta.json");
2615
+ const metaPath = path7.join(this.symbolicPath, "_meta.json");
1287
2616
  const metaContent = await fs3.readFile(metaPath, "utf-8");
1288
2617
  this.meta = JSON.parse(metaContent);
1289
2618
  this.fileSummaries.clear();
@@ -1294,7 +2623,7 @@ class SymbolicIndex {
1294
2623
  try {
1295
2624
  const entries = await fs3.readdir(dir, { withFileTypes: true });
1296
2625
  for (const entry of entries) {
1297
- const fullPath = path4.join(dir, entry.name);
2626
+ const fullPath = path7.join(dir, entry.name);
1298
2627
  if (entry.isDirectory()) {
1299
2628
  await this.loadFileSummariesRecursive(fullPath);
1300
2629
  } else if (entry.name.endsWith(".json") && entry.name !== "_meta.json") {
@@ -1311,7 +2640,7 @@ class SymbolicIndex {
1311
2640
  }
1312
2641
  getFileSummaryPath(filepath) {
1313
2642
  const jsonPath = filepath.replace(/\.[^.]+$/, ".json");
1314
- return path4.join(this.symbolicPath, jsonPath);
2643
+ return path7.join(this.symbolicPath, jsonPath);
1315
2644
  }
1316
2645
  async deleteFileSummary(filepath) {
1317
2646
  try {
@@ -1321,7 +2650,7 @@ class SymbolicIndex {
1321
2650
  }
1322
2651
  async exists() {
1323
2652
  try {
1324
- const metaPath = path4.join(this.symbolicPath, "_meta.json");
2653
+ const metaPath = path7.join(this.symbolicPath, "_meta.json");
1325
2654
  await fs3.access(metaPath);
1326
2655
  return true;
1327
2656
  } catch {
@@ -1361,7 +2690,7 @@ __export(exports_typescript, {
1361
2690
  DEFAULT_TOP_K: () => DEFAULT_TOP_K2,
1362
2691
  DEFAULT_MIN_SCORE: () => DEFAULT_MIN_SCORE2
1363
2692
  });
1364
- import * as path5 from "path";
2693
+ import * as path8 from "path";
1365
2694
 
1366
2695
  class TypeScriptModule {
1367
2696
  id = "language/typescript";
@@ -1533,549 +2862,235 @@ class TypeScriptModule {
1533
2862
  if (hybridScore >= minScore || bm25Score > 0.3) {
1534
2863
  results.push({
1535
2864
  filepath,
1536
- chunk,
1537
- score: hybridScore,
1538
- moduleId: this.id,
1539
- context: {
1540
- semanticScore,
1541
- bm25Score,
1542
- pathBoost
1543
- }
1544
- });
1545
- }
1546
- }
1547
- results.sort((a, b) => b.score - a.score);
1548
- return results.slice(0, topK);
1549
- }
1550
- extractReferences(content, filepath) {
1551
- const references = [];
1552
- const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
1553
- const requireRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
1554
- let match;
1555
- while ((match = importRegex.exec(content)) !== null) {
1556
- const importPath = match[1];
1557
- if (importPath.startsWith(".")) {
1558
- const dir = path5.dirname(filepath);
1559
- const resolved = path5.normalize(path5.join(dir, importPath));
1560
- references.push(resolved);
1561
- }
1562
- }
1563
- while ((match = requireRegex.exec(content)) !== null) {
1564
- const importPath = match[1];
1565
- if (importPath.startsWith(".")) {
1566
- const dir = path5.dirname(filepath);
1567
- const resolved = path5.normalize(path5.join(dir, importPath));
1568
- references.push(resolved);
1569
- }
1570
- }
1571
- return references;
1572
- }
1573
- }
1574
- var DEFAULT_MIN_SCORE2 = 0.15, DEFAULT_TOP_K2 = 10, SEMANTIC_WEIGHT = 0.7, BM25_WEIGHT = 0.3, TIER1_CANDIDATE_MULTIPLIER = 3;
1575
- var init_typescript = __esm(() => {
1576
- init_embeddings();
1577
- init_config2();
1578
- init_parseCode();
1579
- init_storage();
1580
- init_keywords();
1581
- init_keywords();
1582
- });
1583
-
1584
- // src/modules/registry.ts
1585
- class ModuleRegistryImpl {
1586
- modules = new Map;
1587
- register(module) {
1588
- if (this.modules.has(module.id)) {
1589
- console.warn(`Module '${module.id}' is already registered, overwriting...`);
1590
- }
1591
- this.modules.set(module.id, module);
1592
- }
1593
- get(id) {
1594
- return this.modules.get(id);
1595
- }
1596
- list() {
1597
- return Array.from(this.modules.values());
1598
- }
1599
- getEnabled(config) {
1600
- const enabledIds = new Set(config.modules.filter((m) => m.enabled).map((m) => m.id));
1601
- return this.list().filter((m) => enabledIds.has(m.id));
1602
- }
1603
- }
1604
- async function registerBuiltInModules() {
1605
- const { CoreModule: CoreModule2 } = await Promise.resolve().then(() => (init_core(), exports_core));
1606
- const { TypeScriptModule: TypeScriptModule2 } = await Promise.resolve().then(() => (init_typescript(), exports_typescript));
1607
- registry.register(new CoreModule2);
1608
- registry.register(new TypeScriptModule2);
1609
- }
1610
- var registry;
1611
- var init_registry = __esm(() => {
1612
- registry = new ModuleRegistryImpl;
1613
- });
1614
-
1615
- // src/introspection/projectDetector.ts
1616
- import * as path6 from "path";
1617
- import * as fs4 from "fs/promises";
1618
- function detectScopeFromName(name) {
1619
- const nameLower = name.toLowerCase();
1620
- for (const [scope, keywords] of Object.entries(SCOPE_KEYWORDS)) {
1621
- if (scope === "unknown")
1622
- continue;
1623
- for (const keyword of keywords) {
1624
- if (nameLower.includes(keyword)) {
1625
- return scope;
1626
- }
1627
- }
1628
- }
1629
- return "unknown";
1630
- }
1631
- async function scanForPackageJsons(rootDir, currentDir = "", depth = 0) {
1632
- if (depth > MAX_SCAN_DEPTH)
1633
- return [];
1634
- const results = [];
1635
- const fullDir = currentDir ? path6.join(rootDir, currentDir) : rootDir;
1636
- try {
1637
- const entries = await fs4.readdir(fullDir, { withFileTypes: true });
1638
- const hasPackageJson = entries.some((e) => e.isFile() && e.name === "package.json");
1639
- if (hasPackageJson && currentDir) {
1640
- const info = await parsePackageJson(rootDir, currentDir);
1641
- if (info) {
1642
- results.push(info);
1643
- }
1644
- }
1645
- for (const entry of entries) {
1646
- if (!entry.isDirectory())
1647
- continue;
1648
- if (SKIP_DIRS.has(entry.name))
1649
- continue;
1650
- const subPath = currentDir ? `${currentDir}/${entry.name}` : entry.name;
1651
- const subResults = await scanForPackageJsons(rootDir, subPath, depth + 1);
1652
- results.push(...subResults);
1653
- }
1654
- } catch {}
1655
- return results;
1656
- }
1657
- async function parsePackageJson(rootDir, relativePath) {
1658
- try {
1659
- const packageJsonPath = path6.join(rootDir, relativePath, "package.json");
1660
- const content = await fs4.readFile(packageJsonPath, "utf-8");
1661
- const pkg = JSON.parse(content);
1662
- const name = pkg.name || path6.basename(relativePath);
1663
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
1664
- let type = "unknown";
1665
- if (deps["next"] || deps["react"] || deps["vue"] || deps["svelte"]) {
1666
- type = "app";
1667
- } else if (deps["express"] || deps["fastify"] || deps["koa"] || deps["hono"]) {
1668
- type = "service";
1669
- } else if (pkg.main || pkg.exports) {
1670
- type = "library";
1671
- }
1672
- const hasWorkspaces = Boolean(pkg.workspaces);
1673
- return { name, relativePath, type, hasWorkspaces };
1674
- } catch {
1675
- return null;
1676
- }
1677
- }
1678
- async function detectProjectStructure(rootDir) {
1679
- const projectMap = new Map;
1680
- let isMonorepo = false;
1681
- try {
1682
- const entries = await fs4.readdir(rootDir, { withFileTypes: true });
1683
- const dirNames = entries.filter((e) => e.isDirectory()).map((e) => e.name);
1684
- const monorepoPatterns = ["apps", "packages", "libs", "services"];
1685
- const hasMonorepoStructure = monorepoPatterns.some((p) => dirNames.includes(p));
1686
- if (hasMonorepoStructure) {
1687
- isMonorepo = true;
1688
- for (const pattern of monorepoPatterns) {
1689
- if (!dirNames.includes(pattern))
1690
- continue;
1691
- const patternDir = path6.join(rootDir, pattern);
1692
- try {
1693
- const subDirs = await fs4.readdir(patternDir, { withFileTypes: true });
1694
- for (const subDir of subDirs) {
1695
- if (!subDir.isDirectory())
1696
- continue;
1697
- const projectRoot = `${pattern}/${subDir.name}`;
1698
- const type = getProjectType(pattern);
1699
- projectMap.set(projectRoot, {
1700
- name: subDir.name,
1701
- root: projectRoot,
1702
- type
1703
- });
2865
+ chunk,
2866
+ score: hybridScore,
2867
+ moduleId: this.id,
2868
+ context: {
2869
+ semanticScore,
2870
+ bm25Score,
2871
+ pathBoost
1704
2872
  }
1705
- } catch {}
2873
+ });
1706
2874
  }
1707
2875
  }
1708
- const packageJsons = await scanForPackageJsons(rootDir);
1709
- for (const pkg of packageJsons) {
1710
- if (pkg.hasWorkspaces) {
1711
- isMonorepo = true;
1712
- }
1713
- if (packageJsons.length > 1) {
1714
- isMonorepo = true;
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);
1715
2890
  }
1716
- projectMap.set(pkg.relativePath, {
1717
- name: pkg.name,
1718
- root: pkg.relativePath,
1719
- type: pkg.type
1720
- });
1721
2891
  }
1722
- let rootType = "unknown";
1723
- try {
1724
- const rootPkgPath = path6.join(rootDir, "package.json");
1725
- const rootPkg = JSON.parse(await fs4.readFile(rootPkgPath, "utf-8"));
1726
- if (rootPkg.workspaces) {
1727
- isMonorepo = true;
1728
- }
1729
- const deps = { ...rootPkg.dependencies, ...rootPkg.devDependencies };
1730
- if (deps["next"] || deps["react"] || deps["vue"]) {
1731
- rootType = "app";
1732
- } else if (deps["express"] || deps["fastify"] || deps["koa"]) {
1733
- rootType = "service";
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);
1734
2898
  }
1735
- } catch {}
1736
- const projects = Array.from(projectMap.values()).sort((a, b) => a.root.length - b.root.length);
1737
- return {
1738
- projects,
1739
- isMonorepo,
1740
- rootType: isMonorepo ? undefined : rootType
1741
- };
1742
- } catch {
1743
- return {
1744
- projects: [],
1745
- isMonorepo: false,
1746
- rootType: "unknown"
1747
- };
1748
- }
1749
- }
1750
- function getProjectType(patternDir) {
1751
- switch (patternDir) {
1752
- case "apps":
1753
- return "app";
1754
- case "packages":
1755
- case "libs":
1756
- return "library";
1757
- case "services":
1758
- return "service";
1759
- case "scripts":
1760
- case "tools":
1761
- return "script";
1762
- default:
1763
- return "unknown";
1764
- }
1765
- }
1766
- function findProjectForFile(filepath, structure) {
1767
- const normalizedPath = filepath.replace(/\\/g, "/");
1768
- const matches = [];
1769
- for (const project of structure.projects) {
1770
- if (normalizedPath === project.root || normalizedPath.startsWith(project.root + "/")) {
1771
- matches.push(project);
1772
- }
1773
- }
1774
- if (matches.length > 0) {
1775
- return matches.reduce((best, current) => current.root.length > best.root.length ? current : best);
1776
- }
1777
- for (const { pattern, type } of PROJECT_PATTERNS) {
1778
- const match = normalizedPath.match(pattern);
1779
- if (match) {
1780
- return {
1781
- name: match[1],
1782
- root: match[0],
1783
- type
1784
- };
1785
2899
  }
2900
+ return references;
1786
2901
  }
1787
- return {
1788
- name: "root",
1789
- root: "",
1790
- type: structure.rootType ?? "unknown"
1791
- };
1792
2902
  }
1793
- var MAX_SCAN_DEPTH = 4, SKIP_DIRS, PROJECT_PATTERNS, SCOPE_KEYWORDS;
1794
- var init_projectDetector = __esm(() => {
1795
- SKIP_DIRS = new Set([
1796
- "node_modules",
1797
- ".git",
1798
- "dist",
1799
- "build",
1800
- ".next",
1801
- ".nuxt",
1802
- "coverage",
1803
- ".raggrep"
1804
- ]);
1805
- PROJECT_PATTERNS = [
1806
- { pattern: /^apps\/([^/]+)/, type: "app", defaultScope: "unknown" },
1807
- { pattern: /^packages\/([^/]+)/, type: "library", defaultScope: "shared" },
1808
- { pattern: /^libs\/([^/]+)/, type: "library", defaultScope: "shared" },
1809
- { pattern: /^services\/([^/]+)/, type: "service", defaultScope: "backend" },
1810
- { pattern: /^scripts\/([^/]+)/, type: "script", defaultScope: "tooling" },
1811
- { pattern: /^tools\/([^/]+)/, type: "script", defaultScope: "tooling" }
1812
- ];
1813
- SCOPE_KEYWORDS = {
1814
- frontend: [
1815
- "web",
1816
- "webapp",
1817
- "frontend",
1818
- "client",
1819
- "ui",
1820
- "app",
1821
- "mobile",
1822
- "react",
1823
- "vue",
1824
- "angular",
1825
- "next",
1826
- "nuxt"
1827
- ],
1828
- backend: [
1829
- "api",
1830
- "server",
1831
- "backend",
1832
- "service",
1833
- "worker",
1834
- "lambda",
1835
- "functions"
1836
- ],
1837
- shared: ["shared", "common", "utils", "lib", "core", "types", "models"],
1838
- tooling: [
1839
- "scripts",
1840
- "tools",
1841
- "cli",
1842
- "devtools",
1843
- "build",
1844
- "config",
1845
- "infra"
1846
- ],
1847
- unknown: []
1848
- };
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();
1849
2911
  });
1850
2912
 
1851
- // src/introspection/fileIntrospector.ts
1852
- import * as path7 from "path";
1853
- function introspectFile(filepath, structure, fileContent) {
1854
- const normalizedPath = filepath.replace(/\\/g, "/");
1855
- const segments = normalizedPath.split("/").filter((s) => s.length > 0);
1856
- const filename = segments[segments.length - 1] || "";
1857
- const ext = path7.extname(filename);
1858
- const project = findProjectForFile(normalizedPath, structure);
1859
- const language = EXTENSION_TO_LANGUAGE[ext] || "unknown";
1860
- const layer = detectLayer(segments, filename);
1861
- const domain = detectDomain(segments);
1862
- const scope = detectScope(segments, project, layer);
1863
- let framework;
1864
- if (fileContent) {
1865
- framework = detectFramework(fileContent);
1866
- }
1867
- return {
1868
- filepath: normalizedPath,
1869
- project,
1870
- scope,
1871
- layer,
1872
- domain,
1873
- language,
1874
- framework,
1875
- depth: segments.length - 1,
1876
- pathSegments: segments.slice(0, -1)
1877
- };
1878
- }
1879
- function detectLayer(segments, filename) {
1880
- const filenameLower = filename.toLowerCase();
1881
- for (const [layer, patterns] of Object.entries(LAYER_PATTERNS2)) {
1882
- for (const pattern of patterns) {
1883
- if (filenameLower.includes(pattern)) {
1884
- return layer;
1885
- }
1886
- }
1887
- }
1888
- for (let i = segments.length - 2;i >= 0; i--) {
1889
- const segment = segments[i].toLowerCase();
1890
- for (const [layer, patterns] of Object.entries(LAYER_PATTERNS2)) {
1891
- if (patterns.includes(segment)) {
1892
- return layer;
1893
- }
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...`);
1894
2919
  }
2920
+ this.modules.set(module.id, module);
1895
2921
  }
1896
- return;
1897
- }
1898
- function detectDomain(segments) {
1899
- const skipSegments = new Set([
1900
- "src",
1901
- "lib",
1902
- "app",
1903
- "apps",
1904
- "packages",
1905
- "services",
1906
- "modules",
1907
- "features",
1908
- ...Object.values(LAYER_PATTERNS2).flat()
1909
- ]);
1910
- for (const segment of segments) {
1911
- const segmentLower = segment.toLowerCase();
1912
- if (skipSegments.has(segmentLower))
1913
- continue;
1914
- if (DOMAIN_PATTERNS.includes(segmentLower)) {
1915
- return segmentLower;
1916
- }
1917
- for (const domain of DOMAIN_PATTERNS) {
1918
- if (segmentLower.startsWith(domain) || segmentLower.endsWith(domain)) {
1919
- return domain;
1920
- }
1921
- }
2922
+ get(id) {
2923
+ return this.modules.get(id);
1922
2924
  }
1923
- return;
1924
- }
1925
- function detectScope(segments, project, layer) {
1926
- const projectScope = detectScopeFromName(project.name);
1927
- if (projectScope !== "unknown") {
1928
- return projectScope;
2925
+ list() {
2926
+ return Array.from(this.modules.values());
1929
2927
  }
1930
- if (layer) {
1931
- switch (layer) {
1932
- case "controller":
1933
- case "repository":
1934
- case "middleware":
1935
- return "backend";
1936
- case "presentation":
1937
- return "frontend";
1938
- case "util":
1939
- case "model":
1940
- return "shared";
1941
- case "test":
1942
- return "tooling";
1943
- }
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));
1944
2931
  }
1945
- for (const segment of segments) {
1946
- const segmentLower = segment.toLowerCase();
1947
- if (["server", "api", "backend"].includes(segmentLower)) {
1948
- return "backend";
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);
1949
2959
  }
1950
- if (["client", "web", "frontend", "ui"].includes(segmentLower)) {
1951
- return "frontend";
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);
1952
2968
  }
1953
- if (["shared", "common", "lib", "libs"].includes(segmentLower)) {
1954
- return "shared";
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";
1955
2986
  }
2987
+ const hasWorkspaces = Boolean(pkg.workspaces);
2988
+ return { name, relativePath, type, hasWorkspaces };
2989
+ } catch {
2990
+ return null;
1956
2991
  }
1957
- return "unknown";
1958
2992
  }
1959
- function detectFramework(content) {
1960
- for (const [framework, indicators] of Object.entries(FRAMEWORK_INDICATORS)) {
1961
- for (const indicator of indicators) {
1962
- if (content.includes(`from '${indicator}`) || content.includes(`from "${indicator}`) || content.includes(`require('${indicator}`) || content.includes(`require("${indicator}`)) {
1963
- return framework;
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 {}
1964
3037
  }
1965
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
+ };
1966
3076
  }
1967
- return;
1968
3077
  }
1969
- var LAYER_PATTERNS2, DOMAIN_PATTERNS, FRAMEWORK_INDICATORS, EXTENSION_TO_LANGUAGE;
1970
- var init_fileIntrospector = __esm(() => {
1971
- init_projectDetector();
1972
- LAYER_PATTERNS2 = {
1973
- controller: ["controller", "api", "routes", "route", "handler"],
1974
- service: ["service", "logic", "usecase", "usecases", "handler"],
1975
- repository: ["repository", "repo", "dao", "store", "persistence"],
1976
- model: ["model", "models", "entity", "entities", "schema", "schemas", "types", "type"],
1977
- util: ["util", "utils", "helper", "helpers", "common", "lib"],
1978
- config: ["config", "configuration", "settings"],
1979
- middleware: ["middleware", "middlewares"],
1980
- domain: ["domain"],
1981
- infrastructure: ["infrastructure", "infra"],
1982
- application: ["application", "app"],
1983
- presentation: ["presentation", "ui", "views", "view", "component", "components"],
1984
- test: ["test", "tests", "spec", "specs", "__tests__", "e2e"]
1985
- };
1986
- DOMAIN_PATTERNS = [
1987
- "auth",
1988
- "authentication",
1989
- "user",
1990
- "users",
1991
- "account",
1992
- "accounts",
1993
- "profile",
1994
- "profiles",
1995
- "product",
1996
- "products",
1997
- "item",
1998
- "items",
1999
- "catalog",
2000
- "order",
2001
- "orders",
2002
- "cart",
2003
- "checkout",
2004
- "payment",
2005
- "payments",
2006
- "billing",
2007
- "subscription",
2008
- "subscriptions",
2009
- "notification",
2010
- "notifications",
2011
- "email",
2012
- "sms",
2013
- "report",
2014
- "reports",
2015
- "analytics",
2016
- "metrics",
2017
- "dashboard",
2018
- "admin",
2019
- "settings",
2020
- "search",
2021
- "chat",
2022
- "message",
2023
- "messages",
2024
- "feed",
2025
- "post",
2026
- "posts",
2027
- "comment",
2028
- "comments",
2029
- "media",
2030
- "upload",
2031
- "file",
2032
- "files",
2033
- "storage",
2034
- "cache",
2035
- "session",
2036
- "log",
2037
- "logs",
2038
- "audit"
2039
- ];
2040
- FRAMEWORK_INDICATORS = {
2041
- nextjs: ["next", "next/"],
2042
- express: ["express"],
2043
- fastify: ["fastify"],
2044
- react: ["react"],
2045
- vue: ["vue"],
2046
- angular: ["@angular/"],
2047
- nestjs: ["@nestjs/"],
2048
- koa: ["koa"]
2049
- };
2050
- EXTENSION_TO_LANGUAGE = {
2051
- ".ts": "typescript",
2052
- ".tsx": "typescript",
2053
- ".js": "javascript",
2054
- ".jsx": "javascript",
2055
- ".mjs": "javascript",
2056
- ".cjs": "javascript",
2057
- ".py": "python",
2058
- ".go": "go",
2059
- ".rs": "rust",
2060
- ".java": "java",
2061
- ".kt": "kotlin",
2062
- ".swift": "swift",
2063
- ".rb": "ruby",
2064
- ".php": "php",
2065
- ".cs": "csharp",
2066
- ".cpp": "cpp",
2067
- ".c": "c",
2068
- ".h": "c",
2069
- ".hpp": "cpp",
2070
- ".md": "markdown",
2071
- ".json": "json",
2072
- ".yaml": "yaml",
2073
- ".yml": "yaml"
2074
- };
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
+ ]);
2075
3090
  });
2076
3091
 
2077
- // src/introspection/index.ts
2078
- import * as path8 from "path";
3092
+ // src/infrastructure/introspection/IntrospectionIndex.ts
3093
+ import * as path10 from "path";
2079
3094
  import * as fs5 from "fs/promises";
2080
3095
 
2081
3096
  class IntrospectionIndex {
@@ -2089,7 +3104,7 @@ class IntrospectionIndex {
2089
3104
  async initialize() {
2090
3105
  this.structure = await detectProjectStructure(this.rootDir);
2091
3106
  try {
2092
- const configPath = path8.join(this.rootDir, ".raggrep", "config.json");
3107
+ const configPath = path10.join(this.rootDir, ".raggrep", "config.json");
2093
3108
  const configContent = await fs5.readFile(configPath, "utf-8");
2094
3109
  const config = JSON.parse(configContent);
2095
3110
  this.config = config.introspection || {};
@@ -2129,29 +3144,29 @@ class IntrospectionIndex {
2129
3144
  }
2130
3145
  }
2131
3146
  async save(config) {
2132
- const introDir = path8.join(getRaggrepDir(this.rootDir, config), "introspection");
3147
+ const introDir = path10.join(getRaggrepDir(this.rootDir, config), "introspection");
2133
3148
  await fs5.mkdir(introDir, { recursive: true });
2134
- const projectPath = path8.join(introDir, "_project.json");
3149
+ const projectPath = path10.join(introDir, "_project.json");
2135
3150
  await fs5.writeFile(projectPath, JSON.stringify({
2136
3151
  version: "1.0.0",
2137
3152
  lastUpdated: new Date().toISOString(),
2138
3153
  structure: this.structure
2139
3154
  }, null, 2));
2140
3155
  for (const [filepath, intro] of this.files) {
2141
- const introFilePath = path8.join(introDir, "files", filepath.replace(/\.[^.]+$/, ".json"));
2142
- await fs5.mkdir(path8.dirname(introFilePath), { recursive: true });
3156
+ const introFilePath = path10.join(introDir, "files", filepath.replace(/\.[^.]+$/, ".json"));
3157
+ await fs5.mkdir(path10.dirname(introFilePath), { recursive: true });
2143
3158
  await fs5.writeFile(introFilePath, JSON.stringify(intro, null, 2));
2144
3159
  }
2145
3160
  console.log(` [Introspection] Saved metadata for ${this.files.size} files`);
2146
3161
  }
2147
3162
  async load(config) {
2148
- const introDir = path8.join(getRaggrepDir(this.rootDir, config), "introspection");
3163
+ const introDir = path10.join(getRaggrepDir(this.rootDir, config), "introspection");
2149
3164
  try {
2150
- const projectPath = path8.join(introDir, "_project.json");
3165
+ const projectPath = path10.join(introDir, "_project.json");
2151
3166
  const projectContent = await fs5.readFile(projectPath, "utf-8");
2152
3167
  const projectData = JSON.parse(projectContent);
2153
3168
  this.structure = projectData.structure;
2154
- await this.loadFilesRecursive(path8.join(introDir, "files"), "");
3169
+ await this.loadFilesRecursive(path10.join(introDir, "files"), "");
2155
3170
  } catch {
2156
3171
  this.structure = null;
2157
3172
  this.files.clear();
@@ -2161,7 +3176,7 @@ class IntrospectionIndex {
2161
3176
  try {
2162
3177
  const entries = await fs5.readdir(basePath, { withFileTypes: true });
2163
3178
  for (const entry of entries) {
2164
- const entryPath = path8.join(basePath, entry.name);
3179
+ const entryPath = path10.join(basePath, entry.name);
2165
3180
  const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
2166
3181
  if (entry.isDirectory()) {
2167
3182
  await this.loadFilesRecursive(entryPath, relativePath);
@@ -2178,17 +3193,21 @@ class IntrospectionIndex {
2178
3193
  this.structure = null;
2179
3194
  }
2180
3195
  }
2181
- var init_introspection = __esm(() => {
3196
+ var init_IntrospectionIndex = __esm(() => {
2182
3197
  init_projectDetector();
2183
- init_fileIntrospector();
3198
+ init_introspection();
2184
3199
  init_config2();
2185
- init_fileIntrospector();
3200
+ });
3201
+
3202
+ // src/infrastructure/introspection/index.ts
3203
+ var init_introspection2 = __esm(() => {
3204
+ init_IntrospectionIndex();
2186
3205
  init_projectDetector();
2187
3206
  });
2188
3207
 
2189
3208
  // src/app/indexer/watcher.ts
2190
3209
  import { watch } from "chokidar";
2191
- import * as path9 from "path";
3210
+ import * as path11 from "path";
2192
3211
  async function watchDirectory(rootDir, options = {}) {
2193
3212
  const {
2194
3213
  debounceMs = DEFAULT_DEBOUNCE_MS,
@@ -2199,13 +3218,19 @@ async function watchDirectory(rootDir, options = {}) {
2199
3218
  onFileChange,
2200
3219
  onError
2201
3220
  } = options;
2202
- rootDir = path9.resolve(rootDir);
3221
+ rootDir = path11.resolve(rootDir);
2203
3222
  const config = await loadConfig(rootDir);
2204
- const watchPatterns = config.extensions.map((ext) => `**/*${ext}`);
3223
+ const indexLocation = getIndexLocation(rootDir);
3224
+ const validExtensions = new Set(config.extensions);
2205
3225
  const ignorePatterns = [
2206
3226
  ...config.ignorePaths.map((p) => `**/${p}/**`),
2207
- `**/${config.indexDir}/**`
3227
+ "**/node_modules/**",
3228
+ "**/.git/**"
2208
3229
  ];
3230
+ function shouldWatchFile(filepath) {
3231
+ const ext = path11.extname(filepath);
3232
+ return validExtensions.has(ext);
3233
+ }
2209
3234
  let isRunning = true;
2210
3235
  let isIndexing = false;
2211
3236
  let pendingChanges = new Map;
@@ -2285,7 +3310,10 @@ async function watchDirectory(rootDir, options = {}) {
2285
3310
  function handleFileEvent(event, filepath) {
2286
3311
  if (!isRunning)
2287
3312
  return;
2288
- const relativePath = path9.relative(rootDir, filepath);
3313
+ const relativePath = path11.relative(rootDir, filepath);
3314
+ if (!shouldWatchFile(filepath)) {
3315
+ return;
3316
+ }
2289
3317
  for (const ignorePath of config.ignorePaths) {
2290
3318
  if (relativePath.startsWith(ignorePath) || relativePath.includes(`/${ignorePath}/`)) {
2291
3319
  return;
@@ -2301,8 +3329,7 @@ async function watchDirectory(rootDir, options = {}) {
2301
3329
  pendingChanges.set(relativePath, event);
2302
3330
  scheduleProcessing();
2303
3331
  }
2304
- watcher = watch(watchPatterns, {
2305
- cwd: rootDir,
3332
+ watcher = watch(rootDir, {
2306
3333
  ignored: ignorePatterns,
2307
3334
  persistent: true,
2308
3335
  ignoreInitial: true,
@@ -2311,11 +3338,12 @@ async function watchDirectory(rootDir, options = {}) {
2311
3338
  pollInterval: 50
2312
3339
  },
2313
3340
  usePolling: false,
2314
- atomic: true
3341
+ atomic: true,
3342
+ depth: 99
2315
3343
  });
2316
- watcher.on("add", (filepath) => handleFileEvent("add", path9.join(rootDir, filepath)));
2317
- watcher.on("change", (filepath) => handleFileEvent("change", path9.join(rootDir, filepath)));
2318
- watcher.on("unlink", (filepath) => handleFileEvent("unlink", path9.join(rootDir, filepath)));
3344
+ watcher.on("add", (filepath) => handleFileEvent("add", filepath));
3345
+ watcher.on("change", (filepath) => handleFileEvent("change", filepath));
3346
+ watcher.on("unlink", (filepath) => handleFileEvent("unlink", filepath));
2319
3347
  watcher.on("error", (error) => {
2320
3348
  const err = error instanceof Error ? error : new Error(String(error));
2321
3349
  console.error("[Watch] Watcher error:", err);
@@ -2323,9 +3351,9 @@ async function watchDirectory(rootDir, options = {}) {
2323
3351
  onError(err);
2324
3352
  }
2325
3353
  });
2326
- await new Promise((resolve2) => {
3354
+ await new Promise((resolve3) => {
2327
3355
  watcher.on("ready", () => {
2328
- resolve2();
3356
+ resolve3();
2329
3357
  });
2330
3358
  });
2331
3359
  return {
@@ -2359,11 +3387,13 @@ __export(exports_indexer, {
2359
3387
  });
2360
3388
  import { glob } from "glob";
2361
3389
  import * as fs6 from "fs/promises";
2362
- import * as path10 from "path";
3390
+ import * as path12 from "path";
2363
3391
  async function indexDirectory(rootDir, options = {}) {
2364
3392
  const verbose = options.verbose ?? false;
2365
- rootDir = path10.resolve(rootDir);
3393
+ rootDir = path12.resolve(rootDir);
3394
+ const location = getIndexLocation(rootDir);
2366
3395
  console.log(`Indexing directory: ${rootDir}`);
3396
+ console.log(`Index location: ${location.indexDir}`);
2367
3397
  const config = await loadConfig(rootDir);
2368
3398
  const introspection = new IntrospectionIndex(rootDir);
2369
3399
  await introspection.initialize();
@@ -2405,11 +3435,11 @@ async function indexDirectory(rootDir, options = {}) {
2405
3435
  rootDir,
2406
3436
  config,
2407
3437
  readFile: async (filepath) => {
2408
- const fullPath = path10.isAbsolute(filepath) ? filepath : path10.join(rootDir, filepath);
3438
+ const fullPath = path12.isAbsolute(filepath) ? filepath : path12.join(rootDir, filepath);
2409
3439
  return fs6.readFile(fullPath, "utf-8");
2410
3440
  },
2411
3441
  getFileStats: async (filepath) => {
2412
- const fullPath = path10.isAbsolute(filepath) ? filepath : path10.join(rootDir, filepath);
3442
+ const fullPath = path12.isAbsolute(filepath) ? filepath : path12.join(rootDir, filepath);
2413
3443
  const stats = await fs6.stat(fullPath);
2414
3444
  return { lastModified: stats.mtime.toISOString() };
2415
3445
  }
@@ -2434,18 +3464,18 @@ async function indexWithModule(rootDir, files, module, config, verbose, introspe
2434
3464
  rootDir,
2435
3465
  config,
2436
3466
  readFile: async (filepath) => {
2437
- const fullPath = path10.isAbsolute(filepath) ? filepath : path10.join(rootDir, filepath);
3467
+ const fullPath = path12.isAbsolute(filepath) ? filepath : path12.join(rootDir, filepath);
2438
3468
  return fs6.readFile(fullPath, "utf-8");
2439
3469
  },
2440
3470
  getFileStats: async (filepath) => {
2441
- const fullPath = path10.isAbsolute(filepath) ? filepath : path10.join(rootDir, filepath);
3471
+ const fullPath = path12.isAbsolute(filepath) ? filepath : path12.join(rootDir, filepath);
2442
3472
  const stats = await fs6.stat(fullPath);
2443
3473
  return { lastModified: stats.mtime.toISOString() };
2444
3474
  },
2445
3475
  getIntrospection: (filepath) => introspection.getFile(filepath)
2446
3476
  };
2447
3477
  for (const filepath of files) {
2448
- const relativePath = path10.relative(rootDir, filepath);
3478
+ const relativePath = path12.relative(rootDir, filepath);
2449
3479
  try {
2450
3480
  const stats = await fs6.stat(filepath);
2451
3481
  const lastModified = stats.mtime.toISOString();
@@ -2515,13 +3545,13 @@ async function loadModuleManifest(rootDir, moduleId, config) {
2515
3545
  }
2516
3546
  async function writeModuleManifest(rootDir, moduleId, manifest, config) {
2517
3547
  const manifestPath = getModuleManifestPath(rootDir, moduleId, config);
2518
- await fs6.mkdir(path10.dirname(manifestPath), { recursive: true });
3548
+ await fs6.mkdir(path12.dirname(manifestPath), { recursive: true });
2519
3549
  await fs6.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
2520
3550
  }
2521
3551
  async function writeFileIndex(rootDir, moduleId, filepath, fileIndex, config) {
2522
3552
  const indexPath = getModuleIndexPath(rootDir, moduleId, config);
2523
- const indexFilePath = path10.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
2524
- await fs6.mkdir(path10.dirname(indexFilePath), { recursive: true });
3553
+ const indexFilePath = path12.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
3554
+ await fs6.mkdir(path12.dirname(indexFilePath), { recursive: true });
2525
3555
  await fs6.writeFile(indexFilePath, JSON.stringify(fileIndex, null, 2));
2526
3556
  }
2527
3557
  async function updateGlobalManifest(rootDir, modules, config) {
@@ -2531,12 +3561,12 @@ async function updateGlobalManifest(rootDir, modules, config) {
2531
3561
  lastUpdated: new Date().toISOString(),
2532
3562
  modules: modules.map((m) => m.id)
2533
3563
  };
2534
- await fs6.mkdir(path10.dirname(manifestPath), { recursive: true });
3564
+ await fs6.mkdir(path12.dirname(manifestPath), { recursive: true });
2535
3565
  await fs6.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
2536
3566
  }
2537
3567
  async function cleanupIndex(rootDir, options = {}) {
2538
3568
  const verbose = options.verbose ?? false;
2539
- rootDir = path10.resolve(rootDir);
3569
+ rootDir = path12.resolve(rootDir);
2540
3570
  console.log(`Cleaning up index in: ${rootDir}`);
2541
3571
  const config = await loadConfig(rootDir);
2542
3572
  await registerBuiltInModules();
@@ -2566,7 +3596,7 @@ async function cleanupModuleIndex(rootDir, moduleId, config, verbose) {
2566
3596
  const filesToRemove = [];
2567
3597
  const updatedFiles = {};
2568
3598
  for (const [filepath, entry] of Object.entries(manifest.files)) {
2569
- const fullPath = path10.join(rootDir, filepath);
3599
+ const fullPath = path12.join(rootDir, filepath);
2570
3600
  try {
2571
3601
  await fs6.access(fullPath);
2572
3602
  updatedFiles[filepath] = entry;
@@ -2580,7 +3610,7 @@ async function cleanupModuleIndex(rootDir, moduleId, config, verbose) {
2580
3610
  }
2581
3611
  }
2582
3612
  for (const filepath of filesToRemove) {
2583
- const indexFilePath = path10.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
3613
+ const indexFilePath = path12.join(indexPath, filepath.replace(/\.[^.]+$/, ".json"));
2584
3614
  try {
2585
3615
  await fs6.unlink(indexFilePath);
2586
3616
  } catch {}
@@ -2596,7 +3626,7 @@ async function cleanupEmptyDirectories(dir) {
2596
3626
  const entries = await fs6.readdir(dir, { withFileTypes: true });
2597
3627
  for (const entry of entries) {
2598
3628
  if (entry.isDirectory()) {
2599
- const subDir = path10.join(dir, entry.name);
3629
+ const subDir = path12.join(dir, entry.name);
2600
3630
  await cleanupEmptyDirectories(subDir);
2601
3631
  }
2602
3632
  }
@@ -2611,9 +3641,10 @@ async function cleanupEmptyDirectories(dir) {
2611
3641
  }
2612
3642
  }
2613
3643
  async function getIndexStatus(rootDir) {
2614
- rootDir = path10.resolve(rootDir);
3644
+ rootDir = path12.resolve(rootDir);
2615
3645
  const config = await loadConfig(rootDir);
2616
- const indexDir = path10.join(rootDir, config.indexDir);
3646
+ const location = getIndexLocation(rootDir);
3647
+ const indexDir = location.indexDir;
2617
3648
  const status = {
2618
3649
  exists: false,
2619
3650
  rootDir,
@@ -2646,7 +3677,7 @@ async function getIndexStatus(rootDir) {
2646
3677
  }
2647
3678
  } catch {
2648
3679
  try {
2649
- const entries = await fs6.readdir(path10.join(indexDir, "index"));
3680
+ const entries = await fs6.readdir(path12.join(indexDir, "index"));
2650
3681
  if (entries.length > 0) {
2651
3682
  status.exists = true;
2652
3683
  for (const entry of entries) {
@@ -2669,7 +3700,7 @@ async function getIndexStatus(rootDir) {
2669
3700
  var init_indexer = __esm(() => {
2670
3701
  init_config2();
2671
3702
  init_registry();
2672
- init_introspection();
3703
+ init_introspection2();
2673
3704
  init_watcher();
2674
3705
  });
2675
3706
 
@@ -2680,9 +3711,9 @@ __export(exports_search, {
2680
3711
  formatSearchResults: () => formatSearchResults
2681
3712
  });
2682
3713
  import * as fs7 from "fs/promises";
2683
- import * as path11 from "path";
3714
+ import * as path13 from "path";
2684
3715
  async function search(rootDir, query, options = {}) {
2685
- rootDir = path11.resolve(rootDir);
3716
+ rootDir = path13.resolve(rootDir);
2686
3717
  console.log(`Searching for: "${query}"`);
2687
3718
  const config = await loadConfig(rootDir);
2688
3719
  await registerBuiltInModules();
@@ -2723,7 +3754,7 @@ function createSearchContext(rootDir, moduleId, config) {
2723
3754
  config,
2724
3755
  loadFileIndex: async (filepath) => {
2725
3756
  const hasExtension = /\.[^./]+$/.test(filepath);
2726
- const indexFilePath = hasExtension ? path11.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path11.join(indexPath, filepath + ".json");
3757
+ const indexFilePath = hasExtension ? path13.join(indexPath, filepath.replace(/\.[^.]+$/, ".json")) : path13.join(indexPath, filepath + ".json");
2727
3758
  try {
2728
3759
  const content = await fs7.readFile(indexFilePath, "utf-8");
2729
3760
  return JSON.parse(content);
@@ -2735,7 +3766,7 @@ function createSearchContext(rootDir, moduleId, config) {
2735
3766
  const files = [];
2736
3767
  await traverseDirectory(indexPath, files, indexPath);
2737
3768
  return files.filter((f) => f.endsWith(".json") && !f.endsWith("manifest.json")).map((f) => {
2738
- const relative4 = path11.relative(indexPath, f);
3769
+ const relative4 = path13.relative(indexPath, f);
2739
3770
  return relative4.replace(/\.json$/, "");
2740
3771
  });
2741
3772
  }
@@ -2745,7 +3776,7 @@ async function traverseDirectory(dir, files, basePath) {
2745
3776
  try {
2746
3777
  const entries = await fs7.readdir(dir, { withFileTypes: true });
2747
3778
  for (const entry of entries) {
2748
- const fullPath = path11.join(dir, entry.name);
3779
+ const fullPath = path13.join(dir, entry.name);
2749
3780
  if (entry.isDirectory()) {
2750
3781
  await traverseDirectory(fullPath, files, basePath);
2751
3782
  } else if (entry.isFile()) {
@@ -2763,6 +3794,20 @@ async function loadGlobalManifest(rootDir, config) {
2763
3794
  return null;
2764
3795
  }
2765
3796
  }
3797
+ function formatModuleName(moduleId) {
3798
+ switch (moduleId) {
3799
+ case "core":
3800
+ return "Core";
3801
+ case "language/typescript":
3802
+ return "TypeScript";
3803
+ default:
3804
+ if (moduleId.startsWith("language/")) {
3805
+ const lang = moduleId.replace("language/", "");
3806
+ return lang.charAt(0).toUpperCase() + lang.slice(1);
3807
+ }
3808
+ return moduleId;
3809
+ }
3810
+ }
2766
3811
  function formatSearchResults(results) {
2767
3812
  if (results.length === 0) {
2768
3813
  return "No results found.";
@@ -2778,6 +3823,7 @@ function formatSearchResults(results) {
2778
3823
  output += `${i + 1}. ${location}${nameInfo}
2779
3824
  `;
2780
3825
  output += ` Score: ${(result.score * 100).toFixed(1)}% | Type: ${chunk.type}`;
3826
+ output += ` | via ${formatModuleName(result.moduleId)}`;
2781
3827
  if (chunk.isExported) {
2782
3828
  output += " | exported";
2783
3829
  }
@@ -2805,7 +3851,7 @@ init_embeddings();
2805
3851
  // package.json
2806
3852
  var package_default = {
2807
3853
  name: "raggrep",
2808
- version: "0.1.5",
3854
+ version: "0.1.7",
2809
3855
  description: "Local filesystem-based RAG system for codebases - semantic search using local embeddings",
2810
3856
  type: "module",
2811
3857
  main: "./dist/index.js",
@@ -3240,4 +4286,4 @@ Run 'raggrep <command> --help' for more information.
3240
4286
  }
3241
4287
  main();
3242
4288
 
3243
- //# debugId=70A5CEDDD33322C164756E2164756E21
4289
+ //# debugId=C248CB1C621D0FC764756E2164756E21