cc-safe-setup 29.6.39 → 29.7.0

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 (89) hide show
  1. package/.claude-plugin/marketplace.json +66 -0
  2. package/.claude-plugin/plugin.json +11 -0
  3. package/README.md +133 -12
  4. package/SETTINGS_REFERENCE.md +2 -0
  5. package/SKILL.md +47 -0
  6. package/TROUBLESHOOTING.md +26 -0
  7. package/examples/README.md +11 -1
  8. package/examples/activity-logger.sh +58 -0
  9. package/examples/allow-claude-settings.sh +3 -2
  10. package/examples/allow-git-hooks-dir.sh +3 -2
  11. package/examples/allow-protected-dirs.sh +3 -2
  12. package/examples/auto-approve-compound-git.sh +3 -0
  13. package/examples/auto-compact-context-monitor.sh +35 -0
  14. package/examples/auto-mode-safety-enforcer.sh +57 -0
  15. package/examples/background-task-guard.sh +57 -0
  16. package/examples/bash-heuristic-approver.sh +1 -1
  17. package/examples/broad-find-guard.sh +62 -0
  18. package/examples/cache-creation-spike-detector.sh +32 -0
  19. package/examples/case-insensitive-path-guard.sh +96 -0
  20. package/examples/cjk-punctuation-guard.sh +44 -0
  21. package/examples/clipboard-secret-guard.sh +29 -0
  22. package/examples/context-size-alert.sh +38 -0
  23. package/examples/context-usage-drift-alert.sh +33 -0
  24. package/examples/dangerous-pip-flag-guard.sh +51 -0
  25. package/examples/decision-warn.sh +59 -0
  26. package/examples/deny-bypass-detector.sh +143 -0
  27. package/examples/direnv-auto-reload.sh +9 -2
  28. package/examples/dotenv-commit-guard.sh +11 -5
  29. package/examples/dotenv-read-guard.sh +48 -0
  30. package/examples/dotfile-protection-guard.sh +60 -0
  31. package/examples/effort-tracking-logger.sh +30 -0
  32. package/examples/financial-operation-guard.sh +47 -0
  33. package/examples/full-rewrite-detector.sh +63 -0
  34. package/examples/home-critical-bash-guard.sh +56 -0
  35. package/examples/idle-session-cost-alert.sh +36 -0
  36. package/examples/model-version-alert.sh +18 -0
  37. package/examples/model-version-change-alert.sh +31 -0
  38. package/examples/move-delete-sequence-guard.sh +92 -0
  39. package/examples/pii-upload-guard.sh +72 -0
  40. package/examples/pr-duplicate-guard.sh +14 -0
  41. package/examples/production-port-kill-guard.sh +60 -0
  42. package/examples/proof-log-session.sh +62 -0
  43. package/examples/quota-reset-cycle-monitor.sh +30 -0
  44. package/examples/repo-visibility-guard.sh +33 -0
  45. package/examples/sandbox-relative-path-audit.sh +51 -0
  46. package/examples/session-agent-cost-limiter.sh +43 -0
  47. package/examples/session-cost-alert.sh +62 -0
  48. package/examples/session-memory-watchdog.sh +9 -0
  49. package/examples/settings-integrity-monitor.sh +55 -0
  50. package/examples/settings-json-model-guard.sh +89 -0
  51. package/examples/shell-config-truncation-guard.sh +97 -0
  52. package/examples/shell-wrapper-guard.sh +4 -4
  53. package/examples/subagent-spawn-rate-monitor.sh +34 -0
  54. package/examples/subcommand-chain-guard.sh +44 -0
  55. package/examples/system-dir-protection-guard.sh +100 -0
  56. package/examples/thinking-display-enforcer.sh +25 -0
  57. package/examples/tool-retry-budget-guard.sh +59 -0
  58. package/examples/worktree-branch-pollution-detector.sh +35 -0
  59. package/examples/worktree-create-log.sh +6 -0
  60. package/examples/worktree-hook-linker.sh +72 -0
  61. package/examples/worktree-remove-uncommitted-guard.sh +20 -0
  62. package/hooks/hooks.json +60 -0
  63. package/index.mjs +108 -6
  64. package/memory/market-anthropic-japan-strategy-2026-04-13.md +4 -0
  65. package/package.json +2 -2
  66. package/plugins/credential-guard/.claude-plugin/plugin.json +58 -0
  67. package/plugins/git-protection/.claude-plugin/plugin.json +58 -0
  68. package/plugins/safety-essentials/.claude-plugin/plugin.json +58 -0
  69. package/plugins/token-guard/.claude-plugin/plugin.json +51 -0
  70. package/skills/safety-setup/SKILL.md +47 -0
  71. package/tests/dotenv-read-guard.test.sh +65 -0
  72. package/tests/test-auto-mode-safety-enforcer.sh +55 -0
  73. package/tests/test-case-insensitive-path-guard.sh +78 -0
  74. package/tests/test-context-usage-drift-alert.sh +52 -0
  75. package/tests/test-dangerous-pip-flag-guard.sh +56 -0
  76. package/tests/test-dotfile-protection-guard.sh +68 -0
  77. package/tests/test-effort-tracking-logger.sh +55 -0
  78. package/tests/test-financial-operation-guard.sh +59 -0
  79. package/tests/test-home-critical-bash-guard.sh +59 -0
  80. package/tests/test-model-version-change-alert.sh +55 -0
  81. package/tests/test-move-delete-sequence-guard.sh +63 -0
  82. package/tests/test-pr-duplicate-guard.sh +29 -0
  83. package/tests/test-quota-reset-cycle-monitor.sh +52 -0
  84. package/tests/test-shell-config-truncation-guard.sh +104 -0
  85. package/tests/test-subagent-spawn-rate-monitor.sh +43 -0
  86. package/tests/test-system-dir-protection-guard.sh +81 -0
  87. package/tests/test-tool-retry-budget-guard.sh +75 -0
  88. package/tests/test-worktree-branch-pollution-detector.sh +50 -0
  89. package/tests/test-worktree-lifecycle-hooks.sh +29 -0
