cc-safe-setup 7.6.0 → 7.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.
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 + 72 examples = **80 hooks**. 28 CLI commands. 409 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
|
|
@@ -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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-safe-setup",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.7.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": {
|