cc-safety-net 0.9.0 → 1.0.1

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 (95) hide show
  1. package/README.md +352 -120
  2. package/dist/bin/cc-safety-net.js +7974 -4618
  3. package/dist/bin/commands/doctor.d.ts +11 -2
  4. package/dist/bin/commands/explain.d.ts +16 -2
  5. package/dist/bin/commands/hook.d.ts +14 -0
  6. package/dist/bin/commands/index.d.ts +67 -3
  7. package/dist/bin/commands/rule.d.ts +14 -0
  8. package/dist/bin/commands/statusline.d.ts +10 -2
  9. package/dist/bin/commands/types.d.ts +11 -0
  10. package/dist/bin/doctor/config.d.ts +1 -1
  11. package/dist/bin/doctor/hooks.d.ts +2 -1
  12. package/dist/bin/doctor/system-info.d.ts +6 -2
  13. package/dist/bin/doctor/types.d.ts +26 -1
  14. package/dist/bin/explain/config.d.ts +3 -1
  15. package/dist/bin/hook/common.d.ts +18 -0
  16. package/dist/bin/hook/config-edit.d.ts +11 -0
  17. package/dist/bin/hook/constants.d.ts +6 -0
  18. package/dist/bin/hook/install/kimi-cli.d.ts +3 -0
  19. package/dist/bin/hook/install/types.d.ts +4 -0
  20. package/dist/bin/hook/install.d.ts +3 -0
  21. package/dist/bin/hook/integrations.d.ts +12 -0
  22. package/dist/bin/hook/kimi-cli.d.ts +1 -0
  23. package/dist/bin/integration-metadata.d.ts +68 -0
  24. package/dist/bin/rule/doc.d.ts +1 -0
  25. package/dist/bin/rule/format.d.ts +14 -0
  26. package/dist/bin/rule/index.d.ts +1 -0
  27. package/dist/bin/rule/migrate.d.ts +6 -0
  28. package/dist/bin/rule/verify.d.ts +9 -0
  29. package/dist/builtin-commands/templates/cc-safety-net.d.ts +1 -0
  30. package/dist/core/analyze/analyze-command.d.ts +0 -10
  31. package/dist/core/analyze/awk.d.ts +3 -0
  32. package/dist/core/analyze/child-analyzer.d.ts +16 -0
  33. package/dist/core/analyze/child-command.d.ts +15 -0
  34. package/dist/core/analyze/find.d.ts +8 -6
  35. package/dist/core/analyze/index.d.ts +4 -0
  36. package/dist/core/analyze/parallel.d.ts +1 -0
  37. package/dist/core/{rules-rm.d.ts → analyze/rm.d.ts} +0 -1
  38. package/dist/core/analyze/shell-git-env.d.ts +10 -0
  39. package/dist/core/analyze/xargs.d.ts +1 -1
  40. package/dist/core/audit.d.ts +3 -0
  41. package/dist/core/config.d.ts +5 -3
  42. package/dist/core/env.d.ts +39 -1
  43. package/dist/core/format.d.ts +1 -0
  44. package/dist/core/git/config.d.ts +1 -0
  45. package/dist/core/git/env.d.ts +15 -0
  46. package/dist/core/git/index.d.ts +3 -0
  47. package/dist/core/git/parse.d.ts +9 -0
  48. package/dist/core/git/rules.d.ts +8 -0
  49. package/dist/core/git/worktree-relaxation.d.ts +11 -0
  50. package/dist/core/{worktree.d.ts → git/worktree.d.ts} +1 -5
  51. package/dist/core/rules/custom-rule-validation.d.ts +5 -0
  52. package/dist/core/rules/policy/config-file.d.ts +19 -0
  53. package/dist/core/rules/policy/index.d.ts +5 -0
  54. package/dist/core/rules/policy/lockfile.d.ts +5 -0
  55. package/dist/core/rules/policy/paths.d.ts +32 -0
  56. package/dist/core/rules/policy/resolver.d.ts +16 -0
  57. package/dist/core/rules/policy/scope-policy.d.ts +19 -0
  58. package/dist/core/rules/policy/sources.d.ts +28 -0
  59. package/dist/core/rules/policy/sync.d.ts +10 -0
  60. package/dist/core/rules/policy/types.d.ts +76 -0
  61. package/dist/core/rules/rulebook.d.ts +30 -0
  62. package/dist/core/shell/command.d.ts +2 -0
  63. package/dist/core/shell/index.d.ts +4 -0
  64. package/dist/core/shell/options.d.ts +3 -0
  65. package/dist/core/shell/segments.d.ts +6 -0
  66. package/dist/core/shell/shared.d.ts +4 -0
  67. package/dist/core/{shell.d.ts → shell/wrappers.d.ts} +0 -6
  68. package/dist/index.d.ts +1 -1
  69. package/dist/index.js +4205 -2231
  70. package/dist/{features → opencode}/builtin-commands/commands.d.ts +1 -1
  71. package/dist/opencode/builtin-commands/index.d.ts +2 -0
  72. package/dist/{features → opencode}/builtin-commands/types.d.ts +1 -1
  73. package/dist/pi/builtin-commands/commands.d.ts +16 -0
  74. package/dist/pi/builtin-commands/index.d.ts +1 -0
  75. package/dist/pi/index.d.ts +5 -0
  76. package/dist/pi/index.js +6432 -0
  77. package/dist/pi/tool-call.d.ts +21 -0
  78. package/dist/types.d.ts +32 -2
  79. package/package.json +16 -8
  80. package/dist/bin/commands/claude-code.d.ts +0 -2
  81. package/dist/bin/commands/copilot-cli.d.ts +0 -2
  82. package/dist/bin/commands/custom-rules-doc.d.ts +0 -2
  83. package/dist/bin/commands/gemini-cli.d.ts +0 -2
  84. package/dist/bin/commands/verify-config.d.ts +0 -2
  85. package/dist/bin/custom-rules-doc.d.ts +0 -1
  86. package/dist/bin/verify-config.d.ts +0 -12
  87. package/dist/core/analyze.d.ts +0 -21
  88. package/dist/core/rules-git.d.ts +0 -20
  89. package/dist/features/builtin-commands/index.d.ts +0 -2
  90. package/dist/features/builtin-commands/templates/set-custom-rules.d.ts +0 -1
  91. package/dist/features/builtin-commands/templates/verify-custom-rules.d.ts +0 -1
  92. /package/dist/bin/{hooks → hook}/claude-code.d.ts +0 -0
  93. /package/dist/bin/{hooks → hook}/copilot-cli.d.ts +0 -0
  94. /package/dist/bin/{hooks → hook}/gemini-cli.d.ts +0 -0
  95. /package/dist/core/{rules-custom.d.ts → rules/custom.d.ts} +0 -0
