@synap-core/cli 1.10.0 → 1.12.2

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 (461) hide show
  1. package/README.md +23 -198
  2. package/dist/build-info.json +6 -0
  3. package/dist/commands/agent-run.js +3 -2
  4. package/dist/commands/agent-run.js.map +1 -1
  5. package/dist/commands/agents.d.ts +33 -17
  6. package/dist/commands/agents.js +203 -237
  7. package/dist/commands/agents.js.map +1 -1
  8. package/dist/commands/app.d.ts +143 -0
  9. package/dist/commands/app.js +523 -0
  10. package/dist/commands/app.js.map +1 -0
  11. package/dist/commands/automate.d.ts +21 -0
  12. package/dist/commands/automate.js +42 -0
  13. package/dist/commands/automate.js.map +1 -0
  14. package/dist/commands/automation-schema.test.d.ts +16 -0
  15. package/dist/commands/automation-schema.test.js +77 -0
  16. package/dist/commands/automation-schema.test.js.map +1 -0
  17. package/dist/commands/automation.d.ts +12 -0
  18. package/dist/commands/automation.js +80 -35
  19. package/dist/commands/automation.js.map +1 -1
  20. package/dist/commands/bridge-setup.d.ts +14 -1
  21. package/dist/commands/bridge-setup.js +392 -128
  22. package/dist/commands/bridge-setup.js.map +1 -1
  23. package/dist/commands/cap-connections.d.ts +4 -4
  24. package/dist/commands/cap-connections.js +14 -14
  25. package/dist/commands/cap-connections.js.map +1 -1
  26. package/dist/commands/capability.d.ts +2 -0
  27. package/dist/commands/capability.js +297 -176
  28. package/dist/commands/capability.js.map +1 -1
  29. package/dist/commands/catalog.d.ts +275 -0
  30. package/dist/commands/catalog.js +481 -0
  31. package/dist/commands/catalog.js.map +1 -0
  32. package/dist/commands/cell.d.ts +155 -8
  33. package/dist/commands/cell.js +416 -40
  34. package/dist/commands/cell.js.map +1 -1
  35. package/dist/commands/centrality.js +3 -14
  36. package/dist/commands/centrality.js.map +1 -1
  37. package/dist/commands/connect.d.ts +38 -13
  38. package/dist/commands/connect.js +68 -167
  39. package/dist/commands/connect.js.map +1 -1
  40. package/dist/commands/connections.d.ts +3 -2
  41. package/dist/commands/connections.js +95 -25
  42. package/dist/commands/connections.js.map +1 -1
  43. package/dist/commands/context.js +9 -2
  44. package/dist/commands/context.js.map +1 -1
  45. package/dist/commands/data-extra.d.ts +34 -0
  46. package/dist/commands/data-extra.js +198 -25
  47. package/dist/commands/data-extra.js.map +1 -1
  48. package/dist/commands/data.d.ts +120 -2
  49. package/dist/commands/data.js +307 -24
  50. package/dist/commands/data.js.map +1 -1
  51. package/dist/commands/diagnose.d.ts +66 -4
  52. package/dist/commands/diagnose.js +329 -29
  53. package/dist/commands/diagnose.js.map +1 -1
  54. package/dist/commands/digest.js +2 -1
  55. package/dist/commands/digest.js.map +1 -1
  56. package/dist/commands/doc.d.ts +25 -4
  57. package/dist/commands/doc.js +100 -18
  58. package/dist/commands/doc.js.map +1 -1
  59. package/dist/commands/doctor.d.ts +130 -0
  60. package/dist/commands/doctor.js +814 -0
  61. package/dist/commands/doctor.js.map +1 -0
  62. package/dist/commands/events.js +3 -1
  63. package/dist/commands/events.js.map +1 -1
  64. package/dist/commands/explain.js +5 -5
  65. package/dist/commands/explain.js.map +1 -1
  66. package/dist/commands/graph.js +5 -3
  67. package/dist/commands/graph.js.map +1 -1
  68. package/dist/commands/import.d.ts +368 -13
  69. package/dist/commands/import.js +1096 -55
  70. package/dist/commands/import.js.map +1 -1
  71. package/dist/commands/init.d.ts +20 -9
  72. package/dist/commands/init.js +433 -973
  73. package/dist/commands/init.js.map +1 -1
  74. package/dist/commands/keys.d.ts +1 -0
  75. package/dist/commands/keys.js +15 -2
  76. package/dist/commands/keys.js.map +1 -1
  77. package/dist/commands/knowledge.d.ts +28 -6
  78. package/dist/commands/knowledge.js +358 -73
  79. package/dist/commands/knowledge.js.map +1 -1
  80. package/dist/commands/launch.js +2 -2
  81. package/dist/commands/launch.js.map +1 -1
  82. package/dist/commands/lens.d.ts +37 -1
  83. package/dist/commands/lens.js +193 -72
  84. package/dist/commands/lens.js.map +1 -1
  85. package/dist/commands/market-authoring.d.ts +72 -0
  86. package/dist/commands/market-authoring.js +566 -0
  87. package/dist/commands/market-authoring.js.map +1 -0
  88. package/dist/commands/market-official.d.ts +58 -0
  89. package/dist/commands/market-official.js +84 -0
  90. package/dist/commands/market-official.js.map +1 -0
  91. package/dist/commands/market.d.ts +419 -20
  92. package/dist/commands/market.js +2124 -193
  93. package/dist/commands/market.js.map +1 -1
  94. package/dist/commands/mcp.d.ts +70 -13
  95. package/dist/commands/mcp.js +308 -40
  96. package/dist/commands/mcp.js.map +1 -1
  97. package/dist/commands/open.d.ts +39 -9
  98. package/dist/commands/open.js +80 -25
  99. package/dist/commands/open.js.map +1 -1
  100. package/dist/commands/playbook.d.ts +42 -0
  101. package/dist/commands/playbook.js +162 -0
  102. package/dist/commands/playbook.js.map +1 -0
  103. package/dist/commands/pod.js +1 -1
  104. package/dist/commands/pod.js.map +1 -1
  105. package/dist/commands/pods.d.ts +19 -0
  106. package/dist/commands/pods.js +220 -85
  107. package/dist/commands/pods.js.map +1 -1
  108. package/dist/commands/proposals-actions.d.ts +62 -0
  109. package/dist/commands/proposals-actions.js +146 -8
  110. package/dist/commands/proposals-actions.js.map +1 -1
  111. package/dist/commands/proposals-await.d.ts +68 -0
  112. package/dist/commands/proposals-await.js +248 -0
  113. package/dist/commands/proposals-await.js.map +1 -0
  114. package/dist/commands/proton-setup.d.ts +73 -0
  115. package/dist/commands/proton-setup.js +577 -0
  116. package/dist/commands/proton-setup.js.map +1 -0
  117. package/dist/commands/providers.d.ts +23 -0
  118. package/dist/commands/providers.js +126 -1
  119. package/dist/commands/providers.js.map +1 -1
  120. package/dist/commands/raycast.js +6 -2
  121. package/dist/commands/raycast.js.map +1 -1
  122. package/dist/commands/rule.d.ts +150 -0
  123. package/dist/commands/rule.js +447 -0
  124. package/dist/commands/rule.js.map +1 -0
  125. package/dist/commands/session-wait.d.ts +111 -0
  126. package/dist/commands/session-wait.js +209 -0
  127. package/dist/commands/session-wait.js.map +1 -0
  128. package/dist/commands/sessions.d.ts +52 -0
  129. package/dist/commands/sessions.js +479 -58
  130. package/dist/commands/sessions.js.map +1 -1
  131. package/dist/commands/skill-manage.d.ts +21 -0
  132. package/dist/commands/skill-manage.js +136 -19
  133. package/dist/commands/skill-manage.js.map +1 -1
  134. package/dist/commands/skills.js +19 -1
  135. package/dist/commands/skills.js.map +1 -1
  136. package/dist/commands/status.d.ts +1 -4
  137. package/dist/commands/status.js +5 -34
  138. package/dist/commands/status.js.map +1 -1
  139. package/dist/commands/statusline.backoff.test.d.ts +13 -0
  140. package/dist/commands/statusline.backoff.test.js +100 -0
  141. package/dist/commands/statusline.backoff.test.js.map +1 -0
  142. package/dist/commands/statusline.js +16 -9
  143. package/dist/commands/statusline.js.map +1 -1
  144. package/dist/commands/sync-status.d.ts +12 -0
  145. package/dist/commands/sync-status.js +125 -0
  146. package/dist/commands/sync-status.js.map +1 -0
  147. package/dist/commands/templates-retire.d.ts +34 -0
  148. package/dist/commands/templates-retire.js +192 -0
  149. package/dist/commands/templates-retire.js.map +1 -0
  150. package/dist/commands/tools.js +6 -21
  151. package/dist/commands/tools.js.map +1 -1
  152. package/dist/commands/tracks.d.ts +51 -0
  153. package/dist/commands/tracks.js +291 -0
  154. package/dist/commands/tracks.js.map +1 -0
  155. package/dist/commands/update.d.ts +3 -11
  156. package/dist/commands/update.js +5 -134
  157. package/dist/commands/update.js.map +1 -1
  158. package/dist/commands/upload.d.ts +68 -0
  159. package/dist/commands/upload.js +257 -0
  160. package/dist/commands/upload.js.map +1 -0
  161. package/dist/commands/vendor.d.ts +28 -0
  162. package/dist/commands/vendor.js +110 -0
  163. package/dist/commands/vendor.js.map +1 -0
  164. package/dist/commands/versions.js +6 -0
  165. package/dist/commands/versions.js.map +1 -1
  166. package/dist/commands/view.js +10 -5
  167. package/dist/commands/view.js.map +1 -1
  168. package/dist/commands/whoami.d.ts +23 -3
  169. package/dist/commands/whoami.js +169 -23
  170. package/dist/commands/whoami.js.map +1 -1
  171. package/dist/commands/worker.d.ts +122 -0
  172. package/dist/commands/worker.js +435 -0
  173. package/dist/commands/worker.js.map +1 -0
  174. package/dist/commands/worker.test.d.ts +1 -0
  175. package/dist/commands/worker.test.js +116 -0
  176. package/dist/commands/worker.test.js.map +1 -0
  177. package/dist/commands/workspace-ops.d.ts +35 -0
  178. package/dist/commands/workspace-ops.js +153 -0
  179. package/dist/commands/workspace-ops.js.map +1 -0
  180. package/dist/index.d.ts +6 -6
  181. package/dist/index.js +1009 -171
  182. package/dist/index.js.map +1 -1
  183. package/dist/lib/agent-connections.d.ts +31 -0
  184. package/dist/lib/agent-connections.js +33 -0
  185. package/dist/lib/agent-connections.js.map +1 -0
  186. package/dist/lib/agent-detect.d.ts +27 -0
  187. package/dist/lib/agent-detect.js +71 -0
  188. package/dist/lib/agent-detect.js.map +1 -0
  189. package/dist/lib/agent-door.d.ts +138 -0
  190. package/dist/lib/agent-door.js +426 -0
  191. package/dist/lib/agent-door.js.map +1 -0
  192. package/dist/lib/agents-config.d.ts +2 -0
  193. package/dist/lib/agents-config.js +4 -9
  194. package/dist/lib/agents-config.js.map +1 -1
  195. package/dist/lib/approval-poll.d.ts +31 -2
  196. package/dist/lib/approval-poll.js +20 -7
  197. package/dist/lib/approval-poll.js.map +1 -1
  198. package/dist/lib/atomic-file.d.ts +48 -0
  199. package/dist/lib/atomic-file.js +129 -0
  200. package/dist/lib/atomic-file.js.map +1 -0
  201. package/dist/lib/auth.d.ts +14 -9
  202. package/dist/lib/auth.js +44 -61
  203. package/dist/lib/auth.js.map +1 -1
  204. package/dist/lib/batch-approval.d.ts +85 -0
  205. package/dist/lib/batch-approval.js +170 -0
  206. package/dist/lib/batch-approval.js.map +1 -0
  207. package/dist/lib/capture-lane.d.ts +6 -5
  208. package/dist/lib/capture-lane.js +11 -8
  209. package/dist/lib/capture-lane.js.map +1 -1
  210. package/dist/lib/capture-structure.d.ts +342 -0
  211. package/dist/lib/capture-structure.js +295 -0
  212. package/dist/lib/capture-structure.js.map +1 -0
  213. package/dist/lib/catalog-templates.d.ts +59 -0
  214. package/dist/lib/catalog-templates.js +126 -0
  215. package/dist/lib/catalog-templates.js.map +1 -0
  216. package/dist/lib/cli-build.d.ts +19 -0
  217. package/dist/lib/cli-build.js +37 -0
  218. package/dist/lib/cli-build.js.map +1 -0
  219. package/dist/lib/conversation-headers.d.ts +18 -0
  220. package/dist/lib/conversation-headers.js +32 -0
  221. package/dist/lib/conversation-headers.js.map +1 -0
  222. package/dist/lib/cp-packages.d.ts +216 -4
  223. package/dist/lib/cp-packages.js +414 -11
  224. package/dist/lib/cp-packages.js.map +1 -1
  225. package/dist/lib/credential-class.d.ts +117 -0
  226. package/dist/lib/credential-class.js +116 -0
  227. package/dist/lib/credential-class.js.map +1 -0
  228. package/dist/lib/describe-lens.d.ts +35 -0
  229. package/dist/lib/describe-lens.js +40 -0
  230. package/dist/lib/describe-lens.js.map +1 -1
  231. package/dist/lib/device-connect.d.ts +125 -0
  232. package/dist/lib/device-connect.js +256 -0
  233. package/dist/lib/device-connect.js.map +1 -0
  234. package/dist/lib/directory-lens.d.ts +99 -0
  235. package/dist/lib/directory-lens.js +187 -0
  236. package/dist/lib/directory-lens.js.map +1 -0
  237. package/dist/lib/exporter-coverage.d.ts +71 -0
  238. package/dist/lib/exporter-coverage.js +97 -0
  239. package/dist/lib/exporter-coverage.js.map +1 -0
  240. package/dist/lib/hub-client.d.ts +43 -9
  241. package/dist/lib/hub-client.js +429 -65
  242. package/dist/lib/hub-client.js.map +1 -1
  243. package/dist/lib/id.d.ts +23 -0
  244. package/dist/lib/id.js +35 -0
  245. package/dist/lib/id.js.map +1 -0
  246. package/dist/lib/init-connect.d.ts +41 -0
  247. package/dist/lib/init-connect.js +75 -0
  248. package/dist/lib/init-connect.js.map +1 -0
  249. package/dist/lib/init-exit.d.ts +24 -0
  250. package/dist/lib/init-exit.js +41 -0
  251. package/dist/lib/init-exit.js.map +1 -0
  252. package/dist/lib/init-flow.d.ts +25 -0
  253. package/dist/lib/init-flow.js +40 -0
  254. package/dist/lib/init-flow.js.map +1 -0
  255. package/dist/lib/init-handoff.d.ts +16 -0
  256. package/dist/lib/init-handoff.js +19 -0
  257. package/dist/lib/init-handoff.js.map +1 -0
  258. package/dist/lib/init-output.d.ts +18 -0
  259. package/dist/lib/init-output.js +43 -0
  260. package/dist/lib/init-output.js.map +1 -0
  261. package/dist/lib/init-pod.d.ts +45 -0
  262. package/dist/lib/init-pod.js +36 -0
  263. package/dist/lib/init-pod.js.map +1 -0
  264. package/dist/lib/installed.d.ts +210 -11
  265. package/dist/lib/installed.js +234 -36
  266. package/dist/lib/installed.js.map +1 -1
  267. package/dist/lib/key-approval.d.ts +57 -0
  268. package/dist/lib/key-approval.js +117 -0
  269. package/dist/lib/key-approval.js.map +1 -0
  270. package/dist/lib/key-diagnosis.d.ts +56 -0
  271. package/dist/lib/key-diagnosis.js +227 -0
  272. package/dist/lib/key-diagnosis.js.map +1 -0
  273. package/dist/lib/key-doctor.d.ts +136 -0
  274. package/dist/lib/key-doctor.js +286 -0
  275. package/dist/lib/key-doctor.js.map +1 -0
  276. package/dist/lib/key-locations.d.ts +120 -0
  277. package/dist/lib/key-locations.js +326 -0
  278. package/dist/lib/key-locations.js.map +1 -0
  279. package/dist/lib/key-source.d.ts +66 -0
  280. package/dist/lib/key-source.js +106 -0
  281. package/dist/lib/key-source.js.map +1 -0
  282. package/dist/lib/key-state.d.ts +70 -0
  283. package/dist/lib/key-state.js +115 -0
  284. package/dist/lib/key-state.js.map +1 -0
  285. package/dist/lib/kind-package.d.ts +181 -0
  286. package/dist/lib/kind-package.js +679 -0
  287. package/dist/lib/kind-package.js.map +1 -0
  288. package/dist/lib/knowledge-title.d.ts +24 -0
  289. package/dist/lib/knowledge-title.js +46 -0
  290. package/dist/lib/knowledge-title.js.map +1 -0
  291. package/dist/lib/knowledge-title.test.d.ts +1 -0
  292. package/dist/lib/knowledge-title.test.js +34 -0
  293. package/dist/lib/knowledge-title.test.js.map +1 -0
  294. package/dist/lib/machine-instance.d.ts +64 -0
  295. package/dist/lib/machine-instance.js +142 -0
  296. package/dist/lib/machine-instance.js.map +1 -0
  297. package/dist/lib/mcp-bridge.d.ts +42 -0
  298. package/dist/lib/mcp-bridge.js +147 -0
  299. package/dist/lib/mcp-bridge.js.map +1 -0
  300. package/dist/lib/mcp-bridge.test.d.ts +1 -0
  301. package/dist/lib/mcp-bridge.test.js +90 -0
  302. package/dist/lib/mcp-bridge.test.js.map +1 -0
  303. package/dist/lib/mcp-call.d.ts +30 -0
  304. package/dist/lib/mcp-call.js +107 -0
  305. package/dist/lib/mcp-call.js.map +1 -0
  306. package/dist/lib/mcp-snippets.d.ts +53 -0
  307. package/dist/lib/mcp-snippets.js +82 -0
  308. package/dist/lib/mcp-snippets.js.map +1 -0
  309. package/dist/lib/next-steps.d.ts +8 -0
  310. package/dist/lib/next-steps.js +31 -1
  311. package/dist/lib/next-steps.js.map +1 -1
  312. package/dist/lib/package-wire.d.ts +179 -0
  313. package/dist/lib/package-wire.js +68 -0
  314. package/dist/lib/package-wire.js.map +1 -0
  315. package/dist/lib/pair-link.d.ts +38 -0
  316. package/dist/lib/pair-link.js +68 -0
  317. package/dist/lib/pair-link.js.map +1 -0
  318. package/dist/lib/pod-wide.d.ts +40 -0
  319. package/dist/lib/pod-wide.js +46 -0
  320. package/dist/lib/pod-wide.js.map +1 -0
  321. package/dist/lib/pod.d.ts +119 -115
  322. package/dist/lib/pod.js +206 -412
  323. package/dist/lib/pod.js.map +1 -1
  324. package/dist/lib/project-ref.d.ts +9 -3
  325. package/dist/lib/project-ref.js +10 -4
  326. package/dist/lib/project-ref.js.map +1 -1
  327. package/dist/lib/review-credential.d.ts +119 -0
  328. package/dist/lib/review-credential.js +147 -0
  329. package/dist/lib/review-credential.js.map +1 -0
  330. package/dist/lib/safe-open.d.ts +49 -0
  331. package/dist/lib/safe-open.js +119 -0
  332. package/dist/lib/safe-open.js.map +1 -0
  333. package/dist/lib/session-lens.d.ts +40 -1
  334. package/dist/lib/session-lens.js +54 -4
  335. package/dist/lib/session-lens.js.map +1 -1
  336. package/dist/lib/superwhisper-import.d.ts +8 -0
  337. package/dist/lib/superwhisper-import.js +14 -7
  338. package/dist/lib/superwhisper-import.js.map +1 -1
  339. package/dist/lib/superwhisper-title.test.d.ts +1 -0
  340. package/dist/lib/superwhisper-title.test.js +18 -0
  341. package/dist/lib/superwhisper-title.test.js.map +1 -0
  342. package/dist/lib/targets.d.ts +134 -10
  343. package/dist/lib/targets.js +962 -258
  344. package/dist/lib/targets.js.map +1 -1
  345. package/dist/lib/template-file.d.ts +78 -0
  346. package/dist/lib/template-file.js +140 -0
  347. package/dist/lib/template-file.js.map +1 -0
  348. package/dist/lib/template-lifecycle.d.ts +217 -0
  349. package/dist/lib/template-lifecycle.js +433 -0
  350. package/dist/lib/template-lifecycle.js.map +1 -0
  351. package/dist/lib/template-steps.d.ts +88 -0
  352. package/dist/lib/template-steps.js +118 -0
  353. package/dist/lib/template-steps.js.map +1 -0
  354. package/dist/lib/time-ago.d.ts +2 -0
  355. package/dist/lib/time-ago.js +15 -0
  356. package/dist/lib/time-ago.js.map +1 -0
  357. package/dist/utils/logger.js +5 -2
  358. package/dist/utils/logger.js.map +1 -1
  359. package/package.json +25 -18
  360. package/skills/README.md +12 -10
  361. package/skills/agent-os/SKILL.md +220 -99
  362. package/skills/manifest.json +2 -2
  363. package/skills/onboard/SKILL.md +61 -25
  364. package/skills/synap/README.md +5 -8
  365. package/skills/synap/SKILL.md +1185 -301
  366. package/skills/synap/_order.txt +7 -0
  367. package/skills/synap/_skill.yaml +1 -1
  368. package/skills/synap/automations.md +15 -0
  369. package/skills/synap/capture.md +54 -151
  370. package/skills/synap/cli-operations.md +24 -18
  371. package/skills/synap/common-mistakes.md +1 -0
  372. package/skills/synap/concepts.md +28 -0
  373. package/skills/synap/content-os.md +92 -0
  374. package/skills/synap/creative-loop.md +15 -10
  375. package/skills/synap/crm.md +21 -52
  376. package/skills/synap/digesting-a-channel.md +25 -0
  377. package/skills/synap/document-embeds.md +141 -47
  378. package/skills/synap/escalation-ladder.md +20 -10
  379. package/skills/synap/focus-sessions.md +47 -21
  380. package/skills/synap/from-intent.md +142 -0
  381. package/skills/synap/governance-rules.md +4 -4
  382. package/skills/synap/governance.md +10 -7
  383. package/skills/synap/hand-off.md +33 -0
  384. package/skills/synap/inline-patterns.md +15 -7
  385. package/skills/synap/lenses.md +33 -15
  386. package/skills/synap/linking.md +41 -40
  387. package/skills/synap/mcp-remote-servers.md +64 -0
  388. package/skills/synap/mental-model.md +24 -22
  389. package/skills/synap/multi-entity-capture.md +6 -4
  390. package/skills/synap/operating-mode-rules.md +1 -1
  391. package/skills/synap/processes.md +118 -0
  392. package/skills/synap/quick-reference.md +5 -1
  393. package/skills/synap/reading.md +3 -1
  394. package/skills/synap/reflexes.md +12 -42
  395. package/skills/synap/viewframe-cells.md +38 -5
  396. package/skills/synap/work-flow.md +1 -1
  397. package/skills/synap/work-model.md +96 -0
  398. package/skills/synap/worked-examples.md +21 -1
  399. package/skills/synap/workspace-design.md +14 -10
  400. package/skills/synap/writes.md +165 -35
  401. package/skills/synap/writing-titles.md +63 -0
  402. package/skills/synap-app/SKILL.md +319 -0
  403. package/skills/synap-app/_order.txt +5 -0
  404. package/skills/synap-app/_skill.yaml +20 -0
  405. package/skills/synap-app/creating-the-base-app.md +64 -0
  406. package/skills/synap-app/governance.md +42 -0
  407. package/skills/synap-app/install-commands.md +55 -0
  408. package/skills/synap-app/overview.md +50 -0
  409. package/skills/synap-app/using-the-sdk.md +73 -0
  410. package/skills/synap-design/SKILL.md +474 -0
  411. package/skills/synap-design/_order.txt +4 -0
  412. package/skills/synap-design/_skill.yaml +21 -0
  413. package/skills/synap-design/loop.md +134 -0
  414. package/skills/synap-design/overview.md +44 -0
  415. package/skills/synap-design/principles.md +130 -0
  416. package/skills/synap-design/system.md +133 -0
  417. package/skills/synap-market/SKILL.md +625 -0
  418. package/skills/synap-market/_order.txt +5 -0
  419. package/skills/synap-market/_skill.yaml +24 -0
  420. package/skills/synap-market/author-and-publish.md +212 -0
  421. package/skills/synap-market/discover-and-fetch.md +59 -0
  422. package/skills/synap-market/governance-and-catalog.md +63 -0
  423. package/skills/synap-market/install.md +133 -0
  424. package/skills/synap-market/overview.md +119 -0
  425. package/skills/synap-schema/SKILL.md +172 -10
  426. package/skills/synap-schema/_order.txt +2 -0
  427. package/skills/synap-schema/_skill.yaml +4 -2
  428. package/skills/synap-schema/common-mistakes.md +4 -2
  429. package/skills/synap-schema/discover-profiles.md +1 -1
  430. package/skills/synap-schema/entity-scope.md +15 -3
  431. package/skills/synap-schema/extend-first.md +29 -0
  432. package/skills/synap-schema/extend-vs-create.md +1 -1
  433. package/skills/synap-schema/intent-vocabulary-ssot.md +111 -0
  434. package/skills/synap-schema/property-types.md +1 -1
  435. package/skills/synap-schema/relations.md +1 -1
  436. package/skills/synap-ui/SKILL.md +19 -12
  437. package/skills/synap-ui/_skill.yaml +1 -1
  438. package/skills/synap-ui/ai-companion.md +1 -1
  439. package/skills/synap-ui/arranging-bento.md +2 -0
  440. package/skills/synap-ui/bento-layouts.md +10 -10
  441. package/skills/synap-ui/bento-recipes.md +313 -543
  442. package/skills/synap-ui/four-builds.md +5 -0
  443. package/skills/synap-ui/view-types.md +2 -2
  444. package/skills/synap-ui/widget-catalog.md +22 -278
  445. package/templates/automations/bookmark-enrichment.automation.json +290 -13
  446. package/templates/automations/url-bookmark-capture.automation.json +73 -11
  447. package/templates/skills/authoring-playbooks/SKILL.md +6 -3
  448. package/templates/skills/authoring-playbooks/reference/stages-contract.md +15 -0
  449. package/templates/skills/authoring-playbooks/reference/worked-example.md +3 -0
  450. package/dist/commands/infra.d.ts +0 -21
  451. package/dist/commands/infra.js +0 -262
  452. package/dist/commands/infra.js.map +0 -1
  453. package/dist/lib/browser-auth.d.ts +0 -35
  454. package/dist/lib/browser-auth.js +0 -170
  455. package/dist/lib/browser-auth.js.map +0 -1
  456. package/dist/lib/openclaw.d.ts +0 -32
  457. package/dist/lib/openclaw.js +0 -164
  458. package/dist/lib/openclaw.js.map +0 -1
  459. package/dist/lib/seed.d.ts +0 -13
  460. package/dist/lib/seed.js +0 -135
  461. package/dist/lib/seed.js.map +0 -1
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
+ .option("--with-mcp", "Raycast only: also install the full 56-tool MCP server via mcp-remote (names overlap @synap)")
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
@@ -124,6 +150,15 @@ pods
124
150
  const { podsUse } = await import("./commands/pods.js");
