pi-safety-modes
v0.1.3
Published
Pi package providing safety modes for tool-call guardrails
Maintainers
Readme
pi-safety-modes
A small Pi extension that adds conservative runtime safety modes for tool calls.
This extension is not a sandbox. It is a command guardrail. It protects against common accidental destructive operations by parsing bash syntax and classifying commands. It does not defend against a malicious local user, shell aliases/functions, hostile environment variables, or tools that mutate state through unrecognized channels.
Install
From npm:
pi install npm:pi-safety-modesTry without installing:
pi -e npm:pi-safety-modesFrom this repository:
npm install
npm run build
pi -e ./src/index.tsOr install/use it as a Pi package from a local path:
pi install /absolute/path/to/pi-safety-modesModes
off— no enforcement; Pi default behavior.blocklist— normal coding mode. Unlisted operations are allowed; configuredask/denyrules are enforced. This is “do not do stuff on my list.”readonly— state-preserving mode. Allows only Pi built-in read tools (read,grep,find,ls) plus explicitly allowlisted bash operations. Denieswrite,edit,task, MCP tools, opaque shell commands, redirects that write, and unknown tools.
TUI statusline shows safety:off, safety:blocklist, or safety:readonly (red off, yellow blocklist/protected, green readonly).
Shortcut:
Alt+Scyclesblocklist → readonly → off → blocklist.
Use:
/safety-mode
/safety-mode readonly
/safety-mode blocklist
/safety-mode offLegacy aliases still work: read-only, ro, protected, protect, rules, denylist, unrestricted.
Skill
This package also ships a skill so agents can configure it when asked:
/skill:pi-safety-modes set fs.delete to askSkill helps with mode changes, config path, and rule edits. It is guidance only; enforcement lives in extension.
Config
Config lives at:
~/.pi/agent/extensions/pi-safety-modes/config.jsonPI_CODING_AGENT_DIR is respected through Pi's getAgentDir().
Example:
{
"mode": "blocklist",
"readOnlyAllow": ["git.status", "git.diff", "fs.list", "fs.read", "search.grep", "search.find"],
"rules": {
"git.reset.hard": "deny",
"git.clean.force": "deny",
"shell.pipe-to-shell": "deny",
"shell.exec": "ask",
"tool.task": "ask",
"tool.mcp": "ask"
}
}Actions are allow, ask, or deny. In blocklist mode, operation IDs missing from rules are allowed. Invalid config values are ignored with warnings; missing config uses safe defaults.
Operation IDs
Git: git.status, git.diff, git.log, git.show, git.blame, git.grep, git.remote.view, git.branch.list, git.branch.delete, git.tag.list, git.tag.delete, git.push, git.push.force, git.push.delete, git.reset, git.reset.hard, git.clean, git.clean.force, git.rebase, git.cherry-pick, git.merge, git.checkout, git.switch, git.add, git.commit, git.restore, git.stash.
File/search: fs.list, fs.read, fs.write, fs.delete, fs.move, fs.copy, fs.chmod, fs.chown, search.grep, search.find.
Shell: shell.pipe-to-shell, shell.exec, shell.redirect-write, shell.opaque, unknown.
Examples
git statusis allowed inreadonly.echo hi > fileis denied inreadonlyand allowed inblocklistunless configured otherwise.git push --forceis allowed inblocklistunless configured otherwise.rm file,sudo rm file,git rm file,find . -delete, andfind . -exec rm {} +are allowed inblocklistunless configured otherwise; set"fs.delete": "ask"or"deny"to intercept them.git reset --hardis denied inblocklistby default.curl x | shis denied by default.bash -c "...",sh -c "...",eval,source, aliases, and. script.shareshell.execand ask inblocklistby default.- Shell expansions like
$HOMEareshell.opaque; allowed inblocklistby default, denied inreadonly.
Known limitations
This is a guardrail, not isolation. It does not emulate shell execution, expand variables, inspect aliases/functions, or understand every command's side effects. Unknown and plain opaque commands are denied in readonly and allowed in blocklist unless configured otherwise. Dynamic shell execution (shell.exec) asks in blocklist by default.
