@saluzi/codegraph 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/dist/bin/codegraph.d.ts +25 -0
  2. package/dist/bin/node-version-check.d.ts +37 -0
  3. package/dist/bin/uninstall.d.ts +14 -0
  4. package/dist/context/formatter.d.ts +33 -0
  5. package/dist/context/formatter.js +244 -0
  6. package/dist/context/index.d.ts +117 -0
  7. package/dist/context/index.js +1050 -0
  8. package/dist/db/index.d.ts +101 -0
  9. package/dist/db/index.js +250 -0
  10. package/dist/db/migrations.d.ts +47 -0
  11. package/dist/db/migrations.js +131 -0
  12. package/dist/db/queries.d.ts +291 -0
  13. package/dist/db/queries.js +1349 -0
  14. package/dist/db/schema.sql +151 -0
  15. package/dist/db/sqlite-adapter.d.ts +49 -0
  16. package/dist/db/sqlite-adapter.js +141 -0
  17. package/dist/directory.d.ts +62 -0
  18. package/dist/directory.js +264 -0
  19. package/dist/errors.d.ts +149 -0
  20. package/dist/errors.js +219 -0
  21. package/dist/extraction/dfm-extractor.d.ts +31 -0
  22. package/dist/extraction/dfm-extractor.js +151 -0
  23. package/dist/extraction/grammars.d.ts +94 -0
  24. package/dist/extraction/grammars.js +357 -0
  25. package/dist/extraction/index.d.ts +148 -0
  26. package/dist/extraction/index.js +1286 -0
  27. package/dist/extraction/languages/c-cpp.d.ts +4 -0
  28. package/dist/extraction/languages/c-cpp.js +126 -0
  29. package/dist/extraction/languages/csharp.d.ts +3 -0
  30. package/dist/extraction/languages/csharp.js +72 -0
  31. package/dist/extraction/languages/dart.d.ts +3 -0
  32. package/dist/extraction/languages/dart.js +192 -0
  33. package/dist/extraction/languages/go.d.ts +3 -0
  34. package/dist/extraction/languages/go.js +58 -0
  35. package/dist/extraction/languages/index.d.ts +10 -0
  36. package/dist/extraction/languages/index.js +49 -0
  37. package/dist/extraction/languages/java.d.ts +3 -0
  38. package/dist/extraction/languages/java.js +64 -0
  39. package/dist/extraction/languages/javascript.d.ts +3 -0
  40. package/dist/extraction/languages/javascript.js +90 -0
  41. package/dist/extraction/languages/kotlin.d.ts +3 -0
  42. package/dist/extraction/languages/kotlin.js +253 -0
  43. package/dist/extraction/languages/lua.d.ts +3 -0
  44. package/dist/extraction/languages/lua.js +150 -0
  45. package/dist/extraction/languages/luau.d.ts +3 -0
  46. package/dist/extraction/languages/luau.js +37 -0
  47. package/dist/extraction/languages/pascal.d.ts +3 -0
  48. package/dist/extraction/languages/pascal.js +66 -0
  49. package/dist/extraction/languages/php.d.ts +3 -0
  50. package/dist/extraction/languages/php.js +107 -0
  51. package/dist/extraction/languages/python.d.ts +3 -0
  52. package/dist/extraction/languages/python.js +56 -0
  53. package/dist/extraction/languages/ruby.d.ts +3 -0
  54. package/dist/extraction/languages/ruby.js +114 -0
  55. package/dist/extraction/languages/rust.d.ts +3 -0
  56. package/dist/extraction/languages/rust.js +109 -0
  57. package/dist/extraction/languages/scala.d.ts +3 -0
  58. package/dist/extraction/languages/scala.js +139 -0
  59. package/dist/extraction/languages/swift.d.ts +3 -0
  60. package/dist/extraction/languages/swift.js +91 -0
  61. package/dist/extraction/languages/typescript.d.ts +3 -0
  62. package/dist/extraction/languages/typescript.js +129 -0
  63. package/dist/extraction/liquid-extractor.d.ts +52 -0
  64. package/dist/extraction/liquid-extractor.js +313 -0
  65. package/dist/extraction/parse-worker.d.ts +8 -0
  66. package/dist/extraction/parse-worker.js +94 -0
  67. package/dist/extraction/svelte-extractor.d.ts +56 -0
  68. package/dist/extraction/svelte-extractor.js +272 -0
  69. package/dist/extraction/tree-sitter-helpers.d.ts +39 -0
  70. package/dist/extraction/tree-sitter-helpers.js +103 -0
  71. package/dist/extraction/tree-sitter-types.d.ts +191 -0
  72. package/dist/extraction/tree-sitter-types.js +10 -0
  73. package/dist/extraction/tree-sitter.d.ts +238 -0
  74. package/dist/extraction/tree-sitter.js +2430 -0
  75. package/dist/extraction/vue-extractor.d.ts +36 -0
  76. package/dist/extraction/vue-extractor.js +163 -0
  77. package/dist/extraction/wasm/tree-sitter-c.wasm +0 -0
  78. package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  79. package/dist/extraction/wasm/tree-sitter-cpp.wasm +0 -0
  80. package/dist/extraction/wasm/tree-sitter-dart.wasm +0 -0
  81. package/dist/extraction/wasm/tree-sitter-go.wasm +0 -0
  82. package/dist/extraction/wasm/tree-sitter-java.wasm +0 -0
  83. package/dist/extraction/wasm/tree-sitter-javascript.wasm +0 -0
  84. package/dist/extraction/wasm/tree-sitter-kotlin.wasm +0 -0
  85. package/dist/extraction/wasm/tree-sitter-lua.wasm +0 -0
  86. package/dist/extraction/wasm/tree-sitter-luau.wasm +0 -0
  87. package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  88. package/dist/extraction/wasm/tree-sitter-php.wasm +0 -0
  89. package/dist/extraction/wasm/tree-sitter-python.wasm +0 -0
  90. package/dist/extraction/wasm/tree-sitter-ruby.wasm +0 -0
  91. package/dist/extraction/wasm/tree-sitter-rust.wasm +0 -0
  92. package/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
  93. package/dist/extraction/wasm/tree-sitter-swift.wasm +0 -0
  94. package/dist/extraction/wasm/tree-sitter-tsx.wasm +0 -0
  95. package/dist/extraction/wasm/tree-sitter-typescript.wasm +0 -0
  96. package/dist/extraction/wasm-runtime-flags.d.ts +46 -0
  97. package/dist/extraction/wasm-runtime-flags.js +105 -0
  98. package/dist/graph/index.d.ts +8 -0
  99. package/dist/graph/index.js +13 -0
  100. package/dist/graph/queries.d.ts +109 -0
  101. package/dist/graph/queries.js +366 -0
  102. package/dist/graph/traversal.d.ts +137 -0
  103. package/dist/graph/traversal.js +528 -0
  104. package/dist/index.d.ts +509 -0
  105. package/dist/index.js +800 -0
  106. package/dist/installer/claude-md-template.d.ts +19 -0
  107. package/dist/installer/config-writer.d.ts +29 -0
  108. package/dist/installer/index.d.ts +140 -0
  109. package/dist/installer/instructions-template.d.ts +30 -0
  110. package/dist/installer/targets/claude.d.ts +55 -0
  111. package/dist/installer/targets/codex.d.ts +18 -0
  112. package/dist/installer/targets/cursor.d.ts +35 -0
  113. package/dist/installer/targets/hermes.d.ts +18 -0
  114. package/dist/installer/targets/opencode.d.ts +30 -0
  115. package/dist/installer/targets/registry.d.ts +38 -0
  116. package/dist/installer/targets/shared.d.ts +92 -0
  117. package/dist/installer/targets/toml.d.ts +64 -0
  118. package/dist/installer/targets/types.d.ts +122 -0
  119. package/dist/mcp/index.d.ts +98 -0
  120. package/dist/mcp/server-instructions.d.ts +20 -0
  121. package/dist/mcp/tools.d.ts +269 -0
  122. package/dist/mcp/transport.d.ts +117 -0
  123. package/dist/resolution/frameworks/cargo-workspace.d.ts +20 -0
  124. package/dist/resolution/frameworks/cargo-workspace.js +225 -0
  125. package/dist/resolution/frameworks/csharp.d.ts +8 -0
  126. package/dist/resolution/frameworks/csharp.js +213 -0
  127. package/dist/resolution/frameworks/drupal.d.ts +51 -0
  128. package/dist/resolution/frameworks/drupal.js +335 -0
  129. package/dist/resolution/frameworks/express.d.ts +8 -0
  130. package/dist/resolution/frameworks/express.js +225 -0
  131. package/dist/resolution/frameworks/go.d.ts +8 -0
  132. package/dist/resolution/frameworks/go.js +158 -0
  133. package/dist/resolution/frameworks/index.d.ts +52 -0
  134. package/dist/resolution/frameworks/index.js +137 -0
  135. package/dist/resolution/frameworks/java.d.ts +8 -0
  136. package/dist/resolution/frameworks/java.js +177 -0
  137. package/dist/resolution/frameworks/laravel.d.ts +13 -0
  138. package/dist/resolution/frameworks/laravel.js +248 -0
  139. package/dist/resolution/frameworks/nestjs.d.ts +26 -0
  140. package/dist/resolution/frameworks/nestjs.js +374 -0
  141. package/dist/resolution/frameworks/python.d.ts +10 -0
  142. package/dist/resolution/frameworks/python.js +278 -0
  143. package/dist/resolution/frameworks/react.d.ts +8 -0
  144. package/dist/resolution/frameworks/react.js +272 -0
  145. package/dist/resolution/frameworks/ruby.d.ts +8 -0
  146. package/dist/resolution/frameworks/ruby.js +198 -0
  147. package/dist/resolution/frameworks/rust.d.ts +8 -0
  148. package/dist/resolution/frameworks/rust.js +207 -0
  149. package/dist/resolution/frameworks/svelte.d.ts +9 -0
  150. package/dist/resolution/frameworks/svelte.js +249 -0
  151. package/dist/resolution/frameworks/swift.d.ts +10 -0
  152. package/dist/resolution/frameworks/swift.js +376 -0
  153. package/dist/resolution/frameworks/vue.d.ts +9 -0
  154. package/dist/resolution/frameworks/vue.js +306 -0
  155. package/dist/resolution/import-resolver.d.ts +61 -0
  156. package/dist/resolution/import-resolver.js +663 -0
  157. package/dist/resolution/index.d.ts +118 -0
  158. package/dist/resolution/index.js +744 -0
  159. package/dist/resolution/lru-cache.d.ts +24 -0
  160. package/dist/resolution/lru-cache.js +62 -0
  161. package/dist/resolution/name-matcher.d.ts +50 -0
  162. package/dist/resolution/name-matcher.js +384 -0
  163. package/dist/resolution/path-aliases.d.ts +72 -0
  164. package/dist/resolution/path-aliases.js +238 -0
  165. package/dist/resolution/strip-comments.d.ts +40 -0
  166. package/dist/resolution/strip-comments.js +441 -0
  167. package/dist/resolution/types.d.ts +181 -0
  168. package/dist/resolution/types.js +8 -0
  169. package/dist/search/query-parser.d.ts +61 -0
  170. package/dist/search/query-parser.js +177 -0
  171. package/dist/search/query-utils.d.ts +59 -0
  172. package/dist/search/query-utils.js +383 -0
  173. package/dist/sync/git-hooks.d.ts +54 -0
  174. package/dist/sync/git-hooks.js +223 -0
  175. package/dist/sync/index.d.ts +25 -0
  176. package/dist/sync/index.js +28 -0
  177. package/dist/sync/watch-policy.d.ts +51 -0
  178. package/dist/sync/watch-policy.js +124 -0
  179. package/dist/sync/watcher.d.ts +83 -0
  180. package/dist/sync/watcher.js +192 -0
  181. package/dist/types.d.ts +433 -0
  182. package/dist/types.js +75 -0
  183. package/dist/ui/glyphs.d.ts +42 -0
  184. package/dist/ui/shimmer-progress.d.ts +11 -0
  185. package/dist/ui/shimmer-worker.d.ts +2 -0
  186. package/dist/ui/types.d.ts +20 -0
  187. package/dist/utils.d.ts +231 -0
  188. package/dist/utils.js +549 -0
  189. package/package.json +24 -0
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ /**
3
+ * Field-qualified search query parser.
4
+ *
5
+ * Splits a raw query like
6
+ *
7
+ * kind:function name:auth path:src/api authenticate
8
+ *
9
+ * into structured filters (kind=function, name="auth", path prefix
10
+ * "src/api") plus the free-text portion ("authenticate") that goes
11
+ * to FTS. Free-text and filters compose: filters narrow the result
12
+ * set, FTS scores within the narrowed set.
13
+ *
14
+ * Recognised fields (case-insensitive, value is the rest until
15
+ * whitespace):
16
+ *
17
+ * kind: one of function|method|class|interface|struct|...
18
+ * lang: one of typescript|python|go|... (alias: language:)
19
+ * path: case-insensitive substring of file_path
20
+ * name: case-insensitive substring of the symbol's name
21
+ *
22
+ * Unknown field prefixes (e.g. `foo:bar`) are passed through to FTS
23
+ * as plain text — that's how someone searching for `TODO:` gets a
24
+ * result instead of a parse error.
25
+ *
26
+ * Quoting:
27
+ * kind:function path:"src/some path/with spaces" → handled by stripping
28
+ * the surrounding double quotes from the value (single token only,
29
+ * no nested escapes).
30
+ */
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ exports.parseQuery = parseQuery;
33
+ exports.boundedEditDistance = boundedEditDistance;
34
+ const types_1 = require("../types");
35
+ // Derived from the canonical `NODE_KINDS` / `LANGUAGES` arrays in
36
+ // types.ts so adding a new kind or language doesn't silently fall
37
+ // through to plain text here.
38
+ const KIND_VALUES = new Set(types_1.NODE_KINDS);
39
+ const LANGUAGE_VALUES = new Set(types_1.LANGUAGES);
40
+ /**
41
+ * Strip a surrounding pair of double quotes from `s`. Allows users to
42
+ * keep whitespace in path filters: `path:"my dir/file"`.
43
+ */
44
+ function unquote(s) {
45
+ if (s.length >= 2 && s.startsWith('"') && s.endsWith('"'))
46
+ return s.slice(1, -1);
47
+ return s;
48
+ }
49
+ /**
50
+ * Parse a raw query into structured filters + remaining text.
51
+ * Always returns a value; never throws.
52
+ */
53
+ function parseQuery(raw) {
54
+ const out = {
55
+ text: '',
56
+ kinds: [],
57
+ languages: [],
58
+ pathFilters: [],
59
+ nameFilters: [],
60
+ };
61
+ // Tokenise on whitespace, preserving quoted spans as part of the
62
+ // current token. Quotes can appear at the start (`"…"`) OR mid-token
63
+ // (`path:"…"`); in both cases everything from the opening `"` to the
64
+ // matching `"` is included in the token, whitespace and all.
65
+ const tokens = [];
66
+ let i = 0;
67
+ while (i < raw.length) {
68
+ while (i < raw.length && /\s/.test(raw[i]))
69
+ i++;
70
+ if (i >= raw.length)
71
+ break;
72
+ const start = i;
73
+ while (i < raw.length && !/\s/.test(raw[i])) {
74
+ if (raw[i] === '"') {
75
+ const end = raw.indexOf('"', i + 1);
76
+ if (end === -1) {
77
+ // Unterminated quote — swallow the rest of the input as
78
+ // one token. Forgiving rather than throwing.
79
+ i = raw.length;
80
+ break;
81
+ }
82
+ i = end + 1;
83
+ continue;
84
+ }
85
+ i++;
86
+ }
87
+ tokens.push(raw.slice(start, i));
88
+ }
89
+ const textParts = [];
90
+ for (const tok of tokens) {
91
+ const colon = tok.indexOf(':');
92
+ if (colon <= 0 || colon === tok.length - 1) {
93
+ textParts.push(tok);
94
+ continue;
95
+ }
96
+ const key = tok.slice(0, colon).toLowerCase();
97
+ const valueRaw = unquote(tok.slice(colon + 1));
98
+ if (!valueRaw) {
99
+ textParts.push(tok);
100
+ continue;
101
+ }
102
+ switch (key) {
103
+ case 'kind': {
104
+ if (KIND_VALUES.has(valueRaw)) {
105
+ out.kinds.push(valueRaw);
106
+ }
107
+ else {
108
+ textParts.push(tok);
109
+ }
110
+ break;
111
+ }
112
+ case 'lang':
113
+ case 'language': {
114
+ const lower = valueRaw.toLowerCase();
115
+ if (LANGUAGE_VALUES.has(lower)) {
116
+ out.languages.push(lower);
117
+ }
118
+ else {
119
+ textParts.push(tok);
120
+ }
121
+ break;
122
+ }
123
+ case 'path':
124
+ out.pathFilters.push(valueRaw);
125
+ break;
126
+ case 'name':
127
+ out.nameFilters.push(valueRaw);
128
+ break;
129
+ default:
130
+ textParts.push(tok);
131
+ }
132
+ }
133
+ out.text = textParts.join(' ').trim();
134
+ return out;
135
+ }
136
+ /**
137
+ * Damerau-Levenshtein-ish bounded edit distance. Returns `maxDist + 1`
138
+ * as soon as the distance is known to exceed `maxDist`; that early-exit
139
+ * makes the fuzzy fallback cheap even over tens of thousands of names.
140
+ *
141
+ * Pure DP, O(min(len(a), len(b))) memory. Compares case-folded inputs;
142
+ * callers should pass `lowercase(name)` strings.
143
+ */
144
+ function boundedEditDistance(a, b, maxDist) {
145
+ if (a === b)
146
+ return 0;
147
+ const al = a.length;
148
+ const bl = b.length;
149
+ if (Math.abs(al - bl) > maxDist)
150
+ return maxDist + 1;
151
+ if (al === 0)
152
+ return bl;
153
+ if (bl === 0)
154
+ return al;
155
+ let prev = new Array(bl + 1);
156
+ let cur = new Array(bl + 1);
157
+ for (let j = 0; j <= bl; j++)
158
+ prev[j] = j;
159
+ for (let i = 1; i <= al; i++) {
160
+ cur[0] = i;
161
+ let rowMin = cur[0];
162
+ for (let j = 1; j <= bl; j++) {
163
+ const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;
164
+ const insertion = cur[j - 1] + 1;
165
+ const deletion = prev[j] + 1;
166
+ const substitution = prev[j - 1] + cost;
167
+ cur[j] = Math.min(insertion, deletion, substitution);
168
+ if (cur[j] < rowMin)
169
+ rowMin = cur[j];
170
+ }
171
+ if (rowMin > maxDist)
172
+ return maxDist + 1;
173
+ [prev, cur] = [cur, prev];
174
+ }
175
+ return prev[bl];
176
+ }
177
+ //# sourceMappingURL=query-parser.js.map
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Search Query Utilities
3
+ *
4
+ * Shared module for search term extraction and scoring.
5
+ */
6
+ import { Node } from '../types'
7
+ /**
8
+ * Common stop words to filter from search queries.
9
+ * Includes generic English + code-specific noise words.
10
+ */
11
+ export declare const STOP_WORDS: Set<string>
12
+ /**
13
+ * Generate stem variants of a search term by removing common English suffixes.
14
+ * Used for FTS query expansion so "caching" also finds "cache", "eviction" finds "evict", etc.
15
+ * Stems are used as PREFIX matches in FTS, so they don't need to be perfect English words.
16
+ */
17
+ export declare function getStemVariants(term: string): string[]
18
+ /**
19
+ * Extract meaningful search terms from a natural language query.
20
+ * Splits camelCase, PascalCase, snake_case, SCREAMING_SNAKE, and dot.notation
21
+ * into individual tokens before filtering.
22
+ *
23
+ * Preserves original compound identifiers (e.g., "scrapeLoop") alongside
24
+ * their split parts so that FTS can match both the full symbol name and
25
+ * individual words within it.
26
+ *
27
+ * Also generates stem variants (e.g., "caching"→"cache", "eviction"→"evict")
28
+ * so FTS prefix matching can find related code symbols.
29
+ */
30
+ export declare function extractSearchTerms(
31
+ query: string,
32
+ options?: {
33
+ stems?: boolean
34
+ },
35
+ ): string[]
36
+ /**
37
+ * Score path relevance to a query
38
+ * Higher score = more relevant path
39
+ */
40
+ export declare function scorePathRelevance(
41
+ filePath: string,
42
+ query: string,
43
+ ): number
44
+ /**
45
+ * Check if a file path looks like a test file
46
+ */
47
+ export declare function isTestFile(filePath: string): boolean
48
+ /**
49
+ * Bonus when a node's name matches the search query.
50
+ * Exact matches get the largest boost; prefix matches get smaller boosts.
51
+ * Multi-word queries also check individual term matches against the name.
52
+ */
53
+ export declare function nameMatchBonus(nodeName: string, query: string): number
54
+ /**
55
+ * Kind-based bonus for search ranking
56
+ * Functions and classes are typically more relevant than variables/imports
57
+ */
58
+ export declare function kindBonus(kind: Node['kind']): number
59
+ //# sourceMappingURL=query-utils.d.ts.map
@@ -0,0 +1,383 @@
1
+ "use strict";
2
+ /**
3
+ * Search Query Utilities
4
+ *
5
+ * Shared module for search term extraction and scoring.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.STOP_WORDS = void 0;
42
+ exports.getStemVariants = getStemVariants;
43
+ exports.extractSearchTerms = extractSearchTerms;
44
+ exports.scorePathRelevance = scorePathRelevance;
45
+ exports.isTestFile = isTestFile;
46
+ exports.nameMatchBonus = nameMatchBonus;
47
+ exports.kindBonus = kindBonus;
48
+ const path = __importStar(require("path"));
49
+ /**
50
+ * Common stop words to filter from search queries.
51
+ * Includes generic English + code-specific noise words.
52
+ */
53
+ exports.STOP_WORDS = new Set([
54
+ // English
55
+ 'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
56
+ 'of', 'with', 'by', 'from', 'is', 'it', 'that', 'this', 'are', 'was',
57
+ 'be', 'has', 'had', 'have', 'do', 'does', 'did', 'will', 'would', 'could',
58
+ 'should', 'may', 'might', 'can', 'shall', 'not', 'no', 'all', 'each',
59
+ 'every', 'how', 'what', 'where', 'when', 'who', 'which', 'why',
60
+ 'i', 'me', 'my', 'we', 'our', 'you', 'your', 'he', 'she', 'they',
61
+ 'show', 'give', 'tell',
62
+ 'been', 'done', 'made', 'used', 'using', 'work', 'works', 'found',
63
+ 'also', 'into', 'then', 'than', 'just', 'more', 'some', 'such',
64
+ 'over', 'only', 'out', 'its', 'so', 'up', 'as', 'if',
65
+ 'look', 'need', 'needs', 'want', 'happen', 'happens',
66
+ 'affect', 'affected', 'break', 'breaks', 'failing',
67
+ 'implemented', 'implement',
68
+ // Code-specific noise (avoid filtering common symbol names like get/set/add/build/find/list)
69
+ 'code', 'file', 'files', 'function', 'method', 'class', 'type',
70
+ 'fix', 'bug', 'called',
71
+ ]);
72
+ /**
73
+ * Generate stem variants of a search term by removing common English suffixes.
74
+ * Used for FTS query expansion so "caching" also finds "cache", "eviction" finds "evict", etc.
75
+ * Stems are used as PREFIX matches in FTS, so they don't need to be perfect English words.
76
+ */
77
+ function getStemVariants(term) {
78
+ const variants = new Set();
79
+ const t = term.toLowerCase();
80
+ // -ing: caching→cach/cache, handling→handl/handle, running→run
81
+ if (t.endsWith('ing') && t.length > 5) {
82
+ const base = t.slice(0, -3);
83
+ variants.add(base);
84
+ variants.add(base + 'e');
85
+ if (base.length >= 2 && base[base.length - 1] === base[base.length - 2]) {
86
+ variants.add(base.slice(0, -1));
87
+ }
88
+ }
89
+ // -tion/-sion: eviction→evict, expression→express
90
+ if ((t.endsWith('tion') || t.endsWith('sion')) && t.length > 5) {
91
+ variants.add(t.slice(0, -3));
92
+ }
93
+ // -ment: management→manage
94
+ if (t.endsWith('ment') && t.length > 6) {
95
+ variants.add(t.slice(0, -4));
96
+ }
97
+ // -ies: entries→entry
98
+ if (t.endsWith('ies') && t.length > 4) {
99
+ variants.add(t.slice(0, -3) + 'y');
100
+ }
101
+ // -es: processes→process, classes→class
102
+ else if (t.endsWith('es') && t.length > 4) {
103
+ variants.add(t.slice(0, -2));
104
+ }
105
+ // -s: errors→error (skip -ss endings like "class")
106
+ else if (t.endsWith('s') && !t.endsWith('ss') && t.length > 4) {
107
+ variants.add(t.slice(0, -1));
108
+ }
109
+ // -ed: handled→handle, propagated→propagate, carried→carry
110
+ if (t.endsWith('ed') && !t.endsWith('eed') && t.length > 4) {
111
+ variants.add(t.slice(0, -1));
112
+ variants.add(t.slice(0, -2));
113
+ if (t.endsWith('ied') && t.length > 5) {
114
+ variants.add(t.slice(0, -3) + 'y');
115
+ }
116
+ }
117
+ // -er: builder→build/builde, handler→handl/handle, getter→get
118
+ if (t.endsWith('er') && t.length > 4) {
119
+ const base = t.slice(0, -2);
120
+ variants.add(base);
121
+ variants.add(base + 'e');
122
+ if (base.length >= 2 && base[base.length - 1] === base[base.length - 2]) {
123
+ variants.add(base.slice(0, -1));
124
+ }
125
+ }
126
+ return [...variants].filter(v => v.length >= 3 && v !== t);
127
+ }
128
+ /**
129
+ * Extract meaningful search terms from a natural language query.
130
+ * Splits camelCase, PascalCase, snake_case, SCREAMING_SNAKE, and dot.notation
131
+ * into individual tokens before filtering.
132
+ *
133
+ * Preserves original compound identifiers (e.g., "scrapeLoop") alongside
134
+ * their split parts so that FTS can match both the full symbol name and
135
+ * individual words within it.
136
+ *
137
+ * Also generates stem variants (e.g., "caching"→"cache", "eviction"→"evict")
138
+ * so FTS prefix matching can find related code symbols.
139
+ */
140
+ function extractSearchTerms(query, options) {
141
+ const includeStems = options?.stems !== false;
142
+ const tokens = new Set();
143
+ // CJK detection: extract Chinese/Japanese/Korean character sequences
144
+ // before the ASCII split destroys them. FTS5 unicode61 treats CJK
145
+ // as monolithic tokens, so CJK search terms fall through to the LIKE
146
+ // path. Bigrams + trigrams give useful substring matching for Chinese
147
+ // text in docstrings and comments.
148
+ const cjkPattern = /[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]+/g;
149
+ let cjkMatch;
150
+ const cjkTerms = [];
151
+ while ((cjkMatch = cjkPattern.exec(query)) !== null) {
152
+ const text = cjkMatch[0];
153
+ // Keep the full CJK sequence as a token (e.g. "认证中间件")
154
+ if (text.length >= 2) {
155
+ cjkTerms.push(text);
156
+ }
157
+ // Generate bigrams for substring matching (e.g. "认证", "证中", "中间", "间件")
158
+ for (let i = 0; i < text.length - 1; i++) {
159
+ cjkTerms.push(text.slice(i, i + 2));
160
+ }
161
+ // Generate trigrams for more precise matching (e.g. "认证中", "证中间", "中间件")
162
+ for (let i = 0; i < text.length - 2; i++) {
163
+ cjkTerms.push(text.slice(i, i + 3));
164
+ }
165
+ }
166
+ // Cap CJK terms at 8 to prevent query explosion in buildContext
167
+ // (which calls searchNodes for each term individually)
168
+ const uniqueCjk = [...new Set(cjkTerms)];
169
+ // Prefer longer terms (more specific) over shorter ones
170
+ uniqueCjk.sort((a, b) => b.length - a.length);
171
+ for (const term of uniqueCjk.slice(0, 8)) {
172
+ tokens.add(term);
173
+ }
174
+ // Remove CJK from query before ASCII processing to avoid interference
175
+ const asciiQuery = query.replace(cjkPattern, ' ');
176
+ // First, extract and preserve compound identifiers before splitting
177
+ // CamelCase: scrapeLoop, UserService, getCallGraph
178
+ const compoundPattern = /\b([a-zA-Z][a-zA-Z0-9]*(?:[A-Z][a-z]+)+|[A-Z][a-z]+(?:[A-Z][a-z]*)+)\b/g;
179
+ let match;
180
+ while ((match = compoundPattern.exec(asciiQuery)) !== null) {
181
+ if (match[1] && match[1].length >= 3) {
182
+ tokens.add(match[1].toLowerCase()); // preserve full compound: "scrapeloop"
183
+ }
184
+ }
185
+ // snake_case: scrape_loop, user_service
186
+ const snakePattern = /\b([a-zA-Z][a-zA-Z0-9]*(?:_[a-zA-Z0-9]+)+)\b/g;
187
+ while ((match = snakePattern.exec(asciiQuery)) !== null) {
188
+ if (match[1] && match[1].length >= 3) {
189
+ tokens.add(match[1].toLowerCase());
190
+ }
191
+ }
192
+ // Split camelCase / PascalCase: "getUserName" → "get User Name"
193
+ const camelSplit = asciiQuery
194
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
195
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2');
196
+ // Replace underscores and dots with spaces (snake_case, dot.notation)
197
+ const normalised = camelSplit.replace(/[_.]+/g, ' ');
198
+ // Split on any non-alphanumeric character (CJK already extracted above)
199
+ const words = normalised.split(/[^a-zA-Z0-9]+/).filter(Boolean);
200
+ for (const word of words) {
201
+ const lower = word.toLowerCase();
202
+ if (lower.length < 3)
203
+ continue;
204
+ if (exports.STOP_WORDS.has(lower))
205
+ continue;
206
+ tokens.add(lower);
207
+ }
208
+ // Generate stem variants for broader FTS matching.
209
+ // "caching" → "cache" finds CacheBuilder; "eviction" → "evict" finds evictEntries.
210
+ // Also enables co-occurrence dampening by increasing term count above 1.
211
+ // Stems are skipped when scoring path relevance (stems inflate path scores).
212
+ if (includeStems) {
213
+ const stems = new Set();
214
+ for (const token of tokens) {
215
+ for (const variant of getStemVariants(token)) {
216
+ if (!tokens.has(variant) && !exports.STOP_WORDS.has(variant)) {
217
+ stems.add(variant);
218
+ }
219
+ }
220
+ }
221
+ for (const stem of stems) {
222
+ tokens.add(stem);
223
+ }
224
+ }
225
+ return [...tokens];
226
+ }
227
+ /**
228
+ * Score path relevance to a query
229
+ * Higher score = more relevant path
230
+ */
231
+ function scorePathRelevance(filePath, query) {
232
+ // Use base terms only — stem variants inflate path scores by generating
233
+ // many near-duplicate terms that all match the same path segments.
234
+ const terms = extractSearchTerms(query, { stems: false });
235
+ if (terms.length === 0)
236
+ return 0;
237
+ const pathLower = filePath.toLowerCase();
238
+ const fileName = path.basename(filePath).toLowerCase();
239
+ const dirName = path.dirname(filePath).toLowerCase();
240
+ let score = 0;
241
+ for (const term of terms) {
242
+ // Exact filename match (strongest)
243
+ if (fileName.includes(term))
244
+ score += 10;
245
+ // Directory match
246
+ if (dirName.includes(term))
247
+ score += 5;
248
+ // General path match
249
+ else if (pathLower.includes(term))
250
+ score += 3;
251
+ }
252
+ // Deprioritize test files unless the query is explicitly about tests
253
+ const queryLower = query.toLowerCase();
254
+ const isTestQuery = queryLower.includes('test') || queryLower.includes('spec');
255
+ if (!isTestQuery && isTestFile(filePath)) {
256
+ score -= 15;
257
+ }
258
+ return score;
259
+ }
260
+ /**
261
+ * Check if a file path looks like a test file
262
+ */
263
+ function isTestFile(filePath) {
264
+ const lower = filePath.toLowerCase();
265
+ const fileName = path.basename(filePath); // original case — needed for camelCase boundaries
266
+ const lowerName = fileName.toLowerCase();
267
+ // --- Filename patterns ---
268
+ if (lowerName.startsWith('test_') || // python: test_foo.py
269
+ lowerName.startsWith('test.') ||
270
+ // separator-delimited: foo_test.go, foo.test.ts, foo-spec.rb, bar_spec.py
271
+ /[._-](test|tests|spec|specs)\.[a-z0-9]+$/.test(lowerName) ||
272
+ // CamelCase suffix (Java/Kotlin/Swift/C#/Scala): FooTest.kt, BarTests.swift,
273
+ // BazSpec.scala, QuxTestCase.java. Capital-led so "latest.kt"/"manifest.kt"
274
+ // (lowercase "test") are NOT matched.
275
+ /(?:Test|Tests|TestCase|Tester|Spec|Specs)\.[A-Za-z0-9]+$/.test(fileName)) {
276
+ return true;
277
+ }
278
+ // --- Directory patterns ---
279
+ if (lower.includes('/tests/') || lower.includes('/test/') ||
280
+ lower.includes('/__tests__/') || lower.includes('/spec/') ||
281
+ lower.includes('/specs/') || lower.includes('/testlib/') ||
282
+ lower.includes('/testing/') ||
283
+ lower.startsWith('test/') || lower.startsWith('tests/') ||
284
+ lower.startsWith('spec/') || lower.startsWith('specs/') ||
285
+ // CamelCase test source-set dirs (Kotlin Multiplatform / Gradle / Xcode):
286
+ // jvmTest/, commonTest/, androidTest/, iosTest/, integrationTest/. Capital-led
287
+ // so "latest/" / "manifest/" are not matched.
288
+ /(?:^|\/)[A-Za-z0-9]*(?:Test|Tests|Spec)\//.test(filePath)) {
289
+ return true;
290
+ }
291
+ // Non-production directories: examples, samples, benchmarks, fixtures, demos.
292
+ // Check both mid-path (/integration/) and start-of-path (integration/) since
293
+ // file paths may be stored as relative paths without a leading slash.
294
+ return matchesNonProductionDir(lower);
295
+ }
296
+ /**
297
+ * Check if a path is in a non-production directory (integration, sample, example, etc.)
298
+ * Handles both absolute paths (/foo/integration/bar) and relative paths (integration/bar).
299
+ */
300
+ function matchesNonProductionDir(lowerPath) {
301
+ const dirs = [
302
+ 'integration', 'sample', 'samples', 'example', 'examples',
303
+ 'fixture', 'fixtures', 'benchmark', 'benchmarks', 'demo', 'demos',
304
+ ];
305
+ for (const dir of dirs) {
306
+ if (lowerPath.includes('/' + dir + '/') || lowerPath.startsWith(dir + '/')) {
307
+ return true;
308
+ }
309
+ }
310
+ return false;
311
+ }
312
+ /**
313
+ * Bonus when a node's name matches the search query.
314
+ * Exact matches get the largest boost; prefix matches get smaller boosts.
315
+ * Multi-word queries also check individual term matches against the name.
316
+ */
317
+ function nameMatchBonus(nodeName, query) {
318
+ const nameLower = nodeName.toLowerCase();
319
+ // Split query into word-level terms (handles "CacheBuilder build" → ["cache","builder","build"])
320
+ const rawTerms = query
321
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
322
+ .split(/[\s_.\-]+/)
323
+ .map(t => t.toLowerCase())
324
+ .filter(t => t.length >= 2);
325
+ // Also keep original space-separated tokens for exact-term matching
326
+ const queryTokens = query.split(/\s+/).map(t => t.toLowerCase()).filter(t => t.length >= 2);
327
+ // Full query as a single token (for compound identifiers like "CacheBuilder")
328
+ const queryLower = query.replace(/[\s]+/g, '').toLowerCase();
329
+ // Exact match: query exactly equals the node name
330
+ if (nameLower === queryLower)
331
+ return 80;
332
+ // Exact match on a query token: "CacheBuilder build" and node name is "build"
333
+ if (queryTokens.length > 1 && queryTokens.includes(nameLower))
334
+ return 60;
335
+ // Name starts with query — scale by length ratio so "Pod"→"Pod" (exact, handled above)
336
+ // scores much higher than "Pod"→"PodGCControllerOptions" (ratio 0.125).
337
+ if (nameLower.startsWith(queryLower)) {
338
+ const ratio = queryLower.length / nameLower.length;
339
+ return Math.round(10 + 30 * ratio);
340
+ }
341
+ // All camelCase-split terms appear in the name
342
+ if (rawTerms.length > 1) {
343
+ const allMatch = rawTerms.every(t => nameLower.includes(t));
344
+ if (allMatch)
345
+ return 15;
346
+ }
347
+ // Name contains the full query as substring
348
+ if (nameLower.includes(queryLower))
349
+ return 10;
350
+ return 0;
351
+ }
352
+ /**
353
+ * Kind-based bonus for search ranking
354
+ * Functions and classes are typically more relevant than variables/imports
355
+ */
356
+ function kindBonus(kind) {
357
+ const bonuses = {
358
+ function: 10,
359
+ method: 10,
360
+ class: 8,
361
+ interface: 9,
362
+ type_alias: 6,
363
+ struct: 6,
364
+ trait: 9,
365
+ enum: 5,
366
+ component: 8,
367
+ route: 9,
368
+ module: 4,
369
+ property: 3,
370
+ field: 3,
371
+ variable: 2,
372
+ constant: 3,
373
+ import: 1,
374
+ export: 1,
375
+ parameter: 0,
376
+ namespace: 4,
377
+ file: 0,
378
+ protocol: 9,
379
+ enum_member: 3,
380
+ };
381
+ return bonuses[kind] ?? 0;
382
+ }
383
+ //# sourceMappingURL=query-utils.js.map
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Git Sync Hooks
3
+ *
4
+ * When the live file watcher is disabled (e.g. on WSL2 `/mnt/*` drives,
5
+ * see watch-policy.ts), the CodeGraph index would otherwise go stale until
6
+ * the user runs `codegraph sync` by hand. As an opt-in alternative, we can
7
+ * install git hooks that refresh the index after the operations that change
8
+ * files on disk: commit, merge (covers `git pull`), and checkout.
9
+ *
10
+ * The hooks run `codegraph sync` in the background so they never block git,
11
+ * and are guarded by `command -v codegraph` so they no-op cleanly when the
12
+ * CLI isn't on PATH. Our snippet is delimited by marker comments so install
13
+ * is idempotent and removal preserves any user-authored hook content.
14
+ */
15
+ export type GitHookName = 'post-commit' | 'post-merge' | 'post-checkout'
16
+ /** Hooks installed by default: commit, merge (git pull), and checkout. */
17
+ export declare const DEFAULT_SYNC_HOOKS: GitHookName[]
18
+ export interface GitHookResult {
19
+ /** Hook names that were created or updated. */
20
+ installed: GitHookName[]
21
+ /** Resolved hooks directory, or null when not a git repo. */
22
+ hooksDir: string | null
23
+ /** Reason nothing happened (e.g. not a git repository). */
24
+ skipped?: string
25
+ }
26
+ /**
27
+ * Whether `projectRoot` is inside a git working tree. Returns false if git
28
+ * isn't installed or the path isn't a repo.
29
+ */
30
+ export declare function isGitRepo(projectRoot: string): boolean
31
+ /**
32
+ * Install (or update) the CodeGraph sync hooks in a git repository.
33
+ * Idempotent: re-running replaces our marker block rather than duplicating
34
+ * it, and any user-authored hook content is preserved.
35
+ */
36
+ export declare function installGitSyncHook(
37
+ projectRoot: string,
38
+ hooks?: GitHookName[],
39
+ ): GitHookResult
40
+ /**
41
+ * Remove the CodeGraph sync hooks. Strips only our marker block; deletes the
42
+ * hook file entirely when nothing but a shebang remains, otherwise rewrites
43
+ * the user's content untouched.
44
+ */
45
+ export declare function removeGitSyncHook(
46
+ projectRoot: string,
47
+ hooks?: GitHookName[],
48
+ ): GitHookResult
49
+ /** Whether any CodeGraph sync hook is currently installed. */
50
+ export declare function isSyncHookInstalled(
51
+ projectRoot: string,
52
+ hooks?: GitHookName[],
53
+ ): boolean
54
+ //# sourceMappingURL=git-hooks.d.ts.map