cc-safety-net 1.0.6 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +177 -877
  3. package/THIRD_PARTY_LICENSES.txt +23 -0
  4. package/dist/amp/cc-safety-net.ts +102 -0
  5. package/dist/bin/cc-safety-net.js +4825 -10427
  6. package/dist/chunks/index-25nh5wqs.js +45 -0
  7. package/dist/chunks/index-9s8cwhj0.js +64 -0
  8. package/dist/index.d.ts +2 -15
  9. package/dist/index.js +1 -6404
  10. package/dist/openclaw/cc-safety-net/index.js +101 -0
  11. package/dist/openclaw/cc-safety-net/openclaw.plugin.json +14 -0
  12. package/dist/openclaw/cc-safety-net/package.json +11 -0
  13. package/dist/pi/index.js +2 -6540
  14. package/package.json +55 -30
  15. package/dist/bin/cc-safety-net.d.ts +0 -2
  16. package/dist/bin/commands/doctor.d.ts +0 -11
  17. package/dist/bin/commands/explain.d.ts +0 -16
  18. package/dist/bin/commands/hook.d.ts +0 -14
  19. package/dist/bin/commands/index.d.ts +0 -82
  20. package/dist/bin/commands/rule.d.ts +0 -14
  21. package/dist/bin/commands/statusline.d.ts +0 -10
  22. package/dist/bin/commands/types.d.ts +0 -45
  23. package/dist/bin/doctor/activity.d.ts +0 -5
  24. package/dist/bin/doctor/config.d.ts +0 -15
  25. package/dist/bin/doctor/environment.d.ts +0 -5
  26. package/dist/bin/doctor/flags.d.ts +0 -5
  27. package/dist/bin/doctor/format.d.ts +0 -37
  28. package/dist/bin/doctor/hooks.d.ts +0 -25
  29. package/dist/bin/doctor/index.d.ts +0 -6
  30. package/dist/bin/doctor/system-info.d.ts +0 -29
  31. package/dist/bin/doctor/types.d.ts +0 -159
  32. package/dist/bin/doctor/updates.d.ts +0 -5
  33. package/dist/bin/explain/analyze.d.ts +0 -5
  34. package/dist/bin/explain/config.d.ts +0 -25
  35. package/dist/bin/explain/flags.d.ts +0 -9
  36. package/dist/bin/explain/format-helpers.d.ts +0 -35
  37. package/dist/bin/explain/format.d.ts +0 -8
  38. package/dist/bin/explain/index.d.ts +0 -7
  39. package/dist/bin/explain/redact.d.ts +0 -20
  40. package/dist/bin/explain/segment.d.ts +0 -11
  41. package/dist/bin/help.d.ts +0 -19
  42. package/dist/bin/hook/claude-code.d.ts +0 -1
  43. package/dist/bin/hook/common.d.ts +0 -18
  44. package/dist/bin/hook/config-edit.d.ts +0 -11
  45. package/dist/bin/hook/constants.d.ts +0 -6
  46. package/dist/bin/hook/copilot-cli.d.ts +0 -1
  47. package/dist/bin/hook/gemini-cli.d.ts +0 -1
  48. package/dist/bin/hook/install/kimi-code.d.ts +0 -3
  49. package/dist/bin/hook/install/types.d.ts +0 -4
  50. package/dist/bin/hook/install.d.ts +0 -3
  51. package/dist/bin/hook/integrations.d.ts +0 -12
  52. package/dist/bin/hook/kimi-code.d.ts +0 -1
  53. package/dist/bin/integration-metadata.d.ts +0 -68
  54. package/dist/bin/rule/doc.d.ts +0 -1
  55. package/dist/bin/rule/format.d.ts +0 -14
  56. package/dist/bin/rule/index.d.ts +0 -1
  57. package/dist/bin/rule/migrate.d.ts +0 -6
  58. package/dist/bin/rule/verify.d.ts +0 -9
  59. package/dist/bin/statusline.d.ts +0 -1
  60. package/dist/bin/utils/colors.d.ts +0 -36
  61. package/dist/builtin-commands/templates/cc-safety-net.d.ts +0 -1
  62. package/dist/core/analyze/analyze-command.d.ts +0 -6
  63. package/dist/core/analyze/awk.d.ts +0 -3
  64. package/dist/core/analyze/child-analyzer.d.ts +0 -16
  65. package/dist/core/analyze/child-command.d.ts +0 -15
  66. package/dist/core/analyze/constants.d.ts +0 -1
  67. package/dist/core/analyze/dangerous-text.d.ts +0 -1
  68. package/dist/core/analyze/find.d.ts +0 -8
  69. package/dist/core/analyze/index.d.ts +0 -4
  70. package/dist/core/analyze/interpreters.d.ts +0 -2
  71. package/dist/core/analyze/parallel.d.ts +0 -13
  72. package/dist/core/analyze/rm-flags.d.ts +0 -1
  73. package/dist/core/analyze/rm.d.ts +0 -9
  74. package/dist/core/analyze/segment.d.ts +0 -11
  75. package/dist/core/analyze/shell-git-env.d.ts +0 -10
  76. package/dist/core/analyze/shell-wrappers.d.ts +0 -1
  77. package/dist/core/analyze/tmpdir.d.ts +0 -1
  78. package/dist/core/analyze/xargs.d.ts +0 -16
  79. package/dist/core/audit.d.ts +0 -20
  80. package/dist/core/config.d.ts +0 -14
  81. package/dist/core/env.d.ts +0 -39
  82. package/dist/core/format.d.ts +0 -11
  83. package/dist/core/git/config.d.ts +0 -1
  84. package/dist/core/git/env.d.ts +0 -15
  85. package/dist/core/git/index.d.ts +0 -3
  86. package/dist/core/git/parse.d.ts +0 -9
  87. package/dist/core/git/rules.d.ts +0 -8
  88. package/dist/core/git/worktree-relaxation.d.ts +0 -11
  89. package/dist/core/git/worktree.d.ts +0 -11
  90. package/dist/core/path.d.ts +0 -1
  91. package/dist/core/rules/custom-rule-validation.d.ts +0 -5
  92. package/dist/core/rules/custom.d.ts +0 -2
  93. package/dist/core/rules/policy/config-file.d.ts +0 -19
  94. package/dist/core/rules/policy/index.d.ts +0 -5
  95. package/dist/core/rules/policy/lockfile.d.ts +0 -5
  96. package/dist/core/rules/policy/paths.d.ts +0 -32
  97. package/dist/core/rules/policy/resolver.d.ts +0 -16
  98. package/dist/core/rules/policy/scope-policy.d.ts +0 -19
  99. package/dist/core/rules/policy/sources.d.ts +0 -28
  100. package/dist/core/rules/policy/sync.d.ts +0 -10
  101. package/dist/core/rules/policy/types.d.ts +0 -76
  102. package/dist/core/rules/rulebook.d.ts +0 -30
  103. package/dist/core/shell/command.d.ts +0 -2
  104. package/dist/core/shell/index.d.ts +0 -4
  105. package/dist/core/shell/options.d.ts +0 -3
  106. package/dist/core/shell/segments.d.ts +0 -6
  107. package/dist/core/shell/shared.d.ts +0 -4
  108. package/dist/core/shell/wrappers.d.ts +0 -17
  109. package/dist/opencode/builtin-commands/commands.d.ts +0 -2
  110. package/dist/opencode/builtin-commands/index.d.ts +0 -2
  111. package/dist/opencode/builtin-commands/types.d.ts +0 -6
  112. package/dist/pi/builtin-commands/commands.d.ts +0 -16
  113. package/dist/pi/builtin-commands/index.d.ts +0 -1
  114. package/dist/pi/index.d.ts +0 -5
  115. package/dist/pi/tool-call.d.ts +0 -21
  116. package/dist/types.d.ts +0 -275
package/README.md CHANGED
@@ -1,954 +1,254 @@
1
- # CC Safety Net
1
+ <h1>
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="./.github/assets/cc-safety-net-header-logo-dark.svg">
4
+ <source media="(prefers-color-scheme: light)" srcset="./.github/assets/cc-safety-net-header-logo-light.svg">
5
+ <img alt="CC Safety Net" src="./.github/assets/cc-safety-net-header-logo-light.svg">
6
+ </picture>
7
+ </h1>
2
8
 