125
151
  await podsUse(name, { surface: opts.surface, json: opts.json });
126
152
  });
153
+ pods
154
+ .command("update <name> [url]")
155
+ .description("Change a saved pod's URL in place (verifies health + whether the key must be recreated)")
156
+ .option("--url <url>", "New pod URL (alternative to the positional arg)")
157
+ .option("--yes", "Non-interactive: skip prompts (won't recreate a rejected key)")
158
+ .action(async (name, url, opts) => {
159
+ const { podsUpdate } = await import("./commands/pods.js");
160
+ await podsUpdate(name, url, { url: opts.url, yes: opts.yes });
161
+ });
127
162
  pods
128
163
  .command("remove <name>")
129
164
  .alias("rm")
@@ -147,6 +182,16 @@ program
147
182
  const { status } = await import("./commands/status.js");
148
183
  await status({ json: opts.json });
149
184
  });
185
+ program
186
+ .command("doctor")
187
+ .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")
188
+ .option("--json", "Output as JSON")
189
+ .option("--fix", "Fix key problems: finish a pending approval, give this computer its own key, or rewrite stale copies — then verify over MCP")
190
+ .option("-v, --verbose", "Every check with its details, ids and addresses")
191
+ .action(async (opts) => {
192
+ const { doctor } = await import("./commands/doctor.js");
193
+ await doctor(opts);
194
+ });
150
195
  program
151
196
  .command("versions")
152
197
  .description("Show LOCAL vs NPM vs CONTROL-PLANE vs POD version drift for workspace-templates, api-types, and capabilities")
@@ -157,10 +202,11 @@ program
157
202
  });
158
203
  program
159
204
  .command("whoami")
160
- .description("Show the winning API key's identity/scopes/workspace and flag env-vs-surface key divergence")
161
- .action(async () => {
205
+ .description("Show key owner vs EFFECTIVE user (+ isAgent), scopes/workspace, and flag env-vs-surface key divergence")
206
+ .option("--json", "Output as JSON")
207
+ .action(async (opts) => {
162
208
  const { whoami } = await import("./commands/whoami.js");
163
- await whoami();
209
+ await whoami(opts);
164
210
  });
165
211
  // Hidden: compact ANSI line for the Claude Code statusLine (reads stdin JSON).
166
212
  program
@@ -173,59 +219,10 @@ program
173
219
  });
