@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
- classifierChecks: 0,
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
- classifierChecks: 0,
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.classifierChecks > 0
21
- ? ` c:${state.classifierChecks}`
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 checks: ${state.classifierChecks}`,
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
- classifierChecks: entry.data.classifierChecks ?? 0,
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, classifierChecks: 0, recentDenials: [] };
99
+ return { checkedActions: 0, blockedActions: 0, classifierAllowed: 0, classifierDenied: 0, recentDenials: [] };
98
100
  }
@@ -52,7 +52,8 @@ export type AutoModeState = {
52
52
  lastReason?: string;
53
53
  checkedActions: number;
54
54
  blockedActions: number;
55
- classifierChecks: number;
55
+ classifierAllowed: number;
56
+ classifierDenied: number;
56
57
  recentDenials: DenialRecord[];
57
58
  };
58
59
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@czottmann/pi-automode",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Claude Code-style auto mode guardrail for pi.",
5
5
  "repository": {
6
6
  "url": "https://github.com/czottmann/pi-automode"