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,54 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# self-modify-bypass-guard.sh — Auto-allow .claude/ writes in bypass mode
|
|
3
|
+
#
|
|
4
|
+
# Solves: Self-modification guard prompts for .claude/ writes even when
|
|
5
|
+
# bypassPermissions is active (#40463). The guard fires before
|
|
6
|
+
# hooks/permissions are evaluated.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PermissionRequest hook that detects .claude/ write prompts
|
|
9
|
+
# and auto-allows them when the project uses bypassPermissions mode.
|
|
10
|
+
# Exempts security-sensitive paths (settings.json, CLAUDE.md).
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: PermissionRequest
|
|
13
|
+
# MATCHER: ""
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
|
|
19
|
+
# Extract the permission request details
|
|
20
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
21
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
22
|
+
|
|
23
|
+
# Only handle Edit/Write to .claude/ paths
|
|
24
|
+
case "$TOOL" in
|
|
25
|
+
Edit|Write) ;;
|
|
26
|
+
*) exit 0 ;;
|
|
27
|
+
esac
|
|
28
|
+
|
|
29
|
+
case "$FILE" in
|
|
30
|
+
.claude/*|*/.claude/*) ;;
|
|
31
|
+
*) exit 0 ;;
|
|
32
|
+
esac
|
|
33
|
+
|
|
34
|
+
# Security-sensitive files: always prompt (don't auto-allow)
|
|
35
|
+
BASENAME=$(basename "$FILE")
|
|
36
|
+
case "$BASENAME" in
|
|
37
|
+
settings.json|settings.local.json|CLAUDE.md)
|
|
38
|
+
# Let the default prompt handle these
|
|
39
|
+
exit 0 ;;
|
|
40
|
+
esac
|
|
41
|
+
|
|
42
|
+
# Check if bypassPermissions is configured
|
|
43
|
+
SETTINGS=".claude/settings.json"
|
|
44
|
+
if [ -f "$SETTINGS" ]; then
|
|
45
|
+
MODE=$(jq -r '.defaultMode // empty' "$SETTINGS" 2>/dev/null)
|
|
46
|
+
if [ "$MODE" = "bypassPermissions" ]; then
|
|
47
|
+
# Auto-allow: output hookSpecificOutput to approve
|
|
48
|
+
echo '{"hookSpecificOutput":{"hookEventName":"PermissionRequest","allow":true}}'
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Not in bypass mode — let default prompt handle it
|
|
54
|
+
exit 0
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# session-end-logger.sh — Log session activity at exit
|
|
3
|
+
#
|
|
4
|
+
# Solves: No built-in session audit trail. When a session ends,
|
|
5
|
+
# there's no record of what was done unless you manually check
|
|
6
|
+
# git log or conversation history (#40010).
|
|
7
|
+
#
|
|
8
|
+
# How it works: SessionEnd hook that captures recent git commits,
|
|
9
|
+
# modified files, and session metadata into a structured log file.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: SessionEnd
|
|
12
|
+
# MATCHER: ""
|
|
13
|
+
#
|
|
14
|
+
# Usage:
|
|
15
|
+
# {
|
|
16
|
+
# "hooks": {
|
|
17
|
+
# "SessionEnd": [{
|
|
18
|
+
# "hooks": [{ "type": "command", "command": "~/.claude/hooks/session-end-logger.sh" }]
|
|
19
|
+
# }]
|
|
20
|
+
# }
|
|
21
|
+
# }
|
|
22
|
+
|
|
23
|
+
INPUT=$(cat)
|
|
24
|
+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"' 2>/dev/null)
|
|
25
|
+
|
|
26
|
+
LOG_DIR=".claude/session-logs"
|
|
27
|
+
mkdir -p "$LOG_DIR"
|
|
28
|
+
LOG_FILE="$LOG_DIR/$(date '+%Y-%m-%d').md"
|
|
29
|
+
|
|
30
|
+
{
|
|
31
|
+
echo ""
|
|
32
|
+
echo "## Session $SESSION_ID — $(date '+%H:%M')"
|
|
33
|
+
echo ""
|
|
34
|
+
|
|
35
|
+
# Recent git activity
|
|
36
|
+
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
37
|
+
COMMITS=$(git log --oneline --since="1 hour ago" 2>/dev/null)
|
|
38
|
+
if [ -n "$COMMITS" ]; then
|
|
39
|
+
echo "### Commits"
|
|
40
|
+
echo '```'
|
|
41
|
+
echo "$COMMITS"
|
|
42
|
+
echo '```'
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
CHANGED=$(git diff --name-only HEAD~5..HEAD 2>/dev/null | head -20)
|
|
46
|
+
if [ -n "$CHANGED" ]; then
|
|
47
|
+
echo "### Changed files"
|
|
48
|
+
echo '```'
|
|
49
|
+
echo "$CHANGED"
|
|
50
|
+
echo '```'
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
echo ""
|
|
55
|
+
} >> "$LOG_FILE"
|
|
56
|
+
|
|
57
|
+
exit 0
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# session-error-rate-monitor.sh — Detect session quality degradation
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Long sessions (6+ hours) show quality decay: more errors,
|
|
7
|
+
# ignored instructions, and destructive actions. This hook
|
|
8
|
+
# tracks the error rate over a rolling window and warns when
|
|
9
|
+
# it exceeds a threshold, suggesting a session restart.
|
|
10
|
+
#
|
|
11
|
+
# How it works:
|
|
12
|
+
# - Counts tool calls and errors (exit code != 0) in a state file
|
|
13
|
+
# - Calculates error rate over last N tool calls
|
|
14
|
+
# - Warns (stderr) when error rate exceeds threshold
|
|
15
|
+
# - Does NOT block — purely advisory (exit 0 always)
|
|
16
|
+
#
|
|
17
|
+
# TRIGGER: PostToolUse
|
|
18
|
+
# MATCHER: "Bash"
|
|
19
|
+
#
|
|
20
|
+
# CONFIG:
|
|
21
|
+
# CC_ERROR_RATE_WINDOW=20 (rolling window size)
|
|
22
|
+
# CC_ERROR_RATE_THRESHOLD=40 (% error rate to trigger warning)
|
|
23
|
+
#
|
|
24
|
+
# See: https://github.com/anthropics/claude-code/issues/32963
|
|
25
|
+
# ================================================================
|
|
26
|
+
|
|
27
|
+
INPUT=$(cat)
|
|
28
|
+
EXIT_CODE=$(echo "$INPUT" | jq -r '.tool_result.exit_code // "0"' 2>/dev/null)
|
|
29
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
30
|
+
|
|
31
|
+
# Only track Bash tool calls
|
|
32
|
+
[[ "$TOOL" != "Bash" ]] && exit 0
|
|
33
|
+
|
|
34
|
+
WINDOW=${CC_ERROR_RATE_WINDOW:-20}
|
|
35
|
+
THRESHOLD=${CC_ERROR_RATE_THRESHOLD:-40}
|
|
36
|
+
STATE_DIR="${HOME}/.claude/state"
|
|
37
|
+
mkdir -p "$STATE_DIR"
|
|
38
|
+
STATE_FILE="$STATE_DIR/error-rate-history.log"
|
|
39
|
+
|
|
40
|
+
# Record result: 0=success, 1=error
|
|
41
|
+
if [ "$EXIT_CODE" = "0" ]; then
|
|
42
|
+
echo "0" >> "$STATE_FILE"
|
|
43
|
+
else
|
|
44
|
+
echo "1" >> "$STATE_FILE"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Keep only last N entries
|
|
48
|
+
TOTAL=$(wc -l < "$STATE_FILE" 2>/dev/null || echo "0")
|
|
49
|
+
if [ "$TOTAL" -gt "$WINDOW" ]; then
|
|
50
|
+
tail -n "$WINDOW" "$STATE_FILE" > "$STATE_FILE.tmp"
|
|
51
|
+
mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# Calculate error rate
|
|
55
|
+
if [ "$TOTAL" -ge "$WINDOW" ]; then
|
|
56
|
+
ERRORS=$(tail -n "$WINDOW" "$STATE_FILE" | grep -c "^1$" || echo "0")
|
|
57
|
+
RATE=$(( ERRORS * 100 / WINDOW ))
|
|
58
|
+
|
|
59
|
+
if [ "$RATE" -ge "$THRESHOLD" ]; then
|
|
60
|
+
echo "⚠ Session quality alert: ${RATE}% error rate over last ${WINDOW} commands (threshold: ${THRESHOLD}%)" >&2
|
|
61
|
+
echo " Consider: /compact or starting a fresh session" >&2
|
|
62
|
+
fi
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
exit 0
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# session-health-monitor.sh — Monitor session health metrics
|
|
3
|
+
#
|
|
4
|
+
# Solves: Long-running sessions degrade silently — context fills up,
|
|
5
|
+
# tool calls slow down, errors accumulate. No visibility into
|
|
6
|
+
# session health until it's too late.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PreToolUse hook that tracks session metrics:
|
|
9
|
+
# - Tool call count (proxy for context usage)
|
|
10
|
+
# - Error rate (consecutive failures)
|
|
11
|
+
# - Session duration
|
|
12
|
+
# Warns at configurable thresholds.
|
|
13
|
+
#
|
|
14
|
+
# TRIGGER: PreToolUse
|
|
15
|
+
# MATCHER: ""
|
|
16
|
+
#
|
|
17
|
+
# Usage:
|
|
18
|
+
# {
|
|
19
|
+
# "hooks": {
|
|
20
|
+
# "PreToolUse": [{
|
|
21
|
+
# "hooks": [{ "type": "command", "command": "~/.claude/hooks/session-health-monitor.sh" }]
|
|
22
|
+
# }]
|
|
23
|
+
# }
|
|
24
|
+
# }
|
|
25
|
+
|
|
26
|
+
STATE="/tmp/cc-health-$$"
|
|
27
|
+
|
|
28
|
+
# Initialize on first call
|
|
29
|
+
if [ ! -f "$STATE" ]; then
|
|
30
|
+
echo "start=$(date +%s) calls=0 errors=0" > "$STATE"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Read current state
|
|
34
|
+
eval $(cat "$STATE")
|
|
35
|
+
calls=$((calls + 1))
|
|
36
|
+
|
|
37
|
+
# Update state
|
|
38
|
+
echo "start=$start calls=$calls errors=$errors" > "$STATE"
|
|
39
|
+
|
|
40
|
+
# Check thresholds
|
|
41
|
+
DURATION=$(( ($(date +%s) - start) / 60 ))
|
|
42
|
+
|
|
43
|
+
if [ "$calls" -eq 50 ]; then
|
|
44
|
+
echo "ℹ Session health: 50 tool calls, ${DURATION}m elapsed." >&2
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
if [ "$calls" -eq 150 ]; then
|
|
48
|
+
echo "⚠ Session health: 150 tool calls, ${DURATION}m elapsed. Context may be getting full." >&2
|
|
49
|
+
echo "Consider saving state and starting a fresh session." >&2
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
if [ "$calls" -ge 250 ] && [ $((calls % 50)) -eq 0 ]; then
|
|
53
|
+
echo "🔴 Session health: $calls tool calls, ${DURATION}m elapsed. Performance may be degraded." >&2
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Duration warning
|
|
57
|
+
if [ "$DURATION" -ge 120 ] && [ $((calls % 100)) -eq 0 ]; then
|
|
58
|
+
echo "⏰ Session running for ${DURATION}m. Long sessions accumulate context drift." >&2
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
exit 0
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
MAX_RSS_MB="${CC_MAX_RSS_MB:-4096}"
|
|
2
|
+
CHECK_INTERVAL=300
|
|
3
|
+
(
|
|
4
|
+
while true; do
|
|
5
|
+
sleep "$CHECK_INTERVAL"
|
|
6
|
+
pgrep -f "claude" | while read pid; do
|
|
7
|
+
RSS_KB=$(ps -o rss= -p "$pid" 2>/dev/null | tr -d ' ')
|
|
8
|
+
[ -z "$RSS_KB" ] && continue
|
|
9
|
+
RSS_MB=$((RSS_KB / 1024))
|
|
10
|
+
if [ "$RSS_MB" -gt "$MAX_RSS_MB" ]; then
|
|
11
|
+
echo "[$(date -Iseconds)] PID $pid: ${RSS_MB}MB > ${MAX_RSS_MB}MB limit" >> ~/.claude/memory-watchdog.log
|
|
12
|
+
kill -15 "$pid" 2>/dev/null
|
|
13
|
+
fi
|
|
14
|
+
done
|
|
15
|
+
done
|
|
16
|
+
) &
|
|
17
|
+
exit 0
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# session-permission-reset-guard.sh — Override session-cached permissions
|
|
3
|
+
#
|
|
4
|
+
# Solves: Sandbox mode caching session-level permissions (#40384).
|
|
5
|
+
# After approving a command once, sandbox caches the approval
|
|
6
|
+
# for the entire session, bypassing the allow list.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PreToolUse hook that enforces per-invocation checks
|
|
9
|
+
# for specified commands, regardless of session cache state.
|
|
10
|
+
# Acts as a secondary permission layer outside the caching system.
|
|
11
|
+
#
|
|
12
|
+
# CONFIG:
|
|
13
|
+
# CC_ALWAYS_CHECK_COMMANDS="git commit:git push:npm publish:cargo install"
|
|
14
|
+
#
|
|
15
|
+
# TRIGGER: PreToolUse
|
|
16
|
+
# MATCHER: "Bash"
|
|
17
|
+
|
|
18
|
+
INPUT=$(cat)
|
|
19
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
20
|
+
[ -z "$COMMAND" ] && exit 0
|
|
21
|
+
|
|
22
|
+
# Commands that should always require hook-level check
|
|
23
|
+
ALWAYS_CHECK="${CC_ALWAYS_CHECK_COMMANDS:-git commit:git push:npm publish:cargo install:pip install}"
|
|
24
|
+
|
|
25
|
+
IFS=':' read -ra PATTERNS <<< "$ALWAYS_CHECK"
|
|
26
|
+
for pattern in "${PATTERNS[@]}"; do
|
|
27
|
+
# Match the pattern anywhere in the command (including chained commands)
|
|
28
|
+
if echo "$COMMAND" | grep -qiF "$pattern"; then
|
|
29
|
+
echo "HOOK CHECK: '$pattern' detected — hook-level review." >&2
|
|
30
|
+
echo "Command: $COMMAND" >&2
|
|
31
|
+
echo "" >&2
|
|
32
|
+
echo "This hook enforces per-invocation checks regardless of" >&2
|
|
33
|
+
echo "session permission caching. To allow, remove this pattern" >&2
|
|
34
|
+
echo "from CC_ALWAYS_CHECK_COMMANDS." >&2
|
|
35
|
+
exit 2
|
|
36
|
+
fi
|
|
37
|
+
done
|
|
38
|
+
|
|
39
|
+
exit 0
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# session-resume-env-fix.sh — Fix CLAUDE_ENV_FILE path on session resume
|
|
3
|
+
#
|
|
4
|
+
# Solves: CLAUDE_ENV_FILE points to startup session directory, but Bash
|
|
5
|
+
# tool loads from resumed session directory (#40391, #24775).
|
|
6
|
+
# Environment variables written by SessionStart hooks are lost.
|
|
7
|
+
#
|
|
8
|
+
# How it works: SessionStart hook that detects resume (source="resume")
|
|
9
|
+
# and copies/symlinks env files from the startup session directory
|
|
10
|
+
# to the resumed session directory.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: SessionStart
|
|
13
|
+
# MATCHER: ""
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
SOURCE=$(echo "$INPUT" | jq -r '.source // empty' 2>/dev/null)
|
|
19
|
+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
|
|
20
|
+
|
|
21
|
+
# Only act on resume events
|
|
22
|
+
[ "$SOURCE" = "resume" ] || exit 0
|
|
23
|
+
[ -n "$SESSION_ID" ] || exit 0
|
|
24
|
+
[ -n "${CLAUDE_ENV_FILE:-}" ] || exit 0
|
|
25
|
+
|
|
26
|
+
# Extract the startup session directory from CLAUDE_ENV_FILE
|
|
27
|
+
STARTUP_DIR=$(dirname "$CLAUDE_ENV_FILE")
|
|
28
|
+
ENV_BASE=$(dirname "$STARTUP_DIR")
|
|
29
|
+
|
|
30
|
+
# Construct the resumed session directory
|
|
31
|
+
RESUME_DIR="${ENV_BASE}/${SESSION_ID}"
|
|
32
|
+
|
|
33
|
+
# If they're the same, no fix needed
|
|
34
|
+
[ "$STARTUP_DIR" != "$RESUME_DIR" ] || exit 0
|
|
35
|
+
|
|
36
|
+
# Create resumed session directory if missing
|
|
37
|
+
mkdir -p "$RESUME_DIR"
|
|
38
|
+
|
|
39
|
+
# Copy all env files from startup dir to resumed dir
|
|
40
|
+
for envfile in "$STARTUP_DIR"/*.sh; do
|
|
41
|
+
[ -f "$envfile" ] || continue
|
|
42
|
+
BASENAME=$(basename "$envfile")
|
|
43
|
+
if [ ! -f "$RESUME_DIR/$BASENAME" ]; then
|
|
44
|
+
cp "$envfile" "$RESUME_DIR/$BASENAME"
|
|
45
|
+
echo "Copied env file to resumed session: $BASENAME" >&2
|
|
46
|
+
fi
|
|
47
|
+
done
|
|
48
|
+
|
|
49
|
+
exit 0
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
# CC_TOOL_WARN_100 — threshold 1 (default: 100)
|
|
25
25
|
# CC_TOOL_WARN_200 — threshold 2 (default: 200)
|
|
26
26
|
# CC_TOOL_WARN_500 — threshold 3 (default: 500)
|
|
27
|
+
#
|
|
28
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
27
29
|
|
|
28
30
|
INPUT=$(cat)
|
|
29
31
|
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
# This hook unwraps interpreter one-liners and checks the inner command.
|
|
14
14
|
#
|
|
15
15
|
# Usage: PreToolUse hook on "Bash"
|
|
16
|
+
#
|
|
17
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
16
18
|
|
|
17
19
|
INPUT=$(cat)
|
|
18
20
|
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
package/examples/skill-gate.sh
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# skill-injection-detector.sh — Detect silently injected skills/plugins
|
|
3
|
+
#
|
|
4
|
+
# Solves: Skills and plugins from claude.ai silently injected into Claude Code
|
|
5
|
+
# sessions (#39686). External tool definitions bloat context and
|
|
6
|
+
# can override local behavior without user awareness.
|
|
7
|
+
#
|
|
8
|
+
# How it works: Notification hook that monitors for unexpected skill/plugin
|
|
9
|
+
# loading messages. Warns when tools are loaded from external sources.
|
|
10
|
+
# Also checks MCP config for unexpected servers.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: PreToolUse
|
|
13
|
+
# MATCHER: ""
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
19
|
+
|
|
20
|
+
# Check MCP tool calls for unexpected servers
|
|
21
|
+
if [ "$TOOL" = "Bash" ]; then
|
|
22
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
23
|
+
# Detect MCP server addition
|
|
24
|
+
if echo "$COMMAND" | grep -qE 'claude\s+mcp\s+add'; then
|
|
25
|
+
echo "WARNING: MCP server addition detected." >&2
|
|
26
|
+
echo "Command: $COMMAND" >&2
|
|
27
|
+
echo "Verify this server is expected before proceeding." >&2
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Check for skill/plugin invocation from unexpected sources
|
|
32
|
+
if echo "$INPUT" | jq -e '.tool_input.skill // empty' 2>/dev/null | grep -qv '^$'; then
|
|
33
|
+
SKILL=$(echo "$INPUT" | jq -r '.tool_input.skill' 2>/dev/null)
|
|
34
|
+
# Check if skill is local
|
|
35
|
+
if [ ! -f ".claude/skills/${SKILL}/SKILL.md" ] && [ ! -f ".claude/skills/${SKILL}.md" ]; then
|
|
36
|
+
echo "WARNING: Skill '$SKILL' invoked but not found locally." >&2
|
|
37
|
+
echo "This may be an externally injected skill." >&2
|
|
38
|
+
fi
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
exit 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,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
|