@north-light/crouter 0.3.349 → 0.3.351

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 (629) hide show
  1. package/bin/crtr-launcher +4 -0
  2. package/bin/runtime-generation-manifest.mjs +12 -2
  3. package/dist/api/client.d.ts +50 -7
  4. package/dist/api/client.js +2 -2
  5. package/dist/api/dto/analytics.d.ts +184 -0
  6. package/dist/api/dto/analytics.js +0 -0
  7. package/dist/api/dto/broker-ops.d.ts +43 -1
  8. package/dist/api/dto/config.d.ts +11 -0
  9. package/dist/api/dto/grants.d.ts +14 -0
  10. package/dist/api/dto/grants.js +0 -0
  11. package/dist/api/dto/human-requests.d.ts +14 -0
  12. package/dist/api/dto/memory.d.ts +6 -0
  13. package/dist/api/dto/modelauth.d.ts +35 -0
  14. package/dist/api/dto/node-records.d.ts +35 -0
  15. package/dist/api/dto/node-records.js +0 -0
  16. package/dist/api/dto/nodes.d.ts +5 -0
  17. package/dist/api/dto/profiles.d.ts +38 -3
  18. package/dist/api/dto/worktree.d.ts +11 -0
  19. package/dist/api/errors.d.ts +23 -1
  20. package/dist/api/errors.js +1 -1
  21. package/dist/api/index.d.ts +2 -0
  22. package/dist/api/index.js +1 -1
  23. package/dist/api/node-transport.js +1 -1
  24. package/dist/api/routes.d.ts +22 -0
  25. package/dist/api/routes.js +1 -1
  26. package/dist/build-root.d.ts +6 -1
  27. package/dist/build-root.js +1 -1
  28. package/dist/builtin-memory/crouter-sdk/client.md +34 -0
  29. package/dist/builtin-memory/crouter-sdk/errors.md +2 -0
  30. package/dist/builtin-memory/crouter-sdk/memory.md +2 -2
  31. package/dist/builtin-memory/crouter-sdk/streaming.md +21 -0
  32. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +8 -4
  33. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +39 -118
  34. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +6 -1
  35. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +41 -27
  36. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +52 -29
  37. package/dist/cli.js +9 -8
  38. package/dist/clients/attach/chrome/inbox-strip.js +1 -1
  39. package/dist/clients/attach/chrome/roster.js +1 -1
  40. package/dist/clients/attach/config.js +1 -1
  41. package/dist/clients/attach/overlays/auth.js +2 -2
  42. package/dist/clients/attach/overlays/graph.js +1 -1
  43. package/dist/clients/attach/render/tool-calls.js +3 -3
  44. package/dist/clients/attach/session/connection.js +1 -1
  45. package/dist/clients/attach/session/profile-files.js +1 -1
  46. package/dist/clients/attach/viewer.js +1569 -816
  47. package/dist/commands/api-client.js +3 -3
  48. package/dist/commands/broker-permissions.d.ts +2 -0
  49. package/dist/commands/broker-permissions.js +1 -0
  50. package/dist/commands/canvas-analytics/chart.d.ts +52 -0
  51. package/dist/commands/canvas-analytics/chart.js +1 -0
  52. package/dist/commands/canvas-analytics/drilldown.d.ts +34 -0
  53. package/dist/commands/canvas-analytics/drilldown.js +1 -0
  54. package/dist/commands/canvas-analytics/format.d.ts +29 -0
  55. package/dist/commands/canvas-analytics/format.js +1 -0
  56. package/dist/commands/canvas-analytics/graph-detail.d.ts +2 -0
  57. package/dist/commands/canvas-analytics/graph-detail.js +2 -0
  58. package/dist/commands/canvas-analytics/graphs.d.ts +9 -0
  59. package/dist/commands/canvas-analytics/graphs.js +1 -0
  60. package/dist/commands/canvas-analytics/memory.d.ts +2 -0
  61. package/dist/commands/canvas-analytics/memory.js +1 -0
  62. package/dist/commands/canvas-analytics/overview.d.ts +2 -0
  63. package/dist/commands/canvas-analytics/overview.js +1 -0
  64. package/dist/commands/canvas-analytics/shell.d.ts +60 -0
  65. package/dist/commands/canvas-analytics/shell.js +2 -0
  66. package/dist/commands/canvas-analytics/time.d.ts +2 -0
  67. package/dist/commands/canvas-analytics/time.js +1 -0
  68. package/dist/commands/canvas-analytics/usage.d.ts +2 -0
  69. package/dist/commands/canvas-analytics/usage.js +1 -0
  70. package/dist/commands/canvas-analytics/view.d.ts +85 -0
  71. package/dist/commands/canvas-analytics/view.js +1 -0
  72. package/dist/commands/canvas-analytics.d.ts +2 -0
  73. package/dist/commands/canvas-analytics.js +1 -0
  74. package/dist/commands/canvas-prune.js +1 -1
  75. package/dist/commands/canvas.js +1 -1
  76. package/dist/commands/cron.js +5 -5
  77. package/dist/commands/human/components.d.ts +2 -0
  78. package/dist/commands/human/components.js +2 -2
  79. package/dist/commands/human/prompts.js +1 -1
  80. package/dist/commands/human/queue.js +1 -1
  81. package/dist/commands/memory/client.d.ts +11 -0
  82. package/dist/commands/memory/client.js +1 -0
  83. package/dist/commands/memory/delete.js +1 -1
  84. package/dist/commands/memory/edit.js +3 -3
  85. package/dist/commands/memory/find.js +1 -1
  86. package/dist/commands/memory/history.js +4 -4
  87. package/dist/commands/memory/lint.js +1 -1
  88. package/dist/commands/memory/list.d.ts +0 -2
  89. package/dist/commands/memory/list.js +1 -1
  90. package/dist/commands/memory/move.js +1 -1
  91. package/dist/commands/memory/origin.js +1 -1
  92. package/dist/commands/memory/read.d.ts +0 -5
  93. package/dist/commands/memory/read.js +3 -3
  94. package/dist/commands/memory/shared.d.ts +0 -178
  95. package/dist/commands/memory/shared.js +1 -12
  96. package/dist/commands/memory/write.js +5 -5
  97. package/dist/commands/node/bash.js +5 -5
  98. package/dist/commands/node/create.d.ts +0 -17
  99. package/dist/commands/node/create.js +2 -3
  100. package/dist/commands/node/inspect.js +6 -6
  101. package/dist/commands/node/lifecycle.js +2 -2
  102. package/dist/commands/node-context.js +2 -2
  103. package/dist/commands/node-inspect-artifacts.js +6 -6
  104. package/dist/commands/pkg/browse/catalog.js +1 -1
  105. package/dist/commands/pkg/browse/model.d.ts +1 -1
  106. package/dist/commands/pkg/market-manage.js +1 -1
  107. package/dist/commands/pkg/plugin-manage.js +10 -10
  108. package/dist/commands/profile/daemon-profiles.d.ts +13 -0
  109. package/dist/commands/profile/daemon-profiles.js +4 -0
  110. package/dist/commands/profile/delete.js +1 -1
  111. package/dist/commands/profile/env.d.ts +6 -0
  112. package/dist/commands/profile/env.js +2 -2
  113. package/dist/commands/profile/kind.js +1 -2
  114. package/dist/commands/profile/list.js +1 -1
  115. package/dist/commands/profile/meta.js +1 -1
  116. package/dist/commands/profile/new.js +1 -1
  117. package/dist/commands/profile/pause.js +1 -1
  118. package/dist/commands/profile/project.js +1 -1
  119. package/dist/commands/profile/rename.js +1 -1
  120. package/dist/commands/profile/show.js +1 -1
  121. package/dist/commands/profile.js +1 -1
  122. package/dist/commands/push.js +3 -3
  123. package/dist/commands/surface-inspect.js +1 -1
  124. package/dist/commands/sys/branch.js +1 -1
  125. package/dist/commands/sys/config.js +1 -1
  126. package/dist/commands/sys/connect.js +3 -3
  127. package/dist/commands/sys/context/admin/actions.js +1 -1
  128. package/dist/commands/sys/context/admin/model.d.ts +1 -0
  129. package/dist/commands/sys/context/admin/model.js +1 -1
  130. package/dist/commands/sys/context/admin.js +1 -1
  131. package/dist/commands/sys/context/expose.js +2 -1
  132. package/dist/commands/sys/context/prompt-review.js +2 -2
  133. package/dist/commands/sys/context/resolve.js +7 -7
  134. package/dist/commands/sys/context/sysprompt.js +1 -1
  135. package/dist/commands/sys/join.d.ts +1 -0
  136. package/dist/commands/sys/join.js +1 -0
  137. package/dist/commands/sys/migrate.js +1 -1
  138. package/dist/commands/sys/panels/models-panel.js +1 -1
  139. package/dist/commands/sys/panels/profiles-panel.d.ts +0 -1
  140. package/dist/commands/sys/panels/profiles-panel.js +1 -1
  141. package/dist/commands/sys/provider-login.js +6 -6
  142. package/dist/commands/sys/setup-core.d.ts +1 -1
  143. package/dist/commands/sys/setup-core.js +6 -6
  144. package/dist/commands/sys/setup-settings-runner.js +1 -1
  145. package/dist/commands/sys/setup.js +1 -1
  146. package/dist/commands/sys/spaces.d.ts +1 -0
  147. package/dist/commands/sys/spaces.js +2 -0
  148. package/dist/commands/sys/sync-deps.js +2 -2
  149. package/dist/commands/sys/sync-project-guidance.js +4 -4
  150. package/dist/commands/sys/sync-skills.js +2 -2
  151. package/dist/commands/sys/worktrees.d.ts +1 -0
  152. package/dist/commands/sys/worktrees.js +1 -1
  153. package/dist/commands/sys.js +1 -1
  154. package/dist/core/auth-file.js +2 -2
  155. package/dist/core/bash-job-supervisor.d.ts +6 -0
  156. package/dist/core/bash-job-supervisor.js +4 -0
  157. package/dist/core/bash-jobs.d.ts +13 -6
  158. package/dist/core/bash-jobs.js +8 -7
  159. package/dist/core/broker-client/transport-relay.d.ts +6 -3
  160. package/dist/core/broker-client/transport-relay.js +1 -1
  161. package/dist/core/broker-client/transport-socket.d.ts +6 -1
  162. package/dist/core/broker-client/transport-socket.js +1 -1
  163. package/dist/core/canvas/canvas.d.ts +39 -18
  164. package/dist/core/canvas/canvas.js +21 -20
  165. package/dist/core/canvas/context-strip.d.ts +20 -0
  166. package/dist/core/canvas/context-strip.js +5 -0
  167. package/dist/core/canvas/crons.d.ts +12 -20
  168. package/dist/core/canvas/crons.js +15 -13
  169. package/dist/core/canvas/db.js +2 -2
  170. package/dist/core/canvas/deliveries.d.ts +4 -1
  171. package/dist/core/canvas/deliveries.js +1 -1
  172. package/dist/core/canvas/executions.js +1 -1
  173. package/dist/core/canvas/history.d.ts +2 -1
  174. package/dist/core/canvas/history.js +17 -18
  175. package/dist/core/canvas/memory-reads.d.ts +19 -0
  176. package/dist/core/canvas/memory-reads.js +1 -0
  177. package/dist/core/canvas/migrations.d.ts +0 -4
  178. package/dist/core/canvas/migrations.js +99 -32
  179. package/dist/core/canvas/nav-render.js +1 -1
  180. package/dist/core/canvas/node-agent-paths.d.ts +8 -0
  181. package/dist/core/canvas/node-agent-paths.js +1 -0
  182. package/dist/core/canvas/node-recap.d.ts +2 -2
  183. package/dist/core/canvas/node-recap.js +2 -2
  184. package/dist/core/canvas/node-tokens.d.ts +24 -0
  185. package/dist/core/canvas/node-tokens.js +5 -0
  186. package/dist/core/canvas/paths.d.ts +16 -19
  187. package/dist/core/canvas/paths.js +1 -1
  188. package/dist/core/canvas/render-source.d.ts +2 -2
  189. package/dist/core/canvas/render-source.js +6 -9
  190. package/dist/core/canvas/sessions.d.ts +18 -0
  191. package/dist/core/canvas/sessions.js +4 -1
  192. package/dist/core/canvas/types.d.ts +22 -6
  193. package/dist/core/command-hooks/compose.js +1 -1
  194. package/dist/core/command-hooks/discovery.d.ts +15 -1
  195. package/dist/core/command-hooks/discovery.js +1 -1
  196. package/dist/core/command-hooks/help.d.ts +2 -2
  197. package/dist/core/command-hooks/lifecycle-create.d.ts +1 -1
  198. package/dist/core/command-hooks/lifecycle-create.js +1 -1
  199. package/dist/core/command-hooks/transport/exec-invoke.d.ts +2 -0
  200. package/dist/core/command-hooks/transport/exec-invoke.js +1 -1
  201. package/dist/core/command-hooks/transport/exec-lifecycle.d.ts +1 -0
  202. package/dist/core/command-hooks/transport/exec-lifecycle.js +1 -1
  203. package/dist/core/command-hooks/transport/plan.d.ts +3 -0
  204. package/dist/core/command-hooks/transport/plan.js +1 -0
  205. package/dist/core/command-plugins/discovery.js +1 -1
  206. package/dist/core/command.d.ts +6 -2
  207. package/dist/core/command.js +6 -6
  208. package/dist/core/config.d.ts +8 -5
  209. package/dist/core/config.js +1 -1
  210. package/dist/core/conversation-store/client-worker.d.ts +1 -0
  211. package/dist/core/conversation-store/client-worker.js +1 -0
  212. package/dist/core/conversation-store/client.d.ts +2 -0
  213. package/dist/core/conversation-store/client.js +2 -0
  214. package/dist/core/conversation-store/file-store.d.ts +37 -0
  215. package/dist/core/conversation-store/file-store.js +9 -0
  216. package/dist/core/conversation-store/index.d.ts +50 -0
  217. package/dist/core/conversation-store/index.js +1 -0
  218. package/dist/core/conversation-store/listener.d.ts +4 -0
  219. package/dist/core/conversation-store/listener.js +3 -0
  220. package/dist/core/conversation-store/patch.d.ts +1 -0
  221. package/dist/core/conversation-store/patch.js +1 -0
  222. package/dist/core/directory/join.d.ts +27 -0
  223. package/dist/core/directory/join.js +1 -0
  224. package/dist/core/disk-usage.d.ts +4 -0
  225. package/dist/core/disk-usage.js +1 -0
  226. package/dist/core/events/emit.d.ts +2 -0
  227. package/dist/core/events/emit.js +3 -1
  228. package/dist/core/feed/feed.js +4 -4
  229. package/dist/core/feed/messages.d.ts +3 -0
  230. package/dist/core/feed/messages.js +6 -6
  231. package/dist/core/fs-utils.d.ts +2 -0
  232. package/dist/core/fs-utils.js +3 -3
  233. package/dist/core/grants/local.d.ts +4 -0
  234. package/dist/core/grants/local.js +1 -0
  235. package/dist/core/grants/mirror.d.ts +44 -0
  236. package/dist/core/grants/mirror.js +4 -0
  237. package/dist/core/grants/on-sync.d.ts +22 -0
  238. package/dist/core/grants/on-sync.js +1 -0
  239. package/dist/core/grants/remove.d.ts +7 -0
  240. package/dist/core/grants/remove.js +1 -0
  241. package/dist/core/grants/sandboxes.d.ts +7 -0
  242. package/dist/core/grants/sandboxes.js +1 -0
  243. package/dist/core/grants/sync.d.ts +12 -0
  244. package/dist/core/grants/sync.js +1 -0
  245. package/dist/core/help/memory-extensions.d.ts +3 -0
  246. package/dist/core/help/memory-extensions.js +2 -0
  247. package/dist/core/help.d.ts +14 -0
  248. package/dist/core/help.js +7 -7
  249. package/dist/core/host-exports/builtins.js +3 -3
  250. package/dist/core/host-exports/export.js +3 -3
  251. package/dist/core/human/action-binding.d.ts +2 -0
  252. package/dist/core/human/action-binding.js +1 -1
  253. package/dist/core/human/feedback-companion.d.ts +0 -1
  254. package/dist/core/human/feedback-companion.js +1 -1
  255. package/dist/core/human/page-schema.d.ts +1 -1
  256. package/dist/core/inspector/core.d.ts +2 -0
  257. package/dist/core/inspector/core.js +1 -1
  258. package/dist/core/inspector/tui.js +2 -2
  259. package/dist/core/installed-plugins.js +1 -1
  260. package/dist/core/io.js +6 -6
  261. package/dist/core/keybindings/catalog.d.ts +1 -1
  262. package/dist/core/keybindings/catalog.js +1 -1
  263. package/dist/core/layout-migrate/index.d.ts +15 -0
  264. package/dist/core/layout-migrate/index.js +4 -0
  265. package/dist/core/layout-migrate/paths.d.ts +26 -0
  266. package/dist/core/layout-migrate/paths.js +1 -0
  267. package/dist/core/layout-migrate/stored-paths.d.ts +2 -0
  268. package/dist/core/layout-migrate/stored-paths.js +11 -0
  269. package/dist/core/layout-migrate/stores.d.ts +17 -0
  270. package/dist/core/layout-migrate/stores.js +4 -0
  271. package/dist/core/layout.d.ts +26 -0
  272. package/dist/core/layout.js +2 -0
  273. package/dist/core/llm/run.d.ts +60 -0
  274. package/dist/core/llm/run.js +4 -0
  275. package/dist/core/memory/cli-selector.d.ts +13 -0
  276. package/dist/core/memory/cli-selector.js +1 -0
  277. package/dist/core/memory/history.d.ts +5 -4
  278. package/dist/core/memory/history.js +6 -6
  279. package/dist/core/memory/inline-ref-inventory.d.ts +2 -1
  280. package/dist/core/memory/inline-ref-inventory.js +1 -1
  281. package/dist/core/memory/lint.js +2 -2
  282. package/dist/core/memory/mutation-domain.d.ts +2 -2
  283. package/dist/core/memory/mutation-domain.js +8 -7
  284. package/dist/core/memory/mutations.js +1 -1
  285. package/dist/core/memory/service.d.ts +5 -0
  286. package/dist/core/memory/service.js +2 -2
  287. package/dist/core/memory-resolver.d.ts +11 -7
  288. package/dist/core/memory-resolver.js +2 -2
  289. package/dist/core/model-auth/daemon-login.d.ts +13 -0
  290. package/dist/core/model-auth/daemon-login.js +1 -0
  291. package/dist/core/model-auth/flows.d.ts +45 -0
  292. package/dist/core/model-auth/flows.js +1 -0
  293. package/dist/core/model-auth/handoff-jti.d.ts +4 -0
  294. package/dist/core/model-auth/handoff-jti.js +1 -0
  295. package/dist/core/model-routes.js +1 -1
  296. package/dist/core/nested-stores.d.ts +2 -1
  297. package/dist/core/nested-stores.js +1 -2
  298. package/dist/core/preview-registry.js +2 -2
  299. package/dist/core/profiles/env-store.js +2 -2
  300. package/dist/core/profiles/manifest.d.ts +16 -5
  301. package/dist/core/profiles/manifest.js +1 -1
  302. package/dist/core/provider-management.js +1 -1
  303. package/dist/core/review/birth.js +1 -1
  304. package/dist/core/review/companion.d.ts +0 -2
  305. package/dist/core/review/companion.js +1 -1
  306. package/dist/core/review/fork.js +2 -2
  307. package/dist/core/review/realize.js +1 -1
  308. package/dist/core/review/stage.d.ts +1 -1
  309. package/dist/core/review/stage.js +1 -1
  310. package/dist/core/review/types.d.ts +1 -1
  311. package/dist/core/runs/events.d.ts +34 -0
  312. package/dist/core/runs/events.js +1 -0
  313. package/dist/core/runs/operations.d.ts +43 -0
  314. package/dist/core/runs/operations.js +12 -0
  315. package/dist/core/runtime/bearings-render.d.ts +2 -2
  316. package/dist/core/runtime/bearings-render.js +11 -11
  317. package/dist/core/runtime/bearings.js +2 -2
  318. package/dist/core/runtime/broker/cli-relay.d.ts +5 -0
  319. package/dist/core/runtime/broker/cli-relay.js +1 -0
  320. package/dist/core/runtime/broker/client-registry.d.ts +0 -5
  321. package/dist/core/runtime/broker/client-registry.js +1 -1
  322. package/dist/core/runtime/broker/daemon-ops.d.ts +28 -1
  323. package/dist/core/runtime/broker/daemon-ops.js +1 -1
  324. package/dist/core/runtime/broker/engine-drive.d.ts +0 -2
  325. package/dist/core/runtime/broker/engine-drive.js +1 -1
  326. package/dist/core/runtime/broker/fault-retry.d.ts +9 -0
  327. package/dist/core/runtime/broker/fault-retry.js +1 -1
  328. package/dist/core/runtime/broker/frame-dispatch.d.ts +5 -9
  329. package/dist/core/runtime/broker/frame-dispatch.js +1 -1
  330. package/dist/core/runtime/broker/frame-engine-control.d.ts +3 -2
  331. package/dist/core/runtime/broker/frame-engine-control.js +1 -1
  332. package/dist/core/runtime/broker/frame-memory-refs.d.ts +4 -3
  333. package/dist/core/runtime/broker/frame-memory-refs.js +1 -1
  334. package/dist/core/runtime/broker/frame-read.js +1 -1
  335. package/dist/core/runtime/broker/frame-session.js +1 -1
  336. package/dist/core/runtime/broker/held-deferred-inbox.d.ts +3 -1
  337. package/dist/core/runtime/broker/held-deferred-inbox.js +1 -1
  338. package/dist/core/runtime/broker/inbox.d.ts +1 -2
  339. package/dist/core/runtime/broker/inbox.js +7 -10
  340. package/dist/core/runtime/broker/model-adapter.d.ts +9 -0
  341. package/dist/core/runtime/broker/model-adapter.js +2 -0
  342. package/dist/core/runtime/broker/node-token.d.ts +3 -0
  343. package/dist/core/runtime/broker/node-token.js +1 -0
  344. package/dist/core/runtime/broker/read-ops.js +1 -1
  345. package/dist/core/runtime/broker/rebind.d.ts +2 -0
  346. package/dist/core/runtime/broker/rebind.js +1 -1
  347. package/dist/core/runtime/broker/tool-groups.js +1 -1
  348. package/dist/core/runtime/broker/turn-ignition.d.ts +3 -3
  349. package/dist/core/runtime/broker/turn-ignition.js +1 -1
  350. package/dist/core/runtime/broker-cli.js +3 -3
  351. package/dist/core/runtime/broker-extension-render.d.ts +2 -5
  352. package/dist/core/runtime/broker-extension-render.js +4 -4
  353. package/dist/core/runtime/broker-persona-guidance.js +3 -3
  354. package/dist/core/runtime/broker-protocol.d.ts +7 -8
  355. package/dist/core/runtime/broker.js +1 -1
  356. package/dist/core/runtime/canvas-extensions.d.ts +1 -0
  357. package/dist/core/runtime/canvas-extensions.js +1 -1
  358. package/dist/core/runtime/close.d.ts +9 -0
  359. package/dist/core/runtime/close.js +1 -1
  360. package/dist/core/runtime/deliver-live.js +1 -1
  361. package/dist/core/runtime/fleet.d.ts +1 -1
  362. package/dist/core/runtime/front-door-login.js +1 -1
  363. package/dist/core/runtime/front-door.js +2 -2
  364. package/dist/core/runtime/headless-pi.d.ts +1 -1
  365. package/dist/core/runtime/headless-pi.js +1 -1
  366. package/dist/core/runtime/host.d.ts +1 -1
  367. package/dist/core/runtime/host.js +3 -1
  368. package/dist/core/runtime/invocation.d.ts +3 -0
  369. package/dist/core/runtime/kickoff.d.ts +1 -0
  370. package/dist/core/runtime/kickoff.js +16 -16
  371. package/dist/core/runtime/launch-target.d.ts +1 -1
  372. package/dist/core/runtime/launch-target.js +1 -1
  373. package/dist/core/runtime/launch.d.ts +10 -35
  374. package/dist/core/runtime/launch.js +1 -1
  375. package/dist/core/runtime/lifecycle.js +2 -2
  376. package/dist/core/runtime/memory.d.ts +1 -1
  377. package/dist/core/runtime/memory.js +1 -1
  378. package/dist/core/runtime/model-registry.js +1 -1
  379. package/dist/core/runtime/model-swap.d.ts +17 -19
  380. package/dist/core/runtime/model-swap.js +1 -1
  381. package/dist/core/runtime/node-read.js +3 -2
  382. package/dist/core/runtime/nodes.d.ts +7 -0
  383. package/dist/core/runtime/nodes.js +1 -1
  384. package/dist/core/runtime/outcome-document.js +1 -1
  385. package/dist/core/runtime/package-health.js +2 -2
  386. package/dist/core/runtime/persona.js +2 -2
  387. package/dist/core/runtime/pi-vendored.js +1 -1
  388. package/dist/core/runtime/placement-tmux.d.ts +1 -1
  389. package/dist/core/runtime/placement-tmux.js +1 -1
  390. package/dist/core/runtime/placement.js +1 -1
  391. package/dist/core/runtime/promote.js +1 -1
  392. package/dist/core/runtime/provider-credential-gate.js +2 -2
  393. package/dist/core/runtime/recovery-hold.d.ts +2 -0
  394. package/dist/core/runtime/recovery-hold.js +1 -0
  395. package/dist/core/runtime/recycle.js +1 -1
  396. package/dist/core/runtime/reopen.js +1 -1
  397. package/dist/core/runtime/reset.js +1 -1
  398. package/dist/core/runtime/revive.d.ts +1 -1
  399. package/dist/core/runtime/revive.js +2 -3
  400. package/dist/core/runtime/roadmap.js +4 -4
  401. package/dist/core/runtime/run-command.d.ts +6 -0
  402. package/dist/core/runtime/run-command.js +1 -1
  403. package/dist/core/runtime/session-branch.d.ts +2 -8
  404. package/dist/core/runtime/session-branch.js +1 -1
  405. package/dist/core/runtime/session-list-cache.d.ts +6 -22
  406. package/dist/core/runtime/session-list-cache.js +1 -1
  407. package/dist/core/runtime/situational-context.js +2 -2
  408. package/dist/core/runtime/spawn-env.d.ts +6 -6
  409. package/dist/core/runtime/spawn-env.js +1 -1
  410. package/dist/core/runtime/spawn.d.ts +5 -0
  411. package/dist/core/runtime/spawn.js +2 -2
  412. package/dist/core/runtime/stop-guard.js +2 -2
  413. package/dist/core/runtime/stop-signals.d.ts +2 -2
  414. package/dist/core/runtime/stop-signals.js +1 -1
  415. package/dist/core/runtime/structured-output.d.ts +10 -4
  416. package/dist/core/runtime/structured-output.js +4 -4
  417. package/dist/core/runtime/tmux-bindings.js +3 -3
  418. package/dist/core/runtime/tmux-driver.d.ts +4 -0
  419. package/dist/core/runtime/tmux-driver.js +7 -7
  420. package/dist/core/runtime/view-socket.d.ts +9 -2
  421. package/dist/core/runtime/view-socket.js +1 -1
  422. package/dist/core/runtime/warm-pool.js +1 -1
  423. package/dist/core/scope.d.ts +3 -2
  424. package/dist/core/scope.js +1 -1
  425. package/dist/core/scoped-state/db.js +1 -1
  426. package/dist/core/scoped-state/migrate.js +3 -3
  427. package/dist/core/scoped-state/profiles.d.ts +2 -1
  428. package/dist/core/scoped-state/profiles.js +1 -1
  429. package/dist/core/scoped-state/schema.d.ts +2 -1
  430. package/dist/core/scoped-state/schema.js +4 -4
  431. package/dist/core/scopes.d.ts +2 -2
  432. package/dist/core/scopes.js +1 -1
  433. package/dist/core/secrets.d.ts +1 -0
  434. package/dist/core/secrets.js +2 -2
  435. package/dist/core/spaces/mounts.d.ts +32 -0
  436. package/dist/core/spaces/mounts.js +1 -0
  437. package/dist/core/spaces/open-beneath.d.ts +71 -0
  438. package/dist/core/spaces/open-beneath.js +1 -0
  439. package/dist/core/spaces/permissions.d.ts +46 -0
  440. package/dist/core/spaces/permissions.js +1 -0
  441. package/dist/core/spaces/process.d.ts +27 -0
  442. package/dist/core/spaces/process.js +1 -0
  443. package/dist/core/spaces/space.d.ts +8 -0
  444. package/dist/core/spaces/space.js +2 -0
  445. package/dist/core/spaces/stop.d.ts +8 -0
  446. package/dist/core/spaces/stop.js +2 -0
  447. package/dist/core/spaces/users.d.ts +19 -0
  448. package/dist/core/spaces/users.js +1 -0
  449. package/dist/core/subscription-state.d.ts +2 -0
  450. package/dist/core/subscription-state.js +3 -3
  451. package/dist/core/substrate/frontmatter-validation.js +1 -1
  452. package/dist/core/substrate/injected-store.d.ts +28 -3
  453. package/dist/core/substrate/injected-store.js +2 -1
  454. package/dist/core/substrate/listings.js +1 -1
  455. package/dist/core/substrate/memory-events.d.ts +6 -0
  456. package/dist/core/substrate/memory-events.js +1 -0
  457. package/dist/core/substrate/on-read-node.js +1 -1
  458. package/dist/core/substrate/on-read.d.ts +6 -6
  459. package/dist/core/substrate/on-read.js +5 -5
  460. package/dist/core/substrate/plan.js +1 -1
  461. package/dist/core/substrate/render-node.js +1 -1
  462. package/dist/core/substrate/render.d.ts +4 -3
  463. package/dist/core/substrate/render.js +38 -38
  464. package/dist/core/substrate/schema.d.ts +6 -0
  465. package/dist/core/substrate/schema.js +1 -1
  466. package/dist/core/tui/panel.js +1 -1
  467. package/dist/core/user-settings.d.ts +12 -0
  468. package/dist/core/user-settings.js +1 -1
  469. package/dist/core/worktree-mutation-async.js +5 -5
  470. package/dist/core/worktree-quarantine.js +1 -1
  471. package/dist/core/worktree-remove.d.ts +15 -0
  472. package/dist/core/worktree-remove.js +1 -0
  473. package/dist/core/worktree-sweep.js +6 -6
  474. package/dist/daemon/analytics/compute.d.ts +37 -0
  475. package/dist/daemon/analytics/compute.js +1 -0
  476. package/dist/daemon/analytics/scan-worker.d.ts +1 -0
  477. package/dist/daemon/analytics/scan-worker.js +2 -0
  478. package/dist/daemon/analytics/scan.d.ts +37 -0
  479. package/dist/daemon/analytics/scan.js +1 -0
  480. package/dist/daemon/api/app-listener.d.ts +11 -0
  481. package/dist/daemon/api/app-listener.js +21 -0
  482. package/dist/daemon/api/bridge.js +5 -5
  483. package/dist/daemon/api/handlers/analytics.d.ts +2 -0
  484. package/dist/daemon/api/handlers/analytics.js +1 -0
  485. package/dist/daemon/api/handlers/attach.js +1 -1
  486. package/dist/daemon/api/handlers/bash-jobs.js +2 -2
  487. package/dist/daemon/api/handlers/bash.js +1 -1
  488. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  489. package/dist/daemon/api/handlers/canvas.d.ts +1 -1
  490. package/dist/daemon/api/handlers/canvas.js +6 -6
  491. package/dist/daemon/api/handlers/crons.js +1 -1
  492. package/dist/daemon/api/handlers/files.d.ts +1 -1
  493. package/dist/daemon/api/handlers/files.js +1 -1
  494. package/dist/daemon/api/handlers/health.js +2 -2
  495. package/dist/daemon/api/handlers/hook-exec.d.ts +2 -0
  496. package/dist/daemon/api/handlers/hook-exec.js +1 -0
  497. package/dist/daemon/api/handlers/human-requests.js +1 -1
  498. package/dist/daemon/api/handlers/inbox.js +1 -1
  499. package/dist/daemon/api/handlers/llm.d.ts +2 -0
  500. package/dist/daemon/api/handlers/llm.js +3 -0
  501. package/dist/daemon/api/handlers/memory-reads.d.ts +4 -0
  502. package/dist/daemon/api/handlers/memory-reads.js +1 -0
  503. package/dist/daemon/api/handlers/memory.js +5 -1
  504. package/dist/daemon/api/handlers/messages.js +2 -2
  505. package/dist/daemon/api/handlers/modelauth.js +1 -1
  506. package/dist/daemon/api/handlers/node-outcomes.js +1 -1
  507. package/dist/daemon/api/handlers/node-records.d.ts +2 -0
  508. package/dist/daemon/api/handlers/node-records.js +3 -0
  509. package/dist/daemon/api/handlers/nodes.d.ts +1 -1
  510. package/dist/daemon/api/handlers/nodes.js +1 -1
  511. package/dist/daemon/api/handlers/profiles.d.ts +4 -1
  512. package/dist/daemon/api/handlers/profiles.js +1 -1
  513. package/dist/daemon/api/handlers/reports.js +5 -3
  514. package/dist/daemon/api/handlers/reviews.d.ts +1 -1
  515. package/dist/daemon/api/handlers/reviews.js +1 -1
  516. package/dist/daemon/api/handlers/run-routes.d.ts +2 -0
  517. package/dist/daemon/api/handlers/run-routes.js +1 -0
  518. package/dist/daemon/api/handlers/runs.d.ts +9 -0
  519. package/dist/daemon/api/handlers/runs.js +1 -0
  520. package/dist/daemon/api/handlers/subscriptions.js +1 -1
  521. package/dist/daemon/api/handlers/worktree.js +1 -1
  522. package/dist/daemon/api/map.d.ts +10 -4
  523. package/dist/daemon/api/map.js +2 -2
  524. package/dist/daemon/api/operations.d.ts +18 -0
  525. package/dist/daemon/api/operations.js +1 -0
  526. package/dist/daemon/api/principal.d.ts +42 -0
  527. package/dist/daemon/api/principal.js +1 -0
  528. package/dist/daemon/api/router.d.ts +27 -3
  529. package/dist/daemon/api/router.js +1 -1
  530. package/dist/daemon/api/server.d.ts +4 -0
  531. package/dist/daemon/api/server.js +1 -1
  532. package/dist/daemon/api/vendor-signin.d.ts +7 -0
  533. package/dist/daemon/api/vendor-signin.js +77 -0
  534. package/dist/daemon/api/viewer-presence.d.ts +3 -0
  535. package/dist/daemon/api/viewer-presence.js +1 -0
  536. package/dist/daemon/cron/armed-context.d.ts +4 -0
  537. package/dist/daemon/cron/armed-context.js +1 -1
  538. package/dist/daemon/cron/escalation.js +3 -3
  539. package/dist/daemon/cron/lease-recovery.js +1 -1
  540. package/dist/daemon/cron/sinks.js +2 -2
  541. package/dist/daemon/cron-run.d.ts +5 -5
  542. package/dist/daemon/cron-run.js +1 -1
  543. package/dist/daemon/crtrd.js +8 -7
  544. package/dist/daemon/delivery/run-delivery.d.ts +4 -0
  545. package/dist/daemon/delivery/run-delivery.js +1 -1
  546. package/dist/daemon/fleet.d.ts +1 -1
  547. package/dist/daemon/fleet.js +6 -6
  548. package/dist/daemon/human/finish.js +7 -7
  549. package/dist/daemon/joined-owner.d.ts +5 -0
  550. package/dist/daemon/joined-owner.js +1 -0
  551. package/dist/daemon/manage.js +2 -2
  552. package/dist/daemon/messaging/node-message.js +1 -1
  553. package/dist/daemon/park-activity.js +1 -1
  554. package/dist/daemon/reconcilers/bash-deadline.js +1 -1
  555. package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
  556. package/dist/daemon/reconcilers/broker-supervision.js +4 -4
  557. package/dist/daemon/reconcilers/delivery-lane.js +1 -1
  558. package/dist/daemon/reconcilers/live-obligation.d.ts +7 -0
  559. package/dist/daemon/reconcilers/live-obligation.js +1 -1
  560. package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
  561. package/dist/daemon/reconcilers/storage-maintenance.d.ts +20 -1
  562. package/dist/daemon/reconcilers/storage-maintenance.js +1 -1
  563. package/dist/daemon/runs/append-section.d.ts +6 -0
  564. package/dist/daemon/runs/append-section.js +2 -0
  565. package/dist/daemon/runs/observer.d.ts +5 -0
  566. package/dist/daemon/runs/observer.js +4 -0
  567. package/dist/launcher/bwrap.d.ts +4 -0
  568. package/dist/launcher/bwrap.js +2 -0
  569. package/dist/launcher/client.d.ts +2 -0
  570. package/dist/launcher/client.js +3 -0
  571. package/dist/launcher/main.d.ts +1 -0
  572. package/dist/launcher/main.js +3 -0
  573. package/dist/launcher/netrule.d.ts +1 -0
  574. package/dist/launcher/netrule.js +1 -0
  575. package/dist/launcher/seccomp.d.ts +1 -0
  576. package/dist/launcher/seccomp.js +1 -0
  577. package/dist/launcher/spec.d.ts +88 -0
  578. package/dist/launcher/spec.js +1 -0
  579. package/dist/launcher/users.d.ts +18 -0
  580. package/dist/launcher/users.js +7 -0
  581. package/dist/launcher/validate.d.ts +22 -0
  582. package/dist/launcher/validate.js +1 -0
  583. package/dist/migrations/corpus.js +3 -3
  584. package/dist/migrations/runner.js +1 -1
  585. package/dist/native/linux/addon.node +0 -0
  586. package/dist/native/linux.d.ts +29 -0
  587. package/dist/native/linux.js +1 -0
  588. package/dist/pi-extensions/broker-local.js +2 -2
  589. package/dist/pi-extensions/canvas-bash-valve.d.ts +1 -3
  590. package/dist/pi-extensions/canvas-bash-valve.js +4 -7
  591. package/dist/pi-extensions/canvas-context-intro.d.ts +1 -2
  592. package/dist/pi-extensions/canvas-context-intro.js +2 -2
  593. package/dist/pi-extensions/canvas-doc-substrate.js +6 -6
  594. package/dist/pi-extensions/canvas-inbox-watcher.d.ts +2 -1
  595. package/dist/pi-extensions/canvas-inbox-watcher.js +2 -1
  596. package/dist/pi-extensions/canvas-passive-context.d.ts +1 -1
  597. package/dist/pi-extensions/canvas-passive-context.js +11 -11
  598. package/dist/pi-extensions/canvas-recap.js +6 -6
  599. package/dist/pi-extensions/canvas-stophook.js +1 -1
  600. package/dist/pi-extensions/canvas-structured-output.js +4 -4
  601. package/dist/pi-extensions/canvas-tool-guide.js +1 -1
  602. package/dist/pi-extensions/system-prompt-entry.d.ts +15 -0
  603. package/dist/pi-extensions/system-prompt-entry.js +1 -0
  604. package/dist/shared/env.d.ts +5 -1
  605. package/dist/shared/env.js +1 -1
  606. package/dist/shared/strip-cd-prefix.d.ts +1 -0
  607. package/dist/shared/strip-cd-prefix.js +1 -0
  608. package/dist/types.d.ts +31 -15
  609. package/docs/cli/memory-and-preferences.md +3 -1
  610. package/docs/sdk/client.md +34 -0
  611. package/docs/sdk/errors.md +2 -0
  612. package/docs/sdk/memory.md +2 -2
  613. package/docs/sdk/streaming.md +21 -0
  614. package/package.json +18 -4
  615. package/packages/crouter-identity/dist/index.d.ts +54 -0
  616. package/packages/crouter-identity/dist/index.js +166 -0
  617. package/packages/crouter-identity/package.json +33 -0
  618. package/runtime.lock.json +34 -8
  619. package/scripts/build-native-linux.mjs +16 -0
  620. package/scripts/install-runtime.mjs +9 -7
  621. package/scripts/lib/isolation-env.mjs +42 -0
  622. package/scripts/runtime-smoke.mjs +49 -0
  623. package/src/native/linux/addon.c +249 -0
  624. package/dist/core/profiles/state-block.d.ts +0 -4
  625. package/dist/core/profiles/state-block.js +0 -3
  626. package/dist/core/runtime/broker/auth-reload.d.ts +0 -41
  627. package/dist/core/runtime/broker/auth-reload.js +0 -1
  628. package/dist/core/substrate/cli-context-delivery.d.ts +0 -22
  629. package/dist/core/substrate/cli-context-delivery.js +0 -1
