@optave/codegraph 3.1.4 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/README.md +29 -72
  2. package/package.json +10 -8
  3. package/src/ast-analysis/engine.js +260 -246
  4. package/src/ast-analysis/shared.js +2 -14
  5. package/src/ast-analysis/visitors/cfg-visitor.js +635 -649
  6. package/src/ast-analysis/visitors/complexity-visitor.js +135 -139
  7. package/src/ast-analysis/visitors/dataflow-visitor.js +230 -224
  8. package/src/cli/commands/ast.js +4 -7
  9. package/src/cli/commands/audit.js +11 -11
  10. package/src/cli/commands/batch.js +6 -5
  11. package/src/cli/commands/branch-compare.js +1 -1
  12. package/src/cli/commands/brief.js +12 -0
  13. package/src/cli/commands/build.js +1 -1
  14. package/src/cli/commands/cfg.js +5 -8
  15. package/src/cli/commands/check.js +28 -36
  16. package/src/cli/commands/children.js +9 -7
  17. package/src/cli/commands/co-change.js +5 -3
  18. package/src/cli/commands/communities.js +2 -6
  19. package/src/cli/commands/complexity.js +5 -3
  20. package/src/cli/commands/context.js +9 -8
  21. package/src/cli/commands/cycles.js +12 -8
  22. package/src/cli/commands/dataflow.js +5 -8
  23. package/src/cli/commands/deps.js +9 -8
  24. package/src/cli/commands/diff-impact.js +2 -6
  25. package/src/cli/commands/embed.js +1 -1
  26. package/src/cli/commands/export.js +34 -31
  27. package/src/cli/commands/exports.js +2 -6
  28. package/src/cli/commands/flow.js +5 -8
  29. package/src/cli/commands/fn-impact.js +9 -8
  30. package/src/cli/commands/impact.js +2 -6
  31. package/src/cli/commands/info.js +2 -2
  32. package/src/cli/commands/map.js +1 -1
  33. package/src/cli/commands/mcp.js +1 -1
  34. package/src/cli/commands/models.js +1 -1
  35. package/src/cli/commands/owners.js +5 -3
  36. package/src/cli/commands/path.js +2 -2
  37. package/src/cli/commands/plot.js +40 -31
  38. package/src/cli/commands/query.js +9 -8
  39. package/src/cli/commands/registry.js +2 -2
  40. package/src/cli/commands/roles.js +5 -8
  41. package/src/cli/commands/search.js +9 -3
  42. package/src/cli/commands/sequence.js +5 -8
  43. package/src/cli/commands/snapshot.js +6 -1
  44. package/src/cli/commands/stats.js +1 -1
  45. package/src/cli/commands/structure.js +5 -4
  46. package/src/cli/commands/triage.js +41 -30
  47. package/src/cli/commands/watch.js +1 -1
  48. package/src/cli/commands/where.js +2 -6
  49. package/src/cli/index.js +11 -5
  50. package/src/cli/shared/open-graph.js +13 -0
  51. package/src/cli/shared/options.js +22 -2
  52. package/src/cli.js +1 -1
  53. package/src/db/connection.js +140 -11
  54. package/src/{db.js → db/index.js} +12 -5
  55. package/src/db/migrations.js +42 -65
  56. package/src/db/query-builder.js +72 -9
  57. package/src/db/repository/base.js +1 -1
  58. package/src/db/repository/graph-read.js +3 -3
  59. package/src/db/repository/in-memory-repository.js +30 -28
  60. package/src/db/repository/nodes.js +10 -17
  61. package/src/domain/analysis/brief.js +155 -0
  62. package/src/domain/analysis/context.js +392 -0
  63. package/src/domain/analysis/dependencies.js +395 -0
  64. package/src/{analysis → domain/analysis}/exports.js +11 -6
  65. package/src/domain/analysis/impact.js +581 -0
  66. package/src/domain/analysis/module-map.js +348 -0
  67. package/src/{analysis → domain/analysis}/roles.js +12 -9
  68. package/src/{analysis → domain/analysis}/symbol-lookup.js +19 -11
  69. package/src/{builder → domain/graph/builder}/helpers.js +4 -4
  70. package/src/{builder → domain/graph/builder}/incremental.js +119 -93
  71. package/src/domain/graph/builder/pipeline.js +156 -0
  72. package/src/domain/graph/builder/stages/build-edges.js +376 -0
  73. package/src/{builder → domain/graph/builder}/stages/build-structure.js +4 -4
  74. package/src/{builder → domain/graph/builder}/stages/collect-files.js +2 -2
  75. package/src/{builder → domain/graph/builder}/stages/detect-changes.js +204 -183
  76. package/src/{builder → domain/graph/builder}/stages/finalize.js +4 -4
  77. package/src/domain/graph/builder/stages/insert-nodes.js +203 -0
  78. package/src/{builder → domain/graph/builder}/stages/parse-files.js +2 -2
  79. package/src/{builder → domain/graph/builder}/stages/resolve-imports.js +1 -1
  80. package/src/{builder → domain/graph/builder}/stages/run-analyses.js +2 -2
  81. package/src/{change-journal.js → domain/graph/change-journal.js} +1 -1
  82. package/src/{cycles.js → domain/graph/cycles.js} +4 -4
  83. package/src/{journal.js → domain/graph/journal.js} +1 -1
  84. package/src/{resolve.js → domain/graph/resolve.js} +2 -2
  85. package/src/{watcher.js → domain/graph/watcher.js} +7 -7
  86. package/src/{parser.js → domain/parser.js} +24 -15
  87. package/src/{queries.js → domain/queries.js} +17 -16
  88. package/src/{embeddings → domain/search}/generator.js +3 -3
  89. package/src/{embeddings → domain/search}/models.js +2 -2
  90. package/src/{embeddings → domain/search}/search/cli-formatter.js +1 -1
  91. package/src/{embeddings → domain/search}/search/filters.js +9 -5
  92. package/src/{embeddings → domain/search}/search/hybrid.js +1 -1
  93. package/src/{embeddings → domain/search}/search/keyword.js +13 -6
  94. package/src/{embeddings → domain/search}/search/prepare.js +15 -7
  95. package/src/{embeddings → domain/search}/search/semantic.js +1 -1
  96. package/src/{embeddings → domain/search}/strategies/structured.js +1 -1
  97. package/src/extractors/csharp.js +224 -207
  98. package/src/extractors/go.js +176 -172
  99. package/src/extractors/hcl.js +94 -78
  100. package/src/extractors/java.js +213 -207
  101. package/src/extractors/javascript.js +275 -305
  102. package/src/extractors/php.js +234 -221
  103. package/src/extractors/python.js +252 -250
  104. package/src/extractors/ruby.js +192 -185
  105. package/src/extractors/rust.js +182 -167
  106. package/src/{ast.js → features/ast.js} +13 -11
  107. package/src/{audit.js → features/audit.js} +20 -46
  108. package/src/{batch.js → features/batch.js} +5 -5
  109. package/src/{boundaries.js → features/boundaries.js} +100 -85
  110. package/src/{branch-compare.js → features/branch-compare.js} +3 -3
  111. package/src/{cfg.js → features/cfg.js} +141 -150
  112. package/src/{check.js → features/check.js} +13 -30
  113. package/src/{cochange.js → features/cochange.js} +5 -5
  114. package/src/{communities.js → features/communities.js} +72 -57
  115. package/src/{complexity.js → features/complexity.js} +154 -143
  116. package/src/{dataflow.js → features/dataflow.js} +155 -158
  117. package/src/{export.js → features/export.js} +6 -6
  118. package/src/{flow.js → features/flow.js} +4 -4
  119. package/src/{viewer.js → features/graph-enrichment.js} +8 -8
  120. package/src/{manifesto.js → features/manifesto.js} +15 -12
  121. package/src/{owners.js → features/owners.js} +6 -5
  122. package/src/features/sequence.js +300 -0
  123. package/src/features/shared/find-nodes.js +31 -0
  124. package/src/{snapshot.js → features/snapshot.js} +3 -3
  125. package/src/{structure.js → features/structure.js} +139 -108
  126. package/src/features/triage.js +141 -0
  127. package/src/graph/builders/dependency.js +33 -14
  128. package/src/graph/classifiers/risk.js +3 -2
  129. package/src/graph/classifiers/roles.js +6 -3
  130. package/src/index.cjs +16 -0
  131. package/src/index.js +40 -39
  132. package/src/{native.js → infrastructure/native.js} +1 -1
  133. package/src/mcp/middleware.js +1 -1
  134. package/src/mcp/server.js +68 -59
  135. package/src/mcp/tool-registry.js +15 -2
  136. package/src/mcp/tools/ast-query.js +1 -1
  137. package/src/mcp/tools/audit.js +1 -1
  138. package/src/mcp/tools/batch-query.js +1 -1
  139. package/src/mcp/tools/branch-compare.js +3 -1
  140. package/src/mcp/tools/brief.js +8 -0
  141. package/src/mcp/tools/cfg.js +1 -1
  142. package/src/mcp/tools/check.js +3 -3
  143. package/src/mcp/tools/co-changes.js +1 -1
  144. package/src/mcp/tools/code-owners.js +1 -1
  145. package/src/mcp/tools/communities.js +1 -1
  146. package/src/mcp/tools/complexity.js +1 -1
  147. package/src/mcp/tools/dataflow.js +2 -2
  148. package/src/mcp/tools/execution-flow.js +2 -2
  149. package/src/mcp/tools/export-graph.js +2 -2
  150. package/src/mcp/tools/find-cycles.js +2 -2
  151. package/src/mcp/tools/index.js +2 -0
  152. package/src/mcp/tools/list-repos.js +1 -1
  153. package/src/mcp/tools/sequence.js +1 -1
  154. package/src/mcp/tools/structure.js +1 -1
  155. package/src/mcp/tools/triage.js +2 -2
  156. package/src/{commands → presentation}/audit.js +2 -2
  157. package/src/{commands → presentation}/batch.js +1 -1
  158. package/src/{commands → presentation}/branch-compare.js +2 -2
  159. package/src/presentation/brief.js +51 -0
  160. package/src/{commands → presentation}/cfg.js +1 -1
  161. package/src/{commands → presentation}/check.js +2 -2
  162. package/src/{commands → presentation}/communities.js +1 -1
  163. package/src/{commands → presentation}/complexity.js +1 -1
  164. package/src/{commands → presentation}/dataflow.js +1 -1
  165. package/src/{commands → presentation}/flow.js +2 -2
  166. package/src/{commands → presentation}/manifesto.js +1 -1
  167. package/src/{commands → presentation}/owners.js +1 -1
  168. package/src/presentation/queries-cli/exports.js +53 -0
  169. package/src/presentation/queries-cli/impact.js +214 -0
  170. package/src/presentation/queries-cli/index.js +5 -0
  171. package/src/presentation/queries-cli/inspect.js +329 -0
  172. package/src/presentation/queries-cli/overview.js +196 -0
  173. package/src/presentation/queries-cli/path.js +65 -0
  174. package/src/presentation/queries-cli.js +27 -0
  175. package/src/{commands → presentation}/query.js +1 -1
  176. package/src/presentation/result-formatter.js +126 -3
  177. package/src/{commands → presentation}/sequence.js +2 -2
  178. package/src/{commands → presentation}/structure.js +1 -1
  179. package/src/presentation/table.js +0 -8
  180. package/src/{commands → presentation}/triage.js +1 -1
  181. package/src/{constants.js → shared/constants.js} +1 -1
  182. package/src/shared/file-utils.js +2 -2
  183. package/src/shared/generators.js +9 -5
  184. package/src/shared/hierarchy.js +1 -1
  185. package/src/{kinds.js → shared/kinds.js} +1 -1
  186. package/src/analysis/context.js +0 -408
  187. package/src/analysis/dependencies.js +0 -341
  188. package/src/analysis/impact.js +0 -463
  189. package/src/analysis/module-map.js +0 -322
  190. package/src/builder/pipeline.js +0 -130
  191. package/src/builder/stages/build-edges.js +0 -297
  192. package/src/builder/stages/insert-nodes.js +0 -195
  193. package/src/mcp.js +0 -2
  194. package/src/queries-cli.js +0 -866
  195. package/src/sequence.js +0 -289
  196. package/src/triage.js +0 -126
  197. /package/src/{builder → domain/graph/builder}/context.js +0 -0
  198. /package/src/{builder.js → domain/graph/builder.js} +0 -0
  199. /package/src/{embeddings → domain/search}/index.js +0 -0
  200. /package/src/{embeddings → domain/search}/stores/fts5.js +0 -0
  201. /package/src/{embeddings → domain/search}/stores/sqlite-blob.js +0 -0
  202. /package/src/{embeddings → domain/search}/strategies/source.js +0 -0
  203. /package/src/{embeddings → domain/search}/strategies/text-utils.js +0 -0
  204. /package/src/{config.js → infrastructure/config.js} +0 -0
  205. /package/src/{logger.js → infrastructure/logger.js} +0 -0
  206. /package/src/{registry.js → infrastructure/registry.js} +0 -0
  207. /package/src/{update-check.js → infrastructure/update-check.js} +0 -0
  208. /package/src/{commands → presentation}/cochange.js +0 -0
  209. /package/src/{errors.js → shared/errors.js} +0 -0
  210. /package/src/{paginate.js → shared/paginate.js} +0 -0
