@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,10 +1,51 @@
1
- import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
1
+ import { existsSync, readFileSync, unlinkSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { getActivePodConfig, getActiveWorkspaceId, getActiveProjectId, listPodProfiles, getPodOverride, getSurfaceAgentKey } from "./pod.js";
3
+ import { getActivePodConfig, getActiveWorkspaceId, getActiveWorkspaceIdForPod, findPodNameByUrl, getActiveProjectBinding, getActiveProjectId, listPodProfiles, getPodOverride, getSurfaceAgentKey, } from "./pod.js";
4
4
  import { resolveAgentOverride } from "./agents-config.js";
5
- import { resolveActiveLens } from "./session-lens.js";
6
- import { HubRestClient } from "@synap/hub-rest-client";
5
+ import { resolveActiveLensForPod, getClaudeSessionId, writeLens, clearLensField, } from "./session-lens.js";
6
+ import { conversationHeaders } from "./conversation-headers.js";
7
+ import { effectiveDirectoryLens, resolveDirectoryLensForPod, writeDirectoryLens, clearDirectoryLensField, } from "./directory-lens.js";
8
+ import { preferClaudeCodeSurfaceKey } from "./key-source.js";
9
+ import { samePodOrigin } from "./project-ref.js";
10
+ import { HubRestClient } from "@synap-core/hub-rest-client";
7
11
  import { log } from "../utils/logger.js";
12
+ import chalk from "chalk";
13
+ /**
14
+ * Resolve a projectId safe to send to `podUrl`.
15
+ *
16
+ * Order: pod-qualified lens → directory lens → env project when co-located with
17
+ * this pod (trustEnvProject — same model as SYNAP_WORKSPACE_ID under env rung) →
18
+ * ActiveProjectBinding for this pod → legacy activeProjectId only when the
19
+ * active profile is this pod.
20
+ *
21
+ * Write paths should consume `resolveHubConfig().projectId` rather than bare
22
+ * `getActiveProjectId()` / env.
23
+ */
24
+ function resolveProjectIdForPod(podUrl, opts) {
25
+ if (opts.lensProjectId)
26
+ return opts.lensProjectId;
27
+ if (opts.dirLensProjectId)
28
+ return opts.dirLensProjectId;
29
+ // Env pin co-located with env pod (connect --pin-project, Claude settings.env).
30
+ if (opts.trustEnvProject && opts.envProject)
31
+ return opts.envProject;
32
+ const profiles = listPodProfiles();
33
+ const binding = getActiveProjectBinding();
34
+ if (binding) {
35
+ const boundPod = profiles.find((x) => x.name === binding.podName);
36
+ if (boundPod && samePodOrigin(boundPod.config.podUrl, podUrl)) {
37
+ return binding.projectId;
38
+ }
39
+ // Binding is for another pod — do not send it.
40
+ return undefined;
41
+ }
42
+ // Legacy: activeProjectId with no binding — only when active profile is this pod.
43
+ const active = getActivePodConfig();
44
+ if (active && samePodOrigin(active.podUrl, podUrl)) {
45
+ return getActiveProjectId() || undefined;
46
+ }
47
+ return undefined;
48
+ }
8
49
  /**
9
50
  * A failed Hub call, carrying the structured facts callers need to branch on.
10
51
  *
@@ -22,6 +63,10 @@ export class HubError extends Error {
22
63
  rawBody;
23
64
  path;
24
65
  method;
66
+ /** Origin of the pod this call actually hit — for cross-pod diagnostics. */
67
+ podUrl;
68
+ /** Workspace this call actually sent — for cross-pod 403 diagnostics. */
69
+ workspaceId;
25
70
  constructor(init) {
26
71
  super(init.message);
27
72
  this.name = "HubError";
@@ -32,6 +77,8 @@ export class HubError extends Error {
32
77
  this.rawBody = init.rawBody;
33
78
  this.path = init.path;
34
79
  this.method = init.method;
80
+ this.podUrl = init.podUrl;
81
+ this.workspaceId = init.workspaceId;
35
82
  }
36
83
  }
37
84
  /**
@@ -166,21 +213,56 @@ function readErrorBody(status, rawBody) {
166
213
  const oneLine = trimmed.replace(/\s+/g, " ");
167
214
  return { detail: oneLine.length > 200 ? `${oneLine.slice(0, 200)}…` : oneLine };
168
215
  }
216
+ // tRPC batch errors are array-shaped: `[{ error: { message, code, data: { httpStatus, ... } } }]`
217
+ // (superjson may additionally nest the real payload one level deeper under
218
+ // `error.json`). Unwrap to the first entry's error object before falling
219
+ // through to the normal object-shaped handling below, so a tRPC failure
220
+ // surfaces its real `message` instead of the generic status phrase.
221
+ if (Array.isArray(parsed)) {
222
+ const first = parsed[0];
223
+ const err = first?.error;
224
+ if (err && typeof err === "object") {
225
+ const json = err.json;
226
+ parsed = json ?? err;
227
+ }
228
+ }
229
+ else if (parsed &&
230
+ typeof parsed === "object" &&
231
+ "error" in parsed) {
232
+ // Single tRPC-shaped error body: `{ error: { message, ... } }` or
233
+ // `{ error: { json: { message, ... } } }`. Only unwrap when `error` is an
234
+ // OBJECT — a Hub-REST body's `error` is a plain string category
235
+ // (`{ error: "...", detail: "..." }`) and must fall through unchanged.
236
+ const errVal = parsed.error;
237
+ if (errVal && typeof errVal === "object") {
238
+ const errObj = errVal;
239
+ const json = errObj.json;
240
+ parsed = json ?? errObj;
241
+ }
242
+ }
169
243
  if (parsed && typeof parsed === "object") {
170
244
  const o = parsed;
171
245
  const code = typeof o.code === "string" ? o.code : undefined;
172
246
  const reason = typeof o.reason === "string" ? o.reason : undefined;
173
- const detail = typeof o.message === "string" && o.message
174
- ? o.message
175
- : typeof o.error === "string" && o.error
176
- ? o.error
177
- : statusPhrase(status);
247
+ const str = (v) => typeof v === "string" && v ? v : undefined;
248
+ // Backend error contract for the Hub package/workspace routes is
249
+ // `{ error: <category>, detail: <the real underlying cause> }`. The specific
250
+ // cause lives in `detail` — surfacing only `error` throws away the one thing
251
+ // that makes a 500 actionable (e.g. "profile X targets unknown profile Y").
252
+ // Precedence: message (tRPC-shaped) → error+detail combined → error → detail.
253
+ const message = str(o.message);
254
+ const errorField = str(o.error);
255
+ const detailField = str(o.detail);
256
+ const detail = message ??
257
+ (errorField && detailField
258
+ ? `${errorField}: ${detailField}`
259
+ : errorField ?? detailField ?? statusPhrase(status));
178
260
  return { detail, code, reason, parsed };
179
261
  }
180
262
  return { detail: statusPhrase(status), parsed };
181
263
  }
182
264
  /** Build the HubError for a non-ok response body. */
183
- function hubError(method, path, status, rawBody, suffix) {
265
+ function hubError(method, path, status, rawBody, suffix, ctx) {
184
266
  const { detail, code, reason, parsed } = readErrorBody(status, rawBody);
185
267
  return new HubError({
186
268
  message: `Hub ${method} ${path} failed (HTTP ${status})${suffix ? ` ${suffix}` : ""}: ${detail}`,
@@ -191,15 +273,50 @@ function hubError(method, path, status, rawBody, suffix) {
191
273
  rawBody,
192
274
  path,
193
275
  method,
276
+ podUrl: ctx?.podUrl ? originOf(ctx.podUrl) : undefined,
277
+ workspaceId: ctx?.workspaceId,
194
278
  });
195
279
  }
280
+ /** Where to send users who hit a wall. */
281
+ const DISCORD_INVITE_URL = "https://discord.gg/xhRdQ7hG5h";
282
+ /** Wrap a URL in an OSC-8 terminal hyperlink so `label` is clickable. */
283
+ function osc8Link(url, label) {
284
+ return `\x1b]8;;${url}\x1b\\${label}\x1b]8;;\x1b\\`;
285
+ }
286
+ /**
287
+ * Once-per-process guard for the Discord help footer. A single CLI invocation
288
+ * runs one command; printing the footer at most once keeps it a footer, not a
289
+ * per-error/per-line spam.
290
+ */
291
+ let _discordHelpShown = false;
292
+ /**
293
+ * Append a one-line "need help?" footer with a clickable Discord invite. Prints
294
+ * to stderr (same stream as the error it follows, so `2>/dev/null` hides both)
295
+ * and only once per process. The invite is an OSC-8 hyperlink on an interactive
296
+ * terminal, a plain URL when the stream is redirected/piped.
297
+ */
298
+ export function discordHelpLine() {
299
+ if (_discordHelpShown)
300
+ return;
301
+ _discordHelpShown = true;
302
+ const link = process.stderr.isTTY
303
+ ? osc8Link(DISCORD_INVITE_URL, DISCORD_INVITE_URL)
304
+ : DISCORD_INVITE_URL;
305
+ console.error(chalk.dim(` Need help? Join our Discord: ${link}`));
306
+ }
196
307
  /**
197
308
  * Print a failed Hub call: its humanized message plus a hint for the classes
198
309
  * where the cause is not obvious from the message alone. Lives here rather than
199
310
  * in utils/logger so the logger stays domain-agnostic and the taxonomy and its
200
- * presentation sit in one file.
311
+ * presentation sit in one file. Always closes with the one-time Discord footer.
201
312
  */
202
313
  export function renderHubError(err) {
314
+ renderHubErrorBody(err);
315
+ // ONE central place every failed Hub command flows through — append the help
316
+ // footer here so it prints once per failed command (guarded), not per line.
317
+ discordHelpLine();
318
+ }
319
+ function renderHubErrorBody(err) {
203
320
  // Hints use log.hint (stderr), not log.dim (stdout): a hint belongs to the
204
321
  // error it explains, and splitting one message across two streams means
205
322
  // `2>/dev/null` shows a bare hint with no error above it.
@@ -212,15 +329,112 @@ export function renderHubError(err) {
212
329
  log.error(err instanceof Error ? err.message : String(err));
213
330
  return;
214
331
  }
332
+ // Connection-class failure — the execute door returns 424 with a machine
333
+ // `errorClass` (+ `providerRef`) precisely so it SURVIVES the pod's 5xx egress
334
+ // sanitizer. This is client-ACTIONABLE (reconnect), not a server fault, so name
335
+ // the cause + the exact fix instead of the generic status hint. Mirrors the
336
+ // browser's P1 self-heal reconnect chip.
337
+ const errBody = err.body;
338
+ const errorClass = errBody?.errorClass;
339
+ if (errorClass === "auth" || errorClass === "no_connection") {
340
+ const provider = errBody?.providerRef
341
+ ? chalk.bold(errBody.providerRef)
342
+ : "this capability's";
343
+ log.error(`The ${provider} connection needs reconnecting — its access has expired or was revoked.`);
344
+ log.hint('Reconnect it: synap cap connect "<capability>" --reconnect');
345
+ log.hint("Find the capability name with: synap cap list");
346
+ return;
347
+ }
348
+ if (errorClass === "target_missing") {
349
+ log.error(err.message);
350
+ log.hint("The workspace or target this run needs no longer exists on this pod. Run: synap orient");
351
+ return;
352
+ }
353
+ // SETUP-REQUIRED failure — the APPLY door (`POST /capabilities/apply`) returns
354
+ // a DUCK-TYPED body (412 for `no_connection`, 400 for `missing_field`) with
355
+ // `failureClass` (+ `missingFields` / `connection`) so an install that needs a
356
+ // human before it can apply survives as an actionable message. This is a
357
+ // client-ACTIONABLE, NON-retryable state — name the missing step instead of the
358
+ // generic status phrase below (a bare 412/400 tells the user nothing to do).
359
+ const setupBody = err.body;
360
+ const failureClass = setupBody?.failureClass;
361
+ if (failureClass === "no_connection") {
362
+ const provider = setupBody?.connection?.provider;
363
+ log.error(provider
364
+ ? `The "${provider}" provider isn't connected yet — connect it first (synap cap connect "${provider}").`
365
+ : 'A required provider isn\'t connected yet — connect it first (synap cap connect "<provider>").');
366
+ return;
367
+ }
368
+ if (failureClass === "missing_field") {
369
+ const missingFields = Array.isArray(setupBody?.missingFields)
370
+ ? setupBody.missingFields.filter((field) => typeof field === "string")
371
+ : [];
372
+ log.error(missingFields.length > 0
373
+ ? `Missing required value(s): ${missingFields.join(", ")}`
374
+ : "Missing required value(s).");
375
+ return;
376
+ }
215
377
  log.error(err.message);
378
+ // A 500 carries an `errorId` (the egress sanitizer logs the real cause under
379
+ // it server-side) — surface it so "quote errorId when reporting it" is
380
+ // actionable rather than a dead end.
381
+ const errorId = typeof err.body?.errorId === "string"
382
+ ? err.body.errorId
383
+ : undefined;
384
+ if (err.status >= 500 && errorId) {
385
+ log.hint(`errorId: ${errorId}`);
386
+ }
387
+ // A 500 whose body carried a specific cause (detail/message) is already in
388
+ // `err.message` above — do NOT then claim "internal error, not in this
389
+ // command": a workspace/template creation failure is often an actionable
390
+ // template or data problem. Only add the generic pod-fault hint when the body
391
+ // gave nothing better than the status phrase.
392
+ const hasSpecificCause = !!err.rawBody && !err.message.endsWith("the pod hit an internal error");
393
+ // A 403/message naming a workspace: the real cause is almost always "that
394
+ // workspace doesn't exist or isn't accessible ON THIS POD" (a stale saved
395
+ // workspace id after a pod switch), not a scopes problem — name it + the
396
+ // exact next command instead of the generic "credentials" hint.
397
+ const namesWorkspace = /workspace/i.test(err.message);
216
398
  if (err.status >= 500) {
217
- log.hint("The pod hit an internal error — this is a fault on the pod side, not in this command.");
399
+ if (!hasSpecificCause) {
400
+ log.hint("The pod hit an internal error — check the pod logs for the stack trace.");
401
+ }
402
+ }
403
+ else if (err.status === 403 && namesWorkspace) {
404
+ // Name the exact workspace + pod this call used: the usual cause is a global
405
+ // active workspace left over from ANOTHER pod (a stale `synap use` after a
406
+ // pod switch) being sent to a pod it doesn't belong to.
407
+ if (err.workspaceId && err.podUrl) {
408
+ // State the FACT (what was sent where), then the two causes WITHOUT
409
+ // picking one — this code cannot tell them apart, and asserting the
410
+ // cross-pod story is wrong whenever the saved workspace simply no longer
411
+ // exists on its own pod (dogfood: `pods.team.workspaceId` pointed at a
412
+ // deleted workspace; the hint blamed a pod mix-up that had not happened).
413
+ // `synap doctor` is the only thing here that actually CHECKS, so it leads.
414
+ log.hint(`Sent workspace ${err.workspaceId} to ${err.podUrl}.`);
415
+ log.hint("That workspace is not accessible on this pod — either it lives on a different pod, or it no longer exists here.");
416
+ log.hint("Run: synap doctor (checks which of the two it is), then: synap orient (see valid workspaces) and synap use <id>.");
417
+ }
418
+ else {
419
+ log.hint("Workspace not found or not accessible on this pod — run: synap orient (see valid workspaces), or: synap doctor");
420
+ }
218
421
  }
219
422
  else if (err.status === 401 || err.status === 403) {
220
- log.hint("The pod rejected this key's credentials or its scopes for this operation.");
423
+ log.hint("The pod rejected this key's credentials or its scopes for this operation. Run: synap doctor");
221
424
  }
222
425
  else if (err.status === 404) {
223
- log.hint("The pod has no such endpoint or record — it may be running an older build.");
426
+ // A structured JSON body (err.body set) means THIS app answered — the
427
+ // endpoint just doesn't exist here (older build / wrong record). A body
428
+ // that failed to parse as JSON (readErrorBody's plain-text fallback — a
429
+ // proxy default page, an HTML error, a completely different service)
430
+ // means the request never reached this app at all: the classic
431
+ // stale/migrated-domain failure, not an old build.
432
+ if (err.body === undefined && err.rawBody.trim()) {
433
+ log.hint("This host doesn't look like a Synap pod — your saved pod URL may be stale. Run: synap doctor");
434
+ }
435
+ else {
436
+ log.hint("The pod has no such endpoint or record — it may be running an older build, or the id doesn't exist. Run: synap doctor");
437
+ }
224
438
  }
225
439
  }
226
440
  const userIdCache = new Map();
@@ -250,14 +464,21 @@ export async function resolveHubConfig(opts) {
250
464
  // Beats the env vars deliberately — that is the whole point of the flag.
251
465
  const podOverride = getPodOverride();
252
466
  if (podOverride) {
467
+ // Prefer cross-pod-safe resolution (binding + "not another pod's default")
468
+ // over the profile's baked `workspaceId`, which can drift to a foreign UUID
469
+ // after dual-pod work (dogfood: perso profile held team CRM ws → 403).
470
+ const overrideName = findPodNameByUrl(podOverride.podUrl);
471
+ const safeWs = getActiveWorkspaceIdForPod(overrideName) ||
472
+ // Only fall back to profile default when it isn't known to belong elsewhere
473
+ // (getActiveWorkspaceIdForPod already applied that rule against the profile).
474
+ undefined;
253
475
  return {
254
476
  podUrl: podOverride.podUrl,
255
477
  apiKey: podOverride.hubApiKey,
256
478
  // A profile's agentUserId may be empty; "cli" makes resolveUserId fetch /users/me.
257
479
  userId: podOverride.agentUserId || "cli",
258
- // Use the TARGET pod's own workspace — NOT this session's lens, which
259
- // belongs to the default pod and would be meaningless on the target.
260
- workspaceId: podOverride.workspaceId || undefined,
480
+ workspaceId: safeWs,
481
+ projectId: resolveProjectIdForPod(podOverride.podUrl, {}),
261
482
  };
262
483
  }
263
484
  // 1. Explicit flags (escape hatch)
@@ -278,7 +499,10 @@ export async function resolveHubConfig(opts) {
278
499
  return {
279
500
  podUrl,
280
501
  apiKey: agentOverride.apiKey,
281
- userId: agentOverride.podName ?? "agent",
502
+ // The AGENT's own id, recorded with its key. Never the pod profile name
503
+ // (it was, so `synap worker` never recognised its own room posts and
504
+ // resumed itself on them). Unrecorded → "cli": resolveUserId asks the pod.
505
+ userId: agentOverride.agentUserId || "cli",
282
506
  scopes: envScopes ? envScopes.split(",") : undefined,
283
507
  };
284
508
  }
@@ -297,24 +521,51 @@ export async function resolveHubConfig(opts) {
297
521
  // the pinned key so a leaked/inherited env var can't silently redefine
298
522
  // which agent this session acts as. Conservative: only applies here, in
299
523
  // step 3 — steps 0-2 (--pod, explicit flags, SYNAP_AGENT) still win
300
- // outright, untouched.
301
- const surfaceKey = getSurfaceAgentKey("claude-code");
302
- let apiKey = envKey;
303
- let userId = envUser;
304
- if (surfaceKey && surfaceKey.podUrl === envPod && surfaceKey.hubApiKey && surfaceKey.hubApiKey !== envKey) {
524
+ // outright, untouched. Preference logic lives in key-source.ts so whoami
525
+ // reports the same rule (no drift).
526
+ const preferred = preferClaudeCodeSurfaceKey({
527
+ envPod,
528
+ envKey,
529
+ envUser,
530
+ surface: getSurfaceAgentKey("claude-code", envPod),
531
+ });
532
+ if (preferred.usedSurface) {
305
533
  process.stderr.write(`[synap] warning: ambient SYNAP_HUB_API_KEY does not match the claude-code surface key pinned to ${envPod} — using the pinned key instead of the inherited one.\n`);
306
- apiKey = surfaceKey.hubApiKey;
307
- userId = surfaceKey.agentUserId || envUser;
308
534
  }
535
+ const apiKey = preferred.apiKey;
536
+ const userId = preferred.userId;
537
+ // Per-Claude-session lens wins over the global env var, so concurrent
538
+ // sessions can each be scoped to a different workspace — but ONLY when the
539
+ // lens belongs to THIS pod. A lens stamped with another pod is ignored so
540
+ // the pod-qualified fallback below applies (the "Access denied to
541
+ // workspace" 403 after `synap pods use`).
542
+ const lens = resolveActiveLensForPod(envPod);
543
+ // …and the per-DIRECTORY lens sits directly beneath it: the durable default
544
+ // for this working tree, overridden by the session lens when one is set.
545
+ // Pod-qualified through the same door, for the same reason — a directory
546
+ // lens outlives many `synap pods use` switches, so it is MORE exposed to
547
+ // the cross-pod 403 than the session lens is.
548
+ const dirLens = resolveDirectoryLensForPod(envPod)?.lens;
309
549
  return {
310
550
  podUrl: envPod,
311
551
  apiKey,
312
552
  userId,
313
- // Per-Claude-session lens wins over the global env var, so concurrent
314
- // sessions can each be scoped to a different workspace.
315
- workspaceId: resolveActiveLens()?.workspaceId || envWorkspace || getActiveWorkspaceId(),
316
- // Project is a peer lens, threaded exactly like workspaceId.
317
- projectId: resolveActiveLens()?.projectId || envProject || getActiveProjectId(),
553
+ // The global-config fallback is resolved AGAINST THIS POD (envPod) —
554
+ // never send a workspace that belongs to a different pod.
555
+ workspaceId: lens?.workspaceId ||
556
+ dirLens?.workspaceId ||
557
+ envWorkspace ||
558
+ getActiveWorkspaceIdForPod(findPodNameByUrl(envPod)),
559
+ // Project is a peer lens. Lens/dir-lens are already pod-qualified;
560
+ // env/global active project only when ActiveProjectBinding matches this pod.
561
+ // Write paths must use this resolveHubConfig output — never bare
562
+ // getActiveProjectId() / SYNAP_PROJECT_ID without the pod check.
563
+ projectId: resolveProjectIdForPod(envPod, {
564
+ lensProjectId: lens?.projectId,
565
+ dirLensProjectId: dirLens?.projectId,
566
+ envProject,
567
+ trustEnvProject: true,
568
+ }),
318
569
  scopes: envScopes ? envScopes.split(",") : undefined,
319
570
  };
320
571
  }
@@ -322,40 +573,136 @@ export async function resolveHubConfig(opts) {
322
573
  const config = getActivePodConfig();
323
574
  if (!config)
324
575
  throw new Error("No pod configured. Run: synap pods add");
576
+ // Same pod-qualification as step 3: a lens stamped with another pod must not
577
+ // short-circuit `getActiveWorkspaceId()`, which IS cross-pod-safe.
578
+ const activeLens = resolveActiveLensForPod(config.podUrl);
579
+ const activeDirLens = resolveDirectoryLensForPod(config.podUrl)?.lens;
325
580
  return {
326
581
  podUrl: config.podUrl,
327
582
  apiKey: config.hubApiKey,
328
583
  userId: config.agentUserId,
329
- workspaceId: resolveActiveLens()?.workspaceId || getActiveWorkspaceId(),
330
- projectId: resolveActiveLens()?.projectId || getActiveProjectId(),
584
+ // session lens › directory lens › global config. (No env rung here: this
585
+ // branch is only reached when SYNAP_POD_URL is unset.)
586
+ workspaceId: activeLens?.workspaceId || activeDirLens?.workspaceId || getActiveWorkspaceId(),
587
+ // Project: same pod-qualification as step 3 (no bare getActiveProjectId).
588
+ projectId: resolveProjectIdForPod(config.podUrl, {
589
+ lensProjectId: activeLens?.projectId,
590
+ dirLensProjectId: activeDirLens?.projectId,
591
+ }),
331
592
  };
332
593
  }
333
- const SESSION_FILE = ".synap-session";
334
- /** Read the active session ID for this terminal from CWD or env var. */
335
- export function readActiveSessionId() {
594
+ // ─── Active focus session ─────────────────────────────────────────────────────
595
+ //
596
+ // ONE store, two rungs of the SAME lens mechanism:
597
+ //
598
+ // env SYNAP_SESSION_ID › session lens (per Claude tab) › directory lens
599
+ //
600
+ // The directory rung supplies a session only OUTSIDE a Claude Code conversation
601
+ // (`effectiveDirectoryLens`): inside one, the session belongs to the
602
+ // conversation, never to the working tree.
603
+ //
604
+ // The retired legacy store was a per-CWD `.synap-session` file that the session
605
+ // lens already silently shadowed — two concurrent "current session" stores for
606
+ // one concept. It is now read exactly once, to migrate (below).
607
+ /** Legacy per-CWD session file — RETIRED as a store; read once to migrate. */
608
+ const LEGACY_SESSION_FILE = ".synap-session";
609
+ let legacySessionMigrationChecked = false;
610
+ /**
611
+ * One-time migration of a pre-existing `.synap-session` into the DIRECTORY lens.
612
+ *
613
+ * COMPAT DECISION — read-and-migrate, not a permanent read-only fallback. A
614
+ * fallback would keep two stores alive indefinitely, which is exactly the
615
+ * divergence being removed. Migrating is faithful: `.synap-session` was
616
+ * per-working-tree and durable, and the directory lens has precisely that
617
+ * lifetime and scope — so an already-attached session survives the upgrade for
618
+ * every terminal in that tree. The file is deleted afterwards so it can never
619
+ * shadow (or be shadowed by) the lens again.
620
+ *
621
+ * No `podUrl` is stamped: the legacy file carried no pod, and stamping one here
622
+ * would falsely pod-qualify whatever workspace/project the lens already holds.
623
+ */
624
+ function migrateLegacySessionFile() {
625
+ if (legacySessionMigrationChecked)
626
+ return;
627
+ legacySessionMigrationChecked = true;
628
+ const filePath = join(process.cwd(), LEGACY_SESSION_FILE);
629
+ if (!existsSync(filePath))
630
+ return;
631
+ let id;
632
+ try {
633
+ id = readFileSync(filePath, "utf8").trim() || undefined;
634
+ }
635
+ catch {
636
+ return;
637
+ }
638
+ try {
639
+ if (id)
640
+ writeDirectoryLens({ focusSessionId: id });
641
+ unlinkSync(filePath);
642
+ }
643
+ catch {
644
+ return; // best-effort — a read-only tree simply keeps the stale file
645
+ }
646
+ process.stderr.write(`[synap] .synap-session is deprecated${id ? ` — session ${id.slice(0, 8)}… migrated into .synap/lens.json` : " — removed (empty)"}.\n`);
647
+ }
648
+ /**
649
+ * The focus session every hub call should be tagged with.
650
+ *
651
+ * Both lens rungs are pod-qualified: a focus-session id is POD-LOCAL, so a lens
652
+ * stamped with another pod must not leak into `X-Session-Id`. (Lenses written
653
+ * before the stamp existed have no `podUrl` and match leniently — see
654
+ * `lensMatchesPod`.)
655
+ */
656
+ export function resolveActiveSessionId(podUrl) {
336
657
  const fromEnv = process.env.SYNAP_SESSION_ID;
337
658
  if (fromEnv)
338
659
  return fromEnv;
339
- const filePath = join(process.cwd(), SESSION_FILE);
340
- if (existsSync(filePath)) {
341
- return readFileSync(filePath, "utf8").trim() || undefined;
342
- }
343
- return undefined;
660
+ migrateLegacySessionFile();
661
+ const fromSessionLens = resolveActiveLensForPod(podUrl)?.focusSessionId;
662
+ if (fromSessionLens)
663
+ return fromSessionLens;
664
+ const dir = resolveDirectoryLensForPod(podUrl);
665
+ return dir ? effectiveDirectoryLens(dir.lens).focusSessionId : undefined;
344
666
  }
345
- /** Attach a session to this terminal — writes .synap-session in CWD. */
346
- export function writeActiveSessionId(sessionId) {
347
- writeFileSync(join(process.cwd(), SESSION_FILE), sessionId, "utf8");
667
+ /**
668
+ * Attach a focus session to this terminal.
669
+ *
670
+ * Inside Claude Code the SESSION lens wins — per-tab isolation is the point
671
+ * (tab A on session X, tab B on session Y, same directory). Outside it there is
672
+ * no `CLAUDE_CODE_SESSION_ID` to key on, so the DIRECTORY lens is the
673
+ * legitimate fallback: the same per-working-tree scope the retired
674
+ * `.synap-session` had. `attach` therefore never silently no-ops.
675
+ */
676
+ export function attachActiveSessionId(sessionId) {
677
+ const claudeSession = getClaudeSessionId();
678
+ if (claudeSession) {
679
+ writeLens(claudeSession, { focusSessionId: sessionId });
680
+ return "session-lens";
681
+ }
682
+ writeDirectoryLens({ focusSessionId: sessionId });
683
+ return "directory-lens";
348
684
  }
349
- /** Detach the active session from this terminal — removes .synap-session. */
350
- export function clearActiveSessionId() {
351
- const filePath = join(process.cwd(), SESSION_FILE);
352
- if (existsSync(filePath))
353
- unlinkSync(filePath);
685
+ /**
686
+ * Detach — clear the focus session from BOTH lens rungs, and return whatever
687
+ * was active. Clearing only the top rung would "detach" into the lower one,
688
+ * which reads as the command failing.
689
+ */
690
+ export function detachActiveSessionId(podUrl) {
691
+ const previous = resolveActiveSessionId(podUrl);
692
+ const claudeSession = getClaudeSessionId();
693
+ if (claudeSession)
694
+ clearLensField(claudeSession, "focusSessionId");
695
+ clearDirectoryLensField("focusSessionId");
696
+ return previous;
354
697
  }
355
- function sessionHeaders() {
356
- // Per-Claude-session lens wins; fall back to the legacy per-CWD .synap-session.
357
- const id = resolveActiveLens()?.focusSessionId || readActiveSessionId();
358
- return id ? { "X-Session-Id": id } : {};
698
+ /**
699
+ * Per-request headers: the focus session this terminal is attached to, and the
700
+ * Claude Code conversation the call comes from (`conversation-headers.ts`) — so
701
+ * the work it lands can be traced back to, and reopened in, that conversation.
702
+ */
703
+ function sessionHeaders(cfg) {
704
+ const id = resolveActiveSessionId(cfg?.podUrl);
705
+ return { ...conversationHeaders(getClaudeSessionId()), ...(id ? { "X-Session-Id": id } : {}) };
359
706
  }
360
707
  /** Max 429 retries for bulk import (each waits up to the rate-limit window). */
361
708
  const HUB_429_MAX_RETRIES = 12;
@@ -464,15 +811,26 @@ export async function hubGet(path, params, cfg) {
464
811
  url.searchParams.set(k, String(v));
465
812
  }
466
813
  const res = await hubFetchWithRetry(url.toString(), {
467
- headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
814
+ headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
468
815
  signal: AbortSignal.timeout(15_000),
469
816
  }, `GET ${path}`, 15_000);
470
817
  if (!res.ok) {
471
818
  const body = await res.text().catch(() => "");
472
- throw hubError("GET", path, res.status, body);
819
+ throw hubError("GET", path, res.status, body, undefined, cfg);
473
820
  }
474
821
  return res.json();
475
822
  }
823
+ /**
824
+ * The workspace a failing request ACTUALLY carried. A caller may override the
825
+ * configured lens per-command (e.g. `--workspace <id>`), and naming the config's
826
+ * workspace in that error sends the reader chasing the wrong id.
827
+ */
828
+ function errorWorkspace(body, cfg) {
829
+ const sent = body && typeof body === "object" && "workspaceId" in body
830
+ ? body.workspaceId
831
+ : undefined;
832
+ return typeof sent === "string" && sent.length > 0 ? sent : cfg.workspaceId;
833
+ }
476
834
  export async function hubPost(path, body, cfg,
477
835
  // Default 15s suits CRUD doors. Capability runs invoke external provider APIs
478
836
  // (often several sequential calls) and legitimately take longer — callers
@@ -483,14 +841,17 @@ timeoutMs = 15_000) {
483
841
  headers: {
484
842
  Authorization: `Bearer ${cfg.apiKey}`,
485
843
  "Content-Type": "application/json",
486
- ...sessionHeaders(),
844
+ ...sessionHeaders(cfg),
487
845
  },
488
846
  body: JSON.stringify(body),
489
847
  signal: AbortSignal.timeout(timeoutMs),
490
848
  }, `POST ${path}`, timeoutMs);
491
849
  if (!res.ok) {
492
850
  const bodyText = await res.text().catch(() => "");
493
- throw hubError("POST", path, res.status, bodyText);
851
+ throw hubError("POST", path, res.status, bodyText, undefined, {
852
+ podUrl: cfg.podUrl,
853
+ workspaceId: errorWorkspace(body, cfg),
854
+ });
494
855
  }
495
856
  return res.json();
496
857
  }
@@ -504,13 +865,13 @@ timeoutMs = 15_000) {
504
865
  export async function hubPostMultipart(path, form, cfg, timeoutMs = 120_000) {
505
866
  const res = await hubFetchWithRetry(`${cfg.podUrl}/api/hub${path}`, {
506
867
  method: "POST",
507
- headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
868
+ headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
508
869
  body: form,
509
870
  signal: AbortSignal.timeout(timeoutMs),
510
871
  }, `POST multipart ${path}`, timeoutMs);
511
872
  if (!res.ok) {
512
873
  const bodyText = await res.text().catch(() => "");
513
- throw hubError("POST multipart", path, res.status, bodyText);
874
+ throw hubError("POST multipart", path, res.status, bodyText, undefined, cfg);
514
875
  }
515
876
  return res.json();
516
877
  }
@@ -528,7 +889,7 @@ export async function hubPostMultipartRetryable(path, buildForm, cfg, timeoutMs
528
889
  try {
529
890
  res = await fetch(url, {
530
891
  method: "POST",
531
- headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
892
+ headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
532
893
  body: form,
533
894
  signal: AbortSignal.timeout(timeoutMs),
534
895
  });
@@ -539,7 +900,7 @@ export async function hubPostMultipartRetryable(path, buildForm, cfg, timeoutMs
539
900
  if (res.status !== 429) {
540
901
  if (!res.ok) {
541
902
  const bodyText = await res.text().catch(() => "");
542
- throw hubError("POST multipart", path, res.status, bodyText);
903
+ throw hubError("POST multipart", path, res.status, bodyText, undefined, cfg);
543
904
  }
544
905
  return res.json();
545
906
  }
@@ -560,7 +921,7 @@ export async function hubPatch(path, body, cfg) {
560
921
  try {
561
922
  res = await fetch(url, {
562
923
  method: "PATCH",
563
- headers: { Authorization: `Bearer ${cfg.apiKey}`, "Content-Type": "application/json", ...sessionHeaders() },
924
+ headers: { Authorization: `Bearer ${cfg.apiKey}`, "Content-Type": "application/json", ...sessionHeaders(cfg) },
564
925
  body: JSON.stringify(body),
565
926
  signal: AbortSignal.timeout(15_000),
566
927
  });
@@ -570,7 +931,10 @@ export async function hubPatch(path, body, cfg) {
570
931
  }
571
932
  if (!res.ok) {
572
933
  const bodyText = await res.text().catch(() => "");
573
- throw hubError("PATCH", path, res.status, bodyText);
934
+ throw hubError("PATCH", path, res.status, bodyText, undefined, {
935
+ podUrl: cfg.podUrl,
936
+ workspaceId: errorWorkspace(body, cfg),
937
+ });
574
938
  }
575
939
  return res.json();
576
940
  }
@@ -580,7 +944,7 @@ export async function hubDelete(path, cfg) {
580
944
  try {
581
945
  res = await fetch(url, {
582
946
  method: "DELETE",
583
- headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
947
+ headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
584
948
  signal: AbortSignal.timeout(15_000),
585
949
  });
586
950
  }
@@ -589,7 +953,7 @@ export async function hubDelete(path, cfg) {
589
953
  }
590
954
  if (!res.ok) {
591
955
  const bodyText = await res.text().catch(() => "");
592
- throw hubError("DELETE", path, res.status, bodyText);
956
+ throw hubError("DELETE", path, res.status, bodyText, undefined, cfg);
593
957
  }
594
958
  return res.json().catch(() => ({}));
595
959
  }
@@ -605,5 +969,5 @@ export function makeHubClient(cfg) {
605
969
  workspaceId: cfg.workspaceId,
606
970
  });
607
971
  }
608
- export { HubRestClient } from "@synap/hub-rest-client";
972
+ export { HubRestClient } from "@synap-core/hub-rest-client";
609
973
  //# sourceMappingURL=hub-client.js.map