cc-safe-setup 29.6.39 → 29.6.40
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 +15 -5
- package/TROUBLESHOOTING.md +26 -0
- package/examples/README.md +1 -1
- package/examples/activity-logger.sh +58 -0
- package/examples/allow-claude-settings.sh +3 -2
- package/examples/allow-git-hooks-dir.sh +3 -2
- package/examples/allow-protected-dirs.sh +3 -2
- package/examples/bash-heuristic-approver.sh +1 -1
- package/examples/decision-warn.sh +59 -0
- package/examples/direnv-auto-reload.sh +9 -2
- package/examples/dotenv-commit-guard.sh +11 -5
- package/examples/proof-log-session.sh +62 -0
- package/index.mjs +16 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/cc-safe-setup)
|
|
5
5
|
[](https://github.com/yurukusa/cc-safe-setup/actions/workflows/test.yml)
|
|
6
6
|
|
|
7
|
-
**One command to make Claude Code safe for autonomous operation.**
|
|
7
|
+
**One command to make Claude Code safe for autonomous operation.** 653 example hooks · 9,200+ tests · 1,200+ installs/week · [日本語](docs/README.ja.md)
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npx cc-safe-setup
|
|
@@ -14,7 +14,7 @@ Installs 8 safety hooks in ~10 seconds. Blocks `rm -rf /`, prevents pushes to ma
|
|
|
14
14
|
|
|
15
15
|
> **What's a hook?** A checkpoint that runs before Claude executes a command. Like airport security — it inspects what's about to happen and blocks anything dangerous before it reaches the gate.
|
|
16
16
|
|
|
17
|
-
[**Getting Started**](https://yurukusa.github.io/cc-safe-setup/getting-started.html) · [**All Tools**](https://yurukusa.github.io/cc-safe-setup/hub.html) · [**Recipes**](https://yurukusa.github.io/cc-safe-setup/recipes.html) · [Validate your settings.json](https://yurukusa.github.io/cc-safe-setup/validator.html) · [**Check your score**](https://yurukusa.github.io/cc-health-check/) (`npx cc-health-check`)
|
|
17
|
+
[**Getting Started**](https://yurukusa.github.io/cc-safe-setup/getting-started.html) · [**Hook Selector**](https://yurukusa.github.io/cc-safe-setup/hook-selector.html) · [**All Tools**](https://yurukusa.github.io/cc-safe-setup/hub.html) · [**Recipes**](https://yurukusa.github.io/cc-safe-setup/recipes.html) · [Validate your settings.json](https://yurukusa.github.io/cc-safe-setup/validator.html) · [**Check your score**](https://yurukusa.github.io/cc-health-check/) (`npx cc-health-check`)
|
|
18
18
|
|
|
19
19
|
```
|
|
20
20
|
cc-safe-setup
|
|
@@ -29,6 +29,8 @@ Installs 8 safety hooks in ~10 seconds. Blocks `rm -rf /`, prevents pushes to ma
|
|
|
29
29
|
✗ API keys committed to public repos via git add .
|
|
30
30
|
✗ Syntax errors cascading through 30+ files
|
|
31
31
|
✗ Sessions losing all context with no warning
|
|
32
|
+
✗ CLAUDE.md rules silently ignored after context compaction
|
|
33
|
+
✗ Subagents ignoring all CLAUDE.md rules since v2.1.84 (#40459)
|
|
32
34
|
|
|
33
35
|
Hooks to install:
|
|
34
36
|
|
|
@@ -47,12 +49,16 @@ Installs 8 safety hooks in ~10 seconds. Blocks `rm -rf /`, prevents pushes to ma
|
|
|
47
49
|
|
|
48
50
|
## Why This Exists
|
|
49
51
|
|
|
50
|
-
A Claude Code user [lost their entire C:\Users directory](https://github.com/anthropics/claude-code/issues/36339) when `rm -rf` followed NTFS junctions. Another [lost all source code](https://github.com/anthropics/claude-code/issues/37331) when Claude ran `Remove-Item -Recurse -Force *` on a repo. Others had untested code pushed to main at 3am. API keys got committed via `git add .`. Syntax errors cascaded through 30+ files before anyone noticed.
|
|
52
|
+
A Claude Code user [lost their entire C:\Users directory](https://github.com/anthropics/claude-code/issues/36339) when `rm -rf` followed NTFS junctions. Another [lost all source code](https://github.com/anthropics/claude-code/issues/37331) when Claude ran `Remove-Item -Recurse -Force *` on a repo. Others had untested code pushed to main at 3am. API keys got committed via `git add .`. Syntax errors cascaded through 30+ files before anyone noticed. And [CLAUDE.md rules get silently dropped](https://github.com/anthropics/claude-code/issues/6354) after context compaction — your instructions vanish mid-session.
|
|
53
|
+
|
|
54
|
+
One user [analyzed 6,852 sessions](https://github.com/anthropics/claude-code/issues/42796) and found the Read:Edit ratio dropped from 6.6 to 2.0 — Claude editing files it never read jumped from 6% to 34%. That issue has over 1,000 reactions. The `read-before-edit` example hook catches this pattern before damage happens.
|
|
51
55
|
|
|
52
56
|
Claude Code ships with no safety hooks by default. This tool fixes that.
|
|
53
57
|
|
|
54
58
|
**Works with Auto Mode.** Claude Code's [Auto Mode sandboxing](https://www.anthropic.com/engineering/claude-code-sandboxing) provides container-level isolation. cc-safe-setup adds process-level hooks as defense-in-depth — catching destructive commands even outside sandboxed environments.
|
|
55
59
|
|
|
60
|
+
**Works with subagents.** Since v2.1.84, subagents and teammates [don't receive CLAUDE.md](https://github.com/anthropics/claude-code/issues/40459) — your project rules are silently skipped. Hooks operate at the process level, but [subagent tool calls may bypass PreToolUse hooks](https://github.com/anthropics/claude-code/issues/21460) in some configurations. As defense-in-depth, cc-safe-setup installs hooks at the user level (`~/.claude/settings.json`). The `subagent-claudemd-inject` example hook re-injects critical rules into subagent prompts.
|
|
61
|
+
|
|
56
62
|
## What Gets Installed
|
|
57
63
|
|
|
58
64
|
| Hook | Prevents | Related Issues |
|
|
@@ -120,6 +126,7 @@ Guards against issues that corrupt sessions or waste tokens silently.
|
|
|
120
126
|
| `mcp-warmup-wait` | Waits for MCP servers to initialize on session start (fixes first-turn tool errors) | [#41778](https://github.com/anthropics/claude-code/issues/41778) |
|
|
121
127
|
| `pre-compact-transcript-backup` | Full JSONL backup before compaction (protects against rate-limit data loss) | [#40352](https://github.com/anthropics/claude-code/issues/40352) |
|
|
122
128
|
| `conversation-history-guard` | Blocks access to session JSONL files (prevents 20x cache poisoning) | [#40524](https://github.com/anthropics/claude-code/issues/40524) |
|
|
129
|
+
| `read-before-edit` | Warns when Edit targets a file not recently Read (Read:Edit ratio dropped 70% — [#42796](https://github.com/anthropics/claude-code/issues/42796)) | [#42796](https://github.com/anthropics/claude-code/issues/42796) |
|
|
123
130
|
| `replace-all-guard` | Warns/blocks Edit `replace_all:true` (prevents bulk data corruption) | [#41681](https://github.com/anthropics/claude-code/issues/41681) |
|
|
124
131
|
| `ripgrep-permission-fix` | Auto-fixes vendored ripgrep +x permission on start (fixes broken commands/skills) | [#41933](https://github.com/anthropics/claude-code/issues/41933) |
|
|
125
132
|
|
|
@@ -146,7 +153,7 @@ Guards against issues that corrupt sessions or waste tokens silently.
|
|
|
146
153
|
| `--scan [--apply]` | Tech stack detection |
|
|
147
154
|
| `--export / --import` | Team config sharing |
|
|
148
155
|
| `--verify` | Test each hook |
|
|
149
|
-
| `--install-example <name>` | Install from
|
|
156
|
+
| `--install-example <name>` | Install from 653 examples |
|
|
150
157
|
| `--examples [filter]` | Browse examples by keyword |
|
|
151
158
|
| `--full` | All-in-one setup |
|
|
152
159
|
| `--status` | Check installed hooks |
|
|
@@ -245,7 +252,10 @@ Safe to run multiple times. Existing settings are preserved. A backup is created
|
|
|
245
252
|
|
|
246
253
|
**Note:** Hooks are skipped when Claude Code runs with `--bare` or `--dangerously-skip-permissions`. These modes bypass all safety hooks by design.
|
|
247
254
|
|
|
248
|
-
**Known
|
|
255
|
+
**Known limitations:**
|
|
256
|
+
|
|
257
|
+
- In headless mode (`-p` / `--print`), hook exit code 2 may not block tool execution ([#36071](https://github.com/anthropics/claude-code/issues/36071)). For CI pipelines, use interactive mode with hooks rather than `-p` mode.
|
|
258
|
+
- `FileChanged` notifications inject file contents into model context **before** hooks can intervene. If a sensitive file (`.env`, `credentials.json`) is modified externally during a session, its contents may appear in the conversation transcript regardless of hooks ([#44909](https://github.com/anthropics/claude-code/issues/44909)). Mitigation: use `dotenv-watch` to get alerted, and avoid editing sensitive files while Claude Code is running.
|
|
249
259
|
|
|
250
260
|
## Before / After
|
|
251
261
|
|
package/TROUBLESHOOTING.md
CHANGED
|
@@ -335,6 +335,32 @@ This prevents `ToolSearch` deferred loading and preserves the cache prefix acros
|
|
|
335
335
|
|
|
336
336
|
**Related issues**: [#41249](https://github.com/anthropics/claude-code/issues/41249), [#41788](https://github.com/anthropics/claude-code/issues/41788), [#38335](https://github.com/anthropics/claude-code/issues/38335), [#40524](https://github.com/anthropics/claude-code/issues/40524), [#41617](https://github.com/anthropics/claude-code/issues/41617)
|
|
337
337
|
|
|
338
|
+
## Multiple Hook Sources: stdin Race Condition
|
|
339
|
+
|
|
340
|
+
**Symptom**: Safety hooks appear installed but don't block dangerous commands. No errors, no warnings — hooks just silently allow everything.
|
|
341
|
+
|
|
342
|
+
**Root cause**: When multiple `PreToolUse` hooks match the same tool (e.g., two hooks both matching `Bash`), only the first hook receives stdin. The second hook gets empty input, all guard conditions fail, and it exits 0 (allow). This is an upstream Claude Code bug ([#42702](https://github.com/anthropics/claude-code/issues/42702)).
|
|
343
|
+
|
|
344
|
+
**When this happens**:
|
|
345
|
+
- cc-safe-setup hooks + another hook provider (e.g., project-level `.claude/settings.json` hooks)
|
|
346
|
+
- cc-safe-setup hooks + manually added hooks in `~/.claude/settings.json` that match the same trigger
|
|
347
|
+
|
|
348
|
+
**When this does NOT happen**:
|
|
349
|
+
- cc-safe-setup is the only hook source (default install)
|
|
350
|
+
|
|
351
|
+
**How to verify your hooks receive input**:
|
|
352
|
+
|
|
353
|
+
Add a temporary debug line to the top of a hook:
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
INPUT=$(cat)
|
|
357
|
+
echo "DEBUG: input length = ${#INPUT}" >&2
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
If you see `input length = 0`, that hook is not receiving stdin.
|
|
361
|
+
|
|
362
|
+
**Workaround**: Ensure only one hook source matches each trigger+matcher combination. If you need multiple hooks on the same trigger, combine them into a single script.
|
|
363
|
+
|
|
338
364
|
## Still Stuck?
|
|
339
365
|
|
|
340
366
|
1. Wrap the hook with debug wrapper: `npx cc-safe-setup --install-example hook-debug-wrapper`
|
package/examples/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Example Hooks
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
658 installable hooks. Each solves a real problem from GitHub Issues or autonomous operation. 9,200+ tests.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npx cc-safe-setup --install-example <name> # install one
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# activity-logger.sh — Log all tool uses to JSONL for audit and debugging
|
|
3
|
+
#
|
|
4
|
+
# Solves: "What did Claude do overnight?" — no activity trail after long sessions
|
|
5
|
+
# Also useful for: error tracking, cost analysis, compliance auditing
|
|
6
|
+
#
|
|
7
|
+
# Records every tool call with timestamp, tool name, and key metadata.
|
|
8
|
+
# Error patterns in Bash output are flagged for downstream guards.
|
|
9
|
+
#
|
|
10
|
+
# Usage: Add to settings.json as a PostToolUse hook
|
|
11
|
+
#
|
|
12
|
+
# {
|
|
13
|
+
# "hooks": {
|
|
14
|
+
# "PostToolUse": [{
|
|
15
|
+
# "matcher": "",
|
|
16
|
+
# "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/activity-logger.sh" }]
|
|
17
|
+
# }]
|
|
18
|
+
# }
|
|
19
|
+
# }
|
|
20
|
+
#
|
|
21
|
+
# Output: ~/.claude/activity-log.jsonl
|
|
22
|
+
# Each line is a JSON object with ts, tool, and tool-specific fields.
|
|
23
|
+
|
|
24
|
+
set -u
|
|
25
|
+
|
|
26
|
+
INPUT=$(cat)
|
|
27
|
+
TOOL=$(printf '%s' "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
28
|
+
[ -z "$TOOL" ] && exit 0
|
|
29
|
+
|
|
30
|
+
LOG_FILE="${HOME}/.claude/activity-log.jsonl"
|
|
31
|
+
TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
32
|
+
|
|
33
|
+
case "$TOOL" in
|
|
34
|
+
Edit|Write)
|
|
35
|
+
FILE=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
36
|
+
printf '{"ts":"%s","tool":"%s","file":"%s"}\n' "$TS" "$TOOL" "$FILE" >> "$LOG_FILE"
|
|
37
|
+
;;
|
|
38
|
+
Bash)
|
|
39
|
+
CMD=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null | head -c 200)
|
|
40
|
+
STDOUT=$(printf '%s' "$INPUT" | jq -r '.stdout // empty' 2>/dev/null | head -c 500)
|
|
41
|
+
EXIT_CODE=$(printf '%s' "$INPUT" | jq -r '.tool_result.exit_code // "0"' 2>/dev/null)
|
|
42
|
+
ERROR_PATTERN=""
|
|
43
|
+
if echo "$STDOUT" | grep -qiE '(error|ENOENT|EACCES|EPERM|fatal|panic|segfault)'; then
|
|
44
|
+
ERROR_PATTERN=$(echo "$STDOUT" | grep -oiE '(error|ENOENT|EACCES|EPERM|fatal|panic|segfault)' | head -1)
|
|
45
|
+
fi
|
|
46
|
+
printf '{"ts":"%s","tool":"%s","cmd":"%s","exit_code":%s,"error_pattern":"%s"}\n' \
|
|
47
|
+
"$TS" "$TOOL" "$(echo "$CMD" | tr '"' "'")" "$EXIT_CODE" "$ERROR_PATTERN" >> "$LOG_FILE"
|
|
48
|
+
;;
|
|
49
|
+
Read)
|
|
50
|
+
FILE=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
51
|
+
printf '{"ts":"%s","tool":"%s","file":"%s"}\n' "$TS" "$TOOL" "$FILE" >> "$LOG_FILE"
|
|
52
|
+
;;
|
|
53
|
+
*)
|
|
54
|
+
printf '{"ts":"%s","tool":"%s"}\n' "$TS" "$TOOL" >> "$LOG_FILE"
|
|
55
|
+
;;
|
|
56
|
+
esac
|
|
57
|
+
|
|
58
|
+
exit 0
|
|
@@ -25,8 +25,9 @@ if echo "$FILE_PATH" | grep -qE '\.claude/'; then
|
|
|
25
25
|
jq -n '{
|
|
26
26
|
hookSpecificOutput: {
|
|
27
27
|
hookEventName: "PermissionRequest",
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
decision: {
|
|
29
|
+
behavior: "allow"
|
|
30
|
+
}
|
|
30
31
|
}
|
|
31
32
|
}'
|
|
32
33
|
exit 0
|
|
@@ -21,8 +21,9 @@ if echo "$FILE_PATH" | grep -qE '\.git/hooks/[^/]+$'; then
|
|
|
21
21
|
jq -n '{
|
|
22
22
|
hookSpecificOutput: {
|
|
23
23
|
hookEventName: "PermissionRequest",
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
decision: {
|
|
25
|
+
behavior: "allow"
|
|
26
|
+
}
|
|
26
27
|
}
|
|
27
28
|
}'
|
|
28
29
|
exit 0
|
|
@@ -24,8 +24,9 @@ if echo "$FILE_PATH" | grep -qE '\.(claude|git|vscode|idea)/'; then
|
|
|
24
24
|
jq -n '{
|
|
25
25
|
hookSpecificOutput: {
|
|
26
26
|
hookEventName: "PermissionRequest",
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
decision: {
|
|
28
|
+
behavior: "allow"
|
|
29
|
+
}
|
|
29
30
|
}
|
|
30
31
|
}'
|
|
31
32
|
exit 0
|
|
@@ -51,7 +51,7 @@ SAFE_COMMANDS="git|npm|npx|bun|yarn|pnpm|docker|make|cargo|go|pip|python3|node|t
|
|
|
51
51
|
BASE_CMD=$(echo "$COMMAND" | tr '\n' ' ' | sed 's/^[[:space:]]*//' | sed 's/^[A-Z_]*=[^ ]* //' | sed 's/^cd [^&;]* *[&;]* *//' | awk '{print $1}' | sed 's|.*/||')
|
|
52
52
|
|
|
53
53
|
if echo "$BASE_CMD" | grep -qE "^($SAFE_COMMANDS)$"; then
|
|
54
|
-
|
|
54
|
+
jq -n '{"hookSpecificOutput":{"hookEventName":"PermissionRequest","decision":{"behavior":"allow"}}}'
|
|
55
55
|
exit 0
|
|
56
56
|
fi
|
|
57
57
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# decision-warn.sh — Warn and log irreversible operations
|
|
3
|
+
#
|
|
4
|
+
# Solves: "Why did Claude push to production?" — no decision trail for critical actions
|
|
5
|
+
# Detects dangerous operations (git push, rm -rf, database commands) and logs them
|
|
6
|
+
# to a decision log for post-incident analysis.
|
|
7
|
+
#
|
|
8
|
+
# Usage: Add to settings.json as a PostToolUse hook
|
|
9
|
+
#
|
|
10
|
+
# {
|
|
11
|
+
# "hooks": {
|
|
12
|
+
# "PostToolUse": [{
|
|
13
|
+
# "matcher": "Bash",
|
|
14
|
+
# "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/decision-warn.sh" }]
|
|
15
|
+
# }]
|
|
16
|
+
# }
|
|
17
|
+
# }
|
|
18
|
+
#
|
|
19
|
+
# Output: ~/.claude/decision-log.jsonl
|
|
20
|
+
# Each entry logs the command, timestamp, and detected risk category.
|
|
21
|
+
|
|
22
|
+
set -u
|
|
23
|
+
|
|
24
|
+
INPUT=$(cat)
|
|
25
|
+
TOOL=$(printf '%s' "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
26
|
+
[ "$TOOL" = "Bash" ] || exit 0
|
|
27
|
+
|
|
28
|
+
CMD=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
29
|
+
[ -z "$CMD" ] && exit 0
|
|
30
|
+
|
|
31
|
+
LOG_FILE="${HOME}/.claude/decision-log.jsonl"
|
|
32
|
+
TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
33
|
+
|
|
34
|
+
RISK=""
|
|
35
|
+
|
|
36
|
+
# Detect irreversible operations
|
|
37
|
+
if echo "$CMD" | grep -qE 'git\s+push'; then
|
|
38
|
+
RISK="git-push"
|
|
39
|
+
elif echo "$CMD" | grep -qE 'git\s+reset\s+--hard'; then
|
|
40
|
+
RISK="git-reset-hard"
|
|
41
|
+
elif echo "$CMD" | grep -qE 'rm\s+(-rf|--recursive)'; then
|
|
42
|
+
RISK="destructive-delete"
|
|
43
|
+
elif echo "$CMD" | grep -qiE '(DROP\s+(TABLE|DATABASE)|TRUNCATE|DELETE\s+FROM)'; then
|
|
44
|
+
RISK="database-destructive"
|
|
45
|
+
elif echo "$CMD" | grep -qE 'npm\s+publish'; then
|
|
46
|
+
RISK="npm-publish"
|
|
47
|
+
elif echo "$CMD" | grep -qE 'curl\s+.*-X\s*(DELETE|PUT|POST)'; then
|
|
48
|
+
RISK="api-mutation"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
[ -z "$RISK" ] && exit 0
|
|
52
|
+
|
|
53
|
+
printf '{"ts":"%s","risk":"%s","cmd":"%s"}\n' \
|
|
54
|
+
"$TS" "$RISK" "$(echo "$CMD" | head -c 200 | tr '"' "'")" >> "$LOG_FILE"
|
|
55
|
+
|
|
56
|
+
echo "[DECISION] $RISK: $(echo "$CMD" | head -c 100)" >&2
|
|
57
|
+
echo " → Logged to: $LOG_FILE" >&2
|
|
58
|
+
|
|
59
|
+
exit 0
|
|
@@ -24,8 +24,15 @@ OLD_CWD=$(echo "$INPUT" | jq -r '.old_cwd // empty' 2>/dev/null)
|
|
|
24
24
|
if [ -f "${NEW_CWD}/.envrc" ]; then
|
|
25
25
|
echo "📂 Directory changed: found .envrc in ${NEW_CWD}" >&2
|
|
26
26
|
if command -v direnv &>/dev/null; then
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
# Write exports to CLAUDE_ENV_FILE so Claude Code picks them up
|
|
28
|
+
# (eval in a subshell would be lost — CLAUDE_ENV_FILE persists to BashTool)
|
|
29
|
+
if [ -n "$CLAUDE_ENV_FILE" ]; then
|
|
30
|
+
echo " direnv: auto-allowing and writing to CLAUDE_ENV_FILE" >&2
|
|
31
|
+
cd "$NEW_CWD" && direnv allow . 2>/dev/null && \
|
|
32
|
+
direnv export bash > "$CLAUDE_ENV_FILE" 2>/dev/null
|
|
33
|
+
else
|
|
34
|
+
echo " ⚠ CLAUDE_ENV_FILE not set — direnv exports won't persist" >&2
|
|
35
|
+
fi
|
|
29
36
|
else
|
|
30
37
|
echo " ⚠ direnv not installed — .envrc found but not loaded" >&2
|
|
31
38
|
fi
|
|
@@ -28,15 +28,21 @@ if echo "$COMMAND" | grep -qE 'git\s+add\s+.*\.env'; then
|
|
|
28
28
|
exit 2
|
|
29
29
|
fi
|
|
30
30
|
|
|
31
|
-
# Check for git add -
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
# Check for git add -f/--force (bypasses .gitignore — can stage secrets)
|
|
32
|
+
# GitHub Issue anthropics/claude-code#44730: auto-mode used git add -f to
|
|
33
|
+
# force-add .gitignore'd secret files, exposing credentials in a commit.
|
|
34
|
+
if echo "$COMMAND" | grep -qE 'git\s+add\s+.*(-f|--force)\b'; then
|
|
35
|
+
echo "BLOCKED: 'git add --force' bypasses .gitignore and can stage secret files." >&2
|
|
36
|
+
echo " Use 'git add <specific-file>' without --force instead." >&2
|
|
37
|
+
exit 2
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# Check for git add -A/--all that might include .env
|
|
41
|
+
if echo "$COMMAND" | grep -qE 'git\s+add\s+(-A|--all)\b'; then
|
|
34
42
|
if [ -f ".env" ] || [ -f ".env.local" ] || [ -f ".env.production" ]; then
|
|
35
|
-
# Check if .gitignore excludes it
|
|
36
43
|
if ! git check-ignore -q .env 2>/dev/null; then
|
|
37
44
|
echo "WARNING: 'git add -A' with .env file not in .gitignore." >&2
|
|
38
45
|
echo " Add .env to .gitignore before staging all files." >&2
|
|
39
|
-
# Warning only for git add -A
|
|
40
46
|
fi
|
|
41
47
|
fi
|
|
42
48
|
fi
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# proof-log-session.sh — Generate session summary on Stop event
|
|
3
|
+
#
|
|
4
|
+
# Solves: "What did the AI do last week?" — activity logs exist but are unreadable
|
|
5
|
+
# Creates a human-readable 5W1H summary from the activity log at session end.
|
|
6
|
+
#
|
|
7
|
+
# Usage: Add to settings.json as a Stop hook
|
|
8
|
+
#
|
|
9
|
+
# {
|
|
10
|
+
# "hooks": {
|
|
11
|
+
# "Stop": [{
|
|
12
|
+
# "matcher": "",
|
|
13
|
+
# "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/proof-log-session.sh" }]
|
|
14
|
+
# }]
|
|
15
|
+
# }
|
|
16
|
+
# }
|
|
17
|
+
#
|
|
18
|
+
# Output: ~/ops/proof-log/YYYY-MM-DD.md (appended)
|
|
19
|
+
# Requires: activity-logger.sh to be running as a PostToolUse hook
|
|
20
|
+
|
|
21
|
+
set -u
|
|
22
|
+
|
|
23
|
+
LOG_FILE="${HOME}/.claude/activity-log.jsonl"
|
|
24
|
+
DATE=$(date +"%Y-%m-%d")
|
|
25
|
+
PROOF_DIR="${HOME}/ops/proof-log"
|
|
26
|
+
PROOF_FILE="${PROOF_DIR}/${DATE}.md"
|
|
27
|
+
|
|
28
|
+
mkdir -p "$PROOF_DIR"
|
|
29
|
+
|
|
30
|
+
[ ! -f "$LOG_FILE" ] && exit 0
|
|
31
|
+
|
|
32
|
+
# Count today's activity
|
|
33
|
+
TODAY_START=$(date -u -d "today 00:00:00" +"%Y-%m-%dT" 2>/dev/null || date -u +"%Y-%m-%dT")
|
|
34
|
+
EDIT_COUNT=$(grep -c '"tool":"Edit"' "$LOG_FILE" 2>/dev/null) || EDIT_COUNT=0
|
|
35
|
+
WRITE_COUNT=$(grep -c '"tool":"Write"' "$LOG_FILE" 2>/dev/null) || WRITE_COUNT=0
|
|
36
|
+
BASH_COUNT=$(grep -c '"tool":"Bash"' "$LOG_FILE" 2>/dev/null) || BASH_COUNT=0
|
|
37
|
+
READ_COUNT=$(grep -c '"tool":"Read"' "$LOG_FILE" 2>/dev/null) || READ_COUNT=0
|
|
38
|
+
ERROR_COUNT=$(grep -c '"error_pattern":"[^"]*[a-zA-Z]' "$LOG_FILE" 2>/dev/null) || ERROR_COUNT=0
|
|
39
|
+
|
|
40
|
+
# Get edited files
|
|
41
|
+
FILES=$(grep '"tool":"Edit\|Write"' "$LOG_FILE" 2>/dev/null | jq -r '.file // empty' 2>/dev/null | sort -u | head -10)
|
|
42
|
+
|
|
43
|
+
{
|
|
44
|
+
echo ""
|
|
45
|
+
echo "## Session $(date +"%H:%M")"
|
|
46
|
+
echo "- Edit: ${EDIT_COUNT}, Write: ${WRITE_COUNT}, Bash: ${BASH_COUNT}, Read: ${READ_COUNT}"
|
|
47
|
+
[ "$ERROR_COUNT" -gt 0 ] && echo "- Errors detected: ${ERROR_COUNT}"
|
|
48
|
+
if [ -n "$FILES" ]; then
|
|
49
|
+
echo "- Files touched:"
|
|
50
|
+
echo "$FILES" | while read -r f; do
|
|
51
|
+
[ -n "$f" ] && echo " - $f"
|
|
52
|
+
done
|
|
53
|
+
fi
|
|
54
|
+
} >> "$PROOF_FILE"
|
|
55
|
+
|
|
56
|
+
# Rotate activity log (keep last 1000 lines)
|
|
57
|
+
LINE_COUNT=$(wc -l < "$LOG_FILE" 2>/dev/null) || LINE_COUNT=0
|
|
58
|
+
if [ "$LINE_COUNT" -gt 1000 ]; then
|
|
59
|
+
tail -1000 "$LOG_FILE" > "${LOG_FILE}.tmp" && mv "${LOG_FILE}.tmp" "$LOG_FILE"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
exit 0
|
package/index.mjs
CHANGED
|
@@ -690,6 +690,14 @@ function examples() {
|
|
|
690
690
|
'write-overwrite-confirm.sh': 'Warn when Write tool overwrites large files',
|
|
691
691
|
'write-secret-guard.sh': 'Block secrets from being written to files',
|
|
692
692
|
'write-shrink-guard.sh': 'Block writes that drastically shrink files',
|
|
693
|
+
'cch-cache-guard.sh': 'Block reads of session files to prevent cache poisoning',
|
|
694
|
+
'conversation-history-guard.sh': 'Block modifications to conversation history',
|
|
695
|
+
'image-file-validator.sh': 'Block reads of fake image files that corrupt sessions',
|
|
696
|
+
'permission-denial-enforcer.sh': 'Block alternative write methods after permission denial',
|
|
697
|
+
'read-only-mode.sh': 'Block all file modifications and destructive commands',
|
|
698
|
+
'replace-all-guard.sh': 'Warn when Edit uses replace_all: true',
|
|
699
|
+
'task-integrity-guard.sh': 'Prevent Claude from deleting tasks to hide unfinished work',
|
|
700
|
+
'working-directory-fence.sh': 'Block file operations outside current working directory',
|
|
693
701
|
},
|
|
694
702
|
'Auto-Approve': {
|
|
695
703
|
'allow-claude-settings.sh': 'PermissionRequest hook',
|
|
@@ -927,6 +935,9 @@ function examples() {
|
|
|
927
935
|
'tool-call-rate-limiter.sh': 'Prevent runaway tool calls',
|
|
928
936
|
'tool-file-logger.sh': 'Log file paths from Read/Write/Edit to stderr',
|
|
929
937
|
'usage-cache-local.sh': 'Cache usage info locally to avoid API calls',
|
|
938
|
+
'compact-alert-notification.sh': 'Warn when context compaction is imminent',
|
|
939
|
+
'prompt-usage-logger.sh': 'Log every prompt with timestamps',
|
|
940
|
+
'subagent-error-detector.sh': 'Detect failed subagent results',
|
|
930
941
|
},
|
|
931
942
|
'Recovery': {
|
|
932
943
|
'auto-checkpoint.sh': 'Auto-commit after every edit for rollback protection',
|
|
@@ -956,6 +967,10 @@ function examples() {
|
|
|
956
967
|
'session-summary.sh': 'Session Summary',
|
|
957
968
|
'settings-auto-backup.sh': 'Auto-backup settings on session start',
|
|
958
969
|
'terminal-state-restore.sh': 'terminal-state-restore — restore terminal to clean state on session exit',
|
|
970
|
+
'pre-compact-transcript-backup.sh': 'Backup transcript before compaction',
|
|
971
|
+
'ripgrep-permission-fix.sh': 'Auto-fix ripgrep execute permission on session start',
|
|
972
|
+
'session-backup-on-start.sh': 'Backup session JSONL files on start',
|
|
973
|
+
'session-index-repair.sh': 'Rebuild sessions-index.json on exit',
|
|
959
974
|
},
|
|
960
975
|
'UX': {
|
|
961
976
|
'auto-answer-question.sh': 'Auto-answer AskUserQuestion for headless/autonomous mode',
|
|
@@ -1028,6 +1043,7 @@ function examples() {
|
|
|
1028
1043
|
},
|
|
1029
1044
|
'Other': {
|
|
1030
1045
|
'token-spike-alert.sh': 'Alert on abnormal token consumption per turn',
|
|
1046
|
+
'mcp-warmup-wait.sh': 'Wait for MCP servers to be ready on session start',
|
|
1031
1047
|
},
|
|
1032
1048
|
};
|
|
1033
1049
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-safe-setup",
|
|
3
|
-
"version": "29.6.
|
|
3
|
+
"version": "29.6.40",
|
|
4
4
|
"description": "One command to make Claude Code safe. 650 example hooks + 8 built-in. 56 CLI commands. Token consumption diagnosis. Works with Auto Mode.",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"bin": {
|