codeply-cli 0.5.6 → 0.6.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 (429) hide show
  1. package/LICENSE +21 -21
  2. package/THIRD_PARTY_NOTICES.md +65 -65
  3. package/bin/codeply.js +8 -3
  4. package/lib/agent.mjs +2596 -2268
  5. package/lib/ai.js +135 -27
  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/autopilot.js +84 -0
  12. package/lib/bot-mail.js +46 -0
  13. package/lib/bots.js +142 -15
  14. package/lib/brain.js +914 -0
  15. package/lib/call-assist.js +120 -0
  16. package/lib/chatgpt.js +282 -282
  17. package/lib/cloud-browser.mjs +143 -143
  18. package/lib/cloud.mjs +1396 -1320
  19. package/lib/commands.js +103 -103
  20. package/lib/computer.js +136 -0
  21. package/lib/design-library/assemble-artifact.mjs +21 -21
  22. package/lib/design-library/cleanup.mjs +45 -45
  23. package/lib/design-library/index.json +22379 -22379
  24. package/lib/design-library/make-table-data.mjs +39 -39
  25. package/lib/design-tools.mjs +173 -0
  26. package/lib/diagnostics.js +192 -192
  27. package/lib/dsh-acp.mjs +444 -0
  28. package/lib/edit-engine.js +364 -364
  29. package/lib/github-agent.mjs +326 -326
  30. package/lib/handoff.mjs +245 -0
  31. package/lib/history.js +78 -75
  32. package/lib/life-sources.js +319 -0
  33. package/lib/life-store.js +356 -0
  34. package/lib/life-triggers.js +286 -0
  35. package/lib/life.js +487 -0
  36. package/lib/mail-watch.js +3 -3
  37. package/lib/mcp.js +258 -258
  38. package/lib/native-tools.mjs +17 -5
  39. package/lib/oauth-connectors.js +76 -21
  40. package/lib/permissions.js +109 -109
  41. package/lib/plugins.js +356 -353
  42. package/lib/reach.js +67 -67
  43. package/lib/render-md.mjs +647 -617
  44. package/lib/screen.js +77 -0
  45. package/lib/server.mjs +458 -457
  46. package/lib/session-db.js +220 -220
  47. package/lib/share.js +208 -168
  48. package/lib/skills.js +4 -1
  49. package/lib/snapshot.js +146 -146
  50. package/lib/subagents.js +4 -2
  51. package/lib/telemetry.js +105 -105
  52. package/lib/terminal-hints.js +154 -154
  53. package/lib/textarea.mjs +264 -258
  54. package/lib/tools.mjs +326 -40
  55. package/lib/tui.mjs +1459 -1086
  56. package/lib/typecheck-worker.js +147 -147
  57. package/lib/web-tools.js +209 -209
  58. package/package.json +1 -1
  59. package/skills/SOURCE.md +58 -58
  60. package/skills/accessibility/SKILL.md +147 -147
  61. package/skills/agent-architecture-audit/SKILL.md +257 -257
  62. package/skills/agent-eval/SKILL.md +147 -147
  63. package/skills/agent-harness-construction/SKILL.md +74 -74
  64. package/skills/agent-introspection-debugging/SKILL.md +154 -154
  65. package/skills/agent-payment-x402/SKILL.md +225 -225
  66. package/skills/agent-self-evaluation/SKILL.md +182 -182
  67. package/skills/agent-self-evaluation/references/evaluation-criteria.md +71 -71
  68. package/skills/agent-self-evaluation/references/hook-integration.md +64 -64
  69. package/skills/agent-sort/SKILL.md +216 -216
  70. package/skills/agentic-engineering/SKILL.md +64 -64
  71. package/skills/agentic-os/SKILL.md +388 -388
  72. package/skills/ai-first-engineering/SKILL.md +52 -52
  73. package/skills/ai-regression-testing/SKILL.md +386 -386
  74. package/skills/android-clean-architecture/SKILL.md +340 -340
  75. package/skills/angular-developer/SKILL.md +155 -155
  76. package/skills/angular-developer/references/angular-animations.md +160 -160
  77. package/skills/angular-developer/references/angular-aria.md +410 -410
  78. package/skills/angular-developer/references/cli.md +86 -86
  79. package/skills/angular-developer/references/component-harnesses.md +59 -59
  80. package/skills/angular-developer/references/component-styling.md +91 -91
  81. package/skills/angular-developer/references/components.md +117 -117
  82. package/skills/angular-developer/references/creating-services.md +97 -97
  83. package/skills/angular-developer/references/data-resolvers.md +69 -69
  84. package/skills/angular-developer/references/define-routes.md +67 -67
  85. package/skills/angular-developer/references/defining-providers.md +72 -72
  86. package/skills/angular-developer/references/di-fundamentals.md +120 -120
  87. package/skills/angular-developer/references/e2e-testing.md +56 -56
  88. package/skills/angular-developer/references/effects.md +83 -83
  89. package/skills/angular-developer/references/hierarchical-injectors.md +43 -43
  90. package/skills/angular-developer/references/host-elements.md +80 -80
  91. package/skills/angular-developer/references/injection-context.md +63 -63
  92. package/skills/angular-developer/references/inputs.md +101 -101
  93. package/skills/angular-developer/references/linked-signal.md +59 -59
  94. package/skills/angular-developer/references/loading-strategies.md +61 -61
  95. package/skills/angular-developer/references/mcp.md +108 -108
  96. package/skills/angular-developer/references/navigate-to-routes.md +69 -69
  97. package/skills/angular-developer/references/outputs.md +86 -86
  98. package/skills/angular-developer/references/reactive-forms.md +122 -122
  99. package/skills/angular-developer/references/rendering-strategies.md +44 -44
  100. package/skills/angular-developer/references/resource.md +77 -77
  101. package/skills/angular-developer/references/route-animations.md +56 -56
  102. package/skills/angular-developer/references/route-guards.md +52 -52
  103. package/skills/angular-developer/references/router-lifecycle.md +45 -45
  104. package/skills/angular-developer/references/router-testing.md +87 -87
  105. package/skills/angular-developer/references/show-routes-with-outlets.md +68 -68
  106. package/skills/angular-developer/references/signal-forms.md +795 -795
  107. package/skills/angular-developer/references/signals-overview.md +94 -94
  108. package/skills/angular-developer/references/tailwind-css.md +69 -69
  109. package/skills/angular-developer/references/template-driven-forms.md +114 -114
  110. package/skills/angular-developer/references/testing-fundamentals.md +65 -65
  111. package/skills/api-connector-builder/SKILL.md +121 -121
  112. package/skills/api-design/SKILL.md +524 -524
  113. package/skills/architecture-decision-records/SKILL.md +180 -180
  114. package/skills/article-writing/SKILL.md +80 -80
  115. package/skills/automation-audit-ops/SKILL.md +143 -143
  116. package/skills/autonomous-agent-harness/SKILL.md +274 -274
  117. package/skills/autonomous-loops/SKILL.md +611 -611
  118. package/skills/backend-patterns/SKILL.md +562 -562
  119. package/skills/benchmark/SKILL.md +95 -95
  120. package/skills/benchmark-methodology/SKILL.md +191 -191
  121. package/skills/benchmark-optimization-loop/SKILL.md +71 -71
  122. package/skills/blender-motion-state-inspection/SKILL.md +165 -165
  123. package/skills/blueprint/SKILL.md +106 -106
  124. package/skills/brand-discovery/SKILL.md +145 -145
  125. package/skills/brand-discovery/references/10_purpose-why.md +40 -40
  126. package/skills/brand-discovery/references/20_positioning.md +44 -44
  127. package/skills/brand-discovery/references/30_audience-niche.md +52 -52
  128. package/skills/brand-discovery/references/40_personality-archetype.md +57 -57
  129. package/skills/brand-discovery/references/50_voice-tone.md +59 -59
  130. package/skills/brand-discovery/references/60_narrative-story.md +50 -50
  131. package/skills/brand-discovery/references/70_founder-tension.md +49 -49
  132. package/skills/brand-discovery/references/90_SYNTHESIS.md +133 -133
  133. package/skills/brand-voice/SKILL.md +98 -98
  134. package/skills/brand-voice/references/voice-profile-schema.md +55 -55
  135. package/skills/browser-qa/SKILL.md +105 -105
  136. package/skills/bun-runtime/SKILL.md +85 -85
  137. package/skills/canary-watch/SKILL.md +108 -108
  138. package/skills/carrier-relationship-management/SKILL.md +212 -212
  139. package/skills/cisco-ios-patterns/SKILL.md +164 -164
  140. package/skills/ck/SKILL.md +148 -148
  141. package/skills/claude-devfleet/SKILL.md +112 -112
  142. package/skills/click-path-audit/SKILL.md +245 -245
  143. package/skills/clickhouse-io/SKILL.md +445 -445
  144. package/skills/code-tour/SKILL.md +254 -254
  145. package/skills/codebase-onboarding/SKILL.md +234 -234
  146. package/skills/codehealth-mcp/SKILL.md +167 -167
  147. package/skills/codeply-taste/LICENSE +21 -0
  148. package/skills/codeply-taste/SKILL.md +100 -0
  149. package/skills/codeply-taste/SOURCE.md +10 -0
  150. package/skills/codeply-taste/assets/base.css +100 -0
  151. package/skills/codeply-taste/assets/base.js +37 -0
  152. package/skills/codeply-taste/library/directions.json +1078 -0
  153. package/skills/codeply-taste/library/heroes.json +894 -0
  154. package/skills/codeply-taste/library/motions.json +57 -0
  155. package/skills/codeply-taste/library/palettes.json +1485 -0
  156. package/skills/codeply-taste/library/photos.json +7240 -0
  157. package/skills/codeply-taste/library/sections.json +4136 -0
  158. package/skills/codeply-taste/library/sites.json +7274 -0
  159. package/skills/codeply-taste/library/type.json +2024 -0
  160. package/skills/codeply-taste/references/colour.md +141 -0
  161. package/skills/codeply-taste/references/copy.md +177 -0
  162. package/skills/codeply-taste/references/critique.md +171 -0
  163. package/skills/codeply-taste/references/imagery.md +377 -0
  164. package/skills/codeply-taste/references/layout.md +178 -0
  165. package/skills/codeply-taste/references/motion.md +292 -0
  166. package/skills/codeply-taste/references/research.md +306 -0
  167. package/skills/codeply-taste/references/tells.md +115 -0
  168. package/skills/codeply-taste/references/type.md +170 -0
  169. package/skills/codeply-taste/scripts/assets.mjs +865 -0
  170. package/skills/codeply-taste/scripts/draw.mjs +525 -0
  171. package/skills/codeply-taste/scripts/inspect.mjs +205 -0
  172. package/skills/codeply-taste/scripts/lib/cdp.mjs +205 -0
  173. package/skills/codeply-taste/scripts/lib/probe.mjs +569 -0
  174. package/skills/codeply-taste/scripts/lib/rules.mjs +137 -0
  175. package/skills/codeply-taste/scripts/lint.mjs +108 -0
  176. package/skills/coding-standards/SKILL.md +551 -551
  177. package/skills/competitive-platform-analysis/SKILL.md +214 -214
  178. package/skills/competitive-report-structure/SKILL.md +162 -162
  179. package/skills/compose-multiplatform-patterns/SKILL.md +300 -300
  180. package/skills/config-gc/SKILL.md +120 -120
  181. package/skills/configure-ecc/SKILL.md +385 -385
  182. package/skills/connections-optimizer/SKILL.md +190 -190
  183. package/skills/content-engine/SKILL.md +132 -132
  184. package/skills/content-hash-cache-pattern/SKILL.md +162 -162
  185. package/skills/context-budget/SKILL.md +136 -136
  186. package/skills/continuous-agent-loop/SKILL.md +46 -46
  187. package/skills/continuous-learning/SKILL.md +132 -132
  188. package/skills/continuous-learning-v2/SKILL.md +361 -361
  189. package/skills/contract-first/SKILL.md +287 -287
  190. package/skills/cost-aware-llm-pipeline/SKILL.md +184 -184
  191. package/skills/cost-tracking/SKILL.md +97 -97
  192. package/skills/council/SKILL.md +204 -204
  193. package/skills/cpp-coding-standards/SKILL.md +724 -724
  194. package/skills/cpp-testing/SKILL.md +325 -325
  195. package/skills/crosspost/SKILL.md +112 -112
  196. package/skills/csharp-testing/SKILL.md +322 -322
  197. package/skills/customer-billing-ops/SKILL.md +141 -141
  198. package/skills/customs-trade-compliance/SKILL.md +263 -263
  199. package/skills/dart-flutter-patterns/SKILL.md +564 -564
  200. package/skills/dashboard-builder/SKILL.md +109 -109
  201. package/skills/data-scraper-agent/SKILL.md +765 -765
  202. package/skills/data-throughput-accelerator/SKILL.md +74 -74
  203. package/skills/database-migrations/SKILL.md +430 -430
  204. package/skills/deep-research/SKILL.md +160 -160
  205. package/skills/defi-amm-security/SKILL.md +167 -167
  206. package/skills/delivery-gate/SKILL.md +126 -126
  207. package/skills/deployment-patterns/SKILL.md +428 -428
  208. package/skills/design-system/SKILL.md +83 -83
  209. package/skills/django-celery/SKILL.md +458 -458
  210. package/skills/django-patterns/SKILL.md +735 -735
  211. package/skills/django-security/SKILL.md +644 -644
  212. package/skills/django-tdd/SKILL.md +730 -730
  213. package/skills/django-verification/SKILL.md +470 -470
  214. package/skills/dmux-workflows/SKILL.md +192 -192
  215. package/skills/docker-patterns/SKILL.md +365 -365
  216. package/skills/documentation-lookup/SKILL.md +91 -91
  217. package/skills/dotnet-patterns/SKILL.md +322 -322
  218. package/skills/dynamic-workflow-mode/SKILL.md +124 -124
  219. package/skills/e2e-testing/SKILL.md +327 -327
  220. package/skills/ecc-guide/SKILL.md +190 -190
  221. package/skills/ecc-recipes/SKILL.md +149 -149
  222. package/skills/ecc-tools-cost-audit/SKILL.md +161 -161
  223. package/skills/email-ops/SKILL.md +122 -122
  224. package/skills/energy-procurement/SKILL.md +228 -228
  225. package/skills/enterprise-agent-ops/SKILL.md +51 -51
  226. package/skills/error-handling/SKILL.md +377 -377
  227. package/skills/eval-harness/SKILL.md +271 -271
  228. package/skills/evm-token-decimals/SKILL.md +131 -131
  229. package/skills/exa-search/SKILL.md +108 -108
  230. package/skills/fal-ai-media/SKILL.md +289 -289
  231. package/skills/fastapi-patterns/SKILL.md +514 -514
  232. package/skills/finance-billing-ops/SKILL.md +128 -128
  233. package/skills/flox-environments/SKILL.md +497 -497
  234. package/skills/flutter-dart-code-review/SKILL.md +436 -436
  235. package/skills/foundation-models-on-device/SKILL.md +243 -243
  236. package/skills/frontend-a11y/SKILL.md +446 -446
  237. package/skills/frontend-design-direction/SKILL.md +93 -93
  238. package/skills/frontend-patterns/SKILL.md +657 -657
  239. package/skills/frontend-slides/SKILL.md +185 -185
  240. package/skills/fsharp-testing/SKILL.md +281 -281
  241. package/skills/gan-style-harness/SKILL.md +279 -279
  242. package/skills/gateguard/SKILL.md +133 -133
  243. package/skills/generating-python-installer/SKILL.md +820 -820
  244. package/skills/git-workflow/SKILL.md +716 -716
  245. package/skills/github-ops/SKILL.md +145 -145
  246. package/skills/golang-patterns/SKILL.md +676 -676
  247. package/skills/golang-testing/SKILL.md +721 -721
  248. package/skills/google-workspace-ops/SKILL.md +96 -96
  249. package/skills/growth-log/SKILL.md +128 -128
  250. package/skills/healthcare-cdss-patterns/SKILL.md +246 -246
  251. package/skills/healthcare-emr-patterns/SKILL.md +160 -160
  252. package/skills/healthcare-eval-harness/SKILL.md +208 -208
  253. package/skills/healthcare-phi-compliance/SKILL.md +146 -146
  254. package/skills/hermes-imports/SKILL.md +89 -89
  255. package/skills/hexagonal-architecture/SKILL.md +277 -277
  256. package/skills/hipaa-compliance/SKILL.md +79 -79
  257. package/skills/homelab-network-readiness/SKILL.md +170 -170
  258. package/skills/homelab-network-setup/SKILL.md +130 -130
  259. package/skills/homelab-pihole-dns/SKILL.md +275 -275
  260. package/skills/homelab-vlan-segmentation/SKILL.md +312 -312
  261. package/skills/homelab-wireguard-vpn/SKILL.md +306 -306
  262. package/skills/hookify-rules/SKILL.md +128 -128
  263. package/skills/inherit-legacy-style/SKILL.md +157 -157
  264. package/skills/intent-driven-development/SKILL.md +360 -360
  265. package/skills/inventory-demand-planning/SKILL.md +247 -247
  266. package/skills/investor-materials/SKILL.md +97 -97
  267. package/skills/investor-outreach/SKILL.md +92 -92
  268. package/skills/ios-icon-gen/SKILL.md +158 -158
  269. package/skills/iterative-retrieval/SKILL.md +212 -212
  270. package/skills/ito-basket-compare/SKILL.md +64 -64
  271. package/skills/ito-compute/SKILL.md +129 -129
  272. package/skills/ito-data-atlas-agent/SKILL.md +64 -64
  273. package/skills/ito-market-intelligence/SKILL.md +61 -61
  274. package/skills/ito-trade-planner/SKILL.md +68 -68
  275. package/skills/java-coding-standards/SKILL.md +384 -384
  276. package/skills/jira-integration/SKILL.md +303 -303
  277. package/skills/jpa-patterns/SKILL.md +152 -152
  278. package/skills/knowledge-ops/SKILL.md +155 -155
  279. package/skills/kotlin-coroutines-flows/SKILL.md +285 -285
  280. package/skills/kotlin-exposed-patterns/SKILL.md +720 -720
  281. package/skills/kotlin-ktor-patterns/SKILL.md +690 -690
  282. package/skills/kotlin-patterns/SKILL.md +712 -712
  283. package/skills/kotlin-testing/SKILL.md +825 -825
  284. package/skills/kubernetes-patterns/SKILL.md +756 -756
  285. package/skills/laravel-patterns/SKILL.md +416 -416
  286. package/skills/laravel-plugin-discovery/SKILL.md +230 -230
  287. package/skills/laravel-security/SKILL.md +948 -948
  288. package/skills/laravel-tdd/SKILL.md +675 -675
  289. package/skills/laravel-verification/SKILL.md +180 -180
  290. package/skills/latency-critical-systems/SKILL.md +75 -75
  291. package/skills/lead-intelligence/SKILL.md +322 -322
  292. package/skills/liquid-glass-design/SKILL.md +279 -279
  293. package/skills/llm-trading-agent-security/SKILL.md +147 -147
  294. package/skills/logistics-exception-management/SKILL.md +222 -222
  295. package/skills/loop-design-check/SKILL.md +143 -143
  296. package/skills/mailtrap-email-integration/SKILL.md +77 -77
  297. package/skills/make-interfaces-feel-better/SKILL.md +152 -152
  298. package/skills/manim-video/SKILL.md +90 -90
  299. package/skills/market-research/SKILL.md +76 -76
  300. package/skills/marketing-campaign/SKILL.md +114 -114
  301. package/skills/mcp-server-patterns/SKILL.md +70 -70
  302. package/skills/messages-ops/SKILL.md +105 -105
  303. package/skills/ml-adoption-playbook/SKILL.md +57 -57
  304. package/skills/mle-workflow/SKILL.md +348 -348
  305. package/skills/motion-advanced/SKILL.md +596 -596
  306. package/skills/motion-foundations/SKILL.md +299 -299
  307. package/skills/motion-patterns/SKILL.md +434 -434
  308. package/skills/motion-ui/SKILL.md +576 -576
  309. package/skills/mysql-patterns/SKILL.md +413 -413
  310. package/skills/nanoclaw-repl/SKILL.md +34 -34
  311. package/skills/nestjs-patterns/SKILL.md +231 -231
  312. package/skills/netmiko-ssh-automation/SKILL.md +174 -174
  313. package/skills/network-bgp-diagnostics/SKILL.md +168 -168
  314. package/skills/network-config-validation/SKILL.md +211 -211
  315. package/skills/network-interface-health/SKILL.md +153 -153
  316. package/skills/nextjs-turbopack/SKILL.md +58 -58
  317. package/skills/nodejs-keccak256/SKILL.md +103 -103
  318. package/skills/nutrient-document-processing/SKILL.md +168 -168
  319. package/skills/nuxt4-patterns/SKILL.md +101 -101
  320. package/skills/openclaw-persona-forge/SKILL.md +289 -289
  321. package/skills/openclaw-persona-forge/references/avatar-style.md +124 -124
  322. package/skills/openclaw-persona-forge/references/boundary-rules.md +53 -53
  323. package/skills/openclaw-persona-forge/references/error-handling.md +53 -53
  324. package/skills/openclaw-persona-forge/references/identity-tension.md +48 -48
  325. package/skills/openclaw-persona-forge/references/naming-system.md +39 -39
  326. package/skills/openclaw-persona-forge/references/output-template.md +166 -166
  327. package/skills/opensource-pipeline/SKILL.md +256 -256
  328. package/skills/orch-add-feature/SKILL.md +45 -45
  329. package/skills/orch-build-mvp/SKILL.md +49 -49
  330. package/skills/orch-change-feature/SKILL.md +43 -43
  331. package/skills/orch-fix-defect/SKILL.md +43 -43
  332. package/skills/orch-pipeline/SKILL.md +121 -121
  333. package/skills/orch-refine-code/SKILL.md +44 -44
  334. package/skills/parallel-execution-optimizer/SKILL.md +74 -74
  335. package/skills/perl-patterns/SKILL.md +505 -505
  336. package/skills/perl-security/SKILL.md +504 -504
  337. package/skills/perl-testing/SKILL.md +476 -476
  338. package/skills/plan-canvas/SKILL.md +153 -153
  339. package/skills/plan-orchestrate/SKILL.md +263 -263
  340. package/skills/plankton-code-quality/SKILL.md +237 -237
  341. package/skills/postgres-patterns/SKILL.md +148 -148
  342. package/skills/prediction-market-oracle-research/SKILL.md +64 -64
  343. package/skills/prediction-market-risk-review/SKILL.md +61 -61
  344. package/skills/premium-web-design/SKILL.md +154 -154
  345. package/skills/prisma-patterns/SKILL.md +401 -401
  346. package/skills/product-capability/SKILL.md +142 -142
  347. package/skills/product-lens/SKILL.md +93 -93
  348. package/skills/production-audit/SKILL.md +207 -207
  349. package/skills/production-scheduling/SKILL.md +238 -238
  350. package/skills/project-flow-ops/SKILL.md +112 -112
  351. package/skills/prompt-optimizer/SKILL.md +398 -398
  352. package/skills/python-patterns/SKILL.md +751 -751
  353. package/skills/python-testing/SKILL.md +817 -817
  354. package/skills/pytorch-patterns/SKILL.md +397 -397
  355. package/skills/quality-nonconformance/SKILL.md +260 -260
  356. package/skills/quarkus-patterns/SKILL.md +723 -723
  357. package/skills/quarkus-security/SKILL.md +468 -468
  358. package/skills/quarkus-tdd/SKILL.md +812 -812
  359. package/skills/quarkus-verification/SKILL.md +480 -480
  360. package/skills/ralphinho-rfc-pipeline/SKILL.md +68 -68
  361. package/skills/react-native-patterns/SKILL.md +326 -326
  362. package/skills/react-patterns/SKILL.md +342 -342
  363. package/skills/react-performance/SKILL.md +575 -575
  364. package/skills/react-testing/SKILL.md +424 -424
  365. package/skills/recsys-pipeline-architect/SKILL.md +115 -115
  366. package/skills/recursive-decision-ledger/SKILL.md +81 -81
  367. package/skills/redis-patterns/SKILL.md +404 -404
  368. package/skills/regex-vs-llm-structured-text/SKILL.md +221 -221
  369. package/skills/remotion-video-creation/SKILL.md +43 -43
  370. package/skills/repo-scan/SKILL.md +79 -79
  371. package/skills/research-ops/SKILL.md +113 -113
  372. package/skills/returns-reverse-logistics/SKILL.md +240 -240
  373. package/skills/rules-distill/SKILL.md +265 -265
  374. package/skills/rust-patterns/SKILL.md +500 -500
  375. package/skills/rust-testing/SKILL.md +501 -501
  376. package/skills/safety-guard/SKILL.md +76 -76
  377. package/skills/santa-method/SKILL.md +307 -307
  378. package/skills/scientific-db-pubmed-database/SKILL.md +176 -176
  379. package/skills/scientific-db-uspto-database/SKILL.md +178 -178
  380. package/skills/scientific-pkg-gget/SKILL.md +167 -167
  381. package/skills/scientific-thinking-literature-review/SKILL.md +193 -193
  382. package/skills/scientific-thinking-scholar-evaluation/SKILL.md +161 -161
  383. package/skills/search-first/SKILL.md +183 -183
  384. package/skills/security-bounty-hunter/SKILL.md +100 -100
  385. package/skills/security-review/SKILL.md +504 -504
  386. package/skills/security-scan/SKILL.md +166 -166
  387. package/skills/seo/SKILL.md +155 -155
  388. package/skills/skill-comply/SKILL.md +59 -59
  389. package/skills/skill-scout/SKILL.md +141 -141
  390. package/skills/skill-stocktake/SKILL.md +195 -195
  391. package/skills/social-graph-ranker/SKILL.md +155 -155
  392. package/skills/social-publisher/SKILL.md +130 -130
  393. package/skills/springboot-patterns/SKILL.md +315 -315
  394. package/skills/springboot-security/SKILL.md +273 -273
  395. package/skills/springboot-tdd/SKILL.md +159 -159
  396. package/skills/springboot-verification/SKILL.md +232 -232
  397. package/skills/strategic-compact/SKILL.md +142 -142
  398. package/skills/swift-actor-persistence/SKILL.md +144 -144
  399. package/skills/swift-concurrency-6-2/SKILL.md +216 -216
  400. package/skills/swift-protocol-di-testing/SKILL.md +191 -191
  401. package/skills/swiftui-patterns/SKILL.md +259 -259
  402. package/skills/taste/SKILL.md +264 -264
  403. package/skills/taste/references/genre-taxonomy.md +87 -87
  404. package/skills/tdd-workflow/SKILL.md +583 -583
  405. package/skills/team-agent-orchestration/SKILL.md +111 -111
  406. package/skills/team-builder/SKILL.md +169 -169
  407. package/skills/terminal-ops/SKILL.md +110 -110
  408. package/skills/tinystruct-patterns/SKILL.md +279 -279
  409. package/skills/tinystruct-patterns/references/architecture.md +90 -90
  410. package/skills/tinystruct-patterns/references/data-handling.md +60 -60
  411. package/skills/tinystruct-patterns/references/database.md +99 -99
  412. package/skills/tinystruct-patterns/references/routing.md +64 -64
  413. package/skills/tinystruct-patterns/references/system-usage.md +97 -97
  414. package/skills/tinystruct-patterns/references/testing.md +72 -72
  415. package/skills/token-budget-advisor/SKILL.md +134 -134
  416. package/skills/ui-demo/SKILL.md +466 -466
  417. package/skills/ui-to-vue/SKILL.md +135 -135
  418. package/skills/uncloud/SKILL.md +344 -344
  419. package/skills/unified-memory/SKILL.md +170 -170
  420. package/skills/unified-notifications-ops/SKILL.md +188 -188
  421. package/skills/verification-loop/SKILL.md +129 -129
  422. package/skills/video-editing/SKILL.md +311 -311
  423. package/skills/videodb/SKILL.md +375 -375
  424. package/skills/visa-doc-translate/SKILL.md +117 -117
  425. package/skills/vite-patterns/SKILL.md +450 -450
  426. package/skills/vue-patterns/SKILL.md +471 -471
  427. package/skills/windows-desktop-e2e/SKILL.md +888 -888
  428. package/skills/workspace-surface-audit/SKILL.md +126 -126
  429. package/skills/x-api/SKILL.md +235 -235
