codeply-cli 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (387) hide show
  1. package/LICENSE +21 -0
  2. package/THIRD_PARTY_NOTICES.md +65 -63
  3. package/bin/codeply.js +1 -1
  4. package/lib/agent.mjs +133 -24
  5. package/lib/ai.js +268 -9
  6. package/lib/ai.mjs +6 -6
  7. package/lib/apply-patch.js +217 -217
  8. package/lib/arity-table.json +138 -138
  9. package/lib/arity.js +70 -70
  10. package/lib/auth.mjs +6 -6
  11. package/lib/bots.js +82 -17
  12. package/lib/chatgpt.js +282 -0
  13. package/lib/cloud-browser.mjs +143 -143
  14. package/lib/cloud.mjs +1186 -1144
  15. package/lib/commands.js +103 -103
  16. package/lib/config.js +83 -3
  17. package/lib/design-library/assemble-artifact.mjs +21 -21
  18. package/lib/design-library/cleanup.mjs +45 -45
  19. package/lib/design-library/index.json +22379 -22379
  20. package/lib/design-library/make-table-data.mjs +39 -39
  21. package/lib/diagnostics.js +192 -192
  22. package/lib/edit-engine.js +364 -364
  23. package/lib/github-agent.mjs +311 -311
  24. package/lib/history.js +75 -75
  25. package/lib/mail-watch.js +504 -0
  26. package/lib/mcp.js +258 -258
  27. package/lib/model-sync.js +114 -0
  28. package/lib/native-tools.mjs +16 -1
  29. package/lib/oauth-connectors.js +146 -24
  30. package/lib/permissions.js +109 -109
  31. package/lib/plugins.js +353 -353
  32. package/lib/publish.js +916 -0
  33. package/lib/reach.js +67 -0
  34. package/lib/server.mjs +457 -457
  35. package/lib/session-db.js +220 -220
  36. package/lib/share.js +168 -168
  37. package/lib/skills.js +2 -0
  38. package/lib/snapshot.js +146 -146
  39. package/lib/telemetry.js +102 -0
  40. package/lib/terminal-hints.js +154 -154
  41. package/lib/tools.mjs +316 -14
  42. package/lib/typecheck-worker.js +147 -147
  43. package/lib/web-tools.js +209 -158
  44. package/package.json +49 -49
  45. package/skills/SOURCE.md +58 -58
  46. package/skills/accessibility/SKILL.md +147 -147
  47. package/skills/agent-architecture-audit/SKILL.md +257 -257
  48. package/skills/agent-eval/SKILL.md +147 -147
  49. package/skills/agent-harness-construction/SKILL.md +74 -74
  50. package/skills/agent-introspection-debugging/SKILL.md +154 -154
  51. package/skills/agent-payment-x402/SKILL.md +225 -225
  52. package/skills/agent-self-evaluation/SKILL.md +182 -182
  53. package/skills/agent-self-evaluation/references/evaluation-criteria.md +71 -71
  54. package/skills/agent-self-evaluation/references/hook-integration.md +64 -64
  55. package/skills/agent-sort/SKILL.md +216 -216
  56. package/skills/agentic-engineering/SKILL.md +64 -64
  57. package/skills/agentic-os/SKILL.md +388 -388
  58. package/skills/ai-first-engineering/SKILL.md +52 -52
  59. package/skills/ai-regression-testing/SKILL.md +386 -386
  60. package/skills/android-clean-architecture/SKILL.md +340 -340
  61. package/skills/angular-developer/SKILL.md +155 -155
  62. package/skills/angular-developer/references/angular-animations.md +160 -160
  63. package/skills/angular-developer/references/angular-aria.md +410 -410
  64. package/skills/angular-developer/references/cli.md +86 -86
  65. package/skills/angular-developer/references/component-harnesses.md +59 -59
  66. package/skills/angular-developer/references/component-styling.md +91 -91
  67. package/skills/angular-developer/references/components.md +117 -117
  68. package/skills/angular-developer/references/creating-services.md +97 -97
  69. package/skills/angular-developer/references/data-resolvers.md +69 -69
  70. package/skills/angular-developer/references/define-routes.md +67 -67
  71. package/skills/angular-developer/references/defining-providers.md +72 -72
  72. package/skills/angular-developer/references/di-fundamentals.md +120 -120
  73. package/skills/angular-developer/references/e2e-testing.md +56 -56
  74. package/skills/angular-developer/references/effects.md +83 -83
  75. package/skills/angular-developer/references/hierarchical-injectors.md +43 -43
  76. package/skills/angular-developer/references/host-elements.md +80 -80
  77. package/skills/angular-developer/references/injection-context.md +63 -63
  78. package/skills/angular-developer/references/inputs.md +101 -101
  79. package/skills/angular-developer/references/linked-signal.md +59 -59
  80. package/skills/angular-developer/references/loading-strategies.md +61 -61
  81. package/skills/angular-developer/references/mcp.md +108 -108
  82. package/skills/angular-developer/references/navigate-to-routes.md +69 -69
  83. package/skills/angular-developer/references/outputs.md +86 -86
  84. package/skills/angular-developer/references/reactive-forms.md +122 -122
  85. package/skills/angular-developer/references/rendering-strategies.md +44 -44
  86. package/skills/angular-developer/references/resource.md +77 -77
  87. package/skills/angular-developer/references/route-animations.md +56 -56
  88. package/skills/angular-developer/references/route-guards.md +52 -52
  89. package/skills/angular-developer/references/router-lifecycle.md +45 -45
  90. package/skills/angular-developer/references/router-testing.md +87 -87
  91. package/skills/angular-developer/references/show-routes-with-outlets.md +68 -68
  92. package/skills/angular-developer/references/signal-forms.md +795 -795
  93. package/skills/angular-developer/references/signals-overview.md +94 -94
  94. package/skills/angular-developer/references/tailwind-css.md +69 -69
  95. package/skills/angular-developer/references/template-driven-forms.md +114 -114
  96. package/skills/angular-developer/references/testing-fundamentals.md +65 -65
  97. package/skills/api-connector-builder/SKILL.md +121 -121
  98. package/skills/api-design/SKILL.md +524 -524
  99. package/skills/architecture-decision-records/SKILL.md +180 -180
  100. package/skills/article-writing/SKILL.md +80 -80
  101. package/skills/automation-audit-ops/SKILL.md +143 -143
  102. package/skills/autonomous-agent-harness/SKILL.md +274 -274
  103. package/skills/autonomous-loops/SKILL.md +611 -611
  104. package/skills/backend-patterns/SKILL.md +562 -562
  105. package/skills/benchmark/SKILL.md +95 -95
  106. package/skills/benchmark-methodology/SKILL.md +191 -191
  107. package/skills/benchmark-optimization-loop/SKILL.md +71 -71
  108. package/skills/blender-motion-state-inspection/SKILL.md +165 -165
  109. package/skills/blueprint/SKILL.md +106 -106
  110. package/skills/brand-discovery/SKILL.md +145 -145
  111. package/skills/brand-discovery/references/10_purpose-why.md +40 -40
  112. package/skills/brand-discovery/references/20_positioning.md +44 -44
  113. package/skills/brand-discovery/references/30_audience-niche.md +52 -52
  114. package/skills/brand-discovery/references/40_personality-archetype.md +57 -57
  115. package/skills/brand-discovery/references/50_voice-tone.md +59 -59
  116. package/skills/brand-discovery/references/60_narrative-story.md +50 -50
  117. package/skills/brand-discovery/references/70_founder-tension.md +49 -49
  118. package/skills/brand-discovery/references/90_SYNTHESIS.md +133 -133
  119. package/skills/brand-voice/SKILL.md +98 -98
  120. package/skills/brand-voice/references/voice-profile-schema.md +55 -55
  121. package/skills/browser-qa/SKILL.md +105 -105
  122. package/skills/bun-runtime/SKILL.md +85 -85
  123. package/skills/canary-watch/SKILL.md +108 -108
  124. package/skills/carrier-relationship-management/SKILL.md +212 -212
  125. package/skills/cisco-ios-patterns/SKILL.md +164 -164
  126. package/skills/ck/SKILL.md +148 -148
  127. package/skills/claude-devfleet/SKILL.md +112 -112
  128. package/skills/click-path-audit/SKILL.md +245 -245
  129. package/skills/clickhouse-io/SKILL.md +445 -445
  130. package/skills/code-tour/SKILL.md +254 -254
  131. package/skills/codebase-onboarding/SKILL.md +234 -234
  132. package/skills/codehealth-mcp/SKILL.md +167 -167
  133. package/skills/coding-standards/SKILL.md +551 -551
  134. package/skills/competitive-platform-analysis/SKILL.md +214 -214
  135. package/skills/competitive-report-structure/SKILL.md +162 -162
  136. package/skills/compose-multiplatform-patterns/SKILL.md +300 -300
  137. package/skills/config-gc/SKILL.md +120 -120
  138. package/skills/configure-ecc/SKILL.md +385 -385
  139. package/skills/connections-optimizer/SKILL.md +190 -190
  140. package/skills/content-engine/SKILL.md +132 -132
  141. package/skills/content-hash-cache-pattern/SKILL.md +162 -162
  142. package/skills/context-budget/SKILL.md +136 -136
  143. package/skills/continuous-agent-loop/SKILL.md +46 -46
  144. package/skills/continuous-learning/SKILL.md +132 -132
  145. package/skills/continuous-learning-v2/SKILL.md +361 -361
  146. package/skills/contract-first/SKILL.md +287 -287
  147. package/skills/cost-aware-llm-pipeline/SKILL.md +184 -184
  148. package/skills/cost-tracking/SKILL.md +97 -97
  149. package/skills/council/SKILL.md +204 -204
  150. package/skills/cpp-coding-standards/SKILL.md +724 -724
  151. package/skills/cpp-testing/SKILL.md +325 -325
  152. package/skills/crosspost/SKILL.md +112 -112
  153. package/skills/csharp-testing/SKILL.md +322 -322
  154. package/skills/customer-billing-ops/SKILL.md +141 -141
  155. package/skills/customs-trade-compliance/SKILL.md +263 -263
  156. package/skills/dart-flutter-patterns/SKILL.md +564 -564
  157. package/skills/dashboard-builder/SKILL.md +109 -109
  158. package/skills/data-scraper-agent/SKILL.md +765 -765
  159. package/skills/data-throughput-accelerator/SKILL.md +74 -74
  160. package/skills/database-migrations/SKILL.md +430 -430
  161. package/skills/deep-research/SKILL.md +160 -160
  162. package/skills/defi-amm-security/SKILL.md +167 -167
  163. package/skills/delivery-gate/SKILL.md +126 -126
  164. package/skills/deployment-patterns/SKILL.md +428 -428
  165. package/skills/design-system/SKILL.md +83 -83
  166. package/skills/django-celery/SKILL.md +458 -458
  167. package/skills/django-patterns/SKILL.md +735 -735
  168. package/skills/django-security/SKILL.md +644 -644
  169. package/skills/django-tdd/SKILL.md +730 -730
  170. package/skills/django-verification/SKILL.md +470 -470
  171. package/skills/dmux-workflows/SKILL.md +192 -192
  172. package/skills/docker-patterns/SKILL.md +365 -365
  173. package/skills/documentation-lookup/SKILL.md +91 -91
  174. package/skills/dotnet-patterns/SKILL.md +322 -322
  175. package/skills/dynamic-workflow-mode/SKILL.md +124 -124
  176. package/skills/e2e-testing/SKILL.md +327 -327
  177. package/skills/ecc-guide/SKILL.md +190 -190
  178. package/skills/ecc-recipes/SKILL.md +149 -149
  179. package/skills/ecc-tools-cost-audit/SKILL.md +161 -161
  180. package/skills/email-ops/SKILL.md +122 -122
  181. package/skills/energy-procurement/SKILL.md +228 -228
  182. package/skills/enterprise-agent-ops/SKILL.md +51 -51
  183. package/skills/error-handling/SKILL.md +377 -377
  184. package/skills/eval-harness/SKILL.md +271 -271
  185. package/skills/evm-token-decimals/SKILL.md +131 -131
  186. package/skills/exa-search/SKILL.md +108 -108
  187. package/skills/fal-ai-media/SKILL.md +289 -289
  188. package/skills/fastapi-patterns/SKILL.md +514 -514
  189. package/skills/finance-billing-ops/SKILL.md +128 -128
  190. package/skills/flox-environments/SKILL.md +497 -497
  191. package/skills/flutter-dart-code-review/SKILL.md +436 -436
  192. package/skills/foundation-models-on-device/SKILL.md +243 -243
  193. package/skills/frontend-a11y/SKILL.md +446 -446
  194. package/skills/frontend-design-direction/SKILL.md +93 -93
  195. package/skills/frontend-patterns/SKILL.md +657 -657
  196. package/skills/frontend-slides/SKILL.md +185 -185
  197. package/skills/fsharp-testing/SKILL.md +281 -281
  198. package/skills/gan-style-harness/SKILL.md +279 -279
  199. package/skills/gateguard/SKILL.md +133 -133
  200. package/skills/generating-python-installer/SKILL.md +820 -820
  201. package/skills/git-workflow/SKILL.md +716 -716
  202. package/skills/github-ops/SKILL.md +145 -145
  203. package/skills/golang-patterns/SKILL.md +676 -676
  204. package/skills/golang-testing/SKILL.md +721 -721
  205. package/skills/google-workspace-ops/SKILL.md +96 -96
  206. package/skills/growth-log/SKILL.md +128 -128
  207. package/skills/healthcare-cdss-patterns/SKILL.md +246 -246
  208. package/skills/healthcare-emr-patterns/SKILL.md +160 -160
  209. package/skills/healthcare-eval-harness/SKILL.md +208 -208
  210. package/skills/healthcare-phi-compliance/SKILL.md +146 -146
  211. package/skills/hermes-imports/SKILL.md +89 -89
  212. package/skills/hexagonal-architecture/SKILL.md +277 -277
  213. package/skills/hipaa-compliance/SKILL.md +79 -79
  214. package/skills/homelab-network-readiness/SKILL.md +170 -170
  215. package/skills/homelab-network-setup/SKILL.md +130 -130
  216. package/skills/homelab-pihole-dns/SKILL.md +275 -275
  217. package/skills/homelab-vlan-segmentation/SKILL.md +312 -312
  218. package/skills/homelab-wireguard-vpn/SKILL.md +306 -306
  219. package/skills/hookify-rules/SKILL.md +128 -128
  220. package/skills/inherit-legacy-style/SKILL.md +157 -157
  221. package/skills/intent-driven-development/SKILL.md +360 -360
  222. package/skills/inventory-demand-planning/SKILL.md +247 -247
  223. package/skills/investor-materials/SKILL.md +97 -97
  224. package/skills/investor-outreach/SKILL.md +92 -92
  225. package/skills/ios-icon-gen/SKILL.md +158 -158
  226. package/skills/iterative-retrieval/SKILL.md +212 -212
  227. package/skills/ito-basket-compare/SKILL.md +64 -64
  228. package/skills/ito-compute/SKILL.md +129 -129
  229. package/skills/ito-data-atlas-agent/SKILL.md +64 -64
  230. package/skills/ito-market-intelligence/SKILL.md +61 -61
  231. package/skills/ito-trade-planner/SKILL.md +68 -68
  232. package/skills/java-coding-standards/SKILL.md +384 -384
  233. package/skills/jira-integration/SKILL.md +303 -303
  234. package/skills/jpa-patterns/SKILL.md +152 -152
  235. package/skills/knowledge-ops/SKILL.md +155 -155
  236. package/skills/kotlin-coroutines-flows/SKILL.md +285 -285
  237. package/skills/kotlin-exposed-patterns/SKILL.md +720 -720
  238. package/skills/kotlin-ktor-patterns/SKILL.md +690 -690
  239. package/skills/kotlin-patterns/SKILL.md +712 -712
  240. package/skills/kotlin-testing/SKILL.md +825 -825
  241. package/skills/kubernetes-patterns/SKILL.md +756 -756
  242. package/skills/laravel-patterns/SKILL.md +416 -416
  243. package/skills/laravel-plugin-discovery/SKILL.md +230 -230
  244. package/skills/laravel-security/SKILL.md +948 -948
  245. package/skills/laravel-tdd/SKILL.md +675 -675
  246. package/skills/laravel-verification/SKILL.md +180 -180
  247. package/skills/latency-critical-systems/SKILL.md +75 -75
  248. package/skills/lead-intelligence/SKILL.md +322 -322
  249. package/skills/liquid-glass-design/SKILL.md +279 -279
  250. package/skills/llm-trading-agent-security/SKILL.md +147 -147
  251. package/skills/logistics-exception-management/SKILL.md +222 -222
  252. package/skills/loop-design-check/SKILL.md +143 -143
  253. package/skills/mailtrap-email-integration/SKILL.md +77 -77
  254. package/skills/make-interfaces-feel-better/SKILL.md +152 -152
  255. package/skills/manim-video/SKILL.md +90 -90
  256. package/skills/market-research/SKILL.md +76 -76
  257. package/skills/marketing-campaign/SKILL.md +114 -114
  258. package/skills/mcp-server-patterns/SKILL.md +70 -70
  259. package/skills/messages-ops/SKILL.md +105 -105
  260. package/skills/ml-adoption-playbook/SKILL.md +57 -57
  261. package/skills/mle-workflow/SKILL.md +348 -348
  262. package/skills/motion-advanced/SKILL.md +596 -596
  263. package/skills/motion-foundations/SKILL.md +299 -299
  264. package/skills/motion-patterns/SKILL.md +434 -434
  265. package/skills/motion-ui/SKILL.md +576 -576
  266. package/skills/mysql-patterns/SKILL.md +413 -413
  267. package/skills/nanoclaw-repl/SKILL.md +34 -34
  268. package/skills/nestjs-patterns/SKILL.md +231 -231
  269. package/skills/netmiko-ssh-automation/SKILL.md +174 -174
  270. package/skills/network-bgp-diagnostics/SKILL.md +168 -168
  271. package/skills/network-config-validation/SKILL.md +211 -211
  272. package/skills/network-interface-health/SKILL.md +153 -153
  273. package/skills/nextjs-turbopack/SKILL.md +58 -58
  274. package/skills/nodejs-keccak256/SKILL.md +103 -103
  275. package/skills/nutrient-document-processing/SKILL.md +168 -168
  276. package/skills/nuxt4-patterns/SKILL.md +101 -101
  277. package/skills/openclaw-persona-forge/SKILL.md +289 -289
  278. package/skills/openclaw-persona-forge/references/avatar-style.md +124 -124
  279. package/skills/openclaw-persona-forge/references/boundary-rules.md +53 -53
  280. package/skills/openclaw-persona-forge/references/error-handling.md +53 -53
  281. package/skills/openclaw-persona-forge/references/identity-tension.md +48 -48
  282. package/skills/openclaw-persona-forge/references/naming-system.md +39 -39
  283. package/skills/openclaw-persona-forge/references/output-template.md +166 -166
  284. package/skills/opensource-pipeline/SKILL.md +256 -256
  285. package/skills/orch-add-feature/SKILL.md +45 -45
  286. package/skills/orch-build-mvp/SKILL.md +49 -49
  287. package/skills/orch-change-feature/SKILL.md +43 -43
  288. package/skills/orch-fix-defect/SKILL.md +43 -43
  289. package/skills/orch-pipeline/SKILL.md +121 -121
  290. package/skills/orch-refine-code/SKILL.md +44 -44
  291. package/skills/parallel-execution-optimizer/SKILL.md +74 -74
  292. package/skills/perl-patterns/SKILL.md +505 -505
  293. package/skills/perl-security/SKILL.md +504 -504
  294. package/skills/perl-testing/SKILL.md +476 -476
  295. package/skills/plan-canvas/SKILL.md +153 -153
  296. package/skills/plan-orchestrate/SKILL.md +263 -263
  297. package/skills/plankton-code-quality/SKILL.md +237 -237
  298. package/skills/postgres-patterns/SKILL.md +148 -148
  299. package/skills/prediction-market-oracle-research/SKILL.md +64 -64
  300. package/skills/prediction-market-risk-review/SKILL.md +61 -61
  301. package/skills/premium-web-design/SKILL.md +154 -154
  302. package/skills/prisma-patterns/SKILL.md +401 -401
  303. package/skills/product-capability/SKILL.md +142 -142
  304. package/skills/product-lens/SKILL.md +93 -93
  305. package/skills/production-audit/SKILL.md +207 -207
  306. package/skills/production-scheduling/SKILL.md +238 -238
  307. package/skills/project-flow-ops/SKILL.md +112 -112
  308. package/skills/prompt-optimizer/SKILL.md +398 -398
  309. package/skills/publish-website/SKILL.md +76 -0
  310. package/skills/python-patterns/SKILL.md +751 -751
  311. package/skills/python-testing/SKILL.md +817 -817
  312. package/skills/pytorch-patterns/SKILL.md +397 -397
  313. package/skills/quality-nonconformance/SKILL.md +260 -260
  314. package/skills/quarkus-patterns/SKILL.md +723 -723
  315. package/skills/quarkus-security/SKILL.md +468 -468
  316. package/skills/quarkus-tdd/SKILL.md +812 -812
  317. package/skills/quarkus-verification/SKILL.md +480 -480
  318. package/skills/ralphinho-rfc-pipeline/SKILL.md +68 -68
  319. package/skills/react-native-patterns/SKILL.md +326 -326
  320. package/skills/react-patterns/SKILL.md +342 -342
  321. package/skills/react-performance/SKILL.md +575 -575
  322. package/skills/react-testing/SKILL.md +424 -424
  323. package/skills/recsys-pipeline-architect/SKILL.md +115 -115
  324. package/skills/recursive-decision-ledger/SKILL.md +81 -81
  325. package/skills/redis-patterns/SKILL.md +404 -404
  326. package/skills/regex-vs-llm-structured-text/SKILL.md +221 -221
  327. package/skills/remotion-video-creation/SKILL.md +43 -43
  328. package/skills/repo-scan/SKILL.md +79 -79
  329. package/skills/research-ops/SKILL.md +113 -113
  330. package/skills/returns-reverse-logistics/SKILL.md +240 -240
  331. package/skills/rules-distill/SKILL.md +265 -265
  332. package/skills/rust-patterns/SKILL.md +500 -500
  333. package/skills/rust-testing/SKILL.md +501 -501
  334. package/skills/safety-guard/SKILL.md +76 -76
  335. package/skills/santa-method/SKILL.md +307 -307
  336. package/skills/scientific-db-pubmed-database/SKILL.md +176 -176
  337. package/skills/scientific-db-uspto-database/SKILL.md +178 -178
  338. package/skills/scientific-pkg-gget/SKILL.md +167 -167
  339. package/skills/scientific-thinking-literature-review/SKILL.md +193 -193
  340. package/skills/scientific-thinking-scholar-evaluation/SKILL.md +161 -161
  341. package/skills/search-first/SKILL.md +183 -183
  342. package/skills/security-bounty-hunter/SKILL.md +100 -100
  343. package/skills/security-review/SKILL.md +504 -504
  344. package/skills/security-scan/SKILL.md +166 -166
  345. package/skills/seo/SKILL.md +155 -155
  346. package/skills/skill-comply/SKILL.md +59 -59
  347. package/skills/skill-scout/SKILL.md +141 -141
  348. package/skills/skill-stocktake/SKILL.md +195 -195
  349. package/skills/social-graph-ranker/SKILL.md +155 -155
  350. package/skills/social-publisher/SKILL.md +130 -130
  351. package/skills/springboot-patterns/SKILL.md +315 -315
  352. package/skills/springboot-security/SKILL.md +273 -273
  353. package/skills/springboot-tdd/SKILL.md +159 -159
  354. package/skills/springboot-verification/SKILL.md +232 -232
  355. package/skills/strategic-compact/SKILL.md +142 -142
  356. package/skills/swift-actor-persistence/SKILL.md +144 -144
  357. package/skills/swift-concurrency-6-2/SKILL.md +216 -216
  358. package/skills/swift-protocol-di-testing/SKILL.md +191 -191
  359. package/skills/swiftui-patterns/SKILL.md +259 -259
  360. package/skills/taste/SKILL.md +264 -264
  361. package/skills/taste/references/genre-taxonomy.md +87 -87
  362. package/skills/tdd-workflow/SKILL.md +583 -583
  363. package/skills/team-agent-orchestration/SKILL.md +111 -111
  364. package/skills/team-builder/SKILL.md +169 -169
  365. package/skills/terminal-ops/SKILL.md +110 -110
  366. package/skills/tinystruct-patterns/SKILL.md +279 -279
  367. package/skills/tinystruct-patterns/references/architecture.md +90 -90
  368. package/skills/tinystruct-patterns/references/data-handling.md +60 -60
  369. package/skills/tinystruct-patterns/references/database.md +99 -99
  370. package/skills/tinystruct-patterns/references/routing.md +64 -64
  371. package/skills/tinystruct-patterns/references/system-usage.md +97 -97
  372. package/skills/tinystruct-patterns/references/testing.md +72 -72
  373. package/skills/token-budget-advisor/SKILL.md +134 -134
  374. package/skills/ui-demo/SKILL.md +466 -466
  375. package/skills/ui-to-vue/SKILL.md +135 -135
  376. package/skills/uncloud/SKILL.md +344 -344
  377. package/skills/unified-memory/SKILL.md +170 -170
  378. package/skills/unified-notifications-ops/SKILL.md +188 -188
  379. package/skills/verification-loop/SKILL.md +129 -129
  380. package/skills/video-editing/SKILL.md +311 -311
  381. package/skills/videodb/SKILL.md +375 -375
  382. package/skills/visa-doc-translate/SKILL.md +117 -117
  383. package/skills/vite-patterns/SKILL.md +450 -450
  384. package/skills/vue-patterns/SKILL.md +471 -471
  385. package/skills/windows-desktop-e2e/SKILL.md +888 -888
  386. package/skills/workspace-surface-audit/SKILL.md +126 -126
  387. package/skills/x-api/SKILL.md +235 -235
