@synap-core/cli 1.11.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (533) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +40 -198
  3. package/dist/build-info.json +6 -0
  4. package/dist/commands/agent-run.js +3 -2
  5. package/dist/commands/agent-run.js.map +1 -1
  6. package/dist/commands/agents.d.ts +32 -17
  7. package/dist/commands/agents.js +203 -237
  8. package/dist/commands/agents.js.map +1 -1
  9. package/dist/commands/app.d.ts +143 -0
  10. package/dist/commands/app.js +524 -0
  11. package/dist/commands/app.js.map +1 -0
  12. package/dist/commands/automate.d.ts +21 -0
  13. package/dist/commands/automate.js +42 -0
  14. package/dist/commands/automate.js.map +1 -0
  15. package/dist/commands/automation-schema.test.d.ts +16 -0
  16. package/dist/commands/automation-schema.test.js +77 -0
  17. package/dist/commands/automation-schema.test.js.map +1 -0
  18. package/dist/commands/automation.d.ts +12 -0
  19. package/dist/commands/automation.js +116 -66
  20. package/dist/commands/automation.js.map +1 -1
  21. package/dist/commands/box.d.ts +42 -0
  22. package/dist/commands/box.js +89 -0
  23. package/dist/commands/box.js.map +1 -0
  24. package/dist/commands/bridge-setup.d.ts +14 -1
  25. package/dist/commands/bridge-setup.js +395 -129
  26. package/dist/commands/bridge-setup.js.map +1 -1
  27. package/dist/commands/cap-connections.d.ts +4 -4
  28. package/dist/commands/cap-connections.js +14 -14
  29. package/dist/commands/cap-connections.js.map +1 -1
  30. package/dist/commands/capability.d.ts +3 -1
  31. package/dist/commands/capability.js +249 -176
  32. package/dist/commands/capability.js.map +1 -1
  33. package/dist/commands/catalog.d.ts +275 -0
  34. package/dist/commands/catalog.js +481 -0
  35. package/dist/commands/catalog.js.map +1 -0
  36. package/dist/commands/cell.d.ts +155 -8
  37. package/dist/commands/cell.js +416 -40
  38. package/dist/commands/cell.js.map +1 -1
  39. package/dist/commands/centrality.js +1 -13
  40. package/dist/commands/centrality.js.map +1 -1
  41. package/dist/commands/close-questions.d.ts +27 -0
  42. package/dist/commands/close-questions.js +49 -0
  43. package/dist/commands/close-questions.js.map +1 -0
  44. package/dist/commands/close-questions.test.d.ts +1 -0
  45. package/dist/commands/close-questions.test.js +52 -0
  46. package/dist/commands/close-questions.test.js.map +1 -0
  47. package/dist/commands/connect.d.ts +36 -13
  48. package/dist/commands/connect.js +68 -167
  49. package/dist/commands/connect.js.map +1 -1
  50. package/dist/commands/connections.d.ts +3 -2
  51. package/dist/commands/connections.js +95 -25
  52. package/dist/commands/connections.js.map +1 -1
  53. package/dist/commands/context.js +79 -44
  54. package/dist/commands/context.js.map +1 -1
  55. package/dist/commands/data-extra.d.ts +34 -0
  56. package/dist/commands/data-extra.js +203 -25
  57. package/dist/commands/data-extra.js.map +1 -1
  58. package/dist/commands/data.d.ts +120 -2
  59. package/dist/commands/data.js +295 -21
  60. package/dist/commands/data.js.map +1 -1
  61. package/dist/commands/diagnose.d.ts +66 -4
  62. package/dist/commands/diagnose.js +285 -28
  63. package/dist/commands/diagnose.js.map +1 -1
  64. package/dist/commands/doc.d.ts +11 -4
  65. package/dist/commands/doc.js +39 -14
  66. package/dist/commands/doc.js.map +1 -1
  67. package/dist/commands/doctor.d.ts +133 -3
  68. package/dist/commands/doctor.js +761 -41
  69. package/dist/commands/doctor.js.map +1 -1
  70. package/dist/commands/explain.js +5 -5
  71. package/dist/commands/explain.js.map +1 -1
  72. package/dist/commands/hooks.d.ts +19 -0
  73. package/dist/commands/hooks.js +70 -0
  74. package/dist/commands/hooks.js.map +1 -0
  75. package/dist/commands/import.d.ts +393 -13
  76. package/dist/commands/import.js +1150 -54
  77. package/dist/commands/import.js.map +1 -1
  78. package/dist/commands/init.d.ts +34 -9
  79. package/dist/commands/init.js +481 -981
  80. package/dist/commands/init.js.map +1 -1
  81. package/dist/commands/keys.d.ts +1 -0
  82. package/dist/commands/keys.js +15 -2
  83. package/dist/commands/keys.js.map +1 -1
  84. package/dist/commands/knowledge.d.ts +22 -6
  85. package/dist/commands/knowledge.js +121 -57
  86. package/dist/commands/knowledge.js.map +1 -1
  87. package/dist/commands/lens.d.ts +37 -1
  88. package/dist/commands/lens.js +194 -72
  89. package/dist/commands/lens.js.map +1 -1
  90. package/dist/commands/market-authoring.d.ts +29 -2
  91. package/dist/commands/market-authoring.js +348 -7
  92. package/dist/commands/market-authoring.js.map +1 -1
  93. package/dist/commands/market-official.d.ts +58 -0
  94. package/dist/commands/market-official.js +84 -0
  95. package/dist/commands/market-official.js.map +1 -0
  96. package/dist/commands/market.d.ts +400 -16
  97. package/dist/commands/market.js +1802 -146
  98. package/dist/commands/market.js.map +1 -1
  99. package/dist/commands/mcp.d.ts +60 -25
  100. package/dist/commands/mcp.js +251 -64
  101. package/dist/commands/mcp.js.map +1 -1
  102. package/dist/commands/observe.category.test.d.ts +1 -0
  103. package/dist/commands/observe.category.test.js +59 -0
  104. package/dist/commands/observe.category.test.js.map +1 -0
  105. package/dist/commands/observe.d.ts +3 -0
  106. package/dist/commands/observe.js +7 -5
  107. package/dist/commands/observe.js.map +1 -1
  108. package/dist/commands/observe.title.test.d.ts +1 -0
  109. package/dist/commands/observe.title.test.js +37 -0
  110. package/dist/commands/observe.title.test.js.map +1 -0
  111. package/dist/commands/open.d.ts +39 -9
  112. package/dist/commands/open.js +80 -25
  113. package/dist/commands/open.js.map +1 -1
  114. package/dist/commands/playbook.d.ts +42 -0
  115. package/dist/commands/playbook.js +171 -0
  116. package/dist/commands/playbook.js.map +1 -0
  117. package/dist/commands/pod.js +1 -1
  118. package/dist/commands/pod.js.map +1 -1
  119. package/dist/commands/pods.d.ts +11 -1
  120. package/dist/commands/pods.js +129 -131
  121. package/dist/commands/pods.js.map +1 -1
  122. package/dist/commands/proposals-actions.d.ts +62 -0
  123. package/dist/commands/proposals-actions.js +143 -8
  124. package/dist/commands/proposals-actions.js.map +1 -1
  125. package/dist/commands/proposals-await.d.ts +68 -0
  126. package/dist/commands/proposals-await.js +248 -0
  127. package/dist/commands/proposals-await.js.map +1 -0
  128. package/dist/commands/proton-setup.d.ts +73 -0
  129. package/dist/commands/proton-setup.js +579 -0
  130. package/dist/commands/proton-setup.js.map +1 -0
  131. package/dist/commands/providers.d.ts +23 -0
  132. package/dist/commands/providers.js +126 -1
  133. package/dist/commands/providers.js.map +1 -1
  134. package/dist/commands/raycast.js +6 -2
  135. package/dist/commands/raycast.js.map +1 -1
  136. package/dist/commands/rule.d.ts +150 -0
  137. package/dist/commands/rule.js +447 -0
  138. package/dist/commands/rule.js.map +1 -0
  139. package/dist/commands/session-feed-wait.d.ts +48 -0
  140. package/dist/commands/session-feed-wait.js +261 -0
  141. package/dist/commands/session-feed-wait.js.map +1 -0
  142. package/dist/commands/session-post.d.ts +40 -0
  143. package/dist/commands/session-post.js +96 -0
  144. package/dist/commands/session-post.js.map +1 -0
  145. package/dist/commands/session-wait.d.ts +116 -0
  146. package/dist/commands/session-wait.js +224 -0
  147. package/dist/commands/session-wait.js.map +1 -0
  148. package/dist/commands/sessions.d.ts +54 -0
  149. package/dist/commands/sessions.js +501 -58
  150. package/dist/commands/sessions.js.map +1 -1
  151. package/dist/commands/skill-manage.d.ts +21 -0
  152. package/dist/commands/skill-manage.js +136 -19
  153. package/dist/commands/skill-manage.js.map +1 -1
  154. package/dist/commands/skills-commands.d.ts +2 -0
  155. package/dist/commands/skills-commands.js +9 -1
  156. package/dist/commands/skills-commands.js.map +1 -1
  157. package/dist/commands/skills-commands.test.d.ts +1 -0
  158. package/dist/commands/skills-commands.test.js +57 -0
  159. package/dist/commands/skills-commands.test.js.map +1 -0
  160. package/dist/commands/skills.js +16 -0
  161. package/dist/commands/skills.js.map +1 -1
  162. package/dist/commands/status.d.ts +1 -4
  163. package/dist/commands/status.js +6 -35
  164. package/dist/commands/status.js.map +1 -1
  165. package/dist/commands/statusline.backoff.test.d.ts +13 -0
  166. package/dist/commands/statusline.backoff.test.js +100 -0
  167. package/dist/commands/statusline.backoff.test.js.map +1 -0
  168. package/dist/commands/statusline.js +16 -9
  169. package/dist/commands/statusline.js.map +1 -1
  170. package/dist/commands/sync-status.d.ts +12 -0
  171. package/dist/commands/sync-status.js +125 -0
  172. package/dist/commands/sync-status.js.map +1 -0
  173. package/dist/commands/templates-retire.d.ts +34 -0
  174. package/dist/commands/templates-retire.js +192 -0
  175. package/dist/commands/templates-retire.js.map +1 -0
  176. package/dist/commands/tools.js +6 -21
  177. package/dist/commands/tools.js.map +1 -1
  178. package/dist/commands/tracks.d.ts +112 -0
  179. package/dist/commands/tracks.js +543 -0
  180. package/dist/commands/tracks.js.map +1 -0
  181. package/dist/commands/update.d.ts +3 -11
  182. package/dist/commands/update.js +5 -134
  183. package/dist/commands/update.js.map +1 -1
  184. package/dist/commands/upload.d.ts +41 -3
  185. package/dist/commands/upload.js +120 -18
  186. package/dist/commands/upload.js.map +1 -1
  187. package/dist/commands/vendor.d.ts +28 -0
  188. package/dist/commands/vendor.js +110 -0
  189. package/dist/commands/vendor.js.map +1 -0
  190. package/dist/commands/versions.js +6 -0
  191. package/dist/commands/versions.js.map +1 -1
  192. package/dist/commands/view.js +10 -5
  193. package/dist/commands/view.js.map +1 -1
  194. package/dist/commands/whoami.d.ts +30 -3
  195. package/dist/commands/whoami.js +186 -23
  196. package/dist/commands/whoami.js.map +1 -1
  197. package/dist/commands/worker-accounts.d.ts +149 -0
  198. package/dist/commands/worker-accounts.js +339 -0
  199. package/dist/commands/worker-accounts.js.map +1 -0
  200. package/dist/commands/worker-accounts.test.d.ts +1 -0
  201. package/dist/commands/worker-accounts.test.js +224 -0
  202. package/dist/commands/worker-accounts.test.js.map +1 -0
  203. package/dist/commands/worker.d.ts +331 -0
  204. package/dist/commands/worker.js +1067 -0
  205. package/dist/commands/worker.js.map +1 -0
  206. package/dist/commands/worker.test.d.ts +1 -0
  207. package/dist/commands/worker.test.js +427 -0
  208. package/dist/commands/worker.test.js.map +1 -0
  209. package/dist/commands/workspace-ops.d.ts +35 -0
  210. package/dist/commands/workspace-ops.js +153 -0
  211. package/dist/commands/workspace-ops.js.map +1 -0
  212. package/dist/index.d.ts +6 -6
  213. package/dist/index.js +1043 -186
  214. package/dist/index.js.map +1 -1
  215. package/dist/lib/agent-connections.d.ts +31 -0
  216. package/dist/lib/agent-connections.js +33 -0
  217. package/dist/lib/agent-connections.js.map +1 -0
  218. package/dist/lib/agent-detect.d.ts +27 -0
  219. package/dist/lib/agent-detect.js +71 -0
  220. package/dist/lib/agent-detect.js.map +1 -0
  221. package/dist/lib/agent-door.d.ts +136 -0
  222. package/dist/lib/agent-door.js +429 -0
  223. package/dist/lib/agent-door.js.map +1 -0
  224. package/dist/lib/agents-config.d.ts +2 -0
  225. package/dist/lib/agents-config.js +4 -9
  226. package/dist/lib/agents-config.js.map +1 -1
  227. package/dist/lib/approval-poll.d.ts +31 -2
  228. package/dist/lib/approval-poll.js +20 -7
  229. package/dist/lib/approval-poll.js.map +1 -1
  230. package/dist/lib/atomic-file.d.ts +48 -0
  231. package/dist/lib/atomic-file.js +129 -0
  232. package/dist/lib/atomic-file.js.map +1 -0
  233. package/dist/lib/auth.d.ts +17 -11
  234. package/dist/lib/auth.js +47 -64
  235. package/dist/lib/auth.js.map +1 -1
  236. package/dist/lib/batch-approval.d.ts +85 -0
  237. package/dist/lib/batch-approval.js +170 -0
  238. package/dist/lib/batch-approval.js.map +1 -0
  239. package/dist/lib/box/egress.d.ts +82 -0
  240. package/dist/lib/box/egress.js +264 -0
  241. package/dist/lib/box/egress.js.map +1 -0
  242. package/dist/lib/box/evidence.d.ts +86 -0
  243. package/dist/lib/box/evidence.js +121 -0
  244. package/dist/lib/box/evidence.js.map +1 -0
  245. package/dist/lib/box/job-server.d.ts +72 -0
  246. package/dist/lib/box/job-server.js +246 -0
  247. package/dist/lib/box/job-server.js.map +1 -0
  248. package/dist/lib/box/provisioner.d.ts +83 -0
  249. package/dist/lib/box/provisioner.js +312 -0
  250. package/dist/lib/box/provisioner.js.map +1 -0
  251. package/dist/lib/box/proxmox.d.ts +112 -0
  252. package/dist/lib/box/proxmox.js +211 -0
  253. package/dist/lib/box/proxmox.js.map +1 -0
  254. package/dist/lib/box/wire.d.ts +98 -0
  255. package/dist/lib/box/wire.js +70 -0
  256. package/dist/lib/box/wire.js.map +1 -0
  257. package/dist/lib/capture-lane.d.ts +6 -5
  258. package/dist/lib/capture-lane.js +6 -5
  259. package/dist/lib/capture-lane.js.map +1 -1
  260. package/dist/lib/capture-structure.d.ts +230 -11
  261. package/dist/lib/capture-structure.js +251 -13
  262. package/dist/lib/capture-structure.js.map +1 -1
  263. package/dist/lib/catalog-templates.d.ts +59 -0
  264. package/dist/lib/catalog-templates.js +126 -0
  265. package/dist/lib/catalog-templates.js.map +1 -0
  266. package/dist/lib/cli-build.d.ts +19 -0
  267. package/dist/lib/cli-build.js +37 -0
  268. package/dist/lib/cli-build.js.map +1 -0
  269. package/dist/lib/client-header.test.d.ts +1 -0
  270. package/dist/lib/client-header.test.js +85 -0
  271. package/dist/lib/client-header.test.js.map +1 -0
  272. package/dist/lib/conversation-headers.d.ts +33 -0
  273. package/dist/lib/conversation-headers.js +69 -0
  274. package/dist/lib/conversation-headers.js.map +1 -0
  275. package/dist/lib/cp-packages.d.ts +167 -11
  276. package/dist/lib/cp-packages.js +253 -21
  277. package/dist/lib/cp-packages.js.map +1 -1
  278. package/dist/lib/credential-class.d.ts +144 -0
  279. package/dist/lib/credential-class.js +138 -0
  280. package/dist/lib/credential-class.js.map +1 -0
  281. package/dist/lib/describe-lens.d.ts +35 -0
  282. package/dist/lib/describe-lens.js +40 -0
  283. package/dist/lib/describe-lens.js.map +1 -1
  284. package/dist/lib/device-connect.d.ts +125 -0
  285. package/dist/lib/device-connect.js +256 -0
  286. package/dist/lib/device-connect.js.map +1 -0
  287. package/dist/lib/directory-lens.d.ts +99 -0
  288. package/dist/lib/directory-lens.js +187 -0
  289. package/dist/lib/directory-lens.js.map +1 -0
  290. package/dist/lib/exporter-coverage.d.ts +71 -0
  291. package/dist/lib/exporter-coverage.js +97 -0
  292. package/dist/lib/exporter-coverage.js.map +1 -0
  293. package/dist/lib/goal-words.d.ts +38 -0
  294. package/dist/lib/goal-words.js +112 -0
  295. package/dist/lib/goal-words.js.map +1 -0
  296. package/dist/lib/hub-client.d.ts +57 -15
  297. package/dist/lib/hub-client.js +396 -82
  298. package/dist/lib/hub-client.js.map +1 -1
  299. package/dist/lib/init-connect.d.ts +41 -0
  300. package/dist/lib/init-connect.js +75 -0
  301. package/dist/lib/init-connect.js.map +1 -0
  302. package/dist/lib/init-exit.d.ts +24 -0
  303. package/dist/lib/init-exit.js +41 -0
  304. package/dist/lib/init-exit.js.map +1 -0
  305. package/dist/lib/init-flow.d.ts +25 -0
  306. package/dist/lib/init-flow.js +40 -0
  307. package/dist/lib/init-flow.js.map +1 -0
  308. package/dist/lib/init-handoff.d.ts +24 -0
  309. package/dist/lib/init-handoff.js +26 -0
  310. package/dist/lib/init-handoff.js.map +1 -0
  311. package/dist/lib/init-output.d.ts +18 -0
  312. package/dist/lib/init-output.js +43 -0
  313. package/dist/lib/init-output.js.map +1 -0
  314. package/dist/lib/init-pod.d.ts +45 -0
  315. package/dist/lib/init-pod.js +36 -0
  316. package/dist/lib/init-pod.js.map +1 -0
  317. package/dist/lib/installed.d.ts +210 -11
  318. package/dist/lib/installed.js +234 -36
  319. package/dist/lib/installed.js.map +1 -1
  320. package/dist/lib/key-approval.d.ts +57 -0
  321. package/dist/lib/key-approval.js +117 -0
  322. package/dist/lib/key-approval.js.map +1 -0
  323. package/dist/lib/key-diagnosis.d.ts +56 -0
  324. package/dist/lib/key-diagnosis.js +227 -0
  325. package/dist/lib/key-diagnosis.js.map +1 -0
  326. package/dist/lib/key-doctor.d.ts +136 -0
  327. package/dist/lib/key-doctor.js +286 -0
  328. package/dist/lib/key-doctor.js.map +1 -0
  329. package/dist/lib/key-locations.d.ts +120 -0
  330. package/dist/lib/key-locations.js +326 -0
  331. package/dist/lib/key-locations.js.map +1 -0
  332. package/dist/lib/key-source.d.ts +68 -0
  333. package/dist/lib/key-source.js +109 -0
  334. package/dist/lib/key-source.js.map +1 -0
  335. package/dist/lib/key-state.d.ts +70 -0
  336. package/dist/lib/key-state.js +115 -0
  337. package/dist/lib/key-state.js.map +1 -0
  338. package/dist/lib/kind-package.d.ts +181 -0
  339. package/dist/lib/kind-package.js +679 -0
  340. package/dist/lib/kind-package.js.map +1 -0
  341. package/dist/lib/knowledge-title.d.ts +25 -0
  342. package/dist/lib/knowledge-title.js +47 -0
  343. package/dist/lib/knowledge-title.js.map +1 -0
  344. package/dist/lib/knowledge-title.test.d.ts +1 -0
  345. package/dist/lib/knowledge-title.test.js +34 -0
  346. package/dist/lib/knowledge-title.test.js.map +1 -0
  347. package/dist/lib/machine-instance.d.ts +64 -0
  348. package/dist/lib/machine-instance.js +142 -0
  349. package/dist/lib/machine-instance.js.map +1 -0
  350. package/dist/lib/mcp-bridge.d.ts +49 -0
  351. package/dist/lib/mcp-bridge.js +180 -0
  352. package/dist/lib/mcp-bridge.js.map +1 -0
  353. package/dist/lib/mcp-bridge.test.d.ts +1 -0
  354. package/dist/lib/mcp-bridge.test.js +118 -0
  355. package/dist/lib/mcp-bridge.test.js.map +1 -0
  356. package/dist/lib/mcp-call.d.ts +30 -0
  357. package/dist/lib/mcp-call.js +107 -0
  358. package/dist/lib/mcp-call.js.map +1 -0
  359. package/dist/lib/mcp-snippets.d.ts +53 -0
  360. package/dist/lib/mcp-snippets.js +82 -0
  361. package/dist/lib/mcp-snippets.js.map +1 -0
  362. package/dist/lib/next-steps.d.ts +8 -0
  363. package/dist/lib/next-steps.js +31 -1
  364. package/dist/lib/next-steps.js.map +1 -1
  365. package/dist/lib/openclaw.js +3 -2
  366. package/dist/lib/openclaw.js.map +1 -1
  367. package/dist/lib/package-wire.d.ts +179 -0
  368. package/dist/lib/package-wire.js +68 -0
  369. package/dist/lib/package-wire.js.map +1 -0
  370. package/dist/lib/pair-link.d.ts +38 -0
  371. package/dist/lib/pair-link.js +68 -0
  372. package/dist/lib/pair-link.js.map +1 -0
  373. package/dist/lib/pod-moved.d.ts +70 -0
  374. package/dist/lib/pod-moved.js +146 -0
  375. package/dist/lib/pod-moved.js.map +1 -0
  376. package/dist/lib/pod-reach.d.ts +39 -0
  377. package/dist/lib/pod-reach.js +117 -0
  378. package/dist/lib/pod-reach.js.map +1 -0
  379. package/dist/lib/pod-reach.test.d.ts +1 -0
  380. package/dist/lib/pod-reach.test.js +67 -0
  381. package/dist/lib/pod-reach.test.js.map +1 -0
  382. package/dist/lib/pod-wide.d.ts +40 -0
  383. package/dist/lib/pod-wide.js +46 -0
  384. package/dist/lib/pod-wide.js.map +1 -0
  385. package/dist/lib/pod.d.ts +74 -112
  386. package/dist/lib/pod.js +146 -415
  387. package/dist/lib/pod.js.map +1 -1
  388. package/dist/lib/project-ref.d.ts +9 -3
  389. package/dist/lib/project-ref.js +10 -4
  390. package/dist/lib/project-ref.js.map +1 -1
  391. package/dist/lib/register-claude-code-mcp.test.d.ts +1 -0
  392. package/dist/lib/register-claude-code-mcp.test.js +78 -0
  393. package/dist/lib/register-claude-code-mcp.test.js.map +1 -0
  394. package/dist/lib/review-credential.d.ts +119 -0
  395. package/dist/lib/review-credential.js +149 -0
  396. package/dist/lib/review-credential.js.map +1 -0
  397. package/dist/lib/safe-open.d.ts +49 -0
  398. package/dist/lib/safe-open.js +119 -0
  399. package/dist/lib/safe-open.js.map +1 -0
  400. package/dist/lib/session-feed.d.ts +185 -0
  401. package/dist/lib/session-feed.js +257 -0
  402. package/dist/lib/session-feed.js.map +1 -0
  403. package/dist/lib/session-lens.d.ts +40 -1
  404. package/dist/lib/session-lens.js +54 -4
  405. package/dist/lib/session-lens.js.map +1 -1
  406. package/dist/lib/superwhisper-import.d.ts +8 -0
  407. package/dist/lib/superwhisper-import.js +14 -7
  408. package/dist/lib/superwhisper-import.js.map +1 -1
  409. package/dist/lib/superwhisper-title.test.d.ts +1 -0
  410. package/dist/lib/superwhisper-title.test.js +18 -0
  411. package/dist/lib/superwhisper-title.test.js.map +1 -0
  412. package/dist/lib/targets.d.ts +152 -10
  413. package/dist/lib/targets.js +974 -325
  414. package/dist/lib/targets.js.map +1 -1
  415. package/dist/lib/template-file.d.ts +4 -0
  416. package/dist/lib/template-file.js.map +1 -1
  417. package/dist/lib/template-lifecycle.d.ts +217 -0
  418. package/dist/lib/template-lifecycle.js +433 -0
  419. package/dist/lib/template-lifecycle.js.map +1 -0
  420. package/dist/lib/template-steps.d.ts +88 -0
  421. package/dist/lib/template-steps.js +118 -0
  422. package/dist/lib/template-steps.js.map +1 -0
  423. package/dist/lib/time-ago.d.ts +2 -0
  424. package/dist/lib/time-ago.js +15 -0
  425. package/dist/lib/time-ago.js.map +1 -0
  426. package/dist/lib/wake-hook-settings.d.ts +57 -0
  427. package/dist/lib/wake-hook-settings.js +153 -0
  428. package/dist/lib/wake-hook-settings.js.map +1 -0
  429. package/dist/lib/wake-state.d.ts +65 -0
  430. package/dist/lib/wake-state.js +122 -0
  431. package/dist/lib/wake-state.js.map +1 -0
  432. package/dist/utils/logger.js +5 -2
  433. package/dist/utils/logger.js.map +1 -1
  434. package/package.json +26 -17
  435. package/skills/README.md +13 -11
  436. package/skills/agent-os/SKILL.md +214 -99
  437. package/skills/manifest.json +1 -1
  438. package/skills/onboard/SKILL.md +55 -26
  439. package/skills/synap/README.md +5 -8
  440. package/skills/synap/SKILL.md +1149 -289
  441. package/skills/synap/_order.txt +7 -0
  442. package/skills/synap/_skill.yaml +1 -1
  443. package/skills/synap/automations.md +15 -0
  444. package/skills/synap/capture.md +5 -0
  445. package/skills/synap/cli-operations.md +24 -18
  446. package/skills/synap/code-skills.md +70 -0
  447. package/skills/synap/concepts.md +36 -0
  448. package/skills/synap/content-os.md +10 -1
  449. package/skills/synap/creative-loop.md +15 -10
  450. package/skills/synap/crm.md +21 -52
  451. package/skills/synap/document-embeds.md +143 -47
  452. package/skills/synap/escalation-ladder.md +20 -10
  453. package/skills/synap/focus-sessions.md +56 -21
  454. package/skills/synap/from-intent.md +158 -0
  455. package/skills/synap/governance-rules.md +5 -5
  456. package/skills/synap/governance.md +10 -7
  457. package/skills/synap/hand-off.md +33 -0
  458. package/skills/synap/inline-patterns.md +15 -7
  459. package/skills/synap/lenses.md +34 -16
  460. package/skills/synap/linking.md +42 -41
  461. package/skills/synap/mcp-remote-servers.md +64 -0
  462. package/skills/synap/mental-model.md +17 -15
  463. package/skills/synap/operating-mode-rules.md +2 -2
  464. package/skills/synap/processes.md +118 -0
  465. package/skills/synap/quick-reference.md +7 -2
  466. package/skills/synap/reading.md +3 -1
  467. package/skills/synap/reflexes.md +12 -42
  468. package/skills/synap/viewframe-cells.md +38 -5
  469. package/skills/synap/work-flow.md +1 -1
  470. package/skills/synap/work-model.md +110 -0
  471. package/skills/synap/worked-examples.md +4 -5
  472. package/skills/synap/workspace-design.md +15 -11
  473. package/skills/synap/writes.md +105 -25
  474. package/skills/synap/writing-titles.md +63 -0
  475. package/skills/synap-app/SKILL.md +149 -67
  476. package/skills/synap-app/_skill.yaml +1 -1
  477. package/skills/synap-app/creating-the-base-app.md +12 -7
  478. package/skills/synap-app/governance.md +27 -16
  479. package/skills/synap-app/install-commands.md +18 -9
  480. package/skills/synap-app/overview.md +16 -9
  481. package/skills/synap-app/using-the-sdk.md +75 -25
  482. package/skills/synap-design/SKILL.md +474 -0
  483. package/skills/synap-design/_order.txt +4 -0
  484. package/skills/synap-design/_skill.yaml +21 -0
  485. package/skills/synap-design/loop.md +134 -0
  486. package/skills/synap-design/overview.md +44 -0
  487. package/skills/synap-design/principles.md +130 -0
  488. package/skills/synap-design/system.md +133 -0
  489. package/skills/synap-market/SKILL.md +306 -16
  490. package/skills/synap-market/_skill.yaml +1 -1
  491. package/skills/synap-market/author-and-publish.md +145 -8
  492. package/skills/synap-market/governance-and-catalog.md +7 -0
  493. package/skills/synap-market/install.md +79 -1
  494. package/skills/synap-market/overview.md +74 -6
  495. package/skills/synap-schema/SKILL.md +172 -10
  496. package/skills/synap-schema/_order.txt +2 -0
  497. package/skills/synap-schema/_skill.yaml +4 -2
  498. package/skills/synap-schema/common-mistakes.md +4 -2
  499. package/skills/synap-schema/discover-profiles.md +1 -1
  500. package/skills/synap-schema/entity-scope.md +15 -3
  501. package/skills/synap-schema/extend-first.md +29 -0
  502. package/skills/synap-schema/extend-vs-create.md +1 -1
  503. package/skills/synap-schema/intent-vocabulary-ssot.md +111 -0
  504. package/skills/synap-schema/property-types.md +1 -1
  505. package/skills/synap-schema/relations.md +1 -1
  506. package/skills/synap-ui/SKILL.md +19 -12
  507. package/skills/synap-ui/_skill.yaml +1 -1
  508. package/skills/synap-ui/ai-companion.md +1 -1
  509. package/skills/synap-ui/arranging-bento.md +2 -0
  510. package/skills/synap-ui/bento-layouts.md +10 -10
  511. package/skills/synap-ui/bento-recipes.md +313 -543
  512. package/skills/synap-ui/four-builds.md +5 -0
  513. package/skills/synap-ui/view-types.md +2 -2
  514. package/skills/synap-ui/widget-catalog.md +22 -278
  515. package/templates/automations/bookmark-enrichment.automation.json +290 -13
  516. package/templates/automations/url-bookmark-capture.automation.json +73 -11
  517. package/templates/box/runner.py +206 -0
  518. package/templates/skills/authoring-playbooks/SKILL.md +6 -3
  519. package/templates/skills/authoring-playbooks/reference/stages-contract.md +15 -0
  520. package/templates/skills/authoring-playbooks/reference/worked-example.md +3 -0
  521. package/dist/commands/infra.d.ts +0 -21
  522. package/dist/commands/infra.js +0 -262
  523. package/dist/commands/infra.js.map +0 -1
  524. package/node_modules/@synap/hub-rest-client/dist/index.cjs +0 -1261
  525. package/node_modules/@synap/hub-rest-client/dist/index.d.cts +0 -1647
  526. package/node_modules/@synap/hub-rest-client/dist/index.d.ts +0 -1647
  527. package/node_modules/@synap/hub-rest-client/dist/index.js +0 -1231
  528. package/node_modules/@synap/hub-rest-client/package.json +0 -45
  529. package/node_modules/@synap/hub-rest-client/src/client.ts +0 -1631
  530. package/node_modules/@synap/hub-rest-client/src/errors.ts +0 -30
  531. package/node_modules/@synap/hub-rest-client/src/index.ts +0 -153
  532. package/node_modules/@synap/hub-rest-client/src/setup.ts +0 -133
  533. package/node_modules/@synap/hub-rest-client/src/types.ts +0 -1299
