javi-forge 0.1.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/.gitignore.template +105 -0
- package/.releaserc +44 -0
- package/README.md +45 -0
- package/ai-config/.skillignore +15 -0
- package/ai-config/AUTO_INVOKE.md +300 -0
- package/ai-config/agents/_TEMPLATE.md +93 -0
- package/ai-config/agents/business/api-designer.md +1657 -0
- package/ai-config/agents/business/business-analyst.md +1331 -0
- package/ai-config/agents/business/product-strategist.md +206 -0
- package/ai-config/agents/business/project-manager.md +178 -0
- package/ai-config/agents/business/requirements-analyst.md +1277 -0
- package/ai-config/agents/business/technical-writer.md +1679 -0
- package/ai-config/agents/creative/ux-designer.md +205 -0
- package/ai-config/agents/data-ai/ai-engineer.md +487 -0
- package/ai-config/agents/data-ai/analytics-engineer.md +953 -0
- package/ai-config/agents/data-ai/data-engineer.md +173 -0
- package/ai-config/agents/data-ai/data-scientist.md +672 -0
- package/ai-config/agents/data-ai/mlops-engineer.md +814 -0
- package/ai-config/agents/data-ai/prompt-engineer.md +772 -0
- package/ai-config/agents/development/angular-expert.md +620 -0
- package/ai-config/agents/development/backend-architect.md +795 -0
- package/ai-config/agents/development/database-specialist.md +212 -0
- package/ai-config/agents/development/frontend-specialist.md +686 -0
- package/ai-config/agents/development/fullstack-engineer.md +668 -0
- package/ai-config/agents/development/golang-pro.md +338 -0
- package/ai-config/agents/development/java-enterprise.md +400 -0
- package/ai-config/agents/development/javascript-pro.md +422 -0
- package/ai-config/agents/development/nextjs-pro.md +474 -0
- package/ai-config/agents/development/python-pro.md +570 -0
- package/ai-config/agents/development/react-pro.md +487 -0
- package/ai-config/agents/development/rust-pro.md +246 -0
- package/ai-config/agents/development/spring-boot-4-expert.md +326 -0
- package/ai-config/agents/development/typescript-pro.md +336 -0
- package/ai-config/agents/development/vue-specialist.md +605 -0
- package/ai-config/agents/infrastructure/cloud-architect.md +472 -0
- package/ai-config/agents/infrastructure/deployment-manager.md +358 -0
- package/ai-config/agents/infrastructure/devops-engineer.md +455 -0
- package/ai-config/agents/infrastructure/incident-responder.md +519 -0
- package/ai-config/agents/infrastructure/kubernetes-expert.md +705 -0
- package/ai-config/agents/infrastructure/monitoring-specialist.md +674 -0
- package/ai-config/agents/infrastructure/performance-engineer.md +658 -0
- package/ai-config/agents/orchestrator.md +241 -0
- package/ai-config/agents/quality/accessibility-auditor.md +1204 -0
- package/ai-config/agents/quality/code-reviewer-compact.md +123 -0
- package/ai-config/agents/quality/code-reviewer.md +363 -0
- package/ai-config/agents/quality/dependency-manager.md +743 -0
- package/ai-config/agents/quality/e2e-test-specialist.md +1005 -0
- package/ai-config/agents/quality/performance-tester.md +1086 -0
- package/ai-config/agents/quality/security-auditor.md +133 -0
- package/ai-config/agents/quality/test-engineer.md +453 -0
- package/ai-config/agents/specialists/api-designer.md +87 -0
- package/ai-config/agents/specialists/backend-architect.md +73 -0
- package/ai-config/agents/specialists/code-reviewer.md +77 -0
- package/ai-config/agents/specialists/db-optimizer.md +75 -0
- package/ai-config/agents/specialists/devops-engineer.md +83 -0
- package/ai-config/agents/specialists/documentation-writer.md +78 -0
- package/ai-config/agents/specialists/frontend-developer.md +75 -0
- package/ai-config/agents/specialists/performance-analyst.md +82 -0
- package/ai-config/agents/specialists/refactor-specialist.md +74 -0
- package/ai-config/agents/specialists/security-auditor.md +74 -0
- package/ai-config/agents/specialists/test-engineer.md +81 -0
- package/ai-config/agents/specialists/ux-consultant.md +76 -0
- package/ai-config/agents/specialized/agent-generator.md +1190 -0
- package/ai-config/agents/specialized/blockchain-developer.md +149 -0
- package/ai-config/agents/specialized/code-migrator.md +892 -0
- package/ai-config/agents/specialized/context-manager.md +978 -0
- package/ai-config/agents/specialized/documentation-writer.md +1078 -0
- package/ai-config/agents/specialized/ecommerce-expert.md +1756 -0
- package/ai-config/agents/specialized/embedded-engineer.md +1714 -0
- package/ai-config/agents/specialized/error-detective.md +1034 -0
- package/ai-config/agents/specialized/fintech-specialist.md +1659 -0
- package/ai-config/agents/specialized/freelance-project-planner-v2.md +1988 -0
- package/ai-config/agents/specialized/freelance-project-planner-v3.md +2136 -0
- package/ai-config/agents/specialized/freelance-project-planner-v4.md +4503 -0
- package/ai-config/agents/specialized/freelance-project-planner.md +722 -0
- package/ai-config/agents/specialized/game-developer.md +1963 -0
- package/ai-config/agents/specialized/healthcare-dev.md +1620 -0
- package/ai-config/agents/specialized/mobile-developer.md +188 -0
- package/ai-config/agents/specialized/parallel-plan-executor.md +506 -0
- package/ai-config/agents/specialized/plan-executor.md +485 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/00-INDEX.md +485 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/01-CORE.md +3493 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/02-SELF-CORRECTION.md +778 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/03-PROGRESSIVE-SETUP.md +918 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/04-DEPLOYMENT.md +1537 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/05-TESTING.md +2633 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/06-OPERATIONS.md +5610 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/INSTALL.md +335 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/QUICK-REFERENCE.txt +215 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/README.md +260 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/START-HERE.md +379 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/WORKFLOW-DIAGRAM.md +355 -0
- package/ai-config/agents/specialized/solo-dev-planner-modular/solo-dev-planner.md +279 -0
- package/ai-config/agents/specialized/template-writer.md +347 -0
- package/ai-config/agents/specialized/test-runner.md +99 -0
- package/ai-config/agents/specialized/vibekanban-smart-worker.md +244 -0
- package/ai-config/agents/specialized/wave-executor.md +138 -0
- package/ai-config/agents/specialized/workflow-optimizer.md +1114 -0
- package/ai-config/commands/git/changelog.md +32 -0
- package/ai-config/commands/git/ci-local.md +70 -0
- package/ai-config/commands/git/commit.md +35 -0
- package/ai-config/commands/git/fix-issue.md +23 -0
- package/ai-config/commands/git/pr-create.md +42 -0
- package/ai-config/commands/git/pr-review.md +50 -0
- package/ai-config/commands/git/worktree.md +39 -0
- package/ai-config/commands/refactoring/cleanup.md +24 -0
- package/ai-config/commands/refactoring/dead-code.md +40 -0
- package/ai-config/commands/refactoring/extract.md +31 -0
- package/ai-config/commands/testing/e2e.md +30 -0
- package/ai-config/commands/testing/tdd.md +36 -0
- package/ai-config/commands/testing/test-coverage.md +30 -0
- package/ai-config/commands/testing/test-fix.md +24 -0
- package/ai-config/commands/workflow/generate-agents-md.md +85 -0
- package/ai-config/commands/workflow/planning.md +47 -0
- package/ai-config/commands/workflows/compound.md +89 -0
- package/ai-config/commands/workflows/plan.md +77 -0
- package/ai-config/commands/workflows/review.md +78 -0
- package/ai-config/commands/workflows/work.md +75 -0
- package/ai-config/config.yaml +18 -0
- package/ai-config/hooks/_TEMPLATE.md +96 -0
- package/ai-config/hooks/block-dangerous-commands.md +75 -0
- package/ai-config/hooks/commit-guard.md +90 -0
- package/ai-config/hooks/context-loader.md +73 -0
- package/ai-config/hooks/improve-prompt.md +91 -0
- package/ai-config/hooks/learning-log.md +72 -0
- package/ai-config/hooks/model-router.md +86 -0
- package/ai-config/hooks/secret-scanner.md +64 -0
- package/ai-config/hooks/skill-validator.md +102 -0
- package/ai-config/hooks/task-artifact.md +114 -0
- package/ai-config/hooks/validate-workflow.md +100 -0
- package/ai-config/prompts/base.md +71 -0
- package/ai-config/prompts/modes/debug.md +34 -0
- package/ai-config/prompts/modes/deploy.md +40 -0
- package/ai-config/prompts/modes/research.md +32 -0
- package/ai-config/prompts/modes/review.md +33 -0
- package/ai-config/prompts/review-policy.md +79 -0
- package/ai-config/skills/_TEMPLATE.md +157 -0
- package/ai-config/skills/backend/api-gateway/SKILL.md +254 -0
- package/ai-config/skills/backend/bff-concepts/SKILL.md +239 -0
- package/ai-config/skills/backend/bff-spring/SKILL.md +364 -0
- package/ai-config/skills/backend/chi-router/SKILL.md +396 -0
- package/ai-config/skills/backend/error-handling/SKILL.md +255 -0
- package/ai-config/skills/backend/exceptions-spring/SKILL.md +323 -0
- package/ai-config/skills/backend/fastapi/SKILL.md +302 -0
- package/ai-config/skills/backend/gateway-spring/SKILL.md +390 -0
- package/ai-config/skills/backend/go-backend/SKILL.md +457 -0
- package/ai-config/skills/backend/gradle-multimodule/SKILL.md +274 -0
- package/ai-config/skills/backend/graphql-concepts/SKILL.md +352 -0
- package/ai-config/skills/backend/graphql-spring/SKILL.md +398 -0
- package/ai-config/skills/backend/grpc-concepts/SKILL.md +283 -0
- package/ai-config/skills/backend/grpc-spring/SKILL.md +445 -0
- package/ai-config/skills/backend/jwt-auth/SKILL.md +412 -0
- package/ai-config/skills/backend/notifications-concepts/SKILL.md +259 -0
- package/ai-config/skills/backend/recommendations-concepts/SKILL.md +261 -0
- package/ai-config/skills/backend/search-concepts/SKILL.md +263 -0
- package/ai-config/skills/backend/search-spring/SKILL.md +375 -0
- package/ai-config/skills/backend/spring-boot-4/SKILL.md +172 -0
- package/ai-config/skills/backend/websockets/SKILL.md +532 -0
- package/ai-config/skills/data-ai/ai-ml/SKILL.md +423 -0
- package/ai-config/skills/data-ai/analytics-concepts/SKILL.md +195 -0
- package/ai-config/skills/data-ai/analytics-spring/SKILL.md +340 -0
- package/ai-config/skills/data-ai/duckdb-analytics/SKILL.md +440 -0
- package/ai-config/skills/data-ai/langchain/SKILL.md +238 -0
- package/ai-config/skills/data-ai/mlflow/SKILL.md +302 -0
- package/ai-config/skills/data-ai/onnx-inference/SKILL.md +290 -0
- package/ai-config/skills/data-ai/powerbi/SKILL.md +352 -0
- package/ai-config/skills/data-ai/pytorch/SKILL.md +274 -0
- package/ai-config/skills/data-ai/scikit-learn/SKILL.md +321 -0
- package/ai-config/skills/data-ai/vector-db/SKILL.md +301 -0
- package/ai-config/skills/database/graph-databases/SKILL.md +218 -0
- package/ai-config/skills/database/graph-spring/SKILL.md +361 -0
- package/ai-config/skills/database/pgx-postgres/SKILL.md +512 -0
- package/ai-config/skills/database/redis-cache/SKILL.md +343 -0
- package/ai-config/skills/database/sqlite-embedded/SKILL.md +388 -0
- package/ai-config/skills/database/timescaledb/SKILL.md +320 -0
- package/ai-config/skills/docs/api-documentation/SKILL.md +293 -0
- package/ai-config/skills/docs/docs-spring/SKILL.md +377 -0
- package/ai-config/skills/docs/mustache-templates/SKILL.md +190 -0
- package/ai-config/skills/docs/technical-docs/SKILL.md +447 -0
- package/ai-config/skills/frontend/astro-ssr/SKILL.md +441 -0
- package/ai-config/skills/frontend/frontend-design/SKILL.md +54 -0
- package/ai-config/skills/frontend/frontend-web/SKILL.md +368 -0
- package/ai-config/skills/frontend/mantine-ui/SKILL.md +396 -0
- package/ai-config/skills/frontend/tanstack-query/SKILL.md +439 -0
- package/ai-config/skills/frontend/zod-validation/SKILL.md +417 -0
- package/ai-config/skills/frontend/zustand-state/SKILL.md +350 -0
- package/ai-config/skills/infrastructure/chaos-engineering/SKILL.md +244 -0
- package/ai-config/skills/infrastructure/chaos-spring/SKILL.md +378 -0
- package/ai-config/skills/infrastructure/devops-infra/SKILL.md +435 -0
- package/ai-config/skills/infrastructure/docker-containers/SKILL.md +420 -0
- package/ai-config/skills/infrastructure/kubernetes/SKILL.md +456 -0
- package/ai-config/skills/infrastructure/opentelemetry/SKILL.md +546 -0
- package/ai-config/skills/infrastructure/traefik-proxy/SKILL.md +474 -0
- package/ai-config/skills/infrastructure/woodpecker-ci/SKILL.md +315 -0
- package/ai-config/skills/mobile/ionic-capacitor/SKILL.md +504 -0
- package/ai-config/skills/mobile/mobile-ionic/SKILL.md +448 -0
- package/ai-config/skills/prompt-improver/SKILL.md +125 -0
- package/ai-config/skills/quality/ghagga-review/SKILL.md +216 -0
- package/ai-config/skills/references/hooks-patterns/SKILL.md +238 -0
- package/ai-config/skills/references/mcp-servers/SKILL.md +275 -0
- package/ai-config/skills/references/plugins-reference/SKILL.md +110 -0
- package/ai-config/skills/references/skills-reference/SKILL.md +420 -0
- package/ai-config/skills/references/subagent-templates/SKILL.md +193 -0
- package/ai-config/skills/systems-iot/modbus-protocol/SKILL.md +410 -0
- package/ai-config/skills/systems-iot/mqtt-rumqttc/SKILL.md +408 -0
- package/ai-config/skills/systems-iot/rust-systems/SKILL.md +386 -0
- package/ai-config/skills/systems-iot/tokio-async/SKILL.md +324 -0
- package/ai-config/skills/testing/playwright-e2e/SKILL.md +289 -0
- package/ai-config/skills/testing/testcontainers/SKILL.md +299 -0
- package/ai-config/skills/testing/vitest-testing/SKILL.md +381 -0
- package/ai-config/skills/workflow/ci-local-guide/SKILL.md +118 -0
- package/ai-config/skills/workflow/claude-automation-recommender/SKILL.md +299 -0
- package/ai-config/skills/workflow/claude-md-improver/SKILL.md +158 -0
- package/ai-config/skills/workflow/finishing-a-development-branch/SKILL.md +117 -0
- package/ai-config/skills/workflow/git-github/SKILL.md +334 -0
- package/ai-config/skills/workflow/git-github/references/examples.md +160 -0
- package/ai-config/skills/workflow/git-workflow/SKILL.md +214 -0
- package/ai-config/skills/workflow/ide-plugins/SKILL.md +277 -0
- package/ai-config/skills/workflow/ide-plugins-intellij/SKILL.md +401 -0
- package/ai-config/skills/workflow/obsidian-brain-workflow/SKILL.md +199 -0
- package/ai-config/skills/workflow/using-git-worktrees/SKILL.md +100 -0
- package/ai-config/skills/workflow/verification-before-completion/SKILL.md +73 -0
- package/ai-config/skills/workflow/wave-workflow/SKILL.md +178 -0
- package/ci-local/README.md +170 -0
- package/ci-local/ci-local.sh +297 -0
- package/ci-local/hooks/commit-msg +74 -0
- package/ci-local/hooks/pre-commit +162 -0
- package/ci-local/hooks/pre-push +41 -0
- package/ci-local/install.sh +49 -0
- package/ci-local/semgrep.yml +214 -0
- package/dist/commands/analyze.d.ts +9 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +55 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/analyze.test.d.ts +2 -0
- package/dist/commands/analyze.test.d.ts.map +1 -0
- package/dist/commands/analyze.test.js +145 -0
- package/dist/commands/analyze.test.js.map +1 -0
- package/dist/commands/doctor.d.ts +7 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +158 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/doctor.test.d.ts +2 -0
- package/dist/commands/doctor.test.d.ts.map +1 -0
- package/dist/commands/doctor.test.js +200 -0
- package/dist/commands/doctor.test.js.map +1 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +283 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/init.test.d.ts +2 -0
- package/dist/commands/init.test.d.ts.map +1 -0
- package/dist/commands/init.test.js +271 -0
- package/dist/commands/init.test.js.map +1 -0
- package/dist/commands/sync.d.ts +8 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +201 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/constants.d.ts +21 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +57 -0
- package/dist/constants.js.map +1 -0
- package/dist/e2e/aggressive.e2e.test.d.ts +2 -0
- package/dist/e2e/aggressive.e2e.test.d.ts.map +1 -0
- package/dist/e2e/aggressive.e2e.test.js +350 -0
- package/dist/e2e/aggressive.e2e.test.js.map +1 -0
- package/dist/e2e/commands.e2e.test.d.ts +2 -0
- package/dist/e2e/commands.e2e.test.d.ts.map +1 -0
- package/dist/e2e/commands.e2e.test.js +213 -0
- package/dist/e2e/commands.e2e.test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +82 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/common.d.ts +17 -0
- package/dist/lib/common.d.ts.map +1 -0
- package/dist/lib/common.js +111 -0
- package/dist/lib/common.js.map +1 -0
- package/dist/lib/common.test.d.ts +2 -0
- package/dist/lib/common.test.d.ts.map +1 -0
- package/dist/lib/common.test.js +316 -0
- package/dist/lib/common.test.js.map +1 -0
- package/dist/lib/frontmatter.d.ts +18 -0
- package/dist/lib/frontmatter.d.ts.map +1 -0
- package/dist/lib/frontmatter.js +61 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/frontmatter.test.d.ts +2 -0
- package/dist/lib/frontmatter.test.d.ts.map +1 -0
- package/dist/lib/frontmatter.test.js +257 -0
- package/dist/lib/frontmatter.test.js.map +1 -0
- package/dist/lib/template.d.ts +24 -0
- package/dist/lib/template.d.ts.map +1 -0
- package/dist/lib/template.js +78 -0
- package/dist/lib/template.js.map +1 -0
- package/dist/lib/template.test.d.ts +2 -0
- package/dist/lib/template.test.d.ts.map +1 -0
- package/dist/lib/template.test.js +201 -0
- package/dist/lib/template.test.js.map +1 -0
- package/dist/types/index.d.ts +48 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/ui/AnalyzeUI.d.ts +7 -0
- package/dist/ui/AnalyzeUI.d.ts.map +1 -0
- package/dist/ui/AnalyzeUI.js +100 -0
- package/dist/ui/AnalyzeUI.js.map +1 -0
- package/dist/ui/App.d.ts +13 -0
- package/dist/ui/App.d.ts.map +1 -0
- package/dist/ui/App.js +100 -0
- package/dist/ui/App.js.map +1 -0
- package/dist/ui/CIContext.d.ts +9 -0
- package/dist/ui/CIContext.d.ts.map +1 -0
- package/dist/ui/CIContext.js +9 -0
- package/dist/ui/CIContext.js.map +1 -0
- package/dist/ui/CISelector.d.ts +8 -0
- package/dist/ui/CISelector.d.ts.map +1 -0
- package/dist/ui/CISelector.js +45 -0
- package/dist/ui/CISelector.js.map +1 -0
- package/dist/ui/Doctor.d.ts +3 -0
- package/dist/ui/Doctor.d.ts.map +1 -0
- package/dist/ui/Doctor.js +89 -0
- package/dist/ui/Doctor.js.map +1 -0
- package/dist/ui/Header.d.ts +8 -0
- package/dist/ui/Header.d.ts.map +1 -0
- package/dist/ui/Header.js +30 -0
- package/dist/ui/Header.js.map +1 -0
- package/dist/ui/MemorySelector.d.ts +8 -0
- package/dist/ui/MemorySelector.d.ts.map +1 -0
- package/dist/ui/MemorySelector.js +46 -0
- package/dist/ui/MemorySelector.js.map +1 -0
- package/dist/ui/NameInput.d.ts +8 -0
- package/dist/ui/NameInput.d.ts.map +1 -0
- package/dist/ui/NameInput.js +69 -0
- package/dist/ui/NameInput.js.map +1 -0
- package/dist/ui/OptionSelector.d.ts +12 -0
- package/dist/ui/OptionSelector.d.ts.map +1 -0
- package/dist/ui/OptionSelector.js +69 -0
- package/dist/ui/OptionSelector.js.map +1 -0
- package/dist/ui/Progress.d.ts +11 -0
- package/dist/ui/Progress.d.ts.map +1 -0
- package/dist/ui/Progress.js +58 -0
- package/dist/ui/Progress.js.map +1 -0
- package/dist/ui/StackSelector.d.ts +9 -0
- package/dist/ui/StackSelector.d.ts.map +1 -0
- package/dist/ui/StackSelector.js +65 -0
- package/dist/ui/StackSelector.js.map +1 -0
- package/dist/ui/Summary.d.ts +12 -0
- package/dist/ui/Summary.d.ts.map +1 -0
- package/dist/ui/Summary.js +114 -0
- package/dist/ui/Summary.js.map +1 -0
- package/dist/ui/SyncUI.d.ts +10 -0
- package/dist/ui/SyncUI.d.ts.map +1 -0
- package/dist/ui/SyncUI.js +64 -0
- package/dist/ui/SyncUI.js.map +1 -0
- package/dist/ui/Welcome.d.ts +7 -0
- package/dist/ui/Welcome.d.ts.map +1 -0
- package/dist/ui/Welcome.js +45 -0
- package/dist/ui/Welcome.js.map +1 -0
- package/dist/ui/theme.d.ts +10 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js +9 -0
- package/dist/ui/theme.js.map +1 -0
- package/modules/engram/.gitignore-snippet.txt +6 -0
- package/modules/engram/.mcp-config-snippet.json +11 -0
- package/modules/engram/README.md +146 -0
- package/modules/engram/install-engram.sh +216 -0
- package/modules/ghagga/.env.example +43 -0
- package/modules/ghagga/README.md +153 -0
- package/modules/ghagga/docker-compose.yml +80 -0
- package/modules/ghagga/setup-ghagga.sh +139 -0
- package/modules/memory-simple/.project/NOTES.md +22 -0
- package/modules/memory-simple/README.md +23 -0
- package/modules/obsidian-brain/.obsidian/app.json +23 -0
- package/modules/obsidian-brain/.obsidian/appearance.json +5 -0
- package/modules/obsidian-brain/.obsidian/bookmarks.json +34 -0
- package/modules/obsidian-brain/.obsidian/community-plugins.json +1 -0
- package/modules/obsidian-brain/.obsidian/core-plugins-migration.json +21 -0
- package/modules/obsidian-brain/.obsidian/core-plugins.json +18 -0
- package/modules/obsidian-brain/.obsidian/daily-notes.json +5 -0
- package/modules/obsidian-brain/.obsidian/graph.json +37 -0
- package/modules/obsidian-brain/.obsidian/hotkeys.json +14 -0
- package/modules/obsidian-brain/.obsidian/plugins/dataview/data.json +25 -0
- package/modules/obsidian-brain/.obsidian/plugins/obsidian-kanban/data.json +29 -0
- package/modules/obsidian-brain/.obsidian/plugins/templater-obsidian/data.json +18 -0
- package/modules/obsidian-brain/.obsidian/snippets/project-memory.css +71 -0
- package/modules/obsidian-brain/.obsidian-gitignore-snippet.txt +8 -0
- package/modules/obsidian-brain/.project/Attachments/.gitkeep +0 -0
- package/modules/obsidian-brain/.project/Memory/BLOCKERS.md +78 -0
- package/modules/obsidian-brain/.project/Memory/CONTEXT.md +102 -0
- package/modules/obsidian-brain/.project/Memory/DASHBOARD.md +73 -0
- package/modules/obsidian-brain/.project/Memory/DECISIONS.md +87 -0
- package/modules/obsidian-brain/.project/Memory/KANBAN.md +15 -0
- package/modules/obsidian-brain/.project/Memory/README.md +61 -0
- package/modules/obsidian-brain/.project/Memory/WAVES.md +78 -0
- package/modules/obsidian-brain/.project/Sessions/TEMPLATE.md +99 -0
- package/modules/obsidian-brain/.project/Templates/ADR.md +33 -0
- package/modules/obsidian-brain/.project/Templates/Blocker.md +21 -0
- package/modules/obsidian-brain/.project/Templates/Session.md +88 -0
- package/modules/obsidian-brain/README.md +268 -0
- package/modules/obsidian-brain/new-wave.sh +182 -0
- package/package.json +51 -0
- package/schemas/agent.schema.json +34 -0
- package/schemas/ai-config.schema.json +28 -0
- package/schemas/skill.schema.json +44 -0
- package/src/commands/analyze.test.ts +145 -0
- package/src/commands/analyze.ts +69 -0
- package/src/commands/doctor.test.ts +208 -0
- package/src/commands/doctor.ts +163 -0
- package/src/commands/init.test.ts +298 -0
- package/src/commands/init.ts +285 -0
- package/src/constants.ts +69 -0
- package/src/e2e/aggressive.e2e.test.ts +557 -0
- package/src/e2e/commands.e2e.test.ts +298 -0
- package/src/index.tsx +106 -0
- package/src/lib/common.test.ts +318 -0
- package/src/lib/common.ts +127 -0
- package/src/lib/frontmatter.test.ts +291 -0
- package/src/lib/frontmatter.ts +77 -0
- package/src/lib/template.test.ts +226 -0
- package/src/lib/template.ts +99 -0
- package/src/types/index.ts +53 -0
- package/src/ui/AnalyzeUI.tsx +133 -0
- package/src/ui/App.tsx +175 -0
- package/src/ui/CIContext.tsx +25 -0
- package/src/ui/CISelector.tsx +72 -0
- package/src/ui/Doctor.tsx +122 -0
- package/src/ui/Header.tsx +48 -0
- package/src/ui/MemorySelector.tsx +73 -0
- package/src/ui/NameInput.tsx +82 -0
- package/src/ui/OptionSelector.tsx +100 -0
- package/src/ui/Progress.tsx +88 -0
- package/src/ui/StackSelector.tsx +101 -0
- package/src/ui/Summary.tsx +134 -0
- package/src/ui/Welcome.tsx +54 -0
- package/src/ui/theme.ts +10 -0
- package/stryker.config.json +19 -0
- package/tasks/_TEMPLATE/files-edited.md +3 -0
- package/tasks/_TEMPLATE/plan.md +3 -0
- package/tasks/_TEMPLATE/research.md +3 -0
- package/tasks/_TEMPLATE/verification.md +5 -0
- package/templates/common/dependabot/cargo.yml +11 -0
- package/templates/common/dependabot/github-actions.yml +16 -0
- package/templates/common/dependabot/gomod.yml +15 -0
- package/templates/common/dependabot/gradle.yml +15 -0
- package/templates/common/dependabot/header.yml +3 -0
- package/templates/common/dependabot/maven.yml +15 -0
- package/templates/common/dependabot/npm.yml +20 -0
- package/templates/common/dependabot/pip.yml +11 -0
- package/templates/dependabot.yml +162 -0
- package/templates/github/ci-go.yml +41 -0
- package/templates/github/ci-java.yml +45 -0
- package/templates/github/ci-monorepo.yml +150 -0
- package/templates/github/ci-node.yml +42 -0
- package/templates/github/ci-python.yml +42 -0
- package/templates/github/ci-rust.yml +42 -0
- package/templates/github/dependabot-automerge.yml +40 -0
- package/templates/gitlab/gitlab-ci-go.yml +88 -0
- package/templates/gitlab/gitlab-ci-java.yml +79 -0
- package/templates/gitlab/gitlab-ci-monorepo.yml +126 -0
- package/templates/gitlab/gitlab-ci-node.yml +63 -0
- package/templates/gitlab/gitlab-ci-python.yml +147 -0
- package/templates/gitlab/gitlab-ci-rust.yml +67 -0
- package/templates/global/claude-settings.json +98 -0
- package/templates/global/codex-config.toml +8 -0
- package/templates/global/copilot-instructions/base-rules.instructions.md +13 -0
- package/templates/global/copilot-instructions/sdd-orchestrator.instructions.md +37 -0
- package/templates/global/gemini-commands/cleanup.toml +20 -0
- package/templates/global/gemini-commands/commit.toml +15 -0
- package/templates/global/gemini-commands/dead-code.toml +22 -0
- package/templates/global/gemini-commands/plan.toml +30 -0
- package/templates/global/gemini-commands/review.toml +17 -0
- package/templates/global/gemini-commands/sdd-apply.toml +22 -0
- package/templates/global/gemini-commands/sdd-ff.toml +14 -0
- package/templates/global/gemini-commands/sdd-new.toml +21 -0
- package/templates/global/gemini-commands/sdd-verify.toml +21 -0
- package/templates/global/gemini-commands/tdd.toml +26 -0
- package/templates/global/gemini-settings.json +8 -0
- package/templates/global/opencode-config.json +44 -0
- package/templates/global/sdd-instructions.md +47 -0
- package/templates/global/sdd-orchestrator-claude.md +46 -0
- package/templates/global/sdd-orchestrator-copilot.md +34 -0
- package/templates/renovate.json +69 -0
- package/templates/woodpecker/monorepo/backend.yml +34 -0
- package/templates/woodpecker/monorepo/frontend.yml +34 -0
- package/templates/woodpecker/monorepo/summary.yml +25 -0
- package/templates/woodpecker/woodpecker-go.yml +51 -0
- package/templates/woodpecker/woodpecker-java.yml +67 -0
- package/templates/woodpecker/woodpecker-node.yml +47 -0
- package/templates/woodpecker/woodpecker-python.yml +108 -0
- package/templates/woodpecker/woodpecker-rust.yml +57 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +16 -0
- package/workflows/reusable-build-go.yml +111 -0
- package/workflows/reusable-build-java.yml +120 -0
- package/workflows/reusable-build-node.yml +145 -0
- package/workflows/reusable-build-python.yml +159 -0
- package/workflows/reusable-build-rust.yml +135 -0
- package/workflows/reusable-docker.yml +120 -0
- package/workflows/reusable-ghagga-review.yml +165 -0
- package/workflows/reusable-release.yml +91 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: redis-cache
|
|
3
|
+
description: >
|
|
4
|
+
Redis caching, sessions, pub/sub, rate limiting, and distributed locks.
|
|
5
|
+
Trigger: redis, cache, session, pub/sub, rate limiting, distributed lock
|
|
6
|
+
|
|
7
|
+
tools:
|
|
8
|
+
- Read
|
|
9
|
+
- Write
|
|
10
|
+
- Bash
|
|
11
|
+
- Grep
|
|
12
|
+
|
|
13
|
+
metadata:
|
|
14
|
+
author: plataforma-industrial
|
|
15
|
+
version: "2.0"
|
|
16
|
+
tags: [cache, redis, real-time, session]
|
|
17
|
+
updated: "2026-02"
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# Redis Cache & Real-time
|
|
21
|
+
|
|
22
|
+
> Caching, sessions, pub/sub, rate limiting, and distributed locks with Redis.
|
|
23
|
+
|
|
24
|
+
## Stack
|
|
25
|
+
|
|
26
|
+
```yaml
|
|
27
|
+
Redis: 7.2+
|
|
28
|
+
go-redis/redis: v9
|
|
29
|
+
ioredis (Node): 5.3+
|
|
30
|
+
redis-py: 5.0+
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## When to Use
|
|
34
|
+
|
|
35
|
+
- Caching frequently accessed data with TTL
|
|
36
|
+
- Session management with expiration
|
|
37
|
+
- Real-time pub/sub between services
|
|
38
|
+
- Rate limiting API endpoints
|
|
39
|
+
- Distributed locks for coordination
|
|
40
|
+
|
|
41
|
+
## Docker Setup
|
|
42
|
+
|
|
43
|
+
```yaml
|
|
44
|
+
# docker-compose.yml
|
|
45
|
+
services:
|
|
46
|
+
redis:
|
|
47
|
+
image: redis:7-alpine
|
|
48
|
+
ports:
|
|
49
|
+
- "6379:6379"
|
|
50
|
+
volumes:
|
|
51
|
+
- redis_data:/data
|
|
52
|
+
- ./config/redis.conf:/usr/local/etc/redis/redis.conf
|
|
53
|
+
command: redis-server /usr/local/etc/redis/redis.conf
|
|
54
|
+
healthcheck:
|
|
55
|
+
test: ["CMD", "redis-cli", "ping"]
|
|
56
|
+
interval: 5s
|
|
57
|
+
timeout: 3s
|
|
58
|
+
retries: 5
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Redis Config
|
|
62
|
+
|
|
63
|
+
```conf
|
|
64
|
+
# redis.conf
|
|
65
|
+
maxmemory 256mb
|
|
66
|
+
maxmemory-policy allkeys-lru
|
|
67
|
+
save 900 1
|
|
68
|
+
save 300 10
|
|
69
|
+
save 60 10000
|
|
70
|
+
appendonly yes
|
|
71
|
+
appendfsync everysec
|
|
72
|
+
# requirepass your-strong-password # Production
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Go Connection
|
|
76
|
+
|
|
77
|
+
```go
|
|
78
|
+
package cache
|
|
79
|
+
|
|
80
|
+
import (
|
|
81
|
+
"context"
|
|
82
|
+
"time"
|
|
83
|
+
"github.com/redis/go-redis/v9"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
type RedisClient struct {
|
|
87
|
+
client *redis.Client
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
func NewRedisClient(addr, password string, db int) (*RedisClient, error) {
|
|
91
|
+
client := redis.NewClient(&redis.Options{
|
|
92
|
+
Addr: addr,
|
|
93
|
+
Password: password,
|
|
94
|
+
DB: db,
|
|
95
|
+
PoolSize: 100,
|
|
96
|
+
MinIdleConns: 10,
|
|
97
|
+
MaxRetries: 3,
|
|
98
|
+
DialTimeout: 5 * time.Second,
|
|
99
|
+
ReadTimeout: 3 * time.Second,
|
|
100
|
+
WriteTimeout: 3 * time.Second,
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
104
|
+
defer cancel()
|
|
105
|
+
|
|
106
|
+
if err := client.Ping(ctx).Err(); err != nil {
|
|
107
|
+
return nil, err
|
|
108
|
+
}
|
|
109
|
+
return &RedisClient{client: client}, nil
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Cache-Aside Pattern
|
|
114
|
+
|
|
115
|
+
```go
|
|
116
|
+
func (c *Cache) GetOrLoad(ctx context.Context, key string, loader func() (any, error)) (any, error) {
|
|
117
|
+
// Try cache first
|
|
118
|
+
data, err := c.redis.Get(ctx, key).Bytes()
|
|
119
|
+
if err == nil {
|
|
120
|
+
var result any
|
|
121
|
+
json.Unmarshal(data, &result)
|
|
122
|
+
return result, nil
|
|
123
|
+
}
|
|
124
|
+
if err != redis.Nil {
|
|
125
|
+
return nil, err
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Cache miss - load from source
|
|
129
|
+
result, err := loader()
|
|
130
|
+
if err != nil {
|
|
131
|
+
return nil, err
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Store in cache (async)
|
|
135
|
+
go func() {
|
|
136
|
+
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
137
|
+
defer cancel()
|
|
138
|
+
data, _ := json.Marshal(result)
|
|
139
|
+
c.redis.Set(ctx, key, data, 5*time.Minute)
|
|
140
|
+
}()
|
|
141
|
+
|
|
142
|
+
return result, nil
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Session Management
|
|
147
|
+
|
|
148
|
+
```go
|
|
149
|
+
const sessionTTL = 24 * time.Hour
|
|
150
|
+
|
|
151
|
+
type Session struct {
|
|
152
|
+
ID string `json:"id"`
|
|
153
|
+
UserID string `json:"user_id"`
|
|
154
|
+
TenantID string `json:"tenant_id"`
|
|
155
|
+
Role string `json:"role"`
|
|
156
|
+
ExpiresAt time.Time `json:"expires_at"`
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
func (s *SessionStore) Create(ctx context.Context, userID, tenantID, role string) (*Session, error) {
|
|
160
|
+
session := &Session{
|
|
161
|
+
ID: uuid.New().String(),
|
|
162
|
+
UserID: userID,
|
|
163
|
+
TenantID: tenantID,
|
|
164
|
+
Role: role,
|
|
165
|
+
ExpiresAt: time.Now().Add(sessionTTL),
|
|
166
|
+
}
|
|
167
|
+
data, _ := json.Marshal(session)
|
|
168
|
+
|
|
169
|
+
pipe := s.redis.Pipeline()
|
|
170
|
+
pipe.Set(ctx, "session:"+session.ID, data, sessionTTL)
|
|
171
|
+
pipe.SAdd(ctx, "user:"+userID+":sessions", session.ID)
|
|
172
|
+
pipe.Expire(ctx, "user:"+userID+":sessions", sessionTTL)
|
|
173
|
+
_, err := pipe.Exec(ctx)
|
|
174
|
+
return session, err
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
func (s *SessionStore) DeleteAllForUser(ctx context.Context, userID string) error {
|
|
178
|
+
sessionIDs, _ := s.redis.SMembers(ctx, "user:"+userID+":sessions").Result()
|
|
179
|
+
keys := make([]string, len(sessionIDs)+1)
|
|
180
|
+
for i, id := range sessionIDs {
|
|
181
|
+
keys[i] = "session:" + id
|
|
182
|
+
}
|
|
183
|
+
keys[len(sessionIDs)] = "user:" + userID + ":sessions"
|
|
184
|
+
return s.redis.Del(ctx, keys...).Err()
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Pub/Sub
|
|
189
|
+
|
|
190
|
+
```go
|
|
191
|
+
// Publisher
|
|
192
|
+
func (p *Publisher) Publish(ctx context.Context, channel string, event Event) error {
|
|
193
|
+
data, _ := json.Marshal(event)
|
|
194
|
+
return p.redis.Publish(ctx, channel, data).Err()
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Subscriber
|
|
198
|
+
func (s *Subscriber) Subscribe(ctx context.Context, channels ...string) error {
|
|
199
|
+
pubsub := s.redis.Subscribe(ctx, channels...)
|
|
200
|
+
defer pubsub.Close()
|
|
201
|
+
|
|
202
|
+
for msg := range pubsub.Channel() {
|
|
203
|
+
var event Event
|
|
204
|
+
json.Unmarshal([]byte(msg.Payload), &event)
|
|
205
|
+
for _, handler := range s.handlers[event.Type] {
|
|
206
|
+
handler(ctx, event)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return nil
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Rate Limiting (Sliding Window)
|
|
214
|
+
|
|
215
|
+
```go
|
|
216
|
+
func (rl *RateLimiter) Allow(ctx context.Context, key string) (bool, int, error) {
|
|
217
|
+
now := time.Now().UnixMilli()
|
|
218
|
+
windowStart := now - rl.window.Milliseconds()
|
|
219
|
+
redisKey := "ratelimit:" + key
|
|
220
|
+
|
|
221
|
+
pipe := rl.redis.Pipeline()
|
|
222
|
+
pipe.ZRemRangeByScore(ctx, redisKey, "0", fmt.Sprintf("%d", windowStart))
|
|
223
|
+
countCmd := pipe.ZCard(ctx, redisKey)
|
|
224
|
+
pipe.ZAdd(ctx, redisKey, redis.Z{Score: float64(now), Member: now})
|
|
225
|
+
pipe.Expire(ctx, redisKey, rl.window)
|
|
226
|
+
pipe.Exec(ctx)
|
|
227
|
+
|
|
228
|
+
count := int(countCmd.Val())
|
|
229
|
+
remaining := rl.limit - count - 1
|
|
230
|
+
if remaining < 0 {
|
|
231
|
+
remaining = 0
|
|
232
|
+
}
|
|
233
|
+
return count < rl.limit, remaining, nil
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Distributed Lock
|
|
238
|
+
|
|
239
|
+
```go
|
|
240
|
+
func NewLock(redis *redis.Client, key string, ttl time.Duration) *DistributedLock {
|
|
241
|
+
return &DistributedLock{
|
|
242
|
+
redis: redis,
|
|
243
|
+
key: "lock:" + key,
|
|
244
|
+
value: uuid.New().String(),
|
|
245
|
+
ttl: ttl,
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
func (l *DistributedLock) Acquire(ctx context.Context) error {
|
|
250
|
+
success, _ := l.redis.SetNX(ctx, l.key, l.value, l.ttl).Result()
|
|
251
|
+
if !success {
|
|
252
|
+
return ErrLockNotAcquired
|
|
253
|
+
}
|
|
254
|
+
return nil
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
func (l *DistributedLock) Release(ctx context.Context) error {
|
|
258
|
+
script := redis.NewScript(`
|
|
259
|
+
if redis.call("GET", KEYS[1]) == ARGV[1] then
|
|
260
|
+
return redis.call("DEL", KEYS[1])
|
|
261
|
+
end
|
|
262
|
+
return 0
|
|
263
|
+
`)
|
|
264
|
+
_, err := script.Run(ctx, l.redis, []string{l.key}, l.value).Result()
|
|
265
|
+
return err
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
func WithLock(ctx context.Context, redis *redis.Client, key string, ttl time.Duration, fn func() error) error {
|
|
269
|
+
lock := NewLock(redis, key, ttl)
|
|
270
|
+
if err := lock.Acquire(ctx); err != nil {
|
|
271
|
+
return err
|
|
272
|
+
}
|
|
273
|
+
defer lock.Release(ctx)
|
|
274
|
+
return fn()
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## TypeScript/Node.js
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
import Redis from 'ioredis';
|
|
282
|
+
|
|
283
|
+
export const redis = new Redis({
|
|
284
|
+
host: process.env.REDIS_HOST || 'localhost',
|
|
285
|
+
port: parseInt(process.env.REDIS_PORT || '6379'),
|
|
286
|
+
password: process.env.REDIS_PASSWORD,
|
|
287
|
+
maxRetriesPerRequest: 3,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Pub/Sub requires separate connections
|
|
291
|
+
export const publisher = new Redis(/* config */);
|
|
292
|
+
export const subscriber = new Redis(/* config */);
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Best Practices
|
|
296
|
+
|
|
297
|
+
| Practice | Description |
|
|
298
|
+
|----------|-------------|
|
|
299
|
+
| Always set TTL | Avoid memory issues with `Set(key, val, TTL)` |
|
|
300
|
+
| Use pipelines | Single round-trip for batch ops |
|
|
301
|
+
| Namespace keys | `tenant:{id}:sensor:{id}` format |
|
|
302
|
+
| Handle redis.Nil | Not an error, just cache miss |
|
|
303
|
+
| Separate pub/sub conn | Pub/sub blocks, use dedicated client |
|
|
304
|
+
|
|
305
|
+
### Data Structure Selection
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
String - Simple cache, counters
|
|
309
|
+
Hash - Objects with multiple fields
|
|
310
|
+
List - Queues, recent items
|
|
311
|
+
Set - Tags, unique items
|
|
312
|
+
Sorted Set - Leaderboards, time-series
|
|
313
|
+
Stream - Event logs, message queues
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## Quick Reference
|
|
317
|
+
|
|
318
|
+
| Task | Command |
|
|
319
|
+
|------|---------|
|
|
320
|
+
| Set with TTL | `SET key value EX 300` |
|
|
321
|
+
| Get | `GET key` |
|
|
322
|
+
| Delete | `DEL key1 key2` |
|
|
323
|
+
| Publish | `PUBLISH channel message` |
|
|
324
|
+
| Rate limit check | `ZRANGEBYSCORE + ZADD` |
|
|
325
|
+
|
|
326
|
+
## Resources
|
|
327
|
+
|
|
328
|
+
- [Redis Documentation](https://redis.io/docs/)
|
|
329
|
+
- [go-redis Guide](https://redis.uptrace.dev/)
|
|
330
|
+
- [ioredis Documentation](https://github.com/redis/ioredis)
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Changelog
|
|
335
|
+
|
|
336
|
+
- **2.0** - Condensed from Plataforma Industrial SKILL-REDIS
|
|
337
|
+
|
|
338
|
+
## Related Skills
|
|
339
|
+
|
|
340
|
+
- `timescaledb`: Database caching layer
|
|
341
|
+
- `pgx-postgres`: Query result caching
|
|
342
|
+
- `chi-router`: Go cache middleware
|
|
343
|
+
- `fastapi`: Python cache patterns
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sqlite-embedded
|
|
3
|
+
description: >
|
|
4
|
+
SQLite embedded database for edge/offline scenarios with sync queue patterns.
|
|
5
|
+
Trigger: sqlite, embedded database, offline, edge, sync queue, local storage
|
|
6
|
+
|
|
7
|
+
tools:
|
|
8
|
+
- Read
|
|
9
|
+
- Write
|
|
10
|
+
- Bash
|
|
11
|
+
- Grep
|
|
12
|
+
|
|
13
|
+
metadata:
|
|
14
|
+
author: plataforma-industrial
|
|
15
|
+
version: "2.0"
|
|
16
|
+
tags: [sqlite, edge, embedded, offline, database]
|
|
17
|
+
updated: "2026-02"
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# SQLite Embedded Database
|
|
21
|
+
|
|
22
|
+
> Edge/offline embedded database with zero configuration and sync queue patterns.
|
|
23
|
+
|
|
24
|
+
## Stack
|
|
25
|
+
|
|
26
|
+
```yaml
|
|
27
|
+
SQLite: 3.45+
|
|
28
|
+
Driver Go: modernc.org/sqlite # Pure Go, no CGO
|
|
29
|
+
Driver Rust: rusqlite 0.31+
|
|
30
|
+
Mobile: @capacitor-community/sqlite
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## When to Use
|
|
34
|
+
|
|
35
|
+
- Edge/offline-first applications
|
|
36
|
+
- Local configuration storage
|
|
37
|
+
- Sync queue for pending cloud operations
|
|
38
|
+
- Low-footprint deployments (<5MB RAM)
|
|
39
|
+
- Single-file database with easy backup
|
|
40
|
+
|
|
41
|
+
## Optimal PRAGMAs
|
|
42
|
+
|
|
43
|
+
```sql
|
|
44
|
+
PRAGMA journal_mode = WAL; -- Write-Ahead Logging (better concurrency)
|
|
45
|
+
PRAGMA synchronous = NORMAL; -- Balance durability/speed
|
|
46
|
+
PRAGMA foreign_keys = ON; -- Referential integrity
|
|
47
|
+
PRAGMA cache_size = -64000; -- 64MB cache
|
|
48
|
+
PRAGMA mmap_size = 268435456; -- 256MB memory-mapped I/O
|
|
49
|
+
PRAGMA busy_timeout = 5000; -- 5s retry if locked
|
|
50
|
+
PRAGMA temp_store = MEMORY; -- Temp tables in memory
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Go Connection (Pure Go, No CGO)
|
|
54
|
+
|
|
55
|
+
```go
|
|
56
|
+
package db
|
|
57
|
+
|
|
58
|
+
import (
|
|
59
|
+
"context"
|
|
60
|
+
"database/sql"
|
|
61
|
+
"fmt"
|
|
62
|
+
"time"
|
|
63
|
+
_ "modernc.org/sqlite"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
type SQLiteConfig struct {
|
|
67
|
+
Path string
|
|
68
|
+
MaxOpenConns int
|
|
69
|
+
MaxIdleConns int
|
|
70
|
+
ConnMaxLifetime time.Duration
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
func NewSQLiteDB(cfg SQLiteConfig) (*sql.DB, error) {
|
|
74
|
+
dsn := fmt.Sprintf(
|
|
75
|
+
"file:%s?_journal_mode=WAL&_synchronous=NORMAL&_foreign_keys=ON&_busy_timeout=5000&_cache_size=-64000",
|
|
76
|
+
cfg.Path,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
db, err := sql.Open("sqlite", dsn)
|
|
80
|
+
if err != nil {
|
|
81
|
+
return nil, fmt.Errorf("open sqlite: %w", err)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// SQLite: 1 writer, multiple readers
|
|
85
|
+
db.SetMaxOpenConns(cfg.MaxOpenConns) // Typically 1-4
|
|
86
|
+
db.SetMaxIdleConns(cfg.MaxIdleConns)
|
|
87
|
+
db.SetConnMaxLifetime(cfg.ConnMaxLifetime)
|
|
88
|
+
|
|
89
|
+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
90
|
+
defer cancel()
|
|
91
|
+
if err := db.PingContext(ctx); err != nil {
|
|
92
|
+
return nil, fmt.Errorf("ping sqlite: %w", err)
|
|
93
|
+
}
|
|
94
|
+
return db, nil
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
func DefaultConfig(path string) SQLiteConfig {
|
|
98
|
+
return SQLiteConfig{
|
|
99
|
+
Path: path,
|
|
100
|
+
MaxOpenConns: 4,
|
|
101
|
+
MaxIdleConns: 2,
|
|
102
|
+
ConnMaxLifetime: time.Hour,
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Rust Connection
|
|
108
|
+
|
|
109
|
+
```rust
|
|
110
|
+
use rusqlite::{Connection, OpenFlags, Result};
|
|
111
|
+
use std::path::Path;
|
|
112
|
+
|
|
113
|
+
pub struct SqliteDb {
|
|
114
|
+
conn: Connection,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
impl SqliteDb {
|
|
118
|
+
pub fn new(path: &Path) -> Result<Self> {
|
|
119
|
+
let conn = Connection::open_with_flags(
|
|
120
|
+
path,
|
|
121
|
+
OpenFlags::SQLITE_OPEN_READ_WRITE
|
|
122
|
+
| OpenFlags::SQLITE_OPEN_CREATE
|
|
123
|
+
| OpenFlags::SQLITE_OPEN_NO_MUTEX,
|
|
124
|
+
)?;
|
|
125
|
+
|
|
126
|
+
conn.execute_batch("
|
|
127
|
+
PRAGMA journal_mode = WAL;
|
|
128
|
+
PRAGMA synchronous = NORMAL;
|
|
129
|
+
PRAGMA foreign_keys = ON;
|
|
130
|
+
PRAGMA cache_size = -64000;
|
|
131
|
+
PRAGMA busy_timeout = 5000;
|
|
132
|
+
")?;
|
|
133
|
+
|
|
134
|
+
Ok(Self { conn })
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Schema Patterns
|
|
140
|
+
|
|
141
|
+
### Configuration Table
|
|
142
|
+
|
|
143
|
+
```sql
|
|
144
|
+
CREATE TABLE IF NOT EXISTS sensors (
|
|
145
|
+
id TEXT PRIMARY KEY,
|
|
146
|
+
plant_id TEXT NOT NULL REFERENCES plants(id) ON DELETE CASCADE,
|
|
147
|
+
name TEXT NOT NULL,
|
|
148
|
+
type TEXT NOT NULL,
|
|
149
|
+
unit TEXT NOT NULL,
|
|
150
|
+
modbus_address INTEGER,
|
|
151
|
+
modbus_register INTEGER,
|
|
152
|
+
scale_factor REAL DEFAULT 1.0,
|
|
153
|
+
warning_low REAL,
|
|
154
|
+
warning_high REAL,
|
|
155
|
+
critical_low REAL,
|
|
156
|
+
critical_high REAL,
|
|
157
|
+
read_interval_secs INTEGER DEFAULT 10,
|
|
158
|
+
is_active INTEGER NOT NULL DEFAULT 1,
|
|
159
|
+
metadata TEXT DEFAULT '{}',
|
|
160
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
161
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
CREATE INDEX idx_sensors_plant ON sensors(plant_id);
|
|
165
|
+
CREATE INDEX idx_sensors_active ON sensors(is_active);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Recent Readings Cache (with cleanup trigger)
|
|
169
|
+
|
|
170
|
+
```sql
|
|
171
|
+
CREATE TABLE IF NOT EXISTS sensor_readings_recent (
|
|
172
|
+
id TEXT PRIMARY KEY,
|
|
173
|
+
sensor_id TEXT NOT NULL REFERENCES sensors(id) ON DELETE CASCADE,
|
|
174
|
+
value REAL NOT NULL,
|
|
175
|
+
quality TEXT DEFAULT 'good' CHECK (quality IN ('good', 'uncertain', 'bad')),
|
|
176
|
+
timestamp TEXT NOT NULL,
|
|
177
|
+
synced INTEGER NOT NULL DEFAULT 0,
|
|
178
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
CREATE INDEX idx_readings_sensor ON sensor_readings_recent(sensor_id, timestamp DESC);
|
|
182
|
+
CREATE INDEX idx_readings_synced ON sensor_readings_recent(synced) WHERE synced = 0;
|
|
183
|
+
|
|
184
|
+
-- Auto-cleanup: keep only last 1000 per sensor
|
|
185
|
+
CREATE TRIGGER IF NOT EXISTS trg_readings_cleanup
|
|
186
|
+
AFTER INSERT ON sensor_readings_recent
|
|
187
|
+
BEGIN
|
|
188
|
+
DELETE FROM sensor_readings_recent
|
|
189
|
+
WHERE id IN (
|
|
190
|
+
SELECT id FROM sensor_readings_recent
|
|
191
|
+
WHERE sensor_id = NEW.sensor_id
|
|
192
|
+
ORDER BY timestamp DESC
|
|
193
|
+
LIMIT -1 OFFSET 1000
|
|
194
|
+
);
|
|
195
|
+
END;
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Sync Queue
|
|
199
|
+
|
|
200
|
+
```sql
|
|
201
|
+
CREATE TABLE IF NOT EXISTS sync_queue (
|
|
202
|
+
id TEXT PRIMARY KEY,
|
|
203
|
+
plant_id TEXT NOT NULL,
|
|
204
|
+
entity_type TEXT NOT NULL,
|
|
205
|
+
entity_id TEXT NOT NULL,
|
|
206
|
+
operation TEXT NOT NULL CHECK (operation IN ('create', 'update', 'delete')),
|
|
207
|
+
payload TEXT NOT NULL,
|
|
208
|
+
priority INTEGER NOT NULL DEFAULT 0,
|
|
209
|
+
retries INTEGER NOT NULL DEFAULT 0,
|
|
210
|
+
max_retries INTEGER NOT NULL DEFAULT 5,
|
|
211
|
+
last_error TEXT,
|
|
212
|
+
next_retry_at TEXT,
|
|
213
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
214
|
+
processed_at TEXT
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
CREATE INDEX idx_sync_queue_pending ON sync_queue(priority DESC, created_at)
|
|
218
|
+
WHERE processed_at IS NULL;
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Sync Queue Repository
|
|
222
|
+
|
|
223
|
+
```go
|
|
224
|
+
type SyncItem struct {
|
|
225
|
+
ID string `json:"id"`
|
|
226
|
+
PlantID string `json:"plant_id"`
|
|
227
|
+
EntityType string `json:"entity_type"`
|
|
228
|
+
EntityID string `json:"entity_id"`
|
|
229
|
+
Operation string `json:"operation"`
|
|
230
|
+
Payload json.RawMessage `json:"payload"`
|
|
231
|
+
Priority int `json:"priority"`
|
|
232
|
+
Retries int `json:"retries"`
|
|
233
|
+
MaxRetries int `json:"max_retries"`
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
func (r *SyncQueueRepository) GetPending(ctx context.Context, limit int) ([]SyncItem, error) {
|
|
237
|
+
query := `
|
|
238
|
+
SELECT id, plant_id, entity_type, entity_id, operation, payload,
|
|
239
|
+
priority, retries, max_retries, last_error, created_at
|
|
240
|
+
FROM sync_queue
|
|
241
|
+
WHERE processed_at IS NULL
|
|
242
|
+
AND (next_retry_at IS NULL OR next_retry_at <= datetime('now'))
|
|
243
|
+
ORDER BY priority DESC, created_at ASC
|
|
244
|
+
LIMIT ?
|
|
245
|
+
`
|
|
246
|
+
// ... scan rows
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
func (r *SyncQueueRepository) MarkFailed(ctx context.Context, id string, errMsg string) error {
|
|
250
|
+
query := `
|
|
251
|
+
UPDATE sync_queue
|
|
252
|
+
SET retries = retries + 1,
|
|
253
|
+
last_error = ?,
|
|
254
|
+
next_retry_at = datetime('now', '+' || (retries + 1) * 30 || ' seconds')
|
|
255
|
+
WHERE id = ?
|
|
256
|
+
`
|
|
257
|
+
_, err := r.db.ExecContext(ctx, query, errMsg, id)
|
|
258
|
+
return err
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Batch Insert Pattern
|
|
263
|
+
|
|
264
|
+
```go
|
|
265
|
+
func (r *ReadingRepository) InsertBatch(ctx context.Context, readings []Reading) error {
|
|
266
|
+
if len(readings) == 0 {
|
|
267
|
+
return nil
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
tx, err := r.db.BeginTx(ctx, nil)
|
|
271
|
+
if err != nil {
|
|
272
|
+
return err
|
|
273
|
+
}
|
|
274
|
+
defer tx.Rollback()
|
|
275
|
+
|
|
276
|
+
placeholders := make([]string, len(readings))
|
|
277
|
+
args := make([]any, 0, len(readings)*6)
|
|
278
|
+
|
|
279
|
+
for i, rd := range readings {
|
|
280
|
+
placeholders[i] = "(?, ?, ?, ?, ?, ?)"
|
|
281
|
+
args = append(args, rd.ID, rd.SensorID, rd.Value, rd.Quality, rd.RawValue, rd.Timestamp)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
query := `INSERT INTO sensor_readings_recent (id, sensor_id, value, quality, raw_value, timestamp)
|
|
285
|
+
VALUES ` + strings.Join(placeholders, ", ")
|
|
286
|
+
_, err = tx.ExecContext(ctx, query, args...)
|
|
287
|
+
if err != nil {
|
|
288
|
+
return err
|
|
289
|
+
}
|
|
290
|
+
return tx.Commit()
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Useful Views
|
|
295
|
+
|
|
296
|
+
```sql
|
|
297
|
+
-- Latest reading per sensor with status
|
|
298
|
+
CREATE VIEW IF NOT EXISTS v_sensor_latest_readings AS
|
|
299
|
+
SELECT
|
|
300
|
+
s.id AS sensor_id,
|
|
301
|
+
s.name AS sensor_name,
|
|
302
|
+
r.value,
|
|
303
|
+
r.timestamp,
|
|
304
|
+
CASE
|
|
305
|
+
WHEN r.value IS NULL THEN 'no_data'
|
|
306
|
+
WHEN s.critical_low IS NOT NULL AND r.value < s.critical_low THEN 'critical_low'
|
|
307
|
+
WHEN s.critical_high IS NOT NULL AND r.value > s.critical_high THEN 'critical_high'
|
|
308
|
+
WHEN s.warning_low IS NOT NULL AND r.value < s.warning_low THEN 'warning_low'
|
|
309
|
+
WHEN s.warning_high IS NOT NULL AND r.value > s.warning_high THEN 'warning_high'
|
|
310
|
+
ELSE 'normal'
|
|
311
|
+
END AS status
|
|
312
|
+
FROM sensors s
|
|
313
|
+
LEFT JOIN (
|
|
314
|
+
SELECT sensor_id, value, timestamp,
|
|
315
|
+
ROW_NUMBER() OVER (PARTITION BY sensor_id ORDER BY timestamp DESC) AS rn
|
|
316
|
+
FROM sensor_readings_recent
|
|
317
|
+
) r ON r.sensor_id = s.id AND r.rn = 1
|
|
318
|
+
WHERE s.is_active = 1;
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Backup (Hot Backup with WAL)
|
|
322
|
+
|
|
323
|
+
```go
|
|
324
|
+
func BackupDatabase(ctx context.Context, db *sql.DB, destDir string) (string, error) {
|
|
325
|
+
filename := fmt.Sprintf("backup_%s.db", time.Now().Format("20060102_150405"))
|
|
326
|
+
destPath := filepath.Join(destDir, filename)
|
|
327
|
+
|
|
328
|
+
// VACUUM INTO creates consistent backup without stopping
|
|
329
|
+
query := fmt.Sprintf("VACUUM INTO '%s'", destPath)
|
|
330
|
+
_, err := db.ExecContext(ctx, query)
|
|
331
|
+
return destPath, err
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## SQLite vs PostgreSQL Syntax
|
|
336
|
+
|
|
337
|
+
| Aspect | SQLite | PostgreSQL |
|
|
338
|
+
|--------|--------|------------|
|
|
339
|
+
| Datetime | `datetime('now')` | `NOW()` |
|
|
340
|
+
| Interval | `datetime('now', '-1 hour')` | `NOW() - INTERVAL '1 hour'` |
|
|
341
|
+
| Boolean | `INTEGER (0/1)` | `BOOLEAN` |
|
|
342
|
+
| UUID | `TEXT` | `UUID` |
|
|
343
|
+
| Auto-ID | `INTEGER PRIMARY KEY` | `SERIAL` |
|
|
344
|
+
| JSON | `TEXT + json_*()` | `JSONB` |
|
|
345
|
+
|
|
346
|
+
## Performance Tips
|
|
347
|
+
|
|
348
|
+
```sql
|
|
349
|
+
-- Partial indexes (smaller, faster)
|
|
350
|
+
CREATE INDEX idx_readings_unsynced ON sensor_readings_recent(synced)
|
|
351
|
+
WHERE synced = 0;
|
|
352
|
+
|
|
353
|
+
-- Check query plan
|
|
354
|
+
EXPLAIN QUERY PLAN
|
|
355
|
+
SELECT * FROM sensor_readings_recent WHERE sensor_id = 'abc';
|
|
356
|
+
|
|
357
|
+
-- Update statistics
|
|
358
|
+
ANALYZE;
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Quick Reference
|
|
362
|
+
|
|
363
|
+
| Task | Command |
|
|
364
|
+
|------|---------|
|
|
365
|
+
| Check integrity | `PRAGMA integrity_check;` |
|
|
366
|
+
| Enable WAL | `PRAGMA journal_mode = WAL;` |
|
|
367
|
+
| Set busy timeout | `PRAGMA busy_timeout = 5000;` |
|
|
368
|
+
| Hot backup | `VACUUM INTO 'backup.db'` |
|
|
369
|
+
| DB size | `SELECT page_count * page_size FROM pragma_page_count(), pragma_page_size();` |
|
|
370
|
+
|
|
371
|
+
## Resources
|
|
372
|
+
|
|
373
|
+
- [SQLite Documentation](https://sqlite.org/docs.html)
|
|
374
|
+
- [modernc.org/sqlite](https://pkg.go.dev/modernc.org/sqlite)
|
|
375
|
+
- [rusqlite](https://docs.rs/rusqlite)
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Changelog
|
|
380
|
+
|
|
381
|
+
- **2.0** - Condensed from Plataforma Industrial SKILL-SQLITE
|
|
382
|
+
|
|
383
|
+
## Related Skills
|
|
384
|
+
|
|
385
|
+
- `mobile-ionic`: Offline-first mobile apps
|
|
386
|
+
- `ionic-capacitor`: Native SQLite access
|
|
387
|
+
- `duckdb-analytics`: Embedded analytics
|
|
388
|
+
- `rust-systems`: Embedded Rust apps
|