@@ -0,0 +1,35 @@
1
+ #!/bin/bash
2
+ # auto-compact-context-monitor.sh — Detect unexpected auto-compaction via context size drops
3
+ #
4
+ # PreCompact hooks do NOT fire on auto-compaction (only on manual /compact).
5
+ # This PostToolUse hook monitors for sudden context size drops that indicate
6
+ # auto-compaction occurred without PreCompact firing.
7
+ #
8
+ # Born from: https://github.com/anthropics/claude-code/issues/50467
9
+ # Related: https://github.com/anthropics/claude-code/issues/50492 (24% early fire)
10
+ #
11
+ # TRIGGER: PostToolUse MATCHER: ""
12
+ # Runs after every tool use to track context size changes.
13
+
14
+ INPUT=$(cat)
15
+
16
+ # Track context tokens (approximate via tool input size)
17
+ MONITOR_FILE="/tmp/cc-context-monitor-$$"
18
+ CURRENT_SIZE=$(echo "$INPUT" | wc -c)
19
+
20
+ if [ -f "$MONITOR_FILE" ]; then
21
+ PREV_SIZE=$(cat "$MONITOR_FILE")
22
+ # If current input is significantly smaller than previous (>50% drop),
23
+ # auto-compaction likely occurred
24
+ if [ "$PREV_SIZE" -gt 1000 ] && [ "$CURRENT_SIZE" -gt 0 ]; then
25
+ RATIO=$((CURRENT_SIZE * 100 / PREV_SIZE))
26
+ if [ "$RATIO" -lt 30 ]; then
27
+ echo "⚠ AUTO-COMPACTION DETECTED: Context dropped ${RATIO}% (${PREV_SIZE}→${CURRENT_SIZE} bytes)" >&2
28
+ echo " PreCompact hooks did NOT fire for this compaction (#50467)" >&2
29
+ echo " Important context may have been lost. Verify key facts." >&2
30
+ fi
31
+ fi
32
+ fi
33
+
34
+ echo "$CURRENT_SIZE" > "$MONITOR_FILE"
35
+ exit 0
@@ -0,0 +1,57 @@
1
+ #!/bin/bash
2
+ # auto-mode-safety-enforcer.sh — Block dangerous operations in auto/acceptEdits mode
3
+ #
4
+ # Solves: Auto mode safety classifier hardcoded to opus-4-6, fails with Opus 4.7
5
+ # - #49618: Safety classifier doesn't work with non-opus-4-6 models
6
+ # - #49554: auto mode approved ~/.ssh deletion
7
+ # - #18740: Auto-allow mode data loss without warning
8
+ #
9
+ # How it works: PreToolUse hook on Bash that blocks destructive commands
10
+ # regardless of which model or permission mode is active. Acts as a
11
+ # user-space safety net when the built-in classifier fails.
12
+ #
13
+ # What it blocks:
14
+ # - rm -rf on non-safe paths (/, ~, .., /home, /etc, /usr, /var, .git)
15
+ # - Credential file deletion (.ssh, .git-credentials, .env, .npmrc)
16
+ # - dd/mkfs/fdisk (disk operations)
17
+ # - kill -9 on system processes
18
+ # - chmod 777 on sensitive paths
19
+ #
20
+ # TRIGGER: PreToolUse MATCHER: "Bash"
21
+
22
+ set -euo pipefail
23
+
24
+ INPUT=$(cat)
25
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
26
+ [ -z "$COMMAND" ] && exit 0
27
+
28
+ # --- Critical rm operations ---
29
+ if echo "$COMMAND" | grep -qE '(^|\s|;|&&|\|)(sudo\s+)?rm\s'; then
30
+ # Always block rm on root-level and home-level critical paths
31
+ if echo "$COMMAND" | grep -qE 'rm\s.*(/\s|/;|/$|~\/?\s|~\/?$|~\/\.|/home\b|/etc\b|/usr\b|/var\b|/opt\b|/root\b)'; then
32
+ echo "BLOCKED: rm targeting critical system/home path" >&2
33
+ echo "This operation would cause irreversible data loss." >&2
34
+ echo "Command: $COMMAND" >&2
35
+ exit 2
36
+ fi
37
+ # Block rm on dotfiles in home directory
38
+ if echo "$COMMAND" | grep -qE "rm\s.*(${HOME}|\~)/\."; then
39
+ echo "BLOCKED: rm targeting home dotfile" >&2
40
+ echo "Command: $COMMAND" >&2
41
+ exit 2
42
+ fi
43
+ fi
44
+
45
+ # --- Disk-level operations ---
46
+ if echo "$COMMAND" | grep -qE '(^|\s)(sudo\s+)?(dd\s+.*of=/dev|mkfs\.|fdisk\s|parted\s)'; then
47
+ echo "BLOCKED: Disk-level operation (dd/mkfs/fdisk/parted)" >&2
48
+ exit 2
49
+ fi
50
+
51
+ # --- Kill system processes ---
52
+ if echo "$COMMAND" | grep -qE 'kill\s+(-9\s+)?1$|killall\s+(init|systemd)'; then
53
+ echo "BLOCKED: Killing system process" >&2
54
+ exit 2
55
+ fi
56
+
57
+ exit 0
@@ -0,0 +1,57 @@
1
+ #!/bin/bash
2
+ # background-task-guard.sh — Audit background Bash execution
3
+ #
4
+ # Solves: run_in_background:true on Bash tool skips the approval
5
+ # prompt, allowing dangerous commands to execute without user
6
+ # confirmation. (#46950)
7
+ #
8
+ # How it works: Checks if a Bash command is running in background
9
+ # mode. If the command matches dangerous patterns (destructive ops,
10
+ # network access, file deletion), blocks it. Background execution
11
+ # should only be used for safe, read-only operations.
12
+ #
13
+ # TRIGGER: PreToolUse
14
+ # MATCHER: "Bash"
15
+
16
+ set -euo pipefail
17
+
18
+ INPUT=$(cat)
19
+ CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
20
+ [ -z "$CMD" ] && exit 0
21
+
22
+ # Check if this is a background execution
23
+ # Note: run_in_background is in tool_input for Bash
24
+ IS_BG=$(echo "$INPUT" | jq -r '.tool_input.run_in_background // false' 2>/dev/null)
25
+ [ "$IS_BG" != "true" ] && exit 0
26
+
27
+ # Background execution detected — apply strict safety rules
28
+ # Only allow read-only commands in background
29
+
30
+ # Block destructive operations
31
+ if echo "$CMD" | grep -qiE '\brm\s+-rf\b|\bgit\s+(push|reset|clean|checkout\s+--)\b|\bchmod\b|\bchown\b'; then
32
+ echo "BLOCKED: Destructive command not allowed in background mode." >&2
33
+ echo " Background tasks skip approval prompts — run this in foreground." >&2
34
+ exit 2
35
+ fi
36
+
37
+ # Block network writes
38
+ if echo "$CMD" | grep -qiE 'curl\s+.*-X\s*(POST|PUT|PATCH|DELETE)|curl\s+.*--data|curl\s+.*-d\s|wget\s+.*--post'; then
39
+ echo "BLOCKED: Network write operation not allowed in background mode." >&2
40
+ echo " Background tasks skip approval prompts — run this in foreground." >&2
41
+ exit 2
42
+ fi
43
+
44
+ # Block file writes to sensitive locations
45
+ if echo "$CMD" | grep -qiE '>\s*(/etc/|/usr/|/var/|~/.ssh/|~/.gnupg/|~/.claude/settings)'; then
46
+ echo "BLOCKED: Write to sensitive path not allowed in background mode." >&2
47
+ echo " Background tasks skip approval prompts — run this in foreground." >&2
48
+ exit 2
49
+ fi
50
+
51
+ # Block process killing
52
+ if echo "$CMD" | grep -qiE '\bkill\b|\bkillall\b|\bpkill\b'; then
53
+ echo "BLOCKED: Process termination not allowed in background mode." >&2
54
+ exit 2
55
+ fi
56
+
57
+ exit 0
@@ -51,7 +51,7 @@ SAFE_COMMANDS="git|npm|npx|bun|yarn|pnpm|docker|make|cargo|go|pip|python3|node|t
51
51
  BASE_CMD=$(echo "$COMMAND" | tr '\n' ' ' | sed 's/^[[:space:]]*//' | sed 's/^[A-Z_]*=[^ ]* //' | sed 's/^cd [^&;]* *[&;]* *//' | awk '{print $1}' | sed 's|.*/||')
