Skills, Agents And Commands
The working half of Morphic — every property each keyword accepts, and how to tell the three apart.
Three keywords for the imperative half. They share most of their properties, and the differences are the interesting part.
| Invoked by | Declares when | Declares tools as | |
|---|---|---|---|
skill | the platform, when relevant | required | — |
agent | a caller delegating work | — | tools |
command | a person, by name | — | allowedTools |
If a person types it, it is a command. If something delegates a task to it, it is an agent. If it is instruction text other declarations reuse, it is a skill.
skill
A reusable instruction. Agents and commands list skills in use; skills build on
other skills with extends.
export abstract spec Skill as skill:
abstract description: string?
abstract when: string
abstract prompt: string
abstract use: Skill[]?description — string?
One line saying what the skill is. Optional to the compiler. When omitted,
adapters generate <Name> (generated from Psy)., which is never the sentence you
wanted — see requireDescription
if you would like to be nagged about it.
when — string, required
The condition under which the skill applies. Required, and required to be non-empty.
This is the one property Morphic demands that agent and command do not, and I
will not be talked out of it: a skill nobody knows when to reach for is a skill
nobody reaches for. Adapters fold it into whatever field their platform uses to
decide whether to load something.
when: Use before approving any change.Write it as a trigger, not a summary. "Use before approving any change" tells a platform when to load it. "Diff review guidance" does not.
prompt — string, required
The instruction itself, and the body of the generated artifact. Required and non-empty.
Almost always a string block, which preserves paragraphs and lets a subclass compose:
export skill ImplementChange extends ReviewDiff:
prompt:
+ super
Add or update tests for every behavioural change.# ImplementChange
Read the whole diff before commenting on any part of it.
Comment on what the change does, not on how you would have written it.
Add or update tests for every behavioural change.+ super appended rather than replaced — see +.
use — Skill[]?
Other skills this one builds on, in declaration order. Each entry is a reference
to a spec declared with skill. An agent, a command or a plain spec here is
an error.
export skill ImplementChange:
description: Implement a change end to end.
when: Use when asked to change behaviour.
use:
- ProjectContext
- RunChecks
prompt:
Make the smallest change that satisfies the request.---
name: implement-change
description: Implement a change end to end. Use when asked to change behaviour.
---
# ImplementChange
Make the smallest change that satisfies the request.
## Builds on
- ProjectContext
- RunChecksuse is a listing, not an inclusion. It does not merge the referenced
prompts; it tells the platform which other skills are relevant. To actually
inherit text, use extends and + super. A skill may not list itself.
context — Definition[]?
The definitions this declaration needs — the parts of the product it touches.
context:
- Rendering
- LayerStackuse and context answer different questions. use is instructions I build
on, and takes skills. context is knowledge I need, and takes definitions.
Both are type-checked, so swapping them is caught.
This is the property that connects the two halves of Morphic. See Specifying Software.
Naming
The spec name becomes a kebab-case slug: ReviewDiff → review-diff,
ImplementCLI → implement-cli, ReadPsySpec → read-psy-spec.
The slug is the artifact's filename and its identity on the platform, so two declarations that slug the same are an error rather than a silently overwritten file.
agent
An autonomous worker. It is handed a task and carries it out, using the skills it lists.
use "@psy/morphic"
import { ImplementChange, RunChecks } from "./skills"
export agent BuildProject:
description: Implement changes in this project.
use:
- ImplementChange
- RunChecks
model: opus
tools:
- Read
- Edit
- Bash
prompt:
Implement the requested change, then run the project's checks.export abstract spec Agent as agent:
abstract description: string?
abstract prompt: string
abstract use: Skill[]?
abstract model: string?
abstract tools: string[]?An agent has no when. A skill declares when it applies because the platform
decides whether to load it. An agent is chosen deliberately, by something that
already knows it wants an agent.
description — string?
What a caller reads when choosing between agents. Write it as a capability, not a title. "Implement changes in this project" beats "Build agent".
prompt, use, context
Same as a skill, with one difference: an agent's use list is rendered with a
sentence directing the agent to use them, where a skill's is just a list. See
what gets generated.
model — string?
Passed through verbatim, so the accepted values are the platform's, not Psy's. When omitted, the target's own default applies:
claude({ output: ".claude", model: "sonnet" })A model declared on the agent always beats the target default.
tools — string[]?
Passed through verbatim and comma-joined in the generated frontmatter.
An empty list is treated as absent — the field is omitted rather than emitted empty, because on most platforms an empty tools field means "no restriction" rather than "no tools", and emitting one would do the precise opposite of what you meant.
Inheriting agents
export agent ReviewProject extends BuildProject:
description: Review changes without making them.
tools:
- Read
- Grep
prompt:
+ super
Do not edit any file. Report findings instead.Note that tools here is replaced, not merged — a plain list property
overrides. That is what you want when narrowing a toolset. To extend one instead:
tools:
++ super
- WebSearchtools: Read, Edit, Bash, WebSearchuse composes the same way.
command
Invoked explicitly, by name. A slash command, in Claude Code terms.
use "@psy/morphic"
import { DraftRelease } from "./skills"
export command Release:
description: Draft release notes for a version.
argumentHint: "[version]"
use:
- DraftRelease
allowedTools:
- Bash
- Read
prompt:
Draft release notes for the version given as an argument.export abstract spec Command as command:
abstract description: string?
abstract prompt: string
abstract use: Skill[]?
abstract argumentHint: string?
abstract allowedTools: string[]?
abstract model: string?prompt
Unlike a skill or an agent, a command's generated artifact has no heading — the prompt is the entire body. Write it as an instruction, not as a document.
argumentHint — string?
The argument shape shown to whoever invokes it. Convention is a bracketed placeholder:
argumentHint: "[version]"Quote it. A bare [version] is an inline list, and the
compiler will say so:
error PSY4001: `Release.argumentHint` must be `string?` but resolved to a list.Which is the type system doing its job on what would otherwise have been a silent frontmatter bug that you found out about from a confused colleague.
allowedTools — string[]?
Comma-joined in the frontmatter. An empty list is omitted.
Note the name: a command uses allowedTools where an agent uses tools. They map
to different platform fields and are not interchangeable. I did not choose these
names and I would not have chosen these names.
model — string?
The model to run the command on. Falls back to the target's model option, and
overrides it when declared.
A shape that works
Skills are the reusable layer, agents are thin, and the shared instructions live in exactly one place:
// src/skills/ProjectContext.psy
export skill ProjectContext:
description: Read the project before changing it.
when: Use before making any edit.
prompt:
Read the README, the configuration, and the code nearest to the change
before making any edit. Follow the conventions already in the codebase
rather than importing new ones.// src/skills/ImplementChange.psy
export skill ImplementChange extends ProjectContext:
description: Implement a change end to end.
when: Use whenever a change alters behaviour.
prompt:
+ super
Add or update tests for every behavioural change.
Keep unrelated refactoring out of the change.// src/agents/BuildProject.psy
export agent BuildProject:
description: Implement changes in this project.
use:
- ImplementChange
- RunChecks
context:
- Editor
prompt:
Implement the requested change, then run the project's checks and say
plainly what passed and what did not.// src/agents/BuildFeature.psy
export agent BuildFeature extends BuildProject:
description: Implement a user-facing feature.
use:
+ super
- WriteInterfaceCopy
context:
+ super
- Voice
prompt:
+ super
Describe the change from the user's point of view before writing code..claude/agents/build-feature.md:
---
name: build-feature
description: Implement a user-facing feature.
---
# BuildFeature
Implement the requested change, then run the project's checks and say plainly
what passed and what did not.
Describe the change from the user's point of view before writing code.
## Skills
Use the following Psy-defined skills when carrying out this work:
- `implement-change` (ImplementChange)
- `run-checks` (RunChecks)
- `write-interface-copy` (WriteInterfaceCopy)
## Context
Consult these definitions of how the product works:
- `editor` (Editor)
- `voice` (Voice)BuildFeature linearizes as BuildFeature, BuildProject, Agent — explicit
parents outrank the keyword's abstract spec. Its use and context are the
inherited lists plus its own, deduplicated. Its prompt is both paragraphs.
One base agent, one specialisation, and nothing written twice.
Next: Specifying Software