@crouton-kit/crouter 0.3.32 → 0.3.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (440) hide show
  1. package/README.md +8 -0
  2. package/dist/build-root.js +1 -0
  3. package/dist/builtin-memory/crouter-development/marketplaces.md +2 -2
  4. package/dist/builtin-memory/crouter-development/personas/base-prompt.md +2 -2
  5. package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +5 -5
  6. package/dist/builtin-memory/crouter-development/personas.md +3 -4
  7. package/dist/builtin-memory/crouter-development/plugins.md +18 -18
  8. package/dist/builtin-memory/design.md +4 -1
  9. package/dist/builtin-memory/development.md +4 -1
  10. package/dist/builtin-memory/internal/INDEX.md +1 -1
  11. package/dist/builtin-memory/internal/nodes-and-canvas.md +6 -6
  12. package/dist/builtin-memory/internal/storage-tiers.md +5 -5
  13. package/dist/builtin-memory/planning.md +4 -1
  14. package/dist/builtin-memory/product.md +80 -0
  15. package/dist/builtin-memory/spec.md +4 -1
  16. package/dist/builtin-personas/advisor/PERSONA.md +10 -0
  17. package/dist/builtin-personas/design/PERSONA.md +2 -2
  18. package/dist/builtin-personas/design/orchestrator.md +3 -3
  19. package/dist/builtin-personas/developer/PERSONA.md +1 -1
  20. package/dist/builtin-personas/developer/orchestrator.md +2 -2
  21. package/dist/builtin-personas/explore/PERSONA.md +3 -3
  22. package/dist/builtin-personas/explore/orchestrator.md +4 -2
  23. package/dist/builtin-personas/general/PERSONA.md +1 -1
  24. package/dist/builtin-personas/general/orchestrator.md +1 -1
  25. package/dist/builtin-personas/orchestration-kernel.md +7 -14
  26. package/dist/builtin-personas/plan/PERSONA.md +1 -1
  27. package/dist/builtin-personas/plan/orchestrator.md +2 -2
  28. package/dist/builtin-personas/plan/reviewers/architecture-fit/PERSONA.md +1 -1
  29. package/dist/builtin-personas/plan/reviewers/code-smells/PERSONA.md +1 -1
  30. package/dist/builtin-personas/plan/reviewers/pattern-consistency/PERSONA.md +1 -1
  31. package/dist/builtin-personas/plan/reviewers/requirements-coverage/PERSONA.md +1 -1
  32. package/dist/builtin-personas/plan/reviewers/security/PERSONA.md +1 -1
  33. package/dist/builtin-personas/product/PERSONA.md +18 -0
  34. package/dist/builtin-personas/product/orchestrator.md +14 -0
  35. package/dist/builtin-personas/product/teardown/PERSONA.md +13 -0
  36. package/dist/builtin-personas/review/PERSONA.md +1 -1
  37. package/dist/builtin-personas/review/orchestrator.md +1 -1
  38. package/dist/builtin-personas/runtime-base.md +5 -0
  39. package/dist/builtin-personas/spec/PERSONA.md +1 -1
  40. package/dist/builtin-personas/spec/orchestrator.md +2 -2
  41. package/dist/builtin-personas/spec/requirements/PERSONA.md +1 -1
  42. package/dist/builtin-views/canvas/core.mjs +82 -1
  43. package/dist/builtin-views/canvas/tui.mjs +9 -5
  44. package/dist/builtin-views/canvas/web.jsx +3 -2
  45. package/dist/builtin-views/chat/core.mjs +725 -0
  46. package/dist/builtin-views/chat/text.mjs +101 -0
  47. package/dist/builtin-views/chat/tui.mjs +368 -0
  48. package/dist/builtin-views/chat/web.jsx +367 -0
  49. package/dist/builtin-views/prompt-review/core.mjs +863 -0
  50. package/dist/builtin-views/prompt-review/text.mjs +15 -0
  51. package/dist/builtin-views/prompt-review/tui.mjs +196 -0
  52. package/dist/builtin-views/prompt-review/web.jsx +484 -0
  53. package/dist/builtin-views/settings/core.mjs +397 -0
  54. package/dist/builtin-views/settings/text.mjs +40 -0
  55. package/dist/builtin-views/settings/tui.mjs +95 -0
  56. package/dist/builtin-views/settings/web.jsx +167 -0
  57. package/dist/cli.js +16 -3
  58. package/dist/clients/attach/__tests__/autocomplete-and-bash-mode.test.js +96 -0
  59. package/dist/clients/attach/__tests__/bash-bang-routing.test.js +58 -0
  60. package/dist/clients/attach/__tests__/bundle-pi-tui-dedup.test.d.ts +1 -0
  61. package/dist/clients/attach/__tests__/bundle-pi-tui-dedup.test.js +56 -0
  62. package/dist/clients/attach/__tests__/titled-editor.test.js +22 -1
  63. package/dist/clients/attach/attach-cmd.d.ts +18 -0
  64. package/dist/clients/attach/attach-cmd.js +1723 -795
  65. package/dist/clients/attach/canvas-panels.js +2 -2
  66. package/dist/clients/attach/chat-view.d.ts +10 -0
  67. package/dist/clients/attach/chat-view.js +23 -0
  68. package/dist/clients/attach/config-load.d.ts +8 -4
  69. package/dist/clients/attach/config-load.js +2 -0
  70. package/dist/clients/attach/extension-dialogs.d.ts +2 -3
  71. package/dist/clients/attach/extension-dialogs.js +7 -9
  72. package/dist/clients/attach/graph-overlay.js +3 -2
  73. package/dist/clients/attach/input-controller.d.ts +4 -3
  74. package/dist/clients/attach/input-controller.js +24 -3
  75. package/dist/clients/attach/slash-commands.d.ts +7 -7
  76. package/dist/clients/attach/slash-commands.js +38 -14
  77. package/dist/clients/attach/titled-editor.js +18 -14
  78. package/dist/clients/attach/view-socket.d.ts +2 -1
  79. package/dist/clients/attach/view-socket.js +27 -8
  80. package/dist/clients/web/__tests__/source-cache.test.d.ts +1 -0
  81. package/dist/clients/web/__tests__/source-cache.test.js +32 -0
  82. package/dist/clients/web/dev-server.js +1 -0
  83. package/dist/clients/web/events.js +9 -11
  84. package/dist/clients/web/server.d.ts +4 -3
  85. package/dist/clients/web/server.js +138 -31
  86. package/dist/clients/web/source-cache.d.ts +10 -0
  87. package/dist/clients/web/source-cache.js +57 -0
  88. package/dist/clients/web/web-cmd.js +28 -9
  89. package/dist/commands/__tests__/human.test.js +30 -28
  90. package/dist/commands/canvas-history/search.js +1 -1
  91. package/dist/commands/canvas-history.js +5 -8
  92. package/dist/commands/canvas-issue.d.ts +2 -0
  93. package/dist/commands/canvas-issue.js +148 -0
  94. package/dist/commands/canvas-prune.js +19 -0
  95. package/dist/commands/canvas-rebuild-index.d.ts +2 -0
  96. package/dist/commands/canvas-rebuild-index.js +57 -0
  97. package/dist/commands/canvas-snapshot.d.ts +2 -0
  98. package/dist/commands/canvas-snapshot.js +48 -0
  99. package/dist/commands/canvas-tmux-spread.d.ts +2 -0
  100. package/dist/commands/canvas-tmux-spread.js +188 -0
  101. package/dist/commands/canvas.d.ts +1 -0
  102. package/dist/commands/canvas.js +16 -1
  103. package/dist/commands/chord.js +143 -48
  104. package/dist/commands/daemon.js +3 -3
  105. package/dist/commands/human/prompts.d.ts +0 -1
  106. package/dist/commands/human/prompts.js +39 -54
  107. package/dist/commands/human/queue.d.ts +12 -1
  108. package/dist/commands/human/queue.js +468 -73
  109. package/dist/commands/human/shared.d.ts +3 -4
  110. package/dist/commands/human/shared.js +3 -3
  111. package/dist/commands/human.js +11 -10
  112. package/dist/commands/memory/find.js +3 -2
  113. package/dist/commands/memory/list.js +4 -3
  114. package/dist/commands/memory/read.js +3 -3
  115. package/dist/commands/memory/shared.d.ts +1 -4
  116. package/dist/commands/memory/shared.js +6 -9
  117. package/dist/commands/memory/write.js +3 -3
  118. package/dist/commands/memory.js +4 -7
  119. package/dist/commands/node-context.d.ts +2 -0
  120. package/dist/commands/node-context.js +172 -0
  121. package/dist/commands/node-snapshot.d.ts +2 -0
  122. package/dist/commands/node-snapshot.js +123 -0
  123. package/dist/commands/node.js +228 -52
  124. package/dist/commands/pkg/plugin-inspect.js +6 -6
  125. package/dist/commands/pkg/plugin-manage.js +1 -1
  126. package/dist/commands/pkg/plugin.js +2 -2
  127. package/dist/commands/pkg.js +2 -2
  128. package/dist/commands/push.d.ts +2 -0
  129. package/dist/commands/push.js +79 -11
  130. package/dist/commands/revive.js +74 -1
  131. package/dist/commands/search/answer.d.ts +1 -0
  132. package/dist/commands/search/answer.js +50 -0
  133. package/dist/commands/search/contents.d.ts +1 -0
  134. package/dist/commands/search/contents.js +96 -0
  135. package/dist/commands/search/exa.d.ts +53 -0
  136. package/dist/commands/search/exa.js +155 -0
  137. package/dist/commands/search/puremd.d.ts +11 -0
  138. package/dist/commands/search/puremd.js +53 -0
  139. package/dist/commands/search/web.d.ts +1 -0
  140. package/dist/commands/search/web.js +65 -0
  141. package/dist/commands/search.d.ts +2 -0
  142. package/dist/commands/search.js +24 -0
  143. package/dist/commands/sys/__tests__/sync-import.test.d.ts +1 -0
  144. package/dist/commands/sys/__tests__/sync-import.test.js +72 -0
  145. package/dist/commands/sys/config.js +59 -10
  146. package/dist/commands/sys/doctor.js +56 -4
  147. package/dist/commands/sys/prompt-review.d.ts +1 -0
  148. package/dist/commands/sys/prompt-review.js +178 -0
  149. package/dist/commands/sys/promptstudio.d.ts +2 -0
  150. package/dist/commands/sys/promptstudio.js +65 -0
  151. package/dist/commands/sys/settings.d.ts +2 -0
  152. package/dist/commands/sys/settings.js +16 -0
  153. package/dist/commands/sys/sync.js +311 -159
  154. package/dist/commands/sys/sysprompt.d.ts +1 -0
  155. package/dist/commands/sys/sysprompt.js +86 -0
  156. package/dist/commands/sys.js +9 -5
  157. package/dist/commands/view-new.js +2 -2
  158. package/dist/core/__tests__/artifact-paths.test.d.ts +1 -0
  159. package/dist/core/__tests__/artifact-paths.test.js +44 -0
  160. package/dist/core/__tests__/broker-preflight.test.d.ts +1 -0
  161. package/dist/core/__tests__/broker-preflight.test.js +85 -0
  162. package/dist/core/__tests__/broker-sdk-wiring.test.js +15 -0
  163. package/dist/core/__tests__/canvas.test.js +37 -1
  164. package/dist/core/__tests__/chat-view-reconnect.test.d.ts +1 -0
  165. package/dist/core/__tests__/chat-view-reconnect.test.js +70 -0
  166. package/dist/core/__tests__/child-death-wake.test.js +11 -2
  167. package/dist/core/__tests__/close.test.js +39 -1
  168. package/dist/core/__tests__/connection-reconnect-resume.test.d.ts +1 -0
  169. package/dist/core/__tests__/connection-reconnect-resume.test.js +37 -0
  170. package/dist/core/__tests__/context-intro.test.js +48 -14
  171. package/dist/core/__tests__/daemon-boot.test.js +182 -9
  172. package/dist/core/__tests__/editor-label.test.d.ts +1 -0
  173. package/dist/core/__tests__/editor-label.test.js +26 -0
  174. package/dist/core/__tests__/fault-marker.test.d.ts +1 -0
  175. package/dist/core/__tests__/fault-marker.test.js +112 -0
  176. package/dist/core/__tests__/fault-retry-rewind.test.d.ts +1 -0
  177. package/dist/core/__tests__/fault-retry-rewind.test.js +123 -0
  178. package/dist/core/__tests__/fixtures/fake-engine.d.ts +30 -4
  179. package/dist/core/__tests__/fixtures/fake-engine.js +156 -17
  180. package/dist/core/__tests__/fixtures/fake-pi-host.js +10 -1
  181. package/dist/core/__tests__/full/human-new-window-regression.test.js +1 -1
  182. package/dist/core/__tests__/helpers/harness.d.ts +1 -1
  183. package/dist/core/__tests__/helpers/harness.js +2 -2
  184. package/dist/core/__tests__/human-stranded-deliver.test.js +38 -1
  185. package/dist/core/__tests__/human-surface-target.test.js +1 -1
  186. package/dist/core/__tests__/kickoff.test.js +18 -19
  187. package/dist/core/__tests__/memory-resolver.test.js +12 -1
  188. package/dist/core/__tests__/migration.test.js +49 -14
  189. package/dist/core/__tests__/model-ladders.test.d.ts +1 -0
  190. package/dist/core/__tests__/model-ladders.test.js +160 -0
  191. package/dist/core/__tests__/node-env.test.d.ts +1 -0
  192. package/dist/core/__tests__/node-env.test.js +26 -0
  193. package/dist/core/__tests__/push-final-guard.test.js +7 -1
  194. package/dist/core/__tests__/relaunch-root.test.js +9 -9
  195. package/dist/core/__tests__/revive.test.js +36 -6
  196. package/dist/core/__tests__/spawn-root.test.js +24 -1
  197. package/dist/core/__tests__/stop-guard.test.js +10 -0
  198. package/dist/core/__tests__/stranded-relaunch.test.d.ts +1 -0
  199. package/dist/core/__tests__/stranded-relaunch.test.js +72 -0
  200. package/dist/core/__tests__/tmux-surface.test.js +5 -1
  201. package/dist/core/__tests__/unknown-path.test.js +19 -19
  202. package/dist/core/__tests__/yield-ensures-daemon.test.d.ts +1 -0
  203. package/dist/core/__tests__/yield-ensures-daemon.test.js +54 -0
  204. package/dist/core/artifact.d.ts +2 -33
  205. package/dist/core/artifact.js +27 -87
  206. package/dist/core/bootstrap.js +2 -0
  207. package/dist/core/canvas/browse/__tests__/model.test.js +35 -1
  208. package/dist/core/canvas/browse/app.js +87 -17
  209. package/dist/core/canvas/browse/model.d.ts +10 -1
  210. package/dist/core/canvas/browse/model.js +37 -1
  211. package/dist/core/canvas/browse/render.js +35 -33
  212. package/dist/core/canvas/canvas.d.ts +7 -0
  213. package/dist/core/canvas/canvas.js +54 -5
  214. package/dist/core/canvas/db.js +35 -8
  215. package/dist/core/canvas/labels.d.ts +6 -8
  216. package/dist/core/canvas/labels.js +8 -11
  217. package/dist/core/canvas/nav-model.d.ts +5 -4
  218. package/dist/core/canvas/nav-model.js +31 -17
  219. package/dist/core/canvas/paths.d.ts +18 -1
  220. package/dist/core/canvas/paths.js +31 -2
  221. package/dist/core/canvas/render.d.ts +25 -0
  222. package/dist/core/canvas/render.js +96 -13
  223. package/dist/core/canvas/status-glyph.d.ts +35 -0
  224. package/dist/core/canvas/status-glyph.js +104 -0
  225. package/dist/core/canvas/types.d.ts +2 -2
  226. package/dist/core/config.js +114 -16
  227. package/dist/core/fault-classifier.d.ts +41 -0
  228. package/dist/core/fault-classifier.js +110 -0
  229. package/dist/core/feed/inbox.d.ts +15 -0
  230. package/dist/core/feed/inbox.js +40 -14
  231. package/dist/core/frontmatter.d.ts +1 -11
  232. package/dist/core/frontmatter.js +1 -55
  233. package/dist/core/hearth/config.d.ts +2 -0
  234. package/dist/core/hearth/config.js +75 -0
  235. package/dist/core/hearth/guest-env.d.ts +2 -0
  236. package/dist/core/hearth/guest-env.js +11 -0
  237. package/dist/core/hearth/index.d.ts +5 -0
  238. package/dist/core/hearth/index.js +5 -0
  239. package/dist/core/hearth/provider.d.ts +21 -0
  240. package/dist/core/hearth/provider.js +10 -0
  241. package/dist/core/hearth/providers/blaxel-bootstrap.d.ts +10 -0
  242. package/dist/core/hearth/providers/blaxel-bootstrap.js +39 -0
  243. package/dist/core/hearth/providers/blaxel-home.d.ts +11 -0
  244. package/dist/core/hearth/providers/blaxel-home.js +126 -0
  245. package/dist/core/hearth/providers/blaxel.d.ts +26 -0
  246. package/dist/core/hearth/providers/blaxel.js +208 -0
  247. package/dist/core/hearth/providers/types.d.ts +72 -0
  248. package/dist/core/hearth/providers/types.js +1 -0
  249. package/dist/core/hearth/registry.d.ts +15 -0
  250. package/dist/core/hearth/registry.js +179 -0
  251. package/dist/core/hearth/types.d.ts +121 -0
  252. package/dist/core/hearth/types.js +1 -0
  253. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.d.ts +1 -0
  254. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.js +79 -0
  255. package/dist/core/{skill-sync → host-exports}/builtins.d.ts +21 -13
  256. package/dist/core/host-exports/builtins.js +68 -0
  257. package/dist/core/{skill-sync → host-exports}/export.d.ts +9 -6
  258. package/dist/core/{skill-sync → host-exports}/export.js +37 -14
  259. package/dist/core/io.js +1 -1
  260. package/dist/core/log.d.ts +9 -0
  261. package/dist/core/log.js +113 -0
  262. package/dist/core/memory-resolver.d.ts +8 -8
  263. package/dist/core/memory-resolver.js +15 -28
  264. package/dist/core/personas/index.d.ts +4 -4
  265. package/dist/core/personas/index.js +2 -2
  266. package/dist/core/personas/loader.d.ts +51 -0
  267. package/dist/core/personas/loader.js +54 -50
  268. package/dist/core/personas/resolve.d.ts +43 -27
  269. package/dist/core/personas/resolve.js +336 -94
  270. package/dist/core/runtime/auth-reload.d.ts +7 -0
  271. package/dist/core/runtime/auth-reload.js +69 -0
  272. package/dist/core/runtime/bearings.d.ts +35 -15
  273. package/dist/core/runtime/bearings.js +305 -44
  274. package/dist/core/runtime/branded-host.d.ts +4 -2
  275. package/dist/core/runtime/branded-host.js +66 -4
  276. package/dist/core/runtime/broker-protocol.d.ts +38 -3
  277. package/dist/core/runtime/broker.js +343 -18
  278. package/dist/core/runtime/close.js +37 -29
  279. package/dist/core/runtime/connectivity.d.ts +12 -0
  280. package/dist/core/runtime/connectivity.js +73 -0
  281. package/dist/core/runtime/fault-recovery-nudge.d.ts +2 -0
  282. package/dist/core/runtime/fault-recovery-nudge.js +2 -0
  283. package/dist/core/runtime/fault-recovery.d.ts +23 -0
  284. package/dist/core/runtime/fault-recovery.js +74 -0
  285. package/dist/core/runtime/fault.d.ts +25 -0
  286. package/dist/core/runtime/fault.js +176 -0
  287. package/dist/core/runtime/front-door.d.ts +1 -1
  288. package/dist/core/runtime/front-door.js +2 -2
  289. package/dist/core/runtime/host.d.ts +10 -0
  290. package/dist/core/runtime/host.js +71 -9
  291. package/dist/core/runtime/kickoff.js +31 -45
  292. package/dist/core/runtime/launch.d.ts +32 -18
  293. package/dist/core/runtime/launch.js +148 -43
  294. package/dist/core/runtime/model-swap.d.ts +18 -0
  295. package/dist/core/runtime/model-swap.js +95 -0
  296. package/dist/core/runtime/naming.d.ts +7 -0
  297. package/dist/core/runtime/naming.js +61 -9
  298. package/dist/core/runtime/nodes.js +6 -1
  299. package/dist/core/runtime/persona.js +16 -17
  300. package/dist/core/runtime/placement.d.ts +36 -31
  301. package/dist/core/runtime/placement.js +149 -64
  302. package/dist/core/runtime/promote.d.ts +2 -0
  303. package/dist/core/runtime/promote.js +23 -3
  304. package/dist/core/runtime/recycle.js +6 -4
  305. package/dist/core/runtime/reset.d.ts +5 -2
  306. package/dist/core/runtime/reset.js +2 -2
  307. package/dist/core/runtime/revive.d.ts +4 -0
  308. package/dist/core/runtime/revive.js +27 -9
  309. package/dist/core/runtime/roadmap.d.ts +1 -1
  310. package/dist/core/runtime/roadmap.js +1 -1
  311. package/dist/core/runtime/spawn.d.ts +2 -2
  312. package/dist/core/runtime/spawn.js +30 -6
  313. package/dist/core/runtime/stop-guard.js +6 -1
  314. package/dist/core/runtime/tmux-chrome.d.ts +1 -1
  315. package/dist/core/runtime/tmux-chrome.js +1 -1
  316. package/dist/core/runtime/tmux.d.ts +29 -6
  317. package/dist/core/runtime/tmux.js +134 -80
  318. package/dist/core/runtime/view-socket-client.d.ts +46 -0
  319. package/dist/core/runtime/view-socket-client.js +203 -0
  320. package/dist/core/substrate/index.d.ts +1 -1
  321. package/dist/core/substrate/index.js +1 -1
  322. package/dist/core/substrate/on-read.js +16 -4
  323. package/dist/core/substrate/render.d.ts +10 -13
  324. package/dist/core/substrate/render.js +56 -51
  325. package/dist/core/tui/host.js +83 -18
  326. package/dist/core/view/__tests__/transport-cache.test.d.ts +1 -0
  327. package/dist/core/view/__tests__/transport-cache.test.js +62 -0
  328. package/dist/core/view/contract.d.ts +20 -0
  329. package/dist/core/view/stream-local.d.ts +3 -0
  330. package/dist/core/view/stream-local.js +184 -0
  331. package/dist/core/view/transport-cache.d.ts +8 -0
  332. package/dist/core/view/transport-cache.js +38 -0
  333. package/dist/core/view/transport-local.js +4 -0
  334. package/dist/core/view/transport.d.ts +7 -1
  335. package/dist/daemon/crtrd.d.ts +3 -19
  336. package/dist/daemon/crtrd.js +139 -178
  337. package/dist/daemon/manage.d.ts +18 -1
  338. package/dist/daemon/manage.js +54 -9
  339. package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.d.ts +1 -0
  340. package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.js +289 -0
  341. package/dist/hearth/wake-proxy/__tests__/config-timeout.test.d.ts +1 -0
  342. package/dist/hearth/wake-proxy/__tests__/config-timeout.test.js +34 -0
  343. package/dist/hearth/wake-proxy/__tests__/guest-source.test.d.ts +1 -0
  344. package/dist/hearth/wake-proxy/__tests__/guest-source.test.js +203 -0
  345. package/dist/hearth/wake-proxy/__tests__/hardening.test.d.ts +1 -0
  346. package/dist/hearth/wake-proxy/__tests__/hardening.test.js +59 -0
  347. package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.d.ts +1 -0
  348. package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.js +372 -0
  349. package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.d.ts +1 -0
  350. package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.js +258 -0
  351. package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.d.ts +1 -0
  352. package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.js +437 -0
  353. package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.d.ts +1 -0
  354. package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.js +455 -0
  355. package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.d.ts +1 -0
  356. package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.js +15 -0
  357. package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.d.ts +1 -0
  358. package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.js +141 -0
  359. package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.d.ts +1 -0
  360. package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.js +143 -0
  361. package/dist/hearth/wake-proxy/anthropic-oauth.d.ts +58 -0
  362. package/dist/hearth/wake-proxy/anthropic-oauth.js +189 -0
  363. package/dist/hearth/wake-proxy/auth.d.ts +22 -0
  364. package/dist/hearth/wake-proxy/auth.js +128 -0
  365. package/dist/hearth/wake-proxy/config.d.ts +2 -0
  366. package/dist/hearth/wake-proxy/config.js +151 -0
  367. package/dist/hearth/wake-proxy/guest-source.d.ts +14 -0
  368. package/dist/hearth/wake-proxy/guest-source.js +130 -0
  369. package/dist/hearth/wake-proxy/hearth-status.d.ts +44 -0
  370. package/dist/hearth/wake-proxy/hearth-status.js +267 -0
  371. package/dist/hearth/wake-proxy/home.d.ts +67 -0
  372. package/dist/hearth/wake-proxy/home.js +297 -0
  373. package/dist/hearth/wake-proxy/main.d.ts +1 -0
  374. package/dist/hearth/wake-proxy/main.js +134 -0
  375. package/dist/hearth/wake-proxy/model-auth.d.ts +13 -0
  376. package/dist/hearth/wake-proxy/model-auth.js +345 -0
  377. package/dist/hearth/wake-proxy/proxy.d.ts +35 -0
  378. package/dist/hearth/wake-proxy/proxy.js +716 -0
  379. package/dist/hearth/wake-proxy/public-source-gate.d.ts +9 -0
  380. package/dist/hearth/wake-proxy/public-source-gate.js +409 -0
  381. package/dist/hearth/wake-proxy/redact.d.ts +1 -0
  382. package/dist/hearth/wake-proxy/redact.js +17 -0
  383. package/dist/hearth/wake-proxy/server.d.ts +11 -0
  384. package/dist/hearth/wake-proxy/server.js +142 -0
  385. package/dist/hearth/wake-proxy/state.d.ts +18 -0
  386. package/dist/hearth/wake-proxy/state.js +342 -0
  387. package/dist/hearth/wake-proxy/types.d.ts +76 -0
  388. package/dist/hearth/wake-proxy/types.js +1 -0
  389. package/dist/index.js +3 -2
  390. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +6 -5
  391. package/dist/pi-extensions/canvas-context-intro.js +7 -7
  392. package/dist/pi-extensions/canvas-doc-substrate.js +19 -22
  393. package/dist/pi-extensions/canvas-nav.js +15 -8
  394. package/dist/pi-extensions/canvas-stophook.d.ts +1 -1
  395. package/dist/pi-extensions/canvas-stophook.js +78 -11
  396. package/dist/types.d.ts +39 -20
  397. package/dist/types.js +38 -13
  398. package/dist/web/runtime.js +141 -42
  399. package/dist/web/transport-http.js +7 -5
  400. package/dist/web/transport-stream.d.ts +3 -0
  401. package/dist/web/transport-stream.js +204 -0
  402. package/dist/web-client/assets/fragment-mono-latin-400-normal-BYwT3kSJ.woff +0 -0
  403. package/dist/web-client/assets/fragment-mono-latin-400-normal-yxdJ5AmL.woff2 +0 -0
  404. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-BbKYyvR9.woff2 +0 -0
  405. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-CT4YFKeK.woff +0 -0
  406. package/dist/web-client/assets/fraunces-latin-ext-wght-normal-Ca2vKHc0.woff2 +0 -0
  407. package/dist/web-client/assets/fraunces-latin-wght-normal-ukD16Tqj.woff2 +0 -0
  408. package/dist/web-client/assets/fraunces-vietnamese-wght-normal-CnvboYUG.woff2 +0 -0
  409. package/dist/web-client/assets/index-BZUxTkv5.css +2 -0
  410. package/dist/web-client/assets/index-D36PNBj4.js +80 -0
  411. package/dist/web-client/assets/instrument-sans-latin-ext-wght-normal-B5bTHO_g.woff2 +0 -0
  412. package/dist/web-client/assets/instrument-sans-latin-wght-normal-BbzFLZTg.woff2 +0 -0
  413. package/dist/web-client/assets/martian-mono-cyrillic-wght-normal-B84CD5C_.woff2 +0 -0
  414. package/dist/web-client/assets/martian-mono-latin-ext-wght-normal-DlL6xMw5.woff2 +0 -0
  415. package/dist/web-client/assets/martian-mono-latin-wght-normal-5W32yIyr.woff2 +0 -0
  416. package/dist/web-client/index.html +3 -2
  417. package/package.json +32 -10
  418. package/dist/core/__tests__/error-stall-recycle.test.js +0 -141
  419. package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.js +0 -57
  420. package/dist/core/skill-sync/builtins.js +0 -112
  421. package/dist/core/skill-sync/claude-plugins.d.ts +0 -23
  422. package/dist/core/skill-sync/claude-plugins.js +0 -71
  423. package/dist/core/skill-sync/engine.d.ts +0 -42
  424. package/dist/core/skill-sync/engine.js +0 -633
  425. package/dist/core/skill-sync/manifest.d.ts +0 -64
  426. package/dist/core/skill-sync/manifest.js +0 -181
  427. package/dist/core/skill-sync/profile.d.ts +0 -76
  428. package/dist/core/skill-sync/profile.js +0 -173
  429. package/dist/core/skill-sync/snapshot.d.ts +0 -57
  430. package/dist/core/skill-sync/snapshot.js +0 -120
  431. package/dist/pi-extensions/canvas-commands.d.ts +0 -37
  432. package/dist/pi-extensions/canvas-commands.js +0 -113
  433. package/dist/pi-extensions/canvas-resume.d.ts +0 -21
  434. package/dist/pi-extensions/canvas-resume.js +0 -83
  435. package/dist/pi-extensions/canvas-view.d.ts +0 -21
  436. package/dist/pi-extensions/canvas-view.js +0 -76
  437. package/dist/web-client/assets/index-BUvQb4hR.css +0 -2
  438. package/dist/web-client/assets/index-ClLQXYAE.js +0 -10
  439. /package/dist/{core/__tests__/error-stall-recycle.test.d.ts → clients/attach/__tests__/autocomplete-and-bash-mode.test.d.ts} +0 -0
  440. /package/dist/{core/skill-sync/__tests__/dry-run-wrote-count.test.d.ts → clients/attach/__tests__/bash-bang-routing.test.d.ts} +0 -0