3
9
  [![CI](https://github.com/kenryu42/cc-safety-net/actions/workflows/ci.yml/badge.svg)](https://github.com/kenryu42/cc-safety-net/actions/workflows/ci.yml)
4
10
  [![codecov](https://codecov.io/github/kenryu42/cc-safety-net/branch/main/graph/badge.svg?token=C9QTION6ZF)](https://codecov.io/github/kenryu42/cc-safety-net)
5
11
  [![Version](https://img.shields.io/github/v/tag/kenryu42/cc-safety-net?label=version&color=blue)](https://github.com/kenryu42/cc-safety-net)
6
- [![Codex](https://img.shields.io/badge/Codex-white)](#codex-installation)
7
- [![Claude Code](https://img.shields.io/badge/Claude%20Code-D27656)](#claude-code-installation)
8
- [![Copilot CLI](https://img.shields.io/badge/Copilot%20CLI-4EA5C9)](#github-copilot-cli-installation)
9
- [![Gemini CLI](https://img.shields.io/badge/Gemini%20CLI-678AE3)](#gemini-cli-installation)
10
- [![Kimi Code](https://img.shields.io/badge/Kimi%20Code-5587FF)](#kimi-code-installation)
11
- [![OpenCode](https://img.shields.io/badge/OpenCode-black)](#opencode-installation)
12
- [![Pi](https://img.shields.io/badge/Pi%20Coding-22262E)](#pi-installation)
12
+ [![Amp Code](https://img.shields.io/badge/Amp%20Code-467C51)](https://ccsafetynet.com/docs/installation#amp-code-installation)
13
+ [![Antigravity CLI](https://img.shields.io/badge/Antigravity%20CLI-8AA1F8)](https://ccsafetynet.com/docs/installation#antigravity-cli-installation)
14
+ [![Claude Code](https://img.shields.io/badge/Claude%20Code-D27656)](https://ccsafetynet.com/docs/installation#claude-code-installation)
15
+ [![Codex](https://img.shields.io/badge/Codex-white)](https://ccsafetynet.com/docs/installation#codex-installation)
16
+ [![Cursor](https://img.shields.io/badge/Cursor-000000)](https://ccsafetynet.com/docs/installation#cursor-installation)
17
+ [![Gemini CLI](https://img.shields.io/badge/Gemini%20CLI-678AE3)](https://ccsafetynet.com/docs/installation#gemini-cli-installation)
18
+ [![GitHub Copilot CLI](https://img.shields.io/badge/GitHub%20Copilot%20CLI-4EA5C9)](https://ccsafetynet.com/docs/installation#github-copilot-cli-installation)
19
+ [![Hermes Agent](https://img.shields.io/badge/Hermes%20Agent-2D3E50)](#hermes-agent-and-openclaw)
20
+ [![Kimi Code](https://img.shields.io/badge/Kimi%20Code-5587FF)](https://ccsafetynet.com/docs/installation#kimi-code-installation)
21
+ [![OpenClaw](https://img.shields.io/badge/OpenClaw-B4532A)](#hermes-agent-and-openclaw)
22
+ [![OpenCode](https://img.shields.io/badge/OpenCode-black)](https://ccsafetynet.com/docs/installation#opencode-installation)
23
+ [![Pi](https://img.shields.io/badge/Pi%20Coding-22262E)](https://ccsafetynet.com/docs/installation#pi-installation)
13
24
  [![License: MIT](https://img.shields.io/badge/License-MIT-red.svg)](https://opensource.org/licenses/MIT)
14
25
 
15
26
  <div align="center">
16
27
 
17
- [![CC Safety Net](./.github/assets/cc-safety-net.png)](./.github/assets/cc-safety-net.png)
28
+ [![CC Safety Net](./.github/assets/cc-safety-net-v2.png)](./.github/assets/cc-safety-net-v2.png)
18
29
 
19
30
  </div>
20
31
 
21
- A Coding Agent CLI plugin that acts as a safety net, catching destructive git and filesystem commands before they execute.
22
-
23
- ## Contents
24
-
25
- - [Why This Exists](#why-this-exists)
26
- - [Why Use This Instead of Permission Deny Rules?](#why-use-this-instead-of-permission-deny-rules)
27
- - [What About Sandboxing?](#what-about-sandboxing)
28
- - [Prerequisites](#prerequisites)
29
- - [Quick Start](#quick-start)
30
- - [Codex Installation](#codex-installation)
31
- - [Claude Code Installation](#claude-code-installation)
32
- - [Gemini CLI Installation](#gemini-cli-installation)
33
- - [GitHub Copilot CLI Installation](#github-copilot-cli-installation)
34
- - [Kimi Code Installation](#kimi-code-installation)
35
- - [OpenCode Installation](#opencode-installation)
36
- - [Pi Installation](#pi-installation)
37
- - [Status Line Integration](#status-line-integration)
38
- - [Emoji Mode Indicators](#emoji-mode-indicators)
39
- - [Diagnostics](#diagnostics)
40
- - [Explain (Debug Analysis)](#explain-debug-analysis)
41
- - [Commands Blocked](#commands-blocked)
42
- - [Commands Allowed](#commands-allowed)
43
- - [What Happens When Blocked](#what-happens-when-blocked)
44
- - [Testing the Hook](#testing-the-hook)
45
- - [Breaking Change: Custom Rules Migration](#breaking-change-custom-rules-migration)
46
- - [Custom Rules](#custom-rules)
47
- - [Config File Location](#config-file-location)
48
- - [Rule Schema](#rule-schema)
49
- - [Matching Behavior](#matching-behavior)
50
- - [Rule Examples](#rule-examples)
51
- - [Error Handling](#error-handling)
52
- - [Advanced Features](#advanced-features)
53
- - [Strict Mode](#strict-mode)
54
- - [Paranoid Mode](#paranoid-mode)
55
- - [Worktree Mode](#worktree-mode)
56
- - [Shell Wrapper Detection](#shell-wrapper-detection)
57
- - [Interpreter One-Liner Detection](#interpreter-one-liner-detection)
58
- - [Secret Redaction](#secret-redaction)
59
- - [Audit Logging](#audit-logging)
60
- - [Development](#development)
61
- - [License](#license)
62
-
63
- ## Why This Exists
64
-
65
- We learned the [hard way](https://www.reddit.com/r/ClaudeAI/comments/1pgxckk/claude_cli_deleted_my_entire_home_directory_wiped/) that instructions aren't enough to keep AI agents in check.
66
- After Claude Code silently wiped out hours of progress with a single `rm -rf ~/` or `git checkout --`, it became evident that **soft** rules in an `CLAUDE.md` or `AGENTS.md` file cannot replace **hard** technical constraints.
67
- The current approach is to use a dedicated hook to programmatically prevent agents from running destructive commands.
68
-
69
- ## Why Use This Instead of Permission Deny Rules?
70
-
71
- Claude Code's `.claude/settings.json` supports [deny rules](https://code.claude.com/docs/en/iam#tool-specific-permission-rules) with wildcard matching (e.g., `Bash(git reset --hard:*)`). Here's how this plugin differs:
72
-
73
- ### At a Glance
74
-
75
- | | Permission Deny Rules | CC Safety Net |
76
- |---|---|---|
77
- | **Setup** | Manual configuration required | Works out of the box |
78
- | **Parsing** | Wildcard pattern matching | Semantic command analysis |
79
- | **Execution order** | Runs second | Runs first (PreToolUse hook) |
80
- | **Shell wrappers** | Not handled automatically (must match wrapper forms) | Recursively analyzed (up to 10 levels) |
81
- | **Interpreter one-liners** | Not handled automatically (must match interpreter forms) | Detected and blocked |
82
-
83
- ### Permission Rules Have Known Bypass Vectors
84
-
85
- Even with wildcard matching, Bash permission patterns are intentionally limited and can be bypassed in many ways:
86
-
87
- | Bypass Method | Example |
88
- |---------------|---------|
89
- | Options before value | `curl -X GET http://evil.com` bypasses `Bash(curl http://evil.com:*)` |
90
- | Shell variables | `URL=http://evil.com && curl $URL` bypasses URL pattern |
91
- | Flag reordering | `rm -r -f /` bypasses `Bash(rm -rf:*)` |
92
- | Extra whitespace | `rm -rf /` (double space) bypasses pattern |
93
- | Shell wrappers | `sh -c "rm -rf /"` bypasses `Bash(rm:*)` entirely |
94
-
95
- ### CC Safety Net Handles What Patterns Can't
96
-
97
- | Scenario | Permission Rules | CC Safety Net |
98
- |----------|------------------|------------|
99
- | `git checkout -b feature` (safe) | Blocked by `Bash(git checkout:*)` | Allowed |
100
- | `git checkout -- file` (dangerous) | Blocked by `Bash(git checkout:*)` | Blocked |
101
- | `rm -rf /tmp/cache` (safe) | Blocked by `Bash(rm -rf:*)` | Allowed |
102
- | `rm -r -f /` (dangerous) | Allowed (flag order) | Blocked |
103
- | `bash -c 'git reset --hard'` | Allowed (wrapper) | Blocked |
104
- | `python -c 'os.system("rm -rf /")'` | Allowed (interpreter) | Blocked |
105
-
106
- ### Defense in Depth
107
-
108
- PreToolUse hooks run [**before**](https://code.claude.com/docs/en/iam#additional-permission-control-with-hooks) the permission system. This means CC Safety Net inspects every command first, regardless of your permission configuration. Even if you misconfigure deny rules, CC Safety Net provides a fallback layer of protection.
109
-
110
- **Use both together**: Permission deny rules for quick, user-configurable blocks; CC Safety Net for robust, bypass-resistant protection that works out of the box.
111
-
112
- ## What About Sandboxing?
113
-
114
- Claude Code offers [native sandboxing](https://code.claude.com/docs/en/sandboxing) that provides OS-level filesystem and network isolation. Here's how it compares to CC Safety Net:
115
-
116
- ### Different Layers of Protection
117
-
118
- | | Sandboxing | CC Safety Net |
119
- |---|---|---|
120
- | **Enforcement** | OS-level (Seatbelt/bubblewrap) | Application-level (PreToolUse hook) |
121
- | **Approach** | Containment — restricts filesystem + network access | Command analysis — blocks destructive operations |
122
- | **Filesystem** | Writes restricted (default: cwd); reads are broad by default | Only destructive operations blocked |
123
- | **Network** | Domain-based proxy filtering | None |
124
- | **Git awareness** | None | Explicit rules for destructive git operations |
125
- | **Bypass resistance** | High — OS enforces boundaries | Lower — analyzes command strings only |
126
-
127
- ### Why Sandboxing Isn't Enough
128
-
129
- Sandboxing restricts filesystem + network access, but it doesn't understand whether an operation is destructive within those boundaries. These commands are not blocked by the sandbox boundary:
32
+ CC Safety Net — short for **Coding CLI** Safety Net — is a PreToolUse hook that blocks destructive commands and access to secrets like SSH keys and `.env` files, before AI coding agents run them. It parses command **semantics**, so flag reordering, shell wrappers, and interpreter one-liners can't bypass it.
130
33
 
131
34
  > [!NOTE]
132
- > Whether they're auto-run or require confirmation depends on your sandbox mode (auto-allow vs regular permissions), and network access still depends on your allowed-domain policy. Claude Code can also retry a command outside the sandbox via `dangerouslyDisableSandbox` (with user permission); this can be disabled with `allowUnsandboxedCommands: false`.
35
+ > **[Full documentation →](https://ccsafetynet.com/docs)** — installation, configuration, reference, guides, and the security model live on the docs site. This README is the short version.
133
36
 
134
- | Command | Sandboxing | CC Safety Net |
135
- |---------|------------|------------|
136
- | `git reset --hard` | Allowed (within cwd) | **Blocked** |
137
- | `git checkout -- .` | Allowed (within cwd) | **Blocked** |
138
- | `git stash clear` | Allowed (within cwd) | **Blocked** |
139
- | `git push --force` | Allowed (if remote domain is allowed) | **Blocked** |
140
- | `rm -rf .` | Allowed (within cwd) | **Blocked** |
37
+ ## Why this exists
141
38
 
142
- Sandboxing sees `git reset --hard` as a safe operation—it only modifies files within the current directory. But you just lost all uncommitted work.
39
+ We learned the [hard way](https://www.reddit.com/r/ClaudeAI/comments/1pgxckk/claude_cli_deleted_my_entire_home_directory_wiped/) that instructions aren't enough to keep AI agents in check. After an agent silently wiped hours of progress with a single `rm -rf ~/` or `git checkout --`, it became clear that **soft** rules in a `CLAUDE.md` or `AGENTS.md` file cannot replace **hard** technical constraints. CC Safety Net is that constraint: it observes relevant tool calls and blocks destructive commands and secret access before they reach the shell. See [What Is CC Safety Net](https://ccsafetynet.com/docs/introduction) for the full background.
143
40
 
144
- ### When to Use Sandboxing Instead
41
+ ## What's new in v2.0.0
145
42
 
146
- Sandboxing is the better choice when your primary concern is:
147
-
148
- - **Prompt injection attacks** — Reduces exfiltration risk by restricting outbound domains (depends on your allowed-domain policy)
149
- - **Malicious dependencies** — Limits filesystem writes and network access by default (subject to your sandbox configuration)
150
- - **Untrusted code execution** — OS-level containment is stronger than pattern matching
151
- - **Network control** — CC Safety Net has no network protection
152
-
153
- ### Recommended: Use Both
154
-
155
- They protect against different threats:
156
-
157
- - **Sandboxing** contains blast radius — even if something goes wrong, damage is limited to cwd and approved network domains
158
- - **CC Safety Net** prevents footguns — catches git-specific mistakes that are technically "safe" from the sandbox's perspective
159
-
160
- Running both together provides defense-in-depth. Sandboxing handles unknown threats; CC Safety Net handles known destructive patterns that sandboxing permits.
161
-
162
- ## Prerequisites
163
-
164
- - **Node.js**: Version 18 or higher is required to run this plugin
165
-
166
- ## Quick Start
167
-
168
- ### Codex Installation
169
-
170
- 1. Enable Codex plugin hooks in `~/.codex/config.toml`:
171
-
172
- ```toml
173
- [features]
174
- plugin_hooks = true
175
- ```
176
-
177
- 2. Add the marketplace:
43
+ > [!TIP]
44
+ > **Already running v1?** One command upgrades every installed integration to v2: `npx -y cc-safety-net@latest update`. If you defined custom rules under v1, also read [Upgrading from an older version](#upgrading-from-an-older-version).
178
45
 
179
- ```bash
180
- codex plugin marketplace add kenryu42/cc-marketplace
181
- ```
46
+ - **Rebuilt evaluation engine** — canonical command IR, deeply immutable policy snapshots, and an ordered guard pipeline with intrinsic decision tracing behind `explain`.
47
+ - **Secret protection** — built-in rules block content access to SSH keys, `.env` files, cloud credentials, and coding-CLI credential stores, across shell commands and file tools alike.
48
+ - **Always-on catastrophic protections** — recursive deletion of root or home, Git metadata mutation (`.git` control plane, hooks, worktrees, submodules), and mutation of the user policy file are blocked in every mode, regardless of overrides.
49
+ - **Safety presets** — `standard`/`strict`/`paranoid` levels with per-rule overrides, trusted delete allow-paths, and env vars that can only raise protection.
50
+ - **Policy GUI** — `cc-safety-net gui` serves a local, token-authenticated editor with live preset preview.
51
+ - **Universal installer** — interactive `install`/`uninstall` across all twelve supported agent CLIs, with an `update` command for installed integrations.
52
+ - **Command-decision audit trail** — allowed and blocked command decisions recorded by default to local per-project JSONL with secret redaction, retained for 30 days by default, browsable via `cc-safety-net logs`.
53
+ - **Documented threat model** — the [SECURITY.md](SECURITY.md) mode contract, explicit resource limits, and a residual-risk registry of adjudicated bypass families.
182
54
 
183
- 3. Start Codex.
184
- 4. In the TUI, run `/plugins`.
185
- 5. Use arrow keys to select `[cc-marketplace]`.
186
- 6. Press Enter to install the plugin.
187
- 7. run `/hooks` and select the safety-net PreToolUse hook and press `t` to trust it.
55
+ ## Quick start
188
56
 
189
- ---
57
+ **Prerequisite:** Node.js 18 or higher.
190
58
 
191
- ### Claude Code Installation
59
+ Run the interactive selector to install CC Safety Net into one or more installed coding CLIs:
192
60
 
193
61
  ```bash
194
- /plugin marketplace add kenryu42/cc-marketplace
195
- /plugin install safety-net@cc-marketplace
196
- /reload-plugins
62
+ npx -y cc-safety-net@latest install
197
63
  ```
198
64
 
199
- ### Claude Code Auto-Update
200
-
201
- 1. Run `/plugin` → Select `Marketplaces` → Choose `cc-marketplace` → Enable auto-update
202
-
203
- ---
204
-
205
- ### Gemini CLI Installation
65
+ To update every installed integration:
206
66
 
207
67
  ```bash
208
- gemini extensions install https://github.com/kenryu42/gemini-safety-net
68
+ npx -y cc-safety-net@latest update
209
69
  ```
210
70
 
211
- ---
71
+ The `@latest` qualifier matters: a bare `cc-safety-net` spec can re-run an older
72
+ cached copy from the npx cache instead of the current release.
212
73
 
213
- ### GitHub Copilot CLI Installation
74
+ To remove integrations interactively:
214
75
 
215
76
  ```bash
216
- /plugin install kenryu42/copilot-safety-net
77
+ npx -y cc-safety-net uninstall
217
78
  ```
218
79
 
219
- ---
220
-
221
- ### Kimi Code Installation
222
-
223
- Install CC Safety Net into your Kimi Code config:
80
+ If you use the CLI often, install it globally to get `ccsn`, a shorter alias for the same commands:
224
81
 
225
82
  ```bash
226
- npx -y cc-safety-net hook install --kimi-code
83
+ npm install -g cc-safety-net
84
+ ccsn doctor
227
85
  ```
228
86
 
229
- Optional: run `npx skill add kenryu42/cc-safety-net` to add the `/cc-safety-net` skill for configuring custom rules.
87
+ CC Safety Net works across twelve coding agent CLIs — **Claude Code, Antigravity CLI, Codex, Gemini CLI, GitHub Copilot CLI, Kimi Code, OpenCode, Pi, Cursor, Amp Code, Hermes Agent, and OpenClaw** — on **Windows, macOS, and Linux** (the Hermes Agent and OpenClaw integrations are macOS and Linux only; see [Hermes Agent and OpenClaw](#hermes-agent-and-openclaw)). Per-agent instructions, the target flags for scripted non-interactive installs, post-install steps, and per-agent caveats are on [Installation](https://ccsafetynet.com/docs/installation); how each integration hooks its agent is on [Integration Architecture](https://ccsafetynet.com/docs/guides/integration-architecture).
230
88
 
231
- ---
232
-
233
-
234
- ### OpenCode Installation
235
-
236
- Install CC Safety Net with OpenCode's native plugin command:
237
-
238
- ```bash
239
- opencode plugin -g cc-safety-net
240
- ```
89
+ ## Hermes Agent and OpenClaw
241
90
 
242
- > [!NOTE]
243
- > OpenCode can sometimes keep using a stale cached plugin version. See
244
- > anomalyco/opencode#25293 for the current tracking issue.
245
- >
246
- > To force OpenCode to reinstall `cc-safety-net`, remove its cached package and
247
- > install the version you want:
248
- >
249
- > ```sh
250
- > rm -rf ~/.cache/opencode/packages/cc-safety-net@latest
251
- > opencode plugin -g -f cc-safety-net@latest
252
- >
253
- > If you prefer pinning a specific version:
254
- >
255
- > rm -rf ~/.cache/opencode/packages/cc-safety-net@latest
256
- > opencode plugin -g -f cc-safety-net@<version>
257
- >
258
- > Restart OpenCode after updating so the plugin is loaded from the refreshed
259
- > cache.
260
-
261
- ---
262
-
263
- ### Pi Installation
264
-
265
- Install CC Safety Net with Pi's package installer:
91
+ These two integrations are newer than the docs site, so their setup steps and coverage
92
+ boundaries live here.
266
93
 
267
94
  ```bash
268
- pi install npm:cc-safety-net
269
- ```
270
-
271
- ---
272
-
273
- ## Status Line Integration
274
-
275
- CC Safety Net can display its status in Claude Code's status line, showing whether protection is active and which modes are enabled.
276
-
277
- Add the following to your `~/.claude/settings.json`:
278
-
279
- **Using Bun (recommended):**
280
-
281
- ```json
282
- {
283
- "statusLine": {
284
- "type": "command",
285
- "command": "bunx cc-safety-net statusline --claude-code"
286
- }
287
- }
288
- ```
289
-
290
- **Using Claude X:**
291
-
292
- ```json
293
- {
294
- "statusLine": {
295
- "type": "command",
296
- "command": "BUN_BE_BUN=1 claude x cc-safety-net statusline --claude-code"
297
- }
298
- }
299
- ```
300
- > [!NOTE]
301
- > The `claude x` command is only compatible with the native version of Claude Code. If you installed via npm, please use `npx` or `bunx` instead.
302
-
303
-
304
-
305
- **Using NPM:**
306
-
307
- ```json
308
- {
309
- "statusLine": {
310
- "type": "command",
311
- "command": "npx -y cc-safety-net statusline --claude-code"
312
- }
313
- }
314
- ```
95
+ # Hermes Agent — installs a managed Python plugin, then restart Hermes
96
+ npx -y cc-safety-net@latest install --hermes-agent
97
+ npx -y cc-safety-net@latest uninstall --hermes-agent
315
98
 
316
- **Piping with existing status line:**
317
-
318
- If you already have a status line command, you can pipe CC Safety Net at the end:
319
-
320
- ```json
321
- {
322
- "statusLine": {
323
- "type": "command",
324
- "command": "your-existing-command | bunx cc-safety-net statusline --claude-code"
325
- }
326
- }
99
+ # OpenClaw — installs a bundled plugin through OpenClaw's own CLI, then restart the Gateway
100
+ npx -y cc-safety-net@latest install --openclaw
101
+ npx -y cc-safety-net@latest uninstall --openclaw
327
102
  ```
328
103
 
329
- Changes take effect immediately — no restart needed.
330
-
331
- ### Emoji Mode Indicators
104
+ Run `npx cc-safety-net doctor` after either install: it is the only surface that reports a plugin
105
+ that is present but not enabled, not ours, or out of date. It reads disk and config only and never
106
+ asks a running host whether the plugin loaded, so a stopped OpenClaw Gateway is not reported as a
107
+ failure — the OpenClaw install verifies the load itself.
332
108
 
333
- The status line displays different emojis based on the current configuration:
109
+ **Hermes Agent.** Install writes a managed plugin to `$HERMES_HOME/plugins/cc-safety-net/`
110
+ (`~/.hermes` when `HERMES_HOME` is unset) and then
111
+ runs `hermes plugins enable cc-safety-net --no-allow-tool-override`. It is an ordinary Hermes user
112
+ plugin: it runs inside the Hermes process with the trust Hermes grants user plugins, and it is
113
+ inert until Hermes is restarted. On every `pre_tool_call` it shells out to
114
+ `npx -y cc-safety-net hook --hermes-agent` with a 30-second timeout, and blocks the call itself
115
+ whenever that analysis cannot complete — missing `npx`, spawn failure, timeout, unreadable output,
116
+ or a Hermes session directory it cannot read.
117
+ Protected tools are `terminal`, `read_file`, `write_file`, and `patch`; calls to any other Hermes
118
+ tool are not forwarded and get no decision. Uninstall runs `hermes plugins disable cc-safety-net`
119
+ first — Hermes only resolves a plugin that is still on disk — and then removes the managed files,
120
+ so the config entry goes with them; a failing or missing `hermes` is reported as a warning and the
121
+ files are removed anyway.
334
122
 
335
- | Status | Display | Meaning |
336
- |--------|---------|---------|
337
- | Plugin disabled | `🛡️ CC Safety Net ❌` | CC Safety Net plugin is not enabled |
338
- | Default mode | `🛡️ CC Safety Net ✅` | Protection active with default settings |
339
- | Strict mode | `🛡️ CC Safety Net 🔒` | `CC_SAFETY_NET_STRICT=1` — fail-closed on unparseable commands |
340
- | Paranoid mode | `🛡️ CC Safety Net 👁️` | `CC_SAFETY_NET_PARANOID=1` — all paranoid checks enabled |
341
- | Paranoid RM only | `🛡️ CC Safety Net 🗑️` | `CC_SAFETY_NET_PARANOID_RM=1` — blocks `rm -rf` even within cwd |
342
- | Paranoid interpreters only | `🛡️ CC Safety Net 🐚` | `CC_SAFETY_NET_PARANOID_INTERPRETERS=1` — blocks interpreter one-liners |
343
- | Worktree mode | `🛡️ CC Safety Net 🌳` | `CC_SAFETY_NET_WORKTREE=1` — relax local git discards inside linked worktrees |
344
- | Strict + Paranoid | `🛡️ CC Safety Net 🔒👁️` | Both strict and paranoid modes enabled |
123
+ **OpenClaw.** Install runs `openclaw plugins install <bundled plugin dir> --force` followed by
124
+ `openclaw plugins enable cc-safety-net`, then verifies the plugin actually loaded. **Restart the
125
+ OpenClaw Gateway afterwards, and if `plugins.allow` is set in `openclaw.json` it must also list
126
+ `cc-safety-net`** — until both are true the plugin never runs and nothing is blocked. The plugin
127
+ registers `before_tool_call` for the canonical `exec` tool only and returns a plain allow or
128
+ block; it never rewrites tool parameters. It resolves the agent workspace as the policy and
129
+ execution directory, and denies a call whose `workdir` resolves outside that workspace or whose
130
+ agent or workspace context cannot be established. Live end-to-end tests (`bun run test:e2e:live`,
131
+ run before a release) drive the real `openclaw` binary against a throwaway state directory: our
132
+ CLI installs the plugin, the host reports it loaded with the hook registered, and a real Gateway
133
+ agent turn puts a real `exec` call through it — a harmless command runs, `git reset --hard` is
134
+ blocked before it can touch the workspace.
345
135
 
346
- Multiple mode emojis are combined when multiple environment variables are set. Mode flags use `CC_SAFETY_NET_*` names; legacy `SAFETY_NET_*` names are still accepted.
136
+ **What is not covered.**
347
137
 
348
- ## Diagnostics
138
+ - **Hermes bang-shell.** A `!command` you type yourself does not raise `pre_tool_call`; it goes
139
+ straight to Hermes' own command guard. CC Safety Net sees model-generated tool calls only, not
140
+ every subprocess Hermes starts.
141
+ - **Hermes shell hooks fail open.** Hermes' `hooks:` dispatcher allows the tool call when a hook
142
+ cannot start, times out, or returns unparseable output. That is why the shipped integration is
143
+ the Python plugin, which blocks on its own failures, and why `hooks:` is a diagnostic path only.
144
+ One case stays outside our reach either way: if Hermes never loads the plugin, nothing blocks
145
+ and only `doctor` will say so.
146
+ - **OpenClaw execution hosts.** `exec` calls with no `host`, `host: "auto"`, or `host: "gateway"`
147
+ are analysed as local Gateway calls. Explicit `host: "sandbox"` and `host: "node"` are blocked
148
+ as unsupported, because no test proves a correct path mapping for them. When a sandbox runtime
149
+ is active, an `auto` call runs in the sandbox filesystem while paths are still analysed against
150
+ the Gateway workspace — command rules are unaffected, path rules can be evaluated against the
151
+ wrong filesystem. The same applies when a call carries no `host` and your configured
152
+ `tools.exec.host` default is `node` or `sandbox`: the call is still analysed as a local Gateway
153
+ call.
154
+ - **OpenClaw Codex-native relay.** Untested, and therefore unclaimed. The live end-to-end tests
155
+ drive OpenClaw's own agent runtime, so they say nothing about a Codex-native shell, patch, or
156
+ MCP call.
157
+ - **OpenClaw file tools.** `apply_patch` and OpenClaw's read/write/edit tools are not protected;
158
+ only `exec` is.
159
+ - **Windows.** Both integrations assume the POSIX layout. Relocated state is handled the way the
160
+ hosts resolve it — `HERMES_HOME` for Hermes, then `OPENCLAW_STATE_DIR` and the directory of
161
+ `OPENCLAW_CONFIG_PATH` for OpenClaw, falling back to `~/.hermes` and `~/.openclaw`. The Windows
162
+ defaults are not: install and detection target the POSIX path there, so Hermes writes to the
163
+ wrong directory and OpenClaw's own CLI installs correctly while `doctor` misreports the state.
349
164
 
350
- Run the diagnostic command to verify your installation and troubleshoot issues:
165
+ ## What it does
351
166
 
352
- ```bash
167
+ | Capability | What it catches |
168
+ |---|---|
169
+ | **Semantic command analysis** | `rm -rf` on destructive targets, `git reset --hard`, `git checkout --`, `git push --force`, `git stash clear`, `git clean -f`, unsafe `find -delete`, `dd`/`mkfs`/`shred` — by intent, not string pattern. `git checkout -b feature` (safe) is allowed while `git checkout -- file` (destructive) is blocked. |
170
+ | **Shell wrapper detection** | Destructive commands hidden in `bash -c`, `sh -c`, and similar wrappers, recursively analyzed up to 10 levels deep. |
171
+ | **Interpreter one-liners** | Destructive code in `python -c`, `node -e`, `ruby -e`, `perl -e` one-liners (e.g. `os.system("rm -rf /")`). |
172
+ | **Fail-closed by default** | Malformed hook input and unparseable commands (in strict mode) block rather than allow. Invalid config never blocks: an unverifiable rule source is dropped and an unreadable `policy.json` falls back to protective defaults, both with a warning on every reporting surface. |
173
+ | **Secret protection** | Content access to SSH keys, `.env` files, `~/.aws`, kube/docker/gcloud configs, and coding-CLI credential stores — enforced on shell commands and file tools (read/edit/write/search) alike. |
174
+ | **Custom rules via rulebooks** | Add your own blocking rules at user or project scope, pinned by SHA-256 digest when fetched from GitHub. |
175
+ | **Audit logging** | Allowed and blocked command decisions written to local per-project JSONL with secrets auto-redacted, retained for 30 days by default. Browse them with `npx cc-safety-net logs`, or triage them in the **Activity** view of `npx cc-safety-net gui`. |
176
+
177
+ Full rule catalogs: [Blocked Commands](https://ccsafetynet.com/docs/reference/blocked-commands) · [Allowed Commands](https://ccsafetynet.com/docs/reference/allowed-commands) · [Secret Protection](https://ccsafetynet.com/docs/reference/secret-protection).
178
+
179
+ ## Why not just use a sandbox?
180
+
181
+ A workspace-writable sandbox still permits `git reset --hard`, `git push --force`, and `rm -rf .` *inside* the project directory, because the OS only sees writes to an allowed path. Sandboxing contains blast radius; CC Safety Net catches the destructive operations sandboxing permits — use both for defense-in-depth. See [vs Sandboxing](https://ccsafetynet.com/docs/guides/vs-sandboxing).
182
+
183
+ ## Safety presets
184
+
185
+ Set a session safety preset with `CC_SAFETY_NET_LEVEL=standard|strict|paranoid`:
186
+
187
+ | Preset | Effect |
188
+ |---|---|
189
+ | Standard | Blocks recognizable destructive Git and filesystem commands. Allows metadata-only checks of built-in sensitive paths while continuing to block content access. Recommended for normal coding. |
190
+ | Strict | Standard, plus blocks dynamic or unparseable commands the analyzer cannot verify safely and metadata-only discovery of built-in sensitive paths. Occasional false positives on advanced shell. |
191
+ | Paranoid | Strict, plus blocks `rm -rf` inside your project and interpreter one-liners. Expect friction; for untrusted agents or high-stakes repos. |
192
+
193
+ Presets supply inherited defaults; `policy.json` stores only your explicit deviations — per-rule overrides, allow paths, deny paths, worktree mode, and audit retention. Environment variables can only raise protection, never lower it. The full contract is on [Modes](https://ccsafetynet.com/docs/configuration/modes), [Policy](https://ccsafetynet.com/docs/configuration/policy), and [Environment](https://ccsafetynet.com/docs/configuration/environment), or edit everything visually with the [local GUI](https://ccsafetynet.com/docs/guides/dashboard).
194
+
195
+ ## Diagnostics and tracing
196
+
197
+ ```bash
198
+ # Summarize what is being enforced right now
199
+ npx cc-safety-net status
200
+ # Verify your installation and run a self-test
353
201
  npx cc-safety-net doctor
354
- # or with bun
355
- bunx cc-safety-net doctor
356
- ```
357
-
358
- The doctor command checks:
359
-
360
- | Check | Description |
361
- |-------|-------------|
362
- | Hook Integration | Verifies the plugin is properly configured for each supported platform |
363
- | Self-Test | Runs sample commands to confirm blocking works correctly |
364
- | Configuration | Validates custom rules in user and project configs |
365
- | Environment | Shows status of mode flags (`CC_SAFETY_NET_STRICT`, `CC_SAFETY_NET_PARANOID`, etc.; legacy `SAFETY_NET_*` also listed when set) |
366
- | Recent Activity | Summarizes blocked commands from the last 7 days |
367
- | System Info | Displays versions of all relevant tools |
368
- | Update Check | Checks if a newer version is available |
369
-
370
- ### Options
371
-
372
- | Flag | Description |
373
- |------|-------------|
374
- | `--json` | Output in JSON format (useful for sharing in bug reports) |
375
- | `--skip-update-check` | Skip the npm version check |
376
-
377
- ## Explain (Debug Analysis)
378
-
379
- Trace how CC Safety Net analyzes a command step-by-step. Useful for debugging why a command is blocked or allowed, or when developing custom rules.
380
-
381
- ```bash
202
+ # Trace how a command is analyzed step-by-step
382
203
  npx cc-safety-net explain "git reset --hard"
383
- # or with bun
384
- bunx cc-safety-net explain "git reset --hard"
204
+ # Browse recorded denials from the audit trail (add --all to include allowed commands)
205
+ npx cc-safety-net logs
206
+ # Review what was blocked and edit your policy in a local web GUI
207
+ npx cc-safety-net gui
385
208
  ```
386
209
 
387
- ### Options
210
+ `doctor`, `explain`, and `logs` support `--json` for machine-readable output. The audit trail records command decisions only — never command output or prompts — and stays on your machine. Invalid configuration never blocks your agent: unverifiable rule sources are dropped and every degraded state is reported on the next block message, `doctor`, the status line, and the GUI banner.
388
211
 
389
- | Flag | Description |
390
- |------|-------------|
391
- | `--json` | Output analysis as JSON |
392
- | `--cwd <path>` | Use custom working directory for analysis |
212
+ Details: [CLI Commands](https://ccsafetynet.com/docs/reference/cli-commands) · [Explain Trace](https://ccsafetynet.com/docs/reference/explain-trace) · [Audit Log](https://ccsafetynet.com/docs/reference/audit-log) · [Dashboard](https://ccsafetynet.com/docs/guides/dashboard) · [Configuration Recovery](https://ccsafetynet.com/docs/configuration/recovery).
393
213
 
394
- ### Examples
214
+ ## Limitations
395
215
 
396
- ```bash
397
- npx cc-safety-net explain "rm -rf /"
398
- npx cc-safety-net explain --json "git checkout -- file.txt"
399
- npx cc-safety-net explain --cwd /tmp "git status"
400
- ```
216
+ CC Safety Net denies a tool call before it runs; it does not enforce filesystem permissions, inspect network egress, or contain a process. Two v2 bounds worth knowing up front: the policy and sensitive-path command extractors remain primarily POSIX-oriented, so native PowerShell path expressions such as `Get-Content $HOME\.ssh\id_rsa` can evade static path extraction; and policy-file protection is a best-effort exact-path guard, not command emulation. Use operating-system permissions, a sandbox, or equivalent runtime enforcement when complete protection is required.
401
217
 
402
- ## Commands Blocked
403
-
404
- | Command Pattern | Why It's Dangerous |
405
- |-----------------|-------------------|
406
- | git checkout -- files | Discards uncommitted changes permanently |
407
- | git checkout \<ref\> -- \<path\> | Overwrites working tree with ref version |
408
- | git checkout \<ref\> \<path\> | May overwrite working tree when Git disambiguates ref vs pathspec |
409
- | git restore files | Discards uncommitted changes |
410
- | git restore --worktree | Explicitly discards working tree changes |
411
- | git switch --discard-changes | Discards uncommitted changes when switching branches |
412
- | git switch --force / -f | Discards uncommitted changes (force switch) |
413
- | git reset --hard | Destroys all uncommitted changes |
414
- | git reset --merge | Can lose uncommitted changes |
415
- | git clean -f | Removes untracked files permanently |
416
- | git push --force / -f | Destroys remote history |
417
- | git branch -D | Force-deletes branch without merge check |
418
- | git stash drop | Permanently deletes stashed changes |
419
- | git stash clear | Deletes ALL stashed changes |
420
- | git worktree remove --force | Force-deletes worktree without checking for changes |
421
- | rm -rf (destructive targets) | Recursive file deletion of root, home, parent, absolute, or non-temp paths outside cwd |
422
- | rm -rf / or ~ or $HOME | Root/home deletion is extremely dangerous |
423
- | find ... -delete | Permanently removes files matching criteria |
424
- | xargs rm -rf | Dynamic input makes targets unpredictable |
425
- | xargs \<shell\> -c | Can execute arbitrary commands |
426
- | parallel rm -rf | Dynamic input makes targets unpredictable |
427
- | parallel \<shell\> -c | Can execute arbitrary commands |
428
- | dd writing to block devices | Can overwrite disks or partitions |
429
- | mkfs on block devices | Formats disks or partitions |
430
- | shred | Permanently destroys file contents |
431
-
432
- ## Commands Allowed
433
-
434
- | Command Pattern | Why It's Safe |
435
- |-----------------|--------------|
436
- | git checkout -b branch | Creates new branch |
437
- | git checkout --orphan | Creates orphan branch |
438
- | git restore --staged | Only unstages, doesn't discard |
439
- | git restore --help/--version | Help/version output |
440
- | git branch -d | Safe delete with merge check |
441
- | git clean -n / --dry-run | Preview only |
442
- | git push --force-with-lease | Safe force push |
443
- | rm -rf /tmp/... | Temp directories are ephemeral |
444
- | rm -rf /var/tmp/... | System temp directory |
445
- | rm -rf $TMPDIR/... | User's temp directory |
446
- | rm -rf ./... (within cwd) | Limited to current working directory |
447
- | git restore / checkout -- / reset --hard / clean -f (in linked worktree) | Relaxed only when `CC_SAFETY_NET_WORKTREE=1` and cwd is a linked worktree |
448
-
449
- ## What Happens When Blocked
450
-
451
- When a destructive command is detected, the plugin blocks the tool execution and provides a reason.
452
-
453
- Example output:
454
- ```text
455
- BLOCKED by CC Safety Net
456
-
457
- Reason: git checkout -- discards uncommitted changes permanently. Use 'git stash' first.
458
-
459
- Command: git checkout -- src/main.py
460
-
461
- If this operation is truly needed, ask the user for explicit permission and have them run the command manually.
462
- ```
218
+ The full residual-risk registry lives in [SECURITY.md](SECURITY.md); the practical consequences are on [Known Limitations](https://ccsafetynet.com/docs/guides/known-limitations).
463
219
 
464
- ## Testing the Hook
220
+ ## Upgrading from an older version
465
221
 
466
- You can manually test the hook by attempting to run blocked commands in Claude Code:
222
+ Upgrade every installed integration to the current release with one command:
467
223
 
468
224
  ```bash
469
- # This should be blocked
470
- git checkout -- README.md
471
-
472
- # This should be allowed
473
- git checkout -b test-branch
225
+ npx -y cc-safety-net@latest update
474
226
  ```
475
227
 
476
- ## Breaking Change: Custom Rules Migration
477
-
478
228
  > [!WARNING]
479
- > The custom rules system has moved from legacy inline config files to a rulebook-based layout. Legacy inline config files (`.safety-net.json` and `~/.cc-safety-net/config.json`) are **no longer loaded at runtime**. If they contain rules, commands now **fail closed** (stay blocked) until you migrate.
229
+ > If you previously defined custom rules in a legacy inline config (`.safety-net.json` or `~/.cc-safety-net/config.json`), those files are **no longer loaded at runtime** and **their rules are not enforcing anything**. Nothing is blocked, so you will not notice this from normal use — the commands those rules used to block now run. Run `npx -y cc-safety-net rule migrate` to convert them to the rulebook layout, then `npx -y cc-safety-net doctor` to confirm the runtime is `ready`. See the [migration guide](https://ccsafetynet.com/docs/configuration/custom-rules#migration-from-legacy-config).
480
230
 
481
- ### Who Is Affected
231
+ ## Full documentation
482
232
 
483
- - **Affected**: users who previously defined custom rules in `.safety-net.json` (project scope) or `~/.cc-safety-net/config.json` (user scope).
484
- - **Not affected**: users with no custom rules. All built-in destructive-command protections are unchanged and continue to work out of the box.
233
+ All details live on the docs site at **[ccsafetynet.com/docs](https://ccsafetynet.com/docs)**:
485
234
 
486
- ### What Breaks
487
-
488
- | Legacy file state | New behavior |
489
- |-------------------|--------------|
490
- | Empty legacy file | Silently ignored — built-in rules only |
491
- | Legacy file with rules | Fail closed until migrated with `rule migrate` |
492
- | Invalid legacy file | Fail closed until fixed and migrated, or removed |
493
-
494
- "Fail closed" means commands stay blocked until the legacy rules are migrated to the new layout.
495
-
496
- ### How to Migrate
497
-
498
- ```bash
499
- # Convert legacy inline rules into the new rulebook layout
500
- npx -y cc-safety-net rule migrate
501
-
502
- # Optionally delete verified legacy files after migration
503
- npx -y cc-safety-net rule migrate --cleanup
504
-
505
- # Validate the migrated rules
506
- npx -y cc-safety-net rule verify
507
- npx -y cc-safety-net rule test
508
- ```
509
-
510
- ### Before / After
511
-
512
- **Before** — a single inline config with rules embedded:
513
-
514
- ```text
515
- .safety-net.json # project rules (inline)
516
- ~/.cc-safety-net/config.json # user rules (inline)
517
- ```
518
-
519
- **After** — `rule migrate` creates a rulebook-based layout automatically:
520
-
521
- ```text
522
- .cc-safety-net/rules/rule.json # project rulebook sources + overrides
523
- .cc-safety-net/rules/project-rules/rulebook.json # migrated project rules
524
- ~/.cc-safety-net/rules/rule.json # user rulebook sources + overrides
525
- ~/.cc-safety-net/rules/user-rules/rulebook.json # migrated user rules
526
- ```
527
-
528
- See [Custom Rules](#custom-rules) for the full authoring guide and [Error Handling](#error-handling) for fail-closed details.
529
-
530
- ## Custom Rules
531
-
532
- Beyond the built-in protections, you can define your own blocking rules to enforce team conventions or project-specific safety policies.
533
-
534
- > [!TIP]
535
- > The best way to create custom rules is to use the `/cc-safety-net` skill to create custom rules interactively with natural language.
536
-
537
- ### Examples
538
-
539
- ```
540
- /cc-safety-net read my package.json and suggest blocking rules
541
- /cc-safety-net set up rules to block all terraform destroy commands
542
- /cc-safety-net verify my rules and fix any errors
543
- ```
544
-
545
- > [!NOTE]
546
- > If your agent does not support skills, prompt it with:
547
- > ```
548
- > run npx -y cc-safety-net rule doc and help me set up custom rules
549
- > ```
550
-
551
- ### Create Rules Manually
552
-
553
- Create a starter project rule config and rulebook:
554
-
555
- ```bash
556
- npx -y cc-safety-net rule init
557
- ```
558
-
559
- This creates `.cc-safety-net/rules/rule.json`:
560
-
561
- ```json
562
- {
563
- "version": 1,
564
- "rules": ["project-rules"],
565
- "overrides": {}
566
- }
567
- ```
568
-
569
- Rule definitions live in `.cc-safety-net/rules/project-rules/rulebook.json`:
570
-
571
- ```json
572
- {
573
- "rulebook_version": 1,
574
- "name": "project-rules",
575
- "version": "1.0.0",
576
- "description": "Project-specific CC Safety Net rules.",
577
- "author": "project",
578
- "allowed_commands": ["git"],
579
- "rules": [
580
- {
581
- "name": "block-git-add-all",
582
- "command": "git",
583
- "subcommand": "add",
584
- "block_args": ["-A", "--all", "."],
585
- "reason": "Use 'git add <specific-files>' instead of blanket add."
586
- }
587
- ],
588
- "tests": [
589
- {
590
- "command": "git add -A",
591
- "expect": "blocked",
592
- "rule": "block-git-add-all"
593
- },
594
- {
595
- "command": "git add README.md",
596
- "expect": "allowed"
597
- }
598
- ]
599
- }
600
- ```
601
-
602
- After editing rulebooks, run:
603
-
604
- ```bash
605
- npx -y cc-safety-net rule sync
606
- npx -y cc-safety-net rule verify
607
- npx -y cc-safety-net rule test
608
- ```
609
-
610
- Now `git add -A`, `git add --all`, and `git add .` will be blocked with your custom message.
611
-
612
- ### Config File Location
613
-
614
- Config files are loaded from two scopes and merged:
615
-
616
- 1. **User scope**: `~/.cc-safety-net/rules/rule.json` (use `rule init --global`)
617
- 2. **Project scope**: `.cc-safety-net/rules/rule.json` in the current working directory
618
-
619
- Local rulebook sources are bare names like `project-rules`. GitHub rulebook sources use `owner/repo#ref/<rulebook-name>` and point to `.cc-safety-net/rules/<rulebook-name>/rulebook.json` in that repository.
620
-
621
- Legacy inline config files (`.safety-net.json` and `~/.cc-safety-net/config.json`) are no longer loaded at runtime. Empty legacy files are ignored, but legacy files with rules and invalid legacy files fail closed until migrated or fixed. Convert existing legacy rules with `npx -y cc-safety-net rule migrate`; use `npx -y cc-safety-net rule migrate --cleanup` if you also want to delete verified legacy files after migration. See [Breaking Change: Custom Rules Migration](#breaking-change-custom-rules-migration) for the full upgrade guide.
622
-
623
- **Merging behavior**:
624
- - Rulebooks from both scopes are combined
625
- - Duplicate active rulebook names are invalid
626
- - Project overrides win over user overrides for the same `<rulebook-name>/<rule-name>` key
627
-
628
- This allows you to define personal defaults in user scope while letting projects disable or replace reasons for specific rules.
629
-
630
- If no config file is found in either location, only built-in rules apply.
631
-
632
- ### Config Schema
633
-
634
- | Field | Type | Required | Description |
635
- |-------|------|----------|-------------|
636
- | `version` | integer | Yes | Schema version (must be `1`) |
637
- | `rules` | array | No | List of rulebook source strings (defaults to empty) |
638
- | `overrides` | object | No | Rule overrides keyed by `<rulebook-name>/<rule-name>` |
639
-
640
- Override values are either `"off"` to disable a rule or `{ "reason": "..." }` to replace the rule reason.
641
-
642
- ### Rulebook Schema
643
-
644
- | Field | Type | Required | Description |
645
- |-------|------|----------|-------------|
646
- | `rulebook_version` | integer | Yes | Rulebook schema version (must be `1`) |
647
- | `name` | string | Yes | Rulebook name; must match the local directory name or GitHub source name |
648
- | `version` | string | Yes | Rulebook version |
649
- | `description` | string | No | Human-readable description |
650
- | `author` | string | No | Rulebook author |
651
- | `allowed_commands` | array | Yes | Commands this rulebook is allowed to define rules for |
652
- | `rules` | array | Yes | Custom blocking rules |
653
- | `tests` | array | Yes | Rulebook fixtures |
654
-
655
- ### Rule Schema
656
-
657
- | Field | Type | Required | Description |
658
- |-------|------|----------|-------------|
659
- | `name` | string | Yes | Unique within the rulebook (letters, numbers, hyphens, underscores; max 64 chars) |
660
- | `command` | string | Yes | Base command to match; must be listed in `allowed_commands` |
661
- | `subcommand` | string | No | Subcommand to match (e.g., `add`, `install`). If omitted, matches any. |
662
- | `block_args` | array | Yes | Arguments that trigger the block (at least one required) |
663
- | `reason` | string | Yes | Message shown when blocked (max 256 chars) |
664
-
665
- ### Fixture Schema
666
-
667
- | Field | Type | Required | Description |
668
- |-------|------|----------|-------------|
669
- | `command` | string | Yes | Shell command fixture |
670
- | `expect` | string | Yes | Either `blocked` or `allowed` |
671
- | `rule` | string | For blocked fixtures | Rule expected to block the command |
672
-
673
- Every rule must have at least one blocked fixture. Add allowed fixtures for close-but-safe commands.
674
-
675
- ### Matching Behavior
676
-
677
- - **Commands** are normalized to basename (`/usr/bin/git` → `git`)
678
- - **Subcommand** is the first non-option argument after the command
679
- - **Arguments** are matched literally (no regex, no glob), with short option expansion
680
- - A command is blocked if **any** argument in `block_args` is present
681
- - **Short options** are expanded: `-Ap` matches `-A` (bundled flags are unbundled)
682
- - **Long options** use exact match: `--all-files` does NOT match `--all`
683
- - Custom rules only add restrictions—they cannot bypass built-in protections
684
-
685
- #### Known Limitations
686
-
687
- - **Short option expansion**: `-Cfoo` is treated as `-C -f -o -o`, not `-C foo`. Blocking `-f` may false-positive on attached option values.
688
-
689
- ### Rule Examples
690
-
691
- #### Block global npm installs
692
-
693
- `.cc-safety-net/rules/rule.json`:
694
-
695
- ```json
696
- {
697
- "version": 1,
698
- "rules": ["project-rules"],
699
- "overrides": {}
700
- }
701
- ```
702
-
703
- `.cc-safety-net/rules/project-rules/rulebook.json`:
704
-
705
- ```json
706
- {
707
- "rulebook_version": 1,
708
- "name": "project-rules",
709
- "version": "1.0.0",
710
- "allowed_commands": ["npm"],
711
- "rules": [
712
- {
713
- "name": "block-npm-global",
714
- "command": "npm",
715
- "subcommand": "install",
716
- "block_args": ["-g", "--global"],
717
- "reason": "Global npm installs can cause version conflicts. Use npx or local install."
718
- }
719
- ],
720
- "tests": [
721
- {
722
- "command": "npm install -g typescript",
723
- "expect": "blocked",
724
- "rule": "block-npm-global"
725
- },
726
- {
727
- "command": "npm install typescript",
728
- "expect": "allowed"
729
- }
730
- ]
731
- }
732
- ```
733
-
734
- #### Block dangerous docker commands
735
-
736
- ```json
737
- {
738
- "rulebook_version": 1,
739
- "name": "project-rules",
740
- "version": "1.0.0",
741
- "allowed_commands": ["docker"],
742
- "rules": [
743
- {
744
- "name": "block-docker-system-prune",
745
- "command": "docker",
746
- "subcommand": "system",
747
- "block_args": ["prune"],
748
- "reason": "docker system prune removes all unused data. Use targeted cleanup instead."
749
- }
750
- ],
751
- "tests": [
752
- {
753
- "command": "docker system prune",
754
- "expect": "blocked",
755
- "rule": "block-docker-system-prune"
756
- },
757
- {
758
- "command": "docker ps",
759
- "expect": "allowed"
760
- }
761
- ]
762
- }
763
- ```
764
-
765
- #### Multiple rules
766
-
767
- ```json
768
- {
769
- "rulebook_version": 1,
770
- "name": "project-rules",
771
- "version": "1.0.0",
772
- "allowed_commands": ["git", "npm"],
773
- "rules": [
774
- {
775
- "name": "block-git-add-all",
776
- "command": "git",
777
- "subcommand": "add",
778
- "block_args": ["-A", "--all", ".", "-u", "--update"],
779
- "reason": "Use 'git add <specific-files>' instead of blanket add."
780
- },
781
- {
782
- "name": "block-npm-global",
783
- "command": "npm",
784
- "subcommand": "install",
785
- "block_args": ["-g", "--global"],
786
- "reason": "Use npx or local install instead of global."
787
- }
788
- ],
789
- "tests": [
790
- {
791
- "command": "git add -A",
792
- "expect": "blocked",
793
- "rule": "block-git-add-all"
794
- },
795
- {
796
- "command": "npm install -g typescript",
797
- "expect": "blocked",
798
- "rule": "block-npm-global"
799
- }
800
- ]
801
- }
802
- ```
803
-
804
- ### Error Handling
805
-
806
- Rulebook-backed custom rules fail closed when configured rulebooks cannot be loaded safely:
807
-
808
- | Scenario | Behavior |
809
- |----------|----------|
810
- | Config file not found | Silent — use built-in rules only |
811
- | Invalid rule config | Fail closed until fixed |
812
- | Empty legacy config | Silent — use built-in rules only |
813
- | Legacy config with rules and no migrated rule config | Fail closed until `rule migrate` creates the new rule config |
814
- | Invalid legacy config | Fail closed until fixed or removed |
815
- | Missing or stale lock/cache | Fail closed until `rule sync` repairs it |
816
- | Invalid local rulebook | Fail closed until the rulebook is fixed and synced |
817
- | Invalid GitHub rulebook | Fail closed until the source is fixed or removed |
818
-
819
-
820
- > [!IMPORTANT]
821
- > If you add or modify custom rules manually, always validate them with `npx -y cc-safety-net rule verify` and `npx -y cc-safety-net rule test`.
822
-
823
- ### Block Output Format
824
-
825
- When a custom rule blocks a command, the output includes the rule name:
826
-
827
- ```text
828
- BLOCKED by CC Safety Net
829
-
830
- Reason: [block-git-add-all] Use 'git add <specific-files>' instead of blanket add.
831
-
832
- Command: git add -A
833
- ```
834
-
835
- ## Advanced Features
836
-
837
- Mode and debug flags use **`CC_SAFETY_NET_*`** environment variables. Older **`SAFETY_NET_*`** names (without the `CC_` prefix) still work for strict, paranoid, and worktree toggles.
838
-
839
- ### Strict Mode
840
-
841
- Malformed or missing hook input JSON always fails closed. By default, ambiguous shell
842
- command parsing is allowed through. Enable strict mode to fail closed when a shell
843
- command cannot be safely analyzed (e.g., unterminated quotes or malformed `bash -c`
844
- wrappers):
845
-
846
- ```bash
847
- export CC_SAFETY_NET_STRICT=1
848
- ```
849
-
850
- ### Paranoid Mode
851
-
852
- Paranoid mode enables stricter safety checks that may be disruptive to normal workflows.
853
- You can enable it globally or via focused toggles:
854
-
855
- ```bash
856
- # Enable all paranoid checks
857
- export CC_SAFETY_NET_PARANOID=1
858
-
859
- # Or enable specific paranoid checks
860
- export CC_SAFETY_NET_PARANOID_RM=1
861
- export CC_SAFETY_NET_PARANOID_INTERPRETERS=1
862
- ```
863
-
864
- Paranoid behavior:
865
-
866
- - **rm**: blocks non-temp `rm -rf` even within the current working directory.
867
- - **interpreters**: blocks interpreter one-liners like `python -c`, `node -e`, `ruby -e`,
868
- and `perl -e` (these can hide destructive commands).
869
-
870
- ### Worktree Mode
871
-
872
- Linked git worktrees are designed as disposable, isolated workspaces — discarding
873
- changes inside one doesn't risk the main working tree. Worktree mode relaxes
874
- local-discard rules when (and only when) the command is proven to run inside a
875
- linked worktree:
876
-
877
- ```bash
878
- export CC_SAFETY_NET_WORKTREE=1
879
- ```
880
-
881
- When enabled, these commands are allowed inside a linked worktree:
882
-
883
- - `git restore <file>` and `git restore --worktree <file>`
884
- - `git checkout -- <file>`, `git checkout <ref> -- <file>`, `git checkout --force`,
885
- and ambiguous multi-positional checkout forms
886
- - `git switch --discard-changes` and `git switch -f / --force`
887
- - `git reset --hard` and `git reset --merge`
888
- - `git clean -f` (and combined short flags like `-fd`)
889
-
890
- These remain blocked even in linked worktrees because they reach beyond the
891
- local working tree:
892
-
893
- - `git push --force` (affects remote)
894
- - `git branch -D` (affects shared refs)
895
- - `git stash drop` / `git stash clear` (stash is shared across worktrees)
896
- - `git worktree remove --force` (could delete another worktree)
897
-
898
- Detection is fail-closed and mostly filesystem-based:
899
-
900
- - A linked worktree is identified by a `.git` *file* containing `gitdir:` whose
901
- resolved git directory contains a `commondir` file. Main worktrees and
902
- submodules don't satisfy this and are not relaxed.
903
- - The cwd walk uses `realpath` so symlinked paths resolve correctly.
904
- - `git -C <path>` (including chained `-C` and attached `-Cpath`) is honored;
905
- unresolved targets keep the command blocked.
906
- - Relaxation is disabled if cwd becomes unknown (e.g., after `cd`/`pushd`),
907
- if `--git-dir` / `--work-tree` is passed, or if `GIT_DIR` / `GIT_WORK_TREE`
908
- / `GIT_COMMON_DIR` is set in the environment.
909
- - Git may be invoked from a trusted system path to inspect effective config that
910
- could make submodule operations recursive.
911
-
912
- ### Shell Wrapper Detection
913
-
914
- The guard recursively analyzes commands wrapped in shells:
915
-
916
- ```bash
917
- bash -c 'git reset --hard' # Blocked
918
- sh -lc 'rm -rf /' # Blocked
919
- bash -c 'git stash drop' # Blocked
920
- ```
921
-
922
- ### Interpreter One-Liner Detection
923
-
924
- Detects destructive commands hidden in Python/Node/Ruby/Perl one-liners:
925
-
926
- ```bash
927
- python -c 'import os; os.system("rm -rf /")' # Blocked
928
- python -c 'import os; os.system("git stash drop")' # Blocked
929
- python -c 'import os; os.system("dd if=/dev/zero of=/dev/sda")' # Blocked
930
- python -c 'import os; os.system("mkfs.ext4 /dev/sda1")' # Blocked
931
- python -c 'import os; os.system("shred -u secret.txt")' # Blocked
932
- ```
933
-
934
- ### Secret Redaction
935
-
936
- Block messages automatically redact sensitive data (tokens, passwords, API keys) to prevent leaking secrets in logs.
937
-
938
- ### Audit Logging
939
-
940
- All blocked commands are logged to `~/.cc-safety-net/logs/<session_id>.jsonl` for audit purposes:
941
-
942
- ```json
943
- {"ts": "2025-01-15T10:30:00Z", "command": "git reset --hard", "segment": "git reset --hard", "reason": "...", "cwd": "/path/to/project"}
944
- ```
945
-
946
- Sensitive data in log entries is automatically redacted.
235
+ | Area | Pages |
236
+ |---|---|
237
+ | Get started | [Introduction](https://ccsafetynet.com/docs/introduction) · [Installation](https://ccsafetynet.com/docs/installation) · [Quickstart](https://ccsafetynet.com/docs/quickstart) · [How It Works](https://ccsafetynet.com/docs/guides/how-it-works) · [Dashboard](https://ccsafetynet.com/docs/guides/dashboard) |
238
+ | Configuration | [Modes](https://ccsafetynet.com/docs/configuration/modes) · [Policy](https://ccsafetynet.com/docs/configuration/policy) · [Environment](https://ccsafetynet.com/docs/configuration/environment) · [Custom Rules](https://ccsafetynet.com/docs/configuration/custom-rules) · [Status Line](https://ccsafetynet.com/docs/configuration/status-line) · [Configuration Recovery](https://ccsafetynet.com/docs/configuration/recovery) |
239
+ | Reference | [Blocked Commands](https://ccsafetynet.com/docs/reference/blocked-commands) · [Allowed Commands](https://ccsafetynet.com/docs/reference/allowed-commands) · [Secret Protection](https://ccsafetynet.com/docs/reference/secret-protection) · [Audit Log](https://ccsafetynet.com/docs/reference/audit-log) · [CLI Commands](https://ccsafetynet.com/docs/reference/cli-commands) · [Explain Trace](https://ccsafetynet.com/docs/reference/explain-trace) · [Glossary](https://ccsafetynet.com/docs/reference/glossary) |
240
+ | Guides | [Architecture](https://ccsafetynet.com/docs/guides/architecture) · [Analysis Engine](https://ccsafetynet.com/docs/guides/analysis-engine) · [Design Principles](https://ccsafetynet.com/docs/guides/design-principles) · [Security Model](https://ccsafetynet.com/docs/guides/security-model) · [vs Sandboxing](https://ccsafetynet.com/docs/guides/vs-sandboxing) · [Integration Architecture](https://ccsafetynet.com/docs/guides/integration-architecture) · [Known Limitations](https://ccsafetynet.com/docs/guides/known-limitations) · [Troubleshooting](https://ccsafetynet.com/docs/guides/troubleshooting) |
241
+ | Project | [Contributing](https://ccsafetynet.com/docs/contributing) · [Security Policy](https://ccsafetynet.com/docs/security) |
947
242
 
948
243
  ## Development
949
244
 
950
245
  See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to this project.
951
246
 
247
+ Generated distribution ownership is intentionally narrow. Only `dist/index.js`,
248
+ `dist/index.d.ts`, `dist/bin/cc-safety-net.js`, and `dist/pi/index.js` are tracked. Run
249
+ `bun run verify:build`, `bun run verify:package`, and `bun run verify:repository-plugin` when
250
+ changing packaging, integrations, or release automation.
251
+
952
252
  ## License
953
253
 
954
254
  MIT