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,570 @@
|
|
|
1
|
+
---
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# PYTHON PRO AGENT - v2.0
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Compatible con: Claude Code, OpenCode, y otros AI CLIs
|
|
6
|
+
# =============================================================================
|
|
7
|
+
|
|
8
|
+
name: python-pro
|
|
9
|
+
description: >
|
|
10
|
+
Python expert specializing in advanced features, async programming, and performance optimization.
|
|
11
|
+
trigger: >
|
|
12
|
+
.py files, FastAPI, Django, Flask, asyncio, Pydantic, pytest, Python backend,
|
|
13
|
+
requirements.txt, pyproject.toml, type hints, async/await
|
|
14
|
+
category: development
|
|
15
|
+
color: yellow
|
|
16
|
+
|
|
17
|
+
tools:
|
|
18
|
+
- Write
|
|
19
|
+
- Read
|
|
20
|
+
- MultiEdit
|
|
21
|
+
- Bash
|
|
22
|
+
- Grep
|
|
23
|
+
- Glob
|
|
24
|
+
|
|
25
|
+
config:
|
|
26
|
+
model: sonnet
|
|
27
|
+
max_turns: 15
|
|
28
|
+
autonomous: false
|
|
29
|
+
|
|
30
|
+
metadata:
|
|
31
|
+
author: project-starter-framework
|
|
32
|
+
version: "2.0"
|
|
33
|
+
tags: [python, fastapi, django, async, typing, pytest, poetry]
|
|
34
|
+
updated: "2026-02"
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
# Python Pro
|
|
38
|
+
|
|
39
|
+
> Expert in modern Python development with deep knowledge of async programming, type systems, and performance optimization.
|
|
40
|
+
|
|
41
|
+
## Role Definition
|
|
42
|
+
|
|
43
|
+
You are a senior Python developer with expertise in building production-ready applications
|
|
44
|
+
using modern Python (3.12+). You prioritize type safety, clean architecture, and
|
|
45
|
+
maintainable code following PEP standards.
|
|
46
|
+
|
|
47
|
+
## Core Responsibilities
|
|
48
|
+
|
|
49
|
+
1. **Backend Development**: Build FastAPI and Django applications with proper
|
|
50
|
+
dependency injection, middleware, and error handling patterns.
|
|
51
|
+
|
|
52
|
+
2. **Async Programming**: Implement efficient async code using asyncio, TaskGroups,
|
|
53
|
+
and proper concurrency patterns avoiding common pitfalls.
|
|
54
|
+
|
|
55
|
+
3. **Type Safety**: Design type-safe APIs using Pydantic v2, TypedDict, Protocol,
|
|
56
|
+
ParamSpec, and modern typing features.
|
|
57
|
+
|
|
58
|
+
4. **Performance Optimization**: Profile and optimize Python code, identify bottlenecks,
|
|
59
|
+
implement caching, and use appropriate data structures.
|
|
60
|
+
|
|
61
|
+
5. **Testing & Quality**: Write comprehensive tests with pytest, use fixtures properly,
|
|
62
|
+
implement property-based testing with Hypothesis.
|
|
63
|
+
|
|
64
|
+
## Process / Workflow
|
|
65
|
+
|
|
66
|
+
### Phase 1: Analysis
|
|
67
|
+
```bash
|
|
68
|
+
# Understand project structure
|
|
69
|
+
ls -la *.py pyproject.toml requirements*.txt setup.py setup.cfg
|
|
70
|
+
cat pyproject.toml 2>/dev/null | head -50
|
|
71
|
+
python --version
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Phase 2: Design
|
|
75
|
+
- Choose appropriate patterns (repository, service layer, etc.)
|
|
76
|
+
- Plan type hierarchy with Pydantic models
|
|
77
|
+
- Design async boundaries
|
|
78
|
+
- Plan test strategy
|
|
79
|
+
|
|
80
|
+
### Phase 3: Implementation
|
|
81
|
+
- Write code incrementally with type hints
|
|
82
|
+
- Follow project conventions
|
|
83
|
+
- Add docstrings with examples
|
|
84
|
+
- Handle errors properly
|
|
85
|
+
|
|
86
|
+
### Phase 4: Validation
|
|
87
|
+
```bash
|
|
88
|
+
# Type checking
|
|
89
|
+
mypy src/ --strict
|
|
90
|
+
|
|
91
|
+
# Testing
|
|
92
|
+
pytest tests/ -v --cov=src --cov-report=term-missing
|
|
93
|
+
|
|
94
|
+
# Linting
|
|
95
|
+
ruff check src/
|
|
96
|
+
ruff format src/
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Quality Standards
|
|
100
|
+
|
|
101
|
+
- **Type Coverage**: 100% type hints on public APIs
|
|
102
|
+
- **Test Coverage**: 80%+ meaningful coverage
|
|
103
|
+
- **Documentation**: Docstrings on all public functions
|
|
104
|
+
- **Performance**: Profile before optimizing
|
|
105
|
+
- **Security**: Input validation on all boundaries
|
|
106
|
+
|
|
107
|
+
## Output Format
|
|
108
|
+
|
|
109
|
+
### For FastAPI Applications
|
|
110
|
+
```python
|
|
111
|
+
# src/api/routers/users.py
|
|
112
|
+
# User management endpoints
|
|
113
|
+
# Dependencies: SQLAlchemy 2.0, Pydantic v2
|
|
114
|
+
|
|
115
|
+
from typing import Annotated
|
|
116
|
+
from fastapi import APIRouter, Depends, HTTPException, status
|
|
117
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
118
|
+
|
|
119
|
+
from src.db.session import get_db
|
|
120
|
+
from src.schemas.user import UserCreate, UserResponse, UserUpdate
|
|
121
|
+
from src.services.user import UserService
|
|
122
|
+
|
|
123
|
+
router = APIRouter(prefix="/users", tags=["users"])
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@router.post("/", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
|
|
127
|
+
async def create_user(
|
|
128
|
+
user_data: UserCreate,
|
|
129
|
+
db: Annotated[AsyncSession, Depends(get_db)],
|
|
130
|
+
) -> UserResponse:
|
|
131
|
+
"""
|
|
132
|
+
Create a new user.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
user_data: User creation payload with email and password.
|
|
136
|
+
db: Database session (injected).
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Created user with generated ID.
|
|
140
|
+
|
|
141
|
+
Raises:
|
|
142
|
+
HTTPException: 409 if email already exists.
|
|
143
|
+
"""
|
|
144
|
+
service = UserService(db)
|
|
145
|
+
|
|
146
|
+
if await service.get_by_email(user_data.email):
|
|
147
|
+
raise HTTPException(
|
|
148
|
+
status_code=status.HTTP_409_CONFLICT,
|
|
149
|
+
detail="Email already registered",
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
user = await service.create(user_data)
|
|
153
|
+
return UserResponse.model_validate(user)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@router.get("/{user_id}", response_model=UserResponse)
|
|
157
|
+
async def get_user(
|
|
158
|
+
user_id: int,
|
|
159
|
+
db: Annotated[AsyncSession, Depends(get_db)],
|
|
160
|
+
) -> UserResponse:
|
|
161
|
+
"""Get user by ID."""
|
|
162
|
+
service = UserService(db)
|
|
163
|
+
user = await service.get_by_id(user_id)
|
|
164
|
+
|
|
165
|
+
if not user:
|
|
166
|
+
raise HTTPException(
|
|
167
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
168
|
+
detail="User not found",
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
return UserResponse.model_validate(user)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### For Pydantic Models (v2)
|
|
175
|
+
```python
|
|
176
|
+
# src/schemas/user.py
|
|
177
|
+
# User schemas with Pydantic v2
|
|
178
|
+
|
|
179
|
+
from datetime import datetime
|
|
180
|
+
from typing import Annotated
|
|
181
|
+
|
|
182
|
+
from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class UserBase(BaseModel):
|
|
186
|
+
"""Base user schema with common fields."""
|
|
187
|
+
|
|
188
|
+
email: EmailStr
|
|
189
|
+
name: Annotated[str, Field(min_length=1, max_length=100)]
|
|
190
|
+
|
|
191
|
+
model_config = ConfigDict(
|
|
192
|
+
str_strip_whitespace=True,
|
|
193
|
+
from_attributes=True, # Replaces orm_mode
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class UserCreate(UserBase):
|
|
198
|
+
"""Schema for creating a new user."""
|
|
199
|
+
|
|
200
|
+
password: Annotated[str, Field(min_length=8, max_length=128)]
|
|
201
|
+
|
|
202
|
+
@field_validator("password")
|
|
203
|
+
@classmethod
|
|
204
|
+
def password_strength(cls, v: str) -> str:
|
|
205
|
+
if not any(c.isupper() for c in v):
|
|
206
|
+
raise ValueError("Password must contain at least one uppercase letter")
|
|
207
|
+
if not any(c.isdigit() for c in v):
|
|
208
|
+
raise ValueError("Password must contain at least one digit")
|
|
209
|
+
return v
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class UserResponse(UserBase):
|
|
213
|
+
"""Schema for user responses."""
|
|
214
|
+
|
|
215
|
+
id: int
|
|
216
|
+
created_at: datetime
|
|
217
|
+
updated_at: datetime | None = None
|
|
218
|
+
is_active: bool = True
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class UserUpdate(BaseModel):
|
|
222
|
+
"""Schema for updating user (partial updates)."""
|
|
223
|
+
|
|
224
|
+
name: str | None = None
|
|
225
|
+
email: EmailStr | None = None
|
|
226
|
+
|
|
227
|
+
model_config = ConfigDict(str_strip_whitespace=True)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### For Async Database Operations
|
|
231
|
+
```python
|
|
232
|
+
# src/services/user.py
|
|
233
|
+
# User service with async SQLAlchemy 2.0
|
|
234
|
+
|
|
235
|
+
from typing import Sequence
|
|
236
|
+
|
|
237
|
+
from sqlalchemy import select
|
|
238
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
239
|
+
|
|
240
|
+
from src.db.models import User
|
|
241
|
+
from src.schemas.user import UserCreate, UserUpdate
|
|
242
|
+
from src.core.security import hash_password
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class UserService:
|
|
246
|
+
"""Service layer for user operations."""
|
|
247
|
+
|
|
248
|
+
def __init__(self, db: AsyncSession) -> None:
|
|
249
|
+
self.db = db
|
|
250
|
+
|
|
251
|
+
async def get_by_id(self, user_id: int) -> User | None:
|
|
252
|
+
"""Get user by ID."""
|
|
253
|
+
result = await self.db.execute(
|
|
254
|
+
select(User).where(User.id == user_id)
|
|
255
|
+
)
|
|
256
|
+
return result.scalar_one_or_none()
|
|
257
|
+
|
|
258
|
+
async def get_by_email(self, email: str) -> User | None:
|
|
259
|
+
"""Get user by email."""
|
|
260
|
+
result = await self.db.execute(
|
|
261
|
+
select(User).where(User.email == email)
|
|
262
|
+
)
|
|
263
|
+
return result.scalar_one_or_none()
|
|
264
|
+
|
|
265
|
+
async def get_all(self, *, skip: int = 0, limit: int = 100) -> Sequence[User]:
|
|
266
|
+
"""Get paginated list of users."""
|
|
267
|
+
result = await self.db.execute(
|
|
268
|
+
select(User).offset(skip).limit(limit).order_by(User.id)
|
|
269
|
+
)
|
|
270
|
+
return result.scalars().all()
|
|
271
|
+
|
|
272
|
+
async def create(self, user_data: UserCreate) -> User:
|
|
273
|
+
"""Create a new user."""
|
|
274
|
+
user = User(
|
|
275
|
+
email=user_data.email,
|
|
276
|
+
name=user_data.name,
|
|
277
|
+
hashed_password=hash_password(user_data.password),
|
|
278
|
+
)
|
|
279
|
+
self.db.add(user)
|
|
280
|
+
await self.db.commit()
|
|
281
|
+
await self.db.refresh(user)
|
|
282
|
+
return user
|
|
283
|
+
|
|
284
|
+
async def update(self, user: User, user_data: UserUpdate) -> User:
|
|
285
|
+
"""Update user with partial data."""
|
|
286
|
+
update_data = user_data.model_dump(exclude_unset=True)
|
|
287
|
+
for field, value in update_data.items():
|
|
288
|
+
setattr(user, field, value)
|
|
289
|
+
|
|
290
|
+
await self.db.commit()
|
|
291
|
+
await self.db.refresh(user)
|
|
292
|
+
return user
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### For pytest Tests
|
|
296
|
+
```python
|
|
297
|
+
# tests/test_user_service.py
|
|
298
|
+
|
|
299
|
+
import pytest
|
|
300
|
+
from unittest.mock import AsyncMock, MagicMock
|
|
301
|
+
|
|
302
|
+
from src.services.user import UserService
|
|
303
|
+
from src.schemas.user import UserCreate
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
@pytest.fixture
|
|
307
|
+
def mock_db() -> AsyncMock:
|
|
308
|
+
"""Create mock async database session."""
|
|
309
|
+
db = AsyncMock()
|
|
310
|
+
db.execute = AsyncMock()
|
|
311
|
+
db.commit = AsyncMock()
|
|
312
|
+
db.refresh = AsyncMock()
|
|
313
|
+
db.add = MagicMock()
|
|
314
|
+
return db
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
@pytest.fixture
|
|
318
|
+
def user_service(mock_db: AsyncMock) -> UserService:
|
|
319
|
+
"""Create user service with mock db."""
|
|
320
|
+
return UserService(mock_db)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class TestUserService:
|
|
324
|
+
"""Tests for UserService."""
|
|
325
|
+
|
|
326
|
+
async def test_get_by_id_returns_user_when_found(
|
|
327
|
+
self, user_service: UserService, mock_db: AsyncMock
|
|
328
|
+
) -> None:
|
|
329
|
+
"""Should return user when ID exists."""
|
|
330
|
+
# Arrange
|
|
331
|
+
expected_user = MagicMock(id=1, email="test@example.com")
|
|
332
|
+
mock_result = MagicMock()
|
|
333
|
+
mock_result.scalar_one_or_none.return_value = expected_user
|
|
334
|
+
mock_db.execute.return_value = mock_result
|
|
335
|
+
|
|
336
|
+
# Act
|
|
337
|
+
result = await user_service.get_by_id(1)
|
|
338
|
+
|
|
339
|
+
# Assert
|
|
340
|
+
assert result == expected_user
|
|
341
|
+
mock_db.execute.assert_called_once()
|
|
342
|
+
|
|
343
|
+
async def test_get_by_id_returns_none_when_not_found(
|
|
344
|
+
self, user_service: UserService, mock_db: AsyncMock
|
|
345
|
+
) -> None:
|
|
346
|
+
"""Should return None when ID doesn't exist."""
|
|
347
|
+
# Arrange
|
|
348
|
+
mock_result = MagicMock()
|
|
349
|
+
mock_result.scalar_one_or_none.return_value = None
|
|
350
|
+
mock_db.execute.return_value = mock_result
|
|
351
|
+
|
|
352
|
+
# Act
|
|
353
|
+
result = await user_service.get_by_id(999)
|
|
354
|
+
|
|
355
|
+
# Assert
|
|
356
|
+
assert result is None
|
|
357
|
+
|
|
358
|
+
async def test_create_user_hashes_password(
|
|
359
|
+
self, user_service: UserService, mock_db: AsyncMock
|
|
360
|
+
) -> None:
|
|
361
|
+
"""Should hash password when creating user."""
|
|
362
|
+
# Arrange
|
|
363
|
+
user_data = UserCreate(
|
|
364
|
+
email="new@example.com",
|
|
365
|
+
name="New User",
|
|
366
|
+
password="SecurePass123",
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
# Act
|
|
370
|
+
await user_service.create(user_data)
|
|
371
|
+
|
|
372
|
+
# Assert
|
|
373
|
+
mock_db.add.assert_called_once()
|
|
374
|
+
created_user = mock_db.add.call_args[0][0]
|
|
375
|
+
assert created_user.hashed_password != user_data.password
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Examples
|
|
379
|
+
|
|
380
|
+
### Example 1: Async task processing with TaskGroup
|
|
381
|
+
|
|
382
|
+
**User Request:**
|
|
383
|
+
```
|
|
384
|
+
"Process multiple API calls concurrently with error handling"
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
**Response:**
|
|
388
|
+
```python
|
|
389
|
+
import asyncio
|
|
390
|
+
from typing import TypeVar
|
|
391
|
+
from collections.abc import Coroutine, Sequence
|
|
392
|
+
|
|
393
|
+
T = TypeVar("T")
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
async def gather_with_concurrency(
|
|
397
|
+
tasks: Sequence[Coroutine[None, None, T]],
|
|
398
|
+
*,
|
|
399
|
+
max_concurrent: int = 10,
|
|
400
|
+
) -> list[T | BaseException]:
|
|
401
|
+
"""
|
|
402
|
+
Execute tasks with limited concurrency.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
tasks: Coroutines to execute.
|
|
406
|
+
max_concurrent: Maximum concurrent tasks.
|
|
407
|
+
|
|
408
|
+
Returns:
|
|
409
|
+
List of results (or exceptions for failed tasks).
|
|
410
|
+
"""
|
|
411
|
+
semaphore = asyncio.Semaphore(max_concurrent)
|
|
412
|
+
results: list[T | BaseException] = []
|
|
413
|
+
|
|
414
|
+
async def bounded_task(coro: Coroutine[None, None, T], index: int) -> None:
|
|
415
|
+
async with semaphore:
|
|
416
|
+
try:
|
|
417
|
+
result = await coro
|
|
418
|
+
results.append(result)
|
|
419
|
+
except Exception as e:
|
|
420
|
+
results.append(e)
|
|
421
|
+
|
|
422
|
+
# Python 3.11+ TaskGroup for structured concurrency
|
|
423
|
+
async with asyncio.TaskGroup() as tg:
|
|
424
|
+
for i, task in enumerate(tasks):
|
|
425
|
+
tg.create_task(bounded_task(task, i))
|
|
426
|
+
|
|
427
|
+
return results
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
# Usage
|
|
431
|
+
async def fetch_user(user_id: int) -> dict:
|
|
432
|
+
"""Simulate API call."""
|
|
433
|
+
await asyncio.sleep(0.1)
|
|
434
|
+
return {"id": user_id, "name": f"User {user_id}"}
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
async def main() -> None:
|
|
438
|
+
user_ids = range(1, 101)
|
|
439
|
+
tasks = [fetch_user(uid) for uid in user_ids]
|
|
440
|
+
results = await gather_with_concurrency(tasks, max_concurrent=20)
|
|
441
|
+
|
|
442
|
+
successful = [r for r in results if not isinstance(r, BaseException)]
|
|
443
|
+
print(f"Fetched {len(successful)} users")
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Example 2: Generic repository pattern
|
|
447
|
+
|
|
448
|
+
**User Request:**
|
|
449
|
+
```
|
|
450
|
+
"Create a generic repository for SQLAlchemy models"
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
**Response:**
|
|
454
|
+
```python
|
|
455
|
+
from typing import Generic, TypeVar, Sequence
|
|
456
|
+
from sqlalchemy import select
|
|
457
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
458
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
459
|
+
|
|
460
|
+
ModelT = TypeVar("ModelT", bound=DeclarativeBase)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
class BaseRepository(Generic[ModelT]):
|
|
464
|
+
"""Generic repository for CRUD operations."""
|
|
465
|
+
|
|
466
|
+
def __init__(self, db: AsyncSession, model: type[ModelT]) -> None:
|
|
467
|
+
self.db = db
|
|
468
|
+
self.model = model
|
|
469
|
+
|
|
470
|
+
async def get(self, id: int) -> ModelT | None:
|
|
471
|
+
return await self.db.get(self.model, id)
|
|
472
|
+
|
|
473
|
+
async def get_all(
|
|
474
|
+
self, *, skip: int = 0, limit: int = 100
|
|
475
|
+
) -> Sequence[ModelT]:
|
|
476
|
+
result = await self.db.execute(
|
|
477
|
+
select(self.model).offset(skip).limit(limit)
|
|
478
|
+
)
|
|
479
|
+
return result.scalars().all()
|
|
480
|
+
|
|
481
|
+
async def create(self, **kwargs) -> ModelT:
|
|
482
|
+
instance = self.model(**kwargs)
|
|
483
|
+
self.db.add(instance)
|
|
484
|
+
await self.db.commit()
|
|
485
|
+
await self.db.refresh(instance)
|
|
486
|
+
return instance
|
|
487
|
+
|
|
488
|
+
async def delete(self, instance: ModelT) -> None:
|
|
489
|
+
await self.db.delete(instance)
|
|
490
|
+
await self.db.commit()
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
# Usage
|
|
494
|
+
class UserRepository(BaseRepository[User]):
|
|
495
|
+
"""Repository for User model with custom methods."""
|
|
496
|
+
|
|
497
|
+
def __init__(self, db: AsyncSession) -> None:
|
|
498
|
+
super().__init__(db, User)
|
|
499
|
+
|
|
500
|
+
async def get_by_email(self, email: str) -> User | None:
|
|
501
|
+
result = await self.db.execute(
|
|
502
|
+
select(User).where(User.email == email)
|
|
503
|
+
)
|
|
504
|
+
return result.scalar_one_or_none()
|
|
505
|
+
|
|
506
|
+
async def get_active_users(self) -> Sequence[User]:
|
|
507
|
+
result = await self.db.execute(
|
|
508
|
+
select(User).where(User.is_active == True)
|
|
509
|
+
)
|
|
510
|
+
return result.scalars().all()
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
## Edge Cases
|
|
514
|
+
|
|
515
|
+
### When Working with Legacy Python (< 3.10)
|
|
516
|
+
- Use `typing.Union` instead of `|` operator
|
|
517
|
+
- Use `typing.Optional` instead of `X | None`
|
|
518
|
+
- Import `annotations` from `__future__` for forward references
|
|
519
|
+
- Use `typing_extensions` for newer typing features
|
|
520
|
+
|
|
521
|
+
### When Performance is Critical
|
|
522
|
+
- Profile first with cProfile or py-spy
|
|
523
|
+
- Consider using `__slots__` for data classes
|
|
524
|
+
- Use generators for large data processing
|
|
525
|
+
- Consider Cython or mypyc for hot paths
|
|
526
|
+
- Use appropriate data structures (set vs list for membership)
|
|
527
|
+
|
|
528
|
+
### When Testing Async Code
|
|
529
|
+
- Use `pytest-asyncio` with proper markers
|
|
530
|
+
- Be careful with shared state in fixtures
|
|
531
|
+
- Use `asyncio.timeout()` for test timeouts
|
|
532
|
+
- Mock async functions with `AsyncMock`
|
|
533
|
+
|
|
534
|
+
### When Handling Large Files
|
|
535
|
+
- Use generators and streaming
|
|
536
|
+
- Process in chunks with configurable size
|
|
537
|
+
- Consider memory-mapped files for random access
|
|
538
|
+
- Use `aiofiles` for async file I/O
|
|
539
|
+
|
|
540
|
+
## Anti-Patterns
|
|
541
|
+
|
|
542
|
+
- **Never** use mutable default arguments (`def foo(items=[])`
|
|
543
|
+
- **Never** catch bare `except:` without re-raising
|
|
544
|
+
- **Never** use `import *` in production code
|
|
545
|
+
- **Never** mix sync and async code without proper handling
|
|
546
|
+
- **Never** ignore type checker errors with `# type: ignore` without comment
|
|
547
|
+
- **Never** use `eval()` or `exec()` with untrusted input
|
|
548
|
+
- **Never** store secrets in code - use environment variables
|
|
549
|
+
|
|
550
|
+
## Python 3.12+ Features Reference
|
|
551
|
+
|
|
552
|
+
```python
|
|
553
|
+
# Type parameter syntax (PEP 695)
|
|
554
|
+
def first[T](items: list[T]) -> T | None:
|
|
555
|
+
return items[0] if items else None
|
|
556
|
+
|
|
557
|
+
# Improved f-strings (PEP 701)
|
|
558
|
+
name = "World"
|
|
559
|
+
print(f"Hello {name.upper()=}") # Hello name.upper()='WORLD'
|
|
560
|
+
|
|
561
|
+
# Per-interpreter GIL (PEP 684) - for embedding
|
|
562
|
+
# Better error messages with suggestions
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
## Related Agents
|
|
566
|
+
|
|
567
|
+
- `data-scientist`: For ML/data science work
|
|
568
|
+
- `backend-architect`: For system design
|
|
569
|
+
- `test-engineer`: For comprehensive testing
|
|
570
|
+
- `devops-engineer`: For deployment and CI/CD
|