12 KiB
OpenCode Compatibility
This extension's flat permission format and evaluation semantics were directly inspired by OpenCode's permission model (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 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 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 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) |
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:
{
"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:
{
"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:
{
"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_COMMANDSmap understands flag arity forsed,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)
{
"$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)
{
"$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
-
Replace
"permission": "allow"(top-level string) with"permission": { "*": "allow" }. -
Split
editinto separatewriteandeditentries if you need different policies for create vs. modify. If not, set both to the same action. -
Rename search surfaces:
glob→find,list→ls. -
Add
.envrules manually if you relied on OpenCode's built-in protection. Thepathsurface is the recommended approach — it covers all tools and bash in one rule:{ "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):{ "permission": { "read": { "*": "allow", "*.env": "deny", "*.env.*": "deny", "*.env.example": "allow" } } } -
Remove OpenCode-only surfaces (
lsp,question,webfetch,websearch,todowrite,doom_loop) — they have no effect in this extension. -
Add
mcprules if you use MCP servers — OpenCode has no equivalent, so this is new configuration.