cc-safe-setup 28.4.8 → 28.4.9

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 (147) hide show
  1. package/examples/auto-approve-docker.sh +1 -0
  2. package/examples/auto-approve-test.sh +1 -0
  3. package/examples/auto-compact-prep.sh +19 -8
  4. package/examples/auto-git-checkpoint.sh +1 -0
  5. package/examples/backup-before-refactor.sh +1 -0
  6. package/examples/branch-naming-convention.sh +1 -0
  7. package/examples/check-accessibility.sh +1 -0
  8. package/examples/check-aria-labels.sh +1 -0
  9. package/examples/check-charset-meta.sh +1 -0
  10. package/examples/check-cookie-flags.sh +1 -0
  11. package/examples/check-cors-config.sh +1 -0
  12. package/examples/check-csp-headers.sh +1 -0
  13. package/examples/check-csrf-protection.sh +1 -0
  14. package/examples/check-dependency-age.sh +1 -0
  15. package/examples/check-dependency-license.sh +1 -0
  16. package/examples/check-dockerfile-best-practice.sh +1 -0
  17. package/examples/check-error-boundaries.sh +1 -0
  18. package/examples/check-error-handling.sh +1 -0
  19. package/examples/check-error-message.sh +1 -0
  20. package/examples/check-error-stack.sh +1 -0
  21. package/examples/check-favicon.sh +1 -0
  22. package/examples/check-git-hooks-compat.sh +1 -0
  23. package/examples/check-gitattributes.sh +1 -0
  24. package/examples/check-https-redirect.sh +1 -0
  25. package/examples/check-input-validation.sh +1 -0
  26. package/examples/check-lang-attribute.sh +1 -0
  27. package/examples/check-npm-scripts-exist.sh +1 -0
  28. package/examples/check-package-size.sh +1 -0
  29. package/examples/check-port-availability.sh +1 -0
  30. package/examples/check-rate-limiting.sh +1 -0
  31. package/examples/check-return-types.sh +1 -0
  32. package/examples/check-semantic-versioning.sh +1 -0
  33. package/examples/check-test-naming.sh +1 -0
  34. package/examples/check-tls-version.sh +1 -0
  35. package/examples/check-viewport-meta.sh +1 -0
  36. package/examples/claudemd-enforcer.sh +1 -0
  37. package/examples/cors-star-warn.sh +1 -0
  38. package/examples/docker-volume-guard.sh +1 -0
  39. package/examples/edit-verify.sh +1 -0
  40. package/examples/env-naming-convention.sh +1 -0
  41. package/examples/env-prod-guard.sh +1 -0
  42. package/examples/env-required-check.sh +1 -0
  43. package/examples/file-size-limit.sh +1 -0
  44. package/examples/git-hook-bypass-guard.sh +1 -0
  45. package/examples/git-merge-conflict-prevent.sh +1 -0
  46. package/examples/git-message-length.sh +1 -0
  47. package/examples/git-submodule-guard.sh +1 -0
  48. package/examples/git-tag-guard.sh +1 -0
  49. package/examples/gitignore-check.sh +1 -0
  50. package/examples/log-level-guard.sh +1 -0
  51. package/examples/max-file-count-guard.sh +1 -0
  52. package/examples/max-file-delete-count.sh +1 -0
  53. package/examples/max-function-length.sh +1 -0
  54. package/examples/max-import-count.sh +1 -0
  55. package/examples/max-subagent-count.sh +1 -0
  56. package/examples/mcp-tool-guard.sh +1 -0
  57. package/examples/no-absolute-import.sh +1 -0
  58. package/examples/no-alert-confirm-prompt.sh +1 -0
  59. package/examples/no-anonymous-default-export.sh +1 -0
  60. package/examples/no-any-type.sh +1 -0
  61. package/examples/no-assignment-in-condition.sh +1 -0
  62. package/examples/no-callback-hell.sh +1 -0
  63. package/examples/no-circular-dependency.sh +1 -0
  64. package/examples/no-cleartext-storage.sh +1 -0
  65. package/examples/no-commented-code.sh +1 -0
  66. package/examples/no-commit-fixup.sh +1 -0
  67. package/examples/no-console-assert.sh +1 -0
  68. package/examples/no-console-error-swallow.sh +1 -0
  69. package/examples/no-console-in-prod.sh +1 -0
  70. package/examples/no-console-log.sh +1 -0
  71. package/examples/no-console-time.sh +1 -0
  72. package/examples/no-curl-upload.sh +1 -0
  73. package/examples/no-dangerouslySetInnerHTML.sh +1 -0
  74. package/examples/no-debug-in-commit.sh +1 -0
  75. package/examples/no-deep-nesting.sh +1 -0
  76. package/examples/no-default-credentials.sh +1 -0
  77. package/examples/no-disabled-test.sh +1 -0
  78. package/examples/no-document-write.sh +1 -0
  79. package/examples/no-empty-function.sh +1 -0
  80. package/examples/no-eval-in-template.sh +1 -0
  81. package/examples/no-eval.sh +1 -0
  82. package/examples/no-exec-user-input.sh +1 -0
  83. package/examples/no-floating-promises.sh +1 -0
  84. package/examples/no-force-install.sh +1 -0
  85. package/examples/no-git-rebase-public.sh +1 -0
  86. package/examples/no-global-state.sh +1 -0
  87. package/examples/no-hardcoded-port.sh +1 -0
  88. package/examples/no-hardcoded-url.sh +1 -0
  89. package/examples/no-helmet-missing.sh +1 -0
  90. package/examples/no-http-without-https.sh +1 -0
  91. package/examples/no-inline-style.sh +1 -0
  92. package/examples/no-innerhtml.sh +1 -0
  93. package/examples/no-install-global.sh +1 -0
  94. package/examples/no-jwt-in-url.sh +1 -0
  95. package/examples/no-large-commit.sh +1 -0
  96. package/examples/no-localhost-expose.sh +1 -0
  97. package/examples/no-magic-number.sh +1 -0
  98. package/examples/no-md5-sha1.sh +1 -0
  99. package/examples/no-mixed-line-endings.sh +1 -0
  100. package/examples/no-mutation-in-reducer.sh +1 -0
  101. package/examples/no-nested-ternary.sh +1 -0
  102. package/examples/no-network-exfil.sh +1 -0
  103. package/examples/no-open-redirect.sh +1 -0
  104. package/examples/no-package-downgrade.sh +1 -0
  105. package/examples/no-package-lock-edit.sh +1 -0
  106. package/examples/no-path-join-user-input.sh +1 -0
  107. package/examples/no-port-bind.sh +1 -0
  108. package/examples/no-process-exit.sh +1 -0
  109. package/examples/no-prototype-pollution.sh +1 -0
  110. package/examples/no-push-without-ci.sh +1 -0
  111. package/examples/no-raw-password-in-url.sh +1 -0
  112. package/examples/no-root-write.sh +1 -0
  113. package/examples/no-secrets-in-logs.sh +1 -0
  114. package/examples/no-sensitive-log.sh +1 -0
  115. package/examples/no-sleep-in-hooks.sh +1 -0
  116. package/examples/no-string-concat-sql.sh +1 -0
  117. package/examples/no-sudo-guard.sh +1 -0
  118. package/examples/no-sync-fs.sh +1 -0
  119. package/examples/no-todo-in-merge.sh +1 -0
  120. package/examples/no-todo-without-issue.sh +1 -0
  121. package/examples/no-unused-import.sh +1 -0
  122. package/examples/no-var-keyword.sh +1 -0
  123. package/examples/no-wildcard-cors.sh +1 -0
  124. package/examples/no-wildcard-delete.sh +1 -0
  125. package/examples/no-wildcard-import.sh +1 -0
  126. package/examples/no-with-statement.sh +1 -0
  127. package/examples/no-write-outside-src.sh +1 -0
  128. package/examples/no-xml-external-entity.sh +1 -0
  129. package/examples/npm-audit-warn.sh +1 -0
  130. package/examples/npm-publish-guard.sh +1 -0
  131. package/examples/npm-script-injection.sh +1 -0
  132. package/examples/package-json-guard.sh +1 -0
  133. package/examples/pr-description-check.sh +1 -0
  134. package/examples/prefer-const.sh +1 -0
  135. package/examples/prefer-optional-chaining.sh +1 -0
  136. package/examples/rate-limit-guard.sh +1 -0
  137. package/examples/readme-exists-check.sh +1 -0
  138. package/examples/session-state-saver.sh +1 -0
  139. package/examples/session-summary.sh +1 -0
  140. package/examples/skill-gate.sh +1 -0
  141. package/examples/sql-injection-detect.sh +1 -0
  142. package/examples/subagent-budget-guard.sh +1 -0
  143. package/examples/test-before-commit.sh +1 -0
  144. package/examples/timezone-guard.sh +1 -0
  145. package/examples/usage-warn.sh +1 -0
  146. package/examples/write-test-ratio.sh +1 -0
  147. package/package.json +1 -1
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  INPUT=$(cat)
2
3
  TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
