@quolu/lattice 0.52.3 → 0.53.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 (188) hide show
  1. package/LICENSE +147 -147
  2. package/README.ja.md +355 -355
  3. package/README.md +258 -258
  4. package/bin/lattice-bridge.mjs +25 -0
  5. package/bin/lattice-hub.mjs +67 -0
  6. package/bin/lattice-mcp.mjs +0 -0
  7. package/bin/lattice-scripted-adapter.mjs +0 -0
  8. package/bin/lattice-scripted-worker.mjs +0 -0
  9. package/bin/lattice-work-order-adapter.mjs +0 -0
  10. package/bin/lattice.mjs +0 -0
  11. package/docs/bridge-setup.md +132 -132
  12. package/docs/schemas/lattice.executor_packet.v1.schema.json +57 -57
  13. package/docs/schemas/lattice.executor_receipt.v1.schema.json +66 -66
  14. package/docs/schemas/lattice.phase_todo_revision.v3.schema.json +360 -360
  15. package/docs/schemas/lattice.plan_create_input.v1.schema.json +56 -56
  16. package/docs/schemas/lattice.plan_create_input.v2.schema.json +72 -72
  17. package/docs/schemas/lattice.plan_create_input.v3.schema.json +81 -81
  18. package/docs/schemas/lattice.plan_create_input.v4.schema.json +85 -85
  19. package/docs/schemas/lattice.run_request.v1.schema.json +238 -238
  20. package/docs/schemas/lattice.runtime_adapter_capabilities.v2.schema.json +55 -55
  21. package/docs/schemas/lattice.runtime_adapter_registration_input.v1.schema.json +78 -78
  22. package/docs/schemas/lattice.runtime_adapter_registration_input.v2.schema.json +86 -86
  23. package/docs/schemas/lattice.todo_extraction.v2.schema.json +298 -298
  24. package/docs/schemas/lattice.todo_extraction.v3.schema.json +146 -146
  25. package/docs/schemas/lattice.todo_revision.v2.schema.json +260 -260
  26. package/docs/schemas/lattice.todo_revision_set.v3.schema.json +363 -363
  27. package/package.json +103 -103
  28. package/sensor/LICENSE +21 -21
  29. package/sensor/NOTICE +19 -19
  30. package/sensor/dist/bin/lattice-sensor.js +9 -9
  31. package/sensor/dist/db/index.js +24 -24
  32. package/sensor/dist/db/migrations.js +41 -41
  33. package/sensor/dist/db/queries.js +164 -164
  34. package/sensor/dist/db/schema.sql +205 -205
  35. package/sensor/dist/directory.js +5 -5
  36. package/sensor/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  37. package/sensor/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
  38. package/sensor/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
  39. package/sensor/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
  40. package/sensor/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
  41. package/sensor/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
  42. package/sensor/dist/extraction/wasm/tree-sitter-go.wasm +0 -0
  43. package/sensor/dist/extraction/wasm/tree-sitter-java.wasm +0 -0
  44. package/sensor/dist/extraction/wasm/tree-sitter-javascript.wasm +0 -0
  45. package/sensor/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  46. package/sensor/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  47. package/sensor/dist/extraction/wasm/tree-sitter-python.wasm +0 -0
  48. package/sensor/dist/extraction/wasm/tree-sitter-tsx.wasm +0 -0
  49. package/sensor/dist/extraction/wasm/tree-sitter-typescript.wasm +0 -0
  50. package/sensor/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
  51. package/sensor/dist/mcp/liveness-watchdog.js +53 -53
  52. package/sensor/dist/mcp/server-instructions.js +95 -95
  53. package/sensor/package.json +56 -56
  54. package/src/artifact-contracts-v2.mjs +325 -325
  55. package/src/artifact-contracts.mjs +895 -895
  56. package/src/boundary-compiler.mjs +712 -712
  57. package/src/boundary-observation-compiler-v2.mjs +344 -344
  58. package/src/bounded-seam.mjs +230 -230
  59. package/src/bridge-address.mjs +107 -107
  60. package/src/bridge-cli.mjs +303 -297
  61. package/src/bridge-config.mjs +382 -346
  62. package/src/bridge-daemon.mjs +378 -378
  63. package/src/bridge-hub-heartbeat.mjs +170 -0
  64. package/src/bridge-hub-protocol.mjs +198 -0
  65. package/src/bridge-hub-server.mjs +544 -0
  66. package/src/bridge-launch-agent.mjs +323 -323
  67. package/src/bridge-registrar.mjs +102 -102
  68. package/src/bridge-server.mjs +376 -376
  69. package/src/cli-help.mjs +308 -308
  70. package/src/cli-stdio.mjs +40 -40
  71. package/src/control-compiler.mjs +532 -532
  72. package/src/dag-chain.mjs +261 -261
  73. package/src/factory-diagnostics.mjs +188 -188
  74. package/src/git-process.mjs +72 -0
  75. package/src/hash-chain.mjs +76 -76
  76. package/src/hooks-cli.mjs +1057 -1057
  77. package/src/isolation-runner.mjs +409 -409
  78. package/src/node-version-guard.mjs +44 -44
  79. package/src/project-cli.mjs +3 -3
  80. package/src/project-identity.mjs +130 -130
  81. package/src/rc1-black-box-oracle.mjs +906 -906
  82. package/src/rc1-comparison.mjs +154 -154
  83. package/src/rc1-evidence-bundle.mjs +456 -456
  84. package/src/rc1-v4-campaign.mjs +708 -708
  85. package/src/rc1-v4-transform.mjs +467 -467
  86. package/src/rc1-v5-artifact-set.mjs +855 -855
  87. package/src/rc1-v5-behavior-evidence.mjs +594 -594
  88. package/src/rc1-v5-campaign.mjs +797 -797
  89. package/src/rc1-v5-transform.mjs +421 -421
  90. package/src/rc1-v6-artifact-set.mjs +807 -807
  91. package/src/rc1-v6-behavior-evidence.mjs +273 -273
  92. package/src/rc1-v6-campaign.mjs +623 -623
  93. package/src/rc1-v6-causal-binding.mjs +473 -473
  94. package/src/rc1-v6-measurement.mjs +313 -313
  95. package/src/rc2-artifact-set.mjs +1584 -1584
  96. package/src/rc2-campaign.mjs +1506 -1506
  97. package/src/rc2-delivery-policy-front-end.mjs +1079 -1079
  98. package/src/rc2-delivery-policy-oracle.mjs +134 -134
  99. package/src/rc2-delivery-policy-transform.mjs +1127 -1127
  100. package/src/rc2-rc1-transfer-front-end.mjs +511 -511
  101. package/src/rc3-actual-dogfood.mjs +652 -652
  102. package/src/rc3-dogfood-scaffold.mjs +315 -315
  103. package/src/rc3-scripted-campaign.mjs +1383 -1383
  104. package/src/rc4-stage1-dogfood.mjs +673 -673
  105. package/src/runtime-adapter-registry.mjs +524 -524
  106. package/src/runtime-cli.mjs +4588 -4588
  107. package/src/runtime-contracts.mjs +824 -824
  108. package/src/runtime-control-store.mjs +604 -604
  109. package/src/runtime-controller-protocol.mjs +587 -587
  110. package/src/runtime-decision-verifier.mjs +701 -701
  111. package/src/runtime-diff-observer.mjs +361 -361
  112. package/src/runtime-direct-os-observer.mjs +301 -301
  113. package/src/runtime-driver-state.mjs +166 -166
  114. package/src/runtime-engine.mjs +779 -779
  115. package/src/runtime-errors.mjs +356 -356
  116. package/src/runtime-event-store.mjs +189 -189
  117. package/src/runtime-front-end.mjs +925 -925
  118. package/src/runtime-gate-store.mjs +481 -481
  119. package/src/runtime-hold-recompile.mjs +916 -916
  120. package/src/runtime-io-sentinel.mjs +391 -391
  121. package/src/runtime-lifecycle-lock.mjs +294 -294
  122. package/src/runtime-managed-supervisor.mjs +1490 -1490
  123. package/src/runtime-multi-epoch-store.mjs +838 -838
  124. package/src/runtime-projection.mjs +269 -269
  125. package/src/runtime-pull-intake.mjs +1192 -1192
  126. package/src/runtime-scripted-adapter-controller.mjs +1160 -1160
  127. package/src/runtime-scripted-executor.mjs +163 -163
  128. package/src/runtime-scripted-worktree.mjs +104 -104
  129. package/src/runtime-seam-resolve.mjs +428 -428
  130. package/src/runtime-seam-treatment.mjs +173 -173
  131. package/src/runtime-socket-owner.mjs +125 -125
  132. package/src/runtime-work-order-contracts.mjs +91 -91
  133. package/src/runtime-work-order-controller.mjs +1171 -1171
  134. package/src/runtime-worktree-executor.mjs +199 -199
  135. package/src/schedulability-compiler-v2.mjs +303 -303
  136. package/src/schedulability-verifier-v2.mjs +317 -317
  137. package/src/seam-apply.mjs +549 -549
  138. package/src/seam-commit-shared.mjs +22 -22
  139. package/src/seam-commit-transform.mjs +81 -81
  140. package/src/seam-commit.mjs +18 -18
  141. package/src/seam-cost.mjs +322 -322
  142. package/src/seam-derivation.mjs +188 -188
  143. package/src/seam-gate.mjs +146 -146
  144. package/src/seam-proposal-contracts.mjs +446 -446
  145. package/src/seam-proposal-queries.mjs +521 -521
  146. package/src/seam-proposal.mjs +2011 -2011
  147. package/src/seam-ref.mjs +33 -33
  148. package/src/seam-rewrite.mjs +286 -286
  149. package/src/seam-transform.mjs +554 -554
  150. package/src/seam-verification.mjs +260 -260
  151. package/src/sensor-adapter.mjs +432 -432
  152. package/src/sensor-cli.mjs +139 -139
  153. package/src/sensor-diff.mjs +661 -661
  154. package/src/sensor-node-runtime.mjs +53 -53
  155. package/src/sensor-runtime.mjs +52 -52
  156. package/src/timestamp-contract.mjs +8 -8
  157. package/src/todo-audit-pending.mjs +91 -91
  158. package/src/todo-chain.mjs +178 -178
  159. package/src/todo-cli.mjs +8 -8
  160. package/src/todo-contracts.mjs +728 -728
  161. package/src/todo-dashboard-registry.mjs +573 -573
  162. package/src/todo-dispatch-shape.mjs +190 -190
  163. package/src/todo-gantt-html-independence.mjs +239 -239
  164. package/src/todo-gantt-html-shared.mjs +226 -226
  165. package/src/todo-gantt-html-style.mjs +131 -131
  166. package/src/todo-gantt-html.mjs +248 -248
  167. package/src/todo-gantt-layout.mjs +974 -974
  168. package/src/todo-gantt-live.mjs +361 -361
  169. package/src/todo-gantt-nested.mjs +263 -263
  170. package/src/todo-gantt-presentation.mjs +217 -217
  171. package/src/todo-gantt-scope.mjs +123 -123
  172. package/src/todo-gantt-svg.mjs +353 -353
  173. package/src/todo-independence-contracts.mjs +595 -595
  174. package/src/todo-independence-guidance.mjs +322 -322
  175. package/src/todo-independence.mjs +640 -640
  176. package/src/todo-markdown-renderer.mjs +260 -260
  177. package/src/todo-migration.mjs +448 -448
  178. package/src/todo-narrative-anchor.mjs +130 -130
  179. package/src/todo-note-store.mjs +629 -629
  180. package/src/todo-parallel-candidates.mjs +114 -114
  181. package/src/todo-revision.mjs +995 -995
  182. package/src/todo-split.mjs +472 -472
  183. package/src/todo-status.mjs +690 -690
  184. package/src/todo-store-git-transaction.mjs +418 -418
  185. package/src/todo-store.mjs +42 -21
  186. package/src/treatment-compiler.mjs +728 -728
  187. package/src/treatment-runner.mjs +656 -656
  188. package/src/witness-scaffold.mjs +180 -180
