@caddis/cli 0.3.0 → 0.4.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/bundles/antigravity-plugin/agents/knowledge-transfer.md +17 -32
- package/bundles/antigravity-plugin/caddis_statusline.py +25 -3
- package/bundles/antigravity-plugin/guard_agy.py +41 -9
- package/bundles/antigravity-plugin/plugin.json +1 -1
- package/bundles/antigravity-plugin/scripts/caddis_inventory.py +73 -3
- package/bundles/antigravity-plugin/scripts/caddis_statusline.py +25 -3
- package/bundles/antigravity-plugin/scripts/claudster_config.py +0 -5
- package/bundles/antigravity-plugin/scripts/hook_log.py +1 -1
- package/bundles/antigravity-plugin/scripts/new-machine.ps1 +170 -0
- package/bundles/antigravity-plugin/scripts/oss_ask.py +1 -1
- package/bundles/antigravity-plugin/scripts/oss_model.py +10 -2
- package/bundles/antigravity-plugin/scripts/oss_review.py +1 -1
- package/bundles/antigravity-plugin/scripts/setup_project_ai.py +9 -6
- package/bundles/antigravity-plugin/session_end_agy.py +31 -6
- package/bundles/antigravity-plugin/skills/advisory-hub/SKILL.md +44 -0
- package/bundles/antigravity-plugin/skills/code-review/SKILL.md +11 -0
- package/bundles/antigravity-plugin/skills/feature-plan/SKILL.md +21 -0
- package/bundles/antigravity-plugin/skills/gate-review/SKILL.md +11 -0
- package/bundles/antigravity-plugin/skills/implement/SKILL.md +36 -0
- package/bundles/antigravity-plugin/skills/security-review/SKILL.md +9 -0
- package/bundles/antigravity-plugin/skills/validate-phase/SKILL.md +31 -0
- package/bundles/antigravity-plugin/warm_start_agy.py +96 -9
- package/bundles/antigravity-plugin-extras/plugin.json +1 -1
- package/bundles/codex/.codex/config.toml.example +36 -0
- package/bundles/codex/.codex/skills/coding/api-design/SKILL.md +249 -0
- package/bundles/codex/.codex/skills/coding/backend-development/SKILL.md +305 -0
- package/bundles/codex/.codex/skills/coding/code-review/SKILL.md +140 -0
- package/bundles/codex/.codex/skills/coding/codebase-audit/SKILL.md +204 -0
- package/bundles/codex/.codex/skills/coding/dashboard-perf-sweep/SKILL.md +132 -0
- package/bundles/codex/.codex/skills/coding/dashboard-perf-sweep/scripts/perf_sweep.py +249 -0
- package/bundles/codex/.codex/skills/coding/error-handling/SKILL.md +260 -0
- package/bundles/codex/.codex/skills/coding/fastapi-dev/SKILL.md +300 -0
- package/bundles/codex/.codex/skills/coding/javascript-typescript/SKILL.md +142 -0
- package/bundles/codex/.codex/skills/coding/python/SKILL.md +388 -0
- package/bundles/codex/.codex/skills/coding/refactoring/SKILL.md +486 -0
- package/bundles/codex/.codex/skills/coding/security-review/SKILL.md +205 -0
- package/bundles/codex/.codex/skills/coding/sql/SKILL.md +321 -0
- package/bundles/codex/.codex/skills/data/database-design/SKILL.md +177 -0
- package/bundles/codex/.codex/skills/data/db-diagram/SKILL.md +148 -0
- package/bundles/codex/.codex/skills/data/db-diagram/scripts/sql_to_graph.py +1212 -0
- package/bundles/codex/.codex/skills/devops/ci-cd-pipeline/SKILL.md +315 -0
- package/bundles/codex/.codex/skills/devops/gh-cli/SKILL.md +195 -0
- package/bundles/codex/.codex/skills/devops/git-commit/SKILL.md +285 -0
- package/bundles/codex/.codex/skills/devops/using-git-worktrees/SKILL.md +217 -0
- package/bundles/codex/.codex/skills/devops/windows-deployment/SKILL.md +880 -0
- package/bundles/codex/.codex/skills/docs/code-documentation/SKILL.md +271 -0
- package/bundles/codex/.codex/skills/docs/explainer-doc/SKILL.md +97 -0
- package/bundles/codex/.codex/skills/docs/technical-writing/SKILL.md +286 -0
- package/bundles/codex/.codex/skills/docs/writing-plans/SKILL.md +387 -0
- package/bundles/codex/.codex/skills/frontend/css-architecture/SKILL.md +305 -0
- package/bundles/codex/.codex/skills/frontend/css-architecture/references/RESPONSIVE-DESIGN.md +604 -0
- package/bundles/codex/.codex/skills/frontend/frontend-design/SKILL.md +193 -0
- package/bundles/codex/.codex/skills/frontend/frontend-design/references/my-tech-stack.md +127 -0
- package/bundles/codex/.codex/skills/frontend/mockup/SKILL.md +242 -0
- package/bundles/codex/.codex/skills/frontend/react-best-practices/SKILL.md +269 -0
- package/bundles/codex/.codex/skills/frontend/react-dev/README.md +404 -0
- package/bundles/codex/.codex/skills/frontend/react-dev/SKILL.md +459 -0
- package/bundles/codex/.codex/skills/frontend/react-dev/examples/generic-components.md +579 -0
- package/bundles/codex/.codex/skills/frontend/react-dev/examples/server-components.md +579 -0
- package/bundles/codex/.codex/skills/frontend/react-dev/references/event-handlers.md +574 -0
- package/bundles/codex/.codex/skills/frontend/react-dev/references/hooks.md +456 -0
- package/bundles/codex/.codex/skills/frontend/react-dev/references/react-19-patterns.md +638 -0
- package/bundles/codex/.codex/skills/frontend/react-dev/references/react-router.md +1002 -0
- package/bundles/codex/.codex/skills/frontend/react-dev/references/tanstack-router.md +587 -0
- package/bundles/codex/.codex/skills/frontend/ui-review/SKILL.md +176 -0
- package/bundles/codex/.codex/skills/frontend/ui-review/references/framework-fixes.md +471 -0
- package/bundles/codex/.codex/skills/frontend/ui-review/references/visual-checklist.md +236 -0
- package/bundles/codex/.codex/skills/frontend/warm-editorial-ui/DESIGN_TOKENS.md +487 -0
- package/bundles/codex/.codex/skills/frontend/warm-editorial-ui/IMPLEMENTATION_GUIDE.md +177 -0
- package/bundles/codex/.codex/skills/frontend/warm-editorial-ui/SKILL.md +732 -0
- package/bundles/codex/.codex/skills/media/draw-io/SKILL.md +162 -0
- package/bundles/codex/.codex/skills/media/draw-io/references/aws-icons.md +677 -0
- package/bundles/codex/.codex/skills/media/draw-io/references/layout-guidelines.md +142 -0
- package/bundles/codex/.codex/skills/media/draw-io/references/troubleshooting.md +118 -0
- package/bundles/codex/.codex/skills/media/draw-io/references/workflows.md +103 -0
- package/bundles/codex/.codex/skills/media/draw-io/scripts/convert-drawio-to-png.sh +25 -0
- package/bundles/codex/.codex/skills/media/draw-io/scripts/find_aws_icon.py +79 -0
- package/bundles/codex/.codex/skills/media/mermaid-diagrams/SKILL.md +292 -0
- package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/ENHANCEMENTS.md +264 -0
- package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/MERMAID-SUMMARY.md +137 -0
- package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/advanced-features.md +556 -0
- package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/architecture-diagrams.md +192 -0
- package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/c4-diagrams.md +410 -0
- package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/class-diagrams.md +361 -0
- package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/erd-diagrams.md +510 -0
- package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/flowcharts.md +450 -0
- package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/sequence-diagrams.md +394 -0
- package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/troubleshooting.md +335 -0
- package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/workflows.md +418 -0
- package/bundles/codex/.codex/skills/media/particle-art/SKILL.md +243 -0
- package/bundles/codex/.codex/skills/media/particle-art/references/canvas-utils.ts +171 -0
- package/bundles/codex/.codex/skills/media/particle-art/references/dot-field.template.tsx +203 -0
- package/bundles/codex/.codex/skills/media/particle-art/references/flow-field.template.tsx +263 -0
- package/bundles/codex/.codex/skills/media/particle-art/references/node-shape.template.tsx +261 -0
- package/bundles/codex/.codex/skills/media/particle-art/references/shape-sampler.ts +281 -0
- package/bundles/codex/.codex/skills/media/particle-art/references/stipple-morph.template.tsx +167 -0
- package/bundles/codex/.codex/skills/media/particle-art/references/stipple.template.tsx +175 -0
- package/bundles/codex/.codex/skills/media/particle-art/references/trail-ghost.template.tsx +266 -0
- package/bundles/codex/.codex/skills/media/particle-art/references/usage-examples.md +320 -0
- package/bundles/codex/.codex/skills/testing/playwright/API_REFERENCE.md +653 -0
- package/bundles/codex/.codex/skills/testing/playwright/SKILL.md +454 -0
- package/bundles/codex/.codex/skills/testing/playwright/lib/helpers.js +441 -0
- package/bundles/codex/.codex/skills/testing/playwright/package.json +26 -0
- package/bundles/codex/.codex/skills/testing/playwright/run.js +228 -0
- package/bundles/codex/.codex/skills/testing/tdd-workflow/SKILL.md +188 -0
- package/bundles/codex/.codex/skills/testing/test-strategy/SKILL.md +155 -0
- package/bundles/codex/.codex/skills/testing/webapp-testing/LICENSE.txt +202 -0
- package/bundles/codex/.codex/skills/testing/webapp-testing/SKILL.md +97 -0
- package/bundles/codex/.codex/skills/testing/webapp-testing/examples/console_logging.py +35 -0
- package/bundles/codex/.codex/skills/testing/webapp-testing/examples/element_discovery.py +40 -0
- package/bundles/codex/.codex/skills/testing/webapp-testing/examples/static_html_automation.py +33 -0
- package/bundles/codex/.codex/skills/testing/webapp-testing/scripts/with_server.py +106 -0
- package/bundles/codex/.codex/skills/workflow/advisory-hub/SKILL.md +227 -0
- package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/context-template.md +214 -0
- package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/hub-handbook.md +233 -0
- package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/report-contract.md +94 -0
- package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/succession.md +118 -0
- package/bundles/codex/.codex/skills/workflow/best-practices/SKILL.md +500 -0
- package/bundles/codex/.codex/skills/workflow/best-practices/agents/best-practices-referencer.md +263 -0
- package/bundles/codex/.codex/skills/workflow/best-practices/agents/codebase-context-builder.md +326 -0
- package/bundles/codex/.codex/skills/workflow/best-practices/agents/task-intent-analyzer.md +245 -0
- package/bundles/codex/.codex/skills/workflow/best-practices/references/anti-patterns.md +571 -0
- package/bundles/codex/.codex/skills/workflow/best-practices/references/before-after-examples.md +1114 -0
- package/bundles/codex/.codex/skills/workflow/best-practices/references/best-practices-guide.md +513 -0
- package/bundles/codex/.codex/skills/workflow/best-practices/references/common-workflows.md +692 -0
- package/bundles/codex/.codex/skills/workflow/best-practices/references/prompt-patterns.md +547 -0
- package/bundles/codex/.codex/skills/workflow/brainstorming/SKILL.md +57 -0
- package/bundles/codex/.codex/skills/workflow/context-curator/SKILL.md +157 -0
- package/bundles/codex/.codex/skills/workflow/golden-plan/SKILL.md +586 -0
- package/bundles/codex/.codex/skills/workflow/preflight/SKILL.md +440 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/LICENSE.txt +202 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/SKILL.md +491 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/agents/analyzer.md +274 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/agents/comparator.md +202 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/agents/grader.md +223 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/assets/eval_review.html +146 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/eval-viewer/generate_review.py +471 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/eval-viewer/viewer.html +1325 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/references/schemas.md +430 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/__init__.py +0 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/generate_report.py +326 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/improve_description.py +247 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/package_skill.py +136 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/quick_validate.py +103 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/run_eval.py +310 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/run_loop.py +328 -0
- package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/utils.py +47 -0
- package/bundles/codex/AGENTS.md +146 -0
- package/bundles/manifest.json +4 -3
- package/dist/claude-deepseek.js +2 -2
- package/dist/claude-glm.js +2 -2
- package/dist/claude-oss.js +2 -2
- package/dist/cli.js +187 -56
- package/package.json +1 -1
- package/bundles/antigravity-plugin/scripts/dream_capture.py +0 -249
- package/bundles/antigravity-plugin/scripts/dream_memory.py +0 -438
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +0 -94
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Jura-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +0 -93
- package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: error-handling
|
|
3
|
+
description: Error handling patterns for Python and TypeScript applications. Use when designing error hierarchies, implementing retry logic, building error boundaries, or establishing logging strategies. Covers custom exceptions, result types, circuit breakers, and user-facing error messages.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Error Handling Skill
|
|
7
|
+
|
|
8
|
+
Errors are not exceptional — they are part of the contract. This skill defines how to classify, propagate, and recover from errors consistently.
|
|
9
|
+
|
|
10
|
+
## 1. When to Apply This Skill
|
|
11
|
+
|
|
12
|
+
**Trigger conditions:**
|
|
13
|
+
- Designing error handling for a new service or feature
|
|
14
|
+
- "How should we handle errors in this module?"
|
|
15
|
+
- Building retry logic or circuit breakers
|
|
16
|
+
- Reviewing code with bare `except:` or swallowed errors
|
|
17
|
+
- Establishing error handling conventions for a project
|
|
18
|
+
|
|
19
|
+
## 2. Error Classification
|
|
20
|
+
|
|
21
|
+
Every error falls into one of three categories. Handle each differently.
|
|
22
|
+
|
|
23
|
+
| Category | Retryable? | User Action? | Example |
|
|
24
|
+
|----------|-----------|-------------|---------|
|
|
25
|
+
| **Transient** | Yes | None (auto-retry) | Network timeout, DB connection dropped, rate limit |
|
|
26
|
+
| **Operational** | No | Inform user | Invalid input, resource not found, insufficient permissions |
|
|
27
|
+
| **Fatal** | No | Alert team | Corrupted state, config missing, unrecoverable assertion |
|
|
28
|
+
|
|
29
|
+
## 3. Python Exception Hierarchy
|
|
30
|
+
|
|
31
|
+
Build domain-specific exceptions. Never raise raw `Exception`.
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
class AppError(Exception):
|
|
35
|
+
"""Base error for the application."""
|
|
36
|
+
def __init__(self, message: str, code: str | None = None):
|
|
37
|
+
self.message = message
|
|
38
|
+
self.code = code or self.__class__.__name__
|
|
39
|
+
super().__init__(self.message)
|
|
40
|
+
|
|
41
|
+
# Operational errors (expected, handled gracefully)
|
|
42
|
+
class NotFoundError(AppError):
|
|
43
|
+
"""Resource does not exist."""
|
|
44
|
+
|
|
45
|
+
class ValidationError(AppError):
|
|
46
|
+
"""Input validation failed."""
|
|
47
|
+
|
|
48
|
+
class AuthorizationError(AppError):
|
|
49
|
+
"""User not authorized for this action."""
|
|
50
|
+
|
|
51
|
+
# Transient errors (retry-eligible)
|
|
52
|
+
class TransientError(AppError):
|
|
53
|
+
"""Temporary failure — safe to retry."""
|
|
54
|
+
|
|
55
|
+
class ExternalServiceError(TransientError):
|
|
56
|
+
"""External API call failed."""
|
|
57
|
+
|
|
58
|
+
class DatabaseConnectionError(TransientError):
|
|
59
|
+
"""Database connection lost."""
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Usage Patterns
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
# ✅ GOOD: Specific exception with context
|
|
66
|
+
def get_user(user_id: str) -> User:
|
|
67
|
+
user = repo.find_by_id(user_id)
|
|
68
|
+
if user is None:
|
|
69
|
+
raise NotFoundError(f"User {user_id} not found")
|
|
70
|
+
return user
|
|
71
|
+
|
|
72
|
+
# ✅ GOOD: Catch specific, re-raise as domain error
|
|
73
|
+
def fetch_external_data(url: str) -> dict:
|
|
74
|
+
try:
|
|
75
|
+
response = httpx.get(url, timeout=10)
|
|
76
|
+
response.raise_for_status()
|
|
77
|
+
return response.json()
|
|
78
|
+
except httpx.TimeoutException as e:
|
|
79
|
+
raise ExternalServiceError(f"Timeout calling {url}") from e
|
|
80
|
+
except httpx.HTTPStatusError as e:
|
|
81
|
+
raise ExternalServiceError(f"HTTP {e.response.status_code} from {url}") from e
|
|
82
|
+
|
|
83
|
+
# ❌ BAD: Bare except swallows everything
|
|
84
|
+
try:
|
|
85
|
+
result = process()
|
|
86
|
+
except:
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
# ❌ BAD: Catching too broadly
|
|
90
|
+
try:
|
|
91
|
+
result = process()
|
|
92
|
+
except Exception:
|
|
93
|
+
logger.error("Something went wrong")
|
|
94
|
+
return None # Hides the real error
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 4. TypeScript Error Patterns
|
|
98
|
+
|
|
99
|
+
### Result Type (for expected failures)
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
type Result<T, E = Error> =
|
|
103
|
+
| { ok: true; value: T }
|
|
104
|
+
| { ok: false; error: E };
|
|
105
|
+
|
|
106
|
+
function parseConfig(raw: string): Result<Config, ValidationError> {
|
|
107
|
+
try {
|
|
108
|
+
const parsed = JSON.parse(raw);
|
|
109
|
+
if (!isValidConfig(parsed)) {
|
|
110
|
+
return { ok: false, error: new ValidationError("Invalid config shape") };
|
|
111
|
+
}
|
|
112
|
+
return { ok: true, value: parsed as Config };
|
|
113
|
+
} catch {
|
|
114
|
+
return { ok: false, error: new ValidationError("Invalid JSON") };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Usage — caller must handle both cases
|
|
119
|
+
const result = parseConfig(rawInput);
|
|
120
|
+
if (!result.ok) {
|
|
121
|
+
showError(result.error.message);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
useConfig(result.value);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Error Boundaries (React)
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
'use client';
|
|
131
|
+
import { Component, type ReactNode } from 'react';
|
|
132
|
+
|
|
133
|
+
interface Props { children: ReactNode; fallback: ReactNode; }
|
|
134
|
+
interface State { hasError: boolean; }
|
|
135
|
+
|
|
136
|
+
export class ErrorBoundary extends Component<Props, State> {
|
|
137
|
+
state: State = { hasError: false };
|
|
138
|
+
|
|
139
|
+
static getDerivedStateFromError(): State {
|
|
140
|
+
return { hasError: true };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
componentDidCatch(error: Error, info: React.ErrorInfo) {
|
|
144
|
+
console.error('Component error:', error, info.componentStack);
|
|
145
|
+
// Report to error tracking service
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
render() {
|
|
149
|
+
return this.state.hasError ? this.props.fallback : this.props.children;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## 5. Retry with Exponential Backoff
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
import asyncio
|
|
158
|
+
import random
|
|
159
|
+
from loguru import logger
|
|
160
|
+
|
|
161
|
+
async def retry_with_backoff(
|
|
162
|
+
fn,
|
|
163
|
+
max_retries: int = 3,
|
|
164
|
+
base_delay: float = 1.0,
|
|
165
|
+
max_delay: float = 30.0,
|
|
166
|
+
retryable_exceptions: tuple = (TransientError,),
|
|
167
|
+
):
|
|
168
|
+
"""Retry a function with exponential backoff and jitter."""
|
|
169
|
+
for attempt in range(max_retries + 1):
|
|
170
|
+
try:
|
|
171
|
+
return await fn()
|
|
172
|
+
except retryable_exceptions as e:
|
|
173
|
+
if attempt == max_retries:
|
|
174
|
+
logger.error(f"All {max_retries} retries exhausted: {e}")
|
|
175
|
+
raise
|
|
176
|
+
delay = min(base_delay * (2 ** attempt), max_delay)
|
|
177
|
+
jitter = random.uniform(0, delay * 0.1)
|
|
178
|
+
logger.warning(f"Attempt {attempt + 1} failed: {e}. Retrying in {delay:.1f}s")
|
|
179
|
+
await asyncio.sleep(delay + jitter)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## 6. FastAPI Error Handling
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from fastapi import FastAPI, Request
|
|
186
|
+
from fastapi.responses import JSONResponse
|
|
187
|
+
from loguru import logger
|
|
188
|
+
|
|
189
|
+
app = FastAPI()
|
|
190
|
+
|
|
191
|
+
@app.exception_handler(NotFoundError)
|
|
192
|
+
async def not_found_handler(request: Request, exc: NotFoundError):
|
|
193
|
+
return JSONResponse(status_code=404, content={"error": exc.code, "message": exc.message})
|
|
194
|
+
|
|
195
|
+
@app.exception_handler(ValidationError)
|
|
196
|
+
async def validation_handler(request: Request, exc: ValidationError):
|
|
197
|
+
return JSONResponse(status_code=422, content={"error": exc.code, "message": exc.message})
|
|
198
|
+
|
|
199
|
+
@app.exception_handler(AuthorizationError)
|
|
200
|
+
async def auth_handler(request: Request, exc: AuthorizationError):
|
|
201
|
+
return JSONResponse(status_code=403, content={"error": exc.code, "message": exc.message})
|
|
202
|
+
|
|
203
|
+
@app.exception_handler(Exception)
|
|
204
|
+
async def unhandled_handler(request: Request, exc: Exception):
|
|
205
|
+
logger.exception(f"Unhandled error on {request.method} {request.url}")
|
|
206
|
+
# Never expose internal details to the client
|
|
207
|
+
return JSONResponse(status_code=500, content={"error": "InternalError", "message": "An internal error occurred."})
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## 7. Logging Errors Correctly
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
from loguru import logger
|
|
214
|
+
|
|
215
|
+
# ✅ GOOD: Log with context, not just the message
|
|
216
|
+
logger.error(f"Failed to process order {order_id}: {e}", order_id=order_id, customer_id=customer_id)
|
|
217
|
+
|
|
218
|
+
# ✅ GOOD: Use exception() to capture stack trace
|
|
219
|
+
try:
|
|
220
|
+
process_order(order)
|
|
221
|
+
except Exception as e:
|
|
222
|
+
logger.exception(f"Order processing failed for {order.id}")
|
|
223
|
+
raise
|
|
224
|
+
|
|
225
|
+
# ❌ BAD: Logging sensitive data
|
|
226
|
+
logger.error(f"Auth failed for {username} with password {password}")
|
|
227
|
+
|
|
228
|
+
# ❌ BAD: Logging without context
|
|
229
|
+
logger.error("Something failed")
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## 8. Error Message Guidelines
|
|
233
|
+
|
|
234
|
+
| Audience | Include | Exclude |
|
|
235
|
+
|----------|---------|---------|
|
|
236
|
+
| **End user** | What happened, what to do next | Stack traces, internal IDs, SQL errors |
|
|
237
|
+
| **Developer (logs)** | Full context, request ID, stack trace | User passwords, tokens, PII |
|
|
238
|
+
| **API consumer** | Error code, human message, field-level detail | Server internals, file paths |
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
# User-facing message
|
|
242
|
+
"Unable to load your dashboard. Please try again in a few minutes."
|
|
243
|
+
|
|
244
|
+
# API response
|
|
245
|
+
{"error": "ValidationError", "message": "Email format is invalid", "field": "email"}
|
|
246
|
+
|
|
247
|
+
# Log entry
|
|
248
|
+
"Validation failed on POST /api/users: email='not-an-email' — rejected by regex validator"
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## 9. Anti-Patterns
|
|
252
|
+
|
|
253
|
+
| Anti-Pattern | Problem | Fix |
|
|
254
|
+
|---|---|---|
|
|
255
|
+
| Bare `except:` / `except Exception:` | Catches KeyboardInterrupt, SystemExit | Catch specific exceptions |
|
|
256
|
+
| `return None` on error | Caller has no idea what went wrong | Raise typed exception |
|
|
257
|
+
| Logging and re-raising | Double logging, noisy | Log at the boundary, not at every layer |
|
|
258
|
+
| String error codes | Typos, no autocomplete | Enum or class-based error codes |
|
|
259
|
+
| Swallowing errors in background tasks | Silent failures, data loss | Log + alert + dead-letter queue |
|
|
260
|
+
| Generic "Something went wrong" | User can't self-serve | Specific message + action hint |
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fastapi-dev
|
|
3
|
+
description: Build FastAPI backends with standard patterns, error handling, and testing
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# FastAPI Development
|
|
7
|
+
|
|
8
|
+
> **Project Context** — Read `project-config.md` in the repo root for brand tokens, shared-library paths, and deployment targets.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
Invoke this skill when:
|
|
13
|
+
- Creating a new FastAPI backend for a project
|
|
14
|
+
- Adding API endpoints for Streamlit frontends
|
|
15
|
+
- Implementing repository pattern with SQL Server
|
|
16
|
+
- Setting up dependency injection chains
|
|
17
|
+
- Configuring CORS for Streamlit ↔ FastAPI communication
|
|
18
|
+
- Deploying FastAPI on IIS/on-premise infrastructure
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Project Structure
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
backend/
|
|
26
|
+
├── app/
|
|
27
|
+
│ ├── __init__.py
|
|
28
|
+
│ ├── main.py # FastAPI application entry
|
|
29
|
+
│ ├── config.py # Settings (pydantic-settings)
|
|
30
|
+
│ ├── dependencies.py # Shared DI providers
|
|
31
|
+
│ ├── routers/
|
|
32
|
+
│ │ ├── __init__.py
|
|
33
|
+
│ │ ├── records.py # Records endpoints
|
|
34
|
+
│ │ └── analytics.py # Analytics endpoints
|
|
35
|
+
│ ├── models/
|
|
36
|
+
│ │ ├── __init__.py
|
|
37
|
+
│ │ ├── record.py # Pydantic request/response models
|
|
38
|
+
│ │ └── user.py
|
|
39
|
+
│ ├── services/
|
|
40
|
+
│ │ ├── __init__.py
|
|
41
|
+
│ │ └── record_service.py
|
|
42
|
+
│ └── repositories/
|
|
43
|
+
│ ├── __init__.py
|
|
44
|
+
│ └── record_repo.py
|
|
45
|
+
├── tests/
|
|
46
|
+
├── .env
|
|
47
|
+
└── requirements.txt
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Steps
|
|
53
|
+
|
|
54
|
+
### Step 1: Configuration (pydantic-settings)
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
# app/config.py
|
|
58
|
+
from functools import lru_cache
|
|
59
|
+
from pydantic_settings import BaseSettings
|
|
60
|
+
from pydantic import Field
|
|
61
|
+
|
|
62
|
+
class Settings(BaseSettings):
|
|
63
|
+
app_name: str = "API"
|
|
64
|
+
debug: bool = False
|
|
65
|
+
db_server: str = Field(..., description="SQL Server host")
|
|
66
|
+
db_name: str = Field(..., description="Database name")
|
|
67
|
+
db_password: str = Field(..., repr=False)
|
|
68
|
+
api_key: str = Field(..., repr=False)
|
|
69
|
+
allowed_origins: list[str] = ["http://localhost:8501"]
|
|
70
|
+
ollama_base_url: str = "http://localhost:11434"
|
|
71
|
+
ollama_model: str = "llama3.1"
|
|
72
|
+
|
|
73
|
+
class Config:
|
|
74
|
+
env_file = ".env"
|
|
75
|
+
|
|
76
|
+
@lru_cache
|
|
77
|
+
def get_settings() -> Settings:
|
|
78
|
+
return Settings()
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Step 2: Pydantic Models (Base/Create/Update/Response pattern)
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
# app/models/record.py
|
|
85
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
86
|
+
from uuid import UUID
|
|
87
|
+
from datetime import datetime
|
|
88
|
+
from typing import Literal
|
|
89
|
+
|
|
90
|
+
class RecordBase(BaseModel):
|
|
91
|
+
customer_id: str = Field(..., min_length=1, max_length=50, example="CUST001")
|
|
92
|
+
record_type: Literal["billing", "service", "technical"]
|
|
93
|
+
description: str = Field(..., min_length=10, max_length=5000)
|
|
94
|
+
priority: int = Field(default=3, ge=1, le=5)
|
|
95
|
+
|
|
96
|
+
class RecordCreate(RecordBase):
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
class RecordUpdate(BaseModel):
|
|
100
|
+
status: Literal["open", "in_progress", "resolved"] | None = None
|
|
101
|
+
resolution_notes: str | None = Field(None, max_length=2000)
|
|
102
|
+
priority: int | None = Field(None, ge=1, le=5)
|
|
103
|
+
|
|
104
|
+
class RecordResponse(RecordBase):
|
|
105
|
+
model_config = ConfigDict(from_attributes=True)
|
|
106
|
+
id: UUID
|
|
107
|
+
status: str
|
|
108
|
+
created_at: datetime
|
|
109
|
+
updated_at: datetime | None = None
|
|
110
|
+
|
|
111
|
+
class RecordList(BaseModel):
|
|
112
|
+
items: list[RecordResponse]
|
|
113
|
+
total: int
|
|
114
|
+
page: int
|
|
115
|
+
per_page: int
|
|
116
|
+
pages: int
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Step 3: Dependency Injection Chain
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
# app/dependencies.py
|
|
123
|
+
from typing import Annotated, Generator
|
|
124
|
+
from fastapi import Depends, Header, HTTPException
|
|
125
|
+
from libs.data import DatabaseAdapter
|
|
126
|
+
|
|
127
|
+
def get_db_adapter() -> Generator[DatabaseAdapter, None, None]:
|
|
128
|
+
adapter = DatabaseAdapter()
|
|
129
|
+
try:
|
|
130
|
+
yield adapter
|
|
131
|
+
finally:
|
|
132
|
+
adapter.close()
|
|
133
|
+
|
|
134
|
+
def get_record_repository(
|
|
135
|
+
db: Annotated[DatabaseAdapter, Depends(get_db_adapter)]
|
|
136
|
+
) -> RecordRepository:
|
|
137
|
+
return RecordRepository(db)
|
|
138
|
+
|
|
139
|
+
def get_record_service(
|
|
140
|
+
repo: Annotated[RecordRepository, Depends(get_record_repository)]
|
|
141
|
+
) -> RecordService:
|
|
142
|
+
return RecordService(repo)
|
|
143
|
+
|
|
144
|
+
async def verify_api_key(
|
|
145
|
+
x_api_key: Annotated[str, Header(..., description="API Key")]
|
|
146
|
+
) -> str:
|
|
147
|
+
if x_api_key != get_settings().api_key:
|
|
148
|
+
raise HTTPException(status_code=401, detail="Invalid API key")
|
|
149
|
+
return x_api_key
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Step 4: Router Pattern
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
# app/routers/records.py
|
|
156
|
+
from fastapi import APIRouter, Depends, Query
|
|
157
|
+
from typing import Annotated
|
|
158
|
+
|
|
159
|
+
router = APIRouter(prefix="/records", tags=["records"])
|
|
160
|
+
|
|
161
|
+
@router.get("/", response_model=RecordList)
|
|
162
|
+
async def list_records(
|
|
163
|
+
service: Annotated[RecordService, Depends(get_record_service)],
|
|
164
|
+
page: int = Query(1, ge=1),
|
|
165
|
+
per_page: int = Query(25, ge=1, le=100),
|
|
166
|
+
status: str | None = None,
|
|
167
|
+
):
|
|
168
|
+
return await service.list_records(page=page, per_page=per_page, status=status)
|
|
169
|
+
|
|
170
|
+
@router.post("/", response_model=RecordResponse, status_code=201)
|
|
171
|
+
async def create_record(
|
|
172
|
+
record: RecordCreate,
|
|
173
|
+
service: Annotated[RecordService, Depends(get_record_service)],
|
|
174
|
+
):
|
|
175
|
+
return await service.create_record(record)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Step 5: Application Entry
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
# app/main.py
|
|
182
|
+
from fastapi import FastAPI, Request
|
|
183
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
184
|
+
from fastapi.responses import JSONResponse
|
|
185
|
+
from loguru import logger
|
|
186
|
+
|
|
187
|
+
app = FastAPI(title="API", version="1.0.0")
|
|
188
|
+
|
|
189
|
+
# CORS for Streamlit frontends
|
|
190
|
+
app.add_middleware(
|
|
191
|
+
CORSMiddleware,
|
|
192
|
+
allow_origins=get_settings().allowed_origins,
|
|
193
|
+
allow_credentials=True,
|
|
194
|
+
allow_methods=["*"],
|
|
195
|
+
allow_headers=["*"],
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
app.include_router(records_router)
|
|
199
|
+
|
|
200
|
+
@app.get("/health")
|
|
201
|
+
async def health_check():
|
|
202
|
+
return {"status": "healthy", "service": "API"}
|
|
203
|
+
|
|
204
|
+
@app.exception_handler(Exception)
|
|
205
|
+
async def global_exception_handler(request: Request, exc: Exception):
|
|
206
|
+
logger.exception(f"Unhandled error on {request.url.path}: {exc}")
|
|
207
|
+
return JSONResponse(status_code=500, content={"detail": "An internal error occurred"})
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Step 6: SQL Server Connection
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
# Using pyodbc for MSSQL with Windows Auth
|
|
214
|
+
import pyodbc
|
|
215
|
+
|
|
216
|
+
conn_str = (
|
|
217
|
+
"DRIVER={ODBC Driver 18 for SQL Server};"
|
|
218
|
+
f"SERVER={settings.db_server};"
|
|
219
|
+
f"DATABASE={settings.db_name};"
|
|
220
|
+
"Trusted_Connection=yes;"
|
|
221
|
+
"Encrypt=yes;"
|
|
222
|
+
"TrustServerCertificate=yes;"
|
|
223
|
+
)
|
|
224
|
+
conn = pyodbc.connect(conn_str, timeout=10)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Patterns and Examples
|
|
230
|
+
|
|
231
|
+
### Repository Pattern
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
class RecordRepository:
|
|
235
|
+
def __init__(self, adapter: DatabaseAdapter):
|
|
236
|
+
self._adapter = adapter
|
|
237
|
+
|
|
238
|
+
def get_by_id(self, record_id: UUID) -> dict | None:
|
|
239
|
+
query = "SELECT * FROM AppRecords WHERE CaseID = ?"
|
|
240
|
+
return self._adapter.fetch_one(query, (str(record_id),))
|
|
241
|
+
|
|
242
|
+
def get_paginated(self, page: int, per_page: int, status: str | None) -> tuple[list, int]:
|
|
243
|
+
count_query = "SELECT COUNT(*) FROM AppRecords"
|
|
244
|
+
params = []
|
|
245
|
+
if status:
|
|
246
|
+
count_query += " WHERE Status = ?"
|
|
247
|
+
params.append(status)
|
|
248
|
+
total = self._adapter.fetch_scalar(count_query, params)
|
|
249
|
+
|
|
250
|
+
query = "SELECT * FROM AppRecords"
|
|
251
|
+
if status:
|
|
252
|
+
query += " WHERE Status = ?"
|
|
253
|
+
query += " ORDER BY [Created Date Time] DESC OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"
|
|
254
|
+
params.extend([(page - 1) * per_page, per_page])
|
|
255
|
+
items = self._adapter.fetch_all(query, params)
|
|
256
|
+
return items, total
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Error Handling with loguru
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
from loguru import logger
|
|
263
|
+
|
|
264
|
+
@router.post("/")
|
|
265
|
+
async def create_record(record: RecordCreate, service: ...):
|
|
266
|
+
try:
|
|
267
|
+
result = await service.create_record(record)
|
|
268
|
+
logger.info(f"Created record {result.id} for customer {record.customer_id}")
|
|
269
|
+
return result
|
|
270
|
+
except ValidationError as e:
|
|
271
|
+
logger.warning(f"Validation error: {e}")
|
|
272
|
+
raise HTTPException(status_code=422, detail=str(e))
|
|
273
|
+
except Exception as e:
|
|
274
|
+
logger.exception(f"Failed to create record: {e}")
|
|
275
|
+
raise HTTPException(status_code=500, detail="Internal error")
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Checklist
|
|
281
|
+
|
|
282
|
+
- [ ] Project structure follows standard pattern (routers/models/services/repositories)
|
|
283
|
+
- [ ] Pydantic models use Base/Create/Update/Response hierarchy
|
|
284
|
+
- [ ] Dependency injection chain: adapter -> repo -> service
|
|
285
|
+
- [ ] CORS configured for Streamlit frontend origins
|
|
286
|
+
- [ ] Health check endpoint at `/health`
|
|
287
|
+
- [ ] Global exception handler with loguru logging
|
|
288
|
+
- [ ] SQL queries use parameterized inputs (no f-strings)
|
|
289
|
+
- [ ] Configuration via pydantic-settings and .env file
|
|
290
|
+
- [ ] API key verification on protected endpoints
|
|
291
|
+
- [ ] All endpoints have proper response_model types
|
|
292
|
+
|
|
293
|
+
## Related Resources
|
|
294
|
+
|
|
295
|
+
| Resource | Path |
|
|
296
|
+
|----------|------|
|
|
297
|
+
| FastAPI instructions | `instructions/fastapi.instructions.md` |
|
|
298
|
+
| SQL expert skill | the `sql` skill |
|
|
299
|
+
| Python instructions | `instructions/python.instructions.md` |
|
|
300
|
+
| Security instructions | `instructions/security.instructions.md` |
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: javascript-typescript
|
|
3
|
+
description: JavaScript and TypeScript development with ES6+, Node.js, React, and modern web frameworks. Use for frontend, backend, or full-stack JavaScript/TypeScript projects.
|
|
4
|
+
source: wshobson/agents
|
|
5
|
+
license: MIT
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# JavaScript/TypeScript Development
|
|
9
|
+
|
|
10
|
+
## TypeScript Configuration
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"compilerOptions": {
|
|
15
|
+
"target": "ES2022",
|
|
16
|
+
"module": "ESNext",
|
|
17
|
+
"moduleResolution": "bundler",
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUncheckedIndexedAccess": true,
|
|
20
|
+
"noImplicitOverride": true,
|
|
21
|
+
"skipLibCheck": true,
|
|
22
|
+
"declaration": true,
|
|
23
|
+
"outDir": "./dist"
|
|
24
|
+
},
|
|
25
|
+
"include": ["src/**/*"],
|
|
26
|
+
"exclude": ["node_modules", "dist"]
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Type Patterns
|
|
31
|
+
|
|
32
|
+
### Utility Types
|
|
33
|
+
```typescript
|
|
34
|
+
// Pick specific properties
|
|
35
|
+
type UserPreview = Pick<User, 'id' | 'name'>;
|
|
36
|
+
|
|
37
|
+
// Omit properties
|
|
38
|
+
type CreateUser = Omit<User, 'id' | 'createdAt'>;
|
|
39
|
+
|
|
40
|
+
// Make all properties optional
|
|
41
|
+
type PartialUser = Partial<User>;
|
|
42
|
+
|
|
43
|
+
// Make all properties required
|
|
44
|
+
type RequiredUser = Required<User>;
|
|
45
|
+
|
|
46
|
+
// Extract union types
|
|
47
|
+
type Status = 'pending' | 'active' | 'inactive';
|
|
48
|
+
type ActiveStatus = Extract<Status, 'active' | 'pending'>;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Discriminated Unions
|
|
52
|
+
```typescript
|
|
53
|
+
type Result<T> =
|
|
54
|
+
| { success: true; data: T }
|
|
55
|
+
| { success: false; error: Error };
|
|
56
|
+
|
|
57
|
+
function handleResult<T>(result: Result<T>) {
|
|
58
|
+
if (result.success) {
|
|
59
|
+
console.log(result.data); // T
|
|
60
|
+
} else {
|
|
61
|
+
console.error(result.error); // Error
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Generic Constraints
|
|
67
|
+
```typescript
|
|
68
|
+
interface HasId {
|
|
69
|
+
id: string | number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function findById<T extends HasId>(items: T[], id: T['id']): T | undefined {
|
|
73
|
+
return items.find(item => item.id === id);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Modern JavaScript
|
|
78
|
+
|
|
79
|
+
### Destructuring & Spread
|
|
80
|
+
```javascript
|
|
81
|
+
const { name, ...rest } = user;
|
|
82
|
+
const merged = { ...defaults, ...options };
|
|
83
|
+
const [first, ...others] = items;
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Optional Chaining & Nullish Coalescing
|
|
87
|
+
```javascript
|
|
88
|
+
const city = user?.address?.city ?? 'Unknown';
|
|
89
|
+
const count = data?.items?.length ?? 0;
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Array Methods
|
|
93
|
+
```javascript
|
|
94
|
+
const adults = users.filter(u => u.age >= 18);
|
|
95
|
+
const names = users.map(u => u.name);
|
|
96
|
+
const total = items.reduce((sum, item) => sum + item.price, 0);
|
|
97
|
+
const hasAdmin = users.some(u => u.role === 'admin');
|
|
98
|
+
const allActive = users.every(u => u.active);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## React Patterns
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// Props with children
|
|
105
|
+
interface CardProps {
|
|
106
|
+
title: string;
|
|
107
|
+
children: React.ReactNode;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Event handlers
|
|
111
|
+
interface ButtonProps {
|
|
112
|
+
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Custom hooks
|
|
116
|
+
function useLocalStorage<T>(key: string, initial: T) {
|
|
117
|
+
const [value, setValue] = useState<T>(() => {
|
|
118
|
+
const stored = localStorage.getItem(key);
|
|
119
|
+
return stored ? JSON.parse(stored) : initial;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
124
|
+
}, [key, value]);
|
|
125
|
+
|
|
126
|
+
return [value, setValue] as const;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Node.js Patterns
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// ES Modules
|
|
134
|
+
import { readFile } from 'node:fs/promises';
|
|
135
|
+
import { join } from 'node:path';
|
|
136
|
+
|
|
137
|
+
// Error handling
|
|
138
|
+
process.on('unhandledRejection', (reason) => {
|
|
139
|
+
console.error('Unhandled Rejection:', reason);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
});
|
|
142
|
+
```
|