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.
Files changed (384) hide show
  1. package/COOKBOOK.md +70 -0
  2. package/README.md +6 -2
  3. package/examples/absolute-rule-enforcer.sh +42 -0
  4. package/examples/allow-claude-settings.sh +2 -0
  5. package/examples/allow-git-hooks-dir.sh +2 -0
  6. package/examples/allow-protected-dirs.sh +2 -0
  7. package/examples/allowlist.sh +2 -0
  8. package/examples/ansible-vault-guard.sh +2 -0
  9. package/examples/auto-approve-build.sh +2 -0
  10. package/examples/auto-approve-compound-git.sh +2 -0
  11. package/examples/auto-approve-docker.sh +2 -0
  12. package/examples/auto-approve-git-read.sh +2 -0
  13. package/examples/auto-approve-python.sh +2 -0
  14. package/examples/auto-approve-readonly-tools.sh +2 -0
  15. package/examples/auto-approve-ssh.sh +2 -0
  16. package/examples/auto-approve-test.sh +2 -0
  17. package/examples/auto-checkpoint.sh +2 -0
  18. package/examples/auto-git-checkpoint.sh +2 -0
  19. package/examples/auto-mode-safe-commands.sh +2 -0
  20. package/examples/auto-snapshot.sh +2 -0
  21. package/examples/backup-before-refactor.sh +2 -0
  22. package/examples/banned-command-guard.sh +3 -3
  23. package/examples/bash-domain-allowlist.sh +72 -0
  24. package/examples/bash-safety-auto-deny.sh +56 -0
  25. package/examples/bash-secret-output-detector.sh +68 -0
  26. package/examples/bash-timeout-guard.sh +2 -0
  27. package/examples/bashrc-safety-check.sh +59 -0
  28. package/examples/bg-task-cooldown-guard.sh +46 -0
  29. package/examples/block-database-wipe.sh +2 -0
  30. package/examples/branch-name-check.sh +2 -0
  31. package/examples/branch-naming-convention.sh +2 -0
  32. package/examples/cargo-publish-guard.sh +2 -0
  33. package/examples/check-abort-controller.sh +2 -0
  34. package/examples/check-accessibility.sh +2 -0
  35. package/examples/check-aria-labels.sh +2 -0
  36. package/examples/check-async-await-consistency.sh +2 -0
  37. package/examples/check-before-act-enforcer.sh +47 -0
  38. package/examples/check-charset-meta.sh +2 -0
  39. package/examples/check-cleanup-effect.sh +2 -0
  40. package/examples/check-content-type.sh +2 -0
  41. package/examples/check-controlled-input.sh +2 -0
  42. package/examples/check-cookie-flags.sh +2 -0
  43. package/examples/check-cors-config.sh +2 -0
  44. package/examples/check-csp-headers.sh +2 -0
  45. package/examples/check-csrf-protection.sh +2 -0
  46. package/examples/check-debounce.sh +2 -0
  47. package/examples/check-dependency-age.sh +2 -0
  48. package/examples/check-dependency-license.sh +2 -0
  49. package/examples/check-dockerfile-best-practice.sh +2 -0
  50. package/examples/check-error-boundaries.sh +2 -0
  51. package/examples/check-error-class.sh +2 -0
  52. package/examples/check-error-handling.sh +2 -0
  53. package/examples/check-error-logging.sh +2 -0
  54. package/examples/check-error-message.sh +2 -0
  55. package/examples/check-error-page.sh +2 -0
  56. package/examples/check-error-stack.sh +2 -0
  57. package/examples/check-favicon.sh +2 -0
  58. package/examples/check-form-validation.sh +2 -0
  59. package/examples/check-git-hooks-compat.sh +2 -0
  60. package/examples/check-https-redirect.sh +2 -0
  61. package/examples/check-image-optimization.sh +2 -0
  62. package/examples/check-input-validation.sh +2 -0
  63. package/examples/check-key-prop.sh +2 -0
  64. package/examples/check-lang-attribute.sh +2 -0
  65. package/examples/check-lazy-loading.sh +2 -0
  66. package/examples/check-loading-state.sh +2 -0
  67. package/examples/check-memo-deps.sh +2 -0
  68. package/examples/check-meta-description.sh +2 -0
  69. package/examples/check-npm-scripts-exist.sh +2 -0
  70. package/examples/check-null-check.sh +2 -0
  71. package/examples/check-package-size.sh +2 -0
  72. package/examples/check-pagination.sh +2 -0
  73. package/examples/check-port-availability.sh +2 -0
  74. package/examples/check-promise-all.sh +2 -0
  75. package/examples/check-prop-types.sh +2 -0
  76. package/examples/check-rate-limiting.sh +2 -0
  77. package/examples/check-responsive-design.sh +2 -0
  78. package/examples/check-retry-logic.sh +2 -0
  79. package/examples/check-return-types.sh +2 -0
  80. package/examples/check-semantic-html.sh +2 -0
  81. package/examples/check-semantic-versioning.sh +2 -0
  82. package/examples/check-suspense-fallback.sh +2 -0
  83. package/examples/check-test-naming.sh +2 -0
  84. package/examples/check-timeout-cleanup.sh +2 -0
  85. package/examples/check-tls-version.sh +2 -0
  86. package/examples/check-type-coercion.sh +2 -0
  87. package/examples/check-unsubscribe.sh +2 -0
  88. package/examples/check-viewport-meta.sh +2 -0
  89. package/examples/check-worker-terminate.sh +2 -0
  90. package/examples/checkpoint-tamper-guard.sh +2 -0
  91. package/examples/chmod-guard.sh +2 -0
  92. package/examples/chown-guard.sh +2 -0
  93. package/examples/ci-workflow-guard.sh +59 -0
  94. package/examples/classifier-fallback-allow.sh +2 -0
  95. package/examples/claude-cache-gc.sh +15 -0
  96. package/examples/claudeignore-enforce-guard.sh +60 -0
  97. package/examples/claudemd-enforcer.sh +2 -0
  98. package/examples/commit-message-check.sh +2 -0
  99. package/examples/compact-blocker.sh +25 -0
  100. package/examples/composer-guard.sh +2 -0
  101. package/examples/compound-command-allow.sh +2 -0
  102. package/examples/consecutive-failure-circuit-breaker.sh +49 -0
  103. package/examples/console-log-count.sh +2 -0
  104. package/examples/context-compact-advisor.sh +16 -0
  105. package/examples/cors-star-warn.sh +2 -0
  106. package/examples/credential-exfil-guard.sh +2 -0
  107. package/examples/credential-file-cat-guard.sh +2 -0
  108. package/examples/cron-modification-guard.sh +40 -0
  109. package/examples/cwd-project-boundary-guard.sh +50 -0
  110. package/examples/denied-action-retry-guard.sh +41 -0
  111. package/examples/dependency-install-guard.sh +2 -0
  112. package/examples/deploy-guard.sh +2 -0
  113. package/examples/deploy-path-verify-guard.sh +62 -0
  114. package/examples/django-migrate-guard.sh +2 -0
  115. package/examples/docker-volume-guard.sh +2 -0
  116. package/examples/dockerfile-latest-guard.sh +2 -0
  117. package/examples/dotenv-commit-guard.sh +44 -0
  118. package/examples/dotenv-example-sync.sh +55 -0
  119. package/examples/dotnet-build-on-edit.sh +2 -0
  120. package/examples/drizzle-migrate-guard.sh +2 -0
  121. package/examples/edit-counter-test-gate.sh +44 -0
  122. package/examples/edit-error-counter.sh +2 -0
  123. package/examples/edit-guard.sh +2 -0
  124. package/examples/edit-retry-loop-guard.sh +2 -0
  125. package/examples/edit-verify.sh +2 -0
  126. package/examples/enforce-tests.sh +2 -0
  127. package/examples/env-inherit-guard.sh +2 -0
  128. package/examples/env-inline-secret-guard.sh +36 -0
  129. package/examples/env-prod-guard.sh +2 -0
  130. package/examples/env-required-check.sh +2 -0
  131. package/examples/env-var-check.sh +2 -0
  132. package/examples/expo-eject-guard.sh +2 -0
  133. package/examples/export-overwrite-guard.sh +29 -0
  134. package/examples/file-change-tracker.sh +2 -0
  135. package/examples/file-change-undo-tracker.sh +46 -0
  136. package/examples/file-recycle-bin.sh +48 -0
  137. package/examples/file-size-limit.sh +2 -0
  138. package/examples/five-hundred-milestone.sh +2 -0
  139. package/examples/flask-debug-guard.sh +2 -0
  140. package/examples/gem-push-guard.sh +2 -0
  141. package/examples/git-checkout-safety-guard.sh +2 -0
  142. package/examples/git-config-guard.sh +2 -0
  143. package/examples/git-hook-bypass-guard.sh +2 -0
  144. package/examples/git-merge-conflict-prevent.sh +2 -0
  145. package/examples/git-message-length.sh +2 -0
  146. package/examples/git-show-flag-sanitizer.sh +41 -0
  147. package/examples/git-stash-before-danger.sh +2 -0
  148. package/examples/git-submodule-guard.sh +2 -0
  149. package/examples/git-tag-guard.sh +2 -0
  150. package/examples/github-actions-secret-guard.sh +59 -0
  151. package/examples/gitignore-check.sh +2 -0
  152. package/examples/gitops-drift-guard.sh +53 -0
  153. package/examples/go-mod-tidy-warn.sh +2 -0
  154. package/examples/hallucination-url-check.sh +2 -0
  155. package/examples/hardcoded-ip-guard.sh +2 -0
  156. package/examples/headless-empty-result-guard.sh +46 -0
  157. package/examples/headless-stop-guard.sh +43 -0
  158. package/examples/helm-install-guard.sh +2 -0
  159. package/examples/issue-draft-redact-guard.sh +45 -0
  160. package/examples/java-compile-on-edit.sh +2 -0
  161. package/examples/k8s-production-guard.sh +77 -0
  162. package/examples/laravel-artisan-guard.sh +2 -0
  163. package/examples/large-file-guard.sh +2 -0
  164. package/examples/log-level-guard.sh +2 -0
  165. package/examples/magic-number-warn.sh +2 -0
  166. package/examples/max-edit-size-guard.sh +2 -0
  167. package/examples/max-file-count-guard.sh +2 -0
  168. package/examples/max-file-delete-count.sh +2 -0
  169. package/examples/max-function-length.sh +2 -0
  170. package/examples/max-import-count.sh +2 -0
  171. package/examples/max-subagent-count.sh +2 -0
  172. package/examples/mcp-orphan-process-guard.sh +39 -0
  173. package/examples/mcp-server-allowlist.sh +45 -0
  174. package/examples/mcp-tool-audit-log.sh +41 -0
  175. package/examples/mcp-tool-guard.sh +2 -0
  176. package/examples/migration-verify-guard.sh +44 -0
  177. package/examples/monorepo-scope-guard.sh +2 -0
  178. package/examples/network-exfil-guard.sh +61 -0
  179. package/examples/network-guard.sh +2 -0
  180. package/examples/nextjs-env-guard.sh +2 -0
  181. package/examples/no-absolute-import.sh +2 -0
  182. package/examples/no-alert-confirm-prompt.sh +2 -0
  183. package/examples/no-any-type.sh +2 -0
  184. package/examples/no-any-typescript.sh +2 -0
  185. package/examples/no-assignment-in-condition.sh +2 -0
  186. package/examples/no-callback-hell.sh +2 -0
  187. package/examples/no-catch-all-route.sh +2 -0
  188. package/examples/no-circular-dependency.sh +2 -0
  189. package/examples/no-class-in-functional.sh +2 -0
  190. package/examples/no-cleartext-storage.sh +2 -0
  191. package/examples/no-commented-code.sh +2 -0
  192. package/examples/no-commit-fixup.sh +2 -0
  193. package/examples/no-console-assert.sh +2 -0
  194. package/examples/no-console-error-swallow.sh +2 -0
  195. package/examples/no-console-in-prod.sh +2 -0
  196. package/examples/no-console-log.sh +2 -0
  197. package/examples/no-console-time.sh +2 -0
  198. package/examples/no-cors-wildcard.sh +2 -0
  199. package/examples/no-curl-upload.sh +2 -0
  200. package/examples/no-dangerouslySetInnerHTML.sh +2 -0
  201. package/examples/no-dangling-await.sh +2 -0
  202. package/examples/no-debug-in-commit.sh +2 -0
  203. package/examples/no-deep-nesting.sh +2 -0
  204. package/examples/no-deep-relative-import.sh +2 -0
  205. package/examples/no-default-credentials.sh +2 -0
  206. package/examples/no-deprecated-api.sh +2 -0
  207. package/examples/no-direct-dom-manipulation.sh +2 -0
  208. package/examples/no-disabled-test.sh +2 -0
  209. package/examples/no-document-cookie.sh +2 -0
  210. package/examples/no-document-write.sh +2 -0
  211. package/examples/no-empty-function.sh +2 -0
  212. package/examples/no-eval-in-template.sh +2 -0
  213. package/examples/no-eval-template.sh +2 -0
  214. package/examples/no-eval.sh +2 -0
  215. package/examples/no-exec-user-input.sh +2 -0
  216. package/examples/no-expose-internal-ids.sh +2 -0
  217. package/examples/no-floating-promises.sh +2 -0
  218. package/examples/no-force-install.sh +2 -0
  219. package/examples/no-git-rebase-public.sh +2 -0
  220. package/examples/no-global-state.sh +2 -0
  221. package/examples/no-hardcoded-port.sh +2 -0
  222. package/examples/no-hardcoded-url.sh +2 -0
  223. package/examples/no-helmet-missing.sh +2 -0
  224. package/examples/no-http-url.sh +2 -0
  225. package/examples/no-http-without-https.sh +2 -0
  226. package/examples/no-index-as-key.sh +2 -0
  227. package/examples/no-infinite-scroll-mem.sh +2 -0
  228. package/examples/no-inline-event-handler.sh +2 -0
  229. package/examples/no-inline-handler.sh +2 -0
  230. package/examples/no-inline-style.sh +2 -0
  231. package/examples/no-inline-styles.sh +2 -0
  232. package/examples/no-innerhtml.sh +2 -0
  233. package/examples/no-install-global.sh +2 -0
  234. package/examples/no-jwt-in-url.sh +2 -0
  235. package/examples/no-large-commit.sh +2 -0
  236. package/examples/no-localhost-expose.sh +2 -0
  237. package/examples/no-long-switch.sh +2 -0
  238. package/examples/no-magic-number.sh +2 -0
  239. package/examples/no-md5-sha1.sh +2 -0
  240. package/examples/no-memory-leak-interval.sh +2 -0
  241. package/examples/no-mixed-line-endings.sh +2 -0
  242. package/examples/no-mutation-in-reducer.sh +2 -0
  243. package/examples/no-mutation-observer-leak.sh +2 -0
  244. package/examples/no-nested-subscribe.sh +2 -0
  245. package/examples/no-nested-ternary.sh +2 -0
  246. package/examples/no-network-exfil.sh +2 -0
  247. package/examples/no-new-array-fill.sh +2 -0
  248. package/examples/no-object-freeze-mutation.sh +2 -0
  249. package/examples/no-open-redirect.sh +2 -0
  250. package/examples/no-output-truncation.sh +44 -0
  251. package/examples/no-package-downgrade.sh +2 -0
  252. package/examples/no-package-lock-edit.sh +2 -0
  253. package/examples/no-path-join-user-input.sh +2 -0
  254. package/examples/no-port-bind.sh +2 -0
  255. package/examples/no-process-exit.sh +2 -0
  256. package/examples/no-prototype-pollution.sh +2 -0
  257. package/examples/no-push-without-ci.sh +2 -0
  258. package/examples/no-raw-ref.sh +2 -0
  259. package/examples/no-redundant-fragment.sh +2 -0
  260. package/examples/no-render-in-loop.sh +2 -0
  261. package/examples/no-root-user-docker.sh +2 -0
  262. package/examples/no-root-write.sh +2 -0
  263. package/examples/no-secrets-in-args.sh +2 -0
  264. package/examples/no-secrets-in-logs.sh +2 -0
  265. package/examples/no-sensitive-log.sh +2 -0
  266. package/examples/no-side-effects-in-render.sh +2 -0
  267. package/examples/no-sleep-in-hooks.sh +2 -0
  268. package/examples/no-star-import-python.sh +2 -0
  269. package/examples/no-string-concat-sql.sh +2 -0
  270. package/examples/no-sudo-guard.sh +2 -0
  271. package/examples/no-sync-external-call.sh +2 -0
  272. package/examples/no-sync-fs.sh +2 -0
  273. package/examples/no-table-layout.sh +2 -0
  274. package/examples/no-throw-string.sh +2 -0
  275. package/examples/no-todo-in-merge.sh +2 -0
  276. package/examples/no-todo-in-production.sh +2 -0
  277. package/examples/no-todo-without-issue.sh +2 -0
  278. package/examples/no-triple-slash-ref.sh +2 -0
  279. package/examples/no-unreachable-code.sh +2 -0
  280. package/examples/no-unused-import.sh +2 -0
  281. package/examples/no-unused-state.sh +2 -0
  282. package/examples/no-var-keyword.sh +2 -0
  283. package/examples/no-wildcard-cors.sh +2 -0
  284. package/examples/no-wildcard-import.sh +2 -0
  285. package/examples/no-window-location.sh +2 -0
  286. package/examples/no-with-statement.sh +2 -0
  287. package/examples/no-write-outside-src.sh +2 -0
  288. package/examples/no-xml-external-entity.sh +2 -0
  289. package/examples/notify-waiting.sh +2 -0
  290. package/examples/npm-audit-warn.sh +2 -0
  291. package/examples/npm-publish-guard.sh +2 -0
  292. package/examples/npm-script-injection.sh +2 -0
  293. package/examples/npm-supply-chain-guard.sh +92 -0
  294. package/examples/nuxt-config-guard.sh +2 -0
  295. package/examples/output-secret-mask.sh +2 -0
  296. package/examples/package-json-guard.sh +2 -0
  297. package/examples/parallel-session-guard.sh +2 -0
  298. package/examples/path-traversal-guard.sh +2 -0
  299. package/examples/permission-audit-log.sh +2 -0
  300. package/examples/permission-entry-validator.sh +48 -0
  301. package/examples/php-lint-on-edit.sh +2 -0
  302. package/examples/pip-publish-guard.sh +2 -0
  303. package/examples/plain-language-danger-warn.sh +37 -0
  304. package/examples/plan-mode-enforcer.sh +2 -0
  305. package/examples/plugin-process-cleanup.sh +50 -0
  306. package/examples/polyglot-rm-guard.sh +59 -0
  307. package/examples/pr-description-check.sh +2 -0
  308. package/examples/pre-compact-knowledge-save.sh +53 -0
  309. package/examples/pre-compact-transcript-export.sh +85 -0
  310. package/examples/prefer-builtin-tools.sh +2 -0
  311. package/examples/prefer-const.sh +2 -0
  312. package/examples/prefer-dedicated-tools.sh +55 -0
  313. package/examples/prefer-optional-chaining.sh +2 -0
  314. package/examples/prisma-migrate-guard.sh +2 -0
  315. package/examples/prompt-injection-detector.sh +2 -0
  316. package/examples/prompt-length-guard.sh +2 -0
  317. package/examples/protect-dotfiles.sh +2 -0
  318. package/examples/public-repo-push-guard.sh +58 -0
  319. package/examples/push-requires-test-pass-record.sh +2 -0
  320. package/examples/push-requires-test-pass.sh +2 -0
  321. package/examples/rails-migration-guard.sh +2 -0
  322. package/examples/rate-limit-guard.sh +2 -0
  323. package/examples/read-all-files-enforcer.sh +51 -0
  324. package/examples/readme-exists-check.sh +2 -0
  325. package/examples/redis-flushall-guard.sh +2 -0
  326. package/examples/rm-safety-net.sh +2 -0
  327. package/examples/role-tool-guard.sh +69 -0
  328. package/examples/ruby-lint-on-edit.sh +2 -0
  329. package/examples/schema-migration-guard.sh +57 -0
  330. package/examples/scope-guard.sh +2 -0
  331. package/examples/secret-file-read-guard.sh +74 -0
  332. package/examples/self-modify-bypass-guard.sh +54 -0
  333. package/examples/sensitive-log-guard.sh +2 -0
  334. package/examples/session-checkpoint.sh +2 -0
  335. package/examples/session-end-logger.sh +57 -0
  336. package/examples/session-error-rate-monitor.sh +65 -0
  337. package/examples/session-health-monitor.sh +61 -0
  338. package/examples/session-memory-watchdog.sh +17 -0
  339. package/examples/session-permission-reset-guard.sh +39 -0
  340. package/examples/session-resume-env-fix.sh +49 -0
  341. package/examples/session-state-saver.sh +2 -0
  342. package/examples/session-summary-stop.sh +2 -0
  343. package/examples/session-summary.sh +2 -0
  344. package/examples/session-token-counter.sh +2 -0
  345. package/examples/shell-wrapper-guard.sh +2 -0
  346. package/examples/skill-gate.sh +2 -0
  347. package/examples/skill-injection-detector.sh +41 -0
  348. package/examples/spec-file-scope-guard.sh +69 -0
  349. package/examples/spring-profile-guard.sh +2 -0
  350. package/examples/sql-injection-detect.sh +2 -0
  351. package/examples/subagent-budget-guard.sh +2 -0
  352. package/examples/subagent-claudemd-inject.sh +45 -0
  353. package/examples/subagent-tool-call-limiter.sh +48 -0
  354. package/examples/svelte-lint-on-edit.sh +2 -0
  355. package/examples/swift-build-on-edit.sh +2 -0
  356. package/examples/system-message-workaround.sh +44 -0
  357. package/examples/system-package-guard.sh +2 -0
  358. package/examples/temp-file-cleanup.sh +2 -0
  359. package/examples/terminal-state-restore.sh +23 -0
  360. package/examples/test-after-edit.sh +2 -0
  361. package/examples/test-before-commit.sh +2 -0
  362. package/examples/test-before-push.sh +2 -0
  363. package/examples/test-exit-code-verify.sh +2 -0
  364. package/examples/timeout-guard.sh +2 -0
  365. package/examples/timezone-guard.sh +2 -0
  366. package/examples/tmp-output-size-guard.sh +46 -0
  367. package/examples/todo-check.sh +2 -0
  368. package/examples/todo-deadline-warn.sh +48 -0
  369. package/examples/token-budget-per-task.sh +55 -0
  370. package/examples/token-usage-tracker.sh +14 -0
  371. package/examples/turbo-cache-guard.sh +2 -0
  372. package/examples/uncommitted-changes-stop.sh +2 -0
  373. package/examples/uncommitted-work-shield.sh +37 -0
  374. package/examples/usage-warn.sh +2 -0
  375. package/examples/verify-before-commit.sh +2 -0
  376. package/examples/vue-lint-on-edit.sh +2 -0
  377. package/examples/webfetch-domain-allow.sh +96 -0
  378. package/examples/worktree-memory-guard.sh +47 -0
  379. package/examples/worktree-project-unify.sh +19 -0
  380. package/examples/worktree-unmerged-guard.sh +2 -0
  381. package/examples/write-overwrite-confirm.sh +40 -0
  382. package/examples/write-secret-guard.sh +2 -0
  383. package/examples/write-test-ratio.sh +2 -0
  384. package/package.json +2 -2
