@synap-core/cli 1.11.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 (452) 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 +218 -168
  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 +1 -13
  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 +7 -1
  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 +193 -23
  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 +293 -20
  50. package/dist/commands/data.js.map +1 -1
  51. package/dist/commands/diagnose.d.ts +66 -4
  52. package/dist/commands/diagnose.js +285 -28
  53. package/dist/commands/diagnose.js.map +1 -1
  54. package/dist/commands/doc.d.ts +11 -4
  55. package/dist/commands/doc.js +39 -14
  56. package/dist/commands/doc.js.map +1 -1
  57. package/dist/commands/doctor.d.ts +112 -3
  58. package/dist/commands/doctor.js +657 -32
  59. package/dist/commands/doctor.js.map +1 -1
  60. package/dist/commands/explain.js +5 -5
  61. package/dist/commands/explain.js.map +1 -1
  62. package/dist/commands/import.d.ts +368 -13
  63. package/dist/commands/import.js +1064 -54
  64. package/dist/commands/import.js.map +1 -1
  65. package/dist/commands/init.d.ts +20 -9
  66. package/dist/commands/init.js +433 -973
  67. package/dist/commands/init.js.map +1 -1
  68. package/dist/commands/keys.d.ts +1 -0
  69. package/dist/commands/keys.js +15 -2
  70. package/dist/commands/keys.js.map +1 -1
  71. package/dist/commands/knowledge.d.ts +22 -6
  72. package/dist/commands/knowledge.js +117 -55
  73. package/dist/commands/knowledge.js.map +1 -1
  74. package/dist/commands/lens.d.ts +37 -1
  75. package/dist/commands/lens.js +192 -71
  76. package/dist/commands/lens.js.map +1 -1
  77. package/dist/commands/market-authoring.d.ts +29 -2
  78. package/dist/commands/market-authoring.js +348 -7
  79. package/dist/commands/market-authoring.js.map +1 -1
  80. package/dist/commands/market-official.d.ts +58 -0
  81. package/dist/commands/market-official.js +84 -0
  82. package/dist/commands/market-official.js.map +1 -0
  83. package/dist/commands/market.d.ts +400 -16
  84. package/dist/commands/market.js +1802 -146
  85. package/dist/commands/market.js.map +1 -1
  86. package/dist/commands/mcp.d.ts +60 -25
  87. package/dist/commands/mcp.js +251 -64
  88. package/dist/commands/mcp.js.map +1 -1
  89. package/dist/commands/open.d.ts +39 -9
  90. package/dist/commands/open.js +80 -25
  91. package/dist/commands/open.js.map +1 -1
  92. package/dist/commands/playbook.d.ts +42 -0
  93. package/dist/commands/playbook.js +162 -0
  94. package/dist/commands/playbook.js.map +1 -0
  95. package/dist/commands/pod.js +1 -1
  96. package/dist/commands/pod.js.map +1 -1
  97. package/dist/commands/pods.d.ts +5 -0
  98. package/dist/commands/pods.js +97 -116
  99. package/dist/commands/pods.js.map +1 -1
  100. package/dist/commands/proposals-actions.d.ts +62 -0
  101. package/dist/commands/proposals-actions.js +143 -8
  102. package/dist/commands/proposals-actions.js.map +1 -1
  103. package/dist/commands/proposals-await.d.ts +68 -0
  104. package/dist/commands/proposals-await.js +248 -0
  105. package/dist/commands/proposals-await.js.map +1 -0
  106. package/dist/commands/proton-setup.d.ts +73 -0
  107. package/dist/commands/proton-setup.js +577 -0
  108. package/dist/commands/proton-setup.js.map +1 -0
  109. package/dist/commands/providers.d.ts +23 -0
  110. package/dist/commands/providers.js +126 -1
  111. package/dist/commands/providers.js.map +1 -1
  112. package/dist/commands/raycast.js +6 -2
  113. package/dist/commands/raycast.js.map +1 -1
  114. package/dist/commands/rule.d.ts +150 -0
  115. package/dist/commands/rule.js +447 -0
  116. package/dist/commands/rule.js.map +1 -0
  117. package/dist/commands/session-wait.d.ts +111 -0
  118. package/dist/commands/session-wait.js +209 -0
  119. package/dist/commands/session-wait.js.map +1 -0
  120. package/dist/commands/sessions.d.ts +52 -0
  121. package/dist/commands/sessions.js +478 -58
  122. package/dist/commands/sessions.js.map +1 -1
  123. package/dist/commands/skill-manage.d.ts +21 -0
  124. package/dist/commands/skill-manage.js +136 -19
  125. package/dist/commands/skill-manage.js.map +1 -1
  126. package/dist/commands/skills.js +16 -0
  127. package/dist/commands/skills.js.map +1 -1
  128. package/dist/commands/status.d.ts +1 -4
  129. package/dist/commands/status.js +5 -34
  130. package/dist/commands/status.js.map +1 -1
  131. package/dist/commands/statusline.backoff.test.d.ts +13 -0
  132. package/dist/commands/statusline.backoff.test.js +100 -0
  133. package/dist/commands/statusline.backoff.test.js.map +1 -0
  134. package/dist/commands/statusline.js +16 -9
  135. package/dist/commands/statusline.js.map +1 -1
  136. package/dist/commands/sync-status.d.ts +12 -0
  137. package/dist/commands/sync-status.js +125 -0
  138. package/dist/commands/sync-status.js.map +1 -0
  139. package/dist/commands/templates-retire.d.ts +34 -0
  140. package/dist/commands/templates-retire.js +192 -0
  141. package/dist/commands/templates-retire.js.map +1 -0
  142. package/dist/commands/tools.js +6 -21
  143. package/dist/commands/tools.js.map +1 -1
  144. package/dist/commands/tracks.d.ts +51 -0
  145. package/dist/commands/tracks.js +291 -0
  146. package/dist/commands/tracks.js.map +1 -0
  147. package/dist/commands/update.d.ts +3 -11
  148. package/dist/commands/update.js +5 -134
  149. package/dist/commands/update.js.map +1 -1
  150. package/dist/commands/upload.d.ts +41 -3
  151. package/dist/commands/upload.js +120 -18
  152. package/dist/commands/upload.js.map +1 -1
  153. package/dist/commands/vendor.d.ts +28 -0
  154. package/dist/commands/vendor.js +110 -0
  155. package/dist/commands/vendor.js.map +1 -0
  156. package/dist/commands/versions.js +6 -0
  157. package/dist/commands/versions.js.map +1 -1
  158. package/dist/commands/view.js +10 -5
  159. package/dist/commands/view.js.map +1 -1
  160. package/dist/commands/whoami.d.ts +23 -3
  161. package/dist/commands/whoami.js +169 -23
  162. package/dist/commands/whoami.js.map +1 -1
  163. package/dist/commands/worker.d.ts +122 -0
  164. package/dist/commands/worker.js +435 -0
  165. package/dist/commands/worker.js.map +1 -0
  166. package/dist/commands/worker.test.d.ts +1 -0
  167. package/dist/commands/worker.test.js +116 -0
  168. package/dist/commands/worker.test.js.map +1 -0
  169. package/dist/commands/workspace-ops.d.ts +35 -0
  170. package/dist/commands/workspace-ops.js +153 -0
  171. package/dist/commands/workspace-ops.js.map +1 -0
  172. package/dist/index.d.ts +6 -6
  173. package/dist/index.js +900 -180
  174. package/dist/index.js.map +1 -1
  175. package/dist/lib/agent-connections.d.ts +31 -0
  176. package/dist/lib/agent-connections.js +33 -0
  177. package/dist/lib/agent-connections.js.map +1 -0
  178. package/dist/lib/agent-detect.d.ts +27 -0
  179. package/dist/lib/agent-detect.js +71 -0
  180. package/dist/lib/agent-detect.js.map +1 -0
  181. package/dist/lib/agent-door.d.ts +138 -0
  182. package/dist/lib/agent-door.js +426 -0
  183. package/dist/lib/agent-door.js.map +1 -0
  184. package/dist/lib/agents-config.d.ts +2 -0
  185. package/dist/lib/agents-config.js +4 -9
  186. package/dist/lib/agents-config.js.map +1 -1
  187. package/dist/lib/approval-poll.d.ts +31 -2
  188. package/dist/lib/approval-poll.js +20 -7
  189. package/dist/lib/approval-poll.js.map +1 -1
  190. package/dist/lib/atomic-file.d.ts +48 -0
  191. package/dist/lib/atomic-file.js +129 -0
  192. package/dist/lib/atomic-file.js.map +1 -0
  193. package/dist/lib/auth.d.ts +14 -9
  194. package/dist/lib/auth.js +44 -61
  195. package/dist/lib/auth.js.map +1 -1
  196. package/dist/lib/batch-approval.d.ts +85 -0
  197. package/dist/lib/batch-approval.js +170 -0
  198. package/dist/lib/batch-approval.js.map +1 -0
  199. package/dist/lib/capture-lane.d.ts +6 -5
  200. package/dist/lib/capture-lane.js +6 -5
  201. package/dist/lib/capture-lane.js.map +1 -1
  202. package/dist/lib/capture-structure.d.ts +230 -11
  203. package/dist/lib/capture-structure.js +251 -13
  204. package/dist/lib/capture-structure.js.map +1 -1
  205. package/dist/lib/catalog-templates.d.ts +59 -0
  206. package/dist/lib/catalog-templates.js +126 -0
  207. package/dist/lib/catalog-templates.js.map +1 -0
  208. package/dist/lib/cli-build.d.ts +19 -0
  209. package/dist/lib/cli-build.js +37 -0
  210. package/dist/lib/cli-build.js.map +1 -0
  211. package/dist/lib/conversation-headers.d.ts +18 -0
  212. package/dist/lib/conversation-headers.js +32 -0
  213. package/dist/lib/conversation-headers.js.map +1 -0
  214. package/dist/lib/cp-packages.d.ts +167 -11
  215. package/dist/lib/cp-packages.js +253 -21
  216. package/dist/lib/cp-packages.js.map +1 -1
  217. package/dist/lib/credential-class.d.ts +117 -0
  218. package/dist/lib/credential-class.js +116 -0
  219. package/dist/lib/credential-class.js.map +1 -0
  220. package/dist/lib/describe-lens.d.ts +35 -0
  221. package/dist/lib/describe-lens.js +40 -0
  222. package/dist/lib/describe-lens.js.map +1 -1
  223. package/dist/lib/device-connect.d.ts +125 -0
  224. package/dist/lib/device-connect.js +256 -0
  225. package/dist/lib/device-connect.js.map +1 -0
  226. package/dist/lib/directory-lens.d.ts +99 -0
  227. package/dist/lib/directory-lens.js +187 -0
  228. package/dist/lib/directory-lens.js.map +1 -0
  229. package/dist/lib/exporter-coverage.d.ts +71 -0
  230. package/dist/lib/exporter-coverage.js +97 -0
  231. package/dist/lib/exporter-coverage.js.map +1 -0
  232. package/dist/lib/hub-client.d.ts +29 -8
  233. package/dist/lib/hub-client.js +328 -55
  234. package/dist/lib/hub-client.js.map +1 -1
  235. package/dist/lib/init-connect.d.ts +41 -0
  236. package/dist/lib/init-connect.js +75 -0
  237. package/dist/lib/init-connect.js.map +1 -0
  238. package/dist/lib/init-exit.d.ts +24 -0
  239. package/dist/lib/init-exit.js +41 -0
  240. package/dist/lib/init-exit.js.map +1 -0
  241. package/dist/lib/init-flow.d.ts +25 -0
  242. package/dist/lib/init-flow.js +40 -0
  243. package/dist/lib/init-flow.js.map +1 -0
  244. package/dist/lib/init-handoff.d.ts +16 -0
  245. package/dist/lib/init-handoff.js +19 -0
  246. package/dist/lib/init-handoff.js.map +1 -0
  247. package/dist/lib/init-output.d.ts +18 -0
  248. package/dist/lib/init-output.js +43 -0
  249. package/dist/lib/init-output.js.map +1 -0
  250. package/dist/lib/init-pod.d.ts +45 -0
  251. package/dist/lib/init-pod.js +36 -0
  252. package/dist/lib/init-pod.js.map +1 -0
  253. package/dist/lib/installed.d.ts +210 -11
  254. package/dist/lib/installed.js +234 -36
  255. package/dist/lib/installed.js.map +1 -1
  256. package/dist/lib/key-approval.d.ts +57 -0
  257. package/dist/lib/key-approval.js +117 -0
  258. package/dist/lib/key-approval.js.map +1 -0
  259. package/dist/lib/key-diagnosis.d.ts +56 -0
  260. package/dist/lib/key-diagnosis.js +227 -0
  261. package/dist/lib/key-diagnosis.js.map +1 -0
  262. package/dist/lib/key-doctor.d.ts +136 -0
  263. package/dist/lib/key-doctor.js +286 -0
  264. package/dist/lib/key-doctor.js.map +1 -0
  265. package/dist/lib/key-locations.d.ts +120 -0
  266. package/dist/lib/key-locations.js +326 -0
  267. package/dist/lib/key-locations.js.map +1 -0
  268. package/dist/lib/key-source.d.ts +66 -0
  269. package/dist/lib/key-source.js +106 -0
  270. package/dist/lib/key-source.js.map +1 -0
  271. package/dist/lib/key-state.d.ts +70 -0
  272. package/dist/lib/key-state.js +115 -0
  273. package/dist/lib/key-state.js.map +1 -0
  274. package/dist/lib/kind-package.d.ts +181 -0
  275. package/dist/lib/kind-package.js +679 -0
  276. package/dist/lib/kind-package.js.map +1 -0
  277. package/dist/lib/knowledge-title.d.ts +24 -0
  278. package/dist/lib/knowledge-title.js +46 -0
  279. package/dist/lib/knowledge-title.js.map +1 -0
  280. package/dist/lib/knowledge-title.test.d.ts +1 -0
  281. package/dist/lib/knowledge-title.test.js +34 -0
  282. package/dist/lib/knowledge-title.test.js.map +1 -0
  283. package/dist/lib/machine-instance.d.ts +64 -0
  284. package/dist/lib/machine-instance.js +142 -0
  285. package/dist/lib/machine-instance.js.map +1 -0
  286. package/dist/lib/mcp-bridge.d.ts +42 -0
  287. package/dist/lib/mcp-bridge.js +147 -0
  288. package/dist/lib/mcp-bridge.js.map +1 -0
  289. package/dist/lib/mcp-bridge.test.d.ts +1 -0
  290. package/dist/lib/mcp-bridge.test.js +90 -0
  291. package/dist/lib/mcp-bridge.test.js.map +1 -0
  292. package/dist/lib/mcp-call.d.ts +30 -0
  293. package/dist/lib/mcp-call.js +107 -0
  294. package/dist/lib/mcp-call.js.map +1 -0
  295. package/dist/lib/mcp-snippets.d.ts +53 -0
  296. package/dist/lib/mcp-snippets.js +82 -0
  297. package/dist/lib/mcp-snippets.js.map +1 -0
  298. package/dist/lib/next-steps.d.ts +8 -0
  299. package/dist/lib/next-steps.js +31 -1
  300. package/dist/lib/next-steps.js.map +1 -1
  301. package/dist/lib/package-wire.d.ts +179 -0
  302. package/dist/lib/package-wire.js +68 -0
  303. package/dist/lib/package-wire.js.map +1 -0
  304. package/dist/lib/pair-link.d.ts +38 -0
  305. package/dist/lib/pair-link.js +68 -0
  306. package/dist/lib/pair-link.js.map +1 -0
  307. package/dist/lib/pod-wide.d.ts +40 -0
  308. package/dist/lib/pod-wide.js +46 -0
  309. package/dist/lib/pod-wide.js.map +1 -0
  310. package/dist/lib/pod.d.ts +66 -112
  311. package/dist/lib/pod.js +134 -412
  312. package/dist/lib/pod.js.map +1 -1
  313. package/dist/lib/project-ref.d.ts +9 -3
  314. package/dist/lib/project-ref.js +10 -4
  315. package/dist/lib/project-ref.js.map +1 -1
  316. package/dist/lib/review-credential.d.ts +119 -0
  317. package/dist/lib/review-credential.js +147 -0
  318. package/dist/lib/review-credential.js.map +1 -0
  319. package/dist/lib/safe-open.d.ts +49 -0
  320. package/dist/lib/safe-open.js +119 -0
  321. package/dist/lib/safe-open.js.map +1 -0
  322. package/dist/lib/session-lens.d.ts +40 -1
  323. package/dist/lib/session-lens.js +54 -4
  324. package/dist/lib/session-lens.js.map +1 -1
  325. package/dist/lib/superwhisper-import.d.ts +8 -0
  326. package/dist/lib/superwhisper-import.js +14 -7
  327. package/dist/lib/superwhisper-import.js.map +1 -1
  328. package/dist/lib/superwhisper-title.test.d.ts +1 -0
  329. package/dist/lib/superwhisper-title.test.js +18 -0
  330. package/dist/lib/superwhisper-title.test.js.map +1 -0
  331. package/dist/lib/targets.d.ts +134 -10
  332. package/dist/lib/targets.js +962 -258
  333. package/dist/lib/targets.js.map +1 -1
  334. package/dist/lib/template-file.d.ts +4 -0
  335. package/dist/lib/template-file.js.map +1 -1
  336. package/dist/lib/template-lifecycle.d.ts +217 -0
  337. package/dist/lib/template-lifecycle.js +433 -0
  338. package/dist/lib/template-lifecycle.js.map +1 -0
  339. package/dist/lib/template-steps.d.ts +88 -0
  340. package/dist/lib/template-steps.js +118 -0
  341. package/dist/lib/template-steps.js.map +1 -0
  342. package/dist/lib/time-ago.d.ts +2 -0
  343. package/dist/lib/time-ago.js +15 -0
  344. package/dist/lib/time-ago.js.map +1 -0
  345. package/dist/utils/logger.js +5 -2
  346. package/dist/utils/logger.js.map +1 -1
  347. package/package.json +23 -17
  348. package/skills/README.md +12 -10
  349. package/skills/agent-os/SKILL.md +214 -99
  350. package/skills/manifest.json +1 -1
  351. package/skills/onboard/SKILL.md +55 -26
  352. package/skills/synap/README.md +5 -8
  353. package/skills/synap/SKILL.md +1086 -283
  354. package/skills/synap/_order.txt +7 -0
  355. package/skills/synap/_skill.yaml +1 -1
  356. package/skills/synap/automations.md +15 -0
  357. package/skills/synap/capture.md +5 -0
  358. package/skills/synap/cli-operations.md +24 -18
  359. package/skills/synap/concepts.md +28 -0
  360. package/skills/synap/content-os.md +10 -1
  361. package/skills/synap/creative-loop.md +15 -10
  362. package/skills/synap/crm.md +21 -52
  363. package/skills/synap/document-embeds.md +141 -47
  364. package/skills/synap/escalation-ladder.md +20 -10
  365. package/skills/synap/focus-sessions.md +47 -21
  366. package/skills/synap/from-intent.md +142 -0
  367. package/skills/synap/governance-rules.md +4 -4
  368. package/skills/synap/governance.md +10 -7
  369. package/skills/synap/hand-off.md +33 -0
  370. package/skills/synap/inline-patterns.md +15 -7
  371. package/skills/synap/lenses.md +33 -15
  372. package/skills/synap/linking.md +41 -40
  373. package/skills/synap/mcp-remote-servers.md +64 -0
  374. package/skills/synap/mental-model.md +17 -15
  375. package/skills/synap/operating-mode-rules.md +1 -1
  376. package/skills/synap/processes.md +118 -0
  377. package/skills/synap/quick-reference.md +5 -1
  378. package/skills/synap/reading.md +3 -1
  379. package/skills/synap/reflexes.md +12 -42
  380. package/skills/synap/viewframe-cells.md +38 -5
  381. package/skills/synap/work-flow.md +1 -1
  382. package/skills/synap/work-model.md +96 -0
  383. package/skills/synap/worked-examples.md +4 -5
  384. package/skills/synap/workspace-design.md +14 -10
  385. package/skills/synap/writes.md +97 -24
  386. package/skills/synap/writing-titles.md +63 -0
  387. package/skills/synap-app/SKILL.md +2 -2
  388. package/skills/synap-app/_skill.yaml +1 -1
  389. package/skills/synap-app/install-commands.md +1 -1
  390. package/skills/synap-design/SKILL.md +474 -0
  391. package/skills/synap-design/_order.txt +4 -0
  392. package/skills/synap-design/_skill.yaml +21 -0
  393. package/skills/synap-design/loop.md +134 -0
  394. package/skills/synap-design/overview.md +44 -0
  395. package/skills/synap-design/principles.md +130 -0
  396. package/skills/synap-design/system.md +133 -0
  397. package/skills/synap-market/SKILL.md +306 -16
  398. package/skills/synap-market/_skill.yaml +1 -1
  399. package/skills/synap-market/author-and-publish.md +145 -8
  400. package/skills/synap-market/governance-and-catalog.md +7 -0
  401. package/skills/synap-market/install.md +79 -1
  402. package/skills/synap-market/overview.md +74 -6
  403. package/skills/synap-schema/SKILL.md +172 -10
  404. package/skills/synap-schema/_order.txt +2 -0
  405. package/skills/synap-schema/_skill.yaml +4 -2
  406. package/skills/synap-schema/common-mistakes.md +4 -2
  407. package/skills/synap-schema/discover-profiles.md +1 -1
  408. package/skills/synap-schema/entity-scope.md +15 -3
  409. package/skills/synap-schema/extend-first.md +29 -0
  410. package/skills/synap-schema/extend-vs-create.md +1 -1
  411. package/skills/synap-schema/intent-vocabulary-ssot.md +111 -0
  412. package/skills/synap-schema/property-types.md +1 -1
  413. package/skills/synap-schema/relations.md +1 -1
  414. package/skills/synap-ui/SKILL.md +19 -12
  415. package/skills/synap-ui/_skill.yaml +1 -1
  416. package/skills/synap-ui/ai-companion.md +1 -1
  417. package/skills/synap-ui/arranging-bento.md +2 -0
  418. package/skills/synap-ui/bento-layouts.md +10 -10
  419. package/skills/synap-ui/bento-recipes.md +313 -543
  420. package/skills/synap-ui/four-builds.md +5 -0
  421. package/skills/synap-ui/view-types.md +2 -2
  422. package/skills/synap-ui/widget-catalog.md +22 -278
  423. package/templates/automations/bookmark-enrichment.automation.json +290 -13
  424. package/templates/automations/url-bookmark-capture.automation.json +73 -11
  425. package/templates/skills/authoring-playbooks/SKILL.md +6 -3
  426. package/templates/skills/authoring-playbooks/reference/stages-contract.md +15 -0
  427. package/templates/skills/authoring-playbooks/reference/worked-example.md +3 -0
  428. package/dist/commands/infra.d.ts +0 -21
  429. package/dist/commands/infra.js +0 -262
  430. package/dist/commands/infra.js.map +0 -1
  431. package/dist/commands/template.d.ts +0 -42
  432. package/dist/commands/template.js +0 -191
  433. package/dist/commands/template.js.map +0 -1
  434. package/dist/lib/browser-auth.d.ts +0 -35
  435. package/dist/lib/browser-auth.js +0 -170
  436. package/dist/lib/browser-auth.js.map +0 -1
  437. package/dist/lib/openclaw.d.ts +0 -32
  438. package/dist/lib/openclaw.js +0 -164
  439. package/dist/lib/openclaw.js.map +0 -1
  440. package/dist/lib/seed.d.ts +0 -13
  441. package/dist/lib/seed.js +0 -135
  442. package/dist/lib/seed.js.map +0 -1
  443. package/node_modules/@synap/hub-rest-client/dist/index.cjs +0 -1261
  444. package/node_modules/@synap/hub-rest-client/dist/index.d.cts +0 -1647
  445. package/node_modules/@synap/hub-rest-client/dist/index.d.ts +0 -1647
  446. package/node_modules/@synap/hub-rest-client/dist/index.js +0 -1231
  447. package/node_modules/@synap/hub-rest-client/package.json +0 -45
  448. package/node_modules/@synap/hub-rest-client/src/client.ts +0 -1631
  449. package/node_modules/@synap/hub-rest-client/src/errors.ts +0 -30
  450. package/node_modules/@synap/hub-rest-client/src/index.ts +0 -153
  451. package/node_modules/@synap/hub-rest-client/src/setup.ts +0 -133
  452. package/node_modules/@synap/hub-rest-client/src/types.ts +0 -1299
