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.
- package/LICENSE +21 -21
- package/THIRD_PARTY_NOTICES.md +65 -65
- package/bin/codeply.js +8 -3
- package/lib/agent.mjs +2596 -2268
- package/lib/ai.js +135 -27
- package/lib/ai.mjs +6 -6
- package/lib/apply-patch.js +217 -217
- package/lib/arity-table.json +138 -138
- package/lib/arity.js +70 -70
- package/lib/auth.mjs +6 -6
- package/lib/autopilot.js +84 -0
- package/lib/bot-mail.js +46 -0
- package/lib/bots.js +142 -15
- package/lib/brain.js +914 -0
- package/lib/call-assist.js +120 -0
- package/lib/chatgpt.js +282 -282
- package/lib/cloud-browser.mjs +143 -143
- package/lib/cloud.mjs +1396 -1320
- package/lib/commands.js +103 -103
- package/lib/computer.js +136 -0
- package/lib/design-library/assemble-artifact.mjs +21 -21
- package/lib/design-library/cleanup.mjs +45 -45
- package/lib/design-library/index.json +22379 -22379
- package/lib/design-library/make-table-data.mjs +39 -39
- package/lib/design-tools.mjs +173 -0
- package/lib/diagnostics.js +192 -192
- package/lib/dsh-acp.mjs +444 -0
- package/lib/edit-engine.js +364 -364
- package/lib/github-agent.mjs +326 -326
- package/lib/handoff.mjs +245 -0
- package/lib/history.js +78 -75
- package/lib/life-sources.js +319 -0
- package/lib/life-store.js +356 -0
- package/lib/life-triggers.js +286 -0
- package/lib/life.js +487 -0
- package/lib/mail-watch.js +3 -3
- package/lib/mcp.js +258 -258
- package/lib/native-tools.mjs +17 -5
- package/lib/oauth-connectors.js +76 -21
- package/lib/permissions.js +109 -109
- package/lib/plugins.js +356 -353
- package/lib/reach.js +67 -67
- package/lib/render-md.mjs +647 -617
- package/lib/screen.js +77 -0
- package/lib/server.mjs +458 -457
- package/lib/session-db.js +220 -220
- package/lib/share.js +208 -168
- package/lib/skills.js +4 -1
- package/lib/snapshot.js +146 -146
- package/lib/subagents.js +4 -2
- package/lib/telemetry.js +105 -105
- package/lib/terminal-hints.js +154 -154
- package/lib/textarea.mjs +264 -258
- package/lib/tools.mjs +326 -40
- package/lib/tui.mjs +1459 -1086
- package/lib/typecheck-worker.js +147 -147
- package/lib/web-tools.js +209 -209
- package/package.json +1 -1
- package/skills/SOURCE.md +58 -58
- package/skills/accessibility/SKILL.md +147 -147
- package/skills/agent-architecture-audit/SKILL.md +257 -257
- package/skills/agent-eval/SKILL.md +147 -147
- package/skills/agent-harness-construction/SKILL.md +74 -74
- package/skills/agent-introspection-debugging/SKILL.md +154 -154
- package/skills/agent-payment-x402/SKILL.md +225 -225
- package/skills/agent-self-evaluation/SKILL.md +182 -182
- package/skills/agent-self-evaluation/references/evaluation-criteria.md +71 -71
- package/skills/agent-self-evaluation/references/hook-integration.md +64 -64
- package/skills/agent-sort/SKILL.md +216 -216
- package/skills/agentic-engineering/SKILL.md +64 -64
- package/skills/agentic-os/SKILL.md +388 -388
- package/skills/ai-first-engineering/SKILL.md +52 -52
- package/skills/ai-regression-testing/SKILL.md +386 -386
- package/skills/android-clean-architecture/SKILL.md +340 -340
- package/skills/angular-developer/SKILL.md +155 -155
- package/skills/angular-developer/references/angular-animations.md +160 -160
- package/skills/angular-developer/references/angular-aria.md +410 -410
- package/skills/angular-developer/references/cli.md +86 -86
- package/skills/angular-developer/references/component-harnesses.md +59 -59
- package/skills/angular-developer/references/component-styling.md +91 -91
- package/skills/angular-developer/references/components.md +117 -117
- package/skills/angular-developer/references/creating-services.md +97 -97
- package/skills/angular-developer/references/data-resolvers.md +69 -69
- package/skills/angular-developer/references/define-routes.md +67 -67
- package/skills/angular-developer/references/defining-providers.md +72 -72
- package/skills/angular-developer/references/di-fundamentals.md +120 -120
- package/skills/angular-developer/references/e2e-testing.md +56 -56
- package/skills/angular-developer/references/effects.md +83 -83
- package/skills/angular-developer/references/hierarchical-injectors.md +43 -43
- package/skills/angular-developer/references/host-elements.md +80 -80
- package/skills/angular-developer/references/injection-context.md +63 -63
- package/skills/angular-developer/references/inputs.md +101 -101
- package/skills/angular-developer/references/linked-signal.md +59 -59
- package/skills/angular-developer/references/loading-strategies.md +61 -61
- package/skills/angular-developer/references/mcp.md +108 -108
- package/skills/angular-developer/references/navigate-to-routes.md +69 -69
- package/skills/angular-developer/references/outputs.md +86 -86
- package/skills/angular-developer/references/reactive-forms.md +122 -122
- package/skills/angular-developer/references/rendering-strategies.md +44 -44
- package/skills/angular-developer/references/resource.md +77 -77
- package/skills/angular-developer/references/route-animations.md +56 -56
- package/skills/angular-developer/references/route-guards.md +52 -52
- package/skills/angular-developer/references/router-lifecycle.md +45 -45
- package/skills/angular-developer/references/router-testing.md +87 -87
- package/skills/angular-developer/references/show-routes-with-outlets.md +68 -68
- package/skills/angular-developer/references/signal-forms.md +795 -795
- package/skills/angular-developer/references/signals-overview.md +94 -94
- package/skills/angular-developer/references/tailwind-css.md +69 -69
- package/skills/angular-developer/references/template-driven-forms.md +114 -114
- package/skills/angular-developer/references/testing-fundamentals.md +65 -65
- package/skills/api-connector-builder/SKILL.md +121 -121
- package/skills/api-design/SKILL.md +524 -524
- package/skills/architecture-decision-records/SKILL.md +180 -180
- package/skills/article-writing/SKILL.md +80 -80
- package/skills/automation-audit-ops/SKILL.md +143 -143
- package/skills/autonomous-agent-harness/SKILL.md +274 -274
- package/skills/autonomous-loops/SKILL.md +611 -611
- package/skills/backend-patterns/SKILL.md +562 -562
- package/skills/benchmark/SKILL.md +95 -95
- package/skills/benchmark-methodology/SKILL.md +191 -191
- package/skills/benchmark-optimization-loop/SKILL.md +71 -71
- package/skills/blender-motion-state-inspection/SKILL.md +165 -165
- package/skills/blueprint/SKILL.md +106 -106
- package/skills/brand-discovery/SKILL.md +145 -145
- package/skills/brand-discovery/references/10_purpose-why.md +40 -40
- package/skills/brand-discovery/references/20_positioning.md +44 -44
- package/skills/brand-discovery/references/30_audience-niche.md +52 -52
- package/skills/brand-discovery/references/40_personality-archetype.md +57 -57
- package/skills/brand-discovery/references/50_voice-tone.md +59 -59
- package/skills/brand-discovery/references/60_narrative-story.md +50 -50
- package/skills/brand-discovery/references/70_founder-tension.md +49 -49
- package/skills/brand-discovery/references/90_SYNTHESIS.md +133 -133
- package/skills/brand-voice/SKILL.md +98 -98
- package/skills/brand-voice/references/voice-profile-schema.md +55 -55
- package/skills/browser-qa/SKILL.md +105 -105
- package/skills/bun-runtime/SKILL.md +85 -85
- package/skills/canary-watch/SKILL.md +108 -108
- package/skills/carrier-relationship-management/SKILL.md +212 -212
- package/skills/cisco-ios-patterns/SKILL.md +164 -164
- package/skills/ck/SKILL.md +148 -148
- package/skills/claude-devfleet/SKILL.md +112 -112
- package/skills/click-path-audit/SKILL.md +245 -245
- package/skills/clickhouse-io/SKILL.md +445 -445
- package/skills/code-tour/SKILL.md +254 -254
- package/skills/codebase-onboarding/SKILL.md +234 -234
- package/skills/codehealth-mcp/SKILL.md +167 -167
- package/skills/codeply-taste/LICENSE +21 -0
- package/skills/codeply-taste/SKILL.md +100 -0
- package/skills/codeply-taste/SOURCE.md +10 -0
- package/skills/codeply-taste/assets/base.css +100 -0
- package/skills/codeply-taste/assets/base.js +37 -0
- package/skills/codeply-taste/library/directions.json +1078 -0
- package/skills/codeply-taste/library/heroes.json +894 -0
- package/skills/codeply-taste/library/motions.json +57 -0
- package/skills/codeply-taste/library/palettes.json +1485 -0
- package/skills/codeply-taste/library/photos.json +7240 -0
- package/skills/codeply-taste/library/sections.json +4136 -0
- package/skills/codeply-taste/library/sites.json +7274 -0
- package/skills/codeply-taste/library/type.json +2024 -0
- package/skills/codeply-taste/references/colour.md +141 -0
- package/skills/codeply-taste/references/copy.md +177 -0
- package/skills/codeply-taste/references/critique.md +171 -0
- package/skills/codeply-taste/references/imagery.md +377 -0
- package/skills/codeply-taste/references/layout.md +178 -0
- package/skills/codeply-taste/references/motion.md +292 -0
- package/skills/codeply-taste/references/research.md +306 -0
- package/skills/codeply-taste/references/tells.md +115 -0
- package/skills/codeply-taste/references/type.md +170 -0
- package/skills/codeply-taste/scripts/assets.mjs +865 -0
- package/skills/codeply-taste/scripts/draw.mjs +525 -0
- package/skills/codeply-taste/scripts/inspect.mjs +205 -0
- package/skills/codeply-taste/scripts/lib/cdp.mjs +205 -0
- package/skills/codeply-taste/scripts/lib/probe.mjs +569 -0
- package/skills/codeply-taste/scripts/lib/rules.mjs +137 -0
- package/skills/codeply-taste/scripts/lint.mjs +108 -0
- package/skills/coding-standards/SKILL.md +551 -551
- package/skills/competitive-platform-analysis/SKILL.md +214 -214
- package/skills/competitive-report-structure/SKILL.md +162 -162
- package/skills/compose-multiplatform-patterns/SKILL.md +300 -300
- package/skills/config-gc/SKILL.md +120 -120
- package/skills/configure-ecc/SKILL.md +385 -385
- package/skills/connections-optimizer/SKILL.md +190 -190
- package/skills/content-engine/SKILL.md +132 -132
- package/skills/content-hash-cache-pattern/SKILL.md +162 -162
- package/skills/context-budget/SKILL.md +136 -136
- package/skills/continuous-agent-loop/SKILL.md +46 -46
- package/skills/continuous-learning/SKILL.md +132 -132
- package/skills/continuous-learning-v2/SKILL.md +361 -361
- package/skills/contract-first/SKILL.md +287 -287
- package/skills/cost-aware-llm-pipeline/SKILL.md +184 -184
- package/skills/cost-tracking/SKILL.md +97 -97
- package/skills/council/SKILL.md +204 -204
- package/skills/cpp-coding-standards/SKILL.md +724 -724
- package/skills/cpp-testing/SKILL.md +325 -325
- package/skills/crosspost/SKILL.md +112 -112
- package/skills/csharp-testing/SKILL.md +322 -322
- package/skills/customer-billing-ops/SKILL.md +141 -141
- package/skills/customs-trade-compliance/SKILL.md +263 -263
- package/skills/dart-flutter-patterns/SKILL.md +564 -564
- package/skills/dashboard-builder/SKILL.md +109 -109
- package/skills/data-scraper-agent/SKILL.md +765 -765
- package/skills/data-throughput-accelerator/SKILL.md +74 -74
- package/skills/database-migrations/SKILL.md +430 -430
- package/skills/deep-research/SKILL.md +160 -160
- package/skills/defi-amm-security/SKILL.md +167 -167
- package/skills/delivery-gate/SKILL.md +126 -126
- package/skills/deployment-patterns/SKILL.md +428 -428
- package/skills/design-system/SKILL.md +83 -83
- package/skills/django-celery/SKILL.md +458 -458
- package/skills/django-patterns/SKILL.md +735 -735
- package/skills/django-security/SKILL.md +644 -644
- package/skills/django-tdd/SKILL.md +730 -730
- package/skills/django-verification/SKILL.md +470 -470
- package/skills/dmux-workflows/SKILL.md +192 -192
- package/skills/docker-patterns/SKILL.md +365 -365
- package/skills/documentation-lookup/SKILL.md +91 -91
- package/skills/dotnet-patterns/SKILL.md +322 -322
- package/skills/dynamic-workflow-mode/SKILL.md +124 -124
- package/skills/e2e-testing/SKILL.md +327 -327
- package/skills/ecc-guide/SKILL.md +190 -190
- package/skills/ecc-recipes/SKILL.md +149 -149
- package/skills/ecc-tools-cost-audit/SKILL.md +161 -161
- package/skills/email-ops/SKILL.md +122 -122
- package/skills/energy-procurement/SKILL.md +228 -228
- package/skills/enterprise-agent-ops/SKILL.md +51 -51
- package/skills/error-handling/SKILL.md +377 -377
- package/skills/eval-harness/SKILL.md +271 -271
- package/skills/evm-token-decimals/SKILL.md +131 -131
- package/skills/exa-search/SKILL.md +108 -108
- package/skills/fal-ai-media/SKILL.md +289 -289
- package/skills/fastapi-patterns/SKILL.md +514 -514
- package/skills/finance-billing-ops/SKILL.md +128 -128
- package/skills/flox-environments/SKILL.md +497 -497
- package/skills/flutter-dart-code-review/SKILL.md +436 -436
- package/skills/foundation-models-on-device/SKILL.md +243 -243
- package/skills/frontend-a11y/SKILL.md +446 -446
- package/skills/frontend-design-direction/SKILL.md +93 -93
- package/skills/frontend-patterns/SKILL.md +657 -657
- package/skills/frontend-slides/SKILL.md +185 -185
- package/skills/fsharp-testing/SKILL.md +281 -281
- package/skills/gan-style-harness/SKILL.md +279 -279
- package/skills/gateguard/SKILL.md +133 -133
- package/skills/generating-python-installer/SKILL.md +820 -820
- package/skills/git-workflow/SKILL.md +716 -716
- package/skills/github-ops/SKILL.md +145 -145
- package/skills/golang-patterns/SKILL.md +676 -676
- package/skills/golang-testing/SKILL.md +721 -721
- package/skills/google-workspace-ops/SKILL.md +96 -96
- package/skills/growth-log/SKILL.md +128 -128
- package/skills/healthcare-cdss-patterns/SKILL.md +246 -246
- package/skills/healthcare-emr-patterns/SKILL.md +160 -160
- package/skills/healthcare-eval-harness/SKILL.md +208 -208
- package/skills/healthcare-phi-compliance/SKILL.md +146 -146
- package/skills/hermes-imports/SKILL.md +89 -89
- package/skills/hexagonal-architecture/SKILL.md +277 -277
- package/skills/hipaa-compliance/SKILL.md +79 -79
- package/skills/homelab-network-readiness/SKILL.md +170 -170
- package/skills/homelab-network-setup/SKILL.md +130 -130
- package/skills/homelab-pihole-dns/SKILL.md +275 -275
- package/skills/homelab-vlan-segmentation/SKILL.md +312 -312
- package/skills/homelab-wireguard-vpn/SKILL.md +306 -306
- package/skills/hookify-rules/SKILL.md +128 -128
- package/skills/inherit-legacy-style/SKILL.md +157 -157
- package/skills/intent-driven-development/SKILL.md +360 -360
- package/skills/inventory-demand-planning/SKILL.md +247 -247
- package/skills/investor-materials/SKILL.md +97 -97
- package/skills/investor-outreach/SKILL.md +92 -92
- package/skills/ios-icon-gen/SKILL.md +158 -158
- package/skills/iterative-retrieval/SKILL.md +212 -212
- package/skills/ito-basket-compare/SKILL.md +64 -64
- package/skills/ito-compute/SKILL.md +129 -129
- package/skills/ito-data-atlas-agent/SKILL.md +64 -64
- package/skills/ito-market-intelligence/SKILL.md +61 -61
- package/skills/ito-trade-planner/SKILL.md +68 -68
- package/skills/java-coding-standards/SKILL.md +384 -384
- package/skills/jira-integration/SKILL.md +303 -303
- package/skills/jpa-patterns/SKILL.md +152 -152
- package/skills/knowledge-ops/SKILL.md +155 -155
- package/skills/kotlin-coroutines-flows/SKILL.md +285 -285
- package/skills/kotlin-exposed-patterns/SKILL.md +720 -720
- package/skills/kotlin-ktor-patterns/SKILL.md +690 -690
- package/skills/kotlin-patterns/SKILL.md +712 -712
- package/skills/kotlin-testing/SKILL.md +825 -825
- package/skills/kubernetes-patterns/SKILL.md +756 -756
- package/skills/laravel-patterns/SKILL.md +416 -416
- package/skills/laravel-plugin-discovery/SKILL.md +230 -230
- package/skills/laravel-security/SKILL.md +948 -948
- package/skills/laravel-tdd/SKILL.md +675 -675
- package/skills/laravel-verification/SKILL.md +180 -180
- package/skills/latency-critical-systems/SKILL.md +75 -75
- package/skills/lead-intelligence/SKILL.md +322 -322
- package/skills/liquid-glass-design/SKILL.md +279 -279
- package/skills/llm-trading-agent-security/SKILL.md +147 -147
- package/skills/logistics-exception-management/SKILL.md +222 -222
- package/skills/loop-design-check/SKILL.md +143 -143
- package/skills/mailtrap-email-integration/SKILL.md +77 -77
- package/skills/make-interfaces-feel-better/SKILL.md +152 -152
- package/skills/manim-video/SKILL.md +90 -90
- package/skills/market-research/SKILL.md +76 -76
- package/skills/marketing-campaign/SKILL.md +114 -114
- package/skills/mcp-server-patterns/SKILL.md +70 -70
- package/skills/messages-ops/SKILL.md +105 -105
- package/skills/ml-adoption-playbook/SKILL.md +57 -57
- package/skills/mle-workflow/SKILL.md +348 -348
- package/skills/motion-advanced/SKILL.md +596 -596
- package/skills/motion-foundations/SKILL.md +299 -299
- package/skills/motion-patterns/SKILL.md +434 -434
- package/skills/motion-ui/SKILL.md +576 -576
- package/skills/mysql-patterns/SKILL.md +413 -413
- package/skills/nanoclaw-repl/SKILL.md +34 -34
- package/skills/nestjs-patterns/SKILL.md +231 -231
- package/skills/netmiko-ssh-automation/SKILL.md +174 -174
- package/skills/network-bgp-diagnostics/SKILL.md +168 -168
- package/skills/network-config-validation/SKILL.md +211 -211
- package/skills/network-interface-health/SKILL.md +153 -153
- package/skills/nextjs-turbopack/SKILL.md +58 -58
- package/skills/nodejs-keccak256/SKILL.md +103 -103
- package/skills/nutrient-document-processing/SKILL.md +168 -168
- package/skills/nuxt4-patterns/SKILL.md +101 -101
- package/skills/openclaw-persona-forge/SKILL.md +289 -289
- package/skills/openclaw-persona-forge/references/avatar-style.md +124 -124
- package/skills/openclaw-persona-forge/references/boundary-rules.md +53 -53
- package/skills/openclaw-persona-forge/references/error-handling.md +53 -53
- package/skills/openclaw-persona-forge/references/identity-tension.md +48 -48
- package/skills/openclaw-persona-forge/references/naming-system.md +39 -39
- package/skills/openclaw-persona-forge/references/output-template.md +166 -166
- package/skills/opensource-pipeline/SKILL.md +256 -256
- package/skills/orch-add-feature/SKILL.md +45 -45
- package/skills/orch-build-mvp/SKILL.md +49 -49
- package/skills/orch-change-feature/SKILL.md +43 -43
- package/skills/orch-fix-defect/SKILL.md +43 -43
- package/skills/orch-pipeline/SKILL.md +121 -121
- package/skills/orch-refine-code/SKILL.md +44 -44
- package/skills/parallel-execution-optimizer/SKILL.md +74 -74
- package/skills/perl-patterns/SKILL.md +505 -505
- package/skills/perl-security/SKILL.md +504 -504
- package/skills/perl-testing/SKILL.md +476 -476
- package/skills/plan-canvas/SKILL.md +153 -153
- package/skills/plan-orchestrate/SKILL.md +263 -263
- package/skills/plankton-code-quality/SKILL.md +237 -237
- package/skills/postgres-patterns/SKILL.md +148 -148
- package/skills/prediction-market-oracle-research/SKILL.md +64 -64
- package/skills/prediction-market-risk-review/SKILL.md +61 -61
- package/skills/premium-web-design/SKILL.md +154 -154
- package/skills/prisma-patterns/SKILL.md +401 -401
- package/skills/product-capability/SKILL.md +142 -142
- package/skills/product-lens/SKILL.md +93 -93
- package/skills/production-audit/SKILL.md +207 -207
- package/skills/production-scheduling/SKILL.md +238 -238
- package/skills/project-flow-ops/SKILL.md +112 -112
- package/skills/prompt-optimizer/SKILL.md +398 -398
- package/skills/python-patterns/SKILL.md +751 -751
- package/skills/python-testing/SKILL.md +817 -817
- package/skills/pytorch-patterns/SKILL.md +397 -397
- package/skills/quality-nonconformance/SKILL.md +260 -260
- package/skills/quarkus-patterns/SKILL.md +723 -723
- package/skills/quarkus-security/SKILL.md +468 -468
- package/skills/quarkus-tdd/SKILL.md +812 -812
- package/skills/quarkus-verification/SKILL.md +480 -480
- package/skills/ralphinho-rfc-pipeline/SKILL.md +68 -68
- package/skills/react-native-patterns/SKILL.md +326 -326
- package/skills/react-patterns/SKILL.md +342 -342
- package/skills/react-performance/SKILL.md +575 -575
- package/skills/react-testing/SKILL.md +424 -424
- package/skills/recsys-pipeline-architect/SKILL.md +115 -115
- package/skills/recursive-decision-ledger/SKILL.md +81 -81
- package/skills/redis-patterns/SKILL.md +404 -404
- package/skills/regex-vs-llm-structured-text/SKILL.md +221 -221
- package/skills/remotion-video-creation/SKILL.md +43 -43
- package/skills/repo-scan/SKILL.md +79 -79
- package/skills/research-ops/SKILL.md +113 -113
- package/skills/returns-reverse-logistics/SKILL.md +240 -240
- package/skills/rules-distill/SKILL.md +265 -265
- package/skills/rust-patterns/SKILL.md +500 -500
- package/skills/rust-testing/SKILL.md +501 -501
- package/skills/safety-guard/SKILL.md +76 -76
- package/skills/santa-method/SKILL.md +307 -307
- package/skills/scientific-db-pubmed-database/SKILL.md +176 -176
- package/skills/scientific-db-uspto-database/SKILL.md +178 -178
- package/skills/scientific-pkg-gget/SKILL.md +167 -167
- package/skills/scientific-thinking-literature-review/SKILL.md +193 -193
- package/skills/scientific-thinking-scholar-evaluation/SKILL.md +161 -161
- package/skills/search-first/SKILL.md +183 -183
- package/skills/security-bounty-hunter/SKILL.md +100 -100
- package/skills/security-review/SKILL.md +504 -504
- package/skills/security-scan/SKILL.md +166 -166
- package/skills/seo/SKILL.md +155 -155
- package/skills/skill-comply/SKILL.md +59 -59
- package/skills/skill-scout/SKILL.md +141 -141
- package/skills/skill-stocktake/SKILL.md +195 -195
- package/skills/social-graph-ranker/SKILL.md +155 -155
- package/skills/social-publisher/SKILL.md +130 -130
- package/skills/springboot-patterns/SKILL.md +315 -315
- package/skills/springboot-security/SKILL.md +273 -273
- package/skills/springboot-tdd/SKILL.md +159 -159
- package/skills/springboot-verification/SKILL.md +232 -232
- package/skills/strategic-compact/SKILL.md +142 -142
- package/skills/swift-actor-persistence/SKILL.md +144 -144
- package/skills/swift-concurrency-6-2/SKILL.md +216 -216
- package/skills/swift-protocol-di-testing/SKILL.md +191 -191
- package/skills/swiftui-patterns/SKILL.md +259 -259
- package/skills/taste/SKILL.md +264 -264
- package/skills/taste/references/genre-taxonomy.md +87 -87
- package/skills/tdd-workflow/SKILL.md +583 -583
- package/skills/team-agent-orchestration/SKILL.md +111 -111
- package/skills/team-builder/SKILL.md +169 -169
- package/skills/terminal-ops/SKILL.md +110 -110
- package/skills/tinystruct-patterns/SKILL.md +279 -279
- package/skills/tinystruct-patterns/references/architecture.md +90 -90
- package/skills/tinystruct-patterns/references/data-handling.md +60 -60
- package/skills/tinystruct-patterns/references/database.md +99 -99
- package/skills/tinystruct-patterns/references/routing.md +64 -64
- package/skills/tinystruct-patterns/references/system-usage.md +97 -97
- package/skills/tinystruct-patterns/references/testing.md +72 -72
- package/skills/token-budget-advisor/SKILL.md +134 -134
- package/skills/ui-demo/SKILL.md +466 -466
- package/skills/ui-to-vue/SKILL.md +135 -135
- package/skills/uncloud/SKILL.md +344 -344
- package/skills/unified-memory/SKILL.md +170 -170
- package/skills/unified-notifications-ops/SKILL.md +188 -188
- package/skills/verification-loop/SKILL.md +129 -129
- package/skills/video-editing/SKILL.md +311 -311
- package/skills/videodb/SKILL.md +375 -375
- package/skills/visa-doc-translate/SKILL.md +117 -117
- package/skills/vite-patterns/SKILL.md +450 -450
- package/skills/vue-patterns/SKILL.md +471 -471
- package/skills/windows-desktop-e2e/SKILL.md +888 -888
- package/skills/workspace-surface-audit/SKILL.md +126 -126
- 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 };
|
package/lib/native-tools.mjs
CHANGED
|
@@ -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
|
-
|
|
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".' } },
|