codeply-cli 0.5.6 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/THIRD_PARTY_NOTICES.md +65 -65
- package/bin/codeply.js +8 -3
- package/lib/agent.mjs +2596 -2268
- package/lib/ai.js +135 -27
- package/lib/ai.mjs +6 -6
- package/lib/apply-patch.js +217 -217
- package/lib/arity-table.json +138 -138
- package/lib/arity.js +70 -70
- package/lib/auth.mjs +6 -6
- package/lib/autopilot.js +84 -0
- package/lib/bot-mail.js +46 -0
- package/lib/bots.js +142 -15
- package/lib/brain.js +914 -0
- package/lib/call-assist.js +120 -0
- package/lib/chatgpt.js +282 -282
- package/lib/cloud-browser.mjs +143 -143
- package/lib/cloud.mjs +1396 -1320
- package/lib/commands.js +103 -103
- package/lib/computer.js +136 -0
- package/lib/design-library/assemble-artifact.mjs +21 -21
- package/lib/design-library/cleanup.mjs +45 -45
- package/lib/design-library/index.json +22379 -22379
- package/lib/design-library/make-table-data.mjs +39 -39
- package/lib/design-tools.mjs +173 -0
- package/lib/diagnostics.js +192 -192
- package/lib/dsh-acp.mjs +444 -0
- package/lib/edit-engine.js +364 -364
- package/lib/github-agent.mjs +326 -326
- package/lib/handoff.mjs +245 -0
- package/lib/history.js +78 -75
- package/lib/life-sources.js +319 -0
- package/lib/life-store.js +356 -0
- package/lib/life-triggers.js +286 -0
- package/lib/life.js +487 -0
- package/lib/mail-watch.js +3 -3
- package/lib/mcp.js +258 -258
- package/lib/native-tools.mjs +17 -5
- package/lib/oauth-connectors.js +76 -21
- package/lib/permissions.js +109 -109
- package/lib/plugins.js +356 -353
- package/lib/reach.js +67 -67
- package/lib/render-md.mjs +647 -617
- package/lib/screen.js +77 -0
- package/lib/server.mjs +458 -457
- package/lib/session-db.js +220 -220
- package/lib/share.js +208 -168
- package/lib/skills.js +4 -1
- package/lib/snapshot.js +146 -146
- package/lib/subagents.js +4 -2
- package/lib/telemetry.js +105 -105
- package/lib/terminal-hints.js +154 -154
- package/lib/textarea.mjs +264 -258
- package/lib/tools.mjs +326 -40
- package/lib/tui.mjs +1459 -1086
- package/lib/typecheck-worker.js +147 -147
- package/lib/web-tools.js +209 -209
- package/package.json +1 -1
- package/skills/SOURCE.md +58 -58
- package/skills/accessibility/SKILL.md +147 -147
- package/skills/agent-architecture-audit/SKILL.md +257 -257
- package/skills/agent-eval/SKILL.md +147 -147
- package/skills/agent-harness-construction/SKILL.md +74 -74
- package/skills/agent-introspection-debugging/SKILL.md +154 -154
- package/skills/agent-payment-x402/SKILL.md +225 -225
- package/skills/agent-self-evaluation/SKILL.md +182 -182
- package/skills/agent-self-evaluation/references/evaluation-criteria.md +71 -71
- package/skills/agent-self-evaluation/references/hook-integration.md +64 -64
- package/skills/agent-sort/SKILL.md +216 -216
- package/skills/agentic-engineering/SKILL.md +64 -64
- package/skills/agentic-os/SKILL.md +388 -388
- package/skills/ai-first-engineering/SKILL.md +52 -52
- package/skills/ai-regression-testing/SKILL.md +386 -386
- package/skills/android-clean-architecture/SKILL.md +340 -340
- package/skills/angular-developer/SKILL.md +155 -155
- package/skills/angular-developer/references/angular-animations.md +160 -160
- package/skills/angular-developer/references/angular-aria.md +410 -410
- package/skills/angular-developer/references/cli.md +86 -86
- package/skills/angular-developer/references/component-harnesses.md +59 -59
- package/skills/angular-developer/references/component-styling.md +91 -91
- package/skills/angular-developer/references/components.md +117 -117
- package/skills/angular-developer/references/creating-services.md +97 -97
- package/skills/angular-developer/references/data-resolvers.md +69 -69
- package/skills/angular-developer/references/define-routes.md +67 -67
- package/skills/angular-developer/references/defining-providers.md +72 -72
- package/skills/angular-developer/references/di-fundamentals.md +120 -120
- package/skills/angular-developer/references/e2e-testing.md +56 -56
- package/skills/angular-developer/references/effects.md +83 -83
- package/skills/angular-developer/references/hierarchical-injectors.md +43 -43
- package/skills/angular-developer/references/host-elements.md +80 -80
- package/skills/angular-developer/references/injection-context.md +63 -63
- package/skills/angular-developer/references/inputs.md +101 -101
- package/skills/angular-developer/references/linked-signal.md +59 -59
- package/skills/angular-developer/references/loading-strategies.md +61 -61
- package/skills/angular-developer/references/mcp.md +108 -108
- package/skills/angular-developer/references/navigate-to-routes.md +69 -69
- package/skills/angular-developer/references/outputs.md +86 -86
- package/skills/angular-developer/references/reactive-forms.md +122 -122
- package/skills/angular-developer/references/rendering-strategies.md +44 -44
- package/skills/angular-developer/references/resource.md +77 -77
- package/skills/angular-developer/references/route-animations.md +56 -56
- package/skills/angular-developer/references/route-guards.md +52 -52
- package/skills/angular-developer/references/router-lifecycle.md +45 -45
- package/skills/angular-developer/references/router-testing.md +87 -87
- package/skills/angular-developer/references/show-routes-with-outlets.md +68 -68
- package/skills/angular-developer/references/signal-forms.md +795 -795
- package/skills/angular-developer/references/signals-overview.md +94 -94
- package/skills/angular-developer/references/tailwind-css.md +69 -69
- package/skills/angular-developer/references/template-driven-forms.md +114 -114
- package/skills/angular-developer/references/testing-fundamentals.md +65 -65
- package/skills/api-connector-builder/SKILL.md +121 -121
- package/skills/api-design/SKILL.md +524 -524
- package/skills/architecture-decision-records/SKILL.md +180 -180
- package/skills/article-writing/SKILL.md +80 -80
- package/skills/automation-audit-ops/SKILL.md +143 -143
- package/skills/autonomous-agent-harness/SKILL.md +274 -274
- package/skills/autonomous-loops/SKILL.md +611 -611
- package/skills/backend-patterns/SKILL.md +562 -562
- package/skills/benchmark/SKILL.md +95 -95
- package/skills/benchmark-methodology/SKILL.md +191 -191
- package/skills/benchmark-optimization-loop/SKILL.md +71 -71
- package/skills/blender-motion-state-inspection/SKILL.md +165 -165
- package/skills/blueprint/SKILL.md +106 -106
- package/skills/brand-discovery/SKILL.md +145 -145
- package/skills/brand-discovery/references/10_purpose-why.md +40 -40
- package/skills/brand-discovery/references/20_positioning.md +44 -44
- package/skills/brand-discovery/references/30_audience-niche.md +52 -52
- package/skills/brand-discovery/references/40_personality-archetype.md +57 -57
- package/skills/brand-discovery/references/50_voice-tone.md +59 -59
- package/skills/brand-discovery/references/60_narrative-story.md +50 -50
- package/skills/brand-discovery/references/70_founder-tension.md +49 -49
- package/skills/brand-discovery/references/90_SYNTHESIS.md +133 -133
- package/skills/brand-voice/SKILL.md +98 -98
- package/skills/brand-voice/references/voice-profile-schema.md +55 -55
- package/skills/browser-qa/SKILL.md +105 -105
- package/skills/bun-runtime/SKILL.md +85 -85
- package/skills/canary-watch/SKILL.md +108 -108
- package/skills/carrier-relationship-management/SKILL.md +212 -212
- package/skills/cisco-ios-patterns/SKILL.md +164 -164
- package/skills/ck/SKILL.md +148 -148
- package/skills/claude-devfleet/SKILL.md +112 -112
- package/skills/click-path-audit/SKILL.md +245 -245
- package/skills/clickhouse-io/SKILL.md +445 -445
- package/skills/code-tour/SKILL.md +254 -254
- package/skills/codebase-onboarding/SKILL.md +234 -234
- package/skills/codehealth-mcp/SKILL.md +167 -167
- package/skills/codeply-taste/LICENSE +21 -0
- package/skills/codeply-taste/SKILL.md +100 -0
- package/skills/codeply-taste/SOURCE.md +10 -0
- package/skills/codeply-taste/assets/base.css +100 -0
- package/skills/codeply-taste/assets/base.js +37 -0
- package/skills/codeply-taste/library/directions.json +1078 -0
- package/skills/codeply-taste/library/heroes.json +894 -0
- package/skills/codeply-taste/library/motions.json +57 -0
- package/skills/codeply-taste/library/palettes.json +1485 -0
- package/skills/codeply-taste/library/photos.json +7240 -0
- package/skills/codeply-taste/library/sections.json +4136 -0
- package/skills/codeply-taste/library/sites.json +7274 -0
- package/skills/codeply-taste/library/type.json +2024 -0
- package/skills/codeply-taste/references/colour.md +141 -0
- package/skills/codeply-taste/references/copy.md +177 -0
- package/skills/codeply-taste/references/critique.md +171 -0
- package/skills/codeply-taste/references/imagery.md +377 -0
- package/skills/codeply-taste/references/layout.md +178 -0
- package/skills/codeply-taste/references/motion.md +292 -0
- package/skills/codeply-taste/references/research.md +306 -0
- package/skills/codeply-taste/references/tells.md +115 -0
- package/skills/codeply-taste/references/type.md +170 -0
- package/skills/codeply-taste/scripts/assets.mjs +865 -0
- package/skills/codeply-taste/scripts/draw.mjs +525 -0
- package/skills/codeply-taste/scripts/inspect.mjs +205 -0
- package/skills/codeply-taste/scripts/lib/cdp.mjs +205 -0
- package/skills/codeply-taste/scripts/lib/probe.mjs +569 -0
- package/skills/codeply-taste/scripts/lib/rules.mjs +137 -0
- package/skills/codeply-taste/scripts/lint.mjs +108 -0
- package/skills/coding-standards/SKILL.md +551 -551
- package/skills/competitive-platform-analysis/SKILL.md +214 -214
- package/skills/competitive-report-structure/SKILL.md +162 -162
- package/skills/compose-multiplatform-patterns/SKILL.md +300 -300
- package/skills/config-gc/SKILL.md +120 -120
- package/skills/configure-ecc/SKILL.md +385 -385
- package/skills/connections-optimizer/SKILL.md +190 -190
- package/skills/content-engine/SKILL.md +132 -132
- package/skills/content-hash-cache-pattern/SKILL.md +162 -162
- package/skills/context-budget/SKILL.md +136 -136
- package/skills/continuous-agent-loop/SKILL.md +46 -46
- package/skills/continuous-learning/SKILL.md +132 -132
- package/skills/continuous-learning-v2/SKILL.md +361 -361
- package/skills/contract-first/SKILL.md +287 -287
- package/skills/cost-aware-llm-pipeline/SKILL.md +184 -184
- package/skills/cost-tracking/SKILL.md +97 -97
- package/skills/council/SKILL.md +204 -204
- package/skills/cpp-coding-standards/SKILL.md +724 -724
- package/skills/cpp-testing/SKILL.md +325 -325
- package/skills/crosspost/SKILL.md +112 -112
- package/skills/csharp-testing/SKILL.md +322 -322
- package/skills/customer-billing-ops/SKILL.md +141 -141
- package/skills/customs-trade-compliance/SKILL.md +263 -263
- package/skills/dart-flutter-patterns/SKILL.md +564 -564
- package/skills/dashboard-builder/SKILL.md +109 -109
- package/skills/data-scraper-agent/SKILL.md +765 -765
- package/skills/data-throughput-accelerator/SKILL.md +74 -74
- package/skills/database-migrations/SKILL.md +430 -430
- package/skills/deep-research/SKILL.md +160 -160
- package/skills/defi-amm-security/SKILL.md +167 -167
- package/skills/delivery-gate/SKILL.md +126 -126
- package/skills/deployment-patterns/SKILL.md +428 -428
- package/skills/design-system/SKILL.md +83 -83
- package/skills/django-celery/SKILL.md +458 -458
- package/skills/django-patterns/SKILL.md +735 -735
- package/skills/django-security/SKILL.md +644 -644
- package/skills/django-tdd/SKILL.md +730 -730
- package/skills/django-verification/SKILL.md +470 -470
- package/skills/dmux-workflows/SKILL.md +192 -192
- package/skills/docker-patterns/SKILL.md +365 -365
- package/skills/documentation-lookup/SKILL.md +91 -91
- package/skills/dotnet-patterns/SKILL.md +322 -322
- package/skills/dynamic-workflow-mode/SKILL.md +124 -124
- package/skills/e2e-testing/SKILL.md +327 -327
- package/skills/ecc-guide/SKILL.md +190 -190
- package/skills/ecc-recipes/SKILL.md +149 -149
- package/skills/ecc-tools-cost-audit/SKILL.md +161 -161
- package/skills/email-ops/SKILL.md +122 -122
- package/skills/energy-procurement/SKILL.md +228 -228
- package/skills/enterprise-agent-ops/SKILL.md +51 -51
- package/skills/error-handling/SKILL.md +377 -377
- package/skills/eval-harness/SKILL.md +271 -271
- package/skills/evm-token-decimals/SKILL.md +131 -131
- package/skills/exa-search/SKILL.md +108 -108
- package/skills/fal-ai-media/SKILL.md +289 -289
- package/skills/fastapi-patterns/SKILL.md +514 -514
- package/skills/finance-billing-ops/SKILL.md +128 -128
- package/skills/flox-environments/SKILL.md +497 -497
- package/skills/flutter-dart-code-review/SKILL.md +436 -436
- package/skills/foundation-models-on-device/SKILL.md +243 -243
- package/skills/frontend-a11y/SKILL.md +446 -446
- package/skills/frontend-design-direction/SKILL.md +93 -93
- package/skills/frontend-patterns/SKILL.md +657 -657
- package/skills/frontend-slides/SKILL.md +185 -185
- package/skills/fsharp-testing/SKILL.md +281 -281
- package/skills/gan-style-harness/SKILL.md +279 -279
- package/skills/gateguard/SKILL.md +133 -133
- package/skills/generating-python-installer/SKILL.md +820 -820
- package/skills/git-workflow/SKILL.md +716 -716
- package/skills/github-ops/SKILL.md +145 -145
- package/skills/golang-patterns/SKILL.md +676 -676
- package/skills/golang-testing/SKILL.md +721 -721
- package/skills/google-workspace-ops/SKILL.md +96 -96
- package/skills/growth-log/SKILL.md +128 -128
- package/skills/healthcare-cdss-patterns/SKILL.md +246 -246
- package/skills/healthcare-emr-patterns/SKILL.md +160 -160
- package/skills/healthcare-eval-harness/SKILL.md +208 -208
- package/skills/healthcare-phi-compliance/SKILL.md +146 -146
- package/skills/hermes-imports/SKILL.md +89 -89
- package/skills/hexagonal-architecture/SKILL.md +277 -277
- package/skills/hipaa-compliance/SKILL.md +79 -79
- package/skills/homelab-network-readiness/SKILL.md +170 -170
- package/skills/homelab-network-setup/SKILL.md +130 -130
- package/skills/homelab-pihole-dns/SKILL.md +275 -275
- package/skills/homelab-vlan-segmentation/SKILL.md +312 -312
- package/skills/homelab-wireguard-vpn/SKILL.md +306 -306
- package/skills/hookify-rules/SKILL.md +128 -128
- package/skills/inherit-legacy-style/SKILL.md +157 -157
- package/skills/intent-driven-development/SKILL.md +360 -360
- package/skills/inventory-demand-planning/SKILL.md +247 -247
- package/skills/investor-materials/SKILL.md +97 -97
- package/skills/investor-outreach/SKILL.md +92 -92
- package/skills/ios-icon-gen/SKILL.md +158 -158
- package/skills/iterative-retrieval/SKILL.md +212 -212
- package/skills/ito-basket-compare/SKILL.md +64 -64
- package/skills/ito-compute/SKILL.md +129 -129
- package/skills/ito-data-atlas-agent/SKILL.md +64 -64
- package/skills/ito-market-intelligence/SKILL.md +61 -61
- package/skills/ito-trade-planner/SKILL.md +68 -68
- package/skills/java-coding-standards/SKILL.md +384 -384
- package/skills/jira-integration/SKILL.md +303 -303
- package/skills/jpa-patterns/SKILL.md +152 -152
- package/skills/knowledge-ops/SKILL.md +155 -155
- package/skills/kotlin-coroutines-flows/SKILL.md +285 -285
- package/skills/kotlin-exposed-patterns/SKILL.md +720 -720
- package/skills/kotlin-ktor-patterns/SKILL.md +690 -690
- package/skills/kotlin-patterns/SKILL.md +712 -712
- package/skills/kotlin-testing/SKILL.md +825 -825
- package/skills/kubernetes-patterns/SKILL.md +756 -756
- package/skills/laravel-patterns/SKILL.md +416 -416
- package/skills/laravel-plugin-discovery/SKILL.md +230 -230
- package/skills/laravel-security/SKILL.md +948 -948
- package/skills/laravel-tdd/SKILL.md +675 -675
- package/skills/laravel-verification/SKILL.md +180 -180
- package/skills/latency-critical-systems/SKILL.md +75 -75
- package/skills/lead-intelligence/SKILL.md +322 -322
- package/skills/liquid-glass-design/SKILL.md +279 -279
- package/skills/llm-trading-agent-security/SKILL.md +147 -147
- package/skills/logistics-exception-management/SKILL.md +222 -222
- package/skills/loop-design-check/SKILL.md +143 -143
- package/skills/mailtrap-email-integration/SKILL.md +77 -77
- package/skills/make-interfaces-feel-better/SKILL.md +152 -152
- package/skills/manim-video/SKILL.md +90 -90
- package/skills/market-research/SKILL.md +76 -76
- package/skills/marketing-campaign/SKILL.md +114 -114
- package/skills/mcp-server-patterns/SKILL.md +70 -70
- package/skills/messages-ops/SKILL.md +105 -105
- package/skills/ml-adoption-playbook/SKILL.md +57 -57
- package/skills/mle-workflow/SKILL.md +348 -348
- package/skills/motion-advanced/SKILL.md +596 -596
- package/skills/motion-foundations/SKILL.md +299 -299
- package/skills/motion-patterns/SKILL.md +434 -434
- package/skills/motion-ui/SKILL.md +576 -576
- package/skills/mysql-patterns/SKILL.md +413 -413
- package/skills/nanoclaw-repl/SKILL.md +34 -34
- package/skills/nestjs-patterns/SKILL.md +231 -231
- package/skills/netmiko-ssh-automation/SKILL.md +174 -174
- package/skills/network-bgp-diagnostics/SKILL.md +168 -168
- package/skills/network-config-validation/SKILL.md +211 -211
- package/skills/network-interface-health/SKILL.md +153 -153
- package/skills/nextjs-turbopack/SKILL.md +58 -58
- package/skills/nodejs-keccak256/SKILL.md +103 -103
- package/skills/nutrient-document-processing/SKILL.md +168 -168
- package/skills/nuxt4-patterns/SKILL.md +101 -101
- package/skills/openclaw-persona-forge/SKILL.md +289 -289
- package/skills/openclaw-persona-forge/references/avatar-style.md +124 -124
- package/skills/openclaw-persona-forge/references/boundary-rules.md +53 -53
- package/skills/openclaw-persona-forge/references/error-handling.md +53 -53
- package/skills/openclaw-persona-forge/references/identity-tension.md +48 -48
- package/skills/openclaw-persona-forge/references/naming-system.md +39 -39
- package/skills/openclaw-persona-forge/references/output-template.md +166 -166
- package/skills/opensource-pipeline/SKILL.md +256 -256
- package/skills/orch-add-feature/SKILL.md +45 -45
- package/skills/orch-build-mvp/SKILL.md +49 -49
- package/skills/orch-change-feature/SKILL.md +43 -43
- package/skills/orch-fix-defect/SKILL.md +43 -43
- package/skills/orch-pipeline/SKILL.md +121 -121
- package/skills/orch-refine-code/SKILL.md +44 -44
- package/skills/parallel-execution-optimizer/SKILL.md +74 -74
- package/skills/perl-patterns/SKILL.md +505 -505
- package/skills/perl-security/SKILL.md +504 -504
- package/skills/perl-testing/SKILL.md +476 -476
- package/skills/plan-canvas/SKILL.md +153 -153
- package/skills/plan-orchestrate/SKILL.md +263 -263
- package/skills/plankton-code-quality/SKILL.md +237 -237
- package/skills/postgres-patterns/SKILL.md +148 -148
- package/skills/prediction-market-oracle-research/SKILL.md +64 -64
- package/skills/prediction-market-risk-review/SKILL.md +61 -61
- package/skills/premium-web-design/SKILL.md +154 -154
- package/skills/prisma-patterns/SKILL.md +401 -401
- package/skills/product-capability/SKILL.md +142 -142
- package/skills/product-lens/SKILL.md +93 -93
- package/skills/production-audit/SKILL.md +207 -207
- package/skills/production-scheduling/SKILL.md +238 -238
- package/skills/project-flow-ops/SKILL.md +112 -112
- package/skills/prompt-optimizer/SKILL.md +398 -398
- package/skills/python-patterns/SKILL.md +751 -751
- package/skills/python-testing/SKILL.md +817 -817
- package/skills/pytorch-patterns/SKILL.md +397 -397
- package/skills/quality-nonconformance/SKILL.md +260 -260
- package/skills/quarkus-patterns/SKILL.md +723 -723
- package/skills/quarkus-security/SKILL.md +468 -468
- package/skills/quarkus-tdd/SKILL.md +812 -812
- package/skills/quarkus-verification/SKILL.md +480 -480
- package/skills/ralphinho-rfc-pipeline/SKILL.md +68 -68
- package/skills/react-native-patterns/SKILL.md +326 -326
- package/skills/react-patterns/SKILL.md +342 -342
- package/skills/react-performance/SKILL.md +575 -575
- package/skills/react-testing/SKILL.md +424 -424
- package/skills/recsys-pipeline-architect/SKILL.md +115 -115
- package/skills/recursive-decision-ledger/SKILL.md +81 -81
- package/skills/redis-patterns/SKILL.md +404 -404
- package/skills/regex-vs-llm-structured-text/SKILL.md +221 -221
- package/skills/remotion-video-creation/SKILL.md +43 -43
- package/skills/repo-scan/SKILL.md +79 -79
- package/skills/research-ops/SKILL.md +113 -113
- package/skills/returns-reverse-logistics/SKILL.md +240 -240
- package/skills/rules-distill/SKILL.md +265 -265
- package/skills/rust-patterns/SKILL.md +500 -500
- package/skills/rust-testing/SKILL.md +501 -501
- package/skills/safety-guard/SKILL.md +76 -76
- package/skills/santa-method/SKILL.md +307 -307
- package/skills/scientific-db-pubmed-database/SKILL.md +176 -176
- package/skills/scientific-db-uspto-database/SKILL.md +178 -178
- package/skills/scientific-pkg-gget/SKILL.md +167 -167
- package/skills/scientific-thinking-literature-review/SKILL.md +193 -193
- package/skills/scientific-thinking-scholar-evaluation/SKILL.md +161 -161
- package/skills/search-first/SKILL.md +183 -183
- package/skills/security-bounty-hunter/SKILL.md +100 -100
- package/skills/security-review/SKILL.md +504 -504
- package/skills/security-scan/SKILL.md +166 -166
- package/skills/seo/SKILL.md +155 -155
- package/skills/skill-comply/SKILL.md +59 -59
- package/skills/skill-scout/SKILL.md +141 -141
- package/skills/skill-stocktake/SKILL.md +195 -195
- package/skills/social-graph-ranker/SKILL.md +155 -155
- package/skills/social-publisher/SKILL.md +130 -130
- package/skills/springboot-patterns/SKILL.md +315 -315
- package/skills/springboot-security/SKILL.md +273 -273
- package/skills/springboot-tdd/SKILL.md +159 -159
- package/skills/springboot-verification/SKILL.md +232 -232
- package/skills/strategic-compact/SKILL.md +142 -142
- package/skills/swift-actor-persistence/SKILL.md +144 -144
- package/skills/swift-concurrency-6-2/SKILL.md +216 -216
- package/skills/swift-protocol-di-testing/SKILL.md +191 -191
- package/skills/swiftui-patterns/SKILL.md +259 -259
- package/skills/taste/SKILL.md +264 -264
- package/skills/taste/references/genre-taxonomy.md +87 -87
- package/skills/tdd-workflow/SKILL.md +583 -583
- package/skills/team-agent-orchestration/SKILL.md +111 -111
- package/skills/team-builder/SKILL.md +169 -169
- package/skills/terminal-ops/SKILL.md +110 -110
- package/skills/tinystruct-patterns/SKILL.md +279 -279
- package/skills/tinystruct-patterns/references/architecture.md +90 -90
- package/skills/tinystruct-patterns/references/data-handling.md +60 -60
- package/skills/tinystruct-patterns/references/database.md +99 -99
- package/skills/tinystruct-patterns/references/routing.md +64 -64
- package/skills/tinystruct-patterns/references/system-usage.md +97 -97
- package/skills/tinystruct-patterns/references/testing.md +72 -72
- package/skills/token-budget-advisor/SKILL.md +134 -134
- package/skills/ui-demo/SKILL.md +466 -466
- package/skills/ui-to-vue/SKILL.md +135 -135
- package/skills/uncloud/SKILL.md +344 -344
- package/skills/unified-memory/SKILL.md +170 -170
- package/skills/unified-notifications-ops/SKILL.md +188 -188
- package/skills/verification-loop/SKILL.md +129 -129
- package/skills/video-editing/SKILL.md +311 -311
- package/skills/videodb/SKILL.md +375 -375
- package/skills/visa-doc-translate/SKILL.md +117 -117
- package/skills/vite-patterns/SKILL.md +450 -450
- package/skills/vue-patterns/SKILL.md +471 -471
- package/skills/windows-desktop-e2e/SKILL.md +888 -888
- package/skills/workspace-surface-audit/SKILL.md +126 -126
- package/skills/x-api/SKILL.md +235 -235
package/lib/session-db.js
CHANGED
|
@@ -1,220 +1,220 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SQLite-backed session store. Replaces rewriting one big JSON file on every
|
|
3
|
-
* save: each session and each message is a row, and a save only writes the rows
|
|
4
|
-
* whose content changed. Messages are searchable across every session.
|
|
5
|
-
*
|
|
6
|
-
* Driver order (first that loads wins), all with the same sync API:
|
|
7
|
-
* 1. node:sqlite (Node 22.5+, built in, nothing to install)
|
|
8
|
-
* 2. better-sqlite3 (optional dependency; Electron 29 needs this one)
|
|
9
|
-
* When neither loads, openSessionDb() returns null and the caller keeps its
|
|
10
|
-
* JSON file, so a machine without SQLite never loses anything.
|
|
11
|
-
*
|
|
12
|
-
* Shape it stores (same as the old JSON store):
|
|
13
|
-
* { sessions: [{ id, title, cwd, messages: [...], alwaysAllowed, createdAt, updatedAt, ...extra }],
|
|
14
|
-
* <any other top-level key>: value }
|
|
15
|
-
*/
|
|
16
|
-
const fs = require('fs');
|
|
17
|
-
const path = require('path');
|
|
18
|
-
|
|
19
|
-
const SCHEMA_VERSION = 1;
|
|
20
|
-
|
|
21
|
-
function loadDriver(modulePaths) {
|
|
22
|
-
try {
|
|
23
|
-
const { DatabaseSync } = require('node:sqlite');
|
|
24
|
-
if (DatabaseSync) return { kind: 'node:sqlite', open: (f) => new DatabaseSync(f) };
|
|
25
|
-
} catch {}
|
|
26
|
-
try {
|
|
27
|
-
let mod;
|
|
28
|
-
try { mod = require('better-sqlite3'); }
|
|
29
|
-
catch (e) {
|
|
30
|
-
if (!modulePaths || !modulePaths.length) throw e;
|
|
31
|
-
mod = require(require.resolve('better-sqlite3', { paths: modulePaths }));
|
|
32
|
-
}
|
|
33
|
-
return { kind: 'better-sqlite3', open: (f) => new mod(f) };
|
|
34
|
-
} catch {}
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const CORE_SESSION_KEYS = new Set(['id', 'title', 'cwd', 'messages', 'alwaysAllowed', 'createdAt', 'updatedAt']);
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* @param {string} file database path (created if missing)
|
|
42
|
-
* @param {{ modulePaths?: string[], driver?: 'node:sqlite'|'better-sqlite3' }} [opts]
|
|
43
|
-
* @returns {null | { kind:string, file:string, isEmpty():boolean, load():object, save(store:object):void, search(q:string, limit?:number):object[], close():void }}
|
|
44
|
-
*/
|
|
45
|
-
function openSessionDb(file, opts = {}) {
|
|
46
|
-
const driver = loadDriver(opts.modulePaths);
|
|
47
|
-
if (!driver || (opts.driver && driver.kind !== opts.driver)) return null;
|
|
48
|
-
let db;
|
|
49
|
-
try {
|
|
50
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
51
|
-
db = driver.open(file);
|
|
52
|
-
db.exec('PRAGMA journal_mode = WAL');
|
|
53
|
-
db.exec('PRAGMA synchronous = NORMAL');
|
|
54
|
-
db.exec('PRAGMA foreign_keys = ON');
|
|
55
|
-
db.exec(`
|
|
56
|
-
CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
57
|
-
CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
58
|
-
CREATE TABLE IF NOT EXISTS sessions (
|
|
59
|
-
id TEXT PRIMARY KEY,
|
|
60
|
-
title TEXT,
|
|
61
|
-
cwd TEXT,
|
|
62
|
-
always_allowed TEXT,
|
|
63
|
-
extra TEXT,
|
|
64
|
-
created_at INTEGER,
|
|
65
|
-
updated_at INTEGER,
|
|
66
|
-
position INTEGER NOT NULL DEFAULT 0
|
|
67
|
-
);
|
|
68
|
-
CREATE TABLE IF NOT EXISTS messages (
|
|
69
|
-
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
70
|
-
idx INTEGER NOT NULL,
|
|
71
|
-
kind TEXT,
|
|
72
|
-
body TEXT,
|
|
73
|
-
data TEXT NOT NULL,
|
|
74
|
-
PRIMARY KEY (session_id, idx)
|
|
75
|
-
);
|
|
76
|
-
CREATE INDEX IF NOT EXISTS sessions_updated ON sessions(updated_at DESC);
|
|
77
|
-
`);
|
|
78
|
-
db.prepare('INSERT OR IGNORE INTO meta (key, value) VALUES (?, ?)').run('schema', String(SCHEMA_VERSION));
|
|
79
|
-
} catch {
|
|
80
|
-
try { db && db.close(); } catch {}
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const q = {
|
|
85
|
-
countSessions: db.prepare('SELECT COUNT(*) AS n FROM sessions'),
|
|
86
|
-
countKv: db.prepare('SELECT COUNT(*) AS n FROM kv'),
|
|
87
|
-
allSessions: db.prepare('SELECT * FROM sessions ORDER BY position ASC'),
|
|
88
|
-
sessionMessages: db.prepare('SELECT data FROM messages WHERE session_id = ? ORDER BY idx ASC'),
|
|
89
|
-
allKv: db.prepare('SELECT key, value FROM kv'),
|
|
90
|
-
upsertSession: db.prepare(`INSERT INTO sessions (id, title, cwd, always_allowed, extra, created_at, updated_at, position)
|
|
91
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
92
|
-
ON CONFLICT(id) DO UPDATE SET title=excluded.title, cwd=excluded.cwd, always_allowed=excluded.always_allowed,
|
|
93
|
-
extra=excluded.extra, created_at=excluded.created_at, updated_at=excluded.updated_at, position=excluded.position`),
|
|
94
|
-
deleteSession: db.prepare('DELETE FROM sessions WHERE id = ?'),
|
|
95
|
-
listSessionIds: db.prepare('SELECT id FROM sessions'),
|
|
96
|
-
upsertMessage: db.prepare(`INSERT INTO messages (session_id, idx, kind, body, data) VALUES (?, ?, ?, ?, ?)
|
|
97
|
-
ON CONFLICT(session_id, idx) DO UPDATE SET kind=excluded.kind, body=excluded.body, data=excluded.data`),
|
|
98
|
-
trimMessages: db.prepare('DELETE FROM messages WHERE session_id = ? AND idx >= ?'),
|
|
99
|
-
upsertKv: db.prepare('INSERT INTO kv (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value=excluded.value'),
|
|
100
|
-
deleteKv: db.prepare('DELETE FROM kv WHERE key = ?'),
|
|
101
|
-
search: db.prepare(`SELECT m.session_id AS sessionId, m.idx AS idx, m.kind AS kind, m.body AS body, s.title AS title, s.cwd AS cwd, s.updated_at AS updatedAt
|
|
102
|
-
FROM messages m JOIN sessions s ON s.id = m.session_id
|
|
103
|
-
WHERE m.body LIKE ? ESCAPE '\\' ORDER BY s.updated_at DESC, m.idx ASC LIMIT ?`),
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
// What the database already holds, so a save only writes what changed.
|
|
107
|
-
const seenSession = new Map(); // id -> serialized session header
|
|
108
|
-
const seenMessages = new Map(); // id -> string[] (serialized messages)
|
|
109
|
-
const seenKv = new Map(); // key -> serialized value
|
|
110
|
-
|
|
111
|
-
const tx = (fn) => {
|
|
112
|
-
db.exec('BEGIN');
|
|
113
|
-
try { fn(); db.exec('COMMIT'); } catch (e) { try { db.exec('ROLLBACK'); } catch {} throw e; }
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const bodyOf = (m) => {
|
|
117
|
-
if (!m || typeof m !== 'object') return '';
|
|
118
|
-
const t = m.text != null ? m.text : (m.label != null ? m.label : '');
|
|
119
|
-
return typeof t === 'string' ? t.slice(0, 20000) : '';
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
kind: driver.kind,
|
|
124
|
-
file,
|
|
125
|
-
|
|
126
|
-
isEmpty() {
|
|
127
|
-
return q.countSessions.get().n === 0 && q.countKv.get().n === 0;
|
|
128
|
-
},
|
|
129
|
-
|
|
130
|
-
load() {
|
|
131
|
-
const store = {};
|
|
132
|
-
for (const row of q.allKv.all()) {
|
|
133
|
-
try { store[row.key] = JSON.parse(row.value); seenKv.set(row.key, row.value); } catch {}
|
|
134
|
-
}
|
|
135
|
-
store.sessions = q.allSessions.all().map((r, position) => {
|
|
136
|
-
const messages = q.sessionMessages.all(r.id).map((m) => { try { return JSON.parse(m.data); } catch { return null; } }).filter(Boolean);
|
|
137
|
-
let extra = {};
|
|
138
|
-
try { extra = r.extra ? JSON.parse(r.extra) : {}; } catch {}
|
|
139
|
-
let alwaysAllowed = [];
|
|
140
|
-
try { alwaysAllowed = r.always_allowed ? JSON.parse(r.always_allowed) : []; } catch {}
|
|
141
|
-
const s = { ...extra, id: r.id, title: r.title, cwd: r.cwd, messages, alwaysAllowed, createdAt: r.created_at, updatedAt: r.updated_at };
|
|
142
|
-
seenSession.set(r.id, headerOf(s, position));
|
|
143
|
-
seenMessages.set(r.id, messages.map((m) => JSON.stringify(m)));
|
|
144
|
-
return s;
|
|
145
|
-
});
|
|
146
|
-
return store;
|
|
147
|
-
},
|
|
148
|
-
|
|
149
|
-
save(store) {
|
|
150
|
-
const sessions = Array.isArray(store.sessions) ? store.sessions : [];
|
|
151
|
-
tx(() => {
|
|
152
|
-
const keep = new Set();
|
|
153
|
-
sessions.forEach((s, position) => {
|
|
154
|
-
if (!s || !s.id) return;
|
|
155
|
-
keep.add(s.id);
|
|
156
|
-
const header = headerOf(s, position);
|
|
157
|
-
if (seenSession.get(s.id) !== header) {
|
|
158
|
-
const extra = {};
|
|
159
|
-
for (const k of Object.keys(s)) if (!CORE_SESSION_KEYS.has(k)) extra[k] = s[k];
|
|
160
|
-
q.upsertSession.run(
|
|
161
|
-
s.id, s.title == null ? null : String(s.title), s.cwd == null ? null : String(s.cwd),
|
|
162
|
-
JSON.stringify(s.alwaysAllowed || []), JSON.stringify(extra),
|
|
163
|
-
Number(s.createdAt) || Date.now(), Number(s.updatedAt) || Date.now(), position,
|
|
164
|
-
);
|
|
165
|
-
seenSession.set(s.id, header);
|
|
166
|
-
}
|
|
167
|
-
const msgs = Array.isArray(s.messages) ? s.messages : [];
|
|
168
|
-
const prev = seenMessages.get(s.id) || [];
|
|
169
|
-
const next = new Array(msgs.length);
|
|
170
|
-
for (let i = 0; i < msgs.length; i++) {
|
|
171
|
-
const ser = JSON.stringify(msgs[i]);
|
|
172
|
-
next[i] = ser;
|
|
173
|
-
if (prev[i] !== ser) q.upsertMessage.run(s.id, i, msgs[i] && msgs[i].kind ? String(msgs[i].kind) : null, bodyOf(msgs[i]), ser);
|
|
174
|
-
}
|
|
175
|
-
if (prev.length > msgs.length) q.trimMessages.run(s.id, msgs.length);
|
|
176
|
-
seenMessages.set(s.id, next);
|
|
177
|
-
});
|
|
178
|
-
for (const { id } of q.listSessionIds.all()) {
|
|
179
|
-
if (!keep.has(id)) { q.deleteSession.run(id); seenSession.delete(id); seenMessages.delete(id); }
|
|
180
|
-
}
|
|
181
|
-
const keys = new Set();
|
|
182
|
-
for (const k of Object.keys(store)) {
|
|
183
|
-
if (k === 'sessions') continue;
|
|
184
|
-
keys.add(k);
|
|
185
|
-
if (store[k] === undefined) continue;
|
|
186
|
-
const ser = JSON.stringify(store[k]);
|
|
187
|
-
if (seenKv.get(k) !== ser) { q.upsertKv.run(k, ser); seenKv.set(k, ser); }
|
|
188
|
-
}
|
|
189
|
-
for (const k of [...seenKv.keys()]) if (!keys.has(k)) { q.deleteKv.run(k); seenKv.delete(k); }
|
|
190
|
-
});
|
|
191
|
-
},
|
|
192
|
-
|
|
193
|
-
search(query, limit = 50) {
|
|
194
|
-
const term = String(query || '').trim();
|
|
195
|
-
if (!term) return [];
|
|
196
|
-
const like = `%${term.replace(/[\\%_]/g, (c) => `\\${c}`)}%`;
|
|
197
|
-
return q.search.all(like, Math.min(Math.max(limit | 0, 1), 200)).map((r) => ({
|
|
198
|
-
...r, snippet: snippetAround(r.body, term),
|
|
199
|
-
}));
|
|
200
|
-
},
|
|
201
|
-
|
|
202
|
-
close() { try { db.exec('PRAGMA wal_checkpoint(TRUNCATE)'); } catch {} try { db.close(); } catch {} },
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function headerOf(s, position) {
|
|
207
|
-
const extra = {};
|
|
208
|
-
for (const k of Object.keys(s)) if (!CORE_SESSION_KEYS.has(k)) extra[k] = s[k];
|
|
209
|
-
return JSON.stringify([s.title, s.cwd, s.alwaysAllowed || [], extra, Number(s.createdAt) || 0, Number(s.updatedAt) || 0, position]);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function snippetAround(text, term) {
|
|
213
|
-
const t = String(text || '');
|
|
214
|
-
const at = t.toLowerCase().indexOf(term.toLowerCase());
|
|
215
|
-
if (at < 0) return t.slice(0, 120);
|
|
216
|
-
const from = Math.max(0, at - 50);
|
|
217
|
-
return `${from > 0 ? '...' : ''}${t.slice(from, at + term.length + 70).replace(/\s+/g, ' ')}${at + term.length + 70 < t.length ? '...' : ''}`;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
module.exports = { openSessionDb };
|
|
1
|
+
/**
|
|
2
|
+
* SQLite-backed session store. Replaces rewriting one big JSON file on every
|
|
3
|
+
* save: each session and each message is a row, and a save only writes the rows
|
|
4
|
+
* whose content changed. Messages are searchable across every session.
|
|
5
|
+
*
|
|
6
|
+
* Driver order (first that loads wins), all with the same sync API:
|
|
7
|
+
* 1. node:sqlite (Node 22.5+, built in, nothing to install)
|
|
8
|
+
* 2. better-sqlite3 (optional dependency; Electron 29 needs this one)
|
|
9
|
+
* When neither loads, openSessionDb() returns null and the caller keeps its
|
|
10
|
+
* JSON file, so a machine without SQLite never loses anything.
|
|
11
|
+
*
|
|
12
|
+
* Shape it stores (same as the old JSON store):
|
|
13
|
+
* { sessions: [{ id, title, cwd, messages: [...], alwaysAllowed, createdAt, updatedAt, ...extra }],
|
|
14
|
+
* <any other top-level key>: value }
|
|
15
|
+
*/
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
|
|
19
|
+
const SCHEMA_VERSION = 1;
|
|
20
|
+
|
|
21
|
+
function loadDriver(modulePaths) {
|
|
22
|
+
try {
|
|
23
|
+
const { DatabaseSync } = require('node:sqlite');
|
|
24
|
+
if (DatabaseSync) return { kind: 'node:sqlite', open: (f) => new DatabaseSync(f) };
|
|
25
|
+
} catch {}
|
|
26
|
+
try {
|
|
27
|
+
let mod;
|
|
28
|
+
try { mod = require('better-sqlite3'); }
|
|
29
|
+
catch (e) {
|
|
30
|
+
if (!modulePaths || !modulePaths.length) throw e;
|
|
31
|
+
mod = require(require.resolve('better-sqlite3', { paths: modulePaths }));
|
|
32
|
+
}
|
|
33
|
+
return { kind: 'better-sqlite3', open: (f) => new mod(f) };
|
|
34
|
+
} catch {}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const CORE_SESSION_KEYS = new Set(['id', 'title', 'cwd', 'messages', 'alwaysAllowed', 'createdAt', 'updatedAt']);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {string} file database path (created if missing)
|
|
42
|
+
* @param {{ modulePaths?: string[], driver?: 'node:sqlite'|'better-sqlite3' }} [opts]
|
|
43
|
+
* @returns {null | { kind:string, file:string, isEmpty():boolean, load():object, save(store:object):void, search(q:string, limit?:number):object[], close():void }}
|
|
44
|
+
*/
|
|
45
|
+
function openSessionDb(file, opts = {}) {
|
|
46
|
+
const driver = loadDriver(opts.modulePaths);
|
|
47
|
+
if (!driver || (opts.driver && driver.kind !== opts.driver)) return null;
|
|
48
|
+
let db;
|
|
49
|
+
try {
|
|
50
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
51
|
+
db = driver.open(file);
|
|
52
|
+
db.exec('PRAGMA journal_mode = WAL');
|
|
53
|
+
db.exec('PRAGMA synchronous = NORMAL');
|
|
54
|
+
db.exec('PRAGMA foreign_keys = ON');
|
|
55
|
+
db.exec(`
|
|
56
|
+
CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
57
|
+
CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
|
58
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
59
|
+
id TEXT PRIMARY KEY,
|
|
60
|
+
title TEXT,
|
|
61
|
+
cwd TEXT,
|
|
62
|
+
always_allowed TEXT,
|
|
63
|
+
extra TEXT,
|
|
64
|
+
created_at INTEGER,
|
|
65
|
+
updated_at INTEGER,
|
|
66
|
+
position INTEGER NOT NULL DEFAULT 0
|
|
67
|
+
);
|
|
68
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
69
|
+
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
70
|
+
idx INTEGER NOT NULL,
|
|
71
|
+
kind TEXT,
|
|
72
|
+
body TEXT,
|
|
73
|
+
data TEXT NOT NULL,
|
|
74
|
+
PRIMARY KEY (session_id, idx)
|
|
75
|
+
);
|
|
76
|
+
CREATE INDEX IF NOT EXISTS sessions_updated ON sessions(updated_at DESC);
|
|
77
|
+
`);
|
|
78
|
+
db.prepare('INSERT OR IGNORE INTO meta (key, value) VALUES (?, ?)').run('schema', String(SCHEMA_VERSION));
|
|
79
|
+
} catch {
|
|
80
|
+
try { db && db.close(); } catch {}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const q = {
|
|
85
|
+
countSessions: db.prepare('SELECT COUNT(*) AS n FROM sessions'),
|
|
86
|
+
countKv: db.prepare('SELECT COUNT(*) AS n FROM kv'),
|
|
87
|
+
allSessions: db.prepare('SELECT * FROM sessions ORDER BY position ASC'),
|
|
88
|
+
sessionMessages: db.prepare('SELECT data FROM messages WHERE session_id = ? ORDER BY idx ASC'),
|
|
89
|
+
allKv: db.prepare('SELECT key, value FROM kv'),
|
|
90
|
+
upsertSession: db.prepare(`INSERT INTO sessions (id, title, cwd, always_allowed, extra, created_at, updated_at, position)
|
|
91
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
92
|
+
ON CONFLICT(id) DO UPDATE SET title=excluded.title, cwd=excluded.cwd, always_allowed=excluded.always_allowed,
|
|
93
|
+
extra=excluded.extra, created_at=excluded.created_at, updated_at=excluded.updated_at, position=excluded.position`),
|
|
94
|
+
deleteSession: db.prepare('DELETE FROM sessions WHERE id = ?'),
|
|
95
|
+
listSessionIds: db.prepare('SELECT id FROM sessions'),
|
|
96
|
+
upsertMessage: db.prepare(`INSERT INTO messages (session_id, idx, kind, body, data) VALUES (?, ?, ?, ?, ?)
|
|
97
|
+
ON CONFLICT(session_id, idx) DO UPDATE SET kind=excluded.kind, body=excluded.body, data=excluded.data`),
|
|
98
|
+
trimMessages: db.prepare('DELETE FROM messages WHERE session_id = ? AND idx >= ?'),
|
|
99
|
+
upsertKv: db.prepare('INSERT INTO kv (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value=excluded.value'),
|
|
100
|
+
deleteKv: db.prepare('DELETE FROM kv WHERE key = ?'),
|
|
101
|
+
search: db.prepare(`SELECT m.session_id AS sessionId, m.idx AS idx, m.kind AS kind, m.body AS body, s.title AS title, s.cwd AS cwd, s.updated_at AS updatedAt
|
|
102
|
+
FROM messages m JOIN sessions s ON s.id = m.session_id
|
|
103
|
+
WHERE m.body LIKE ? ESCAPE '\\' ORDER BY s.updated_at DESC, m.idx ASC LIMIT ?`),
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// What the database already holds, so a save only writes what changed.
|
|
107
|
+
const seenSession = new Map(); // id -> serialized session header
|
|
108
|
+
const seenMessages = new Map(); // id -> string[] (serialized messages)
|
|
109
|
+
const seenKv = new Map(); // key -> serialized value
|
|
110
|
+
|
|
111
|
+
const tx = (fn) => {
|
|
112
|
+
db.exec('BEGIN');
|
|
113
|
+
try { fn(); db.exec('COMMIT'); } catch (e) { try { db.exec('ROLLBACK'); } catch {} throw e; }
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const bodyOf = (m) => {
|
|
117
|
+
if (!m || typeof m !== 'object') return '';
|
|
118
|
+
const t = m.text != null ? m.text : (m.label != null ? m.label : '');
|
|
119
|
+
return typeof t === 'string' ? t.slice(0, 20000) : '';
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
kind: driver.kind,
|
|
124
|
+
file,
|
|
125
|
+
|
|
126
|
+
isEmpty() {
|
|
127
|
+
return q.countSessions.get().n === 0 && q.countKv.get().n === 0;
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
load() {
|
|
131
|
+
const store = {};
|
|
132
|
+
for (const row of q.allKv.all()) {
|
|
133
|
+
try { store[row.key] = JSON.parse(row.value); seenKv.set(row.key, row.value); } catch {}
|
|
134
|
+
}
|
|
135
|
+
store.sessions = q.allSessions.all().map((r, position) => {
|
|
136
|
+
const messages = q.sessionMessages.all(r.id).map((m) => { try { return JSON.parse(m.data); } catch { return null; } }).filter(Boolean);
|
|
137
|
+
let extra = {};
|
|
138
|
+
try { extra = r.extra ? JSON.parse(r.extra) : {}; } catch {}
|
|
139
|
+
let alwaysAllowed = [];
|
|
140
|
+
try { alwaysAllowed = r.always_allowed ? JSON.parse(r.always_allowed) : []; } catch {}
|
|
141
|
+
const s = { ...extra, id: r.id, title: r.title, cwd: r.cwd, messages, alwaysAllowed, createdAt: r.created_at, updatedAt: r.updated_at };
|
|
142
|
+
seenSession.set(r.id, headerOf(s, position));
|
|
143
|
+
seenMessages.set(r.id, messages.map((m) => JSON.stringify(m)));
|
|
144
|
+
return s;
|
|
145
|
+
});
|
|
146
|
+
return store;
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
save(store) {
|
|
150
|
+
const sessions = Array.isArray(store.sessions) ? store.sessions : [];
|
|
151
|
+
tx(() => {
|
|
152
|
+
const keep = new Set();
|
|
153
|
+
sessions.forEach((s, position) => {
|
|
154
|
+
if (!s || !s.id) return;
|
|
155
|
+
keep.add(s.id);
|
|
156
|
+
const header = headerOf(s, position);
|
|
157
|
+
if (seenSession.get(s.id) !== header) {
|
|
158
|
+
const extra = {};
|
|
159
|
+
for (const k of Object.keys(s)) if (!CORE_SESSION_KEYS.has(k)) extra[k] = s[k];
|
|
160
|
+
q.upsertSession.run(
|
|
161
|
+
s.id, s.title == null ? null : String(s.title), s.cwd == null ? null : String(s.cwd),
|
|
162
|
+
JSON.stringify(s.alwaysAllowed || []), JSON.stringify(extra),
|
|
163
|
+
Number(s.createdAt) || Date.now(), Number(s.updatedAt) || Date.now(), position,
|
|
164
|
+
);
|
|
165
|
+
seenSession.set(s.id, header);
|
|
166
|
+
}
|
|
167
|
+
const msgs = Array.isArray(s.messages) ? s.messages : [];
|
|
168
|
+
const prev = seenMessages.get(s.id) || [];
|
|
169
|
+
const next = new Array(msgs.length);
|
|
170
|
+
for (let i = 0; i < msgs.length; i++) {
|
|
171
|
+
const ser = JSON.stringify(msgs[i]);
|
|
172
|
+
next[i] = ser;
|
|
173
|
+
if (prev[i] !== ser) q.upsertMessage.run(s.id, i, msgs[i] && msgs[i].kind ? String(msgs[i].kind) : null, bodyOf(msgs[i]), ser);
|
|
174
|
+
}
|
|
175
|
+
if (prev.length > msgs.length) q.trimMessages.run(s.id, msgs.length);
|
|
176
|
+
seenMessages.set(s.id, next);
|
|
177
|
+
});
|
|
178
|
+
for (const { id } of q.listSessionIds.all()) {
|
|
179
|
+
if (!keep.has(id)) { q.deleteSession.run(id); seenSession.delete(id); seenMessages.delete(id); }
|
|
180
|
+
}
|
|
181
|
+
const keys = new Set();
|
|
182
|
+
for (const k of Object.keys(store)) {
|
|
183
|
+
if (k === 'sessions') continue;
|
|
184
|
+
keys.add(k);
|
|
185
|
+
if (store[k] === undefined) continue;
|
|
186
|
+
const ser = JSON.stringify(store[k]);
|
|
187
|
+
if (seenKv.get(k) !== ser) { q.upsertKv.run(k, ser); seenKv.set(k, ser); }
|
|
188
|
+
}
|
|
189
|
+
for (const k of [...seenKv.keys()]) if (!keys.has(k)) { q.deleteKv.run(k); seenKv.delete(k); }
|
|
190
|
+
});
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
search(query, limit = 50) {
|
|
194
|
+
const term = String(query || '').trim();
|
|
195
|
+
if (!term) return [];
|
|
196
|
+
const like = `%${term.replace(/[\\%_]/g, (c) => `\\${c}`)}%`;
|
|
197
|
+
return q.search.all(like, Math.min(Math.max(limit | 0, 1), 200)).map((r) => ({
|
|
198
|
+
...r, snippet: snippetAround(r.body, term),
|
|
199
|
+
}));
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
close() { try { db.exec('PRAGMA wal_checkpoint(TRUNCATE)'); } catch {} try { db.close(); } catch {} },
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function headerOf(s, position) {
|
|
207
|
+
const extra = {};
|
|
208
|
+
for (const k of Object.keys(s)) if (!CORE_SESSION_KEYS.has(k)) extra[k] = s[k];
|
|
209
|
+
return JSON.stringify([s.title, s.cwd, s.alwaysAllowed || [], extra, Number(s.createdAt) || 0, Number(s.updatedAt) || 0, position]);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function snippetAround(text, term) {
|
|
213
|
+
const t = String(text || '');
|
|
214
|
+
const at = t.toLowerCase().indexOf(term.toLowerCase());
|
|
215
|
+
if (at < 0) return t.slice(0, 120);
|
|
216
|
+
const from = Math.max(0, at - 50);
|
|
217
|
+
return `${from > 0 ? '...' : ''}${t.slice(from, at + term.length + 70).replace(/\s+/g, ' ')}${at + term.length + 70 < t.length ? '...' : ''}`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
module.exports = { openSessionDb, loadDriver };
|