@synap-core/cli 1.11.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (533) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +40 -198
  3. package/dist/build-info.json +6 -0
  4. package/dist/commands/agent-run.js +3 -2
  5. package/dist/commands/agent-run.js.map +1 -1
  6. package/dist/commands/agents.d.ts +32 -17
  7. package/dist/commands/agents.js +203 -237
  8. package/dist/commands/agents.js.map +1 -1
  9. package/dist/commands/app.d.ts +143 -0
  10. package/dist/commands/app.js +524 -0
  11. package/dist/commands/app.js.map +1 -0
  12. package/dist/commands/automate.d.ts +21 -0
  13. package/dist/commands/automate.js +42 -0
  14. package/dist/commands/automate.js.map +1 -0
  15. package/dist/commands/automation-schema.test.d.ts +16 -0
  16. package/dist/commands/automation-schema.test.js +77 -0
  17. package/dist/commands/automation-schema.test.js.map +1 -0
  18. package/dist/commands/automation.d.ts +12 -0
  19. package/dist/commands/automation.js +116 -66
  20. package/dist/commands/automation.js.map +1 -1
  21. package/dist/commands/box.d.ts +42 -0
  22. package/dist/commands/box.js +89 -0
  23. package/dist/commands/box.js.map +1 -0
  24. package/dist/commands/bridge-setup.d.ts +14 -1
  25. package/dist/commands/bridge-setup.js +395 -129
  26. package/dist/commands/bridge-setup.js.map +1 -1
  27. package/dist/commands/cap-connections.d.ts +4 -4
  28. package/dist/commands/cap-connections.js +14 -14
  29. package/dist/commands/cap-connections.js.map +1 -1
  30. package/dist/commands/capability.d.ts +3 -1
  31. package/dist/commands/capability.js +249 -176
  32. package/dist/commands/capability.js.map +1 -1
  33. package/dist/commands/catalog.d.ts +275 -0
  34. package/dist/commands/catalog.js +481 -0
  35. package/dist/commands/catalog.js.map +1 -0
  36. package/dist/commands/cell.d.ts +155 -8
  37. package/dist/commands/cell.js +416 -40
  38. package/dist/commands/cell.js.map +1 -1
  39. package/dist/commands/centrality.js +1 -13
  40. package/dist/commands/centrality.js.map +1 -1
  41. package/dist/commands/close-questions.d.ts +27 -0
  42. package/dist/commands/close-questions.js +49 -0
  43. package/dist/commands/close-questions.js.map +1 -0
  44. package/dist/commands/close-questions.test.d.ts +1 -0
  45. package/dist/commands/close-questions.test.js +52 -0
  46. package/dist/commands/close-questions.test.js.map +1 -0
  47. package/dist/commands/connect.d.ts +36 -13
  48. package/dist/commands/connect.js +68 -167
  49. package/dist/commands/connect.js.map +1 -1
  50. package/dist/commands/connections.d.ts +3 -2
  51. package/dist/commands/connections.js +95 -25
  52. package/dist/commands/connections.js.map +1 -1
  53. package/dist/commands/context.js +79 -44
  54. package/dist/commands/context.js.map +1 -1
  55. package/dist/commands/data-extra.d.ts +34 -0
  56. package/dist/commands/data-extra.js +203 -25
  57. package/dist/commands/data-extra.js.map +1 -1
  58. package/dist/commands/data.d.ts +120 -2
  59. package/dist/commands/data.js +295 -21
  60. package/dist/commands/data.js.map +1 -1
  61. package/dist/commands/diagnose.d.ts +66 -4
  62. package/dist/commands/diagnose.js +285 -28
  63. package/dist/commands/diagnose.js.map +1 -1
  64. package/dist/commands/doc.d.ts +11 -4
  65. package/dist/commands/doc.js +39 -14
  66. package/dist/commands/doc.js.map +1 -1
  67. package/dist/commands/doctor.d.ts +133 -3
  68. package/dist/commands/doctor.js +761 -41
  69. package/dist/commands/doctor.js.map +1 -1
  70. package/dist/commands/explain.js +5 -5
  71. package/dist/commands/explain.js.map +1 -1
  72. package/dist/commands/hooks.d.ts +19 -0
  73. package/dist/commands/hooks.js +70 -0
  74. package/dist/commands/hooks.js.map +1 -0
  75. package/dist/commands/import.d.ts +393 -13
  76. package/dist/commands/import.js +1150 -54
  77. package/dist/commands/import.js.map +1 -1
  78. package/dist/commands/init.d.ts +34 -9
  79. package/dist/commands/init.js +481 -981
  80. package/dist/commands/init.js.map +1 -1
  81. package/dist/commands/keys.d.ts +1 -0
  82. package/dist/commands/keys.js +15 -2
  83. package/dist/commands/keys.js.map +1 -1
  84. package/dist/commands/knowledge.d.ts +22 -6
  85. package/dist/commands/knowledge.js +121 -57
  86. package/dist/commands/knowledge.js.map +1 -1
  87. package/dist/commands/lens.d.ts +37 -1
  88. package/dist/commands/lens.js +194 -72
  89. package/dist/commands/lens.js.map +1 -1
  90. package/dist/commands/market-authoring.d.ts +29 -2
  91. package/dist/commands/market-authoring.js +348 -7
  92. package/dist/commands/market-authoring.js.map +1 -1
  93. package/dist/commands/market-official.d.ts +58 -0
  94. package/dist/commands/market-official.js +84 -0
  95. package/dist/commands/market-official.js.map +1 -0
  96. package/dist/commands/market.d.ts +400 -16
  97. package/dist/commands/market.js +1802 -146
  98. package/dist/commands/market.js.map +1 -1
  99. package/dist/commands/mcp.d.ts +60 -25
  100. package/dist/commands/mcp.js +251 -64
  101. package/dist/commands/mcp.js.map +1 -1
  102. package/dist/commands/observe.category.test.d.ts +1 -0
  103. package/dist/commands/observe.category.test.js +59 -0
  104. package/dist/commands/observe.category.test.js.map +1 -0
  105. package/dist/commands/observe.d.ts +3 -0
  106. package/dist/commands/observe.js +7 -5
  107. package/dist/commands/observe.js.map +1 -1
  108. package/dist/commands/observe.title.test.d.ts +1 -0
  109. package/dist/commands/observe.title.test.js +37 -0
  110. package/dist/commands/observe.title.test.js.map +1 -0
  111. package/dist/commands/open.d.ts +39 -9
  112. package/dist/commands/open.js +80 -25
  113. package/dist/commands/open.js.map +1 -1
  114. package/dist/commands/playbook.d.ts +42 -0
  115. package/dist/commands/playbook.js +171 -0
  116. package/dist/commands/playbook.js.map +1 -0
  117. package/dist/commands/pod.js +1 -1
  118. package/dist/commands/pod.js.map +1 -1
  119. package/dist/commands/pods.d.ts +11 -1
  120. package/dist/commands/pods.js +129 -131
  121. package/dist/commands/pods.js.map +1 -1
  122. package/dist/commands/proposals-actions.d.ts +62 -0
  123. package/dist/commands/proposals-actions.js +143 -8
  124. package/dist/commands/proposals-actions.js.map +1 -1
  125. package/dist/commands/proposals-await.d.ts +68 -0
  126. package/dist/commands/proposals-await.js +248 -0
  127. package/dist/commands/proposals-await.js.map +1 -0
  128. package/dist/commands/proton-setup.d.ts +73 -0
  129. package/dist/commands/proton-setup.js +579 -0
  130. package/dist/commands/proton-setup.js.map +1 -0
  131. package/dist/commands/providers.d.ts +23 -0
  132. package/dist/commands/providers.js +126 -1
  133. package/dist/commands/providers.js.map +1 -1
  134. package/dist/commands/raycast.js +6 -2
  135. package/dist/commands/raycast.js.map +1 -1
  136. package/dist/commands/rule.d.ts +150 -0
  137. package/dist/commands/rule.js +447 -0
  138. package/dist/commands/rule.js.map +1 -0
  139. package/dist/commands/session-feed-wait.d.ts +48 -0
  140. package/dist/commands/session-feed-wait.js +261 -0
  141. package/dist/commands/session-feed-wait.js.map +1 -0
  142. package/dist/commands/session-post.d.ts +40 -0
  143. package/dist/commands/session-post.js +96 -0
  144. package/dist/commands/session-post.js.map +1 -0
  145. package/dist/commands/session-wait.d.ts +116 -0
  146. package/dist/commands/session-wait.js +224 -0
  147. package/dist/commands/session-wait.js.map +1 -0
  148. package/dist/commands/sessions.d.ts +54 -0
  149. package/dist/commands/sessions.js +501 -58
  150. package/dist/commands/sessions.js.map +1 -1
  151. package/dist/commands/skill-manage.d.ts +21 -0
  152. package/dist/commands/skill-manage.js +136 -19
  153. package/dist/commands/skill-manage.js.map +1 -1
  154. package/dist/commands/skills-commands.d.ts +2 -0
  155. package/dist/commands/skills-commands.js +9 -1
  156. package/dist/commands/skills-commands.js.map +1 -1
  157. package/dist/commands/skills-commands.test.d.ts +1 -0
  158. package/dist/commands/skills-commands.test.js +57 -0
  159. package/dist/commands/skills-commands.test.js.map +1 -0
  160. package/dist/commands/skills.js +16 -0
  161. package/dist/commands/skills.js.map +1 -1
  162. package/dist/commands/status.d.ts +1 -4
  163. package/dist/commands/status.js +6 -35
  164. package/dist/commands/status.js.map +1 -1
  165. package/dist/commands/statusline.backoff.test.d.ts +13 -0
  166. package/dist/commands/statusline.backoff.test.js +100 -0
  167. package/dist/commands/statusline.backoff.test.js.map +1 -0
  168. package/dist/commands/statusline.js +16 -9
  169. package/dist/commands/statusline.js.map +1 -1
  170. package/dist/commands/sync-status.d.ts +12 -0
  171. package/dist/commands/sync-status.js +125 -0
  172. package/dist/commands/sync-status.js.map +1 -0
  173. package/dist/commands/templates-retire.d.ts +34 -0
  174. package/dist/commands/templates-retire.js +192 -0
  175. package/dist/commands/templates-retire.js.map +1 -0
  176. package/dist/commands/tools.js +6 -21
  177. package/dist/commands/tools.js.map +1 -1
  178. package/dist/commands/tracks.d.ts +112 -0
  179. package/dist/commands/tracks.js +543 -0
  180. package/dist/commands/tracks.js.map +1 -0
  181. package/dist/commands/update.d.ts +3 -11
  182. package/dist/commands/update.js +5 -134
  183. package/dist/commands/update.js.map +1 -1
  184. package/dist/commands/upload.d.ts +41 -3
  185. package/dist/commands/upload.js +120 -18
  186. package/dist/commands/upload.js.map +1 -1
  187. package/dist/commands/vendor.d.ts +28 -0
  188. package/dist/commands/vendor.js +110 -0
  189. package/dist/commands/vendor.js.map +1 -0
  190. package/dist/commands/versions.js +6 -0
  191. package/dist/commands/versions.js.map +1 -1
  192. package/dist/commands/view.js +10 -5
  193. package/dist/commands/view.js.map +1 -1
  194. package/dist/commands/whoami.d.ts +30 -3
  195. package/dist/commands/whoami.js +186 -23
  196. package/dist/commands/whoami.js.map +1 -1
  197. package/dist/commands/worker-accounts.d.ts +149 -0
  198. package/dist/commands/worker-accounts.js +339 -0
  199. package/dist/commands/worker-accounts.js.map +1 -0
  200. package/dist/commands/worker-accounts.test.d.ts +1 -0
  201. package/dist/commands/worker-accounts.test.js +224 -0
  202. package/dist/commands/worker-accounts.test.js.map +1 -0
  203. package/dist/commands/worker.d.ts +331 -0
  204. package/dist/commands/worker.js +1067 -0
  205. package/dist/commands/worker.js.map +1 -0
  206. package/dist/commands/worker.test.d.ts +1 -0
  207. package/dist/commands/worker.test.js +427 -0
  208. package/dist/commands/worker.test.js.map +1 -0
  209. package/dist/commands/workspace-ops.d.ts +35 -0
  210. package/dist/commands/workspace-ops.js +153 -0
  211. package/dist/commands/workspace-ops.js.map +1 -0
  212. package/dist/index.d.ts +6 -6
  213. package/dist/index.js +1043 -186
  214. package/dist/index.js.map +1 -1
  215. package/dist/lib/agent-connections.d.ts +31 -0
  216. package/dist/lib/agent-connections.js +33 -0
  217. package/dist/lib/agent-connections.js.map +1 -0
  218. package/dist/lib/agent-detect.d.ts +27 -0
  219. package/dist/lib/agent-detect.js +71 -0
  220. package/dist/lib/agent-detect.js.map +1 -0
  221. package/dist/lib/agent-door.d.ts +136 -0
  222. package/dist/lib/agent-door.js +429 -0
  223. package/dist/lib/agent-door.js.map +1 -0
  224. package/dist/lib/agents-config.d.ts +2 -0
  225. package/dist/lib/agents-config.js +4 -9
  226. package/dist/lib/agents-config.js.map +1 -1
  227. package/dist/lib/approval-poll.d.ts +31 -2
  228. package/dist/lib/approval-poll.js +20 -7
  229. package/dist/lib/approval-poll.js.map +1 -1
  230. package/dist/lib/atomic-file.d.ts +48 -0
  231. package/dist/lib/atomic-file.js +129 -0
  232. package/dist/lib/atomic-file.js.map +1 -0
  233. package/dist/lib/auth.d.ts +17 -11
  234. package/dist/lib/auth.js +47 -64
  235. package/dist/lib/auth.js.map +1 -1
  236. package/dist/lib/batch-approval.d.ts +85 -0
  237. package/dist/lib/batch-approval.js +170 -0
  238. package/dist/lib/batch-approval.js.map +1 -0
  239. package/dist/lib/box/egress.d.ts +82 -0
  240. package/dist/lib/box/egress.js +264 -0
  241. package/dist/lib/box/egress.js.map +1 -0
  242. package/dist/lib/box/evidence.d.ts +86 -0
  243. package/dist/lib/box/evidence.js +121 -0
  244. package/dist/lib/box/evidence.js.map +1 -0
  245. package/dist/lib/box/job-server.d.ts +72 -0
  246. package/dist/lib/box/job-server.js +246 -0
  247. package/dist/lib/box/job-server.js.map +1 -0
  248. package/dist/lib/box/provisioner.d.ts +83 -0
  249. package/dist/lib/box/provisioner.js +312 -0
  250. package/dist/lib/box/provisioner.js.map +1 -0
  251. package/dist/lib/box/proxmox.d.ts +112 -0
  252. package/dist/lib/box/proxmox.js +211 -0
  253. package/dist/lib/box/proxmox.js.map +1 -0
  254. package/dist/lib/box/wire.d.ts +98 -0
  255. package/dist/lib/box/wire.js +70 -0
  256. package/dist/lib/box/wire.js.map +1 -0
  257. package/dist/lib/capture-lane.d.ts +6 -5
  258. package/dist/lib/capture-lane.js +6 -5
  259. package/dist/lib/capture-lane.js.map +1 -1
  260. package/dist/lib/capture-structure.d.ts +230 -11
  261. package/dist/lib/capture-structure.js +251 -13
  262. package/dist/lib/capture-structure.js.map +1 -1
  263. package/dist/lib/catalog-templates.d.ts +59 -0
  264. package/dist/lib/catalog-templates.js +126 -0
  265. package/dist/lib/catalog-templates.js.map +1 -0
  266. package/dist/lib/cli-build.d.ts +19 -0
  267. package/dist/lib/cli-build.js +37 -0
  268. package/dist/lib/cli-build.js.map +1 -0
  269. package/dist/lib/client-header.test.d.ts +1 -0
  270. package/dist/lib/client-header.test.js +85 -0
  271. package/dist/lib/client-header.test.js.map +1 -0
  272. package/dist/lib/conversation-headers.d.ts +33 -0
  273. package/dist/lib/conversation-headers.js +69 -0
  274. package/dist/lib/conversation-headers.js.map +1 -0
  275. package/dist/lib/cp-packages.d.ts +167 -11
  276. package/dist/lib/cp-packages.js +253 -21
  277. package/dist/lib/cp-packages.js.map +1 -1
  278. package/dist/lib/credential-class.d.ts +144 -0
  279. package/dist/lib/credential-class.js +138 -0
  280. package/dist/lib/credential-class.js.map +1 -0
  281. package/dist/lib/describe-lens.d.ts +35 -0
  282. package/dist/lib/describe-lens.js +40 -0
  283. package/dist/lib/describe-lens.js.map +1 -1
  284. package/dist/lib/device-connect.d.ts +125 -0
  285. package/dist/lib/device-connect.js +256 -0
  286. package/dist/lib/device-connect.js.map +1 -0
  287. package/dist/lib/directory-lens.d.ts +99 -0
  288. package/dist/lib/directory-lens.js +187 -0
  289. package/dist/lib/directory-lens.js.map +1 -0
  290. package/dist/lib/exporter-coverage.d.ts +71 -0
  291. package/dist/lib/exporter-coverage.js +97 -0
  292. package/dist/lib/exporter-coverage.js.map +1 -0
  293. package/dist/lib/goal-words.d.ts +38 -0
  294. package/dist/lib/goal-words.js +112 -0
  295. package/dist/lib/goal-words.js.map +1 -0
  296. package/dist/lib/hub-client.d.ts +57 -15
  297. package/dist/lib/hub-client.js +396 -82
  298. package/dist/lib/hub-client.js.map +1 -1
  299. package/dist/lib/init-connect.d.ts +41 -0
  300. package/dist/lib/init-connect.js +75 -0
  301. package/dist/lib/init-connect.js.map +1 -0
  302. package/dist/lib/init-exit.d.ts +24 -0
  303. package/dist/lib/init-exit.js +41 -0
  304. package/dist/lib/init-exit.js.map +1 -0
  305. package/dist/lib/init-flow.d.ts +25 -0
  306. package/dist/lib/init-flow.js +40 -0
  307. package/dist/lib/init-flow.js.map +1 -0
  308. package/dist/lib/init-handoff.d.ts +24 -0
  309. package/dist/lib/init-handoff.js +26 -0
  310. package/dist/lib/init-handoff.js.map +1 -0
  311. package/dist/lib/init-output.d.ts +18 -0
  312. package/dist/lib/init-output.js +43 -0
  313. package/dist/lib/init-output.js.map +1 -0
  314. package/dist/lib/init-pod.d.ts +45 -0
  315. package/dist/lib/init-pod.js +36 -0
  316. package/dist/lib/init-pod.js.map +1 -0
  317. package/dist/lib/installed.d.ts +210 -11
  318. package/dist/lib/installed.js +234 -36
  319. package/dist/lib/installed.js.map +1 -1
  320. package/dist/lib/key-approval.d.ts +57 -0
  321. package/dist/lib/key-approval.js +117 -0
  322. package/dist/lib/key-approval.js.map +1 -0
  323. package/dist/lib/key-diagnosis.d.ts +56 -0
  324. package/dist/lib/key-diagnosis.js +227 -0
  325. package/dist/lib/key-diagnosis.js.map +1 -0
  326. package/dist/lib/key-doctor.d.ts +136 -0
  327. package/dist/lib/key-doctor.js +286 -0
  328. package/dist/lib/key-doctor.js.map +1 -0
  329. package/dist/lib/key-locations.d.ts +120 -0
  330. package/dist/lib/key-locations.js +326 -0
  331. package/dist/lib/key-locations.js.map +1 -0
  332. package/dist/lib/key-source.d.ts +68 -0
  333. package/dist/lib/key-source.js +109 -0
  334. package/dist/lib/key-source.js.map +1 -0
  335. package/dist/lib/key-state.d.ts +70 -0
  336. package/dist/lib/key-state.js +115 -0
  337. package/dist/lib/key-state.js.map +1 -0
  338. package/dist/lib/kind-package.d.ts +181 -0
  339. package/dist/lib/kind-package.js +679 -0
  340. package/dist/lib/kind-package.js.map +1 -0
  341. package/dist/lib/knowledge-title.d.ts +25 -0
  342. package/dist/lib/knowledge-title.js +47 -0
  343. package/dist/lib/knowledge-title.js.map +1 -0
  344. package/dist/lib/knowledge-title.test.d.ts +1 -0
  345. package/dist/lib/knowledge-title.test.js +34 -0
  346. package/dist/lib/knowledge-title.test.js.map +1 -0
  347. package/dist/lib/machine-instance.d.ts +64 -0
  348. package/dist/lib/machine-instance.js +142 -0
  349. package/dist/lib/machine-instance.js.map +1 -0
  350. package/dist/lib/mcp-bridge.d.ts +49 -0
  351. package/dist/lib/mcp-bridge.js +180 -0
  352. package/dist/lib/mcp-bridge.js.map +1 -0
  353. package/dist/lib/mcp-bridge.test.d.ts +1 -0
  354. package/dist/lib/mcp-bridge.test.js +118 -0
  355. package/dist/lib/mcp-bridge.test.js.map +1 -0
  356. package/dist/lib/mcp-call.d.ts +30 -0
  357. package/dist/lib/mcp-call.js +107 -0
  358. package/dist/lib/mcp-call.js.map +1 -0
  359. package/dist/lib/mcp-snippets.d.ts +53 -0
  360. package/dist/lib/mcp-snippets.js +82 -0
  361. package/dist/lib/mcp-snippets.js.map +1 -0
  362. package/dist/lib/next-steps.d.ts +8 -0
  363. package/dist/lib/next-steps.js +31 -1
  364. package/dist/lib/next-steps.js.map +1 -1
  365. package/dist/lib/openclaw.js +3 -2
  366. package/dist/lib/openclaw.js.map +1 -1
  367. package/dist/lib/package-wire.d.ts +179 -0
  368. package/dist/lib/package-wire.js +68 -0
  369. package/dist/lib/package-wire.js.map +1 -0
  370. package/dist/lib/pair-link.d.ts +38 -0
  371. package/dist/lib/pair-link.js +68 -0
  372. package/dist/lib/pair-link.js.map +1 -0
  373. package/dist/lib/pod-moved.d.ts +70 -0
  374. package/dist/lib/pod-moved.js +146 -0
  375. package/dist/lib/pod-moved.js.map +1 -0
  376. package/dist/lib/pod-reach.d.ts +39 -0
  377. package/dist/lib/pod-reach.js +117 -0
  378. package/dist/lib/pod-reach.js.map +1 -0
  379. package/dist/lib/pod-reach.test.d.ts +1 -0
  380. package/dist/lib/pod-reach.test.js +67 -0
  381. package/dist/lib/pod-reach.test.js.map +1 -0
  382. package/dist/lib/pod-wide.d.ts +40 -0
  383. package/dist/lib/pod-wide.js +46 -0
  384. package/dist/lib/pod-wide.js.map +1 -0
  385. package/dist/lib/pod.d.ts +74 -112
  386. package/dist/lib/pod.js +146 -415
  387. package/dist/lib/pod.js.map +1 -1
  388. package/dist/lib/project-ref.d.ts +9 -3
  389. package/dist/lib/project-ref.js +10 -4
  390. package/dist/lib/project-ref.js.map +1 -1
  391. package/dist/lib/register-claude-code-mcp.test.d.ts +1 -0
  392. package/dist/lib/register-claude-code-mcp.test.js +78 -0
  393. package/dist/lib/register-claude-code-mcp.test.js.map +1 -0
  394. package/dist/lib/review-credential.d.ts +119 -0
  395. package/dist/lib/review-credential.js +149 -0
  396. package/dist/lib/review-credential.js.map +1 -0
  397. package/dist/lib/safe-open.d.ts +49 -0
  398. package/dist/lib/safe-open.js +119 -0
  399. package/dist/lib/safe-open.js.map +1 -0
  400. package/dist/lib/session-feed.d.ts +185 -0
  401. package/dist/lib/session-feed.js +257 -0
  402. package/dist/lib/session-feed.js.map +1 -0
  403. package/dist/lib/session-lens.d.ts +40 -1
  404. package/dist/lib/session-lens.js +54 -4
  405. package/dist/lib/session-lens.js.map +1 -1
  406. package/dist/lib/superwhisper-import.d.ts +8 -0
  407. package/dist/lib/superwhisper-import.js +14 -7
  408. package/dist/lib/superwhisper-import.js.map +1 -1
  409. package/dist/lib/superwhisper-title.test.d.ts +1 -0
  410. package/dist/lib/superwhisper-title.test.js +18 -0
  411. package/dist/lib/superwhisper-title.test.js.map +1 -0
  412. package/dist/lib/targets.d.ts +152 -10
  413. package/dist/lib/targets.js +974 -325
  414. package/dist/lib/targets.js.map +1 -1
  415. package/dist/lib/template-file.d.ts +4 -0
  416. package/dist/lib/template-file.js.map +1 -1
  417. package/dist/lib/template-lifecycle.d.ts +217 -0
  418. package/dist/lib/template-lifecycle.js +433 -0
  419. package/dist/lib/template-lifecycle.js.map +1 -0
  420. package/dist/lib/template-steps.d.ts +88 -0
  421. package/dist/lib/template-steps.js +118 -0
  422. package/dist/lib/template-steps.js.map +1 -0
  423. package/dist/lib/time-ago.d.ts +2 -0
  424. package/dist/lib/time-ago.js +15 -0
  425. package/dist/lib/time-ago.js.map +1 -0
  426. package/dist/lib/wake-hook-settings.d.ts +57 -0
  427. package/dist/lib/wake-hook-settings.js +153 -0
  428. package/dist/lib/wake-hook-settings.js.map +1 -0
  429. package/dist/lib/wake-state.d.ts +65 -0
  430. package/dist/lib/wake-state.js +122 -0
  431. package/dist/lib/wake-state.js.map +1 -0
  432. package/dist/utils/logger.js +5 -2
  433. package/dist/utils/logger.js.map +1 -1
  434. package/package.json +26 -17
  435. package/skills/README.md +13 -11
  436. package/skills/agent-os/SKILL.md +214 -99
  437. package/skills/manifest.json +1 -1
  438. package/skills/onboard/SKILL.md +55 -26
  439. package/skills/synap/README.md +5 -8
  440. package/skills/synap/SKILL.md +1149 -289
  441. package/skills/synap/_order.txt +7 -0
  442. package/skills/synap/_skill.yaml +1 -1
  443. package/skills/synap/automations.md +15 -0
  444. package/skills/synap/capture.md +5 -0
  445. package/skills/synap/cli-operations.md +24 -18
  446. package/skills/synap/code-skills.md +70 -0
  447. package/skills/synap/concepts.md +36 -0
  448. package/skills/synap/content-os.md +10 -1
  449. package/skills/synap/creative-loop.md +15 -10
  450. package/skills/synap/crm.md +21 -52
  451. package/skills/synap/document-embeds.md +143 -47
  452. package/skills/synap/escalation-ladder.md +20 -10
  453. package/skills/synap/focus-sessions.md +56 -21
  454. package/skills/synap/from-intent.md +158 -0
  455. package/skills/synap/governance-rules.md +5 -5
  456. package/skills/synap/governance.md +10 -7
  457. package/skills/synap/hand-off.md +33 -0
  458. package/skills/synap/inline-patterns.md +15 -7
  459. package/skills/synap/lenses.md +34 -16
  460. package/skills/synap/linking.md +42 -41
  461. package/skills/synap/mcp-remote-servers.md +64 -0
  462. package/skills/synap/mental-model.md +17 -15
  463. package/skills/synap/operating-mode-rules.md +2 -2
  464. package/skills/synap/processes.md +118 -0
  465. package/skills/synap/quick-reference.md +7 -2
  466. package/skills/synap/reading.md +3 -1
  467. package/skills/synap/reflexes.md +12 -42
  468. package/skills/synap/viewframe-cells.md +38 -5
  469. package/skills/synap/work-flow.md +1 -1
  470. package/skills/synap/work-model.md +110 -0
  471. package/skills/synap/worked-examples.md +4 -5
  472. package/skills/synap/workspace-design.md +15 -11
  473. package/skills/synap/writes.md +105 -25
  474. package/skills/synap/writing-titles.md +63 -0
  475. package/skills/synap-app/SKILL.md +149 -67
  476. package/skills/synap-app/_skill.yaml +1 -1
  477. package/skills/synap-app/creating-the-base-app.md +12 -7
  478. package/skills/synap-app/governance.md +27 -16
  479. package/skills/synap-app/install-commands.md +18 -9
  480. package/skills/synap-app/overview.md +16 -9
  481. package/skills/synap-app/using-the-sdk.md +75 -25
  482. package/skills/synap-design/SKILL.md +474 -0
  483. package/skills/synap-design/_order.txt +4 -0
  484. package/skills/synap-design/_skill.yaml +21 -0
  485. package/skills/synap-design/loop.md +134 -0
  486. package/skills/synap-design/overview.md +44 -0
  487. package/skills/synap-design/principles.md +130 -0
  488. package/skills/synap-design/system.md +133 -0
  489. package/skills/synap-market/SKILL.md +306 -16
  490. package/skills/synap-market/_skill.yaml +1 -1
  491. package/skills/synap-market/author-and-publish.md +145 -8
  492. package/skills/synap-market/governance-and-catalog.md +7 -0
  493. package/skills/synap-market/install.md +79 -1
  494. package/skills/synap-market/overview.md +74 -6
  495. package/skills/synap-schema/SKILL.md +172 -10
  496. package/skills/synap-schema/_order.txt +2 -0
  497. package/skills/synap-schema/_skill.yaml +4 -2
  498. package/skills/synap-schema/common-mistakes.md +4 -2
  499. package/skills/synap-schema/discover-profiles.md +1 -1
  500. package/skills/synap-schema/entity-scope.md +15 -3
  501. package/skills/synap-schema/extend-first.md +29 -0
  502. package/skills/synap-schema/extend-vs-create.md +1 -1
  503. package/skills/synap-schema/intent-vocabulary-ssot.md +111 -0
  504. package/skills/synap-schema/property-types.md +1 -1
  505. package/skills/synap-schema/relations.md +1 -1
  506. package/skills/synap-ui/SKILL.md +19 -12
  507. package/skills/synap-ui/_skill.yaml +1 -1
  508. package/skills/synap-ui/ai-companion.md +1 -1
  509. package/skills/synap-ui/arranging-bento.md +2 -0
  510. package/skills/synap-ui/bento-layouts.md +10 -10
  511. package/skills/synap-ui/bento-recipes.md +313 -543
  512. package/skills/synap-ui/four-builds.md +5 -0
  513. package/skills/synap-ui/view-types.md +2 -2
  514. package/skills/synap-ui/widget-catalog.md +22 -278
  515. package/templates/automations/bookmark-enrichment.automation.json +290 -13
  516. package/templates/automations/url-bookmark-capture.automation.json +73 -11
  517. package/templates/box/runner.py +206 -0
  518. package/templates/skills/authoring-playbooks/SKILL.md +6 -3
  519. package/templates/skills/authoring-playbooks/reference/stages-contract.md +15 -0
  520. package/templates/skills/authoring-playbooks/reference/worked-example.md +3 -0
  521. package/dist/commands/infra.d.ts +0 -21
  522. package/dist/commands/infra.js +0 -262
  523. package/dist/commands/infra.js.map +0 -1
  524. package/node_modules/@synap/hub-rest-client/dist/index.cjs +0 -1261
  525. package/node_modules/@synap/hub-rest-client/dist/index.d.cts +0 -1647
  526. package/node_modules/@synap/hub-rest-client/dist/index.d.ts +0 -1647
  527. package/node_modules/@synap/hub-rest-client/dist/index.js +0 -1231
  528. package/node_modules/@synap/hub-rest-client/package.json +0 -45
  529. package/node_modules/@synap/hub-rest-client/src/client.ts +0 -1631
  530. package/node_modules/@synap/hub-rest-client/src/errors.ts +0 -30
  531. package/node_modules/@synap/hub-rest-client/src/index.ts +0 -153
  532. package/node_modules/@synap/hub-rest-client/src/setup.ts +0 -133
  533. package/node_modules/@synap/hub-rest-client/src/types.ts +0 -1299