3
4
  [ "$TOOL" != "Bash" ] && exit 0
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
2
3
  [ -z "$COMMAND" ] && exit 0
3
4
  if echo "$COMMAND" | grep -qE '^\s*(npm\s+test|npm\s+run\s+test|npx\s+(jest|vitest|mocha|ava|tap|playwright\s+test|cypress\s+run)|yarn\s+test|pnpm\s+test|bun\s+test)\b'; then
@@ -1,3 +1,18 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # auto-compact-prep.sh — Save checkpoint before context compaction
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # Tracks tool call count per session. When threshold is reached,
7
+ # saves a checkpoint file with git state so Claude can recover
8
+ # context after automatic compaction.
9
+ #
10
+ # TRIGGER: PreToolUse MATCHER: ""
11
+ #
12
+ # CONFIG:
13
+ # CC_COMPACT_PREP_THRESHOLD=200 (save checkpoint after N tool calls)
14
+ # ================================================================
15
+
1
16
  INPUT=$(cat)
2
17
  STATE_DIR="${HOME}/.claude"
3
18
  COUNTER_FILE="${STATE_DIR}/session-call-count"
@@ -5,15 +20,11 @@ PREP_FLAG="${STATE_DIR}/compact-prep-done"
5
20
  CHECKPOINT=".claude/pre-compact-checkpoint.md"
