pi-permission-system 0.4.7 → 0.4.9

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,34 @@ 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
+ ## [Unreleased]
9
+
10
+ ## [0.4.9] - 2026-05-05
11
+
12
+ ### Changed
13
+ - Permission-system config parsing now accepts JSONC comments and trailing commas across both policy files and the extension config, and invalid config warnings are emitted once per session as a compact single-line message with explicit fallback behavior.
14
+
15
+ ### Fixed
16
+ - Stopped repeating identical invalid-config warnings in the TUI when the same broken permission policy is re-evaluated during the same session or reload cycle (thanks to @jviel-beta for issue #20).
17
+
18
+ ## [0.4.8] - 2026-05-04
19
+
20
+ ### Added
21
+ - Added runtime yolo-mode control through the `/permission-system` settings modal and `globalThis.__piPermissionSystem` for other extensions.
22
+ - Added wildcard support for `permission.tools` so direct tools from any extension can be controlled without adapter-specific hardcoding.
23
+ - Added `PI_PERMISSION_SYSTEM_POLICY_AGENT_DIR` so global policy and global agent override lookup can be pointed at a different agent root when needed.
24
+
25
+ ### Changed
26
+ - Simplified `/permission-system` to only open the settings modal, removing the previous `show`, `path`, `reset`, `help`, and `yolo ...` subcommands.
27
+ - Expanded the README with an OpenCode-to-Pi migration guide covering agent file placement, frontmatter structure, permission mapping, Pi-specific `tools` vs `mcp` behavior, and policy-root overrides.
28
+
29
+ ### Fixed
30
+ - Removed artifact validation that forced local runtime `config.json` values to match release defaults.
31
+ - Restored requested bash command text in permission review log entries while keeping prompts and generic tool payload previews redacted.
32
+ - Clarified last-declared-match wildcard precedence in the README and fixed wildcard-order examples/recipes so documented `tools` and `mcp` examples now match runtime behavior (thanks to @Nateowami for issue #17 and @gotgenes for the rule-order diagnosis).
33
+ - Replaced the removed `session_switch` lifecycle subscription with a single supported `session_start` refresh path so session changes rebuild runtime permission state without duplicate handlers (thanks to @gotgenes for PR #11).
34
+ - Removed stale README wording around exact-name tool matching and refreshed the documented architecture/module list to match the current extension layout.
35
+
8
36
  ## [0.4.7] - 2026-05-04
9
37
 
10
38
  ### Added
package/README.md CHANGED
@@ -6,6 +6,87 @@ Permission enforcement extension for the Pi coding agent that provides centraliz
6
6
 
7
7
  <img width="1360" height="752" alt="image" src="https://github.com/user-attachments/assets/3e85190a-17fa-4d94-ac8e-efa54337df5d" />
8
8
 
9
+ ## Coming from OpenCode?
10
+
11
+ Yes — this extension was designed so OpenCode-style agent permission policies can be ported into Pi with minimal friction.
12
+
13
+ ### Start here
14
+
15
+ | If you have this in OpenCode | In Pi, use this |
16
+ |---|---|
17
+ | Agent markdown file | `~/.pi/agent/agents/<agent-name>.md` (respects `PI_CODING_AGENT_DIR`) |
18
+ | YAML frontmatter | Same place: top of the markdown file |
19
+ | Agent instructions / system prompt body | Same file, below frontmatter |
20
+ | Agent permission rules | `permission:` inside that same frontmatter |
21
+
22
+ ### Important compatibility notes
23
+
24
+ - **Agents are still markdown files with YAML frontmatter.**
25
+ - **Wildcard permissions still use last-match-wins ordering.**
26
+ - **Keep frontmatter simple when porting.** This extension intentionally supports `key: value` scalars and nested maps, not full YAML features like arrays, anchors, or multiline scalars.
27
+
28
+ ### Minimal Pi agent example
29
+
30
+ ```md
31
+ ---
32
+ name: my-agent
33
+ mode: primary
34
+ description: My ported agent
35
+ permission:
36
+ tools:
37
+ read: allow
38
+ grep: allow
39
+ bash:
40
+ "*": ask
41
+ mcp:
42
+ "*": ask
43
+ ---
44
+
45
+ Your agent instructions go here.
46
+ ```
47
+
48
+ ### Compatibility matrix
49
+
50
+ | OpenCode concept | Pi equivalent with this extension | Compatibility | Porting notes |
51
+ |---|---|---:|---|
52
+ | Agent markdown files with YAML frontmatter | `~/.pi/agent/agents/<agent-name>.md` | High | Your agent-local `permission:` frontmatter pattern carries over cleanly. |
53
+ | Wildcard precedence | Same last-declared-match-wins behavior | High | Broad rules first, specific overrides later. |
54
+ | `bash` permission rules | `permission.bash` | High | Command-pattern gating ports cleanly. |
55
+ | Per-tool permission rules like `read`, `grep`, `list`, `task`, or arbitrary extension tool names | `permission.tools` | Medium-High | Pi groups registered tool names under `tools`, including built-ins and extension tools. |
56
+ | `external_directory` | `permission.special.external_directory` | Medium | Same idea, different location. |
57
+ | `doom_loop` | `permission.special.doom_loop` | Medium | Same idea, different location. |
58
+ | `skill` permission rules | `permission.skills` | Medium | Same purpose, but Pi uses a dedicated plural `skills` section. |
59
+ | MCP-related access | `permission.mcp` for proxy targets, `permission.tools` for direct registered tools | Medium | This is the biggest Pi-specific difference: proxy MCP targets and direct tool names are intentionally split. |
60
+ | OpenCode-specific permissions like `webfetch`, `websearch`, `question`, `lsp`, `todowrite` | Usually extension-specific Pi tool names under `permission.tools` | Low-Medium | These do not have universal built-in one-to-one Pi names; map them to the actual registered tools available in your Pi setup. |
61
+
62
+ ### Most important difference
63
+
64
+ In OpenCode, many permission names live in one broad permission namespace. In Pi with this extension, there is a deliberate split:
65
+
66
+ | Use this when... | Put the rule here |
67
+ |---|---|
68
+ | You are targeting the registered **`mcp` proxy tool** and its internal server/tool targets | `permission.mcp` |
69
+ | You are targeting an actual registered tool name, including direct extension tools like `context7_*`, `github_*`, or `exa_*` | `permission.tools` |
70
+
71
+ ### Fast porting guide
72
+
73
+ | If your OpenCode agent has... | In Pi, do this |
74
+ |---|---|
75
+ | `permission.bash` rules | Move them into `permission.bash` |
76
+ | `permission.external_directory` | Move it to `permission.special.external_directory` |
77
+ | `permission.doom_loop` | Move it to `permission.special.doom_loop` |
78
+ | `permission.skill` rules | Move them to `permission.skills` |
79
+ | Tool-ish permissions like `read`, `grep`, `list`, `task`, or third-party tool names | Put them in `permission.tools` |
80
+ | MCP server/tool target logic | Put proxy-target rules in `permission.mcp` |
81
+
82
+ ### Practical takeaway
83
+
84
+ If you are coming from OpenCode, you usually do **not** need to rewrite your whole agent. In most cases, porting is just:
85
+
86
+ 1. Keep the agent markdown/frontmatter structure.
87
+ 2. Move OpenCode-style tool permissions into Pi's `tools` section.
88
+ 3. Move `external_directory` and `doom_loop` into `special`.
89
+ 4. Split MCP proxy target rules into `mcp` and direct registered tool rules into `tools`.
9
90
 
10
91
  ## Features
11
92
 
@@ -17,6 +98,7 @@ Permission enforcement extension for the Pi coding agent that provides centraliz
17
98
  - **Skill Protection** — Controls which skills can be loaded or read from disk, including multi-block prompt sanitization
18
99
  - **Per-Agent Overrides** — Agent-specific permission policies via YAML frontmatter
19
100
  - **Subagent Permission Forwarding** — Forwards `ask` confirmations from non-UI subagents back to the main interactive session
101
+ - **Runtime YOLO Control** — Lets users toggle yolo mode from the settings modal and lets other extensions toggle it through the runtime API
20
102
  - **File-Based Review Logging** — Writes permission request/denial review entries to a file by default for later auditing
21
103
  - **Optional Debug Logging** — Keeps verbose extension diagnostics in a separate file when enabled in `config.json`
22
104
  - **JSON Schema Validation** — Full schema for editor autocomplete and config validation
@@ -41,7 +123,7 @@ Place this folder in one of the following locations:
41
123
 
42
124
  Pi auto-discovers extensions in these paths.
43
125
 
44
- > **Tip:** All `~/.pi/agent` paths shown in this document are defaults. If the `PI_CODING_AGENT_DIR` environment variable is set, pi uses that directory instead. The extension automatically follows pi's `getAgentDir()` helper, so global policy files, per-agent overrides, session directories, and extension installation paths all resolve under the configured agent directory.
126
+ > **Tip:** All `~/.pi/agent` paths shown in this document are defaults. If the `PI_CODING_AGENT_DIR` environment variable is set, pi uses that directory instead. The extension automatically follows pi's `getAgentDir()` helper for extension installation, session directories, and extension-local config paths. If you need policy lookup to come from a different global agent root, set `PI_PERMISSION_SYSTEM_POLICY_AGENT_DIR`.
45
127
 
46
128
  ## Usage
47
129
 
@@ -90,10 +172,10 @@ The extension integrates via Pi's lifecycle hooks:
90
172
  **Additional behaviors:**
91
173
  - Unknown/unregistered tools are blocked before permission checks (prevents bypass attempts)
92
174
  - The `Available tools:` system prompt section is rewritten to match the filtered active tool set
93
- - Extension-provided tools like `task`, `mcp`, and third-party tools are handled by exact registered name instead of private built-in hardcodes
175
+ - Extension-provided tools like `task`, `mcp`, and third-party tools are handled through the same registered-tool permission layer instead of private built-in hardcodes
94
176
  - When a subagent hits an `ask` permission without direct UI access, the request can be forwarded to the main interactive session for confirmation
95
177
  - Generic extension-tool approval prompts include a bounded input preview; built-in file tools use concise human-readable summaries instead of raw multiline JSON
96
- - Permission review logs include redacted prompt/input metadata for auditing without writing raw prompts, commands, or tool payload previews
178
+ - Permission review logs include requested bash command text plus redacted prompt/input metadata for auditing without writing raw prompts or generic tool payload previews
97
179
  - Path-bearing file tools (`read`, `write`, `edit`, `find`, `grep`, `ls`) evaluate `special.external_directory` before their normal tool permission when an explicit path points outside `ctx.cwd`
98
180
 
99
181
  ## Configuration
@@ -104,7 +186,7 @@ The extension integrates via Pi's lifecycle hooks:
104
186
 
105
187
  Set `PI_PERMISSION_SYSTEM_CONFIG_PATH` to point this extension at a specific config file when the default global path is not appropriate.
106
188
 
107
- The extension creates this file automatically when it is missing. It controls only extension-local logging behavior:
189
+ The extension creates this file automatically when it is missing. It controls extension-local logging behavior and yolo mode defaults:
108
190
 
109
191
  ```json
110
192
  {
@@ -122,26 +204,53 @@ The extension creates this file automatically when it is missing. It controls on
122
204
 
123
205
  Both logs write to files only under the extension directory by default. Set `PI_PERMISSION_SYSTEM_LOGS_DIR` to redirect review/debug logs to a specific directory. No debug output is printed to the terminal.
124
206
 
207
+ ### Runtime YOLO Control
208
+
209
+ Use `/permission-system` to open the settings modal and inspect or change yolo mode interactively.
210
+
211
+ Other extensions can toggle yolo mode immediately through the shared runtime API:
212
+
213
+ ```ts
214
+ type PermissionSystemGlobal = typeof globalThis & {
215
+ __piPermissionSystem?: {
216
+ toggleYoloMode(options?: { persist?: boolean; source?: string }): { error?: string };
217
+ };
218
+ };
219
+
220
+ pi.registerShortcut("f8", {
221
+ description: "Toggle pi-permission-system YOLO mode",
222
+ handler: () => {
223
+ const permissionSystem = (globalThis as PermissionSystemGlobal).__piPermissionSystem;
224
+ const result = permissionSystem?.toggleYoloMode({ source: "my-extension" });
225
+ if (result?.error) {
226
+ // Notify or log the error in your extension.
227
+ }
228
+ },
229
+ });
230
+ ```
231
+
232
+ The runtime API exposes `getYoloMode()`, `setYoloMode(enabled, options?)`, and `toggleYoloMode(options?)`. Runtime updates persist to `config.json` by default; pass `{ persist: false }` for a current-session-only toggle.
233
+
125
234
  ### Global Policy File
126
235
 
127
- **Location:** global Pi policy file (default: `~/.pi/agent/pi-permissions.jsonc`, respects `PI_CODING_AGENT_DIR`)
236
+ **Location:** global Pi policy file (default: `~/.pi/agent/pi-permissions.jsonc`, respects `PI_PERMISSION_SYSTEM_POLICY_AGENT_DIR` when set and otherwise follows `PI_CODING_AGENT_DIR`)
128
237
 
129
238
  The policy file is a JSON object with these sections:
130
239
 
131
240
  | Section | Description |
132
241
  |-----------------|-----------------------------------------------------|
133
242
  | `defaultPolicy` | Fallback permissions per category |
134
- | `tools` | Exact-name tool permissions for registered tools |
243
+ | `tools` | Pattern-based tool permissions for registered tools |
135
244
  | `bash` | Command pattern permissions |
136
245
  | `mcp` | MCP server/tool permissions for calls routed through a registered `mcp` tool |
137
246
  | `skills` | Skill name pattern permissions |
138
247
  | `special` | Reserved permission checks such as external directory access |
139
248
 
140
- > **Note:** Trailing commas are **not** supported. If parsing fails, the extension falls back to `ask` for all categories.
249
+ > **Note:** JSONC comments and trailing commas are supported. If parsing still fails, the extension falls back to `ask` for all categories and shows a warning in the TUI when available.
141
250
 
142
251
  ### Global Per-Agent Overrides
143
252
 
144
- Override global permissions for specific agents via YAML frontmatter in the global Pi agents directory (default: `~/.pi/agent/agents/<agent>.md`, respects `PI_CODING_AGENT_DIR`):
253
+ Override global permissions for specific agents via YAML frontmatter in the global Pi agents directory (default: `~/.pi/agent/agents/<agent-name>.md`, respects `PI_PERMISSION_SYSTEM_POLICY_AGENT_DIR` when set and otherwise follows `PI_CODING_AGENT_DIR`):
145
254
 
146
255
  ```yaml
147
256
  ---
@@ -164,7 +273,7 @@ permission:
164
273
 
165
274
  **MCP behavior:** `permission.tools.mcp` is the coarse entry/fallback permission for a registered `mcp` tool when one is available. More specific `permission.mcp` target rules override that fallback when they match.
166
275
 
167
- **Limitations:** The frontmatter parser is intentionally minimal. Use only `key: value` scalars and nested maps. Avoid arrays, multi-line scalars, and YAML anchors.
276
+ **Limitations:** The frontmatter parser is intentionally minimal. Use only `key: value` scalars and nested maps. Avoid arrays, multi-line scalars, and YAML anchors. If you are porting from OpenCode, simplify richer YAML frontmatter before expecting a clean migration.
168
277
 
169
278
  ### Project-Level Policy Files
170
279
 
@@ -173,7 +282,7 @@ The extension can also layer project-local permission files relative to the acti
173
282
  | Scope | Path |
174
283
  |-------|------|
175
284
  | Project policy | `<cwd>/.pi/agent/pi-permissions.jsonc` |
176
- | Project agent override | `<cwd>/.pi/agent/agents/<agent>.md` |
285
+ | Project agent override | `<cwd>/.pi/agent/agents/<agent-name>.md` |
177
286
 
178
287
  Project-local files use the same formats as the global policy file and global agent frontmatter. These project files are resolved from Pi's current session `cwd`, so they are workspace-specific and do **not** move under `PI_CODING_AGENT_DIR`.
179
288
 
@@ -183,7 +292,7 @@ Project-local files use the same formats as the global policy file and global ag
183
292
  3. Global agent frontmatter
184
293
  4. Project agent frontmatter
185
294
 
186
- Later trusted layers override earlier layers within the same permission category, and project-local layers can tighten policy by adding `deny` rules. Project-local policy cannot relax a `deny` from the global policy file or global agent frontmatter: an `allow` or `ask` in a project policy is ignored when the latest matching trusted layer is `deny`. For wildcard-based sections like `bash`, `mcp`, `skills`, and `special`, matching still follows **last matching rule wins** within the applicable trust boundary, with global/system `deny` rules acting as floors for project-local overrides.
295
+ Later trusted layers override earlier layers within the same permission category, and project-local layers can tighten policy by adding `deny` rules. Project-local policy cannot relax a `deny` from the global policy file or global agent frontmatter: an `allow` or `ask` in a project policy is ignored when the latest matching trusted layer is `deny`. For wildcard-based sections like `tools`, `bash`, `mcp`, `skills`, and `special`, matching still follows **last matching rule wins** within the applicable trust boundary, with global/system `deny` rules acting as floors for project-local overrides.
187
296
 
188
297
  ---
189
298
 
@@ -207,7 +316,7 @@ Sets fallback permissions when no specific rule matches:
207
316
 
208
317
  ### `tools`
209
318
 
210
- Controls tools by exact registered name (no wildcards). This is the recommended standalone format for **all** tool entries, including Pi built-ins and arbitrary third-party extension tools.
319
+ Controls tools by registered name pattern. This is the recommended standalone format for **all** tool entries, including Pi built-ins and arbitrary third-party extension tools. Patterns use `*` wildcards and follow last-declared-match semantics, so put broad fallbacks first and specific overrides later.
211
320
 
212
321
  | Tool name example | Description |
213
322
  |-----------------------|-------------|
@@ -216,29 +325,33 @@ Controls tools by exact registered name (no wildcards). This is the recommended
216
325
  | `mcp` | Registered MCP proxy tool entry/fallback when available |
217
326
  | `task` | Delegation tool handled like any other registered extension tool |
218
327
  | `third_party_tool` | Arbitrary registered extension tool |
328
+ | `context7_*` | Wildcard for direct tools registered by another extension |
329
+ | `*` | Fallback for every registered tool not matched by a later rule |
219
330
 
220
331
  ```jsonc
221
332
  {
222
333
  "tools": {
223
- "read": "allow",
224
- "write": "deny",
334
+ "*": "ask",
335
+ "context7_*": "ask",
336
+ "third_party_tool": "ask",
225
337
  "mcp": "allow",
226
- "third_party_tool": "ask"
338
+ "read": "allow",
339
+ "write": "deny"
227
340
  }
228
341
  }
229
342
  ```
230
343
 
231
- Unknown or absent tools are not required in the config. If another extension is not installed, its tool simply will not be registered at runtime, and this extension will block attempts to call that missing tool before permission checks run.
344
+ Unknown or absent tools are not required in the config. If another extension is not installed, its tool simply will not be registered at runtime, and this extension will block attempts to call that missing tool before permission checks run. Wildcard `tools` rules apply to direct tools from any extension; no adapter-specific naming is required.
232
345
 
233
346
  > **Note:** Setting `tools.bash` affects the *default* for bash commands, but `bash` patterns can provide command-level overrides.
234
347
  >
235
- > **Note:** Setting `tools.mcp` controls coarse access to a registered `mcp` tool when one is available. Specific `mcp` rules still override it when a target pattern matches.
348
+ > **Note:** Setting `tools.mcp` controls coarse access to a registered `mcp` proxy tool when one is available. Specific `mcp` rules still override it when a proxy target pattern matches. Direct MCP tools registered by extensions are regular registered tools and should be controlled with `tools` patterns such as `context7_*` or `github_*`.
236
349
  >
237
350
  > **Note:** Top-level shorthand is only supported for the canonical Pi built-ins (`bash`, `read`, `write`, `edit`, `grep`, `find`, `ls`) in agent frontmatter. Use `permission.tools.<name>` for `mcp`, `task`, and any third-party tool.
238
351
 
239
352
  ### `bash`
240
353
 
241
- Command patterns use `*` wildcards and match against the full command string. If multiple patterns match, the **last matching rule wins**.
354
+ Command patterns use `*` wildcards and match against the full command string. If multiple patterns match, the **last declared matching rule wins**. Put broad fallback rules first and more specific overrides later.
242
355
 
243
356
  ```jsonc
244
357
  {
@@ -264,9 +377,10 @@ MCP permissions match against derived targets from tool input. These rules are m
264
377
  ```jsonc
265
378
  {
266
379
  "mcp": {
380
+ "*": "ask",
381
+ "myServer:*": "ask",
267
382
  "mcp_status": "allow",
268
383
  "mcp_list": "allow",
269
- "myServer:*": "ask",
270
384
  "dangerousServer": "deny"
271
385
  }
272
386
  }
@@ -362,10 +476,10 @@ Reserved permission checks:
362
476
  {
363
477
  "defaultPolicy": { "tools": "ask", "bash": "deny", "mcp": "ask", "skills": "ask", "special": "ask" },
364
478
  "bash": {
479
+ "git *": "ask",
365
480
  "git status": "allow",
366
481
  "git diff": "allow",
367
- "git log *": "allow",
368
- "git *": "ask"
482
+ "git log *": "allow"
369
483
  }
370
484
  }
371
485
  ```
@@ -376,11 +490,11 @@ Reserved permission checks:
376
490
  {
377
491
  "defaultPolicy": { "tools": "ask", "bash": "ask", "mcp": "ask", "skills": "ask", "special": "ask" },
378
492
  "mcp": {
493
+ "*": "ask",
379
494
  "mcp_status": "allow",
380
495
  "mcp_list": "allow",
381
496
  "mcp_search": "allow",
382
- "mcp_describe": "allow",
383
- "*": "ask"
497
+ "mcp_describe": "allow"
384
498
  }
385
499
  }
386
500
  ```
@@ -441,26 +555,36 @@ Override logs directory: $PI_PERMISSION_SYSTEM_LOGS_DIR when set
441
555
  ### Architecture
442
556
 
443
557
  ```
444
- index.ts → Root Pi entrypoint shim
558
+ index.ts → Root Pi entrypoint shim
445
559
  src/
446
- ├── index.ts → Extension bootstrap, permission checks, readable prompts, review logging, reload handling, and subagent forwarding
447
- ├── extension-config.ts Extension-local config loading and default creation
448
- ├── logging.ts File-only debug/review logging helpers
449
- ├── permission-manager.ts Global/project policy loading, merging, and resolution with caching
450
- ├── skill-prompt-sanitizer.ts Skill prompt parsing, multi-block sanitization, and skill-read path matching
451
- ├── bash-filter.ts → Bash command wildcard pattern matching
452
- ├── wildcard-matcher.ts Shared wildcard pattern compilation and matching
453
- ├── common.ts Shared utilities (YAML parsing, type guards, etc.)
454
- ├── tool-registry.ts Registered tool name resolution
455
- └── types.ts TypeScript type definitions
560
+ ├── index.ts → Extension bootstrap, permission checks, readable prompts, review logging, reload handling, and subagent forwarding
561
+ ├── before-agent-start-cache.ts Caches prompt/tool filtering state between before_agent_start runs
562
+ ├── bash-filter.ts Bash command wildcard pattern matching
563
+ ├── common.ts Shared utilities (YAML parsing, type guards, etc.)
564
+ ├── config-modal.ts `/permission-system` modal registration and settings UI wiring
565
+ ├── extension-config.ts → Extension-local config loading and default creation
566
+ ├── logging.ts File-only debug/review logging helpers
567
+ ├── model-option-compatibility.ts Guards unsupported provider/model options
568
+ ├── permission-dialog.ts Interactive permission approval UI helpers
569
+ ├── permission-forwarding.ts Subagent-to-parent permission forwarding utilities
570
+ ├── permission-manager.ts → Global/project policy loading, merging, and resolution with caching
571
+ ├── skill-prompt-sanitizer.ts → Skill prompt parsing, multi-block sanitization, and skill-read path matching
572
+ ├── status.ts → Status line integration for runtime yolo state
573
+ ├── system-prompt-sanitizer.ts → Available-tools prompt filtering helpers
574
+ ├── tool-registry.ts → Registered tool name resolution
575
+ ├── types.ts → TypeScript type definitions
576
+ ├── wildcard-matcher.ts → Shared wildcard pattern compilation and matching
577
+ ├── yolo-mode.ts → Runtime yolo approval helpers
578
+ ├── yolo-mode-api.ts → Shared global runtime API for yolo toggling
579
+ └── zellij-modal.ts → Reusable modal/settings UI components
456
580
  tests/
457
- ├── permission-system.test.ts → Core permission, layering, forwarding, and policy tests
458
- ├── config-modal.test.ts Config command and modal behavior tests
459
- └── test-harness.ts → Shared lightweight test helpers
581
+ ├── permission-system.test.ts → Core permission, layering, forwarding, and policy tests
582
+ ├── config-modal.test.ts Modal command behavior tests
583
+ └── test-harness.ts → Shared lightweight test helpers
460
584
  schemas/
461
- └── permissions.schema.json → JSON Schema for policy validation
585
+ └── permissions.schema.json → JSON Schema for policy validation
462
586
  config/
463
- └── config.example.json → Starter global policy template
587
+ └── config.example.json → Starter global policy template
464
588
  ```
465
589
 
466
590
  #### Module Organization
@@ -513,7 +637,7 @@ npx --yes ajv-cli@5 validate \
513
637
 
514
638
  | Problem | Cause | Solution |
515
639
  |---------|-------|----------|
516
- | Config not applied (everything asks) | File not found or parse error | Verify the global Pi policy file (default: `~/.pi/agent/pi-permissions.jsonc`, respects `PI_CODING_AGENT_DIR`); check for trailing commas |
640
+ | Config not applied (everything asks) | File not found or parse error | Verify the global Pi policy file (default: `~/.pi/agent/pi-permissions.jsonc`, respects `PI_CODING_AGENT_DIR`); check the TUI warning for the parse location/message |
517
641
  | Per-agent override not applied | Frontmatter parsing issue | Ensure `---` delimiters at file top; keep YAML simple; restart session |
518
642
  | Tool blocked as unregistered | Unknown tool name | Use a registered `mcp` tool for server tools: `{ "tool": "server:tool" }` |
519
643
  | `/skill:<name>` blocked | Deny policy or confirmation unavailable | Check merged `skills` policy (global/project/agent layers). Active agent context is optional in the main session; `ask` still requires UI or forwarded confirmation. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-permission-system",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "Permission enforcement extension for the Pi coding agent.",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
@@ -64,5 +64,8 @@
64
64
  "@mariozechner/pi-coding-agent": "^0.72.0",
65
65
  "@mariozechner/pi-tui": "^0.72.0",
66
66
  "@sinclair/typebox": "^0.34.49"
67
+ },
68
+ "dependencies": {
69
+ "jsonc-parser": "^3.3.1"
67
70
  }
68
71
  }
@@ -31,7 +31,7 @@
31
31
  }