174
220
  program
175
221
  .command("update")
176
- .description("Update synap skill and check for CLI updates")
177
- .option("--server", "Also trigger a Dokploy redeploy of the control-plane")
178
- .option("--all", "Trigger a Dokploy redeploy of all registered services")
179
- .action(async (opts) => {
180
- const { update } = await import("./commands/update.js");
181
- await update(opts);
182
- });
183
- // ─── infra ────────────────────────────────────────────────────────────────────
184
- const infra = program
185
- .command("infra")
186
- .description("Manage servers and deployments via Dokploy");
187
- infra
188
- .command("status", { isDefault: true })
189
- .description("Overview: all servers and services status")
190
- .action(async () => {
191
- const { infraStatus } = await import("./commands/infra.js");
192
- await infraStatus();
193
- });
194
- infra
195
- .command("deploy <app>")
196
- .description("Trigger a redeploy for a named service")
197
- .action(async (app) => {
198
- const { infraDeploy } = await import("./commands/infra.js");
199
- await infraDeploy(app);
200
- });
201
- infra
202
- .command("logs <app>")
203
- .description("Tail logs for a service")
204
- .option("-n, --lines <n>", "Number of log lines", "100")
205
- .option("-f, --follow", "Follow log output")
206
- .action(async (app, opts) => {
207
- const { infraLogs } = await import("./commands/infra.js");
208
- await infraLogs(app, { lines: opts.lines ? parseInt(opts.lines, 10) : 100, follow: opts.follow });
209
- });
210
- infra
211
- .command("sync")
212
- .description("Pull Dokploy state into Synap entities (server + deployment profiles)")
213
- .option("--pod-url <url>", "Synap pod URL", process.env.SYNAP_POD_URL)
214
- .option("--api-key <key>", "Hub Protocol API key", process.env.SYNAP_API_KEY)
215
- .action(async (opts) => {
216
- if (!opts.podUrl || !opts.apiKey) {
217
- console.error(" --pod-url and --api-key are required (or set SYNAP_POD_URL / SYNAP_API_KEY)");
218
- process.exit(1);
219
- }
220
- const { infraSync } = await import("./commands/infra.js");
221
- await infraSync(opts.podUrl, opts.apiKey);
222
- });
223
- infra
224
- .command("open")
225
- .description("Open the Dokploy dashboard in your browser")
222
+ .description("Show how to update the CLI")
226
223
  .action(async () => {
227
- const { infraOpen } = await import("./commands/infra.js");
228
- infraOpen();
224
+ const { update } = await import("./commands/update.js");
225
+ await update();
229
226
  });
230
227
  program
231
228
  .command("switch [name]")
@@ -237,11 +234,31 @@ program
237
234
  program
238
235
  .command("connections")
239
236
  .alias("conn")
240
- .description("Show which pod each agent surface (Claude Code, Desktop, Cursor, Raycast) is connected to")
237
+ .description("Show everything connected: which pod each agent points at, and which tools the pod has connected")
241
238
  .action(async () => {
242
239
  const { connections } = await import("./commands/connections.js");
243
240
  await connections();
244
241
  });
242
+ // ─── app ────────────────────────────────────────────────────────────────────
243
+ // The App Connect flow: a `synap.app.json` in your app repo → one command wires
244
+ // the app's key into its deploy target. See APP-CONNECT-SPEC §5.
245
+ const app = program
246
+ .command("app")
247
+ .description("Connect an app in this repo to your pod (synap.app.json)");
248
+ app
249
+ .command("init")
250
+ .description("Write synap.app.json (asks name, pod, permissions) and register the app")
251
+ .action(async () => {
252
+ const { appInitCommand } = await import("./commands/app.js");
253
+ await appInitCommand();
254
+ });
255
+ app
256
+ .command("connect")
257
+ .description("Request the app's access, await approval, then write its key into the deploy target")
258
+ .action(async () => {
259
+ const { appConnectCommand } = await import("./commands/app.js");
260
+ await appConnectCommand();
261
+ });
245
262
  // ─── mcp ────────────────────────────────────────────────────────────────────
246
263
  // The MCP front door: point any AI client at the pod's /mcp server.
247
264
  const mcp = program
@@ -271,13 +288,67 @@ mcp
271
288
  const { mcpVerify } = await import("./commands/mcp.js");
272
289
  await mcpVerify(opts);
273
290
  });
291
+ mcp
292
+ .command("bridge")
293
+ .description("Stdio MCP server for Claude Code: forwards to the pod and names the conversation (spawned by Claude Code, not run by hand)")
294
+ .option("--url <url>", "The pod MCP URL to forward to")
295
+ .action(async (opts) => {
296
+ const { mcpBridge } = await import("./commands/mcp.js");
297
+ await mcpBridge(opts);
298
+ });
299
+ mcp
300
+ .command("use-bridge")
301
+ .description("Connect Claude Code through the stdio bridge, so work can be traced to and reopened in its conversation")
302
+ .option("--off", "Switch back to the direct http connection")
303
+ .action(async (opts) => {
304
+ const { mcpUseBridge } = await import("./commands/mcp.js");
305
+ await mcpUseBridge(opts);
306
+ });
274
307
  mcp
275
308
  .command("connect [client]")
276
- .description("Write the MCP config for a file-configurable client (Claude Code, Cursor, Desktop, …) — alias of `synap connect`")
309
+ .description("Write the MCP config for a file-configurable client (Claude Code, Cursor, Desktop, ChatGPT, …) — alias of `synap agents add <client>`")
277
310
  .option("--name <name>", "Pod profile to connect")
278
311
  .action(async (client, opts) => {
279
312
  const { connect } = await import("./commands/connect.js");
280
- await connect({ target: client, name: opts.name });
313
+ await connect(client, { name: opts.name });
314
+ });
315
+ mcp
316
+ .command("connect-chatgpt")
317
+ .description("Connect ChatGPT (web) to Synap — API-key connector to your pod")
318
+ .option("--name <name>", "Pod profile to connect")
319
+ .action(async (opts) => {
320
+ const { connect } = await import("./commands/connect.js");
321
+ await connect("chatgpt", { name: opts.name });
322
+ });
323
+ mcp
324
+ .command("connect-chatgpt-oauth")
325
+ .description("Connect ChatGPT (web) via Control Plane OAuth — multi-pod, no key to paste")
326
+ .option("--name <name>", "Pod profile to connect")
327
+ .action(async (opts) => {
328
+ const { connect } = await import("./commands/connect.js");
329
+ await connect("chatgpt-oauth", { name: opts.name });
330
+ });
331
+ mcp
332
+ .command("connect-claude")
333
+ .description("Print the claude.ai (web) OAuth connection steps — paste a URL, approve in the browser, done")
334
+ .option("--cp-url <url>", "Control-plane origin override (default: derived from the pod host)")
335
+ .option("--json", "Output as JSON")
336
+ .option("--pod-url <url>", "Pod URL override")
337
+ .option("--api-key <key>", "API key override")
338
+ .action(async (opts) => {
339
+ const { mcpConnectClaudeWeb } = await import("./commands/mcp.js");
340
+ await mcpConnectClaudeWeb(opts);
341
+ });
342
+ mcp
343
+ .command("ui-setup <client>")
344
+ .description("Print the exact form fields to fill in the client's MCP/Connector UI (ChatGPT, Codex, Cursor, etc.)")
345
+ .option("--name <name>", "Pod profile to use (default: active)")
346
+ .option("--workspace <id>", "Scope to a specific workspace")
347
+ .option("--project <id>", "Focus the agent on a project")
348
+ .option("--json", "Output as JSON")
349
+ .action(async (client, opts) => {
350
+ const { mcpUiSetup } = await import("./commands/mcp.js");
351
+ await mcpUiSetup(client, opts);
281
352
  });
282
353
  program
283
354
  .command("login")
@@ -295,11 +366,9 @@ program
295
366
  }
296
367
  const profiles = listPodProfiles();
297
368
  if (profiles.length === 0) {
298
- // No pod saved — guide to pods add
299
- console.log(chalk.dim(" No pod configured yet."));
300
- console.log(chalk.blue(" Adding a new pod...\n"));
301
- const { podsAdd } = await import("./commands/pods.js");
302
- await podsAdd();
369
+ // No pod saved: sign in to the account first, it finds the pods.
370
+ const { podsFirstLogin } = await import("./commands/pods.js");
371
+ await podsFirstLogin();
303
372
  return;
304
373
  }
305
374
  // Already have pods — show status and offer reconnect if any are unreachable
@@ -338,8 +407,23 @@ program
338
407
  console.log(` ${active}${chalk.bold(p.name.padEnd(14))} ${status} ${chalk.dim(p.config.podUrl)}`);
339
408
  }
340
409
  console.log();
410
+ // A computer key (device flow) that expires within 7 days, or has: connect
411
+ // this computer again now — the pod offers the same computer, so its
412
+ // agents, history and rules are kept. Never without a person to approve.
413
+ const { computerKeyNeedsRenewal, connectThisComputer } = await import("./lib/device-connect.js");
414
+ const renewed = new Set();
415
+ for (const p of checks.filter((c) => c.healthy && computerKeyNeedsRenewal(c.config))) {
416
+ const when = `this computer's key ${p.keyValid ? "expires" : "expired"} ${p.config.keyExpiresAt}`;
417
+ if (!process.stdin.isTTY) {
418
+ console.log(chalk.yellow(` ${p.name}: ${when}. Run synap login in a terminal to renew it.`));
419
+ continue;
420
+ }
421
+ console.log(chalk.yellow(` ${p.name}: ${when}. Renewing it.`));
422
+ if (await connectThisComputer(p.config.podUrl, { name: p.name }))
423
+ renewed.add(p.name);
424
+ }
341
425
  // Anything that won't authenticate — unreachable OR a revoked/expired key.
342
- const needsReconnect = checks.filter((p) => !p.healthy || !p.keyValid);
426
+ const needsReconnect = checks.filter((p) => !renewed.has(p.name) && (!p.healthy || !p.keyValid));
343
427
  if (needsReconnect.length > 0) {
344
428
  console.log(chalk.yellow(` ${needsReconnect.length} pod(s) need re-authentication. Refresh credentials:`));
345
429
  for (const p of needsReconnect) {
@@ -396,6 +480,7 @@ program
396
480
  .description("Show a lightweight lens map: who you are, your projects (companies/initiatives), and workspaces (operational domains)")
397
481
  .option("--json", "Output as JSON")
398
482
  .option("--details", "Include per-workspace profiles + entity counts (heavier)")
483
+ .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")
399
484
  .option("--pod-url <url>", "Pod URL override")
400
485
  .option("--api-key <key>", "API key override")
401
486
  .action(async (opts) => {
@@ -421,9 +506,9 @@ program
421
506
  // See what an AI did across flows: the unified run feed, or one run's activity
422
507
  // timeline (for a capture: WHY a facet/entity dropped, with a fixHint).
423
508
  program
424
- .command("diagnose [runId]")
425
- .description("See what an AI did — the run feed, or one run's activity (why it did what it did)")
426
- .option("--flow <automation|playbook|capture|session>", "Restrict the feed, or the run's flow (default capture for a runId)")
509
+ .command("diagnose [run]")
510
+ .description("See what an AI did — the run feed, or one run's nodes (by id or flow name)")
511
+ .option("--flow <automation|playbook|capture|capability|session|chat>", "Restrict the feed, or narrow the run lookup to one flow")
427
512
  .option("--flow-id <id>", "Restrict the feed to one flow's runs (automationId / playbookId)")
428
513
  .option("--limit <n>", "Max runs in the feed")
429
514
  .option("--json", "Output raw JSON")
@@ -436,7 +521,9 @@ program
436
521
  // ─── use ──────────────────────────────────────────────────────────────────────
437
522
  program
438
523
  .command("use <workspace>")
439
- .description("Set the active workspace (by ID or name) — persisted to ~/.synap/config.json")
524
+ .description("Set the active workspace (by ID or name) — short form of `synap workspace use`. Default scope: this directory")
525
+ .option("--session", "Ephemeral: scope only this Claude Code session, don't persist")
526
+ .option("--global", "Durable machine-wide default (~/.synap/config.json) instead of this directory's .synap/lens.json")
440
527
  .option("--json", "Output as JSON")
441
528
  .option("--pod-url <url>", "Pod URL override")
442
529
  .option("--api-key <key>", "API key override")
@@ -447,7 +534,7 @@ program
447
534
  // ─── project / lens (per-Claude-session scoping) ──────────────────────────────
448
535
  const project = program
449
536
  .command("project")
450
- .description("Manage + focus the cross-cutting project lens (a company/initiative): list, new, use, clear");
537
+ .description("Manage + focus the project lens (a commitment, spanning workspaces): list, new, use, clear");
451
538
  project
452
539
  .command("list", { isDefault: true })
453
540
  .description("List projects on the active pod, with the pinned one marked (bare `synap project` runs this)")
@@ -460,7 +547,7 @@ project
460
547
  });
461
548
  project
462
549
  .command("new <name>")
463
- .description("Create a project (a company/initiative) on the active pod, then guide you to pin + add to it")
550
+ .description("Create a project (a commitment you are driving, with whom) on the active pod, then guide you to pin + add to it")
464
551
  .option("--description <text>", "Optional one-line description")
465
552
  .option("--json", "Output as JSON (incl. nextSteps for agents)")
466
553
  .option("--pod-url <url>", "Pod URL override")
@@ -473,6 +560,7 @@ project
473
560
  .command("use <ref>")
474
561
  .description("Pin the active project (durable, like `synap use <workspace>`). Ref = uuid, slug, or <pod>/<slug> — cross-pod refs switch the active pod too")
475
562
  .option("--session", "Ephemeral: scope only this Claude Code session, don't persist (cross-pod refs refused)")
563
+ .option("--global", "Durable machine-wide default (~/.synap/config.json) instead of this directory's .synap/lens.json")
476
564
  .option("--json", "Output as JSON")
477
565
  .option("--pod-url <url>", "Pod URL override")
478
566
  .option("--api-key <key>", "API key override")
@@ -503,8 +591,10 @@ project
503
591
  });
504
592
  program
505
593
  .command("lens")
506
- .description("Show this Claude session's lens (workspace + project + focus session)")
594
+ .description("Show the effective lens (workspace + project + focus session) and WHICH rung set each one")
507
595
  .option("--json", "Output as JSON")