6
21
  COUNT=0
7
22
  [ -f "$COUNTER_FILE" ] && COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo 0)
8
- if [ "$COUNT" -eq 0 ]; then
9
- COUNT=1
10
- echo "$COUNT" > "$COUNTER_FILE"
11
- else
12
- COUNT=$((COUNT + 1))
13
- echo "$COUNT" > "$COUNTER_FILE"
14
- fi
23
+ COUNT=$((COUNT + 1))
24
+ echo "$COUNT" > "$COUNTER_FILE"
25
+
15
26
  THRESHOLD=${CC_COMPACT_PREP_THRESHOLD:-200}
16
- if (( COUNT >= THRESHOLD )) && [ ! -f "$PREP_FLAG" ]; then
27
+ if [ "$COUNT" -ge "$THRESHOLD" ] && [ ! -f "$PREP_FLAG" ]; then
17
28
  mkdir -p "$(dirname "$CHECKPOINT")" 2>/dev/null
18
29
  BRANCH=$(git branch --show-current 2>/dev/null || echo "?")
19
30
  DIRTY=$(git status --porcelain 2>/dev/null | wc -l)
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  INPUT=$(cat)
2
3
  TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
3
4
  FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  INPUT=$(cat)
2
3
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
4
  CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  INPUT=$(cat)
2
3
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
4
  CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "<img[^>]+(?!alt=)" && echo "NOTE: img without alt attribute" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "<(button|a|input)[^>]*>" && ! echo "$CONTENT" | grep -q "aria-" && echo "NOTE: Interactive element without ARIA" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -q "<head" && ! echo "$CONTENT" | grep -q "charset" && echo "NOTE: Missing charset meta" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "setCookie|res\.cookie" && ! echo "$CONTENT" | grep -q "secure" && echo "NOTE: Cookie without secure flag" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "cors\(\{.*origin.*true" && echo "NOTE: Permissive CORS config" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "Content-Security-Policy" || (echo "$CONTENT" | grep -q "helmet" && echo "NOTE: Consider adding CSP headers" >&2)
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "<form.*method.*POST" && ! echo "$CONTENT" | grep -qE "csrf|_token|csrfmiddleware" && echo "NOTE: Form without CSRF protection" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "NOTE: Run npm outdated to check dependency age" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  COMMAND=$(cat | jq -r ".tool_input.command // empty" 2>/dev/null); echo "$COMMAND" | grep -qE "npm\s+install\s+\w" && echo "NOTE: Check dependency license before adding" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  FILE=$(cat | jq -r '.tool_input.file_path // empty' 2>/dev/null)
2
3
  case "$FILE" in *Dockerfile*) ;; *) exit 0;; esac
