@nanonets/graft 0.23.1 → 0.24.1

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 (650) hide show
  1. package/README.md +4 -4
  2. package/dist/ai/crux.d.ts +31 -3
  3. package/dist/ai/crux.d.ts.map +1 -1
  4. package/dist/ai/crux.js +88 -10
  5. package/dist/ai/crux.js.map +1 -1
  6. package/dist/ai/llm/openai.d.ts +101 -0
  7. package/dist/ai/llm/openai.d.ts.map +1 -1
  8. package/dist/ai/llm/openai.js +396 -6
  9. package/dist/ai/llm/openai.js.map +1 -1
  10. package/dist/ai/llm/recover-tool.d.ts +2 -0
  11. package/dist/ai/llm/recover-tool.d.ts.map +1 -1
  12. package/dist/ai/llm/recover-tool.js +1 -1
  13. package/dist/ai/llm/recover-tool.js.map +1 -1
  14. package/dist/ai/synthesize.d.ts.map +1 -1
  15. package/dist/ai/synthesize.js +100 -1
  16. package/dist/ai/synthesize.js.map +1 -1
  17. package/dist/ask/ask.d.ts +8 -0
  18. package/dist/ask/ask.d.ts.map +1 -1
  19. package/dist/ask/ask.js +33 -12
  20. package/dist/ask/ask.js.map +1 -1
  21. package/dist/claude/format.js +1 -1
  22. package/dist/claude/format.js.map +1 -1
  23. package/dist/claude/hook-io.d.ts +44 -0
  24. package/dist/claude/hook-io.d.ts.map +1 -0
  25. package/dist/claude/hook-io.js +144 -0
  26. package/dist/claude/hook-io.js.map +1 -0
  27. package/dist/claude/hooks.d.ts +0 -18
  28. package/dist/claude/hooks.d.ts.map +1 -1
  29. package/dist/claude/hooks.js +24 -77
  30. package/dist/claude/hooks.js.map +1 -1
  31. package/dist/claude/init.js +3 -3
  32. package/dist/claude/init.js.map +1 -1
  33. package/dist/claude/note-signals.d.ts +28 -0
  34. package/dist/claude/note-signals.d.ts.map +1 -0
  35. package/dist/claude/note-signals.js +325 -0
  36. package/dist/claude/note-signals.js.map +1 -0
  37. package/dist/claude/shim-template.d.ts.map +1 -1
  38. package/dist/claude/shim-template.js +10 -4
  39. package/dist/claude/shim-template.js.map +1 -1
  40. package/dist/claude/skill-template.d.ts.map +1 -1
  41. package/dist/claude/skill-template.js +49 -18
  42. package/dist/claude/skill-template.js.map +1 -1
  43. package/dist/claude/skills-hook-util.d.ts +25 -0
  44. package/dist/claude/skills-hook-util.d.ts.map +1 -0
  45. package/dist/claude/skills-hook-util.js +71 -0
  46. package/dist/claude/skills-hook-util.js.map +1 -0
  47. package/dist/claude/state.d.ts +4 -3
  48. package/dist/claude/state.d.ts.map +1 -1
  49. package/dist/claude/state.js.map +1 -1
  50. package/dist/claude/sync-run.js +1 -1
  51. package/dist/claude/sync-run.js.map +1 -1
  52. package/dist/cli-picker.d.ts +36 -5
  53. package/dist/cli-picker.d.ts.map +1 -1
  54. package/dist/cli-picker.js +60 -13
  55. package/dist/cli-picker.js.map +1 -1
  56. package/dist/cli.d.ts +2 -0
  57. package/dist/cli.d.ts.map +1 -1
  58. package/dist/cli.js +406 -64
  59. package/dist/cli.js.map +1 -1
  60. package/dist/cloud/lookup-activity.d.ts +16 -0
  61. package/dist/cloud/lookup-activity.d.ts.map +1 -0
  62. package/dist/cloud/lookup-activity.js +56 -0
  63. package/dist/cloud/lookup-activity.js.map +1 -0
  64. package/dist/cloud/nudge.d.ts.map +1 -1
  65. package/dist/cloud/nudge.js +3 -0
  66. package/dist/cloud/nudge.js.map +1 -1
  67. package/dist/cloud/onboarding.d.ts +237 -0
  68. package/dist/cloud/onboarding.d.ts.map +1 -0
  69. package/dist/cloud/onboarding.js +536 -0
  70. package/dist/cloud/onboarding.js.map +1 -0
  71. package/dist/cloud/pair.d.ts +76 -0
  72. package/dist/cloud/pair.d.ts.map +1 -0
  73. package/dist/cloud/pair.js +249 -0
  74. package/dist/cloud/pair.js.map +1 -0
  75. package/dist/cloud/workspace-sync.d.ts +10 -2
  76. package/dist/cloud/workspace-sync.d.ts.map +1 -1
  77. package/dist/cloud/workspace-sync.js +119 -7
  78. package/dist/cloud/workspace-sync.js.map +1 -1
  79. package/dist/cloud/workspace.d.ts +22 -1
  80. package/dist/cloud/workspace.d.ts.map +1 -1
  81. package/dist/cloud/workspace.js +16 -1
  82. package/dist/cloud/workspace.js.map +1 -1
  83. package/dist/context/batches.d.ts +29 -0
  84. package/dist/context/batches.d.ts.map +1 -0
  85. package/dist/context/batches.js +130 -0
  86. package/dist/context/batches.js.map +1 -0
  87. package/dist/context/build.d.ts +7 -2
  88. package/dist/context/build.d.ts.map +1 -1
  89. package/dist/context/build.js +84 -50
  90. package/dist/context/build.js.map +1 -1
  91. package/dist/context/node-file.d.ts.map +1 -1
  92. package/dist/context/node-file.js +41 -4
  93. package/dist/context/node-file.js.map +1 -1
  94. package/dist/engine.d.ts +2 -0
  95. package/dist/engine.d.ts.map +1 -1
  96. package/dist/engine.js +1 -0
  97. package/dist/engine.js.map +1 -1
  98. package/dist/graph/cards.d.ts.map +1 -1
  99. package/dist/graph/cards.js +9 -4
  100. package/dist/graph/cards.js.map +1 -1
  101. package/dist/graph/enrich.d.ts.map +1 -1
  102. package/dist/graph/enrich.js +8 -3
  103. package/dist/graph/enrich.js.map +1 -1
  104. package/dist/graph/generic.d.ts.map +1 -1
  105. package/dist/graph/generic.js +207 -6
  106. package/dist/graph/generic.js.map +1 -1
  107. package/dist/graph/lsp/client.d.ts +18 -0
  108. package/dist/graph/lsp/client.d.ts.map +1 -1
  109. package/dist/graph/lsp/client.js +40 -2
  110. package/dist/graph/lsp/client.js.map +1 -1
  111. package/dist/graph/lsp/enrich.d.ts.map +1 -1
  112. package/dist/graph/lsp/enrich.js +9 -1
  113. package/dist/graph/lsp/enrich.js.map +1 -1
  114. package/dist/graph/lsp/registry.d.ts +7 -0
  115. package/dist/graph/lsp/registry.d.ts.map +1 -1
  116. package/dist/graph/lsp/registry.js +27 -2
  117. package/dist/graph/lsp/registry.js.map +1 -1
  118. package/dist/graph/native.d.ts +2 -0
  119. package/dist/graph/native.d.ts.map +1 -1
  120. package/dist/graph/native.js +29 -4
  121. package/dist/graph/native.js.map +1 -1
  122. package/dist/graph/queries/gleam.scm +51 -0
  123. package/dist/graph/resolve.d.ts.map +1 -1
  124. package/dist/graph/resolve.js +65 -3
  125. package/dist/graph/resolve.js.map +1 -1
  126. package/dist/graph/workspace-cli.d.ts +2 -0
  127. package/dist/graph/workspace-cli.d.ts.map +1 -1
  128. package/dist/graph/workspace-cli.js +1 -1
  129. package/dist/graph/workspace-cli.js.map +1 -1
  130. package/dist/hosts/claude-global.d.ts +14 -0
  131. package/dist/hosts/claude-global.d.ts.map +1 -1
  132. package/dist/hosts/claude-global.js +38 -3
  133. package/dist/hosts/claude-global.js.map +1 -1
  134. package/dist/init/install-record.d.ts +21 -0
  135. package/dist/init/install-record.d.ts.map +1 -0
  136. package/dist/init/install-record.js +144 -0
  137. package/dist/init/install-record.js.map +1 -0
  138. package/dist/init/plain-file.d.ts +11 -0
  139. package/dist/init/plain-file.d.ts.map +1 -0
  140. package/dist/init/plain-file.js +80 -0
  141. package/dist/init/plain-file.js.map +1 -0
  142. package/dist/mcp/brains.d.ts +72 -0
  143. package/dist/mcp/brains.d.ts.map +1 -0
  144. package/dist/mcp/brains.js +220 -0
  145. package/dist/mcp/brains.js.map +1 -0
  146. package/dist/mcp/server.d.ts +3 -1
  147. package/dist/mcp/server.d.ts.map +1 -1
  148. package/dist/mcp/server.js +40 -5
  149. package/dist/mcp/server.js.map +1 -1
  150. package/dist/mcp/tools.d.ts.map +1 -1
  151. package/dist/mcp/tools.js +7 -2
  152. package/dist/mcp/tools.js.map +1 -1
  153. package/dist/notes/branches.d.ts +7 -1
  154. package/dist/notes/branches.d.ts.map +1 -1
  155. package/dist/notes/branches.js +31 -3
  156. package/dist/notes/branches.js.map +1 -1
  157. package/dist/notes/cold-start.d.ts +42 -3
  158. package/dist/notes/cold-start.d.ts.map +1 -1
  159. package/dist/notes/cold-start.js +144 -24
  160. package/dist/notes/cold-start.js.map +1 -1
  161. package/dist/notes/credit.d.ts +37 -0
  162. package/dist/notes/credit.d.ts.map +1 -0
  163. package/dist/notes/credit.js +78 -0
  164. package/dist/notes/credit.js.map +1 -0
  165. package/dist/notes/every-repo.d.ts +18 -0
  166. package/dist/notes/every-repo.d.ts.map +1 -0
  167. package/dist/notes/every-repo.js +86 -0
  168. package/dist/notes/every-repo.js.map +1 -0
  169. package/dist/notes/find-notes.d.ts +17 -0
  170. package/dist/notes/find-notes.d.ts.map +1 -0
  171. package/dist/notes/find-notes.js +48 -0
  172. package/dist/notes/find-notes.js.map +1 -0
  173. package/dist/notes/flatten.d.ts +31 -0
  174. package/dist/notes/flatten.d.ts.map +1 -0
  175. package/dist/notes/flatten.js +73 -0
  176. package/dist/notes/flatten.js.map +1 -0
  177. package/dist/notes/git-facts.d.ts +2 -0
  178. package/dist/notes/git-facts.d.ts.map +1 -1
  179. package/dist/notes/git-facts.js +6 -0
  180. package/dist/notes/git-facts.js.map +1 -1
  181. package/dist/notes/guardrails.d.ts +8 -0
  182. package/dist/notes/guardrails.d.ts.map +1 -0
  183. package/dist/notes/guardrails.js +38 -0
  184. package/dist/notes/guardrails.js.map +1 -0
  185. package/dist/notes/home.d.ts +24 -6
  186. package/dist/notes/home.d.ts.map +1 -1
  187. package/dist/notes/home.js +82 -15
  188. package/dist/notes/home.js.map +1 -1
  189. package/dist/notes/learning-fields.d.ts +102 -0
  190. package/dist/notes/learning-fields.d.ts.map +1 -0
  191. package/dist/notes/learning-fields.js +214 -0
  192. package/dist/notes/learning-fields.js.map +1 -0
  193. package/dist/notes/lesson-index.d.ts +13 -0
  194. package/dist/notes/lesson-index.d.ts.map +1 -0
  195. package/dist/notes/lesson-index.js +135 -0
  196. package/dist/notes/lesson-index.js.map +1 -0
  197. package/dist/notes/linked-learnings.d.ts +8 -0
  198. package/dist/notes/linked-learnings.d.ts.map +1 -0
  199. package/dist/notes/linked-learnings.js +113 -0
  200. package/dist/notes/linked-learnings.js.map +1 -0
  201. package/dist/notes/memory-notes.d.ts +71 -0
  202. package/dist/notes/memory-notes.d.ts.map +1 -0
  203. package/dist/notes/memory-notes.js +239 -0
  204. package/dist/notes/memory-notes.js.map +1 -0
  205. package/dist/notes/note-gate.d.ts +42 -0
  206. package/dist/notes/note-gate.d.ts.map +1 -0
  207. package/dist/notes/note-gate.js +181 -0
  208. package/dist/notes/note-gate.js.map +1 -0
  209. package/dist/notes/notes.d.ts +29 -85
  210. package/dist/notes/notes.d.ts.map +1 -1
  211. package/dist/notes/notes.js +69 -254
  212. package/dist/notes/notes.js.map +1 -1
  213. package/dist/notes/pr-reviews.d.ts +35 -7
  214. package/dist/notes/pr-reviews.d.ts.map +1 -1
  215. package/dist/notes/pr-reviews.js +156 -46
  216. package/dist/notes/pr-reviews.js.map +1 -1
  217. package/dist/notes/replace.d.ts +14 -0
  218. package/dist/notes/replace.d.ts.map +1 -0
  219. package/dist/notes/replace.js +38 -0
  220. package/dist/notes/replace.js.map +1 -0
  221. package/dist/notes/repo-notes-dir.d.ts +2 -0
  222. package/dist/notes/repo-notes-dir.d.ts.map +1 -0
  223. package/dist/notes/repo-notes-dir.js +16 -0
  224. package/dist/notes/repo-notes-dir.js.map +1 -0
  225. package/dist/notes/secrets.d.ts +28 -0
  226. package/dist/notes/secrets.d.ts.map +1 -0
  227. package/dist/notes/secrets.js +362 -0
  228. package/dist/notes/secrets.js.map +1 -0
  229. package/dist/notes/targets.d.ts +37 -0
  230. package/dist/notes/targets.d.ts.map +1 -0
  231. package/dist/notes/targets.js +108 -0
  232. package/dist/notes/targets.js.map +1 -0
  233. package/dist/notes/team-check.d.ts +22 -0
  234. package/dist/notes/team-check.d.ts.map +1 -0
  235. package/dist/notes/team-check.js +130 -0
  236. package/dist/notes/team-check.js.map +1 -0
  237. package/dist/notes/trail-home.d.ts +3 -0
  238. package/dist/notes/trail-home.d.ts.map +1 -0
  239. package/dist/notes/trail-home.js +12 -0
  240. package/dist/notes/trail-home.js.map +1 -0
  241. package/dist/skills/anchors-cli.d.ts +33 -0
  242. package/dist/skills/anchors-cli.d.ts.map +1 -0
  243. package/dist/skills/anchors-cli.js +274 -0
  244. package/dist/skills/anchors-cli.js.map +1 -0
  245. package/dist/skills/anchors-git.d.ts +29 -0
  246. package/dist/skills/anchors-git.d.ts.map +1 -0
  247. package/dist/skills/anchors-git.js +241 -0
  248. package/dist/skills/anchors-git.js.map +1 -0
  249. package/dist/skills/anchors-wide.d.ts +25 -0
  250. package/dist/skills/anchors-wide.d.ts.map +1 -0
  251. package/dist/skills/anchors-wide.js +481 -0
  252. package/dist/skills/anchors-wide.js.map +1 -0
  253. package/dist/skills/anchors.d.ts +32 -0
  254. package/dist/skills/anchors.d.ts.map +1 -0
  255. package/dist/skills/anchors.js +485 -0
  256. package/dist/skills/anchors.js.map +1 -0
  257. package/dist/skills/answers-prompt.d.ts +13 -0
  258. package/dist/skills/answers-prompt.d.ts.map +1 -0
  259. package/dist/skills/answers-prompt.js +83 -0
  260. package/dist/skills/answers-prompt.js.map +1 -0
  261. package/dist/skills/check-result.d.ts +26 -0
  262. package/dist/skills/check-result.d.ts.map +1 -0
  263. package/dist/skills/check-result.js +46 -0
  264. package/dist/skills/check-result.js.map +1 -0
  265. package/dist/skills/codex-sandbox.d.ts +6 -0
  266. package/dist/skills/codex-sandbox.d.ts.map +1 -0
  267. package/dist/skills/codex-sandbox.js +55 -0
  268. package/dist/skills/codex-sandbox.js.map +1 -0
  269. package/dist/skills/compile-split.d.ts +57 -0
  270. package/dist/skills/compile-split.d.ts.map +1 -0
  271. package/dist/skills/compile-split.js +532 -0
  272. package/dist/skills/compile-split.js.map +1 -0
  273. package/dist/skills/compile-version.d.ts +37 -0
  274. package/dist/skills/compile-version.d.ts.map +1 -0
  275. package/dist/skills/compile-version.js +56 -0
  276. package/dist/skills/compile-version.js.map +1 -0
  277. package/dist/skills/compile-waits.d.ts +12 -0
  278. package/dist/skills/compile-waits.d.ts.map +1 -0
  279. package/dist/skills/compile-waits.js +52 -0
  280. package/dist/skills/compile-waits.js.map +1 -0
  281. package/dist/skills/compile-words.d.ts +82 -0
  282. package/dist/skills/compile-words.d.ts.map +1 -0
  283. package/dist/skills/compile-words.js +385 -0
  284. package/dist/skills/compile-words.js.map +1 -0
  285. package/dist/skills/compile.d.ts +35 -0
  286. package/dist/skills/compile.d.ts.map +1 -0
  287. package/dist/skills/compile.js +488 -0
  288. package/dist/skills/compile.js.map +1 -0
  289. package/dist/skills/credit-facts.d.ts +8 -0
  290. package/dist/skills/credit-facts.d.ts.map +1 -0
  291. package/dist/skills/credit-facts.js +32 -0
  292. package/dist/skills/credit-facts.js.map +1 -0
  293. package/dist/skills/directive-words.d.ts +11 -0
  294. package/dist/skills/directive-words.d.ts.map +1 -0
  295. package/dist/skills/directive-words.js +33 -0
  296. package/dist/skills/directive-words.js.map +1 -0
  297. package/dist/skills/fidelity.d.ts +42 -0
  298. package/dist/skills/fidelity.d.ts.map +1 -0
  299. package/dist/skills/fidelity.js +138 -0
  300. package/dist/skills/fidelity.js.map +1 -0
  301. package/dist/skills/forget-notice.d.ts +44 -0
  302. package/dist/skills/forget-notice.d.ts.map +1 -0
  303. package/dist/skills/forget-notice.js +131 -0
  304. package/dist/skills/forget-notice.js.map +1 -0
  305. package/dist/skills/found-skill.d.ts +35 -0
  306. package/dist/skills/found-skill.d.ts.map +1 -0
  307. package/dist/skills/found-skill.js +66 -0
  308. package/dist/skills/found-skill.js.map +1 -0
  309. package/dist/skills/front-version.d.ts +12 -0
  310. package/dist/skills/front-version.d.ts.map +1 -0
  311. package/dist/skills/front-version.js +87 -0
  312. package/dist/skills/front-version.js.map +1 -0
  313. package/dist/skills/host-facts.d.ts +51 -0
  314. package/dist/skills/host-facts.d.ts.map +1 -0
  315. package/dist/skills/host-facts.js +134 -0
  316. package/dist/skills/host-facts.js.map +1 -0
  317. package/dist/skills/ledger.d.ts +43 -0
  318. package/dist/skills/ledger.d.ts.map +1 -0
  319. package/dist/skills/ledger.js +336 -0
  320. package/dist/skills/ledger.js.map +1 -0
  321. package/dist/skills/lessons.d.ts +60 -0
  322. package/dist/skills/lessons.d.ts.map +1 -0
  323. package/dist/skills/lessons.js +396 -0
  324. package/dist/skills/lessons.js.map +1 -0
  325. package/dist/skills/never-save.d.ts +5 -0
  326. package/dist/skills/never-save.d.ts.map +1 -0
  327. package/dist/skills/never-save.js +158 -0
  328. package/dist/skills/never-save.js.map +1 -0
  329. package/dist/skills/offer-answers.d.ts +20 -0
  330. package/dist/skills/offer-answers.d.ts.map +1 -0
  331. package/dist/skills/offer-answers.js +51 -0
  332. package/dist/skills/offer-answers.js.map +1 -0
  333. package/dist/skills/own-folder.d.ts +9 -0
  334. package/dist/skills/own-folder.d.ts.map +1 -0
  335. package/dist/skills/own-folder.js +36 -0
  336. package/dist/skills/own-folder.js.map +1 -0
  337. package/dist/skills/params.d.ts +39 -0
  338. package/dist/skills/params.d.ts.map +1 -0
  339. package/dist/skills/params.js +275 -0
  340. package/dist/skills/params.js.map +1 -0
  341. package/dist/skills/person.d.ts +34 -0
  342. package/dist/skills/person.d.ts.map +1 -0
  343. package/dist/skills/person.js +102 -0
  344. package/dist/skills/person.js.map +1 -0
  345. package/dist/skills/probe-clean.d.ts +9 -0
  346. package/dist/skills/probe-clean.d.ts.map +1 -0
  347. package/dist/skills/probe-clean.js +48 -0
  348. package/dist/skills/probe-clean.js.map +1 -0
  349. package/dist/skills/probes.d.ts +26 -0
  350. package/dist/skills/probes.d.ts.map +1 -0
  351. package/dist/skills/probes.js +225 -0
  352. package/dist/skills/probes.js.map +1 -0
  353. package/dist/skills/ran-less.d.ts +5 -0
  354. package/dist/skills/ran-less.d.ts.map +1 -0
  355. package/dist/skills/ran-less.js +51 -0
  356. package/dist/skills/ran-less.js.map +1 -0
  357. package/dist/skills/recall.d.ts +119 -0
  358. package/dist/skills/recall.d.ts.map +1 -0
  359. package/dist/skills/recall.js +370 -0
  360. package/dist/skills/recall.js.map +1 -0
  361. package/dist/skills/receipts.d.ts +44 -0
  362. package/dist/skills/receipts.d.ts.map +1 -0
  363. package/dist/skills/receipts.js +223 -0
  364. package/dist/skills/receipts.js.map +1 -0
  365. package/dist/skills/risk-delta.d.ts +32 -0
  366. package/dist/skills/risk-delta.d.ts.map +1 -0
  367. package/dist/skills/risk-delta.js +56 -0
  368. package/dist/skills/risk-delta.js.map +1 -0
  369. package/dist/skills/risk.d.ts +73 -0
  370. package/dist/skills/risk.d.ts.map +1 -0
  371. package/dist/skills/risk.js +396 -0
  372. package/dist/skills/risk.js.map +1 -0
  373. package/dist/skills/run-checkout.d.ts +4 -0
  374. package/dist/skills/run-checkout.d.ts.map +1 -0
  375. package/dist/skills/run-checkout.js +37 -0
  376. package/dist/skills/run-checkout.js.map +1 -0
  377. package/dist/skills/runblock.d.ts +45 -0
  378. package/dist/skills/runblock.d.ts.map +1 -0
  379. package/dist/skills/runblock.js +222 -0
  380. package/dist/skills/runblock.js.map +1 -0
  381. package/dist/skills/save-parse.d.ts +42 -0
  382. package/dist/skills/save-parse.d.ts.map +1 -0
  383. package/dist/skills/save-parse.js +172 -0
  384. package/dist/skills/save-parse.js.map +1 -0
  385. package/dist/skills/save-store.d.ts +12 -0
  386. package/dist/skills/save-store.d.ts.map +1 -0
  387. package/dist/skills/save-store.js +22 -0
  388. package/dist/skills/save-store.js.map +1 -0
  389. package/dist/skills/settings.d.ts +38 -0
  390. package/dist/skills/settings.d.ts.map +1 -0
  391. package/dist/skills/settings.js +133 -0
  392. package/dist/skills/settings.js.map +1 -0
  393. package/dist/skills/skill-diff.d.ts +40 -0
  394. package/dist/skills/skill-diff.d.ts.map +1 -0
  395. package/dist/skills/skill-diff.js +191 -0
  396. package/dist/skills/skill-diff.js.map +1 -0
  397. package/dist/skills/skill-file.d.ts +24 -0
  398. package/dist/skills/skill-file.d.ts.map +1 -0
  399. package/dist/skills/skill-file.js +97 -0
  400. package/dist/skills/skill-file.js.map +1 -0
  401. package/dist/skills/skill-rebuild.d.ts +27 -0
  402. package/dist/skills/skill-rebuild.d.ts.map +1 -0
  403. package/dist/skills/skill-rebuild.js +85 -0
  404. package/dist/skills/skill-rebuild.js.map +1 -0
  405. package/dist/skills/skillmd.d.ts +17 -0
  406. package/dist/skills/skillmd.d.ts.map +1 -0
  407. package/dist/skills/skillmd.js +219 -0
  408. package/dist/skills/skillmd.js.map +1 -0
  409. package/dist/skills/skills.d.ts +2 -0
  410. package/dist/skills/skills.d.ts.map +1 -1
  411. package/dist/skills/skills.js +1 -1
  412. package/dist/skills/skills.js.map +1 -1
  413. package/dist/skills/staleness.d.ts +27 -0
  414. package/dist/skills/staleness.d.ts.map +1 -0
  415. package/dist/skills/staleness.js +178 -0
  416. package/dist/skills/staleness.js.map +1 -0
  417. package/dist/skills/state.d.ts +36 -0
  418. package/dist/skills/state.d.ts.map +1 -0
  419. package/dist/skills/state.js +259 -0
  420. package/dist/skills/state.js.map +1 -0
  421. package/dist/skills/store-fs.d.ts +77 -0
  422. package/dist/skills/store-fs.d.ts.map +1 -0
  423. package/dist/skills/store-fs.js +194 -0
  424. package/dist/skills/store-fs.js.map +1 -0
  425. package/dist/skills/store-held.d.ts +44 -0
  426. package/dist/skills/store-held.d.ts.map +1 -0
  427. package/dist/skills/store-held.js +147 -0
  428. package/dist/skills/store-held.js.map +1 -0
  429. package/dist/skills/store.d.ts +73 -0
  430. package/dist/skills/store.d.ts.map +1 -0
  431. package/dist/skills/store.js +279 -0
  432. package/dist/skills/store.js.map +1 -0
  433. package/dist/skills/types.d.ts +422 -0
  434. package/dist/skills/types.d.ts.map +1 -0
  435. package/dist/skills/types.js +2 -0
  436. package/dist/skills/types.js.map +1 -0
  437. package/dist/skills/validate.d.ts +11 -0
  438. package/dist/skills/validate.d.ts.map +1 -0
  439. package/dist/skills/validate.js +112 -0
  440. package/dist/skills/validate.js.map +1 -0
  441. package/dist/skills/versions-fs.d.ts +45 -0
  442. package/dist/skills/versions-fs.d.ts.map +1 -0
  443. package/dist/skills/versions-fs.js +234 -0
  444. package/dist/skills/versions-fs.js.map +1 -0
  445. package/dist/skills/versions.d.ts +56 -0
  446. package/dist/skills/versions.d.ts.map +1 -0
  447. package/dist/skills/versions.js +351 -0
  448. package/dist/skills/versions.js.map +1 -0
  449. package/dist/telemetry/contract.d.ts.map +1 -1
  450. package/dist/telemetry/contract.js +8 -6
  451. package/dist/telemetry/contract.js.map +1 -1
  452. package/dist/telemetry/notice.d.ts.map +1 -1
  453. package/dist/telemetry/notice.js +3 -1
  454. package/dist/telemetry/notice.js.map +1 -1
  455. package/dist/trace/check-reads.d.ts +36 -0
  456. package/dist/trace/check-reads.d.ts.map +1 -0
  457. package/dist/trace/check-reads.js +57 -0
  458. package/dist/trace/check-reads.js.map +1 -0
  459. package/dist/trace/checks.d.ts +63 -0
  460. package/dist/trace/checks.d.ts.map +1 -0
  461. package/dist/trace/checks.js +487 -0
  462. package/dist/trace/checks.js.map +1 -0
  463. package/dist/trace/claude.d.ts +124 -0
  464. package/dist/trace/claude.d.ts.map +1 -0
  465. package/dist/trace/claude.js +303 -0
  466. package/dist/trace/claude.js.map +1 -0
  467. package/dist/trace/codex-exec.d.ts +22 -0
  468. package/dist/trace/codex-exec.d.ts.map +1 -0
  469. package/dist/trace/codex-exec.js +129 -0
  470. package/dist/trace/codex-exec.js.map +1 -0
  471. package/dist/trace/codex-meta.d.ts +23 -0
  472. package/dist/trace/codex-meta.d.ts.map +1 -0
  473. package/dist/trace/codex-meta.js +112 -0
  474. package/dist/trace/codex-meta.js.map +1 -0
  475. package/dist/trace/codex.d.ts +33 -0
  476. package/dist/trace/codex.d.ts.map +1 -0
  477. package/dist/trace/codex.js +485 -0
  478. package/dist/trace/codex.js.map +1 -0
  479. package/dist/trace/cursor.d.ts +16 -0
  480. package/dist/trace/cursor.d.ts.map +1 -0
  481. package/dist/trace/cursor.js +133 -0
  482. package/dist/trace/cursor.js.map +1 -0
  483. package/dist/trace/dialect.d.ts +43 -0
  484. package/dist/trace/dialect.d.ts.map +1 -0
  485. package/dist/trace/dialect.js +95 -0
  486. package/dist/trace/dialect.js.map +1 -0
  487. package/dist/trace/epochs.d.ts +10 -0
  488. package/dist/trace/epochs.d.ts.map +1 -0
  489. package/dist/trace/epochs.js +16 -0
  490. package/dist/trace/epochs.js.map +1 -0
  491. package/dist/trace/failure.d.ts +15 -0
  492. package/dist/trace/failure.d.ts.map +1 -0
  493. package/dist/trace/failure.js +69 -0
  494. package/dist/trace/failure.js.map +1 -0
  495. package/dist/trace/gate-file.d.ts +71 -0
  496. package/dist/trace/gate-file.d.ts.map +1 -0
  497. package/dist/trace/gate-file.js +329 -0
  498. package/dist/trace/gate-file.js.map +1 -0
  499. package/dist/trace/guard-fast.d.ts +22 -0
  500. package/dist/trace/guard-fast.d.ts.map +1 -0
  501. package/dist/trace/guard-fast.js +213 -0
  502. package/dist/trace/guard-fast.js.map +1 -0
  503. package/dist/trace/host.d.ts +40 -0
  504. package/dist/trace/host.d.ts.map +1 -0
  505. package/dist/trace/host.js +84 -0
  506. package/dist/trace/host.js.map +1 -0
  507. package/dist/trace/housekeeping.d.ts +5 -0
  508. package/dist/trace/housekeeping.d.ts.map +1 -0
  509. package/dist/trace/housekeeping.js +93 -0
  510. package/dist/trace/housekeeping.js.map +1 -0
  511. package/dist/trace/jobs.d.ts +52 -0
  512. package/dist/trace/jobs.d.ts.map +1 -0
  513. package/dist/trace/jobs.js +182 -0
  514. package/dist/trace/jobs.js.map +1 -0
  515. package/dist/trace/known-repo.d.ts +3 -0
  516. package/dist/trace/known-repo.d.ts.map +1 -0
  517. package/dist/trace/known-repo.js +55 -0
  518. package/dist/trace/known-repo.js.map +1 -0
  519. package/dist/trace/pool.d.ts +40 -0
  520. package/dist/trace/pool.d.ts.map +1 -0
  521. package/dist/trace/pool.js +154 -0
  522. package/dist/trace/pool.js.map +1 -0
  523. package/dist/trace/redact.d.ts +8 -0
  524. package/dist/trace/redact.d.ts.map +1 -0
  525. package/dist/trace/redact.js +63 -0
  526. package/dist/trace/redact.js.map +1 -0
  527. package/dist/trace/repairs.d.ts +27 -0
  528. package/dist/trace/repairs.d.ts.map +1 -0
  529. package/dist/trace/repairs.js +303 -0
  530. package/dist/trace/repairs.js.map +1 -0
  531. package/dist/trace/run-calls.d.ts +82 -0
  532. package/dist/trace/run-calls.d.ts.map +1 -0
  533. package/dist/trace/run-calls.js +331 -0
  534. package/dist/trace/run-calls.js.map +1 -0
  535. package/dist/trace/run-join.d.ts +20 -0
  536. package/dist/trace/run-join.d.ts.map +1 -0
  537. package/dist/trace/run-join.js +99 -0
  538. package/dist/trace/run-join.js.map +1 -0
  539. package/dist/trace/run-sheets.d.ts +26 -0
  540. package/dist/trace/run-sheets.d.ts.map +1 -0
  541. package/dist/trace/run-sheets.js +145 -0
  542. package/dist/trace/run-sheets.js.map +1 -0
  543. package/dist/trace/run-turns.d.ts +24 -0
  544. package/dist/trace/run-turns.d.ts.map +1 -0
  545. package/dist/trace/run-turns.js +67 -0
  546. package/dist/trace/run-turns.js.map +1 -0
  547. package/dist/trace/run-verdict.d.ts +24 -0
  548. package/dist/trace/run-verdict.d.ts.map +1 -0
  549. package/dist/trace/run-verdict.js +176 -0
  550. package/dist/trace/run-verdict.js.map +1 -0
  551. package/dist/trace/runs.d.ts +54 -0
  552. package/dist/trace/runs.d.ts.map +1 -0
  553. package/dist/trace/runs.js +501 -0
  554. package/dist/trace/runs.js.map +1 -0
  555. package/dist/trace/seen-cache.d.ts +9 -0
  556. package/dist/trace/seen-cache.d.ts.map +1 -0
  557. package/dist/trace/seen-cache.js +46 -0
  558. package/dist/trace/seen-cache.js.map +1 -0
  559. package/dist/trace/shell-scan.d.ts +52 -0
  560. package/dist/trace/shell-scan.d.ts.map +1 -0
  561. package/dist/trace/shell-scan.js +318 -0
  562. package/dist/trace/shell-scan.js.map +1 -0
  563. package/dist/trace/shell-words.d.ts +91 -0
  564. package/dist/trace/shell-words.d.ts.map +1 -0
  565. package/dist/trace/shell-words.js +558 -0
  566. package/dist/trace/shell-words.js.map +1 -0
  567. package/dist/trace/shell.d.ts +91 -0
  568. package/dist/trace/shell.d.ts.map +1 -0
  569. package/dist/trace/shell.js +435 -0
  570. package/dist/trace/shell.js.map +1 -0
  571. package/dist/trace/skill-session.d.ts +64 -0
  572. package/dist/trace/skill-session.d.ts.map +1 -0
  573. package/dist/trace/skill-session.js +232 -0
  574. package/dist/trace/skill-session.js.map +1 -0
  575. package/dist/trace/state.d.ts +27 -0
  576. package/dist/trace/state.d.ts.map +1 -0
  577. package/dist/trace/state.js +98 -0
  578. package/dist/trace/state.js.map +1 -0
  579. package/dist/trace/tamper-code.d.ts +9 -0
  580. package/dist/trace/tamper-code.d.ts.map +1 -0
  581. package/dist/trace/tamper-code.js +174 -0
  582. package/dist/trace/tamper-code.js.map +1 -0
  583. package/dist/trace/tamper-git.d.ts +12 -0
  584. package/dist/trace/tamper-git.d.ts.map +1 -0
  585. package/dist/trace/tamper-git.js +77 -0
  586. package/dist/trace/tamper-git.js.map +1 -0
  587. package/dist/trace/tamper.d.ts +12 -0
  588. package/dist/trace/tamper.d.ts.map +1 -0
  589. package/dist/trace/tamper.js +405 -0
  590. package/dist/trace/tamper.js.map +1 -0
  591. package/dist/trace/typed.d.ts +30 -0
  592. package/dist/trace/typed.d.ts.map +1 -0
  593. package/dist/trace/typed.js +132 -0
  594. package/dist/trace/typed.js.map +1 -0
  595. package/dist/trace/types.d.ts +477 -0
  596. package/dist/trace/types.d.ts.map +1 -0
  597. package/dist/trace/types.js +12 -0
  598. package/dist/trace/types.js.map +1 -0
  599. package/dist/trace/wall-state.d.ts +86 -0
  600. package/dist/trace/wall-state.d.ts.map +1 -0
  601. package/dist/trace/wall-state.js +279 -0
  602. package/dist/trace/wall-state.js.map +1 -0
  603. package/dist/trace/walls-glob.d.ts +11 -0
  604. package/dist/trace/walls-glob.d.ts.map +1 -0
  605. package/dist/trace/walls-glob.js +264 -0
  606. package/dist/trace/walls-glob.js.map +1 -0
  607. package/dist/trace/walls-quoted.d.ts +25 -0
  608. package/dist/trace/walls-quoted.d.ts.map +1 -0
  609. package/dist/trace/walls-quoted.js +81 -0
  610. package/dist/trace/walls-quoted.js.map +1 -0
  611. package/dist/trace/walls.d.ts +40 -0
  612. package/dist/trace/walls.d.ts.map +1 -0
  613. package/dist/trace/walls.js +318 -0
  614. package/dist/trace/walls.js.map +1 -0
  615. package/dist/upkeep.d.ts +1 -1
  616. package/dist/upkeep.d.ts.map +1 -1
  617. package/dist/upkeep.js +4 -1
  618. package/dist/upkeep.js.map +1 -1
  619. package/dist/util/actions-folder.d.ts +2 -0
  620. package/dist/util/actions-folder.d.ts.map +1 -0
  621. package/dist/util/actions-folder.js +9 -0
  622. package/dist/util/actions-folder.js.map +1 -0
  623. package/dist/util/front-matter.d.ts +31 -4
  624. package/dist/util/front-matter.d.ts.map +1 -1
  625. package/dist/util/front-matter.js +95 -3
  626. package/dist/util/front-matter.js.map +1 -1
  627. package/dist/util/private-file.d.ts +14 -0
  628. package/dist/util/private-file.d.ts.map +1 -0
  629. package/dist/util/private-file.js +82 -0
  630. package/dist/util/private-file.js.map +1 -0
  631. package/dist/util/private-fs.d.ts +25 -0
  632. package/dist/util/private-fs.d.ts.map +1 -0
  633. package/dist/util/private-fs.js +253 -0
  634. package/dist/util/private-fs.js.map +1 -0
  635. package/dist/util/state.d.ts.map +1 -1
  636. package/dist/util/state.js +3 -0
  637. package/dist/util/state.js.map +1 -1
  638. package/dist/vendor/tree-sitter-swift/LICENSE +21 -0
  639. package/dist/vendor/tree-sitter-swift/node-types.json +30694 -0
  640. package/dist/vendor/tree-sitter-swift/prebuilds/darwin-arm64/tree-sitter-swift.node +0 -0
  641. package/dist/vendor/tree-sitter-swift/prebuilds/darwin-x64/tree-sitter-swift.node +0 -0
  642. package/dist/vendor/tree-sitter-swift/prebuilds/linux-arm64/tree-sitter-swift.node +0 -0
  643. package/dist/vendor/tree-sitter-swift/prebuilds/linux-x64/tree-sitter-swift.node +0 -0
  644. package/dist/vendor/tree-sitter-swift/prebuilds/win32-arm64/tree-sitter-swift.node +0 -0
  645. package/dist/vendor/tree-sitter-swift/prebuilds/win32-x64/tree-sitter-swift.node +0 -0
  646. package/dist/viewer/app.js +2 -2
  647. package/package.json +10 -7
  648. package/scripts/run-tests.mjs +14 -2
  649. package/scripts/stamp-telemetry-key.mjs +8 -5
  650. package/scripts/vendor-grammars.mjs +35 -0
