Skip to content

Instantly share code, notes, and snippets.

@remorses
Created October 9, 2026 10:39
Show Gist options
  • Select an option

  • Save remorses/9f3737a6f516c01f4bdb612045c64072 to your computer and use it in GitHub Desktop.

Select an option

Save remorses/9f3737a6f516c01f4bdb612045c64072 to your computer and use it in GitHub Desktop.
JSON Schemas for OpenCode V2 agent, command and skill markdown frontmatter

JSON Schemas for OpenCode V2 markdown frontmatter

OpenCode V2 publishes a schema for cli.json only. These schemas cover the frontmatter of agent, command, and skill markdown files.

File For $schema in frontmatter
opencode-command.schema.json commands/<name>.md safe, OpenCode ignores it
agent-skill.schema.json skills/<id>/SKILL.md safe, OpenCode ignores it
opencode-agent.schema.json agents/<id>.md do not add, see below
---
$schema: https://gist.githubusercontent.com/remorses/9f3737a6f516c01f4bdb612045c64072/raw/opencode-command.schema.json
description: Commit in groups
subagent: false
---

Agent files must not set $schema

OpenCode 2.0.19 to 2.0.26 checks agent frontmatter keys against a fixed list (agentKeys in packages/core/src/config/plugin/agent.ts). Any other key, including $schema, makes the file decode as V1. The V1 decoder moves unknown keys into request.body, so V2 permissions are lost and model is dropped. Use the agent schema through editor settings only, until OpenCode accepts $schema.

Exhaustiveness

The agent and command schemas list every key that the V2 decoder accepts. They reject unknown keys, because OpenCode silently ignores (commands) or misreads (agents) them. Examples: V1 variant on commands is dropped, V1 permission/disable/prompt on agents switch the file to V1 mode.

The skill schema is the union of the OpenCode V2 skill fields and the Agent Skills spec. It allows extra keys, because OpenCode ignores them and other tools use their own keys.

Sources

Code (the decoders are the source of truth; schemas match tag v2.0.26, identical to v2.0.19 except disable-model-invocation):

