@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,64 @@
|
|
|
1
|
+
# claude-oss / claude-glm — launch a Claude Code session against an OSS provider's
|
|
2
|
+
# Anthropic-protocol endpoint (GLM, DeepSeek, or a custom OSS_BASE_URL/OSS_MODEL;
|
|
3
|
+
# OpenRouter/local models need an Anthropic-compatible gateway — see LOCAL-MODELS.md).
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# claude-oss <provider> [claude args...] # e.g. claude-oss glm -p "say ok"
|
|
7
|
+
# claude-glm [claude args...] # convenience alias (provider = glm)
|
|
8
|
+
#
|
|
9
|
+
# Endpoint/model/key are resolved by oss_model.py (keys are never hardcoded — see
|
|
10
|
+
# CADDIS_KEYS_FILE). This reads $args directly and declares NO parameter block: a
|
|
11
|
+
# declared parameter would swallow claude's own flags (e.g. -p) before claude sees them.
|
|
12
|
+
# The ANTHROPIC_* env is saved and RESTORED in finally, so your default `claude` in this
|
|
13
|
+
# session is untouched.
|
|
14
|
+
$ErrorActionPreference = "Stop"
|
|
15
|
+
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
16
|
+
$invoked = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name)
|
|
17
|
+
|
|
18
|
+
# claude-glm forces provider=glm and passes every arg to claude; otherwise the first
|
|
19
|
+
# arg is the provider and the rest go to claude.
|
|
20
|
+
if ($invoked -like "claude-glm*") {
|
|
21
|
+
$provider = "glm"
|
|
22
|
+
$rest = $args
|
|
23
|
+
} else {
|
|
24
|
+
$provider = if ($args.Count -ge 1) { $args[0] } else { $null }
|
|
25
|
+
$rest = if ($args.Count -ge 2) { $args[1..($args.Count - 1)] } else { @() }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (-not $provider) {
|
|
29
|
+
Write-Error "usage: claude-oss <provider> [claude args...] (providers: deepseek, glm, openrouter)"
|
|
30
|
+
exit 2
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# Save the current ANTHROPIC_* so we can restore them — this launcher shares the session.
|
|
34
|
+
$restore = @{}
|
|
35
|
+
foreach ($name in "ANTHROPIC_BASE_URL", "ANTHROPIC_MODEL", "ANTHROPIC_AUTH_TOKEN", "CADDIS_HEADLESS") {
|
|
36
|
+
$restore[$name] = [Environment]::GetEnvironmentVariable($name)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# claude's -p/--print means no human is watching. Say so in the env so the caddis hooks can
|
|
40
|
+
# act on it — the SessionStart relay injection is skipped for headless runs, because a run
|
|
41
|
+
# handed its task on the command line does not need a resume pointer and was observed
|
|
42
|
+
# EXECUTING the relay's leftover next step before its own prompt.
|
|
43
|
+
$headless = $false
|
|
44
|
+
foreach ($a in $rest) { if ($a -eq "-p" -or $a -eq "--print") { $headless = $true } }
|
|
45
|
+
if ($headless) { $env:CADDIS_HEADLESS = "1" }
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
# Resolve endpoint/model/key. The three stdout lines are captured into a variable and
|
|
49
|
+
# assigned to the env — the key is never written to the console.
|
|
50
|
+
$cfg = & python "$scriptDir\oss_model.py" $provider
|
|
51
|
+
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
52
|
+
|
|
53
|
+
$env:ANTHROPIC_BASE_URL = $cfg[0]
|
|
54
|
+
$env:ANTHROPIC_MODEL = $cfg[1]
|
|
55
|
+
$env:ANTHROPIC_AUTH_TOKEN = $cfg[2]
|
|
56
|
+
|
|
57
|
+
& claude @rest
|
|
58
|
+
exit $LASTEXITCODE
|
|
59
|
+
} finally {
|
|
60
|
+
foreach ($name in $restore.Keys) {
|
|
61
|
+
Set-Item -Path "Env:$name" -Value $restore[$name] -ErrorAction SilentlyContinue
|
|
62
|
+
if ($null -eq $restore[$name]) { Remove-Item -Path "Env:$name" -ErrorAction SilentlyContinue }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# claude-oss / claude-glm — launch a Claude Code session against an OSS provider's
|
|
3
|
+
# Anthropic-protocol endpoint (GLM, DeepSeek, or a custom OSS_BASE_URL/OSS_MODEL;
|
|
4
|
+
# OpenRouter/local models need an Anthropic-compatible gateway — see LOCAL-MODELS.md).
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# claude-oss <provider> [claude args...] # e.g. claude-oss glm -p "say ok"
|
|
8
|
+
# claude-glm [claude args...] # convenience alias (provider = glm)
|
|
9
|
+
#
|
|
10
|
+
# The endpoint/model/key are resolved by oss_model.py (keys are never hardcoded — see
|
|
11
|
+
# CADDIS_KEYS_FILE). This runs as its OWN process and `exec`s claude, so the
|
|
12
|
+
# ANTHROPIC_* env it sets dies with it — your default `claude` is untouched.
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
|
|
17
|
+
# claude-glm is the same launcher symlinked/renamed: force provider=glm and treat every
|
|
18
|
+
# arg as a claude arg. Otherwise the first arg is the provider.
|
|
19
|
+
case "$(basename "$0")" in
|
|
20
|
+
claude-glm*) provider="glm" ;;
|
|
21
|
+
*)
|
|
22
|
+
provider="${1:-}"
|
|
23
|
+
if [ "$#" -gt 0 ]; then shift; fi
|
|
24
|
+
;;
|
|
25
|
+
esac
|
|
26
|
+
|
|
27
|
+
if [ -z "${provider}" ]; then
|
|
28
|
+
echo "usage: claude-oss <provider> [claude args...] (providers: deepseek, glm, openrouter)" >&2
|
|
29
|
+
exit 2
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# claude's -p/--print means no human is watching. Say so in the env so the caddis hooks can
|
|
33
|
+
# act on it — the SessionStart relay injection is skipped for headless runs, because a run
|
|
34
|
+
# handed its task on the command line does not need a resume pointer and was observed
|
|
35
|
+
# EXECUTING the relay's leftover next step before its own prompt.
|
|
36
|
+
for _arg in "$@"; do
|
|
37
|
+
case "$_arg" in
|
|
38
|
+
-p|--print) export CADDIS_HEADLESS=1 ;;
|
|
39
|
+
esac
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
# Prefer python3, fall back to python.
|
|
43
|
+
PY="python3"; command -v "$PY" >/dev/null 2>&1 || PY="python"
|
|
44
|
+
|
|
45
|
+
# Resolve endpoint/model/key. On failure the resolver prints an actionable message to
|
|
46
|
+
# stderr and exits non-zero — propagate that (command substitution lets us check it).
|
|
47
|
+
if ! _out="$("$PY" "$SCRIPT_DIR/oss_model.py" "$provider")"; then
|
|
48
|
+
exit 3
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Split the three lines (base_url, model, api_key) into the env WITHOUT echoing them.
|
|
52
|
+
{
|
|
53
|
+
IFS= read -r ANTHROPIC_BASE_URL
|
|
54
|
+
IFS= read -r ANTHROPIC_MODEL
|
|
55
|
+
IFS= read -r ANTHROPIC_AUTH_TOKEN
|
|
56
|
+
} <<< "$_out"
|
|
57
|
+
export ANTHROPIC_BASE_URL ANTHROPIC_MODEL ANTHROPIC_AUTH_TOKEN
|
|
58
|
+
|
|
59
|
+
# Hand off to the real claude with all remaining args (exec = env dies with this process).
|
|
60
|
+
exec claude "$@"
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Shared identity + config reader for a repo's caddis artifact dir — fail-open, dependency-free.
|
|
2
|
+
|
|
3
|
+
Two jobs:
|
|
4
|
+
|
|
5
|
+
1. **Functional identity in ONE place.** The artifact dir name, the env-var prefix, and the
|
|
6
|
+
repo/tarball URL are constants here, so a future rename edits a constant instead of sweeping
|
|
7
|
+
the tree. Anything that needs them imports from this module.
|
|
8
|
+
2. **The optional per-repo ``config.toml`` reader**, so the doc-coverage checker and Dream Memory
|
|
9
|
+
don't each reimplement it (the guard hook keeps its own tiny reader — it lives under hooks/ and
|
|
10
|
+
is safety-critical, so it stays self-contained). Every function returns a safe default rather
|
|
11
|
+
than raising: a missing file, a parse error, a missing section, or a wrong-typed value all
|
|
12
|
+
degrade to the caller's default — the "degrade gracefully, never block" bar of the rest of caddis.
|
|
13
|
+
|
|
14
|
+
The per-repo artifact dir is always ``.caddis`` — use :func:`artifact_root` / :func:`artifact_read` /
|
|
15
|
+
:func:`artifact_write`. A repo still carrying a pre-rename ``.claudster/`` (there should be none left
|
|
16
|
+
in this fleet) can be converted with ``/caddis:migrate-dir`` (``scripts/caddis_migrate_dir.py``);
|
|
17
|
+
nothing renames a repo's dir behind its back.
|
|
18
|
+
|
|
19
|
+
Config schema (see ``.caddis/config.toml.example``)::
|
|
20
|
+
|
|
21
|
+
[doc_coverage]
|
|
22
|
+
route_tree = "frontend/src/routeTree.gen.ts"
|
|
23
|
+
page_guide = "UI_PAGE_GUIDE.md"
|
|
24
|
+
claude_md_budget = 200
|
|
25
|
+
ignore_routes = ["/health"]
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import os
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
|
|
33
|
+
# ── functional identity (rename here, not everywhere) ────────────────────────
|
|
34
|
+
#: The per-repo artifact directory caddis writes into.
|
|
35
|
+
ARTIFACT_DIR = ".caddis"
|
|
36
|
+
#: Read-preference order for the artifact dir. Single-element tuple kept so importers that iterate
|
|
37
|
+
#: this (a future rename adds an element here, not at each call site) don't need to change shape.
|
|
38
|
+
ARTIFACT_DIRS: tuple[str, ...] = (ARTIFACT_DIR,)
|
|
39
|
+
|
|
40
|
+
#: Environment-variable prefix — every caddis env var is ``<ENV_PREFIX>_<SUFFIX>``.
|
|
41
|
+
ENV_PREFIX = "CADDIS"
|
|
42
|
+
|
|
43
|
+
#: The published plugin repo (mirror). Everything URL-shaped derives from this one slug.
|
|
44
|
+
REPO_SLUG = "saajunaid/caddis-plugin"
|
|
45
|
+
REPO_URL = f"https://github.com/{REPO_SLUG}"
|
|
46
|
+
TARBALL_URL = f"https://codeload.github.com/{REPO_SLUG}/tar.gz/refs/heads/main"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def env_name(suffix: str) -> str:
|
|
50
|
+
"""The current env-var name for ``suffix`` (e.g. ``"GUARD_DISABLED"`` → ``CADDIS_GUARD_DISABLED``)."""
|
|
51
|
+
return f"{ENV_PREFIX}_{suffix}"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def env_get(suffix: str, default: str | None = None) -> str | None:
|
|
55
|
+
"""``$CADDIS_<suffix>``."""
|
|
56
|
+
val = os.environ.get(env_name(suffix))
|
|
57
|
+
return default if val is None else val
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# ── artifact-dir resolution ───────────────────────────────────────────────────
|
|
61
|
+
def artifact_root(root) -> Path:
|
|
62
|
+
"""The artifact dir to WRITE into for ``root`` — always ``.caddis``.
|
|
63
|
+
|
|
64
|
+
Returns a path that may not exist yet — callers ``mkdir`` as needed.
|
|
65
|
+
"""
|
|
66
|
+
return Path(root) / ARTIFACT_DIR
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def artifact_dir_name(root) -> str:
|
|
70
|
+
"""Just the directory NAME the repo lives in (``".caddis"``) — for messages."""
|
|
71
|
+
return artifact_root(root).name
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def artifact_read(root, *parts) -> Path:
|
|
75
|
+
"""``.caddis/<parts>`` — the path to read an artifact from."""
|
|
76
|
+
return artifact_root(root).joinpath(*parts)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def artifact_write(root, *parts) -> Path:
|
|
80
|
+
"""Path under :func:`artifact_root` — where a NEW artifact should be written."""
|
|
81
|
+
return artifact_root(root).joinpath(*parts)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def load_config(root, section: str) -> dict:
|
|
85
|
+
"""Return the ``[section]`` table from the repo's ``.caddis/config.toml``, or ``{}`` on any problem."""
|
|
86
|
+
try:
|
|
87
|
+
import tomllib # Python 3.11+ stdlib
|
|
88
|
+
except Exception:
|
|
89
|
+
return {}
|
|
90
|
+
cfg = artifact_read(root, "config.toml")
|
|
91
|
+
if not cfg.is_file():
|
|
92
|
+
return {}
|
|
93
|
+
try:
|
|
94
|
+
with open(cfg, "rb") as fh:
|
|
95
|
+
data = tomllib.load(fh)
|
|
96
|
+
except Exception:
|
|
97
|
+
return {}
|
|
98
|
+
sec = data.get(section, {})
|
|
99
|
+
return sec if isinstance(sec, dict) else {}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def get_int(cfg: dict, key: str, default: int) -> int:
|
|
103
|
+
"""A positive int from ``cfg[key]``, else ``default`` (rejects bool, non-int, and < 1)."""
|
|
104
|
+
v = cfg.get(key, default)
|
|
105
|
+
if isinstance(v, bool) or not isinstance(v, int) or v < 1:
|
|
106
|
+
return default
|
|
107
|
+
return v
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def get_str(cfg: dict, key: str, default: str) -> str:
|
|
111
|
+
"""A non-empty string from ``cfg[key]``, else ``default``."""
|
|
112
|
+
v = cfg.get(key, default)
|
|
113
|
+
return v if isinstance(v, str) and v.strip() else default
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def get_str_list(cfg: dict, key: str, default: list[str]) -> list[str]:
|
|
117
|
+
"""A list-of-strings from ``cfg[key]``, else ``default``."""
|
|
118
|
+
v = cfg.get(key, default)
|
|
119
|
+
if isinstance(v, list) and all(isinstance(x, str) for x in v):
|
|
120
|
+
return v
|
|
121
|
+
return default
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"""claudster_doctor — read-only health + maintenance report for a caddis install, any harness.
|
|
2
|
+
|
|
3
|
+
Diagnoses the things that silently break a cross-harness setup: a harness binary missing from PATH
|
|
4
|
+
(the Windows "new terminal" gotcha), a harness version that has drifted past the probed contract,
|
|
5
|
+
rules-file integrity (root AGENTS.md present; CLAUDE.md is a shim, not a fork), the user-level skills
|
|
6
|
+
path, and python for the tooling itself. It also computes DETERMINISTIC maintenance signals (no LLM):
|
|
7
|
+
an oversize always-loaded AGENTS.md, dangling DOC-MAP links, harness-contract version drift, and days
|
|
8
|
+
since the last doctor run. Read-only; exit 1 on a hard failure.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
python claudster_doctor.py # check cwd + all known harnesses
|
|
12
|
+
python claudster_doctor.py --dest <proj> # check a specific project's rules
|
|
13
|
+
python claudster_doctor.py --quiet # only the one-line maintenance nudge (for SessionStart)
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import shutil
|
|
21
|
+
import subprocess
|
|
22
|
+
import sys
|
|
23
|
+
from datetime import datetime, timezone
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
# Probed contract versions (docs/analysis/*-contract.md). WARN when the installed binary drifts past
|
|
27
|
+
# these — the recorded schemas may be stale and want a re-probe.
|
|
28
|
+
PROBED_VERSIONS = {"codex": "0.137", "agy": "1.1.5"}
|
|
29
|
+
AGENTS_MD_BUDGET = 200 # always-loaded rules file line budget (mirrors check_doc_coverage)
|
|
30
|
+
|
|
31
|
+
# Per-repo artifact dir + env prefix, mirrored from claude-harness/scripts/claudster_config.py —
|
|
32
|
+
# the doctor is a standalone, import-free diagnostic that must run from a bare checkout.
|
|
33
|
+
ARTIFACT_DIRS = (".caddis",)
|
|
34
|
+
ENV_PREFIX = "CADDIS"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _home() -> Path:
|
|
38
|
+
return Path(os.environ.get(f"{ENV_PREFIX}_FAKE_HOME") or Path.home())
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _user_scope_path(name: str) -> Path:
|
|
42
|
+
"""A ``~/.caddis/<name>`` user-scope file."""
|
|
43
|
+
return _home() / ".caddis" / name
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _now() -> datetime:
|
|
47
|
+
return datetime.now(timezone.utc)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _which(name: str) -> str | None:
|
|
51
|
+
hit = shutil.which(name)
|
|
52
|
+
if hit:
|
|
53
|
+
return hit
|
|
54
|
+
# off-PATH but installed (the agy case on Windows): probe the known install dir
|
|
55
|
+
if name == "agy":
|
|
56
|
+
cand = _home() / "AppData" / "Local" / "agy" / "bin" / "agy.exe"
|
|
57
|
+
if cand.is_file():
|
|
58
|
+
return str(cand)
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _binary_version(path: str) -> str | None:
|
|
63
|
+
try:
|
|
64
|
+
out = subprocess.run([path, "--version"], capture_output=True, text=True, timeout=20)
|
|
65
|
+
return (out.stdout or out.stderr).strip().splitlines()[0] if (out.stdout or out.stderr) else None
|
|
66
|
+
except Exception:
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# ── rules integrity (self-contained fork detector — same rule as claudster_migrate_rules --check) ──
|
|
71
|
+
_SKIP_DIRS = {".git", "node_modules", ".venv", "venv", "__pycache__", "dist", "build", "done", "archive"}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _is_shim(text: str) -> bool:
|
|
75
|
+
return any(ln.strip() == "@AGENTS.md" for ln in text.splitlines())
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def rules_findings(dest: Path) -> list[str]:
|
|
79
|
+
"""Fork detector: a non-shim CLAUDE.md beside an AGENTS.md, or a bare CLAUDE.md with no sibling."""
|
|
80
|
+
dest = Path(dest)
|
|
81
|
+
out: list[str] = []
|
|
82
|
+
for dirpath, dirnames, filenames in os.walk(dest):
|
|
83
|
+
dirnames[:] = [d for d in dirnames if d not in _SKIP_DIRS]
|
|
84
|
+
if "CLAUDE.md" not in filenames:
|
|
85
|
+
continue
|
|
86
|
+
d = Path(dirpath)
|
|
87
|
+
rel = (d / "CLAUDE.md").relative_to(dest).as_posix()
|
|
88
|
+
try:
|
|
89
|
+
text = (d / "CLAUDE.md").read_text(encoding="utf-8", errors="replace")
|
|
90
|
+
except OSError:
|
|
91
|
+
continue
|
|
92
|
+
if "AGENTS.md" in filenames:
|
|
93
|
+
if not _is_shim(text):
|
|
94
|
+
out.append(f"{rel}: not a shim but a sibling AGENTS.md exists (drifted shim)")
|
|
95
|
+
else:
|
|
96
|
+
out.append(f"{rel}: bare CLAUDE.md with no sibling AGENTS.md (fork — run /caddis:add-rules)")
|
|
97
|
+
return out
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def oversize_rules_files(dest: Path, budget: int = AGENTS_MD_BUDGET) -> list[tuple[str, int]]:
|
|
101
|
+
"""Always-loaded rules files (AGENTS.md, or CLAUDE.md pre-migration) over the line budget."""
|
|
102
|
+
dest = Path(dest)
|
|
103
|
+
out: list[tuple[str, int]] = []
|
|
104
|
+
for dirpath, dirnames, filenames in os.walk(dest):
|
|
105
|
+
dirnames[:] = [d for d in dirnames if d not in _SKIP_DIRS]
|
|
106
|
+
d = Path(dirpath)
|
|
107
|
+
f = "AGENTS.md" if "AGENTS.md" in filenames else ("CLAUDE.md" if "CLAUDE.md" in filenames else None)
|
|
108
|
+
if not f:
|
|
109
|
+
continue
|
|
110
|
+
try:
|
|
111
|
+
n = len((d / f).read_text(encoding="utf-8", errors="replace").splitlines())
|
|
112
|
+
except OSError:
|
|
113
|
+
continue
|
|
114
|
+
if n > budget:
|
|
115
|
+
out.append(((d / f).relative_to(dest).as_posix(), n))
|
|
116
|
+
return out
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _severity_word(ratio: float) -> str:
|
|
120
|
+
"""Coarse severity label so a 1.1x overage doesn't read identically to a 5x one
|
|
121
|
+
(register 0e: the nudge used to print the exact same sentence for both)."""
|
|
122
|
+
if ratio >= 3.0:
|
|
123
|
+
return "way"
|
|
124
|
+
if ratio >= 1.5:
|
|
125
|
+
return "well"
|
|
126
|
+
return "slightly"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def oversize_message(rel_path: str, lines: int, budget: int = AGENTS_MD_BUDGET) -> str:
|
|
130
|
+
"""One-line, severity-scaled description of an oversize rules file. Shared by the
|
|
131
|
+
SessionStart/PreCompact nudge (nudge_line) and the PostToolUse re-warn
|
|
132
|
+
(hooks/rules_budget_nudge.py) so both read identically and the threshold lives in
|
|
133
|
+
exactly one place: ``AGENTS_MD_BUDGET`` above (mirrored in check_doc_coverage.py)."""
|
|
134
|
+
ratio = lines / budget if budget else 0.0
|
|
135
|
+
return (f"AGENTS.md {_severity_word(ratio)} over budget "
|
|
136
|
+
f"({rel_path}: {lines} lines, {ratio:.1f}x the {budget}-line budget) — run claude-md-curator")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def docmap_dangling(dest: Path) -> list[str]:
|
|
140
|
+
"""Dangling `.md` links in <artifact-dir>/kb/DOC-MAP.md (the '/caddis:kb' signal)."""
|
|
141
|
+
dm = next((p for p in (Path(dest) / n / "kb" / "DOC-MAP.md" for n in ARTIFACT_DIRS) if p.is_file()), None)
|
|
142
|
+
if dm is None:
|
|
143
|
+
return []
|
|
144
|
+
import re
|
|
145
|
+
text = dm.read_text(encoding="utf-8", errors="replace")
|
|
146
|
+
text = re.sub(r"\A---\r?\n.*?\r?\n---\r?\n", "", text, flags=re.DOTALL)
|
|
147
|
+
text = re.sub(r"<!--.*?-->", "", text, flags=re.DOTALL)
|
|
148
|
+
dangling = []
|
|
149
|
+
for target in re.findall(r"\]\(([^)]+\.md)\)", text):
|
|
150
|
+
target = target.strip()
|
|
151
|
+
if target.startswith(("http://", "https://")):
|
|
152
|
+
continue
|
|
153
|
+
resolved = (dm.parent / target).resolve()
|
|
154
|
+
if not resolved.exists():
|
|
155
|
+
dangling.append(target)
|
|
156
|
+
return dangling
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# ── maintenance signals + nudge ──────────────────────────────────────────────
|
|
160
|
+
def _last_doctor_path() -> Path:
|
|
161
|
+
return _user_scope_path("last-doctor.json")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def days_since_last_doctor() -> int | None:
|
|
165
|
+
p = _last_doctor_path()
|
|
166
|
+
if not p.is_file():
|
|
167
|
+
return None
|
|
168
|
+
try:
|
|
169
|
+
ts = json.loads(p.read_text(encoding="utf-8")).get("timestamp")
|
|
170
|
+
last = datetime.fromisoformat(ts)
|
|
171
|
+
return (_now() - last).days
|
|
172
|
+
except Exception:
|
|
173
|
+
return None
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _stamp_last_doctor() -> None:
|
|
177
|
+
p = _last_doctor_path()
|
|
178
|
+
p.parent.mkdir(parents=True, exist_ok=True)
|
|
179
|
+
p.write_text(json.dumps({"timestamp": _now().isoformat(timespec="seconds")}) + "\n", encoding="utf-8")
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def version_drift() -> list[str]:
|
|
183
|
+
out = []
|
|
184
|
+
for name, probed in PROBED_VERSIONS.items():
|
|
185
|
+
path = _which(name)
|
|
186
|
+
if not path:
|
|
187
|
+
continue
|
|
188
|
+
ver = _binary_version(path) or ""
|
|
189
|
+
# crude: if the probed version string isn't a substring of the reported version, it drifted
|
|
190
|
+
digits = "".join(c for c in ver if c.isdigit() or c == ".")
|
|
191
|
+
if probed not in digits and digits:
|
|
192
|
+
out.append(f"{name} {digits.strip('.')} vs probed {probed} — re-probe the contract")
|
|
193
|
+
return out
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _is_caddis_source(dest: Path) -> bool:
|
|
197
|
+
"""True for the caddis authoring repo, where `.github/tools/` is the MASTER, not a copy.
|
|
198
|
+
|
|
199
|
+
Without this the drift check fires permanently in the one repo where a difference is the
|
|
200
|
+
normal state — and a check that is always red is a check nobody reads."""
|
|
201
|
+
return (dest / "claude-harness").is_dir()
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _norm(text: str) -> str:
|
|
205
|
+
return text.replace("\r\n", "\n").strip()
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def vendored_drift(dest: Path, plugin_root: Path | None = None) -> list[str]:
|
|
209
|
+
"""Vendored `.github/tools/*.py` copies that no longer match the plugin's shipped copy.
|
|
210
|
+
|
|
211
|
+
WHY THIS EXISTS
|
|
212
|
+
---------------
|
|
213
|
+
`/caddis:cross-review` resolved its tool as `.github/tools/oss_review.py` FIRST and the plugin
|
|
214
|
+
copy second, with nothing comparing them. On 2026-08-10 a repo ran a stale vendored
|
|
215
|
+
`oss_review.py` and got a false CLEAN on a database write path; the fixed copy was sitting
|
|
216
|
+
unused in the plugin the whole time. The bug had been fixed on 2026-08-01 and the fix simply
|
|
217
|
+
never reached the caller.
|
|
218
|
+
|
|
219
|
+
That is worse than an ordinary stale file. It means **any** caddis fix is untrustworthy in a
|
|
220
|
+
repo that vendored early, and nothing anywhere reports the skew. The vendoring itself was
|
|
221
|
+
reasonable when it happened — the plugin did not ship these scripts until 2026-07-30 — which is
|
|
222
|
+
exactly why the copies are still lying around.
|
|
223
|
+
|
|
224
|
+
Pure file comparison, no subprocess, safe for SessionStart. Returns [] when there is nothing to
|
|
225
|
+
compare against (no plugin root, no vendored dir) — silence, not a complaint.
|
|
226
|
+
"""
|
|
227
|
+
# The SessionStart hook passes ROOT as a str, not a Path. Coerce rather than assume — the
|
|
228
|
+
# first version of this function assumed Path and broke the hook's own regression test.
|
|
229
|
+
dest = Path(dest)
|
|
230
|
+
if _is_caddis_source(dest):
|
|
231
|
+
return []
|
|
232
|
+
root = plugin_root or (Path(os.environ["CLAUDE_PLUGIN_ROOT"])
|
|
233
|
+
if os.environ.get("CLAUDE_PLUGIN_ROOT") else None)
|
|
234
|
+
if root is None:
|
|
235
|
+
return []
|
|
236
|
+
tools = dest / ".github" / "tools"
|
|
237
|
+
if not tools.is_dir():
|
|
238
|
+
return []
|
|
239
|
+
out: list[str] = []
|
|
240
|
+
for local in sorted(tools.glob("*.py")):
|
|
241
|
+
shipped = root / "scripts" / local.name
|
|
242
|
+
if not shipped.is_file():
|
|
243
|
+
continue
|
|
244
|
+
try:
|
|
245
|
+
if _norm(local.read_text(encoding="utf-8", errors="ignore")) != \
|
|
246
|
+
_norm(shipped.read_text(encoding="utf-8", errors="ignore")):
|
|
247
|
+
out.append(f".github/tools/{local.name} differs from the caddis copy — "
|
|
248
|
+
"probably a stale vendor; delete it or keep the change deliberately")
|
|
249
|
+
except OSError:
|
|
250
|
+
continue
|
|
251
|
+
return out
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _file_signals(dest: Path) -> list[str]:
|
|
255
|
+
"""PURE FILE-CHECK signals only (no subprocess) — safe + fast for SessionStart. One line each."""
|
|
256
|
+
signals: list[str] = []
|
|
257
|
+
over = oversize_rules_files(dest)
|
|
258
|
+
if over:
|
|
259
|
+
worst = max(over, key=lambda x: x[1])
|
|
260
|
+
signals.append(oversize_message(worst[0], worst[1]))
|
|
261
|
+
dangling = docmap_dangling(dest)
|
|
262
|
+
if dangling:
|
|
263
|
+
signals.append(f"{len(dangling)} dangling DOC-MAP link(s) — run /caddis:kb")
|
|
264
|
+
days = days_since_last_doctor()
|
|
265
|
+
if days is not None and days >= 30:
|
|
266
|
+
signals.append(f"{days} days since last caddis doctor — run caddis-init --doctor")
|
|
267
|
+
# Listed LAST but it is the most dangerous signal here: the others degrade a session's quality,
|
|
268
|
+
# this one silently runs old code and reports success. It is cheap (a file compare) and almost
|
|
269
|
+
# always empty, so it costs nothing to carry.
|
|
270
|
+
signals += vendored_drift(dest)
|
|
271
|
+
return signals
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def maintenance_signals(dest: Path) -> list[str]:
|
|
275
|
+
"""All deterministic signals (file checks + harness-version drift). For the full doctor report."""
|
|
276
|
+
return _file_signals(dest) + version_drift()
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def nudge_line(dest: Path) -> str | None:
|
|
280
|
+
"""The single SessionStart nudge line, or None. PURE FILE CHECKS only (no subprocess, no LLM, no
|
|
281
|
+
auto-fix) so it never slows session start; curator/kb stay human-triggered."""
|
|
282
|
+
sig = _file_signals(dest)
|
|
283
|
+
if not sig:
|
|
284
|
+
return None
|
|
285
|
+
head = sig[0]
|
|
286
|
+
more = f" (+{len(sig) - 1} more — run caddis doctor)" if len(sig) > 1 else ""
|
|
287
|
+
return f"[caddis] {head}{more}"
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
# ── report ───────────────────────────────────────────────────────────────────
|
|
291
|
+
def run(dest: Path, quiet: bool) -> int:
|
|
292
|
+
if quiet: # SessionStart mode: only the nudge, never non-zero
|
|
293
|
+
line = nudge_line(dest)
|
|
294
|
+
if line:
|
|
295
|
+
print(line)
|
|
296
|
+
return 0
|
|
297
|
+
|
|
298
|
+
hard = 0
|
|
299
|
+
print(f"=== caddis doctor — {dest} ===")
|
|
300
|
+
print(f"python: {sys.version.split()[0]} ({sys.executable})")
|
|
301
|
+
for name in ("codex", "agy", "claude"):
|
|
302
|
+
path = _which(name)
|
|
303
|
+
if path:
|
|
304
|
+
ver = _binary_version(path) or "?"
|
|
305
|
+
print(f" {name:7} OK {ver} [{path}]")
|
|
306
|
+
else:
|
|
307
|
+
print(f" {name:7} not found on PATH (install, or open a new terminal so PATH refreshes)")
|
|
308
|
+
|
|
309
|
+
print("-- rules integrity")
|
|
310
|
+
findings = rules_findings(dest)
|
|
311
|
+
if findings:
|
|
312
|
+
hard = 1
|
|
313
|
+
for f in findings:
|
|
314
|
+
print(f" FAIL {f}")
|
|
315
|
+
else:
|
|
316
|
+
root = dest / "AGENTS.md"
|
|
317
|
+
print(f" OK root AGENTS.md {'present' if root.is_file() else 'ABSENT (not migrated yet?)'}; shims are shims")
|
|
318
|
+
|
|
319
|
+
print("-- maintenance signals (deterministic — read-only)")
|
|
320
|
+
sig = maintenance_signals(dest)
|
|
321
|
+
if sig:
|
|
322
|
+
for s in sig:
|
|
323
|
+
print(f" ~ {s}")
|
|
324
|
+
else:
|
|
325
|
+
print(" none")
|
|
326
|
+
|
|
327
|
+
print("-- install registry")
|
|
328
|
+
reg = _user_scope_path("installs.json")
|
|
329
|
+
if reg.is_file():
|
|
330
|
+
try:
|
|
331
|
+
installs = json.loads(reg.read_text(encoding="utf-8")).get("installs", [])
|
|
332
|
+
vers = {e.get("version") for e in installs}
|
|
333
|
+
print(f" {len(installs)} install(s); versions: {sorted(v for v in vers if v)}"
|
|
334
|
+
+ (" ⚠ skew" if len([v for v in vers if v]) > 1 else ""))
|
|
335
|
+
except Exception:
|
|
336
|
+
print(" (registry unreadable)")
|
|
337
|
+
else:
|
|
338
|
+
print(" no installs recorded")
|
|
339
|
+
|
|
340
|
+
_stamp_last_doctor()
|
|
341
|
+
print(f"\n{'FAIL — fix the above.' if hard else 'OK.'}")
|
|
342
|
+
return hard
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def main(argv: list[str] | None = None) -> int:
|
|
346
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
347
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
348
|
+
ap = argparse.ArgumentParser(description="caddis health + maintenance report (read-only).")
|
|
349
|
+
ap.add_argument("--dest", default=".", help="Project dir whose rules to check (default: cwd).")
|
|
350
|
+
ap.add_argument("--quiet", action="store_true", help="Print only the one-line maintenance nudge (SessionStart).")
|
|
351
|
+
args = ap.parse_args(argv)
|
|
352
|
+
return run(Path(args.dest).resolve(), args.quiet)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
if __name__ == "__main__":
|
|
356
|
+
raise SystemExit(main())
|