@crouton-kit/crouter 0.3.31 → 0.3.33

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 (443) hide show
  1. package/README.md +8 -0
  2. package/dist/build-root.js +1 -0
  3. package/dist/builtin-memory/crouter-development/marketplaces.md +2 -2
  4. package/dist/builtin-memory/crouter-development/personas/base-prompt.md +2 -2
  5. package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +5 -5
  6. package/dist/builtin-memory/crouter-development/personas.md +3 -4
  7. package/dist/builtin-memory/crouter-development/plugins.md +18 -18
  8. package/dist/builtin-memory/design.md +4 -1
  9. package/dist/builtin-memory/development.md +4 -1
  10. package/dist/builtin-memory/internal/INDEX.md +1 -1
  11. package/dist/builtin-memory/internal/nodes-and-canvas.md +6 -6
  12. package/dist/builtin-memory/internal/storage-tiers.md +5 -5
  13. package/dist/builtin-memory/planning.md +4 -1
  14. package/dist/builtin-memory/product.md +80 -0
  15. package/dist/builtin-memory/spec.md +4 -1
  16. package/dist/builtin-personas/advisor/PERSONA.md +10 -0
  17. package/dist/builtin-personas/design/PERSONA.md +2 -2
  18. package/dist/builtin-personas/design/orchestrator.md +3 -3
  19. package/dist/builtin-personas/developer/PERSONA.md +1 -1
  20. package/dist/builtin-personas/developer/orchestrator.md +2 -2
  21. package/dist/builtin-personas/explore/PERSONA.md +3 -3
  22. package/dist/builtin-personas/explore/orchestrator.md +4 -2
  23. package/dist/builtin-personas/general/PERSONA.md +1 -1
  24. package/dist/builtin-personas/general/orchestrator.md +1 -1
  25. package/dist/builtin-personas/orchestration-kernel.md +7 -14
  26. package/dist/builtin-personas/plan/PERSONA.md +1 -1
  27. package/dist/builtin-personas/plan/orchestrator.md +2 -2
  28. package/dist/builtin-personas/plan/reviewers/architecture-fit/PERSONA.md +1 -1
  29. package/dist/builtin-personas/plan/reviewers/code-smells/PERSONA.md +1 -1
  30. package/dist/builtin-personas/plan/reviewers/pattern-consistency/PERSONA.md +1 -1
  31. package/dist/builtin-personas/plan/reviewers/requirements-coverage/PERSONA.md +1 -1
  32. package/dist/builtin-personas/plan/reviewers/security/PERSONA.md +1 -1
  33. package/dist/builtin-personas/product/PERSONA.md +18 -0
  34. package/dist/builtin-personas/product/orchestrator.md +14 -0
  35. package/dist/builtin-personas/product/teardown/PERSONA.md +13 -0
  36. package/dist/builtin-personas/review/PERSONA.md +1 -1
  37. package/dist/builtin-personas/review/orchestrator.md +1 -1
  38. package/dist/builtin-personas/runtime-base.md +5 -0
  39. package/dist/builtin-personas/spec/PERSONA.md +1 -1
  40. package/dist/builtin-personas/spec/orchestrator.md +2 -2
  41. package/dist/builtin-personas/spec/requirements/PERSONA.md +1 -1
  42. package/dist/builtin-views/canvas/core.mjs +82 -1
  43. package/dist/builtin-views/canvas/tui.mjs +9 -5
  44. package/dist/builtin-views/canvas/web.jsx +3 -2
  45. package/dist/builtin-views/chat/core.mjs +725 -0
  46. package/dist/builtin-views/chat/text.mjs +101 -0
  47. package/dist/builtin-views/chat/tui.mjs +368 -0
  48. package/dist/builtin-views/chat/web.jsx +367 -0
  49. package/dist/builtin-views/prompt-review/core.mjs +863 -0
  50. package/dist/builtin-views/prompt-review/text.mjs +15 -0
  51. package/dist/builtin-views/prompt-review/tui.mjs +196 -0
  52. package/dist/builtin-views/prompt-review/web.jsx +484 -0
  53. package/dist/builtin-views/settings/core.mjs +397 -0
  54. package/dist/builtin-views/settings/text.mjs +40 -0
  55. package/dist/builtin-views/settings/tui.mjs +95 -0
  56. package/dist/builtin-views/settings/web.jsx +167 -0
  57. package/dist/cli.js +16 -3
  58. package/dist/clients/attach/__tests__/autocomplete-and-bash-mode.test.js +96 -0
  59. package/dist/clients/attach/__tests__/bash-bang-routing.test.js +58 -0
  60. package/dist/clients/attach/__tests__/bundle-pi-tui-dedup.test.d.ts +1 -0
  61. package/dist/clients/attach/__tests__/bundle-pi-tui-dedup.test.js +56 -0
  62. package/dist/clients/attach/__tests__/titled-editor.test.js +22 -1
  63. package/dist/clients/attach/attach-cmd.d.ts +18 -0
  64. package/dist/clients/attach/attach-cmd.js +1723 -795
  65. package/dist/clients/attach/canvas-panels.js +2 -2
  66. package/dist/clients/attach/chat-view.d.ts +10 -0
  67. package/dist/clients/attach/chat-view.js +23 -0
  68. package/dist/clients/attach/config-load.d.ts +8 -4
  69. package/dist/clients/attach/config-load.js +2 -0
  70. package/dist/clients/attach/extension-dialogs.d.ts +2 -3
  71. package/dist/clients/attach/extension-dialogs.js +7 -9
  72. package/dist/clients/attach/graph-overlay.js +3 -2
  73. package/dist/clients/attach/input-controller.d.ts +4 -3
  74. package/dist/clients/attach/input-controller.js +24 -3
  75. package/dist/clients/attach/slash-commands.d.ts +7 -7
  76. package/dist/clients/attach/slash-commands.js +38 -14
  77. package/dist/clients/attach/titled-editor.js +18 -14
  78. package/dist/clients/attach/view-socket.d.ts +2 -1
  79. package/dist/clients/attach/view-socket.js +27 -8
  80. package/dist/clients/web/__tests__/source-cache.test.d.ts +1 -0
  81. package/dist/clients/web/__tests__/source-cache.test.js +32 -0
  82. package/dist/clients/web/dev-server.js +1 -0
  83. package/dist/clients/web/events.js +9 -11
  84. package/dist/clients/web/server.d.ts +4 -3
  85. package/dist/clients/web/server.js +138 -31
  86. package/dist/clients/web/source-cache.d.ts +10 -0
  87. package/dist/clients/web/source-cache.js +57 -0
  88. package/dist/clients/web/web-cmd.js +28 -9
  89. package/dist/commands/__tests__/human.test.js +30 -28
  90. package/dist/commands/canvas-history/search.js +1 -1
  91. package/dist/commands/canvas-history.js +5 -8
  92. package/dist/commands/canvas-issue.d.ts +2 -0
  93. package/dist/commands/canvas-issue.js +148 -0
  94. package/dist/commands/canvas-prune.js +19 -0
  95. package/dist/commands/canvas-rebuild-index.d.ts +2 -0
  96. package/dist/commands/canvas-rebuild-index.js +57 -0
  97. package/dist/commands/canvas-snapshot.d.ts +2 -0
  98. package/dist/commands/canvas-snapshot.js +48 -0
  99. package/dist/commands/canvas-tmux-spread.d.ts +2 -0
  100. package/dist/commands/canvas-tmux-spread.js +188 -0
  101. package/dist/commands/canvas.d.ts +1 -0
  102. package/dist/commands/canvas.js +16 -1
  103. package/dist/commands/chord.js +143 -48
  104. package/dist/commands/daemon.js +3 -3
  105. package/dist/commands/human/prompts.d.ts +0 -1
  106. package/dist/commands/human/prompts.js +39 -54
  107. package/dist/commands/human/queue.d.ts +12 -1
  108. package/dist/commands/human/queue.js +468 -73
  109. package/dist/commands/human/shared.d.ts +3 -4
  110. package/dist/commands/human/shared.js +3 -3
  111. package/dist/commands/human.js +11 -10
  112. package/dist/commands/memory/find.js +3 -2
  113. package/dist/commands/memory/list.js +4 -3
  114. package/dist/commands/memory/read.js +3 -3
  115. package/dist/commands/memory/shared.d.ts +1 -4
  116. package/dist/commands/memory/shared.js +6 -9
  117. package/dist/commands/memory/write.js +3 -3
  118. package/dist/commands/memory.js +4 -7
  119. package/dist/commands/node-context.d.ts +2 -0
  120. package/dist/commands/node-context.js +172 -0
  121. package/dist/commands/node-snapshot.d.ts +2 -0
  122. package/dist/commands/node-snapshot.js +123 -0
  123. package/dist/commands/node.js +228 -52
  124. package/dist/commands/pkg/plugin-inspect.js +6 -6
  125. package/dist/commands/pkg/plugin-manage.js +1 -1
  126. package/dist/commands/pkg/plugin.js +2 -2
  127. package/dist/commands/pkg.js +2 -2
  128. package/dist/commands/push.d.ts +2 -0
  129. package/dist/commands/push.js +79 -11
  130. package/dist/commands/revive.js +74 -1
  131. package/dist/commands/search/answer.d.ts +1 -0
  132. package/dist/commands/search/answer.js +50 -0
  133. package/dist/commands/search/contents.d.ts +1 -0
  134. package/dist/commands/search/contents.js +96 -0
  135. package/dist/commands/search/exa.d.ts +53 -0
  136. package/dist/commands/search/exa.js +155 -0
  137. package/dist/commands/search/puremd.d.ts +11 -0
  138. package/dist/commands/search/puremd.js +53 -0
  139. package/dist/commands/search/web.d.ts +1 -0
  140. package/dist/commands/search/web.js +65 -0
  141. package/dist/commands/search.d.ts +2 -0
  142. package/dist/commands/search.js +24 -0
  143. package/dist/commands/sys/__tests__/sync-import.test.d.ts +1 -0
  144. package/dist/commands/sys/__tests__/sync-import.test.js +72 -0
  145. package/dist/commands/sys/config.js +59 -10
  146. package/dist/commands/sys/doctor.js +56 -4
  147. package/dist/commands/sys/prompt-review.d.ts +1 -0
  148. package/dist/commands/sys/prompt-review.js +178 -0
  149. package/dist/commands/sys/promptstudio.d.ts +2 -0
  150. package/dist/commands/sys/promptstudio.js +65 -0
  151. package/dist/commands/sys/settings.d.ts +2 -0
  152. package/dist/commands/sys/settings.js +16 -0
  153. package/dist/commands/sys/sync.js +311 -159
  154. package/dist/commands/sys/sysprompt.d.ts +1 -0
  155. package/dist/commands/sys/sysprompt.js +86 -0
  156. package/dist/commands/sys.js +9 -5
  157. package/dist/commands/view-new.js +2 -2
  158. package/dist/core/__tests__/artifact-paths.test.d.ts +1 -0
  159. package/dist/core/__tests__/artifact-paths.test.js +44 -0
  160. package/dist/core/__tests__/broker-preflight.test.d.ts +1 -0
  161. package/dist/core/__tests__/broker-preflight.test.js +85 -0
  162. package/dist/core/__tests__/broker-sdk-wiring.test.js +15 -0
  163. package/dist/core/__tests__/canvas.test.js +37 -1
  164. package/dist/core/__tests__/chat-view-reconnect.test.d.ts +1 -0
  165. package/dist/core/__tests__/chat-view-reconnect.test.js +70 -0
  166. package/dist/core/__tests__/child-death-wake.test.js +11 -2
  167. package/dist/core/__tests__/close.test.js +39 -1
  168. package/dist/core/__tests__/connection-reconnect-resume.test.d.ts +1 -0
  169. package/dist/core/__tests__/connection-reconnect-resume.test.js +37 -0
  170. package/dist/core/__tests__/context-intro.test.js +48 -14
  171. package/dist/core/__tests__/daemon-boot.test.js +182 -9
  172. package/dist/core/__tests__/editor-label.test.d.ts +1 -0
  173. package/dist/core/__tests__/editor-label.test.js +26 -0
  174. package/dist/core/__tests__/fault-marker.test.d.ts +1 -0
  175. package/dist/core/__tests__/fault-marker.test.js +112 -0
  176. package/dist/core/__tests__/fault-retry-rewind.test.d.ts +1 -0
  177. package/dist/core/__tests__/fault-retry-rewind.test.js +123 -0
  178. package/dist/core/__tests__/fixtures/fake-engine.d.ts +30 -4
  179. package/dist/core/__tests__/fixtures/fake-engine.js +156 -17
  180. package/dist/core/__tests__/fixtures/fake-pi-host.js +10 -1
  181. package/dist/core/__tests__/full/human-new-window-regression.test.js +1 -1
  182. package/dist/core/__tests__/helpers/harness.d.ts +1 -1
  183. package/dist/core/__tests__/helpers/harness.js +9 -3
  184. package/dist/core/__tests__/human-stranded-deliver.test.js +38 -1
  185. package/dist/core/__tests__/human-surface-target.test.js +1 -1
  186. package/dist/core/__tests__/kickoff.test.js +18 -19
  187. package/dist/core/__tests__/memory-resolver.test.js +12 -1
  188. package/dist/core/__tests__/migration.test.js +49 -14
  189. package/dist/core/__tests__/model-ladders.test.d.ts +1 -0
  190. package/dist/core/__tests__/model-ladders.test.js +160 -0
  191. package/dist/core/__tests__/node-env.test.d.ts +1 -0
  192. package/dist/core/__tests__/node-env.test.js +26 -0
  193. package/dist/core/__tests__/push-final-guard.test.js +7 -1
  194. package/dist/core/__tests__/relaunch-root.test.js +9 -9
  195. package/dist/core/__tests__/revive.test.js +36 -6
  196. package/dist/core/__tests__/spawn-root.test.js +24 -1
  197. package/dist/core/__tests__/stop-guard.test.js +10 -0
  198. package/dist/core/__tests__/stranded-relaunch.test.d.ts +1 -0
  199. package/dist/core/__tests__/stranded-relaunch.test.js +72 -0
  200. package/dist/core/__tests__/tmux-surface.test.js +5 -1
  201. package/dist/core/__tests__/unknown-path.test.js +19 -19
  202. package/dist/core/__tests__/yield-ensures-daemon.test.d.ts +1 -0
  203. package/dist/core/__tests__/yield-ensures-daemon.test.js +54 -0
  204. package/dist/core/artifact.d.ts +2 -33
  205. package/dist/core/artifact.js +27 -87
  206. package/dist/core/bootstrap.js +2 -0
  207. package/dist/core/canvas/browse/__tests__/model.test.js +35 -1
  208. package/dist/core/canvas/browse/app.js +87 -17
  209. package/dist/core/canvas/browse/model.d.ts +10 -1
  210. package/dist/core/canvas/browse/model.js +37 -1
  211. package/dist/core/canvas/browse/render.js +35 -33
  212. package/dist/core/canvas/canvas.d.ts +7 -0
  213. package/dist/core/canvas/canvas.js +54 -5
  214. package/dist/core/canvas/db.js +35 -8
  215. package/dist/core/canvas/labels.d.ts +6 -8
  216. package/dist/core/canvas/labels.js +8 -11
  217. package/dist/core/canvas/nav-model.d.ts +5 -4
  218. package/dist/core/canvas/nav-model.js +31 -17
  219. package/dist/core/canvas/paths.d.ts +18 -1
  220. package/dist/core/canvas/paths.js +37 -2
  221. package/dist/core/canvas/render.d.ts +25 -0
  222. package/dist/core/canvas/render.js +96 -13
  223. package/dist/core/canvas/status-glyph.d.ts +35 -0
  224. package/dist/core/canvas/status-glyph.js +104 -0
  225. package/dist/core/canvas/types.d.ts +2 -2
  226. package/dist/core/config.js +114 -16
  227. package/dist/core/fault-classifier.d.ts +41 -0
  228. package/dist/core/fault-classifier.js +110 -0
  229. package/dist/core/feed/__tests__/empty-node-guard.test.d.ts +1 -0
  230. package/dist/core/feed/__tests__/empty-node-guard.test.js +45 -0
  231. package/dist/core/feed/feed.js +8 -0
  232. package/dist/core/feed/inbox.d.ts +15 -0
  233. package/dist/core/feed/inbox.js +40 -14
  234. package/dist/core/frontmatter.d.ts +1 -11
  235. package/dist/core/frontmatter.js +1 -55
  236. package/dist/core/hearth/config.d.ts +2 -0
  237. package/dist/core/hearth/config.js +75 -0
  238. package/dist/core/hearth/guest-env.d.ts +2 -0
  239. package/dist/core/hearth/guest-env.js +11 -0
  240. package/dist/core/hearth/index.d.ts +5 -0
  241. package/dist/core/hearth/index.js +5 -0
  242. package/dist/core/hearth/provider.d.ts +21 -0
  243. package/dist/core/hearth/provider.js +10 -0
  244. package/dist/core/hearth/providers/blaxel-bootstrap.d.ts +10 -0
  245. package/dist/core/hearth/providers/blaxel-bootstrap.js +39 -0
  246. package/dist/core/hearth/providers/blaxel-home.d.ts +11 -0
  247. package/dist/core/hearth/providers/blaxel-home.js +126 -0
  248. package/dist/core/hearth/providers/blaxel.d.ts +26 -0
  249. package/dist/core/hearth/providers/blaxel.js +208 -0
  250. package/dist/core/hearth/providers/types.d.ts +72 -0
  251. package/dist/core/hearth/providers/types.js +1 -0
  252. package/dist/core/hearth/registry.d.ts +15 -0
  253. package/dist/core/hearth/registry.js +179 -0
  254. package/dist/core/hearth/types.d.ts +121 -0
  255. package/dist/core/hearth/types.js +1 -0
  256. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.d.ts +1 -0
  257. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.js +79 -0
  258. package/dist/core/{skill-sync → host-exports}/builtins.d.ts +21 -13
  259. package/dist/core/host-exports/builtins.js +68 -0
  260. package/dist/core/{skill-sync → host-exports}/export.d.ts +9 -6
  261. package/dist/core/{skill-sync → host-exports}/export.js +37 -14
  262. package/dist/core/io.js +1 -1
  263. package/dist/core/log.d.ts +9 -0
  264. package/dist/core/log.js +113 -0
  265. package/dist/core/memory-resolver.d.ts +8 -8
  266. package/dist/core/memory-resolver.js +15 -28
  267. package/dist/core/personas/index.d.ts +4 -4
  268. package/dist/core/personas/index.js +2 -2
  269. package/dist/core/personas/loader.d.ts +51 -0
  270. package/dist/core/personas/loader.js +54 -50
  271. package/dist/core/personas/resolve.d.ts +43 -27
  272. package/dist/core/personas/resolve.js +336 -94
  273. package/dist/core/runtime/auth-reload.d.ts +7 -0
  274. package/dist/core/runtime/auth-reload.js +69 -0
  275. package/dist/core/runtime/bearings.d.ts +35 -15
  276. package/dist/core/runtime/bearings.js +305 -44
  277. package/dist/core/runtime/branded-host.d.ts +4 -2
  278. package/dist/core/runtime/branded-host.js +66 -4
  279. package/dist/core/runtime/broker-protocol.d.ts +38 -3
  280. package/dist/core/runtime/broker.js +343 -18
  281. package/dist/core/runtime/close.js +37 -29
  282. package/dist/core/runtime/connectivity.d.ts +12 -0
  283. package/dist/core/runtime/connectivity.js +73 -0
  284. package/dist/core/runtime/fault-recovery-nudge.d.ts +2 -0
  285. package/dist/core/runtime/fault-recovery-nudge.js +2 -0
  286. package/dist/core/runtime/fault-recovery.d.ts +23 -0
  287. package/dist/core/runtime/fault-recovery.js +74 -0
  288. package/dist/core/runtime/fault.d.ts +25 -0
  289. package/dist/core/runtime/fault.js +176 -0
  290. package/dist/core/runtime/front-door.d.ts +1 -1
  291. package/dist/core/runtime/front-door.js +2 -2
  292. package/dist/core/runtime/host.d.ts +10 -0
  293. package/dist/core/runtime/host.js +71 -9
  294. package/dist/core/runtime/kickoff.js +31 -45
  295. package/dist/core/runtime/launch.d.ts +32 -18
  296. package/dist/core/runtime/launch.js +148 -43
  297. package/dist/core/runtime/model-swap.d.ts +18 -0
  298. package/dist/core/runtime/model-swap.js +95 -0
  299. package/dist/core/runtime/naming.d.ts +7 -0
  300. package/dist/core/runtime/naming.js +61 -9
  301. package/dist/core/runtime/nodes.js +6 -1
  302. package/dist/core/runtime/persona.js +16 -17
  303. package/dist/core/runtime/placement.d.ts +36 -31
  304. package/dist/core/runtime/placement.js +149 -64
  305. package/dist/core/runtime/promote.d.ts +2 -0
  306. package/dist/core/runtime/promote.js +23 -3
  307. package/dist/core/runtime/recycle.js +6 -4
  308. package/dist/core/runtime/reset.d.ts +5 -2
  309. package/dist/core/runtime/reset.js +2 -2
  310. package/dist/core/runtime/revive.d.ts +4 -0
  311. package/dist/core/runtime/revive.js +27 -9
  312. package/dist/core/runtime/roadmap.d.ts +1 -1
  313. package/dist/core/runtime/roadmap.js +1 -1
  314. package/dist/core/runtime/spawn.d.ts +2 -2
  315. package/dist/core/runtime/spawn.js +30 -6
  316. package/dist/core/runtime/stop-guard.js +6 -1
  317. package/dist/core/runtime/tmux-chrome.d.ts +1 -1
  318. package/dist/core/runtime/tmux-chrome.js +1 -1
  319. package/dist/core/runtime/tmux.d.ts +29 -6
  320. package/dist/core/runtime/tmux.js +134 -80
  321. package/dist/core/runtime/view-socket-client.d.ts +46 -0
  322. package/dist/core/runtime/view-socket-client.js +203 -0
  323. package/dist/core/substrate/index.d.ts +1 -1
  324. package/dist/core/substrate/index.js +1 -1
  325. package/dist/core/substrate/on-read.js +16 -4
  326. package/dist/core/substrate/render.d.ts +10 -13
  327. package/dist/core/substrate/render.js +56 -51
  328. package/dist/core/tui/host.js +83 -18
  329. package/dist/core/view/__tests__/transport-cache.test.d.ts +1 -0
  330. package/dist/core/view/__tests__/transport-cache.test.js +62 -0
  331. package/dist/core/view/contract.d.ts +20 -0
  332. package/dist/core/view/stream-local.d.ts +3 -0
  333. package/dist/core/view/stream-local.js +184 -0
  334. package/dist/core/view/transport-cache.d.ts +8 -0
  335. package/dist/core/view/transport-cache.js +38 -0
  336. package/dist/core/view/transport-local.js +4 -0
  337. package/dist/core/view/transport.d.ts +7 -1
  338. package/dist/daemon/crtrd.d.ts +3 -19
  339. package/dist/daemon/crtrd.js +139 -178
  340. package/dist/daemon/manage.d.ts +18 -1
  341. package/dist/daemon/manage.js +54 -9
  342. package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.d.ts +1 -0
  343. package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.js +289 -0
  344. package/dist/hearth/wake-proxy/__tests__/config-timeout.test.d.ts +1 -0
  345. package/dist/hearth/wake-proxy/__tests__/config-timeout.test.js +34 -0
  346. package/dist/hearth/wake-proxy/__tests__/guest-source.test.d.ts +1 -0
  347. package/dist/hearth/wake-proxy/__tests__/guest-source.test.js +203 -0
  348. package/dist/hearth/wake-proxy/__tests__/hardening.test.d.ts +1 -0
  349. package/dist/hearth/wake-proxy/__tests__/hardening.test.js +59 -0
  350. package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.d.ts +1 -0
  351. package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.js +372 -0
  352. package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.d.ts +1 -0
  353. package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.js +258 -0
  354. package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.d.ts +1 -0
  355. package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.js +437 -0
  356. package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.d.ts +1 -0
  357. package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.js +455 -0
  358. package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.d.ts +1 -0
  359. package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.js +15 -0
  360. package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.d.ts +1 -0
  361. package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.js +141 -0
  362. package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.d.ts +1 -0
  363. package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.js +143 -0
  364. package/dist/hearth/wake-proxy/anthropic-oauth.d.ts +58 -0
  365. package/dist/hearth/wake-proxy/anthropic-oauth.js +189 -0
  366. package/dist/hearth/wake-proxy/auth.d.ts +22 -0
  367. package/dist/hearth/wake-proxy/auth.js +128 -0
  368. package/dist/hearth/wake-proxy/config.d.ts +2 -0
  369. package/dist/hearth/wake-proxy/config.js +151 -0
  370. package/dist/hearth/wake-proxy/guest-source.d.ts +14 -0
  371. package/dist/hearth/wake-proxy/guest-source.js +130 -0
  372. package/dist/hearth/wake-proxy/hearth-status.d.ts +44 -0
  373. package/dist/hearth/wake-proxy/hearth-status.js +267 -0
  374. package/dist/hearth/wake-proxy/home.d.ts +67 -0
  375. package/dist/hearth/wake-proxy/home.js +297 -0
  376. package/dist/hearth/wake-proxy/main.d.ts +1 -0
  377. package/dist/hearth/wake-proxy/main.js +134 -0
  378. package/dist/hearth/wake-proxy/model-auth.d.ts +13 -0
  379. package/dist/hearth/wake-proxy/model-auth.js +345 -0
  380. package/dist/hearth/wake-proxy/proxy.d.ts +35 -0
  381. package/dist/hearth/wake-proxy/proxy.js +716 -0
  382. package/dist/hearth/wake-proxy/public-source-gate.d.ts +9 -0
  383. package/dist/hearth/wake-proxy/public-source-gate.js +409 -0
  384. package/dist/hearth/wake-proxy/redact.d.ts +1 -0
  385. package/dist/hearth/wake-proxy/redact.js +17 -0
  386. package/dist/hearth/wake-proxy/server.d.ts +11 -0
  387. package/dist/hearth/wake-proxy/server.js +142 -0
  388. package/dist/hearth/wake-proxy/state.d.ts +18 -0
  389. package/dist/hearth/wake-proxy/state.js +342 -0
  390. package/dist/hearth/wake-proxy/types.d.ts +76 -0
  391. package/dist/hearth/wake-proxy/types.js +1 -0
  392. package/dist/index.js +3 -2
  393. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +6 -5
  394. package/dist/pi-extensions/canvas-context-intro.js +7 -7
  395. package/dist/pi-extensions/canvas-doc-substrate.js +19 -22
  396. package/dist/pi-extensions/canvas-nav.js +15 -8
  397. package/dist/pi-extensions/canvas-stophook.d.ts +1 -1
  398. package/dist/pi-extensions/canvas-stophook.js +78 -11
  399. package/dist/types.d.ts +39 -20
  400. package/dist/types.js +38 -13
  401. package/dist/web/runtime.js +141 -42
  402. package/dist/web/transport-http.js +7 -5
  403. package/dist/web/transport-stream.d.ts +3 -0
  404. package/dist/web/transport-stream.js +204 -0
  405. package/dist/web-client/assets/fragment-mono-latin-400-normal-BYwT3kSJ.woff +0 -0
  406. package/dist/web-client/assets/fragment-mono-latin-400-normal-yxdJ5AmL.woff2 +0 -0
  407. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-BbKYyvR9.woff2 +0 -0
  408. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-CT4YFKeK.woff +0 -0
  409. package/dist/web-client/assets/fraunces-latin-ext-wght-normal-Ca2vKHc0.woff2 +0 -0
  410. package/dist/web-client/assets/fraunces-latin-wght-normal-ukD16Tqj.woff2 +0 -0
  411. package/dist/web-client/assets/fraunces-vietnamese-wght-normal-CnvboYUG.woff2 +0 -0
  412. package/dist/web-client/assets/index-BZUxTkv5.css +2 -0
  413. package/dist/web-client/assets/index-D36PNBj4.js +80 -0
  414. package/dist/web-client/assets/instrument-sans-latin-ext-wght-normal-B5bTHO_g.woff2 +0 -0
  415. package/dist/web-client/assets/instrument-sans-latin-wght-normal-BbzFLZTg.woff2 +0 -0
  416. package/dist/web-client/assets/martian-mono-cyrillic-wght-normal-B84CD5C_.woff2 +0 -0
  417. package/dist/web-client/assets/martian-mono-latin-ext-wght-normal-DlL6xMw5.woff2 +0 -0
  418. package/dist/web-client/assets/martian-mono-latin-wght-normal-5W32yIyr.woff2 +0 -0
  419. package/dist/web-client/index.html +3 -2
  420. package/package.json +32 -10
  421. package/dist/core/__tests__/error-stall-recycle.test.js +0 -141
  422. package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.js +0 -57
  423. package/dist/core/skill-sync/builtins.js +0 -112
  424. package/dist/core/skill-sync/claude-plugins.d.ts +0 -23
  425. package/dist/core/skill-sync/claude-plugins.js +0 -71
  426. package/dist/core/skill-sync/engine.d.ts +0 -42
  427. package/dist/core/skill-sync/engine.js +0 -633
  428. package/dist/core/skill-sync/manifest.d.ts +0 -64
  429. package/dist/core/skill-sync/manifest.js +0 -181
  430. package/dist/core/skill-sync/profile.d.ts +0 -76
  431. package/dist/core/skill-sync/profile.js +0 -173
  432. package/dist/core/skill-sync/snapshot.d.ts +0 -57
  433. package/dist/core/skill-sync/snapshot.js +0 -120
  434. package/dist/pi-extensions/canvas-commands.d.ts +0 -37
  435. package/dist/pi-extensions/canvas-commands.js +0 -113
  436. package/dist/pi-extensions/canvas-resume.d.ts +0 -21
  437. package/dist/pi-extensions/canvas-resume.js +0 -83
  438. package/dist/pi-extensions/canvas-view.d.ts +0 -21
  439. package/dist/pi-extensions/canvas-view.js +0 -76
  440. package/dist/web-client/assets/index-BUvQb4hR.css +0 -2
  441. package/dist/web-client/assets/index-ClLQXYAE.js +0 -10
  442. /package/dist/{core/__tests__/error-stall-recycle.test.d.ts → clients/attach/__tests__/autocomplete-and-bash-mode.test.d.ts} +0 -0
  443. /package/dist/{core/skill-sync/__tests__/dry-run-wrote-count.test.d.ts → clients/attach/__tests__/bash-bang-routing.test.d.ts} +0 -0
