pi-permission-system 0.4.0 → 0.4.2

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,45 @@ 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
+ ## [0.4.2] - 2026-04-20
9
+
10
+ ### Added
11
+ - Added project-level permission layering from the active session workspace via `<cwd>/.pi/agent/pi-permissions.jsonc`
12
+ - Added project-level per-agent overrides via `<cwd>/.pi/agent/agents/<agent>.md` (thanks to @Talia-12 for PR #7)
13
+ - Added reload-aware permission manager refresh paths so policy caches are rebuilt when Pi reload events occur
14
+ - Added a dedicated `tests/` directory with modular test entrypoints and a shared test harness
15
+ - Added before-agent-start caching module to dedupe unchanged active-tool exposure and prompt state across `before_agent_start` lifecycle invocations
16
+ - Added `PermissionPromptDecision` type with `state` and `denialReason` fields for richer permission prompt resolution
17
+ - Added `getPolicyCacheStamp()` method to `PermissionManager` for cache invalidation tracking
18
+
19
+ ### Changed
20
+ - Global path resolution now follows Pi's `getAgentDir()` helper, so global config, agents, sessions, and logs respect `PI_CODING_AGENT_DIR` (thanks to @jvortmann for PR #6)
21
+ - Updated `@mariozechner/pi-coding-agent` and `@mariozechner/pi-tui` peer dependencies to `^0.67.68`
22
+ - Updated TypeScript project configuration and npm scripts to run tests from `tests/` instead of `src/`
23
+ - Updated README documentation for project-level policy files, yolo mode config, test layout, and `PI_CODING_AGENT_DIR`
24
+ - Permission prompts and forwarding now return `PermissionPromptDecision` instead of boolean for richer resolution tracking
25
+ - Permission denial messages now include user-provided denial reasons when available
26
+
27
+ ### Removed
28
+ - Removed the legacy packaged `asset/` directory because the README now uses externally hosted images instead of repository-bundled screenshots
29
+
30
+ ### Fixed
31
+ - `/skill:<name>` permission handling now falls back to the current merged skill policy when no active agent context is available in the main session (thanks to @NSBeidou and @hidromagnetismo for reporting the issue)
32
+ - Skill denial messaging now reflects whether the block came from an agent-specific rule or the merged policy without agent context
33
+
34
+ ### Tests
35
+ - Added coverage for project-level precedence across global, project, system-agent, and project-agent layers
36
+ - Added coverage for resolving config from `PI_CODING_AGENT_DIR`
37
+ - Added coverage for before-agent-start cache key generation and state deduplication
38
+ - Added coverage for cache invalidation on permission policy changes
39
+
40
+ ## [0.4.1] - 2026-04-01
41
+
42
+ ### Changed
43
+ - Updated npm keywords for improved discoverability (`pi-coding-agent`, `coding-agent`, `access-control`, `authorization`, `security`)
44
+ - Updated README permission prompt example image
45
+ - Added Related Pi Extensions cross-linking section to README
46
+
8
47
  ## [0.4.0] - 2026-04-01
9
48
 
10
49
  ### Added
package/README.md CHANGED
@@ -1,11 +1,12 @@
1
1
  # 🔐 pi-permission-system
2
2
 
3
- [![Version](https://img.shields.io/badge/version-0.3.1-blue.svg)](package.json)
3
+ [![Version](https://img.shields.io/badge/version-0.4.2-blue.svg)](package.json)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
5
 
6
6
  Permission enforcement extension for the Pi coding agent that provides centralized, deterministic permission gates for tool, bash, MCP, skill, and special operations.
7
7
 
8
- ![Permission Prompt Example](https://raw.githubusercontent.com/MasuRii/pi-permission-system/main/asset/pi-permission-system.png)
8
+ <img width="1360" height="752" alt="image" src="https://github.com/user-attachments/assets/3e85190a-17fa-4d94-ac8e-efa54337df5d" />
9
+
9
10
 
10
11
  ## Features
11
12
 
@@ -25,18 +26,20 @@ Permission enforcement extension for the Pi coding agent that provides centraliz
25
26
 
26
27
  Place this folder in one of the following locations:
27
28
 
28
- | Scope | Path |
29
- |---------|-----------------------------------------------|
30
- | Global | `~/.pi/agent/extensions/pi-permission-system` |
31
- | Project | `.pi/extensions/pi-permission-system` |
29
+ | Scope | Path |
30
+ |---------|------|
31
+ | Global default | `~/.pi/agent/extensions/pi-permission-system` (respects `PI_CODING_AGENT_DIR`) |
32
+ | Project | `.pi/extensions/pi-permission-system` |
32
33
 
33
34
  Pi auto-discovers extensions in these paths.
34
35
 
36
+ > **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.
37
+
35
38
  ## Usage
36
39
 
37
40
  ### Quick Start
38
41
 
39
- 1. Create the global policy file at `~/.pi/agent/pi-permissions.jsonc`:
42
+ 1. Create the global policy file at the Pi agent runtime root (default: `~/.pi/agent/pi-permissions.jsonc`, respects `PI_CODING_AGENT_DIR`):
40
43
 
41
44
  ```jsonc
42
45
  {
@@ -81,20 +84,20 @@ The extension integrates via Pi's lifecycle hooks:
81
84
  - The `Available tools:` system prompt section is rewritten to match the filtered active tool set
82
85
  - Extension-provided tools like `task`, `mcp`, and third-party tools are handled by exact registered name instead of private built-in hardcodes
83
86
  - When a subagent hits an `ask` permission without direct UI access, the request can be forwarded to the main interactive session for confirmation
84
- - When a subagent triggers an `ask` permission without UI access, the request can be forwarded to the main session and answered there
85
87
 
86
88
  ## Configuration
87
89
 
88
90
  ### Extension Config File
89
91
 
90
- **Location:** `~/.pi/agent/extensions/pi-permission-system/config.json`
92
+ **Location:** global Pi extension config (default: `~/.pi/agent/extensions/pi-permission-system/config.json`, respects `PI_CODING_AGENT_DIR`)
91
93
 
92
94
  The extension creates this file automatically when it is missing. It controls only extension-local logging behavior:
93
95
 
94
96
  ```json
95
97
  {
96
98
  "debugLog": false,
97
- "permissionReviewLog": true
99
+ "permissionReviewLog": true,
100
+ "yoloMode": false
98
101
  }
99
102
  ```
100
103
 
@@ -102,12 +105,13 @@ The extension creates this file automatically when it is missing. It controls on
102
105
  |-----|---------|-------------|
103
106
  | `debugLog` | `false` | Enables verbose diagnostic logging to `logs/pi-permission-system-debug.jsonl` |
104
107
  | `permissionReviewLog` | `true` | Enables the permission request/denial review log at `logs/pi-permission-system-permission-review.jsonl` |
108
+ | `yoloMode` | `false` | Auto-approves `ask` results instead of prompting when yolo mode is enabled |
105
109
 
106
110
  Both logs write to files only under the extension directory. No debug output is printed to the terminal.
107
111
 
108
112
  ### Global Policy File
109
113
 
110
- **Location:** `~/.pi/agent/pi-permissions.jsonc`
114
+ **Location:** global Pi policy file (default: `~/.pi/agent/pi-permissions.jsonc`, respects `PI_CODING_AGENT_DIR`)
111
115
 
112
116
  The policy file is a JSON object with these sections:
113
117
 
@@ -122,9 +126,9 @@ The policy file is a JSON object with these sections:
122
126
 
123
127
  > **Note:** Trailing commas are **not** supported. If parsing fails, the extension falls back to `ask` for all categories.
124
128
 
125
- ### Per-Agent Overrides
129
+ ### Global Per-Agent Overrides
126
130
 
127
- Override global permissions for specific agents via YAML frontmatter in `~/.pi/agent/agents/<agent>.md`:
131
+ 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`):
128
132
 
129
133
  ```yaml
130
134
  ---
@@ -145,12 +149,29 @@ permission:
145
149
  ---
146
150
  ```
147
151
 
148
- **Precedence:** Agent frontmatter overrides global config (shallow-merged per section).
149
-
150
152
  **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.
151
153
 
152
154
  **Limitations:** The frontmatter parser is intentionally minimal. Use only `key: value` scalars and nested maps. Avoid arrays, multi-line scalars, and YAML anchors.
153
155
 
156
+ ### Project-Level Policy Files
157
+
158
+ The extension can also layer project-local permission files relative to the active session working directory:
159
+
160
+ | Scope | Path |
161
+ |-------|------|
162
+ | Project policy | `<cwd>/.pi/agent/pi-permissions.jsonc` |
163
+ | Project agent override | `<cwd>/.pi/agent/agents/<agent>.md` |
164
+
165
+ 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`.
166
+
167
+ **Precedence order:**
168
+ 1. Global policy file
169
+ 2. Project policy file
170
+ 3. Global agent frontmatter
171
+ 4. Project agent frontmatter
172
+
173
+ Later layers override earlier layers within the same permission category. For wildcard-based sections like `bash`, `mcp`, `skills`, and `special`, matching still follows the extension's existing **last matching rule wins** behavior after the layers are combined.
174
+
154
175
  ---
155
176
 
156
177
  ## Policy Reference
@@ -255,7 +276,7 @@ A registered `mcp` tool can use `tools.mcp` as an entry permission point. This p
255
276
  This is useful for per-agent configurations where you want to grant MCP access broadly:
256
277
 
257
278
  ```yaml
258
- # In ~/.pi/agent/agents/researcher.md
279
+ # In the global Pi agents directory (default: ~/.pi/agent/agents/researcher.md; respects PI_CODING_AGENT_DIR)
259
280
  ---
260
281
  name: researcher
261
282
  permission:
@@ -352,7 +373,7 @@ Reserved permission checks:
352
373
 
353
374
  ### Per-Agent Lockdown
354
375
 
355
- In `~/.pi/agent/agents/reviewer.md`:
376
+ In the global Pi agents directory (default: `~/.pi/agent/agents/reviewer.md`, respects `PI_CODING_AGENT_DIR`):
356
377
 
357
378
  ```yaml
358
379
  ---
@@ -380,7 +401,8 @@ This keeps `ask` policies usable even when the original permission check happens
380
401
  When the extension prompts, denies, or forwards permission requests, it can append structured JSONL entries under:
381
402
 
382
403
  ```text
383
- ~/.pi/agent/extensions/pi-permission-system/logs/
404
+ Default global logs directory: ~/.pi/agent/extensions/pi-permission-system/logs/
405
+ Actual global logs directory: $PI_CODING_AGENT_DIR/extensions/pi-permission-system/logs when PI_CODING_AGENT_DIR is set
384
406
  ```
385
407
 
386
408
  - `pi-permission-system-permission-review.jsonl` — enabled by default for permission review/audit history
@@ -391,16 +413,19 @@ When the extension prompts, denies, or forwards permission requests, it can appe
391
413
  ```
392
414
  index.ts → Root Pi entrypoint shim
393
415
  src/
394
- ├── index.ts → Extension bootstrap, permission checks, review logging, and subagent forwarding
416
+ ├── index.ts → Extension bootstrap, permission checks, review logging, reload handling, and subagent forwarding
395
417
  ├── extension-config.ts → Extension-local config loading and default creation
396
418
  ├── logging.ts → File-only debug/review logging helpers
397
- ├── permission-manager.ts → Policy loading, merging, and resolution with caching
419
+ ├── permission-manager.ts → Global/project policy loading, merging, and resolution with caching
398
420
  ├── bash-filter.ts → Bash command wildcard pattern matching
399
421
  ├── wildcard-matcher.ts → Shared wildcard pattern compilation and matching
400
422
  ├── common.ts → Shared utilities (YAML parsing, type guards, etc.)
401
423
  ├── tool-registry.ts → Registered tool name resolution
402
- ├── types.ts → TypeScript type definitions
403
- └── test.ts → Test runner
424
+ └── types.ts → TypeScript type definitions
425
+ tests/
426
+ ├── permission-system.test.ts → Core permission, layering, forwarding, and policy tests
427
+ ├── config-modal.test.ts → Config command and modal behavior tests
428
+ └── test-harness.ts → Shared lightweight test helpers
404
429
  schemas/
405
430
  └── permissions.schema.json → JSON Schema for policy validation
406
431
  config/
@@ -455,10 +480,10 @@ npx --yes ajv-cli@5 validate \
455
480
 
456
481
  | Problem | Cause | Solution |
457
482
  |---------|-------|----------|
458
- | Config not applied (everything asks) | File not found or parse error | Verify file at `~/.pi/agent/pi-permissions.jsonc`; check for trailing commas |
483
+ | 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 |
459
484
  | Per-agent override not applied | Frontmatter parsing issue | Ensure `---` delimiters at file top; keep YAML simple; restart session |
460
485
  | Tool blocked as unregistered | Unknown tool name | Use a registered `mcp` tool for server tools: `{ "tool": "server:tool" }` |
461
- | `/skill:<name>` blocked | Missing context or deny policy | Requires active agent context; `ask` behaves as block in headless mode |
486
+ | `/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. |
462
487
 
463
488
  ---
464
489
 
@@ -467,12 +492,19 @@ npx --yes ajv-cli@5 validate \
467
492
  ```bash
468
493
  npm run build # Compile TypeScript
469
494
  npm run lint # Run linter (uses build)
470
- npm run test # Run tests
495
+ npm run test # Run tests from ./tests
471
496
  npm run check # Run lint + test
472
497
  ```
473
498
 
474
499
  ---
475
500
 
501
+ ## Related Pi Extensions
502
+
503
+ - [pi-multi-auth](https://github.com/MasuRii/pi-multi-auth) — Multi-provider credential management and quota-aware rotation
504
+ - [pi-tool-display](https://github.com/MasuRii/pi-tool-display) — Compact tool rendering and diff visualization
505
+ - [pi-rtk-optimizer](https://github.com/MasuRii/pi-rtk-optimizer) — RTK command rewriting and output compaction
506
+ - [pi-MUST-have-extension](https://github.com/MasuRii/pi-MUST-have-extension) — RFC 2119 keyword normalization for prompts
507
+
476
508
  ## License
477
509
 
478
510
  [MIT](LICENSE)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-permission-system",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Permission enforcement extension for the Pi coding agent.",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
@@ -10,10 +10,10 @@
10
10
  "files": [
11
11
  "index.ts",
12
12
  "src",
13
+ "tests",
13
14
  "config.json",
14
15
  "config/config.example.json",
15
16
  "schemas/permissions.schema.json",
16
- "asset",
17
17
  "README.md",
18
18
  "CHANGELOG.md",
19
19
  "LICENSE"
@@ -21,16 +21,20 @@
21
21
  "scripts": {
22
22
  "build": "npx --yes -p typescript@5.7.3 tsc -p tsconfig.json --noCheck",
23
23
  "lint": "npm run build",
24
- "test": "bun ./src/test.ts && bun ./src/config-modal-test.ts",
24
+ "test": "bun ./tests/permission-system.test.ts && bun ./tests/config-modal.test.ts",
25
25
  "check": "npm run lint && npm run test"
26
26
  },
27
27
  "keywords": [
28
28
  "pi-package",
29
29
  "pi",
30
30
  "pi-extension",
31
+ "pi-coding-agent",
32
+ "coding-agent",
31
33
  "permissions",
32
34
  "policy",
33
- "coding-agent"
35
+ "access-control",
36
+ "authorization",
37
+ "security"
34
38
  ],
35
39
  "author": "MasuRii",
36
40
  "license": "MIT",
@@ -54,8 +58,8 @@
54
58
  ]
55
59
  },
56
60
  "peerDependencies": {
57
- "@mariozechner/pi-coding-agent": "^0.64.0",
58
- "@mariozechner/pi-tui": "^0.64.0",
61
+ "@mariozechner/pi-coding-agent": "^0.67.68",
62
+ "@mariozechner/pi-tui": "^0.67.68",
59
63
  "@sinclair/typebox": "^0.34.49"
60
64
  }
61
65
  }
@@ -0,0 +1,37 @@
1
+ export interface BeforeAgentStartPromptStateInput {
2
+ agentName: string | null;
3
+ cwd: string;
4
+ permissionStamp: string;
5
+ systemPrompt: string;
6
+ allowedToolNames: readonly string[];
7
+ }
8
+
9
+ function normalizeAgentName(agentName: string | null): string {
10
+ return agentName ?? "";
11
+ }
12
+
13
+ function normalizePrompt(prompt: string): string {
14
+ return prompt.replace(/\r\n/g, "\n");
15
+ }
16
+
17
+ function createCacheKey(parts: readonly unknown[]): string {
18
+ return JSON.stringify(parts);
19
+ }
20
+
21
+ export function createActiveToolsCacheKey(allowedToolNames: readonly string[]): string {
22
+ return createCacheKey(allowedToolNames);
23
+ }
24
+
25
+ export function createBeforeAgentStartPromptStateKey(input: BeforeAgentStartPromptStateInput): string {
26
+ return createCacheKey([
27
+ normalizeAgentName(input.agentName),
28
+ input.cwd,
29
+ input.permissionStamp,
30
+ createActiveToolsCacheKey(input.allowedToolNames),
31
+ normalizePrompt(input.systemPrompt),
32
+ ]);
33
+ }
34
+
35
+ export function shouldApplyCachedAgentStartState(previousKey: string | null, nextKey: string): boolean {
36
+ return previousKey !== nextKey;
37
+ }