@aliou/pi-guardrails 0.7.7 → 0.8.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.
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # Guardrails
2
2
 
3
- Security hooks to prevent potentially dangerous operations.
3
+ Security hooks for Pi to reduce accidental destructive actions and secret-file access.
4
4
 
5
5
  ## Demo
6
6
 
7
- <video src="https://assets.aliou.me/pi-extensions/2026-01-26-guardrails-demo.mp4" controls playsinline muted></video>
7
+ <video src="https://assets.aliou.me/pi-extensions/demos/pi-guardrails.mp4" controls playsinline muted></video>
8
8
 
9
- ## Installation
9
+ ## Install
10
10
 
11
11
  ```bash
12
12
  pi install npm:@aliou/pi-guardrails
@@ -18,66 +18,57 @@ Or from git:
18
18
  pi install git:github.com/aliou/pi-guardrails
19
19
  ```
20
20
 
21
- ## Features
21
+ ## What it does
22
22
 
23
- - **protect-env-files**: Prevents access to `.env` files (except `.example`/`.sample`/`.test`)
24
- - **permission-gate**: Prompts for confirmation on dangerous commands
23
+ - **policies**: named file-protection rules with per-rule protection levels.
24
+ - **permission-gate**: detects dangerous bash commands and asks for confirmation.
25
+ - **optional command explainer**: can call a small LLM to explain a dangerous command inline in the confirmation dialog.
25
26
 
26
- All hooks use structural shell parsing via `@aliou/sh` to avoid false positives from keywords inside commit messages, grep patterns, heredocs, or file paths. On parse failure, each hook falls back to regex matching (previous behavior).
27
+ ## Config locations
27
28
 
