@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
@@ -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,7 +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}%]`)}` : "";
93
- console.log(` ${chalk.bold(String(s.id ?? "").slice(0, 8))} ${statusColor}${progress} ${chalk.white(String(s.goal ?? ""))}`);
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})`)}` : "";
297
+ // Full id — feeds straight into `synap session get/update/attach <id>`.
298
+ console.log(` ${chalk.bold(String(s.id ?? ""))} ${statusColor}${kindCol}${progress} ${chalk.white(resolveSessionTitle(s))}${verdictCol}`);
94
299
  }
95
300
  }
96
301
  catch (e) {
@@ -99,22 +304,62 @@ export async function listSessions(opts) {
99
304
  }
100
305
  }
101
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
+ }
102
326
  export async function getSession(id, opts) {
103
327
  try {
104
328
  const cfg = await resolveHubConfig(opts);
105
- const params = {};
106
- const wsId = opts.workspace || cfg.workspaceId;
107
- if (wsId)
108
- params.workspaceId = wsId;
109
- const res = await hubGet(`/focus-sessions/${id}`, params, cfg);
329
+ const res = await fetchSessionById(id, cfg, opts.workspace);
110
330
  if (opts.json) {
111
331
  console.log(JSON.stringify(res, null, 2));
112
332
  return;
113
333
  }
114
334
  const s = res;
115
335
  log.info(`Session ${chalk.bold(String(s.id ?? ""))}`);
336
+ console.log(` Name: ${chalk.white(resolveSessionTitle(s))}`);
116
337
  console.log(` Goal: ${chalk.white(String(s.goal ?? ""))}`);
117
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))}`);
118
363
  if (typeof s.progress === "number")
119
364
  console.log(` Progress: ${s.progress}%`);
120
365
  if (s.templateId)
@@ -135,16 +380,31 @@ export async function getSession(id, opts) {
135
380
  export async function updateSession(id, opts) {
136
381
  try {
137
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.
138
387
  const wsId = opts.workspace || cfg.workspaceId;
139
- if (!wsId) {
140
- console.error(chalk.red("Error: no active workspace — pass --workspace <id> or set one with `synap use`"));
141
- 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;
142
403
  }
143
- const body = { workspaceId: wsId };
144
- if (opts.progress !== undefined)
145
- body.progress = parseInt(opts.progress, 10);
146
404
  if (opts.status)
147
405
  body.status = opts.status;
406
+ if (opts.stage)
407
+ body.currentStage = opts.stage;
148
408
  if (opts.goal)
149
409
  body.goal = opts.goal;
150
410
  const res = await hubPatch(`/focus-sessions/${id}`, body, cfg);
@@ -157,6 +417,8 @@ export async function updateSession(id, opts) {
157
417
  log.dim(` progress → ${opts.progress}%`);
158
418
  if (opts.status)
159
419
  log.dim(` status → ${opts.status}`);
420
+ if (opts.stage)
421
+ log.dim(` stage → ${opts.stage}`);
160
422
  }
161
423
  catch (e) {
162
424
  renderHubError(e);
@@ -165,34 +427,27 @@ export async function updateSession(id, opts) {
165
427
  }
166
428
  // ─── attachSession ────────────────────────────────────────────────────────────
167
429
  export async function attachSession(id, opts) {
168
- // Validate the session exists before attaching
430
+ // Validate the session exists before attaching (project-scoped OK — no workspace required)
169
431
  try {
170
432
  const cfg = await resolveHubConfig(opts);
171
- const params = {};
172
- if (cfg.workspaceId)
173
- params.workspaceId = cfg.workspaceId;
174
- await hubGet(`/focus-sessions/${id}`, params, cfg);
433
+ await fetchSessionById(id, cfg, opts.workspace);
175
434
  }
176
435
  catch (e) {
177
436
  console.error(chalk.red("Error: session not found — " + e.message));
178
437
  process.exit(1);
179
438
  }
180
- writeActiveSessionId(id);
181
- const { getClaudeSessionId, writeLens } = await import("../lib/session-lens.js");
182
- const cs = getClaudeSessionId();
183
- if (cs)
184
- writeLens(cs, { focusSessionId: id });
439
+ const target = attachActiveSessionId(id);
185
440
  if (opts.json) {
186
- console.log(JSON.stringify({ ok: true, sessionId: id }));
441
+ console.log(JSON.stringify({ ok: true, sessionId: id, scope: target }));
187
442
  return;
188
443
  }
189
444
  log.success(`Session attached ${chalk.dim(id.slice(0, 8))}`);
445
+ log.dim(` Active ${describeAttachTarget(target)}`);
190
446
  log.dim(` All hub calls in this terminal will tag X-Session-Id: ${id.slice(0, 8)}…`);
191
447
  }
192
448
  // ─── detachSession ────────────────────────────────────────────────────────────
193
449
  export function detachSession(opts) {
194
- const current = readActiveSessionId();
195
- clearActiveSessionId();
450
+ const current = detachActiveSessionId();
196
451
  if (opts.json) {
197
452
  console.log(JSON.stringify({ ok: true }));
198
453
  return;
@@ -206,39 +461,205 @@ export function detachSession(opts) {
206
461
  }
207
462
  // ─── sessionStatus ────────────────────────────────────────────────────────────
208
463
  export function sessionStatus(opts) {
209
- const id = readActiveSessionId();
464
+ const id = resolveActiveSessionId();
210
465
  if (opts.json) {
211
466
  console.log(JSON.stringify({ sessionId: id ?? null }));
212
467
  return;
213
468
  }
214
469
  if (id) {
215
- 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)")}`);
216
471
  }
217
472
  else {
218
473
  log.dim("No active session — run `synap session start` or `synap session attach <id>`");
219
474
  }
220
475
  }
221
- // ─── 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
+ */
222
486
  export async function closeSession(id, opts) {
223
487
  try {
224
488
  const cfg = await resolveHubConfig(opts);
225
489
  const wsId = opts.workspace || cfg.workspaceId;
226
- if (!wsId) {
227
- console.error(chalk.red("Error: no active workspace — pass --workspace <id> or set one with `synap use`"));
228
- 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);
229
521
  }
230
- // Close via PATCH — the /focus-sessions/:id endpoint sets closedAt automatically
231
- // when status transitions to "closed"
232
- const body = { workspaceId: wsId, status: "closed" };
233
- await hubPatch(`/focus-sessions/${id}`, body, cfg);
234
522
  // Detach if this was the active terminal session
235
- if (readActiveSessionId() === id)
236
- 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));
237
657
  if (opts.json) {
238
- console.log(JSON.stringify({ ok: true }));
658
+ console.log(JSON.stringify(result, null, 2));
239
659
  return;
240
660
  }
241
- log.success(`Session closed ${chalk.dim(id.slice(0, 8))}`);
661
+ log.success(`Session evaluated ${chalk.dim(id.slice(0, 8))}`);
662
+ printEvaluateResult(result);
242
663
  }
243
664
  catch (e) {
244
665
  renderHubError(e);