@nanonets/graft 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (791) hide show
  1. package/README.md +26 -21
  2. package/dist/ask/ask.d.ts +8 -0
  3. package/dist/ask/ask.d.ts.map +1 -1
  4. package/dist/ask/ask.js +33 -12
  5. package/dist/ask/ask.js.map +1 -1
  6. package/dist/blast/render.js +1 -1
  7. package/dist/blast/render.js.map +1 -1
  8. package/dist/brand.js +1 -1
  9. package/dist/brand.js.map +1 -1
  10. package/dist/check/learning-edits.d.ts +18 -3
  11. package/dist/check/learning-edits.d.ts.map +1 -1
  12. package/dist/check/learning-edits.js +54 -10
  13. package/dist/check/learning-edits.js.map +1 -1
  14. package/dist/check/local-check.d.ts +7 -3
  15. package/dist/check/local-check.d.ts.map +1 -1
  16. package/dist/check/local-check.js +55 -11
  17. package/dist/check/local-check.js.map +1 -1
  18. package/dist/check/types.d.ts +11 -2
  19. package/dist/check/types.d.ts.map +1 -1
  20. package/dist/claude/format.d.ts +11 -7
  21. package/dist/claude/format.d.ts.map +1 -1
  22. package/dist/claude/format.js +50 -28
  23. package/dist/claude/format.js.map +1 -1
  24. package/dist/claude/hook-io.d.ts +44 -0
  25. package/dist/claude/hook-io.d.ts.map +1 -0
  26. package/dist/claude/hook-io.js +144 -0
  27. package/dist/claude/hook-io.js.map +1 -0
  28. package/dist/claude/hook-layers.d.ts +4 -2
  29. package/dist/claude/hook-layers.d.ts.map +1 -1
  30. package/dist/claude/hook-layers.js +21 -4
  31. package/dist/claude/hook-layers.js.map +1 -1
  32. package/dist/claude/hooks.d.ts +9 -32
  33. package/dist/claude/hooks.d.ts.map +1 -1
  34. package/dist/claude/hooks.js +154 -350
  35. package/dist/claude/hooks.js.map +1 -1
  36. package/dist/claude/init.d.ts.map +1 -1
  37. package/dist/claude/init.js +56 -18
  38. package/dist/claude/init.js.map +1 -1
  39. package/dist/claude/note-signals.d.ts +6 -0
  40. package/dist/claude/note-signals.d.ts.map +1 -1
  41. package/dist/claude/note-signals.js +14 -3
  42. package/dist/claude/note-signals.js.map +1 -1
  43. package/dist/claude/post-bash.d.ts +26 -0
  44. package/dist/claude/post-bash.d.ts.map +1 -0
  45. package/dist/claude/post-bash.js +191 -0
  46. package/dist/claude/post-bash.js.map +1 -0
  47. package/dist/claude/pre-bash-skills.d.ts +21 -0
  48. package/dist/claude/pre-bash-skills.d.ts.map +1 -0
  49. package/dist/claude/pre-bash-skills.js +253 -0
  50. package/dist/claude/pre-bash-skills.js.map +1 -0
  51. package/dist/claude/pre-bash.d.ts +50 -0
  52. package/dist/claude/pre-bash.d.ts.map +1 -0
  53. package/dist/claude/pre-bash.js +245 -0
  54. package/dist/claude/pre-bash.js.map +1 -0
  55. package/dist/claude/prompt-consent.d.ts +19 -0
  56. package/dist/claude/prompt-consent.d.ts.map +1 -0
  57. package/dist/claude/prompt-consent.js +14 -0
  58. package/dist/claude/prompt-consent.js.map +1 -0
  59. package/dist/claude/prompt-hook.d.ts +76 -0
  60. package/dist/claude/prompt-hook.d.ts.map +1 -0
  61. package/dist/claude/prompt-hook.js +303 -0
  62. package/dist/claude/prompt-hook.js.map +1 -0
  63. package/dist/claude/prompt-skills.d.ts +19 -0
  64. package/dist/claude/prompt-skills.d.ts.map +1 -0
  65. package/dist/claude/prompt-skills.js +305 -0
  66. package/dist/claude/prompt-skills.js.map +1 -0
  67. package/dist/claude/session-metrics.js +4 -4
  68. package/dist/claude/session-metrics.js.map +1 -1
  69. package/dist/claude/settings-merge.d.ts +10 -0
  70. package/dist/claude/settings-merge.d.ts.map +1 -1
  71. package/dist/claude/settings-merge.js +46 -17
  72. package/dist/claude/settings-merge.js.map +1 -1
  73. package/dist/claude/shim-template.d.ts.map +1 -1
  74. package/dist/claude/shim-template.js +86 -28
  75. package/dist/claude/shim-template.js.map +1 -1
  76. package/dist/claude/skill-template.d.ts +2 -0
  77. package/dist/claude/skill-template.d.ts.map +1 -1
  78. package/dist/claude/skill-template.js +53 -34
  79. package/dist/claude/skill-template.js.map +1 -1
  80. package/dist/claude/skill-use.d.ts +16 -0
  81. package/dist/claude/skill-use.d.ts.map +1 -0
  82. package/dist/claude/skill-use.js +78 -0
  83. package/dist/claude/skill-use.js.map +1 -0
  84. package/dist/claude/skills-hook-util.d.ts +25 -0
  85. package/dist/claude/skills-hook-util.d.ts.map +1 -0
  86. package/dist/claude/skills-hook-util.js +71 -0
  87. package/dist/claude/skills-hook-util.js.map +1 -0
  88. package/dist/claude/skills-wiring.d.ts +26 -0
  89. package/dist/claude/skills-wiring.d.ts.map +1 -0
  90. package/dist/claude/skills-wiring.js +110 -0
  91. package/dist/claude/skills-wiring.js.map +1 -0
  92. package/dist/claude/start-init.d.ts +13 -0
  93. package/dist/claude/start-init.d.ts.map +1 -0
  94. package/dist/claude/start-init.js +75 -0
  95. package/dist/claude/start-init.js.map +1 -0
  96. package/dist/claude/start-skills.d.ts +31 -0
  97. package/dist/claude/start-skills.d.ts.map +1 -0
  98. package/dist/claude/start-skills.js +274 -0
  99. package/dist/claude/start-skills.js.map +1 -0
  100. package/dist/claude/state.d.ts +19 -7
  101. package/dist/claude/state.d.ts.map +1 -1
  102. package/dist/claude/state.js +59 -4
  103. package/dist/claude/state.js.map +1 -1
  104. package/dist/claude/stop-skills.d.ts +23 -0
  105. package/dist/claude/stop-skills.d.ts.map +1 -0
  106. package/dist/claude/stop-skills.js +233 -0
  107. package/dist/claude/stop-skills.js.map +1 -0
  108. package/dist/claude/stop.d.ts +23 -0
  109. package/dist/claude/stop.d.ts.map +1 -0
  110. package/dist/claude/stop.js +219 -0
  111. package/dist/claude/stop.js.map +1 -0
  112. package/dist/claude/tally.d.ts +29 -41
  113. package/dist/claude/tally.d.ts.map +1 -1
  114. package/dist/claude/tally.js +111 -165
  115. package/dist/claude/tally.js.map +1 -1
  116. package/dist/claude/walls-hook.d.ts +52 -0
  117. package/dist/claude/walls-hook.d.ts.map +1 -0
  118. package/dist/claude/walls-hook.js +279 -0
  119. package/dist/claude/walls-hook.js.map +1 -0
  120. package/dist/cli-epilogue.d.ts.map +1 -1
  121. package/dist/cli-epilogue.js +24 -14
  122. package/dist/cli-epilogue.js.map +1 -1
  123. package/dist/cli-meta.js +3 -3
  124. package/dist/cli-meta.js.map +1 -1
  125. package/dist/cli-picker.d.ts +24 -0
  126. package/dist/cli-picker.d.ts.map +1 -1
  127. package/dist/cli-picker.js +44 -11
  128. package/dist/cli-picker.js.map +1 -1
  129. package/dist/cli.d.ts.map +1 -1
  130. package/dist/cli.js +475 -390
  131. package/dist/cli.js.map +1 -1
  132. package/dist/cloud/head-learnings.d.ts +13 -0
  133. package/dist/cloud/head-learnings.d.ts.map +1 -0
  134. package/dist/cloud/head-learnings.js +84 -0
  135. package/dist/cloud/head-learnings.js.map +1 -0
  136. package/dist/cloud/lookup-activity.d.ts +18 -0
  137. package/dist/cloud/lookup-activity.d.ts.map +1 -0
  138. package/dist/cloud/lookup-activity.js +56 -0
  139. package/dist/cloud/lookup-activity.js.map +1 -0
  140. package/dist/cloud/nudge.d.ts +15 -7
  141. package/dist/cloud/nudge.d.ts.map +1 -1
  142. package/dist/cloud/nudge.js +45 -33
  143. package/dist/cloud/nudge.js.map +1 -1
  144. package/dist/cloud/onboarding.d.ts +273 -0
  145. package/dist/cloud/onboarding.d.ts.map +1 -0
  146. package/dist/cloud/onboarding.js +612 -0
  147. package/dist/cloud/onboarding.js.map +1 -0
  148. package/dist/cloud/other-repos.d.ts +39 -0
  149. package/dist/cloud/other-repos.d.ts.map +1 -0
  150. package/dist/cloud/other-repos.js +137 -0
  151. package/dist/cloud/other-repos.js.map +1 -0
  152. package/dist/cloud/pair.d.ts +86 -0
  153. package/dist/cloud/pair.d.ts.map +1 -0
  154. package/dist/cloud/pair.js +270 -0
  155. package/dist/cloud/pair.js.map +1 -0
  156. package/dist/cloud/workspace-sync.d.ts +75 -11
  157. package/dist/cloud/workspace-sync.d.ts.map +1 -1
  158. package/dist/cloud/workspace-sync.js +409 -53
  159. package/dist/cloud/workspace-sync.js.map +1 -1
  160. package/dist/cloud/workspace.d.ts +95 -10
  161. package/dist/cloud/workspace.d.ts.map +1 -1
  162. package/dist/cloud/workspace.js +112 -9
  163. package/dist/cloud/workspace.js.map +1 -1
  164. package/dist/graph/native.d.ts +2 -0
  165. package/dist/graph/native.d.ts.map +1 -1
  166. package/dist/graph/native.js +29 -4
  167. package/dist/graph/native.js.map +1 -1
  168. package/dist/hosts/claude-global.d.ts +14 -0
  169. package/dist/hosts/claude-global.d.ts.map +1 -1
  170. package/dist/hosts/claude-global.js +39 -4
  171. package/dist/hosts/claude-global.js.map +1 -1
  172. package/dist/hosts/codex-hooks.d.ts +6 -0
  173. package/dist/hosts/codex-hooks.d.ts.map +1 -1
  174. package/dist/hosts/codex-hooks.js +89 -16
  175. package/dist/hosts/codex-hooks.js.map +1 -1
  176. package/dist/hosts/codex-trust.d.ts +28 -0
  177. package/dist/hosts/codex-trust.d.ts.map +1 -0
  178. package/dist/hosts/codex-trust.js +280 -0
  179. package/dist/hosts/codex-trust.js.map +1 -0
  180. package/dist/hosts/cursor-hooks.d.ts.map +1 -1
  181. package/dist/hosts/cursor-hooks.js +3 -1
  182. package/dist/hosts/cursor-hooks.js.map +1 -1
  183. package/dist/init/candidates.d.ts +60 -0
  184. package/dist/init/candidates.d.ts.map +1 -0
  185. package/dist/init/candidates.js +202 -0
  186. package/dist/init/candidates.js.map +1 -0
  187. package/dist/init/claude-call.d.ts +51 -0
  188. package/dist/init/claude-call.d.ts.map +1 -0
  189. package/dist/init/claude-call.js +199 -0
  190. package/dist/init/claude-call.js.map +1 -0
  191. package/dist/init/discover-worker.d.ts +18 -0
  192. package/dist/init/discover-worker.d.ts.map +1 -0
  193. package/dist/init/discover-worker.js +111 -0
  194. package/dist/init/discover-worker.js.map +1 -0
  195. package/dist/init/handover.d.ts +16 -0
  196. package/dist/init/handover.d.ts.map +1 -0
  197. package/dist/init/handover.js +92 -0
  198. package/dist/init/handover.js.map +1 -0
  199. package/dist/init/howtos.d.ts +20 -0
  200. package/dist/init/howtos.d.ts.map +1 -0
  201. package/dist/init/howtos.js +90 -0
  202. package/dist/init/howtos.js.map +1 -0
  203. package/dist/init/init-record.d.ts +134 -0
  204. package/dist/init/init-record.d.ts.map +1 -0
  205. package/dist/init/init-record.js +339 -0
  206. package/dist/init/init-record.js.map +1 -0
  207. package/dist/init/install-paths.d.ts +6 -0
  208. package/dist/init/install-paths.d.ts.map +1 -0
  209. package/dist/init/install-paths.js +87 -0
  210. package/dist/init/install-paths.js.map +1 -0
  211. package/dist/init/install-record.d.ts +21 -0
  212. package/dist/init/install-record.d.ts.map +1 -0
  213. package/dist/init/install-record.js +144 -0
  214. package/dist/init/install-record.js.map +1 -0
  215. package/dist/init/keep-answer.d.ts +43 -0
  216. package/dist/init/keep-answer.d.ts.map +1 -0
  217. package/dist/init/keep-answer.js +118 -0
  218. package/dist/init/keep-answer.js.map +1 -0
  219. package/dist/init/keep-gate.d.ts +30 -0
  220. package/dist/init/keep-gate.d.ts.map +1 -0
  221. package/dist/init/keep-gate.js +178 -0
  222. package/dist/init/keep-gate.js.map +1 -0
  223. package/dist/init/keep-list.d.ts +67 -0
  224. package/dist/init/keep-list.d.ts.map +1 -0
  225. package/dist/init/keep-list.js +232 -0
  226. package/dist/init/keep-list.js.map +1 -0
  227. package/dist/init/keep.d.ts +52 -0
  228. package/dist/init/keep.d.ts.map +1 -0
  229. package/dist/init/keep.js +393 -0
  230. package/dist/init/keep.js.map +1 -0
  231. package/dist/init/look-back-mine.d.ts +37 -0
  232. package/dist/init/look-back-mine.d.ts.map +1 -0
  233. package/dist/init/look-back-mine.js +215 -0
  234. package/dist/init/look-back-mine.js.map +1 -0
  235. package/dist/init/look-back.d.ts +81 -0
  236. package/dist/init/look-back.d.ts.map +1 -0
  237. package/dist/init/look-back.js +170 -0
  238. package/dist/init/look-back.js.map +1 -0
  239. package/dist/init/miners.d.ts +46 -0
  240. package/dist/init/miners.d.ts.map +1 -0
  241. package/dist/init/miners.js +141 -0
  242. package/dist/init/miners.js.map +1 -0
  243. package/dist/init/name-call.d.ts +37 -0
  244. package/dist/init/name-call.d.ts.map +1 -0
  245. package/dist/init/name-call.js +151 -0
  246. package/dist/init/name-call.js.map +1 -0
  247. package/dist/init/plain-file.d.ts +11 -0
  248. package/dist/init/plain-file.d.ts.map +1 -0
  249. package/dist/init/plain-file.js +83 -0
  250. package/dist/init/plain-file.js.map +1 -0
  251. package/dist/init/prefs.d.ts +70 -0
  252. package/dist/init/prefs.d.ts.map +1 -0
  253. package/dist/init/prefs.js +282 -0
  254. package/dist/init/prefs.js.map +1 -0
  255. package/dist/init/relay.d.ts +15 -0
  256. package/dist/init/relay.d.ts.map +1 -0
  257. package/dist/init/relay.js +15 -0
  258. package/dist/init/relay.js.map +1 -0
  259. package/dist/init/replay-runs.d.ts +121 -0
  260. package/dist/init/replay-runs.d.ts.map +1 -0
  261. package/dist/init/replay-runs.js +457 -0
  262. package/dist/init/replay-runs.js.map +1 -0
  263. package/dist/init/replay-tamper.d.ts +18 -0
  264. package/dist/init/replay-tamper.d.ts.map +1 -0
  265. package/dist/init/replay-tamper.js +87 -0
  266. package/dist/init/replay-tamper.js.map +1 -0
  267. package/dist/init/review-screen.d.ts +74 -0
  268. package/dist/init/review-screen.d.ts.map +1 -0
  269. package/dist/init/review-screen.js +258 -0
  270. package/dist/init/review-screen.js.map +1 -0
  271. package/dist/init/review-tty.d.ts +29 -0
  272. package/dist/init/review-tty.d.ts.map +1 -0
  273. package/dist/init/review-tty.js +218 -0
  274. package/dist/init/review-tty.js.map +1 -0
  275. package/dist/init/review.d.ts +12 -0
  276. package/dist/init/review.d.ts.map +1 -0
  277. package/dist/init/review.js +12 -0
  278. package/dist/init/review.js.map +1 -0
  279. package/dist/init/run.d.ts +87 -0
  280. package/dist/init/run.d.ts.map +1 -0
  281. package/dist/init/run.js +347 -0
  282. package/dist/init/run.js.map +1 -0
  283. package/dist/init/sessions.d.ts +63 -0
  284. package/dist/init/sessions.d.ts.map +1 -0
  285. package/dist/init/sessions.js +377 -0
  286. package/dist/init/sessions.js.map +1 -0
  287. package/dist/init/transcript.d.ts +81 -0
  288. package/dist/init/transcript.d.ts.map +1 -0
  289. package/dist/init/transcript.js +216 -0
  290. package/dist/init/transcript.js.map +1 -0
  291. package/dist/init/types.d.ts +139 -0
  292. package/dist/init/types.d.ts.map +1 -0
  293. package/dist/init/types.js +2 -0
  294. package/dist/init/types.js.map +1 -0
  295. package/dist/mcp/brains.d.ts +72 -0
  296. package/dist/mcp/brains.d.ts.map +1 -0
  297. package/dist/mcp/brains.js +220 -0
  298. package/dist/mcp/brains.js.map +1 -0
  299. package/dist/mcp/server.d.ts +3 -1
  300. package/dist/mcp/server.d.ts.map +1 -1
  301. package/dist/mcp/server.js +40 -5
  302. package/dist/mcp/server.js.map +1 -1
  303. package/dist/mcp/tools.d.ts.map +1 -1
  304. package/dist/mcp/tools.js +7 -2
  305. package/dist/mcp/tools.js.map +1 -1
  306. package/dist/notes/cold-start.d.ts +5 -184
  307. package/dist/notes/cold-start.d.ts.map +1 -1
  308. package/dist/notes/cold-start.js +5 -575
  309. package/dist/notes/cold-start.js.map +1 -1
  310. package/dist/notes/edit-overlap.d.ts.map +1 -1
  311. package/dist/notes/edit-overlap.js +4 -1
  312. package/dist/notes/edit-overlap.js.map +1 -1
  313. package/dist/notes/every-repo.d.ts +18 -0
  314. package/dist/notes/every-repo.d.ts.map +1 -0
  315. package/dist/notes/every-repo.js +86 -0
  316. package/dist/notes/every-repo.js.map +1 -0
  317. package/dist/notes/guardrails.d.ts +8 -0
  318. package/dist/notes/guardrails.d.ts.map +1 -0
  319. package/dist/notes/guardrails.js +38 -0
  320. package/dist/notes/guardrails.js.map +1 -0
  321. package/dist/notes/lesson-index.d.ts +13 -0
  322. package/dist/notes/lesson-index.d.ts.map +1 -0
  323. package/dist/notes/lesson-index.js +135 -0
  324. package/dist/notes/lesson-index.js.map +1 -0
  325. package/dist/notes/linked-learnings.d.ts +8 -0
  326. package/dist/notes/linked-learnings.d.ts.map +1 -0
  327. package/dist/notes/linked-learnings.js +113 -0
  328. package/dist/notes/linked-learnings.js.map +1 -0
  329. package/dist/notes/notes.d.ts +8 -4
  330. package/dist/notes/notes.d.ts.map +1 -1
  331. package/dist/notes/notes.js +9 -7
  332. package/dist/notes/notes.js.map +1 -1
  333. package/dist/notes/pr-reviews.d.ts +4 -0
  334. package/dist/notes/pr-reviews.d.ts.map +1 -1
  335. package/dist/notes/pr-reviews.js +6 -5
  336. package/dist/notes/pr-reviews.js.map +1 -1
  337. package/dist/notes/replace.d.ts +14 -0
  338. package/dist/notes/replace.d.ts.map +1 -0
  339. package/dist/notes/replace.js +38 -0
  340. package/dist/notes/replace.js.map +1 -0
  341. package/dist/notes/repo-notes-dir.d.ts +2 -0
  342. package/dist/notes/repo-notes-dir.d.ts.map +1 -0
  343. package/dist/notes/repo-notes-dir.js +16 -0
  344. package/dist/notes/repo-notes-dir.js.map +1 -0
  345. package/dist/notes/repo-trail.d.ts +1 -1
  346. package/dist/notes/repo-trail.d.ts.map +1 -1
  347. package/dist/notes/repo-trail.js +24 -4
  348. package/dist/notes/repo-trail.js.map +1 -1
  349. package/dist/search/grep.d.ts.map +1 -1
  350. package/dist/search/grep.js +4 -2
  351. package/dist/search/grep.js.map +1 -1
  352. package/dist/search/user-regexp.d.ts +26 -0
  353. package/dist/search/user-regexp.d.ts.map +1 -0
  354. package/dist/search/user-regexp.js +150 -0
  355. package/dist/search/user-regexp.js.map +1 -0
  356. package/dist/skills/anchors-cli.d.ts +38 -0
  357. package/dist/skills/anchors-cli.d.ts.map +1 -0
  358. package/dist/skills/anchors-cli.js +279 -0
  359. package/dist/skills/anchors-cli.js.map +1 -0
  360. package/dist/skills/anchors-git.d.ts +29 -0
  361. package/dist/skills/anchors-git.d.ts.map +1 -0
  362. package/dist/skills/anchors-git.js +241 -0
  363. package/dist/skills/anchors-git.js.map +1 -0
  364. package/dist/skills/anchors-wide.d.ts +25 -0
  365. package/dist/skills/anchors-wide.d.ts.map +1 -0
  366. package/dist/skills/anchors-wide.js +481 -0
  367. package/dist/skills/anchors-wide.js.map +1 -0
  368. package/dist/skills/anchors.d.ts +32 -0
  369. package/dist/skills/anchors.d.ts.map +1 -0
  370. package/dist/skills/anchors.js +485 -0
  371. package/dist/skills/anchors.js.map +1 -0
  372. package/dist/skills/check-result.d.ts +26 -0
  373. package/dist/skills/check-result.d.ts.map +1 -0
  374. package/dist/skills/check-result.js +46 -0
  375. package/dist/skills/check-result.js.map +1 -0
  376. package/dist/skills/cli-skills-1b.d.ts +17 -0
  377. package/dist/skills/cli-skills-1b.d.ts.map +1 -0
  378. package/dist/skills/cli-skills-1b.js +166 -0
  379. package/dist/skills/cli-skills-1b.js.map +1 -0
  380. package/dist/skills/cli-skills-2.d.ts +35 -0
  381. package/dist/skills/cli-skills-2.d.ts.map +1 -0
  382. package/dist/skills/cli-skills-2.js +129 -0
  383. package/dist/skills/cli-skills-2.js.map +1 -0
  384. package/dist/skills/cli-skills-report-1b.d.ts +49 -0
  385. package/dist/skills/cli-skills-report-1b.d.ts.map +1 -0
  386. package/dist/skills/cli-skills-report-1b.js +191 -0
  387. package/dist/skills/cli-skills-report-1b.js.map +1 -0
  388. package/dist/skills/cli-skills-report.d.ts +86 -0
  389. package/dist/skills/cli-skills-report.d.ts.map +1 -0
  390. package/dist/skills/cli-skills-report.js +121 -0
  391. package/dist/skills/cli-skills-report.js.map +1 -0
  392. package/dist/skills/cli-skills.d.ts +30 -0
  393. package/dist/skills/cli-skills.d.ts.map +1 -0
  394. package/dist/skills/cli-skills.js +379 -0
  395. package/dist/skills/cli-skills.js.map +1 -0
  396. package/dist/skills/codex-sandbox.d.ts +6 -0
  397. package/dist/skills/codex-sandbox.d.ts.map +1 -0
  398. package/dist/skills/codex-sandbox.js +55 -0
  399. package/dist/skills/codex-sandbox.js.map +1 -0
  400. package/dist/skills/compile-split.d.ts +57 -0
  401. package/dist/skills/compile-split.d.ts.map +1 -0
  402. package/dist/skills/compile-split.js +550 -0
  403. package/dist/skills/compile-split.js.map +1 -0
  404. package/dist/skills/compile-version.d.ts +37 -0
  405. package/dist/skills/compile-version.d.ts.map +1 -0
  406. package/dist/skills/compile-version.js +56 -0
  407. package/dist/skills/compile-version.js.map +1 -0
  408. package/dist/skills/compile-waits.d.ts +12 -0
  409. package/dist/skills/compile-waits.d.ts.map +1 -0
  410. package/dist/skills/compile-waits.js +52 -0
  411. package/dist/skills/compile-waits.js.map +1 -0
  412. package/dist/skills/compile-words.d.ts +85 -0
  413. package/dist/skills/compile-words.d.ts.map +1 -0
  414. package/dist/skills/compile-words.js +445 -0
  415. package/dist/skills/compile-words.js.map +1 -0
  416. package/dist/skills/compile.d.ts +42 -0
  417. package/dist/skills/compile.d.ts.map +1 -0
  418. package/dist/skills/compile.js +505 -0
  419. package/dist/skills/compile.js.map +1 -0
  420. package/dist/skills/credit-facts.d.ts +8 -0
  421. package/dist/skills/credit-facts.d.ts.map +1 -0
  422. package/dist/skills/credit-facts.js +32 -0
  423. package/dist/skills/credit-facts.js.map +1 -0
  424. package/dist/skills/credit.d.ts +86 -0
  425. package/dist/skills/credit.d.ts.map +1 -0
  426. package/dist/skills/credit.js +259 -0
  427. package/dist/skills/credit.js.map +1 -0
  428. package/dist/skills/directive-words.d.ts +11 -0
  429. package/dist/skills/directive-words.d.ts.map +1 -0
  430. package/dist/skills/directive-words.js +33 -0
  431. package/dist/skills/directive-words.js.map +1 -0
  432. package/dist/skills/fidelity.d.ts +42 -0
  433. package/dist/skills/fidelity.d.ts.map +1 -0
  434. package/dist/skills/fidelity.js +138 -0
  435. package/dist/skills/fidelity.js.map +1 -0
  436. package/dist/skills/fold-gated.d.ts +3 -0
  437. package/dist/skills/fold-gated.d.ts.map +1 -0
  438. package/dist/skills/fold-gated.js +78 -0
  439. package/dist/skills/fold-gated.js.map +1 -0
  440. package/dist/skills/fold.d.ts +87 -0
  441. package/dist/skills/fold.d.ts.map +1 -0
  442. package/dist/skills/fold.js +348 -0
  443. package/dist/skills/fold.js.map +1 -0
  444. package/dist/skills/follow-learnings.d.ts +128 -0
  445. package/dist/skills/follow-learnings.d.ts.map +1 -0
  446. package/dist/skills/follow-learnings.js +278 -0
  447. package/dist/skills/follow-learnings.js.map +1 -0
  448. package/dist/skills/forget-notice.d.ts +44 -0
  449. package/dist/skills/forget-notice.d.ts.map +1 -0
  450. package/dist/skills/forget-notice.js +131 -0
  451. package/dist/skills/forget-notice.js.map +1 -0
  452. package/dist/skills/front-version.d.ts +12 -0
  453. package/dist/skills/front-version.d.ts.map +1 -0
  454. package/dist/skills/front-version.js +87 -0
  455. package/dist/skills/front-version.js.map +1 -0
  456. package/dist/skills/host-facts.d.ts +51 -0
  457. package/dist/skills/host-facts.d.ts.map +1 -0
  458. package/dist/skills/host-facts.js +134 -0
  459. package/dist/skills/host-facts.js.map +1 -0
  460. package/dist/skills/learning-steps.d.ts +43 -0
  461. package/dist/skills/learning-steps.d.ts.map +1 -0
  462. package/dist/skills/learning-steps.js +294 -0
  463. package/dist/skills/learning-steps.js.map +1 -0
  464. package/dist/skills/ledger.d.ts +43 -0
  465. package/dist/skills/ledger.d.ts.map +1 -0
  466. package/dist/skills/ledger.js +336 -0
  467. package/dist/skills/ledger.js.map +1 -0
  468. package/dist/skills/lesson-gate.d.ts +25 -0
  469. package/dist/skills/lesson-gate.d.ts.map +1 -0
  470. package/dist/skills/lesson-gate.js +316 -0
  471. package/dist/skills/lesson-gate.js.map +1 -0
  472. package/dist/skills/lessons.d.ts +60 -0
  473. package/dist/skills/lessons.d.ts.map +1 -0
  474. package/dist/skills/lessons.js +396 -0
  475. package/dist/skills/lessons.js.map +1 -0
  476. package/dist/skills/offer-answers.d.ts +20 -0
  477. package/dist/skills/offer-answers.d.ts.map +1 -0
  478. package/dist/skills/offer-answers.js +51 -0
  479. package/dist/skills/offer-answers.js.map +1 -0
  480. package/dist/skills/offer-drift.d.ts +35 -0
  481. package/dist/skills/offer-drift.d.ts.map +1 -0
  482. package/dist/skills/offer-drift.js +98 -0
  483. package/dist/skills/offer-drift.js.map +1 -0
  484. package/dist/skills/offer-lesson.d.ts +31 -0
  485. package/dist/skills/offer-lesson.d.ts.map +1 -0
  486. package/dist/skills/offer-lesson.js +123 -0
  487. package/dist/skills/offer-lesson.js.map +1 -0
  488. package/dist/skills/offer-lines.d.ts +40 -0
  489. package/dist/skills/offer-lines.d.ts.map +1 -0
  490. package/dist/skills/offer-lines.js +80 -0
  491. package/dist/skills/offer-lines.js.map +1 -0
  492. package/dist/skills/offer-moment.d.ts +102 -0
  493. package/dist/skills/offer-moment.d.ts.map +1 -0
  494. package/dist/skills/offer-moment.js +260 -0
  495. package/dist/skills/offer-moment.js.map +1 -0
  496. package/dist/skills/offer-repair.d.ts +36 -0
  497. package/dist/skills/offer-repair.d.ts.map +1 -0
  498. package/dist/skills/offer-repair.js +212 -0
  499. package/dist/skills/offer-repair.js.map +1 -0
  500. package/dist/skills/offer-retire.d.ts +53 -0
  501. package/dist/skills/offer-retire.d.ts.map +1 -0
  502. package/dist/skills/offer-retire.js +149 -0
  503. package/dist/skills/offer-retire.js.map +1 -0
  504. package/dist/skills/offer-update.d.ts +54 -0
  505. package/dist/skills/offer-update.d.ts.map +1 -0
  506. package/dist/skills/offer-update.js +115 -0
  507. package/dist/skills/offer-update.js.map +1 -0
  508. package/dist/skills/offer-words-1b.d.ts +32 -0
  509. package/dist/skills/offer-words-1b.d.ts.map +1 -0
  510. package/dist/skills/offer-words-1b.js +80 -0
  511. package/dist/skills/offer-words-1b.js.map +1 -0
  512. package/dist/skills/offer-words-2.d.ts +123 -0
  513. package/dist/skills/offer-words-2.d.ts.map +1 -0
  514. package/dist/skills/offer-words-2.js +194 -0
  515. package/dist/skills/offer-words-2.js.map +1 -0
  516. package/dist/skills/offer-words.d.ts +65 -0
  517. package/dist/skills/offer-words.d.ts.map +1 -0
  518. package/dist/skills/offer-words.js +205 -0
  519. package/dist/skills/offer-words.js.map +1 -0
  520. package/dist/skills/offers.d.ts +66 -0
  521. package/dist/skills/offers.d.ts.map +1 -0
  522. package/dist/skills/offers.js +284 -0
  523. package/dist/skills/offers.js.map +1 -0
  524. package/dist/skills/own-folder.d.ts +9 -0
  525. package/dist/skills/own-folder.d.ts.map +1 -0
  526. package/dist/skills/own-folder.js +36 -0
  527. package/dist/skills/own-folder.js.map +1 -0
  528. package/dist/skills/person.d.ts +34 -0
  529. package/dist/skills/person.d.ts.map +1 -0
  530. package/dist/skills/person.js +102 -0
  531. package/dist/skills/person.js.map +1 -0
  532. package/dist/skills/probe-clean.d.ts +9 -0
  533. package/dist/skills/probe-clean.d.ts.map +1 -0
  534. package/dist/skills/probe-clean.js +48 -0
  535. package/dist/skills/probe-clean.js.map +1 -0
  536. package/dist/skills/probes.d.ts +26 -0
  537. package/dist/skills/probes.d.ts.map +1 -0
  538. package/dist/skills/probes.js +225 -0
  539. package/dist/skills/probes.js.map +1 -0
  540. package/dist/skills/recall.d.ts +119 -0
  541. package/dist/skills/recall.d.ts.map +1 -0
  542. package/dist/skills/recall.js +370 -0
  543. package/dist/skills/recall.js.map +1 -0
  544. package/dist/skills/receipts.d.ts +44 -0
  545. package/dist/skills/receipts.d.ts.map +1 -0
  546. package/dist/skills/receipts.js +223 -0
  547. package/dist/skills/receipts.js.map +1 -0
  548. package/dist/skills/run-checkout.d.ts +4 -0
  549. package/dist/skills/run-checkout.d.ts.map +1 -0
  550. package/dist/skills/run-checkout.js +37 -0
  551. package/dist/skills/run-checkout.js.map +1 -0
  552. package/dist/skills/runblock.d.ts +47 -0
  553. package/dist/skills/runblock.d.ts.map +1 -0
  554. package/dist/skills/runblock.js +227 -0
  555. package/dist/skills/runblock.js.map +1 -0
  556. package/dist/skills/save-gate.d.ts +64 -0
  557. package/dist/skills/save-gate.d.ts.map +1 -0
  558. package/dist/skills/save-gate.js +354 -0
  559. package/dist/skills/save-gate.js.map +1 -0
  560. package/dist/skills/save-parse.d.ts +42 -0
  561. package/dist/skills/save-parse.d.ts.map +1 -0
  562. package/dist/skills/save-parse.js +172 -0
  563. package/dist/skills/save-parse.js.map +1 -0
  564. package/dist/skills/save-store.d.ts +12 -0
  565. package/dist/skills/save-store.d.ts.map +1 -0
  566. package/dist/skills/save-store.js +22 -0
  567. package/dist/skills/save-store.js.map +1 -0
  568. package/dist/skills/save-verify.d.ts +59 -0
  569. package/dist/skills/save-verify.d.ts.map +1 -0
  570. package/dist/skills/save-verify.js +390 -0
  571. package/dist/skills/save-verify.js.map +1 -0
  572. package/dist/skills/settings.d.ts +48 -0
  573. package/dist/skills/settings.d.ts.map +1 -0
  574. package/dist/skills/settings.js +155 -0
  575. package/dist/skills/settings.js.map +1 -0
  576. package/dist/skills/skill-diff.d.ts +40 -0
  577. package/dist/skills/skill-diff.d.ts.map +1 -0
  578. package/dist/skills/skill-diff.js +191 -0
  579. package/dist/skills/skill-diff.js.map +1 -0
  580. package/dist/skills/skill-file.d.ts +24 -0
  581. package/dist/skills/skill-file.d.ts.map +1 -0
  582. package/dist/skills/skill-file.js +97 -0
  583. package/dist/skills/skill-file.js.map +1 -0
  584. package/dist/skills/skill-rebuild.d.ts +27 -0
  585. package/dist/skills/skill-rebuild.d.ts.map +1 -0
  586. package/dist/skills/skill-rebuild.js +85 -0
  587. package/dist/skills/skill-rebuild.js.map +1 -0
  588. package/dist/skills/skillmd.d.ts +17 -0
  589. package/dist/skills/skillmd.d.ts.map +1 -0
  590. package/dist/skills/skillmd.js +219 -0
  591. package/dist/skills/skillmd.js.map +1 -0
  592. package/dist/skills/skills.d.ts +28 -8
  593. package/dist/skills/skills.d.ts.map +1 -1
  594. package/dist/skills/skills.js +106 -53
  595. package/dist/skills/skills.js.map +1 -1
  596. package/dist/skills/staleness.d.ts +27 -0
  597. package/dist/skills/staleness.d.ts.map +1 -0
  598. package/dist/skills/staleness.js +178 -0
  599. package/dist/skills/staleness.js.map +1 -0
  600. package/dist/skills/state.d.ts +36 -0
  601. package/dist/skills/state.d.ts.map +1 -0
  602. package/dist/skills/state.js +259 -0
  603. package/dist/skills/state.js.map +1 -0
  604. package/dist/skills/store-fs.d.ts +77 -0
  605. package/dist/skills/store-fs.d.ts.map +1 -0
  606. package/dist/skills/store-fs.js +194 -0
  607. package/dist/skills/store-fs.js.map +1 -0
  608. package/dist/skills/store-held.d.ts +44 -0
  609. package/dist/skills/store-held.d.ts.map +1 -0
  610. package/dist/skills/store-held.js +147 -0
  611. package/dist/skills/store-held.js.map +1 -0
  612. package/dist/skills/store.d.ts +73 -0
  613. package/dist/skills/store.d.ts.map +1 -0
  614. package/dist/skills/store.js +279 -0
  615. package/dist/skills/store.js.map +1 -0
  616. package/dist/skills/types.d.ts +8 -0
  617. package/dist/skills/types.d.ts.map +1 -1
  618. package/dist/skills/update-gate.d.ts +36 -0
  619. package/dist/skills/update-gate.d.ts.map +1 -0
  620. package/dist/skills/update-gate.js +357 -0
  621. package/dist/skills/update-gate.js.map +1 -0
  622. package/dist/skills/update-plan.d.ts +40 -0
  623. package/dist/skills/update-plan.d.ts.map +1 -0
  624. package/dist/skills/update-plan.js +196 -0
  625. package/dist/skills/update-plan.js.map +1 -0
  626. package/dist/skills/validate.d.ts +11 -0
  627. package/dist/skills/validate.d.ts.map +1 -0
  628. package/dist/skills/validate.js +112 -0
  629. package/dist/skills/validate.js.map +1 -0
  630. package/dist/skills/value-line.d.ts +94 -0
  631. package/dist/skills/value-line.d.ts.map +1 -0
  632. package/dist/skills/value-line.js +355 -0
  633. package/dist/skills/value-line.js.map +1 -0
  634. package/dist/skills/versions-fs.d.ts +45 -0
  635. package/dist/skills/versions-fs.d.ts.map +1 -0
  636. package/dist/skills/versions-fs.js +234 -0
  637. package/dist/skills/versions-fs.js.map +1 -0
  638. package/dist/skills/versions.d.ts +56 -0
  639. package/dist/skills/versions.d.ts.map +1 -0
  640. package/dist/skills/versions.js +351 -0
  641. package/dist/skills/versions.js.map +1 -0
  642. package/dist/telemetry/contract.d.ts +15 -1
  643. package/dist/telemetry/contract.d.ts.map +1 -1
  644. package/dist/telemetry/contract.js +80 -11
  645. package/dist/telemetry/contract.js.map +1 -1
  646. package/dist/telemetry/gate.js +4 -4
  647. package/dist/telemetry/gate.js.map +1 -1
  648. package/dist/telemetry/index.d.ts +1 -1
  649. package/dist/telemetry/index.d.ts.map +1 -1
  650. package/dist/telemetry/index.js +1 -1
  651. package/dist/telemetry/index.js.map +1 -1
  652. package/dist/telemetry/notice.js +5 -5
  653. package/dist/telemetry/notice.js.map +1 -1
  654. package/dist/telemetry/queue.d.ts.map +1 -1
  655. package/dist/telemetry/queue.js +7 -7
  656. package/dist/telemetry/queue.js.map +1 -1
  657. package/dist/telemetry/track.d.ts.map +1 -1
  658. package/dist/telemetry/track.js +4 -3
  659. package/dist/telemetry/track.js.map +1 -1
  660. package/dist/trace/checks.d.ts +63 -0
  661. package/dist/trace/checks.d.ts.map +1 -0
  662. package/dist/trace/checks.js +495 -0
  663. package/dist/trace/checks.js.map +1 -0
  664. package/dist/trace/claude.d.ts +124 -0
  665. package/dist/trace/claude.d.ts.map +1 -0
  666. package/dist/trace/claude.js +303 -0
  667. package/dist/trace/claude.js.map +1 -0
  668. package/dist/trace/codex-exec.d.ts +22 -0
  669. package/dist/trace/codex-exec.d.ts.map +1 -0
  670. package/dist/trace/codex-exec.js +129 -0
  671. package/dist/trace/codex-exec.js.map +1 -0
  672. package/dist/trace/codex-meta.d.ts +23 -0
  673. package/dist/trace/codex-meta.d.ts.map +1 -0
  674. package/dist/trace/codex-meta.js +112 -0
  675. package/dist/trace/codex-meta.js.map +1 -0
  676. package/dist/trace/codex.d.ts +33 -0
  677. package/dist/trace/codex.d.ts.map +1 -0
  678. package/dist/trace/codex.js +485 -0
  679. package/dist/trace/codex.js.map +1 -0
  680. package/dist/trace/cursor.d.ts +16 -0
  681. package/dist/trace/cursor.d.ts.map +1 -0
  682. package/dist/trace/cursor.js +133 -0
  683. package/dist/trace/cursor.js.map +1 -0
  684. package/dist/trace/dialect.d.ts +43 -0
  685. package/dist/trace/dialect.d.ts.map +1 -0
  686. package/dist/trace/dialect.js +95 -0
  687. package/dist/trace/dialect.js.map +1 -0
  688. package/dist/trace/epochs.d.ts +10 -0
  689. package/dist/trace/epochs.d.ts.map +1 -0
  690. package/dist/trace/epochs.js +16 -0
  691. package/dist/trace/epochs.js.map +1 -0
  692. package/dist/trace/failure.d.ts +15 -0
  693. package/dist/trace/failure.d.ts.map +1 -0
  694. package/dist/trace/failure.js +69 -0
  695. package/dist/trace/failure.js.map +1 -0
  696. package/dist/trace/gate-file.d.ts +71 -0
  697. package/dist/trace/gate-file.d.ts.map +1 -0
  698. package/dist/trace/gate-file.js +329 -0
  699. package/dist/trace/gate-file.js.map +1 -0
  700. package/dist/trace/guard-fast.d.ts +22 -0
  701. package/dist/trace/guard-fast.d.ts.map +1 -0
  702. package/dist/trace/guard-fast.js +213 -0
  703. package/dist/trace/guard-fast.js.map +1 -0
  704. package/dist/trace/housekeeping.d.ts +5 -0
  705. package/dist/trace/housekeeping.d.ts.map +1 -0
  706. package/dist/trace/housekeeping.js +93 -0
  707. package/dist/trace/housekeeping.js.map +1 -0
  708. package/dist/trace/jobs.d.ts +52 -0
  709. package/dist/trace/jobs.d.ts.map +1 -0
  710. package/dist/trace/jobs.js +182 -0
  711. package/dist/trace/jobs.js.map +1 -0
  712. package/dist/trace/known-repo.d.ts +3 -0
  713. package/dist/trace/known-repo.d.ts.map +1 -0
  714. package/dist/trace/known-repo.js +55 -0
  715. package/dist/trace/known-repo.js.map +1 -0
  716. package/dist/trace/pool.d.ts +40 -0
  717. package/dist/trace/pool.d.ts.map +1 -0
  718. package/dist/trace/pool.js +154 -0
  719. package/dist/trace/pool.js.map +1 -0
  720. package/dist/trace/repairs.d.ts +27 -0
  721. package/dist/trace/repairs.d.ts.map +1 -0
  722. package/dist/trace/repairs.js +303 -0
  723. package/dist/trace/repairs.js.map +1 -0
  724. package/dist/trace/run-calls.d.ts +82 -0
  725. package/dist/trace/run-calls.d.ts.map +1 -0
  726. package/dist/trace/run-calls.js +331 -0
  727. package/dist/trace/run-calls.js.map +1 -0
  728. package/dist/trace/run-join.d.ts +20 -0
  729. package/dist/trace/run-join.d.ts.map +1 -0
  730. package/dist/trace/run-join.js +99 -0
  731. package/dist/trace/run-join.js.map +1 -0
  732. package/dist/trace/run-sheets.d.ts +26 -0
  733. package/dist/trace/run-sheets.d.ts.map +1 -0
  734. package/dist/trace/run-sheets.js +145 -0
  735. package/dist/trace/run-sheets.js.map +1 -0
  736. package/dist/trace/run-turns.d.ts +24 -0
  737. package/dist/trace/run-turns.d.ts.map +1 -0
  738. package/dist/trace/run-turns.js +67 -0
  739. package/dist/trace/run-turns.js.map +1 -0
  740. package/dist/trace/run-verdict.d.ts +24 -0
  741. package/dist/trace/run-verdict.d.ts.map +1 -0
  742. package/dist/trace/run-verdict.js +176 -0
  743. package/dist/trace/run-verdict.js.map +1 -0
  744. package/dist/trace/runs.d.ts +54 -0
  745. package/dist/trace/runs.d.ts.map +1 -0
  746. package/dist/trace/runs.js +501 -0
  747. package/dist/trace/runs.js.map +1 -0
  748. package/dist/trace/skill-session.d.ts +64 -0
  749. package/dist/trace/skill-session.d.ts.map +1 -0
  750. package/dist/trace/skill-session.js +232 -0
  751. package/dist/trace/skill-session.js.map +1 -0
  752. package/dist/trace/tamper.d.ts +12 -0
  753. package/dist/trace/tamper.d.ts.map +1 -0
  754. package/dist/trace/tamper.js +405 -0
  755. package/dist/trace/tamper.js.map +1 -0
  756. package/dist/trace/typed.d.ts +30 -0
  757. package/dist/trace/typed.d.ts.map +1 -0
  758. package/dist/trace/typed.js +132 -0
  759. package/dist/trace/typed.js.map +1 -0
  760. package/dist/trace/wall-state.d.ts +86 -0
  761. package/dist/trace/wall-state.d.ts.map +1 -0
  762. package/dist/trace/wall-state.js +279 -0
  763. package/dist/trace/wall-state.js.map +1 -0
  764. package/dist/trace/walls.d.ts +40 -0
  765. package/dist/trace/walls.d.ts.map +1 -0
  766. package/dist/trace/walls.js +318 -0
  767. package/dist/trace/walls.js.map +1 -0
  768. package/dist/upkeep-run.d.ts.map +1 -1
  769. package/dist/upkeep-run.js +2 -23
  770. package/dist/upkeep-run.js.map +1 -1
  771. package/dist/upkeep.d.ts.map +1 -1
  772. package/dist/upkeep.js +2 -3
  773. package/dist/upkeep.js.map +1 -1
  774. package/dist/util/private-fs.d.ts +6 -0
  775. package/dist/util/private-fs.d.ts.map +1 -1
  776. package/dist/util/private-fs.js +9 -1
  777. package/dist/util/private-fs.js.map +1 -1
  778. package/dist/util/state.d.ts.map +1 -1
  779. package/dist/util/state.js +17 -3
  780. package/dist/util/state.js.map +1 -1
  781. package/dist/vendor/tree-sitter-swift/LICENSE +21 -0
  782. package/dist/vendor/tree-sitter-swift/node-types.json +30694 -0
  783. package/dist/vendor/tree-sitter-swift/prebuilds/darwin-arm64/tree-sitter-swift.node +0 -0
  784. package/dist/vendor/tree-sitter-swift/prebuilds/darwin-x64/tree-sitter-swift.node +0 -0
  785. package/dist/vendor/tree-sitter-swift/prebuilds/linux-arm64/tree-sitter-swift.node +0 -0
  786. package/dist/vendor/tree-sitter-swift/prebuilds/linux-x64/tree-sitter-swift.node +0 -0
  787. package/dist/vendor/tree-sitter-swift/prebuilds/win32-arm64/tree-sitter-swift.node +0 -0
  788. package/dist/vendor/tree-sitter-swift/prebuilds/win32-x64/tree-sitter-swift.node +0 -0
  789. package/package.json +6 -3
  790. package/scripts/stamp-telemetry-key.mjs +8 -5
  791. package/scripts/vendor-grammars.mjs +35 -0