package/dist/cli.js CHANGED
@@ -41,6 +41,7 @@ import { planInit, selectedWrites } from "./hosts/plan.js";
41
41
  import { planRetract, runRetract, changed } from "./hosts/retract.js";
42
42
  import { compactWrites, formatNonInteractiveHelp, formatPlan, pickLearnings, runPicker } from "./cli-picker.js";
43
43
  import { buildGraphWithProgress } from "./claude/build-progress.js";
44
+ import { installBrainsServer, removeBrainsServer } from "./hosts/claude-global.js";
44
45
  import { homedir } from "node:os";
45
46
  import { formatUpgradeReport, formatVersionReport, getNpmViewVersion, readCurrentVersion, readPackageName, runUpgrade } from "./cli-meta.js";
46
47
  import { nativeFallbackNotice } from "./graph/native.js";
@@ -49,13 +50,20 @@ import { normalizePathPrefix } from "./util/paths.js";
49
50
  import { latestSession, formatSessionStats, sessionInputRate } from "./claude/session-metrics.js";
50
51
  import { listSessionIds, readSession, sessionDir } from "./claude/state.js";
51
52
  import { costLabel, listNotes, noteName, notePlace, noteTargets, readingTokens, savedByNote, commitLines, PERSONAL_SAVED_LINE, PR_SPEEDUP, SECTIONS, tokensWorthSaying, sectionLead, shortDate, tokensLabel, writeNote, mentionedFiles } from "./notes/notes.js";
