cc-safe-setup 28.4.3 → 28.4.5

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.
@@ -0,0 +1,45 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # hook-permission-fixer.sh — Auto-fix missing execute permissions on hooks
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # Claude Code's plugin manager and some extraction tools strip
7
+ # execute permissions from shell scripts. This hook runs at session
8
+ # start and ensures all .sh files in the hooks directory are executable.
9
+ #
10
+ # Without this, hooks fail with "Permission denied" errors.
11
+ # See: github.com/anthropics/claude-code/issues/38901
12
+ #
13
+ # TRIGGER: SessionStart MATCHER: ""
14
+ # ================================================================
15
+
16
+ HOOKS_DIR="$HOME/.claude/hooks"
17
+ PLUGINS_DIR="$HOME/.claude/plugins"
18
+ FIXED=0
19
+
20
+ # Fix hooks directory
21
+ if [ -d "$HOOKS_DIR" ]; then
22
+ for f in "$HOOKS_DIR"/*.sh; do
23
+ [ -f "$f" ] || continue
24
+ if [ ! -x "$f" ]; then
25
+ chmod +x "$f"
26
+ FIXED=$((FIXED + 1))
27
+ fi
28
+ done
29
+ fi
30
+
31
+ # Fix plugin hooks
32
+ if [ -d "$PLUGINS_DIR" ]; then
33
+ while IFS= read -r -d '' f; do
34
+ if [ ! -x "$f" ]; then
35
+ chmod +x "$f"
36
+ FIXED=$((FIXED + 1))
37
+ fi
38
+ done < <(find "$PLUGINS_DIR" -name "*.sh" -print0 2>/dev/null)
39
+ fi
40
+
41
+ if [ "$FIXED" -gt 0 ]; then
42
+ echo "Fixed execute permissions on $FIXED hook script(s)" >&2
43
+ fi
44
+
45
+ exit 0
@@ -0,0 +1,43 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # response-budget-guard.sh — Track and limit tool calls per response
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # Prevents runaway tool call loops where Claude calls hundreds of
7
+ # tools in a single response. Common in autonomous mode when the
8
+ # agent enters a retry loop or tries to brute-force a solution.
9
+ #
10
+ # Tracks tool calls per response cycle and warns after threshold.
11
+ #
12
+ # TRIGGER: PreToolUse MATCHER: ""
13
+ #
14
+ # CONFIG:
15
+ # CC_RESPONSE_TOOL_LIMIT=50 (warn after this many tool calls)
16
+ # ================================================================
17
+
18
+ LIMIT="${CC_RESPONSE_TOOL_LIMIT:-50}"
19
+ STATE="/tmp/cc-response-budget-$(echo "$PWD" | md5sum | cut -c1-8)"
20
+
21
+ # Read current count
22
+ COUNT=0
23
+ if [ -f "$STATE" ]; then
24
+ COUNT=$(cat "$STATE")
25
+ fi
26
+
27
+ COUNT=$((COUNT + 1))
28
+ echo "$COUNT" > "$STATE"
29
+
30
+ if [ "$COUNT" -eq "$LIMIT" ]; then
31
+ echo "WARNING: $COUNT tool calls in this response cycle." >&2
32
+ echo "Consider whether you're in a retry loop." >&2
33
+ echo "Reset: rm $STATE" >&2
34
+ fi
35
+
36
+ # Hard block at 2x limit to prevent truly runaway sessions
37
+ if [ "$COUNT" -gt $((LIMIT * 2)) ]; then
38
+ echo "BLOCKED: $COUNT tool calls exceeds safety limit ($((LIMIT * 2)))." >&2
39
+ echo "You appear to be in a loop. Stop and reassess." >&2
40
+ exit 2
41
+ fi
42
+
43
+ exit 0
package/index.mjs CHANGED
@@ -405,6 +405,8 @@ function examples() {
405
405
  'pip-venv-guard.sh': 'Warn on pip install outside venv',
406
406
  'no-git-amend-push.sh': 'Warn on amending pushed commits',
407
407
  'typosquat-guard.sh': 'Detect npm/pip typosquatting attacks',
408
+ 'hook-permission-fixer.sh': 'Auto-fix missing execute permissions on hooks (SessionStart)',
409
+ 'response-budget-guard.sh': 'Track and limit tool calls per response (anti-loop)',
408
410
  },
409
411
  'Auto-Approve': {
410
412
  'auto-approve-build.sh': 'Auto-approve npm/yarn/cargo/go build, test, lint',
@@ -1573,7 +1575,7 @@ async function guard(description) {
1573
1575
  if (!settings.hooks) settings.hooks = {};
1574
1576
  if (!settings.hooks[trigger]) settings.hooks[trigger] = [];
1575
1577
 
1576
- const cmd = `bash ${hookPath}`;
1578
+ const cmd = `bash ${toBashPath(hookPath)}`;
1577
1579
  const alreadyExists = JSON.stringify(settings.hooks).includes(hookName);
1578
1580
  if (!alreadyExists) {
1579
1581
  const existing = settings.hooks[trigger].find(e => e.matcher === matcher);
@@ -2435,7 +2437,7 @@ async function shield() {
2435
2437
 
2436
2438
  for (const f of hookFiles) {
2437
2439
  const content = readFileSync(join(HOOKS_DIR, f), 'utf-8');
2438
- const cmd = `bash ${join(HOOKS_DIR, f)}`;
2440
+ const cmd = `bash ${toBashPath(join(HOOKS_DIR, f))}`;
2439
2441
 
2440
2442
  // Check if already in settings
2441
2443
  const alreadyConfigured = JSON.stringify(settings.hooks).includes(f);
@@ -4557,7 +4559,7 @@ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
4557
4559
  if (!settings.hooks) settings.hooks = {};
4558
4560
  if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
4559
4561
 
4560
- const hookCmd = `bash ${hookPath}`;
4562
+ const hookCmd = `bash ${toBashPath(hookPath)}`;
4561
4563
 
4562
4564
  // Check all matchers for existing compiled-rules entry
4563
4565
  let found = false;
@@ -4679,7 +4681,7 @@ exit 0
4679
4681
  if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
4680
4682
 
4681
4683
  // Register under specific matchers based on rule types (NEVER use "" matcher)
4682
- const hookCmd = `bash ${hookPath}`;
4684
+ const hookCmd = `bash ${toBashPath(hookPath)}`;
4683
4685
  const hasBlocks = rules.some(r => r.type === 'block');
4684
4686
  const hasApproves = rules.some(r => r.type === 'approve');
4685
4687
  const hasProtects = rules.some(r => r.type === 'protect');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cc-safe-setup",
3
- "version": "28.4.3",
4
- "description": "One command to make Claude Code safe. 336 hooks (8 built-in + 328 examples). 49 CLI commands. 980 tests. 5 languages.",
3
+ "version": "28.4.5",
4
+ "description": "One command to make Claude Code safe. 336 hooks (8 built-in + 330 examples). 49 CLI commands. 987 tests. 5 languages.",
5
5
  "main": "index.mjs",
6
6
  "bin": {
7
7
  "cc-safe-setup": "index.mjs"