# OpenCode Compatibility This extension's flat permission format and evaluation semantics were directly inspired by [OpenCode's permission model](https://opencode.ai/docs/permissions/) (v1.1.x permission rework). If you are familiar with OpenCode's permission system, most concepts transfer directly — the same mental model applies. > **Point-in-time reference.** > This comparison reflects OpenCode as of May 2026. > See the [official OpenCode permissions docs](https://opencode.ai/docs/permissions/) for the latest upstream behavior. ## What Transfers Directly The following concepts are shared between OpenCode and this extension: | Concept | Description | | ----------------------------- | --------------------------------------------------------------------------------------------------------------- | | Three actions | `allow` / `ask` / `deny` — identical semantics | | Flat `permission` object | Top-level key in config; surface names as keys | | `"*"` universal fallback | Sets the default action when no surface-specific rule matches | | Granular object syntax | Surface key → string (catch-all) or `{ pattern: action }` map | | Last-match-wins | When multiple patterns match, the last one in config order wins | | `*` wildcard | Matches zero or more of any character (including path separators) | | `?` wildcard | Matches exactly one character | | Home directory expansion | `~/`, `$HOME/`, and `${HOME}/` expand to the OS home directory in patterns | | `external_directory` surface | Gates access to paths outside the working directory | | `bash` surface | Command patterns matched against shell commands | | `skill` surface | Skill name patterns matched against skill invocations | | `task` surface | Gates subagent/delegation tool calls | | Session-scoped approvals | `once` / `always` / `reject` from the ask dialog; `always` adds a session rule | | Per-agent overrides | Override global permissions for specific agents | | Tool hiding | Denied tools are removed before the agent starts (no wasted turns probing) | | Bash path extraction | Tree-sitter AST parsing to detect external paths in shell commands (see [details below](#bash-path-extraction)) | | Bash arity table | Generates smart approval pattern suggestions (e.g., `git checkout *` not `git *`) | | Trailing wildcard optionality | `"ls *"` matches bare `"ls"` — the trailing `*` is optional | If your OpenCode config uses these features, the equivalent works in this extension with minimal translation (see [Porting Guide](#porting-an-opencode-config) below). ## Where They Diverge ### Summary Table | Area | OpenCode | This extension | | -------------------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | Default fallback | `"*": "allow"` (permissive) | `"*": "ask"` (least privilege) | | `.env` file protection | Built-in `read` rules deny/ask `.env` files | No built-in rules; user configures with the cross-cutting `path` surface or per-tool path patterns (see [porting guide](#porting-an-opencode-config)) | | Cross-cutting `path` gate | No equivalent — `.env` protection is per-tool only | `path` surface denies/asks across all tools and bash at once; a `path` deny cannot be overridden by a per-tool allow | | OpenCode-only surfaces | `lsp`, `question`, `webfetch`, `websearch`, `todowrite`, `doom_loop` | Not applicable — Pi does not expose these tools or events | | File mutation surfaces | `edit` covers `edit`, `write`, `apply_patch` | Separate `write` and `edit` surfaces | | Search/discovery surfaces | `glob`, `grep`, `list` | `find`, `grep`, `ls` (Pi tool names) | | `mcp` surface | Not a documented permission surface | First-class with server/tool-level granularity | | Top-level string shorthand | `"permission": "allow"` sets all surfaces | Not supported; must use an object | | Per-agent config location | `agent` key in config JSON or YAML frontmatter | YAML frontmatter in agent `.md` files only | | Config file paths | `~/.config/opencode/opencode.json` | `~/.pi/agent/extensions/pi-permission-system/config.json` | | Subagent prompt forwarding | Not documented | `ask` policies work in non-UI subagent contexts | | Infrastructure auto-allow | N/A | Read-only tools to Pi infra dirs bypass the gate | | Permission review log | No equivalent documented | Writes decisions to a JSONL audit log | ### Notable Differences Explained #### Default Fallback: `allow` vs `ask` OpenCode defaults to permissive — most tools work without configuration. This extension defaults to least privilege — omitting `"*"` gives you `"ask"` for everything. If you want OpenCode-like permissiveness: ```jsonc { "permission": { "*": "allow", "external_directory": "ask" } } ``` #### File Mutation Surfaces OpenCode unifies all file writes under a single `edit` permission. This extension exposes Pi's actual tool names: `write` (create/overwrite) and `edit` (targeted replacement). To replicate OpenCode's unified behavior, set both to the same action: ```jsonc { "permission": { "write": "ask", "edit": "ask" } } ``` #### MCP Surface (Pi-Only) This extension provides a first-class `mcp` permission surface with granular server and tool-level control: ```jsonc { "permission": { "mcp": { "*": "ask", "mcp_status": "allow", "myServer:*": "ask", "dangerousServer": "deny" } } } ``` OpenCode does not expose MCP as a configurable permission surface. #### Bash Path Extraction Both systems use `web-tree-sitter` + `tree-sitter-bash` to parse shell commands into an AST for `external_directory` path detection, but the extraction strategies differ significantly: **OpenCode** only extracts paths from a hardcoded allowlist of file-manipulating commands (`rm`, `cp`, `mv`, `mkdir`, `touch`, `chmod`, `chown`, `cat`, plus PowerShell equivalents). Commands not in the list — including `sed`, `awk`, `grep` — get no path extraction at all. For allowlisted commands, all non-flag positional arguments are assumed to be paths. **This extension** extracts path candidates from all commands generically, then applies additional intelligence: - A `PATTERN_FIRST_COMMANDS` map understands flag arity for `sed`, `awk`, `grep`, `rg`, and similar tools, distinguishing inline patterns/scripts from file arguments to avoid false positives. - Redirect destinations (`> /path/to/file`) are extracted. - Heredoc bodies, comments, and variable assignments are skipped. The result is broader coverage (paths detected in any command, not just a curated list) with fewer false positives on pattern-first commands (no spurious prompts for sed regexes or grep patterns that happen to contain `/`). ## Porting an OpenCode Config ### Before (OpenCode) ```json { "$schema": "https://opencode.ai/config.json", "permission": { "*": "allow", "bash": { "*": "ask", "git *": "allow", "npm *": "allow", "rm *": "deny" }, "edit": { "*": "ask", "src/*.ts": "allow" }, "external_directory": { "~/projects/*": "allow" } } } ``` ### After (this extension) ```jsonc { "$schema": "https://raw.githubusercontent.com/gotgenes/pi-packages/main/packages/pi-permission-system/schemas/permissions.schema.json", "permission": { "*": "allow", "bash": { "*": "ask", "git *": "allow", "npm *": "allow", "rm *": "deny" }, "write": "ask", "edit": "ask", "external_directory": { "*": "ask", "~/projects/*": "allow" } } } ``` ### Key Translation Steps 1. **Replace `"permission": "allow"`** (top-level string) with `"permission": { "*": "allow" }`. 2. **Split `edit`** into separate `write` and `edit` entries if you need different policies for create vs. modify. If not, set both to the same action. 3. **Rename search surfaces**: `glob` → `find`, `list` → `ls`. 4. **Add `.env` rules manually** if you relied on OpenCode's built-in protection. The `path` surface is the recommended approach — it covers all tools and bash in one rule: ```jsonc { "permission": { "path": { "*": "allow", "*.env": "deny", "*.env.*": "deny", "*.env.example": "allow" } } } ``` Alternatively, use per-tool patterns if you only need to protect specific tools (e.g., `read`): ```jsonc { "permission": { "read": { "*": "allow", "*.env": "deny", "*.env.*": "deny", "*.env.example": "allow" } } } ``` 5. **Remove OpenCode-only surfaces** (`lsp`, `question`, `webfetch`, `websearch`, `todowrite`, `doom_loop`) — they have no effect in this extension. 6. **Add `mcp` rules** if you use MCP servers — OpenCode has no equivalent, so this is new configuration.