596
+ .option("--pod-url <url>", "Pod URL override")
597
+ .option("--api-key <key>", "API key override")
508
598
  .action(async (opts) => {
509
599
  const { showLens } = await import("./commands/lens.js");
510
600
  await showLens(opts);
@@ -514,11 +604,12 @@ program
514
604
  // procedural (how-to docs), episodic (captures) — and returns a glass-box answer.
515
605
  program
516
606
  .command("ask <query>")
517
- .description("Ask your pod anything — routes to the right knowledge substrate(s) and shows which answered")
607
+ .description("Ask your pod anything — returns a synthesized answer with its sources")
518
608
  .option("--workspace <id>", "Scope to a specific workspace (omit for pod-wide)")
519
609
  .option("--limit <n>", "Max results per substrate", "10")
520
610
  .option("--json", "Output as JSON")
521
611
  .option("--session", "Scope retrieval to the active session context")
612
+ .option("--raw", "Skip synthesis: show the raw per-substrate matches instead")
522
613
  .option("--compare", "A/B diagnostic: show baseline vs Horizon rankers side-by-side (read-only, does not change the answer)")
523
614
  .option("--pod-url <url>", "Pod URL override")
524
615
  .option("--api-key <key>", "API key override")
@@ -527,21 +618,26 @@ program
527
618
  await askKnowledge(query, opts);
528
619
  });
529
620
  // ─── capture ──────────────────────────────────────────────────────────────────
530
- // Uses the active pod + workspace from config (set via `synap pods use` /
531
- // `synap use` / `synap workspace provision-agent`). No auth flags needed.
621
+ // Uses the active pod from config. Work-lane knowledge does NOT default-pin
622
+ // the active workspace (omit workspaceId → server-derived / pod-wide preferred);
623
+ // pin only with --workspace or --team.
532
624
  program
533
625
  .command("capture [text]")
534
- .description("Capture knowledge: smart `capture \"<free text>\"` (AI structures it) OR typed `capture --type gotcha --claim \"…\"`")
535
- .option("--type <type>", "Typed mode: gotcha|lesson|decision|reference")
626
+ .description("Capture knowledge: smart `capture \"<free text>\"` (AI structures it). For canonical typed Knowledge, use `create entity --profile=knowledge --props='{\"knowledgeForm\":\"insight|caution\"}'`.")
627
+ .option("--type <type>", "Legacy compatibility typed mode (gotcha|lesson|decision|reference); new automation should use knowledgeForm")
536
628
  .option("--claim <text>", "Typed mode: one-line assertion (required with --type)")
629
+ .option("--title <text>", "Typed mode: short name, 80 characters or less (default: the claim's first sentence, never cut)")
630
+ .option("--content <markdown>", "Typed mode: full Markdown explanation, stored as the linked Knowledge note")
537
631
  .option("--why <text>", "Reasoning or context")
538
632
  .option("--evidence <text>", "Supporting file path, URL, or code snippet")
539
633
  .option("--tags <csv>", "Comma-separated tags e.g. repo:synap-backend,layer:migrations")
540
634
  .option("--session", "Link captured knowledge to the active session")
541
- .option("--global", "GLOBAL lane: pod-wide cross-cutting runbook (→ knowledge_keys), visible in every workspace")
542
- .option("--key <ns:slug>", "Stable key for a --global runbook (derived from --type + --claim if omitted)")
543
- .option("--team", "Write to the first product workspace instead of the active workspace")
544
- .option("--workspace <id>", "Explicit workspace override")
635
+ .option("--pod-wide", "GLOBAL lane: pod-wide cross-cutting runbook (→ knowledge_keys), visible in every workspace")
636
+ .option("--global", "Alias for --pod-wide")
637
+ .option("--key <ns:slug>", "Stable key for a --pod-wide runbook (derived from --type + --claim if omitted)")
638
+ .option("--team", "Pin to the first product workspace (default: omit workspaceId — server-derived / pod-wide preferred for knowledge)")
639
+ .option("--workspace <id>", "Explicit workspace pin (otherwise workspaceId is omitted for knowledge)")
640
+ .option("--project <id|name>", "Pin the capture to a project (id or name) — overrides SYNAP_PROJECT_ID / the session lens")
545
641
  .option("--yes", "Skip the y/N confirmation prompt (smart mode)")
546
642
  .option("--json", "Output as JSON")
547
643
  .action(async (text, opts) => {
@@ -553,10 +649,14 @@ program
553
649
  // the SAME AI capture pipeline (/capture/structure → /capture/execute), so it
554
650
  // inherits entity extraction, the relation graph, and workspace/project routing.
555
651
  program
556
- .command("import <inputs...>")
652
+ .command("import [inputs...]")
557
653
  .description("Import files, folders, or URLs into the pod (AI capture pipeline, or Superwhisper store-first)")
558
654
  .option("--workspace <id|name>", "Override AI workspace routing")
559
655
  .option("--project <id|name>", "Override AI project routing")
656
+ .option("--home-map <pairs>", "Multi-home path map: pathSubstring=workspaceNameOrId pairs " +
657
+ '(e.g. "Projects=Builder,Posts=Content OS"). Matched batch paths are ' +
658
+ "rewritten with the workspace name as the first segment so placement " +
659
+ "heuristics pin the right home. No product defaults.")
560
660
  .option("--dry-run", "Preview only — do not write")
561
661
  .option("--yes", "Skip confirmation / auto-confirm store-first")
562
662
  .option("--session", "Link created entities to the active session")
@@ -565,14 +665,49 @@ program
565
665
  .option("--api-key <key>", "API key override")
566
666
  .option("--source <name>", "Adapter: superwhisper (paired meta.json+output.wav store-first)")
567
667
  .option("--store-first", "Skip AI structure; store units as pod-wide notes (+ optional audio)")
668
+ .option("--keep-raw", "Keep the original file and link it to the created entity (default: extract, then discard the bytes)")
568
669
  .option("--with-audio", "Upload WAV provenance (default on for --source superwhisper --store-first)")
569
670
  .option("--no-with-audio", "Transcript-only (no WAV upload)")
570
671
  .option("--limit <n>", "Max Superwhisper units to process", (v) => parseInt(v, 10))
571
672
  .option("--concurrency <n>", "Parallel unit uploads (1–4)", (v) => parseInt(v, 10))
572
673
  .option("--no-resume", "Ignore local import ledger (re-import all)")
674
+ .option("--job-status <jobId>", "Re-poll a background corpus import started by an earlier run (no waiting)")
573
675
  .action(async (inputs, opts) => {
574
- const { importData } = await import("./commands/import.js");
575
- await importData(inputs, opts);
676
+ const mod = await import("./commands/import.js");
677
+ if (opts.jobStatus) {
678
+ await mod.importJobStatus(opts.jobStatus, opts);
679
+ return;
680
+ }
681
+ await mod.importData(inputs ?? [], opts);
682
+ });
683
+ // ─── upload ───────────────────────────────────────────────────────────────────
684
+ // Store a real file (arbitrary bytes) on the pod — the door an AI/CLI needs to
685
+ // persist an actual file, not just text. POSTs multipart to /api/hub/files,
686
+ // which mints a `file` entity for the bytes.
687
+ program
688
+ .command("upload <path>")
689
+ .description("Upload a file to the pod as an entity (>10MB goes direct to storage: video ≤500MB, audio ≤100MB, zip ≤200MB)")
690
+ .option("--workspace <id>", "Pin the entity to this workspace (explicit placement)")
691
+ .option("--profile <slug>", "Entity kind to create (default: file), e.g. brand-asset")
692
+ .option("--prop <key=value>", "Entity property (repeatable)", (v, acc = []) => [...acc, v])
693
+ .option("--attach <entityId>", "After upload, link the document to this entity (references relation)")
694
+ .option("--title <title>", "Title for the created document")
695
+ .option("--open", "Open in the Synap desktop app after upload")
696
+ .option("--json", "Output as JSON")
697
+ .option("--pod-url <url>", "Pod URL override")
698
+ .option("--api-key <key>", "API key override")
699
+ .addHelpText("after", `
700
+ Examples:
701
+ synap upload ./report.pdf
702
+ synap upload ./logo.png --title "Brand logo" --workspace <id>
703
+ synap upload ./spec.pdf --attach <entityId>
704
+ synap upload ./logo.svg --profile brand-asset --prop variant=dark --workspace <id>
705
+ synap upload ./launch.mp4 --workspace <id>
706
+ synap upload ./report.pdf --open
707
+ `)
708
+ .action(async (path, opts) => {
709
+ const { uploadFile } = await import("./commands/upload.js");
710
+ await uploadFile(path, opts);
576
711
  });
577
712
  program
578
713
  .command("subscribe")
@@ -628,7 +763,42 @@ program
628
763
  // commands (capture, recall, search, …) inherit the active pod + workspace.
629
764
  const workspace = program
630
765
  .command("workspace")
631
- .description("Set up and switch workspace context (run once — persisted to ~/.synap/config.json)");
766
+ .description("Manage + focus the workspace lens (an operational domain): list, use, clear");
767
+ // `workspace use|list|clear` mirror `project use|list|clear` so the two
768
+ // composable lenses read as siblings. ADDITIVE: the original short form
769
+ // `synap use <workspace>` and `synap list workspaces` are untouched and keep
770
+ // working exactly as before — these are aliases, not replacements.
771
+ workspace
772
+ .command("use <workspace>")
773
+ .description("Pin the active workspace by ID or name (durable, like `synap use <workspace>`). Default scope: this directory")
774
+ .option("--session", "Ephemeral: scope only this Claude Code session, don't persist")
775
+ .option("--global", "Durable machine-wide default (~/.synap/config.json) instead of this directory's .synap/lens.json")
776
+ .option("--json", "Output as JSON")
777
+ .option("--pod-url <url>", "Pod URL override")
778
+ .option("--api-key <key>", "API key override")
779
+ .action(async (ws, opts) => {
780
+ const { useWorkspace } = await import("./commands/data.js");
781
+ await useWorkspace(ws, opts);
782
+ });
783
+ workspace
784
+ .command("list", { isDefault: true })
785
+ .description("List workspaces you belong to (bare `synap workspace` runs this)")
786
+ .option("--json", "Output as JSON")
787
+ .option("--pod-url <url>", "Pod URL override")
788
+ .option("--api-key <key>", "API key override")
789
+ .action(async (opts) => {
790
+ const { listWorkspaces } = await import("./commands/data.js");
791
+ await listWorkspaces(opts);
792
+ });
793
+ workspace
794
+ .command("clear")
795
+ .description("Clear the active workspace focus (durable by default)")
796
+ .option("--session", "Ephemeral: clear only this Claude Code session's workspace")
797
+ .option("--json", "Output as JSON")
798
+ .action(async (opts) => {
799
+ const { clearWorkspace } = await import("./commands/data.js");
800
+ await clearWorkspace(opts);
801
+ });
632
802
  workspace
633
803
  .command("provision-agent")
634
804
  .description("Provision an agent-owned workspace and set it as active (idempotent)")
@@ -652,6 +822,54 @@ workspace
652
822
  const { workspacePurge } = await import("./commands/purge.js");
653
823
  await workspacePurge(target, opts);
654
824
  });
825
+ // R8a — governed lifecycle: an agent key gets a proposal, an owner key applies.
826
+ workspace
827
+ .command("archive <workspace>")
828
+ .description("Archive a workspace (hides it, pauses its workspace automations). Governed: agents propose")
829
+ .option("--reason <text>", "Why — shown to the reviewer")
830
+ .option("--json", "Output as JSON")
831
+ .option("--pod-url <url>", "Pod URL override")
832
+ .option("--api-key <key>", "API key override")
833
+ .action(async (target, opts) => {
834
+ const { workspaceArchive } = await import("./commands/workspace-ops.js");
835
+ await workspaceArchive(target, opts);
836
+ });
837
+ workspace
838
+ .command("restore <workspace>")
839
+ .description("Restore an archived workspace (by id). Re-enables nothing; lists automations still paused")
840
+ .option("--reason <text>", "Why — shown to the reviewer")
841
+ .option("--json", "Output as JSON")
842
+ .option("--pod-url <url>", "Pod URL override")
843
+ .option("--api-key <key>", "API key override")
844
+ .action(async (target, opts) => {
845
+ const { workspaceRestore } = await import("./commands/workspace-ops.js");
846
+ await workspaceRestore(target, opts);
847
+ });
848
+ workspace
849
+ .command("rename <workspace> <newName>")
850
+ .description("Rename a workspace. Governed: agents propose")
851
+ .option("--json", "Output as JSON")
852
+ .option("--pod-url <url>", "Pod URL override")
853
+ .option("--api-key <key>", "API key override")
854
+ .action(async (target, newName, opts) => {
855
+ const { workspaceRename } = await import("./commands/workspace-ops.js");
856
+ await workspaceRename(target, newName, opts);
857
+ });
858
+ // R8a — `synap entity move`. (`get/create/set entity` stay under their verbs.)
859
+ program
860
+ .command("entity")
861
+ .description("Entity operations: move")
862
+ .command("move <entityIds...>")
863
+ .description("Move entities into another workspace. Governed per entity: moved / proposed / errors")
864
+ .requiredOption("--to <workspace>", "Destination workspace id or name")
865
+ .option("--reason <text>", "Why they belong there — shown to the reviewer")
866
+ .option("--json", "Output as JSON")
867
+ .option("--pod-url <url>", "Pod URL override")
868
+ .option("--api-key <key>", "API key override")
869
+ .action(async (entityIds, opts) => {
870
+ const { entityMove } = await import("./commands/workspace-ops.js");
871
+ await entityMove(entityIds, opts);
872
+ });
655
873
  // ─── list ─────────────────────────────────────────────────────────────────────
656
874
  const list = program
657
875
  .command("list")
@@ -765,8 +983,10 @@ const proposals = program
765
983
  .description("Review and act on governance proposals");
766
984
  proposals
767
985
  .command("list", { isDefault: true })
768
- .description("List pending governance proposals for this agent")
986
+ .description("List governance proposals (default: the pending queue)")
987
+ .option("--status <status>", "pending (default) | approved | auto_approved (what the agent already did) | rejected | expired (never decided) | all", "pending")
769
988
  .option("--workspace <id>", "Scope to a specific workspace")
989
+ .option("--session <id>", "Scope to one focus session (the agent run that filed them)")
770
990
  .option("--limit <n>", "Max results", "20")
771
991
  .option("--json", "Output as JSON")
772
992
  .option("--pod-url <url>", "Pod URL override")
@@ -777,8 +997,11 @@ proposals
777
997
  });
778
998
  proposals
779
999
  .command("approve <id>")
780
- .description("Approve a pending proposal")
1000
+ .description("Approve a pending proposal (interactive only — requires a TTY and a typed confirmation)")
781
1001
  .option("--reason <text>", "Optional approval note")
1002
+ .option("--verb <verb>", "Same-name choice: fill_empty | keep_existing | use_capture | separate")
1003
+ .option("--existing <uuid>", "Existing record id — required for fill_empty, keep_existing, and use_capture")
1004
+ .option("--yes", "Accepted for symmetry with other commands, but does NOT skip the typed confirmation — approval is always deliberate")
782
1005
  .option("--json", "Output as JSON")
783
1006
  .option("--pod-url <url>", "Pod URL override")
784
1007
  .option("--api-key <key>", "API key override")
@@ -797,6 +1020,39 @@ proposals
797
1020
  const { rejectProposal } = await import("./commands/proposals-actions.js");
798
1021
  await rejectProposal(id, opts);
799
1022
  });