52
52
 
53
53
  if echo "$BASE_CMD" | grep -qE "^($SAFE_COMMANDS)$"; then
54
- echo '{"permissionDecision":"allow"}'
54
+ jq -n '{"hookSpecificOutput":{"hookEventName":"PermissionRequest","decision":{"behavior":"allow"}}}'
55
55
  exit 0
56
56
  fi
57
57
 
@@ -0,0 +1,62 @@
1
+ #!/bin/bash
2
+ # broad-find-guard.sh — Block overly broad find/locate commands that scan the entire home directory
3
+ #
4
+ # Solves: Claude Code running `find $HOME` or `find /` which scans all user files,
5
+ # triggering OneDrive/iCloud/Dropbox to download synced files (#51010)
6
+ #
7
+ # Detects patterns like:
8
+ # find / -name "CLAUDE.md"
9
+ # find ~ -name "*.py"
10
+ # find $HOME -type f
11
+ # find /c/Users/username -name "*.md"
12
+ # locate "CLAUDE.md"
13
+ #
14
+ # Why: On Windows/macOS, cloud sync folders (OneDrive, iCloud, Dropbox) are
15
+ # under the home directory. A broad `find` traverses them and triggers
16
+ # downloading of ALL synced files — potentially gigabytes of data.
17
+ # Claude Code should only search project-scoped directories.
18
+ #
19
+ # Usage: Add to settings.json as a PreToolUse hook
20
+ #
21
+ # {
22
+ # "hooks": {
23
+ # "PreToolUse": [{
24
+ # "matcher": "Bash",
25
+ # "hooks": [{ "type": "command", "command": "~/.claude/hooks/broad-find-guard.sh" }]
26
+ # }]
27
+ # }
28
+ # }
29
+ #
30
+ # TRIGGER: PreToolUse MATCHER: "Bash"
31
+
32
+ INPUT=$(cat)
33
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
34
+
35
+ [ -z "$COMMAND" ] && exit 0
36
+
37
+ # Pattern 1: find starting from root
38
+ if echo "$COMMAND" | grep -qE 'find\s+/\s'; then
39
+ echo "BLOCKED: find from root (/) scans entire filesystem. Use a project-scoped path instead." >&2
40
+ exit 2
41
+ fi
42
+
43
+ # Pattern 2: find starting from home directory (various forms)
44
+ if echo "$COMMAND" | grep -qE 'find\s+(~|\$HOME|/home/[a-zA-Z0-9_]+|/Users/[a-zA-Z0-9_]+|/c/Users/[a-zA-Z0-9_]+)\s'; then
45
+ echo "BLOCKED: find from home directory scans all user files including cloud sync folders (OneDrive/iCloud/Dropbox). Use a specific project path instead." >&2
46
+ exit 2
47
+ fi
48
+
49
+ # Pattern 3: find with -maxdepth 0 or 1 from home is OK (shallow), but deep scans are not
50
+ # This pattern catches find ~ without further path restriction
51
+ if echo "$COMMAND" | grep -qE 'find\s+(~|\$HOME|/home/|/Users/|/c/Users/)\b' && ! echo "$COMMAND" | grep -qE '\-maxdepth\s+[01]'; then
52
+ echo "BLOCKED: broad find from home directory. Add -maxdepth or use a specific subdirectory to avoid scanning cloud sync folders." >&2
53
+ exit 2
54
+ fi
55
+
56
+ # Pattern 4: locate without project-scoping (scans entire DB)
57
+ if echo "$COMMAND" | grep -qE '^\s*locate\s' && ! echo "$COMMAND" | grep -qE 'locate.*--database|locate.*-d\s'; then
58
+ echo "BLOCKED: locate searches the entire filesystem index. Use find with a specific directory instead." >&2
59
+ exit 2
60
+ fi
61
+
62
+ exit 0
@@ -0,0 +1,32 @@
1
+ #!/bin/bash
2
+ # cache-creation-spike-detector.sh — cache_creationスパイクを検知して警告
3
+ # Why: smooshSystemReminderSiblings関数がsystem-reminderを毎ターンtool_result.contentに
4
+ # 折り込むことで、プロンプトキャッシュのプレフィックスが変わり、cache_creationが
5
+ # 数十万トークン単位でスパイクする (#49585)。5xの消費率上昇報告あり (#49593)
6
+ # Event: PostToolUse (全ツール実行後にチェック)
7
+ # Action: cache_creation_input_tokensが閾値を超えた場合に警告
8
+
9
+ INPUT=$(cat)
10
+ # PostToolUseではusageデータにアクセスできないため、
11
+ # /costコマンド出力のログファイルで累積cache_creationを追跡する
12
+ CACHE_LOG="/tmp/cc-cache-creation-tracker.log"
13
+ THRESHOLD=100000 # 100Kトークン以上でcache_creation警告
14
+
15
+ # 10回に1回だけチェック(パフォーマンス配慮)
16
+ COUNTER_FILE="/tmp/cache-spike-check-counter"
17
+ COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo "0")
18
+ COUNT=$((COUNT + 1))
19
+ echo "$COUNT" > "$COUNTER_FILE"
20
+ [ $((COUNT % 10)) -ne 0 ] && exit 0
21
+
22
+ # セッション開始からの経過時間をチェック
23
+ SESSION_START=$(stat -c %Y /tmp/cache-spike-check-counter 2>/dev/null || echo "0")
24
+ NOW=$(date +%s)
25
+ ELAPSED=$((NOW - SESSION_START))
26
+
27
+ # セッション開始10分以内はスキップ(初期キャッシュ構築は正常)
28
+ [ "$ELAPSED" -lt 600 ] && exit 0
29
+
30
+ echo "INFO: cache_creationスパイク検知hookが動作中。異常なトークン消費を感じたら /cost で確認してください。" >&2
31
+ echo "詳細: https://github.com/anthropics/claude-code/issues/49585" >&2
32
+ exit 0
@@ -0,0 +1,96 @@
1
+ #!/bin/bash
2
+ # case-insensitive-path-guard.sh — Detect path case mismatches on case-insensitive filesystems
3
+ #
4
+ # Solves: Claude Code resolving paths with wrong case on macOS APFS (case-insensitive),
5
+ # causing rm -rf to destroy unintended directories
6
+ # - #48792: rm -rf WebstormProjects/ destroyed webstormprojects/ (10 years of work)
7
+ # - #49102: Same APFS bug, second catastrophic loss in 48 hours
8
+ #
9
+ # How it works:
10
+ # For rm/mv/cp commands targeting paths under $HOME, resolves the ACTUAL
11
+ # filesystem path and compares case. If the specified path exists only via
12
+ # case-insensitive matching (e.g., "Projects" matches "projects/"), blocks
13
+ # the command because the user likely intended a different directory.
14
+ #
15
+ # Platform: macOS only (APFS case-insensitive is default). On Linux (ext4, case-sensitive),
16
+ # the guard exits cleanly since case mismatches simply won't resolve.
17
+ #
18
+ # TRIGGER: PreToolUse MATCHER: "Bash"
19
+
20
+ set -euo pipefail
21
+
22
+ INPUT=$(cat)
23
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
24
+ [ -z "$COMMAND" ] && exit 0
25
+
26
+ # Only check destructive commands
27
+ echo "$COMMAND" | grep -qE '\b(rm|mv|cp)\s' || exit 0
28
+
29
+ # Skip if not macOS (Linux filesystems are case-sensitive by default)
30
+ if [ "$(uname)" != "Darwin" ]; then
31
+ exit 0
32
+ fi
33
+
34
+ # Extract target paths from destructive commands
35
+ # Handles: rm -rf path, mv path dest, cp -r path dest
36
+ TARGETS=""
37
+
38
+ # rm: all non-flag arguments
39
+ if echo "$COMMAND" | grep -qE '\brm\s'; then
40
+ TARGETS=$(echo "$COMMAND" | grep -oP '\brm\s+(-[a-zA-Z]+\s+)*\K[^\s;&|]+(\s+[^\s;&|]+)*' 2>/dev/null || true)
41
+ fi
42
+
43
+ # mv: first non-flag argument (source)
44
+ if echo "$COMMAND" | grep -qE '\bmv\s'; then
45
+ MV_TARGET=$(echo "$COMMAND" | grep -oP '\bmv\s+(-[a-zA-Z]+\s+)*\K\S+' 2>/dev/null || true)
46
+ TARGETS="$TARGETS $MV_TARGET"
47
+ fi
48
+
49
+ [ -z "$TARGETS" ] && exit 0
50
+
51
+ # Expand ~ to $HOME
52
+ TARGETS=$(echo "$TARGETS" | sed "s|~|$HOME|g")
53
+
54
+ for target in $TARGETS; do
55
+ # Skip flags
56
+ echo "$target" | grep -q '^-' && continue
57
+
58
+ # Skip safe disposable paths
59
+ echo "$target" | grep -qE '(node_modules|\.cache|__pycache__|/tmp/|dist/|build/)' && continue
60
+
61
+ # Only check paths under home directory (most risk)
62
+ echo "$target" | grep -qE "^($HOME|~)" || continue
63
+
64
+ # Resolve the canonical path using the filesystem
65
+ # On case-insensitive APFS, /Users/me/Projects resolves even if actual is /Users/me/projects
66
+ CANONICAL=$(python3 -c "
67
+ import os, sys
68
+ p = os.path.expanduser('$target')
69
+ # Walk up to find the longest existing prefix
70
+ check = p
71
+ while check and not os.path.exists(check):
72
+ check = os.path.dirname(check)
73
+ if not check:
74
+ sys.exit(0)
75
+ # Get the real path (canonical case)
76
+ real = os.path.realpath(check)
77
+ # Get the suffix that was beyond the existing part
78
+ suffix = p[len(check):]
79
+ print(real + suffix)
80
+ " 2>/dev/null || echo "")
81
+
82
+ [ -z "$CANONICAL" ] && continue
83
+
84
+ # Compare: if the specified path differs in case from the canonical path
85
+ if [ "$target" != "$CANONICAL" ] && [ "${target,,}" = "${CANONICAL,,}" ] 2>/dev/null; then
86
+ echo "BLOCKED: Case mismatch detected on case-insensitive filesystem" >&2
87
+ echo " Specified: $target" >&2
88
+ echo " Actual: $CANONICAL" >&2
89
+ echo " On macOS APFS, these resolve to the SAME directory." >&2
90
+ echo " This mismatch has caused catastrophic data loss (#48792, #49102)." >&2
91
+ echo " Verify the path case matches the actual filesystem before proceeding." >&2
92
+ exit 2
93
+ fi
94
+ done
95
+
96
+ exit 0
@@ -0,0 +1,44 @@
1
+ #!/bin/bash
2
+ # cjk-punctuation-guard.sh — Detect CJK punctuation corruption after Write/Edit
3
+ #
4
+ # Solves: Claude Code's Write and Edit tools silently convert full-width CJK
5
+ # punctuation to half-width ASCII (,→, 。→. 「→" etc.) without warning.
6
+ # This corrupts Chinese, Japanese, and Korean text.
7
+ # (GitHub Issue #50975)
8
+ #
9
+ # How it works:
10
+ # After Write or Edit, checks if the file contains CJK characters.
11
+ # If yes, runs git diff to see if any full-width punctuation was replaced
12
+ # with half-width equivalents in the same commit.
13
+ #
14
+ # TRIGGER: PostToolUse MATCHER: "Write|Edit"
15
+
16
+ INPUT=$(cat)
17
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.file // empty' 2>/dev/null)
18
+
19
+ [ -z "$FILE" ] || [ ! -f "$FILE" ] && exit 0
20
+
21
+ # Only check files that contain CJK characters
22
+ if ! grep -Pq '[\p{Han}\p{Hiragana}\p{Katakana}\p{Hangul}]' "$FILE" 2>/dev/null; then
23
+ exit 0
24
+ fi
25
+
26
+ # Check git diff for punctuation changes (full-width → half-width)
27
+ DIFF=$(git diff -- "$FILE" 2>/dev/null)
28
+ [ -z "$DIFF" ] && exit 0
29
+
30
+ # Detect suspicious patterns: removed full-width, added half-width in same hunk
31
+ # Common corruptions: ,→, 。→. 、→, :→: ;→; !→! ?→? 「→" 」→" (→( )→)
32
+ REMOVED_FW=$(echo "$DIFF" | grep '^-' | grep -Pc '[,。、:;!?「」()【】『』]' 2>/dev/null || echo 0)
33
+ ADDED_HW=$(echo "$DIFF" | grep '^+' | grep -Pc '[,\.;:!\?\(\)\[\]]' 2>/dev/null || echo 0)
34
+
35
+ if [ "$REMOVED_FW" -gt 0 ] && [ "$ADDED_HW" -gt 0 ]; then
36
+ echo "⚠ WARNING: CJK punctuation may have been corrupted in $FILE" >&2
37
+ echo " $REMOVED_FW lines with full-width punctuation removed" >&2
38
+ echo " $ADDED_HW lines with half-width punctuation added" >&2
39
+ echo " Review: git diff -- \"$FILE\"" >&2
40
+ echo " Undo: git checkout -- \"$FILE\"" >&2
41
+ # Warning only (exit 0), not blocking — the write already happened
42
+ fi
43
+
44
+ exit 0
@@ -0,0 +1,29 @@
1
+ #!/bin/bash
2
+ # clipboard-secret-guard.sh — Block secrets from being copied to clipboard
3
+ #
4
+ # Solves: Claude Code may pipe sensitive data (API keys, tokens, passwords)
5
+ # to clipboard utilities (pbcopy, xclip, xsel, wl-copy). This leaks
6
+ # secrets outside the terminal where they persist and may sync to cloud.
7
+ #
8
+ # How it works: PreToolUse hook that detects clipboard commands containing
9
+ # secret-like patterns. Blocks the operation.
10
+ #
11
+ # TRIGGER: PreToolUse
12
+ # MATCHER: "Bash"
13
+ # CATEGORY: security
14
+
15
+ INPUT=$(cat)
16
+ CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
17
+ [ -z "$CMD" ] && exit 0
18
+
19
+ # Check if command pipes to clipboard
20
+ if echo "$CMD" | grep -qiE '(pbcopy|xclip|xsel|wl-copy|clip\.exe)'; then
21
+ # Check if the piped content looks like it contains secrets
22
+ if echo "$CMD" | grep -qiE '(api.?key|secret|token|password|passwd|credential|private.?key|Bearer|AWS_|ANTHROPIC_|OPENAI_)'; then
23
+ echo "BLOCKED: Attempting to copy secret-like content to clipboard." >&2
24
+ echo " Clipboard data may sync to cloud or persist after session." >&2
25
+ exit 2
26
+ fi
27
+ fi
28
+
29
+ exit 0
@@ -0,0 +1,38 @@
1
+ #!/bin/bash
2
+ # context-size-alert.sh — Warn when context window usage exceeds threshold
3
+ # Trigger: PostToolUse
4
+ # Matcher: (empty — runs after every tool use)
5
+ #
6
+ # Since v2.1.100, the system prompt grew ~40-50% (~4,000 tokens).
7
+ # This hook monitors context usage and warns before you hit limits.
8
+ # See: https://github.com/anthropics/claude-code/issues/46339
9
+ #
10
+ # Optimization tips: https://zenn.dev/yurukusa/books/token-savings-guide
11
+ #
12
+ # TRIGGER: PostToolUse MATCHER: ""
13
+
14
+ INPUT=$(cat)
15
+ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
16
+
17
+ # Only check every 10th invocation to minimize overhead
18
+ COUNTER_FILE="/tmp/context-size-alert-${SESSION_ID:-default}.count"
19
+ COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo 0)
20
+ COUNT=$((COUNT + 1))
21
+ echo "$COUNT" > "$COUNTER_FILE"
22
+ [ $((COUNT % 10)) -ne 0 ] && exit 0
23
+
24
+ # Check if /cost or /context data is available via transcript
25
+ TRANSCRIPT=$(echo "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)
26
+ [ -z "$TRANSCRIPT" ] && exit 0
27
+
28
+ # Count approximate context by transcript file size (rough proxy)
29
+ if [ -f "$TRANSCRIPT" ]; then
30
+ SIZE_KB=$(du -k "$TRANSCRIPT" | cut -f1)
31
+ # Rough threshold: 500KB transcript ≈ approaching context limits
32
+ if [ "$SIZE_KB" -gt 500 ]; then
33
+ echo "⚠ Context is large (~${SIZE_KB}KB transcript). Consider /compact or /clear to reduce token cost." >&2
34
+ echo " Tip: Run /context to see exact usage breakdown." >&2
35
+ fi
36
+ fi
37
+
38
+ exit 0
@@ -0,0 +1,33 @@
1
+ #!/bin/bash
2
+ # context-usage-drift-alert.sh — コンテキスト使用率の急増を検知
3
+ # Why: 1Mコンテキストモデルで実際124%使用中にUI上60%と表示される問題 (#50204)。
4
+ # 予告なくauto-compactが発火してコンテキストが消失する。
5
+ # ツール呼び出し回数でコンテキスト消費を推定し、警告する。
6
+ # Event: PostToolUse MATCHER: ""
7
+ # Action: セッション内のツール呼び出し回数が閾値を超えたら警告
8
+
9
+ COUNTER_FILE="/tmp/cc-context-usage-counter-$$"
10
+ # セッションPIDが変わると新しいカウンターになる
11
+ # フォールバック: 親PIDでグループ化
12
+ if [ ! -f "$COUNTER_FILE" ]; then
13
+ COUNTER_FILE="/tmp/cc-context-usage-counter-$(date +%Y%m%d)"
14
+ fi
15
+
16
+ COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo "0")
17
+ COUNT=$((COUNT + 1))
18
+ echo "$COUNT" > "$COUNTER_FILE"
19
+
20
+ # 50回: 注意喚起、100回: 強い警告、150回: compact推奨
21
+ if [ "$COUNT" -eq 50 ]; then
22
+ echo "📊 Session checkpoint: $COUNT tool calls. Context may be growing large." >&2
23
+ echo "Run /cost to check actual usage. UI display may undercount by 2x (#50204)." >&2
24
+ elif [ "$COUNT" -eq 100 ]; then
25
+ echo "⚠ HIGH CONTEXT USAGE: $COUNT tool calls this session." >&2
26
+ echo "UI may show ~50% when actual usage is near 100%. Consider /compact." >&2
27
+ echo "Unexpected auto-compact can erase your working context. See: #50204" >&2
28
+ elif [ "$COUNT" -eq 150 ]; then
29
+ echo "🚨 VERY HIGH CONTEXT: $COUNT tool calls. Auto-compact likely imminent." >&2
30
+ echo "Save important state to files NOW. Run /compact manually to control what's kept." >&2
31
+ fi
32
+
33
+ exit 0
@@ -0,0 +1,51 @@
1
+ #!/bin/bash
2
+ # dangerous-pip-flag-guard.sh — Block dangerous pip flags that bypass safety
3
+ #
4
+ # Solves: Claude Code using pip install with dangerous flags in auto mode
5
+ # - #48992: pip install --break-system-packages passed through auto mode
6
+ # - Breaks system Python installations, can render OS tools unusable
7
+ #
8
+ # What it blocks:
9
+ # --break-system-packages (bypasses PEP 668 externally-managed check)
10
+ # --force-reinstall combined with system paths
11
+ # pip install targeting /usr/lib or /usr/local/lib directly
12
+ #
13
+ # What it allows:
14
+ # pip install in virtual environments (venv/conda)
15
+ # pip install --user (installs to user directory)
16
+ # pip install in project directories
17
+ #
18
+ # TRIGGER: PreToolUse MATCHER: "Bash"
19
+
20
+ set -euo pipefail
21
+
22
+ INPUT=$(cat)
23
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
24
+ [ -z "$COMMAND" ] && exit 0
25
+
26
+ # Only check pip commands
27
+ echo "$COMMAND" | grep -qE '\bpip[3]?\s+install\b' || exit 0
28
+
29
+ # Block --break-system-packages
30
+ if echo "$COMMAND" | grep -qE '\-\-break-system-packages'; then
31
+ echo "BLOCKED: --break-system-packages flag detected" >&2
32
+ echo "This flag bypasses PEP 668 protection and can break your system Python." >&2
33
+ echo "Use a virtual environment instead: python3 -m venv .venv && source .venv/bin/activate" >&2
34
+ exit 2
35
+ fi
36
+
37
+ # Block sudo pip install (system-wide install without venv)
38
+ if echo "$COMMAND" | grep -qE '\bsudo\s+pip[3]?\s+install\b'; then
39
+ echo "BLOCKED: sudo pip install detected" >&2
40
+ echo "System-wide pip install can break OS tools. Use a virtual environment." >&2
41
+ exit 2
42
+ fi
43
+
44
+ # Block pip targeting system directories
45
+ if echo "$COMMAND" | grep -qE '\bpip[3]?\s+install\b.*\-\-target\s*=?\s*/(usr|opt|lib)'; then
46
+ echo "BLOCKED: pip install targeting system directory" >&2
47
+ echo "Installing packages to system directories can break OS tools." >&2
48
+ exit 2
49
+ fi
50
+
51
+ exit 0
@@ -0,0 +1,59 @@
1
+ #!/bin/bash
2
+ # decision-warn.sh — Warn and log irreversible operations
3
+ #
4
+ # Solves: "Why did Claude push to production?" — no decision trail for critical actions
5
+ # Detects dangerous operations (git push, rm -rf, database commands) and logs them
6
+ # to a decision log for post-incident analysis.
7
+ #
8
+ # Usage: Add to settings.json as a PostToolUse hook
9
+ #
10
+ # {
11
+ # "hooks": {
12
+ # "PostToolUse": [{
13
+ # "matcher": "Bash",
14
+ # "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/decision-warn.sh" }]
15
+ # }]
16
+ # }
17
+ # }
18
+ #
19
+ # Output: ~/.claude/decision-log.jsonl
20
+ # Each entry logs the command, timestamp, and detected risk category.
21
+
22
+ set -u
23
+
24
+ INPUT=$(cat)
25
+ TOOL=$(printf '%s' "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
26
+ [ "$TOOL" = "Bash" ] || exit 0
27
+
28
+ CMD=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
29
+ [ -z "$CMD" ] && exit 0
30
+
31
+ LOG_FILE="${HOME}/.claude/decision-log.jsonl"
32
+ TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
33
+
34
+ RISK=""
35
+
36
+ # Detect irreversible operations
37
+ if echo "$CMD" | grep -qE 'git\s+push'; then
38
+ RISK="git-push"
39
+ elif echo "$CMD" | grep -qE 'git\s+reset\s+--hard'; then
40
+ RISK="git-reset-hard"
41
+ elif echo "$CMD" | grep -qE 'rm\s+(-rf|--recursive)'; then
42
+ RISK="destructive-delete"
43
+ elif echo "$CMD" | grep -qiE '(DROP\s+(TABLE|DATABASE)|TRUNCATE|DELETE\s+FROM)'; then
44
+ RISK="database-destructive"
45
+ elif echo "$CMD" | grep -qE 'npm\s+publish'; then
46
+ RISK="npm-publish"
47
+ elif echo "$CMD" | grep -qE 'curl\s+.*-X\s*(DELETE|PUT|POST)'; then
48
+ RISK="api-mutation"
49
+ fi
50
+
51
+ [ -z "$RISK" ] && exit 0
52
+
53
+ printf '{"ts":"%s","risk":"%s","cmd":"%s"}\n' \
54
+ "$TS" "$RISK" "$(echo "$CMD" | head -c 200 | tr '"' "'")" >> "$LOG_FILE"
55
+
56
+ echo "[DECISION] $RISK: $(echo "$CMD" | head -c 100)" >&2
57
+ echo " → Logged to: $LOG_FILE" >&2
58
+
59
+ exit 0