"I want to try safety-check without reading my real settings"
Use this before the real audit when you want to see the checker run on your machine without touching your real Claude Code settings. It creates a temporary HOME and a temporary project, runs the bounded summary command there, and removes the empty temporary directories afterward.
Network boundary: this downloads tools/safety-check/check.sh from GitHub raw content, then runs it locally against only the temporary HOME and temporary project created below. It does not upload your Claude Code settings, hook files, shell history, repository contents, or summary output.
Fix path: run the isolated baseline, confirm the setup is unprotected, then run the real project audit
$ (
tmp_home="$(mktemp -d)"
tmp_project="$(mktemp -d)"
cleanup() {
if [ "${KEEP_BOUCLE_FIRST_TEST:-0}" != "1" ]; then
rmdir "$tmp_home" "$tmp_project" 2>/dev/null || {
printf 'Temporary directories were not empty; inspect and remove manually:\n'
printf ' %s\n %s\n' "$tmp_home" "$tmp_project"
}
fi
}
trap cleanup EXIT
cd "$tmp_project"
curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | PYTHONDONTWRITEBYTECODE=1 HOME="$tmp_home" bash -s -- --verify --summary-only
if [ "${KEEP_BOUCLE_FIRST_TEST:-0}" = "1" ]; then
printf 'Temporary HOME: %s\nTemporary project: %s\n' "$tmp_home" "$tmp_project"
fi
)
Expected shape:
--- Safety Summary (copy/paste) ---
...
Verify: not run | no hooks found | 0 payload checks
Boundary: install hooks before trusting the hook layer.
github.com/Bande-a-Bonnot/Boucle-framework
--- End Safety Summary ---
This does not install hooks and does not prove your real Claude Code setup is safe. It proves the checker can start, the support summary is bounded, and an empty temporary setup is reported as unverified instead of safe. The repository URL line is printed as support context inside the bounded block; it is not evidence that the checker uploaded output.
After the isolated test, run the real audit from the same project root where you start Claude Code:
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --summary-only
Full copy-paste guide and cleanup notes: safety-check first test.
"I want to know if my Claude Code setup is safe"
Start with the audit before installing or changing hooks. Run it from the same project root where you launch Claude Code, so user-level and project-level settings are both visible. In a git checkout, move to the repo root first; outside git, stay in the project directory you use for Claude Code.
Fix path: audit, install the essentials, doctor, verify, then restart Claude Code
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- recommended
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- doctor
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --strict
Trust the hook layer only after verification reports zero FAIL-OPEN results. The verifier sends representative Claude-style PreToolUse payloads to installed PreToolUse hooks; other hook events are inventoried and reported, but skipped for payload verification because they do not receive those tool payloads. It does not execute the dangerous shell or git commands named inside the payloads.
When asking for help, share only the bounded summary block, not raw settings files, hook source, paths, tokens, or private CLAUDE.md rules:
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --summary-only
On native Windows, use PowerShell 7 for the native hooks:
PS> $root = if (Get-Command git -ErrorAction SilentlyContinue) { git rev-parse --show-toplevel 2>$null }
PS> if ($root) { Set-Location $root }
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } recommended"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } doctor"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } verify"
After a clean verification, start a fresh Claude Code session from the same root. A running session may still have old hook settings loaded. For a longer walkthrough, use the safety-check quickstart.
"Safety-check failed. What should I fix first?"
Use this after safety-check --verify, safety-check --verify --strict, or native Windows install.ps1 verify reports warnings, skipped checks, missing hooks, invalid settings, or FAIL-OPEN. Treat the output as a repair list, not as a certificate.
Fix path: remove global hook-disabling flags, validate settings, repair hook files, then rerun verification from the same project root
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ unset IS_DEMO CLAUDE_CODE_SIMPLE
$ test ! -f ~/.claude/settings.json || python3 -m json.tool ~/.claude/settings.json >/dev/null
$ test ! -f .claude/settings.json || python3 -m json.tool .claude/settings.json >/dev/null
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- doctor
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --strict
Prioritize anything that can make all hooks disappear or fail open: IS_DEMO, CLAUDE_CODE_SIMPLE, invalid settings.json, no hooks found, no payload checks, missing or non-executable hook files, and any FAIL-OPEN payload result. Do not chase the letter grade until those are fixed.
On native Windows, use PowerShell 7 and the native verifier first. If Git Bash or WSL is available, check --verify --summary-only can also print the bounded summary block used for support.
PS> $root = if (Get-Command git -ErrorAction SilentlyContinue) { git rev-parse --show-toplevel 2>$null }
PS> if ($root) { Set-Location $root }
PS> Remove-Item Env:IS_DEMO -ErrorAction SilentlyContinue
PS> Remove-Item Env:CLAUDE_CODE_SIMPLE -ErrorAction SilentlyContinue
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } doctor"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } verify"
A useful baseline is Verify: 0 FAIL-OPEN with payload checks actually run and no skipped PreToolUse checks for hooks that enforce your boundary. After repairs pass, start a fresh Claude Code session from the same project root.
Full repair table: safety summary triage. If you need public help, share only the bounded summary from --verify --summary-only plus OS, shell, Claude Code version, where hooks are installed, and what changed recently.
"I edited Claude settings or hook files by hand"
Use this after editing ~/.claude/settings.json, project .claude/settings.json, hook commands, or hook scripts. Also use it after changing the directory where Claude Code starts. One valid hook file is not enough. A malformed sibling entry, stale settings cache, or launch from the wrong root can leave the boundary unverified.
Fix path: snapshot settings, validate both settings files, run doctor, strictly verify payloads, then start a fresh Claude Code session
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- backup
$ test ! -f ~/.claude/settings.json || python3 -m json.tool ~/.claude/settings.json >/dev/null
$ test ! -f .claude/settings.json || python3 -m json.tool .claude/settings.json >/dev/null
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- doctor
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --strict
Strict verification should show that PreToolUse payload checks ran. It should also show no FAIL-OPEN result for hooks that enforce your boundary. Treat the current session as unprotected if the report says no hooks found, no payload checks ran, invalid JSON, missing files, or skipped PreToolUse checks. Skipped non-PreToolUse events remain inventory unless no PreToolUse payload checks ran. Fix the named issue first.
On native Windows, use PowerShell 7 and verify the native hook set:
PS> $root = if (Get-Command git -ErrorAction SilentlyContinue) { git rev-parse --show-toplevel 2>$null }
PS> if ($root) { Set-Location $root }
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } backup"
PS> if (Test-Path "$HOME/.claude/settings.json") { Get-Content "$HOME/.claude/settings.json" | ConvertFrom-Json | Out-Null }
PS> if (Test-Path ".claude/settings.json") { Get-Content ".claude/settings.json" | ConvertFrom-Json | Out-Null }
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } doctor"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } verify"
After a clean check, start a fresh Claude Code session from the same root. Running sessions can keep old settings or permission state in memory even when the files on disk are correct. If the edit was only a hook test, keep the backup until the fresh session proves the boundary still works.
If verification fails, use the failed verification recipe and triage table before reinstalling repeatedly.
"I want to try the hooks without keeping them installed"
Use this when you are on a borrowed machine, a client repo, a CI runner, or any place where the hook boundary should be tested and then removed. The trial still edits user-level Claude Code settings while it is active, so snapshot first and verify both installation and cleanup.
Fix path: back up settings, install the smallest useful set, doctor, verify, optionally run one fresh Claude Code session, uninstall, then rerun the audit
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- backup
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- recommended
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- doctor
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --strict
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- uninstall all
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --summary-only
If the trial is only a configuration check, do not start Claude Code between install and uninstall. If you want to test behavior in Claude Code itself, start a fresh session from the same root after strict verification, run one bounded task, then uninstall before leaving the machine or handing the repo back.
After uninstall, the summary should no longer claim a verified hook boundary. A result such as Verify: not run, no hooks found, or 0 payload checks is expected after cleanup. That means the trial hooks are gone; it does not mean the current setup is protected.
Run backup list first, inspect the available snapshots, and restore the named backup you intend to use. Use bare restore only when the most recent backup is the exact snapshot you want back.
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- backup list
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- restore settings.20260101_120000.json
On native Windows, use the same shape with PowerShell 7 and the native verifier:
PS> $root = if (Get-Command git -ErrorAction SilentlyContinue) { git rev-parse --show-toplevel 2>$null }
PS> if ($root) { Set-Location $root }
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } backup"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } recommended"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } doctor"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } verify"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } uninstall all"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } verify"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } backup list"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } restore settings.20260101_120000.json"
For a no-install dry run before touching real settings, use the temporary first test. It uses a temporary HOME and does not prove your real setup is safe.
"I am on Windows and do not want Git Bash or WSL"
Use this path for native PowerShell hooks. It installs and verifies the recommended .ps1 hook baseline from PowerShell 7 (pwsh) without bash or jq. Use install.ps1 all when you want all seven standalone hooks. Run from the same project root where you start Claude Code so project-level settings are visible.
Fix path: use PowerShell 7, install the recommended native hooks, doctor, verify, then start a fresh Claude Code session
PS> $root = if (Get-Command git -ErrorAction SilentlyContinue) { git rev-parse --show-toplevel 2>$null }
PS> if ($root) { Set-Location $root }
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } recommended"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } doctor"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } verify"
After a clean verification, start a fresh Claude Code session from that same root. A running session may still have old hook settings loaded.
Use native verification to prove the PowerShell hooks installed under Claude Code settings block representative payloads. Use Git Bash or WSL only when you need the broader bash-based safety-check report with the bounded copy/paste summary:
PS> $root = if (Get-Command git -ErrorAction SilentlyContinue) { git rev-parse --show-toplevel 2>$null }
PS> if ($root) { Set-Location $root }
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } check --verify --summary-only"
For a temporary Windows trial, snapshot settings first and verify cleanup after removal:
PS> $root = if (Get-Command git -ErrorAction SilentlyContinue) { git rev-parse --show-toplevel 2>$null }
PS> if ($root) { Set-Location $root }
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } backup"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } recommended"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } doctor"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } verify"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } uninstall all"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } verify"
Native Windows verification is narrower than the full bash safety-check audit. It proves the native PowerShell hooks it checks; it does not prove every developer's global settings, WSL settings, Git Bash path, or checked-in bash hooks are protected.
"Claude ignores my CLAUDE.md rules"
CLAUDE.md instructions are text appended to the context window. The model reads them and usually follows them, but nothing prevents deviation. As context grows, rule-following degrades. This is documented behavior (#37599, #38065, #40425).
Fix: enforce-hooks
Tag rules in CLAUDE.md with @enforced, then install the dynamic enforce hook. It re-reads CLAUDE.md on every tool call and blocks covered violations at the tool-call level. Verify the hook after install, run the runtime smoke test, and use OS-level controls for anything outside tool calls.
# Install enforce, then ask Claude: "Enforce my CLAUDE.md rules"
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/enforce/install.sh | bash
$ python3 .claude/hooks/enforce-hooks.py --verify
$ python3 .claude/hooks/enforce-hooks.py --smoke-test
Install boundary: this downloads tools/enforce/install.sh and tools/enforce/enforce-hooks.py from GitHub raw content, then runs them locally in the current project. It may create CLAUDE.md, install .claude/hooks/enforce-hooks.py, update project .claude/settings.json, and add armor rules for the generated hook files. It does not upload CLAUDE.md, settings, hook files, shell history, repository contents, or audit output.
For audit-only sessions, use the read-only audit recipe, which includes setup, strict verification, smoke testing, fresh-session guidance, and removal steps.
Also helps: fewer rules placed more prominently, shorter sessions, and accepting that CLAUDE.md is guidance while hooks are enforcement.
"I need Claude to inspect the repo, not change it"
Use this when Claude Code should review, test, and report without writing files, mutating data, restarting services, or committing. A prompt that says "do not edit" is guidance. The enforced rule below blocks covered tool calls before they execute.
This is read-only for the audited Claude Code session after the hook is installed. Setting up the boundary intentionally edits project files first: you add an @enforced rule to CLAUDE.md and register a project-level hook in .claude/settings.json. Use a disposable branch or worktree when the main checkout must stay untouched, and keep the timestamped settings backup until the audit is done.
Fix path: add an enforced read-only rule, install the dynamic hook, verify, smoke test, then start a fresh session
## Read-only mode @enforced
- Never modify any files
- Never run rm -rf
- Never run `>`, `>>`, `tee`, `touch`, `mkdir`, `rm`, `sed -i`, `perl -pi`, `mv`, `cp`, `unlink`, `chmod`, or `chown`
- Never run ALTER, DROP, TRUNCATE, INSERT, UPDATE, or DELETE
- Never run docker restart, docker stop, docker build, or docker rm
- Never run sudo
- Never run git commit, git push, or git merge
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ mkdir -p .claude
$ backup_stamp="$(date +%Y%m%d_%H%M%S)"
$ test -f .claude/settings.json && cp -p .claude/settings.json ".claude/settings.json.read-only-${backup_stamp}.bak"
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/enforce/enforce-hooks.py -o /tmp/enforce-hooks.py
$ python3 /tmp/enforce-hooks.py CLAUDE.md --scan
$ python3 /tmp/enforce-hooks.py CLAUDE.md --install-plugin
$ python3 /tmp/enforce-hooks.py CLAUDE.md --audit --strict
$ python3 /tmp/enforce-hooks.py CLAUDE.md --verify --strict
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify
$ python3 /tmp/enforce-hooks.py CLAUDE.md --smoke-test --strict
After verification, start a fresh Claude Code session from the same project root and use a narrow prompt such as: Audit this repository. Do not edit files, run migrations, restart services, or commit. Report findings only.
To leave read-only mode, remove or rename the Read-only mode @enforced section, then verify from the same project root. Plugin mode reads CLAUDE.md dynamically, so the read-only rule stops applying on the next tool call after the section is gone.
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ python3 /tmp/enforce-hooks.py CLAUDE.md --audit --strict
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify
If you also want to remove the hook registration, restore the settings snapshot only when you want .claude/settings.json returned to its pre-audit state, or remove enforce-pretooluse.sh from Claude Code's hooks UI. Then run the checks again. A strict audit should fail if the read-only section still exists without an active hook, or if restored settings contain broken hook references.
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ ls -1t .claude/settings.json.read-only-*.bak 2>/dev/null || true
$ cp -p .claude/settings.json.read-only-20260101_120000.bak .claude/settings.json
$ python3 /tmp/enforce-hooks.py CLAUDE.md --audit --strict
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify
If there was no pre-audit settings file, remove only the temporary enforce hook entry and generated plugin files. Keep .claude/settings.json if it contains other project settings.
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ python3 - <<'PY'
import json
from pathlib import Path
path = Path(".claude/settings.json")
if not path.exists():
raise SystemExit(0)
settings = json.loads(path.read_text())
hooks = settings.get("hooks", {})
pre_tool = hooks.get("PreToolUse", [])
for entry in pre_tool:
entry["hooks"] = [
hook for hook in entry.get("hooks", [])
if not hook.get("command", "").endswith("enforce-pretooluse.sh")
]
hooks["PreToolUse"] = [
entry for entry in pre_tool
if entry.get("hooks")
]
if not hooks["PreToolUse"]:
hooks.pop("PreToolUse")
if not hooks:
settings.pop("hooks", None)
path.write_text(json.dumps(settings, indent=2) + "\n")
PY
$ rm -f .claude/hooks/enforce-hooks.py .claude/hooks/enforce-pretooluse.sh
$ rmdir .claude/hooks .claude 2>/dev/null || true
$ python3 /tmp/enforce-hooks.py CLAUDE.md --audit --strict
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify
This is not a sandbox. Hooks cover Claude Code tool calls, not every way a process can affect a system. For production data or secrets, combine this with OS permissions, backups, containers, or a disposable clone. Full copy-paste guide: read-only audit mode.
"Claude deleted/overwrote my files"
Users have lost 87GB of personal data, had 30+ files destroyed, and seen explicit "don't touch" instructions ignored. The permission system has known bypass patterns (#38119).
Fix: file-guard
Protects files matching patterns you define in .file-guard. In default write-protect mode it blocks Edit, Write, MultiEdit, NotebookEdit, and modifying Bash commands for matching paths. Put patterns under a [deny] section when Claude must not read, search, list, or reference those paths at all. Works on absolute and relative paths.
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- file-guard
# Create .file-guard in your project root:.env*.pemcredentials.*/etc/*[deny]~/.ssh/*
Also: bash-guard blocks rm -rf /, rm -rf ~, and recursive deletion of system paths, even inside compound commands and pipes.
"Claude ran a dangerous command"
Claude can run sudo, drop databases, delete Docker volumes, expose credentials via env/printenv, or modify cloud infrastructure. The built-in permission system sometimes approves these in bypass mode (#39981).
Fix: bash-guard
Blocks dangerous Bash commands before execution. Catches compound commands (cd /tmp && rm -rf /), pipes, subshells, and common evasion patterns. Covered by hundreds of bash tests, with additional PowerShell coverage when pwsh is available.
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- bash-guard
Blocked categories: recursive deletion, sudo, Docker/container commands, database drops, credential exposure, cloud infrastructure (aws/gcloud/az destructive operations), chmod 777, disk operations.
"Claude force-pushed / reset my branch / deleted commits"
Claude can run git push --force, git reset --hard, git checkout ., git clean -f, or push --delete. It can also bypass pre-commit hooks with --no-verify (#40117).
Fix: git-safe + branch-guard
git-safe blocks force pushes, hard resets, checkout ., clean -f, push --delete, and --no-verify. branch-guard prevents commits directly to main/master/production.
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- git-safe branch-guard
"Claude is about to commit or push code that may contain secrets"
A Claude Code session can bulk-stage files, commit plaintext credentials, and push before a product guardrail or PostToolUse logger can stop the side effect (#89273). Hook verification proves representative hooks answered their payloads; it does not prove the current staged content or remote destination is safe.
Fix path: inspect staged content, run an external secret scanner, verify the remote, then approve the exact commit or push
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ git status --short
$ git diff --cached --name-only
$ git diff --cached --check
$ git diff --cached -- . ':(exclude)package-lock.json' ':(exclude)yarn.lock' | sed -n '1,240p'
$ git remote -v
$ git branch --show-current
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- git-safe bash-guard session-log
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --strict
Also run the secret scanner your project already trusts, such as a pre-commit hook, CI scanner, gitleaks, trufflehog, or the host's built-in secret scanning. Keep that scanner outside Claude Code's hook layer so a broken Claude hook is not the only line of defense.
Approve only an exact action after those checks: commit message, branch, remote, and whether push is allowed. Avoid broad git add . and unattended commit/push tasks in auto-mode sessions that can touch credential files, generated config, logs, transcripts, or environment dumps.
If a secret may already have been committed or pushed, do not paste the value into chat or public issues. Stop, revoke or rotate the credential through the provider, preserve the commit SHA and affected path for private incident handling, and assume the value is exposed even if the branch is later force-pushed away.
Related known limitation: git commits can leak plaintext credentials before any built-in guard reacts.
"Nested Claude calls used API credits instead of my subscription"
Use this before letting Claude Code launch nested claude -p subprocesses from Bash, cron, launchd, CI, or an autonomous loop. A shell alias that unsets ANTHROPIC_API_KEY for manual commands does not protect noninteractive Bash subprocesses. If the parent Claude Code environment exports the key, assistant-launched subprocesses can inherit it and use API-key billing instead of subscription/OAuth auth.
Fix path: start Claude Code with billing-sensitive API keys unset, verify the warning is gone, then block or wrap nested claude -p calls before autonomous work
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ env | grep '^ANTHROPIC_API_KEY=' && printf 'API key is exported in this shell\n'
$ unset ANTHROPIC_API_KEY
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --summary-only
If the summary still reports ANTHROPIC_API_KEY is set, fix the shell, terminal profile, launchd environment, CI secret injection, or wrapper that starts Claude Code. Then start a fresh Claude Code process from that clean environment. Do not trust an already-running session to forget inherited environment variables.
If you intentionally use API-key auth for some workflows, keep it in a separate shell profile or a real wrapper executable that unsets billing-sensitive credentials before subscription/OAuth sessions can spawn nested Claude calls. An interactive alias is not enough because aliases do not expand in noninteractive Bash tool shells.
For unattended sessions, add an explicit policy around nested Claude invocations: either block claude -p with a PreToolUse Bash rule, or require a wrapper command whose source you can inspect and whose behavior is verified before the session starts. Record the intended auth path in your handoff when the next action is autonomous work, a commit, or a push.
Related known limitation: exported ANTHROPIC_API_KEY can override subscription auth in Bash-spawned Claude subprocesses.
"My hook blocks Write/Edit. Does it also block shell writes?"
Claude Code can route file changes through Bash, heredocs, sed, scripts, or PowerShell instead of the dedicated Write, Edit, MultiEdit, and NotebookEdit tools. A hook that only matches dedicated file tools will not see those shell operations. Use this canary before trusting protected-file policy in auto-mode, bypass mode, CI, or unattended sessions.
Fix path: protect the file pattern, install both file and shell guards, verify the installed hooks, then dry-run one structured write payload and one Bash write payload
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ test -f .file-guard || touch .file-guard
$ grep -qxF '.env' .file-guard || printf '%s\n' '.env' >> .file-guard
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- file-guard bash-guard
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- doctor
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --strict
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/test-hook.sh -o /tmp/test-hook.sh
$ chmod +x /tmp/test-hook.sh
$ /tmp/test-hook.sh "bash ~/.claude/file-guard/hook.sh" --tool Write --file "$repo_root/.env" --content "TOKEN=example" --expect-deny
$ /tmp/test-hook.sh "bash ~/.claude/file-guard/hook.sh" --tool Bash --command "printf 'TOKEN=example\n' > .env" --expect-deny
The last two commands send synthetic Claude-style payloads to the hook. They do not create or modify .env. The structured Write case uses an absolute path because relative Write/Edit payloads can be ambiguous; the Bash case checks that a shell redirection targeting the same protected path is blocked too.
A passing canary proves only those payload shapes against the hook command loaded from your current filesystem. Before relying on it, start a fresh Claude Code session from the same project root and keep an external fallback for secrets, generated credentials, or client data.
Related known limitation: permission modes can steer Edit and Write work through shell commands.
"Claude re-reads the same files and wastes tokens"
Claude often reads the same file multiple times in a session, each time consuming tokens for content already in context.
Fix: read-once
Tracks file reads and blocks redundant re-reads within a session. Allows re-reads when the file has changed on disk. Typical savings: 20-40% fewer tokens per session.
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- read-once
Note: if you need to Edit a file, read-once allows the re-read. It only blocks reads where the file content hasn't changed.
"Claude bypasses a denied tool by using a different tool"
When Bash(rm) is denied, Claude may use Python os.remove() instead (#39459). When a pre-commit hook blocks a commit, Claude may use --no-verify (#40117). The tool is blocked but the goal is not.
Partial fix: bash-guard + enforce-hooks
bash-guard catches common equivalences (blocks rm in both shell and Python patterns). enforce-hooks can define broader rules that span multiple tools. But goal-level enforcement is a known limitation of the hook architecture: hooks gate tool calls, not intentions.
For critical paths, combine hooks with OS-level controls (file permissions, network policy, containerization). See the full searchable list of known gaps.
"I wrote a hook and want to test it before Claude Code runs it"
Use this when a custom or third-party hook needs a repeatable dry run. The harness feeds synthetic Claude-style PreToolUse payloads to the hook command and reports whether it allows, denies, errors, or crashes. It does not require a live Claude Code session.
Fix path: download the test harness, run one allow and one deny case, then keep the command in CI for hook changes
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/test-hook.sh -o /tmp/test-hook.sh
$ chmod +x /tmp/test-hook.sh
$ /tmp/test-hook.sh "bash .claude/hooks/my-hook.sh" --command "echo hello" --expect-allow
$ /tmp/test-hook.sh "bash .claude/hooks/my-hook.sh" --command "rm -rf /" --expect-deny
For non-Bash tools, set the tool name and payload fields explicitly:
$ /tmp/test-hook.sh "bash .claude/hooks/my-hook.sh" --tool Write --file ".env" --content "SECRET=x" --expect-deny
$ /tmp/test-hook.sh "python3 .claude/hooks/my-hook.py" --tool Read --input '{"file_path":"secrets/api-key.txt"}' --expect-deny
When a hook has more than a few cases, commit a small JSONL fixture and run it in batch mode. Each line is one case with tool, input, expect, and label:
{"tool":"Bash","input":{"command":"echo hello"},"expect":"allow","label":"safe echo"}
{"tool":"Bash","input":{"command":"rm -rf /"},"expect":"deny","label":"block root delete"}
{"tool":"Write","input":{"file_path":".env","content":"SECRET=x"},"expect":"deny","label":"block secret write"}
$ /tmp/test-hook.sh "bash .claude/hooks/my-hook.sh" --batch .claude/hooks/my-hook-tests.jsonl
A passing dry run proves only the hook command's stdin behavior for those payloads. Before relying on the boundary, also verify the hook is registered in the settings file Claude Code will load, use a command-type hook for enforcement, start a fresh session, and run safety-check --verify --strict from the same project root.
For ready-made examples, see test-hook examples and the README test-hook section.
"I need to prove what Claude Code actually did"
Use this after an unattended session, a confusing change, or a disputed agent claim. session-log records every Claude Code tool call to daily JSONL files, then the repository's tools/session-log/report.sh helper summarizes calls, errors, files touched, commands run, and multi-day trends.
Fix path: install the PostToolUse logger, start a fresh session, then inspect the report before trusting claims
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- session-log
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- verify
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/session-log/report.sh | bash
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/session-log/report.sh | bash -s -- --week
The raw logs live at ~/.claude/session-logs/YYYY-MM-DD.jsonl. They are local audit records, so review them before sharing. Paths, commands, and file names may reveal private project structure or secrets in command arguments.
$ today="$(date -u +%Y-%m-%d)"
$ grep '"tool":"Bash"' ~/.claude/session-logs/"$today".jsonl
$ grep '"git push' ~/.claude/session-logs/*.jsonl
When Claude says "I pushed", "I ran tests", or "I only read files", check the log for the actual tool call and, for Bash, its recorded exit code. A claimed action without a matching log entry should be treated as unverified.
For commit or push work, also inspect the staged diff and the exact remote target before approval. Hook verification can show that local guardrails responded to representative payloads; it does not prove the current staged content is safe to publish.
On native Windows, install and verify the PowerShell hook with PowerShell 7. The native hook writes the same JSONL format, but the report helper is a Bash script in the repository, so use Git Bash or WSL for the summary report if needed.
PS> $root = if (Get-Command git -ErrorAction SilentlyContinue) { git rev-parse --show-toplevel 2>$null }
PS> if ($root) { Set-Location $root }
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } session-log"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } verify"
PS> Get-Content "$HOME/.claude/session-logs/$((Get-Date).ToUniversalTime().ToString('yyyy-MM-dd')).jsonl" | Select-Object -Last 20
The audit trail begins after installation and a fresh Claude Code session. It cannot reconstruct earlier sessions, and it records tool calls rather than model reasoning. Full documentation: session-log.
"I need help without leaking private settings"
Do not paste raw Claude Code settings, hook commands, hook stderr, transcripts, screenshots, or private CLAUDE.md rules into a public issue. The support path is to run the verifier from the affected project root and share only the bounded summary block.
Fix path: rerun from the project root, print the public summary, then add short environment context
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --summary-only
Command boundary: this downloads tools/safety-check/check.sh from GitHub raw content, then runs it locally on your current project and Claude Code settings. The checker does not upload settings.json, hook files, shell history, repository contents, session logs, or safety summary output.
Post the output from --- Safety Summary (copy/paste) --- through --- End Safety Summary ---, plus only these short fields:
OS:
Shell:
Claude Code version:
Where hooks are installed:
Command/scope: real project-root --verify --summary-only / isolated first test / native install.ps1 verify
What changed recently:
Pre-update baseline: summary/version captured / not captured / not an update issue
Fresh Claude Code session from verified root: started / not yet / not changed
MCP servers involved: none / names from claude mcp list / not sure
Next intended action: audit only / local fix / commit / push / public report
Staged diff and destination reviewed separately: yes / no / not applicable
For failures after a Claude Code update, say whether you captured the pre-update version and bounded summary from the update checklist. Do not paste both full command outputs; the key distinction is whether the new strict result is a regression from a known baseline or the first verified result for an already-untrusted setup.
For MCP-related failures, include only visible server names and connection state from claude mcp list, plus whether a fresh session can run one harmless read-only call against each critical server. Do not paste claude mcp get output for HTTP servers into public reports; headers, URLs, and command arguments can expose bearer tokens, private hosts, or local paths. A clean hook summary proves local hooks responded to representative payloads, not that a remote MCP server kept the same tool surface or instructions.
If the next action is commit, push, or public report, say whether the staged diff and destination have been reviewed separately. The safety summary is support evidence for the hook boundary, not approval to publish repository contents, logs, settings, or secrets.
If native PowerShell verification is the only available path, copy the final count line and any WARN or SKIP lines instead of the startup hook table:
PS> $root = if (Get-Command git -ErrorAction SilentlyContinue) { git rev-parse --show-toplevel 2>$null }
PS> if ($root) { Set-Location $root }
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } verify"
If someone asks for raw files, build a temporary reproduction with throwaway settings first. The safe support evidence guide and safe support examples show what is safe to paste and what to redact.
"I want CI to fail when repo hooks stop blocking"
Use this when a repository commits Claude Code hook settings or hook scripts and you want pull requests to prove that boundary still works. CI can verify checked-in project hooks. It cannot prove every developer's global ~/.claude/settings.json is safe.
Network boundary: the workflow downloads tools/safety-check/check.sh from GitHub raw content, then runs it locally on the checked-out repository and a temporary CI home directory. The checker does not upload settings.json, hook files, shell history, repository contents, or safety summary output. Treat CI logs as the only publication surface, because hook commands can print paths or stderr.
Fix path: commit repo-local settings and hook scripts, require the checked-in settings file, isolate HOME, run strict safety-check from the repository root, and skip only the optional Claude Code version probe
name: claude-code-safety
on:
pull_request:
push:
branches: [main]
jobs:
safety-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Verify Claude Code hooks
working-directory: ${{ github.workspace }}
run: |
test -f .claude/settings.json
tmp_home="$(mktemp -d)"
cleanup() {
rmdir "$tmp_home" 2>/dev/null || {
printf 'Temporary HOME was not empty; inspect and remove manually:\n'
printf ' %s\n' "$tmp_home"
}
}
trap cleanup EXIT
curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh -o /tmp/safety-check.sh
SAFETY_CHECK_SKIP_CLAUDE_VERSION=1 PYTHONDONTWRITEBYTECODE=1 HOME="$tmp_home" bash /tmp/safety-check.sh --verify --strict
The test -f .claude/settings.json line fails early when the repository policy file is absent. The temporary HOME prevents a self-hosted runner's global Claude Code settings from making the job pass when the checked-out repository has no working hook layer.
For a runnable sample, commit a minimal hook such as hooks/block-dangerous-bash.sh that reads the hook JSON from stdin and exits 2 for destructive commands:
#!/usr/bin/env bash
set -euo pipefail
payload="$(cat)"
tool="$(python3 -c 'import json,sys; print(json.load(sys.stdin).get("tool_name", ""))' <<<"$payload")"
command="$(python3 -c 'import json,sys; print(json.load(sys.stdin).get("tool_input", {}).get("command", ""))' <<<"$payload")"
if [ "$tool" = "Bash" ] && printf '%s\n' "$command" | grep -Eq '(^|[;&|[:space:]])rm[[:space:]]+-rf([[:space:]]|$)'; then
echo "Blocked destructive rm -rf command" >&2
exit 2
fi
exit 0
The cleanup uses rmdir instead of recursive deletion. If a hook, shell startup file, or unexpected tool writes into the temporary home, the path is left in the CI log for inspection.
Do not treat install.sh recommended in CI as proof that repo-local policy is protected. That installs user-level hooks under the runner's temporary HOME. It is useful as an installer smoke test, but it does not prove the checked-out .claude/settings.json or repository hook files protect developers.
For native PowerShell hook scripts, use a runner where both bash and pwsh are available so safety-check --verify --strict can execute the checked-in .ps1 commands. Do not use install.ps1 verify as proof for repo-local hooks; it verifies Boucle hooks installed under the CI user's home directory.
Use an explicit command such as pwsh -NoProfile -File ./hooks/block-dangerous-bash.ps1 in checked-in settings, and commit the PowerShell hook script beside it:
$payload = [Console]::In.ReadToEnd()
$event = $payload | ConvertFrom-Json
$tool = [string]$event.tool_name
$command = [string]$event.tool_input.command
if ($tool -eq "Bash" -and $command -match '(^|[;&|\s])rm\s+-rf(\s|$)') {
[Console]::Error.WriteLine("Blocked destructive rm -rf command")
exit 2
}
exit 0
Full workflow examples and expected failure modes: scripted safety-check checks.
"I need to leave a PR or incident handoff"
Use this when a teammate, reviewer, or incident owner needs to know exactly what hook boundary was checked. Keep the note short: root, command, result, residual warnings, and the trigger for the next recheck.
Fix path: run the bounded summary, paste the handoff record, and mark whether a fresh Claude Code session was started
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --summary-only
Command boundary: this downloads tools/safety-check/check.sh from GitHub raw content, then runs it locally on your current project and Claude Code settings. The checker does not upload settings.json, hook files, shell history, repository contents, session logs, or safety summary output.
Claude Code hook boundary checked:
- Project/root:
- OS and shell:
- Claude Code version:
- Hooks installed in: user settings / project settings / both / not sure
- Command used:
- Command/scope: real project-root --verify / isolated reproduction / native install.ps1 verify
- What changed recently: fresh install / Claude Code update / settings edit / hook edit / moved hook files
- Pre-update baseline: summary/version captured / not captured / not an update issue
- Fresh Claude Code session started after verification: yes / no
- Next intended action: audit only / local fix / commit / push / public report
- Staged diff and destination reviewed separately: yes / no / not applicable
- Result:
--- Safety Summary (copy/paste) ---
...
--- End Safety Summary ---
Residual platform warnings:
- ...
Next recheck trigger:
- Claude Code update / settings edit / hook edit / launch directory change / before risky automation
Treat this as a boundary statement, not a certificate. Verify: 0 FAIL-OPEN means representative PreToolUse payload checks did not find a configured PreToolUse hook that failed open; it does not prove every possible path or lifecycle hook event is blocked.
Full template and Windows notes: team handoff reports.
"I am about to let Claude Code work for a while"
Before a long, unattended, or high-trust session, verify the hooks from the exact project root Claude Code will use. This preflight catches missing hook files, stale settings, failed payload checks, direct commits to protected branches, and missing audit logs before the session starts.
Fix path: install the session guard set, verify strictly, snapshot state, then restart Claude Code
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- bash-guard git-safe file-guard branch-guard worktree-guard session-log
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- verify
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --strict
$ git status --short
The guard set blocks dangerous shell and git commands, protects sensitive files, prevents direct commits to protected branches, prevents losing work when exiting Claude Code worktrees, and writes a JSONL tool-call audit trail. The final git status --short is a human checkpoint: start from a state you can explain, or commit/stash intentionally before turning the session loose.
On native Windows, use PowerShell 7 and the native verifier:
PS> $root = if (Get-Command git -ErrorAction SilentlyContinue) { git rev-parse --show-toplevel 2>$null }
PS> if ($root) { Set-Location $root }
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } bash-guard git-safe file-guard branch-guard worktree-guard session-log"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } verify"
PS> git status --short
After a clean preflight, start a fresh Claude Code session from that same root. Existing sessions may still have old hook settings loaded. If the session will touch secrets, production data, cloud resources, or customer files, combine hooks with OS permissions, containers, network policy, and backups.
"Claude Code updated. Are my hooks still working?"
Claude Code, IDE integrations, terminal profiles, and settings files can all change the hook boundary. A clean install from last week does not prove the current session is protected.
Fix path: back up settings, upgrade, doctor, then strictly verify from the same project root
Run the update check before trusting Claude Code with destructive commands, repo writes, or autonomous work after a version change. The strict verifier sends representative hook payloads; it does not execute the dangerous shell or git commands named in those payloads.
Before changing anything, record claude --version and a --verify --summary-only result. After the update, compare those notes with the new strict verification so you can tell a new regression from an old unverified setup.
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ claude --version 2>/dev/null || true
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --summary-only
$ unset IS_DEMO CLAUDE_CODE_SIMPLE
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- backup
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- upgrade
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- doctor
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --strict
On native Windows, use PowerShell 7 and verify the native hooks:
PS> $root = if (Get-Command git -ErrorAction SilentlyContinue) { git rev-parse --show-toplevel 2>$null }
PS> if ($root) { Set-Location $root }
PS> claude --version 2>$null
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } verify"
PS> Remove-Item Env:IS_DEMO -ErrorAction SilentlyContinue
PS> Remove-Item Env:CLAUDE_CODE_SIMPLE -ErrorAction SilentlyContinue
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } backup"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } upgrade"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } doctor"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } verify"
Keep the backup until the fresh session proves the hook boundary still works. If restore is needed, run backup list first and restore the named snapshot you intend to use. After a clean verification, start a fresh Claude Code session from that same project root. For restore steps and failure triage, use the Claude Code update checklist.
On macOS, add a protected-folder canary if the fresh session was launched from Claude Desktop, an IDE, or another app wrapper and the project lives under Desktop, Documents, Downloads, iCloud Drive, Dropbox, Google Drive, OneDrive, or another CloudStorage path. In that same Claude Code session, ask it to open one byte from a known project file with Bash, then read the same file with the Read tool. Treat EPERM, an empty Read result, or a mismatch where stat works but opening the file fails as a stop signal. Launch Claude Code directly from Terminal, or move the checkout to a non-TCC-protected path, before consequential edits.
$ target="${BOUCLE_TCC_CANARY:-README.md}"
$ python3 - "$target" <<'PY'
import pathlib, sys
p = pathlib.Path(sys.argv[1])
with p.open("rb") as f:
print(f.read(1))
PY
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- backup list
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- restore settings.20260101_120000.json
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } backup list"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } restore settings.20260101_120000.json"
"I resumed a session after hooks or settings changed"
Use this when a Claude Code conversation was resumed, left open during a hook install, or kept running while settings.json, settings.local.json, MCP servers, plugins, or permission modes changed. Disk files can be correct while the active session still uses old hook paths, old permission state, or a different mode.
Fix path: verify from disk, start a fresh session from the verified root, then run one harmless canary in that fresh session
First prove the files on disk are healthy from the exact project root Claude Code should use:
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ claude --version 2>/dev/null || true
$ unset IS_DEMO CLAUDE_CODE_SIMPLE
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- doctor
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --strict
If strict verification reports missing hooks, skipped boundary checks, no payload checks, or FAIL-OPEN, repair that before continuing. If disk verification is clean, do not treat a resumed or long-running session as automatically refreshed. Start a new Claude Code process from the same root and keep the old session read-only until the fresh one proves the current boundary.
In the fresh session, run a harmless canary that must pass through the same boundary you care about. For example, ask Claude Code to report the current working directory, then ask it to attempt a known-safe dry run or a blocked test command that your installed guard is expected to deny. The point is not to run destructive work; it is to prove the fresh session loaded the hook and permission state you just verified on disk.
On native Windows, use PowerShell 7 and the native verifier before opening the fresh session:
PS> $root = if (Get-Command git -ErrorAction SilentlyContinue) { git rev-parse --show-toplevel 2>$null }
PS> if ($root) { Set-Location $root }
PS> claude --version 2>$null
PS> Remove-Item Env:IS_DEMO -ErrorAction SilentlyContinue
PS> Remove-Item Env:CLAUDE_CODE_SIMPLE -ErrorAction SilentlyContinue
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } doctor"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } verify"
Treat these as recheck triggers: resumed conversation, compacted session, changed launch directory, edited .claude/settings.local.json, hook install or restore, plugin or MCP update, IDE extension update, or any unexpected permission prompt after a clean install.
Related limitations: bypassPermissions not restored on session resume and settings.local.json permission-state desync.
"An MCP server or plugin changed. Is it still the same tool?"
Use this after adding, updating, reconnecting, or approving an MCP server, Claude connector, marketplace plugin, or plugin channel. MCP tool names, schemas, descriptions, server URLs, and prompt metadata are part of the trust boundary. A hook verifier proves local hooks still execute; it does not prove a remote server kept the same tool surface.
Fix path: record the visible server list, rerun hook verification, start a fresh session, then test one harmless read-only tool per critical server
Before changing the server, connector, plugin, or channel, capture a bounded baseline. Do not paste claude mcp get output into public logs for HTTP servers that may store secrets in headers.
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ claude --version 2>/dev/null || true
$ claude mcp list 2>/dev/null || printf 'claude mcp list failed\n'
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --summary-only
After the change, run the strict local hook recheck from the same project root and terminal profile. If hooks disappeared, payload checks did not run, or any boundary hook reports FAIL-OPEN, stop before using the changed MCP surface for credential reads, shell execution, repository writes, network actions, or external publication.
$ repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
$ cd "$repo_root"
$ unset IS_DEMO CLAUDE_CODE_SIMPLE
$ claude mcp list 2>/dev/null || printf 'claude mcp list failed\n'
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- doctor
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/safety-check/check.sh | bash -s -- --verify --strict
On native Windows, use PowerShell 7 for the same baseline and verifier:
PS> $root = if (Get-Command git -ErrorAction SilentlyContinue) { git rev-parse --show-toplevel 2>$null }
PS> if ($root) { Set-Location $root }
PS> claude --version 2>$null
PS> claude mcp list 2>$null
PS> Remove-Item Env:IS_DEMO -ErrorAction SilentlyContinue
PS> Remove-Item Env:CLAUDE_CODE_SIMPLE -ErrorAction SilentlyContinue
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } doctor"
PS> iex "& { $(irm https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.ps1) } verify"
Start a fresh Claude Code session from the same root after the check. In that fresh session, compare the visible MCP server list with the baseline and run one harmless read-only call for each critical server before trusting write-capable tools. Treat a new tool, missing tool, changed description, changed schema, unexpected URL, failed read-only probe, or connector crossing from read/draft behavior into send/delete/publish behavior as a renewed approval event.
Related limitations: runtime-gated MCP metadata poisoning, remote MCP connectors gaining write tools, and MCP hook ask/deny enforcement gaps.
Quick reference
| Problem | Hook |
|---|---|
| CLAUDE.md rules ignored | enforce |
| Try safety-check before it reads real settings | safety-check isolated first test |
| Read-only review or audit | enforce + @enforced read-only policy |
| Files deleted or overwritten | file-guard + bash-guard |
| Dangerous shell commands | bash-guard |
| Force push / hard reset / checkout . | git-safe |
| Commits to main/master | branch-guard |
| --no-verify / bypass pre-commit | git-safe |
| Redundant file re-reads (token waste) | read-once |
| Lost work exiting worktrees | worktree-guard |
| Claude Code update changed hook behavior | safety-check --verify --strict + doctor |
| Resumed session after settings changed | doctor + strict verification + fresh-session canary |
| MCP server, connector, or plugin changed | claude mcp list + strict hook recheck + harmless read-only probe |
| Nested Claude call used API-key billing | unset ANTHROPIC_API_KEY + fresh process + wrapper or Bash deny rule |
| Verification failed or skipped checks | doctor + safety summary triage |
| Settings or hook files edited by hand | backup + JSON validation + doctor + strict verification |
| Temporary trial before handing back a machine | backup + recommended + uninstall all + recheck |
| Native Windows hook setup | install.ps1 recommended + doctor + verify |
| Custom hook dry run | test-hook.sh |
| Audit all tool calls | session-log |
| Long unattended session | bash-guard + git-safe + file-guard + branch-guard + worktree-guard + session-log |
| Repo-local hooks drift in CI | safety-check --verify --strict from the repository root |
| Stale cache after compaction | read-once (PostCompact) |
Install everything
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- all
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- doctor
$ curl -fsSL https://raw.githubusercontent.com/Bande-a-Bonnot/Boucle-framework/main/tools/install.sh | bash -s -- verify
Or start with the safety essentials: bash -s -- recommended installs bash-guard + git-safe + file-guard. After doctor and verification pass, start a fresh Claude Code session from the same project root before relying on the new hook boundary.