@@ -2,31 +2,42 @@
2
2
  * Keeps the workspace's copy of a repo's learnings, and its record of how
3
3
  * they're used, in step with this machine.
4
4
  *
5
- * What goes up for a repo in the workspace is every learning in its `.trail/`:
6
- * the working tree's and the ones on teammates' pushed branches, after a
7
- * `git fetch`. Not just this person's, so one person is enough to cover a
8
- * repo. Personal learnings in ~/.trail never go up.
5
+ * What goes up for a repo in the workspace is every learning in its `.trail/`
6
+ * that git has: the ones committed at HEAD and the ones on teammates' pushed
7
+ * branches, after a `git fetch`. Never an uncommitted edit or an untracked
8
+ * file. Not just this person's, so one person is enough to cover a repo.
9
+ * Personal learnings in ~/.trail never go up.
9
10
  *
10
11
  * Uses (a search that showed learnings, a check against them) are appended to
11
12
  * `~/.trail/outbox.jsonl` as they happen, and sent with the next sync. That's
12
- * how a teammate's reuse of a learning shows on the team page. Only while the
13
- * machine is connected, and only for a repo in the workspace: a search holds
14
- * what someone typed and the files they're in, and nobody agreed to send that
15
- * for a repo they didn't pick, or from before they signed in.
13
+ * how a teammate's reuse of a learning shows on the team page. A use is counts
14
+ * and the names of the learnings shown: never what someone typed, never the
15
+ * files they're in. And only names the workspace already has: a learning's
16
+ * file name carries the first words of its title, so a personal learning, or
17
+ * one git doesn't have yet, is left out. Only while the machine is connected,
18
+ * and only for a repo in the workspace: nobody agreed to send even that for a
19
+ * repo they didn't pick, or from before they signed in.
16
20
  *