@@ -0,0 +1,300 @@
1
+ /**
2
+ * Sequence diagram generation – Mermaid sequenceDiagram from call graph edges.
3
+ *
4
+ * Participants are files (not individual functions). Calls within the same file
5
+ * become self-messages. This keeps diagrams readable and matches typical
6
+ * sequence-diagram conventions.
7
+ */
8
+
9
+ import { openRepo } from '../db/index.js';
10
+ import { SqliteRepository } from '../db/repository/sqlite-repository.js';
11
+ import { findMatchingNodes } from '../domain/queries.js';
12
+ import { isTestFile } from '../infrastructure/test-filter.js';
13
+ import { paginateResult } from '../shared/paginate.js';
14
+ import { FRAMEWORK_ENTRY_PREFIXES } from './structure.js';
15
+
16
+ // ─── Alias generation ────────────────────────────────────────────────
17
+
18
+ /**
19
+ * Build short participant aliases from file paths with collision handling.
20
+ * e.g. "src/builder.js" → "builder", but if two files share basename,
21
+ * progressively add parent dirs: "src/builder" vs "lib/builder".
22
+ */
23
+ function buildAliases(files) {
24
+ const aliases = new Map();
25
+ const basenames = new Map();
26
+
27
+ // Group by basename
28
+ for (const file of files) {
29
+ const base = file
30
+ .split('/')
31
+ .pop()
32
+ .replace(/\.[^.]+$/, '');
33
+ if (!basenames.has(base)) basenames.set(base, []);
34
+ basenames.get(base).push(file);
35
+ }
36
+
37
+ for (const [base, paths] of basenames) {
38
+ if (paths.length === 1) {
39
+ aliases.set(paths[0], base);
40
+ } else {
41
+ // Collision — progressively add parent dirs until aliases are unique
42
+ for (let depth = 2; depth <= 10; depth++) {
43
+ const trial = new Map();
44
+ let allUnique = true;
45
+ const seen = new Set();
46
+
47
+ for (const p of paths) {
48
+ const parts = p.replace(/\.[^.]+$/, '').split('/');
49
+ const alias = parts
50
+ .slice(-depth)
51
+ .join('_')
52
+ .replace(/[^a-zA-Z0-9_-]/g, '_');
53
+ trial.set(p, alias);
54
+ if (seen.has(alias)) allUnique = false;
55
+ seen.add(alias);
56
+ }
57
+
58
+ if (allUnique || depth === 10) {
59
+ for (const [p, alias] of trial) {
60
+ aliases.set(p, alias);
61
+ }
62
+ break;
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+ return aliases;
69
+ }
70
+
71
+ // ─── Helpers ─────────────────────────────────────────────────────────
72
+
73
+ function findEntryNode(repo, name, opts) {
74
+ let matchNode = findMatchingNodes(repo, name, opts)[0] ?? null;
75
+ if (!matchNode) {
76
+ for (const prefix of FRAMEWORK_ENTRY_PREFIXES) {
77
+ matchNode = findMatchingNodes(repo, `${prefix}${name}`, opts)[0] ?? null;
78
+ if (matchNode) break;
79
+ }
80
+ }
81
+ return matchNode;
82
+ }
83
+
84
+ function bfsCallees(repo, matchNode, maxDepth, noTests) {
85
+ const visited = new Set([matchNode.id]);
86
+ let frontier = [matchNode.id];
87
+ const messages = [];
88
+ const fileSet = new Set([matchNode.file]);
89
+ const idToNode = new Map();
90
+ idToNode.set(matchNode.id, matchNode);
91
+ let truncated = false;
92
+
93
+ for (let d = 1; d <= maxDepth; d++) {
94
+ const nextFrontier = [];
95
+
96
+ for (const fid of frontier) {
97
+ const callees = repo.findCallees(fid);
98
+ const caller = idToNode.get(fid);
99
+
100
+ for (const c of callees) {
101
+ if (noTests && isTestFile(c.file)) continue;
102
+
103
+ fileSet.add(c.file);
104
+ messages.push({
105
+ from: caller.file,
106
+ to: c.file,
107
+ label: c.name,
108
+ type: 'call',
109
+ depth: d,
110
+ });
111
+
112
+ if (visited.has(c.id)) continue;
113
+
114
+ visited.add(c.id);
115
+ nextFrontier.push(c.id);
116
+ idToNode.set(c.id, c);
117
+ }
118
+ }
119
+
120
+ frontier = nextFrontier;
121
+ if (frontier.length === 0) break;
122
+
123
+ if (d === maxDepth && frontier.length > 0) {
124
+ const hasMoreCalls = frontier.some((fid) => repo.findCallees(fid).length > 0);
125
+ if (hasMoreCalls) truncated = true;
126
+ }
127
+ }
128
+
129
+ return { messages, fileSet, idToNode, truncated };
130
+ }
131
+
132
+ function annotateDataflow(repo, messages, idToNode) {
133
+ const hasTable = repo.hasDataflowTable();
134
+
135
+ if (!hasTable || !(repo instanceof SqliteRepository)) return;
136
+
137
+ const db = repo.db;
138
+ const nodeByNameFile = new Map();
139
+ for (const n of idToNode.values()) {
140
+ nodeByNameFile.set(`${n.name}|${n.file}`, n);
141
+ }
142
+
143
+ const getReturns = db.prepare(
144
+ `SELECT d.expression FROM dataflow d
145
+ WHERE d.source_id = ? AND d.kind = 'returns'`,
146
+ );
147
+ const getFlowsTo = db.prepare(
148
+ `SELECT d.expression FROM dataflow d
149
+ WHERE d.target_id = ? AND d.kind = 'flows_to'
150
+ ORDER BY d.param_index`,
151
+ );
152
+
153
+ const seenReturns = new Set();
154
+ for (const msg of [...messages]) {
155
+ if (msg.type !== 'call') continue;
156
+ const targetNode = nodeByNameFile.get(`${msg.label}|${msg.to}`);
157
+ if (!targetNode) continue;
158
+
159
+ const returnKey = `${msg.to}->${msg.from}:${msg.label}`;
160
+ if (seenReturns.has(returnKey)) continue;
161
+
162
+ const returns = getReturns.all(targetNode.id);
163
+
164
+ if (returns.length > 0) {
165
+ seenReturns.add(returnKey);
166
+ const expr = returns[0].expression || 'result';
167
+ messages.push({
168
+ from: msg.to,
169
+ to: msg.from,
170
+ label: expr,
171
+ type: 'return',
172
+ depth: msg.depth,
173
+ });
174
+ }
175
+ }
176
+
177
+ for (const msg of messages) {
178
+ if (msg.type !== 'call') continue;
179
+ const targetNode = nodeByNameFile.get(`${msg.label}|${msg.to}`);
180
+ if (!targetNode) continue;
181
+
182
+ const params = getFlowsTo.all(targetNode.id);
183
+
184
+ if (params.length > 0) {
185
+ const paramNames = params
186
+ .map((p) => p.expression)
187
+ .filter(Boolean)
188
+ .slice(0, 3);
189
+ if (paramNames.length > 0) {
190
+ msg.label = `${msg.label}(${paramNames.join(', ')})`;
191
+ }
192
+ }
193
+ }
194
+ }
195
+
196
+ function buildParticipants(fileSet, entryFile) {
197
+ const aliases = buildAliases([...fileSet]);
198
+ const participants = [...fileSet].map((file) => ({
199
+ id: aliases.get(file),
200
+ label: file.split('/').pop(),
201
+ file,
202
+ }));
203
+
204
+ participants.sort((a, b) => {
205
+ if (a.file === entryFile) return -1;
206
+ if (b.file === entryFile) return 1;
207
+ return a.file.localeCompare(b.file);
208
+ });
209
+
210
+ return { participants, aliases };
211
+ }
212
+
213
+ // ─── Core data function ──────────────────────────────────────────────
214
+
215
+ /**
216
+ * Build sequence diagram data by BFS-forward from an entry point.
217
+ *
218
+ * @param {string} name - Symbol name to trace from
219
+ * @param {string} [dbPath]
220
+ * @param {object} [opts]
221
+ * @param {number} [opts.depth=10]
222
+ * @param {boolean} [opts.noTests]
223
+ * @param {string} [opts.file]
224
+ * @param {string} [opts.kind]
225
+ * @param {boolean} [opts.dataflow]
226
+ * @param {number} [opts.limit]
227
+ * @param {number} [opts.offset]
228
+ * @returns {{ entry, participants, messages, depth, totalMessages, truncated }}
229
+ */
230
+ export function sequenceData(name, dbPath, opts = {}) {
231
+ const { repo, close } = openRepo(dbPath, opts);
232
+ try {
233
+ const maxDepth = opts.depth || 10;
234
+ const noTests = opts.noTests || false;
235
+
236
+ const matchNode = findEntryNode(repo, name, opts);
237
+ if (!matchNode) {
238
+ return {
239
+ entry: null,
240
+ participants: [],
241
+ messages: [],
242
+ depth: maxDepth,
243
+ totalMessages: 0,
244
+ truncated: false,
245
+ };
246
+ }
247
+
248
+ const entry = {
249
+ name: matchNode.name,
250
+ file: matchNode.file,
251
+ kind: matchNode.kind,
252
+ line: matchNode.line,
253
+ };
254
+
255
+ const { messages, fileSet, idToNode, truncated } = bfsCallees(
256
+ repo,
257
+ matchNode,
258
+ maxDepth,
259
+ noTests,
260
+ );
261
+
262
+ if (opts.dataflow && messages.length > 0) {
263
+ annotateDataflow(repo, messages, idToNode);
264
+ }
265
+
266
+ messages.sort((a, b) => {
267
+ if (a.depth !== b.depth) return a.depth - b.depth;
268
+ if (a.type === 'call' && b.type === 'return') return -1;
269
+ if (a.type === 'return' && b.type === 'call') return 1;
270
+ return 0;
271
+ });
272
+
273
+ const { participants, aliases } = buildParticipants(fileSet, entry.file);
274
+
275
+ for (const msg of messages) {
276
+ msg.from = aliases.get(msg.from);
277
+ msg.to = aliases.get(msg.to);
278
+ }
279
+
280
+ const base = {
281
+ entry,
282
+ participants,
283
+ messages,
284
+ depth: maxDepth,
285
+ totalMessages: messages.length,
286
+ truncated,
287
+ };
288
+ const result = paginateResult(base, 'messages', { limit: opts.limit, offset: opts.offset });
289
+ if (opts.limit !== undefined || opts.offset !== undefined) {
290
+ const activeFiles = new Set(result.messages.flatMap((m) => [m.from, m.to]));
291
+ result.participants = result.participants.filter((p) => activeFiles.has(p.id));
292
+ }
293
+ return result;
294
+ } finally {
295
+ close();
296
+ }
297
+ }
298
+
299
+ // Re-export Mermaid renderer from presentation layer
300
+ export { sequenceToMermaid } from '../presentation/sequence-renderer.js';
@@ -0,0 +1,31 @@
1
+ import { buildFileConditionSQL } from '../../db/query-builder.js';
2
+ import { isTestFile } from '../../infrastructure/test-filter.js';
3
+
4
+ /**
5
+ * Look up node(s) by name with optional file/kind/noTests filtering.
6
+ *
7
+ * @param {object} db - open SQLite database handle
8
+ * @param {string} name - symbol name (partial LIKE match)
9
+ * @param {object} [opts] - { kind, file, noTests }
10
+ * @param {string[]} defaultKinds - fallback kinds when opts.kind is not set
11
+ * @returns {object[]} matching node rows
12
+ */
13
+ export function findNodes(db, name, opts = {}, defaultKinds = []) {
14
+ const kinds = opts.kind ? [opts.kind] : defaultKinds;
15
+ if (kinds.length === 0) throw new Error('findNodes: no kinds specified');
16
+ const placeholders = kinds.map(() => '?').join(', ');
17
+ const params = [`%${name}%`, ...kinds];
18
+
19
+ const fc = buildFileConditionSQL(opts.file, 'file');
20
+ params.push(...fc.params);
21
+
22
+ const rows = db
23
+ .prepare(
24
+ `SELECT * FROM nodes
25
+ WHERE name LIKE ? AND kind IN (${placeholders})${fc.sql}
26
+ ORDER BY file, line`,
27
+ )
28
+ .all(...params);
29
+
30
+ return opts.noTests ? rows.filter((n) => !isTestFile(n.file)) : rows;
31
+ }
@@ -1,9 +1,9 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import Database from 'better-sqlite3';
4
- import { findDbPath } from './db.js';
5
- import { ConfigError, DbError } from './errors.js';
6
- import { debug } from './logger.js';
4
+ import { findDbPath } from '../db/index.js';
5
+ import { debug } from '../infrastructure/logger.js';
6
+ import { ConfigError, DbError } from '../shared/errors.js';
7
7
 
8
8
  const NAME_RE = /^[a-zA-Z0-9_-]+$/;
9
9