@caddis/cli 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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/check_doc_coverage.py +1 -1
- 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 +42 -10
- package/bundles/antigravity-plugin/session_end_agy.py +31 -6
- package/bundles/antigravity-plugin/skills/_registry.md +1 -0
- 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/safe-cleanup/SKILL.md +186 -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/antigravity-plugin-extras/skills/_registry.md +1 -0
- package/bundles/antigravity-plugin-extras/skills/safe-cleanup/SKILL.md +186 -0
- 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/safe-cleanup/SKILL.md +186 -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
|
@@ -150,6 +150,63 @@ def _hook_note(feature, exc, root=None):
|
|
|
150
150
|
except Exception:
|
|
151
151
|
pass
|
|
152
152
|
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _injected_marker(root, conversation):
|
|
156
|
+
"""Path of the per-conversation "relay already injected" marker, or None."""
|
|
157
|
+
try:
|
|
158
|
+
safe = "".join(c for c in str(conversation) if c.isalnum() or c in "-_")[:64]
|
|
159
|
+
if not safe:
|
|
160
|
+
return None
|
|
161
|
+
return os.path.join(str(root), ".caddis", ".relay-injected", safe)
|
|
162
|
+
except Exception:
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _already_injected(root, conversation):
|
|
167
|
+
"""True when this conversation has had its relay injected before. Fail-open: on any
|
|
168
|
+
error return False, because a duplicate relay is a smaller harm than none at all."""
|
|
169
|
+
try:
|
|
170
|
+
marker = _injected_marker(root, conversation)
|
|
171
|
+
return bool(marker) and os.path.isfile(marker)
|
|
172
|
+
except Exception:
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _mark_injected(root, conversation):
|
|
177
|
+
"""Record that this conversation received its relay. Never raises.
|
|
178
|
+
|
|
179
|
+
A failure here means the next truncation may re-inject — the pre-2026-08-22 behaviour,
|
|
180
|
+
which is exactly the fail-open direction we want.
|
|
181
|
+
"""
|
|
182
|
+
try:
|
|
183
|
+
marker = _injected_marker(root, conversation)
|
|
184
|
+
if not marker:
|
|
185
|
+
return
|
|
186
|
+
os.makedirs(os.path.dirname(marker), exist_ok=True)
|
|
187
|
+
with open(marker, "w", encoding="utf-8") as fh:
|
|
188
|
+
fh.write("")
|
|
189
|
+
except Exception as _exc:
|
|
190
|
+
_hook_note("agy relay injection marker", _exc, root)
|
|
191
|
+
|
|
192
|
+
def _workspace_roots(data):
|
|
193
|
+
"""Every workspace root in the payload, as a list. Empty when the payload has none.
|
|
194
|
+
|
|
195
|
+
agy sends `workspacePaths` as a LIST. All three caddis agy hooks used to read
|
|
196
|
+
`workspacePaths[0]` and treat it as "the workspace" — silently, with no signal that a
|
|
197
|
+
choice had been made. In a single-root workspace that is correct and the behaviour here
|
|
198
|
+
is unchanged. In a MULTI-root workspace it is a coin toss: the hook acts on the first
|
|
199
|
+
root regardless of which one the session is actually working in.
|
|
200
|
+
|
|
201
|
+
Nothing on this machine is multi-root today, so this has never fired. It is a live trap
|
|
202
|
+
for exactly the multi-repo workspaces the leakage investigation was about, which is why
|
|
203
|
+
each caller below now handles the list rather than assuming its first element.
|
|
204
|
+
"""
|
|
205
|
+
roots = data.get("workspacePaths") if isinstance(data, dict) else None
|
|
206
|
+
if not isinstance(roots, list):
|
|
207
|
+
return []
|
|
208
|
+
return [str(r) for r in roots if isinstance(r, (str, bytes)) and str(r).strip()]
|
|
209
|
+
|
|
153
210
|
def main() -> None:
|
|
154
211
|
try:
|
|
155
212
|
_reconfig = getattr(sys.stdout, "reconfigure", None)
|
|
@@ -173,17 +230,47 @@ def main() -> None:
|
|
|
173
230
|
return
|
|
174
231
|
if is_headless():
|
|
175
232
|
return
|
|
176
|
-
|
|
177
|
-
if not
|
|
233
|
+
roots = _workspace_roots(data)
|
|
234
|
+
if not roots:
|
|
178
235
|
return
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
236
|
+
|
|
237
|
+
# DOUBLE-INJECTION GUARD (2026-08-22). The gate above is `invocationNum == 0`, and
|
|
238
|
+
# agy RESTARTS invocation numbering after a CHECKPOINT truncation. A long session
|
|
239
|
+
# therefore hits invocation 0 a second time mid-conversation and injected the relay
|
|
240
|
+
# again — observed at steps 53 and 56 of one transcript, after a truncation at 50.
|
|
241
|
+
# A relay injected mid-task is worse than noise: it carries a "next step" from a
|
|
242
|
+
# PREVIOUS session, and that has been seen executed in place of the real prompt.
|
|
243
|
+
#
|
|
244
|
+
# Conversation ids are unique per session, so a marker file per id is enough. Stale
|
|
245
|
+
# markers are harmless — they only ever suppress a re-injection into a conversation
|
|
246
|
+
# that already received one.
|
|
247
|
+
conversation = str(data.get("conversationId") or "").strip()
|
|
248
|
+
|
|
249
|
+
blocks = []
|
|
250
|
+
for root in roots:
|
|
251
|
+
relay = resolve_relay(root)
|
|
252
|
+
if not relay:
|
|
253
|
+
continue
|
|
254
|
+
try:
|
|
255
|
+
text = open(relay, encoding="utf-8").read().strip()
|
|
256
|
+
except Exception as _exc:
|
|
257
|
+
_hook_note("agy relay read", _exc, root)
|
|
258
|
+
continue
|
|
259
|
+
if not text:
|
|
260
|
+
continue
|
|
261
|
+
if conversation and _already_injected(root, conversation):
|
|
262
|
+
continue
|
|
263
|
+
# Label the block ONLY when there is more than one root. A single-root session
|
|
264
|
+
# must produce byte-identical output to before this change.
|
|
265
|
+
if len(roots) > 1:
|
|
266
|
+
blocks.append("relay for " + os.path.basename(root.rstrip("/\\")) + ":")
|
|
267
|
+
blocks.append(truncate_relay(text))
|
|
268
|
+
if conversation:
|
|
269
|
+
_mark_injected(root, conversation)
|
|
270
|
+
|
|
271
|
+
if not blocks:
|
|
185
272
|
return
|
|
186
|
-
message = (RELAY_FRAME_HEADER + "\n" +
|
|
273
|
+
message = (RELAY_FRAME_HEADER + "\n" + "\n\n".join(blocks)
|
|
187
274
|
+ "\n\n" + RELAY_FRAME_FOOTER)
|
|
188
275
|
payload = json.dumps({"injectSteps": [{"ephemeralMessage": message}]})
|
|
189
276
|
sys.stdout.write(payload)
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
| Changelog Generator | `changelog-generator/` | Automatically creates user-facing changelogs from git commits by analyzing commit history, categorizing changes, and transforming technical commits into clear, customer-friendly release notes. Turns hours of manual changelog writing into minutes of automated generation. |
|
|
52
52
|
| Deploy Local | `deploy-local/` | End-to-end local deployment loop for Gitea-hosted projects. Use when the user wants to commit on dev, push to remote, monitor the golden CI/build/deploy workflow, validate prod on the configured prod host, and fix lint/test/pipeline failures until deployment is healthy. |
|
|
53
53
|
| Monorepo | `monorepo/` | Monorepo management with Turborepo and pnpm workspaces. Use for Turborepo setup, turbo.json task dependencies, remote caching, pnpm workspace protocol, shared packages (ui-library, config, types), affected-only CI/CD builds, or monorepo structure (apps/ vs packages/). Covers pitfalls like circular deps, version drift, and hoisting issues. |
|
|
54
|
+
| Safe Cleanup | `safe-cleanup/` | Use BEFORE deleting any directory tree, clearing a scratchpad, removing git worktrees, or "tidying up" generated folders - especially on Windows, where a recursive delete that meets a junction or symlink destroys the link's TARGET. Also use when verifying that a Python venv or node_modules survived a cleanup, because pip check and file counts both report healthy on a half-deleted environment. |
|
|
54
55
|
| Update Readme | `update-readme/` | Detect feature commits and update README sections (Features, API, Usage) with accurate repo-aware changes, then stage only README for a docs commit. Use when users add features/routes/components and want README maintained automatically. |
|
|
55
56
|
|
|
56
57
|
### Docs
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: safe-cleanup
|
|
3
|
+
description: Use BEFORE deleting any directory tree, clearing a scratchpad, removing git worktrees, or "tidying up" generated folders - especially on Windows, where a recursive delete that meets a junction or symlink destroys the link's TARGET. Also use when verifying that a Python venv or node_modules survived a cleanup, because pip check and file counts both report healthy on a half-deleted environment.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Safe Cleanup
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Cleanup looks like the safest thing you do all day. It is the most destructive.
|
|
11
|
+
|
|
12
|
+
This skill exists because of a real incident: an agent created 31 directory
|
|
13
|
+
junctions from throwaway worktrees into live repositories, unlinked them, saw
|
|
14
|
+
"31 unlinked, 0 failed", then recursively deleted the parent. A junction it had
|
|
15
|
+
never seen survived the scan, the delete followed it, and **five repositories'
|
|
16
|
+
development environments were destroyed**. Every verification afterwards said
|
|
17
|
+
"intact", because the verification was wrong too.
|
|
18
|
+
|
|
19
|
+
Two hours of repair. None of it would have happened with the rules below.
|
|
20
|
+
|
|
21
|
+
## The one-paragraph version
|
|
22
|
+
|
|
23
|
+
On Windows, `Remove-Item -Recurse` **follows junctions and deletes their
|
|
24
|
+
targets**. Enumerating with `-ErrorAction SilentlyContinue` hides the ones you
|
|
25
|
+
cannot read. And after the damage, `pip check`, `pip list` and file counts all
|
|
26
|
+
report success, because metadata survives while package files do not. So: never
|
|
27
|
+
link into somewhere real, never recurse through a link, prefer rename over
|
|
28
|
+
delete, and verify by *using* the thing rather than counting it.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## RULE 1 - Prefer rename over delete
|
|
33
|
+
|
|
34
|
+
Rename is atomic. Recursive delete is not: it can fail half way and leave a
|
|
35
|
+
directory that looks present and is unusable.
|
|
36
|
+
|
|
37
|
+
```powershell
|
|
38
|
+
Rename-Item -Path $target -NewName ".broken-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
In the incident, a venv holding `_greenlet.cp314-win_amd64.pyd` open **refused to
|
|
42
|
+
delete** but **renamed fine**. Had the delete been retried or forced, it would
|
|
43
|
+
have half-emptied the directory.
|
|
44
|
+
|
|
45
|
+
Rename first, build fresh alongside, delete the old copy later when nothing holds
|
|
46
|
+
it. Disk is cheap. An environment nobody can recover is not.
|
|
47
|
+
|
|
48
|
+
## RULE 2 - Never recurse a delete through a reparse point
|
|
49
|
+
|
|
50
|
+
Junctions and symlinks are transparent to `Remove-Item -Recurse`. Deleting a
|
|
51
|
+
folder that contains a link to `C:\live\thing` deletes **the contents of
|
|
52
|
+
`C:\live\thing`**.
|
|
53
|
+
|
|
54
|
+
Check first, and **fail if the check itself cannot complete**:
|
|
55
|
+
|
|
56
|
+
```powershell
|
|
57
|
+
$links = @(Get-ChildItem $path -Recurse -Force -Directory -ErrorAction Stop |
|
|
58
|
+
Where-Object { $_.Attributes -band [IO.FileAttributes]::ReparsePoint })
|
|
59
|
+
if ($links.Count) {
|
|
60
|
+
$links | ForEach-Object { Write-Host " $($_.FullName) -> $($_.Target)" }
|
|
61
|
+
throw "refusing: $($links.Count) link(s) inside $path"
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`-ErrorAction SilentlyContinue` on that scan is precisely what caused the
|
|
66
|
+
incident. A link inside a directory the scanner could not read was skipped in
|
|
67
|
+
silence and never appeared in the count. **A scan that cannot see everything must
|
|
68
|
+
fail, not shrug.**
|
|
69
|
+
|
|
70
|
+
To remove a link itself, unlink it - never delete through it:
|
|
71
|
+
|
|
72
|
+
```powershell
|
|
73
|
+
[System.IO.Directory]::Delete($linkPath, $false) # link only, never the target
|
|
74
|
+
cmd /c rmdir "$linkPath" # same effect
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## RULE 3 - Do not create links into live directories
|
|
78
|
+
|
|
79
|
+
The safest delete is the one with nothing to follow.
|
|
80
|
+
|
|
81
|
+
If a throwaway workspace needs dependencies, **build them there**, or point the
|
|
82
|
+
tool at an absolute interpreter path. Do not junction `<scratch>\.venv` to
|
|
83
|
+
`<live-repo>\.venv` for convenience - that is a loaded gun aimed at the repo, and
|
|
84
|
+
any cleanup above the link can fire it.
|
|
85
|
+
|
|
86
|
+
If you must link, remove the link the moment you are done, and never leave one
|
|
87
|
+
inside a directory that something else will clean up.
|
|
88
|
+
|
|
89
|
+
## RULE 4 - Verify by using, not by counting
|
|
90
|
+
|
|
91
|
+
After any cleanup near an environment, prove it still works. These all LIE on a
|
|
92
|
+
half-deleted Python venv, because dist-info metadata survives while package files
|
|
93
|
+
are removed:
|
|
94
|
+
|
|
95
|
+
| Check | Why it lies |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `Test-Path .venv\Scripts\python.exe` | Proves the interpreter exists. Says nothing about packages. |
|
|
98
|
+
| file or directory counts | Metadata directories inflate the count. |
|
|
99
|
+
| `pip list` | Reads metadata. |
|
|
100
|
+
| `pip check` | Reads metadata. Reported "No broken requirements found" on a venv that could not import fastapi. |
|
|
101
|
+
| `pip install -e .` | Reads metadata, calls the package satisfied, **skips it, and reports success**. |
|
|
102
|
+
|
|
103
|
+
The honest check is whether the interpreter can import what the project declares:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
import importlib.util
|
|
107
|
+
importlib.util.find_spec("fastapi") is None # -> True means genuinely missing
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
For node, check that a real entry point resolves, not that `node_modules` exists.
|
|
111
|
+
|
|
112
|
+
**Map distribution names to import names.** `pyjwt` imports as `jwt`,
|
|
113
|
+
`python-ulid` as `ulid`, `python-dotenv` as `dotenv`. A naive audit produces false
|
|
114
|
+
positives and will have you rebuilding healthy environments - which is its own way
|
|
115
|
+
of causing an outage.
|
|
116
|
+
|
|
117
|
+
## RULE 5 - Stop what holds the files, or refuse
|
|
118
|
+
|
|
119
|
+
A running service turns a delete into a **partial** delete - the worst outcome.
|
|
120
|
+
|
|
121
|
+
Stop named services first. Then check for surviving processes and **refuse rather
|
|
122
|
+
than force**:
|
|
123
|
+
|
|
124
|
+
```powershell
|
|
125
|
+
$held = @(Get-CimInstance Win32_Process -Filter "Name='python.exe'" |
|
|
126
|
+
Where-Object { $_.ExecutablePath -like "$venv\*" })
|
|
127
|
+
if ($held.Count) { throw "refusing: still held by PID $($held[0].ProcessId)" }
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
In the incident's repair, this refused on one repo because a **VS Code language
|
|
131
|
+
server** had held that venv since the previous day. Not a service, not safe to
|
|
132
|
+
kill, not the script's business. That repo was fixed a different way. **Refusing
|
|
133
|
+
was correct.**
|
|
134
|
+
|
|
135
|
+
## RULE 6 - One agent at a time on a shared resource
|
|
136
|
+
|
|
137
|
+
Two agents installing into the same environment concurrently can produce exactly
|
|
138
|
+
this partial state. Three sessions were live in one repository during the
|
|
139
|
+
incident. Whoever is repairing says so; everyone else keeps out.
|
|
140
|
+
|
|
141
|
+
## RULE 7 - Verify against the source of truth, not the working copy
|
|
142
|
+
|
|
143
|
+
Checkouts go stale. During the same incident a subagent reported a file as absent
|
|
144
|
+
from a configuration, contradicting a change made hours earlier. The file was
|
|
145
|
+
present on `origin/main`; the working copy was **10 commits behind**.
|
|
146
|
+
|
|
147
|
+
Before asserting something is missing, check `git show origin/main:<path>`, not
|
|
148
|
+
the file on disk.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Before you delete anything - the checklist
|
|
153
|
+
|
|
154
|
+
1. Can I **rename** instead? Do that.
|
|
155
|
+
2. Does the tree contain **reparse points**? Scan with `-ErrorAction Stop`. Refuse if any, or if the scan fails.
|
|
156
|
+
3. Does anything **hold files open**? Stop services; refuse on surviving processes.
|
|
157
|
+
4. Did I create any **links into live directories** earlier in this session? Unlink them explicitly, by name.
|
|
158
|
+
5. After: **verify by using**, not by counting.
|
|
159
|
+
6. If something looks wrong: check `origin/main`, not the working copy.
|
|
160
|
+
|
|
161
|
+
## What good verification output looks like
|
|
162
|
+
|
|
163
|
+
Not this:
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
.venv intact: True <- only proves python.exe exists
|
|
167
|
+
31 unlinked, 0 failed <- says nothing about what the scan missed
|
|
168
|
+
No broken requirements found <- reads metadata, not reality
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
This:
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
service-a OK declared=24 missing=0 <- every declared dep imported
|
|
175
|
+
service-b OK declared=19 missing=0
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Reference implementation
|
|
179
|
+
|
|
180
|
+
The estate this skill came from keeps working tools at `.caddis/tools/`:
|
|
181
|
+
|
|
182
|
+
- `venv-audit.py` - imports every dependency each `pyproject.toml` declares
|
|
183
|
+
- `rebuild-venv.ps1` - renames aside, rebuilds, restarts services, refuses on links or live processes
|
|
184
|
+
- `README-venv-safety.md` - the incident record
|
|
185
|
+
|
|
186
|
+
Adapt rather than copy: the rules matter more than the scripts.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Codex CLI configuration example
|
|
2
|
+
# Copy to ~/.codex/config.toml (user-level, all projects)
|
|
3
|
+
# or <project>/.codex/config.toml (project-level; project must be trusted)
|
|
4
|
+
#
|
|
5
|
+
# Add a server interactively: codex mcp add <name> -- <command> [args...]
|
|
6
|
+
# Verify loaded servers: codex mcp list
|
|
7
|
+
|
|
8
|
+
# ── Model (adjust to your OpenAI tier) ───────────────────────────────────────
|
|
9
|
+
# model = "codex-mini-latest"
|
|
10
|
+
|
|
11
|
+
# ── Skills ────────────────────────────────────────────────────────────────────
|
|
12
|
+
# Project skills: <project>/.codex/skills/<category>/<name>/SKILL.md
|
|
13
|
+
# User skills: ~/.codex/skills/<name>/SKILL.md (no category subdir)
|
|
14
|
+
# (Paths probed from codex 0.137 — see docs/analysis/codex-cli-contract.md.
|
|
15
|
+
# .agents/ is codex's PLUGINS root, not a skills root.)
|
|
16
|
+
#
|
|
17
|
+
# Disable a skill without deleting it:
|
|
18
|
+
# [[skills.config]]
|
|
19
|
+
# path = ".codex/skills/devops/windows-deployment"
|
|
20
|
+
# enabled = false
|
|
21
|
+
|
|
22
|
+
# ── junai MCP (pipeline state + project context) ─────────────────────────────
|
|
23
|
+
# OFF by default. The junai MCP server tracks pipeline stage and project state
|
|
24
|
+
# across sessions. Enable when the server is available locally.
|
|
25
|
+
#
|
|
26
|
+
# Prerequisites:
|
|
27
|
+
# pip install junai-mcp (or: uv tool install junai-mcp)
|
|
28
|
+
# codex mcp add junai -- python -m junai_mcp --project-root .
|
|
29
|
+
#
|
|
30
|
+
# Or add the block below manually and run `codex mcp list` to confirm:
|
|
31
|
+
#
|
|
32
|
+
# [[mcp_servers]]
|
|
33
|
+
# name = "junai"
|
|
34
|
+
# command = "python"
|
|
35
|
+
# args = ["-m", "junai_mcp", "--project-root", "."]
|
|
36
|
+
# env = {}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: api-design
|
|
3
|
+
description: REST and GraphQL API design patterns. Use when designing new API endpoints, defining resource schemas, planning versioning strategies, or reviewing API contracts. Covers naming conventions, pagination, filtering, error responses, and OpenAPI documentation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# API Design Skill
|
|
7
|
+
|
|
8
|
+
Design APIs that are consistent, discoverable, and hard to misuse. Conventions over configuration.
|
|
9
|
+
|
|
10
|
+
## 1. When to Apply This Skill
|
|
11
|
+
|
|
12
|
+
**Trigger conditions:**
|
|
13
|
+
- Designing new API endpoints or a full API surface
|
|
14
|
+
- "What should this endpoint look like?"
|
|
15
|
+
- Planning API versioning or migration
|
|
16
|
+
- Reviewing API contracts for consistency
|
|
17
|
+
- Building an API spec or OpenAPI document
|
|
18
|
+
|
|
19
|
+
## 2. REST Resource Naming
|
|
20
|
+
|
|
21
|
+
### URL Convention
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
# Resources are nouns, plural, lowercase, kebab-case
|
|
25
|
+
GET /api/v1/customers # List
|
|
26
|
+
GET /api/v1/customers/{id} # Get one
|
|
27
|
+
POST /api/v1/customers # Create
|
|
28
|
+
PUT /api/v1/customers/{id} # Full update
|
|
29
|
+
PATCH /api/v1/customers/{id} # Partial update
|
|
30
|
+
DELETE /api/v1/customers/{id} # Delete
|
|
31
|
+
|
|
32
|
+
# Nested resources for strong ownership
|
|
33
|
+
GET /api/v1/customers/{id}/orders # Customer's orders
|
|
34
|
+
POST /api/v1/customers/{id}/orders # Create order for customer
|
|
35
|
+
|
|
36
|
+
# Actions (when CRUD doesn't fit) — verb suffix
|
|
37
|
+
POST /api/v1/orders/{id}/cancel # Action on resource
|
|
38
|
+
POST /api/v1/reports/generate # Trigger async operation
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Naming Rules
|
|
42
|
+
|
|
43
|
+
| Rule | Good | Bad |
|
|
44
|
+
|------|------|-----|
|
|
45
|
+
| Plural nouns | `/customers` | `/customer`, `/getCustomers` |
|
|
46
|
+
| Kebab-case | `/order-items` | `/orderItems`, `/order_items` |
|
|
47
|
+
| No verbs in path | `/customers` (POST = create) | `/createCustomer` |
|
|
48
|
+
| Consistent nesting depth | Max 2 levels: `/customers/{id}/orders` | `/customers/{id}/orders/{id}/items/{id}/details` |
|
|
49
|
+
| No trailing slash | `/customers` | `/customers/` |
|
|
50
|
+
|
|
51
|
+
## 3. Request & Response Envelope
|
|
52
|
+
|
|
53
|
+
### Success Response
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"data": { ... },
|
|
58
|
+
"meta": {
|
|
59
|
+
"request_id": "req_abc123",
|
|
60
|
+
"timestamp": "2025-01-15T10:30:00Z"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### List Response (with pagination)
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"data": [ ... ],
|
|
70
|
+
"pagination": {
|
|
71
|
+
"page": 1,
|
|
72
|
+
"per_page": 25,
|
|
73
|
+
"total": 142,
|
|
74
|
+
"total_pages": 6
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Error Response
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"error": {
|
|
84
|
+
"code": "VALIDATION_ERROR",
|
|
85
|
+
"message": "Request validation failed",
|
|
86
|
+
"details": [
|
|
87
|
+
{ "field": "email", "message": "Invalid email format" },
|
|
88
|
+
{ "field": "age", "message": "Must be at least 18" }
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 4. HTTP Status Codes
|
|
95
|
+
|
|
96
|
+
Use the right code. Clients depend on these for control flow.
|
|
97
|
+
|
|
98
|
+
| Code | When to Use |
|
|
99
|
+
|------|-------------|
|
|
100
|
+
| `200 OK` | Successful GET, PUT, PATCH |
|
|
101
|
+
| `201 Created` | Successful POST that creates a resource |
|
|
102
|
+
| `204 No Content` | Successful DELETE |
|
|
103
|
+
| `400 Bad Request` | Malformed request body or parameters |
|
|
104
|
+
| `401 Unauthorized` | Missing or invalid authentication |
|
|
105
|
+
| `403 Forbidden` | Authenticated but not authorized |
|
|
106
|
+
| `404 Not Found` | Resource doesn't exist |
|
|
107
|
+
| `409 Conflict` | Duplicate resource, version conflict |
|
|
108
|
+
| `422 Unprocessable Entity` | Valid syntax but semantic validation failed |
|
|
109
|
+
| `429 Too Many Requests` | Rate limit exceeded (include `Retry-After` header) |
|
|
110
|
+
| `500 Internal Server Error` | Unhandled server error |
|
|
111
|
+
|
|
112
|
+
## 5. Pagination
|
|
113
|
+
|
|
114
|
+
### Offset-based (simple, good for most cases)
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
GET /api/v1/customers?page=2&per_page=25
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Cursor-based (better for real-time data, large datasets)
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
GET /api/v1/events?cursor=eyJpZCI6MTAwfQ&limit=25
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
| Strategy | Pros | Cons |
|
|
127
|
+
|----------|------|------|
|
|
128
|
+
| Offset | Simple, supports jumping to page N | Skips/duplicates on mutations, slow at high offsets |
|
|
129
|
+
| Cursor | Consistent with mutations, performant | No "jump to page 5", harder to implement |
|
|
130
|
+
|
|
131
|
+
**Rule:** Use offset for admin/dashboard UIs. Use cursor for feeds, events, and public APIs.
|
|
132
|
+
|
|
133
|
+
## 6. Filtering, Sorting, and Search
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
# Filtering — field-level with operators
|
|
137
|
+
GET /api/v1/orders?status=open&created_after=2025-01-01
|
|
138
|
+
|
|
139
|
+
# Sorting — field with direction prefix
|
|
140
|
+
GET /api/v1/orders?sort=-created_at # descending
|
|
141
|
+
GET /api/v1/orders?sort=status,created_at # multi-field
|
|
142
|
+
|
|
143
|
+
# Search — dedicated parameter
|
|
144
|
+
GET /api/v1/customers?q=acme+corp
|
|
145
|
+
|
|
146
|
+
# Field selection — sparse fieldsets
|
|
147
|
+
GET /api/v1/customers?fields=id,name,email
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## 7. Versioning
|
|
151
|
+
|
|
152
|
+
### URL Path versioning (recommended)
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
/api/v1/customers
|
|
156
|
+
/api/v2/customers
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Version lifecycle
|
|
160
|
+
|
|
161
|
+
| Phase | Duration | Behaviour |
|
|
162
|
+
|-------|----------|-----------|
|
|
163
|
+
| **Current** | Indefinite | Actively maintained |
|
|
164
|
+
| **Deprecated** | 6-12 months | Returns `Deprecation` header, still works |
|
|
165
|
+
| **Sunset** | After deprecation | Returns `410 Gone` |
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
# FastAPI deprecation header
|
|
169
|
+
@app.get("/api/v1/old-endpoint", deprecated=True)
|
|
170
|
+
async def old_endpoint(response: Response):
|
|
171
|
+
response.headers["Deprecation"] = "true"
|
|
172
|
+
response.headers["Sunset"] = "2026-06-01"
|
|
173
|
+
response.headers["Link"] = '</api/v2/new-endpoint>; rel="successor-version"'
|
|
174
|
+
return {"data": ...}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## 8. Authentication & Security
|
|
178
|
+
|
|
179
|
+
- Use `Authorization: Bearer <token>` for API auth
|
|
180
|
+
- Never put tokens in URL query parameters (they appear in logs)
|
|
181
|
+
- Return `401` for missing/invalid auth, `403` for insufficient permissions
|
|
182
|
+
- Include `X-Request-Id` header for tracing
|
|
183
|
+
- Rate limit all public endpoints
|
|
184
|
+
|
|
185
|
+
## 9. FastAPI Implementation Pattern
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from fastapi import APIRouter, Depends, Query, HTTPException, status
|
|
189
|
+
from pydantic import BaseModel, Field
|
|
190
|
+
|
|
191
|
+
router = APIRouter(prefix="/api/v1/customers", tags=["customers"])
|
|
192
|
+
|
|
193
|
+
class CustomerCreate(BaseModel):
|
|
194
|
+
name: str = Field(..., min_length=1, max_length=200)
|
|
195
|
+
email: str = Field(..., pattern=r"^[\w.-]+@[\w.-]+\.\w+$")
|
|
196
|
+
|
|
197
|
+
class CustomerResponse(BaseModel):
|
|
198
|
+
id: str
|
|
199
|
+
name: str
|
|
200
|
+
email: str
|
|
201
|
+
|
|
202
|
+
class PaginatedResponse(BaseModel):
|
|
203
|
+
data: list[CustomerResponse]
|
|
204
|
+
pagination: dict
|
|
205
|
+
|
|
206
|
+
@router.get("", response_model=PaginatedResponse)
|
|
207
|
+
async def list_customers(
|
|
208
|
+
page: int = Query(1, ge=1),
|
|
209
|
+
per_page: int = Query(25, ge=1, le=100),
|
|
210
|
+
):
|
|
211
|
+
...
|
|
212
|
+
|
|
213
|
+
@router.post("", response_model=CustomerResponse, status_code=status.HTTP_201_CREATED)
|
|
214
|
+
async def create_customer(body: CustomerCreate):
|
|
215
|
+
...
|
|
216
|
+
|
|
217
|
+
@router.get("/{customer_id}", response_model=CustomerResponse)
|
|
218
|
+
async def get_customer(customer_id: str):
|
|
219
|
+
customer = await repo.find(customer_id)
|
|
220
|
+
if not customer:
|
|
221
|
+
raise HTTPException(status_code=404, detail="Customer not found")
|
|
222
|
+
return customer
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## 10. API Design Checklist
|
|
226
|
+
|
|
227
|
+
Before shipping an API endpoint:
|
|
228
|
+
|
|
229
|
+
- [ ] Resource names are plural nouns in kebab-case
|
|
230
|
+
- [ ] Correct HTTP methods (GET reads, POST creates, etc.)
|
|
231
|
+
- [ ] Correct status codes (201 for create, 204 for delete)
|
|
232
|
+
- [ ] Request body validated with Pydantic/Zod
|
|
233
|
+
- [ ] Error responses use consistent envelope with error code
|
|
234
|
+
- [ ] Pagination implemented for list endpoints
|
|
235
|
+
- [ ] Authentication required (unless intentionally public)
|
|
236
|
+
- [ ] Rate limiting configured
|
|
237
|
+
- [ ] OpenAPI spec generated and reviewed
|
|
238
|
+
- [ ] No sensitive data in URLs or logs
|
|
239
|
+
|
|
240
|
+
## 11. Anti-Patterns
|
|
241
|
+
|
|
242
|
+
| Anti-Pattern | Problem | Fix |
|
|
243
|
+
|---|---|---|
|
|
244
|
+
| Verbs in URLs | `/getUser`, `/deleteOrder` | Use HTTP methods + nouns |
|
|
245
|
+
| Inconsistent pluralisation | `/user` vs `/orders` | Always plural |
|
|
246
|
+
| Returning 200 for errors | Client can't distinguish success/failure | Use proper status codes |
|
|
247
|
+
| Nested resources > 2 levels | Hard to discover, fragile URLs | Flatten with query params |
|
|
248
|
+
| No pagination on lists | Unbounded response size, OOM | Always paginate |
|
|
249
|
+
| Exposing internal IDs/schemas | Coupling clients to implementation | Use public-facing schemas |
|