codeply-cli 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/THIRD_PARTY_NOTICES.md +65 -63
- package/bin/codeply.js +1 -1
- package/lib/agent.mjs +133 -24
- package/lib/ai.js +268 -9
- 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/bots.js +493 -16
- package/lib/chatgpt.js +282 -0
- package/lib/cloud-browser.mjs +143 -143
- package/lib/cloud.mjs +1186 -1105
- package/lib/commands.js +103 -103
- package/lib/config.js +83 -3
- 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/diagnostics.js +192 -192
- package/lib/edit-engine.js +364 -364
- package/lib/github-agent.mjs +311 -311
- package/lib/history.js +75 -75
- package/lib/mail-watch.js +504 -0
- package/lib/mcp.js +258 -258
- package/lib/model-sync.js +114 -0
- package/lib/native-tools.mjs +16 -1
- package/lib/oauth-connectors.js +146 -24
- package/lib/permissions.js +109 -109
- package/lib/plugins.js +353 -353
- package/lib/publish.js +916 -0
- package/lib/reach.js +67 -0
- package/lib/server.mjs +457 -457
- package/lib/session-db.js +220 -220
- package/lib/share.js +168 -168
- package/lib/skills.js +2 -0
- package/lib/snapshot.js +146 -146
- package/lib/telemetry.js +102 -0
- package/lib/terminal-hints.js +154 -154
- package/lib/tools.mjs +316 -14
- package/lib/typecheck-worker.js +147 -147
- package/lib/web-tools.js +209 -158
- package/package.json +49 -49
- 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/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/publish-website/SKILL.md +76 -0
- 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/commands.js
CHANGED
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Custom slash commands: a markdown file per command, same layout opencode
|
|
3
|
-
* and Claude Code use, so existing ones can be copied over.
|
|
4
|
-
*
|
|
5
|
-
* ~/.codeply/commands/<name>.md (every project)
|
|
6
|
-
* <project>/.codeply/commands/<name>.md (this project; same name wins)
|
|
7
|
-
*
|
|
8
|
-
* ---
|
|
9
|
-
* description: Review the changed files for bugs
|
|
10
|
-
* mode: Plan # optional: Build | Plan | Ask
|
|
11
|
-
* ---
|
|
12
|
-
* Review $ARGUMENTS for correctness bugs. Cite file:line.
|
|
13
|
-
*
|
|
14
|
-
* In the body, $ARGUMENTS is everything typed after the command, $1 $2 ...
|
|
15
|
-
* are single words (the highest one takes the rest), and if the body has no
|
|
16
|
-
* placeholder at all the arguments are appended on their own line.
|
|
17
|
-
* Sub-folders become part of the name: commands/git/pr.md is /git/pr.
|
|
18
|
-
* Installed plugins add theirs as /<plugin>:<name> (see plugins.js).
|
|
19
|
-
*/
|
|
20
|
-
const fs = require('fs');
|
|
21
|
-
const os = require('os');
|
|
22
|
-
const path = require('path');
|
|
23
|
-
const plugins = require('./plugins.js');
|
|
24
|
-
|
|
25
|
-
const MAX_BODY = 20000;
|
|
26
|
-
const RESERVED = new Set(['goal', 'g', 'skills', 'skill', 'skill-list']);
|
|
27
|
-
|
|
28
|
-
function parseFrontmatter(text) {
|
|
29
|
-
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text);
|
|
30
|
-
if (!m) return { meta: {}, body: text };
|
|
31
|
-
const meta = {};
|
|
32
|
-
for (const line of m[1].split(/\r?\n/)) {
|
|
33
|
-
const kv = /^([A-Za-z_-]+)\s*:\s*(.*)$/.exec(line);
|
|
34
|
-
if (kv) meta[kv[1].toLowerCase()] = kv[2].trim().replace(/^["']|["']$/g, '');
|
|
35
|
-
}
|
|
36
|
-
return { meta, body: text.slice(m[0].length) };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function scan(dir, prefix, out) {
|
|
40
|
-
let entries;
|
|
41
|
-
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
42
|
-
for (const e of entries) {
|
|
43
|
-
const full = path.join(dir, e.name);
|
|
44
|
-
if (e.isDirectory()) scan(full, `${prefix}${e.name}/`, out);
|
|
45
|
-
else if (e.isFile() && e.name.toLowerCase().endsWith('.md')) {
|
|
46
|
-
const name = `${prefix}${e.name.slice(0, -3)}`.toLowerCase().replace(/\s+/g, '-');
|
|
47
|
-
if (RESERVED.has(name)) continue;
|
|
48
|
-
try {
|
|
49
|
-
const { meta, body } = parseFrontmatter(fs.readFileSync(full, 'utf8').slice(0, MAX_BODY));
|
|
50
|
-
const mode = ['Build', 'Plan', 'Ask'].find((x) => x.toLowerCase() === String(meta.mode || '').toLowerCase());
|
|
51
|
-
out.set(name, {
|
|
52
|
-
name,
|
|
53
|
-
description: meta.description || body.trim().split('\n')[0].slice(0, 100),
|
|
54
|
-
mode: mode || null,
|
|
55
|
-
body: body.trim(),
|
|
56
|
-
file: full,
|
|
57
|
-
});
|
|
58
|
-
} catch {}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** @returns {Map<string, {name, description, mode, body, file}>} */
|
|
64
|
-
function loadCommands(cwd) {
|
|
65
|
-
const out = new Map();
|
|
66
|
-
for (const { plugin, dir } of plugins.commandDirs(cwd)) scan(dir, `${plugin}:`, out);
|
|
67
|
-
scan(path.join(os.homedir(), '.codeply', 'commands'), '', out);
|
|
68
|
-
if (cwd) scan(path.join(cwd, '.codeply', 'commands'), '', out);
|
|
69
|
-
return out;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** Fill a command's template with what was typed after it. */
|
|
73
|
-
function expand(cmd, argText) {
|
|
74
|
-
const args = String(argText || '').trim();
|
|
75
|
-
const words = args ? args.split(/\s+/) : [];
|
|
76
|
-
let body = cmd.body;
|
|
77
|
-
const nums = [...body.matchAll(/\$(\d+)/g)].map((m) => Number(m[1]));
|
|
78
|
-
const max = nums.length ? Math.max(...nums) : 0;
|
|
79
|
-
const used = /\$ARGUMENTS|\$\d+/.test(body);
|
|
80
|
-
body = body.replace(/\$(\d+)/g, (_, n) => {
|
|
81
|
-
const i = Number(n) - 1;
|
|
82
|
-
return Number(n) === max ? words.slice(i).join(' ') : (words[i] || '');
|
|
83
|
-
});
|
|
84
|
-
body = body.replace(/\$ARGUMENTS/g, args);
|
|
85
|
-
if (!used && args) body = `${body}\n\n${args}`;
|
|
86
|
-
return body.trim();
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* If `text` starts with a known custom command, the expanded prompt and the
|
|
91
|
-
* command; otherwise null. `/goal` and the app's built-ins are never matched.
|
|
92
|
-
*/
|
|
93
|
-
function resolve(text, cwd) {
|
|
94
|
-
const m = /^\/([A-Za-z0-9_./:-]+)(?:\s+([\s\S]*))?$/.exec(String(text || '').trim());
|
|
95
|
-
if (!m) return null;
|
|
96
|
-
const name = m[1].toLowerCase();
|
|
97
|
-
if (RESERVED.has(name)) return null;
|
|
98
|
-
const cmd = loadCommands(cwd).get(name);
|
|
99
|
-
if (!cmd) return null;
|
|
100
|
-
return { command: cmd, prompt: expand(cmd, m[2] || '') };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
module.exports = { loadCommands, expand, resolve, parseFrontmatter };
|
|
1
|
+
/**
|
|
2
|
+
* Custom slash commands: a markdown file per command, same layout opencode
|
|
3
|
+
* and Claude Code use, so existing ones can be copied over.
|
|
4
|
+
*
|
|
5
|
+
* ~/.codeply/commands/<name>.md (every project)
|
|
6
|
+
* <project>/.codeply/commands/<name>.md (this project; same name wins)
|
|
7
|
+
*
|
|
8
|
+
* ---
|
|
9
|
+
* description: Review the changed files for bugs
|
|
10
|
+
* mode: Plan # optional: Build | Plan | Ask
|
|
11
|
+
* ---
|
|
12
|
+
* Review $ARGUMENTS for correctness bugs. Cite file:line.
|
|
13
|
+
*
|
|
14
|
+
* In the body, $ARGUMENTS is everything typed after the command, $1 $2 ...
|
|
15
|
+
* are single words (the highest one takes the rest), and if the body has no
|
|
16
|
+
* placeholder at all the arguments are appended on their own line.
|
|
17
|
+
* Sub-folders become part of the name: commands/git/pr.md is /git/pr.
|
|
18
|
+
* Installed plugins add theirs as /<plugin>:<name> (see plugins.js).
|
|
19
|
+
*/
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const os = require('os');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const plugins = require('./plugins.js');
|
|
24
|
+
|
|
25
|
+
const MAX_BODY = 20000;
|
|
26
|
+
const RESERVED = new Set(['goal', 'g', 'skills', 'skill', 'skill-list']);
|
|
27
|
+
|
|
28
|
+
function parseFrontmatter(text) {
|
|
29
|
+
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text);
|
|
30
|
+
if (!m) return { meta: {}, body: text };
|
|
31
|
+
const meta = {};
|
|
32
|
+
for (const line of m[1].split(/\r?\n/)) {
|
|
33
|
+
const kv = /^([A-Za-z_-]+)\s*:\s*(.*)$/.exec(line);
|
|
34
|
+
if (kv) meta[kv[1].toLowerCase()] = kv[2].trim().replace(/^["']|["']$/g, '');
|
|
35
|
+
}
|
|
36
|
+
return { meta, body: text.slice(m[0].length) };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function scan(dir, prefix, out) {
|
|
40
|
+
let entries;
|
|
41
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
42
|
+
for (const e of entries) {
|
|
43
|
+
const full = path.join(dir, e.name);
|
|
44
|
+
if (e.isDirectory()) scan(full, `${prefix}${e.name}/`, out);
|
|
45
|
+
else if (e.isFile() && e.name.toLowerCase().endsWith('.md')) {
|
|
46
|
+
const name = `${prefix}${e.name.slice(0, -3)}`.toLowerCase().replace(/\s+/g, '-');
|
|
47
|
+
if (RESERVED.has(name)) continue;
|
|
48
|
+
try {
|
|
49
|
+
const { meta, body } = parseFrontmatter(fs.readFileSync(full, 'utf8').slice(0, MAX_BODY));
|
|
50
|
+
const mode = ['Build', 'Plan', 'Ask'].find((x) => x.toLowerCase() === String(meta.mode || '').toLowerCase());
|
|
51
|
+
out.set(name, {
|
|
52
|
+
name,
|
|
53
|
+
description: meta.description || body.trim().split('\n')[0].slice(0, 100),
|
|
54
|
+
mode: mode || null,
|
|
55
|
+
body: body.trim(),
|
|
56
|
+
file: full,
|
|
57
|
+
});
|
|
58
|
+
} catch {}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** @returns {Map<string, {name, description, mode, body, file}>} */
|
|
64
|
+
function loadCommands(cwd) {
|
|
65
|
+
const out = new Map();
|
|
66
|
+
for (const { plugin, dir } of plugins.commandDirs(cwd)) scan(dir, `${plugin}:`, out);
|
|
67
|
+
scan(path.join(os.homedir(), '.codeply', 'commands'), '', out);
|
|
68
|
+
if (cwd) scan(path.join(cwd, '.codeply', 'commands'), '', out);
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Fill a command's template with what was typed after it. */
|
|
73
|
+
function expand(cmd, argText) {
|
|
74
|
+
const args = String(argText || '').trim();
|
|
75
|
+
const words = args ? args.split(/\s+/) : [];
|
|
76
|
+
let body = cmd.body;
|
|
77
|
+
const nums = [...body.matchAll(/\$(\d+)/g)].map((m) => Number(m[1]));
|
|
78
|
+
const max = nums.length ? Math.max(...nums) : 0;
|
|
79
|
+
const used = /\$ARGUMENTS|\$\d+/.test(body);
|
|
80
|
+
body = body.replace(/\$(\d+)/g, (_, n) => {
|
|
81
|
+
const i = Number(n) - 1;
|
|
82
|
+
return Number(n) === max ? words.slice(i).join(' ') : (words[i] || '');
|
|
83
|
+
});
|
|
84
|
+
body = body.replace(/\$ARGUMENTS/g, args);
|
|
85
|
+
if (!used && args) body = `${body}\n\n${args}`;
|
|
86
|
+
return body.trim();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* If `text` starts with a known custom command, the expanded prompt and the
|
|
91
|
+
* command; otherwise null. `/goal` and the app's built-ins are never matched.
|
|
92
|
+
*/
|
|
93
|
+
function resolve(text, cwd) {
|
|
94
|
+
const m = /^\/([A-Za-z0-9_./:-]+)(?:\s+([\s\S]*))?$/.exec(String(text || '').trim());
|
|
95
|
+
if (!m) return null;
|
|
96
|
+
const name = m[1].toLowerCase();
|
|
97
|
+
if (RESERVED.has(name)) return null;
|
|
98
|
+
const cmd = loadCommands(cwd).get(name);
|
|
99
|
+
if (!cmd) return null;
|
|
100
|
+
return { command: cmd, prompt: expand(cmd, m[2] || '') };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = { loadCommands, expand, resolve, parseFrontmatter };
|
package/lib/config.js
CHANGED
|
@@ -98,8 +98,19 @@ function getConfig() {
|
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
// Research Mode (local Ollama or Ollama Cloud), see research-mode/.
|
|
102
|
+
const fr = file.research || {};
|
|
103
|
+
const research = {
|
|
104
|
+
enabled: fr.enabled === true,
|
|
105
|
+
mode: fr.mode === 'cloud' ? 'cloud' : 'local',
|
|
106
|
+
model: String(fr.model || ''),
|
|
107
|
+
apiKey: process.env.OLLAMA_API_KEY || fr.apiKey || '',
|
|
108
|
+
context: String(fr.context || ''),
|
|
109
|
+
};
|
|
110
|
+
|
|
101
111
|
return {
|
|
102
112
|
provider,
|
|
113
|
+
research,
|
|
103
114
|
ollama: {
|
|
104
115
|
host: (process.env.OLLAMA_HOST || fileOllama.host || DEFAULTS.ollama.host).replace(/\/+$/, ''),
|
|
105
116
|
model: process.env.OLLAMA_MODEL || fileOllama.model || DEFAULTS.ollama.model,
|
|
@@ -121,7 +132,7 @@ function saveConfig(patch) {
|
|
|
121
132
|
const current = readFile();
|
|
122
133
|
if (lastReadFailed) return { ok: false, error: `${configPath} is not valid JSON. Fix or delete it, then try again.` };
|
|
123
134
|
const next = { ...current, ...patch };
|
|
124
|
-
for (const name of ['ollama', ...BYOK_PROVIDERS]) {
|
|
135
|
+
for (const name of ['ollama', 'research', ...BYOK_PROVIDERS]) {
|
|
125
136
|
if (current[name] || patch[name]) {
|
|
126
137
|
next[name] = { ...(current[name] || {}), ...(patch[name] || {}) };
|
|
127
138
|
}
|
|
@@ -209,7 +220,8 @@ function disconnectIntegration(name) {
|
|
|
209
220
|
// ever sent to the base URL the user typed in, never to Codeply.
|
|
210
221
|
//
|
|
211
222
|
// selectedModel: 'auto' | <model id>
|
|
212
|
-
// models: [{ id, name, kind: 'openai' | 'ollama', baseUrl, model, apiKey, createdAt
|
|
223
|
+
// models: [{ id, name, kind: 'openai' | 'ollama', baseUrl, model, apiKey, createdAt,
|
|
224
|
+
// phone?, phoneSyncedAt?, phoneUser?, phoneError? }] (phone sync: model-sync.js)
|
|
213
225
|
|
|
214
226
|
const AUTO_MODEL_ID = 'auto';
|
|
215
227
|
|
|
@@ -326,6 +338,12 @@ function saveModel(input) {
|
|
|
326
338
|
apiKey: input.apiKey === undefined || input.apiKey === null ? (existing?.apiKey || '') : String(input.apiKey).trim(),
|
|
327
339
|
createdAt: existing?.createdAt || Date.now(),
|
|
328
340
|
};
|
|
341
|
+
// "Use on my phone" (model-sync.js). phone: true/false once chosen, absent
|
|
342
|
+
// for models added before the option existed; the sync status fields are
|
|
343
|
+
// only ever written by setModelPhone.
|
|
344
|
+
const phone = typeof input.phone === 'boolean' ? input.phone : existing?.phone;
|
|
345
|
+
if (typeof phone === 'boolean') entry.phone = phone;
|
|
346
|
+
for (const k of PHONE_FIELDS) if (existing && existing[k] !== undefined) entry[k] = existing[k];
|
|
329
347
|
file.models = existing
|
|
330
348
|
? file.models.map((m) => (m.id === entry.id ? entry : m))
|
|
331
349
|
: [...file.models, entry];
|
|
@@ -333,8 +351,41 @@ function saveModel(input) {
|
|
|
333
351
|
return w.ok ? { ok: true, model: entry } : w;
|
|
334
352
|
}
|
|
335
353
|
|
|
354
|
+
const PHONE_FIELDS = ['phoneSyncedAt', 'phoneUser', 'phoneError'];
|
|
355
|
+
|
|
356
|
+
/** Updates a model's phone sync state: { phone?, phoneSyncedAt?, phoneUser?, phoneError? }. */
|
|
357
|
+
function setModelPhone(id, patch) {
|
|
358
|
+
const file = readModelsState();
|
|
359
|
+
const m = file.models.find((x) => x.id === id);
|
|
360
|
+
if (!m) return { ok: false, error: 'Unknown model.' };
|
|
361
|
+
for (const k of ['phone', ...PHONE_FIELDS]) if (patch[k] !== undefined) m[k] = patch[k];
|
|
362
|
+
const w = writeFile(file);
|
|
363
|
+
return w.ok ? { ok: true, model: m } : w;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Synced models removed here but not yet removed from the account (offline,
|
|
368
|
+
* signed out). Kept until the delete goes through, so a removed key never
|
|
369
|
+
* lingers in the cloud: [{ id, user }].
|
|
370
|
+
*/
|
|
371
|
+
function getPhoneDeletes() {
|
|
372
|
+
const list = readFile().phoneDeletes;
|
|
373
|
+
return Array.isArray(list) ? list.filter((d) => d && d.id) : [];
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function setPhoneDeletes(list) {
|
|
377
|
+
const file = readModelsState();
|
|
378
|
+
file.phoneDeletes = list;
|
|
379
|
+
return writeFile(file);
|
|
380
|
+
}
|
|
381
|
+
|
|
336
382
|
function deleteModel(id) {
|
|
337
383
|
const file = readModelsState();
|
|
384
|
+
const gone = file.models.find((m) => m.id === id);
|
|
385
|
+
if (gone && gone.phoneSyncedAt) {
|
|
386
|
+
const pending = Array.isArray(file.phoneDeletes) ? file.phoneDeletes : [];
|
|
387
|
+
if (!pending.some((d) => d.id === id)) file.phoneDeletes = [...pending, { id, user: gone.phoneUser || null }];
|
|
388
|
+
}
|
|
338
389
|
file.models = file.models.filter((m) => m.id !== id);
|
|
339
390
|
if (file.selectedModel === id) file.selectedModel = AUTO_MODEL_ID;
|
|
340
391
|
return writeFile(file);
|
|
@@ -347,6 +398,34 @@ function selectModel(id) {
|
|
|
347
398
|
return writeFile(file);
|
|
348
399
|
}
|
|
349
400
|
|
|
401
|
+
/**
|
|
402
|
+
* The models the signed-in ChatGPT plan offers (see chatgpt.js), kept as
|
|
403
|
+
* kind "chatgpt" entries so the pickers, routing and the phone all treat them
|
|
404
|
+
* like any other model. No key is stored: chatgpt.js holds the tokens.
|
|
405
|
+
* Passing [] removes them all (sign-out); the selection falls back to Auto if
|
|
406
|
+
* the chosen one went away.
|
|
407
|
+
*/
|
|
408
|
+
function syncChatGPTModels(list) {
|
|
409
|
+
const file = readModelsState();
|
|
410
|
+
const existing = file.models.filter((m) => m.kind === 'chatgpt');
|
|
411
|
+
const next = (list || []).map((x) => {
|
|
412
|
+
const prev = existing.find((m) => m.model === x.slug);
|
|
413
|
+
return {
|
|
414
|
+
id: prev ? prev.id : newModelId(),
|
|
415
|
+
name: x.name || x.slug,
|
|
416
|
+
kind: 'chatgpt',
|
|
417
|
+
baseUrl: 'https://api.openai.com/v1',
|
|
418
|
+
model: x.slug,
|
|
419
|
+
apiKey: '',
|
|
420
|
+
createdAt: prev?.createdAt || Date.now(),
|
|
421
|
+
};
|
|
422
|
+
});
|
|
423
|
+
file.models = [...file.models.filter((m) => m.kind !== 'chatgpt'), ...next];
|
|
424
|
+
if (file.selectedModel !== AUTO_MODEL_ID && !file.models.some((m) => m.id === file.selectedModel)) file.selectedModel = AUTO_MODEL_ID;
|
|
425
|
+
const w = writeFile(file);
|
|
426
|
+
return w.ok ? { ok: true, models: next } : w;
|
|
427
|
+
}
|
|
428
|
+
|
|
350
429
|
/** Never render a key in full - this is what goes on screen and in logs. */
|
|
351
430
|
function maskKey(key) {
|
|
352
431
|
if (!key) return '(none)';
|
|
@@ -388,5 +467,6 @@ module.exports = {
|
|
|
388
467
|
getConfig, saveConfig, maskKey, describeProvider, describeProviderShort, byokHint,
|
|
389
468
|
configPath, PROVIDERS, BYOK_PROVIDERS,
|
|
390
469
|
getIntegration, saveIntegration, disconnectIntegration, isIntegrationConnected, INTEGRATIONS,
|
|
391
|
-
AUTO_MODEL_ID, getModels, getModel, getSelectedModel, getSelectedModelId, saveModel, deleteModel, selectModel,
|
|
470
|
+
AUTO_MODEL_ID, getModels, getModel, getSelectedModel, getSelectedModelId, saveModel, deleteModel, selectModel, syncChatGPTModels,
|
|
471
|
+
setModelPhone, getPhoneDeletes, setPhoneDeletes,
|
|
392
472
|
};
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
|
|
5
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const template = readFileSync(path.join(__dirname, 'artifact-template.html'), 'utf8');
|
|
7
|
-
const data = JSON.parse(readFileSync(path.join(__dirname, 'table-data.json'), 'utf8'));
|
|
8
|
-
|
|
9
|
-
const totalApps = data.rows.length;
|
|
10
|
-
const totalShots = data.rows.reduce((s, r) => s + r.s, 0);
|
|
11
|
-
const generatedAt = new Date(data.generatedAt).toISOString().slice(0, 16).replace('T', ' ') + ' UTC';
|
|
12
|
-
|
|
13
|
-
const out = template
|
|
14
|
-
.replace('__DATA_JSON__', JSON.stringify(data).replace(/</g, '\\u003c'))
|
|
15
|
-
.replace('__GENERATED_AT__', generatedAt)
|
|
16
|
-
.replace('__TOTAL_APPS__', String(totalApps))
|
|
17
|
-
.replace('__TOTAL_SHOTS__', String(totalShots));
|
|
18
|
-
|
|
19
|
-
const outPath = process.argv[2];
|
|
20
|
-
writeFileSync(outPath, out);
|
|
21
|
-
console.log('wrote', outPath, '(' + (out.length / 1024).toFixed(0) + ' KB)');
|
|
1
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const template = readFileSync(path.join(__dirname, 'artifact-template.html'), 'utf8');
|
|
7
|
+
const data = JSON.parse(readFileSync(path.join(__dirname, 'table-data.json'), 'utf8'));
|
|
8
|
+
|
|
9
|
+
const totalApps = data.rows.length;
|
|
10
|
+
const totalShots = data.rows.reduce((s, r) => s + r.s, 0);
|
|
11
|
+
const generatedAt = new Date(data.generatedAt).toISOString().slice(0, 16).replace('T', ' ') + ' UTC';
|
|
12
|
+
|
|
13
|
+
const out = template
|
|
14
|
+
.replace('__DATA_JSON__', JSON.stringify(data).replace(/</g, '\\u003c'))
|
|
15
|
+
.replace('__GENERATED_AT__', generatedAt)
|
|
16
|
+
.replace('__TOTAL_APPS__', String(totalApps))
|
|
17
|
+
.replace('__TOTAL_SHOTS__', String(totalShots));
|
|
18
|
+
|
|
19
|
+
const outPath = process.argv[2];
|
|
20
|
+
writeFileSync(outPath, out);
|
|
21
|
+
console.log('wrote', outPath, '(' + (out.length / 1024).toFixed(0) + ' KB)');
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
|
|
5
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const OUT_PATH = path.join(__dirname, 'index.json');
|
|
7
|
-
|
|
8
|
-
// [category, appId] pairs confirmed as wrong-category mismatches on manual review.
|
|
9
|
-
// Same app can be a correct match in one category and a mismatch in another
|
|
10
|
-
// (e.g. "BetterHelp" is a real health_fitness app but wrongly matched a
|
|
11
|
-
// real_estate search term), so removal is scoped per (category, id), not global.
|
|
12
|
-
const REMOVE = [
|
|
13
|
-
['productivity', 6470821480], // "Zapia" matched from "Zapier" search - unrelated app
|
|
14
|
-
['productivity', 1038369065], // "Flo Cycle & Period Tracker" matched from "Glo" - wrong app (correct in health_fitness)
|
|
15
|
-
['productivity', 1573566111], // "Complices" matched from "Complice" - unrelated French dating app
|
|
16
|
-
['shopping', 6748155184], // "Courtyard: TCG, Watches, Cards" - unrelated marketplace
|
|
17
|
-
['food_delivery', 1416720539], // "Tango Reserve by AgilQuest" matched from generic "Reserve" search - enterprise workspace booking, not food
|
|
18
|
-
['food_delivery', 6748155184], // "Courtyard" again - unrelated
|
|
19
|
-
['real_estate', 995252384], // "BetterHelp - Therapy" matched from "Better.com" - wrong app (correct in health_fitness)
|
|
20
|
-
['real_estate', 1264782561], // "Co-Star Personalized Astrology" matched from "CoStar" - astrology app, not commercial real estate data
|
|
21
|
-
['real_estate', 1457119021], // "Rainbow - Ethereum Wallet" - unrelated crypto wallet
|
|
22
|
-
['real_estate', 407108860], // "Cozi Family Organizer" - unrelated family calendar app
|
|
23
|
-
['real_estate', 880735556], // "Gods of Olympus" matched from "Zeus Living" - unrelated mobile game
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
const idx = JSON.parse(readFileSync(OUT_PATH, 'utf8'));
|
|
27
|
-
let removed = 0;
|
|
28
|
-
for (const [cat, id] of REMOVE) {
|
|
29
|
-
const before = idx.categories[cat].length;
|
|
30
|
-
idx.categories[cat] = idx.categories[cat].filter((a) => a.id !== id);
|
|
31
|
-
removed += before - idx.categories[cat].length;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
idx.cleanedAt = new Date().toISOString();
|
|
35
|
-
writeFileSync(OUT_PATH, JSON.stringify(idx, null, 2));
|
|
36
|
-
console.log(`Removed ${removed} confirmed-mismatch entries.`);
|
|
37
|
-
|
|
38
|
-
let totalApps = 0, totalShots = 0;
|
|
39
|
-
for (const [cat, apps] of Object.entries(idx.categories)) {
|
|
40
|
-
const shots = apps.reduce((s, a) => s + a.screenshots.length, 0);
|
|
41
|
-
totalApps += apps.length;
|
|
42
|
-
totalShots += shots;
|
|
43
|
-
console.log(`${cat}: ${apps.length} apps, ${shots} screenshots`);
|
|
44
|
-
}
|
|
45
|
-
console.log(`\nFinal: ${totalApps} apps, ${totalShots} screenshots`);
|
|
1
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const OUT_PATH = path.join(__dirname, 'index.json');
|
|
7
|
+
|
|
8
|
+
// [category, appId] pairs confirmed as wrong-category mismatches on manual review.
|
|
9
|
+
// Same app can be a correct match in one category and a mismatch in another
|
|
10
|
+
// (e.g. "BetterHelp" is a real health_fitness app but wrongly matched a
|
|
11
|
+
// real_estate search term), so removal is scoped per (category, id), not global.
|
|
12
|
+
const REMOVE = [
|
|
13
|
+
['productivity', 6470821480], // "Zapia" matched from "Zapier" search - unrelated app
|
|
14
|
+
['productivity', 1038369065], // "Flo Cycle & Period Tracker" matched from "Glo" - wrong app (correct in health_fitness)
|
|
15
|
+
['productivity', 1573566111], // "Complices" matched from "Complice" - unrelated French dating app
|
|
16
|
+
['shopping', 6748155184], // "Courtyard: TCG, Watches, Cards" - unrelated marketplace
|
|
17
|
+
['food_delivery', 1416720539], // "Tango Reserve by AgilQuest" matched from generic "Reserve" search - enterprise workspace booking, not food
|
|
18
|
+
['food_delivery', 6748155184], // "Courtyard" again - unrelated
|
|
19
|
+
['real_estate', 995252384], // "BetterHelp - Therapy" matched from "Better.com" - wrong app (correct in health_fitness)
|
|
20
|
+
['real_estate', 1264782561], // "Co-Star Personalized Astrology" matched from "CoStar" - astrology app, not commercial real estate data
|
|
21
|
+
['real_estate', 1457119021], // "Rainbow - Ethereum Wallet" - unrelated crypto wallet
|
|
22
|
+
['real_estate', 407108860], // "Cozi Family Organizer" - unrelated family calendar app
|
|
23
|
+
['real_estate', 880735556], // "Gods of Olympus" matched from "Zeus Living" - unrelated mobile game
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const idx = JSON.parse(readFileSync(OUT_PATH, 'utf8'));
|
|
27
|
+
let removed = 0;
|
|
28
|
+
for (const [cat, id] of REMOVE) {
|
|
29
|
+
const before = idx.categories[cat].length;
|
|
30
|
+
idx.categories[cat] = idx.categories[cat].filter((a) => a.id !== id);
|
|
31
|
+
removed += before - idx.categories[cat].length;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
idx.cleanedAt = new Date().toISOString();
|
|
35
|
+
writeFileSync(OUT_PATH, JSON.stringify(idx, null, 2));
|
|
36
|
+
console.log(`Removed ${removed} confirmed-mismatch entries.`);
|
|
37
|
+
|
|
38
|
+
let totalApps = 0, totalShots = 0;
|
|
39
|
+
for (const [cat, apps] of Object.entries(idx.categories)) {
|
|
40
|
+
const shots = apps.reduce((s, a) => s + a.screenshots.length, 0);
|
|
41
|
+
totalApps += apps.length;
|
|
42
|
+
totalShots += shots;
|
|
43
|
+
console.log(`${cat}: ${apps.length} apps, ${shots} screenshots`);
|
|
44
|
+
}
|
|
45
|
+
console.log(`\nFinal: ${totalApps} apps, ${totalShots} screenshots`);
|