3
4
  CONTENT=$(cat | jq -r '.tool_input.new_string // empty' 2>/dev/null)
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "class.*extends.*Component|function.*\(\)" && echo "$CONTENT" | grep -q "render" && ! echo "$CONTENT" | grep -q "ErrorBoundary" && echo "NOTE: Consider adding ErrorBoundary" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "\.then\(" && ! echo "$CONTENT" | grep -q "\.catch" && echo "NOTE: Promise without .catch()" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "throw new Error\(['\"](error|Error|something went wrong)" && echo "NOTE: Generic error message — be specific" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "res\.(send|json)\(.*err\.(stack|message)" && echo "WARNING: Exposing error details to client" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -q "<head" && ! echo "$CONTENT" | grep -q "favicon" && echo "NOTE: Missing favicon link" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  INPUT=$(cat)
2
3
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
4
  [ -z "$COMMAND" ] && exit 0
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  # TRIGGER: PreToolUse MATCHER: "Bash"
2
3
  COMMAND=$(cat | jq -r ".tool_input.command // empty" 2>/dev/null)
3
4
  echo "$COMMAND" | grep -qE "git\s+add.*\.(zip|tar|bin|exe)" && [ ! -f ".gitattributes" ] && echo "NOTE: Binary file without .gitattributes LFS config" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "http://" && echo "$CONTENT" | grep -q "redirect" && ! echo "$CONTENT" | grep -q "https" && echo "NOTE: HTTP redirect without HTTPS" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "req\.(body|query|params)\.\w+" && ! echo "$CONTENT" | grep -qE "validate|sanitize|Joi|zod|yup" && echo "NOTE: User input without validation" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "<html[^>]*>" && ! echo "$CONTENT" | grep -q "lang=" && echo "NOTE: Missing lang attribute" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  FILE=$(cat | jq -r ".tool_input.file_path // empty" 2>/dev/null); case "$FILE" in *package.json) ;; *) exit 0;; esac; echo "$CONTENT" | grep -qE "npm run [a-z]+" && echo "NOTE: Verify referenced npm scripts exist" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  FILE=$(cat | jq -r '.tool_input.file_path // empty' 2>/dev/null)
2
3
  case "$FILE" in *package.json) ;; *) exit 0;; esac
3
4
  CONTENT=$(cat | jq -r '.tool_input.new_string // empty' 2>/dev/null)
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  COMMAND=$(cat | jq -r ".tool_input.command // empty" 2>/dev/null); echo "$COMMAND" | grep -qE "listen\(|--port|:3000|:8080" && echo "NOTE: Check port availability before starting server" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "app\.(get|post|put|delete)\(" && ! echo "$CONTENT" | grep -q "rateLimit" && echo "NOTE: API endpoint without rate limiting" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "function\s+\w+\([^)]*\)\s*{" && ! echo "$CONTENT" | grep -q ": " && echo "NOTE: Missing return type annotation" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "\"version\":\s*\"[^0-9]" && echo "NOTE: Non-semver version string" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "it\(['\"](test|check|should)\s" && echo "NOTE: Non-descriptive test name" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "TLSv1[^.]|SSLv3" && echo "WARNING: Weak TLS version" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -q "<head" && ! echo "$CONTENT" | grep -q "viewport" && echo "NOTE: Missing viewport meta" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  INPUT=$(cat)
2
3
  TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
3
4
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
2
3
  [ -z "$COMMAND" ] && exit 0
3
4
  # PostToolUse matcher: Edit|Write
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
2
3
  [ -z "$COMMAND" ] && exit 0
3
4
  if echo "$COMMAND" | grep -qE '\bdocker\s+volume\s+(rm|prune)\b'; then
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  INPUT=$(cat)
2
3
  TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
3
4
  FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  # TRIGGER: PostToolUse MATCHER: "Edit|Write"
2
3
  CONTENT=$(cat | jq -r ".tool_input.new_string // empty" 2>/dev/null)
