@backbencher/brain 0.0.0-stage → 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 (319) hide show
  1. package/CHANGELOG.md +298 -0
  2. package/EMBEDDINGS.md +355 -0
  3. package/LICENSE +21 -0
  4. package/README.md +415 -2
  5. package/bin/bb +13 -0
  6. package/dist/analysis-exclusions-7ZJDXOVZ.js +35 -0
  7. package/dist/brain-index-health-KY7K552E.js +17 -0
  8. package/dist/brain-relationship-enrichment-QH7OOHUF.js +558 -0
  9. package/dist/brain.sql-EIM4WS4D.js +29 -0
  10. package/dist/chunk-2BSKD7O3.js +40 -0
  11. package/dist/chunk-2DE27DVC.js +1314 -0
  12. package/dist/chunk-2RRBMNBQ.js +322 -0
  13. package/dist/chunk-2RTN2DFU.js +711 -0
  14. package/dist/chunk-2WXYNLBA.js +29 -0
  15. package/dist/chunk-3CTFIJJB.js +17 -0
  16. package/dist/chunk-3LFTSMIV.js +174 -0
  17. package/dist/chunk-47HKX4AJ.js +930 -0
  18. package/dist/chunk-4H22TLOT.js +543 -0
  19. package/dist/chunk-5SD45FLC.js +818 -0
  20. package/dist/chunk-66A5ZFYA.js +1758 -0
  21. package/dist/chunk-6AIRWAO2.js +247 -0
  22. package/dist/chunk-6WNGD3NN.js +196 -0
  23. package/dist/chunk-7AN7U5IZ.js +371 -0
  24. package/dist/chunk-BHB7BYYI.js +626 -0
  25. package/dist/chunk-BQ3OVQ3P.js +125 -0
  26. package/dist/chunk-CW4XXSNN.js +1479 -0
  27. package/dist/chunk-D2Q4Y47A.js +53 -0
  28. package/dist/chunk-DIYAGQJL.js +34 -0
  29. package/dist/chunk-DYM4FQEE.js +90 -0
  30. package/dist/chunk-EWMWK7WN.js +75 -0
  31. package/dist/chunk-FVVQTSIM.js +3427 -0
  32. package/dist/chunk-FWSL4VG6.js +1290 -0
  33. package/dist/chunk-GBI7V7KY.js +69 -0
  34. package/dist/chunk-GEFFO6O2.js +152 -0
  35. package/dist/chunk-HOWAQXJO.js +236 -0
  36. package/dist/chunk-HQSPJDHI.js +613 -0
  37. package/dist/chunk-IFZNR5UJ.js +2863 -0
  38. package/dist/chunk-J4PMBXQQ.js +336 -0
  39. package/dist/chunk-KAET47ZL.js +125 -0
  40. package/dist/chunk-KCDECRJY.js +440 -0
  41. package/dist/chunk-KLK3PBTO.js +1119 -0
  42. package/dist/chunk-L5TDYKYK.js +76 -0
  43. package/dist/chunk-MLKGABMK.js +9 -0
  44. package/dist/chunk-OWISBAOF.js +61 -0
  45. package/dist/chunk-PJTNGD3Z.js +6924 -0
  46. package/dist/chunk-QLK6ED4Q.js +235 -0
  47. package/dist/chunk-R2GW4F6R.js +19 -0
  48. package/dist/chunk-RCMTFSEF.js +87 -0
  49. package/dist/chunk-RH7KC5HU.js +12050 -0
  50. package/dist/chunk-RM3PY6TG.js +376 -0
  51. package/dist/chunk-ROMM6KMC.js +433 -0
  52. package/dist/chunk-SK6YVHFZ.js +211 -0
  53. package/dist/chunk-SM7RFQRO.js +214 -0
  54. package/dist/chunk-UFE77L67.js +782 -0
  55. package/dist/chunk-UGR4QIGS.js +67 -0
  56. package/dist/chunk-UUNHHX4M.js +13 -0
  57. package/dist/chunk-V44PGPA6.js +2104 -0
  58. package/dist/chunk-VL52PL2T.js +71 -0
  59. package/dist/chunk-VURYFIHW.js +201 -0
  60. package/dist/chunk-XJKJYIPP.js +7436 -0
  61. package/dist/chunk-YI6FF2ZR.js +340 -0
  62. package/dist/cli/index.js +37 -0
  63. package/dist/config-HZAAYDP3.js +10 -0
  64. package/dist/content-search-642F7E5V.js +593 -0
  65. package/dist/database-TCTZDLE5.js +9 -0
  66. package/dist/doctor-GXCKSJOX.js +395 -0
  67. package/dist/edit-engine-KM7MPFEC.js +51 -0
  68. package/dist/engine-42TZFNIZ.js +11 -0
  69. package/dist/engine-LO6L4JLQ.js +18 -0
  70. package/dist/fingerprint-XDXFRWO6.js +33 -0
  71. package/dist/framework-intelligence-DWFCKV44.js +860 -0
  72. package/dist/freshness-3K6VU5OT.js +174 -0
  73. package/dist/graph-engine-PL5YA7BA.js +21 -0
  74. package/dist/graph_request-MM4REP6H.js +350 -0
  75. package/dist/impact_request-WDE47IZ5.js +68 -0
  76. package/dist/index-space-IUUN5WYV.js +15 -0
  77. package/dist/index-status-6BD7UBHR.js +34 -0
  78. package/dist/index.js +159 -0
  79. package/dist/instance-FO6QZRLH.js +19 -0
  80. package/dist/language-detection-DFCAU5DZ.js +11 -0
  81. package/dist/lsp/server.js +4301 -0
  82. package/dist/mcp/server.js +132 -0
  83. package/dist/onboard-DIQZV2NW.js +161 -0
  84. package/dist/override-companion-pass-XR7MHTP7.js +117 -0
  85. package/dist/parallel-indexer-4PW3AFXP.js +88 -0
  86. package/dist/pool-worker.js +1153 -0
  87. package/dist/project-health-ZS5TS2Z6.js +94 -0
  88. package/dist/project-index-5SDPHITE.js +57 -0
  89. package/dist/project-registry-DGZBIZS7.js +355 -0
  90. package/dist/real-bash-analyzer-SXSV67E2.js +2201 -0
  91. package/dist/real-c-analyzer-SY7FTZ6W.js +5942 -0
  92. package/dist/real-clean-analyzer-IGDHHLZK.js +3470 -0
  93. package/dist/real-clojure-analyzer-4OH7IWLB.js +2818 -0
  94. package/dist/real-cpp-analyzer-7LTDRJRJ.js +4101 -0
  95. package/dist/real-csharp-analyzer-KICU3TLH.js +3298 -0
  96. package/dist/real-css-analyzer-HPU277Q6.js +15 -0
  97. package/dist/real-dart-analyzer-7LZ5LCMW.js +3944 -0
  98. package/dist/real-elixir-analyzer-BE3MEVZO.js +2192 -0
  99. package/dist/real-erlang-analyzer-BZMAKYBK.js +1731 -0
  100. package/dist/real-fsharp-analyzer-5QLTOV3E.js +2390 -0
  101. package/dist/real-go-analyzer-POD7MZQR.js +4126 -0
  102. package/dist/real-graphql-analyzer-MAQRZ6QW.js +1442 -0
  103. package/dist/real-groovy-analyzer-MEWSUPMJ.js +2285 -0
  104. package/dist/real-haskell-analyzer-I5L2GR3Y.js +2533 -0
  105. package/dist/real-hcl-analyzer-DBOPXSWJ.js +725 -0
  106. package/dist/real-html-analyzer-O63XZLZD.js +1759 -0
  107. package/dist/real-java-analyzer-CHCW3EKA.js +4677 -0
  108. package/dist/real-javascript-analyzer-QAAJQYZE.js +14 -0
  109. package/dist/real-json-analyzer-UGBR7YQN.js +1640 -0
  110. package/dist/real-julia-analyzer-35Q4GHOZ.js +2085 -0
  111. package/dist/real-kotlin-analyzer-GRAYCWW2.js +3956 -0
  112. package/dist/real-lua-analyzer-5BAPDZDF.js +1957 -0
  113. package/dist/real-makefile-analyzer-YJKAHNAS.js +920 -0
  114. package/dist/real-markdown-analyzer-UKUQZO4F.js +1215 -0
  115. package/dist/real-matlab-analyzer-FVIKQZKG.js +480 -0
  116. package/dist/real-nim-analyzer-M7FAIABG.js +3603 -0
  117. package/dist/real-objective-c-analyzer-XMYQIGQI.js +1518 -0
  118. package/dist/real-perl-analyzer-ZLY6M2DZ.js +1865 -0
  119. package/dist/real-php-analyzer-KI2ZAM7X.js +9368 -0
  120. package/dist/real-powershell-analyzer-B3T5ZP6U.js +2915 -0
  121. package/dist/real-protobuf-analyzer-23OZ75XV.js +867 -0
  122. package/dist/real-python-analyzer-I4TIUB6D.js +4880 -0
  123. package/dist/real-r-analyzer-FJZJXXS6.js +2596 -0
  124. package/dist/real-ruby-analyzer-M3UFIVTQ.js +2429 -0
  125. package/dist/real-rust-analyzer-LVSONPAN.js +3359 -0
  126. package/dist/real-scala-analyzer-CWPI33Q2.js +3293 -0
  127. package/dist/real-scss-analyzer-TGRJQ7TH.js +1515 -0
  128. package/dist/real-solidity-analyzer-4P2VBVWA.js +2608 -0
  129. package/dist/real-sql-analyzer-J7DYZOZR.js +3840 -0
  130. package/dist/real-svelte-analyzer-NHEN2SX5.js +2532 -0
  131. package/dist/real-swift-analyzer-2NSYULWI.js +2311 -0
  132. package/dist/real-toml-analyzer-L3C66L7Q.js +929 -0
  133. package/dist/real-typescript-analyzer-FTC6ETRQ.js +14 -0
  134. package/dist/real-vb-analyzer-ORY7LAQS.js +3145 -0
  135. package/dist/real-vba-analyzer-JH2TTUFI.js +466 -0
  136. package/dist/real-vue-analyzer-OO4FUGBV.js +2680 -0
  137. package/dist/real-xml-analyzer-6CQVR477.js +2405 -0
  138. package/dist/real-yaml-analyzer-J6MQG3YX.js +3528 -0
  139. package/dist/real-zig-analyzer-EFKCPJNU.js +2430 -0
  140. package/dist/regex-search-OPTLKUN5.js +239 -0
  141. package/dist/rules-I453JTRC.js +152 -0
  142. package/dist/scoped-path-MJYWJZQX.js +11 -0
  143. package/dist/setup-show-Y5GB5KXA.js +15 -0
  144. package/dist/setup-wizard-G5YYR5T6.js +24 -0
  145. package/dist/symbol-search-KB4MU4SI.js +16 -0
  146. package/dist/taint-YOTN5X55.js +202 -0
  147. package/dist/taint-analyzer-N7YLZU3N.js +163 -0
  148. package/dist/tool-handlers-G4NAGJIH.js +72 -0
  149. package/dist/tool-registry-BPEJNSZG.js +17 -0
  150. package/dist/tool-validation-RF675A3C.js +32 -0
  151. package/dist/type-inference-4Y7WTMFE.js +13 -0
  152. package/dist/vector-binary-LMY4QGAN.js +19 -0
  153. package/dist/version-LXGNFO37.js +9 -0
  154. package/dist/wasm-NMWO3WXY.js +16 -0
  155. package/docs/EMBEDDINGS.md +132 -0
  156. package/docs/README.md +178 -0
  157. package/docs/data/m10-baseline.json +602 -0
  158. package/docs/data/m10-benchmark-baseline.json +57 -0
  159. package/docs/data/m10-benchmark-last-run.json +127 -0
  160. package/docs/data/m10-final-audit.json +215 -0
  161. package/docs/data/m12-final-audit.json +138 -0
  162. package/docs/data/m12-surface-matrix.json +50 -0
  163. package/docs/data/public-tools.json +1902 -0
  164. package/docs/indexing/DEAD-ASSETS.md +88 -0
  165. package/docs/indexing/INDEXER-REVIEW.md +634 -0
  166. package/docs/indexing/PERFORMANCE-FINDINGS.md +106 -0
  167. package/docs/indexing/README.md +427 -0
  168. package/docs/indexing/discovery-matrix.md +41 -0
  169. package/docs/indexing/embeddings.md +325 -0
  170. package/docs/indexing/environment-variables.md +77 -0
  171. package/docs/indexing/performance.md +323 -0
  172. package/docs/indexing/resume.md +345 -0
  173. package/docs/indexing/troubleshooting.md +391 -0
  174. package/docs/man/bb.1 +190 -0
  175. package/docs/tools/bb_analyze.md +147 -0
  176. package/docs/tools/bb_annotate.md +110 -0
  177. package/docs/tools/bb_annotations.md +106 -0
  178. package/docs/tools/bb_ask.md +101 -0
  179. package/docs/tools/bb_cross_language.md +103 -0
  180. package/docs/tools/bb_doc.md +169 -0
  181. package/docs/tools/bb_edit.md +185 -0
  182. package/docs/tools/bb_graph.md +169 -0
  183. package/docs/tools/bb_health.md +156 -0
  184. package/docs/tools/bb_impact.md +117 -0
  185. package/docs/tools/bb_index.md +108 -0
  186. package/docs/tools/bb_migrate.md +135 -0
  187. package/docs/tools/bb_patterns.md +140 -0
  188. package/docs/tools/bb_search.md +150 -0
  189. package/docs/tools/bb_security.md +100 -0
  190. package/docs/tools/bb_visualize.md +184 -0
  191. package/grammars/README.md +57 -0
  192. package/grammars/THIRD-PARTY-NOTICES.md +50 -0
  193. package/grammars/tree-sitter-c/LICENSE +21 -0
  194. package/grammars/tree-sitter-clojure/LICENSE.md +21 -0
  195. package/grammars/tree-sitter-cpp/LICENSE +21 -0
  196. package/grammars/tree-sitter-csharp/LICENSE +21 -0
  197. package/grammars/tree-sitter-css/LICENSE +21 -0
  198. package/grammars/tree-sitter-css/README.md +21 -0
  199. package/grammars/tree-sitter-dart/LICENSE +20 -0
  200. package/grammars/tree-sitter-dart-upstream/LICENSE +20 -0
  201. package/grammars/tree-sitter-dart-upstream/README.md +18 -0
  202. package/grammars/tree-sitter-elixir/LICENSE +176 -0
  203. package/grammars/tree-sitter-elixir/README.md +11 -0
  204. package/grammars/tree-sitter-fsharp/LICENSE +21 -0
  205. package/grammars/tree-sitter-fsharp/README.md +45 -0
  206. package/grammars/tree-sitter-graphql/LICENSE +21 -0
  207. package/grammars/tree-sitter-graphql/README.md +106 -0
  208. package/grammars/tree-sitter-groovy/LICENSE +21 -0
  209. package/grammars/tree-sitter-groovy/README.md +4 -0
  210. package/grammars/tree-sitter-haskell/LICENSE +21 -0
  211. package/grammars/tree-sitter-haskell/README.md +394 -0
  212. package/grammars/tree-sitter-hcl/CHANGELOG.md +102 -0
  213. package/grammars/tree-sitter-hcl/LICENSE +201 -0
  214. package/grammars/tree-sitter-hcl/README.md +39 -0
  215. package/grammars/tree-sitter-html/LICENSE +21 -0
  216. package/grammars/tree-sitter-html/README.md +21 -0
  217. package/grammars/tree-sitter-julia/LICENSE +21 -0
  218. package/grammars/tree-sitter-julia/README.md +24 -0
  219. package/grammars/tree-sitter-kotlin/LICENSE +9 -0
  220. package/grammars/tree-sitter-lua/LICENSE.md +21 -0
  221. package/grammars/tree-sitter-lua/README.md +29 -0
  222. package/grammars/tree-sitter-make/LICENSE +21 -0
  223. package/grammars/tree-sitter-matlab/LICENSE +19 -0
  224. package/grammars/tree-sitter-matlab/README.md +99 -0
  225. package/grammars/tree-sitter-nim/LICENSE +373 -0
  226. package/grammars/tree-sitter-nim/README.md +46 -0
  227. package/grammars/tree-sitter-objc/LICENSE +21 -0
  228. package/grammars/tree-sitter-objc/README.md +20 -0
  229. package/grammars/tree-sitter-perl/LICENSE +7 -0
  230. package/grammars/tree-sitter-perl/README.md +100 -0
  231. package/grammars/tree-sitter-powershell/LICENSE +21 -0
  232. package/grammars/tree-sitter-powershell/README.md +7 -0
  233. package/grammars/tree-sitter-proto/LICENSE +21 -0
  234. package/grammars/tree-sitter-proto/README.md +105 -0
  235. package/grammars/tree-sitter-r/LICENSE +9 -0
  236. package/grammars/tree-sitter-r/README.md +66 -0
  237. package/grammars/tree-sitter-ruby/LICENSE +22 -0
  238. package/grammars/tree-sitter-scss/LICENSE +21 -0
  239. package/grammars/tree-sitter-scss/README.md +17 -0
  240. package/grammars/tree-sitter-solidity/LICENSE +7 -0
  241. package/grammars/tree-sitter-sql/LICENSE +21 -0
  242. package/grammars/tree-sitter-typescript/LICENSE +21 -0
  243. package/grammars/tree-sitter-vba/LICENSE +21 -0
  244. package/grammars/tree-sitter-vba/README.md +738 -0
  245. package/grammars/tree-sitter-vbnet/LICENSE +21 -0
  246. package/grammars/tree-sitter-vbnet/README.md +322 -0
  247. package/grammars/tree-sitter-xml/LICENSE +19 -0
  248. package/grammars/tree-sitter-yaml/LICENSE +20 -0
  249. package/grammars/tree-sitter-yaml/README.md +21 -0
  250. package/package.json +184 -4
  251. package/wasm/dart.wasm +0 -0
  252. package/wasm/tree-sitter-bash.wasm +0 -0
  253. package/wasm/tree-sitter-c.wasm +0 -0
  254. package/wasm/tree-sitter-c_sharp.wasm +0 -0
  255. package/wasm/tree-sitter-clean.wasm +0 -0
  256. package/wasm/tree-sitter-clojure.wasm +0 -0
  257. package/wasm/tree-sitter-core_schema.wasm +0 -0
  258. package/wasm/tree-sitter-cpp.wasm +0 -0
  259. package/wasm/tree-sitter-css.wasm +0 -0
  260. package/wasm/tree-sitter-dart.wasm +0 -0
  261. package/wasm/tree-sitter-elisp.wasm +0 -0
  262. package/wasm/tree-sitter-elixir.wasm +0 -0
  263. package/wasm/tree-sitter-elm.wasm +0 -0
  264. package/wasm/tree-sitter-embedded_template.wasm +0 -0
  265. package/wasm/tree-sitter-erlang.wasm +0 -0
  266. package/wasm/tree-sitter-fsharp.wasm +0 -0
  267. package/wasm/tree-sitter-fsharp_signature.wasm +0 -0
  268. package/wasm/tree-sitter-go.wasm +0 -0
  269. package/wasm/tree-sitter-graphql.wasm +0 -0
  270. package/wasm/tree-sitter-groovy.wasm +0 -0
  271. package/wasm/tree-sitter-haskell.wasm +0 -0
  272. package/wasm/tree-sitter-hcl.wasm +0 -0
  273. package/wasm/tree-sitter-html.wasm +0 -0
  274. package/wasm/tree-sitter-java.wasm +0 -0
  275. package/wasm/tree-sitter-javascript.wasm +0 -0
  276. package/wasm/tree-sitter-json.wasm +0 -0
  277. package/wasm/tree-sitter-json_schema.wasm +0 -0
  278. package/wasm/tree-sitter-julia.wasm +0 -0
  279. package/wasm/tree-sitter-kotlin.wasm +0 -0
  280. package/wasm/tree-sitter-lua.wasm +0 -0
  281. package/wasm/tree-sitter-make.wasm +0 -0
  282. package/wasm/tree-sitter-markdown.wasm +0 -0
  283. package/wasm/tree-sitter-markdown_inline.wasm +0 -0
  284. package/wasm/tree-sitter-matlab.wasm +0 -0
  285. package/wasm/tree-sitter-nim.wasm +0 -0
  286. package/wasm/tree-sitter-objc.wasm +0 -0
  287. package/wasm/tree-sitter-ocaml.wasm +0 -0
  288. package/wasm/tree-sitter-perl.wasm +0 -0
  289. package/wasm/tree-sitter-php.wasm +0 -0
  290. package/wasm/tree-sitter-php_only.wasm +0 -0
  291. package/wasm/tree-sitter-powershell.wasm +0 -0
  292. package/wasm/tree-sitter-protobuf.wasm +0 -0
  293. package/wasm/tree-sitter-python.wasm +0 -0
  294. package/wasm/tree-sitter-ql.wasm +0 -0
  295. package/wasm/tree-sitter-r.wasm +0 -0
  296. package/wasm/tree-sitter-rescript.wasm +0 -0
  297. package/wasm/tree-sitter-ruby.wasm +0 -0
  298. package/wasm/tree-sitter-rust.wasm +0 -0
  299. package/wasm/tree-sitter-scala.wasm +0 -0
  300. package/wasm/tree-sitter-scss.wasm +0 -0
  301. package/wasm/tree-sitter-solidity.wasm +0 -0
  302. package/wasm/tree-sitter-sql.wasm +0 -0
  303. package/wasm/tree-sitter-svelte.wasm +0 -0
  304. package/wasm/tree-sitter-swift.wasm +0 -0
  305. package/wasm/tree-sitter-systemrdl.wasm +0 -0
  306. package/wasm/tree-sitter-terraform.wasm +0 -0
  307. package/wasm/tree-sitter-tlaplus.wasm +0 -0
  308. package/wasm/tree-sitter-toml.wasm +0 -0
  309. package/wasm/tree-sitter-tsx.wasm +0 -0
  310. package/wasm/tree-sitter-typescript.wasm +0 -0
  311. package/wasm/tree-sitter-vba.wasm +0 -0
  312. package/wasm/tree-sitter-vbnet.wasm +0 -0
  313. package/wasm/tree-sitter-vue.wasm +0 -0
  314. package/wasm/tree-sitter-xml.wasm +0 -0
  315. package/wasm/tree-sitter-yaml.wasm +0 -0
  316. package/wasm/tree-sitter-zig.wasm +0 -0
  317. package/wasm/tree-sitter.wasm +0 -0
  318. package/wasm/tsx.wasm +0 -0
  319. package/wasm/typescript.wasm +0 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,298 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 — first public release
