@synap-core/workspace-templates 0.11.0 → 0.23.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 (249) hide show
  1. package/README.md +483 -0
  2. package/automations/bookmark-enrichment.automation.json +321 -0
  3. package/automations/github-pr-events.automation.json +505 -0
  4. package/automations/mail-feed.automation.json +86 -0
  5. package/automations/ship-a-change-checks-green.automation.json +106 -0
  6. package/automations/ship-a-change-checks-red.automation.json +106 -0
  7. package/automations/ship-a-change-deployed.automation.json +56 -0
  8. package/automations/ship-a-change-pr-merged.automation.json +110 -0
  9. package/automations/ship-a-change-pr-opened.automation.json +104 -0
  10. package/automations/url-bookmark-capture.automation.json +132 -0
  11. package/capabilities/agency-skills.capability.json +157 -0
  12. package/capabilities/apify.capability.json +265 -0
  13. package/capabilities/apollo.capability.json +120 -0
  14. package/capabilities/arch-backend.capability.json +97 -0
  15. package/capabilities/arch-client-intelligence.capability.json +401 -0
  16. package/capabilities/backblaze-b2.capability.json +205 -0
  17. package/capabilities/cal-com.capability.json +266 -0
  18. package/capabilities/canva.capability.json +274 -0
  19. package/capabilities/claude-code-routines.capability.json +172 -0
  20. package/capabilities/claude-managed-agents.capability.json +215 -0
  21. package/capabilities/client-comms-report.capability.json +246 -0
  22. package/capabilities/content-idea-to-production.capability.json +29 -0
  23. package/capabilities/datagouv.capability.json +315 -0
  24. package/capabilities/discord-bot.capability.json +157 -0
  25. package/capabilities/dropbox.capability.json +281 -0
  26. package/capabilities/exa.capability.json +109 -0
  27. package/capabilities/fal-ai.capability.json +83 -0
  28. package/capabilities/fal-media-lab.capability.json +158 -0
  29. package/capabilities/fireflies.capability.json +191 -0
  30. package/capabilities/freellmapi.capability.json +114 -0
  31. package/capabilities/generic-apikey.capability.json +92 -0
  32. package/capabilities/github-merge.capability.json +99 -0
  33. package/capabilities/github-read.capability.json +325 -0
  34. package/capabilities/governance-tighten-recommender.capability.json +81 -0
  35. package/capabilities/jev.capability.json +226 -0
  36. package/capabilities/lead-conversion-enrollment.capability.json +246 -0
  37. package/capabilities/mail-feed.capability.json +277 -0
  38. package/capabilities/mailgun.capability.json +52 -0
  39. package/capabilities/nango-google.capability.json +639 -0
  40. package/capabilities/notion.capability.json +417 -0
  41. package/capabilities/omniroute.capability.json +137 -0
  42. package/capabilities/posthog-analytics.capability.json +258 -0
  43. package/capabilities/postiz.capability.json +467 -0
  44. package/capabilities/proactive-digest.capability.json +190 -0
  45. package/capabilities/proton.capability.json +288 -0
  46. package/capabilities/replicate-rvm.capability.json +97 -0
  47. package/capabilities/research-methods.capability.json +57 -0
  48. package/capabilities/shopify.capability.json +395 -0
  49. package/capabilities/slack.capability.json +369 -0
  50. package/capabilities/stripe.capability.json +420 -0
  51. package/capabilities/telegram-bridge.capability.json +56 -0
  52. package/capabilities/treg.capability.json +102 -0
  53. package/capabilities/unipile-linkedin.capability.json +471 -0
  54. package/capabilities/vercel.capability.json +424 -0
  55. package/dist/define-CPS0n8BV.d.ts +1331 -0
  56. package/dist/index.d.ts +189 -900
  57. package/dist/index.js +24654 -21639
  58. package/dist/index.js.map +1 -1
  59. package/dist/wire.d.ts +222 -0
  60. package/dist/wire.js +77 -0
  61. package/dist/wire.js.map +1 -0
  62. package/package.json +41 -27
  63. package/packs/bookmark-views-pack.json +33 -0
  64. package/packs/crm-pack.json +76 -0
  65. package/packs/customer-profile.json +28 -0
  66. package/packs/deep-research.playbook.json +30 -0
  67. package/packs/event-views-pack.json +33 -0
  68. package/packs/habit-profile.json +27 -0
  69. package/packs/hr-pack.json +54 -0
  70. package/packs/invoice-profile.json +28 -0
  71. package/packs/knowledge-base-pack.json +56 -0
  72. package/packs/meeting-profile.json +29 -0
  73. package/packs/product-profile.json +30 -0
  74. package/packs/project-management-pack.json +49 -0
  75. package/packs/recipe-profile.json +29 -0
  76. package/packs/task-views-pack.json +39 -0
  77. package/retired.yaml +136 -0
  78. package/skills/app-factory-build-slice/SKILL.md +70 -0
  79. package/skills/app-factory-build-slice.catalog.json +17 -0
  80. package/skills/app-factory-design-canvas/SKILL.md +96 -0
  81. package/skills/app-factory-design-canvas.catalog.json +17 -0
  82. package/skills/app-factory-design-review/SKILL.md +99 -0
  83. package/skills/app-factory-design-review.catalog.json +18 -0
  84. package/skills/app-factory-research/SKILL.md +123 -0
  85. package/skills/app-factory-research.catalog.json +17 -0
  86. package/skills/bookmark-enrichment/SKILL.md +40 -0
  87. package/skills/carousel-format/carousel-format.skill.json +18 -0
  88. package/skills/collage-broll/collage-broll.skill.json +20 -0
  89. package/skills/content-taste/content-taste.skill.json +18 -0
  90. package/skills/creative-director/creative-director.skill.json +18 -0
  91. package/skills/design-critique/SKILL.md +135 -0
  92. package/skills/design-critique.catalog.json +12 -0
  93. package/skills/designing-with-real-components/SKILL.md +140 -0
  94. package/skills/designing-with-real-components.catalog.json +12 -0
  95. package/skills/edit-loop/edit-loop.skill.json +25 -0
  96. package/skills/facecam-edit/facecam-edit.skill.json +24 -0
  97. package/skills/mapping-the-server/SKILL.md +54 -0
  98. package/skills/math-explainer-manimgx/math-explainer-manimgx.skill.json +20 -0
  99. package/skills/motion-quality-gates/motion-quality-gates.skill.json +20 -0
  100. package/skills/open-design-edit/open-design-edit.skill.json +24 -0
  101. package/skills/reading-client-progress/SKILL.md +65 -0
  102. package/skills/routing-to-channels/SKILL.md +54 -0
  103. package/skills/stellar-scf-grants-advisor/ASSOCIATE-CHEATSHEET.md +133 -0
  104. package/skills/stellar-scf-grants-advisor/ASSOCIATE-ONBOARDING-GUIDE.md +761 -0
  105. package/skills/stellar-scf-grants-advisor/CLIENT-FAQ.md +181 -0
  106. package/skills/stellar-scf-grants-advisor/PARTNER-HANDOVER.md +130 -0
  107. package/skills/stellar-scf-grants-advisor/SKILL.md +102 -0
  108. package/skills/stellar-scf-grants-advisor/examples/README.md +43 -0
  109. package/skills/stellar-scf-grants-advisor/examples/chat-summary-templates.md +158 -0
  110. package/skills/stellar-scf-grants-advisor/examples/corpus-comparable-rows.md +136 -0
  111. package/skills/stellar-scf-grants-advisor/examples/q10-team-rewrites.md +109 -0
  112. package/skills/stellar-scf-grants-advisor/examples/q2-description-rewrites.md +88 -0
  113. package/skills/stellar-scf-grants-advisor/examples/q6-integration-rewrites.md +107 -0
  114. package/skills/stellar-scf-grants-advisor/examples/tranche-structures.md +176 -0
  115. package/skills/stellar-scf-grants-advisor/reference/01-methodology.md +96 -0
  116. package/skills/stellar-scf-grants-advisor/reference/02-scoring-framework.md +199 -0
  117. package/skills/stellar-scf-grants-advisor/reference/03-rejection-patterns.md +241 -0
  118. package/skills/stellar-scf-grants-advisor/reference/04-winner-patterns.md +120 -0
  119. package/skills/stellar-scf-grants-advisor/reference/05-comeback-patterns.md +92 -0
  120. package/skills/stellar-scf-grants-advisor/reference/06-budget-benchmarks.md +125 -0
  121. package/skills/stellar-scf-grants-advisor/reference/07-integration-list.md +147 -0
  122. package/skills/stellar-scf-grants-advisor/reference/08-abstract-fields.md +258 -0
  123. package/skills/stellar-scf-grants-advisor/reference/09-build-fields.md +259 -0
  124. package/skills/stellar-scf-grants-advisor/reference/10-corpus-winners.md +135 -0
  125. package/skills/stellar-scf-grants-advisor/reference/11-corpus-rejects.md +267 -0
  126. package/skills/stellar-scf-grants-advisor/reference/12-client-communication.md +131 -0
  127. package/skills/stellar-scf-grants-advisor/reference/13-excel-output-spec.md +199 -0
  128. package/skills/stellar-scf-grants-advisor/reference/14-worked-examples.md +160 -0
  129. package/skills/stellar-scf-grants-advisor/templates/abstract-review-builder.py +253 -0
  130. package/skills/stellar-scf-grants-advisor/templates/build-review-builder.py +213 -0
  131. package/skills/video-direction/video-direction.skill.json +20 -0
  132. package/skills/video-style-vocabulary/video-style-vocabulary.skill.json +20 -0
  133. package/skills/video-toolbox/video-toolbox.skill.json +20 -0
  134. package/src/__fixtures__/lifecycle-presets.flat-before.json +205 -0
  135. package/src/agency-os.yaml +122 -0
  136. package/src/agent-os-skill-slugs.test.ts +65 -0
  137. package/src/apify-social-reader.test.ts +317 -0
  138. package/src/base.template.test.ts +25 -19
  139. package/src/base.yaml +95 -39
  140. package/src/blockchain-ecosystem.yaml +1 -0
  141. package/src/brand-library.yaml +525 -14
  142. package/src/builder-dev-fold.template.test.ts +113 -0
  143. package/src/builder.yaml +798 -92
  144. package/src/business-model-catalog-description.test.ts +47 -0
  145. package/src/business-model-grp.template.test.ts +214 -0
  146. package/src/business-model.yaml +598 -0
  147. package/src/catalog-safety.test.ts +198 -0
  148. package/src/catalog.template-items.test.ts +75 -0
  149. package/src/catalog.ts +85 -2
  150. package/src/civic-france.yaml +102 -0
  151. package/src/concept-guard.test.ts +28 -8
  152. package/src/concept-guard.ts +2 -2
  153. package/src/content-creation.template.test.ts +52 -0
  154. package/src/content-creation.yaml +192 -0
  155. package/src/content-os.edit-loop.template.test.ts +210 -0
  156. package/src/content-os.publish-due-posts.template.test.ts +261 -0
  157. package/src/content-os.template.test.ts +322 -0
  158. package/src/content-os.video-takes.template.test.ts +338 -0
  159. package/src/content-os.work-a-take.template.test.ts +125 -0
  160. package/src/content-os.yaml +1830 -39
  161. package/src/content-studio-retired.template.test.ts +82 -0
  162. package/src/corpus-flow-validity.test.ts +4 -5
  163. package/src/crm-absorbs-outreach.template.test.ts +117 -0
  164. package/src/crm-build-plan.template.test.ts +128 -0
  165. package/src/crm-lists.template.test.ts +96 -0
  166. package/src/crm-messages-cadence.template.test.ts +164 -0
  167. package/src/crm-one-send-door.template.test.ts +65 -0
  168. package/src/crm.yaml +1456 -181
  169. package/src/decisions-wave.template.test.ts +186 -0
  170. package/src/define.ts +130 -17
  171. package/src/design-to-implementation.template.test.ts +94 -0
  172. package/src/dist-freshness.test.ts +97 -0
  173. package/src/domain-template-type.test.ts +75 -0
  174. package/src/ecosystem.yaml +27 -66
  175. package/src/enterprise-os.yaml +10 -134
  176. package/src/facecam-edit.audio-studio.test.ts +188 -0
  177. package/src/fal-generate-image.test.ts +200 -0
  178. package/src/fal-media-lab-pod-files.test.ts +180 -0
  179. package/src/finance.yaml +254 -1
  180. package/src/foundation.yaml +130 -28
  181. package/src/frame-host-modules.d.mts +6 -0
  182. package/src/frame-host-modules.mjs +58 -0
  183. package/src/generate.mjs +9 -3
  184. package/src/github-merge.test.ts +210 -0
  185. package/src/grants.yaml +57 -17
  186. package/src/hr.yaml +39 -6
  187. package/src/index.ts +36 -1
  188. package/src/intent.test.ts +161 -0
  189. package/src/internal-runbook.yaml +19 -1
  190. package/src/jev.test.ts +356 -0
  191. package/src/legal.yaml +33 -1
  192. package/src/life-os.yaml +65 -240
  193. package/src/lifecycle-coverage.test.ts +124 -0
  194. package/src/lifecycle-declarations.test.ts +252 -0
  195. package/src/lifecycle-presets.test.ts +468 -0
  196. package/src/lifecycle-presets.ts +494 -0
  197. package/src/lifecycle-stuck.test.ts +46 -0
  198. package/src/lifecycle-waits.parity.test.ts +319 -0
  199. package/src/marketing.yaml +108 -4
  200. package/src/meetings-knowledge.yaml +18 -0
  201. package/src/networking.yaml +23 -2
  202. package/src/operations.yaml +304 -4
  203. package/src/pipeline-views.template.test.ts +88 -0
  204. package/src/playbook-goal-placeholders.test.ts +57 -0
  205. package/src/postiz-contract.test.ts +134 -0
  206. package/src/presentation-declarations.test.ts +174 -0
  207. package/src/process-contract.validate.test.ts +352 -0
  208. package/src/project-management.yaml +39 -740
  209. package/src/publish-body.d.mts +29 -0
  210. package/src/publish-body.mjs +74 -0
  211. package/src/publish-body.test.ts +74 -0
  212. package/src/publish-official-lib.test.ts +176 -0
  213. package/src/replicate-rvm.test.ts +504 -0
  214. package/src/research-base.yaml +119 -20
  215. package/src/research-decision.yaml +60 -3
  216. package/src/reserved-profile-slugs.test.ts +83 -0
  217. package/src/retired-goal-kinds.test.ts +58 -0
  218. package/src/role-reference-and-step-domain.test.ts +192 -0
  219. package/src/seed-obligation.test.ts +176 -0
  220. package/src/send-message-maps.test.ts +227 -0
  221. package/src/shared-role-ssot.test.ts +42 -1
  222. package/src/ship-a-change.template.test.ts +177 -0
  223. package/src/skills.test.ts +170 -0
  224. package/src/social.yaml +85 -27
  225. package/src/space-packages.freshness.test.ts +97 -0
  226. package/src/space-packages.generated.ts +40 -0
  227. package/src/space-packages.test.ts +125 -0
  228. package/src/space-packages.ts +73 -0
  229. package/src/start.test.ts +188 -0
  230. package/src/task-intents.test.ts +293 -0
  231. package/src/templates.ts +21639 -21522
  232. package/src/types.ts +443 -60
  233. package/src/unipile-publish-post.test.ts +162 -0
  234. package/src/validate-flow.drift.test.ts +2 -3
  235. package/src/validate-flow.ts +32 -2
  236. package/src/validate-onboarding.test.ts +59 -0
  237. package/src/validate.test.ts +156 -3
  238. package/src/validate.ts +1384 -7
  239. package/src/wire-core.ts +231 -0
  240. package/src/wire.test.ts +159 -0
  241. package/src/wire.ts +107 -0
  242. package/src/__fixtures__/report-automation-flow.json +0 -393
  243. package/src/agent-fleet.yaml +0 -1645
  244. package/src/business-developer.template.test.ts +0 -351
  245. package/src/business-developer.yaml +0 -860
  246. package/src/content-studio.yaml +0 -361
  247. package/src/dev-dashboard.yaml +0 -990
  248. package/src/outreach-comms.yaml +0 -486
  249. package/src/the-arch.yaml +0 -201
