cc-safe-setup 29.8.0 → 30.0.5
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/.claude-plugin/marketplace.json +72 -19
- package/.claude-plugin/plugin.json +1 -1
- package/CONTRIBUTING.md +7 -2
- package/COOKBOOK.md +50 -46
- package/LICENSE +21 -0
- package/MIGRATION.md +8 -8
- package/README.md +421 -512
- package/SAFETY_CHECKLIST.md +15 -7
- package/SERVICES.md +194 -0
- package/SETTINGS_REFERENCE.md +236 -17
- package/SKILL.md +5 -5
- package/TROUBLESHOOTING.md +35 -14
- package/action.yml +12 -4
- package/audit/README.md +84 -0
- package/audit/audit-checklist.md +152 -0
- package/audit/boundary-selftest.sh +85 -0
- package/audit/boundary.sh +123 -0
- package/audit/count-hooks.py +42 -0
- package/audit/find-dead-hooks.sh +82 -0
- package/audit/find-unfirable-rules.sh +142 -0
- package/audit/fire.sh +97 -0
- package/audit/selftest.sh +42 -0
- package/audit/unfirable-selftest.sh +81 -0
- package/audit-web/index.html +9 -9
- package/examples/README.md +54 -17
- package/examples/account-billing-log.sh +98 -0
- package/examples/account-routing-preflight.sh +123 -0
- package/examples/agent-sdk-credit-pool-monitor.sh +145 -0
- package/examples/agent-view-quota-decrement.sh +32 -0
- package/examples/agent-view-quota-warn.sh +94 -0
- package/examples/agents-md-edit-drift-warner.sh +223 -0
- package/examples/agents-md-loader.sh +160 -0
- package/examples/agents-md-sync-checker.sh +102 -0
- package/examples/ai-slop-punctuation-arrest.sh +210 -0
- package/examples/allowlist.sh +70 -10
- package/examples/always-allow-pattern-suggester.sh +125 -0
- package/examples/ansible-vault-guard.sh +2 -1
- package/examples/anthropic-base-url-guard.sh +76 -0
- package/examples/api-busyloop-guard.sh +123 -0
- package/examples/api-endpoint-guard.sh +12 -0
- package/examples/api-key-in-url-guard.sh +17 -1
- package/examples/api-rate-limit-guard.sh +36 -1
- package/examples/api-rate-limit-tracker.sh +12 -0
- package/examples/articulated-scope-capture.sh +101 -0
- package/examples/askuserquestion-autonomy-gate.sh +102 -0
- package/examples/attachment-traversal-guard.sh +118 -0
- package/examples/aup-block-pattern-logger.sh +161 -0
- package/examples/aup-false-positive-helper.sh +124 -0
- package/examples/aup-large-tool-output-warner.sh +282 -0
- package/examples/aup-retry-loop-guard.sh +229 -0
- package/examples/auth-expiry-reminder.sh +157 -0
- package/examples/auth-macos-sleep-detector.sh +185 -0
- package/examples/auth-path-detector.sh +171 -0
- package/examples/auth-status-checker.sh +157 -0
- package/examples/authorized-reconfirmation-detector.sh +195 -0
- package/examples/auto-approve-build.sh +39 -0
- package/examples/auto-approve-cargo.sh +31 -1
- package/examples/auto-approve-docker.sh +35 -0
- package/examples/auto-approve-git-read.sh +37 -14
- package/examples/auto-approve-go.sh +30 -1
- package/examples/auto-approve-gradle.sh +29 -1
- package/examples/auto-approve-make.sh +29 -1
- package/examples/auto-approve-maven.sh +29 -1
- package/examples/auto-approve-python.sh +41 -1
- package/examples/auto-approve-readonly-tools.sh +1 -0
- package/examples/auto-approve-readonly.sh +110 -33
- package/examples/auto-approve-ssh.sh +30 -1
- package/examples/auto-approve-test.sh +44 -0
- package/examples/auto-checkpoint.sh +1 -1
- package/examples/auto-compact-prep.sh +0 -0
- package/examples/auto-git-checkpoint.sh +0 -0
- package/examples/auto-mode-safe-commands.sh +53 -1
- package/examples/auto-mode-safety-enforcer.sh +151 -4
- package/examples/auto-push-session-branch.sh +22 -0
- package/examples/aws-production-guard.sh +13 -1
- package/examples/aws-region-guard.sh +1 -1
- package/examples/background-cost-launch-guard.sh +82 -0
- package/examples/background-task-guard.sh +12 -0
- package/examples/backup-before-refactor.sh +0 -0
- package/examples/banned-command-guard.sh +12 -0
- package/examples/bash-allowlist-secondary-check.sh +157 -0
- package/examples/bash-domain-allowlist.sh +12 -0
- package/examples/bash-fanout-bounded-rewriter.sh +191 -0
- package/examples/bash-heuristic-approver.sh +112 -10
- package/examples/bash-project-fence.sh +194 -0
- package/examples/bash-safety-auto-deny.sh +12 -0
- package/examples/bash-secret-output-detector.sh +44 -3
- package/examples/bash-trace-guard.sh +12 -0
- package/examples/bashrc-safety-check.sh +0 -0
- package/examples/bg-permission-prompt-warner.sh +182 -0
- package/examples/bg-task-cooldown-guard.sh +12 -0
- package/examples/binary-read-detector.sh +135 -0
- package/examples/binary-upload-guard.sh +12 -0
- package/examples/block-database-wipe.sh +35 -10
- package/examples/branch-name-check.sh +8 -3
- package/examples/branch-naming-convention.sh +0 -0
- package/examples/broad-find-guard.sh +12 -0
- package/examples/broad-prefix-session-trap-warner.sh +106 -0
- package/examples/bulk-file-delete-guard.sh +45 -0
- package/examples/bypass-mode-effective-verifier.sh +203 -0
- package/examples/cache-creation-drift-detector.sh +120 -0
- package/examples/cache-residue-detector.sh +195 -0
- package/examples/cache-tier-logger.sh +70 -0
- package/examples/cache-ttl-eviction-detector.sh +149 -0
- package/examples/cargo-publish-guard.sh +14 -1
- package/examples/case-insensitive-path-guard.sh +21 -2
- package/examples/case-sensitive-guard.sh +23 -2
- package/examples/cch-cache-guard.sh +0 -0
- package/examples/cch-sentinel-precommit-guard.sh +47 -0
- package/examples/check-abort-controller.sh +1 -1
- package/examples/check-accessibility.sh +0 -0
- package/examples/check-aria-labels.sh +0 -0
- package/examples/check-before-act-enforcer.sh +12 -0
- package/examples/check-charset-meta.sh +0 -0
- package/examples/check-cookie-flags.sh +0 -0
- package/examples/check-cors-config.sh +0 -0
- package/examples/check-csp-headers.sh +0 -0
- package/examples/check-csrf-protection.sh +0 -0
- package/examples/check-debounce.sh +1 -1
- package/examples/check-dependency-age.sh +0 -0
- package/examples/check-dependency-license.sh +0 -0
- package/examples/check-dockerfile-best-practice.sh +0 -0
- package/examples/check-error-boundaries.sh +0 -0
- package/examples/check-error-class.sh +1 -1
- package/examples/check-error-handling.sh +0 -0
- package/examples/check-error-logging.sh +1 -1
- package/examples/check-error-message.sh +0 -0
- package/examples/check-error-stack.sh +0 -0
- package/examples/check-favicon.sh +0 -0
- package/examples/check-git-hooks-compat.sh +0 -0
- package/examples/check-gitattributes.sh +0 -0
- package/examples/check-https-redirect.sh +0 -0
- package/examples/check-input-validation.sh +0 -0
- package/examples/check-lang-attribute.sh +0 -0
- package/examples/check-npm-scripts-exist.sh +0 -0
- package/examples/check-package-size.sh +0 -0
- package/examples/check-port-availability.sh +0 -0
- package/examples/check-promise-all.sh +1 -1
- package/examples/check-rate-limiting.sh +0 -0
- package/examples/check-return-types.sh +0 -0
- package/examples/check-semantic-versioning.sh +0 -0
- package/examples/check-suspense-fallback.sh +1 -1
- package/examples/check-test-exists.sh +0 -0
- package/examples/check-test-naming.sh +0 -0
- package/examples/check-timeout-cleanup.sh +1 -1
- package/examples/check-tls-version.sh +0 -0
- package/examples/check-unsubscribe.sh +1 -1
- package/examples/check-viewport-meta.sh +0 -0
- package/examples/check-worker-terminate.sh +1 -1
- package/examples/checkpoint-tamper-guard.sh +12 -0
- package/examples/chmod-guard.sh +12 -0
- package/examples/chown-guard.sh +24 -0
- package/examples/ci-workflow-guard.sh +6 -1
- package/examples/claim-verify-detector.sh +109 -0
- package/examples/claim-vs-caveat-checker.sh +77 -0
- package/examples/classifier-fallback-allow.sh +114 -40
- package/examples/claude-cache-gc.sh +1 -0
- package/examples/claude-code-version-pin-advisor.sh +183 -0
- package/examples/claude-md-reinjector.sh +129 -0
- package/examples/claude-md-size-monitor.sh +66 -0
- package/examples/claude-update-budget-guard.sh +123 -0
- package/examples/claude-update-smart.sh +113 -0
- package/examples/claudeignore-enforce-guard.sh +12 -0
- package/examples/claudemd-enforcer.sh +0 -0
- package/examples/claudemd-tool-prohibition.sh +128 -0
- package/examples/clear-command-confirm-guard.sh +32 -15
- package/examples/cli-config-pinning-detector.sh +162 -0
- package/examples/cliff-countdown-advisor.sh +96 -0
- package/examples/clipboard-secret-guard.sh +12 -0
- package/examples/closure-word-verify-gate.sh +150 -0
- package/examples/cloud-cli-guard.sh +39 -2
- package/examples/commit-all-scope-guard.sh +44 -0
- package/examples/commit-message-check.sh +1 -1
- package/examples/commit-message-quality.sh +6 -1
- package/examples/commitment-carry-forward-arrest.sh +361 -0
- package/examples/compact-alert-notification.sh +0 -0
- package/examples/compact-dispatch-watchdog.sh +149 -0
- package/examples/completion-claim-without-verification-detector.sh +207 -0
- package/examples/composer-guard.sh +12 -0
- package/examples/compound-bash-permission-resolver.sh +156 -0
- package/examples/compound-command-allow.sh +1 -1
- package/examples/compound-command-deny-enforcer.sh +183 -0
- package/examples/compound-inject-guard.sh +12 -0
- package/examples/concurrent-edit-lock.sh +12 -0
- package/examples/conflict-marker-guard.sh +12 -0
- package/examples/consecutive-error-breaker.sh +0 -0
- package/examples/console-log-count.sh +1 -0
- package/examples/context-compact-advisor.sh +29 -2
- package/examples/context-length-budget.sh +69 -0
- package/examples/context-threshold-alert.sh +12 -0
- package/examples/context-usage-drift-alert.sh +41 -13
- package/examples/conversation-history-guard.sh +12 -0
- package/examples/core-file-protect-guard.sh +12 -0
- package/examples/cors-star-warn.sh +0 -0
- package/examples/cost-incident-self-audit.sh +162 -0
- package/examples/cowork-claude-md-load-checker.sh +100 -0
- package/examples/cowork-fuse-staleness-watcher.sh +140 -0
- package/examples/cowork-hook-absence-warner.sh +167 -0
- package/examples/cowork-model-picker-advisor.sh +115 -0
- package/examples/credential-exfil-guard.sh +65 -4
- package/examples/credential-file-cat-guard.sh +17 -1
- package/examples/cron-create-receipt.sh +112 -0
- package/examples/cron-modification-guard.sh +12 -0
- package/examples/cross-product-mcp-spawn-detector.sh +178 -0
- package/examples/cwd-project-boundary-guard.sh +0 -0
- package/examples/daemon-zombie-worker-surfacer.sh +118 -0
- package/examples/daily-cost-guard.sh +164 -0
- package/examples/daily-usage-tracker.sh +0 -0
- package/examples/dangerous-pip-flag-guard.sh +12 -0
- package/examples/db-connect-guard.sh +13 -1
- package/examples/denied-action-retry-guard.sh +12 -0
- package/examples/deny-bypass-detector.sh +12 -0
- package/examples/deny-rule-integrity-verifier.sh +181 -0
- package/examples/dependency-install-guard.sh +12 -0
- package/examples/deploy-guard.sh +14 -2
- package/examples/deploy-path-verify-guard.sh +12 -0
- package/examples/deployment-readback-gate.sh +135 -0
- package/examples/deployment-readback-gh-adapter.sh +247 -0
- package/examples/deployment-verify-guard.sh +25 -2
- package/examples/destructive-db-script-write-guard.sh +166 -0
- package/examples/destructive-migration-write-guard.sh +163 -0
- package/examples/diff-size-guard.sh +12 -0
- package/examples/disabled-feature-toggle-advisor.sh +88 -0
- package/examples/disk-partition-guard.sh +12 -0
- package/examples/dispatch-allowlist-preflight.sh +301 -0
- package/examples/dispatch-liveness-watchdog.sh +216 -0
- package/examples/dispatch-receipt.sh +174 -0
- package/examples/django-migrate-guard.sh +12 -0
- package/examples/dns-config-guard.sh +12 -0
- package/examples/docker-dangerous-guard.sh +12 -0
- package/examples/docker-volume-guard.sh +0 -0
- package/examples/dockerfile-latest-guard.sh +1 -0
- package/examples/dotenv-anthropic-key-billing-guard.sh +99 -0
- package/examples/dotenv-commit-guard.sh +12 -0
- package/examples/dotenv-read-guard.sh +12 -0
- package/examples/dotfile-protection-guard.sh +12 -0
- package/examples/dotnet-build-on-edit.sh +1 -1
- package/examples/drizzle-migrate-guard.sh +27 -2
- package/examples/echo-probe-spam-detector.sh +176 -0
- package/examples/edit-counter-test-gate.sh +12 -0
- package/examples/edit-error-counter.sh +1 -1
- package/examples/edit-guard.sh +12 -0
- package/examples/edit-old-string-validator.sh +12 -0
- package/examples/edit-quote-ambiguity-guard.sh +97 -0
- package/examples/edit-retry-loop-guard.sh +0 -0
- package/examples/edit-verify.sh +0 -0
- package/examples/env-inline-secret-guard.sh +14 -2
- package/examples/env-naming-convention.sh +0 -0
- package/examples/env-prod-guard.sh +0 -0
- package/examples/env-required-check.sh +0 -0
- package/examples/env-source-guard.sh +12 -0
- package/examples/env-var-check.sh +24 -1
- package/examples/ephemeral-container-detector.sh +147 -0
- package/examples/error-memory-guard.sh +12 -0
- package/examples/evidence-claim-gate.sh +187 -0
- package/examples/exploration-budget-guard.sh +12 -0
- package/examples/expo-eject-guard.sh +13 -0
- package/examples/extended-thinking-loop-guard.sh +139 -0
- package/examples/extended-thinking-resume-warning.sh +119 -0
- package/examples/extended-thinking-tool-use-mismatch-detector.sh +212 -0
- package/examples/fabricated-command-detector.sh +145 -0
- package/examples/false-disk-full-guard.sh +63 -0
- package/examples/feature-deprecation-detector.sh +109 -0
- package/examples/file-change-tracker.sh +1 -1
- package/examples/file-change-undo-tracker.sh +0 -0
- package/examples/file-edit-backup.sh +63 -14
- package/examples/file-reference-check.sh +0 -0
- package/examples/file-size-limit.sh +12 -0
- package/examples/financial-operation-guard.sh +12 -0
- package/examples/firewall-guard.sh +12 -0
- package/examples/fish-shell-wrapper.sh +125 -7
- package/examples/five-hundred-milestone.sh +1 -0
- package/examples/flask-static-route-guard.sh +89 -0
- package/examples/forbidden-response-phrase-detector.sh +161 -0
- package/examples/gem-push-guard.sh +13 -0
- package/examples/gh-cli-destructive-guard.sh +13 -1
- package/examples/git-checkout-safety-guard.sh +12 -0
- package/examples/git-checkout-uncommitted-guard.sh +12 -0
- package/examples/git-config-guard.sh +12 -0
- package/examples/git-crypt-worktree-guard.sh +12 -0
- package/examples/git-filter-repo-guard.sh +56 -0
- package/examples/git-history-rewrite-guard.sh +12 -0
- package/examples/git-hook-bypass-guard.sh +0 -0
- package/examples/git-maintenance-guard.sh +83 -0
- package/examples/git-merge-conflict-prevent.sh +0 -0
- package/examples/git-message-length-check.sh +0 -0
- package/examples/git-message-length.sh +0 -0
- package/examples/git-operations-require-approval.sh +68 -14
- package/examples/git-pack-temp-cleanup-guard.sh +102 -0
- package/examples/git-push-blast-radius-guard.sh +218 -0
- package/examples/git-remote-guard.sh +24 -3
- package/examples/git-rm-orphan-wipe-guard.sh +85 -0
- package/examples/git-stash-before-checkout.sh +12 -0
- package/examples/git-stash-before-danger.sh +0 -0
- package/examples/git-submodule-guard.sh +69 -2
- package/examples/git-tag-guard.sh +12 -0
- package/examples/github-actions-secret-guard.sh +0 -0
- package/examples/gitignore-check.sh +0 -0
- package/examples/gitops-drift-guard.sh +12 -0
- package/examples/go-mod-tidy-warn.sh +1 -0
- package/examples/go-vet-after-edit.sh +12 -0
- package/examples/goal-iteration-limit-warner.sh +152 -0
- package/examples/grep-nul-false-negative-guard.sh +30 -0
- package/examples/growthbook-flag-monitor.sh +257 -0
- package/examples/hallucination-url-check.sh +1 -1
- package/examples/halt-signal-detector.sh +135 -0
- package/examples/handoff-on-clear.sh +65 -0
- package/examples/hardcoded-ip-guard.sh +1 -0
- package/examples/headless-empty-result-guard.sh +0 -0
- package/examples/headless-stop-guard.sh +0 -0
- package/examples/helm-install-guard.sh +2 -1
- package/examples/home-critical-bash-guard.sh +17 -1
- package/examples/hook-debug-wrapper.sh +6 -2
- package/examples/hook-permission-fixer.sh +0 -0
- package/examples/hook-self-disable-detector.sh +99 -0
- package/examples/hook-stdout-sanitizer.sh +6 -2
- package/examples/hook-tamper-guard.sh +12 -0
- package/examples/idle-session-cost-alert.sh +0 -0
- package/examples/image-dimension-guard.sh +109 -0
- package/examples/image-file-validator.sh +0 -0
- package/examples/iteration-quality-degradation-detector.sh +170 -0
- package/examples/java-compile-on-edit.sh +1 -1
- package/examples/json-syntax-check.sh +0 -0
- package/examples/k8s-production-guard.sh +13 -1
- package/examples/kill-process-guard.sh +12 -0
- package/examples/kubernetes-guard.sh +13 -1
- package/examples/laravel-artisan-guard.sh +13 -0
- package/examples/large-file-guard.sh +1 -1
- package/examples/log-level-guard.sh +0 -0
- package/examples/log-truncation-guard.sh +12 -0
- package/examples/long-session-malformed-tool-call-detector.sh +183 -0
- package/examples/loop-detector.sh +12 -0
- package/examples/magic-number-warn.sh +1 -0
- package/examples/main-branch-warn.sh +12 -0
- package/examples/markdown-link-check.sh +0 -0
- package/examples/max-concurrent-agents.sh +12 -0
- package/examples/max-edit-size-guard.sh +13 -0
- package/examples/max-file-count-guard.sh +0 -0
- package/examples/max-file-delete-count.sh +0 -0
- package/examples/max-function-length.sh +1 -0
- package/examples/max-import-count.sh +0 -0
- package/examples/max-subagent-count.sh +0 -0
- package/examples/mcp-config-freeze.sh +12 -0
- package/examples/mcp-config-poisoning-audit.sh +150 -0
- package/examples/mcp-data-boundary.sh +1 -1
- package/examples/mcp-misdiagnosis-arrest.sh +209 -0
- package/examples/mcp-orphan-leak-warner.sh +49 -0
- package/examples/mcp-permissions-ask-warner.sh +151 -0
- package/examples/mcp-routine-approval-detector.sh +154 -0
- package/examples/mcp-server-allowlist.sh +12 -0
- package/examples/mcp-server-guard.sh +12 -0
- package/examples/mcp-startup-bloat-detector.sh +87 -0
- package/examples/mcp-stdio-compatibility-test.sh +153 -0
- package/examples/mcp-tool-audit-log.sh +0 -0
- package/examples/mcp-tool-guard.sh +101 -1
- package/examples/mcp-warmup-wait.sh +0 -0
- package/examples/memory-chain-audit.sh +56 -0
- package/examples/memory-orphan-detector.sh +188 -0
- package/examples/memory-write-guard.sh +99 -17
- package/examples/migration-verify-guard.sh +12 -0
- package/examples/model-swap-suggester.sh +177 -0
- package/examples/model-version-alert.sh +5 -1
- package/examples/model-version-lock.sh +63 -0
- package/examples/move-delete-sequence-guard.sh +33 -2
- package/examples/multi-file-plan-routing-gate.sh +192 -0
- package/examples/multi-vendor-concurrent-warner.sh +108 -0
- package/examples/multi-window-auth-drift-detector.sh +183 -0
- package/examples/multiline-command-approver.sh +161 -23
- package/examples/nested-background-agent-guard.sh +109 -0
- package/examples/nested-spawn-inflight-guard.sh +178 -0
- package/examples/network-exfil-guard.sh +12 -0
- package/examples/network-guard.sh +12 -0
- package/examples/network-interface-guard.sh +12 -0
- package/examples/nextjs-env-guard.sh +1 -1
- package/examples/no-absolute-import.sh +0 -0
- package/examples/no-alert-confirm-prompt.sh +0 -0
- package/examples/no-anonymous-default-export.sh +0 -0
- package/examples/no-any-type.sh +0 -0
- package/examples/no-any-typescript.sh +1 -0
- package/examples/no-ask-human.sh +12 -0
- package/examples/no-assignment-in-condition.sh +0 -0
- package/examples/no-base64-exfil.sh +12 -0
- package/examples/no-callback-hell.sh +0 -0
- package/examples/no-circular-dependency.sh +0 -0
- package/examples/no-cleartext-storage.sh +0 -0
- package/examples/no-commented-code.sh +0 -0
- package/examples/no-commit-fixup.sh +0 -0
- package/examples/no-console-assert.sh +0 -0
- package/examples/no-console-error-swallow.sh +0 -0
- package/examples/no-console-in-prod.sh +0 -0
- package/examples/no-console-log-commit.sh +12 -0
- package/examples/no-console-log.sh +0 -0
- package/examples/no-console-time.sh +0 -0
- package/examples/no-cors-wildcard.sh +4 -1
- package/examples/no-curl-upload.sh +0 -0
- package/examples/no-dangerouslySetInnerHTML.sh +0 -0
- package/examples/no-dangling-await.sh +1 -0
- package/examples/no-debug-commit.sh +12 -0
- package/examples/no-debug-in-commit.sh +0 -0
- package/examples/no-deep-nesting.sh +0 -0
- package/examples/no-deep-relative-import.sh +1 -0
- package/examples/no-default-credentials.sh +0 -0
- package/examples/no-deploy-friday.sh +13 -1
- package/examples/no-disabled-test.sh +0 -0
- package/examples/no-document-write.sh +0 -0
- package/examples/no-empty-function.sh +0 -0
- package/examples/no-eval-in-template.sh +0 -0
- package/examples/no-eval-template.sh +3 -1
- package/examples/no-eval.sh +0 -0
- package/examples/no-exec-user-input.sh +0 -0
- package/examples/no-fixme-ship.sh +12 -0
- package/examples/no-floating-promises.sh +0 -0
- package/examples/no-force-flag.sh +22 -5
- package/examples/no-force-install.sh +0 -0
- package/examples/no-git-amend.sh +12 -0
- package/examples/no-git-rebase-public.sh +0 -0
- package/examples/no-global-install.sh +12 -0
- package/examples/no-global-state.sh +0 -0
- package/examples/no-hardcoded-port.sh +1 -0
- package/examples/no-hardcoded-url.sh +0 -0
- package/examples/no-helmet-missing.sh +0 -0
- package/examples/no-http-url.sh +1 -0
- package/examples/no-http-without-https.sh +0 -0
- package/examples/no-inline-style.sh +0 -0
- package/examples/no-inline-styles.sh +1 -0
- package/examples/no-innerhtml.sh +0 -0
- package/examples/no-install-global.sh +12 -0
- package/examples/no-jwt-in-url.sh +0 -0
- package/examples/no-large-commit.sh +0 -0
- package/examples/no-localhost-expose.sh +0 -0
- package/examples/no-magic-number.sh +0 -0
- package/examples/no-md5-sha1.sh +0 -0
- package/examples/no-mixed-line-endings.sh +8 -1
- package/examples/no-mutation-in-reducer.sh +0 -0
- package/examples/no-nested-ternary.sh +0 -0
- package/examples/no-network-exfil.sh +0 -0
- package/examples/no-open-redirect.sh +0 -0
- package/examples/no-output-truncation.sh +12 -0
- package/examples/no-package-downgrade.sh +0 -0
- package/examples/no-package-lock-edit.sh +12 -0
- package/examples/no-path-join-user-input.sh +0 -0
- package/examples/no-port-bind.sh +0 -0
- package/examples/no-process-exit.sh +0 -0
- package/examples/no-prototype-pollution.sh +0 -0
- package/examples/no-push-without-ci.sh +0 -0
- package/examples/no-push-without-tests.sh +12 -0
- package/examples/no-raw-password-in-url.sh +0 -0
- package/examples/no-root-user-docker.sh +1 -0
- package/examples/no-root-write.sh +12 -0
- package/examples/no-secrets-in-args.sh +4 -1
- package/examples/no-secrets-in-logs.sh +0 -0
- package/examples/no-sensitive-log.sh +0 -0
- package/examples/no-sleep-in-hooks.sh +0 -0
- package/examples/no-star-import-python.sh +1 -0
- package/examples/no-string-concat-sql.sh +0 -0
- package/examples/no-sudo-guard.sh +12 -0
- package/examples/no-sync-fs.sh +0 -0
- package/examples/no-todo-in-merge.sh +0 -0
- package/examples/no-todo-in-production.sh +1 -0
- package/examples/no-todo-without-issue.sh +0 -0
- package/examples/no-unused-import.sh +0 -0
- package/examples/no-var-keyword.sh +0 -0
- package/examples/no-verify-blocker.sh +12 -0
- package/examples/no-wget-piped-bash.sh +12 -0
- package/examples/no-wildcard-cors.sh +0 -0
- package/examples/no-wildcard-delete.sh +0 -0
- package/examples/no-wildcard-import.sh +0 -0
- package/examples/no-with-statement.sh +0 -0
- package/examples/no-write-outside-src.sh +0 -0
- package/examples/no-xml-external-entity.sh +0 -0
- package/examples/non-english-quality-warner.sh +156 -0
- package/examples/notify-waiting.sh +1 -1
- package/examples/npm-audit-warn.sh +0 -0
- package/examples/npm-global-install-guard.sh +12 -0
- package/examples/npm-publish-guard.sh +12 -0
- package/examples/npm-script-injection.sh +0 -0
- package/examples/npm-supply-chain-guard.sh +0 -0
- package/examples/nuxt-config-guard.sh +1 -0
- package/examples/oauth-refresh-monitor.sh +210 -0
- package/examples/opus-degradation-tracker.sh +187 -0
- package/examples/opus48-routine-task-warning.sh +196 -0
- package/examples/opus48-thinking-wedge-advisor.sh +143 -0
- package/examples/output-credential-scan.sh +1 -1
- package/examples/output-secret-mask.sh +31 -9
- package/examples/output-token-spike-detector.sh +145 -0
- package/examples/package-json-guard.sh +12 -0
- package/examples/package-lock-frozen.sh +12 -0
- package/examples/parallel-batch-size-limiter.sh +121 -0
- package/examples/parallel-cascade-detector.sh +116 -0
- package/examples/partial-view-claim-arrest.sh +174 -0
- package/examples/path-deny-bash-guard.sh +12 -0
- package/examples/path-traversal-guard.sh +12 -0
- package/examples/per-prompt-token-cap.sh +124 -0
- package/examples/permission-audit-log.sh +1 -1
- package/examples/permission-denial-enforcer.sh +12 -0
- package/examples/permission-entry-validator.sh +0 -0
- package/examples/persisted-1m-model-advisor.sh +88 -0
- package/examples/php-lint-on-edit.sh +1 -1
- package/examples/pii-upload-guard.sh +12 -0
- package/examples/pip-publish-guard.sh +13 -0
- package/examples/pip-requirements-guard.sh +12 -0
- package/examples/pip-venv-required.sh +12 -0
- package/examples/plan-mode-edit-guard.sh +12 -0
- package/examples/plan-mode-enforcer.sh +12 -0
- package/examples/plan-mode-strict-guard.sh +12 -0
- package/examples/plugin-hooks-json-bloat-detector.sh +169 -0
- package/examples/plugin-process-cleanup.sh +0 -0
- package/examples/polyglot-rm-guard.sh +12 -0
- package/examples/post-compact-safety.sh +12 -0
- package/examples/post-edit-disk-verify.sh +152 -0
- package/examples/powershell-remove-item-guard.sh +85 -0
- package/examples/pr-description-check.sh +0 -0
- package/examples/pr-duplicate-guard.sh +13 -0
- package/examples/pre-bash-sed-line-ending-windows.sh +87 -0
- package/examples/pre-compact-knowledge-save.sh +0 -0
- package/examples/pre-compact-transcript-backup.sh +0 -0
- package/examples/pre-execution-claim-detector.sh +179 -0
- package/examples/prefer-builtin-tools.sh +10 -1
- package/examples/prefer-const.sh +0 -0
- package/examples/prefer-dedicated-tools.sh +12 -0
- package/examples/prefer-optional-chaining.sh +0 -0
- package/examples/prisma-migrate-guard.sh +24 -7
- package/examples/production-port-kill-guard.sh +12 -0
- package/examples/prompt-injection-guard.sh +2 -1
- package/examples/prompt-usage-logger.sh +0 -0
- package/examples/protect-claudemd.sh +12 -0
- package/examples/protect-dotfiles.sh +12 -0
- package/examples/proxy-capture-suggester.sh +159 -0
- package/examples/public-artefact-socratic-narrowing.sh +232 -0
- package/examples/public-repo-push-guard.sh +12 -0
- package/examples/push-requires-test-pass-record.sh +0 -0
- package/examples/push-requires-test-pass.sh +12 -0
- package/examples/quota-anomaly-detector.sh +173 -0
- package/examples/quota-reset-audit.sh +58 -0
- package/examples/quoted-flag-approver.sh +99 -10
- package/examples/rails-migration-guard.sh +12 -0
- package/examples/rate-limit-guard.sh +0 -0
- package/examples/read-all-files-enforcer.sh +0 -0
- package/examples/read-budget-guard.sh +12 -0
- package/examples/read-loop-detector.sh +59 -0
- package/examples/read-once-guard.sh +96 -0
- package/examples/read-only-mode.sh +12 -0
- package/examples/readme-exists-check.sh +0 -0
- package/examples/record-read-coverage.sh +73 -0
- package/examples/redirect-fragment-warner.sh +149 -0
- package/examples/redis-flushall-guard.sh +13 -0
- package/examples/redundant-read-blocker.sh +132 -0
- package/examples/refusal-arrest-gate.sh +248 -0
- package/examples/registry-publish-guard.sh +13 -1
- package/examples/remote-control-billing-classifier.sh +135 -0
- package/examples/replace-all-guard.sh +12 -0
- package/examples/repo-visibility-guard.sh +12 -0
- package/examples/reroute-after-block-guard.sh +150 -0
- package/examples/response-budget-guard.sh +0 -0
- package/examples/resume-drift-watcher.sh +57 -0
- package/examples/rg-replace-flag-detector.sh +125 -0
- package/examples/rhetorical-verification-prompt-detector.sh +102 -0
- package/examples/ripgrep-permission-fix.sh +0 -0
- package/examples/rm-safety-net.sh +151 -8
- package/examples/role-tool-guard.sh +12 -0
- package/examples/route-handler-emptiness-gate.sh +324 -0
- package/examples/ruby-lint-on-edit.sh +1 -1
- package/examples/runtime-binary-change-detector.sh +102 -0
- package/examples/same-command-repeat-detector.sh +196 -0
- package/examples/same-correction-arrest.sh +169 -0
- package/examples/sandbox-denywrite-enforcer.sh +78 -0
- package/examples/sandbox-relative-path-audit.sh +83 -25
- package/examples/sandbox-write-verify.sh +12 -0
- package/examples/schema-migration-guard.sh +0 -0
- package/examples/scope-expansion-receipt.sh +178 -0
- package/examples/scope-guard.sh +88 -9
- package/examples/secret-file-read-guard.sh +12 -0
- package/examples/secret-file-write-guard.sh +150 -0
- package/examples/self-modify-bypass-guard.sh +0 -0
- package/examples/sensitive-file-read-guard.sh +23 -3
- package/examples/sensitive-log-guard.sh +1 -0
- package/examples/server-side-prompt-injection-detector.sh +69 -0
- package/examples/session-agent-cost-limiter.sh +12 -0
- package/examples/session-backup-on-start.sh +1 -1
- package/examples/session-cost-alert.sh +12 -0
- package/examples/session-drift-guard.sh +12 -0
- package/examples/session-duration-guard.sh +25 -2
- package/examples/session-end-logger.sh +0 -0
- package/examples/session-health-monitor.sh +0 -0
- package/examples/session-index-repair.sh +1 -1
- package/examples/session-memory-watchdog.sh +1 -0
- package/examples/session-permission-reset-guard.sh +12 -0
- package/examples/session-persistence-verifier.sh +93 -0
- package/examples/session-plugin-pin-watch.sh +68 -0
- package/examples/session-rate-monitor.sh +176 -0
- package/examples/session-resume-env-fix.sh +0 -0
- package/examples/session-start-quota-status.sh +193 -0
- package/examples/session-start-safety-check.sh +55 -3
- package/examples/session-state-saver.sh +0 -0
- package/examples/session-summary-stop.sh +1 -1
- package/examples/session-summary.sh +0 -0
- package/examples/session-token-counter.sh +1 -1
- package/examples/settings-hooks-key-restorer.sh +99 -0
- package/examples/settings-json-health-check.sh +57 -0
- package/examples/settings-json-model-guard.sh +0 -0
- package/examples/settings-regression-tester.sh +188 -0
- package/examples/shell-config-truncation-guard.sh +12 -0
- package/examples/shell-wrapper-guard.sh +12 -0
- package/examples/skill-cumulative-size-detector.sh +104 -0
- package/examples/skill-description-drop-detector.sh +148 -0
- package/examples/skill-gate.sh +0 -0
- package/examples/skill-truncation-detector.sh +154 -0
- package/examples/skills-context-recorder.sh +167 -0
- package/examples/skills-load-verifier.sh +96 -0
- package/examples/skills-settings-validator.sh +139 -0
- package/examples/sonnet-45-deprecation-helper.sh +85 -0
- package/examples/spec-file-scope-guard.sh +12 -0
- package/examples/spring-profile-guard.sh +1 -0
- package/examples/spurious-malformed-notice-detector.sh +247 -0
- package/examples/sql-bulk-delete-warn.sh +88 -0
- package/examples/sql-injection-detect.sh +0 -0
- package/examples/ssh-key-protect.sh +12 -0
- package/examples/staged-secret-scan.sh +22 -1
- package/examples/stale-temp-settings-detector.sh +86 -0
- package/examples/standing-rule-session-surfacer.sh +137 -0
- package/examples/stop-hook-sigterm-wrapper.sh +182 -0
- package/examples/stop-transcript-write-health.sh +64 -0
- package/examples/strict-allowlist.sh +57 -15
- package/examples/strip-coauthored-by.sh +50 -10
- package/examples/subagent-blast-radius-guard.sh +184 -0
- package/examples/subagent-boundary-precheck.sh +126 -0
- package/examples/subagent-budget-guard.sh +12 -0
- package/examples/subagent-claudemd-inject.sh +0 -0
- package/examples/subagent-closure-verify-gate.sh +164 -0
- package/examples/subagent-destructive-git-guard.sh +120 -0
- package/examples/subagent-error-detector.sh +0 -0
- package/examples/subagent-forged-system-reminder-guard.sh +156 -0
- package/examples/subagent-identity-leak-guard.sh +102 -0
- package/examples/subagent-identity-reassertion.sh +51 -0
- package/examples/subagent-inheritance-tester.sh +179 -0
- package/examples/subagent-permission-mode-guard.sh +110 -0
- package/examples/subagent-scope-guard.sh +12 -0
- package/examples/subagent-spawn-verification-enforcer.sh +102 -0
- package/examples/subagent-tool-allowlist-enforcer.sh +101 -0
- package/examples/subagent-tool-call-limiter.sh +87 -15
- package/examples/subcommand-chain-guard.sh +12 -0
- package/examples/subscription-api-billing-warner.sh +160 -0
- package/examples/subscription-bypass-detector.sh +152 -0
- package/examples/svelte-lint-on-edit.sh +1 -0
- package/examples/swift-build-on-edit.sh +1 -1
- package/examples/symlink-guard.sh +27 -2
- package/examples/symlink-protect.sh +1 -0
- package/examples/system-dir-protection-guard.sh +73 -8
- package/examples/system-message-workaround.sh +38 -13
- package/examples/system-package-guard.sh +12 -0
- package/examples/systemd-service-guard.sh +12 -0
- package/examples/task-integrity-guard.sh +12 -0
- package/examples/temp-file-cleanup.sh +1 -1
- package/examples/temporal-suggestion-detector.sh +135 -0
- package/examples/terraform-guard.sh +27 -6
- package/examples/test-after-edit.sh +1 -1
- package/examples/test-before-push.sh +12 -0
- package/examples/test-coverage-reminder.sh +24 -1
- package/examples/thinking-budget-effort-mismatch-detector.sh +149 -0
- package/examples/timezone-guard.sh +0 -0
- package/examples/tmp-output-size-guard.sh +0 -0
- package/examples/todo-check.sh +1 -1
- package/examples/token-budget-guard.sh +12 -0
- package/examples/token-budget-per-task.sh +12 -0
- package/examples/token-usage-tracker.sh +7 -1
- package/examples/tokenizer-ratio-alert-corpus.sh +90 -0
- package/examples/tokenizer-ratio-alert.sh +104 -0
- package/examples/tool-call-rate-limiter.sh +0 -0
- package/examples/tool-result-correlation-checker.sh +167 -0
- package/examples/tool-result-size-guard.sh +132 -0
- package/examples/tool-retry-budget-guard.sh +12 -0
- package/examples/transcript-contamination-detector.sh +138 -0
- package/examples/trustfall-mcp-injection-guard.sh +103 -0
- package/examples/ultrareview-large-diff-advisor.sh +184 -0
- package/examples/unbounded-output-guard.sh +94 -0
- package/examples/uncommitted-changes-stop.sh +1 -0
- package/examples/uncommitted-discard-guard.sh +23 -5
- package/examples/uncommitted-work-guard.sh +12 -0
- package/examples/usage-warn.sh +0 -0
- package/examples/user-account-guard.sh +12 -0
- package/examples/userprompt-submit-receipt.sh +123 -0
- package/examples/variable-expansion-guard.sh +12 -0
- package/examples/verify-after-publish-reminder.sh +93 -0
- package/examples/verify-before-commit.sh +12 -0
- package/examples/version-bump-detector.sh +156 -0
- package/examples/version-regression-warner.sh +144 -0
- package/examples/vue-lint-on-edit.sh +1 -0
- package/examples/warn-cron-cost-trap.sh +173 -0
- package/examples/webfetch-runaway-guard.sh +89 -0
- package/examples/wellness-break-reminder.sh +118 -0
- package/examples/windows-destructive-command-guard.sh +177 -0
- package/examples/windows-path-guard.sh +12 -0
- package/examples/windows-python-stub-detector.sh +63 -0
- package/examples/work-hours-guard.sh +12 -0
- package/examples/working-directory-fence.sh +12 -0
- package/examples/workspace-lease-guard.sh +205 -0
- package/examples/worktree-create-log.sh +1 -0
- package/examples/worktree-delete-guard.sh +12 -0
- package/examples/worktree-edit-boundary-guard.sh +127 -0
- package/examples/worktree-escape-write-guard.sh +114 -0
- package/examples/worktree-hook-linker.sh +0 -0
- package/examples/worktree-hooks-path-fix.sh +111 -0
- package/examples/worktree-parent-write-guard.sh +43 -0
- package/examples/worktree-project-unify.sh +2 -1
- package/examples/worktree-remove-uncommitted-guard.sh +13 -0
- package/examples/worktree-unmerged-guard.sh +7 -1
- package/examples/write-byte-integrity-verifier.sh +190 -0
- package/examples/write-empty-content-guard.sh +67 -0
- package/examples/write-nul-corruption-detector.sh +61 -0
- package/examples/write-overwrite-confirm.sh +160 -14
- package/examples/write-secret-guard.sh +27 -5
- package/examples/write-shrink-guard.sh +12 -0
- package/examples/write-test-ratio.sh +0 -0
- package/examples/wsl-host-disk-space-guard.sh +97 -0
- package/examples/xml-format-leak-detector.sh +266 -0
- package/examples/yaml-syntax-check.sh +12 -0
- package/hooks/hooks.json +6 -6
- package/index.mjs +1718 -183
- package/mission.md +2 -0
- package/package.json +4 -4
- package/plugins/credential-guard/.claude-plugin/plugin.json +9 -7
- package/plugins/git-protection/.claude-plugin/plugin.json +10 -8
- package/plugins/safety-essentials/.claude-plugin/plugin.json +9 -7
- package/plugins/token-guard/.claude-plugin/plugin.json +10 -8
- package/scripts/agents-md-sync-setup.sh +156 -0
- package/scripts/autonomy-blocked-summary.sh +148 -0
- package/scripts/build-search-index.py +43 -0
- package/scripts/build-sitemap.py +119 -0
- package/scripts/cc-doctor.sh +192 -0
- package/scripts/check-hook-event-names.py +127 -0
- package/scripts/check-misfire-instrument.sh +64 -0
- package/scripts/claim-verify-audit.sh +467 -0
- package/scripts/cowork-claudemd-helper.sh +136 -0
- package/scripts/june-15-deprecated-model-scan.sh +111 -0
- package/scripts/measure-heredoc-shapes.sh +67 -0
- package/scripts/measure-literal-match-misfire.sh +110 -0
- package/scripts/probe-scope-guard-branches.sh +48 -0
- package/scripts/probe-skip-line-both-ways.sh +65 -0
- package/scripts/receipts-aggregate.py +199 -0
- package/scripts/session-forensic-audit.py +360 -0
- package/scripts.json +9 -9
- package/scripts.json.bak-2026-07-27-find +13 -0
- package/skills/safety-setup/SKILL.md +5 -5
- package/tests/approve-side-remaining-two.test.sh +129 -0
- package/tests/approver-list-granularity.test.sh +145 -0
- package/tests/askuserquestion-autonomy-gate.test.sh +181 -0
- package/tests/audit-ci-gate.test.sh +87 -0
- package/tests/audit-cross-layer-claudemd.test.sh +170 -0
- package/tests/audit-fail-open-parse.test.sh +100 -0
- package/tests/auto-approve-compound-tail.test.sh +194 -0
- package/tests/auto-mode-path-invoked-commands.test.sh +89 -0
- package/tests/auto-mode-quoted-targets.test.sh +127 -0
- package/tests/auto-mode-unquoted-targets.test.sh +112 -0
- package/tests/bash-secret-output-detector-detects.test.sh +102 -0
- package/tests/blindspots-cross-layer.test.sh +177 -0
- package/tests/block-database-wipe.test.sh +64 -0
- package/tests/bundled-short-flags-bypass.test.sh +85 -0
- package/tests/cd-git-allow-compound-tail.test.sh +67 -0
- package/tests/chown-guard-filesystem-root.test.sh +64 -0
- package/tests/ci-workflow-guard-skip-rule.test.sh +115 -0
- package/tests/compound-deny-safe-rm-targets.test.sh +105 -0
- package/tests/core-scripts-parser-fallback.test.sh +140 -0
- package/tests/destructive-guard-bare-glob.test.sh +74 -0
- package/tests/destructive-guard-every-operand.test.sh +100 -0
- package/tests/destructive-guard-find-deletion.test.sh +114 -0
- package/tests/destructive-guard-flag-order.test.sh +106 -0
- package/tests/destructive-guard-mentions-vs-invocations.test.sh +107 -0
- package/tests/destructive-guard-quoted-target.test.sh +89 -0
- package/tests/destructive-guard-separators.test.sh +105 -0
- package/tests/destructive-guard-variable-target.test.sh +98 -0
- package/tests/destructive-guard-wrapped-command.test.sh +91 -0
- package/tests/doctor-hook-disabling-env.test.sh +92 -0
- package/tests/doctor-misspelled-settings-keys.test.sh +106 -0
- package/tests/example-guards-wrapped-command.test.sh +169 -0
- package/tests/file-edit-backup.test.sh +93 -0
- package/tests/git-filter-repo-guard.test.sh +60 -0
- package/tests/git-global-options-bypass.test.sh +141 -0
- package/tests/git-invocation-prefixes-all-guards.test.sh +114 -0
- package/tests/git-maintenance-guard.test.sh +83 -0
- package/tests/git-pack-temp-cleanup-guard.test.sh +133 -0
- package/tests/git-rm-orphan-wipe-guard.test.sh +66 -0
- package/tests/git-submodule-guard.test.sh +50 -0
- package/tests/grep-pcre-portability.test.sh +155 -0
- package/tests/hook-registration-matches-input.test.sh +73 -0
- package/tests/hook-trigger-matches-envelope.test.sh +178 -0
- package/tests/install-does-not-claim-protection.test.sh +110 -0
- package/tests/install-example-standalone-tools.test.sh +139 -0
- package/tests/line-continuation-bypass.test.sh +114 -0
- package/tests/mcp-config-poisoning-audit.test.sh +106 -0
- package/tests/nested-background-agent-guard.test.sh +58 -0
- package/tests/no-unqualified-safety-claims.test.sh +114 -0
- package/tests/option-eating-grep-patterns.test.sh +130 -0
- package/tests/outdated-core-guards.test.sh +134 -0
- package/tests/outdated-report.test.sh +155 -0
- package/tests/output-secret-mask-warns.test.sh +73 -0
- package/tests/plugin-hooks-fail-open-under-sh.test.sh +165 -0
- package/tests/plugins-word-order.test.sh +126 -0
- package/tests/powershell-remove-item-guard.test.sh +85 -0
- package/tests/project-dir-naming.test.sh +103 -0
- package/tests/quoted-safe-targets-and-mentions.test.sh +190 -0
- package/tests/reroute-after-block-guard.test.sh +104 -0
- package/tests/secret-guard-documented-promises.test.sh +97 -0
- package/tests/session-plugin-pin-watch.test.sh +45 -0
- package/tests/settings-unreadable-refuses-to-write.test.sh +95 -0
- package/tests/shield-no-silent-adoption.test.py +110 -0
- package/tests/shield-no-silent-adoption.test.sh +46 -0
- package/tests/stop-transcript-write-health.test.sh +46 -0
- package/tests/system-dir-guard-root-erased.test.sh +66 -0
- package/tests/test-account-billing-log.sh +100 -0
- package/tests/test-account-routing-preflight.sh +77 -0
- package/tests/test-agent-sdk-credit-pool-monitor.sh +157 -0
- package/tests/test-agent-view-quota-warn.sh +184 -0
- package/tests/test-agents-md-edit-drift-warner.sh +176 -0
- package/tests/test-agents-md-loader.sh +204 -0
- package/tests/test-agents-md-sync-checker.sh +125 -0
- package/tests/test-ai-slop-punctuation-arrest.sh +280 -0
- package/tests/test-allowlist.sh +60 -0
- package/tests/test-always-allow-pattern-suggester.sh +147 -0
- package/tests/test-anthropic-base-url-guard.sh +88 -0
- package/tests/test-articulated-scope-capture.sh +139 -0
- package/tests/test-attachment-traversal-guard.sh +168 -0
- package/tests/test-aup-block-pattern-logger.sh +315 -0
- package/tests/test-aup-false-positive-helper.sh +188 -0
- package/tests/test-aup-large-tool-output-warner.sh +370 -0
- package/tests/test-aup-retry-loop-guard.sh +341 -0
- package/tests/test-auth-expiry-reminder.sh +303 -0
- package/tests/test-auth-macos-sleep-detector.sh +254 -0
- package/tests/test-auth-path-detector.sh +232 -0
- package/tests/test-auth-status-checker.sh +233 -0
- package/tests/test-authorized-reconfirmation-detector.sh +280 -0
- package/tests/test-background-cost-launch-guard.sh +62 -0
- package/tests/test-bash-allowlist-secondary-check.sh +143 -0
- package/tests/test-bash-fanout-bounded-rewriter.sh +186 -0
- package/tests/test-bash-project-fence.sh +127 -0
- package/tests/test-bg-permission-prompt-warner.sh +221 -0
- package/tests/test-binary-read-detector.sh +173 -0
- package/tests/test-broad-prefix-session-trap-warner.sh +167 -0
- package/tests/test-bulk-delete-target-resolution.sh +49 -0
- package/tests/test-bypass-mode-effective-verifier.sh +179 -0
- package/tests/test-cache-creation-drift-detector.sh +244 -0
- package/tests/test-cache-residue-detector.sh +429 -0
- package/tests/test-cache-tier-logger.sh +59 -0
- package/tests/test-cache-ttl-eviction-detector.sh +282 -0
- package/tests/test-cch-sentinel-precommit-guard.sh +64 -0
- package/tests/test-claim-verify-detector.sh +222 -0
- package/tests/test-claim-vs-caveat-checker.sh +134 -0
- package/tests/test-claude-code-version-pin-advisor.sh +353 -0
- package/tests/test-claude-md-reinjector.sh +173 -0
- package/tests/test-claude-md-size-monitor.sh +107 -0
- package/tests/test-claude-update-budget-guard.sh +132 -0
- package/tests/test-claude-update-smart.sh +91 -0
- package/tests/test-claudemd-tool-prohibition.sh +239 -0
- package/tests/test-cli-config-pinning-detector.sh +233 -0
- package/tests/test-cliff-countdown-advisor.sh +138 -0
- package/tests/test-closure-word-verify-gate.sh +158 -0
- package/tests/test-commit-all-scope-guard.sh +73 -0
- package/tests/test-commitment-carry-forward-arrest.sh +250 -0
- package/tests/test-compact-dispatch-watchdog.sh +147 -0
- package/tests/test-completion-claim-without-verification-detector.sh +326 -0
- package/tests/test-compound-bash-permission-resolver.sh +264 -0
- package/tests/test-compound-command-deny-enforcer.sh +67 -0
- package/tests/test-context-length-budget.sh +68 -0
- package/tests/test-context-usage-drift-alert.sh +52 -22
- package/tests/test-cost-incident-self-audit.sh +94 -0
- package/tests/test-cowork-claude-md-load-checker.sh +257 -0
- package/tests/test-cowork-claudemd-helper.sh +141 -0
- package/tests/test-cowork-fuse-staleness-watcher.sh +287 -0
- package/tests/test-cowork-hook-absence-warner.sh +230 -0
- package/tests/test-cowork-model-picker-advisor.sh +317 -0
- package/tests/test-credential-exfil-guard.sh +62 -0
- package/tests/test-cron-create-receipt.sh +165 -0
- package/tests/test-cross-product-mcp-spawn-detector.sh +112 -0
- package/tests/test-daemon-zombie-worker-surfacer.sh +71 -0
- package/tests/test-daily-cost-guard.sh +131 -0
- package/tests/test-deny-rule-integrity-verifier.sh +288 -0
- package/tests/test-deployment-readback-gate.sh +92 -0
- package/tests/test-deployment-readback-gh-adapter.sh +163 -0
- package/tests/test-disabled-feature-toggle-advisor.sh +78 -0
- package/tests/test-dispatch-allowlist-preflight.sh +385 -0
- package/tests/test-dispatch-liveness-watchdog.sh +319 -0
- package/tests/test-dispatch-receipt.sh +314 -0
- package/tests/test-dotenv-anthropic-key-billing-guard.sh +160 -0
- package/tests/test-echo-probe-spam-detector.sh +143 -0
- package/tests/test-edit-quote-ambiguity-guard.sh +75 -0
- package/tests/test-ephemeral-container-detector.sh +251 -0
- package/tests/test-evidence-claim-gate.sh +291 -0
- package/tests/test-extended-thinking-loop-guard.sh +289 -0
- package/tests/test-extended-thinking-resume-warning.sh +240 -0
- package/tests/test-extended-thinking-tool-use-mismatch-detector.sh +236 -0
- package/tests/test-fabricated-command-detector.sh +296 -0
- package/tests/test-false-disk-full-guard.sh +36 -0
- package/tests/test-feature-deprecation-detector.sh +117 -0
- package/tests/test-forbidden-response-phrase-detector.sh +246 -0
- package/tests/test-git-operations-require-approval.sh +53 -0
- package/tests/test-git-push-blast-radius-guard.sh +115 -0
- package/tests/test-goal-iteration-limit-warner.sh +128 -0
- package/tests/test-growthbook-flag-monitor.sh +428 -0
- package/tests/test-halt-signal-detector.sh +120 -0
- package/tests/test-hook-self-disable-detector.sh +232 -0
- package/tests/test-iteration-quality-degradation-detector.sh +244 -0
- package/tests/test-long-session-malformed-tool-call-detector.sh +185 -0
- package/tests/test-marketplace-plugins.sh +193 -0
- package/tests/test-mcp-misdiagnosis-arrest.sh +235 -0
- package/tests/test-mcp-orphan-leak-warner.sh +71 -0
- package/tests/test-mcp-permissions-ask-warner.sh +207 -0
- package/tests/test-mcp-routine-approval-detector.sh +229 -0
- package/tests/test-mcp-startup-bloat-detector.sh +141 -0
- package/tests/test-mcp-stdio-compatibility-test.sh +112 -0
- package/tests/test-memory-chain-audit.sh +59 -0
- package/tests/test-memory-orphan-detector.sh +227 -0
- package/tests/test-model-swap-suggester.sh +368 -0
- package/tests/test-model-version-lock.sh +83 -0
- package/tests/test-multi-file-plan-routing-gate.sh +277 -0
- package/tests/test-multi-window-auth-drift-detector.sh +262 -0
- package/tests/test-nested-spawn-inflight-guard.sh +281 -0
- package/tests/test-no-force-flag.sh +49 -0
- package/tests/test-non-english-quality-warner.sh +229 -0
- package/tests/test-oauth-refresh-monitor.sh +311 -0
- package/tests/test-opus-degradation-tracker.sh +274 -0
- package/tests/test-opus48-routine-task-warning.sh +201 -0
- package/tests/test-opus48-thinking-wedge-advisor.sh +249 -0
- package/tests/test-output-token-spike-detector.sh +315 -0
- package/tests/test-parallel-batch-size-limiter.sh +228 -0
- package/tests/test-parallel-cascade-detector.sh +191 -0
- package/tests/test-partial-view-claim-arrest.sh +272 -0
- package/tests/test-per-prompt-token-cap.sh +136 -0
- package/tests/test-persisted-1m-model-advisor.sh +71 -0
- package/tests/test-plugin-hooks-json-bloat-detector.sh +267 -0
- package/tests/test-post-edit-disk-verify.sh +294 -0
- package/tests/test-pre-bash-sed-line-ending-windows.sh +108 -0
- package/tests/test-pre-execution-claim-detector.sh +366 -0
- package/tests/test-private-key-backup-and-git-credentials.sh +100 -0
- package/tests/test-proxy-capture-suggester.sh +214 -0
- package/tests/test-public-artefact-socratic-narrowing.sh +346 -0
- package/tests/test-quota-anomaly-detector.sh +321 -0
- package/tests/test-quota-reset-audit.sh +81 -0
- package/tests/test-read-loop-detector.sh +137 -0
- package/tests/test-read-once-guard.sh +85 -0
- package/tests/test-receipts-aggregate.sh +143 -0
- package/tests/test-redirect-fragment-warner.sh +130 -0
- package/tests/test-redundant-read-blocker.sh +155 -0
- package/tests/test-refusal-arrest-gate.sh +303 -0
- package/tests/test-remote-control-billing-classifier.sh +125 -0
- package/tests/test-resume-drift-watcher.sh +65 -0
- package/tests/test-rg-replace-flag-detector.sh +175 -0
- package/tests/test-rhetorical-verification-prompt-detector.sh +237 -0
- package/tests/test-rm-safety-net.sh +115 -0
- package/tests/test-route-handler-emptiness-gate.sh +300 -0
- package/tests/test-runtime-binary-change-detector.sh +196 -0
- package/tests/test-same-command-repeat-detector.sh +165 -0
- package/tests/test-same-correction-arrest.sh +165 -0
- package/tests/test-scope-expansion-receipt.sh +207 -0
- package/tests/test-server-side-prompt-injection-detector.sh +181 -0
- package/tests/test-session-persistence-verifier.sh +128 -0
- package/tests/test-session-rate-monitor.sh +284 -0
- package/tests/test-session-start-quota-status.sh +210 -0
- package/tests/test-session-start-safety-check.sh +138 -0
- package/tests/test-settings-hooks-key-restorer.sh +257 -0
- package/tests/test-settings-json-health-check.sh +70 -0
- package/tests/test-settings-regression-tester.sh +197 -0
- package/tests/test-skill-cumulative-size-detector.sh +198 -0
- package/tests/test-skill-description-drop-detector.sh +236 -0
- package/tests/test-skill-truncation-detector.sh +263 -0
- package/tests/test-skills-context-recorder.sh +329 -0
- package/tests/test-skills-load-verifier.sh +184 -0
- package/tests/test-skills-settings-validator.sh +242 -0
- package/tests/test-sonnet-45-deprecation-helper.sh +150 -0
- package/tests/test-spurious-malformed-notice-detector.sh +290 -0
- package/tests/test-sql-bulk-delete-warn.sh +123 -0
- package/tests/test-ssh-key-protect.sh +81 -0
- package/tests/test-stale-temp-settings-detector.sh +161 -0
- package/tests/test-standing-rule-session-surfacer.sh +215 -0
- package/tests/test-starter-hooks-manifest.sh +96 -0
- package/tests/test-stop-hook-sigterm-wrapper.sh +258 -0
- package/tests/test-strict-allowlist.sh +72 -0
- package/tests/test-subagent-boundary-precheck.sh +137 -0
- package/tests/test-subagent-closure-verify-gate.sh +193 -0
- package/tests/test-subagent-destructive-git-guard.sh +86 -0
- package/tests/test-subagent-identity-leak-guard.sh +62 -0
- package/tests/test-subagent-identity-reassertion.sh +61 -0
- package/tests/test-subagent-inheritance-tester.sh +319 -0
- package/tests/test-subagent-permission-mode-guard.sh +61 -0
- package/tests/test-subagent-spawn-verification-enforcer.sh +60 -0
- package/tests/test-subagent-tool-allowlist-enforcer.sh +62 -0
- package/tests/test-subagent-tool-call-limiter.sh +97 -0
- package/tests/test-subscription-api-billing-warner.sh +138 -0
- package/tests/test-subscription-bypass-detector.sh +255 -0
- package/tests/test-temporal-suggestion-detector.sh +183 -0
- package/tests/test-thinking-budget-effort-mismatch-detector.sh +327 -0
- package/tests/test-tokenizer-ratio-alert-corpus.sh +77 -0
- package/tests/test-tokenizer-ratio-alert.sh +93 -0
- package/tests/test-tool-result-correlation-checker.sh +264 -0
- package/tests/test-tool-result-size-guard.sh +121 -0
- package/tests/test-transcript-contamination-detector.sh +264 -0
- package/tests/test-trigger-header-parsing.sh +151 -0
- package/tests/test-ultrareview-large-diff-advisor.sh +192 -0
- package/tests/test-userprompt-submit-receipt.sh +181 -0
- package/tests/test-verify-after-publish-reminder.sh +184 -0
- package/tests/test-version-bump-detector.sh +212 -0
- package/tests/test-version-regression-warner.sh +165 -0
- package/tests/test-warn-cron-cost-trap.sh +82 -0
- package/tests/test-webfetch-runaway-guard.sh +77 -0
- package/tests/test-wellness-break-reminder.sh +138 -0
- package/tests/test-windows-destructive-command-guard.sh +169 -0
- package/tests/test-windows-python-stub-detector.sh +146 -0
- package/tests/test-worktree-edit-boundary-guard.sh +219 -0
- package/tests/test-write-byte-integrity-verifier.sh +192 -0
- package/tests/test-xml-format-leak-detector.sh +299 -0
- package/tests/trustfall-mcp-injection-guard.test.sh +183 -0
- package/tests/workspace-lease-guard.test.sh +144 -0
- package/tests/worktree-escape-write-guard.test.sh +68 -0
- package/tests/worktree-hooks-path-fix.test.sh +181 -0
- package/tests/worktree-parent-write-guard.test.sh +51 -0
- package/tests/write-empty-content-guard.test.sh +39 -0
- package/tests/write-nul-corruption-detector.test.sh +29 -0
- package/tests/write-overwrite-unread-content.test.sh +178 -0
- package/tests/wsl-host-disk-space-guard.test.sh +55 -0
|
@@ -1,40 +1,122 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# ================================================================
|
|
3
|
-
# memory-write-guard.sh —
|
|
3
|
+
# memory-write-guard.sh — See, approve, or block silent memory writes
|
|
4
4
|
# ================================================================
|
|
5
5
|
# PURPOSE:
|
|
6
|
-
# Claude
|
|
7
|
-
#
|
|
8
|
-
#
|
|
6
|
+
# Claude Code writes to its memory without asking: it edits CLAUDE.md
|
|
7
|
+
# and silently creates/updates/deletes files under
|
|
8
|
+
# ~/.claude/projects/*/memory/. Users report having to re-check those
|
|
9
|
+
# files after every session to find changes they never approved.
|
|
9
10
|
#
|
|
10
|
-
#
|
|
11
|
+
# This hook gives the operator control over those writes. By default it
|
|
12
|
+
# only makes them VISIBLE (logs + a stderr note), which is non-intrusive
|
|
13
|
+
# and safe for automated runs. Opt in to an approval prompt — or a hard
|
|
14
|
+
# block — when you want every memory change to pass through you first.
|
|
11
15
|
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
16
|
+
# WHAT COUNTS AS A "MEMORY" WRITE (Write/Edit/MultiEdit on):
|
|
17
|
+
# - any CLAUDE.md / CLAUDE.local.md (project or user memory)
|
|
18
|
+
# - anything under a .claude/ directory, which includes the auto-memory
|
|
19
|
+
# store ~/.claude/projects/*/memory/ and ~/.claude/settings.json
|
|
20
|
+
#
|
|
21
|
+
# MODE (env var CC_MEMORY_WRITE_APPROVAL):
|
|
22
|
+
# off (default) — log + warn, never interrupt (backward compatible)
|
|
23
|
+
# ask — require interactive approval for each memory write
|
|
24
|
+
# block — refuse all memory writes
|
|
25
|
+
#
|
|
26
|
+
# WHY OPT-IN: existing installs and headless/CI runs must keep working
|
|
27
|
+
# unchanged, so the prompt is never on unless the operator asks for it.
|
|
28
|
+
#
|
|
29
|
+
# VERSION NOTE (measured 2026-08-13 on 2.1.228, corrects an earlier note here):
|
|
30
|
+
# #77212 reported that under bypassPermissions an "ask" decision is silently
|
|
31
|
+
# auto-approved and does not gate the write. This comment used to repeat that
|
|
32
|
+
# as current behaviour. It does not reproduce on 2.1.228: run interactively in
|
|
33
|
+
# an isolated HOME, the confirmation appears both under
|
|
34
|
+
# --dangerously-skip-permissions and under --permission-mode auto, carrying
|
|
35
|
+
# this hook's permissionDecisionReason. Control: with the hook removed, both
|
|
36
|
+
# modes ran the same command with no prompt at all, so the permissive mode was
|
|
37
|
+
# genuinely in effect. Non-interactively (claude -p) the call is refused
|
|
38
|
+
# rather than approved. Older builds may still match the report — verify on
|
|
39
|
+
# your own version with `--version` before relying on either behaviour.
|
|
40
|
+
# Independent of all this: an "ask" needs somebody present to answer, so for
|
|
41
|
+
# fully unattended runs use CC_MEMORY_WRITE_APPROVAL=block (exit 2), which is
|
|
42
|
+
# enforced regardless of permission mode.
|
|
43
|
+
#
|
|
44
|
+
# TRIGGER: PreToolUse MATCHER: "Write|Edit|MultiEdit"
|
|
45
|
+
#
|
|
46
|
+
# Born from:
|
|
47
|
+
# https://github.com/anthropics/claude-code/issues/38040
|
|
48
|
+
# "No way to enforce approval on all file modifications"
|
|
49
|
+
# https://github.com/anthropics/claude-code/issues/65064
|
|
50
|
+
# "claude code writes memory without asking for permission"
|
|
14
51
|
# ================================================================
|
|
15
52
|
|
|
53
|
+
set -u
|
|
54
|
+
|
|
55
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
56
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
57
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
58
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
59
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
60
|
+
_nojq_warned="/tmp/cc-nojq-warned-memory-write-guard-$PPID"
|
|
61
|
+
[ -f "$_nojq_warned" ] || {
|
|
62
|
+
echo "WARNING [memory-write-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
63
|
+
: > "$_nojq_warned"
|
|
64
|
+
}
|
|
65
|
+
fi
|
|
66
|
+
|
|
16
67
|
INPUT=$(cat)
|
|
17
68
|
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
18
69
|
[ -z "$FILE" ] && exit 0
|
|
19
70
|
|
|
20
|
-
#
|
|
71
|
+
# Is this a memory file? Match the auto-memory/.claude store and CLAUDE.md
|
|
72
|
+
# files anywhere (project ./CLAUDE.md as well as ~/.claude/CLAUDE.md).
|
|
73
|
+
BASENAME=$(basename "$FILE")
|
|
74
|
+
IS_MEMORY=0
|
|
21
75
|
case "$FILE" in
|
|
22
|
-
*/.claude/*|~/.claude/*)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
76
|
+
*/.claude/*|~/.claude/*) IS_MEMORY=1 ;;
|
|
77
|
+
esac
|
|
78
|
+
case "$BASENAME" in
|
|
79
|
+
CLAUDE.md|CLAUDE.local.md) IS_MEMORY=1 ;;
|
|
80
|
+
esac
|
|
81
|
+
[ "$IS_MEMORY" -eq 0 ] && exit 0
|
|
82
|
+
|
|
83
|
+
# Record every memory write so the operator can audit what was stored.
|
|
84
|
+
LOG="$HOME/.claude/memory-writes.log"
|
|
85
|
+
echo "[$(date -Iseconds)] Write to: $FILE" >> "$LOG" 2>/dev/null
|
|
26
86
|
|
|
27
|
-
|
|
28
|
-
echo "NOTE: Writing to Claude config directory: $FILE" >&2
|
|
87
|
+
MODE="${CC_MEMORY_WRITE_APPROVAL:-off}"
|
|
29
88
|
|
|
30
|
-
|
|
89
|
+
case "$MODE" in
|
|
90
|
+
ask)
|
|
91
|
+
# Emit a PreToolUse permission decision. In an interactive session
|
|
92
|
+
# this surfaces an approve/deny prompt — including under
|
|
93
|
+
# bypassPermissions and auto mode, measured on 2.1.228 (see the
|
|
94
|
+
# VERSION NOTE at the top; #77212 reported otherwise on older builds).
|
|
95
|
+
# Use =block when nobody is present to answer the prompt.
|
|
96
|
+
jq -n --arg f "$FILE" '{
|
|
97
|
+
hookSpecificOutput: {
|
|
98
|
+
hookEventName: "PreToolUse",
|
|
99
|
+
permissionDecision: "ask",
|
|
100
|
+
permissionDecisionReason: ("Approve write to memory file: " + $f)
|
|
101
|
+
}
|
|
102
|
+
}'
|
|
103
|
+
exit 0
|
|
104
|
+
;;
|
|
105
|
+
block)
|
|
106
|
+
echo "BLOCKED: memory write refused by memory-write-guard." >&2
|
|
107
|
+
echo "File: $FILE" >&2
|
|
108
|
+
echo "Set CC_MEMORY_WRITE_APPROVAL=ask to approve per-write, or =off to only log." >&2
|
|
109
|
+
exit 2
|
|
110
|
+
;;
|
|
111
|
+
*)
|
|
112
|
+
# off (default): only make the write visible.
|
|
113
|
+
echo "NOTE: Writing to Claude memory: $FILE" >&2
|
|
31
114
|
case "$FILE" in
|
|
32
115
|
*/settings.json|*/settings.local.json)
|
|
33
116
|
echo "WARNING: Modifying Claude Code settings file." >&2
|
|
34
117
|
echo "Verify this change is intentional." >&2
|
|
35
118
|
;;
|
|
36
119
|
esac
|
|
120
|
+
exit 0
|
|
37
121
|
;;
|
|
38
122
|
esac
|
|
39
|
-
|
|
40
|
-
exit 0
|
|
@@ -14,6 +14,18 @@
|
|
|
14
14
|
|
|
15
15
|
set -euo pipefail
|
|
16
16
|
|
|
17
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
18
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
19
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
20
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
21
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
22
|
+
_nojq_warned="/tmp/cc-nojq-warned-migration-verify-guard-$PPID"
|
|
23
|
+
[ -f "$_nojq_warned" ] || {
|
|
24
|
+
echo "WARNING [migration-verify-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
25
|
+
: > "$_nojq_warned"
|
|
26
|
+
}
|
|
27
|
+
fi
|
|
28
|
+
|
|
17
29
|
INPUT=$(cat)
|
|
18
30
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
19
31
|
[ -z "$COMMAND" ] && exit 0
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# model-swap-suggester.sh — Evidence-driven swap recommendation for the AUP false-positive cluster
|
|
3
|
+
#
|
|
4
|
+
# Background:
|
|
5
|
+
# Cluster 9 (Usage Policy classifier over-trigger) accumulated 25+ open GitHub issues
|
|
6
|
+
# between 2026-05-18 and 2026-05-27, with Opus 4.7 / 4.6 / 1M variants over-triggering
|
|
7
|
+
# on benign Claude Code prompts (English, Russian, Polish, Spanish; kernel security,
|
|
8
|
+
# biomedical, FPGA, plain greetings — context-independent). Sonnet variants are reported
|
|
9
|
+
# unaffected.
|
|
10
|
+
#
|
|
11
|
+
# Two defense hooks ship today:
|
|
12
|
+
# - aup-false-positive-helper.sh (SessionStart, opt-in, generic awareness)
|
|
13
|
+
# - aup-block-pattern-logger.sh (PostToolUse, on-by-default, records each block)
|
|
14
|
+
#
|
|
15
|
+
# The gap they leave: the operator who is actively being blocked needs an evidence-driven
|
|
16
|
+
# recommendation at the right moment — "you have been blocked N times in the last hour
|
|
17
|
+
# on Opus, here is the exact swap command." The helper hook is generic and fires on every
|
|
18
|
+
# Opus session; the logger collects evidence but does not act on it.
|
|
19
|
+
#
|
|
20
|
+
# This hook closes that loop. On SessionStart, it reads the block log written by the
|
|
21
|
+
# logger, counts Opus blocks in a recent time window, and emits a concrete swap-to-Sonnet
|
|
22
|
+
# advisory only when the count crosses a threshold. Silent otherwise.
|
|
23
|
+
#
|
|
24
|
+
# Reference: https://gist.github.com/yurukusa/4fa4751044be45bd83345601ee79c2db
|
|
25
|
+
#
|
|
26
|
+
# What this hook does:
|
|
27
|
+
# On SessionStart, if ANTHROPIC_MODEL is pinned to an Opus variant AND the AUP block log
|
|
28
|
+
# shows N or more blocks on any Opus model in the last M minutes, emit a one-time advisory
|
|
29
|
+
# with the exact swap command for the current session. Silent in every other case.
|
|
30
|
+
#
|
|
31
|
+
# When this hook does NOT emit anything:
|
|
32
|
+
# - CC_MODEL_SWAP_SUGGESTER_DISABLE=1
|
|
33
|
+
# - CC_MODEL_SWAP_SUGGESTER_QUIET=1
|
|
34
|
+
# - ANTHROPIC_MODEL unset (default routing)
|
|
35
|
+
# - ANTHROPIC_MODEL pinned to a non-Opus variant (Sonnet / Haiku unaffected)
|
|
36
|
+
# - aup-block log missing, empty, or unreadable
|
|
37
|
+
# - Opus blocks in the configured window < threshold
|
|
38
|
+
#
|
|
39
|
+
# Hook config (settings.json):
|
|
40
|
+
# {
|
|
41
|
+
# "hooks": {
|
|
42
|
+
# "SessionStart": [{
|
|
43
|
+
# "matcher": "",
|
|
44
|
+
# "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/model-swap-suggester.sh" }]
|
|
45
|
+
# }]
|
|
46
|
+
# }
|
|
47
|
+
# }
|
|
48
|
+
#
|
|
49
|
+
# Env vars:
|
|
50
|
+
# CC_MODEL_SWAP_SUGGESTER_DISABLE=1 — never emit
|
|
51
|
+
# CC_MODEL_SWAP_SUGGESTER_QUIET=1 — silent (post-acknowledgment)
|
|
52
|
+
# CC_MODEL_SWAP_SUGGESTER_THRESHOLD=<n> — Opus block count threshold (default 3)
|
|
53
|
+
# CC_MODEL_SWAP_SUGGESTER_WINDOW_MIN=<n> — lookback window in minutes (default 60)
|
|
54
|
+
# CC_MODEL_SWAP_SUGGESTER_TARGET=<model> — target Sonnet model (default claude-sonnet-4-7)
|
|
55
|
+
# CC_AUP_BLOCK_LOG_PATH=<path> — log path (shared with logger; default ~/.claude/aup-block-history.log)
|
|
56
|
+
#
|
|
57
|
+
# Design notes:
|
|
58
|
+
# - Evidence-driven, not opt-in. The advisory only fires when real blocks have happened,
|
|
59
|
+
# so it never adds noise for users who haven't been affected.
|
|
60
|
+
# - Counts Opus blocks across all Opus variants in the log (not just the currently pinned
|
|
61
|
+
# model), because the cluster signature is Opus-family-wide. If you swapped from
|
|
62
|
+
# opus-4-7 to opus-4-6 mid-day and both blocked, both count toward the threshold.
|
|
63
|
+
# - Window is timestamp-based, not last-N-lines, so a quiet session with stale entries
|
|
64
|
+
# does not trigger the advisory.
|
|
65
|
+
# - Never blocks. All failure paths exit 0 so a corrupt log or missing date utility
|
|
66
|
+
# cannot break the session.
|
|
67
|
+
|
|
68
|
+
set -u
|
|
69
|
+
|
|
70
|
+
# Disable path
|
|
71
|
+
if [ "${CC_MODEL_SWAP_SUGGESTER_DISABLE:-0}" = "1" ]; then
|
|
72
|
+
exit 0
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# Quiet path
|
|
76
|
+
if [ "${CC_MODEL_SWAP_SUGGESTER_QUIET:-0}" = "1" ]; then
|
|
77
|
+
exit 0
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Model unset → default routing, no specific advisory
|
|
81
|
+
if [ -z "${ANTHROPIC_MODEL:-}" ]; then
|
|
82
|
+
exit 0
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# Non-Opus model → unaffected by the cluster
|
|
86
|
+
case "$ANTHROPIC_MODEL" in
|
|
87
|
+
*opus*|*Opus*|*OPUS*) : ;;
|
|
88
|
+
*) exit 0 ;;
|
|
89
|
+
esac
|
|
90
|
+
|
|
91
|
+
LOG_PATH="${CC_AUP_BLOCK_LOG_PATH:-$HOME/.claude/aup-block-history.log}"
|
|
92
|
+
[ -f "$LOG_PATH" ] || exit 0
|
|
93
|
+
[ -r "$LOG_PATH" ] || exit 0
|
|
94
|
+
[ -s "$LOG_PATH" ] || exit 0
|
|
95
|
+
|
|
96
|
+
THRESHOLD="${CC_MODEL_SWAP_SUGGESTER_THRESHOLD:-3}"
|
|
97
|
+
WINDOW_MIN="${CC_MODEL_SWAP_SUGGESTER_WINDOW_MIN:-60}"
|
|
98
|
+
TARGET="${CC_MODEL_SWAP_SUGGESTER_TARGET:-claude-sonnet-4-7}"
|
|
99
|
+
|
|
100
|
+
# Validate numeric env vars; fall back to defaults on garbage input.
|
|
101
|
+
case "$THRESHOLD" in
|
|
102
|
+
''|*[!0-9]*) THRESHOLD=3 ;;
|
|
103
|
+
esac
|
|
104
|
+
case "$WINDOW_MIN" in
|
|
105
|
+
''|*[!0-9]*) WINDOW_MIN=60 ;;
|
|
106
|
+
esac
|
|
107
|
+
|
|
108
|
+
# Compute cutoff epoch (now - window_min * 60). Use GNU date first, then BSD date.
|
|
109
|
+
NOW=$(date -u '+%s' 2>/dev/null) || exit 0
|
|
110
|
+
CUTOFF=$((NOW - WINDOW_MIN * 60))
|
|
111
|
+
|
|
112
|
+
ts_to_epoch() {
|
|
113
|
+
local ts="$1"
|
|
114
|
+
date -u -d "$ts" '+%s' 2>/dev/null \
|
|
115
|
+
|| date -j -u -f '%Y-%m-%dT%H:%M:%SZ' "$ts" '+%s' 2>/dev/null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# Count Opus blocks in the window. The log schema is:
|
|
119
|
+
# ISO8601_UTC | MODEL | TOOL | PATTERN_KIND | EXCERPT
|
|
120
|
+
# We only count rows whose model field matches an Opus variant and whose timestamp falls
|
|
121
|
+
# inside the cutoff window. Rows with unparseable timestamps are skipped silently.
|
|
122
|
+
COUNT=0
|
|
123
|
+
while IFS='|' read -r LOG_TS LOG_MODEL _LOG_TOOL _LOG_KIND _LOG_EXCERPT; do
|
|
124
|
+
[ -z "$LOG_TS" ] && continue
|
|
125
|
+
[ -z "$LOG_MODEL" ] && continue
|
|
126
|
+
case "$LOG_MODEL" in
|
|
127
|
+
*opus*|*Opus*|*OPUS*) : ;;
|
|
128
|
+
*) continue ;;
|
|
129
|
+
esac
|
|
130
|
+
LOG_EPOCH=$(ts_to_epoch "$LOG_TS")
|
|
131
|
+
[ -z "$LOG_EPOCH" ] && continue
|
|
132
|
+
case "$LOG_EPOCH" in
|
|
133
|
+
*[!0-9]*) continue ;;
|
|
134
|
+
esac
|
|
135
|
+
if [ "$LOG_EPOCH" -ge "$CUTOFF" ] 2>/dev/null; then
|
|
136
|
+
COUNT=$((COUNT + 1))
|
|
137
|
+
fi
|
|
138
|
+
done < "$LOG_PATH"
|
|
139
|
+
|
|
140
|
+
if [ "$COUNT" -lt "$THRESHOLD" ] 2>/dev/null; then
|
|
141
|
+
exit 0
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
# Threshold crossed → emit one-time advisory. The advisory is deliberately concrete:
|
|
145
|
+
# exact swap command, exact env-var suppression command, and pointers to the partner hooks
|
|
146
|
+
# the operator can lean on after swapping.
|
|
147
|
+
cat >&2 <<EOF
|
|
148
|
+
[model-swap-suggester] $COUNT Usage Policy block(s) recorded on Opus models in the last $WINDOW_MIN minutes.
|
|
149
|
+
|
|
150
|
+
Current model: $ANTHROPIC_MODEL
|
|
151
|
+
Log file: $LOG_PATH
|
|
152
|
+
|
|
153
|
+
The Anthropic Usage Policy classifier has been over-triggering on benign prompts on Opus
|
|
154
|
+
variants since 2026-05-18. Sonnet variants are reportedly unaffected. Recommended swap
|
|
155
|
+
for this session:
|
|
156
|
+
|
|
157
|
+
export ANTHROPIC_MODEL=$TARGET
|
|
158
|
+
|
|
159
|
+
After swapping, the false-positive helper will go silent automatically (Sonnet is treated
|
|
160
|
+
as unaffected). Re-pin Opus once the upstream classifier stabilises.
|
|
161
|
+
|
|
162
|
+
Related hooks (already shipped):
|
|
163
|
+
aup-false-positive-helper.sh — generic awareness advisory (opt-in via CC_AUP_FALSE_POSITIVE_HELPER_REMIND=1)
|
|
164
|
+
aup-block-pattern-logger.sh — records each block to $LOG_PATH
|
|
165
|
+
|
|
166
|
+
To silence this advisory permanently for the current session, even when blocks continue:
|
|
167
|
+
export CC_MODEL_SWAP_SUGGESTER_QUIET=1
|
|
168
|
+
|
|
169
|
+
To raise the threshold instead of silencing:
|
|
170
|
+
export CC_MODEL_SWAP_SUGGESTER_THRESHOLD=10 # only suggest after 10 blocks in $WINDOW_MIN min
|
|
171
|
+
|
|
172
|
+
GitHub tracker (25+ independent reports, 2026-05-18 through 2026-05-27):
|
|
173
|
+
https://github.com/anthropics/claude-code/issues/60366
|
|
174
|
+
https://github.com/anthropics/claude-code/issues/62190
|
|
175
|
+
EOF
|
|
176
|
+
|
|
177
|
+
exit 0
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#!/bin/bash
|
|
1
2
|
INPUT=$(cat)
|
|
2
3
|
COUNTER_FILE="/tmp/.cc-model-check-counter"
|
|
3
4
|
COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo 0)
|
|
@@ -6,7 +7,10 @@ echo "$COUNT" > "$COUNTER_FILE"
|
|
|
6
7
|
if [ $((COUNT % 50)) -ne 0 ]; then
|
|
7
8
|
exit 0
|
|
8
9
|
fi
|
|
9
|
-
|
|
10
|
+
# 記録の場所を推測しない(旧: projects/*/session.jsonl は実在せず、
|
|
11
|
+
# glob が空のまま黙って一度も警告しなかった)。
|
|
12
|
+
SESSION_FILE=$(printf '%s' "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)
|
|
13
|
+
[ -f "$SESSION_FILE" ] || SESSION_FILE=$(ls -t "$HOME"/.claude/projects/*/*.jsonl 2>/dev/null | head -1)
|
|
10
14
|
if [ -n "$SESSION_FILE" ]; then
|
|
11
15
|
MODEL=$(grep -o '"model":"[^"]*"' "$SESSION_FILE" 2>/dev/null | tail -1 | cut -d'"' -f4)
|
|
12
16
|
if echo "$MODEL" | grep -qi "opus-4-7\|opus-4.7"; then
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# model-version-lock.sh — silent mid-session downgrade detector (Incident 4)
|
|
3
|
+
# Why: Opus 4.7 sessions sometimes migrate mid-session to a different model
|
|
4
|
+
# (for example the 1M-context automatic switch, #49541) with no client-
|
|
5
|
+
# visible announcement. The downgrade silently changes token accounting,
|
|
6
|
+
# context window, and quality. This hook writes a lock file containing
|
|
7
|
+
# the model id seen on the first turn of the session and emits a stderr
|
|
8
|
+
# banner whenever a later turn reports a different model id.
|
|
9
|
+
# Event: PostToolUse MATCHER: "*"
|
|
10
|
+
# Action: First per-session call creates
|
|
11
|
+
# ~/.claude/logs/model-locks/<session-id>.model. Subsequent calls read
|
|
12
|
+
# the lock and compare. Mismatch → banner. /model is the expected
|
|
13
|
+
# user path for intentional switches, so the banner says so.
|
|
14
|
+
#
|
|
15
|
+
# To reset: rm -rf ~/.claude/logs/model-locks/ (between sessions that
|
|
16
|
+
# deliberately switch models, otherwise locks accumulate)
|
|
17
|
+
|
|
18
|
+
set -u
|
|
19
|
+
|
|
20
|
+
LOG_DIR="${HOME}/.claude/logs"
|
|
21
|
+
LOCK_DIR="${LOG_DIR}/model-locks"
|
|
22
|
+
mkdir -p "$LOCK_DIR" 2>/dev/null
|
|
23
|
+
|
|
24
|
+
INPUT=$(cat)
|
|
25
|
+
|
|
26
|
+
SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // "unknown"' 2>/dev/null)
|
|
27
|
+
[ "$SESSION_ID" = "unknown" ] && exit 0
|
|
28
|
+
|
|
29
|
+
# Resolve the current model. PostToolUse payload rarely has it directly, so
|
|
30
|
+
# read the last assistant turn from the transcript for its "model" field.
|
|
31
|
+
MODEL=""
|
|
32
|
+
TRANSCRIPT=$(printf '%s' "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)
|
|
33
|
+
if [ -n "$TRANSCRIPT" ] && [ -r "$TRANSCRIPT" ]; then
|
|
34
|
+
MODEL=$(tac "$TRANSCRIPT" 2>/dev/null \
|
|
35
|
+
| grep -m1 '"model"' \
|
|
36
|
+
| jq -r '.message.model // .model // empty' 2>/dev/null)
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# Fallback: environment variable (used by some wrappers and tests).
|
|
40
|
+
if [ -z "$MODEL" ] && [ -n "${CLAUDE_MODEL:-}" ]; then
|
|
41
|
+
MODEL="$CLAUDE_MODEL"
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# If we still do not know the model, do nothing — no false positives.
|
|
45
|
+
[ -z "$MODEL" ] && exit 0
|
|
46
|
+
|
|
47
|
+
LOCK_FILE="${LOCK_DIR}/${SESSION_ID}.model"
|
|
48
|
+
|
|
49
|
+
if [ ! -s "$LOCK_FILE" ]; then
|
|
50
|
+
printf '%s\n' "$MODEL" > "$LOCK_FILE"
|
|
51
|
+
exit 0
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
LOCKED=$(cat "$LOCK_FILE" 2>/dev/null)
|
|
55
|
+
if [ -n "$LOCKED" ] && [ "$LOCKED" != "$MODEL" ]; then
|
|
56
|
+
echo "⚠ model-version-lock: $LOCKED → $MODEL (session $SESSION_ID)" >&2
|
|
57
|
+
echo " Use /model to switch intentionally. Silent migrations (#49541) change token accounting and quality." >&2
|
|
58
|
+
# Update the lock so we do not emit again for the same transition this
|
|
59
|
+
# session. Users who want to see every transition can remove this line.
|
|
60
|
+
printf '%s\n' "$MODEL" > "$LOCK_FILE"
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
exit 0
|
|
@@ -20,6 +20,18 @@
|
|
|
20
20
|
|
|
21
21
|
set -euo pipefail
|
|
22
22
|
|
|
23
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
24
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
25
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
26
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
27
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
28
|
+
_nojq_warned="/tmp/cc-nojq-warned-move-delete-sequence-guard-$PPID"
|
|
29
|
+
[ -f "$_nojq_warned" ] || {
|
|
30
|
+
echo "WARNING [move-delete-sequence-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
31
|
+
: > "$_nojq_warned"
|
|
32
|
+
}
|
|
33
|
+
fi
|
|
34
|
+
|
|
23
35
|
INPUT=$(cat)
|
|
24
36
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
25
37
|
[ -z "$COMMAND" ] && exit 0
|
|
@@ -33,10 +45,29 @@ fi
|
|
|
33
45
|
# Pattern: mv <src> <dst> [&&;||] rm [-rf] <target>
|
|
34
46
|
# We check if the rm target is a parent of, or the same as, the mv source
|
|
35
47
|
|
|
48
|
+
# The extraction below used grep -oP, which is GNU-only. BSD grep (macOS)
|
|
49
|
+
# rejects -P outright, so both variables came back empty, and the two [ -z ]
|
|
50
|
+
# checks under this block read that as "nothing to compare" and exited 0 —
|
|
51
|
+
# the guard was off on macOS with no sign of it. Keep the GNU path exactly as
|
|
52
|
+
# it was and add an equivalent for platforms without PCRE.
|
|
53
|
+
if echo x | grep -qP x 2>/dev/null; then HAS_PCRE=1; else HAS_PCRE=0; fi
|
|
54
|
+
|
|
55
|
+
# Reads a command line on stdin, prints the first argument after $1 and its flags.
|
|
56
|
+
# \b and \K have no POSIX equivalent, so the fallback matches the command word
|
|
57
|
+
# with its leading boundary and strips that prefix afterwards.
|
|
58
|
+
extract_operand() {
|
|
59
|
+
if [ "$HAS_PCRE" = 1 ]; then
|
|
60
|
+
grep -oP "\\b$1\\s+(-[a-zA-Z]+\\s+)*\\K\\S+" 2>/dev/null || true
|
|
61
|
+
else
|
|
62
|
+
grep -oE "(^|[^a-zA-Z])$1[[:space:]]+(-[a-zA-Z]+[[:space:]]+)*[^[:space:]]+" 2>/dev/null \
|
|
63
|
+
| sed -E "s/^[^a-zA-Z]?$1[[:space:]]+(-[a-zA-Z]+[[:space:]]+)*//" || true
|
|
64
|
+
fi
|
|
65
|
+
}
|
|
66
|
+
|
|
36
67
|
# Get all mv source paths (first arg after mv and optional flags)
|
|
37
|
-
MV_SOURCES=$(echo "$COMMAND" |
|
|
68
|
+
MV_SOURCES=$(echo "$COMMAND" | extract_operand mv)
|
|
38
69
|
# Get all rm targets
|
|
39
|
-
RM_TARGETS=$(echo "$COMMAND" |
|
|
70
|
+
RM_TARGETS=$(echo "$COMMAND" | extract_operand rm)
|
|
40
71
|
|
|
41
72
|
[ -z "$MV_SOURCES" ] && exit 0
|
|
42
73
|
[ -z "$RM_TARGETS" ] && exit 0
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# multi-file-plan-routing-gate.sh — Refuse Write/Edit when the current session
|
|
3
|
+
# has touched N or more distinct files without a written plan, route the
|
|
4
|
+
# work through plan-mode before further writes land.
|
|
5
|
+
#
|
|
6
|
+
# Solves: #60506 — supplier-side recommendation #7 in the model's first-person
|
|
7
|
+
# self-report:
|
|
8
|
+
#
|
|
9
|
+
# "Automatic routing of three-or-more-file changes into a plan-mode path.
|
|
10
|
+
# Currently I can sprawl across the codebase without writing a plan first.
|
|
11
|
+
# The hook should refuse the third file-touching Write until a plan exists,
|
|
12
|
+
# and then enforce the plan as the contract for the remaining touches."
|
|
13
|
+
#
|
|
14
|
+
# This is the seventh and final operator-side implementation of the seven
|
|
15
|
+
# recommendations Semih extracted from claude-opus-4-7's self-report. The
|
|
16
|
+
# preceding six are shipped or in PR:
|
|
17
|
+
#
|
|
18
|
+
# 1. Chain templates as architectural rules → CLAUDE.md hook frameworks
|
|
19
|
+
# 2. Drift detector (3-correction repeat) → same-correction-arrest.sh
|
|
20
|
+
# 3. Failure-mode re-injection at Write → claude-md-reinjector.sh
|
|
21
|
+
# 4. Closure-word verification gate → closure-word-verify-gate.sh
|
|
22
|
+
# 5. Evidence requirements for "tested" → evidence-claim-gate.sh (PR #256)
|
|
23
|
+
# 6. Apology weighting at training time → supplier-only
|
|
24
|
+
# 7. THIS HOOK — plan-mode routing for 3+ file changes
|
|
25
|
+
#
|
|
26
|
+
# HOW IT WORKS:
|
|
27
|
+
# 1. Tracks distinct file_path values per session in a small state file.
|
|
28
|
+
# 2. On each PreToolUse Write/Edit/MultiEdit, adds the file_path to the
|
|
29
|
+
# session's set.
|
|
30
|
+
# 3. When the count reaches CC_PLAN_GATE_THRESHOLD (default 3), checks
|
|
31
|
+
# for the presence of a plan file at $PLAN_DIR/<session>.md.
|
|
32
|
+
# 4. If no plan file exists, refuses the Write with exit 2 + system reminder
|
|
33
|
+
# asking the assistant to write the plan first.
|
|
34
|
+
# 5. Once the plan file appears, subsequent writes pass through silently.
|
|
35
|
+
# 6. Stale session state (older than 7 days) is purged automatically.
|
|
36
|
+
#
|
|
37
|
+
# DESIGN PRINCIPLE: don't block the work, route it. The hook does not
|
|
38
|
+
# prevent multi-file changes; it requires that they be planned before being
|
|
39
|
+
# executed. The plan file is the contract; the writes are the contract's
|
|
40
|
+
# fulfillment. The arrest happens at the *third* file (configurable), not
|
|
41
|
+
# the first — single-file and two-file changes are common and not the
|
|
42
|
+
# failure mode #60506 is describing.
|
|
43
|
+
#
|
|
44
|
+
# Related Issues:
|
|
45
|
+
# #60506 (@zean89, 2026-05-19) — original report, recommendation 7
|
|
46
|
+
# #60226 (@suwayama, 2026-05-18) — recognition-without-arrest framework
|
|
47
|
+
# #60177 (@mike-prokhorov) — twelve days, fifty-one commits, no plan
|
|
48
|
+
#
|
|
49
|
+
# TRIGGER: PreToolUse
|
|
50
|
+
# MATCHER: Write|Edit|MultiEdit
|
|
51
|
+
#
|
|
52
|
+
# CONFIGURATION (environment variables):
|
|
53
|
+
# CC_PLAN_GATE_THRESHOLD default 3 — file count that triggers the gate
|
|
54
|
+
# CC_PLAN_GATE_STATE_DIR default /tmp/cc-plan-gate
|
|
55
|
+
# CC_PLAN_GATE_PLAN_DIR default .claude/plans
|
|
56
|
+
# CC_PLAN_GATE_DISABLE set to "1" to disable the gate entirely
|
|
57
|
+
# CC_PLAN_GATE_STATE_TTL default 7 (days) — stale state cleanup window
|
|
58
|
+
#
|
|
59
|
+
# USAGE (settings.json):
|
|
60
|
+
# {
|
|
61
|
+
# "hooks": {
|
|
62
|
+
# "PreToolUse": [{
|
|
63
|
+
# "matcher": "Write|Edit|MultiEdit",
|
|
64
|
+
# "hooks": [{
|
|
65
|
+
# "type": "command",
|
|
66
|
+
# "command": "~/.claude/hooks/multi-file-plan-routing-gate.sh"
|
|
67
|
+
# }]
|
|
68
|
+
# }]
|
|
69
|
+
# }
|
|
70
|
+
# }
|
|
71
|
+
|
|
72
|
+
set -uo pipefail
|
|
73
|
+
|
|
74
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
75
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
76
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
77
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
78
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
79
|
+
_nojq_warned="/tmp/cc-nojq-warned-multi-file-plan-routing-gate-$PPID"
|
|
80
|
+
[ -f "$_nojq_warned" ] || {
|
|
81
|
+
echo "WARNING [multi-file-plan-routing-gate]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
82
|
+
: > "$_nojq_warned"
|
|
83
|
+
}
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
[ "${CC_PLAN_GATE_DISABLE:-0}" = "1" ] && exit 0
|
|
87
|
+
|
|
88
|
+
# OPT-IN: this gate BLOCKS the 3rd+ file Write/Edit in a session when no plan
|
|
89
|
+
# file exists. Ordinary multi-file work (refactors, features touching 3+ files)
|
|
90
|
+
# would be arrested by default, so it is off unless explicitly enabled.
|
|
91
|
+
# Set CC_PLAN_GATE_ENABLE=1 to turn it on.
|
|
92
|
+
[ "${CC_PLAN_GATE_ENABLE:-0}" = "1" ] || exit 0
|
|
93
|
+
|
|
94
|
+
INPUT=$(cat 2>/dev/null || true)
|
|
95
|
+
[ -z "$INPUT" ] && exit 0
|
|
96
|
+
|
|
97
|
+
THRESHOLD="${CC_PLAN_GATE_THRESHOLD:-3}"
|
|
98
|
+
STATE_DIR="${CC_PLAN_GATE_STATE_DIR:-/tmp/cc-plan-gate}"
|
|
99
|
+
PLAN_DIR="${CC_PLAN_GATE_PLAN_DIR:-.claude/plans}"
|
|
100
|
+
TTL_DAYS="${CC_PLAN_GATE_STATE_TTL:-7}"
|
|
101
|
+
|
|
102
|
+
mkdir -p "$STATE_DIR" 2>/dev/null || true
|
|
103
|
+
|
|
104
|
+
# Stale state cleanup. Purge session files older than TTL_DAYS.
|
|
105
|
+
find "$STATE_DIR" -type f -mtime "+${TTL_DAYS}" -delete 2>/dev/null || true
|
|
106
|
+
|
|
107
|
+
# Extract session_id and file_path from input.
|
|
108
|
+
SESSION_ID=$(printf '%s' "$INPUT" | jq -r '
|
|
109
|
+
.session_id //
|
|
110
|
+
.stop_input.session_id //
|
|
111
|
+
.transcript[-1].session_id //
|
|
112
|
+
"default"
|
|
113
|
+
' 2>/dev/null)
|
|
114
|
+
[ -z "$SESSION_ID" ] && SESSION_ID="default"
|
|
115
|
+
|
|
116
|
+
# Sanitize session_id for filename use
|
|
117
|
+
SAFE_SESSION=$(printf '%s' "$SESSION_ID" | tr -c '[:alnum:]_-' '_' | head -c 64)
|
|
118
|
+
|
|
119
|
+
FILE_PATH=$(printf '%s' "$INPUT" | jq -r '
|
|
120
|
+
.tool_input.file_path //
|
|
121
|
+
empty
|
|
122
|
+
' 2>/dev/null)
|
|
123
|
+
|
|
124
|
+
# No file_path → nothing to track (e.g. MultiEdit without path, fall back
|
|
125
|
+
# to tool_input.file as a fallback)
|
|
126
|
+
[ -z "$FILE_PATH" ] && FILE_PATH=$(printf '%s' "$INPUT" | jq -r '.tool_input.file // empty' 2>/dev/null)
|
|
127
|
+
[ -z "$FILE_PATH" ] && exit 0
|
|
128
|
+
|
|
129
|
+
STATE_FILE="$STATE_DIR/${SAFE_SESSION}.files"
|
|
130
|
+
|
|
131
|
+
# Add file_path to the session's set (deduplicated).
|
|
132
|
+
touch "$STATE_FILE"
|
|
133
|
+
if ! grep -Fxq "$FILE_PATH" "$STATE_FILE" 2>/dev/null; then
|
|
134
|
+
echo "$FILE_PATH" >> "$STATE_FILE"
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
# Count distinct files touched this session.
|
|
138
|
+
FILE_COUNT=$(wc -l < "$STATE_FILE" 2>/dev/null | tr -d ' ')
|
|
139
|
+
FILE_COUNT="${FILE_COUNT:-0}"
|
|
140
|
+
|
|
141
|
+
# Below threshold → nothing to gate.
|
|
142
|
+
if [ "$FILE_COUNT" -lt "$THRESHOLD" ]; then
|
|
143
|
+
exit 0
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# At-or-above threshold. Is there a plan file for this session?
|
|
147
|
+
PLAN_FILE="$PLAN_DIR/${SAFE_SESSION}.md"
|
|
148
|
+
PLAN_FILE_ALT="$PLAN_DIR/plan-${SAFE_SESSION}.md"
|
|
149
|
+
PLAN_FILE_GENERIC="$PLAN_DIR/plan.md"
|
|
150
|
+
|
|
151
|
+
if [ -f "$PLAN_FILE" ] || [ -f "$PLAN_FILE_ALT" ] || [ -f "$PLAN_FILE_GENERIC" ]; then
|
|
152
|
+
# Plan exists — work has been routed, allow.
|
|
153
|
+
exit 0
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# No plan. Refuse the Write/Edit and ask the assistant to write a plan.
|
|
157
|
+
cat >&2 <<EOF
|
|
158
|
+
<system-reminder>
|
|
159
|
+
MULTI-FILE CHANGE WITHOUT A PLAN — this session has now touched $FILE_COUNT
|
|
160
|
+
distinct files via Write/Edit/MultiEdit, and no plan file exists at
|
|
161
|
+
$PLAN_FILE.
|
|
162
|
+
|
|
163
|
+
This is the failure mode documented in #60506 (recommendation 7): the model
|
|
164
|
+
sprawls across the codebase without writing a plan first, and the resulting
|
|
165
|
+
changes are scattered, partially-wired, or contradict each other. The cost
|
|
166
|
+
of an unplanned multi-file change lands on the operator at review time.
|
|
167
|
+
|
|
168
|
+
The files touched so far in this session:
|
|
169
|
+
$(sed 's/^/ - /' "$STATE_FILE" 2>/dev/null | head -20)
|
|
170
|
+
|
|
171
|
+
Before the next Write/Edit lands, do one of the following:
|
|
172
|
+
|
|
173
|
+
1. Write a plan to $PLAN_FILE that lists the goal, the files involved,
|
|
174
|
+
the order of changes, and the verification step for each file. Once
|
|
175
|
+
the plan file exists, this gate auto-clears and writes proceed silently.
|
|
176
|
+
|
|
177
|
+
2. If the changes are unrelated and a unified plan is not appropriate,
|
|
178
|
+
split the work across separate sessions — one task per session, gate
|
|
179
|
+
resets at session end (TTL 7 days).
|
|
180
|
+
|
|
181
|
+
3. If this is a refactor where multi-file scope was inherent from the
|
|
182
|
+
start, the plan can be one or two paragraphs — the gate's purpose
|
|
183
|
+
is to require explicit forethought, not extensive documentation.
|
|
184
|
+
|
|
185
|
+
To disable this gate for design discussions, retrospectives, or sessions
|
|
186
|
+
where multi-file scope is not the failure mode you are guarding against,
|
|
187
|
+
set CC_PLAN_GATE_DISABLE=1 in your environment. The threshold can be
|
|
188
|
+
raised via CC_PLAN_GATE_THRESHOLD (default 3).
|
|
189
|
+
</system-reminder>
|
|
190
|
+
EOF
|
|
191
|
+
|
|
192
|
+
exit 2
|