cc-safe-setup 29.8.0 → 30.0.4

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 (1017) hide show
  1. package/.claude-plugin/marketplace.json +72 -19
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CONTRIBUTING.md +7 -2
  4. package/COOKBOOK.md +50 -46
  5. package/LICENSE +21 -0
  6. package/MIGRATION.md +8 -8
  7. package/README.md +419 -512
  8. package/SAFETY_CHECKLIST.md +15 -7
  9. package/SERVICES.md +194 -0
  10. package/SETTINGS_REFERENCE.md +236 -17
  11. package/SKILL.md +5 -5
  12. package/TROUBLESHOOTING.md +35 -14
  13. package/action.yml +12 -4
  14. package/audit/README.md +84 -0
  15. package/audit/audit-checklist.md +152 -0
  16. package/audit/boundary-selftest.sh +85 -0
  17. package/audit/boundary.sh +123 -0
  18. package/audit/count-hooks.py +42 -0
  19. package/audit/find-dead-hooks.sh +82 -0
  20. package/audit/find-unfirable-rules.sh +142 -0
  21. package/audit/fire.sh +97 -0
  22. package/audit/selftest.sh +42 -0
  23. package/audit/unfirable-selftest.sh +81 -0
  24. package/audit-web/index.html +9 -9
  25. package/examples/README.md +54 -17
  26. package/examples/account-billing-log.sh +98 -0
  27. package/examples/account-routing-preflight.sh +123 -0
  28. package/examples/agent-sdk-credit-pool-monitor.sh +145 -0
  29. package/examples/agent-view-quota-decrement.sh +32 -0
  30. package/examples/agent-view-quota-warn.sh +94 -0
  31. package/examples/agents-md-edit-drift-warner.sh +223 -0
  32. package/examples/agents-md-loader.sh +160 -0
  33. package/examples/agents-md-sync-checker.sh +102 -0
  34. package/examples/ai-slop-punctuation-arrest.sh +210 -0
  35. package/examples/allowlist.sh +70 -10
  36. package/examples/always-allow-pattern-suggester.sh +125 -0
  37. package/examples/ansible-vault-guard.sh +2 -1
  38. package/examples/anthropic-base-url-guard.sh +76 -0
  39. package/examples/api-busyloop-guard.sh +123 -0
  40. package/examples/api-endpoint-guard.sh +12 -0
  41. package/examples/api-key-in-url-guard.sh +17 -1
  42. package/examples/api-rate-limit-guard.sh +36 -1
  43. package/examples/api-rate-limit-tracker.sh +12 -0
  44. package/examples/articulated-scope-capture.sh +101 -0
  45. package/examples/askuserquestion-autonomy-gate.sh +102 -0
  46. package/examples/attachment-traversal-guard.sh +118 -0
  47. package/examples/aup-block-pattern-logger.sh +161 -0
  48. package/examples/aup-false-positive-helper.sh +124 -0
  49. package/examples/aup-large-tool-output-warner.sh +282 -0
  50. package/examples/aup-retry-loop-guard.sh +229 -0
  51. package/examples/auth-expiry-reminder.sh +157 -0
  52. package/examples/auth-macos-sleep-detector.sh +185 -0
  53. package/examples/auth-path-detector.sh +171 -0
  54. package/examples/auth-status-checker.sh +157 -0
  55. package/examples/authorized-reconfirmation-detector.sh +195 -0
  56. package/examples/auto-approve-build.sh +39 -0
  57. package/examples/auto-approve-cargo.sh +31 -1
  58. package/examples/auto-approve-docker.sh +35 -0
  59. package/examples/auto-approve-git-read.sh +37 -14
  60. package/examples/auto-approve-go.sh +30 -1
  61. package/examples/auto-approve-gradle.sh +29 -1
  62. package/examples/auto-approve-make.sh +29 -1
  63. package/examples/auto-approve-maven.sh +29 -1
  64. package/examples/auto-approve-python.sh +41 -1
  65. package/examples/auto-approve-readonly-tools.sh +1 -0
  66. package/examples/auto-approve-readonly.sh +110 -33
  67. package/examples/auto-approve-ssh.sh +30 -1
  68. package/examples/auto-approve-test.sh +44 -0
  69. package/examples/auto-checkpoint.sh +1 -1
  70. package/examples/auto-compact-prep.sh +0 -0
  71. package/examples/auto-git-checkpoint.sh +0 -0
  72. package/examples/auto-mode-safe-commands.sh +53 -1
  73. package/examples/auto-mode-safety-enforcer.sh +151 -4
  74. package/examples/auto-push-session-branch.sh +22 -0
  75. package/examples/aws-production-guard.sh +13 -1
  76. package/examples/aws-region-guard.sh +1 -1
  77. package/examples/background-cost-launch-guard.sh +82 -0
  78. package/examples/background-task-guard.sh +12 -0
  79. package/examples/backup-before-refactor.sh +0 -0
  80. package/examples/banned-command-guard.sh +12 -0
  81. package/examples/bash-allowlist-secondary-check.sh +157 -0
  82. package/examples/bash-domain-allowlist.sh +12 -0
  83. package/examples/bash-fanout-bounded-rewriter.sh +191 -0
  84. package/examples/bash-heuristic-approver.sh +112 -10
  85. package/examples/bash-project-fence.sh +194 -0
  86. package/examples/bash-safety-auto-deny.sh +12 -0
  87. package/examples/bash-secret-output-detector.sh +44 -3
  88. package/examples/bash-trace-guard.sh +12 -0
  89. package/examples/bashrc-safety-check.sh +0 -0
  90. package/examples/bg-permission-prompt-warner.sh +182 -0
  91. package/examples/bg-task-cooldown-guard.sh +12 -0
  92. package/examples/binary-read-detector.sh +135 -0
  93. package/examples/binary-upload-guard.sh +12 -0
  94. package/examples/block-database-wipe.sh +35 -10
  95. package/examples/branch-name-check.sh +8 -3
  96. package/examples/branch-naming-convention.sh +0 -0
  97. package/examples/broad-find-guard.sh +12 -0
  98. package/examples/broad-prefix-session-trap-warner.sh +106 -0
  99. package/examples/bulk-file-delete-guard.sh +45 -0
  100. package/examples/bypass-mode-effective-verifier.sh +203 -0
  101. package/examples/cache-creation-drift-detector.sh +120 -0
  102. package/examples/cache-residue-detector.sh +195 -0
  103. package/examples/cache-tier-logger.sh +70 -0
  104. package/examples/cache-ttl-eviction-detector.sh +149 -0
  105. package/examples/cargo-publish-guard.sh +14 -1
  106. package/examples/case-insensitive-path-guard.sh +21 -2
  107. package/examples/case-sensitive-guard.sh +23 -2
  108. package/examples/cch-cache-guard.sh +0 -0
  109. package/examples/cch-sentinel-precommit-guard.sh +47 -0
  110. package/examples/check-abort-controller.sh +1 -1
  111. package/examples/check-accessibility.sh +0 -0
  112. package/examples/check-aria-labels.sh +0 -0
  113. package/examples/check-before-act-enforcer.sh +12 -0
  114. package/examples/check-charset-meta.sh +0 -0
  115. package/examples/check-cookie-flags.sh +0 -0
  116. package/examples/check-cors-config.sh +0 -0
  117. package/examples/check-csp-headers.sh +0 -0
  118. package/examples/check-csrf-protection.sh +0 -0
  119. package/examples/check-debounce.sh +1 -1
  120. package/examples/check-dependency-age.sh +0 -0
  121. package/examples/check-dependency-license.sh +0 -0
  122. package/examples/check-dockerfile-best-practice.sh +0 -0
  123. package/examples/check-error-boundaries.sh +0 -0
  124. package/examples/check-error-class.sh +1 -1
  125. package/examples/check-error-handling.sh +0 -0
  126. package/examples/check-error-logging.sh +1 -1
  127. package/examples/check-error-message.sh +0 -0
  128. package/examples/check-error-stack.sh +0 -0
  129. package/examples/check-favicon.sh +0 -0
  130. package/examples/check-git-hooks-compat.sh +0 -0
  131. package/examples/check-gitattributes.sh +0 -0
  132. package/examples/check-https-redirect.sh +0 -0
  133. package/examples/check-input-validation.sh +0 -0
  134. package/examples/check-lang-attribute.sh +0 -0
  135. package/examples/check-npm-scripts-exist.sh +0 -0
  136. package/examples/check-package-size.sh +0 -0
  137. package/examples/check-port-availability.sh +0 -0
  138. package/examples/check-promise-all.sh +1 -1
  139. package/examples/check-rate-limiting.sh +0 -0
  140. package/examples/check-return-types.sh +0 -0
  141. package/examples/check-semantic-versioning.sh +0 -0
  142. package/examples/check-suspense-fallback.sh +1 -1
  143. package/examples/check-test-exists.sh +0 -0
  144. package/examples/check-test-naming.sh +0 -0
  145. package/examples/check-timeout-cleanup.sh +1 -1
  146. package/examples/check-tls-version.sh +0 -0
  147. package/examples/check-unsubscribe.sh +1 -1
  148. package/examples/check-viewport-meta.sh +0 -0
  149. package/examples/check-worker-terminate.sh +1 -1
  150. package/examples/checkpoint-tamper-guard.sh +12 -0
  151. package/examples/chmod-guard.sh +12 -0
  152. package/examples/chown-guard.sh +24 -0
  153. package/examples/ci-workflow-guard.sh +6 -1
  154. package/examples/claim-verify-detector.sh +109 -0
  155. package/examples/claim-vs-caveat-checker.sh +77 -0
  156. package/examples/classifier-fallback-allow.sh +114 -40
  157. package/examples/claude-cache-gc.sh +1 -0
  158. package/examples/claude-code-version-pin-advisor.sh +183 -0
  159. package/examples/claude-md-reinjector.sh +129 -0
  160. package/examples/claude-md-size-monitor.sh +66 -0
  161. package/examples/claude-update-budget-guard.sh +123 -0
  162. package/examples/claude-update-smart.sh +113 -0
  163. package/examples/claudeignore-enforce-guard.sh +12 -0
  164. package/examples/claudemd-enforcer.sh +0 -0
  165. package/examples/claudemd-tool-prohibition.sh +128 -0
  166. package/examples/clear-command-confirm-guard.sh +32 -15
  167. package/examples/cli-config-pinning-detector.sh +162 -0
  168. package/examples/cliff-countdown-advisor.sh +96 -0
  169. package/examples/clipboard-secret-guard.sh +12 -0
  170. package/examples/closure-word-verify-gate.sh +150 -0
  171. package/examples/cloud-cli-guard.sh +39 -2
  172. package/examples/commit-all-scope-guard.sh +44 -0
  173. package/examples/commit-message-check.sh +1 -1
  174. package/examples/commit-message-quality.sh +6 -1
  175. package/examples/commitment-carry-forward-arrest.sh +361 -0
  176. package/examples/compact-alert-notification.sh +0 -0
  177. package/examples/compact-dispatch-watchdog.sh +149 -0
  178. package/examples/completion-claim-without-verification-detector.sh +207 -0
  179. package/examples/composer-guard.sh +12 -0
  180. package/examples/compound-bash-permission-resolver.sh +156 -0
  181. package/examples/compound-command-allow.sh +1 -1
  182. package/examples/compound-command-deny-enforcer.sh +183 -0
  183. package/examples/compound-inject-guard.sh +12 -0
  184. package/examples/concurrent-edit-lock.sh +12 -0
  185. package/examples/conflict-marker-guard.sh +12 -0
  186. package/examples/consecutive-error-breaker.sh +0 -0
  187. package/examples/console-log-count.sh +1 -0
  188. package/examples/context-compact-advisor.sh +29 -2
  189. package/examples/context-length-budget.sh +69 -0
  190. package/examples/context-threshold-alert.sh +12 -0
  191. package/examples/context-usage-drift-alert.sh +41 -13
  192. package/examples/conversation-history-guard.sh +12 -0
  193. package/examples/core-file-protect-guard.sh +12 -0
  194. package/examples/cors-star-warn.sh +0 -0
  195. package/examples/cost-incident-self-audit.sh +162 -0
  196. package/examples/cowork-claude-md-load-checker.sh +100 -0
  197. package/examples/cowork-fuse-staleness-watcher.sh +140 -0
  198. package/examples/cowork-hook-absence-warner.sh +167 -0
  199. package/examples/cowork-model-picker-advisor.sh +115 -0
  200. package/examples/credential-exfil-guard.sh +65 -4
  201. package/examples/credential-file-cat-guard.sh +17 -1
  202. package/examples/cron-create-receipt.sh +112 -0
  203. package/examples/cron-modification-guard.sh +12 -0
  204. package/examples/cross-product-mcp-spawn-detector.sh +178 -0
  205. package/examples/cwd-project-boundary-guard.sh +0 -0
  206. package/examples/daemon-zombie-worker-surfacer.sh +118 -0
  207. package/examples/daily-cost-guard.sh +164 -0
  208. package/examples/daily-usage-tracker.sh +0 -0
  209. package/examples/dangerous-pip-flag-guard.sh +12 -0
  210. package/examples/db-connect-guard.sh +13 -1
  211. package/examples/denied-action-retry-guard.sh +12 -0
  212. package/examples/deny-bypass-detector.sh +12 -0
  213. package/examples/deny-rule-integrity-verifier.sh +181 -0
  214. package/examples/dependency-install-guard.sh +12 -0
  215. package/examples/deploy-guard.sh +14 -2
  216. package/examples/deploy-path-verify-guard.sh +12 -0
  217. package/examples/deployment-readback-gate.sh +135 -0
  218. package/examples/deployment-readback-gh-adapter.sh +247 -0
  219. package/examples/deployment-verify-guard.sh +25 -2
  220. package/examples/destructive-db-script-write-guard.sh +166 -0
  221. package/examples/destructive-migration-write-guard.sh +163 -0
  222. package/examples/diff-size-guard.sh +12 -0
  223. package/examples/disabled-feature-toggle-advisor.sh +88 -0
  224. package/examples/disk-partition-guard.sh +12 -0
  225. package/examples/dispatch-allowlist-preflight.sh +301 -0
  226. package/examples/dispatch-liveness-watchdog.sh +216 -0
  227. package/examples/dispatch-receipt.sh +174 -0
  228. package/examples/django-migrate-guard.sh +12 -0
  229. package/examples/dns-config-guard.sh +12 -0
  230. package/examples/docker-dangerous-guard.sh +12 -0
  231. package/examples/docker-volume-guard.sh +0 -0
  232. package/examples/dockerfile-latest-guard.sh +1 -0
  233. package/examples/dotenv-anthropic-key-billing-guard.sh +99 -0
  234. package/examples/dotenv-commit-guard.sh +12 -0
  235. package/examples/dotenv-read-guard.sh +12 -0
  236. package/examples/dotfile-protection-guard.sh +12 -0
  237. package/examples/dotnet-build-on-edit.sh +1 -1
  238. package/examples/drizzle-migrate-guard.sh +27 -2
  239. package/examples/echo-probe-spam-detector.sh +176 -0
  240. package/examples/edit-counter-test-gate.sh +12 -0
  241. package/examples/edit-error-counter.sh +1 -1
  242. package/examples/edit-guard.sh +12 -0
  243. package/examples/edit-old-string-validator.sh +12 -0
  244. package/examples/edit-quote-ambiguity-guard.sh +97 -0
  245. package/examples/edit-retry-loop-guard.sh +0 -0
  246. package/examples/edit-verify.sh +0 -0
  247. package/examples/env-inline-secret-guard.sh +14 -2
  248. package/examples/env-naming-convention.sh +0 -0
  249. package/examples/env-prod-guard.sh +0 -0
  250. package/examples/env-required-check.sh +0 -0
  251. package/examples/env-source-guard.sh +12 -0
  252. package/examples/env-var-check.sh +24 -1
  253. package/examples/ephemeral-container-detector.sh +147 -0
  254. package/examples/error-memory-guard.sh +12 -0
  255. package/examples/evidence-claim-gate.sh +187 -0
  256. package/examples/exploration-budget-guard.sh +12 -0
  257. package/examples/expo-eject-guard.sh +13 -0
  258. package/examples/extended-thinking-loop-guard.sh +139 -0
  259. package/examples/extended-thinking-resume-warning.sh +119 -0
  260. package/examples/extended-thinking-tool-use-mismatch-detector.sh +212 -0
  261. package/examples/fabricated-command-detector.sh +145 -0
  262. package/examples/false-disk-full-guard.sh +63 -0
  263. package/examples/feature-deprecation-detector.sh +109 -0
  264. package/examples/file-change-tracker.sh +1 -1
  265. package/examples/file-change-undo-tracker.sh +0 -0
  266. package/examples/file-edit-backup.sh +63 -14
  267. package/examples/file-reference-check.sh +0 -0
  268. package/examples/file-size-limit.sh +12 -0
  269. package/examples/financial-operation-guard.sh +12 -0
  270. package/examples/firewall-guard.sh +12 -0
  271. package/examples/fish-shell-wrapper.sh +125 -7
  272. package/examples/five-hundred-milestone.sh +1 -0
  273. package/examples/flask-static-route-guard.sh +89 -0
  274. package/examples/forbidden-response-phrase-detector.sh +161 -0
  275. package/examples/gem-push-guard.sh +13 -0
  276. package/examples/gh-cli-destructive-guard.sh +13 -1
  277. package/examples/git-checkout-safety-guard.sh +12 -0
  278. package/examples/git-checkout-uncommitted-guard.sh +12 -0
  279. package/examples/git-config-guard.sh +12 -0
  280. package/examples/git-crypt-worktree-guard.sh +12 -0
  281. package/examples/git-filter-repo-guard.sh +56 -0
  282. package/examples/git-history-rewrite-guard.sh +12 -0
  283. package/examples/git-hook-bypass-guard.sh +0 -0
  284. package/examples/git-maintenance-guard.sh +83 -0
  285. package/examples/git-merge-conflict-prevent.sh +0 -0
  286. package/examples/git-message-length-check.sh +0 -0
  287. package/examples/git-message-length.sh +0 -0
  288. package/examples/git-operations-require-approval.sh +68 -14
  289. package/examples/git-pack-temp-cleanup-guard.sh +102 -0
  290. package/examples/git-push-blast-radius-guard.sh +218 -0
  291. package/examples/git-remote-guard.sh +24 -3
  292. package/examples/git-rm-orphan-wipe-guard.sh +85 -0
  293. package/examples/git-stash-before-checkout.sh +12 -0
  294. package/examples/git-stash-before-danger.sh +0 -0
  295. package/examples/git-submodule-guard.sh +69 -2
  296. package/examples/git-tag-guard.sh +12 -0
  297. package/examples/github-actions-secret-guard.sh +0 -0
  298. package/examples/gitignore-check.sh +0 -0
  299. package/examples/gitops-drift-guard.sh +12 -0
  300. package/examples/go-mod-tidy-warn.sh +1 -0
  301. package/examples/go-vet-after-edit.sh +12 -0
  302. package/examples/goal-iteration-limit-warner.sh +152 -0
  303. package/examples/grep-nul-false-negative-guard.sh +30 -0
  304. package/examples/growthbook-flag-monitor.sh +257 -0
  305. package/examples/hallucination-url-check.sh +1 -1
  306. package/examples/halt-signal-detector.sh +135 -0
  307. package/examples/handoff-on-clear.sh +65 -0
  308. package/examples/hardcoded-ip-guard.sh +1 -0
  309. package/examples/headless-empty-result-guard.sh +0 -0
  310. package/examples/headless-stop-guard.sh +0 -0
  311. package/examples/helm-install-guard.sh +2 -1
  312. package/examples/home-critical-bash-guard.sh +17 -1
  313. package/examples/hook-debug-wrapper.sh +6 -2
  314. package/examples/hook-permission-fixer.sh +0 -0
  315. package/examples/hook-self-disable-detector.sh +99 -0
  316. package/examples/hook-stdout-sanitizer.sh +6 -2
  317. package/examples/hook-tamper-guard.sh +12 -0
  318. package/examples/idle-session-cost-alert.sh +0 -0
  319. package/examples/image-dimension-guard.sh +109 -0
  320. package/examples/image-file-validator.sh +0 -0
  321. package/examples/iteration-quality-degradation-detector.sh +170 -0
  322. package/examples/java-compile-on-edit.sh +1 -1
  323. package/examples/json-syntax-check.sh +0 -0
  324. package/examples/k8s-production-guard.sh +13 -1
  325. package/examples/kill-process-guard.sh +12 -0
  326. package/examples/kubernetes-guard.sh +13 -1
  327. package/examples/laravel-artisan-guard.sh +13 -0
  328. package/examples/large-file-guard.sh +1 -1
  329. package/examples/log-level-guard.sh +0 -0
  330. package/examples/log-truncation-guard.sh +12 -0
  331. package/examples/long-session-malformed-tool-call-detector.sh +183 -0
  332. package/examples/loop-detector.sh +12 -0
  333. package/examples/magic-number-warn.sh +1 -0
  334. package/examples/main-branch-warn.sh +12 -0
  335. package/examples/markdown-link-check.sh +0 -0
  336. package/examples/max-concurrent-agents.sh +12 -0
  337. package/examples/max-edit-size-guard.sh +13 -0
  338. package/examples/max-file-count-guard.sh +0 -0
  339. package/examples/max-file-delete-count.sh +0 -0
  340. package/examples/max-function-length.sh +1 -0
  341. package/examples/max-import-count.sh +0 -0
  342. package/examples/max-subagent-count.sh +0 -0
  343. package/examples/mcp-config-freeze.sh +12 -0
  344. package/examples/mcp-config-poisoning-audit.sh +150 -0
  345. package/examples/mcp-data-boundary.sh +1 -1
  346. package/examples/mcp-misdiagnosis-arrest.sh +209 -0
  347. package/examples/mcp-orphan-leak-warner.sh +49 -0
  348. package/examples/mcp-permissions-ask-warner.sh +151 -0
  349. package/examples/mcp-routine-approval-detector.sh +154 -0
  350. package/examples/mcp-server-allowlist.sh +12 -0
  351. package/examples/mcp-server-guard.sh +12 -0
  352. package/examples/mcp-startup-bloat-detector.sh +87 -0
  353. package/examples/mcp-stdio-compatibility-test.sh +153 -0
  354. package/examples/mcp-tool-audit-log.sh +0 -0
  355. package/examples/mcp-tool-guard.sh +101 -1
  356. package/examples/mcp-warmup-wait.sh +0 -0
  357. package/examples/memory-chain-audit.sh +56 -0
  358. package/examples/memory-orphan-detector.sh +188 -0
  359. package/examples/memory-write-guard.sh +99 -17
  360. package/examples/migration-verify-guard.sh +12 -0
  361. package/examples/model-swap-suggester.sh +177 -0
  362. package/examples/model-version-alert.sh +5 -1
  363. package/examples/model-version-lock.sh +63 -0
  364. package/examples/move-delete-sequence-guard.sh +33 -2
  365. package/examples/multi-file-plan-routing-gate.sh +192 -0
  366. package/examples/multi-vendor-concurrent-warner.sh +108 -0
  367. package/examples/multi-window-auth-drift-detector.sh +183 -0
  368. package/examples/multiline-command-approver.sh +161 -23
  369. package/examples/nested-background-agent-guard.sh +109 -0
  370. package/examples/nested-spawn-inflight-guard.sh +178 -0
  371. package/examples/network-exfil-guard.sh +12 -0
  372. package/examples/network-guard.sh +12 -0
  373. package/examples/network-interface-guard.sh +12 -0
  374. package/examples/nextjs-env-guard.sh +1 -1
  375. package/examples/no-absolute-import.sh +0 -0
  376. package/examples/no-alert-confirm-prompt.sh +0 -0
  377. package/examples/no-anonymous-default-export.sh +0 -0
  378. package/examples/no-any-type.sh +0 -0
  379. package/examples/no-any-typescript.sh +1 -0
  380. package/examples/no-ask-human.sh +12 -0
  381. package/examples/no-assignment-in-condition.sh +0 -0
  382. package/examples/no-base64-exfil.sh +12 -0
  383. package/examples/no-callback-hell.sh +0 -0
  384. package/examples/no-circular-dependency.sh +0 -0
  385. package/examples/no-cleartext-storage.sh +0 -0
  386. package/examples/no-commented-code.sh +0 -0
  387. package/examples/no-commit-fixup.sh +0 -0
  388. package/examples/no-console-assert.sh +0 -0
  389. package/examples/no-console-error-swallow.sh +0 -0
  390. package/examples/no-console-in-prod.sh +0 -0
  391. package/examples/no-console-log-commit.sh +12 -0
  392. package/examples/no-console-log.sh +0 -0
  393. package/examples/no-console-time.sh +0 -0
  394. package/examples/no-cors-wildcard.sh +4 -1
  395. package/examples/no-curl-upload.sh +0 -0
  396. package/examples/no-dangerouslySetInnerHTML.sh +0 -0
  397. package/examples/no-dangling-await.sh +1 -0
  398. package/examples/no-debug-commit.sh +12 -0
  399. package/examples/no-debug-in-commit.sh +0 -0
  400. package/examples/no-deep-nesting.sh +0 -0
  401. package/examples/no-deep-relative-import.sh +1 -0
  402. package/examples/no-default-credentials.sh +0 -0
  403. package/examples/no-deploy-friday.sh +13 -1
  404. package/examples/no-disabled-test.sh +0 -0
  405. package/examples/no-document-write.sh +0 -0
  406. package/examples/no-empty-function.sh +0 -0
  407. package/examples/no-eval-in-template.sh +0 -0
  408. package/examples/no-eval-template.sh +3 -1
  409. package/examples/no-eval.sh +0 -0
  410. package/examples/no-exec-user-input.sh +0 -0
  411. package/examples/no-fixme-ship.sh +12 -0
  412. package/examples/no-floating-promises.sh +0 -0
  413. package/examples/no-force-flag.sh +22 -5
  414. package/examples/no-force-install.sh +0 -0
  415. package/examples/no-git-amend.sh +12 -0
  416. package/examples/no-git-rebase-public.sh +0 -0
  417. package/examples/no-global-install.sh +12 -0
  418. package/examples/no-global-state.sh +0 -0
  419. package/examples/no-hardcoded-port.sh +1 -0
  420. package/examples/no-hardcoded-url.sh +0 -0
  421. package/examples/no-helmet-missing.sh +0 -0
  422. package/examples/no-http-url.sh +1 -0
  423. package/examples/no-http-without-https.sh +0 -0
  424. package/examples/no-inline-style.sh +0 -0
  425. package/examples/no-inline-styles.sh +1 -0
  426. package/examples/no-innerhtml.sh +0 -0
  427. package/examples/no-install-global.sh +12 -0
  428. package/examples/no-jwt-in-url.sh +0 -0
  429. package/examples/no-large-commit.sh +0 -0
  430. package/examples/no-localhost-expose.sh +0 -0
  431. package/examples/no-magic-number.sh +0 -0
  432. package/examples/no-md5-sha1.sh +0 -0
  433. package/examples/no-mixed-line-endings.sh +8 -1
  434. package/examples/no-mutation-in-reducer.sh +0 -0
  435. package/examples/no-nested-ternary.sh +0 -0
  436. package/examples/no-network-exfil.sh +0 -0
  437. package/examples/no-open-redirect.sh +0 -0
  438. package/examples/no-output-truncation.sh +12 -0
  439. package/examples/no-package-downgrade.sh +0 -0
  440. package/examples/no-package-lock-edit.sh +12 -0
  441. package/examples/no-path-join-user-input.sh +0 -0
  442. package/examples/no-port-bind.sh +0 -0
  443. package/examples/no-process-exit.sh +0 -0
  444. package/examples/no-prototype-pollution.sh +0 -0
  445. package/examples/no-push-without-ci.sh +0 -0
  446. package/examples/no-push-without-tests.sh +12 -0
  447. package/examples/no-raw-password-in-url.sh +0 -0
  448. package/examples/no-root-user-docker.sh +1 -0
  449. package/examples/no-root-write.sh +12 -0
  450. package/examples/no-secrets-in-args.sh +4 -1
  451. package/examples/no-secrets-in-logs.sh +0 -0
  452. package/examples/no-sensitive-log.sh +0 -0
  453. package/examples/no-sleep-in-hooks.sh +0 -0
  454. package/examples/no-star-import-python.sh +1 -0
  455. package/examples/no-string-concat-sql.sh +0 -0
  456. package/examples/no-sudo-guard.sh +12 -0
  457. package/examples/no-sync-fs.sh +0 -0
  458. package/examples/no-todo-in-merge.sh +0 -0
  459. package/examples/no-todo-in-production.sh +1 -0
  460. package/examples/no-todo-without-issue.sh +0 -0
  461. package/examples/no-unused-import.sh +0 -0
  462. package/examples/no-var-keyword.sh +0 -0
  463. package/examples/no-verify-blocker.sh +12 -0
  464. package/examples/no-wget-piped-bash.sh +12 -0
  465. package/examples/no-wildcard-cors.sh +0 -0
  466. package/examples/no-wildcard-delete.sh +0 -0
  467. package/examples/no-wildcard-import.sh +0 -0
  468. package/examples/no-with-statement.sh +0 -0
  469. package/examples/no-write-outside-src.sh +0 -0
  470. package/examples/no-xml-external-entity.sh +0 -0
  471. package/examples/non-english-quality-warner.sh +156 -0
  472. package/examples/notify-waiting.sh +1 -1
  473. package/examples/npm-audit-warn.sh +0 -0
  474. package/examples/npm-global-install-guard.sh +12 -0
  475. package/examples/npm-publish-guard.sh +12 -0
  476. package/examples/npm-script-injection.sh +0 -0
  477. package/examples/npm-supply-chain-guard.sh +0 -0
  478. package/examples/nuxt-config-guard.sh +1 -0
  479. package/examples/oauth-refresh-monitor.sh +210 -0
  480. package/examples/opus-degradation-tracker.sh +187 -0
  481. package/examples/opus48-routine-task-warning.sh +196 -0
  482. package/examples/opus48-thinking-wedge-advisor.sh +143 -0
  483. package/examples/output-credential-scan.sh +1 -1
  484. package/examples/output-secret-mask.sh +31 -9
  485. package/examples/output-token-spike-detector.sh +145 -0
  486. package/examples/package-json-guard.sh +12 -0
  487. package/examples/package-lock-frozen.sh +12 -0
  488. package/examples/parallel-batch-size-limiter.sh +121 -0
  489. package/examples/parallel-cascade-detector.sh +116 -0
  490. package/examples/partial-view-claim-arrest.sh +174 -0
  491. package/examples/path-deny-bash-guard.sh +12 -0
  492. package/examples/path-traversal-guard.sh +12 -0
  493. package/examples/per-prompt-token-cap.sh +124 -0
  494. package/examples/permission-audit-log.sh +1 -1
  495. package/examples/permission-denial-enforcer.sh +12 -0
  496. package/examples/permission-entry-validator.sh +0 -0
  497. package/examples/persisted-1m-model-advisor.sh +88 -0
  498. package/examples/php-lint-on-edit.sh +1 -1
  499. package/examples/pii-upload-guard.sh +12 -0
  500. package/examples/pip-publish-guard.sh +13 -0
  501. package/examples/pip-requirements-guard.sh +12 -0
  502. package/examples/pip-venv-required.sh +12 -0
  503. package/examples/plan-mode-edit-guard.sh +12 -0
  504. package/examples/plan-mode-enforcer.sh +12 -0
  505. package/examples/plan-mode-strict-guard.sh +12 -0
  506. package/examples/plugin-hooks-json-bloat-detector.sh +169 -0
  507. package/examples/plugin-process-cleanup.sh +0 -0
  508. package/examples/polyglot-rm-guard.sh +12 -0
  509. package/examples/post-compact-safety.sh +12 -0
  510. package/examples/post-edit-disk-verify.sh +152 -0
  511. package/examples/powershell-remove-item-guard.sh +85 -0
  512. package/examples/pr-description-check.sh +0 -0
  513. package/examples/pr-duplicate-guard.sh +13 -0
  514. package/examples/pre-bash-sed-line-ending-windows.sh +87 -0
  515. package/examples/pre-compact-knowledge-save.sh +0 -0
  516. package/examples/pre-compact-transcript-backup.sh +0 -0
  517. package/examples/pre-execution-claim-detector.sh +179 -0
  518. package/examples/prefer-builtin-tools.sh +10 -1
  519. package/examples/prefer-const.sh +0 -0
  520. package/examples/prefer-dedicated-tools.sh +12 -0
  521. package/examples/prefer-optional-chaining.sh +0 -0
  522. package/examples/prisma-migrate-guard.sh +24 -7
  523. package/examples/production-port-kill-guard.sh +12 -0
  524. package/examples/prompt-injection-guard.sh +2 -1
  525. package/examples/prompt-usage-logger.sh +0 -0
  526. package/examples/protect-claudemd.sh +12 -0
  527. package/examples/protect-dotfiles.sh +12 -0
  528. package/examples/proxy-capture-suggester.sh +159 -0
  529. package/examples/public-artefact-socratic-narrowing.sh +232 -0
  530. package/examples/public-repo-push-guard.sh +12 -0
  531. package/examples/push-requires-test-pass-record.sh +0 -0
  532. package/examples/push-requires-test-pass.sh +12 -0
  533. package/examples/quota-anomaly-detector.sh +173 -0
  534. package/examples/quota-reset-audit.sh +58 -0
  535. package/examples/quoted-flag-approver.sh +99 -10
  536. package/examples/rails-migration-guard.sh +12 -0
  537. package/examples/rate-limit-guard.sh +0 -0
  538. package/examples/read-all-files-enforcer.sh +0 -0
  539. package/examples/read-budget-guard.sh +12 -0
  540. package/examples/read-loop-detector.sh +59 -0
  541. package/examples/read-once-guard.sh +96 -0
  542. package/examples/read-only-mode.sh +12 -0
  543. package/examples/readme-exists-check.sh +0 -0
  544. package/examples/record-read-coverage.sh +73 -0
  545. package/examples/redirect-fragment-warner.sh +149 -0
  546. package/examples/redis-flushall-guard.sh +13 -0
  547. package/examples/redundant-read-blocker.sh +132 -0
  548. package/examples/refusal-arrest-gate.sh +248 -0
  549. package/examples/registry-publish-guard.sh +13 -1
  550. package/examples/remote-control-billing-classifier.sh +135 -0
  551. package/examples/replace-all-guard.sh +12 -0
  552. package/examples/repo-visibility-guard.sh +12 -0
  553. package/examples/reroute-after-block-guard.sh +150 -0
  554. package/examples/response-budget-guard.sh +0 -0
  555. package/examples/resume-drift-watcher.sh +57 -0
  556. package/examples/rg-replace-flag-detector.sh +125 -0
  557. package/examples/rhetorical-verification-prompt-detector.sh +102 -0
  558. package/examples/ripgrep-permission-fix.sh +0 -0
  559. package/examples/rm-safety-net.sh +151 -8
  560. package/examples/role-tool-guard.sh +12 -0
  561. package/examples/route-handler-emptiness-gate.sh +324 -0
  562. package/examples/ruby-lint-on-edit.sh +1 -1
  563. package/examples/runtime-binary-change-detector.sh +102 -0
  564. package/examples/same-command-repeat-detector.sh +196 -0
  565. package/examples/same-correction-arrest.sh +169 -0
  566. package/examples/sandbox-denywrite-enforcer.sh +78 -0
  567. package/examples/sandbox-relative-path-audit.sh +83 -25
  568. package/examples/sandbox-write-verify.sh +12 -0
  569. package/examples/schema-migration-guard.sh +0 -0
  570. package/examples/scope-expansion-receipt.sh +178 -0
  571. package/examples/scope-guard.sh +88 -9
  572. package/examples/secret-file-read-guard.sh +12 -0
  573. package/examples/secret-file-write-guard.sh +150 -0
  574. package/examples/self-modify-bypass-guard.sh +0 -0
  575. package/examples/sensitive-file-read-guard.sh +23 -3
  576. package/examples/sensitive-log-guard.sh +1 -0
  577. package/examples/server-side-prompt-injection-detector.sh +69 -0
  578. package/examples/session-agent-cost-limiter.sh +12 -0
  579. package/examples/session-backup-on-start.sh +1 -1
  580. package/examples/session-cost-alert.sh +12 -0
  581. package/examples/session-drift-guard.sh +12 -0
  582. package/examples/session-duration-guard.sh +25 -2
  583. package/examples/session-end-logger.sh +0 -0
  584. package/examples/session-health-monitor.sh +0 -0
  585. package/examples/session-index-repair.sh +1 -1
  586. package/examples/session-memory-watchdog.sh +1 -0
  587. package/examples/session-permission-reset-guard.sh +12 -0
  588. package/examples/session-persistence-verifier.sh +93 -0
  589. package/examples/session-plugin-pin-watch.sh +68 -0
  590. package/examples/session-rate-monitor.sh +176 -0
  591. package/examples/session-resume-env-fix.sh +0 -0
  592. package/examples/session-start-quota-status.sh +193 -0
  593. package/examples/session-start-safety-check.sh +55 -3
  594. package/examples/session-state-saver.sh +0 -0
  595. package/examples/session-summary-stop.sh +1 -1
  596. package/examples/session-summary.sh +0 -0
  597. package/examples/session-token-counter.sh +1 -1
  598. package/examples/settings-hooks-key-restorer.sh +99 -0
  599. package/examples/settings-json-health-check.sh +57 -0
  600. package/examples/settings-json-model-guard.sh +0 -0
  601. package/examples/settings-regression-tester.sh +188 -0
  602. package/examples/shell-config-truncation-guard.sh +12 -0
  603. package/examples/shell-wrapper-guard.sh +12 -0
  604. package/examples/skill-cumulative-size-detector.sh +104 -0
  605. package/examples/skill-description-drop-detector.sh +148 -0
  606. package/examples/skill-gate.sh +0 -0
  607. package/examples/skill-truncation-detector.sh +154 -0
  608. package/examples/skills-context-recorder.sh +167 -0
  609. package/examples/skills-load-verifier.sh +96 -0
  610. package/examples/skills-settings-validator.sh +139 -0
  611. package/examples/sonnet-45-deprecation-helper.sh +85 -0
  612. package/examples/spec-file-scope-guard.sh +12 -0
  613. package/examples/spring-profile-guard.sh +1 -0
  614. package/examples/spurious-malformed-notice-detector.sh +247 -0
  615. package/examples/sql-bulk-delete-warn.sh +88 -0
  616. package/examples/sql-injection-detect.sh +0 -0
  617. package/examples/ssh-key-protect.sh +12 -0
  618. package/examples/staged-secret-scan.sh +22 -1
  619. package/examples/stale-temp-settings-detector.sh +86 -0
  620. package/examples/standing-rule-session-surfacer.sh +137 -0
  621. package/examples/stop-hook-sigterm-wrapper.sh +182 -0
  622. package/examples/stop-transcript-write-health.sh +64 -0
  623. package/examples/strict-allowlist.sh +57 -15
  624. package/examples/strip-coauthored-by.sh +50 -10
  625. package/examples/subagent-blast-radius-guard.sh +184 -0
  626. package/examples/subagent-boundary-precheck.sh +126 -0
  627. package/examples/subagent-budget-guard.sh +12 -0
  628. package/examples/subagent-claudemd-inject.sh +0 -0
  629. package/examples/subagent-closure-verify-gate.sh +164 -0
  630. package/examples/subagent-destructive-git-guard.sh +120 -0
  631. package/examples/subagent-error-detector.sh +0 -0
  632. package/examples/subagent-forged-system-reminder-guard.sh +156 -0
  633. package/examples/subagent-identity-leak-guard.sh +102 -0
  634. package/examples/subagent-identity-reassertion.sh +51 -0
  635. package/examples/subagent-inheritance-tester.sh +179 -0
  636. package/examples/subagent-permission-mode-guard.sh +110 -0
  637. package/examples/subagent-scope-guard.sh +12 -0
  638. package/examples/subagent-spawn-verification-enforcer.sh +102 -0
  639. package/examples/subagent-tool-allowlist-enforcer.sh +101 -0
  640. package/examples/subagent-tool-call-limiter.sh +87 -15
  641. package/examples/subcommand-chain-guard.sh +12 -0
  642. package/examples/subscription-api-billing-warner.sh +160 -0
  643. package/examples/subscription-bypass-detector.sh +152 -0
  644. package/examples/svelte-lint-on-edit.sh +1 -0
  645. package/examples/swift-build-on-edit.sh +1 -1
  646. package/examples/symlink-guard.sh +27 -2
  647. package/examples/symlink-protect.sh +1 -0
  648. package/examples/system-dir-protection-guard.sh +73 -8
  649. package/examples/system-message-workaround.sh +38 -13
  650. package/examples/system-package-guard.sh +12 -0
  651. package/examples/systemd-service-guard.sh +12 -0
  652. package/examples/task-integrity-guard.sh +12 -0
  653. package/examples/temp-file-cleanup.sh +1 -1
  654. package/examples/temporal-suggestion-detector.sh +135 -0
  655. package/examples/terraform-guard.sh +27 -6
  656. package/examples/test-after-edit.sh +1 -1
  657. package/examples/test-before-push.sh +12 -0
  658. package/examples/test-coverage-reminder.sh +24 -1
  659. package/examples/thinking-budget-effort-mismatch-detector.sh +149 -0
  660. package/examples/timezone-guard.sh +0 -0
  661. package/examples/tmp-output-size-guard.sh +0 -0
  662. package/examples/todo-check.sh +1 -1
  663. package/examples/token-budget-guard.sh +12 -0
  664. package/examples/token-budget-per-task.sh +12 -0
  665. package/examples/token-usage-tracker.sh +7 -1
  666. package/examples/tokenizer-ratio-alert-corpus.sh +90 -0
  667. package/examples/tokenizer-ratio-alert.sh +104 -0
  668. package/examples/tool-call-rate-limiter.sh +0 -0
  669. package/examples/tool-result-correlation-checker.sh +167 -0
  670. package/examples/tool-result-size-guard.sh +132 -0
  671. package/examples/tool-retry-budget-guard.sh +12 -0
  672. package/examples/transcript-contamination-detector.sh +138 -0
  673. package/examples/trustfall-mcp-injection-guard.sh +103 -0
  674. package/examples/ultrareview-large-diff-advisor.sh +184 -0
  675. package/examples/unbounded-output-guard.sh +94 -0
  676. package/examples/uncommitted-changes-stop.sh +1 -0
  677. package/examples/uncommitted-discard-guard.sh +23 -5
  678. package/examples/uncommitted-work-guard.sh +12 -0
  679. package/examples/usage-warn.sh +0 -0
  680. package/examples/user-account-guard.sh +12 -0
  681. package/examples/userprompt-submit-receipt.sh +123 -0
  682. package/examples/variable-expansion-guard.sh +12 -0
  683. package/examples/verify-after-publish-reminder.sh +93 -0
  684. package/examples/verify-before-commit.sh +12 -0
  685. package/examples/version-bump-detector.sh +156 -0
  686. package/examples/version-regression-warner.sh +144 -0
  687. package/examples/vue-lint-on-edit.sh +1 -0
  688. package/examples/warn-cron-cost-trap.sh +173 -0
  689. package/examples/webfetch-runaway-guard.sh +89 -0
  690. package/examples/wellness-break-reminder.sh +118 -0
  691. package/examples/windows-destructive-command-guard.sh +177 -0
  692. package/examples/windows-path-guard.sh +12 -0
  693. package/examples/windows-python-stub-detector.sh +63 -0
  694. package/examples/work-hours-guard.sh +12 -0
  695. package/examples/working-directory-fence.sh +12 -0
  696. package/examples/workspace-lease-guard.sh +205 -0
  697. package/examples/worktree-create-log.sh +1 -0
  698. package/examples/worktree-delete-guard.sh +12 -0
  699. package/examples/worktree-edit-boundary-guard.sh +127 -0
  700. package/examples/worktree-escape-write-guard.sh +114 -0
  701. package/examples/worktree-hook-linker.sh +0 -0
  702. package/examples/worktree-hooks-path-fix.sh +111 -0
  703. package/examples/worktree-parent-write-guard.sh +43 -0
  704. package/examples/worktree-project-unify.sh +2 -1
  705. package/examples/worktree-remove-uncommitted-guard.sh +13 -0
  706. package/examples/worktree-unmerged-guard.sh +7 -1
  707. package/examples/write-byte-integrity-verifier.sh +190 -0
  708. package/examples/write-empty-content-guard.sh +67 -0
  709. package/examples/write-nul-corruption-detector.sh +61 -0
  710. package/examples/write-overwrite-confirm.sh +160 -14
  711. package/examples/write-secret-guard.sh +27 -5
  712. package/examples/write-shrink-guard.sh +12 -0
  713. package/examples/write-test-ratio.sh +0 -0
  714. package/examples/wsl-host-disk-space-guard.sh +97 -0
  715. package/examples/xml-format-leak-detector.sh +266 -0
  716. package/examples/yaml-syntax-check.sh +12 -0
  717. package/hooks/hooks.json +6 -6
  718. package/index.mjs +1699 -183
  719. package/mission.md +2 -0
  720. package/package.json +4 -4
  721. package/plugins/credential-guard/.claude-plugin/plugin.json +9 -7
  722. package/plugins/git-protection/.claude-plugin/plugin.json +10 -8
  723. package/plugins/safety-essentials/.claude-plugin/plugin.json +9 -7
  724. package/plugins/token-guard/.claude-plugin/plugin.json +10 -8
  725. package/scripts/agents-md-sync-setup.sh +156 -0
  726. package/scripts/autonomy-blocked-summary.sh +148 -0
  727. package/scripts/build-search-index.py +43 -0
  728. package/scripts/build-sitemap.py +119 -0
  729. package/scripts/cc-doctor.sh +192 -0
  730. package/scripts/check-hook-event-names.py +127 -0
  731. package/scripts/check-misfire-instrument.sh +64 -0
  732. package/scripts/claim-verify-audit.sh +467 -0
  733. package/scripts/cowork-claudemd-helper.sh +136 -0
  734. package/scripts/june-15-deprecated-model-scan.sh +111 -0
  735. package/scripts/measure-heredoc-shapes.sh +67 -0
  736. package/scripts/measure-literal-match-misfire.sh +110 -0
  737. package/scripts/probe-scope-guard-branches.sh +48 -0
  738. package/scripts/probe-skip-line-both-ways.sh +65 -0
  739. package/scripts/receipts-aggregate.py +199 -0
  740. package/scripts/session-forensic-audit.py +360 -0
  741. package/scripts.json +9 -9
  742. package/scripts.json.bak-2026-07-27-find +13 -0
  743. package/skills/safety-setup/SKILL.md +5 -5
  744. package/tests/approve-side-remaining-two.test.sh +129 -0
  745. package/tests/approver-list-granularity.test.sh +145 -0
  746. package/tests/askuserquestion-autonomy-gate.test.sh +181 -0
  747. package/tests/audit-ci-gate.test.sh +87 -0
  748. package/tests/audit-cross-layer-claudemd.test.sh +170 -0
  749. package/tests/audit-fail-open-parse.test.sh +100 -0
  750. package/tests/auto-approve-compound-tail.test.sh +194 -0
  751. package/tests/auto-mode-path-invoked-commands.test.sh +89 -0
  752. package/tests/auto-mode-quoted-targets.test.sh +127 -0
  753. package/tests/auto-mode-unquoted-targets.test.sh +112 -0
  754. package/tests/bash-secret-output-detector-detects.test.sh +102 -0
  755. package/tests/blindspots-cross-layer.test.sh +177 -0
  756. package/tests/block-database-wipe.test.sh +64 -0
  757. package/tests/bundled-short-flags-bypass.test.sh +85 -0
  758. package/tests/cd-git-allow-compound-tail.test.sh +67 -0
  759. package/tests/chown-guard-filesystem-root.test.sh +64 -0
  760. package/tests/ci-workflow-guard-skip-rule.test.sh +115 -0
  761. package/tests/compound-deny-safe-rm-targets.test.sh +105 -0
  762. package/tests/core-scripts-parser-fallback.test.sh +140 -0
  763. package/tests/destructive-guard-bare-glob.test.sh +74 -0
  764. package/tests/destructive-guard-every-operand.test.sh +100 -0
  765. package/tests/destructive-guard-find-deletion.test.sh +114 -0
  766. package/tests/destructive-guard-flag-order.test.sh +106 -0
  767. package/tests/destructive-guard-mentions-vs-invocations.test.sh +107 -0
  768. package/tests/destructive-guard-quoted-target.test.sh +89 -0
  769. package/tests/destructive-guard-separators.test.sh +105 -0
  770. package/tests/destructive-guard-variable-target.test.sh +98 -0
  771. package/tests/destructive-guard-wrapped-command.test.sh +91 -0
  772. package/tests/doctor-hook-disabling-env.test.sh +92 -0
  773. package/tests/doctor-misspelled-settings-keys.test.sh +106 -0
  774. package/tests/example-guards-wrapped-command.test.sh +169 -0
  775. package/tests/file-edit-backup.test.sh +93 -0
  776. package/tests/git-filter-repo-guard.test.sh +60 -0
  777. package/tests/git-global-options-bypass.test.sh +141 -0
  778. package/tests/git-invocation-prefixes-all-guards.test.sh +114 -0
  779. package/tests/git-maintenance-guard.test.sh +83 -0
  780. package/tests/git-pack-temp-cleanup-guard.test.sh +133 -0
  781. package/tests/git-rm-orphan-wipe-guard.test.sh +66 -0
  782. package/tests/git-submodule-guard.test.sh +50 -0
  783. package/tests/grep-pcre-portability.test.sh +155 -0
  784. package/tests/hook-registration-matches-input.test.sh +73 -0
  785. package/tests/hook-trigger-matches-envelope.test.sh +178 -0
  786. package/tests/install-does-not-claim-protection.test.sh +110 -0
  787. package/tests/install-example-standalone-tools.test.sh +139 -0
  788. package/tests/line-continuation-bypass.test.sh +114 -0
  789. package/tests/mcp-config-poisoning-audit.test.sh +106 -0
  790. package/tests/nested-background-agent-guard.test.sh +58 -0
  791. package/tests/no-unqualified-safety-claims.test.sh +114 -0
  792. package/tests/option-eating-grep-patterns.test.sh +130 -0
  793. package/tests/outdated-core-guards.test.sh +134 -0
  794. package/tests/outdated-report.test.sh +155 -0
  795. package/tests/output-secret-mask-warns.test.sh +73 -0
  796. package/tests/plugin-hooks-fail-open-under-sh.test.sh +165 -0
  797. package/tests/plugins-word-order.test.sh +126 -0
  798. package/tests/powershell-remove-item-guard.test.sh +85 -0
  799. package/tests/project-dir-naming.test.sh +103 -0
  800. package/tests/quoted-safe-targets-and-mentions.test.sh +190 -0
  801. package/tests/reroute-after-block-guard.test.sh +104 -0
  802. package/tests/secret-guard-documented-promises.test.sh +97 -0
  803. package/tests/session-plugin-pin-watch.test.sh +45 -0
  804. package/tests/settings-unreadable-refuses-to-write.test.sh +95 -0
  805. package/tests/shield-no-silent-adoption.test.py +110 -0
  806. package/tests/shield-no-silent-adoption.test.sh +46 -0
  807. package/tests/stop-transcript-write-health.test.sh +46 -0
  808. package/tests/system-dir-guard-root-erased.test.sh +66 -0
  809. package/tests/test-account-billing-log.sh +100 -0
  810. package/tests/test-account-routing-preflight.sh +77 -0
  811. package/tests/test-agent-sdk-credit-pool-monitor.sh +157 -0
  812. package/tests/test-agent-view-quota-warn.sh +184 -0
  813. package/tests/test-agents-md-edit-drift-warner.sh +176 -0
  814. package/tests/test-agents-md-loader.sh +204 -0
  815. package/tests/test-agents-md-sync-checker.sh +125 -0
  816. package/tests/test-ai-slop-punctuation-arrest.sh +280 -0
  817. package/tests/test-allowlist.sh +60 -0
  818. package/tests/test-always-allow-pattern-suggester.sh +147 -0
  819. package/tests/test-anthropic-base-url-guard.sh +88 -0
  820. package/tests/test-articulated-scope-capture.sh +139 -0
  821. package/tests/test-attachment-traversal-guard.sh +168 -0
  822. package/tests/test-aup-block-pattern-logger.sh +315 -0
  823. package/tests/test-aup-false-positive-helper.sh +188 -0
  824. package/tests/test-aup-large-tool-output-warner.sh +370 -0
  825. package/tests/test-aup-retry-loop-guard.sh +341 -0
  826. package/tests/test-auth-expiry-reminder.sh +303 -0
  827. package/tests/test-auth-macos-sleep-detector.sh +254 -0
  828. package/tests/test-auth-path-detector.sh +232 -0
  829. package/tests/test-auth-status-checker.sh +233 -0
  830. package/tests/test-authorized-reconfirmation-detector.sh +280 -0
  831. package/tests/test-background-cost-launch-guard.sh +62 -0
  832. package/tests/test-bash-allowlist-secondary-check.sh +143 -0
  833. package/tests/test-bash-fanout-bounded-rewriter.sh +186 -0
  834. package/tests/test-bash-project-fence.sh +127 -0
  835. package/tests/test-bg-permission-prompt-warner.sh +221 -0
  836. package/tests/test-binary-read-detector.sh +173 -0
  837. package/tests/test-broad-prefix-session-trap-warner.sh +167 -0
  838. package/tests/test-bulk-delete-target-resolution.sh +49 -0
  839. package/tests/test-bypass-mode-effective-verifier.sh +179 -0
  840. package/tests/test-cache-creation-drift-detector.sh +244 -0
  841. package/tests/test-cache-residue-detector.sh +429 -0
  842. package/tests/test-cache-tier-logger.sh +59 -0
  843. package/tests/test-cache-ttl-eviction-detector.sh +282 -0
  844. package/tests/test-cch-sentinel-precommit-guard.sh +64 -0
  845. package/tests/test-claim-verify-detector.sh +222 -0
  846. package/tests/test-claim-vs-caveat-checker.sh +134 -0
  847. package/tests/test-claude-code-version-pin-advisor.sh +353 -0
  848. package/tests/test-claude-md-reinjector.sh +173 -0
  849. package/tests/test-claude-md-size-monitor.sh +107 -0
  850. package/tests/test-claude-update-budget-guard.sh +132 -0
  851. package/tests/test-claude-update-smart.sh +91 -0
  852. package/tests/test-claudemd-tool-prohibition.sh +239 -0
  853. package/tests/test-cli-config-pinning-detector.sh +233 -0
  854. package/tests/test-cliff-countdown-advisor.sh +138 -0
  855. package/tests/test-closure-word-verify-gate.sh +158 -0
  856. package/tests/test-commit-all-scope-guard.sh +73 -0
  857. package/tests/test-commitment-carry-forward-arrest.sh +250 -0
  858. package/tests/test-compact-dispatch-watchdog.sh +147 -0
  859. package/tests/test-completion-claim-without-verification-detector.sh +326 -0
  860. package/tests/test-compound-bash-permission-resolver.sh +264 -0
  861. package/tests/test-compound-command-deny-enforcer.sh +67 -0
  862. package/tests/test-context-length-budget.sh +68 -0
  863. package/tests/test-context-usage-drift-alert.sh +52 -22
  864. package/tests/test-cost-incident-self-audit.sh +94 -0
  865. package/tests/test-cowork-claude-md-load-checker.sh +257 -0
  866. package/tests/test-cowork-claudemd-helper.sh +141 -0
  867. package/tests/test-cowork-fuse-staleness-watcher.sh +287 -0
  868. package/tests/test-cowork-hook-absence-warner.sh +230 -0
  869. package/tests/test-cowork-model-picker-advisor.sh +317 -0
  870. package/tests/test-credential-exfil-guard.sh +62 -0
  871. package/tests/test-cron-create-receipt.sh +165 -0
  872. package/tests/test-cross-product-mcp-spawn-detector.sh +112 -0
  873. package/tests/test-daemon-zombie-worker-surfacer.sh +71 -0
  874. package/tests/test-daily-cost-guard.sh +131 -0
  875. package/tests/test-deny-rule-integrity-verifier.sh +288 -0
  876. package/tests/test-deployment-readback-gate.sh +92 -0
  877. package/tests/test-deployment-readback-gh-adapter.sh +163 -0
  878. package/tests/test-disabled-feature-toggle-advisor.sh +78 -0
  879. package/tests/test-dispatch-allowlist-preflight.sh +385 -0
  880. package/tests/test-dispatch-liveness-watchdog.sh +319 -0
  881. package/tests/test-dispatch-receipt.sh +314 -0
  882. package/tests/test-dotenv-anthropic-key-billing-guard.sh +160 -0
  883. package/tests/test-echo-probe-spam-detector.sh +143 -0
  884. package/tests/test-edit-quote-ambiguity-guard.sh +75 -0
  885. package/tests/test-ephemeral-container-detector.sh +251 -0
  886. package/tests/test-evidence-claim-gate.sh +291 -0
  887. package/tests/test-extended-thinking-loop-guard.sh +289 -0
  888. package/tests/test-extended-thinking-resume-warning.sh +240 -0
  889. package/tests/test-extended-thinking-tool-use-mismatch-detector.sh +236 -0
  890. package/tests/test-fabricated-command-detector.sh +296 -0
  891. package/tests/test-false-disk-full-guard.sh +36 -0
  892. package/tests/test-feature-deprecation-detector.sh +117 -0
  893. package/tests/test-forbidden-response-phrase-detector.sh +246 -0
  894. package/tests/test-git-operations-require-approval.sh +53 -0
  895. package/tests/test-git-push-blast-radius-guard.sh +115 -0
  896. package/tests/test-goal-iteration-limit-warner.sh +128 -0
  897. package/tests/test-growthbook-flag-monitor.sh +428 -0
  898. package/tests/test-halt-signal-detector.sh +120 -0
  899. package/tests/test-hook-self-disable-detector.sh +232 -0
  900. package/tests/test-iteration-quality-degradation-detector.sh +244 -0
  901. package/tests/test-long-session-malformed-tool-call-detector.sh +185 -0
  902. package/tests/test-marketplace-plugins.sh +193 -0
  903. package/tests/test-mcp-misdiagnosis-arrest.sh +235 -0
  904. package/tests/test-mcp-orphan-leak-warner.sh +71 -0
  905. package/tests/test-mcp-permissions-ask-warner.sh +207 -0
  906. package/tests/test-mcp-routine-approval-detector.sh +229 -0
  907. package/tests/test-mcp-startup-bloat-detector.sh +141 -0
  908. package/tests/test-mcp-stdio-compatibility-test.sh +112 -0
  909. package/tests/test-memory-chain-audit.sh +59 -0
  910. package/tests/test-memory-orphan-detector.sh +227 -0
  911. package/tests/test-model-swap-suggester.sh +368 -0
  912. package/tests/test-model-version-lock.sh +83 -0
  913. package/tests/test-multi-file-plan-routing-gate.sh +277 -0
  914. package/tests/test-multi-window-auth-drift-detector.sh +262 -0
  915. package/tests/test-nested-spawn-inflight-guard.sh +281 -0
  916. package/tests/test-no-force-flag.sh +49 -0
  917. package/tests/test-non-english-quality-warner.sh +229 -0
  918. package/tests/test-oauth-refresh-monitor.sh +311 -0
  919. package/tests/test-opus-degradation-tracker.sh +274 -0
  920. package/tests/test-opus48-routine-task-warning.sh +201 -0
  921. package/tests/test-opus48-thinking-wedge-advisor.sh +249 -0
  922. package/tests/test-output-token-spike-detector.sh +315 -0
  923. package/tests/test-parallel-batch-size-limiter.sh +228 -0
  924. package/tests/test-parallel-cascade-detector.sh +191 -0
  925. package/tests/test-partial-view-claim-arrest.sh +272 -0
  926. package/tests/test-per-prompt-token-cap.sh +136 -0
  927. package/tests/test-persisted-1m-model-advisor.sh +71 -0
  928. package/tests/test-plugin-hooks-json-bloat-detector.sh +267 -0
  929. package/tests/test-post-edit-disk-verify.sh +294 -0
  930. package/tests/test-pre-bash-sed-line-ending-windows.sh +108 -0
  931. package/tests/test-pre-execution-claim-detector.sh +366 -0
  932. package/tests/test-private-key-backup-and-git-credentials.sh +100 -0
  933. package/tests/test-proxy-capture-suggester.sh +214 -0
  934. package/tests/test-public-artefact-socratic-narrowing.sh +346 -0
  935. package/tests/test-quota-anomaly-detector.sh +321 -0
  936. package/tests/test-quota-reset-audit.sh +81 -0
  937. package/tests/test-read-loop-detector.sh +137 -0
  938. package/tests/test-read-once-guard.sh +85 -0
  939. package/tests/test-receipts-aggregate.sh +143 -0
  940. package/tests/test-redirect-fragment-warner.sh +130 -0
  941. package/tests/test-redundant-read-blocker.sh +155 -0
  942. package/tests/test-refusal-arrest-gate.sh +303 -0
  943. package/tests/test-remote-control-billing-classifier.sh +125 -0
  944. package/tests/test-resume-drift-watcher.sh +65 -0
  945. package/tests/test-rg-replace-flag-detector.sh +175 -0
  946. package/tests/test-rhetorical-verification-prompt-detector.sh +237 -0
  947. package/tests/test-rm-safety-net.sh +115 -0
  948. package/tests/test-route-handler-emptiness-gate.sh +300 -0
  949. package/tests/test-runtime-binary-change-detector.sh +196 -0
  950. package/tests/test-same-command-repeat-detector.sh +165 -0
  951. package/tests/test-same-correction-arrest.sh +165 -0
  952. package/tests/test-scope-expansion-receipt.sh +207 -0
  953. package/tests/test-server-side-prompt-injection-detector.sh +181 -0
  954. package/tests/test-session-persistence-verifier.sh +128 -0
  955. package/tests/test-session-rate-monitor.sh +284 -0
  956. package/tests/test-session-start-quota-status.sh +210 -0
  957. package/tests/test-session-start-safety-check.sh +138 -0
  958. package/tests/test-settings-hooks-key-restorer.sh +257 -0
  959. package/tests/test-settings-json-health-check.sh +70 -0
  960. package/tests/test-settings-regression-tester.sh +197 -0
  961. package/tests/test-skill-cumulative-size-detector.sh +198 -0
  962. package/tests/test-skill-description-drop-detector.sh +236 -0
  963. package/tests/test-skill-truncation-detector.sh +263 -0
  964. package/tests/test-skills-context-recorder.sh +329 -0
  965. package/tests/test-skills-load-verifier.sh +184 -0
  966. package/tests/test-skills-settings-validator.sh +242 -0
  967. package/tests/test-sonnet-45-deprecation-helper.sh +150 -0
  968. package/tests/test-spurious-malformed-notice-detector.sh +290 -0
  969. package/tests/test-sql-bulk-delete-warn.sh +123 -0
  970. package/tests/test-ssh-key-protect.sh +81 -0
  971. package/tests/test-stale-temp-settings-detector.sh +161 -0
  972. package/tests/test-standing-rule-session-surfacer.sh +215 -0
  973. package/tests/test-starter-hooks-manifest.sh +96 -0
  974. package/tests/test-stop-hook-sigterm-wrapper.sh +258 -0
  975. package/tests/test-strict-allowlist.sh +72 -0
  976. package/tests/test-subagent-boundary-precheck.sh +137 -0
  977. package/tests/test-subagent-closure-verify-gate.sh +193 -0
  978. package/tests/test-subagent-destructive-git-guard.sh +86 -0
  979. package/tests/test-subagent-identity-leak-guard.sh +62 -0
  980. package/tests/test-subagent-identity-reassertion.sh +61 -0
  981. package/tests/test-subagent-inheritance-tester.sh +319 -0
  982. package/tests/test-subagent-permission-mode-guard.sh +61 -0
  983. package/tests/test-subagent-spawn-verification-enforcer.sh +60 -0
  984. package/tests/test-subagent-tool-allowlist-enforcer.sh +62 -0
  985. package/tests/test-subagent-tool-call-limiter.sh +97 -0
  986. package/tests/test-subscription-api-billing-warner.sh +138 -0
  987. package/tests/test-subscription-bypass-detector.sh +255 -0
  988. package/tests/test-temporal-suggestion-detector.sh +183 -0
  989. package/tests/test-thinking-budget-effort-mismatch-detector.sh +327 -0
  990. package/tests/test-tokenizer-ratio-alert-corpus.sh +77 -0
  991. package/tests/test-tokenizer-ratio-alert.sh +93 -0
  992. package/tests/test-tool-result-correlation-checker.sh +264 -0
  993. package/tests/test-tool-result-size-guard.sh +121 -0
  994. package/tests/test-transcript-contamination-detector.sh +264 -0
  995. package/tests/test-trigger-header-parsing.sh +151 -0
  996. package/tests/test-ultrareview-large-diff-advisor.sh +192 -0
  997. package/tests/test-userprompt-submit-receipt.sh +181 -0
  998. package/tests/test-verify-after-publish-reminder.sh +184 -0
  999. package/tests/test-version-bump-detector.sh +212 -0
  1000. package/tests/test-version-regression-warner.sh +165 -0
  1001. package/tests/test-warn-cron-cost-trap.sh +82 -0
  1002. package/tests/test-webfetch-runaway-guard.sh +77 -0
  1003. package/tests/test-wellness-break-reminder.sh +138 -0
  1004. package/tests/test-windows-destructive-command-guard.sh +169 -0
  1005. package/tests/test-windows-python-stub-detector.sh +146 -0
  1006. package/tests/test-worktree-edit-boundary-guard.sh +219 -0
  1007. package/tests/test-write-byte-integrity-verifier.sh +192 -0
  1008. package/tests/test-xml-format-leak-detector.sh +299 -0
  1009. package/tests/trustfall-mcp-injection-guard.test.sh +183 -0
  1010. package/tests/workspace-lease-guard.test.sh +144 -0
  1011. package/tests/worktree-escape-write-guard.test.sh +68 -0
  1012. package/tests/worktree-hooks-path-fix.test.sh +181 -0
  1013. package/tests/worktree-parent-write-guard.test.sh +51 -0
  1014. package/tests/write-empty-content-guard.test.sh +39 -0
  1015. package/tests/write-nul-corruption-detector.test.sh +29 -0
  1016. package/tests/write-overwrite-unread-content.test.sh +178 -0
  1017. package/tests/wsl-host-disk-space-guard.test.sh +55 -0
