@caddis/cli 0.2.6 → 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/claude-md-curator.md +1 -1
- package/bundles/antigravity-plugin/agents/knowledge-transfer.md +17 -32
- package/bundles/antigravity-plugin/caddis_statusline.py +825 -0
- package/bundles/antigravity-plugin/claude-md/folder-agents.md.tmpl +21 -0
- package/bundles/antigravity-plugin/guard_agy.py +60 -9
- package/bundles/antigravity-plugin/plugin.json +1 -1
- package/bundles/antigravity-plugin/scripts/caddis_gate.py +524 -0
- package/bundles/antigravity-plugin/scripts/caddis_inventory.py +439 -0
- package/bundles/antigravity-plugin/scripts/caddis_migrate_dir.py +270 -0
- package/bundles/antigravity-plugin/scripts/caddis_spawn.py +300 -0
- package/bundles/antigravity-plugin/scripts/caddis_statusline.py +825 -0
- package/bundles/antigravity-plugin/scripts/caddis_tidy.py +366 -0
- package/bundles/antigravity-plugin/scripts/check_doc_coverage.py +924 -0
- package/bundles/antigravity-plugin/scripts/claude-deepseek.ps1 +4 -0
- package/bundles/antigravity-plugin/scripts/claude-deepseek.sh +3 -0
- package/bundles/antigravity-plugin/scripts/claude-glm.ps1 +4 -0
- package/bundles/antigravity-plugin/scripts/claude-glm.sh +3 -0
- package/bundles/antigravity-plugin/scripts/claude-oss.ps1 +64 -0
- package/bundles/antigravity-plugin/scripts/claude-oss.sh +60 -0
- package/bundles/antigravity-plugin/scripts/claudster_config.py +121 -0
- package/bundles/antigravity-plugin/scripts/claudster_doctor.py +356 -0
- package/bundles/antigravity-plugin/scripts/claudster_init.py +400 -0
- package/bundles/antigravity-plugin/scripts/claudster_migrate_rules.py +300 -0
- package/bundles/antigravity-plugin/scripts/enforcement_inventory.py +261 -0
- package/bundles/antigravity-plugin/scripts/hook_log.py +156 -0
- package/bundles/antigravity-plugin/scripts/new-machine.ps1 +170 -0
- package/bundles/antigravity-plugin/scripts/oss_ask.py +98 -0
- package/bundles/antigravity-plugin/scripts/oss_model.py +174 -0
- package/bundles/antigravity-plugin/scripts/oss_review.py +718 -0
- package/bundles/antigravity-plugin/scripts/setup_project_ai.py +1432 -0
- package/bundles/antigravity-plugin/scripts/usage_review.py +1323 -0
- package/bundles/antigravity-plugin/session_end_agy.py +52 -8
- 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/fastapi-dev/SKILL.md +1 -1
- 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/ship/SKILL.md +1 -1
- package/bundles/antigravity-plugin/skills/ship-merge/SKILL.md +1 -1
- package/bundles/antigravity-plugin/skills/ship-pr/SKILL.md +1 -1
- package/bundles/antigravity-plugin/skills/statusline/SKILL.md +105 -0
- package/bundles/antigravity-plugin/skills/use-model/SKILL.md +15 -3
- package/bundles/antigravity-plugin/skills/validate-phase/SKILL.md +31 -0
- package/bundles/antigravity-plugin/warm_start_agy.py +117 -11
- package/bundles/antigravity-plugin-extras/plugin.json +1 -1
- package/bundles/antigravity-plugin-extras/skills/architecture-design/SKILL.md +1 -1
- package/bundles/antigravity-plugin-extras/skills/caching-patterns/SKILL.md +1 -1
- package/bundles/antigravity-plugin-extras/skills/webapp-development/SKILL.md +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 +544 -0
- package/dist/claude-glm.js +544 -0
- package/dist/claude-oss.js +544 -0
- package/dist/cli.js +542 -90
- package/package.json +5 -2
- package/bundles/antigravity-plugin/statusline-command-agy.sh +0 -91
- 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,156 @@
|
|
|
1
|
+
"""Throttled error ledger for caddis hooks — so a broken hook is not a silent one.
|
|
2
|
+
|
|
3
|
+
The problem
|
|
4
|
+
-----------
|
|
5
|
+
caddis hooks fail open, which is right: a SessionStart hook that raises would break
|
|
6
|
+
the session, and a PreToolUse hook that raises would break the tool call. The way
|
|
7
|
+
they fail open is ``except Exception: pass``, which is wrong: a hook that is broken
|
|
8
|
+
and a hook that had nothing to say produce the identical result — nothing.
|
|
9
|
+
|
|
10
|
+
There were 20 such swallows across 6 hook files. Each one guards a named feature.
|
|
11
|
+
When the tidy nudge stops printing, nobody learns that it stopped; they
|
|
12
|
+
learn that they have not seen it lately, months later, if at all.
|
|
13
|
+
|
|
14
|
+
The fix keeps fail-open and adds a record. ``record()`` never raises, never prints,
|
|
15
|
+
and never blocks — if the ledger itself cannot be written, that is swallowed too.
|
|
16
|
+
|
|
17
|
+
Throttle
|
|
18
|
+
--------
|
|
19
|
+
One line per ``(hook, feature, error type)`` per day. A hook that fails on every
|
|
20
|
+
single tool call writes one line, not thousands. The ledger is a signal, not a trace.
|
|
21
|
+
|
|
22
|
+
Surfacing
|
|
23
|
+
---------
|
|
24
|
+
``summarise()`` returns a one-line summary of the last N days, or "" when clean.
|
|
25
|
+
``inject_relay.py`` prints it at SessionStart. That is the only place a user is
|
|
26
|
+
told; there is no second channel to ignore.
|
|
27
|
+
"""
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import json
|
|
31
|
+
import os
|
|
32
|
+
import time
|
|
33
|
+
|
|
34
|
+
_ARTIFACT_DIR = ".caddis"
|
|
35
|
+
_LEDGER_NAME = "hook-errors.jsonl"
|
|
36
|
+
_MAX_LINES = 400 # ring-buffer cap; the ledger must never grow without bound
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _ledger_path(root: str | None) -> str | None:
|
|
40
|
+
try:
|
|
41
|
+
base = root or os.getcwd()
|
|
42
|
+
d = os.path.join(base, _ARTIFACT_DIR)
|
|
43
|
+
if not os.path.isdir(d):
|
|
44
|
+
return None # not a caddis project — nothing to write into, and that is fine
|
|
45
|
+
return os.path.join(d, _LEDGER_NAME)
|
|
46
|
+
except Exception:
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def record(hook: str, feature: str, exc: BaseException, root: str | None = None) -> None:
|
|
51
|
+
"""Note that ``feature`` inside ``hook`` failed. Never raises, never prints.
|
|
52
|
+
|
|
53
|
+
``feature`` is the thing the user loses — "parked-stack surface", "relay read" —
|
|
54
|
+
not the function name. The ledger is read by a human deciding whether to care.
|
|
55
|
+
"""
|
|
56
|
+
try:
|
|
57
|
+
path = _ledger_path(root)
|
|
58
|
+
if path is None:
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
etype = type(exc).__name__
|
|
62
|
+
# Truncate: an exception message can carry a path, a command, or a chunk of
|
|
63
|
+
# file content. One line of context is enough to act on.
|
|
64
|
+
emsg = str(exc)[:200]
|
|
65
|
+
today = time.strftime("%Y-%m-%d")
|
|
66
|
+
key = f"{hook}|{feature}|{etype}"
|
|
67
|
+
|
|
68
|
+
lines: list[str] = []
|
|
69
|
+
if os.path.isfile(path):
|
|
70
|
+
try:
|
|
71
|
+
with open(path, "r", encoding="utf-8", errors="replace") as fh:
|
|
72
|
+
lines = fh.read().splitlines()
|
|
73
|
+
except Exception:
|
|
74
|
+
lines = []
|
|
75
|
+
|
|
76
|
+
for line in reversed(lines[-120:]): # today's entries are at the tail
|
|
77
|
+
try:
|
|
78
|
+
rec = json.loads(line)
|
|
79
|
+
except Exception:
|
|
80
|
+
continue
|
|
81
|
+
if rec.get("key") == key and str(rec.get("ts", ""))[:10] == today:
|
|
82
|
+
return # already recorded today — throttled
|
|
83
|
+
|
|
84
|
+
lines.append(json.dumps({
|
|
85
|
+
"ts": time.strftime("%Y-%m-%dT%H:%M:%S"),
|
|
86
|
+
"key": key,
|
|
87
|
+
"hook": hook,
|
|
88
|
+
"feature": feature,
|
|
89
|
+
"error": etype,
|
|
90
|
+
"message": emsg,
|
|
91
|
+
}, ensure_ascii=False))
|
|
92
|
+
|
|
93
|
+
with open(path, "w", encoding="utf-8", newline="\n") as fh:
|
|
94
|
+
fh.write("\n".join(lines[-_MAX_LINES:]) + "\n")
|
|
95
|
+
except Exception:
|
|
96
|
+
return # the ledger must never be the thing that breaks a hook
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def summarise(root: str | None = None, days: int = 7) -> str:
|
|
100
|
+
"""One line naming what has been failing, or "" when there is nothing to say."""
|
|
101
|
+
try:
|
|
102
|
+
path = _ledger_path(root)
|
|
103
|
+
if path is None or not os.path.isfile(path):
|
|
104
|
+
return ""
|
|
105
|
+
|
|
106
|
+
cutoff = time.strftime("%Y-%m-%d", time.localtime(time.time() - days * 86400))
|
|
107
|
+
counts: dict[str, int] = {}
|
|
108
|
+
with open(path, "r", encoding="utf-8", errors="replace") as fh:
|
|
109
|
+
for line in fh:
|
|
110
|
+
line = line.strip()
|
|
111
|
+
if not line:
|
|
112
|
+
continue
|
|
113
|
+
try:
|
|
114
|
+
rec = json.loads(line)
|
|
115
|
+
except Exception:
|
|
116
|
+
continue
|
|
117
|
+
if str(rec.get("ts", ""))[:10] < cutoff:
|
|
118
|
+
continue
|
|
119
|
+
label = f"{rec.get('hook', '?')}/{rec.get('feature', '?')}"
|
|
120
|
+
counts[label] = counts.get(label, 0) + 1
|
|
121
|
+
|
|
122
|
+
if not counts:
|
|
123
|
+
return ""
|
|
124
|
+
ranked = sorted(counts.items(), key=lambda kv: (-kv[1], kv[0]))
|
|
125
|
+
shown = ", ".join(f"{k} x{v}" for k, v in ranked[:4])
|
|
126
|
+
more = f" (+{len(ranked) - 4} more)" if len(ranked) > 4 else ""
|
|
127
|
+
return (f"[caddis] hook errors in the last {days} days: {shown}{more} — "
|
|
128
|
+
f"see {_ARTIFACT_DIR}/{_LEDGER_NAME}")
|
|
129
|
+
except Exception:
|
|
130
|
+
return ""
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def main() -> int:
|
|
134
|
+
"""``python hook_log.py [root]`` prints the ledger, newest last. For eyeballing."""
|
|
135
|
+
import sys
|
|
136
|
+
root = sys.argv[1] if len(sys.argv) > 1 else os.getcwd()
|
|
137
|
+
path = _ledger_path(root)
|
|
138
|
+
if path is None or not os.path.isfile(path):
|
|
139
|
+
print(f"no hook-error ledger at {root}/{_ARTIFACT_DIR}/{_LEDGER_NAME} — nothing has failed")
|
|
140
|
+
return 0
|
|
141
|
+
with open(path, "r", encoding="utf-8", errors="replace") as fh:
|
|
142
|
+
for line in fh:
|
|
143
|
+
try:
|
|
144
|
+
rec = json.loads(line)
|
|
145
|
+
except Exception:
|
|
146
|
+
continue
|
|
147
|
+
print(f"{rec.get('ts', '?'):<20} {rec.get('hook', '?')}/{rec.get('feature', '?'):<28} "
|
|
148
|
+
f"{rec.get('error', '?')}: {rec.get('message', '')}")
|
|
149
|
+
summary = summarise(root)
|
|
150
|
+
if summary:
|
|
151
|
+
print("\n" + summary)
|
|
152
|
+
return 0
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
if __name__ == "__main__":
|
|
156
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Set up a new Windows machine with the caddis toolchain, in the right order.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
The ORDER matters, and it is the only thing this script really encodes:
|
|
7
|
+
|
|
8
|
+
1. Agents first. caddis is a META-installer — `caddis init` detects which agents are
|
|
9
|
+
present and installs into each. An agent installed AFTER it is simply not seen,
|
|
10
|
+
and you get a silent no-op rather than an error.
|
|
11
|
+
|
|
12
|
+
Note the rule is about `caddis init`, NOT about installing the CLI. The npm package
|
|
13
|
+
has no postinstall step; it only puts binaries on PATH. Installing it early is safe.
|
|
14
|
+
2. caddis CLI second, which brings the model-lane binaries with it.
|
|
15
|
+
3. Keys and status line last. Both depend on the CLI being on PATH.
|
|
16
|
+
|
|
17
|
+
Fetch this script on a machine that has no caddis yet (the SOURCE repo is private; the
|
|
18
|
+
public mirror is what a new machine can reach):
|
|
19
|
+
|
|
20
|
+
irm https://raw.githubusercontent.com/saajunaid/caddis-plugin/main/plugin/scripts/new-machine.ps1 -OutFile $env:TEMP
|
|
21
|
+
ew-machine.ps1
|
|
22
|
+
|
|
23
|
+
Everything is idempotent: re-running skips what is already correct.
|
|
24
|
+
|
|
25
|
+
This script NEVER installs an agent for you. Each vendor's installer is theirs, changes
|
|
26
|
+
without notice, and may need an interactive login. It checks, reports, and tells you the
|
|
27
|
+
one command to run — then verifies afterwards.
|
|
28
|
+
|
|
29
|
+
.PARAMETER SkipKeys
|
|
30
|
+
Do not run `caddis keys` (which prompts). Use on a machine that only needs Claude.
|
|
31
|
+
|
|
32
|
+
.PARAMETER WhatIf
|
|
33
|
+
Report what would happen; change nothing.
|
|
34
|
+
|
|
35
|
+
.EXAMPLE
|
|
36
|
+
.\new-machine.ps1
|
|
37
|
+
.\new-machine.ps1 -SkipKeys
|
|
38
|
+
#>
|
|
39
|
+
[CmdletBinding(SupportsShouldProcess)]
|
|
40
|
+
param(
|
|
41
|
+
[switch]$SkipKeys
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
$ErrorActionPreference = 'Stop'
|
|
45
|
+
$script:Problems = @()
|
|
46
|
+
|
|
47
|
+
function Step($n, $text) { Write-Host "`n[$n] $text" -ForegroundColor Cyan }
|
|
48
|
+
function Ok($text) { Write-Host " OK $text" -ForegroundColor Green }
|
|
49
|
+
function Missing($text) { Write-Host " TODO $text" -ForegroundColor Yellow }
|
|
50
|
+
function Bad($text) { Write-Host " FAIL $text" -ForegroundColor Red; $script:Problems += $text }
|
|
51
|
+
function Note($text) { Write-Host " $text" -ForegroundColor DarkGray }
|
|
52
|
+
|
|
53
|
+
function Test-Bin($name) { $null -ne (Get-Command $name -ErrorAction SilentlyContinue) }
|
|
54
|
+
|
|
55
|
+
# ── 0. prerequisites ────────────────────────────────────────────────────────────────────
|
|
56
|
+
Step 0 'Prerequisites'
|
|
57
|
+
|
|
58
|
+
foreach ($p in @(
|
|
59
|
+
@{ bin = 'node'; why = 'runs the caddis CLI and every agent installer'; get = 'winget install OpenJS.NodeJS.LTS' },
|
|
60
|
+
@{ bin = 'python'; why = 'runs the caddis hooks and the status line'; get = 'winget install Python.Python.3.12' },
|
|
61
|
+
@{ bin = 'git'; why = 'the status line and most commands read git state'; get = 'winget install Git.Git' }
|
|
62
|
+
)) {
|
|
63
|
+
if (Test-Bin $p.bin) { Ok "$($p.bin) — $($p.why)" }
|
|
64
|
+
else { Bad "$($p.bin) is missing — $($p.why). Install: $($p.get)" }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if ($script:Problems.Count -gt 0) {
|
|
68
|
+
Write-Host "`nStopping: the prerequisites above are required before anything else works." -ForegroundColor Red
|
|
69
|
+
exit 1
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# ── 1. the agents — BEFORE caddis, or caddis cannot see them ────────────────────────────
|
|
73
|
+
Step 1 'Agents (install these BEFORE caddis)'
|
|
74
|
+
Note 'caddis installs INTO an agent. One added later needs a re-run of `caddis init`.'
|
|
75
|
+
|
|
76
|
+
$agents = @(
|
|
77
|
+
@{ bin = 'claude'; name = 'Claude Code'; get = 'npm i -g @anthropic-ai/claude-code'; login = 'claude (log in on first run)' },
|
|
78
|
+
@{ bin = 'agy'; name = 'Antigravity (agy)'; get = 'download from antigravity.google'; login = 'agy (log in on first run)' },
|
|
79
|
+
@{ bin = 'codex'; name = 'Codex'; get = 'npm i -g @openai/codex'; login = 'codex login (INTERACTIVE — a script cannot do this)' }
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
$present = @()
|
|
83
|
+
foreach ($a in $agents) {
|
|
84
|
+
if (Test-Bin $a.bin) {
|
|
85
|
+
$ver = (& $a.bin --version 2>&1 | Select-Object -First 1)
|
|
86
|
+
Ok "$($a.name) — $ver"
|
|
87
|
+
$present += $a.name
|
|
88
|
+
} else {
|
|
89
|
+
Missing "$($a.name) not found. Install: $($a.get)"
|
|
90
|
+
Note "then: $($a.login)"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if ($present.Count -eq 0) {
|
|
95
|
+
Write-Host "`nStopping: no agents found, so caddis would install into nothing." -ForegroundColor Red
|
|
96
|
+
exit 1
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# ── 2. caddis CLI ───────────────────────────────────────────────────────────────────────
|
|
100
|
+
Step 2 'caddis CLI'
|
|
101
|
+
Note 'Brings claude-oss / claude-glm / claude-deepseek onto PATH as well — no profile edit.'
|
|
102
|
+
|
|
103
|
+
if (Test-Bin 'caddis') {
|
|
104
|
+
Ok "caddis CLI present — $(caddis --version 2>&1 | Select-Object -First 1)"
|
|
105
|
+
} elseif ($PSCmdlet.ShouldProcess('npm', 'install -g @caddis/cli')) {
|
|
106
|
+
npm i -g '@caddis/cli'
|
|
107
|
+
if (Test-Bin 'caddis') { Ok 'caddis CLI installed' } else { Bad 'npm reported success but `caddis` is not on PATH — open a new shell and re-run' ; exit 1 }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# ── 3. drive each agent ─────────────────────────────────────────────────────────────────
|
|
111
|
+
Step 3 'Install caddis into every detected agent'
|
|
112
|
+
if ($PSCmdlet.ShouldProcess('agents', 'caddis init')) {
|
|
113
|
+
caddis init
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# ── 4. provider keys ────────────────────────────────────────────────────────────────────
|
|
117
|
+
Step 4 'Provider API keys (GLM / DeepSeek)'
|
|
118
|
+
if ($SkipKeys) {
|
|
119
|
+
Note 'Skipped (-SkipKeys). Run `caddis keys` later to enable the OSS model lanes.'
|
|
120
|
+
} elseif ($PSCmdlet.ShouldProcess('~/.caddis/keys.env', 'caddis keys')) {
|
|
121
|
+
Note 'Prompts per provider, validates against the live endpoint, never prints a key.'
|
|
122
|
+
caddis keys
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
# ── 5. status line ──────────────────────────────────────────────────────────────────────
|
|
126
|
+
Step 5 'Status line (Claude Code + agy, user scope, once per machine)'
|
|
127
|
+
$statusline = Join-Path $HOME '.caddis\statusline.py'
|
|
128
|
+
if (Test-Path $statusline) {
|
|
129
|
+
if ($PSCmdlet.ShouldProcess($statusline, 'install')) { python $statusline --install }
|
|
130
|
+
} else {
|
|
131
|
+
Missing "statusline.py not found at $statusline"
|
|
132
|
+
Note 'It ships with the plugin. Run `/caddis:statusline` in a Claude Code session, or'
|
|
133
|
+
Note 'python PLUGIN_ROOT/scripts/caddis_statusline.py --install'
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# ── 6. verify ───────────────────────────────────────────────────────────────────────────
|
|
137
|
+
Step 6 'Verify'
|
|
138
|
+
|
|
139
|
+
caddis status
|
|
140
|
+
|
|
141
|
+
if (Test-Path $statusline) {
|
|
142
|
+
python $statusline --check
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (-not $SkipKeys -and (Test-Bin 'caddis')) {
|
|
146
|
+
Note 'Validating keys against the live endpoints...'
|
|
147
|
+
caddis keys --check
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
Write-Host "`n-- Per repo, not per machine --" -ForegroundColor Cyan
|
|
151
|
+
Note 'In each project, once: /caddis:setup-project-ai'
|
|
152
|
+
Note 'The status line is deliberately NOT per-repo — one renderer serves every project.'
|
|
153
|
+
|
|
154
|
+
Write-Host "`n-- Known gotchas on this platform --" -ForegroundColor Cyan
|
|
155
|
+
Note 'codex headless: pass -c model_reasoning_effort=low. The default `high` can exceed 300s'
|
|
156
|
+
Note ' on a trivial call.'
|
|
157
|
+
Note '`codex doctor` is NOT an auth check — it validates the auth FILE, not the token, and'
|
|
158
|
+
Note ' reports healthy against a dead one. Real check:'
|
|
159
|
+
Note ' codex exec --skip-git-repo-check -s read-only "Reply OK"'
|
|
160
|
+
Note 'agy statusLine is NOT parsed through a shell, so its path must be UNQUOTED. --install'
|
|
161
|
+
Note ' handles this; if you wire it by hand, do not add quotes.'
|
|
162
|
+
Note 'Codex has NO status line and cannot have one — no status_line key in config.toml and'
|
|
163
|
+
Note ' nothing in `codex --help` (measured on codex-cli 0.150.1). Upstream limit,'
|
|
164
|
+
Note ' not a caddis gap. Two profiles exist because two hosts can call them.'
|
|
165
|
+
|
|
166
|
+
if ($script:Problems.Count -gt 0) {
|
|
167
|
+
Write-Host "`n$($script:Problems.Count) problem(s) above." -ForegroundColor Red
|
|
168
|
+
exit 1
|
|
169
|
+
}
|
|
170
|
+
Write-Host "`nDone." -ForegroundColor Green
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""One-shot question to an OSS provider (DeepSeek/GLM/OpenRouter) — plain chat, no tools.
|
|
3
|
+
|
|
4
|
+
Backs the /ask-glm and /ask-deepseek commands: a fast second-opinion answer from a
|
|
5
|
+
different model family, without spinning up a full Claude Code session. Speaks the
|
|
6
|
+
providers' OpenAI-compatible ``/chat/completions`` dialect (NOT the Anthropic endpoints
|
|
7
|
+
oss_model.py hands to Claude Code), and reuses oss_model's key resolution so one entry
|
|
8
|
+
in ~/.caddis/keys.env lights up every lane.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
python oss_ask.py <provider> <prompt...> [--model M] [--base-url U] [--system S]
|
|
12
|
+
|
|
13
|
+
Exit codes: 0 answer printed; 2 endpoint/parse error; 3 config error (message on stderr).
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import json
|
|
19
|
+
import sys
|
|
20
|
+
import urllib.error
|
|
21
|
+
import urllib.request
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
25
|
+
import oss_model # noqa: E402 (same-directory import: shared key resolution)
|
|
26
|
+
|
|
27
|
+
# OpenAI-compatible chat endpoints — intentionally different from oss_model.PROVIDERS,
|
|
28
|
+
# which holds the Anthropic-protocol endpoints for Claude Code.
|
|
29
|
+
CHAT_PROVIDERS: dict[str, dict[str, str]] = {
|
|
30
|
+
"deepseek": {"base_url": "https://api.deepseek.com", "model": "deepseek-v4-flash", "key_env": "DEEPSEEK_API_KEY"},
|
|
31
|
+
"glm": {"base_url": "https://api.z.ai/api/coding/paas/v4", "model": "glm-5.3", "key_env": "GLM_API_KEY"},
|
|
32
|
+
"openrouter": {"base_url": "https://openrouter.ai/api/v1", "model": "deepseek/deepseek-v4-flash", "key_env": "OPENROUTER_API_KEY"},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
EXIT_OK = 0
|
|
36
|
+
EXIT_ERROR = 2
|
|
37
|
+
EXIT_CONFIG = 3
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def resolve_chat_config(provider: str, env: dict[str, str],
|
|
41
|
+
model: str | None = None, base_url: str | None = None) -> dict[str, str]:
|
|
42
|
+
"""{base_url, model, api_key} for the chat lane. Flag > preset; key via oss_model."""
|
|
43
|
+
preset = CHAT_PROVIDERS.get(provider.strip().lower(), {})
|
|
44
|
+
resolved_base = (base_url or preset.get("base_url") or "").rstrip("/")
|
|
45
|
+
resolved_model = model or preset.get("model") or ""
|
|
46
|
+
if not resolved_base or not resolved_model:
|
|
47
|
+
known = ", ".join(sorted(CHAT_PROVIDERS))
|
|
48
|
+
raise oss_model.ConfigError(
|
|
49
|
+
f"unknown provider {provider!r}: pass --base-url and --model, or use one of: {known}."
|
|
50
|
+
)
|
|
51
|
+
key_env = preset.get("key_env") or f"{provider.upper()}_API_KEY"
|
|
52
|
+
api_key = oss_model._resolve_key(provider, key_env, env)
|
|
53
|
+
return {"base_url": resolved_base, "model": resolved_model, "api_key": api_key}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def ask(cfg: dict[str, str], prompt: str, system: str | None = None, timeout: int = 120) -> str:
|
|
57
|
+
"""POST the prompt to {base_url}/chat/completions and return the reply text."""
|
|
58
|
+
messages = ([{"role": "system", "content": system}] if system else []) + [
|
|
59
|
+
{"role": "user", "content": prompt}
|
|
60
|
+
]
|
|
61
|
+
body = json.dumps({"model": cfg["model"], "messages": messages}).encode("utf-8")
|
|
62
|
+
req = urllib.request.Request(
|
|
63
|
+
f"{cfg['base_url']}/chat/completions",
|
|
64
|
+
data=body,
|
|
65
|
+
headers={
|
|
66
|
+
"Content-Type": "application/json",
|
|
67
|
+
"Authorization": f"Bearer {cfg['api_key']}",
|
|
68
|
+
},
|
|
69
|
+
)
|
|
70
|
+
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
71
|
+
payload = json.loads(resp.read().decode("utf-8"))
|
|
72
|
+
return payload["choices"][0]["message"]["content"]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def main(argv: list[str], env: dict[str, str] | None = None) -> int:
|
|
76
|
+
env = dict(oss_model.os.environ) if env is None else env
|
|
77
|
+
ap = argparse.ArgumentParser(description="One-shot question to an OSS provider (no tools).")
|
|
78
|
+
ap.add_argument("provider", help="deepseek | glm | openrouter (or custom with --base-url/--model)")
|
|
79
|
+
ap.add_argument("prompt", nargs="+", help="the question")
|
|
80
|
+
ap.add_argument("--model", default=None)
|
|
81
|
+
ap.add_argument("--base-url", default=None)
|
|
82
|
+
ap.add_argument("--system", default=None)
|
|
83
|
+
args = ap.parse_args(argv)
|
|
84
|
+
try:
|
|
85
|
+
cfg = resolve_chat_config(args.provider, env, model=args.model, base_url=args.base_url)
|
|
86
|
+
except oss_model.ConfigError as exc:
|
|
87
|
+
print(str(exc), file=sys.stderr)
|
|
88
|
+
return EXIT_CONFIG
|
|
89
|
+
try:
|
|
90
|
+
print(ask(cfg, " ".join(args.prompt), system=args.system))
|
|
91
|
+
except (urllib.error.URLError, KeyError, IndexError, json.JSONDecodeError, TimeoutError) as exc:
|
|
92
|
+
print(f"oss_ask: endpoint/parse failure: {exc}", file=sys.stderr)
|
|
93
|
+
return EXIT_ERROR
|
|
94
|
+
return EXIT_OK
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
if __name__ == "__main__":
|
|
98
|
+
raise SystemExit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Shared provider + key resolver for the model-switching launchers (Track A).
|
|
3
|
+
|
|
4
|
+
`claude-oss` / `claude-glm` point a Claude Code session at an OpenAI-compatible
|
|
5
|
+
endpoint (GLM, DeepSeek, OpenRouter, or any custom base_url) by setting the
|
|
6
|
+
``ANTHROPIC_*`` env vars. This module owns the ONE place that maps a provider name
|
|
7
|
+
to its endpoint + default model, and resolves the API key WITHOUT ever hardcoding a
|
|
8
|
+
path or a secret.
|
|
9
|
+
|
|
10
|
+
Key resolution precedence (highest wins):
|
|
11
|
+
1. the provider's explicit env var (GLM_API_KEY / DEEPSEEK_API_KEY / OPENROUTER_API_KEY),
|
|
12
|
+
or the generic OSS_API_KEY;
|
|
13
|
+
2. a keys file at CADDIS_KEYS_FILE (default ~/.caddis/keys.env), INI/KEY=VALUE style,
|
|
14
|
+
comments (#) and blank lines allowed;
|
|
15
|
+
3. ConfigError with an actionable message.
|
|
16
|
+
|
|
17
|
+
Endpoint/model precedence: OSS_BASE_URL / OSS_MODEL env override > the provider preset.
|
|
18
|
+
An unknown provider is fine as long as OSS_BASE_URL + OSS_MODEL are supplied.
|
|
19
|
+
|
|
20
|
+
The PROVIDERS table mirrors oss_review.py so a renamed model id / moved endpoint is a
|
|
21
|
+
one-line edit in each. (oss_review may import this later; not refactored here to keep
|
|
22
|
+
the diff small.)
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import os
|
|
27
|
+
import sys
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
# Provider presets — the SINGLE place a renamed model id / moved endpoint is edited.
|
|
31
|
+
# These are the providers' ANTHROPIC-protocol endpoints: this table feeds
|
|
32
|
+
# ANTHROPIC_BASE_URL for Claude Code, which speaks the Anthropic Messages API — NOT the
|
|
33
|
+
# OpenAI /chat/completions dialect that oss_review.py uses (its table differs on purpose).
|
|
34
|
+
# "openrouter" has no Anthropic-protocol endpoint: it only works through an
|
|
35
|
+
# Anthropic-compatible gateway (e.g. LiteLLM — see claude-harness/LOCAL-MODELS.md) whose
|
|
36
|
+
# URL you supply via OSS_BASE_URL.
|
|
37
|
+
# The `[1m]` suffix on a model id is a CLAUDE CODE convention, not part of the model name.
|
|
38
|
+
# Claude Code strips it before calling the API; it only tells the client the real context
|
|
39
|
+
# window. Without it, an id Claude Code does not recognise is assumed to be 200k and
|
|
40
|
+
# auto-compact throws away context the model could still hold. Both providers are 1M
|
|
41
|
+
# (GLM-5.3: docs.z.ai; DeepSeek V4 Flash: 1,048,576 positions) — verified 2026-08-23 by
|
|
42
|
+
# running with and without the suffix and diffing the cap warning.
|
|
43
|
+
# NEVER put this suffix in the OpenAI-dialect tables (oss_review.py / oss_ask.py): those
|
|
44
|
+
# send the id raw to /chat/completions, which would reject it.
|
|
45
|
+
PROVIDERS: dict[str, dict[str, str]] = {
|
|
46
|
+
"deepseek": {"base_url": "https://api.deepseek.com/anthropic", "model": "deepseek-v4-flash[1m]", "key_env": "DEEPSEEK_API_KEY"},
|
|
47
|
+
"glm": {"base_url": "https://api.z.ai/api/anthropic", "model": "glm-5.3[1m]", "key_env": "GLM_API_KEY"},
|
|
48
|
+
"openrouter": {"base_url": "", "model": "deepseek/deepseek-v4-flash", "key_env": "OPENROUTER_API_KEY"},
|
|
49
|
+
}
|
|
50
|
+
DEFAULT_PROVIDER = "deepseek"
|
|
51
|
+
DEFAULT_KEYS_FILE = "~/.caddis/keys.env"
|
|
52
|
+
|
|
53
|
+
# Env-var naming derives from the shared prefix constant, so a future rename is one edit there.
|
|
54
|
+
try:
|
|
55
|
+
from claudster_config import ENV_PREFIX
|
|
56
|
+
except Exception: # pragma: no cover — standalone copy
|
|
57
|
+
ENV_PREFIX = "CADDIS"
|
|
58
|
+
KEYS_FILE_ENV = f"{ENV_PREFIX}_KEYS_FILE"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ConfigError(Exception):
|
|
62
|
+
"""Configuration can't be resolved — unknown provider without an override, or no key."""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _parse_keys_file(path: str) -> dict[str, str]:
|
|
66
|
+
"""Parse an INI/``KEY=VALUE`` keys file. Comments (#) and blank lines are ignored;
|
|
67
|
+
surrounding quotes are stripped. A missing file yields ``{}`` (not an error — the
|
|
68
|
+
key may still come from the environment)."""
|
|
69
|
+
p = Path(path).expanduser()
|
|
70
|
+
if not p.is_file():
|
|
71
|
+
return {}
|
|
72
|
+
out: dict[str, str] = {}
|
|
73
|
+
for raw in p.read_text(encoding="utf-8").splitlines():
|
|
74
|
+
line = raw.strip()
|
|
75
|
+
if not line or line.startswith("#") or "=" not in line:
|
|
76
|
+
continue
|
|
77
|
+
key, val = line.split("=", 1)
|
|
78
|
+
out[key.strip()] = val.strip().strip('"').strip("'")
|
|
79
|
+
return out
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _resolve_key(provider: str, key_env: str, env: dict[str, str]) -> str:
|
|
83
|
+
"""Provider key via env, then the keys file, else ConfigError. Never logged/echoed."""
|
|
84
|
+
# 1. explicit env (provider-specific, then generic)
|
|
85
|
+
for name in (key_env, "OSS_API_KEY"):
|
|
86
|
+
val = (env.get(name) or "").strip()
|
|
87
|
+
if val:
|
|
88
|
+
return val
|
|
89
|
+
# 2. keys file (CADDIS_KEYS_FILE if set, else ~/.caddis/keys.env)
|
|
90
|
+
keys_path = env.get(KEYS_FILE_ENV) or DEFAULT_KEYS_FILE
|
|
91
|
+
file_keys = _parse_keys_file(keys_path)
|
|
92
|
+
for name in (key_env, "OSS_API_KEY"):
|
|
93
|
+
val = (file_keys.get(name) or "").strip()
|
|
94
|
+
if val:
|
|
95
|
+
return val
|
|
96
|
+
# 3. actionable error — name the var and the file, never a value.
|
|
97
|
+
#
|
|
98
|
+
# It also names the providers that DO have a key. When DeepSeek is unset but GLM is configured,
|
|
99
|
+
# the fix is `--provider glm`, not "go and find a key" — and the old message sent the reader
|
|
100
|
+
# hunting for a credential they already had. This fails at the moment of use, which is the
|
|
101
|
+
# least convenient moment to be told to go looking (`.caddis/parking-lot/003`).
|
|
102
|
+
others = [p for p in configured_providers(env) if p != provider]
|
|
103
|
+
have = (f"\nProviders that DO have a key here: {', '.join(others)}. "
|
|
104
|
+
f"To use one now: --provider {others[0]}." if others else "")
|
|
105
|
+
raise ConfigError(
|
|
106
|
+
f"no API key for provider {provider!r}. Set ${key_env} (or $OSS_API_KEY), or add\n"
|
|
107
|
+
f" {key_env}=<your-key>\n"
|
|
108
|
+
f"to your keys file ({keys_path}). Override the file path with ${KEYS_FILE_ENV}."
|
|
109
|
+
+ have
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def configured_providers(env: dict[str, str]) -> list[str]:
|
|
114
|
+
"""Every preset provider that currently has a usable key. Never returns or logs a key value.
|
|
115
|
+
|
|
116
|
+
Exists so a caller can find out BEFORE it needs one. `/caddis:cross-review` used to discover a
|
|
117
|
+
missing key mid-task, with a commit pending — and a command that fails once at an inconvenient
|
|
118
|
+
moment does not get retried, so the safety check it provides is simply lost and nobody records
|
|
119
|
+
that it was lost.
|
|
120
|
+
"""
|
|
121
|
+
keys_path = env.get(KEYS_FILE_ENV) or DEFAULT_KEYS_FILE
|
|
122
|
+
file_keys = _parse_keys_file(keys_path)
|
|
123
|
+
generic = (env.get("OSS_API_KEY") or file_keys.get("OSS_API_KEY") or "").strip()
|
|
124
|
+
out = []
|
|
125
|
+
for name, preset in sorted(PROVIDERS.items()):
|
|
126
|
+
key_env = preset.get("key_env") or f"{name.upper()}_API_KEY"
|
|
127
|
+
if generic or (env.get(key_env) or "").strip() or (file_keys.get(key_env) or "").strip():
|
|
128
|
+
out.append(name)
|
|
129
|
+
return out
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def resolve(provider: str | None, env: dict[str, str]) -> dict[str, str]:
|
|
133
|
+
"""Resolve ``{base_url, model, api_key}`` for ``provider`` using ``env``.
|
|
134
|
+
|
|
135
|
+
``provider`` defaults to $OSS_PROVIDER then DEEPSEEK. An unknown provider is allowed
|
|
136
|
+
only when OSS_BASE_URL + OSS_MODEL are supplied. Raises ConfigError otherwise, or
|
|
137
|
+
when no API key can be resolved.
|
|
138
|
+
"""
|
|
139
|
+
provider = (provider or env.get("OSS_PROVIDER") or DEFAULT_PROVIDER).strip().lower()
|
|
140
|
+
preset = PROVIDERS.get(provider, {})
|
|
141
|
+
base_url = (env.get("OSS_BASE_URL") or preset.get("base_url") or "").rstrip("/")
|
|
142
|
+
model = env.get("OSS_MODEL") or preset.get("model") or ""
|
|
143
|
+
if not base_url or not model:
|
|
144
|
+
known = ", ".join(sorted(PROVIDERS))
|
|
145
|
+
raise ConfigError(
|
|
146
|
+
f"unknown provider {provider!r}: set $OSS_BASE_URL and $OSS_MODEL, "
|
|
147
|
+
f"or use one of: {known}."
|
|
148
|
+
)
|
|
149
|
+
key_env = preset.get("key_env") or f"{provider.upper()}_API_KEY"
|
|
150
|
+
api_key = _resolve_key(provider, key_env, env)
|
|
151
|
+
return {"base_url": base_url, "model": model, "api_key": api_key}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def main(argv: list[str], env: dict[str, str] | None = None) -> int:
|
|
155
|
+
"""CLI bridge for the launchers: ``oss_model.py <provider>`` prints the resolved
|
|
156
|
+
base_url, model, and api_key on three lines (in that order) to stdout — the caller
|
|
157
|
+
(claude-oss.sh / .ps1) CAPTURES this output into variables and sets ANTHROPIC_*; it
|
|
158
|
+
is never displayed. A ConfigError prints its message to stderr and exits 3.
|
|
159
|
+
"""
|
|
160
|
+
env = os.environ if env is None else env
|
|
161
|
+
provider = argv[0] if argv else None
|
|
162
|
+
try:
|
|
163
|
+
cfg = resolve(provider, env)
|
|
164
|
+
except ConfigError as exc:
|
|
165
|
+
print(str(exc), file=sys.stderr)
|
|
166
|
+
return 3
|
|
167
|
+
print(cfg["base_url"])
|
|
168
|
+
print(cfg["model"])
|
|
169
|
+
print(cfg["api_key"])
|
|
170
|
+
return 0
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
if __name__ == "__main__":
|
|
174
|
+
raise SystemExit(main(sys.argv[1:]))
|