@@ -18,27 +18,133 @@
18
18
  * Hidden on purpose: this is a dogfood/dev convenience, not a first-class
19
19
  * surface. Not shown in `synap --help`.
20
20
  *
21
+ * The bridge lands data POD-WIDE by default. An OPTIONAL project scope can be
22
+ * picked interactively (or via --project-id); --workspace-id remains an
23
+ * advanced/legacy escape hatch to pin a single workspace, never prompted.
24
+ *
21
25
  * Usage:
22
26
  * synap bridge-setup --client-id <discord-app-id> [--bridge-dir <path>]
23
- * [--pod <profile-name>] [--workspace-id <uuid>]
27
+ * [--pod <profile-name>] [--workspace-id <uuid>] [--project-id <uuid>]
24
28
  * [--proactive-channel <discord-channel-id>] [--enable-react]
25
29
  * [--pod-url <url>] [--api-key <key>]
26
30
  */
27
31
  import path from "node:path";
28
32
  import os from "node:os";
29
33
  import fs from "node:fs";
34
+ import { writeSecretFile } from "../lib/atomic-file.js";
30
35
  import { createRequire } from "node:module";
31
36
  import { fileURLToPath } from "node:url";
32
37
  import chalk from "chalk";
33
38
  import ora from "ora";
34
39
  import prompts from "prompts";
