Agent folders other tools keep
You have probably written these definitions once already. Morse reads the agent
folders your other tooling keeps, so a populated .claude/agents/ needs no
copying:
.claude/agents/backend.md → morse join backend.codex/agents/backend.toml → morse join backendEach rung of the lookup ladder is widened
with those folders, and .morse/roles still wins at the same rung — writing the
morse file is how you say “I mean this one”. $MORSE_ROLES is not widened;
packs stay morse-shaped.
The built-in plugins
Section titled “The built-in plugins”| Plugin | Project | Personal | Layout |
|---|---|---|---|
claude |
.claude/agents |
~/.claude/agents |
<name>.md |
codex |
.codex/agents |
~/.codex/agents |
<name>.toml |
pi |
.pi/agent/agents, .pi/agents |
~/.pi/agent/agents |
<pack>/<name>.md |
pi’s project-local convention is unconfirmed, so both plausible directories are
searched. A directory that is not there is the normal case, not an error —
morse roles lists it as (absent) rather than staying quiet about it.
Only two fields are borrowed
Section titled “Only two fields are borrowed”Only name and description are taken, plus Codex’s developer_instructions
as the guidance body.
A tools: list, a sandbox_mode or a model is a permission, not a capability
blurb, so none of them are mapped onto morse skills. Agents pick teammates by
reading skills, and a borrowed role arriving with none is honest about what its
source file actually said. Write a .morse/roles/<name>.md when you want that
agent to advertise skills.
The TOML reader refuses rather than guesses
Section titled “The TOML reader refuses rather than guesses”Codex files are TOML, which morse reads with a deliberately small reader:
key = "value"key = """multi-line"""- comments
Anything else — tables, dotted keys, arrays, ''' literals — refuses the
whole file rather than guessing. A prompt body silently truncated at the first
""" is worse than no role at all, because nothing looks wrong.
A refused file is reported with its reason by morse roles, and by morse join
when you asked for that role by name.
Teaching morse a fourth ecosystem
Section titled “Teaching morse a fourth ecosystem”It is a JSON file, not a patch. Drop it in .morse/plugins/ (or
~/.morse/plugins/):
{ "id": "acme", "project": [".acme/agents"], "depth": 0, "map": { "description": "summary" } }A plugin is a manifest, never code — morse reads config files, it does not run
them. Reusing an id replaces that plugin, which is how you correct a built-in
without waiting for a release; when a manifest inside the project does that,
morse roles says so, because it may have arrived with a clone.
Manifests are read from .morse/plugins/*.json in a project and
plugins/*.json under $MORSE_HOME. One that does not parse is skipped rather
than fatal.
| Field | Default | Meaning |
|---|---|---|
id |
required | Names the plugin in morse roles output. Lowercase letters, digits, dot, dash, underscore. |
project |
none | Directories to search, relative to a project root (the current directory, the git root). |
personal |
none | Directories to search, relative to your home directory. |
depth |
0 |
Levels below each directory to descend. Flat layouts are 0; pi’s agents/<pack>/<name>.md is 1. |
extensions |
[".md", ".markdown"] |
Which files are agent definitions. |
format |
"frontmatter" |
How a file is read: frontmatter or toml. |
map |
{} |
Which key in that ecosystem supplies each morse field — any of name, role, description, skills, brief. Anything left out is absent rather than guessed. |
brief is the guidance body, and is only consulted for formats with no document
body of their own — a markdown file’s body is its brief. A directory that
contains .. or starts with / rejects the manifest: a manifest directory is
joined onto a search root, and it must not be able to climb out of one.
Auditing what morse found
Section titled “Auditing what morse found”morse roles # every definition, labelled with the plugin that supplied itmorse roles --no-plugins # only .morse/rolesmorse roles labels every borrowed definition with the plugin that supplied it
and lists every directory searched, including the ones that were absent. To turn
discovery off and get pre-plugin behaviour exactly, pass --no-plugins or set
MORSE_PLUGINS=off.