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.3.0- Published
- Jun 13, 2026
- Downloads
- 559/mo · 44/wk
- Author
- gsanhueza
- License
- MIT
- Types
- extension
- Size
- 289.1 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 threshold | 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/*"]
},
"tools": {
"minimal": ["read", "ls", "grep", "find"],
"low": ["write", "edit"]
},
"mcp": {
"minimal": ["search", "describe", "list", "status", "connect"],
"low": ["serper_search", "serper_scrape", "github_list_commits"],
"medium": ["github_create_issue"]
},
"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 |
|---|---|---|---|
overrides |
PermissionOverrides |
{} |
Per-level glob patterns for shell command overrides |
tools |
ToolPermissionConfig |
{} |
Per-level tool name assignments (delta model — only specify what you want to change) |
mcp |
McpPermissionConfig |
{} |
Per-level MCP tool/mode name assignments (delta model — only specify what you want to change) |
prefixMappings |
PermissionPrefixMapping[] |
[] |
Normalize version-manager commands to their base tools |
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.
Per-command overrides: For compound commands (pipes,
&&,||,;), each command is checked against overrides independently. If a command matches an override pattern, it uses the override level instead of its default classification. For example,cd /tmp && command_awithoverrides.low: ["command_a"]classifiescommand_aas low (not high from default), and the final result is low sincecd /tmpis 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.
Example: Full Config with Tools and MCP Overrides
{
"permissionConfig": {
"overrides": {
"minimal": ["tmux list-*", "tmux show-*"],
"medium": ["tmux attach*", "tmux new*"]
},
"tools": {
"low": ["read"] // move read up from minimal to low
},
"mcp": {
"medium": ["github_create_issue"] // require medium for this tool
},
"prefixMappings": [
{ "from": "fvm flutter", "to": "flutter" }
]
}
}
Tool Permission Config (tools)
Assign permission levels to individual tool names. This uses a delta/override model: only specify what you want to change — unmentioned tools keep their default levels.
| Level | Default tools | Description |
|---|---|---|
minimal |
read, ls, grep, find |
Read-only operations |
low |
write, edit |
File operations |
medium |
(implicit) | All other tools (blocked by default) |
high |
(implicit — blocked) | Unknown tools require high permission |
dangerous |
(none by default) | Always prompt, even at high level |
Example: { "minimal": ["read"], "low": ["grep"] }:
read→ minimal (explicit override)grep→ low (explicit override, moves up from default minimal)ls,find→ minimal (defaults preserved — not mentioned in config)write,edit→ low (defaults preserved)
Note:
bashis ignored in thetoolsconfig. Shell commands are classified throughclassifyCommand()and theoverridesconfig instead.
MCP Permission Config (mcp)
Assign permission levels to MCP tools and modes. MCP config supports two types of entries:
- Mode names (
search,describe,list,status,connect,call,action) — match against the call's mode - Tool names (
serper_search,github_list_commits) — match against the specific tool
Tool name match takes precedence over mode match (more specific → more general).
| Mode | Description |
|---|---|
search |
MCP search mode |
describe |
MCP describe mode |
list |
MCP list mode (server listing) |
status |
MCP status mode |
connect |
MCP connect mode |
call |
MCP tool call mode (default when tool is specified) |
action |
MCP action mode |
| Level | Default entries | Description |
|---|---|---|
minimal |
search, describe, list, status, connect (modes) |
Read-only MCP modes |
low |
~45 read-only MCP tools (GitHub read, Atlassian read, etc.) | Read-only MCP tools |
medium |
(implicit) | All other MCP tools |
high |
(implicit — blocked) | Unknown MCP tools require high permission |
dangerous |
(none by default) | Always prompt, even at high level |
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
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).