Files

214 lines
12 KiB
Markdown

# 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.