cc-safe-setup 29.6.32 → 29.6.33
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/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/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-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/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-retry-loop-guard.sh +2 -0
- package/examples/edit-verify.sh +2 -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-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-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/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/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/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-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/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-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/system-message-workaround.sh +44 -0
- package/examples/system-package-guard.sh +2 -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 +2 -0
- 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-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/vue-lint-on-edit.sh +2 -0
- package/examples/webfetch-domain-allow.sh +96 -0
- package/examples/worktree-memory-guard.sh +47 -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-test-ratio.sh +2 -0
- package/package.json +2 -2
|
@@ -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,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
|
|
@@ -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,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,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,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,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
|
|
@@ -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
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-safe-setup",
|
|
3
|
-
"version": "29.6.
|
|
4
|
-
"description": "One command to make Claude Code safe.
|
|
3
|
+
"version": "29.6.33",
|
|
4
|
+
"description": "One command to make Claude Code safe. 611 example hooks + 8 built-in. 56 CLI commands. 10411 tests. Works with Auto Mode.",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cc-safe-setup": "index.mjs"
|