Agent Artifacts
Skills teach an agent a capability and rules constrain it; an agent artifact defines an agent itself — a named, delegatable assistant with its own system prompt, model, and tool access.
Every major AI client has grown such a definition format: Claude Code subagents, OpenCode agents, and Copilot CLI custom agents. All three read a Markdown file with YAML frontmatter whose body is the system prompt — but each with its own field names, its own directory, and its own quirks. Teams end up copy-pasting near-identical agent files between repositories and editing three variants by hand.
Grimoire treats an agent like any other artifact: author one canonical
file, publish it once, and let grim install project it into each
client’s native format — the same model that powers
vendor-specific metadata for skills and rules.
The canonical format
An agent is a single .md file. Unlike a rule, the
frontmatter is required — every client needs at least a description
to route work to the agent:
# code-reviewer.md
---
name: code-reviewer
description: Reviews diffs for correctness, security, and style.
model: sonnet
tools: Read,Grep,Bash
metadata:
summary: Multi-pass diff reviewer
keywords: review,quality
claude.memory: project
opencode.mode: subagent
opencode.temperature: "0.2"
---
You are a code reviewer. Analyze the diff and report specific,
actionable findings.
The body below the frontmatter is the agent’s system prompt and installs verbatim for every client.
Common fields
| Field | Required | Type | Validation |
|---|---|---|---|
name | yes | string | Must equal the file stem (code-reviewer.md ⇒ code-reviewer); lowercase letters, digits, hyphens |
description | yes | string | Free text — when a client should delegate to this agent |
model | no | string | Passed through verbatim to each client; no alias translation |
tools | no | string | Comma-separated tool list, projected into each client’s native shape |
metadata | no | string→string map | Catalog keys (summary, keywords) plus vendor-namespaced keys (<vendor>.<field>) |
The name-equals-stem rule exists because OpenCode derives an agent’s identity from its filename; Grimoire enforces the rule for every client so the identity is consistent everywhere.
Everything a single vendor understands — Claude’s permissionMode,
OpenCode’s temperature, Copilot’s tool restrictions — is authored as a
<vendor>.<field> string key inside metadata. The full key tables live
in the vendor metadata reference.
Override precedence
The common model and tools fields are defaults. When a vendor key
lifts to the same native field, the vendor key wins for that vendor —
silently, because the collision is the documented escape hatch:
model: sonnet
metadata:
claude.model: opus # Claude installs model: opus
opencode.model: anthropic/claude-sonnet-4-5 # OpenCode gets this instead of "sonnet"
This matters most for model: Claude Code reads
aliases like sonnet, while OpenCode expects a
provider/model-id string. Grimoire deliberately does not translate
between the two — set opencode.model when the common value is not what
OpenCode needs.
What each client receives
On install, grim projects the canonical file per client:
| Canonical field | Claude Code | OpenCode | Copilot CLI |
|---|---|---|---|
name | kept | dropped (filename is the identity) | kept |
description | kept | kept | kept |
model | kept | kept (see precedence) | dropped (no documented field) |
tools | kept (comma string) | dropped (deprecated upstream) | emitted as a YAML list |
plain metadata / unknown keys | kept | dropped | dropped |
| body | verbatim | verbatim | verbatim |
| provenance comment | none | yes | yes |
The canonical format is Claude Code’s native subagent format, so a
plain agent — one with no <vendor>.<field> metadata keys — installs for
Claude byte-identical to the published file (generated: false). The
OpenCode and Copilot files are always generated transforms and carry a
provenance comment; editing them by hand is detected as
drift, exactly like any generated file.
Install locations
Project scope:
| Client | Path |
|---|---|
| Claude Code | .claude/agents/<name>.md |
| OpenCode | .opencode/agents/<name>.md |
| Copilot CLI | .github/agents/<name>.md |
Global scope (native user-level discovery directories, honoring each client’s directory-override variable — the same resolution as skill discovery):
| Client | Path | Env override |
|---|---|---|
| Claude Code | ~/.claude/agents/<name>.md | $CLAUDE_CONFIG_DIR/agents/ |
| OpenCode | ~/.config/opencode/agents/<name>.md (XDG) | $OPENCODE_CONFIG_DIR/agents/ |
| Copilot CLI | ~/.copilot/agents/<name>.md | $COPILOT_HOME/agents/ |
Unlike global rules, Copilot agents have a real user-level home — no inert-install warning applies.
Publishing
grim build and grim release need --kind agent for an agent file:
grim build ./code-reviewer.md --kind agent
grim release ./code-reviewer.md ghcr.io/acme/code-reviewer:1.0.0 --kind agent
The flag is required because a bare .md path is indistinguishable from a
rule by shape — and rules accept arbitrary frontmatter,
so guessing from content would silently flip kinds. When a file released
as a rule carries both name and description, grim warns that it looks
like an agent definition.
Publishing runs the same gate as skills and rules: every
<vendor>.<field> metadata key is validated against the vendor
registries, and an invalid literal (say claude.permission-mode: yolo)
fails the release with exit 65 before anything reaches the registry. The
artifact publishes with a com.grimoire.kind annotation of agent, so
grim add infers the kind with no flag.
Catalog metadata (summary, keywords) is authored in the metadata
map, like a skill — see catalog metadata.
Consuming
Agents ride the standard lifecycle. Declarations live in an [agents]
table of grimoire.toml; the lock carries [[agent]] entries; and
bundles accept agent members alongside skills
and rules:
grim add ghcr.io/acme/code-reviewer:1 # kind inferred from com.grimoire.kind
grim install # projects into every selected client
grim status # shows the agent row
grim uninstall agent code-reviewer # removes files + declaration
Limitations
- Object-valued vendor fields cannot be authored: the
metadatamap is string-valued by the agentskills contract, so Claude’smcpServersandhooks, OpenCode’spermission, and Copilot’smcp-serversare not projectable. Add them by editing the installed file (Claude/Copilot) or the client’s own config. - No support directory. An agent packs to exactly one
<name>.md; a sibling folder sharing the stem is ignored (unlike rules). - No model translation. The common
modelpasses through verbatim; useopencode.modelwhen the OpenCode side needs aprovider/model-idvalue.