package/lib/mcp.js CHANGED
@@ -1,258 +1,258 @@
1
- /**
2
- * A small Model Context Protocol client: enough to connect to MCP servers,
3
- * list their tools and call them. No SDK dependency; MCP is JSON-RPC 2.0
4
- * over either a child process's stdio (one JSON message per line) or HTTP
5
- * ("streamable HTTP": POST a request, get JSON or an SSE stream back).
6
- *
7
- * Servers are configured in the same shape Claude Desktop / Cursor use, so an
8
- * existing config can be pasted in:
9
- *
10
- * ~/.codeply/mcp.json (every project)
11
- * <project>/.codeply/mcp.json (this project; same name wins)
12
- *
13
- * { "mcpServers": {
14
- * "github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { "GITHUB_TOKEN": "..." } },
15
- * "docs": { "url": "https://example.com/mcp", "headers": { "Authorization": "Bearer ..." } },
16
- * "old": { "command": "...", "disabled": true } } }
17
- *
18
- * Connections are kept for the life of the process and reused across turns.
19
- */
20
- const fs = require('fs');
21
- const os = require('os');
22
- const path = require('path');
23
- const { spawn } = require('child_process');
24
- const plugins = require('./plugins.js');
25
-
26
- const PROTOCOL_VERSION = '2025-06-18';
27
- const CONNECT_TIMEOUT_MS = 20000;
28
- const CALL_TIMEOUT_MS = 120000;
29
- const CLIENT_INFO = { name: 'codeply', version: '1.0' };
30
-
31
- function readConfig(file) {
32
- try {
33
- const j = JSON.parse(fs.readFileSync(file, 'utf8'));
34
- return j.mcpServers || j.servers || {};
35
- } catch { return {}; }
36
- }
37
-
38
- /** Merged server config for a project: project entries override global ones. */
39
- function loadServers(cwd) {
40
- const merged = { ...plugins.mcpServers(cwd), ...readConfig(path.join(os.homedir(), '.codeply', 'mcp.json')) };
41
- if (cwd) Object.assign(merged, readConfig(path.join(cwd, '.codeply', 'mcp.json')));
42
- return Object.fromEntries(Object.entries(merged).filter(([, s]) => s && !s.disabled && (s.command || s.url)));
43
- }
44
-
45
- // ─── Transports ─────────────────────────────────────────────────────────────
46
-
47
- class StdioConnection {
48
- constructor(name, spec, cwd) {
49
- this.name = name;
50
- this.pending = new Map();
51
- this.nextId = 1;
52
- this.stderr = '';
53
- // npx/npm/uvx are .cmd shims on Windows and only start through a shell.
54
- // A full path to a program (which may contain spaces) is started
55
- // directly. Through the shell, arguments are quoted here, since Node
56
- // just joins them with spaces.
57
- const cmd = String(spec.command);
58
- const args = (spec.args || []).map(String);
59
- const viaShell = process.platform === 'win32' && !/[\\/]/.test(cmd) && !/\.exe$/i.test(cmd);
60
- const quote = (a) => (/[\s"&|<>^()]/.test(a) ? `"${a.replace(/"/g, '\\"')}"` : a);
61
- this.proc = spawn(viaShell ? [cmd, ...args].map(quote).join(' ') : cmd, viaShell ? [] : args, {
62
- cwd: spec.cwd || cwd || process.cwd(),
63
- env: { ...process.env, ...(spec.env || {}) },
64
- stdio: ['pipe', 'pipe', 'pipe'],
65
- windowsHide: true,
66
- shell: viaShell,
67
- });
68
- let buf = '';
69
- this.proc.stdout.on('data', (d) => {
70
- buf += d.toString('utf8');
71
- let nl;
72
- while ((nl = buf.indexOf('\n')) !== -1) {
73
- const line = buf.slice(0, nl).trim();
74
- buf = buf.slice(nl + 1);
75
- if (!line) continue;
76
- let msg;
77
- try { msg = JSON.parse(line); } catch { continue; } // servers sometimes log to stdout
78
- this.onMessage(msg);
79
- }
80
- });
81
- this.proc.stderr.on('data', (d) => { this.stderr = (this.stderr + d.toString('utf8')).slice(-4000); });
82
- this.proc.on('error', (e) => this.failAll(`could not start: ${e.message}`));
83
- this.proc.on('exit', (code) => { this.closed = true; this.failAll(`exited (code ${code})${this.stderr ? `: ${this.stderr.trim().split('\n').slice(-3).join(' ')}` : ''}`); });
84
- }
85
-
86
- onMessage(msg) {
87
- if (msg.id !== undefined && this.pending.has(msg.id)) {
88
- const p = this.pending.get(msg.id);
89
- this.pending.delete(msg.id);
90
- if (msg.error) p.reject(new Error(msg.error.message || JSON.stringify(msg.error)));
91
- else p.resolve(msg.result);
92
- } else if (msg.method && msg.id !== undefined) {
93
- // A request from the server (sampling, roots...): we offer none of those.
94
- this.write({ jsonrpc: '2.0', id: msg.id, error: { code: -32601, message: 'Not supported by this client' } });
95
- }
96
- }
97
-
98
- write(obj) {
99
- if (!this.closed) this.proc.stdin.write(JSON.stringify(obj) + '\n');
100
- }
101
-
102
- request(method, params, timeoutMs) {
103
- return new Promise((resolve, reject) => {
104
- if (this.closed) return reject(new Error(`${this.name} is not running`));
105
- const id = this.nextId++;
106
- const timer = setTimeout(() => { this.pending.delete(id); reject(new Error(`${method} timed out after ${timeoutMs / 1000}s`)); }, timeoutMs);
107
- this.pending.set(id, { resolve: (v) => { clearTimeout(timer); resolve(v); }, reject: (e) => { clearTimeout(timer); reject(e); } });
108
- this.write({ jsonrpc: '2.0', id, method, params });
109
- });
110
- }
111
-
112
- notify(method, params) { this.write({ jsonrpc: '2.0', method, ...(params ? { params } : {}) }); }
113
-
114
- failAll(why) {
115
- for (const [, p] of this.pending) p.reject(new Error(`${this.name} ${why}`));
116
- this.pending.clear();
117
- }
118
-
119
- close() { try { this.proc.kill(); } catch {} }
120
- }
121
-
122
- class HttpConnection {
123
- constructor(name, spec) {
124
- this.name = name;
125
- this.url = spec.url;
126
- this.headers = spec.headers || {};
127
- this.sessionId = null;
128
- this.nextId = 1;
129
- }
130
-
131
- async post(body, timeoutMs) {
132
- const res = await fetch(this.url, {
133
- method: 'POST',
134
- headers: {
135
- 'Content-Type': 'application/json',
136
- Accept: 'application/json, text/event-stream',
137
- 'MCP-Protocol-Version': PROTOCOL_VERSION,
138
- ...(this.sessionId ? { 'Mcp-Session-Id': this.sessionId } : {}),
139
- ...this.headers,
140
- },
141
- body: JSON.stringify(body),
142
- signal: AbortSignal.timeout(timeoutMs),
143
- });
144
- const sid = res.headers.get('mcp-session-id');
145
- if (sid) this.sessionId = sid;
146
- return res;
147
- }
148
-
149
- async request(method, params, timeoutMs) {
150
- const id = this.nextId++;
151
- const res = await this.post({ jsonrpc: '2.0', id, method, params }, timeoutMs);
152
- if (!res.ok) throw new Error(`${method}: HTTP ${res.status}${res.status === 401 ? ' (needs authorization - add a token under "headers")' : ''}`);
153
- const type = res.headers.get('content-type') || '';
154
- let msg = null;
155
- if (type.includes('text/event-stream')) {
156
- // Read events until the one answering our id.
157
- const text = await res.text();
158
- for (const block of text.split(/\r?\n\r?\n/)) {
159
- const data = block.split(/\r?\n/).filter((l) => l.startsWith('data:')).map((l) => l.slice(5).trim()).join('\n');
160
- if (!data) continue;
161
- try { const m = JSON.parse(data); if (m.id === id) { msg = m; break; } } catch {}
162
- }
163
- } else {
164
- msg = await res.json().catch(() => null);
165
- }
166
- if (!msg) throw new Error(`${method}: no response`);
167
- if (msg.error) throw new Error(msg.error.message || JSON.stringify(msg.error));
168
- return msg.result;
169
- }
170
-
171
- async notify(method, params) {
172
- try { await this.post({ jsonrpc: '2.0', method, ...(params ? { params } : {}) }, 10000); } catch {}
173
- }
174
-
175
- close() {}
176
- }
177
-
178
- // ─── Servers ────────────────────────────────────────────────────────────────
179
-
180
- const servers = new Map(); // key -> { name, conn, tools, error, specKey }
181
-
182
- function specKey(spec) { return JSON.stringify(spec); }
183
-
184
- async function connect(name, spec, cwd) {
185
- const conn = spec.url ? new HttpConnection(name, spec) : new StdioConnection(name, spec, cwd);
186
- try {
187
- const init = await conn.request('initialize', { protocolVersion: PROTOCOL_VERSION, capabilities: {}, clientInfo: CLIENT_INFO }, CONNECT_TIMEOUT_MS);
188
- await conn.notify('notifications/initialized');
189
- const tools = [];
190
- let cursor;
191
- do {
192
- const page = await conn.request('tools/list', cursor ? { cursor } : {}, CONNECT_TIMEOUT_MS);
193
- tools.push(...(page.tools || []));
194
- cursor = page.nextCursor;
195
- } while (cursor && tools.length < 500);
196
- return { name, conn, tools, instructions: init?.instructions || '', error: null };
197
- } catch (e) {
198
- conn.close();
199
- return { name, conn: null, tools: [], instructions: '', error: e.message };
200
- }
201
- }
202
-
203
- /**
204
- * Connect to every configured server for this project (reusing live
205
- * connections) and return what they offer. Never throws.
206
- * @returns {Promise<Array<{name, tools, error, instructions}>>}
207
- */
208
- async function listServers(cwd) {
209
- const wanted = loadServers(cwd);
210
- const out = [];
211
- await Promise.all(Object.entries(wanted).map(async ([name, spec]) => {
212
- const key = `${cwd}::${name}`;
213
- let s = servers.get(key);
214
- const stale = s && (s.specKey !== specKey(spec) || (s.conn && s.conn.closed) || s.error);
215
- if (!s || stale) {
216
- if (s?.conn) s.conn.close();
217
- s = { ...(await connect(name, spec, cwd)), specKey: specKey(spec) };
218
- servers.set(key, s);
219
- }
220
- out.push({ name, tools: s.tools, error: s.error, instructions: s.instructions });
221
- }));
222
- return out.sort((a, b) => a.name.localeCompare(b.name));
223
- }
224
-
225
- /** Tool results as plain text for the model, images kept aside as data URLs. */
226
- function flattenResult(result) {
227
- const parts = Array.isArray(result?.content) ? result.content : [];
228
- const text = [];
229
- const images = [];
230
- for (const p of parts) {
231
- if (p.type === 'text') text.push(p.text);
232
- else if (p.type === 'image' && p.data) images.push(`data:${p.mimeType || 'image/png'};base64,${p.data}`);
233
- else if (p.type === 'resource') text.push(p.resource?.text || `[resource ${p.resource?.uri || ''}]`);
234
- else text.push(`[${p.type} content]`);
235
- }
236
- if (!parts.length && result?.structuredContent) text.push(JSON.stringify(result.structuredContent, null, 1));
237
- return { text: text.join('\n') || '(empty result)', images, isError: !!result?.isError };
238
- }
239
-
240
- async function callTool(cwd, serverName, toolName, args) {
241
- const s = servers.get(`${cwd}::${serverName}`);
242
- if (!s || !s.conn) throw new Error(`MCP server "${serverName}" is not connected${s?.error ? ` (${s.error})` : ''}.`);
243
- const result = await s.conn.request('tools/call', { name: toolName, arguments: args || {} }, CALL_TIMEOUT_MS);
244
- return flattenResult(result);
245
- }
246
-
247
- function findTool(cwd, serverName, toolName) {
248
- const s = servers.get(`${cwd}::${serverName}`);
249
- return s ? s.tools.find((t) => t.name === toolName) || null : null;
250
- }
251
-
252
- function closeAll() {
253
- for (const [, s] of servers) if (s.conn) s.conn.close();
254
- servers.clear();
255
- }
256
- process.on('exit', closeAll);
257
-
258
- module.exports = { listServers, callTool, findTool, loadServers, closeAll, flattenResult };
1
+ /**
2
+ * A small Model Context Protocol client: enough to connect to MCP servers,
3
+ * list their tools and call them. No SDK dependency; MCP is JSON-RPC 2.0
4
+ * over either a child process's stdio (one JSON message per line) or HTTP
5
+ * ("streamable HTTP": POST a request, get JSON or an SSE stream back).
6
+ *
7
+ * Servers are configured in the same shape Claude Desktop / Cursor use, so an
8
+ * existing config can be pasted in:
9
+ *
10
+ * ~/.codeply/mcp.json (every project)
11
+ * <project>/.codeply/mcp.json (this project; same name wins)
12
+ *
13
+ * { "mcpServers": {
14
+ * "github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { "GITHUB_TOKEN": "..." } },
15
+ * "docs": { "url": "https://example.com/mcp", "headers": { "Authorization": "Bearer ..." } },
16
+ * "old": { "command": "...", "disabled": true } } }
17
+ *
18
+ * Connections are kept for the life of the process and reused across turns.
19
+ */
20
+ const fs = require('fs');
21
+ const os = require('os');
22
+ const path = require('path');
23
+ const { spawn } = require('child_process');
24
+ const plugins = require('./plugins.js');
25
+
26
+ const PROTOCOL_VERSION = '2025-06-18';
27
+ const CONNECT_TIMEOUT_MS = 20000;
28
+ const CALL_TIMEOUT_MS = 120000;
29
+ const CLIENT_INFO = { name: 'codeply', version: '1.0' };
30
+
31
+ function readConfig(file) {
32
+ try {
33
+ const j = JSON.parse(fs.readFileSync(file, 'utf8'));
34
+ return j.mcpServers || j.servers || {};
35
+ } catch { return {}; }
36
+ }
37
+
38
+ /** Merged server config for a project: project entries override global ones. */
39
+ function loadServers(cwd) {
40
+ const merged = { ...plugins.mcpServers(cwd), ...readConfig(path.join(os.homedir(), '.codeply', 'mcp.json')) };
41
+ if (cwd) Object.assign(merged, readConfig(path.join(cwd, '.codeply', 'mcp.json')));
42
+ return Object.fromEntries(Object.entries(merged).filter(([, s]) => s && !s.disabled && (s.command || s.url)));
43
+ }
44
+
45
+ // ─── Transports ─────────────────────────────────────────────────────────────
46
+
47
+ class StdioConnection {
48
+ constructor(name, spec, cwd) {
49
+ this.name = name;
50
+ this.pending = new Map();
51
+ this.nextId = 1;
52
+ this.stderr = '';
53
+ // npx/npm/uvx are .cmd shims on Windows and only start through a shell.
54
+ // A full path to a program (which may contain spaces) is started
55
+ // directly. Through the shell, arguments are quoted here, since Node
56
+ // just joins them with spaces.
57
+ const cmd = String(spec.command);
58
+ const args = (spec.args || []).map(String);
59
+ const viaShell = process.platform === 'win32' && !/[\\/]/.test(cmd) && !/\.exe$/i.test(cmd);
60
+ const quote = (a) => (/[\s"&|<>^()]/.test(a) ? `"${a.replace(/"/g, '\\"')}"` : a);
61
+ this.proc = spawn(viaShell ? [cmd, ...args].map(quote).join(' ') : cmd, viaShell ? [] : args, {
62
+ cwd: spec.cwd || cwd || process.cwd(),
63
+ env: { ...process.env, ...(spec.env || {}) },
64
+ stdio: ['pipe', 'pipe', 'pipe'],
65
+ windowsHide: true,
66
+ shell: viaShell,
67
+ });
68
+ let buf = '';
69
+ this.proc.stdout.on('data', (d) => {
70
+ buf += d.toString('utf8');
71
+ let nl;
72
+ while ((nl = buf.indexOf('\n')) !== -1) {
73
+ const line = buf.slice(0, nl).trim();
74
+ buf = buf.slice(nl + 1);
75
+ if (!line) continue;
76
+ let msg;
77
+ try { msg = JSON.parse(line); } catch { continue; } // servers sometimes log to stdout
78
+ this.onMessage(msg);
79
+ }
80
+ });
81
+ this.proc.stderr.on('data', (d) => { this.stderr = (this.stderr + d.toString('utf8')).slice(-4000); });
82
+ this.proc.on('error', (e) => this.failAll(`could not start: ${e.message}`));
83
+ this.proc.on('exit', (code) => { this.closed = true; this.failAll(`exited (code ${code})${this.stderr ? `: ${this.stderr.trim().split('\n').slice(-3).join(' ')}` : ''}`); });
84
+ }
85
+
86
+ onMessage(msg) {
87
+ if (msg.id !== undefined && this.pending.has(msg.id)) {
88
+ const p = this.pending.get(msg.id);
89
+ this.pending.delete(msg.id);
90
+ if (msg.error) p.reject(new Error(msg.error.message || JSON.stringify(msg.error)));
91
+ else p.resolve(msg.result);
92
+ } else if (msg.method && msg.id !== undefined) {
93
+ // A request from the server (sampling, roots...): we offer none of those.
94
+ this.write({ jsonrpc: '2.0', id: msg.id, error: { code: -32601, message: 'Not supported by this client' } });
95
+ }
96
+ }
97
+
98
+ write(obj) {
99
+ if (!this.closed) this.proc.stdin.write(JSON.stringify(obj) + '\n');
100
+ }
101
+
102
+ request(method, params, timeoutMs) {
103
+ return new Promise((resolve, reject) => {
104
+ if (this.closed) return reject(new Error(`${this.name} is not running`));
105
+ const id = this.nextId++;
106
+ const timer = setTimeout(() => { this.pending.delete(id); reject(new Error(`${method} timed out after ${timeoutMs / 1000}s`)); }, timeoutMs);
107
+ this.pending.set(id, { resolve: (v) => { clearTimeout(timer); resolve(v); }, reject: (e) => { clearTimeout(timer); reject(e); } });
108
+ this.write({ jsonrpc: '2.0', id, method, params });
109
+ });
110
+ }
111
+
112
+ notify(method, params) { this.write({ jsonrpc: '2.0', method, ...(params ? { params } : {}) }); }
113
+
114
+ failAll(why) {
115
+ for (const [, p] of this.pending) p.reject(new Error(`${this.name} ${why}`));
116
+ this.pending.clear();
117
+ }
118
+
119
+ close() { try { this.proc.kill(); } catch {} }
120
+ }
121
+
122
+ class HttpConnection {
123
+ constructor(name, spec) {
124
+ this.name = name;
125
+ this.url = spec.url;
126
+ this.headers = spec.headers || {};
127
+ this.sessionId = null;
128
+ this.nextId = 1;
129
+ }
130
+
131
+ async post(body, timeoutMs) {
132
+ const res = await fetch(this.url, {
133
+ method: 'POST',
134
+ headers: {
135
+ 'Content-Type': 'application/json',
136
+ Accept: 'application/json, text/event-stream',
137
+ 'MCP-Protocol-Version': PROTOCOL_VERSION,
138
+ ...(this.sessionId ? { 'Mcp-Session-Id': this.sessionId } : {}),
139
+ ...this.headers,
140
+ },
141
+ body: JSON.stringify(body),
142
+ signal: AbortSignal.timeout(timeoutMs),
143
+ });
144
+ const sid = res.headers.get('mcp-session-id');
145
+ if (sid) this.sessionId = sid;
146
+ return res;
147
+ }
148
+
149
+ async request(method, params, timeoutMs) {
150
+ const id = this.nextId++;
151
+ const res = await this.post({ jsonrpc: '2.0', id, method, params }, timeoutMs);
152
+ if (!res.ok) throw new Error(`${method}: HTTP ${res.status}${res.status === 401 ? ' (needs authorization - add a token under "headers")' : ''}`);
153
+ const type = res.headers.get('content-type') || '';
154
+ let msg = null;
155
+ if (type.includes('text/event-stream')) {
156
+ // Read events until the one answering our id.
157
+ const text = await res.text();
158
+ for (const block of text.split(/\r?\n\r?\n/)) {
159
+ const data = block.split(/\r?\n/).filter((l) => l.startsWith('data:')).map((l) => l.slice(5).trim()).join('\n');
160
+ if (!data) continue;
161
+ try { const m = JSON.parse(data); if (m.id === id) { msg = m; break; } } catch {}
162
+ }
163
+ } else {
164
+ msg = await res.json().catch(() => null);
165
+ }
166
+ if (!msg) throw new Error(`${method}: no response`);
167
+ if (msg.error) throw new Error(msg.error.message || JSON.stringify(msg.error));
168
+ return msg.result;
169
+ }
170
+
171
+ async notify(method, params) {
172
+ try { await this.post({ jsonrpc: '2.0', method, ...(params ? { params } : {}) }, 10000); } catch {}
173
+ }
174
+
175
+ close() {}
176
+ }
177
+
178
+ // ─── Servers ────────────────────────────────────────────────────────────────
179
+
180
+ const servers = new Map(); // key -> { name, conn, tools, error, specKey }
181
+
182
+ function specKey(spec) { return JSON.stringify(spec); }
183
+
184
+ async function connect(name, spec, cwd) {
185
+ const conn = spec.url ? new HttpConnection(name, spec) : new StdioConnection(name, spec, cwd);
186
+ try {
187
+ const init = await conn.request('initialize', { protocolVersion: PROTOCOL_VERSION, capabilities: {}, clientInfo: CLIENT_INFO }, CONNECT_TIMEOUT_MS);
188
+ await conn.notify('notifications/initialized');
189
+ const tools = [];
190
+ let cursor;
191
+ do {
192
+ const page = await conn.request('tools/list', cursor ? { cursor } : {}, CONNECT_TIMEOUT_MS);
193
+ tools.push(...(page.tools || []));
194
+ cursor = page.nextCursor;
195
+ } while (cursor && tools.length < 500);
196
+ return { name, conn, tools, instructions: init?.instructions || '', error: null };
197
+ } catch (e) {
198
+ conn.close();
199
+ return { name, conn: null, tools: [], instructions: '', error: e.message };
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Connect to every configured server for this project (reusing live
205
+ * connections) and return what they offer. Never throws.
206
+ * @returns {Promise<Array<{name, tools, error, instructions}>>}
207
+ */
208
+ async function listServers(cwd) {
209
+ const wanted = loadServers(cwd);
210
+ const out = [];
211
+ await Promise.all(Object.entries(wanted).map(async ([name, spec]) => {
212
+ const key = `${cwd}::${name}`;
213
+ let s = servers.get(key);
214
+ const stale = s && (s.specKey !== specKey(spec) || (s.conn && s.conn.closed) || s.error);
215
+ if (!s || stale) {
216
+ if (s?.conn) s.conn.close();
217
+ s = { ...(await connect(name, spec, cwd)), specKey: specKey(spec) };
218
+ servers.set(key, s);
219
+ }
220
+ out.push({ name, tools: s.tools, error: s.error, instructions: s.instructions });
221
+ }));
222
+ return out.sort((a, b) => a.name.localeCompare(b.name));
223
+ }
224
+
225
+ /** Tool results as plain text for the model, images kept aside as data URLs. */
226
+ function flattenResult(result) {
227
+ const parts = Array.isArray(result?.content) ? result.content : [];
228
+ const text = [];
229
+ const images = [];
230
+ for (const p of parts) {
231
+ if (p.type === 'text') text.push(p.text);
232
+ else if (p.type === 'image' && p.data) images.push(`data:${p.mimeType || 'image/png'};base64,${p.data}`);
233
+ else if (p.type === 'resource') text.push(p.resource?.text || `[resource ${p.resource?.uri || ''}]`);
234
+ else text.push(`[${p.type} content]`);
235
+ }
236
+ if (!parts.length && result?.structuredContent) text.push(JSON.stringify(result.structuredContent, null, 1));
237
+ return { text: text.join('\n') || '(empty result)', images, isError: !!result?.isError };
238
+ }
239
+
240
+ async function callTool(cwd, serverName, toolName, args) {
241
+ const s = servers.get(`${cwd}::${serverName}`);
242
+ if (!s || !s.conn) throw new Error(`MCP server "${serverName}" is not connected${s?.error ? ` (${s.error})` : ''}.`);
243
+ const result = await s.conn.request('tools/call', { name: toolName, arguments: args || {} }, CALL_TIMEOUT_MS);
244
+ return flattenResult(result);
245
+ }
246
+
247
+ function findTool(cwd, serverName, toolName) {
248
+ const s = servers.get(`${cwd}::${serverName}`);
249
+ return s ? s.tools.find((t) => t.name === toolName) || null : null;
250
+ }
251
+
252
+ function closeAll() {
253
+ for (const [, s] of servers) if (s.conn) s.conn.close();
254
+ servers.clear();
255
+ }
256
+ process.on('exit', closeAll);
257
+
258
+ module.exports = { listServers, callTool, findTool, loadServers, closeAll, flattenResult };
@@ -27,17 +27,21 @@ export const TOOL_DOCS = {
27
27
  write_file: { d: 'Create a file or fully rewrite one. For changes to an existing file prefer edit_file.', req: ['path', 'content'], p: { path: 'File path.', content: 'The complete file content, verbatim.' } },
28
28
  edit_file: { d: 'Replace one exact block of an existing file.', req: ['path', 'search', 'replace'], p: { path: 'File path.', search: 'Existing text copied exactly from a read (no line-number prefixes), just enough to be unique.', replace: 'The replacement text.', all: '"true" to replace every occurrence (renames).' } },
29
29
  search: { d: 'Regex search across project files, or find files by name when only glob is given.', req: [], p: { pattern: 'Case-insensitive regex.', glob: 'Limit to paths like "**/*.ts", or alone to find files by name.', path: 'Folder or single file to search in.', context: 'Lines of surrounding code per hit (0-5).', files_only: '"true" to list matching files with hit counts.' } },
30
- run: { d: 'Run a shell command in the project folder on the user\'s machine.', req: ['command'], p: { command: 'The command line.' } },
30
+ run: { d: 'Run a shell command in the project folder on the user\'s machine.', req: ['command'], p: { command: 'The command line.', background: '"true" for anything that keeps running (dev server, watcher): it starts, returns the first lines and a job id, and keeps going.' } },
31
+ job: { d: 'A background command started with run background=true: read its latest output, or stop it. No id lists every job.', req: [], p: { id: 'The job id run gave you.', stop: '"true" to stop it.' } },
31
32
  use_skill: { d: 'Load a skill\'s full instructions.', req: ['name'], p: { name: 'Skill name from the SKILLS list.' } },
32
33
  list_skills: { d: 'Search the full skill library.', req: ['query'], p: { query: 'One or two words.' } },
33
34
  fetch_image: { d: 'Download an image into the project.', req: ['url', 'path'], p: { url: 'Image URL.', path: 'Where to save it, e.g. assets/hero.jpg.' } },
34
35
  browser_check: { d: 'Open a page in a real browser and get errors plus a screenshot.', req: ['url'], p: { url: 'file:///absolute/path or http://localhost:port.', wait: 'Extra milliseconds to wait before capturing.', viewport: 'desktop (default), tablet, mobile, or WIDTHxHEIGHT.' } },
35
- gmail_send: { d: 'Send a real email now. Only when the user asked for it to go out. The user sees it on an editable card first and may change it or save it as a draft instead; the result says what really happened.', req: [], p: { to: 'Recipient address.', subject: 'Subject line.', body: 'Plain-text body.', draft: 'Optional: the id of a draft saved in Codeply (from drafts_list) to send; to/subject/body then come from it.' } },
36
- gmail_search: { d: 'Search the connected Gmail account. Leave query empty to list the newest mail.', req: [], p: { query: 'Gmail search syntax, e.g. "is:unread" or "from:x@y.com". Empty = newest mail.' } },
37
- gmail_draft: { d: 'Save an email as a draft without sending it: in Gmail\'s Drafts when Gmail is connected, otherwise in Codeply\'s own drafts.', req: [], p: { to: 'Recipient address (optional for a draft).', subject: 'Subject line.', body: 'Plain-text body.' } },
36
+ gmail_send: { d: 'Send a real email now. Only when the user asked for it to go out. The user sees it on an editable card first and may change it or save it as a draft instead; the result says what really happened.', req: [], p: { to: 'Recipient address.', cc: 'Optional: addresses to copy, comma-separated.', subject: 'Subject line.', body: 'Plain-text body.', draft: 'Optional: the id of a draft saved in Codeply (from drafts_list) to send; to/subject/body then come from it.' } },
37
+ gmail_search: { d: 'Search the connected Gmail account. Leave query empty to list the newest mail. Results are previews, each with an [id] for gmail_read.', req: [], p: { query: 'Gmail search syntax, e.g. "is:unread" or "from:x@y.com". Empty = newest mail.' } },
38
+ use_computer: { d: 'Use the mouse and keyboard in another app on this computer (the Claude app, a browser, Excel...) and get a fresh screenshot back after every step. Do it yourself instead of asking the user to click or bring a window forward. Every action names the <window> it is for; it is brought to the front first, and nothing is typed or clicked if it cannot be. Typing, clicking and keys ask the user first unless they allowed it.', req: ['action'], p: { action: 'look (screenshot), focus (bring a window to the front), click, double_click, right_click, type, keys, scroll, or wait.', window: 'A few words of the app name or window title, e.g. "Claude" or "Excel". Needed for everything but look and wait.', x: 'For click and scroll: x on the latest screenshot, in its pixels.', y: 'For click and scroll: y on the latest screenshot.', target: 'What you are clicking, e.g. "New chat button" (shown to the user).', text: 'For type: the text. A newline presses Enter.', keys: 'For keys: enter, tab, escape, ctrl+n, cmd+shift+t...', amount: 'For scroll: clicks, negative scrolls up. Default 3.', seconds: 'For wait: 1 to 30. Waits, then shows the screen.' } },
39
+ gmail_read: { d: 'Read one whole email (sender, recipients, cc, date and the full body) by its id from gmail_search.', req: ['id'], p: { id: 'The [id] gmail_search showed for the email.' } },
40
+ gmail_draft: { d: 'Save an email as a draft without sending it: in Gmail\'s Drafts when Gmail is connected, otherwise in Codeply\'s own drafts.', req: [], p: { to: 'Recipient address (optional for a draft).', cc: 'Optional: addresses to copy, comma-separated.', subject: 'Subject line.', body: 'Plain-text body.' } },
38
41
  drafts_list: { d: 'List the drafts saved in Codeply (ids, subject, recipient). Send one with gmail_send and its id as draft.', req: [], p: {} },
39
42
  calendar_list: { d: 'List events on the user\'s Google Calendar in a time range (default: today). Times are local.', req: [], p: { from: 'Start: today, tomorrow, 2026-10-03 or 2026-10-03T14:00. Default today.', to: 'End (a date means through that whole day). Default: end of the from day.', max: 'Most events to return (1-50, default 20).' } },
40
- calendar_add: { d: 'Add an event to the user\'s Google Calendar (asks the user first).', req: ['title', 'start'], p: { title: 'Event title.', start: 'Local start: 2026-10-03T14:00, or 2026-10-03 for an all-day event.', end: 'Local end. Default: one hour after start (all-day: the same day).', description: 'Notes for the event.', location: 'Where it is.', reminders: 'Popup reminders, minutes before, comma-separated, e.g. "10,60".' } },
43
+ calendar_add: { d: 'Add an event to the user\'s Google Calendar (asks the user first).', req: ['title', 'start'], p: { title: 'Event title.', start: 'Local start: 2026-10-03T14:00, or 2026-10-03 for an all-day event.', end: 'Local end. Default: one hour after start (all-day: the same day).', description: 'Notes for the event.', location: 'Where it is.', reminders: 'Popup reminders, minutes before, comma-separated, e.g. "10,60".', attendees: 'Optional: guest email addresses, comma-separated. They get an invite.' } },
44
+ calendar_update: { d: 'Move, rename or cancel an event already on the user\'s Google Calendar, by its [id] from calendar_list (asks the user first; guests are told). Use this to change an event, never calendar_add a second one.', req: ['id'], p: { id: 'The event [id] from calendar_list.', start: 'New local start with a time: 2026-10-03T14:00.', end: 'New local end. Default: minutes after start.', minutes: 'Length in minutes when no end is given (default 60).', title: 'New title.', location: 'New place.', description: 'New notes.', cancel: '"yes" to cancel (delete) the event.' } },
41
45
  add_model: { d: 'Add a model to the user\'s Codeply model menu when they ask (they give a name, base URL, model id and API key). Asks them first, tests it, saves it on this computer, selects it and syncs it to their phone. Any OpenAI-compatible API works; Ollama on this computer is http://localhost:11434 with no key.', req: ['base_url', 'model'], p: { name: 'Display name, e.g. "GPT-5 mini". Default: the model id.', base_url: 'OpenAI-compatible base URL, e.g. https://api.openai.com/v1, https://api.deepseek.com, https://openrouter.ai/api/v1.', model: 'The model id the provider expects, e.g. gpt-5-mini, deepseek-chat.', api_key: 'The API key the user gave (leave empty for a server with no key).' } },
42
46
  slack_post_message: { d: 'Post a real Slack message now. Only when the user asked for it.', req: ['channel', 'text'], p: { channel: 'Channel name or id.', text: 'Message text.' } },
43
47
  vercel_deploy: { d: 'Deploy a folder to the connected Vercel account (live, real).', req: [], p: { path: 'Folder to deploy. Default ".".' } },
@@ -46,6 +50,8 @@ export const TOOL_DOCS = {
46
50
  github_create_repo: { d: 'Create a GitHub repo in the connected account and push this folder to it.', req: ['name'], p: { path: 'Folder to push. Default ".".', name: 'Repository name.' } },
47
51
  design_reference_search: { d: 'Search the local library of real app screenshots for a kind of screen.', req: ['term'], p: { term: 'What the screen is, e.g. "checkout".', category: 'Optional category filter.' } },
48
52
  view_images: { d: 'Look at images (e.g. reference screenshots).', req: ['urls'], p: { urls: 'Comma-separated image URLs.' } },
53
+ design_direction: { d: 'Deal an art direction for a page or app from the codeply-taste research library.', req: [], p: { brief: 'One line: what it is and for whom.', sector: 'e.g. saas, restaurant, consumer-app, portfolio (list=sectors lists them).', tone: 'quiet, bold, warm, technical, playful, luxury or editorial.', sections: 'Comma-separated section roles to plan.', options: '3 to see three directions first.', direction: 'The id of the direction to deal in full.', redraw: 'Re-deal one part: type, palette, hero, photos, section:<role> or direction.', have: 'Assets that really exist: photography, product-photo, product-ui, illustration, video.', keep: 'Parts the brand already fixes: palette, type.', avoid: 'Direction ids to skip.', show: 'true to reprint the saved sheet.', list: 'sectors, directions, roles or tones.', site: 'Print one studied reference site in full.' } },
54
+ design_lint: { d: 'Check a design against the codeply-taste rules and a real render; returns findings and screenshots.', req: ['path'], p: { path: 'The design file, usually index.html.', kind: 'website, app, slides, doc, poster, social or logo.', widths: 'Optional comma-separated widths to render.', allow: 'Rule ids to allow, each with a reason you can state.' } },
49
55
  supabase_api: { d: 'Call the Supabase Management API.', req: ['method', 'path'], p: { method: 'GET, POST, PATCH, PUT or DELETE.', path: 'Path starting with /v1/.', body: 'JSON body.' } },
50
56
  supabase_sql: { d: 'Run SQL on a Supabase project database.', req: ['ref', 'query'], p: { ref: 'Project ref.', query: 'SQL.' } },
51
57
  vercel_api: { d: 'Call the Vercel REST API.', req: ['method', 'path'], p: { method: 'HTTP method.', path: 'Path like /v9/projects.', body: 'JSON body.' } },
@@ -58,6 +64,12 @@ export const TOOL_DOCS = {
58
64
  web_fetch: { d: 'Fetch a web page or API URL and get its content as readable text.', req: ['url'], p: { url: 'http:// or https:// URL.', format: '"text" (default, HTML converted to readable text), "html" or "raw".' } },
59
65
  reach_me: { d: 'Reach the user on their phone: how=call rings their phone with your voice (only when it is urgent or they asked for a call), how=text sends a notification (anything else). Now, or later with at.', req: ['how', 'message'], p: { how: 'call or text.', message: 'What to tell them, short and specific.', at: 'Optional: when, like 17:30, 5pm, "in 20 minutes" or an ISO time. Empty means now.', bot: 'Optional: which of the user\'s bots it comes from, by name ("tell Shella to call me" means bot=Shella). Empty means you.' } },
60
66
  watch_email: { d: 'Watch the inbox for an email from a sender and reach the user the moment one arrives (works in the background, also with Craft closed to the tray).', req: ['from', 'how'], p: { from: 'The email address, or a domain like example.com.', how: 'call or text, when it arrives.', note: 'Optional: what to say about it.', repeat: 'Optional: yes to alert on every email from them; default is once.' } },
67
+ look_at_screen: { d: 'See what is on the user\'s screen right now: a screenshot plus the text on it. Call it first whenever they point at something you cannot see in this chat (this, that error, her message, what is on my right, see, reply to him), instead of asking them to paste it.', req: [], p: { why: 'A few words on what you are looking for.', window: 'Optional: a few words of an earlier window or tab title (from the last 12 the user had in front) to see how it looked then, instead of the screen now.' } },
68
+ life_context: { d: 'Look up the user\'s life context, shared by all their bots: calendar, email (and who is waiting on a reply), calls, plans, projects, what is on their screen, what was heard around them, Slack, bot tasks.', req: [], p: { about: 'now, today, tomorrow, week, person, calls, email, activity, heard, plans, projects, slack, tasks or health (steps, workouts, sleep, if this bot may read it). Leave empty to search with query.', query: 'A name for about=person, or words to search for.' } },
69
+ remember: { d: 'Save one lasting thing to the user\'s shared Codeply brain (every app and bot reads it): a preference, a fact about them, a routine, how they like to work, a project decision, idea, requirement or to-do, or temporary context. Or remove one with forget.', req: [], p: { kind: 'preference, fact, routine, style, decision, idea, requirement, todo or context (temporary).', slot: 'What it is about in 1 to 4 words, the same each time (reply length, favorite color, auth provider).', value: 'The value in a few words.', text: 'One short third-person sentence, e.g. The user wants short replies.', certainty: 'stated (they said it plainly), implied, temporary, joking (sarcasm, banter: not saved), hypothetical or quoting.', project: 'The project, for decisions, ideas, requirements and to-dos. Default: this folder.', until: 'For context: when it stops being true, in the user\'s local time (17:00 or 2026-10-26T17:00). Better than ttlHours when they said when.', ttlHours: 'For context: how many hours it stays true.', forget: 'A memory id from recall, to remove it instead.' } },
70
+ recall: { d: 'Search the user\'s shared Codeply brain: their preferences and facts, project decisions and to-dos from any Codeply app, temporary context, lessons other bots learned.', req: [], p: { query: 'Words to look for. Empty lists the newest.', project: 'Only this project.' } },
71
+ hand_off: { d: 'Pass the part of a job that belongs to another bot (calendar, email, reminders, research...) and carry on without waiting. Send only what they need.', req: ['to', 'intent'], p: { to: 'A bot\'s name, or a domain: calendar, email, reminders, calls, texts, research, code, social, health, money.', intent: 'What they should get done, in one or two sentences.', facts: 'The facts they need, one per line: names, addresses, dates and times with the time zone, ids.', decision: 'What has been decided so far, if anything.' } },
72
+ bot_email_send: { d: 'Email someone from your own address (not the user\'s Gmail), as the user\'s AI assistant. The user sees and can edit it first unless your settings allow sending alone.', req: ['to', 'body'], p: { to: 'One email address.', subject: 'Subject. For a reply, leave empty to keep the thread\'s subject.', body: 'Plain text. Sign with your own name.', reply_to: 'Optional: the id of the received email you are answering, so it stays in the same thread.' } },
61
73
  image_search: { d: 'Search the whole web for real photos (like Google Images). Returns image URLs with sizes; download the one you pick with fetch_image.', req: ['query'], p: { query: 'What the photo should show.', num: 'Number of results (1-16, default 8).' } },
62
74
  web_search: { d: 'Search the web for current information (docs, errors, versions, news).', req: ['query'], p: { query: 'The search query.', num: 'Number of results (1-20, default 8).' } },
63
75
  apply_patch: { d: 'Change several files at once (add, update, delete, move) with one patch. Use it for multi-file changes; use edit_file for a single block.', req: ['patch'], p: { patch: 'The patch text: "*** Begin Patch", then "*** Add File: path" (lines start with +), "*** Update File: path" (optional "*** Move to: path", "@@ anchor line", then lines starting with " ", "-" or "+"), "*** Delete File: path", and "*** End Patch".' } },