@@ -16,6 +16,8 @@
16
16
  # }
17
17
  # }
18
18
  # ================================================================
19
+ #
20
+ # TRIGGER: PreToolUse MATCHER: "Bash"
19
21
 
20
22
  INPUT=$(cat)
21
23
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
@@ -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
@@ -19,6 +19,8 @@
19
19
  # }
20
20
  # }
21
21
  # ================================================================
22
+ #
23
+ # TRIGGER: PreToolUse MATCHER: "Edit|Write"
22
24
 
23
25
  INPUT=$(cat)
24
26
  TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
@@ -16,6 +16,8 @@
16
16
  # }]
17
17
  # }
18
18
  # }
19
+ #
20
+ # TRIGGER: PreToolUse MATCHER: "Bash"
19
21
 
20
22
  INPUT=$(cat)
21
23
  TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
@@ -6,6 +6,8 @@
6
6
  #
7
7
  # Tracks consecutive Edit failures on the same file. After 3 failures,
8
8
  # warns the model to verify the file path.
9
+ #
10
+ # TRIGGER: PreToolUse MATCHER: "Edit|Write"
9
11
 
10
12
  INPUT=$(cat)
11
13
  TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
@@ -1,4 +1,6 @@
1
1
  #!/bin/bash
2
+ #
3
+ # TRIGGER: PreToolUse MATCHER: "Edit|Write"
2
4
  INPUT=$(cat)