1023
+ proposals
1024
+ .command("await <id>")
1025
+ .description("Block until a proposal is decided. Exit 0 approved · 1 rejected/expired/withdrawn · 2 timeout")
1026
+ .option("--timeout <duration>", "Give up after this long (e.g. 30m, 2h)", "30m")
1027
+ .option("--interval <duration>", "Delay between checks (e.g. 10s)", "10s")
1028
+ .option("--json", "Output the final status as JSON")
1029
+ .option("--pod-url <url>", "Pod URL override")
1030
+ .option("--api-key <key>", "API key override")
1031
+ .action(async (id, opts) => {
1032
+ const { awaitProposalCommand } = await import("./commands/proposals-await.js");
1033
+ await awaitProposalCommand(id, opts);
1034
+ });
1035
+ // ─── worker ───────────────────────────────────────────────────────────────────
1036
+ // Run Claude Code unattended on THIS machine for the sessions handed to an agent
1037
+ // (the session roster → orient handedToYou). See src/commands/worker.ts.
1038
+ program
1039
+ .command("worker")
1040
+ .description("Run Claude Code unattended here for the sessions handed to an agent")
1041
+ .requiredOption("--agent <name>", "The named agent this machine works as (its key comes from ~/.synap)")
1042
+ .option("--interval <dur>", "How often to check for handed sessions (e.g. 30s, 2m)", "30s")
1043
+ .option("--once", "Check once, run what is handed, wait for it, exit")
1044
+ .option("--cwd <dir>", "Where the launched Claude Code works (default ~/Documents/Code/synap)")
1045
+ .option("--max-parallel <n>", "Runs at the same time", "1")
1046
+ .option("--claude-bin <path>", "The claude binary", "claude")
1047
+ .option("--claude-config-dir <dir>", "CLAUDE_CONFIG_DIR for launched runs — picks which Claude subscription they use")
1048
+ .option("--mcp-server <name>", "Claude Code MCP server name for this pod (detected from ~/.claude.json by default)")
1049
+ .option("--allowed-tools <list>", "Comma-separated Claude tools allowed without asking (the pod MCP server is always added)")
1050
+ .option("--skip-permissions", "Launch with --dangerously-skip-permissions instead of an allow-list")
1051
+ .option("--dry-run", "Claim and log what would launch, without launching Claude")
1052
+ .action(async (opts) => {
1053
+ const { workerCommand } = await import("./commands/worker.js");
1054
+ await workerCommand(opts);
1055
+ });
800
1056
  // ─── session ──────────────────────────────────────────────────────────────────
801
1057
  // Focus sessions — goal-bound work rooms tracked on the pod.
802
1058
  // AI agents: use start_session to begin, update_session to report progress,
@@ -810,7 +1066,14 @@ session
810
1066
  .description("Start a new focus session and return its ID")
811
1067
  .requiredOption("--goal <text>", "What this session is working toward")
812
1068
  .option("--workspace <id>", "Workspace that owns the session")
1069
+ .option("--project <id>", "Project that owns the session (defaults to the active project lens; the pod accepts a workspace OR a project)")
813
1070
  .option("--task-id <id>", "Link session to an existing task entity")
1071
+ .option("--track <id>", "Born inside a track (a project's lasting goal) — implies its project")
1072
+ .option("--template <id>", "Start from a playbook (seeds currentStage from its stages)")
1073
+ .option("--no-template", "Opt out of auto-matching a playbook — start ad-hoc")
1074
+ .option("--parent <id>", "Push from another session — records session --spawned_from--> session (the parent stays open)")
1075
+ .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")
1076
+ .option("--criteria <file.json>", "Path to a JSON file of binary acceptance criteria (SessionCriterion[])")
814
1077
  .option("--json", "Output as JSON")
815
1078
  .option("--pod-url <url>", "Pod URL override")
816
1079
  .option("--api-key <key>", "API key override")
@@ -822,7 +1085,9 @@ session
822
1085
  .command("list", { isDefault: true })
823
1086
  .description("List focus sessions in the active workspace")
824
1087
  .option("--workspace <id>", "Scope to a specific workspace")
1088
+ .option("--project <id>", "Scope to a project (its sessions across spaces)")
825
1089
  .option("--status <status>", "Filter by status: active | paused | closed")
1090
+ .option("--kind <kind>", "Filter by population: work | run | receipt | all", "all")
826
1091
  .option("--limit <n>", "Max results", "20")
827
1092
  .option("--json", "Output as JSON")
828
1093
  .option("--pod-url <url>", "Pod URL override")
@@ -833,8 +1098,8 @@ session
833
1098
  });
834
1099
  session
835
1100
  .command("get <id>")
836
- .description("Show details for a specific focus session")
837
- .option("--workspace <id>", "Workspace that owns the session (required for scoped fetch)")
1101
+ .description("Show details for a specific focus session (works for project-scoped sessions)")
1102
+ .option("--workspace <id>", "Workspace hint if the pod still requires it for GET")
838
1103
  .option("--json", "Output as JSON")
839
1104
  .option("--pod-url <url>", "Pod URL override")
840
1105
  .option("--api-key <key>", "API key override")
@@ -844,10 +1109,14 @@ session
844
1109
  });
845
1110
  session
846
1111
  .command("update <id>")
847
- .description("Update progress or status of a focus session")
848
- .requiredOption("--workspace <id>", "Workspace that owns the session")
849
- .option("--progress <0-100>", "Progress percentage")
1112
+ .description("Update a focus session's progress, status, stage or goal")
1113
+ // NOT required: `workspaceId` is `.optional()` on the pod's UpdateBodySchema
1114
+ // and the session is addressed by <id>. Demanding it made an already-attached
1115
+ // or project-scoped session un-updatable without restating a known lens.
1116
+ .option("--workspace <id>", "Workspace that owns the session (optional; inferred from the active lens)")
1117
+ .option("--progress <0-100>", "Progress percentage — a whole number 0-100, not prose")
850
1118
  .option("--status <status>", "New status: active | paused")
1119
+ .option("--stage <key>", "Advance the session's current stage (playbook stage key)")
851
1120
  .option("--goal <text>", "Revised goal")
852
1121
  .option("--json", "Output as JSON")
853
1122
  .option("--pod-url <url>", "Pod URL override")
@@ -858,10 +1127,16 @@ session
858
1127
  });
859
1128
  session
860
1129
  .command("close <id>")
861
- .description("Close a focus session and optionally attach a recap")
862
- .requiredOption("--workspace <id>", "Workspace that owns the session")
863
- .option("--recap <text>", "Short summary of what was accomplished")
864
- .option("--json", "Output as JSON")
1130
+ .description("End a focus session (proposal pack) and optionally attach a recap")
1131
+ .option("--workspace <id>", "Workspace hint (optional; not required for complete)")
1132
+ .option("--recap <text>", "Short summary of what was accomplished (maps to summary)")
1133
+ // Deliberately NOT validated here, unlike --progress. A bad --progress
1134
+ // serialized to NaN and came back as an unreadable zod echo; a bad --as comes
1135
+ // back naming the three legal values, because the pod's enum derives from
1136
+ // TERMINAL_SESSION_STATUSES. Re-listing them here would be a second copy of
1137
+ // an enum this CLI cannot import — the hand-mirrored-vocabulary defect.
1138
+ .option("--as <status>", "How it ended: closed (default, finished) | cancelled (abandoned) | failed (could not complete)")
1139
+ .option("--json", "Output full pack (or close result) as JSON")
865
1140
  .option("--pod-url <url>", "Pod URL override")
866
1141
  .option("--api-key <key>", "API key override")
867
1142
  .action(async (id, opts) => {
@@ -888,12 +1163,162 @@ session
888
1163
  });
889
1164
  session
890
1165
  .command("current")
891
- .description("Show the active session for this terminal (from .synap-session or SYNAP_SESSION_ID)")
1166
+ .description("Show the active session for this terminal (SYNAP_SESSION_ID, Claude-session lens, or .synap/lens.json)")
892
1167
  .option("--json", "Output as JSON")
893
1168
  .action(async (opts) => {
894
1169
  const { sessionStatus } = await import("./commands/sessions.js");
895
1170
  sessionStatus(opts);
896
1171
  });
1172
+ session
1173
+ .command("evidence <key>")
1174
+ .description("Post your own pass/fail report for one evidence-checked acceptance criterion")
1175
+ .option("--session <id>", "Focus session ID (defaults to this terminal's active session)")
1176
+ .option("--passed", "The criterion is satisfied")
1177
+ .option("--failed", "The criterion is not satisfied")
1178
+ .option("--detail <text>", "Optional note explaining the verdict")
1179
+ .option("--json", "Output as JSON")
1180
+ .option("--pod-url <url>", "Pod URL override")
1181
+ .option("--api-key <key>", "API key override")
1182
+ .action(async (key, opts) => {
1183
+ const { evidenceSession } = await import("./commands/sessions.js");
1184
+ await evidenceSession(key, opts);
1185
+ });
1186
+ session
1187
+ .command("evaluate")
1188
+ .description("Run every pending acceptance-criterion check (evidence, capability, judge) and print the verdicts")
1189
+ .option("--session <id>", "Focus session ID (defaults to this terminal's active session)")
1190
+ .option("--json", "Output as JSON")
1191
+ .option("--pod-url <url>", "Pod URL override")
1192
+ .option("--api-key <key>", "API key override")
1193
+ .action(async (opts) => {
1194
+ const { evaluateSession } = await import("./commands/sessions.js");
1195
+ await evaluateSession(opts);
1196
+ });
1197
+ session
1198
+ .command("wait <sessionId>")
1199
+ .description("Block until the human replies in the session room, then print the reply. Exit 0 replied · 1 timeout · 2 failure (auth/network/broken read)")
1200
+ .option("--since <iso>", "Only answers strictly after this ISO timestamp (default: now — skips anything that predates this wait)")
1201
+ .option("--timeout <duration>", "Give up after this long (e.g. 30m, 2h)", "30m")
1202
+ .option("--interval <duration>", "Delay between checks (e.g. 10s)", "10s")
1203
+ .option("--limit <n>", "Max answers per page, 1-100", "20")
1204
+ .option("--json", "Output the reply(ies) — or the timeout — as JSON")
1205
+ .option("--pod-url <url>", "Pod URL override")
1206
+ .option("--api-key <key>", "API key override")
1207
+ .action(async (sessionId, opts) => {
1208
+ const { waitSessionCommand } = await import("./commands/session-wait.js");
1209
+ await waitSessionCommand(sessionId, opts);
1210
+ });
1211
+ // ─── playbook ─────────────────────────────────────────────────────────────────
1212
+ // Start a governed process in one step — the Hub door MCP synap_run_playbook
1213
+ // shares. A proposal ("queued for your review") is success.
1214
+ const playbookCmd = program
1215
+ .command("playbook")
1216
+ .description("Run playbooks — governed, reusable processes");
1217
+ playbookCmd
1218
+ .command("run <name-or-id>")
1219
+ .description("Run a playbook by name or id, optionally on a subject entity")
1220
+ .option("--subject <entityId>", "The entity the playbook runs on")
1221
+ .option("--param <key=value>", "Answer a declared param (repeatable; typed by the playbook)", collect, [])
1222
+ .option("--workspace <id>", "Write home for the run (defaults to the playbook's own workspace)")
1223
+ .option("--reason <text>", "Why — shown to the reviewer")
1224
+ .option("--json", "Output as JSON")
1225
+ .option("--pod-url <url>", "Pod URL override")
1226
+ .option("--api-key <key>", "API key override")
1227
+ .action(async (ref, opts) => {
1228
+ const { runPlaybook } = await import("./commands/playbook.js");
1229
+ await runPlaybook(ref, opts);
1230
+ });
1231
+ // ─── track ────────────────────────────────────────────────────────────────────
1232
+ // A LASTING goal inside a project, worked in sessions (`session start --track`).
1233
+ // Legacy tracks also run a method with steps. Writes are governed —
1234
+ // "queued for your review" is success.
1235
+ const track = program
1236
+ .command("track")
1237
+ .description("Manage tracks — the lasting goals a project pursues");
1238
+ track
1239
+ .command("list")
1240
+ .description("List a project's tracks (defaults to the active project lens)")
1241
+ .option("--project <id>", "Project id (defaults to the active project lens)")
1242
+ .option("--all", "Include archived tracks")
1243
+ .option("--json", "Output as JSON")
1244
+ .option("--pod-url <url>", "Pod URL override")
1245
+ .option("--api-key <key>", "API key override")
1246
+ .action(async (opts) => {
1247
+ const { listTracks } = await import("./commands/tracks.js");
1248
+ await listTracks(opts);
1249
+ });
1250
+ track
1251
+ .command("show <id>")
1252
+ .description("Show one track: its steps and where it stands")
1253
+ .option("--json", "Output as JSON")
1254
+ .option("--pod-url <url>", "Pod URL override")
1255
+ .option("--api-key <key>", "API key override")
1256
+ .action(async (id, opts) => {
1257
+ const { showTrack } = await import("./commands/tracks.js");
1258
+ await showTrack(id, opts);
1259
+ });
1260
+ track
1261
+ .command("start")
1262
+ .description("Start a track — a lasting goal — on a project")
1263
+ .option("--name <name>", "The track's name (required without --playbook)")
1264
+ .option("--direction <text>", "Where it is heading, one line")
1265
+ .option("--playbook <id>", "Legacy: a track template (project-scoped playbook with steps)")
1266
+ .option("--project <id>", "Project id (defaults to the active project lens)")
1267
+ .option("--param <key=value>", "Answer a declared param (repeatable)", collect, [])
1268
+ .option("--reason <text>", "Why — shown to the reviewer")
1269
+ .option("--json", "Output as JSON")
1270
+ .option("--pod-url <url>", "Pod URL override")
1271
+ .option("--api-key <key>", "API key override")
1272
+ .action(async (opts) => {
1273
+ const { startTrack } = await import("./commands/tracks.js");
1274
+ await startTrack(opts);
1275
+ });
1276
+ track
1277
+ .command("advance <id> <stage>")
1278
+ .description("Legacy (tracks with steps): move a track to a step; prints the session that step offers")
1279
+ .option("--reason <text>", "Why — shown to the reviewer")
1280
+ .option("--json", "Output as JSON")
1281
+ .option("--pod-url <url>", "Pod URL override")
1282
+ .option("--api-key <key>", "API key override")
1283
+ .action(async (id, stage, opts) => {
1284
+ const { advanceTrack } = await import("./commands/tracks.js");
1285
+ await advanceTrack(id, stage, opts);
1286
+ });
1287
+ track
1288
+ .command("status <id> <status>")
1289
+ .description("Pause, resume or archive a track (active|paused|archived)")
1290
+ .option("--reason <text>", "Why — shown to the reviewer")
1291
+ .option("--json", "Output as JSON")
1292
+ .option("--pod-url <url>", "Pod URL override")
1293
+ .option("--api-key <key>", "API key override")
1294
+ .action(async (id, status, opts) => {
1295
+ const { setTrackStatus } = await import("./commands/tracks.js");
1296
+ await setTrackStatus(id, status, opts);
1297
+ });
1298
+ track
1299
+ .command("params <id> <pairs...>")
1300
+ .description("Answer the method's params as key=value (merged; key=null clears one)")
1301
+ .option("--reason <text>", "Why — shown to the reviewer")
1302
+ .option("--json", "Output as JSON")
1303
+ .option("--pod-url <url>", "Pod URL override")
1304
+ .option("--api-key <key>", "API key override")
1305
+ .action(async (id, pairs, opts) => {
1306
+ const { setTrackParams } = await import("./commands/tracks.js");
1307
+ await setTrackParams(id, pairs, opts);
1308
+ });
1309
+ track
1310
+ .command("start-step <id>")
1311
+ .description("Legacy (tracks with steps): start one step's session — on a track with no steps, use 'session start --track'")
1312
+ .option("--stage <key>", "Step key (defaults to the current step)")
1313
+ .option("--title <text>", "Session name (defaults to the step's name)")
1314
+ .option("--goal <text>", "Outcome (defaults to the step's goal)")
1315
+ .option("--json", "Output as JSON")
1316
+ .option("--pod-url <url>", "Pod URL override")
1317
+ .option("--api-key <key>", "API key override")
1318
+ .action(async (id, opts) => {
1319
+ const { startStep } = await import("./commands/tracks.js");
1320
+ await startStep(id, opts);
1321
+ });
897
1322
  // ─── skill ────────────────────────────────────────────────────────────────────