35
- import { hubGet, hubPost, hubPatch, resolveUserId } from "../lib/hub-client.js";
40
+ import { hubGet, hubPost, hubPatch, resolveUserId, renderHubError } from "../lib/hub-client.js";
36
41
  import { unwrapList } from "../lib/unwrapList.js";
37
- import { discoverSkillDirs, parseSkillDir, importSkill } from "../lib/skill-import.js";
42
+ import { fetchProjects } from "../lib/project.js";
43
+ import { parseSkillDir, importSkill } from "../lib/skill-import.js";
44
+ import { CLI_TEMPLATES_DIR, resolveAgencySkillDirs, resolveAutomationTemplatesDir, resolveInstalledCatalogRoot, } from "../lib/catalog-templates.js";
38
45
  import { checkPodHealth, getSurfaceAgentKey, setSurfaceAgentKey, listPodProfiles, getSurfacePodName, setSurfacePod, podNotFoundError, } from "../lib/pod.js";
39
46
  import { enrollAgentIfNeeded, ensureAgentGovernance, provisionAgentKey, configureAgentContext, } from "../lib/targets.js";
40
47
  import { log, banner } from "../utils/logger.js";
41
48
  const DEFAULT_BRIDGE_DIR = path.join(os.homedir(), "Documents", "Code", "telegram-discord-bridge");