package/dist/index.js CHANGED
@@ -1,32 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @synap/cli — Connect AI agents to your Synap data pod
3
+ * @synap-core/cli — Connect AI agents to your Synap data pod
4
4
  *
5
5
  * Usage:
6
- * npx @synap/cli init Full setup: detect, harden, connect, install
7
- * npx @synap/cli connect Connect an AI surface to a Synap pod
8
- * npx @synap/cli status Show pod health and API status
9
- * npx @synap/cli update Update skill + check for CLI updates
6
+ * npx @synap-core/cli init Full setup: detect, harden, connect, install
7
+ * npx @synap-core/cli connect Connect an AI surface to a Synap pod
8
+ * npx @synap-core/cli status Show pod health and API status
9
+ * npx @synap-core/cli update Show how to update the CLI
10
10
  *
11
11
  * Or install globally:
12
- * npm i -g @synap/cli
12
+ * npm i -g @synap-core/cli
13
13
  * synap init
14
14
  */
15
- import { Command } from "commander";
16
- import { readFileSync } from "fs";
17
- import { fileURLToPath } from "url";
18
- import { dirname, join } from "path";
15
+ import { Command, Option } from "commander";
19
16
  import { bootstrapPodOverride } from "./lib/pod.js";
20
- const __filename = fileURLToPath(import.meta.url);
21
- const __dirname = dirname(__filename);
22
- let version = "0.1.0";
23
- try {
24
- const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
25
- version = pkg.version;
26
- }
27
- catch {
28
- // use default
29
- }
17
+ import { INIT_EXIT_HELP } from "./lib/init-exit.js";
18
+ import { cliBuild, formatCliBuild } from "./lib/cli-build.js";
19
+ // The version AND the commit this build came from — a stale build is visible.
20
+ const version = formatCliBuild(cliBuild());
30
21
  const program = new Command();
31
22
  program
32
23
  .name("synap")
@@ -42,7 +33,12 @@ program
42
33
  .description("Full setup: connect to a Synap pod and install the skill")
43
34
  .option("--pod-url <url>", "Synap pod URL (skip pod choice prompt)")
44
35
  .option("--api-key <key>", "Hub Protocol API key (skip key generation)")
