cc-safe-setup 7.6.0 → 7.8.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 +
|
|
9
|
+
8 built-in + 75 examples = **83 hooks**. 28 CLI commands. 420 tests. [Web Tool](https://yurukusa.github.io/cc-safe-setup/) · [Hooks Cheat Sheet](https://yurukusa.github.io/cc-safe-setup/hooks-cheatsheet.html) · [Playground](https://yurukusa.github.io/cc-hook-registry/playground.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
|
|
90
|
+
| `--install-example <name>` | Install from 75 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,49 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# error-memory-guard.sh — Detect repeated failed commands
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Claude often retries the exact same command that just failed,
|
|
7
|
+
# sometimes 5-10 times before trying a different approach.
|
|
8
|
+
# This hook tracks command+error pairs and blocks retries of
|
|
9
|
+
# commands that have already failed with the same error.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PostToolUse MATCHER: "Bash"
|
|
12
|
+
#
|
|
13
|
+
# Unlike loop-detector (which catches any repetition), this
|
|
14
|
+
# specifically targets the "same command, same error" pattern.
|
|
15
|
+
# ================================================================
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
19
|
+
[ -z "$COMMAND" ] && exit 0
|
|
20
|
+
|
|
21
|
+
EXIT_CODE=$(echo "$INPUT" | jq -r '.tool_result_exit_code // 0' 2>/dev/null)
|
|
22
|
+
ERROR=$(echo "$INPUT" | jq -r '.tool_result // empty' 2>/dev/null | tail -5)
|
|
23
|
+
|
|
24
|
+
# Only track failures
|
|
25
|
+
[ "$EXIT_CODE" = "0" ] && exit 0
|
|
26
|
+
|
|
27
|
+
STATE="/tmp/cc-error-memory-$(echo "$PWD" | md5sum | cut -c1-8)"
|
|
28
|
+
HASH=$(echo "$COMMAND" | md5sum | cut -c1-16)
|
|
29
|
+
|
|
30
|
+
# Check if this exact command already failed
|
|
31
|
+
if grep -q "^$HASH:" "$STATE" 2>/dev/null; then
|
|
32
|
+
PREV_COUNT=$(grep "^$HASH:" "$STATE" | cut -d: -f2)
|
|
33
|
+
NEW_COUNT=$((PREV_COUNT + 1))
|
|
34
|
+
sed -i "s/^$HASH:.*/$HASH:$NEW_COUNT/" "$STATE"
|
|
35
|
+
|
|
36
|
+
if [ "$NEW_COUNT" -ge 3 ]; then
|
|
37
|
+
echo "BLOCKED: This command has failed $NEW_COUNT times with the same error." >&2
|
|
38
|
+
echo "Try a different approach instead of retrying." >&2
|
|
39
|
+
echo "Command: $(echo "$COMMAND" | head -c 80)" >&2
|
|
40
|
+
echo "Reset: rm $STATE" >&2
|
|
41
|
+
exit 2
|
|
42
|
+
elif [ "$NEW_COUNT" -ge 2 ]; then
|
|
43
|
+
echo "WARNING: Command failed $NEW_COUNT times. Consider a different approach." >&2
|
|
44
|
+
fi
|
|
45
|
+
else
|
|
46
|
+
echo "$HASH:1" >> "$STATE"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
exit 0
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# large-read-guard.sh — Warn before reading large files
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Claude sometimes cats entire log files, database dumps, or
|
|
7
|
+
# minified bundles into context, wasting tokens and accelerating
|
|
8
|
+
# context exhaustion. This hook warns before reading files larger
|
|
9
|
+
# than a threshold.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
12
|
+
#
|
|
13
|
+
# CONFIG:
|
|
14
|
+
# CC_MAX_READ_KB=100 (warn above 100KB)
|
|
15
|
+
# ================================================================
|
|
16
|
+
|
|
17
|
+
COMMAND=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
18
|
+
[ -z "$COMMAND" ] && exit 0
|
|
19
|
+
|
|
20
|
+
MAX_KB="${CC_MAX_READ_KB:-100}"
|
|
21
|
+
|
|
22
|
+
# Detect file-reading commands
|
|
23
|
+
FILE=""
|
|
24
|
+
if echo "$COMMAND" | grep -qE '^\s*cat\s+'; then
|
|
25
|
+
FILE=$(echo "$COMMAND" | grep -oE 'cat\s+([^ |>]+)' | awk '{print $2}')
|
|
26
|
+
elif echo "$COMMAND" | grep -qE '^\s*less\s+|^\s*more\s+'; then
|
|
27
|
+
FILE=$(echo "$COMMAND" | awk '{print $2}')
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
[ -z "$FILE" ] && exit 0
|
|
31
|
+
[ ! -f "$FILE" ] && exit 0
|
|
32
|
+
|
|
33
|
+
# Check file size
|
|
34
|
+
SIZE_KB=$(du -k "$FILE" 2>/dev/null | cut -f1)
|
|
35
|
+
[ -z "$SIZE_KB" ] && exit 0
|
|
36
|
+
|
|
37
|
+
if [ "$SIZE_KB" -gt "$MAX_KB" ]; then
|
|
38
|
+
LINES=$(wc -l < "$FILE" 2>/dev/null || echo "?")
|
|
39
|
+
echo "WARNING: $FILE is ${SIZE_KB}KB ($LINES lines)." >&2
|
|
40
|
+
echo "Reading large files wastes context tokens." >&2
|
|
41
|
+
echo "Consider: head -100 $FILE, grep pattern $FILE, or tail -50 $FILE" >&2
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
exit 0
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# parallel-edit-guard.sh — Detect concurrent edits to same file
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# When Claude uses subagents (Agent tool), multiple agents can
|
|
7
|
+
# try to edit the same file simultaneously, causing conflicts
|
|
8
|
+
# and lost changes. This hook uses lock files to detect and
|
|
9
|
+
# warn about concurrent edits.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PreToolUse MATCHER: "Edit|Write"
|
|
12
|
+
# ================================================================
|
|
13
|
+
|
|
14
|
+
INPUT=$(cat)
|
|
15
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
16
|
+
[ -z "$FILE" ] && exit 0
|
|
17
|
+
|
|
18
|
+
# Create a lock directory for tracking
|
|
19
|
+
LOCK_DIR="/tmp/cc-edit-locks"
|
|
20
|
+
mkdir -p "$LOCK_DIR"
|
|
21
|
+
|
|
22
|
+
# Normalize file path for lock name
|
|
23
|
+
LOCK_FILE="$LOCK_DIR/$(echo "$FILE" | md5sum | cut -c1-16).lock"
|
|
24
|
+
|
|
25
|
+
# Check if another process has a lock
|
|
26
|
+
if [ -f "$LOCK_FILE" ]; then
|
|
27
|
+
LOCK_AGE=$(($(date +%s) - $(stat -c %Y "$LOCK_FILE" 2>/dev/null || echo 0)))
|
|
28
|
+
LOCK_PID=$(cat "$LOCK_FILE" 2>/dev/null)
|
|
29
|
+
|
|
30
|
+
# Lock is stale if older than 30 seconds
|
|
31
|
+
if [ "$LOCK_AGE" -lt 30 ] && [ "$LOCK_PID" != "$$" ]; then
|
|
32
|
+
echo "WARNING: File $FILE may be edited by another agent." >&2
|
|
33
|
+
echo "Lock age: ${LOCK_AGE}s, PID: $LOCK_PID" >&2
|
|
34
|
+
echo "Wait for the other edit to complete." >&2
|
|
35
|
+
fi
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Set our lock
|
|
39
|
+
echo "$$" > "$LOCK_FILE"
|
|
40
|
+
|
|
41
|
+
# Clean up lock after 30s in background
|
|
42
|
+
(sleep 30 && rm -f "$LOCK_FILE") &>/dev/null &
|
|
43
|
+
|
|
44
|
+
exit 0
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# revert-helper.sh — Show revert command when session ends badly
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# When a Claude Code session ends (Stop event), check if there
|
|
7
|
+
# are uncommitted changes and show a one-line revert command.
|
|
8
|
+
# Makes it easy to undo everything Claude did if it went wrong.
|
|
9
|
+
#
|
|
10
|
+
# TRIGGER: Stop MATCHER: ""
|
|
11
|
+
# ================================================================
|
|
12
|
+
|
|
13
|
+
# Check if we're in a git repo
|
|
14
|
+
git rev-parse --git-dir &>/dev/null || exit 0
|
|
15
|
+
|
|
16
|
+
# Check for uncommitted changes
|
|
17
|
+
DIRTY=$(git status --porcelain 2>/dev/null)
|
|
18
|
+
[ -z "$DIRTY" ] && exit 0
|
|
19
|
+
|
|
20
|
+
COUNT=$(echo "$DIRTY" | wc -l)
|
|
21
|
+
LAST_COMMIT=$(git log --oneline -1 2>/dev/null | head -c 50)
|
|
22
|
+
|
|
23
|
+
echo "" >&2
|
|
24
|
+
echo "Session ended with $COUNT uncommitted change(s)." >&2
|
|
25
|
+
echo "Last commit: $LAST_COMMIT" >&2
|
|
26
|
+
echo "" >&2
|
|
27
|
+
echo "To undo all changes:" >&2
|
|
28
|
+
echo " git checkout -- . && git clean -fd" >&2
|
|
29
|
+
echo "" >&2
|
|
30
|
+
echo "To review changes:" >&2
|
|
31
|
+
echo " git diff --stat" >&2
|
|
32
|
+
|
|
33
|
+
exit 0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-safe-setup",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.8.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": {
|