49
+ /** Parse a dotenv file into KEY→value. Values are used, never printed. */
50
+ function readEnvFile(filePath) {
51
+ if (!fs.existsSync(filePath))
52
+ return {};
53
+ const out = {};
54
+ for (const line of fs.readFileSync(filePath, "utf-8").split("\n")) {
55
+ const m = line.match(/^([A-Z0-9_]+)=(.*)$/);
56
+ if (m)
57
+ out[m[1]] = m[2].trim();
58
+ }
59
+ return out;
60
+ }
61
+ /** Local-only detection — no network, so it runs before the first prompt. */
62
+ function detectPriorState(bridgeDir) {
63
+ const envPath = path.join(bridgeDir, ".env");
64
+ const env = readEnvFile(envPath);
65
+ const podName = getSurfacePodName("discord");
66
+ const hasAgentKey = Boolean(getSurfaceAgentKey("discord")?.hubApiKey);
67
+ const ctx = path.join(os.homedir(), ".synap", "contexts", "discord.md");
68
+ const vaultRef = env.SYNAP_DISCORD_TOKEN_REF || undefined;
69
+ return {
70
+ isReRun: Boolean(podName || hasAgentKey || env.SYNAP_POD_URL || vaultRef),
71
+ bridgeProvisioned: Boolean(hasAgentKey || env.SYNAP_POD_URL || vaultRef),
72
+ envPath,
73
+ podName,
74
+ hasAgentKey,
75
+ contextPath: fs.existsSync(ctx) ? ctx : null,
76
+ vaultRef,
77
+ secretId: vaultRef?.startsWith("vault://")
78
+ ? vaultRef.slice("vault://".length)
79
+ : undefined,
80
+ workspaceId: env.SYNAP_WORKSPACE_ID || undefined,
81
+ projectId: env.SYNAP_PROJECT_ID || undefined,
82
+ guildId: env.DISCORD_GUILD_ID || undefined,
83
+ };
84
+ }
85
+ /**
86
+ * Report what is already configured BEFORE any prompt, so the operator can see
87
+ * this is a re-run rather than a first run — and can see exactly what will be
88
+ * reused vs. re-asked. Silent on a genuine first run (unchanged experience).
89
+ */
90
+ function printPriorState(prior, reconfigure) {
91
+ if (!prior.isReRun)
92
+ return;
93
+ log.blank();
94
+ log.heading(reconfigure
95
+ ? "Existing setup detected — --reconfigure: re-asking everything"
96
+ : "Existing setup detected — reusing what is already configured");
97
+ const found = [];
98
+ const ask = [];
99
+ const item = (label, detail, ifMissing) => {
100
+ if (detail)
101
+ found.push(` ${chalk.green("✓")} ${label.padEnd(20)}${detail}`);
102
+ else
103
+ ask.push(`${label.toLowerCase()} (${ifMissing})`);
104
+ };
105
+ item("Pod profile", prior.podName && `${prior.podName} ${chalk.dim("(discord surface)")}`, "will ask");
106
+ item("Discord agent key", prior.hasAgentKey ? "stored locally — reused, not rotated" : null, "will be minted");
107
+ item("Agent template", prior.contextPath, "will ask");
108
+ item("Bot token", prior.vaultRef ? `in the pod vault ${chalk.dim(prior.vaultRef)} — not re-asked` : null, "will ask");
109
+ item("Workspace pin", prior.workspaceId ?? null, "pod-wide");
110
+ item("Project pin", prior.projectId ?? null, "pod-wide");
111
+ item("Discord server id", prior.guildId ?? null, "pass --guild-id");
112
+ if (reconfigure) {
113
+ // Nothing below is reused — say so rather than listing it as "found".
114
+ log.dim(" Every question is asked again and every step re-provisioned.");
115
+ }
116
+ else {
117
+ for (const line of found)
118
+ console.log(line);
119
+ log.dim(ask.length
120
+ ? ` Still to configure: ${ask.join(", ")}`
121
+ : " Nothing left to ask — everything above is reused.");
122
+ }
123
+ log.dim(" Capabilities, skills and automations re-apply idempotently — each reports its own line below.");
124
+ if (!reconfigure) {
125
+ log.dim(` To re-ask everything instead: ${chalk.cyan("synap bridge-setup --reconfigure")}`);
126
+ }
127
+ log.blank();
128
+ }
129
+ /**
130
+ * Confirm the bot token a previous run vaulted is STILL in the vault, before
131
+ * deciding not to ask for it. Never returns or logs the secret itself.
132
+ *
133
+ * - "present" → skip the prompt, reuse the ref
134
+ * - "missing" → the .env ref is stale; ask for the token again (and say why)
135
+ * - "unknown" → the read failed / the door isn't deployed; keep the ref
136
+ * WITHOUT re-asking, and say it could not be verified
137
+ */
138
+ async function checkVaultSecret(cfg, workspaceId, secretId) {
139
+ try {
140
+ const res = await hubGet("/vault/secrets", { workspaceId }, cfg);
141
+ const rows = unwrapList(res, ["secrets"]);
142
+ return rows.some((r) => r.id === secretId) ? "present" : "missing";
143
+ }
144
+ catch {
145
+ return "unknown";
146
+ }
147
+ }
42
148
  // Bot invite permissions — the full set the bridge's features actually use.
43
149
  // Bits per Discord's permission reference (https://discord.com/developers/docs/topics/permissions).
44
150
  // BigInt because thread permissions exceed 2^31. Least-privilege *within* what we
@@ -73,10 +179,11 @@ const BOT_PERMISSIONS = Object.values(BOT_PERMISSION_BITS)
73
179
  * Discord bridge points at a different (e.g. team / client) pod.
74
180
  *