@@ -0,0 +1,65 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { defineLeaf } from '../../core/command.js';
3
+ import { startWebServer } from '../../clients/web/server.js';
4
+ const DEFAULT_PORT = 7878;
5
+ const PROMPT_STUDIO_PATH = '/views/prompt-review';
6
+ function openBrowser(url) {
7
+ const [cmd, args] = process.platform === 'darwin'
8
+ ? ['open', [url]]
9
+ : process.platform === 'win32'
10
+ ? ['cmd', ['/c', 'start', '', url]]
11
+ : ['xdg-open', [url]];
12
+ try {
13
+ const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
14
+ child.on('error', () => {
15
+ /* the printed URL is enough when no browser opener is available */
16
+ });
17
+ child.unref();
18
+ }
19
+ catch {
20
+ /* the printed URL is enough when no browser opener is available */
21
+ }
22
+ }
23
+ export const sysPromptStudioLeaf = defineLeaf({
24
+ name: 'promptstudio',
25
+ description: 'open Prompt Studio in the browser',
26
+ whenToUse: 'you want the Prompt Studio browser UI — a prompt review surface for toggling crouter persona/mode/lifecycle configs, inspecting the assembled system prompt, comparing configurations, and exporting review comments with source-file pointers.',
27
+ help: {
28
+ name: 'sys promptstudio',
29
+ summary: 'start the crouter web server and open Prompt Studio directly',
30
+ params: [
31
+ { kind: 'flag', name: 'port', type: 'int', required: false, default: DEFAULT_PORT, constraint: `TCP port to bind on 127.0.0.1. Default ${DEFAULT_PORT}.` },
32
+ { kind: 'flag', name: 'dev', type: 'bool', required: false, default: false, constraint: 'Serve the web shell through Vite middleware/HMR while iterating on Prompt Studio or the web client.' },
33
+ ],
34
+ output: [
35
+ { name: 'url', type: 'string', required: true, constraint: 'Prompt Studio URL opened in the browser.' },
36
+ ],
37
+ outputKind: 'object',
38
+ effects: [
39
+ 'Binds the same long-running local web server as `crtr web serve`, opens `/views/prompt-review` in the default browser, and keeps serving until ctrl+c/SIGTERM.',
40
+ 'The Prompt Studio view uses the existing source bridge to call `crtr sys prompt-review` for composed prompts and review export.',
41
+ ],
42
+ },
43
+ run: async (input) => {
44
+ const port = input['port'] ?? DEFAULT_PORT;
45
+ const dev = input['dev'] ?? false;
46
+ const server = await startWebServer({ port, dev });
47
+ const url = `${server.url}${PROMPT_STUDIO_PATH}`;
48
+ process.stdout.write(`Prompt Studio serving at ${url}${dev ? ' (--dev: Vite middleware)' : ''}\n` +
49
+ ` bridge: POST ${server.url}/__crtr/source\n` +
50
+ ` events (SSE): GET ${server.url}/__crtr/events\n` +
51
+ ` shell assets: ${dev ? 'Vite middleware (HMR)' : server.clientDir}\n` +
52
+ ` (ctrl+c to stop)\n`);
53
+ openBrowser(url);
54
+ await new Promise((resolveShutdown) => {
55
+ const shutdown = () => {
56
+ void server.close().then(() => resolveShutdown());
57
+ };
58
+ process.once('SIGINT', shutdown);
59
+ process.once('SIGTERM', shutdown);
60
+ process.once('SIGHUP', shutdown);
61
+ });
62
+ return { url };
63
+ },
64
+ render: () => '',
65
+ });
@@ -0,0 +1,2 @@
1
+ import type { LeafDef } from '../../core/command.js';
2
+ export declare const sysSettingsLeaf: LeafDef;
@@ -0,0 +1,16 @@
1
+ import { defineLeaf } from '../../core/command.js';
2
+ import { viewRunLeaf } from '../view-run.js';
3
+ export const sysSettingsLeaf = defineLeaf({
4
+ name: 'settings',
5
+ description: 'open the model ladders settings view',
6
+ whenToUse: 'you want to open the built-in model ladders settings view in tmux — the same interactive surface as `crtr view run settings`. Use this from `crtr sys` when you want the settings opener beside the rest of system config.',
7
+ help: {
8
+ name: 'sys settings',
9
+ summary: 'open the built-in model ladders settings view',
10
+ inputNote: 'No input parameters.',
11
+ output: [],
12
+ outputKind: 'object',
13
+ effects: ['Hosts the built-in `settings` view via `crtr view run settings`, preserving the same tmux and non-TTY behavior.'],
14
+ },
15
+ run: async () => viewRunLeaf.run({ name: 'settings' }),
16
+ });
@@ -1,187 +1,339 @@
1
- // `crtr sys sync` — the SOLE entry point for bidirectional crtr Claude skill
2
- // sync (R-U1). Loads the layered manifest (P2), reconciles each enrolled pair
3
- // once via the direction-agnostic engine (P6), summarizes the outcome, and
4
- // sets the exit code: non-zero IFF any pair conflicted (R-E7). A malformed
5
- // manifest (R-U7) or an unknown `--pair` id (OD-3) aborts hard before any
6
- // reconcile runs no lenient fallback, no partial write.
1
+ // `crtr sys sync` — explicit migration from legacy host Agent Skill bundles into
2
+ // crouter memory documents. It is NOT a bidirectional SKILL.md sync surface: the
3
+ // crouter side after a sync is a plain memory/<name>.md document.
4
+ import { existsSync } from 'node:fs';
5
+ import { homedir } from 'node:os';
6
+ import { basename, dirname, join, relative, resolve, sep } from 'node:path';
7
7
  import { defineLeaf } from '../../core/command.js';
