@gotgenes/pi-permission-system 23.0.2 → 23.0.3

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/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [23.0.3](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v23.0.2...pi-permission-system-v23.0.3) (2026-07-26)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **pi-permission-system:** preserve tool expansion in inline permission prompts ([6a0d241](https://github.com/gotgenes/pi-packages/commit/6a0d241291f8eefa51493b683e12514a83f295bd))
14
+
15
+
16
+ ### Documentation
17
+
18
+ * **pi-permission-system:** document tool expansion during permission prompts ([f4098d3](https://github.com/gotgenes/pi-packages/commit/f4098d331efc72538ee3836ffb4056beee8cbed0))
19
+
8
20
  ## [23.0.2](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v23.0.1...pi-permission-system-v23.0.2) (2026-07-26)
9
21
 
10
22
 
package/README.md CHANGED
@@ -66,6 +66,7 @@ All permissions use one of three states:
66
66
 
67
67
  When the dialog prompts, you can approve once or approve a pattern for the rest of the session.
68
68
  In an interactive TUI session the prompt is an inline keybind dialog — `y` approve, `s` approve for this session, `n` deny, `r` deny with a reason — where each hotkey arms and a second press confirms (configurable via `doublePressToConfirm`).
69
+ Pi's tool-expansion binding (`app.tools.expand`, `Ctrl+O` by default) keeps working while the dialog is open, so you can expand a truncated tool preview before deciding.
69
70
  See [docs/configuration.md](docs/configuration.md#inline-permission-dialog-tui) for the hotkeys and [docs/session-approvals.md](docs/session-approvals.md) for session-scoped rules and pattern suggestions.
70
71
 
71
72
  The `path` surface is a cross-cutting gate that applies to **all** file access — Pi tools, bash commands, MCP calls, and extension tools alike.
@@ -127,6 +127,10 @@ Arrow keys / `j`/`k` move the highlight, `enter` confirms the highlighted option
127
127
  With `doublePressToConfirm` enabled (the default), a letter hotkey **arms** its action and shows a `Press y again to approve.` hint; press the same key again to commit.
128
128
  Set `doublePressToConfirm` to `false` to commit on the first press.
129
129
 
130
+ Pi's tool-expansion binding (`app.tools.expand`, `Ctrl+O` by default) stays live while the dialog is open, so you can expand a truncated tool preview before deciding.
131
+ It only toggles the display — it never resolves, commits, or arms the pending decision.
132
+ While you are typing a denial reason it is not intercepted, so a rebound printable key still reaches the reason editor.
133
+
130
134
  Non-TUI contexts (RPC / frontend-driven sessions) keep the single-select prompt and are unaffected by `doublePressToConfirm`.
131
135
 
132
136
  ### `piInfrastructureReadPaths` patterns
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotgenes/pi-permission-system",
3
- "version": "23.0.2",
3
+ "version": "23.0.3",
4
4
  "description": "Permission enforcement extension for the Pi coding agent.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,6 +1,7 @@
1
1
  import type {
2
2
  ExtensionContext,
3
3
  ExtensionUIContext,
4
+ KeybindingsManager,
4
5
  } from "@earendil-works/pi-coding-agent";
5
6
  import {
6
7
  type Component,
@@ -35,9 +36,12 @@ import {
35
36
  /** The subset of the session UI surface the inline dialog needs. */
36
37
  export type PermissionPromptUi = Pick<
37
38
  ExtensionUIContext,
38
- "select" | "input" | "custom"
39
+ "select" | "input" | "custom" | "getToolsExpanded" | "setToolsExpanded"
39
40
  >;
40
41
 
42
+ /** The keybindings surface the dialog consults; only `matches` is read (ISP). */
43
+ type PromptKeybindings = Pick<KeybindingsManager, "matches">;
44
+
41
45
  /** The resolved presentation context selected once per activation. */
42
46
  export interface PermissionPromptView {
43
47
  mode: ExtensionContext["mode"];
@@ -97,12 +101,13 @@ export function presentInlinePermissionPrompt(
97
101
  sessionScope: options?.sessionScope,
98
102
  };
99
103
  return view.ui.custom<PermissionPromptDecision>(
100
- (tui, theme, _keybindings, done) =>
104
+ (tui, theme, keybindings, done) =>
101
105
  new PermissionPromptComponent(
102
106
  theme,
103
107
  config,
104
108
  title,
105
109
  message,
110
+ (data) => handleToolsExpandAction(data, keybindings, view.ui),
106
111
  () => {
107
112
  tui.requestRender();
108
113
  },
@@ -112,6 +117,31 @@ export function presentInlinePermissionPrompt(
112
117
  );
113
118
  }
114
119
 
120
+ /**
121
+ * Forward Pi's tool-expansion action while the dialog holds keyboard focus.
122
+ *
123
+ * A focused `ctx.ui.custom` component consumes every keystroke, so `Ctrl+O`
124
+ * would otherwise be dead for the duration of an ask — exactly when the user
125
+ * most needs to see the full pending tool invocation. Returns `true` when the
126
+ * keystroke was the action (and was handled), so the caller stops before
127
+ * mapping it to a {@link PromptEvent}; expansion is a display concern and must
128
+ * never reach the decision model.
129
+ *
130
+ * Deliberately does not request a render: `setToolsExpanded` re-renders the
131
+ * host itself, and the dialog's own lines are unaffected by tool expansion.
132
+ */
133
+ function handleToolsExpandAction(
134
+ data: string,
135
+ keybindings: PromptKeybindings,
136
+ ui: PermissionPromptUi,
137
+ ): boolean {
138
+ if (!keybindings.matches(data, "app.tools.expand")) {
139
+ return false;
140
+ }
141
+ ui.setToolsExpanded(!ui.getToolsExpanded());
142
+ return true;
143
+ }
144
+
115
145
  class PermissionPromptComponent implements Component {
116
146
  private state: PromptViewState;
117
147
  private reasonBuffer = "";
@@ -121,6 +151,7 @@ class PermissionPromptComponent implements Component {
121
151
  private readonly config: PromptModelConfig,
122
152
  private readonly title: string,
123
153
  private readonly message: string,
154
+ private readonly handleAppAction: (data: string) => boolean,
124
155
  private readonly requestRender: () => void,
125
156
  private readonly done: (decision: PermissionPromptDecision) => void,
126
157
  ) {
@@ -151,6 +182,9 @@ class PermissionPromptComponent implements Component {
151
182
  this.handleReasonInput(data);
152
183
  return;
153
184
  }
185
+ if (this.handleAppAction(data)) {
186
+ return;
187
+ }
154
188
  const event = this.toEvent(data);
155
189
  if (event) {
156
190
  this.apply(event);