Files
AGENTS/.sisyphus/evidence/task-4-opencode-agent-format.md
2026-04-13 16:53:17 +02:00

15 KiB
Raw Permalink Blame History

Task 4: OpenCode File-Based Agent Format Research

Date: 2026-04-10
Status: Complete
Research Method: WebFetch + Documentation Analysis


Executive Summary

OpenCode supports two agent configuration methods:

  1. JSON - Embedded in opencode.json (config.json)
  2. Markdown Files - File-based in .opencode/agents/ directory (per-project) or ~/.config/opencode/agents/ (global)

This research focuses on the file-based markdown format, which is the target for the harness-agnostic migration.


File Location & Discovery

Directory Structure

Per-project agents (takes precedence):

.opencode/agents/
├── agent-name.md
├── another-agent.md
└── ...

Global agents (fallback):

~/.config/opencode/agents/
├── agent-name.md
├── another-agent.md
└── ...

Discovery Mechanism

  • OpenCode scans both directories for *.md files
  • The filename (without .md extension) becomes the agent name
  • Per-project agents override global agents with the same name
  • All agents are loaded at startup and available via Tab switching or @mention

Key Finding

The agent name is derived from the filename, not from a name field in the frontmatter. Example:

  • File: review.md → Agent name: review
  • File: code-reviewer.md → Agent name: code-reviewer

YAML Frontmatter Specification

All file-based agent markdown files must include YAML frontmatter with the following fields:

Required Fields

Field Type Description Example
description string Brief description of agent purpose and when to use it. REQUIRED. "Reviews code for quality and best practices"

Optional Fields