45
- .option("--skip-is", "Skip Intelligence Service provider setup")
36
+ // Deprecated no-op, kept so old scripts do not fail on an unknown option.
37
+ .addOption(new Option("--skip-is", "deprecated, ignored").hideHelp())
38
+ .option("-y, --yes", "Accept the defaults, no prompts (connects the agents found on this machine)")
39
+ .option("--agents <list>", "Agents to connect, comma-separated: claude-code,codex,cursor")
40
+ .option("--json", "Print a JSON summary on stdout: {podUrl, connected, notApproved, failed, pairLink, exitCode}")
41
+ .addHelpText("after", INIT_EXIT_HELP)
46
42
  .action(async (opts) => {
47
43
  const { init } = await import("./commands/init.js");
48
44
  await init(opts);
@@ -71,31 +67,61 @@ program
71
67
  .option("--bot-token <token>", "Discord bot token — provisioned into the pod vault")
72
68
  .option("--guild-id <id>", "Discord server (guild) id — written to the bridge .env")
73
69
  .option("--bridge-dir <path>", "Path to the telegram-discord-bridge repo")
74
- .option("--workspace-id <uuid>", "Target workspace (default: pod profile's)")
70
+ .option("--workspace-id <uuid>", "Advanced/legacy: pin the bridge to one workspace (default: pod-wide, never prompted)")
71
+ .option("--project-id <uuid>", "Scope the bridge to one project (default: prompt, pod-wide if declined)")
75
72
  .option("--proactive-channel <id>", "Discord channel id for the agent's proactive posts")
76
73
  .option("--enable-react", "Turn on ✅-react capture")
74
+ // Re-running bridge-setup on an already-provisioned pod now DETECTS what is
75
+ // configured and skips it (the bot token is already in the vault — re-asking
76
+ // for a secret the pod already holds was the sharpest instance). This flag
77
+ // restores the old behaviour for a genuine rotation or repoint.
78
+ .option("--reconfigure", "Re-ask every question and re-provision from scratch (ignore what a previous run configured)")
77
79
  .option("--pod <name>", "Pod profile to connect the bridge to (default: prompt)")
78
- .option("--governance <mode>", "Agent governance preset: safe | normal | crazy (default: prompt)")
80
+ .option("--governance <mode>", "Agent approval preset: safe | creates | normal | crazy (default: prompt; \"follow the pod default\" writes nothing)")
79
81
  .option("--pod-url <url>", "Synap pod URL (override)")
80
82
  .option("--api-key <key>", "Hub Protocol API key (override)")
81
83
  .action(async (opts) => {
82
84
  const { bridgeSetup } = await import("./commands/bridge-setup.js");
83
85
  await bridgeSetup(opts);
84
86
  });
87
+ // Hidden: one-shot Proton Mail↔Synap bridge provisioning (dogfood/dev convenience).
88
+ // Fork of bridge-setup for the sovereign self-host Proton Bridge path — see
89
+ // the catalog capability synap-app/packages/workspace-templates/capabilities/proton.capability.json.
85
90
  program
86
- .command("connect")
87
- .description("Connect an AI surface (Claude Code, Claude Desktop, Cursor, Raycast) to a Synap pod")
88
- .option("--target <name>", "AI surface: claude-code | claude-desktop | cursor | raycast | custom")
91
+ .command("proton-setup", { hidden: true })
92
+ .description("(hidden) One-shot Proton Mail↔Synap bridge provisioning")
93
+ .option("--proton-email <email>", "Proton account email — provisioned into the pod vault")
94
+ .option("--proton-password <password>", "Proton account password (not a bridge-generated value) — provisioned into the pod vault")
95
+ .option("--proton-totp-seed <seed>", "Proton account 2FA/TOTP seed, base32 (optional) — provisioned into the pod vault")
96
+ .option("--proton-mailbox-password <password>", "Proton mailbox password — ONLY for two-password-mode accounts (optional) — provisioned into the pod vault")
97
+ .option("--bridge-dir <path>", "Path to the synap-proton-bridge repo")
98
+ .option("--workspace-id <uuid>", "Advanced/legacy: pin the bridge to one workspace (default: pod-wide, never prompted)")
99
+ .option("--project-id <uuid>", "Scope the bridge to one project (default: prompt, pod-wide if declined)")
100
+ .option("--pod <name>", "Pod profile to connect the bridge to (default: prompt)")
101
+ .option("--governance <mode>", "Agent approval preset: safe | creates | normal | crazy (default: prompt; \"follow the pod default\" writes nothing)")
102
+ .option("--pod-url <url>", "Synap pod URL (override)")
103
+ .option("--api-key <key>", "Hub Protocol API key (override)")
104
+ .action(async (opts) => {
105
+ const { protonSetup } = await import("./commands/proton-setup.js");
106
+ await protonSetup(opts);
107
+ });
108
+ program
109
+ .command("connect [thing]")
110
+ .description("Plug something into your pod: an AI agent (claude-code, cursor, codex, …) or a tool/account (gmail, github, …)")
111
+ .option("--tool", "Treat <thing> as a tool/account even if it is also an agent name (no <thing>: list connectable services)")
112
+ .option("--workspace <id>", "Focus on one workspace (default: pod-wide)")
113
+ .option("--project <id>", "Focus on one project (agents only; composable with --workspace)")
89
114
  .option("--pod-url <url>", "Synap pod URL")
90
115
  .option("--api-key <key>", "Hub Protocol API key")
91
- .option("--name <name>", "Pod profile name to save credentials under (default: 'default')")
92
- .option("--list", "List supported targets and exit")
93
- .option("--manual-key", "Skip the browser approval flow and paste a key (or provisioning token)")
94
- .option("--pin-workspace <id>", "Pin the connection to one workspace (default: pod-wide lens)")
95
- .option("--pin-project <id>", "Pin the connection to one project (composable with --pin-workspace)")
96
- .action(async (opts) => {
116
+ .option("--name <name>", "Pod profile to use (default: active pod)")
117
+ .option("--list", "List what can be connected and exit")
118
+ .addOption(new Option("--with-mcp", "deprecated, ignored: Raycast always gets the pod's MCP server").hideHelp())
119
+ .option("--target <name>", "Legacy spelling of <thing>")
120
+ .option("--pin-workspace <id>", "Legacy spelling of --workspace")
121
+ .option("--pin-project <id>", "Legacy spelling of --project")
122
+ .action(async (thing, opts) => {
97
123
  const { connect } = await import("./commands/connect.js");
98
- await connect(opts);
124
+ await connect(thing, opts);
99
125
  });
100
126
  // ─── pods ─────────────────────────────────────────────────────────────────────
101
127
  const pods = program
@@ -131,7 +157,8 @@ pods
131
157
  .option("--yes", "Non-interactive: skip prompts (won't recreate a rejected key)")
132
158
  .action(async (name, url, opts) => {
133
159
  const { podsUpdate } = await import("./commands/pods.js");
134
- await podsUpdate(name, url, { url: opts.url, yes: opts.yes });
160
+ if (!(await podsUpdate(name, url, { url: opts.url, yes: opts.yes })))
161
+ process.exitCode = 1;
135
162
  });
136
163
  pods
137
164
  .command("remove <name>")
@@ -158,8 +185,11 @@ program
158
185
  });
159
186
  program
160
187
  .command("doctor")
161
- .description("Coherence preflight: pod host resolves, key authenticates, workspace/project exist on THIS pod — catches config drift in one call")
188
+ .description("Coherence preflight: pod host resolves, key authenticates, workspace/project exist on THIS pod, and why each configured key does or does not work — catches config drift in one call")
162
189
  .option("--json", "Output as JSON")
190
+ .option("--fix", "Fix key problems: finish a pending approval, give this computer its own key, or rewrite stale copies — then verify over MCP")
191
+ .option("-v, --verbose", "Every check with its details, ids and addresses")
192
+ .option("--pod-url <url>", "Probe this pod URL (works with no pod configured)")
163
193
  .action(async (opts) => {
164
194
  const { doctor } = await import("./commands/doctor.js");
165
195
  await doctor(opts);
@@ -174,10 +204,11 @@ program
174
204
  });
175
205
  program
176
206
  .command("whoami")
177
- .description("Show the winning API key's identity/scopes/workspace and flag env-vs-surface key divergence")
178
- .action(async () => {
207
+ .description("Show key owner vs EFFECTIVE user (+ isAgent), scopes/workspace, and flag env-vs-surface key divergence")
208
+ .option("--json", "Output as JSON")
209
+ .action(async (opts) => {
179
210
  const { whoami } = await import("./commands/whoami.js");
180
- await whoami();
211
+ await whoami(opts);
181
212
  });
182
213
  // Hidden: compact ANSI line for the Claude Code statusLine (reads stdin JSON).
183
214
  program
@@ -190,59 +221,10 @@ program
190
221
  });
191
222
  program
192
223
  .command("update")
193
- .description("Update synap skill and check for CLI updates")
194
- .option("--server", "Also trigger a Dokploy redeploy of the control-plane")
195
- .option("--all", "Trigger a Dokploy redeploy of all registered services")
196
- .action(async (opts) => {
197
- const { update } = await import("./commands/update.js");
198
- await update(opts);
199
- });
200
- // ─── infra ────────────────────────────────────────────────────────────────────
201
- const infra = program
202
- .command("infra")
203
- .description("Manage servers and deployments via Dokploy");
204
- infra
205
- .command("status", { isDefault: true })
206
- .description("Overview: all servers and services status")
224
+ .description("Show how to update the CLI")
207
225
  .action(async () => {
208
- const { infraStatus } = await import("./commands/infra.js");
209
- await infraStatus();
210
- });
211
- infra
212
- .command("deploy <app>")
213
- .description("Trigger a redeploy for a named service")
214
- .action(async (app) => {
215
- const { infraDeploy } = await import("./commands/infra.js");
216
- await infraDeploy(app);
217
- });
218
- infra
219
- .command("logs <app>")
220
- .description("Tail logs for a service")
221
- .option("-n, --lines <n>", "Number of log lines", "100")
222
- .option("-f, --follow", "Follow log output")
223
- .action(async (app, opts) => {
224
- const { infraLogs } = await import("./commands/infra.js");
225
- await infraLogs(app, { lines: opts.lines ? parseInt(opts.lines, 10) : 100, follow: opts.follow });
226
- });
227
- infra
228
- .command("sync")
229
- .description("Pull Dokploy state into Synap entities (server + deployment profiles)")
230
- .option("--pod-url <url>", "Synap pod URL", process.env.SYNAP_POD_URL)
231
- .option("--api-key <key>", "Hub Protocol API key", process.env.SYNAP_API_KEY)
232
- .action(async (opts) => {
233
- if (!opts.podUrl || !opts.apiKey) {
234
- console.error(" --pod-url and --api-key are required (or set SYNAP_POD_URL / SYNAP_API_KEY)");
235
- process.exit(1);
236
- }
237
- const { infraSync } = await import("./commands/infra.js");
238
- await infraSync(opts.podUrl, opts.apiKey);
239
- });
240
- infra
241
- .command("open")
242
- .description("Open the Dokploy dashboard in your browser")
243
- .action(async () => {
244
- const { infraOpen } = await import("./commands/infra.js");
245
- infraOpen();
226
+ const { update } = await import("./commands/update.js");
227
+ await update();
246
228
  });
247
229
  program
248
230
  .command("switch [name]")
@@ -254,11 +236,31 @@ program
254
236
  program
255
237
  .command("connections")
256
238
  .alias("conn")
257
- .description("Show which pod each agent surface (Claude Code, Desktop, Cursor, Raycast) is connected to")
239
+ .description("Show everything connected: which pod each agent points at, and which tools the pod has connected")
258
240
  .action(async () => {
259
241
  const { connections } = await import("./commands/connections.js");
260
242
  await connections();
261
243
  });
244
+ // ─── app ────────────────────────────────────────────────────────────────────
245
+ // The App Connect flow: a `synap.app.json` in your app repo → one command wires
246
+ // the app's key into its deploy target. See APP-CONNECT-SPEC §5.
247
+ const app = program
248
+ .command("app")
249
+ .description("Connect an app in this repo to your pod (synap.app.json)");
250
+ app
251
+ .command("init")
252
+ .description("Write synap.app.json (asks name, pod, permissions) and register the app")
253
+ .action(async () => {
254
+ const { appInitCommand } = await import("./commands/app.js");
255
+ await appInitCommand();
256
+ });
257
+ app
258
+ .command("connect")
259
+ .description("Request the app's access, await approval, then write its key into the deploy target")
260
+ .action(async () => {
261
+ const { appConnectCommand } = await import("./commands/app.js");
262
+ await appConnectCommand();
263
+ });
262
264
  // ─── mcp ────────────────────────────────────────────────────────────────────
263
265
  // The MCP front door: point any AI client at the pod's /mcp server.
264
266
  const mcp = program
@@ -288,13 +290,45 @@ mcp
288
290
  const { mcpVerify } = await import("./commands/mcp.js");
289
291
  await mcpVerify(opts);
290
292
  });
293
+ mcp
294
+ .command("bridge")
295
+ .description("Stdio MCP server for Claude Code: forwards to the pod and names the conversation (spawned by Claude Code, not run by hand)")
296
+ .option("--url <url>", "The pod MCP URL to forward to")
297
+ .action(async (opts) => {
298
+ const { mcpBridge } = await import("./commands/mcp.js");
299
+ await mcpBridge(opts);
300
+ });
301
+ mcp
302
+ .command("use-bridge")
303
+ .description("Connect Claude Code through the stdio bridge, so work can be traced to and reopened in its conversation")
304
+ .option("--off", "Switch back to the direct http connection")
305
+ .action(async (opts) => {
306
+ const { mcpUseBridge } = await import("./commands/mcp.js");
307
+ await mcpUseBridge(opts);
308
+ });
291
309
  mcp
292
310
  .command("connect [client]")
293
- .description("Write the MCP config for a file-configurable client (Claude Code, Cursor, Desktop, …) — alias of `synap connect`")
311
+ .description("Write the MCP config for a file-configurable client (Claude Code, Cursor, Desktop, ChatGPT, …) — alias of `synap agents add <client>`")
294
312
  .option("--name <name>", "Pod profile to connect")
295
313
  .action(async (client, opts) => {
296
314
  const { connect } = await import("./commands/connect.js");
297
- await connect({ target: client, name: opts.name });
315
+ await connect(client, { name: opts.name });
316
+ });
317
+ mcp
318
+ .command("connect-chatgpt")
319
+ .description("Connect ChatGPT (web) to Synap — API-key connector to your pod")
320
+ .option("--name <name>", "Pod profile to connect")
321
+ .action(async (opts) => {
322
+ const { connect } = await import("./commands/connect.js");
323
+ await connect("chatgpt", { name: opts.name });
324
+ });
325
+ mcp
326
+ .command("connect-chatgpt-oauth")
327
+ .description("Connect ChatGPT (web) via Control Plane OAuth — multi-pod, no key to paste")
328
+ .option("--name <name>", "Pod profile to connect")
329
+ .action(async (opts) => {
330
+ const { connect } = await import("./commands/connect.js");
331
+ await connect("chatgpt-oauth", { name: opts.name });
298
332
  });
299
333
  mcp
300
334
  .command("connect-claude")
@@ -307,6 +341,17 @@ mcp
307
341
  const { mcpConnectClaudeWeb } = await import("./commands/mcp.js");
308
342
  await mcpConnectClaudeWeb(opts);
309
343
  });
344
+ mcp
345
+ .command("ui-setup <client>")
346
+ .description("Print the exact form fields to fill in the client's MCP/Connector UI (ChatGPT, Codex, Cursor, etc.)")
347
+ .option("--name <name>", "Pod profile to use (default: active)")
348
+ .option("--workspace <id>", "Scope to a specific workspace")
349
+ .option("--project <id>", "Focus the agent on a project")
350
+ .option("--json", "Output as JSON")
351
+ .action(async (client, opts) => {
352
+ const { mcpUiSetup } = await import("./commands/mcp.js");
353
+ await mcpUiSetup(client, opts);
354
+ });
310
355
  program
311
356
  .command("login")
312
357
  .description("Connect your Synap account (private templates) + your pods")
@@ -323,11 +368,9 @@ program
323
368
  }
324
369
  const profiles = listPodProfiles();
325
370
  if (profiles.length === 0) {
326
- // No pod saved — guide to pods add
327
- console.log(chalk.dim(" No pod configured yet."));
328
- console.log(chalk.blue(" Adding a new pod...\n"));
329
- const { podsAdd } = await import("./commands/pods.js");
330
- await podsAdd();
371
+ // No pod saved: sign in to the account first, it finds the pods.
372
+ const { podsFirstLogin } = await import("./commands/pods.js");
373
+ await podsFirstLogin();
331
374
  return;
332
375
  }
333
376
  // Already have pods — show status and offer reconnect if any are unreachable
@@ -366,8 +409,23 @@ program
366
409
  console.log(` ${active}${chalk.bold(p.name.padEnd(14))} ${status} ${chalk.dim(p.config.podUrl)}`);
367
410
  }
368
411
  console.log();
412
+ // A computer key (device flow) that expires within 7 days, or has: connect
413
+ // this computer again now — the pod offers the same computer, so its
414
+ // agents, history and rules are kept. Never without a person to approve.
415
+ const { computerKeyNeedsRenewal, connectThisComputer } = await import("./lib/device-connect.js");
416
+ const renewed = new Set();
417
+ for (const p of checks.filter((c) => c.healthy && computerKeyNeedsRenewal(c.config))) {
418
+ const when = `this computer's key ${p.keyValid ? "expires" : "expired"} ${p.config.keyExpiresAt}`;
419
+ if (!process.stdin.isTTY) {
420
+ console.log(chalk.yellow(` ${p.name}: ${when}. Run synap login in a terminal to renew it.`));
421
+ continue;
422
+ }
423
+ console.log(chalk.yellow(` ${p.name}: ${when}. Renewing it.`));
424
+ if (await connectThisComputer(p.config.podUrl, { name: p.name }))
425
+ renewed.add(p.name);
426
+ }
369
427
  // Anything that won't authenticate — unreachable OR a revoked/expired key.
370
- const needsReconnect = checks.filter((p) => !p.healthy || !p.keyValid);
428
+ const needsReconnect = checks.filter((p) => !renewed.has(p.name) && (!p.healthy || !p.keyValid));
371
429
  if (needsReconnect.length > 0) {
372
430
  console.log(chalk.yellow(` ${needsReconnect.length} pod(s) need re-authentication. Refresh credentials:`));
373
431
  for (const p of needsReconnect) {
@@ -412,7 +470,7 @@ program
412
470
  }
413
471
  }
414
472
  else {
415
- console.log(chalk.dim(" Later: synap login · or synap login --token <token> (synap.live/account/tokens)"));
473
+ console.log(chalk.dim(" Later: synap login (prints a link you can open on any device)"));
416
474
  }
417
475
  }
418
476
  const { renderNextSteps, FLOW } = await import("./lib/next-steps.js");
@@ -424,6 +482,7 @@ program
424
482
  .description("Show a lightweight lens map: who you are, your projects (companies/initiatives), and workspaces (operational domains)")
425
483
  .option("--json", "Output as JSON")
426
484
  .option("--details", "Include per-workspace profiles + entity counts (heavier)")
485
+ .option("--workspace <space>", "Orient ONE space by name or id, for this call only — shows its brief (purpose, kinds, rules, skills) without changing your pinned lens")
427
486
  .option("--pod-url <url>", "Pod URL override")
428
487
  .option("--api-key <key>", "API key override")