53
+ import { findLearning, liveLearnings } from "./notes/learning-fields.js";
52
54
  import { aboutTerms, currentSessionCost } from "./notes/session-cost.js";
53
55
  import { beginPastRead, finishPastRead } from "./notes/cold-start.js";
54
- import { beginReviewRead, finishReviewRead } from "./notes/pr-reviews.js";
56
+ import { skippedSkillLines } from "./skills/found-skill.js";
57
+ import { anchoredReviews, beginReviewRead, finishReviewRead } from "./notes/pr-reviews.js";
58
+ import { finishMemoryRead, memoryCandidates, offeredBefore } from "./notes/memory-notes.js";
59
+ import { finishPair, PairRun, validPairCode } from "./cloud/pair.js";
55
60
  import { currentBranch, noteAuthor, teammatesOn } from "./notes/git-facts.js";
61
+ import { gateNote, namesRepoSymbol, symbolCandidates, trackedFiles } from "./notes/note-gate.js";
56
62
  import { confirm, fold, getSkill, learn, listSkills, publish, skillForAgent, skillName, skillStatus } from "./skills/skills.js";
63
+ import { recordLookup } from "./cloud/lookup-activity.js";
57
64
  import { clearPending, cloudLines, loginLink, sessionKey, waitForConnect } from "./cloud/nudge.js";
