pi-permission-system 0.4.1 → 0.4.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,56 @@ 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.3] - 2026-04-22
11
+
12
+ ### Added
13
+ - Added `src/skill-prompt-sanitizer.ts` to centralize skill prompt parsing and sanitization helpers
14
+
15
+ ### Changed
16
+ - Updated `@mariozechner/pi-coding-agent` and `@mariozechner/pi-tui` peer dependencies to `^0.68.1`
17
+ - Refactored `src/index.ts` to reuse shared skill prompt sanitizer helpers
18
+
19
+ ### Fixed
20
+ - Skill prompt sanitization now removes denied skills from every `<available_skills>` block and drops fully denied blocks from the prompt
21
+ - Hidden skills are no longer retained for skill path matching after sanitization
22
+
23
+ ### Tests
24
+ - Added regression coverage for multi-block `<available_skills>` parsing, sanitization, and visible-skill path matching
25
+
26
+ ## [0.4.2] - 2026-04-20
27
+
28
+ ### Added
29
+ - Added project-level permission layering from the active session workspace via `<cwd>/.pi/agent/pi-permissions.jsonc`
30
+ - Added project-level per-agent overrides via `<cwd>/.pi/agent/agents/<agent>.md` (thanks to @Talia-12 for PR #7)
31
+ - Added reload-aware permission manager refresh paths so policy caches are rebuilt when Pi reload events occur
32
+ - Added a dedicated `tests/` directory with modular test entrypoints and a shared test harness
33
+ - Added before-agent-start caching module to dedupe unchanged active-tool exposure and prompt state across `before_agent_start` lifecycle invocations
34
+ - Added `PermissionPromptDecision` type with `state` and `denialReason` fields for richer permission prompt resolution
35
+ - Added `getPolicyCacheStamp()` method to `PermissionManager` for cache invalidation tracking
36
+
37
+ ### Changed
38
+ - 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)
39
+ - Updated `@mariozechner/pi-coding-agent` and `@mariozechner/pi-tui` peer dependencies to `^0.67.68`
40
+ - Updated TypeScript project configuration and npm scripts to run tests from `tests/` instead of `src/`
41
+ - Updated README documentation for project-level policy files, yolo mode config, test layout, and `PI_CODING_AGENT_DIR`
42
+ - Permission prompts and forwarding now return `PermissionPromptDecision` instead of boolean for richer resolution tracking
43
+ - Permission denial messages now include user-provided denial reasons when available
44
+
45
+ ### Removed
46
+ - Removed the legacy packaged `asset/` directory because the README now uses externally hosted images instead of repository-bundled screenshots
47
+
48
+ ### Fixed
49
+ - `/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)
50
+ - Skill denial messaging now reflects whether the block came from an agent-specific rule or the merged policy without agent context
51
+
52
+ ### Tests
53
+ - Added coverage for project-level precedence across global, project, system-agent, and project-agent layers
54
+ - Added coverage for resolving config from `PI_CODING_AGENT_DIR`
55
+ - Added coverage for before-agent-start cache key generation and state deduplication
56
+ - Added coverage for cache invalidation on permission policy changes
57
+
8
58
  ## [0.4.1] - 2026-04-01
9
59
 
10
60
  ### Changed
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 🔐 pi-permission-system
2
2
 
3
- [![Version](https://img.shields.io/badge/version-0.4.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.
@@ -26,18 +26,20 @@ Permission enforcement extension for the Pi coding agent that provides centraliz
26
26
 
27
27
  Place this folder in one of the following locations:
28
28
 
29
- | Scope | Path |
30
- |---------|-----------------------------------------------|
31
- | Global | `~/.pi/agent/extensions/pi-permission-system` |
32
- | 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` |
33
33
 
34
34
  Pi auto-discovers extensions in these paths.
35
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
+
36
38
  ## Usage
37
39
 
38
40
  ### Quick Start
39
41
 
40
- 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`):
41
43
 
42
44
  ```jsonc
