@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.
Files changed (265) hide show
  1. package/bundles/antigravity-plugin/agents/claude-md-curator.md +1 -1
  2. package/bundles/antigravity-plugin/agents/knowledge-transfer.md +17 -32
  3. package/bundles/antigravity-plugin/caddis_statusline.py +825 -0
  4. package/bundles/antigravity-plugin/claude-md/folder-agents.md.tmpl +21 -0
  5. package/bundles/antigravity-plugin/guard_agy.py +60 -9
  6. package/bundles/antigravity-plugin/plugin.json +1 -1
  7. package/bundles/antigravity-plugin/scripts/caddis_gate.py +524 -0
  8. package/bundles/antigravity-plugin/scripts/caddis_inventory.py +439 -0
  9. package/bundles/antigravity-plugin/scripts/caddis_migrate_dir.py +270 -0
  10. package/bundles/antigravity-plugin/scripts/caddis_spawn.py +300 -0
  11. package/bundles/antigravity-plugin/scripts/caddis_statusline.py +825 -0
  12. package/bundles/antigravity-plugin/scripts/caddis_tidy.py +366 -0
  13. package/bundles/antigravity-plugin/scripts/check_doc_coverage.py +924 -0
  14. package/bundles/antigravity-plugin/scripts/claude-deepseek.ps1 +4 -0
  15. package/bundles/antigravity-plugin/scripts/claude-deepseek.sh +3 -0
  16. package/bundles/antigravity-plugin/scripts/claude-glm.ps1 +4 -0
  17. package/bundles/antigravity-plugin/scripts/claude-glm.sh +3 -0
  18. package/bundles/antigravity-plugin/scripts/claude-oss.ps1 +64 -0
  19. package/bundles/antigravity-plugin/scripts/claude-oss.sh +60 -0
  20. package/bundles/antigravity-plugin/scripts/claudster_config.py +121 -0
  21. package/bundles/antigravity-plugin/scripts/claudster_doctor.py +356 -0
  22. package/bundles/antigravity-plugin/scripts/claudster_init.py +400 -0
  23. package/bundles/antigravity-plugin/scripts/claudster_migrate_rules.py +300 -0
  24. package/bundles/antigravity-plugin/scripts/enforcement_inventory.py +261 -0
  25. package/bundles/antigravity-plugin/scripts/hook_log.py +156 -0
  26. package/bundles/antigravity-plugin/scripts/new-machine.ps1 +170 -0
  27. package/bundles/antigravity-plugin/scripts/oss_ask.py +98 -0
  28. package/bundles/antigravity-plugin/scripts/oss_model.py +174 -0
  29. package/bundles/antigravity-plugin/scripts/oss_review.py +718 -0
  30. package/bundles/antigravity-plugin/scripts/setup_project_ai.py +1432 -0
  31. package/bundles/antigravity-plugin/scripts/usage_review.py +1323 -0
  32. package/bundles/antigravity-plugin/session_end_agy.py +52 -8
  33. package/bundles/antigravity-plugin/skills/_registry.md +1 -0
  34. package/bundles/antigravity-plugin/skills/advisory-hub/SKILL.md +44 -0
  35. package/bundles/antigravity-plugin/skills/code-review/SKILL.md +11 -0
  36. package/bundles/antigravity-plugin/skills/fastapi-dev/SKILL.md +1 -1
  37. package/bundles/antigravity-plugin/skills/feature-plan/SKILL.md +21 -0
  38. package/bundles/antigravity-plugin/skills/gate-review/SKILL.md +11 -0
  39. package/bundles/antigravity-plugin/skills/implement/SKILL.md +36 -0
  40. package/bundles/antigravity-plugin/skills/security-review/SKILL.md +9 -0
  41. package/bundles/antigravity-plugin/skills/ship/SKILL.md +1 -1
  42. package/bundles/antigravity-plugin/skills/ship-merge/SKILL.md +1 -1
  43. package/bundles/antigravity-plugin/skills/ship-pr/SKILL.md +1 -1
  44. package/bundles/antigravity-plugin/skills/statusline/SKILL.md +105 -0
  45. package/bundles/antigravity-plugin/skills/use-model/SKILL.md +15 -3
  46. package/bundles/antigravity-plugin/skills/validate-phase/SKILL.md +31 -0
  47. package/bundles/antigravity-plugin/warm_start_agy.py +117 -11
  48. package/bundles/antigravity-plugin-extras/plugin.json +1 -1
  49. package/bundles/antigravity-plugin-extras/skills/architecture-design/SKILL.md +1 -1
  50. package/bundles/antigravity-plugin-extras/skills/caching-patterns/SKILL.md +1 -1
  51. package/bundles/antigravity-plugin-extras/skills/webapp-development/SKILL.md +1 -1
  52. package/bundles/codex/.codex/config.toml.example +36 -0
  53. package/bundles/codex/.codex/skills/coding/api-design/SKILL.md +249 -0
  54. package/bundles/codex/.codex/skills/coding/backend-development/SKILL.md +305 -0
  55. package/bundles/codex/.codex/skills/coding/code-review/SKILL.md +140 -0
  56. package/bundles/codex/.codex/skills/coding/codebase-audit/SKILL.md +204 -0
  57. package/bundles/codex/.codex/skills/coding/dashboard-perf-sweep/SKILL.md +132 -0
  58. package/bundles/codex/.codex/skills/coding/dashboard-perf-sweep/scripts/perf_sweep.py +249 -0
  59. package/bundles/codex/.codex/skills/coding/error-handling/SKILL.md +260 -0
  60. package/bundles/codex/.codex/skills/coding/fastapi-dev/SKILL.md +300 -0
  61. package/bundles/codex/.codex/skills/coding/javascript-typescript/SKILL.md +142 -0
  62. package/bundles/codex/.codex/skills/coding/python/SKILL.md +388 -0
  63. package/bundles/codex/.codex/skills/coding/refactoring/SKILL.md +486 -0
  64. package/bundles/codex/.codex/skills/coding/security-review/SKILL.md +205 -0
  65. package/bundles/codex/.codex/skills/coding/sql/SKILL.md +321 -0
  66. package/bundles/codex/.codex/skills/data/database-design/SKILL.md +177 -0
  67. package/bundles/codex/.codex/skills/data/db-diagram/SKILL.md +148 -0
  68. package/bundles/codex/.codex/skills/data/db-diagram/scripts/sql_to_graph.py +1212 -0
  69. package/bundles/codex/.codex/skills/devops/ci-cd-pipeline/SKILL.md +315 -0
  70. package/bundles/codex/.codex/skills/devops/gh-cli/SKILL.md +195 -0
  71. package/bundles/codex/.codex/skills/devops/git-commit/SKILL.md +285 -0
  72. package/bundles/codex/.codex/skills/devops/using-git-worktrees/SKILL.md +217 -0
  73. package/bundles/codex/.codex/skills/devops/windows-deployment/SKILL.md +880 -0
  74. package/bundles/codex/.codex/skills/docs/code-documentation/SKILL.md +271 -0
  75. package/bundles/codex/.codex/skills/docs/explainer-doc/SKILL.md +97 -0
  76. package/bundles/codex/.codex/skills/docs/technical-writing/SKILL.md +286 -0
  77. package/bundles/codex/.codex/skills/docs/writing-plans/SKILL.md +387 -0
  78. package/bundles/codex/.codex/skills/frontend/css-architecture/SKILL.md +305 -0
  79. package/bundles/codex/.codex/skills/frontend/css-architecture/references/RESPONSIVE-DESIGN.md +604 -0
  80. package/bundles/codex/.codex/skills/frontend/frontend-design/SKILL.md +193 -0
  81. package/bundles/codex/.codex/skills/frontend/frontend-design/references/my-tech-stack.md +127 -0
  82. package/bundles/codex/.codex/skills/frontend/mockup/SKILL.md +242 -0
  83. package/bundles/codex/.codex/skills/frontend/react-best-practices/SKILL.md +269 -0
  84. package/bundles/codex/.codex/skills/frontend/react-dev/README.md +404 -0
  85. package/bundles/codex/.codex/skills/frontend/react-dev/SKILL.md +459 -0
  86. package/bundles/codex/.codex/skills/frontend/react-dev/examples/generic-components.md +579 -0
  87. package/bundles/codex/.codex/skills/frontend/react-dev/examples/server-components.md +579 -0
  88. package/bundles/codex/.codex/skills/frontend/react-dev/references/event-handlers.md +574 -0
  89. package/bundles/codex/.codex/skills/frontend/react-dev/references/hooks.md +456 -0
  90. package/bundles/codex/.codex/skills/frontend/react-dev/references/react-19-patterns.md +638 -0
  91. package/bundles/codex/.codex/skills/frontend/react-dev/references/react-router.md +1002 -0
  92. package/bundles/codex/.codex/skills/frontend/react-dev/references/tanstack-router.md +587 -0
  93. package/bundles/codex/.codex/skills/frontend/ui-review/SKILL.md +176 -0
  94. package/bundles/codex/.codex/skills/frontend/ui-review/references/framework-fixes.md +471 -0
  95. package/bundles/codex/.codex/skills/frontend/ui-review/references/visual-checklist.md +236 -0
  96. package/bundles/codex/.codex/skills/frontend/warm-editorial-ui/DESIGN_TOKENS.md +487 -0
  97. package/bundles/codex/.codex/skills/frontend/warm-editorial-ui/IMPLEMENTATION_GUIDE.md +177 -0
  98. package/bundles/codex/.codex/skills/frontend/warm-editorial-ui/SKILL.md +732 -0
  99. package/bundles/codex/.codex/skills/media/draw-io/SKILL.md +162 -0
  100. package/bundles/codex/.codex/skills/media/draw-io/references/aws-icons.md +677 -0
  101. package/bundles/codex/.codex/skills/media/draw-io/references/layout-guidelines.md +142 -0
  102. package/bundles/codex/.codex/skills/media/draw-io/references/troubleshooting.md +118 -0
  103. package/bundles/codex/.codex/skills/media/draw-io/references/workflows.md +103 -0
  104. package/bundles/codex/.codex/skills/media/draw-io/scripts/convert-drawio-to-png.sh +25 -0
  105. package/bundles/codex/.codex/skills/media/draw-io/scripts/find_aws_icon.py +79 -0
  106. package/bundles/codex/.codex/skills/media/mermaid-diagrams/SKILL.md +292 -0
  107. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/ENHANCEMENTS.md +264 -0
  108. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/MERMAID-SUMMARY.md +137 -0
  109. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/advanced-features.md +556 -0
  110. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/architecture-diagrams.md +192 -0
  111. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/c4-diagrams.md +410 -0
  112. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/class-diagrams.md +361 -0
  113. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/erd-diagrams.md +510 -0
  114. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/flowcharts.md +450 -0
  115. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/sequence-diagrams.md +394 -0
  116. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/troubleshooting.md +335 -0
  117. package/bundles/codex/.codex/skills/media/mermaid-diagrams/references/workflows.md +418 -0
  118. package/bundles/codex/.codex/skills/media/particle-art/SKILL.md +243 -0
  119. package/bundles/codex/.codex/skills/media/particle-art/references/canvas-utils.ts +171 -0
  120. package/bundles/codex/.codex/skills/media/particle-art/references/dot-field.template.tsx +203 -0
  121. package/bundles/codex/.codex/skills/media/particle-art/references/flow-field.template.tsx +263 -0
  122. package/bundles/codex/.codex/skills/media/particle-art/references/node-shape.template.tsx +261 -0
  123. package/bundles/codex/.codex/skills/media/particle-art/references/shape-sampler.ts +281 -0
  124. package/bundles/codex/.codex/skills/media/particle-art/references/stipple-morph.template.tsx +167 -0
  125. package/bundles/codex/.codex/skills/media/particle-art/references/stipple.template.tsx +175 -0
  126. package/bundles/codex/.codex/skills/media/particle-art/references/trail-ghost.template.tsx +266 -0
  127. package/bundles/codex/.codex/skills/media/particle-art/references/usage-examples.md +320 -0
  128. package/bundles/codex/.codex/skills/testing/playwright/API_REFERENCE.md +653 -0
  129. package/bundles/codex/.codex/skills/testing/playwright/SKILL.md +454 -0
  130. package/bundles/codex/.codex/skills/testing/playwright/lib/helpers.js +441 -0
  131. package/bundles/codex/.codex/skills/testing/playwright/package.json +26 -0
  132. package/bundles/codex/.codex/skills/testing/playwright/run.js +228 -0
  133. package/bundles/codex/.codex/skills/testing/tdd-workflow/SKILL.md +188 -0
  134. package/bundles/codex/.codex/skills/testing/test-strategy/SKILL.md +155 -0
  135. package/bundles/codex/.codex/skills/testing/webapp-testing/LICENSE.txt +202 -0
  136. package/bundles/codex/.codex/skills/testing/webapp-testing/SKILL.md +97 -0
  137. package/bundles/codex/.codex/skills/testing/webapp-testing/examples/console_logging.py +35 -0
  138. package/bundles/codex/.codex/skills/testing/webapp-testing/examples/element_discovery.py +40 -0
  139. package/bundles/codex/.codex/skills/testing/webapp-testing/examples/static_html_automation.py +33 -0
  140. package/bundles/codex/.codex/skills/testing/webapp-testing/scripts/with_server.py +106 -0
  141. package/bundles/codex/.codex/skills/workflow/advisory-hub/SKILL.md +227 -0
  142. package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/context-template.md +214 -0
  143. package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/hub-handbook.md +233 -0
  144. package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/report-contract.md +94 -0
  145. package/bundles/codex/.codex/skills/workflow/advisory-hub/reference/succession.md +118 -0
  146. package/bundles/codex/.codex/skills/workflow/best-practices/SKILL.md +500 -0
  147. package/bundles/codex/.codex/skills/workflow/best-practices/agents/best-practices-referencer.md +263 -0
  148. package/bundles/codex/.codex/skills/workflow/best-practices/agents/codebase-context-builder.md +326 -0
  149. package/bundles/codex/.codex/skills/workflow/best-practices/agents/task-intent-analyzer.md +245 -0
  150. package/bundles/codex/.codex/skills/workflow/best-practices/references/anti-patterns.md +571 -0
  151. package/bundles/codex/.codex/skills/workflow/best-practices/references/before-after-examples.md +1114 -0
  152. package/bundles/codex/.codex/skills/workflow/best-practices/references/best-practices-guide.md +513 -0
  153. package/bundles/codex/.codex/skills/workflow/best-practices/references/common-workflows.md +692 -0
  154. package/bundles/codex/.codex/skills/workflow/best-practices/references/prompt-patterns.md +547 -0
  155. package/bundles/codex/.codex/skills/workflow/brainstorming/SKILL.md +57 -0
  156. package/bundles/codex/.codex/skills/workflow/context-curator/SKILL.md +157 -0
  157. package/bundles/codex/.codex/skills/workflow/golden-plan/SKILL.md +586 -0
  158. package/bundles/codex/.codex/skills/workflow/preflight/SKILL.md +440 -0
  159. package/bundles/codex/.codex/skills/workflow/skill-creator/LICENSE.txt +202 -0
  160. package/bundles/codex/.codex/skills/workflow/skill-creator/SKILL.md +491 -0
  161. package/bundles/codex/.codex/skills/workflow/skill-creator/agents/analyzer.md +274 -0
  162. package/bundles/codex/.codex/skills/workflow/skill-creator/agents/comparator.md +202 -0
  163. package/bundles/codex/.codex/skills/workflow/skill-creator/agents/grader.md +223 -0
  164. package/bundles/codex/.codex/skills/workflow/skill-creator/assets/eval_review.html +146 -0
  165. package/bundles/codex/.codex/skills/workflow/skill-creator/eval-viewer/generate_review.py +471 -0
  166. package/bundles/codex/.codex/skills/workflow/skill-creator/eval-viewer/viewer.html +1325 -0
  167. package/bundles/codex/.codex/skills/workflow/skill-creator/references/schemas.md +430 -0
  168. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/__init__.py +0 -0
  169. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/aggregate_benchmark.py +401 -0
  170. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/generate_report.py +326 -0
  171. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/improve_description.py +247 -0
  172. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/package_skill.py +136 -0
  173. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/quick_validate.py +103 -0
  174. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/run_eval.py +310 -0
  175. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/run_loop.py +328 -0
  176. package/bundles/codex/.codex/skills/workflow/skill-creator/scripts/utils.py +47 -0
  177. package/bundles/codex/AGENTS.md +146 -0
  178. package/bundles/manifest.json +4 -3
  179. package/dist/claude-deepseek.js +544 -0
  180. package/dist/claude-glm.js +544 -0
  181. package/dist/claude-oss.js +544 -0
  182. package/dist/cli.js +542 -90
  183. package/package.json +5 -2
  184. package/bundles/antigravity-plugin/statusline-command-agy.sh +0 -91
  185. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +0 -93
  186. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  187. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  188. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +0 -93
  189. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  190. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +0 -93
  191. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  192. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  193. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -93
  194. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  195. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  196. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  197. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +0 -93
  198. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  199. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +0 -93
  200. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  201. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +0 -94
  202. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  203. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  204. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +0 -93
  205. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  206. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +0 -93
  207. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  208. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  209. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +0 -93
  210. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  211. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  212. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  213. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  214. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  215. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  216. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  217. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  218. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +0 -93
  219. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  220. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  221. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  222. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +0 -93
  223. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  224. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  225. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +0 -93
  226. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  227. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  228. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  229. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Jura-OFL.txt +0 -93
  230. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +0 -93
  231. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  232. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  233. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  234. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  235. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-OFL.txt +0 -93
  236. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  237. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  238. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +0 -93
  239. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  240. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -93
  241. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  242. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  243. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +0 -93
  244. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  245. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  246. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +0 -93
  247. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +0 -93
  248. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  249. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  250. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +0 -93
  251. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  252. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +0 -93
  253. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  254. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  255. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +0 -93
  256. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  257. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +0 -93
  258. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  259. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  260. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  261. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  262. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +0 -93
  263. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  264. package/bundles/antigravity-plugin-extras/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +0 -93
  265. 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:]))