58
- import { addRepo, clearCloud, cloudEnabled, fetchMe, fetchTeam, isError, readCloud, writeCloud } from "./cloud/workspace.js";
65
+ import { addRepo, clearCloud, cloudEnabled, fetchMe, fetchTeam, isError, readCloud, trailUrl, writeCloud } from "./cloud/workspace.js";
66
+ import { claimConnectCode, connectAgain, offlineLine, learningsSummary, machineName, normalizeCode, OnboardingReporter, repoRef, teammatesChecked, teammatesFromGit, tokensOnOneLookup, } from "./cloud/onboarding.js";
59
67
  import { inWorkspace, maybeSyncInBackground, recordActivity, runSyncChild, syncRepo } from "./cloud/workspace-sync.js";
60
68
  import { checkLocally } from "./check/local-check.js";
61
69
  import { formatOverlap, maybeRefreshBranches, overlapsWith, pushedWhen, readBranchCache, refreshBranches } from "./notes/branches.js";
@@ -304,6 +312,14 @@ program
304
312
  .action(async () => {
305
313
  await waitForConnect();
306
314
  });
315
+ program
316
+ .command("_trail-pair", { hidden: true })
317
+ .description("internal: after `init --pair`, wait for the connect and the review in the browser, then upload the repo's learnings")
318
+ .argument("<state>")
319
+ .argument("<dir>")
320
+ .action(async (state, dir) => {
321
+ await finishPair(state, resolve(dir));
322
+ });
307
323
  program
308
324
  .command("_update-check", { hidden: true })
309
325
  .description("internal: refresh the cached latest-version answer")
@@ -390,6 +406,8 @@ grouped(program.command("build"), GROUP.setup)
390
406
  .option("--deep", "run the LLM pass: concept nodes (graft/*.md) + per-symbol summary/crux")
