codeply-cli 0.5.5 → 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 -1312
- 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/web-tools.js
CHANGED
|
@@ -1,209 +1,209 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Web access for the agent: fetch a page as readable text, and search the web.
|
|
3
|
-
*
|
|
4
|
-
* Search uses Exa's public MCP endpoint (the same one opencode's websearch tool
|
|
5
|
-
* talks to; no key needed) and falls back to DuckDuckGo's HTML results when
|
|
6
|
-
* Exa is unreachable. Fetching converts HTML to plain markdown-ish text so the
|
|
7
|
-
* model gets the content, not the markup.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36 Codeply';
|
|
11
|
-
const MAX_BYTES = 5 * 1024 * 1024;
|
|
12
|
-
const FETCH_TIMEOUT_MS = 30000;
|
|
13
|
-
const SEARCH_TIMEOUT_MS = 25000;
|
|
14
|
-
|
|
15
|
-
const ENTITIES = { amp: '&', lt: '<', gt: '>', quot: '"', apos: "'", nbsp: ' ', ndash: '-', mdash: '-', hellip: '...', rsquo: "'", lsquo: "'", rdquo: '"', ldquo: '"', copy: '(c)' };
|
|
16
|
-
|
|
17
|
-
function decodeEntities(s) {
|
|
18
|
-
return s
|
|
19
|
-
.replace(/&#(\d+);/g, (_, n) => { try { return String.fromCodePoint(Number(n)); } catch { return ''; } })
|
|
20
|
-
.replace(/&#x([0-9a-f]+);/gi, (_, n) => { try { return String.fromCodePoint(parseInt(n, 16)); } catch { return ''; } })
|
|
21
|
-
.replace(/&([a-z]+);/gi, (m, n) => (ENTITIES[n.toLowerCase()] ?? m));
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** HTML -> readable text: headings, links, lists, code and paragraphs survive; the rest goes. */
|
|
25
|
-
function htmlToText(html, baseUrl) {
|
|
26
|
-
let s = String(html);
|
|
27
|
-
s = s.replace(/<!--[\s\S]*?-->/g, '');
|
|
28
|
-
s = s.replace(/<(script|style|noscript|template|svg|iframe|head)\b[\s\S]*?<\/\1>/gi, '');
|
|
29
|
-
s = s.replace(/<pre\b[^>]*>([\s\S]*?)<\/pre>/gi, (_, code) => '\n```\n' + code.replace(/<[^>]+>/g, '') + '\n```\n');
|
|
30
|
-
s = s.replace(/<code\b[^>]*>([\s\S]*?)<\/code>/gi, (_, c) => '`' + c.replace(/<[^>]+>/g, '') + '`');
|
|
31
|
-
s = s.replace(/<h([1-6])\b[^>]*>([\s\S]*?)<\/h\1>/gi, (_, n, t) => `\n\n${'#'.repeat(Number(n))} ${t.replace(/<[^>]+>/g, '').trim()}\n\n`);
|
|
32
|
-
s = s.replace(/<a\b[^>]*href\s*=\s*["']([^"']*)["'][^>]*>([\s\S]*?)<\/a>/gi, (_, href, t) => {
|
|
33
|
-
const label = t.replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();
|
|
34
|
-
if (!label) return '';
|
|
35
|
-
if (!href || href.startsWith('#') || /^(javascript|mailto):/i.test(href)) return label;
|
|
36
|
-
let abs = href;
|
|
37
|
-
try { abs = new URL(href, baseUrl).href; } catch {}
|
|
38
|
-
return `[${label}](${abs})`;
|
|
39
|
-
});
|
|
40
|
-
s = s.replace(/<li\b[^>]*>/gi, '\n- ');
|
|
41
|
-
s = s.replace(/<br\s*\/?>/gi, '\n');
|
|
42
|
-
s = s.replace(/<\/(p|div|section|article|header|footer|main|nav|ul|ol|table|tr|blockquote|figure)>/gi, '\n');
|
|
43
|
-
s = s.replace(/<\/t[dh]>/gi, ' | ');
|
|
44
|
-
s = s.replace(/<[^>]+>/g, '');
|
|
45
|
-
s = decodeEntities(s);
|
|
46
|
-
s = s.replace(/[ \t\f\v]+/g, ' ').replace(/ *\n */g, '\n').replace(/\n{3,}/g, '\n\n');
|
|
47
|
-
return s.trim();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* @param {string} url
|
|
52
|
-
* @param {{format?:'text'|'html'|'raw', signal?:AbortSignal, maxChars?:number}} [opts]
|
|
53
|
-
* @returns {Promise<{ok:boolean, text?:string, error?:string, status?:number, contentType?:string, finalUrl?:string}>}
|
|
54
|
-
*/
|
|
55
|
-
async function fetchPage(url, opts = {}) {
|
|
56
|
-
let parsed;
|
|
57
|
-
try { parsed = new URL(url); } catch { return { ok: false, error: `Not a valid URL: ${url}` }; }
|
|
58
|
-
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return { ok: false, error: 'web_fetch only supports http:// and https:// URLs.' };
|
|
59
|
-
const signals = [AbortSignal.timeout(FETCH_TIMEOUT_MS)];
|
|
60
|
-
if (opts.signal) signals.push(opts.signal);
|
|
61
|
-
let res;
|
|
62
|
-
try {
|
|
63
|
-
res = await fetch(parsed.href, {
|
|
64
|
-
redirect: 'follow',
|
|
65
|
-
signal: AbortSignal.any(signals),
|
|
66
|
-
headers: { 'User-Agent': UA, Accept: 'text/html,application/xhtml+xml,application/json,text/plain,text/markdown;q=0.9,*/*;q=0.5', 'Accept-Language': 'en-US,en;q=0.9' },
|
|
67
|
-
});
|
|
68
|
-
} catch (e) {
|
|
69
|
-
return { ok: false, error: `Could not reach ${parsed.href}: ${e.name === 'TimeoutError' ? 'timed out' : e.message}` };
|
|
70
|
-
}
|
|
71
|
-
if (!res.ok) return { ok: false, status: res.status, error: `${parsed.href} returned HTTP ${res.status}.` };
|
|
72
|
-
const contentType = (res.headers.get('content-type') || '').split(';')[0].trim().toLowerCase();
|
|
73
|
-
const declared = Number(res.headers.get('content-length') || 0);
|
|
74
|
-
if (declared > MAX_BYTES) return { ok: false, error: `The page is ${(declared / 1e6).toFixed(1)}MB, over the ${MAX_BYTES / 1e6}MB limit.` };
|
|
75
|
-
if (/^(image|video|audio)\//.test(contentType) || /^application\/(zip|octet-stream|pdf|gzip)/.test(contentType)) {
|
|
76
|
-
return { ok: false, contentType, error: `${parsed.href} is ${contentType}, not text. Use fetch_image for images or run curl to download a file.` };
|
|
77
|
-
}
|
|
78
|
-
let buf;
|
|
79
|
-
try { buf = Buffer.from(await res.arrayBuffer()); } catch (e) { return { ok: false, error: `Failed reading ${parsed.href}: ${e.message}` }; }
|
|
80
|
-
if (buf.length > MAX_BYTES) return { ok: false, error: `The page is over the ${MAX_BYTES / 1e6}MB limit.` };
|
|
81
|
-
const body = buf.toString('utf8');
|
|
82
|
-
const format = opts.format || 'text';
|
|
83
|
-
const isHtml = contentType.includes('html') || (!contentType && /^\s*<(!doctype|html)/i.test(body));
|
|
84
|
-
let text = body;
|
|
85
|
-
if (isHtml && format === 'text') text = htmlToText(body, res.url || parsed.href);
|
|
86
|
-
else if (contentType.includes('json') && format !== 'raw') {
|
|
87
|
-
try { text = JSON.stringify(JSON.parse(body), null, 2); } catch {}
|
|
88
|
-
}
|
|
89
|
-
return { ok: true, text, contentType, finalUrl: res.url || parsed.href, status: res.status };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// ─── Search ──────────────────────────────────────────────────────────────────
|
|
93
|
-
|
|
94
|
-
async function exaSearch(query, num, signal) {
|
|
95
|
-
const signals = [AbortSignal.timeout(SEARCH_TIMEOUT_MS)];
|
|
96
|
-
if (signal) signals.push(signal);
|
|
97
|
-
const res = await fetch('https://mcp.exa.ai/mcp', {
|
|
98
|
-
method: 'POST',
|
|
99
|
-
signal: AbortSignal.any(signals),
|
|
100
|
-
headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/event-stream' },
|
|
101
|
-
body: JSON.stringify({
|
|
102
|
-
jsonrpc: '2.0', id: 1, method: 'tools/call',
|
|
103
|
-
params: { name: 'web_search_exa', arguments: { query, numResults: num, type: 'auto', livecrawl: 'fallback' } },
|
|
104
|
-
}),
|
|
105
|
-
});
|
|
106
|
-
if (!res.ok) throw new Error(`Exa returned HTTP ${res.status}`);
|
|
107
|
-
const raw = await res.text();
|
|
108
|
-
// The reply is either plain JSON or an SSE stream of "data: {...}" lines.
|
|
109
|
-
const payloads = [];
|
|
110
|
-
if (raw.trimStart().startsWith('{')) payloads.push(raw);
|
|
111
|
-
else for (const line of raw.split('\n')) if (line.startsWith('data:')) payloads.push(line.slice(5).trim());
|
|
112
|
-
for (const p of payloads) {
|
|
113
|
-
let j;
|
|
114
|
-
try { j = JSON.parse(p); } catch { continue; }
|
|
115
|
-
if (j.error) throw new Error(j.error.message || 'Exa error');
|
|
116
|
-
const text = (j.result?.content || []).filter((c) => c.type === 'text').map((c) => c.text).join('\n').trim();
|
|
117
|
-
if (text) return text;
|
|
118
|
-
}
|
|
119
|
-
throw new Error('Exa returned no results');
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async function ddgSearch(query, num, signal) {
|
|
123
|
-
const signals = [AbortSignal.timeout(SEARCH_TIMEOUT_MS)];
|
|
124
|
-
if (signal) signals.push(signal);
|
|
125
|
-
const res = await fetch('https://html.duckduckgo.com/html/', {
|
|
126
|
-
method: 'POST',
|
|
127
|
-
signal: AbortSignal.any(signals),
|
|
128
|
-
headers: { 'User-Agent': UA, 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
129
|
-
body: `q=${encodeURIComponent(query)}`,
|
|
130
|
-
});
|
|
131
|
-
if (!res.ok) throw new Error(`DuckDuckGo returned HTTP ${res.status}`);
|
|
132
|
-
const html = await res.text();
|
|
133
|
-
const out = [];
|
|
134
|
-
const re = /<a[^>]+class="result__a"[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>[\s\S]*?(?:<a[^>]+class="result__snippet"[^>]*>([\s\S]*?)<\/a>)?/gi;
|
|
135
|
-
let m;
|
|
136
|
-
while ((m = re.exec(html)) && out.length < num) {
|
|
137
|
-
let href = decodeEntities(m[1]);
|
|
138
|
-
const u = /[?&]uddg=([^&]+)/.exec(href);
|
|
139
|
-
if (u) { try { href = decodeURIComponent(u[1]); } catch {} }
|
|
140
|
-
const title = decodeEntities(m[2].replace(/<[^>]+>/g, '')).trim();
|
|
141
|
-
const snippet = decodeEntities((m[3] || '').replace(/<[^>]+>/g, '')).trim();
|
|
142
|
-
if (title && /^https?:/.test(href)) out.push(`Title: ${title}\nURL: ${href}${snippet ? `\n${snippet}` : ''}`);
|
|
143
|
-
}
|
|
144
|
-
if (!out.length) throw new Error('DuckDuckGo returned no results');
|
|
145
|
-
return out.join('\n\n');
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// ─── Image search ───────────────────────────────────────────────────────────
|
|
149
|
-
// Real photos from across the web, like a Google image search: DuckDuckGo's
|
|
150
|
-
// image results (no key), with Wikimedia Commons (free to use) as a fallback.
|
|
151
|
-
|
|
152
|
-
// Stock sites whose previews carry a watermark: never useful on a real page.
|
|
153
|
-
const WATERMARKED = /(dreamstime|shutterstock|alamy|istockphoto|gettyimages|depositphotos|123rf|stock\.adobe|bigstockphoto|canstockphoto|agefotostock|vectorstock|pond5|featurepics)\./i;
|
|
154
|
-
|
|
155
|
-
async function ddgImages(query, num, signal) {
|
|
156
|
-
const signals = [AbortSignal.timeout(SEARCH_TIMEOUT_MS)];
|
|
157
|
-
if (signal) signals.push(signal);
|
|
158
|
-
const page = await fetch(`https://duckduckgo.com/?q=${encodeURIComponent(query)}&iax=images&ia=images`, { signal: AbortSignal.any(signals), headers: { 'User-Agent': UA } });
|
|
159
|
-
const vqd = (/vqd=["']?([\d-]+)/.exec(await page.text()) || [])[1];
|
|
160
|
-
if (!vqd) throw new Error('DuckDuckGo images did not answer');
|
|
161
|
-
const res = await fetch(`https://duckduckgo.com/i.js?l=us-en&o=json&q=${encodeURIComponent(query)}&vqd=${vqd}&f=,,,,,&p=1`, {
|
|
162
|
-
signal: AbortSignal.any(signals), headers: { 'User-Agent': UA, Referer: 'https://duckduckgo.com/' },
|
|
163
|
-
});
|
|
164
|
-
if (!res.ok) throw new Error(`DuckDuckGo images returned HTTP ${res.status}`);
|
|
165
|
-
const j = await res.json();
|
|
166
|
-
const out = (j.results || [])
|
|
167
|
-
.filter((r) => /^https:\/\//.test(r.image || '') && (r.width || 0) >= 600 && !WATERMARKED.test(`${r.image} ${r.url || ''}`))
|
|
168
|
-
.slice(0, num)
|
|
169
|
-
.map((r) => ({ title: String(r.title || '').slice(0, 120), url: r.image, width: r.width, height: r.height, page: r.url || '' }));
|
|
170
|
-
if (!out.length) throw new Error('DuckDuckGo images found nothing');
|
|
171
|
-
return out;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async function commonsImages(query, num, signal) {
|
|
175
|
-
const signals = [AbortSignal.timeout(SEARCH_TIMEOUT_MS)];
|
|
176
|
-
if (signal) signals.push(signal);
|
|
177
|
-
const u = `https://commons.wikimedia.org/w/api.php?action=query&format=json&generator=search&gsrnamespace=6&gsrlimit=${num * 2}` +
|
|
178
|
-
`&gsrsearch=${encodeURIComponent(query + ' filetype:bitmap')}&prop=imageinfo&iiprop=url|size&iiurlwidth=1600`;
|
|
179
|
-
const res = await fetch(u, { signal: AbortSignal.any(signals), headers: { 'User-Agent': UA } });
|
|
180
|
-
if (!res.ok) throw new Error(`Wikimedia returned HTTP ${res.status}`);
|
|
181
|
-
const pages = Object.values((await res.json()).query?.pages || {});
|
|
182
|
-
const out = pages.map((p) => ({ p, i: (p.imageinfo || [])[0] })).filter(({ i }) => i && i.width >= 600)
|
|
183
|
-
.slice(0, num)
|
|
184
|
-
.map(({ p, i }) => ({ title: String(p.title || '').replace(/^File:/, '').slice(0, 120), url: i.thumburl || i.url, width: i.thumbwidth || i.width, height: i.thumbheight || i.height, page: i.descriptionurl || '' }));
|
|
185
|
-
if (!out.length) throw new Error('Wikimedia Commons found nothing');
|
|
186
|
-
return out;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/** @returns {Promise<{ok:boolean, images?:object[], error?:string, via?:string}>} */
|
|
190
|
-
async function imageSearch(query, opts = {}) {
|
|
191
|
-
const num = Math.min(Math.max(Number(opts.num) || 8, 1), 16);
|
|
192
|
-
const errors = [];
|
|
193
|
-
try { return { ok: true, images: await ddgImages(query, num, opts.signal), via: 'web' }; } catch (e) { errors.push(e.message); }
|
|
194
|
-
if (opts.signal?.aborted) return { ok: false, error: 'Search cancelled.' };
|
|
195
|
-
try { return { ok: true, images: await commonsImages(query, num, opts.signal), via: 'wikimedia' }; } catch (e) { errors.push(e.message); }
|
|
196
|
-
return { ok: false, error: `Image search failed (${errors.join('; ')}).` };
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/** @returns {Promise<{ok:boolean, text?:string, error?:string, via?:string}>} */
|
|
200
|
-
async function webSearch(query, opts = {}) {
|
|
201
|
-
const num = Math.min(Math.max(Number(opts.num) || 8, 1), 20);
|
|
202
|
-
const errors = [];
|
|
203
|
-
try { return { ok: true, text: await exaSearch(query, num, opts.signal), via: 'exa' }; } catch (e) { errors.push(e.message); }
|
|
204
|
-
if (opts.signal?.aborted) return { ok: false, error: 'Search cancelled.' };
|
|
205
|
-
try { return { ok: true, text: await ddgSearch(query, num, opts.signal), via: 'duckduckgo' }; } catch (e) { errors.push(e.message); }
|
|
206
|
-
return { ok: false, error: `Web search failed (${errors.join('; ')}).` };
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
module.exports = { fetchPage, webSearch, imageSearch, htmlToText };
|
|
1
|
+
/**
|
|
2
|
+
* Web access for the agent: fetch a page as readable text, and search the web.
|
|
3
|
+
*
|
|
4
|
+
* Search uses Exa's public MCP endpoint (the same one opencode's websearch tool
|
|
5
|
+
* talks to; no key needed) and falls back to DuckDuckGo's HTML results when
|
|
6
|
+
* Exa is unreachable. Fetching converts HTML to plain markdown-ish text so the
|
|
7
|
+
* model gets the content, not the markup.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36 Codeply';
|
|
11
|
+
const MAX_BYTES = 5 * 1024 * 1024;
|
|
12
|
+
const FETCH_TIMEOUT_MS = 30000;
|
|
13
|
+
const SEARCH_TIMEOUT_MS = 25000;
|
|
14
|
+
|
|
15
|
+
const ENTITIES = { amp: '&', lt: '<', gt: '>', quot: '"', apos: "'", nbsp: ' ', ndash: '-', mdash: '-', hellip: '...', rsquo: "'", lsquo: "'", rdquo: '"', ldquo: '"', copy: '(c)' };
|
|
16
|
+
|
|
17
|
+
function decodeEntities(s) {
|
|
18
|
+
return s
|
|
19
|
+
.replace(/&#(\d+);/g, (_, n) => { try { return String.fromCodePoint(Number(n)); } catch { return ''; } })
|
|
20
|
+
.replace(/&#x([0-9a-f]+);/gi, (_, n) => { try { return String.fromCodePoint(parseInt(n, 16)); } catch { return ''; } })
|
|
21
|
+
.replace(/&([a-z]+);/gi, (m, n) => (ENTITIES[n.toLowerCase()] ?? m));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** HTML -> readable text: headings, links, lists, code and paragraphs survive; the rest goes. */
|
|
25
|
+
function htmlToText(html, baseUrl) {
|
|
26
|
+
let s = String(html);
|
|
27
|
+
s = s.replace(/<!--[\s\S]*?-->/g, '');
|
|
28
|
+
s = s.replace(/<(script|style|noscript|template|svg|iframe|head)\b[\s\S]*?<\/\1>/gi, '');
|
|
29
|
+
s = s.replace(/<pre\b[^>]*>([\s\S]*?)<\/pre>/gi, (_, code) => '\n```\n' + code.replace(/<[^>]+>/g, '') + '\n```\n');
|
|
30
|
+
s = s.replace(/<code\b[^>]*>([\s\S]*?)<\/code>/gi, (_, c) => '`' + c.replace(/<[^>]+>/g, '') + '`');
|
|
31
|
+
s = s.replace(/<h([1-6])\b[^>]*>([\s\S]*?)<\/h\1>/gi, (_, n, t) => `\n\n${'#'.repeat(Number(n))} ${t.replace(/<[^>]+>/g, '').trim()}\n\n`);
|
|
32
|
+
s = s.replace(/<a\b[^>]*href\s*=\s*["']([^"']*)["'][^>]*>([\s\S]*?)<\/a>/gi, (_, href, t) => {
|
|
33
|
+
const label = t.replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();
|
|
34
|
+
if (!label) return '';
|
|
35
|
+
if (!href || href.startsWith('#') || /^(javascript|mailto):/i.test(href)) return label;
|
|
36
|
+
let abs = href;
|
|
37
|
+
try { abs = new URL(href, baseUrl).href; } catch {}
|
|
38
|
+
return `[${label}](${abs})`;
|
|
39
|
+
});
|
|
40
|
+
s = s.replace(/<li\b[^>]*>/gi, '\n- ');
|
|
41
|
+
s = s.replace(/<br\s*\/?>/gi, '\n');
|
|
42
|
+
s = s.replace(/<\/(p|div|section|article|header|footer|main|nav|ul|ol|table|tr|blockquote|figure)>/gi, '\n');
|
|
43
|
+
s = s.replace(/<\/t[dh]>/gi, ' | ');
|
|
44
|
+
s = s.replace(/<[^>]+>/g, '');
|
|
45
|
+
s = decodeEntities(s);
|
|
46
|
+
s = s.replace(/[ \t\f\v]+/g, ' ').replace(/ *\n */g, '\n').replace(/\n{3,}/g, '\n\n');
|
|
47
|
+
return s.trim();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {string} url
|
|
52
|
+
* @param {{format?:'text'|'html'|'raw', signal?:AbortSignal, maxChars?:number}} [opts]
|
|
53
|
+
* @returns {Promise<{ok:boolean, text?:string, error?:string, status?:number, contentType?:string, finalUrl?:string}>}
|
|
54
|
+
*/
|
|
55
|
+
async function fetchPage(url, opts = {}) {
|
|
56
|
+
let parsed;
|
|
57
|
+
try { parsed = new URL(url); } catch { return { ok: false, error: `Not a valid URL: ${url}` }; }
|
|
58
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return { ok: false, error: 'web_fetch only supports http:// and https:// URLs.' };
|
|
59
|
+
const signals = [AbortSignal.timeout(FETCH_TIMEOUT_MS)];
|
|
60
|
+
if (opts.signal) signals.push(opts.signal);
|
|
61
|
+
let res;
|
|
62
|
+
try {
|
|
63
|
+
res = await fetch(parsed.href, {
|
|
64
|
+
redirect: 'follow',
|
|
65
|
+
signal: AbortSignal.any(signals),
|
|
66
|
+
headers: { 'User-Agent': UA, Accept: 'text/html,application/xhtml+xml,application/json,text/plain,text/markdown;q=0.9,*/*;q=0.5', 'Accept-Language': 'en-US,en;q=0.9' },
|
|
67
|
+
});
|
|
68
|
+
} catch (e) {
|
|
69
|
+
return { ok: false, error: `Could not reach ${parsed.href}: ${e.name === 'TimeoutError' ? 'timed out' : e.message}` };
|
|
70
|
+
}
|
|
71
|
+
if (!res.ok) return { ok: false, status: res.status, error: `${parsed.href} returned HTTP ${res.status}.` };
|
|
72
|
+
const contentType = (res.headers.get('content-type') || '').split(';')[0].trim().toLowerCase();
|
|
73
|
+
const declared = Number(res.headers.get('content-length') || 0);
|
|
74
|
+
if (declared > MAX_BYTES) return { ok: false, error: `The page is ${(declared / 1e6).toFixed(1)}MB, over the ${MAX_BYTES / 1e6}MB limit.` };
|
|
75
|
+
if (/^(image|video|audio)\//.test(contentType) || /^application\/(zip|octet-stream|pdf|gzip)/.test(contentType)) {
|
|
76
|
+
return { ok: false, contentType, error: `${parsed.href} is ${contentType}, not text. Use fetch_image for images or run curl to download a file.` };
|
|
77
|
+
}
|
|
78
|
+
let buf;
|
|
79
|
+
try { buf = Buffer.from(await res.arrayBuffer()); } catch (e) { return { ok: false, error: `Failed reading ${parsed.href}: ${e.message}` }; }
|
|
80
|
+
if (buf.length > MAX_BYTES) return { ok: false, error: `The page is over the ${MAX_BYTES / 1e6}MB limit.` };
|
|
81
|
+
const body = buf.toString('utf8');
|
|
82
|
+
const format = opts.format || 'text';
|
|
83
|
+
const isHtml = contentType.includes('html') || (!contentType && /^\s*<(!doctype|html)/i.test(body));
|
|
84
|
+
let text = body;
|
|
85
|
+
if (isHtml && format === 'text') text = htmlToText(body, res.url || parsed.href);
|
|
86
|
+
else if (contentType.includes('json') && format !== 'raw') {
|
|
87
|
+
try { text = JSON.stringify(JSON.parse(body), null, 2); } catch {}
|
|
88
|
+
}
|
|
89
|
+
return { ok: true, text, contentType, finalUrl: res.url || parsed.href, status: res.status };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ─── Search ──────────────────────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
async function exaSearch(query, num, signal) {
|
|
95
|
+
const signals = [AbortSignal.timeout(SEARCH_TIMEOUT_MS)];
|
|
96
|
+
if (signal) signals.push(signal);
|
|
97
|
+
const res = await fetch('https://mcp.exa.ai/mcp', {
|
|
98
|
+
method: 'POST',
|
|
99
|
+
signal: AbortSignal.any(signals),
|
|
100
|
+
headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/event-stream' },
|
|
101
|
+
body: JSON.stringify({
|
|
102
|
+
jsonrpc: '2.0', id: 1, method: 'tools/call',
|
|
103
|
+
params: { name: 'web_search_exa', arguments: { query, numResults: num, type: 'auto', livecrawl: 'fallback' } },
|
|
104
|
+
}),
|
|
105
|
+
});
|
|
106
|
+
if (!res.ok) throw new Error(`Exa returned HTTP ${res.status}`);
|
|
107
|
+
const raw = await res.text();
|
|
108
|
+
// The reply is either plain JSON or an SSE stream of "data: {...}" lines.
|
|
109
|
+
const payloads = [];
|
|
110
|
+
if (raw.trimStart().startsWith('{')) payloads.push(raw);
|
|
111
|
+
else for (const line of raw.split('\n')) if (line.startsWith('data:')) payloads.push(line.slice(5).trim());
|
|
112
|
+
for (const p of payloads) {
|
|
113
|
+
let j;
|
|
114
|
+
try { j = JSON.parse(p); } catch { continue; }
|
|
115
|
+
if (j.error) throw new Error(j.error.message || 'Exa error');
|
|
116
|
+
const text = (j.result?.content || []).filter((c) => c.type === 'text').map((c) => c.text).join('\n').trim();
|
|
117
|
+
if (text) return text;
|
|
118
|
+
}
|
|
119
|
+
throw new Error('Exa returned no results');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function ddgSearch(query, num, signal) {
|
|
123
|
+
const signals = [AbortSignal.timeout(SEARCH_TIMEOUT_MS)];
|
|
124
|
+
if (signal) signals.push(signal);
|
|
125
|
+
const res = await fetch('https://html.duckduckgo.com/html/', {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
signal: AbortSignal.any(signals),
|
|
128
|
+
headers: { 'User-Agent': UA, 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
129
|
+
body: `q=${encodeURIComponent(query)}`,
|
|
130
|
+
});
|
|
131
|
+
if (!res.ok) throw new Error(`DuckDuckGo returned HTTP ${res.status}`);
|
|
132
|
+
const html = await res.text();
|
|
133
|
+
const out = [];
|
|
134
|
+
const re = /<a[^>]+class="result__a"[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>[\s\S]*?(?:<a[^>]+class="result__snippet"[^>]*>([\s\S]*?)<\/a>)?/gi;
|
|
135
|
+
let m;
|
|
136
|
+
while ((m = re.exec(html)) && out.length < num) {
|
|
137
|
+
let href = decodeEntities(m[1]);
|
|
138
|
+
const u = /[?&]uddg=([^&]+)/.exec(href);
|
|
139
|
+
if (u) { try { href = decodeURIComponent(u[1]); } catch {} }
|
|
140
|
+
const title = decodeEntities(m[2].replace(/<[^>]+>/g, '')).trim();
|
|
141
|
+
const snippet = decodeEntities((m[3] || '').replace(/<[^>]+>/g, '')).trim();
|
|
142
|
+
if (title && /^https?:/.test(href)) out.push(`Title: ${title}\nURL: ${href}${snippet ? `\n${snippet}` : ''}`);
|
|
143
|
+
}
|
|
144
|
+
if (!out.length) throw new Error('DuckDuckGo returned no results');
|
|
145
|
+
return out.join('\n\n');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ─── Image search ───────────────────────────────────────────────────────────
|
|
149
|
+
// Real photos from across the web, like a Google image search: DuckDuckGo's
|
|
150
|
+
// image results (no key), with Wikimedia Commons (free to use) as a fallback.
|
|
151
|
+
|
|
152
|
+
// Stock sites whose previews carry a watermark: never useful on a real page.
|
|
153
|
+
const WATERMARKED = /(dreamstime|shutterstock|alamy|istockphoto|gettyimages|depositphotos|123rf|stock\.adobe|bigstockphoto|canstockphoto|agefotostock|vectorstock|pond5|featurepics)\./i;
|
|
154
|
+
|
|
155
|
+
async function ddgImages(query, num, signal) {
|
|
156
|
+
const signals = [AbortSignal.timeout(SEARCH_TIMEOUT_MS)];
|
|
157
|
+
if (signal) signals.push(signal);
|
|
158
|
+
const page = await fetch(`https://duckduckgo.com/?q=${encodeURIComponent(query)}&iax=images&ia=images`, { signal: AbortSignal.any(signals), headers: { 'User-Agent': UA } });
|
|
159
|
+
const vqd = (/vqd=["']?([\d-]+)/.exec(await page.text()) || [])[1];
|
|
160
|
+
if (!vqd) throw new Error('DuckDuckGo images did not answer');
|
|
161
|
+
const res = await fetch(`https://duckduckgo.com/i.js?l=us-en&o=json&q=${encodeURIComponent(query)}&vqd=${vqd}&f=,,,,,&p=1`, {
|
|
162
|
+
signal: AbortSignal.any(signals), headers: { 'User-Agent': UA, Referer: 'https://duckduckgo.com/' },
|
|
163
|
+
});
|
|
164
|
+
if (!res.ok) throw new Error(`DuckDuckGo images returned HTTP ${res.status}`);
|
|
165
|
+
const j = await res.json();
|
|
166
|
+
const out = (j.results || [])
|
|
167
|
+
.filter((r) => /^https:\/\//.test(r.image || '') && (r.width || 0) >= 600 && !WATERMARKED.test(`${r.image} ${r.url || ''}`))
|
|
168
|
+
.slice(0, num)
|
|
169
|
+
.map((r) => ({ title: String(r.title || '').slice(0, 120), url: r.image, width: r.width, height: r.height, page: r.url || '' }));
|
|
170
|
+
if (!out.length) throw new Error('DuckDuckGo images found nothing');
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function commonsImages(query, num, signal) {
|
|
175
|
+
const signals = [AbortSignal.timeout(SEARCH_TIMEOUT_MS)];
|
|
176
|
+
if (signal) signals.push(signal);
|
|
177
|
+
const u = `https://commons.wikimedia.org/w/api.php?action=query&format=json&generator=search&gsrnamespace=6&gsrlimit=${num * 2}` +
|
|
178
|
+
`&gsrsearch=${encodeURIComponent(query + ' filetype:bitmap')}&prop=imageinfo&iiprop=url|size&iiurlwidth=1600`;
|
|
179
|
+
const res = await fetch(u, { signal: AbortSignal.any(signals), headers: { 'User-Agent': UA } });
|
|
180
|
+
if (!res.ok) throw new Error(`Wikimedia returned HTTP ${res.status}`);
|
|
181
|
+
const pages = Object.values((await res.json()).query?.pages || {});
|
|
182
|
+
const out = pages.map((p) => ({ p, i: (p.imageinfo || [])[0] })).filter(({ i }) => i && i.width >= 600)
|
|
183
|
+
.slice(0, num)
|
|
184
|
+
.map(({ p, i }) => ({ title: String(p.title || '').replace(/^File:/, '').slice(0, 120), url: i.thumburl || i.url, width: i.thumbwidth || i.width, height: i.thumbheight || i.height, page: i.descriptionurl || '' }));
|
|
185
|
+
if (!out.length) throw new Error('Wikimedia Commons found nothing');
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** @returns {Promise<{ok:boolean, images?:object[], error?:string, via?:string}>} */
|
|
190
|
+
async function imageSearch(query, opts = {}) {
|
|
191
|
+
const num = Math.min(Math.max(Number(opts.num) || 8, 1), 16);
|
|
192
|
+
const errors = [];
|
|
193
|
+
try { return { ok: true, images: await ddgImages(query, num, opts.signal), via: 'web' }; } catch (e) { errors.push(e.message); }
|
|
194
|
+
if (opts.signal?.aborted) return { ok: false, error: 'Search cancelled.' };
|
|
195
|
+
try { return { ok: true, images: await commonsImages(query, num, opts.signal), via: 'wikimedia' }; } catch (e) { errors.push(e.message); }
|
|
196
|
+
return { ok: false, error: `Image search failed (${errors.join('; ')}).` };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** @returns {Promise<{ok:boolean, text?:string, error?:string, via?:string}>} */
|
|
200
|
+
async function webSearch(query, opts = {}) {
|
|
201
|
+
const num = Math.min(Math.max(Number(opts.num) || 8, 1), 20);
|
|
202
|
+
const errors = [];
|
|
203
|
+
try { return { ok: true, text: await exaSearch(query, num, opts.signal), via: 'exa' }; } catch (e) { errors.push(e.message); }
|
|
204
|
+
if (opts.signal?.aborted) return { ok: false, error: 'Search cancelled.' };
|
|
205
|
+
try { return { ok: true, text: await ddgSearch(query, num, opts.signal), via: 'duckduckgo' }; } catch (e) { errors.push(e.message); }
|
|
206
|
+
return { ok: false, error: `Web search failed (${errors.join('; ')}).` };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
module.exports = { fetchPage, webSearch, imageSearch, htmlToText };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeply-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Codeply from the terminal: an interactive AI coding agent, a local server (codeply serve), and a GitHub agent that runs in your own Actions runner.",
|
|
5
5
|
"main": "bin/codeply.js",
|
|
6
6
|
"bin": {
|
package/skills/SOURCE.md
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
# Where these skills came from
|
|
2
|
-
|
|
3
|
-
The skill library in this directory — all 281 of them — is vendored from
|
|
4
|
-
[affaan-m/ECC](https://github.com/affaan-m/ECC) ("Every Coding Copilot"), an
|
|
5
|
-
open-source collection of agent skills for Claude Code, Codex, and other
|
|
6
|
-
harnesses. It's their full `skills/` library, not a subset — every `SKILL.md`
|
|
7
|
-
and its `references/*.md` companions, unmodified.
|
|
8
|
-
|
|
9
|
-
Codeply loads its skill format directly from that project's convention: a
|
|
10
|
-
`SKILL.md` per skill with YAML frontmatter (`name`, `description`) and a
|
|
11
|
-
markdown body. See [lib/skills.js](../lib/skills.js) for the loader.
|
|
12
|
-
|
|
13
|
-
Only `SKILL.md` and `references/*.md` were vendored — each skill's
|
|
14
|
-
`agents/openai.yaml` (a tool schema for a different harness's native
|
|
15
|
-
function-calling) was left out, since Codeply calls skills through its own
|
|
16
|
-
text-based action protocol and has no use for it.
|
|
17
|
-
|
|
18
|
-
Not every one of the 281 is loaded into the agent's context by default —
|
|
19
|
-
`lib/skills.js` shows a curated "daily" subset (ECC's own `.agents/skills/`
|
|
20
|
-
curation of its full library) to keep the always-injected index small; the
|
|
21
|
-
rest are still fully installed and reachable with `list_skills <query>` or
|
|
22
|
-
`codeply skill show <name>`.
|
|
23
|
-
|
|
24
|
-
## License
|
|
25
|
-
|
|
26
|
-
ECC is MIT-licensed. Its license and copyright notice, reproduced here as MIT
|
|
27
|
-
requires when redistributing:
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
MIT License
|
|
31
|
-
|
|
32
|
-
Copyright (c) 2026 Affaan Mustafa
|
|
33
|
-
|
|
34
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
35
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
36
|
-
in the Software without restriction, including without limitation the rights
|
|
37
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
38
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
39
|
-
furnished to do so, subject to the following conditions:
|
|
40
|
-
|
|
41
|
-
The above copyright notice and this permission notice shall be included in all
|
|
42
|
-
copies or substantial portions of the Software.
|
|
43
|
-
|
|
44
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
45
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
46
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
47
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
48
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
49
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
50
|
-
SOFTWARE.
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Adding your own
|
|
54
|
-
|
|
55
|
-
Anything you drop in `~/.codeply/skills/<name>/SKILL.md` (same frontmatter
|
|
56
|
-
format) is picked up the same way, and takes priority over a bundled skill of
|
|
57
|
-
the same name. `codeply skill install <path-or-github-url>` automates that —
|
|
58
|
-
see `codeply skill --help`.
|
|
1
|
+
# Where these skills came from
|
|
2
|
+
|
|
3
|
+
The skill library in this directory — all 281 of them — is vendored from
|
|
4
|
+
[affaan-m/ECC](https://github.com/affaan-m/ECC) ("Every Coding Copilot"), an
|
|
5
|
+
open-source collection of agent skills for Claude Code, Codex, and other
|
|
6
|
+
harnesses. It's their full `skills/` library, not a subset — every `SKILL.md`
|
|
7
|
+
and its `references/*.md` companions, unmodified.
|
|
8
|
+
|
|
9
|
+
Codeply loads its skill format directly from that project's convention: a
|
|
10
|
+
`SKILL.md` per skill with YAML frontmatter (`name`, `description`) and a
|
|
11
|
+
markdown body. See [lib/skills.js](../lib/skills.js) for the loader.
|
|
12
|
+
|
|
13
|
+
Only `SKILL.md` and `references/*.md` were vendored — each skill's
|
|
14
|
+
`agents/openai.yaml` (a tool schema for a different harness's native
|
|
15
|
+
function-calling) was left out, since Codeply calls skills through its own
|
|
16
|
+
text-based action protocol and has no use for it.
|
|
17
|
+
|
|
18
|
+
Not every one of the 281 is loaded into the agent's context by default —
|
|
19
|
+
`lib/skills.js` shows a curated "daily" subset (ECC's own `.agents/skills/`
|
|
20
|
+
curation of its full library) to keep the always-injected index small; the
|
|
21
|
+
rest are still fully installed and reachable with `list_skills <query>` or
|
|
22
|
+
`codeply skill show <name>`.
|
|
23
|
+
|
|
24
|
+
## License
|
|
25
|
+
|
|
26
|
+
ECC is MIT-licensed. Its license and copyright notice, reproduced here as MIT
|
|
27
|
+
requires when redistributing:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
MIT License
|
|
31
|
+
|
|
32
|
+
Copyright (c) 2026 Affaan Mustafa
|
|
33
|
+
|
|
34
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
35
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
36
|
+
in the Software without restriction, including without limitation the rights
|
|
37
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
38
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
39
|
+
furnished to do so, subject to the following conditions:
|
|
40
|
+
|
|
41
|
+
The above copyright notice and this permission notice shall be included in all
|
|
42
|
+
copies or substantial portions of the Software.
|
|
43
|
+
|
|
44
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
45
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
46
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
47
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
48
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
49
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
50
|
+
SOFTWARE.
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Adding your own
|
|
54
|
+
|
|
55
|
+
Anything you drop in `~/.codeply/skills/<name>/SKILL.md` (same frontmatter
|
|
56
|
+
format) is picked up the same way, and takes priority over a bundled skill of
|
|
57
|
+
the same name. `codeply skill install <path-or-github-url>` automates that —
|
|
58
|
+
see `codeply skill --help`.
|