@@ -8,7 +8,7 @@ export const DECK_SCHEMA_HINT = 'Deck must match the humanloop deck schema: {tit
8
8
  'source?:{sessionName?,askedBy?,blockedSince?}, ' +
9
9
  'interactions:[{id,title,subtitle?,(body?|bodyPath?),options:[{id,label,' +
10
10
  'description?}],multiSelect?,allowFreetext?,freetextLabel?,' +
11
- "kind?:'notify'|'validation'|'decision'|'context'|'error'}]}.";
11
+ "kind?:'notify'|'decision'|'context'|'error'}]}.";
12
12
  export function resolveMaxPanes() {
13
13
  return readConfig('user').max_panes_per_window;
14
14
  }
@@ -42,7 +42,7 @@ export function resolveHumanTarget() {
42
42
  /**
43
43
  * Open the detached humanloop `_run` pane for an interaction dir, ROUTED to the
44
44
  * session the user is watching (`resolveHumanTarget`). The single spawn path
45
- * behind ask/approve/review (`spawnHumanJob`) and notify. `detached: true` keeps
45
+ * behind ask/review (`spawnHumanJob`) and notify. `detached: true` keeps
46
46
  * the user's view put — the prompt lands beside the watched node without
47
47
  * jumping their session/window. `jobId`, when given, is injected as CRTR_JOB_ID.
48
48
  */
@@ -90,7 +90,7 @@ export function followUpReview(_jobId) {
90
90
  *
91
91
  * Completion routing needs no bookkeeping here: the human node was created
92
92
  * under the asking node as its parent (spawnNode auto-subscribes the parent),
93
- * so the `pushFinal` the `_run` worker emits — for ask, approve, AND review —
93
+ * so the `pushFinal` the `_run` worker emits — for ask AND review —
94
94
  * fans the answer straight into the asking node's inbox. The pane id is recorded
95
95
  * on run.json (not returned) so `human cancel` can later kill the TUI.
96
96
  */
@@ -1,41 +1,42 @@
1
1
  // `crtr human` subtree — in-process humanloop bridge.
2
2
  //
3
- // Kickoff leaves (ask/approve/review) create a kind:'human' node under the
3
+ // Kickoff leaves (ask/review) create a kind:'human' node under the
4
4
  // asking node, write deck.json/run.json into the per-cwd interaction dir, spawn
5
5
  // a detached `crtr human _run` pane, and return immediately. The human's answer
6
6
  // is pushed as the node's final report, which fans out to the asking node's
7
7
  // inbox — no polling surface. notify/show create no node. _run runs the blocking
8
8
  // humanloop call at the pane TTY and pushes the result itself.
9
9
  //
10
- // TTY safety: every leaf is argv-only none declares a stdin parameter, so
11
- // the spawned pane's TTY stays free for humanloop's raw-mode input. Control
12
- // params travel via CRTR_HUMAN_DIR (set inline in the spawned command) +
13
- // run.json, never stdin.
10
+ // TTY safety: kickoff/TUI leaves are argv-only, so the spawned pane's TTY stays
11
+ // free for humanloop's raw-mode input. Control params travel via CRTR_HUMAN_DIR
12
+ // (set inline in the spawned command) + run.json, never stdin. The noninteractive
13
+ // resolve leaf is the only stdin-taking child and does not spawn a TUI pane.
14
14
  import { defineBranch } from '../core/command.js';
15
- import { humanAsk, humanApprove, humanReview, humanNotify, humanShow } from './human/prompts.js';
16
- import { humanInbox, humanList, humanCancel, humanRun } from './human/queue.js';
15
+ import { humanAsk, humanReview, humanNotify, humanShow } from './human/prompts.js';
16
+ import { humanInbox, humanList, humanDeck, humanResolve, humanCancel, humanRun } from './human/queue.js';
17
17
  export function registerHuman() {
18
18
  return defineBranch({
19
19
  name: 'human',
20
20
  rootEntry: {
21
21
  concept: 'human-in-the-loop decisions, document review, and live display',
22
- desc: 'ask, approve, review, notify, show, cancel, inbox, list',
22
+ desc: 'ask, review, notify, show, cancel, inbox, list',
23
23
  useWhen: 'you have a question for the user, want their feedback, or are presenting them with options or a choice — always reach for human instead of guessing or assuming, or laying a choice out as prose, when a person can decide; and whenever the user wants to review a spec, plan, or requirements document, this is the command (use `human review` for anchored line-by-line comments)'
24
24
  },
25
25
  help: {
26
26
  name: 'human',
27
27
  summary: 'human-in-the-loop decisions, document review, and live display',
28
- model: "Every body and displayed file is directive-flavored markdown rendered by termrender (panels, columns, trees, callouts, mermaid) — see `termrender doc -h` for the directive set before authoring one. ask, approve, and review are kickoffs: they create a kind:'human' node under you and return instantly, never blocking — the human's response is pushed to your inbox when they answer, so keep working (or just end your turn) and you'll be woken with it. review puts a .md live on the human's screen for anchored comments and tracks the file, so edit in place rather than canceling and re-presenting. notify and show create no node.",
28
+ model: "Every body and displayed file is directive-flavored markdown rendered by termrender (panels, columns, trees, callouts, mermaid) — see `termrender doc -h` for the directive set before authoring one. ask and review are kickoffs: they create a kind:'human' node under you and return instantly, never blocking — the human's response is pushed to your inbox when they answer, so keep working (or just end your turn) and you'll be woken with it. ask covers everything from a yes/no sign-off gate (two options) to an open-ended judgment call; review puts a .md live on the human's screen for anchored comments and tracks the file, so edit in place rather than canceling and re-presenting. notify and show create no node.",
29
29
  },
30
30
  children: [
31
31
  humanAsk,
32
- humanApprove,
33
32
  humanReview,
34
33
  humanNotify,
35
34
  humanShow,
36
35
  humanCancel,
37
36
  humanInbox,
38
37
  humanList,
38
+ humanDeck,
39
+ humanResolve,
39
40
  humanRun,
40
41
  ],
41
42
  });
@@ -57,7 +57,7 @@ export const findLeaf = defineLeaf({
57
57
  ],
58
58
  output: [
59
59
  { name: 'query', type: 'string', required: true, constraint: 'Echo of the input query.' },
60
- { name: 'hits', type: 'object[]', required: true, constraint: 'Ranked mode — each: {name, kind, scope, score, short_form}, sorted by score descending. Under --grep instead — each: {path, line, text} for every body line matching the regex, sorted by path then line ascending.' },
60
+ { name: 'hits', type: 'object[]', required: true, constraint: 'Ranked mode — each: {name, kind, scope, path, score, short_form}, sorted by score descending; path is the absolute file on disk — edit it directly to tweak the doc. Under --grep instead — each: {path, line, text} for every body line matching the regex, sorted by path then line ascending.' },
61
61
  { name: 'follow_up', type: 'string', required: true, constraint: 'Concrete next commands for reading a hit in full or refining the search.' },
62
62
  ],
63
63
  outputKind: 'object',
@@ -133,10 +133,11 @@ export const findLeaf = defineLeaf({
133
133
  name: h.unit.name,
134
134
  kind: h.unit.kind,
135
135
  scope: h.unit.scope,
136
+ path: h.unit.path,
136
137
  score: h.score,
137
138
  short_form: h.unit.shortForm,
138
139
  })),
139
- follow_up: 'Read a hit in full with `crtr memory read <name>`. Add --body to weigh body text, or --grep for an exact regex.',
140
+ follow_up: 'Read a hit in full with `crtr memory read <name>`, or edit its `path` directly to tweak it. Add --body to weigh body text, or --grep for an exact regex.',
140
141
  };
141
142
  },
142
143
  });