3
4
  [ -z "$CONTENT" ] && exit 0
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
2
3
  [ -z "$COMMAND" ] && exit 0
3
4
  if echo "$COMMAND" | grep -qiE "(NODE_ENV|RAILS_ENV|FLASK_ENV)=production"; then echo "WARNING: Production env detected in command" >&2; fi
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "process\.env\.\w+\s*\|\|" || echo "$CONTENT" | grep -qE "process\.env\.\w+!" && echo "NOTE: Env var without default — add fallback" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  INPUT=$(cat)
2
3
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
4
  CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
2
3
  [ -z "$COMMAND" ] && exit 0
3
4
  if echo "$COMMAND" | grep -qE "git\s+(commit|push|merge).*--no-verify"; then echo "WARNING: --no-verify bypasses git hooks" >&2; fi
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  INPUT=$(cat)
2
3
  CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
3
4
  [ -z "$CONTENT" ] && exit 0
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
2
3
  [ -z "$COMMAND" ] && exit 0
3
4
  echo "$COMMAND" | grep -qE "git\s+commit\s+-m" || exit 0; MSG=$(echo "$COMMAND" | grep -oP "(?<=-m\s[\x27\x22])[^\x27\x22]+"); [ ${#MSG} -lt 10 ] && echo "WARNING: Commit message too short (${#MSG} chars)" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
2
3
  [ -z "$COMMAND" ] && exit 0
3
4
  if echo "$COMMAND" | grep -qE '\bgit\s+submodule\s+(deinit|rm)\b'; then
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
2
3
  [ -z "$COMMAND" ] && exit 0
3
4
  if echo "$COMMAND" | grep -qE 'git\s+tag\s+(-a\s+|-d\s+)?v'; then
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
2
3
  [ -z "$COMMAND" ] && exit 0
3
4
  echo "$COMMAND" | grep -qE '^\s*git\s+add' || exit 0
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  INPUT=$(cat)
2
3
  CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
3
4
  [ -z "$CONTENT" ] && exit 0
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  INPUT=$(cat)
2
3
  FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
3
4
  [ -z "$FILE" ] && exit 0
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
2
3
  [ -z "$COMMAND" ] && exit 0
3
4
  if echo "$COMMAND" | grep -qE "^\s*rm\b"; then COUNT=$(echo "$COMMAND" | tr " " "\n" | grep -cvE "^-" | head -1); [ "$COUNT" -gt 5 ] && echo "WARNING: Deleting $COUNT files at once" >&2; fi
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  LINES=$(echo "$CONTENT" | wc -l); [ "$LINES" -gt 100 ] && echo "NOTE: Edit adds 100+ lines — consider splitting" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  IMPORTS=$(echo "$CONTENT" | grep -cE "^(import|from|require)" || echo 0); [ "$IMPORTS" -gt 20 ] && echo "NOTE: $IMPORTS imports — consider splitting module" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
2
3
  [ -z "$COMMAND" ] && exit 0
3
4
  STATE="/tmp/cc-subagent-count"; C=$(cat "$STATE" 2>/dev/null || echo 0); echo $((C+1)) > "$STATE"; [ "$C" -gt 5 ] && echo "WARNING: $C subagents spawned this session" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  INPUT=$(cat)
2
3
  TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
3
4
  echo "$TOOL" | grep -q '^mcp__' || exit 0
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  if echo "$CONTENT" | grep -qE "from ['\"]/|require\(['\"]/"; then echo "NOTE: Absolute import path detected" >&2; fi
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "\balert\(|\bconfirm\(|\bprompt\(" && echo "WARNING: alert/confirm/prompt in code" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  # TRIGGER: PostToolUse MATCHER: "Edit|Write"
2
3
  CONTENT=$(cat | jq -r ".tool_input.new_string // empty" 2>/dev/null)
3
4
  [ -z "$CONTENT" ] && exit 0
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE ": any\b|<any>" && echo "NOTE: TypeScript any detected — add proper types" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "if\s*\([^=]*=[^=]" && echo "NOTE: Assignment in condition (use ===)" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  DEPTH=$(echo "$CONTENT" | grep -c "function\s*("); [ "$DEPTH" -gt 3 ] && echo "NOTE: Possible callback hell ($DEPTH levels)" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  FILE=$(cat | jq -r '.tool_input.file_path // empty' 2>/dev/null)
2
3
  case "$FILE" in *package.json) ;; *) exit 0;; esac