391
407
  .option("-e, --extensions <exts...>", 'code extensions to include (e.g. ".ts" ".py"); an extension with no parser is ignored with a warning that lists the supported set')
392
408
  .option("-j, --concurrency <n>", "files summarized in parallel during --deep (default 5)")
409
+ .option("--synth-concurrency <n>", "concept synthesis batches in flight at once during --deep (default 4). Separate from -j: " +
410
+ "each synthesis call carries a whole batch of summaries, not one file")
393
411
  .option("--no-reuse", "re-parse every file instead of replaying unchanged ones from the extraction cache")
394
412
  .option("--lsp", "add compiler-grade call edges via a language server if one is installed (opt-in, slower; e.g. rust-analyzer, clangd)")
395
413
  .option("--allow-partial", "with --deep: exit 0 even when some files' summaries failed (default: a degraded meaning tier exits 1)")
@@ -423,6 +441,11 @@ grouped(program.command("build"), GROUP.setup)
423
441
  console.error(`✗ --concurrency must be a number, got "${opts.concurrency}"`);
424
442
  process.exit(1);
425
443
  }
444
+ const synthConcurrency = opts.synthConcurrency ? Math.max(1, Number(opts.synthConcurrency)) : undefined;
445
+ if (opts.synthConcurrency && !Number.isFinite(synthConcurrency)) {
446
+ console.error(`✗ --synth-concurrency must be a number, got "${opts.synthConcurrency}"`);
447
+ process.exit(1);
448
+ }
426
449
  warnUnsupportedExtensions(opts.extensions);
427
450
  // Persisted BEFORE the build itself runs, so this invocation's walks (and
428
451
  // every later no-flag build / hooks refresh) see it identically — the
@@ -499,6 +522,7 @@ grouped(program.command("build"), GROUP.setup)
499
522
  deep: !!deep,
500
523
  extensions: opts.extensions,
501
524
  concurrency,
525
+ synthConcurrency,
502
526
  childConfig: cliConfig(),
503
527
  override: buildGlobalDir,
504
528
  includeDirs: opts.includeDir,
@@ -515,6 +539,7 @@ grouped(program.command("build"), GROUP.setup)
515
539
  const c = await engine.init(dir, {
516
540
  extensions: opts.extensions,
517
541
  onlyDirs,
542
+ synthConcurrency,
518
543
  onProgress: ({ phase, index, total, file }) => process.stderr.write(`\r${phase === "summarize" ? "reading" : "writing"} concepts ${index + 1}/${total}: ${file.slice(0, 40).padEnd(40)}`),
519
544
  }).catch((err) => {
520
545
  // Only the stage and a code enum; the message stays on this machine.
@@ -522,7 +547,11 @@ grouped(program.command("build"), GROUP.setup)
522
547
  throw err;
523
548
  });
524
549
  process.stderr.write("\n");
525
- console.log(`✓ concepts: ${c.nodes} nodes, ${c.links} links from ${c.files} files (${c.summarized} read, ${c.cached} cached)`);
550
+ const conceptLine = `concepts: ${c.nodes} nodes, ${c.links} links from ${c.files} files (${c.summarized} read, ${c.cached} cached)`;
551
+ if (c.nodes === 0)
552
+ console.error(`⚠ ${conceptLine}`);
553
+ else
554
+ console.log(`✓ ${conceptLine}`);
526
555
  for (const e of c.errors)
527
556
  console.error(`✗ ${e}`);
528
557
  conceptErrors = c.errors;
@@ -606,39 +635,27 @@ grouped(program.command("build"), GROUP.setup)
606
635
  }
607
636
  }
608
637
  });
609
- /** The file part of a hit's `file:Lx-Ly` pointer. */
610
- function pointerFile(pointer) {
611
- return pointer.replace(/:L\d+(-L?\d+)?$/, "");
612
- }
613
638
  /**
614
639
  * After `ask`, under trail: which shown learnings are a teammate's, and the
615
- * shown learnings recorded as a use, which is what the team page counts as
616
- * reuse. Then the background upkeep that keeps branches and the workspace
617
- * current. Spawns no git itself. Teammates' branches come up after an edit
640
+ * lookup recorded (the learnings shown are a use, which is what the team page
641
+ * counts as reuse; the tokens saved ride along). Then the background upkeep
642
+ * that keeps branches and the workspace current. Spawns no git itself. Teammates' branches come up after an edit
618
643
  * instead, once there's a function to compare (notes/edit-overlap.ts).
619
644
  */
