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
|
+
# edit-counter-test-gate.sh — Require testing after N consecutive edits
|
|
3
|
+
#
|
|
4
|
+
# Solves: Reactive cycling through fixes without testing (#40401).
|
|
5
|
+
# Opus writing 4 different fix approaches in sequence without
|
|
6
|
+
# verifying any of them actually work.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PostToolUse hook on Edit that counts consecutive edits.
|
|
9
|
+
# After CC_MAX_EDITS_BEFORE_TEST (default 3) edits without a Bash
|
|
10
|
+
# command (assumed test/build), warns the model to test first.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: PostToolUse
|
|
13
|
+
# MATCHER: "Edit|Bash"
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
19
|
+
MAX_EDITS="${CC_MAX_EDITS_BEFORE_TEST:-3}"
|
|
20
|
+
COUNTER_FILE="/tmp/claude-edit-test-gate-${PPID:-0}"
|
|
21
|
+
|
|
22
|
+
case "$TOOL" in
|
|
23
|
+
Edit|Write)
|
|
24
|
+
# Increment edit counter
|
|
25
|
+
COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo 0)
|
|
26
|
+
COUNT=$((COUNT + 1))
|
|
27
|
+
echo "$COUNT" > "$COUNTER_FILE"
|
|
28
|
+
|
|
29
|
+
if [ "$COUNT" -ge "$MAX_EDITS" ]; then
|
|
30
|
+
echo "WARNING: $COUNT consecutive edits without testing." >&2
|
|
31
|
+
echo "" >&2
|
|
32
|
+
echo "Run your test/build command before making more changes." >&2
|
|
33
|
+
echo "Untested fixes compound — verify each approach works" >&2
|
|
34
|
+
echo "before trying the next one." >&2
|
|
35
|
+
# Warning only — change to exit 2 to block
|
|
36
|
+
fi
|
|
37
|
+
;;
|
|
38
|
+
Bash)
|
|
39
|
+
# Bash command (likely test/build) — reset counter
|
|
40
|
+
echo "0" > "$COUNTER_FILE"
|
|
41
|
+
;;
|
|
42
|
+
esac
|
|
43
|
+
|
|
44
|
+
exit 0
|
package/examples/edit-guard.sh
CHANGED
package/examples/edit-verify.sh
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
# Usage: Add to settings.json as a PostToolUse hook on "Edit|Write"
|
|
9
9
|
#
|
|
10
10
|
# Customize TEST_PATTERN for your project's test file naming convention.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
11
13
|
|
|
12
14
|
INPUT=$(cat)
|
|
13
15
|
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# env-inline-secret-guard.sh — Block .env values from appearing in commands
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude reading .env and hardcoding secrets into inline scripts (#24185).
|
|
5
|
+
# API keys, database URLs, and tokens get embedded in bash commands,
|
|
6
|
+
# potentially leaking to logs, history, or screen recordings.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PreToolUse hook on Bash that detects common secret patterns
|
|
9
|
+
# (API keys, tokens, connection strings) in command text.
|
|
10
|
+
# If found, blocks with exit 2 and suggests environment variable usage.
|
|
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 inline secrets: known API key prefixes (20+ chars)
|
|
22
|
+
# sk- (OpenAI), ghp_/ghu_ (GitHub), AKIA (AWS)
|
|
23
|
+
if echo "$COMMAND" | grep -qE '(sk-[a-zA-Z0-9]{20,}|ghp_[a-zA-Z0-9]{36}|ghu_[a-zA-Z0-9]{36}|AKIA[0-9A-Z]{16}|xoxb-[0-9]+-[0-9]+-[a-zA-Z0-9]+)'; then
|
|
24
|
+
echo "BLOCKED: Possible secret/credential detected in command." >&2
|
|
25
|
+
echo "Use environment variables instead of inline secrets." >&2
|
|
26
|
+
exit 2
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Detect generic long tokens in auth headers or parameters
|
|
30
|
+
if echo "$COMMAND" | grep -qE "(Authorization:|Bearer |token=|api[_-]?key=|secret=|password=)['\"]?[a-zA-Z0-9+/=_-]{32,}"; then
|
|
31
|
+
echo "BLOCKED: Long token/key detected inline in command." >&2
|
|
32
|
+
echo "Use environment variables or a secrets file instead." >&2
|
|
33
|
+
exit 2
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
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
|
if echo "$COMMAND" | grep -qiE "(NODE_ENV|RAILS_ENV|FLASK_ENV)=production"; then echo "WARNING: Production env detected in command" >&2; fi
|
|
@@ -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 "process\.env\.\w+\s*\|\|" || echo "$CONTENT" | grep -qE "process\.env\.\w+!" && echo "NOTE: Env var without default — add fallback" >&2
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# export-overwrite-guard.sh — Prevent /export from overwriting existing files
|
|
3
|
+
#
|
|
4
|
+
# Solves: /export command overwrites files without warning (#37595).
|
|
5
|
+
# Users lose existing files when Claude exports to the same path.
|
|
6
|
+
#
|
|
7
|
+
# How it works: PreToolUse hook on Write that checks if the target file
|
|
8
|
+
# exists and contains content. If so, warns before allowing overwrite.
|
|
9
|
+
#
|
|
10
|
+
# TRIGGER: PreToolUse
|
|
11
|
+
# MATCHER: "Write"
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
INPUT=$(cat)
|
|
16
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
17
|
+
[ -z "$FILE" ] && exit 0
|
|
18
|
+
|
|
19
|
+
# Check if file exists and has content
|
|
20
|
+
if [ -f "$FILE" ] && [ -s "$FILE" ]; then
|
|
21
|
+
SIZE=$(wc -c < "$FILE" 2>/dev/null || echo 0)
|
|
22
|
+
if [ "$SIZE" -gt 100 ]; then
|
|
23
|
+
echo "WARNING: Overwriting existing file '$FILE' ($SIZE bytes)." >&2
|
|
24
|
+
echo "Consider writing to a different path or backing up first." >&2
|
|
25
|
+
# Don't block — just warn via stderr
|
|
26
|
+
fi
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
exit 0
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# file-change-undo-tracker.sh — Track file changes for easy undo
|
|
3
|
+
#
|
|
4
|
+
# Solves: After Claude makes unwanted changes across multiple files,
|
|
5
|
+
# users have no easy way to identify and revert all affected files.
|
|
6
|
+
# git diff works but only for tracked files.
|
|
7
|
+
#
|
|
8
|
+
# How it works: FileChanged hook that logs every file modification
|
|
9
|
+
# with timestamp and change type. Creates a revert script
|
|
10
|
+
# that can undo all changes from the current session.
|
|
11
|
+
#
|
|
12
|
+
# Usage: After session, run: bash /tmp/claude-undo-session.sh
|
|
13
|
+
#
|
|
14
|
+
# TRIGGER: FileChanged
|
|
15
|
+
# MATCHER: ""
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
INPUT=$(cat)
|
|
20
|
+
FILE=$(echo "$INPUT" | jq -r '.file // empty' 2>/dev/null)
|
|
21
|
+
EVENT=$(echo "$INPUT" | jq -r '.event // empty' 2>/dev/null)
|
|
22
|
+
|
|
23
|
+
[ -z "$FILE" ] && exit 0
|
|
24
|
+
|
|
25
|
+
LOG_FILE="/tmp/claude-file-changes-${PPID:-0}.log"
|
|
26
|
+
UNDO_FILE="/tmp/claude-undo-session-${PPID:-0}.sh"
|
|
27
|
+
TIMESTAMP=$(date +"%H:%M:%S")
|
|
28
|
+
|
|
29
|
+
# Log the change
|
|
30
|
+
echo "${TIMESTAMP} ${EVENT:-modified} ${FILE}" >> "$LOG_FILE"
|
|
31
|
+
|
|
32
|
+
# Track for undo (git-tracked files only)
|
|
33
|
+
if git ls-files --error-unmatch "$FILE" &>/dev/null 2>&1; then
|
|
34
|
+
# File is git-tracked — can be reverted with git checkout
|
|
35
|
+
if ! grep -qF "git checkout -- \"$FILE\"" "$UNDO_FILE" 2>/dev/null; then
|
|
36
|
+
echo "git checkout -- \"$FILE\" # ${EVENT:-modified} at ${TIMESTAMP}" >> "$UNDO_FILE"
|
|
37
|
+
fi
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# Count changes
|
|
41
|
+
COUNT=$(wc -l < "$LOG_FILE" 2>/dev/null || echo 0)
|
|
42
|
+
if [ "$((COUNT % 10))" -eq 0 ] && [ "$COUNT" -gt 0 ]; then
|
|
43
|
+
echo "Session file changes: $COUNT files modified. Undo: bash $UNDO_FILE" >&2
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
exit 0
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# file-recycle-bin.sh — Move deleted files to recycle bin instead of permanent deletion
|
|
3
|
+
#
|
|
4
|
+
# Solves: No undo for file operations during Claude Code sessions (#39949).
|
|
5
|
+
# When Claude deletes or overwrites files, they're gone permanently
|
|
6
|
+
# unless git tracked. This hook intercepts rm commands and moves
|
|
7
|
+
# files to a session recycle bin.
|
|
8
|
+
#
|
|
9
|
+
# How it works: PreToolUse hook on Bash that intercepts rm commands,
|
|
10
|
+
# copies target files to .claude/recycle-bin/ before allowing deletion.
|
|
11
|
+
# Restore with: cp .claude/recycle-bin/<file> <original-path>
|
|
12
|
+
#
|
|
13
|
+
# TRIGGER: PreToolUse
|
|
14
|
+
# MATCHER: "Bash"
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
INPUT=$(cat)
|
|
19
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
20
|
+
[ -z "$COMMAND" ] && exit 0
|
|
21
|
+
|
|
22
|
+
# Only intercept rm commands (not rm -rf which destructive-guard handles)
|
|
23
|
+
if ! echo "$COMMAND" | grep -qE '^\s*rm\s'; then
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Skip if destructive-guard would catch it (rm -rf /, rm -rf ~, etc.)
|
|
28
|
+
if echo "$COMMAND" | grep -qE 'rm\s+(-[rf]+\s+)*(/|~|\$HOME)'; then
|
|
29
|
+
exit 0 # Let destructive-guard handle these
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Extract file paths from rm command (simple extraction)
|
|
33
|
+
FILES=$(echo "$COMMAND" | sed 's/^[[:space:]]*rm[[:space:]]*//' | sed 's/-[rfiv]*//g' | tr ' ' '\n' | grep -v '^$' | grep -v '^-')
|
|
34
|
+
|
|
35
|
+
BIN_DIR=".claude/recycle-bin"
|
|
36
|
+
mkdir -p "$BIN_DIR"
|
|
37
|
+
|
|
38
|
+
for FILE in $FILES; do
|
|
39
|
+
if [ -f "$FILE" ]; then
|
|
40
|
+
BASENAME=$(basename "$FILE")
|
|
41
|
+
TIMESTAMP=$(date +%H%M%S)
|
|
42
|
+
cp "$FILE" "$BIN_DIR/${TIMESTAMP}-${BASENAME}" 2>/dev/null || true
|
|
43
|
+
echo "Backed up: $FILE -> $BIN_DIR/${TIMESTAMP}-${BASENAME}" >&2
|
|
44
|
+
fi
|
|
45
|
+
done
|
|
46
|
+
|
|
47
|
+
# Allow the rm to proceed (files are backed up)
|
|
48
|
+
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
|
if echo "$COMMAND" | grep -qE "git\s+(commit|push|merge).*--no-verify"; then echo "WARNING: --no-verify bypasses git hooks" >&2; fi
|
|
@@ -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 "git\s+commit\s+-m" || exit 0; MSG=$(echo "$COMMAND" | grep -oP "(?<=-m\s[\x27\x22])[^\x27\x22]+"); [ ${#MSG} -lt 10 ] && echo "WARNING: Commit message too short (${#MSG} chars)" >&2
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# git-show-flag-sanitizer.sh — Strip invalid --no-stat from git show
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Claude Code frequently runs `git show <ref> --no-stat`, but --no-stat
|
|
7
|
+
# is not a valid git-show flag. The command fails with exit code 128,
|
|
8
|
+
# wasting context on error output and retries.
|
|
9
|
+
# This hook silently rewrites the command to remove --no-stat.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PreToolUse
|
|
12
|
+
# MATCHER: "Bash"
|
|
13
|
+
#
|
|
14
|
+
# See: https://github.com/anthropics/claude-code/issues/13071
|
|
15
|
+
# ================================================================
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
19
|
+
[ -z "$COMMAND" ] && exit 0
|
|
20
|
+
|
|
21
|
+
# Only act on git show commands containing --no-stat
|
|
22
|
+
case "$COMMAND" in
|
|
23
|
+
*git\ show*--no-stat*|*git\ show*--no-stat*) ;;
|
|
24
|
+
*) exit 0 ;;
|
|
25
|
+
esac
|
|
26
|
+
|
|
27
|
+
# Remove --no-stat flag (handles multiple spaces)
|
|
28
|
+
NEW_COMMAND=$(echo "$COMMAND" | sed 's/ --no-stat//')
|
|
29
|
+
|
|
30
|
+
# Collapse any double spaces left behind
|
|
31
|
+
NEW_COMMAND=$(echo "$NEW_COMMAND" | sed 's/ */ /g')
|
|
32
|
+
|
|
33
|
+
jq -n --arg cmd "$NEW_COMMAND" '{
|
|
34
|
+
hookSpecificOutput: {
|
|
35
|
+
hookEventName: "PreToolUse",
|
|
36
|
+
permissionDecision: "allow",
|
|
37
|
+
updatedInput: { command: $cmd }
|
|
38
|
+
}
|
|
39
|
+
}'
|
|
40
|
+
|
|
41
|
+
exit 0
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# github-actions-secret-guard.sh — Prevent hardcoded secrets in GitHub Actions workflows
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude writes API keys, tokens, or passwords directly into
|
|
5
|
+
# .github/workflows/*.yml files instead of using ${{ secrets.NAME }}.
|
|
6
|
+
# These get committed and pushed to public/private repos.
|
|
7
|
+
#
|
|
8
|
+
# How it works: PostToolUse hook on Edit/Write that checks if the target
|
|
9
|
+
# is a GitHub Actions workflow file, then scans for patterns that look
|
|
10
|
+
# like hardcoded secrets instead of ${{ secrets.* }} references.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: PostToolUse
|
|
13
|
+
# MATCHER: "Edit|Write"
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
19
|
+
case "$TOOL" in Edit|Write) ;; *) exit 0 ;; esac
|
|
20
|
+
|
|
21
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
22
|
+
[ -z "$FILE" ] && exit 0
|
|
23
|
+
|
|
24
|
+
# Only check GitHub Actions workflow files
|
|
25
|
+
case "$FILE" in
|
|
26
|
+
.github/workflows/*.yml|.github/workflows/*.yaml) ;;
|
|
27
|
+
*/.github/workflows/*.yml|*/.github/workflows/*.yaml) ;;
|
|
28
|
+
*) exit 0 ;;
|
|
29
|
+
esac
|
|
30
|
+
|
|
31
|
+
[ -f "$FILE" ] || exit 0
|
|
32
|
+
|
|
33
|
+
# Check for hardcoded secret patterns (not using ${{ secrets.* }})
|
|
34
|
+
ISSUES=""
|
|
35
|
+
|
|
36
|
+
# API keys / tokens in env or with: blocks
|
|
37
|
+
if grep -nE '(APIKEY|API_KEY|TOKEN|SECRET|PASSWORD|PRIVATE_KEY)\s*[:=]\s*["\x27]?[A-Za-z0-9+/=_-]{20,}' "$FILE" 2>/dev/null | grep -v '\${{' | head -3 | grep -q .; then
|
|
38
|
+
ISSUES="${ISSUES}Hardcoded API key/token found (use \${{ secrets.NAME }} instead)\n"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Bearer tokens
|
|
42
|
+
if grep -nE 'Bearer\s+[A-Za-z0-9._-]{20,}' "$FILE" 2>/dev/null | grep -v '\${{' | head -1 | grep -q .; then
|
|
43
|
+
ISSUES="${ISSUES}Hardcoded Bearer token found\n"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# AWS credentials
|
|
47
|
+
if grep -nE 'AKIA[0-9A-Z]{16}' "$FILE" 2>/dev/null | head -1 | grep -q .; then
|
|
48
|
+
ISSUES="${ISSUES}AWS access key ID found in workflow\n"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
if [ -n "$ISSUES" ]; then
|
|
52
|
+
echo "WARNING: Potential secrets in GitHub Actions workflow:" >&2
|
|
53
|
+
echo " File: $FILE" >&2
|
|
54
|
+
echo -e " $ISSUES" >&2
|
|
55
|
+
echo " Use \${{ secrets.NAME }} instead of hardcoded values." >&2
|
|
56
|
+
echo " Add secrets via: gh secret set NAME --body 'value'" >&2
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
exit 0
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# gitops-drift-guard.sh — Warn when editing infrastructure files without PR
|
|
3
|
+
#
|
|
4
|
+
# Solves: Claude directly modifying Terraform, Kubernetes manifests, or
|
|
5
|
+
# Helm values on the default branch instead of creating a PR.
|
|
6
|
+
# In GitOps workflows, direct commits to main trigger immediate
|
|
7
|
+
# infrastructure changes.
|
|
8
|
+
#
|
|
9
|
+
# How it works: PreToolUse hook on Edit/Write that checks if the target
|
|
10
|
+
# file is an infrastructure file AND the current branch is main/master.
|
|
11
|
+
# Warns that changes should go through a PR for review.
|
|
12
|
+
#
|
|
13
|
+
# TRIGGER: PreToolUse
|
|
14
|
+
# MATCHER: "Edit|Write"
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
INPUT=$(cat)
|
|
19
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
20
|
+
case "$TOOL" in Edit|Write) ;; *) exit 0 ;; esac
|
|
21
|
+
|
|
22
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
23
|
+
[ -z "$FILE" ] && exit 0
|
|
24
|
+
|
|
25
|
+
# Check if this is an infrastructure file
|
|
26
|
+
IS_INFRA=false
|
|
27
|
+
case "$FILE" in
|
|
28
|
+
*.tf|*.tfvars) IS_INFRA=true ;; # Terraform
|
|
29
|
+
*/k8s/*.yaml|*/k8s/*.yml) IS_INFRA=true ;; # Kubernetes manifests
|
|
30
|
+
*/helm/**/values*.yaml) IS_INFRA=true ;; # Helm values
|
|
31
|
+
*/charts/**/*.yaml) IS_INFRA=true ;; # Helm charts
|
|
32
|
+
.github/workflows/*.yml) IS_INFRA=true ;; # CI/CD workflows
|
|
33
|
+
Dockerfile*|docker-compose*.yml) IS_INFRA=true ;; # Docker
|
|
34
|
+
*/argocd/*.yaml) IS_INFRA=true ;; # ArgoCD
|
|
35
|
+
*/flux/*.yaml) IS_INFRA=true ;; # Flux
|
|
36
|
+
ansible/*.yml|*/playbooks/*.yml) IS_INFRA=true ;; # Ansible
|
|
37
|
+
esac
|
|
38
|
+
|
|
39
|
+
$IS_INFRA || exit 0
|
|
40
|
+
|
|
41
|
+
# Check if we're on a protected branch
|
|
42
|
+
BRANCH=$(git branch --show-current 2>/dev/null || echo "")
|
|
43
|
+
case "$BRANCH" in
|
|
44
|
+
main|master|production|release|release/*)
|
|
45
|
+
echo "WARNING: Editing infrastructure file on protected branch '$BRANCH'." >&2
|
|
46
|
+
echo " File: $FILE" >&2
|
|
47
|
+
echo " In GitOps workflows, changes to $BRANCH trigger immediate deployment." >&2
|
|
48
|
+
echo " Consider creating a feature branch and PR instead." >&2
|
|
49
|
+
# Warning only — change to exit 2 to block
|
|
50
|
+
;;
|
|
51
|
+
esac
|
|
52
|
+
|
|
53
|
+
exit 0
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# headless-empty-result-guard.sh — Detect empty results in headless mode
|
|
3
|
+
#
|
|
4
|
+
# Solves: claude -p exits with empty result when stream is interrupted (#40432).
|
|
5
|
+
# stop_reason: "tool_use" with result: "" is treated as success,
|
|
6
|
+
# but the model was actually mid-generation.
|
|
7
|
+
#
|
|
8
|
+
# How it works: Stop hook that checks environment for headless indicators
|
|
9
|
+
# and warns if the session produced no visible output. In CI/automation,
|
|
10
|
+
# this can trigger a retry or alert.
|
|
11
|
+
#
|
|
12
|
+
# CONFIG:
|
|
13
|
+
# CC_HEADLESS_MARKER="/tmp/claude-headless-result"
|
|
14
|
+
# Set this in your CI script to check after claude -p exits.
|
|
15
|
+
#
|
|
16
|
+
# TRIGGER: Stop
|
|
17
|
+
# MATCHER: ""
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
INPUT=$(cat)
|
|
22
|
+
|
|
23
|
+
# Check if we're in headless/print mode
|
|
24
|
+
# Indicators: CLAUDE_PRINT_MODE env var, or no TTY
|
|
25
|
+
if [ -z "${CLAUDE_PRINT_MODE:-}" ] && [ -t 0 ]; then
|
|
26
|
+
# Interactive mode — skip
|
|
27
|
+
exit 0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Check for empty/incomplete result indicators
|
|
31
|
+
STOP_REASON=$(echo "$INPUT" | jq -r '.stop_reason // empty' 2>/dev/null)
|
|
32
|
+
RESULT=$(echo "$INPUT" | jq -r '.result // empty' 2>/dev/null)
|
|
33
|
+
|
|
34
|
+
MARKER="${CC_HEADLESS_MARKER:-/tmp/claude-headless-result-${PPID:-0}}"
|
|
35
|
+
|
|
36
|
+
if [ "$STOP_REASON" = "tool_use" ] || [ -z "$RESULT" ]; then
|
|
37
|
+
echo "WARNING: Headless session ended with incomplete result." >&2
|
|
38
|
+
echo " stop_reason: ${STOP_REASON:-unknown}" >&2
|
|
39
|
+
echo " result length: ${#RESULT}" >&2
|
|
40
|
+
echo " This may indicate a stream interruption, not a completed task." >&2
|
|
41
|
+
echo "incomplete" > "$MARKER"
|
|
42
|
+
else
|
|
43
|
+
echo "complete" > "$MARKER"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
exit 0
|