@@ -1,11 +1,11 @@
1
- var L=Object.defineProperty;var o=(n,e)=>L(n,"name",{value:e,configurable:!0});import{envNodeId as E,envProfileId as P}from"../shared/env.js";import{existsSync as K,statSync as F}from"node:fs";import{resolve as W}from"node:path";import{defineBranch as M,defineLeaf as h}from"../core/command.js";import{InputError as c,readStdinRaw as Y}from"../core/io.js";import{parseWhen as D,parseCadence as H,cadenceDisplay as x}from"../core/wake.js";import{loadProfileManifest as B,resolveProfileOperand as Z}from"../core/profiles/manifest.js";import{profilesStateBlock as N}from"../core/profiles/state-block.js";import{cliClient as l,rethrowAsCliError as u}from"./api-client.js";function U(n){return Z(n.trim()).profileId}o(U,"canonicalProfileId");function k(n,e){return e!==void 0&&e.trim()!==""?{tz:e,now:n}:{now:n}}o(k,"parseOpts");function b(n){const e={wake_in_past:'Pick a future instant \u2014 a positive duration ("5m","2h") or an ISO time later than now.',bad_when:'Use a duration ("5m","1h30m"), a zoned ISO ("2026-06-07T09:00:00Z"), or a bare ISO ("2026-06-07T09:00").',bad_cadence:'Use a duration ("6h"), a 5-field cron ("0 9 * * *"), or an @alias ("@daily").',unknown_zone:'Pass --tz with an IANA zone name (e.g. "America/New_York").',cadence_too_fast:'Use a cadence of at least 60s (e.g. "1m","5m","1h").'};throw new c({error:n.code,message:n.message,received:n.received,next:e[n.code]??"Fix the time value and retry."})}o(b,"throwTimeError");function J(n){const e=/^(\d+)(s|m|h)?$/.exec(n.trim());if(e===null)throw new c({error:"bad_run_timeout",message:`"${n}" is not a valid run timeout`,field:"run-timeout",next:'Use a duration like "90s", "5m", "1h", or bare seconds ("300").'});const t=Number(e[1]),r=e[2]==="h"?3600:e[2]==="m"?60:1,i=t*r;if(i<=0)throw new c({error:"bad_run_timeout",message:"run timeout must be positive",field:"run-timeout",next:'Use a positive duration, e.g. "5m".'});return i}o(J,"parseRunTimeout");function f(n){const e=typeof n=="string"&&n.trim()!==""?U(n):P();return e!==void 0&&e.trim()!==""?{profile:e}:void 0}o(f,"scopeQuery");function V(n){if(n===void 0)return process.cwd();const e=W(n);if(!K(e)||!F(e).isDirectory())throw new c({error:"bad_cwd",message:`${e} is not a directory`,field:"cwd",next:"Pass --cwd with an existing directory, or omit it to inherit this one."});return e}o(V,"resolveCwd");function G(n){const e=Array.isArray(n)?n.map(String):typeof n=="string"&&n!==""?[n]:[];if(e.length===0)return null;const t={};for(const r of e){const i=r.indexOf("="),a=i===-1?"":r.slice(0,i);if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(a))throw new c({error:"bad_env",message:`"${r}" is not KEY=VALUE`,field:"env",next:"Pass --env KEY=VALUE (repeat the flag per variable); KEY must be a shell-legal name."});t[a]=r.slice(i+1)}return JSON.stringify(t)}o(G,"resolveEnvJson");function p(n){const e=typeof n.cronId=="string"?n.cronId.trim():"";if(e==="")throw new c({error:"missing_cron_id",message:"a cron id is required",field:"cron-id",next:"Pass the cron id from `crtr cron list`."});return e}o(p,"requireCronId");function Q(n){if(n===null)return"never run";const e=n.finished??"",t=n.exit_code===0?"ok":n.exit_code===-1?"TIMEOUT":n.exit_code===null?"no exit":`exit ${n.exit_code}`;return e===""?t:`${t} ${e}`}o(Q,"healthDisplay");function X(n){const e=n.exit_code===null?"no exit":n.exit_code===-1?"TIMEOUT":`exit ${n.exit_code}`,t=n.duration_ms!==null?`${n.duration_ms}ms`:"\u2014";return`- ${n.started} \u2014 ${e}, ${t} \u2014 ${n.delivered??"(no disposition recorded)"}`}o(X,"renderRun");const ee=h({name:"add",description:"arm a scheduled command (bash on stdin; one of --at / --every)",whenToUse:"you need to arm a new bash schedule, rather than inspect or control an existing cron.",help:{name:"cron add",summary:"arm a scheduled command",params:[{kind:"stdin",name:"command",required:!0,constraint:"Arbitrary bash the daemon runs at each fire. Pipe it from a single-quoted heredoc (`<<'EOF'`) to preserve literal bytes. Runs via `bash -c` in this cwd with $CRTR_CRON_ID, $CRTR_CRON_NAME, and $CRTR_RUN_ID in the env ($CRTR_ANCHOR too when anchored). The scheduler does not interpret it as a node action; a bash predicate can decide whether this fire acts, not arm a native predicate trigger. A run that must branch on canvas state should call `crtr --json <cmd>` and read the leaf's declared outputs, rather than parsing the prose output."},{kind:"flag",name:"name",type:"string",required:!0,constraint:"Agent-legible label \u2014 how the cron shows in `cron list`."},{kind:"flag",name:"at",type:"string",required:!1,constraint:`One-shot: run the stored bash once at <when> \u2014 a duration ("90s","1h30m"), a zoned ISO ("2026-06-07T09:00:00Z"), or a bare ISO ("2026-06-07T09:00", interpreted in --tz else host zone). Exactly one of --at / --every is required. The row is deleted after its run unless the run's disposition pauses it.`},{kind:"flag",name:"every",type:"string",required:!1,constraint:'Recurring: run the stored bash at each cadence \u2014 a fixed interval ("6h" \u2014 first fire one interval from now), a 5-field cron ("0 9 * * *"), or an @alias ("@daily"). Each fire is another bash run. Minimum cadence 60s. Exactly one of --at / --every is required. Use this for a bash condition that only the local filesystem knows: if it exits 75, the next cadence re-checks it. A host platform can also re-check it early by calling the daemon\'s `POST /v1/crons/poke` endpoint.'},{kind:"flag",name:"tz",type:"string",required:!1,constraint:'IANA zone (e.g. "America/New_York") for a bare-ISO --at or a calendar --every. Defaults to the host zone.'},{kind:"flag",name:"on-output",type:"enum",choices:["silent","on-failure","always","on-change"],required:!1,constraint:"What each run does with its output. Defaults to on-change for a node:<id> sink; otherwise on-failure. silent: record in the run log, tell nobody \u2014 never escalate. on-failure: no success delivery; a failing run pauses the cron and spawns a node to deal with it. always: deliver stdout every run to --sink at --tier. on-change: deliver stdout only when it differs from the previous run's \u2014 the shape polling wants."},{kind:"flag",name:"sink",type:"string",required:!1,constraint:`Where always/on-change stdout deliveries go: "node:<id>" (an existing node's inbox \u2014 must exist now; gone/finalized at fire time is a failure), "spawn:<kind>" (each delivered output creates a self-finishing node with stdout as kickoff \u2014 a managed child while its creator exists, otherwise a terminal parentless root), or "human" (the humanloop inbox). Required by always/on-change, rejected otherwise.`},{kind:"flag",name:"tier",type:"enum",choices:["normal","urgent","critical"],required:!1,default:"urgent",constraint:"Inbox priority of a node-sink delivery. urgent (default): steers the target mid-turn, or wakes it when dormant \u2014 a fire is news the node asked for on a schedule, so it should land now. normal: waits for the running turn to settle. critical: aborts the running turn outright."},{kind:"flag",name:"expires",type:"string",required:!1,constraint:"Clock bound (same grammar as --at): the row is deleted once this instant passes. The self-limiting half of a poll-until pattern. For a held one-shot (last run exited 75), expiry bounds the wait for eligibility \u2014 the row is deleted UNFIRED, never fired blind at expiry."},{kind:"flag",name:"anchor",type:"string",required:!1,constraint:"Couple the cron's lifetime to a node: the row is deleted when that node is deleted. Without an anchor it outlives every node; it ends through its one-shot run, --expires, explicit cancellation, or self-cancellation from its bash."},{kind:"flag",name:"anchor-self",type:"bool",required:!1,constraint:"Anchor to the calling node ($CRTR_NODE_ID). Errors outside a node."},{kind:"flag",name:"cancel-on-wake",type:"bool",required:!1,constraint:"Also delete the row when the anchor node revives: use only when the cron belongs to that current dormancy, not for standing work. Requires an anchor; at most one cancel-on-wake cron per anchor \u2014 arming a second replaces the anchor\u2019s pending one."},{kind:"flag",name:"run-timeout",type:"string",required:!1,default:"5m",constraint:'Kill a run that exceeds this ("90s","5m","1h", or bare seconds). A timeout is a failure.'},{kind:"flag",name:"overlap",type:"enum",choices:["skip","queue","replace"],required:!1,default:"skip",constraint:"A fire landing while the previous run is still going. skip (default): log the skip, do not start a second run. queue: run once more after the current finishes (depth 1; further fires collapse). replace: kill the running instance and start fresh."},{kind:"flag",name:"cwd",type:"path",required:!1,constraint:"Directory every run executes in. Defaults to this one, must exist now, and is snapshotted onto the row \u2014 a cron never re-resolves its context."},{kind:"flag",name:"env",type:"string",required:!1,repeatable:!0,constraint:"KEY=VALUE added to every run's env (repeat per variable). Snapshotted at arm time; overlays the operational env, alongside $CRTR_CRON_ID/$CRTR_CRON_NAME/$CRTR_RUN_ID."},{kind:"flag",name:"profile",type:"string",required:!1,constraint:"Profile the runs resolve config and credentials under, and \u2014 under --scope profile \u2014 the profile this cron lists and cancels within. A profile id or manifest name from the <profiles> list below; a close-enough name resolves to the nearest profile. Defaults to the calling node's ($CRTR_PROFILE_ID)."},{kind:"flag",name:"scope",type:"enum",choices:["profile","global"],required:!1,constraint:"Who lists and cancels this cron: profile (default when a profile is resolved) confines it to that profile; global makes it canvas-home-wide, visible from every profile."}],output:[{name:"cron_id",type:"string",required:!0,constraint:"The server-minted armed cron id \u2014 inspect with `cron show <cron_id>`, cancel with `cron cancel <cron_id>`."},{name:"fire_at",type:"string",required:!0,constraint:"Next fire (UTC ISO)."},{name:"recur",type:"string",required:!0,constraint:'Cadence display; "none" for a one-shot.'}],outputKind:"object",dynamicState:o(()=>N(),"dynamicState"),effects:["Arms one cron row (daemon-run; survives this process and every node).","Each fire runs the command via bash in this cwd and records a run-log entry.","A failing run (nonzero exit, timeout, undeliverable sink) pauses the cron and spawns a node to deal with it \u2014 unless --on-output silent."]},run:o(async n=>{let e=typeof n.command=="string"?n.command.trim():"";if(e===""&&(e=(await Y()).trim()),e==="")throw new c({error:"missing_command",message:"no command on stdin",field:"command",next:"Pipe the bash command on stdin (e.g. from a single-quoted heredoc)."});const t=typeof n.name=="string"?n.name.trim():"";if(t==="")throw new c({error:"missing_name",message:"--name is required",field:"name",next:"Pass --name <label>."});const r=typeof n.at=="string"&&n.at!==""?n.at:void 0,i=typeof n.every=="string"&&n.every!==""?n.every:void 0;if(r===void 0==(i===void 0))throw new c({error:"bad_schedule",message:r===void 0?"one of --at / --every is required":"--at and --every are mutually exclusive",next:"Pass exactly one: --at <when> for a one-shot, or --every <cadence> for a recurrence."});const a=typeof n.tz=="string"&&n.tz!==""?n.tz:void 0,m=new Date;let d,$=null;if(i!==void 0){const s=H(i,k(m,a));"error"in s&&b(s.error),d=s.firstFireAt,$=s.recur}else{const s=D(r,k(m,a));"error"in s&&b(s.error),d=s.fireAt}let T=null;const R=typeof n.expires=="string"&&n.expires!==""?n.expires:void 0;if(R!==void 0){const s=D(R,k(m,a));"error"in s&&b(s.error),T=s.fireAt}const q=typeof n.anchor=="string"&&n.anchor!==""?n.anchor:void 0,A=n.anchorSelf===!0;if(q!==void 0&&A)throw new c({error:"bad_anchor",message:"--anchor and --anchor-self are mutually exclusive",next:"Pass one anchor or the other."});let g=q??null;if(A){const s=E();if(s===void 0||s==="")throw new c({error:"no_self_node",message:"--anchor-self requires a calling node ($CRTR_NODE_ID is unset)",next:"Run from inside a node, or pass --anchor <node-id> explicitly."});g=s}const C=n.cancelOnWake===!0;if(C&&g===null)throw new c({error:"cancel_on_wake_needs_anchor",message:"--cancel-on-wake requires an anchor",next:"Add --anchor <node-id> or --anchor-self."});const I=typeof n.runTimeout=="string"&&n.runTimeout!==""?J(n.runTimeout):void 0,z=V(typeof n.cwd=="string"&&n.cwd!==""?n.cwd:void 0),j=G(n.env),O=typeof n.profile=="string"&&n.profile!==""?n.profile:void 0,w=P(),y=O!==void 0?U(O):w!==void 0&&w.trim()!==""?B(w).profileId:void 0,S=n.scope??(y!==void 0?"profile":"global");if(S==="profile"&&y===void 0)throw new c({error:"scope_needs_profile",message:"--scope profile names the profile this cron lists and cancels within, but no profile is resolved",field:"profile",next:"Pass --profile <id>, or use --scope global for a canvas-home-wide cron."});const _=E(),v=await l().armCron({name:t,command:e,fire_at:d,recur:$,tz:a??null,created_by:_!==void 0&&_!==""?_:null,cwd:z,env_json:j,profile:y??null,scope:S,on_output:n.onOutput,sink:typeof n.sink=="string"&&n.sink!==""?n.sink:null,tier:n.tier??"urgent",expires_at:T,anchor_node:g,cancel_on_wake:C,...I!==void 0?{run_timeout_s:I}:{},overlap:n.overlap??"skip"}).catch(u);return{cron_id:v.cron_id,fire_at:v.fire_at,recur:x(v.recur)}},"run"),render:o(n=>{const e=String(n.recur),t=e==="none"?"one-shot (row deleted after the run)":e;return`Cron armed \u2014 ${String(n.cron_id)}.
1
+ var L=Object.defineProperty;var o=(n,e)=>L(n,"name",{value:e,configurable:!0});import{envNodeId as E,envProfileId as D}from"../shared/env.js";import{existsSync as K,statSync as F}from"node:fs";import{resolve as W}from"node:path";import{defineBranch as M,defineLeaf as h}from"../core/command.js";import{InputError as c,readStdinRaw as Y}from"../core/io.js";import{parseWhen as P,parseCadence as H,cadenceDisplay as x}from"../core/wake.js";import{profilesStateBlock as N,resolveProfileViaDaemon as B}from"./profile/daemon-profiles.js";import{cliClient as l,rethrowAsCliError as u}from"./api-client.js";async function U(n){return(await B(n.trim())).id}o(U,"canonicalProfileId");function k(n,e){return e!==void 0&&e.trim()!==""?{tz:e,now:n}:{now:n}}o(k,"parseOpts");function b(n){const e={wake_in_past:'Pick a future instant \u2014 a positive duration ("5m","2h") or an ISO time later than now.',bad_when:'Use a duration ("5m","1h30m"), a zoned ISO ("2026-06-07T09:00:00Z"), or a bare ISO ("2026-06-07T09:00").',bad_cadence:'Use a duration ("6h"), a 5-field cron ("0 9 * * *"), or an @alias ("@daily").',unknown_zone:'Pass --tz with an IANA zone name (e.g. "America/New_York").',cadence_too_fast:'Use a cadence of at least 60s (e.g. "1m","5m","1h").'};throw new c({error:n.code,message:n.message,received:n.received,next:e[n.code]??"Fix the time value and retry."})}o(b,"throwTimeError");function V(n){const e=/^(\d+)(s|m|h)?$/.exec(n.trim());if(e===null)throw new c({error:"bad_run_timeout",message:`"${n}" is not a valid run timeout`,field:"run-timeout",next:'Use a duration like "90s", "5m", "1h", or bare seconds ("300").'});const t=Number(e[1]),r=e[2]==="h"?3600:e[2]==="m"?60:1,i=t*r;if(i<=0)throw new c({error:"bad_run_timeout",message:"run timeout must be positive",field:"run-timeout",next:'Use a positive duration, e.g. "5m".'});return i}o(V,"parseRunTimeout");function f(n){const e=n??D();return e!==void 0&&e.trim()!==""?{profile:e}:void 0}o(f,"scopeQuery");function Z(n){if(n===void 0)return process.cwd();const e=W(n);if(!K(e)||!F(e).isDirectory())throw new c({error:"bad_cwd",message:`${e} is not a directory`,field:"cwd",next:"Pass --cwd with an existing directory, or omit it to inherit this one."});return e}o(Z,"resolveCwd");function J(n){const e=Array.isArray(n)?n.map(String):typeof n=="string"&&n!==""?[n]:[];if(e.length===0)return null;const t={};for(const r of e){const i=r.indexOf("="),s=i===-1?"":r.slice(0,i);if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(s))throw new c({error:"bad_env",message:`"${r}" is not KEY=VALUE`,field:"env",next:"Pass --env KEY=VALUE (repeat the flag per variable); KEY must be a shell-legal name."});t[s]=r.slice(i+1)}return JSON.stringify(t)}o(J,"resolveEnvJson");function p(n){const e=typeof n.cronId=="string"?n.cronId.trim():"";if(e==="")throw new c({error:"missing_cron_id",message:"a cron id is required",field:"cron-id",next:"Pass the cron id from `crtr cron list`."});return e}o(p,"requireCronId");function G(n){if(n===null)return"never run";const e=n.finished??"",t=n.exit_code===0?"ok":n.exit_code===-1?"TIMEOUT":n.exit_code===null?"no exit":`exit ${n.exit_code}`;return e===""?t:`${t} ${e}`}o(G,"healthDisplay");function Q(n){const e=n.exit_code===null?"no exit":n.exit_code===-1?"TIMEOUT":`exit ${n.exit_code}`,t=n.duration_ms!==null?`${n.duration_ms}ms`:"\u2014";return`- ${n.started} \u2014 ${e}, ${t} \u2014 ${n.delivered??"(no disposition recorded)"}`}o(Q,"renderRun");const X=h({name:"add",description:"arm a scheduled command (bash on stdin; one of --at / --every)",whenToUse:"you need to arm a new bash schedule, rather than inspect or control an existing cron.",help:{name:"cron add",summary:"arm a scheduled command",params:[{kind:"stdin",name:"command",required:!0,constraint:"Arbitrary bash the daemon runs at each fire. Pipe it from a single-quoted heredoc (`<<'EOF'`) to preserve literal bytes. Runs via `bash -c` in this cwd with $CRTR_CRON_ID, $CRTR_CRON_NAME, and $CRTR_RUN_ID in the env ($CRTR_ANCHOR too when anchored). The scheduler does not interpret it as a node action; a bash predicate can decide whether this fire acts, not arm a native predicate trigger. A run that must branch on canvas state should call `crtr --json <cmd>` and read the leaf's declared outputs, rather than parsing the prose output."},{kind:"flag",name:"name",type:"string",required:!0,constraint:"Agent-legible label \u2014 how the cron shows in `cron list`."},{kind:"flag",name:"at",type:"string",required:!1,constraint:`One-shot: run the stored bash once at <when> \u2014 a duration ("90s","1h30m"), a zoned ISO ("2026-06-07T09:00:00Z"), or a bare ISO ("2026-06-07T09:00", interpreted in --tz else host zone). Exactly one of --at / --every is required. The row is deleted after its run unless the run's disposition pauses it.`},{kind:"flag",name:"every",type:"string",required:!1,constraint:'Recurring: run the stored bash at each cadence \u2014 a fixed interval ("6h" \u2014 first fire one interval from now), a 5-field cron ("0 9 * * *"), or an @alias ("@daily"). Each fire is another bash run. Minimum cadence 60s. Exactly one of --at / --every is required. Use this for a bash condition that only the local filesystem knows: if it exits 75, the next cadence re-checks it. A host platform can also re-check it early by calling the daemon\'s `POST /v1/crons/poke` endpoint.'},{kind:"flag",name:"tz",type:"string",required:!1,constraint:'IANA zone (e.g. "America/New_York") for a bare-ISO --at or a calendar --every. Defaults to the host zone.'},{kind:"flag",name:"on-output",type:"enum",choices:["silent","on-failure","always","on-change"],required:!1,constraint:"What each run does with its output. Defaults to on-change for a node:<id> sink; otherwise on-failure. silent: record in the run log, tell nobody \u2014 never escalate. on-failure: no success delivery; a failing run pauses the cron and spawns a node to deal with it. always: deliver stdout every run to --sink at --tier. on-change: deliver stdout only when it differs from the previous run's \u2014 the shape polling wants."},{kind:"flag",name:"sink",type:"string",required:!1,constraint:`Where always/on-change stdout deliveries go: "node:<id>" (an existing node's inbox \u2014 must exist now; gone/finalized at fire time is a failure), "spawn:<kind>" (each delivered output creates a self-finishing node with stdout as kickoff \u2014 a managed child while its creator exists, otherwise a terminal parentless root), or "human" (the humanloop inbox). Required by always/on-change, rejected otherwise.`},{kind:"flag",name:"tier",type:"enum",choices:["normal","urgent","critical"],required:!1,default:"urgent",constraint:"Inbox priority of a node-sink delivery. urgent (default): steers the target mid-turn, or wakes it when dormant \u2014 a fire is news the node asked for on a schedule, so it should land now. normal: waits for the running turn to settle. critical: aborts the running turn outright."},{kind:"flag",name:"expires",type:"string",required:!1,constraint:"Clock bound (same grammar as --at): the row is deleted once this instant passes. The self-limiting half of a poll-until pattern. For a held one-shot (last run exited 75), expiry bounds the wait for eligibility \u2014 the row is deleted UNFIRED, never fired blind at expiry."},{kind:"flag",name:"anchor",type:"string",required:!1,constraint:"Couple the cron's lifetime to a node: the row is deleted when that node is deleted. Without an anchor it outlives every node; it ends through its one-shot run, --expires, explicit cancellation, or self-cancellation from its bash."},{kind:"flag",name:"anchor-self",type:"bool",required:!1,constraint:"Anchor to the calling node ($CRTR_NODE_ID). Errors outside a node."},{kind:"flag",name:"cancel-on-wake",type:"bool",required:!1,constraint:"Also delete the row when the anchor node revives: use only when the cron belongs to that current dormancy, not for standing work. Requires an anchor; at most one cancel-on-wake cron per anchor \u2014 arming a second replaces the anchor\u2019s pending one."},{kind:"flag",name:"run-timeout",type:"string",required:!1,default:"5m",constraint:'Kill a run that exceeds this ("90s","5m","1h", or bare seconds). A timeout is a failure.'},{kind:"flag",name:"overlap",type:"enum",choices:["skip","queue","replace"],required:!1,default:"skip",constraint:"A fire landing while the previous run is still going. skip (default): log the skip, do not start a second run. queue: run once more after the current finishes (depth 1; further fires collapse). replace: kill the running instance and start fresh."},{kind:"flag",name:"cwd",type:"path",required:!1,constraint:"Directory every run executes in. Defaults to this one, must exist now, and is snapshotted onto the row \u2014 a cron never re-resolves its context."},{kind:"flag",name:"env",type:"string",required:!1,repeatable:!0,constraint:"KEY=VALUE added to every run's env (repeat per variable). Snapshotted at arm time; overlays the operational env, alongside $CRTR_CRON_ID/$CRTR_CRON_NAME/$CRTR_RUN_ID."},{kind:"flag",name:"profile",type:"string",required:!1,constraint:"Profile the runs resolve config and credentials under, and \u2014 under --scope profile \u2014 the profile this cron lists and cancels within. A profile id or manifest name from the <profiles> list below; a close-enough name resolves to the nearest profile. Defaults to the calling node's ($CRTR_PROFILE_ID)."},{kind:"flag",name:"scope",type:"enum",choices:["profile","global"],required:!1,constraint:"Who lists and cancels this cron: profile (default when a profile is resolved) confines it to that profile; global makes it canvas-home-wide, visible from every profile."}],output:[{name:"cron_id",type:"string",required:!0,constraint:"The server-minted armed cron id \u2014 inspect with `cron show <cron_id>`, cancel with `cron cancel <cron_id>`."},{name:"fire_at",type:"string",required:!0,constraint:"Next fire (UTC ISO)."},{name:"recur",type:"string",required:!0,constraint:'Cadence display; "none" for a one-shot.'}],outputKind:"object",effects:["Arms one cron row (daemon-run; survives this process and every node).","Each fire runs the command via bash in this cwd and records a run-log entry.","A failing run (nonzero exit, timeout, undeliverable sink) pauses the cron and spawns a node to deal with it \u2014 unless --on-output silent."]},helpState:{leaf:N},run:o(async n=>{let e=typeof n.command=="string"?n.command.trim():"";if(e===""&&(e=(await Y()).trim()),e==="")throw new c({error:"missing_command",message:"no command on stdin",field:"command",next:"Pipe the bash command on stdin (e.g. from a single-quoted heredoc)."});const t=typeof n.name=="string"?n.name.trim():"";if(t==="")throw new c({error:"missing_name",message:"--name is required",field:"name",next:"Pass --name <label>."});const r=typeof n.at=="string"&&n.at!==""?n.at:void 0,i=typeof n.every=="string"&&n.every!==""?n.every:void 0;if(r===void 0==(i===void 0))throw new c({error:"bad_schedule",message:r===void 0?"one of --at / --every is required":"--at and --every are mutually exclusive",next:"Pass exactly one: --at <when> for a one-shot, or --every <cadence> for a recurrence."});const s=typeof n.tz=="string"&&n.tz!==""?n.tz:void 0,m=new Date;let d,$=null;if(i!==void 0){const a=H(i,k(m,s));"error"in a&&b(a.error),d=a.firstFireAt,$=a.recur}else{const a=P(r,k(m,s));"error"in a&&b(a.error),d=a.fireAt}let T=null;const R=typeof n.expires=="string"&&n.expires!==""?n.expires:void 0;if(R!==void 0){const a=P(R,k(m,s));"error"in a&&b(a.error),T=a.fireAt}const q=typeof n.anchor=="string"&&n.anchor!==""?n.anchor:void 0,A=n.anchorSelf===!0;if(q!==void 0&&A)throw new c({error:"bad_anchor",message:"--anchor and --anchor-self are mutually exclusive",next:"Pass one anchor or the other."});let g=q??null;if(A){const a=E();if(a===void 0||a==="")throw new c({error:"no_self_node",message:"--anchor-self requires a calling node ($CRTR_NODE_ID is unset)",next:"Run from inside a node, or pass --anchor <node-id> explicitly."});g=a}const C=n.cancelOnWake===!0;if(C&&g===null)throw new c({error:"cancel_on_wake_needs_anchor",message:"--cancel-on-wake requires an anchor",next:"Add --anchor <node-id> or --anchor-self."});const O=typeof n.runTimeout=="string"&&n.runTimeout!==""?V(n.runTimeout):void 0,z=Z(typeof n.cwd=="string"&&n.cwd!==""?n.cwd:void 0),j=J(n.env),I=typeof n.profile=="string"&&n.profile!==""?n.profile:void 0,w=D(),y=I!==void 0?await U(I):w!==void 0&&w.trim()!==""?w:void 0,S=n.scope??(y!==void 0?"profile":"global");if(S==="profile"&&y===void 0)throw new c({error:"scope_needs_profile",message:"--scope profile names the profile this cron lists and cancels within, but no profile is resolved",field:"profile",next:"Pass --profile <id>, or use --scope global for a canvas-home-wide cron."});const _=E(),v=await l().armCron({name:t,command:e,fire_at:d,recur:$,tz:s??null,created_by:_!==void 0&&_!==""?_:null,cwd:z,env_json:j,profile:y??null,scope:S,on_output:n.onOutput,sink:typeof n.sink=="string"&&n.sink!==""?n.sink:null,tier:n.tier??"urgent",expires_at:T,anchor_node:g,cancel_on_wake:C,...O!==void 0?{run_timeout_s:O}:{},overlap:n.overlap??"skip"}).catch(u);return{cron_id:v.cron_id,fire_at:v.fire_at,recur:x(v.recur)}},"run"),render:o(n=>{const e=String(n.recur),t=e==="none"?"one-shot (row deleted after the run)":e;return`Cron armed \u2014 ${String(n.cron_id)}.
2
2
 
3
3
  - next fire: ${String(n.fire_at)}
4
4
  - cadence: ${t}
5
5
 
6
- It runs daemon-side; nothing more to do here. Inspect it with \`crtr cron show ${String(n.cron_id)}\`; cancel with \`crtr cron cancel ${String(n.cron_id)}\` from its profile, or pass \`--profile <profile>\` from another profile.`},"render")}),ne=h({name:"list",description:"what is scheduled, with next fire and recent health",whenToUse:"you want to see every cron in scope \u2014 to find an id to show/cancel, to check what fires next and what is paused, or to spot one whose last run failed.",help:{name:"cron list",summary:"every cron in scope, next-fire order",params:[],output:[{name:"crons",type:"object[]",required:!0,constraint:"One row per cron: cron_id, name, fire_at (next fire, UTC), recur (cadence display), state (active|paused), held (true after an exit-75 gate; a recurring row re-checks at fire_at or when a host platform calls `POST /v1/crons/poke`, while a one-shot needs that call or `crtr cron run <cron-id>`), on_output, sink, expires_at, last_run (the most recent settled run: finished, exit_code, delivered \u2014 null if it never ran)."}],outputKind:"object",effects:["None. Read-only."]},run:o(async()=>({crons:(await l().listCrons(f()).catch(u)).map(e=>({cron_id:e.cron_id,name:e.name,fire_at:e.fire_at,recur:x(e.recur),state:e.state,held:e.held,on_output:e.on_output,sink:e.sink,expires_at:e.expires_at,last_run:e.last_run}))}),"run"),render:o(n=>{const e=n.crons;if(e.length===0)return"No crons armed.";const t=e.map(r=>{const i=[`next ${r.fire_at}`,r.recur==="none"?"one-shot":r.recur,r.sink!==null?`${r.on_output} \u2192 ${r.sink}`:r.on_output,`last: ${Q(r.last_run)}`];r.expires_at!==null&&i.push(`expires ${r.expires_at}`);const a=r.state==="paused"?r.held?" [PAUSED (held) \u2014 resume or cancel]":" [PAUSED \u2014 resume or cancel]":r.held?r.recur==="none"?` [HELD \u2014 last run exited 75; run manually with \`crtr cron run ${r.cron_id}\`, or wait for a host platform to call \`POST /v1/crons/poke\`]`:" [HELD \u2014 last run exited 75; re-checks at the next cadence or when a host platform calls `POST /v1/crons/poke`]":"";return`- ${r.name} (${r.cron_id}) \u2014 ${i.join(", ")}${a}`});return`${e.length} cron(s):
6
+ It runs daemon-side; nothing more to do here. Inspect it with \`crtr cron show ${String(n.cron_id)}\`; cancel with \`crtr cron cancel ${String(n.cron_id)}\` from its profile, or pass \`--profile <profile>\` from another profile.`},"render")}),ee=h({name:"list",description:"what is scheduled, with next fire and recent health",whenToUse:"you want to see every cron in scope \u2014 to find an id to show/cancel, to check what fires next and what is paused, or to spot one whose last run failed.",help:{name:"cron list",summary:"every cron in scope, next-fire order",params:[],output:[{name:"crons",type:"object[]",required:!0,constraint:"One row per cron: cron_id, name, fire_at (next fire, UTC), recur (cadence display), state (active|paused), held (true after an exit-75 gate; a recurring row re-checks at fire_at or when a host platform calls `POST /v1/crons/poke`, while a one-shot needs that call or `crtr cron run <cron-id>`), on_output, sink, expires_at, last_run (the most recent settled run: finished, exit_code, delivered \u2014 null if it never ran)."}],outputKind:"object",effects:["None. Read-only."]},run:o(async()=>({crons:(await l().listCrons(f()).catch(u)).map(e=>({cron_id:e.cron_id,name:e.name,fire_at:e.fire_at,recur:x(e.recur),state:e.state,held:e.held,on_output:e.on_output,sink:e.sink,expires_at:e.expires_at,last_run:e.last_run}))}),"run"),render:o(n=>{const e=n.crons;if(e.length===0)return"No crons armed.";const t=e.map(r=>{const i=[`next ${r.fire_at}`,r.recur==="none"?"one-shot":r.recur,r.sink!==null?`${r.on_output} \u2192 ${r.sink}`:r.on_output,`last: ${G(r.last_run)}`];r.expires_at!==null&&i.push(`expires ${r.expires_at}`);const s=r.state==="paused"?r.held?" [PAUSED (held) \u2014 resume or cancel]":" [PAUSED \u2014 resume or cancel]":r.held?r.recur==="none"?` [HELD \u2014 last run exited 75; run manually with \`crtr cron run ${r.cron_id}\`, or wait for a host platform to call \`POST /v1/crons/poke\`]`:" [HELD \u2014 last run exited 75; re-checks at the next cadence or when a host platform calls `POST /v1/crons/poke`]":"";return`- ${r.name} (${r.cron_id}) \u2014 ${i.join(", ")}${s}`});return`${e.length} cron(s):
7
7
 
8
8
  ${t.join(`
9
- `)}`},"render")}),re=h({name:"show",description:"one cron: full config plus its run history",whenToUse:'you want to inspect one cron \u2014 its exact command and settings, and what its recent runs actually did (exit codes, output heads, where deliveries went). The run log is how you answer "did it silently stop working".',help:{name:"cron show",summary:"one cron with its run-log ring",params:[{kind:"positional",name:"cron-id",required:!0,constraint:"The cron to inspect (from `cron list`)."}],output:[{name:"cron",type:"object",required:!0,constraint:"The full cron config: command, schedule, snapshotted context (cwd, profile, scope, env var names \u2014 values stay server-side), disposition, sink, coupling, state."},{name:"runs",type:"object[]",required:!0,constraint:"The bounded run log, most recent first: started, exit_code (-1 = timeout kill), duration_ms, stdout/stderr heads, delivered (what the sink did, or why it did not)."}],outputKind:"object",effects:["None. Read-only."]},run:o(async n=>{const e=p(n),t=await l().showCron(e,f()).catch(r=>u(r,"Check the id against `crtr cron list`."));return{cron:t.cron,runs:t.runs}},"run"),render:o(n=>{const e=n.cron,t=n.runs,r=e.recur!==null?x(e.recur):e.held?"one-shot":`one-shot at ${e.fire_at}`,i=e.tz!==null?` (tz ${e.tz})`:"",a=e.held?e.recur!==null?`- schedule: ${r}${i} \u2014 next re-check ${e.fire_at} (held \u2014 also re-checks when a host platform calls \`POST /v1/crons/poke\`; last run exited 75)`:e.expires_at!==null?`- schedule: ${r}${i} \u2014 no scheduled re-check \u2014 expires ${e.expires_at} (held \u2014 run manually with \`crtr cron run ${e.cron_id}\`, or wait for a host platform to call \`POST /v1/crons/poke\`; deleted unfired at expiry)`:`- schedule: ${r}${i} \u2014 no scheduled re-check (held \u2014 run manually with \`crtr cron run ${e.cron_id}\`, or wait for a host platform to call \`POST /v1/crons/poke\`)`:`- schedule: ${r}${i} \u2014 next fire ${e.fire_at}`,m=e.state==="paused"?e.held?" \u2014 PAUSED (held)":" \u2014 PAUSED":e.held?" \u2014 HELD":"",d=[`# ${e.name} (${e.cron_id})${m}`,"",a,`- disposition: ${e.on_output}${e.sink!==null?` \u2192 ${e.sink} (tier ${e.tier})`:""}`,`- context: cwd ${e.cwd}${e.profile!==null?`, profile ${e.profile}`:""}, scope ${e.scope}${e.env_keys.length>0?`, env ${e.env_keys.join(", ")}`:""}`,`- run: timeout ${e.run_timeout_s}s, overlap ${e.overlap}${e.run_state==="running"?" \u2014 a run is IN FLIGHT now":""}`];return e.expires_at!==null&&d.push(`- expires: ${e.expires_at}`),e.anchor_node!==null&&d.push(`- anchored to node ${e.anchor_node}${e.cancel_on_wake?" (canceled when it wakes)":""}`),e.created_by!==null&&d.push(`- created by node ${e.created_by}`),d.push("","## Command","","```bash",e.command,"```","",`## Runs (most recent first, last ${t.length})`,""),d.push(...t.length>0?t.map(X):["(no runs yet)"]),d.join(`
10
- `)},"render")}),te=h({name:"run",description:"execute now, out of band \u2014 does not advance the schedule",whenToUse:"you need the stored command to execute now because its own behavior has not been exercised, or you fixed the command behind a paused cron. This is not a scheduled-delivery check: once `cron add` has armed a valid row, trust the clock and node delivery rather than firing it again for reassurance.",help:{name:"cron run",summary:"fire one cron now, out of band, and report the settled run",params:[{kind:"positional",name:"cron-id",required:!0,constraint:"The cron to run (from `cron list`)."}],output:[{name:"exit_code",type:"number",required:!1,constraint:"The run's exit code (-1 = timeout kill; null when the process failed to start)."},{name:"duration_ms",type:"number",required:!0,constraint:"Wall-clock run time."},{name:"stdout_head",type:"string",required:!1,constraint:"Captured stdout (head)."},{name:"stderr_head",type:"string",required:!1,constraint:"Captured stderr (head)."},{name:"delivered",type:"string",required:!0,constraint:"What the sink did with the output, or why it did not."}],outputKind:"object",effects:["Runs the command NOW (blocking this call until it settles) and records the run in the cron's run log.","Delivers per the cron's disposition; never advances the schedule, consumes a one-shot, or escalates a failure."]},run:o(async n=>{const e=p(n),t=await l().showCron(e,f()).catch(a=>u(a,"Check the id against `crtr cron list`."));return await l({timeoutMs:t.cron.run_timeout_s*1e3+15e3}).runCron(e,f()).catch(a=>u(a))},"run"),render:o(n=>{const e=n,r=[`Run settled \u2014 exit ${e.exit_code===null?"no exit (process error)":e.exit_code===-1?"-1 (TIMEOUT)":String(e.exit_code)}, ${e.duration_ms??"?"}ms.`,"",`- delivered: ${e.delivered??"(nothing recorded)"}`];return e.stdout_head!==null&&r.push("","## stdout (head)","",e.stdout_head),e.stderr_head!==null&&r.push("","## stderr (head)","",e.stderr_head),r.join(`
11
- `)},"render")}),oe=h({name:"pause",description:"stop firing without losing config or history",whenToUse:"a cron should stop firing for now but its config and run history are worth keeping \u2014 a job under investigation, or a poll to silence temporarily. (A failing cron pauses itself.)",help:{name:"cron pause",summary:"pause one cron",params:[{kind:"positional",name:"cron-id",required:!0,constraint:"The cron to pause (from `cron list`)."}],output:[],outputKind:"object",effects:["Stops the cron firing until `cron resume`. An in-flight run settles normally; the row just stops being due."]},run:o(async n=>{const e=p(n);return await l().pauseCron(e,f()).catch(t=>u(t,"Check the id against `crtr cron list`.")),{}},"run"),render:o(()=>"Cron paused.","render")}),ie=h({name:"resume",description:"re-arm a paused cron",whenToUse:"a paused cron should fire again \u2014 you fixed what made it fail (verify first with `cron run`), or its silence is over. A fire that came due while paused fires immediately on resume.",help:{name:"cron resume",summary:"resume one paused cron",params:[{kind:"positional",name:"cron-id",required:!0,constraint:"The cron to resume (from `cron list`)."}],output:[{name:"fire_at",type:"string",required:!0,constraint:"Next fire \u2014 if already past, it fires on the next daemon tick."}],outputKind:"object",effects:["Re-arms the cron. A past-due fire_at fires immediately (instant verification after un-wedging); a recurrence then advances naturally."]},run:o(async n=>{const e=p(n);return{fire_at:(await l().resumeCron(e,f()).catch(r=>u(r,"Check the id against `crtr cron list`."))).fire_at}},"run"),render:o(n=>`Cron resumed \u2014 next fire ${String(n.fire_at)} (if that is already past, it fires on the next tick).`,"render")}),se=h({name:"cancel",description:"remove one cron (idempotent)",whenToUse:"a scheduled command should stop firing \u2014 it is obsolete, wrong, or its purpose is served. Idempotent, so a script can safely self-cancel with `crtr cron cancel $CRTR_CRON_ID` even if a racing cancel got there first.",help:{name:"cron cancel",summary:"remove one cron",params:[{kind:"positional",name:"cron-id",required:!0,constraint:"The cron to remove (from `cron list`, or $CRTR_CRON_ID inside a run)."},{kind:"flag",name:"profile",type:"string",required:!1,constraint:"Profile scope to cancel within. Defaults to the calling node's profile; pass the profile id or manifest name for a cron armed under another profile."}],output:[],outputKind:"object",dynamicState:o(()=>N(),"dynamicState"),effects:["Deletes the cron row and its run history; kills any in-flight run. A no-op if already gone."]},run:o(async n=>{const e=p(n),t=process.env.CRTR_RUN_ID;return await l().cancelCron(e,{...f(n.profile),...t===void 0||t===""?{}:{run_id:t}}).catch(r=>u(r,"Check the id against `crtr cron list`.")),{}},"run"),render:o(()=>"Cron canceled.","render")});function ye(){return M({name:"cron",rootEntry:{concept:"one scheduling primitive \u2014 run this bash command, then",desc:"arm, inspect, and control scheduled bash commands the daemon runs on the clock",useWhen:"bash must run later or on a cadence, rather than waiting for a runtime-delivered wake."},help:{name:"cron",summary:"scheduled bash commands, daemon-run",model:"A cron stores and daemon-runs arbitrary bash at its clock. Use ordinary bash for ordinary work; choose node:<id> for stdout as inbox information to an existing node (its disposition defaults to on-change), an existing node's lifecycle fresh-revive action in the bash for a clean re-check without inbox output, or spawn:<kind> to route each output-producing fire to a fresh self-finishing node (a managed child while its creator exists, otherwise a terminal parentless root). Per-fire fresh agent work belongs on the existing spawn:<kind> sink, not an independent resident-root birth. A gate is bash, at the top of the command. Exit 0 after deciding not to act and the occurrence is simply spent \u2014 right when the recurrence is your polling cadence. Exit 75 holds the row. A held recurring row re-checks at its next natural slot, or when a host platform calls the daemon's `POST /v1/crons/poke` endpoint. A held one-shot has no scheduled re-check: it stays held until that endpoint is called, or you execute its bash explicitly with `crtr cron run <cron-id>` (which does not unhold or reschedule the row). A local filesystem condition does not call the daemon, so use --every for that condition or run the cron explicitly after it changes. Any other nonzero exit is a real failure and escalates per --on-output. Keep gate errors nonzero so a broken probe is loud instead of silently held. Beyond the exit-code contract, cron has no native predicate-trigger semantics. --at runs once and deletes the row unless the run's disposition pauses it; --every runs each cadence. Output disposition (--on-output) records silently, escalates failures, or routes stdout to its --sink. Anchor a cron to delete it with a node; --cancel-on-wake instead deletes it when the anchor wakes. An unanchored recurring cron ends through --expires, explicit cancellation, or self-cancellation from its bash. Every run lands in a bounded per-cron run log (`cron show`); cwd, env, and profile are snapshotted at arm time, and --scope controls who lists and cancels it."},children:[ee,ne,re,te,oe,ie,se]})}o(ye,"registerCron");export{ye as registerCron};
9
+ `)}`},"render")}),ne=h({name:"show",description:"one cron: full config plus its run history",whenToUse:'you want to inspect one cron \u2014 its exact command and settings, and what its recent runs actually did (exit codes, output heads, where deliveries went). The run log is how you answer "did it silently stop working".',help:{name:"cron show",summary:"one cron with its run-log ring",params:[{kind:"positional",name:"cron-id",required:!0,constraint:"The cron to inspect (from `cron list`)."}],output:[{name:"cron",type:"object",required:!0,constraint:"The full cron config: command, schedule, snapshotted context (cwd, profile, scope, env var names \u2014 values stay server-side), disposition, sink, coupling, state."},{name:"runs",type:"object[]",required:!0,constraint:"The bounded run log, most recent first: started, exit_code (-1 = timeout kill), duration_ms, stdout/stderr heads, delivered (what the sink did, or why it did not)."}],outputKind:"object",effects:["None. Read-only."]},run:o(async n=>{const e=p(n),t=await l().showCron(e,f()).catch(r=>u(r,"Check the id against `crtr cron list`."));return{cron:t.cron,runs:t.runs}},"run"),render:o(n=>{const e=n.cron,t=n.runs,r=e.recur!==null?x(e.recur):e.held?"one-shot":`one-shot at ${e.fire_at}`,i=e.tz!==null?` (tz ${e.tz})`:"",s=e.held?e.recur!==null?`- schedule: ${r}${i} \u2014 next re-check ${e.fire_at} (held \u2014 also re-checks when a host platform calls \`POST /v1/crons/poke\`; last run exited 75)`:e.expires_at!==null?`- schedule: ${r}${i} \u2014 no scheduled re-check \u2014 expires ${e.expires_at} (held \u2014 run manually with \`crtr cron run ${e.cron_id}\`, or wait for a host platform to call \`POST /v1/crons/poke\`; deleted unfired at expiry)`:`- schedule: ${r}${i} \u2014 no scheduled re-check (held \u2014 run manually with \`crtr cron run ${e.cron_id}\`, or wait for a host platform to call \`POST /v1/crons/poke\`)`:`- schedule: ${r}${i} \u2014 next fire ${e.fire_at}`,m=e.state==="paused"?e.held?" \u2014 PAUSED (held)":" \u2014 PAUSED":e.held?" \u2014 HELD":"",d=[`# ${e.name} (${e.cron_id})${m}`,"",s,`- disposition: ${e.on_output}${e.sink!==null?` \u2192 ${e.sink} (tier ${e.tier})`:""}`,`- context: cwd ${e.cwd}${e.profile!==null?`, profile ${e.profile}`:""}, scope ${e.scope}${e.env_keys.length>0?`, env ${e.env_keys.join(", ")}`:""}`,`- run: timeout ${e.run_timeout_s}s, overlap ${e.overlap}${e.run_state==="running"?" \u2014 a run is IN FLIGHT now":""}`];return e.expires_at!==null&&d.push(`- expires: ${e.expires_at}`),e.anchor_node!==null&&d.push(`- anchored to node ${e.anchor_node}${e.cancel_on_wake?" (canceled when it wakes)":""}`),e.created_by!==null&&d.push(`- created by node ${e.created_by}`),d.push("","## Command","","```bash",e.command,"```","",`## Runs (most recent first, last ${t.length})`,""),d.push(...t.length>0?t.map(Q):["(no runs yet)"]),d.join(`
10
+ `)},"render")}),re=h({name:"run",description:"execute now, out of band \u2014 does not advance the schedule",whenToUse:"you need the stored command to execute now because its own behavior has not been exercised, or you fixed the command behind a paused cron. This is not a scheduled-delivery check: once `cron add` has armed a valid row, trust the clock and node delivery rather than firing it again for reassurance.",help:{name:"cron run",summary:"fire one cron now, out of band, and report the settled run",params:[{kind:"positional",name:"cron-id",required:!0,constraint:"The cron to run (from `cron list`)."}],output:[{name:"exit_code",type:"number",required:!1,constraint:"The run's exit code (-1 = timeout kill; null when the process failed to start)."},{name:"duration_ms",type:"number",required:!0,constraint:"Wall-clock run time."},{name:"stdout_head",type:"string",required:!1,constraint:"Captured stdout (head)."},{name:"stderr_head",type:"string",required:!1,constraint:"Captured stderr (head)."},{name:"delivered",type:"string",required:!0,constraint:"What the sink did with the output, or why it did not."}],outputKind:"object",effects:["Runs the command NOW (blocking this call until it settles) and records the run in the cron's run log.","Delivers per the cron's disposition; never advances the schedule, consumes a one-shot, or escalates a failure."]},run:o(async n=>{const e=p(n),t=await l().showCron(e,f()).catch(s=>u(s,"Check the id against `crtr cron list`."));return await l({timeoutMs:t.cron.run_timeout_s*1e3+15e3}).runCron(e,f()).catch(s=>u(s))},"run"),render:o(n=>{const e=n,r=[`Run settled \u2014 exit ${e.exit_code===null?"no exit (process error)":e.exit_code===-1?"-1 (TIMEOUT)":String(e.exit_code)}, ${e.duration_ms??"?"}ms.`,"",`- delivered: ${e.delivered??"(nothing recorded)"}`];return e.stdout_head!==null&&r.push("","## stdout (head)","",e.stdout_head),e.stderr_head!==null&&r.push("","## stderr (head)","",e.stderr_head),r.join(`
11
+ `)},"render")}),te=h({name:"pause",description:"stop firing without losing config or history",whenToUse:"a cron should stop firing for now but its config and run history are worth keeping \u2014 a job under investigation, or a poll to silence temporarily. (A failing cron pauses itself.)",help:{name:"cron pause",summary:"pause one cron",params:[{kind:"positional",name:"cron-id",required:!0,constraint:"The cron to pause (from `cron list`)."}],output:[],outputKind:"object",effects:["Stops the cron firing until `cron resume`. An in-flight run settles normally; the row just stops being due."]},run:o(async n=>{const e=p(n);return await l().pauseCron(e,f()).catch(t=>u(t,"Check the id against `crtr cron list`.")),{}},"run"),render:o(()=>"Cron paused.","render")}),oe=h({name:"resume",description:"re-arm a paused cron",whenToUse:"a paused cron should fire again \u2014 you fixed what made it fail (verify first with `cron run`), or its silence is over. A fire that came due while paused fires immediately on resume.",help:{name:"cron resume",summary:"resume one paused cron",params:[{kind:"positional",name:"cron-id",required:!0,constraint:"The cron to resume (from `cron list`)."}],output:[{name:"fire_at",type:"string",required:!0,constraint:"Next fire \u2014 if already past, it fires on the next daemon tick."}],outputKind:"object",effects:["Re-arms the cron. A past-due fire_at fires immediately (instant verification after un-wedging); a recurrence then advances naturally."]},run:o(async n=>{const e=p(n);return{fire_at:(await l().resumeCron(e,f()).catch(r=>u(r,"Check the id against `crtr cron list`."))).fire_at}},"run"),render:o(n=>`Cron resumed \u2014 next fire ${String(n.fire_at)} (if that is already past, it fires on the next tick).`,"render")}),ie=h({name:"cancel",description:"remove one cron (idempotent)",whenToUse:"a scheduled command should stop firing \u2014 it is obsolete, wrong, or its purpose is served. Idempotent, so a script can safely self-cancel with `crtr cron cancel $CRTR_CRON_ID` even if a racing cancel got there first.",help:{name:"cron cancel",summary:"remove one cron",params:[{kind:"positional",name:"cron-id",required:!0,constraint:"The cron to remove (from `cron list`, or $CRTR_CRON_ID inside a run)."},{kind:"flag",name:"profile",type:"string",required:!1,constraint:"Profile scope to cancel within. Defaults to the calling node's profile; pass the profile id or manifest name for a cron armed under another profile."}],output:[],outputKind:"object",effects:["Deletes the cron row and its run history; kills any in-flight run. A no-op if already gone."]},helpState:{leaf:N},run:o(async n=>{const e=p(n),t=process.env.CRTR_RUN_ID;return await l().cancelCron(e,{...f(typeof n.profile=="string"&&n.profile.trim()!==""?await U(n.profile):void 0),...t===void 0||t===""?{}:{run_id:t}}).catch(r=>u(r,"Check the id against `crtr cron list`.")),{}},"run"),render:o(()=>"Cron canceled.","render")});function ge(){return M({name:"cron",rootEntry:{concept:"one scheduling primitive \u2014 run this bash command, then",desc:"arm, inspect, and control scheduled bash commands the daemon runs on the clock",useWhen:"bash must run later or on a cadence, rather than waiting for a runtime-delivered wake."},help:{name:"cron",summary:"scheduled bash commands, daemon-run",model:"A cron stores and daemon-runs arbitrary bash at its clock. Use ordinary bash for ordinary work; choose node:<id> for stdout as inbox information to an existing node (its disposition defaults to on-change), an existing node's lifecycle fresh-revive action in the bash for a clean re-check without inbox output, or spawn:<kind> to route each output-producing fire to a fresh self-finishing node (a managed child while its creator exists, otherwise a terminal parentless root). Per-fire fresh agent work belongs on the existing spawn:<kind> sink, not an independent resident-root birth. A gate is bash, at the top of the command. Exit 0 after deciding not to act and the occurrence is simply spent \u2014 right when the recurrence is your polling cadence. Exit 75 holds the row. A held recurring row re-checks at its next natural slot, or when a host platform calls the daemon's `POST /v1/crons/poke` endpoint. A held one-shot has no scheduled re-check: it stays held until that endpoint is called, or you execute its bash explicitly with `crtr cron run <cron-id>` (which does not unhold or reschedule the row). A local filesystem condition does not call the daemon, so use --every for that condition or run the cron explicitly after it changes. Any other nonzero exit is a real failure and escalates per --on-output. Keep gate errors nonzero so a broken probe is loud instead of silently held. Beyond the exit-code contract, cron has no native predicate-trigger semantics. --at runs once and deletes the row unless the run's disposition pauses it; --every runs each cadence. Output disposition (--on-output) records silently, escalates failures, or routes stdout to its --sink. Anchor a cron to delete it with a node; --cancel-on-wake instead deletes it when the anchor wakes. An unanchored recurring cron ends through --expires, explicit cancellation, or self-cancellation from its bash. Every run lands in a bounded per-cron run log (`cron show`); cwd, env, and profile are snapshotted at arm time, and --scope controls who lists and cancels it."},children:[X,ee,ne,re,te,oe,ie]})}o(ge,"registerCron");export{ge as registerCron};
@@ -1,2 +1,4 @@
1
1
  import { type BranchDef } from '../../core/command.js';