429
488
  .action(async (opts) => {
@@ -449,9 +508,9 @@ program
449
508
  // See what an AI did across flows: the unified run feed, or one run's activity
450
509
  // timeline (for a capture: WHY a facet/entity dropped, with a fixHint).
451
510
  program
452
- .command("diagnose [runId]")
453
- .description("See what an AI did — the run feed, or one run's activity (why it did what it did)")
454
- .option("--flow <automation|playbook|capture|session>", "Restrict the feed, or the run's flow (default capture for a runId)")
511
+ .command("diagnose [run]")
512
+ .description("See what an AI did — the run feed, or one run's nodes (by id or flow name)")
513
+ .option("--flow <automation|playbook|capture|capability|session|chat>", "Restrict the feed, or narrow the run lookup to one flow")
455
514
  .option("--flow-id <id>", "Restrict the feed to one flow's runs (automationId / playbookId)")
456
515
  .option("--limit <n>", "Max runs in the feed")
457
516
  .option("--json", "Output raw JSON")
@@ -464,7 +523,9 @@ program
464
523
  // ─── use ──────────────────────────────────────────────────────────────────────
465
524
  program
466
525
  .command("use <workspace>")
467
- .description("Set the active workspace (by ID or name) — persisted to ~/.synap/config.json")
526
+ .description("Set the active workspace (by ID or name) — short form of `synap workspace use`. Default scope: this directory")
527
+ .option("--session", "Ephemeral: scope only this Claude Code session, don't persist")
528
+ .option("--global", "Durable machine-wide default (~/.synap/config.json) instead of this directory's .synap/lens.json")
468
529
  .option("--json", "Output as JSON")
469
530
  .option("--pod-url <url>", "Pod URL override")
470
531
  .option("--api-key <key>", "API key override")
@@ -475,7 +536,7 @@ program
475
536
  // ─── project / lens (per-Claude-session scoping) ──────────────────────────────
476
537
  const project = program
477
538
  .command("project")
478
- .description("Manage + focus the cross-cutting project lens (a company/initiative): list, new, use, clear");
539
+ .description("Manage + focus the project lens (a commitment, spanning workspaces): list, new, use, clear");
479
540
  project
480
541
  .command("list", { isDefault: true })
481
542
  .description("List projects on the active pod, with the pinned one marked (bare `synap project` runs this)")
@@ -488,7 +549,7 @@ project
488
549
  });
489
550
  project
490
551
  .command("new <name>")
491
- .description("Create a project (a company/initiative) on the active pod, then guide you to pin + add to it")
552
+ .description("Create a project (a commitment you are driving, with whom) on the active pod, then guide you to pin + add to it")
492
553
  .option("--description <text>", "Optional one-line description")
493
554
  .option("--json", "Output as JSON (incl. nextSteps for agents)")
494
555
  .option("--pod-url <url>", "Pod URL override")
@@ -501,6 +562,7 @@ project
501
562
  .command("use <ref>")
502
563
  .description("Pin the active project (durable, like `synap use <workspace>`). Ref = uuid, slug, or <pod>/<slug> — cross-pod refs switch the active pod too")
503
564
  .option("--session", "Ephemeral: scope only this Claude Code session, don't persist (cross-pod refs refused)")
565
+ .option("--global", "Durable machine-wide default (~/.synap/config.json) instead of this directory's .synap/lens.json")
504
566
  .option("--json", "Output as JSON")
505
567
  .option("--pod-url <url>", "Pod URL override")
506
568
  .option("--api-key <key>", "API key override")
@@ -531,8 +593,10 @@ project
531
593
  });
532
594
  program
533
595
  .command("lens")
534
- .description("Show this Claude session's lens (workspace + project + focus session)")
596
+ .description("Show the effective lens (workspace + project + focus session) and WHICH rung set each one")
535
597
  .option("--json", "Output as JSON")
598
+ .option("--pod-url <url>", "Pod URL override")
599
+ .option("--api-key <key>", "API key override")
536
600
  .action(async (opts) => {
537
601
  const { showLens } = await import("./commands/lens.js");
538
602
  await showLens(opts);
@@ -542,11 +606,12 @@ program
542
606
  // procedural (how-to docs), episodic (captures) — and returns a glass-box answer.
543
607
  program
544
608
  .command("ask <query>")
545
- .description("Ask your pod anything — routes to the right knowledge substrate(s) and shows which answered")
609
+ .description("Ask your pod anything — returns a synthesized answer with its sources")
546
610
  .option("--workspace <id>", "Scope to a specific workspace (omit for pod-wide)")
547
611
  .option("--limit <n>", "Max results per substrate", "10")
548
612
  .option("--json", "Output as JSON")
549
613
  .option("--session", "Scope retrieval to the active session context")
614
+ .option("--raw", "Skip synthesis: show the raw per-substrate matches instead")
550
615
  .option("--compare", "A/B diagnostic: show baseline vs Horizon rankers side-by-side (read-only, does not change the answer)")
551
616
  .option("--pod-url <url>", "Pod URL override")
552
617
  .option("--api-key <key>", "API key override")
@@ -555,21 +620,25 @@ program
555
620
  await askKnowledge(query, opts);
556
621
  });
557
622
  // ─── capture ──────────────────────────────────────────────────────────────────
558
- // Uses the active pod + workspace from config (set via `synap pods use` /
559
- // `synap use` / `synap workspace provision-agent`). No auth flags needed.
623
+ // Uses the active pod from config. Work-lane knowledge does NOT default-pin
624
+ // the active workspace (omit workspaceId → server-derived / pod-wide preferred);
625
+ // pin only with --workspace or --team.
560
626
  program
561
627
  .command("capture [text]")
562
- .description("Capture knowledge: smart `capture \"<free text>\"` (AI structures it) OR typed `capture --type gotcha --claim \"…\"`")
563
- .option("--type <type>", "Typed mode: gotcha|lesson|decision|reference")
628
+ .description("Capture knowledge: smart `capture \"<free text>\"` (AI structures it). For canonical typed Knowledge, use `create entity --profile=knowledge --props='{\"knowledgeForm\":\"insight|caution\"}'`.")
629
+ .option("--type <type>", "Legacy compatibility typed mode (gotcha|lesson|decision|reference); new automation should use knowledgeForm")
564
630
  .option("--claim <text>", "Typed mode: one-line assertion (required with --type)")
631
+ .option("--title <text>", "Typed mode: short name, 80 characters or less (default: the claim's first sentence, never cut)")
632
+ .option("--content <markdown>", "Typed mode: full Markdown explanation, stored as the linked Knowledge note")
565
633
  .option("--why <text>", "Reasoning or context")
566
634
  .option("--evidence <text>", "Supporting file path, URL, or code snippet")
567
635
  .option("--tags <csv>", "Comma-separated tags e.g. repo:synap-backend,layer:migrations")
568
636
  .option("--session", "Link captured knowledge to the active session")
569
- .option("--global", "GLOBAL lane: pod-wide cross-cutting runbook (→ knowledge_keys), visible in every workspace")
570
- .option("--key <ns:slug>", "Stable key for a --global runbook (derived from --type + --claim if omitted)")
571
- .option("--team", "Write to the first product workspace instead of the active workspace")
572
- .option("--workspace <id>", "Explicit workspace override")
637
+ .option("--pod-wide", "GLOBAL lane: pod-wide cross-cutting runbook (→ knowledge_keys), visible in every workspace")
638
+ .option("--global", "Alias for --pod-wide")
639
+ .option("--key <ns:slug>", "Stable key for a --pod-wide runbook (derived from --type + --claim if omitted)")
640
+ .option("--team", "Pin to the first product workspace (default: omit workspaceId — server-derived / pod-wide preferred for knowledge)")
641
+ .option("--workspace <id>", "Explicit workspace pin (otherwise workspaceId is omitted for knowledge)")
573
642
  .option("--project <id|name>", "Pin the capture to a project (id or name) — overrides SYNAP_PROJECT_ID / the session lens")
574
643
  .option("--yes", "Skip the y/N confirmation prompt (smart mode)")
575
644
  .option("--json", "Output as JSON")
@@ -582,10 +651,14 @@ program
582
651
  // the SAME AI capture pipeline (/capture/structure → /capture/execute), so it
583
652
  // inherits entity extraction, the relation graph, and workspace/project routing.
584
653
  program
585
- .command("import <inputs...>")
654
+ .command("import [inputs...]")
586
655
  .description("Import files, folders, or URLs into the pod (AI capture pipeline, or Superwhisper store-first)")
587
656
  .option("--workspace <id|name>", "Override AI workspace routing")
588
657
  .option("--project <id|name>", "Override AI project routing")
658
+ .option("--home-map <pairs>", "Multi-home path map: pathSubstring=workspaceNameOrId pairs " +
659
+ '(e.g. "Projects=Builder,Posts=Content OS"). Matched batch paths are ' +
660
+ "rewritten with the workspace name as the first segment so placement " +
661
+ "heuristics pin the right home. No product defaults.")
589
662
  .option("--dry-run", "Preview only — do not write")
590
663
  .option("--yes", "Skip confirmation / auto-confirm store-first")
591
664
  .option("--session", "Link created entities to the active session")
@@ -594,14 +667,20 @@ program
594
667
  .option("--api-key <key>", "API key override")
595
668
  .option("--source <name>", "Adapter: superwhisper (paired meta.json+output.wav store-first)")
596
669
  .option("--store-first", "Skip AI structure; store units as pod-wide notes (+ optional audio)")
670
+ .option("--keep-raw", "Keep the original file and link it to the created entity (default: extract, then discard the bytes)")
597
671
  .option("--with-audio", "Upload WAV provenance (default on for --source superwhisper --store-first)")
598
672
  .option("--no-with-audio", "Transcript-only (no WAV upload)")
599
673
  .option("--limit <n>", "Max Superwhisper units to process", (v) => parseInt(v, 10))
600
674
  .option("--concurrency <n>", "Parallel unit uploads (1–4)", (v) => parseInt(v, 10))
601
675
  .option("--no-resume", "Ignore local import ledger (re-import all)")
676
+ .option("--job-status <jobId>", "Re-poll a background corpus import started by an earlier run (no waiting)")
602
677
  .action(async (inputs, opts) => {
603
- const { importData } = await import("./commands/import.js");
604
- await importData(inputs, opts);
678
+ const mod = await import("./commands/import.js");
679
+ if (opts.jobStatus) {
680
+ await mod.importJobStatus(opts.jobStatus, opts);
681
+ return;
682
+ }
683
+ await mod.importData(inputs ?? [], opts);
605
684
  });
606
685
  // ─── upload ───────────────────────────────────────────────────────────────────
607
686
  // Store a real file (arbitrary bytes) on the pod — the door an AI/CLI needs to
@@ -609,8 +688,10 @@ program
609
688
  // which mints a `file` entity for the bytes.
610
689
  program
611
690
  .command("upload <path>")
612
- .description("Upload a file (≤10MB) to the pod as a file entity")
613
- .option("--workspace <id>", "Workspace to store the file in")
691
+ .description("Upload a file to the pod as an entity (>10MB goes direct to storage: video ≤500MB, audio ≤100MB, zip ≤200MB)")
692
+ .option("--workspace <id>", "Pin the entity to this workspace (explicit placement)")
693
+ .option("--profile <slug>", "Entity kind to create (default: file), e.g. brand-asset")
694
+ .option("--prop <key=value>", "Entity property (repeatable)", (v, acc = []) => [...acc, v])
614
695
  .option("--attach <entityId>", "After upload, link the document to this entity (references relation)")
615
696
  .option("--title <title>", "Title for the created document")
616
697
  .option("--open", "Open in the Synap desktop app after upload")
@@ -622,6 +703,8 @@ Examples:
622
703
  synap upload ./report.pdf
623
704
  synap upload ./logo.png --title "Brand logo" --workspace <id>
624
705
  synap upload ./spec.pdf --attach <entityId>
706
+ synap upload ./logo.svg --profile brand-asset --prop variant=dark --workspace <id>
707
+ synap upload ./launch.mp4 --workspace <id>
625
708
  synap upload ./report.pdf --open