32
32
  },
33
33
  "tools": {
34
- "description": "Exact-name permissions for registered tools. Use this map for the canonical Pi built-ins and any extension-provided or third-party tools.",
34
+ "description": "Pattern-based permissions for registered tools. Use exact names or `*` wildcards for canonical Pi built-ins and any extension-provided or third-party tools.",
35
35
  "$ref": "#/$defs/permissionMap"
36
36
  },
37
37
  "bash": {
@@ -1,7 +1,7 @@
1
1
  import type { ExtensionAPI, ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
2
2
  import type { SettingItem } from "@mariozechner/pi-tui";
3
3
 
4
- import { cloneDefaultConfig, type PermissionSystemExtensionConfig } from "./extension-config.js";
4
+ import type { PermissionSystemExtensionConfig } from "./extension-config.js";
5
5
  import { ZellijModal, ZellijSettingsModal } from "./zellij-modal.js";
6
6
 
7
7
  interface PermissionSystemConfigController {
@@ -15,42 +15,11 @@ interface SettingValueSyncTarget {
15
15
  }
16
16
 
17
17
  const ON_OFF = ["on", "off"];
18
- const COMMAND_ARGUMENTS = [
19
- {
20
- value: "show",
21
- label: "Show active settings",
22
- description: "Display the current permission-system config summary",
23
- },
24
- {
25
- value: "path",
26
- label: "Show config path",
27
- description: "Display the config.json path used by pi-permission-system",
28
- },
29
- {
30
- value: "reset",
31
- label: "Reset defaults",
32
- description: "Restore default yolo/logging settings and persist them",
33
- },
34
- {
35
- value: "help",
36
- label: "Show help",
37
- description: "Display command usage",
38
- },
39
- ] as const;
40
- const USAGE_TEXT = "Usage: /permission-system [show|path|reset|help] (or run /permission-system with no args to open settings modal)";
41
18
 
42
19
  function toOnOff(value: boolean): string {
43
20
  return value ? "on" : "off";
44
21
  }
45
22
 
46
- function summarizeConfig(config: PermissionSystemExtensionConfig): string {
47
- return [
48
- `yoloMode=${toOnOff(config.yoloMode)}`,
49
- `permissionReviewLog=${toOnOff(config.permissionReviewLog)}`,
50
- `debugLog=${toOnOff(config.debugLog)}`,
51
- ].join(", ");
52
- }
53
-
54
23
  function buildSettingItems(config: PermissionSystemExtensionConfig): SettingItem[] {
55
24
  return [
56
25
  {
@@ -100,16 +69,6 @@ function syncSettingValues(settingsList: SettingValueSyncTarget, config: Permiss
100
69
  settingsList.updateValue("debugLog", toOnOff(config.debugLog));
101
70
  }
102
71
 
103
- function getArgumentCompletions(argumentPrefix: string): Array<{ value: string; label: string; description: string }> | null {
104
- const normalized = argumentPrefix.trim().toLowerCase();
105
- if (normalized.includes(" ")) {
106
- return null;
107
- }
108
-
109
- const filtered = COMMAND_ARGUMENTS.filter((item) => item.value.startsWith(normalized));
110
- return filtered.length > 0 ? [...filtered] : null;
111
- }
112
-
113
72
  async function openSettingsModal(ctx: ExtensionCommandContext, controller: PermissionSystemConfigController): Promise<void> {
114
73
  const overlayOptions = { anchor: "center" as const, width: 82, maxHeight: "85%" as const, margin: 1 };
115
74
 
@@ -132,7 +91,7 @@ async function openSettingsModal(ctx: ExtensionCommandContext, controller: Permi
132
91
  }
133
92
  },
134
93
  onClose: () => done(),
135
- helpText: `/permission-system show • /permission-system reset • ${controller.getConfigPath()}`,
94
+ helpText: `Config file: ${controller.getConfigPath()}`,
136
95
  enableSearch: true,
137
96
  },
138
97
  theme,
@@ -172,46 +131,10 @@ async function openSettingsModal(ctx: ExtensionCommandContext, controller: Permi
172
131
  );
173
132
  }
174
133
 
175
- function handleArgs(args: string, ctx: ExtensionCommandContext, controller: PermissionSystemConfigController): boolean {
176
- const normalized = args.trim().toLowerCase();
177
- if (!normalized) {
178
- return false;
179
- }
180
-
181
- if (normalized === "show") {
182
- ctx.ui.notify(`permission-system: ${summarizeConfig(controller.getConfig())}`, "info");
183
- return true;
184
- }
185
-
186
- if (normalized === "path") {
187
- ctx.ui.notify(`permission-system config: ${controller.getConfigPath()}`, "info");
188
- return true;
189
- }
190
-
191
- if (normalized === "reset") {
192
- controller.setConfig(cloneDefaultConfig(), ctx);
193
- ctx.ui.notify("Permission system settings reset to defaults.", "info");
194
- return true;
195
- }
196
-
197
- if (normalized === "help") {
198
- ctx.ui.notify(USAGE_TEXT, "info");
199
- return true;
200
- }
201
-
202
- ctx.ui.notify(USAGE_TEXT, "warning");
203
- return true;
204
- }
205
-
206
134
  export function registerPermissionSystemCommand(pi: ExtensionAPI, controller: PermissionSystemConfigController): void {
207
135
  pi.registerCommand("permission-system", {
208
136
  description: "Configure pi-permission-system logging and yolo-mode behavior",
209
- getArgumentCompletions,
210
- handler: async (args, ctx) => {
211
- if (handleArgs(args, ctx, controller)) {
212
- return;
213
- }
214
-
137
+ handler: async (_args, ctx) => {
215
138
  if (!ctx.hasUI) {
216
139
  ctx.ui.notify("/permission-system requires interactive TUI mode.", "warning");
217
140
  return;
@@ -3,6 +3,7 @@ import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
 
5
5
  import { toRecord } from "./common.js";
6
+ import { formatJsoncConfigLoadWarning, parseJsoncConfig } from "./jsonc-config.js";
6
7
 
7
8
  export const EXTENSION_ID = "pi-permission-system";
8
9
 
@@ -107,7 +108,7 @@ export function loadPermissionSystemConfig(configPath = getPermissionSystemConfi
107
108
 
108
109
  try {
109
110
  const raw = readFileSync(configPath, "utf-8");
110
- const parsed = JSON.parse(raw) as unknown;
111
+ const parsed = parseJsoncConfig(raw, configPath, "permission-system config");
111
112
  const config = normalizePermissionSystemConfig(parsed);
112
113
  return {
113
114
  config,
@@ -115,11 +116,12 @@ export function loadPermissionSystemConfig(configPath = getPermissionSystemConfi
115
116
  warning: ensureResult.warning,
116
117
  };
117
118
  } catch (error) {
118
- const message = error instanceof Error ? error.message : String(error);
119
119
  return {
120
120
  config: cloneDefaultConfig(),
121
121
  created: ensureResult.created,
122
- warning: ensureResult.warning ?? `Failed to read permission-system config at '${configPath}': ${message}`,
122
+ warning: ensureResult.warning
123
+ ?? formatJsoncConfigLoadWarning(configPath, error, "permission-system config", "using default extension config")
124
+ ?? undefined,
123
125
  };
124
126
  }
125
127
  }