@yaosu/pi-path-guard
Path Guard for pi — blocks destructive commands & path overwrites, protects .env/keys, with strict/normal/loose/trusted/naked guard modes (/guard). pi 防误删/防误覆盖扩展,支持 5 种防护模式。
Package details
Install @yaosu/pi-path-guard from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@yaosu/pi-path-guard- Package
@yaosu/pi-path-guard- Version
1.5.7- Published
- Sep 19, 2026
- Downloads
- 2,909/mo · 451/wk
- Author
- yaosu
- License
- MIT
- Types
- extension
- Size
- 149.6 KB
- Dependencies
- 0 dependencies · 1 peer
Pi manifest JSON
{
"extensions": [
"./extensions"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-path-guard
Path Guard — pi extension: prevents accidental deletes / overwrites / edits Path Guard — pi 扩展:防误删 / 防误覆盖 / 防误改
Intercepts destructive operations in tool calls (bash, write, edit): protected paths (.env, .ssh, keys, credentials, …), system-destructive commands (mkfs/reboot/block-device writes/bulk deletes, …), overwrites/deletes outside the project, and > truncation of existing files — deciding block / confirm / pass per guard mode.
拦截 bash / write / edit 等工具调用中的破坏性操作:受保护路径(.env、.ssh、密钥、凭据等)、系统级破坏命令(mkfs/reboot/写块设备/批量删除等)、项目外覆盖/删除、> 截断已有文件等,按防护模式决定 阻止 / 询问 / 放行。
Feature highlights
- 🛡 5 modes, every rule tunable —
strict/normal(default) /loose/trusted/nakedrun off one built-in matrix of 19 guard rules; switching modes tightens or loosens the whole guard, and the active mode persists across sessions (/guardor/guard <mode>). Inside every mode, each of the 19 rules is individually re-tunable to block / confirm / pass (or reset to its built-in default) via/guard → rules— no code changes; only your overrides are stored. 五种防护模式,规则逐条可调:strict/normal/loose/trusted/naked共用一套内置的 19 条守护规则矩阵;切换模式即整体收紧或放宽防护,当前模式跨会话保留。而每种模式下,19 条规则每一条都能独立调整为 阻止/询问/放行(或恢复内置默认),经/guard → rules即可,无需改代码,仅保存你的覆盖项。 - 📁 Protected & trusted paths —
/guard → pathssplits into two categories: protected paths (/guard paths protected …, the default) are guarded in every mode (incl. naked); trusted paths (/guard paths trusted …) are always allowed — operations on them pass like trusted mode, in any mode. System-important paths can never be trusted (see below). 管理受保护路径与信任路径两类路径。 - 🔒 Three-way verdict — every intercepted operation resolves to block / confirm / pass: block refuses outright, confirm asks you, pass executes. How strict the guard is depends entirely on your rules.
⚠️ Security notice: pi extensions run with full system permissions and can execute arbitrary code. Review the source before installing (this project is open source — see
extensions/path-guard.ts).
Install / 安装
# From git (recommended / 推荐)
pi install git:github.com/yaodashanren/pi-path-guard@v1
# Local directory (development / 本地目录,开发用)
pi install /path/to/pi-path-guard
# Try without installing (no settings change / 临时试用,不写入 settings)
pi -e ./pi-path-guard
# npm: scope package
# pi install npm:@yaosu/pi-path-guard
After installing, run /reload or restart pi. 安装后 /reload 或重启 pi 生效。
Usage / 使用
/guard command
/guard— interactive main menu with three actions: Switch mode (title shows the full decision matrix; choices are bilingual), Customize per-mode guard rules, and Manage custom protected paths. The main menu loops: a sub-menu's back returns to the previous menu, and eventually back to this main menu; only cancelling at the top level (no selection) exits the command. 交互式主菜单,三个动作:切换防护模式(标题展示完整判定矩阵,选项中英双语)、定制每模式守护规则、管理自定义受保护路径。主菜单为循环:子菜单的 back 逐级返回上一级,最终回到本主菜单;只有顶层取消(不选)才退出命令/guard→ rules sub-menu: pick a mode → the rule editor lists all 19 rules with their current levels; pick one → set block / confirm / pass (or reset to the built-in default). Stays in the editor so you can set several rules per mode before choosing back. Also offers a read-only full overview matrix in a scrollable viewer (↑/↓/PgUp/PgDn scroll, q/⏎/esc to close) and reset (clear all overrides).pathGuard.rules.{mode}.{rule}in settings.json./guard rules子菜单:选模式 → 规则编辑器列出全部 19 条规则及其当前级别;选一条 → 设为 block / confirm / pass(或恢复内置默认)。改完停留在编辑器可连续改多条,再选 back。另提供只读 overview 全矩阵(可滚动查看,↑/↓/PgUp/PgDn 滚动,q/⏎/esc 关闭)与 reset(清空全部覆盖),存于 settings.json 的pathGuard.rules.{mode}.{rule}/guard <strict|normal|loose|trusted|naked>— quick switch (trusted requires a warning; naked requires a double warning) 快捷切换(trusted 需警告确认;naked 需两级确认)- Invalid argument → falls back to the interactive main menu 非法参数 → 兜底弹出交互主菜单
- The active mode persists across sessions via global settings.json (
pathGuard.modein~/.pi/agent/settings.json), falling back tonormal./guard <mode>writes it back there. Persistence is intentionally global-only, never project-scoped: writing a project.pi/settings.jsonwould make the project "trust-requiring", so pi would start asking for trust on the next launch (defaultProjectTrust=ask) and a declined/untrusted launch would silently ignore the saved mode — the old behavior that made a saved mode revert to normal. Global settings are never trust-gated, so the mode always survives. 模式跨会话持久化到全局 settings.json(~/.pi/agent/settings.json的pathGuard.mode),缺省回normal;/guard <mode>切换时回写到该文件。持久化刻意只写全局、不写项目:写入项目.pi/settings.json会让项目变为需信任项目,导致下次启动 pi 弹出信任询问,若项目被拒/未信任则保存的模式会被静默忽略(这正是旧版模式重置为 normal 的根因)。全局设置不受信任判定门控,模式必然保留 /guard paths protected add|rm|list|clear <path>(alias: omitprotected) — manage custom protected paths, enforced in EVERY mode (incl. naked);/guard paths trusted add|rm|list|clear <path>— manage trusted paths (always allowed, trusted-mode protection; adding one requires a warning confirm; protected/system paths are refused) 管理自定义受保护/信任路径- The active mode is shown in the footer status bar (
🛡 <mode>,🛡 NAKEDin warning color) 当前模式显示在底部状态栏(🛡 <mode>,naked 用警示色🛡 NAKED)
Protected & trusted paths / 受保护路径与信任路径
/guard → paths (or /guard paths) presents two categories:
/guard → paths(或 /guard paths)提供两个分类:
# Protected paths (default category) — guarded in EVERY mode incl. naked 受保护路径
/guard paths protected list | add <path> | rm <path> | clear
/guard paths list | add <path> | rm <path> | clear # same, category defaults to protected
# Trusted paths — always allowed (trusted-mode protection) 信任路径——始终放行
/guard paths trusted list | add <path> | rm <path> | clear
Protected paths are checked against the resolved real path and protected in every mode — even naked (built-in protected paths are NOT enforced in naked; only yours are). They are also settable statically:
受保护路径按解析后的真实路径匹配,且在任何模式下都被守护——包括 naked(内置受保护路径在 naked 下不生效,但你自定义的始终生效)。也可在 settings.json 静态配置:
"pathGuard": { "extraProtected": ["~/secrets", "/path/to/important.txt"] }
Trusted paths are the inverse: path-guard treats every operation whose target lies inside one as if the active mode were trusted for just that path — writes/edits/deletes/overwrites/truncates/in-place edits there pass without any prompt, in every mode (even strict). Protection always outranks trust:
信任路径则相反:落在信任路径内的所有操作都被视为“对该路径采用 trusted 模式的保护”——写入/编辑/删除/覆盖/截断/就地修改一律不弹窗、直接放行,在任何模式下都生效(包括 strict)。但保护始终优先于信任:
- A trusted path can never be a protected path — adding
.env/.ssh/keys/node_modules/… (built-in system paths) or an existing user-protected path is refused. 信任路径绝不可是受保护路径——添加.env/.ssh/密钥/node_modules等内置系统路径或已被你设为受保护的路径会被拒绝。 - A protected file inside a trusted subtree is still blocked (e.g. a
.envunder a trusted dir stays protected). 即便信任目录里出现受保护文件也仍会被拦截(如信任目录下的.env依旧受保护)。 - Adding a trusted path requires an interactive warning confirm (refused without a UI). 添加信任路径需要警告确认(无 UI 下拒绝)。
Also settable statically: 也可在 settings.json 静态配置:
"pathGuard": {
"extraProtected": ["~/secrets"],
"trustedPaths": ["/path/to/scratch-or-build-dir"]
}
Tunable rules / 可调规则
Each mode's judgement is a set of 19 rules; override any per mode in settings.json (rule = "block" | "confirm" | "pass"; invalid values are ignored):
每个模式的判定由 19 条规则组成;可在 settings.json 里按模式覆盖(rule 取 "block"|"confirm"|"pass",非法值忽略):
"pathGuard": {
"mode": "normal",
"rules": {
"normal": { "deleteOutside": "confirm", "confirmGroup": "pass" },
"naked": { "blockGroup": "block" }
}
}
Rule IDs: blockGroup, confirmGroup, writeOutside, writeHome, writeInProject, deleteOutside, deleteInProject, overwriteOutsideExisting, overwriteOutsideNew, overwriteInProject, truncateInProject, truncateOutside, gitDestructive, pipeToShellInProject, pipeToShellOutside, runScriptInProject, runScriptOutside, runScriptProtected, scriptUnresolved. Defaults reproduce the matrix above exactly.
Guard mode matrix / 防护模式矩阵
| Checkpoint / 判定点 | strict | normal | loose | trusted | naked |
|---|---|---|---|---|---|
| Protected paths (.env/.ssh/keys/credentials) / 受保护路径 | block | block | block | block | pass |
| Block group (mkfs/reboot/block-device writes/bulk delete) / Block 组危险命令 | block | block | block | block | confirm |
| Confirm group (sudo/ssh/chmod 777 …) / Confirm 组 | block | confirm | confirm | confirm | pass |
| git destructive (reset --hard/clean -f …) / git 破坏性 | confirm | confirm | confirm | confirm | pass |
| In-project write/edit/new / 项目内写/改/新建 | confirm | pass | pass | pass | pass |
| In-project delete / 项目内删除 | confirm | confirm | pass | pass | pass |
| Outside write (new file) / 项目外写新文件 | confirm | confirm | pass | pass | pass |
| Outside overwrite existing / 项目外覆盖已存在 | block | block | confirm | pass | pass |
| Outside delete ordinary / 项目外删除普通文件 | block | block | confirm | pass | pass |
> truncate existing in-project / 截断项目内已有文件 |
confirm | confirm | pass | pass | pass |
> truncate existing outside / 截断项目外已有文件 |
block | confirm | confirm | pass | pass |
| Pipe to shell (in-workspace, curl…|bash) / 管道到 shell(项目内) | confirm | pass | pass | pass | pass |
| Pipe to shell (remote/outside, curl…|bash) / 管道到 shell(远程/项目外) | confirm | confirm | pass | pass | pass |
| Run script in-project (source/. / bash x.sh) / 运行脚本·项目内 | confirm | confirm | pass | pass | pass |
| Run script outside/HOME / 运行脚本·项目外/HOME | block | confirm | confirm | pass | pass |
| Run script of built-in protected path / 运行脚本·内置保护 | block | confirm | confirm | pass | pass |
Run script with an unresolvable $VAR/glob target / 运行脚本·路径不可解析 |
block | confirm | confirm | pass | pass |
| cwd=HOME write / HOME 目录写 | confirm | confirm | pass | pass | pass |
| No UI (headless) / 无交互界面 | block* | block* | block* | block* | pass |
*block = denied directly, no confirmation opportunity / 直接阻止,无确认机会;confirm = prompt / 弹窗询问;pass = allow / 放行;*headless: items that would be confirmed are blocked instead / 无 UI 时需确认项一律阻止
⚠️ naked mode / 裸奔模式: passes almost everything — protected paths, the write/edit tool checks, git destructive, truncation, outside deletes/overwrites all pass even with no UI. Only system-destructive commands (mkfs/reboot/bulk-delete/block-device writes) are still confirmed. Switching requires a double confirmation (two prompts). Use only when you want minimal path-guard interference. ⚠️ 裸奔模式:除系统级破坏命令外几乎全部放行——受保护路径、write/edit 工具检查、git 破坏性、截断、外部删除/覆盖均放行,无 UI 下也放行;但系统级破坏命令(mkfs/reboot/批量删除/写块设备)仍会弹窗询问。切换需要两级确认(两次弹窗)。仅当你需要最少的路径守护干扰时使用。
Core capabilities / 核心能力
Protected-path interception / 受保护路径拦截:
.env/.envrc/.ssh/.secrets/.aws/.kube/ private keys (*.pem/*.key/*.p12/*.pfx,id_rsa/id_ed25519) / credentials / shell configs (.bashrc…) /node_modules/dist/build… blocked hard in every mode (except naked) — 任何模式下硬性阻止(naked 除外)Block group / Block 组危险命令:
mkfs.*/mkswap/poweroff/reboot/shutdown/ddto block devices /> /dev/sdX/find -delete/find -exec rm/xargs rmConfirm group / Confirm 组:
sudo/doas/pkexec/chmod 777/ssh/scp/sftp/rsh/telnet/wget -O /dev/nullOverwrite detection / 覆盖检测:
mv/cp/install/tee/ln -f/rsync --deleteon existing targets, classified by in/out project; rsync/scp remote targets (user@host:/path,host:/path) are never resolved as local paths — 目标已存在时按内外策略处理;rsync/scp 远程目标不会被当成本地路径解析Redirect truncation / 重定向截断:
> existing file(incl.2>/&>/>|, excluding>>and devices) → confirm —> existing file截断已有文件需确认(含>|)Redirect / download / dd target location / 重定向·下载·dd 目标位置: a redirect (
>,>>…),dd of=,curl -o|-Oorwget -Otarget that lies outside the project (or a HOME write) is judged like an overwrite (writeOutside/writeHome/overwriteOutsideExisting/overwriteOutsideNew); devices (/dev/null…) and trusted paths are exempt — 重定向 /dd of=/curl -o/wget -O的目标在项目外(或 HOME 下写入)时按覆盖规则判定;设备与信任路径放行Shell wrapper recursion / shell 包装器递归: strips
sudo/nohup/timeout/env… prefixes, recurses intobash -c/eval; quote-aware tokenization — 前缀剥除后分析真实命令;引号感知分词git destructive commands / git 破坏性命令:
clean -f/reset --hard/checkout -- ./checkout|switch -f|--force/restore ./worktree remove --force/tag -d/branch -D/push --force/stash drop(a narrowrestore --source=<ref> -- <path>is not treated as destructive) —restore --source带路径的常规用法不再弹窗Dangerous pipe-to-shell / 危险管道到 shell:
curl … \| bash/wget -qO- … \| sh/python -c '…' \| sh— strict confirms at all positions; normal passes in-workspace and confirms remote/outside sources; other modes pass (perpipeToShell*rules) — 判定curl/wget等下载或解释器内联代码的输出被管道进 shell 执行Run-script guard / 运行脚本守护:
source file/. file/bash|sh|zsh|dash|ksh [flags] scriptare judged by target path instead of a blanket confirm — in-project (runScriptInProject), outside/HOME (runScriptOutside), built-in protected (runScriptProtected), each tunable per mode; user-protected targets stay hard-blocked in every mode (incl naked) and trusted targets always pass — a$VAR/glob target that cannot be resolved follows the newscriptUnresolvedrule (strict block / normal·loose confirm / trusted·naked pass), but its literal tail is still inspected first: a user-protected tail stays hard-blocked in every mode, a built-in protected tail ($D/id_rsa,$D/.ssh/config) usesrunScriptProtected, and a bare$VARwith nothing literal to inspect stays a conservative confirm even in trusted — 按目标路径判定并可按模式调整:项目内 / 项目外 / 内置保护各一条规则;用户自定义保护路径硬拦、信任路径放行。无法静态解析的$VAR/通配目标走新规则scriptUnresolved(strict 阻止 / normal·loose 询问 / trusted·naked 放行),但会先检查字面尾部:命中用户自定义保护路径仍全模式硬拦,命中内置保护(如$D/id_rsa、$D/.ssh/config)走runScriptProtected,而无任何字面信息的裸$VAR即使在 trusted 下也保持确认Bypass resistance / 防绕过: variable/wildcard paths that can't be statically resolved always confirm (script targets excepted — see the run-script guard: they follow
scriptUnresolvedand are exempt only in trusted/naked); command substitutions ($(...)/ backticks) are recursively judged; any hard block in a compound command blocks the whole thing — 变量/通配符路径一律 confirm;命令替换($(...)/ 反引号)递归判定;复合命令任一段硬性阻止则整体阻止Block escape hints / 拦截提示: every block message appends a short, category-aware "To run anyway / 如需执行:" hint — an English hint followed by the Chinese note on its own indented line — user-configured protected paths suggest
/guard paths rm, built-in protected paths & system-destructive commands point to/guard naked, rule-level blocks suggest/guard looseor/guard rules— 每次拦截都会附一条按类别给出的解除建议:英文提示一行、中文注释另起一行缩进(头部To run anyway / 如需执行:)Session pass from the confirm dialog / 弹窗内会话级放行: every confirm prompt offers a third option
🔓 Allow & set <rule> = pass (session)(multiple rules →N rules), so a repeated prompt can be answered in place without opening/guard rules; the pass is in-memory only (never persisted, cleared on a new session) and is not offered forconfirmGroup(sudo/ssh/chmod 777), system-destructive commands, or in naked mode — 确认弹窗提供会话级放行第三选项,仅内存生效(不落盘、新会话清除);confirmGroup、系统级破坏命令与 naked 模式不提供
Known limitations / 已知边界
- Static heuristics, not a sandbox / 静态启发式,不是沙箱: the guard inspects the
bashcommand text and thewrite/edittarget paths; it is meant to catch accidental destructive operations, not to defeat a determined adversary or prompt injection. Run untrusted code in a real sandbox / VM / container under a least-privilege account — 本扩展只检查bash命令文本与write/edit目标路径,用于拦截误操作,并非对抗恶意输入或 prompt injection 的完整沙箱;不可信代码请放到真正的沙箱/容器/虚拟机里运行。 - POSIX-oriented / 面向 POSIX: path handling assumes POSIX separators and
/dev-style device names; Windows is not a supported target — 路径判定基于 POSIX 分隔符与/dev设备名,不支持 Windows。 - What is not statically expanded / 不做静态展开: variable/glob targets (
$F,*.log) cannot be resolved, so they trigger a conservative confirm instead of being followed — except the script-file target ofsource/./<interp> script, which is additionally judged by its literal tail (scriptUnresolvedrule; a bare$VARwith no literal tail still confirms);~useris not expanded either but is anchored outside the project (never mistaken for an in-project path); aliases, shell functions,evalof dynamically built strings, andbash -c/$(...)nesting beyond depth 4 are not resolved (too deep → confirm) — 变量/通配符目标($F、*.log)无法静态解析,一律保守 confirm而不展开;~user同样不展开,但按项目外处理(不会被误当成项目内路径);别名、shell 函数、动态拼接后eval、以及超过 4 层的bash -c/$()嵌套不会被解析(过深 → confirm)。 - Conservative by design / 宁可误报: unresolvable targets confirm; in a no-UI environment, confirm-grade operations are blocked instead of prompted — expect the occasional false positive and tune it with
/guard rulesor trusted paths — 无法解析的目标会 confirm;无 UI 时需确认项直接阻止;可能出现误报,可用/guard rules或信任路径调整。 - Scope / 范围: only the
bashtool and thewrite/edittools are guarded; network access, MCP servers, other tools and extensions are out of scope — 仅守护bash工具与write/edit工具;网络、MCP、其它工具与扩展不在范围内。
Development / 开发与测试
Automated tests (275 assertions) load the real extension with a mocked pi API, covering the 5 modes × protected paths / dangerous commands / truncation / git destructive / dangerous pipe-to-shell matrix, plus /guard command interaction, trusted-mode confirmation, naked-mode double confirmation, the footer status indicator, settings.json mode persistence, custom protected paths (incl. naked), trusted paths (always-allowed, incl. protected-path refusal and strict-mode pass), run-script judging (source/./bash × in/out/protected/trusted), redirect/download/dd outside+variable targets, command-substitution recursion, and per-mode rule overrides:
cd tests && node --experimental-strip-types test-pathguard.ts
自动化测试(275 断言)模拟 pi API 加载真实扩展,覆盖 5 种模式 × 受保护路径 / 危险命令 / 截断 / git 破坏性 / 危险管道到 shell 等判定矩阵,以及 /guard 命令交互、trusted 确认与 naked 两级确认、底部状态栏指示、settings.json 模式持久化、自定义受保护路径(含 naked)、信任路径(始终放行,含受保护路径拒绝与 strict 下放行)、运行脚本判定(source/./bash × 项目内/外/受保护/信任)、重定向/下载/dd 的项目外与变量目标判定、命令替换递归、按模式规则覆盖等流程:
cd tests && node --experimental-strip-types test-pathguard.ts
Changelog
See CHANGELOG.md for the full version history (aligned with package.json); the latest release is v1.5.7.
License
MIT © yaosu