626
709
  `)
627
710
  .action(async (path, opts) => {
@@ -682,7 +765,42 @@ program
682
765
  // commands (capture, recall, search, …) inherit the active pod + workspace.
683
766
  const workspace = program
684
767
  .command("workspace")
685
- .description("Set up and switch workspace context (run once — persisted to ~/.synap/config.json)");
768
+ .description("Manage + focus the workspace lens (an operational domain): list, use, clear");
769
+ // `workspace use|list|clear` mirror `project use|list|clear` so the two
770
+ // composable lenses read as siblings. ADDITIVE: the original short form
771
+ // `synap use <workspace>` and `synap list workspaces` are untouched and keep
772
+ // working exactly as before — these are aliases, not replacements.
773
+ workspace
774
+ .command("use <workspace>")
775
+ .description("Pin the active workspace by ID or name (durable, like `synap use <workspace>`). Default scope: this directory")
776
+ .option("--session", "Ephemeral: scope only this Claude Code session, don't persist")
777
+ .option("--global", "Durable machine-wide default (~/.synap/config.json) instead of this directory's .synap/lens.json")
778
+ .option("--json", "Output as JSON")
779
+ .option("--pod-url <url>", "Pod URL override")
780
+ .option("--api-key <key>", "API key override")
781
+ .action(async (ws, opts) => {
782
+ const { useWorkspace } = await import("./commands/data.js");
783
+ await useWorkspace(ws, opts);
784
+ });
785
+ workspace
786
+ .command("list", { isDefault: true })
787
+ .description("List workspaces you belong to (bare `synap workspace` runs this)")
788
+ .option("--json", "Output as JSON")
789
+ .option("--pod-url <url>", "Pod URL override")
790
+ .option("--api-key <key>", "API key override")
791
+ .action(async (opts) => {
792
+ const { listWorkspaces } = await import("./commands/data.js");
793
+ await listWorkspaces(opts);
794
+ });
795
+ workspace
796
+ .command("clear")
797
+ .description("Clear the active workspace focus (durable by default)")
798
+ .option("--session", "Ephemeral: clear only this Claude Code session's workspace")
799
+ .option("--json", "Output as JSON")
800
+ .action(async (opts) => {
801
+ const { clearWorkspace } = await import("./commands/data.js");
802
+ await clearWorkspace(opts);
803
+ });
686
804
  workspace
687
805
  .command("provision-agent")
688
806
  .description("Provision an agent-owned workspace and set it as active (idempotent)")
@@ -706,6 +824,54 @@ workspace
706
824
  const { workspacePurge } = await import("./commands/purge.js");
707
825
  await workspacePurge(target, opts);
708
826
  });
827
+ // R8a — governed lifecycle: an agent key gets a proposal, an owner key applies.
828
+ workspace
829
+ .command("archive <workspace>")
830
+ .description("Archive a workspace (hides it, pauses its workspace automations). Governed: agents propose")
831
+ .option("--reason <text>", "Why — shown to the reviewer")
832
+ .option("--json", "Output as JSON")
833
+ .option("--pod-url <url>", "Pod URL override")
834
+ .option("--api-key <key>", "API key override")
835
+ .action(async (target, opts) => {
836
+ const { workspaceArchive } = await import("./commands/workspace-ops.js");
837
+ await workspaceArchive(target, opts);
838
+ });
839
+ workspace
840
+ .command("restore <workspace>")
841
+ .description("Restore an archived workspace (by id). Re-enables nothing; lists automations still paused")
842
+ .option("--reason <text>", "Why — shown to the reviewer")
843
+ .option("--json", "Output as JSON")
844
+ .option("--pod-url <url>", "Pod URL override")
845
+ .option("--api-key <key>", "API key override")
846
+ .action(async (target, opts) => {
847
+ const { workspaceRestore } = await import("./commands/workspace-ops.js");
848
+ await workspaceRestore(target, opts);
849
+ });
850
+ workspace
851
+ .command("rename <workspace> <newName>")
852
+ .description("Rename a workspace. Governed: agents propose")
853
+ .option("--json", "Output as JSON")
854
+ .option("--pod-url <url>", "Pod URL override")
855
+ .option("--api-key <key>", "API key override")
856
+ .action(async (target, newName, opts) => {
857
+ const { workspaceRename } = await import("./commands/workspace-ops.js");
858
+ await workspaceRename(target, newName, opts);
859
+ });
860
+ // R8a — `synap entity move`. (`get/create/set entity` stay under their verbs.)
861
+ program
862
+ .command("entity")
863
+ .description("Entity operations: move")
864
+ .command("move <entityIds...>")
865
+ .description("Move entities into another workspace. Governed per entity: moved / proposed / errors")
866
+ .requiredOption("--to <workspace>", "Destination workspace id or name")
867
+ .option("--reason <text>", "Why they belong there — shown to the reviewer")
868
+ .option("--json", "Output as JSON")
869
+ .option("--pod-url <url>", "Pod URL override")
870
+ .option("--api-key <key>", "API key override")
871
+ .action(async (entityIds, opts) => {
872
+ const { entityMove } = await import("./commands/workspace-ops.js");
873
+ await entityMove(entityIds, opts);
874
+ });
709
875
  // ─── list ─────────────────────────────────────────────────────────────────────
710
876
  const list = program
711
877
  .command("list")
@@ -819,8 +985,10 @@ const proposals = program
819
985
  .description("Review and act on governance proposals");
820
986
  proposals
821
987
  .command("list", { isDefault: true })
822
- .description("List pending governance proposals for this agent")
988
+ .description("List governance proposals (default: the pending queue)")
989
+ .option("--status <status>", "pending (default) | approved | auto_approved (what the agent already did) | rejected | expired (never decided) | all", "pending")
823
990
  .option("--workspace <id>", "Scope to a specific workspace")
991
+ .option("--session <id>", "Scope to one focus session (the agent run that filed them)")
824
992
  .option("--limit <n>", "Max results", "20")
825
993
  .option("--json", "Output as JSON")
826
994
  .option("--pod-url <url>", "Pod URL override")
@@ -831,8 +999,11 @@ proposals
831
999
  });
832
1000
  proposals
833
1001
  .command("approve <id>")
834
- .description("Approve a pending proposal")
1002
+ .description("Approve a pending proposal (interactive only — requires a TTY and a typed confirmation)")
835
1003
  .option("--reason <text>", "Optional approval note")
1004
+ .option("--verb <verb>", "Same-name choice: fill_empty | keep_existing | use_capture | separate")
1005
+ .option("--existing <uuid>", "Existing record id — required for fill_empty, keep_existing, and use_capture")
1006
+ .option("--yes", "Accepted for symmetry with other commands, but does NOT skip the typed confirmation — approval is always deliberate")
836
1007
  .option("--json", "Output as JSON")
837
1008
  .option("--pod-url <url>", "Pod URL override")
838
1009
  .option("--api-key <key>", "API key override")
@@ -851,6 +1022,61 @@ proposals
851
1022
  const { rejectProposal } = await import("./commands/proposals-actions.js");
852
1023
  await rejectProposal(id, opts);
853
1024
  });
1025
+ proposals
1026
+ .command("await <id>")
1027
+ .description("Block until a proposal is decided. Exit 0 approved · 1 rejected/expired/withdrawn · 2 timeout")
1028
+ .option("--timeout <duration>", "Give up after this long (e.g. 30m, 2h)", "30m")
1029
+ .option("--interval <duration>", "Delay between checks (e.g. 10s)", "10s")
1030
+ .option("--json", "Output the final status as JSON")
1031
+ .option("--pod-url <url>", "Pod URL override")
1032
+ .option("--api-key <key>", "API key override")
1033
+ .action(async (id, opts) => {
1034
+ const { awaitProposalCommand } = await import("./commands/proposals-await.js");
1035
+ await awaitProposalCommand(id, opts);
1036
+ });
1037
+ // ─── worker ───────────────────────────────────────────────────────────────────
1038
+ // Run Claude Code unattended on THIS machine for the sessions handed to an agent
1039
+ // (the session roster → orient handedToYou). See src/commands/worker.ts.
1040
+ program
1041
+ .command("worker")
1042
+ .description("Run Claude Code unattended here for the sessions handed to an agent")
1043
+ .requiredOption("--agent <name>", "The named agent this machine works as (its key comes from ~/.synap)")
1044
+ .option("--interval <dur>", "How often to check for handed sessions (e.g. 30s, 2m)", "30s")
1045
+ .option("--once", "Check once, run what is handed, wait for it, exit")
1046
+ .option("--cwd <dir>", "Where code sessions' Claude Code works (default ~/Documents/Code/synap)")
1047
+ .option("--work-root <dir>", "Where playbook runs over pod data (no repo) get a folder each, <dir>/<session id> (default ~/synap-work)")
1048
+ .option("--max-parallel <n>", "Runs at the same time", "1")
1049
+ .option("--claude-bin <path>", "The claude binary", "claude")
1050
+ .option("--claude-config-dir <dir>", "CLAUDE_CONFIG_DIR for launched runs — picks which Claude subscription they use")
1051
+ .option("--accounts <file>", "Claude accounts to rotate across ({version:1, reserve, accounts:[{id,label,token,design?}]}, mode 600); default ~/.config/synap-worker/accounts.json when it exists, else the environment's login")
1052
+ .option("--allowed-tools <list>", "Comma-separated Claude tools allowed without asking (the pod MCP server is always added)")
1053
+ .option("--skip-permissions", "Launch with --dangerously-skip-permissions instead of an allow-list")
1054
+ .option("--dry-run", "Claim and log what would launch, without launching Claude")
1055
+ .action(async (opts) => {
1056
+ const { workerCommand } = await import("./commands/worker.js");
1057
+ await workerCommand(opts);
1058
+ });
1059
+ // ─── box ──────────────────────────────────────────────────────────────────────
1060
+ // Run THIS computer as a run box provisioner: lease the box runs queued on the
1061
+ // pod, run each in a fresh isolated LXC, hand back the evidence. See src/commands/box.ts.
1062
+ const box = program.command("box").description("Run scripts in fresh isolated boxes on this computer (run box provisioner)");
1063
+ box
1064
+ .command("serve")
1065
+ .description("Lease queued box runs from the pod and run each in a fresh box")
1066
+ .option("--config <file>", "Provisioner config", "/etc/synap-box/config.json")
1067
+ .option("--once", "Lease at most one run, work it, exit")
1068
+ .action(async (opts) => {
1069
+ const { boxServeCommand } = await import("./commands/box.js");
1070
+ await boxServeCommand(opts);
1071
+ });
1072
+ box
1073
+ .command("reap")
1074
+ .description("Destroy every box no run holds (after a crash)")
1075
+ .option("--config <file>", "Provisioner config", "/etc/synap-box/config.json")
1076
+ .action(async (opts) => {
1077
+ const { boxReapCommand } = await import("./commands/box.js");
1078
+ await boxReapCommand(opts);
1079
+ });
854
1080
  // ─── session ──────────────────────────────────────────────────────────────────
855
1081
  // Focus sessions — goal-bound work rooms tracked on the pod.
856
1082
  // AI agents: use start_session to begin, update_session to report progress,
@@ -864,7 +1090,14 @@ session
864
1090
  .description("Start a new focus session and return its ID")
865
1091
  .requiredOption("--goal <text>", "What this session is working toward")
866
1092
  .option("--workspace <id>", "Workspace that owns the session")
1093
+ .option("--project <id>", "Project that owns the session (defaults to the active project lens; the pod accepts a workspace OR a project)")
867
1094
  .option("--task-id <id>", "Link session to an existing task entity")
1095
+ .option("--track <id>", "Born inside a track (a project's goal) — implies its project")
1096
+ .option("--template <id>", "Start from a playbook (seeds currentStage from its stages)")
1097
+ .option("--no-template", "Opt out of auto-matching a playbook — start ad-hoc")
1098
+ .option("--parent <id>", "Push from another session — records session --spawned_from--> session (the parent stays open)")
1099
+ .option("--suspended-intent <text>", "With --parent: this session BLOCKS the parent. One line naming what the parent was about to do; the parent waits until this session closes")
1100
+ .option("--criteria <file.json>", "Path to a JSON file of binary acceptance criteria (SessionCriterion[])")
868
1101
  .option("--json", "Output as JSON")
869
1102
  .option("--pod-url <url>", "Pod URL override")
870
1103
  .option("--api-key <key>", "API key override")
@@ -876,7 +1109,9 @@ session
876
1109
  .command("list", { isDefault: true })
877
1110
  .description("List focus sessions in the active workspace")
878
1111
  .option("--workspace <id>", "Scope to a specific workspace")
1112
+ .option("--project <id>", "Scope to a project (its sessions across spaces)")
879
1113
  .option("--status <status>", "Filter by status: active | paused | closed")
1114
+ .option("--kind <kind>", "Filter by population: work | run | receipt | all", "all")
880
1115
  .option("--limit <n>", "Max results", "20")
881
1116
  .option("--json", "Output as JSON")
882
1117
  .option("--pod-url <url>", "Pod URL override")
@@ -887,8 +1122,8 @@ session
887
1122
  });
888
1123
  session
889
1124
  .command("get <id>")
890
- .description("Show details for a specific focus session")
891
- .option("--workspace <id>", "Workspace that owns the session (required for scoped fetch)")
1125
+ .description("Show details for a specific focus session (works for project-scoped sessions)")
1126
+ .option("--workspace <id>", "Workspace hint if the pod still requires it for GET")
892
1127
  .option("--json", "Output as JSON")
893
1128
  .option("--pod-url <url>", "Pod URL override")
894
1129
  .option("--api-key <key>", "API key override")
@@ -898,10 +1133,14 @@ session
898
1133
  });
899
1134
  session
900
1135
  .command("update <id>")
901
- .description("Update progress or status of a focus session")
902
- .requiredOption("--workspace <id>", "Workspace that owns the session")
903
- .option("--progress <0-100>", "Progress percentage")
1136
+ .description("Update a focus session's progress, status, stage or goal")
1137
+ // NOT required: `workspaceId` is `.optional()` on the pod's UpdateBodySchema
1138
+ // and the session is addressed by <id>. Demanding it made an already-attached
1139
+ // or project-scoped session un-updatable without restating a known lens.
1140
+ .option("--workspace <id>", "Workspace that owns the session (optional; inferred from the active lens)")
1141
+ .option("--progress <0-100>", "Progress percentage — a whole number 0-100, not prose")
904
1142
  .option("--status <status>", "New status: active | paused")
1143
+ .option("--stage <key>", "Advance the session's current stage (playbook stage key)")
905
1144
  .option("--goal <text>", "Revised goal")
906
1145
  .option("--json", "Output as JSON")
907
1146
  .option("--pod-url <url>", "Pod URL override")
@@ -912,10 +1151,18 @@ session
912
1151
  });
913
1152
  session
914
1153
  .command("close <id>")
915
- .description("Close a focus session and optionally attach a recap")
916
- .requiredOption("--workspace <id>", "Workspace that owns the session")
917
- .option("--recap <text>", "Short summary of what was accomplished")
918
- .option("--json", "Output as JSON")
1154
+ .description("End a focus session (proposal pack) and optionally attach a recap")
1155
+ .option("--workspace <id>", "Workspace hint (optional; not required for complete)")
1156
+ .option("--recap <text>", "Short summary of what was accomplished (maps to summary)")
1157
+ // Deliberately NOT validated here, unlike --progress. A bad --progress
1158
+ // serialized to NaN and came back as an unreadable zod echo; a bad --as comes
1159
+ // back naming the three legal values, because the pod's enum derives from
1160
+ // TERMINAL_SESSION_STATUSES. Re-listing them here would be a second copy of
1161
+ // an enum this CLI cannot import — the hand-mirrored-vocabulary defect.
1162
+ .option("--as <status>", "How it ended: closed (default, finished) | cancelled (abandoned) | failed (could not complete)")
1163
+ .option("--withdraw <messageId=note>", "An open question you asked that your work overtook, with why (repeatable)", collect, [])
1164
+ .option("--keep-owed <messageId>", "An open question the person still owes you an answer to after this session (repeatable)", collect, [])
1165
+ .option("--json", "Output full pack (or close result) as JSON")
919
1166
  .option("--pod-url <url>", "Pod URL override")
920
1167
  .option("--api-key <key>", "API key override")
921
1168
  .action(async (id, opts) => {
@@ -942,12 +1189,271 @@ session
942
1189
  });
943
1190
  session
944
1191
  .command("current")
945
- .description("Show the active session for this terminal (from .synap-session or SYNAP_SESSION_ID)")
1192
+ .description("Show the active session for this terminal (SYNAP_SESSION_ID, Claude-session lens, or .synap/lens.json)")
946
1193
  .option("--json", "Output as JSON")
947
1194
  .action(async (opts) => {
948
1195
  const { sessionStatus } = await import("./commands/sessions.js");
949
1196
  sessionStatus(opts);
950
1197
  });
1198
+ session
1199
+ .command("evidence <key>")
1200
+ .description("Post your own pass/fail report for one evidence-checked acceptance criterion")
1201
+ .option("--session <id>", "Focus session ID (defaults to this terminal's active session)")
1202
+ .option("--passed", "The criterion is satisfied")
1203
+ .option("--failed", "The criterion is not satisfied")
1204
+ .option("--detail <text>", "Optional note explaining the verdict")
1205
+ .option("--json", "Output as JSON")
1206
+ .option("--pod-url <url>", "Pod URL override")
1207
+ .option("--api-key <key>", "API key override")
1208
+ .action(async (key, opts) => {
1209
+ const { evidenceSession } = await import("./commands/sessions.js");
1210
+ await evidenceSession(key, opts);
1211
+ });
1212
+ session
1213
+ .command("evaluate")
1214
+ .description("Run every pending acceptance-criterion check (evidence, capability, judge) and print the verdicts")
1215
+ .option("--session <id>", "Focus session ID (defaults to this terminal's active session)")
1216
+ .option("--json", "Output as JSON")
1217
+ .option("--pod-url <url>", "Pod URL override")
1218
+ .option("--api-key <key>", "API key override")
1219
+ .action(async (opts) => {
1220
+ const { evaluateSession } = await import("./commands/sessions.js");
1221
+ await evaluateSession(opts);
1222
+ });
1223
+ session
1224
+ .command("wait [sessionId]")
1225
+ .description("Block until the human replies in the session room (or, with --events, until the session changes), then print it. Session: the argument, else the one attached to this terminal. Exit 0 replied · 1 timeout · 2 failure (auth/network/broken read); with --hook: 2 wake · 0 anything else")
1226
+ .option("--since <iso>", "Only events strictly after this ISO timestamp (default: now — skips anything that predates this wait; with --events: the unread ones)")
1227
+ .option("--events <kinds>", "Wait on the session change feed: comma list of answer, proposal_decided, stage_moved, owner_message, session_message (a message from another session's agent), or all")
1228
+ .option("--wake-only", "Stop only on what should wake an agent: answers, owner messages, and agent messages the pod marked wake (the rest is read and skipped)")
1229
+ .option("--once", "One read, no waiting loop")
1230
+ .option("--peek", "Mark nothing picked up or read (a watcher that is not the agent doing the work)")
1231
+ .option("--hook", "Run as a Claude Code asyncRewake Stop hook: wait for a wake-worthy event, print it on stderr, exit 2 (see: synap hooks install-wake)")
1232
+ .option("--timeout <duration>", "Give up after this long (e.g. 30m, 2h)", "30m")
1233
+ .option("--interval <duration>", "Delay between checks of the answer-only wait (e.g. 10s)", "10s")
1234
+ .option("--limit <n>", "Max answers / events per page, 1-100", "20")
1235
+ .option("--json", "Output the reply(ies) or events — or the timeout — as JSON")
1236
+ .option("--pod-url <url>", "Pod URL override")
1237
+ .option("--api-key <key>", "API key override")
1238
+ .action(async (sessionId, opts) => {
1239
+ const { waitSessionCommand } = await import("./commands/session-wait.js");
1240
+ await waitSessionCommand(sessionId, opts);
1241
+ });
1242
+ session
1243
+ .command("post <toSessionId> <text...>")
1244
+ .description("Message ANOTHER of your sessions' agent: blocked_on_you / handoff may wake it, done / fyi never do. Replying to a message that woke you? Pass its id as --cause. Exit 0 delivered · 1 pod does not deliver agent messages · 2 failure")
1245
+ .option("--intent <intent>", "blocked_on_you | handoff | done | fyi", "fyi")
1246
+ .option("--cause <messageId>", "The message that woke you (the id `synap session wait` printed), when this replies to it")
1247
+ .option("--from <sessionId>", "Your session, when this terminal is not bound to it")
1248
+ .option("--ref <kind:id[:label]>", "The state behind the message: session:<id>, blocked_by:<id>, proposal:<id>, slot:<sessionId>:<label> (repeatable)", collect, [])
1249
+ .option("--json", "Output the pod's reply as JSON")
1250
+ .option("--pod-url <url>", "Pod URL override")
1251
+ .option("--api-key <key>", "API key override")
1252
+ .action(async (toSessionId, text, opts) => {
1253
+ const { sessionPostCommand } = await import("./commands/session-post.js");
1254
+ await sessionPostCommand(toSessionId, text, opts);
1255
+ });
1256
+ // ─── hooks ────────────────────────────────────────────────────────────────────
1257
+ // Claude Code hooks the CLI owns. install-wake: the asyncRewake Stop hook that
1258
+ // lets another session's agent (or the person) wake an idle terminal.
1259
+ const hooksCmd = program
1260
+ .command("hooks")
1261
+ .description("Claude Code hooks that connect a terminal to its Synap session");
1262
+ hooksCmd
1263
+ .command("install-wake")
1264
+ .description("Add the wake hook (a Stop hook running `synap session wait --hook`) to this project's .claude/settings.json — idempotent, other hooks kept")
1265
+ .option("--uninstall", "Remove the wake hook instead")
1266
+ .option("--user", "Use ~/.claude/settings.json (every project) instead of the project's")
1267
+ .option("--dir <path>", "The project directory (default: the current one)")
1268
+ .option("--json", "Output the result as JSON")
1269
+ .action(async (opts) => {
1270
+ const { installWakeCommand } = await import("./commands/hooks.js");
1271
+ installWakeCommand(opts);
1272
+ });
1273
+ // ─── playbook ─────────────────────────────────────────────────────────────────
1274
+ // Start a governed process in one step — the Hub door MCP synap_run_playbook
1275
+ // shares. A proposal ("queued for your review") is success.
1276
+ const playbookCmd = program
1277
+ .command("playbook")
1278
+ .description("Run playbooks — governed, reusable processes");
1279
+ playbookCmd
1280
+ .command("run <name-or-id>")
1281
+ .description("Run a playbook by name or id, optionally on a subject entity")
1282
+ .option("--subject <entityId>", "The entity the playbook runs on")
1283
+ .option("--param <key=value>", "Answer a declared param (repeatable; typed by the playbook)", collect, [])
1284
+ .option("--workspace <id>", "Write home for the run (defaults to the playbook's own workspace)")
1285
+ .option("--reason <text>", "Why — shown to the reviewer")
1286
+ .option("--json", "Output as JSON")
1287
+ .option("--pod-url <url>", "Pod URL override")
1288
+ .option("--api-key <key>", "API key override")
1289
+ .action(async (ref, opts) => {
1290
+ const { runPlaybook } = await import("./commands/playbook.js");
1291
+ await runPlaybook(ref, opts);
1292
+ });
1293
+ // ─── track ────────────────────────────────────────────────────────────────────
1294
+ // A goal inside a project, worked in sessions (`session start --track`); a
1295
+ // person closes it with a verdict. Legacy tracks also run a method with steps. Writes are governed —
1296
+ // "queued for your review" is success.
1297
+ const track = program
1298
+ .command("track")
1299
+ .description("Manage tracks — the goals a project pursues");
1300
+ track
1301
+ .command("list")
1302
+ .description("List a project's goals (the active project lens); with no project in scope, every goal on the pod")
1303
+ .option("--project <id>", "Project id (defaults to the active project lens)")
1304
+ .option("--everywhere", "List every goal on the pod, ignoring the project lens")
1305
+ .option("--limit <n>", "Pod-wide: page size (default 50)")
1306
+ .option("--cursor <cursor>", "Pod-wide: the next page (printed under the last one)")
1307
+ .option("--all", "Include dropped (archived) tracks")
1308
+ .option("--json", "Output as JSON")
1309
+ .option("--pod-url <url>", "Pod URL override")
1310
+ .option("--api-key <key>", "API key override")
1311
+ .action(async (opts) => {
1312
+ const { listTracks } = await import("./commands/tracks.js");
1313
+ await listTracks(opts);
1314
+ });
1315
+ track
1316
+ .command("show <id>")
1317
+ .description("Show one track: its steps and where it stands")
1318
+ .option("--json", "Output as JSON")
1319
+ .option("--pod-url <url>", "Pod URL override")
1320
+ .option("--api-key <key>", "API key override")
1321
+ .action(async (id, opts) => {
1322
+ const { showTrack } = await import("./commands/tracks.js");
1323
+ await showTrack(id, opts);
1324
+ });
1325
+ track
1326
+ .command("start")
1327
+ .description("Start a track — a goal — on a project")
1328
+ .option("--name <name>", "The track's name (required without --playbook)")
1329
+ .option("--direction <text>", "Where it is heading, one line")
1330
+ .option("--playbook <id>", "Legacy: a track template (project-scoped playbook with steps)")
1331
+ .option("--project <id>", "Project id (defaults to the active project lens)")
1332
+ .option("--param <key=value>", "Answer a declared param (repeatable)", collect, [])
1333
+ .option("--end-state <text>", "What done looks like, in your words")
1334
+ .option("--criterion <text>", "How it is judged done — '[measured|judged|handed_over|learning|decided_outside|certified:] statement'; give 0 or 2-5 (repeatable)", collect, [])
1335
+ .option("--drop-condition <text>", "When to stop pursuing it (repeatable, at most 5)", collect, [])
1336
+ .option("--kind <kind>", "Kind of work: project|flow|operation|investigation|incident|commission|pathway (a proposal until you confirm)")
1337
+ .option("--uncertainty <level>", "How well the way is known: clear|complicated|complex|chaotic|unknown")
1338
+ .option("--loop <on|off|n>", "Let the goal propose its next session: on, off, or n sessions a week")
1339
+ .option("--reason <text>", "Why — shown to the reviewer")
1340
+ .option("--json", "Output as JSON")
1341
+ .option("--pod-url <url>", "Pod URL override")
1342
+ .option("--api-key <key>", "API key override")
1343
+ .action(async (opts) => {
1344
+ const { startTrack } = await import("./commands/tracks.js");
1345
+ await startTrack(opts);
1346
+ });
1347
+ track
1348
+ .command("update <id>")
1349
+ .description("Set a goal's direction, KPI and what it holds (each field is written whole; kpi is merged)")
1350
+ .option("--direction <text>", "Where it is heading, one line")
1351
+ .option("--kpi-current <n>", "The KPI's latest value (stated, never measured)")
1352
+ .option("--kpi-target <n>", "Move the KPI's target")
1353
+ .option("--end-state <text>", "What done looks like, in your words")
1354
+ .option("--criterion <text>", "How it is judged done — '[measured|judged|handed_over|learning|decided_outside|certified:] statement'; give 0 or 2-5 (repeatable)", collect, [])
1355
+ .option("--drop-condition <text>", "When to stop pursuing it (repeatable, at most 5)", collect, [])
1356
+ .option("--kind <kind>", "Kind of work: project|flow|operation|investigation|incident|commission|pathway (a proposal until you confirm)")
1357
+ .option("--uncertainty <level>", "How well the way is known: clear|complicated|complex|chaotic|unknown")
1358
+ .option("--loop <on|off|n>", "Let the goal propose its next session: on, off, or n sessions a week")
1359
+ .option("--reason <text>", "Why — shown to the reviewer")
1360
+ .option("--json", "Output as JSON")
1361
+ .option("--pod-url <url>", "Pod URL override")
1362
+ .option("--api-key <key>", "API key override")
1363
+ .action(async (id, opts) => {
1364
+ const { updateTrack } = await import("./commands/tracks.js");
1365
+ await updateTrack(id, opts);
1366
+ });
1367
+ track
1368
+ .command("accept <id> <criterion> <decision>")
1369
+ .description("The goal's judge decides a claimed criterion: accepted | changes_asked | rejected (a person's act)")
1370
+ .option("--note <text>", "What to change, or why it was rejected")
1371
+ .option("--json", "Output as JSON")
1372
+ .option("--pod-url <url>", "Pod URL override")
1373
+ .option("--api-key <key>", "API key override")
1374
+ .action(async (id, criterion, decision, opts) => {
1375
+ const { acceptCriterion } = await import("./commands/tracks.js");
1376
+ await acceptCriterion(id, criterion, decision, opts);
1377
+ });
1378
+ track
1379
+ .command("advance <id> <stage>")
1380
+ .description("Legacy (tracks with steps): move a track to a step; prints the session that step offers")
1381
+ .option("--reason <text>", "Why — shown to the reviewer")
1382
+ .option("--json", "Output as JSON")
1383
+ .option("--pod-url <url>", "Pod URL override")
1384
+ .option("--api-key <key>", "API key override")
1385
+ .action(async (id, stage, opts) => {
1386
+ const { advanceTrack } = await import("./commands/tracks.js");
1387
+ await advanceTrack(id, stage, opts);
1388
+ });
1389
+ track
1390
+ .command("status <id> <status>")
1391
+ .description("Move a track: proposed|agreed|ready|active|paused|completed (Closed)|archived (Dropped). " +
1392
+ "Closing needs --verdict met|partly_met|not_met (an operation closes retired, and only one does); dropping should carry " +
1393
+ "no_longer_needed|impossible|replaced|mistake (impossible and replaced need --verdict-reason). " +
1394
+ "Both are final — reopen makes a new version.")
1395
+ .option("--verdict <verdict>", "How the goal ended (required to close; advised to drop)")
1396
+ .option("--verdict-reason <text>", "Why — required for impossible and replaced")
1397
+ .option("--reason <text>", "Why — shown to the reviewer")
1398
+ .option("--json", "Output as JSON")
1399
+ .option("--pod-url <url>", "Pod URL override")
1400
+ .option("--api-key <key>", "API key override")
1401
+ .action(async (id, status, opts) => {
1402
+ const { setTrackStatus } = await import("./commands/tracks.js");
1403
+ await setTrackStatus(id, status, opts);
1404
+ });
1405
+ track
1406
+ .command("reopen <id>")
1407
+ .description("Reopen a closed or dropped goal as a NEW version (the old one keeps its verdict)")
1408
+ .option("--name <name>", "A new name (default: the old one's)")
1409
+ .option("--reason <text>", "Why — shown to the reviewer")
1410
+ .option("--json", "Output as JSON")
1411
+ .option("--pod-url <url>", "Pod URL override")
1412
+ .option("--api-key <key>", "API key override")
1413
+ .action(async (id, opts) => {
1414
+ const { reopenTrack } = await import("./commands/tracks.js");
1415
+ await reopenTrack(id, opts);
1416
+ });
1417
+ const goal = program
1418
+ .command("goal")
1419
+ .description("Goals — what people call a track");
1420
+ goal
1421
+ .command("convert <entityId>")
1422
+ .description("Make a goal, objective or OKR entity a goal (once; the entity is kept and linked)")
1423
+ .option("--project <id>", "The goal's project — only when the entity is filed in none or several")
1424
+ .option("--name <name>", "A name other than the entity's title")
1425
+ .option("--reason <text>", "Why — shown to the reviewer")
1426
+ .option("--json", "Output as JSON")
1427
+ .option("--pod-url <url>", "Pod URL override")
1428
+ .option("--api-key <key>", "API key override")
1429
+ .action(async (entityId, opts) => {
1430
+ const { convertToGoal } = await import("./commands/tracks.js");
1431
+ await convertToGoal(entityId, opts);
1432
+ });
1433
+ track
1434
+ .command("params <id> <pairs...>")
1435
+ .description("Answer the method's params as key=value (merged; key=null clears one)")
1436
+ .option("--reason <text>", "Why — shown to the reviewer")
1437
+ .option("--json", "Output as JSON")
1438
+ .option("--pod-url <url>", "Pod URL override")
1439
+ .option("--api-key <key>", "API key override")
1440
+ .action(async (id, pairs, opts) => {
1441
+ const { setTrackParams } = await import("./commands/tracks.js");
1442
+ await setTrackParams(id, pairs, opts);
1443
+ });
1444
+ track
1445
+ .command("start-step <id>")
1446
+ .description("Legacy (tracks with steps): start one step's session — on a track with no steps, use 'session start --track'")
1447
+ .option("--stage <key>", "Step key (defaults to the current step)")
1448
+ .option("--title <text>", "Session name (defaults to the step's name)")
1449
+ .option("--goal <text>", "Outcome (defaults to the step's goal)")
1450
+ .option("--json", "Output as JSON")
1451
+ .option("--pod-url <url>", "Pod URL override")
1452
+ .option("--api-key <key>", "API key override")
1453
+ .action(async (id, opts) => {
1454
+ const { startStep } = await import("./commands/tracks.js");
1455
+ await startStep(id, opts);
1456
+ });
951
1457
  // ─── skill ────────────────────────────────────────────────────────────────────
952
1458
  // Autonomous workspace session skills — context gathering, task planning, etc.
953
1459
  const skillCmd = program
@@ -981,6 +1487,7 @@ skillCmd
981
1487
  skillCmd
982
1488
  .command("get <slug>")
983
1489
  .description("Fetch a skill from the knowledge base by slug and print its body")
1490
+ .option("--raw", "Print only the stored body, exactly (for scripts)")
984
1491
  .option("--pod-url <url>", "Pod URL override")
985
1492
  .option("--api-key <key>", "API key override")
986
1493
  .action(async (slug, opts) => {
@@ -1007,6 +1514,16 @@ skillCmd
1007
1514
  const { listSkills } = await import("./commands/skill-manage.js");
1008
1515
  await listSkills(opts);
1009
1516
  });
1517
+ skillCmd
1518
+ .command("approve <slug-or-id>")
1519
+ .description("Approve a skill so it loads (agent-authored skills are born unapproved)")
1520
+ .option("--revoke", "Revoke approval instead of granting it")
1521
+ .option("--pod-url <url>", "Pod URL override")
1522
+ .option("--api-key <key>", "API key override")
1523
+ .action(async (ref, opts) => {
1524
+ const { approveSkill } = await import("./commands/skill-manage.js");
1525
+ await approveSkill(ref, opts);
1526
+ });
1010
1527
  skillCmd
1011
1528
  .command("remove <slug>")
1012
1529
  .alias("rm")
@@ -1063,12 +1580,34 @@ const observe = program
1063
1580
  observe
1064
1581
  .command("write <text>", { isDefault: true })
1065
1582
  .description("Record an observation about the user (pod-scoped, AI-maintained)")
1066
- .option("--category <cat>", "working_style | communication | focus | technical | preference", "preference")
1583
+ .option("--category <cat>", "working_style | communication | focus | preferences | habits | technical", "preferences")
1067
1584
  .option("--confidence <n>", "Confidence score 0-1", "0.5")
1068
1585
  .option("--json", "Output as JSON")
1069
1586
  .option("--pod-url <url>", "Pod URL override")
1070
1587
  .option("--api-key <key>", "API key override")
1071
1588
  .action(async (text, opts) => {
1589
+ // `write` is the DEFAULT subcommand, so an unrecognized token lands here as
1590
+ // `text` instead of erroring — and this default WRITES. `synap observe recal`
1591
+ // therefore recorded an observation literally reading "recal" into the
1592
+ // AI-maintained user model, silently. Every other `isDefault` group in this
1593
+ // file has a zero-arg default, which commander rejects on its own; this is
1594
+ // the only one that can absorb a typo, and the only one where absorbing it
1595
+ // is a mutation.
1596
+ //
1597
+ // The guard keys off whether `write` was named EXPLICITLY, not off the text:
1598
+ // a mistyped subcommand is always a single bare token, and a real
1599
+ // observation is a sentence. Going through `observe write <text>` skips the
1600
+ // check entirely, so the escape hatch is real rather than circular.
1601
+ const argv = process.argv;
1602
+ const observeAt = argv.indexOf("observe");
1603
+ const explicitWrite = observeAt !== -1 && argv[observeAt + 1] === "write";
1604
+ if (!explicitWrite && !/\s/.test(text.trim())) {
1605
+ const siblings = observe.commands.map((c) => c.name()).filter((n) => n !== "write");
1606
+ console.error(`error: '${text}' looks like a subcommand, not observation text — nothing was recorded.\n` +
1607
+ ` Available: ${siblings.map((n) => `synap observe ${n}`).join(", ")}\n` +
1608
+ ` To record it as an observation anyway: synap observe write ${JSON.stringify(text)}`);
1609
+ process.exit(1);
1610
+ }
1072
1611
  const { observeWrite } = await import("./commands/observe.js");
1073
1612
  await observeWrite(text, opts);
1074
1613
  });
@@ -1108,6 +1647,7 @@ create
1108
1647
  .description("Create a new entity")
1109
1648
  .requiredOption("--profile <slug>", "Profile slug (e.g. person, note, task)")
1110
1649
  .requiredOption("--name <name>", "Entity name")
1650
+ .option("--content <markdown>", "Long-form Markdown body, stored as the entity's linked document")
1111
1651
  .option("--workspace <id>", "Create in this workspace")
1112
1652
  .option("--props <json>", "Properties as JSON string", "{}")
1113
1653
  .option("--json", "Output as JSON")
@@ -1150,43 +1690,48 @@ set
1150
1690
  // ─── agents ───────────────────────────────────────────────────────────────────
1151
1691
  const agents = program
1152
1692
  .command("agents")
1153
- .description("Manage named agent identities (different keys, different pod access)");
1693
+ .description("Manage agent principals on your pod (one per type for you; local keys are a cache)");
1154
1694
  agents
1155
1695
  .command("list", { isDefault: true })
1156
- .description("List configured agent identities")
1696
+ .description("List your agents on this pod (one per type). Use --local for ~/.synap cache only.")
1157
1697
  .option("--json", "Output as JSON")
1698
+ .option("--local", "List only the local ~/.synap agent cache")
1158
1699
  .action(async (opts) => {
1159
1700
  const { agentsList } = await import("./commands/agents.js");
1160
- agentsList(opts);
1701
+ await agentsList(opts);
1161
1702
  });
1162
1703
  agents
1163
- .command("add")
1164
- .description("Register a pre-existing agent credential locally. Use `synap agents create` for new agents.")
1165
- .option("--name <name>", "Agent name (e.g. researcher, builder)")
1166
- .option("--api-key <key>", "Hub Protocol API key for this agent")
1167
- .option("--pod <name>", "Pod profile to link (default: active pod)")
1168
- .option("--workspace <id>", "Default workspace for this agent")
1704
+ .command("add [kind]")
1705
+ .description("Bring an agent into the pod: a client (claude-code, cursor, codex, …), your own (--name → URL + key), or pick from this machine (no kind)")
1706
+ .option("--name <name>", "Name your own agent (e.g. researcher) — prints a URL + key to paste")
1707
+ .option("--template <tmpl>", "Your own agent's template: twin | assistant | custom (default: custom)")
1708
+ .option("--type <type>", "Explicit agent type for your own agent (default: a slug of --name)")
1709
+ .option("--role <role>", "Workspace role: admin | editor | viewer (default: editor)")
1710
+ .option("--workspace <id>", "Focus on one workspace (default: pod-wide)")
1711
+ .option("--project <id>", "Focus on one project (composable with --workspace)")
1712
+ .addOption(new Option("--with-mcp", "deprecated, ignored: Raycast always gets the pod's MCP server").hideHelp())
1713
+ .option("--api-key <key>", "Register a key you already hold, on this machine only (mints nothing)")
1169
1714
  .option("--label <label>", "Human-readable description")
1170
- .action(async (opts) => {
1715
+ .action(async (kind, opts) => {
1171
1716
  const { agentsAdd } = await import("./commands/agents.js");
1172
- await agentsAdd(opts);
1717
+ await agentsAdd(kind, opts);
1173
1718
  });
1174
1719
  agents
1175
1720
  .command("create")
1176
- .description("Create a new agent on the pod (template-aware). Replaces `add` for new agents.")
1721
+ .description("Deprecated — use `synap agents add` (same flags)")
1177
1722
  .option("--template <tmpl>", "Agent template: twin | assistant | custom (default: custom)")
1178
1723
  .option("--name <name>", "Agent name (not needed for twin — auto-generated)")
1179
1724
  .option("--type <type>", "Agent type string for custom agents (default: matches template)")
1180
- .option("--role <role>", "Workspace role: admin | editor | viewer (default: editor; twin auto-inherits)")
1181
- .option("--workspace <id>", "Workspace ID to add the agent to (auto-detected if omitted)")
1182
- .option("--pod <name>", "Pod profile to use (default: active pod)")
1725
+ .option("--role <role>", "Workspace role: admin | editor | viewer (default: editor)")
1726
+ .option("--workspace <id>", "Focus on one workspace (default: pod-wide)")
1727
+ .option("--project <id>", "Focus on one project")
1183
1728
  .action(async (opts) => {
1184
1729
  const { agentsCreate } = await import("./commands/agents.js");
1185
1730
  await agentsCreate(opts);
1186
1731
  });
1187
1732
  agents
1188
1733
  .command("rotate-key <name-or-id>")
1189
- .description("Rotate the Hub Protocol API key for an agent (invalidates the current key)")
1734
+ .description("Rotate the Hub Protocol API key for an agent (local cache only)")
1190
1735
  .action(async (nameOrId) => {
1191
1736
  const { agentsRotateKey } = await import("./commands/agents.js");
1192
1737
  await agentsRotateKey(nameOrId);
@@ -1194,17 +1739,19 @@ agents
1194
1739
  agents
1195
1740
  .command("remove <name>")
1196
1741
  .alias("rm")
1197
- .description("Remove a named agent identity")
1742
+ .description("Remove a named agent identity (local cache only)")
1198
1743
  .action(async (name) => {
1199
1744
  const { agentsRemove } = await import("./commands/agents.js");
1200
1745
  agentsRemove(name);
1201
1746
  });
1202
1747
  agents
1203
1748
  .command("info <name>")
1204
- .description("Show details for a named agent identity")
1205
- .action(async (name) => {
1749
+ .description("Show details for a named agent identity (local cache only)")
1750
+ .option("--show-key", "Print the MCP URL and the full API key")
1751
+ .option("--snippets", "Print paste-ready configs (Claude Code, Cursor, stdio clients)")
1752
+ .action(async (name, opts) => {
1206
1753
  const { agentsInfo } = await import("./commands/agents.js");
1207
- agentsInfo(name);
1754
+ await agentsInfo(name, opts);
1208
1755
  });
1209
1756
  // ─── agent (autonomous runner) ───────────────────────────────────────────────
1210
1757
  const agent = program
@@ -1282,6 +1829,52 @@ agent
1282
1829
  const { agentTick } = await import("./commands/agent-run.js");
1283
1830
  await agentTick(opts);
1284
1831
  });
1832
+ // ─── automate ───────────────────────────────────────────────────────────────
1833
+ // The natural-language automation door. It stays deliberately separate from
1834
+ // `automation create`, which remains the expert, explicit-DSL path.
1835
+ program
1836
+ .command("automate <instruction>")
1837
+ .description("Ask the workspace agent to prepare a governed automation proposal")
1838
+ .option("--workspace <id|name>", "Workspace to use (uses active workspace/lens if omitted)")
1839
+ .option("--thread <id>", "Continue an existing agent thread/channel")
1840
+ .option("--new", "Start a fresh agent thread")
1841
+ .option("--timeout <seconds>", "How long to wait for the agent's reply (default: 90)")
1842
+ .option("--json", "Machine-readable output { ok, reply, threadId, workspaceId }")
1843
+ .option("--pod-url <url>", "Override pod URL")
1844
+ .option("--api-key <key>", "Override API key")
1845
+ .action(async (instruction, opts) => {
1846
+ const { automate } = await import("./commands/automate.js");
1847
+ await automate(instruction, opts);
1848
+ });
1849
+ // ─── rule ────────────────────────────────────────────────────────────────────
1850
+ // The fourth door, beside `ask` (recall), `capture` (write) and `diagnose`
1851
+ // (inspect): a STANDING instruction. It classifies the sentence and shows the
1852
+ // reading before writing anything — and it records the rule only. Building the
1853
+ // behaviour half is still `synap automate`.
1854
+ const ruleCmd = program
1855
+ .command("rule [text]")
1856
+ .description("State a standing rule in plain language — classified, shown, then governed-created")
1857
+ .option("--workspace <id>", "Workspace to scope the rule to (uses active workspace if omitted)")
1858
+ .option("--pod-wide", "Scope the rule to the whole pod, ignoring the active workspace")
1859
+ .option("--yes", "Skip the one-off confirmation (never skips a clarification)")
1860
+ .option("--json", "Output raw JSON")
1861
+ .option("--pod-url <url>", "Pod URL override")
1862
+ .option("--api-key <key>", "API key override")
1863
+ .action(async (text, opts) => {
1864
+ const { rule } = await import("./commands/rule.js");
1865
+ await rule(text ?? "", opts);
1866
+ });
1867
+ ruleCmd
1868
+ .command("list")
1869
+ .description("List rules with their intent, scope, and whether a behaviour is attached")
1870
+ .option("--workspace <id>", "Filter by workspace")
1871
+ .option("--json", "Output raw JSON")
1872
+ .option("--pod-url <url>", "Pod URL override")
1873
+ .option("--api-key <key>", "API key override")
1874
+ .action(async (opts) => {
1875
+ const { ruleList } = await import("./commands/rule.js");
1876
+ await ruleList(opts);
1877
+ });
1285
1878
  // ─── automation ──────────────────────────────────────────────────────────────
1286
1879
  function collect(val, prev) { return [...prev, val]; }
1287
1880
  const automation = program
@@ -1473,12 +2066,11 @@ program
1473
2066
  .description("Open in the Synap desktop app. Use: open <kind> <id> or open <id> (auto-resolves type)")
1474
2067
  .action(async (kindOrId, id) => {
1475
2068
  if (id) {
1476
- // open entity|view|cell|document|proposal <id>
1477
- const validKinds = ["entity", "view", "cell", "document", "proposal"];
1478
- if (!validKinds.includes(kindOrId)) {
1479
- console.error(`Unknown kind: "${kindOrId}". Use: ${validKinds.join(" | ")}`);
1480
- process.exit(1);
1481
- }
2069
+ // open <kind> <id> — the browser's object-nav.ts is the one route
2070
+ // table for openable kinds (21 and growing); the CLI doesn't
2071
+ // duplicate it here. An unroutable kind still opens the app and
2072
+ // is reported via the printed deep link (objectNavTarget decides
2073
+ // routability on the other side).
1482
2074
  const { openInBrowser } = await import("./commands/open.js");
1483
2075
  await openInBrowser({ kind: kindOrId, id });
1484
2076
  }
@@ -1515,10 +2107,12 @@ Examples:
1515
2107
  });
1516
2108
  doc
1517
2109
  .command("update <documentId>")
1518
- .description("Update a document's content (full replacement, proposal-gated)")
2110
+ .description("Update a document's content: the whole body, or one section (--section). Governed: an agent's edit is proposed.")
1519
2111
  .option("--content <text>", "New markdown content (inline)")
1520
2112
  .option("--file <path>", "Read new content from a file")
1521
- .option("--title <title>", "Update document title (requires --content too)")
2113
+ .option("--section <id>", "Write ONE section by id (content = the section body) instead of the whole document")
2114
+ .option("--section-title <title>", "The section heading (required with --section)")
2115
+ .option("--base-revision <n>", "The revision you read; refused if the document changed since")
1522
2116
  .option("--open", "Open in the Synap desktop app after update")
1523
2117
  .option("--json", "JSON output")
1524
2118
  .option("--pod-url <url>", "Pod URL override")
@@ -1528,6 +2122,7 @@ Examples:
1528
2122
  synap doc update <id> --content "# Updated"
1529
2123
  synap doc update <id> --file ./notes.md
1530
2124
  cat notes.md | synap doc update <id>
2125
+ synap doc update <id> --section risks --section-title "Risks" --file ./risks.md
1531
2126
  `)