898
1323
  // Autonomous workspace session skills — context gathering, task planning, etc.
899
1324
  const skillCmd = program
@@ -953,6 +1378,16 @@ skillCmd
953
1378
  const { listSkills } = await import("./commands/skill-manage.js");
954
1379
  await listSkills(opts);
955
1380
  });
1381
+ skillCmd
1382
+ .command("approve <slug-or-id>")
1383
+ .description("Approve a skill so it loads (agent-authored skills are born unapproved)")
1384
+ .option("--revoke", "Revoke approval instead of granting it")
1385
+ .option("--pod-url <url>", "Pod URL override")
1386
+ .option("--api-key <key>", "API key override")
1387
+ .action(async (ref, opts) => {
1388
+ const { approveSkill } = await import("./commands/skill-manage.js");
1389
+ await approveSkill(ref, opts);
1390
+ });
956
1391
  skillCmd
957
1392
  .command("remove <slug>")
958
1393
  .alias("rm")
@@ -1015,6 +1450,28 @@ observe
1015
1450
  .option("--pod-url <url>", "Pod URL override")
1016
1451
  .option("--api-key <key>", "API key override")
1017
1452
  .action(async (text, opts) => {
1453
+ // `write` is the DEFAULT subcommand, so an unrecognized token lands here as
1454
+ // `text` instead of erroring — and this default WRITES. `synap observe recal`
1455
+ // therefore recorded an observation literally reading "recal" into the
1456
+ // AI-maintained user model, silently. Every other `isDefault` group in this
1457
+ // file has a zero-arg default, which commander rejects on its own; this is
1458
+ // the only one that can absorb a typo, and the only one where absorbing it
1459
+ // is a mutation.
1460
+ //
1461
+ // The guard keys off whether `write` was named EXPLICITLY, not off the text:
1462
+ // a mistyped subcommand is always a single bare token, and a real
1463
+ // observation is a sentence. Going through `observe write <text>` skips the
1464
+ // check entirely, so the escape hatch is real rather than circular.
1465
+ const argv = process.argv;
1466
+ const observeAt = argv.indexOf("observe");
1467
+ const explicitWrite = observeAt !== -1 && argv[observeAt + 1] === "write";
1468
+ if (!explicitWrite && !/\s/.test(text.trim())) {
1469
+ const siblings = observe.commands.map((c) => c.name()).filter((n) => n !== "write");
1470
+ console.error(`error: '${text}' looks like a subcommand, not observation text — nothing was recorded.\n` +
1471
+ ` Available: ${siblings.map((n) => `synap observe ${n}`).join(", ")}\n` +
1472
+ ` To record it as an observation anyway: synap observe write ${JSON.stringify(text)}`);
1473
+ process.exit(1);
1474
+ }
1018
1475
  const { observeWrite } = await import("./commands/observe.js");
1019
1476
  await observeWrite(text, opts);
1020
1477
  });
@@ -1054,6 +1511,7 @@ create
1054
1511
  .description("Create a new entity")
1055
1512
  .requiredOption("--profile <slug>", "Profile slug (e.g. person, note, task)")
1056
1513
  .requiredOption("--name <name>", "Entity name")
1514
+ .option("--content <markdown>", "Long-form Markdown body, stored as the entity's linked document")
1057
1515
  .option("--workspace <id>", "Create in this workspace")
1058
1516
  .option("--props <json>", "Properties as JSON string", "{}")
1059
1517
  .option("--json", "Output as JSON")
@@ -1096,43 +1554,48 @@ set
1096
1554
  // ─── agents ───────────────────────────────────────────────────────────────────
1097
1555
  const agents = program
1098
1556
  .command("agents")
1099
- .description("Manage named agent identities (different keys, different pod access)");
1557
+ .description("Manage agent principals on your pod (one per type for you; local keys are a cache)");
1100
1558
  agents
1101
1559
  .command("list", { isDefault: true })
1102
- .description("List configured agent identities")
1560
+ .description("List your agents on this pod (one per type). Use --local for ~/.synap cache only.")
1103
1561
  .option("--json", "Output as JSON")
1562
+ .option("--local", "List only the local ~/.synap agent cache")
1104
1563
  .action(async (opts) => {
1105
1564
  const { agentsList } = await import("./commands/agents.js");
1106
- agentsList(opts);
1565
+ await agentsList(opts);
1107
1566
  });
1108
1567
  agents
1109
- .command("add")
1110
- .description("Register a pre-existing agent credential locally. Use `synap agents create` for new agents.")
1111
- .option("--name <name>", "Agent name (e.g. researcher, builder)")
1112
- .option("--api-key <key>", "Hub Protocol API key for this agent")
1113
- .option("--pod <name>", "Pod profile to link (default: active pod)")
1114
- .option("--workspace <id>", "Default workspace for this agent")
1568
+ .command("add [kind]")
1569
+ .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)")
1570
+ .option("--name <name>", "Name your own agent (e.g. researcher) — prints a URL + key to paste")
1571
+ .option("--template <tmpl>", "Your own agent's template: twin | assistant | custom (default: custom)")
1572
+ .option("--type <type>", "Explicit agent type for your own agent (default: a slug of --name)")
1573
+ .option("--role <role>", "Workspace role: admin | editor | viewer (default: editor)")
1574
+ .option("--workspace <id>", "Focus on one workspace (default: pod-wide)")
1575
+ .option("--project <id>", "Focus on one project (composable with --workspace)")
1576
+ .option("--with-mcp", "Raycast only: also install the full MCP server via mcp-remote")
1577
+ .option("--api-key <key>", "Register a key you already hold, on this machine only (mints nothing)")
1115
1578
  .option("--label <label>", "Human-readable description")