package/lib/tools.mjs CHANGED
@@ -23,12 +23,14 @@ const terminalHints = require('./terminal-hints.js');
23
23
  const diagnostics = require('./diagnostics.js');
24
24
  const { commandPatterns } = require('./arity.js');
25
25
  const mcpLib = require('./mcp.js');
26
+ const reachLib = require('./reach.js');
26
27
  const applyLimit = require('./apply-limit.js');
27
28
  const config = require('./config.js');
28
29
  const skills = require('./skills.js');
29
30
  const oauth = require('./oauth-connectors.js');
30
31
  const webTools = require('./web-tools.js');
31
32
  const applyPatchLib = require('./apply-patch.js');
33
+ const publishLib = require('./publish.js');
32
34
 
33
35
  // Successful writes made while using the hosted model are logged (counts
34
36
  // only) for the usage dashboard. There is no cap - see apply-limit.js.
@@ -713,33 +715,289 @@ async function getValidGmailToken({ force = false } = {}) {
713
715
  return refreshed.access_token;
714
716
  }
715
717
 
718
+ /**
719
+ * An approval answer: a plain string ('once', 'always', 'reject', 'draft') or
720
+ * { verdict, edits } when the user changed the email on the card before
721
+ * saying yes. Every host may answer either way.
722
+ */
723
+ function readVerdict(v) {
724
+ if (v && typeof v === 'object') {
725
+ const edits = v.edits && typeof v.edits === 'object' ? v.edits : null;
726
+ return { action: String(v.verdict || v.action || 'reject'), edits };
727
+ }
728
+ return { action: String(v || 'reject'), edits: null };
729
+ }
730
+
731
+ /** The email as the user left it on the approval card. */
732
+ function applyEmailEdits(draft, edits) {
733
+ if (!edits) return draft;
734
+ const out = { ...draft };
735
+ for (const k of ['to', 'subject', 'body']) {
736
+ if (typeof edits[k] === 'string') out[k] = k === 'body' ? edits[k].slice(0, 100000) : edits[k].trim().slice(0, 1000);
737
+ }
738
+ return out;
739
+ }
740
+
741
+ const GMAIL_NOT_CONNECTED = 'Gmail is not connected. Ask the user to connect it from Connect Apps (account menu → Connect Apps) first.';
742
+
743
+ // ─── Drafts kept by Codeply itself (~/.codeply/drafts) ──────────────────────
744
+ // Where gmail_draft saves when Gmail is not connected (or not allowed to
745
+ // draft yet). One JSON file per draft; gmail_send can send one by id.
746
+ function draftsDir() { return path.join(os.homedir(), '.codeply', 'drafts'); }
747
+ const DRAFT_ID = /^d[a-z0-9]{6,30}$/;
748
+
749
+ function saveLocalDraft({ to, subject, body }) {
750
+ const dir = draftsDir();
751
+ fs.mkdirSync(dir, { recursive: true });
752
+ const id = `d${Date.now().toString(36)}${crypto.randomBytes(3).toString('hex')}`;
753
+ const draft = { id, to: to || '', subject: subject || '', body: body || '', createdAt: new Date().toISOString() };
754
+ fs.writeFileSync(path.join(dir, `${id}.json`), JSON.stringify(draft, null, 2));
755
+ return draft;
756
+ }
757
+
758
+ function readLocalDraft(id) {
759
+ if (!DRAFT_ID.test(String(id || ''))) return null;
760
+ try { return JSON.parse(fs.readFileSync(path.join(draftsDir(), `${id}.json`), 'utf8')); } catch { return null; }
761
+ }
762
+
763
+ function listLocalDrafts() {
764
+ let files = [];
765
+ try { files = fs.readdirSync(draftsDir()).filter((f) => f.endsWith('.json')); } catch { return []; }
766
+ return files.map((f) => { try { return JSON.parse(fs.readFileSync(path.join(draftsDir(), f), 'utf8')); } catch { return null; } })
767
+ .filter((d) => d && d.id).sort((a, b) => String(b.createdAt).localeCompare(String(a.createdAt)));
768
+ }
769
+
770
+ /** True only when we know the stored connection lacks a scope (older connections did not record scopes). */
771
+ function lacksScope(integration, scope) {
772
+ return typeof integration.scope === 'string' && integration.scope.trim() !== '' && !integration.scope.split(/\s+/).includes(scope);
773
+ }
774
+
775
+ /**
776
+ * Saves a draft: in Gmail when it is connected and allowed to draft,
777
+ * otherwise in Codeply's own drafts. Returns a tool result.
778
+ */
779
+ async function storeDraft(draft, token) {
780
+ let why = '';
781
+ if (token) {
782
+ if (lacksScope(config.getIntegration('gmail'), oauth.GOOGLE_SCOPE.compose)) {
783
+ why = 'Gmail was connected before drafts were allowed, so it was saved in Codeply\'s drafts instead. Reconnect Gmail in Connect Apps to allow drafts in Gmail.';
784
+ } else {
785
+ try {
786
+ const r = await oauth.gmailCreateDraft(token, draft);
787
+ return { ok: true, output: `Draft saved in Gmail's Drafts folder${draft.to ? ` (to ${draft.to})` : ''}, Gmail draft id ${r.id}. Nothing was sent.`, meta: { label: `draft: ${draft.subject || '(no subject)'}` } };
788
+ } catch (e) {
789
+ if (!e.scopeMissing) return { ok: false, output: `Gmail draft failed: ${e.message}` };
790
+ why = 'Gmail was connected before drafts were allowed, so it was saved in Codeply\'s drafts instead. Reconnect Gmail in Connect Apps to allow drafts in Gmail.';
791
+ }
792
+ }
793
+ } else {
794
+ why = 'Gmail is not connected, so it was saved in Codeply\'s own drafts (not in Gmail). Connect Gmail in Connect Apps to keep drafts in Gmail.';
795
+ }
796
+ const d = saveLocalDraft(draft);
797
+ return {
798
+ ok: true,
799
+ output: `Draft saved, id ${d.id}. ${why} Nothing was sent. drafts_list shows saved drafts; gmail_send with <draft>${d.id}</draft> sends it once Gmail is connected.`,
800
+ meta: { label: `draft ${d.id}: ${d.subject || '(no subject)'}`, draftId: d.id },
801
+ };
802
+ }
803
+
716
804
  async function gmail_send(args, ctx) {
717
- const to = (args.to || '').trim();
718
- const subject = (args.subject || '').trim();
719
- const body = args.body || '';
805
+ let to = (args.to || '').trim();
806
+ let subject = (args.subject || '').trim();
807
+ let body = args.body || '';
808
+ const draftId = String(args.draft || '').trim();
809
+ let saved = null;
810
+ if (draftId) {
811
+ saved = readLocalDraft(draftId);
812
+ if (!saved) return { ok: false, output: `No saved draft with id "${draftId}". drafts_list shows the saved drafts.` };
813
+ to = to || saved.to; subject = subject || saved.subject; body = body || saved.body;
814
+ }
720
815
  if (!to) return { ok: false, output: 'gmail_send needs a <to> address.' };
721
816
  if (!subject) return { ok: false, output: 'gmail_send needs a <subject>.' };
722
817
 
723
818
  let token;
724
819
  try { token = await getValidGmailToken(); } catch (e) { return { ok: false, output: `Gmail send failed: ${e.message}` }; }
725
- if (!token) return { ok: false, output: 'Gmail is not connected. Ask the user to connect it from Connect Apps (account menu → Connect Apps) first.' };
820
+ if (!token) return { ok: false, output: `${GMAIL_NOT_CONNECTED} (gmail_draft can save it as a draft in the meantime.)` };
726
821
 
727
- const verdict = await ctx.approve({
822
+ const verdict = readVerdict(await ctx.approve({
728
823
  tool: 'gmail_send',
729
824
  title: `Send email to ${to}`,
730
825
  detail: `Subject: ${subject}\n\n${body}`,
731
826
  danger: false,
732
- });
733
- if (verdict === 'reject') return { ok: false, output: `User declined to send the email to ${to}.`, meta: { rejected: true } };
827
+ // The card shows these as editable fields; edits come back with the verdict.
828
+ draft: { to, subject, body },
829
+ }));
830
+ if (verdict.action === 'reject') return { ok: false, output: `User declined to send the email to ${to}.`, meta: { rejected: true } };
831
+ const email = applyEmailEdits({ to, subject, body }, verdict.edits);
832
+ const edited = !!verdict.edits && (email.to !== to || email.subject !== subject || email.body !== body);
833
+ if (verdict.action === 'draft') {
834
+ const r = await storeDraft(email, token);
835
+ return { ...r, output: `The user chose to save it as a draft instead of sending. ${r.output}` };
836
+ }
837
+ if (!email.to) return { ok: false, output: 'The email has no recipient after the user\'s edits, so it was not sent.' };
734
838
 
735
839
  try {
736
- const result = await oauth.gmailSend(token, { to, subject, body });
737
- return { ok: true, output: `Email sent to ${to} (message id ${result.id}).`, meta: { label: `to ${to}: ${subject}` } };
840
+ const result = await oauth.gmailSend(token, email);
841
+ if (saved) { try { fs.unlinkSync(path.join(draftsDir(), `${saved.id}.json`)); } catch {} }
842
+ const note = edited ? ` The user edited it before sending; what went out: to ${email.to}, subject "${email.subject}".` : '';
843
+ return { ok: true, output: `Email sent to ${email.to} (message id ${result.id}).${note}`, meta: { label: `to ${email.to}: ${email.subject}` } };
738
844
  } catch (e) {
739
845
  return { ok: false, output: `Gmail send failed: ${e.message}` };
740
846
  }
741
847
  }