3
4
  CONTENT=$(cat | jq -r '.tool_input.new_string // empty' 2>/dev/null)
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "localStorage\.setItem.*password|sessionStorage.*token" && echo "WARNING: Storing secrets in browser storage" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  COMMENTED=$(echo "$CONTENT" | grep -cE "^\s*(//|#)\s*(if|for|while|function|const|let|var|import|class)" || echo 0); [ "$COMMENTED" -gt 5 ] && echo "NOTE: Large block of commented code — delete or uncomment" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  INPUT=$(cat)
2
3
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
4
  [ -z "$COMMAND" ] && exit 0
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -q "console.assert" && echo "NOTE: console.assert in code" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  if echo "$CONTENT" | grep -qE "catch\s*\([^)]*\)\s*\{[\s\n]*\}|except:[\s\n]*pass"; then echo "WARNING: Empty catch/except block swallows errors" >&2; fi
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  FILE=$(cat | jq -r ".tool_input.file_path // empty" 2>/dev/null); case "$FILE" in *test*|*spec*) exit 0;; esac; echo "$CONTENT" | grep -qE "console\.(log|warn)" && echo "NOTE: console statement in non-test file" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  INPUT=$(cat)
2
3
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
4
  CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "console\.(time|timeEnd|timeLog)" && echo "NOTE: console.time left in code" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
2
3
  [ -z "$COMMAND" ] && exit 0
3
4
  if echo "$COMMAND" | grep -qE 'curl\s+.*(-X\s+POST|-d\s+@|--data-binary|--upload-file)'; then
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -q "dangerouslySetInnerHTML" && echo "WARNING: XSS risk via dangerouslySetInnerHTML" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
2
3
  echo "$COMMAND" | grep -qE 'git\s+commit' || exit 0
3
4
  git diff --cached 2>/dev/null | grep -qE '^\+.*(debugger|pdb\.set_trace)' && echo "WARNING: Debug in staged" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  DEPTH=$(echo "$CONTENT" | awk "{n=0; for(i=1;i<=length;i++) if(substr(\$0,i,1)==\"{\") n++; if(n>m) m=n} END{print m}"); [ "$DEPTH" -gt 4 ] && echo "NOTE: Deep nesting ($DEPTH levels)" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qiE "password.*admin|pass.*1234|secret.*default" && echo "WARNING: Default credentials" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "\.(skip|only)\(|xit\(|xdescribe\(" && echo "WARNING: Disabled/focused test detected" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "document\.write\(" && echo "WARNING: document.write XSS risk" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "(function|=>)\s*\{[\s\n]*\}" && echo "NOTE: Empty function body" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "\$\{.*eval|new Function" && echo "WARNING: eval in template" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  INPUT=$(cat)
2
3
  COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
4
  CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "exec\(.*req\.|spawn\(.*req\." && echo "WARNING: exec with user input — command injection" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "^\s+\w+\.\w+\(" && echo "$CONTENT" | grep -q "await\|\.then\|\.catch" || echo "$CONTENT" | grep -qE "async" && echo "NOTE: Check for unhandled promises" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
2
3
  [ -z "$COMMAND" ] && exit 0
3
4
  if echo "$COMMAND" | grep -qE '\b(npm|pip|yarn)\s+install\s+.*--force'; then
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
2
3
  [ -z "$COMMAND" ] && exit 0
3
4
  if echo "$COMMAND" | grep -qE "git\s+rebase" && git log --oneline origin/$(git branch --show-current 2>/dev/null) 2>/dev/null | head -1 | grep -q .; then echo "WARNING: Rebasing pushed branch" >&2; fi
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE "^(let|var)\s+\w+\s*=" && echo "NOTE: Module-level mutable state — consider encapsulation" >&2
@@ -1,3 +1,4 @@
1
+ #!/bin/bash
1
2
  CONTENT=$(cat | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
2
3
  [ -z "$CONTENT" ] && exit 0
3
4
  echo "$CONTENT" | grep -qE ":(3000|8080|8000|5000|4000)[^0-9]" && echo "NOTE: Hardcoded port number — use env var" >&2