@optave/codegraph 3.5.0 → 3.6.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 (310) hide show
  1. package/README.md +35 -14
  2. package/dist/ast-analysis/engine.d.ts.map +1 -1
  3. package/dist/ast-analysis/engine.js +119 -127
  4. package/dist/ast-analysis/engine.js.map +1 -1
  5. package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
  6. package/dist/ast-analysis/visitors/ast-store-visitor.js +14 -1
  7. package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
  8. package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
  9. package/dist/ast-analysis/visitors/complexity-visitor.js +11 -13
  10. package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
  11. package/dist/db/connection.d.ts +12 -2
  12. package/dist/db/connection.d.ts.map +1 -1
  13. package/dist/db/connection.js +81 -53
  14. package/dist/db/connection.js.map +1 -1
  15. package/dist/db/index.d.ts +1 -1
  16. package/dist/db/index.d.ts.map +1 -1
  17. package/dist/db/index.js +1 -1
  18. package/dist/db/index.js.map +1 -1
  19. package/dist/db/migrations.d.ts.map +1 -1
  20. package/dist/db/migrations.js +38 -32
  21. package/dist/db/migrations.js.map +1 -1
  22. package/dist/domain/analysis/context.d.ts.map +1 -1
  23. package/dist/domain/analysis/context.js +51 -66
  24. package/dist/domain/analysis/context.js.map +1 -1
  25. package/dist/domain/analysis/dependencies.d.ts.map +1 -1
  26. package/dist/domain/analysis/dependencies.js +62 -70
  27. package/dist/domain/analysis/dependencies.js.map +1 -1
  28. package/dist/domain/analysis/diff-impact.d.ts +9 -7
  29. package/dist/domain/analysis/diff-impact.d.ts.map +1 -1
  30. package/dist/domain/analysis/exports.d.ts.map +1 -1
  31. package/dist/domain/analysis/exports.js +29 -33
  32. package/dist/domain/analysis/exports.js.map +1 -1
  33. package/dist/domain/analysis/fn-impact.d.ts +15 -17
  34. package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
  35. package/dist/domain/analysis/fn-impact.js +35 -65
  36. package/dist/domain/analysis/fn-impact.js.map +1 -1
  37. package/dist/domain/analysis/module-map.d.ts.map +1 -1
  38. package/dist/domain/analysis/module-map.js +91 -6
  39. package/dist/domain/analysis/module-map.js.map +1 -1
  40. package/dist/domain/analysis/query-helpers.d.ts +20 -0
  41. package/dist/domain/analysis/query-helpers.d.ts.map +1 -0
  42. package/dist/domain/analysis/query-helpers.js +27 -0
  43. package/dist/domain/analysis/query-helpers.js.map +1 -0
  44. package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
  45. package/dist/domain/graph/builder/helpers.js +15 -9
  46. package/dist/domain/graph/builder/helpers.js.map +1 -1
  47. package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
  48. package/dist/domain/graph/builder/incremental.js +3 -2
  49. package/dist/domain/graph/builder/incremental.js.map +1 -1
  50. package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
  51. package/dist/domain/graph/builder/pipeline.js +69 -3
  52. package/dist/domain/graph/builder/pipeline.js.map +1 -1
  53. package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
  54. package/dist/domain/graph/builder/stages/build-edges.js +7 -51
  55. package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
  56. package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
  57. package/dist/domain/graph/builder/stages/build-structure.js +7 -5
  58. package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
  59. package/dist/domain/graph/builder/stages/collect-files.js +2 -2
  60. package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
  61. package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
  62. package/dist/domain/graph/builder/stages/detect-changes.js +2 -2
  63. package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
  64. package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
  65. package/dist/domain/graph/builder/stages/finalize.js +124 -105
  66. package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
  67. package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
  68. package/dist/domain/graph/builder/stages/insert-nodes.js +28 -15
  69. package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
  70. package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
  71. package/dist/domain/graph/builder/stages/resolve-imports.js +3 -2
  72. package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
  73. package/dist/domain/graph/resolve.d.ts +0 -4
  74. package/dist/domain/graph/resolve.d.ts.map +1 -1
  75. package/dist/domain/graph/resolve.js +32 -48
  76. package/dist/domain/graph/resolve.js.map +1 -1
  77. package/dist/domain/graph/watcher.d.ts.map +1 -1
  78. package/dist/domain/graph/watcher.js +12 -12
  79. package/dist/domain/graph/watcher.js.map +1 -1
  80. package/dist/domain/parser.d.ts +1 -1
  81. package/dist/domain/parser.d.ts.map +1 -1
  82. package/dist/domain/parser.js +164 -101
  83. package/dist/domain/parser.js.map +1 -1
  84. package/dist/domain/search/search/cli-formatter.d.ts.map +1 -1
  85. package/dist/domain/search/search/cli-formatter.js +88 -83
  86. package/dist/domain/search/search/cli-formatter.js.map +1 -1
  87. package/dist/extractors/bash.d.ts +6 -0
  88. package/dist/extractors/bash.d.ts.map +1 -0
  89. package/dist/extractors/bash.js +91 -0
  90. package/dist/extractors/bash.js.map +1 -0
  91. package/dist/extractors/c.d.ts +6 -0
  92. package/dist/extractors/c.d.ts.map +1 -0
  93. package/dist/extractors/c.js +204 -0
  94. package/dist/extractors/c.js.map +1 -0
  95. package/dist/extractors/cpp.d.ts +6 -0
  96. package/dist/extractors/cpp.d.ts.map +1 -0
  97. package/dist/extractors/cpp.js +283 -0
  98. package/dist/extractors/cpp.js.map +1 -0
  99. package/dist/extractors/csharp.d.ts.map +1 -1
  100. package/dist/extractors/csharp.js +42 -54
  101. package/dist/extractors/csharp.js.map +1 -1
  102. package/dist/extractors/go.d.ts.map +1 -1
  103. package/dist/extractors/go.js +126 -130
  104. package/dist/extractors/go.js.map +1 -1
  105. package/dist/extractors/hcl.js +6 -6
  106. package/dist/extractors/hcl.js.map +1 -1
  107. package/dist/extractors/helpers.d.ts +32 -1
  108. package/dist/extractors/helpers.d.ts.map +1 -1
  109. package/dist/extractors/helpers.js +74 -0
  110. package/dist/extractors/helpers.js.map +1 -1
  111. package/dist/extractors/index.d.ts +6 -0
  112. package/dist/extractors/index.d.ts.map +1 -1
  113. package/dist/extractors/index.js +6 -0
  114. package/dist/extractors/index.js.map +1 -1
  115. package/dist/extractors/java.d.ts.map +1 -1
  116. package/dist/extractors/java.js +32 -47
  117. package/dist/extractors/java.js.map +1 -1
  118. package/dist/extractors/javascript.d.ts.map +1 -1
  119. package/dist/extractors/javascript.js +306 -292
  120. package/dist/extractors/javascript.js.map +1 -1
  121. package/dist/extractors/kotlin.d.ts +6 -0
  122. package/dist/extractors/kotlin.d.ts.map +1 -0
  123. package/dist/extractors/kotlin.js +275 -0
  124. package/dist/extractors/kotlin.js.map +1 -0
  125. package/dist/extractors/php.d.ts.map +1 -1
  126. package/dist/extractors/php.js +39 -44
  127. package/dist/extractors/php.js.map +1 -1
  128. package/dist/extractors/python.d.ts.map +1 -1
  129. package/dist/extractors/python.js +75 -93
  130. package/dist/extractors/python.js.map +1 -1
  131. package/dist/extractors/ruby.js +6 -13
  132. package/dist/extractors/ruby.js.map +1 -1
  133. package/dist/extractors/rust.d.ts.map +1 -1
  134. package/dist/extractors/rust.js +58 -83
  135. package/dist/extractors/rust.js.map +1 -1
  136. package/dist/extractors/scala.d.ts +6 -0
  137. package/dist/extractors/scala.d.ts.map +1 -0
  138. package/dist/extractors/scala.js +269 -0
  139. package/dist/extractors/scala.js.map +1 -0
  140. package/dist/extractors/swift.d.ts +6 -0
  141. package/dist/extractors/swift.d.ts.map +1 -0
  142. package/dist/extractors/swift.js +275 -0
  143. package/dist/extractors/swift.js.map +1 -0
  144. package/dist/features/ast.d.ts +2 -0
  145. package/dist/features/ast.d.ts.map +1 -1
  146. package/dist/features/ast.js +9 -24
  147. package/dist/features/ast.js.map +1 -1
  148. package/dist/features/audit.d.ts.map +1 -1
  149. package/dist/features/audit.js +17 -21
  150. package/dist/features/audit.js.map +1 -1
  151. package/dist/features/branch-compare.d.ts.map +1 -1
  152. package/dist/features/branch-compare.js +47 -3
  153. package/dist/features/branch-compare.js.map +1 -1
  154. package/dist/features/cfg.d.ts +7 -1
  155. package/dist/features/cfg.d.ts.map +1 -1
  156. package/dist/features/cfg.js +118 -62
  157. package/dist/features/cfg.js.map +1 -1
  158. package/dist/features/check.d.ts.map +1 -1
  159. package/dist/features/check.js +79 -62
  160. package/dist/features/check.js.map +1 -1
  161. package/dist/features/complexity-query.d.ts.map +1 -1
  162. package/dist/features/complexity-query.js +142 -137
  163. package/dist/features/complexity-query.js.map +1 -1
  164. package/dist/features/complexity.d.ts +7 -1
  165. package/dist/features/complexity.d.ts.map +1 -1
  166. package/dist/features/complexity.js +62 -1
  167. package/dist/features/complexity.js.map +1 -1
  168. package/dist/features/dataflow.d.ts +7 -1
  169. package/dist/features/dataflow.d.ts.map +1 -1
  170. package/dist/features/dataflow.js +356 -188
  171. package/dist/features/dataflow.js.map +1 -1
  172. package/dist/features/graph-enrichment.d.ts.map +1 -1
  173. package/dist/features/graph-enrichment.js +117 -104
  174. package/dist/features/graph-enrichment.js.map +1 -1
  175. package/dist/features/sequence.d.ts.map +1 -1
  176. package/dist/features/sequence.js +25 -4
  177. package/dist/features/sequence.js.map +1 -1
  178. package/dist/features/structure-query.d.ts.map +1 -1
  179. package/dist/features/structure-query.js +29 -4
  180. package/dist/features/structure-query.js.map +1 -1
  181. package/dist/features/structure.d.ts.map +1 -1
  182. package/dist/features/structure.js +35 -15
  183. package/dist/features/structure.js.map +1 -1
  184. package/dist/graph/algorithms/leiden/adapter.d.ts.map +1 -1
  185. package/dist/graph/algorithms/leiden/adapter.js +88 -73
  186. package/dist/graph/algorithms/leiden/adapter.js.map +1 -1
  187. package/dist/graph/algorithms/leiden/index.js +43 -28
  188. package/dist/graph/algorithms/leiden/index.js.map +1 -1
  189. package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
  190. package/dist/graph/algorithms/leiden/optimiser.js +90 -104
  191. package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
  192. package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
  193. package/dist/graph/algorithms/leiden/partition.js +89 -106
  194. package/dist/graph/algorithms/leiden/partition.js.map +1 -1
  195. package/dist/graph/model.d.ts +2 -0
  196. package/dist/graph/model.d.ts.map +1 -1
  197. package/dist/graph/model.js +20 -8
  198. package/dist/graph/model.js.map +1 -1
  199. package/dist/infrastructure/config.d.ts +0 -8
  200. package/dist/infrastructure/config.d.ts.map +1 -1
  201. package/dist/infrastructure/config.js +73 -62
  202. package/dist/infrastructure/config.js.map +1 -1
  203. package/dist/infrastructure/registry.d.ts +0 -8
  204. package/dist/infrastructure/registry.d.ts.map +1 -1
  205. package/dist/infrastructure/registry.js +12 -14
  206. package/dist/infrastructure/registry.js.map +1 -1
  207. package/dist/mcp/server.d.ts.map +1 -1
  208. package/dist/mcp/server.js +45 -36
  209. package/dist/mcp/server.js.map +1 -1
  210. package/dist/presentation/audit.d.ts.map +1 -1
  211. package/dist/presentation/audit.js +61 -57
  212. package/dist/presentation/audit.js.map +1 -1
  213. package/dist/presentation/branch-compare.d.ts.map +1 -1
  214. package/dist/presentation/branch-compare.js +56 -38
  215. package/dist/presentation/branch-compare.js.map +1 -1
  216. package/dist/presentation/check.d.ts.map +1 -1
  217. package/dist/presentation/check.js +30 -32
  218. package/dist/presentation/check.js.map +1 -1
  219. package/dist/presentation/colors.d.ts.map +1 -1
  220. package/dist/presentation/colors.js +2 -0
  221. package/dist/presentation/colors.js.map +1 -1
  222. package/dist/presentation/complexity.d.ts.map +1 -1
  223. package/dist/presentation/complexity.js +25 -19
  224. package/dist/presentation/complexity.js.map +1 -1
  225. package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
  226. package/dist/presentation/queries-cli/exports.js +15 -15
  227. package/dist/presentation/queries-cli/exports.js.map +1 -1
  228. package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
  229. package/dist/presentation/queries-cli/impact.js +29 -19
  230. package/dist/presentation/queries-cli/impact.js.map +1 -1
  231. package/dist/types.d.ts +182 -7
  232. package/dist/types.d.ts.map +1 -1
  233. package/grammars/tree-sitter-bash.wasm +0 -0
  234. package/grammars/tree-sitter-c.wasm +0 -0
  235. package/grammars/tree-sitter-cpp.wasm +0 -0
  236. package/grammars/tree-sitter-kotlin.wasm +0 -0
  237. package/grammars/tree-sitter-scala.wasm +0 -0
  238. package/grammars/tree-sitter-swift.wasm +0 -0
  239. package/package.json +13 -7
  240. package/src/ast-analysis/engine.ts +147 -138
  241. package/src/ast-analysis/visitors/ast-store-visitor.ts +15 -2
  242. package/src/ast-analysis/visitors/complexity-visitor.ts +11 -11
  243. package/src/db/connection.ts +90 -59
  244. package/src/db/index.ts +1 -0
  245. package/src/db/migrations.ts +36 -32
  246. package/src/domain/analysis/context.ts +73 -75
  247. package/src/domain/analysis/dependencies.ts +78 -68
  248. package/src/domain/analysis/exports.ts +45 -34
  249. package/src/domain/analysis/fn-impact.ts +67 -64
  250. package/src/domain/analysis/module-map.ts +103 -8
  251. package/src/domain/analysis/query-helpers.ts +35 -0
  252. package/src/domain/graph/builder/helpers.ts +12 -6
  253. package/src/domain/graph/builder/incremental.ts +3 -2
  254. package/src/domain/graph/builder/pipeline.ts +71 -3
  255. package/src/domain/graph/builder/stages/build-edges.ts +10 -75
  256. package/src/domain/graph/builder/stages/build-structure.ts +9 -7
  257. package/src/domain/graph/builder/stages/collect-files.ts +2 -2
  258. package/src/domain/graph/builder/stages/detect-changes.ts +7 -2
  259. package/src/domain/graph/builder/stages/finalize.ts +159 -125
  260. package/src/domain/graph/builder/stages/insert-nodes.ts +32 -21
  261. package/src/domain/graph/builder/stages/resolve-imports.ts +3 -2
  262. package/src/domain/graph/resolve.ts +34 -46
  263. package/src/domain/graph/watcher.ts +12 -14
  264. package/src/domain/parser.ts +168 -97
  265. package/src/domain/search/search/cli-formatter.ts +121 -94
  266. package/src/extractors/bash.ts +97 -0
  267. package/src/extractors/c.ts +212 -0
  268. package/src/extractors/cpp.ts +298 -0
  269. package/src/extractors/csharp.ts +53 -56
  270. package/src/extractors/go.ts +152 -134
  271. package/src/extractors/hcl.ts +6 -6
  272. package/src/extractors/helpers.ts +93 -1
  273. package/src/extractors/index.ts +6 -0
  274. package/src/extractors/java.ts +43 -48
  275. package/src/extractors/javascript.ts +328 -281
  276. package/src/extractors/kotlin.ts +293 -0
  277. package/src/extractors/php.ts +46 -40
  278. package/src/extractors/python.ts +81 -104
  279. package/src/extractors/ruby.ts +6 -13
  280. package/src/extractors/rust.ts +65 -85
  281. package/src/extractors/scala.ts +285 -0
  282. package/src/extractors/swift.ts +293 -0
  283. package/src/features/ast.ts +10 -25
  284. package/src/features/audit.ts +24 -20
  285. package/src/features/branch-compare.ts +51 -4
  286. package/src/features/cfg.ts +158 -65
  287. package/src/features/check.ts +90 -74
  288. package/src/features/complexity-query.ts +181 -163
  289. package/src/features/complexity.ts +64 -1
  290. package/src/features/dataflow.ts +462 -217
  291. package/src/features/graph-enrichment.ts +161 -117
  292. package/src/features/sequence.ts +27 -4
  293. package/src/features/structure-query.ts +43 -4
  294. package/src/features/structure.ts +50 -22
  295. package/src/graph/algorithms/leiden/adapter.ts +126 -71
  296. package/src/graph/algorithms/leiden/index.ts +67 -28
  297. package/src/graph/algorithms/leiden/optimiser.ts +114 -105
  298. package/src/graph/algorithms/leiden/partition.ts +131 -98
  299. package/src/graph/model.ts +19 -7
  300. package/src/infrastructure/config.ts +60 -58
  301. package/src/infrastructure/registry.ts +17 -14
  302. package/src/mcp/server.ts +46 -37
  303. package/src/presentation/audit.ts +72 -67
  304. package/src/presentation/branch-compare.ts +54 -50
  305. package/src/presentation/check.ts +34 -34
  306. package/src/presentation/colors.ts +2 -0
  307. package/src/presentation/complexity.ts +39 -33
  308. package/src/presentation/queries-cli/exports.ts +17 -17
  309. package/src/presentation/queries-cli/impact.ts +30 -22
  310. package/src/types.ts +189 -7
