Opencode permissions
Skill Timmy6942025/opencode-builder-skill/skills/opencode-permissions
Kilo/agent skill for building OpenCode extensions, plugins, and integrations
npx -y skills add Timmy6942025/opencode-builder-skill --skill opencode-permissionsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use this skill when configuring OpenCode's permission system β controlling which tools require approval, setting granular bash command rules, managing external directory access, configuring per-agent permissions, or understanding the allow/ask/deny permission model. Covers wildcards, pattern matching, doom loop detection, and the complete permission key reference.
SKILL.md
11.2 KB, as published. Nobody here has run it
OpenCode Permissions
π Official Docs: For the latest information, always refer to the official documentation: https://opencode.ai/docs/permissions/
Control which actions require approval to run in OpenCode.
OpenCode uses the permission config to decide whether a given action should run automatically, prompt you, or be blocked. Permissions control what tools can do during a session.
Actions
Each permission rule resolves to one of three actions:
| Action | Behavior |
|---|---|
"allow" | Run without approval |
"ask" | Prompt for approval before running |
"deny" | Block the action entirely |
Configuration
Set All Permissions at Once
You can set a global default for all permissions:
{
"$schema": "https://opencode.ai/config.json",
"permission": "allow"
}
Set Per-Tool Permissions
Override specific tools while keeping a global default:
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"*": "ask",
"bash": "allow",
"edit": "deny"
}
}
Granular Rules (Object Syntax)
For most permissions, you can use an object to apply different actions based on the tool input. Rules are evaluated by pattern match, with the last matching rule winning.
Basic Pattern
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"bash": {
"*": "ask",
"git *": "allow",
"npm *": "allow",
"rm *": "deny",
"grep *": "allow"
},
"edit": {
"*": "deny",
"packages/web/src/content/docs/*.mdx": "allow"
}
}
}
In this example:
git statusβ last matching rule is"git *": "allow"β allowedrm -rf /β last matching rule is"rm *": "deny"β deniednpm installβ last matching rule is"npm *": "allow"β allowed- Edit to
.mdxfiles β allowed; edit to other files β denied
Wildcards
Permission patterns use simple wildcard matching:
| Pattern | Matches |
|---|---|
* | Zero or more of any character |
? | Exactly one character |
| All other characters | Match literally |
Examples:
"git *"matchesgit status,git commit -m "msg",git push origin main"git ?*"matchesgit statusbut notgitalone"rm *"matchesrm file.txt,rm -rf /tmp/dir
Home Directory Expansion
You can use ~ or $HOME at the start of a pattern to reference your home directory:
| Pattern | Expands To |
|---|---|
~/projects/* | /Users/username/projects/* |
$HOME/projects/* | /Users/username/projects/* |
~ | /Users/username |
This is particularly useful for external_directory rules.
Available Permissions
OpenCode permissions are keyed by tool name, plus a couple of safety guards:
| Key | Tools It Gates | Matching Input |
|---|---|---|
read | read | File path |
edit | write, edit, apply_patch | File path |
glob | glob | Glob pattern |
grep | grep | Regex pattern |
bash | bash | Parsed command (e.g., git status --porcelain) |
task | task | Subagent type |
skill | skill | Skill name |
lsp | lsp | (non-granular currently) |
question | question | (non-granular) |
webfetch | webfetch | URL |
websearch | websearch | Query |
external_directory | Any tool reading/writing outside project | Path |
todowrite | todowrite, todoread | (non-granular) |
doom_loop | Recovery when same tool call repeats 3x with identical input | (non-granular) |
The read, edit, glob, grep, list, bash, task, external_directory, lsp, and skill keys accept either a shorthand action ("allow" | "ask" | "deny") or an object of glob/pattern β action for fine-grained control. The remaining keys accept the shorthand action only.
Defaults
If you don't specify anything, OpenCode starts from permissive defaults:
- Most permissions default to
"allow" doom_loopandexternal_directorydefault to"ask"readis"allow", but.envfiles are denied by default:
{
"permission": {
"read": {
"*": "allow",
"*.env": "deny",
"*.env.*": "deny",
"*.env.example": "allow"
}
}
}
What "Ask" Does
When OpenCode prompts for approval, the UI offers three outcomes:
| Outcome | Behavior |
|---|---|
once | Approve just this request |
always | Approve future requests matching the suggested patterns (for the rest of the current OpenCode session) |
reject | Deny the request |
The set of patterns that always would approve is provided by the tool (for example, bash approvals typically whitelist a safe command prefix like git status*).
External Directories
Use external_directory to allow tool calls that touch paths outside the working directory where OpenCode was started. This applies to any tool that takes a path as input (e.g., read, edit, glob, grep, and many bash commands).
Home expansion (~/...) only affects how a pattern is written. It does not make an external path part of the current workspace, so paths outside the working directory must still be allowed via external_directory.
Allow Access to a Directory
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"external_directory": {
"~/projects/personal/**": "allow"
}
}
}
Layer Additional Rules
Any directory allowed here inherits the same defaults as the current workspace. Since read defaults to allow, reads are also allowed for entries under external_directory unless overridden:
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"external_directory": {
"~/projects/personal/**": "allow"
},
"edit": {
"~/projects/personal/**": "deny"
}
}
}
This allows reading files in ~/projects/personal/ but blocks editing them.
Agent Permissions
You can override permissions per agent. Agent permissions are merged with the global config, and agent rules take precedence.
JSON Configuration
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"bash": {
"*": "ask",
"git *": "allow",
"git commit *": "deny",
"git push *": "deny",
"grep *": "allow"
}
},
"agent": {
"build": {
"permission": {
"bash": {
"*": "ask",
"git *": "allow",
"git commit *": "ask",
"git push *": "deny",
"grep *": "allow"
}
}
}
}
}
Markdown Agent Configuration
---
description: Code review without edits
mode: subagent
permission:
edit: deny
bash: ask
webfetch: deny
---
Only analyze code and suggest changes.
Bash Permission Patterns
Use pattern matching for commands with arguments. "grep *" allows grep pattern file.txt, while "grep" alone would block it. Commands like git status work for default behavior but require explicit permission (like "git status *") when arguments are passed.
{
"permission": {
"bash": {
"*": "ask",
"git status *": "allow",
"git diff *": "allow",
"git log *": "allow",
"npm test *": "allow",
"npm run *": "allow",
"rm *": "deny"
}
}
}
Complete Examples
Restrictive Setup
Everything requires approval except safe read operations:
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"*": "ask",
"read": "allow",
"glob": "allow",
"grep": "allow",
"skill": "allow",
"question": "allow"
}
}
Permissive with Specific Denials
Allow everything except dangerous operations:
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"*": "allow",
"bash": {
"*": "allow",
"rm -rf *": "deny",
"sudo *": "deny",
"git push --force *": "deny"
},
"external_directory": "ask"
}
}
Development Agent with Git Restrictions
{
"agent": {
"build": {
"permission": {
"bash": {
"*": "allow",
"git push *": "ask",
"git commit *": "ask",
"rm *": "ask"
},
"edit": {
"*.env": "deny",
"*.env.*": "deny",
"*": "allow"
}
}
}
}
}
Read-Only Review Agent
{
"agent": {
"reviewer": {
"mode": "subagent",
"permission": {
"edit": "deny",
"bash": {
"*": "deny",
"git diff *": "allow",
"git log *": "allow",
"grep *": "allow",
"find *": "allow"
},
"webfetch": "allow",
"skill": "allow"
}
}
}
}
MCP Tool Permissions
You can control permissions for MCP server tools using glob patterns on the tool name prefix:
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"sentry_*": "allow",
"context7_*": "ask",
"github_*": {
"*": "ask",
"github_list_repos *": "allow",
"github_create_issue *": "deny"
}
}
}
Doom Loop Detection
The doom_loop permission triggers when the same tool call repeats 3 times with identical input. This is a safety mechanism to prevent infinite loops. It defaults to "ask", giving you the option to break out of the loop.
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"doom_loop": "deny"
}
}
Troubleshooting
Tool Blocked Unexpectedly
- Check the global permission config in
opencode.json - Check agent-specific permissions (agent rules take precedence)
- Check if the tool matches a deny pattern
- Use
opencode debug configto see the resolved permission config
Permission Prompts Too Frequent
- Use the
alwaysoption in the permission dialog to whitelist patterns - Configure explicit
"allow"rules for safe commands - Use object syntax to allow specific command patterns
.env Files Blocked
This is the default behavior. The read permission denies *.env and *.env.* by default. Override with:
{
"permission": {
"read": {
"*": "allow",
"*.env": "allow"
}
}
}
Warning: Allowing
.envreads means the LLM can see your secrets. Only do this if you understand the security implications.
Key Points
- Last match wins β Put broad rules first, specific exceptions after
- Agent overrides global β Agent-specific permissions take precedence
.envdenied by default β Override explicitly if neededexternal_directoryanddoom_loopdefault to"ask"β Everything else defaults to"allow"- Patterns use simple globs β
*matches anything,?matches one character alwaysin the UI β Whitelists patterns for the rest of the session- MCP tools use prefix matching β
servername_*matches all tools from that server