742
848
 
849
+ async function gmail_draft(args, ctx) {
850
+ const draft = { to: (args.to || '').trim(), subject: (args.subject || '').trim(), body: args.body || '' };
851
+ if (!draft.subject && !draft.body.trim()) return { ok: false, output: 'gmail_draft needs at least a <subject> or a <body>.' };
852
+
853
+ let token = null;
854
+ try { token = await getValidGmailToken(); } catch { token = null; } // an expired sign-in still gets a local draft
855
+
856
+ const verdict = readVerdict(await ctx.approve({
857
+ tool: 'gmail_draft',
858
+ title: `Save a draft${draft.to ? ` to ${draft.to}` : ''}${token ? ' in Gmail' : ''}`,
859
+ detail: `Subject: ${draft.subject}\n\n${draft.body}`,
860
+ danger: false,
861
+ draft,
862
+ draftOnly: true,
863
+ }));
864
+ if (verdict.action === 'reject') return { ok: false, output: 'User declined to save the draft.', meta: { rejected: true } };
865
+ return storeDraft(applyEmailEdits(draft, verdict.edits), token);
866
+ }
867
+
868
+ async function drafts_list() {
869
+ const list = listLocalDrafts();
870
+ if (!list.length) return { ok: true, output: 'No drafts saved in Codeply. (Drafts saved in Gmail are in Gmail\'s Drafts folder.)', meta: { label: 'no drafts' } };
871
+ const lines = list.slice(0, 30).map((d) => `- ${d.id}: "${d.subject || '(no subject)'}" to ${d.to || '(no recipient yet)'}, saved ${d.createdAt}\n ${String(d.body || '').replace(/\s+/g, ' ').slice(0, 160)}`);
872
+ return { ok: true, output: `${list.length} draft(s) saved in Codeply, newest first:\n\n${lines.join('\n')}\n\nSend one with gmail_send and <draft>id</draft>.`, meta: { label: `${list.length} draft(s)`, count: list.length } };
873
+ }
874
+
875
+ // ─── Google Calendar (the Gmail connection, with the calendar scope) ────────
876
+ const pad2 = (n) => String(n).padStart(2, '0');
877
+ const WEEKDAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
878
+
879
+ /** Local wall-clock ISO with the offset, e.g. 2026-10-03T14:00:00+05:00. */
880
+ function localIso(d) {
881
+ const off = -d.getTimezoneOffset();
882
+ const sign = off >= 0 ? '+' : '-';
883
+ const a = Math.abs(off);
884
+ return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}T${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}${sign}${pad2(Math.floor(a / 60))}:${pad2(a % 60)}`;
885
+ }
886
+ const ymd = (d) => `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
887
+ const showLocal = (d) => `${WEEKDAYS[d.getDay()]} ${ymd(d)} ${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
888
+
889
+ /**
890
+ * "today", "tomorrow", "2026-10-03" (a whole day, local) or a date-time
891
+ * ("2026-10-03T14:00", local unless it carries an offset). null if unreadable.
892
+ */
893
+ function parseWhen(input, now = new Date()) {
894
+ const s = String(input || '').trim().toLowerCase();
895
+ if (!s) return null;
896
+ const day = (offset) => { const d = new Date(now); d.setHours(0, 0, 0, 0); d.setDate(d.getDate() + offset); return { date: d, allDay: true }; };
897
+ if (s === 'today') return day(0);
898
+ if (s === 'tomorrow') return day(1);
899
+ if (s === 'yesterday') return day(-1);
900
+ const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
901
+ if (m) return { date: new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3])), allDay: true };
902
+ const d = new Date(String(input).trim().replace(' ', 'T'));
903
+ return Number.isNaN(d.getTime()) ? null : { date: d, allDay: false };
904
+ }
905
+
906
+ async function googleTokenFor(what) {
907
+ const token = await getValidGmailToken();
908
+ if (!token) return { error: `${what} uses the Gmail connection, and Gmail is not connected. Ask the user to connect Gmail from Connect Apps (account menu → Connect Apps) first; it covers Calendar too.` };
909
+ if (lacksScope(config.getIntegration('gmail'), oauth.GOOGLE_SCOPE.calendar)) {
910
+ return { error: 'Gmail was connected before Calendar access was added. Ask the user to reconnect Gmail in Connect Apps (it covers Calendar too) and allow calendar access.' };
911
+ }
912
+ return { token };
913
+ }
914
+
915
+ function eventLine(ev) {
916
+ const title = ev.summary || '(no title)';
917
+ let when;
918
+ if (ev.start && ev.start.date) {
919
+ const [y, mo, da] = ev.start.date.split('-').map(Number);
920
+ const s = new Date(y, mo - 1, da);
921
+ when = `${WEEKDAYS[s.getDay()]} ${ev.start.date}, all day`;
922
+ } else {
923
+ const s = new Date(ev.start && ev.start.dateTime);
924
+ const e = new Date(ev.end && ev.end.dateTime);
925
+ when = Number.isNaN(s.getTime()) ? '(no time)' : `${showLocal(s)} to ${Number.isNaN(e.getTime()) ? '?' : (ymd(e) === ymd(s) ? `${pad2(e.getHours())}:${pad2(e.getMinutes())}` : showLocal(e))}`;
926
+ }
927
+ return `- ${title}: ${when}${ev.location ? `, at ${ev.location}` : ''}`;
928
+ }
929
+
930
+ async function calendar_list(args) {
931
+ const now = new Date();
932
+ const from = args.from ? parseWhen(args.from, now) : parseWhen('today', now);
933
+ if (!from) return { ok: false, output: `calendar_list could not read <from> "${args.from}". Use today, tomorrow, 2026-10-03 or 2026-10-03T14:00.` };
934
+ let to = args.to ? parseWhen(args.to, now) : null;
935
+ if (args.to && !to) return { ok: false, output: `calendar_list could not read <to> "${args.to}". Use today, tomorrow, 2026-10-03 or 2026-10-03T14:00.` };
936
+ let end;
937
+ if (to) { end = new Date(to.date); if (to.allDay) end.setDate(end.getDate() + 1); } // a date as the end means through that whole day
938
+ else if (from.allDay) { end = new Date(from.date); end.setDate(end.getDate() + 1); }
939
+ else end = new Date(from.date.getTime() + 24 * 3600 * 1000);
940
+ if (end <= from.date) return { ok: false, output: 'calendar_list: <to> must be after <from>.' };
941
+ const max = Math.min(50, Math.max(1, parseInt(args.max, 10) || 20));
942
+ const range = `${showLocal(from.date)} to ${showLocal(end)}`;
943
+ try {
944
+ const t = await googleTokenFor('Google Calendar');
945
+ if (t.error) return { ok: false, output: t.error };
946
+ const items = await oauth.calendarListEvents(t.token, { timeMin: localIso(from.date), timeMax: localIso(end), maxResults: max });
947
+ if (!items.length) return { ok: true, output: `No events from ${range} (local time).`, meta: { label: 'no events' } };
948
+ return { ok: true, output: `${items.length} event(s) from ${range} (local time):\n${items.map(eventLine).join('\n')}`, meta: { label: `${items.length} event(s)`, count: items.length } };
949
+ } catch (e) {
950
+ return { ok: false, output: `Calendar list failed: ${e.message} Tell the user this exact error.` };
951
+ }
952
+ }
953
+
954
+ async function calendar_add(args, ctx) {
955
+ const title = (args.title || '').trim();
956
+ if (!title) return { ok: false, output: 'calendar_add needs a <title>.' };
957
+ const start = parseWhen(args.start);
958
+ if (!start) return { ok: false, output: `calendar_add needs a <start> it can read (got "${args.start || ''}"). Use 2026-10-03T14:00 for a time, or 2026-10-03 for an all-day event.` };
959
+ let end = args.end ? parseWhen(args.end) : null;
960
+ if (args.end && !end) return { ok: false, output: `calendar_add could not read <end> "${args.end}".` };
961
+ const event = { summary: title };
962
+ if (args.description) event.description = String(args.description);
963
+ if (args.location) event.location = String(args.location).trim();
964
+ let when;
965
+ if (start.allDay && (!end || end.allDay)) {
966
+ // All-day: Google's end date is exclusive, so a one-day event ends the next day.
967
+ const last = end ? end.date : start.date;
968
+ const after = new Date(last); after.setDate(after.getDate() + 1);
969
+ if (after <= start.date) return { ok: false, output: 'calendar_add: <end> must not be before <start>.' };
970
+ event.start = { date: ymd(start.date) };
971
+ event.end = { date: ymd(after) };
972
+ when = ymd(last) === ymd(start.date) ? `${WEEKDAYS[start.date.getDay()]} ${ymd(start.date)}, all day` : `${ymd(start.date)} to ${ymd(last)}, all day`;
973
+ } else {
974
+ const s = start.date;
975
+ const e = end ? end.date : new Date(s.getTime() + 60 * 60 * 1000);
976
+ if (e <= s) return { ok: false, output: 'calendar_add: <end> must be after <start>.' };
977
+ let tz = '';
978
+ try { tz = Intl.DateTimeFormat().resolvedOptions().timeZone || ''; } catch {}
979
+ event.start = { dateTime: localIso(s), ...(tz ? { timeZone: tz } : {}) };
980
+ event.end = { dateTime: localIso(e), ...(tz ? { timeZone: tz } : {}) };
981
+ when = `${showLocal(s)} to ${ymd(e) === ymd(s) ? `${pad2(e.getHours())}:${pad2(e.getMinutes())}` : showLocal(e)}`;
982
+ }
983
+ const mins = String(args.reminders || '').split(/[\s,]+/).map((x) => parseInt(x, 10)).filter((n) => Number.isFinite(n) && n >= 0 && n <= 40320).slice(0, 5);
984
+ if (mins.length) event.reminders = { useDefault: false, overrides: mins.map((minutes) => ({ method: 'popup', minutes })) };
985
+
986
+ let t;
987
+ try { t = await googleTokenFor('Google Calendar'); } catch (e) { return { ok: false, output: `Calendar add failed: ${e.message}` }; }
988
+ if (t.error) return { ok: false, output: t.error };
989
+
990
+ const detail = [`When: ${when} (local time)`, event.location ? `Where: ${event.location}` : '', mins.length ? `Reminders: ${mins.map((m) => `${m} min before`).join(', ')}` : '', event.description ? `\n${event.description}` : ''].filter(Boolean).join('\n');
991
+ const verdict = readVerdict(await ctx.approve({ tool: 'calendar_add', title: `Add "${title}" to your calendar`, detail, danger: false }));
992
+ if (verdict.action === 'reject') return { ok: false, output: `User declined to add "${title}" to the calendar.`, meta: { rejected: true } };
993
+ try {
994
+ const r = await oauth.calendarInsertEvent(t.token, event);
995
+ return { ok: true, output: `Added "${title}" to the calendar: ${when} (local time)${event.location ? `, at ${event.location}` : ''}.${r.htmlLink ? ` Link: ${r.htmlLink}` : ''}`, meta: { label: `${title}: ${when}` } };
996
+ } catch (e) {
997
+ return { ok: false, output: `Calendar add failed: ${e.message} Tell the user this exact error.` };
998
+ }
999
+ }
1000
+
743
1001
  async function gmail_search(args, ctx) {
744
1002
  const query = (args.query || '').trim();
745
1003
  // An empty query is fine: it lists the newest mail ("check my emails").
@@ -1502,8 +1760,12 @@ function formatApiResult(service, method, apiPath, r) {
1502
1760
  async function getValidSupabaseToken() {
1503
1761
  const sb = config.getIntegration('supabase');
1504
1762
  if (!sb.accessToken) return null;
1763
+ // A token with no expiry (a pasted personal access token) never lapses here.
1505
1764
  if (!sb.expiresAt || Date.now() < sb.expiresAt - 120000) return sb.accessToken;
1506
- if (!sb.refreshToken || !sb.clientId || !sb.clientSecret) return sb.accessToken;
1765
+ // Past expiry with no way to renew it (no OAuth app credentials): report
1766
+ // "not connected" so the user is asked to reconnect, instead of every call
1767
+ // failing with a bare "Unauthorized".
1768
+ if (!sb.refreshToken || !sb.clientId || !sb.clientSecret) return null;
1507
1769
  try {
1508
1770
  const t = await oauth.refreshSupabaseToken(sb.clientId, sb.clientSecret, sb.refreshToken);
1509
1771
  config.saveIntegration('supabase', {
@@ -1758,6 +2020,20 @@ async function web_search(args, ctx) {
1758
2020
  return { ok: true, output: truncate(r.text), meta: { label: query } };
1759
2021
  }
1760
2022
 
2023
+ async function image_search(args, ctx) {
2024
+ const query = String(args.query || '').trim();
2025
+ if (!query) return { ok: false, output: 'image_search needs a <query>.' };
2026
+ const r = await webTools.imageSearch(query, { num: Number(args.num) || 8, signal: ctx.signal });
2027
+ if (!r.ok) return { ok: false, output: r.error, meta: { label: query } };
2028
+ const lines = r.images.map((im, i) => `${i + 1}. ${im.title || 'image'} (${im.width}x${im.height})\n ${im.url}${im.page ? `\n from ${im.page}` : ''}`);
2029
+ return {
2030
+ ok: true,
2031
+ output: `${r.images.length} images for "${query}"${r.via === 'wikimedia' ? ' (Wikimedia Commons, free to use)' : ''}:\n${lines.join('\n')}\n\n` +
2032
+ 'Pick the one that fits the section best and download it with fetch_image to a path under assets/. Prefer landscape for heroes and banners.',
2033
+ meta: { label: query },
2034
+ };
2035
+ }
2036
+
1761
2037
  // ─── lsp ────────────────────────────────────────────────────────────────────
1762
2038
 
1763
2039
  const LSP_OPS = new Set(['definition', 'references', 'implementation', 'hover', 'documentsymbol', 'workspacesymbol']);
@@ -1950,14 +2226,26 @@ async function ask_bot(args, ctx) {
1950
2226
  return ctx.askBot({ name: String(args.bot || '').trim(), task: String(args.task || '').trim(), signal: ctx.signal });
1951
2227
  }
1952
2228
 
2229
+ // ─── Reaching the user's phone (lib/reach.js; the host registers the handler) ─
2230
+ const reach_me = (args, ctx) => reachLib.reachMe(args, ctx);
2231
+ const watch_email = (args, ctx) => reachLib.watchEmail(args, ctx);
2232
+
2233
+ // ─── Publishing (Supabase + Vercel + GitHub, see lib/publish.js) ────────────
2234
+ const publishTools = publishLib.makePublishTools({
2235
+ config,
2236
+ getSupabaseToken: () => getValidSupabaseToken(),
2237
+ resolveDir: (p, cwd) => resolvePath(String(p).trim(), cwd).abs,
2238
+ });
2239
+
1953
2240
  export const TOOLS = {
2241
+ ...publishTools,
1954
2242
  todo,
1955
2243
  ask_bot,
1956
2244
  ask_user,
1957
2245
  mcp,
1958
- web_fetch, web_search, apply_patch, plan_exit, plan_enter, lsp,
2246
+ web_fetch, web_search, image_search, apply_patch, plan_exit, plan_enter, lsp, reach_me, watch_email,
1959
2247
  list_dir, read_file, write_file, edit_file, search, run, use_skill, list_skills, fetch_image, browser_check,
1960
- gmail_send, gmail_search, slack_post_message, vercel_deploy, supabase_create_project, supabase_delete_project, github_create_repo,
2248
+ gmail_send, gmail_search, gmail_draft, drafts_list, calendar_list, calendar_add, slack_post_message, vercel_deploy, supabase_create_project, supabase_delete_project, github_create_repo,
1961
2249
  design_reference_search, view_images, supabase_api, supabase_sql, vercel_api,
1962
2250
  };
1963
2251
 
@@ -1965,7 +2253,7 @@ export const TOOLS = {
1965
2253
  // actually prompt for non-read requests (see above); read-only tools
1966
2254
  // (browser_check, gmail_search, design_reference_search, view_images, ...)
1967
2255
  // never prompt.
1968
- export const TOOL_NEEDS_APPROVAL = new Set(['write_file', 'edit_file', 'apply_patch', 'run', 'fetch_image', 'gmail_send', 'slack_post_message', 'vercel_deploy', 'supabase_create_project', 'supabase_delete_project', 'github_create_repo', 'supabase_api', 'supabase_sql', 'vercel_api']);
2256
+ export const TOOL_NEEDS_APPROVAL = new Set(['supabase_setup', 'supabase_schema', 'publish_deploy', 'publish_github', 'write_file', 'edit_file', 'apply_patch', 'run', 'fetch_image', 'gmail_send', 'gmail_draft', 'calendar_add', 'slack_post_message', 'vercel_deploy', 'supabase_create_project', 'supabase_delete_project', 'github_create_repo', 'supabase_api', 'supabase_sql', 'vercel_api']);
1969
2257
 
1970
2258
  /** Human-facing verb + colour hint for the transcript. */
1971
2259
  export const TOOL_DISPLAY = {
@@ -1975,6 +2263,9 @@ export const TOOL_DISPLAY = {
1975
2263
  mcp: { verb: 'mcp', icon: '⧉' },
1976
2264
  web_fetch: { verb: 'fetch', icon: '⇩' },
1977
2265
  web_search: { verb: 'web', icon: '▸' },
2266
+ image_search: { verb: 'images', icon: '▸' },
2267
+ reach_me: { verb: 'phone', icon: '☎' },
2268
+ watch_email: { verb: 'watch', icon: '✉' },
1978
2269
  apply_patch:{ verb: 'patch', icon: '✎' },
1979
2270
  plan_exit: { verb: 'plan', icon: '☐' },
1980
2271
  plan_enter: { verb: 'plan', icon: '☐' },
@@ -1991,6 +2282,10 @@ export const TOOL_DISPLAY = {
1991
2282
  browser_check:{ verb: 'check', icon: '◎' },
1992
2283
  gmail_send: { verb: 'email', icon: '✉' },
1993
2284
  gmail_search:{ verb: 'search', icon: '✉' },
2285
+ gmail_draft:{ verb: 'draft', icon: '✉' },
2286
+ drafts_list:{ verb: 'drafts', icon: '✉' },
2287
+ calendar_list:{ verb: 'calendar', icon: '▦' },
2288
+ calendar_add:{ verb: 'calendar', icon: '▦' },
1994
2289
  slack_post_message:{ verb: 'post', icon: '#' },
1995
2290
  vercel_deploy:{ verb: 'deploy', icon: '▲' },
1996
2291
  supabase_create_project:{ verb: 'provision', icon: '◆' },
@@ -2001,14 +2296,21 @@ export const TOOL_DISPLAY = {
2001
2296
  supabase_api:{ verb: 'supabase', icon: '◆' },
2002
2297
  supabase_sql:{ verb: 'sql', icon: '◆' },
2003
2298
  vercel_api:{ verb: 'vercel', icon: '▲' },
2299
+ publish_check:{ verb: 'check', icon: '▲' },
2300
+ publish_connect:{ verb: 'connect', icon: '▲' },
2301
+ supabase_setup:{ verb: 'database', icon: '◆' },
2302
+ supabase_schema:{ verb: 'schema', icon: '◆' },
2303
+ publish_deploy:{ verb: 'publish', icon: '▲' },
2304
+ publish_github:{ verb: 'push', icon: '⎇' },
2004
2305
  };
2005
2306
 
2006
2307
  // Plan and Ask are read-only. write_file/edit_file are also refused in
2007
2308
  // agent.mjs; this covers every other tool that changes something outside the
2008
2309
  // conversation, so read-only can't be escaped through a side door.
2009
2310
  const MUTATING_TOOLS = new Set([
2010
- 'write_file', 'edit_file', 'apply_patch', 'fetch_image', 'gmail_send', 'slack_post_message',
2311
+ 'write_file', 'edit_file', 'apply_patch', 'fetch_image', 'gmail_send', 'gmail_draft', 'calendar_add', 'slack_post_message',
2011
2312
  'vercel_deploy', 'supabase_create_project', 'supabase_delete_project', 'github_create_repo',
2313
+ 'supabase_setup', 'supabase_schema', 'publish_deploy', 'publish_github',
2012
2314
  ]);
2013
2315
 
2014
2316
  export async function executeTool(name, args, ctx) {