cc-safe-setup 29.6.40 → 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 (79) hide show
  1. package/.claude-plugin/marketplace.json +66 -0
  2. package/.claude-plugin/plugin.json +11 -0
  3. package/README.md +123 -12
  4. package/SETTINGS_REFERENCE.md +2 -0
  5. package/SKILL.md +47 -0
  6. package/examples/README.md +11 -1
  7. package/examples/auto-approve-compound-git.sh +3 -0
  8. package/examples/auto-compact-context-monitor.sh +35 -0
  9. package/examples/auto-mode-safety-enforcer.sh +57 -0
  10. package/examples/background-task-guard.sh +57 -0
  11. package/examples/broad-find-guard.sh +62 -0
  12. package/examples/cache-creation-spike-detector.sh +32 -0
  13. package/examples/case-insensitive-path-guard.sh +96 -0
  14. package/examples/cjk-punctuation-guard.sh +44 -0
  15. package/examples/clipboard-secret-guard.sh +29 -0
  16. package/examples/context-size-alert.sh +38 -0
  17. package/examples/context-usage-drift-alert.sh +33 -0
  18. package/examples/dangerous-pip-flag-guard.sh +51 -0
  19. package/examples/deny-bypass-detector.sh +143 -0
  20. package/examples/dotenv-read-guard.sh +48 -0
  21. package/examples/dotfile-protection-guard.sh +60 -0
  22. package/examples/effort-tracking-logger.sh +30 -0
  23. package/examples/financial-operation-guard.sh +47 -0
  24. package/examples/full-rewrite-detector.sh +63 -0
  25. package/examples/home-critical-bash-guard.sh +56 -0
  26. package/examples/idle-session-cost-alert.sh +36 -0
  27. package/examples/model-version-alert.sh +18 -0
  28. package/examples/model-version-change-alert.sh +31 -0
  29. package/examples/move-delete-sequence-guard.sh +92 -0
  30. package/examples/pii-upload-guard.sh +72 -0
  31. package/examples/pr-duplicate-guard.sh +14 -0
  32. package/examples/production-port-kill-guard.sh +60 -0
  33. package/examples/quota-reset-cycle-monitor.sh +30 -0
  34. package/examples/repo-visibility-guard.sh +33 -0
  35. package/examples/sandbox-relative-path-audit.sh +51 -0
  36. package/examples/session-agent-cost-limiter.sh +43 -0
  37. package/examples/session-cost-alert.sh +62 -0
  38. package/examples/session-memory-watchdog.sh +9 -0
  39. package/examples/settings-integrity-monitor.sh +55 -0
  40. package/examples/settings-json-model-guard.sh +89 -0
  41. package/examples/shell-config-truncation-guard.sh +97 -0
  42. package/examples/shell-wrapper-guard.sh +4 -4
  43. package/examples/subagent-spawn-rate-monitor.sh +34 -0
  44. package/examples/subcommand-chain-guard.sh +44 -0
  45. package/examples/system-dir-protection-guard.sh +100 -0
  46. package/examples/thinking-display-enforcer.sh +25 -0
  47. package/examples/tool-retry-budget-guard.sh +59 -0
  48. package/examples/worktree-branch-pollution-detector.sh +35 -0
  49. package/examples/worktree-create-log.sh +6 -0
  50. package/examples/worktree-hook-linker.sh +72 -0
  51. package/examples/worktree-remove-uncommitted-guard.sh +20 -0
  52. package/hooks/hooks.json +60 -0
  53. package/index.mjs +92 -6
  54. package/memory/market-anthropic-japan-strategy-2026-04-13.md +4 -0
  55. package/package.json +2 -2
  56. package/plugins/credential-guard/.claude-plugin/plugin.json +58 -0
  57. package/plugins/git-protection/.claude-plugin/plugin.json +58 -0
  58. package/plugins/safety-essentials/.claude-plugin/plugin.json +58 -0
  59. package/plugins/token-guard/.claude-plugin/plugin.json +51 -0
  60. package/skills/safety-setup/SKILL.md +47 -0
  61. package/tests/dotenv-read-guard.test.sh +65 -0
  62. package/tests/test-auto-mode-safety-enforcer.sh +55 -0
  63. package/tests/test-case-insensitive-path-guard.sh +78 -0
  64. package/tests/test-context-usage-drift-alert.sh +52 -0
  65. package/tests/test-dangerous-pip-flag-guard.sh +56 -0
  66. package/tests/test-dotfile-protection-guard.sh +68 -0
  67. package/tests/test-effort-tracking-logger.sh +55 -0
  68. package/tests/test-financial-operation-guard.sh +59 -0
  69. package/tests/test-home-critical-bash-guard.sh +59 -0
  70. package/tests/test-model-version-change-alert.sh +55 -0
  71. package/tests/test-move-delete-sequence-guard.sh +63 -0
  72. package/tests/test-pr-duplicate-guard.sh +29 -0
  73. package/tests/test-quota-reset-cycle-monitor.sh +52 -0
  74. package/tests/test-shell-config-truncation-guard.sh +104 -0
  75. package/tests/test-subagent-spawn-rate-monitor.sh +43 -0
  76. package/tests/test-system-dir-protection-guard.sh +81 -0
  77. package/tests/test-tool-retry-budget-guard.sh +75 -0
  78. package/tests/test-worktree-branch-pollution-detector.sh +50 -0
  79. package/tests/test-worktree-lifecycle-hooks.sh +29 -0