1116
- .action(async (opts) => {
1579
+ .action(async (kind, opts) => {
1117
1580
  const { agentsAdd } = await import("./commands/agents.js");
1118
- await agentsAdd(opts);
1581
+ await agentsAdd(kind, opts);
1119
1582
  });
1120
1583
  agents
1121
1584
  .command("create")
1122
- .description("Create a new agent on the pod (template-aware). Replaces `add` for new agents.")
1585
+ .description("Deprecated — use `synap agents add` (same flags)")
1123
1586
  .option("--template <tmpl>", "Agent template: twin | assistant | custom (default: custom)")
1124
1587
  .option("--name <name>", "Agent name (not needed for twin — auto-generated)")
1125
1588
  .option("--type <type>", "Agent type string for custom agents (default: matches template)")
1126
- .option("--role <role>", "Workspace role: admin | editor | viewer (default: editor; twin auto-inherits)")
1127
- .option("--workspace <id>", "Workspace ID to add the agent to (auto-detected if omitted)")
1128
- .option("--pod <name>", "Pod profile to use (default: active pod)")
1589
+ .option("--role <role>", "Workspace role: admin | editor | viewer (default: editor)")
1590
+ .option("--workspace <id>", "Focus on one workspace (default: pod-wide)")
1591
+ .option("--project <id>", "Focus on one project")
1129
1592
  .action(async (opts) => {
1130
1593
  const { agentsCreate } = await import("./commands/agents.js");
1131
1594
  await agentsCreate(opts);
1132
1595
  });
1133
1596
  agents
1134
1597
  .command("rotate-key <name-or-id>")
1135
- .description("Rotate the Hub Protocol API key for an agent (invalidates the current key)")
1598
+ .description("Rotate the Hub Protocol API key for an agent (local cache only)")
1136
1599
  .action(async (nameOrId) => {
1137
1600
  const { agentsRotateKey } = await import("./commands/agents.js");
1138
1601
  await agentsRotateKey(nameOrId);
@@ -1140,17 +1603,19 @@ agents
1140
1603
  agents
1141
1604
  .command("remove <name>")
1142
1605
  .alias("rm")
1143
- .description("Remove a named agent identity")
1606
+ .description("Remove a named agent identity (local cache only)")
1144
1607
  .action(async (name) => {
1145
1608
  const { agentsRemove } = await import("./commands/agents.js");
1146
1609
  agentsRemove(name);
1147
1610
  });
1148
1611
  agents
1149
1612
  .command("info <name>")
1150
- .description("Show details for a named agent identity")
1151
- .action(async (name) => {
1613
+ .description("Show details for a named agent identity (local cache only)")
1614
+ .option("--show-key", "Print the MCP URL and the full API key")
1615
+ .option("--snippets", "Print paste-ready configs (Claude Code, Cursor, stdio clients)")
1616
+ .action(async (name, opts) => {
1152
1617
  const { agentsInfo } = await import("./commands/agents.js");
1153
- agentsInfo(name);
1618
+ await agentsInfo(name, opts);
1154
1619
  });
1155
1620
  // ─── agent (autonomous runner) ───────────────────────────────────────────────
1156
1621
  const agent = program
@@ -1228,6 +1693,52 @@ agent
1228
1693
  const { agentTick } = await import("./commands/agent-run.js");
1229
1694
  await agentTick(opts);
1230
1695
  });
1696
+ // ─── automate ───────────────────────────────────────────────────────────────
1697
+ // The natural-language automation door. It stays deliberately separate from
1698
+ // `automation create`, which remains the expert, explicit-DSL path.
1699
+ program
1700
+ .command("automate <instruction>")
1701
+ .description("Ask the workspace agent to prepare a governed automation proposal")
1702
+ .option("--workspace <id|name>", "Workspace to use (uses active workspace/lens if omitted)")
1703
+ .option("--thread <id>", "Continue an existing agent thread/channel")
1704
+ .option("--new", "Start a fresh agent thread")
1705
+ .option("--timeout <seconds>", "How long to wait for the agent's reply (default: 90)")
1706
+ .option("--json", "Machine-readable output { ok, reply, threadId, workspaceId }")
1707
+ .option("--pod-url <url>", "Override pod URL")
1708
+ .option("--api-key <key>", "Override API key")
1709
+ .action(async (instruction, opts) => {
1710
+ const { automate } = await import("./commands/automate.js");
1711
+ await automate(instruction, opts);
1712
+ });
1713
+ // ─── rule ────────────────────────────────────────────────────────────────────
1714
+ // The fourth door, beside `ask` (recall), `capture` (write) and `diagnose`
1715
+ // (inspect): a STANDING instruction. It classifies the sentence and shows the
1716
+ // reading before writing anything — and it records the rule only. Building the
1717
+ // behaviour half is still `synap automate`.
1718
+ const ruleCmd = program
1719
+ .command("rule [text]")
1720
+ .description("State a standing rule in plain language — classified, shown, then governed-created")
1721
+ .option("--workspace <id>", "Workspace to scope the rule to (uses active workspace if omitted)")
1722
+ .option("--pod-wide", "Scope the rule to the whole pod, ignoring the active workspace")
1723
+ .option("--yes", "Skip the one-off confirmation (never skips a clarification)")
1724
+ .option("--json", "Output raw JSON")
1725
+ .option("--pod-url <url>", "Pod URL override")
1726
+ .option("--api-key <key>", "API key override")
1727
+ .action(async (text, opts) => {
1728
+ const { rule } = await import("./commands/rule.js");
1729
+ await rule(text ?? "", opts);
1730
+ });
1731
+ ruleCmd
1732
+ .command("list")
1733
+ .description("List rules with their intent, scope, and whether a behaviour is attached")
1734
+ .option("--workspace <id>", "Filter by workspace")
1735
+ .option("--json", "Output raw JSON")
1736
+ .option("--pod-url <url>", "Pod URL override")
1737
+ .option("--api-key <key>", "API key override")
1738
+ .action(async (opts) => {
1739
+ const { ruleList } = await import("./commands/rule.js");
1740
+ await ruleList(opts);
1741
+ });
1231
1742
  // ─── automation ──────────────────────────────────────────────────────────────
1232
1743
  function collect(val, prev) { return [...prev, val]; }
1233
1744
  const automation = program
@@ -1419,12 +1930,11 @@ program
1419
1930
  .description("Open in the Synap desktop app. Use: open <kind> <id> or open <id> (auto-resolves type)")
1420
1931
  .action(async (kindOrId, id) => {
1421
1932
  if (id) {
1422
- // open entity|view|cell|document|proposal <id>
1423
- const validKinds = ["entity", "view", "cell", "document", "proposal"];
1424
- if (!validKinds.includes(kindOrId)) {
1425
- console.error(`Unknown kind: "${kindOrId}". Use: ${validKinds.join(" | ")}`);
1426
- process.exit(1);
1427
- }
1933
+ // open <kind> <id> — the browser's object-nav.ts is the one route
1934
+ // table for openable kinds (21 and growing); the CLI doesn't
1935
+ // duplicate it here. An unroutable kind still opens the app and
1936
+ // is reported via the printed deep link (objectNavTarget decides
1937
+ // routability on the other side).
1428
1938
  const { openInBrowser } = await import("./commands/open.js");
1429
1939
  await openInBrowser({ kind: kindOrId, id });
1430
1940
  }
@@ -1461,10 +1971,12 @@ Examples:
1461
1971
  });
1462
1972
  doc
1463
1973
  .command("update <documentId>")
1464
- .description("Update a document's content (full replacement, proposal-gated)")
1974
+ .description("Update a document's content: the whole body, or one section (--section). Governed: an agent's edit is proposed.")
1465
1975
  .option("--content <text>", "New markdown content (inline)")
1466
1976
  .option("--file <path>", "Read new content from a file")
1467
- .option("--title <title>", "Update document title (requires --content too)")
1977
+ .option("--section <id>", "Write ONE section by id (content = the section body) instead of the whole document")
1978
+ .option("--section-title <title>", "The section heading (required with --section)")
1979
+ .option("--base-revision <n>", "The revision you read; refused if the document changed since")
1468
1980
  .option("--open", "Open in the Synap desktop app after update")
1469
1981
  .option("--json", "JSON output")
1470
1982
  .option("--pod-url <url>", "Pod URL override")
@@ -1474,11 +1986,29 @@ Examples:
1474
1986
  synap doc update <id> --content "# Updated"
1475
1987
  synap doc update <id> --file ./notes.md
1476
1988
  cat notes.md | synap doc update <id>
1989
+ synap doc update <id> --section risks --section-title "Risks" --file ./risks.md
1477
1990
  `)
1478
1991
  .action(async (documentId, opts) => {
1479
1992
  const { docUpdate } = await import("./commands/doc.js");
1480
1993
  await docUpdate(documentId, opts);
1481
1994
  });
1995
+ doc
1996
+ .command("reference <url>")
1997
+ .description("Create an external reference document (points at a URL — no bytes stored)")
1998
+ .requiredOption("--title <title>", "Document title")
1999
+ .option("--workspace <id>", "Workspace to create the reference in")
2000
+ .option("--open", "Open in the Synap desktop app after creation")
2001
+ .option("--json", "JSON output")
2002
+ .option("--pod-url <url>", "Pod URL override")
2003
+ .option("--api-key <key>", "API key override")
2004
+ .addHelpText("after", `
2005
+ Examples:
2006
+ synap doc reference https://example.com/spec --title "Vendor spec"
2007
+ `)
2008
+ .action(async (url, opts) => {
2009
+ const { docReference } = await import("./commands/doc.js");
2010
+ await docReference(url, opts);
2011
+ });
1482
2012
  // ─── cell ─────────────────────────────────────────────────────────────────────
1483
2013
  const cell = program
1484
2014
  .command("cell")
@@ -1489,17 +2019,30 @@ cell
1489
2019
  .requiredOption("--name <name>", "Cell display name")
1490
2020
  .option("--file <path>", "ESM source file (or pipe stdin)")
1491
2021
  .option("--type-key <key>", "Explicit typeKey (default: generated:<slug>)")
1492
- .option("--deps <json>", 'Dependency map JSON, e.g. \'{"recharts":"2.12.0"}\' (sent in payload; backend support pending)')
1493
- .option("--workspace <id>", "Workspace to scope the cell to (omit for pod-global)")
2022
+ .option("--deps <json>", 'Dependency map JSON, e.g. \'{"recharts":"2.12.0"}\'')
2023
+ .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')
2024
+ .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)")
2025
+ // NOT "omit for pod-wide": `cell.ts` resolves `opts.workspace ?? cfg.workspaceId`,
2026
+ // and `cfg.workspaceId` is the resolved lens (session › directory › env ›
2027
+ // profile default). Omitting therefore scopes the cell to whatever workspace
2028
+ // your lens currently points at — which is pod-wide ONLY when nothing resolves.
2029
+ // Verified 2026-09-05: run from a directory with no `.synap/lens.json` and the
2030
+ // cell silently lands in the pod profile's default workspace.
2031
+ .option("--workspace <id>", "Workspace to scope the cell to (default: your active workspace lens — run `synap lens` to see it)")
1494
2032
  .option("--description <text>", "Short description")
1495
2033
  .option("--open", "Open in the Synap desktop app after define")
1496
2034
  .option("--json", "JSON output")
1497
2035
  .option("--pod-url <url>", "Pod URL override")
1498
2036
  .option("--api-key <key>", "API key override")
1499
2037
  .addHelpText("after", `
2038
+ Without --view-types / --content-kind a cell installs cleanly but can never be
2039
+ PICKED as a view or profile renderer — it is only placeable on a bento.
2040
+
1500
2041
  Examples:
1501
2042
  synap cell define --name "Revenue Chart" --file ./chart.js
1502
2043
  synap cell define --name "Revenue Chart" --file ./chart.js --deps '{"recharts":"2.12.0"}'
2044
+ synap cell define --name "Deal Board" --file ./board.js --view-types kanban,table --content-kind collection
2045
+ synap cell define --name "Contact Page" --file ./contact.js --content-kind entity-detail
1503
2046
  cat chart.js | synap cell define --name "Revenue Chart"
1504
2047
  `)
1505
2048
  .action(async (opts) => {
@@ -1510,10 +2053,16 @@ cell
1510
2053
  .command("build <entry>")
1511
2054
  .description("Bundle a multi-file cell/app into a single ESM module the runtime expects")
1512
2055
  .option("--out <file>", "Output file path (default: <entry>.bundle.js)")
2056
+ .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")
2057
+ .option("--self-contained", 'Build ONE complete HTML document (React, CSS, brand fonts inlined; nothing external) — the code of a rendererType "mcp-app" cell')
2058
+ .option("--fonts", "With --self-contained: require the inlined brand fonts (default: include when @synap-core/design-tokens resolves)")
2059
+ .option("--no-fonts", "With --self-contained: skip the inlined brand fonts")
1513
2060
  .option("--define", "Chain into cell define after bundling (requires --name)")
1514
2061
  .option("--name <name>", "Cell name (required with --define)")
1515
2062
  .option("--type-key <key>", "Explicit typeKey (used with --define)")
1516
2063
  .option("--deps <json>", "Override dependency versions as JSON, e.g. '{\"react\":\"18.3.0\"}'")
2064
+ .option("--view-types <list>", 'Comma-separated view-renderer affinity (used with --define), e.g. "list,table"')
2065
+ .option("--content-kind <kind>", "entity-detail | entity-card | entity-profile | collection | widget (used with --define)")
1517
2066
  .option("--workspace <id>", "Workspace (used with --define)")
1518
2067
  .option("--description <text>", "Description (used with --define)")
1519
2068
  .option("--open", "Open in browser after define (used with --define)")
@@ -1521,12 +2070,20 @@ cell
1521
2070
  .option("--pod-url <url>", "Pod URL override")
1522
2071
  .option("--api-key <key>", "API key override")
1523
2072
  .addHelpText("after", `
1524
- Output: a single ESM file. Bare imports (react, recharts, …) are externalized
1525
- and emitted as a deps map. At runtime Synap resolves them via esm.sh importmap.
2073
+ Output: a single ESM file.
2074
+
2075
+ Default mode: bare imports (react, recharts, …) are externalized and emitted
2076
+ as a deps map. At runtime Synap resolves them via esm.sh importmap.
2077
+
2078
+ --bundle-deps mode: only react/react-dom are externalized (React is
2079
+ host-inlined at runtime, never via esm.sh); everything else is bundled into
2080
+ the output, so the deps map is empty. A cell built this way reaches no esm.sh
2081
+ origin at runtime — the runtime CSP collapses to drop it entirely.
1526
2082
 
1527
2083
  Examples:
1528
2084
  synap cell build ./src/chart.tsx --out ./dist/chart.js
1529
2085
  synap cell build ./src/chart.tsx --out ./dist/chart.js --define --name "Revenue Chart"
2086
+ synap cell build ./src/chart.tsx --out ./dist/chart.js --bundle-deps
1530
2087
  `)
1531
2088
  .action(async (entry, opts) => {
1532
2089
  const { cellBuild } = await import("./commands/cell.js");
@@ -1576,7 +2133,7 @@ tools
1576
2133
  });
1577
2134
  tools
1578
2135
  .command("connect <service>")
1579
- .description("Connect a credential to a tool (via Nango OAuth or vault)")
2136
+ .description("Connect a credential to a tool (OAuth via the connection broker, or vault)")
1580
2137
  .option("--workspace <id>", "Workspace context")
1581
2138
  .option("--pod-url <url>", "Pod URL override")
1582
2139
  .option("--api-key <key>", "API key override")
@@ -1641,6 +2198,33 @@ podCmd
1641
2198
  const { podAdopt } = await import("./commands/pod.js");
1642
2199
  await podAdopt(workspaceNameOrId, opts);
1643
2200
  });
2201
+ // ─── vendor — the author's publisher profile ─────────────────────────────────
2202
+ // Attribution is write-time-only at the CP: a package published before a vendor
2203
+ // exists is orphaned from its author forever. See src/commands/vendor.ts.
2204
+ const vendor = program
2205
+ .command("vendor")
2206
+ .description("Your marketplace publisher profile (what packages are attributed to)");
2207
+ vendor
2208
+ .command("create <slug>")
2209
+ .description("Create your publisher profile — do this BEFORE your first publish")
2210
+ .option("--name <name>", "Display name (defaults to a title-cased slug)")
2211
+ .option("--description <text>", "Short description of you or your studio")
2212
+ .option("--website <url>", "Website URL (must be a full URL)")
2213
+ .option("--json", "Output as JSON")
2214
+ .action(async (slug, _opts, cmd) => {
2215
+ const opts = cmd.optsWithGlobals();
2216
+ const { vendorCreate } = await import("./commands/vendor.js");
2217
+ await vendorCreate(slug, opts);
2218
+ });
2219
+ vendor
2220
+ .command("show")
2221
+ .description("Show whether you have a publisher profile")
2222
+ .option("--json", "Output as JSON")
2223
+ .action(async (_opts, cmd) => {
2224
+ const opts = cmd.optsWithGlobals();
2225
+ const { vendorShow } = await import("./commands/vendor.js");
2226
+ await vendorShow(opts);
2227
+ });
1644
2228
  // ─── market — discover + install ANY package type ────────────────────────────
1645
2229
  // launch is workspace-first (bundled, offline); market reaches every CP package
1646
2230
  // type (capability/skill/workflow/view/cell/workspace), public or private.
@@ -1659,7 +2243,9 @@ market
1659
2243
  .command("install <slug>")
1660
2244
  .description("Install a package by slug into a project — workspaces install now; other types route you to the right surface")
1661
2245
  .option("--project <id>", "Optionally tag the seeded entities to a project (installs are pod-wide by default)")
2246
+ .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")
1662
2247
  .option("--onto <workspaceId>", "Reconcile this template ONTO an existing workspace (additive) instead of creating a new one")
2248
+ .option("--dry-run", "Preview the create path write-free — report would-create / reuse / conflicts, install nothing")
1663
2249
  .option("--timeout <seconds>", "How long to wait for the apply to finish (default: 120)")
1664
2250
  .option("--json", "Output as JSON")
1665
2251
  .option("--pod-url <url>", "Pod URL override")
@@ -1678,7 +2264,8 @@ market
1678
2264
  market
1679
2265
  .command("update [slugs...]")
1680
2266
  .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)")
1681
- .option("--yes", "Apply every available update without prompting (no slugs required)")
2267
+ .option("--yes", "Apply every available update without prompting (no slugs required), agreeing to every data change it lists")
2268
+ .option("--accept-data-changes <opKeys>", "Agree to exactly these data changes (comma-separated opKeys from the preview)")
1682
2269
  .option("--dry-run", "Force the preview path — never apply, even with --yes")
1683
2270
  .option("--timeout <seconds>", "How long to wait for each package's apply to finish (default: 120)")
1684
2271
  .option("--json", "Output as JSON")
@@ -1703,6 +2290,209 @@ market
1703
2290
  const { marketInstalled } = await import("./commands/market.js");
1704
2291
  await marketInstalled(opts);
1705
2292
  });
2293
+ market
2294
+ .command("workspaces")
2295
+ .description("List your workspaces with their template-attachment status — find the one that isn't attached yet (pure read)")
2296
+ .option("--unattached", "Only show workspaces that need attention (unattached or missing a version stamp)")
2297
+ .option("--json", "Output as JSON")
2298
+ .action(async (_opts, cmd) => {
2299
+ // Same parent/child `--json` collision as `market install` — see its comment.
2300
+ const opts = cmd.optsWithGlobals();
2301
+ const { marketWorkspaces } = await import("./commands/market.js");
2302
+ await marketWorkspaces(opts);
2303
+ });
2304
+ market
2305
+ .command("attach <workspaceId>")
2306
+ .description("Reconcile a marketplace template onto an existing workspace + version-stamp it — how you relink a workspace installed before the marketplace")
2307
+ .option("--slug <slug>", "Template to attach (inferred from the workspace's existing attachment or domain when omitted)")
2308
+ .option("--project <id>", "Optionally tag the reconciled entities to a project")
2309
+ .option("--timeout <seconds>", "How long to wait for the apply to finish (default: 120)")
2310
+ .option("--json", "Output as JSON")
2311
+ .option("--pod-url <url>", "Pod URL override")
2312
+ .option("--api-key <key>", "API key override")
2313
+ .action(async (workspaceId, _opts, cmd) => {
2314
+ // Same parent/child `--json` collision as `market install` — see its comment.
2315
+ const opts = cmd.optsWithGlobals();
2316
+ const { marketAttach } = await import("./commands/market.js");
2317
+ await marketAttach(workspaceId, opts);
2318
+ });
2319
+ // ── `synap templates` — the ONE home for workspace ↔ template health ─────────
2320
+ // Consolidates `market update`/`workspaces`/`installed`: one grouped, action-
2321
+ // ranked view where an UNATTACHED workspace is visible (it wasn't in `update`),
2322
+ // with a suggested template and a one-word attach. The `market *` subcommands
2323
+ // still work — this is the front door, not a breaking rename.
2324
+ const templates = program
2325
+ .command("templates")
2326
+ .description("See every workspace's template status (attached, drifted, or unattached) and connect them — the one home for template health")
2327
+ .option("--needs-attention", "Show only workspaces that need action (drifted / unstamped / unattached-with-suggestion)")
2328
+ .option("--json", "Output as JSON")
2329
+ .option("--pod-url <url>", "Pod URL override")
2330
+ .option("--api-key <key>", "API key override")
2331
+ .action(async (_opts, cmd) => {
2332
+ const opts = cmd.optsWithGlobals();
2333
+ const { marketTemplates } = await import("./commands/market.js");
2334
+ await marketTemplates(opts);
2335
+ });
2336
+ templates
2337
+ .command("attach <workspace>")
2338
+ .description("Connect a workspace (by NAME or id) to a template — suggests the right one so you don't guess a slug")
2339
+ .option("--slug <slug>", "Force a specific template instead of the suggestion")
2340
+ .option("--yes", "Accept a confident (domain-match) suggestion without prompting — for scripts")
2341
+ .option("--project <id>", "Optionally tag the reconciled entities to a project")
2342
+ .option("--timeout <seconds>", "How long to wait for the apply to finish (default: 120)")
2343
+ .option("--json", "Output as JSON")
2344
+ .option("--pod-url <url>", "Pod URL override")
2345
+ .option("--api-key <key>", "API key override")
2346
+ .action(async (workspace, _opts, cmd) => {
2347
+ const opts = cmd.optsWithGlobals();
2348
+ const { templatesAttach } = await import("./commands/market.js");
2349
+ await templatesAttach(workspace, opts);
2350
+ });
2351
+ templates
2352
+ .command("update [slugs...]")
2353
+ .alias("updates")
2354
+ .description("Apply template updates — 'update all' (or --yes) for every drifted workspace, or name specific slugs. Bare 'update' previews what's available.")
2355
+ .option("--yes", "Apply every available update without prompting, agreeing to every data change it lists")
2356
+ .option("--accept-data-changes <opKeys>", "Agree to exactly these data changes (comma-separated opKeys from the preview)")
2357
+ .option("--dry-run", "Preview only — never apply")
2358
+ .option("--timeout <seconds>", "How long to wait for each apply (default: 120)")
2359
+ .option("--json", "Output as JSON")
2360
+ .option("--pod-url <url>", "Pod URL override")
2361
+ .option("--api-key <key>", "API key override")
2362
+ .action(async (slugs, _opts, cmd) => {
2363
+ const opts = cmd.optsWithGlobals();
2364
+ // `update all` reads more naturally than `--yes` and is what people type —
2365
+ // treat the literal word "all" as "apply every available update". It is NOT
2366
+ // `--yes`: data changes still need the person's agreement (asked, or flagged).
2367
+ const wantsAll = !!slugs?.some((s) => s.toLowerCase() === "all");
2368
+ const explicit = (slugs ?? []).filter((s) => s.toLowerCase() !== "all");
2369
+ const { marketUpdate } = await import("./commands/market.js");
2370
+ await marketUpdate(explicit.length > 0 ? explicit : undefined, wantsAll ? { ...opts, all: true } : opts);
2371
+ });
2372
+ templates
2373
+ .command("retire <target>")
2374
+ .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.")
2375
+ .option("--dry-run", "Show the plan only — move nothing")
2376
+ .option("--yes", "Move without asking (for scripts)")
2377
+ .option("--json", "Output as JSON")
2378
+ .option("--pod-url <url>", "Pod URL override")
2379
+ .option("--api-key <key>", "API key override")
2380
+ .action(async (target, _opts, cmd) => {
2381
+ const opts = cmd.optsWithGlobals();
2382
+ const { templatesRetire } = await import("./commands/templates-retire.js");
2383
+ await templatesRetire(target, opts);
2384
+ });
2385
+ // ── Authoring loop: scaffold → validate → publish → unpublish ────────────────
2386
+ market
2387
+ .command("scaffold <slug>")
2388
+ .description("Write a minimal valid <slug>.template.yaml to edit in place (refuses to overwrite)")
2389
+ // This list MUST equal `SCAFFOLDABLE_KINDS` (src/lib/kind-package.ts). It is
2390
+ // written out rather than imported because index.ts keeps its eager module
2391
+ // graph to commander + one lib — importing kind-package.ts here would pull
2392
+ // `yaml` into the startup path of every `synap` invocation. What keeps the two
2393
+ // honest is `test/scaffold-kind-help-parity.test.ts`, which source-scans both.
2394
+ //
2395
+ // It advertised `skill` — which `marketScaffold` refuses, since the CP package
2396
+ // schema has no standalone slot for one — and omitted `workflow`, which
2397
+ // `market validate` has always accepted. A help string that offers a refused
2398
+ // option and hides a working one is the documented-loop defect class.
2399
+ .option("--kind <kind>", "Scaffold a standalone package instead of a workspace template: cell, view, workflow, capability, skill")
2400
+ .option("--json", "Output as JSON")
2401
+ .action(async (slug, _opts, cmd) => {
2402
+ // Same parent/child `--json` collision as `market install` — see its comment.
2403
+ const opts = cmd.optsWithGlobals();
2404
+ const { marketScaffold } = await import("./commands/market-authoring.js");
2405
+ await marketScaffold(slug, opts);
2406
+ });
2407
+ market
2408
+ .command("validate <file>")
2409
+ .description("Validate a local template file against the shared template validator — fast author feedback")
2410
+ .option("--json", "Output the validator result as JSON")
2411
+ .action(async (file, _opts, cmd) => {
2412
+ // Same parent/child `--json` collision as `market install` — see its comment.
2413
+ const opts = cmd.optsWithGlobals();
2414
+ const { marketValidate } = await import("./commands/market-authoring.js");
2415
+ await marketValidate(file, opts);
2416
+ });
2417
+ market
2418
+ .command("publish [targets...]")
2419
+ .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`)")
2420
+ .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")
2421
+ .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)")
2422
+ .option("--dry-run", "With --official: preview what would change against the live catalog, publish nothing")
2423
+ .option("--yes", "With --official: skip the confirmation prompt")
2424
+ .option("--public", "Publish as PUBLIC (default is private)")
2425
+ .option("--private", "Publish as private (the default — stated explicitly)")
2426
+ .option("--from-workspace <id>", "Serialize a live workspace into a template and publish it (instead of a file)")
2427
+ .option("--from-project <id>", "Serialize a live project's used workspaces into a suite package (tags: suite, require deps) and publish it")
2428
+ .option("--price <usd>", "Stamp pricingModel=one_time + priceUsd (dollars→cents). Settlement/Stripe is not this wave — columns only.")
2429
+ .option("--json", "Output as JSON")
2430
+ .option("--pod-url <url>", "Pod URL override (for --from-workspace / --from-project)")
2431
+ .option("--api-key <key>", "API key override (for --from-workspace / --from-project)")
2432
+ // Default ON for a cell package's `codeFile` resolution — see `cell build
2433
+ // --bundle-deps`'s doc comment for why: a published cell's `deps` map is
2434
+ // otherwise resolved through esm.sh at RUNTIME, so the artifact reviewed at
2435
+ // publish time isn't what actually executes. `synap cell build` invoked
2436
+ // directly is unaffected — its own default stays external-deps-via-esm.sh.
2437
+ .option("--no-bundle-deps", "Publish a cell's codeFile with external deps via esm.sh instead of bundling them in (default: bundled)")
2438
+ .action(async (targets, _opts, cmd) => {
2439
+ // Same parent/child `--json` collision as `market install` — see its comment.
2440
+ const opts = cmd.optsWithGlobals();
2441
+ const args = targets ?? [];
2442
+ if (opts.official) {
2443
+ const { marketPublishOfficial } = await import("./commands/market-official.js");
2444
+ process.exitCode = await marketPublishOfficial(args, opts);
2445
+ return;
2446
+ }
2447
+ const { nonOfficialUsageError } = await import("./commands/market-official.js");
2448
+ const usage = nonOfficialUsageError(args, opts);
2449
+ if (usage) {
2450
+ console.error(` ✗ ${usage}`);
2451
+ process.exitCode = 2;
2452
+ return;
2453
+ }
2454
+ const { marketPublish } = await import("./commands/market-authoring.js");
2455
+ await marketPublish(args[0], opts);
2456
+ });
2457
+ market
2458
+ .command("unpublish <slug>")
2459
+ .description("Flip a published package back to private (owner only)")
2460
+ .option("--json", "Output as JSON")
2461
+ .action(async (slug, _opts, cmd) => {
2462
+ // Same parent/child `--json` collision as `market install` — see its comment.
2463
+ const opts = cmd.optsWithGlobals();
2464
+ const { marketUnpublish } = await import("./commands/market-authoring.js");
2465
+ await marketUnpublish(slug, opts);
2466
+ });
2467
+ // ─── catalog ──────────────────────────────────────────────────────────────────
2468
+ // The team's ONE door to the official catalog (every installable kind). Thin
2469
+ // wiring: `runCatalog` (commands/catalog.ts) calls the ONE catalog lib in the
2470
+ // monorepo checkout. `market publish --official` is its alias (kind workspace).
2471
+ const catalog = program
2472
+ .command("catalog")
2473
+ .description("Validate, diff and publish the OFFICIAL template catalog (workspaces, capabilities, skills, packs, automations) from the monorepo checkout");
2474
+ for (const verb of ["validate", "diff", "publish"]) {
2475
+ const sub = catalog
2476
+ .command(`${verb} [slugs...]`)
2477
+ .description(verb === "validate"
2478
+ ? "Validate the catalog (per-kind schema, cross-kind refs, goal grammar, duplicates) — offline"
2479
+ : verb === "diff"
2480
+ ? "Show what would change against the live control-plane catalog (new / changed / unchanged)"
2481
+ : "Validate, preview the diff, confirm, then publish to the control plane — founder only: --official + a `synap login` session that owns synap-official")
2482
+ .option("--kind <kind>", "Only this kind: workspace | capability | skill | pack | automation")
2483
+ .option("--catalog-dir <dir>", "Path to the catalog package (default: found by walking up from cwd)")
2484
+ .option("--json", "Output as JSON");
2485
+ if (verb === "publish") {
2486
+ sub
2487
+ .option("--official", "Publish as the synap-official vendor (required)")
2488
+ .option("--dry-run", "Preview the diff only — publish nothing")
2489
+ .option("--yes", "Skip the confirmation prompt");
2490
+ }
2491
+ sub.action(async (slugs, opts) => {
2492
+ const { runCatalog } = await import("./commands/catalog.js");
2493
+ process.exitCode = await runCatalog(verb, slugs ?? [], opts);
2494
+ });
2495
+ }
1706
2496
  // ─── capability ───────────────────────────────────────────────────────────────
1707
2497
  const capability = program
1708
2498
  .command("capability")
@@ -1755,6 +2545,7 @@ capability
1755
2545
  .command("connect [name]")
1756
2546
  .description("Connect a service — omit the name to pick from connectable services")
1757
2547
  .option("--workspace <id>", "Workspace context")
2548
+ .option("--reconnect", "Force a fresh sign-in even if a connection already exists — fixes an expired or revoked token")
1758
2549
  .action(async (name, opts) => {
1759
2550
  const { capabilityConnect } = await import("./commands/capability.js");
1760
2551
  await capabilityConnect(name, opts);
@@ -1851,13 +2642,37 @@ capConnections
1851
2642
  const { capabilityConnectionsRemove } = await import("./commands/cap-connections.js");
1852
2643
  await capabilityConnectionsRemove(capability, connectionId, opts);
1853
2644
  });
1854
- // ─── raycast ──────────────────────────────────────────────────────────────────
2645
+ capability
2646
+ .command("sync-status [provider]")
2647
+ .description("Show sync status per provider × kind × connection (phase, counts, errors) — omit provider to see all")
2648
+ .option("--workspace <id>", "Workspace context")
2649
+ .option("--json", "Output as JSON")
2650
+ .action(async (provider, opts) => {
2651
+ const { capabilitySyncStatus } = await import("./commands/sync-status.js");
2652
+ await capabilitySyncStatus(provider, opts);
2653
+ });
2654
+ // ─── sync (thin alias root for `cap sync-status`) ──────────────────────────────
2655
+ const sync = program
2656
+ .command("sync")
2657
+ .description("Sync status for connected providers — see `cap sync-status`");
2658
+ sync
2659
+ .command("status [provider]")
2660
+ .description("Show sync status per provider × kind × connection (phase, counts, errors) — omit provider to see all. Alias of `synap cap sync-status`.")
2661
+ .option("--workspace <id>", "Workspace context")
2662
+ .option("--json", "Output as JSON")
2663
+ .action(async (provider, opts) => {
2664
+ const { capabilitySyncStatus } = await import("./commands/sync-status.js");
2665
+ await capabilitySyncStatus(provider, opts);
2666
+ });
2667
+ // ─── raycast (hidden power-user escape) ───────────────────────────────────────
2668
+ // Prefer the native Synap Raycast extension (@synap). Capability verbs there go
2669
+ // through list-actions / run-action. Script Commands are a per-verb escape hatch.
1855
2670
  const raycast = program
1856
- .command("raycast")
1857
- .description("Generate Raycast Script Commands from your enabled capabilities");
2671
+ .command("raycast", { hidden: true })
2672
+ .description("(hidden) Power-user escape: generate Raycast Script Commands per capability verb");
1858
2673
  raycast
1859
2674
  .command("generate")
1860
- .description("Emit a Raycast Script Command per enabled runnable verb")
2675
+ .description("Power-user escape: emit a Script Command per enabled verb (extension uses list-actions/run-action)")
1861
2676
  .option("--out <dir>", "Output directory (default: ~/.synap/raycast-commands)")
1862
2677
  .option("--workspace <id>", "Workspace context")
1863
2678
  .action(async (opts) => {
@@ -1867,7 +2682,7 @@ raycast
1867
2682
  // ─── providers ───────────────────────────────────────────────────────────────
1868
2683
  const providers = program
1869
2684
  .command("providers")
1870
- .description("Discover AI providers from the pod and configure local tools");
2685
+ .description("LLM providers on the pod (OpenAI/Anthropic/…) for local coding tools — not capability packs");
1871
2686
  providers
1872
2687
  .command("list", { isDefault: true })
1873
2688
  .description("Show enabled AI providers and their models")
@@ -1880,14 +2695,36 @@ providers
1880
2695
  });
1881
2696
  providers
1882
2697
  .command("pull")
1883
- .description("Write pod provider config to a local AI tool (opencode, aider)")
2698
+ .description("Write pod LLM credentials into opencode or aider, or print the OpenAI-compatible {pod}/v1 endpoint")
1884
2699
  .option("--target <name>", "Target tool: opencode | aider")
1885
2700
  .option("--pod-url <url>", "Pod URL override")
1886
2701
  .option("--api-key <key>", "API key override")
2702
+ .option("--show-key", "With --target show: print the full API key (default: redacted)")
1887
2703
  .action(async (opts) => {
1888
2704
  const { providersPull } = await import("./commands/providers.js");
1889
2705
  await providersPull(opts);
1890
2706
  });
2707
+ providers
2708
+ .command("set-key <providerId>")
2709
+ .description("Store your own API key override for a provider (BYOI)")
2710
+ .option("--workspace <id>", "Workspace ID for workspace-scoped override")
2711
+ .option("--api-key <key>", "API key (prompted if omitted)")
2712
+ .option("--pod-url <url>", "Pod URL override")
2713
+ .option("--hub-api-key <key>", "Hub API key override")
2714
+ .action(async (opts) => {
2715
+ const { providersSetKey } = await import("./commands/providers.js");
2716
+ await providersSetKey({ ...opts, providerId: opts.providerId });
2717
+ });
2718
+ providers
2719
+ .command("list-keys")
2720
+ .description("List user/workspace-level provider key overrides")
2721
+ .option("--workspace <id>", "Workspace ID to check")
2722
+ .option("--pod-url <url>", "Pod URL override")
2723
+ .option("--hub-api-key <key>", "Hub API key override")
2724
+ .action(async (opts) => {
2725
+ const { providersListKeys } = await import("./commands/providers.js");
2726
+ await providersListKeys(opts);
2727
+ });
1891
2728
  // ─── keys ────────────────────────────────────────────────────────────────────
1892
2729
  const keys = program
1893
2730
  .command("keys")
@@ -1895,9 +2732,10 @@ const keys = program
1895
2732
  keys
1896
2733
  .command("rotate")
1897
2734
  .description("Rotate your CLI key to pick up the latest scope set")
1898
- .action(async () => {
2735
+ .option("--show-key", "Print the new key in full if it could not be saved to the config")
2736
+ .action(async (opts) => {
1899
2737
  const { keysRotate } = await import("./commands/keys.js");
1900
- await keysRotate({});
2738
+ await keysRotate({ showKey: opts.showKey });
1901
2739
  });
1902
2740
  // Resolve a global `--pod <name>` before commander parses (it isn't registered
1903
2741
  // per-command). Fails fast with a clear message on an unknown profile name.