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,68 @@
1
+ #!/bin/bash
2
+ # Test for worktree-escape-write-guard.sh
3
+ #
4
+ # Verifies the hook blocks (exit 2) Edit/Write/MultiEdit whose absolute
5
+ # file_path escapes the active worktree onto the main checkout
6
+ # (Claude Code issue #70069), while letting through:
7
+ # - in-worktree absolute paths
8
+ # - relative paths (resolve against cwd = the worktree)
9
+ # - sessions not in a worktree
10
+ # - paths entirely outside the repo (other guards' concern)
11
+ # - non-edit tools
12
+
13
+ set -u
14
+
15
+ HOOK="$(dirname "$0")/../examples/worktree-escape-write-guard.sh"
16
+ [ ! -x "$HOOK" ] && chmod +x "$HOOK"
17
+
18
+ REPO="/home/dev/myrepo"
19
+ WT="$REPO/.claude/worktrees/feature-x"
20
+
21
+ PASS=0
22
+ FAIL=0
23
+
24
+ run_case() {
25
+ local name="$1" tool="$2" cwd="$3" file="$4" expect="$5"
26
+ local input rc
27
+ input=$(jq -nc --arg t "$tool" --arg c "$cwd" --arg f "$file" \
28
+ '{tool_name:$t, cwd:$c, tool_input:{file_path:$f}}')
29
+ echo "$input" | "$HOOK" >/dev/null 2>&1
30
+ rc=$?
31
+ if [ "$expect" = "block" ]; then
32
+ if [ "$rc" = "2" ]; then PASS=$((PASS + 1)); echo "PASS: $name"
33
+ else FAIL=$((FAIL + 1)); echo "FAIL: $name (expected block/exit2, got $rc)"; fi
34
+ else
35
+ if [ "$rc" = "0" ]; then PASS=$((PASS + 1)); echo "PASS: $name"
36
+ else FAIL=$((FAIL + 1)); echo "FAIL: $name (expected allow/exit0, got $rc)"; fi
37
+ fi
38
+ }
39
+
40
+ # --- the bug: must block ---
41
+ run_case "Edit escapes to main checkout" "Edit" "$WT" "$REPO/packages/rrweb/src/record/foo.ts" block
42
+ run_case "Write escapes to main checkout" "Write" "$WT" "$REPO/src/index.ts" block
43
+ run_case "MultiEdit escapes to main checkout" "MultiEdit" "$WT" "$REPO/README.md" block
44
+ run_case "escape from a worktree subdir" "Edit" "$WT/packages/a" "$REPO/packages/a/b.ts" block
45
+ run_case "escape to repo root file" "Write" "$WT" "$REPO/package.json" block
46
+
47
+ # --- legitimate edits inside the worktree: must allow ---
48
+ run_case "in-worktree absolute path" "Edit" "$WT" "$WT/packages/rrweb/src/record/foo.ts" allow
49
+ run_case "in-worktree repo-root file" "Write" "$WT" "$WT/package.json" allow
50
+ run_case "in-worktree from subdir" "Edit" "$WT/packages/a" "$WT/packages/a/b.ts" allow
51
+ run_case "relative path (resolves in wt)" "Edit" "$WT" "packages/rrweb/src/record/foo.ts" allow
52
+ run_case "relative dotslash path" "Write" "$WT" "./src/index.ts" allow
53
+
54
+ # --- not a worktree session: hook inactive, must allow ---
55
+ run_case "normal repo, absolute under repo" "Edit" "$REPO" "$REPO/packages/a/b.ts" allow
56
+ run_case "normal repo, relative" "Write" "$REPO" "src/index.ts" allow
57
+
58
+ # --- outside the repo entirely: not this guard's concern, allow ---
59
+ run_case "absolute path outside repo" "Edit" "$WT" "/etc/hosts" allow
60
+ run_case "absolute path other project" "Write" "$WT" "/home/dev/other/file.ts" allow
61
+
62
+ # --- wrong tool: allow ---
63
+ run_case "Bash tool ignored" "Bash" "$WT" "$REPO/packages/a/b.ts" allow
64
+ run_case "Read tool ignored" "Read" "$WT" "$REPO/packages/a/b.ts" allow
65
+
66
+ echo ""
67
+ echo "PASS=$PASS FAIL=$FAIL"
68
+ [ "$FAIL" -eq 0 ]
@@ -0,0 +1,181 @@
1
+ #!/bin/bash
2
+ # Tests for worktree-hooks-path-fix.sh
3
+ # This suite cds into temp repos before invoking the hook, so a relative path
4
+ # here only resolves when the suite itself was launched with an absolute path.
5
+ HOOK="$(cd "$(dirname "$0")/../examples" && pwd)/worktree-hooks-path-fix.sh"
6
+ PASS=0; FAIL=0
7
+
8
+ TEST_BASE=$(mktemp -d)
9
+ cleanup() { rm -rf "$TEST_BASE"; }
10
+ trap cleanup EXIT
11
+
12
+ run_test() {
13
+ local desc="$1" expect_code="$2" check_fn="${3:-}"
14
+ if [ -n "$check_fn" ]; then
15
+ if "$check_fn"; then
16
+ echo "PASS: $desc"
17
+ PASS=$((PASS + 1))
18
+ else
19
+ echo "FAIL: $desc"
20
+ FAIL=$((FAIL + 1))
21
+ fi
22
+ fi
23
+ }
24
+
25
+ # === Helper: create a main repo + a Claude-style worktree at known path ===
26
+ setup_repo_with_worktree() {
27
+ local main_repo="$1"
28
+ local worktree_subdir="$2"
29
+
30
+ rm -rf "$main_repo"
31
+ mkdir -p "$main_repo"
32
+ cd "$main_repo" || return 1
33
+ git init --quiet
34
+ git config user.email "test@test"
35
+ git config user.name "test"
36
+ git commit --allow-empty --quiet -m "init"
37
+ git config extensions.worktreeConfig true
38
+ # Simulate Husky-style shared core.hooksPath
39
+ git config core.hooksPath ".hooks_shared"
40
+
41
+ # Create a Claude-style worktree
42
+ local worktree_path="$main_repo/.claude/worktrees/$worktree_subdir"
43
+ mkdir -p "$(dirname "$worktree_path")"
44
+ git worktree add -q "$worktree_path" 2>/dev/null
45
+ }
46
+
47
+ # === Test 1: bogus absolute hooksPath gets unset ===
48
+ setup_repo_with_worktree "$TEST_BASE/repo1" "wt1"
49
+ WORKTREE="$TEST_BASE/repo1/.claude/worktrees/wt1"
50
+ # Write a bogus absolute hooksPath
51
+ git -C "$WORKTREE" config --worktree core.hooksPath "/tmp/nonexistent/hooks"
52
+
53
+ # Run the hook
54
+ echo '{"hook_event_name":"SessionStart"}' | CLAUDE_PROJECT_DIR="$WORKTREE" bash "$HOOK" >/dev/null 2>&1
55
+
56
+ # Verify the worktree config no longer has core.hooksPath
57
+ RESULT=$(git -C "$WORKTREE" config --worktree --get core.hooksPath 2>/dev/null || echo "")
58
+ if [ -z "$RESULT" ]; then
59
+ echo "PASS: bogus absolute hooksPath unset"
60
+ PASS=$((PASS + 1))
61
+ else
62
+ echo "FAIL: hooksPath still set: $RESULT"
63
+ FAIL=$((FAIL + 1))
64
+ fi
65
+
66
+ # Verify shared config's hooksPath still resolves
67
+ SHARED=$(git -C "$WORKTREE" config --get core.hooksPath 2>/dev/null || echo "")
68
+ if [ "$SHARED" = ".hooks_shared" ]; then
69
+ echo "PASS: shared hooksPath fell through correctly"
70
+ PASS=$((PASS + 1))
71
+ else
72
+ echo "FAIL: shared hooksPath wrong: $SHARED (expected .hooks_shared)"
73
+ FAIL=$((FAIL + 1))
74
+ fi
75
+
76
+ # === Test 2: relative hooksPath is left alone ===
77
+ setup_repo_with_worktree "$TEST_BASE/repo2" "wt2"
78
+ WORKTREE="$TEST_BASE/repo2/.claude/worktrees/wt2"
79
+ git -C "$WORKTREE" config --worktree core.hooksPath ".husky/_"
80
+ echo '{"hook_event_name":"SessionStart"}' | CLAUDE_PROJECT_DIR="$WORKTREE" bash "$HOOK" >/dev/null 2>&1
81
+ RESULT=$(git -C "$WORKTREE" config --worktree --get core.hooksPath 2>/dev/null || echo "")
82
+ if [ "$RESULT" = ".husky/_" ]; then
83
+ echo "PASS: relative hooksPath left alone"
84
+ PASS=$((PASS + 1))
85
+ else
86
+ echo "FAIL: relative hooksPath altered: $RESULT"
87
+ FAIL=$((FAIL + 1))
88
+ fi
89
+
90
+ # === Test 3: absolute path inside worktree is left alone ===
91
+ setup_repo_with_worktree "$TEST_BASE/repo3" "wt3"
92
+ WORKTREE="$TEST_BASE/repo3/.claude/worktrees/wt3"
93
+ INTERNAL_PATH="$WORKTREE/.local-hooks"
94
+ git -C "$WORKTREE" config --worktree core.hooksPath "$INTERNAL_PATH"
95
+ echo '{"hook_event_name":"SessionStart"}' | CLAUDE_PROJECT_DIR="$WORKTREE" bash "$HOOK" >/dev/null 2>&1
96
+ RESULT=$(git -C "$WORKTREE" config --worktree --get core.hooksPath 2>/dev/null || echo "")
97
+ if [ "$RESULT" = "$INTERNAL_PATH" ]; then
98
+ echo "PASS: absolute path inside worktree left alone"
99
+ PASS=$((PASS + 1))
100
+ else
101
+ echo "FAIL: internal absolute path altered: $RESULT"
102
+ FAIL=$((FAIL + 1))
103
+ fi
104
+
105
+ # === Test 4: non-worktree (main checkout) is a no-op ===
106
+ setup_repo_with_worktree "$TEST_BASE/repo4" "wt4"
107
+ MAIN_REPO="$TEST_BASE/repo4"
108
+ # Set a bogus hooksPath in main config (per-worktree scope not applicable here, use --local)
109
+ git -C "$MAIN_REPO" config core.hooksPath "/tmp/bogus"
110
+ echo '{"hook_event_name":"SessionStart"}' | CLAUDE_PROJECT_DIR="$MAIN_REPO" bash "$HOOK" >/dev/null 2>&1
111
+ RESULT=$(git -C "$MAIN_REPO" config --get core.hooksPath 2>/dev/null || echo "")
112
+ if [ "$RESULT" = "/tmp/bogus" ]; then
113
+ echo "PASS: main checkout untouched"
114
+ PASS=$((PASS + 1))
115
+ else
116
+ echo "FAIL: main checkout altered: $RESULT"
117
+ FAIL=$((FAIL + 1))
118
+ fi
119
+
120
+ # === Test 5: worktree NOT under .claude/worktrees/ is left alone ===
121
+ mkdir -p "$TEST_BASE/repo5"
122
+ cd "$TEST_BASE/repo5"
123
+ git init --quiet
124
+ git config user.email t@t; git config user.name t
125
+ git commit --allow-empty --quiet -m i
126
+ git config extensions.worktreeConfig true
127
+ OTHER_WORKTREE="$TEST_BASE/other-wt-location/wt5"
128
+ mkdir -p "$(dirname "$OTHER_WORKTREE")"
129
+ git worktree add -q "$OTHER_WORKTREE" 2>/dev/null
130
+ git -C "$OTHER_WORKTREE" config --worktree core.hooksPath "/tmp/bogus"
131
+ echo '{"hook_event_name":"SessionStart"}' | CLAUDE_PROJECT_DIR="$OTHER_WORKTREE" bash "$HOOK" >/dev/null 2>&1
132
+ RESULT=$(git -C "$OTHER_WORKTREE" config --worktree --get core.hooksPath 2>/dev/null || echo "")
133
+ if [ "$RESULT" = "/tmp/bogus" ]; then
134
+ echo "PASS: non-Claude worktree path pattern left alone"
135
+ PASS=$((PASS + 1))
136
+ else
137
+ echo "FAIL: non-Claude worktree altered: $RESULT"
138
+ FAIL=$((FAIL + 1))
139
+ fi
140
+
141
+ # === Test 6: CC_WORKTREE_HOOKS_FIX_DISABLE bypasses ===
142
+ setup_repo_with_worktree "$TEST_BASE/repo6" "wt6"
143
+ WORKTREE="$TEST_BASE/repo6/.claude/worktrees/wt6"
144
+ git -C "$WORKTREE" config --worktree core.hooksPath "/tmp/bogus6"
145
+ echo '{"hook_event_name":"SessionStart"}' | CC_WORKTREE_HOOKS_FIX_DISABLE=1 CLAUDE_PROJECT_DIR="$WORKTREE" bash "$HOOK" >/dev/null 2>&1
146
+ RESULT=$(git -C "$WORKTREE" config --worktree --get core.hooksPath 2>/dev/null || echo "")
147
+ if [ "$RESULT" = "/tmp/bogus6" ]; then
148
+ echo "PASS: DISABLE env var bypasses hook"
149
+ PASS=$((PASS + 1))
150
+ else
151
+ echo "FAIL: DISABLE bypass failed: $RESULT"
152
+ FAIL=$((FAIL + 1))
153
+ fi
154
+
155
+ # === Test 7: non-git directory is a no-op ===
156
+ NON_GIT="$TEST_BASE/not-a-repo"
157
+ mkdir -p "$NON_GIT"
158
+ echo '{"hook_event_name":"SessionStart"}' | CLAUDE_PROJECT_DIR="$NON_GIT" bash "$HOOK" >/dev/null 2>&1
159
+ EXIT_CODE=$?
160
+ if [ "$EXIT_CODE" = "0" ]; then
161
+ echo "PASS: non-git directory exits 0"
162
+ PASS=$((PASS + 1))
163
+ else
164
+ echo "FAIL: non-git directory exit code: $EXIT_CODE"
165
+ FAIL=$((FAIL + 1))
166
+ fi
167
+
168
+ # === Test 8: empty input is no-op ===
169
+ echo '{}' | CLAUDE_PROJECT_DIR="$TEST_BASE/repo6/.claude/worktrees/wt6" bash "$HOOK" >/dev/null 2>&1
170
+ EXIT_CODE=$?
171
+ if [ "$EXIT_CODE" = "0" ]; then
172
+ echo "PASS: empty event payload exits 0"
173
+ PASS=$((PASS + 1))
174
+ else
175
+ echo "FAIL: empty event exit code: $EXIT_CODE"
176
+ FAIL=$((FAIL + 1))
177
+ fi
178
+
179
+ echo ""
180
+ echo "Results: $PASS passed, $FAIL failed"
181
+ [ "$FAIL" -eq 0 ]
@@ -0,0 +1,51 @@
1
+ #!/bin/bash
2
+ # Test for worktree-parent-write-guard.sh
3
+ #
4
+ # Verifies the hook blocks (exit 2) writes that target the PARENT repo while
5
+ # the agent is working in a worktree nested at <repo>/.claude/worktrees/<name>/,
6
+ # and allows (exit 0) correct in-worktree writes and writes that are not part of
7
+ # the nested-worktree danger (normal repos, /tmp, home configs).
8
+ #
9
+ # Customer evidence: anthropics/claude-code #62547, #60679, #69026.
10
+
11
+ set -u
12
+
13
+ HOOK="$(dirname "$0")/../examples/worktree-parent-write-guard.sh"
14
+ [ ! -x "$HOOK" ] && chmod +x "$HOOK"
15
+
16
+ PASS=0
17
+ FAIL=0
18
+
19
+ run_case() {
20
+ local name="$1" cwd="$2" target="$3" expect="$4"
21
+ local input rc
22
+ input=$(jq -nc --arg cwd "$cwd" --arg t "$target" '{cwd:$cwd, tool_input:{file_path:$t}}')
23
+ echo "$input" | "$HOOK" >/dev/null 2>&1
24
+ rc=$?
25
+ if [ "$expect" = "block" ]; then
26
+ if [ "$rc" = "2" ]; then PASS=$((PASS+1)); echo "PASS: $name"
27
+ else FAIL=$((FAIL+1)); echo "FAIL: $name (expected block/2, got $rc)"; fi
28
+ else
29
+ if [ "$rc" = "0" ]; then PASS=$((PASS+1)); echo "PASS: $name"
30
+ else FAIL=$((FAIL+1)); echo "FAIL: $name (expected allow/0, got $rc)"; fi
31
+ fi
32
+ }
33
+
34
+ W="/home/u/app/.claude/worktrees/foo"
35
+
36
+ # --- dangerous: must block ---
37
+ run_case "parent canonical src" "$W" "/home/u/app/src/x.js" block
38
+ run_case "parent root file" "$W" "/home/u/app/README.md" block
39
+ run_case "subdir cwd -> parent" "$W/src" "/home/u/app/src/x.js" block
40
+ run_case "sibling worktree target" "$W" "/home/u/app/.claude/worktrees/bar/x.js" block
41
+
42
+ # --- safe: must allow ---
43
+ run_case "correct in-worktree (abs)" "$W" "$W/src/x.js" allow
44
+ run_case "in-worktree subdir cwd" "$W/src" "$W/src/x.js" allow
45
+ run_case "normal repo (no worktree)" "/home/u/app" "/home/u/app/src/x.js" allow
46
+ run_case "write to /tmp" "$W" "/tmp/scratch.txt" allow
47
+ run_case "write to home config" "$W" "/home/u/.claude/settings.json" allow
48
+
49
+ echo "----------------------------------------"
50
+ echo "PASS=$PASS FAIL=$FAIL"
51
+ [ "$FAIL" -eq 0 ] || exit 1
@@ -0,0 +1,39 @@
1
+ #!/bin/bash
2
+ # Tests for write-empty-content-guard.sh (#72666)
3
+ # Run: bash tests/write-empty-content-guard.test.sh
4
+ set -uo pipefail
5
+
6
+ PASS=0
7
+ FAIL=0
8
+ HOOK="$(cd "$(dirname "$0")/.." && pwd)/examples/write-empty-content-guard.sh"
9
+
10
+ TMP=$(mktemp -d)
11
+ trap 'rm -rf "$TMP"' EXIT
12
+
13
+ NONEMPTY="$TMP/keep.txt"; printf 'line1\nline2\nline3\n' > "$NONEMPTY"
14
+ EMPTYF="$TMP/empty.txt"; : > "$EMPTYF"
15
+ NEWF="$TMP/new.txt" # does not exist
16
+
17
+ run() { # $1 = file, $2 = content (raw)
18
+ local exit=0
19
+ jq -cn --arg f "$1" --arg c "$2" '{tool_input:{file_path:$f,content:$c}}' | bash "$HOOK" >/dev/null 2>/dev/null || exit=$?
20
+ echo "$exit"
21
+ }
22
+
23
+ check() { # $1 = expected exit, $2 = actual exit, $3 = desc
24
+ if [ "$1" = "$2" ]; then echo " PASS: $3"; PASS=$((PASS+1)); else echo " FAIL: $3 (expected $1, got $2)"; FAIL=$((FAIL+1)); fi
25
+ }
26
+
27
+ echo "write-empty-content-guard.sh tests"
28
+ echo ""
29
+
30
+ check 2 "$(run "$NONEMPTY" "")" "empty content over non-empty file -> BLOCK (exit 2)"
31
+ check 2 "$(run "$NONEMPTY" "
32
+ ")" "whitespace-only content over non-empty file -> BLOCK"
33
+ check 0 "$(run "$NONEMPTY" "new real text")" "real content over non-empty file -> allow"
34
+ check 0 "$(run "$EMPTYF" "")" "empty content over already-empty file -> allow (nothing to lose)"
35
+ check 0 "$(run "$NEWF" "")" "empty content to a new (nonexistent) file -> allow (creation)"
36
+
37
+ echo ""
38
+ echo "PASS=$PASS FAIL=$FAIL"
39
+ [ "$FAIL" -eq 0 ]
@@ -0,0 +1,29 @@
1
+ #!/bin/bash
2
+ # Standalone tests for write-nul-corruption-detector.sh
3
+ # Run: bash tests/write-nul-corruption-detector.test.sh
4
+ HOOK="$(dirname "$0")/../examples/write-nul-corruption-detector.sh"
5
+ PASS=0; FAIL=0
6
+ TMP=$(mktemp -d)
7
+
8
+ run() { # desc input expected_exit
9
+ local desc="$1" input="$2" expected="$3" actual=0
10
+ echo "$input" | bash "$HOOK" >/dev/null 2>&1 || actual=$?
11
+ if [ "$actual" -eq "$expected" ]; then echo " PASS: $desc"; PASS=$((PASS+1));
12
+ else echo " FAIL: $desc (expected $expected, got $actual)"; FAIL=$((FAIL+1)); fi
13
+ }
14
+
15
+ printf 'clean text line\n' > "$TMP/clean.txt"
16
+ printf 'good stuff\x00\x00\x00corrupted tail' > "$TMP/bad.txt"
17
+ # Same byte count as a 12-char file but tail is NUL-padded — wc -c cannot tell.
18
+ printf 'AAAAAA\x00\x00\x00\x00\x00\x00' > "$TMP/pad.txt"
19
+ printf 'binary\x00\x00data' > "$TMP/img.png"
20
+
21
+ run "empty input passes" '{}' 0
22
+ run "missing file passes" '{"tool_name":"Write","tool_input":{"file_path":"/nonexistent/xyz.txt"}}' 0
23
+ run "clean text passes" "{\"tool_name\":\"Write\",\"tool_input\":{\"file_path\":\"$TMP/clean.txt\"}}" 0
24
+ run "NUL corruption flagged" "{\"tool_name\":\"Write\",\"tool_input\":{\"file_path\":\"$TMP/bad.txt\"}}" 2
25
+ run "NUL padding (size-preserving) flagged" "{\"tool_name\":\"Edit\",\"tool_input\":{\"file_path\":\"$TMP/pad.txt\"}}" 2
26
+ run "binary extension skipped" "{\"tool_name\":\"Write\",\"tool_input\":{\"file_path\":\"$TMP/img.png\"}}" 0
27
+
28
+ echo "Results: $PASS/$((PASS+FAIL)) passed"
29
+ [ "$FAIL" -eq 0 ]
@@ -0,0 +1,178 @@
1
+ #!/bin/bash
2
+ # write-overwrite-confirm judged an overwrite by size alone, so the accident in
3
+ # issue #78273 passed silently: the agent read FIVE lines of a hand-built file,
4
+ # confirmed it had content, then replaced the whole file with its own analysis.
5
+ # The replacement was not smaller, so the size check never fired. The original
6
+ # was not in git and could not be recovered.
7
+ #
8
+ # Check 2 asks the other question — was the content being destroyed ever read —
9
+ # using the coverage recorded by examples/record-read-coverage.sh.
10
+ #
11
+ # The controls matter as much as the failing case. A guard that fires on every
12
+ # overwrite is the failure mode read-before-edit.sh already had while its
13
+ # recorder was missing, so these tests pin down when it must stay silent:
14
+ # - the whole file was read
15
+ # - the file is new, or small
16
+ # - the replacement keeps the existing content
17
+ # - no recorder is installed (no coverage information exists)
18
+
19
+ set -u
20
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
21
+ HOOK="$ROOT/examples/write-overwrite-confirm.sh"
22
+ REC="$ROOT/examples/record-read-coverage.sh"
23
+ WORK="$(mktemp -d)"
24
+ trap 'rm -rf "$WORK" 2>/dev/null' EXIT
25
+ PASS=0; FAIL=0
26
+
27
+ mk_file() { # mk_file <path> <lines>
28
+ local p="$1" n="$2" i
29
+ : > "$p"
30
+ for ((i = 1; i <= n; i++)); do printf 'original line %d\n' "$i" >> "$p"; done
31
+ }
32
+
33
+ payload() { # payload <tool> <file> <content>
34
+ python3 -c '
35
+ import json,sys
36
+ print(json.dumps({"tool_name": sys.argv[1],
37
+ "tool_input": {"file_path": sys.argv[2], "content": sys.argv[3]}}))' "$@"
38
+ }
39
+
40
+ read_payload() { # read_payload <file> <offset> <limit>
41
+ python3 -c '
42
+ import json,sys
43
+ ti={"file_path": sys.argv[1]}
44
+ if int(sys.argv[2]): ti["offset"]=int(sys.argv[2])
45
+ if int(sys.argv[3]): ti["limit"]=int(sys.argv[3])
46
+ print(json.dumps({"tool_name":"Read","tool_input":ti}))' "$@"
47
+ }
48
+
49
+ check() { # check <label> <expect-fire:0|1> <log> <write-json> [env...]
50
+ local label="$1" expect="$2" log="$3" json="$4"; shift 4
51
+ local out fired
52
+ out=$(printf '%s' "$json" | env CC_READ_LOG="$log" "$@" bash "$HOOK" 2>&1 >/dev/null)
53
+ if printf '%s' "$out" | grep -q 'never read'; then fired=1; else fired=0; fi
54
+ if [ "$fired" = "$expect" ]; then
55
+ PASS=$((PASS + 1)); printf ' ok %s\n' "$label"
56
+ else
57
+ FAIL=$((FAIL + 1)); printf ' FAIL %s (expected fire=%s, got %s)\n' "$label" "$expect" "$fired"
58
+ printf '%s\n' "$out" | sed 's/^/ /' | head -4
59
+ fi
60
+ }
61
+
62
+ # ---- the accident: five lines read, whole file replaced -------------------
63
+ F="$WORK/math.txt"; LOG="$WORK/log1"
64
+ mk_file "$F" 200
65
+ printf '%s' "$(read_payload "$F" 1 5)" | CC_READ_LOG="$LOG" bash "$REC" >/dev/null 2>&1
66
+ NEW=$(python3 -c 'print("\n".join("analysis line %d" % i for i in range(1,221)))')
67
+ check "#78273: read 5 of 200 lines, replaced all 200" 1 "$LOG" "$(payload Write "$F" "$NEW")"
68
+
69
+ # ---- same write, but the file was actually read in full ------------------
70
+ LOG2="$WORK/log2"
71
+ printf '%s' "$(read_payload "$F" 0 0)" | CC_READ_LOG="$LOG2" bash "$REC" >/dev/null 2>&1
72
+ check "control: whole file read first" 0 "$LOG2" "$(payload Write "$F" "$NEW")"
73
+
74
+ # ---- read in pages that together cover the file --------------------------
75
+ LOG3="$WORK/log3"
76
+ printf '%s' "$(read_payload "$F" 1 100)" | CC_READ_LOG="$LOG3" bash "$REC" >/dev/null 2>&1
77
+ printf '%s' "$(read_payload "$F" 101 100)" | CC_READ_LOG="$LOG3" bash "$REC" >/dev/null 2>&1
78
+ check "control: read as two pages covering the whole file" 0 "$LOG3" "$(payload Write "$F" "$NEW")"
79
+
80
+ # ---- pages that leave a hole --------------------------------------------
81
+ LOG4="$WORK/log4"
82
+ printf '%s' "$(read_payload "$F" 1 50)" | CC_READ_LOG="$LOG4" bash "$REC" >/dev/null 2>&1
83
+ printf '%s' "$(read_payload "$F" 150 51)" | CC_READ_LOG="$LOG4" bash "$REC" >/dev/null 2>&1
84
+ check "pages with a gap in the middle still count as unread" 1 "$LOG4" "$(payload Write "$F" "$NEW")"
85
+
86
+ # ---- the replacement keeps every existing line ---------------------------
87
+ LOG5="$WORK/log5"
88
+ printf '%s' "$(read_payload "$F" 1 5)" | CC_READ_LOG="$LOG5" bash "$REC" >/dev/null 2>&1
89
+ KEEP=$(cat "$F"; printf 'appended line\n')
90
+ check "control: replacement keeps all existing lines" 0 "$LOG5" "$(payload Write "$F" "$KEEP")"
91
+
92
+ # ---- small file ----------------------------------------------------------
93
+ S="$WORK/small.txt"; LOG6="$WORK/log6"
94
+ mk_file "$S" 5
95
+ printf '%s' "$(read_payload "$S" 1 1)" | CC_READ_LOG="$LOG6" bash "$REC" >/dev/null 2>&1
96
+ check "control: file below the minimum line count" 0 "$LOG6" "$(payload Write "$S" "replaced")"
97
+
98
+ # ---- new file ------------------------------------------------------------
99
+ check "control: file does not exist yet" 0 "$WORK/log1" "$(payload Write "$WORK/brand-new.txt" "$NEW")"
100
+
101
+ # ---- no recorder installed ----------------------------------------------
102
+ check "control: no read log at all stays silent" 0 "$WORK/absent-log" "$(payload Write "$F" "$NEW")"
103
+
104
+ # ---- blocking mode returns exit 2 ---------------------------------------
105
+ printf '%s' "$(payload Write "$F" "$NEW")" | \
106
+ env CC_READ_LOG="$WORK/log1" CC_WRITE_OVERWRITE_BLOCK=1 bash "$HOOK" >/dev/null 2>&1
107
+ if [ "$?" = "2" ]; then
108
+ PASS=$((PASS + 1)); echo " ok CC_WRITE_OVERWRITE_BLOCK=1 refuses the write (exit 2)"
109
+ else
110
+ FAIL=$((FAIL + 1)); echo " FAIL CC_WRITE_OVERWRITE_BLOCK=1 did not exit 2"
111
+ fi
112
+
113
+ # ---- the label must match what the run actually does --------------------
114
+ OUT=$(printf '%s' "$(payload Write "$F" "$NEW")" | env CC_READ_LOG="$WORK/log1" bash "$HOOK" 2>&1 >/dev/null)
115
+ if printf '%s' "$OUT" | grep -q 'WARNING: overwriting' && ! printf '%s' "$OUT" | grep -q 'BLOCKED'; then
116
+ PASS=$((PASS + 1)); echo " ok warn mode says WARNING, not BLOCKED"
117
+ else
118
+ FAIL=$((FAIL + 1)); echo " FAIL warn mode mislabels its own verdict"
119
+ fi
120
+
121
+ # ---- the original size check still works --------------------------------
122
+ OUT=$(printf '%s' "$(payload Write "$F" "tiny")" | env CC_READ_LOG="$WORK/log2" bash "$HOOK" 2>&1 >/dev/null)
123
+ if printf '%s' "$OUT" | grep -q 'File shrinking from'; then
124
+ PASS=$((PASS + 1)); echo " ok original shrink warning is unchanged"
125
+ else
126
+ FAIL=$((FAIL + 1)); echo " FAIL original shrink warning regressed"
127
+ fi
128
+
129
+ # ---- blocking mode when the recorder was never installed ----------------
130
+ # Measured on 2026-09-19: with CC_WRITE_OVERWRITE_BLOCK=1 set but no read log
131
+ # on disk, every write returned exit 0. Check 2 exits early without coverage
132
+ # information, and check 1 - the shrink check, the one that fires on the
133
+ # 500-lines-to-10 accident this hook was written for - never looked at the
134
+ # variable at all. So the documented "refuse the write" mode refused nothing
135
+ # for anyone who installed this hook without record-read-coverage.sh.
136
+ #
137
+ # The controls below are what make the fix meaningful: a hook that simply
138
+ # exits 2 whenever BLOCK is set would pass the first case and fail all four
139
+ # after it.
140
+ BIG="$WORK/shrink.txt"; NOLOG="$WORK/no-recorder-was-ever-installed"
141
+ mk_file "$BIG" 60
142
+ SAME=$(python3 -c 'print("\n".join("replacement line %d" % i for i in range(1,61)))')
143
+
144
+ verdict() { # verdict <label> <expect-exit> <write-json> [env...]
145
+ local label="$1" expect="$2" json="$3"; shift 3
146
+ local out rc
147
+ out=$(printf '%s' "$json" | env CC_READ_LOG="$NOLOG" "$@" bash "$HOOK" 2>&1 >/dev/null); rc=$?
148
+ if [ "$rc" = "$expect" ]; then
149
+ PASS=$((PASS + 1)); printf ' ok %s\n' "$label"
150
+ else
151
+ FAIL=$((FAIL + 1)); printf ' FAIL %s (expected exit %s, got %s)\n' "$label" "$expect" "$rc"
152
+ printf '%s\n' "$out" | sed 's/^/ /' | head -3
153
+ fi
154
+ }
155
+
156
+ verdict "shrink + BLOCK=1 + no recorder refuses (exit 2)" 2 \
157
+ "$(payload Write "$BIG" "one line replaces sixty")" CC_WRITE_OVERWRITE_BLOCK=1
158
+ verdict "control: same write without BLOCK only warns" 0 \
159
+ "$(payload Write "$BIG" "one line replaces sixty")"
160
+ verdict "control: BLOCK=1 but the file does not shrink" 0 \
161
+ "$(payload Write "$BIG" "$SAME")" CC_WRITE_OVERWRITE_BLOCK=1
162
+ verdict "control: BLOCK=1 on a file under the size floor" 0 \
163
+ "$(payload Write "$WORK/small.txt" "x")" CC_WRITE_OVERWRITE_BLOCK=1
164
+ verdict "control: BLOCK=1 creating a file that does not exist" 0 \
165
+ "$(payload Write "$WORK/still-brand-new.txt" "x")" CC_WRITE_OVERWRITE_BLOCK=1
166
+
167
+ # The word printed has to match the exit code, in this path too.
168
+ OUT=$(printf '%s' "$(payload Write "$BIG" "one line")" | \
169
+ env CC_READ_LOG="$NOLOG" CC_WRITE_OVERWRITE_BLOCK=1 bash "$HOOK" 2>&1 >/dev/null)
170
+ if printf '%s' "$OUT" | grep -q 'BLOCKED: File shrinking'; then
171
+ PASS=$((PASS + 1)); echo " ok blocking shrink says BLOCKED, not WARNING"
172
+ else
173
+ FAIL=$((FAIL + 1)); echo " FAIL blocking shrink still labels itself WARNING"
174
+ fi
175
+
176
+ echo ""
177
+ echo "PASS=$PASS FAIL=$FAIL"
178
+ [ "$FAIL" -eq 0 ]
@@ -0,0 +1,55 @@
1
+ #!/bin/bash
2
+ # Tests for wsl-host-disk-space-guard.sh
3
+ # Run: bash tests/wsl-host-disk-space-guard.test.sh
4
+ #
5
+ # Note: this hook reads real `df` output. We can't easily mock the
6
+ # filesystem, so we test what we can deterministically:
7
+ # 1. The hook exits 0 on a healthy system (most CI environments).
8
+ # 2. The hook respects the DISABLE flag.
9
+ # 3. The hook does not trip on environments with no /mnt/c.
10
+ set -euo pipefail
11
+
12
+ PASS=0
13
+ FAIL=0
14
+ HOOK="$(dirname "$0")/../examples/wsl-host-disk-space-guard.sh"
15
+
16
+ run_hook() {
17
+ local input="$1" expected_exit="$2" desc="$3" extra_env="${4:-}"
18
+ local actual_exit=0
19
+ if [ -n "$extra_env" ]; then
20
+ # shellcheck disable=SC2086
21
+ echo "$input" | env $extra_env bash "$HOOK" >/dev/null 2>/dev/null || actual_exit=$?
22
+ else
23
+ echo "$input" | bash "$HOOK" >/dev/null 2>/dev/null || actual_exit=$?
24
+ fi
25
+ if [ "$actual_exit" -eq "$expected_exit" ]; then
26
+ echo " PASS: $desc"
27
+ PASS=$((PASS + 1))
28
+ else
29
+ echo " FAIL: $desc (expected exit $expected_exit, got $actual_exit)"
30
+ FAIL=$((FAIL + 1))
31
+ fi
32
+ }
33
+
34
+ echo "wsl-host-disk-space-guard.sh tests"
35
+ echo ""
36
+
37
+ # --- DISABLE flag bypasses everything (always exits 0) ---
38
+ run_hook '{"tool_input":{"command":"echo hello"}}' 0 "DISABLE flag exits 0" "CC_HOST_DISK_DISABLE=1 CC_HOST_DISK_BLOCK_PCT=0"
39
+
40
+ # --- With block thresholds at 100, never trips block (any used % is < 100 + small margin) ---
41
+ # We use 101 so even a 100%-full disk stays under threshold.
42
+ run_hook '{"tool_input":{"command":"echo hello"}}' 0 "Generous thresholds: no block" "CC_HOST_DISK_BLOCK_PCT=101 CC_LINUX_DISK_BLOCK_PCT=101 CC_HOST_DISK_WARN_PCT=101 CC_LINUX_DISK_WARN_PCT=101"
43
+
44
+ # --- Block when host threshold is set absurdly low (and /mnt/c exists or doesn't) ---
45
+ # If /mnt/c does not exist (non-WSL CI), block only triggers from / or /home,
46
+ # whose usage is generally well above 0%. Use threshold 0 to force the block branch.
47
+ run_hook '{"tool_input":{"command":"echo hello"}}' 2 "Block when LINUX_BLOCK threshold is 0" "CC_LINUX_DISK_BLOCK_PCT=0"
48
+
49
+ # --- Empty / missing input ---
50
+ run_hook '{}' 0 "Allow empty input on healthy system" "CC_HOST_DISK_BLOCK_PCT=101 CC_LINUX_DISK_BLOCK_PCT=101"
51
+ run_hook '{"tool_input":{}}' 0 "Allow missing command" "CC_HOST_DISK_BLOCK_PCT=101 CC_LINUX_DISK_BLOCK_PCT=101"
52
+
53
+ echo ""
54
+ echo "Results: $PASS passed, $FAIL failed out of $((PASS + FAIL)) tests"
55
+ [ "$FAIL" -eq 0 ] && exit 0 || exit 1