opencode-starterkit 1.0.1
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/README.md +47 -0
- package/baseline/.env.example +196 -0
- package/baseline/.template-manifest.json +646 -0
- package/baseline/.version +1 -0
- package/baseline/AGENTS.md +410 -0
- package/baseline/AGENT_ALIGNMENT.md +564 -0
- package/baseline/README.md +79 -0
- package/baseline/agent/build.md +373 -0
- package/baseline/agent/explore.md +96 -0
- package/baseline/agent/general.md +186 -0
- package/baseline/agent/painter.md +74 -0
- package/baseline/agent/plan.md +435 -0
- package/baseline/agent/review.md +243 -0
- package/baseline/agent/runner.md +79 -0
- package/baseline/agent/scout.md +100 -0
- package/baseline/agent/vision.md +91 -0
- package/baseline/command/compound.md +143 -0
- package/baseline/command/create.md +213 -0
- package/baseline/command/design.md +112 -0
- package/baseline/command/handoff.md +147 -0
- package/baseline/command/init-context.md +273 -0
- package/baseline/command/init-user.md +105 -0
- package/baseline/command/init.md +117 -0
- package/baseline/command/lfg.md +170 -0
- package/baseline/command/plan.md +355 -0
- package/baseline/command/pr.md +161 -0
- package/baseline/command/research.md +125 -0
- package/baseline/command/resume.md +87 -0
- package/baseline/command/review-codebase.md +131 -0
- package/baseline/command/ship.md +342 -0
- package/baseline/command/start.md +158 -0
- package/baseline/command/status.md +117 -0
- package/baseline/command/ui-review.md +92 -0
- package/baseline/command/ui-slop-check.md +146 -0
- package/baseline/command/verify.md +160 -0
- package/baseline/context/README.md +29 -0
- package/baseline/dcp.jsonc +72 -0
- package/baseline/memory/README.md +89 -0
- package/baseline/memory/_templates/design.md +59 -0
- package/baseline/memory/_templates/prd.md +192 -0
- package/baseline/memory/_templates/project.md +58 -0
- package/baseline/memory/_templates/proposal.md +38 -0
- package/baseline/memory/_templates/roadmap.md +93 -0
- package/baseline/memory/_templates/state.md +89 -0
- package/baseline/memory/_templates/tasks.md +198 -0
- package/baseline/memory/_templates/tech-stack.md +85 -0
- package/baseline/memory/_templates/user.md +26 -0
- package/baseline/memory/project/gotchas.md +67 -0
- package/baseline/memory/project/project.md +92 -0
- package/baseline/memory/project/roadmap.md +142 -0
- package/baseline/memory/project/state.md +84 -0
- package/baseline/memory/project/tech-stack.md +53 -0
- package/baseline/memory/project/user.md +38 -0
- package/baseline/memory/research/benchmark-framework.md +162 -0
- package/baseline/memory/research/ccpm-analysis.md +334 -0
- package/baseline/memory/research/context-management-analysis.md +685 -0
- package/baseline/memory/research/effectiveness-audit.md +213 -0
- package/baseline/memory/research/opencode-mcp-bug-report.md +129 -0
- package/baseline/memory/research/openspec-analysis.md +226 -0
- package/baseline/memory/session-context.md +40 -0
- package/baseline/opencode.json +1431 -0
- package/baseline/opencode.json.tui-migration.bak +1380 -0
- package/baseline/package-lock.json +87 -0
- package/baseline/package.json +21 -0
- package/baseline/plans/1768385996691-silent-wizard.md +247 -0
- package/baseline/plans/1770006237537-mighty-otter.md +418 -0
- package/baseline/plans/1770006913647-glowing-forest.md +170 -0
- package/baseline/plans/1770013678126-witty-planet.md +278 -0
- package/baseline/plans/1770112267595-shiny-rocket.md +258 -0
- package/baseline/plans/swarm-protocol.md +123 -0
- package/baseline/plugin/README.md +70 -0
- package/baseline/plugin/copilot-auth.ts +607 -0
- package/baseline/plugin/lib/capture.ts +177 -0
- package/baseline/plugin/lib/context.ts +198 -0
- package/baseline/plugin/lib/curator.ts +234 -0
- package/baseline/plugin/lib/db/maintenance.ts +312 -0
- package/baseline/plugin/lib/db/observations.ts +299 -0
- package/baseline/plugin/lib/db/pipeline.ts +520 -0
- package/baseline/plugin/lib/db/schema.ts +356 -0
- package/baseline/plugin/lib/db/types.ts +211 -0
- package/baseline/plugin/lib/distill.ts +376 -0
- package/baseline/plugin/lib/inject.ts +126 -0
- package/baseline/plugin/lib/memory-admin-tools.ts +188 -0
- package/baseline/plugin/lib/memory-db.ts +58 -0
- package/baseline/plugin/lib/memory-helpers.ts +111 -0
- package/baseline/plugin/lib/memory-hooks.ts +195 -0
- package/baseline/plugin/lib/memory-tools.ts +341 -0
- package/baseline/plugin/lib/notify.ts +81 -0
- package/baseline/plugin/memory.ts +89 -0
- package/baseline/plugin/notification.ts.bak +64 -0
- package/baseline/plugin/package.json +7 -0
- package/baseline/plugin/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts +178 -0
- package/baseline/plugin/sdk/copilot/chat/get-response-metadata.ts +15 -0
- package/baseline/plugin/sdk/copilot/chat/map-openai-compatible-finish-reason.ts +19 -0
- package/baseline/plugin/sdk/copilot/chat/openai-compatible-api-types.ts +72 -0
- package/baseline/plugin/sdk/copilot/chat/openai-compatible-chat-language-model.ts +833 -0
- package/baseline/plugin/sdk/copilot/chat/openai-compatible-chat-options.ts +30 -0
- package/baseline/plugin/sdk/copilot/chat/openai-compatible-metadata-extractor.ts +48 -0
- package/baseline/plugin/sdk/copilot/chat/openai-compatible-prepare-tools.ts +92 -0
- package/baseline/plugin/sdk/copilot/copilot-provider.ts +94 -0
- package/baseline/plugin/sdk/copilot/index.ts +5 -0
- package/baseline/plugin/sdk/copilot/openai-compatible-error.ts +30 -0
- package/baseline/plugin/sessions.ts +428 -0
- package/baseline/plugin/skill-mcp.ts +618 -0
- package/baseline/plugin/tsconfig.json +16 -0
- package/baseline/skill/accessibility-audit/SKILL.md +191 -0
- package/baseline/skill/agent-browser/SKILL.md +413 -0
- package/baseline/skill/agent-teams/SKILL.md +268 -0
- package/baseline/skill/augment-context-engine/SKILL.md +122 -0
- package/baseline/skill/augment-context-engine/mcp.json +6 -0
- package/baseline/skill/beads/SKILL.md +181 -0
- package/baseline/skill/beads/references/BEST_PRACTICES.md +27 -0
- package/baseline/skill/beads/references/BOUNDARIES.md +219 -0
- package/baseline/skill/beads/references/DEPENDENCIES.md +124 -0
- package/baseline/skill/beads/references/EXAMPLES.md +45 -0
- package/baseline/skill/beads/references/FILE_CLAIMING.md +101 -0
- package/baseline/skill/beads/references/GIT_SYNC.md +25 -0
- package/baseline/skill/beads/references/HIERARCHY.md +71 -0
- package/baseline/skill/beads/references/MULTI_AGENT.md +40 -0
- package/baseline/skill/beads/references/RESUMABILITY.md +177 -0
- package/baseline/skill/beads/references/SESSION_PROTOCOL.md +61 -0
- package/baseline/skill/beads/references/TASK_CREATION.md +38 -0
- package/baseline/skill/beads/references/TROUBLESHOOTING.md +38 -0
- package/baseline/skill/beads/references/WORKFLOWS.md +226 -0
- package/baseline/skill/beads-bridge/SKILL.md +321 -0
- package/baseline/skill/brainstorming/SKILL.md +114 -0
- package/baseline/skill/chrome-devtools/SKILL.md +76 -0
- package/baseline/skill/chrome-devtools/mcp.json +19 -0
- package/baseline/skill/cloudflare/SKILL.md +253 -0
- package/baseline/skill/cloudflare/references/agents-sdk/README.md +35 -0
- package/baseline/skill/cloudflare/references/agents-sdk/api.md +100 -0
- package/baseline/skill/cloudflare/references/agents-sdk/configuration.md +99 -0
- package/baseline/skill/cloudflare/references/agents-sdk/gotchas.md +59 -0
- package/baseline/skill/cloudflare/references/agents-sdk/patterns.md +89 -0
- package/baseline/skill/cloudflare/references/ai-gateway/README.md +695 -0
- package/baseline/skill/cloudflare/references/ai-search/README.md +14 -0
- package/baseline/skill/cloudflare/references/ai-search/api.md +38 -0
- package/baseline/skill/cloudflare/references/ai-search/configuration.md +52 -0
- package/baseline/skill/cloudflare/references/ai-search/gotchas.md +41 -0
- package/baseline/skill/cloudflare/references/ai-search/patterns.md +45 -0
- package/baseline/skill/cloudflare/references/analytics-engine/README.md +14 -0
- package/baseline/skill/cloudflare/references/analytics-engine/api.md +27 -0
- package/baseline/skill/cloudflare/references/analytics-engine/configuration.md +45 -0
- package/baseline/skill/cloudflare/references/analytics-engine/gotchas.md +3 -0
- package/baseline/skill/cloudflare/references/analytics-engine/patterns.md +36 -0
- package/baseline/skill/cloudflare/references/api/README.md +21 -0
- package/baseline/skill/cloudflare/references/api/api.md +31 -0
- package/baseline/skill/cloudflare/references/api/configuration.md +20 -0
- package/baseline/skill/cloudflare/references/api/gotchas.md +28 -0
- package/baseline/skill/cloudflare/references/api/patterns.md +47 -0
- package/baseline/skill/cloudflare/references/api-shield/README.md +20 -0
- package/baseline/skill/cloudflare/references/api-shield/api.md +78 -0
- package/baseline/skill/cloudflare/references/api-shield/configuration.md +128 -0
- package/baseline/skill/cloudflare/references/api-shield/gotchas.md +51 -0
- package/baseline/skill/cloudflare/references/api-shield/patterns.md +145 -0
- package/baseline/skill/cloudflare/references/argo-smart-routing/README.md +16 -0
- package/baseline/skill/cloudflare/references/argo-smart-routing/api.md +50 -0
- package/baseline/skill/cloudflare/references/argo-smart-routing/configuration.md +53 -0
- package/baseline/skill/cloudflare/references/argo-smart-routing/gotchas.md +16 -0
- package/baseline/skill/cloudflare/references/argo-smart-routing/patterns.md +45 -0
- package/baseline/skill/cloudflare/references/bindings/README.md +14 -0
- package/baseline/skill/cloudflare/references/bindings/api.md +3 -0
- package/baseline/skill/cloudflare/references/bindings/configuration.md +58 -0
- package/baseline/skill/cloudflare/references/bindings/gotchas.md +35 -0
- package/baseline/skill/cloudflare/references/bindings/patterns.md +37 -0
- package/baseline/skill/cloudflare/references/bot-management/README.md +71 -0
- package/baseline/skill/cloudflare/references/bot-management/api.md +168 -0
- package/baseline/skill/cloudflare/references/bot-management/configuration.md +114 -0
- package/baseline/skill/cloudflare/references/bot-management/gotchas.md +99 -0
- package/baseline/skill/cloudflare/references/bot-management/patterns.md +125 -0
- package/baseline/skill/cloudflare/references/browser-rendering/README.md +16 -0
- package/baseline/skill/cloudflare/references/browser-rendering/api.md +54 -0
- package/baseline/skill/cloudflare/references/browser-rendering/configuration.md +47 -0
- package/baseline/skill/cloudflare/references/browser-rendering/gotchas.md +29 -0
- package/baseline/skill/cloudflare/references/browser-rendering/patterns.md +29 -0
- package/baseline/skill/cloudflare/references/c3/README.md +264 -0
- package/baseline/skill/cloudflare/references/cache-reserve/README.md +93 -0
- package/baseline/skill/cloudflare/references/cache-reserve/api.md +176 -0
- package/baseline/skill/cloudflare/references/cache-reserve/configuration.md +164 -0
- package/baseline/skill/cloudflare/references/cache-reserve/gotchas.md +203 -0
- package/baseline/skill/cloudflare/references/cache-reserve/patterns.md +180 -0
- package/baseline/skill/cloudflare/references/containers/README.md +16 -0
- package/baseline/skill/cloudflare/references/containers/api.md +43 -0
- package/baseline/skill/cloudflare/references/containers/configuration.md +56 -0
- package/baseline/skill/cloudflare/references/containers/gotchas.md +21 -0
- package/baseline/skill/cloudflare/references/containers/patterns.md +40 -0
- package/baseline/skill/cloudflare/references/cron-triggers/README.md +85 -0
- package/baseline/skill/cloudflare/references/cron-triggers/api.md +198 -0
- package/baseline/skill/cloudflare/references/cron-triggers/configuration.md +151 -0
- package/baseline/skill/cloudflare/references/cron-triggers/gotchas.md +129 -0
- package/baseline/skill/cloudflare/references/cron-triggers/patterns.md +122 -0
- package/baseline/skill/cloudflare/references/d1/README.md +92 -0
- package/baseline/skill/cloudflare/references/d1/api.md +141 -0
- package/baseline/skill/cloudflare/references/d1/configuration.md +127 -0
- package/baseline/skill/cloudflare/references/d1/gotchas.md +70 -0
- package/baseline/skill/cloudflare/references/d1/patterns.md +144 -0
- package/baseline/skill/cloudflare/references/ddos/README.md +34 -0
- package/baseline/skill/cloudflare/references/ddos/api.md +136 -0
- package/baseline/skill/cloudflare/references/ddos/configuration.md +67 -0
- package/baseline/skill/cloudflare/references/ddos/gotchas.md +114 -0
- package/baseline/skill/cloudflare/references/ddos/patterns.md +158 -0
- package/baseline/skill/cloudflare/references/do-storage/README.md +62 -0
- package/baseline/skill/cloudflare/references/do-storage/api.md +89 -0
- package/baseline/skill/cloudflare/references/do-storage/configuration.md +116 -0
- package/baseline/skill/cloudflare/references/do-storage/gotchas.md +93 -0
- package/baseline/skill/cloudflare/references/do-storage/patterns.md +112 -0
- package/baseline/skill/cloudflare/references/durable-objects/README.md +125 -0
- package/baseline/skill/cloudflare/references/durable-objects/api.md +152 -0
- package/baseline/skill/cloudflare/references/durable-objects/configuration.md +148 -0
- package/baseline/skill/cloudflare/references/durable-objects/gotchas.md +158 -0
- package/baseline/skill/cloudflare/references/durable-objects/patterns.md +255 -0
- package/baseline/skill/cloudflare/references/email-routing/README.md +18 -0
- package/baseline/skill/cloudflare/references/email-routing/api.md +46 -0
- package/baseline/skill/cloudflare/references/email-routing/configuration.md +63 -0
- package/baseline/skill/cloudflare/references/email-routing/gotchas.md +16 -0
- package/baseline/skill/cloudflare/references/email-routing/patterns.md +46 -0
- package/baseline/skill/cloudflare/references/email-workers/README.md +598 -0
- package/baseline/skill/cloudflare/references/hyperdrive/README.md +62 -0
- package/baseline/skill/cloudflare/references/hyperdrive/api.md +137 -0
- package/baseline/skill/cloudflare/references/hyperdrive/configuration.md +133 -0
- package/baseline/skill/cloudflare/references/hyperdrive/gotchas.md +184 -0
- package/baseline/skill/cloudflare/references/hyperdrive/patterns.md +176 -0
- package/baseline/skill/cloudflare/references/images/README.md +14 -0
- package/baseline/skill/cloudflare/references/images/api.md +3 -0
- package/baseline/skill/cloudflare/references/images/configuration.md +45 -0
- package/baseline/skill/cloudflare/references/images/gotchas.md +23 -0
- package/baseline/skill/cloudflare/references/images/patterns.md +31 -0
- package/baseline/skill/cloudflare/references/kv/README.md +60 -0
- package/baseline/skill/cloudflare/references/kv/api.md +114 -0
- package/baseline/skill/cloudflare/references/kv/configuration.md +92 -0
- package/baseline/skill/cloudflare/references/kv/gotchas.md +117 -0
- package/baseline/skill/cloudflare/references/kv/patterns.md +139 -0
- package/baseline/skill/cloudflare/references/miniflare/README.md +64 -0
- package/baseline/skill/cloudflare/references/miniflare/api.md +144 -0
- package/baseline/skill/cloudflare/references/miniflare/configuration.md +203 -0
- package/baseline/skill/cloudflare/references/miniflare/gotchas.md +187 -0
- package/baseline/skill/cloudflare/references/miniflare/patterns.md +211 -0
- package/baseline/skill/cloudflare/references/network-interconnect/README.md +60 -0
- package/baseline/skill/cloudflare/references/network-interconnect/api.md +240 -0
- package/baseline/skill/cloudflare/references/network-interconnect/configuration.md +127 -0
- package/baseline/skill/cloudflare/references/network-interconnect/gotchas.md +171 -0
- package/baseline/skill/cloudflare/references/network-interconnect/patterns.md +171 -0
- package/baseline/skill/cloudflare/references/observability/README.md +18 -0
- package/baseline/skill/cloudflare/references/observability/api.md +51 -0
- package/baseline/skill/cloudflare/references/observability/configuration.md +60 -0
- package/baseline/skill/cloudflare/references/observability/gotchas.md +36 -0
- package/baseline/skill/cloudflare/references/observability/patterns.md +42 -0
- package/baseline/skill/cloudflare/references/pages/README.md +76 -0
- package/baseline/skill/cloudflare/references/pages/api.md +200 -0
- package/baseline/skill/cloudflare/references/pages/configuration.md +228 -0
- package/baseline/skill/cloudflare/references/pages/gotchas.md +161 -0
- package/baseline/skill/cloudflare/references/pages/patterns.md +145 -0
- package/baseline/skill/cloudflare/references/pages-functions/README.md +57 -0
- package/baseline/skill/cloudflare/references/pages-functions/api.md +201 -0
- package/baseline/skill/cloudflare/references/pages-functions/configuration.md +159 -0
- package/baseline/skill/cloudflare/references/pages-functions/gotchas.md +151 -0
- package/baseline/skill/cloudflare/references/pages-functions/patterns.md +190 -0
- package/baseline/skill/cloudflare/references/pipelines/README.md +664 -0
- package/baseline/skill/cloudflare/references/pulumi/README.md +107 -0
- package/baseline/skill/cloudflare/references/pulumi/api.md +194 -0
- package/baseline/skill/cloudflare/references/pulumi/configuration.md +216 -0
- package/baseline/skill/cloudflare/references/pulumi/gotchas.md +223 -0
- package/baseline/skill/cloudflare/references/pulumi/patterns.md +139 -0
- package/baseline/skill/cloudflare/references/queues/README.md +69 -0
- package/baseline/skill/cloudflare/references/queues/api.md +138 -0
- package/baseline/skill/cloudflare/references/queues/configuration.md +125 -0
- package/baseline/skill/cloudflare/references/queues/gotchas.md +112 -0
- package/baseline/skill/cloudflare/references/queues/patterns.md +155 -0
- package/baseline/skill/cloudflare/references/r2/README.md +61 -0
- package/baseline/skill/cloudflare/references/r2/api.md +127 -0
- package/baseline/skill/cloudflare/references/r2/configuration.md +76 -0
- package/baseline/skill/cloudflare/references/r2/gotchas.md +94 -0
- package/baseline/skill/cloudflare/references/r2/patterns.md +127 -0
- package/baseline/skill/cloudflare/references/r2-data-catalog/README.md +18 -0
- package/baseline/skill/cloudflare/references/r2-data-catalog/api.md +29 -0
- package/baseline/skill/cloudflare/references/r2-data-catalog/configuration.md +39 -0
- package/baseline/skill/cloudflare/references/r2-data-catalog/gotchas.md +20 -0
- package/baseline/skill/cloudflare/references/r2-data-catalog/patterns.md +46 -0
- package/baseline/skill/cloudflare/references/r2-sql/README.md +512 -0
- package/baseline/skill/cloudflare/references/realtime-sfu/README.md +21 -0
- package/baseline/skill/cloudflare/references/realtime-sfu/api.md +135 -0
- package/baseline/skill/cloudflare/references/realtime-sfu/configuration.md +63 -0
- package/baseline/skill/cloudflare/references/realtime-sfu/gotchas.md +75 -0
- package/baseline/skill/cloudflare/references/realtime-sfu/patterns.md +102 -0
- package/baseline/skill/cloudflare/references/realtimekit/README.md +81 -0
- package/baseline/skill/cloudflare/references/realtimekit/api.md +164 -0
- package/baseline/skill/cloudflare/references/realtimekit/configuration.md +147 -0
- package/baseline/skill/cloudflare/references/realtimekit/gotchas.md +172 -0
- package/baseline/skill/cloudflare/references/realtimekit/patterns.md +155 -0
- package/baseline/skill/cloudflare/references/sandbox/README.md +90 -0
- package/baseline/skill/cloudflare/references/sandbox/api.md +178 -0
- package/baseline/skill/cloudflare/references/sandbox/configuration.md +131 -0
- package/baseline/skill/cloudflare/references/sandbox/gotchas.md +156 -0
- package/baseline/skill/cloudflare/references/sandbox/patterns.md +203 -0
- package/baseline/skill/cloudflare/references/secrets-store/README.md +58 -0
- package/baseline/skill/cloudflare/references/secrets-store/api.md +182 -0
- package/baseline/skill/cloudflare/references/secrets-store/configuration.md +140 -0
- package/baseline/skill/cloudflare/references/secrets-store/gotchas.md +129 -0
- package/baseline/skill/cloudflare/references/secrets-store/patterns.md +218 -0
- package/baseline/skill/cloudflare/references/smart-placement/README.md +91 -0
- package/baseline/skill/cloudflare/references/smart-placement/api.md +139 -0
- package/baseline/skill/cloudflare/references/smart-placement/configuration.md +129 -0
- package/baseline/skill/cloudflare/references/smart-placement/gotchas.md +87 -0
- package/baseline/skill/cloudflare/references/smart-placement/patterns.md +135 -0
- package/baseline/skill/cloudflare/references/snippets/README.md +15 -0
- package/baseline/skill/cloudflare/references/snippets/api.md +47 -0
- package/baseline/skill/cloudflare/references/snippets/configuration.md +33 -0
- package/baseline/skill/cloudflare/references/snippets/gotchas.md +21 -0
- package/baseline/skill/cloudflare/references/snippets/patterns.md +34 -0
- package/baseline/skill/cloudflare/references/spectrum/README.md +16 -0
- package/baseline/skill/cloudflare/references/spectrum/api.md +24 -0
- package/baseline/skill/cloudflare/references/spectrum/configuration.md +43 -0
- package/baseline/skill/cloudflare/references/spectrum/gotchas.md +42 -0
- package/baseline/skill/cloudflare/references/spectrum/patterns.md +40 -0
- package/baseline/skill/cloudflare/references/static-assets/README.md +14 -0
- package/baseline/skill/cloudflare/references/static-assets/api.md +3 -0
- package/baseline/skill/cloudflare/references/static-assets/configuration.md +47 -0
- package/baseline/skill/cloudflare/references/static-assets/gotchas.md +44 -0
- package/baseline/skill/cloudflare/references/static-assets/patterns.md +42 -0
- package/baseline/skill/cloudflare/references/stream/README.md +103 -0
- package/baseline/skill/cloudflare/references/stream/api.md +204 -0
- package/baseline/skill/cloudflare/references/stream/configuration.md +127 -0
- package/baseline/skill/cloudflare/references/stream/gotchas.md +131 -0
- package/baseline/skill/cloudflare/references/stream/patterns.md +152 -0
- package/baseline/skill/cloudflare/references/tail-workers/README.md +640 -0
- package/baseline/skill/cloudflare/references/terraform/README.md +76 -0
- package/baseline/skill/cloudflare/references/terraform/api.md +159 -0
- package/baseline/skill/cloudflare/references/terraform/configuration.md +156 -0
- package/baseline/skill/cloudflare/references/terraform/gotchas.md +207 -0
- package/baseline/skill/cloudflare/references/terraform/patterns.md +135 -0
- package/baseline/skill/cloudflare/references/tunnel/README.md +82 -0
- package/baseline/skill/cloudflare/references/tunnel/api.md +105 -0
- package/baseline/skill/cloudflare/references/tunnel/configuration.md +113 -0
- package/baseline/skill/cloudflare/references/tunnel/gotchas.md +115 -0
- package/baseline/skill/cloudflare/references/tunnel/patterns.md +157 -0
- package/baseline/skill/cloudflare/references/turn/README.md +699 -0
- package/baseline/skill/cloudflare/references/turnstile/README.md +14 -0
- package/baseline/skill/cloudflare/references/turnstile/api.md +3 -0
- package/baseline/skill/cloudflare/references/turnstile/configuration.md +19 -0
- package/baseline/skill/cloudflare/references/turnstile/gotchas.md +27 -0
- package/baseline/skill/cloudflare/references/turnstile/patterns.md +41 -0
- package/baseline/skill/cloudflare/references/vectorize/README.md +682 -0
- package/baseline/skill/cloudflare/references/waf/README.md +14 -0
- package/baseline/skill/cloudflare/references/waf/api.md +3 -0
- package/baseline/skill/cloudflare/references/waf/configuration.md +44 -0
- package/baseline/skill/cloudflare/references/waf/gotchas.md +24 -0
- package/baseline/skill/cloudflare/references/waf/patterns.md +29 -0
- package/baseline/skill/cloudflare/references/web-analytics/README.md +19 -0
- package/baseline/skill/cloudflare/references/web-analytics/api.md +52 -0
- package/baseline/skill/cloudflare/references/web-analytics/configuration.md +31 -0
- package/baseline/skill/cloudflare/references/web-analytics/gotchas.md +28 -0
- package/baseline/skill/cloudflare/references/web-analytics/patterns.md +52 -0
- package/baseline/skill/cloudflare/references/workerd/README.md +47 -0
- package/baseline/skill/cloudflare/references/workerd/api.md +199 -0
- package/baseline/skill/cloudflare/references/workerd/configuration.md +185 -0
- package/baseline/skill/cloudflare/references/workerd/gotchas.md +203 -0
- package/baseline/skill/cloudflare/references/workerd/patterns.md +216 -0
- package/baseline/skill/cloudflare/references/workers/README.md +96 -0
- package/baseline/skill/cloudflare/references/workers/api.md +137 -0
- package/baseline/skill/cloudflare/references/workers/configuration.md +147 -0
- package/baseline/skill/cloudflare/references/workers/gotchas.md +99 -0
- package/baseline/skill/cloudflare/references/workers/patterns.md +149 -0
- package/baseline/skill/cloudflare/references/workers-ai/README.md +116 -0
- package/baseline/skill/cloudflare/references/workers-for-platforms/README.md +48 -0
- package/baseline/skill/cloudflare/references/workers-for-platforms/api.md +169 -0
- package/baseline/skill/cloudflare/references/workers-for-platforms/configuration.md +136 -0
- package/baseline/skill/cloudflare/references/workers-for-platforms/gotchas.md +130 -0
- package/baseline/skill/cloudflare/references/workers-for-platforms/patterns.md +170 -0
- package/baseline/skill/cloudflare/references/workers-playground/README.md +16 -0
- package/baseline/skill/cloudflare/references/workers-playground/api.md +20 -0
- package/baseline/skill/cloudflare/references/workers-playground/configuration.md +3 -0
- package/baseline/skill/cloudflare/references/workers-playground/gotchas.md +35 -0
- package/baseline/skill/cloudflare/references/workers-playground/patterns.md +42 -0
- package/baseline/skill/cloudflare/references/workers-vpc/README.md +579 -0
- package/baseline/skill/cloudflare/references/workflows/README.md +62 -0
- package/baseline/skill/cloudflare/references/workflows/api.md +125 -0
- package/baseline/skill/cloudflare/references/workflows/configuration.md +177 -0
- package/baseline/skill/cloudflare/references/workflows/gotchas.md +136 -0
- package/baseline/skill/cloudflare/references/workflows/patterns.md +132 -0
- package/baseline/skill/cloudflare/references/wrangler/README.md +90 -0
- package/baseline/skill/cloudflare/references/wrangler/api.md +140 -0
- package/baseline/skill/cloudflare/references/wrangler/configuration.md +128 -0
- package/baseline/skill/cloudflare/references/wrangler/gotchas.md +93 -0
- package/baseline/skill/cloudflare/references/wrangler/patterns.md +150 -0
- package/baseline/skill/cloudflare/references/zaraz/README.md +360 -0
- package/baseline/skill/code-navigation/SKILL.md +130 -0
- package/baseline/skill/compaction/SKILL.md +317 -0
- package/baseline/skill/condition-based-waiting/SKILL.md +123 -0
- package/baseline/skill/condition-based-waiting/example.ts +158 -0
- package/baseline/skill/context-engineering/SKILL.md +176 -0
- package/baseline/skill/context-initialization/SKILL.md +70 -0
- package/baseline/skill/context-management/SKILL.md +163 -0
- package/baseline/skill/core-data-expert/SKILL.md +93 -0
- package/baseline/skill/core-data-expert/references/batch-operations.md +543 -0
- package/baseline/skill/core-data-expert/references/cloudkit-integration.md +259 -0
- package/baseline/skill/core-data-expert/references/concurrency.md +522 -0
- package/baseline/skill/core-data-expert/references/fetch-requests.md +643 -0
- package/baseline/skill/core-data-expert/references/glossary.md +233 -0
- package/baseline/skill/core-data-expert/references/migration.md +393 -0
- package/baseline/skill/core-data-expert/references/model-configuration.md +597 -0
- package/baseline/skill/core-data-expert/references/performance.md +300 -0
- package/baseline/skill/core-data-expert/references/persistent-history.md +553 -0
- package/baseline/skill/core-data-expert/references/project-audit.md +60 -0
- package/baseline/skill/core-data-expert/references/saving.md +574 -0
- package/baseline/skill/core-data-expert/references/stack-setup.md +625 -0
- package/baseline/skill/core-data-expert/references/testing.md +300 -0
- package/baseline/skill/core-data-expert/references/threading.md +589 -0
- package/baseline/skill/deep-research/SKILL.md +384 -0
- package/baseline/skill/defense-in-depth/SKILL.md +166 -0
- package/baseline/skill/design-system-audit/SKILL.md +153 -0
- package/baseline/skill/development-lifecycle/SKILL.md +356 -0
- package/baseline/skill/dispatching-parallel-agents/SKILL.md +191 -0
- package/baseline/skill/executing-plans/SKILL.md +247 -0
- package/baseline/skill/figma/SKILL.md +224 -0
- package/baseline/skill/finishing-a-development-branch/SKILL.md +357 -0
- package/baseline/skill/frontend-design/SKILL.md +235 -0
- package/baseline/skill/frontend-design/references/animation/motion-advanced.md +224 -0
- package/baseline/skill/frontend-design/references/animation/motion-core.md +181 -0
- package/baseline/skill/frontend-design/references/canvas/execution.md +90 -0
- package/baseline/skill/frontend-design/references/canvas/philosophy.md +94 -0
- package/baseline/skill/frontend-design/references/design/color-system.md +111 -0
- package/baseline/skill/frontend-design/references/design/interaction.md +149 -0
- package/baseline/skill/frontend-design/references/design/typography-rules.md +106 -0
- package/baseline/skill/frontend-design/references/design/ux-writing.md +99 -0
- package/baseline/skill/frontend-design/references/shadcn/accessibility.md +132 -0
- package/baseline/skill/frontend-design/references/shadcn/core-components.md +153 -0
- package/baseline/skill/frontend-design/references/shadcn/form-components.md +158 -0
- package/baseline/skill/frontend-design/references/shadcn/setup.md +69 -0
- package/baseline/skill/frontend-design/references/shadcn/theming.md +152 -0
- package/baseline/skill/frontend-design/references/tailwind/responsive.md +112 -0
- package/baseline/skill/frontend-design/references/tailwind/utilities-layout.md +134 -0
- package/baseline/skill/frontend-design/references/tailwind/utilities-styling.md +165 -0
- package/baseline/skill/frontend-design/references/tailwind/v4-config.md +147 -0
- package/baseline/skill/frontend-design/references/tailwind/v4-features.md +128 -0
- package/baseline/skill/gemini-large-context/SKILL.md +216 -0
- package/baseline/skill/index-knowledge/SKILL.md +413 -0
- package/baseline/skill/jira/SKILL.md +283 -0
- package/baseline/skill/jira/mcp.json +6 -0
- package/baseline/skill/memory-system/SKILL.md +84 -0
- package/baseline/skill/mockup-to-code/SKILL.md +184 -0
- package/baseline/skill/mqdh/SKILL.md +171 -0
- package/baseline/skill/obsidian/SKILL.md +192 -0
- package/baseline/skill/obsidian/mcp.json +22 -0
- package/baseline/skill/opensrc/SKILL.md +127 -0
- package/baseline/skill/opensrc/references/architecture.md +176 -0
- package/baseline/skill/opensrc/references/cli-usage.md +176 -0
- package/baseline/skill/opensrc/references/registry-support.md +137 -0
- package/baseline/skill/pdf-extract/SKILL.md +438 -0
- package/baseline/skill/playwright/SKILL.md +320 -0
- package/baseline/skill/playwright/mcp.json +16 -0
- package/baseline/skill/playwriter/SKILL.md +158 -0
- package/baseline/skill/polar/SKILL.md +102 -0
- package/baseline/skill/prd/SKILL.md +146 -0
- package/baseline/skill/prd-task/SKILL.md +182 -0
- package/baseline/skill/prd-task/references/prd-schema.json +124 -0
- package/baseline/skill/ralph/SKILL.md +296 -0
- package/baseline/skill/react-best-practices/AGENTS.md +2410 -0
- package/baseline/skill/react-best-practices/README.md +123 -0
- package/baseline/skill/react-best-practices/SKILL.md +133 -0
- package/baseline/skill/react-best-practices/metadata.json +15 -0
- package/baseline/skill/react-best-practices/rules/_sections.md +46 -0
- package/baseline/skill/react-best-practices/rules/_template.md +28 -0
- package/baseline/skill/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/baseline/skill/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/baseline/skill/react-best-practices/rules/async-api-routes.md +38 -0
- package/baseline/skill/react-best-practices/rules/async-defer-await.md +80 -0
- package/baseline/skill/react-best-practices/rules/async-dependencies.md +36 -0
- package/baseline/skill/react-best-practices/rules/async-parallel.md +28 -0
- package/baseline/skill/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/baseline/skill/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/baseline/skill/react-best-practices/rules/bundle-conditional.md +31 -0
- package/baseline/skill/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/baseline/skill/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/baseline/skill/react-best-practices/rules/bundle-preload.md +50 -0
- package/baseline/skill/react-best-practices/rules/client-event-listeners.md +74 -0
- package/baseline/skill/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/baseline/skill/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/baseline/skill/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/baseline/skill/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/baseline/skill/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/baseline/skill/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/baseline/skill/react-best-practices/rules/js-cache-storage.md +70 -0
- package/baseline/skill/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/baseline/skill/react-best-practices/rules/js-early-exit.md +50 -0
- package/baseline/skill/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/baseline/skill/react-best-practices/rules/js-index-maps.md +37 -0
- package/baseline/skill/react-best-practices/rules/js-length-check-first.md +49 -0
- package/baseline/skill/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/baseline/skill/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/baseline/skill/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/baseline/skill/react-best-practices/rules/rendering-activity.md +26 -0
- package/baseline/skill/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/baseline/skill/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/baseline/skill/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/baseline/skill/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/baseline/skill/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/baseline/skill/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/baseline/skill/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/baseline/skill/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/baseline/skill/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/baseline/skill/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/baseline/skill/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/baseline/skill/react-best-practices/rules/rerender-memo.md +44 -0
- package/baseline/skill/react-best-practices/rules/rerender-transitions.md +40 -0
- package/baseline/skill/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/baseline/skill/react-best-practices/rules/server-cache-lru.md +41 -0
- package/baseline/skill/react-best-practices/rules/server-cache-react.md +76 -0
- package/baseline/skill/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/baseline/skill/react-best-practices/rules/server-serialization.md +38 -0
- package/baseline/skill/receiving-code-review/SKILL.md +252 -0
- package/baseline/skill/requesting-code-review/SKILL.md +397 -0
- package/baseline/skill/requesting-code-review/review.md +160 -0
- package/baseline/skill/resend/SKILL.md +177 -0
- package/baseline/skill/resend/references/react-email.md +287 -0
- package/baseline/skill/resend/references/receive-email.md +248 -0
- package/baseline/skill/resend/references/send-email.md +318 -0
- package/baseline/skill/root-cause-tracing/SKILL.md +192 -0
- package/baseline/skill/root-cause-tracing/find-polluter.sh +63 -0
- package/baseline/skill/session-management/SKILL.md +9 -0
- package/baseline/skill/sharing-skills/SKILL.md +214 -0
- package/baseline/skill/skill-creator/SKILL.md +156 -0
- package/baseline/skill/source-code-research/SKILL.md +293 -0
- package/baseline/skill/source-code-research/references/analysis-tips.md +43 -0
- package/baseline/skill/source-code-research/references/anti-patterns.md +36 -0
- package/baseline/skill/source-code-research/references/common-patterns.md +57 -0
- package/baseline/skill/source-code-research/references/example-workflow.md +60 -0
- package/baseline/skill/source-code-research/references/further-reading.md +5 -0
- package/baseline/skill/source-code-research/references/source-structure.md +45 -0
- package/baseline/skill/stitch/SKILL.md +147 -0
- package/baseline/skill/stitch/mcp.json +9 -0
- package/baseline/skill/structured-edit/SKILL.md +181 -0
- package/baseline/skill/subagent-driven-development/SKILL.md +237 -0
- package/baseline/skill/supabase/SKILL.md +130 -0
- package/baseline/skill/supabase/mcp.json +27 -0
- package/baseline/skill/supabase-postgres-best-practices/AGENTS.md +1490 -0
- package/baseline/skill/supabase-postgres-best-practices/SKILL.md +65 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/advanced-full-text-search.md +55 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/advanced-jsonb-indexing.md +49 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/conn-idle-timeout.md +46 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/conn-limits.md +44 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/conn-pooling.md +41 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/conn-prepared-statements.md +46 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/data-batch-inserts.md +54 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/data-n-plus-one.md +53 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/data-pagination.md +50 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/data-upsert.md +50 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/lock-advisory.md +56 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/lock-deadlock-prevention.md +68 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/lock-short-transactions.md +50 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/lock-skip-locked.md +54 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/monitor-explain-analyze.md +45 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/monitor-pg-stat-statements.md +55 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/monitor-vacuum-analyze.md +55 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/query-composite-indexes.md +44 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/query-covering-indexes.md +40 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/query-index-types.md +45 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/query-missing-indexes.md +43 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/query-partial-indexes.md +45 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/schema-data-types.md +46 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/schema-foreign-key-indexes.md +59 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/schema-lowercase-identifiers.md +55 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/schema-partitioning.md +55 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/schema-primary-keys.md +61 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/security-privileges.md +54 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/security-rls-basics.md +50 -0
- package/baseline/skill/supabase-postgres-best-practices/rules/security-rls-performance.md +57 -0
- package/baseline/skill/swarm-coordination/SKILL.md +179 -0
- package/baseline/skill/swarm-coordination/references/architecture.md +39 -0
- package/baseline/skill/swarm-coordination/references/delegation-worker-protocol.md +145 -0
- package/baseline/skill/swarm-coordination/references/dependency-graph.md +50 -0
- package/baseline/skill/swarm-coordination/references/drift-check.md +90 -0
- package/baseline/skill/swarm-coordination/references/integration-beads.md +20 -0
- package/baseline/skill/swarm-coordination/references/launch-flow.md +186 -0
- package/baseline/skill/swarm-coordination/references/reconciler.md +172 -0
- package/baseline/skill/swarm-coordination/references/tier-enforcement.md +78 -0
- package/baseline/skill/swarm-coordination/references/tmux-integration.md +134 -0
- package/baseline/skill/swift-concurrency/SKILL.md +266 -0
- package/baseline/skill/swift-concurrency/references/actors.md +640 -0
- package/baseline/skill/swift-concurrency/references/async-algorithms.md +822 -0
- package/baseline/skill/swift-concurrency/references/async-await-basics.md +249 -0
- package/baseline/skill/swift-concurrency/references/async-sequences.md +670 -0
- package/baseline/skill/swift-concurrency/references/core-data.md +533 -0
- package/baseline/skill/swift-concurrency/references/glossary.md +128 -0
- package/baseline/skill/swift-concurrency/references/linting.md +142 -0
- package/baseline/skill/swift-concurrency/references/memory-management.md +542 -0
- package/baseline/skill/swift-concurrency/references/migration.md +1076 -0
- package/baseline/skill/swift-concurrency/references/performance.md +574 -0
- package/baseline/skill/swift-concurrency/references/sendable.md +578 -0
- package/baseline/skill/swift-concurrency/references/tasks.md +604 -0
- package/baseline/skill/swift-concurrency/references/testing.md +565 -0
- package/baseline/skill/swift-concurrency/references/threading.md +452 -0
- package/baseline/skill/swiftui-expert-skill/SKILL.md +329 -0
- package/baseline/skill/swiftui-expert-skill/references/animation-advanced.md +351 -0
- package/baseline/skill/swiftui-expert-skill/references/animation-basics.md +284 -0
- package/baseline/skill/swiftui-expert-skill/references/animation-transitions.md +326 -0
- package/baseline/skill/swiftui-expert-skill/references/image-optimization.md +286 -0
- package/baseline/skill/swiftui-expert-skill/references/layout-best-practices.md +312 -0
- package/baseline/skill/swiftui-expert-skill/references/liquid-glass.md +377 -0
- package/baseline/skill/swiftui-expert-skill/references/list-patterns.md +153 -0
- package/baseline/skill/swiftui-expert-skill/references/modern-apis.md +400 -0
- package/baseline/skill/swiftui-expert-skill/references/performance-patterns.md +377 -0
- package/baseline/skill/swiftui-expert-skill/references/scroll-patterns.md +305 -0
- package/baseline/skill/swiftui-expert-skill/references/sheet-navigation-patterns.md +292 -0
- package/baseline/skill/swiftui-expert-skill/references/state-management.md +447 -0
- package/baseline/skill/swiftui-expert-skill/references/text-formatting.md +285 -0
- package/baseline/skill/swiftui-expert-skill/references/view-structure.md +276 -0
- package/baseline/skill/systematic-debugging/SKILL.md +402 -0
- package/baseline/skill/test-driven-development/SKILL.md +388 -0
- package/baseline/skill/testing-anti-patterns/SKILL.md +333 -0
- package/baseline/skill/testing-skills-with-subagents/SKILL.md +405 -0
- package/baseline/skill/tilth-cli/SKILL.md +180 -0
- package/baseline/skill/tool-priority/SKILL.md +299 -0
- package/baseline/skill/ui-ux-research/SKILL.md +9 -0
- package/baseline/skill/using-git-worktrees/SKILL.md +259 -0
- package/baseline/skill/using-skills/SKILL.md +117 -0
- package/baseline/skill/v0/SKILL.md +158 -0
- package/baseline/skill/v1-run/SKILL.md +175 -0
- package/baseline/skill/v1-run/mcp.json +6 -0
- package/baseline/skill/vercel-deploy-claimable/SKILL.md +124 -0
- package/baseline/skill/vercel-deploy-claimable/scripts/deploy.sh +249 -0
- package/baseline/skill/verification-before-completion/SKILL.md +236 -0
- package/baseline/skill/verification-before-completion/references/VERIFICATION_PROTOCOL.md +171 -0
- package/baseline/skill/visual-analysis/SKILL.md +154 -0
- package/baseline/skill/web-design-guidelines/SKILL.md +46 -0
- package/baseline/skill/writing-plans/SKILL.md +320 -0
- package/baseline/skill/writing-skills/SKILL.md +287 -0
- package/baseline/skill/writing-skills/anthropic-best-practices.md +1173 -0
- package/baseline/skill/writing-skills/graphviz-conventions.dot +172 -0
- package/baseline/skill/writing-skills/persuasion-principles.md +220 -0
- package/baseline/skill/writing-skills/references/anti-patterns.md +25 -0
- package/baseline/skill/writing-skills/references/claude-search-optimization.md +140 -0
- package/baseline/skill/writing-skills/references/discovery-workflow.md +11 -0
- package/baseline/skill/writing-skills/references/file-organization.md +32 -0
- package/baseline/skill/writing-skills/references/flowcharts-and-examples.md +57 -0
- package/baseline/skill/writing-skills/references/rationalization-hardening.md +75 -0
- package/baseline/skill/writing-skills/references/testing-skill-types.md +52 -0
- package/baseline/tool/context7.ts +191 -0
- package/baseline/tool/grepsearch.ts +143 -0
- package/baseline/tsconfig.json +21 -0
- package/baseline/tui.json +15 -0
- package/bin/mrc-opc.mjs +25 -0
- package/bin/ocp.mjs +25 -0
- package/bin/opc.mjs +25 -0
- package/bin/opencode-starterkit.mjs +25 -0
- package/docs/reports/2026-03-18-memory-db-architecture-note.md +47 -0
- package/docs/reports/2026-03-18-vat-opencode-classification-matrix.md +180 -0
- package/package.json +25 -0
- package/src/cli.mjs +41 -0
- package/src/config-merge.mjs +61 -0
- package/src/constants.mjs +58 -0
- package/src/fs-utils.mjs +46 -0
- package/src/install-global.mjs +102 -0
- package/src/install-project.mjs +81 -0
- package/src/memory-bootstrap.mjs +29 -0
- package/src/prompt.mjs +31 -0
- package/src/templates.mjs +31 -0
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
# Persistent History Tracking
|
|
2
|
+
|
|
3
|
+
Persistent history tracking enables Core Data to track changes across contexts, app extensions, and batch operations. This is essential for keeping your UI synchronized and supporting multi-target apps.
|
|
4
|
+
|
|
5
|
+
## Why Persistent History Tracking?
|
|
6
|
+
|
|
7
|
+
**Without persistent history tracking:**
|
|
8
|
+
- Batch operations don't update UI
|
|
9
|
+
- App extensions can't notify main app of changes
|
|
10
|
+
- Multiple contexts don't stay synchronized
|
|
11
|
+
|
|
12
|
+
**With persistent history tracking:**
|
|
13
|
+
- All changes are recorded in a transaction log
|
|
14
|
+
- Changes can be merged into any context
|
|
15
|
+
- Works across app targets (main app, extensions, etc.)
|
|
16
|
+
|
|
17
|
+
## Enabling Persistent History Tracking
|
|
18
|
+
|
|
19
|
+
### In NSPersistentContainer
|
|
20
|
+
|
|
21
|
+
```swift
|
|
22
|
+
class PersistentContainer: NSPersistentContainer {
|
|
23
|
+
override init(name: String, managedObjectModel model: NSManagedObjectModel) {
|
|
24
|
+
super.init(name: name, managedObjectModel: model)
|
|
25
|
+
|
|
26
|
+
guard let description = persistentStoreDescriptions.first else {
|
|
27
|
+
fatalError("No store description")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Enable persistent history tracking
|
|
31
|
+
description.setOption(true as NSNumber,
|
|
32
|
+
forKey: NSPersistentHistoryTrackingKey)
|
|
33
|
+
|
|
34
|
+
// Enable remote change notifications
|
|
35
|
+
description.setOption(true as NSNumber,
|
|
36
|
+
forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
|
|
37
|
+
|
|
38
|
+
loadPersistentStores { description, error in
|
|
39
|
+
if let error = error {
|
|
40
|
+
fatalError("Failed to load store: \(error)")
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### For App Groups (Extensions)
|
|
48
|
+
|
|
49
|
+
```swift
|
|
50
|
+
let storeURL = FileManager.default.containerURL(
|
|
51
|
+
forSecurityApplicationGroupIdentifier: "group.com.example.app"
|
|
52
|
+
)?.appendingPathComponent("Shared.sqlite")
|
|
53
|
+
|
|
54
|
+
let description = NSPersistentStoreDescription(url: storeURL!)
|
|
55
|
+
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
|
|
56
|
+
description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
|
|
57
|
+
|
|
58
|
+
container.persistentStoreDescriptions = [description]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## The Four Components
|
|
62
|
+
|
|
63
|
+
Persistent history tracking typically involves four components:
|
|
64
|
+
|
|
65
|
+
1. **Observer** - Listens for remote change notifications
|
|
66
|
+
2. **Fetcher** - Retrieves relevant transactions
|
|
67
|
+
3. **Merger** - Merges transactions into view context
|
|
68
|
+
4. **Cleaner** - Removes old transactions
|
|
69
|
+
|
|
70
|
+
## 1. Observer: Listening for Changes
|
|
71
|
+
|
|
72
|
+
```swift
|
|
73
|
+
final class PersistentHistoryObserver {
|
|
74
|
+
private let coordinator: NSPersistentStoreCoordinator
|
|
75
|
+
private let historyContext: NSManagedObjectContext
|
|
76
|
+
private let merger: PersistentHistoryMerger
|
|
77
|
+
|
|
78
|
+
init(container: NSPersistentContainer, viewContext: NSManagedObjectContext) {
|
|
79
|
+
self.coordinator = container.persistentStoreCoordinator
|
|
80
|
+
self.historyContext = container.newBackgroundContext()
|
|
81
|
+
self.historyContext.name = "PersistentHistoryContext"
|
|
82
|
+
self.historyContext.transactionAuthor = "PersistentHistory"
|
|
83
|
+
self.merger = PersistentHistoryMerger(historyContext: historyContext, viewContext: viewContext)
|
|
84
|
+
|
|
85
|
+
NotificationCenter.default.addObserver(
|
|
86
|
+
self,
|
|
87
|
+
selector: #selector(processStoreRemoteChanges),
|
|
88
|
+
name: .NSPersistentStoreRemoteChange,
|
|
89
|
+
object: coordinator
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@objc private func processStoreRemoteChanges(_ notification: Notification) {
|
|
94
|
+
merger.merge()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
deinit {
|
|
98
|
+
NotificationCenter.default.removeObserver(self)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## 2. Fetcher: Retrieving Transactions
|
|
104
|
+
|
|
105
|
+
```swift
|
|
106
|
+
class PersistentHistoryFetcher {
|
|
107
|
+
private let context: NSManagedObjectContext
|
|
108
|
+
private let lastToken: NSPersistentHistoryToken?
|
|
109
|
+
|
|
110
|
+
init(context: NSManagedObjectContext, lastToken: NSPersistentHistoryToken?) {
|
|
111
|
+
self.context = context
|
|
112
|
+
self.lastToken = lastToken
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
func fetch() throws -> [NSPersistentHistoryTransaction] {
|
|
116
|
+
let fetchRequest = createFetchRequest()
|
|
117
|
+
|
|
118
|
+
guard let historyResult = try context.execute(fetchRequest) as? NSPersistentHistoryResult,
|
|
119
|
+
let transactions = historyResult.result as? [NSPersistentHistoryTransaction] else {
|
|
120
|
+
return []
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return transactions
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private func createFetchRequest() -> NSPersistentHistoryChangeRequest {
|
|
127
|
+
let request: NSPersistentHistoryChangeRequest
|
|
128
|
+
|
|
129
|
+
if let token = lastToken {
|
|
130
|
+
request = NSPersistentHistoryChangeRequest.fetchHistory(after: token)
|
|
131
|
+
} else {
|
|
132
|
+
request = NSPersistentHistoryChangeRequest.fetchHistory(after: Date.distantPast)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Filter out transactions from this app target
|
|
136
|
+
if let fetchRequest = request.fetchRequest {
|
|
137
|
+
fetchRequest.predicate = NSPredicate(
|
|
138
|
+
format: "author != %@",
|
|
139
|
+
"MainApp" // Your app's transaction author
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return request
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## 3. Merger: Applying Changes
|
|
149
|
+
|
|
150
|
+
```swift
|
|
151
|
+
final class PersistentHistoryMerger {
|
|
152
|
+
private let historyContext: NSManagedObjectContext
|
|
153
|
+
private let viewContext: NSManagedObjectContext
|
|
154
|
+
private var lastToken: NSPersistentHistoryToken?
|
|
155
|
+
|
|
156
|
+
init(historyContext: NSManagedObjectContext, viewContext: NSManagedObjectContext) {
|
|
157
|
+
self.historyContext = historyContext
|
|
158
|
+
self.viewContext = viewContext
|
|
159
|
+
self.lastToken = loadLastToken()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
func merge() {
|
|
163
|
+
historyContext.perform {
|
|
164
|
+
do {
|
|
165
|
+
let fetcher = PersistentHistoryFetcher(
|
|
166
|
+
context: self.historyContext,
|
|
167
|
+
lastToken: self.lastToken
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
let transactions = try fetcher.fetch()
|
|
171
|
+
guard !transactions.isEmpty else { return }
|
|
172
|
+
|
|
173
|
+
self.viewContext.perform {
|
|
174
|
+
self.mergeTransactions(transactions)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if let newToken = transactions.last?.token {
|
|
178
|
+
self.lastToken = newToken
|
|
179
|
+
self.saveLastToken(newToken)
|
|
180
|
+
}
|
|
181
|
+
} catch {
|
|
182
|
+
print("Failed to merge history: \(error)")
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private func mergeTransactions(_ transactions: [NSPersistentHistoryTransaction]) {
|
|
188
|
+
for transaction in transactions {
|
|
189
|
+
guard let userInfo = transaction.objectIDNotification().userInfo else { continue }
|
|
190
|
+
NSManagedObjectContext.mergeChanges(fromRemoteContextSave: userInfo, into: [viewContext])
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private func loadLastToken() -> NSPersistentHistoryToken? {
|
|
195
|
+
guard let data = UserDefaults.standard.data(forKey: "lastHistoryToken") else {
|
|
196
|
+
return nil
|
|
197
|
+
}
|
|
198
|
+
return try? NSKeyedUnarchiver.unarchivedObject(
|
|
199
|
+
ofClass: NSPersistentHistoryToken.self,
|
|
200
|
+
from: data
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private func saveLastToken(_ token: NSPersistentHistoryToken) {
|
|
205
|
+
if let data = try? NSKeyedArchiver.archivedData(
|
|
206
|
+
withRootObject: token,
|
|
207
|
+
requiringSecureCoding: true
|
|
208
|
+
) {
|
|
209
|
+
UserDefaults.standard.set(data, forKey: "lastHistoryToken")
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## 4. Cleaner: Removing Old Transactions
|
|
216
|
+
|
|
217
|
+
```swift
|
|
218
|
+
class PersistentHistoryCleaner {
|
|
219
|
+
private let context: NSManagedObjectContext
|
|
220
|
+
private let targets: [AppTarget]
|
|
221
|
+
|
|
222
|
+
enum AppTarget {
|
|
223
|
+
case mainApp
|
|
224
|
+
case shareExtension
|
|
225
|
+
case widgetExtension
|
|
226
|
+
|
|
227
|
+
var lastTokenKey: String {
|
|
228
|
+
switch self {
|
|
229
|
+
case .mainApp: return "mainApp.lastHistoryToken"
|
|
230
|
+
case .shareExtension: return "shareExtension.lastHistoryToken"
|
|
231
|
+
case .widgetExtension: return "widgetExtension.lastHistoryToken"
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
init(context: NSManagedObjectContext, targets: [AppTarget]) {
|
|
237
|
+
self.context = context
|
|
238
|
+
self.targets = targets
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
func clean() {
|
|
242
|
+
context.perform {
|
|
243
|
+
// Find the oldest token across all targets
|
|
244
|
+
guard let oldestToken = self.findOldestToken() else { return }
|
|
245
|
+
|
|
246
|
+
// Delete history before that token
|
|
247
|
+
let deleteRequest = NSPersistentHistoryChangeRequest.deleteHistory(before: oldestToken)
|
|
248
|
+
|
|
249
|
+
do {
|
|
250
|
+
try self.context.execute(deleteRequest)
|
|
251
|
+
} catch {
|
|
252
|
+
print("Failed to clean history: \(error)")
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private func findOldestToken() -> NSPersistentHistoryToken? {
|
|
258
|
+
var oldestDate: Date?
|
|
259
|
+
var oldestToken: NSPersistentHistoryToken?
|
|
260
|
+
|
|
261
|
+
for target in targets {
|
|
262
|
+
guard let token = loadToken(for: target) else { continue }
|
|
263
|
+
|
|
264
|
+
// Get timestamp from token (requires fetching transaction)
|
|
265
|
+
let historyRequest = NSPersistentHistoryChangeRequest.fetchHistory(after: token)
|
|
266
|
+
historyRequest.fetchRequest?.fetchLimit = 1
|
|
267
|
+
|
|
268
|
+
guard let result = try? context.execute(historyRequest) as? NSPersistentHistoryResult,
|
|
269
|
+
let transactions = result.result as? [NSPersistentHistoryTransaction],
|
|
270
|
+
let transaction = transactions.first else {
|
|
271
|
+
continue
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
let date = transaction.timestamp
|
|
275
|
+
if oldestDate == nil || date < oldestDate! {
|
|
276
|
+
oldestDate = date
|
|
277
|
+
oldestToken = token
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return oldestToken
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private func loadToken(for target: AppTarget) -> NSPersistentHistoryToken? {
|
|
285
|
+
guard let data = UserDefaults.standard.data(forKey: target.lastTokenKey) else {
|
|
286
|
+
return nil
|
|
287
|
+
}
|
|
288
|
+
return try? NSKeyedUnarchiver.unarchivedObject(
|
|
289
|
+
ofClass: NSPersistentHistoryToken.self,
|
|
290
|
+
from: data
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Complete Integration Example
|
|
297
|
+
|
|
298
|
+
```swift
|
|
299
|
+
class CoreDataStack {
|
|
300
|
+
static let shared = CoreDataStack()
|
|
301
|
+
|
|
302
|
+
lazy var persistentContainer: NSPersistentContainer = {
|
|
303
|
+
let container = NSPersistentContainer(name: "Model")
|
|
304
|
+
|
|
305
|
+
// Configure store
|
|
306
|
+
guard let description = container.persistentStoreDescriptions.first else {
|
|
307
|
+
fatalError("No store description")
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Enable persistent history tracking
|
|
311
|
+
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
|
|
312
|
+
description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
|
|
313
|
+
|
|
314
|
+
container.loadPersistentStores { description, error in
|
|
315
|
+
if let error = error {
|
|
316
|
+
fatalError("Failed to load store: \(error)")
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
self.setupHistoryTracking(container: container)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Configure view context
|
|
323
|
+
container.viewContext.automaticallyMergesChangesFromParent = true
|
|
324
|
+
container.viewContext.name = "ViewContext"
|
|
325
|
+
container.viewContext.transactionAuthor = "MainApp"
|
|
326
|
+
|
|
327
|
+
return container
|
|
328
|
+
}()
|
|
329
|
+
|
|
330
|
+
private var historyObserver: PersistentHistoryObserver?
|
|
331
|
+
|
|
332
|
+
private init() {}
|
|
333
|
+
|
|
334
|
+
private func setupHistoryTracking(container: NSPersistentContainer) {
|
|
335
|
+
historyObserver = PersistentHistoryObserver(container: container, viewContext: container.viewContext)
|
|
336
|
+
cleanHistoryPeriodically(container: container)
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
private func cleanHistoryPeriodically(container: NSPersistentContainer) {
|
|
340
|
+
Timer.scheduledTimer(withTimeInterval: 3600, repeats: true) { _ in
|
|
341
|
+
let context = container.newBackgroundContext()
|
|
342
|
+
let cleaner = PersistentHistoryCleaner(
|
|
343
|
+
context: context,
|
|
344
|
+
targets: [.mainApp, .shareExtension]
|
|
345
|
+
)
|
|
346
|
+
cleaner.clean()
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Transaction Authors
|
|
353
|
+
|
|
354
|
+
Set unique transaction authors for each app target:
|
|
355
|
+
|
|
356
|
+
```swift
|
|
357
|
+
// Main app
|
|
358
|
+
viewContext.transactionAuthor = "MainApp"
|
|
359
|
+
|
|
360
|
+
// Share extension
|
|
361
|
+
viewContext.transactionAuthor = "ShareExtension"
|
|
362
|
+
|
|
363
|
+
// Widget extension
|
|
364
|
+
viewContext.transactionAuthor = "WidgetExtension"
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
**Why this matters:**
|
|
368
|
+
- Filter out your own transactions (avoid redundant merges)
|
|
369
|
+
- Identify which target made changes
|
|
370
|
+
- Debug multi-target issues
|
|
371
|
+
|
|
372
|
+
## Filtering Transactions
|
|
373
|
+
|
|
374
|
+
### By Author
|
|
375
|
+
|
|
376
|
+
```swift
|
|
377
|
+
let fetchRequest = NSPersistentHistoryChangeRequest.fetchHistory(after: lastToken)
|
|
378
|
+
if let request = fetchRequest.fetchRequest {
|
|
379
|
+
request.predicate = NSPredicate(format: "author != %@", "MainApp")
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### By Date
|
|
384
|
+
|
|
385
|
+
```swift
|
|
386
|
+
let cutoffDate = Calendar.current.date(byAdding: .day, value: -7, to: Date())!
|
|
387
|
+
let fetchRequest = NSPersistentHistoryChangeRequest.fetchHistory(after: cutoffDate)
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### By Entity
|
|
391
|
+
|
|
392
|
+
```swift
|
|
393
|
+
let fetchRequest = NSPersistentHistoryChangeRequest.fetchHistory(after: lastToken)
|
|
394
|
+
if let request = fetchRequest.fetchRequest {
|
|
395
|
+
request.predicate = NSPredicate(format: "ANY changes.changedObjectID.entity.name == %@", "Article")
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
## Batch Operations Integration
|
|
400
|
+
|
|
401
|
+
Persistent history tracking is **required** for batch operations to update the UI:
|
|
402
|
+
|
|
403
|
+
```swift
|
|
404
|
+
// 1. Enable persistent history tracking
|
|
405
|
+
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
|
|
406
|
+
|
|
407
|
+
// 2. Perform batch operation
|
|
408
|
+
let context = container.newBackgroundContext()
|
|
409
|
+
context.perform {
|
|
410
|
+
let batchInsert = NSBatchInsertRequest(entity: Article.entity()) { object in
|
|
411
|
+
// Insert logic
|
|
412
|
+
return false
|
|
413
|
+
}
|
|
414
|
+
try? context.execute(batchInsert)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// 3. UI updates automatically via persistent history tracking
|
|
418
|
+
// The observer detects the change and merges it into the view context
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
## Testing Persistent History
|
|
422
|
+
|
|
423
|
+
```swift
|
|
424
|
+
func testPersistentHistory() throws {
|
|
425
|
+
// Enable persistent history
|
|
426
|
+
let description = container.persistentStoreDescriptions.first!
|
|
427
|
+
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
|
|
428
|
+
|
|
429
|
+
// Create object in background
|
|
430
|
+
let backgroundContext = container.newBackgroundContext()
|
|
431
|
+
backgroundContext.transactionAuthor = "Test"
|
|
432
|
+
|
|
433
|
+
let expectation = XCTestExpectation(description: "Save")
|
|
434
|
+
|
|
435
|
+
backgroundContext.perform {
|
|
436
|
+
let article = Article(context: backgroundContext)
|
|
437
|
+
article.name = "Test"
|
|
438
|
+
try? backgroundContext.save()
|
|
439
|
+
expectation.fulfill()
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
wait(for: [expectation], timeout: 5.0)
|
|
443
|
+
|
|
444
|
+
// Fetch history
|
|
445
|
+
let fetchRequest = NSPersistentHistoryChangeRequest.fetchHistory(after: Date.distantPast)
|
|
446
|
+
let result = try container.viewContext.execute(fetchRequest) as? NSPersistentHistoryResult
|
|
447
|
+
let transactions = result?.result as? [NSPersistentHistoryTransaction]
|
|
448
|
+
|
|
449
|
+
XCTAssertNotNil(transactions)
|
|
450
|
+
XCTAssertFalse(transactions!.isEmpty)
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## Common Pitfalls
|
|
455
|
+
|
|
456
|
+
### ❌ Not Enabling Remote Change Notifications
|
|
457
|
+
|
|
458
|
+
```swift
|
|
459
|
+
// Only this isn't enough
|
|
460
|
+
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
|
|
461
|
+
|
|
462
|
+
// Need both!
|
|
463
|
+
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
|
|
464
|
+
description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### ❌ Not Filtering Own Transactions
|
|
468
|
+
|
|
469
|
+
```swift
|
|
470
|
+
// Merges own transactions (redundant)
|
|
471
|
+
let fetchRequest = NSPersistentHistoryChangeRequest.fetchHistory(after: lastToken)
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### ❌ Not Cleaning Old Transactions
|
|
475
|
+
|
|
476
|
+
```swift
|
|
477
|
+
// History grows unbounded, wastes space
|
|
478
|
+
// Always implement cleaning!
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### ❌ Not Setting Transaction Authors
|
|
482
|
+
|
|
483
|
+
```swift
|
|
484
|
+
// Can't filter transactions by source
|
|
485
|
+
context.transactionAuthor = nil // Bad!
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### ✅ Correct Approach
|
|
489
|
+
|
|
490
|
+
```swift
|
|
491
|
+
// 1. Enable both options
|
|
492
|
+
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
|
|
493
|
+
description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
|
|
494
|
+
|
|
495
|
+
// 2. Set transaction author
|
|
496
|
+
context.transactionAuthor = "MainApp"
|
|
497
|
+
|
|
498
|
+
// 3. Filter own transactions
|
|
499
|
+
fetchRequest.predicate = NSPredicate(format: "author != %@", "MainApp")
|
|
500
|
+
|
|
501
|
+
// 4. Clean periodically
|
|
502
|
+
let cleaner = PersistentHistoryCleaner(context: context, targets: [.mainApp, .shareExtension])
|
|
503
|
+
cleaner.clean()
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
## Performance Considerations
|
|
507
|
+
|
|
508
|
+
### Clean History Regularly
|
|
509
|
+
|
|
510
|
+
```swift
|
|
511
|
+
// Clean daily
|
|
512
|
+
Timer.scheduledTimer(withTimeInterval: 86400, repeats: true) { _ in
|
|
513
|
+
cleaner.clean()
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Or on app launch
|
|
517
|
+
func applicationDidFinishLaunching() {
|
|
518
|
+
cleaner.clean()
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### Limit Fetch Range
|
|
523
|
+
|
|
524
|
+
```swift
|
|
525
|
+
// Don't fetch all history
|
|
526
|
+
let sevenDaysAgo = Calendar.current.date(byAdding: .day, value: -7, to: Date())!
|
|
527
|
+
let fetchRequest = NSPersistentHistoryChangeRequest.fetchHistory(after: sevenDaysAgo)
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Batch Merge Changes
|
|
531
|
+
|
|
532
|
+
```swift
|
|
533
|
+
// Merge multiple transactions at once
|
|
534
|
+
let transactions = try fetcher.fetch()
|
|
535
|
+
for transaction in transactions {
|
|
536
|
+
let userInfo = transaction.objectIDNotification().userInfo
|
|
537
|
+
NSManagedObjectContext.mergeChanges(
|
|
538
|
+
fromRemoteContextSave: userInfo!,
|
|
539
|
+
into: [viewContext]
|
|
540
|
+
)
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
## Summary
|
|
545
|
+
|
|
546
|
+
1. **Enable persistent history tracking** - Required for batch operations and multi-target apps
|
|
547
|
+
2. **Enable remote change notifications** - Required for cross-context updates
|
|
548
|
+
3. **Set transaction authors** - Identify change sources
|
|
549
|
+
4. **Filter own transactions** - Avoid redundant merges
|
|
550
|
+
5. **Implement all four components** - Observer, Fetcher, Merger, Cleaner
|
|
551
|
+
6. **Clean history regularly** - Prevent unbounded growth
|
|
552
|
+
7. **Use with batch operations** - Essential for UI updates
|
|
553
|
+
8. **Test thoroughly** - Verify history tracking works across targets
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Project Audit (Core Data)
|
|
2
|
+
|
|
3
|
+
Use this checklist to quickly discover how a project uses Core Data and which constraints apply (platform availability, CloudKit, history tracking, etc.).
|
|
4
|
+
|
|
5
|
+
## Determine platform constraints
|
|
6
|
+
|
|
7
|
+
- Find the deployment target (iOS/macOS version). Many recommendations depend on this (e.g. staged migration and composite attributes require iOS 17+/macOS 14+).
|
|
8
|
+
- Note whether the project is Swift 6 / strict concurrency enabled (Sendable and isolation warnings change the advice).
|
|
9
|
+
|
|
10
|
+
## Inspect the data model
|
|
11
|
+
|
|
12
|
+
- Open the model XML (`*.xcdatamodeld/*/contents`) and check:
|
|
13
|
+
- entities, attributes, relationships, constraints
|
|
14
|
+
- versioning setup (multiple model versions)
|
|
15
|
+
- renaming identifiers (for lightweight migration)
|
|
16
|
+
- composite attributes (iOS 17+)
|
|
17
|
+
|
|
18
|
+
## Identify stack setup
|
|
19
|
+
|
|
20
|
+
Search for:
|
|
21
|
+
|
|
22
|
+
- `NSPersistentContainer` vs `NSPersistentCloudKitContainer`
|
|
23
|
+
- `loadPersistentStores` configuration
|
|
24
|
+
- `persistentStoreDescriptions` (migration options, history tracking, CloudKit options)
|
|
25
|
+
- `viewContext` configuration (merge policy, `automaticallyMergesChangesFromParent`, query generations)
|
|
26
|
+
- background context creation (`newBackgroundContext`, `performBackgroundTask`)
|
|
27
|
+
|
|
28
|
+
Then consult:
|
|
29
|
+
|
|
30
|
+
- `stack-setup.md` for recommended defaults and merge policies
|
|
31
|
+
- `cloudkit-integration.md` if CloudKit is enabled
|
|
32
|
+
|
|
33
|
+
## Check for persistent history tracking (required for some flows)
|
|
34
|
+
|
|
35
|
+
Search for:
|
|
36
|
+
|
|
37
|
+
- `NSPersistentHistoryTrackingKey`
|
|
38
|
+
- `NSPersistentStoreRemoteChangeNotificationPostOptionKey`
|
|
39
|
+
- remote change notifications and history processing/merging
|
|
40
|
+
|
|
41
|
+
Then consult:
|
|
42
|
+
|
|
43
|
+
- `persistent-history.md` for the Observer/Fetcher/Merger/Cleaner pattern
|
|
44
|
+
|
|
45
|
+
## Spot risky concurrency patterns
|
|
46
|
+
|
|
47
|
+
Search for:
|
|
48
|
+
|
|
49
|
+
- cross-thread access to managed objects (look for passing `NSManagedObject` into async tasks/closures)
|
|
50
|
+
- `performAndWait` usage (risk of deadlocks / UI blocking)
|
|
51
|
+
- `@unchecked Sendable` applied to Core Data types (usually hides a real problem)
|
|
52
|
+
|
|
53
|
+
Then consult:
|
|
54
|
+
|
|
55
|
+
- `threading.md` and `concurrency.md`
|
|
56
|
+
|
|
57
|
+
## Useful debugging flags (for repro builds only)
|
|
58
|
+
|
|
59
|
+
- `-com.apple.CoreData.ConcurrencyDebug 1` (threading violations)
|
|
60
|
+
- `-com.apple.CoreData.SQLDebug 1` (SQL logging)
|