17
21
  * Commands never wait on any of this: when something changed, a detached
18
22
  * `_trail-sync` child does it.
19
23
  */
20
24
  import { createHash } from "node:crypto";
21
- import { spawn } from "node:child_process";
22
- import { appendFileSync, existsSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
23
- import { basename, join } from "node:path";
25
+ import { execFileSync, spawn } from "node:child_process";
26
+ import { existsSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync } from "node:fs";
27
+ import { basename, join, relative, sep } from "node:path";
28
+ import { isUtf8 } from "node:buffer";
24
29
  import { graftCliPath } from "../claude/paths.js";
25
30
  import { readBranchCache, refreshBranches } from "../notes/branches.js";
26
31
  import { knownRepos, repoName, repoPlace, trailHome } from "../notes/home.js";
27
32
  import { LEARNINGS_DIR, learningFiles, readLearningFile } from "../notes/repo-trail.js";
28
33
  import { parseNote } from "../notes/notes.js";
34
+ import { liveLearnings } from "../notes/learning-fields.js";
35
+ import { secretReason } from "../notes/secrets.js";
36
+ import { OWN_SKILLS, skillDirs } from "../skills/skills.js";
37
+ import { matter } from "../util/front-matter.js";
38
+ import { appendPrivateFile, ensurePrivateDir, PRIVATE_FILE_MODE, writePrivateFile } from "../util/private-file.js";
29
39
  import { cacheDir, readJson, writeJsonAtomic } from "../util/state.js";
40
+ import { headLearnings } from "./head-learnings.js";
30
41
  import { isError, postActivity, readCloud, uploadLearnings } from "./workspace.js";
31
42
  /** A failed sync is retried after this long, not on every command. */
32
43
  const RETRY_MS = 5 * 60 * 1000;
@@ -36,59 +47,293 @@ const OUTBOX_KEEP_LINES = 2000;
36
47
  function sha256(text) {
37
48
  return createHash("sha256").update(text).digest("hex");
38
49
  }
50
+ /** A skill folder name safe to send as a path part. */
51
+ const SKILL_FOLDER = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
52
+ function inside(root, p) {
53
+ return p === root || p.startsWith(root + sep);
54
+ }
55
+ /** A SKILL.md bigger than this is not a skill, and is never sent. */
56
+ const SKILL_MAX_BYTES = 256 * 1024;
57
+ /** What Trail keeps of a skill's folder besides SKILL.md (the server's limits too). */
58
+ const SKILL_FILES_MAX = 100;
59
+ const SKILL_FILE_MAX_BYTES = 1024 * 1024;
60
+ const SKILL_FOLDER_MAX_BYTES = 4 * 1024 * 1024;
61
+ /** Never part of a skill, though a folder may hold them. */
62
+ const NOT_SKILL_FILE = /(^|\/)(node_modules|__pycache__|\.git|\.venv)(\/|$)|(^|\/)\.DS_Store$|\.(map|pyc)$/;
39
63
  /**
40
- * Every learning in the repo's `.trail/`, as the workspace takes them: the
41
- * working tree's, then the ones only a pushed branch has (from the branch
42
- * cache). Paths are repo-relative; nothing about this machine is sent.
64
+ * The files git tracks in a skill's folder besides SKILL.md, as they go up:
65
+ * text as is, anything else as base64, the executable bit kept. A file that
66
+ * resolves outside the checkout, or is too big, stays behind; so do the
67
+ * ones past the folder's limits, smallest paths first.
43
68
  */
44
- export function learningsPayload(checkout) {
69
+ function skillFolderFiles(root, folder) {
70
+ let rel;
71
+ try {
72
+ rel = relative(root, realpathSync(folder)).split(sep).join("/");
73
+ }
74
+ catch {
75
+ return [];
76
+ }
45
77
  const out = [];
78
+ let total = 0;
79
+ for (const path of [...trackedAmong(root, [rel + "/"])].sort()) {
80
+ const inner = path.slice(rel.length + 1);
81
+ if (!inner || inner === "SKILL.md" || NOT_SKILL_FILE.test(inner) || out.length === SKILL_FILES_MAX)
82
+ continue;
83
+ try {
84
+ const file = join(root, ...path.split("/"));
85
+ const st = statSync(file);
86
+ if (!st.isFile() || st.size > SKILL_FILE_MAX_BYTES || total + st.size > SKILL_FOLDER_MAX_BYTES)
87
+ continue;
88
+ if (!inside(root, realpathSync(file)))
89
+ continue;
90
+ const bytes = readFileSync(file);
91
+ const text = !bytes.includes(0) && isUtf8(bytes);
92
+ total += bytes.length;
93
+ out.push({
94
+ path: inner,
95
+ content: text ? bytes.toString("utf8") : bytes.toString("base64"),
96
+ ...(text ? {} : { encoding: "base64" }),
97
+ ...(st.mode & 0o111 ? { executable: true } : {}),
98
+ });
99
+ }
100
+ catch {
101
+ /* gone, or can't be read */
102
+ }
103
+ }
104
+ return out;
105
+ }
106
+ /** `cut-a-release` → `Cut a release`: a skill's folder name as a title. */
107
+ function skillTitle(name) {
108
+ const words = name.replace(/[-_]+/g, " ").trim();
109
+ return words ? words[0].toUpperCase() + words.slice(1) : name;
110
+ }
111
+ /**
112
+ * Which of `paths` (relative to `root`) git tracks, by what `git ls-files` says,
113
+ * so a gitignored or untracked file is not among them. Empty when `root` isn't a
114
+ * git checkout or git fails: no skill goes up then.
115
+ */
116
+ function trackedAmong(root, paths) {
117
+ if (paths.length === 0)
118
+ return new Set();
119
+ try {
120
+ const out = execFileSync("git", ["--literal-pathspecs", "ls-files", "-z", "--", ...paths], {
121
+ cwd: root,
122
+ encoding: "utf8",
123
+ stdio: ["ignore", "pipe", "ignore"],
124
+ timeout: 15_000,
125
+ maxBuffer: 16 * 1024 * 1024,
126
+ });
127
+ return new Set(out.split("\0").filter(Boolean));
128
+ }
129
+ catch {
130
+ return new Set();
131
+ }
132
+ }
133
+ /**
134
+ * The repo's skills, as the workspace takes them: each `.claude/skills/<name>/SKILL.md`
135
+ * in the working tree that git tracks (an untracked or gitignored one isn't the
136
+ * team's, so it stays here; no git, no skills). Same item as the one init sends
137
+ * for a skill it just found (the server keeps one per repo and path): the body is
138
+ * SKILL.md without its frontmatter, the hash is of the whole file. A symlinked
139
+ * skill is read only when it resolves inside the checkout, and goes up when the
140
+ * file it resolves to is tracked. A SKILL.md that can't be read or parsed, or is
141
+ * over 256 KB, is skipped. Skills kept in ~/.trail are never sent.
142
+ */
143
+ function skillItems(checkout) {
144
+ const dir = skillDirs(checkout).repo;
145
+ let root;
146
+ let names;
147
+ try {
148
+ root = realpathSync(join(dir, "..", ".."));
149
+ names = readdirSync(dir).sort();
150
+ }
151
+ catch {
152
+ return [];
153
+ }
154
+ // Each candidate that passes the cheap checks, with where its file really is.
155
+ const candidates = [];
156
+ for (const name of names) {
157
+ if (!SKILL_FOLDER.test(name) || OWN_SKILLS.has(name))
158
+ continue;
159
+ try {
160
+ const file = join(dir, name, "SKILL.md");
161
+ if (!statSync(join(dir, name)).isDirectory())
162
+ continue;
163
+ const st = statSync(file);
164
+ if (!st.isFile() || st.size > SKILL_MAX_BYTES)
165
+ continue;
166
+ const real = realpathSync(file);
167
+ if (!inside(root, realpathSync(join(dir, name))) || !inside(root, real))
168
+ continue;
169
+ candidates.push({ name, file, real: relative(root, real).split(sep).join("/") });
170
+ }
171
+ catch {
172
+ /* no SKILL.md here */
173
+ }
174
+ }
175
+ const tracked = trackedAmong(root, [...new Set(candidates.map((c) => c.real))]);
176
+ const out = [];
177
+ for (const { name, file, real } of candidates) {
178
+ if (!tracked.has(real))
179
+ continue;
180
+ try {
181
+ const text = readFileSync(file, "utf8");
182
+ const parsed = matter(text);
183
+ const d = parsed.data;
184
+ const description = typeof d.description === "string" ? d.description.trim().replace(/\s+/g, " ") : "";
185
+ const files = skillFolderFiles(root, join(dir, name));
186
+ out.push({
187
+ path: `.claude/skills/${name}/SKILL.md`,
188
+ kind: "skill",
189
+ ref: "",
190
+ title: skillTitle(typeof d.name === "string" && d.name.trim() ? d.name.trim() : name),
191
+ why: description,
192
+ author: typeof d.author === "string" ? d.author.trim() : "",
193
+ date: "",
194
+ touches: [],
195
+ body: parsed.content.trim(),
196
+ // The whole folder: any file changing sends the skill again.
197
+ hash: sha256(JSON.stringify(["folder", text, files.map((f) => [f.path, sha256(f.content), !!f.executable])])),
198
+ files,
199
+ });
200
+ }
201
+ catch {
202
+ /* a SKILL.md that can't be read or parsed */
203
+ }
204
+ }
205
+ return out;
206
+ }
207
+ /**
208
+ * Every live learning in the repo's `.trail/`, as the workspace takes them:
209
+ * the ones committed at HEAD, then the ones only a pushed branch has (from
210
+ * the branch cache), then the repo's skills that git tracks (`skillItems`).
211
+ * Never the working tree's learnings: an uncommitted or untracked learning
212
+ * stays on this machine. Paths are repo-relative; nothing about this
213
+ * machine is sent. Null when git couldn't read HEAD: then nothing may go up,
214
+ * since a set without HEAD's learnings would tell the workspace they're gone.
215
+ *
216
+ * Live means what recall reads (`liveLearnings`): a retired, held or replaced
217
+ * learning stays behind, so the team page never shows one nobody is meant to
218
+ * follow. A learning on an unmerged branch replaces nothing yet.
219
+ *
220
+ * A learning or skill whose text holds a secret (`secretReason`) stays behind, every
221
+ * copy of it: what a sync sent is what vouches for a learning's name in a
222
+ * use, so its name never goes up either. It is listed in `leftOut`, so
223
+ * `trail workspace` can say so instead of it going missing in silence.
224
+ *
225
+ * `tree`: the working tree's learnings in place of HEAD's, for `init --pair`'s
226
+ * review page only, which shows the person what init just wrote before any of
227
+ * it is committed. A sync never reads them.
228
+ */
229
+ export function workspaceLearnings(checkout, opts = {}) {
230
+ const head = opts.tree ? treeLearnings(checkout) : headLearnings(checkout);
231
+ if (!head)
232
+ return null;
233
+ const read = [];
234
+ const leftOut = [];
46
235
  const seen = new Set();
47
236
  const add = (name, text, ref) => {
48
237
  if (seen.has(name))
49
238
  return;
239
+ const why = secretReason(text);
240
+ if (why) {
241
+ seen.add(name);
242
+ leftOut.push({ name, ref, why });
243
+ return;
244
+ }
50
245
  const n = parseNote(text, name);
51
246
  if (!n)
52
247
  return;
53
248
  seen.add(name);
54
- out.push({
55
- path: `${LEARNINGS_DIR}/${name}`,
56
- ref,
57
- branch: n.branch,
58
- title: n.title,
59
- author: n.author,
60
- date: n.date,
61
- minutes: n.cost?.minutes,
62
- tokens: n.cost?.tokens,
63
- touches: n.touches,
64
- body: n.body,
65
- hash: sha256(text),
66
- });
249
+ read.push({ ...n, scope: "repo", ...(ref ? { ref } : {}), text });
67
250
  };
251
+ for (const l of head)
252
+ add(l.name, l.text, "");
253
+ for (const b of readBranchCache(checkout)?.branches ?? [])
254
+ for (const l of b.learnings)
255
+ add(l.name, l.text, b.ref);
256
+ const learnings = liveLearnings(read).map((n) => ({
257
+ path: `${LEARNINGS_DIR}/${n.path}`,
258
+ ref: n.ref ?? "",
259
+ branch: n.branch,
260
+ title: n.title,
261
+ author: n.author,
262
+ date: n.date,
263
+ minutes: n.cost?.minutes,
264
+ tokens: n.cost?.tokens,
265
+ touches: n.touches,
266
+ body: n.body,
267
+ hash: sha256(n.text),
268
+ }));
269
+ // A skill whose text holds a secret stays behind too, named like a learning is.
270
+ for (const skill of skillItems(checkout)) {
271
+ const texts = (skill.files ?? []).filter((f) => !f.encoding).map((f) => f.content);
272
+ const why = secretReason([skill.title, skill.why ?? "", skill.body, ...texts].join("\n"));
273
+ if (why)
274
+ leftOut.push({ name: skill.path, ref: "", why });
275
+ else
276
+ learnings.push(skill);
277
+ }
278
+ return { learnings, leftOut };
279
+ }
280
+ /** The working tree's `.trail/learnings/` files, as `headLearnings` gives HEAD's. */
281
+ function treeLearnings(checkout) {
282
+ const out = [];
68
283
  for (const name of learningFiles(checkout)) {
69
284
  const text = readLearningFile(checkout, name);
70
285
  if (text)
71
- add(name, text, "");
286
+ out.push({ name, text });
72
287
  }
73
- for (const b of readBranchCache(checkout)?.branches ?? [])
74
- for (const l of b.learnings)
75
- add(l.name, l.text, b.ref);
76
288
  return out;
77
289
  }
290
+ /**
291
+ * What `init --pair` hands its review page: the working tree's live learnings
292
+ * (what the person's agents start from, the ones init just wrote among them),
293
+ * teammates' pushed ones and the repo's tracked skills, secrets left out.
294
+ */
295
+ export function reviewPayload(checkout) {
296
+ return workspaceLearnings(checkout, { tree: true })?.learnings ?? [];
297
+ }
298
+ /** The learnings a sync uploads: `workspaceLearnings` without what it left out. Null when git couldn't read HEAD. */
299
+ export function learningsPayload(checkout) {
300
+ return workspaceLearnings(checkout)?.learnings ?? null;
301
+ }
78
302
  function fingerprint(learnings) {
79
303
  return sha256(JSON.stringify(learnings.map((l) => [l.path, l.hash]).sort()));
80
304
  }
305
+ /** The checkout's branch, or "" when git can't say (detached HEAD, no git). */
306
+ function currentBranch(checkout) {
307
+ try {
308
+ const b = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: checkout, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5000 }).trim();
309
+ return b === "HEAD" ? "" : b;
310
+ }
311
+ catch {
312
+ return "";
313
+ }
314
+ }
81
315
  function statePath(repo) {
82
316
  return join(cacheDir(repo), "trail-workspace-sync.json");
83
317
  }