2
+ /** The product components the runtime registers, read through the daemon. */
3
+ export declare function productComponents(): Promise<import("../../api/index.js").PageComponentDTO[]>;
2
4
  export declare const humanComponents: BranchDef;
@@ -1,3 +1,3 @@
1
- var s=Object.defineProperty;var e=(n,o)=>s(n,"name",{value:o,configurable:!0});import{defineBranch as c,defineLeaf as a}from"../../core/command.js";import{stateBlock as p}from"../../core/help.js";import{InputError as m}from"../../core/io.js";import{resolvePageComponents as i}from"../../core/config.js";import{componentNames as d,renderComponentCatalog as u,renderComponentContract as h}from"../../core/human/component-docs.js";function t(){return i()}e(t,"productComponents");function l(){const n=d(t());return p("components",{count:n.length},n.join(", "))}e(l,"componentNameState");const g=a({name:"list",description:"list page components for selection",whenToUse:"choosing which components to put in an authored page module.",help:{name:"human components list",summary:"list page components for selection",inputNote:"No input parameters.",output:[{name:"catalog",type:"markdown",required:!0,constraint:"One concise selection entry per registry and product-registered component, grouped structural/response/display; no props or response schemas. A product component is written with the PascalCase tag derived from its registered kind."},{name:"follow_up",type:"string",required:!0,constraint:"Points to `crtr human components show <component>` for one selected component contract."}],outputKind:"object",effects:["None. Read-only."]},run:e(async()=>({catalog:u(t()),follow_up:"Use `crtr human components show <component>` for one component contract."}),"run"),render:e(n=>`${String(n.catalog)}
1
+ var s=Object.defineProperty;var e=(n,o)=>s(n,"name",{value:o,configurable:!0});import{defineBranch as c,defineLeaf as a}from"../../core/command.js";import{stateBlock as p}from"../../core/help.js";import{InputError as m}from"../../core/io.js";import{componentNames as i,renderComponentCatalog as d,renderComponentContract as u}from"../../core/human/component-docs.js";import{cliClient as h}from"../api-client.js";async function t(){return(await h().listPageComponents()).components}e(t,"productComponents");async function l(){const n=i(await t());return p("components",{count:n.length},n.join(", "))}e(l,"componentNameState");const g=a({name:"list",description:"list page components for selection",whenToUse:"choosing which components to put in an authored page module.",help:{name:"human components list",summary:"list page components for selection",inputNote:"No input parameters.",output:[{name:"catalog",type:"markdown",required:!0,constraint:"One concise selection entry per registry and product-registered component, grouped structural/response/display; no props or response schemas. A product component is written with the PascalCase tag derived from its registered kind."},{name:"follow_up",type:"string",required:!0,constraint:"Points to `crtr human components show <component>` for one selected component contract."}],outputKind:"object",effects:["None. Read-only."]},run:e(async()=>({catalog:d(await t()),follow_up:"Use `crtr human components show <component>` for one component contract."}),"run"),render:e(n=>`${String(n.catalog)}
2
2
 
3
- ${String(n.follow_up)}`,"render")}),f=a({name:"show",description:"show one page component contract",whenToUse:"authoring one selected page component and needing its exact props, response contract, and shape in a page module.",help:{name:"human components show",summary:"show one page component contract",params:[{kind:"positional",name:"component",type:"string",required:!0,constraint:"Component name from the live <components> catalog below. Case-sensitive: registry components are PascalCase."}],output:[{name:"component",type:"string",required:!0,constraint:"The selected component name."},{name:"contract",type:"markdown",required:!0,constraint:"Hand-written props, response contract, and authoring example for that component. A shadcn family (Card, CardHeader, \u2026) shares one contract covering every member."}],outputKind:"object",effects:["None. Read-only."],dynamicState:l},run:e(async n=>{const o=n.component,r=h(o,t());if(r===void 0)throw new m({error:"unknown_component",message:`unknown page component "${o}"`,field:"component",next:"Run `crtr human components show -h` and choose a component from its live catalog."});return{component:o,contract:r}},"run"),render:e(n=>String(n.contract),"render")}),S=c({name:"components",description:"discover page components and their contracts",whenToUse:"authoring a page module and choosing components, or needing one component\u2019s exact props and response contract.",help:{name:"human components",summary:"discover page components and their contracts",model:"These are the components already in scope in a page module \u2014 no import, ever. List is the selection catalog; show owns the full props, response contract, and authoring shape for one selected component."},children:[g,f]});export{S as humanComponents};
3
+ ${String(n.follow_up)}`,"render")}),f=a({name:"show",description:"show one page component contract",whenToUse:"authoring one selected page component and needing its exact props, response contract, and shape in a page module.",help:{name:"human components show",summary:"show one page component contract",params:[{kind:"positional",name:"component",type:"string",required:!0,constraint:"Component name from the live <components> catalog below. Case-sensitive: registry components are PascalCase."}],output:[{name:"component",type:"string",required:!0,constraint:"The selected component name."},{name:"contract",type:"markdown",required:!0,constraint:"Hand-written props, response contract, and authoring example for that component. A shadcn family (Card, CardHeader, \u2026) shares one contract covering every member."}],outputKind:"object",effects:["None. Read-only."]},helpState:{leaf:l},run:e(async n=>{const o=n.component,r=u(o,await t());if(r===void 0)throw new m({error:"unknown_component",message:`unknown page component "${o}"`,field:"component",next:"Run `crtr human components show -h` and choose a component from its live catalog."});return{component:o,contract:r}},"run"),render:e(n=>String(n.contract),"render")}),N=c({name:"components",description:"discover page components and their contracts",whenToUse:"authoring a page module and choosing components, or needing one component\u2019s exact props and response contract.",help:{name:"human components",summary:"discover page components and their contracts",model:"These are the components already in scope in a page module \u2014 no import, ever. List is the selection catalog; show owns the full props, response contract, and authoring shape for one selected component."},children:[g,f]});export{N as humanComponents,t as productComponents};
@@ -1 +1 @@
1
- var A=Object.defineProperty;var i=(n,e)=>A(n,"name",{value:e,configurable:!0});import{envNodeId as S}from"../../shared/env.js";import{defineLeaf as v}from"../../core/command.js";import{InputError as o}from"../../core/io.js";import{cliClient as l,rethrowAsCliError as N}from"../api-client.js";import{join as _}from"node:path";import{mkdtempSync as j,rmSync as F,writeFileSync as W}from"node:fs";import{tmpdir as C}from"node:os";import{resolvePageComponents as E}from"../../core/config.js";import{preparePage as q}from"../../core/human/page-authoring.js";import{PageAuthoringError as D}from"../../core/human/page.js";import{isResponseBearingSlot as K,validateAuthoredPageManifest as U}from"../../core/human/page-schema.js";import{display as $}from"../../core/termrender/display.js";import{pageHelpText as P,resolveMaxPanes as M}from"./shared.js";const Q=P().pageHint;function k(){return S()??null}i(k,"askingNode");async function B(){const n=k();if(n===null||n==="")return{source:{},lifecycle:null};try{const e=l(),t=await e.getNode(n),a=t.name.trim(),r=t.profile_id===null?void 0:(await e.getProfile(t.profile_id)).name;return{source:{nodeId:n,...a===""?{}:{sessionName:a},...r===void 0?{}:{profileName:r}},lifecycle:t.lifecycle}}catch{return{source:{nodeId:n},lifecycle:null}}}i(B,"sender");function T(){return E()}i(T,"pageProductKinds");function G(n,e,t){const a=t==="terminal"||n.inbox===!0?!0:n.noInbox===!0?!1:e;return{placement:n.placement??"inline",inbox:a,reply:e}}i(G,"deliveryFrom");function g(n){try{return n()}catch(e){throw e instanceof D?new o({error:"invalid_page",message:e.message,field:"page",next:"Fix the authored page and submit again; the reported location is in the authored `.tsx` source."}):e}}i(g,"submitAuthoredPage");async function L(n){try{const e=await l().getHumanRequest(n);if(e.kind!=="page")throw new o({error:"unknown_page",message:`no page ${n} on this canvas`,field:"replace",next:"Pass the page_id a previous send returned, or omit --replace to raise a new page."});return(await l().getHumanInboxPage(e.inbox_ticket_id)).page}catch(e){if(e instanceof o)throw e;N(e,"Pass the page_id a previous send returned, or omit --replace to raise a new page.")}}i(L,"replacedPage");function z(n){const e=j(_(C(),"crtr-human-page-")),t=_(e,"page.tsx");return W(t,n,{encoding:"utf8",mode:384}),{dir:e,path:t}}i(z,"writePipedPage");function J(n){const e=n.page,t=n.document,a=typeof t=="string"&&t.trim()!==""?t:void 0;if(e!==void 0&&a!==void 0)throw new o({error:"bad_invocation",message:"received both --page and a piped page document",field:"page, document",next:"Pass exactly one page source: --page PATH or the document on stdin."});if(e===void 0&&a===void 0)throw new o({error:"missing_parameter",message:"a page source is required",field:"page, document",next:"Pass exactly one page source: --page PATH or the document on stdin."});if(e!==void 0){if(typeof e!="string")throw new o({error:"invalid_type",message:"--page must be a path",field:"page",next:"Retry with --page PATH."});return{sourceFile:e}}if(typeof a!="string")throw new o({error:"invalid_type",message:"stdin must contain a page document",field:"document",next:"Pipe the page document on stdin."});const r=z(a);return{sourceFile:r.path,tempDir:r.dir}}i(J,"pageInput");function O(){return"Queued for the user. Their answer is pushed to your inbox and wakes you when they respond \u2014 never poll, verify it opened, or block."}i(O,"queuedFollowUp");function le(n=!1){const e=P(n);return v({name:"send",description:"send a durable page with explicit delivery",whenToUse:e.sendWhenToUse,help:{name:"human send",summary:e.sendSummary,guide:"Page authoring: `crtr human -h`.",params:[{kind:"stdin",name:"document",required:!1,allowPositional:!1,constraint:e.stdinConstraint},{kind:"flag",name:"page",type:"path",required:!1,constraint:`${Q} ${e.pageFlagConstraint}`},{kind:"flag",name:"placement",type:"enum",choices:["inline","panel"],required:!1,default:"inline",constraint:"Where the page is delivered. inline renders in this conversation's transcript; panel opens from the inbox beside it."},{kind:"flag",name:"inbox",type:"bool",required:!1,constraint:"Project this page into the inbox. Mutually exclusive with --no-inbox."},{kind:"flag",name:"no-inbox",type:"bool",required:!1,constraint:"Do not project this page into the inbox, leaving this conversation's transcript as its only route to the user. Mutually exclusive with --inbox, and refused from a terminal node, whose transcript nobody reads once it finishes."},{kind:"flag",name:"replace",type:"string",required:!1,constraint:"Page id returned by an earlier send. Rewrites source in place while retaining its delivery and transcript anchor. Refused once the page is settled or canceled. Not combinable with delivery flags."}],output:[{name:"page_id",type:"string",required:!0,constraint:"Identifier for the page; pass it as --replace to revise source in place."},{name:"dir",type:"string",required:!0,constraint:e.dirConstraint},{name:"placement",type:"inline | panel",required:!0,constraint:"Resolved page placement."},{name:"inbox",type:"boolean",required:!0,constraint:"Whether the page is projected into the inbox."},{name:"reply",type:"boolean",required:!0,constraint:"Whether completion is delivered to the asking request identity. Not a flag: true exactly when the page has response-bearing components."},{name:"follow_up",type:"string",required:!0,constraint:"What happens next; never instructs the caller to poll."}],outputKind:"object",effects:["Writes one durable page request. Inbox projection follows the delivery flags, except from a terminal node, which always projects; completion delivery follows the page's own components.","A display-only page's inbox entry is a notice, not a task: it clears itself once the person has actually seen the page, wherever they see it.","A reply-bearing page records the asking node on the request and delivers completion directly to that node.","--replace rewrites source in place while retaining delivery and emitted time; open surfaces reload it without creating another page."]},run:i(async(t,a)=>{const{sourceFile:r,tempDir:m}=J(t),c=t.replace,R=["placement","inbox","noInbox"];if(t.inbox===!0&&t.noInbox===!0)throw new o({error:"bad_invocation",message:"received conflicting delivery flags",field:"inbox",next:"Pass at most one of --inbox and --no-inbox."});if(c!==void 0&&R.some(f=>a?.providedParams.has(f)===!0))throw new o({error:"bad_invocation",message:"--replace cannot change delivery",field:"replace",next:"Remove delivery flags: replacement retains the page placement, inbox, reply, and emitted time."});try{if(c!==void 0){const u=await L(c);if((await l().getHumanRequest(c)).state!=="pending")throw new o({error:"page_settled",message:`page ${c} already has a terminal outcome`,field:"replace",next:"The page was answered or canceled; nothing on it can change now. Raise a new page if more is needed."});const p=g(()=>q({sourceFile:r,source:u.source,productKinds:T(),delivery:u.delivery})),I=await l().replaceHumanRequest(c,{page:{dialect:p.manifest.dialect,source:p.document}});return{page_id:c,dir:I.request_dir,...u.delivery,follow_up:"Replaced in place. Every open surface reloads it; delivery and emitted time are unchanged."}}const{source:f,lifecycle:w}=await B();if(w==="terminal"&&t.noInbox===!0)throw new o({error:"invalid_page_delivery",message:"a terminal node cannot send a page --no-inbox",field:"inbox",next:"Drop --no-inbox. This node finishes, and nobody reads a finished node's transcript, so a page kept out of the inbox reaches no one."});const y=T(),d=g(()=>q({sourceFile:r,source:f,productKinds:y})),H=d.manifest.slots.some(K),s=G(t,H,w);if(d.manifest=g(()=>U({...d.manifest,delivery:s},y)),!s.reply){const u=await l().createHumanRequest({page:{dialect:d.manifest.dialect,source:d.document},delivery:s,source:d.manifest.source,requester_node_id:null,creator_cwd:process.cwd()}),x=u.request_dir,p=s.placement==="inline"&&s.inbox?"Shown in this conversation and queued in the user's inbox.":s.placement==="inline"?"Shown in this conversation as passive page content with no inbox entry.":s.inbox?"Queued in the inbox; it opens in the panel and waits until dismissed.":"Published as passive page content with no inbox or response lifecycle.";return{page_id:u.request_id,dir:x,...s,follow_up:p}}const h=k();if(h===null||h==="")throw new o({error:"no_requester",message:"a response-bearing page must be sent from a crouter node",field:"page",next:"Run `crtr human send` from the node that should receive the answer."});const b=await l().createHumanRequest({page:{dialect:d.manifest.dialect,source:d.document},delivery:s,source:d.manifest.source,requester_node_id:h,creator_cwd:process.cwd()});return{page_id:b.request_id,dir:b.request_dir,...s,follow_up:O()}}finally{m!==void 0&&F(m,{recursive:!0,force:!0})}},"run")})}i(le,"humanSend");const ue=v({name:"show",description:"put a file live on the user's screen",whenToUse:"displaying a doc on screen while the user comments",help:{name:"human show",summary:"put a file live on screen in a tmux pane with crouter's renderer",guide:"The pane always watches the file and live-updates on every save \u2014 a displayed doc is a live view by definition, so point it at a file something keeps rewriting (a status board, a running summary) and it stays current. The file is directive-flavored markdown rendered by termrender (panels, columns, trees, callouts, mermaid). To put a rich document in front of the user as a durable inbox page, use `crtr human send --page PATH`; a page with no response-bearing components is a standalone announcement.",params:[{kind:"positional",name:"path",type:"path",required:!0,constraint:"Path to the file to render."},{kind:"flag",name:"window",type:"enum",choices:["auto","split","new"],required:!1,default:"auto",constraint:"Placement. Default auto."}],output:[{name:"pane_id",type:"string | null",required:!0,constraint:"Tmux pane id, or null when not displayed."},{name:"reason",type:"string | null",required:!0,constraint:"Why no pane was created, or null on success."}],outputKind:"object",effects:["Spawns a live-watch tmux pane when possible. No job. Always exits 0."]},run:i(async n=>{const e=n.path,t=n.window,a=t!==void 0?t:"auto";try{const r=$(e,{window:a,maxPanes:M()});return r.paneId!==void 0?{pane_id:r.paneId,reason:null}:{pane_id:null,reason:r.reason}}catch(r){return{pane_id:null,reason:`display failed: ${r instanceof Error?r.message:String(r)}`}}},"run")});export{le as humanSend,ue as humanShow,B as sender};
1
+ var I=Object.defineProperty;var i=(n,e)=>I(n,"name",{value:e,configurable:!0});import{envNodeId as A}from"../../shared/env.js";import{defineLeaf as x}from"../../core/command.js";import{InputError as o}from"../../core/io.js";import{cliClient as l,rethrowAsCliError as S}from"../api-client.js";import{join as v}from"node:path";import{mkdtempSync as N,rmSync as j,writeFileSync as F}from"node:fs";import{tmpdir as W}from"node:os";import{productComponents as C}from"./components.js";import{preparePage as _}from"../../core/human/page-authoring.js";import{PageAuthoringError as E}from"../../core/human/page.js";import{isResponseBearingSlot as D,validateAuthoredPageManifest as U}from"../../core/human/page-schema.js";import{display as $}from"../../core/termrender/display.js";import{pageHelpText as q,resolveMaxPanes as M}from"./shared.js";const K=q().pageHint;function P(){return A()??null}i(P,"askingNode");async function Q(){const n=P();if(n===null||n==="")return{source:{},lifecycle:null};try{const e=l(),t=await e.getNode(n),a=t.name.trim(),r=t.profile_id===null?void 0:(await e.getProfile(t.profile_id)).name;return{source:{nodeId:n,...a===""?{}:{sessionName:a},...r===void 0?{}:{profileName:r}},lifecycle:t.lifecycle}}catch{return{source:{nodeId:n},lifecycle:null}}}i(Q,"sender");function B(n,e,t){const a=t==="terminal"||n.inbox===!0?!0:n.noInbox===!0?!1:e;return{placement:n.placement??"inline",inbox:a,reply:e}}i(B,"deliveryFrom");function g(n){try{return n()}catch(e){throw e instanceof E?new o({error:"invalid_page",message:e.message,field:"page",next:"Fix the authored page and submit again; the reported location is in the authored `.tsx` source."}):e}}i(g,"submitAuthoredPage");async function G(n){try{const e=await l().getHumanRequest(n);if(e.kind!=="page")throw new o({error:"unknown_page",message:`no page ${n} on this canvas`,field:"replace",next:"Pass the page_id a previous send returned, or omit --replace to raise a new page."});return(await l().getHumanInboxPage(e.inbox_ticket_id)).page}catch(e){if(e instanceof o)throw e;S(e,"Pass the page_id a previous send returned, or omit --replace to raise a new page.")}}i(G,"replacedPage");function L(n){const e=N(v(W(),"crtr-human-page-")),t=v(e,"page.tsx");return F(t,n,{encoding:"utf8",mode:384}),{dir:e,path:t}}i(L,"writePipedPage");function z(n){const e=n.page,t=n.document,a=typeof t=="string"&&t.trim()!==""?t:void 0;if(e!==void 0&&a!==void 0)throw new o({error:"bad_invocation",message:"received both --page and a piped page document",field:"page, document",next:"Pass exactly one page source: --page PATH or the document on stdin."});if(e===void 0&&a===void 0)throw new o({error:"missing_parameter",message:"a page source is required",field:"page, document",next:"Pass exactly one page source: --page PATH or the document on stdin."});if(e!==void 0){if(typeof e!="string")throw new o({error:"invalid_type",message:"--page must be a path",field:"page",next:"Retry with --page PATH."});return{sourceFile:e}}if(typeof a!="string")throw new o({error:"invalid_type",message:"stdin must contain a page document",field:"document",next:"Pipe the page document on stdin."});const r=L(a);return{sourceFile:r.path,tempDir:r.dir}}i(z,"pageInput");function J(){return"Queued for the user. Their answer is pushed to your inbox and wakes you when they respond \u2014 never poll, verify it opened, or block."}i(J,"queuedFollowUp");function ce(n=!1){const e=q(n);return x({name:"send",description:"send a durable page with explicit delivery",whenToUse:e.sendWhenToUse,help:{name:"human send",summary:e.sendSummary,guide:"Page authoring: `crtr human -h`.",params:[{kind:"stdin",name:"document",required:!1,allowPositional:!1,constraint:e.stdinConstraint},{kind:"flag",name:"page",type:"path",required:!1,constraint:`${K} ${e.pageFlagConstraint}`},{kind:"flag",name:"placement",type:"enum",choices:["inline","panel"],required:!1,default:"inline",constraint:"Where the page is delivered. inline renders in this conversation's transcript; panel opens from the inbox beside it."},{kind:"flag",name:"inbox",type:"bool",required:!1,constraint:"Project this page into the inbox. Mutually exclusive with --no-inbox."},{kind:"flag",name:"no-inbox",type:"bool",required:!1,constraint:"Do not project this page into the inbox, leaving this conversation's transcript as its only route to the user. Mutually exclusive with --inbox, and refused from a terminal node, whose transcript nobody reads once it finishes."},{kind:"flag",name:"replace",type:"string",required:!1,constraint:"Page id returned by an earlier send. Rewrites source in place while retaining its delivery and transcript anchor. Refused once the page is settled or canceled. Not combinable with delivery flags."}],output:[{name:"page_id",type:"string",required:!0,constraint:"Identifier for the page; pass it as --replace to revise source in place."},{name:"dir",type:"string",required:!0,constraint:e.dirConstraint},{name:"placement",type:"inline | panel",required:!0,constraint:"Resolved page placement."},{name:"inbox",type:"boolean",required:!0,constraint:"Whether the page is projected into the inbox."},{name:"reply",type:"boolean",required:!0,constraint:"Whether completion is delivered to the asking request identity. Not a flag: true exactly when the page has response-bearing components."},{name:"follow_up",type:"string",required:!0,constraint:"What happens next; never instructs the caller to poll."}],outputKind:"object",effects:["Writes one durable page request. Inbox projection follows the delivery flags, except from a terminal node, which always projects; completion delivery follows the page's own components.","A display-only page's inbox entry is a notice, not a task: it clears itself once the person has actually seen the page, wherever they see it.","A reply-bearing page records the asking node on the request and delivers completion directly to that node.","--replace rewrites source in place while retaining delivery and emitted time; open surfaces reload it without creating another page."]},run:i(async(t,a)=>{const{sourceFile:r,tempDir:f}=z(t),c=t.replace,k=["placement","inbox","noInbox"];if(t.inbox===!0&&t.noInbox===!0)throw new o({error:"bad_invocation",message:"received conflicting delivery flags",field:"inbox",next:"Pass at most one of --inbox and --no-inbox."});if(c!==void 0&&k.some(p=>a?.providedParams.has(p)===!0))throw new o({error:"bad_invocation",message:"--replace cannot change delivery",field:"replace",next:"Remove delivery flags: replacement retains the page placement, inbox, reply, and emitted time."});try{const p=await C();if(c!==void 0){const u=await G(c);if((await l().getHumanRequest(c)).state!=="pending")throw new o({error:"page_settled",message:`page ${c} already has a terminal outcome`,field:"replace",next:"The page was answered or canceled; nothing on it can change now. Raise a new page if more is needed."});const m=g(()=>_({sourceFile:r,source:u.source,productKinds:p,delivery:u.delivery})),H=await l().replaceHumanRequest(c,{page:{dialect:m.manifest.dialect,source:m.document}});return{page_id:c,dir:H.request_dir,...u.delivery,follow_up:"Replaced in place. Every open surface reloads it; delivery and emitted time are unchanged."}}const{source:T,lifecycle:w}=await Q();if(w==="terminal"&&t.noInbox===!0)throw new o({error:"invalid_page_delivery",message:"a terminal node cannot send a page --no-inbox",field:"inbox",next:"Drop --no-inbox. This node finishes, and nobody reads a finished node's transcript, so a page kept out of the inbox reaches no one."});const d=g(()=>_({sourceFile:r,source:T,productKinds:p})),R=d.manifest.slots.some(D),s=B(t,R,w);if(d.manifest=g(()=>U({...d.manifest,delivery:s},p)),!s.reply){const u=await l().createHumanRequest({page:{dialect:d.manifest.dialect,source:d.document},delivery:s,source:d.manifest.source,requester_node_id:null,creator_cwd:process.cwd()}),b=u.request_dir,m=s.placement==="inline"&&s.inbox?"Shown in this conversation and queued in the user's inbox.":s.placement==="inline"?"Shown in this conversation as passive page content with no inbox entry.":s.inbox?"Queued in the inbox; it opens in the panel and waits until dismissed.":"Published as passive page content with no inbox or response lifecycle.";return{page_id:u.request_id,dir:b,...s,follow_up:m}}const h=P();if(h===null||h==="")throw new o({error:"no_requester",message:"a response-bearing page must be sent from a crouter node",field:"page",next:"Run `crtr human send` from the node that should receive the answer."});const y=await l().createHumanRequest({page:{dialect:d.manifest.dialect,source:d.document},delivery:s,source:d.manifest.source,requester_node_id:h,creator_cwd:process.cwd()});return{page_id:y.request_id,dir:y.request_dir,...s,follow_up:J()}}finally{f!==void 0&&j(f,{recursive:!0,force:!0})}},"run")})}i(ce,"humanSend");const le=x({name:"show",description:"put a file live on the user's screen",whenToUse:"displaying a doc on screen while the user comments",help:{name:"human show",summary:"put a file live on screen in a tmux pane with crouter's renderer",guide:"The pane always watches the file and live-updates on every save \u2014 a displayed doc is a live view by definition, so point it at a file something keeps rewriting (a status board, a running summary) and it stays current. The file is directive-flavored markdown rendered by termrender (panels, columns, trees, callouts, mermaid). To put a rich document in front of the user as a durable inbox page, use `crtr human send --page PATH`; a page with no response-bearing components is a standalone announcement.",params:[{kind:"positional",name:"path",type:"path",required:!0,constraint:"Path to the file to render."},{kind:"flag",name:"window",type:"enum",choices:["auto","split","new"],required:!1,default:"auto",constraint:"Placement. Default auto."}],output:[{name:"pane_id",type:"string | null",required:!0,constraint:"Tmux pane id, or null when not displayed."},{name:"reason",type:"string | null",required:!0,constraint:"Why no pane was created, or null on success."}],outputKind:"object",effects:["Spawns a live-watch tmux pane when possible. No job. Always exits 0."]},run:i(async n=>{const e=n.path,t=n.window,a=t!==void 0?t:"auto";try{const r=$(e,{window:a,maxPanes:M()});return r.paneId!==void 0?{pane_id:r.paneId,reason:null}:{pane_id:null,reason:r.reason}}catch(r){return{pane_id:null,reason:`display failed: ${r instanceof Error?r.message:String(r)}`}}},"run")});export{ce as humanSend,le as humanShow,Q as sender};
@@ -1 +1 @@
1
- var w=Object.defineProperty;var s=(e,t)=>w(e,"name",{value:t,configurable:!0});import{envNodeId as _}from"../../shared/env.js";import{ApiError as h}from"../../api/index.js";import{defineLeaf as u}from"../../core/command.js";import{InputError as c}from"../../core/io.js";import{paginate as y}from"../../core/pagination.js";import{cliClient as d,rethrowAsCliError as p}from"../api-client.js";async function b(){return(await d().listNodes()).map(e=>({node_id:e.node_id,name:e.name,cwd:e.cwd,parent:e.parent,status:e.status}))}s(b,"fetchNodeRefs");function q(e,t){if(e===void 0)return;const n=new Map(t.map(r=>[r.node_id,r]));let i=e;const o=new Set;for(;i.parent!==null&&!o.has(i.node_id);){o.add(i.node_id);const r=n.get(i.parent);if(r===void 0)break;i=r}return i}s(q,"resolveConversation");function v(e){const t=e?.sessionName?.trim();if(t!==void 0&&t!=="")return t;const n=e?.askedBy?.trim();return n===""?void 0:n}s(v,"readableSourceLabel");function x(e,t,n){const i=e.source.nodeId===void 0?void 0:n.get(e.source.nodeId),o=q(i,t),r=v(e.source);return{ticket_id:e.request_id,dir:e.request_dir,title:e.title,subtitle:e.subtitle??"",kind:e.kind,...e.kind==="page"?{emitted_at:e.emitted_at}:{blocked_since:e.blocked_since},...r===void 0?{}:{source_label:r},...i===void 0?{}:{asking_node_id:i.node_id,asking_node_name:i.name,cwd:i.cwd},...o===void 0?{}:{conversation_id:o.node_id,conversation_title:o.name}}}s(x,"summarizeTicket");async function k(e){try{const n=await d().getHumanRequest(e);return{request_id:n.request_id,kind:n.kind}}catch(n){(!(n instanceof h)||n.status!==404)&&p(n)}const t=(await d().listHumanInbox()).tickets.find(n=>n.ticket_id===e);if(t===void 0)throw new c({error:"not_found",message:`no interaction ticket: ${e}`,field:"ticket_id",next:"Pass a ticket_id from `crtr human list`."});return{request_id:t.request_id,kind:t.kind}}s(k,"requireInteraction");function g(e){return e instanceof h&&e.status===409&&(e.code==="already_settled"||e.code==="review_terminal_conflict")}s(g,"isAlreadySettled");const A=u({name:"list",description:"enumerate pending interactions",whenToUse:"discovering what is blocked on the user",help:{name:"human list",summary:"paginated list of pending interactions, oldest first",params:[{kind:"flag",name:"limit",type:"int",required:!1,default:20,constraint:"Default 20, max 100."},{kind:"flag",name:"cursor",type:"string",required:!1,constraint:"Opaque token from a previous response's next_cursor. Omit on first call."}],output:[{name:"items",type:"object[]",required:!0,constraint:"Each: {ticket_id, dir, title, subtitle, kind, emitted_at? | blocked_since?}; pages use emitted_at, reviews use blocked_since. Enriched when derivable with claim_owner, source_label, asking_node_id/name, conversation_id/title. Oldest first."},{name:"next_cursor",type:"string | null",required:!0,constraint:"Pass on the next call to continue. null means no more items."},{name:"total",type:"integer | null",required:!0,constraint:"Total pending interactions."}],outputKind:"object",effects:["None. Read-only."]},run:s(async e=>{const t=Math.min(Math.max(1,e.limit),100),n=await b(),i=new Map(n.map(a=>[a.node_id,a])),o=(await d().listHumanInbox()).tickets.map(a=>x(a,n,i)).sort((a,l)=>{const m=`${a.emitted_at??a.blocked_since}|${a.ticket_id}`,f=`${l.emitted_at??l.blocked_since}|${l.ticket_id}`;return m<f?-1:m>f?1:0}),r=y(o,{limit:t,cursor:e.cursor},{defaultLimit:20,maxLimit:100,keyOf:s(a=>`${a.emitted_at??a.blocked_since}|${a.ticket_id}`,"keyOf"),total:"count"});return{items:r.items,next_cursor:r.next_cursor,total:r.total}},"run")});function $(e){let t;try{t=JSON.parse(e)}catch(n){throw new c({error:"invalid_json",message:`stdin is not valid JSON: ${String(n)}`,field:"stdin",next:'Pass {"responses":{"<component_id>":{...}}} on stdin.'})}if(typeof t.responses!="object"||t.responses===null||Array.isArray(t.responses))throw new c({error:"invalid_field",message:"responses must be an object keyed by the id of a response-bearing component",field:"responses",next:'Pass {"responses":{"<component_id>":{...}}}, or {"responses":{}} to acknowledge a page with no response-bearing components.'});return t.responses}s($,"parseResolveBody");const L=u({name:"resolve",description:"resolve one human page from stdin answers",whenToUse:"an API client or local tool is submitting answers for a pending human interaction.",tier:"hidden",help:{name:"human resolve",summary:"resolve a pending page with the given answers",params:[{kind:"positional",name:"ticket_id",type:"string",required:!0,constraint:"page_id from human send, or a page ticket_id from human list."},{kind:"stdin",name:"body",required:!0,constraint:"JSON {responses:{<component_id>:{...}}} \u2014 one typed response object per response-bearing component of the page."}],output:[{name:"resolved",type:"boolean",required:!0,constraint:"True when this call completed the page."},{name:"ticket_id",type:"string",required:!0,constraint:"Interaction request id."},{name:"reason",type:"string",required:!1,constraint:"already_settled when resolved is false."}],outputKind:"object",effects:["The daemon settles the request and directly delivers a reply-bearing completion to its asking node."]},run:s(async e=>{const t=e.ticket_id,n=await k(t);if(n.kind!=="page")throw new c({error:"not_found",message:`no page for interaction: ${t}`,field:"ticket_id",next:"Pass a pending page ticket_id from `crtr human list`."});try{await d().respondHumanRequest(n.request_id,{responses:$(e.body),actor:_()??"human"})}catch(i){if(g(i))return{resolved:!1,ticket_id:t,reason:"already_settled"};p(i)}return{resolved:!0,ticket_id:t}},"run")}),P=u({name:"cancel",description:"retract a pending page/review",whenToUse:"a page or review went stale before the user completed it",help:{name:"human cancel",summary:"retract a pending page or review",params:[{kind:"positional",name:"ticket_id",type:"string",required:!0,constraint:"page_id returned by human send, or ticket_id returned by human list."},{kind:"flag",name:"reason",type:"string",required:!1,constraint:"Optional short note delivered to the asking node explaining why it was retracted."}],output:[{name:"canceled",type:"boolean",required:!0,constraint:"True when the interaction was retracted; false when it was already terminal."},{name:"ticket_id",type:"string",required:!0,constraint:"The interaction request id."},{name:"reason",type:"string",required:!1,constraint:"already_settled when canceled is false."}],outputKind:"object",effects:["The daemon removes the request from the human inbox and directly notifies its asking node when it has one."]},run:s(async e=>{const t=e.ticket_id,n=await k(t),i=e.reason,o={...i===void 0||i===""?{}:{reason:i},actor:_()??"human"};try{n.kind==="review"?await d().cancelReview(n.request_id,o):await d().cancelHumanRequest(n.request_id,o)}catch(r){if(g(r))return{canceled:!1,ticket_id:t,reason:"already_settled"};p(r)}return{canceled:!0,ticket_id:t}},"run"),render:s(e=>e.canceled===!0?`Canceled human interaction ${e.ticket_id} \u2014 it left the human inbox.`:`Nothing to cancel for ${e.ticket_id} \u2014 ${e.reason??"nothing to cancel"}.`,"render")});export{P as humanCancel,A as humanList,L as humanResolve};
1
+ var w=Object.defineProperty;var s=(e,t)=>w(e,"name",{value:t,configurable:!0});import{envNodeId as _}from"../../shared/env.js";import{ApiError as h}from"../../api/index.js";import{defineLeaf as u}from"../../core/command.js";import{InputError as c}from"../../core/io.js";import{paginate as y}from"../../core/pagination.js";import{cliClient as d,rethrowAsCliError as p}from"../api-client.js";async function b(){return(await d().listNodes()).map(e=>({node_id:e.node_id,name:e.name,cwd:e.cwd,parent:e.parent,status:e.status}))}s(b,"fetchNodeRefs");function q(e,t){if(e===void 0)return;const n=new Map(t.map(r=>[r.node_id,r]));let i=e;const o=new Set;for(;i.parent!==null&&!o.has(i.node_id);){o.add(i.node_id);const r=n.get(i.parent);if(r===void 0)break;i=r}return i}s(q,"resolveConversation");function v(e){const t=e?.sessionName?.trim();if(t!==void 0&&t!=="")return t;const n=e?.askedBy?.trim();return n===""?void 0:n}s(v,"readableSourceLabel");function x(e,t,n){const i=e.source.nodeId===void 0?void 0:n.get(e.source.nodeId),o=q(i,t),r=v(e.source);return{ticket_id:e.request_id,dir:e.request_dir,title:e.title,subtitle:e.subtitle??"",kind:e.kind,...e.kind==="page"?{emitted_at:e.emitted_at}:{blocked_since:e.blocked_since},...r===void 0?{}:{source_label:r},...i===void 0?{}:{asking_node_id:i.node_id,asking_node_name:i.name,cwd:i.cwd},...o===void 0?{}:{conversation_id:o.node_id,conversation_title:o.name}}}s(x,"summarizeTicket");async function k(e){try{const n=await d().getHumanRequest(e);return{request_id:n.request_id,kind:n.kind}}catch(n){(!(n instanceof h)||n.status!==404)&&p(n)}const t=(await d().listHumanInbox()).tickets.find(n=>n.request_id===e);if(t===void 0)throw new c({error:"not_found",message:`no interaction ticket: ${e}`,field:"ticket_id",next:"Pass a ticket_id from `crtr human list`."});return{request_id:t.request_id,kind:t.kind}}s(k,"requireInteraction");function g(e){return e instanceof h&&e.status===409&&(e.code==="already_settled"||e.code==="review_terminal_conflict")}s(g,"isAlreadySettled");const A=u({name:"list",description:"enumerate pending interactions",whenToUse:"discovering what is blocked on the user",help:{name:"human list",summary:"paginated list of pending interactions, oldest first",params:[{kind:"flag",name:"limit",type:"int",required:!1,default:20,constraint:"Default 20, max 100."},{kind:"flag",name:"cursor",type:"string",required:!1,constraint:"Opaque token from a previous response's next_cursor. Omit on first call."}],output:[{name:"items",type:"object[]",required:!0,constraint:"Each: {ticket_id, dir, title, subtitle, kind, emitted_at? | blocked_since?}; pages use emitted_at, reviews use blocked_since. Enriched when derivable with claim_owner, source_label, asking_node_id/name, conversation_id/title. Oldest first."},{name:"next_cursor",type:"string | null",required:!0,constraint:"Pass on the next call to continue. null means no more items."},{name:"total",type:"integer | null",required:!0,constraint:"Total pending interactions."}],outputKind:"object",effects:["None. Read-only."]},run:s(async e=>{const t=Math.min(Math.max(1,e.limit),100),n=await b(),i=new Map(n.map(a=>[a.node_id,a])),o=(await d().listHumanInbox()).tickets.map(a=>x(a,n,i)).sort((a,l)=>{const m=`${a.emitted_at??a.blocked_since}|${a.ticket_id}`,f=`${l.emitted_at??l.blocked_since}|${l.ticket_id}`;return m<f?-1:m>f?1:0}),r=y(o,{limit:t,cursor:e.cursor},{defaultLimit:20,maxLimit:100,keyOf:s(a=>`${a.emitted_at??a.blocked_since}|${a.ticket_id}`,"keyOf"),total:"count"});return{items:r.items,next_cursor:r.next_cursor,total:r.total}},"run")});function $(e){let t;try{t=JSON.parse(e)}catch(n){throw new c({error:"invalid_json",message:`stdin is not valid JSON: ${String(n)}`,field:"stdin",next:'Pass {"responses":{"<component_id>":{...}}} on stdin.'})}if(typeof t.responses!="object"||t.responses===null||Array.isArray(t.responses))throw new c({error:"invalid_field",message:"responses must be an object keyed by the id of a response-bearing component",field:"responses",next:'Pass {"responses":{"<component_id>":{...}}}, or {"responses":{}} to acknowledge a page with no response-bearing components.'});return t.responses}s($,"parseResolveBody");const L=u({name:"resolve",description:"resolve one human page from stdin answers",whenToUse:"an API client or local tool is submitting answers for a pending human interaction.",tier:"hidden",help:{name:"human resolve",summary:"resolve a pending page with the given answers",params:[{kind:"positional",name:"ticket_id",type:"string",required:!0,constraint:"page_id from human send, or a page ticket_id from human list."},{kind:"stdin",name:"body",required:!0,constraint:"JSON {responses:{<component_id>:{...}}} \u2014 one typed response object per response-bearing component of the page."}],output:[{name:"resolved",type:"boolean",required:!0,constraint:"True when this call completed the page."},{name:"ticket_id",type:"string",required:!0,constraint:"Interaction request id."},{name:"reason",type:"string",required:!1,constraint:"already_settled when resolved is false."}],outputKind:"object",effects:["The daemon settles the request and directly delivers a reply-bearing completion to its asking node."]},run:s(async e=>{const t=e.ticket_id,n=await k(t);if(n.kind!=="page")throw new c({error:"not_found",message:`no page for interaction: ${t}`,field:"ticket_id",next:"Pass a pending page ticket_id from `crtr human list`."});try{await d().respondHumanRequest(n.request_id,{responses:$(e.body),actor:_()??"human"})}catch(i){if(g(i))return{resolved:!1,ticket_id:t,reason:"already_settled"};p(i)}return{resolved:!0,ticket_id:t}},"run")}),P=u({name:"cancel",description:"retract a pending page/review",whenToUse:"a page or review went stale before the user completed it",help:{name:"human cancel",summary:"retract a pending page or review",params:[{kind:"positional",name:"ticket_id",type:"string",required:!0,constraint:"page_id returned by human send, or ticket_id returned by human list."},{kind:"flag",name:"reason",type:"string",required:!1,constraint:"Optional short note delivered to the asking node explaining why it was retracted."}],output:[{name:"canceled",type:"boolean",required:!0,constraint:"True when the interaction was retracted; false when it was already terminal."},{name:"ticket_id",type:"string",required:!0,constraint:"The interaction request id."},{name:"reason",type:"string",required:!1,constraint:"already_settled when canceled is false."}],outputKind:"object",effects:["The daemon removes the request from the human inbox and directly notifies its asking node when it has one."]},run:s(async e=>{const t=e.ticket_id,n=await k(t),i=e.reason,o={...i===void 0||i===""?{}:{reason:i},actor:_()??"human"};try{n.kind==="review"?await d().cancelReview(n.request_id,o):await d().cancelHumanRequest(n.request_id,o)}catch(r){if(g(r))return{canceled:!1,ticket_id:t,reason:"already_settled"};p(r)}return{canceled:!0,ticket_id:t}},"run"),render:s(e=>e.canceled===!0?`Canceled human interaction ${e.ticket_id} \u2014 it left the human inbox.`:`Nothing to cancel for ${e.ticket_id} \u2014 ${e.reason??"nothing to cancel"}.`,"render")});export{P as humanCancel,A as humanList,L as humanResolve};
@@ -0,0 +1,11 @@
1
+ /** The daemon resolves memory against the caller's view, not its own cwd. A node names only itself:
2
+ * the daemon derives its cwd and profile from the node row and refuses a cwd a broker supplies. */
3
+ export declare function memoryCaller(): {
4
+ cwd?: string;
5
+ node?: string;
6
+ profile?: string;
7
+ };
8
+ export declare function memorySelection(input: Record<string, unknown>): Record<string, string>;
9
+ export declare function memoryRequest<T>(method: string, path: string, body?: unknown): Promise<T>;
10
+ export declare function memoryExtensionHelp(operation: 'write' | 'edit'): Promise<string | null>;
11
+ export declare function memoryQuery(params: Record<string, string | number | boolean | undefined>): string;
@@ -0,0 +1 @@
1
+ var m=Object.defineProperty;var t=(r,e)=>m(r,"name",{value:e,configurable:!0});import{envNodeId as f,envProfileId as l}from"../../shared/env.js";import{cliClient as y,rethrowAsCliError as u}from"../api-client.js";import{memoryExtensionFieldCatalogHelp as d}from"../../core/help/memory-extensions.js";function s(){const r=f();if(r)return{node:r};const e=l();return{cwd:process.cwd(),...e?{profile:e}:{}}}t(s,"memoryCaller");function A(r){return{...s(),...typeof r.scope=="string"?{store:r.scope}:{},...typeof r.dir=="string"?{dir:r.dir}:{},...typeof r.profile=="string"?{select_profile:r.profile}:{}}}t(A,"memorySelection");async function p(r,e,n){try{const o=await y().request(r,e,n);if(o!==null&&typeof o=="object"&&!Array.isArray(o)&&"diagnostics"in o){const{diagnostics:i,...c}=o;if(Array.isArray(i))for(const a of i)console.error(a);return c}return o}catch(o){u(o,e.startsWith("/v1/memory/resolve")&&o.status===404?"Find the canonical name with `crtr memory find <query>` or browse `crtr memory list`.":void 0)}}t(p,"memoryRequest");async function q(r){try{const e=await p("GET",`/v1/memory/extensions?${g(s())}`);return d(r,e)}catch{return null}}t(q,"memoryExtensionHelp");function g(r){const e=new URLSearchParams;for(const[n,o]of Object.entries(r))o!==void 0&&e.set(n,String(o));return e.toString()}t(g,"memoryQuery");export{s as memoryCaller,q as memoryExtensionHelp,g as memoryQuery,p as memoryRequest,A as memorySelection};
@@ -1 +1 @@
1
- var s=Object.defineProperty;var r=(e,t)=>s(e,"name",{value:t,configurable:!0});import{defineLeaf as n}from"../../core/command.js";import{deleteDoc as i}from"../../core/memory/mutations.js";import{ambientMemoryTarget as l}from"../../core/memory-resolver.js";import{MEMORY_WRITE_SCOPES as d,historyActorFromEnvironment as c,resolveReadSelector as m,selectorParam as a}from"./shared.js";const g=n({name:"delete",description:"delete a memory document by name",whenToUse:"a stored document is wrong, obsolete, or superseded and should be removed \u2014 the sanctioned way to delete one, so you never rm the markdown off disk. Resolves the full canonical name exactly as `read` does and removes that one file: if you can read it, you can delete it. Refuses docs you do not own \u2014 builtin docs (shipped with the package) and installed-plugin docs (managed by `crtr pkg`). If two physical files in the selected store share the name, it selects the first physical path in stable lexical order and recovers the collision.",help:{name:"memory delete",summary:"resolve an exact canonical name and remove that document from its store",params:[{kind:"positional",name:"name",required:!0,constraint:"Full canonical name of the document \u2014 a project document is `<namespace>/<local name>`. Resolution is exact, as `read` resolves it: a file path, a bare leaf, a `<scope>/<name>` spelling, and a trailing `/INDEX` all fail. A canonical directory\u2019s own document is deleted at the directory name; its members are untouched and stay reachable. If two physical files in the selected store own the name, delete selects the first path in stable lexical order and reports it. Builtin and installed-plugin documents are read-only and refuse to delete, as does the repository-root document, which declares the immutable namespace for every document in the repository; a nested store\u2019s root guide is ordinary and may be deleted."},a("scope-read",{choices:[...d]},"Package-shipped documents cannot be deleted through this writable-scope selector."),a("dir",{},"Deletes from that one store, including a non-winning duplicate no target view resolves to.")],output:[{name:"name",type:"string",required:!0,constraint:"Full canonical name of the document that was deleted, as resolution settled it."},{name:"scope",type:"string",required:!0,constraint:"Scope the document was deleted from: node, user, project, or profile."},{name:"path",type:"string",required:!0,constraint:"Absolute path of the file that was removed."},{name:"log_path",type:"string",required:!0,constraint:"Absolute path to the document\u2019s revision log, which OUTLIVES the document \u2014 the delete is appended to it as a tombstone carrying the full final text, still readable with `crtr memory history`."},{name:"follow_up",type:"string",required:!1,constraint:"Present only when a same-store canonical collision was recovered by selecting the first physical path in lexical order."}],outputKind:"object",effects:["Permanently removes the resolved file from its store, and prunes any parent directories it leaves empty. Irreversible. Refuses the repository-root document: it declares the immutable `namespace:` every document in this repository composes under, so removing it would unmount the whole repository.","Appends a `delete` record carrying the document\u2019s full final text to `.history/<physical path>.jsonl` in the same store. The log itself is never removed."]},run:r(async e=>{const t=m({scope:e.scope,dir:e.dir}),o=l();return i({target:o,selection:{target:o,scope:t.scope,store:t.store},history:c(),fields:e})},"run")});export{g as deleteLeaf};
1
+ var n=Object.defineProperty;var t=(e,a)=>n(e,"name",{value:a,configurable:!0});import{defineLeaf as r}from"../../core/command.js";import{memoryRequest as s,memorySelection as i}from"./client.js";import{MEMORY_WRITE_SCOPES as l,selectorParam as o}from"./shared.js";const u=r({name:"delete",description:"delete a memory document by name",whenToUse:"a stored document is wrong, obsolete, or superseded and should be removed \u2014 the sanctioned way to delete one, so you never rm the markdown off disk. Resolves the full canonical name exactly as `read` does and removes that one file: if you can read it, you can delete it. Refuses docs you do not own \u2014 builtin docs (shipped with the package) and installed-plugin docs (managed by `crtr pkg`). If two physical files in the selected store share the name, it selects the first physical path in stable lexical order and recovers the collision.",help:{name:"memory delete",summary:"resolve an exact canonical name and remove that document from its store",params:[{kind:"positional",name:"name",required:!0,constraint:"Full canonical name of the document \u2014 a project document is `<namespace>/<local name>`. Resolution is exact, as `read` resolves it: a file path, a bare leaf, a `<scope>/<name>` spelling, and a trailing `/INDEX` all fail. A canonical directory\u2019s own document is deleted at the directory name; its members are untouched and stay reachable. If two physical files in the selected store own the name, delete selects the first path in stable lexical order and reports it. Builtin and installed-plugin documents are read-only and refuse to delete, as does the repository-root document, which declares the immutable namespace for every document in the repository; a nested store\u2019s root guide is ordinary and may be deleted."},o("scope-read",{choices:[...l]},"Package-shipped documents cannot be deleted through this writable-scope selector."),o("dir",{},"Deletes from that one store, including a non-winning duplicate no target view resolves to.")],output:[{name:"name",type:"string",required:!0,constraint:"Full canonical name of the document that was deleted, as resolution settled it."},{name:"scope",type:"string",required:!0,constraint:"Scope the document was deleted from: node, user, project, or profile."},{name:"path",type:"string",required:!0,constraint:"Absolute path of the file that was removed."},{name:"log_path",type:"string",required:!0,constraint:"Absolute path to the document\u2019s revision log, which OUTLIVES the document \u2014 the delete is appended to it as a tombstone carrying the full final text, still readable with `crtr memory history`."},{name:"follow_up",type:"string",required:!1,constraint:"Present only when a same-store canonical collision was recovered by selecting the first physical path in lexical order."}],outputKind:"object",effects:["Permanently removes the resolved file from its store, and prunes any parent directories it leaves empty. Irreversible. Refuses the repository-root document: it declares the immutable `namespace:` every document in this repository composes under, so removing it would unmount the whole repository.","Appends a `delete` record carrying the document\u2019s full final text to `.history/<physical path>.jsonl` in the same store. The log itself is never removed."]},run:t(async e=>s("DELETE",`/v1/memory/docs/${encodeURIComponent(e.name)}`,{...i(e),mode:"cli"}),"run")});export{u as deleteLeaf};
@@ -1,10 +1,10 @@
1
- var o=Object.defineProperty;var n=(t,r)=>o(t,"name",{value:r,configurable:!0});import{defineLeaf as s}from"../../core/command.js";import{updateDoc as d}from"../../core/memory/mutations.js";import{ambientMemoryTarget as l}from"../../core/memory-resolver.js";import{DOC_RATIONALE_CONSTRAINT as c,GUIDE_CANONICAL_NAMES as h,GUIDE_DOC_LINKS as u,GUIDE_PREDICATE_VOCABULARY as m,GUIDE_ROUTING_LINE as p,GUIDE_SURFACES as y,historyActorFromEnvironment as f,memoryExtensionFieldCatalogHelp as g,overlayParam as e,resolveReadSelector as v,selectorParam as i}from"./shared.js";const w=["short-form","gate","surfaces","unlisted","slash","rationale","lint-ignore"],b=["kind","when-and-why-to-read","origin","last-updated"],I=s({name:"edit",description:"revise an existing memory document, recording why",whenToUse:"a document that already exists needs to change \u2014 its body, its frontmatter, or both. Every revision carries a --rationale and is appended to the doc\u2019s revision history, so the corpus keeps a record of why it reached its current state. Resolves the name exactly as `read` does and revises the doc where it lives; use `crtr memory write` only to create one that does not exist yet.",help:{name:"memory edit",summary:"resolve an existing document and revise its body and/or frontmatter, appending a history record",guide:`The body you pipe is saved byte for byte. Nothing between stdin and disk parses, normalizes, summarizes, or rewrites it \u2014 the frontmatter fence is prepended and the text is otherwise untouched. Relaying a person\u2019s own words is therefore safe: pass them through unchanged and mark them with --verbatim.
1
+ var i=Object.defineProperty;var r=(t,a)=>i(t,"name",{value:a,configurable:!0});import{defineLeaf as o}from"../../core/command.js";import{memoryExtensionHelp as s,memoryRequest as d,memorySelection as l}from"./client.js";import{DOC_RATIONALE_CONSTRAINT as c,GUIDE_CANONICAL_NAMES as h,GUIDE_DOC_LINKS as u,GUIDE_PREDICATE_VOCABULARY as m,GUIDE_ROUTING_LINE as p,GUIDE_SURFACES as y,overlayParam as e,selectorParam as n}from"./shared.js";const f=["short-form","gate","surfaces","unlisted","slash","rationale","lint-ignore"],g=["kind","privacy","when-and-why-to-read","origin","last-updated"],v=o({name:"edit",description:"revise an existing memory document, recording why",whenToUse:"a document that already exists needs to change \u2014 its body, its frontmatter, or both. Every revision carries a --rationale and is appended to the doc\u2019s revision history, so the corpus keeps a record of why it reached its current state. Resolves the name exactly as `read` does and revises the doc where it lives; use `crtr memory write` only to create one that does not exist yet.",help:{name:"memory edit",summary:"resolve an existing document and revise its body and/or frontmatter, appending a history record",guide:`The body you pipe is saved byte for byte. Nothing between stdin and disk parses, normalizes, summarizes, or rewrites it \u2014 the frontmatter fence is prepended and the text is otherwise untouched. Relaying a person\u2019s own words is therefore safe: pass them through unchanged and mark them with --verbatim.
2
2
 
3
3
  Two different rationales exist and they never share a flag. --rationale is why THIS REVISION is happening; it is required, it lands in the revision history, and it never touches the document text. --doc-rationale replaces the document\u2019s standing \`rationale\` frontmatter field \u2014 the observed gap the doc exists to close.
4
4
 
5
5
  Omitting stdin leaves the body exactly as it stands, which is how a frontmatter-only revision is made. Piping a body REPLACES the whole body; there is no partial or append mode. Emptying a document is not an edit \u2014 that intent is \`crtr memory delete\`.
6
6
 
7
- Omitted frontmatter flags preserve their fields. --unset is the only way to REMOVE one. The canonical clearable set (short-form, gate, surfaces, unlisted, slash, rationale, lint-ignore) is always accepted; any other field name is accepted only when the document\u2019s frontmatter already carries it \u2014 the path for dropping a legacy or non-canonical field. --surface replaces the whole surfaces list rather than adding to it.
7
+ Omitted frontmatter flags preserve their fields; a legacy document without privacy is stamped private on edit. --unset is the only way to REMOVE a clearable field. The canonical clearable set (short-form, gate, surfaces, unlisted, slash, rationale, lint-ignore) is always accepted; any other field name is accepted only when the document\u2019s frontmatter already carries it \u2014 the path for dropping a legacy or non-canonical field. --surface replaces the whole surfaces list rather than adding to it.
8
8
 
9
9
  An edit that changes nothing fails. A rationale attached to zero change is noise in the trail the history exists to build \u2014 if the doc already says what you meant, there is nothing to record.
10
10
 
@@ -18,4 +18,4 @@ An edit that changes nothing fails. A rationale attached to zero change is noise
18
18
 
19
19
  `+h+`
20
20
 
21
- `+u+"\n\nCreate a document that does not exist yet with `crtr memory write`. Read the revisions back with `crtr memory history`.",params:[{kind:"positional",name:"name",required:!0,constraint:"Full canonical identity, resolved exactly in the target view or exact project store. A project document requires its complete `<namespace>/<local name>`; a directory document answers at the directory name, never with a trailing `/INDEX`. Same-store physical collisions are refused with both paths; `--dir` selects one exact project store. Builtin and installed-plugin docs are read-only corpora and are refused."},{kind:"flag",name:"rationale",type:"string",required:!0,constraint:"Why THIS REVISION is happening \u2014 the audit-trail entry, required on every edit. Recorded in the document\u2019s revision history, NEVER written into the document. To change the doc\u2019s standing `rationale` frontmatter field instead, use --doc-rationale."},{kind:"flag",name:"verbatim",type:"bool",required:!1,constraint:"Declares the piped body a verbatim payload from the principal \u2014 their own text, relayed unchanged \u2014 rather than agent-authored prose. Pure provenance recorded in the history record; the save path is identical either way, because there is only one save path and it never transforms."},e("kind"),e("when-and-why-to-read"),e("short-form"),e("unlisted",{},"Presence SETS the field; remove it with `--unset unlisted`."),e("surface"),e("gate"),e("lint-ignore"),e("slash",{},"Presence SETS the field; remove it with `--unset slash`."),{kind:"flag",name:"doc-rationale",type:"string",required:!1,constraint:c},{kind:"flag",name:"extension",type:"string",required:!1,repeatable:!0,constraint:"Set one declared plugin field as `extensions.<plugin>.<field>=VALUE`. The path is required in full; booleans accept only true or false, numbers require finite numeric syntax, and strings/enums preserve the literal text after the first =. Every requested field validates before the document is revised."},{kind:"flag",name:"unset-extension",type:"string",required:!1,repeatable:!0,constraint:"Remove one explicitly persisted declared field by its full `extensions.<plugin>.<field>` path. A removed explicit value lets its declaration default apply only in structured output."},{kind:"flag",name:"unset",type:"string",required:!1,repeatable:!0,constraint:`Remove a frontmatter field. One field per occurrence. ${w.join(", ")} are always accepted, whether or not the doc currently carries them. Any other field name is accepted only when the target document's frontmatter already carries it \u2014 this is the path for dropping a legacy or otherwise non-canonical field (e.g. a stale \`name:\` or \`description:\` left over from before \`when-and-why-to-read\`). Requesting a field the doc does not have fails, naming the fields it does have. \`${b.join("`, `")}\` are never clearable.`},i("scope-read",{},"Edit resolves the address winner and refuses same-store physical collisions."),i("dir"),i("profile"),{kind:"stdin",name:"body",required:!1,constraint:"Full replacement body (markdown, no frontmatter), saved byte for byte. Piped on stdin only \u2014 this leaf already claims the one positional for NAME. Absent or empty stdin leaves the existing body untouched, which is how a frontmatter-only revision is made."}],output:[{name:"name",type:"string",required:!0,constraint:"Resolved document name."},{name:"kind",type:"string",required:!0,constraint:"Kind recorded in frontmatter after the revision."},{name:"scope",type:"string",required:!0,constraint:"Scope the document was revised in: node, project, profile, or user."},{name:"path",type:"string",required:!0,constraint:"Absolute path to the revised document."},{name:"revision",type:"number",required:!0,constraint:"This document\u2019s record count after the append \u2014 the revision number to pass to `crtr memory history --revision`."},{name:"changed",type:"string[]",required:!0,constraint:"`body` when the body text changed, plus the frontmatter field names whose values changed. `last-updated` is excluded \u2014 it is stamped on every edit, so naming it says nothing."}],outputKind:"object",dynamicState:n(()=>g("edit"),"dynamicState"),effects:["Rewrites the selected physical memory document and appends one `edit` record to its mirrored `.history` sidecar."]},run:n(async t=>{const r=v({scope:t.scope,dir:t.dir,profile:t.profile}),a=l();return d({target:a,selection:{target:a,scope:r.scope,store:r.store},history:f(),fields:t})},"run")});export{I as editLeaf};
21
+ `+u+"\n\nCreate a document that does not exist yet with `crtr memory write`. Read the revisions back with `crtr memory history`.",params:[{kind:"positional",name:"name",required:!0,constraint:"Full canonical identity, resolved exactly in the target view or exact project store. A project document requires its complete `<namespace>/<local name>`; a directory document answers at the directory name, never with a trailing `/INDEX`. Same-store physical collisions are refused with both paths; `--dir` selects one exact project store. Builtin and installed-plugin docs are read-only corpora and are refused."},{kind:"flag",name:"rationale",type:"string",required:!0,constraint:"Why THIS REVISION is happening \u2014 the audit-trail entry, required on every edit. Recorded in the document\u2019s revision history, NEVER written into the document. To change the doc\u2019s standing `rationale` frontmatter field instead, use --doc-rationale."},{kind:"flag",name:"verbatim",type:"bool",required:!1,constraint:"Declares the piped body a verbatim payload from the principal \u2014 their own text, relayed unchanged \u2014 rather than agent-authored prose. Pure provenance recorded in the history record; the save path is identical either way, because there is only one save path and it never transforms."},e("kind"),e("privacy"),e("when-and-why-to-read"),e("short-form"),e("unlisted",{},"Presence SETS the field; remove it with `--unset unlisted`."),e("surface"),e("gate"),e("lint-ignore"),e("slash",{},"Presence SETS the field; remove it with `--unset slash`."),{kind:"flag",name:"doc-rationale",type:"string",required:!1,constraint:c},{kind:"flag",name:"extension",type:"string",required:!1,repeatable:!0,constraint:"Set one declared plugin field as `extensions.<plugin>.<field>=VALUE`. The path is required in full; booleans accept only true or false, numbers require finite numeric syntax, and strings/enums preserve the literal text after the first =. Every requested field validates before the document is revised."},{kind:"flag",name:"unset-extension",type:"string",required:!1,repeatable:!0,constraint:"Remove one explicitly persisted declared field by its full `extensions.<plugin>.<field>` path. A removed explicit value lets its declaration default apply only in structured output."},{kind:"flag",name:"unset",type:"string",required:!1,repeatable:!0,constraint:`Remove a frontmatter field. One field per occurrence. ${f.join(", ")} are always accepted, whether or not the doc currently carries them. Any other field name is accepted only when the target document's frontmatter already carries it \u2014 this is the path for dropping a legacy or otherwise non-canonical field (e.g. a stale \`name:\` or \`description:\` left over from before \`when-and-why-to-read\`). Requesting a field the doc does not have fails, naming the fields it does have. \`${g.join("`, `")}\` are never clearable.`},n("scope-read",{},"Edit resolves the address winner and refuses same-store physical collisions."),n("dir"),n("profile"),{kind:"stdin",name:"body",required:!1,constraint:"Full replacement body (markdown, no frontmatter), saved byte for byte. Piped on stdin only \u2014 this leaf already claims the one positional for NAME. Absent or empty stdin leaves the existing body untouched, which is how a frontmatter-only revision is made."}],output:[{name:"name",type:"string",required:!0,constraint:"Resolved document name."},{name:"kind",type:"string",required:!0,constraint:"Kind recorded in frontmatter after the revision."},{name:"scope",type:"string",required:!0,constraint:"Scope the document was revised in: node, project, profile, or user."},{name:"path",type:"string",required:!0,constraint:"Absolute path to the revised document."},{name:"revision",type:"number",required:!0,constraint:"This document\u2019s record count after the append \u2014 the revision number to pass to `crtr memory history --revision`."},{name:"changed",type:"string[]",required:!0,constraint:"`body` when the body text changed, plus the frontmatter field names whose values changed. `last-updated` is excluded \u2014 it is stamped on every edit, so naming it says nothing."}],outputKind:"object",effects:["Rewrites the selected physical memory document and appends one `edit` record to its mirrored `.history` sidecar."]},effectiveHelp:r(async()=>{const t=await s("edit");return{...v.help,dynamicState:r(()=>t,"dynamicState")}},"effectiveHelp"),run:r(async t=>d("PATCH",`/v1/memory/docs/${encodeURIComponent(t.name)}`,{...l(t),mode:"cli",fields:t}),"run")});export{v as editLeaf};
@@ -1 +1 @@
1
- var q=Object.defineProperty;var s=(e,a)=>q(e,"name",{value:a,configurable:!0});import{defineLeaf as S}from"../../core/command.js";import{usage as R}from"../../core/errors.js";import{ambientMemoryTarget as M,memoryStoresInPrecedence as O}from"../../core/memory-resolver.js";import{searchDocs as $}from"../../core/memory/service.js";import{paginate as v}from"../../core/pagination.js";import{MEMORY_KINDS as L,registerMemoryListingExposure as k,resolveReadSelector as P,selectorParam as _}from"./shared.js";const F=S({name:"find",description:"relevance search across memory documents",whenToUse:"the discovery surface when you do NOT know a document\u2019s canonical name \u2014 ranks documents by relevance over the canonical name, the store-local name, the physical path, the read-routing line, and short-form (add --body to also weigh body text), and every hit reports the canonical name `crtr memory read` takes. Searches every scope by default regardless of any visibility gate; --scope narrows to one class of store, --dir to one exact store. Reach for `crtr memory list` instead when you want the whole inventory rather than a topic match, and --grep when you need an exact regex or literal-string match across document bodies.",help:{name:"memory find",summary:"bounded relevance search across memory documents, weighted over name/local name/physical path/routing-line/short-form (and body with --body)",params:[{kind:"positional",name:"query",required:!0,constraint:"With ranked search (default): whitespace-separated terms, matched case-insensitively and weighted over the document\u2019s names (canonical, store-local, and physical path), the read-routing line, and short-form (plus body with --body); documents matching more/stronger fields rank higher. By default returns at most 10 hits scoring at least half the top score. With --grep: an ECMAScript regex applied to each document body line."},{kind:"flag",name:"kind",type:"enum",choices:[...L],required:!1,constraint:"Filter to a single kind. Default: all kinds."},_("scope-read",{},"Default: every resolved scope, builtin included."),_("dir",{},"Searches that store alone, and searches it physically: two documents sharing one canonical name are both searched and both reported, where the default view searches only the winner."),{kind:"flag",name:"grep",type:"bool",required:!1,constraint:"Treat the query as an ECMAScript regex and match it against document bodies, instead of weighted relevance ranking. Mutually exclusive with --body."},{kind:"flag",name:"body",type:"bool",required:!1,constraint:"Also weigh document body text in the relevance ranking (in addition to name/when/why/short-form). Ignored under --grep, which always scans bodies."},{kind:"flag",name:"limit",type:"int",required:!1,constraint:"Maximum hits to return per page after relevance filtering. Default 10, hard max 100."},{kind:"flag",name:"cursor",type:"string",required:!1,constraint:"Opaque token from next_cursor. Omit on first call."},{kind:"flag",name:"min-score",type:"int",required:!1,constraint:"Ranked mode only. Minimum score to return, replacing the default adaptive floor of half the top score. Must be at least 1."},{kind:"flag",name:"paths",type:"bool",required:!1,constraint:"Include each hit\u2019s physical location \u2014 absolute path, store root, store-relative path, and every other physical document sharing its canonical name. Off by default; the canonical name is sufficient for `crtr memory read <name>`."}],output:[{name:"query",type:"string",required:!0,constraint:"Echo of the input query."},{name:"matches",type:"integer",required:!0,constraint:"Total matches before the default relevance floor and limit are applied (ranked mode) or total matching lines (grep mode) \u2014 across all pages, not just this one."},{name:"hits",type:"object[]",required:!0,constraint:"Ranked mode \u2014 each: {name, kind, scope, score, short_form}, sorted by score descending. Grep mode \u2014 each: {name, scope, line, text}, sorted by name then line. `name` is always the canonical name, whatever field matched. With --paths, each shape also carries path, store_root, physical_relative_path, and candidates when several physical documents share the name. Paginated via --limit/--cursor."},{name:"next_cursor",type:"string | null",required:!0,constraint:"Opaque token for the next page; null means no more items."},{name:"follow_up",type:"string",required:!0,constraint:"Concrete next commands for reading a hit in full or refining the search."}],outputKind:"object",effects:["Records this search page as listing context for the current node without marking any matched document as loaded."]},run:s(async e=>{const a=e.query,m=e.kind,h=e.dir,o=P({scope:e.scope,dir:h}),u=e.grep,p=e.body,i=e.limit,c=e.cursor,y=e.minScore,d=e.paths;if(i!==void 0&&(i<1||i>100))throw R("--limit must be between 1 and 100.");const l=i??10,f=`listing:memory-find:${JSON.stringify({query:a,kind:m??null,scope:o.scope??null,dir:h??null,grep:u===!0,body:p===!0,limit:l,cursor:c??null,minScore:y??null,paths:d===!0})}`,g=M();o.store===null&&O(g,o.scope,!0,!0);const n=$({target:g,scope:o.scope,store:o.store,query:a,kind:m,grep:u,body:p,minScore:y}),b=s(t=>{const{path:r,store_root:A,physical_relative_path:D,candidates:E,...x}=t;return x},"omitPaths");if(n.mode==="grep"){const t=v([...n.hits],{limit:l,cursor:c},{defaultLimit:10,maxLimit:100,keyOf:s(r=>`${r.name}:${r.scope}:${r.physical_relative_path}:${String(r.line).padStart(7,"0")}`,"keyOf"),total:"omit"});return k(f),{query:a,matches:n.matches,hits:t.items.map(r=>d?r:b(r)),next_cursor:t.next_cursor,follow_up:"Read a hit with `crtr memory read <name>`. Run `crtr memory find -h` for filters and verbosity."}}const w=v([...n.hits],{limit:l,cursor:c},{defaultLimit:10,maxLimit:100,keyOf:s(t=>`${String(1e6-t.score).padStart(7,"0")}:${t.scope}:${t.name}:${t.physical_relative_path}`,"keyOf"),total:"omit"});return k(f),{query:a,matches:n.matches,hits:w.items.map(t=>d?t:b(t)),next_cursor:w.next_cursor,follow_up:"Read a hit with `crtr memory read <name>`. Run `crtr memory find -h` for filters and verbosity."}},"run")});export{F as findLeaf};
1
+ var f=Object.defineProperty;var o=(e,t)=>f(e,"name",{value:t,configurable:!0});import{defineLeaf as g}from"../../core/command.js";import{usage as w}from"../../core/errors.js";import{memoryRequest as b,memorySelection as v}from"./client.js";import{MEMORY_KINDS as k,selectorParam as s}from"./shared.js";const A=g({name:"find",description:"relevance search across memory documents",whenToUse:"the discovery surface when you do NOT know a document\u2019s canonical name \u2014 ranks documents by relevance over the canonical name, the store-local name, the physical path, the read-routing line, and short-form (add --body to also weigh body text), and every hit reports the canonical name `crtr memory read` takes. Searches every scope by default regardless of any visibility gate; --scope narrows to one class of store, --dir to one exact store. Reach for `crtr memory list` instead when you want the whole inventory rather than a topic match, and --grep when you need an exact regex or literal-string match across document bodies.",help:{name:"memory find",summary:"bounded relevance search across memory documents, weighted over name/local name/physical path/routing-line/short-form (and body with --body)",params:[{kind:"positional",name:"query",required:!0,constraint:"With ranked search (default): whitespace-separated terms, matched case-insensitively and weighted over the document\u2019s names (canonical, store-local, and physical path), the read-routing line, and short-form (plus body with --body); documents matching more/stronger fields rank higher. By default returns at most 10 hits scoring at least half the top score. With --grep: an ECMAScript regex applied to each document body line."},{kind:"flag",name:"kind",type:"enum",choices:[...k],required:!1,constraint:"Filter to a single kind. Default: all kinds."},s("scope-read",{},"Default: every resolved scope, builtin included."),s("dir",{},"Searches that store alone, and searches it physically: two documents sharing one canonical name are both searched and both reported, where the default view searches only the winner."),{kind:"flag",name:"grep",type:"bool",required:!1,constraint:"Treat the query as an ECMAScript regex and match it against document bodies, instead of weighted relevance ranking. Mutually exclusive with --body."},{kind:"flag",name:"body",type:"bool",required:!1,constraint:"Also weigh document body text in the relevance ranking (in addition to name/when/why/short-form). Ignored under --grep, which always scans bodies."},{kind:"flag",name:"limit",type:"int",required:!1,constraint:"Maximum hits to return per page after relevance filtering. Default 10, hard max 100."},{kind:"flag",name:"cursor",type:"string",required:!1,constraint:"Opaque token from next_cursor. Omit on first call."},{kind:"flag",name:"min-score",type:"int",required:!1,constraint:"Ranked mode only. Minimum score to return, replacing the default adaptive floor of half the top score. Must be at least 1."},{kind:"flag",name:"paths",type:"bool",required:!1,constraint:"Include each hit\u2019s physical location \u2014 absolute path, store root, store-relative path, and every other physical document sharing its canonical name. Off by default; the canonical name is sufficient for `crtr memory read <name>`."}],output:[{name:"query",type:"string",required:!0,constraint:"Echo of the input query."},{name:"matches",type:"integer",required:!0,constraint:"Total matches before the default relevance floor and limit are applied (ranked mode) or total matching lines (grep mode) \u2014 across all pages, not just this one."},{name:"hits",type:"object[]",required:!0,constraint:"Ranked mode \u2014 each: {name, kind, privacy, scope, score, short_form}, sorted by score descending. Grep mode \u2014 each: {name, privacy, scope, line, text}, sorted by name then line. `name` is always the canonical name, whatever field matched. With --paths, each shape also carries path, store_root, physical_relative_path, and candidates when several physical documents share the name. Paginated via --limit/--cursor."},{name:"next_cursor",type:"string | null",required:!0,constraint:"Opaque token for the next page; null means no more items."},{name:"follow_up",type:"string",required:!0,constraint:"Concrete next commands for reading a hit in full or refining the search."}],outputKind:"object",effects:["Records this search page as listing context for the current node without marking any matched document as loaded."]},run:o(async e=>{const t=e.query,i=e.kind,c=e.grep,d=e.body,a=e.limit,l=e.cursor,h=e.minScore,m=e.paths;if(a!==void 0&&(a<1||a>100))throw w("--limit must be between 1 and 100.");const u=a??10,n=await b("POST","/v1/memory/search",{...v(e),mode:"cli",query:t,...i===void 0?{}:{kind:i},grep:c,body:d,limit:u,cursor:l,min_score:h}),y=o(r=>{const{path:q,store_root:x,physical_relative_path:_,candidates:R,...p}=r;return p},"omitPaths");return{query:t,matches:n.matches,hits:n.hits.map(r=>m?r:y(r)),next_cursor:n.next_cursor,follow_up:"Read a hit with `crtr memory read <name>`. Run `crtr memory find -h` for filters and verbosity."}},"run")});export{A as findLeaf};
@@ -1,5 +1,5 @@
1
- var u=Object.defineProperty;var l=(t,n)=>u(t,"name",{value:n,configurable:!0});import{defineLeaf as p}from"../../core/command.js";import{ambientMemoryTarget as y,memoryStoresInPrecedence as g}from"../../core/memory-resolver.js";import{readHistory as v}from"../../core/memory/service.js";import{renderResult as w}from"../../core/render.js";import{resolveReadSelector as b,selectorParam as f}from"./shared.js";const $=p({name:"history",description:"read a memory document\u2019s revision history",whenToUse:"you want to know how a document reached its current state \u2014 who revised it, when, why, and what changed per revision. Works for a deleted document too: the log outlives the doc. A diagnostics read; it advances nothing and changes nothing.",help:{name:"memory history",summary:"list a document\u2019s revisions newest-first, with diffs or one full record on demand",guide:"Every mutation through `crtr memory write`, `edit`, `move`, and `delete` appends one self-contained record \u2014 complete document text before and after, plus who, when, and (on an edit) why. Diffs and revision numbers are derived here at read time, never stored.\n\nA record whose `before` differs from the previous record\u2019s `after` is the evidence of an out-of-band direct file edit; nothing prevents those, and nothing detects them beyond this.\n\nThe log is a plain JSONL file at `log_path`, one JSON object per line \u2014 an external consumer reads it directly rather than through this command.",params:[{kind:"positional",name:"name",required:!0,constraint:"Full canonical memory name (a project document includes its namespace), resolved exactly. Deleted history uses the same canonical name and the selected store\u2019s physical sidecars; there is no leaf or scope-prefix fallback. Builtin and installed-plugin docs are read-only corpora and carry no history."},f("scope-read"),f("dir"),f("profile"),{kind:"flag",name:"limit",type:"int",required:!1,default:10,constraint:"How many revisions to return, newest first. Default 10."},{kind:"flag",name:"diff",type:"bool",required:!1,constraint:"Include a unified diff per listed revision, computed from that record\u2019s own before/after texts."},{kind:"flag",name:"revision",type:"int",required:!1,constraint:"Show ONE revision in full \u2014 its metadata plus the complete before and after document texts. 1-based position in the log: the number `crtr memory edit` reports. Overrides --limit and --diff."}],output:[{name:"name",type:"string",required:!0,constraint:"Resolved document name."},{name:"scope",type:"string",required:!0,constraint:"Scope whose store holds the log: node, project, profile, or user."},{name:"log_path",type:"string",required:!0,constraint:"Absolute path to the append-only JSONL log \u2014 the direct-consumption pointer, one self-contained record per line."},{name:"history_candidates",type:"object[]",required:!1,constraint:"Deleted-history physical candidates: {path, representation, winner}. The directory-index form is listed first and wins deterministically."},{name:"history_collision",type:"object",required:!1,constraint:"Present when deleted history has both physical forms: {paths, winner}."},{name:"total",type:"number",required:!0,constraint:"Total revisions in the log."},{name:"revisions",type:"object[]",required:!1,constraint:"Newest-first, capped by --limit. Each: {revision, op, at, node, rationale, body_changed, frontmatter_changed, diff}. `node` and `rationale` are present only when recorded; `diff` only with --diff. Absent when --revision selects one record."},{name:"record",type:"object",required:!1,constraint:"Present only with --revision: {revision, op, at, node, cwd, rationale, verbatim, before, after} \u2014 the full record including both complete document texts."},{name:"follow_up",type:"string",required:!0,constraint:"Concrete next commands \u2014 the flag variants and the doc read, or, for a deleted document, the revision carrying its retained final text."}],outputKind:"object",effects:["None. Read-only."]},render:l(t=>{const n=t.revisions;if(n===void 0||!n.some(o=>typeof o.diff=="string"))return w(t,$.help);const c=n.map(o=>{const r=[`op: ${String(o.op)}`,`at: ${String(o.at)}`];o.node!==void 0&&r.push(`node: ${String(o.node)}`),o.verbatim===!0&&r.push("verbatim");const i=o.frontmatter_changed??[],s=[...o.body_changed===!0?["body"]:[],...i.length>0?[`frontmatter: ${i.join(", ")}`]:[]],a=String(o.diff??"");return[`## revision ${String(o.revision)} \u2014 ${r.join(" \xB7 ")}`,...o.rationale!==void 0?[`rationale: ${String(o.rationale)}`]:[],`changed: ${s.length>0?s.join("; "):"nothing"}`,...a===""?[]:["","```diff",a,"```"]].join(`
2
- `)});return[`- name: ${String(t.name)}`,`- scope: ${String(t.scope)}`,`- log_path: ${String(t.log_path)}`,`- total: ${String(t.total)}`,"",c.join(`
1
+ var l=Object.defineProperty;var s=(n,o)=>l(n,"name",{value:o,configurable:!0});import{defineLeaf as f}from"../../core/command.js";import{memoryQuery as m,memoryRequest as h,memorySelection as u}from"./client.js";import{renderResult as p}from"../../core/render.js";import{selectorParam as d}from"./shared.js";const y=f({name:"history",description:"read a memory document\u2019s revision history",whenToUse:"you want to know how a document reached its current state \u2014 who revised it, when, why, and what changed per revision. Works for a deleted document too: the log outlives the doc. A diagnostics read; it advances nothing and changes nothing.",help:{name:"memory history",summary:"list a document\u2019s revisions newest-first, with diffs or one full record on demand",guide:"Every mutation through `crtr memory write`, `edit`, `move`, and `delete` appends one self-contained record \u2014 complete document text before and after, plus who, when, and (on an edit) why. Diffs and revision numbers are derived here at read time, never stored.\n\nA record whose `before` differs from the previous record\u2019s `after` is the evidence of an out-of-band direct file edit; nothing prevents those, and nothing detects them beyond this.\n\nThe log is a plain JSONL file at `log_path`, one JSON object per line \u2014 an external consumer reads it directly rather than through this command.",params:[{kind:"positional",name:"name",required:!0,constraint:"Full canonical memory name (a project document includes its namespace), resolved exactly. Deleted history uses the same canonical name and the selected store\u2019s physical sidecars; there is no leaf or scope-prefix fallback. Builtin and installed-plugin docs are read-only corpora and carry no history."},d("scope-read"),d("dir"),d("profile"),{kind:"flag",name:"limit",type:"int",required:!1,default:10,constraint:"How many revisions to return, newest first. Default 10."},{kind:"flag",name:"diff",type:"bool",required:!1,constraint:"Include a unified diff per listed revision, computed from that record\u2019s own before/after texts."},{kind:"flag",name:"revision",type:"int",required:!1,constraint:"Show ONE revision in full \u2014 its metadata plus the complete before and after document texts. 1-based position in the log: the number `crtr memory edit` reports. Overrides --limit and --diff."}],output:[{name:"name",type:"string",required:!0,constraint:"Resolved document name."},{name:"scope",type:"string",required:!0,constraint:"Scope whose store holds the log: node, project, profile, or user."},{name:"log_path",type:"string",required:!0,constraint:"Absolute path to the append-only JSONL log \u2014 the direct-consumption pointer, one self-contained record per line."},{name:"history_candidates",type:"object[]",required:!1,constraint:"Deleted-history physical candidates: {path, representation, winner}. The directory-index form is listed first and wins deterministically."},{name:"history_collision",type:"object",required:!1,constraint:"Present when deleted history has both physical forms: {paths, winner}."},{name:"total",type:"number",required:!0,constraint:"Total revisions in the log."},{name:"revisions",type:"object[]",required:!1,constraint:"Newest-first, capped by --limit. Each: {revision, op, at, node, rationale, body_changed, frontmatter_changed, diff}. `node` and `rationale` are present only when recorded; `diff` only with --diff. Absent when --revision selects one record."},{name:"record",type:"object",required:!1,constraint:"Present only with --revision: {revision, op, at, node, cwd, rationale, verbatim, before, after} \u2014 the full record including both complete document texts."},{name:"follow_up",type:"string",required:!0,constraint:"Concrete next commands \u2014 the flag variants and the doc read, or, for a deleted document, the revision carrying its retained final text."}],outputKind:"object",effects:["None. Read-only."]},render:s(n=>{const o=n.revisions;if(o===void 0||!o.some(t=>typeof t.diff=="string"))return p(n,y.help);const a=o.map(t=>{const i=[`op: ${String(t.op)}`,`at: ${String(t.at)}`];t.node!==void 0&&i.push(`node: ${String(t.node)}`),t.verbatim===!0&&i.push("verbatim");const e=t.frontmatter_changed??[],r=[...t.body_changed===!0?["body"]:[],...e.length>0?[`frontmatter: ${e.join(", ")}`]:[]],c=String(t.diff??"");return[`## revision ${String(t.revision)} \u2014 ${i.join(" \xB7 ")}`,...t.rationale!==void 0?[`rationale: ${String(t.rationale)}`]:[],`changed: ${r.length>0?r.join("; "):"nothing"}`,...c===""?[]:["","```diff",c,"```"]].join(`
2
+ `)});return[`- name: ${String(n.name)}`,`- scope: ${String(n.scope)}`,`- log_path: ${String(n.log_path)}`,`- total: ${String(n.total)}`,...(n.diagnostics??[]).map(t=>`- diagnostic: ${t}`),"",a.join(`
3
3
 
4
- `),"",String(t.follow_up)].join(`
5
- `)},"render"),run:l(async t=>{const n=t.name,c=t.scope,o=t.dir,r=t.profile,i=t.limit??10,s=t.diff===!0,a=t.revision,d=b({scope:c,dir:o,profile:r}),m=y();d.store===null&&g(m,d.scope,!0,!0);const e=v({target:m,scope:d.scope,store:d.store,name:n,limit:i,diff:s,revision:a}),h=e.deleted===void 0?{}:{history_candidates:e.deleted.candidates,...e.deleted.collision===void 0?{}:{history_collision:e.deleted.collision}};return"record"in e?{name:e.name,scope:e.scope,log_path:e.logPath,...h,total:e.total,record:e.record,follow_up:e.deleted===void 0?`Read the document as it stands with \`crtr memory read ${e.name}\`, or list the revisions with \`crtr memory history ${e.name} --diff\`.`:`${e.name} is deleted \u2014 no read answers to it. This record's \`before\` is its text at that revision; the last revision (${e.total}) carries the text it was deleted with. List the revisions with \`crtr memory history ${e.name} --diff\`.`}:{name:e.name,scope:e.scope,log_path:e.logPath,...h,total:e.total,revisions:e.revisions,follow_up:e.deleted===void 0?`Add --diff for per-revision unified diffs, --revision N for one record's full before/after text, or read the document as it stands with \`crtr memory read ${e.name}\`.`:`${e.name} is deleted and only this log survives \u2014 no read answers to it. Add --diff for per-revision unified diffs, or recover its final text with \`crtr memory history ${e.name} --revision ${e.total}\`, whose \`before\` is the document as it stood when deleted.`}},"run")});export{$ as historyLeaf};
4
+ `),"",String(n.follow_up)].join(`
5
+ `)},"render"),run:s(async n=>{const o=n.name,a=n.limit??10,t=n.diff===!0,i=n.revision,e=await h("GET",`/v1/memory/docs/${encodeURIComponent(o)}/history?${m({...u(n),mode:"cli",limit:a,diff:t,revision:i})}`),r=e.deleted===void 0?{}:{history_candidates:e.deleted.candidates,...e.deleted.collision===void 0?{}:{history_collision:e.deleted.collision}};return"record"in e?{name:e.name,scope:e.scope,log_path:e.logPath,...r,total:e.total,...e.diagnostics===void 0?{}:{diagnostics:e.diagnostics},record:e.record,follow_up:e.deleted===void 0?`Read the document as it stands with \`crtr memory read ${e.name}\`, or list the revisions with \`crtr memory history ${e.name} --diff\`.`:`${e.name} is deleted \u2014 no read answers to it. This record's \`before\` is its text at that revision; the last revision (${e.total}) carries the text it was deleted with. List the revisions with \`crtr memory history ${e.name} --diff\`.`}:{name:e.name,scope:e.scope,log_path:e.logPath,...r,total:e.total,...e.diagnostics===void 0?{}:{diagnostics:e.diagnostics},revisions:e.revisions,follow_up:e.deleted===void 0?`Add --diff for per-revision unified diffs, --revision N for one record's full before/after text, or read the document as it stands with \`crtr memory read ${e.name}\`.`:`${e.name} is deleted and only this log survives \u2014 no read answers to it. Add --diff for per-revision unified diffs, or recover its final text with \`crtr memory history ${e.name} --revision ${e.total}\`, whose \`before\` is the document as it stood when deleted.`}},"run")});export{y as historyLeaf};
@@ -1 +1 @@
1
- var h=Object.defineProperty;var s=(e,o)=>h(e,"name",{value:o,configurable:!0});import{envProfileId as u}from"../../shared/env.js";import{defineLeaf as f}from"../../core/command.js";import{warn as y}from"../../core/output.js";import{ExitCode as w}from"../../types.js";import{lintMemoryStore as g,lintMemoryTarget as v,lintOrphans as b,resolvableCanonicalNames as k}from"../../core/memory/lint.js";import{loadMemoryTargetView as x}from"../../core/memory-resolver.js";import{resolveReadSelector as j,selectorParam as l}from"./shared.js";function i(e){return{rule:e.rule,path:e.path,message:e.message,...e.name===void 0?{}:{name:e.name},...e.scope===void 0?{}:{scope:e.scope},...e.storeRoot===void 0?{}:{store_root:e.storeRoot},...e.paths===void 0?{}:{paths:[...e.paths]}}}s(i,"findingRow");function R(e){return x({cwd:e.ownerDir??e.storeRoot,profileId:u()||null,nodeId:null},{quiet:!0,includeDescendants:!0})}s(R,"targetViewForStore");const A=f({name:"lint",description:"validate memory identity, frontmatter, and references across a target view or one exact store",whenToUse:"you authored, moved, or migrated documents and want the authoring-time gate before shipping them. It strict-parses every doc the selection covers and fails on: invalid YAML or substrate schema, a repository whose root store declares no valid `namespace:` (so the runtime refuses to mount it), a `namespace:` outside the repository-root store\u2019s root INDEX.md, two directories in one repository whose paths normalize to the same prefix, a `name:` on a root INDEX.md or a non-root `name:` that is not a valid store-LOCAL name, two files in one store owning one canonical identity, a `[[link]]` or literal `memory-read` route naming no document or directory in the view (a directory\u2019s own document answers at the directory name, never at `<dir>/INDEX`), a body longer than its delivery entries earn, and a repository whose root store\u2019s workspace front door is missing or sits anywhere but its root INDEX.md. One canonical identity held by SEVERAL stores is legitimate (a linked worktree sharing its main checkout\u2019s namespace), so it is reported as a notice with the view\u2019s winner and every candidate, never failed. CI-friendly: non-zero exit on any error, warnings and notices stay non-fatal.",help:{name:"memory lint",summary:"validate canonical identity, frontmatter, and references across the memory corpus",params:[l("scope-read",{},"Lints only that source class. Cross-store candidate notices and project front-door coverage need the unfiltered view, so a scope filter suppresses them."),l("dir",{},"Lints that one store even when the runtime refuses to mount it \u2014 the only way to check a project store before its repository namespace is declared. Its links and routes are resolved against the target view that project\u2019s own directory sees, and an unmounted store defers those reference checks (its identities are undetermined until the namespace exists).")],output:[{name:"checked",type:"number",required:!0,constraint:"Markdown files examined."},{name:"stores",type:"number",required:!0,constraint:"Physical memory stores linted."},{name:"profile_projects",type:"number",required:!1,constraint:"Projects the selected profile manages that were checked for a workspace front door \u2014 zero under a scope filter excluding project stores. Absent under --dir."},{name:"findings",type:"object[]",required:!0,constraint:"One row per ERROR: {rule, path, message, name?, scope?, store_root?, paths?}. `rule` names the violated rule (yaml, schema, extension, length, store-namespace, namespace-placement, root-name, root-index-unaddressable, local-name, canonical-collision, dangling-link, memory-read-route, front-door, owner-path-collision). `paths` carries every file sharing a collided identity, read winner first. Empty when green."},{name:"warnings",type:"object[]",required:!0,constraint:"Same row shape, non-fatal authoring smells: short-preview, broad-memory-read, nested-store-boot, an advisory missing front door, a wildcard memory-read glob matching nothing, and orphan (a doc no reachability path routes an agent to \u2014 unfiltered target/--dir view only, never under --scope)."},{name:"notices",type:"object[]",required:!0,constraint:"Same row shape, inspection data rather than faults: cross-store-candidates (one canonical name held by several stores, winner first) and reference-check-deferred."},{name:"follow_up",type:"string",required:!0,constraint:"Concrete next command."}],outputKind:"object",effects:["None. Read-only. Exits non-zero when any error finding exists."]},run:s(async e=>{const o=j({scope:e.scope,dir:e.dir});let r;if(o.store!==null){const t=R(o.store),a=g(o.store,{resolvable:k(t.docs),frontDoor:"advisory"}),d=b(t.docs,{flagStoreRoot:o.store.storeRoot});r={...a,findings:[...a.findings,...d],warnings:[...a.warnings,...d]}}else r=v({...o.scope===void 0?{}:{scope:o.scope}});const n=r.errors.map(i),c=r.warnings.map(i),m=r.notices.map(i),p={checked:r.checked,stores:r.storesLinted,..."profileProjects"in r?{profile_projects:r.profileProjects}:{}};for(const t of[...r.warnings,...r.errors])y(`memory lint: ${t.rule}: ${t.path}: ${t.message}`);return n.length>0&&(process.exitCode=w.GENERAL),{...p,findings:n,warnings:c,notices:m,follow_up:n.length>0?`${n.length} error(s) across ${r.checked} files in ${r.storesLinted} store(s) \u2014 fix each, then re-run. A repository store that does not mount is repaired with \`crtr sys migrate --dir <repository-root>\`; an owner-path collision is fixed by renaming one of the directories named in the finding; a canonical collision is recovered with \`crtr memory move <name> --to <new-free-name>\` (it selects the first physical path in stable lexical order); a dangling link or route is retargeted with the name \`crtr memory find <leaf>\` reports, or dropped; an over-length body splits into [[linked]] surface-less reference docs.`:c.length>0?'No errors \u2014 review the warnings above, then revise with `crtr memory edit <name> --rationale "<why>"`.':"No errors. Notices are inspection data, not faults: a canonical name held by several stores is reachable per-store with `crtr memory read <name> --dir <project>`."}},"run")});export{A as lintLeaf};
1
+ var m=Object.defineProperty;var a=(r,o)=>m(r,"name",{value:o,configurable:!0});import{memoryRequest as l,memorySelection as h}from"./client.js";import{defineLeaf as p}from"../../core/command.js";import{warn as u}from"../../core/output.js";import{ExitCode as f}from"../../types.js";import{selectorParam as i}from"./shared.js";const j=p({name:"lint",description:"validate memory identity, frontmatter, and references across a target view or one exact store",whenToUse:"you authored, moved, or migrated documents and want the authoring-time gate before shipping them. It strict-parses every doc the selection covers and fails on: invalid YAML or substrate schema, a repository whose root store declares no valid `namespace:` (so the runtime refuses to mount it), a `namespace:` outside the repository-root store\u2019s root INDEX.md, two directories in one repository whose paths normalize to the same prefix, a `name:` on a root INDEX.md or a non-root `name:` that is not a valid store-LOCAL name, two files in one store owning one canonical identity, a `[[link]]` or literal `memory-read` route naming no document or directory in the view (a directory\u2019s own document answers at the directory name, never at `<dir>/INDEX`), a body longer than its delivery entries earn, and a repository whose root store\u2019s workspace front door is missing or sits anywhere but its root INDEX.md. One canonical identity held by SEVERAL stores is legitimate (a linked worktree sharing its main checkout\u2019s namespace), so it is reported as a notice with the view\u2019s winner and every candidate, never failed. CI-friendly: non-zero exit on any error, warnings and notices stay non-fatal.",help:{name:"memory lint",summary:"validate canonical identity, frontmatter, and references across the memory corpus",params:[i("scope-read",{},"Lints only that source class. Cross-store candidate notices and project front-door coverage need the unfiltered view, so a scope filter suppresses them."),i("dir",{},"Lints that one store even when the runtime refuses to mount it \u2014 the only way to check a project store before its repository namespace is declared. Its links and routes are resolved against the target view that project\u2019s own directory sees, and an unmounted store defers those reference checks (its identities are undetermined until the namespace exists).")],output:[{name:"checked",type:"number",required:!0,constraint:"Markdown files examined."},{name:"stores",type:"number",required:!0,constraint:"Physical memory stores linted."},{name:"profile_projects",type:"number",required:!1,constraint:"Projects the selected profile manages that were checked for a workspace front door \u2014 zero under a scope filter excluding project stores. Absent under --dir."},{name:"findings",type:"object[]",required:!0,constraint:"One row per ERROR: {rule, path, message, name?, scope?, store_root?, paths?}. `rule` names the violated rule (yaml, schema, extension, length, store-namespace, namespace-placement, root-name, root-index-unaddressable, local-name, canonical-collision, dangling-link, memory-read-route, front-door, owner-path-collision). `paths` carries every file sharing a collided identity, read winner first. Empty when green."},{name:"warnings",type:"object[]",required:!0,constraint:"Same row shape, non-fatal authoring smells: short-preview, broad-memory-read, nested-store-boot, an advisory missing front door, a wildcard memory-read glob matching nothing, and orphan (a doc no reachability path routes an agent to \u2014 unfiltered target/--dir view only, never under --scope)."},{name:"notices",type:"object[]",required:!0,constraint:"Same row shape, inspection data rather than faults: cross-store-candidates (one canonical name held by several stores, winner first) and reference-check-deferred."},{name:"follow_up",type:"string",required:!0,constraint:"Concrete next command."}],outputKind:"object",effects:["None. Read-only. Exits non-zero when any error finding exists."]},run:a(async r=>{const o=r.scope;if(r.dir!==void 0&&o!==void 0)throw new Error("--dir cannot combine with --scope");const e=await l("POST","/v1/memory/lint",{...h(r),mode:"cli"}),t=e.findings,n=e.warnings,c=e.notices,d={checked:e.checked,stores:e.stores,...e.profile_projects===void 0?{}:{profile_projects:e.profile_projects}};for(const s of[...n,...t])u(`memory lint: ${s.rule}: ${s.path}: ${s.message}`);return t.length>0&&(process.exitCode=f.GENERAL),{...d,findings:t,warnings:n,notices:c,follow_up:t.length>0?`${t.length} error(s) across ${e.checked} files in ${e.stores} store(s) \u2014 fix each, then re-run. A repository store that does not mount is repaired with \`crtr sys migrate --dir <repository-root>\`; an owner-path collision is fixed by renaming one of the directories named in the finding; a canonical collision is recovered with \`crtr memory move <name> --to <new-free-name>\` (it selects the first physical path in stable lexical order); a dangling link or route is retargeted with the name \`crtr memory find <leaf>\` reports, or dropped; an over-length body splits into [[linked]] surface-less reference docs.`:n.length>0?'No errors \u2014 review the warnings above, then revise with `crtr memory edit <name> --rationale "<why>"`.':"No errors. Notices are inspection data, not faults: a canonical name held by several stores is reachable per-store with `crtr memory read <name> --dir <project>`."}},"run")});export{j as lintLeaf};
@@ -1,3 +1 @@
1
- export { compareMemoryListKeys, listDocs as listMemoryDocs, memoryListKey } from '../../core/memory/service.js';
2
- export type { ListedMemoryDoc } from '../../core/memory/service.js';
3
1
  export declare const listLeaf: import("../../core/command.js").LeafDef;