620
645
  function afterAsk(r, repo) {
646
+ // Every lookup is reported, not only ones that showed a learning: the tokens
647
+ // the map saved are what the team page's running total counts. A no-op unless
648
+ // this machine is connected and the repo is the workspace's.
649
+ recordLookup(r, repo);
621
650
  if (!r.notesChecked)
622
651
  return;
623
652
  rememberRepo(repo);
624
653
  maybeRefreshBranches(repo);
625
654
  const me = noteAuthor(repo);
626
- const files = [...new Set(r.hits.map((h) => pointerFile(h.pointer)))];
627
655
  const shown = r.notes ?? [];
628
656
  if (shown.length) {
629
657
  for (const h of shown)
630
658
  h.note.teammate = !!h.note.author && !!me && h.note.author !== me;
631
- recordActivity({
632
- repo: repoPlace(repo).key,
633
- kind: "search",
634
- asker: me,
635
- session: sessionKey(repo) ?? `${me}-${new Date().toISOString().slice(0, 10)}`,
636
- query: r.query,
637
- files,
638
- shown_paths: shown.map((h) => noteName(h.note)),
639
- shown_authors: shown.map((h) => h.note.author),
640
- at: new Date().toISOString(),
641
- });
642
659
  const saved = shown.reduce((sum, h) => sum + savedByNote(h.note), 0);
643
660
  track("learning_reused", { teammate: String(shown.some((h) => h.note.teammate)), saved_tokens_bucket: savedTokensBucket(saved) }, { repo });
644
661
  }
@@ -837,9 +854,14 @@ program
837
854
  .command("mcp", { hidden: TRAIL })
838
855
  .description("Serve the graph over MCP (stdio) — exposes graft_find_code, graft_trace_calls, graft_find_all, graft_file_api, graft_repo_map and graft_check_freshness as tools")
839
856
  .argument(...DIR_ARG)
840
- .action(async (dirArg) => {
841
- const dir = noteQuery(queryRoot(dirArg));
857
+ .option("--brains", "serve only the connected Trail workspace's brain tools (what Claude Code gets after init --connect)")
858
+ .action(async (dirArg, opts) => {
842
859
  const { startMcpServer } = await import("./mcp/server.js");
860
+ if (opts.brains) {
861
+ startMcpServer(process.cwd(), undefined, currentVersion, { brainsOnly: true });
862
+ return;
863
+ }
864
+ const dir = noteQuery(queryRoot(dirArg));
843
865
  const globalOpts = program.opts();
844
866
  startMcpServer(dir, globalOpts.dir, currentVersion);
845
867
  });
@@ -984,6 +1006,8 @@ grouped(program.command("note", { hidden: !TRAIL }), GROUP.memory)
984
1006
  .option("--author <name>", "who wrote it (default: the first word of git config user.name)")
985
1007
  .option("--minutes <n>", "what it took to work out, when the agent's transcript can't say")
986
1008
  .option("--tokens <n>", "the same, in tokens")
1009
+ .option("--replaces <file>", "an earlier learning this one takes over from (its file name in .trail/learnings/); the old file stays")
1010
+ .option("--anyway", "save it even if trail's checks (names code, says why, not status, not a repeat) turn it away")
987
1011
  .option("--json", "print the saved learning as JSON")
988
1012
  .argument("[dir]", "repository root", ".")
989
1013
  .action(async (dir, opts) => {
@@ -1154,8 +1178,19 @@ grouped(program.command("init"), GROUP.setup)
1154
1178
  .option("-y, --yes", "skip the picker and wire every detected agent (the pre-0.8 default)")
1155
1179
  .option("--no-global", "skip writes outside this repo (the ~/.codex/ config + hooks)")
1156
1180
  .option("--verbose", "print every file written, the graph build's own output and the closing banner")
1181
+ .option("--connect <code>", "connect this machine to your Trail workspace with the one-time code from team.trailhq.com, and send it a summary of this repo (titles, counts and names; never code)")
1182
+ .option("--pair <code>", "the pair code from Trail's onboarding page: that page shows this run live, then you review what it found")
1183
+ .option("--server <url>", "the Trail that gave the code (default: Trail cloud, https://team.trailhq.com)")
1157
1184
  .action(async (dir, opts) => {
1158
- await runInitCommand(dir, opts);
1185
+ if (opts.connect !== undefined && opts.pair !== undefined) {
1186
+ console.error("✗ --connect and --pair are two ways in: use the one your Trail page gave you");
1187
+ process.exitCode = 1;
1188
+ return;
1189
+ }
1190
+ if (opts.connect !== undefined)
1191
+ await runConnectInit(dir, opts);
1192
+ else
1193
+ await runInitCommand(dir, opts);
1159
1194
  });
1160
1195
  /** Everything on stdin, or "" when nothing is piped in. */
1161
1196
  async function readStdin() {
@@ -1204,14 +1239,55 @@ async function runNoteCommand(repo, opts) {
1204
1239
  // Then the files the note names, then the ones the session only read.
1205
1240
  touches: [...new Set([...t.touches, ...mentionedFiles(t.dir, body), ...(t.read ?? [])])].slice(0, 10),
1206
1241
  }));
1242
+ // A touch is a file git tracks or one this session edited: never a file
1243
+ // that was just lying around (a token, a scratch file).
1244
+ const real = (dir, files) => {
1245
+ const tracked = trackedFiles(dir, files.map((f) => f.split("#")[0]));
1246
+ const edited = new Set((spent?.edited ?? []).map((f) => relative(dir, f).split("\\").join("/")));
1247
+ return files.filter((f) => tracked.has(f.split("#")[0]) || edited.has(f.split("#")[0]));
1248
+ };
1249
+ for (const t of targets)
1250
+ t.touches = real(t.dir, t.touches);
1251
+ const scope = opts.personal ? "personal" : "repo";
1252
+ const primary = targets[0];
1253
+ const existing = listNotes(primary.dir, { branches: false });
1254
+ let old;
1255
+ if (opts.replaces) {
1256
+ const want = basename(opts.replaces.trim());
1257
+ old = findLearning(existing, want);
1258
+ if (!old) {
1259
+ console.error(`✗ --replaces: no learning named ${want} here. The names are the files in .trail/learnings/.`);
1260
+ process.exitCode = 1;
1261
+ return;
1262
+ }
1263
+ }
1264
+ // What `replaces:` holds is the old learning's id (learning-fields.ts), not its file name.
1265
+ const replaces = old?.id;
1266
+ const text = `${opts.title}\n${body}`;
1267
+ const gate = gateNote({
1268
+ title: opts.title,
1269
+ body,
1270
+ personal: scope === "personal",
1271
+ files: real(primary.dir, [...new Set([...mentionedFiles(primary.dir, text), ...(opts.touches ? primary.touches : [])])]),
1272
+ namesSymbol: namesRepoSymbol(primary.dir, symbolCandidates(text)),
1273
+ touches: primary.touches,
1274
+ replaces,
1275
+ }, liveLearnings(existing).filter((n) => (n.scope ?? "repo") === scope));
1276
+ if (gate.problems.length && !opts.anyway) {
1277
+ console.error("✗ not saved yet:");
1278
+ for (const p of gate.problems)
1279
+ console.error(` · ${p}`);
1280
+ console.error(`Fix it and run ${cmd("graft note")} again, or add --anyway if a check is wrong.`);
1281
+ process.exitCode = 1;
1282
+ return;
1283
+ }
1207
1284
  const author = opts.author?.trim() || noteAuthor(repo);
1208
1285
  const date = new Date().toISOString().slice(0, 10);
1209
- const scope = opts.personal ? "personal" : "repo";
1210
1286
  const earlier = [];
1211
1287
  const saved = [];
1212
1288
  for (const t of targets) {
1213
1289
  earlier.push(...listNotes(t.dir));
1214
- saved.push(writeNote(t.dir, { title: opts.title, body, author, date, branch: currentBranch(t.dir), cost, touches: t.touches }, scope));
1290
+ saved.push(writeNote(t.dir, { title: opts.title, body, author, date, branch: currentBranch(t.dir), cost, touches: t.touches, replaces }, scope));
1215
1291
  }
1216
1292
  const touches = targets.flatMap((t) => t.touches);
1217
1293
  track("note_saved", { has_cost: String(cost?.tokens !== undefined || cost?.minutes !== undefined), touches_bucket: countBucket(touches.length), scope: saved[0]?.scope ?? scope }, { repo });
@@ -1240,6 +1316,10 @@ async function runNoteCommand(repo, opts) {
1240
1316
  console.log(`· builds on ${prior.author ? `${prior.author}'s` : "a"} learning from ${shortDate(prior.date)}`);
1241
1317
  if (!SECTIONS.some((s) => sectionLead(body, s.heading) !== null))
1242
1318
  console.log("· tip: learnings read best under ## Decided, ## Tried and ruled out and ## Watch out");
1319
+ for (const w of gate.warnings)
1320
+ console.log(`· ${w}`);
1321
+ if (old)
1322
+ console.log(`· takes over from ${noteName(old)}, which stays as the record`);
1243
1323
  const repoSaved = saved.filter((n) => n.scope === "repo");
1244
1324
  if (repoSaved.length) {
1245
1325
  // Who else works on these files, for the case for committing it.
@@ -1247,7 +1327,8 @@ async function runNoteCommand(repo, opts) {
1247
1327
  console.log(`${lines.uncommitted} · git add ${repoSaved.map(notePlace).join(" ")}`);
1248
1328
  if (!process.stdout.isTTY && TRAIL)
1249
1329
  console.log(`[trail] Close your reply with one line, word for word. If this learning is committed by the end of your turn: ${lines.committed}\n` +
1250
- ` If it isn't: ${lines.uncommitted}`);
1330
+ ` If it isn't: ${commitAsk(note.title, about)}\n` +
1331
+ " On a yes, commit it with the change: in the change's own commit when that isn't pushed yet.");
1251
1332
  }
1252
1333
  else {
1253
1334
  console.log("· just for you: kept on this machine, out of the repo");
@@ -1260,24 +1341,68 @@ async function runNoteCommand(repo, opts) {
1260
1341
  }
1261
1342
  printLines(cloudLines(repo, { command: "note" }));
1262
1343
  }
1344
+ /** What the agent asks after saving a team learning it hasn't committed: who reads it, and when. */
1345
+ export function commitAsk(title, about) {
1346
+ const when = about ? `when they open ${basename(about.split("#")[0])}` : "on their next session here";
1347
+ return `📝 Learning: "${title.replace(/"/g, "'")}" · your teammates' Claude reads it ${when}. Commit it with this change? (y/n)`;
1348
+ }
1263
1349
  /** Lines that close a command's output, with a blank line before them. */
1264
1350
  function printLines(lines) {
1265
1351
  if (lines.length)
1266
1352
  console.log(`\n${lines.join("\n")}`);
1267
1353
  }
1354
+ /**
1355
+ * `--server <url>` on `init --connect` and `--pair`: the Trail that gave the
1356
+ * code, kept for the rest of this run as TRAIL_URL. False, after saying why,
1357
+ * when it isn't a URL.
1358
+ */
1359
+ function useServerOption(raw) {
1360
+ const server = raw?.trim().replace(/\/+$/, "");
1361
+ if (!server)
1362
+ return true;
1363
+ if (!/^https?:\/\//.test(server)) {
1364
+ console.error("✗ --server takes a URL, like https://dev.trailhq.com");
1365
+ process.exitCode = 1;
1366
+ return false;
1367
+ }
1368
+ process.env.TRAIL_URL = server;
1369
+ return true;
1370
+ }
1371
+ /**
1372
+ * What `init --pair` hears from init, as the onboarding page's steps: the map
1373
+ * is done when `mapped` fires, the past sessions when `learned` does. Nothing
1374
+ * to say for a run that read no sessions (that step is already marked).
1375
+ */
1376
+ function pairProgress(pair) {
1377
+ return {
1378
+ mapped: (m) => pair.phase("files", "done", m.files ? `${m.files.toLocaleString("en-US")} files` : "", m.files ? `Mapped ${m.files.toLocaleString("en-US")} files` : "Mapped the code"),
1379
+ learned: (l) => {
1380
+ if (l.sessions <= 0)
1381
+ return;
1382
+ // The PR reviews' learnings come with `sessions: 0`; the rest were said in past sessions.
1383
+ const fromSessions = l.notes.filter((n) => n.sessions !== 0).length;
1384
+ pair.phase("decisions", "done", `${l.sessions} sessions`, `Read ${l.sessions} past sessions: ${fromSessions} thing${fromSessions === 1 ? "" : "s"} you told Claude more than once`);
1385
+ },
1386
+ };
1387
+ }
1268
1388
  /** Repo-relative with forward slashes, for printing. */
1269
1389
  function shown(repo, path) {
1270
1390
  return relative(repo, path).split("\\").join("/");
1271
1391
  }
1392
+ /** Agents init knows by another id: both read AGENTS.md. */
1393
+ const AGENT_ALIASES = { codex: "agents", opencode: "agents" };
1272
1394
  /** `graft init`. */
1273
- async function runInitCommand(dir, opts) {
1395
+ async function runInitCommand(dir, opts, progress) {
1274
1396
  if (opts.listAgents) {
1275
1397
  for (const id of [...hostIds(), "claude"])
1276
1398
  console.log(id);
1277
1399
  return null;
1278
1400
  }
1279
1401
  const repo = resolve(dir);
1280
- const explicit = Array.isArray(opts.agents) ? opts.agents : undefined;
1402
+ // Codex and OpenCode read AGENTS.md: `--agents codex` means that wiring.
1403
+ // The name as given is kept for Trail's onboarding page.
1404
+ const asked = Array.isArray(opts.agents) ? opts.agents : undefined;
1405
+ const explicit = asked ? [...new Set(asked.map((id) => AGENT_ALIASES[id] ?? id))] : undefined;
1281
1406
  // Checked before anything is written: run from the home folder, init wired
1282
1407
  // it, mapped every code file under it, then said to commit.
1283
1408
  const refusal = homeFolderRefusal(repo);
@@ -1357,6 +1482,26 @@ async function runInitCommand(dir, opts) {
1357
1482
  console.error("· no agents selected — nothing written");
1358
1483
  return null;
1359
1484
  }
1485
+ // With --pair, Trail's onboarding page is waiting on this run: tell it
1486
+ // first, so the person can sign up while the rest of init runs.
1487
+ let pair = null;
1488
+ if (opts.pair !== undefined) {
1489
+ if (!useServerOption(opts.server))
1490
+ return null;
1491
+ if (!validPairCode(opts.pair)) {
1492
+ console.error(`✗ --pair takes the code from Trail's onboarding page, got "${opts.pair}"`);
1493
+ process.exitCode = 1;
1494
+ return null;
1495
+ }
1496
+ const started = await PairRun.start(repo, opts.pair, { agent: asked?.[0] ?? ids[0], version: currentVersion });
1497
+ if ("error" in started) {
1498
+ console.error(`⚠ couldn't tell Trail about this run (${started.error}); setting up here anyway`);
1499
+ }
1500
+ else {
1501
+ pair = started;
1502
+ console.error(`· Trail's page is following along · ${started.url}`);
1503
+ }
1504
+ }
1360
1505
  const wantClaude = ids.includes("claude");
1361
1506
  const cliPath = fileURLToPath(import.meta.url);
1362
1507
  // Under trail, each repo gets its `.trail/learnings/`, committed with the
@@ -1383,6 +1528,16 @@ async function runInitCommand(dir, opts) {
1383
1528
  const reviewRead = TRAIL && wantClaude ? beginReviewRead(repo) : null;
1384
1529
  if (reviewRead)
1385
1530
  console.error("· reading review comments on this repo's merged PRs for what reviewers keep flagging");
1531
+ if (pair) {
1532
+ if (pastRead)
1533
+ pair.phase("decisions", "running", "", `Reading ${pastRead.sessions} past Claude sessions`);
1534
+ else
1535
+ pair.phase("decisions", "done", "none here");
1536
+ if (reviewRead)
1537
+ pair.phase("prs", "running", "", "Reading review comments on merged PRs");
1538
+ else
1539
+ pair.phase("prs", "done", "none here");
1540
+ }
1386
1541
  if (children.length)
1387
1542
  console.error(`· workspace: wiring ${repo} and ${children.length} child repo(s) — ${children.join(", ")}`);
1388
1543
  // Today's output, file by file, behind --verbose. Everything else gets one
@@ -1398,6 +1553,7 @@ async function runInitCommand(dir, opts) {
1398
1553
  // the workspace build, so it covers every child.
1399
1554
  let graphLine = null;
1400
1555
  if (!verbose) {
1556
+ pair?.phase("files", "running", "", "Mapping the code");
1401
1557
  const res = await buildGraphWithProgress(repo, { build: opts.build, cliPath });
1402
1558
  for (const m of res.messages)
1403
1559
  console.error(m);
@@ -1422,6 +1578,25 @@ async function runInitCommand(dir, opts) {
1422
1578
  graphLine = `· skipped the graph build — run ${cmd("graft build")}`;
1423
1579
  }
1424
1580
  }
1581
+ // --pair hears the same stages --connect does; only the ones InitProgress
1582
+ // has no slot for (a read starting, the PR read, nothing to read) stay inline.
1583
+ const report = pair ? pairProgress(pair) : progress;
1584
+ if (report?.mapped) {
1585
+ const g = loadGraphCached(contextDirFor(repo, children.length ? undefined : program.opts().dir));
1586
+ report.mapped({
1587
+ nodes: g?.meta.nodeCount ?? 0,
1588
+ edges: g?.meta.edgeCount ?? 0,
1589
+ files: g ? g.nodes.filter((n) => n.kind === "file").length : 0,
1590
+ });
1591
+ }
1592
+ // With --pair, one lookup measured against reading the files whole: the
1593
+ // page's token step. It needs the map, so it follows the build.
1594
+ let tokens = null;
1595
+ if (pair) {
1596
+ pair.phase("tokens", "running", "", "Measuring one lookup against reading the files");
1597
+ tokens = tokensOnOneLookup(repo, { contextDir: program.opts().dir });
1598
+ pair.tokensDone(tokens);
1599
+ }
1425
1600
  if (!verbose) {
1426
1601
  // Warnings and removals in full, whatever else is folded.
1427
1602
  const removed = [...new Set(reports.flatMap((r) => r.retracted))];
@@ -1438,23 +1613,30 @@ async function runInitCommand(dir, opts) {
1438
1613
  console.error(`✓ ${id.padEnd(width)} ${summary}`.trimEnd());
1439
1614
  }
1440
1615
  }
1441
- // Under trail: what init learned from past sessions and PR reviews, where
1442
- // this repo's learnings are, and how many there already are.
1616
+ // Under trail: what init learned, where this repo's learnings are, and how
1617
+ // many there already are. The team's come from what is tied to the code:
1618
+ // what reviewers kept flagging on merged PRs, and Claude Code's notes about
1619
+ // the repo on this machine. What the person kept telling Claude is theirs.
1443
1620
  let learnedNow = false;
1444
- if (TRAIL && (pastRead || reviewRead)) {
1621
+ let skillsWritten = [];
1622
+ let teamSaved = 0;
1623
+ const memory = TRAIL && wantClaude ? memoryCandidates(repo, { offered: offeredBefore(repo) }) : [];
1624
+ if (TRAIL && (pastRead || reviewRead || memory.length)) {
1445
1625
  if (pastRead && !pastRead.done)
1446
1626
  console.error("· still reading your past sessions…");
1447
1627
  const fromSessions = pastRead ? (await pastRead.result).findings : [];
1448
1628
  if (reviewRead && !reviewRead.done)
1449
1629
  console.error("· still reading PR reviews…");
1450
- const fromReviews = reviewRead ? (await reviewRead.result).findings : [];
1451
- // On a terminal, the person ticks which to keep in one list for both
1452
- // sources, all ticked to start; a scripted run, or one with --yes, keeps
1453
- // them all. Cancelling saves none, and the next init asks again.
1454
- const picked = process.stdin.isTTY && process.stderr.isTTY && !opts.yes && fromSessions.length + fromReviews.length
1630
+ const fromReviews = reviewRead ? anchoredReviews(repo, (await reviewRead.result).findings) : [];
1631
+ // On a terminal, the person ticks which to keep in one list, the team's
1632
+ // first and ticked, their own unticked. A scripted run, or one with
1633
+ // --yes, keeps the team's and what they told Claude. Cancelling saves
1634
+ // none, and the next init asks again.
1635
+ const picked = process.stdin.isTTY && process.stderr.isTTY && !opts.yes && !pair && fromReviews.length + memory.length + fromSessions.length
1455
1636
  ? await pickLearnings([
1456
- ...fromSessions.map((f) => ({ title: f.title, forTeam: f.for === "team", sessions: f.sessions.length })),
1457
- ...fromReviews.map((f) => ({ title: f.title, forTeam: true, prs: f.prs.length })),
1637
+ ...fromReviews.map((f) => ({ title: f.title, forTeam: true, prs: f.prs })),
1638
+ ...memory.map((c) => ({ title: c.title, forTeam: c.forTeam, memory: true })),
1639
+ ...fromSessions.map((f) => ({ title: f.title, forTeam: false, sessions: f.sessions.length })),
1458
1640
  ])
1459
1641
  : undefined;
1460
1642
  // The picked rows that are `found`'s, which start at `from` in the list.
@@ -1462,65 +1644,101 @@ async function runInitCommand(dir, opts) {
1462
1644
  ? undefined
1463
1645
  : async (found) => picked && picked.filter((i) => i >= from && i < from + found.length).map((i) => found[i - from]);
1464
1646
  const learned = [];
1465
- if (pastRead) {
1466
- const { result, saved, kept, cancelled } = await finishPastRead(repo, pastRead, keepFrom(0));
1467
- const offered = result.findings.length;
1647
+ const learnedFrom = [];
1648
+ let sessionsRead = 0;
1649
+ // Reviews first: which of them are offered depends on the learnings already saved.
1650
+ if (reviewRead) {
1651
+ const { result, saved, kept, cancelled } = await finishReviewRead(repo, reviewRead, keepFrom(0));
1652
+ const offered = fromReviews.length;
1653
+ pair?.phase("prs", "done", `${result.prs} PRs`, `Read ${result.prs} merged PRs: ${saved.length} thing${saved.length === 1 ? "" : "s"} reviewers made authors change`);
1468
1654
  if (saved.length) {
1469
1655
  const n = saved.length;
1470
1656
  const what = n < offered ? `kept ${n} of the ${offered} things` : `learned ${n} thing${n === 1 ? "" : "s"}`;
1471
- console.error(`✓ ${what} you told Claude more than once, from your last ${result.read} sessions here`);
1472
- // Each with how many sessions it was said in: saveFindings keeps the findings' order.
1473
- saved.forEach((note, i) => {
1474
- const said = kept[i]?.sessions.length ?? 0;
1475
- const count = said > 1 ? `told Claude in ${said} sessions: ` : "";
1476
- console.error(` ${note.scope === "personal" ? "just for you " : "for your team"} · ${count}${note.title}`);
1477
- });
1657
+ console.error(`✓ ${what} reviewers made authors change, from the last ${result.prs} merged PRs here`);
1658
+ // saveReviewFindings keeps the findings' order.
1659
+ saved.forEach((note, i) => console.error(` for your team · from review on ${(kept[i]?.prs ?? []).map((n) => `#${n}`).join(", ")}: ${note.title}`));
1478
1660
  }
1479
1661
  else if (cancelled) {
1480
- console.error(`· saved none of the ${offered} things you told Claude more than once; ${cmd("graft init")} again asks again`);
1662
+ console.error(`· saved none of the ${offered} things reviewers made authors change; ${cmd("graft init")} again asks again`);
1481
1663
  }
1482
1664
  else if (offered) {
1483
- console.error(`· kept none of the ${offered} things you told Claude more than once`);
1665
+ console.error(`· kept none of the ${offered} things reviewers made authors change`);
1484
1666
  }
1485
1667
  else if (result.reason === "failed") {
1486
- console.error(`· couldn't read your past sessions this time; ${cmd("graft init")} again tries again`);
1668
+ console.error(`· couldn't read PR reviews this time; ${cmd("graft init")} again tries again`);
1487
1669
  }
1488
1670
  learned.push(...saved);
1671
+ // Not said in past sessions: `sessions: 0` keeps them out of the page's session count.
1672
+ for (const note of saved)
1673
+ learnedFrom.push({ note, sessions: 0 });
1489
1674
  }
1490
- if (reviewRead) {
1491
- const { result, saved, kept, cancelled } = await finishReviewRead(repo, reviewRead, keepFrom(fromSessions.length));
1675
+ if (memory.length) {
1676
+ const { saved, cancelled } = await finishMemoryRead(repo, memory, keepFrom(fromReviews.length));
1677
+ if (saved.length) {
1678
+ console.error(`✓ kept ${saved.length} of the ${memory.length} notes your Claude keeps about this repo`);
1679
+ for (const note of saved)
1680
+ console.error(` ${note.scope === "personal" ? "just for you " : "for your team"} · ${note.title}`);
1681
+ }
1682
+ else if (cancelled) {
1683
+ console.error(`· saved none of the ${memory.length} notes your Claude keeps about this repo; ${cmd("graft init")} again asks again`);
1684
+ }
1685
+ learned.push(...saved);
1686
+ for (const note of saved)
1687
+ learnedFrom.push({ note, sessions: 0 });
1688
+ }
1689
+ if (pastRead) {
1690
+ const { result, saved, kept, skills, cancelled } = await finishPastRead(repo, pastRead, keepFrom(fromReviews.length + memory.length));
1691
+ sessionsRead = result.read ?? 0;
1692
+ skillsWritten = skills;
1693
+ for (const { skill, path } of skills) {
1694
+ console.error(`✓ wrote a skill from a procedure you walked Claude through more than once: ${skill.title}`);
1695
+ console.error(` ${skill.for === "team" ? "for your team" : "just for you "} · ${path}`);
1696
+ }
1697
+ if (!cancelled)
1698
+ for (const line of skippedSkillLines(repo, result.skills, skills))
1699
+ console.error(line);
1700
+ saved.forEach((note, i) => learnedFrom.push({ note, sessions: kept[i]?.sessions.length }));
1492
1701
  const offered = result.findings.length;
1493
1702
  if (saved.length) {
1494
1703
  const n = saved.length;
1495
1704
  const what = n < offered ? `kept ${n} of the ${offered} things` : `learned ${n} thing${n === 1 ? "" : "s"}`;
1496
- console.error(`✓ ${what} reviewers flagged on more than one PR, from the last ${result.prs} merged PRs here`);
1497
- // saveReviewFindings keeps the findings' order.
1498
- saved.forEach((note, i) => console.error(` for your team · flagged in review on ${kept[i]?.prs.length ?? 2} PRs: ${note.title}`));
1705
+ console.error(`✓ ${what} you told Claude more than once, from your last ${result.read} sessions here, just for you`);
1706
+ // Each with how many sessions it was said in: saveFindings keeps the findings' order.
1707
+ saved.forEach((note, i) => {
1708
+ const said = kept[i]?.sessions.length ?? 0;
1709
+ const count = said > 1 ? `told Claude in ${said} sessions: ` : "";
1710
+ console.error(` just for you · ${count}${note.title}`);
1711
+ });
1499
1712
  }
1500
1713
  else if (cancelled) {
1501
- console.error(`· saved none of the ${offered} things reviewers flagged; ${cmd("graft init")} again asks again`);
1714
+ console.error(`· saved none of the ${offered} things you told Claude more than once; ${cmd("graft init")} again asks again`);
1502
1715
  }
1503
1716
  else if (offered) {
1504
- console.error(`· kept none of the ${offered} things reviewers flagged`);
1717
+ console.error(`· kept none of the ${offered} things you told Claude more than once`);
1505
1718
  }
1506
1719
  else if (result.reason === "failed") {
1507
- console.error(`· couldn't read PR reviews this time; ${cmd("graft init")} again tries again`);
1720
+ console.error(`· couldn't read your past sessions this time; ${cmd("graft init")} again tries again`);
1508
1721
  }
1509
1722
  learned.push(...saved);
1510
1723
  }
1511
1724
  learnedNow = learned.length > 0;
1725
+ teamSaved = learned.filter((s) => s.scope === "repo").length;
1726
+ report?.learned?.({ notes: learnedFrom, sessions: sessionsRead });
1512
1727
  if (learnedNow) {
1513
1728
  console.error("· Claude follows them from your next session");
1514
- if (learned.some((s) => s.scope === "repo"))
1729
+ if (teamSaved)
1515
1730
  console.error(commitLines({}, teammatesOn(repo), "repo").uncommitted);
1516
1731
  }
1517
1732
  }
1733
+ // Nothing read this run (already read once, or not Claude Code): the page still gets its stage.
1734
+ if (!(TRAIL && (pastRead || reviewRead || memory.length)))
1735
+ report?.learned?.({ notes: [], sessions: 0 });
1518
1736
  if (TRAIL && !learnedNow) {
1519
1737
  const home = notesHome.get(repo);
1520
1738
  if (home) {
1521
1739
  const learnings = listNotes(repo, { branches: false }).filter((n) => n.scope === "repo");
1522
1740
  if (learnings.length === 0)
1523
- console.error(`✓ learnings ${LEARNINGS_DIR}/ · sessions that take real digging leave one there, for your whole team`);
1741
+ console.error(`✓ learnings ${LEARNINGS_DIR}/ · a session that fixes a bug, takes a correction or opens a PR leaves one there, for your whole team`);
1524
1742
  else
1525
1743
  console.error(`✓ learnings ${learnings.length} in ${LEARNINGS_DIR}/, from ${new Set(learnings.map((n) => n.author || "someone")).size} ${new Set(learnings.map((n) => n.author)).size === 1 ? "person" : "people"}`);
1526
1744
  }
@@ -1571,10 +1789,130 @@ async function runInitCommand(dir, opts) {
1571
1789
  nodes: graphs.reduce((n, g) => n + g.meta.nodeCount, 0),
1572
1790
  edges: graphs.reduce((n, g) => n + g.meta.edgeCount, 0),
1573
1791
  }));
1792
+ // Hand what was found to the review page, and leave a child waiting for
1793
+ // the person to connect and review there.
1794
+ if (pair) {
1795
+ // Teammates' branches on this person's files, checked against the
1796
+ // learnings just saved and the repo's own: synchronous, so the finding
1797
+ // exists before the page is told the read is done.
1798
+ pair.phase("team", "running", "", "Checking your team's branches");
1799
+ const teamNotes = listNotes(repo, { branches: false }).filter((n) => n.scope === "repo" && !n.ref);
1800
+ // No remote (or no default branch) means nothing was checked: `teammates`
1801
+ // is left out, since an empty list would say "checked, none".
1802
+ const teammates = teammatesChecked(repo, { learnings: teamNotes });
1803
+ pair.teamDone(teammates);
1804
+ pair.findings({ tokens, sessions: pastRead?.sessions, teammates });
1805
+ await pair.finish(repo, skillsWritten);
1806
+ pair.handOff(repo);
1807
+ console.error(`✓ Trail has what it found · review it in the browser: ${pair.url}`);
1808
+ if (!process.stdout.isTTY)
1809
+ console.error(`[trail] Tell the user, in one line: their Trail page has moved on; review what Trail found there. If it isn't open, the link is ${pair.url}`);
1810
+ }
1811
+ // Run by an agent, not a person: the last line tells it to ask about the
1812
+ // commit, since the person never saw the picker and Claude Code folds the rest.
1813
+ // With --pair the person reviews them on the Trail page first, so no ask here.
1814
+ if (teamSaved && !pair && !process.stderr.isTTY)
1815
+ console.error(`→ ${teamSaved} team learning${teamSaved === 1 ? "" : "s"} in ${LEARNINGS_DIR}/ ${teamSaved === 1 ? "isn't" : "aren't"} committed yet. Ask the user: "Commit ${teamSaved === 1 ? "it" : `the ${teamSaved}`} so your teammates' Claude starts from ${teamSaved === 1 ? "it" : "them"}? (yes / no)"`);
1574
1816
  // Sorted so `claude,cursor` and `cursor,claude` aggregate as one value.
1575
1817
  track("init_completed", { agents: [...ids].sort().join(","), consent: consent === undefined ? "unasked" : String(consent) }, { repo });
1576
1818
  return { ids };
1577
1819
  }
1820
+ /**
1821
+ * `trail init --connect <CODE>`: the onboarding on team.trailhq.com. Claims the
1822
+ * one-time code (a wrong or spent one stops here, before anything is written),
1823
+ * saves the workspace token where `trail login` keeps it, then runs init as
1824
+ * usual while the page ticks off connected, mapped and learned. At the end,
1825
+ * one summary of the repo goes up (a lookup's tokens saved, the learnings'
1826
+ * titles, teammates' branches from git: never code, diffs or session text),
1827
+ * then `done`. Offline, init still sets the repo up and says how to connect.
1828
+ */
1829
+ async function runConnectInit(dir, opts) {
1830
+ if (opts.listAgents || opts.dryRun) {
1831
+ if (opts.dryRun)
1832
+ console.error("· --dry-run: not connecting");
1833
+ await runInitCommand(dir, opts);
1834
+ return;
1835
+ }
1836
+ const repo = resolve(dir);
1837
+ if (!useServerOption(opts.server))
1838
+ return;
1839
+ const base = trailUrl();
1840
+ const code = normalizeCode(opts.connect ?? "");
1841
+ if (!code) {
1842
+ console.error(`✗ "${opts.connect ?? ""}" isn't a Trail code · it looks like TRL-7F2A-K9QD, from ${base}/welcome/code`);
1843
+ process.exitCode = 1;
1844
+ return;
1845
+ }
1846
+ const ref = repoRef(repo);
1847
+ const machine = machineName();
1848
+ const got = await claimConnectCode(base, code, { machine, repo: ref });
1849
+ const cloud = "cloud" in got ? got.cloud : null;
1850
+ if ("cloud" in got) {
1851
+ const cloud = got.cloud;
1852
+ writeCloud(cloud);
1853
+ // A sign-in link out from before is moot now.
1854
+ clearPending();
1855
+ console.error(`✓ connected to workspace ${cloud.workspace.name}`);
1856
+ track("brain_signup_settled", { outcome: "linked", mode: "connect", duration_bucket: durationBucket(0) }, { repo });
1857
+ }
1858
+ else if (got.reason === "invalid") {
1859
+ console.error(`✗ ${got.error} · get a new code at ${base}/welcome/code`);
1860
+ process.exitCode = 1;
1861
+ return;
1862
+ }
1863
+ else {
1864
+ console.error(offlineLine(got.error, base, code, cmd("graft init")));
1865
+ }
1866
+ const reporter = cloud ? new OnboardingReporter(cloud, ref, { machine }) : null;
1867
+ void reporter?.stage("connected");
1868
+ // What init learned this run, kept for the summary.
1869
+ const heard = { notes: [], sessions: 0 };
1870
+ const progress = reporter
1871
+ ? {
1872
+ mapped: (m) => void reporter.stage("mapped", m),
1873
+ learned: (l) => {
1874
+ Object.assign(heard, l);
1875
+ const team = learningsSummary(repo, l.notes);
1876
+ void reporter.stage("learned", { learnings: team.length, sessions: l.sessions || Math.max(0, ...team.map((t) => t.sessions)) });
1877
+ },
1878
+ }
1879
+ : undefined;
1880
+ // The page's command is pasted into a terminal, but an agent may run it for
1881
+ // them: with no terminal to pick in, wire the agents this machine has.
1882
+ const interactive = process.stdin.isTTY && process.stderr.isTTY;
1883
+ const res = await runInitCommand(dir, interactive ? opts : { ...opts, yes: true }, progress);
1884
+ if (!reporter || !cloud)
1885
+ return;
1886
+ if (!res) {
1887
+ await reporter.flush();
1888
+ return;
1889
+ }
1890
+ // What Trail found, for the page: route B, summaries only.
1891
+ const learnings = learningsSummary(repo, heard.notes);
1892
+ const sessions = heard.sessions || Math.max(0, ...learnings.map((l) => l.sessions));
1893
+ const tokens = tokensOnOneLookup(repo, { contextDir: program.opts().dir });
1894
+ const teamNotes = listNotes(repo, { branches: false }).filter((n) => n.scope === "repo" && !n.ref);
1895
+ const teammates = teammatesFromGit(repo, { learnings: teamNotes });
1896
+ const sent = await reporter.summary({ tokens, ...(sessions ? { sessions } : {}), learnings, teammates });
1897
+ await reporter.stage("done");
1898
+ await reporter.flush();
1899
+ // Claude Code gets the workspace's brains through one user-level `trail` entry.
1900
+ const brainsEntry = res.ids.includes("claude") && opts.global !== false ? installBrainsServer(homedir()) : null;
1901
+ if (sent) {
1902
+ const parts = [
1903
+ tokens ? `one lookup saved ${tokens.pct}% of the tokens` : null,
1904
+ `${learnings.length} learning${learnings.length === 1 ? "" : "s"}`,
1905
+ `${teammates.length} teammate branch${teammates.length === 1 ? "" : "es"} on your files`,
1906
+ ].filter(Boolean);
1907
+ console.error(`✓ sent ${cloud.workspace.name} a summary: ${parts.join(", ")} · titles, counts and names only, no code`);
1908
+ console.error(`· see what Trail found: ${base}/welcome/code/found`);
1909
+ }
1910
+ else {
1911
+ console.error(`· couldn't send the summary to Trail this time · with a new code from ${base}/welcome/code, ${connectAgain(cmd("graft init"), "<code>", base)} sends it again`);
1912
+ }
1913
+ if (brainsEntry?.action === "created" || brainsEntry?.action === "updated")
1914
+ console.error(`✓ Claude Code gets your workspace's brains in every project · restart it to pick them up`);
1915
+ }
1578
1916
  /** One repo's worth of `init` writes — the parent, then each workspace child. */
1579
1917
  function wireTarget(repo, ids, ctx) {
1580
1918
  const { home, cliPath, plan, wantClaude } = ctx;
@@ -1736,7 +2074,7 @@ function loginCommand(name = "login") {
1736
2074
  return new Command(name)
1737
2075
  .description("Connect this machine to a Trail workspace: sign in, pick the repos it gets, invite your team")
1738
2076
  .argument("[dir]", "the repo you're in, ticked first in the picker", ".")
1739
- .option("--server <url>", "a self-hosted Trail, e.g. https://trail.acme.internal (default: Trail cloud)")
2077
+ .option("--server <url>", "a self-hosted Trail, e.g. https://trail.acme.internal (default: Trail cloud, https://team.trailhq.com)")
1740
2078
  .action(async (dir, opts) => {
1741
2079
  const repo = resolve(dir);
1742
2080
  const server = opts.server?.trim().replace(/\/+$/, "");
@@ -1776,6 +2114,7 @@ function logoutCommand(name = "logout") {
1776
2114
  .action(() => {
1777
2115
  const cloud = readCloud();
1778
2116
  clearPending();
2117
+ removeBrainsServer(homedir());
1779
2118
  if (!clearCloud()) {
1780
2119
  console.error("· not connected to a workspace");
1781
2120
  return;
@@ -2157,6 +2496,9 @@ function statusCommand(name = "status") {
2157
2496
  if (cloudEnabled())
2158
2497
  row("workspace", "not connected · Claude offers a link once there are learnings to share");
2159
2498
  }
2499
+ else if (cloud.via === "connect") {
2500
+ row("workspace", `${cloud.workspace.name} · gets a summary of the repos you run ${cmd("graft init")} --connect in · ${cloud.workspace.url}`);
2501
+ }
2160
2502
  else
2161
2503
  row("workspace", `${cloud.workspace.name} · ${cloud.repos.length} repo${cloud.repos.length === 1 ? "" : "s"}${shared ? ", this one included" : `, not this one (${cmd("graft workspace add")})`} · ${cloud.workspace.url}`);
2162
2504
  row("this week", saved > 0 ? `~${shortCount(saved)} tokens saved` : "nothing saved yet");