@@ -0,0 +1,274 @@
1
+ {
2
+ "apiVersion": "synap/v1",
3
+ "kind": "capability",
4
+ "meta": {
5
+ "slug": "canva",
6
+ "name": "Canva — Design & Brand",
7
+ "description": "Canva design operations through one Nango OAuth connection (Connect REST API): list/get designs, brand templates and folders, and export designs. List verbs default their limit. Connect your Canva account to use it.",
8
+ "tags": [
9
+ "capability"
10
+ ],
11
+ "isPublic": true,
12
+ "syncByDefault": true
13
+ },
14
+ "spec": {
15
+ "key": "canva",
16
+ "name": "Canva — Design & Brand",
17
+ "description": "Canva design operations through ONE Nango OAuth connection (Connect REST API). List verbs default their limit (10–100). Read your designs, folders, and brand templates; inspect what a brand template accepts for autofill; and export designs. PRECONDITION: the 'canva' integration must be declared + configured on the pod's Nango (Connect API scopes: design:read, design:write, design:meta:read, asset:read, folder:read, profile:read) and the acting user must connect their Canva account via Connectors; the dispatcher resolves the most-recent canva connection automatically. Paths are RELATIVE to the Nango canva integration's base URL (api.canva.com) — no baseUrlOverride, so no provider-proxy-host allowlist entry is needed. Reads (list/get) return data inline and auto-run. Writes (export) are side-effecting and route through the capability-execution gate. NOTE: response-field shaping is best-effort and confirmed against the live API at first run.",
18
+ "params": [],
19
+ "vault": [],
20
+ "tools": [
21
+ {
22
+ "name": "canva",
23
+ "kind": "provider",
24
+ "description": "Canva Connect REST access through Nango. credentialRef nango://canva resolves the user's Canva connection (Connection-Id + Provider-Config-Key) and proxies to the integration's base URL (api.canva.com).",
25
+ "credentialRef": "nango://canva",
26
+ "executor": "is-agent",
27
+ "config": {
28
+ "providerConfigKey": "canva"
29
+ }
30
+ }
31
+ ],
32
+ "skills": [
33
+ {
34
+ "name": "canva_list_designs",
35
+ "intent": "list_records",
36
+ "kind": "declarative",
37
+ "scope": "pod",
38
+ "description": "Lists the user's Canva designs (most relevant first). Args: { limit?: number (default 10, max 50) }.",
39
+ "requires": [
40
+ "canva"
41
+ ],
42
+ "executionMode": "sync",
43
+ "timeoutSeconds": 60,
44
+ "parameters": {
45
+ "limit": "number?"
46
+ },
47
+ "providerSpec": {
48
+ "tool": "canva",
49
+ "method": "GET",
50
+ "pathTemplate": "/rest/v1/designs",
51
+ "query": {
52
+ "limit": "{{limit}}"
53
+ },
54
+ "paramMapping": {
55
+ "limit": {
56
+ "default": 10,
57
+ "clampMin": 1,
58
+ "clampMax": 50
59
+ }
60
+ },
61
+ "responseShape": {
62
+ "collectionPath": "items",
63
+ "collectionAs": "designs",
64
+ "item": {
65
+ "id": "id",
66
+ "title": "title",
67
+ "thumbnail": "thumbnail.url",
68
+ "updatedAt": "updated_at"
69
+ },
70
+ "scalar": {
71
+ "count": "@count"
72
+ }
73
+ }
74
+ }
75
+ },
76
+ {
77
+ "name": "canva_get_design",
78
+ "intent": "fetch_record",
79
+ "kind": "declarative",
80
+ "scope": "pod",
81
+ "description": "Gets one Canva design's metadata by id. Args: { designId: string }.",
82
+ "requires": [
83
+ "canva"
84
+ ],
85
+ "executionMode": "sync",
86
+ "timeoutSeconds": 60,
87
+ "parameters": {
88
+ "designId": "string"
89
+ },
90
+ "providerSpec": {
91
+ "tool": "canva",
92
+ "method": "GET",
93
+ "pathTemplate": "/rest/v1/designs/{{designId}}",
94
+ "paramMapping": {
95
+ "designId": {
96
+ "required": true,
97
+ "encode": "uri"
98
+ }
99
+ },
100
+ "responseShape": {
101
+ "scalar": {
102
+ "id": "id",
103
+ "title": "title",
104
+ "thumbnail": "thumbnail.url",
105
+ "url": "urls.edit"
106
+ }
107
+ }
108
+ }
109
+ },
110
+ {
111
+ "name": "canva_list_brand_templates",
112
+ "intent": "list_records",
113
+ "kind": "declarative",
114
+ "scope": "pod",
115
+ "description": "Lists the user's Canva brand templates — this is the branding the AI can see. Args: { limit?: number (default 20, max 100) }.",
116
+ "requires": [
117
+ "canva"
118
+ ],
119
+ "executionMode": "sync",
120
+ "timeoutSeconds": 60,
121
+ "parameters": {
122
+ "limit": "number?"
123
+ },
124
+ "providerSpec": {
125
+ "tool": "canva",
126
+ "method": "GET",
127
+ "pathTemplate": "/rest/v1/brand-templates",
128
+ "query": {
129
+ "limit": "{{limit}}"
130
+ },
131
+ "paramMapping": {
132
+ "limit": {
133
+ "default": 20,
134
+ "clampMin": 1,
135
+ "clampMax": 100
136
+ }
137
+ },
138
+ "responseShape": {
139
+ "collectionPath": "items",
140
+ "collectionAs": "brand_templates",
141
+ "item": {
142
+ "id": "id",
143
+ "title": "title",
144
+ "thumbnail": "thumbnail.url"
145
+ },
146
+ "scalar": {
147
+ "count": "@count"
148
+ }
149
+ }
150
+ }
151
+ },
152
+ {
153
+ "name": "canva_get_brand_template_dataset",
154
+ "intent": "fetch_record",
155
+ "kind": "declarative",
156
+ "scope": "pod",
157
+ "description": "Gets the dataset schema for a brand template — what fields it accepts for autofill (text/image/data slots). Args: { brandTemplateId: string }.",
158
+ "requires": [
159
+ "canva"
160
+ ],
161
+ "executionMode": "sync",
162
+ "timeoutSeconds": 60,
163
+ "parameters": {
164
+ "brandTemplateId": "string"
165
+ },
166
+ "providerSpec": {
167
+ "tool": "canva",
168
+ "method": "GET",
169
+ "pathTemplate": "/rest/v1/brand-templates/{{brandTemplateId}}/dataset",
170
+ "paramMapping": {
171
+ "brandTemplateId": {
172
+ "required": true,
173
+ "encode": "uri"
174
+ }
175
+ },
176
+ "responseShape": {
177
+ "scalar": {
178
+ "brandTemplateId": "{{brandTemplateId}}",
179
+ "dataset": "dataset"
180
+ }
181
+ }
182
+ }
183
+ },
184
+ {
185
+ "name": "canva_list_folders",
186
+ "intent": "list_records",
187
+ "kind": "declarative",
188
+ "scope": "pod",
189
+ "description": "Lists the user's Canva folders. Args: { limit?: number (default 20, max 100) }.",
190
+ "requires": [
191
+ "canva"
192
+ ],
193
+ "executionMode": "sync",
194
+ "timeoutSeconds": 60,
195
+ "parameters": {
196
+ "limit": "number?"
197
+ },
198
+ "providerSpec": {
199
+ "tool": "canva",
200
+ "method": "GET",
201
+ "pathTemplate": "/rest/v1/folders",
202
+ "query": {
203
+ "limit": "{{limit}}"
204
+ },
205
+ "paramMapping": {
206
+ "limit": {
207
+ "default": 20,
208
+ "clampMin": 1,
209
+ "clampMax": 100
210
+ }
211
+ },
212
+ "responseShape": {
213
+ "collectionPath": "items",
214
+ "collectionAs": "folders",
215
+ "item": {
216
+ "id": "id",
217
+ "name": "name"
218
+ },
219
+ "scalar": {
220
+ "count": "@count"
221
+ }
222
+ }
223
+ }
224
+ },
225
+ {
226
+ "name": "canva_export_design",
227
+ "intent": "manage_file",
228
+ "kind": "declarative",
229
+ "scope": "pod",
230
+ "description": "Exports a Canva design to a file (PNG, JPG, PDF, PPTX, MP4…). Side-effecting write — routes through the capability-execution gate. Args: { designId: string, format?: string (default 'png') }.",
231
+ "requires": [
232
+ "canva"
233
+ ],
234
+ "executionMode": "async",
235
+ "timeoutSeconds": 120,
236
+ "parameters": {
237
+ "designId": "string",
238
+ "format": "string?"
239
+ },
240
+ "providerSpec": {
241
+ "tool": "canva",
242
+ "method": "POST",
243
+ "pathTemplate": "/rest/v1/exports",
244
+ "body": {
245
+ "design_id": "{{designId}}",
246
+ "format": {
247
+ "type": "{{format}}"
248
+ }
249
+ },
250
+ "paramMapping": {
251
+ "designId": {
252
+ "required": true
253
+ },
254
+ "format": {
255
+ "default": "png"
256
+ }
257
+ },
258
+ "responseShape": {
259
+ "scalar": {
260
+ "jobId": "job.id",
261
+ "status": "job.status"
262
+ }
263
+ }
264
+ }
265
+ }
266
+ ],
267
+ "provides": [
268
+ "fetch_record",
269
+ "list_records",
270
+ "manage_file"
271
+ ]
272
+ },
273
+ "dependencies": []
274
+ }
@@ -0,0 +1,172 @@
1
+ {
2
+ "apiVersion": "synap/v1",
3
+ "kind": "capability",
4
+ "meta": {
5
+ "slug": "claude-code-routines",
6
+ "name": "Claude Code Routines",
7
+ "description": "Hand a Synap session's work to a Claude Code routine you set up on claude.ai/code. It runs in the cloud on your Claude plan, works in your connected GitHub repos, and reports back in the session room. Good for dev tasks and research.",
8
+ "tags": [
9
+ "capability",
10
+ "agents",
11
+ "anthropic",
12
+ "claude-code",
13
+ "opt-in"
14
+ ],
15
+ "isPublic": true,
16
+ "syncByDefault": false
17
+ },
18
+ "spec": {
19
+ "key": "claude-code-routines",
20
+ "name": "Claude Code Routines",
21
+ "description": "Hands Synap work to a Claude Code routine you created on claude.ai/code, so it runs as a cloud Claude Code session on YOUR Claude plan (Pro, Max, Team or Enterprise) — Anthropic's documented routine fire API, never a subscription login token. Installs ONE external-agent tool (claude_code_routines, kind external, executor external-agent) whose config.agentBinding names two verbs, both intent delegate_agent_task: <key>_dispatch (start: POST /v1/claude_code/routines/{trig_...}/fire with the fenced task as text; returns the claude.ai/code session URL) and <key>_send (the fire API cannot message a running session, so an answer starts a NEW run that continues from the Synap room). No status, no cancel: the fire token reads nothing, so the agent reports through the Synap MCP connector attached to the routine and closes the run with complete_session. ONE-TIME SETUP: (1) on claude.ai/code/routines create a routine: attach the repositories, attach the Synap MCP connector, and give it a short prompt ('You are a Synap worker. Do the task given in this run's context and follow its report-back rules.'). (2) Add trigger -> API -> Generate token; copy the fire URL and the token (shown once). (3) Install this capability with both. (4) Bind: agentUsers.setBinding({agentUserId, toolId: the claude_code_routines tool}) (a person only) and grant that agent auto-run on dispatch/send, otherwise every hand-off is a proposal. LIMITS (Anthropic's): 30 runs per hour per routine, 100 per hour per account, shared with Run now; every call is a new session (no idempotency); the API is experimental. Best for dev tasks and research, not for per-capture work.",
22
+ "params": [
23
+ {
24
+ "name": "routineFireUrl",
25
+ "label": "Routine fire URL",
26
+ "type": "text",
27
+ "required": true,
28
+ "description": "The URL shown when you add an API trigger to the routine: https://api.anthropic.com/v1/claude_code/routines/trig_.../fire. Not a secret."
29
+ },
30
+ {
31
+ "name": "routineToken",
32
+ "label": "Routine API token",
33
+ "type": "password",
34
+ "required": true,
35
+ "description": "The sk-ant-oat01-... token generated for that trigger (shown once). It can only start this routine. Stored server-encrypted in the vault; never persisted in the template."
36
+ }
37
+ ],
38
+ "vault": [
39
+ {
40
+ "ref": "routineTokenSecret",
41
+ "name": "{{name}} routine token",
42
+ "value": "{{routineToken}}",
43
+ "type": "api_key",
44
+ "service": "anthropic",
45
+ "description": "Per-routine bearer token for the claude_code_routines binding tool (sent as Authorization: Bearer). Starts one routine; reads nothing."
46
+ }
47
+ ],
48
+ "tools": [
49
+ {
50
+ "name": "claude_code_routines",
51
+ "kind": "external",
52
+ "executor": "external-agent",
53
+ "description": "The dispatch binding to a Claude Code routine on claude.ai/code. Synap never runs the agent: its verbs only fire the routine over https://api.anthropic.com. The vaulted routine token is injected as Authorization: Bearer; the credentialRef is remapped to vault://<id> at apply time.",
54
+ "credentialRef": "routineTokenSecret",
55
+ "config": {
56
+ "baseUrl": "https://api.anthropic.com",
57
+ "auth": {
58
+ "in": "header",
59
+ "name": "Authorization",
60
+ "prefix": "Bearer "
61
+ },
62
+ "agentBinding": {
63
+ "protocol": "vendor:anthropic-claude-code-routines",
64
+ "provider": "claude-code-routines",
65
+ "supports": {
66
+ "push": false,
67
+ "inputRequired": true,
68
+ "cancel": false
69
+ },
70
+ "verbs": {
71
+ "start": "{{key}}_dispatch",
72
+ "send": "{{key}}_send"
73
+ }
74
+ }
75
+ }
76
+ }
77
+ ],
78
+ "skills": [
79
+ {
80
+ "name": "{{key}}_dispatch",
81
+ "intent": "delegate_agent_task",
82
+ "kind": "code",
83
+ "scope": "pod",
84
+ "description": "Starts a run of the Claude Code routine for a Synap task (the binding's start verb): fires the routine with the fenced task, the session and room ids and the report-back rules as its run context. Returns { externalId (session_...), url (claude.ai/code) }.",
85
+ "requires": [
86
+ "claude_code_routines"
87
+ ],
88
+ "executionMode": "sync",
89
+ "timeoutSeconds": 60,
90
+ "parameters": {
91
+ "task": "object",
92
+ "sessionId": "string",
93
+ "pod": "object",
94
+ "agent": "object",
95
+ "repos": "array?",
96
+ "branch": "string?",
97
+ "workBranch": "string?",
98
+ "runId": "string?",
99
+ "channelId": "string?"
100
+ },
101
+ "code": "// Claude Code Routines binding verb. Statement body: the executor wraps it as\n// (async function(args, context){ <code> }). The ONE call is Anthropic's\n// documented routine fire endpoint (experimental):\n// POST /v1/claude_code/routines/{trig_...}/fire body {text} → {claude_code_session_id, claude_code_session_url}\n// callProvider('claude_code_routines', ...) resolves the binding tool by NAME,\n// injects the vaulted per-routine token as `Authorization: Bearer` (tool\n// config.auth) and pins the host to https://api.anthropic.com (tool config.baseUrl).\n// The token can ONLY start this one routine: it reads nothing. The session runs on\n// claude.ai/code and bills the owner's Claude Code subscription.\nconst CFG = { fireUrl: '{{routineFireUrl}}' };\nconst TEXT_MAX = 65536;\nfunction routinePath() {\n const m = /^https:\\/\\/api\\.anthropic\\.com(\\/v1\\/claude_code\\/routines\\/(trig_[A-Za-z0-9_-]+)\\/fire)\\/?$/.exec(String(CFG.fireUrl || '').trim());\n if (!m) {\n throw new Error(\n 'claude-code-routines: routineFireUrl is not a routine fire URL ' +\n '(https://api.anthropic.com/v1/claude_code/routines/trig_.../fire). Copy it from the routine\\'s API trigger and re-apply the capability.'\n );\n }\n return m[1];\n}\nasync function fire(text) {\n if (text.length > TEXT_MAX) {\n throw new Error('claude-code-routines: the kickoff is ' + text.length + ' characters; a routine accepts at most ' + TEXT_MAX + '. Shorten the task.');\n }\n // No idempotency key exists on this endpoint: every successful call starts a\n // NEW session, so this verb is never retried here.\n const res = await callProvider('claude_code_routines', 'POST', routinePath(), { text }, {\n headers: { 'anthropic-version': '2023-06-01' },\n });\n if (res && res.proposed) {\n throw new Error(\n 'claude-code-routines: starting the routine was queued for review (proposal ' + res.proposalId + ') instead of running. ' +\n 'Approve that proposal, or grant this agent auto-run on the dispatch/send verbs, then run again.'\n );\n }\n const body = res && res.body !== undefined ? res.body : res;\n if (!body || typeof body.claude_code_session_id !== 'string') {\n throw new Error('claude-code-routines: the routine fire returned no session id');\n }\n return { externalId: body.claude_code_session_id, url: typeof body.claude_code_session_url === 'string' ? body.claude_code_session_url : null };\n}\n// A person's words / names sit OUTSIDE the fenced task: one line each, capped,\n// so they cannot open a new instruction line or forge a fence marker.\nfunction oneLine(v, max) {\n return String(v == null ? '' : v).replace(/[\\r\\n\\u2028\\u2029]+/g, ' ').replace(/-{3,}/g, '-').trim().slice(0, max);\n}\nfunction reportBack(args, mcpUrl) {\n const session = oneLine(args.sessionId, 80);\n return [\n 'Report back through the Synap MCP connector attached to this routine (' + (mcpUrl || 'the Synap pod') + '):',\n args.channelId\n ? '- post progress, questions and results in the Synap session room with post_message, channelId ' + oneLine(args.channelId, 80) + ';'\n : '- post progress, questions and results in Synap session ' + session + ';',\n '- pass sessionId ' + session + ' on every Synap write (create, update, capture, proposal), so the work is filed under this session;',\n '- if you need a person (a question, a choice), ask it there with post_message kind \"question\", then STOP. The session waits on them; their answer starts a NEW run of this routine that continues the work;',\n '- when the task is complete, post the result there (PR link, branch, findings), then call complete_session for session ' + session + ' with a one-line summary.',\n 'Synap reviews every write you make through it. Never push to a repository\\'s default branch.',\n ];\n}\n\n// ── start: fire the routine with the task as its run context ────────────────\nconst task = args.task && typeof args.task === 'object' ? args.task : null;\nif (!task || typeof task.goal !== 'string' || !task.goal.trim()) {\n throw new Error('claude-code-routines: task.goal is required');\n}\nconst pod = args.pod && typeof args.pod === 'object' ? args.pod : {};\nconst mcpUrl = typeof pod.mcpUrl === 'string' ? pod.mcpUrl.trim() : '';\n\nfunction repoOf(raw) {\n const s = String(raw || '').trim().replace(/\\.git$/, '').replace(/\\/+$/, '');\n const m = /^https:\\/\\/github\\.com\\/([\\w.-]+)\\/([\\w.-]+)$/.exec(s) || /^([\\w.-]+)\\/([\\w.-]+)$/.exec(s);\n if (!m) throw new Error('claude-code-routines: \"' + s + '\" is not a GitHub repository (owner/repo or https://github.com/owner/repo)');\n return m[1] + '/' + m[2];\n}\nfunction branchName(v) {\n if (typeof v !== 'string' || !v.trim()) return null;\n const b = v.trim();\n if (!/^[\\w./-]{1,255}$/.test(b)) throw new Error('claude-code-routines: \"' + b + '\" is not a branch name');\n return b;\n}\nconst repos = (Array.isArray(args.repos) ? args.repos : []).map(repoOf);\nconst branch = branchName(args.branch);\nconst workBranch = branch ? null : branchName(args.workBranch);\n\nconst lines = [\n 'Synap handed you this task. Synap session ' + oneLine(args.sessionId, 80) + (args.runId ? ', run ' + oneLine(args.runId, 80) : '') + '.',\n '',\n].concat(reportBack(args, mcpUrl), ['']);\nif (task.currentStage) lines.push('Current stage: ' + oneLine(task.currentStage, 120));\nif (task.subject && task.subject.id) {\n lines.push('Subject: ' + oneLine(task.subject.name || task.subject.id, 200) + (task.subject.profile ? ' (' + oneLine(task.subject.profile, 80) + ')' : '') + ', Synap id ' + oneLine(task.subject.id, 80));\n}\nif (repos.length) {\n // The routine's own repositories are what is cloned. A run naming others can\n // only be worked on if the routine has them; say so instead of pretending.\n lines.push('Repositories for this task: ' + repos.join(', ') + (branch ? ' on branch ' + branch : '') + '. If one is not attached to this routine, say so in the room and stop.');\n}\nif (workBranch) {\n lines.push('Work branch: create ' + workBranch + ' from the default branch, push your work to it, and open the pull request from it.');\n}\nlines.push('', 'The task:', task.goal);\nif (typeof task.feedback === 'string' && task.feedback.trim()) lines.push('', 'Reviewer feedback on an earlier attempt:', task.feedback);\n\nconst started = await fire(lines.join('\\n'));\nreturn { externalId: started.externalId, url: started.url, repos: repos };\n"
102
+ },
103
+ {
104
+ "name": "{{key}}_send",
105
+ "intent": "delegate_agent_task",
106
+ "kind": "code",
107
+ "scope": "pod",
108
+ "description": "Passes a person's answer or decision on (the binding's send verb). A routine session cannot be messaged, so this starts a NEW run of the routine that continues from the Synap room. Returns { externalId (the new session_...), url, continuedFrom }.",
109
+ "requires": [
110
+ "claude_code_routines"
111
+ ],
112
+ "executionMode": "sync",
113
+ "timeoutSeconds": 60,
114
+ "parameters": {
115
+ "sessionId": "string",
116
+ "externalId": "string",
117
+ "message": "object",
118
+ "channelId": "string?"
119
+ },
120
+ "code": "// Claude Code Routines binding verb. Statement body: the executor wraps it as\n// (async function(args, context){ <code> }). The ONE call is Anthropic's\n// documented routine fire endpoint (experimental):\n// POST /v1/claude_code/routines/{trig_...}/fire body {text} → {claude_code_session_id, claude_code_session_url}\n// callProvider('claude_code_routines', ...) resolves the binding tool by NAME,\n// injects the vaulted per-routine token as `Authorization: Bearer` (tool\n// config.auth) and pins the host to https://api.anthropic.com (tool config.baseUrl).\n// The token can ONLY start this one routine: it reads nothing. The session runs on\n// claude.ai/code and bills the owner's Claude Code subscription.\nconst CFG = { fireUrl: '{{routineFireUrl}}' };\nconst TEXT_MAX = 65536;\nfunction routinePath() {\n const m = /^https:\\/\\/api\\.anthropic\\.com(\\/v1\\/claude_code\\/routines\\/(trig_[A-Za-z0-9_-]+)\\/fire)\\/?$/.exec(String(CFG.fireUrl || '').trim());\n if (!m) {\n throw new Error(\n 'claude-code-routines: routineFireUrl is not a routine fire URL ' +\n '(https://api.anthropic.com/v1/claude_code/routines/trig_.../fire). Copy it from the routine\\'s API trigger and re-apply the capability.'\n );\n }\n return m[1];\n}\nasync function fire(text) {\n if (text.length > TEXT_MAX) {\n throw new Error('claude-code-routines: the kickoff is ' + text.length + ' characters; a routine accepts at most ' + TEXT_MAX + '. Shorten the task.');\n }\n // No idempotency key exists on this endpoint: every successful call starts a\n // NEW session, so this verb is never retried here.\n const res = await callProvider('claude_code_routines', 'POST', routinePath(), { text }, {\n headers: { 'anthropic-version': '2023-06-01' },\n });\n if (res && res.proposed) {\n throw new Error(\n 'claude-code-routines: starting the routine was queued for review (proposal ' + res.proposalId + ') instead of running. ' +\n 'Approve that proposal, or grant this agent auto-run on the dispatch/send verbs, then run again.'\n );\n }\n const body = res && res.body !== undefined ? res.body : res;\n if (!body || typeof body.claude_code_session_id !== 'string') {\n throw new Error('claude-code-routines: the routine fire returned no session id');\n }\n return { externalId: body.claude_code_session_id, url: typeof body.claude_code_session_url === 'string' ? body.claude_code_session_url : null };\n}\n// A person's words / names sit OUTSIDE the fenced task: one line each, capped,\n// so they cannot open a new instruction line or forge a fence marker.\nfunction oneLine(v, max) {\n return String(v == null ? '' : v).replace(/[\\r\\n\\u2028\\u2029]+/g, ' ').replace(/-{3,}/g, '-').trim().slice(0, max);\n}\nfunction reportBack(args, mcpUrl) {\n const session = oneLine(args.sessionId, 80);\n return [\n 'Report back through the Synap MCP connector attached to this routine (' + (mcpUrl || 'the Synap pod') + '):',\n args.channelId\n ? '- post progress, questions and results in the Synap session room with post_message, channelId ' + oneLine(args.channelId, 80) + ';'\n : '- post progress, questions and results in Synap session ' + session + ';',\n '- pass sessionId ' + session + ' on every Synap write (create, update, capture, proposal), so the work is filed under this session;',\n '- if you need a person (a question, a choice), ask it there with post_message kind \"question\", then STOP. The session waits on them; their answer starts a NEW run of this routine that continues the work;',\n '- when the task is complete, post the result there (PR link, branch, findings), then call complete_session for session ' + session + ' with a one-line summary.',\n 'Synap reviews every write you make through it. Never push to a repository\\'s default branch.',\n ];\n}\n\n// ── send: a routine session cannot be messaged — continue in a NEW run ──────\n// The fire endpoint has no follow-up, read or cancel. So a person's answer\n// starts a new run of the same routine, which picks the thread up from the\n// Synap session room (through the Synap MCP connector) and from the previous\n// Claude Code session named below.\nconst message = args.message && typeof args.message === 'object' ? args.message : null;\nif (!message || (message.kind !== 'answer' && message.kind !== 'decision')) {\n throw new Error('claude-code-routines: message.kind must be \"answer\" or \"decision\"');\n}\nif (message.confirmationId != null) {\n throw new Error('claude-code-routines: a routine session has no pending tool approval to settle; it asks in the Synap room instead');\n}\nconst fenced = typeof message.text === 'string' ? message.text : '';\nconst previous = oneLine(args.externalId, 120);\nconst head = message.kind === 'decision'\n ? 'A decision from Synap' + (message.proposalId ? ' on proposal ' + oneLine(message.proposalId, 200) : '') + ': ' + oneLine(message.decision || 'noted', 50) + '.'\n : 'A person answered in the Synap session' + (message.slotKey ? ' (about: ' + oneLine(message.slotKey, 200) + ')' : '') + '.';\nconst lines = [\n 'You are CONTINUING Synap session ' + oneLine(args.sessionId, 80) + '. The previous Claude Code session was ' + (previous || 'not recorded') + '.',\n 'Read the Synap session room first (get_session, then the room thread) to pick up where it stopped.',\n '',\n].concat(reportBack(args, ''), ['', head, fenced]);\nconst started = await fire(lines.join('\\n'));\nreturn { externalId: started.externalId, url: started.url, continuedFrom: previous || null, sent: ['routine_fire'] };\n"
121
+ }
122
+ ],
123
+ "provides": [
124
+ "delegate_agent_task"
125
+ ],
126
+ "playbooks": [
127
+ {
128
+ "name": "Hand off to Claude Code",
129
+ "description": "Give a dev task or a research question to your Claude Code routine. It runs in the cloud on your Claude plan, works in the routine's GitHub repos, reports progress and questions in this session's room, and closes the session with its result (PR, branch or findings).",
130
+ "goalTemplate": "{task}",
131
+ "params": [
132
+ {
133
+ "name": "task",
134
+ "label": "What should Claude Code do?",
135
+ "type": "text",
136
+ "required": true,
137
+ "description": "The task in your own words: a bug to fix, a feature to build, a question to research. Be specific about what done looks like."
138
+ },
139
+ {
140
+ "name": "repo",
141
+ "label": "Repository (owner/repo)",
142
+ "type": "text",
143
+ "required": false,
144
+ "description": "Optional. Must be one of the repositories attached to your routine. Leave empty for research."
145
+ }
146
+ ],
147
+ "executor": "external-agent",
148
+ "status": "active",
149
+ "stages": [
150
+ {
151
+ "key": "working",
152
+ "name": "Working",
153
+ "category": "started",
154
+ "goal": "Claude Code is working; progress and questions appear in the room."
155
+ },
156
+ {
157
+ "key": "review",
158
+ "name": "Review",
159
+ "category": "started",
160
+ "goal": "Review the result Claude Code posted (PR, branch or findings)."
161
+ },
162
+ {
163
+ "key": "done",
164
+ "name": "Done",
165
+ "category": "completed"
166
+ }
167
+ ]
168
+ }
169
+ ]
170
+ },
171
+ "dependencies": []
172
+ }