{"_id":"@coderdkai/pi-permission-system","name":"@coderdkai/pi-permission-system","dist-tags":{"latest":"26.3.0"},"versions":{"26.3.0":{"name":"@coderdkai/pi-permission-system","version":"26.3.0","description":"Permission enforcement extension for the Pi coding agent.","type":"module","exports":{".":{"types":"./dist/public.d.ts","default":"./src/service.ts"}},"imports":{"#src/*":"./src/*","#test/*":"./test/*"},"scripts":{"check":"tsc --noEmit","build:types":"rollup -c rollup.dts.config.mjs","prepack":"pnpm run build:types","gen:schema":"node --experimental-strip-types scripts/generate-permissions-schema.ts && biome format --write schemas/permissions.schema.json","test":"vitest run","test:watch":"vitest","verify:public-types":"bash scripts/verify-public-types.sh","lint:md":"rumdl check *.md docs/**/*.md","lint":"biome check . && eslint . && pnpm run lint:md"},"keywords":["pi-package","pi","pi-extension","pi-coding-agent","coding-agent","permissions","policy","access-control","authorization","security"],"author":{"name":"Chris Lasher"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/coderdkai/pi-packages.git","directory":"packages/pi-permission-system"},"homepage":"https://github.com/gotgenes/pi-packages/tree/main/packages/pi-permission-system#readme","bugs":{"url":"https://github.com/gotgenes/pi-packages/issues"},"engines":{"node":">=22"},"publishConfig":{"access":"public"},"pi":{"extensions":["./src/index.ts"]},"peerDependencies":{"@earendil-works/pi-coding-agent":">=0.79.0","@earendil-works/pi-tui":">=0.79.0"},"devDependencies":{"@biomejs/biome":"catalog:","@earendil-works/pi-coding-agent":"0.79.1","@earendil-works/pi-tui":"0.79.1","@types/node":"catalog:","rollup":"catalog:","rollup-plugin-dts":"catalog:","rumdl":"catalog:","typescript":"catalog:","vitest":"catalog:"},"dependencies":{"tree-sitter-bash":"^0.25.1","web-tree-sitter":"^0.26.9","zod":"^4.4.3"},"gitHead":"eb5ea1df272fe4c30784a79c445843c218e0e199","_id":"@coderdkai/pi-permission-system@26.3.0","_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-P3qsVpa/lVQ14gBz7sTKsyQiDePTi3XEiivsa0klB4uX+cx06kKM/YGCpDOqyYi0sMaKMgzYMUJzdADyv/Sj9g==","shasum":"b6c4f6c0b4d2c2024d0c37f4821978be2cf0c03f","tarball":"https://registry.npmjs.org/@coderdkai/pi-permission-system/-/pi-permission-system-26.3.0.tgz","fileCount":167,"unpackedSize":1335686,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDr/dl8DkWVWEWZ40khdRh2z8t5A2On1Fxf5jm2/AXEUAIgRtwIZ+P+9nGPMCRYnMmkFkHNAZF0wPZjqzzEZ64bi/4="}]},"_npmUser":{"name":"coderdkai","email":"coderdkai@gmail.com"},"directories":{},"maintainers":[{"name":"coderdkai","email":"coderdkai@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pi-permission-system_26.3.0_1787131408662_0.7178202267979159"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-19T09:23:28.368Z","26.3.0":"2026-08-19T09:23:28.809Z","modified":"2026-08-19T09:23:29.096Z"},"maintainers":[{"name":"coderdkai","email":"coderdkai@gmail.com"}],"description":"Permission enforcement extension for the Pi coding agent.","homepage":"https://github.com/gotgenes/pi-packages/tree/main/packages/pi-permission-system#readme","keywords":["pi-package","pi","pi-extension","pi-coding-agent","coding-agent","permissions","policy","access-control","authorization","security"],"repository":{"type":"git","url":"git+https://github.com/coderdkai/pi-packages.git","directory":"packages/pi-permission-system"},"author":{"name":"Chris Lasher"},"bugs":{"url":"https://github.com/gotgenes/pi-packages/issues"},"license":"MIT","readme":"<p align=\"center\">\n  <img src=\"docs/assets/logo.png\" alt=\"pi-permission-system logo\">\n</p>\n\n# @gotgenes/pi-permission-system\n\n[![npm version](https://img.shields.io/npm/v/@gotgenes/pi-permission-system?style=flat&logo=npm&logoColor=white)](https://www.npmjs.com/package/@gotgenes/pi-permission-system) [![CI](https://img.shields.io/github/actions/workflow/status/gotgenes/pi-packages/ci.yml?style=flat&logo=github&label=CI)](https://github.com/gotgenes/pi-packages/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/TypeScript-6.x-3178C6?style=flat&logo=typescript&logoColor=white)](https://www.typescriptlang.org/) [![pnpm](https://img.shields.io/badge/pnpm-%3E%3D11-F69220?style=flat&logo=pnpm&logoColor=white)](https://pnpm.io/) [![Pi Package](https://img.shields.io/badge/Pi-Package-6366F1?style=flat)](https://pi.mariozechner.at/)\n\nPermission enforcement extension for the [Pi](https://pi.mariozechner.at/) coding agent that provides centralized, deterministic permission gates over tool, bash, MCP, skill, and special operations.\n\n> **Fork notice:** This package is a full fork of [MasuRii/pi-permission-system](https://github.com/MasuRii/pi-permission-system), published to npm as `@gotgenes/pi-permission-system`.\n> It has diverged substantially from upstream in config format, internal architecture, and permission model.\n\n## What It Does\n\n- **Hides disallowed tools** before the agent starts — no wasted turns probing for blocked tools\n- **Enforces allow / ask / deny** at tool-call time with UI confirmation dialogs\n- **Controls bash commands** with wildcard pattern matching (`git *: ask`, `rm -rf *: deny`)\n- **Gates MCP and skill access** at server, tool, and skill-name granularity\n- **Protects sensitive file patterns** — cross-cutting `path` rules deny `.env`, `~/.ssh/*`, etc. across all tools and bash at once, matching both the path as referenced and its symlink-resolved form so a deny cannot be evaded through a symlink alias\n- **Guards external paths** — prompts before file tools or bash commands reach outside `cwd`\n- **Fails closed** — an internal gate error blocks the tool (with a `gate_error` review-log entry and a matching `permissions:decision` broadcast), and an unparseable bash command — or an indirection wrapper that hides the gated command (`bash -c`/`eval`, `sudo`, `env`, `xargs`, `find -exec`, …) — prompts (`ask`) rather than passing silently\n- **Forwards prompts from subagents** — `ask` policies work even in non-UI execution contexts\n- **Broadcasts UI prompt events** — `permissions:ui_prompt` fires only when the permission system is about to invoke the active user-facing permission UI, and every prompt it announces — including one forwarded up from a subagent — is answered by a `permissions:decision` on the same bus\n- **Native [`@gotgenes/pi-subagents`](https://github.com/gotgenes/pi-subagents) integration** — in-process child sessions register with the permission system automatically, enabling per-agent policy enforcement and `ask`-state forwarding to the parent UI without configuration\n\n## Install\n\n```bash\npi install npm:@gotgenes/pi-permission-system\n```\n\n## Quick Start\n\n1. Create the global config file at `~/.pi/agent/extensions/pi-permission-system/config.json`:\n\n    ```jsonc\n    {\n      \"permission\": {\n        \"*\": \"allow\",\n        \"path\": {\n          \"*\": \"allow\",\n          \"*.env\": \"deny\",\n          \"*.env.*\": \"deny\",\n          \"*.env.example\": \"allow\"\n        },\n        \"bash\": {\n          \"*\": \"ask\",\n          \"rm -rf *\": \"deny\",\n          \"sudo *\": \"ask\"\n        },\n        \"external_directory\": \"ask\"\n      }\n    }\n    ```\n\n2. Start Pi — the extension automatically loads and enforces your policy.\n\nAll permissions use one of three states:\n\n| State   | Behavior                                 |\n| ------- | ---------------------------------------- |\n| `allow` | Permits the action silently              |\n| `deny`  | Blocks the action with an error message  |\n| `ask`   | Prompts the user for confirmation via UI |\n\nWhen the dialog prompts, you can approve once or approve a pattern for the rest of the session.\nIn an interactive TUI session the prompt is an inline keybind dialog — `y` approve, `s` approve for this session, `n` deny, `r` deny with a reason — where each hotkey arms and a second press confirms (configurable via `doublePressToConfirm`).\nThe prompt shows one fact per line — who is asking, the tool, the matched rule, the value being decided — within a row budget, so a large tool input cannot take over the transcript; `Ctrl+O` (`app.tools.expand`) expands it to the complete request.\nSee [docs/configuration.md](docs/configuration.md#inline-permission-dialog-tui) for the hotkeys and [docs/session-approvals.md](docs/session-approvals.md) for session-scoped rules and pattern suggestions.\n\nThe `path` surface is a cross-cutting gate that applies to **all** file access — Pi tools, bash commands, MCP calls, and extension tools alike.\nExtension and MCP tools that operate on paths (via `input.path`, MCP's `input.arguments.path`, or a registered access extractor) are gated by default, so a `path` deny cannot be overridden by a per-tool allow — making it the right place to protect sensitive files like `.env` or `~/.ssh/*` from every tool at once.\nA `path` pattern matches both the path as the agent references it and its canonical (symlink-resolved) form, so a deny still fires when a symlink aliases a sensitive target.\n\nFor per-tool path patterns (`read`, `write`, `edit`, `find`, `grep`, `ls`), patterns are matched against the file path from `input.path`.\nThis lets you express rules like \"allow reads but deny `.env` files\" at the individual tool level.\nLike the cross-cutting `path` surface, per-tool patterns match both the referenced path and its canonical (symlink-resolved) form, so a per-tool deny resists symlink-alias evasion.\nWhen Pi's current working directory is known, relative path inputs also match their cwd-normalized absolute form, so `src/App.jsx` can match both `src/*` and `/workspace/project/*`.\n\nThe `external_directory` surface is the CWD-boundary gate: it decides whether reaching **outside** the working tree is allowed, and accepts a pattern map so you can allow specific outside-CWD directories without opening up all external access.\nThis is the right surface for silencing repeated prompts on a local cache like `~/.cargo/registry` — allow it here, not on `path`:\n\n```jsonc\n{\n  \"permission\": {\n    \"external_directory\": {\n      \"*\": \"ask\",\n      \"~/.cargo/registry/*\": \"allow\"\n    }\n  }\n}\n```\n\nThe trailing `*` is greedy and crosses subdirectory boundaries, so it allows every file beneath the directory; a bare `~/.cargo/registry` matches only the directory entry itself.\n\nFour layers compose with most-restrictive-wins: `path` (cross-cutting) → `external_directory` (CWD boundary) → per-tool patterns → `bash` command patterns.\nBecause `ask` is more restrictive than `allow`, a `path` allow cannot loosen an `external_directory: ask` boundary — allow outside-CWD directories on `external_directory`.\nSee [docs/configuration.md](docs/configuration.md) for the full recipe.\n\n## Configuration\n\nConfig lives in one JSON file per scope:\n\n| Scope   | Path                                                      |\n| ------- | --------------------------------------------------------- |\n| Global  | `~/.pi/agent/extensions/pi-permission-system/config.json` |\n| Project | `<cwd>/.pi/extensions/pi-permission-system/config.json`   |\n\nProject overrides global; per-agent YAML frontmatter overrides both.\nProject config (policy and runtime knobs) is loaded only once the project is trusted — in an untrusted directory only global config applies, so an untrusted repository cannot loosen your global policy (see [Upgrading](#2200--project-config-requires-project-trust)).\n\nWithin a surface map like `bash` or `mcp`, **last matching rule wins** — put broad catch-alls first and specific overrides after.\n\nThe optional `shellTools` field records which non-`bash` tools carry shell semantics (e.g. an `exec_command` tool that replaces native `bash`), so they are gated at full parity with native `bash` — see [docs/configuration.md](docs/configuration.md#shelltools--gating-aliased-shell-tools).\n\nThe optional `authorizerChain` field names registered case-by-case decision links (e.g. a light model judge) to consult when a request lands on `ask`, ahead of the interactive prompt.\nA downstream extension registers a link via `getPermissionsService().registerAuthorizer(name, authorize)`; it decides nothing until you name it here (opt-in), config order fixes the chain order, and the chain owner caps any link's `allow` on `external_directory`/`path` to keep it within your policy — see [docs/configuration.md](docs/configuration.md#authorizer-chain--case-by-case-decision-links).\nA subagent's ask is reviewed by the chain of the session serving it, one hop up, rather than inside the subagent — see the same section.\n[`@gotgenes/pi-permission-model-judge`](https://github.com/gotgenes/pi-packages/tree/main/packages/pi-permission-model-judge) is a first-party reference implementation of such a link — a deny-first reviewer that auto-denies mistyped out-of-directory paths.\n\nFor the full reference — all surfaces, runtime knobs, per-agent overrides, merge semantics, and common recipes — see [docs/configuration.md](docs/configuration.md).\n\n## Upgrading\n\n### 22.0.0 — project config requires project trust\n\nProject-scoped configuration (the project `config.json` and project-agent frontmatter — both permission policy and runtime knobs such as `yoloMode`) is now loaded only when Pi reports the project as trusted.\nIn an untrusted directory, only global config applies; a skip is surfaced with a warning and a `project_trust.skipped` review-log entry.\nGrant project trust (or set `defaultProjectTrust`) to load a project's config.\nSee [docs/migration/0644-project-trust-gating.md](docs/migration/0644-project-trust-gating.md).\n\n### 16.0.0 — the bash gate now fails closed\n\nThe permission gate fails closed: an internal gate error blocks the tool (with a `gate_error` review-log entry) instead of running it ungated, and a non-empty bash command that cannot be parsed resolves to `ask` (sentinel `<unparseable-bash-command>`) rather than falling through to a permissive top-level `*`.\nCommands that previously slipped through silently on the error or empty-parse path now block or prompt.\n\nIf you relied on the old permissive behavior for bash, set an explicit permissive bash policy — `\"bash\": { \"*\": \"allow\" }` — which also suppresses the new startup warning emitted when a top-level `\"*\": \"allow\"` leaves bash ungated.\n\n## Documentation\n\n| Document                                                                                                                       | Contents                                                                                                             |\n| ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |\n| [docs/configuration.md](docs/configuration.md)                                                                                 | Full policy reference, runtime knobs, per-agent overrides, recipes                                                   |\n| [docs/session-approvals.md](docs/session-approvals.md)                                                                         | Session-scoped rules, pattern suggestions, bash arity table                                                          |\n| [docs/cross-extension-api.md](docs/cross-extension-api.md)                                                                     | Cross-extension service accessor, event bus integration, prompt and decision broadcasts                              |\n| [docs/subagent-integration.md](docs/subagent-integration.md)                                                                   | Permission forwarding, coexistence with subagent extensions                                                          |\n| [docs/guides/permission-frontmatter-for-subagent-extensions.md](docs/guides/permission-frontmatter-for-subagent-extensions.md) | Convention guide for subagent extension authors                                                                      |\n| [docs/opencode-compatibility.md](docs/opencode-compatibility.md)                                                               | OpenCode compatibility — shared concepts, divergences, porting guide                                                 |\n| [docs/troubleshooting.md](docs/troubleshooting.md)                                                                             | Common issues, diagnostic logging, threat model                                                                      |\n| [docs/migration/legacy-to-flat.md](docs/migration/legacy-to-flat.md)                                                           | Migration from pre-v2 config layout                                                                                  |\n| [docs/migration/strict-config-validation.md](docs/migration/strict-config-validation.md)                                       | Strict config validation (breaking) — rejected configs, and the cross-scope fail-closed clamp                        |\n| [docs/migration/0644-project-trust-gating.md](docs/migration/0644-project-trust-gating.md)                                     | Project-trust gating (breaking) — project config loads only after project trust                                      |\n| [docs/migration/0745-prompt-payload-contracts.md](docs/migration/0745-prompt-payload-contracts.md)                             | Prompt payload contracts (breaking) — the forwarded wire, the `ui_prompt` broadcast, and the deprecated preview caps |\n| [docs/migration/0746-review-log-fields.md](docs/migration/0746-review-log-fields.md)                                           | Review-log fields (breaking) — `message` replaced by request facts, and the `reviewLogFieldMaxWidth` bound           |\n\n## Development\n\n```bash\npnpm run check       # Type-check TypeScript (no emit)\npnpm run lint        # Biome + ESLint + lint:md\npnpm run lint:md     # rumdl on README and docs\npnpm run test        # Run tests from ./test\npnpm run test:watch  # Run tests in watch mode\n```\n\n### Pre-commit hooks\n\nThis project uses [prek](https://prek.j178.dev/) to run Biome, ESLint, and rumdl on staged files before each commit.\nRun `pnpm install` to set up hooks automatically.\n\n## Acknowledgments\n\nThis project began as a fork of [MasuRii/pi-permission-system](https://github.com/MasuRii/pi-permission-system).\nThank you to [MasuRii](https://github.com/MasuRii) for the original work that made this possible.\n\nThank you to the [OpenCode](https://opencode.ai) team for the permission model design that inspired the flat config format and evaluation semantics used in this extension.\n\n## License\n\n[MIT](LICENSE)\n","readmeFilename":"README.md","_rev":"1-82112ba887664476c8081d6acd9e35c4"}