@@ -11,113 +11,98 @@ interface SearchOpts extends SemanticSearchOpts {
11
11
  offset?: number;
12
12
  }
13
13
 
14
- export async function search(
15
- query: string,
16
- customDbPath: string | undefined,
17
- opts: SearchOpts = {},
18
- ): Promise<void> {
19
- const mode = opts.mode || 'hybrid';
14
+ const kindIcon = (kind: string): string =>
15
+ kind === 'function' ? 'f' : kind === 'class' ? '*' : 'o';
20
16
 
21
- const queries = query
22
- .split(';')
23
- .map((q) => q.trim())
24
- .filter((q) => q.length > 0);
25
-
26
- const kindIcon = (kind: string): string =>
27
- kind === 'function' ? 'f' : kind === 'class' ? '*' : 'o';
28
-
29
- // Keyword-only mode
30
- if (mode === 'keyword') {
31
- const singleQuery = queries.length === 1 ? queries[0]! : query;
32
- const data = ftsSearchData(singleQuery, customDbPath, opts);
33
- if (!data) {
34
- console.log('No FTS5 index found. Run `codegraph embed` to build the keyword index.');
35
- return;
36
- }
37
- if (opts.json) {
38
- console.log(JSON.stringify(data, null, 2));
39
- return;
40
- }
41
- console.log(`\nKeyword search: "${singleQuery}" (BM25)\n`);
42
- if (data.results.length === 0) {
43
- console.log(' No results found.');
44
- } else {
45
- for (const r of data.results) {
46
- console.log(
47
- ` BM25 ${r.bm25Score.toFixed(2)} ${kindIcon(r.kind)} ${r.name} -- ${r.file}:${r.line}`,
48
- );
49
- }
50
- }
51
- console.log(`\n ${data.results.length} results shown\n`);
17
+ function formatKeywordResults(
18
+ singleQuery: string,
19
+ customDbPath: string | undefined,
20
+ opts: SearchOpts,
21
+ ): void {
22
+ const data = ftsSearchData(singleQuery, customDbPath, opts);
23
+ if (!data) {
24
+ console.log('No FTS5 index found. Run `codegraph embed` to build the keyword index.');
52
25
  return;
53
26
  }
54
-
55
- // Semantic-only mode
56
- if (mode === 'semantic') {
57
- if (queries.length <= 1) {
58
- const singleQuery = queries[0] || query;
59
- const data = await searchData(singleQuery, customDbPath, opts);
60
- if (!data) return;
61
- if (opts.json) {
62
- console.log(JSON.stringify(data, null, 2));
63
- return;
64
- }
65
- console.log(`\nSemantic search: "${singleQuery}"\n`);
66
- if (data.results.length === 0) {
67
- console.log(' No results above threshold.');
68
- } else {
69
- for (const r of data.results) {
70
- const bar = '#'.repeat(Math.round(r.similarity * 20));
71
- console.log(` ${(r.similarity * 100).toFixed(1)}% ${bar}`);
72
- console.log(` ${kindIcon(r.kind)} ${r.name} -- ${r.file}:${r.line}`);
73
- }
74
- }
75
- console.log(`\n ${data.results.length} results shown\n`);
76
- } else {
77
- const data = await multiSearchData(queries, customDbPath, opts);
78
- if (!data) return;
79
- if (opts.json) {
80
- console.log(JSON.stringify(data, null, 2));
81
- return;
82
- }
83
- console.log(`\nMulti-query semantic search (RRF, k=${opts.rrfK || 60}):`);
84
- for (let i = 0; i < queries.length; i++) console.log(` [${i + 1}] "${queries[i]}"`);
85
- console.log();
86
- if (data.results.length === 0) {
87
- console.log(' No results above threshold.');
88
- } else {
89
- for (const r of data.results) {
90
- console.log(
91
- ` RRF ${r.rrf.toFixed(4)} ${kindIcon(r.kind)} ${r.name} -- ${r.file}:${r.line}`,
92
- );
93
- for (const qs of r.queryScores) {
94
- const bar = '#'.repeat(Math.round(qs.similarity * 20));
95
- console.log(
96
- ` [${queries.indexOf(qs.query) + 1}] ${(qs.similarity * 100).toFixed(1)}% ${bar} (rank ${qs.rank})`,
97
- );
98
- }
99
- }
100
- }
101
- console.log(`\n ${data.results.length} results shown\n`);
102
- }
27
+ if (opts.json) {
28
+ console.log(JSON.stringify(data, null, 2));
103
29
  return;
104
30
  }
31
+ console.log(`\nKeyword search: "${singleQuery}" (BM25)\n`);
32
+ if (data.results.length === 0) {
33
+ console.log(' No results found.');
34
+ } else {
35
+ for (const r of data.results) {
36
+ console.log(
37
+ ` BM25 ${r.bm25Score.toFixed(2)} ${kindIcon(r.kind)} ${r.name} -- ${r.file}:${r.line}`,
38
+ );
39
+ }
40
+ }
41
+ console.log(`\n ${data.results.length} results shown\n`);
42
+ }
105
43
 
106
- // Hybrid mode (default)
107
- const data = await hybridSearchData(query, customDbPath, opts);
108
-
109
- if (!data) {
110
- warn(
111
- 'FTS5 index not found using semantic search only. Re-run `codegraph embed` to enable hybrid mode.',
112
- );
113
- return search(query, customDbPath, { ...opts, mode: 'semantic' });
44
+ async function formatSemanticSingle(
45
+ singleQuery: string,
46
+ customDbPath: string | undefined,
47
+ opts: SearchOpts,
48
+ ): Promise<void> {
49
+ const data = await searchData(singleQuery, customDbPath, opts);
50
+ if (!data) return;
51
+ if (opts.json) {
52
+ console.log(JSON.stringify(data, null, 2));
53
+ return;
54
+ }
55
+ console.log(`\nSemantic search: "${singleQuery}"\n`);
56
+ if (data.results.length === 0) {
57
+ console.log(' No results above threshold.');
58
+ } else {
59
+ for (const r of data.results) {
60
+ const bar = '#'.repeat(Math.round(r.similarity * 20));
61
+ console.log(` ${(r.similarity * 100).toFixed(1)}% ${bar}`);
62
+ console.log(` ${kindIcon(r.kind)} ${r.name} -- ${r.file}:${r.line}`);
63
+ }
114
64
  }
65
+ console.log(`\n ${data.results.length} results shown\n`);
66
+ }
115
67
 
68
+ async function formatSemanticMulti(
69
+ queries: string[],
70
+ customDbPath: string | undefined,
71
+ opts: SearchOpts,
72
+ ): Promise<void> {
73
+ const data = await multiSearchData(queries, customDbPath, opts);
74
+ if (!data) return;
116
75
  if (opts.json) {
117
76
  console.log(JSON.stringify(data, null, 2));
118
77
  return;
119
78
  }
79
+ console.log(`\nMulti-query semantic search (RRF, k=${opts.rrfK || 60}):`);
80
+ for (let i = 0; i < queries.length; i++) console.log(` [${i + 1}] "${queries[i]}"`);
81
+ console.log();
82
+ if (data.results.length === 0) {
83
+ console.log(' No results above threshold.');
84
+ } else {
85
+ for (const r of data.results) {
86
+ console.log(
87
+ ` RRF ${r.rrf.toFixed(4)} ${kindIcon(r.kind)} ${r.name} -- ${r.file}:${r.line}`,
88
+ );
89
+ for (const qs of r.queryScores) {
90
+ const bar = '#'.repeat(Math.round(qs.similarity * 20));
91
+ console.log(
92
+ ` [${queries.indexOf(qs.query) + 1}] ${(qs.similarity * 100).toFixed(1)}% ${bar} (rank ${qs.rank})`,
93
+ );
94
+ }
95
+ }
96
+ }
97
+ console.log(`\n ${data.results.length} results shown\n`);
98
+ }
120
99
 
100
+ function formatHybridResults(
101
+ queries: string[],
102
+ query: string,
103
+ data: { results: any[] },
104
+ opts: SearchOpts,
105
+ ): void {
121
106
  const rrfK = opts.rrfK || 60;
122
107
  if (queries.length <= 1) {
123
108
  const singleQuery = queries[0] || query;
@@ -150,3 +135,45 @@ export async function search(
150
135
 
151
136
  console.log(`\n ${data.results.length} results shown\n`);
152
137
  }
138
+
139
+ export async function search(
140
+ query: string,
141
+ customDbPath: string | undefined,
142
+ opts: SearchOpts = {},
143
+ ): Promise<void> {
144
+ const mode = opts.mode || 'hybrid';
145
+
146
+ const queries = query
147
+ .split(';')
148
+ .map((q) => q.trim())
149
+ .filter((q) => q.length > 0);
150
+
151
+ if (mode === 'keyword') {
152
+ const singleQuery = queries.length === 1 ? queries[0]! : query;
153
+ return formatKeywordResults(singleQuery, customDbPath, opts);
154
+ }
155
+
156
+ if (mode === 'semantic') {
157
+ if (queries.length <= 1) {
158
+ return formatSemanticSingle(queries[0] || query, customDbPath, opts);
159
+ }
160
+ return formatSemanticMulti(queries, customDbPath, opts);
161
+ }
162
+
163
+ // Hybrid mode (default)
164
+ const data = await hybridSearchData(query, customDbPath, opts);
165
+
166
+ if (!data) {
167
+ warn(
168
+ 'FTS5 index not found — using semantic search only. Re-run `codegraph embed` to enable hybrid mode.',
169
+ );
170
+ return search(query, customDbPath, { ...opts, mode: 'semantic' });
171
+ }
172
+
173
+ if (opts.json) {
174
+ console.log(JSON.stringify(data, null, 2));
175
+ return;
176
+ }
177
+
178
+ formatHybridResults(queries, query, data, opts);
179
+ }
@@ -0,0 +1,97 @@
1
+ import type { Call, ExtractorOutput, TreeSitterNode, TreeSitterTree } from '../types.js';
2
+ import { nodeEndLine } from './helpers.js';
3
+
4
+ /**
5
+ * Extract symbols from Bash/Shell files.
6
+ */
7
+ export function extractBashSymbols(tree: TreeSitterTree, _filePath: string): ExtractorOutput {
8
+ const ctx: ExtractorOutput = {
9
+ definitions: [],
10
+ calls: [],
11
+ imports: [],
12
+ classes: [],
13
+ exports: [],
14
+ typeMap: new Map(),
15
+ };
16
+
17
+ walkBashNode(tree.rootNode, ctx);
18
+ return ctx;
19
+ }
20
+
21
+ function walkBashNode(node: TreeSitterNode, ctx: ExtractorOutput): void {
22
+ switch (node.type) {
23
+ case 'function_definition':
24
+ handleBashFunctionDef(node, ctx);
25
+ break;
26
+ case 'command':
27
+ handleBashCommand(node, ctx);
28
+ break;
29
+ }
30
+
31
+ for (let i = 0; i < node.childCount; i++) {
32
+ const child = node.child(i);
33
+ if (child) walkBashNode(child, ctx);
34
+ }
35
+ }
36
+
37
+ // ── Walk-path per-node-type handlers ────────────────────────────────────────
38
+
39
+ function handleBashFunctionDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
40
+ const nameNode = node.childForFieldName('name');
41
+ if (!nameNode) return;
42
+ ctx.definitions.push({
43
+ name: nameNode.text,
44
+ kind: 'function',
45
+ line: node.startPosition.row + 1,
46
+ endLine: nodeEndLine(node),
47
+ });
48
+ }
49
+
50
+ function handleBashCommand(node: TreeSitterNode, ctx: ExtractorOutput): void {
51
+ // First child is command_name
52
+ let commandNameNode: TreeSitterNode | null = null;
53
+ for (let i = 0; i < node.childCount; i++) {
54
+ const child = node.child(i);
55
+ if (child && child.type === 'command_name') {
56
+ commandNameNode = child;
57
+ break;
58
+ }
59
+ }
60
+ if (!commandNameNode) return;
61
+
62
+ const cmdText = commandNameNode.text;
63
+
64
+ // "source" or "." commands are imports
65
+ if (cmdText === 'source' || cmdText === '.') {
66
+ // Second argument is the source path
67
+ let argNode: TreeSitterNode | null = null;
68
+ let foundCmd = false;
69
+ for (let i = 0; i < node.childCount; i++) {
70
+ const child = node.child(i);
71
+ if (!child) continue;
72
+ if (child.type === 'command_name') {
73
+ foundCmd = true;
74
+ continue;
75
+ }
76
+ if (foundCmd && child.type !== 'command_name') {
77
+ argNode = child;
78
+ break;
79
+ }
80
+ }
81
+ if (argNode) {
82
+ const source = argNode.text;
83
+ const lastName = source.split('/').pop() ?? source;
84
+ ctx.imports.push({
85
+ source,
86
+ names: [lastName],
87
+ line: node.startPosition.row + 1,
88
+ bashSource: true,
89
+ });
90
+ }
91
+ return;
92
+ }
93
+
94
+ // Regular command call
95
+ const call: Call = { name: cmdText, line: node.startPosition.row + 1 };
96
+ ctx.calls.push(call);
97
+ }
@@ -0,0 +1,212 @@
1
+ import type {
2
+ Call,
3
+ ExtractorOutput,
4
+ SubDeclaration,
5
+ TreeSitterNode,
6
+ TreeSitterTree,
7
+ } from '../types.js';
8
+ import { findChild, nodeEndLine } from './helpers.js';
9
+
10
+ /**
11
+ * Extract symbols from C files.
12
+ */
13
+ export function extractCSymbols(tree: TreeSitterTree, _filePath: string): ExtractorOutput {
14
+ const ctx: ExtractorOutput = {
15
+ definitions: [],
16
+ calls: [],
17
+ imports: [],
18
+ classes: [],
19
+ exports: [],
20
+ typeMap: new Map(),
21
+ };
22
+
23
+ walkCNode(tree.rootNode, ctx);
24
+ return ctx;
25
+ }
26
+
27
+ function walkCNode(node: TreeSitterNode, ctx: ExtractorOutput): void {
28
+ switch (node.type) {
29
+ case 'function_definition':
30
+ handleCFunctionDef(node, ctx);
31
+ break;
32
+ case 'struct_specifier':
33
+ handleCStructSpecifier(node, ctx);
34
+ break;
35
+ case 'enum_specifier':
36
+ handleCEnumSpecifier(node, ctx);
37
+ break;
38
+ case 'type_definition':
39
+ handleCTypedef(node, ctx);
40
+ break;
41
+ case 'preproc_include':
42
+ handleCInclude(node, ctx);
43
+ break;
44
+ case 'call_expression':
45
+ handleCCallExpression(node, ctx);
46
+ break;
47
+ }
48
+
49
+ for (let i = 0; i < node.childCount; i++) {
50
+ const child = node.child(i);
51
+ if (child) walkCNode(child, ctx);
52
+ }
53
+ }
54
+
55
+ // ── Walk-path per-node-type handlers ────────────────────────────────────────
56
+
57
+ function handleCFunctionDef(node: TreeSitterNode, ctx: ExtractorOutput): void {
58
+ // declarator > function_declarator > declarator(identifier)
59
+ const declarator = node.childForFieldName('declarator');
60
+ if (!declarator) return;
61
+ const funcDeclarator =
62
+ declarator.type === 'function_declarator'
63
+ ? declarator
64
+ : findChild(declarator, 'function_declarator');
65
+ if (!funcDeclarator) return;
66
+ const nameNode = funcDeclarator.childForFieldName('declarator');
67
+ if (!nameNode) return;
68
+ const name = nameNode.type === 'identifier' ? nameNode.text : nameNode.text;
69
+
70
+ const params = extractCParameters(funcDeclarator.childForFieldName('parameters'));
71
+ ctx.definitions.push({
72
+ name,
73
+ kind: 'function',
74
+ line: node.startPosition.row + 1,
75
+ endLine: nodeEndLine(node),
76
+ children: params.length > 0 ? params : undefined,
77
+ });
78
+ }
79
+
80
+ function handleCStructSpecifier(node: TreeSitterNode, ctx: ExtractorOutput): void {
81
+ const nameNode = node.childForFieldName('name');
82
+ if (!nameNode) return;
83
+ const children = extractStructFields(node);
84
+ ctx.definitions.push({
85
+ name: nameNode.text,
86
+ kind: 'struct',
87
+ line: node.startPosition.row + 1,
88
+ endLine: nodeEndLine(node),
89
+ children: children.length > 0 ? children : undefined,
90
+ });
91
+ }
92
+
93
+ function handleCEnumSpecifier(node: TreeSitterNode, ctx: ExtractorOutput): void {
94
+ const nameNode = node.childForFieldName('name');
95
+ if (!nameNode) return;
96
+ const children = extractEnumEntries(node);
97
+ ctx.definitions.push({
98
+ name: nameNode.text,
99
+ kind: 'enum',
100
+ line: node.startPosition.row + 1,
101
+ endLine: nodeEndLine(node),
102
+ children: children.length > 0 ? children : undefined,
103
+ });
104
+ }
105
+
106
+ function handleCTypedef(node: TreeSitterNode, ctx: ExtractorOutput): void {
107
+ // The typedef name is the last type_identifier, identifier, or primitive_type child
108
+ let name: string | undefined;
109
+ for (let i = node.childCount - 1; i >= 0; i--) {
110
+ const child = node.child(i);
111
+ if (
112
+ child &&
113
+ (child.type === 'type_identifier' ||
114
+ child.type === 'identifier' ||
115
+ child.type === 'primitive_type')
116
+ ) {
117
+ name = child.text;
118
+ break;
119
+ }
120
+ }
121
+ if (!name) return;
122
+ ctx.definitions.push({
123
+ name,
124
+ kind: 'type',
125
+ line: node.startPosition.row + 1,
126
+ endLine: nodeEndLine(node),
127
+ });
128
+ }
129
+
130
+ function handleCInclude(node: TreeSitterNode, ctx: ExtractorOutput): void {
131
+ const pathNode = node.childForFieldName('path');
132
+ if (!pathNode) return;
133
+ // Strip quotes or angle brackets
134
+ const raw = pathNode.text;
135
+ const source = raw.replace(/^["<]|[">]$/g, '');
136
+ const lastName = source.split('/').pop() ?? source;
137
+ ctx.imports.push({
138
+ source,
139
+ names: [lastName],
140
+ line: node.startPosition.row + 1,
141
+ cInclude: true,
142
+ });
143
+ }
144
+
145
+ function handleCCallExpression(node: TreeSitterNode, ctx: ExtractorOutput): void {
146
+ const funcNode = node.childForFieldName('function');
147
+ if (!funcNode) return;
148
+ const call: Call = { name: '', line: node.startPosition.row + 1 };
149
+ if (funcNode.type === 'field_expression') {
150
+ const field = funcNode.childForFieldName('field');
151
+ const argument = funcNode.childForFieldName('argument');
152
+ if (field) call.name = field.text;
153
+ if (argument) call.receiver = argument.text;
154
+ } else {
155
+ call.name = funcNode.text;
156
+ }
157
+ if (call.name) ctx.calls.push(call);
158
+ }
159
+
160
+ // ── Child extraction helpers ────────────────────────────────────────────────
161
+
162
+ function extractCParameters(paramListNode: TreeSitterNode | null): SubDeclaration[] {
163
+ const params: SubDeclaration[] = [];
164
+ if (!paramListNode) return params;
165
+ for (let i = 0; i < paramListNode.childCount; i++) {
166
+ const param = paramListNode.child(i);
167
+ if (!param || param.type !== 'parameter_declaration') continue;
168
+ const nameNode = param.childForFieldName('declarator');
169
+ if (nameNode) {
170
+ const name =
171
+ nameNode.type === 'identifier'
172
+ ? nameNode.text
173
+ : (findChild(nameNode, 'identifier')?.text ?? nameNode.text);
174
+ params.push({ name, kind: 'parameter', line: param.startPosition.row + 1 });
175
+ }
176
+ }
177
+ return params;
178
+ }
179
+
180
+ function extractStructFields(structNode: TreeSitterNode): SubDeclaration[] {
181
+ const fields: SubDeclaration[] = [];
182
+ const body = findChild(structNode, 'field_declaration_list');
183
+ if (!body) return fields;
184
+ for (let i = 0; i < body.childCount; i++) {
185
+ const member = body.child(i);
186
+ if (!member || member.type !== 'field_declaration') continue;
187
+ const nameNode = member.childForFieldName('declarator');
188
+ if (nameNode) {
189
+ const name =
190
+ nameNode.type === 'identifier'
191
+ ? nameNode.text
192
+ : (findChild(nameNode, 'identifier')?.text ?? nameNode.text);
193
+ fields.push({ name, kind: 'property', line: member.startPosition.row + 1 });
194
+ }
195
+ }
196
+ return fields;
197
+ }
198
+
199
+ function extractEnumEntries(enumNode: TreeSitterNode): SubDeclaration[] {
200
+ const entries: SubDeclaration[] = [];
201
+ const body = findChild(enumNode, 'enumerator_list');
202
+ if (!body) return entries;
203
+ for (let i = 0; i < body.childCount; i++) {
204
+ const member = body.child(i);
205
+ if (!member || member.type !== 'enumerator') continue;
206
+ const nameNode = member.childForFieldName('name');
207
+ if (nameNode) {
208
+ entries.push({ name: nameNode.text, kind: 'constant', line: member.startPosition.row + 1 });
209
+ }
210
+ }
211
+ return entries;
212
+ }