cc-safe-setup 29.6.32 → 29.6.36
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/COOKBOOK.md +70 -0
- package/README.md +6 -2
- package/examples/absolute-rule-enforcer.sh +42 -0
- package/examples/allow-claude-settings.sh +2 -0
- package/examples/allow-git-hooks-dir.sh +2 -0
- package/examples/allow-protected-dirs.sh +2 -0
- package/examples/allowlist.sh +2 -0
- package/examples/ansible-vault-guard.sh +2 -0
- package/examples/auto-approve-build.sh +2 -0
- package/examples/auto-approve-compound-git.sh +2 -0
- package/examples/auto-approve-docker.sh +2 -0
- package/examples/auto-approve-git-read.sh +2 -0
- package/examples/auto-approve-python.sh +2 -0
- package/examples/auto-approve-readonly-tools.sh +2 -0
- package/examples/auto-approve-ssh.sh +2 -0
- package/examples/auto-approve-test.sh +2 -0
- package/examples/auto-checkpoint.sh +2 -0
- package/examples/auto-git-checkpoint.sh +2 -0
- package/examples/auto-mode-safe-commands.sh +2 -0
- package/examples/auto-snapshot.sh +2 -0
- package/examples/backup-before-refactor.sh +2 -0
- package/examples/banned-command-guard.sh +3 -3
- package/examples/bash-domain-allowlist.sh +72 -0
- package/examples/bash-safety-auto-deny.sh +56 -0
- package/examples/bash-secret-output-detector.sh +68 -0
- package/examples/bash-timeout-guard.sh +2 -0
- package/examples/bashrc-safety-check.sh +59 -0
- package/examples/bg-task-cooldown-guard.sh +46 -0
- package/examples/block-database-wipe.sh +2 -0
- package/examples/branch-name-check.sh +2 -0
- package/examples/branch-naming-convention.sh +2 -0
- package/examples/cargo-publish-guard.sh +2 -0
- package/examples/check-abort-controller.sh +2 -0
- package/examples/check-accessibility.sh +2 -0
- package/examples/check-aria-labels.sh +2 -0
- package/examples/check-async-await-consistency.sh +2 -0
- package/examples/check-before-act-enforcer.sh +47 -0
- package/examples/check-charset-meta.sh +2 -0
- package/examples/check-cleanup-effect.sh +2 -0
- package/examples/check-content-type.sh +2 -0
- package/examples/check-controlled-input.sh +2 -0
- package/examples/check-cookie-flags.sh +2 -0
- package/examples/check-cors-config.sh +2 -0
- package/examples/check-csp-headers.sh +2 -0
- package/examples/check-csrf-protection.sh +2 -0
- package/examples/check-debounce.sh +2 -0
- package/examples/check-dependency-age.sh +2 -0
- package/examples/check-dependency-license.sh +2 -0
- package/examples/check-dockerfile-best-practice.sh +2 -0
- package/examples/check-error-boundaries.sh +2 -0
- package/examples/check-error-class.sh +2 -0
- package/examples/check-error-handling.sh +2 -0
- package/examples/check-error-logging.sh +2 -0
- package/examples/check-error-message.sh +2 -0
- package/examples/check-error-page.sh +2 -0
- package/examples/check-error-stack.sh +2 -0
- package/examples/check-favicon.sh +2 -0
- package/examples/check-form-validation.sh +2 -0
- package/examples/check-git-hooks-compat.sh +2 -0
- package/examples/check-https-redirect.sh +2 -0
- package/examples/check-image-optimization.sh +2 -0
- package/examples/check-input-validation.sh +2 -0
- package/examples/check-key-prop.sh +2 -0
- package/examples/check-lang-attribute.sh +2 -0
- package/examples/check-lazy-loading.sh +2 -0
- package/examples/check-loading-state.sh +2 -0
- package/examples/check-memo-deps.sh +2 -0
- package/examples/check-meta-description.sh +2 -0
- package/examples/check-npm-scripts-exist.sh +2 -0
- package/examples/check-null-check.sh +2 -0
- package/examples/check-package-size.sh +2 -0
- package/examples/check-pagination.sh +2 -0
- package/examples/check-port-availability.sh +2 -0
- package/examples/check-promise-all.sh +2 -0
- package/examples/check-prop-types.sh +2 -0
- package/examples/check-rate-limiting.sh +2 -0
- package/examples/check-responsive-design.sh +2 -0
- package/examples/check-retry-logic.sh +2 -0
- package/examples/check-return-types.sh +2 -0
- package/examples/check-semantic-html.sh +2 -0
- package/examples/check-semantic-versioning.sh +2 -0
- package/examples/check-suspense-fallback.sh +2 -0
- package/examples/check-test-naming.sh +2 -0
- package/examples/check-timeout-cleanup.sh +2 -0
- package/examples/check-tls-version.sh +2 -0
- package/examples/check-type-coercion.sh +2 -0
- package/examples/check-unsubscribe.sh +2 -0
- package/examples/check-viewport-meta.sh +2 -0
- package/examples/check-worker-terminate.sh +2 -0
- package/examples/checkpoint-tamper-guard.sh +2 -0
- package/examples/chmod-guard.sh +2 -0
- package/examples/chown-guard.sh +2 -0
- package/examples/ci-workflow-guard.sh +59 -0
- package/examples/classifier-fallback-allow.sh +2 -0
- package/examples/claude-cache-gc.sh +15 -0
- package/examples/claudeignore-enforce-guard.sh +60 -0
- package/examples/claudemd-enforcer.sh +2 -0
- package/examples/claudemd-violation-detector.sh +36 -0
- package/examples/clear-command-confirm-guard.sh +21 -0
- package/examples/commit-message-check.sh +2 -0
- package/examples/compact-blocker.sh +25 -0
- package/examples/composer-guard.sh +2 -0
- package/examples/compound-command-allow.sh +2 -0
- package/examples/consecutive-failure-circuit-breaker.sh +49 -0
- package/examples/console-log-count.sh +2 -0
- package/examples/context-compact-advisor.sh +16 -0
- package/examples/core-file-protect-guard.sh +91 -0
- package/examples/cors-star-warn.sh +2 -0
- package/examples/credential-exfil-guard.sh +2 -0
- package/examples/credential-file-cat-guard.sh +2 -0
- package/examples/cron-modification-guard.sh +40 -0
- package/examples/cwd-drift-detector.sh +47 -0
- package/examples/cwd-project-boundary-guard.sh +50 -0
- package/examples/denied-action-retry-guard.sh +41 -0
- package/examples/dependency-install-guard.sh +2 -0
- package/examples/deploy-guard.sh +2 -0
- package/examples/deploy-path-verify-guard.sh +62 -0
- package/examples/deployment-verify-guard.sh +81 -0
- package/examples/django-migrate-guard.sh +2 -0
- package/examples/docker-volume-guard.sh +2 -0
- package/examples/dockerfile-latest-guard.sh +2 -0
- package/examples/dotenv-commit-guard.sh +44 -0
- package/examples/dotenv-example-sync.sh +55 -0
- package/examples/dotnet-build-on-edit.sh +2 -0
- package/examples/drizzle-migrate-guard.sh +2 -0
- package/examples/edit-counter-test-gate.sh +44 -0
- package/examples/edit-error-counter.sh +2 -0
- package/examples/edit-guard.sh +2 -0
- package/examples/edit-old-string-validator.sh +37 -0
- package/examples/edit-retry-loop-guard.sh +2 -0
- package/examples/edit-verify.sh +2 -0
- package/examples/encoding-preserve-guard.sh +34 -0
- package/examples/enforce-tests.sh +2 -0
- package/examples/env-inherit-guard.sh +2 -0
- package/examples/env-inline-secret-guard.sh +36 -0
- package/examples/env-prod-guard.sh +2 -0
- package/examples/env-required-check.sh +2 -0
- package/examples/env-var-check.sh +2 -0
- package/examples/expo-eject-guard.sh +2 -0
- package/examples/export-overwrite-guard.sh +29 -0
- package/examples/file-change-tracker.sh +2 -0
- package/examples/file-change-undo-tracker.sh +46 -0
- package/examples/file-recycle-bin.sh +48 -0
- package/examples/file-size-limit.sh +2 -0
- package/examples/five-hundred-milestone.sh +2 -0
- package/examples/flask-debug-guard.sh +2 -0
- package/examples/gem-push-guard.sh +2 -0
- package/examples/git-checkout-safety-guard.sh +2 -0
- package/examples/git-config-guard.sh +2 -0
- package/examples/git-crypt-worktree-guard.sh +36 -0
- package/examples/git-hook-bypass-guard.sh +2 -0
- package/examples/git-merge-conflict-prevent.sh +2 -0
- package/examples/git-message-length.sh +2 -0
- package/examples/git-operations-require-approval.sh +99 -0
- package/examples/git-show-flag-sanitizer.sh +41 -0
- package/examples/git-stash-before-danger.sh +2 -0
- package/examples/git-submodule-guard.sh +2 -0
- package/examples/git-tag-guard.sh +2 -0
- package/examples/github-actions-secret-guard.sh +59 -0
- package/examples/gitignore-check.sh +2 -0
- package/examples/gitops-drift-guard.sh +53 -0
- package/examples/go-mod-tidy-warn.sh +2 -0
- package/examples/hallucination-url-check.sh +2 -0
- package/examples/hardcoded-ip-guard.sh +2 -0
- package/examples/headless-empty-result-guard.sh +46 -0
- package/examples/headless-stop-guard.sh +43 -0
- package/examples/helm-install-guard.sh +2 -0
- package/examples/issue-draft-redact-guard.sh +45 -0
- package/examples/java-compile-on-edit.sh +2 -0
- package/examples/k8s-production-guard.sh +77 -0
- package/examples/laravel-artisan-guard.sh +2 -0
- package/examples/large-file-guard.sh +2 -0
- package/examples/line-ending-guard.sh +30 -0
- package/examples/log-level-guard.sh +2 -0
- package/examples/magic-number-warn.sh +2 -0
- package/examples/max-edit-size-guard.sh +2 -0
- package/examples/max-file-count-guard.sh +2 -0
- package/examples/max-file-delete-count.sh +2 -0
- package/examples/max-function-length.sh +2 -0
- package/examples/max-import-count.sh +2 -0
- package/examples/max-subagent-count.sh +2 -0
- package/examples/mcp-orphan-process-guard.sh +39 -0
- package/examples/mcp-server-allowlist.sh +45 -0
- package/examples/mcp-tool-audit-log.sh +41 -0
- package/examples/mcp-tool-guard.sh +2 -0
- package/examples/migration-verify-guard.sh +44 -0
- package/examples/monorepo-scope-guard.sh +2 -0
- package/examples/network-exfil-guard.sh +61 -0
- package/examples/network-guard.sh +2 -0
- package/examples/nextjs-env-guard.sh +2 -0
- package/examples/no-absolute-import.sh +2 -0
- package/examples/no-alert-confirm-prompt.sh +2 -0
- package/examples/no-any-type.sh +2 -0
- package/examples/no-any-typescript.sh +2 -0
- package/examples/no-assignment-in-condition.sh +2 -0
- package/examples/no-callback-hell.sh +2 -0
- package/examples/no-catch-all-route.sh +2 -0
- package/examples/no-circular-dependency.sh +2 -0
- package/examples/no-class-in-functional.sh +2 -0
- package/examples/no-cleartext-storage.sh +2 -0
- package/examples/no-commented-code.sh +2 -0
- package/examples/no-commit-fixup.sh +2 -0
- package/examples/no-console-assert.sh +2 -0
- package/examples/no-console-error-swallow.sh +2 -0
- package/examples/no-console-in-prod.sh +2 -0
- package/examples/no-console-log.sh +2 -0
- package/examples/no-console-time.sh +2 -0
- package/examples/no-cors-wildcard.sh +2 -0
- package/examples/no-curl-upload.sh +2 -0
- package/examples/no-dangerouslySetInnerHTML.sh +2 -0
- package/examples/no-dangling-await.sh +2 -0
- package/examples/no-debug-in-commit.sh +2 -0
- package/examples/no-deep-nesting.sh +2 -0
- package/examples/no-deep-relative-import.sh +2 -0
- package/examples/no-default-credentials.sh +2 -0
- package/examples/no-deprecated-api.sh +2 -0
- package/examples/no-direct-dom-manipulation.sh +2 -0
- package/examples/no-disabled-test.sh +2 -0
- package/examples/no-document-cookie.sh +2 -0
- package/examples/no-document-write.sh +2 -0
- package/examples/no-empty-function.sh +2 -0
- package/examples/no-eval-in-template.sh +2 -0
- package/examples/no-eval-template.sh +2 -0
- package/examples/no-eval.sh +2 -0
- package/examples/no-exec-user-input.sh +2 -0
- package/examples/no-expose-internal-ids.sh +2 -0
- package/examples/no-floating-promises.sh +2 -0
- package/examples/no-force-install.sh +2 -0
- package/examples/no-git-rebase-public.sh +2 -0
- package/examples/no-global-state.sh +2 -0
- package/examples/no-hardcoded-port.sh +2 -0
- package/examples/no-hardcoded-url.sh +2 -0
- package/examples/no-helmet-missing.sh +2 -0
- package/examples/no-http-url.sh +2 -0
- package/examples/no-http-without-https.sh +2 -0
- package/examples/no-index-as-key.sh +2 -0
- package/examples/no-infinite-scroll-mem.sh +2 -0
- package/examples/no-inline-event-handler.sh +2 -0
- package/examples/no-inline-handler.sh +2 -0
- package/examples/no-inline-style.sh +2 -0
- package/examples/no-inline-styles.sh +2 -0
- package/examples/no-innerhtml.sh +2 -0
- package/examples/no-install-global.sh +2 -0
- package/examples/no-jwt-in-url.sh +2 -0
- package/examples/no-large-commit.sh +2 -0
- package/examples/no-localhost-expose.sh +2 -0
- package/examples/no-long-switch.sh +2 -0
- package/examples/no-magic-number.sh +2 -0
- package/examples/no-md5-sha1.sh +2 -0
- package/examples/no-memory-leak-interval.sh +2 -0
- package/examples/no-mixed-line-endings.sh +2 -0
- package/examples/no-mutation-in-reducer.sh +2 -0
- package/examples/no-mutation-observer-leak.sh +2 -0
- package/examples/no-nested-subscribe.sh +2 -0
- package/examples/no-nested-ternary.sh +2 -0
- package/examples/no-network-exfil.sh +2 -0
- package/examples/no-new-array-fill.sh +2 -0
- package/examples/no-object-freeze-mutation.sh +2 -0
- package/examples/no-open-redirect.sh +2 -0
- package/examples/no-output-truncation.sh +44 -0
- package/examples/no-package-downgrade.sh +2 -0
- package/examples/no-package-lock-edit.sh +2 -0
- package/examples/no-path-join-user-input.sh +2 -0
- package/examples/no-port-bind.sh +2 -0
- package/examples/no-process-exit.sh +2 -0
- package/examples/no-prototype-pollution.sh +2 -0
- package/examples/no-push-without-ci.sh +2 -0
- package/examples/no-raw-ref.sh +2 -0
- package/examples/no-redundant-fragment.sh +2 -0
- package/examples/no-render-in-loop.sh +2 -0
- package/examples/no-root-user-docker.sh +2 -0
- package/examples/no-root-write.sh +2 -0
- package/examples/no-secrets-in-args.sh +2 -0
- package/examples/no-secrets-in-logs.sh +2 -0
- package/examples/no-sensitive-log.sh +2 -0
- package/examples/no-side-effects-in-render.sh +2 -0
- package/examples/no-sleep-in-hooks.sh +2 -0
- package/examples/no-star-import-python.sh +2 -0
- package/examples/no-string-concat-sql.sh +2 -0
- package/examples/no-sudo-guard.sh +2 -0
- package/examples/no-sync-external-call.sh +2 -0
- package/examples/no-sync-fs.sh +2 -0
- package/examples/no-table-layout.sh +2 -0
- package/examples/no-throw-string.sh +2 -0
- package/examples/no-todo-in-merge.sh +2 -0
- package/examples/no-todo-in-production.sh +2 -0
- package/examples/no-todo-without-issue.sh +2 -0
- package/examples/no-triple-slash-ref.sh +2 -0
- package/examples/no-unreachable-code.sh +2 -0
- package/examples/no-unused-import.sh +2 -0
- package/examples/no-unused-state.sh +2 -0
- package/examples/no-var-keyword.sh +2 -0
- package/examples/no-wildcard-cors.sh +2 -0
- package/examples/no-wildcard-import.sh +2 -0
- package/examples/no-window-location.sh +2 -0
- package/examples/no-with-statement.sh +2 -0
- package/examples/no-write-outside-src.sh +2 -0
- package/examples/no-xml-external-entity.sh +2 -0
- package/examples/notify-waiting.sh +2 -0
- package/examples/npm-audit-warn.sh +2 -0
- package/examples/npm-publish-guard.sh +2 -0
- package/examples/npm-script-injection.sh +2 -0
- package/examples/npm-supply-chain-guard.sh +92 -0
- package/examples/nuxt-config-guard.sh +2 -0
- package/examples/output-secret-mask.sh +2 -0
- package/examples/package-json-guard.sh +2 -0
- package/examples/parallel-session-guard.sh +2 -0
- package/examples/path-traversal-guard.sh +2 -0
- package/examples/permission-audit-log.sh +2 -0
- package/examples/permission-entry-validator.sh +48 -0
- package/examples/permission-pattern-auto-allow.sh +50 -0
- package/examples/php-lint-on-edit.sh +2 -0
- package/examples/pip-publish-guard.sh +2 -0
- package/examples/plain-language-danger-warn.sh +37 -0
- package/examples/plan-mode-enforcer.sh +2 -0
- package/examples/plugin-process-cleanup.sh +50 -0
- package/examples/polyglot-rm-guard.sh +59 -0
- package/examples/pr-description-check.sh +2 -0
- package/examples/pre-compact-knowledge-save.sh +53 -0
- package/examples/pre-compact-transcript-export.sh +85 -0
- package/examples/prefer-builtin-tools.sh +2 -0
- package/examples/prefer-const.sh +2 -0
- package/examples/prefer-dedicated-tools.sh +55 -0
- package/examples/prefer-optional-chaining.sh +2 -0
- package/examples/prisma-migrate-guard.sh +2 -0
- package/examples/prompt-injection-detector.sh +2 -0
- package/examples/prompt-length-guard.sh +2 -0
- package/examples/protect-dotfiles.sh +2 -0
- package/examples/public-repo-push-guard.sh +58 -0
- package/examples/push-requires-test-pass-record.sh +2 -0
- package/examples/push-requires-test-pass.sh +2 -0
- package/examples/rails-migration-guard.sh +2 -0
- package/examples/rate-limit-guard.sh +2 -0
- package/examples/read-all-files-enforcer.sh +51 -0
- package/examples/read-audit-log.sh +34 -0
- package/examples/readme-exists-check.sh +2 -0
- package/examples/redis-flushall-guard.sh +2 -0
- package/examples/rm-safety-net.sh +2 -0
- package/examples/role-tool-guard.sh +69 -0
- package/examples/ruby-lint-on-edit.sh +2 -0
- package/examples/schema-migration-guard.sh +57 -0
- package/examples/scope-guard.sh +2 -0
- package/examples/secret-file-read-guard.sh +74 -0
- package/examples/self-modify-bypass-guard.sh +54 -0
- package/examples/sensitive-log-guard.sh +2 -0
- package/examples/session-checkpoint.sh +2 -0
- package/examples/session-duration-guard.sh +51 -0
- package/examples/session-end-logger.sh +57 -0
- package/examples/session-error-rate-monitor.sh +65 -0
- package/examples/session-health-monitor.sh +61 -0
- package/examples/session-memory-watchdog.sh +17 -0
- package/examples/session-permission-reset-guard.sh +39 -0
- package/examples/session-resume-env-fix.sh +49 -0
- package/examples/session-state-saver.sh +2 -0
- package/examples/session-summary-stop.sh +2 -0
- package/examples/session-summary.sh +2 -0
- package/examples/session-token-counter.sh +2 -0
- package/examples/settings-auto-backup.sh +53 -0
- package/examples/settings-mutation-detector.sh +45 -0
- package/examples/shell-wrapper-guard.sh +2 -0
- package/examples/skill-gate.sh +2 -0
- package/examples/skill-injection-detector.sh +41 -0
- package/examples/spec-file-scope-guard.sh +69 -0
- package/examples/spring-profile-guard.sh +2 -0
- package/examples/sql-injection-detect.sh +2 -0
- package/examples/subagent-budget-guard.sh +2 -0
- package/examples/subagent-claudemd-inject.sh +45 -0
- package/examples/subagent-context-size-guard.sh +26 -0
- package/examples/subagent-tool-call-limiter.sh +48 -0
- package/examples/svelte-lint-on-edit.sh +2 -0
- package/examples/swift-build-on-edit.sh +2 -0
- package/examples/symlink-protect.sh +12 -0
- package/examples/system-message-workaround.sh +44 -0
- package/examples/system-package-guard.sh +2 -0
- package/examples/temp-file-cleanup-stop.sh +28 -0
- package/examples/temp-file-cleanup.sh +2 -0
- package/examples/terminal-state-restore.sh +23 -0
- package/examples/test-after-edit.sh +2 -0
- package/examples/test-before-commit.sh +13 -14
- package/examples/test-before-push.sh +2 -0
- package/examples/test-exit-code-verify.sh +2 -0
- package/examples/timeout-guard.sh +2 -0
- package/examples/timezone-guard.sh +2 -0
- package/examples/tmp-output-size-guard.sh +46 -0
- package/examples/todo-check.sh +2 -0
- package/examples/todo-deadline-warn.sh +48 -0
- package/examples/token-budget-per-task.sh +55 -0
- package/examples/token-spike-alert.sh +51 -0
- package/examples/token-usage-tracker.sh +14 -0
- package/examples/turbo-cache-guard.sh +2 -0
- package/examples/uncommitted-changes-stop.sh +2 -0
- package/examples/uncommitted-work-shield.sh +37 -0
- package/examples/usage-warn.sh +2 -0
- package/examples/verify-before-commit.sh +2 -0
- package/examples/virtual-cwd-helper.sh +40 -0
- package/examples/vue-lint-on-edit.sh +2 -0
- package/examples/webfetch-domain-allow.sh +96 -0
- package/examples/worktree-delete-guard.sh +43 -0
- package/examples/worktree-memory-guard.sh +47 -0
- package/examples/worktree-path-validator.sh +42 -0
- package/examples/worktree-project-unify.sh +19 -0
- package/examples/worktree-unmerged-guard.sh +2 -0
- package/examples/write-overwrite-confirm.sh +40 -0
- package/examples/write-secret-guard.sh +2 -0
- package/examples/write-shrink-guard.sh +46 -0
- package/examples/write-test-ratio.sh +2 -0
- package/index.mjs +631 -138
- package/package.json +2 -2
- package/scripts/generate-categories.mjs +206 -0
- package/scripts.json +4 -1
- package/test.sh.new_tests +0 -0
- package/test.sh.patch +0 -0
- package/tests/test-core-file-protect-guard.sh +73 -0
- package/tests/test-deployment-verify-guard.sh +74 -0
- package/tests/test-git-operations-require-approval.sh +65 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
INPUT=$(cat)
|
|
2
|
+
TRANSCRIPT=$(ls -t ~/.claude/projects/*/sessions/*/transcript.jsonl 2>/dev/null | head -1)
|
|
3
|
+
[ -f "$TRANSCRIPT" ] || exit 0
|
|
4
|
+
USAGE=$(tail -20 "$TRANSCRIPT" | grep -o '"usage":{[^}]*}' | tail -1)
|
|
5
|
+
if [ -n "$USAGE" ]; then
|
|
6
|
+
IN=$(echo "$USAGE" | grep -o '"input_tokens":[0-9]*' | grep -o '[0-9]*')
|
|
7
|
+
OUT=$(echo "$USAGE" | grep -o '"output_tokens":[0-9]*' | grep -o '[0-9]*')
|
|
8
|
+
TOTAL=$((${IN:-0} + ${OUT:-0}))
|
|
9
|
+
echo "$(date -Iseconds) in=${IN:-0} out=${OUT:-0} total=$TOTAL" >> ~/.claude/token-usage.log
|
|
10
|
+
BUDGET="${CC_TOKEN_BUDGET:-500000}"
|
|
11
|
+
SUM=$(awk -F'total=' '{sum+=$2}END{print sum+0}' ~/.claude/token-usage.log 2>/dev/null)
|
|
12
|
+
[ "${SUM:-0}" -gt "$BUDGET" ] && echo "Token usage: ~$SUM (budget: $BUDGET)" >&2
|
|
13
|
+
fi
|
|
14
|
+
exit 0
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# uncommitted-work-shield.sh — Auto-stash before destructive git operations
|
|
3
|
+
#
|
|
4
|
+
# Solves: Git operations destroying uncommitted work (#34327, #33850, #37150).
|
|
5
|
+
# Users lost days of work from git reset/checkout/clean.
|
|
6
|
+
# Unlike uncommitted-work-guard (which blocks), this hook SAVES work.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PreToolUse hook on Bash that detects destructive git
|
|
9
|
+
# commands and auto-stashes uncommitted changes before allowing them.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PreToolUse
|
|
12
|
+
# MATCHER: "Bash"
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
INPUT=$(cat)
|
|
17
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
18
|
+
[ -z "$COMMAND" ] && exit 0
|
|
19
|
+
|
|
20
|
+
# Only check destructive git commands
|
|
21
|
+
DESTRUCTIVE_GIT='git\s+(reset\s+--hard|checkout\s+--|clean\s+-[fd]|stash\s+drop|stash\s+clear)'
|
|
22
|
+
|
|
23
|
+
if ! echo "$COMMAND" | grep -qE "$DESTRUCTIVE_GIT"; then
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Check if there are uncommitted changes
|
|
28
|
+
if ! git diff --quiet 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then
|
|
29
|
+
# Auto-stash with timestamp
|
|
30
|
+
STASH_MSG="auto-shield-$(date +%Y%m%d-%H%M%S)"
|
|
31
|
+
git stash push -m "$STASH_MSG" 2>/dev/null || true
|
|
32
|
+
echo "SHIELDED: Uncommitted changes saved to stash '$STASH_MSG'." >&2
|
|
33
|
+
echo "Restore with: git stash pop" >&2
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Allow the command to proceed (changes are saved)
|
|
37
|
+
exit 0
|
package/examples/usage-warn.sh
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# virtual-cwd-helper.sh — Remind about virtual working directory
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude Code is bound to the directory where it was
|
|
5
|
+
# spawned. Users can't switch projects mid-session (#3473).
|
|
6
|
+
#
|
|
7
|
+
# How it works: Reads ~/.claude/virtual-cwd file. If set,
|
|
8
|
+
# warns that commands should be prefixed with cd to the
|
|
9
|
+
# virtual CWD. Users can switch directories by updating
|
|
10
|
+
# the file.
|
|
11
|
+
#
|
|
12
|
+
# Setup: echo "/path/to/project" > ~/.claude/virtual-cwd
|
|
13
|
+
#
|
|
14
|
+
# TRIGGER: PreToolUse
|
|
15
|
+
# MATCHER: "Bash"
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
INPUT=$(cat)
|
|
19
|
+
|
|
20
|
+
VCWD_FILE="${HOME}/.claude/virtual-cwd"
|
|
21
|
+
[ ! -f "$VCWD_FILE" ] && exit 0
|
|
22
|
+
|
|
23
|
+
VCWD=$(cat "$VCWD_FILE" 2>/dev/null)
|
|
24
|
+
[ -z "$VCWD" ] && exit 0
|
|
25
|
+
|
|
26
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
27
|
+
[ -z "$COMMAND" ] && exit 0
|
|
28
|
+
|
|
29
|
+
# Skip if command already starts with cd to the virtual CWD
|
|
30
|
+
if echo "$COMMAND" | grep -q "^cd $VCWD"; then
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# Skip cd commands (user is navigating)
|
|
35
|
+
if echo "$COMMAND" | grep -q "^cd "; then
|
|
36
|
+
exit 0
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
echo "NOTE: Virtual CWD is $VCWD — prefix with: cd $VCWD &&" >&2
|
|
40
|
+
exit 0
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# webfetch-domain-allow.sh — Auto-approve WebFetch for allowed domains
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# WebFetch(domain:*) in settings.json silently fails to match in
|
|
7
|
+
# many configurations (especially sandbox mode). This hook reads
|
|
8
|
+
# the requested URL, extracts the domain, and auto-approves if
|
|
9
|
+
# it matches a configurable allowlist.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PreToolUse
|
|
12
|
+
# MATCHER: "WebFetch"
|
|
13
|
+
#
|
|
14
|
+
# DECISION: exit 0 with permissionDecision "allow" = auto-approve
|
|
15
|
+
# exit 0 with empty JSON = passthrough (ask user)
|
|
16
|
+
#
|
|
17
|
+
# CONFIG: Set CC_WEBFETCH_ALLOW_DOMAINS env var (comma-separated)
|
|
18
|
+
# or edit the ALLOWED_DOMAINS array below.
|
|
19
|
+
# Use "*" to allow all domains.
|
|
20
|
+
#
|
|
21
|
+
# See: https://github.com/anthropics/claude-code/issues/9329
|
|
22
|
+
# ================================================================
|
|
23
|
+
|
|
24
|
+
INPUT=$(cat)
|
|
25
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
26
|
+
|
|
27
|
+
# Only handle WebFetch
|
|
28
|
+
[[ "$TOOL" != "WebFetch" ]] && exit 0
|
|
29
|
+
|
|
30
|
+
# Extract URL from tool input
|
|
31
|
+
URL=$(echo "$INPUT" | jq -r '.tool_input.url // empty' 2>/dev/null)
|
|
32
|
+
[ -z "$URL" ] && exit 0
|
|
33
|
+
|
|
34
|
+
# Extract domain from URL
|
|
35
|
+
DOMAIN=$(echo "$URL" | sed -E 's|^https?://||' | sed 's|/.*||' | sed 's|:.*||')
|
|
36
|
+
[ -z "$DOMAIN" ] && exit 0
|
|
37
|
+
|
|
38
|
+
# ========================================
|
|
39
|
+
# DOMAIN ALLOWLIST — edit to your needs
|
|
40
|
+
# ========================================
|
|
41
|
+
# Option 1: Environment variable (comma-separated)
|
|
42
|
+
# export CC_WEBFETCH_ALLOW_DOMAINS="docs.anthropic.com,github.com,*.example.com"
|
|
43
|
+
# Option 2: Edit this array directly
|
|
44
|
+
ALLOWED_DOMAINS=(
|
|
45
|
+
"*" # Allow all domains — change to specific domains for tighter control
|
|
46
|
+
# "docs.anthropic.com"
|
|
47
|
+
# "github.com"
|
|
48
|
+
# "*.github.io"
|
|
49
|
+
# "developer.mozilla.org"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Override from env if set
|
|
53
|
+
if [ -n "$CC_WEBFETCH_ALLOW_DOMAINS" ]; then
|
|
54
|
+
IFS=',' read -ra ALLOWED_DOMAINS <<< "$CC_WEBFETCH_ALLOW_DOMAINS"
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# Check domain against allowlist
|
|
58
|
+
for pattern in "${ALLOWED_DOMAINS[@]}"; do
|
|
59
|
+
pattern=$(echo "$pattern" | xargs) # trim whitespace
|
|
60
|
+
# Wildcard: allow everything
|
|
61
|
+
if [ "$pattern" = "*" ]; then
|
|
62
|
+
jq -n '{
|
|
63
|
+
hookSpecificOutput: {
|
|
64
|
+
hookEventName: "PreToolUse",
|
|
65
|
+
permissionDecision: "allow"
|
|
66
|
+
}
|
|
67
|
+
}'
|
|
68
|
+
exit 0
|
|
69
|
+
fi
|
|
70
|
+
# Glob pattern: *.example.com matches sub.example.com
|
|
71
|
+
if [[ "$pattern" == \** ]]; then
|
|
72
|
+
suffix="${pattern#\*}"
|
|
73
|
+
if [[ "$DOMAIN" == *"$suffix" ]]; then
|
|
74
|
+
jq -n '{
|
|
75
|
+
hookSpecificOutput: {
|
|
76
|
+
hookEventName: "PreToolUse",
|
|
77
|
+
permissionDecision: "allow"
|
|
78
|
+
}
|
|
79
|
+
}'
|
|
80
|
+
exit 0
|
|
81
|
+
fi
|
|
82
|
+
fi
|
|
83
|
+
# Exact match
|
|
84
|
+
if [ "$DOMAIN" = "$pattern" ]; then
|
|
85
|
+
jq -n '{
|
|
86
|
+
hookSpecificOutput: {
|
|
87
|
+
hookEventName: "PreToolUse",
|
|
88
|
+
permissionDecision: "allow"
|
|
89
|
+
}
|
|
90
|
+
}'
|
|
91
|
+
exit 0
|
|
92
|
+
fi
|
|
93
|
+
done
|
|
94
|
+
|
|
95
|
+
# Not in allowlist — passthrough to normal permission flow
|
|
96
|
+
exit 0
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# worktree-delete-guard.sh — Block git worktree removal
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Prevents one Claude session from deleting a worktree that
|
|
7
|
+
# another session is actively using. Opus 4.6 has been observed
|
|
8
|
+
# removing worktrees during cleanup without checking for
|
|
9
|
+
# concurrent sessions. (#40850)
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PreToolUse
|
|
12
|
+
# MATCHER: "Bash"
|
|
13
|
+
#
|
|
14
|
+
# WHAT IT BLOCKS:
|
|
15
|
+
# - git worktree remove <path>
|
|
16
|
+
# - git worktree prune
|
|
17
|
+
# - rm -rf on worktree directories
|
|
18
|
+
# ================================================================
|
|
19
|
+
|
|
20
|
+
COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
21
|
+
[ -z "$COMMAND" ] && exit 0
|
|
22
|
+
|
|
23
|
+
# Block explicit worktree removal
|
|
24
|
+
if echo "$COMMAND" | grep -qE 'git\s+worktree\s+(remove|prune)'; then
|
|
25
|
+
echo "BLOCKED: Cannot remove git worktrees — other sessions may depend on them." >&2
|
|
26
|
+
echo "Command: $COMMAND" >&2
|
|
27
|
+
echo "List worktrees first: git worktree list" >&2
|
|
28
|
+
exit 2
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Block rm on worktree paths (if we're in a git repo)
|
|
32
|
+
if git rev-parse --git-dir &>/dev/null; then
|
|
33
|
+
COMMON_DIR=$(git rev-parse --path-format=absolute --git-common-dir 2>/dev/null)
|
|
34
|
+
if [ -n "$COMMON_DIR" ]; then
|
|
35
|
+
WORKTREES_DIR="${COMMON_DIR}/worktrees"
|
|
36
|
+
if echo "$COMMAND" | grep -qE "(rm|rmdir)\s+.*worktrees"; then
|
|
37
|
+
echo "BLOCKED: Cannot delete worktree storage directory." >&2
|
|
38
|
+
exit 2
|
|
39
|
+
fi
|
|
40
|
+
fi
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
exit 0
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# worktree-memory-guard.sh — Warn when memory path resolves to main worktree
|
|
3
|
+
#
|
|
4
|
+
# Solves: Git worktrees resolving to main worktree's memory directory (#39920).
|
|
5
|
+
# In worktree isolation mode, Claude writes memory to the main repo's
|
|
6
|
+
# .claude/projects/ instead of the worktree's, causing cross-contamination.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PreToolUse hook on Write/Edit that checks if the target
|
|
9
|
+
# path is a memory file (.claude/projects/*/memory/) and warns if
|
|
10
|
+
# the current working directory differs from the resolved path's repo root.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: PreToolUse
|
|
13
|
+
# MATCHER: "Write|Edit"
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
19
|
+
[ -z "$FILE" ] && exit 0
|
|
20
|
+
|
|
21
|
+
# Only check memory files
|
|
22
|
+
if ! echo "$FILE" | grep -q '.claude/projects/.*/memory/'; then
|
|
23
|
+
exit 0
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Check if we're in a worktree
|
|
27
|
+
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null || echo "")
|
|
28
|
+
if [ -z "$GIT_DIR" ]; then
|
|
29
|
+
exit 0
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Detect worktree by checking if .git is a file (worktree) vs directory (main)
|
|
33
|
+
if [ -f ".git" ]; then
|
|
34
|
+
# We're in a worktree — check if memory path points to main repo
|
|
35
|
+
MAIN_WORKTREE=$(git rev-parse --path-format=absolute --git-common-dir 2>/dev/null | sed 's|/.git$||')
|
|
36
|
+
CWD=$(pwd)
|
|
37
|
+
|
|
38
|
+
if [ "$MAIN_WORKTREE" != "$CWD" ] && echo "$FILE" | grep -q "$MAIN_WORKTREE"; then
|
|
39
|
+
echo "WARNING: Memory file resolves to main worktree, not current worktree." >&2
|
|
40
|
+
echo " File: $FILE" >&2
|
|
41
|
+
echo " Main: $MAIN_WORKTREE" >&2
|
|
42
|
+
echo " CWD: $CWD" >&2
|
|
43
|
+
echo " Consider writing to worktree-local memory instead." >&2
|
|
44
|
+
fi
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
exit 0
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# worktree-path-validator.sh — Warn when file operations target main workspace instead of worktree
|
|
3
|
+
#
|
|
4
|
+
# Solves: In worktree sessions, Edit/Read/Write tools target
|
|
5
|
+
# files in the main workspace instead of the worktree
|
|
6
|
+
# directory (#36182). This causes edits to the wrong
|
|
7
|
+
# copy of files.
|
|
8
|
+
#
|
|
9
|
+
# How it works: Detects if running in a worktree (git rev-parse
|
|
10
|
+
# --git-common-dir differs from --git-dir). If so, checks
|
|
11
|
+
# that file_path targets the worktree, not the main workspace.
|
|
12
|
+
#
|
|
13
|
+
# TRIGGER: PreToolUse
|
|
14
|
+
# MATCHER: "Edit|Write|Read"
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
|
|
19
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
20
|
+
[ -z "$FILE_PATH" ] && exit 0
|
|
21
|
+
|
|
22
|
+
# Check if we're in a worktree
|
|
23
|
+
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) || exit 0
|
|
24
|
+
GIT_COMMON=$(git rev-parse --git-common-dir 2>/dev/null) || exit 0
|
|
25
|
+
|
|
26
|
+
# If git-dir == git-common-dir, we're in the main repo (not a worktree)
|
|
27
|
+
[ "$GIT_DIR" = "$GIT_COMMON" ] && exit 0
|
|
28
|
+
|
|
29
|
+
# We're in a worktree — check that file_path is within the worktree
|
|
30
|
+
WORKTREE_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0
|
|
31
|
+
MAIN_ROOT=$(cd "$GIT_COMMON/.." && pwd 2>/dev/null) || exit 0
|
|
32
|
+
|
|
33
|
+
# If file_path starts with the main workspace path instead of worktree
|
|
34
|
+
if echo "$FILE_PATH" | grep -q "^$MAIN_ROOT" && ! echo "$FILE_PATH" | grep -q "^$WORKTREE_ROOT"; then
|
|
35
|
+
echo "WARNING: File path targets main workspace, not this worktree." >&2
|
|
36
|
+
echo " File: $FILE_PATH" >&2
|
|
37
|
+
echo " Worktree: $WORKTREE_ROOT" >&2
|
|
38
|
+
echo " Main repo: $MAIN_ROOT" >&2
|
|
39
|
+
echo " Consider using: ${FILE_PATH/$MAIN_ROOT/$WORKTREE_ROOT}" >&2
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
exit 0
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
MAIN_TREE=$(git worktree list --porcelain 2>/dev/null | head -1 | sed 's/worktree //')
|
|
2
|
+
[ -z "$MAIN_TREE" ] && exit 0
|
|
3
|
+
CUR_DIR=$(pwd)
|
|
4
|
+
[ "$CUR_DIR" = "$MAIN_TREE" ] && exit 0
|
|
5
|
+
to_project_dir() {
|
|
6
|
+
echo "$HOME/.claude/projects/$(echo "$1" | sed 's|/|-|g; s|^-||')"
|
|
7
|
+
}
|
|
8
|
+
MAIN_PROJECT=$(to_project_dir "$MAIN_TREE")
|
|
9
|
+
CUR_PROJECT=$(to_project_dir "$CUR_DIR")
|
|
10
|
+
if [ -d "$MAIN_PROJECT" ] && [ ! -L "$CUR_PROJECT" ]; then
|
|
11
|
+
if [ -d "$CUR_PROJECT" ] && [ -z "$(ls -A "$CUR_PROJECT" 2>/dev/null)" ]; then
|
|
12
|
+
rmdir "$CUR_PROJECT"
|
|
13
|
+
fi
|
|
14
|
+
if [ ! -e "$CUR_PROJECT" ]; then
|
|
15
|
+
ln -s "$MAIN_PROJECT" "$CUR_PROJECT"
|
|
16
|
+
echo "Linked worktree project dir → main repo" >&2
|
|
17
|
+
fi
|
|
18
|
+
fi
|
|
19
|
+
exit 0
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# write-overwrite-confirm.sh — Warn when Write tool overwrites large files
|
|
3
|
+
#
|
|
4
|
+
# Solves: Write tool silently replacing large files with new content (#34597).
|
|
5
|
+
# A 500-line file can be overwritten with 10 lines without warning.
|
|
6
|
+
#
|
|
7
|
+
# How it works: PreToolUse hook on Write that compares the new content
|
|
8
|
+
# size against the existing file. If the new content is significantly
|
|
9
|
+
# smaller, warns about potential data loss.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PreToolUse
|
|
12
|
+
# MATCHER: "Write"
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
INPUT=$(cat)
|
|
17
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
18
|
+
[ -z "$FILE" ] && exit 0
|
|
19
|
+
|
|
20
|
+
# Skip if file doesn't exist (new file creation)
|
|
21
|
+
[ ! -f "$FILE" ] && exit 0
|
|
22
|
+
|
|
23
|
+
# Get current file size (lines)
|
|
24
|
+
CURRENT_LINES=$(wc -l < "$FILE" 2>/dev/null || echo 0)
|
|
25
|
+
|
|
26
|
+
# Get new content size (approximate from JSON length)
|
|
27
|
+
NEW_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // empty' 2>/dev/null)
|
|
28
|
+
NEW_LINES=$(echo "$NEW_CONTENT" | wc -l 2>/dev/null || echo 0)
|
|
29
|
+
|
|
30
|
+
# Warn if shrinking by more than 50% and file is > 50 lines
|
|
31
|
+
if [ "$CURRENT_LINES" -gt 50 ] && [ "$NEW_LINES" -gt 0 ]; then
|
|
32
|
+
RATIO=$((NEW_LINES * 100 / CURRENT_LINES))
|
|
33
|
+
if [ "$RATIO" -lt 50 ]; then
|
|
34
|
+
echo "WARNING: File shrinking from $CURRENT_LINES to ~$NEW_LINES lines ($RATIO%)." >&2
|
|
35
|
+
echo "File: $FILE" >&2
|
|
36
|
+
echo "Consider using Edit tool for targeted changes instead of full rewrite." >&2
|
|
37
|
+
fi
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
exit 0
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# write-shrink-guard.sh — Block writes that drastically shrink files
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Prevents accidental file truncation. When Claude uses the Write
|
|
7
|
+
# tool, if the new content is <10% of the original file size,
|
|
8
|
+
# it's likely a truncation bug, not an intentional edit.
|
|
9
|
+
#
|
|
10
|
+
# Real case: 31,699-line file truncated to 16 lines, destroying
|
|
11
|
+
# 5 hours of work. (#40807)
|
|
12
|
+
#
|
|
13
|
+
# TRIGGER: PreToolUse
|
|
14
|
+
# MATCHER: "Write"
|
|
15
|
+
#
|
|
16
|
+
# DECISION: exit 2 = block, exit 0 = allow
|
|
17
|
+
# ================================================================
|
|
18
|
+
|
|
19
|
+
INPUT=$(cat)
|
|
20
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
21
|
+
[ "$TOOL" != "Write" ] && exit 0
|
|
22
|
+
|
|
23
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
24
|
+
[ -z "$FILE" ] || [ ! -f "$FILE" ] && exit 0
|
|
25
|
+
|
|
26
|
+
# Get original file size
|
|
27
|
+
OLD_SIZE=$(wc -c < "$FILE" 2>/dev/null || echo 0)
|
|
28
|
+
[ "$OLD_SIZE" -lt 1000 ] && exit 0 # Skip small files
|
|
29
|
+
|
|
30
|
+
# Get new content size
|
|
31
|
+
NEW_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // empty' 2>/dev/null)
|
|
32
|
+
NEW_SIZE=${#NEW_CONTENT}
|
|
33
|
+
|
|
34
|
+
# Calculate ratio
|
|
35
|
+
if [ "$NEW_SIZE" -gt 0 ] && [ "$OLD_SIZE" -gt 0 ]; then
|
|
36
|
+
RATIO=$((NEW_SIZE * 100 / OLD_SIZE))
|
|
37
|
+
if [ "$RATIO" -lt 10 ]; then
|
|
38
|
+
echo "BLOCKED: Write would shrink $(basename "$FILE") from $OLD_SIZE to $NEW_SIZE bytes (${RATIO}% of original)." >&2
|
|
39
|
+
echo "This looks like accidental truncation. Use Edit for targeted changes instead." >&2
|
|
40
|
+
exit 2
|
|
41
|
+
elif [ "$RATIO" -lt 25 ]; then
|
|
42
|
+
echo "WARNING: Write would significantly reduce $(basename "$FILE") from $OLD_SIZE to $NEW_SIZE bytes (${RATIO}%)." >&2
|
|
43
|
+
fi
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
exit 0
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
2
4
|
COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
3
5
|
[ -z "$COMMAND" ] && exit 0
|
|
4
6
|
echo "$COMMAND" | grep -qE "^\s*git\s+commit" || exit 0; S=$(git diff --cached --name-only 2>/dev/null | grep -cvE "test|spec" || echo 0); T=$(git diff --cached --name-only 2>/dev/null | grep -cE "test|spec" || echo 0); [ "$S" -gt 5 ] && [ "$T" -eq 0 ] && echo "WARNING: $S source files, 0 test files" >&2
|