@@ -14,7 +14,7 @@ export const listLeaf = defineLeaf({
14
14
  { kind: 'flag', name: 'scope', type: 'enum', choices: [...MEMORY_SCOPES], required: false, constraint: 'Filter to a single scope. Default: all resolved scopes.' },
15
15
  ],
16
16
  output: [
17
- { name: 'items', type: 'object[]', required: true, constraint: 'One row per document. Each: {name, title, short_form, kind, scope, is_dir}. A directory INDEX.md surfaces as a dir entry (is_dir true, name = the directory path with a trailing slash); is_dir is false for an ordinary doc. short_form is the abbreviated hook — shown here and nowhere else. Sorted by scope then kind then name ascending.' },
17
+ { name: 'items', type: 'object[]', required: true, constraint: 'One row per document. Each: {name, title, short_form, kind, scope, path, is_dir}. path is the absolute file on disk — edit it directly to tweak the doc. A directory INDEX.md surfaces as a dir entry (is_dir true, name = the directory path with a trailing slash, path = its INDEX.md); is_dir is false for an ordinary doc. short_form is the abbreviated hook — shown here and nowhere else. Sorted by scope then kind then name ascending.' },
18
18
  { name: 'follow_up', type: 'string', required: true, constraint: 'Concrete next commands — read a document in full or narrow the inventory.' },
19
19
  ],
20
20
  outputKind: 'object',
@@ -43,7 +43,7 @@ export const listLeaf = defineLeaf({
43
43
  // slash, flagged is_dir, so the inventory distinguishes it from a doc.
44
44
  const isDir = isIndexName(sub.name);
45
45
  const name = isDir ? indexDirOf(sub.name) + '/' : sub.name;
46
- addItem({ name, kind: sub.kind, scope: sub.scope, shortForm: sub.shortForm, isDir });
46
+ addItem({ name, kind: sub.kind, scope: sub.scope, path: sub.path, shortForm: sub.shortForm, isDir });
47
47
  }
48
48
  items.sort((a, b) => {
49
49
  const sr = scopeRank(a.scope) - scopeRank(b.scope);
@@ -61,9 +61,10 @@ export const listLeaf = defineLeaf({
61
61
  short_form: d.shortForm,
62
62
  kind: d.kind,
63
63
  scope: d.scope,
64
+ path: d.path,
64
65
  is_dir: d.isDir,
65
66
  })),
66
- follow_up: 'Read one in full with `crtr memory read <name>`. Narrow with --kind / --scope, or search a topic with `crtr memory find <query>`.',
67
+ follow_up: 'Read one in full with `crtr memory read <name>`, or edit its `path` directly to tweak it. Narrow with --kind / --scope, or search a topic with `crtr memory find <query>`.',
67
68
  };
68
69
  },
69
70
  });