1532
2127
  .action(async (documentId, opts) => {
1533
2128
  const { docUpdate } = await import("./commands/doc.js");
@@ -1560,17 +2155,30 @@ cell
1560
2155
  .requiredOption("--name <name>", "Cell display name")
1561
2156
  .option("--file <path>", "ESM source file (or pipe stdin)")
1562
2157
  .option("--type-key <key>", "Explicit typeKey (default: generated:<slug>)")
1563
- .option("--deps <json>", 'Dependency map JSON, e.g. \'{"recharts":"2.12.0"}\' (sent in payload; backend support pending)')
1564
- .option("--workspace <id>", "Workspace to scope the cell to (omit for pod-global)")
2158
+ .option("--deps <json>", 'Dependency map JSON, e.g. \'{"recharts":"2.12.0"}\'')
2159
+ .option("--view-types <list>", 'Comma-separated view types this cell can render as a VIEW RENDERER, e.g. "list,table,kanban". Omit to leave an existing affinity untouched; pass "" to clear it')
2160
+ .option("--content-kind <kind>", "What this cell renders — entity-detail | entity-card | entity-profile | collection | widget (default: widget, which is placeable but never offered as a profile renderer)")
2161
+ // NOT "omit for pod-wide": `cell.ts` resolves `opts.workspace ?? cfg.workspaceId`,
2162
+ // and `cfg.workspaceId` is the resolved lens (session › directory › env ›
2163
+ // profile default). Omitting therefore scopes the cell to whatever workspace
2164
+ // your lens currently points at — which is pod-wide ONLY when nothing resolves.
2165
+ // Verified 2026-09-05: run from a directory with no `.synap/lens.json` and the
2166
+ // cell silently lands in the pod profile's default workspace.
2167
+ .option("--workspace <id>", "Workspace to scope the cell to (default: your active workspace lens — run `synap lens` to see it)")
1565
2168
  .option("--description <text>", "Short description")
1566
2169
  .option("--open", "Open in the Synap desktop app after define")
1567
2170
  .option("--json", "JSON output")
1568
2171
  .option("--pod-url <url>", "Pod URL override")
1569
2172
  .option("--api-key <key>", "API key override")
1570
2173
  .addHelpText("after", `
2174
+ Without --view-types / --content-kind a cell installs cleanly but can never be
2175
+ PICKED as a view or profile renderer — it is only placeable on a bento.
2176
+
1571
2177
  Examples:
1572
2178
  synap cell define --name "Revenue Chart" --file ./chart.js
1573
2179
  synap cell define --name "Revenue Chart" --file ./chart.js --deps '{"recharts":"2.12.0"}'
2180
+ synap cell define --name "Deal Board" --file ./board.js --view-types kanban,table --content-kind collection
2181
+ synap cell define --name "Contact Page" --file ./contact.js --content-kind entity-detail
1574
2182
  cat chart.js | synap cell define --name "Revenue Chart"
1575
2183
  `)
1576
2184
  .action(async (opts) => {
@@ -1581,10 +2189,16 @@ cell
1581
2189
  .command("build <entry>")
1582
2190
  .description("Bundle a multi-file cell/app into a single ESM module the runtime expects")
1583
2191
  .option("--out <file>", "Output file path (default: <entry>.bundle.js)")
2192
+ .option("--bundle-deps", "Inline all third-party deps into the bundle (only react/react-dom stay external) — output has deps={} and the runtime CSP drops esm.sh entirely")
2193
+ .option("--self-contained", 'Build ONE complete HTML document (React, CSS, brand fonts inlined; nothing external) — the code of a rendererType "mcp-app" cell')
2194
+ .option("--fonts", "With --self-contained: require the inlined brand fonts (default: include when @synap-core/design-tokens resolves)")
2195
+ .option("--no-fonts", "With --self-contained: skip the inlined brand fonts")
1584
2196
  .option("--define", "Chain into cell define after bundling (requires --name)")
1585
2197
  .option("--name <name>", "Cell name (required with --define)")
1586
2198
  .option("--type-key <key>", "Explicit typeKey (used with --define)")
1587
2199
  .option("--deps <json>", "Override dependency versions as JSON, e.g. '{\"react\":\"18.3.0\"}'")
2200
+ .option("--view-types <list>", 'Comma-separated view-renderer affinity (used with --define), e.g. "list,table"')
2201
+ .option("--content-kind <kind>", "entity-detail | entity-card | entity-profile | collection | widget (used with --define)")
1588
2202
  .option("--workspace <id>", "Workspace (used with --define)")
1589
2203
  .option("--description <text>", "Description (used with --define)")
1590
2204
  .option("--open", "Open in browser after define (used with --define)")
@@ -1592,12 +2206,20 @@ cell
1592
2206
  .option("--pod-url <url>", "Pod URL override")
1593
2207
  .option("--api-key <key>", "API key override")
1594
2208
  .addHelpText("after", `
1595
- Output: a single ESM file. Bare imports (react, recharts, …) are externalized
1596
- and emitted as a deps map. At runtime Synap resolves them via esm.sh importmap.
2209
+ Output: a single ESM file.
2210
+
2211
+ Default mode: bare imports (react, recharts, …) are externalized and emitted
2212
+ as a deps map. At runtime Synap resolves them via esm.sh importmap.
2213
+
2214
+ --bundle-deps mode: only react/react-dom are externalized (React is
2215
+ host-inlined at runtime, never via esm.sh); everything else is bundled into
2216
+ the output, so the deps map is empty. A cell built this way reaches no esm.sh
2217
+ origin at runtime — the runtime CSP collapses to drop it entirely.
1597
2218
 
1598
2219
  Examples:
1599
2220
  synap cell build ./src/chart.tsx --out ./dist/chart.js
1600
2221
  synap cell build ./src/chart.tsx --out ./dist/chart.js --define --name "Revenue Chart"
2222
+ synap cell build ./src/chart.tsx --out ./dist/chart.js --bundle-deps
1601
2223
  `)
1602
2224
  .action(async (entry, opts) => {
1603
2225
  const { cellBuild } = await import("./commands/cell.js");
@@ -1606,12 +2228,12 @@ Examples:
1606
2228
  // ─── artifact ─────────────────────────────────────────────────────────────────
1607
2229
  const artifact = program
1608
2230
  .command("artifact")
1609
- .description("Create and manage HTML artifacts as Synap documents");
2231
+ .description("Create HTML artifacts as Synap cells: an HTML/JS page that opens in the desktop app and can read pod data");
1610
2232
  artifact
1611
2233
  .command("create")
1612
- .description("Create an HTML artifact from a local file")
2234
+ .description("Define a cell from a local HTML file (governed: an agent key files a proposal)")
1613
2235
  .requiredOption("--html <file>", "HTML file to upload")
1614
- .option("--name <name>", "Document title (default: filename)")
2236
+ .option("--name <name>", "Cell name (default: filename)")
1615
2237
  .option("--workspace <id>", "Workspace context")
1616
2238
  .option("--open", "Open in the Synap desktop app after creation")
1617
2239
  .option("--json", "JSON output")
@@ -1647,7 +2269,7 @@ tools
1647
2269
  });
1648
2270
  tools
1649
2271
  .command("connect <service>")
1650
- .description("Connect a credential to a tool (via Nango OAuth or vault)")
2272
+ .description("Connect a credential to a tool (OAuth via the connection broker, or vault)")
1651
2273
  .option("--workspace <id>", "Workspace context")
1652
2274
  .option("--pod-url <url>", "Pod URL override")
1653
2275
  .option("--api-key <key>", "API key override")
@@ -1712,6 +2334,33 @@ podCmd
1712
2334
  const { podAdopt } = await import("./commands/pod.js");
1713
2335
  await podAdopt(workspaceNameOrId, opts);
1714
2336
  });
2337
+ // ─── vendor — the author's publisher profile ─────────────────────────────────
2338
+ // Attribution is write-time-only at the CP: a package published before a vendor
2339
+ // exists is orphaned from its author forever. See src/commands/vendor.ts.
2340
+ const vendor = program
2341
+ .command("vendor")
2342
+ .description("Your marketplace publisher profile (what packages are attributed to)");
2343
+ vendor
2344
+ .command("create <slug>")
2345
+ .description("Create your publisher profile — do this BEFORE your first publish")
2346
+ .option("--name <name>", "Display name (defaults to a title-cased slug)")
2347
+ .option("--description <text>", "Short description of you or your studio")
2348
+ .option("--website <url>", "Website URL (must be a full URL)")
2349
+ .option("--json", "Output as JSON")
2350
+ .action(async (slug, _opts, cmd) => {
2351
+ const opts = cmd.optsWithGlobals();
2352
+ const { vendorCreate } = await import("./commands/vendor.js");
2353
+ await vendorCreate(slug, opts);
2354
+ });
2355
+ vendor
2356
+ .command("show")
2357
+ .description("Show whether you have a publisher profile")
2358
+ .option("--json", "Output as JSON")
2359
+ .action(async (_opts, cmd) => {
2360
+ const opts = cmd.optsWithGlobals();
2361
+ const { vendorShow } = await import("./commands/vendor.js");
2362
+ await vendorShow(opts);
2363
+ });
1715
2364
  // ─── market — discover + install ANY package type ────────────────────────────
1716
2365
  // launch is workspace-first (bundled, offline); market reaches every CP package
1717
2366
  // type (capability/skill/workflow/view/cell/workspace), public or private.
@@ -1730,6 +2379,7 @@ market
1730
2379
  .command("install <slug>")
1731
2380
  .description("Install a package by slug into a project — workspaces install now; other types route you to the right surface")
1732
2381
  .option("--project <id>", "Optionally tag the seeded entities to a project (installs are pod-wide by default)")
2382
+ .option("--workspace <id>", "Acting workspace (UUID) a workflow/automation package is added to (required for that kind; defaults to your active lens). Ignored for pod-wide kinds")
1733
2383
  .option("--onto <workspaceId>", "Reconcile this template ONTO an existing workspace (additive) instead of creating a new one")
1734
2384
  .option("--dry-run", "Preview the create path write-free — report would-create / reuse / conflicts, install nothing")
1735
2385
  .option("--timeout <seconds>", "How long to wait for the apply to finish (default: 120)")
@@ -1750,7 +2400,8 @@ market
1750
2400
  market
1751
2401
  .command("update [slugs...]")
1752
2402
  .description("Check installed packages for drift against the catalog, and re-apply the ones that are stale (all with --yes, specific ones with explicit slugs)")
1753
- .option("--yes", "Apply every available update without prompting (no slugs required)")
2403
+ .option("--yes", "Apply every available update without prompting (no slugs required), agreeing to every data change it lists")
2404
+ .option("--accept-data-changes <opKeys>", "Agree to exactly these data changes (comma-separated opKeys from the preview)")
1754
2405
  .option("--dry-run", "Force the preview path — never apply, even with --yes")
1755
2406
  .option("--timeout <seconds>", "How long to wait for each package's apply to finish (default: 120)")
1756
2407
  .option("--json", "Output as JSON")
@@ -1775,10 +2426,113 @@ market
1775
2426
  const { marketInstalled } = await import("./commands/market.js");
1776
2427
  await marketInstalled(opts);
1777
2428
  });
