trail-code 0.0.0-stage → 0.28.0-semantic.3

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 (593) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +567 -2
  3. package/dist/ai/crux.d.ts +88 -0
  4. package/dist/ai/crux.d.ts.map +1 -0
  5. package/dist/ai/crux.js +219 -0
  6. package/dist/ai/crux.js.map +1 -0
  7. package/dist/ai/failure.d.ts +60 -0
  8. package/dist/ai/failure.d.ts.map +1 -0
  9. package/dist/ai/failure.js +86 -0
  10. package/dist/ai/failure.js.map +1 -0
  11. package/dist/ai/llm/anthropic.d.ts +37 -0
  12. package/dist/ai/llm/anthropic.d.ts.map +1 -0
  13. package/dist/ai/llm/anthropic.js +144 -0
  14. package/dist/ai/llm/anthropic.js.map +1 -0
  15. package/dist/ai/llm/factory.d.ts +28 -0
  16. package/dist/ai/llm/factory.d.ts.map +1 -0
  17. package/dist/ai/llm/factory.js +36 -0
  18. package/dist/ai/llm/factory.js.map +1 -0
  19. package/dist/ai/llm/litellm.d.ts +34 -0
  20. package/dist/ai/llm/litellm.d.ts.map +1 -0
  21. package/dist/ai/llm/litellm.js +45 -0
  22. package/dist/ai/llm/litellm.js.map +1 -0
  23. package/dist/ai/llm/openai.d.ts +145 -0
  24. package/dist/ai/llm/openai.d.ts.map +1 -0
  25. package/dist/ai/llm/openai.js +629 -0
  26. package/dist/ai/llm/openai.js.map +1 -0
  27. package/dist/ai/llm/orcarouter.d.ts +34 -0
  28. package/dist/ai/llm/orcarouter.d.ts.map +1 -0
  29. package/dist/ai/llm/orcarouter.js +45 -0
  30. package/dist/ai/llm/orcarouter.js.map +1 -0
  31. package/dist/ai/llm/recover-tool.d.ts +21 -0
  32. package/dist/ai/llm/recover-tool.d.ts.map +1 -0
  33. package/dist/ai/llm/recover-tool.js +109 -0
  34. package/dist/ai/llm/recover-tool.js.map +1 -0
  35. package/dist/ai/llm/types.d.ts +122 -0
  36. package/dist/ai/llm/types.d.ts.map +1 -0
  37. package/dist/ai/llm/types.js +33 -0
  38. package/dist/ai/llm/types.js.map +1 -0
  39. package/dist/ai/providers.d.ts +59 -0
  40. package/dist/ai/providers.d.ts.map +1 -0
  41. package/dist/ai/providers.js +54 -0
  42. package/dist/ai/providers.js.map +1 -0
  43. package/dist/ai/summarize.d.ts +25 -0
  44. package/dist/ai/summarize.d.ts.map +1 -0
  45. package/dist/ai/summarize.js +34 -0
  46. package/dist/ai/summarize.js.map +1 -0
  47. package/dist/ai/synthesize.d.ts +40 -0
  48. package/dist/ai/synthesize.d.ts.map +1 -0
  49. package/dist/ai/synthesize.js +230 -0
  50. package/dist/ai/synthesize.js.map +1 -0
  51. package/dist/ask/ask.d.ts +199 -0
  52. package/dist/ask/ask.d.ts.map +1 -0
  53. package/dist/ask/ask.js +1291 -0
  54. package/dist/ask/ask.js.map +1 -0
  55. package/dist/ask/file-rank.d.ts +72 -0
  56. package/dist/ask/file-rank.d.ts.map +1 -0
  57. package/dist/ask/file-rank.js +160 -0
  58. package/dist/ask/file-rank.js.map +1 -0
  59. package/dist/ask/file-selection.d.ts +22 -0
  60. package/dist/ask/file-selection.d.ts.map +1 -0
  61. package/dist/ask/file-selection.js +66 -0
  62. package/dist/ask/file-selection.js.map +1 -0
  63. package/dist/ask/fuse.d.ts +195 -0
  64. package/dist/ask/fuse.d.ts.map +1 -0
  65. package/dist/ask/fuse.js +344 -0
  66. package/dist/ask/fuse.js.map +1 -0
  67. package/dist/ask/graphrank.d.ts +61 -0
  68. package/dist/ask/graphrank.d.ts.map +1 -0
  69. package/dist/ask/graphrank.js +130 -0
  70. package/dist/ask/graphrank.js.map +1 -0
  71. package/dist/ask/index-file.d.ts +44 -0
  72. package/dist/ask/index-file.d.ts.map +1 -0
  73. package/dist/ask/index-file.js +128 -0
  74. package/dist/ask/index-file.js.map +1 -0
  75. package/dist/ask/semantic.d.ts +42 -0
  76. package/dist/ask/semantic.d.ts.map +1 -0
  77. package/dist/ask/semantic.js +293 -0
  78. package/dist/ask/semantic.js.map +1 -0
  79. package/dist/bin/graft.d.ts +3 -0
  80. package/dist/bin/graft.d.ts.map +1 -0
  81. package/dist/bin/graft.js +14 -0
  82. package/dist/bin/graft.js.map +1 -0
  83. package/dist/bin/trail.d.ts +3 -0
  84. package/dist/bin/trail.d.ts.map +1 -0
  85. package/dist/bin/trail.js +13 -0
  86. package/dist/bin/trail.js.map +1 -0
  87. package/dist/blast/blast-cli.d.ts +35 -0
  88. package/dist/blast/blast-cli.d.ts.map +1 -0
  89. package/dist/blast/blast-cli.js +155 -0
  90. package/dist/blast/blast-cli.js.map +1 -0
  91. package/dist/blast/blast.d.ts +128 -0
  92. package/dist/blast/blast.d.ts.map +1 -0
  93. package/dist/blast/blast.js +383 -0
  94. package/dist/blast/blast.js.map +1 -0
  95. package/dist/blast/diff.d.ts +53 -0
  96. package/dist/blast/diff.d.ts.map +1 -0
  97. package/dist/blast/diff.js +193 -0
  98. package/dist/blast/diff.js.map +1 -0
  99. package/dist/blast/evidence.d.ts +51 -0
  100. package/dist/blast/evidence.d.ts.map +1 -0
  101. package/dist/blast/evidence.js +162 -0
  102. package/dist/blast/evidence.js.map +1 -0
  103. package/dist/blast/modules.d.ts +33 -0
  104. package/dist/blast/modules.d.ts.map +1 -0
  105. package/dist/blast/modules.js +83 -0
  106. package/dist/blast/modules.js.map +1 -0
  107. package/dist/blast/name.d.ts +74 -0
  108. package/dist/blast/name.d.ts.map +1 -0
  109. package/dist/blast/name.js +269 -0
  110. package/dist/blast/name.js.map +1 -0
  111. package/dist/blast/owners.d.ts +85 -0
  112. package/dist/blast/owners.d.ts.map +1 -0
  113. package/dist/blast/owners.js +272 -0
  114. package/dist/blast/owners.js.map +1 -0
  115. package/dist/blast/render.d.ts +46 -0
  116. package/dist/blast/render.d.ts.map +1 -0
  117. package/dist/blast/render.js +422 -0
  118. package/dist/blast/render.js.map +1 -0
  119. package/dist/blast/viz.d.ts +37 -0
  120. package/dist/blast/viz.d.ts.map +1 -0
  121. package/dist/blast/viz.js +180 -0
  122. package/dist/blast/viz.js.map +1 -0
  123. package/dist/brand.d.ts +111 -0
  124. package/dist/brand.d.ts.map +1 -0
  125. package/dist/brand.js +282 -0
  126. package/dist/brand.js.map +1 -0
  127. package/dist/claude/build-progress.d.ts +30 -0
  128. package/dist/claude/build-progress.d.ts.map +1 -0
  129. package/dist/claude/build-progress.js +81 -0
  130. package/dist/claude/build-progress.js.map +1 -0
  131. package/dist/claude/hermes-skill-template.d.ts +16 -0
  132. package/dist/claude/hermes-skill-template.d.ts.map +1 -0
  133. package/dist/claude/hermes-skill-template.js +154 -0
  134. package/dist/claude/hermes-skill-template.js.map +1 -0
  135. package/dist/claude/init.d.ts +65 -0
  136. package/dist/claude/init.d.ts.map +1 -0
  137. package/dist/claude/init.js +258 -0
  138. package/dist/claude/init.js.map +1 -0
  139. package/dist/claude/paths.d.ts +9 -0
  140. package/dist/claude/paths.d.ts.map +1 -0
  141. package/dist/claude/paths.js +22 -0
  142. package/dist/claude/paths.js.map +1 -0
  143. package/dist/claude/session-metrics.d.ts +73 -0
  144. package/dist/claude/session-metrics.d.ts.map +1 -0
  145. package/dist/claude/session-metrics.js +209 -0
  146. package/dist/claude/session-metrics.js.map +1 -0
  147. package/dist/claude/settings-merge.d.ts +76 -0
  148. package/dist/claude/settings-merge.d.ts.map +1 -0
  149. package/dist/claude/settings-merge.js +283 -0
  150. package/dist/claude/settings-merge.js.map +1 -0
  151. package/dist/claude/skill-template.d.ts +13 -0
  152. package/dist/claude/skill-template.d.ts.map +1 -0
  153. package/dist/claude/skill-template.js +290 -0
  154. package/dist/claude/skill-template.js.map +1 -0
  155. package/dist/claude/state.d.ts +85 -0
  156. package/dist/claude/state.d.ts.map +1 -0
  157. package/dist/claude/state.js +88 -0
  158. package/dist/claude/state.js.map +1 -0
  159. package/dist/claude/stats.d.ts +5 -0
  160. package/dist/claude/stats.d.ts.map +1 -0
  161. package/dist/claude/stats.js +24 -0
  162. package/dist/claude/stats.js.map +1 -0
  163. package/dist/cli-epilogue.d.ts +12 -0
  164. package/dist/cli-epilogue.d.ts.map +1 -0
  165. package/dist/cli-epilogue.js +72 -0
  166. package/dist/cli-epilogue.js.map +1 -0
  167. package/dist/cli-meta.d.ts +82 -0
  168. package/dist/cli-meta.d.ts.map +1 -0
  169. package/dist/cli-meta.js +238 -0
  170. package/dist/cli-meta.js.map +1 -0
  171. package/dist/cli-picker.d.ts +139 -0
  172. package/dist/cli-picker.d.ts.map +1 -0
  173. package/dist/cli-picker.js +439 -0
  174. package/dist/cli-picker.js.map +1 -0
  175. package/dist/cli.d.ts +3 -0
  176. package/dist/cli.d.ts.map +1 -0
  177. package/dist/cli.js +1638 -0
  178. package/dist/cli.js.map +1 -0
  179. package/dist/context/batches.d.ts +29 -0
  180. package/dist/context/batches.d.ts.map +1 -0
  181. package/dist/context/batches.js +130 -0
  182. package/dist/context/batches.js.map +1 -0
  183. package/dist/context/build.d.ts +57 -0
  184. package/dist/context/build.d.ts.map +1 -0
  185. package/dist/context/build.js +396 -0
  186. package/dist/context/build.js.map +1 -0
  187. package/dist/context/check.d.ts +42 -0
  188. package/dist/context/check.d.ts.map +1 -0
  189. package/dist/context/check.js +162 -0
  190. package/dist/context/check.js.map +1 -0
  191. package/dist/context/map-dir.d.ts +13 -0
  192. package/dist/context/map-dir.d.ts.map +1 -0
  193. package/dist/context/map-dir.js +81 -0
  194. package/dist/context/map-dir.js.map +1 -0
  195. package/dist/context/node-file.d.ts +109 -0
  196. package/dist/context/node-file.d.ts.map +1 -0
  197. package/dist/context/node-file.js +390 -0
  198. package/dist/context/node-file.js.map +1 -0
  199. package/dist/context/price.d.ts +49 -0
  200. package/dist/context/price.d.ts.map +1 -0
  201. package/dist/context/price.js +83 -0
  202. package/dist/context/price.js.map +1 -0
  203. package/dist/context/savings.d.ts +67 -0
  204. package/dist/context/savings.d.ts.map +1 -0
  205. package/dist/context/savings.js +125 -0
  206. package/dist/context/savings.js.map +1 -0
  207. package/dist/engine.d.ts +85 -0
  208. package/dist/engine.d.ts.map +1 -0
  209. package/dist/engine.js +122 -0
  210. package/dist/engine.js.map +1 -0
  211. package/dist/graph/bindings.d.ts +64 -0
  212. package/dist/graph/bindings.d.ts.map +1 -0
  213. package/dist/graph/bindings.js +713 -0
  214. package/dist/graph/bindings.js.map +1 -0
  215. package/dist/graph/build-run.d.ts +25 -0
  216. package/dist/graph/build-run.d.ts.map +1 -0
  217. package/dist/graph/build-run.js +121 -0
  218. package/dist/graph/build-run.js.map +1 -0
  219. package/dist/graph/build.d.ts +88 -0
  220. package/dist/graph/build.d.ts.map +1 -0
  221. package/dist/graph/build.js +386 -0
  222. package/dist/graph/build.js.map +1 -0
  223. package/dist/graph/cards.d.ts +46 -0
  224. package/dist/graph/cards.d.ts.map +1 -0
  225. package/dist/graph/cards.js +280 -0
  226. package/dist/graph/cards.js.map +1 -0
  227. package/dist/graph/check.d.ts +25 -0
  228. package/dist/graph/check.d.ts.map +1 -0
  229. package/dist/graph/check.js +192 -0
  230. package/dist/graph/check.js.map +1 -0
  231. package/dist/graph/container.d.ts +63 -0
  232. package/dist/graph/container.d.ts.map +1 -0
  233. package/dist/graph/container.js +191 -0
  234. package/dist/graph/container.js.map +1 -0
  235. package/dist/graph/depth-parser.d.ts +61 -0
  236. package/dist/graph/depth-parser.d.ts.map +1 -0
  237. package/dist/graph/depth-parser.js +153 -0
  238. package/dist/graph/depth-parser.js.map +1 -0
  239. package/dist/graph/enrich.d.ts +62 -0
  240. package/dist/graph/enrich.d.ts.map +1 -0
  241. package/dist/graph/enrich.js +258 -0
  242. package/dist/graph/enrich.js.map +1 -0
  243. package/dist/graph/extract-cache.d.ts +101 -0
  244. package/dist/graph/extract-cache.d.ts.map +1 -0
  245. package/dist/graph/extract-cache.js +199 -0
  246. package/dist/graph/extract-cache.js.map +1 -0
  247. package/dist/graph/extract.d.ts +85 -0
  248. package/dist/graph/extract.d.ts.map +1 -0
  249. package/dist/graph/extract.js +2377 -0
  250. package/dist/graph/extract.js.map +1 -0
  251. package/dist/graph/fingerprint.d.ts +76 -0
  252. package/dist/graph/fingerprint.d.ts.map +1 -0
  253. package/dist/graph/fingerprint.js +160 -0
  254. package/dist/graph/fingerprint.js.map +1 -0
  255. package/dist/graph/generic.d.ts +74 -0
  256. package/dist/graph/generic.d.ts.map +1 -0
  257. package/dist/graph/generic.js +773 -0
  258. package/dist/graph/generic.js.map +1 -0
  259. package/dist/graph/invariants.d.ts +33 -0
  260. package/dist/graph/invariants.d.ts.map +1 -0
  261. package/dist/graph/invariants.js +52 -0
  262. package/dist/graph/invariants.js.map +1 -0
  263. package/dist/graph/load.d.ts +29 -0
  264. package/dist/graph/load.d.ts.map +1 -0
  265. package/dist/graph/load.js +97 -0
  266. package/dist/graph/load.js.map +1 -0
  267. package/dist/graph/lsp/client.d.ts +61 -0
  268. package/dist/graph/lsp/client.d.ts.map +1 -0
  269. package/dist/graph/lsp/client.js +217 -0
  270. package/dist/graph/lsp/client.js.map +1 -0
  271. package/dist/graph/lsp/enrich.d.ts +11 -0
  272. package/dist/graph/lsp/enrich.d.ts.map +1 -0
  273. package/dist/graph/lsp/enrich.js +163 -0
  274. package/dist/graph/lsp/enrich.js.map +1 -0
  275. package/dist/graph/lsp/registry.d.ts +22 -0
  276. package/dist/graph/lsp/registry.d.ts.map +1 -0
  277. package/dist/graph/lsp/registry.js +71 -0
  278. package/dist/graph/lsp/registry.js.map +1 -0
  279. package/dist/graph/map.d.ts +107 -0
  280. package/dist/graph/map.d.ts.map +1 -0
  281. package/dist/graph/map.js +239 -0
  282. package/dist/graph/map.js.map +1 -0
  283. package/dist/graph/markdown.d.ts +22 -0
  284. package/dist/graph/markdown.d.ts.map +1 -0
  285. package/dist/graph/markdown.js +143 -0
  286. package/dist/graph/markdown.js.map +1 -0
  287. package/dist/graph/native.d.ts +46 -0
  288. package/dist/graph/native.d.ts.map +1 -0
  289. package/dist/graph/native.js +169 -0
  290. package/dist/graph/native.js.map +1 -0
  291. package/dist/graph/queries/c.scm +13 -0
  292. package/dist/graph/queries/c_sharp.scm +19 -0
  293. package/dist/graph/queries/clojure.scm +58 -0
  294. package/dist/graph/queries/cpp.scm +23 -0
  295. package/dist/graph/queries/dart.scm +42 -0
  296. package/dist/graph/queries/elixir.scm +54 -0
  297. package/dist/graph/queries/gleam.scm +51 -0
  298. package/dist/graph/queries/java.scm +20 -0
  299. package/dist/graph/queries/kotlin.scm +43 -0
  300. package/dist/graph/queries/lua.scm +36 -0
  301. package/dist/graph/queries/nix.scm +32 -0
  302. package/dist/graph/queries/php.scm +40 -0
  303. package/dist/graph/queries/ruby.scm +65 -0
  304. package/dist/graph/queries/rust.scm +41 -0
  305. package/dist/graph/queries/scala.scm +66 -0
  306. package/dist/graph/queries/solidity.scm +43 -0
  307. package/dist/graph/queries/swift.scm +51 -0
  308. package/dist/graph/refresh.d.ts +106 -0
  309. package/dist/graph/refresh.d.ts.map +1 -0
  310. package/dist/graph/refresh.js +430 -0
  311. package/dist/graph/refresh.js.map +1 -0
  312. package/dist/graph/relations.d.ts +14 -0
  313. package/dist/graph/relations.d.ts.map +1 -0
  314. package/dist/graph/relations.js +8 -0
  315. package/dist/graph/relations.js.map +1 -0
  316. package/dist/graph/resolve.d.ts +17 -0
  317. package/dist/graph/resolve.d.ts.map +1 -0
  318. package/dist/graph/resolve.js +716 -0
  319. package/dist/graph/resolve.js.map +1 -0
  320. package/dist/graph/root.d.ts +11 -0
  321. package/dist/graph/root.d.ts.map +1 -0
  322. package/dist/graph/root.js +40 -0
  323. package/dist/graph/root.js.map +1 -0
  324. package/dist/graph/scopes.d.ts +40 -0
  325. package/dist/graph/scopes.d.ts.map +1 -0
  326. package/dist/graph/scopes.js +383 -0
  327. package/dist/graph/scopes.js.map +1 -0
  328. package/dist/graph/seed.d.ts +38 -0
  329. package/dist/graph/seed.d.ts.map +1 -0
  330. package/dist/graph/seed.js +229 -0
  331. package/dist/graph/seed.js.map +1 -0
  332. package/dist/graph/source-files.d.ts +46 -0
  333. package/dist/graph/source-files.d.ts.map +1 -0
  334. package/dist/graph/source-files.js +118 -0
  335. package/dist/graph/source-files.js.map +1 -0
  336. package/dist/graph/traverse-cli.d.ts +55 -0
  337. package/dist/graph/traverse-cli.d.ts.map +1 -0
  338. package/dist/graph/traverse-cli.js +189 -0
  339. package/dist/graph/traverse-cli.js.map +1 -0
  340. package/dist/graph/traverse.d.ts +112 -0
  341. package/dist/graph/traverse.d.ts.map +1 -0
  342. package/dist/graph/traverse.js +192 -0
  343. package/dist/graph/traverse.js.map +1 -0
  344. package/dist/graph/types.d.ts +78 -0
  345. package/dist/graph/types.d.ts.map +1 -0
  346. package/dist/graph/types.js +14 -0
  347. package/dist/graph/types.js.map +1 -0
  348. package/dist/graph/wasm-runtime.d.ts +19 -0
  349. package/dist/graph/wasm-runtime.d.ts.map +1 -0
  350. package/dist/graph/wasm-runtime.js +38 -0
  351. package/dist/graph/wasm-runtime.js.map +1 -0
  352. package/dist/graph/workspace-cli.d.ts +56 -0
  353. package/dist/graph/workspace-cli.d.ts.map +1 -0
  354. package/dist/graph/workspace-cli.js +111 -0
  355. package/dist/graph/workspace-cli.js.map +1 -0
  356. package/dist/graph/workspace.d.ts +172 -0
  357. package/dist/graph/workspace.d.ts.map +1 -0
  358. package/dist/graph/workspace.js +714 -0
  359. package/dist/graph/workspace.js.map +1 -0
  360. package/dist/graph/write.d.ts +13 -0
  361. package/dist/graph/write.d.ts.map +1 -0
  362. package/dist/graph/write.js +86 -0
  363. package/dist/graph/write.js.map +1 -0
  364. package/dist/hosts/antigravity.d.ts +14 -0
  365. package/dist/hosts/antigravity.d.ts.map +1 -0
  366. package/dist/hosts/antigravity.js +59 -0
  367. package/dist/hosts/antigravity.js.map +1 -0
  368. package/dist/hosts/auto.d.ts +37 -0
  369. package/dist/hosts/auto.d.ts.map +1 -0
  370. package/dist/hosts/auto.js +141 -0
  371. package/dist/hosts/auto.js.map +1 -0
  372. package/dist/hosts/claude-global.d.ts +72 -0
  373. package/dist/hosts/claude-global.d.ts.map +1 -0
  374. package/dist/hosts/claude-global.js +230 -0
  375. package/dist/hosts/claude-global.js.map +1 -0
  376. package/dist/hosts/config-write.d.ts +36 -0
  377. package/dist/hosts/config-write.d.ts.map +1 -0
  378. package/dist/hosts/config-write.js +64 -0
  379. package/dist/hosts/config-write.js.map +1 -0
  380. package/dist/hosts/init.d.ts +28 -0
  381. package/dist/hosts/init.d.ts.map +1 -0
  382. package/dist/hosts/init.js +89 -0
  383. package/dist/hosts/init.js.map +1 -0
  384. package/dist/hosts/instructions.d.ts +14 -0
  385. package/dist/hosts/instructions.d.ts.map +1 -0
  386. package/dist/hosts/instructions.js +95 -0
  387. package/dist/hosts/instructions.js.map +1 -0
  388. package/dist/hosts/mcp-config.d.ts +78 -0
  389. package/dist/hosts/mcp-config.d.ts.map +1 -0
  390. package/dist/hosts/mcp-config.js +251 -0
  391. package/dist/hosts/mcp-config.js.map +1 -0
  392. package/dist/hosts/plan.d.ts +32 -0
  393. package/dist/hosts/plan.d.ts.map +1 -0
  394. package/dist/hosts/plan.js +73 -0
  395. package/dist/hosts/plan.js.map +1 -0
  396. package/dist/hosts/registry.d.ts +41 -0
  397. package/dist/hosts/registry.d.ts.map +1 -0
  398. package/dist/hosts/registry.js +164 -0
  399. package/dist/hosts/registry.js.map +1 -0
  400. package/dist/hosts/retract.d.ts +47 -0
  401. package/dist/hosts/retract.d.ts.map +1 -0
  402. package/dist/hosts/retract.js +520 -0
  403. package/dist/hosts/retract.js.map +1 -0
  404. package/dist/hosts/sections.d.ts +47 -0
  405. package/dist/hosts/sections.d.ts.map +1 -0
  406. package/dist/hosts/sections.js +127 -0
  407. package/dist/hosts/sections.js.map +1 -0
  408. package/dist/index.d.ts +41 -0
  409. package/dist/index.d.ts.map +1 -0
  410. package/dist/index.js +32 -0
  411. package/dist/index.js.map +1 -0
  412. package/dist/ingest/fs.d.ts +79 -0
  413. package/dist/ingest/fs.d.ts.map +1 -0
  414. package/dist/ingest/fs.js +306 -0
  415. package/dist/ingest/fs.js.map +1 -0
  416. package/dist/init-guard.d.ts +16 -0
  417. package/dist/init-guard.d.ts.map +1 -0
  418. package/dist/init-guard.js +27 -0
  419. package/dist/init-guard.js.map +1 -0
  420. package/dist/mcp/instructions.d.ts +5 -0
  421. package/dist/mcp/instructions.d.ts.map +1 -0
  422. package/dist/mcp/instructions.js +64 -0
  423. package/dist/mcp/instructions.js.map +1 -0
  424. package/dist/mcp/kept-listing.d.ts +27 -0
  425. package/dist/mcp/kept-listing.d.ts.map +1 -0
  426. package/dist/mcp/kept-listing.js +63 -0
  427. package/dist/mcp/kept-listing.js.map +1 -0
  428. package/dist/mcp/server.d.ts +4 -0
  429. package/dist/mcp/server.d.ts.map +1 -0
  430. package/dist/mcp/server.js +147 -0
  431. package/dist/mcp/server.js.map +1 -0
  432. package/dist/mcp/tool-names.d.ts +27 -0
  433. package/dist/mcp/tool-names.d.ts.map +1 -0
  434. package/dist/mcp/tool-names.js +47 -0
  435. package/dist/mcp/tool-names.js.map +1 -0
  436. package/dist/mcp/tools.d.ts +22 -0
  437. package/dist/mcp/tools.d.ts.map +1 -0
  438. package/dist/mcp/tools.js +440 -0
  439. package/dist/mcp/tools.js.map +1 -0
  440. package/dist/node-version.d.ts +15 -0
  441. package/dist/node-version.d.ts.map +1 -0
  442. package/dist/node-version.js +23 -0
  443. package/dist/node-version.js.map +1 -0
  444. package/dist/search/grep-cli.d.ts +35 -0
  445. package/dist/search/grep-cli.d.ts.map +1 -0
  446. package/dist/search/grep-cli.js +115 -0
  447. package/dist/search/grep-cli.js.map +1 -0
  448. package/dist/search/grep.d.ts +60 -0
  449. package/dist/search/grep.d.ts.map +1 -0
  450. package/dist/search/grep.js +143 -0
  451. package/dist/search/grep.js.map +1 -0
  452. package/dist/search/ignored-code.d.ts +9 -0
  453. package/dist/search/ignored-code.d.ts.map +1 -0
  454. package/dist/search/ignored-code.js +73 -0
  455. package/dist/search/ignored-code.js.map +1 -0
  456. package/dist/search/user-regexp.d.ts +26 -0
  457. package/dist/search/user-regexp.d.ts.map +1 -0
  458. package/dist/search/user-regexp.js +150 -0
  459. package/dist/search/user-regexp.js.map +1 -0
  460. package/dist/telemetry/contract.d.ts +117 -0
  461. package/dist/telemetry/contract.d.ts.map +1 -0
  462. package/dist/telemetry/contract.js +341 -0
  463. package/dist/telemetry/contract.js.map +1 -0
  464. package/dist/telemetry/flush.d.ts +21 -0
  465. package/dist/telemetry/flush.d.ts.map +1 -0
  466. package/dist/telemetry/flush.js +85 -0
  467. package/dist/telemetry/flush.js.map +1 -0
  468. package/dist/telemetry/gate.d.ts +30 -0
  469. package/dist/telemetry/gate.d.ts.map +1 -0
  470. package/dist/telemetry/gate.js +81 -0
  471. package/dist/telemetry/gate.js.map +1 -0
  472. package/dist/telemetry/identity.d.ts +40 -0
  473. package/dist/telemetry/identity.d.ts.map +1 -0
  474. package/dist/telemetry/identity.js +80 -0
  475. package/dist/telemetry/identity.js.map +1 -0
  476. package/dist/telemetry/index.d.ts +18 -0
  477. package/dist/telemetry/index.d.ts.map +1 -0
  478. package/dist/telemetry/index.js +16 -0
  479. package/dist/telemetry/index.js.map +1 -0
  480. package/dist/telemetry/key.d.ts +21 -0
  481. package/dist/telemetry/key.d.ts.map +1 -0
  482. package/dist/telemetry/key.js +49 -0
  483. package/dist/telemetry/key.js.map +1 -0
  484. package/dist/telemetry/notice.d.ts +19 -0
  485. package/dist/telemetry/notice.d.ts.map +1 -0
  486. package/dist/telemetry/notice.js +93 -0
  487. package/dist/telemetry/notice.js.map +1 -0
  488. package/dist/telemetry/queue.d.ts +35 -0
  489. package/dist/telemetry/queue.d.ts.map +1 -0
  490. package/dist/telemetry/queue.js +162 -0
  491. package/dist/telemetry/queue.js.map +1 -0
  492. package/dist/telemetry/send.d.ts +19 -0
  493. package/dist/telemetry/send.d.ts.map +1 -0
  494. package/dist/telemetry/send.js +84 -0
  495. package/dist/telemetry/send.js.map +1 -0
  496. package/dist/telemetry/sessions.d.ts +25 -0
  497. package/dist/telemetry/sessions.d.ts.map +1 -0
  498. package/dist/telemetry/sessions.js +104 -0
  499. package/dist/telemetry/sessions.js.map +1 -0
  500. package/dist/telemetry/track.d.ts +62 -0
  501. package/dist/telemetry/track.d.ts.map +1 -0
  502. package/dist/telemetry/track.js +143 -0
  503. package/dist/telemetry/track.js.map +1 -0
  504. package/dist/upkeep-run.d.ts +22 -0
  505. package/dist/upkeep-run.d.ts.map +1 -0
  506. package/dist/upkeep-run.js +85 -0
  507. package/dist/upkeep-run.js.map +1 -0
  508. package/dist/upkeep.d.ts +127 -0
  509. package/dist/upkeep.d.ts.map +1 -0
  510. package/dist/upkeep.js +252 -0
  511. package/dist/upkeep.js.map +1 -0
  512. package/dist/util/actions-folder.d.ts +2 -0
  513. package/dist/util/actions-folder.d.ts.map +1 -0
  514. package/dist/util/actions-folder.js +9 -0
  515. package/dist/util/actions-folder.js.map +1 -0
  516. package/dist/util/browser.d.ts +3 -0
  517. package/dist/util/browser.d.ts.map +1 -0
  518. package/dist/util/browser.js +14 -0
  519. package/dist/util/browser.js.map +1 -0
  520. package/dist/util/child-process.d.ts +15 -0
  521. package/dist/util/child-process.d.ts.map +1 -0
  522. package/dist/util/child-process.js +15 -0
  523. package/dist/util/child-process.js.map +1 -0
  524. package/dist/util/front-matter.d.ts +46 -0
  525. package/dist/util/front-matter.d.ts.map +1 -0
  526. package/dist/util/front-matter.js +113 -0
  527. package/dist/util/front-matter.js.map +1 -0
  528. package/dist/util/id.d.ts +7 -0
  529. package/dist/util/id.d.ts.map +1 -0
  530. package/dist/util/id.js +14 -0
  531. package/dist/util/id.js.map +1 -0
  532. package/dist/util/paths.d.ts +29 -0
  533. package/dist/util/paths.d.ts.map +1 -0
  534. package/dist/util/paths.js +71 -0
  535. package/dist/util/paths.js.map +1 -0
  536. package/dist/util/private-file.d.ts +14 -0
  537. package/dist/util/private-file.d.ts.map +1 -0
  538. package/dist/util/private-file.js +82 -0
  539. package/dist/util/private-file.js.map +1 -0
  540. package/dist/util/source.d.ts +4 -0
  541. package/dist/util/source.d.ts.map +1 -0
  542. package/dist/util/source.js +12 -0
  543. package/dist/util/source.js.map +1 -0
  544. package/dist/util/spinner.d.ts +15 -0
  545. package/dist/util/spinner.d.ts.map +1 -0
  546. package/dist/util/spinner.js +52 -0
  547. package/dist/util/spinner.js.map +1 -0
  548. package/dist/util/state.d.ts +120 -0
  549. package/dist/util/state.d.ts.map +1 -0
  550. package/dist/util/state.js +264 -0
  551. package/dist/util/state.js.map +1 -0
  552. package/dist/vendor/grammars/tree-sitter-go.LICENSE +21 -0
  553. package/dist/vendor/grammars/tree-sitter-go.wasm +0 -0
  554. package/dist/vendor/grammars/tree-sitter-java.LICENSE +21 -0
  555. package/dist/vendor/grammars/tree-sitter-java.wasm +0 -0
  556. package/dist/vendor/grammars/tree-sitter-kotlin.LICENSE +9 -0
  557. package/dist/vendor/grammars/tree-sitter-kotlin.wasm +0 -0
  558. package/dist/vendor/grammars/tree-sitter-php.LICENSE +22 -0
  559. package/dist/vendor/grammars/tree-sitter-php.wasm +0 -0
  560. package/dist/vendor/grammars/tree-sitter-python.LICENSE +21 -0
  561. package/dist/vendor/grammars/tree-sitter-python.wasm +0 -0
  562. package/dist/vendor/grammars/tree-sitter-r.LICENSE +9 -0
  563. package/dist/vendor/grammars/tree-sitter-r.wasm +0 -0
  564. package/dist/vendor/grammars/tree-sitter-swift.LICENSE +21 -0
  565. package/dist/vendor/grammars/tree-sitter-swift.wasm +0 -0
  566. package/dist/vendor/grammars/tree-sitter-tsx.wasm +0 -0
  567. package/dist/vendor/grammars/tree-sitter-typescript.LICENSE +21 -0
  568. package/dist/vendor/grammars/tree-sitter-typescript.wasm +0 -0
  569. package/dist/viewer/app.js +2 -0
  570. package/dist/viewer/index.html +49 -0
  571. package/dist/viewer/style.css +154 -0
  572. package/dist/viz/assemble.d.ts +83 -0
  573. package/dist/viz/assemble.d.ts.map +1 -0
  574. package/dist/viz/assemble.js +101 -0
  575. package/dist/viz/assemble.js.map +1 -0
  576. package/dist/viz/export.d.ts +39 -0
  577. package/dist/viz/export.d.ts.map +1 -0
  578. package/dist/viz/export.js +98 -0
  579. package/dist/viz/export.js.map +1 -0
  580. package/dist/viz/serve.d.ts +13 -0
  581. package/dist/viz/serve.d.ts.map +1 -0
  582. package/dist/viz/serve.js +130 -0
  583. package/dist/viz/serve.js.map +1 -0
  584. package/package.json +115 -4
  585. package/scripts/build-grammar-wasm.mjs +112 -0
  586. package/scripts/build-viewer.mjs +44 -0
  587. package/scripts/graph-quality.mjs +117 -0
  588. package/scripts/pack-trail.mjs +53 -0
  589. package/scripts/postinstall.mjs +65 -0
  590. package/scripts/run-tests.mjs +72 -0
  591. package/scripts/stamp-telemetry-key.mjs +56 -0
  592. package/scripts/tally-audit.mjs +140 -0
  593. package/scripts/vendor-grammars.mjs +53 -0