@@ -7,7 +7,7 @@ import { MEMORY_KINDS } from './shared.js';
7
7
  export const readLeaf = defineLeaf({
8
8
  name: 'read',
9
9
  description: 'load a memory document body by name',
10
- whenToUse: 'a task in front of you matches a stored document and you already know its name — read it before improvising. Resolves the path-derived name across scopes by precedence (project > user > builtin), with leaf-name fallback like skills. You name the document by its crtr identifier, never a file path — do not cat or find the markdown off disk. Reach for `crtr memory find` first when you do not yet know which document applies.',
10
+ whenToUse: 'a task in front of you matches a stored document and you already know its name — read it before improvising. Resolves the path-derived name across scopes by precedence (project > user > builtin), with leaf-name fallback. You name the document by its crtr identifier, never a file path — do not cat or find the markdown off disk. Reach for `crtr memory find` first when you do not yet know which document applies.',
11
11
  help: {
12
12
  name: 'memory read',
13
13
  summary: 'resolve a path-derived name to its document body, frontmatter stripped unless --frontmatter',
@@ -20,7 +20,7 @@ export const readLeaf = defineLeaf({
20
20
  { name: 'name', type: 'string', required: true, constraint: 'Resolved document name.' },
21
21
  { name: 'kind', type: 'string', required: true, constraint: 'Resolved kind: knowledge or preference.' },
22
22
  { name: 'scope', type: 'string', required: true, constraint: 'Scope the document was resolved from: project, user, or builtin.' },
23
- { name: 'path', type: 'string', required: true, constraint: 'Absolute path to the document on disk.' },
23
+ { name: 'path', type: 'string', required: true, constraint: 'Absolute path to the document on disk — edit this file directly to tweak the doc in place.' },
24
24
  { name: 'content', type: 'string', required: true, constraint: 'Document body. Frontmatter stripped unless --frontmatter is set.' },
25
25
  { name: 'follow_up', type: 'string', required: true, constraint: 'Hints at variant flags or next commands.' },
26
26
  ],
@@ -58,7 +58,7 @@ export const readLeaf = defineLeaf({
58
58
  scope: doc.scope,
59
59
  path: doc.path,
60
60
  content,
61
- follow_up: 'Add --frontmatter to include the YAML header. Browse the inventory with `crtr memory list`.',
61
+ follow_up: 'Read the raw file at `path` to view the YAML frontmatter or edit this memory. Browse the inventory with `crtr memory list`.',
62
62
  };
63
63
  }
64
64
  }
@@ -29,8 +29,5 @@ export declare function coerceAppliesTo(raw: string): unknown;
29
29
  /** Serialize a substrate frontmatter record + body into a complete `.md`
30
30
  * document. Frontmatter is emitted as a `---` fenced YAML block (the `yaml`
31
31
  * package — the same one the parser uses — so nested gate maps and applies-to
32
- * arrays round-trip), in canonical field order with preserved extras last. The
33
- * skill-shaped `serializeFrontmatter` in core can only represent
34
- * name/description/type/keywords, so it cannot carry the substrate fields —
35
- * hence this focused serializer. */
32
+ * arrays round-trip), in canonical field order with preserved extras last. */
36
33
  export declare function serializeMemoryDoc(frontmatter: Record<string, unknown>, body: string): string;
@@ -1,8 +1,8 @@
1
- // Shared constants + helpers for the `crtr memory` command family (task B2).
2
- // The leaf handlers (list/read/find/write) consume the resolver, substrate
3
- // schema, scope, and skill-resolver modules and build their documented output
4
- // objects on top of the small helpers here. Nothing in this file forks on kind
5
- // or re-implements the schema/gate/resolver — it only composes them.
1
+ // Shared constants + helpers for the `crtr memory` command family. The leaf
2
+ // handlers (list/read/find/write) consume the resolver, substrate schema, and
3
+ // scope modules and build their documented output objects on top of the small
4
+ // helpers here. Nothing in this file forks on kind or re-implements the
5
+ // schema/gate/resolver — it only composes them.
6
6
  import { join } from 'node:path';
7
7
  import { stringify as yamlStringify, parse as yamlParse } from 'yaml';
8
8
  import { usage } from '../../core/errors.js';
@@ -116,10 +116,7 @@ const FRONTMATTER_ORDER = [
116
116
  /** Serialize a substrate frontmatter record + body into a complete `.md`
117
117
  * document. Frontmatter is emitted as a `---` fenced YAML block (the `yaml`
118
118
  * package — the same one the parser uses — so nested gate maps and applies-to
119
- * arrays round-trip), in canonical field order with preserved extras last. The
120
- * skill-shaped `serializeFrontmatter` in core can only represent
121
- * name/description/type/keywords, so it cannot carry the substrate fields —
122
- * hence this focused serializer. */
119
+ * arrays round-trip), in canonical field order with preserved extras last. */
123
120
  export function serializeMemoryDoc(frontmatter, body) {
124
121
  const ordered = {};
125
122
  for (const key of FRONTMATTER_ORDER) {
@@ -12,7 +12,7 @@ export const writeLeaf = defineLeaf({
12
12
  summary: 'create or update memory/<name>.md at the resolved scope from frontmatter flags + a stdin body',
13
13
  guide: 'The body is the easy part; the craft is ROUTING — every frontmatter flag decides who sees this doc, when, and at what context cost. Each rung up is paid by every future agent at every boot or read, forever, so default each rung DOWN.\n\n' +
14
14
  'Pick the kind. knowledge = anything you CONSULT \u2014 how to DO something (a repeatable procedure/playbook) OR what is TRUE / how something WORKS (a fact about the user, a system\u2019s behavior, code docs); preference = how to BEHAVE (a directive, a standing correction). The test that splits them: does it DIRECT behavior ("always lint after authoring" \u2192 preference) or INFORM what you know ("Silas likes chicken", "the daemon never reloads dist/" \u2192 knowledge)? A correction yields a preference; a learned fact or a repeatable procedure yields knowledge.\n\n' +
15
- 'Set the rungs \u2014 BOTH --system-prompt-visibility (boot) and --file-read-visibility (on-read) are REQUIRED on create. There is no default: the right rung is a case-by-case call, never a function of kind. The ladder, lowest to highest: `none` for niche docs almost nothing should pull into context; `name` for the common case \u2014 references and skills that are relatively uncommon but an agent (or the user, by name) may reach for; `preview` for docs important enough that their routing line is worth its token cost every session (preferences usually land here); `content` (full body injected) for a doc that would be `preview` except its body is already so short you may as well inline it whole. `content` is rare \u2014 when a `content` doc grows past a bullet or two, downgrade it to `preview`. Each axis is independent: usually one carries a real rung and the other is `none` (see the hook paragraph next).\n\n' +
15
+ 'Set the rungs \u2014 BOTH --system-prompt-visibility (boot) and --file-read-visibility (on-read) are REQUIRED on create. There is no default: the right rung is a case-by-case call, never a function of kind. The ladder, lowest to highest: `none` for niche docs almost nothing should pull into context; `name` for the common case \u2014 knowledge docs that are relatively uncommon but an agent (or the user, by name) may reach for; `preview` for docs important enough that their routing line is worth its token cost every session (preferences usually land here); `content` (full body injected) for a doc that would be `preview` except its body is already so short you may as well inline it whole. `content` is rare \u2014 when a `content` doc grows past a bullet or two, downgrade it to `preview`. Each axis is independent: usually one carries a real rung and the other is `none` (see the hook paragraph next).\n\n' +
16
16
  'Choose the hook \u2014 boot vs file-read. There are exactly two moments a doc can surface. Behavior (preferences) and how-to procedure are relevant whatever file is open \u2192 surface at boot. Knowledge about code belongs NEXT TO the code: put the file in that directory\u2019s .crouter/memory/ and it fires positionally when files there are read, costing nothing at boot. The exception that matters: a knowledge doc about a PERSON or PROCESS has no code directory to anchor to, so on-read triggering is meaningless \u2014 set --system-prompt-visibility preview so its routing line surfaces at boot instead.\n\n' +
17
17
  'Write the routing line (--when-and-why-to-read) FIRST, before storing anything: "When <circumstance the agent is in>, this <kind> should be read because <what the read buys>." The test: can a stranger mid-task decide from that one line alone whether to spend the read? If you cannot name the concrete situation that triggers it, you do not yet understand the memory \u2014 ask the user ONE sharp question instead of improvising. ("Remember I like chicken" routes cleanly \u2192 food/meal decisions; "be careful with the API" does not \u2192 which API, careful how, against what failure?) And NEVER paraphrase the advice in the routing line \u2014 a preview that gives away the gist makes every future agent skip the real read. BAD: "\u2026because it carries the placement policy \u2014 -h first, memory only for dev-mode material." GOOD: "\u2026because it carries the standing placement policy."\n\n' +
18
18
  'Find before write. `crtr memory find <topic>` first; grow ONE doc per recurring circumstance rather than minting near-duplicates \u2014 extend `food-preferences`, do not create `likes-chicken`. Group related docs with path names (area/topic). Do not store what is already recorded (code structure, git history, CLAUDE.md) or what only matters to this conversation.\n\n' +
@@ -35,7 +35,7 @@ export const writeLeaf = defineLeaf({
35
35
  { name: 'name', type: 'string', required: true, constraint: 'The path-derived document name written.' },
36
36
  { name: 'kind', type: 'string', required: true, constraint: 'Kind recorded in frontmatter.' },
37
37
  { name: 'scope', type: 'string', required: true, constraint: 'Scope the document was written to: user or project.' },
38
- { name: 'path', type: 'string', required: true, constraint: 'Absolute path to the written document.' },
38
+ { name: 'path', type: 'string', required: true, constraint: 'Absolute path to the written document — edit this file directly for later body tweaks instead of re-running write.' },
39
39
  { name: 'created', type: 'boolean', required: true, constraint: 'true when a new document was created, false when an existing one was updated in place.' },
40
40
  { name: 'follow_up', type: 'string', required: true, constraint: 'Concrete next commands — read it back or list the inventory.' },
41
41
  ],
@@ -97,7 +97,7 @@ export const writeLeaf = defineLeaf({
97
97
  scope,
98
98
  path,
99
99
  created,
100
- follow_up: `Read it back with \`crtr memory read ${name}\`, or browse the inventory with \`crtr memory list\`.`,
100
+ follow_up: `Read it back with \`crtr memory read ${name}\`, edit ${path} directly for later tweaks, or browse the inventory with \`crtr memory list\`.`,
101
101
  };
102
102
  },
103
103
  });
@@ -1,8 +1,5 @@
1
- // `crtr memory` subtree — the document substrate (knowledge and
2
- // preferences) accessed via the CLI. Four flat leaves: list, read, find, write.
3
- // SKELETON ONLY (task B1): the `-h` contracts here are final; the leaf handlers
4
- // are stubs that B2 fills in. Mirrors `skill.ts` minus the loaded-skills
5
- // catalog dynamicState.
1
+ // `crtr memory` subtree — the document substrate (knowledge and preferences)
2
+ // accessed via the CLI. Flat leaves: list, read, find, write, lint.
6
3
  import { defineBranch } from '../core/command.js';
7
4
  import { listLeaf } from './memory/list.js';
8
5
  import { readLeaf } from './memory/read.js';
@@ -15,12 +12,12 @@ export function registerMemory() {
15
12
  rootEntry: {
16
13
  concept: 'a memory document you read on demand — knowledge or a preference',
17
14
  desc: 'list, read, search, and write memory documents',
18
- useWhen: 'a task matches stored knowledge or a preference — read it before improvising. `crtr memory read <name>` loads one by name; `crtr memory list` browses the inventory; `crtr memory find` searches by topic when you do not yet know the name. Names are path-derived crtr identifiers, not file paths — never cat or find the markdown off disk.',
15
+ useWhen: 'a task matches stored knowledge or a preference — read it before improvising. `crtr memory read <name>` loads one by name; `crtr memory list` browses the inventory; `crtr memory find` searches by topic when you do not yet know the name. Names are path-derived crtr identifiers, not file paths — to READ a doc go through these commands, never cat or find the markdown off disk. To EDIT one in place, every command emits the doc’s absolute `path` — edit that file directly.',
19
16
  },
20
17
  help: {
21
18
  name: 'memory',
22
19
  summary: 'list, read, search, and write memory documents — knowledge and preferences',
23
- model: '`list` for a human inventory of what is stored — one line per document, the only surface that shows short-form. `read` (leaf) loads one document body by name, resolved project > user > builtin with leaf-name fallback; --frontmatter keeps the YAML header. `find` when you do not yet know which document applies — it ranks by relevance over name/when/why/short-form, --body to also weigh bodies, --grep for an exact regex over bodies. `write` creates or updates memory/<name>.md at a scope from frontmatter flags + a body piped on stdin. `lint` strict-parses the whole bounded corpus and fails on any invalid frontmatter — run it after authoring. A directory may carry an `INDEX.md` with the same frontmatter schema as any doc; the dir then renders as one entry at the INDEX\'s rung, and that rung is a ceiling for its whole subtree (`none` hides the dir) — when a doc mysteriously is not surfacing, check its ancestors\' INDEX rungs and its gate. Append `-h` at any leaf for its full schema, and `crtr memory write -h` for the authoring guide.',
20
+ model: '`list` for a human inventory of what is stored — one line per document, the only surface that shows short-form. `read` (leaf) loads one document body by name, resolved project > user > builtin with leaf-name fallback; --frontmatter keeps the YAML header. `find` when you do not yet know which document applies — it ranks by relevance over name/when/why/short-form, --body to also weigh bodies, --grep for an exact regex over bodies. `write` creates or updates memory/<name>.md at a scope from frontmatter flags + a body piped on stdin — use it for new docs and frontmatter changes; for a quick body tweak, edit the `path` every leaf emits directly. `lint` strict-parses the whole bounded corpus and fails on any invalid frontmatter — run it after authoring. A directory may carry an `INDEX.md` with the same frontmatter schema as any doc; the dir then renders as one entry at the INDEX\'s rung, and that rung is a ceiling for its whole subtree (`none` hides the dir) — when a doc mysteriously is not surfacing, check its ancestors\' INDEX rungs and its gate. Append `-h` at any leaf for its full schema, and `crtr memory write -h` for the authoring guide.',
24
21
  },
25
22
  children: [listLeaf, readLeaf, findLeaf, writeLeaf, lintLeaf],
26
23
  });
@@ -0,0 +1,2 @@
1
+ import type { LeafDef } from '../core/command.js';
2
+ export declare const nodeContextLeaf: LeafDef;
@@ -0,0 +1,172 @@
1
+ // `crtr node context` — browse this node's context and reports dirs and those of
2
+ // every node it subscribes to, in one read-only nvim file explorer.
3
+ //
4
+ // crtr makes a STRONG CLAIM that nvim is installed (see crouter/CLAUDE.md): the
5
+ // file explorer + file viewer are nvim, not a hand-rolled view-contract surface.
6
+ // We synthesize a temp dir of LABELLED NODE DIRS — `self` first, then one per
7
+ // subscription — and place `context` / `reports` symlinks inside each label dir
8
+ // for the node's existing on-disk dirs. We then open `nvim -R` on the temp tree
9
+ // (read-only: this is a viewer into siblings' state, edits never write back).
10
+ // The symlinks are torn down when nvim exits; the real node dirs are untouched.
11
+ //
12
+ // The /context popup (src/pi-extensions/canvas-context.ts) shells this leaf inside
13
+ // a tmux display-popup, exactly as /view shells `crtr view pick`. Piped / non-TTY
14
+ // it prints the resolved roster (node · path · file count) and exits 0.
15
+ import { spawnSync } from 'node:child_process';
16
+ import { existsSync, mkdirSync, mkdtempSync, readdirSync, rmSync, symlinkSync } from 'node:fs';
17
+ import { tmpdir } from 'node:os';
18
+ import { join } from 'node:path';
19
+ import { defineLeaf } from '../core/command.js';
20
+ import { InputError } from '../core/io.js';
21
+ import { getNode, subscriptionsOf } from '../core/canvas/index.js';
22
+ import { contextDir, nodeDir, reportsDir } from '../core/canvas/paths.js';
23
+ /** Make a node label safe as a directory name (netrw shows it verbatim). */
24
+ function sanitize(s) {
25
+ return s.replace(/[^A-Za-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'node';
26
+ }
27
+ /** Shallow file count for the roster (cheap; 0 when the dir is empty/absent). */
28
+ function countFiles(dir) {
29
+ try {
30
+ return readdirSync(dir).length;
31
+ }
32
+ catch {
33
+ return 0;
34
+ }
35
+ }
36
+ function nodeFiles(nodeId) {
37
+ return countFiles(contextDir(nodeId)) + countFiles(reportsDir(nodeId));
38
+ }
39
+ /** Resolve the current node + every node it subscribes to into browse roots,
40
+ * keeping any node whose `context/` or `reports/` dir exists on disk. `self`
41
+ * is always first and labelled `self`. */
42
+ function resolveRoots(selfId) {
43
+ const roots = [];
44
+ const selfMeta = getNode(selfId);
45
+ const selfContext = contextDir(selfId);
46
+ const selfReports = reportsDir(selfId);
47
+ if (existsSync(selfContext) || existsSync(selfReports)) {
48
+ roots.push({
49
+ nodeId: selfId,
50
+ label: `self-${sanitize(selfMeta?.name ?? selfId.slice(0, 8))}`,
51
+ dir: nodeDir(selfId),
52
+ self: true,
53
+ fileCount: nodeFiles(selfId),
54
+ contextDir: selfContext,
55
+ reportsDir: selfReports,
56
+ });
57
+ }
58
+ for (const sub of subscriptionsOf(selfId)) {
59
+ const context = contextDir(sub.node_id);
60
+ const reports = reportsDir(sub.node_id);
61
+ if (!existsSync(context) && !existsSync(reports))
62
+ continue;
63
+ const meta = getNode(sub.node_id);
64
+ const short = sub.node_id.slice(0, 8);
65
+ roots.push({
66
+ nodeId: sub.node_id,
67
+ label: `${sanitize(meta?.name ?? sub.node_id)}-${short}`,
68
+ dir: nodeDir(sub.node_id),
69
+ self: false,
70
+ fileCount: nodeFiles(sub.node_id),
71
+ contextDir: context,
72
+ reportsDir: reports,
73
+ });
74
+ }
75
+ return roots;
76
+ }
77
+ /** Is `nvim` on PATH and runnable? */
78
+ function nvimAvailable() {
79
+ const r = spawnSync('nvim', ['--version'], { stdio: 'ignore' });
80
+ return r.error === undefined && (r.status === 0 || r.status === null);
81
+ }
82
+ /** Build a temp dir of labelled node dirs. Self's label is prefixed `0-` so it
83
+ * sorts first in netrw; each label dir contains `context` and `reports` symlinks
84
+ * for whichever of those dirs exist on disk. */
85
+ function buildSymlinkTree(roots) {
86
+ const tree = mkdtempSync(join(tmpdir(), 'crtr-context-'));
87
+ for (const r of roots) {
88
+ const name = r.self ? `0-${r.label}` : r.label;
89
+ const nodeRoot = join(tree, name);
90
+ mkdirSync(nodeRoot, { recursive: true });
91
+ try {
92
+ if (r.contextDir !== undefined && existsSync(r.contextDir)) {
93
+ symlinkSync(r.contextDir, join(nodeRoot, 'context'), 'dir');
94
+ }
95
+ if (r.reportsDir !== undefined && existsSync(r.reportsDir)) {
96
+ symlinkSync(r.reportsDir, join(nodeRoot, 'reports'), 'dir');
97
+ }
98
+ }
99
+ catch {
100
+ /* best-effort: a label collision or symlink failure just drops that node */
101
+ }
102
+ }
103
+ return tree;
104
+ }
105
+ export const nodeContextLeaf = defineLeaf({
106
+ name: 'context',
107
+ description: 'browse this node + its subscriptions’ context and reports dirs in an nvim popup',
108
+ whenToUse: 'you want to SEE what the nodes you subscribe to have written — open a read-only nvim file explorer over this node’s context and reports dirs and every subscribed node’s context and reports dirs at once. The /context popup shells this. Piped it prints the roster instead',
109
+ help: {
110
+ name: 'node context',
111
+ summary: 'open a read-only nvim file explorer over this node’s context and reports dirs and those of every node it subscribes to',
112
+ params: [
113
+ { kind: 'flag', name: 'node', type: 'string', required: false, constraint: 'Node id to root on. Defaults to $CRTR_NODE_ID (the calling node).' },
114
+ ],
115
+ output: [
116
+ { name: 'roots', type: 'object[]', required: true, constraint: 'Resolved browse roots: {node_id, label, dir, self, files}. Returned on the piped/non-TTY path.' },
117
+ ],
118
+ outputKind: 'object',
119
+ effects: [
120
+ 'TTY: builds a temp dir of labelled node dirs, symlinking each available `context`/`reports` dir inside it, then execs `nvim -R` on the tree; the temp links are removed on exit (real dirs untouched).',
121
+ 'Requires nvim on PATH (a crtr strong dependency). Missing nvim -> error with an install hint.',
122
+ 'Non-TTY / piped: prints the resolved roster and exits 0; opens nothing.',
123
+ ],
124
+ },
125
+ run: async (input) => {
126
+ const selfId = input['node']?.trim() || process.env['CRTR_NODE_ID'];
127
+ if (selfId === undefined || selfId === '') {
128
+ throw new InputError({
129
+ error: 'no_node',
130
+ message: 'no current node: $CRTR_NODE_ID is unset and --node was not given',
131
+ field: 'node',
132
+ next: 'Run this from inside a canvas node (the /context popup does), or pass --node <id>.',
133
+ });
134
+ }
135
+ if (getNode(selfId) === null) {
136
+ throw new InputError({ error: 'not_found', message: `no node: ${selfId}`, field: 'node', next: 'List nodes with `crtr node inspect list`.' });
137
+ }
138
+ const roots = resolveRoots(selfId);
139
+ const rosterOut = roots.map((r) => ({ node_id: r.nodeId, label: r.label, dir: r.dir, self: r.self, files: r.fileCount }));
140
+ // Non-TTY: print the roster and exit. Opens nothing.
141
+ if (!process.stdout.isTTY) {
142
+ return { roots: rosterOut };
143
+ }
144
+ if (!nvimAvailable()) {
145
+ throw new InputError({
146
+ error: 'nvim_missing',
147
+ message: 'nvim is not on PATH — `crtr node context` opens the context explorer in nvim',
148
+ next: 'Install Neovim (macOS: `brew install neovim`; Debian/Ubuntu: `apt install neovim`), then retry.',
149
+ });
150
+ }
151
+ const tree = buildSymlinkTree(roots);
152
+ try {
153
+ // `nvim -R .` opens netrw (or the user's dir handler) read-only, cwd = the
154
+ // symlink tree, so the explorer roots on `self` + each subscription.
155
+ spawnSync('nvim', ['-R', '.'], { cwd: tree, stdio: 'inherit' });
156
+ }
157
+ finally {
158
+ rmSync(tree, { recursive: true, force: true }); // removes symlinks only; targets untouched
159
+ }
160
+ return { roots: rosterOut };
161
+ },
162
+ render: (result) => {
163
+ const roots = result['roots'] ?? [];
164
+ if (roots.length === 0)
165
+ return 'No node dirs to show (no subscriptions with written context or reports, and this node’s own context/reports dirs are empty/absent).';
166
+ const lines = roots.map((r) => {
167
+ const tag = r['self'] === true ? ' (self)' : '';
168
+ return ` ${String(r['label'])}${tag}\t${String(r['files'])} files\t${String(r['dir'])}`;
169
+ });
170
+ return [`Context roots (${roots.length}):`, ...lines].join('\n');
171
+ },
172
+ });
@@ -0,0 +1,2 @@
1
+ import type { LeafDef } from '../core/command.js';
2
+ export declare const nodeSnapshotLeaf: LeafDef;
@@ -0,0 +1,123 @@
1
+ import { copyFileSync, existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { basename, join } from 'node:path';
4
+ import { defineLeaf } from '../core/command.js';
5
+ import { InputError } from '../core/io.js';
6
+ import { getNode } from '../core/canvas/index.js';
7
+ import { sessionPtrPath } from '../core/canvas/paths.js';
8
+ import { SessionManager } from '../core/runtime/broker-sdk.js';
9
+ import { BUILTIN_SLASH_COMMANDS } from '../core/runtime/pi-vendored.js';
10
+ function resolveSessionFile(nodeId, metaFile) {
11
+ if (typeof metaFile === 'string' && metaFile !== '' && existsSync(metaFile))
12
+ return metaFile;
13
+ const ptr = sessionPtrPath(nodeId);
14
+ if (!existsSync(ptr))
15
+ return null;
16
+ const raw = readFileSync(ptr, 'utf8').trim();
17
+ if (raw === '')
18
+ return null;
19
+ if (existsSync(raw))
20
+ return raw;
21
+ return null;
22
+ }
23
+ function emptyStats(sessionId, sessionFile) {
24
+ return {
25
+ sessionFile,
26
+ sessionId,
27
+ userMessages: 0,
28
+ assistantMessages: 0,
29
+ toolCalls: 0,
30
+ toolResults: 0,
31
+ totalMessages: 0,
32
+ tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
33
+ cost: 0,
34
+ };
35
+ }
36
+ function statsFromMessages(messages, sessionId, sessionFile) {
37
+ const stats = emptyStats(sessionId, sessionFile);
38
+ stats.totalMessages = messages.length;
39
+ for (const message of messages) {
40
+ if (message.role === 'user')
41
+ stats.userMessages++;
42
+ else if (message.role === 'assistant') {
43
+ stats.assistantMessages++;
44
+ const content = Array.isArray(message.content) ? message.content : [];
45
+ stats.toolCalls += content.filter((c) => c !== null && typeof c === 'object' && c.type === 'toolCall').length;
46
+ const usage = message.usage;
47
+ if (usage !== undefined) {
48
+ stats.tokens.input += usage.input ?? 0;
49
+ stats.tokens.output += usage.output ?? 0;
50
+ stats.tokens.cacheRead += usage.cacheRead ?? 0;
51
+ stats.tokens.cacheWrite += usage.cacheWrite ?? 0;
52
+ stats.cost += usage.cost?.total ?? 0;
53
+ }
54
+ }
55
+ else if (message.role === 'toolResult') {
56
+ stats.toolResults++;
57
+ }
58
+ }
59
+ stats.tokens.total = stats.tokens.input + stats.tokens.output + stats.tokens.cacheRead + stats.tokens.cacheWrite;
60
+ return stats;
61
+ }
62
+ export const nodeSnapshotLeaf = defineLeaf({
63
+ name: 'snapshot',
64
+ description: 'read a dormant node session snapshot without launching its broker',
65
+ whenToUse: 'you need a read-only broker-compatible snapshot of a dormant node; it copies the session JSONL before parsing and never revives the node',
66
+ help: {
67
+ name: 'node snapshot',
68
+ summary: 'read-only dormant node snapshot in the broker welcome shape',
69
+ params: [{ kind: 'positional', name: 'id', type: 'string', required: true, constraint: 'Canvas node id.' }],
70
+ output: [
71
+ { name: 'node_id', type: 'string', required: true, constraint: 'The node id.' },
72
+ { name: 'snapshot', type: 'object', required: true, constraint: 'BrokerSnapshot-compatible {messages, stats, state}.' },
73
+ { name: 'commands', type: 'object[]', required: true, constraint: "Builtin slash commands only: {name,description,source:'builtin'}." },
74
+ ],
75
+ outputKind: 'object',
76
+ effects: ['Read-only: copies the session JSONL to a temp dir, parses the copy, then deletes the temp dir. Does not launch or revive brokers.'],
77
+ },
78
+ run: async (input) => {
79
+ const nodeId = input['id'];
80
+ const node = getNode(nodeId);
81
+ if (node === null) {
82
+ throw new InputError({ error: 'not_found', message: `no node: ${nodeId}`, field: 'id', next: 'List nodes with `crtr node inspect list`.' });
83
+ }
84
+ const sessionFile = resolveSessionFile(nodeId, node.pi_session_file);
85
+ if (sessionFile === null) {
86
+ throw new InputError({ error: 'no_session', message: `node has no readable session file: ${nodeId}`, field: 'id', next: 'Revive the node first, or choose a node that has captured pi_session_file.' });
87
+ }
88
+ const tempDir = mkdtempSync(join(tmpdir(), 'crtr-node-snapshot-'));
89
+ try {
90
+ const copy = join(tempDir, basename(sessionFile));
91
+ copyFileSync(sessionFile, copy);
92
+ const manager = SessionManager.open(copy);
93
+ const context = manager.buildSessionContext();
94
+ const messages = context.messages;
95
+ const sessionId = manager.getSessionId();
96
+ const sessionName = manager.getSessionName();
97
+ const snapshot = {
98
+ messages,
99
+ stats: statsFromMessages(messages, sessionId, sessionFile),
100
+ state: {
101
+ sessionId,
102
+ sessionFile,
103
+ model: context.model?.modelId,
104
+ isStreaming: false,
105
+ thinkingLevel: context.thinkingLevel,
106
+ steeringMode: 'one-at-a-time',
107
+ followUpMode: 'one-at-a-time',
108
+ sessionName,
109
+ autoCompactionEnabled: true,
110
+ pendingMessageCount: 0,
111
+ },
112
+ };
113
+ return {
114
+ node_id: nodeId,
115
+ snapshot,
116
+ commands: BUILTIN_SLASH_COMMANDS.map((c) => ({ ...c, source: 'builtin' })),
117
+ };
118
+ }
119
+ finally {
120
+ rmSync(tempDir, { recursive: true, force: true });
121
+ }
122
+ },
123
+ });