75
181
  * Order: explicit --pod-url/--api-key escape hatch → --pod <name> →
76
- * single saved profile (auto) → interactive picker (default = the pod already
77
- * assigned to the discord surface, else the CLI-active one).
182
+ * single saved profile (auto) → the pod THIS BRIDGE is already connected to
183
+ * (re-run, no prompt) → interactive picker (default = the pod already assigned
184
+ * to the discord surface, else the CLI-active one).
78
185
  */
79
- async function resolveBridgePod(opts) {
186
+ async function resolveBridgePod(opts, prior, reconfigure) {
80
187
  // 1. Raw URL+key escape hatch — bypasses profiles entirely.
81
188
  if (opts.podUrl && opts.apiKey) {
82
189
  return {
@@ -99,6 +206,14 @@ async function resolveBridgePod(opts) {
99
206
  chosen = profiles[0];
100
207
  log.dim(`Using your only saved pod profile: ${chosen.name}`);
101
208
  }
209
+ else if (!reconfigure &&
210
+ prior.podName &&
211
+ profiles.some((p) => p.name === prior.podName)) {
212
+ // Re-run: the bridge is already bound to a pod (discord surface override).
213
+ // Re-asking would only invite a mis-click that repoints a live bridge.
214
+ chosen = profiles.find((p) => p.name === prior.podName);
215
+ log.dim(` [skip] pod — already connected to "${chosen.name}" (--pod or --reconfigure to change)`);
216
+ }
102
217
  else {
103
218
  const defaultName = getSurfacePodName("discord") ?? profiles.find((p) => p.active)?.name ?? profiles[0].name;
104
219
  const initial = Math.max(0, profiles.findIndex((p) => p.name === defaultName));
@@ -124,7 +239,9 @@ async function resolveBridgePod(opts) {
124
239
  cfg: {
125
240
  podUrl: c.podUrl,
126
241
  apiKey: c.hubApiKey,
127
- userId: c.agentUserId,
242
+ // Acting user asked of the pod; the key's agent id rides apart (HubConfig.userId).
243
+ userId: "cli",
244
+ agentUserId: c.agentUserId || undefined,
128
245
  workspaceId: c.workspaceId || undefined,
129
246
  },
130
247
  podName: chosen.name,
@@ -133,11 +250,17 @@ async function resolveBridgePod(opts) {
133
250
  export async function bridgeSetup(opts) {
134
251
  banner();
135
252
  log.heading("Discord bridge — one-shot setup");
253
+ // ── 0. Detect what a previous run already configured, and SAY SO before we
254
+ // ask anything. Purely local — no network, no prompt. ────────────────
255
+ const bridgeDir = opts.bridgeDir ?? DEFAULT_BRIDGE_DIR;
256
+ const reconfigure = Boolean(opts.reconfigure) || process.env.SYNAP_BRIDGE_SETUP_RECONFIGURE === "1";
257
+ const prior = detectPriorState(bridgeDir);
258
+ printPriorState(prior, reconfigure);
136
259
  // ── 1. Choose the pod the BRIDGE connects to (independent of the CLI pod) ──
137
260
  let cfg;
138
261
  let bridgePodName = null;
139
262
  try {
140
- const resolved = await resolveBridgePod(opts);
263
+ const resolved = await resolveBridgePod(opts, prior, reconfigure);
141
264
  cfg = resolved.cfg;
142
265
  bridgePodName = resolved.podName;
143
266
  }
@@ -157,13 +280,11 @@ export async function bridgeSetup(opts) {
157
280
  process.exit(1);
158
281
  }
159
282
  log.success(`Pod healthy: ${cfg.podUrl}${bridgePodName ? ` (profile: ${bridgePodName})` : ""}`);
160
- // ── 3. Resolve workspace ───────────────────────────────────────────────
161
- const workspaceId = await resolveWorkspaceId(opts.workspaceId, cfg.workspaceId, cfg);
162
- if (!workspaceId) {
163
- log.error("Could not resolve a workspace. Pass --workspace-id <uuid>.");
164
- process.exit(1);
165
- }
166
- log.dim(`Workspace: ${workspaceId}`);
283
+ // ── 3. Resolve scope — pod-wide by default, workspace/project optional ───
284
+ const { workspaceId, projectId } = await resolveScope(opts, cfg, prior, reconfigure);
285
+ log.dim(workspaceId ? `Workspace: ${workspaceId} (explicit pin)` : "Workspace: pod-wide (no pin)");
286
+ if (projectId)
287
+ log.dim(`Project: ${projectId}`);
167
288
  // ── 3b. Key + scope sanity check (GET /capabilities needs hub-protocol.read) ─
168
289
  const probe = ora("Checking key scope…").start();
169
290
  try {
@@ -172,7 +293,7 @@ export async function bridgeSetup(opts) {
172
293
  }
173
294
  catch (err) {
174
295
  probe.fail("Key/scope check failed");
175
- log.error(err.message);
296
+ renderHubError(err);
176
297
  log.dim("The key may be expired or missing scope. Run `synap keys rotate` or `synap init`.");
177
298
  process.exit(1);
178
299
  }
@@ -182,33 +303,65 @@ export async function bridgeSetup(opts) {
182
303
  // reads to the operator's data floor (hub-protocol-rest.ts auth middleware)
183
304
  // while attributing writes to the agent for governed proposals. All the
184
305
  // operator-key work below (capability apply, vault grant) keeps using `cfg`.
185
- const agentKey = await provisionDiscordAgentKey(cfg.podUrl, cfg.apiKey, cfg.workspaceId);
306
+ const agentKey = await provisionDiscordAgentKey(cfg.podUrl, cfg.apiKey, cfg.workspaceId, !reconfigure ? prior.contextPath : null);
186
307
  // ── 4. Provision the capability (vault credential + tool + skill) ─────────
187
- const bridgeDir = opts.bridgeDir ?? DEFAULT_BRIDGE_DIR;
188
- const botToken = await resolveBotToken(opts.botToken);
189
- const { vaultRef, secretId } = await applyBridgeCapability(bridgeDir, workspaceId, cfg, botToken);
308
+ // The bot token is the sharpest re-run case: it is ALREADY in the pod vault,
309
+ // so re-prompting for it asks the operator to paste a live secret the system
310
+ // holds. Verify the vaulted secret instead, and only ask when it is genuinely
311
+ // gone (or when --reconfigure / --bot-token says to rotate it).
312
+ let botToken;
313
+ let reuseVaultRef;
314
+ if (opts.botToken) {
315
+ botToken = opts.botToken;
316
+ }
317
+ else if (!reconfigure && prior.secretId) {
318
+ const state = await checkVaultSecret(cfg, workspaceId, prior.secretId);
319
+ if (state === "present") {
320
+ reuseVaultRef = prior.vaultRef;
321
+ log.dim(` [skip] bot token — already in the pod vault (${prior.vaultRef}); not re-asked`);
322
+ }
323
+ else if (state === "unknown") {
324
+ reuseVaultRef = prior.vaultRef;
325
+ log.warn(` [warn] bot token — could not read the vault to confirm ${prior.vaultRef}; ` +
326
+ "keeping it rather than asking you to paste the token again (--reconfigure to re-enter).");
327
+ }
328
+ else {
329
+ log.warn(` [warn] bot token — the vault no longer holds ${prior.vaultRef} (deleted or on another pod); asking again.`);
330
+ botToken = await resolveBotToken(undefined);
331
+ }
332
+ }
333
+ else {
334
+ botToken = await resolveBotToken(undefined);
335
+ }
336
+ const { vaultRef, secretId, error: capabilityError } = await applyBridgeCapability(bridgeDir, workspaceId, cfg, botToken, reuseVaultRef);
190
337
  // ── 4. Choose agent governance mode ──────────────────────────────────
191
338
  const discordAgentUserId = getSurfaceAgentKey("discord")?.agentUserId;
192
339
  if (discordAgentUserId) {
193
- await ensureAgentGovernance(cfg, discordAgentUserId, opts.governance);
340
+ // A re-run leaves the agent's governance exactly as the operator set it.
341
+ // (The pod exposes no GET for it, so we say "unchanged" — never claim a
342
+ // specific mode we did not read.)
343
+ if (!opts.governance && !reconfigure && prior.bridgeProvisioned) {
344
+ log.dim(" [skip] governance — left unchanged from your previous run " +
345
+ "(--governance safe|normal|crazy, or --reconfigure, to change it)");
346
+ }
347
+ else {
348
+ await ensureAgentGovernance(cfg, discordAgentUserId, opts.governance);
349
+ }
194
350
  }
195
351
  // ── 4b. Seed the bundled instruction skills FIRST, then the capabilities ────
196
- // Order matters: the `stellar-scf-grants-advisor` skill now ALSO ships embedded
197
- // in the stellar-grant-client capability (so a plain `packages/apply` install
198
- // stands it up with NO bridge-setup). Seeding the bundled SKILL.md dir first
199
- // attaches its full reference-doc corpus; the capability apply below then
200
- // REUSES that same pod-scoped skill row by name (create-from-definition is
201
- // idempotent) instead of creating a docless instruction-only copy.
352
+ // Order matters: seeding the bundled SKILL.md dirs first attaches their full
353
+ // reference-doc corpus; a capability apply below that embeds a skill of the
354
+ // same name REUSES that pod-scoped row (create-from-definition is idempotent)
355
+ // instead of creating a docless instruction-only copy.
202
356
  await applyAgencySkills(cfg);
203
357
  // ── 4c. Apply the agency capability templates (idempotent, self-healing) ────
204
358
  await applyAgencyCapabilities(cfg, workspaceId);
205
359
  // ── 4e. Seed the GENERIC automation templates (idempotent, event-driven) ────
206
- // Only the domain-agnostic automations live in templates/automations/ now
207
- // (mail-feed, url/bookmark capture). The grant-specific automations
208
- // (stellar-grant-provision + proactive-grant-review) SHIP INSIDE the
209
- // stellar-grant-client capability's automations[] (seeded above via
210
- // applyAgencyCapabilities), so any install door — including a plain
211
- // packages/apply with no bridge-setup — stands them up. Thread the operator's
360
+ // Only the domain-agnostic automations are seeded here (mail-feed,
361
+ // url/bookmark capture), read from the installed catalog package when it
362
+ // ships them, else from templates/automations/. The grant operation itself
363
+ // ships with the `grants` workspace template (its "Grant Process" method +
364
+ // automations). Thread the operator's
212
365
  // chosen proactive channel through — output nodes pin their post target with it
213
366
  // when it's a Synap channels.id (see the note in applyAutomationTemplates).
214
367
  await applyAutomationTemplates(cfg, workspaceId, opts.proactiveChannel);
@@ -219,16 +372,22 @@ export async function bridgeSetup(opts) {
219
372
  await setDiscordToolConfig(cfg, workspaceId, { reactCapture: true });
220
373
  }
221
374
  // ── 4d. Grant the bridge redeem access — token lives ONLY in the vault ─────
375
+ // Re-run path: `secretId` is recovered from the .env vault ref, so the grant
376
+ // is re-verified (it answers "(existing)") WITHOUT the token being re-entered.
222
377
  let granted = false;
223
- if (secretId && botToken) {
378
+ if (secretId) {
224
379
  // Grant to the AGENT (the bridge's redeem identity), not the operator.
225
380
  const agentUserId = getSurfaceAgentKey("discord")?.agentUserId;
226
381
  granted = await grantRedeemAccess(secretId, cfg, agentUserId);
227
382
  }
228
383
  // ── 5. Proactive delivery routing (optional) ─────────────────────────────
229
- if (opts.proactiveChannel) {
384
+ // Delivery preferences live ON a workspace — pod-wide has none to route to.
385
+ if (opts.proactiveChannel && workspaceId) {
230
386
  await configureProactive(workspaceId, opts.proactiveChannel, cfg);
231
387
  }
388
+ else if (opts.proactiveChannel) {
389
+ log.warn("Proactive routing needs a workspace — pass --workspace-id to enable it.");
390
+ }
232
391
  // ── 6. Write the bridge .env ─────────────────────────────────────────────
233
392
  const envPath = path.join(bridgeDir, ".env");
234
393
  const managed = {
@@ -237,12 +396,14 @@ export async function bridgeSetup(opts) {
237
396
  // the operator's raw key. Reads remap to the operator floor; writes are
238
397
  // governed as the agent.
239
398
  SYNAP_HUB_API_KEY: agentKey,
240
- SYNAP_WORKSPACE_ID: workspaceId,
241
399
  };
242
- // Project is a peer lens to workspace — only injected when the resolved
243
- // config carries one (e.g. via the durable `activeProjectId` or session lens).
244
- if (cfg.projectId)
245
- managed.SYNAP_PROJECT_ID = cfg.projectId;
400
+ // Pod-wide by default: SYNAP_WORKSPACE_ID/SYNAP_PROJECT_ID are only written
401
+ // when the operator explicitly pinned a workspace (--workspace-id) or chose
402
+ // a project in the picker. Neither is required.
403
+ if (workspaceId)
404
+ managed.SYNAP_WORKSPACE_ID = workspaceId;
405
+ if (projectId)
406
+ managed.SYNAP_PROJECT_ID = projectId;
246
407
  // The token is NEVER written to .env — it lives in the pod vault and the
247
408
  // bridge redeems it on boot via this ref. (Requires the grant below to land;
248
409
  // if it didn't, we warn loudly rather than leaking the token to env.)
@@ -253,8 +414,15 @@ export async function bridgeSetup(opts) {
253
414
  if (opts.proactiveChannel)
254
415
  managed.SYNAP_PROACTIVE_CHANNEL_ID = opts.proactiveChannel;
255
416
  try {
256
- // Once the token is in the vault, purge any stale env copy of it.
257
- upsertEnv(envPath, managed, vaultRef ? ["DISCORD_BOT_TOKEN"] : []);
417
+ // Once the token is in the vault, purge any stale env copy of it. Also
418
+ // purge a stale scope pin from a prior run — re-running pod-wide (no
419
+ // --workspace-id / no project picked) must actually clear an old pin.
420
+ const removeKeys = [
421
+ ...(vaultRef ? ["DISCORD_BOT_TOKEN"] : []),
422
+ ...(workspaceId ? [] : ["SYNAP_WORKSPACE_ID"]),
423
+ ...(projectId ? [] : ["SYNAP_PROJECT_ID"]),
424
+ ];
425
+ upsertEnv(envPath, managed, removeKeys);
258
426
  log.success(`Wrote pod credentials → ${envPath}`);
259
427
  }
260
428
  catch (err) {
@@ -270,10 +438,12 @@ export async function bridgeSetup(opts) {
270
438
  const envHasGuild = fs.existsSync(envPath) &&
271
439
  /^DISCORD_GUILD_ID=.+/m.test(fs.readFileSync(envPath, "utf-8"));
272
440
  printNextSteps(opts.clientId, bridgeDir, {
273
- hasToken: Boolean(botToken),
441
+ hasToken: Boolean(botToken) || Boolean(reuseVaultRef),
274
442
  hasGuild: Boolean(opts.guildId) || envHasGuild,
275
443
  vaultRef,
276
444
  granted,
445
+ reusedToken: Boolean(reuseVaultRef),
446
+ capabilityError,
277
447
  });
278
448
  await printSynapNextSteps(cfg, workspaceId);
279
449
  }
@@ -295,11 +465,6 @@ export async function bridgeSetup(opts) {
295
465
  const AGENCY_CAPABILITY_PLAN = [
296
466
  { key: "nango-google", needs: null, params: {}, workspaceScoped: false },
297
467
  { key: "agency-skills", needs: null, params: {}, workspaceScoped: true },
298
- // stellar-grant-client bundles the grant PLAYBOOK, all four grant AUTOMATIONS
299
- // (including stellar-grant-provision + proactive-grant-review, moved here from
300
- // templates/automations/), and the stellar-scf-grants-advisor SKILL — so one
301
- // capability apply stands up the whole grant operation on ANY install door.
302
- { key: "stellar-grant-client", needs: null, params: {}, workspaceScoped: true },
303
468
  // Cal.com scheduling connector — requires a `calApiKey` param (a `cal_live_...`
304
469
  // token), so gate it like the other keyed connectors: skip gracefully unless
305
470
  // CALCOM_API_KEY is set. Seeds the `cal_com` tool + booking skills + vault key,
@@ -326,25 +491,6 @@ const AGENCY_CAPABILITY_PLAN = [
326
491
  workspaceScoped: false,
327
492
  },
328
493
  ];
329
- /**
330
- * Resolve the automations templates directory — CLI-BUNDLED (like the skills at
331
- * templates/skills). The backend stays agnostic: it ships no feature templates;
332
- * automation config is bundled with the CLI that seeds it.
333
- *
334
- * Resolution order:
335
- * 1. AUTOMATION_TEMPLATES_DIR env var (override for CI / custom installs)
336
- * 2. CLI-bundled default: <cli-package-root>/templates/automations
337
- */
338
- function resolveAutomationsTemplatesDir() {
339
- if (process.env.AUTOMATION_TEMPLATES_DIR) {
340
- const override = process.env.AUTOMATION_TEMPLATES_DIR;
341
- return fs.existsSync(override) ? override : null;
342
- }
343
- const thisFile = fileURLToPath(import.meta.url);
344
- const cliRoot = path.resolve(path.dirname(thisFile), "..", "..");
345
- const candidate = path.resolve(cliRoot, "templates", "automations");
346
- return fs.existsSync(candidate) ? candidate : null;
347
- }
348
494
  /**
349
495
  * Apply the agency capability templates idempotently via the Control Plane catalog.
350
496
  *
@@ -367,6 +513,12 @@ async function applyAgencyCapabilities(cfg, workspaceId) {
367
513
  log.dim(` [skip] ${item.key} — set ${item.needs} to seed it`);
368
514
  continue;
369
515
  }
516
+ // Playbook-bundling capabilities require a workspace to land in — pod-wide
517
+ // (no --workspace-id) has none, so skip rather than round-trip a known 4xx.
518
+ if (item.workspaceScoped && !workspaceId) {
519
+ log.dim(` [skip] ${item.key} — bundles playbooks; pass --workspace-id to seed it`);
520
+ continue;
521
+ }
370
522
  try {
371
523
  const res = (await hubPost("/capabilities/apply", item.workspaceScoped
372
524
  ? { templateKey: item.key, params: item.params, workspaceId }
@@ -396,19 +548,22 @@ export function resolveBundledSkillsDir() {
396
548
  return fs.existsSync(candidate) ? candidate : null;
397
549
  }
398
550
  /**
399
- * Seed the bundled Synap-native skills from templates/skills/ (standard SKILL.md
400
- * directories). Each becomes a pod-wide instruction skill via /agent-skills/import;
401
- * references/ files become linked documents. Idempotent (slug-unique guard).
551
+ * Seed the Synap-native skills (standard SKILL.md directories): the agency
552
+ * skills from the installed catalog package when it ships them, else from
553
+ * templates/skills/; the CLI runtime skills from templates/skills/. Each becomes
554
+ * a pod-wide instruction skill via /agent-skills/import; references/ files
555
+ * become linked documents. Idempotent (slug-unique guard).
402
556
  */
403
557
  async function applyAgencySkills(cfg) {
404
- const skillsDir = resolveBundledSkillsDir();
405
- if (!skillsDir) {
558
+ const catalogRoot = resolveInstalledCatalogRoot();
559
+ const { dirs, fromCatalog } = resolveAgencySkillDirs({ cliSkillsDir: resolveBundledSkillsDir(), catalogRoot });
560
+ if (dirs.length === 0) {
406
561
  log.warn("Bundled skill templates not found — skipping.");
407
562
  return;
408
563
  }
409
564
  const userId = await resolveUserId(cfg);
410
- const dirs = discoverSkillDirs(skillsDir);
411
- log.dim(`Seeding ${dirs.length} bundled skill(s) from ${skillsDir}…`);
565
+ log.dim(`Seeding ${dirs.length} skill(s)` +
566
+ (fromCatalog.length ? ` (${fromCatalog.length} from ${catalogRoot})` : "") + "…");
412
567
  for (const dir of dirs) {
413
568
  const parsed = parseSkillDir(dir);
414
569
  if (!parsed)
@@ -559,8 +714,8 @@ async function resolveGoogleConnectionId(cfg, workspaceId) {
559
714
  const google = containers.find((c) => /google/i.test(c.name ?? ""));
560
715
  if (!google?.id)
561
716
  return null;
562
- const connRes = await hubGet(`/capabilities/${google.id}/connections`, {}, cfg);
563
- const conns = unwrapList(connRes, ["connections"]);
717
+ const connRes = await hubGet(`/capabilities/${google.id}/credentials`, {}, cfg);
718
+ const conns = unwrapList(connRes, ["credentials"]);
564
719
  if (conns.length === 0)
565
720
  return null;
566
721
  return (conns.find((c) => c.isDefault) ?? conns[0]).id;
@@ -570,7 +725,9 @@ async function resolveGoogleConnectionId(cfg, workspaceId) {
570
725
  }
571
726
  }
572
727
  /**
573
- * Apply automation templates from the CLI-bundled templates/automations/*.automation.json.
728
+ * Apply the *.automation.json templates: AUTOMATION_TEMPLATES_DIR when set, else
729
+ * the installed catalog package's automations/ when it ships them, else the
730
+ * CLI's templates/automations/.
574
731
  *
575
732
  * For each file:
576
733
  * - Fill placeholders + validate references in the flowDefinition:
@@ -588,7 +745,11 @@ async function resolveGoogleConnectionId(cfg, workspaceId) {
588
745
  * - Aborts (never partial) if the prerequisite skills/automations fetch fails.
589
746
  */
590
747
  async function applyAutomationTemplates(cfg, workspaceId, feedChannelId) {
591
- const templatesDir = resolveAutomationsTemplatesDir();
748
+ const templatesDir = resolveAutomationTemplatesDir({
749
+ override: process.env.AUTOMATION_TEMPLATES_DIR,
750
+ catalogRoot: resolveInstalledCatalogRoot(),
751
+ cliDir: path.join(CLI_TEMPLATES_DIR, "automations"),
752
+ });
592
753
  if (!templatesDir) {
593
754
  log.warn("Automation templates dir not found — skipping. " +
594
755
  "Set AUTOMATION_TEMPLATES_DIR=<path> to seed from a custom location.");
@@ -613,6 +774,32 @@ async function applyAutomationTemplates(cfg, workspaceId, feedChannelId) {
613
774
  log.warn(`Could not fetch existing automations — aborting seeding: ${err.message}`);
614
775
  return;
615
776
  }
777
+ // Fetch the capability catalog's verb ids once — needed for capability-node
778
+ // validation. This is a DIFFERENT namespace from `/skills`: a catalog verb's
779
+ // backing skill need not appear in the `/skills` list at all. Dogfood: the
780
+ // `ai.generate` verb on "Synap Core" is `enabled: true, runnable: true` and is
781
+ // what every AI automation node uses, yet its backing skill
782
+ // (e441519b-…) is absent from all 50 rows `/skills` returns. Validating verbs
783
+ // against skill NAMES therefore marked every AI automation as missing and
784
+ // silently skipped it.
785
+ let catalogVerbIds = new Set();
786
+ try {
787
+ const res = await hubGet("/capabilities/catalog", { userId, workspaceId }, cfg);
788
+ for (const cap of unwrapList(res, [
789
+ "capabilities",
790
+ ])) {
791
+ for (const v of cap.verbs ?? [])
792
+ if (v.verbId)
793
+ catalogVerbIds.add(v.verbId);
794
+ }
795
+ }
796
+ catch (err) {
797
+ // NON-fatal, unlike the skills fetch: an empty verb set must not skip every
798
+ // AI automation. Fall back to accepting any verbId and let the runtime be the
799
+ // judge — a seeded automation that fails loudly beats one silently not seeded.
800
+ log.warn(`Could not fetch the capability catalog — verb ids will not be pre-validated: ${err.message}`);
801
+ catalogVerbIds = new Set();
802
+ }
616
803
  // Fetch existing skills once — needed for skill-node injection.
617
804
  let existingSkills = [];
618
805
  try {
@@ -660,11 +847,16 @@ async function applyAutomationTemplates(cfg, workspaceId, feedChannelId) {
660
847
  missing ??= `skill "${skillName}"`;
661
848
  }
662
849
  }
663
- // `capability` node → validate verbId (a verb IS a seeded skill NAME) and
664
- // fill an empty/placeholder connection id with the Google/Nango connection.
850
+ // `capability` node → validate verbId against the CAPABILITY CATALOG first
851
+ // (its real home), falling back to seeded skill names for verbs that are
852
+ // skill-backed by name. When the catalog could not be fetched the set is
853
+ // empty and we do not pre-validate at all — see the fetch above.
665
854
  if (node.type === "capability") {
666
855
  const data = node.data;
667
- if (data.verbId && !existingSkills.some((s) => s.name === data.verbId)) {
856
+ if (data.verbId &&
857
+ catalogVerbIds.size > 0 &&
858
+ !catalogVerbIds.has(data.verbId) &&
859
+ !existingSkills.some((s) => s.name === data.verbId)) {
668
860
  missing ??= `verb "${data.verbId}"`;
669
861
  }
670
862
  const sel = data.connectionSelector;
@@ -800,9 +992,12 @@ async function applyAutomationTemplates(cfg, workspaceId, feedChannelId) {
800
992
  * @param podUrl Pod base URL.
801
993
  * @param operatorKey The operator's raw hub key (authorizes the provisioning).
802
994
  * @param workspaceId Workspace to enroll the agent into (omit for all).
995
+ * @param existingContextPath Path to the CONTEXT.md a previous run already
996
+ * wrote — when set, the agent-template wizard is SKIPPED (same reuse-and-say-so
997
+ * contract as the agent key itself). Pass null to run it.
803
998
  * @returns the Discord agent key to write as SYNAP_HUB_API_KEY.
804
999
  */
805
- async function provisionDiscordAgentKey(podUrl, operatorKey, workspaceId) {
1000
+ async function provisionDiscordAgentKey(podUrl, operatorKey, workspaceId, existingContextPath) {
806
1001
  const spinner = ora("Provisioning Discord agent key…").start();
807
1002
  let hubApiKey;
808
1003
  let agentUserId;
@@ -833,44 +1028,67 @@ async function provisionDiscordAgentKey(podUrl, operatorKey, workspaceId) {
833
1028
  // Enroll the agent user into the operator's workspace(s) using the OPERATOR
834
1029
  // key (before the bridge ever uses the agent key).
835
1030
  await enrollAgentIfNeeded(podUrl, operatorKey, agentUserId, workspaceId);
836
- // Same CONTEXT.md routing file every other provisioned agent gets.
837
- try {
838
- await configureAgentContext(podUrl, operatorKey, "discord", agentUserId);
1031
+ // Same CONTEXT.md routing file every other provisioned agent gets — written
1032
+ // once. A re-run keeps the template the operator already picked.
1033
+ if (existingContextPath) {
1034
+ log.dim(` [skip] agent template — already configured (${existingContextPath}); --reconfigure to pick another`);
839
1035
  }
840
- catch (err) {
841
- log.warn(`Agent context wizard failed: ${err instanceof Error ? err.message : String(err)}`);
1036
+ else {
1037
+ try {
1038
+ await configureAgentContext(podUrl, operatorKey, "discord", agentUserId);
1039
+ }
1040
+ catch (err) {
1041
+ log.warn(`Agent context wizard failed: ${err instanceof Error ? err.message : String(err)}`);
1042
+ }
842
1043
  }
843
1044
  return hubApiKey;
844
1045
  }
845
- async function resolveWorkspaceId(explicit, defaultWs, cfg) {
846
- // An explicit --workspace-id wins outright. Otherwise ALWAYS ask (with the
847
- // saved profile workspace pre-selected) — the operator must consciously pick
848
- // the workspace every channel + entity the bridge creates will live in (e.g.
849
- // your CRM workspace), instead of silently inheriting the saved default.
850
- if (explicit)
851
- return explicit;
852
- let list = [];
853
- try {
854
- const res = (await hubGet("/workspaces", {}, cfg));
855
- const arr = unwrapList(res, ["workspaces"]);
856
- list = arr.filter((w) => w?.id);
1046
+ /**
1047
+ * Resolve the OPTIONAL scope the bridge writes into — pod-wide by default.
1048
+ *
1049
+ * Workspace: an explicit --workspace-id is the only way to pin one (advanced/
1050
+ * legacy escape hatch) — never prompted, never required.
1051
+ *
1052
+ * Project: a peer, independent lens. --project-id skips the prompt; otherwise
1053
+ * offer a picker with "Pod-wide (no project)" as the default alongside the
1054
+ * operator's existing projects. No projects on the pod → skip the prompt.
1055
+ *
1056
+ * Re-run: BOTH lenses are inherited from the bridge's own .env instead of being
1057
+ * re-asked. That also fixes a real re-run defect — re-running without
1058
+ * --workspace-id used to silently UNPIN a bridge that had been pinned (the .env
1059
+ * key is purged), which in turn silently skipped every playbook-bundling
1060
+ * capability. --reconfigure restores the ask-me-everything behaviour.
1061
+ */
1062
+ async function resolveScope(opts, cfg, prior, reconfigure) {
1063
+ let workspaceId = opts.workspaceId;
1064
+ if (!workspaceId && !reconfigure && prior.workspaceId) {
1065
+ workspaceId = prior.workspaceId;
1066
+ log.dim(` [skip] workspace — keeping the pin from your previous run (${workspaceId}); --reconfigure to clear it`);
1067
+ }
1068
+ let projectId = opts.projectId;
1069
+ if (!projectId && !reconfigure && prior.bridgeProvisioned) {
1070
+ projectId = prior.projectId;
1071
+ log.dim(prior.projectId
1072
+ ? ` [skip] project — keeping ${prior.projectId} from your previous run (--project-id or --reconfigure to change)`
1073
+ : " [skip] project — you chose pod-wide previously; keeping it (--project-id or --reconfigure to change)");
1074
+ }
1075
+ else if (!projectId) {
1076
+ const projects = await fetchProjects(cfg);
1077
+ if (projects.length > 0) {
1078
+ const { picked } = await prompts({
1079
+ type: "select",
1080
+ name: "picked",
1081
+ message: "Which project should the bridge write into? (optional — pod-wide by default)",
1082
+ initial: 0,
1083
+ choices: [
1084
+ { title: "Pod-wide (no project)", value: "" },
1085
+ ...projects.map((p) => ({ title: p.name, description: p.id, value: p.id })),
1086
+ ],
1087
+ });
1088
+ projectId = picked || undefined;
1089
+ }
857
1090
  }
858
- catch {
859
- return defaultWs;
860
- }
861
- if (list.length === 0)
862
- return defaultWs;
863
- if (list.length === 1)
864
- return list[0].id;
865
- const defaultIdx = defaultWs ? list.findIndex((w) => w.id === defaultWs) : -1;
866
- const { ws } = await prompts({
867
- type: "select",
868
- name: "ws",
869
- message: "Which workspace should the bridge write into? (channels + entities land here)",
870
- choices: list.map((w) => ({ title: `${w.name ?? "(unnamed)"} ${chalk.dim(w.id)}`, value: w.id })),
871
- initial: defaultIdx >= 0 ? defaultIdx : 0,
872
- });
873
- return ws || defaultWs;
1091
+ return { workspaceId, projectId };
874
1092
  }
875
1093
  async function resolveBotToken(provided) {
876
1094
  if (provided)
@@ -882,8 +1100,27 @@ async function resolveBotToken(provided) {
882
1100
  });
883
1101
  return token || undefined;
884
1102
  }
885
- async function applyBridgeCapability(bridgeDir, workspaceId, cfg, botToken) {
1103
+ /**
1104
+ * @param existingVaultRef `vault://<id>` from a previous run. When there is no
1105
+ * NEW token but the credential is already vaulted, the definition is applied
1106
+ * with the vault entry dropped (we must never store a `{{botToken}}`
1107
+ * placeholder) while the tools' credentialRef is REPOINTED at the existing
1108
+ * secret — which is byte-identical to what a token-carrying apply produces,
1109
+ * since createVaultSecret reuses the same secret id. Without this the re-run
1110
+ * would strip the tool's credentialRef and break a working bridge.
1111
+ */
1112
+ async function applyBridgeCapability(bridgeDir, workspaceId, cfg, botToken, existingVaultRef) {
886
1113
  const spinner = ora("Provisioning capability…").start();
1114
+ // What we still know about the vaulted credential if the apply can't run —
1115
+ // returning it keeps the .env + grant + summary truthful on a re-run.
1116
+ const reuse = existingVaultRef
1117
+ ? {
1118
+ vaultRef: existingVaultRef,
1119
+ secretId: existingVaultRef.startsWith("vault://")
1120
+ ? existingVaultRef.slice("vault://".length)
1121
+ : undefined,
1122
+ }
1123
+ : {};
887
1124
  // Read the capability definition from the bridge repo — ONE source of truth.
888
1125
  const capPath = path.join(bridgeDir, "src", "synapCapabilities.js");
889
1126
  let rawDef;
@@ -899,11 +1136,11 @@ async function applyBridgeCapability(bridgeDir, workspaceId, cfg, botToken) {
899
1136
  spinner.fail("Capability definition not found");
900
1137
  log.dim(` Looked in: ${capPath}`);
901
1138
  log.dim(` ${err.message}`);
902
- return {};
1139
+ return { ...reuse, error: `capability definition not found at ${capPath}` };
903
1140
  }
904
1141
  if (!rawDef) {
905
1142
  spinner.fail("No CAPABILITY_DEFINITION export in the bridge");
906
- return {};
1143
+ return { ...reuse, error: "no CAPABILITY_DEFINITION export in the bridge" };
907
1144
  }
908
1145
  // Deep-clone + inject the bot token into the vault credential. With no token,
909
1146
  // drop the vault + credentialRef so we never store an empty/placeholder secret.
@@ -916,25 +1153,32 @@ async function applyBridgeCapability(bridgeDir, workspaceId, cfg, botToken) {
916
1153
  }
917
1154
  }
918
1155
  else {
919
- // No token → drop the vault and any tool credentialRef that referenced it
920
- // (a bare template-local ref or a vault:// string), so we never store a
921
- // placeholder secret or leave a dangling reference.
1156
+ // No NEW token → drop the vault entry, so we never store a placeholder
1157
+ // secret. Two cases for the tools that referenced it:
1158
+ // · re-run (existingVaultRef) → REPOINT at the already-vaulted secret,
1159
+ // preserving the exact wiring a token-carrying apply would produce.
1160
+ // · first run with no token → delete the ref (no dangling reference).
922
1161
  const vaultRefs = new Set((def.vault ?? []).map((v) => v.ref).filter((r) => Boolean(r)));
923
1162
  delete def.vault;
924
1163
  for (const t of def.tools ?? []) {
925
1164
  if (t.credentialRef &&
926
1165
  (vaultRefs.has(t.credentialRef) || t.credentialRef.startsWith("vault://"))) {
927
- delete t.credentialRef;
1166
+ if (existingVaultRef)
1167
+ t.credentialRef = existingVaultRef;
1168
+ else
1169
+ delete t.credentialRef;
928
1170
  }
929
1171
  }
930
- spinner.text = "Provisioning capability (no token → structure only)…";
1172
+ spinner.text = existingVaultRef
1173
+ ? "Provisioning capability (token already vaulted → reusing the credential)…"
1174
+ : "Provisioning capability (no token → structure only)…";
931
1175
  }
932
1176
  try {
933
1177
  const res = (await hubPost("/capabilities/apply", { definition: def, workspaceId }, cfg));
934
1178
  const tools = res.created?.tools?.length ?? 0;
935
1179
  const skills = res.created?.skills?.length ?? 0;
936
- const vaultRef = res.created?.vault?.[0]?.vaultRef;
937
- const secretId = res.created?.vault?.[0]?.secretId;
1180
+ const vaultRef = res.created?.vault?.[0]?.vaultRef ?? reuse.vaultRef;
1181
+ const secretId = res.created?.vault?.[0]?.secretId ?? reuse.secretId;
938
1182
  spinner.succeed(`Capability provisioned: ${res.capabilityKey ?? "?"} (${vaultRef ? "vault + " : ""}${tools} tool, ${skills} skill)`);
939
1183
  return { vaultRef, secretId };
940
1184
  }
@@ -942,6 +1186,14 @@ async function applyBridgeCapability(bridgeDir, workspaceId, cfg, botToken) {
942
1186
  const msg = err.message;
943
1187
  spinner.fail("Capability apply failed");
944
1188
  log.error(` ${msg}`);
1189
+ if (existingVaultRef) {
1190
+ // Re-run: the pod already holds a working credential + tool wiring, so a
1191
+ // failed RE-apply is not fatal — but it is not a "skip" either. Say what
1192
+ // failed, say what was left alone, and carry it into the final summary.
1193
+ log.dim(" The capability was NOT re-applied — the pod's existing wiring is untouched.");
1194
+ log.dim(" Setup continues; nothing below depends on this apply succeeding.");
1195
+ return { ...reuse, error: msg };
1196
+ }
945
1197
  if (botToken) {
946
1198
  // The bot token MUST be vaulted for the bridge to run — continuing to write
947
1199
  // a .env and printing "Done" would leave a crash-looping bridge (redeem
@@ -954,7 +1206,7 @@ async function applyBridgeCapability(bridgeDir, workspaceId, cfg, botToken) {
954
1206
  }
955
1207
  process.exit(1);
956
1208
  }
957
- return {};
1209
+ return { ...reuse, error: msg };
958
1210
  }
959
1211
  }
960
1212
  /**
@@ -971,6 +1223,14 @@ async function grantRedeemAccess(secretId, cfg, agentUserId) {
971
1223
  // with no_grant. Fall back to the operator only if the agent id is unknown.
972
1224
  const principal = agentUserId || (await resolveUserId(cfg));
973
1225
  const res = (await hubPost(`/vault/secrets/${secretId}/grant`, { grantedTo: principal, scope: "permanent" }, cfg));
1226
+ if (res.status === "proposed") {
1227
+ // An agent key cannot grant vault access by itself: the pod filed it for
1228
+ // the secret owner's approval. Nothing is granted until it is approved.
1229
+ spinner.warn("Redeem access is waiting for your approval");
1230
+ if (res.reviewUrl)
1231
+ log.dim(` Approve it: ${res.reviewUrl}`);
1232
+ return false;
1233
+ }
974
1234
  spinner.succeed(`Redeem access granted${res.reused ? " (existing)" : ""} — token stays in the vault`);
975
1235
  return Boolean(res.grantId);
976
1236
  }
@@ -1041,7 +1301,7 @@ function upsertEnv(filePath, kv, remove = []) {
1041
1301
  for (const [k, v] of remaining)
1042
1302
  out.push(`${k}=${v}`);
1043
1303
  }
1044
- fs.writeFileSync(filePath, out.join("\n"), { mode: 0o600 });
1304
+ writeSecretFile(filePath, out.join("\n"), { mode: 0o600 });
1045
1305
  }
1046
1306
  /**
1047
1307
  * Data-aware "what you must do next INSIDE Synap" — the discoverability layer:
@@ -1079,8 +1339,14 @@ function printNextSteps(clientId, bridgeDir, state) {
1079
1339
  log.blank();
1080
1340
  log.heading("Done");
1081
1341
  log.blank();
1082
- if (state.vaultRef && state.granted) {
1083
- console.log(` ${chalk.green("✓")} Bot token in the pod vault, bridge granted redeem access — no env token ${chalk.dim(state.vaultRef)}`);
1342
+ if (state.capabilityError) {
1343
+ console.log(` ${chalk.yellow("⚠")} Capability was not re-applied: ${state.capabilityError}`);
1344
+ console.log(` Everything else below still ran; the pod's existing capability wiring was left untouched.`);
1345
+ }
1346
+ if (state.vaultRef && state.granted && state.reusedToken) {
1347
+ console.log(` ${chalk.green("✓")} Bot token reused from the pod vault (never re-entered), redeem access confirmed ${chalk.dim(state.vaultRef)}`);
1348
+ }
1349
+ else if (state.vaultRef && state.granted) {
1084
1350
  }
1085
1351
  else if (state.vaultRef && state.hasToken) {
1086
1352
  console.log(` ${chalk.yellow("⚠")} Token stored in the vault (${chalk.dim(state.vaultRef)}) but the redeem GRANT did not land.`);