3
5
  TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
4
6
  FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
@@ -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)
@@ -23,6 +23,8 @@
23
23
  # }
24
24
  # }
25
25
  # ================================================================
26
+ #
27
+ # TRIGGER: PreToolUse MATCHER: "Bash"
26
28
 
27
29
  INPUT=$(cat)
28
30
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
@@ -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
@@ -14,6 +14,8 @@
14
14
  # }]
15
15
  # }
16
16
  # }
17
+ #
18
+ # TRIGGER: PreToolUse MATCHER: "Bash"
17
19
 
18
20
  INPUT=$(cat)
19
21
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
@@ -1,3 +1,5 @@
1
+ #
2
+ # TRIGGER: PreToolUse MATCHER: "Bash"
1
3
  INPUT=$(cat)
2
4
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
5
  [[ -z "$COMMAND" ]] && exit 0
@@ -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
@@ -23,6 +23,8 @@
23
23
  # }
24
24
  #
25
25
  # View changes: cat ~/.claude/session-changes.log
26
+ #
27
+ # TRIGGER: PreToolUse MATCHER: "Edit|Write"
26
28
 
27
29
  INPUT=$(cat)
28
30
  TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
@@ -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
  INPUT=$(cat)
3
5
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
4
6
  CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
@@ -1,3 +1,5 @@
1
+ #
2
+ # TRIGGER: Notification MATCHER: ""
1
3
  INPUT=$(cat)