43
45
  {
@@ -82,20 +84,20 @@ The extension integrates via Pi's lifecycle hooks:
82
84
  - The `Available tools:` system prompt section is rewritten to match the filtered active tool set
83
85
  - Extension-provided tools like `task`, `mcp`, and third-party tools are handled by exact registered name instead of private built-in hardcodes
84
86
  - When a subagent hits an `ask` permission without direct UI access, the request can be forwarded to the main interactive session for confirmation
85
- - When a subagent triggers an `ask` permission without UI access, the request can be forwarded to the main session and answered there
86
87
 
87
88
  ## Configuration
88
89
 
89
90
  ### Extension Config File
90
91
 
91
- **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`)
92
93
 
93
94
  The extension creates this file automatically when it is missing. It controls only extension-local logging behavior:
94
95
 
95
96
  ```json
96
97
  {
97
98
  "debugLog": false,
98
- "permissionReviewLog": true
99
+ "permissionReviewLog": true,
100
+ "yoloMode": false
99
101
  }
100
102
  ```
101
103
 
@@ -103,12 +105,13 @@ The extension creates this file automatically when it is missing. It controls on
103
105
  |-----|---------|-------------|
104
106
  | `debugLog` | `false` | Enables verbose diagnostic logging to `logs/pi-permission-system-debug.jsonl` |
105
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 |
106
109
 
107
110
  Both logs write to files only under the extension directory. No debug output is printed to the terminal.
108
111
 
109
112
  ### Global Policy File
110
113
 
111
- **Location:** `~/.pi/agent/pi-permissions.jsonc`
114
+ **Location:** global Pi policy file (default: `~/.pi/agent/pi-permissions.jsonc`, respects `PI_CODING_AGENT_DIR`)
112
115
 
113
116
  The policy file is a JSON object with these sections:
114
117
 
@@ -123,9 +126,9 @@ The policy file is a JSON object with these sections:
123
126
 
124
127
  > **Note:** Trailing commas are **not** supported. If parsing fails, the extension falls back to `ask` for all categories.
125
128
 
126
- ### Per-Agent Overrides
129
+ ### Global Per-Agent Overrides
127
130
 
128
- 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`):
129
132
 
130
133
  ```yaml
131
134
  ---
@@ -146,12 +149,29 @@ permission:
146
149
  ---
147
150
  ```
148
151
 
149
- **Precedence:** Agent frontmatter overrides global config (shallow-merged per section).
150
-
151
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.
152
153
 
153
154
  **Limitations:** The frontmatter parser is intentionally minimal. Use only `key: value` scalars and nested maps. Avoid arrays, multi-line scalars, and YAML anchors.
154
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
+
155
175
  ---
156
176
 
157
177
  ## Policy Reference
@@ -256,7 +276,7 @@ A registered `mcp` tool can use `tools.mcp` as an entry permission point. This p
256
276
  This is useful for per-agent configurations where you want to grant MCP access broadly:
257
277
 
258
278
  ```yaml
259
- # In ~/.pi/agent/agents/researcher.md
279
+ # In the global Pi agents directory (default: ~/.pi/agent/agents/researcher.md; respects PI_CODING_AGENT_DIR)
260
280
  ---
261
281
  name: researcher
262
282
  permission:
@@ -353,7 +373,7 @@ Reserved permission checks:
353
373
 
354
374
  ### Per-Agent Lockdown
355
375
 
356
- In `~/.pi/agent/agents/reviewer.md`:
376
+ In the global Pi agents directory (default: `~/.pi/agent/agents/reviewer.md`, respects `PI_CODING_AGENT_DIR`):
357
377
 
358
378
  ```yaml
359
379
  ---
@@ -381,7 +401,8 @@ This keeps `ask` policies usable even when the original permission check happens
381
401
  When the extension prompts, denies, or forwards permission requests, it can append structured JSONL entries under:
382
402
 
383
403
  ```text
384
- ~/.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
385
406
  ```
386
407
 
387
408
  - `pi-permission-system-permission-review.jsonl` — enabled by default for permission review/audit history
@@ -392,16 +413,19 @@ When the extension prompts, denies, or forwards permission requests, it can appe
392
413
  ```
393
414
  index.ts → Root Pi entrypoint shim
394
415
  src/
395
- ├── 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
396
417
  ├── extension-config.ts → Extension-local config loading and default creation
397
418
  ├── logging.ts → File-only debug/review logging helpers
398
- ├── permission-manager.ts → Policy loading, merging, and resolution with caching
419
+ ├── permission-manager.ts → Global/project policy loading, merging, and resolution with caching
399
420
  ├── bash-filter.ts → Bash command wildcard pattern matching
400
421
  ├── wildcard-matcher.ts → Shared wildcard pattern compilation and matching
401
422
  ├── common.ts → Shared utilities (YAML parsing, type guards, etc.)
402
423
  ├── tool-registry.ts → Registered tool name resolution
403
- ├── types.ts → TypeScript type definitions
404
- └── 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
405
429
  schemas/
406
430
  └── permissions.schema.json → JSON Schema for policy validation
407
431
  config/
@@ -456,10 +480,10 @@ npx --yes ajv-cli@5 validate \
456
480
 
457
481
  | Problem | Cause | Solution |
458
482
  |---------|-------|----------|
459
- | 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 |
460
484
  | Per-agent override not applied | Frontmatter parsing issue | Ensure `---` delimiters at file top; keep YAML simple; restart session |
461
485
  | Tool blocked as unregistered | Unknown tool name | Use a registered `mcp` tool for server tools: `{ "tool": "server:tool" }` |
462
- | `/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. |
463
487
 
