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,383 @@
1
+ /**
2
+ * Blast radius of a diff: what else in the repo depends on the lines this change
3
+ * touched.
4
+ *
5
+ * The whole command is a join between two things graft already has — git's changed
6
+ * line ranges (`diff.ts`) and the wiring graph's incoming edges (`traverse.ts`) —
7
+ * so there is no new graph logic here, only the seeding rule:
8
+ *
9
+ * changed line → the innermost symbol whose span contains it → seed
10
+ *
11
+ * Seeding by SYMBOL rather than by file is what makes the answer worth reading: a
12
+ * 400-line file with a one-line edit reports the dependents of that one function,
13
+ * not of everything the file happens to define. The file node is seeded too, so
14
+ * importers (whose edge targets the file, not the symbol) are still found.
15
+ */
16
+ import { impactOfMany } from "../graph/traverse.js";
17
+ import { dirLabel, moduleIndex, parentDir, shortLabel } from "./modules.js";
18
+ const SPAN_RE = /^L(\d+)-L(\d+)$/;
19
+ function spanBounds(span) {
20
+ const m = SPAN_RE.exec(span);
21
+ return m ? { start: Number(m[1]), end: Number(m[2]) } : null;
22
+ }
23
+ /**
24
+ * The innermost symbols whose spans overlap any changed range in `path`.
25
+ *
26
+ * "Innermost" matters for a method: a changed line inside `Cache.get` overlaps both
27
+ * the class span and the method span, and seeding the class would report everyone
28
+ * who touches any part of `Cache`. Nesting is resolved by keeping, for each range,
29
+ * only symbols that contain no other matched symbol.
30
+ */
31
+ function seedsForFile(graph, path, ranges) {
32
+ const symbols = [];
33
+ for (const n of graph.nodes) {
34
+ if (n.kind === "file" || n.path !== path)
35
+ continue;
36
+ const b = spanBounds(n.span);
37
+ if (b)
38
+ symbols.push({ node: n, ...b });
39
+ }
40
+ if (symbols.length === 0)
41
+ return [];
42
+ const hit = new Map();
43
+ for (const r of ranges) {
44
+ const overlapping = symbols.filter((s) => s.start <= r.end && s.end >= r.start);
45
+ // Drop any symbol that strictly contains another overlapping one (the class
46
+ // around a changed method), keeping the tightest description of the edit.
47
+ for (const s of overlapping) {
48
+ const containsAnother = overlapping.some((o) => o !== s && o.start >= s.start && o.end <= s.end);
49
+ if (!containsAnother)
50
+ hit.set(s.node.id, s.node);
51
+ }
52
+ }
53
+ return [...hit.values()];
54
+ }
55
+ /** Compute the blast radius of `changed` against `graph`. */
56
+ export function blastRadius(graph, changed, basis, opts) {
57
+ const fileNodes = new Map();
58
+ for (const n of graph.nodes)
59
+ if (n.kind === "file")
60
+ fileNodes.set(n.path, n);
61
+ const seeds = [];
62
+ const unindexed = [];
63
+ const deleted = [];
64
+ /** Seed nodes per changed file, kept for the area grouping and the test signal. */
65
+ const seedNodes = new Map();
66
+ /** Merged dependents, keyed by node id, at the shallowest depth any file reached
67
+ * them — plus which changed files did the reaching. */
68
+ const merged = new Map();
69
+ for (const file of changed) {
70
+ if (file.status === "deleted") {
71
+ deleted.push(file.path);
72
+ continue;
73
+ }
74
+ const fileNode = fileNodes.get(file.path);
75
+ if (!fileNode) {
76
+ unindexed.push(file.path);
77
+ continue;
78
+ }
79
+ // No hunk ranges (a rename with no content change, a mode change) or an added
80
+ // file: the unit of change is the file itself.
81
+ const symbolSeeds = file.ranges.length > 0 ? seedsForFile(graph, file.path, file.ranges) : [];
82
+ for (const node of symbolSeeds) {
83
+ seeds.push({ id: node.id, name: node.name, kind: node.kind, path: node.path, span: node.span, wholeFile: false });
84
+ }
85
+ if (symbolSeeds.length === 0) {
86
+ seeds.push({
87
+ id: fileNode.id, name: fileNode.name, kind: fileNode.kind,
88
+ path: fileNode.path, span: fileNode.span, wholeFile: true,
89
+ });
90
+ }
91
+ // One walk PER CHANGED FILE, not one walk over every seed at once. A combined
92
+ // walk records the depth a node was first reached at but not by which seed, so
93
+ // the diagram could only draw "every changed file reaches every module" — a
94
+ // cross-product that tells a reviewer nothing. Walking per file costs one
95
+ // adjacency build each (cheap: the graph is already in memory) and buys arrows
96
+ // that are true.
97
+ //
98
+ // The FILE node is a seed only when no symbol matched. `imports` edges target
99
+ // the file id, so seeding it alongside symbols would pull in every importer of
100
+ // the file — a one-line edit to one function would report every module that
101
+ // imports the module, which is the noise this command exists to avoid. With no
102
+ // symbol seeds (a change to a top-level constant, a new import, a file with no
103
+ // extracted symbols) the file IS the unit of change, and its importers are the
104
+ // only dependents there are.
105
+ const walkSeeds = symbolSeeds.length > 0 ? symbolSeeds : [fileNode];
106
+ seedNodes.set(file.path, walkSeeds);
107
+ const hits = impactOfMany(graph, walkSeeds, opts.depth, "in");
108
+ for (const h of hits) {
109
+ if (!hasNode(h))
110
+ continue;
111
+ const hit = toImpacted(h);
112
+ const prev = merged.get(hit.id);
113
+ if (!prev)
114
+ merged.set(hit.id, { hit, from: new Set([file.path]) });
115
+ else {
116
+ prev.from.add(file.path);
117
+ // Keep the shallowest reach: a symbol two hops from one changed file and one
118
+ // hop from another is one hop away from this PR.
119
+ if (hit.depth < prev.hit.depth)
120
+ prev.hit = hit;
121
+ }
122
+ }
123
+ }
124
+ const impacted = [...merged.values()].map((m) => m.hit);
125
+ const origins = new Map([...merged].map(([id, m]) => [id, m.from]));
126
+ const index = opts.modules ?? emptyIndex();
127
+ const grouped = groupByModule(impacted, changed, origins, index);
128
+ return {
129
+ basis,
130
+ depth: opts.depth,
131
+ changed,
132
+ unindexed,
133
+ deleted,
134
+ seeds,
135
+ impacted,
136
+ modules: grouped.modules,
137
+ testModules: grouped.testModules,
138
+ areas: changedAreas(graph, changed, seedNodes, index, grouped.modules),
139
+ };
140
+ }
141
+ /**
142
+ * Group the changed files into areas, and work out whether each one's tests moved.
143
+ *
144
+ * The diff's own side of the diagram used to be one box per changed file, which can
145
+ * never fit: a 24-file PR drew ten arbitrary paths. Grouped by the same concept
146
+ * labels the dependents use, the same PR is three or four circles — and once the
147
+ * files are grouped, "did a test that reaches this move too?" is one question per
148
+ * circle instead of per file.
149
+ */
150
+ function changedAreas(graph, changed, seedNodes, index, modules) {
151
+ const changedTests = new Set(changed.filter((c) => TEST_PATH.test(c.path)).map((c) => c.path));
152
+ const nodeById = new Map(graph.nodes.map((n) => [n.id, n]));
153
+ /** How many edges point AT each node — how central it is in the graph. */
154
+ const inDegree = new Map();
155
+ for (const e of graph.edges)
156
+ inDegree.set(e.target, (inDegree.get(e.target) ?? 0) + 1);
157
+ /** Incoming edge sources per node id, resolved to the source's file path. */
158
+ const incoming = new Map();
159
+ for (const e of graph.edges) {
160
+ const path = nodeById.get(e.source)?.path;
161
+ if (!path || !TEST_PATH.test(path))
162
+ continue;
163
+ const at = incoming.get(e.target) ?? new Set();
164
+ at.add(path);
165
+ incoming.set(e.target, at);
166
+ }
167
+ // Group by DIRECTORY, not by concept. Concepts in a well-summarised repo are
168
+ // near-file-grained — graft's own PR produced nine of them, one file each — so
169
+ // grouping the diff by concept groups nothing: the left side was nine circles
170
+ // with near-identical truncated names. A directory is the coarser unit reviewers
171
+ // already use, and a concept name is only worth borrowing when one concept
172
+ // happens to claim the whole group.
173
+ const groups = new Map();
174
+ for (const path of seedNodes.keys()) {
175
+ // A changed test file is the signal, not the source of a blast radius. Drawing
176
+ // it as an area would put "your tests changed" on both sides of the arrow.
177
+ if (TEST_PATH.test(path))
178
+ continue;
179
+ const dir = dirLabel(path);
180
+ groups.set(dir, [...(groups.get(dir) ?? []), path]);
181
+ }
182
+ coarsen(groups, MAX_AREAS);
183
+ const byKey = new Map();
184
+ /** Hub candidates per area, ranked once every seed has been seen. */
185
+ const candidates = new Map();
186
+ for (const [dir, paths] of groups) {
187
+ const area = {
188
+ label: dir, labelSource: "symbol", key: dir,
189
+ files: [], seeds: 0, tests: "none", testFiles: [], changedTestFiles: [],
190
+ reached: 0, behavioural: 0, unreached: [], seedNames: [],
191
+ };
192
+ byKey.set(dir, area);
193
+ for (const path of paths) {
194
+ const nodes = seedNodes.get(path) ?? [];
195
+ area.files.push(path);
196
+ area.seeds += nodes.length;
197
+ for (const node of nodes) {
198
+ // Reach is measured over behaviour only. Counting interfaces and type aliases
199
+ // would sink every ratio: nothing "calls" a type, so a fully tested area of
200
+ // typed code would report a third of its symbols as unreached.
201
+ const behavioural = node.kind === "function" || node.kind === "method" || node.kind === "class";
202
+ const from = incoming.get(node.id) ?? new Set();
203
+ for (const t of from) {
204
+ if (!area.testFiles.includes(t))
205
+ area.testFiles.push(t);
206
+ if (changedTests.has(t) && !area.changedTestFiles.includes(t))
207
+ area.changedTestFiles.push(t);
208
+ }
209
+ candidates.set(area, [
210
+ ...(candidates.get(area) ?? []),
211
+ { name: node.name, behavioural, degree: inDegree.get(node.id) ?? 0 },
212
+ ]);
213
+ if (!behavioural)
214
+ continue;
215
+ area.behavioural++;
216
+ if (from.size > 0)
217
+ area.reached++;
218
+ else
219
+ area.unreached.push(node.name);
220
+ }
221
+ }
222
+ }
223
+ for (const area of byKey.values()) {
224
+ // The hub is the most-depended-on function in the area, not whichever symbol the
225
+ // file walk happened to end on: `LlmFailureGate` rather than `transportRetries`.
226
+ // Behaviour outranks types, since nothing calls an interface.
227
+ area.seedNames = (candidates.get(area) ?? [])
228
+ .sort((a, b) => Number(b.behavioural) - Number(a.behavioural) || b.degree - a.degree || a.name.localeCompare(b.name))
229
+ .map((c) => c.name);
230
+ const concept = sharedConcept(area.files, index);
231
+ area.label = concept ?? hubLabel(area.seedNames, area.key);
232
+ area.labelSource = concept ? "concept" : "symbol";
233
+ area.files.sort();
234
+ area.testFiles.sort();
235
+ area.changedTestFiles.sort();
236
+ area.tests =
237
+ area.behavioural === 0 ? "na"
238
+ : area.changedTestFiles.length > 0 ? "changed"
239
+ : area.testFiles.length > 0 ? "stale"
240
+ : "none";
241
+ }
242
+ // Ordered by how much each area actually reaches, because the diagram caps this
243
+ // list and folds the rest into one circle. Ordered by size instead, the fold ends
244
+ // up holding the best-connected areas and the tail circle collects every arrow —
245
+ // the same mistake, one level up, as capping changed FILES in diff order.
246
+ const reach = (a) => modules.filter((m) => m.from.some((f) => a.files.includes(f))).length;
247
+ return [...byKey.values()].sort((a, b) => reach(b) - reach(a) || b.files.length - a.files.length || a.label.localeCompare(b.label));
248
+ }
249
+ /** Convenience wrapper: build the module index from a context dir. */
250
+ export function blastRadiusIn(graph, contextDir, changed, basis, depth) {
251
+ return blastRadius(graph, changed, basis, { depth, modules: moduleIndex(contextDir) });
252
+ }
253
+ function hasNode(h) {
254
+ // Unresolved endpoints (an import module string no node exists for) carry no
255
+ // location, so they can neither be grouped nor opened — dropped rather than
256
+ // rendered as a box with no file behind it.
257
+ return h.node !== null;
258
+ }
259
+ function toImpacted(h) {
260
+ return {
261
+ id: h.node.id, name: h.node.name, kind: h.node.kind,
262
+ path: h.node.path, span: h.node.span, relation: h.relation, depth: h.depth,
263
+ };
264
+ }
265
+ function emptyIndex() {
266
+ return { hasConcepts: false, labelOf: (p) => p, conceptOf: () => null };
267
+ }
268
+ /**
269
+ * The concept claiming EVERY file in a cluster, or null when they disagree.
270
+ *
271
+ * Unanimity is the point: a concept that covers three of five files describes
272
+ * something narrower than the cluster, and stamping its name on the whole thing is
273
+ * how a diagram starts lying about what it drew.
274
+ */
275
+ function sharedConcept(paths, index) {
276
+ const concepts = new Set(paths.map((p) => index.conceptOf(p)));
277
+ const [only] = concepts;
278
+ return concepts.size === 1 && only ? shortLabel(only) : null;
279
+ }
280
+ /**
281
+ * The deterministic backstop label: the cluster's most significant symbol.
282
+ *
283
+ * Reached with no concept, no cache and no API key, so this is what guarantees a
284
+ * circle never carries a bare directory. It is a fact rather than a guess — the
285
+ * reviewer can grep the name — which is why it beats borrowing a neighbouring
286
+ * concept: on graft's own graph that borrowing labelled the freshness gate
287
+ * "Graph Extraction and Loading", which misleads worse than any path.
288
+ */
289
+ export function hubLabel(names, fallback) {
290
+ const [head] = names.filter(Boolean);
291
+ // The hub alone, with no "+8" tail: how many symbols came with it is already the
292
+ // circle's second line and the table's own column, and repeating it in the label
293
+ // turned "writeGraph" into "writeGraph +8", which reads as a version number.
294
+ return head ? shortLabel(head) : fallback;
295
+ }
296
+ /**
297
+ * Group dependents into modules. `origins` carries, per dependent, the changed
298
+ * files whose walk actually reached it — so a module's `from` is the real arrow
299
+ * set, not every changed file in the PR.
300
+ */
301
+ function groupByModule(impacted, changed, origins, modules) {
302
+ const changedPaths = new Set(changed.map((c) => c.path));
303
+ const byKey = new Map();
304
+ for (const hit of impacted) {
305
+ // A dependent inside a file the PR already changes is not "reach" — it is the
306
+ // diff. Reviewers see it in the diff itself; repeating it as blast radius is
307
+ // what makes these comments feel like noise.
308
+ if (changedPaths.has(hit.path))
309
+ continue;
310
+ // Cluster by concept where one exists, else by directory. Clustering and
311
+ // LABELLING are separate steps now: the key only has to be stable, while the
312
+ // label goes through the ladder below and may end up naming a symbol.
313
+ const key = modules.conceptOf(hit.path) ?? dirLabel(hit.path);
314
+ let mod = byKey.get(key);
315
+ if (!mod) {
316
+ mod = { label: key, labelSource: "symbol", key, files: [], symbols: [], from: [] };
317
+ byKey.set(key, mod);
318
+ }
319
+ mod.symbols.push(hit);
320
+ if (!mod.files.includes(hit.path))
321
+ mod.files.push(hit.path);
322
+ }
323
+ for (const mod of byKey.values()) {
324
+ const from = new Set();
325
+ for (const s of mod.symbols)
326
+ for (const p of origins.get(s.id) ?? [])
327
+ from.add(p);
328
+ mod.from = [...from].sort();
329
+ mod.files.sort();
330
+ mod.symbols.sort((a, b) => a.depth - b.depth || a.path.localeCompare(b.path));
331
+ // Shallowest symbol first after that sort, so it is the cluster's hub: the
332
+ // thing one hop from the diff rather than an arbitrary member.
333
+ const concept = sharedConcept(mod.files, modules);
334
+ mod.label = concept ?? hubLabel(mod.symbols.map((s) => s.name), mod.key);
335
+ mod.labelSource = concept ? "concept" : "symbol";
336
+ }
337
+ // Test-only modules are separated, not just sorted last. On a repo with a test
338
+ // per module they are the majority of the graph's dependents — graft's own 24-file
339
+ // PR produced 31 modules, 24 of them a single test file — so leaving them in the
340
+ // same list means they crowd out every module a reviewer needs whatever the caps
341
+ // are. "Your tests reference the thing you changed" is one line, not 24 sections.
342
+ const bySize = (a, b) => b.symbols.length - a.symbols.length || a.label.localeCompare(b.label);
343
+ const all = [...byKey.values()];
344
+ return {
345
+ modules: all.filter((m) => !isTestOnly(m)).sort(bySize),
346
+ testModules: all.filter(isTestOnly).sort(bySize),
347
+ };
348
+ }
349
+ /**
350
+ * Directory groups the diff is folded down to before anything is drawn.
351
+ *
352
+ * Kept here rather than in the renderer because it is a statement about the diff,
353
+ * not about the picture: five areas is roughly what a reviewer holds in their head,
354
+ * and the renderer then draws all of them.
355
+ */
356
+ const MAX_AREAS = 5;
357
+ /**
358
+ * Fold directory groups into their parents until there are at most `max`.
359
+ *
360
+ * The deepest group goes first, and only into a parent that already exists, so
361
+ * `src/ai/llm/` joins `src/ai/` rather than everything collapsing towards the repo
362
+ * root. When no group has an existing parent, one is created — but never at the top
363
+ * level, since a single `(root)` area is no grouping at all.
364
+ */
365
+ function coarsen(groups, max) {
366
+ const depth = (dir) => dir.split("/").length;
367
+ while (groups.size > max) {
368
+ const keys = [...groups.keys()].sort((a, b) => depth(b) - depth(a) || (groups.get(a).length - groups.get(b).length));
369
+ const pick = keys.find((k) => groups.has(parentDir(k)) && parentDir(k) !== "") ??
370
+ keys.find((k) => parentDir(k) !== "");
371
+ if (!pick)
372
+ return;
373
+ const parent = parentDir(pick);
374
+ groups.set(parent, [...(groups.get(parent) ?? []), ...groups.get(pick)]);
375
+ groups.delete(pick);
376
+ }
377
+ }
378
+ const TEST_PATH = /(^|\/)(tests?|specs?|__tests__)\/|\.(test|spec)\.[cm]?[jt]sx?$|_test\.(go|py|rb)$/i;
379
+ /** True when every file in the module is a test file. */
380
+ function isTestOnly(mod) {
381
+ return mod.files.length > 0 && mod.files.every((f) => TEST_PATH.test(f));
382
+ }
383
+ //# sourceMappingURL=blast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blast.js","sourceRoot":"","sources":["../../src/blast/blast.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ export type ChangeStatus = "added" | "modified" | "deleted" | "renamed";
2
+ /** A contiguous run of changed lines, in POST-image (new file) line numbers. */
3
+ export interface LineRange {
4
+ start: number;
5
+ end: number;
6
+ }
7
+ /**
8
+ * One line git reported inside a hunk.
9
+ *
10
+ * `n` is the POST-image number, so it is the number a reviewer sees in the file
11
+ * on disk — a deleted line has none.
12
+ */
13
+ export interface DiffLine {
14
+ n: number | null;
15
+ sign: "+" | "-";
16
+ text: string;
17
+ }
18
+ /** A hunk's range and the lines in it. `ranges` on the file mirrors these. */
19
+ export interface Hunk extends LineRange {
20
+ lines: DiffLine[];
21
+ /** Lines dropped by the per-hunk cap — a generated file is not worth holding. */
22
+ dropped: number;
23
+ }
24
+ export interface ChangedFile {
25
+ /** Repo-relative posix path, post-image (the new name for a rename). */
26
+ path: string;
27
+ status: ChangeStatus;
28
+ /** Pre-image path, renames only. */
29
+ oldPath?: string;
30
+ /** Post-image changed line ranges. Empty for a pure delete or a mode-only change. */
31
+ ranges: LineRange[];
32
+ /** The same hunks, carrying their text: what a panel shows so a reader sees the
33
+ * change rather than a line number they have to go look up. One per range. */
34
+ hunks: Hunk[];
35
+ }
36
+ export interface DiffResult {
37
+ /** Human label for what was compared, e.g. "origin/main...HEAD". */
38
+ basis: string;
39
+ files: ChangedFile[];
40
+ }
41
+ /** True when `ref` names something git can resolve — checked before diffing so
42
+ * an unknown `--base` fails with a caller-facing message, not a git stack. */
43
+ export declare function refExists(root: string, ref: string): boolean;
44
+ /**
45
+ * Changed files + post-image line ranges.
46
+ *
47
+ * With no `base` this reports the working tree against HEAD (the local
48
+ * "what am I about to push" case), falling back to the last commit when the
49
+ * tree is clean — so the command answers something useful when run by hand,
50
+ * and CI passes `--base` for the real PR range.
51
+ */
52
+ export declare function changedFiles(root: string, base?: string): DiffResult | null;
53
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/blast/diff.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;AAExE,gFAAgF;AAChF,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjB,IAAI,EAAE,GAAG,GAAG,GAAG,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,8EAA8E;AAC9E,MAAM,WAAW,IAAK,SAAQ,SAAS;IACrC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,CAAC;IACrB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB;kFAC8E;IAC9E,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAqBD;8EAC8E;AAC9E,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAE5D;AAcD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAe3E"}
@@ -0,0 +1,193 @@
1
+ /**
2
+ * What a PR touched, read from git: changed files plus the *line ranges* inside
3
+ * them, which is what turns a diff into graph seeds (an enclosing-symbol lookup
4
+ * per changed line, see `blast.ts`).
5
+ *
6
+ * Git is shelled out to rather than parsed from a library because the CI job
7
+ * this feeds already has git — and `--unified=0` gives exactly the post-image
8
+ * line ranges we need, with no context lines to subtract back out.
9
+ */
10
+ import { WINHIDE } from "../util/child-process.js";
11
+ import { spawnSync } from "node:child_process";
12
+ import { resolve } from "node:path";
13
+ const HUNK_RE = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/;
14
+ /** Text kept per hunk, and per file. A regenerated lockfile is one hunk of 40,000
15
+ * lines: the ranges still matter (they seed the graph), the text does not, and it
16
+ * would otherwise ride along in `--format json` and in every exported page. */
17
+ const MAX_HUNK_LINES = 24;
18
+ const MAX_FILE_LINES = 200;
19
+ /** Run git in `root`, or return null when git fails (not a repo, unknown ref). */
20
+ function git(root, args) {
21
+ const res = spawnSync("git", ["-c", "core.quotePath=false", ...args], { ...WINHIDE, cwd: root,
22
+ encoding: "utf8",
23
+ stdio: ["ignore", "pipe", "pipe"],
24
+ maxBuffer: 64 * 1024 * 1024,
25
+ });
26
+ if (res.error || res.status !== 0 || typeof res.stdout !== "string")
27
+ return null;
28
+ return res.stdout;
29
+ }
30
+ /** True when `ref` names something git can resolve — checked before diffing so
31
+ * an unknown `--base` fails with a caller-facing message, not a git stack. */
32
+ export function refExists(root, ref) {
33
+ return git(resolve(root), ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]) !== null;
34
+ }
35
+ /**
36
+ * The diff arguments for a base ref, and the label to show for it.
37
+ *
38
+ * `<base>...HEAD` (three dots) diffs against the MERGE BASE, not the tip of the
39
+ * base branch — the difference matters on every PR whose base branch moved after
40
+ * it was cut: two-dot would attribute other people's merges to this PR and
41
+ * inflate the blast radius with files the author never touched.
42
+ */
43
+ function rangeArgs(base) {
44
+ return { args: [`${base}...HEAD`], basis: `${base}...HEAD` };
45
+ }
46
+ /**
47
+ * Changed files + post-image line ranges.
48
+ *
49
+ * With no `base` this reports the working tree against HEAD (the local
50
+ * "what am I about to push" case), falling back to the last commit when the
51
+ * tree is clean — so the command answers something useful when run by hand,
52
+ * and CI passes `--base` for the real PR range.
53
+ */
54
+ export function changedFiles(root, base) {
55
+ const dir = resolve(root);
56
+ if (base !== undefined) {
57
+ const { args, basis } = rangeArgs(base);
58
+ const files = diffFiles(dir, args);
59
+ return files === null ? null : { basis, files };
60
+ }
61
+ const working = diffFiles(dir, ["HEAD"]);
62
+ if (working === null)
63
+ return null;
64
+ if (working.length > 0)
65
+ return { basis: "working tree vs HEAD", files: working };
66
+ const last = diffFiles(dir, ["HEAD~1...HEAD"]);
67
+ if (last === null)
68
+ return { basis: "working tree vs HEAD", files: [] };
69
+ return { basis: "HEAD~1...HEAD", files: last };
70
+ }
71
+ /** Both git passes for one range: name-status (statuses, renames) then hunks. */
72
+ function diffFiles(dir, range) {
73
+ const status = git(dir, ["diff", "--name-status", "--find-renames", "-z", ...range, "--"]);
74
+ if (status === null)
75
+ return null;
76
+ const files = parseNameStatus(status);
77
+ if (files.length === 0)
78
+ return files;
79
+ const patch = git(dir, [
80
+ "diff",
81
+ "--unified=0",
82
+ "--no-color",
83
+ "--no-ext-diff",
84
+ "--find-renames",
85
+ ...range,
86
+ "--",
87
+ ]);
88
+ if (patch !== null)
89
+ applyHunks(files, patch);
90
+ return files;
91
+ }
92
+ /**
93
+ * `--name-status -z` output: NUL-separated fields, where a rename spends THREE
94
+ * fields (`R096`, old, new) and everything else spends two. Splitting on NUL and
95
+ * walking with a cursor is the only way to read it — a line-oriented parse
96
+ * silently pairs a rename's old path with the next file's status.
97
+ */
98
+ function parseNameStatus(out) {
99
+ const fields = out.split("\0").filter((f) => f !== "");
100
+ const files = [];
101
+ for (let i = 0; i < fields.length;) {
102
+ const code = fields[i++];
103
+ if (code.startsWith("R") || code.startsWith("C")) {
104
+ const oldPath = fields[i++];
105
+ const path = fields[i++];
106
+ if (path === undefined)
107
+ break;
108
+ files.push({ path, status: "renamed", oldPath, ranges: [], hunks: [] });
109
+ continue;
110
+ }
111
+ const path = fields[i++];
112
+ if (path === undefined)
113
+ break;
114
+ if (code.startsWith("A"))
115
+ files.push({ path, status: "added", ranges: [], hunks: [] });
116
+ else if (code.startsWith("D"))
117
+ files.push({ path, status: "deleted", ranges: [], hunks: [] });
118
+ else
119
+ files.push({ path, status: "modified", ranges: [], hunks: [] });
120
+ }
121
+ return files;
122
+ }
123
+ /** Attach each hunk's post-image range — and its text — to its file. */
124
+ function applyHunks(files, patch) {
125
+ const byPath = new Map(files.map((f) => [f.path, f]));
126
+ let current;
127
+ let hunk;
128
+ /** Post-image number for the next `+` line of the open hunk. */
129
+ let next = 0;
130
+ let kept = 0;
131
+ for (const line of patch.split("\n")) {
132
+ // Every file starts with this, and a content line cannot: a deleted one begins
133
+ // with `-`. Closing the open hunk here is what stops the NEXT file's `--- a/x`
134
+ // header being absorbed as a deleted line of the previous hunk.
135
+ if (line.startsWith("diff --git ")) {
136
+ current = undefined;
137
+ hunk = undefined;
138
+ continue;
139
+ }
140
+ if (line.startsWith("+++ ")) {
141
+ const raw = line.slice(4);
142
+ current = raw === "/dev/null" ? undefined : byPath.get(stripPrefix(raw));
143
+ hunk = undefined;
144
+ kept = 0;
145
+ continue;
146
+ }
147
+ if (line.startsWith("@@")) {
148
+ hunk = undefined;
149
+ if (!current)
150
+ continue;
151
+ const m = HUNK_RE.exec(line);
152
+ if (!m)
153
+ continue;
154
+ const start = Number(m[1]);
155
+ // `+N,0` is a pure deletion: nothing survives in the post-image, and git
156
+ // reports the line BEFORE the gap. Recording that single line is what keeps
157
+ // "the body of foo() lost 10 lines" attributable to foo() at all.
158
+ const count = m[2] === undefined ? 1 : Number(m[2]);
159
+ const range = count === 0 ? { start, end: start } : { start, end: start + count - 1 };
160
+ hunk = { ...range, lines: [], dropped: 0 };
161
+ current.ranges.push(range);
162
+ current.hunks.push(hunk);
163
+ next = start;
164
+ continue;
165
+ }
166
+ if (!hunk)
167
+ continue;
168
+ // With `--unified=0` every line inside a hunk is an edit, so there is no
169
+ // context to skip. `+++`/`---` headers are already consumed above; a bare
170
+ // `--- a/x` for the next file arrives before its `+++`, and the `!hunk`
171
+ // guard on a fresh file keeps it out.
172
+ if (line.startsWith("+")) {
173
+ pushLine(hunk, { n: next, sign: "+", text: line.slice(1) }, kept++ < MAX_FILE_LINES);
174
+ next += 1;
175
+ }
176
+ else if (line.startsWith("-")) {
177
+ pushLine(hunk, { n: null, sign: "-", text: line.slice(1) }, kept++ < MAX_FILE_LINES);
178
+ }
179
+ }
180
+ }
181
+ function pushLine(hunk, line, withinFile) {
182
+ if (!withinFile || hunk.lines.length >= MAX_HUNK_LINES) {
183
+ hunk.dropped += 1;
184
+ return;
185
+ }
186
+ hunk.lines.push(line);
187
+ }
188
+ /** `b/src/x.ts` → `src/x.ts`, minus any trailing tab-separated timestamp. */
189
+ function stripPrefix(raw) {
190
+ const untabbed = raw.split("\t")[0];
191
+ return untabbed.startsWith("b/") ? untabbed.slice(2) : untabbed;
192
+ }
193
+ //# sourceMappingURL=diff.js.map