8
8
  import { usage } from '../../core/errors.js';
9
- import { emit, isJsonOutput } from '../../core/io.js';
10
- import { ExitCode } from '../../types.js';
11
- import { readLayeredManifest } from '../../core/skill-sync/manifest.js';
12
- import { resolveProfile } from '../../core/skill-sync/profile.js';
13
- import { reconcilePair } from '../../core/skill-sync/engine.js';
14
- /** Render the per-pair status table + summary line as agent-ready markdown.
15
- * Shown for both the clean and the conflict exit so the caller always sees the
16
- * full partition. */
17
- function renderSync(results, summary, dryRun) {
18
- const parts = [];
19
- parts.push(dryRun
20
- ? 'crtr ↔ Claude skill sync — DRY RUN (nothing written):'
21
- : 'crtr ↔ Claude skill sync:');
22
- if (results.length === 0) {
23
- parts.push('No enrolled pairs.');
9
+ import { parseFrontmatterGeneric } from '../../core/frontmatter.js';
10
+ import { pathExists, readJsonIfExists, readText, walkFiles, writeJson, writeText } from '../../core/fs-utils.js';
11
+ import { findProjectScopeRoot, scopeMemoryDir } from '../../core/scope.js';
12
+ import { memoryFilePath, resolveWriteTarget, serializeMemoryDoc } from '../memory/shared.js';
13
+ const LEGACY_BOOT_SKILL_MARKER_PREFIX = '<!-- crtr-boot-skill v';
14
+ const HOST_SKILL_FILE = 'SKILL.md';
15
+ const IGNORE_FILE = 'skill-import-ignore.json';
16
+ function projectDir() {
17
+ const root = findProjectScopeRoot();
18
+ return root ? dirname(root) : null;
19
+ }
20
+ function ignoreFilePath() {
21
+ return join(homedir(), '.crouter', IGNORE_FILE);
22
+ }
23
+ function defaultSourceRoots() {
24
+ const roots = [
25
+ { label: 'claude-user', root: join(homedir(), '.claude', 'skills'), defaultScope: 'user' },
26
+ { label: 'pi-user', root: join(homedir(), '.pi', 'agent', 'skills'), defaultScope: 'user' },
27
+ ];
28
+ const proj = projectDir();
29
+ if (proj) {
30
+ roots.push({ label: 'claude-project', root: join(proj, '.claude', 'skills'), defaultScope: 'project' });
31
+ roots.push({ label: 'pi-project', root: join(proj, '.pi', 'agent', 'skills'), defaultScope: 'project' });
32
+ }
33
+ return roots;
34
+ }
35
+ function sourceRootsFromArg(sourceArg, scopeArg) {
36
+ if (sourceArg === undefined)
37
+ return defaultSourceRoots();
38
+ const root = resolve(sourceArg);
39
+ if (scopeArg !== undefined && scopeArg !== 'user' && scopeArg !== 'project') {
40
+ resolveWriteTarget(scopeArg);
41
+ }
42
+ return [{ label: 'source', root, defaultScope: scopeArg ?? 'user' }];
43
+ }
44
+ function collectCandidates(rootDef) {
45
+ const root = rootDef.root;
46
+ if (!existsSync(root))
47
+ return [];
48
+ const statRoot = resolve(root);
49
+ const candidates = [];
50
+ if (basename(statRoot) === HOST_SKILL_FILE) {
51
+ candidates.push({ path: statRoot, root: dirname(statRoot), source: rootDef.label, defaultScope: rootDef.defaultScope });
52
+ return candidates;
53
+ }
54
+ const directSkill = join(statRoot, HOST_SKILL_FILE);
55
+ if (existsSync(directSkill)) {
56
+ candidates.push({ path: directSkill, root: dirname(statRoot), source: rootDef.label, defaultScope: rootDef.defaultScope });
57
+ return candidates;
58
+ }
59
+ for (const file of walkFiles(statRoot, (name) => name === HOST_SKILL_FILE)) {
60
+ candidates.push({ path: file, root: statRoot, source: rootDef.label, defaultScope: rootDef.defaultScope });
61
+ }
62
+ return candidates;
63
+ }
64
+ function scalarString(value) {
65
+ if (typeof value === 'string')
66
+ return value;
67
+ if (typeof value === 'number' || typeof value === 'boolean')
68
+ return String(value);
69
+ return undefined;
70
+ }
71
+ function cleanClause(s) {
72
+ let out = s.replace(/\s+/g, ' ').trim();
73
+ out = out.replace(/^[,;:.\s]+/, '').replace(/[,;:.\s]+$/, '');
74
+ if (/^[A-Z][a-z]/.test(out))
75
+ out = out.charAt(0).toLowerCase() + out.slice(1);
76
+ return out;
77
+ }
78
+ const USE_WHEN_RE = /\b(?:use\s+this\s+skill\s+when|use\s+this\s+when|this\s+skill\s+should\s+be\s+used\s+when|used\s+when|use\s+when)\b\s*/i;
79
+ function routeFromDescription(description, kind) {
80
+ const desc = description.replace(/\s+/g, ' ').trim();
81
+ if (desc === '')
82
+ return `When this migrated ${kind} applies, this ${kind} should be read.`;
83
+ const m = desc.match(USE_WHEN_RE);
84
+ if (m && m.index !== undefined) {
85
+ const gist = cleanClause(desc.slice(0, m.index));
86
+ const situation = cleanClause(desc.slice(m.index + m[0].length));
87
+ if (situation !== '') {
88
+ let out = `When ${situation}, this ${kind} should be read`;
89
+ if (gist !== '')
90
+ out += ` because ${gist}`;
91
+ return `${out}.`;
92
+ }
93
+ }
94
+ return `When ${cleanClause(desc)}, this ${kind} should be read.`;
95
+ }
96
+ function normalizedName(raw) {
97
+ const segments = raw
98
+ .replace(/\\/g, '/')
99
+ .replace(/\.md$/i, '')
100
+ .split('/')
101
+ .map((s) => s.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, ''))
102
+ .filter(Boolean);
103
+ return segments.join('/');
104
+ }
105
+ function nameForCandidate(candidate, fm) {
106
+ const fmName = scalarString(fm?.name);
107
+ if (fmName && fmName.trim() !== '')
108
+ return normalizedName(fmName);
109
+ const relDir = relative(candidate.root, dirname(candidate.path)).split(sep).join('/');
110
+ return normalizedName(relDir || basename(dirname(candidate.path)));
111
+ }
112
+ function targetMemoryDir(scope) {
113
+ const dir = scopeMemoryDir(scope);
114
+ if (!dir)
115
+ return resolveWriteTarget(scope).memoryDir;
116
+ return dir;
117
+ }
118
+ function readIgnoreState() {
119
+ let doc;
120
+ try {
121
+ doc = readJsonIfExists(ignoreFilePath());
24
122
  }
25
- else {
26
- const rows = results
27
- .map((r) => `| ${r.id} | ${r.status} | ${r.wrote} |`)
28
- .join('\n');
29
- parts.push(`| pair | status | wrote |\n| --- | --- | --- |\n${rows}`);
123
+ catch (e) {
124
+ throw usage(`invalid JSON in ${ignoreFilePath()}: ${e instanceof Error ? e.message : String(e)}`);
30
125
  }
31
- parts.push(`synced: ${summary.synced.length} · conflicts: ${summary.conflicts.length} · ` +
32
- `noop: ${summary.noop.length} · files written: ${summary.wrote}`);
33
- if (summary.conflicts.length > 0) {
34
- parts.push(`Conflicts (NOT written): ${summary.conflicts.join(', ')}. ` +
35
- 'Each conflicting pair has a git-style report under ' +
36
- '`~/.crouter/skill-sync/conflicts/<id>.md` — resolve the endpoints by hand, then re-run.');
126
+ if (doc === null)
127
+ return { version: 1, ignored: [] };
128
+ if (typeof doc !== 'object' ||
129
+ doc === null ||
130
+ doc.version !== 1 ||
131
+ !Array.isArray(doc.ignored)) {
132
+ throw usage(`malformed ${ignoreFilePath()}: expected {"version":1,"ignored":[...]}`);
37
133
  }
38
- return parts.join('\n\n');
39
- }
40
- /** Drift label for the read-only `--status` view, derived from a dry-run
41
- * reconcile: `noop` = already in agreement, `synced` = a clean merge the engine
42
- * WOULD write, `conflict` = diverged and blocked. These are the only three
43
- * statuses `reconcilePair` returns. */
44
- const DRIFT = {
45
- noop: 'in-sync',
46
- synced: 'would-write',
47
- conflict: 'conflict',
48
- };
49
- /** Render one endpoint as `scope/name` (or `<scope>:<plugin>/<name>` for a
50
- * plugin-scoped side) so a reader sees where each half of the pair lives. */
51
- function endpointLabel(ep) {
52
- return ep.scope === 'plugin' || ep.scope === 'claude-plugin'
53
- ? `${ep.scope}:${ep.plugin}/${ep.name}`
54
- : `${ep.scope}/${ep.name}`;
55
- }
56
- /** Render the read-only `--status` inventory: every selected pair with its
57
- * crtr↔Claude endpoints and drift state, computed via a dry-run reconcile so
58
- * nothing is written. `results` is keyed back to `pairs` by id. */
59
- function renderStatus(pairs, results) {
60
- const parts = [];
61
- parts.push('crtr ↔ Claude skill-sync pairs (read-only; nothing written):');
62
- if (pairs.length === 0) {
63
- parts.push('No enrolled pairs.');
64
- return parts.join('\n\n');
134
+ const ignored = [];
135
+ for (const entry of doc.ignored) {
136
+ if (typeof entry !== 'object' || entry === null)
137
+ continue;
138
+ const e = entry;
139
+ if (typeof e.source === 'string' &&
140
+ typeof e.name === 'string' &&
141
+ (e.scope === 'user' || e.scope === 'project') &&
142
+ typeof e.ignoredAt === 'string') {
143
+ ignored.push({ source: resolve(e.source), name: e.name, scope: e.scope, ignoredAt: e.ignoredAt });
144
+ }
65
145
  }
66
- const byId = new Map(results.map((r) => [r.id, r]));
67
- const rows = pairs
68
- .map((p) => {
69
- const drift = DRIFT[byId.get(p.id).status];
70
- return `| ${p.id} | ${endpointLabel(p.crtr)} | ${endpointLabel(p.claude)} | ${drift} |`;
71
- })
72
- .join('\n');
73
- parts.push(`| pair | crtr | claude | drift |\n| --- | --- | --- | --- |\n${rows}`);
74
- const inSync = results.filter((r) => r.status === 'noop').length;
75
- const wouldWrite = results.filter((r) => r.status === 'synced').length;
76
- const conflict = results.filter((r) => r.status === 'conflict').length;
77
- parts.push(`in-sync: ${inSync} · would-write: ${wouldWrite} · conflict: ${conflict}`);
78
- return parts.join('\n\n');
146
+ return { version: 1, ignored };
147
+ }
148
+ function writeIgnoreState(state) {
149
+ writeJson(ignoreFilePath(), state);
150
+ }
151
+ function isIgnored(prepared, state) {
152
+ const source = resolve(prepared.candidate.path);
153
+ return state.ignored.some((entry) => entry.source === source ||
154
+ (entry.scope === prepared.scope && entry.name === prepared.name));
155
+ }
156
+ function rememberIgnored(prepared, state) {
157
+ if (isIgnored(prepared, state))
158
+ return false;
159
+ state.ignored.push({
160
+ source: resolve(prepared.candidate.path),
161
+ name: prepared.name,
162
+ scope: prepared.scope,
163
+ ignoredAt: new Date().toISOString(),
164
+ });
165
+ state.ignored.sort((a, b) => `${a.scope}/${a.name}`.localeCompare(`${b.scope}/${b.name}`));
166
+ return true;
167
+ }
168
+ function skipResult(candidate, scope, reason) {
169
+ return { source: candidate.path, target: '', name: '', scope, status: 'skipped', reason };
170
+ }
171
+ function prepareCandidate(candidate, scopeOverride) {
172
+ const raw = readText(candidate.path);
173
+ const scope = scopeOverride ?? candidate.defaultScope;
174
+ if (raw.includes(LEGACY_BOOT_SKILL_MARKER_PREFIX)) {
175
+ return skipResult(candidate, scope, 'legacy generated crtr boot skill is pruned by host exports, not imported');
176
+ }
177
+ const parsed = parseFrontmatterGeneric(raw);
178
+ const sourceFm = parsed.data ?? {};
179
+ const kind = sourceFm.kind === 'preference' ? 'preference' : 'knowledge';
180
+ const name = nameForCandidate(candidate, sourceFm);
181
+ if (name === '')
182
+ return skipResult(candidate, scope, 'could not derive memory document name');
183
+ const memoryDir = targetMemoryDir(scope);
184
+ const target = memoryFilePath(memoryDir, name);
185
+ return { candidate, raw, parsed, sourceFm, kind, name, scope, target };
186
+ }
187
+ function convertPrepared(prepared, opts) {
188
+ if (pathExists(prepared.target) && !opts.overwrite) {
189
+ return {
190
+ source: prepared.candidate.path,
191
+ target: prepared.target,
192
+ name: prepared.name,
193
+ scope: prepared.scope,
194
+ status: 'skipped',
195
+ reason: 'target memory doc already exists; re-run with --overwrite to replace it',
196
+ };
197
+ }
198
+ const description = scalarString(prepared.sourceFm.description) ?? '';
199
+ const frontmatter = { ...prepared.sourceFm };
200
+ delete frontmatter.name;
201
+ delete frontmatter.description;
202
+ delete frontmatter.type;
203
+ delete frontmatter.keywords;
204
+ frontmatter.kind = prepared.kind;
205
+ if (typeof frontmatter['when-and-why-to-read'] !== 'string' || frontmatter['when-and-why-to-read'].trim() === '') {
206
+ frontmatter['when-and-why-to-read'] = routeFromDescription(description, prepared.kind);
207
+ }
208
+ if (typeof frontmatter['short-form'] !== 'string' || frontmatter['short-form'].trim() === '') {
209
+ frontmatter['short-form'] = description.replace(/\s+/g, ' ').trim();
210
+ }
211
+ if (typeof frontmatter['system-prompt-visibility'] !== 'string')
212
+ frontmatter['system-prompt-visibility'] = 'preview';
213
+ if (typeof frontmatter['file-read-visibility'] !== 'string')
214
+ frontmatter['file-read-visibility'] = 'none';
215
+ if (!opts.dryRun)
216
+ writeText(prepared.target, serializeMemoryDoc(frontmatter, prepared.parsed.body));
217
+ return {
218
+ source: prepared.candidate.path,
219
+ target: prepared.target,
220
+ name: prepared.name,
221
+ scope: prepared.scope,
222
+ status: opts.dryRun ? 'would-import' : 'imported',
223
+ };
224
+ }
225
+ function ignoreResult(prepared, reason, dryRun) {
226
+ return {
227
+ source: prepared.candidate.path,
228
+ target: prepared.target,
229
+ name: prepared.name,
230
+ scope: prepared.scope,
231
+ status: dryRun ? 'would-ignore' : 'ignored',
232
+ reason,
233
+ };
234
+ }
235
+ function renderSummary(results, ignoreMode) {
236
+ const imported = results.filter((r) => r.status === 'imported').length;
237
+ const wouldImport = results.filter((r) => r.status === 'would-import').length;
238
+ const skipped = results.filter((r) => r.status === 'skipped').length;
239
+ const ignored = results.filter((r) => r.status === 'ignored').length;
240
+ const wouldIgnore = results.filter((r) => r.status === 'would-ignore').length;
241
+ let header;
242
+ if (ignoreMode)
243
+ header = `crtr sys sync — ${wouldIgnore > 0 ? `${wouldIgnore} would ignore` : `${ignored} ignored`}, ${skipped} skipped`;
244
+ else if (wouldImport > 0)
245
+ header = `crtr sys sync — dry run: ${wouldImport} would import, ${skipped} skipped, ${ignored} ignored`;
246
+ else
247
+ header = `crtr sys sync — ${imported} imported, ${skipped} skipped, ${ignored} ignored`;
248
+ const rows = results.map((r) => {
249
+ const target = r.target || '—';
250
+ const reason = r.reason ? ` (${r.reason})` : '';
251
+ return `| ${r.status} | ${r.scope}/${r.name || '—'} | ${target} | ${r.source}${reason} |`;
252
+ });
253
+ const body = rows.length === 0
254
+ ? ['No sync candidates.']
255
+ : ['| status | memory doc | target | source |', '| --- | --- | --- | --- |', ...rows];
256
+ return [header, '', ...body].join('\n');
79
257
  }
80
258
  export const sysSyncLeaf = defineLeaf({
81
259
  name: 'sync',
82
- description: 'reconcile crtr Claude skill pairs (bidirectional)',
83
- whenToUse: 'you want enrolled crtr skills and their Claude SKILL.md counterparts brought back into agreement. Performs the single, direction-agnostic 3-way reconcile over every pair in the skill-sync manifest (or just one via --pair), writing both sides + the merge base when clean and a conflict report when not.',
260
+ description: 'convert legacy SKILL.md bundles into crouter memory docs',
261
+ whenToUse: 'migrating host-native Agent Skill bundles into the crouter memory-doc model. This is a one-way import from SKILL.md to memory/<name>.md; it never exports memory docs back to SKILL.md and never treats SKILL.md as an active crouter guidance surface.',
84
262
  help: {
85
263
  name: 'sys sync',
86
- summary: 'reconcile enrolled crtr Claude skill pairs (bidirectional 3-way merge)',
264
+ summary: 'one-way import: SKILL.md bundles crouter memory docs',
87
265
  params: [
88
- {
89
- kind: 'flag',
90
- name: 'pair',
91
- type: 'string',
92
- required: false,
93
- constraint: 'Reconcile only the pair with this id. Unknown id hard error naming it + the valid ids, nothing written. Default: every enrolled pair.',
94
- },
95
- {
96
- kind: 'flag',
97
- name: 'dry-run',
98
- type: 'bool',
99
- required: false,
100
- constraint: 'Compute every merge and report the would-be status, but write nothing — no endpoint, snapshot, or conflict report touched.',
101
- },
102
- {
103
- kind: 'flag',
104
- name: 'status',
105
- type: 'bool',
106
- required: false,
107
- constraint: 'Read-only inventory mode: instead of reconciling, list every enrolled pair (honoring --pair) with its crtr↔Claude endpoints and drift state (in-sync / would-write / conflict), computed via a dry-run reconcile. Writes nothing and never exits non-zero on drift; a malformed manifest or unknown --pair id still aborts hard.',
108
- },
266
+ { kind: 'flag', name: 'source', type: 'string', required: false, constraint: 'A SKILL.md file, a single skill bundle dir containing SKILL.md, or a skills root to scan recursively. Default: user/project Claude and pi skill roots that exist.' },
267
+ { kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'Target memory scope. Default: user for user host roots and project for project host roots; explicit value applies to every imported source.' },
268
+ { kind: 'flag', name: 'dry-run', type: 'bool', required: false, constraint: 'Show what would be imported without writing memory docs.' },
269
+ { kind: 'flag', name: 'overwrite', type: 'bool', required: false, constraint: 'Replace an existing target memory doc. Default: skip existing docs to avoid clobbering user-authored memory.' },
270
+ { kind: 'flag', name: 'ignore', type: 'bool', required: false, constraint: `Permanently ignore every selected source by recording it in ~/.crouter/${IGNORE_FILE}. Honors --source/--scope; with no --source, ignores every currently discovered default host skill candidate.` },
271
+ { kind: 'flag', name: 'include-ignored', type: 'bool', required: false, constraint: 'Include ignored candidates in the report without making sync read-only. Pair with --dry-run to audit the full candidate set without importing non-ignored skills.' },
109
272
  ],
110
273
  output: [
111
- { name: 'synced', type: 'string[]', required: true, constraint: 'Pair ids written through cleanly to both endpoints + the merge base.' },
112
- { name: 'conflicts', type: 'string[]', required: true, constraint: 'Pair ids that diverged and were NOT written; each has a report under ~/.crouter/skill-sync/conflicts/<id>.md. Non-empty → exit non-zero.' },
113
- { name: 'noop', type: 'string[]', required: true, constraint: 'Pair ids already in agreement nothing to do.' },
114
- { name: 'wrote', type: 'number', required: true, constraint: 'Total endpoint files created/updated across all pairs (0 under --dry-run).' },
274
+ { name: 'imported', type: 'number', required: true, constraint: 'Count of memory docs written.' },
275
+ { name: 'skipped', type: 'number', required: true, constraint: 'Count of SKILL.md files skipped.' },
276
+ { name: 'ignored', type: 'number', required: true, constraint: 'Count of selected SKILL.md files that are permanently ignored.' },
277
+ { name: 'wouldIgnore', type: 'number', required: true, constraint: 'Count of selected SKILL.md files that would be ignored under --dry-run.' },
278
+ { name: 'results', type: 'object[]', required: true, constraint: 'Each: {source,target,name,scope,status,reason?}; status may be imported, skipped, would-import, ignored, or would-ignore.' },
115
279
  ],
116
280
  outputKind: 'object',
117
281
  effects: [
118
- 'For each clean pair: writes the merged SKILL.md + assets to BOTH the crtr and Claude endpoints and updates the per-pair merge snapshot under ~/.crouter/skill-sync/snapshots/<id>/.',
119
- 'For each conflicting pair: writes NOTHING to the endpoints; writes a git-style conflict report to ~/.crouter/skill-sync/conflicts/<id>.md.',
120
- 'Exits non-zero iff any pair conflicted. A malformed manifest or an unknown --pair id aborts hard (non-zero) before any reconcile and writes nothing.',
121
- 'With --dry-run: read-only computes every merge and the would-be summary, touches no endpoint, snapshot, or report.',
122
- 'With --status: read-only — emits the pair inventory + drift table (in-sync / would-write / conflict) instead of reconciling; writes nothing and exits zero regardless of drift.',
282
+ 'Writes memory/<name>.md in the selected crouter scope with substrate frontmatter and the SKILL.md body.',
283
+ 'Skips existing memory docs unless --overwrite is present.',
284
+ 'Skips marker-bearing generated crtr boot skills; host exports prune those legacy artifacts instead.',
285
+ `With --ignore: writes ~/.crouter/${IGNORE_FILE} and does not import the selected sources.`,
286
+ 'With --dry-run: read-only; writes nothing.',
123
287
  ],
124
288
  },
125
289
  run: async (input) => {
126
- const pairFilter = input['pair'];
290
+ const sourceArg = input['source'];
291
+ const scopeArg = input['scope'];
127
292
  const dryRun = input['dryRun'] ?? false;
128
- const statusMode = input['status'] ?? false;
129
- // Malformed manifest throws here (R-U7) → dispatcher's handle() → non-zero.
130
- const { pairs } = readLayeredManifest();
131
- let selected = pairs;
132
- if (pairFilter !== undefined) {
133
- selected = pairs.filter((p) => p.id === pairFilter);
134
- if (selected.length === 0) {
135
- // OD-3 / R-O2 unknown id aborts hard, naming the bad id + valid ids,
136
- // nothing written.
137
- const valid = pairs.map((p) => p.id);
138
- throw usage(`unknown pair id: ${pairFilter}`, {
139
- received: pairFilter,
140
- next: `Valid pair ids: ${valid.length > 0 ? valid.join(', ') : '(none)'}. Run \`crtr sys sync -h\`.`,
141
- });
293
+ const overwrite = input['overwrite'] ?? false;
294
+ const ignoreMode = input['ignore'] ?? false;
295
+ const includeIgnored = input['includeIgnored'] ?? false;
296
+ const roots = sourceRootsFromArg(sourceArg, scopeArg);
297
+ const seen = new Set();
298
+ const candidates = [];
299
+ for (const root of roots) {
300
+ for (const c of collectCandidates(root)) {
301
+ if (seen.has(c.path))
302
+ continue;
303
+ seen.add(c.path);
304
+ candidates.push(c);
142
305
  }
143
306
  }
144
- // --status: read-only inventory. Run the same engine in dry-run mode and
145
- // render endpoints + drift; never write, never exit non-zero on drift.
146
- if (statusMode) {
147
- const results = selected.map((p) => reconcilePair(p, resolveProfile(p.frontmatter), { dryRun: true }));
148
- if (isJsonOutput()) {
149
- emit({
150
- pairs: selected.map((p, i) => ({
151
- id: p.id,
152
- crtr: p.crtr,
153
- claude: p.claude,
154
- drift: DRIFT[results[i].status],
155
- })),
156
- });
307
+ const ignoreState = readIgnoreState();
308
+ const results = [];
309
+ let changedIgnoreState = false;
310
+ for (const candidate of candidates) {
311
+ const prepared = prepareCandidate(candidate, scopeArg);
312
+ if ('status' in prepared) {
313
+ results.push(prepared);
314
+ continue;
157
315
  }
158
- else {
159
- process.stdout.write(renderStatus(selected, results) + '\n');
316
+ if (ignoreMode) {
317
+ if (!dryRun)
318
+ changedIgnoreState = rememberIgnored(prepared, ignoreState) || changedIgnoreState;
319
+ results.push(ignoreResult(prepared, dryRun ? 'would record permanent ignore' : `recorded in ~/.crouter/${IGNORE_FILE}`, dryRun));
320
+ continue;
160
321
  }
161
- process.exit(ExitCode.SUCCESS);
162
- }
163
- // One conflict must NOT abort the loop (R-X3): collect every pair's result,
164
- // partition, then decide the exit code once. A structural error (an
165
- // unresolvable endpoint, both sides missing) is a hard config fault and is
166
- // allowed to propagate — only content conflicts are a collected outcome.
167
- const results = [];
168
- for (const pair of selected) {
169
- const profile = resolveProfile(pair.frontmatter);
170
- results.push(reconcilePair(pair, profile, { dryRun }));
171
- }
172
- const summary = {
173
- synced: results.filter((r) => r.status === 'synced').map((r) => r.id),
174
- conflicts: results.filter((r) => r.status === 'conflict').map((r) => r.id),
175
- noop: results.filter((r) => r.status === 'noop').map((r) => r.id),
176
- wrote: results.reduce((n, r) => n + r.wrote, 0),
177
- };
178
- if (isJsonOutput()) {
179
- emit(summary);
180
- }
181
- else {
182
- process.stdout.write(renderSync(results, summary, dryRun) + '\n');
322
+ if (isIgnored(prepared, ignoreState)) {
323
+ if (includeIgnored)
324
+ results.push(ignoreResult(prepared, `matched ~/.crouter/${IGNORE_FILE}`, false));
325
+ continue;
326
+ }
327
+ results.push(convertPrepared(prepared, { dryRun, overwrite }));
183
328
  }
184
- // R-E7 — exit non-zero IFF a conflict was surfaced.
185
- process.exit(summary.conflicts.length > 0 ? ExitCode.GENERAL : ExitCode.SUCCESS);
329
+ if (changedIgnoreState)
330
+ writeIgnoreState(ignoreState);
331
+ const imported = results.filter((r) => r.status === 'imported').length;
332
+ const wouldImport = results.filter((r) => r.status === 'would-import').length;
333
+ const skipped = results.filter((r) => r.status === 'skipped').length;
334
+ const ignored = results.filter((r) => r.status === 'ignored').length;
335
+ const wouldIgnore = results.filter((r) => r.status === 'would-ignore').length;
336
+ return { imported, wouldImport, skipped, ignored, wouldIgnore, results, ignoreFile: ignoreFilePath(), ignoreMode };
186
337
  },
338
+ render: (result) => renderSummary(result.results, result.ignoreMode === true),
187
339
  });
@@ -0,0 +1 @@
1
+ export declare const sysSyspromptLeaf: import("../../core/command.js").LeafDef;