84
- /** Whether the repo at `checkout` is one of the workspace's. */
318
+ /**
319
+ * Whether the repo at `checkout` is one of the workspace's, so its learnings
320
+ * and uses go up. Never on a machine connected by `trail init --connect`: that
321
+ * one sends a repo summary only (titles and counts, no learning bodies, no
322
+ * searches), whatever repos the workspace lists.
323
+ */
85
324
  export function inWorkspace(checkout, cloud = readCloud()) {
86
- return !!cloud && cloud.repos.includes(repoPlace(checkout).key);
325
+ return !!cloud && cloud.via !== "connect" && cloud.repos.includes(repoPlace(checkout).key);
87
326
  }
88
327
  /**
89
328
  * Upload the repo's learnings now, after fetching teammates' branches. Only for
90
329
  * a repo in the workspace. Remembers what was sent so an unchanged repo isn't
91
- * sent again.
330
+ * sent again. When git can't read HEAD nothing is sent and the sync counts as
331
+ * failed, so it's tried again later.
332
+ *
333
+ * A learning or skill HEAD had at the last sync on this same branch, and no
334
+ * longer has (deleted, or now left out for a secret), is sent as removed, so
335
+ * it comes down from the web. On another branch a missing file says nothing,
336
+ * so switching branches never takes anything down.
92
337
  */