package/README.md CHANGED
@@ -1,13 +1,15 @@
1
- # Claude Code Safety Net
1
+ # CC Safety Net
2
2
 
3
- [![CI](https://github.com/kenryu42/claude-code-safety-net/actions/workflows/ci.yml/badge.svg)](https://github.com/kenryu42/claude-code-safety-net/actions/workflows/ci.yml)
4
- [![codecov](https://codecov.io/github/kenryu42/claude-code-safety-net/branch/main/graph/badge.svg?token=C9QTION6ZF)](https://codecov.io/github/kenryu42/claude-code-safety-net)
5
- [![Version](https://img.shields.io/github/v/tag/kenryu42/claude-code-safety-net?label=version&color=blue)](https://github.com/kenryu42/claude-code-safety-net)
3
+ [![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
+ [![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
+ [![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)
6
7
  [![Claude Code](https://img.shields.io/badge/Claude%20Code-D27656)](#claude-code-installation)
7
- [![OpenCode](https://img.shields.io/badge/OpenCode-black)](#opencode-installation)
8
- [![Gemini CLI](https://img.shields.io/badge/Gemini%20CLI-678AE3)](#gemini-cli-installation)
9
8
  [![Copilot CLI](https://img.shields.io/badge/Copilot%20CLI-4EA5C9)](#github-copilot-cli-installation)
10
- [![Codex](https://img.shields.io/badge/Codex-white)](#codex-installation)
9
+ [![Gemini CLI](https://img.shields.io/badge/Gemini%20CLI-678AE3)](#gemini-cli-installation)
10
+ [![Kimi CLI](https://img.shields.io/badge/Kimi%20CLI-5587FF)](#kimi-cli-installation)
11
+ [![OpenCode](https://img.shields.io/badge/OpenCode-black)](#opencode-installation)
12
+ [![Pi](https://img.shields.io/badge/Pi%20Coding-22262E)](#pi-installation)
11
13
  [![License: MIT](https://img.shields.io/badge/License-MIT-red.svg)](https://opensource.org/licenses/MIT)
12
14
 
13
15
  <div align="center">
@@ -16,7 +18,7 @@
16
18
 
17
19
  </div>
18
20
 
19
- A Claude Code plugin that acts as a safety net, catching destructive git and filesystem commands before they execute.
21
+ A Coding Agent CLI plugin that acts as a safety net, catching destructive git and filesystem commands before they execute.
20
22
 
21
23
  ## Contents
22
24
 
@@ -25,14 +27,14 @@ A Claude Code plugin that acts as a safety net, catching destructive git and fil
25
27
  - [What About Sandboxing?](#what-about-sandboxing)
26
28
  - [Prerequisites](#prerequisites)
27
29
  - [Quick Start](#quick-start)
30
+ - [Codex Installation](#codex-installation)
28
31
  - [Claude Code Installation](#claude-code-installation)
29
- - [OpenCode Installation](#opencode-installation)
30
32
  - [Gemini CLI Installation](#gemini-cli-installation)
31
33
  - [GitHub Copilot CLI Installation](#github-copilot-cli-installation)
32
- - [Codex Installation](#codex-installation)
34
+ - [Kimi CLI Installation](#kimi-cli-installation)
35
+ - [OpenCode Installation](#opencode-installation)
36
+ - [Pi Installation](#pi-installation)
33
37
  - [Status Line Integration](#status-line-integration)
34
- - [Setup via Slash Command](#setup-via-slash-command)
35
- - [Manual Setup](#manual-setup)
36
38
  - [Emoji Mode Indicators](#emoji-mode-indicators)
37
39
  - [Diagnostics](#diagnostics)
38
40
  - [Explain (Debug Analysis)](#explain-debug-analysis)
@@ -40,12 +42,12 @@ A Claude Code plugin that acts as a safety net, catching destructive git and fil
40
42
  - [Commands Allowed](#commands-allowed)
41
43
  - [What Happens When Blocked](#what-happens-when-blocked)
42
44
  - [Testing the Hook](#testing-the-hook)
43
- - [Development](#development)
44
- - [Custom Rules (Experimental)](#custom-rules-experimental)
45
+ - [Breaking Change: Custom Rules Migration](#breaking-change-custom-rules-migration)
46
+ - [Custom Rules](#custom-rules)
45
47
  - [Config File Location](#config-file-location)
46
48
  - [Rule Schema](#rule-schema)
47
49
  - [Matching Behavior](#matching-behavior)
48
- - [Examples](#examples)
50
+ - [Rule Examples](#rule-examples)
49
51
  - [Error Handling](#error-handling)
50
52
  - [Advanced Features](#advanced-features)
51
53
  - [Strict Mode](#strict-mode)
@@ -55,6 +57,7 @@ A Claude Code plugin that acts as a safety net, catching destructive git and fil
55
57
  - [Interpreter One-Liner Detection](#interpreter-one-liner-detection)
56
58
  - [Secret Redaction](#secret-redaction)
57
59
  - [Audit Logging](#audit-logging)
60
+ - [Development](#development)
58
61
  - [License](#license)
59
62
 
60
63
  ## Why This Exists
@@ -69,12 +72,12 @@ Claude Code's `.claude/settings.json` supports [deny rules](https://code.claude.
69
72
 
70
73
  ### At a Glance
71
74
 
72
- | | Permission Deny Rules | Safety Net |
75
+ | | Permission Deny Rules | CC Safety Net |
73
76
  |---|---|---|
74
77
  | **Setup** | Manual configuration required | Works out of the box |
75
78
  | **Parsing** | Wildcard pattern matching | Semantic command analysis |
76
79
  | **Execution order** | Runs second | Runs first (PreToolUse hook) |
77
- | **Shell wrappers** | Not handled automatically (must match wrapper forms) | Recursively analyzed (5 levels) |
80
+ | **Shell wrappers** | Not handled automatically (must match wrapper forms) | Recursively analyzed (up to 10 levels) |
78
81
  | **Interpreter one-liners** | Not handled automatically (must match interpreter forms) | Detected and blocked |
79
82
 
80
83
  ### Permission Rules Have Known Bypass Vectors
@@ -89,9 +92,9 @@ Even with wildcard matching, Bash permission patterns are intentionally limited
89
92
  | Extra whitespace | `rm -rf /` (double space) bypasses pattern |
90
93
  | Shell wrappers | `sh -c "rm -rf /"` bypasses `Bash(rm:*)` entirely |
91
94
 
92
- ### Safety Net Handles What Patterns Can't
95
+ ### CC Safety Net Handles What Patterns Can't
93
96
 
94
- | Scenario | Permission Rules | Safety Net |
97
+ | Scenario | Permission Rules | CC Safety Net |
95
98
  |----------|------------------|------------|
96
99
  | `git checkout -b feature` (safe) | Blocked by `Bash(git checkout:*)` | Allowed |
97
100
  | `git checkout -- file` (dangerous) | Blocked by `Bash(git checkout:*)` | Blocked |
@@ -102,17 +105,17 @@ Even with wildcard matching, Bash permission patterns are intentionally limited
102
105
 
103
106
  ### Defense in Depth
104
107
 
105
- PreToolUse hooks run [**before**](https://code.claude.com/docs/en/iam#additional-permission-control-with-hooks) the permission system. This means Safety Net inspects every command first, regardless of your permission configuration. Even if you misconfigure deny rules, Safety Net provides a fallback layer of protection.
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.
106
109
 
107
- **Use both together**: Permission deny rules for quick, user-configurable blocks; Safety Net for robust, bypass-resistant protection that works out of the box.
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.
108
111
 
109
112
  ## What About Sandboxing?
110
113
 
111
- 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 Safety Net:
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:
112
115
 
113
116
  ### Different Layers of Protection
114
117
 
115
- | | Sandboxing | Safety Net |
118
+ | | Sandboxing | CC Safety Net |
116
119
  |---|---|---|
117
120
  | **Enforcement** | OS-level (Seatbelt/bubblewrap) | Application-level (PreToolUse hook) |
118
121
  | **Approach** | Containment — restricts filesystem + network access | Command analysis — blocks destructive operations |
@@ -128,7 +131,7 @@ Sandboxing restricts filesystem + network access, but it doesn't understand whet
128
131
  > [!NOTE]
129
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`.
130
133
 
131
- | Command | Sandboxing | Safety Net |
134
+ | Command | Sandboxing | CC Safety Net |
132
135
  |---------|------------|------------|
133
136
  | `git reset --hard` | Allowed (within cwd) | **Blocked** |
134
137
  | `git checkout -- .` | Allowed (within cwd) | **Blocked** |
@@ -145,16 +148,16 @@ Sandboxing is the better choice when your primary concern is:
145
148
  - **Prompt injection attacks** — Reduces exfiltration risk by restricting outbound domains (depends on your allowed-domain policy)
146
149
  - **Malicious dependencies** — Limits filesystem writes and network access by default (subject to your sandbox configuration)
147
150
  - **Untrusted code execution** — OS-level containment is stronger than pattern matching
148
- - **Network control** — Safety Net has no network protection
151
+ - **Network control** — CC Safety Net has no network protection
149
152
 
150
153
  ### Recommended: Use Both
151
154
 
152
155
  They protect against different threats:
153
156
 
154
157
  - **Sandboxing** contains blast radius — even if something goes wrong, damage is limited to cwd and approved network domains
155
- - **Safety Net** prevents footguns — catches git-specific mistakes that are technically "safe" from the sandbox's perspective
158
+ - **CC Safety Net** prevents footguns — catches git-specific mistakes that are technically "safe" from the sandbox's perspective
156
159
 
157
- Running both together provides defense-in-depth. Sandboxing handles unknown threats; Safety Net handles known destructive patterns that sandboxing permits.
160
+ Running both together provides defense-in-depth. Sandboxing handles unknown threats; CC Safety Net handles known destructive patterns that sandboxing permits.
158
161
 
159
162
  ## Prerequisites
160
163
 
@@ -162,6 +165,29 @@ Running both together provides defense-in-depth. Sandboxing handles unknown thre
162
165
 
163
166
  ## Quick Start
164
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:
178
+
179
+ ```bash
180
+ codex plugin marketplace add kenryu42/cc-marketplace
181
+ ```
182
+
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.
188
+
189
+ ---
190
+
165
191
  ### Claude Code Installation
166
192
 
167
193
  ```bash
@@ -176,72 +202,75 @@ Running both together provides defense-in-depth. Sandboxing handles unknown thre
176
202
 
177
203
  ---
178
204
 
179
- ### OpenCode Installation
180
-
181
- **Option A: Let an LLM do it**
182
-
183
- Paste this into any LLM agent (Claude Code, OpenCode, Cursor, etc.):
205
+ ### Gemini CLI Installation
184
206
 
185
- ```
186
- Install the cc-safety-net plugin in `~/.config/opencode/opencode.json` (or `.jsonc`) according to the schema at: https://opencode.ai/config.json
207
+ ```bash
208
+ gemini extensions install https://github.com/kenryu42/gemini-safety-net
187
209
  ```
188
210
 
189
- **Option B: Manual setup**
211
+ ---
190
212
 
191
- 1. **Add the plugin to your config** `~/.config/opencode/opencode.json` (or `.jsonc`):
213
+ ### GitHub Copilot CLI Installation
192
214
 
193
- ```json
194
- {
195
- "plugin": ["cc-safety-net"]
196
- }
197
- ```
215
+ ```bash
216
+ /plugin install kenryu42/copilot-safety-net
217
+ ```
198
218
 
199
219
  ---
200
220
 
201
- ### Gemini CLI Installation
221
+ ### Kimi CLI Installation
222
+
223
+ Install CC Safety Net into your Kimi CLI config:
202
224
 
203
225
  ```bash
204
- gemini extensions install https://github.com/kenryu42/gemini-safety-net
226
+ npx -y cc-safety-net hook install --kimi-cli
205
227
  ```
206
228
 
207
229
  ---
208
230
 
209
- ### GitHub Copilot CLI Installation
231
+
232
+ ### OpenCode Installation
233
+
234
+ Install CC Safety Net with OpenCode's native plugin command:
210
235
 
211
236
  ```bash
212
- /plugin install kenryu42/copilot-safety-net
237
+ opencode plugin -g cc-safety-net
213
238
  ```
214
239
 
215
240
  > [!NOTE]
216
- > After installing the plugin, you need to restart your Copilot CLI for it to take effect.
241
+ > OpenCode can sometimes keep using a stale cached plugin version. See
242
+ > anomalyco/opencode#25293 for the current tracking issue.
243
+ >
244
+ > To force OpenCode to reinstall `cc-safety-net`, remove its cached package and
245
+ > install the version you want:
246
+ >
247
+ > ```sh
248
+ > rm -rf ~/.cache/opencode/packages/cc-safety-net@latest
249
+ > opencode plugin -g -f cc-safety-net@latest
250
+ >
251
+ > If you prefer pinning a specific version:
252
+ >
253
+ > rm -rf ~/.cache/opencode/packages/cc-safety-net@latest
254
+ > opencode plugin -g -f cc-safety-net@<version>
255
+ >
256
+ > Restart OpenCode after updating so the plugin is loaded from the refreshed
257
+ > cache.
217
258
 
218
259
  ---
219
260
 
220
- ### Codex Installation
221
-
222
- 1. Enable Codex plugin hooks in `~/.codex/config.toml`:
223
-
224
- ```toml
225
- [features]
226
- plugin_hooks = true
227
- ```
261
+ ### Pi Installation
228
262
 
229
- 2. Add the marketplace:
263
+ Install CC Safety Net with Pi's package installer:
230
264
 
231
- ```bash
232
- codex plugin marketplace add kenryu42/cc-marketplace
233
- ```
234
-
235
- 3. Start Codex.
236
- 4. In the TUI, run `/plugins`.
237
- 5. Use arrow keys to select `[cc-marketplace]`.
238
- 6. Press Enter to install the plugin.
265
+ ```bash
266
+ pi install npm:cc-safety-net
267
+ ```
239
268
 
240
269
  ---
241
270
 
242
271
  ## Status Line Integration
243
272
 
244
- Safety Net can display its status in Claude Code's status line, showing whether protection is active and which modes are enabled.
273
+ CC Safety Net can display its status in Claude Code's status line, showing whether protection is active and which modes are enabled.
245
274
 
246
275
  Add the following to your `~/.claude/settings.json`:
247
276
 
@@ -251,7 +280,7 @@ Add the following to your `~/.claude/settings.json`:
251
280
  {
252
281
  "statusLine": {
253
282
  "type": "command",
254
- "command": "bunx cc-safety-net --statusline"
283
+ "command": "bunx cc-safety-net statusline --claude-code"
255
284
  }
256
285
  }
257
286
  ```
@@ -262,7 +291,7 @@ Add the following to your `~/.claude/settings.json`:
262
291
  {
263
292
  "statusLine": {
264
293
  "type": "command",
265
- "command": "BUN_BE_BUN=1 claude x cc-safety-net --statusline"
294
+ "command": "BUN_BE_BUN=1 claude x cc-safety-net statusline --claude-code"
266
295
  }
267
296
  }
268
297
  ```
@@ -277,20 +306,20 @@ Add the following to your `~/.claude/settings.json`:
277
306
  {
278
307
  "statusLine": {
279
308
  "type": "command",
280
- "command": "npx -y cc-safety-net --statusline"
309
+ "command": "npx -y cc-safety-net statusline --claude-code"
281
310
  }
282
311
  }
283
312
  ```
284
313
 
285
314
  **Piping with existing status line:**
286
315
 
287
- If you already have a status line command, you can pipe Safety Net at the end:
316
+ If you already have a status line command, you can pipe CC Safety Net at the end:
288
317
 
289
318
  ```json
290
319
  {
291
320
  "statusLine": {
292
321
  "type": "command",
293
- "command": "your-existing-command | bunx cc-safety-net --statusline"
322
+ "command": "your-existing-command | bunx cc-safety-net statusline --claude-code"
294
323
  }
295
324
  }
296
325
  ```
@@ -303,16 +332,16 @@ The status line displays different emojis based on the current configuration:
303
332
 
304
333
  | Status | Display | Meaning |
305
334
  |--------|---------|---------|
306
- | Plugin disabled | `🛡️ Safety Net ❌` | Safety Net plugin is not enabled |
307
- | Default mode | `🛡️ Safety Net ✅` | Protection active with default settings |
308
- | Strict mode | `🛡️ Safety Net 🔒` | `SAFETY_NET_STRICT=1` — fail-closed on unparseable commands |
309
- | Paranoid mode | `🛡️ Safety Net 👁️` | `SAFETY_NET_PARANOID=1` — all paranoid checks enabled |
310
- | Paranoid RM only | `🛡️ Safety Net 🗑️` | `SAFETY_NET_PARANOID_RM=1` — blocks `rm -rf` even within cwd |
311
- | Paranoid interpreters only | `🛡️ Safety Net 🐚` | `SAFETY_NET_PARANOID_INTERPRETERS=1` — blocks interpreter one-liners |
312
- | Worktree mode | `🛡️ Safety Net 🌳` | `SAFETY_NET_WORKTREE=1` — relax local git discards inside linked worktrees |
313
- | Strict + Paranoid | `🛡️ Safety Net 🔒👁️` | Both strict and paranoid modes enabled |
335
+ | Plugin disabled | `🛡️ CC Safety Net ❌` | CC Safety Net plugin is not enabled |
336
+ | Default mode | `🛡️ CC Safety Net ✅` | Protection active with default settings |
337
+ | Strict mode | `🛡️ CC Safety Net 🔒` | `CC_SAFETY_NET_STRICT=1` — fail-closed on unparseable commands |
338
+ | Paranoid mode | `🛡️ CC Safety Net 👁️` | `CC_SAFETY_NET_PARANOID=1` — all paranoid checks enabled |
339
+ | Paranoid RM only | `🛡️ CC Safety Net 🗑️` | `CC_SAFETY_NET_PARANOID_RM=1` — blocks `rm -rf` even within cwd |
340
+ | Paranoid interpreters only | `🛡️ CC Safety Net 🐚` | `CC_SAFETY_NET_PARANOID_INTERPRETERS=1` — blocks interpreter one-liners |
341
+ | Worktree mode | `🛡️ CC Safety Net 🌳` | `CC_SAFETY_NET_WORKTREE=1` — relax local git discards inside linked worktrees |
342
+ | Strict + Paranoid | `🛡️ CC Safety Net 🔒👁️` | Both strict and paranoid modes enabled |
314
343
 
315
- Multiple mode emojis are combined when multiple environment variables are set.
344
+ 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.
316
345
 
317
346
  ## Diagnostics
318
347
 
@@ -331,7 +360,7 @@ The doctor command checks:
331
360
  | Hook Integration | Verifies the plugin is properly configured for each supported platform |
332
361
  | Self-Test | Runs sample commands to confirm blocking works correctly |
333
362
  | Configuration | Validates custom rules in user and project configs |
334
- | Environment | Shows status of mode flags (SAFETY_NET_STRICT, SAFETY_NET_PARANOID, etc.) |
363
+ | Environment | Shows status of mode flags (`CC_SAFETY_NET_STRICT`, `CC_SAFETY_NET_PARANOID`, etc.; legacy `SAFETY_NET_*` also listed when set) |
335
364
  | Recent Activity | Summarizes blocked commands from the last 7 days |
336
365
  | System Info | Displays versions of all relevant tools |
337
366
  | Update Check | Checks if a newer version is available |
@@ -345,7 +374,7 @@ The doctor command checks:
345
374
 
346
375
  ## Explain (Debug Analysis)
347
376
 
348
- Trace how Safety Net analyzes a command step-by-step. Useful for debugging why a command is blocked or allowed, or when developing custom rules.
377
+ 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.
349
378
 
350
379
  ```bash
351
380
  npx cc-safety-net explain "git reset --hard"
@@ -377,6 +406,8 @@ npx cc-safety-net explain --cwd /tmp "git status"
377
406
  | git checkout \<ref\> \<path\> | May overwrite working tree when Git disambiguates ref vs pathspec |
378
407
  | git restore files | Discards uncommitted changes |
379
408
  | git restore --worktree | Explicitly discards working tree changes |
409
+ | git switch --discard-changes | Discards uncommitted changes when switching branches |
410
+ | git switch --force / -f | Discards uncommitted changes (force switch) |
380
411
  | git reset --hard | Destroys all uncommitted changes |
381
412
  | git reset --merge | Can lose uncommitted changes |
382
413
  | git clean -f | Removes untracked files permanently |
@@ -385,13 +416,16 @@ npx cc-safety-net explain --cwd /tmp "git status"
385
416
  | git stash drop | Permanently deletes stashed changes |
386
417
  | git stash clear | Deletes ALL stashed changes |
387
418
  | git worktree remove --force | Force-deletes worktree without checking for changes |
388
- | rm -rf (paths outside cwd) | Recursive file deletion outside the current directory |
419
+ | rm -rf (destructive targets) | Recursive file deletion of root, home, parent, absolute, or non-temp paths outside cwd |
389
420
  | rm -rf / or ~ or $HOME | Root/home deletion is extremely dangerous |
390
421
  | find ... -delete | Permanently removes files matching criteria |
391
422
  | xargs rm -rf | Dynamic input makes targets unpredictable |
392
423
  | xargs \<shell\> -c | Can execute arbitrary commands |
393
424
  | parallel rm -rf | Dynamic input makes targets unpredictable |
394
425
  | parallel \<shell\> -c | Can execute arbitrary commands |
426
+ | dd writing to block devices | Can overwrite disks or partitions |
427
+ | mkfs on block devices | Formats disks or partitions |
428
+ | shred | Permanently destroys file contents |
395
429
 
396
430
  ## Commands Allowed
397
431
 
@@ -408,7 +442,7 @@ npx cc-safety-net explain --cwd /tmp "git status"
408
442
  | rm -rf /var/tmp/... | System temp directory |
409
443
  | rm -rf $TMPDIR/... | User's temp directory |
410
444
  | rm -rf ./... (within cwd) | Limited to current working directory |
411
- | git restore / checkout -- / reset --hard / clean -f (in linked worktree) | Relaxed only when `SAFETY_NET_WORKTREE=1` and cwd is a linked worktree |
445
+ | git restore / checkout -- / reset --hard / clean -f (in linked worktree) | Relaxed only when `CC_SAFETY_NET_WORKTREE=1` and cwd is a linked worktree |
412
446
 
413
447
  ## What Happens When Blocked
414
448
 
@@ -416,7 +450,7 @@ When a destructive command is detected, the plugin blocks the tool execution and
416
450
 
417
451
  Example output:
418
452
  ```text
419
- BLOCKED by Safety Net
453
+ BLOCKED by CC Safety Net
420
454
 
421
455
  Reason: git checkout -- discards uncommitted changes permanently. Use 'git stash' first.
422
456
 
@@ -437,29 +471,109 @@ git checkout -- README.md
437
471
  git checkout -b test-branch
438
472
  ```
439
473
 
440
- ## Development
474
+ ## Breaking Change: Custom Rules Migration
441
475
 
442
- See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to this project.
476
+ > [!WARNING]
477
+ > 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.
478
+
479
+ ### Who Is Affected
480
+
481
+ - **Affected**: users who previously defined custom rules in `.safety-net.json` (project scope) or `~/.cc-safety-net/config.json` (user scope).
482
+ - **Not affected**: users with no custom rules. All built-in destructive-command protections are unchanged and continue to work out of the box.
483
+
484
+ ### What Breaks
443
485
 
444
- ## Custom Rules (Experimental)
486
+ | Legacy file state | New behavior |
487
+ |-------------------|--------------|
488
+ | Empty legacy file | Silently ignored — built-in rules only |
489
+ | Legacy file with rules | Fail closed until migrated with `rule migrate` |
490
+ | Invalid legacy file | Fail closed until fixed and migrated, or removed |
491
+
492
+ "Fail closed" means commands stay blocked until the legacy rules are migrated to the new layout.
493
+
494
+ ### How to Migrate
495
+
496
+ ```bash
497
+ # Convert legacy inline rules into the new rulebook layout
498
+ npx -y cc-safety-net rule migrate
499
+
500
+ # Optionally delete verified legacy files after migration
501
+ npx -y cc-safety-net rule migrate --cleanup
502
+
503
+ # Validate the migrated rules
504
+ npx -y cc-safety-net rule verify
505
+ npx -y cc-safety-net rule test
506
+ ```
507
+
508
+ ### Before / After
509
+
510
+ **Before** — a single inline config with rules embedded:
511
+
512
+ ```text
513
+ .safety-net.json # project rules (inline)
514
+ ~/.cc-safety-net/config.json # user rules (inline)
515
+ ```
516
+
517
+ **After** — `rule migrate` creates a rulebook-based layout automatically:
518
+
519
+ ```text
520
+ .cc-safety-net/rules/rule.json # project rulebook sources + overrides
521
+ .cc-safety-net/rules/project-rules/rulebook.json # migrated project rules
522
+ ~/.cc-safety-net/rules/rule.json # user rulebook sources + overrides
523
+ ~/.cc-safety-net/rules/user-rules/rulebook.json # migrated user rules
524
+ ```
525
+
526
+ See [Custom Rules](#custom-rules) for the full authoring guide and [Error Handling](#error-handling) for fail-closed details.
527
+
528
+ ## Custom Rules
445
529
 
446
530
  Beyond the built-in protections, you can define your own blocking rules to enforce team conventions or project-specific safety policies.
447
531
 
448
532
  > [!TIP]
449
- > Use the `set-custom-rules` skill to create custom rules interactively with natural language.
450
- >
533
+ > The best way to create custom rules is to use the `/cc-safety-net` skill to create custom rules interactively with natural language.
534
+
535
+ ### Examples
536
+
537
+ ```
538
+ /cc-safety-net read my package.json and suggest blocking rules
539
+ /cc-safety-net set up rules to block all terraform destroy commands
540
+ /cc-safety-net verify my rules and fix any errors
541
+ ```
542
+
543
+ > [!NOTE]
451
544
  > If your agent does not support skills, prompt it with:
452
- > ```text
453
- > run npx cc-safety-net --custom-rules-doc and help me set up custom rules
545
+ > ```
546
+ > run npx -y cc-safety-net rule doc and help me set up custom rules
454
547
  > ```
455
548
 
456
- ### Quick Example
549
+ ### Create Rules Manually
457
550
 
458
- Create `.safety-net.json` in your project root:
551
+ Create a starter project rule config and rulebook:
552
+
553
+ ```bash
554
+ npx -y cc-safety-net rule init
555
+ ```
556
+
557
+ This creates `.cc-safety-net/rules/rule.json`:
459
558
 
460
559
  ```json
461
560
  {
462
561
  "version": 1,
562
+ "rules": ["project-rules"],
563
+ "overrides": {}
564
+ }
565
+ ```
566
+
567
+ Rule definitions live in `.cc-safety-net/rules/project-rules/rulebook.json`:
568
+
569
+ ```json
570
+ {
571
+ "rulebook_version": 1,
572
+ "name": "project-rules",
573
+ "version": "1.0.0",
574
+ "description": "Project-specific CC Safety Net rules.",
575
+ "author": "project",
576
+ "allowed_commands": ["git"],
463
577
  "rules": [
464
578
  {
465
579
  "name": "block-git-add-all",
@@ -468,25 +582,48 @@ Create `.safety-net.json` in your project root:
468
582
  "block_args": ["-A", "--all", "."],
469
583
  "reason": "Use 'git add <specific-files>' instead of blanket add."
470
584
  }
585
+ ],
586
+ "tests": [
587
+ {
588
+ "command": "git add -A",
589
+ "expect": "blocked",
590
+ "rule": "block-git-add-all"
591
+ },
592
+ {
593
+ "command": "git add README.md",
594
+ "expect": "allowed"
595
+ }
471
596
  ]
472
597
  }
473
598
  ```
474
599
 
600
+ After editing rulebooks, run:
601
+
602
+ ```bash
603
+ npx -y cc-safety-net rule sync
604
+ npx -y cc-safety-net rule verify
605
+ npx -y cc-safety-net rule test
606
+ ```
607
+
475
608
  Now `git add -A`, `git add --all`, and `git add .` will be blocked with your custom message.
476
609
 
477
610
  ### Config File Location
478
611
 
479
612
  Config files are loaded from two scopes and merged:
480
613
 
481
- 1. **User scope**: `~/.cc-safety-net/config.json` (always loaded if exists)
482
- 2. **Project scope**: `.safety-net.json` in the current working directory (loaded if exists)
614
+ 1. **User scope**: `~/.cc-safety-net/rules/rule.json` (use `rule init --global`)
615
+ 2. **Project scope**: `.cc-safety-net/rules/rule.json` in the current working directory
616
+
617
+ 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.
618
+
619
+ 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.
483
620
 
484
621
  **Merging behavior**:
485
- - Rules from both scopes are combined
486
- - If the same rule name exists in both scopes, **project scope wins**
487
- - Rule name comparison is case-insensitive (`MyRule` and `myrule` are considered duplicates)
622
+ - Rulebooks from both scopes are combined
623
+ - Duplicate active rulebook names are invalid
624
+ - Project overrides win over user overrides for the same `<rulebook-name>/<rule-name>` key
488
625
 
489
- This allows you to define personal defaults in user scope while letting projects override specific rules.
626
+ This allows you to define personal defaults in user scope while letting projects disable or replace reasons for specific rules.
490
627
 
491
628
  If no config file is found in either location, only built-in rules apply.
492
629
 
@@ -495,18 +632,44 @@ If no config file is found in either location, only built-in rules apply.
495
632
  | Field | Type | Required | Description |
496
633
  |-------|------|----------|-------------|
497
634
  | `version` | integer | Yes | Schema version (must be `1`) |
498
- | `rules` | array | No | List of custom blocking rules (defaults to empty) |
635
+ | `rules` | array | No | List of rulebook source strings (defaults to empty) |
636
+ | `overrides` | object | No | Rule overrides keyed by `<rulebook-name>/<rule-name>` |
637
+
638
+ Override values are either `"off"` to disable a rule or `{ "reason": "..." }` to replace the rule reason.
639
+
640
+ ### Rulebook Schema
641
+
642
+ | Field | Type | Required | Description |
643
+ |-------|------|----------|-------------|
644
+ | `rulebook_version` | integer | Yes | Rulebook schema version (must be `1`) |
645
+ | `name` | string | Yes | Rulebook name; must match the local directory name or GitHub source name |
646
+ | `version` | string | Yes | Rulebook version |
647
+ | `description` | string | No | Human-readable description |
648
+ | `author` | string | No | Rulebook author |
649
+ | `allowed_commands` | array | Yes | Commands this rulebook is allowed to define rules for |
650
+ | `rules` | array | Yes | Custom blocking rules |
651
+ | `tests` | array | Yes | Rulebook fixtures |
499
652
 
500
653
  ### Rule Schema
501
654
 
502
655
  | Field | Type | Required | Description |
503
656
  |-------|------|----------|-------------|
504
- | `name` | string | Yes | Unique identifier (letters, numbers, hyphens, underscores; max 64 chars) |
505
- | `command` | string | Yes | Base command to match (e.g., `git`, `npm`, `docker`) |
657
+ | `name` | string | Yes | Unique within the rulebook (letters, numbers, hyphens, underscores; max 64 chars) |
658
+ | `command` | string | Yes | Base command to match; must be listed in `allowed_commands` |
506
659
  | `subcommand` | string | No | Subcommand to match (e.g., `add`, `install`). If omitted, matches any. |
507
660
  | `block_args` | array | Yes | Arguments that trigger the block (at least one required) |
508
661
  | `reason` | string | Yes | Message shown when blocked (max 256 chars) |
509
662
 
663
+ ### Fixture Schema
664
+
665
+ | Field | Type | Required | Description |
666
+ |-------|------|----------|-------------|
667
+ | `command` | string | Yes | Shell command fixture |
668
+ | `expect` | string | Yes | Either `blocked` or `allowed` |
669
+ | `rule` | string | For blocked fixtures | Rule expected to block the command |
670
+
671
+ Every rule must have at least one blocked fixture. Add allowed fixtures for close-but-safe commands.
672
+
510
673
  ### Matching Behavior
511
674
 
512
675
  - **Commands** are normalized to basename (`/usr/bin/git` → `git`)
@@ -521,13 +684,28 @@ If no config file is found in either location, only built-in rules apply.
521
684
 
522
685
  - **Short option expansion**: `-Cfoo` is treated as `-C -f -o -o`, not `-C foo`. Blocking `-f` may false-positive on attached option values.
523
686
 
524
- ### Examples
687
+ ### Rule Examples
525
688
 
526
689
  #### Block global npm installs
527
690
 
691
+ `.cc-safety-net/rules/rule.json`:
692
+
528
693
  ```json
529
694
  {
530
695
  "version": 1,
696
+ "rules": ["project-rules"],
697
+ "overrides": {}
698
+ }
699
+ ```
700
+
701
+ `.cc-safety-net/rules/project-rules/rulebook.json`:
702
+
703
+ ```json
704
+ {
705
+ "rulebook_version": 1,
706
+ "name": "project-rules",
707
+ "version": "1.0.0",
708
+ "allowed_commands": ["npm"],
531
709
  "rules": [
532
710
  {
533
711
  "name": "block-npm-global",
@@ -536,6 +714,17 @@ If no config file is found in either location, only built-in rules apply.
536
714
  "block_args": ["-g", "--global"],
537
715
  "reason": "Global npm installs can cause version conflicts. Use npx or local install."
538
716
  }
717
+ ],
718
+ "tests": [
719
+ {
720
+ "command": "npm install -g typescript",
721
+ "expect": "blocked",
722
+ "rule": "block-npm-global"
723
+ },
724
+ {
725
+ "command": "npm install typescript",
726
+ "expect": "allowed"
727
+ }
539
728
  ]
540
729
  }
541
730
  ```
@@ -544,7 +733,10 @@ If no config file is found in either location, only built-in rules apply.
544
733
 
545
734
  ```json
546
735
  {
547
- "version": 1,
736
+ "rulebook_version": 1,
737
+ "name": "project-rules",
738
+ "version": "1.0.0",
739
+ "allowed_commands": ["docker"],
548
740
  "rules": [
549
741
  {
550
742
  "name": "block-docker-system-prune",
@@ -553,6 +745,17 @@ If no config file is found in either location, only built-in rules apply.
553
745
  "block_args": ["prune"],
554
746
  "reason": "docker system prune removes all unused data. Use targeted cleanup instead."
555
747
  }
748
+ ],
749
+ "tests": [
750
+ {
751
+ "command": "docker system prune",
752
+ "expect": "blocked",
753
+ "rule": "block-docker-system-prune"
754
+ },
755
+ {
756
+ "command": "docker ps",
757
+ "expect": "allowed"
758
+ }
556
759
  ]
557
760
  }
558
761
  ```
@@ -561,7 +764,10 @@ If no config file is found in either location, only built-in rules apply.
561
764
 
562
765
  ```json
563
766
  {
564
- "version": 1,
767
+ "rulebook_version": 1,
768
+ "name": "project-rules",
769
+ "version": "1.0.0",
770
+ "allowed_commands": ["git", "npm"],
565
771
  "rules": [
566
772
  {
567
773
  "name": "block-git-add-all",
@@ -577,33 +783,47 @@ If no config file is found in either location, only built-in rules apply.
577
783
  "block_args": ["-g", "--global"],
578
784
  "reason": "Use npx or local install instead of global."
579
785
  }
786
+ ],
787
+ "tests": [
788
+ {
789
+ "command": "git add -A",
790
+ "expect": "blocked",
791
+ "rule": "block-git-add-all"
792
+ },
793
+ {
794
+ "command": "npm install -g typescript",
795
+ "expect": "blocked",
796
+ "rule": "block-npm-global"
797
+ }
580
798
  ]
581
799
  }
582
800
  ```
583
801
 
584
802
  ### Error Handling
585
803
 
586
- Custom rules use **silent fallback** error handling. If your config file is invalid, the safety net silently falls back to built-in rules only:
804
+ Rulebook-backed custom rules fail closed when configured rulebooks cannot be loaded safely:
587
805
 
588
806
  | Scenario | Behavior |
589
807
  |----------|----------|
590
808
  | Config file not found | Silent — use built-in rules only |
591
- | Empty config file | Silent use built-in rules only |
592
- | Invalid JSON syntax | Silent — use built-in rules only |
593
- | Missing required field | Silent use built-in rules only |
594
- | Invalid field format | Silent use built-in rules only |
595
- | Duplicate rule name | Silent use built-in rules only |
809
+ | Invalid rule config | Fail closed until fixed |
810
+ | Empty legacy config | Silent — use built-in rules only |
811
+ | Legacy config with rules and no migrated rule config | Fail closed until `rule migrate` creates the new rule config |
812
+ | Invalid legacy config | Fail closed until fixed or removed |
813
+ | Missing or stale lock/cache | Fail closed until `rule sync` repairs it |
814
+ | Invalid local rulebook | Fail closed until the rulebook is fixed and synced |
815
+ | Invalid GitHub rulebook | Fail closed until the source is fixed or removed |
596
816
 
597
817
 
598
818
  > [!IMPORTANT]
599
- > If you add or modify custom rules manually, always validate them with `npx -y cc-safety-net --verify-config` or the `verify-custom-rules` skill in your coding agent.
819
+ > 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`.
600
820
 
601
821
  ### Block Output Format
602
822
 
603
823
  When a custom rule blocks a command, the output includes the rule name:
604
824
 
605
825
  ```text
606
- BLOCKED by Safety Net
826
+ BLOCKED by CC Safety Net
607
827
 
608
828
  Reason: [block-git-add-all] Use 'git add <specific-files>' instead of blanket add.
609
829
 
@@ -612,14 +832,17 @@ Command: git add -A
612
832
 
613
833
  ## Advanced Features
614
834
 
835
+ 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.
836
+
615
837
  ### Strict Mode
616
838
 
617
- By default, unparseable commands are allowed through. Enable strict mode to fail-closed
618
- when the hook input or shell command cannot be safely analyzed (e.g., invalid JSON,
619
- unterminated quotes, malformed `bash -c` wrappers):
839
+ Malformed or missing hook input JSON always fails closed. By default, ambiguous shell
840
+ command parsing is allowed through. Enable strict mode to fail closed when a shell
841
+ command cannot be safely analyzed (e.g., unterminated quotes or malformed `bash -c`
842
+ wrappers):
620
843
 
621
844
  ```bash
622
- export SAFETY_NET_STRICT=1
845
+ export CC_SAFETY_NET_STRICT=1
623
846
  ```
624
847
 
625
848
  ### Paranoid Mode
@@ -629,11 +852,11 @@ You can enable it globally or via focused toggles:
629
852
 
630
853
  ```bash
631
854
  # Enable all paranoid checks
632
- export SAFETY_NET_PARANOID=1
855
+ export CC_SAFETY_NET_PARANOID=1
633
856
 
634
857
  # Or enable specific paranoid checks
635
- export SAFETY_NET_PARANOID_RM=1
636
- export SAFETY_NET_PARANOID_INTERPRETERS=1
858
+ export CC_SAFETY_NET_PARANOID_RM=1
859
+ export CC_SAFETY_NET_PARANOID_INTERPRETERS=1
637
860
  ```
638
861
 
639
862
  Paranoid behavior:
@@ -650,7 +873,7 @@ local-discard rules when (and only when) the command is proven to run inside a
650
873
  linked worktree:
651
874
 
652
875
  ```bash
653
- export SAFETY_NET_WORKTREE=1
876
+ export CC_SAFETY_NET_WORKTREE=1
654
877
  ```
655
878
 
656
879
  When enabled, these commands are allowed inside a linked worktree:
@@ -691,6 +914,7 @@ The guard recursively analyzes commands wrapped in shells:
691
914
  ```bash
692
915
  bash -c 'git reset --hard' # Blocked
693
916
  sh -lc 'rm -rf /' # Blocked
917
+ bash -c 'git stash drop' # Blocked
694
918
  ```
695
919
 
696
920
  ### Interpreter One-Liner Detection
@@ -699,6 +923,10 @@ Detects destructive commands hidden in Python/Node/Ruby/Perl one-liners:
699
923
 
700
924
  ```bash
701
925
  python -c 'import os; os.system("rm -rf /")' # Blocked
926
+ python -c 'import os; os.system("git stash drop")' # Blocked
927
+ python -c 'import os; os.system("dd if=/dev/zero of=/dev/sda")' # Blocked
928
+ python -c 'import os; os.system("mkfs.ext4 /dev/sda1")' # Blocked
929
+ python -c 'import os; os.system("shred -u secret.txt")' # Blocked
702
930
  ```
703
931
 
704
932
  ### Secret Redaction
@@ -715,6 +943,10 @@ All blocked commands are logged to `~/.cc-safety-net/logs/<session_id>.jsonl` fo
715
943
 
716
944
  Sensitive data in log entries is automatically redacted.
717
945
 
946
+ ## Development
947
+
948
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to this project.
949
+
718
950
  ## License
719
951
 
720
952
  MIT