Docs:

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Agent skill frontmatter (OpenCode V2 + Agent Skills spec)",
"description": "Frontmatter of SKILL.md. The skill ID comes from the directory name, not `name`. OpenCode ignores keys it does not know, so extra keys are allowed. Sources: see README.md in this gist.",
"type": "object",
"properties": {
"$schema": {
"type": "string",
"description": "JSON Schema URL for editor validation. Ignored by OpenCode."
},
"name": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
"description": "Display name. Agent Skills spec: lowercase kebab-case, 1-64 chars, must match the directory name. OpenCode uses it only as a label and defaults it to the ID."
},
"description": {
"type": "string",
"minLength": 1,
"maxLength": 1024,
"description": "What the skill does and when to use it. OpenCode only advertises skills that have a description."
},
"license": {
"type": "string",
"description": "License name or a bundled license file. Accepted but not interpreted by OpenCode."
},
"compatibility": {
"type": "string",
"minLength": 1,
"maxLength": 500,
"description": "Environment requirements (product, packages, network). Accepted but not interpreted by OpenCode."
},
"metadata": {
"type": "object",
"description": "Free key-value map. Agent Skills spec expects string values.",
"properties": {
"opencode/autoinvoke": {
"description": "false hides the skill from the model list; it can still be loaded by ID or @skill-id. Wins over disable-model-invocation. OpenCode also accepts the strings \"true\" and \"false\".",
"anyOf": [{ "type": "boolean" }, { "type": "string", "enum": ["true", "false"] }]
}
}
},
"disable-model-invocation": {
"type": "boolean",
"description": "true hides the skill from the model list, like metadata.opencode/autoinvoke: false. Supported by OpenCode 2.0.26+ (not 2.0.19) and Claude Code."
},
"allowed-tools": {
"type": "string",
"description": "Experimental Agent Skills field: space-separated pre-approved tools, e.g. `Bash(git:*) Read`. Not interpreted by OpenCode."
}
},
"required": ["name", "description"],
"additionalProperties": true
}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "OpenCode V2 agent frontmatter",
"description": "Frontmatter of agents/<id>.md for OpenCode V2. The Markdown body is the agent system prompt. Do not put `$schema` in agent frontmatter: in OpenCode 2.0.19 to 2.0.26 any key outside this schema makes the file decode as V1, which drops `model` and `permissions`. Sources: see README.md in this gist.",
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "Purpose of the agent. Shown to the model that chooses which subagent to launch."
},
"mode": {
"type": "string",
"enum": ["primary", "subagent", "all"],
"default": "primary",
"description": "primary: main agent of a session. subagent: only runs in a child session through the subagent tool. all: both."
},
"model": {
"description": "Model reference `provider/model` with an optional `#variant`, or the expanded object form. A subagent without a model inherits the parent session model.",
"anyOf": [
{
"type": "string",
"pattern": "^[^/#]+\\/[^#]+(?:#[^#]+)?$",
"examples": ["anthropic/claude-sonnet-4-5#high", "openrouter/anthropic/claude-sonnet-4.5"]
},
{
"type": "object",
"properties": {
"providerID": { "type": "string", "pattern": "^[^/#]+$" },
"model": { "type": "string", "pattern": "^[^#]+$" },
"variant": { "type": "string", "pattern": "^[^#]+$" }
},
"required": ["providerID", "model"],
"additionalProperties": false
}
]
},
"variant": {
"type": "string",
"pattern": "^[^#]+$",
"description": "Legacy separate variant. Accepted as a native key and joined to `model` as `model#variant`. Prefer `model: provider/model#variant`."
},
"permissions": {
"description": "Ordered permission rules appended after the global `permissions`. The last matching rule wins. If no rule matches, OpenCode asks.",
"type": "array",
"items": { "$ref": "#/$defs/rule" }
},
"steps": {
"type": "integer",
"exclusiveMinimum": 0,
"description": "Maximum model steps. On the last step tools are removed and the model must answer in text."
},
"hidden": {
"type": "boolean",
"description": "Hides the agent from listings, discovery, and the subagent catalog. Visibility only, not security."
},
"color": {
"type": "string",
"pattern": "^#[0-9a-fA-F]{6}$",
"description": "UI color as a six-digit hex value."
},
"disabled": {
"type": "boolean",
"description": "Removes this built-in or custom agent."
},
"request": {
"type": "object",
"description": "Per-agent request overlays. OpenCode V2 preserves them but does not send them yet. Configure request settings on the provider, model, or variant instead.",
"properties": {
"headers": { "type": "object", "additionalProperties": { "type": "string" } },
"body": { "type": "object" }
},
"additionalProperties": false
},
"system": {
"type": "string",
"description": "System prompt. In Markdown agents the body sets it; do not use this key in frontmatter."
}
},
"additionalProperties": false,
"$defs": {
"rule": {
"type": "object",
"properties": {
"action": {
"type": "string",
"description": "Permission action. Wildcards `*` and `?` match the whole value. Plugins and MCP servers can add actions (`<server>_<tool>`).",
"anyOf": [
{
"enum": [
"*",
"read",
"edit",
"glob",
"grep",
"shell",
"subagent",
"skill",
"question",
"webfetch",
"websearch",
"external_directory",
"execute",
"browser"
]
},
{ "type": "string" }
]
},
"resource": {
"type": "string",
"description": "Matched value: path (read, edit, external_directory; `~` and `$HOME` expand), command (shell), agent ID (subagent), skill ID (skill), URL (webfetch), query (websearch), pattern (glob, grep), `*` for question, execute, and MCP tools."
},
"effect": { "type": "string", "enum": ["allow", "deny", "ask"] }
},
"required": ["action", "resource", "effect"],
"additionalProperties": false
}
}
}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "OpenCode V2 command frontmatter",
"description": "Frontmatter of commands/<name>.md for OpenCode V2. The trimmed Markdown body is the prompt template; it supports $ARGUMENTS, $1..$N, and !`shell` blocks. OpenCode ignores unknown keys, so `$schema` is safe here. Sources: see README.md in this gist.",
"type": "object",
"properties": {
"$schema": {
"type": "string",
"description": "JSON Schema URL for editor validation. Ignored by OpenCode."
},
"description": {
"type": "string",
"description": "Text shown in command lists and discovery."
},
"agent": {
"type": "string",
"description": "Agent selected when the command runs. Its model is used unless `model` is set."
},
"model": {
"description": "Model override `provider/model` with an optional `#variant`, or the expanded object form. Overrides every other model. There is no separate `variant` key in V2 commands.",
"anyOf": [
{
"type": "string",
"pattern": "^[^/#]+\\/[^#]+(?:#[^#]+)?$",
"examples": ["anthropic/claude-sonnet-4-5#high"]
},
{
"type": "object",
"properties": {
"providerID": { "type": "string", "pattern": "^[^/#]+$" },
"model": { "type": "string", "pattern": "^[^#]+$" },
"variant": { "type": "string", "pattern": "^[^#]+$" }
},
"required": ["providerID", "model"],
"additionalProperties": false
}
]
},
"subagent": {
"type": "boolean",
"description": "true: run in a background child session and report back. false: always run in the current session. Omitted: child session only when the agent has mode: subagent."
},
"subtask": {
"type": "boolean",
"deprecated": true,
"description": "Deprecated alias for `subagent`. `subagent` wins when both are set."
},
"template": {
"type": "string",
"description": "Prompt template. JSON config only; in Markdown commands the body sets it."
}
},
"additionalProperties": false
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment