cc-safe-setup 29.7.0 → 30.0.4
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 +419 -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-circuit-breaker.sh +72 -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 +89 -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/thinking-stall-detector.sh +61 -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 +1699 -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-circuit-breaker.sh +134 -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-exploration-budget-guard.sh +164 -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-thinking-stall-detector.sh +151 -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
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# memory-orphan-detector.sh — Detect Claude Code memory that may have been
|
|
3
|
+
# orphaned when the working directory was renamed.
|
|
4
|
+
#
|
|
5
|
+
# Solves: #61349 — "[BUG] Memory orphaned when working directory is renamed"
|
|
6
|
+
# Filed 2026-05-22 by adamcopley.
|
|
7
|
+
#
|
|
8
|
+
# Claude Code stores per-project memory at
|
|
9
|
+
# ~/.claude/projects/<encoded-working-dir>/memory/
|
|
10
|
+
# where <encoded-working-dir> is the absolute working directory path with
|
|
11
|
+
# every `/` and `.` rewritten as `-`.
|
|
12
|
+
#
|
|
13
|
+
# When the operator renames or moves a working directory, Claude Code does
|
|
14
|
+
# not migrate the memory. Instead, on next session start it silently creates
|
|
15
|
+
# a fresh empty memory directory at the new encoded path. The old memory
|
|
16
|
+
# directory remains on disk forever — orphaned, not loaded by any session,
|
|
17
|
+
# with no notification to the operator. The accumulated investment (user
|
|
18
|
+
# role, project conventions, validated feedback, references to external
|
|
19
|
+
# systems) is effectively lost.
|
|
20
|
+
#
|
|
21
|
+
# This hook runs on SessionStart and emits a warning when:
|
|
22
|
+
# (a) the CURRENT working directory's memory is empty (≤ N entries), AND
|
|
23
|
+
# (b) the current working directory has substantive git history (suggesting
|
|
24
|
+
# prior work in the project that would have accumulated memory), AND
|
|
25
|
+
# (c) one or more OTHER memory directories on disk have substantive content
|
|
26
|
+
# (≥ M entries or ≥ K bytes), making them candidate orphans
|
|
27
|
+
#
|
|
28
|
+
# All three signals together: high likelihood of an orphaning event. The hook
|
|
29
|
+
# does not block session start. It surfaces the candidate orphan directories
|
|
30
|
+
# so the operator can decide whether to migrate them manually.
|
|
31
|
+
#
|
|
32
|
+
# Path decoding is LOSSY (a `-` in the encoded path could have originated as
|
|
33
|
+
# either a `/` or a literal `-` in the working directory name). This hook
|
|
34
|
+
# does NOT attempt to decode and verify path existence; it uses encoded-form
|
|
35
|
+
# similarity heuristics instead.
|
|
36
|
+
#
|
|
37
|
+
# TRIGGER: SessionStart
|
|
38
|
+
# MATCHER: ""
|
|
39
|
+
#
|
|
40
|
+
# CONFIGURATION (environment variables):
|
|
41
|
+
# CC_PROJECTS_DIR default ~/.claude/projects
|
|
42
|
+
# CC_MEMORY_ORPHAN_DISABLE set to "1" to disable
|
|
43
|
+
# CC_MEMORY_EMPTY_THRESHOLD max entries to consider "empty" (default 1)
|
|
44
|
+
# CC_MEMORY_ORPHAN_THRESHOLD min entries for candidate orphan (default 5)
|
|
45
|
+
# CC_MEMORY_ORPHAN_MIN_BYTES min bytes for candidate orphan (default 2048)
|
|
46
|
+
#
|
|
47
|
+
# USAGE (settings.json):
|
|
48
|
+
# {
|
|
49
|
+
# "hooks": {
|
|
50
|
+
# "SessionStart": [{
|
|
51
|
+
# "matcher": "",
|
|
52
|
+
# "hooks": [{
|
|
53
|
+
# "type": "command",
|
|
54
|
+
# "command": "~/.claude/hooks/memory-orphan-detector.sh"
|
|
55
|
+
# }]
|
|
56
|
+
# }]
|
|
57
|
+
# }
|
|
58
|
+
# }
|
|
59
|
+
|
|
60
|
+
set -uo pipefail
|
|
61
|
+
|
|
62
|
+
[ "${CC_MEMORY_ORPHAN_DISABLE:-0}" = "1" ] && exit 0
|
|
63
|
+
|
|
64
|
+
PROJECTS_DIR="${CC_PROJECTS_DIR:-$HOME/.claude/projects}"
|
|
65
|
+
EMPTY_THRESHOLD="${CC_MEMORY_EMPTY_THRESHOLD:-1}"
|
|
66
|
+
ORPHAN_THRESHOLD="${CC_MEMORY_ORPHAN_THRESHOLD:-5}"
|
|
67
|
+
ORPHAN_MIN_BYTES="${CC_MEMORY_ORPHAN_MIN_BYTES:-2048}"
|
|
68
|
+
|
|
69
|
+
[ ! -d "$PROJECTS_DIR" ] && exit 0
|
|
70
|
+
|
|
71
|
+
CWD="$(pwd)"
|
|
72
|
+
|
|
73
|
+
# Compute the encoded form of the current working directory.
|
|
74
|
+
encode_path() {
|
|
75
|
+
# / → -, . → -
|
|
76
|
+
printf '%s' "$1" | tr '/.' '--'
|
|
77
|
+
}
|
|
78
|
+
CWD_ENCODED="$(encode_path "$CWD")"
|
|
79
|
+
CURRENT_MEMORY_DIR="$PROJECTS_DIR/${CWD_ENCODED}/memory"
|
|
80
|
+
|
|
81
|
+
# (a) Is current memory empty?
|
|
82
|
+
CURRENT_COUNT=0
|
|
83
|
+
if [ -d "$CURRENT_MEMORY_DIR" ]; then
|
|
84
|
+
CURRENT_COUNT=$(find "$CURRENT_MEMORY_DIR" -maxdepth 1 -type f 2>/dev/null | wc -l)
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# Not empty → likely fine. Exit silently.
|
|
88
|
+
if [ "$CURRENT_COUNT" -gt "$EMPTY_THRESHOLD" ]; then
|
|
89
|
+
exit 0
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# (b) Does current working directory have git history?
|
|
93
|
+
HAS_GIT_HISTORY=0
|
|
94
|
+
if [ -d "$CWD/.git" ] || git -C "$CWD" rev-parse --git-dir > /dev/null 2>&1; then
|
|
95
|
+
COMMITS=$(git -C "$CWD" rev-list --count HEAD 2>/dev/null || echo 0)
|
|
96
|
+
if [ "$COMMITS" -gt 5 ]; then
|
|
97
|
+
HAS_GIT_HISTORY=1
|
|
98
|
+
fi
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# No git history → not a strong signal of prior work. Exit silently.
|
|
102
|
+
if [ "$HAS_GIT_HISTORY" -eq 0 ]; then
|
|
103
|
+
exit 0
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# (c) Find candidate orphans: other memory directories with content.
|
|
107
|
+
ORPHAN_CANDIDATES=()
|
|
108
|
+
for d in "$PROJECTS_DIR"/*/; do
|
|
109
|
+
[ -d "$d" ] || continue
|
|
110
|
+
base="${d%/}"
|
|
111
|
+
base="${base##*/}"
|
|
112
|
+
# Skip current
|
|
113
|
+
if [ "$base" = "$CWD_ENCODED" ]; then continue; fi
|
|
114
|
+
mem_dir="$d/memory"
|
|
115
|
+
[ ! -d "$mem_dir" ] && continue
|
|
116
|
+
cnt=$(find "$mem_dir" -maxdepth 1 -type f 2>/dev/null | wc -l)
|
|
117
|
+
bytes=$(du -sb "$mem_dir" 2>/dev/null | awk '{print $1}')
|
|
118
|
+
bytes="${bytes:-0}"
|
|
119
|
+
if [ "$cnt" -ge "$ORPHAN_THRESHOLD" ] && [ "$bytes" -ge "$ORPHAN_MIN_BYTES" ]; then
|
|
120
|
+
# Compute encoded-form similarity: shared prefix length / longer length
|
|
121
|
+
# (very rough — but better than nothing without lossless decode)
|
|
122
|
+
ORPHAN_CANDIDATES+=("$base|$cnt|$bytes")
|
|
123
|
+
fi
|
|
124
|
+
done
|
|
125
|
+
|
|
126
|
+
# No candidate orphans → nothing to report.
|
|
127
|
+
if [ "${#ORPHAN_CANDIDATES[@]}" -eq 0 ]; then
|
|
128
|
+
exit 0
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
# Limit output to top 5 candidates, sorted by byte size descending.
|
|
132
|
+
TOP=()
|
|
133
|
+
while IFS= read -r line; do
|
|
134
|
+
TOP+=("$line")
|
|
135
|
+
done < <(printf '%s\n' "${ORPHAN_CANDIDATES[@]}" | sort -t'|' -k3 -rn | head -5)
|
|
136
|
+
|
|
137
|
+
EXTRA=$(( ${#ORPHAN_CANDIDATES[@]} - ${#TOP[@]} ))
|
|
138
|
+
|
|
139
|
+
cat >&2 <<EOF
|
|
140
|
+
<system-reminder>
|
|
141
|
+
POSSIBLE MEMORY ORPHANING DETECTED — the current working directory's memory
|
|
142
|
+
is empty, but this project has substantive git history AND there are other
|
|
143
|
+
memory directories on disk with non-trivial content. One of them may be the
|
|
144
|
+
prior version of this project's memory that was orphaned by a directory
|
|
145
|
+
rename, move, or symlink change.
|
|
146
|
+
|
|
147
|
+
This is the failure mode documented in anthropics/claude-code#61349. Claude
|
|
148
|
+
Code derives memory location from the encoded working-directory path
|
|
149
|
+
(everything in / and . becomes -). Renaming the directory produces a
|
|
150
|
+
different encoded form, so a fresh empty memory directory is created at
|
|
151
|
+
the new path while the old memory stays on disk, unreferenced.
|
|
152
|
+
|
|
153
|
+
Current working directory: $CWD
|
|
154
|
+
Encoded form: $CWD_ENCODED
|
|
155
|
+
Current memory: $CURRENT_COUNT file(s) at $CURRENT_MEMORY_DIR
|
|
156
|
+
|
|
157
|
+
Candidate orphan memory directories (top 5 by size):
|
|
158
|
+
EOF
|
|
159
|
+
for entry in "${TOP[@]}"; do
|
|
160
|
+
IFS='|' read -r b c bytes <<< "$entry"
|
|
161
|
+
# Print bytes in KB for readability
|
|
162
|
+
kb=$(( bytes / 1024 ))
|
|
163
|
+
echo " $PROJECTS_DIR/$b/memory/ ($c files, ${kb}KB)" >&2
|
|
164
|
+
done
|
|
165
|
+
if [ "$EXTRA" -gt 0 ]; then
|
|
166
|
+
echo " ... and $EXTRA more" >&2
|
|
167
|
+
fi
|
|
168
|
+
cat >&2 <<EOF
|
|
169
|
+
|
|
170
|
+
Recommended next actions for the operator (NOT for the model to perform
|
|
171
|
+
automatically):
|
|
172
|
+
1. Inspect the candidate directories. The encoded path is the original
|
|
173
|
+
working directory's absolute path with every / and . replaced by -.
|
|
174
|
+
Note that this encoding is LOSSY — a - in the encoded form could be a
|
|
175
|
+
literal - or a / in the original path. Decode by trying both.
|
|
176
|
+
2. If you recognize an orphan that corresponds to a previous location of
|
|
177
|
+
this project (before a rename/move), copy its memory files into the
|
|
178
|
+
current memory directory: cp -r <orphan>/memory/* $CURRENT_MEMORY_DIR/
|
|
179
|
+
3. To prevent future orphaning, version-control the memory directory:
|
|
180
|
+
store memory in the project's git tree (e.g., .claude-memory/) and
|
|
181
|
+
symlink the system memory directory to it. Renames preserve the link.
|
|
182
|
+
|
|
183
|
+
To disable this detector, set CC_MEMORY_ORPHAN_DISABLE=1.
|
|
184
|
+
</system-reminder>
|
|
185
|
+
EOF
|
|
186
|
+
|
|
187
|
+
# Exit 0 — detection-only.
|
|
188
|
+
exit 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
|