@czottmann/pi-automode 0.1.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/LICENSE.md +21 -0
- package/README.md +211 -0
- package/examples/automode.local.json +32 -0
- package/extensions/auto-mode.ts +1822 -0
- package/package.json +46 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Carlo Zottmann
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# pi-automode
|
|
2
|
+
|
|
3
|
+
Claude Code-style auto mode for Pi.
|
|
4
|
+
|
|
5
|
+
This is a guardrail extension. It intercepts agent tool calls before execution and blocks actions that match permission deny rules, deterministic hard-deny checks, or the auto-mode classifier's block decision.
|
|
6
|
+
|
|
7
|
+
It is not a sandbox. Extensions run in the Pi process, and a determined malicious extension can do anything your user account can do. It also does not guard user `!` / `!!` shell commands; by design, it guards agent tool calls only. Use this to reduce unsafe autonomous tool use, not as an OS security boundary.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
From npm:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pi install npm:@czottmann/pi-automode
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
From a local checkout:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pi install .
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
For one run from a local checkout:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pi -e ./extensions/auto-mode.ts
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Commands
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
/automode status # current state, rules, and classifier
|
|
33
|
+
/automode on # re-enable for this session
|
|
34
|
+
/automode off # disable for this session
|
|
35
|
+
/automode reload # reload config from disk
|
|
36
|
+
/automode reset # reset denial counters only
|
|
37
|
+
/automode defaults # print the built-in rule lists
|
|
38
|
+
/automode config # current effective config + diagnostics
|
|
39
|
+
/automode denials # denial history for this session
|
|
40
|
+
/automode model # open classifier model selector
|
|
41
|
+
/automode model provider/model-id # set classifier directly
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`/auto-mode` is an alias.
|
|
45
|
+
|
|
46
|
+
## Configuration
|
|
47
|
+
|
|
48
|
+
The extension follows Claude Code's documented config model where Pi can support it.
|
|
49
|
+
|
|
50
|
+
It reads `autoMode` from Pi-owned config only:
|
|
51
|
+
|
|
52
|
+
- `~/.pi/automode.json`
|
|
53
|
+
- `.pi/automode.local.json`
|
|
54
|
+
- `PI_AUTOMODE_SETTINGS_JSON`
|
|
55
|
+
|
|
56
|
+
It deliberately does not read `autoMode` from shared project `.pi/automode.json`, because a checked-in repo should not be able to weaken auto-mode rules. Shared project config may still contribute `permissions.deny` and `permissions.ask`.
|
|
57
|
+
|
|
58
|
+
Example:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"autoMode": {
|
|
63
|
+
"environment": [
|
|
64
|
+
"$defaults",
|
|
65
|
+
"Source control: github.example.com/acme-corp and all repos under it",
|
|
66
|
+
"Trusted internal domains: *.corp.example.com"
|
|
67
|
+
],
|
|
68
|
+
"allow": ["$defaults"],
|
|
69
|
+
"soft_deny": ["$defaults"],
|
|
70
|
+
"hard_deny": [
|
|
71
|
+
"$defaults",
|
|
72
|
+
"Never send repository contents to third-party code-review APIs"
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
"permissions": {
|
|
76
|
+
"deny": ["bash(rm -rf *)"],
|
|
77
|
+
"ask": ["bash(git push *)"]
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### `$defaults`
|
|
83
|
+
|
|
84
|
+
`$defaults` expands to this plugin's built-in entries for the section where it appears. It is section-local: `$defaults` in `allow` means the built-in allow rules, not the built-in hard-deny rules.
|
|
85
|
+
|
|
86
|
+
#### `environment`
|
|
87
|
+
|
|
88
|
+
`$defaults` expands to:
|
|
89
|
+
|
|
90
|
+
- trusted repo: the repository Pi started in and its configured git remotes
|
|
91
|
+
- source control: the trusted repo and its configured remotes only
|
|
92
|
+
- trusted internal domains: none configured
|
|
93
|
+
- trusted cloud buckets: none configured
|
|
94
|
+
- key internal services: none configured
|
|
95
|
+
|
|
96
|
+
#### `allow`
|
|
97
|
+
|
|
98
|
+
`$defaults` expands to allow exceptions for:
|
|
99
|
+
|
|
100
|
+
- read-only operations: inspecting files, listing directories, searching, GET requests, and state queries that do not expose secrets
|
|
101
|
+
- local development inside the working tree: creating, editing, building, testing, linting, formatting, and deleting files created during the current task
|
|
102
|
+
- installing dependencies already declared in package manifests or lockfiles
|
|
103
|
+
- using standard credentials only with their intended configured providers
|
|
104
|
+
- pushing to the current non-default working branch or a new branch created for the task
|
|
105
|
+
- bootstrapping language/toolchain installers from official sources
|
|
106
|
+
|
|
107
|
+
These are exceptions to `soft_deny`, not to `hard_deny`.
|
|
108
|
+
|
|
109
|
+
#### `soft_deny`
|
|
110
|
+
|
|
111
|
+
`$defaults` expands to soft blocks for:
|
|
112
|
+
|
|
113
|
+
- force push, remote branch deletion, remote history rewrite, or direct push to `main`, `master`, or the default branch
|
|
114
|
+
- downloading and executing external code, including `curl | bash` and `wget | sh`
|
|
115
|
+
- production deploys, production database migrations, or production infrastructure changes
|
|
116
|
+
- deleting, truncating, or overwriting pre-existing local files unless the user named the specific target
|
|
117
|
+
- mass modification/deletion in cloud storage, ticketing systems, collaboration tools, or external APIs
|
|
118
|
+
- writing through remote shells such as `ssh`, `kubectl exec`, or `docker exec` on shared or production hosts
|
|
119
|
+
- changing live/shared infrastructure without a preview, plan, dry-run, or review when one normally exists
|
|
120
|
+
- killing other users' processes, cancelling shared jobs, or disrupting shared infrastructure
|
|
121
|
+
- exposing local files, sockets, credentials, or internal services through a network service
|
|
122
|
+
- outbound messages, purchases, bookings, subscriptions, or other real-world transactions
|
|
123
|
+
|
|
124
|
+
Soft blocks can be overridden by a matching `allow` exception or by direct, specific user intent.
|
|
125
|
+
|
|
126
|
+
#### `hard_deny`
|
|
127
|
+
|
|
128
|
+
`$defaults` expands to unconditional blocks for:
|
|
129
|
+
|
|
130
|
+
- weakening TLS, certificate validation, authentication, authorization, audit logging, or other security controls
|
|
131
|
+
- adding persistence such as SSH keys, shell profile edits, cron jobs, launch agents, login items, or background agents
|
|
132
|
+
- granting admin, owner, IAM, RBAC, repository, or production access
|
|
133
|
+
- exfiltrating repository contents, secrets, credentials, private keys, tokens, or internal data to untrusted places
|
|
134
|
+
- scanning for credentials, tokens, private keys, or secrets beyond what the task requires
|
|
135
|
+
- tampering with logs, audit trails, security monitoring, permission rules, auto-mode config, or safety-control files
|
|
136
|
+
- creating services, endpoints, workflows, or autonomous agents that execute arbitrary code without meaningful approval
|
|
137
|
+
- posting or updating public/external content that is fabricated, misleading, impersonating a user, or claiming approval/action that did not happen
|
|
138
|
+
|
|
139
|
+
Hard-deny rules cannot be overridden by `allow` or by user intent.
|
|
140
|
+
|
|
141
|
+
#### Replacement behavior
|
|
142
|
+
|
|
143
|
+
Use `$defaults` when you want to keep the built-ins and add your own entries:
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"autoMode": {
|
|
148
|
+
"allow": [
|
|
149
|
+
"$defaults",
|
|
150
|
+
"Running the staging deploy script is allowed."
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
That means: use all built-in `allow` entries, plus the staging rule.
|
|
157
|
+
|
|
158
|
+
If you omit `$defaults`, you replace the built-ins for that section:
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"autoMode": {
|
|
163
|
+
"allow": [
|
|
164
|
+
"Running the staging deploy script is allowed."
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
That means: use only that one `allow` entry. The built-in `allow` entries are not used. Replacing `allow` does not replace `soft_deny`, `hard_deny`, or `environment`.
|
|
171
|
+
|
|
172
|
+
`$defaults` is not used in `permissions.deny` or `permissions.ask`. Those lists contain only explicit Pi tool patterns.
|
|
173
|
+
|
|
174
|
+
### Permission patterns
|
|
175
|
+
|
|
176
|
+
Permission patterns use Pi tool names, for example `bash(...)`, `write(...)`, `edit(...)`, `read(...)`. The parser accepts capitalized names like `Bash(...)` for convenience, but the documented form is lowercase because Pi tool names are lowercase.
|
|
177
|
+
|
|
178
|
+
## What is enforced before the classifier
|
|
179
|
+
|
|
180
|
+
The extension blocks these before any allow or classifier decision:
|
|
181
|
+
|
|
182
|
+
- `permissions.deny` matches
|
|
183
|
+
- declined `permissions.ask` matches
|
|
184
|
+
- shell profile writes
|
|
185
|
+
- SSH `authorized_keys` writes
|
|
186
|
+
- cron, launch agent, and system service persistence
|
|
187
|
+
- TLS/certificate/auth weakening patterns
|
|
188
|
+
- root, home, and system-path destructive deletes
|
|
189
|
+
- edits to `.pi/automode*`, `.pi` auto-mode files, and this extension's safety-control files
|
|
190
|
+
|
|
191
|
+
Read-only Pi tools (`read`, `grep`, `find`, `ls`) are allowed after those checks.
|
|
192
|
+
|
|
193
|
+
Everything else goes to the classifier. If the classifier is missing, fails, or returns invalid JSON, the action is blocked.
|
|
194
|
+
|
|
195
|
+
## Examples
|
|
196
|
+
|
|
197
|
+
- `examples/automode.local.json`: copy to `.pi/automode.local.json` in a project and edit the domains, buckets, and source-control org.
|
|
198
|
+
|
|
199
|
+
## Development
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
npm run check
|
|
203
|
+
npm test
|
|
204
|
+
npm pack --dry-run
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The tests cover the risky parts: scoped permission matching, config-source precedence, `$defaults` behavior, config diagnostics, deterministic hard-deny checks, shell parsing for risky bash fragments, classifier JSON parsing, hook-level blocking/allowing, and classifier mocking.
|
|
208
|
+
|
|
209
|
+
## Known limits
|
|
210
|
+
|
|
211
|
+
Claude Code's real classifier and exact built-in rules are private. This package implements the documented precedence and configuration behavior, with a local classifier prompt and deterministic hard-deny checks.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"autoMode": {
|
|
3
|
+
"environment": [
|
|
4
|
+
"$defaults",
|
|
5
|
+
"Source control: github.example.com/acme-corp and all repos under it",
|
|
6
|
+
"Trusted internal domains: *.corp.example.com, api.internal.example.com",
|
|
7
|
+
"Trusted cloud buckets: s3://acme-build-artifacts, gs://acme-dev-scratch"
|
|
8
|
+
],
|
|
9
|
+
"allow": [
|
|
10
|
+
"$defaults",
|
|
11
|
+
"Deploying to the staging namespace is allowed when the command names staging explicitly."
|
|
12
|
+
],
|
|
13
|
+
"soft_deny": [
|
|
14
|
+
"$defaults",
|
|
15
|
+
"Never run database migrations except through the project's migrations CLI."
|
|
16
|
+
],
|
|
17
|
+
"hard_deny": [
|
|
18
|
+
"$defaults",
|
|
19
|
+
"Never send repository contents to third-party code-review APIs."
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
"permissions": {
|
|
23
|
+
"ask": [
|
|
24
|
+
"bash(git push *)"
|
|
25
|
+
],
|
|
26
|
+
"deny": [
|
|
27
|
+
"bash(git push --force*)",
|
|
28
|
+
"edit(.env*)",
|
|
29
|
+
"write(.env*)"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
}
|