cc-safe-setup 8.0.0 → 8.1.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 + 77 examples = **85 hooks**. 28 CLI commands. 423 tests. [Web Tool](https://yurukusa.github.io/cc-safe-setup/) · [Cheat Sheet](https://yurukusa.github.io/cc-safe-setup/hooks-cheatsheet.html) · [Builder](https://yurukusa.github.io/cc-safe-setup/builder.html) · [FAQ](https://yurukusa.github.io/cc-safe-setup/faq.html) · [Playground](https://yurukusa.github.io/cc-hook-registry/playground.html)
9
+ 8 built-in + 92 examples = **100 hooks**. 29 CLI commands. 433 tests. [Web Tool](https://yurukusa.github.io/cc-safe-setup/) · [Cheat Sheet](https://yurukusa.github.io/cc-safe-setup/hooks-cheatsheet.html) · [Builder](https://yurukusa.github.io/cc-safe-setup/builder.html) · [FAQ](https://yurukusa.github.io/cc-safe-setup/faq.html) · [Playground](https://yurukusa.github.io/cc-hook-registry/playground.html)
10
10
 
11
11
  ```bash
12
12
  npx cc-safe-setup
@@ -103,6 +103,8 @@ Each hook exists because a real incident happened without it.
103
103
 
104
104
  Safe to run multiple times. Existing settings are preserved. A backup is created if settings.json can't be parsed.
105
105
 
106
+ **Maximum safety:** `npx cc-safe-setup --shield` — one command: fix environment, install hooks, detect stack, configure settings, generate CLAUDE.md.
107
+
106
108
  **Preview first:** `npx cc-safe-setup --dry-run`
107
109
 
108
110
  **Check status:** `npx cc-safe-setup --status` — see which hooks are installed (exit code 1 if missing).
@@ -0,0 +1,10 @@
1
+ INPUT=$(cat)
2
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
4
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
5
+ [ -z "$COMMAND" ] && exit 0
6
+ if echo "$COMMAND" | grep -qE '\bgit\s+mv\b.*\b(src|lib|app)\b'; then
7
+ git stash push -m "pre-refactor-backup-$(date +%s)" 2>/dev/null
8
+ echo "NOTE: Stashed changes as pre-refactor backup." >&2
9
+ fi
10
+ exit 0
@@ -0,0 +1,13 @@
1
+ INPUT=$(cat)
2
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
4
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
5
+ [ -z "$COMMAND" ] && exit 0
6
+ if echo "$COMMAND" | grep -qE '\bgit\s+(checkout|switch)\s+-b\s+'; then
7
+ BRANCH=$(echo "$COMMAND" | grep -oE '(-b|--create)\s+(\S+)' | awk '{print $2}')
8
+ if [ -n "$BRANCH" ] && ! echo "$BRANCH" | grep -qE '^(feat|fix|chore|docs|test|refactor)/'; then
9
+ echo "WARNING: Branch '$BRANCH' doesn't follow convention." >&2
10
+ echo "Use: feat/, fix/, chore/, docs/, test/, refactor/" >&2
11
+ fi
12
+ fi
13
+ exit 0
@@ -0,0 +1,22 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # changelog-reminder.sh — Remind to update CHANGELOG on version bump
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # When Claude bumps a version number (npm version, cargo set-version,
7
+ # etc.), this hook reminds to update CHANGELOG.md with the changes.
8
+ #
9
+ # TRIGGER: PostToolUse MATCHER: "Bash"
10
+ # ================================================================
11
+
12
+ COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
13
+ [ -z "$COMMAND" ] && exit 0
14
+
15
+ # Detect version bump commands
16
+ if echo "$COMMAND" | grep -qE '(npm\s+version|cargo\s+set-version|bump2version|poetry\s+version)'; then
17
+ if [ -f "CHANGELOG.md" ]; then
18
+ echo "REMINDER: Update CHANGELOG.md with the new version's changes." >&2
19
+ fi
20
+ fi
21
+
22
+ exit 0
@@ -0,0 +1,12 @@
1
+ INPUT=$(cat)
2
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
4
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
5
+ [ -z "$CONTENT" ] && exit 0
6
+ LEN=${#CONTENT}
7
+ MAX="${CC_MAX_FILE_SIZE:-1048576}"
8
+ if [ "$LEN" -gt "$MAX" ]; then
9
+ echo "BLOCKED: File content is ${LEN} bytes (limit: ${MAX})." >&2
10
+ exit 2
11
+ fi
12
+ exit 0
@@ -0,0 +1,60 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # hardcoded-secret-detector.sh — Detect hardcoded secrets in edits
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # Claude sometimes hardcodes API keys, passwords, or tokens
7
+ # directly into source files instead of using environment
8
+ # variables. This hook checks edited content for secret patterns.
9
+ #
10
+ # TRIGGER: PostToolUse MATCHER: "Edit|Write"
11
+ # ================================================================
12
+
13
+ INPUT=$(cat)
14
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
15
+ [ -z "$CONTENT" ] && exit 0
16
+
17
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
18
+
19
+ # Skip config/env files (secrets are expected there)
20
+ case "$FILE" in
21
+ *.env*|*credentials*|*secret*|*.key|*.pem) exit 0 ;;
22
+ esac
23
+
24
+ FOUND=0
25
+
26
+ # AWS keys (AKIA...)
27
+ if echo "$CONTENT" | grep -qE 'AKIA[0-9A-Z]{16}'; then
28
+ echo "WARNING: Possible AWS access key in $FILE" >&2
29
+ FOUND=1
30
+ fi
31
+
32
+ # Generic API key patterns
33
+ if echo "$CONTENT" | grep -qE "(api_key|apikey|api-key|secret_key|access_token)\s*[=:]\s*['\"][a-zA-Z0-9]{20,}['\"]"; then
34
+ echo "WARNING: Possible hardcoded API key in $FILE" >&2
35
+ FOUND=1
36
+ fi
37
+
38
+ # Password patterns
39
+ if echo "$CONTENT" | grep -qiE "(password|passwd|pwd)\s*[=:]\s*['\"][^'\"]{8,}['\"]"; then
40
+ echo "WARNING: Possible hardcoded password in $FILE" >&2
41
+ FOUND=1
42
+ fi
43
+
44
+ # JWT tokens
45
+ if echo "$CONTENT" | grep -qE 'eyJ[a-zA-Z0-9_-]{20,}\.eyJ[a-zA-Z0-9_-]{20,}'; then
46
+ echo "WARNING: Possible JWT token in $FILE" >&2
47
+ FOUND=1
48
+ fi
49
+
50
+ # Private keys
51
+ if echo "$CONTENT" | grep -qE 'BEGIN (RSA |EC |DSA )?PRIVATE KEY'; then
52
+ echo "WARNING: Private key detected in $FILE" >&2
53
+ FOUND=1
54
+ fi
55
+
56
+ if [ "$FOUND" -eq 1 ]; then
57
+ echo "Use environment variables instead of hardcoding secrets." >&2
58
+ fi
59
+
60
+ exit 0
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+ # license-check.sh — Warn when creating files without a license header
3
+ # TRIGGER: PostToolUse MATCHER: "Write"
4
+ FILE=$(cat | jq -r '.tool_input.file_path // empty' 2>/dev/null)
5
+ [ -z "$FILE" ] && exit 0
6
+ case "$FILE" in *.js|*.ts|*.py|*.go|*.rs|*.java|*.rb|*.sh) ;; *) exit 0 ;; esac
7
+ [ ! -f "$FILE" ] && exit 0
8
+ if ! head -5 "$FILE" | grep -qiE '(license|copyright|MIT|Apache|GPL)'; then
9
+ if [ -f "LICENSE" ] || [ -f "LICENSE.md" ]; then
10
+ echo "NOTE: New source file $FILE has no license header." >&2
11
+ fi
12
+ fi
13
+ exit 0
@@ -0,0 +1,10 @@
1
+ INPUT=$(cat)
2
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
4
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
5
+ [ -z "$CONTENT" ] && exit 0
6
+ case "$FILE" in *.test.*|*.spec.*|*debug*) exit 0 ;; esac
7
+ if echo "$CONTENT" | grep -qE '\bconsole\.(log|debug)\b'; then
8
+ echo "WARNING: console.log detected in $FILE. Use proper logging." >&2
9
+ fi
10
+ exit 0
@@ -0,0 +1,9 @@
1
+ INPUT=$(cat)
2
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
4
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
5
+ [ -z "$CONTENT" ] && exit 0
6
+ if echo "$CONTENT" | grep -qE '\beval\s*\('; then
7
+ echo "WARNING: eval() detected in $FILE. Avoid eval for security." >&2
8
+ fi
9
+ exit 0
@@ -0,0 +1,12 @@
1
+ #!/bin/bash
2
+ # no-todo-ship.sh — Block commits with TODO/FIXME/HACK markers
3
+ # TRIGGER: PreToolUse MATCHER: "Bash"
4
+ COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
5
+ [ -z "$COMMAND" ] && exit 0
6
+ echo "$COMMAND" | grep -qE '^\s*git\s+commit' || exit 0
7
+ TODOS=$(git diff --cached 2>/dev/null | grep -cE '^\+.*\b(TODO|FIXME|HACK|XXX)\b' || echo 0)
8
+ if [ "$TODOS" -gt 0 ]; then
9
+ echo "WARNING: $TODOS TODO/FIXME/HACK markers in staged changes." >&2
10
+ echo "Resolve them before shipping, or document why they're needed." >&2
11
+ fi
12
+ exit 0
@@ -0,0 +1,9 @@
1
+ INPUT=$(cat)
2
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
4
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
5
+ [ -z "$CONTENT" ] && exit 0
6
+ if echo "$CONTENT" | grep -qE '(from\s+\S+\s+import\s+\*|import\s+\*\s+from)'; then
7
+ echo "WARNING: Wildcard import detected. Import specific names." >&2
8
+ fi
9
+ exit 0
@@ -0,0 +1,11 @@
1
+ INPUT=$(cat)
2
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
4
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
5
+ [ -z "$COMMAND" ] && exit 0
6
+ if echo "$COMMAND" | grep -qE '\bgh\s+pr\s+create\b'; then
7
+ if ! echo "$COMMAND" | grep -qE '\-\-body|\-b\s'; then
8
+ echo "WARNING: PR created without --body description." >&2
9
+ fi
10
+ fi
11
+ exit 0
@@ -0,0 +1,15 @@
1
+ INPUT=$(cat)
2
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
3
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null)
4
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
5
+ STATE="/tmp/cc-rate-limit-$(echo "$PWD" | md5sum | cut -c1-8)"
6
+ NOW=$(date +%s)
7
+ if [ -f "$STATE" ]; then
8
+ LAST=$(cat "$STATE")
9
+ DIFF=$((NOW - LAST))
10
+ if [ "$DIFF" -lt 1 ]; then
11
+ echo "WARNING: Rapid tool calls (${DIFF}s apart). Slow down." >&2
12
+ fi
13
+ fi
14
+ echo "$NOW" > "$STATE"
15
+ exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-safe-setup",
3
- "version": "8.0.0",
3
+ "version": "8.1.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": {