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,210 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ai-slop-punctuation-arrest.sh — Refuse Write/Edit to public-text files
|
|
3
|
+
# that contain AI-slop punctuation patterns the operator has chosen to
|
|
4
|
+
# exclude — em-dash (—) and the double-hyphen substitute the model often
|
|
5
|
+
# emits when prose-instructed to "stop using em dashes".
|
|
6
|
+
#
|
|
7
|
+
# Background: the punctuation patterns that read as machine-authored prose
|
|
8
|
+
# are recognised widely enough that they function as a heuristic-trust
|
|
9
|
+
# signal against the artefact's writer (operator credibility cost), even
|
|
10
|
+
# when the content itself is honest and substantive. The most-cited
|
|
11
|
+
# offenders are em-dash (—) and its close substitute the double-hyphen
|
|
12
|
+
# (` -- ` in prose context).
|
|
13
|
+
#
|
|
14
|
+
# The active failure mode this hook addresses is the *substitution-by-
|
|
15
|
+
# default* variant of recognition-without-arrest (@suwayama's second
|
|
16
|
+
# comment on #60226): the operator says "stop using em-dashes", the model
|
|
17
|
+
# acknowledges the rule at the recognition layer, and emits double-hyphens
|
|
18
|
+
# in lieu — a near-substitute that satisfies the literal rule and violates
|
|
19
|
+
# the operator's intent. This is the punctuation-surface instance of the
|
|
20
|
+
# substitution mechanism @suwayama named on the Spotify-playlist case.
|
|
21
|
+
#
|
|
22
|
+
# This hook is not opinionated about whether you should ban em-dashes. It
|
|
23
|
+
# is opinionated that *if* you choose to ban them, the ban must hold under
|
|
24
|
+
# the model's tendency to substitute — the runtime gate must catch both
|
|
25
|
+
# the original and the near-substitute.
|
|
26
|
+
#
|
|
27
|
+
# Background from the customer pain side: this hook was prompted by a
|
|
28
|
+
# 2026-05-20 r/ClaudeAI thread ("I told Claude to stop using em dashes. It
|
|
29
|
+
# happily obliged...") in which the operator reported the substitution
|
|
30
|
+
# failure ("Now Claude is using double hyphens in lieu of em dashes") and
|
|
31
|
+
# explicitly asked for runtime defences.
|
|
32
|
+
#
|
|
33
|
+
# Related Reports:
|
|
34
|
+
# #60226 (@suwayama) — recognition-without-arrest framework, and
|
|
35
|
+
# substitution-by-default variant in the second
|
|
36
|
+
# comment of that thread
|
|
37
|
+
# #60506 (@zean89) — six-day drift across artefact emissions
|
|
38
|
+
#
|
|
39
|
+
# TRIGGER: PreToolUse
|
|
40
|
+
# MATCHER: Write|Edit
|
|
41
|
+
#
|
|
42
|
+
# HOW IT WORKS:
|
|
43
|
+
# 1. Reads PreToolUse input via stdin.
|
|
44
|
+
# 2. For Write/Edit, matches the target path against the configured
|
|
45
|
+
# file-type filter (default: markdown, .md, .mdx, .markdown).
|
|
46
|
+
# 3. Scans the artefact content for em-dash (—) and the double-hyphen
|
|
47
|
+
# substitute in prose context (space-dash-dash-space).
|
|
48
|
+
# 4. Skips code-block content (between fenced ``` blocks) and inline
|
|
49
|
+
# code (between ` `), so command-line flags and code samples inside
|
|
50
|
+
# a markdown file do not false-positive.
|
|
51
|
+
# 5. If any banned pattern survives the skip, exits 2 with a stderr
|
|
52
|
+
# reminder naming the matched pattern and the substitution-by-default
|
|
53
|
+
# mechanism. A content-hash cache (default 600s) lets a re-emission
|
|
54
|
+
# pass through if the agent stands by the artefact deliberately.
|
|
55
|
+
#
|
|
56
|
+
# CONFIGURATION (environment variables):
|
|
57
|
+
# CC_AI_SLOP_DISABLE set to "1" to disable the gate
|
|
58
|
+
# CC_AI_SLOP_STATE_DIR default /tmp/cc-ai-slop-cache
|
|
59
|
+
# CC_AI_SLOP_CACHE_TTL_SECONDS default 600 — same-hash re-emit window
|
|
60
|
+
# CC_AI_SLOP_FILE_PATTERNS default built-in — regex of file paths
|
|
61
|
+
# to gate (case-insensitive)
|
|
62
|
+
# CC_AI_SLOP_PUNCT_PATTERNS default built-in — pipe-separated
|
|
63
|
+
# extended-regex patterns
|
|
64
|
+
# of banned punctuation
|
|
65
|
+
#
|
|
66
|
+
# USAGE (settings.json):
|
|
67
|
+
# {
|
|
68
|
+
# "hooks": {
|
|
69
|
+
# "PreToolUse": [{
|
|
70
|
+
# "matcher": "Write|Edit",
|
|
71
|
+
# "hooks": [{
|
|
72
|
+
# "type": "command",
|
|
73
|
+
# "command": "~/.claude/hooks/ai-slop-punctuation-arrest.sh"
|
|
74
|
+
# }]
|
|
75
|
+
# }]
|
|
76
|
+
# }
|
|
77
|
+
# }
|
|
78
|
+
|
|
79
|
+
set -uo pipefail
|
|
80
|
+
|
|
81
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
82
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
83
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
84
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
85
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
86
|
+
_nojq_warned="/tmp/cc-nojq-warned-ai-slop-punctuation-arrest-$PPID"
|
|
87
|
+
[ -f "$_nojq_warned" ] || {
|
|
88
|
+
echo "WARNING [ai-slop-punctuation-arrest]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
89
|
+
: > "$_nojq_warned"
|
|
90
|
+
}
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
[ "${CC_AI_SLOP_DISABLE:-0}" = "1" ] && exit 0
|
|
94
|
+
|
|
95
|
+
# OPT-IN: this gate BLOCKS Write/Edit to markdown containing em-dash or "--",
|
|
96
|
+
# which are legitimate punctuation in normal prose (English and Japanese), so a
|
|
97
|
+
# default-on block would arrest ordinary documentation writing. Off unless
|
|
98
|
+
# explicitly enabled. Set CC_AI_SLOP_ENABLE=1 to turn it on.
|
|
99
|
+
[ "${CC_AI_SLOP_ENABLE:-0}" = "1" ] || exit 0
|
|
100
|
+
|
|
101
|
+
INPUT=$(cat 2>/dev/null || true)
|
|
102
|
+
[ -z "$INPUT" ] && exit 0
|
|
103
|
+
|
|
104
|
+
STATE_DIR="${CC_AI_SLOP_STATE_DIR:-/tmp/cc-ai-slop-cache}"
|
|
105
|
+
CACHE_TTL="${CC_AI_SLOP_CACHE_TTL_SECONDS:-600}"
|
|
106
|
+
|
|
107
|
+
# Default file filter: markdown variants
|
|
108
|
+
DEFAULT_FILES='\.(md|mdx|markdown|mkd|txt)$'
|
|
109
|
+
FILE_PATTERNS="${CC_AI_SLOP_FILE_PATTERNS:-$DEFAULT_FILES}"
|
|
110
|
+
|
|
111
|
+
# Default punctuation patterns:
|
|
112
|
+
# em-dash: the literal character —
|
|
113
|
+
# double-hyphen substitute: space, dash, dash, space (in prose context)
|
|
114
|
+
# or word-dash-dash-word (attached substitute)
|
|
115
|
+
DEFAULT_PUNCT='—| -- |[[:alnum:]]--[[:alnum:]]'
|
|
116
|
+
PUNCT_PATTERNS="${CC_AI_SLOP_PUNCT_PATTERNS:-$DEFAULT_PUNCT}"
|
|
117
|
+
|
|
118
|
+
mkdir -p "$STATE_DIR" 2>/dev/null || true
|
|
119
|
+
|
|
120
|
+
TOOL_NAME=$(printf '%s' "$INPUT" | jq -r '.tool_name // .tool // empty' 2>/dev/null)
|
|
121
|
+
[ -z "$TOOL_NAME" ] && exit 0
|
|
122
|
+
|
|
123
|
+
case "$TOOL_NAME" in
|
|
124
|
+
Write|Edit) ;;
|
|
125
|
+
*) exit 0 ;;
|
|
126
|
+
esac
|
|
127
|
+
|
|
128
|
+
FILE_PATH=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
129
|
+
[ -z "$FILE_PATH" ] && exit 0
|
|
130
|
+
|
|
131
|
+
# File-type filter
|
|
132
|
+
printf '%s' "$FILE_PATH" | grep -Eiq "$FILE_PATTERNS" || exit 0
|
|
133
|
+
|
|
134
|
+
# Extract the content (Write) or new_string (Edit)
|
|
135
|
+
ARTEFACT_BODY=""
|
|
136
|
+
if [ "$TOOL_NAME" = "Write" ]; then
|
|
137
|
+
ARTEFACT_BODY=$(printf '%s' "$INPUT" | jq -r '.tool_input.content // empty' 2>/dev/null)
|
|
138
|
+
else
|
|
139
|
+
ARTEFACT_BODY=$(printf '%s' "$INPUT" | jq -r '.tool_input.new_string // empty' 2>/dev/null)
|
|
140
|
+
fi
|
|
141
|
+
[ -z "$ARTEFACT_BODY" ] && exit 0
|
|
142
|
+
|
|
143
|
+
# Strip code blocks (```…```) and inline code (`…`) so command-line flags
|
|
144
|
+
# and code samples in the document do not false-positive.
|
|
145
|
+
BODY_FOR_SCAN=$(printf '%s' "$ARTEFACT_BODY" \
|
|
146
|
+
| awk 'BEGIN{in_fence=0} /^```/{in_fence=!in_fence; next} !in_fence{print}' \
|
|
147
|
+
| sed 's|`[^`]*`||g')
|
|
148
|
+
|
|
149
|
+
# Does the surviving prose carry a banned punctuation pattern?
|
|
150
|
+
if ! printf '%s' "$BODY_FOR_SCAN" | grep -Eq "$PUNCT_PATTERNS"; then
|
|
151
|
+
exit 0
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
# Hash dedup: if the same content was just re-emitted, the agent has
|
|
155
|
+
# considered the gate and stands by the artefact — let it pass.
|
|
156
|
+
HASH=$(printf '%s' "$ARTEFACT_BODY" | sha256sum 2>/dev/null | awk '{print $1}')
|
|
157
|
+
[ -z "$HASH" ] && exit 0
|
|
158
|
+
|
|
159
|
+
CACHE_FILE="$STATE_DIR/$HASH"
|
|
160
|
+
NOW=$(date +%s)
|
|
161
|
+
if [ -f "$CACHE_FILE" ]; then
|
|
162
|
+
CACHED_AT=$(cat "$CACHE_FILE" 2>/dev/null || echo 0)
|
|
163
|
+
AGE=$((NOW - CACHED_AT))
|
|
164
|
+
if [ "$AGE" -lt "$CACHE_TTL" ]; then
|
|
165
|
+
exit 0
|
|
166
|
+
fi
|
|
167
|
+
fi
|
|
168
|
+
echo "$NOW" > "$CACHE_FILE"
|
|
169
|
+
find "$STATE_DIR" -type f -mmin +1440 -delete 2>/dev/null || true
|
|
170
|
+
|
|
171
|
+
# Surface the first matched pattern for the reminder
|
|
172
|
+
MATCHED=$(printf '%s' "$BODY_FOR_SCAN" | grep -Eo "$PUNCT_PATTERNS" | head -1)
|
|
173
|
+
|
|
174
|
+
cat >&2 <<EOF
|
|
175
|
+
<system-reminder>
|
|
176
|
+
AI-SLOP PUNCTUATION DETECTED in $FILE_PATH — pattern matched: "$MATCHED"
|
|
177
|
+
|
|
178
|
+
This gate exists because banning em-dash (—) at the prose layer alone
|
|
179
|
+
fails under the substitution-by-default mechanism (@suwayama, #60226):
|
|
180
|
+
when the operator says "stop using em-dashes", the model often acknowledges
|
|
181
|
+
at the recognition layer and emits the near-substitute (double-hyphen,
|
|
182
|
+
" -- ") that satisfies the literal rule and violates the intent.
|
|
183
|
+
|
|
184
|
+
Both the original and the near-substitute are gated here.
|
|
185
|
+
|
|
186
|
+
To resolve:
|
|
187
|
+
|
|
188
|
+
1. Replace em-dash (—) with a natural punctuation mark for the language
|
|
189
|
+
and register: colon, comma, period, or semicolon depending on the
|
|
190
|
+
pause and the syntactic role the dash was carrying.
|
|
191
|
+
|
|
192
|
+
2. Replace double-hyphen substitutes (" -- " in prose) with the same
|
|
193
|
+
natural punctuation. If a double-hyphen is appearing as a
|
|
194
|
+
command-line flag inside a code block (\`--flag\`) or fenced code,
|
|
195
|
+
wrap it as code — the gate's body scan strips code blocks and inline
|
|
196
|
+
code before matching, so a properly-fenced flag will not trigger.
|
|
197
|
+
|
|
198
|
+
3. If the punctuation is intentional (technical document quoting
|
|
199
|
+
legitimate uses, retrospective analysis, etc.), re-emit the same
|
|
200
|
+
content within ${CACHE_TTL}s and the hash-match will let it through
|
|
201
|
+
once, or set CC_AI_SLOP_DISABLE=1 for the session.
|
|
202
|
+
|
|
203
|
+
To narrow the scope (e.g. only flag em-dash and not double-hyphens), set
|
|
204
|
+
CC_AI_SLOP_PUNCT_PATTERNS to a custom regex.
|
|
205
|
+
To narrow the file types (e.g. only README and CHANGELOG), set
|
|
206
|
+
CC_AI_SLOP_FILE_PATTERNS to a custom regex.
|
|
207
|
+
</system-reminder>
|
|
208
|
+
EOF
|
|
209
|
+
|
|
210
|
+
exit 2
|
package/examples/allowlist.sh
CHANGED
|
@@ -9,6 +9,16 @@
|
|
|
9
9
|
# Use case: Highly sensitive environments where you want to
|
|
10
10
|
# enumerate exactly what Claude Code can do.
|
|
11
11
|
#
|
|
12
|
+
# Known limits — read these before trusting it as your only layer:
|
|
13
|
+
# * Redirection is not inspected. An approved command word can still write
|
|
14
|
+
# anywhere ("echo evil > ~/.bashrc"). Enumerating safe write targets is a
|
|
15
|
+
# different job than enumerating command names; pair this with a hook that
|
|
16
|
+
# judges the destination.
|
|
17
|
+
# * Approval is per command name, not per argument. "git commit" is approved
|
|
18
|
+
# regardless of its flags.
|
|
19
|
+
# * Patterns are yours to own. Everything not listed is blocked, so an
|
|
20
|
+
# incomplete list fails closed (annoying) rather than open (dangerous).
|
|
21
|
+
#
|
|
12
22
|
# Born from GitHub Issue #37471 (Immutable session manifest)
|
|
13
23
|
#
|
|
14
24
|
# Usage: Add to settings.json as a PreToolUse hook
|
|
@@ -22,7 +32,26 @@
|
|
|
22
32
|
# }
|
|
23
33
|
# }
|
|
24
34
|
#
|
|
25
|
-
# TRIGGER:
|
|
35
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
36
|
+
#
|
|
37
|
+
# This line used to say PermissionRequest, contradicting the settings.json
|
|
38
|
+
# example above. PreToolUse is the correct one: the reference states it fires
|
|
39
|
+
# "Before a tool call executes", while PermissionRequest fires only "When a
|
|
40
|
+
# tool call needs a permission decision" — which never happens under auto mode
|
|
41
|
+
# or bypassed permissions. A guard registered on PermissionRequest would sit
|
|
42
|
+
# silent in exactly the sessions that need it most.
|
|
43
|
+
|
|
44
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
45
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
46
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
47
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
48
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
49
|
+
_nojq_warned="/tmp/cc-nojq-warned-allowlist-$PPID"
|
|
50
|
+
[ -f "$_nojq_warned" ] || {
|
|
51
|
+
echo "WARNING [allowlist]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
52
|
+
: > "$_nojq_warned"
|
|
53
|
+
}
|
|
54
|
+
fi
|
|
26
55
|
|
|
27
56
|
INPUT=$(cat)
|
|
28
57
|
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
@@ -53,14 +82,45 @@ ALLOWED=(
|
|
|
53
82
|
"^\s*(cd|mkdir|touch)"
|
|
54
83
|
)
|
|
55
84
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
85
|
+
# A single line can chain several commands. Every pattern above is anchored with
|
|
86
|
+
# "^", so matching them against the whole string only ever inspects the first
|
|
87
|
+
# word: "echo hi && rm -rf /" matched "^\s*echo" and was approved. For a hook
|
|
88
|
+
# whose stated purpose is to enumerate exactly what Claude Code may do, that is
|
|
89
|
+
# not a gap — it is the allowlist enforcing nothing past the first segment.
|
|
90
|
+
# Claude Code closed the same class of hole in its own permission checking
|
|
91
|
+
# (2.1.216, "Fixed Bash permission checking for compound statements").
|
|
92
|
+
#
|
|
93
|
+
# So: split on the operators that begin a new command, and require EVERY segment
|
|
94
|
+
# to be approved. One unapproved segment blocks the line.
|
|
95
|
+
SEGMENTS=$(printf '%s' "$COMMAND" | sed -E 's/(\|\||&&|[;|&])/\n/g')
|
|
96
|
+
|
|
97
|
+
# Command substitution executes text that never shows up as a segment, so no
|
|
98
|
+
# amount of splitting can vet it. Refuse instead of guessing.
|
|
99
|
+
if printf '%s' "$COMMAND" | grep -qE '\$\(|`'; then
|
|
100
|
+
echo "BLOCKED: command substitution is not allowed by the allowlist" >&2
|
|
101
|
+
echo "Command: $COMMAND" >&2
|
|
102
|
+
exit 2
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
while IFS= read -r SEGMENT; do
|
|
106
|
+
# sed leaves an empty field where a two-character operator was split
|
|
107
|
+
[[ -z "${SEGMENT//[[:space:]]/}" ]] && continue
|
|
108
|
+
|
|
109
|
+
APPROVED=0
|
|
110
|
+
for pattern in "${ALLOWED[@]}"; do
|
|
111
|
+
if printf '%s' "$SEGMENT" | grep -qE "$pattern"; then
|
|
112
|
+
APPROVED=1
|
|
113
|
+
break
|
|
114
|
+
fi
|
|
115
|
+
done
|
|
116
|
+
|
|
117
|
+
if [[ $APPROVED -eq 0 ]]; then
|
|
118
|
+
echo "BLOCKED: Command not in allowlist" >&2
|
|
119
|
+
echo "Command: $COMMAND" >&2
|
|
120
|
+
echo "Unapproved segment: $SEGMENT" >&2
|
|
121
|
+
echo "To approve, add a pattern to ~/.claude/hooks/allowlist.sh" >&2
|
|
122
|
+
exit 2
|
|
59
123
|
fi
|
|
60
|
-
done
|
|
124
|
+
done <<< "$SEGMENTS"
|
|
61
125
|
|
|
62
|
-
|
|
63
|
-
echo "BLOCKED: Command not in allowlist" >&2
|
|
64
|
-
echo "Command: $COMMAND" >&2
|
|
65
|
-
echo "To approve, add a pattern to ~/.claude/hooks/allowlist.sh" >&2
|
|
66
|
-
exit 2
|
|
126
|
+
exit 0
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# always-allow-pattern-suggester.sh — Suggest wildcard patterns instead of verbatim "Always Allow" rules
|
|
3
|
+
#
|
|
4
|
+
# Solves: Cluster 6 Axis 2 — "Always Allow" saves the verbatim command string
|
|
5
|
+
# (commit message, exact filename, exact URL) instead of a wildcard
|
|
6
|
+
# pattern. Over a working week, settings.local.json accumulates
|
|
7
|
+
# hundreds of one-off dead rules while wildcard patterns the user
|
|
8
|
+
# originally configured sit unused.
|
|
9
|
+
#
|
|
10
|
+
# Issues addressed: #6850 (45 reactions), #11380 (64 reactions, closed
|
|
11
|
+
# without fix), #29187 (regression, no staff response), and the
|
|
12
|
+
# meta-issue #30519 Axis 2 articulation.
|
|
13
|
+
#
|
|
14
|
+
# How it works: PreToolUse hook on Bash. When a command would trigger a
|
|
15
|
+
# permission prompt, the hook computes the canonical wildcard pattern
|
|
16
|
+
# the operator probably wants (e.g., Bash(git commit:*) for
|
|
17
|
+
# `git commit -m "fix typo"`) and emits it as a stderr advisory.
|
|
18
|
+
# Operator pastes the suggestion into settings.local.json once,
|
|
19
|
+
# never sees the prompt for that command family again.
|
|
20
|
+
#
|
|
21
|
+
# This hook NEVER blocks. It always exits 0. It is a wrapper around
|
|
22
|
+
# the "Always Allow" UX, not a replacement.
|
|
23
|
+
#
|
|
24
|
+
# Existing-pattern check: reads ~/.claude/settings.json,
|
|
25
|
+
# ~/.claude/settings.local.json, ./.claude/settings.json, and
|
|
26
|
+
# ./.claude/settings.local.json. If the suggested pattern is already
|
|
27
|
+
# in permissions.allow in any of them, no advisory is emitted
|
|
28
|
+
# (the rule exists; the prompt is happening for a different reason).
|
|
29
|
+
#
|
|
30
|
+
# Environment variables:
|
|
31
|
+
# CC_PATTERN_SUGGESTER_DISABLE=1 — disable the advisory entirely
|
|
32
|
+
# CC_PATTERN_SUGGESTER_VERBOSE=1 — also print the canonical pattern
|
|
33
|
+
# breakdown (binary + first arg)
|
|
34
|
+
#
|
|
35
|
+
# TRIGGER: PreToolUse
|
|
36
|
+
# MATCHER: "Bash"
|
|
37
|
+
|
|
38
|
+
set -uo pipefail
|
|
39
|
+
|
|
40
|
+
INPUT=$(cat)
|
|
41
|
+
|
|
42
|
+
# Fail-open on missing input or jq parse errors.
|
|
43
|
+
COMMAND=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
44
|
+
[ -z "$COMMAND" ] && exit 0
|
|
45
|
+
|
|
46
|
+
# Allow operator to disable without removing the hook.
|
|
47
|
+
[ "${CC_PATTERN_SUGGESTER_DISABLE:-0}" = "1" ] && exit 0
|
|
48
|
+
|
|
49
|
+
# Extract the leading binary + first non-flag argument.
|
|
50
|
+
# Strip leading shell-prefix patterns (one or more env var assignments).
|
|
51
|
+
WORK_CMD="$COMMAND"
|
|
52
|
+
while printf '%s' "$WORK_CMD" | grep -qE '^\s*[A-Z_][A-Z0-9_]*=[^ ]+\s+'; do
|
|
53
|
+
WORK_CMD=$(printf '%s' "$WORK_CMD" | sed -E 's/^\s*[A-Z_][A-Z0-9_]*=[^ ]+\s+//')
|
|
54
|
+
done
|
|
55
|
+
WORK_CMD=$(printf '%s' "$WORK_CMD" | sed -E 's/^\s+//')
|
|
56
|
+
|
|
57
|
+
# Split on shell-compound separators and take the FIRST simple command.
|
|
58
|
+
# (The operator may chain commands; the prompt fires on a specific tool
|
|
59
|
+
# match, but we only need to suggest a pattern for the leading verb.)
|
|
60
|
+
FIRST_SEG=$(printf '%s' "$WORK_CMD" | awk -F'[|&;]' '{print $1}' | sed -E 's/^\s+//; s/\s+$//')
|
|
61
|
+
|
|
62
|
+
# Tokenize the first segment by whitespace.
|
|
63
|
+
read -r BIN ARG1 _ <<< "$FIRST_SEG"
|
|
64
|
+
|
|
65
|
+
# Need at least a binary to suggest anything.
|
|
66
|
+
[ -z "${BIN:-}" ] && exit 0
|
|
67
|
+
|
|
68
|
+
# Strip path prefix from binary (e.g., /usr/bin/git → git).
|
|
69
|
+
BIN_BASE=$(basename "$BIN")
|
|
70
|
+
|
|
71
|
+
# Skip binaries that don't take subcommands or where wildcards
|
|
72
|
+
# wouldn't help (single-purpose tools).
|
|
73
|
+
case "$BIN_BASE" in
|
|
74
|
+
ls|pwd|whoami|hostname|date|uname|true|false|echo|printf|cat|head|tail|wc|sort|uniq|tr|cut|awk|sed|grep|find|xargs|tee|sleep|test|[)
|
|
75
|
+
# These are typically already covered by Bash(<bin>:*) wildcards;
|
|
76
|
+
# if the operator already allowed them, no advisory needed.
|
|
77
|
+
;;
|
|
78
|
+
esac
|
|
79
|
+
|
|
80
|
+
# Determine if the first argument looks like a subcommand vs a flag/value.
|
|
81
|
+
# Subcommand: starts with a letter, no leading dash, no special chars.
|
|
82
|
+
PATTERN_BODY="$BIN_BASE"
|
|
83
|
+
IS_SUBCOMMAND=0
|
|
84
|
+
if [ -n "${ARG1:-}" ] && printf '%s' "$ARG1" | grep -qE '^[a-zA-Z][a-zA-Z0-9_-]*$'; then
|
|
85
|
+
IS_SUBCOMMAND=1
|
|
86
|
+
PATTERN_BODY="$BIN_BASE $ARG1"
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
SUGGESTED="Bash($PATTERN_BODY:*)"
|
|
90
|
+
|
|
91
|
+
# Check if the pattern already exists in any settings file.
|
|
92
|
+
EXISTING=0
|
|
93
|
+
for CFG in \
|
|
94
|
+
"${HOME}/.claude/settings.json" \
|
|
95
|
+
"${HOME}/.claude/settings.local.json" \
|
|
96
|
+
"./.claude/settings.json" \
|
|
97
|
+
"./.claude/settings.local.json"; do
|
|
98
|
+
[ -f "$CFG" ] || continue
|
|
99
|
+
if jq -e --arg p "$SUGGESTED" '.permissions.allow // [] | any(. == $p)' "$CFG" >/dev/null 2>&1; then
|
|
100
|
+
EXISTING=1
|
|
101
|
+
break
|
|
102
|
+
fi
|
|
103
|
+
done
|
|
104
|
+
|
|
105
|
+
# If pattern already exists, emit nothing; the prompt is for a different
|
|
106
|
+
# reason (compound bash, quote-tracking warning, etc.).
|
|
107
|
+
[ "$EXISTING" = "1" ] && exit 0
|
|
108
|
+
|
|
109
|
+
# Build the advisory.
|
|
110
|
+
{
|
|
111
|
+
echo "[always-allow-pattern-suggester] If this command triggers a permission prompt,"
|
|
112
|
+
echo "you can avoid future prompts for the same command family by adding this pattern"
|
|
113
|
+
echo "to permissions.allow in .claude/settings.local.json:"
|
|
114
|
+
echo ""
|
|
115
|
+
echo " \"$SUGGESTED\""
|
|
116
|
+
echo ""
|
|
117
|
+
echo "This is broader than the verbatim string \"Always Allow\" would save."
|
|
118
|
+
if [ "${CC_PATTERN_SUGGESTER_VERBOSE:-0}" = "1" ]; then
|
|
119
|
+
echo ""
|
|
120
|
+
echo "(suggester breakdown: binary=$BIN_BASE; first_arg_is_subcommand=$IS_SUBCOMMAND)"
|
|
121
|
+
fi
|
|
122
|
+
} >&2
|
|
123
|
+
|
|
124
|
+
# Always exit 0 — this hook never blocks.
|
|
125
|
+
exit 0
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# anthropic-base-url-guard.sh — Block execution when ANTHROPIC_BASE_URL is suspicious
|
|
3
|
+
#
|
|
4
|
+
# Solves: #51123 — Setting ANTHROPIC_BASE_URL to a local proxy bypasses
|
|
5
|
+
# allowManagedHooksOnly enforcement. This hook detects when the
|
|
6
|
+
# URL points to non-standard endpoints and warns/blocks.
|
|
7
|
+
#
|
|
8
|
+
# WHY THIS MATTERS:
|
|
9
|
+
# Enterprise admins set allowManagedHooksOnly: true to restrict hooks.
|
|
10
|
+
# A developer can set ANTHROPIC_BASE_URL=http://localhost:4010 and
|
|
11
|
+
# bypass ALL managed hook restrictions. This hook is the defense layer.
|
|
12
|
+
#
|
|
13
|
+
# TRIGGER: PreToolUse MATCHER: ""
|
|
14
|
+
#
|
|
15
|
+
# CONFIGURATION:
|
|
16
|
+
# CC_ALLOWED_BASE_URLS — comma-separated allowlist (default: api.anthropic.com)
|
|
17
|
+
# CC_BASE_URL_ACTION — "warn" or "block" (default: warn)
|
|
18
|
+
|
|
19
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
20
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
21
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
22
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
23
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
24
|
+
_nojq_warned="/tmp/cc-nojq-warned-anthropic-base-url-guard-$PPID"
|
|
25
|
+
[ -f "$_nojq_warned" ] || {
|
|
26
|
+
echo "WARNING [anthropic-base-url-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
27
|
+
: > "$_nojq_warned"
|
|
28
|
+
}
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
INPUT=$(cat)
|
|
32
|
+
|
|
33
|
+
# Default allowlist: only official Anthropic API
|
|
34
|
+
ALLOWED="${CC_ALLOWED_BASE_URLS:-https://api.anthropic.com}"
|
|
35
|
+
ACTION="${CC_BASE_URL_ACTION:-warn}"
|
|
36
|
+
|
|
37
|
+
# Get current ANTHROPIC_BASE_URL
|
|
38
|
+
CURRENT_URL="${ANTHROPIC_BASE_URL:-https://api.anthropic.com}"
|
|
39
|
+
|
|
40
|
+
# Check if URL is in allowlist
|
|
41
|
+
is_allowed() {
|
|
42
|
+
local url="$1"
|
|
43
|
+
IFS=',' read -ra URLS <<< "$ALLOWED"
|
|
44
|
+
for allowed_url in "${URLS[@]}"; do
|
|
45
|
+
# Trim whitespace
|
|
46
|
+
allowed_url=$(echo "$allowed_url" | xargs)
|
|
47
|
+
if [[ "$url" == "$allowed_url"* ]]; then
|
|
48
|
+
return 0
|
|
49
|
+
fi
|
|
50
|
+
done
|
|
51
|
+
return 1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# If URL is default/allowed, pass through silently
|
|
55
|
+
if is_allowed "$CURRENT_URL"; then
|
|
56
|
+
exit 0
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# URL is non-standard — take action
|
|
60
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // "unknown"' 2>/dev/null)
|
|
61
|
+
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
|
|
62
|
+
|
|
63
|
+
# Log the event
|
|
64
|
+
LOG_FILE="${CC_BASE_URL_LOG:-/tmp/cc-base-url-violations.log}"
|
|
65
|
+
echo "[$TIMESTAMP] ANTHROPIC_BASE_URL=$CURRENT_URL tool=$TOOL action=$ACTION" >> "$LOG_FILE"
|
|
66
|
+
|
|
67
|
+
if [[ "$ACTION" == "block" ]]; then
|
|
68
|
+
echo '{"decision":"block","reason":"ANTHROPIC_BASE_URL points to non-standard endpoint ('"$CURRENT_URL"'). This may bypass managed hook restrictions. Set CC_ALLOWED_BASE_URLS to allowlist this endpoint."}' >&2
|
|
69
|
+
exit 2
|
|
70
|
+
else
|
|
71
|
+
# Warn but allow
|
|
72
|
+
echo "⚠️ WARNING: ANTHROPIC_BASE_URL=$CURRENT_URL (non-standard)" >&2
|
|
73
|
+
echo " This may bypass allowManagedHooksOnly restrictions (#51123)" >&2
|
|
74
|
+
echo " Set CC_BASE_URL_ACTION=block to enforce, or CC_ALLOWED_BASE_URLS to allowlist" >&2
|
|
75
|
+
exit 0
|
|
76
|
+
fi
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# api-busyloop-guard.sh — Stop a single Bash command that busy-polls a
|
|
3
|
+
# rate-limited API in an unbounded loop with no delay
|
|
4
|
+
#
|
|
5
|
+
# Solves: When asked to "wait for the CI run", the model reaches for a generic
|
|
6
|
+
# shell poll instead of the purpose-built command, e.g.
|
|
7
|
+
#
|
|
8
|
+
# until gh run view <run-id> 2>&1 | grep -q "completed"; do true; done
|
|
9
|
+
#
|
|
10
|
+
# `do true; done` spins as fast as the shell allows, calling `gh` (the GitHub
|
|
11
|
+
# REST API) hundreds of times a minute. In #65985 this drained the 5,000 req/h
|
|
12
|
+
# limit to 0/5000 within minutes and blocked every gh command for the rest of
|
|
13
|
+
# the hour. Worse, the loops were started in the background and kept running
|
|
14
|
+
# silently after the session moved on — nothing cleaned them up. The same shape
|
|
15
|
+
# hammers any rate-limited CLI (curl/wget/aws/gcloud/kubectl) the same way.
|
|
16
|
+
#
|
|
17
|
+
# Existing hooks miss this exact shape:
|
|
18
|
+
# - loop-detector.sh keys on the *same command* repeating across separate tool
|
|
19
|
+
# calls. Here the loop lives *inside one* Bash command, so the hook sees it
|
|
20
|
+
# only once and never trips.
|
|
21
|
+
# - api-rate-limit-guard.sh / api-rate-limit-tracker.sh only match curl/wget/
|
|
22
|
+
# http and likewise count *separate* tool calls — an internal shell loop is
|
|
23
|
+
# a single call to them, and `gh` is not even in their match set.
|
|
24
|
+
# - unbounded-output-guard.sh stops infinite *output* generators (yes, cat
|
|
25
|
+
# /dev/zero), not API busy-loops.
|
|
26
|
+
# - webfetch-runaway-guard.sh counts the WebFetch *tool*, not Bash loops.
|
|
27
|
+
#
|
|
28
|
+
# Why two precision levels (so legitimate loops are not blocked):
|
|
29
|
+
# block (default) fires ONLY on the unambiguous busy-wait signature — an
|
|
30
|
+
# unbounded while/until/for((;;)) loop whose body is a no-op (true / : /
|
|
31
|
+
# continue / sleep 0) that calls a rate-limited CLI with no real sleep.
|
|
32
|
+
# A no-op-body poll has essentially no legitimate use: a real wait always
|
|
33
|
+
# sleeps. This is exactly the #65985 incident.
|
|
34
|
+
# warn (stderr, non-blocking) fires on the broader shape — any unbounded
|
|
35
|
+
# while/until/for((;;)) loop calling such a CLI with no sleep — because a
|
|
36
|
+
# counter-bounded loop can look the same to a pattern match and we would
|
|
37
|
+
# rather nudge than wrongly block it.
|
|
38
|
+
# Bounded loops are left completely alone: `for x in <list>` and `while read`
|
|
39
|
+
# (stream/file consumption) terminate on their own, and any loop that
|
|
40
|
+
# already sleeps or already uses `gh run watch` is the correct form.
|
|
41
|
+
#
|
|
42
|
+
# The right fix, surfaced in the message:
|
|
43
|
+
# - For GitHub Actions: `gh run watch <run-id> --exit-status` streams status,
|
|
44
|
+
# exits on completion/failure/cancel, and makes efficient API use — no loop.
|
|
45
|
+
# - For any poll: put a `sleep N` in the body plus a terminal-state or
|
|
46
|
+
# max-iteration exit so it cannot spin forever or linger in the background.
|
|
47
|
+
#
|
|
48
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
49
|
+
# Config (all optional):
|
|
50
|
+
# CC_API_BUSYLOOP_GUARD=block block | warn | off (default: block)
|
|
51
|
+
# Related: https://github.com/anthropics/claude-code/issues/65985
|
|
52
|
+
|
|
53
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
54
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
55
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
56
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
57
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
58
|
+
_nojq_warned="/tmp/cc-nojq-warned-api-busyloop-guard-$PPID"
|
|
59
|
+
[ -f "$_nojq_warned" ] || {
|
|
60
|
+
echo "WARNING [api-busyloop-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
61
|
+
: > "$_nojq_warned"
|
|
62
|
+
}
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
INPUT=$(cat)
|
|
66
|
+
|
|
67
|
+
MODE="${CC_API_BUSYLOOP_GUARD:-block}"
|
|
68
|
+
[ "$MODE" = "off" ] && exit 0
|
|
69
|
+
|
|
70
|
+
COMMAND=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
71
|
+
[ -z "$COMMAND" ] && exit 0
|
|
72
|
+
|
|
73
|
+
# 1. An unbounded loop construct: while / until (condition loops = the classic
|
|
74
|
+
# poll) or a C-style for ((...)). `for x in <list>` is bounded, so excluded.
|
|
75
|
+
echo "$COMMAND" | grep -qE '(^|[^[:alnum:]_])(while|until)([^[:alnum:]_]|$)' \
|
|
76
|
+
|| echo "$COMMAND" | grep -qE 'for[[:space:]]*\(\(' \
|
|
77
|
+
|| exit 0
|
|
78
|
+
|
|
79
|
+
# 2. Bounded stream/file consumption (`while read ... done < file`) terminates
|
|
80
|
+
# at EOF — not a poll. Leave it alone.
|
|
81
|
+
echo "$COMMAND" | grep -qE '(while|until)[^;]*\bread\b' && exit 0
|
|
82
|
+
|
|
83
|
+
# 3. A rate-limited network CLI somewhere in the command.
|
|
84
|
+
echo "$COMMAND" | grep -qE '(^|[^[:alnum:]_./-])(gh|curl|wget|aws|gcloud|kubectl)[[:space:]]' || exit 0
|
|
85
|
+
|
|
86
|
+
# 4. Already throttled? A positive sleep, or the purpose-built gh run watch,
|
|
87
|
+
# means this is the correct form — allow it.
|
|
88
|
+
echo "$COMMAND" | grep -qE '\bsleep[[:space:]]+([1-9][0-9]*|[0-9]*\.[0-9]*[1-9])' && exit 0
|
|
89
|
+
echo "$COMMAND" | grep -qE '\bgh[[:space:]]+run[[:space:]]+watch\b' && exit 0
|
|
90
|
+
|
|
91
|
+
# Tailor the fix hint to the CLI in play.
|
|
92
|
+
if echo "$COMMAND" | grep -qE '(^|[^[:alnum:]_./-])gh[[:space:]]'; then
|
|
93
|
+
FIX="Use 'gh run watch <run-id> --exit-status': it streams status, exits automatically on completion/failure/cancel, and makes one efficient call instead of a busy poll. GitHub's REST limit is 5,000 req/h and a no-delay loop can drain it to 0 in minutes (#65985)."
|
|
94
|
+
else
|
|
95
|
+
FIX="Add a 'sleep N' inside the loop body and a terminal-state or max-iteration exit, or use the tool's own blocking/wait subcommand. A no-delay loop hammers the API's rate limit and, if backgrounded, keeps running unnoticed (#65985)."
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# The unambiguous busy-wait signatures, either of which has essentially no
|
|
99
|
+
# legitimate no-sleep form:
|
|
100
|
+
# (a) a no-op / empty loop body (true / : / continue / sleep 0), or
|
|
101
|
+
# (b) an explicitly infinite loop (while true | while : | until false |
|
|
102
|
+
# for ((;;))), which never terminates on its own.
|
|
103
|
+
# A counter- or condition-bounded loop (e.g. `while [ $n -lt 5 ]`) matches
|
|
104
|
+
# neither and only warns.
|
|
105
|
+
BUSY=0
|
|
106
|
+
if echo "$COMMAND" | grep -qE 'do[[:space:]]+(true|:|continue|sleep[[:space:]]+0)[[:space:]]*;?[[:space:]]*done' \
|
|
107
|
+
|| echo "$COMMAND" | grep -qE 'do[[:space:]]*;[[:space:]]*done' \
|
|
108
|
+
|| echo "$COMMAND" | grep -qE 'while[[:space:]]+(true|:)([^[:alnum:]_]|$)' \
|
|
109
|
+
|| echo "$COMMAND" | grep -qE 'until[[:space:]]+false([^[:alnum:]_]|$)' \
|
|
110
|
+
|| echo "$COMMAND" | grep -qE 'for[[:space:]]*\(\([[:space:]]*;[[:space:]]*;[[:space:]]*\)\)'; then
|
|
111
|
+
BUSY=1
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
MSG="This command busy-polls a rate-limited API in an unbounded loop with no delay. $FIX Set CC_API_BUSYLOOP_GUARD=warn to only warn, or =off to disable."
|
|
115
|
+
|
|
116
|
+
if [ "$MODE" = "block" ] && [ "$BUSY" -eq 1 ]; then
|
|
117
|
+
echo "api-busyloop-guard: $MSG" >&2
|
|
118
|
+
exit 2
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
# Broader shape, or warn mode: nudge without blocking.
|
|
122
|
+
echo "api-busyloop-guard: $MSG" >&2
|
|
123
|
+
exit 0
|
|
@@ -10,6 +10,18 @@
|
|
|
10
10
|
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
11
11
|
# ================================================================
|
|
12
12
|
|
|
13
|
+
# Without jq, the parse below silently yields empty and this hook stops
|
|
14
|
+
# guarding - with no error anywhere. Say so. We deliberately do not exit
|
|
15
|
+
# here: blocking would halt every tool call, and exiting 0 would change
|
|
16
|
+
# the behaviour of guards that do not depend on the parsed value.
|
|
17
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
18
|
+
_nojq_warned="/tmp/cc-nojq-warned-api-endpoint-guard-$PPID"
|
|
19
|
+
[ -f "$_nojq_warned" ] || {
|
|
20
|
+
echo "WARNING [api-endpoint-guard]: jq not found - this hook cannot inspect tool calls and is NOT protecting you. Install jq." >&2
|
|
21
|
+
: > "$_nojq_warned"
|
|
22
|
+
}
|
|
23
|
+
fi
|
|
24
|
+
|
|
13
25
|
COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
14
26
|
[ -z "$COMMAND" ] && exit 0
|
|
15
27
|
|