@@ -0,0 +1,714 @@
1
+ /**
2
+ * Workspace federation — a "workspace" is a parent directory that holds two or
3
+ * more immediate git-repo children and has no source graph of its own. Instead
4
+ * of one mega-graph pooling every repo, each child keeps its OWN committable
5
+ * `graft/` (byte-identical to building that child standalone), and the parent
6
+ * holds a single `graft/workspace.json` index:
7
+ *
8
+ * { "version": 1, "children": ["repoA", "repoB"] }
9
+ *
10
+ * Queries run at the parent federate across the children: `ask` fuses every
11
+ * child's ranked hits with `fuseScopes`, reciprocal-rank fusion, so the big
12
+ * repo can't drown the small one. Scopes INSIDE one repo no longer take that
13
+ * path — they share corpus statistics and a normalization denominator, so they
14
+ * are combined by score (see `ask/fuse.ts`). Separate child repositories have
15
+ * no such shared scale, which is exactly the case rank fusion is for.
16
+ * `grep`/`map`/`check`/`callers` run per child and merge, always labeled
17
+ * `<child>/`.
18
+ *
19
+ * This module owns the pure/core pieces (the format, its readers/writers, graph
20
+ * loading, and the federated command bodies that return renderable data). The
21
+ * CLI print/exit wrappers and the per-child build orchestration live in
22
+ * `workspace-cli.ts`; `mcp/tools.ts` calls the federate* functions directly.
23
+ */
24
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
25
+ import { join, resolve, dirname } from "node:path";
26
+ import { contextDirFor } from "../context/node-file.js";
27
+ import { checkGraph } from "./check.js";
28
+ import { loadGraphCached } from "./load.js";
29
+ import { buildRepoMap, formatRepoMap } from "./map.js";
30
+ import { discoverWorkspaceChildren } from "./scopes.js";
31
+ import { callersSavings, headerOf, hitLine, looseNoteFor, } from "./traverse-cli.js";
32
+ import { edgeWalk, resolveSymbol } from "./traverse.js";
33
+ import { wiringPath } from "./write.js";
34
+ import { ask, } from "../ask/ask.js";
35
+ import { fileFirstRoundRobin, roundRobinQueues, } from "../ask/file-selection.js";
36
+ import { fuseScopes, STRONG_FLOOR, HIGH_FLOOR } from "../ask/fuse.js";
37
+ import { grepGraph } from "../search/grep.js";
38
+ import { formatGrepResult, zeroHitNote } from "../search/grep-cli.js";
39
+ import { withSavings } from "../context/savings.js";
40
+ import { cmd } from "../brand.js";
41
+ import { searchSemantic } from "../ask/semantic.js";
42
+ const WORKSPACE_FILE = "workspace.json";
43
+ /** Committed file that stores shared workspace settings (knowledgeRepos).
44
+ * Lives in .trail/ (committed) separate from .trail/cache/workspace.json (git-ignored). */
45
+ const KNOWLEDGE_REPOS_FILE = "knowledge-repos.json";
46
+ /** Absolute path to the knowledge-repos config for a workspace root. Always committed. */
47
+ export function knowledgeReposPath(root) {
48
+ return join(root, ".trail", KNOWLEDGE_REPOS_FILE);
49
+ }
50
+ /** Read the list of knowledge repo URLs for a workspace root. */
51
+ export function readKnowledgeRepos(root) {
52
+ try {
53
+ const parsed = JSON.parse(readFileSync(knowledgeReposPath(root), "utf8"));
54
+ return Array.isArray(parsed) ? parsed.map(String) : [];
55
+ }
56
+ catch {
57
+ return [];
58
+ }
59
+ }
60
+ /** Write the list of knowledge repo URLs. Ensures .trail/ exists. */
61
+ export function writeKnowledgeRepos(root, urls) {
62
+ const dir = join(root, ".trail");
63
+ mkdirSync(dir, { recursive: true });
64
+ writeFileSync(knowledgeReposPath(root), JSON.stringify(urls, null, 2) + "\n");
65
+ }
66
+ /** Absolute path to the workspace index for a parent root (`<dir>/graft/workspace.json`). */
67
+ export function workspacePath(root, override) {
68
+ return join(contextDirFor(root, override), WORKSPACE_FILE);
69
+ }
70
+ /** Read the workspace index, or null when the parent has none (not a workspace,
71
+ * or unparseable/foreign json — treated the same as absent). */
72
+ export function readWorkspace(root, override) {
73
+ const path = workspacePath(root, override);
74
+ try {
75
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
76
+ if (parsed.version !== 1 || !Array.isArray(parsed.children))
77
+ return null;
78
+ // knowledgeRepos: read from committed .trail/knowledge-repos.json first,
79
+ // fall back to workspace.json for backward compatibility.
80
+ const knowledgeRepos = readKnowledgeRepos(root).length > 0
81
+ ? readKnowledgeRepos(root)
82
+ : Array.isArray(parsed.knowledgeRepos) ? parsed.knowledgeRepos.map(String) : undefined;
83
+ return {
84
+ version: 1,
85
+ children: parsed.children.map(String),
86
+ excludeFromDiscovery: Array.isArray(parsed.excludeFromDiscovery)
87
+ ? parsed.excludeFromDiscovery.map(String)
88
+ : undefined,
89
+ knowledgeRepos,
90
+ };
91
+ }
92
+ catch {
93
+ return null;
94
+ }
95
+ }
96
+ /** Write the workspace index, sorting children for a stable, minimal git diff.
97
+ * Preserves `excludeFromDiscovery` across rebuilds. */
98
+ export function writeWorkspace(root, ws, override) {
99
+ const dir = contextDirFor(root, override);
100
+ mkdirSync(dir, { recursive: true });
101
+ const path = join(dir, WORKSPACE_FILE);
102
+ const sorted = { version: 1, children: [...ws.children].sort() };
103
+ if (ws.excludeFromDiscovery && ws.excludeFromDiscovery.length > 0)
104
+ sorted.excludeFromDiscovery = [...ws.excludeFromDiscovery].sort();
105
+ if (ws.knowledgeRepos && ws.knowledgeRepos.length > 0)
106
+ sorted.knowledgeRepos = [...ws.knowledgeRepos];
107
+ writeFileSync(path, JSON.stringify(sorted, null, 2) + "\n");
108
+ return path;
109
+ }
110
+ /** A parent is a workspace BUILD target when it has no own `.git` and ≥2 git
111
+ * children — or it was already split (a `workspace.json` is present). The
112
+ * no-own-`.git` guard keeps a normal repo with git submodules from being
113
+ * mistaken for a workspace. */
114
+ /**
115
+ * Walk up from `dir` to find the nearest directory with knowledgeRepos configured
116
+ * (.trail/knowledge-repos.json). Works for repos, workspaces, and plain folders.
117
+ * Falls back to the first workspace found if none has knowledgeRepos.
118
+ */
119
+ export function findWorkspaceRoot(dir) {
120
+ let current = resolve(dir);
121
+ let firstWorkspace = null;
122
+ for (let i = 0; i < 8; i++) {
123
+ // Check for committed knowledge-repos.json first (works in any git repo).
124
+ if (readKnowledgeRepos(current).length > 0)
125
+ return current;
126
+ const ws = readWorkspace(current);
127
+ if (ws) {
128
+ if (!firstWorkspace)
129
+ firstWorkspace = current;
130
+ if (ws.knowledgeRepos && ws.knowledgeRepos.length > 0)
131
+ return current;
132
+ }
133
+ const parent = dirname(current);
134
+ if (parent === current)
135
+ break;
136
+ current = parent;
137
+ }
138
+ return firstWorkspace;
139
+ }
140
+ export function isWorkspaceBuildRoot(root, override) {
141
+ if (readWorkspace(root, override))
142
+ return true;
143
+ if (existsSync(join(root, ".git")))
144
+ return false;
145
+ return discoverWorkspaceChildren(root).length >= 2;
146
+ }
147
+ /** True when the parent has a mega-graph from an older single-graph build
148
+ * (`graft/.graph/wiring.json`) — the thing a workspace build migrates away. */
149
+ export function hasMegaGraph(root, override) {
150
+ return existsSync(wiringPath(contextDirFor(root, override)));
151
+ }
152
+ /** The EXACT split warning printed once, when a mega-graph parent is first
153
+ * built as a workspace. Templated on the child list so it names the real repos. */
154
+ export function migrationNote(children) {
155
+ const dirs = children.map((c) => `${c}/graft/`).join(", ");
156
+ return (`⚠ this folder contains ${children.length} separate git repos — splitting: ` +
157
+ `each repo now gets its own committable graft/ (${dirs}); the combined graph ` +
158
+ `here is replaced by a workspace index. Queries from here now search all repos, fairly.`);
159
+ }
160
+ /** Remove the parent's entire `graft/` tree — the mega-graph, its `.cache`, and
161
+ * any stale cards — so after `writeWorkspace` the parent holds ONLY
162
+ * workspace.json. Child graphs live in sibling `<child>/graft/`, never under
163
+ * this dir, so they are untouched. */
164
+ export function clearParentGraft(root, override) {
165
+ rmSync(contextDirFor(root, override), { recursive: true, force: true });
166
+ }
167
+ /** Load each child's graph via `loadGraphCached`. Children come from
168
+ * workspace.json when present, otherwise from live git-child discovery (a
169
+ * not-yet-built workspace). A child without a built graph is counted into
170
+ * `missing`, not dropped. */
171
+ export function loadWorkspaceGraphs(root, override) {
172
+ const ws = readWorkspace(root, override);
173
+ const children = (ws ? ws.children : discoverWorkspaceChildren(root)).slice().sort();
174
+ const loaded = [];
175
+ const missing = [];
176
+ for (const child of children) {
177
+ const graph = loadGraphCached(contextDirFor(join(root, child)));
178
+ if (graph)
179
+ loaded.push({ child, graph });
180
+ else
181
+ missing.push(child);
182
+ }
183
+ return { loaded, missing };
184
+ }
185
+ /** "2 of 3 workspace repos have graphs; run graft build to cover repoC" — the
186
+ * coverage line federated commands append when some listed child is unbuilt.
187
+ * Empty string when every child has a graph. */
188
+ export function coverageNote(g) {
189
+ if (g.missing.length === 0)
190
+ return "";
191
+ const total = g.loaded.length + g.missing.length;
192
+ return `${g.loaded.length} of ${total} workspace repos have graphs; run ${cmd("graft build")} to cover ${g.missing.join(", ")}`;
193
+ }
194
+ /** Prefix a hit pointer with its child dir so a `path:span` (or concept path
195
+ * list) opens correctly from the parent. Leaves free-text fragments alone. */
196
+ function prefixPointer(child, pointer) {
197
+ return pointer
198
+ .split(",")
199
+ .map((part) => {
200
+ const t = part.trim();
201
+ if (!t || t.includes(" "))
202
+ return part;
203
+ return `${child}/${t}`;
204
+ })
205
+ .join(", ");
206
+ }
207
+ /** The exact child-relative file owning a lexical symbol hit. File nodes use a
208
+ * bare path; definition hits append `:Lx-Ly`. */
209
+ function hitFile(pointer) {
210
+ return pointer.match(/^(.*):L\d+-L\d+$/)?.[1] ?? pointer;
211
+ }
212
+ // STRONG_FLOOR / HIGH_FLOOR (a child federates if its top hit matched a
213
+ // query term in a NAME/PATH field ≥ STRONG_FLOOR, OR its overall name+path+
214
+ // body coverage is broad enough to be real even body-only, ≥ HIGH_FLOOR) are
215
+ // defined ONCE in `../ask/fuse.js` and imported here. This is now their only
216
+ // caller: a share-of-the-query threshold is the best available signal when the
217
+ // candidates carry no common score scale, which is the situation across child
218
+ // repositories. `rankScopesAndFuse` used to gate on them for the same reason
219
+ // and no longer needs to — within one repo the scores are comparable, so a
220
+ // weak scope is held back by scoring low rather than by a strength floor.
221
+ /**
222
+ * Federated `ask` across a workspace: run each child's own ask pipeline, then
223
+ * fuse ALL the children's scope lists with `fuseScopes` (reciprocal rank). Each
224
+ * child contributes one fusion scope per intra-child scope, labeled
225
+ * `<child>/<scope>` (or just `<child>` for a child's root scope), so a big repo
226
+ * can't drown a small one — rank positions are comparable across repos where
227
+ * raw scores are not.
228
+ *
229
+ * Rank fusion is still right HERE, and only here: each child was scored against
230
+ * its own corpus, so cross-child scores share no scale. Within a single repo
231
+ * that is no longer true — `rankScopesAndFuse` gives every scope the same
232
+ * statistics and the same denominator and combines them by score.
233
+ *
234
+ * Returns a normal `AskResult`; `formatAsk` renders it unchanged, labeling each
235
+ * hit `[<child>/…]` via the standard multi-scope path.
236
+ */
237
+ export function federateAsk(root, override, query, opts = {}) {
238
+ const wg = loadWorkspaceGraphs(root, override);
239
+ const limit = opts.limit ?? 8;
240
+ const fileTopLock = opts.fileTopLock ?? true;
241
+ // `--in` scopes to a single child (and, past the child path, a sub-scope
242
+ // within it). A prefix naming no known child at all is a caller mistake.
243
+ // Children may be compound paths (e.g. "default-core/frontend"), so we
244
+ // find the longest child path that is a prefix of (or equal to) the --in
245
+ // value, rather than splitting on the first "/" only.
246
+ let onlyChild;
247
+ let childIn;
248
+ if (opts.in) {
249
+ const prefix = opts.in.replace(/\/+$/, "");
250
+ const allChildren = [...wg.loaded.map((l) => l.child), ...wg.missing].sort();
251
+ // Longest matching child path wins (e.g. "default-core/frontend" over "default-core").
252
+ const match = allChildren
253
+ .filter((c) => prefix === c || prefix.startsWith(c + "/"))
254
+ .sort((a, b) => b.length - a.length)[0];
255
+ if (!match) {
256
+ throw new Error(`no workspace repo matching "${prefix}" — repos: ${allChildren.join(", ")}`);
257
+ }
258
+ onlyChild = match;
259
+ const remaining = prefix.slice(match.length).replace(/^\//, "");
260
+ childIn = remaining || undefined;
261
+ }
262
+ // Pass 1: run each child's ask; keep its hits + RAW top-hit coverage.
263
+ const runs = [];
264
+ for (const { child } of wg.loaded) {
265
+ if (onlyChild && child !== onlyChild)
266
+ continue;
267
+ let r;
268
+ try {
269
+ // Over-fetch per child so cross-child fusion has enough candidates to
270
+ // rank before the final `limit` slice.
271
+ r = ask(join(root, child), query, {
272
+ limit: Math.max(limit * 4, 20),
273
+ source: opts.source,
274
+ full: opts.full,
275
+ graphRank: opts.graphRank,
276
+ in: childIn,
277
+ // The parent owns the final cross-repo ranking. Selecting inside each
278
+ // child would be undone by fusion and could truncate a hybrid list.
279
+ fileFirst: false,
280
+ fileComplement: fileTopLock,
281
+ includeRankingMetadata: fileTopLock,
282
+ });
283
+ }
284
+ catch {
285
+ continue; // corrupt child, or a sub-scope --in matching nothing here
286
+ }
287
+ if (r.hits.length === 0)
288
+ continue;
289
+ const topGroup = r.ranking?.groups[0];
290
+ runs.push({
291
+ child,
292
+ hits: r.hits,
293
+ coverage: topGroup?.coverage ?? r.coverage ?? 0,
294
+ coverageStrong: topGroup?.coverageStrong ?? r.coverageStrong ?? 0,
295
+ baselineCoverage: r.ranking?.baselineCoverage ?? r.coverage ?? 0,
296
+ baselineCoverageStrong: r.ranking?.baselineCoverageStrong ?? r.coverageStrong ?? 0,
297
+ ranking: r.ranking,
298
+ });
299
+ }
300
+ // Cross-child participation gate on match STRENGTH, not a lenient ratio.
301
+ // Per-child hit scores are each per-child-normalized (top ~1.0 everywhere),
302
+ // so feeding them to fuseScopes makes its gate vacuous across repos — a junk
303
+ // body-token collision would federate beside another repo's genuine hits.
304
+ // Gate: a child federates iff its top hit matched a query term in a NAME/PATH
305
+ // field (coverageStrong ≥ STRONG_FLOOR — not just an incidental body token)
306
+ // OR its overall coverage is broad enough to be real (≥ HIGH_FLOOR). A
307
+ // body-only collision has coverageStrong 0 and coverage ~0.3 → gated to
308
+ // alsoMatched. Both floors are absolute + scale-invariant (see their docs).
309
+ // The gate is a cross-child FAIRNESS mechanism: it stops a weak body-token
310
+ // collision in one repo from federating beside another repo's genuine hits.
311
+ // An explicit `--in <child>` removes all cross-child competition — the caller
312
+ // has already chosen the scope — so the gate must NOT apply there, or the
313
+ // `also matched: <child> — narrow with --in <child>` hint would lead to an
314
+ // empty result for exactly the weak child it points at. With `onlyChild` set,
315
+ // every run (there is at most one) survives, matching a standalone child ask.
316
+ const gatedOut = [];
317
+ const survivors = [];
318
+ const baselineSurvivors = [];
319
+ for (const run of runs) {
320
+ const baselineEligible = onlyChild ||
321
+ run.baselineCoverageStrong >= STRONG_FLOOR ||
322
+ run.baselineCoverage >= HIGH_FLOOR;
323
+ const fileEligible = onlyChild ||
324
+ run.coverageStrong >= STRONG_FLOOR ||
325
+ run.coverage >= HIGH_FLOOR;
326
+ if (baselineEligible)
327
+ baselineSurvivors.push(run);
328
+ // Distributed file evidence may admit a child, but the exact baseline
329
+ // top lock must never resurrect a child that the final stream calls gated
330
+ // out. The production survivor set is therefore the union of both gates;
331
+ // baselineSurvivors remains the strict baseline subset used only to choose the
332
+ // locked pointer.
333
+ if (baselineEligible || fileEligible)
334
+ survivors.push(run);
335
+ else {
336
+ const best = run.ranking?.groups[0]?.hits[0] ?? run.hits[0];
337
+ gatedOut.push({ scope: run.child, bestId: prefixPointer(run.child, best.pointer) });
338
+ }
339
+ }
340
+ if (fileTopLock) {
341
+ const qualifyHit = (child, hit, score = hit.score, scope = hit.scope ? `${child}/${hit.scope}` : child) => ({
342
+ ...hit,
343
+ score,
344
+ scope,
345
+ pointer: prefixPointer(child, hit.pointer),
346
+ });
347
+ const sameHit = (a, b) => a.kind === b.kind && a.pointer === b.pointer && a.title === b.title;
348
+ // Baseline workspace stream: reproduce the all-span RRF exactly so the
349
+ // final lock owns the same concrete pointer and fused score as the baseline.
350
+ const baselineDocs = [];
351
+ const baselineBack = new Map();
352
+ for (const run of baselineSurvivors) {
353
+ const entries = run.ranking?.baseline ?? run.hits.map((hit, index) => ({
354
+ group: `singleton:${index}`,
355
+ hit,
356
+ }));
357
+ entries.forEach((entry, index) => {
358
+ // Keep the legacy id shape so equal-score baseline ties remain exact.
359
+ const id = `${run.child} ${index}`;
360
+ const scope = entry.hit.scope
361
+ ? `${run.child}/${entry.hit.scope}`
362
+ : run.child;
363
+ baselineDocs.push({ id, scope, score: entry.hit.score });
364
+ baselineBack.set(id, {
365
+ child: run.child,
366
+ group: `${run.child}\0${entry.group}`,
367
+ hit: entry.hit,
368
+ });
369
+ });
370
+ }
371
+ const baselineFused = fuseScopes(baselineDocs);
372
+ const baselineTopRanked = baselineFused.ranked[0];
373
+ const baselineTopBack = baselineTopRanked
374
+ ? baselineBack.get(baselineTopRanked.id)
375
+ : undefined;
376
+ const baselineTop = baselineTopRanked && baselineTopBack
377
+ ? qualifyHit(baselineTopBack.child, baselineTopBack.hit, baselineTopRanked.score, baselineTopRanked.scope)
378
+ : undefined;
379
+ // File-aware workspace stream: exactly one leader per child/file (concepts stay
380
+ // singleton groups) participates in RRF. Span queues remain in a side map
381
+ // and cannot consume reciprocal-rank positions.
382
+ const fileDocs = [];
383
+ const fileBack = new Map();
384
+ for (const run of survivors) {
385
+ const groups = run.ranking?.groups ?? run.hits.map((hit, index) => ({
386
+ key: `singleton:${index}`,
387
+ hits: [hit],
388
+ baselineHits: [hit],
389
+ coverage: 0,
390
+ coverageStrong: 0,
391
+ }));
392
+ groups.forEach((group, index) => {
393
+ const leader = group.hits[0];
394
+ if (!leader)
395
+ return;
396
+ const id = `${run.child} file ${String(index).padStart(8, "0")}`;
397
+ const scope = leader.scope ? `${run.child}/${leader.scope}` : run.child;
398
+ const key = `${run.child}\0${group.key}`;
399
+ fileDocs.push({ id, scope, score: leader.score });
400
+ fileBack.set(id, {
401
+ key,
402
+ child: run.child,
403
+ hits: group.hits,
404
+ baselineHits: group.baselineHits ?? [leader],
405
+ });
406
+ });
407
+ }
408
+ const fileFused = fuseScopes(fileDocs);
409
+ const rankedGroups = fileFused.ranked.flatMap((ranked) => {
410
+ const group = fileBack.get(ranked.id);
411
+ if (!group)
412
+ return [];
413
+ return [{
414
+ ...group,
415
+ // These spans are projections of one cross-child file document. Keep
416
+ // their public score on that same RRF scale; child-local tail scores are
417
+ // not comparable after workspace fusion and downstream re-sorting would
418
+ // otherwise silently undo the file ordering.
419
+ hits: group.hits.map((hit) => qualifyHit(group.child, hit, ranked.score, hit.scope ? `${group.child}/${hit.scope}` : group.child)),
420
+ baselineHits: group.baselineHits.map((hit) => qualifyHit(group.child, hit)),
421
+ }];
422
+ });
423
+ let projectedGroups = rankedGroups;
424
+ if (baselineTop && baselineTopBack) {
425
+ const key = baselineTopBack.group;
426
+ const existing = rankedGroups.find((group) => group.key === key);
427
+ const baselineQueue = existing?.baselineHits ?? [baselineTop];
428
+ const projectedScore = existing?.hits[0]?.score ?? baselineTop.score;
429
+ const locked = existing
430
+ ? {
431
+ ...existing,
432
+ hits: [
433
+ baselineTop,
434
+ ...baselineQueue
435
+ .filter((hit) => !sameHit(hit, baselineTop))
436
+ .map((hit) => ({ ...hit, score: projectedScore })),
437
+ ],
438
+ }
439
+ : {
440
+ key,
441
+ child: baselineTopBack.child,
442
+ hits: [baselineTop],
443
+ baselineHits: [baselineTop],
444
+ };
445
+ projectedGroups = [
446
+ locked,
447
+ ...rankedGroups.filter((group) => group.key !== key),
448
+ ];
449
+ }
450
+ const hits = roundRobinQueues(projectedGroups.map((group) => group.hits), limit);
451
+ const unmatched = [...fileFused.alsoMatched, ...gatedOut];
452
+ const note = coverageNote(wg);
453
+ const result = {
454
+ query,
455
+ mode: hits.length ? "lexical" : "empty",
456
+ hits,
457
+ };
458
+ if (hits.length) {
459
+ const lockedScope = baselineTop?.scope;
460
+ const federated = [
461
+ ...new Set([
462
+ ...(lockedScope ? [lockedScope] : []),
463
+ ...fileFused.federated,
464
+ ]),
465
+ ];
466
+ const federatedSet = new Set(federated);
467
+ result.scopes = {
468
+ federated,
469
+ alsoMatched: unmatched.filter((match) => !federatedSet.has(match.scope)),
470
+ };
471
+ }
472
+ else {
473
+ result.note = `no matching nodes across ${wg.loaded.length} workspace repo(s) — try different words, or \`${cmd("graft build")}\` at a child`;
474
+ }
475
+ if (note)
476
+ result.note = result.note ? `${result.note}\n${note}` : note;
477
+ return result;
478
+ }
479
+ // Fuse the survivors' scope lists (within-child fusion is left untouched —
480
+ // each child already ran its own per-scope RRF).
481
+ const docs = [];
482
+ const back = new Map();
483
+ for (const { child, hits } of survivors) {
484
+ hits.forEach((hit, i) => {
485
+ const scope = hit.scope ? `${child}/${hit.scope}` : child;
486
+ const id = `${child} ${i}`;
487
+ docs.push({ id, scope, score: hit.score });
488
+ back.set(id, { child, hit });
489
+ });
490
+ }
491
+ const fused = fuseScopes(docs);
492
+ const rankedHits = fused.ranked.map((rd) => {
493
+ const { child, hit } = back.get(rd.id);
494
+ const value = {
495
+ ...hit,
496
+ score: rd.score,
497
+ scope: rd.scope,
498
+ pointer: prefixPointer(child, hit.pointer),
499
+ };
500
+ return {
501
+ // Lexical symbols share a file queue. Concepts and structural neighbours
502
+ // remain singleton partitions, so this lexical selection layer cannot
503
+ // reorder `who calls` / `dependencies of` answers.
504
+ group: hit.kind === "symbol"
505
+ ? `file:${child}/${hitFile(hit.pointer)}`
506
+ : `singleton:${child}:${rd.id}`,
507
+ value,
508
+ };
509
+ });
510
+ const hits = fileFirstRoundRobin(rankedHits, limit);
511
+ const alsoMatched = [...fused.alsoMatched, ...gatedOut];
512
+ const note = coverageNote(wg);
513
+ const result = { query, mode: hits.length ? "lexical" : "empty", hits };
514
+ if (hits.length) {
515
+ const federated = fused.federated.length ? fused.federated : [...new Set(hits.map((h) => h.scope))];
516
+ result.scopes = { federated, alsoMatched };
517
+ }
518
+ else {
519
+ result.note = `no matching nodes across ${wg.loaded.length} workspace repo(s) — try different words, or \`${cmd("graft build")}\` at a child`;
520
+ }
521
+ if (note)
522
+ result.note = result.note ? `${result.note}\n${note}` : note;
523
+ return result;
524
+ }
525
+ /** Merge every child's `GrepResult` into one, prefixing group paths with the
526
+ * child dir and re-sorting by coupling (inDegree desc, path asc) across repos.
527
+ * In-degree stays each child's own — it comes from that child's graph. */
528
+ export function federateGrep(root, override, pattern, opts = {}) {
529
+ const wg = loadWorkspaceGraphs(root, override);
530
+ const groups = [];
531
+ let filesSearched = 0;
532
+ let totalHits = 0;
533
+ const truncated = { files: 0, hits: 0 };
534
+ let savedFiles = 0;
535
+ let savedChars = 0;
536
+ for (const { child, graph } of wg.loaded) {
537
+ const r = grepGraph(graph, join(root, child), pattern, {
538
+ ignoreCase: opts.ignoreCase,
539
+ fixed: opts.fixed,
540
+ });
541
+ filesSearched += r.filesSearched;
542
+ totalHits += r.totalHits;
543
+ truncated.files += r.truncated.files;
544
+ truncated.hits += r.truncated.hits;
545
+ if (r.saved) {
546
+ savedFiles += r.saved.files;
547
+ savedChars += r.saved.baselineChars;
548
+ }
549
+ for (const g of r.groups) {
550
+ groups.push({
551
+ ...g,
552
+ path: `${child}/${g.path}`,
553
+ symbol: g.symbol ? { ...g.symbol, path: `${child}/${g.symbol.path}` } : null,
554
+ });
555
+ }
556
+ }
557
+ groups.sort((a, b) => b.inDegree - a.inDegree || a.path.localeCompare(b.path));
558
+ const saved = savedChars > 0 ? { files: savedFiles, baselineChars: savedChars } : undefined;
559
+ const result = { pattern, filesSearched, totalHits, groups, truncated, saved };
560
+ return { result, coverage: coverageNote(wg) };
561
+ }
562
+ /** One `graft map` section per child (each child's own map, budget split evenly
563
+ * across the loaded children), joined under `<child>/` headers. */
564
+ export function federateMap(root, override, opts = {}) {
565
+ const wg = loadWorkspaceGraphs(root, override);
566
+ const perChild = opts.maxDirs !== undefined && wg.loaded.length > 0
567
+ ? Math.max(1, Math.floor(opts.maxDirs / wg.loaded.length))
568
+ : undefined;
569
+ const sections = wg.loaded.map(({ child, graph }) => {
570
+ const map = buildRepoMap(graph, perChild !== undefined ? { maxDirs: perChild } : {});
571
+ return `## ${child}/\n${formatRepoMap(map).trimEnd()}`;
572
+ });
573
+ const head = `workspace map — ${wg.loaded.length} repo(s)`;
574
+ const parts = [head, "", sections.join("\n\n")];
575
+ const cov = coverageNote(wg);
576
+ if (cov)
577
+ parts.push("", cov);
578
+ return parts.join("\n") + "\n";
579
+ }
580
+ /** Per-child drift status. `ok` is false when any BUILT child is stale — an
581
+ * unbuilt child is surfaced (coverage), never a failure. */
582
+ export async function federateCheck(root, override) {
583
+ const wg = loadWorkspaceGraphs(root, override);
584
+ const lines = [`workspace check — ${wg.loaded.length + wg.missing.length} repo(s)`, ""];
585
+ let ok = true;
586
+ for (const { child } of wg.loaded) {
587
+ const g = await checkGraph(join(root, child));
588
+ if (g.ok) {
589
+ lines.push(`${child}/: OK`);
590
+ }
591
+ else {
592
+ ok = false;
593
+ const bits = [];
594
+ if (g.added.length)
595
+ bits.push(`${g.added.length} added`);
596
+ if (g.removed.length)
597
+ bits.push(`${g.removed.length} removed`);
598
+ if (g.changed.length)
599
+ bits.push(`${g.changed.length} changed`);
600
+ if (g.stale.length)
601
+ bits.push(`${g.stale.length} stale`);
602
+ lines.push(`${child}/: STALE (${bits.join(", ")})`);
603
+ }
604
+ }
605
+ for (const child of wg.missing)
606
+ lines.push(`${child}/: not built (run ${cmd("graft build")})`);
607
+ const cov = coverageNote(wg);
608
+ if (cov)
609
+ lines.push("", cov);
610
+ return { text: lines.join("\n") + "\n", ok };
611
+ }
612
+ /** Resolve a symbol across every child, grouped per child. Reuses the shared
613
+ * traverse-cli formatters so each block reads exactly like `graft callers`. */
614
+ export function federateCallers(root, override, symbol, opts = {}) {
615
+ const wg = loadWorkspaceGraphs(root, override);
616
+ const direction = opts.direction ?? "in";
617
+ const depth = opts.depth && opts.depth >= 1 ? Math.floor(opts.depth) : 1;
618
+ const showDepth = depth > 1;
619
+ const blocks = [];
620
+ let found = false;
621
+ for (const { child, graph } of wg.loaded) {
622
+ const matches = resolveSymbol(graph, symbol, opts.in ? { in: opts.in } : {});
623
+ if (matches.length === 0)
624
+ continue;
625
+ found = true;
626
+ const results = matches.map((m) => ({ symbol: m, hits: edgeWalk(graph, m, direction, depth) }));
627
+ const lines = [`## ${child}/`];
628
+ for (const { symbol: sym, hits } of results) {
629
+ lines.push(headerOf(sym));
630
+ if (hits.length === 0)
631
+ lines.push(looseNoteFor(direction, sym.name, matches.length));
632
+ else
633
+ for (const h of hits)
634
+ lines.push(hitLine(direction, h, showDepth));
635
+ }
636
+ const body = lines.join("\n");
637
+ blocks.push(withSavings(body, callersSavings(graph, results)));
638
+ }
639
+ const cov = coverageNote(wg);
640
+ if (!found) {
641
+ const base = `no symbol "${symbol}" in any of the ${wg.loaded.length} workspace repo(s) — check spelling or run ${cmd("graft build")}`;
642
+ return { text: cov ? `${base}\n${cov}` : base, found: false };
643
+ }
644
+ let text = blocks.join("\n\n");
645
+ if (cov)
646
+ text += `\n\n${cov}`;
647
+ return { text, found: true };
648
+ }
649
+ /**
650
+ * Split a parent into a workspace: build each git child (via the supplied
651
+ * `buildChild` callback, so this stays free of any engine/LLM dependency),
652
+ * then REPLACE the parent's `graft/` with just `workspace.json`. `onStart`
653
+ * fires once — before any child is built — carrying whether this build is a
654
+ * mega-graph migration, so the caller can print the one-time split warning
655
+ * first, exactly as the spec requires.
656
+ *
657
+ * The child build writes into `<child>/graft/` and is byte-identical to
658
+ * building that child standalone (`buildChild` is just `buildGraph(childDir)`),
659
+ * because nothing about the parent path enters the child's build.
660
+ */
661
+ export async function splitWorkspace(root, override, buildChild, onStart) {
662
+ // Preserve user-configured exclusions across rebuilds.
663
+ const existing = readWorkspace(root, override);
664
+ const excluded = existing?.excludeFromDiscovery ?? [];
665
+ // Match exact name OR prefix (e.g. "Biometria" excludes "Biometria/frontend")
666
+ const isExcluded = (child) => excluded.some((e) => child === e || child.startsWith(`${e}/`));
667
+ const discovered = discoverWorkspaceChildren(root)
668
+ .filter((c) => !isExcluded(c))
669
+ .sort();
670
+ const migrated = hasMegaGraph(root, override);
671
+ onStart?.({ children: discovered, migrated });
672
+ for (const child of discovered)
673
+ await buildChild(join(root, child), child);
674
+ clearParentGraft(root, override);
675
+ writeWorkspace(root, { version: 1, children: discovered, excludeFromDiscovery: existing?.excludeFromDiscovery }, override);
676
+ return { children: discovered, migrated };
677
+ }
678
+ export { formatGrepResult, zeroHitNote };
679
+ /**
680
+ * Federated semantic search across all workspace children.
681
+ * For each child, queries that child's embeddings DB and prefixes hits with
682
+ * `[child/]`. All hits are merged, sorted by score desc, top `limit` returned.
683
+ */
684
+ export async function federateSemantic(root, override, query, limit) {
685
+ const wg = loadWorkspaceGraphs(root, override);
686
+ const allHits = [];
687
+ for (const { child } of wg.loaded) {
688
+ const dbPath = join(root, child, 'graft', '.cache', 'embeddings.db');
689
+ try {
690
+ const hits = await searchSemantic(query, dbPath, limit * 2);
691
+ for (const h of hits) {
692
+ allHits.push({
693
+ child,
694
+ node_id: h.node_id,
695
+ file_path: `[${child}/]${h.file_path}`,
696
+ symbol: h.symbol,
697
+ score: h.score,
698
+ snippet: h.snippet,
699
+ });
700
+ }
701
+ }
702
+ catch {
703
+ // Child has no embeddings yet — skip silently.
704
+ }
705
+ }
706
+ if (allHits.length === 0) {
707
+ return { text: 'no semantic matches found across workspace children', isError: false };
708
+ }
709
+ allHits.sort((a, b) => b.score - a.score);
710
+ const top = allHits.slice(0, limit);
711
+ const lines = top.map((h, i) => `${i + 1}. ${h.file_path} · ${h.symbol} (score: ${h.score})\n ${h.snippet}`);
712
+ return { text: lines.join('\n\n'), isError: false };
713
+ }
714
+ //# sourceMappingURL=workspace.js.map