28
- > **Migration note**: The `preventBrew`, `preventPython`, `enforcePackageManager`, and `packageManager` fields have been removed from guardrails and moved to the [`@aliou/pi-toolchain`](https://github.com/aliou/pi-toolchain) extension. Old configs containing these fields are auto-cleaned on first load with a one-time warning. Install `@aliou/pi-toolchain` and configure `.pi/extensions/toolchain.json` instead.
29
+ Guardrails reads and merges config from:
29
30
 
30
- ## Configuration
31
+ - Global: `~/.pi/agent/extensions/guardrails.json`
32
+ - Project: `.pi/extensions/guardrails.json`
33
+ - Memory (session): internal temporary scope used by settings/commands
31
34
 
32
- Configuration is loaded from two optional JSON files, merged in order (project overrides global):
35
+ Priority: `memory > local > global > defaults`.
33
36
 
34
- - **Global**: `~/.pi/agent/extensions/guardrails.json`
35
- - **Project**: `.pi/extensions/guardrails.json`
37
+ Use `/guardrails:settings` to edit config interactively.
36
38
 
37
- ### Settings Command
38
-
39
- Run `/guardrails:settings` to open an interactive settings UI with two tabs:
40
- - **Local**: edit project-scoped config (`.pi/extensions/guardrails.json`)
41
- - **Global**: edit global config (`~/.pi/agent/extensions/guardrails.json`)
42
-
43
- Use `Tab` / `Shift+Tab` to switch tabs. Boolean settings can be toggled directly.
44
-
45
- ### Migration from v0
46
-
47
- Configs without a `version` field are automatically migrated on first load. The migration:
48
- - Backs up the original as `guardrails.v0.json`
49
- - Converts all string patterns to `{ pattern, regex: true }` to preserve behavior
50
- - Adds a `version` field
51
-
52
- ### Configuration Schema
39
+ ## Current schema
53
40
 
54
41
  ```json
55
42
  {
56
43
  "enabled": true,
57
44
  "features": {
58
- "protectEnvFiles": true,
45
+ "policies": true,
59
46
  "permissionGate": true
60
47
  },
61
- "envFiles": {
62
- "protectedPatterns": [
63
- { "pattern": ".env" },
64
- { "pattern": ".env.local" },
65
- { "pattern": ".env.production" },
66
- { "pattern": ".env.prod" },
67
- { "pattern": ".dev.vars" }
68
- ],
69
- "allowedPatterns": [
70
- { "pattern": ".env.example" },
71
- { "pattern": ".env.sample" },
72
- { "pattern": ".env.test" },
73
- { "pattern": "*.example.env" },
74
- { "pattern": "*.sample.env" },
75
- { "pattern": "*.test.env" }
76
- ],
77
- "protectedDirectories": [],
78
- "protectedTools": ["read", "write", "edit", "bash", "grep", "find", "ls"],
79
- "onlyBlockIfExists": true,
80
- "blockMessage": "Accessing {file} is not allowed. ..."
48
+ "policies": {
49
+ "rules": [
50
+ {
51
+ "id": "secret-files",
52
+ "description": "Files containing secrets",
53
+ "patterns": [
54
+ { "pattern": ".env" },
55
+ { "pattern": ".env.local" },
56
+ { "pattern": ".env.production" },
57
+ { "pattern": ".env.prod" },
58
+ { "pattern": ".dev.vars" }
59
+ ],
60
+ "allowedPatterns": [
61
+ { "pattern": ".env.example" },
62
+ { "pattern": ".env.sample" },
63
+ { "pattern": ".env.test" },
64
+ { "pattern": "*.example.env" },
65
+ { "pattern": "*.sample.env" },
66
+ { "pattern": "*.test.env" }
67
+ ],
68
+ "protection": "noAccess",
69
+ "onlyIfExists": true
70
+ }
71
+ ]
81
72
  },
82
73
  "permissionGate": {
83
74
  "patterns": [
@@ -87,110 +78,93 @@ Configs without a `version` field are automatically migrated on first load. The
87
78
  "customPatterns": [],
88
79
  "requireConfirmation": true,
89
80
  "allowedPatterns": [],
90
- "autoDenyPatterns": []
81
+ "autoDenyPatterns": [],
82
+ "explainCommands": false,
83
+ "explainModel": null,
84
+ "explainTimeout": 5000
91
85
  }
92
86
  }
93
87
  ```
94
88
 
95
- All fields are optional. Missing fields use defaults shown above. The default patterns listed here may change between versions. Check the source code or run `/guardrails:settings` to see the current defaults and update them to your liking.
89
+ All fields optional. Missing fields use defaults.
96
90
 
97
- ### Pattern Format
91
+ ## Policies
98
92
 
99
- Patterns support two modes controlled by the `regex` flag:
93
+ Each rule has:
100
94
 
101
- **File patterns** (envFiles section):
102
- - Default (`regex` omitted or `false`): glob matching against the filename. `*` matches any non-`/` chars, `?` matches a single char. Example: `.env.*` matches `.env.local`, `.env.production`.
103
- - `regex: true`: full regex (case-insensitive) against the full path. Example: `{ "pattern": "\\.env$", "regex": true }`.
95
+ - `id`: stable identifier used for overrides across scopes.
96
+ - `patterns`: files to match (glob by default, regex if `regex: true`).
97
+ - `allowedPatterns`: exceptions.
98
+ - `protection`:
99
+ - `noAccess`: block `read`, `write`, `edit`, `bash`, `grep`, `find`, `ls`
100
+ - `readOnly`: block `write`, `edit`, `bash`
101
+ - `none`: explicit no protection
102
+ - `onlyIfExists` (default true)
103
+ - `blockMessage` with `{file}` placeholder
104
+ - `enabled` (default true)
104
105
 
105
- **Command patterns** (permissionGate section):
106
- - Default (`regex` omitted or `false`): substring matching against the raw command string. Example: `"rm -rf"` matches any command containing `rm -rf`.
107
- - `regex: true`: full regex against the raw command string. Example: `{ "pattern": "rm\\s+-rf", "regex": true }`.
106
+ When multiple rules match the same file, strongest protection wins:
107
+ `noAccess > readOnly > none`.
108
108
 
109
- Built-in dangerous command patterns (`rm -rf`, `sudo`, `dd if=`, `mkfs.*`, `chmod -R 777`, `chown -R`) are matched structurally via AST parsing, independent of the pattern format.
109
+ ### Add rule with AI
110
110
 
111
- ### Configuration Details
111
+ Use:
112
112
 
113
- #### `features`
113
+ ```text
114
+ /guardrails:add-policy
115
+ ```
114
116
 
115
- | Key | Default | Description |
116
- |---|---|---|
117
- | `protectEnvFiles` | `true` | Block access to `.env` files containing secrets |
118
- | `permissionGate` | `true` | Prompt for confirmation on dangerous commands |
117
+ This starts a subagent that helps build and save one policy rule.
119
118
 
120
- #### `envFiles`
119
+ ## Permission gate
121
120
 
122
- | Key | Default | Description |
123
- |---|---|---|
124
- | `protectedPatterns` | `[".env", ".env.local", ...]` | Patterns for files to protect (glob by default) |
125
- | `allowedPatterns` | `[".env.example", "*.example.env", ...]` | Patterns for allowed exceptions |
126
- | `protectedDirectories` | `[]` | Patterns for directories to protect |
127
- | `protectedTools` | `["read", "write", "edit", "bash", "grep", "find", "ls"]` | Tools to intercept |
128
- | `onlyBlockIfExists` | `true` | Only block if the file exists on disk |
129
- | `blockMessage` | See defaults | Message shown when blocked. Supports `{file}` placeholder |
121
+ Detects dangerous bash commands and prompts user confirmation.
130
122
 
131
- #### `permissionGate`
123
+ Built-in dangerous patterns are matched structurally (AST-based) for better accuracy:
132
124
 
133
- | Key | Default | Description |
134
- |---|---|---|
135
- | `patterns` | See defaults | Array of `{ pattern, description }` for dangerous commands |
136
- | `customPatterns` | Not set | If set, replaces `patterns` entirely |
137
- | `requireConfirmation` | `true` | Show confirmation dialog (if `false`, just warns) |
138
- | `allowedPatterns` | `[]` | Patterns that bypass the gate |
139
- | `autoDenyPatterns` | `[]` | Patterns that are blocked immediately without dialog |
125
+ - `rm -rf`
126
+ - `sudo`
127
+ - `dd if=`
128
+ - `mkfs.`
129
+ - `chmod -R 777`
130
+ - `chown -R`
140
131
 
141
- ### Examples
132
+ You can also add custom dangerous patterns.
142
133
 
143
- Add a custom dangerous command pattern (substring match):
134
+ ### Explain commands (opt-in)
144
135
 
145
- ```json
146
- {
147
- "permissionGate": {
148
- "patterns": [
149
- { "pattern": "rm -rf", "description": "recursive force delete" },
150
- { "pattern": "sudo", "description": "superuser command" },
151
- { "pattern": "docker system prune", "description": "docker system prune" }
152
- ]
153
- }
154
- }
155
- ```
136
+ If enabled, guardrails calls an LLM before showing the confirmation dialog and displays a short explanation.
156
137
 
157
- Add a regex-based pattern:
138
+ Config fields:
158
139
 
159
- ```json
160
- {
161
- "permissionGate": {
162
- "patterns": [
163
- { "pattern": "rm\\s+-rf\\s+/(?!tmp)", "description": "rm -rf outside /tmp", "regex": true }
164
- ]
165
- }
166
- }
167
- ```
140
+ - `permissionGate.explainCommands` (boolean)
141
+ - `permissionGate.explainModel` (`provider/model-id`)
142
+ - `permissionGate.explainTimeout` (ms)
168
143
 
169
- Protect env files with glob patterns:
144
+ Failures/timeouts degrade gracefully: dialog still shows without explanation.
170
145
 
171
- ```json
172
- {
173
- "envFiles": {
174
- "protectedPatterns": [
175
- { "pattern": ".env" },
176
- { "pattern": ".env.*" },
177
- { "pattern": ".dev.vars" }
178
- ]
179
- }
180
- }
181
- ```
146
+ ## Migration notes
147
+
148
+ Legacy fields are auto-migrated:
149
+
150
+ - `features.protectEnvFiles` -> `features.policies`
151
+ - `envFiles` -> `policies.rules` (migrated into `secret-files`)
152
+
153
+ `config.version` is a schema marker, not npm package version.
154
+
155
+ Also note:
156
+
157
+ - `preventBrew`, `preventPython`, `enforcePackageManager`, `packageManager` were removed from guardrails and moved to `@aliou/pi-toolchain`.
182
158
 
183
159
  ## Events
184
160
 
185
- The extension emits events on the pi event bus for inter-extension communication.
161
+ Guardrails emits events for other extensions:
186
162
 
187
163
  ### `guardrails:blocked`
188
164
 
189
- Emitted when a tool call is blocked by any guardrail.
190
-
191
- ```typescript
165
+ ```ts
192
166
  interface GuardrailsBlockedEvent {
193
- feature: "protectEnvFiles" | "permissionGate";
167
+ feature: "policies" | "permissionGate";
194
168
  toolName: string;
195
169
  input: Record<string, unknown>;
196
170
  reason: string;
@@ -200,36 +174,10 @@ interface GuardrailsBlockedEvent {
200
174
 
201
175
  ### `guardrails:dangerous`
202
176
 
203
- Emitted when a dangerous command is detected (before the confirmation dialog).
204
-
205
- ```typescript
177
+ ```ts
206
178
  interface GuardrailsDangerousEvent {
207
179
  command: string;
208
180
  description: string;
209
181
  pattern: string;
210
182
  }
211
183
  ```
212
-
213
- The [presenter extension](https://github.com/aliou/pi-extensions/tree/main/extensions/presenter) listens for `guardrails:dangerous` events and plays a notification sound.
214
-
215
- ## Hooks
216
-
217
- ### protect-env-files
218
-
219
- Prevents accessing `.env` files that might contain secrets. Only allows access to safe variants like `.env.example`, `.env.sample`, `.env.test`.
220
-
221
- Shell globs (e.g. `.env*`) are expanded via `fd` to check if any expanded path matches a protected pattern.
222
-
223
- Covers tools: `read`, `write`, `edit`, `bash`, `grep`, `find`, `ls` (configurable).
224
-
225
- ### permission-gate
226
-
227
- Prompts user confirmation before executing dangerous commands:
228
- - `rm -rf` (recursive force delete)
229
- - `sudo` (superuser command)
230
- - `dd if=` (disk write operation)
231
- - `mkfs.` (filesystem format)
232
- - `chmod -R 777` (insecure recursive permissions)
233
- - `chown -R` (recursive ownership change)
234
-
235
- Built-in patterns are matched structurally (AST-based). Custom patterns use substring or regex matching. Supports allow-lists and auto-deny lists.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aliou/pi-guardrails",
3
- "version": "0.7.7",
3
+ "version": "0.8.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "private": false,
@@ -29,17 +29,21 @@
29
29
  "README.md"
30
30
  ],
31
31
  "dependencies": {
32
- "@aliou/pi-utils-settings": "^0.4.0",
32
+ "@aliou/pi-utils-settings": "^0.7.0",
33
33
  "@aliou/sh": "^0.1.0"
34
34
  },
35
35
  "peerDependencies": {
36
- "@mariozechner/pi-coding-agent": ">=0.51.0",
36
+ "@mariozechner/pi-agent-core": ">=0.52.7",
37
+ "@mariozechner/pi-ai": ">=0.52.7",
38
+ "@mariozechner/pi-coding-agent": ">=0.52.7",
37
39
  "@mariozechner/pi-tui": ">=0.51.0"
38
40
  },
39
41
  "devDependencies": {
40
42
  "@aliou/biome-plugins": "^0.3.2",
41
43
  "@biomejs/biome": "^2.3.13",
42
44
  "@changesets/cli": "^2.27.11",
45
+ "@mariozechner/pi-agent-core": "0.52.7",
46
+ "@mariozechner/pi-ai": "0.52.7",
43
47
  "@mariozechner/pi-coding-agent": "0.52.7",
44
48
  "@sinclair/typebox": "^0.34.48",
45
49
  "@types/node": "^25.0.10",
@@ -47,6 +51,12 @@
47
51
  "typescript": "^5.9.3"
48
52
  },
49
53
  "peerDependenciesMeta": {
54
+ "@mariozechner/pi-agent-core": {
55
+ "optional": true
56
+ },
57
+ "@mariozechner/pi-ai": {
58
+ "optional": true
59
+ },
50
60
  "@mariozechner/pi-coding-agent": {
51
61
  "optional": true
52
62
  },