2
4
  SETTINGS="$HOME/.claude/settings.local.json"
3
5
  [[ ! -f "$SETTINGS" ]] && exit 0
@@ -17,6 +17,8 @@
17
17
  # }
18
18
  # }
19
19
  # ================================================================
20
+ #
21
+ # TRIGGER: PreToolUse MATCHER: "Bash"
20
22
 
21
23
  INPUT=$(cat)
22
24
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
@@ -1,3 +1,5 @@
1
+ #
2
+ # TRIGGER: PreToolUse MATCHER: "Bash"
1
3
  INPUT=$(cat)
2
4
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
5
  [[ -z "$COMMAND" ]] && exit 0
@@ -23,6 +23,8 @@
23
23
  # }]
24
24
  # }
25
25
  # }
26
+ #
27
+ # TRIGGER: PreToolUse MATCHER: "Bash"
26
28
 
27
29
  INPUT=$(cat)
28
30
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
@@ -14,6 +14,8 @@
14
14
  # }]
15
15
  # }
16
16
  # }
17
+ #
18
+ # TRIGGER: PreToolUse MATCHER: "Bash"
17
19
 
18
20
  INPUT=$(cat)
19
21
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
@@ -1,4 +1,6 @@
1
1
  #!/bin/bash
2
+ #
3
+ # TRIGGER: PreToolUse MATCHER: "Bash"
2
4
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
3
5
  [ -z "$COMMAND" ] && exit 0