@@ -0,0 +1,35 @@
1
+ #!/bin/bash
2
+ # worktree-branch-pollution-detector.sh — worktreeが親ブランチを汚染していないか検知
3
+ # Why: サブエージェントのworktree操作が親リポを予期しないブランチに移動させ、
4
+ # 意図しないcommit-to-mainが発生する。1週間で3回の事故報告あり (#50207)
5
+ # Event: PostToolUse MATCHER: Bash
6
+ # Action: 現在のブランチが期待値と異なる場合に警告
7
+
8
+ INPUT=$(cat)
9
+
10
+ # 期待ブランチ(セッション開始時に記録)
11
+ EXPECTED_BRANCH_FILE="/tmp/cc-expected-branch-$(pwd | md5sum | cut -c1-8)"
12
+
13
+ # git管理下でなければスキップ
14
+ git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
15
+
16
+ CURRENT_BRANCH=$(git branch --show-current 2>/dev/null)
17
+ [ -z "$CURRENT_BRANCH" ] && exit 0
18
+
19
+ # 初回実行時はブランチを記録
20
+ if [ ! -f "$EXPECTED_BRANCH_FILE" ]; then
21
+ echo "$CURRENT_BRANCH" > "$EXPECTED_BRANCH_FILE"
22
+ exit 0
23
+ fi
24
+
25
+ EXPECTED_BRANCH=$(cat "$EXPECTED_BRANCH_FILE" 2>/dev/null)
26
+
27
+ if [ "$CURRENT_BRANCH" != "$EXPECTED_BRANCH" ]; then
28
+ echo "⚠ BRANCH CHANGED: Expected '$EXPECTED_BRANCH' but now on '$CURRENT_BRANCH'" >&2
29
+ echo "This may be caused by a worktree or subagent switching your branch." >&2
30
+ echo "Run 'git checkout $EXPECTED_BRANCH' to return. See: #50207" >&2
31
+ # 新しいブランチを記録(意図的な切替かもしれない)
32
+ echo "$CURRENT_BRANCH" > "$EXPECTED_BRANCH_FILE"
33
+ fi
34
+
35
+ exit 0
@@ -0,0 +1,6 @@
1
+ LOGFILE="${HOME}/.claude/worktree-audit.log"
2
+ INFO=$(cat)
3
+ BRANCH=$(echo "$INFO" | jq -r '.branch // "unknown"' 2>/dev/null)
4
+ PATH_WT=$(echo "$INFO" | jq -r '.path // "unknown"' 2>/dev/null)
5
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] CREATE branch=$BRANCH path=$PATH_WT" >> "$LOGFILE"
6
+ exit 0
@@ -0,0 +1,72 @@
1
+ #!/bin/bash
2
+ # worktree-hook-linker.sh — Auto-link settings to worktrees
3
+ #
4
+ # Solves: In git worktrees, .claude/settings.json is not found because
5
+ # worktrees share .git but not the working directory. All hooks
6
+ # become silently disabled. (#46808)
7
+ #
8
+ # How it works: On SessionStart, checks if the current directory is a
9
+ # git worktree. If so, creates a symlink from the worktree's
10
+ # .claude/settings.json to the main tree's settings. This ensures
11
+ # hooks work identically in worktrees.
12
+ #
13
+ # {
14
+ # "hooks": {
15
+ # "Notification": [{
16
+ # "matcher": "SessionStart",
17
+ # "hooks": [{ "type": "command", "command": "~/.claude/hooks/worktree-hook-linker.sh" }]
18
+ # }]
19
+ # }
20
+ # }
21
+ #
22
+ # TRIGGER: Notification
23
+ # MATCHER: "SessionStart"
24
+
25
+ # Detect if we're in a git worktree
26
+ GITDIR=$(git rev-parse --git-dir 2>/dev/null) || exit 0
27
+ echo "$GITDIR" | grep -q "worktrees" || exit 0
28
+
29
+ # We're in a worktree — find the main working tree
30
+ MAIN_GITDIR=$(git rev-parse --path-format=absolute --git-common-dir 2>/dev/null) || exit 0
31
+ MAIN_WORKDIR=$(echo "$MAIN_GITDIR" | sed 's|/.git$||')
32
+
33
+ MAIN_CLAUDE_DIR="$MAIN_WORKDIR/.claude"
34
+ LOCAL_CLAUDE_DIR=".claude"
35
+
36
+ # Skip if main tree has no .claude directory
37
+ [ -d "$MAIN_CLAUDE_DIR" ] || exit 0
38
+
39
+ # Create .claude directory in worktree if needed
40
+ mkdir -p "$LOCAL_CLAUDE_DIR" 2>/dev/null
41
+
42
+ # Link settings files if they exist in main but not in worktree
43
+ for f in settings.json settings.local.json; do
44
+ MAIN_FILE="$MAIN_CLAUDE_DIR/$f"
45
+ LOCAL_FILE="$LOCAL_CLAUDE_DIR/$f"
46
+
47
+ [ ! -f "$MAIN_FILE" ] && continue
48
+
49
+ if [ ! -e "$LOCAL_FILE" ]; then
50
+ ln -s "$MAIN_FILE" "$LOCAL_FILE"
51
+ echo "Linked $f from main tree → worktree (hooks now active)" >&2
52
+ elif [ -L "$LOCAL_FILE" ]; then
53
+ # Already a symlink — verify it points to the right place
54
+ TARGET=$(readlink -f "$LOCAL_FILE" 2>/dev/null)
55
+ EXPECTED=$(readlink -f "$MAIN_FILE" 2>/dev/null)
56
+ if [ "$TARGET" != "$EXPECTED" ]; then
57
+ rm "$LOCAL_FILE"
58
+ ln -s "$MAIN_FILE" "$LOCAL_FILE"
59
+ echo "Re-linked $f (was pointing to wrong location)" >&2
60
+ fi
61
+ fi
62
+ done
63
+
64
+ # Also link hooks directory if it exists
65
+ MAIN_HOOKS="$MAIN_CLAUDE_DIR/hooks"
66
+ LOCAL_HOOKS="$LOCAL_CLAUDE_DIR/hooks"
67
+ if [ -d "$MAIN_HOOKS" ] && [ ! -e "$LOCAL_HOOKS" ]; then
68
+ ln -s "$MAIN_HOOKS" "$LOCAL_HOOKS"
69
+ echo "Linked hooks/ directory from main tree → worktree" >&2
70
+ fi
71
+
72
+ exit 0
@@ -0,0 +1,20 @@
1
+ INFO=$(cat)
2
+ PATH_WT=$(echo "$INFO" | jq -r '.path // empty' 2>/dev/null)
3
+ [ -z "$PATH_WT" ] && exit 0
4
+ [ ! -d "$PATH_WT" ] && exit 0
5
+ cd "$PATH_WT" 2>/dev/null || exit 0
6
+ DIRTY=$(git status --porcelain 2>/dev/null | wc -l)
7
+ if [ "$DIRTY" -gt 0 ]; then
8
+ echo "BLOCKED: Worktree at $PATH_WT has $DIRTY uncommitted change(s)." >&2
9
+ echo "Commit or stash changes before removing." >&2
10
+ exit 2
11
+ fi
12
+ BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
13
+ if [ -n "$BRANCH" ]; then
14
+ UNPUSHED=$(git log --oneline "origin/$BRANCH..$BRANCH" 2>/dev/null | wc -l)
15
+ if [ "$UNPUSHED" -gt 0 ]; then
16
+ echo "WARNING: $UNPUSHED unpushed commit(s) on $BRANCH." >&2
17
+ echo "Push before removing: git push origin $BRANCH" >&2
18
+ fi
19
+ fi
20
+ exit 0
@@ -0,0 +1,60 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Bash",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty'); [ -z \"$CMD\" ] && exit 0; if echo \"$CMD\" | grep -qE '^\\s*(sudo\\s+)?rm\\s+.*-[rRf]*[rR]' && ! echo \"$CMD\" | grep -qE '(node_modules|dist|build|__pycache__|/tmp)'; then echo 'BLOCKED: recursive rm on non-safe target. Use specific paths.' >&2; exit 2; fi"
10
+ }
11
+ ]
12
+ },
13
+ {
14
+ "matcher": "Bash",
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty'); [ -z \"$CMD\" ] && exit 0; if echo \"$CMD\" | grep -qE 'git\\s+push\\s+.*--force|git\\s+reset\\s+--hard|git\\s+clean\\s+-fd'; then echo 'BLOCKED: destructive git operation. Use safer alternatives.' >&2; exit 2; fi"
19
+ }
20
+ ]
21
+ },
22
+ {
23
+ "matcher": "Bash",
24
+ "hooks": [
25
+ {
26
+ "type": "command",
27
+ "command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty'); [ -z \"$CMD\" ] && exit 0; if echo \"$CMD\" | grep -qiE '(api.key|secret|password|token).*=.*[A-Za-z0-9]{20}'; then echo 'BLOCKED: potential credential in command. Use environment variables.' >&2; exit 2; fi"
28
+ }
29
+ ]
30
+ },
31
+ {
32
+ "matcher": "Write|Edit",
33
+ "hooks": [
34
+ {
35
+ "type": "command",
36
+ "command": "INPUT=$(cat); FILE=$(echo \"$INPUT\" | jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; if echo \"$FILE\" | grep -qE '\\.(env|pem|key|credentials|secret)$'; then echo 'BLOCKED: writing to sensitive file. Check if this is intentional.' >&2; exit 2; fi"
37
+ }
38
+ ]
39
+ },
40
+ {
41
+ "matcher": "Bash",
42
+ "hooks": [
43
+ {
44
+ "type": "command",
45
+ "command": "~/.claude/hooks/move-delete-sequence-guard.sh"
46
+ }
47
+ ]
48
+ },
49
+ {
50
+ "matcher": "Bash",
51
+ "hooks": [
52
+ {
53
+ "type": "command",
54
+ "command": "~/.claude/hooks/system-dir-protection-guard.sh"
55
+ }
56
+ ]
57
+ }
58
+ ]
59
+ }
60
+ }
package/index.mjs CHANGED
@@ -94,6 +94,7 @@ const GENERATE_CI = process.argv.includes('--generate-ci');
94
94
  const REPORT = process.argv.includes('--report');