@@ -265,20 +265,20 @@ class QueryBuilder {
265
265
  */
266
266
  insertNode(node) {
267
267
  if (!this.stmts.insertNode) {
268
- this.stmts.insertNode = this.db.prepare(`
269
- INSERT OR REPLACE INTO nodes (
270
- id, kind, name, qualified_name, file_path, language,
271
- start_line, end_line, start_column, end_column,
272
- docstring, signature, visibility,
273
- is_exported, is_async, is_static, is_abstract,
274
- decorators, type_parameters, return_type, extent_start_line, updated_at
275
- ) VALUES (
276
- @id, @kind, @name, @qualifiedName, @filePath, @language,
277
- @startLine, @endLine, @startColumn, @endColumn,
278
- @docstring, @signature, @visibility,
279
- @isExported, @isAsync, @isStatic, @isAbstract,
280
- @decorators, @typeParameters, @returnType, @extentStartLine, @updatedAt
281
- )
268
+ this.stmts.insertNode = this.db.prepare(`
269
+ INSERT OR REPLACE INTO nodes (
270
+ id, kind, name, qualified_name, file_path, language,
271
+ start_line, end_line, start_column, end_column,
272
+ docstring, signature, visibility,
273
+ is_exported, is_async, is_static, is_abstract,
274
+ decorators, type_parameters, return_type, extent_start_line, updated_at
275
+ ) VALUES (
276
+ @id, @kind, @name, @qualifiedName, @filePath, @language,
277
+ @startLine, @endLine, @startColumn, @endColumn,
278
+ @docstring, @signature, @visibility,
279
+ @isExported, @isAsync, @isStatic, @isAbstract,
280
+ @decorators, @typeParameters, @returnType, @extentStartLine, @updatedAt
281
+ )
282
282
  `);
283
283
  }
284
284
  // Validate required fields to prevent SQLite bind errors
@@ -396,12 +396,12 @@ class QueryBuilder {
396
396
  if (this.isSegmentableKind(node.kind))
397
397
  this.collectNameSegmentRows(node.name, segmentRows);
398
398
  }
399
- this.runBatched('insertNodes', `INSERT OR REPLACE INTO nodes (
400
- id, kind, name, qualified_name, file_path, language,
401
- start_line, end_line, start_column, end_column,
402
- docstring, signature, visibility,
403
- is_exported, is_async, is_static, is_abstract,
404
- decorators, type_parameters, return_type, extent_start_line, updated_at
399
+ this.runBatched('insertNodes', `INSERT OR REPLACE INTO nodes (
400
+ id, kind, name, qualified_name, file_path, language,
401
+ start_line, end_line, start_column, end_column,
402
+ docstring, signature, visibility,
403
+ is_exported, is_async, is_static, is_abstract,
404
+ decorators, type_parameters, return_type, extent_start_line, updated_at
405
405
  ) VALUES `, '(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)', rows);
406
406
  this.runBatched('insertNameSegments', 'INSERT OR IGNORE INTO name_segment_vocab (segment, name) VALUES ', '(?,?)', segmentRows);
407
407
  })();
@@ -460,30 +460,30 @@ class QueryBuilder {
460
460
  */
461
461
  updateNode(node) {
462
462
  if (!this.stmts.updateNode) {
463
- this.stmts.updateNode = this.db.prepare(`
464
- UPDATE nodes SET
465
- kind = @kind,
466
- name = @name,
467
- qualified_name = @qualifiedName,
468
- file_path = @filePath,
469
- language = @language,
470
- start_line = @startLine,
471
- end_line = @endLine,
472
- start_column = @startColumn,
473
- end_column = @endColumn,
474
- docstring = @docstring,
475
- signature = @signature,
476
- visibility = @visibility,
477
- is_exported = @isExported,
478
- is_async = @isAsync,
479
- is_static = @isStatic,
480
- is_abstract = @isAbstract,
481
- decorators = @decorators,
482
- type_parameters = @typeParameters,
483
- return_type = @returnType,
484
- extent_start_line = @extentStartLine,
485
- updated_at = @updatedAt
486
- WHERE id = @id
463
+ this.stmts.updateNode = this.db.prepare(`
464
+ UPDATE nodes SET
465
+ kind = @kind,
466
+ name = @name,
467
+ qualified_name = @qualifiedName,
468
+ file_path = @filePath,
469
+ language = @language,
470
+ start_line = @startLine,
471
+ end_line = @endLine,
472
+ start_column = @startColumn,
473
+ end_column = @endColumn,
474
+ docstring = @docstring,
475
+ signature = @signature,
476
+ visibility = @visibility,
477
+ is_exported = @isExported,
478
+ is_async = @isAsync,
479
+ is_static = @isStatic,
480
+ is_abstract = @isAbstract,
481
+ decorators = @decorators,
482
+ type_parameters = @typeParameters,
483
+ return_type = @returnType,
484
+ extent_start_line = @extentStartLine,
485
+ updated_at = @updatedAt
486
+ WHERE id = @id
487
487
  `);
488
488
  }
489
489
  // Invalidate cache before update
@@ -607,12 +607,12 @@ class QueryBuilder {
607
607
  const placeholders = variants.map(() => '?').join(', ');
608
608
  const whens = variants.map(() => 'WHEN ? THEN ?').join(' ');
609
609
  const rows = this.db
610
- .prepare(`SELECT name, COUNT(DISTINCT CASE segment ${whens} END) AS matches
611
- FROM name_segment_vocab
612
- WHERE segment IN (${placeholders})
613
- GROUP BY name
614
- HAVING matches >= ?
615
- ORDER BY matches DESC, length(name) ASC
610
+ .prepare(`SELECT name, COUNT(DISTINCT CASE segment ${whens} END) AS matches
611
+ FROM name_segment_vocab
612
+ WHERE segment IN (${placeholders})
613
+ GROUP BY name
614
+ HAVING matches >= ?
615
+ ORDER BY matches DESC, length(name) ASC
616
616
  LIMIT ?`)
617
617
  .all(...variants.flatMap((v) => [v.segment, v.word]), ...variants.map((v) => v.segment), minWords, limit);
618
618
  return rows;
@@ -624,7 +624,7 @@ class QueryBuilder {
624
624
  return new Map();
625
625
  const placeholders = segments.map(() => '?').join(', ');
626
626
  const rows = this.db
627
- .prepare(`SELECT segment, COUNT(*) AS n FROM name_segment_vocab
627
+ .prepare(`SELECT segment, COUNT(*) AS n FROM name_segment_vocab
628
628
  WHERE segment IN (${placeholders}) GROUP BY segment`)
629
629
  .all(...segments);
630
630
  return new Map(rows.map((r) => [r.segment, r.n]));
@@ -784,15 +784,15 @@ class QueryBuilder {
784
784
  // protobuf stub) outranks the real `server/etcdserver/server.go`
785
785
  // (470 edges) by 4×, and the boost would push the agent toward
786
786
  // generated code.
787
- this.stmts.getDominantFile = this.db.prepare(`
788
- SELECT n.file_path AS file_path, COUNT(*) AS edge_count
789
- FROM edges e
790
- JOIN nodes n ON e.source = n.id
791
- JOIN nodes m ON e.target = m.id
792
- WHERE n.file_path = m.file_path
793
- GROUP BY n.file_path
794
- ORDER BY edge_count DESC
795
- LIMIT 20
787
+ this.stmts.getDominantFile = this.db.prepare(`
788
+ SELECT n.file_path AS file_path, COUNT(*) AS edge_count
789
+ FROM edges e
790
+ JOIN nodes n ON e.source = n.id
791
+ JOIN nodes m ON e.target = m.id
792
+ WHERE n.file_path = m.file_path
793
+ GROUP BY n.file_path
794
+ ORDER BY edge_count DESC
795
+ LIMIT 20
796
796
  `);
797
797
  }
798
798
  const rows = this.stmts.getDominantFile.all();
@@ -819,13 +819,13 @@ class QueryBuilder {
819
819
  */
820
820
  getTopRouteFile() {
821
821
  if (!this.stmts.getTopRouteFile) {
822
- this.stmts.getTopRouteFile = this.db.prepare(`
823
- SELECT file_path, COUNT(*) AS cnt
824
- FROM nodes
825
- WHERE kind = 'route'
826
- GROUP BY file_path
827
- ORDER BY cnt DESC
828
- LIMIT 20
822
+ this.stmts.getTopRouteFile = this.db.prepare(`
823
+ SELECT file_path, COUNT(*) AS cnt
824
+ FROM nodes
825
+ WHERE kind = 'route'
826
+ GROUP BY file_path
827
+ ORDER BY cnt DESC
828
+ LIMIT 20
829
829
  `);
830
830
  }
831
831
  const rows = this.stmts.getTopRouteFile.all();
@@ -856,21 +856,21 @@ class QueryBuilder {
856
856
  // Edge kind varies across framework resolvers: Spring/Rails/
857
857
  // Laravel/Drupal emit `references`, Express emits `calls`. Accept
858
858
  // both — the semantic is the same (route → its handler).
859
- this.stmts.getRoutingManifest = this.db.prepare(`
860
- SELECT
861
- r.name AS url,
862
- h.name AS handler,
863
- h.file_path AS handler_file,
864
- h.start_line AS handler_line,
865
- h.kind AS handler_kind
866
- FROM nodes r
867
- JOIN edges e ON e.source = r.id
868
- JOIN nodes h ON e.target = h.id
869
- WHERE r.kind = 'route'
870
- AND e.kind IN ('references', 'calls')
871
- AND h.kind IN ('function', 'method', 'class')
872
- ORDER BY r.file_path, r.start_line
873
- LIMIT ?
859
+ this.stmts.getRoutingManifest = this.db.prepare(`
860
+ SELECT
861
+ r.name AS url,
862
+ h.name AS handler,
863
+ h.file_path AS handler_file,
864
+ h.start_line AS handler_line,
865
+ h.kind AS handler_kind
866
+ FROM nodes r
867
+ JOIN edges e ON e.source = r.id
868
+ JOIN nodes h ON e.target = h.id
869
+ WHERE r.kind = 'route'
870
+ AND e.kind IN ('references', 'calls')
871
+ AND h.kind IN ('function', 'method', 'class')
872
+ ORDER BY r.file_path, r.start_line
873
+ LIMIT ?
874
874
  `);
875
875
  }
876
876
  const rows = this.stmts.getRoutingManifest.all(limit);
@@ -1234,11 +1234,11 @@ class QueryBuilder {
1234
1234
  // Fetch 5x requested limit so post-hoc rescoring (kindBonus, pathRelevance,
1235
1235
  // nameMatchBonus) can promote results that BM25 alone undervalues.
1236
1236
  const ftsLimit = Math.max(limit * 5, 100);
1237
- let sql = `
1238
- SELECT nodes.*, bm25(nodes_fts, 0, 20, 5, 1, 2) as score
1239
- FROM nodes_fts
1240
- JOIN nodes ON nodes_fts.id = nodes.id
1241
- WHERE nodes_fts MATCH ?
1237
+ let sql = `
1238
+ SELECT nodes.*, bm25(nodes_fts, 0, 20, 5, 1, 2) as score
1239
+ FROM nodes_fts
1240
+ JOIN nodes ON nodes_fts.id = nodes.id
1241
+ WHERE nodes_fts MATCH ?
1242
1242
  `;
1243
1243
  const params = [ftsQuery];
1244
1244
  if (kinds && kinds.length > 0) {
@@ -1269,21 +1269,21 @@ class QueryBuilder {
1269
1269
  */
1270
1270
  searchNodesLike(query, options) {
1271
1271
  const { kinds, languages, limit = 100, offset = 0 } = options;
1272
- let sql = `
1273
- SELECT nodes.*,
1274
- CASE
1275
- WHEN name = ? THEN 1.0
1276
- WHEN name LIKE ? THEN 0.9
1277
- WHEN name LIKE ? THEN 0.8
1278
- WHEN qualified_name LIKE ? THEN 0.7
1279
- ELSE 0.5
1280
- END as score
1281
- FROM nodes
1282
- WHERE (
1283
- name LIKE ? OR
1284
- qualified_name LIKE ? OR
1285
- name LIKE ?
1286
- )
1272
+ let sql = `
1273
+ SELECT nodes.*,
1274
+ CASE
1275
+ WHEN name = ? THEN 1.0
1276
+ WHEN name LIKE ? THEN 0.9
1277
+ WHEN name LIKE ? THEN 0.8
1278
+ WHEN qualified_name LIKE ? THEN 0.7
1279
+ ELSE 0.5
1280
+ END as score
1281
+ FROM nodes
1282
+ WHERE (
1283
+ name LIKE ? OR
1284
+ qualified_name LIKE ? OR
1285
+ name LIKE ?
1286
+ )
1287
1287
  `;
1288
1288
  // Pattern variants for better matching
1289
1289
  const exactMatch = query;
@@ -1357,10 +1357,10 @@ class QueryBuilder {
1357
1357
  const allResults = [];
1358
1358
  const seenIds = new Set();
1359
1359
  for (const name of names) {
1360
- let sql = `
1361
- SELECT nodes.*, 1.0 as score
1362
- FROM nodes
1363
- WHERE name COLLATE NOCASE = ?
1360
+ let sql = `
1361
+ SELECT nodes.*, 1.0 as score
1362
+ FROM nodes
1363
+ WHERE name COLLATE NOCASE = ?
1364
1364
  `;
1365
1365
  const params = [name];
1366
1366
  if (kinds && kinds.length > 0) {
@@ -1404,10 +1404,10 @@ class QueryBuilder {
1404
1404
  */
1405
1405
  findNodesByNameSubstring(substring, options = {}) {
1406
1406
  const { kinds, languages, limit = 30, excludePrefix } = options;
1407
- let sql = `
1408
- SELECT nodes.*, 1.0 as score
1409
- FROM nodes
1410
- WHERE name LIKE ?
1407
+ let sql = `
1408
+ SELECT nodes.*, 1.0 as score
1409
+ FROM nodes
1410
+ WHERE name LIKE ?
1411
1411
  `;
1412
1412
  const params = [`%${substring}%`];
1413
1413
  // Exclude prefix matches (handled by FTS-based prefix search in Step 2b)
@@ -1439,9 +1439,9 @@ class QueryBuilder {
1439
1439
  */
1440
1440
  insertEdge(edge) {
1441
1441
  if (!this.stmts.insertEdge) {
1442
- this.stmts.insertEdge = this.db.prepare(`
1443
- INSERT OR IGNORE INTO edges (source, target, kind, metadata, line, col, provenance, confidence, resolved_by)
1444
- VALUES (@source, @target, @kind, @metadata, @line, @col, @provenance, @confidence, @resolvedBy)
1442
+ this.stmts.insertEdge = this.db.prepare(`
1443
+ INSERT OR IGNORE INTO edges (source, target, kind, metadata, line, col, provenance, confidence, resolved_by)
1444
+ VALUES (@source, @target, @kind, @metadata, @line, @col, @provenance, @confidence, @resolvedBy)
1445
1445
  `);
1446
1446
  }
1447
1447
  this.stmts.insertEdge.run({
@@ -1571,18 +1571,18 @@ class QueryBuilder {
1571
1571
  * it. One indexed query (idx_nodes_file_path + idx_edges_target_kind).
1572
1572
  */
1573
1573
  getDependentFilePaths(filePath) {
1574
- const sql = `SELECT DISTINCT src.file_path AS fp
1575
- FROM edges e
1576
- JOIN nodes tgt ON tgt.id = e.target
1577
- JOIN nodes src ON src.id = e.source
1578
- WHERE tgt.file_path = ?
1579
- AND e.kind != 'contains'
1580
- AND src.file_path != ?
1581
- AND (
1582
- e.kind = 'imports'
1583
- OR src.language NOT IN (${UNCORROBORATED_FILTER_LANGUAGES_SQL_LIST})
1584
- OR e.resolved_by IS NULL
1585
- OR e.resolved_by NOT IN (${UNCORROBORATED_RESOLVED_BY_SQL_LIST})
1574
+ const sql = `SELECT DISTINCT src.file_path AS fp
1575
+ FROM edges e
1576
+ JOIN nodes tgt ON tgt.id = e.target
1577
+ JOIN nodes src ON src.id = e.source
1578
+ WHERE tgt.file_path = ?
1579
+ AND e.kind != 'contains'
1580
+ AND src.file_path != ?
1581
+ AND (
1582
+ e.kind = 'imports'
1583
+ OR src.language NOT IN (${UNCORROBORATED_FILTER_LANGUAGES_SQL_LIST})
1584
+ OR e.resolved_by IS NULL
1585
+ OR e.resolved_by NOT IN (${UNCORROBORATED_RESOLVED_BY_SQL_LIST})
1586
1586
  )`;
1587
1587
  const rows = this.db.prepare(sql).all(filePath, filePath);
1588
1588
  return rows.map((r) => r.fp);
@@ -1594,18 +1594,18 @@ class QueryBuilder {
1594
1594
  * (all kinds except `contains`); same reason imports-only is insufficient.
1595
1595
  */
1596
1596
  getDependencyFilePaths(filePath) {
1597
- const sql = `SELECT DISTINCT tgt.file_path AS fp
1598
- FROM edges e
1599
- JOIN nodes src ON src.id = e.source
1600
- JOIN nodes tgt ON tgt.id = e.target
1601
- WHERE src.file_path = ?
1602
- AND e.kind != 'contains'
1603
- AND tgt.file_path != ?
1604
- AND (
1605
- e.kind = 'imports'
1606
- OR src.language NOT IN (${UNCORROBORATED_FILTER_LANGUAGES_SQL_LIST})
1607
- OR e.resolved_by IS NULL
1608
- OR e.resolved_by NOT IN (${UNCORROBORATED_RESOLVED_BY_SQL_LIST})
1597
+ const sql = `SELECT DISTINCT tgt.file_path AS fp
1598
+ FROM edges e
1599
+ JOIN nodes src ON src.id = e.source
1600
+ JOIN nodes tgt ON tgt.id = e.target
1601
+ WHERE src.file_path = ?
1602
+ AND e.kind != 'contains'
1603
+ AND tgt.file_path != ?
1604
+ AND (
1605
+ e.kind = 'imports'
1606
+ OR src.language NOT IN (${UNCORROBORATED_FILTER_LANGUAGES_SQL_LIST})
1607
+ OR e.resolved_by IS NULL
1608
+ OR e.resolved_by NOT IN (${UNCORROBORATED_RESOLVED_BY_SQL_LIST})
1609
1609
  )`;
1610
1610
  const rows = this.db.prepare(sql).all(filePath, filePath);
1611
1611
  return rows.map((r) => r.fp);
@@ -1621,13 +1621,13 @@ class QueryBuilder {
1621
1621
  * {@link getDependentFilePaths}: all kinds except `contains`.
1622
1622
  */
1623
1623
  getCrossFileIncomingEdgesWithTarget(filePath) {
1624
- const sql = `SELECT e.*, tgt.name AS target_name, tgt.kind AS target_kind,
1625
- src.file_path AS source_file_path, src.language AS source_language
1626
- FROM edges e
1627
- JOIN nodes tgt ON tgt.id = e.target
1628
- JOIN nodes src ON src.id = e.source
1629
- WHERE tgt.file_path = ?
1630
- AND e.kind != 'contains'
1624
+ const sql = `SELECT e.*, tgt.name AS target_name, tgt.kind AS target_kind,
1625
+ src.file_path AS source_file_path, src.language AS source_language
1626
+ FROM edges e
1627
+ JOIN nodes tgt ON tgt.id = e.target
1628
+ JOIN nodes src ON src.id = e.source
1629
+ WHERE tgt.file_path = ?
1630
+ AND e.kind != 'contains'
1631
1631
  AND src.file_path != ?`;
1632
1632
  const rows = this.db.prepare(sql).all(filePath, filePath);
1633
1633
  return rows.map(row => ({
@@ -1646,18 +1646,18 @@ class QueryBuilder {
1646
1646
  */
1647
1647
  upsertFile(file) {
1648
1648
  if (!this.stmts.upsertFile) {
1649
- this.stmts.upsertFile = this.db.prepare(`
1650
- INSERT INTO files (path, content_hash, language, size, modified_at, indexed_at, node_count, extraction_version, errors)
1651
- VALUES (@path, @contentHash, @language, @size, @modifiedAt, @indexedAt, @nodeCount, @extractionVersion, @errors)
1652
- ON CONFLICT(path) DO UPDATE SET
1653
- content_hash = @contentHash,
1654
- language = @language,
1655
- size = @size,
1656
- modified_at = @modifiedAt,
1657
- indexed_at = @indexedAt,
1658
- node_count = @nodeCount,
1659
- extraction_version = @extractionVersion,
1660
- errors = @errors
1649
+ this.stmts.upsertFile = this.db.prepare(`
1650
+ INSERT INTO files (path, content_hash, language, size, modified_at, indexed_at, node_count, extraction_version, errors)
1651
+ VALUES (@path, @contentHash, @language, @size, @modifiedAt, @indexedAt, @nodeCount, @extractionVersion, @errors)
1652
+ ON CONFLICT(path) DO UPDATE SET
1653
+ content_hash = @contentHash,
1654
+ language = @language,
1655
+ size = @size,
1656
+ modified_at = @modifiedAt,
1657
+ indexed_at = @indexedAt,
1658
+ node_count = @nodeCount,
1659
+ extraction_version = @extractionVersion,
1660
+ errors = @errors
1661
1661
  `);
1662
1662
  }
1663
1663
  this.stmts.upsertFile.run({
@@ -1771,9 +1771,9 @@ class QueryBuilder {
1771
1771
  */
1772
1772
  insertUnresolvedRef(ref) {
1773
1773
  if (!this.stmts.insertUnresolved) {
1774
- this.stmts.insertUnresolved = this.db.prepare(`
1775
- INSERT INTO unresolved_refs (from_node_id, reference_name, reference_kind, line, col, candidates, file_path, language, binding_form, imported_name)
1776
- VALUES (@fromNodeId, @referenceName, @referenceKind, @line, @col, @candidates, @filePath, @language, @bindingForm, @importedName)
1774
+ this.stmts.insertUnresolved = this.db.prepare(`
1775
+ INSERT INTO unresolved_refs (from_node_id, reference_name, reference_kind, line, col, candidates, file_path, language, binding_form, imported_name)
1776
+ VALUES (@fromNodeId, @referenceName, @referenceKind, @line, @col, @candidates, @filePath, @language, @bindingForm, @importedName)
1777
1777
  `);
1778
1778
  }
1779
1779
  this.stmts.insertUnresolved.run({
@@ -2246,11 +2246,11 @@ class QueryBuilder {
2246
2246
  */
2247
2247
  getStats() {
2248
2248
  // Single query for all three aggregate counts
2249
- const counts = this.db.prepare(`
2250
- SELECT
2251
- (SELECT COUNT(*) FROM nodes) AS node_count,
2252
- (SELECT COUNT(*) FROM edges) AS edge_count,
2253
- (SELECT COUNT(*) FROM files) AS file_count
2249
+ const counts = this.db.prepare(`
2250
+ SELECT
2251
+ (SELECT COUNT(*) FROM nodes) AS node_count,
2252
+ (SELECT COUNT(*) FROM edges) AS edge_count,
2253
+ (SELECT COUNT(*) FROM files) AS file_count
2254
2254
  `).get();
2255
2255
  const nodesByKind = {};
2256
2256
  const nodeKindRows = this.db