npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@inobit/pi-permission

v1.1.1

Published

Lightweight permission enforcement extension for the Pi coding agent: sensitive-file guard, read/write-aware project boundary, dangerous-operation confirmation, and plan/build read-only mode.

Readme

@inobit/pi-permission

English | 中文

Lightweight permission control for Pi coding agent. Decisions are tiered by "effect provability" — only block what needs blocking:

  1. Sensitive file protection: .env, ~/.ssh/*, *.pem, .npmrc, ~/.config/gh/hosts.yml etc. — any read or write (tool or bash) triggers a confirmation prompt.
  2. Trust domain boundary: plan is read-only (out-of-domain writes silently denied); build trust domain = project dir ∪ trusted paths — everything inside is allowed, crossings are gated.
  3. Dangerous operation confirmation: git push / rm -rf, sudo, curl | sh and the danger overlay always require confirmation; unverifiable execution (interpreters/unknown programs) asks in plan and is allowed inside the build domain.
  4. Plan / Build modes: /plan is read-only (out-of-domain writes silently denied), /build returns to normal.

Effect Classification Model (R/W/X)

Commands are classified by "can side effects be fully derived from the arguments" into three tiers, plus a danger overlay:

| Tier | Criterion | Examples | | ---- | --------- | -------- | | R pure reader | No file side effects | cat/grep/jq/git status/sort (no -o) | | W bounded writer | Write targets fully enumerable from arguments | touch/mkdir/cp/mv/sed -i/redirects/find -delete | | X opaque | Effects not derivable — interpreters, build tools, every unrecognized program, parse-failure downgrades | python3/npm/make/bash -c/tar extraction/patch |

The danger overlay (rm -r/-f, chmod/chown -R, git write subcommands, curl|sh, sudo, bash -c, xargs, find -exec) sits above the tiers and keeps the existing product contract.

Features

  • Zero third-party dependencies (besides Pi core packages), purely local deterministic decisions, fail-closed — never silently allows
  • Precise read/write distinction down to path granularity; symlinks and relative paths cannot bypass, relative paths after cd are resolved correctly
  • Sensitive file rules apply uniformly across all tools + bash
  • Trust domain model: plan domain = trusted paths (e.g. /tmp, free scratch reads/writes); build domain = project ∪ trusted — everything inside is allowed including executors; dangerous/sensitive checks always take precedence
  • Zero friction for high-frequency essentials (cat / grep / ls / git status / sleep etc.)

Installation

pi install npm:@inobit/pi-permission

Commands

/plan   enter read-only planning mode (writes denied, status bar shows Plan)
/build  return to normal mode (Build)
/yolo   enter yolo mode (allows everything except sensitive files, requires second confirmation, status bar shows Yolo)
/readonly-tools   manage read-only tools for plan mode (multi-select with Space, session/project/global scopes)
  • Defaults to build mode, session-scoped and non-persistent (resets to build on restart). yolo requires a second confirmation y: confirm yolo with no shortcut.
  • Toggle shortcut: Alt+P cycles between plan/build (remappable via toggleModeShortcut, empty string to disable; key format follows Pi keybindings)
  • Status bar: Plan green / Build red (theme-aware), status key pi-permission-mode

Core Decision Paths

Plan mode (read-only contract)

danger overlay hit (rm -rf / git push / sudo / curl|sh ...)   -> silent deny
enumerable write target outside trust domain T_plan           -> silent deny
(includes project files; plain args of X segments are refs only)
sensitive file involved (read or write)                        -> ask
all segments are R or W (W targets proven inside T_plan)       -> allow
fallback: any X segment (unverifiable effects)                 -> strict ? silent deny : ask

Build mode

danger overlay hit                                             -> ask
sensitive file involved (read or write)                        -> ask
all refs & write targets of every segment inside T_build       -> allow (R/W/X alike)
   (T_build = cwd + trusted paths; python3 /tmp/x.py and npm test both allowed)
pure R (refs anywhere)                                         -> allow
otherwise (W/X with cross-domain refs)                         -> ask

Pre-layer: unparseable syntax / $() / subshell / process substitution -> fail-closed before the tables (build=ask, plan=deny). Yolo mode skips all checks except sensitive files (still denied). Ask dialogs pick s to approve per program/path/parent-dir for the session; hints show once per rule per session.

Threat Model & Residual Risks (must read)

  • Allowing executors inside the build trust domain = accepting the full capability of arbitrary code execution there. Script content can silently cross trust domains (write ~/.ssh, network) — the permission system sees the command surface, not code behavior. This extension is an in-process rule layer, not a sandbox; use containers/disposable environments for real isolation
  • W-tier safety equals target-enumeration correctness; unparseable syntax conservatively downgrades to X, but enumeration bugs themselves become mis-allows
  • TOCTOU/symlink races are mitigated, not eliminated; /tmp is world-writable on multi-user machines
  • Plan mode has no executor exemption mechanism: X segments always ask (silent deny under strict), keeping the read-only contract intact

Configuration

Merged by layer (array fields are union-deduplicated across layers, non-array fields are overridden by higher layers):

| Layer | Location | | -- | --- | | Global | ~/.pi/agent/extensions/pi-permission/config.json | | Project | .pi/extensions/pi-permission/config.json (project must be trusted) | | Session | In-memory (via /readonly-tools with session scope, lost on restart) |

| Field | Description | Default | | -- | --- | --- | | sensitivePatterns | Sensitive file glob list | *.env *.env.* ~/.ssh/* *.pem *.key id_rsa* credentials.json secrets*.yaml ~/.aws/* .npmrc ~/.config/gh/hosts.yml | | envExampleReadAllowed | Allow reading .env.example without prompt | true | | readonlyBashCommands | Bash read allowlist | High-frequency read-only commands (cat/grep/ls/..., 72 entries) | | dangerousBashCommands | Unified dangerous operation list (sudo or git commit) | Git write subcommands + dangerous shell | | readonlyPowerShellCommands | PowerShell read allowlist (canonical cmdlet names, aliases normalized before matching) | Read-only cmdlets (get-childitem/get-content/select-string/...) | | dangerousPowerShellCommands | PowerShell dangerous operation list | start-process / add-type / register-scheduledtask / ... | | trustedExternalPaths | Trusted external path prefixes — reads/writes under these prefixes are auto-allowed (e.g. /tmp for temp files; os.tmpdir() is merged at runtime) | ["/tmp"] | | readonlyTools | Tool read allowlist (union across layers) | read grep find ls | | strictPlanMode | Plan mode: unverifiable execution (X segments) tightened from ask to silent deny | false | | toggleModeShortcut | Plan/build toggle shortcut (empty string to disable) | alt+p | | reviewLog | Review log toggle (FR-6) | true | | debugLog | Debug log toggle (separate from review log, verbose events) | false | | logDir | Log directory (relative to ~/.pi/agent, respects PI_CODING_AGENT_DIR; supports absolute path and ~/, 0600; extension dir holds only config) | logs/pi-permission |

Fixed rules (not configurable): built-in write tools write/edit, rm -r/-f, chmod -R, chown -R, curl/wget | sh/bash, bash -c/eval/sudo/xargs/find -exec are always treated as dangerous; redirect targets >/>> are always checked; git subcommands not in dangerousBashCommands are treated as read-only. Prompt reasons carry a [bash] / [tool:<name>] source prefix and include configuration hints.

PowerShell tool (pi 0.84.3+, Windows, opt-in via defaultTools: [... "powershell"]): same R/W/X pipeline as bash. Aliases are normalized first (gciget-childitem, rmremove-item, catget-content, ...); native exes (git/node/npm) reuse the bash registries. Fixed PowerShell dangers (not configurable): iex/Invoke-Expression, icm/Invoke-Command, Set-ExecutionPolicy, nested pwsh/powershell invocations (incl. -EncodedCommand), call operator &, dot-sourcing, script blocks {...}, Remove-Item -Recurse/-Force, and pipe-to-shell (irm|iex). $() subexpressions, bare grouping, splatting and here-strings are fail-closed. Ambiguous names stay conservative: curl/wget are X (PS 5.1 alias vs PS 7 exe), sc is always treated as the service controller.

Log location: defaults to ~/.pi/agent/logs/pi-permission/<project>/pi-permission-{review,debug}.jsonl (co-located with pi-debug.log), isolated per project, files 0600, dirs 0700, with size-based rotation. The extension directory ~/.pi/agent/extensions/pi-permission holds only config.json. Custom paths support absolute and ~/ forms, e.g. "logDir": "~/my-logs/pi-permission" or "/var/log/pi-permission".

Trusted exemption boundary: only the "directory boundary" is exempted, always after dangerous/sensitive checks — even inside a trusted directory, writes matching a sensitive filename (e.g. /tmp/.env, or writing .env while cwd is /tmp) are still deny in plan and ask in build; dangerous commands (sudo rm -rf /tmp etc.) are path-independent and always intercepted before trusted checks; dual-form realpath guards against symlink escapes.

Ask Dialog

ask (= confirmation required) shows a 4-option selector + optional emacs input. All denies are terminate:false so the model immediately sees the reason and continues; only Esc hard terminate forces true.

| Key | Action | terminate | Audit | | --- | --- | --- | --- | | y | allow once | — | allow-after-ask | | s | allow session (remembered as <session>:<approvalKey>) | — | allow-after-ask + sessionApprovals | | n | deny (default) | false (model continues) | deny | | r | deny with reason → emacs input | false (fully replaces denyFeedback text, model continues) | deny + customReason (truncated, redacted) | | Esc (on select) | hard terminate — deny and stop | true (force) | deny + terminatedByEsc (reason="[pi-permission] Denied by user — stopping.") |

r second layer (ctx.ui.input, inherits tui.input.* emacs keys C-a/e/k/u/f/b): title Deny reason — emacs keys, Enter submit, Esc to go back, placeholder e.g. use .env.example instead. Empty (trim()==="") notifies reason cannot be empty and stays; Esc (input===undefined) returns to the 4-option select; Enter non-empty returns {kind:"reason", customReason} fully replacing the default reason as [pi-permission] User denied: <custom>. No UI (rpc/print) degrades to notify + deny with default.

ask → select [y/s/n/r]
  ├─ y/s → allow (+ s remembered)
  ├─ n   → deny (default, terminate:false → model continues)
  ├─ r   → input (emacs)
  │        ├─ non-empty Enter → deny with reason (fully replaces, terminate:false)
  │        ├─ empty Enter     → stay (notify)
  │        └─ Esc             → back to select
  └─ Esc  → deny + hard terminate (force true)

/readonly-tools Interaction

Space to select/deselect, //j/k to move, Enter to confirm, Esc/q to cancel. Pick the edit target first (each layer edits only itself, other layers are locked):

  • session (in-memory, session-scoped): built-in + global + project-configured tools are locked
  • project (writes to .pi/extensions/pi-permission/config.json): built-in + global-configured are locked, project must be trusted
  • global (writes to global config.json): only built-in tools are locked

Built-in tools (read/grep/find/ls), bash, and write/edit are always locked.

Status Bar Integration

  • Pi built-in statusline: Plan/Build appears in the footer extension status row, no configuration needed.
  • pi-powerline-footer: status value without [ prefix, goes into the extension_statuses aggregated segment; to place it at the leftmost of the main bar:
{
  "powerline": {
    "preset": "default",
    "placement": "below",
    "customItems": [
      { "id": "pi-mode", "statusKey": "pi-permission-mode", "position": "left", "excludeFromExtensionStatuses": true }
    ],
    "layout": { "left": ["custom:pi-mode", "model", "thinking", "shell_mode", "path", "git", "queue", "context_pct", "cache_read", "cost"] }
  }
}

Development

pnpm --filter @inobit/pi-permission check   # tsc --noEmit
pnpm --filter @inobit/pi-permission test    # vitest
pnpm --filter @inobit/pi-permission pack:check
pi -ne -e ./packages/pi-permission

License

MIT