464
488
  ---
465
489
 
@@ -468,7 +492,7 @@ npx --yes ajv-cli@5 validate \
468
492
  ```bash
469
493
  npm run build # Compile TypeScript
470
494
  npm run lint # Run linter (uses build)
471
- npm run test # Run tests
495
+ npm run test # Run tests from ./tests
472
496
  npm run check # Run lint + test
473
497
  ```
474
498
 
package/package.json CHANGED
@@ -1,65 +1,65 @@
1
- {
2
- "name": "pi-permission-system",
3
- "version": "0.4.1",
4
- "description": "Permission enforcement extension for the Pi coding agent.",
5
- "type": "module",
6
- "main": "./index.ts",
7
- "exports": {
8
- ".": "./index.ts"
9
- },
10
- "files": [
11
- "index.ts",
12
- "src",
13
- "config.json",
14
- "config/config.example.json",
15
- "schemas/permissions.schema.json",
16
- "asset",
17
- "README.md",
18
- "CHANGELOG.md",
19
- "LICENSE"
20
- ],
21
- "scripts": {
22
- "build": "npx --yes -p typescript@5.7.3 tsc -p tsconfig.json --noCheck",
23
- "lint": "npm run build",
24
- "test": "bun ./src/test.ts && bun ./src/config-modal-test.ts",
25
- "check": "npm run lint && npm run test"
26
- },
27
- "keywords": [
28
- "pi-package",
29
- "pi",
30
- "pi-extension",
31
- "pi-coding-agent",
32
- "coding-agent",
33
- "permissions",
34
- "policy",
35
- "access-control",
36
- "authorization",
37
- "security"
38
- ],
39
- "author": "MasuRii",
40
- "license": "MIT",
41
- "repository": {
42
- "type": "git",
43
- "url": "git+https://github.com/MasuRii/pi-permission-system.git"
44
- },
45
- "homepage": "https://github.com/MasuRii/pi-permission-system#readme",
46
- "bugs": {
47
- "url": "https://github.com/MasuRii/pi-permission-system/issues"
48
- },
49
- "engines": {
50
- "node": ">=20"
51
- },
52
- "publishConfig": {
53
- "access": "public"
54
- },
55
- "pi": {
56
- "extensions": [
57
- "./index.ts"
58
- ]
59
- },
60
- "peerDependencies": {
61
- "@mariozechner/pi-coding-agent": "^0.64.0",
62
- "@mariozechner/pi-tui": "^0.64.0",
63
- "@sinclair/typebox": "^0.34.49"
64
- }
65
- }
1
+ {
2
+ "name": "pi-permission-system",
3
+ "version": "0.4.3",
4
+ "description": "Permission enforcement extension for the Pi coding agent.",
5
+ "type": "module",
6
+ "main": "./index.ts",
7
+ "exports": {
8
+ ".": "./index.ts"
9
+ },
10
+ "files": [
11
+ "index.ts",
12
+ "src",
13
+ "tests",
14
+ "config.json",
15
+ "config/config.example.json",
16
+ "schemas/permissions.schema.json",
17
+ "README.md",
18
+ "CHANGELOG.md",
19
+ "LICENSE"
20
+ ],
21
+ "scripts": {
22
+ "build": "npx --yes -p typescript@5.7.3 tsc -p tsconfig.json --noCheck",
23
+ "lint": "npm run build",
24
+ "test": "bun ./tests/permission-system.test.ts && bun ./tests/config-modal.test.ts",
25
+ "check": "npm run lint && npm run test"
26
+ },
27
+ "keywords": [
28
+ "pi-package",
29
+ "pi",
30
+ "pi-extension",
31
+ "pi-coding-agent",
32
+ "coding-agent",
33
+ "permissions",
34
+ "policy",
35
+ "access-control",
36
+ "authorization",
37
+ "security"
38
+ ],
39
+ "author": "MasuRii",
40
+ "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/MasuRii/pi-permission-system.git"
44
+ },
45
+ "homepage": "https://github.com/MasuRii/pi-permission-system#readme",
46
+ "bugs": {
47
+ "url": "https://github.com/MasuRii/pi-permission-system/issues"
48
+ },
49
+ "engines": {
50
+ "node": ">=20"
51
+ },
52
+ "publishConfig": {
53
+ "access": "public"
54
+ },
55
+ "pi": {
56
+ "extensions": [
57
+ "./index.ts"
58
+ ]
59
+ },
60
+ "peerDependencies": {
61
+ "@mariozechner/pi-coding-agent": "^0.68.1",
62
+ "@mariozechner/pi-tui": "^0.68.1",
63
+ "@sinclair/typebox": "^0.34.49"
64
+ }
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
+ }