Morphic Agent Framework

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 byDeclares whenDeclares tools as
skillthe platform, when relevantrequired
agenta caller delegating worktools
commanda person, by nameallowedTools

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[]?

descriptionstring?

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.

whenstring, 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.

promptstring, 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 +.

useSkill[]?

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
- RunChecks

use 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.

contextDefinition[]?

The definitions this declaration needs — the parts of the product it touches.

    context:
        - Rendering
        - LayerStack

use 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: ReviewDiffreview-diff, ImplementCLIimplement-cli, ReadPsySpecread-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.

descriptionstring?

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.

modelstring?

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.

toolsstring[]?

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
        - WebSearch
tools: Read, Edit, Bash, WebSearch

use 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.

argumentHintstring?

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.

allowedToolsstring[]?

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.

modelstring?

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

On this page