93
338
  export async function syncRepo(checkout, cloud, opts = {}) {
94
339
  const place = repoPlace(checkout);
@@ -96,15 +341,78 @@ export async function syncRepo(checkout, cloud, opts = {}) {
96
341
  return { error: "this repo isn't in your workspace" };
97
342
  if (opts.fetch !== false)
98
343
  refreshBranches(place.checkout);
99
- const learnings = learningsPayload(place.checkout);
100
- const res = await uploadLearnings(cloud, { repo: { key: place.key, name: repoName(place.key, place.checkout) }, learnings }, opts.fetchImpl);
344
+ const found = workspaceLearnings(place.checkout);
345
+ const learnings = found?.learnings ?? null;
101
346
  const prior = readJson(statePath(place.checkout));
347
+ const branch = currentBranch(place.checkout);
348
+ const paths = (learnings ?? []).filter((l) => l.ref === "").map((l) => l.path);
349
+ const here = new Set(paths);
350
+ const removed = branch && prior?.branch === branch ? (prior.paths ?? []).filter((p) => !here.has(p)) : [];
351
+ const held = found?.leftOut.length ?? 0;
352
+ const res = learnings
353
+ ? await uploadLearnings(cloud, { repo: { key: place.key, name: repoName(place.key, place.checkout) }, learnings, ...(removed.length ? { removed } : {}), ...(held ? { held } : {}) }, opts.fetchImpl)
354
+ : { error: "git couldn't read this repo's learnings at HEAD" };
102
355
  if (isError(res))
103
356
  writeJsonAtomic(statePath(place.checkout), { ...(prior ?? { fingerprint: "", at: 0 }), failedAt: Date.now() }, true);
104
- else
105
- writeJsonAtomic(statePath(place.checkout), { fingerprint: fingerprint(learnings), at: Date.now(), learnings: res.learnings }, true);
357
+ else if (learnings) {
358
+ writeJsonAtomic(statePath(place.checkout), { fingerprint: fingerprint(learnings), at: Date.now(), learnings: res.learnings, paths, branch }, true);
359
+ rememberSent(place.key, learnings);
360
+ }
106
361
  return res;
107
362
  }
363
+ /** `.claude/skills/<name>/SKILL.md` → `<name>`; "" for any other path. */
364
+ export function repoSkillName(path) {
365
+ return /^\.claude\/skills\/([^/]+)\/SKILL\.md$/.exec(path)?.[1] ?? "";
366
+ }
367
+ function sentPath(env = process.env) {
368
+ return join(trailHome(env), "workspace-sent.json");
369
+ }
370
+ /**
371
+ * Note what the workspace now has of a repo, which is what a use may name.
372
+ * Two syncs of different repos at once can lose one's entry: its uses then
373
+ * name nothing until its next sync, an undercount, never a leak.
374
+ */
375
+ function rememberSent(key, learnings) {
376
+ try {
377
+ const all = readJson(sentPath()) ?? {};
378
+ const skills = learnings.filter((l) => l.kind === "skill").map((l) => repoSkillName(l.path)).filter(Boolean);
379
+ all[key] = { at: Date.now(), learnings: Object.fromEntries(learnings.filter((l) => l.kind !== "skill").map((l) => [learningName(l.path), l.author])), skills };
380
+ ensurePrivateDir(trailHome());
381
+ writeJsonAtomic(sentPath(), all, true, PRIVATE_FILE_MODE);
382
+ }
383
+ catch {
384
+ /* uses name nothing until the next sync */
385
+ }
386
+ }
387
+ /** What the workspace got of each repo from this machine. Empty when nothing was sent yet. */
388
+ function readSent() {
389
+ const out = { learnings: new Map(), skills: new Map() };
390
+ const all = readJson(sentPath());
391
+ if (typeof all !== "object" || all === null)
392
+ return out;
393
+ for (const [key, v] of Object.entries(all)) {
394
+ const ls = v?.learnings;
395
+ if (typeof ls === "object" && ls !== null)
396
+ out.learnings.set(key, new Map(Object.entries(ls).filter((e) => typeof e[1] === "string")));
397
+ out.skills.set(key, new Set(asStrings(v?.skills)));
398
+ }
399
+ return out;
400
+ }
401
+ /**
402
+ * The use as it may go up: only the learnings the workspace already got of
403
+ * its repo, each with the author that upload gave it. A personal learning,
404
+ * an uncommitted or untracked one, or one an older trail queued, is dropped
405
+ * from it, name and author both. A skill use goes only for a skill the
406
+ * workspace got of that repo; any other is dropped whole.
407
+ */
408
+ function onlySent(e, sent) {
409
+ if (e.kind === "skill")
410
+ return e.skill && sent.skills.get(e.repo)?.has(e.skill) ? e : null;
411
+ const has = sent.learnings.get(e.repo);
412
+ const names = [...new Set(e.shown_paths)].filter((n) => has?.has(n));
413
+ const authors = names.map((n) => has.get(n));
414
+ return { ...e, shown_paths: names, shown_authors: e.kind === "check" ? [...new Set(authors)] : authors };
415
+ }
108
416
  /** Sync every repo of the workspace that's on this machine. Returns what went up. */
109
417
  export async function syncWorkspaceRepos(cloud, fetchImpl) {
110
418
  let repos = 0;
@@ -131,20 +439,58 @@ function outboxPath(env = process.env) {
131
439
  function sendable(e, cloud) {
132
440
  return cloud.repos.includes(e.repo) && Date.parse(e.at) >= (cloud.linkedAt || 0);
133
441
  }
442
+ /** Whether uses of `repo` are recorded at all: connected by `pair`/login (not `connect`) and the repo is the workspace's. */
443
+ export function records(cloud, repo) {
444
+ return !!cloud && cloud.via !== "connect" && cloud.repos.includes(repo);
445
+ }
446
+ /** A skill's folder name, as Claude Code names skills. */
447
+ const SKILL_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
448
+ const asStrings = (x) => (Array.isArray(x) ? x.filter((s) => typeof s === "string") : []);
449
+ const asText = (x) => (typeof x === "string" ? x : "");
450
+ /**
451
+ * A use as it may be queued or sent, built field by field from what was
452
+ * handed in. Anything else is left behind: an older trail queued the query
453
+ * (for the prompt hook, the person's whole prompt) and the file paths, and
454
+ * neither may reach the outbox or Trail. Null when it isn't a use at all.
455
+ */
456
+ export function activityEvent(raw) {
457
+ if (typeof raw !== "object" || raw === null)
458
+ return null;
459
+ const r = raw;
460
+ if (r.kind !== "search" && r.kind !== "check" && r.kind !== "skill")
461
+ return null;
462
+ const skill = r.kind === "skill" ? asText(r.skill) : "";
463
+ if (r.kind === "skill" && !SKILL_NAME.test(skill))
464
+ return null;
465
+ const count = typeof r.file_count === "number" && Number.isFinite(r.file_count) ? Math.max(0, Math.floor(r.file_count)) : asStrings(r.files).length;
466
+ return {
467
+ repo: asText(r.repo),
468
+ kind: r.kind,
469
+ ...(skill ? { skill } : {}),
470
+ asker: asText(r.asker),
471
+ session: asText(r.session),
472
+ file_count: count,
473
+ shown_paths: asStrings(r.shown_paths),
474
+ shown_authors: asStrings(r.shown_authors),
475
+ ...(typeof r.saved_tokens === "number" && Number.isFinite(r.saved_tokens) && r.saved_tokens > 0 ? { saved_tokens: Math.floor(r.saved_tokens) } : {}),
476
+ at: asText(r.at),
477
+ };
478
+ }
134
479
  /**
135
480
  * Remember one use of the learnings, to send with the next sync: only while
136
481
  * this machine is connected, and only for a repo in the workspace. Not
137
482
  * connected, or another repo, and nothing is written. Never throws.
138
483
  */
139
- export function recordActivity(e, cloud = readCloud()) {
140
- if (!cloud || !cloud.repos.includes(e.repo))
484
+ export function recordActivity(use, cloud = readCloud()) {
485
+ const e = activityEvent(use);
486
+ if (!e || !records(cloud, e.repo))
141
487
  return;
142
488
  try {
143
489
  const p = outboxPath();
144
- appendFileSync(p, `${JSON.stringify(e)}\n`);
490
+ appendPrivateFile(p, `${JSON.stringify(e)}\n`);
145
491
  if (statSync(p).size > OUTBOX_MAX_BYTES) {
146
492
  const lines = readFileSync(p, "utf8").split("\n").filter(Boolean).slice(-OUTBOX_KEEP_LINES);
147
- writeFileSync(p, `${lines.join("\n")}\n`);
493
+ writePrivateFile(p, `${lines.join("\n")}\n`);
148
494
  }
149
495
  }
150
496
  catch {
@@ -152,12 +498,14 @@ export function recordActivity(e, cloud = readCloud()) {
152
498
  }
153
499
  }
154
500
  /**
155
- * Forget every use waiting to go. Run when a machine connects, so nothing
156
- * recorded before that is ever sent.
501
+ * Forget every use waiting to go, and what an earlier connection was sent.
502
+ * Run when a machine connects, so nothing recorded before that is ever sent,
503
+ * and only what this connection uploads vouches for a learning's name.
157
504
  */
158
505
  export function dropActivity(env = process.env) {
159
506
  try {
160
507
  rmSync(outboxPath(env), { force: true });
508
+ rmSync(sentPath(env), { force: true });
161
509
  }
162
510
  catch {
163
511
  /* sent-time filtering in flushActivity drops them anyway */
@@ -175,7 +523,9 @@ function outboxHasEvents() {
175
523
  * Send the outbox. It's moved aside first, so events recorded while the
176
524
  * request is out land in a fresh file; on failure they're put back. A use
177
525
  * from before this connection, or for a repo the workspace doesn't have
178
- * (left by an older trail), is dropped, never sent.
526
+ * (left by an older trail), is dropped, never sent; so is the name of any
527
+ * learning the workspace didn't get from this machine's last upload of that
528
+ * repo, put back or sent.
179
529
  */
180
530
  export async function flushActivity(cloud, fetchImpl) {
181
531
  const p = outboxPath();
@@ -188,14 +538,16 @@ export async function flushActivity(cloud, fetchImpl) {
188
538
  catch {
189
539
  return 0;
190
540
  }
541
+ const has = readSent();
191
542
  const events = [];
192
543
  for (const line of readFileSync(sending, "utf8").split("\n")) {
193
544
  try {
194
545
  if (!line.trim())
195
546
  continue;
196
- const e = JSON.parse(line);
197
- if (sendable(e, cloud))
198
- events.push(e);
547
+ const e = activityEvent(JSON.parse(line));
548
+ const out = e && sendable(e, cloud) ? onlySent(e, has) : null;
549
+ if (out)
550
+ events.push(out);
199
551
  }
200
552
  catch {
201
553
  /* a torn line */
@@ -207,7 +559,7 @@ export async function flushActivity(cloud, fetchImpl) {
207
559
  if (isError(res)) {
208
560
  // Put back what didn't go, ahead of anything recorded since.
209
561
  const rest = events.slice(i).map((e) => JSON.stringify(e)).join("\n");
210
- appendFileSync(p, `${rest}\n`);
562
+ appendPrivateFile(p, `${rest}\n`);
211
563
  break;
212
564
  }
213
565
  sent += Math.min(500, events.length - i);
@@ -219,12 +571,13 @@ export async function flushActivity(cloud, fetchImpl) {
219
571
  /* -------------------------------------------------------------------------- */
220
572
  /* in the background */
221
573
  /* -------------------------------------------------------------------------- */
222
- /** Whether the repo's learnings changed since they last went up, and a retry is due. */
574
+ /** Whether the repo's learnings changed since they last went up, and a retry is due. Never while git can't read HEAD. */
223
575
  export function syncIsDue(checkout, now = Date.now()) {
224
576
  const state = readJson(statePath(checkout));
225
577
  if (state?.failedAt && now - state.failedAt < RETRY_MS)
226
578
  return false;
227
- return state?.fingerprint !== fingerprint(learningsPayload(checkout));
579
+ const learnings = learningsPayload(checkout);
580
+ return !!learnings && state?.fingerprint !== fingerprint(learnings);
228
581
  }
229
582
  /**
230
583
  * Sync in a detached child when this machine is connected and either the
@@ -234,8 +587,11 @@ export function syncIsDue(checkout, now = Date.now()) {
234
587
  export function maybeSyncInBackground(dir, now = Date.now()) {
235
588
  try {
236
589
  const cloud = readCloud();
237
- if (!cloud)
590
+ if (!cloud) {
591
+ // Whatever an older trail queued here can never go: connecting drops it.
592
+ dropActivity();
238
593
  return false;
594
+ }
239
595
  const checkout = repoPlace(dir).checkout;
240
596
  const due = (inWorkspace(checkout, cloud) && syncIsDue(checkout, now)) || outboxHasEvents();
241
597
  if (!due)