2429
+ market
2430
+ .command("workspaces")
2431
+ .description("List your workspaces with their template-attachment status — find the one that isn't attached yet (pure read)")
2432
+ .option("--unattached", "Only show workspaces that need attention (unattached or missing a version stamp)")
2433
+ .option("--json", "Output as JSON")
2434
+ .action(async (_opts, cmd) => {
2435
+ // Same parent/child `--json` collision as `market install` — see its comment.
2436
+ const opts = cmd.optsWithGlobals();
2437
+ const { marketWorkspaces } = await import("./commands/market.js");
2438
+ await marketWorkspaces(opts);
2439
+ });
2440
+ market
2441
+ .command("attach <workspaceId>")
2442
+ .description("Reconcile a marketplace template onto an existing workspace + version-stamp it — how you relink a workspace installed before the marketplace")
2443
+ .option("--slug <slug>", "Template to attach (inferred from the workspace's existing attachment or domain when omitted)")
2444
+ .option("--project <id>", "Optionally tag the reconciled entities to a project")
2445
+ .option("--timeout <seconds>", "How long to wait for the apply to finish (default: 120)")
2446
+ .option("--json", "Output as JSON")
2447
+ .option("--pod-url <url>", "Pod URL override")
2448
+ .option("--api-key <key>", "API key override")
2449
+ .action(async (workspaceId, _opts, cmd) => {
2450
+ // Same parent/child `--json` collision as `market install` — see its comment.
2451
+ const opts = cmd.optsWithGlobals();
2452
+ const { marketAttach } = await import("./commands/market.js");
2453
+ await marketAttach(workspaceId, opts);
2454
+ });
2455
+ // ── `synap templates` — the ONE home for workspace ↔ template health ─────────
2456
+ // Consolidates `market update`/`workspaces`/`installed`: one grouped, action-
2457
+ // ranked view where an UNATTACHED workspace is visible (it wasn't in `update`),
2458
+ // with a suggested template and a one-word attach. The `market *` subcommands
2459
+ // still work — this is the front door, not a breaking rename.
2460
+ const templates = program
2461
+ .command("templates")
2462
+ .description("See every workspace's template status (attached, drifted, or unattached) and connect them — the one home for template health")
2463
+ .option("--needs-attention", "Show only workspaces that need action (drifted / unstamped / unattached-with-suggestion)")
2464
+ .option("--json", "Output as JSON")
2465
+ .option("--pod-url <url>", "Pod URL override")
2466
+ .option("--api-key <key>", "API key override")
2467
+ .action(async (_opts, cmd) => {
2468
+ const opts = cmd.optsWithGlobals();
2469
+ const { marketTemplates } = await import("./commands/market.js");
2470
+ await marketTemplates(opts);
2471
+ });
2472
+ templates
2473
+ .command("attach <workspace>")
2474
+ .description("Connect a workspace (by NAME or id) to a template — suggests the right one so you don't guess a slug")
2475
+ .option("--slug <slug>", "Force a specific template instead of the suggestion")
2476
+ .option("--yes", "Accept a confident (domain-match) suggestion without prompting — for scripts")
2477
+ .option("--project <id>", "Optionally tag the reconciled entities to a project")
2478
+ .option("--timeout <seconds>", "How long to wait for the apply to finish (default: 120)")
2479
+ .option("--json", "Output as JSON")
2480
+ .option("--pod-url <url>", "Pod URL override")
2481
+ .option("--api-key <key>", "API key override")
2482
+ .action(async (workspace, _opts, cmd) => {
2483
+ const opts = cmd.optsWithGlobals();
2484
+ const { templatesAttach } = await import("./commands/market.js");
2485
+ await templatesAttach(workspace, opts);
2486
+ });
2487
+ templates
2488
+ .command("update [slugs...]")
2489
+ .alias("updates")
2490
+ .description("Apply template updates — 'update all' (or --yes) for every drifted workspace, or name specific slugs. Bare 'update' previews what's available.")
2491
+ .option("--yes", "Apply every available update without prompting, agreeing to every data change it lists")
2492
+ .option("--accept-data-changes <opKeys>", "Agree to exactly these data changes (comma-separated opKeys from the preview)")
2493
+ .option("--dry-run", "Preview only — never apply")
2494
+ .option("--timeout <seconds>", "How long to wait for each apply (default: 120)")
2495
+ .option("--json", "Output as JSON")
2496
+ .option("--pod-url <url>", "Pod URL override")
2497
+ .option("--api-key <key>", "API key override")
2498
+ .action(async (slugs, _opts, cmd) => {
2499
+ const opts = cmd.optsWithGlobals();
2500
+ // `update all` reads more naturally than `--yes` and is what people type —
2501
+ // treat the literal word "all" as "apply every available update". It is NOT
2502
+ // `--yes`: data changes still need the person's agreement (asked, or flagged).
2503
+ const wantsAll = !!slugs?.some((s) => s.toLowerCase() === "all");
2504
+ const explicit = (slugs ?? []).filter((s) => s.toLowerCase() !== "all");
2505
+ const { marketUpdate } = await import("./commands/market.js");
2506
+ await marketUpdate(explicit.length > 0 ? explicit : undefined, wantsAll ? { ...opts, all: true } : opts);
2507
+ });
2508
+ templates
2509
+ .command("retire <target>")
2510
+ .description("Move a retired template's space into its replacement (by template slug, workspace name or id). Shows the plan, asks, then moves through the pod — never deletes.")
2511
+ .option("--dry-run", "Show the plan only — move nothing")
2512
+ .option("--yes", "Move without asking (for scripts)")
2513
+ .option("--json", "Output as JSON")
2514
+ .option("--pod-url <url>", "Pod URL override")
2515
+ .option("--api-key <key>", "API key override")
2516
+ .action(async (target, _opts, cmd) => {
2517
+ const opts = cmd.optsWithGlobals();
2518
+ const { templatesRetire } = await import("./commands/templates-retire.js");
2519
+ await templatesRetire(target, opts);
2520
+ });
1778
2521
  // ── Authoring loop: scaffold → validate → publish → unpublish ────────────────
