pi-permission-layers
Pi extension to handle permissions using a layered approach
Package details
Install pi-permission-layers from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-permission-layers- Package
pi-permission-layers- Version
1.1.0- Published
- May 16, 2026
- Downloads
- not available
- Author
- gsanhueza
- License
- MIT
- Types
- extension
- Size
- 183.2 KB
- Dependencies
- 1 dependency · 1 peer
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-permission-layers
A Pi Coding Agent extension that implements a layered permission control extension to protect users from unintended operations. It classifies shell commands into 5 security levels and enforces them at runtime, with configurable overrides and two permission modes (ask / block).
Note: This project is a refactored fork. See the Acknowledgements section for lineage and credits. The goal of this fork is to have a more maintainable codebase while preserving the same core idea, and to adjust a few things deemed useful to me.
Key Features
- Command classification — Automatically classifies shell commands by required permission level
- Dangerous command detection — Special handling for
rm -rf,sudo,chmod 777, etc. - MCP tool permissioning — Controls access to MCP tools (search, connect, etc.)
- Shell trick detection — Prevents bypass via
$(cmd), backticks, process substitution,eval, etc. - Configurable overrides — Users can customize classification in
~/.pi/agent/settings.json - Two permission modes —
ask(prompt) orblock(deny without asking) - Prefix mappings — Normalize version-manager commands (
fvm flutter,nvm exec, etc.) to their base tools - Terminal awareness — Detects tmux/screen for appropriate notifications
Levels
| Level | Description | Allowed Operations |
|---|---|---|
minimal |
Read-only (default) | cat, ls, grep, git status/log/diff, npm list, etc. |
low |
File operations | Output redirection (>, >>), write/edit tool calls, known read-only MCP tools |
medium |
Development operations | Create/edit files (mkdir, cp, mv, ln), npm install, builds, tests, git commit/pull, linters, package managers |
high |
Full operations | git push, deployments, curl, docker push, shell execution (eval, exec, source, env, etc.) |
bypassed |
All checks disabled | Everything (dangerous — CI/containers only) |
Dangerous commands (always require confirmation in interactive mode, always blocked in print mode): sudo, rm -rf, chmod 777, dd of=/dev/*, mkfs*, fdisk, parted, format, shutdown, reboot, halt, poweroff, init, fork bombs
Shell tricks (always classified as high): $(cmd), backticks, <(cmd), >(cmd), ${VAR:-$(cmd)}
See docs/command-classification.md for the complete classification reference.
Installation
This package is a Pi extension. Install it with
npm install pi-permission-layers
or
pi install https://github.com/gsanhueza/pi-permission-layers
Usage
Interactive Mode
Interactive mode enables the usage of the following commands:
Commands:
/permission— Show selector to change level/permission medium— Set level directly (asks session/global)/permission-mode— Switch betweenask/blockwhen permission is required/permission-mode block— Block instead of prompting/permission config show— Display current configuration/permission config reset— Reset to default (empty)/permission settings— Interactive UI to togglequietStartup/forceUI/systemNotifications
Autocomplete: All commands support argument autocomplete. After typing the command name, press Space to see available values. Nested subcommands also autocomplete — e.g. /permission config <space> shows show/reset.
When a command needs higher permission:
[Requires Medium]: $ npm install lodash
Allow once → Execute this command once
Allow all Medium (session) → Update the session's permissions and execute
Cancel → Don't execute
If permission mode is set to block, commands requiring higher permission are blocked without prompting. Use /permission-mode ask to restore prompts.
Print Mode
Permission mode is ignored in print mode; insufficient permissions always block.
# Set level via environment variable
PI_PERMISSION_LEVEL=medium pi -p "install deps and run tests"
# Bypass all permission checks (CI/containers — dangerous!)
PI_PERMISSION_LEVEL=bypassed pi -p "do anything"
If permission is insufficient: The command is blocked but execution continues. The agent receives:
$ npm install lodash
Blocked by permission (minimal). Allowed at this level: Read-only
User can re-run with: PI_PERMISSION_LEVEL=medium pi -p "..."
If a dangerous command is used:
Dangerous command requires confirmation: sudo rm -rf /tmp/foo
User can re-run with: PI_PERMISSION_LEVEL=bypassed pi -p "..."
The agent can then work around the limitation or inform the user.
Environment Variables
| Variable | Values | Description |
|---|---|---|
PI_PERMISSION_LEVEL |
minimal, low, medium, high, bypassed |
Set permission level |
PI_QUIET |
1, true, yes |
Suppress startup notifications |
PI_FORCEUI |
1, true, yes |
Force interactive UI mode |
Settings
Global settings are stored in ~/.pi/agent/settings.json:
{
"permissionLevel": "medium",
"permissionMode": "ask",
"permissionConfig": {
"overrides": {
"minimal": ["tmux list-*", "tmux show-*"],
"medium": ["tmux attach*", "tmux new*"],
"high": ["rm -rf *"],
"dangerous": ["dd if=* of=/dev/*"]
},
"prefixMappings": [
{ "from": "fvm flutter", "to": "flutter" },
{ "from": "nvm exec", "to": "" },
{ "from": "rbenv exec", "to": "" },
{ "from": "pyenv exec", "to": "" }
],
"quietStartup": true,
"forceUI": true,
"systemNotifications": "unfocused"
}
}
permissionMode accepts ask (prompt) or block (deny without prompting).
permissionConfig Options
| Option | Type | Default | Description |
|---|---|---|---|
quietStartup |
boolean |
false |
Suppress the startup notification message |
forceUI |
boolean |
false |
Force interactive UI mode regardless of context (e.g., in print mode) |
systemNotifications |
"off" | "on" | "unfocused" | "persistent" |
"unfocused" |
Control OS notifications ("off" = fully disabled, "unfocused" = only when terminal is not focused, "on" = always show, "persistent" = always show with critical/persistent priority) |
Note on Linux: Terminal focus detection is not supported on Linux, so the
"unfocused"option behaves the same as"on"(notifications are always shown).
Override Patterns
Glob patterns are matched against the full command:
*matches any characters?matches single character- Patterns are case-insensitive
Override priority (highest to lowest):
dangerous— Always prompt, even at high levelhigh— Require high permissionmedium— Require medium permissionlow— Require low permissionminimal— Allow at minimal (read-only)
Note: When a command matches patterns in multiple levels, the most restrictive level wins. Avoid overlapping patterns across levels. For example, don't put
tmux *in medium if you wanttmux list-*to be minimal.
Note on the
lowlevel:lowis not a standalone command classification level (there is noisLowLevel()classifier). Instead, it serves as a permission threshold used for output redirections, write/edit tool calls, known read-only MCP tools, and as an override target. Commands likemkdir,cp,mv,ln, andtouchare classified asmedium, notlow.
Examples:
{
"overrides": {
"minimal": [
"tmux list-*", // tmux list-sessions, tmux list-windows, etc.
"tmux show-*", // tmux show-options, tmux show-messages, etc.
"screen -list" // List screen sessions
],
"medium": [
"tmux attach*", // Attach to sessions
"tmux new*", // Create new sessions
"screen -r *" // Reattach to screen
],
"high": [
"rm -rf *", // Force rm with any arguments
"dd of=/dev/*" // dd writing to any device
],
"dangerous": [
"dd if=* of=/dev/*" // dd writing to device from any source
]
}
}
Prefix Mappings
Normalize version manager commands to their base tools:
fvm flutter build→ treated asflutter build(classified normally)nvm exec node→ treated asnode(classified normally)rbenv exec ruby→ treated asruby(classified normally)
How it works:
- Commands are checked against prefix mappings first
- If a prefix matches, it's replaced with the mapped value
- The normalized command is then classified
Testing
Run tests with:
npm run test
Test Structure
permission.test.ts — Tests
classifyCommand()directly- Covers all 5 permission levels
- Tests command parsing, pipelines, redirections
- Tests shell tricks (
$(), backticks,eval) - Tests config overrides and prefix mappings
permission-prompt.test.ts — Tests UI handler functions
- Tests prompt messages and options
- Tests Allow/Cancel/Block behavior
- Tests block mode vs ask mode
interactive-ui.test.ts — Tests
hasInteractiveUI(),isQuietMode(),notifySystem(), terminal detection,systemNotificationshandling
New features MUST be covered by tests. All command classification changes require test updates. Run
npm run testbefore committing.
Architecture
See docs/architecture.md for a complete breakdown of the codebase structure, module responsibilities, and design decisions.
Acknowledgements
This project is a fork of:
- pi-permission — the starting point of this fork, which added MCP tool permissioning and expanded classification (archived project).
- permission-pi — the original extension that established the layered permission control concept (the starting point of the fork above).