@czottmann/pi-automode 1.5.0 → 1.6.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
|
@@ -43,6 +43,19 @@ pi -e ./extensions/auto-mode.ts
|
|
|
43
43
|
|
|
44
44
|
`/auto-mode` is an alias.
|
|
45
45
|
|
|
46
|
+
## Status line
|
|
47
|
+
|
|
48
|
+
When the Pi TUI is available, the extension renders a persistent status line:
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
AM● a:12 d:2 ca:5 cd:1
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- `AM` — auto-mode prefix; `●` when enabled, `○` when disabled (via config or `/automode off`).
|
|
55
|
+
- `a:` — actions allowed so far (checked minus blocked).
|
|
56
|
+
- `d:` — actions blocked so far, for any reason (permission rule, deterministic hard-deny, or classifier).
|
|
57
|
+
- `ca:` / `cd:` — classifier decisions split into allowed vs denied. These segments appear only after the classifier has run at least once; `d:` counts all blocks, so `d:` is always `>= cd:`.
|
|
58
|
+
|
|
46
59
|
## Docs
|
|
47
60
|
|
|
48
61
|
- [Defaults and rule-list behavior](docs/defaults.md)
|
|
@@ -69,7 +69,8 @@ export function createPiAutomode(options: PiAutomodeOptions = {}) {
|
|
|
69
69
|
let state: AutoModeState = {
|
|
70
70
|
checkedActions: 0,
|
|
71
71
|
blockedActions: 0,
|
|
72
|
-
|
|
72
|
+
classifierAllowed: 0,
|
|
73
|
+
classifierDenied: 0,
|
|
73
74
|
recentDenials: [],
|
|
74
75
|
};
|
|
75
76
|
let loadedContext = "";
|
|
@@ -216,15 +217,16 @@ export function createPiAutomode(options: PiAutomodeOptions = {}) {
|
|
|
216
217
|
if (event.toolName === "write" || event.toolName === "edit") {
|
|
217
218
|
const path = resolveInputPath(ctx.cwd, input.path);
|
|
218
219
|
if (path && isProtectedPath(path, ctx.cwd, cfg.protectedPaths)) {
|
|
219
|
-
state.classifierChecks += 1;
|
|
220
220
|
const decision = await classify(ctx, cfg, summary, loadedContext);
|
|
221
221
|
if (decision.decision === "allow") {
|
|
222
|
+
state.classifierAllowed += 1;
|
|
222
223
|
state.lastDecision = "allow";
|
|
223
224
|
state.lastReason = decision.reason;
|
|
224
225
|
persist();
|
|
225
226
|
updateUi(ctx);
|
|
226
227
|
return undefined;
|
|
227
228
|
}
|
|
229
|
+
state.classifierDenied += 1;
|
|
228
230
|
return block(ctx, {
|
|
229
231
|
timestamp: Date.now(),
|
|
230
232
|
toolName: event.toolName,
|
|
@@ -235,9 +237,9 @@ export function createPiAutomode(options: PiAutomodeOptions = {}) {
|
|
|
235
237
|
}
|
|
236
238
|
}
|
|
237
239
|
|
|
238
|
-
state.classifierChecks += 1;
|
|
239
240
|
const decision = await classify(ctx, cfg, summary, loadedContext);
|
|
240
241
|
if (decision.decision === "allow") {
|
|
242
|
+
state.classifierAllowed += 1;
|
|
241
243
|
state.lastDecision = "allow";
|
|
242
244
|
state.lastReason = decision.reason;
|
|
243
245
|
persist();
|
|
@@ -245,6 +247,7 @@ export function createPiAutomode(options: PiAutomodeOptions = {}) {
|
|
|
245
247
|
return undefined;
|
|
246
248
|
}
|
|
247
249
|
|
|
250
|
+
state.classifierDenied += 1;
|
|
248
251
|
return block(ctx, {
|
|
249
252
|
timestamp: Date.now(),
|
|
250
253
|
toolName: event.toolName,
|
|
@@ -298,7 +301,8 @@ export function createPiAutomode(options: PiAutomodeOptions = {}) {
|
|
|
298
301
|
state = {
|
|
299
302
|
checkedActions: 0,
|
|
300
303
|
blockedActions: 0,
|
|
301
|
-
|
|
304
|
+
classifierAllowed: 0,
|
|
305
|
+
classifierDenied: 0,
|
|
302
306
|
recentDenials: [],
|
|
303
307
|
enabledOverride: state.enabledOverride,
|
|
304
308
|
};
|
|
@@ -17,8 +17,8 @@ export function statusLine(
|
|
|
17
17
|
const enabled = state.enabledOverride ?? config.enabled;
|
|
18
18
|
const circle = enabled ? "●" : "○";
|
|
19
19
|
const allowed = state.checkedActions - state.blockedActions;
|
|
20
|
-
const classifier = state.
|
|
21
|
-
? `
|
|
20
|
+
const classifier = state.classifierAllowed > 0 || state.classifierDenied > 0
|
|
21
|
+
? ` ca:${state.classifierAllowed} cd:${state.classifierDenied}`
|
|
22
22
|
: "";
|
|
23
23
|
return `AM${circle} a:${allowed} d:${state.blockedActions}${classifier}`;
|
|
24
24
|
}
|
|
@@ -32,7 +32,8 @@ export function statusText(
|
|
|
32
32
|
`classifier: ${config.classifierModel ?? "current session model"}`,
|
|
33
33
|
`checked actions: ${state.checkedActions}`,
|
|
34
34
|
`blocked actions: ${state.blockedActions}`,
|
|
35
|
-
`classifier
|
|
35
|
+
`classifier allowed: ${state.classifierAllowed}`,
|
|
36
|
+
`classifier denied: ${state.classifierDenied}`,
|
|
36
37
|
`permissions.deny rules: ${config.permissionDeny.length}`,
|
|
37
38
|
`permissions.ask rules: ${config.permissionAsk.length}`,
|
|
38
39
|
`environment entries: ${config.environment.length}`,
|
|
@@ -88,11 +89,12 @@ export function restoreState(ctx: ExtensionContext): AutoModeState {
|
|
|
88
89
|
lastReason: entry.data.lastReason,
|
|
89
90
|
checkedActions: entry.data.checkedActions ?? 0,
|
|
90
91
|
blockedActions: entry.data.blockedActions ?? 0,
|
|
91
|
-
|
|
92
|
+
classifierAllowed: entry.data.classifierAllowed ?? 0,
|
|
93
|
+
classifierDenied: entry.data.classifierDenied ?? 0,
|
|
92
94
|
recentDenials: Array.isArray(entry.data.recentDenials)
|
|
93
95
|
? entry.data.recentDenials.slice(-DENIAL_HISTORY_LIMIT)
|
|
94
96
|
: [],
|
|
95
97
|
};
|
|
96
98
|
}
|
|
97
|
-
return { checkedActions: 0, blockedActions: 0,
|
|
99
|
+
return { checkedActions: 0, blockedActions: 0, classifierAllowed: 0, classifierDenied: 0, recentDenials: [] };
|
|
98
100
|
}
|