1779
2522
  market
1780
2523
  .command("scaffold <slug>")
1781
2524
  .description("Write a minimal valid <slug>.template.yaml to edit in place (refuses to overwrite)")
2525
+ // This list MUST equal `SCAFFOLDABLE_KINDS` (src/lib/kind-package.ts). It is
2526
+ // written out rather than imported because index.ts keeps its eager module
2527
+ // graph to commander + one lib — importing kind-package.ts here would pull
2528
+ // `yaml` into the startup path of every `synap` invocation. What keeps the two
2529
+ // honest is `test/scaffold-kind-help-parity.test.ts`, which source-scans both.
2530
+ //
2531
+ // It advertised `skill` — which `marketScaffold` refuses, since the CP package
2532
+ // schema has no standalone slot for one — and omitted `workflow`, which
2533
+ // `market validate` has always accepted. A help string that offers a refused
2534
+ // option and hides a working one is the documented-loop defect class.
2535
+ .option("--kind <kind>", "Scaffold a standalone package instead of a workspace template: cell, view, workflow, capability, skill")
1782
2536
  .option("--json", "Output as JSON")
1783
2537
  .action(async (slug, _opts, cmd) => {
1784
2538
  // Same parent/child `--json` collision as `market install` — see its comment.
@@ -1797,19 +2551,44 @@ market
1797
2551
  await marketValidate(file, opts);
1798
2552
  });
1799
2553
  market
1800
- .command("publish [file]")
1801
- .description("Validate then publish a template to the marketplace (private by default) — pass a file, or --from-workspace <id>")
2554
+ .command("publish [targets...]")
2555
+ .description("Validate then publish a template (or a standalone cell/view package) to the marketplace (private by default) — pass a file, --from-workspace <id>, or --from-project <id>. Non-official publish never uses the synap-official vendor. Founder: --official [slug...] publishes the official catalog from the monorepo checkout (alias of `synap catalog publish --official --kind workspace`)")
2556
+ .option("--official", "Publish the OFFICIAL Synap catalog (all templates, or the named slugs) from the monorepo checkout — requires a `synap login` session that owns the synap-official vendor; previews and asks first")
2557
+ .option("--templates-dir <dir>", "With --official: path to the catalog package — synap-app/packages/catalog or workspace-templates (default: found by walking up from cwd)")
2558
+ .option("--dry-run", "With --official: preview what would change against the live catalog, publish nothing")
2559
+ .option("--yes", "With --official: skip the confirmation prompt")
1802
2560
  .option("--public", "Publish as PUBLIC (default is private)")
1803
2561
  .option("--private", "Publish as private (the default — stated explicitly)")
1804
2562
  .option("--from-workspace <id>", "Serialize a live workspace into a template and publish it (instead of a file)")
2563
+ .option("--from-project <id>", "Serialize a live project's used workspaces into a suite package (tags: suite, require deps) and publish it")
2564
+ .option("--price <usd>", "Stamp pricingModel=one_time + priceUsd (dollars→cents). Settlement/Stripe is not this wave — columns only.")
1805
2565
  .option("--json", "Output as JSON")
1806
- .option("--pod-url <url>", "Pod URL override (for --from-workspace)")
1807
- .option("--api-key <key>", "API key override (for --from-workspace)")
1808
- .action(async (file, _opts, cmd) => {
2566
+ .option("--pod-url <url>", "Pod URL override (for --from-workspace / --from-project)")
2567
+ .option("--api-key <key>", "API key override (for --from-workspace / --from-project)")
2568
+ // Default ON for a cell package's `codeFile` resolution — see `cell build
2569
+ // --bundle-deps`'s doc comment for why: a published cell's `deps` map is
2570
+ // otherwise resolved through esm.sh at RUNTIME, so the artifact reviewed at
2571
+ // publish time isn't what actually executes. `synap cell build` invoked
2572
+ // directly is unaffected — its own default stays external-deps-via-esm.sh.
2573
+ .option("--no-bundle-deps", "Publish a cell's codeFile with external deps via esm.sh instead of bundling them in (default: bundled)")
2574
+ .action(async (targets, _opts, cmd) => {
1809
2575
  // Same parent/child `--json` collision as `market install` — see its comment.
1810
2576
  const opts = cmd.optsWithGlobals();
2577
+ const args = targets ?? [];
2578
+ if (opts.official) {
2579
+ const { marketPublishOfficial } = await import("./commands/market-official.js");
2580
+ process.exitCode = await marketPublishOfficial(args, opts);
2581
+ return;
2582
+ }
2583
+ const { nonOfficialUsageError } = await import("./commands/market-official.js");
2584
+ const usage = nonOfficialUsageError(args, opts);
2585
+ if (usage) {
2586
+ console.error(` ✗ ${usage}`);
2587
+ process.exitCode = 2;
2588
+ return;
2589
+ }
1811
2590
  const { marketPublish } = await import("./commands/market-authoring.js");
1812
- await marketPublish(file, opts);
2591
+ await marketPublish(args[0], opts);
1813
2592
  });
1814
2593
  market
1815
2594
  .command("unpublish <slug>")
@@ -1821,6 +2600,35 @@ market
1821
2600
  const { marketUnpublish } = await import("./commands/market-authoring.js");
1822
2601
  await marketUnpublish(slug, opts);
1823
2602
  });
2603
+ // ─── catalog ──────────────────────────────────────────────────────────────────
2604
+ // The team's ONE door to the official catalog (every installable kind). Thin
2605
+ // wiring: `runCatalog` (commands/catalog.ts) calls the ONE catalog lib in the
2606
+ // monorepo checkout. `market publish --official` is its alias (kind workspace).
2607
+ const catalog = program
2608
+ .command("catalog")
2609
+ .description("Validate, diff and publish the OFFICIAL template catalog (workspaces, capabilities, skills, packs, automations) from the monorepo checkout");
2610
+ for (const verb of ["validate", "diff", "publish"]) {
2611
+ const sub = catalog
2612
+ .command(`${verb} [slugs...]`)
2613
+ .description(verb === "validate"
2614
+ ? "Validate the catalog (per-kind schema, cross-kind refs, goal grammar, duplicates) — offline"
2615
+ : verb === "diff"
2616
+ ? "Show what would change against the live control-plane catalog (new / changed / unchanged)"
2617
+ : "Validate, preview the diff, confirm, then publish to the control plane — founder only: --official + a `synap login` session that owns synap-official")
2618
+ .option("--kind <kind>", "Only this kind: workspace | capability | skill | pack | automation")
2619
+ .option("--catalog-dir <dir>", "Path to the catalog package (default: found by walking up from cwd)")
2620
+ .option("--json", "Output as JSON");
2621
+ if (verb === "publish") {
2622
+ sub
2623
+ .option("--official", "Publish as the synap-official vendor (required)")
2624
+ .option("--dry-run", "Preview the diff only — publish nothing")
2625
+ .option("--yes", "Skip the confirmation prompt");
2626
+ }
2627
+ sub.action(async (slugs, opts) => {
2628
+ const { runCatalog } = await import("./commands/catalog.js");
2629
+ process.exitCode = await runCatalog(verb, slugs ?? [], opts);
2630
+ });
2631
+ }
1824
2632
  // ─── capability ───────────────────────────────────────────────────────────────
1825
2633
  const capability = program
1826
2634
  .command("capability")
@@ -1873,6 +2681,7 @@ capability
1873
2681
  .command("connect [name]")
1874
2682
  .description("Connect a service — omit the name to pick from connectable services")
1875
2683
  .option("--workspace <id>", "Workspace context")
2684
+ .option("--reconnect", "Force a fresh sign-in even if a connection already exists — fixes an expired or revoked token")
1876
2685
  .action(async (name, opts) => {
1877
2686
  const { capabilityConnect } = await import("./commands/capability.js");
1878
2687
  await capabilityConnect(name, opts);
@@ -1969,13 +2778,38 @@ capConnections
1969
2778
  const { capabilityConnectionsRemove } = await import("./commands/cap-connections.js");
1970
2779
  await capabilityConnectionsRemove(capability, connectionId, opts);
1971
2780
  });
1972
- // ─── raycast ──────────────────────────────────────────────────────────────────
2781
+ capability
2782
+ .command("sync-status [provider]")
2783
+ .description("Show sync status per provider × kind × connection (phase, counts, errors) — omit provider to see all")
2784
+ .option("--workspace <id>", "Workspace context")
2785
+ .option("--json", "Output as JSON")
2786
+ .action(async (provider, opts) => {
2787
+ const { capabilitySyncStatus } = await import("./commands/sync-status.js");
2788
+ await capabilitySyncStatus(provider, opts);
2789
+ });
2790
+ // ─── sync (thin alias root for `cap sync-status`) ──────────────────────────────
2791
+ const sync = program
2792
+ .command("sync")
2793
+ .description("Sync status for connected providers — see `cap sync-status`");
2794
+ sync
2795
+ .command("status [provider]")
2796
+ .description("Show sync status per provider × kind × connection (phase, counts, errors) — omit provider to see all. Alias of `synap cap sync-status`.")
2797
+ .option("--workspace <id>", "Workspace context")
2798
+ .option("--json", "Output as JSON")
2799
+ .action(async (provider, opts) => {
2800
+ const { capabilitySyncStatus } = await import("./commands/sync-status.js");
2801
+ await capabilitySyncStatus(provider, opts);
2802
+ });
2803
+ // ─── raycast (hidden power-user escape) ───────────────────────────────────────
2804
+ // Prefer Raycast AI on the pod's MCP server (`synap connect raycast`), where
2805
+ // capability verbs run through synap_run_capability. Script Commands are a
2806
+ // per-verb escape hatch.
1973
2807
  const raycast = program
1974
- .command("raycast")
1975
- .description("Generate Raycast Script Commands from your enabled capabilities");
2808
+ .command("raycast", { hidden: true })
2809
+ .description("(hidden) Power-user escape: generate Raycast Script Commands per capability verb");
1976
2810
  raycast
1977
2811
  .command("generate")
1978
- .description("Emit a Raycast Script Command per enabled runnable verb")
2812
+ .description("Power-user escape: emit a Script Command per enabled verb (Raycast AI uses the pod's MCP server)")
1979
2813
  .option("--out <dir>", "Output directory (default: ~/.synap/raycast-commands)")
1980
2814
  .option("--workspace <id>", "Workspace context")
1981
2815
  .action(async (opts) => {
@@ -1985,7 +2819,7 @@ raycast
1985
2819
  // ─── providers ───────────────────────────────────────────────────────────────
1986
2820
  const providers = program
1987
2821
  .command("providers")
1988
- .description("Discover AI providers from the pod and configure local tools");
2822
+ .description("LLM providers on the pod (OpenAI/Anthropic/…) for local coding tools — not capability packs");
1989
2823
  providers
1990
2824
  .command("list", { isDefault: true })
1991
2825
  .description("Show enabled AI providers and their models")
@@ -1998,14 +2832,36 @@ providers
1998
2832
  });
1999
2833
  providers
2000
2834
  .command("pull")
2001
- .description("Write pod provider config to a local AI tool (opencode, aider)")
2835
+ .description("Write pod LLM credentials into opencode or aider, or print the OpenAI-compatible {pod}/v1 endpoint")
2002
2836
  .option("--target <name>", "Target tool: opencode | aider")
2003
2837
  .option("--pod-url <url>", "Pod URL override")
2004
2838
  .option("--api-key <key>", "API key override")
2839
+ .option("--show-key", "With --target show: print the full API key (default: redacted)")
2005
2840
  .action(async (opts) => {
2006
2841
  const { providersPull } = await import("./commands/providers.js");
2007
2842
  await providersPull(opts);
2008
2843
  });
2844
+ providers
2845
+ .command("set-key <providerId>")
2846
+ .description("Store your own API key override for a provider (BYOI)")
2847
+ .option("--workspace <id>", "Workspace ID for workspace-scoped override")
2848
+ .option("--api-key <key>", "API key (prompted if omitted)")
2849
+ .option("--pod-url <url>", "Pod URL override")
2850
+ .option("--hub-api-key <key>", "Hub API key override")
2851
+ .action(async (opts) => {
2852
+ const { providersSetKey } = await import("./commands/providers.js");
2853
+ await providersSetKey({ ...opts, providerId: opts.providerId });
2854
+ });
2855
+ providers
2856
+ .command("list-keys")
2857
+ .description("List user/workspace-level provider key overrides")
2858
+ .option("--workspace <id>", "Workspace ID to check")
2859
+ .option("--pod-url <url>", "Pod URL override")
2860
+ .option("--hub-api-key <key>", "Hub API key override")
2861
+ .action(async (opts) => {
2862
+ const { providersListKeys } = await import("./commands/providers.js");
2863
+ await providersListKeys(opts);
2864
+ });
2009
2865
  // ─── keys ────────────────────────────────────────────────────────────────────
2010
2866
  const keys = program
2011
2867
  .command("keys")
@@ -2013,9 +2869,10 @@ const keys = program
2013
2869
  keys
2014
2870
  .command("rotate")
2015
2871
  .description("Rotate your CLI key to pick up the latest scope set")
2016
- .action(async () => {
2872
+ .option("--show-key", "Print the new key in full if it could not be saved to the config")
2873
+ .action(async (opts) => {
2017
2874
  const { keysRotate } = await import("./commands/keys.js");
2018
- await keysRotate({});
2875
+ await keysRotate({ showKey: opts.showKey });
2019
2876
  });
2020
2877
  // Resolve a global `--pod <name>` before commander parses (it isn't registered
2021
2878
  // per-command). Fails fast with a clear message on an unknown profile name.