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,53 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# pre-compact-knowledge-save.sh — Save critical context before compaction
|
|
3
|
+
#
|
|
4
|
+
# Solves: Context compaction losing important decisions, file locations,
|
|
5
|
+
# and progress state. After /compact, Claude forgets what it was
|
|
6
|
+
# doing and repeats work or contradicts earlier decisions.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PreCompact hook that saves the current session state
|
|
9
|
+
# to a checkpoint file that survives compaction. The model can read
|
|
10
|
+
# this file after compaction to restore context.
|
|
11
|
+
#
|
|
12
|
+
# The checkpoint includes: current task, recent decisions, modified files.
|
|
13
|
+
#
|
|
14
|
+
# TRIGGER: PreCompact
|
|
15
|
+
# MATCHER: ""
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
INPUT=$(cat)
|
|
20
|
+
|
|
21
|
+
CHECKPOINT="${CC_COMPACT_CHECKPOINT:-.claude/pre-compact-checkpoint.md}"
|
|
22
|
+
mkdir -p "$(dirname "$CHECKPOINT")"
|
|
23
|
+
|
|
24
|
+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
25
|
+
|
|
26
|
+
# Gather session state
|
|
27
|
+
GIT_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
|
|
28
|
+
MODIFIED=$(git diff --name-only 2>/dev/null | head -10 || echo "none")
|
|
29
|
+
STAGED=$(git diff --cached --name-only 2>/dev/null | head -10 || echo "none")
|
|
30
|
+
RECENT_COMMITS=$(git log --oneline -3 2>/dev/null || echo "none")
|
|
31
|
+
|
|
32
|
+
cat > "$CHECKPOINT" << CHECKPOINT_EOF
|
|
33
|
+
# Pre-Compact Checkpoint
|
|
34
|
+
Generated: ${TIMESTAMP}
|
|
35
|
+
Branch: ${GIT_BRANCH}
|
|
36
|
+
|
|
37
|
+
## Modified files (not staged)
|
|
38
|
+
${MODIFIED}
|
|
39
|
+
|
|
40
|
+
## Staged files
|
|
41
|
+
${STAGED}
|
|
42
|
+
|
|
43
|
+
## Recent commits
|
|
44
|
+
${RECENT_COMMITS}
|
|
45
|
+
|
|
46
|
+
## Notes
|
|
47
|
+
Read this file after compaction to restore context.
|
|
48
|
+
Check tasks/todo.md for current task progress.
|
|
49
|
+
CHECKPOINT_EOF
|
|
50
|
+
|
|
51
|
+
echo "Pre-compact checkpoint saved to $CHECKPOINT" >&2
|
|
52
|
+
|
|
53
|
+
exit 0
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# pre-compact-transcript-export.sh — Export conversation before compaction
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# After compaction, the pre-compaction conversation is inaccessible
|
|
7
|
+
# in the TUI. This hook exports the conversation to a human-readable
|
|
8
|
+
# markdown file before compaction happens, so you can review it later.
|
|
9
|
+
#
|
|
10
|
+
# Reads transcript.jsonl and extracts:
|
|
11
|
+
# - User messages (prompts)
|
|
12
|
+
# - Assistant messages (responses)
|
|
13
|
+
# - Tool calls and results (summarized)
|
|
14
|
+
#
|
|
15
|
+
# TRIGGER: PreCompact
|
|
16
|
+
# MATCHER: (none — PreCompact has no matcher)
|
|
17
|
+
#
|
|
18
|
+
# OUTPUT: .claude/conversation-snapshots/TIMESTAMP.md
|
|
19
|
+
#
|
|
20
|
+
# See: https://github.com/anthropics/claude-code/issues/27242
|
|
21
|
+
# ================================================================
|
|
22
|
+
|
|
23
|
+
# Find the current session transcript
|
|
24
|
+
SESSION_DIR="${HOME}/.claude/projects"
|
|
25
|
+
TRANSCRIPT=""
|
|
26
|
+
|
|
27
|
+
# Try to find transcript from input
|
|
28
|
+
INPUT=$(cat)
|
|
29
|
+
TRANSCRIPT=$(echo "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)
|
|
30
|
+
|
|
31
|
+
# Fallback: find most recent transcript.jsonl
|
|
32
|
+
if [ -z "$TRANSCRIPT" ] || [ ! -f "$TRANSCRIPT" ]; then
|
|
33
|
+
TRANSCRIPT=$(find "$SESSION_DIR" -name "transcript.jsonl" -newer /tmp/.claude-session-start 2>/dev/null | head -1)
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# If still not found, try the current working directory
|
|
37
|
+
if [ -z "$TRANSCRIPT" ] || [ ! -f "$TRANSCRIPT" ]; then
|
|
38
|
+
TRANSCRIPT=$(find "$SESSION_DIR" -name "transcript.jsonl" -mmin -60 2>/dev/null | sort -t/ -k1 | tail -1)
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
[ -z "$TRANSCRIPT" ] || [ ! -f "$TRANSCRIPT" ] && exit 0
|
|
42
|
+
|
|
43
|
+
# Create snapshot directory
|
|
44
|
+
SNAPSHOT_DIR="${HOME}/.claude/conversation-snapshots"
|
|
45
|
+
mkdir -p "$SNAPSHOT_DIR"
|
|
46
|
+
TIMESTAMP=$(date -u +"%Y%m%d-%H%M%S")
|
|
47
|
+
OUTPUT="$SNAPSHOT_DIR/${TIMESTAMP}.md"
|
|
48
|
+
|
|
49
|
+
# Extract human-readable conversation
|
|
50
|
+
{
|
|
51
|
+
echo "# Conversation Snapshot"
|
|
52
|
+
echo "Exported: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
|
|
53
|
+
echo "Source: $TRANSCRIPT"
|
|
54
|
+
echo ""
|
|
55
|
+
echo "---"
|
|
56
|
+
echo ""
|
|
57
|
+
|
|
58
|
+
# Parse JSONL and extract messages
|
|
59
|
+
while IFS= read -r line; do
|
|
60
|
+
ROLE=$(echo "$line" | jq -r '.message.role // empty' 2>/dev/null)
|
|
61
|
+
case "$ROLE" in
|
|
62
|
+
user)
|
|
63
|
+
CONTENT=$(echo "$line" | jq -r '.message.content // empty' 2>/dev/null)
|
|
64
|
+
if [ -n "$CONTENT" ] && [ "$CONTENT" != "null" ]; then
|
|
65
|
+
echo "## User"
|
|
66
|
+
echo "$CONTENT" | head -20
|
|
67
|
+
echo ""
|
|
68
|
+
fi
|
|
69
|
+
;;
|
|
70
|
+
assistant)
|
|
71
|
+
TEXT=$(echo "$line" | jq -r '[.message.content[]? | select(.type=="text") | .text] | join("\n")' 2>/dev/null)
|
|
72
|
+
if [ -n "$TEXT" ] && [ "$TEXT" != "null" ]; then
|
|
73
|
+
echo "## Assistant"
|
|
74
|
+
echo "$TEXT" | head -50
|
|
75
|
+
echo ""
|
|
76
|
+
fi
|
|
77
|
+
;;
|
|
78
|
+
esac
|
|
79
|
+
done < "$TRANSCRIPT"
|
|
80
|
+
} > "$OUTPUT" 2>/dev/null
|
|
81
|
+
|
|
82
|
+
LINES=$(wc -l < "$OUTPUT" 2>/dev/null || echo 0)
|
|
83
|
+
echo "Conversation snapshot saved: $OUTPUT ($LINES lines)" >&2
|
|
84
|
+
|
|
85
|
+
exit 0
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
# Claude Code has built-in Read, Edit, Grep, Glob tools that are faster and safer
|
|
7
7
|
# than bash equivalents. But Claude often reaches for sed, grep, cat instead.
|
|
8
8
|
# This hook denies those commands with a pointer to the correct built-in tool.
|
|
9
|
+
#
|
|
10
|
+
# TRIGGER: PermissionRequest MATCHER: ""
|
|
9
11
|
|
|
10
12
|
INPUT=$(cat)
|
|
11
13
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
package/examples/prefer-const.sh
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# TRIGGER: PreToolUse MATCHER: "Edit|Write"
|
|
2
4
|
CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
|
|
3
5
|
[ -z "$CONTENT" ] && exit 0
|
|
4
6
|
echo "$CONTENT" | grep -qE "^\s*let\s+\w+\s*=" && echo "NOTE: Consider const if not reassigned" >&2
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# prefer-dedicated-tools.sh — Force use of dedicated tools instead of Bash equivalents
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude uses Bash cat/grep/head/tail instead of dedicated Read/Grep
|
|
5
|
+
# tools, wasting tokens on shell overhead and losing structured output
|
|
6
|
+
# (#39979). The system prompt says to use dedicated tools, but the
|
|
7
|
+
# model ignores it.
|
|
8
|
+
#
|
|
9
|
+
# How it works: PreToolUse hook on Bash that detects file-reading commands
|
|
10
|
+
# and blocks them with a suggestion to use the dedicated tool instead.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: PreToolUse
|
|
13
|
+
# MATCHER: "Bash"
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
# {
|
|
17
|
+
# "hooks": {
|
|
18
|
+
# "PreToolUse": [{
|
|
19
|
+
# "matcher": "Bash",
|
|
20
|
+
# "hooks": [{ "type": "command", "command": "~/.claude/hooks/prefer-dedicated-tools.sh" }]
|
|
21
|
+
# }]
|
|
22
|
+
# }
|
|
23
|
+
# }
|
|
24
|
+
|
|
25
|
+
INPUT=$(cat)
|
|
26
|
+
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
27
|
+
[ -z "$CMD" ] && exit 0
|
|
28
|
+
|
|
29
|
+
# Detect cat used for reading files (not piped)
|
|
30
|
+
if echo "$CMD" | grep -qE '^\s*cat\s+[^|]+$'; then
|
|
31
|
+
echo "BLOCKED: Use the Read tool instead of 'cat' for reading files." >&2
|
|
32
|
+
exit 2
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Detect head/tail used for reading files
|
|
36
|
+
if echo "$CMD" | grep -qE '^\s*(head|tail)\s+(-[0-9]+\s+)?[^|]+$'; then
|
|
37
|
+
echo "BLOCKED: Use the Read tool (with offset/limit) instead of head/tail." >&2
|
|
38
|
+
exit 2
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Detect grep used for searching (not piped)
|
|
42
|
+
if echo "$CMD" | grep -qE '^\s*grep\s+(-[a-zA-Z]*\s+)*[^|]+$' && ! echo "$CMD" | grep -qE '\|'; then
|
|
43
|
+
echo "BLOCKED: Use the Grep tool instead of 'grep' for searching files." >&2
|
|
44
|
+
exit 2
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Detect find used for file discovery
|
|
48
|
+
if echo "$CMD" | grep -qE '^\s*find\s+\S+\s+-name\s'; then
|
|
49
|
+
echo "BLOCKED: Use the Glob tool instead of 'find' for file discovery." >&2
|
|
50
|
+
exit 2
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Allow piped commands (cat file | grep pattern is a valid use case)
|
|
54
|
+
# Allow other legitimate bash usage
|
|
55
|
+
exit 0
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# TRIGGER: PreToolUse MATCHER: "Edit|Write"
|
|
2
4
|
CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
|
|
3
5
|
[ -z "$CONTENT" ] && exit 0
|
|
4
6
|
echo "$CONTENT" | grep -qE "\w+\s*&&\s*\w+\.\w+" && echo "NOTE: Consider optional chaining (?.) instead" >&2
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
# Warns when a user prompt exceeds a character threshold.
|
|
7
7
|
# Very long prompts can consume excessive context and tokens.
|
|
8
8
|
# Adjust THRESHOLD to match your comfort level.
|
|
9
|
+
#
|
|
10
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
9
11
|
|
|
10
12
|
INPUT=$(cat)
|
|
11
13
|
PROMPT=$(echo "$INPUT" | jq -r '.prompt // empty' 2>/dev/null)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# public-repo-push-guard.sh — Block pushing proprietary code to public repos
|
|
3
|
+
#
|
|
4
|
+
# Solves: Accidental exposure of private code to public repositories (#29225).
|
|
5
|
+
# Claude pushes code containing internal paths, strategy files, or
|
|
6
|
+
# proprietary patterns to a public GitHub repo.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PreToolUse hook on Bash that detects git push commands,
|
|
9
|
+
# checks if the remote repo is public via gh API, then scans staged
|
|
10
|
+
# files against a configurable blocklist.
|
|
11
|
+
#
|
|
12
|
+
# Configuration: CC_PRIVATE_PATTERNS (colon-separated glob patterns)
|
|
13
|
+
# Default: "internal/:strategies/:*.secret.*:*.private.*"
|
|
14
|
+
#
|
|
15
|
+
# TRIGGER: PreToolUse
|
|
16
|
+
# MATCHER: "Bash"
|
|
17
|
+
|
|
18
|
+
set -uo pipefail
|
|
19
|
+
|
|
20
|
+
INPUT=$(cat)
|
|
21
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
22
|
+
[ -z "$COMMAND" ] && exit 0
|
|
23
|
+
|
|
24
|
+
# Only check git push
|
|
25
|
+
if ! echo "$COMMAND" | grep -qE '^\s*git\s+push\b'; then
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Get remote URL
|
|
30
|
+
REMOTE=$(git remote get-url origin 2>/dev/null || echo "")
|
|
31
|
+
[ -z "$REMOTE" ] && exit 0
|
|
32
|
+
|
|
33
|
+
# Extract owner/repo from URL
|
|
34
|
+
REPO=$(echo "$REMOTE" | sed -E 's|.*github\.com[:/]||; s|\.git$||')
|
|
35
|
+
[ -z "$REPO" ] && exit 0
|
|
36
|
+
|
|
37
|
+
# Check visibility (requires gh CLI)
|
|
38
|
+
if command -v gh &>/dev/null; then
|
|
39
|
+
VISIBILITY=$(gh repo view "$REPO" --json visibility --jq '.visibility' 2>/dev/null || echo "UNKNOWN")
|
|
40
|
+
if [ "$VISIBILITY" = "PUBLIC" ]; then
|
|
41
|
+
# Check staged files against private patterns
|
|
42
|
+
PATTERNS="${CC_PRIVATE_PATTERNS:-internal/:strategies/:*.secret.*:*.private.*}"
|
|
43
|
+
STAGED=$(git diff --cached --name-only 2>/dev/null || echo "")
|
|
44
|
+
|
|
45
|
+
IFS=':' read -ra PATS <<< "$PATTERNS"
|
|
46
|
+
for pat in "${PATS[@]}"; do
|
|
47
|
+
MATCH=$(echo "$STAGED" | grep -E "$pat" | head -1)
|
|
48
|
+
if [ -n "$MATCH" ]; then
|
|
49
|
+
echo "BLOCKED: Pushing to PUBLIC repo '$REPO' with private file pattern." >&2
|
|
50
|
+
echo "File: $MATCH matches pattern: $pat" >&2
|
|
51
|
+
echo "Remove private files from staging or push to a private repo." >&2
|
|
52
|
+
exit 2
|
|
53
|
+
fi
|
|
54
|
+
done
|
|
55
|
+
fi
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
exit 0
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
# The PreToolUse companion then checks this record before allowing git push.
|
|
6
6
|
#
|
|
7
7
|
# Detected test commands: npm test, pytest, cargo test, go test, make test, etc.
|
|
8
|
+
#
|
|
9
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
8
10
|
|
|
9
11
|
INPUT=$(cat)
|
|
10
12
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# read-all-files-enforcer.sh — Track which files have been read in a directory
|
|
3
|
+
#
|
|
4
|
+
# Solves: Agent skips files when told to "read every file" (#40389).
|
|
5
|
+
# Claude delegates to subagents, summarizes instead of reading,
|
|
6
|
+
# or substitutes its own judgment about what needs reading.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PostToolUse hook on Read that logs read files.
|
|
9
|
+
# Paired with a manual check: after the user says "read all files in X",
|
|
10
|
+
# compare the log against actual directory contents.
|
|
11
|
+
#
|
|
12
|
+
# Usage: Set CC_READ_TRACK_DIR to the target directory.
|
|
13
|
+
# export CC_READ_TRACK_DIR="docs/"
|
|
14
|
+
# After the task, check: diff <(ls docs/) <(cat /tmp/claude-read-log-*)
|
|
15
|
+
#
|
|
16
|
+
# TRIGGER: PostToolUse
|
|
17
|
+
# MATCHER: "Read"
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
INPUT=$(cat)
|
|
22
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
23
|
+
[ "$TOOL" = "Read" ] || exit 0
|
|
24
|
+
|
|
25
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
26
|
+
[ -z "$FILE" ] && exit 0
|
|
27
|
+
|
|
28
|
+
TRACK_DIR="${CC_READ_TRACK_DIR:-}"
|
|
29
|
+
[ -n "$TRACK_DIR" ] || exit 0
|
|
30
|
+
|
|
31
|
+
# Check if the read file is in the tracked directory
|
|
32
|
+
case "$FILE" in
|
|
33
|
+
${TRACK_DIR}*)
|
|
34
|
+
# Log the read
|
|
35
|
+
LOG_FILE="/tmp/claude-read-track-${PPID:-0}"
|
|
36
|
+
echo "$FILE" >> "$LOG_FILE"
|
|
37
|
+
|
|
38
|
+
# Count total files in directory vs read files
|
|
39
|
+
TOTAL=$(find "$TRACK_DIR" -type f 2>/dev/null | wc -l)
|
|
40
|
+
READ_COUNT=$(sort -u "$LOG_FILE" 2>/dev/null | wc -l)
|
|
41
|
+
|
|
42
|
+
if [ "$READ_COUNT" -lt "$TOTAL" ]; then
|
|
43
|
+
REMAINING=$((TOTAL - READ_COUNT))
|
|
44
|
+
echo "Progress: $READ_COUNT/$TOTAL files read in $TRACK_DIR ($REMAINING remaining)" >&2
|
|
45
|
+
else
|
|
46
|
+
echo "All $TOTAL files in $TRACK_DIR have been read." >&2
|
|
47
|
+
fi
|
|
48
|
+
;;
|
|
49
|
+
esac
|
|
50
|
+
|
|
51
|
+
exit 0
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
2
4
|
CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
|
|
3
5
|
[ -z "$CONTENT" ] && exit 0
|
|
4
6
|
COMMAND=$(cat | jq -r ".tool_input.command // empty" 2>/dev/null); echo "$COMMAND" | grep -qE "git\s+commit" && [ ! -f "README.md" ] && echo "NOTE: No README.md in project" >&2
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
#
|
|
31
31
|
# Note: This hook checks rm, find -delete, and shred. Do NOT add an "if" field
|
|
32
32
|
# (v2.1.85) because "if" only supports one pattern and would miss the others.
|
|
33
|
+
#
|
|
34
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
33
35
|
|
|
34
36
|
INPUT=$(cat)
|
|
35
37
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# role-tool-guard.sh — Restrict tools based on current agent role
|
|
3
|
+
#
|
|
4
|
+
# Solves: Agent team workflows where PM writes code instead of delegating (#40425).
|
|
5
|
+
# When using structured team roles (PM → Architect → Developer),
|
|
6
|
+
# each role should only access tools appropriate to its function.
|
|
7
|
+
# CLAUDE.md rules are advisory and get ignored under context pressure.
|
|
8
|
+
#
|
|
9
|
+
# How it works: Reads current role from a scope file, then blocks tools
|
|
10
|
+
# that don't match the role's allowed tool set.
|
|
11
|
+
#
|
|
12
|
+
# Setup:
|
|
13
|
+
# echo "pm" > .claude/current-role.txt
|
|
14
|
+
# Roles: pm, architect, developer, reviewer (customizable)
|
|
15
|
+
#
|
|
16
|
+
# TRIGGER: PreToolUse
|
|
17
|
+
# MATCHER: "Bash|Edit|Write|NotebookEdit|Agent"
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
INPUT=$(cat)
|
|
22
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
23
|
+
[ -z "$TOOL" ] && exit 0
|
|
24
|
+
|
|
25
|
+
# Read current role
|
|
26
|
+
ROLE_FILE=".claude/current-role.txt"
|
|
27
|
+
[ -f "$ROLE_FILE" ] || exit 0
|
|
28
|
+
|
|
29
|
+
ROLE=$(head -1 "$ROLE_FILE" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')
|
|
30
|
+
[ -z "$ROLE" ] && exit 0
|
|
31
|
+
|
|
32
|
+
# Define allowed tools per role (customizable via env)
|
|
33
|
+
case "$ROLE" in
|
|
34
|
+
pm|"product-manager"|"project-manager")
|
|
35
|
+
# PM can read, search, and delegate — not write code
|
|
36
|
+
BLOCKED_TOOLS="Edit|Write|Bash|NotebookEdit"
|
|
37
|
+
ROLE_DESC="PM (read/delegate only)"
|
|
38
|
+
;;
|
|
39
|
+
architect|"system-architect")
|
|
40
|
+
# Architect can read and write docs, not execute
|
|
41
|
+
BLOCKED_TOOLS="Bash|NotebookEdit"
|
|
42
|
+
ROLE_DESC="Architect (design only, no execution)"
|
|
43
|
+
;;
|
|
44
|
+
reviewer|"code-reviewer")
|
|
45
|
+
# Reviewer can read and comment, not modify
|
|
46
|
+
BLOCKED_TOOLS="Edit|Write|NotebookEdit"
|
|
47
|
+
ROLE_DESC="Reviewer (read-only)"
|
|
48
|
+
;;
|
|
49
|
+
developer|"dev")
|
|
50
|
+
# Developer has full access
|
|
51
|
+
exit 0
|
|
52
|
+
;;
|
|
53
|
+
*)
|
|
54
|
+
# Unknown role — allow by default
|
|
55
|
+
exit 0
|
|
56
|
+
;;
|
|
57
|
+
esac
|
|
58
|
+
|
|
59
|
+
# Check if current tool is blocked for this role
|
|
60
|
+
if echo "$TOOL" | grep -qE "^($BLOCKED_TOOLS)$"; then
|
|
61
|
+
echo "BLOCKED: Role '$ROLE' cannot use $TOOL." >&2
|
|
62
|
+
echo "Current role: $ROLE_DESC" >&2
|
|
63
|
+
echo "" >&2
|
|
64
|
+
echo "To change role: echo 'developer' > .claude/current-role.txt" >&2
|
|
65
|
+
echo "Or delegate this task to the appropriate agent." >&2
|
|
66
|
+
exit 2
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
exit 0
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# schema-migration-guard.sh — Warn on database schema migrations without backup
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude running destructive schema migrations (DROP COLUMN,
|
|
5
|
+
# ALTER TABLE, DROP INDEX) without first creating a backup or
|
|
6
|
+
# generating a rollback migration.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PreToolUse hook on Bash that detects migration commands
|
|
9
|
+
# (Rails, Django, Prisma, Flyway, Liquibase, raw SQL) and warns if
|
|
10
|
+
# they contain destructive operations.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: PreToolUse
|
|
13
|
+
# MATCHER: "Bash"
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
19
|
+
[ -z "$COMMAND" ] && exit 0
|
|
20
|
+
|
|
21
|
+
# Detect migration commands
|
|
22
|
+
IS_MIGRATION=false
|
|
23
|
+
case "$COMMAND" in
|
|
24
|
+
*"migrate"*|*"migration"*|*"db:migrate"*|*"prisma migrate"*|*"alembic"*|*"flyway"*|*"liquibase"*)
|
|
25
|
+
IS_MIGRATION=true ;;
|
|
26
|
+
esac
|
|
27
|
+
|
|
28
|
+
# Also check for raw SQL with destructive operations
|
|
29
|
+
if echo "$COMMAND" | grep -qEi '(DROP\s+(TABLE|COLUMN|INDEX|DATABASE|SCHEMA)|ALTER\s+TABLE.*DROP|TRUNCATE\s+TABLE|DELETE\s+FROM.*WHERE\s+1)'; then
|
|
30
|
+
IS_MIGRATION=true
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
$IS_MIGRATION || exit 0
|
|
34
|
+
|
|
35
|
+
# Check for destructive patterns
|
|
36
|
+
DESTRUCT=""
|
|
37
|
+
if echo "$COMMAND" | grep -qEi 'DROP\s+(TABLE|COLUMN|INDEX|DATABASE)'; then
|
|
38
|
+
DESTRUCT="${DESTRUCT}DROP operation detected. "
|
|
39
|
+
fi
|
|
40
|
+
if echo "$COMMAND" | grep -qEi 'TRUNCATE'; then
|
|
41
|
+
DESTRUCT="${DESTRUCT}TRUNCATE detected. "
|
|
42
|
+
fi
|
|
43
|
+
if echo "$COMMAND" | grep -qEi -- '--force|--no-check|--skip-validation'; then
|
|
44
|
+
DESTRUCT="${DESTRUCT}Safety bypass flag detected. "
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
if [ -n "$DESTRUCT" ]; then
|
|
48
|
+
echo "WARNING: Destructive database migration detected." >&2
|
|
49
|
+
echo " Command: $COMMAND" >&2
|
|
50
|
+
echo " Issues: $DESTRUCT" >&2
|
|
51
|
+
echo " Before running:" >&2
|
|
52
|
+
echo " 1. Create a database backup" >&2
|
|
53
|
+
echo " 2. Generate a rollback migration" >&2
|
|
54
|
+
echo " 3. Test on staging first" >&2
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
exit 0
|
package/examples/scope-guard.sh
CHANGED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# secret-file-read-guard.sh — Block Read/Grep of files containing secrets
|
|
3
|
+
#
|
|
4
|
+
# Solves: Sensitive data (API keys, credentials, PII) is sent to the
|
|
5
|
+
# LLM provider when Read/Grep tools access configuration files
|
|
6
|
+
# (#39882). This hook blocks reads of known-sensitive files.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PreToolUse hook that checks if Read/Grep targets a
|
|
9
|
+
# file that commonly contains secrets (.env, credentials, keys, etc.)
|
|
10
|
+
# and blocks the operation before file contents enter the context.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: PreToolUse
|
|
13
|
+
# MATCHER: "Read|Grep"
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
# {
|
|
17
|
+
# "hooks": {
|
|
18
|
+
# "PreToolUse": [{
|
|
19
|
+
# "matcher": "Read|Grep",
|
|
20
|
+
# "hooks": [{ "type": "command", "command": "~/.claude/hooks/secret-file-read-guard.sh" }]
|
|
21
|
+
# }]
|
|
22
|
+
# }
|
|
23
|
+
# }
|
|
24
|
+
|
|
25
|
+
INPUT=$(cat)
|
|
26
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
27
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null)
|
|
28
|
+
|
|
29
|
+
[[ "$TOOL" != "Read" && "$TOOL" != "Grep" ]] && exit 0
|
|
30
|
+
[ -z "$FILE" ] && exit 0
|
|
31
|
+
|
|
32
|
+
# Expand ~ to $HOME
|
|
33
|
+
FILE=$(echo "$FILE" | sed "s|^~|$HOME|")
|
|
34
|
+
|
|
35
|
+
# ============================================
|
|
36
|
+
# BLOCKED FILE PATTERNS — customize as needed
|
|
37
|
+
# ============================================
|
|
38
|
+
BLOCKED_PATTERNS=(
|
|
39
|
+
'\.env$'
|
|
40
|
+
'\.env\.'
|
|
41
|
+
'credentials'
|
|
42
|
+
'\.pem$'
|
|
43
|
+
'\.key$'
|
|
44
|
+
'\.p12$'
|
|
45
|
+
'\.jks$'
|
|
46
|
+
'\.keystore$'
|
|
47
|
+
'id_rsa'
|
|
48
|
+
'id_ed25519'
|
|
49
|
+
'\.ssh/config$'
|
|
50
|
+
'\.aws/credentials$'
|
|
51
|
+
'\.aws/config$'
|
|
52
|
+
'\.npmrc$'
|
|
53
|
+
'\.pypirc$'
|
|
54
|
+
'\.docker/config\.json$'
|
|
55
|
+
'\.kube/config$'
|
|
56
|
+
'secrets\.ya?ml$'
|
|
57
|
+
'vault\.ya?ml$'
|
|
58
|
+
'\.htpasswd$'
|
|
59
|
+
'shadow$'
|
|
60
|
+
'master\.key$'
|
|
61
|
+
'service[-_]account.*\.json$'
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
BASENAME=$(basename "$FILE")
|
|
65
|
+
for pattern in "${BLOCKED_PATTERNS[@]}"; do
|
|
66
|
+
if echo "$FILE" | grep -qE "$pattern" || echo "$BASENAME" | grep -qE "$pattern"; then
|
|
67
|
+
echo "BLOCKED: Cannot read file that may contain secrets: $FILE" >&2
|
|
68
|
+
echo "This file matches pattern: $pattern" >&2
|
|
69
|
+
echo "If you need this file's contents, describe what you need and the user can provide it." >&2
|
|
70
|
+
exit 2
|
|
71
|
+
fi
|
|
72
|
+
done
|
|
73
|
+
|
|
74
|
+
exit 0
|