cc-safe-setup 7.3.0 → 7.5.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.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  **One command to make Claude Code safe for autonomous operation.** [日本語](docs/README.ja.md)
8
8
 
9
- 8 built-in + 51 examples = **59 hooks**. 26 CLI commands. 284 tests. [Web Tool](https://yurukusa.github.io/cc-safe-setup/) · [Cheat Sheet](https://yurukusa.github.io/cc-safe-setup/cheatsheet.html) · [Troubleshooting](TROUBLESHOOTING.md)
9
+ 8 built-in + 68 examples = **76 hooks**. 28 CLI commands. 394 tests. [Web Tool](https://yurukusa.github.io/cc-safe-setup/) · [Cheat Sheet](https://yurukusa.github.io/cc-safe-setup/cheatsheet.html) · [Troubleshooting](TROUBLESHOOTING.md)
10
10
 
11
11
  ```bash
12
12
  npx cc-safe-setup
@@ -87,7 +87,7 @@ Each hook exists because a real incident happened without it.
87
87
  | `--scan [--apply]` | Tech stack detection |
88
88
  | `--export / --import` | Team config sharing |
89
89
  | `--verify` | Test each hook |
90
- | `--install-example <name>` | Install from 51 examples |
90
+ | `--install-example <name>` | Install from 68 examples |
91
91
  | `--examples [filter]` | Browse examples by keyword |
92
92
  | `--full` | All-in-one setup |
93
93
  | `--status` | Check installed hooks |
@@ -0,0 +1,33 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # conflict-marker-guard.sh — Block commits with conflict markers
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # Claude sometimes resolves merge conflicts incorrectly, leaving
7
+ # <<<<<<< / ======= / >>>>>>> markers in files. This hook checks
8
+ # staged files for conflict markers before allowing a commit.
9
+ #
10
+ # TRIGGER: PreToolUse MATCHER: "Bash"
11
+ # ================================================================
12
+
13
+ COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
14
+ [ -z "$COMMAND" ] && exit 0
15
+
16
+ # Only check on git commit
17
+ echo "$COMMAND" | grep -qE '^\s*git\s+commit' || exit 0
18
+
19
+ # Check staged files for conflict markers
20
+ CONFLICTS=$(git diff --cached --name-only 2>/dev/null | while read -r f; do
21
+ [ -f "$f" ] && grep -lE '^(<{7}|={7}|>{7})' "$f" 2>/dev/null
22
+ done)
23
+
24
+ if [ -n "$CONFLICTS" ]; then
25
+ COUNT=$(echo "$CONFLICTS" | wc -l)
26
+ echo "BLOCKED: $COUNT file(s) contain merge conflict markers:" >&2
27
+ echo "$CONFLICTS" | head -5 | sed 's/^/ /' >&2
28
+ echo "" >&2
29
+ echo "Resolve conflicts before committing." >&2
30
+ exit 2
31
+ fi
32
+
33
+ exit 0
@@ -0,0 +1,28 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # disk-space-guard.sh — Warn when disk space is running low
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # Long Claude Code sessions can generate large files, logs, and
7
+ # build artifacts. This hook warns before writes when disk space
8
+ # is below a threshold.
9
+ #
10
+ # TRIGGER: PreToolUse MATCHER: "Write|Bash"
11
+ #
12
+ # CONFIG:
13
+ # CC_DISK_WARN_PCT=90 (warn at this percentage used)
14
+ # ================================================================
15
+
16
+ WARN_PCT="${CC_DISK_WARN_PCT:-90}"
17
+
18
+ # Check disk usage (percentage used on the working directory's partition)
19
+ USAGE=$(df --output=pcent . 2>/dev/null | tail -1 | tr -d ' %')
20
+ [ -z "$USAGE" ] && exit 0
21
+
22
+ if [ "$USAGE" -ge "$WARN_PCT" ]; then
23
+ AVAIL=$(df -h --output=avail . 2>/dev/null | tail -1 | tr -d ' ')
24
+ echo "WARNING: Disk usage is ${USAGE}% (${AVAIL} available)." >&2
25
+ echo "Consider cleaning up build artifacts, logs, or /tmp files." >&2
26
+ fi
27
+
28
+ exit 0
@@ -0,0 +1,53 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # fact-check-gate.sh — Warn when docs reference unread source files
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # Claude writes documentation that references source code without
7
+ # actually reading the files first. This leads to hallucinated
8
+ # function signatures, wrong parameter names, and false claims.
9
+ #
10
+ # This hook tracks which files were Read in the session, and warns
11
+ # when a doc edit mentions source files that weren't read.
12
+ #
13
+ # TRIGGER: PostToolUse MATCHER: "Edit|Write"
14
+ #
15
+ # Born from: https://github.com/anthropics/claude-code/issues/38057
16
+ # "Claude produces false claims in technical docs"
17
+ # ================================================================
18
+
19
+ INPUT=$(cat)
20
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
21
+ [ -z "$FILE" ] && exit 0
22
+
23
+ # Only check documentation files
24
+ case "$FILE" in
25
+ *.md|*.rst|*.txt|*/docs/*|*/doc/*|*README*|*CHANGELOG*|*CONTRIBUTING*)
26
+ ;;
27
+ *)
28
+ exit 0
29
+ ;;
30
+ esac
31
+
32
+ # Track reads in a session state file
33
+ STATE="/tmp/cc-fact-check-reads-$(echo "$PWD" | md5sum | cut -c1-8)"
34
+
35
+ # Get the content being written
36
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
37
+ [ -z "$CONTENT" ] && exit 0
38
+
39
+ # Extract referenced source files from the doc content
40
+ # Looks for: `filename.ext`, filename.ext, import from, require()
41
+ REFS=$(echo "$CONTENT" | grep -oE '`[a-zA-Z0-9_/-]+\.(js|ts|py|go|rs|java|rb|sh|mjs|cjs|jsx|tsx)`' | tr -d '`' | sort -u)
42
+ [ -z "$REFS" ] && exit 0
43
+
44
+ # Check if referenced files were read in this session
45
+ if [ ! -f "$STATE" ]; then
46
+ # No reads tracked yet — warn about all references
47
+ COUNT=$(echo "$REFS" | wc -l)
48
+ echo "WARNING: Doc references $COUNT source file(s) that may not have been read:" >&2
49
+ echo "$REFS" | head -5 | sed 's/^/ /' >&2
50
+ echo "Read the source files before documenting them to avoid hallucination." >&2
51
+ fi
52
+
53
+ exit 0
@@ -0,0 +1,40 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # memory-write-guard.sh — Log writes to ~/.claude/ directory
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # Claude auto-writes to ~/.claude/projects/*/memory/ without
7
+ # user visibility. This hook logs all writes to ~/.claude/ paths
8
+ # so users know what's being stored.
9
+ #
10
+ # TRIGGER: PreToolUse MATCHER: "Write|Edit"
11
+ #
12
+ # Born from: https://github.com/anthropics/claude-code/issues/38040
13
+ # "No way to enforce approval on all file modifications"
14
+ # ================================================================
15
+
16
+ INPUT=$(cat)
17
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
18
+ [ -z "$FILE" ] && exit 0
19
+
20
+ # Check if targeting ~/.claude/
21
+ case "$FILE" in
22
+ */.claude/*|~/.claude/*)
23
+ # Log the write
24
+ LOG="$HOME/.claude/memory-writes.log"
25
+ echo "[$(date -Iseconds)] Write to: $FILE" >> "$LOG" 2>/dev/null
26
+
27
+ # Only warn (don't block) — memory writes are usually intentional
28
+ echo "NOTE: Writing to Claude config directory: $FILE" >&2
29
+
30
+ # Block writes to settings.json unless explicitly allowed
31
+ case "$FILE" in
32
+ */settings.json|*/settings.local.json)
33
+ echo "WARNING: Modifying Claude Code settings file." >&2
34
+ echo "Verify this change is intentional." >&2
35
+ ;;
36
+ esac
37
+ ;;
38
+ esac
39
+
40
+ exit 0
@@ -0,0 +1,32 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # overwrite-guard.sh — Warn before overwriting existing files
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # Claude's Write tool can silently overwrite files without
7
+ # confirmation. This hook warns when a Write targets a file
8
+ # that already exists, giving visibility into potential data loss.
9
+ #
10
+ # TRIGGER: PreToolUse MATCHER: "Write"
11
+ #
12
+ # Born from: https://github.com/anthropics/claude-code/issues/37595
13
+ # "/export overwrites existing files without warning"
14
+ # ================================================================
15
+
16
+ INPUT=$(cat)
17
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
18
+ [ -z "$FILE" ] && exit 0
19
+
20
+ # Expand ~ to home directory
21
+ FILE="${FILE/#\~/$HOME}"
22
+
23
+ if [ -f "$FILE" ]; then
24
+ SIZE=$(wc -c < "$FILE" 2>/dev/null || echo 0)
25
+ if [ "$SIZE" -gt 0 ]; then
26
+ LINES=$(wc -l < "$FILE" 2>/dev/null || echo 0)
27
+ echo "WARNING: Overwriting existing file: $FILE ($LINES lines, $SIZE bytes)" >&2
28
+ echo "Use Edit tool instead to make targeted changes." >&2
29
+ fi
30
+ fi
31
+
32
+ exit 0
@@ -0,0 +1,51 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # prompt-injection-guard.sh — Detect prompt injection in tool output
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # When Claude reads files or fetches web content, malicious
7
+ # instructions can be injected. This hook warns when tool output
8
+ # contains common prompt injection patterns.
9
+ #
10
+ # TRIGGER: PostToolUse MATCHER: ""
11
+ #
12
+ # Born from: https://github.com/anthropics/claude-code/issues/38046
13
+ # "Prompt Injection in /insights output"
14
+ # ================================================================
15
+
16
+ INPUT=$(cat)
17
+ OUTPUT=$(echo "$INPUT" | jq -r '.tool_result // empty' 2>/dev/null)
18
+ [ -z "$OUTPUT" ] && exit 0
19
+
20
+ # Check for common prompt injection patterns
21
+ SUSPICIOUS=0
22
+
23
+ # "Ignore previous instructions" pattern
24
+ if echo "$OUTPUT" | grep -qiE 'ignore\s+(all\s+)?previous\s+instructions'; then
25
+ echo "WARNING: Possible prompt injection detected: 'ignore previous instructions'" >&2
26
+ SUSPICIOUS=1
27
+ fi
28
+
29
+ # "You are now" role reassignment
30
+ if echo "$OUTPUT" | grep -qiE 'you\s+are\s+now\s+(a|an)\s+'; then
31
+ echo "WARNING: Possible prompt injection detected: role reassignment" >&2
32
+ SUSPICIOUS=1
33
+ fi
34
+
35
+ # "System prompt" manipulation
36
+ if echo "$OUTPUT" | grep -qiE '(new|updated|override)\s+system\s+prompt'; then
37
+ echo "WARNING: Possible prompt injection detected: system prompt override" >&2
38
+ SUSPICIOUS=1
39
+ fi
40
+
41
+ # Hidden instructions in HTML comments or zero-width chars
42
+ if echo "$OUTPUT" | grep -qP '<!--.*(?:execute|run|delete|remove).*-->'; then
43
+ echo "WARNING: Possible prompt injection in HTML comment" >&2
44
+ SUSPICIOUS=1
45
+ fi
46
+
47
+ if [ "$SUSPICIOUS" -eq 1 ]; then
48
+ echo "Review the output carefully before acting on it." >&2
49
+ fi
50
+
51
+ exit 0
@@ -0,0 +1,48 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # test-deletion-guard.sh — Block deletion of test assertions
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # Claude sometimes deletes or comments out failing tests instead
7
+ # of fixing the underlying code. This hook detects when an Edit
8
+ # to a test file removes test assertions.
9
+ #
10
+ # TRIGGER: PreToolUse MATCHER: "Edit"
11
+ #
12
+ # Born from: https://github.com/anthropics/claude-code/issues/38050
13
+ # "Claude skips/deletes tests instead of fixing them"
14
+ # ================================================================
15
+
16
+ INPUT=$(cat)
17
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
18
+ [ -z "$FILE" ] && exit 0
19
+
20
+ # Only check test files
21
+ case "$FILE" in
22
+ *test*|*spec*|*__tests__*|*_test.go|*_test.py|*Test.java|*Test.kt)
23
+ ;;
24
+ *)
25
+ exit 0
26
+ ;;
27
+ esac
28
+
29
+ OLD=$(echo "$INPUT" | jq -r '.tool_input.old_string // empty' 2>/dev/null)
30
+ NEW=$(echo "$INPUT" | jq -r '.tool_input.new_string // empty' 2>/dev/null)
31
+ [ -z "$OLD" ] && exit 0
32
+
33
+ # Count test assertions in old vs new
34
+ count_tests() {
35
+ echo "$1" | grep -cE '(it\(|test\(|describe\(|def test_|#\[test\]|@Test|assert|expect\(|should\b)' 2>/dev/null || echo 0
36
+ }
37
+
38
+ OLD_COUNT=$(count_tests "$OLD")
39
+ NEW_COUNT=$(count_tests "$NEW")
40
+
41
+ if [ "$OLD_COUNT" -gt 0 ] && [ "$NEW_COUNT" -lt "$OLD_COUNT" ]; then
42
+ REMOVED=$((OLD_COUNT - NEW_COUNT))
43
+ echo "WARNING: This edit removes $REMOVED test assertion(s) from $FILE." >&2
44
+ echo "If tests are failing, fix the code instead of deleting tests." >&2
45
+ echo "Old assertions: $OLD_COUNT → New assertions: $NEW_COUNT" >&2
46
+ fi
47
+
48
+ exit 0
@@ -0,0 +1,54 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # token-budget-guard.sh — Estimate and limit session token cost
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # Claude Code sessions can consume hundreds of dollars in tokens
7
+ # without the user realizing it. This hook estimates cumulative
8
+ # cost and warns/blocks when a budget threshold is exceeded.
9
+ #
10
+ # TRIGGER: PostToolUse MATCHER: ""
11
+ #
12
+ # CONFIG:
13
+ # CC_TOKEN_BUDGET=10 (warn at $10 estimated cost)
14
+ # CC_TOKEN_BLOCK=50 (block at $50 estimated cost)
15
+ #
16
+ # Born from: https://github.com/anthropics/claude-code/issues/38029
17
+ # "652k output tokens ($342) without user input"
18
+ # ================================================================
19
+
20
+ WARN_BUDGET="${CC_TOKEN_BUDGET:-10}"
21
+ BLOCK_BUDGET="${CC_TOKEN_BLOCK:-50}"
22
+ STATE="/tmp/cc-token-budget-$(echo "$PWD" | md5sum | cut -c1-8)"
23
+
24
+ # Estimate tokens from tool output size
25
+ INPUT=$(cat)
26
+ OUTPUT=$(echo "$INPUT" | jq -r '.tool_result // empty' 2>/dev/null)
27
+ OUTPUT_LEN=${#OUTPUT}
28
+
29
+ # Rough estimation: 1 token ≈ 4 chars, $15/M input + $75/M output for Opus
30
+ # This is approximate — actual costs depend on model and caching
31
+ TOKENS=$((OUTPUT_LEN / 4))
32
+
33
+ # Accumulate
34
+ TOTAL=0
35
+ [ -f "$STATE" ] && TOTAL=$(cat "$STATE" 2>/dev/null || echo 0)
36
+ TOTAL=$((TOTAL + TOKENS))
37
+ echo "$TOTAL" > "$STATE"
38
+
39
+ # Estimate cost (output tokens at $75/M for Opus)
40
+ # Using integer math: cost_cents = tokens * 75 / 10000
41
+ COST_CENTS=$((TOTAL * 75 / 10000))
42
+
43
+ if [ "$COST_CENTS" -ge "$((BLOCK_BUDGET * 100))" ]; then
44
+ echo "BLOCKED: Estimated session cost ~\$${COST_CENTS%??}.${COST_CENTS: -2} exceeds \$$BLOCK_BUDGET budget." >&2
45
+ echo "Reset: rm $STATE" >&2
46
+ exit 2
47
+ fi
48
+
49
+ if [ "$COST_CENTS" -ge "$((WARN_BUDGET * 100))" ]; then
50
+ echo "WARNING: Estimated session cost ~\$${COST_CENTS%??}.${COST_CENTS: -2} approaching \$$BLOCK_BUDGET limit." >&2
51
+ echo "Consider using /compact or starting a new session." >&2
52
+ fi
53
+
54
+ exit 0
@@ -0,0 +1,45 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # uncommitted-work-guard.sh — Block destructive git when dirty
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # Claude sometimes runs git checkout --, git reset --hard, or
7
+ # git stash drop when there are uncommitted changes, destroying
8
+ # hours of work. This hook checks git status before allowing
9
+ # destructive git commands.
10
+ #
11
+ # TRIGGER: PreToolUse MATCHER: "Bash"
12
+ #
13
+ # Born from: https://github.com/anthropics/claude-code/issues/37888
14
+ # "Claude runs forbidden destructive git commands, destroys work twice"
15
+ # ================================================================
16
+
17
+ COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
18
+ [ -z "$COMMAND" ] && exit 0
19
+
20
+ # Only check destructive git commands
21
+ DESTRUCTIVE=0
22
+ echo "$COMMAND" | grep -qE '\bgit\s+checkout\s+--\s' && DESTRUCTIVE=1
23
+ echo "$COMMAND" | grep -qE '\bgit\s+checkout\s+\.\s*$' && DESTRUCTIVE=1
24
+ echo "$COMMAND" | grep -qE '\bgit\s+restore\s+--staged\s+\.' && DESTRUCTIVE=1
25
+ echo "$COMMAND" | grep -qE '\bgit\s+restore\s+\.\s*$' && DESTRUCTIVE=1
26
+ echo "$COMMAND" | grep -qE '\bgit\s+reset\s+--hard' && DESTRUCTIVE=1
27
+ echo "$COMMAND" | grep -qE '\bgit\s+clean\s+-[a-zA-Z]*f' && DESTRUCTIVE=1
28
+ echo "$COMMAND" | grep -qE '\bgit\s+stash\s+drop' && DESTRUCTIVE=1
29
+
30
+ [ "$DESTRUCTIVE" -eq 0 ] && exit 0
31
+
32
+ # Check for uncommitted changes
33
+ DIRTY=$(git status --porcelain 2>/dev/null | head -20)
34
+ if [ -n "$DIRTY" ]; then
35
+ COUNT=$(echo "$DIRTY" | wc -l)
36
+ echo "BLOCKED: Destructive git command with $COUNT uncommitted change(s)." >&2
37
+ echo "Changes that would be lost:" >&2
38
+ echo "$DIRTY" | head -10 | sed 's/^/ /' >&2
39
+ [ "$COUNT" -gt 10 ] && echo " ... and $((COUNT-10)) more" >&2
40
+ echo "" >&2
41
+ echo "Commit or stash your changes first, then retry." >&2
42
+ exit 2
43
+ fi
44
+
45
+ exit 0
@@ -0,0 +1,43 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # verify-before-done.sh — Warn when committing without running tests
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # Claude Code often declares fixes "done" and commits without
7
+ # verifying the fix actually works. This hook warns when a commit
8
+ # is made in a project that has tests, but no test command was
9
+ # run recently in the session.
10
+ #
11
+ # TRIGGER: PreToolUse MATCHER: "Bash"
12
+ #
13
+ # Born from: https://github.com/anthropics/claude-code/issues/37818
14
+ # "Claude repeatedly declares fixes done without verification"
15
+ # ================================================================
16
+
17
+ COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
18
+ [ -z "$COMMAND" ] && exit 0
19
+
20
+ # Only check on git commit
21
+ echo "$COMMAND" | grep -qE '^\s*git\s+commit' || exit 0
22
+
23
+ # Track test execution via state file
24
+ STATE="/tmp/cc-tests-ran-$(pwd | md5sum | cut -c1-8)"
25
+
26
+ # Check if tests were run in this session
27
+ if [ ! -f "$STATE" ]; then
28
+ # Detect if project has tests
29
+ HAS_TESTS=0
30
+ [ -f "package.json" ] && grep -q '"test"' package.json 2>/dev/null && HAS_TESTS=1
31
+ [ -f "pytest.ini" ] || [ -f "setup.cfg" ] || [ -f "pyproject.toml" ] && HAS_TESTS=1
32
+ [ -f "Cargo.toml" ] && HAS_TESTS=1
33
+ [ -f "go.mod" ] && HAS_TESTS=1
34
+ [ -f "Makefile" ] && grep -q 'test:' Makefile 2>/dev/null && HAS_TESTS=1
35
+
36
+ if [ "$HAS_TESTS" -eq 1 ]; then
37
+ echo "WARNING: Committing without running tests first." >&2
38
+ echo "Run your test suite before committing to verify changes work." >&2
39
+ echo "To suppress: touch $STATE" >&2
40
+ fi
41
+ fi
42
+
43
+ exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-safe-setup",
3
- "version": "7.3.0",
3
+ "version": "7.5.0",
4
4
  "description": "One command to make Claude Code safe. 59 hooks (8 built-in + 51 examples). 26 CLI commands: dashboard, create, audit, lint, diff, migrate, compare, generate-ci. 284 tests.",
5
5
  "main": "index.mjs",
6
6
  "bin": {