95
95
  const QUICKFIX = process.argv.includes('--quickfix');
96
96
  const SHIELD = process.argv.includes('--shield');
97
+ const OPUS47 = process.argv.includes('--opus47');
97
98
  const ANALYZE = process.argv.includes('--analyze');
98
99
  const TEAM = process.argv.includes('--team');
99
100
  const MIGRATE_FROM_IDX = process.argv.findIndex(a => a === '--migrate-from');
@@ -192,8 +193,9 @@ if (HELP) {
192
193
  Find hooks: npx cc-hook-registry search <keyword>
193
194
  Test hooks: npx cc-hook-test <hook.sh>
194
195
 
195
- Support: https://github.com/sponsors/yurukusa
196
- Book: https://zenn.dev/yurukusa/books/6076c23b1cb18b
196
+ Token Checkup: https://yurukusa.github.io/cc-safe-setup/token-checkup.html
197
+ Token Book: https://yurukusa.github.io/cc-safe-setup/token-book.html
198
+ Safety Guide: https://zenn.dev/yurukusa/books/6076c23b1cb18b
197
199
  `);
198
200
  process.exit(0);
199
201
  }
@@ -2916,6 +2918,79 @@ async function analyze() {
2916
2918
  console.log();
2917
2919
  }
2918
2920
 
2921
+ async function opus47() {
2922
+ console.log();
2923
+ console.log(c.bold + ' 🚨 cc-safe-setup --opus47' + c.reset);
2924
+ console.log(c.dim + ' Opus 4.7 protection — fixes for known critical issues' + c.reset);
2925
+ console.log();
2926
+
2927
+ // First install core hooks if not already installed
2928
+ mkdirSync(HOOKS_DIR, { recursive: true });
2929
+ let coreInstalled = 0;
2930
+ for (const [hookId, hookMeta] of Object.entries(HOOKS)) {
2931
+ const hookPath = join(HOOKS_DIR, `${hookId}.sh`);
2932
+ if (!existsSync(hookPath)) {
2933
+ writeFileSync(hookPath, SCRIPTS[hookId]);
2934
+ chmodSync(hookPath, 0o755);
2935
+ coreInstalled++;
2936
+ }
2937
+ }
2938
+ if (coreInstalled > 0) {
2939
+ // Update settings.json for core hooks
2940
+ let settings = {};
2941
+ if (existsSync(SETTINGS_PATH)) {
2942
+ settings = JSON.parse(readFileSync(SETTINGS_PATH, 'utf8'));
2943
+ }
2944
+ if (!settings.hooks) settings.hooks = {};
2945
+ for (const [hookId, hookMeta] of Object.entries(HOOKS)) {
2946
+ const trigger = hookMeta.trigger;
2947
+ if (!settings.hooks[trigger]) settings.hooks[trigger] = [];
2948
+ const hookPath = toBashPath(join(HOOKS_DIR, `${hookId}.sh`));
2949
+ const exists = settings.hooks[trigger].some(h => h.hooks?.some(hh => hh.command?.includes(hookId)));
2950
+ if (!exists) {
2951
+ settings.hooks[trigger].push({
2952
+ matcher: hookMeta.matcher,
2953
+ hooks: [{ type: 'command', command: hookPath }]
2954
+ });
2955
+ }
2956
+ }
2957
+ writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n');
2958
+ console.log(c.green + ' ✓' + c.reset + ` ${coreInstalled} core safety hooks installed`);
2959
+ } else {
2960
+ console.log(c.dim + ' ✓ Core safety hooks already installed' + c.reset);
2961
+ }
2962
+
2963
+ // Install Opus 4.7-specific hooks
2964
+ const opus47Hooks = [
2965
+ { name: 'model-version-alert', desc: 'Warns when Opus 4.7 is silently active (#49541)', issue: '4x token consumption' },
2966
+ { name: 'shell-config-truncation-guard', desc: 'Blocks installer from truncating ~/.bash_profile (#49615)', issue: 'config destruction' },
2967
+ { name: 'credential-exfil-guard', desc: 'Blocks credential file access (#49539, #49554)', issue: 'credential deletion' },
2968
+ { name: 'home-critical-bash-guard', desc: 'Blocks destructive ops on critical dotfiles (#49554)', issue: 'home directory attacks' },
2969
+ ];
2970
+
2971
+ console.log();
2972
+ console.log(c.bold + ' Opus 4.7 protection hooks:' + c.reset);
2973
+ let added = 0;
2974
+ for (const hook of opus47Hooks) {
2975
+ try {
2976
+ await installExample(hook.name);
2977
+ added++;
2978
+ } catch (e) {
2979
+ // installExample may exit on error, but we want to continue
2980
+ }
2981
+ }
2982
+
2983
+ console.log();
2984
+ console.log(c.bold + ' Why these hooks matter:' + c.reset);
2985
+ console.log(c.dim + ' Opus 4.7\'s safety classifier is hardcoded to 4.6 (#49618).' + c.reset);
2986
+ console.log(c.dim + ' Auto mode can\'t block dangerous commands on 4.7.' + c.reset);
2987
+ console.log(c.dim + ' These hooks run at process level — independent of the model.' + c.reset);
2988
+ console.log();
2989
+ console.log(c.green + ' Done.' + c.reset + ' Your setup is protected against known Opus 4.7 issues.');
2990
+ console.log(c.dim + ' Guide: https://yurukusa.github.io/cc-safe-setup/opus-47-survival-guide.html' + c.reset);
2991
+ console.log();
2992
+ }
2993
+
2919
2994
  async function shield() {
2920
2995
  const { execSync } = await import('child_process');
2921
2996
  const { readdirSync } = await import('fs');
@@ -2969,6 +3044,9 @@ async function shield() {
2969
3044
  // Always include these for maximum safety
2970
3045
  extras.push('scope-guard', 'no-sudo-guard', 'protect-claudemd', 'memory-write-guard', 'skill-gate', 'auto-approve-test', 'auto-approve-readonly');
2971
3046
 
3047
+ // Opus 4.7 safety: classifier is hardcoded to 4.6 (#49618) — hooks are the only defense
3048
+ extras.push('dotfile-protection-guard', 'home-critical-bash-guard');
3049
+
2972
3050
  for (const ex of extras) {
2973
3051
  const exPath = join(__dirname, 'examples', `${ex}.sh`);
2974
3052
  const hookPath = join(HOOKS_DIR, `${ex}.sh`);
@@ -3118,6 +3196,9 @@ async function shield() {
3118
3196
  console.log(c.dim + ' Verify: npx cc-safe-setup --verify' + c.reset);
3119
3197
  console.log(c.dim + ' Status: npx cc-safe-setup --status' + c.reset);
3120
3198
  console.log();
3199
+ console.log(c.dim + ' Burning tokens too fast? Free diagnosis:' + c.reset);
3200
+ console.log(c.blue + ' https://yurukusa.github.io/cc-safe-setup/token-checkup.html' + c.reset);
3201
+ console.log();
3121
3202
  }
3122
3203
 
3123
3204
  async function quickfix() {
@@ -5784,6 +5865,7 @@ async function main() {
5784
5865
  if (TEAM) return team();
5785
5866
  if (PROFILE_IDX !== -1) return profile(PROFILE);
5786
5867
  if (ANALYZE) return analyze();
5868
+ if (OPUS47) return opus47();
5787
5869
  if (SHIELD) return shield();
5788
5870
  if (QUICKFIX) return quickfix();
5789
5871
  if (REPORT) return report();
@@ -5898,12 +5980,16 @@ async function main() {
5898
5980
  console.log(' ' + c.blue + ' --doctor' + c.reset + ' Verify hooks work');
5899
5981
  console.log(' ' + c.blue + ' --simulate "cmd"' + c.reset + ' Test how hooks react');
5900
5982
  console.log(' ' + c.blue + ' --shield' + c.reset + ' Maximum safety (recommended)');
5983
+ console.log(' ' + c.blue + ' --opus47' + c.reset + ' Opus 4.7 crisis protection');
5901
5984
  console.log();
5902
- console.log(' ' + c.dim + '23 web tools: https://yurukusa.github.io/cc-safe-setup/hub.html' + c.reset);
5985
+ console.log(' ' + c.dim + 'Free tools:' + c.reset);
5986
+ console.log(' ' + c.blue + ' Token Checkup' + c.reset + ' https://yurukusa.github.io/cc-safe-setup/token-checkup.html');
5987
+ console.log(' ' + c.blue + ' Token Book' + c.reset + ' https://yurukusa.github.io/cc-safe-setup/token-book.html');
5988
+ console.log(' ' + c.dim + ' 28 web tools: https://yurukusa.github.io/cc-safe-setup/hub.html' + c.reset);
5903
5989
  console.log();
5904
- console.log(' ' + c.dim + 'Like this tool? Support development:' + c.reset);
5905
- console.log(' ' + c.dim + ' Sponsor: https://github.com/sponsors/yurukusa' + c.reset);
5906
- console.log(' ' + c.dim + ' Book: https://zenn.dev/yurukusa/books/6076c23b1cb18b' + c.reset);
5990
+ console.log(' ' + c.dim + 'Tokens disappearing too fast?' + c.reset);
5991
+ console.log(' ' + c.blue + ' Token Book' + c.reset + ' Cut consumption in half — https://yurukusa.github.io/cc-safe-setup/token-book.html');
5992
+ console.log(' ' + c.dim + ' Safety Guide: https://zenn.dev/yurukusa/books/6076c23b1cb18b' + c.reset);
5907
5993
  console.log();
5908
5994
  }
5909
5995
 
@@ -0,0 +1,4 @@
1
+ This file was created in the wrong location. The correct version is at:
2
+ ~/.claude/projects/-home-namakusa-projects-cc-loop/memory/market-anthropic-japan-strategy-2026-04-13.md
3
+
4
+ This file can be deleted.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cc-safe-setup",
3
- "version": "29.6.40",
4
- "description": "One command to make Claude Code safe. 650 example hooks + 8 built-in. 56 CLI commands. Token consumption diagnosis. Works with Auto Mode.",
3
+ "version": "29.7.0",
4
+ "description": "One command to make Claude Code safe. 700 example hooks + 8 built-in. 56 CLI commands. Token consumption diagnosis. Works with Auto Mode.",
5
5
  "main": "index.mjs",
6
6
  "bin": {
7
7
  "cc-safe-setup": "index.mjs"
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "credential-guard",
3
+ "description": "Protect secrets and credentials from Claude Code. Blocks writes to .env files, detects API keys in shell commands, prevents hardcoded tokens, and guards service account JSON files.",
4
+ "version": "1.1.0",
5
+ "author": { "name": "yurukusa" },
6
+ "homepage": "https://yurukusa.github.io/cc-safe-setup/",
7
+ "repository": "https://github.com/yurukusa/cc-safe-setup",
8
+ "license": "MIT",
9
+ "hooks": {
10
+ "PreToolUse": [
11
+ {
12
+ "matcher": "Write",
13
+ "hooks": [
14
+ {
15
+ "type": "command",
16
+ "command": "INPUT=$(cat); FILE=$(echo \"$INPUT\" | jq -r '.tool_input.file_path // empty' 2>/dev/null); [ -z \"$FILE\" ] && exit 0; if echo \"$FILE\" | grep -qE '\\.env$|\\.env\\.|credentials|secret'; then echo \"BLOCKED: Writing to sensitive file: $FILE\" >&2; exit 2; fi"
17
+ }
18
+ ]
19
+ },
20
+ {
21
+ "matcher": "Edit",
22
+ "hooks": [
23
+ {
24
+ "type": "command",
25
+ "command": "INPUT=$(cat); FILE=$(echo \"$INPUT\" | jq -r '.tool_input.file_path // empty' 2>/dev/null); [ -z \"$FILE\" ] && exit 0; if echo \"$FILE\" | grep -qE '\\.env$|\\.env\\.|credentials|secret'; then echo \"BLOCKED: Editing sensitive file: $FILE\" >&2; exit 2; fi"
26
+ }
27
+ ]
28
+ },
29
+ {
30
+ "matcher": "Bash",
31
+ "hooks": [
32
+ {
33
+ "type": "command",
34
+ "command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty' 2>/dev/null); [ -z \"$CMD\" ] && exit 0; if echo \"$CMD\" | grep -qE '(sk|pk|api|key|token|secret|password)[-_]?[a-zA-Z0-9]{20,}'; then echo 'WARNING: Possible API key or token detected in command. Verify no secrets are exposed.' >&2; fi"
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "matcher": "Write",
40
+ "hooks": [
41
+ {
42
+ "type": "command",
43
+ "command": "INPUT=$(cat); FILE=$(echo \"$INPUT\" | jq -r '.tool_input.file_path // empty' 2>/dev/null); [ -z \"$FILE\" ] && exit 0; if echo \"$FILE\" | grep -qE 'serviceaccount.*\\.json|key\\.json|credentials\\.json'; then echo \"BLOCKED: Writing to service account file: $FILE\" >&2; exit 2; fi"
44
+ }
45
+ ]
46
+ },
47
+ {
48
+ "matcher": "Bash",
49
+ "hooks": [
50
+ {
51
+ "type": "command",
52
+ "command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty' 2>/dev/null); [ -z \"$CMD\" ] && exit 0; if echo \"$CMD\" | grep -qE 'ANTHROPIC_API_KEY|OPENAI_API_KEY|AWS_SECRET|GITHUB_TOKEN|DATABASE_URL'; then echo 'WARNING: Environment variable with potential secret detected in command.' >&2; fi"
53
+ }
54
+ ]
55
+ }
56
+ ]
57
+ }
58
+ }
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "git-protection",
3
+ "description": "Git safety hooks for Claude Code. Blocks force-push, protects main/master branch, prevents hard-reset, guards interactive rebase, and blocks git clean -fd.",
4
+ "version": "1.1.0",
5
+ "author": { "name": "yurukusa" },
6
+ "homepage": "https://yurukusa.github.io/cc-safe-setup/",
7
+ "repository": "https://github.com/yurukusa/cc-safe-setup",
8
+ "license": "MIT",
9
+ "hooks": {
10
+ "PreToolUse": [
11
+ {
12
+ "matcher": "Bash",
13
+ "hooks": [
14
+ {
15
+ "type": "command",
16
+ "command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty' 2>/dev/null); [ -z \"$CMD\" ] && exit 0; if echo \"$CMD\" | grep -qE 'git\\s+push.*--force|git\\s+push.*-f\\b'; then echo 'BLOCKED: Force push. Use --force-with-lease for safer alternative.' >&2; exit 2; fi"
17
+ }
18
+ ]
19
+ },
20
+ {
21
+ "matcher": "Bash",
22
+ "hooks": [
23
+ {
24
+ "type": "command",
25
+ "command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty' 2>/dev/null); [ -z \"$CMD\" ] && exit 0; if echo \"$CMD\" | grep -qE 'git\\s+push\\s+(origin\\s+)?(main|master)\\b'; then echo 'BLOCKED: Direct push to main/master. Use a feature branch and PR.' >&2; exit 2; fi"
26
+ }
27
+ ]
28
+ },
29
+ {
30
+ "matcher": "Bash",
31
+ "hooks": [
32
+ {
33
+ "type": "command",
34
+ "command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty' 2>/dev/null); [ -z \"$CMD\" ] && exit 0; if echo \"$CMD\" | grep -qE 'git\\s+reset\\s+--hard'; then echo 'BLOCKED: git reset --hard. Use git stash or git reset --soft.' >&2; exit 2; fi"
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "matcher": "Bash",
40
+ "hooks": [
41
+ {
42
+ "type": "command",
43
+ "command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty' 2>/dev/null); [ -z \"$CMD\" ] && exit 0; if echo \"$CMD\" | grep -qE 'git\\s+clean\\s+-fd|git\\s+clean\\s+-f'; then echo 'BLOCKED: git clean removes untracked files permanently. Review with git clean -n first.' >&2; exit 2; fi"
44
+ }
45
+ ]
46
+ },
47
+ {
48
+ "matcher": "Bash",
49
+ "hooks": [
50
+ {
51
+ "type": "command",
52
+ "command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty' 2>/dev/null); [ -z \"$CMD\" ] && exit 0; if echo \"$CMD\" | grep -qE 'git\\s+branch\\s+-D'; then echo 'BLOCKED: Force branch deletion. Use -d (safe delete) instead of -D.' >&2; exit 2; fi"
53
+ }
54
+ ]
55
+ }
56
+ ]
57
+ }
58
+ }
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "safety-essentials",
3
+ "description": "5 essential safety hooks for Claude Code. Blocks rm -rf, force-push, hard-reset, .env overwrites, and package publish. The minimum viable safety net from 800+ hours of autonomous operation.",
4
+ "version": "1.1.0",
5
+ "author": { "name": "yurukusa" },
6
+ "homepage": "https://yurukusa.github.io/cc-safe-setup/",
7
+ "repository": "https://github.com/yurukusa/cc-safe-setup",
8
+ "license": "MIT",
9
+ "hooks": {
10
+ "PreToolUse": [
11
+ {
12
+ "matcher": "Bash",
13
+ "hooks": [
14
+ {
15
+ "type": "command",
16
+ "command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty' 2>/dev/null); [ -z \"$CMD\" ] && exit 0; if echo \"$CMD\" | grep -qE 'rm\\s+-r(f|F)|rm\\s+-(f|F)r|rm\\s+--force.*-r|rm\\s+-r.*--force'; then echo 'BLOCKED: rm -rf detected. Use git clean or manual deletion instead.' >&2; exit 2; fi"
17
+ }
18
+ ]
19
+ },
20
+ {
21
+ "matcher": "Bash",
22
+ "hooks": [
23
+ {
24
+ "type": "command",
25
+ "command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty' 2>/dev/null); [ -z \"$CMD\" ] && exit 0; if echo \"$CMD\" | grep -qE 'git\\s+push.*--force|git\\s+push.*-f\\b'; then echo 'BLOCKED: Force push detected. Use --force-with-lease or push normally.' >&2; exit 2; fi"
26
+ }
27
+ ]
28
+ },
29
+ {
30
+ "matcher": "Bash",
31
+ "hooks": [
32
+ {
33
+ "type": "command",
34
+ "command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty' 2>/dev/null); [ -z \"$CMD\" ] && exit 0; if echo \"$CMD\" | grep -qE 'git\\s+reset\\s+--hard'; then echo 'BLOCKED: git reset --hard discards uncommitted changes. Use git stash instead.' >&2; exit 2; fi"
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "matcher": "Write",
40
+ "hooks": [
41
+ {
42
+ "type": "command",
43
+ "command": "INPUT=$(cat); FILE=$(echo \"$INPUT\" | jq -r '.tool_input.file_path // empty' 2>/dev/null); [ -z \"$FILE\" ] && exit 0; if echo \"$FILE\" | grep -qE '\\.env$|\\.env\\.'; then echo \"BLOCKED: Writing to environment file: $FILE\" >&2; exit 2; fi"
44
+ }
45
+ ]
46
+ },
47
+ {
48
+ "matcher": "Bash",
49
+ "hooks": [
50
+ {
51
+ "type": "command",
52
+ "command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty' 2>/dev/null); [ -z \"$CMD\" ] && exit 0; if echo \"$CMD\" | grep -qE 'npm\\s+publish|yarn\\s+publish'; then echo 'BLOCKED: Package publish requires manual execution.' >&2; exit 2; fi"
53
+ }
54
+ ]
55
+ }
56
+ ]
57
+ }
58
+ }
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "token-guard",
3
+ "description": "Token consumption guards for Claude Code. Warns on large file reads (100KB+), limits unique file reads per session, estimates token budget, and caps subagent spawns. From 800+ hours of autonomous operation data.",
4
+ "version": "1.0.0",
5
+ "author": { "name": "yurukusa" },
6
+ "homepage": "https://yurukusa.github.io/cc-safe-setup/token-book.html",
7
+ "repository": "https://github.com/yurukusa/cc-safe-setup",
8
+ "license": "MIT",
9
+ "hooks": {
10
+ "PreToolUse": [
11
+ {
12
+ "matcher": "Read",
13
+ "hooks": [
14
+ {
15
+ "type": "command",
16
+ "command": "INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r '.tool_input.file_path // empty' 2>/dev/null); [ -z \"$FP\" ] || [ ! -f \"$FP\" ] && exit 0; SZ=$(stat -c%s \"$FP\" 2>/dev/null || stat -f%z \"$FP\" 2>/dev/null); [ \"$SZ\" -gt 102400 ] 2>/dev/null && echo \"Warning: $(basename $FP) is $((SZ/1024))KB. Use limit parameter to read only what you need.\" || true"
17
+ }
18
+ ]
19
+ },
20
+ {
21
+ "matcher": "Read",
22
+ "hooks": [
23
+ {
24
+ "type": "command",
25
+ "command": "BUDGET=${CC_READ_BUDGET:-100}; WARN=${CC_READ_WARN:-50}; T=/tmp/cc-read-budget-$PPID; INPUT=$(cat); FP=$(echo \"$INPUT\" | jq -r '.tool_input.file_path // empty' 2>/dev/null); [ -z \"$FP\" ] && exit 0; C=0; if [ -f \"$T\" ]; then grep -qF \"$FP\" \"$T\" || echo \"$FP\" >> \"$T\"; C=$(wc -l < \"$T\"); else echo \"$FP\" > \"$T\"; C=1; fi; [ \"$C\" -ge \"$BUDGET\" ] && { echo \"[BLOCK] Read budget reached (${BUDGET} files). Use Glob/Grep to narrow down.\"; exit 2; }; [ \"$C\" -ge \"$WARN\" ] && echo \"Warning: ${C}/${BUDGET} files read.\""
26
+ }
27
+ ]
28
+ },
29
+ {
30
+ "matcher": "Agent",
31
+ "hooks": [
32
+ {
33
+ "type": "command",
34
+ "command": "MAX=${CC_MAX_AGENTS:-3}; T=/tmp/cc-agents-$PPID; C=0; [ -f \"$T\" ] && C=$(cat \"$T\"); C=$((C+1)); echo $C > \"$T\"; [ $C -gt $MAX ] && { echo \"[BLOCK] Subagent limit (${MAX}). Complete existing agents first.\"; exit 2; }; [ $C -ge $MAX ] && echo \"Warning: ${C}/${MAX} subagents spawned.\""
35
+ }
36
+ ]
37
+ }
38
+ ],
39
+ "PostToolUse": [
40
+ {
41
+ "matcher": {},
42
+ "hooks": [
43
+ {
44
+ "type": "command",
45
+ "command": "WARN=${CC_TOKEN_BUDGET:-50000}; BLOCK=${CC_TOKEN_BLOCK:-100000}; T=/tmp/cc-tokens-$PPID; INPUT=$(cat); SZ=${#INPUT}; TK=$((SZ/4)); TOTAL=0; [ -f \"$T\" ] && TOTAL=$(cat \"$T\"); TOTAL=$((TOTAL+TK)); echo $TOTAL > \"$T\"; [ $TOTAL -ge $BLOCK ] && { echo \"[BLOCK] Token budget exceeded (~${TOTAL}). Run /compact.\"; exit 2; }; [ $TOTAL -ge $WARN ] && echo \"Warning: ~${TOTAL} tokens consumed (limit: ${BLOCK}).\""
46
+ }
47
+ ]
48
+ }
49
+ ]
50
+ }
51
+ }
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: cc-safe-setup
3
+ description: Safety hooks for Claude Code — 695 pre-built hooks that prevent file deletion, credential leaks, git disasters, and token waste during autonomous AI coding sessions. Install with npx cc-safe-setup.
4
+ ---
5
+
6
+ # cc-safe-setup
7
+
8
+ Safety-first configuration for Claude Code. Prevents the accidents that happen when AI writes code autonomously.
9
+
10
+ ## What it does
11
+
12
+ Installs pre-built safety hooks into your Claude Code environment. These hooks run automatically before/after tool calls to block dangerous operations.
13
+
14
+ **Categories:**
15
+ - **File protection**: Block `rm -rf`, prevent overwriting files outside project
16
+ - **Git safety**: Prevent force-push to main, block `reset --hard`
17
+ - **Credential guards**: Stop `.env` files from being committed or read by AI
18
+ - **Token optimization**: Warn on large file reads, limit subagent spawning
19
+ - **Quality gates**: Detect lazy rewrites, verify claims before committing
20
+
21
+ ## Quick start
22
+
23
+ ```bash
24
+ npx cc-safe-setup
25
+ ```
26
+
27
+ This runs an interactive wizard that configures hooks based on your risk profile.
28
+
29
+ ## Install individual hooks
30
+
31
+ ```bash
32
+ npx cc-safe-setup --install-example large-read-guard
33
+ npx cc-safe-setup --install-example prevent-rm-rf
34
+ npx cc-safe-setup --install-example git-force-push-block
35
+ ```
36
+
37
+ ## Why hooks instead of CLAUDE.md rules
38
+
39
+ Rules in CLAUDE.md are suggestions — Claude can forget them. Hooks are enforced at the system level. A hook that blocks `rm -rf` cannot be overridden by the AI.
40
+
41
+ From 800+ hours of autonomous operation: the hooks that matter most are the ones you don't notice until something goes wrong.
42
+
43
+ ## Resources
44
+
45
+ - Repository: https://github.com/yurukusa/cc-safe-setup
46
+ - Hook Selector (find hooks for your setup): https://yurukusa.github.io/cc-safe-setup/hook-selector.html
47
+ - Token Checkup (diagnose waste): https://yurukusa.github.io/cc-safe-setup/token-checkup.html