@@ -1,13 +1,129 @@
1
1
  import chalk from "chalk";
2
2
  import { log } from "../utils/logger.js";
3
- import { resolveHubConfig, hubGet, hubPatch, hubPost, writeActiveSessionId, clearActiveSessionId, readActiveSessionId, renderHubError, } from "../lib/hub-client.js";
3
+ import { resolveHubConfig, hubGet, hubPatch, hubPost, attachActiveSessionId, detachActiveSessionId, resolveActiveSessionId, renderHubError, HubError, } from "../lib/hub-client.js";
4
+ import { readFileSync, existsSync } from "fs";
5
+ import { resolve } from "path";
6
+ /**
7
+ * The three session-population lenses ("kind"), and their human labels.
8
+ *
9
+ * `@synap-core/types/vocabulary` (`resolveStatusLabel`) is the SSOT for this —
10
+ * `work`/`run`/`receipt` are pinned there verbatim (`.claude/rules/
11
+ * vocabulary.md` forbids a local `charAt(0).toUpperCase()` guess). But this
12
+ * package cannot import it: the CLI links only `@synap-core/workspace-
13
+ * templates` (a real npm dep already), and `@synap-core/types` pulls in
14
+ * drizzle-orm/drizzle-zod/yjs as transitive deps for a globally-installed
15
+ * binary — an install-topology change, not a labelling fix. Same shape as
16
+ * `KIND_HEADINGS` in `market.ts`: a local table, pinned by a source-scan
17
+ * parity test (`test/session-kind-vocabulary-parity.test.ts`) against the
18
+ * registry across the repo boundary, so it cannot silently drift.
19
+ */
20
+ const SESSION_KIND_LABELS = {
21
+ work: "Work",
22
+ run: "Run",
23
+ receipt: "Receipt",
24
+ };
25
+ export function sessionKindLabel(kind) {
26
+ return SESSION_KIND_LABELS[kind] ?? kind;
27
+ }
28
+ const SESSION_KIND_FILTERS = ["work", "run", "receipt", "all"];
29
+ /**
30
+ * The name to show for a session: mirrors `resolveSessionTitle` in
31
+ * `synap-backend/packages/types/src/focus-sessions/title.ts` — the
32
+ * platform's ONE resolver (title when set, else the goal's first line,
33
+ * clipped at a word boundary). This package cannot import that file (see the
34
+ * doc comment on `SESSION_KIND_LABELS` above for why), so only the DISPLAY
35
+ * half of the rule is mirrored: the row's `title` already arrives
36
+ * server-normalized (HTML-entity decoded, one line), so the write-path
37
+ * decode step has nothing to do here. Pinned by
38
+ * `test/session-title-resolver-parity.test.ts`, which runs the registry's
39
+ * OWN fixtures (from `title.test.ts`) through this copy.
40
+ */
41
+ const SESSION_TITLE_FALLBACK_MAX = 80;
42
+ function oneLine(value) {
43
+ return value.replace(/\s+/g, " ").trim();
44
+ }
45
+ function clipAtWordBoundary(line, max) {
46
+ if (line.length <= max)
47
+ return line;
48
+ let cut = line.slice(0, Math.max(1, max - 1));
49
+ const lastSpace = cut.lastIndexOf(" ");
50
+ // Only back off to a word boundary when it keeps most of the budget; a
51
+ // single very long first word is cut mid-word rather than reduced to
52
+ // nothing.
53
+ if (lastSpace >= Math.floor(max / 2))
54
+ cut = cut.slice(0, lastSpace);
55
+ return `${cut.replace(/[\s,;:.\-–—]+$/, "")}…`;
56
+ }
57
+ export function resolveSessionTitle(session, opts = {}) {
58
+ const title = typeof session.title === "string" ? oneLine(session.title) : "";
59
+ if (title)
60
+ return title;
61
+ const goal = typeof session.goal === "string" ? session.goal : "";
62
+ const firstLine = goal
63
+ .split(/\r?\n/)
64
+ .map(oneLine)
65
+ .find((l) => l.length > 0) ?? "";
66
+ return clipAtWordBoundary(firstLine, opts.maxLength ?? SESSION_TITLE_FALLBACK_MAX);
67
+ }
68
+ /**
69
+ * Session-evaluation verdicts (`session_evaluations.verdict`). SSOT is
70
+ * `@synap-core/types/vocabulary`'s `STATUS_LABELS` (`pass`/`fail`/
71
+ * `unmeasured`) — same cross-boundary constraint as `SESSION_KIND_LABELS`
72
+ * above, mirrored here and pinned by
73
+ * `test/verdict-label-vocabulary-parity.test.ts`.
74
+ */
75
+ const VERDICT_LABELS = {
76
+ pass: "Passed",
77
+ fail: "Failed",
78
+ unmeasured: "Not checked",
79
+ };
80
+ function verdictLabel(verdict) {
81
+ return VERDICT_LABELS[verdict] ?? verdict;
82
+ }
83
+ /**
84
+ * One-glance verdict summary for a list/detail row: "1 unmet" when a
85
+ * required criterion is failing or unmeasured (the thing worth flagging),
86
+ * else "2/3 ✓". `null` when the session carries no criteria at all — most
87
+ * sessions don't, and a bare session shouldn't grow a verdict column.
88
+ */
89
+ function verdictSummary(v) {
90
+ const verdict = v;
91
+ if (!verdict || typeof verdict.total !== "number" || verdict.total === 0) {
92
+ return null;
93
+ }
94
+ if (verdict.requiredUnmet && verdict.requiredUnmet > 0) {
95
+ return `${verdict.requiredUnmet} unmet`;
96
+ }
97
+ return `${verdict.passed ?? 0}/${verdict.total} ✓`;
98
+ }
99
+ /** Human phrasing for where an attach landed — so scope is never a mystery. */
100
+ function describeAttachTarget(target) {
101
+ return target === "session-lens"
102
+ ? "in this Claude Code session (~/.synap/lenses)"
103
+ : "in this working tree (.synap/lens.json)";
104
+ }
4
105
  // ─── startSession ─────────────────────────────────────────────────────────────