Field Type Default Description
mode string all Agent mode: primary, subagent, or all
model string Model globally configured in config Override LLM model for this agent
temperature float Model-specific (usually 0 or 0.55 for Qwen) LLM response randomness (0.01.0)
top_p float Alternative to temperature for diversity control
steps integer No limit Max agentic iterations before forced text-only response
disable boolean false Set to true to disable the agent
hidden boolean false Hide from @ autocomplete (subagents only)
color string Hex color (e.g., #FF5733) or theme color (primary, secondary, accent, success, warning, error, info)
permission object Permission rules for edit, bash, webfetch, question, websearch, external_directory
task object Control which subagents this agent can invoke via Task tool

Provider-Specific Fields

Any additional fields are passed through directly to the LLM provider. Example for OpenAI reasoning models:

---
description: Agent using high reasoning effort
model: openai/gpt-5
reasoningEffort: high
textVerbosity: low
---

Permission Format (YAML)

Permissions control what actions an agent can perform. The format supports two styles:

Simple Format (Single Action)

permission:
  edit: deny
  bash: ask
  webfetch: allow

Granular Format (Rules Array)

For more control over specific patterns:

permission:
  edit: 
    "*": allow
    "/run/agenix/**": deny
  bash:
    "*": ask
    "git status*": allow
    "git log*": allow
    "git push": ask
    "grep *": allow
  webfetch: deny
  question: allow
  websearch: allow
  external_directory:
    "*": ask
    "~/p/**": allow
    "~/.config/opencode/**": allow
    "/tmp/**": allow

Permission Actions

Value Meaning
allow Tool allowed without approval
ask Prompt user for approval before running
deny Tool disabled

Supported Permission Keys

Key Values Notes
edit allow|ask|deny or nested rules File write/patch operations
bash allow|ask|deny or nested rules Bash command execution; supports glob patterns
webfetch allow|ask|deny HTTP requests
question allow|ask|deny User questions/clarification
websearch allow|ask|deny Web search operations
external_directory allow|ask|deny or nested rules Access to external directories
task nested rules Subagent invocation control (glob patterns)

Glob Pattern Support

Patterns support wildcards and recursion:

  • * — single-level wildcard
  • ** — recursive wildcard
  • git push* — suffix matching
  • ~/p/** — home directory paths
  • /run/agenix/** — absolute paths

Rule Precedence

When multiple rules match, the last matching rule wins:

bash:
  "*": ask
  "git status*": allow
  "git push*": deny

In this example:

  • git status matches both * and git status* → result: allow (last rule wins)
  • git push origin main matches both * and git push* → result: deny
  • ls -la matches only * → result: ask

Mode Field Values

Mode Type Description
primary Primary agent Agent available via Tab key switching; handles main conversation
subagent Specialized agent Invoked via @mention or automatically by other agents for specific tasks
all Flexible Can be used as both primary and subagent (default if omitted)

System Prompt Delivery

The markdown file body (after the YAML frontmatter) contains the system prompt:

---
description: Code review without edits
mode: subagent
permission:
  edit: deny
---
You are a code reviewer. Focus on:
- Code quality and best practices
- Potential bugs and edge cases
- Performance implications
- Security considerations

Provide constructive feedback without making direct changes.

The markdown content is passed directly as the system prompt to the LLM. It supports:

  • Inline markdown formatting
  • Lists and sections
  • Structured instructions
  • Code examples (fenced with backticks)

Default Behavior for Omitted Fields

Field Default Notes
description ERROR Required; absence causes parse failure
mode all Agent can be used as primary or subagent
model Global config model Primary agents use global model; subagents use parent's model
temperature Model-specific Usually 0 for most models; 0.55 for Qwen models
permission Full access If omitted, all tools enabled (no restrictions)
disable false Agent is enabled by default
hidden false Agent visible in @ autocomplete (if subagent)

Interaction with config.json (JSON Format)

Current State (Task 1 Finding)

The current system embeds agents in config.json via JSON:

{
  "agent": {
    "build": {
      "description": "...",
      "mode": "primary",
      "permission": { ... }
    }
  }
}

File-Based Agents Complement, Don't Replace

  • JSON agents (in config.json) are loaded from embedded config
  • Markdown agents (.opencode/agents/*.md files) are symlinked
  • Both are loaded and available simultaneously
  • Markdown agents override JSON agents with the same name

Migration Path

The harness-agnostic migration will:

  1. Move agent definitions from agents.json.opencode/agent/{name}.md files
  2. Update home-manager deployment to symlink .opencode/agents/ instead of embedding agents.json
  3. System prompt changes (markdown file edits) will NOT require home-manager switch

Key Advantage: Prompt Changes Don't Require home-manager switch

Current Limitation (JSON/Embedded)

agents.json → home-manager → embedded into config.json
↓
Change required in nixpkgs module
↓
home-manager switch (full system rebuild)

New Capability (File-Based)

.opencode/agents/{name}.md → home-manager → symlinks to ~/.config/opencode/agents/
↓
Change markdown file directly
↓
OpenCode reloads on next startup (NO home-manager switch needed)

This is the KEY ADVANTAGE of file-based agents: faster iteration on prompts and agent configuration.


Limitations & Gotchas

No Name Field in Frontmatter

  • Agent name comes from filename only
  • No name: foo field in frontmatter
  • Renaming file renames the agent

Model References with {file:...}

In JSON config, you can reference external files:

{
  "prompt": "{file:./prompts/build.txt}"
}

In markdown files, the body IS the prompt — no {file:...} syntax. The entire markdown content after frontmatter is the system prompt.

Subdirectories Not Scanned

  • Only files directly in .opencode/agents/ are loaded
  • Subdirectories are ignored
  • All agent definitions must be in one directory level

Filename Validation

The filename should follow these conventions (not enforced, but recommended):

  • Lowercase letters, numbers, hyphens: [a-z0-9-]+
  • No spaces, no special characters
  • Examples: code-reviewer.md, security-auditor.md, docs-writer.md

Complete Example: File-Based Agent

File: .opencode/agents/code-reviewer.md

---
description: Performs comprehensive code review focusing on quality, security, and performance
mode: subagent
model: anthropic/claude-sonnet-4-20250514
temperature: 0.1
permission:
  edit:
    "*": deny
  bash:
    "*": allow
    "grep *": allow
    "git diff*": allow
  webfetch: allow
  question: allow
---
You are an expert code reviewer with deep knowledge of software architecture, security best practices, and performance optimization.

## Your Mission

Review code for:
1. **Correctness** - Logic errors, edge cases, off-by-one bugs
2. **Security** - Input validation, injection vulnerabilities, data exposure
3. **Performance** - Algorithmic efficiency, memory usage, unnecessary allocations
4. **Maintainability** - Code clarity, naming, documentation, SOLID principles
5. **Testing** - Coverage gaps, missing test cases, integration test concerns

## Process

1. Ask clarifying questions about context and constraints
2. Provide specific, actionable feedback with examples
3. Suggest refactorings with rationale
4. Never make changes directly (read-only mode)
5. Prioritize critical issues over style concerns

## Output Format

- **Critical Issues** (must fix before merge)
- **Important Improvements** (should fix)
- **Nice-to-Have Suggestions** (consider for future)
- **Questions** (for author clarification)

Complete Example: JSON Config Format (For Reference)

For comparison, here's the equivalent in JSON config.json:

{
  "$schema": "https://opencode.ai/config.json",
  "agent": {
    "code-reviewer": {
      "description": "Performs comprehensive code review focusing on quality, security, and performance",
      "mode": "subagent",
      "model": "anthropic/claude-sonnet-4-20250514",
      "temperature": 0.1,
      "permission": {
        "edit": {
          "*": "deny"
        },
        "bash": {
          "*": "allow",
          "grep *": "allow",
          "git diff*": "allow"
        },
        "webfetch": "allow",
        "question": "allow"
      },
      "prompt": "You are an expert code reviewer...\n\n## Your Mission\n..."
    }
  }
}

Source Materials

Documentation

  • Official: https://opencode.ai/docs/agents
  • Agents Section: Comprehensive spec for all agent config options
  • Markdown Example: Review agent example provided in docs
  • Security Auditor Example: Security-focused agent example

Code References

Current System Reference

  • Nix Module: /home/m3tam3re/p/NIX/nixpkgs/modules/home-manager/coding/opencode.nix

    • Line 149: agent = builtins.fromJSON (builtins.readFile "${inputs.agents}/agents/agents.json");
    • Line 149: Shows current embedding pattern
  • AGENTS repo: /home/m3tam3re/p/AI/AGENTS/agents/agents.json

    • 6 agents: Chiron, Chiron Forge, Hermes, Athena, Apollo, Calliope
    • Permission structure: nested objects with wildcard patterns

Questions Addressed

Q: Do file-based agents need home-manager switch for prompt changes?

A: NO

  • File changes are immediately available
  • .opencode/agents/ is symlinked (not embedded)
  • OpenCode reloads agent definitions at startup
  • Prompt changes require only file edit + app restart

This is the KEY ADVANTAGE driving the migration.

Q: What directory: agent or agents?

A: agents (plural) (both global and per-project)

  • Global: ~/.config/opencode/agents/
  • Per-project: .opencode/agents/

Q: Do agent names need a name field in frontmatter?

A: NO

  • Agent name comes from filename only
  • No name: foo field in frontmatter
  • Example: review.md → agent name is review

Q: What YAML frontmatter fields are required?

A: Only description is truly required

  • All other fields have sensible defaults
  • Missing fields use their defaults
  • Frontmatter-less file will fail to parse

Q: How are permissions specified in markdown?

A: Same nested object format as JSON

permission:
  edit: 
    "*": allow
    "/sensitive/**": deny
  bash:
    "*": ask
    "git push": deny

Confirmation Summary

Question Finding
Directory: agent or agents? agents/ (both global and per-project)
File naming: How determined? Filename (without .md) becomes agent name
Required fields: What's mandatory? description only; others have defaults
Permission format: YAML or different? Same nested object format as JSON
Mode values: Options? primary | subagent | all
Prompt format: How specified? Markdown body after frontmatter
Requires HM switch for prompt changes? NO (major advantage)
Does frontmatter need name field? NO (filename is the name)
Can agents be in subdirectories? NO (only root level of .opencode/agents/)
Can you override agents from JSON config? YES (markdown agents override JSON with same name)

Next Steps (Task 9: OpenCode Renderer)

The renderer will generate .opencode/agents/{name}.md files with:

  1. Frontmatter generation:

    • Convert agent.toml [description] → YAML description:
    • Convert [mode] → YAML mode:
    • Convert [temperature] → YAML temperature:
    • Convert [permission] from two-level format → nested YAML objects
  2. Body generation:

    • Use agent.toml system_prompt field → markdown body
  3. File naming:

    • Filename: {agent_name}.md (from agent.toml name field)
    • Agent name in OpenCode: derived from filename automatically

Evidence Collection

  • Source 1: https://opencode.ai/docs/agents (Official documentation)
  • Source 2: /home/m3tam3re/p/NIX/nixpkgs/modules/home-manager/coding/opencode.nix (Current deployment)
  • Source 3: /home/m3tam3re/p/AI/AGENTS/agents/agents.json (Current agent definitions)
  • Source 4: /home/m3tam3re/p/AI/AGENTS/AGENTS.md (Repository documentation)

Research Date: 2026-04-10
Researcher: Sisyphus-Junior
Task: Task 4 of harness-agnostic-migration plan