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,69 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# spec-file-scope-guard.sh — Restrict edits to files mentioned in a spec document
|
|
3
|
+
#
|
|
4
|
+
# Solves: Agent ignoring specification across multiple sessions (#40383).
|
|
5
|
+
# Claude modifies files not mentioned in the spec, introduces
|
|
6
|
+
# fabricated data, and drifts from the stated objective.
|
|
7
|
+
#
|
|
8
|
+
# How it works: Reads a spec/requirements file, extracts file paths and
|
|
9
|
+
# directory names mentioned in it, then blocks Edit/Write to files
|
|
10
|
+
# outside that scope.
|
|
11
|
+
#
|
|
12
|
+
# Setup:
|
|
13
|
+
# echo "spec.md" > .claude/spec-file.txt
|
|
14
|
+
# # Or set CC_SPEC_FILE=spec.md
|
|
15
|
+
#
|
|
16
|
+
# TRIGGER: PreToolUse
|
|
17
|
+
# MATCHER: "Edit|Write"
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
INPUT=$(cat)
|
|
22
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
23
|
+
|
|
24
|
+
case "$TOOL" in
|
|
25
|
+
Edit|Write) ;;
|
|
26
|
+
*) exit 0 ;;
|
|
27
|
+
esac
|
|
28
|
+
|
|
29
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
30
|
+
[ -z "$FILE" ] && exit 0
|
|
31
|
+
|
|
32
|
+
# Find spec file
|
|
33
|
+
SPEC_FILE="${CC_SPEC_FILE:-}"
|
|
34
|
+
if [ -z "$SPEC_FILE" ] && [ -f ".claude/spec-file.txt" ]; then
|
|
35
|
+
SPEC_FILE=$(head -1 ".claude/spec-file.txt" | tr -d '\n')
|
|
36
|
+
fi
|
|
37
|
+
[ -z "$SPEC_FILE" ] && exit 0
|
|
38
|
+
[ -f "$SPEC_FILE" ] || exit 0
|
|
39
|
+
|
|
40
|
+
# Extract paths/directories mentioned in spec
|
|
41
|
+
# Matches: path/to/file.ext, src/components/, ./config.ts, etc.
|
|
42
|
+
MENTIONED=$(grep -oE '(\.?/?[a-zA-Z0-9_-]+/)+[a-zA-Z0-9_.-]*' "$SPEC_FILE" | sort -u || true)
|
|
43
|
+
|
|
44
|
+
if [ -z "$MENTIONED" ]; then
|
|
45
|
+
# No paths found in spec — don't restrict
|
|
46
|
+
exit 0
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# Check if the target file matches any mentioned path
|
|
50
|
+
BASENAME=$(basename "$FILE")
|
|
51
|
+
DIRNAME=$(dirname "$FILE")
|
|
52
|
+
|
|
53
|
+
for path in $MENTIONED; do
|
|
54
|
+
# Match if file path contains the spec path
|
|
55
|
+
if echo "$FILE" | grep -qF "$path"; then
|
|
56
|
+
exit 0
|
|
57
|
+
fi
|
|
58
|
+
# Match if basename matches
|
|
59
|
+
if echo "$BASENAME" | grep -qF "$path"; then
|
|
60
|
+
exit 0
|
|
61
|
+
fi
|
|
62
|
+
done
|
|
63
|
+
|
|
64
|
+
echo "WARNING: Editing file not mentioned in spec ($SPEC_FILE):" >&2
|
|
65
|
+
echo " File: $FILE" >&2
|
|
66
|
+
echo " Spec mentions: $(echo "$MENTIONED" | head -5 | tr '\n' ', ')" >&2
|
|
67
|
+
echo " Stay focused on the specification." >&2
|
|
68
|
+
# Warning only — change to exit 2 to block
|
|
69
|
+
exit 0
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# subagent-claudemd-inject.sh — Inject CLAUDE.md rules into subagent prompts
|
|
3
|
+
#
|
|
4
|
+
# Solves: Subagents lose CLAUDE.md context since v2.1.84 (#40459).
|
|
5
|
+
# omitClaudeMd:true strips project instructions from Explore/Plan agents,
|
|
6
|
+
# causing them to ignore language preferences, environment config, etc.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PreToolUse hook on Agent that appends key CLAUDE.md rules
|
|
9
|
+
# to the subagent's prompt. Extracts critical rules (marked with SUBAGENT:
|
|
10
|
+
# prefix in CLAUDE.md) and injects them into the agent description/prompt.
|
|
11
|
+
#
|
|
12
|
+
# Setup: Mark critical rules in CLAUDE.md with "SUBAGENT:" prefix:
|
|
13
|
+
# ## SUBAGENT: Always respond in Japanese
|
|
14
|
+
# ## SUBAGENT: Use DEV environment for testing
|
|
15
|
+
#
|
|
16
|
+
# TRIGGER: PreToolUse
|
|
17
|
+
# MATCHER: "Agent"
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
INPUT=$(cat)
|
|
22
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
23
|
+
[ "$TOOL" = "Agent" ] || exit 0
|
|
24
|
+
|
|
25
|
+
# Find CLAUDE.md
|
|
26
|
+
CLAUDEMD=""
|
|
27
|
+
for candidate in "CLAUDE.md" "../CLAUDE.md" "../../CLAUDE.md"; do
|
|
28
|
+
if [ -f "$candidate" ]; then
|
|
29
|
+
CLAUDEMD="$candidate"
|
|
30
|
+
break
|
|
31
|
+
fi
|
|
32
|
+
done
|
|
33
|
+
[ -n "$CLAUDEMD" ] || exit 0
|
|
34
|
+
|
|
35
|
+
# Extract SUBAGENT-tagged rules
|
|
36
|
+
RULES=$(grep -iE '^##?\s*SUBAGENT:' "$CLAUDEMD" 2>/dev/null | sed 's/^##\?\s*SUBAGENT:\s*//' | head -10 || true)
|
|
37
|
+
[ -z "$RULES" ] && exit 0
|
|
38
|
+
|
|
39
|
+
# Inject rules as a system message warning
|
|
40
|
+
echo "REMINDER: Project rules from CLAUDE.md (apply to all subagents):" >&2
|
|
41
|
+
echo "$RULES" | while IFS= read -r rule; do
|
|
42
|
+
echo " - $rule" >&2
|
|
43
|
+
done
|
|
44
|
+
|
|
45
|
+
exit 0
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# subagent-context-size-guard.sh — Warn on thin subagent prompts
|
|
3
|
+
#
|
|
4
|
+
# Solves: Subagents get spawned with minimal context, leading to
|
|
5
|
+
# poor results because they lack necessary background (#40929).
|
|
6
|
+
# The parent agent assumes shared context, but each subagent
|
|
7
|
+
# starts fresh.
|
|
8
|
+
#
|
|
9
|
+
# How it works: Checks Agent tool's prompt parameter length.
|
|
10
|
+
# If under 100 characters, warns that the prompt may be too thin
|
|
11
|
+
# for a standalone agent to work effectively.
|
|
12
|
+
#
|
|
13
|
+
# TRIGGER: PreToolUse
|
|
14
|
+
# MATCHER: "Agent"
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
PROMPT=$(echo "$INPUT" | jq -r '.tool_input.prompt // empty' 2>/dev/null)
|
|
19
|
+
|
|
20
|
+
[ -z "$PROMPT" ] && exit 0
|
|
21
|
+
|
|
22
|
+
LEN=${#PROMPT}
|
|
23
|
+
if [ "$LEN" -lt 100 ]; then
|
|
24
|
+
echo "WARNING: Agent prompt is only ${LEN} chars. Subagents start with zero context — include enough background for them to work independently." >&2
|
|
25
|
+
fi
|
|
26
|
+
exit 0
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# subagent-tool-call-limiter.sh — Limit total tool calls per session
|
|
3
|
+
#
|
|
4
|
+
# Solves: Subagents making unbounded tool calls (#36727).
|
|
5
|
+
# One user reported 234 tool calls in 1.5 hours from a single subagent.
|
|
6
|
+
# Existing rate limiters check frequency, not total count.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PreToolUse hook (all tools) that increments a counter file.
|
|
9
|
+
# When CC_MAX_TOOL_CALLS (default 200) is reached, blocks further calls.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PreToolUse
|
|
12
|
+
# MATCHER: ""
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
MAX_CALLS="${CC_MAX_TOOL_CALLS:-200}"
|
|
17
|
+
COUNTER_FILE="/tmp/claude-tool-call-counter-$$"
|
|
18
|
+
|
|
19
|
+
# Use session-based counter (PID of parent process)
|
|
20
|
+
PPID_FILE="/tmp/claude-tool-call-counter-${PPID:-0}"
|
|
21
|
+
[ -f "$PPID_FILE" ] && COUNTER_FILE="$PPID_FILE"
|
|
22
|
+
|
|
23
|
+
# Initialize or read counter
|
|
24
|
+
if [ -f "$COUNTER_FILE" ]; then
|
|
25
|
+
COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo 0)
|
|
26
|
+
else
|
|
27
|
+
COUNT=0
|
|
28
|
+
COUNTER_FILE="$PPID_FILE"
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
COUNT=$((COUNT + 1))
|
|
32
|
+
echo "$COUNT" > "$COUNTER_FILE"
|
|
33
|
+
|
|
34
|
+
# Check limit
|
|
35
|
+
if [ "$COUNT" -gt "$MAX_CALLS" ]; then
|
|
36
|
+
echo "BLOCKED: Tool call limit reached ($COUNT/$MAX_CALLS)." >&2
|
|
37
|
+
echo "This session has made $COUNT tool calls (limit: $MAX_CALLS)." >&2
|
|
38
|
+
echo "Consider starting a new session or increasing CC_MAX_TOOL_CALLS." >&2
|
|
39
|
+
exit 2
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# Warn at 80%
|
|
43
|
+
WARN_AT=$((MAX_CALLS * 80 / 100))
|
|
44
|
+
if [ "$COUNT" -eq "$WARN_AT" ]; then
|
|
45
|
+
echo "WARNING: $COUNT/$MAX_CALLS tool calls used (80%). Consider wrapping up." >&2
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
exit 0
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
INPUT=$(cat)
|
|
2
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
3
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
4
|
+
[ -z "$FILE" ] && exit 0
|
|
5
|
+
[[ "$TOOL" != "Write" && "$TOOL" != "Edit" ]] && exit 0
|
|
6
|
+
if [ -L "$FILE" ]; then
|
|
7
|
+
TARGET=$(readlink -f "$FILE")
|
|
8
|
+
echo "NOTE: Redirecting write from symlink $FILE → $TARGET" >&2
|
|
9
|
+
echo "{\"updatedInput\":{\"file_path\":\"$TARGET\"}}"
|
|
10
|
+
exit 1
|
|
11
|
+
fi
|
|
12
|
+
exit 0
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# system-message-workaround.sh — Ensure hook warnings reach both user and model
|
|
3
|
+
#
|
|
4
|
+
# Solves: PreToolUse/PostToolUse systemMessage silently dropped (#40380).
|
|
5
|
+
# When a hook returns only systemMessage (without hookSpecificOutput),
|
|
6
|
+
# the warning is invisible to both user and model.
|
|
7
|
+
#
|
|
8
|
+
# How it works: Template hook that demonstrates the correct pattern for
|
|
9
|
+
# sending warnings that are visible. Uses stderr for user visibility
|
|
10
|
+
# AND hookSpecificOutput.systemMessage for model context injection.
|
|
11
|
+
#
|
|
12
|
+
# Usage: Copy and adapt this pattern for your custom warn hooks.
|
|
13
|
+
#
|
|
14
|
+
# TRIGGER: PreToolUse
|
|
15
|
+
# MATCHER: "Bash"
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
INPUT=$(cat)
|
|
20
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
21
|
+
[ -z "$COMMAND" ] && exit 0
|
|
22
|
+
|
|
23
|
+
# Example: warn on dangerous-looking but not blocked commands
|
|
24
|
+
WARNING=""
|
|
25
|
+
|
|
26
|
+
if echo "$COMMAND" | grep -qE 'DROP\s+TABLE|TRUNCATE\s+TABLE'; then
|
|
27
|
+
WARNING="Database destructive operation detected: $COMMAND"
|
|
28
|
+
elif echo "$COMMAND" | grep -qE 'curl.*-X\s*(DELETE|PUT|PATCH)'; then
|
|
29
|
+
WARNING="Destructive HTTP method detected: $COMMAND"
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
if [ -n "$WARNING" ]; then
|
|
33
|
+
# Method 1: stderr — always visible to the user in terminal
|
|
34
|
+
echo "⚠ WARNING: $WARNING" >&2
|
|
35
|
+
|
|
36
|
+
# Method 2: hookSpecificOutput with systemMessage — visible to model
|
|
37
|
+
# This is the workaround for #40380: include hookSpecificOutput
|
|
38
|
+
# to ensure the systemMessage is actually processed
|
|
39
|
+
cat << ENDJSON
|
|
40
|
+
{"hookSpecificOutput":{"hookEventName":"PreToolUse","decision":"allow","systemMessage":"WARNING: $WARNING. Proceed with caution."}}
|
|
41
|
+
ENDJSON
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
exit 0
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# temp-file-cleanup-stop.sh — Clean up tmpclaude-* files on session end
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude Code creates tmpclaude-{hash}-cwd temporary
|
|
5
|
+
# files in the working directory but doesn't clean them
|
|
6
|
+
# up after the session ends (#17720). These accumulate
|
|
7
|
+
# over time and clutter the project.
|
|
8
|
+
#
|
|
9
|
+
# How it works: On Stop event, finds and removes all
|
|
10
|
+
# tmpclaude-*-cwd files in the current directory and /tmp.
|
|
11
|
+
# Only removes files matching the exact pattern to avoid
|
|
12
|
+
# deleting user files.
|
|
13
|
+
#
|
|
14
|
+
# TRIGGER: Stop
|
|
15
|
+
# MATCHER: ""
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
# Clean up tmpclaude-* files in current directory
|
|
20
|
+
find . -maxdepth 1 -name "tmpclaude-*-cwd" -type f -delete 2>/dev/null || true
|
|
21
|
+
|
|
22
|
+
# Also clean up in /tmp
|
|
23
|
+
find /tmp -maxdepth 1 -name "tmpclaude-*" -type f -mmin +60 -delete 2>/dev/null || true
|
|
24
|
+
|
|
25
|
+
# Clean up any .claude-tmp-* files too
|
|
26
|
+
find . -maxdepth 1 -name ".claude-tmp-*" -type f -delete 2>/dev/null || true
|
|
27
|
+
|
|
28
|
+
exit 0
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# terminal-state-restore — restore terminal to clean state on session exit
|
|
3
|
+
# Fixes: bracketed paste mode, application cursor keys, cursor visibility,
|
|
4
|
+
# line wrapping, Kitty keyboard protocol left enabled after exit.
|
|
5
|
+
# Event: Notification (matcher: "stop")
|
|
6
|
+
# Related: https://github.com/anthropics/claude-code/issues/39272
|
|
7
|
+
|
|
8
|
+
# Reset bracketed paste mode
|
|
9
|
+
printf '\e[?2004l'
|
|
10
|
+
# Reset application cursor keys to normal mode
|
|
11
|
+
printf '\e[?1l'
|
|
12
|
+
# Ensure cursor is visible
|
|
13
|
+
printf '\e[?25h'
|
|
14
|
+
# Re-enable line wrapping
|
|
15
|
+
printf '\e[?7h'
|
|
16
|
+
# Disable Kitty keyboard protocol (if enabled)
|
|
17
|
+
printf '\e[>0u' 2>/dev/null
|
|
18
|
+
# Reset character set to default
|
|
19
|
+
printf '\e(B'
|
|
20
|
+
# Restore default SGR (color/style reset)
|
|
21
|
+
printf '\e[0m'
|
|
22
|
+
|
|
23
|
+
exit 0
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
|
|
2
|
+
INPUT=$(cat)
|
|
3
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
3
4
|
[ -z "$COMMAND" ] && exit 0
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
echo "Run your test suite first, then commit." >&2
|
|
16
|
-
exit 2
|
|
5
|
+
STATE="/tmp/cc-tests-ran-$$"
|
|
6
|
+
if echo "$COMMAND" | grep -qE '^\s*(npm\s+test|npx\s+jest|pytest|python\s+-m\s+pytest|cargo\s+test|go\s+test|make\s+test|bundle\s+exec\s+rspec|mix\s+test)'; then
|
|
7
|
+
echo "1" > "$STATE"
|
|
8
|
+
exit 0
|
|
9
|
+
fi
|
|
10
|
+
if echo "$COMMAND" | grep -qE '^\s*git\s+commit'; then
|
|
11
|
+
if [ ! -f "$STATE" ] || [ "$(cat "$STATE" 2>/dev/null)" != "1" ]; then
|
|
12
|
+
echo "WARNING: No test commands detected since last commit." >&2
|
|
13
|
+
echo " Run tests before committing to verify your changes." >&2
|
|
14
|
+
fi
|
|
15
|
+
rm -f "$STATE"
|
|
17
16
|
fi
|
|
18
17
|
exit 0
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
#
|
|
24
24
|
# The "if" field (v2.1.85+) eliminates process spawning for non-push commands.
|
|
25
25
|
# Without "if", the hook still works — it checks internally and exits early.
|
|
26
|
+
#
|
|
27
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
26
28
|
|
|
27
29
|
INPUT=$(cat)
|
|
28
30
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
@@ -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
|
if echo "$COMMAND" | grep -qE "TZ=|--timezone" && ! echo "$COMMAND" | grep -q "UTC"; then echo "NOTE: Non-UTC timezone in command" >&2; fi
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tmp-output-size-guard.sh — Monitor and warn about large tmp output files
|
|
3
|
+
#
|
|
4
|
+
# Solves: Task .output files in /tmp grow unbounded (95GB+) filling disk
|
|
5
|
+
# (#39909). Subagent output aggregation can create multi-GB files.
|
|
6
|
+
#
|
|
7
|
+
# How it works: Notification/SessionStart hook that checks /tmp for large
|
|
8
|
+
# Claude Code output files and warns if any exceed a threshold.
|
|
9
|
+
# Also provides a cleanup command suggestion.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: SessionStart
|
|
12
|
+
# MATCHER: ""
|
|
13
|
+
#
|
|
14
|
+
# Usage:
|
|
15
|
+
# {
|
|
16
|
+
# "hooks": {
|
|
17
|
+
# "SessionStart": [{
|
|
18
|
+
# "hooks": [{ "type": "command", "command": "~/.claude/hooks/tmp-output-size-guard.sh" }]
|
|
19
|
+
# }]
|
|
20
|
+
# }
|
|
21
|
+
# }
|
|
22
|
+
|
|
23
|
+
# Configurable threshold in MB
|
|
24
|
+
THRESHOLD_MB="${CC_TMP_THRESHOLD_MB:-500}"
|
|
25
|
+
|
|
26
|
+
# Find Claude Code tmp directories
|
|
27
|
+
TMP_BASE="/tmp/claude-$(id -u)"
|
|
28
|
+
[ -d "/private/tmp/claude-$(id -u)" ] && TMP_BASE="/private/tmp/claude-$(id -u)"
|
|
29
|
+
[ ! -d "$TMP_BASE" ] && exit 0
|
|
30
|
+
|
|
31
|
+
# Find files over threshold
|
|
32
|
+
LARGE_FILES=$(find "$TMP_BASE" -name "*.output" -size "+${THRESHOLD_MB}M" 2>/dev/null)
|
|
33
|
+
[ -z "$LARGE_FILES" ] && exit 0
|
|
34
|
+
|
|
35
|
+
TOTAL_SIZE=$(echo "$LARGE_FILES" | xargs du -sh 2>/dev/null | awk '{sum+=$1} END {print sum}')
|
|
36
|
+
FILE_COUNT=$(echo "$LARGE_FILES" | wc -l | tr -d ' ')
|
|
37
|
+
|
|
38
|
+
echo "⚠ Found $FILE_COUNT large task output file(s) in $TMP_BASE (>${THRESHOLD_MB}MB each):" >&2
|
|
39
|
+
echo "$LARGE_FILES" | while read f; do
|
|
40
|
+
SIZE=$(du -sh "$f" 2>/dev/null | cut -f1)
|
|
41
|
+
echo " $SIZE $(basename "$f")" >&2
|
|
42
|
+
done
|
|
43
|
+
echo "" >&2
|
|
44
|
+
echo "To clean up: find $TMP_BASE -name '*.output' -size +${THRESHOLD_MB}M -delete" >&2
|
|
45
|
+
|
|
46
|
+
exit 0
|
package/examples/todo-check.sh
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# todo-deadline-warn.sh — Warn about expired TODO deadlines in edited files
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# TODOs with dates (e.g., "TODO(2026-03-01): fix this") are often
|
|
7
|
+
# forgotten. When Claude edits a file containing expired TODOs,
|
|
8
|
+
# this hook warns so they can be addressed while the file is open.
|
|
9
|
+
#
|
|
10
|
+
# TRIGGER: PostToolUse
|
|
11
|
+
# MATCHER: "Edit|Write"
|
|
12
|
+
#
|
|
13
|
+
# DECISION: Advisory only (exit 0). Warns via stderr.
|
|
14
|
+
# ================================================================
|
|
15
|
+
|
|
16
|
+
INPUT=$(cat)
|
|
17
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
18
|
+
[ -z "$FILE" ] && exit 0
|
|
19
|
+
[ ! -f "$FILE" ] && exit 0
|
|
20
|
+
|
|
21
|
+
# Skip non-code files
|
|
22
|
+
case "$FILE" in
|
|
23
|
+
*.md|*.txt|*.json|*.yaml|*.yml|*.toml|*.xml|*.html|*.css) exit 0 ;;
|
|
24
|
+
esac
|
|
25
|
+
|
|
26
|
+
TODAY=$(date +%Y-%m-%d)
|
|
27
|
+
EXPIRED=0
|
|
28
|
+
|
|
29
|
+
# Find TODO/FIXME/HACK with dates like TODO(2026-03-01) or TODO 2026-03-01
|
|
30
|
+
while IFS= read -r line; do
|
|
31
|
+
# Extract date from patterns like TODO(2026-01-15) or FIXME 2026-01-15
|
|
32
|
+
DATE=$(echo "$line" | grep -oE '(TODO|FIXME|HACK|XXX)\s*\(?\s*[0-9]{4}-[0-9]{2}-[0-9]{2}' | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}')
|
|
33
|
+
if [ -n "$DATE" ] && [[ "$DATE" < "$TODAY" ]]; then
|
|
34
|
+
if [ "$EXPIRED" -eq 0 ]; then
|
|
35
|
+
echo "⚠ Expired TODOs in $(basename "$FILE"):" >&2
|
|
36
|
+
fi
|
|
37
|
+
LINENUM=$(echo "$line" | cut -d: -f1)
|
|
38
|
+
CONTENT=$(echo "$line" | cut -d: -f2- | sed 's/^[[:space:]]*//')
|
|
39
|
+
echo " L${LINENUM}: $CONTENT (expired: $DATE)" >&2
|
|
40
|
+
EXPIRED=$((EXPIRED + 1))
|
|
41
|
+
fi
|
|
42
|
+
done < <(grep -n -E '(TODO|FIXME|HACK|XXX).*[0-9]{4}-[0-9]{2}-[0-9]{2}' "$FILE" 2>/dev/null)
|
|
43
|
+
|
|
44
|
+
if [ "$EXPIRED" -gt 0 ]; then
|
|
45
|
+
echo " $EXPIRED expired TODO(s) — consider resolving while editing this file." >&2
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
exit 0
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# token-budget-per-task.sh — Track and warn on per-task token usage
|
|
3
|
+
#
|
|
4
|
+
# Solves: A single task consuming the entire daily token budget.
|
|
5
|
+
# Without visibility into per-task costs, users don't realize
|
|
6
|
+
# until they hit rate limits.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PostToolUse hook that estimates tokens per tool call
|
|
9
|
+
# and tracks cumulative usage. Warns at configurable thresholds.
|
|
10
|
+
#
|
|
11
|
+
# CONFIG:
|
|
12
|
+
# CC_TOKEN_WARN_THRESHOLD=50000 (warn at this many tokens)
|
|
13
|
+
# CC_TOKEN_BLOCK_THRESHOLD=200000 (block at this many tokens)
|
|
14
|
+
#
|
|
15
|
+
# TRIGGER: PostToolUse
|
|
16
|
+
# MATCHER: ""
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
INPUT=$(cat)
|
|
21
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
22
|
+
[ -z "$TOOL" ] && exit 0
|
|
23
|
+
|
|
24
|
+
COUNTER_FILE="/tmp/claude-token-budget-${PPID:-0}"
|
|
25
|
+
WARN="${CC_TOKEN_WARN_THRESHOLD:-50000}"
|
|
26
|
+
BLOCK="${CC_TOKEN_BLOCK_THRESHOLD:-200000}"
|
|
27
|
+
|
|
28
|
+
# Rough token estimates per tool call
|
|
29
|
+
case "$TOOL" in
|
|
30
|
+
Bash) TOKENS=500 ;;
|
|
31
|
+
Read) TOKENS=2000 ;;
|
|
32
|
+
Edit|Write) TOKENS=1000 ;;
|
|
33
|
+
Glob|Grep) TOKENS=300 ;;
|
|
34
|
+
Agent) TOKENS=5000 ;;
|
|
35
|
+
*) TOKENS=200 ;;
|
|
36
|
+
esac
|
|
37
|
+
|
|
38
|
+
# Add result size estimate
|
|
39
|
+
RESULT_LEN=$(echo "$INPUT" | jq -r '.tool_result // "" | length' 2>/dev/null || echo 0)
|
|
40
|
+
TOKENS=$((TOKENS + RESULT_LEN / 4)) # ~4 chars per token
|
|
41
|
+
|
|
42
|
+
# Update counter
|
|
43
|
+
TOTAL=$(cat "$COUNTER_FILE" 2>/dev/null || echo 0)
|
|
44
|
+
TOTAL=$((TOTAL + TOKENS))
|
|
45
|
+
echo "$TOTAL" > "$COUNTER_FILE"
|
|
46
|
+
|
|
47
|
+
if [ "$TOTAL" -ge "$BLOCK" ]; then
|
|
48
|
+
echo "TOKEN BUDGET: ~${TOTAL} tokens used in this task (limit: ${BLOCK})." >&2
|
|
49
|
+
echo "Consider breaking this into smaller tasks." >&2
|
|
50
|
+
# Warning only — change to exit 2 to block
|
|
51
|
+
elif [ "$TOTAL" -ge "$WARN" ]; then
|
|
52
|
+
echo "TOKEN BUDGET: ~${TOTAL} tokens used (warning threshold: ${WARN})." >&2
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
exit 0
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# token-spike-alert.sh — Alert on abnormal token consumption per turn
|
|
3
|
+
#
|
|
4
|
+
# Solves: Users report 10-20% of their 5-hour quota consumed by
|
|
5
|
+
# a single lightweight question (#40524, #38029, #40881).
|
|
6
|
+
# Cache invalidation causes full context re-processing,
|
|
7
|
+
# spiking token usage without user awareness.
|
|
8
|
+
#
|
|
9
|
+
# How it works: Tracks tool call count per session via a counter
|
|
10
|
+
# file. If more than MAX_TOOLS_PER_TURN tool calls happen in
|
|
11
|
+
# rapid succession (within 30 seconds), warns about possible
|
|
12
|
+
# runaway behavior that could spike token usage.
|
|
13
|
+
#
|
|
14
|
+
# TRIGGER: PostToolUse
|
|
15
|
+
# MATCHER: ""
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
COUNTER_FILE="/tmp/claude-token-spike-$$"
|
|
20
|
+
MAX_TOOLS_PER_BURST="${MAX_TOOLS_PER_BURST:-15}"
|
|
21
|
+
|
|
22
|
+
# Get current timestamp
|
|
23
|
+
NOW=$(date +%s)
|
|
24
|
+
|
|
25
|
+
# Read last timestamp and count
|
|
26
|
+
if [ -f "$COUNTER_FILE" ]; then
|
|
27
|
+
LAST_TS=$(head -1 "$COUNTER_FILE" 2>/dev/null || echo "0")
|
|
28
|
+
COUNT=$(tail -1 "$COUNTER_FILE" 2>/dev/null || echo "0")
|
|
29
|
+
else
|
|
30
|
+
LAST_TS=0
|
|
31
|
+
COUNT=0
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# If within 30-second burst window
|
|
35
|
+
DELTA=$((NOW - LAST_TS))
|
|
36
|
+
if [ "$DELTA" -lt 30 ]; then
|
|
37
|
+
COUNT=$((COUNT + 1))
|
|
38
|
+
else
|
|
39
|
+
COUNT=1
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# Save state
|
|
43
|
+
echo "$NOW" > "$COUNTER_FILE"
|
|
44
|
+
echo "$COUNT" >> "$COUNTER_FILE"
|
|
45
|
+
|
|
46
|
+
# Alert if burst detected
|
|
47
|
+
if [ "$COUNT" -ge "$MAX_TOOLS_PER_BURST" ]; then
|
|
48
|
+
echo "WARNING: $COUNT tool calls in ${DELTA}s burst. Possible runaway behavior — check token consumption." >&2
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
exit 0
|