5
106
  export async function startSession(opts) {
6
107
  try {
7
108
  const cfg = await resolveHubConfig(opts);
8
- const workspaceId = opts.workspace || cfg.workspaceId;
9
- if (!workspaceId) {
10
- console.error(chalk.red("Error: --workspace <id> is required (or set active workspace with 'synap use')"));
109
+ // LENS PARITY. `resolveHubConfig` already resolves the active project
110
+ // ("write paths should consume resolveHubConfig().projectId" —
111
+ // lib/hub-client.ts), and `synap lens` / `synap capture` both honour it.
112
+ // This door read `cfg.workspaceId` and dropped `cfg.projectId` on the
113
+ // floor, so the same terminal answered differently depending on which
114
+ // command you used: capture filed into the project, `session start` wrote
115
+ // projectId: null. The value was one field away the whole time.
116
+ // A track names its own project (the pod refuses a different projectId):
117
+ // with `--track`, only an EXPLICIT --workspace / --project is sent, never
118
+ // the ambient lens, which may point at another project.
119
+ const workspaceId = opts.track ? opts.workspace : opts.workspace || cfg.workspaceId;
120
+ const projectId = opts.track ? opts.project : opts.project || cfg.projectId;
121
+ // The POD accepts workspaceId OR projectId ("Provide a workspaceId or a
122
+ // projectId" — rest/focus-sessions.ts). Requiring a workspace here was a
123
+ // CLI-only rule, stricter than the contract, and it made a project-scoped
124
+ // session unreachable from the terminal.
125
+ if (!workspaceId && !projectId && !opts.track) {
126
+ console.error(chalk.red("Error: no workspace or project in scope — pass --workspace <id> or --project <id>, or set one with 'synap use' / 'synap project use'"));
11
127
  process.exit(1);
12
128
  }
13
129
  // Resolve caller userId from the hub key
@@ -17,33 +133,93 @@ export async function startSession(opts) {
17
133
  console.error(chalk.red("Error: could not resolve userId from /users/me"));
18
134
  process.exit(1);
19
135
  }
20
- const body = {
21
- workspaceId,
22
- userId,
23
- goal: opts.goal,
24
- };
136
+ const body = { userId, goal: opts.goal };
137
+ // Send only what is in scope: the pod's refine accepts either, and sending
138
+ // an undefined workspaceId would fail the "min(1)" on the wire.
139
+ if (workspaceId)
140
+ body.workspaceId = workspaceId;
141
+ if (projectId)
142
+ body.projectId = projectId;
143
+ if (opts.track)
144
+ body.trackId = opts.track;
25
145
  if (opts.taskId)
26
146
  body.correlationId = `task:${opts.taskId}`;
147
+ // `--no-template` → opts.template === false → send an explicit `null`,
148
+ // which SKIPS matching (`TEMPLATE_OPT_OUT` in match-session-template.ts).
149
+ // `--template <id>` → send that id, the one way a playbook binds. Neither
150
+ // → omit, so the start comes back with the pod's playbooks to choose from
151
+ // (suggestions; nothing is applied).
152
+ if (opts.template === false)
153
+ body.templateId = null;
154
+ else if (opts.template)
155
+ body.templateId = opts.template;
156
+ // Detour push: the pod records `session --spawned_from--> session` and (with
157
+ // --suspended-intent) writes the "what were you about to do" line onto the
158
+ // PARENT and a `parent --blocked_by--> this session` edge. The parent is
159
+ // NOT closed or paused — it waits, and popping back is `synap session attach <parent>`.
160
+ if (opts.parent)
161
+ body.parentSessionId = opts.parent;
162
+ if (opts.suspendedIntent)
163
+ body.suspendedIntent = opts.suspendedIntent;
164
+ if (opts.criteria) {
165
+ const path = resolve(process.cwd(), opts.criteria);
166
+ if (!existsSync(path)) {
167
+ console.error(chalk.red(`Error: --criteria file not found: ${path}`));
168
+ process.exit(1);
169
+ return;
170
+ }
171
+ try {
172
+ body.criteria = JSON.parse(readFileSync(path, "utf-8"));
173
+ }
174
+ catch (parseErr) {
175
+ console.error(chalk.red(`Error: --criteria file is not valid JSON — ${parseErr.message}`));
176
+ process.exit(1);
177
+ return;
178
+ }
179
+ }
27
180
  const session = (await hubPost("/focus-sessions", body, cfg));
28
181
  if (opts.json) {
29
182
  console.log(JSON.stringify(session, null, 2));
30
183
  return;
31
184
  }
185
+ // A governed start comes back as a PROPOSAL, not a session: say so, and
186
+ // attach nothing — there is no session id to attach yet.
187
+ if (session.status === "proposed" && typeof session.proposalId === "string") {
188
+ log.warn("Starting this session is queued for your review — not started yet.");
189
+ if (typeof session.reviewUrl === "string")
190
+ log.hint(`Review: ${session.reviewUrl}`);
191
+ else
192
+ log.hint(`Proposal ${session.proposalId} — see: synap proposals list`);
193
+ return;
194
+ }
32
195
  const id = String(session.id ?? "");
33
- // Auto-attach: make this session active for the current terminal (per-CWD)
34
- // AND for this Claude Code session's lens (per-session), so the statusline
35
- // and every scoped call reflect it.
36
- writeActiveSessionId(id);
37
- const { getClaudeSessionId, writeLens } = await import("../lib/session-lens.js");
38
- const cs = getClaudeSessionId();
39
- if (cs)
40
- writeLens(cs, { focusSessionId: id });
196
+ // Auto-attach: make this session active so the statusline and every scoped
197
+ // call reflect it. Lands on the Claude-session lens inside Claude Code,
198
+ // otherwise on this working tree's directory lens.
199
+ const target = attachActiveSessionId(id);
41
200
  log.success(`Session started and attached`);
42
201
  console.log(` ID: ${chalk.bold(id)}`);
202
+ console.log(` Name: ${chalk.white(resolveSessionTitle(session))}`);
43
203
  console.log(` Goal: ${chalk.white(opts.goal)}`);
44
204
  if (opts.taskId)
45
205
  console.log(` Task: ${chalk.dim(opts.taskId)}`);
46
- log.dim(` Active in this terminal (.synap-session)`);
206
+ if (opts.parent)
207
+ console.log(` Forked from: ${chalk.dim(opts.parent)}`);
208
+ if (session.adopted === true) {
209
+ log.dim(` Continued your auto-opened session`);
210
+ }
211
+ // `playbooks` (SessionPlaybookCandidates) is present iff matching ran —
212
+ // see match-session-template.ts. SUGGESTIONS ONLY: nothing was applied, so
213
+ // say what fits and how to bind one, never that one is in force.
214
+ const playbooks = session.playbooks;
215
+ if (playbooks?.candidates?.length) {
216
+ log.dim(` Playbooks that fit this goal (none applied):`);
217
+ for (const c of playbooks.candidates) {
218
+ log.dim(` - ${c.name} (${c.reason})`);
219
+ log.dim(` ${chalk.dim(`--template ${c.id}`)}`);
220
+ }
221
+ }
222
+ log.dim(` Active ${describeAttachTarget(target)}`);
47
223
  console.log();
48
224
  // Print the ID alone on a final line so scripts can grab it easily
49
225
  console.log(id);
@@ -56,18 +232,37 @@ export async function startSession(opts) {
56
232
  // ─── listSessions ─────────────────────────────────────────────────────────────
57
233
  export async function listSessions(opts) {
58
234
  try {
235
+ // Validate at the edge, same reasoning as `--progress` above: a typo should
236
+ // fail here with the legal values, not reach the pod and bounce as a raw
237
+ // zod echo.
238
+ if (opts.kind !== undefined &&
239
+ !SESSION_KIND_FILTERS.includes(opts.kind)) {
240
+ console.error(chalk.red(`Error: --kind must be one of ${SESSION_KIND_FILTERS.join(" | ")} (got ${JSON.stringify(opts.kind)}).`));
241
+ process.exit(1);
242
+ }
59
243
  const cfg = await resolveHubConfig(opts);
60
244
  const params = {};
61
245
  // Fall back to the active workspace (config/env) like `start` does — the
62
246
  // focus-sessions REST requires workspaceId, and an operator with an active
63
247
  // workspace shouldn't have to repeat --workspace on every session command.
64
- const wsId = opts.workspace || cfg.workspaceId;
248
+ // `--project` is a scope of its own (the pod accepts projectId alone,
249
+ // project-member floor): a project's sessions span spaces, so with it only
250
+ // an EXPLICIT --workspace narrows — never the ambient one, which would hide
251
+ // every session the project runs in another space.
252
+ const wsId = opts.project ? opts.workspace : opts.workspace || cfg.workspaceId;
65
253
  if (wsId)
66
254
  params.workspaceId = wsId;
255
+ if (opts.project)
256
+ params.projectId = opts.project;
67
257
  if (opts.status)
68
258
  params.status = opts.status;
69
259
  if (opts.limit)
70
260
  params.limit = parseInt(opts.limit, 10);
261
+ // Population lens (`services/focus-sessions/session-kind.ts` on the pod).
262
+ // The Hub REST door defaults to "all" itself, so omit rather than send a
263
+ // redundant param when the flag isn't given.
264
+ if (opts.kind && opts.kind !== "all")
265
+ params.kind = opts.kind;
71
266
  // The Hub REST GET /focus-sessions returns a bare array of sessions.
72
267
  // (Tolerate a { sessions: [...] } envelope too, for forward-compat.)
73
268
  const res = await hubGet("/focus-sessions", params, cfg);
@@ -90,8 +285,17 @@ export async function listSessions(opts) {
90
285
  ? chalk.yellow(status)
91
286
  : chalk.dim(status);
92
287
  const progress = typeof s.progress === "number" ? ` ${chalk.dim(`[${s.progress}%]`)}` : "";
288
+ // `kind` is projected on every row by the Hub REST door regardless of the
289
+ // `--kind` filter — an older pod without the kind wave simply omits it.
290
+ const kindLabel = typeof s.kind === "string" ? sessionKindLabel(s.kind) : null;
291
+ const kindCol = kindLabel ? ` ${chalk.dim(`[${kindLabel}]`)}` : "";
292
+ // Present only when the door lifts `verdict` onto the row (today: the
293
+ // single-session GET, not this list) — `verdictSummary` returns null on
294
+ // an absent field, so an older/list door just omits the marker.
295
+ const verdict = verdictSummary(s.verdict);
296
+ const verdictCol = verdict ? ` ${chalk.dim(`(${verdict})`)}` : "";
93
297
  // Full id — feeds straight into `synap session get/update/attach <id>`.
94
- console.log(` ${chalk.bold(String(s.id ?? ""))} ${statusColor}${progress} ${chalk.white(String(s.goal ?? ""))}`);
298
+ console.log(` ${chalk.bold(String(s.id ?? ""))} ${statusColor}${kindCol}${progress} ${chalk.white(resolveSessionTitle(s))}${verdictCol}`);
95
299
  }
96
300
  }
97
301
  catch (e) {
@@ -100,22 +304,62 @@ export async function listSessions(opts) {
100
304
  }
101
305
  }
102
306
  // ─── getSession ───────────────────────────────────────────────────────────────
307
+ /**
308
+ * Fetch a focus session by id.
309
+ *
310
+ * Prefer GET without workspaceId so project-scoped sessions (workspaceId null)
311
+ * resolve. Older pods still require the query param — if the bare GET fails
312
+ * with 400 and we have a workspace, retry with it.
313
+ */
314
+ async function fetchSessionById(id, cfg, workspaceHint) {
315
+ try {
316
+ return (await hubGet(`/focus-sessions/${id}`, {}, cfg));
317
+ }
318
+ catch (e) {
319
+ const wsId = workspaceHint || cfg.workspaceId;
320
+ if (e instanceof HubError && e.status === 400 && wsId) {
321
+ return (await hubGet(`/focus-sessions/${id}`, { workspaceId: wsId }, cfg));
322
+ }
323
+ throw e;
324
+ }
325
+ }
103
326
  export async function getSession(id, opts) {
104
327
  try {
105
328
  const cfg = await resolveHubConfig(opts);
106
- const params = {};
107
- const wsId = opts.workspace || cfg.workspaceId;
108
- if (wsId)
109
- params.workspaceId = wsId;
110
- const res = await hubGet(`/focus-sessions/${id}`, params, cfg);
329
+ const res = await fetchSessionById(id, cfg, opts.workspace);
111
330
  if (opts.json) {
112
331
  console.log(JSON.stringify(res, null, 2));
113
332
  return;
114
333
  }
115
334
  const s = res;
116
335
  log.info(`Session ${chalk.bold(String(s.id ?? ""))}`);
336
+ console.log(` Name: ${chalk.white(resolveSessionTitle(s))}`);
117
337
  console.log(` Goal: ${chalk.white(String(s.goal ?? ""))}`);
118
338
  console.log(` Status: ${chalk.cyan(String(s.status ?? ""))}`);
339
+ if (typeof s.kind === "string")
340
+ console.log(` Kind: ${chalk.dim(sessionKindLabel(s.kind))}`);
341
+ // `verdict` (SessionVerdict) is present iff the session carries criteria —
342
+ // see `test/verdict-label-vocabulary-parity.test.ts` for the label SSOT.
343
+ const verdict = s.verdict;
344
+ if (verdict && typeof verdict.total === "number" && verdict.total > 0) {
345
+ const summary = verdictSummary(verdict);
346
+ console.log(` Criteria: ${chalk.white(`${verdict.passed ?? 0}/${verdict.total}`)}` +
347
+ (summary && verdict.requiredUnmet
348
+ ? ` ${chalk.yellow(summary)}`
349
+ : ""));
350
+ }
351
+ // Project-scoped sessions have workspaceId null — show scope honestly.
352
+ if (s.workspaceId != null && s.workspaceId !== "") {
353
+ console.log(` Workspace: ${chalk.dim(String(s.workspaceId))}`);
354
+ }
355
+ else if (s.projectId) {
356
+ console.log(` Project: ${chalk.dim(String(s.projectId))} ${chalk.dim("(no workspace)")}`);
357
+ }
358
+ else {
359
+ console.log(` Scope: ${chalk.dim("project/unscoped (workspace null)")}`);
360
+ }
361
+ if (s.projectId && s.workspaceId)
362
+ console.log(` Project: ${chalk.dim(String(s.projectId))}`);
119
363
  if (typeof s.progress === "number")
120
364
  console.log(` Progress: ${s.progress}%`);
121
365
  if (s.templateId)
@@ -136,16 +380,31 @@ export async function getSession(id, opts) {
136
380
  export async function updateSession(id, opts) {
137
381
  try {
138
382
  const cfg = await resolveHubConfig(opts);
383
+ // The session is identified by `id` in the URL and `workspaceId` is
384
+ // `.optional()` on the pod's UpdateBodySchema — so demanding one here was a
385
+ // CLI-only rule that made an already-attached (or project-scoped) session
386
+ // un-updatable without re-stating a lens the terminal already holds.
139
387
  const wsId = opts.workspace || cfg.workspaceId;
140
- if (!wsId) {
141
- console.error(chalk.red("Error: no active workspace — pass --workspace <id> or set one with `synap use`"));
142
- process.exit(1);
388
+ const body = {};
389
+ if (wsId)
390
+ body.workspaceId = wsId;
391
+ // `progress` is `z.number().int().min(0).max(100)` on the pod. `parseInt`
392
+ // on prose yields NaN, which serialized to the wire and came back as a raw
393
+ // zod echo — an unreadable error for a mistake the CLI could name here.
394
+ // Validate at the edge and say what the flag actually takes.
395
+ if (opts.progress !== undefined) {
396
+ const pct = Number(opts.progress);
397
+ if (!Number.isInteger(pct) || pct < 0 || pct > 100) {
398
+ console.error(chalk.red(`Error: --progress takes a whole number 0-100 (got ${JSON.stringify(opts.progress)}).`));
399
+ log.hint("For a free-text note, use --goal to restate the goal, or capture the note: synap capture --type lesson --claim \"...\"");
400
+ process.exit(1);
401
+ }
402
+ body.progress = pct;
143
403
  }
144
- const body = { workspaceId: wsId };
145
- if (opts.progress !== undefined)
146
- body.progress = parseInt(opts.progress, 10);
147
404
  if (opts.status)
148
405
  body.status = opts.status;
406
+ if (opts.stage)
407
+ body.currentStage = opts.stage;
149
408
  if (opts.goal)
150
409
  body.goal = opts.goal;
151
410
  const res = await hubPatch(`/focus-sessions/${id}`, body, cfg);
@@ -158,6 +417,8 @@ export async function updateSession(id, opts) {
158
417
  log.dim(` progress → ${opts.progress}%`);
159
418
  if (opts.status)
160
419
  log.dim(` status → ${opts.status}`);
420
+ if (opts.stage)
421
+ log.dim(` stage → ${opts.stage}`);
161
422
  }
162
423
  catch (e) {
163
424
  renderHubError(e);
@@ -166,34 +427,27 @@ export async function updateSession(id, opts) {
166
427
  }
167
428
  // ─── attachSession ────────────────────────────────────────────────────────────
168
429
  export async function attachSession(id, opts) {
169
- // Validate the session exists before attaching
430
+ // Validate the session exists before attaching (project-scoped OK — no workspace required)
170
431
  try {
171
432
  const cfg = await resolveHubConfig(opts);
172
- const params = {};
173
- if (cfg.workspaceId)
174
- params.workspaceId = cfg.workspaceId;
175
- await hubGet(`/focus-sessions/${id}`, params, cfg);
433
+ await fetchSessionById(id, cfg, opts.workspace);
176
434
  }
177
435
  catch (e) {
178
436
  console.error(chalk.red("Error: session not found — " + e.message));
179
437
  process.exit(1);
180
438
  }
181
- writeActiveSessionId(id);
182
- const { getClaudeSessionId, writeLens } = await import("../lib/session-lens.js");
183
- const cs = getClaudeSessionId();
184
- if (cs)
185
- writeLens(cs, { focusSessionId: id });
439
+ const target = attachActiveSessionId(id);
186
440
  if (opts.json) {
187
- console.log(JSON.stringify({ ok: true, sessionId: id }));
441
+ console.log(JSON.stringify({ ok: true, sessionId: id, scope: target }));
188
442
  return;
189
443
  }
190
444
  log.success(`Session attached ${chalk.dim(id.slice(0, 8))}`);
445
+ log.dim(` Active ${describeAttachTarget(target)}`);
191
446
  log.dim(` All hub calls in this terminal will tag X-Session-Id: ${id.slice(0, 8)}…`);
192
447
  }
193
448
  // ─── detachSession ────────────────────────────────────────────────────────────
194
449
  export function detachSession(opts) {
195
- const current = readActiveSessionId();
196
- clearActiveSessionId();
450
+ const current = detachActiveSessionId();
197
451
  if (opts.json) {
198
452
  console.log(JSON.stringify({ ok: true }));
199
453
  return;
@@ -207,39 +461,205 @@ export function detachSession(opts) {
207
461
  }
208
462
  // ─── sessionStatus ────────────────────────────────────────────────────────────
209
463
  export function sessionStatus(opts) {
210
- const id = readActiveSessionId();
464
+ const id = resolveActiveSessionId();
211
465
  if (opts.json) {
212
466
  console.log(JSON.stringify({ sessionId: id ?? null }));
213
467
  return;
214
468
  }
215
469
  if (id) {
216
- log.info(`Active session: ${chalk.bold(id.slice(0, 8))}… ${chalk.dim("(from .synap-session or SYNAP_SESSION_ID)")}`);
470
+ log.info(`Active session: ${chalk.bold(id.slice(0, 8))}… ${chalk.dim("(SYNAP_SESSION_ID, session lens, or .synap/lens.json)")}`);
217
471
  }
218
472
  else {
219
473
  log.dim("No active session — run `synap session start` or `synap session attach <id>`");
220
474
  }
221
475
  }
222
- // ─── closeSession ─────────────────────────────────────────────────────────────
476
+ /**
477
+ * Close a focus session via Hub complete (proposal pack).
478
+ *
479
+ * Prefer POST /focus-sessions/:id/complete with `{ summary }` from `--recap`.
480
+ * On 404 (older pods without the route) fall back to PATCH status=closed and
481
+ * write verificationReport.summary — graceful degrade, pack unavailable.
482
+ *
483
+ * --workspace is optional: complete loads the row by id; PATCH also scopes
484
+ * from the row (workspaceId body is back-compat only).
485
+ */
223
486
  export async function closeSession(id, opts) {
224
487
  try {
225
488
  const cfg = await resolveHubConfig(opts);
226
489
  const wsId = opts.workspace || cfg.workspaceId;
227
- if (!wsId) {
228
- console.error(chalk.red("Error: no active workspace — pass --workspace <id> or set one with `synap use`"));
229
- process.exit(1);
490
+ let pack = null;
491
+ let usedPack = false;
492
+ try {
493
+ // Canonical close: completeFocusSession — returns proposal pack.
494
+ // Map CLI --recap → body.summary (server field; there is no `recap` key).
495
+ const body = {};
496
+ if (opts.recap)
497
+ body.summary = opts.recap;
498
+ // How the session ended. Validated at the edge (see index.ts) so a typo
499
+ // fails here with the three legal values, rather than reaching the pod
500
+ // and being rejected by a zod enum the user cannot see.
501
+ if (opts.as)
502
+ body.terminalStatus = opts.as;
503
+ pack = (await hubPost(`/focus-sessions/${id}/complete`, body, cfg));
504
+ usedPack = true;
505
+ }
506
+ catch (e) {
507
+ if (!(e instanceof HubError) || e.status !== 404)
508
+ throw e;
509
+ // Older pod: no complete route — close via PATCH and warn that pack is
510
+ // unavailable. verificationReport.summary is the field the complete path
511
+ // writes (complete-session.ts); UpdateBodySchema accepts it as z.unknown().
512
+ log.dim("Pack unavailable on this pod (POST …/complete → 404); falling back to status close.");
513
+ const patchBody = {
514
+ status: opts.as ?? "closed",
515
+ };
516
+ if (wsId)
517
+ patchBody.workspaceId = wsId;
518
+ if (opts.recap)
519
+ patchBody.verificationReport = { summary: opts.recap };
520
+ await hubPatch(`/focus-sessions/${id}`, patchBody, cfg);
230
521
  }
231
- // Close via PATCH — the /focus-sessions/:id endpoint sets closedAt automatically
232
- // when status transitions to "closed"
233
- const body = { workspaceId: wsId, status: "closed" };
234
- await hubPatch(`/focus-sessions/${id}`, body, cfg);
235
522
  // Detach if this was the active terminal session
236
- if (readActiveSessionId() === id)
237
- clearActiveSessionId();
523
+ if (resolveActiveSessionId(cfg.podUrl) === id)
524
+ detachActiveSessionId(cfg.podUrl);
525
+ if (opts.json) {
526
+ if (usedPack && pack) {
527
+ console.log(JSON.stringify(pack, null, 2));
528
+ }
529
+ else {
530
+ console.log(JSON.stringify({
531
+ ok: true,
532
+ status: "closed",
533
+ pack: false,
534
+ summary: opts.recap ?? null,
535
+ }, null, 2));
536
+ }
537
+ return;
538
+ }
539
+ const sessionStatus = (pack?.session && String(pack.session.status ?? "")) ||
540
+ pack?.status ||
541
+ "closed";
542
+ // Close never blocks on criteria — a required-but-unmet criterion is a
543
+ // flag on the closed session, never a refusal.
544
+ const closeVerdict = pack?.verdict && pack.verdict.requiredUnmet
545
+ ? ` · ${pack.verdict.requiredUnmet} criteri${pack.verdict.requiredUnmet === 1 ? "on" : "a"} not met`
546
+ : "";
547
+ log.success(`Session closed ${chalk.dim(id.slice(0, 8))} ${chalk.cyan(sessionStatus)}${chalk.yellow(closeVerdict)}`);
548
+ if (opts.recap)
549
+ log.dim(` recap: ${opts.recap}`);
550
+ if (usedPack && pack) {
551
+ const pending = pack.counts?.pending ??
552
+ (Array.isArray(pack.pendingProposals) ? pack.pendingProposals.length : 0);
553
+ const unfinished = pack.counts?.unfinishedOutputs ?? 0;
554
+ console.log(` Pack: ${chalk.white(String(pending))} pending proposal(s)` +
555
+ (unfinished > 0
556
+ ? ` ${chalk.yellow(`${unfinished} unfinished output(s)`)}`
557
+ : ""));
558
+ if (Array.isArray(pack.warnings)) {
559
+ for (const w of pack.warnings) {
560
+ log.warn(String(w));
561
+ }
562
+ }
563
+ if (pack.note)
564
+ log.dim(` ${pack.note}`);
565
+ if (pending > 0) {
566
+ log.dim(` Review: synap proposals list --session ${id}`);
567
+ }
568
+ }
569
+ }
570
+ catch (e) {
571
+ renderHubError(e);
572
+ process.exit(1);
573
+ }
574
+ }
575
+ // ─── evidenceSession / evaluateSession ─────────────────────────────────────────
576
+ /** Resolve the target session: `--session <id>`, else this terminal's active one. */
577
+ function resolveTargetSessionId(opts, cfg) {
578
+ const id = opts.session || resolveActiveSessionId(cfg.podUrl);
579
+ if (!id) {
580
+ console.error(chalk.red("Error: no session in scope — pass --session <id> or attach one with 'synap session attach <id>'"));
581
+ process.exit(1);
582
+ }
583
+ return id;
584
+ }
585
+ function printEvaluateResult(result) {
586
+ if (result.status === "not_found") {
587
+ log.warn("Session not found");
588
+ return;
589
+ }
590
+ for (const r of result.results ?? []) {
591
+ if (r.status === "recorded" && r.verdict) {
592
+ const label = verdictLabel(r.verdict);
593
+ const colored = r.verdict === "pass"
594
+ ? chalk.green(label)
595
+ : r.verdict === "fail"
596
+ ? chalk.red(label)
597
+ : chalk.dim(label);
598
+ console.log(` ${chalk.bold(r.key)}: ${colored}${r.escalated ? chalk.yellow(" (escalated)") : ""}`);
599
+ }
600
+ else {
601
+ console.log(` ${chalk.bold(r.key)}: ${chalk.dim(`skipped — ${r.reason ?? "not applicable"}`)}`);
602
+ }
603
+ }
604
+ const summary = verdictSummary(result.verdict);
605
+ if (summary)
606
+ log.dim(` Verdict: ${summary}`);
607
+ if (result.resumed)
608
+ log.dim(` Resumed a paused check-gate`);
609
+ }
610
+ /**
611
+ * `synap session evidence <key> --passed|--failed [--detail "…"]`
612
+ *
613
+ * POST /focus-sessions/:id/evidence — the agent's own deterministic report
614
+ * for an `evidence`-checked criterion. Grades ONLY evidence-checked
615
+ * criteria; never spends a judge call or runs a capability
616
+ * (`rest/focus-sessions.ts`, the `onlyEvidence` branch).
617
+ */
618
+ export async function evidenceSession(key, opts) {
619
+ try {
620
+ const cfg = await resolveHubConfig(opts);
621
+ const id = resolveTargetSessionId(opts, cfg);
622
+ if (opts.passed === opts.failed) {
623
+ // Both or neither given — commander gives us two independent booleans,
624
+ // not an enum, so this is the edge check for "exactly one".
625
+ console.error(chalk.red("Error: pass exactly one of --passed or --failed"));
626
+ process.exit(1);
627
+ return;
628
+ }
629
+ const evidence = {
630
+ [key]: { passed: !!opts.passed, ...(opts.detail ? { detail: opts.detail } : {}) },
631
+ };
632
+ const result = (await hubPost(`/focus-sessions/${id}/evidence`, { evidence }, cfg));
633
+ if (opts.json) {
634
+ console.log(JSON.stringify(result, null, 2));
635
+ return;
636
+ }
637
+ log.success(`Evidence recorded ${chalk.dim(id.slice(0, 8))}`);
638
+ printEvaluateResult(result);
639
+ }
640
+ catch (e) {
641
+ renderHubError(e);
642
+ process.exit(1);
643
+ }
644
+ }
645
+ /**
646
+ * `synap session evaluate [--session <id>]`
647
+ *
648
+ * POST /focus-sessions/:id/evaluations — runs every PENDING criterion check
649
+ * (evidence already posted, then capability, then judge) and prints the
650
+ * per-criterion verdicts.
651
+ */
652
+ export async function evaluateSession(opts) {
653
+ try {
654
+ const cfg = await resolveHubConfig(opts);
655
+ const id = resolveTargetSessionId(opts, cfg);
656
+ const result = (await hubPost(`/focus-sessions/${id}/evaluations`, {}, cfg));
238
657
  if (opts.json) {
239
- console.log(JSON.stringify({ ok: true }));
658
+ console.log(JSON.stringify(result, null, 2));
240
659
  return;
241
660
  }
242
- log.success(`Session closed ${chalk.dim(id.slice(0, 8))}`);
661
+ log.success(`Session evaluated ${chalk.dim(id.slice(0, 8))}`);
662
+ printEvaluateResult(result);
243
663
  }
244
664
  catch (e) {
245
665
  renderHubError(e);