4
6
  if echo "$COMMAND" | grep -qE "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
  INPUT=$(cat)
3
5
  CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
4
6
  [ -z "$CONTENT" ] && exit 0
@@ -1,4 +1,6 @@
1
1
  #!/bin/bash
2
+ #
3
+ # TRIGGER: PreToolUse MATCHER: "Bash"
2
4
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
3
5
  [ -z "$COMMAND" ] && exit 0
4
6
  echo "$COMMAND" | grep -qE "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
@@ -18,6 +18,8 @@
18
18
  # }]
19
19
  # }
20
20
  # }
21
+ #
22
+ # TRIGGER: PreToolUse MATCHER: "Bash"
21
23
 
22
24
  INPUT=$(cat)
23
25
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
@@ -1,4 +1,6 @@
1
1
  #!/bin/bash
2
+ #
3
+ # TRIGGER: PreToolUse MATCHER: "Bash"
2
4
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
3
5
  [ -z "$COMMAND" ] && exit 0
4
6
  if echo "$COMMAND" | grep -qE '\bgit\s+submodule\s+(deinit|rm)\b'; then
@@ -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+tag\s+(-a\s+|-d\s+)?v'; then
@@ -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
@@ -1,4 +1,6 @@
1
1
  #!/bin/bash
2
+ #
3
+ # TRIGGER: PreToolUse MATCHER: "Bash"
2
4
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
3
5
  [ -z "$COMMAND" ] && exit 0
4
6
  echo "$COMMAND" | grep -qE '^\s*git\s+add' || 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
@@ -1,3 +1,5 @@
1
+ #
2
+ # TRIGGER: PostToolUse MATCHER: "Bash"
1
3
  INPUT=$(cat)
2
4
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
5
  [[ -z "$COMMAND" ]] && exit 0
@@ -21,6 +21,8 @@
21
21
  # }
22
22
  # }
23
23
  # ================================================================
24
+ #
25
+ # TRIGGER: PreToolUse MATCHER: "Bash"
24
26
 
25
27
  INPUT=$(cat)
26
28
  TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
@@ -1,3 +1,5 @@
1
+ #
2
+ # TRIGGER: PreToolUse MATCHER: "Edit|Write"
1
3
  INPUT=$(cat)
2
4
  FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
3
5
  [[ -z "$FILE" ]] && 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