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,162 @@
1
+ /**
2
+ * The local event queue: one NDJSON line per event in `~/.graft/`.
3
+ *
4
+ * The queue exists because graft is a CLI, not an app. Every other tool we
5
+ * studied sends from a process that is already running and can afford to wait;
6
+ * `graft ask` lives for two seconds and its whole value proposition is being
7
+ * faster than reading files. So nothing is ever sent inline — events are
8
+ * appended here, and a detached child (`graft _telemetry-flush`, once a day)
9
+ * does the network. A user who is offline for a week loses nothing; a user on a
10
+ * hostile network never notices, because no command ever waits on a socket.
11
+ *
12
+ * Concurrency: appends are a single `appendFileSync` of one short line to a file
13
+ * opened `O_APPEND`, which POSIX makes atomic below PIPE_BUF (4 KB — an event is
14
+ * ~300 bytes). Two graft processes interleaving cannot produce a torn line.
15
+ *
16
+ * Draining renames the file first and reads the renamed copy, so events appended
17
+ * during a flush land in a fresh queue instead of being dropped by the truncate.
18
+ */
19
+ import { homedir } from 'node:os';
20
+ import { join } from 'node:path';
21
+ import { readFileSync, renameSync, rmSync, statSync } from 'node:fs';
22
+ import { appendPrivateFile, writePrivateFile } from '../util/private-file.js';
23
+ /**
24
+ * The ceiling on the queue. An unflushable machine — permanently offline, or a
25
+ * key that never works — must not grow a file in someone's home forever.
26
+ *
27
+ * Bytes are the bound, because size is the only thing cheap enough to check on
28
+ * every append (one `statSync`); counting lines would mean reading the whole
29
+ * file each time. {@link TRIM_TO_EVENTS} is not a second ceiling — it is how
30
+ * FAR a trim cuts back, so a queue of unusually small events sheds a worthwhile
31
+ * amount rather than one line at a time. Between trims the file can hold more
32
+ * than that many events, and can exceed the byte bound by one append.
33
+ */
34
+ export const MAX_QUEUE_BYTES = 256 * 1024;
35
+ export const TRIM_TO_EVENTS = 500;
36
+ export function queuePath(home = homedir()) {
37
+ return join(home, '.graft', 'telemetry-queue.ndjson');
38
+ }
39
+ /**
40
+ * Append one event. Never throws — a full disk or a read-only home must not fail
41
+ * the command the user actually ran.
42
+ */
43
+ export function enqueue(event, home) {
44
+ const path = queuePath(home);
45
+ try {
46
+ // Owner-only, folder included: it waits here to be sent.
47
+ appendPrivateFile(path, JSON.stringify(event) + '\n');
48
+ trim(path);
49
+ }
50
+ catch { /* unwritable home, full disk — telemetry is never worth an error */ }
51
+ }
52
+ /**
53
+ * Enforce the cap by dropping the OLDEST events. Newest-wins because the recent
54
+ * ones describe the version the user is on now; a year-old queue from 0.9
55
+ * answers nothing. Only rewrites when actually over, so the common path is one
56
+ * `statSync`.
57
+ */
58
+ function trim(path) {
59
+ let size;
60
+ try {
61
+ size = statSync(path).size;
62
+ }
63
+ catch {
64
+ return;
65
+ }
66
+ if (size <= MAX_QUEUE_BYTES)
67
+ return;
68
+ try {
69
+ const lines = readFileSync(path, 'utf8').split('\n').filter(Boolean);
70
+ // Walk backwards from the newest, taking lines while both budgets hold, so
71
+ // the file left behind always satisfies the byte bound — a count-only trim
72
+ // would leave 500 large events well over it.
73
+ const kept = [];
74
+ let bytes = 0;
75
+ for (let i = lines.length - 1; i >= 0 && kept.length < TRIM_TO_EVENTS; i--) {
76
+ bytes += Buffer.byteLength(lines[i]) + 1;
77
+ if (bytes > MAX_QUEUE_BYTES)
78
+ break;
79
+ kept.push(lines[i]);
80
+ }
81
+ kept.reverse();
82
+ writePrivateFile(path, kept.length ? kept.join('\n') + '\n' : '');
83
+ }
84
+ catch { /* leave it; the next drain clears it anyway */ }
85
+ }
86
+ /**
87
+ * Take everything pending, leaving an empty queue behind.
88
+ *
89
+ * The rename is what makes this safe against a concurrent append: from the
90
+ * instant it returns, other processes are writing to a brand-new file. Malformed
91
+ * lines are skipped rather than poisoning the batch — a torn write from a crash
92
+ * mid-append should cost one event, not the whole queue.
93
+ */
94
+ export function drain(home) {
95
+ const path = queuePath(home);
96
+ const taken = `${path}.${process.pid}.sending`;
97
+ try {
98
+ renameSync(path, taken);
99
+ }
100
+ catch {
101
+ return [];
102
+ } // nothing queued
103
+ let text = '';
104
+ try {
105
+ text = readFileSync(taken, 'utf8');
106
+ }
107
+ catch { /* fall through to cleanup */ }
108
+ try {
109
+ rmSync(taken, { force: true });
110
+ }
111
+ catch { /* best effort */ }
112
+ const out = [];
113
+ for (const line of text.split('\n')) {
114
+ if (!line.trim())
115
+ continue;
116
+ try {
117
+ out.push(JSON.parse(line));
118
+ }
119
+ catch { /* torn line — skip just this one */ }
120
+ }
121
+ return out;
122
+ }
123
+ /** Read without draining, for `graft telemetry debug`. */
124
+ export function peek(home) {
125
+ const out = [];
126
+ let text = '';
127
+ try {
128
+ text = readFileSync(queuePath(home), 'utf8');
129
+ }
130
+ catch {
131
+ return out;
132
+ }
133
+ for (const line of text.split('\n')) {
134
+ if (!line.trim())
135
+ continue;
136
+ try {
137
+ out.push(JSON.parse(line));
138
+ }
139
+ catch { /* skip */ }
140
+ }
141
+ return out;
142
+ }
143
+ /** Put a failed batch back, so a flush that could not reach the network does not
144
+ * silently discard a week of events. Oldest-first, ahead of anything queued
145
+ * since the drain. */
146
+ export function requeue(events, home) {
147
+ if (events.length === 0)
148
+ return;
149
+ const path = queuePath(home);
150
+ try {
151
+ const pending = (() => { try {
152
+ return readFileSync(path, 'utf8');
153
+ }
154
+ catch {
155
+ return '';
156
+ } })();
157
+ writePrivateFile(path, events.map((e) => JSON.stringify(e)).join('\n') + '\n' + pending);
158
+ trim(path);
159
+ }
160
+ catch { /* best effort */ }
161
+ }
162
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/telemetry/queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export interface SendResult {
2
+ ok: boolean;
3
+ status?: number;
4
+ /** Why it failed, for `graft telemetry debug` only — never itself reported. */
5
+ error?: string;
6
+ }
7
+ /**
8
+ * The events as PostHog wants them — everything the wire body contains EXCEPT
9
+ * the project key.
10
+ *
11
+ * The key is deliberately not in here. `graft telemetry debug` prints this
12
+ * object so a user can audit exactly what graft sends, and a command that exists
13
+ * to be pasted into a bug report must not also paste our ingestion key into it.
14
+ * `sendBatch` adds the key at the moment of the request and nowhere else, which
15
+ * keeps the key entirely out of every code path that can reach a terminal.
16
+ */
17
+ export declare function buildBatch(events: unknown[]): Record<string, unknown>;
18
+ export declare function sendBatch(events: unknown[]): Promise<SendResult>;
19
+ //# sourceMappingURL=send.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../src/telemetry/send.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgBrE;AAkBD,wBAAsB,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAmBtE"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The one network call in graft that is not an LLM request.
3
+ *
4
+ * A single POST of the whole queued batch to PostHog's capture endpoint, with
5
+ * `fetch` and no SDK. `posthog-node` would be the conventional choice, but it is
6
+ * a runtime dependency in a CLI whose install weight users notice, it wants to
7
+ * own batching and flushing (both of which the queue already does, and does
8
+ * differently because we are a short-lived process), and "an analytics library
9
+ * that can see everything" is precisely the objection this whole design exists
10
+ * to disarm. Twenty lines of `fetch` is auditable in a sitting.
11
+ *
12
+ * `$process_person_profile: false` on every event is what makes these ANONYMOUS
13
+ * events in PostHog: no person profile is created, no identity is stored, and
14
+ * the `distinct_id` is only ever the random install UUID.
15
+ */
16
+ import { posthogKey, posthogHost } from './key.js';
17
+ /** Anything longer and the detached child is just holding a socket open. */
18
+ const SEND_TIMEOUT_MS = 8000;
19
+ /**
20
+ * The events as PostHog wants them — everything the wire body contains EXCEPT
21
+ * the project key.
22
+ *
23
+ * The key is deliberately not in here. `graft telemetry debug` prints this
24
+ * object so a user can audit exactly what graft sends, and a command that exists
25
+ * to be pasted into a bug report must not also paste our ingestion key into it.
26
+ * `sendBatch` adds the key at the moment of the request and nowhere else, which
27
+ * keeps the key entirely out of every code path that can reach a terminal.
28
+ */
29
+ export function buildBatch(events) {
30
+ return {
31
+ batch: events.map((e) => {
32
+ const ev = e;
33
+ return {
34
+ event: ev.event,
35
+ timestamp: ev.timestamp,
36
+ properties: {
37
+ ...(ev.properties ?? {}),
38
+ distinct_id: ev.distinct_id,
39
+ // Anonymous event: PostHog stores it without creating a person.
40
+ $process_person_profile: false,
41
+ },
42
+ };
43
+ }),
44
+ };
45
+ }
46
+ /**
47
+ * The ingest path.
48
+ *
49
+ * `/batch/` only, with no fallback. An earlier revision also tried `/e/`,
50
+ * because assign's `frontend/src/lib/posthog.ts` documents `events.nanonets.com`
51
+ * as serving the older path and answering `400 invalid_payload` to bodies it
52
+ * will not take. Probed directly, that host returns **401** to a well-formed
53
+ * batch with a bad key — so `/batch/` is there and does parse the body, and the
54
+ * historical 400 was gzip from `posthog-js` in a browser, which we never send.
55
+ *
56
+ * The fallback was therefore a second request that could not fire, and a branch
57
+ * production never exercises is worse than no branch. If a future host needs
58
+ * `/e/`, point `GRAFT_POSTHOG_HOST` at it and add the path back with evidence.
59
+ */
60
+ const INGEST_PATH = '/batch/';
61
+ export async function sendBatch(events) {
62
+ if (events.length === 0)
63
+ return { ok: true };
64
+ const key = posthogKey();
65
+ if (!key)
66
+ return { ok: false, error: 'no key' };
67
+ try {
68
+ const res = await fetch(`${posthogHost()}${INGEST_PATH}`, {
69
+ method: 'POST',
70
+ headers: { 'content-type': 'application/json' },
71
+ // The one place the key is ever attached: the request body itself.
72
+ body: JSON.stringify({ api_key: key, ...buildBatch(events) }),
73
+ signal: AbortSignal.timeout(SEND_TIMEOUT_MS),
74
+ });
75
+ // A 4xx is our bug (a bad key, a malformed body) and retrying it forever
76
+ // would pin the queue at its cap; only 5xx and transport errors go back on
77
+ // the queue — see shouldRequeue in flush.ts.
78
+ return { ok: res.ok, status: res.status };
79
+ }
80
+ catch (e) {
81
+ return { ok: false, error: e instanceof Error ? e.name : 'unknown' };
82
+ }
83
+ }
84
+ //# sourceMappingURL=send.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/telemetry/send.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEnD,4EAA4E;AAC5E,MAAM,eAAe,GAAG,IAAI,CAAC;AAS7B;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CAAC,MAAiB;IAC1C,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACtB,MAAM,EAAE,GAAG,CAAqG,CAAC;YACjH,OAAO;gBACL,KAAK,EAAE,EAAE,CAAC,KAAK;gBACf,SAAS,EAAE,EAAE,CAAC,SAAS;gBACvB,UAAU,EAAE;oBACV,GAAG,CAAC,EAAE,CAAC,UAAU,IAAI,EAAE,CAAC;oBACxB,WAAW,EAAE,EAAE,CAAC,WAAW;oBAC3B,gEAAgE;oBAChE,uBAAuB,EAAE,KAAK;iBAC/B;aACF,CAAC;QACJ,CAAC,CAAC;KACH,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,GAAG,SAAS,CAAC;AAE9B,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAiB;IAC/C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IAC7C,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAChD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,WAAW,EAAE,GAAG,WAAW,EAAE,EAAE;YACxD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,mEAAmE;YACnE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7D,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,eAAe,CAAC;SAC7C,CAAC,CAAC;QACH,yEAAyE;QACzE,2EAA2E;QAC3E,6CAA6C;QAC7C,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;IAC5C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;IACvE,CAAC;AACH,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { type AgentHost } from './contract.js';
2
+ /** Untouched for this long and the session is treated as over. */
3
+ export declare const SESSION_IDLE_MS: number;
4
+ /**
5
+ * Roll one session up into a `session_summary`, ignoring the idle gate — for a
6
+ * host with a real end-of-session signal (Cursor's `sessionEnd`). Returns 1 if
7
+ * queued, 0 if already summarised or telemetry is off. Never throws.
8
+ *
9
+ * The event is attributed to the host stamped on the session file when there is
10
+ * one (a session used from Cursor stays Cursor even if Claude's idle sweep is
11
+ * what finally flushes it); `opts.host` is only the fallback for a file written
12
+ * before host-stamping, or by a path that didn't stamp.
13
+ */
14
+ export declare function summarizeSession(repo: string, id: string, opts?: {
15
+ host?: AgentHost;
16
+ home?: string;
17
+ env?: NodeJS.ProcessEnv;
18
+ }): number;
19
+ /**
20
+ * Queue a `session_summary` for every closed, not-yet-summarised session in this
21
+ * repo. "Closed" = untouched for {@link SESSION_IDLE_MS}. Returns how many were
22
+ * queued (for tests). Never throws.
23
+ */
24
+ export declare function flushClosedSessions(repo: string, now?: number, home?: string, env?: NodeJS.ProcessEnv, host?: AgentHost): number;
25
+ //# sourceMappingURL=sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/telemetry/sessions.ts"],"names":[],"mappings":"AA0BA,OAAO,EAAkC,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAI/E,kEAAkE;AAClE,eAAO,MAAM,eAAe,QAAqB,CAAC;AAMlD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,SAAS,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAO,GACtE,MAAM,CA8BR;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,GAAG,SAAa,EAChB,IAAI,CAAC,EAAE,MAAM,EACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,EACvB,IAAI,GAAE,SAAyB,GAC9B,MAAM,CAiBR"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Rolling a finished agent session up into one `session_summary` event.
3
+ *
4
+ * This is the event that justifies the whole exercise. Everything else here
5
+ * counts commands; this one answers the question the product actually rests on —
6
+ * when an agent had both graft and grep available, which did it reach for, and
7
+ * how many tokens did that save? The counters already exist: the Claude Code
8
+ * hooks have been writing `graftReads`, `sourceReads` and `savedTokens` per
9
+ * session into `graft/.cache/session/` since well before telemetry did. Nothing
10
+ * new is measured here; a closed session is simply bucketed and queued.
11
+ *
12
+ * "Closed" is inferred from mtime by the idle sweep ({@link flushClosedSessions})
13
+ * because Claude Code has no reliable end-of-session hook — `Stop` fires per
14
+ * turn, and an editor that crashes fires nothing at all. A session untouched for
15
+ * two hours is over. Cursor DOES have `sessionEnd`, so it force-closes the exact
16
+ * conversation that just ended via {@link summarizeSession}, skipping the idle
17
+ * gate that would otherwise ignore the file the last tool use just wrote.
18
+ *
19
+ * Called from the session-start hook (the sweep) and the Cursor sessionEnd hook
20
+ * (the force-close) — both places guaranteed to run at the right moment. A hook
21
+ * must never touch the network (see the note atop upkeep.ts) and this doesn't:
22
+ * it only appends to the local queue.
23
+ */
24
+ import { statSync } from 'node:fs';
25
+ import { join } from 'node:path';
26
+ import { readSession, writeSession, sessionDir, listSessionIds } from '../claude/state.js';
27
+ import { countBucket, savedTokensBucket } from './contract.js';
28
+ import { track } from './track.js';
29
+ import { telemetryOn } from './gate.js';
30
+ /** Untouched for this long and the session is treated as over. */
31
+ export const SESSION_IDLE_MS = 2 * 60 * 60 * 1000;
32
+ /** Never roll up more than this in one hook run — a repo with a huge backlog of
33
+ * session files must not slow down a session start. The rest wait for the next. */
34
+ const MAX_PER_RUN = 20;
35
+ /**
36
+ * Roll one session up into a `session_summary`, ignoring the idle gate — for a
37
+ * host with a real end-of-session signal (Cursor's `sessionEnd`). Returns 1 if
38
+ * queued, 0 if already summarised or telemetry is off. Never throws.
39
+ *
40
+ * The event is attributed to the host stamped on the session file when there is
41
+ * one (a session used from Cursor stays Cursor even if Claude's idle sweep is
42
+ * what finally flushes it); `opts.host` is only the fallback for a file written
43
+ * before host-stamping, or by a path that didn't stamp.
44
+ */
45
+ export function summarizeSession(repo, id, opts = {}) {
46
+ try {
47
+ const { host = 'claude-code', home, env } = opts;
48
+ if (!telemetryOn(home, env))
49
+ return 0;
50
+ const s = readSession(repo, id);
51
+ if (s.summarized)
52
+ return 0;
53
+ // An empty session — opened, nothing asked — is still a fact worth having:
54
+ // it is the difference between "graft is installed" and "graft is used".
55
+ track('session_summary', {
56
+ graft_reads_bucket: countBucket(s.graftReads ?? 0),
57
+ source_reads_bucket: countBucket(s.sourceReads ?? 0),
58
+ saved_tokens_bucket: savedTokensBucket(s.savedTokens ?? 0),
59
+ // Saved vs *said*: the gap between these two is value the user never
60
+ // heard about. Only turns whose reply was actually readable are in
61
+ // either count (claude/tally.ts), so a host that exposes no transcript
62
+ // reports 0/0 rather than a misleading 0-out-of-many.
63
+ graft_turns_bucket: countBucket(s.graftTurns ?? 0),
64
+ reported_turns_bucket: countBucket(s.reportedTurns ?? 0),
65
+ }, { repo, host: s.host ?? host, home, env });
66
+ writeSession(repo, id, { ...s, summarized: true });
67
+ return 1;
68
+ }
69
+ catch {
70
+ return 0;
71
+ }
72
+ }
73
+ /**
74
+ * Queue a `session_summary` for every closed, not-yet-summarised session in this
75
+ * repo. "Closed" = untouched for {@link SESSION_IDLE_MS}. Returns how many were
76
+ * queued (for tests). Never throws.
77
+ */
78
+ export function flushClosedSessions(repo, now = Date.now(), home, env, host = 'claude-code') {
79
+ try {
80
+ if (!telemetryOn(home, env))
81
+ return 0;
82
+ const dir = sessionDir(repo);
83
+ let queued = 0;
84
+ for (const id of listSessionIds(repo).sort()) {
85
+ if (queued >= MAX_PER_RUN)
86
+ break;
87
+ let mtime;
88
+ try {
89
+ mtime = statSync(join(dir, `${id}.json`)).mtimeMs;
90
+ }
91
+ catch {
92
+ continue;
93
+ }
94
+ if (now - mtime < SESSION_IDLE_MS)
95
+ continue;
96
+ queued += summarizeSession(repo, id, { host, home, env });
97
+ }
98
+ return queued;
99
+ }
100
+ catch {
101
+ return 0; // a session start is never worth failing over a metric
102
+ }
103
+ }
104
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/telemetry/sessions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import type { AgentHost } from './contract.js';
2
+ /** One queued event, exactly as it will be sent. */
3
+ export interface QueuedEvent {
4
+ event: string;
5
+ properties: Record<string, string>;
6
+ /** ISO timestamp, so a batch flushed a day late still lands on the right day. */
7
+ timestamp: string;
8
+ /** The install id, used as PostHog's `distinct_id` at send time. */
9
+ distinct_id: string;
10
+ }
11
+ export interface TrackContext {
12
+ /** Repo root, when the call site has one — supplies `repo_id`. */
13
+ repo?: string;
14
+ /** Which surface is calling. Defaults to the CLI. */
15
+ host?: AgentHost;
16
+ /** Test seam: a scratch `$HOME`. Production callers never pass this. */
17
+ home?: string;
18
+ /** Test seam: an environment to gate against. Production callers never pass
19
+ * this — a real run must be gated on the real `DO_NOT_TRACK` and `CI`. */
20
+ env?: NodeJS.ProcessEnv;
21
+ }
22
+ /**
23
+ * Which agent graft is running under, from the surface plus one env probe.
24
+ *
25
+ * Only the PRESENCE of `CLAUDECODE` is read, never its value — the distinction
26
+ * matters: a value could be anything a user's shell profile put there, presence
27
+ * is a boolean. Anything unrecognised is `cli`, which is the honest answer.
28
+ */
29
+ export declare function detectHost(explicit?: AgentHost, env?: NodeJS.ProcessEnv): AgentHost;
30
+ /**
31
+ * Record one event, if the contract allows it and the gates are open.
32
+ *
33
+ * Returns the event that was queued, or null — the return value exists for
34
+ * tests and for `graft telemetry debug`, not for control flow at call sites.
35
+ */
36
+ export declare function track(event: string, props?: Record<string, string | undefined>, ctx?: TrackContext): QueuedEvent | null;
37
+ /**
38
+ * The `first_run` event, fired the once. Separate from `track` because its
39
+ * trigger is a comparison against persisted state rather than anything a call
40
+ * site knows.
41
+ *
42
+ * Gated on `firstRunAt` and NOT on `installId().firstRun`, which is the whole
43
+ * reason this field exists: the npm postinstall hook mints the install id before
44
+ * any command runs, so "did this call mint the id" is false on the genuine first
45
+ * command and would have silenced this event permanently.
46
+ */
47
+ export declare function trackFirstRunIfNew(ctx?: TrackContext): void;
48
+ /**
49
+ * The `install` event, fired from the npm postinstall hook.
50
+ *
51
+ * Once per machine per version: an upgrade is a real install and worth counting,
52
+ * a second `npm install` of a version already recorded is not. Unique machines
53
+ * are then distinct install ids, exactly as for every other event.
54
+ *
55
+ * Nothing here is an exception to the gates — a fork with no key, CI,
56
+ * `DO_NOT_TRACK` and `graft telemetry disable` all still close it. It is only a
57
+ * call site that happens to run outside a command.
58
+ */
59
+ export declare function trackInstallIfNew(ctx?: TrackContext & {
60
+ global?: boolean;
61
+ }): void;
62
+ //# sourceMappingURL=track.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track.d.ts","sourceRoot":"","sources":["../../src/telemetry/track.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAO/C,oDAAoD;AACpD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;+EAC2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,SAAS,CAIhG;AAuBD;;;;;GAKG;AACH,wBAAgB,KAAK,CACnB,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAM,EAC9C,GAAG,GAAE,YAAiB,GACrB,WAAW,GAAG,IAAI,CA0BpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,YAAiB,GAAG,IAAI,CAO/D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,YAAY,GAAG;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,IAAI,CAQrF"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * `track()` — the only way an event is ever created, and the place the contract
3
+ * is enforced.
4
+ *
5
+ * Call sites pass a name and some properties; this drops anything the contract
6
+ * (contract.ts) does not list, stamps the common properties, and appends to the
7
+ * local queue. It never sends, never blocks, and never throws — a telemetry bug
8
+ * must be incapable of failing a build or a query.
9
+ *
10
+ * Two rules do the actual privacy work, and both are enforced here rather than
11
+ * asked of callers:
12
+ *
13
+ * • unknown event → dropped whole; unknown property key → dropped, the rest of
14
+ * the event survives. A careless `track('query', { path })` sends `query`
15
+ * without a path.
16
+ * • non-string values → dropped. Everything that crosses the wire is a bucket
17
+ * label or an enum member, so a raw count or a raw message cannot get out
18
+ * even under a key that happens to be allowed. Where the contract fixes a
19
+ * key's values (`VALUES`), a value outside them is dropped too.
20
+ */
21
+ import { EVENTS, allowedValue } from './contract.js';
22
+ import { enqueue } from './queue.js';
23
+ import { telemetryOn } from './gate.js';
24
+ import { installId, patchState, readState, repoId } from './identity.js';
25
+ import { runningVersion } from '../upkeep.js';
26
+ import { brand } from '../brand.js';
27
+ /**
28
+ * Which agent graft is running under, from the surface plus one env probe.
29
+ *
30
+ * Only the PRESENCE of `CLAUDECODE` is read, never its value — the distinction
31
+ * matters: a value could be anything a user's shell profile put there, presence
32
+ * is a boolean. Anything unrecognised is `cli`, which is the honest answer.
33
+ */
34
+ export function detectHost(explicit, env = process.env) {
35
+ if (explicit)
36
+ return explicit;
37
+ if (env.CLAUDECODE !== undefined)
38
+ return 'claude-code';
39
+ return 'cli';
40
+ }
41
+ /** The properties every event carries. Kept in one place so no call site can
42
+ * disagree about them. */
43
+ function commonProps(ctx) {
44
+ const props = {
45
+ app_version: runningVersion(),
46
+ os: process.platform,
47
+ arch: process.arch,
48
+ node_major: String(process.versions.node.split('.')[0]),
49
+ ci: 'false', // an event only exists at all when the CI gate passed
50
+ agent_host: detectHost(ctx.host, ctx.env ?? process.env),
51
+ // `trail` or `graft`: which name started this process. The share of
52
+ // queries under `trail` is how the rename is measured, and both names
53
+ // share one install id, so nobody is counted twice.
54
+ cli_name: brand(ctx.env ?? process.env),
55
+ };
56
+ if (ctx.repo) {
57
+ try {
58
+ props.repo_id = repoId(ctx.repo);
59
+ }
60
+ catch { /* unwritable cache — omit */ }
61
+ }
62
+ return props;
63
+ }
64
+ /**
65
+ * Record one event, if the contract allows it and the gates are open.
66
+ *
67
+ * Returns the event that was queued, or null — the return value exists for
68
+ * tests and for `graft telemetry debug`, not for control flow at call sites.
69
+ */
70
+ export function track(event, props = {}, ctx = {}) {
71
+ try {
72
+ if (!telemetryOn(ctx.home, ctx.env))
73
+ return null;
74
+ // `Object.hasOwn`, not `EVENTS[event]`: a bare object literal inherits from
75
+ // Object.prototype, so `EVENTS['constructor']` is truthy and would sail past
76
+ // a plain existence check — only failing later, on a TypeError swallowed by
77
+ // the catch below. The allowlist must reject by design, not by exception.
78
+ if (!Object.hasOwn(EVENTS, event))
79
+ return null;
80
+ const allowed = EVENTS[event];
81
+ const properties = commonProps(ctx);
82
+ for (const [k, v] of Object.entries(props)) {
83
+ if (allowed.has(k) && typeof v === 'string' && allowedValue(event, k, v))
84
+ properties[k] = v;
85
+ }
86
+ const queued = {
87
+ event,
88
+ properties,
89
+ timestamp: new Date().toISOString(),
90
+ distinct_id: installId(ctx.home).id,
91
+ };
92
+ enqueue(queued, ctx.home);
93
+ return queued;
94
+ }
95
+ catch {
96
+ return null; // telemetry never surfaces as a user-visible failure
97
+ }
98
+ }
99
+ /**
100
+ * The `first_run` event, fired the once. Separate from `track` because its
101
+ * trigger is a comparison against persisted state rather than anything a call
102
+ * site knows.
103
+ *
104
+ * Gated on `firstRunAt` and NOT on `installId().firstRun`, which is the whole
105
+ * reason this field exists: the npm postinstall hook mints the install id before
106
+ * any command runs, so "did this call mint the id" is false on the genuine first
107
+ * command and would have silenced this event permanently.
108
+ */
109
+ export function trackFirstRunIfNew(ctx = {}) {
110
+ try {
111
+ if (!telemetryOn(ctx.home, ctx.env))
112
+ return;
113
+ if (readState(ctx.home)?.firstRunAt)
114
+ return;
115
+ track('first_run', {}, ctx);
116
+ patchState({ firstRunAt: new Date().toISOString() }, ctx.home);
117
+ }
118
+ catch { /* never */ }
119
+ }
120
+ /**
121
+ * The `install` event, fired from the npm postinstall hook.
122
+ *
123
+ * Once per machine per version: an upgrade is a real install and worth counting,
124
+ * a second `npm install` of a version already recorded is not. Unique machines
125
+ * are then distinct install ids, exactly as for every other event.
126
+ *
127
+ * Nothing here is an exception to the gates — a fork with no key, CI,
128
+ * `DO_NOT_TRACK` and `graft telemetry disable` all still close it. It is only a
129
+ * call site that happens to run outside a command.
130
+ */
131
+ export function trackInstallIfNew(ctx = {}) {
132
+ try {
133
+ if (!telemetryOn(ctx.home, ctx.env))
134
+ return;
135
+ const version = runningVersion();
136
+ if (readState(ctx.home)?.installedVersion === version)
137
+ return;
138
+ track('install', { global: String(ctx.global ?? false) }, ctx);
139
+ patchState({ installedVersion: version }, ctx.home);
140
+ }
141
+ catch { /* never */ }
142
+ }
143
+ //# sourceMappingURL=track.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track.js","sourceRoot":"","sources":["../../src/telemetry/track.ts"],"names":[],"mappings":"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"}