@@ -0,0 +1,103 @@
1
+ #!/bin/bash
2
+ # Hooks that look inside ~/.claude/projects/ must derive the directory name the
3
+ # way Claude Code actually writes it.
4
+ #
5
+ # Claude Code names a project directory by replacing every `/` in the absolute
6
+ # working directory with `-`. The leading slash becomes a leading dash, and it
7
+ # stays:
8
+ #
9
+ # /home/u/projects/app -> -home-u-projects-app
10
+ #
11
+ # Six shipped hooks stripped that leading dash, in two different spellings:
12
+ # `sed 's|/|-|g; s|^-||'` removes it after the conversion, `sed 's|^/||; s|/|-|g'`
13
+ # removes the slash before it. The path they built therefore never existed, and each
14
+ # hook has an early `[ ! -d "$SESSION_DIR" ] && exit 0` guard — so all six ran,
15
+ # exited 0, and did nothing. One of them is the session backup. It had never made
16
+ # a backup.
17
+ #
18
+ # The first sweep grepped for one spelling and found three. The other three were
19
+ # structurally invisible to that search — the same shape of mistake the hooks
20
+ # themselves had. That is why both spellings are controls below.
21
+ #
22
+ # Measured on 2026-08-12 against a real install:
23
+ # built by the old form: ~/.claude/projects/home-namakusa-projects-cc-loop (absent)
24
+ # built by the fixed form: ~/.claude/projects/-home-namakusa-projects-cc-loop (present)
25
+ #
26
+ # The control below is the point of this file. Asserting only that the fixed form
27
+ # is right would pass just as happily if the assertion were vacuous; the old form
28
+ # has to fail it.
29
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
30
+
31
+ PASS=0; FAIL=0
32
+
33
+ check() {
34
+ local label="$1" got="$2" want="$3"
35
+ if [ "$got" = "$want" ]; then
36
+ PASS=$((PASS + 1))
37
+ else
38
+ FAIL=$((FAIL + 1))
39
+ echo "FAIL: $label"
40
+ echo " got: $got"
41
+ echo " want: $want"
42
+ fi
43
+ }
44
+
45
+ # The naming rule, stated once.
46
+ name_of() { printf '%s' "$1" | sed 's|/|-|g'; }
47
+
48
+ for abs in /home/u/projects/app /home/namakusa/projects/cc-loop /tmp/x; do
49
+ want="$(printf '%s' "$abs" | tr '/' '-')"
50
+ check "leading dash kept: $abs" "$(name_of "$abs")" "$want"
51
+ done
52
+
53
+ # Control: the form that shipped must NOT satisfy the rule. If this ever passes,
54
+ # the assertions above are not testing anything.
55
+ old_form() { printf '%s' "$1" | sed 's|/|-|g; s|^-||'; }
56
+ for abs in /home/u/projects/app /tmp/x; do
57
+ if [ "$(old_form "$abs")" = "$(name_of "$abs")" ]; then
58
+ FAIL=$((FAIL + 1))
59
+ echo "FAIL: control did not discriminate for $abs"
60
+ else
61
+ PASS=$((PASS + 1))
62
+ fi
63
+ done
64
+
65
+ # The stripping shows up in two spellings. Searching for one of them is how the
66
+ # first sweep found three of the six: `s|^-||` removes the dash after the
67
+ # conversion, `s|^/||` removes the slash before it. Same result, different grep.
68
+ old_form_b() { printf '%s' "$1" | sed 's|^/||; s|/|-|g'; }
69
+ for abs in /home/u/projects/app /tmp/x; do
70
+ if [ "$(old_form_b "$abs")" = "$(name_of "$abs")" ]; then
71
+ FAIL=$((FAIL + 1))
72
+ echo "FAIL: control B did not discriminate for $abs"
73
+ else
74
+ PASS=$((PASS + 1))
75
+ fi
76
+ done
77
+
78
+ # The shipped hooks must not carry either spelling any more.
79
+ for f in examples/session-backup-on-start.sh \
80
+ examples/session-index-repair.sh \
81
+ examples/worktree-project-unify.sh \
82
+ examples/extended-thinking-loop-guard.sh \
83
+ examples/extended-thinking-resume-warning.sh \
84
+ examples/opus48-thinking-wedge-advisor.sh; do
85
+ if grep -qF 's|^-||' "$ROOT/$f" || grep -qF 's|^/||; s|/|-|g' "$ROOT/$f"; then
86
+ FAIL=$((FAIL + 1))
87
+ echo "FAIL: $f still strips the leading separator"
88
+ else
89
+ PASS=$((PASS + 1))
90
+ fi
91
+ # And they must still build the name at all — a hook that stopped deriving a
92
+ # path would also pass the grep above.
93
+ if grep -qF "sed 's|/|-|g'" "$ROOT/$f"; then
94
+ PASS=$((PASS + 1))
95
+ else
96
+ FAIL=$((FAIL + 1))
97
+ echo "FAIL: $f no longer derives a project directory name"
98
+ fi
99
+ done
100
+
101
+ echo
102
+ echo "project-dir-naming: $PASS passed, $FAIL failed"
103
+ [ "$FAIL" -eq 0 ]
@@ -0,0 +1,190 @@
1
+ #!/bin/bash
2
+ # The two guards that still judged a target by its quotes, and the mention that
3
+ # one of them blocked.
4
+ #
5
+ # Measured 2026-08-11 (before this change), same eight inputs through three hooks:
6
+ #
7
+ # auto-mode rm-safety-net compound-deny
8
+ # a quoted safe target ok BLOCKED BLOCKED
9
+ # a quoted safe target (single) ok BLOCKED BLOCKED
10
+ # a quoted safe target (dist) ok BLOCKED BLOCKED
11
+ # a mention in echo ok ok BLOCKED
12
+ #
13
+ # auto-mode-safety-enforcer had just been fixed (PR #1009). The other two had
14
+ # not, and they fail for two different reasons:
15
+ #
16
+ # rm-safety-net blocks anything that is not on its safe list, so a quote made
17
+ # an ordinary cleanup fail the same test a dangerous path fails -- the quote,
18
+ # not the path, decided it.
19
+ #
20
+ # compound-command-deny-enforcer compares each operand against the safe list
21
+ # as a literal name, so a quoted name never matched, and the deny patterns
22
+ # then saw the whole line. It also had no mention-vs-invocation pass at all,
23
+ # which is why naming a command inside echo was blocked.
24
+ #
25
+ # Both now drop quote characters before judging a target. The compound guard did
26
+ # NOT receive the gate auto-mode got in PR #1009: it was tried and taken out,
27
+ # because that hook's deny patterns cover more than a list of destructive verbs
28
+ # can express, and a gate built from such a list let two real things through
29
+ # (a git subcommand not on the list, and a redirect writing to a device -- CI
30
+ # caught both). So a mention is still blocked there, and this file asserts that
31
+ # rather than pretending otherwise.
32
+ #
33
+ # --- known limit, asserted on purpose ----------------------------------------
34
+ # A name containing spaces is still blocked by both. That is word splitting, not
35
+ # quoting: the operands are cut apart on whitespace, so a quoted name with a
36
+ # space in it arrives as several words. Fixing it means rewriting how operands
37
+ # are extracted. The cases at the bottom record the current behaviour so that a
38
+ # future change to that code shows up here instead of passing unnoticed.
39
+ #
40
+ # Dangerous strings are assembled from parts so that reading or editing this
41
+ # file does not trip the operator's own destructive-guard.
42
+
43
+ set -u
44
+ DIR="$(cd "$(dirname "$0")/.." && pwd)/examples"
45
+ PASS=0
46
+ FAIL=0
47
+
48
+ run() { # hook command
49
+ printf '{"tool_name":"Bash","tool_input":{"command":%s}}' \
50
+ "$(printf '%s' "$2" | sed 's/\\/\\\\/g; s/"/\\"/g; s/^/"/; s/$/"/')" \
51
+ | bash "$DIR/$1.sh" >/dev/null 2>&1
52
+ echo $?
53
+ }
54
+
55
+ expect() { # hook description command expected_exit
56
+ actual="$(run "$1" "$3")"
57
+ if [ "$actual" = "$4" ]; then
58
+ PASS=$((PASS + 1))
59
+ else
60
+ FAIL=$((FAIL + 1))
61
+ echo " FAIL [$1]: $2"
62
+ echo " command: $3"
63
+ echo " expected: exit $4, got exit $actual"
64
+ fi
65
+ }
66
+
67
+ RM="r""m -rf"
68
+ SLASH="/"
69
+ DQ='"'
70
+ SQ="'"
71
+ HOME_VAR='$HOME'
72
+
73
+ for h in rm-safety-net compound-command-deny-enforcer auto-mode-safety-enforcer; do
74
+
75
+ # --- quoted safe targets must pass ----------------------------------------
76
+
77
+ expect "$h" "a quoted safe target is ordinary cleanup" \
78
+ "$RM ${DQ}node_modules${DQ}" 0
79
+ expect "$h" "a single-quoted safe target is ordinary cleanup" \
80
+ "$RM ${SQ}node_modules${SQ}" 0
81
+ expect "$h" "a quoted dist is ordinary cleanup" \
82
+ "$RM ${DQ}dist${DQ}" 0
83
+ expect "$h" "a quoted build is ordinary cleanup" \
84
+ "$RM ${DQ}build${DQ}" 0
85
+ expect "$h" "a bare safe target still passes" \
86
+ "$RM node_modules" 0
87
+ expect "$h" "cleanup after another command still passes" \
88
+ "npm ci && $RM ${DQ}node_modules${DQ}" 0
89
+
90
+ # --- mentions ---------------------------------------------------------------
91
+ # auto-mode-safety-enforcer carries the gate from PR #1009 and lets a mention
92
+ # through. rm-safety-net never blocked one (it only looks at the target of an
93
+ # rm). compound-command-deny-enforcer still blocks them, and that is recorded
94
+ # here rather than wished away: a gate was tried there and taken out, because
95
+ # this hook's deny patterns cover more than a verb list can express and the
96
+ # gate let two real things through (CI caught a git subcommand missing from the
97
+ # list and a redirect to a device). Fixing it means driving the gate off the
98
+ # deny patterns themselves. Until then this line is the honest state.
99
+ case "$h" in
100
+ compound-command-deny-enforcer) MENTION=2 ;;
101
+ *) MENTION=0 ;;
102
+ esac
103
+ expect "$h" "a mention in echo" \
104
+ "echo ${DQ}${RM} ${SLASH}${DQ}" "$MENTION"
105
+ expect "$h" "a mention in a commit message" \
106
+ "git commit -m ${DQ}guard against ${RM} ${SLASH}${DQ}" "$MENTION"
107
+ expect "$h" "a mention of the home variable" \
108
+ "echo ${DQ}never write ${RM} ${HOME_VAR}${DQ}" "$MENTION"
109
+
110
+ # --- quoted dangerous targets must still be blocked -----------------------
111
+
112
+ expect "$h" "a quoted root is a run" \
113
+ "$RM ${DQ}${SLASH}${DQ}" 2
114
+ expect "$h" "a quoted home variable is a run" \
115
+ "$RM ${DQ}${HOME_VAR}${DQ}" 2
116
+ expect "$h" "a quoted etc is a run" \
117
+ "$RM ${DQ}${SLASH}etc${DQ}" 2
118
+
119
+ # --- quoted text that is executed is not a mention ------------------------
120
+ # Only the two guards that carry the gate judge this. rm-safety-net looks at
121
+ # the target of an rm and has no unwrapping step, so a deletion handed to a
122
+ # shell has always passed it (measured on the version before this change too
123
+ # -- not a regression introduced here). destructive-guard is the layer that
124
+ # catches it, and the bottom of this file checks that it does.
125
+ case "$h" in
126
+ rm-safety-net) WRAP_EXEC=0 ;;
127
+ *) WRAP_EXEC=2 ;;
128
+ esac
129
+ expect "$h" "sh -c runs its quoted argument" \
130
+ "sh -c ${DQ}${RM} ${SLASH}${DQ}" "$WRAP_EXEC"
131
+ expect "$h" "eval runs its quoted argument" \
132
+ "eval ${DQ}${RM} ${SLASH}${DQ}" "$WRAP_EXEC"
133
+
134
+ # --- known limit: a name with spaces --------------------------------------
135
+ # Word splitting, not quoting: the operands are cut apart on whitespace, so a
136
+ # quoted name containing a space arrives as several words. The two safe-list
137
+ # guards block it (the words are not on the list). auto-mode works off a list
138
+ # of dangerous paths instead, so an unlisted name passes there. Both are the
139
+ # behaviour before this change; the assertions are here so that a future
140
+ # rewrite of operand extraction shows up in this file.
141
+ case "$h" in
142
+ auto-mode-safety-enforcer) SPACED=0 ;;
143
+ *) SPACED=2 ;;
144
+ esac
145
+ expect "$h" "KNOWN LIMIT: a quoted name with a space" \
146
+ "$RM ${DQ}my build dir${DQ}" "$SPACED"
147
+
148
+ done
149
+
150
+ # --- the layer that catches what rm-safety-net does not ----------------------
151
+ # rm-safety-net passes a deletion handed to a shell (it has no unwrapping step),
152
+ # so the claim "another layer catches it" has to be checked, not asserted.
153
+ # destructive-guard lives in scripts.json, so it is extracted the same way
154
+ # tests/destructive-guard-mentions-vs-invocations.test.sh extracts it.
155
+
156
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
157
+ WORK="$(mktemp -d)"
158
+ python3 -c "import json,sys;
159
+ sys.stdout.write(json.load(open('$ROOT/scripts.json'))['destructive-guard'])" > "$WORK/dg.sh"
160
+
161
+ run_dg() { # command
162
+ printf '{"tool_name":"Bash","tool_input":{"command":%s}}' \
163
+ "$(printf '%s' "$1" | sed 's/\\/\\\\/g; s/\"/\\\"/g; s/^/\"/; s/$/\"/')" \
164
+ | bash "$WORK/dg.sh" >/dev/null 2>&1
165
+ echo $?
166
+ }
167
+
168
+ expect_dg() { # description command expected_exit
169
+ actual="$(run_dg "$2")"
170
+ if [ "$actual" = "$3" ]; then
171
+ PASS=$((PASS + 1))
172
+ else
173
+ FAIL=$((FAIL + 1))
174
+ echo " FAIL [destructive-guard]: $1"
175
+ echo " command: $2"
176
+ echo " expected: exit $3, got exit $actual"
177
+ fi
178
+ }
179
+
180
+ expect_dg "sh -c with a quoted deletion is caught by the core guard" \
181
+ "sh -c ${DQ}${RM} ${SLASH}${DQ}" 2
182
+ expect_dg "eval with a quoted deletion is caught by the core guard" \
183
+ "eval ${DQ}${RM} ${SLASH}${DQ}" 2
184
+ expect_dg "a mention stays a mention in the core guard too" \
185
+ "echo ${DQ}${RM} ${SLASH}${DQ}" 0
186
+
187
+ rm -rf "$WORK" 2>/dev/null
188
+
189
+ echo "quoted-safe-targets-and-mentions: $PASS passed, $FAIL failed"
190
+ [ "$FAIL" -eq 0 ]
@@ -0,0 +1,104 @@
1
+ #!/bin/bash
2
+ # Tests for reroute-after-block-guard.sh
3
+ # Run: bash tests/reroute-after-block-guard.test.sh
4
+ set -uo pipefail
5
+
6
+ PASS=0
7
+ FAIL=0
8
+ HOOK="$(dirname "$0")/../examples/reroute-after-block-guard.sh"
9
+ TMP="$(mktemp -d)"
10
+ trap 'rm -rf "$TMP"' EXIT
11
+
12
+ # Build a transcript with a previous (tool_use + tool_result) pair, then check
13
+ # the hook against a current action. $1=blocked target, $2=is_error,
14
+ # $3=result text, $4=tool name for blocked action.
15
+ make_transcript() {
16
+ local blocked="$1" err="$2" text="$3" tn="${4:-Bash}" f
17
+ f="$TMP/t_$RANDOM.jsonl"
18
+ local inkey="command"
19
+ [ "$tn" != "Bash" ] && inkey="file_path"
20
+ jq -nc --arg b "$blocked" --arg k "$inkey" --arg tn "$tn" \
21
+ '{type:"assistant",message:{content:[{type:"tool_use",id:"toolu_x",name:$tn,input:{($k):$b}}]}}' >"$f"
22
+ jq -nc --arg e "$err" --arg t "$text" \
23
+ '{type:"user",message:{content:[{type:"tool_result",tool_use_id:"toolu_x",is_error:($e=="true"),content:$t}]}}' >>"$f"
24
+ printf '%s' "$f"
25
+ }
26
+
27
+ # $1=transcript path, $2=current command/file, $3=tool name -> input JSON
28
+ make_input() {
29
+ local tp="$1" cur="$2" tn="${3:-Bash}" k="command"
30
+ [ "$tn" != "Bash" ] && k="file_path"
31
+ jq -nc --arg tp "$tp" --arg c "$cur" --arg k "$k" --arg tn "$tn" \
32
+ '{transcript_path:$tp, tool_name:$tn, tool_input:{($k):$c}}'
33
+ }
34
+
35
+ run() {
36
+ local input="$1" expected="$2" desc="$3" env="${4:-}" actual=0
37
+ if [ -n "$env" ]; then
38
+ echo "$input" | env $env bash "$HOOK" >/dev/null 2>/dev/null || actual=$?
39
+ else
40
+ echo "$input" | bash "$HOOK" >/dev/null 2>/dev/null || actual=$?
41
+ fi
42
+ if [ "$actual" -eq "$expected" ]; then
43
+ echo " PASS: $desc"; PASS=$((PASS + 1))
44
+ else
45
+ echo " FAIL: $desc (expected $expected, got $actual)"; FAIL=$((FAIL + 1))
46
+ fi
47
+ }
48
+
49
+ echo "reroute-after-block-guard.sh tests"
50
+ echo ""
51
+
52
+ HOOKERR='PreToolUse:Bash hook error: [bash /home/u/.claude/hooks/block-push.sh]: blocked push'
53
+
54
+ # 1. Block fired on a path, current reroutes toward the same path -> stop.
55
+ t=$(make_transcript "git push origin main:src/app.py" "true" "$HOOKERR")
56
+ run "$(make_input "$t" "git -C . push --force origin main && cat src/app.py")" 2 "reroute toward same path after block -> exit 2"
57
+
58
+ # 2. Same block, but current targets an unrelated path -> allow.
59
+ t=$(make_transcript "git push origin main:src/app.py" "true" "$HOOKERR")
60
+ run "$(make_input "$t" "ls docs/readme.md")" 0 "different target after block -> exit 0"
61
+
62
+ # 3. Previous tool SUCCEEDED (no gate fired) -> allow even if same path.
63
+ t=$(make_transcript "cat src/app.py" "false" "file contents here")
64
+ run "$(make_input "$t" "rm src/app.py")" 0 "previous succeeded, no gate -> exit 0"
65
+
66
+ # 4. Error present but NOT a gate block (generic failure) -> allow.
67
+ t=$(make_transcript "cat src/app.py" "true" "cat: src/app.py: No such file or directory")
68
+ run "$(make_input "$t" "vi src/app.py")" 0 "non-gate error -> exit 0"
69
+
70
+ # 5. Edit reroute toward the same file that was just blocked -> stop.
71
+ t=$(make_transcript "/home/u/proj/secret.env" "true" "PreToolUse:Edit hook error: [bash /x/dotenv-guard.sh]: blocked" "Edit")
72
+ run "$(make_input "$t" "/home/u/proj/secret.env" "Write")" 2 "Write reroute to just-blocked file -> exit 2"
73
+
74
+ # 6. One-shot override -> allow.
75
+ t=$(make_transcript "git push origin main:src/app.py" "true" "$HOOKERR")
76
+ run "$(make_input "$t" "git push --force origin main:src/app.py")" 0 "CC_REROUTE_ALLOW=1 override -> exit 0" "CC_REROUTE_ALLOW=1"
77
+
78
+ # 7. Disabled -> allow.
79
+ t=$(make_transcript "git push origin main:src/app.py" "true" "$HOOKERR")
80
+ run "$(make_input "$t" "git push origin main:src/app.py")" 0 "CC_REROUTE_DISABLE=1 -> exit 0" "CC_REROUTE_DISABLE=1"
81
+
82
+ # 8. No transcript_path -> allow (fail open).
83
+ run '{"tool_name":"Bash","tool_input":{"command":"rm -rf /"}}' 0 "no transcript_path -> exit 0"
84
+
85
+ # 9. Generic "permission denied" is NOT a Claude Code gate -> allow (fail open).
86
+ t=$(make_transcript "rm /etc/hosts" "true" "rm: cannot remove '/etc/hosts': Permission denied")
87
+ run "$(make_input "$t" "sudo rm /etc/hosts")" 0 "generic permission denied is not a gate -> exit 0"
88
+
89
+ # 10. Block on a path but current shares only a trivial flag/word -> allow.
90
+ t=$(make_transcript "git push origin main" "true" "$HOOKERR")
91
+ run "$(make_input "$t" "git status")" 0 "no shared concrete path token -> exit 0"
92
+
93
+ # 11. Server-side git rejection ("blocked by required reviews") is NOT a gate -> allow.
94
+ t=$(make_transcript "git push origin main" "true" "remote: error: GH006: Protected branch update failed; blocked by required reviews")
95
+ run "$(make_input "$t" "git push --force origin main")" 0 "branch-protection rejection is not a gate -> exit 0"
96
+
97
+ # 12. After a real hook block, inspecting the rejected branch (a git ref, not a
98
+ # file) must stay allowed — git refs are not concrete targets.
99
+ t=$(make_transcript "git push origin/main" "true" "$HOOKERR")
100
+ run "$(make_input "$t" "git log origin/main --oneline")" 0 "git ref (origin/main) is not a target -> exit 0"
101
+
102
+ echo ""
103
+ echo "Results: $PASS passed, $FAIL failed"
104
+ [ "$FAIL" -eq 0 ]
@@ -0,0 +1,97 @@
1
+ #!/bin/bash
2
+ # secret-guard: are the two promises in its own header actually implemented?
3
+ #
4
+ # Why this exists. The header has said, since the first release, that the hook
5
+ # blocks `git add *secret*` and that CC_SECRET_PATTERNS adds patterns to block.
6
+ # Measured 2026-09-04 against the shipped scripts.json: the credential regex was
7
+ # (credentials|\.pem|\.key|\.p12|\.pfx|id_rsa|id_ed25519) -- no `secret` -- and
8
+ # the body never referenced CC_SECRET_PATTERNS at all. So `git add secrets.yaml`
9
+ # exited 0, and an operator who set CC_SECRET_PATTERNS got no error and no
10
+ # enforcement.
11
+ #
12
+ # That is the expensive direction of wrong. A guard that fails to block is
13
+ # silent: nothing breaks, no issue is filed, and the operator keeps believing
14
+ # the header. These assertions pin both promises, in both directions -- the new
15
+ # patterns must block, and ordinary staging must still pass, because a guard
16
+ # that blocks everything is uninstalled within a day.
17
+
18
+ set -u
19
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
20
+ HOOK="$(mktemp)"
21
+ trap 'rm -f "$HOOK"' EXIT
22
+ python3 -c "
23
+ import json,sys
24
+ sys.stdout.write(json.load(open('$ROOT/scripts.json'))['secret-guard'])" > "$HOOK"
25
+
26
+ PASS=0; FAIL=0
27
+ WORK="$(mktemp -d)"
28
+ trap 'rm -f "$HOOK"; rm -rf "$WORK"' EXIT
29
+
30
+ run() { # run <command> -> ERR/CODE (cwd is a clean dir with no .env)
31
+ local payload
32
+ payload=$(python3 -c "
33
+ import json,sys
34
+ print(json.dumps({'tool_input': {'command': sys.argv[1]}}))" "$1")
35
+ ERR=$(printf '%s' "$payload" | (cd "$WORK" && bash "$HOOK") 2>&1 >/dev/null)
36
+ CODE=$?
37
+ }
38
+
39
+ check() { # check <name> <command> <expected-code>
40
+ run "$2"
41
+ if [ "$CODE" = "$3" ]; then
42
+ PASS=$((PASS + 1))
43
+ else
44
+ FAIL=$((FAIL + 1))
45
+ echo " FAIL: $1"
46
+ echo " command: $2"
47
+ echo " expected exit $3, got $CODE"
48
+ [ -n "$ERR" ] && echo " stderr: $(printf '%s' "$ERR" | head -1)"
49
+ fi
50
+ }
51
+
52
+ echo "secret-guard: documented promises"
53
+
54
+ # Promise 1 -- the header says *secret* is blocked.
55
+ check "secrets.yaml is blocked" "git add secrets.yaml" 2
56
+ check "config/secret.json is blocked" "git add config/secret.json" 2
57
+ check "SECRETS.env is blocked" "git add SECRETS.env" 2
58
+
59
+ # The built-ins the header already delivered must keep working.
60
+ check ".env still blocked" "git add .env" 2
61
+ check "credentials.json still blocked" "git add credentials.json" 2
62
+ check "id_rsa still blocked" "git add id_rsa" 2
63
+
64
+ # The other direction. A guard that blocks ordinary work gets removed.
65
+ check "ordinary source passes" "git add src/app.js" 0
66
+ check "package.json passes" "git add package.json" 0
67
+ check "non-add git passes" "git status" 0
68
+
69
+ # Promise 2 -- CC_SECRET_PATTERNS is documented as configuration.
70
+ patterns_case() { # patterns_case <name> <patterns> <command> <expected>
71
+ local payload
72
+ payload=$(python3 -c "
73
+ import json,sys
74
+ print(json.dumps({'tool_input': {'command': sys.argv[1]}}))" "$3")
75
+ local code
76
+ printf '%s' "$payload" | (cd "$WORK" && CC_SECRET_PATTERNS="$2" bash "$HOOK") >/dev/null 2>&1
77
+ code=$?
78
+ if [ "$code" = "$4" ]; then
79
+ PASS=$((PASS + 1))
80
+ else
81
+ FAIL=$((FAIL + 1))
82
+ echo " FAIL: $1 (CC_SECRET_PATTERNS=$2, expected exit $4, got $code)"
83
+ fi
84
+ }
85
+
86
+ patterns_case "extra literal pattern blocks" ".npmrc" "git add .npmrc" 2
87
+ patterns_case "extra glob pattern blocks" "*.jks" "git add release.jks" 2
88
+ patterns_case "unrelated file still passes" ".npmrc" "git add README.md" 0
89
+ patterns_case "empty value changes nothing" "" "git add README.md" 0
90
+ # A malformed value must not become an approval for the built-ins. This is the
91
+ # fail-open direction that matters: the operator typed something wrong and the
92
+ # .env check has to survive it.
93
+ patterns_case "built-ins survive junk value" "[[[:*(" "git add .env" 2
94
+ patterns_case "junk value blocks nothing new" "[[[:*(" "git add README.md" 0
95
+
96
+ echo " $PASS passed, $FAIL failed"
97
+ [ "$FAIL" -eq 0 ]
@@ -0,0 +1,45 @@
1
+ #!/bin/bash
2
+ # Tests for session-plugin-pin-watch.sh (#73952)
3
+ # Run: bash tests/session-plugin-pin-watch.test.sh
4
+ set -uo pipefail
5
+
6
+ PASS=0
7
+ FAIL=0
8
+ HOOK="$(cd "$(dirname "$0")/.." && pwd)/examples/session-plugin-pin-watch.sh"
9
+
10
+ TESTHOME=$(mktemp -d)
11
+ trap 'rm -rf "$TESTHOME"' EXIT
12
+ mkdir -p "$TESTHOME/.claude/plugins/mkt/pluginA/v1.0.0/hooks" \
13
+ "$TESTHOME/.claude/plugins/mkt/pluginB/v2.3.0/hooks"
14
+ GLOB="$TESTHOME/.claude/plugins/*/*/*"
15
+ SID="t-sid"
16
+
17
+ run() { # $1 = event
18
+ HOME="$TESTHOME" CC_PLUGIN_WATCH_GLOB="$GLOB" bash "$HOOK" <<EOF
19
+ {"hook_event_name":"$1","session_id":"$SID"}
20
+ EOF
21
+ }
22
+
23
+ assert_warns() { # $1 = "warn"|"quiet", $2 = event, $3 = desc
24
+ local out; out=$(run "$2")
25
+ if [ "$1" = "warn" ]; then
26
+ if printf '%s' "$out" | grep -q "systemMessage"; then echo " PASS: $3"; PASS=$((PASS+1)); else echo " FAIL: $3 (expected warning, got: ${out:-none})"; FAIL=$((FAIL+1)); fi
27
+ else
28
+ if [ -z "$out" ]; then echo " PASS: $3"; PASS=$((PASS+1)); else echo " FAIL: $3 (expected quiet, got: $out)"; FAIL=$((FAIL+1)); fi
29
+ fi
30
+ }
31
+
32
+ echo "session-plugin-pin-watch.sh tests"
33
+ echo ""
34
+
35
+ assert_warns quiet SessionStart "SessionStart -> snapshot, quiet"
36
+ assert_warns quiet PreToolUse "PreToolUse, all dirs present -> quiet"
37
+
38
+ rm -rf "$TESTHOME/.claude/plugins/mkt/pluginA/v1.0.0" # simulate re-materialization removing pinned path
39
+ assert_warns warn PreToolUse "pinned version dir vanished -> warn"
40
+
41
+ assert_warns quiet PreToolUse "state refreshed, nothing new vanished -> quiet (no spam)"
42
+
43
+ echo ""
44
+ echo "PASS=$PASS FAIL=$FAIL"
45
+ [ "$FAIL" -eq 0 ]
@@ -0,0 +1,95 @@
1
+ #!/bin/bash
2
+ # A settings.json that exists but does not parse must never be treated as {}.
3
+ #
4
+ # The installer read it with a bare JSON.parse in 34 places, swallowed the failure
5
+ # outright in 9, and in 6 of those 9 wrote the resulting object straight back with
6
+ # writeFileSync(SETTINGS_PATH, ...). Every hook, permission and env var the user had
7
+ # was replaced by a fresh object holding only the hook just added — and the command
8
+ # exited 0, printing "Registered in settings.json".
9
+ #
10
+ # The usual trigger is a snippet copied from docs whose first line is
11
+ # `// path/to/file`, which is not legal JSON. This repo shipped four such examples.
12
+ #
13
+ # Also covered here: --protect used to throw ReferenceError on every single run
14
+ # (protect() called rules.some(...) and `rules` never existed in that function), so
15
+ # the hook file was written and never registered. Nothing in tests/ exercised
16
+ # --protect at all, which is why it could stay broken.
17
+ #
18
+ # Every case runs in a throwaway HOME. Nothing touches the real one.
19
+
20
+ set -u
21
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
22
+ PASS=0; FAIL=0
23
+
24
+ BROKEN='// ~/.claude/settings.json
25
+ {
26
+ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "keep-me-guard.sh" } ] } ] },
27
+ "permissions": { "allow": ["Bash(ls:*)"] },
28
+ "env": { "KEEP_ME": "1" }
29
+ }'
30
+ VALID='{"hooks":{"PreToolUse":[{"matcher":"Bash","hooks":[{"type":"command","command":"keep-me-guard.sh"}]}]},"env":{"KEEP_ME":"1"}}'
31
+
32
+ # check <name> <settings body> <expected exit> <must survive: 1|0> <expect warning: 1|0> <args...>
33
+ check() {
34
+ local name="$1" body="$2" want_rc="$3" want_keep="$4" want_warn="$5"; shift 5
35
+ local T; T="$(mktemp -d)"
36
+ mkdir -p "$T/.claude/hooks"
37
+ printf '%s' "$body" > "$T/.claude/settings.json"
38
+
39
+ local out rc
40
+ out="$(cd "$ROOT" && HOME="$T" node index.mjs "$@" 2>&1)"; rc=$?
41
+
42
+ local keep=0
43
+ grep -q 'keep-me-guard' "$T/.claude/settings.json" && \
44
+ grep -q 'KEEP_ME' "$T/.claude/settings.json" && keep=1
45
+ local warn=0
46
+ case "$out" in *'is not valid JSON'*) warn=1 ;; esac
47
+
48
+ if [ "$rc" -eq "$want_rc" ] && [ "$keep" -eq "$want_keep" ] && [ "$warn" -eq "$want_warn" ]; then
49
+ PASS=$((PASS+1)); echo " ok $name"
50
+ else
51
+ FAIL=$((FAIL+1))
52
+ echo " FAIL $name"
53
+ echo " exit want=$want_rc got=$rc"
54
+ echo " survived want=$want_keep got=$keep"
55
+ echo " warned want=$want_warn got=$warn"
56
+ echo "$out" | tail -4 | sed 's/^/ | /'
57
+ fi
58
+ find "$T" -mindepth 1 -delete 2>/dev/null; rmdir "$T" 2>/dev/null
59
+ }
60
+
61
+ echo "settings-unreadable-refuses-to-write"
62
+
63
+ # --- the file is unreadable: refuse to write, keep everything ---------------
64
+ check "guard: broken settings is not overwritten" \
65
+ "$BROKEN" 1 1 1 --guard "block rm -rf"
66
+ check "protect: broken settings is not overwritten" \
67
+ "$BROKEN" 1 1 1 --protect .env
68
+
69
+ # --- controls: a readable file must behave exactly as before ----------------
70
+ # Without these, "refuses to write" and "cannot write at all" look the same.
71
+ check "guard: valid settings still registers" \
72
+ "$VALID" 0 1 0 --guard "block rm -rf"
73
+ check "protect: valid settings still registers" \
74
+ "$VALID" 0 1 0 --protect .env
75
+
76
+ # --- --protect used to throw ReferenceError before it could register --------
77
+ T="$(mktemp -d)"; mkdir -p "$T/.claude/hooks"
78
+ printf '%s' "$VALID" > "$T/.claude/settings.json"
79
+ out="$(cd "$ROOT" && HOME="$T" node index.mjs --protect .env 2>&1)"
80
+ if echo "$out" | grep -q 'ReferenceError'; then
81
+ FAIL=$((FAIL+1)); echo " FAIL protect: no ReferenceError"
82
+ echo "$out" | tail -3 | sed 's/^/ | /'
83
+ else
84
+ PASS=$((PASS+1)); echo " ok protect: no ReferenceError"
85
+ fi
86
+ if grep -q 'Edit|Write' "$T/.claude/settings.json"; then
87
+ PASS=$((PASS+1)); echo " ok protect: registered under the Edit|Write matcher"
88
+ else
89
+ FAIL=$((FAIL+1)); echo " FAIL protect: registered under the Edit|Write matcher"
90
+ fi
91
+ find "$T" -mindepth 1 -delete 2>/dev/null; rmdir "$T" 2>/dev/null
92
+
93
+ echo
94
+ echo "settings-unreadable-refuses-to-write: $PASS passed, $FAIL failed"
95
+ [ "$FAIL" -eq 0 ]