@@ -1 +1 @@
1
- var f=Object.defineProperty;var i=(e,r)=>f(e,"name",{value:r,configurable:!0});import{defineLeaf as w}from"../../core/command.js";import{ambientMemoryTarget as g,memoryStoresInPrecedence as v}from"../../core/memory-resolver.js";import{compareMemoryListKeys as k,candidateLocation as b,listDocs as x,memoryListKey as _}from"../../core/memory/service.js";import{renderResult as q}from"../../core/render.js";import{paginate as L}from"../../core/pagination.js";import{MEMORY_KINDS as M,registerMemoryListingExposure as R,resolveReadSelector as O,selectorParam as h}from"./shared.js";import{compareMemoryListKeys as A,listDocs as C,memoryListKey as J}from"../../core/memory/service.js";const D=w({name:"list",description:"inventory of stored memory documents",whenToUse:"browse everything stored \u2014 one row per canonical name with its short-form hook, kind, and scope. Every name is readable as-is with `crtr memory read`. This is the one surface that shows short-form. Reach for `crtr memory find` instead when you already have a topic or keyword in mind rather than wanting the whole inventory.",help:{name:"memory list",summary:"inventory every memory document across the resolved scopes, one row per canonical name",params:[{kind:"flag",name:"kind",type:"enum",choices:[...M],required:!1,constraint:"Filter to a single kind. Default: all kinds."},h("scope-read",{},"Default: every resolved scope."),h("dir",{},"Lists that store alone, and lists it physically: two documents sharing one canonical name both appear, where the default view shows only the winner."),{kind:"flag",name:"paths",type:"bool",required:!1,default:!1,constraint:"Include each row's physical location \u2014 absolute path, store root, store-relative path, and every other physical document sharing its canonical name. Off by default."},{kind:"flag",name:"limit",type:"int",required:!1,default:50,constraint:"Default 50, max 200."},{kind:"flag",name:"cursor",type:"string",required:!1,constraint:"Opaque token from next_cursor. Omit on first call."}],output:[{name:"items",type:"object[]",required:!0,constraint:"One row per canonical name: {name, short_form, kind, scope, extensions}. extensions carries effective valid plugin-owned metadata; enabled defaults appear here without becoming frontmatter, while disabled and unresolved namespaces are omitted. With --paths each row also carries path, store_root, physical_relative_path, and \u2014 only when several physical documents share the name \u2014 candidates, one {winner, scope, store_root, physical_relative_path, path} row each in source order. Every name is readable as-is with `crtr memory read` (a name that carries members answers with its own document plus that directory listing). short_form is the abbreviated hook \u2014 shown here and nowhere else. Sorted by scope then kind then name ascending."},{name:"next_cursor",type:"string | null",required:!0,constraint:"Opaque token for the next page; null means no more items."},{name:"total",type:"integer | null",required:!0,constraint:"Exact total across all pages when cheap to compute (always cheap here); never null in practice for this leaf."},{name:"follow_up",type:"string",required:!0,constraint:"Concrete next commands \u2014 read a document in full or narrow the inventory."}],outputKind:"object",effects:["Records this inventory page as listing context for the current node without marking any listed document as loaded."]},run:i(async e=>{const r=e.kind,n=e.dir,a=e.paths??!1,l=e.limit,c=l!==void 0?Math.min(Math.max(1,l),200):50,m=e.cursor,o=O({scope:e.scope,dir:n}),d=g();o.store===null&&v(d,o.scope,!0,!0);const p=x({target:d,scope:o.scope,store:o.store,kind:r}),s=L(p,{limit:c,cursor:m},{defaultLimit:50,maxLimit:200,keyOf:_,compareKeys:k,total:"count"}),u={items:s.items.map(t=>({name:t.name,short_form:t.shortForm,kind:t.kind,scope:t.scope,extensions:t.extensions,...a?{path:t.path,store_root:t.storeRoot,physical_relative_path:t.physicalRelativePath,...t.candidates.length>1?{candidates:t.candidates.map(y=>b(y,t.candidates[0]))}:{}}:{}})),next_cursor:s.next_cursor,total:s.total,follow_up:'Read one in full with `crtr memory read <name>`, or revise one with `crtr memory edit <name> --rationale "<why>"` (body, routing, frontmatter, and visibility all go through that one verb, and it records the change). Narrow with --kind / --scope, page with --cursor, or search a topic with `crtr memory find <query>`.'};return R(`listing:memory-list:${JSON.stringify({kind:r??null,scope:o.scope??null,dir:n??null,paths:a,limit:c,cursor:m??null})}`),u},"run"),render:i(e=>{const r=e.items.map(({extensions:n,...a})=>a);return q({...e,items:r},D.help)},"render")});export{A as compareMemoryListKeys,D as listLeaf,C as listMemoryDocs,J as memoryListKey};
1
+ var c=Object.defineProperty;var s=(t,r)=>c(t,"name",{value:r,configurable:!0});import{defineLeaf as d}from"../../core/command.js";import{memoryQuery as p,memoryRequest as u,memorySelection as y}from"./client.js";import{renderResult as f}from"../../core/render.js";import{MEMORY_KINDS as w,selectorParam as l}from"./shared.js";const v=d({name:"list",description:"inventory of stored memory documents",whenToUse:"browse everything stored \u2014 one row per canonical name with its short-form hook, kind, and scope. Every name is readable as-is with `crtr memory read`. This is the one surface that shows short-form. Reach for `crtr memory find` instead when you already have a topic or keyword in mind rather than wanting the whole inventory.",help:{name:"memory list",summary:"inventory every memory document across the resolved scopes, one row per canonical name",params:[{kind:"flag",name:"kind",type:"enum",choices:[...w],required:!1,constraint:"Filter to a single kind. Default: all kinds."},l("scope-read",{},"Default: every resolved scope."),l("dir",{},"Lists that store alone, and lists it physically: two documents sharing one canonical name both appear, where the default view shows only the winner."),{kind:"flag",name:"paths",type:"bool",required:!1,default:!1,constraint:"Include each row's physical location \u2014 absolute path, store root, store-relative path, and every other physical document sharing its canonical name. Off by default."},{kind:"flag",name:"limit",type:"int",required:!1,default:50,constraint:"Default 50, max 200."},{kind:"flag",name:"cursor",type:"string",required:!1,constraint:"Opaque token from next_cursor. Omit on first call."}],output:[{name:"items",type:"object[]",required:!0,constraint:"One row per canonical name: {name, short_form, kind, privacy, scope, extensions}. extensions carries effective valid plugin-owned metadata; enabled defaults appear here without becoming frontmatter, while disabled and unresolved namespaces are omitted. With --paths each row also carries path, store_root, physical_relative_path, and \u2014 only when several physical documents share the name \u2014 candidates, one {winner, scope, store_root, physical_relative_path, path} row each in source order. Every name is readable as-is with `crtr memory read` (a name that carries members answers with its own document plus that directory listing). short_form is the abbreviated hook \u2014 shown here and nowhere else. Sorted by scope then kind then name ascending."},{name:"next_cursor",type:"string | null",required:!0,constraint:"Opaque token for the next page; null means no more items."},{name:"total",type:"integer | null",required:!0,constraint:"Exact total across all pages when cheap to compute (always cheap here); never null in practice for this leaf."},{name:"follow_up",type:"string",required:!0,constraint:"Concrete next commands \u2014 read a document in full or narrow the inventory."}],outputKind:"object",effects:["Records this inventory page as listing context for the current node without marking any listed document as loaded."]},run:s(async t=>{const r=t.kind,i=t.paths??!1,o=t.limit,m=o!==void 0?Math.min(Math.max(1,o),200):50,h=t.cursor,n=await u("GET",`/v1/memory/docs?${p({...y(t),mode:"cli",kind:r,limit:m,cursor:h})}`);return{items:n.items.map(e=>({name:e.name,short_form:e.shortForm,kind:e.kind,privacy:e.privacy,scope:e.scope,extensions:e.extensions,...i?{path:e.path,store_root:e.storeRoot,physical_relative_path:e.physicalRelativePath,...e.candidates.length>1?{candidates:e.candidates.map(a=>({winner:a.path===e.path,scope:a.scope,store_root:a.root,physical_relative_path:a.physicalRelativePath,path:a.path}))}:{}}:{}})),next_cursor:n.next_cursor,total:n.total,follow_up:'Read one in full with `crtr memory read <name>`, or revise one with `crtr memory edit <name> --rationale "<why>"` (body and frontmatter go through that one verb, and it records the change). Narrow with --kind / --scope, page with --cursor, or search a topic with `crtr memory find <query>`.'}},"run"),render:s(t=>{const r=t.items.map(({extensions:i,...o})=>o);return f({...t,items:r},v.help)},"render")});export{v as listLeaf};
@@ -1 +1 @@
1
- var a=Object.defineProperty;var n=(e,t)=>a(e,"name",{value:t,configurable:!0});import{defineLeaf as i}from"../../core/command.js";import{moveDoc as s}from"../../core/memory/mutations.js";import{ambientMemoryTarget as c}from"../../core/memory-resolver.js";import{MEMORY_WRITE_SCOPES as d,historyActorFromEnvironment as l,resolveReadSelector as m,selectorParam as r}from"./shared.js";const f=i({name:"move",description:"rename or relocate a memory document, rewriting inbound links",whenToUse:"a stored document should answer to a different canonical name \u2014 renamed in place or relocated under another directory. The sanctioned way to move one, so you never mv the markdown off disk: this verb carries the document\u2019s revision history along and rewrites every inbound `[[ref]]` across writable stores, so authored links break loudly or not at all. Source and destination are both full canonical names in the SAME store, so a move never changes which repository-relative prefix the document composes under. The repository-root document is immutable, but a nested store\u2019s optional root guide is an ordinary document that may move. One document per move: moving a directory\u2019s own document leaves its members where they are, so relocate a whole directory member by member. If two physical files in that store share the source identity, this verb selects the first physical path in stable lexical order and recovers the collision.",help:{name:"memory move",summary:"move one document to a new canonical name inside its own store, rewriting inbound [[refs]]",params:[{kind:"positional",name:"name",required:!0,constraint:"Full canonical name of the document to move \u2014 a project document is `<namespace>/<local name>`. Resolution is exact, as `read` resolves it: a file path, a bare leaf, a `<scope>/<name>` spelling, and a trailing `/INDEX` all fail. If two physical files in the selected store own the name, move selects the first path in stable lexical order and recovers the collision. Builtin and installed-plugin documents are read-only and refuse to move, as does the repository-root document, which declares the immutable namespace for every document in the repository; a nested store\u2019s root guide is ordinary and may move."},{kind:"flag",name:"to",type:"string",required:!0,constraint:"The new full canonical name, in the SAME store \u2014 the repository namespace and store path included, since a move never crosses stores. `/`-joined segments of [A-Za-z0-9_-], no trailing `INDEX` (a directory\u2019s document answers at the directory name). A nested store\u2019s effective prefix is a legal destination for its optional root guide. Rejected when a document in that store already answers to it, or when either `<local name>.md` or `<local name>/INDEX.md` already exists there."},r("scope-read",{choices:[...d]},"Package-shipped documents cannot move through this writable-scope selector."),r("dir",{},"Source, destination, and inbound-link rewriting all stay inside that one store, so moving a non-winning duplicate leaves every other store untouched \u2014 and rewrites no link at all, because the name still resolves to the candidate that outranks it here.")],output:[{name:"name",type:"string",required:!0,constraint:"The document\u2019s new canonical name \u2014 `--to` after canonicalization, so it can differ from what you passed."},{name:"previous_name",type:"string",required:!0,constraint:"The canonical name it answered to before the move."},{name:"scope",type:"string",required:!0,constraint:"Scope of the store the document moved within: node, user, project, or profile."},{name:"path",type:"string",required:!0,constraint:"Absolute path of the document at its new location \u2014 `<local name>/INDEX.md` when that canonical directory already has members in the store, else `<local name>.md`."},{name:"log_path",type:"string",required:!0,constraint:"Absolute path to the revision log at its new location, ending in a `move` record that names the previous canonical name."},{name:"refs_rewritten",type:"object[]",required:!0,constraint:"Documents whose bodies were rewritten from `[[<previous_name>]]` to `[[<name>]]`. Each: {name, scope, path, refs}. Empty when nothing linked to the document \u2014 or when the moved file is not what that name resolves to here (another store\u2019s candidate wins the address), in which case inbound refs keep resolving to that winner and are left alone."},{name:"follow_up",type:"string",required:!0,constraint:"Outcome-dependent collision recovery and inbound-reference rewriting signal."}],outputKind:"object",effects:["Moves the document to the physical form its new local name takes in that store (creating parent directories, pruning any it leaves empty) and moves its `.history/<physical path>.jsonl` log with it, appending a `move` record naming the previous canonical name.","Drops an explicit `name:` frontmatter pin, so the moved document answers to its path-derived identity \u2014 the name you asked for.","Rewrites every inbound `[[<name>]]` doc link to `[[<new-name>]]` \u2014 across the target view\u2019s writable stores, or inside the one store `--dir` selected \u2014 appending an `edit` record to each rewritten document\u2019s revision log. Aborts before any write when a read-only (builtin or installed-plugin) document carries such a link."]},run:n(async e=>{const t=m({scope:e.scope,dir:e.dir}),o=c();return s({target:o,selection:{target:o,scope:t.scope,store:t.store},history:l(),fields:e})},"run")});export{f as moveLeaf};
1
+ var r=Object.defineProperty;var t=(e,n)=>r(e,"name",{value:n,configurable:!0});import{defineLeaf as a}from"../../core/command.js";import{memoryRequest as i,memorySelection as s}from"./client.js";import{MEMORY_WRITE_SCOPES as c,selectorParam as o}from"./shared.js";const u=a({name:"move",description:"rename or relocate a memory document, rewriting inbound links",whenToUse:"a stored document should answer to a different canonical name \u2014 renamed in place or relocated under another directory. The sanctioned way to move one, so you never mv the markdown off disk: this verb carries the document\u2019s revision history along and rewrites every inbound `[[ref]]` across writable stores, so authored links break loudly or not at all. Source and destination are both full canonical names in the SAME store, so a move never changes which repository-relative prefix the document composes under. The repository-root document is immutable, but a nested store\u2019s optional root guide is an ordinary document that may move. One document per move: moving a directory\u2019s own document leaves its members where they are, so relocate a whole directory member by member. If two physical files in that store share the source identity, this verb selects the first physical path in stable lexical order and recovers the collision.",help:{name:"memory move",summary:"move one document to a new canonical name inside its own store, rewriting inbound [[refs]]",params:[{kind:"positional",name:"name",required:!0,constraint:"Full canonical name of the document to move \u2014 a project document is `<namespace>/<local name>`. Resolution is exact, as `read` resolves it: a file path, a bare leaf, a `<scope>/<name>` spelling, and a trailing `/INDEX` all fail. If two physical files in the selected store own the name, move selects the first path in stable lexical order and recovers the collision. Builtin and installed-plugin documents are read-only and refuse to move, as does the repository-root document, which declares the immutable namespace for every document in the repository; a nested store\u2019s root guide is ordinary and may move."},{kind:"flag",name:"to",type:"string",required:!0,constraint:"The new full canonical name, in the SAME store \u2014 the repository namespace and store path included, since a move never crosses stores. `/`-joined segments of [A-Za-z0-9_-], no trailing `INDEX` (a directory\u2019s document answers at the directory name). A nested store\u2019s effective prefix is a legal destination for its optional root guide. Rejected when a document in that store already answers to it, or when either `<local name>.md` or `<local name>/INDEX.md` already exists there."},o("scope-read",{choices:[...c]},"Package-shipped documents cannot move through this writable-scope selector."),o("dir",{},"Source, destination, and inbound-link rewriting all stay inside that one store, so moving a non-winning duplicate leaves every other store untouched \u2014 and rewrites no link at all, because the name still resolves to the candidate that outranks it here.")],output:[{name:"name",type:"string",required:!0,constraint:"The document\u2019s new canonical name \u2014 `--to` after canonicalization, so it can differ from what you passed."},{name:"previous_name",type:"string",required:!0,constraint:"The canonical name it answered to before the move."},{name:"scope",type:"string",required:!0,constraint:"Scope of the store the document moved within: node, user, project, or profile."},{name:"path",type:"string",required:!0,constraint:"Absolute path of the document at its new location \u2014 `<local name>/INDEX.md` when that canonical directory already has members in the store, else `<local name>.md`."},{name:"log_path",type:"string",required:!0,constraint:"Absolute path to the revision log at its new location, ending in a `move` record that names the previous canonical name."},{name:"refs_rewritten",type:"object[]",required:!0,constraint:"Documents whose bodies were rewritten from `[[<previous_name>]]` to `[[<name>]]`. Each: {name, scope, path, refs}. Empty when nothing linked to the document \u2014 or when the moved file is not what that name resolves to here (another store\u2019s candidate wins the address), in which case inbound refs keep resolving to that winner and are left alone."},{name:"follow_up",type:"string",required:!0,constraint:"Outcome-dependent collision recovery and inbound-reference rewriting signal."}],outputKind:"object",effects:["Moves the document to the physical form its new local name takes in that store (creating parent directories, pruning any it leaves empty) and moves its `.history/<physical path>.jsonl` log with it, appending a `move` record naming the previous canonical name.","Drops an explicit `name:` frontmatter pin, so the moved document answers to its path-derived identity \u2014 the name you asked for.","Rewrites every inbound `[[<name>]]` doc link to `[[<new-name>]]` \u2014 across the target view\u2019s writable stores, or inside the one store `--dir` selected \u2014 appending an `edit` record to each rewritten document\u2019s revision log. Aborts before any write when a read-only (builtin or installed-plugin) document carries such a link."]},run:t(async e=>i("POST",`/v1/memory/docs/${encodeURIComponent(e.name)}/move`,{...s(e),mode:"cli",fields:e}),"run")});export{u as moveLeaf};