4
+
5
+ The first public release of Backbencher Brain (`backbencher-brain` on npm, `bb` on
6
+ the command line). Everything below is what a fresh `npm install -g @backbencher/brain`
7
+ gives you. The section still headed **Unreleased** further down was written against
8
+ internal builds and ships in this release; read it as the detailed change record
9
+ behind this summary.
10
+
11
+ ### What ships
12
+
13
+ - **16 MCP tools**, served over stdio to any MCP host (Claude Code, Cursor, VS Code,
14
+ Zed, and anything else that speaks MCP): `bb_search`, `bb_ask`, `bb_analyze`,
15
+ `bb_graph`, `bb_impact`, `bb_patterns`, `bb_security`, `bb_doc`, `bb_visualize`,
16
+ `bb_migrate`, `bb_cross_language`, `bb_edit`, `bb_index`, `bb_health`,
17
+ `bb_annotate`, `bb_annotations`. Tool descriptors are the same for the CLI and the
18
+ MCP server, and `docs/data/public-tools.json` is generated from the executable
19
+ registry so the catalog cannot drift from the tool that answers.
20
+ - **50 language analyzers**, from TypeScript/JavaScript, Python, Go,
21
+ Rust, Java/Kotlin/Scala/Groovy and C/C++ through to Haskell, Clojure, Clean, Erlang,
22
+ Julia, Nim, Solidity, HCL/Terraform, Vue, Svelte and more. TSX, Sass and Terraform are
23
+ aliases of the TypeScript, CSS and HCL analyzers, not extra languages; the editor lane
24
+ covers 49 of the 50 (Objective-C is excluded because `.m` is shared with MATLAB) and 48
25
+ carry recorded in-editor evidence. The list is the `SUPPORTED_LANGUAGES` map in
26
+ `src/ast/engine.ts`; the site reads the same map.
27
+ - **A CLI with 14 commands**: `bb index` (build/inspect the index), `bb search`,
28
+ `bb mcp`, `bb setup`, `bb stats`, `bb status`, `bb health`, `bb doctor`,
29
+ `bb projects`, `bb onboard`, `bb tool`, `bb upgrade`, `bb version`, `bb help`.
30
+ - **An LSP server** (`dist/lsp/server.js` over stdio; the editor extension launches it) for the launch languages recorded in
31
+ `src/lsp/launch_languages.ts` — hover, definition, references, document symbols,
32
+ workspace symbols, call hierarchy. The launch set is extended only through the
33
+ recorded wave protocol, so the set here is what the gates prove, not a promise.
34
+ - **Local-first by construction.** The index is SQLite, one database per project,
35
+ outside your repository (`bb stats` prints the path). Nothing is uploaded, there is
36
+ no account, no telemetry and no hosted service; semantic search needs a local
37
+ embedding provider you choose (`bb setup`).
38
+ - **Edits that declare their limits.** `bb_edit` does AST-bounded write/update/
39
+ insert/delete/refactor with pre-image checks, dry runs, batch atomicity and a
40
+ formatter step that reports what it actually did. Every read surface carries a
41
+ freshness block rather than quietly answering from a stale index.
42
+
43
+ ### Install
44
+
45
+ ```bash
46
+ npm install -g @backbencher/brain # primary path, Node.js >= 18
47
+ bb --version
48
+ bb setup # optional: semantic search provider
49
+ bb index /path/to/project
50
+ ```
51
+
52
+ `curl -fsSL https://backbencher.cc/install.sh | bash` and
53
+ `irm https://backbencher.cc/install.ps1 | iex` remain as alternatives: they install
54
+ the same npm package, and fall back to a checksum-verified GitHub release binary only
55
+ when npm is unavailable. Pin a version with `VERSION=0.1.0` in front of either script.
56
+ Windows, Linux and macOS are supported; the native modules (`better-sqlite3`,
57
+ `web-tree-sitter`) build through npm's normal platform support.
58
+
59
+ ### Known limits, stated rather than buried
60
+
61
+ - Node.js 18 or newer is required; there is no bundled runtime.
62
+ - Parsing and analysis are static. Nothing here executes the code it reads, so a
63
+ clean report is not a proof of correctness or security.
64
+ - The LSP serves the languages in the launch set; other indexed languages still get
65
+ the MCP/CLI surfaces but not editor features.
66
+ - Semantic retrieval quality depends on the embedding provider you configure; the
67
+ lexical/graph surfaces work without one.
68
+ - `npm upgrade`/`bb upgrade` move you to the next published release; the index schema
69
+ migrates in place and is never wiped by an upgrade.
70
+
71
+ ### Version numbering
72
+
73
+ `0.1.0` is the first public version. Earlier internal builds counted up to `0.3.0`,
74
+ and a separate pre-release binary product used `1.0.x` version numbers; neither is
75
+ continued here. No `1.0.x` artifact was ever published to npm, and the `1.0.x`
76
+ installer line is retired — the installers in this repository install
77
+ `backbencher-brain` from npm.
78
+
79
+ ## Unreleased
80
+
81
+ ### The editor surfaces stopped disagreeing with each other
82
+
83
+ Four surfaces answer "who uses this symbol": the hover card, `bb/graph` `mode:"usages"`
84
+ (the hub), `textDocument/references`, and the hover's own `connected:` line. Measured on a
85
+ real index over real stdio, one symbol produced four numbers — `greet` reported 6 usages in
86
+ prose while its own connections line listed 3 callers, the hub listed 6 caller groups, and
87
+ references returned 7 locations. Each number was individually defensible: they counted
88
+ different things, and none of them said what.
89
+
90
+ They now count one thing and say so. A **usage site** is a stored relationship row whose
91
+ target is the symbol and whose kind is not a declaration containment, owned in one place
92
+ (`src/lsp/relations.ts`); a **count** is a count of served positions, and the unit sentence is
93
+ the owner's single copy quoted by every surface. The hover's count equals its rendered rows
94
+ plus a declared remainder; zero is stated rather than omitted.
95
+
96
+ Three separate lies were removed to get there, each found only because a human read the
97
+ surface:
98
+
99
+ - **The hover's "usages" list was not usages.** `greetAll` has zero callers and rendered
100
+ "3 usages" listing its own class (`Greeter`) and its own callee (`greet`). Declaration
101
+ containments now render in their own labelled section and never in the usage list or the
102
+ connection summary.
103
+ - **The hub served the owner-family cascade as callers.** A method with **zero** callers
104
+ answered six caller groups — every class member's referrers plus the class's own
105
+ instantiators — so "who calls this" listed symbols that never call it. Cascade rows are now
106
+ excluded **and counted** per bucket (`selfReferentialRows`, `declarationContainments`,
107
+ `ownerFamilyRows`), with `callerCandidateRows` on the payload so every excluded row is
108
+ accounted for. The naive fix — drop everything not adjacent to the root — was measured and
109
+ rejected: it deleted the hub's legitimate transitive call tree. The law that satisfies both
110
+ is that a deeper row is served only when it extends an already-served chain.
111
+ - **The class's caller list carried a member's own call**, re-attributed to the class. It now
112
+ equals the class's usage sites.
113
+
114
+ `script/m55/verify-relations.ts` grew four cross-surface assertions (`R3b`) that pin the
115
+ agreement on the wire: a zero-caller symbol answers zero from hover, references and the hub;
116
+ an empty answer is empty *by a declared filter* whose counts close the row arithmetic; a
117
+ symbol with callers reconciles as groups = usage places + the declared unlocatable referrers.
118
+
119
+ ### A position a surface could not know is no longer shown
120
+
121
+ Three classes of invented position, all found by reading rendered surfaces:
122
+
123
+ - **The hover served stored call-site lines past the end of the source.** A three-line file
124
+ with rows stored at lines 40 and 41 rendered two usage links that opened past the file.
125
+ The hover now applies the same line bound `textDocument/references` applies
126
+ (`createPositionMapper(text).lineTexts.length`), refuses to serve such a row, and declares
127
+ the count: *"usages omitted — N stored rows name a call-site line past the end of the source
128
+ file"*, with `data.omittedUsageRows`. Measured before/after: collector sites 2 → 0,
129
+ `omittedUnresolvable` unchanged at 2. The agreement is gated, not described.
130
+ - **Call hierarchy listed the root as its own caller and callee**, pointed every `fromRanges`
131
+ entry at a declaration instead of the call site, and threw `-32603` when a caller was
132
+ expanded. Self-edges are dropped and **declared with their reason**; ranges come from the
133
+ M62 address owner and carry the declared unknown where the store holds no real line.
134
+ - **Outgoing connected/containment links opened the far end's file at the caller's stored
135
+ line** — a link labelled "instantiates [Greeter]" that opened `greeter.ts:4` when the site is
136
+ `app.ts:4`. Outgoing rows now open the far end's declaration and never turn a stored line
137
+ into a link target for a different symbol.
138
+
139
+ A new absence code `no_stored_calls` distinguishes "served symbol with an empty call list" from
140
+ `symbol_not_at_position`, which was false for it — a machine-readable code a client may branch
141
+ on must not contradict the answer.
142
+
143
+ ### The hover's relationship denominator was ten kinds against an index that stores fourteen
144
+
145
+ `decorates`, `renders`, `mixes_in` and `part_of` were stored, classified by the hover's own tier
146
+ owner, and could not be rendered or declared because the denominator and the verb table stopped
147
+ at ten. All fourteen now have a denominator entry, both-direction verbs, and — for `part_of` —
148
+ a declared absence instead of a link (a module-level inclusion, like `imports`, is not drawn as
149
+ a symbol link). The drift gate that catches this was itself anchored on a file the kind
150
+ enumeration had moved out of, so it had been reporting a refactor as a pass rather than
151
+ checking a fact.
152
+
153
+ ### Warm hover latency: p95 51.31 ms → 9.82 ms
154
+
155
+ The warm budget is 50 ms (§3). One of three busy shapes missed it. The cause was not the parse:
156
+ the hover resolved a full call-site location for **every** row and then discarded it for any row
157
+ whose stored call-site line was real, so a 32-file symbol paid 30 file reads and 60 position
158
+ mappers per request for positions nothing later read. Now a row with a real stored call-site
159
+ line is served from `(caller path, stored line)` validated only by the line-count bound, and
160
+ line counts ride a bounded LRU keyed by `(path, mtimeMs, size)` — stat-only on a warm hit,
161
+ re-read on any mismatch. Measured with load average 20.67 on 8 CPUs: shape A p50 6.62 ms /
162
+ p95 9.82 ms; shapes B and C under 5 ms. No regex was introduced (regex policy: 0 new).
163
+
164
+ ### The extension activated on 7 of the 14 languages the server serves
165
+
166
+ `src/lsp/launch_languages.ts` is the single owner of the launch set (14 languages), but
167
+ `package.json` `activationEvents` and the client's `documentSelector` listed 7. On a Go, Rust,
168
+ C, C++, Vue, Svelte or Zig file the extension never activated; in a mixed workspace VS Code
169
+ sent no requests for those files at all. Both lists are now derived from the owner through a
170
+ type-enforced language-id table, and `esbuild-extension.mjs` fails the build if
171
+ `activationEvents` drifts from the launch set.
172
+
173
+ ### The status-bar badge was dead on three paths
174
+
175
+ Clicking the `bb` badge opened nothing when there was no workspace folder (a single file
176
+ opened), when the server was not resolved, or when the server version was incompatible — the
177
+ click target was assigned only on the success path. The command is now assigned at creation and
178
+ every command is registered before any early return, so no badge renders without a working
179
+ click.
180
+
181
+ ### A claim ladder with three definitions
182
+
183
+ The I0–I5 levels were defined three times and disagreed: the contract said I2
184
+ INDEX-CONFORMANT / I3 EDITOR-VERIFIED / I4 FORK-VERIFIED, the extension README said
185
+ RUNTIME-PASSED / IN-EDITOR / CROSS-EDITOR, and the status model said HARNESS-VERIFIED /
186
+ EDITOR-VERIFIED / PRECISION-VERIFIED. The website rendered two of them side by side. The
187
+ contract is governing; both other copies now defer to it and define no second ladder.
188
+
189
+ ### Verification machinery repaired
190
+
191
+ Six gates were red or silently not running, each for a reason unrelated to the product:
192
+
193
+ - `verify-relations.ts` M4 anchored on a source expression M62 had moved behind the
194
+ single-owner constant `STORED_LINE_COLUMN`, so it matched zero times.
195
+ - `verify-call-hierarchy.ts` C1 asserted a store shape the engine stopped producing in
196
+ `2617ac1bd` ("an unresolved target is reported as unresolved, not matched by name"); it is
197
+ now a hermetic planted-row test, and the seven free verdicts it caused are gone.
198
+ - `verify-hover-coverage.ts` V1c read `src/mcp/tool-registry.ts`, where the relationship-kind
199
+ enumeration no longer lives, and its machine metadata shelled out to
200
+ `sysctl -n hw.ncpu`, which crashes on Linux before any result is written.
201
+ - `verify-evidence-tree-clean.ts` counted ~6.7k **deliberately gitignored** research artifacts
202
+ as "scratch" while its own self-check asked git the same question and got a different
203
+ answer; and its EVID-TREE claim declared only verifier paths as coverage, so the fingerprint
204
+ could not discriminate and the gate threw on every run that reached the evidence write.
205
+ - Four gates (`verify-server`, `verify-usage-scope`, `verify-usages-fallback`) did not pin
206
+ `BB_DATA_DIR`, so on a host with a read-only `~/.bb` every absence-carrier path died with
207
+ `-32603 ENOENT` — a red gate about the sandbox. They now pin a temp data dir unless one is
208
+ supplied, and `L1` is 142/142 reproducibly.
209
+
210
+ ### Documentation and tree hygiene
211
+
212
+ Documentation now states measured fact rather than narrated decision: the "owner said",
213
+ "his words" and "owner live test" framing is gone from the IDE documents and from the
214
+ analyzer header template that taught it. Thirteen superseded/duplicate documents and twelve
215
+ stray root files (six ad-hoc probes referencing a path that does not exist in this layout, a
216
+ certificate log, two XML probes, two superseded root docs, an unused wasm copy) were removed
217
+ after a reference grep proved nothing cited them. A new `docs/IDE-LANE-BASELINE.md` records,
218
+ per record, what the documents claimed, what the record claimed, what the gate returned at
219
+ HEAD, and what is actually true — four different states that had been collapsed into one.
220
+
221
+ ### A `callers` count was not a count of calls
222
+
223
+ The graph engine's default covers every relationship kind the index stores — documented, and
224
+ deliberate, because `contains` is the parent→member edge and excluding it stops a traversal rooted
225
+ at a class from reaching that class's own methods. Three consumers in `src/` read that set while
226
+ making a claim about one kind:
227
+
228
+ - `testImpact` published a depth-0 row from a test file as a DIRECT test without asking what kind
229
+ of row it was, so a test class that *contains* the symbol under test answered as a test that
230
+ depends on it — and `testSuggestions` stamped that `direct_call` at 0.85 confidence.
231
+ - `testSuggestions`'s `blastRadius`, labelled "caller closure", counted containment and lexical
232
+ rows, and it feeds `priorityFor`, so structure inflated a priority.
233
+ - the deprecated `analyzeImpact` (not reachable from any tool since M48) fitted every kind into
234
+ `directSymbols` / `affectedFiles`.
235
+
236
+ Measured on a fixture whose store holds, from the real analyzer, `realTest --calls--> nested` and
237
+ `TestHost --contains--> nested`: the unscoped walk returns both callers, the scoped walk returns
238
+ one, and the tool now reports `directTests: [{ testSymbol: "realTest", relationshipType: "calls",
239
+ evidence: "direct_call" }]`. `TestImpactReport.directTests` / `indirectTests` and
240
+ `AffectedTestEntry` now carry `relationshipType` and `evidence`, so "directly a test" is never
241
+ published as "directly calls". `testSuggestions` walks `bb_impact`'s declared dependency rule
242
+ (`IMPACT_REL_TYPES`, one list, imported from its owner) instead of every kind.
243
+
244
+ `bb_analyze`'s page said nothing about the kind set its `callers`/`callees`/`trace` modes walk; it
245
+ does now, including that mode `impact` walks the declared dependency rule instead — so a `callers`
246
+ count and an `impact` count about the same symbol are not the same question, and both name the rule.
247
+ Pinned by `script/m65/verify-edge-kind-scoping.ts`: an inventory of every graph call site in `src/`
248
+ (an unscoped one must be a declared all-kinds door), a fixture differential against the store, two
249
+ mutations, and source guards so a reverted filter fails the gate.
250
+
251
+ ### The public surface grew from ten to sixteen
252
+
253
+ The 0.3.0 section below describes that release's surface, and it was accurate when it
254
+ was written (2026-08-09). Six tools have been added since, so the live surface is
255
+ sixteen — the number is now read from `docs/data/public-tools.json` wherever it is
256
+ needed instead of being restated:
257
+
258
+ - `bb_index` — reindex a file or directory so the index reflects edits immediately,
259
+ plus `mode=post` to re-run derived phases without re-parsing.
260
+ - `bb_graph` — per-symbol callers/callees at a declared depth, with per-edge evidence
261
+ tier and confidence from the canonical graph engine.
262
+ - `bb_impact` — bounded reverse-dependency impact with a declared node budget and an
263
+ echoed relationship-type rule.
264
+ - `bb_cross_language` — stored cross-language facts for one symbol, with absence
265
+ declared rather than inferred when the store holds none.
266
+ - `bb_annotate` — write an agent-authored note with `author_class` enforced at the
267
+ schema level, never spoofable.
268
+ - `bb_annotations` — read notes back with full provenance and a declared bound.
269
+
270
+ Same release window, same defect class: `README.md` and both MCP acceptance harnesses
271
+ (`script/verify-mcp.ts`, `script/verify-mcp-e2e.ts`) had kept the old count in prose and
272
+ in an assertion. The stale assertion failed at command 125 of 358 in `npm run verify`,
273
+ which made the 233 commands after it unreachable — so nothing downstream could notice.
274
+ Both harnesses now derive the expected surface from the contract file, cover all sixteen
275
+ tools (five had no MCP-level test at all, and `bb_index` had never been called as a tool),
276
+ and fail with a named missing/undeclared list rather than a bare number.
277
+
278
+ ## 0.3.0
279
+
280
+ ### Intelligence Tools Rebuild
281
+
282
+ - Published the final ten-tool MCP surface: `bb_search`, `bb_ask`, `bb_analyze`,
283
+ `bb_patterns`, `bb_security`, `bb_doc`, `bb_visualize`, `bb_migrate`, `bb_edit`,
284
+ and `bb_health`.
285
+ - Removed compatibility aliases after the announced breaking-release window:
286
+ `bb_select`, `bb_relationships`, `bb_refresh`, `bb_write`, `bb_update`,
287
+ `bb_insert`, `bb_delete`, `bb_refactor`, `bb_test_quality`, and
288
+ `bb_test_suggestions`.
289
+ - Replacements are `bb_search mode=symbol`; `bb_analyze` callers/callees/trace and
290
+ test modes; `bb_health refresh=true`; and `bb_edit` write/update/insert/delete/
291
+ refactor modes.
292
+ - Removed alias-only stale-index mutation exceptions. Canonical symbol mutations
293
+ require current indexed AST bounds.
294
+ - Preserved internal indexing, semantic, graph, analysis, test, edit, health,
295
+ documentation, visualization, and migration engines. They are implementation
296
+ layers, not separately advertised MCP tools.
297
+
298
+ Historical migration details remain in `archive/docs-pre-consolidation/MCP_TOOL_MIGRATION.md`.
package/EMBEDDINGS.md ADDED
@@ -0,0 +1,355 @@
1
+ # Semantic Embeddings — Setup Guide
2
+
3
+ brain-core ships two embedding layers:
4
+
5
+ 1. **API embeddings** (best quality) — any OpenAI-compatible `/embeddings`
6
+ endpoint: Google Gemini, NVIDIA NIM, OpenAI, or a self-hosted vLLM /
7
+ llama.cpp server. Off by default — one config file or 3 env vars turns it on.
8
+ 2. **Local word vectors** (offline fallback) — always available, no API key.
9
+ Quality is keyword-level, not true semantics.
10
+
11
+ ## Interactive setup: `bb setup` (recommended first run)
12
+
13
+ `bb setup` walks you through provider choice with prefilled base URL and model
14
+ so you only paste the API key (input is hidden). It validates the key with one
15
+ live `/embeddings` call before saving, writes `~/.bb/config.json` with mode
16
+ 0600 (owner-only), and never stores the key anywhere else. The built-in free
17
+ options are:
18
+
19
+ | Provider | base_url (prefilled) | model (prefilled) |
20
+ |---|---|---|
21
+ | OpenRouter free | `https://openrouter.ai/api/v1` | `liquid/lfm-2.5-embedding-350m:free` |
22
+ | NVIDIA NIM free | `https://integrate.api.nvidia.com/v1` | `nvidia/nemotron-3-embed-1b` |
23
+ | Custom / paid | you supply both | you supply both |
24
+
25
+ If you skip setup or have no key, `bb index` shows exactly this notice (one
26
+ line, non-interactive environments never block on it):
27
+
28
+ ```
29
+ note: no embedding key — semantic search will use local vectors (weaker). Setup: bb setup
30
+ ```
31
+
32
+ The wizard runs **once**: an explicit skip is remembered (a `setup_skipped`
33
+ marker in the 0600 config), so `bb index` never re-prompts after you chose to
34
+ skip. Entering a key later via `bb setup` replaces the skip automatically.
35
+
36
+ ### What a failed key validation means (pre-save, `bb setup`)
37
+
38
+ A key is validated before it is saved. The exact messages users see:
39
+
40
+ - `Key rejected (401). Check the key and provider — copy-paste may have dropped or added characters.`
41
+ - `Rate limit (429). The key looks valid; free-tier limit. It can be saved — indexing backs off and retries.`
42
+ - `Provider error (503). The key may be valid; the provider is temporarily failing. It can be saved — indexing retries automatically.`
43
+ - `Model not found (404). Check the model name: the provider's /embeddings endpoint does not recognize it.`
44
+ - `Provider did not respond (timeout). Check your internet connection and the base URL.`
45
+ - `Could not reach the provider: <error>. Check the base URL.`
46
+ - `Provider returned invalid JSON. The base URL must be a /v1 endpoint.`
47
+ - `Provider returned no embedding. Check the base URL and model name.`
48
+
49
+ 429 and 5xx are treated as "key shape accepted" (rate/provider side issue) and
50
+ saved; A 401/403/404 or malformed response is a hard error: the key is NOT saved.
51
+
52
+ **What a saved key actually gets at index time.** The wording above is
53
+ `bb setup`'s pre-save validation, and one of those messages is optimistic about
54
+ what follows: indexing does not re-run key validation, so a 503 seen here is not
55
+ proof that indexing will retry the same way. Concretely, at index time:
56
+
57
+ - A **429** degrades only that batch, per-batch, to local word vectors, and
58
+ further batches are held off by a bounded `Retry-After` window in the engine
59
+ (never a process-wide latch, so the run recovers by itself).
60
+ - A **5xx, timeout, or unreachable provider** degrades that batch to local word
61
+ vectors. The next batch tries again — there is no wait, so a provider that is
62
+ down for a whole run costs one degraded batch per batch, not one at the end.
63
+ - A **401 or 404** stops being retried for the rest of the process: further
64
+ embedding requests fail immediately with the same classified error, and `bb
65
+ doctor` reports the status and what to change. Re-running the command after
66
+ fixing the key picks it up again.
67
+ - **No batch is silently lost**: a batch that cannot be embedded by any provider
68
+ falls back to sparse `tfidf-v1` rows, and the run reports how many were
69
+ embedded, fell back, or failed.
70
+
71
+ If you want a second provider tried when the first one fails, configure
72
+ `BB_EMBEDDING_FALLBACK_*` (below) — that provider is the one with a client-side
73
+ retry loop, bounded by `BB_EMBEDDING_FALLBACK_RETRIES`.
74
+
75
+ ## Inspecting and diagnosing: `bb setup show` and `bb doctor` (M32)
76
+
77
+ `bb setup show` prints the effective embedding configuration without ever
78
+ displaying the full key — the key appears only as its last four characters:
79
+
80
+ ```
81
+ bb — embedding configuration
82
+ Precedence: env > ~/.bb/config.json > bb.json
83
+
84
+ env : — (not set)
85
+ global : ✓ https://g.example/v1 · g-model · key: ••••2345 · mode: 600
86
+ project: — (not set)
87
+
88
+ Winner: global
89
+ ```
90
+
91
+ It names the winning precedence tier, shows incomplete tiers with what is
92
+ missing (an incomplete tier can never win), warns when a config file's mode is
93
+ not 0600, and exits 1 when nothing is configured (scripts can branch on it).
94
+ A corrupt config file is treated as an absent tier — it never crashes.
95
+
96
+ `bb doctor` diagnoses degradation from real local state (config file, file
97
+ permissions, and the embedding models actually stored in the project index —
98
+ read-only, no network). Every finding names the exact fix command:
99
+
100
+ ```
101
+ bb doctor — diagnostics
102
+
103
+ ✗ No embedding configuration.
104
+ Fix: bb setup
105
+ ! global config file has mode 644 — the key may be world-readable.
106
+ Fix: bb doctor --fix
107
+ · No stored vectors for this project (no index or empty). Config model: g-model.
108
+ Fix: bb index .
109
+ ```
110
+
111
+ A model mismatch (config says one model, the index was built with another) is
112
+ reported as a hard failure with `Fix: bb index --force` — stored vectors
113
+ from different models are never compared. `bb doctor` exits 1 when a fail-level
114
+ finding exists.
115
+
116
+ ### `bb doctor --fix`: the only mutating arm
117
+
118
+ `bb doctor --fix` repairs **only file permissions** of embedding config files
119
+ to 0600. It prints every change it makes; config content, keys, and the index
120
+ are never modified by `--fix`. Everything else remains advice naming the exact
121
+ command — diagnosis never auto-mutates, and `--fix` never touches anything
122
+ beyond permissions:
123
+
124
+ ```
125
+ bb doctor --fix — applied
126
+
127
+ ✓ /root/.bb/config.json: mode 644 -> 600 (applied)
128
+ ```
129
+
130
+ ## Discovering these commands
131
+
132
+ Every command documents itself: `bb --help` lists all commands,
133
+ `bb <command> --help` shows that command's options, and `bb -v`,
134
+ `bb version`, and `bb --version` all print the version. A mistyped command
135
+ suggests the closest real one (`bb docter` suggests `doctor`). The full
136
+ manual ships with npm installs and is available as `man bb`.
137
+
138
+ ## Quick start (3 environment variables)
139
+
140
+ ```bash
141
+ export BB_EMBEDDING_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai/
142
+ export BB_EMBEDDING_API_KEY=your-google-api-key
143
+ export BB_EMBEDDING_MODEL=text-embedding-004
144
+
145
+ bb index ./my-project # vectors stored under text-embedding-004
146
+ ```
147
+
148
+ ## Config file (per machine)
149
+
150
+ `~/.bb/config.json`:
151
+
152
+ ```json
153
+ {
154
+ "embedding_model": "text-embedding-004",
155
+ "embedding_base_url": "https://generativelanguage.googleapis.com/v1beta/openai/",
156
+ "embedding_api_key": "your-google-api-key"
157
+ }
158
+ ```
159
+
160
+ `bb.json` in the project root overrides it per project. Environment variables
161
+ (`BB_EMBEDDING_*`) always win.
162
+
163
+ ## Known-good providers
164
+
165
+ | Provider | base_url | model | notes |
166
+ |---|---|---|---|
167
+ | Google Gemini | `https://generativelanguage.googleapis.com/v1beta/openai/` | `text-embedding-004` (768d) or `gemini-embedding-001` (3072d) | OpenAI-compatible endpoint |
168
+ | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` | `nvidia/nv-embedqa-e5-v5` (1024d) or `nvidia/bge-m3` | OpenAI-compatible |
169
+ | OpenAI | `https://api.openai.com/v1` | `text-embedding-3-small` (1536d) or `-3-large` (3072d) | native |
170
+ | Self-hosted vLLM | `http://localhost:8000/v1` | any served model | OpenAI-compatible |
171
+ | Self-hosted llama.cpp | `http://localhost:8080/v1` | any served model | OpenAI-compatible |
172
+
173
+ ### Google (Gemini API key from aistudio.google.com)
174
+
175
+ ```
176
+ BB_EMBEDDING_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai/
177
+ BB_EMBEDDING_MODEL=text-embedding-004
178
+ ```
179
+
180
+ ### NVIDIA NIM (key from build.nvidia.com)
181
+
182
+ ```
183
+ BB_EMBEDDING_BASE_URL=https://integrate.api.nvidia.com/v1
184
+ BB_EMBEDDING_MODEL=nvidia/nv-embedqa-e5-v5
185
+ ```
186
+
187
+ ## Environment variables
188
+
189
+ The three above decide **which** provider is used, and their precedence is
190
+ `env BB_EMBEDDING_*` > config file (`BB_CONFIG_FILE` > `bb.json` > `config.json`)
191
+ > built-in default. Everything below tunes how that provider is called. None of
192
+ them has a config-file equivalent: they are operational knobs, read from the
193
+ environment only.
194
+
195
+ | Variable | Default | What it does |
196
+ |---|---|---|
197
+ | `BB_EMBEDDING_DISABLED` | unset | `1`/`true`/`yes`/`on` turns remote embeddings off entirely. Indexing stores local word vectors and makes no embedding request. An unrecognised value is treated as OFF, so a typo cannot silently drop a provider you pay for. |
198
+ | `BB_EMBEDDING_FALLBACK_BASE_URL` | unset | A second provider to try when the primary fails. Requires `BB_EMBEDDING_FALLBACK_MODEL`. |
199
+ | `BB_EMBEDDING_FALLBACK_MODEL` | unset | Model for the fallback provider. |
200
+ | `BB_EMBEDDING_FALLBACK_API_KEY` | unset | Key for the fallback provider. A loopback fallback needs none. |
201
+ | `BB_EMBEDDING_FALLBACK_RETRIES` | `2` | Retries for the FALLBACK provider only, exponential backoff with jitter. `0` means one attempt, no retry loop. |
202
+ | `BB_EMBEDDING_TIMEOUT_MS` | `30000` | Bound on one embedding request, covering headers **and** body. Floored at 1000ms, so a typo cannot instant-fail every request. |
203
+ | `BB_EMBEDDING_MAX_INPUTS` | `256` | Ceiling on inputs per `/embeddings` request. A ceiling, not a partition: the indexer already batches 100 snippets, so ordinary indexing stays one request. |
204
+ | `BB_EMBEDDING_RETRY_BASE_MS` | `250` | First backoff step; doubles per attempt. Fallback provider only. |
205
+ | `BB_EMBEDDING_RETRY_CAP_MS` | `4000` | Ceiling on one backoff wait. Fallback provider only. |
206
+ | `BB_EMBEDDING_CACHE_MAX` | `128` | How many recent query vectors are cached in memory (LRU). Cached only when they came back non-empty; a config change keys the cache so a different model or endpoint can never serve a vector from another vector space. |
207
+ | `BB_EMBEDDING_CALL_TIMEOUT_MS` | `60000` | Bound on one whole `embedMany` call inside the background drainer, above the client's own per-request bound. Exceeding it counts as a provider failure for that batch, which is what routes it to the local fallback instead of wedging the queue. |
208
+ | `BB_EMBEDDING_DIMENSION` | unset | Assertion, not a request parameter: the vector width you expect from the configured model. A response of a different width is refused rather than written, because vectors of two widths cannot share one `embedding_model` label. Unset it if the real width differs. |
209
+
210
+ Two of these deserve the emphasis, because the defaults are not the only
211
+ sensible choice for everyone:
212
+
213
+ - **`BB_EMBEDDING_DISABLED`** is the switch to reach for when you would rather
214
+ pay the local quality cost than any network call — an air-gapped machine, a CI
215
+ runner with no provider key, or a debugging session where you want indexing to
216
+ stop waiting on someone else's uptime.
217
+ - **`BB_EMBEDDING_MAX_INPUTS`** exists because a failure in any chunk of a
218
+ multi-chunk request discards the whole request, so needless fragmentation turns
219
+ an unrelated later failure into a loss of earlier completed work.
220
+
221
+ ## The fallback chain, in order
222
+
223
+ 1. `BB_EMBEDDING_DISABLED` on → local word vectors only, no HTTP.
224
+ 2. The primary provider (env / config file / default, by the precedence above).
225
+ 3. The fallback provider, if `BB_EMBEDDING_FALLBACK_*` is configured. Reached
226
+ only when the primary fails, and the only layer with a client-side retry loop.
227
+ 4. Local word vectors (`local-word-vectors-v3`, 50-dimensional) — always
228
+ available, offline, and the reason the chain has no dead end.
229
+
230
+ The chain has no dead end by design: no configuration makes indexing fail to
231
+ produce vectors. What a misconfiguration costs is quality (layer 4 instead of
232
+ layer 2), which is why the diagnosis below is worth reading.
233
+
234
+ ## When something is wrong: `bb doctor` and `bb setup show`
235
+
236
+ Both print the chain in the order above with the env var each layer came from,
237
+ so "which provider am I on and what do I change to move off it" is answerable
238
+ without reading a config file. `bb health --verbose` prints the same chain, from
239
+ the same code, so the three surfaces cannot disagree.
240
+
241
+ ```
242
+ $ bb doctor
243
+ bb doctor — diagnostics
244
+
245
+ ! Embeddings are switched off (BB_EMBEDDING_DISABLED), so indexing stores only local word vectors.
246
+ Fix: unset BB_EMBEDDING_DISABLED
247
+ ✗ env tier is incomplete: missing model.
248
+ Fix: bb setup
249
+ embedding provider chain (embeddings disabled — local only):
250
+ 1. embeddings disabled: none · none [env:BB_EMBEDDING_DISABLED]
251
+ →2. local word vectors: offline · 50-dim [built in]
252
+ ```
253
+
254
+ What each surface adds:
255
+
256
+ - **`bb doctor`** — the findings, each naming the exact command or env var that
257
+ resolves it, and the exit code (1 when a FAIL-level finding exists, so CI can
258
+ branch). It also reports failures the provider has already given this process:
259
+ a 401 and a 404 are FAILs because neither is fixed by waiting, while a 429 or a
260
+ 5xx is a WARN because indexing degrades that batch and carries on.
261
+ - **`bb setup show`** — the tier table (base URL, model, and the key as its last
262
+ four characters) plus the chain and any problem found in it.
263
+ - **`bb health --verbose`** — the chain, and `embeddingChain` in the JSON for a
264
+ machine consumer.
265
+
266
+ Diagnosis is read-only and makes no network call, so it is safe in CI and on a
267
+ machine that cannot reach the provider. The one exception is a live probe,
268
+ available as `runDoctorChecksWithProbe` in `src/setup/doctor.ts`: it makes a
269
+ single bounded request through the same client indexing uses, and it does not
270
+ affect what indexing does next. `bb health` never probes.
271
+
272
+ ## What changes when it's on
273
+
274
+ - `bb index` stores real embedding vectors (`brain_semantic_embedding`,
275
+ `embedding_model` = your model, `vector_dimension` = the model's dimension)
276
+ - Semantic search ranks by cosine similarity over those vectors
277
+ - No key configured? The index falls back to local word vectors and semantic
278
+ search degrades to keyword-level matching — nothing breaks.
279
+
280
+ ## How indexing generates them (background queue)
281
+
282
+ Embedding generation is **non-blocking**: `bb index` completes and returns
283
+ usable code intelligence immediately, while vectors populate lazily on a
284
+ shared background queue (one serialized drainer per process). To wait for the
285
+ queue before reading vectors back programmatically, call
286
+ `flushBackgroundEmbeddings()` from `src/ast/parallel-indexer`. Call it before
287
+ closing or deleting a project database; queued jobs must never outlive their
288
+ SQLite handle.
289
+
290
+ Two knobs bound the work so huge repositories never stall indexing or exhaust
291
+ memory:
292
+
293
+ - `BB_EMBEDDING_BATCH_LIMIT` (default `50000`) — a single storeResults batch
294
+ at or above this limit is skipped entirely (no vectors).
295
+ - `BB_EMBEDDING_QUEUE_LIMIT` (default `25000`) — total queued snippet-items
296
+ across the process. Once the queue is at capacity, further batches are
297
+ dropped with a debug log rather than accumulated in memory.
298
+
299
+ Dropped batches are recoverable: run `bb index --force <project>` (or re-run
300
+ `bb_refresh(force)`) to generate vectors for the whole project at once. For
301
+ measurement harnesses that must not touch the embedding API at all (e.g. the
302
+ M11 corpus benchmarks), set `BB_EMBEDDING_BATCH_LIMIT=0` in the harness scope.
303
+
304
+ ## The no-key trade-off, in plain terms
305
+
306
+ Without any embedding key, semantic search uses **local word vectors
307
+ (`local-word-vectors-v3`, keyword-level matching)**. That label is what appears
308
+ in the `embedding_model` column of `brain_semantic_embedding`, so it is the
309
+ string to query when you want the offline vectors:
310
+
311
+ ```sql
312
+ SELECT embedding_model, vector_dimension, COUNT(*)
313
+ FROM brain_semantic_embedding
314
+ WHERE embedding_model = 'local-word-vectors-v3'
315
+ GROUP BY 1, 2;
316
+ ```
317
+
318
+ (The `-v3` suffix is load-bearing, not decoration: earlier versions of this
319
+ model tokenized and combined words differently, so those vectors occupy a
320
+ different space and are never compared with current ones. Bumping the version is
321
+ how a model change is prevented from producing a confident nonsense score; an
322
+ index built by an older version reports `no-matching-model-embeddings` until it
323
+ is re-embedded with `bb index --force`.) That is a real, declared
324
+ quality cost — not "the same, just offline". It is the honest default: nothing
325
+ is silently degraded, and the notice above says so. Adding a real model later
326
+ **requires `bb index --force <project>`** to re-embed the whole index; old
327
+ vectors are never reused or mixed with new ones.
328
+
329
+ ## Vector-space mixing rule
330
+
331
+ Vectors produced by different models live in **different vector spaces** and
332
+ are **never compared or mixed**. Every stored vector records the model that
333
+ produced it (`embedding_model`); the search only compares vectors with the
334
+ same label, and the query vector's producing model
335
+ (`SemanticEngine.getLastEmbeddingModel()`) is authoritative. Changing model or
336
+ dimension therefore requires a forced reindex (`bb index --force`), and a rate
337
+ limit or provider blip degrades one batch to the local fallback — never a
338
+ silent cross-space comparison.
339
+
340
+ ## Tool evidence semantics
341
+
342
+ `bb_ask` uses embeddings to discover and rank candidate symbols. Similarity is
343
+ not proof: factual claims should resolve to indexed symbols, source locations,
344
+ and relationships. `bb_doc` follows the same rule and classifies unsupported
345
+ claims as unknown rather than treating semantic proximity as evidence. `bb_health`
346
+ reports the active model, vector dimensions, and coverage; coverage describes
347
+ stored vectors, not answer quality. Changing model or dimensions requires a
348
+ forced reindex because vectors from different spaces are never compared.
349
+
350
+ ## Verification
351
+
352
+ ```bash
353
+ bun run script/verify-embed.ts # mock API + fallback
354
+ bun run script/m16/verify-embedding-lifecycle.ts # durable vectors + cleanup ordering
355
+ ```