# Plugin commands

How a plugin registers a command, what its arguments and result look like, and where the CLI and agents find it.

A command is an action a plugin registers. The palette, the `loam` command line and AI agents all run it the same way. This page shows how to define a command, how arguments and results work, and where the command shows up.

## Register a command

Call `context.registerCommand` in `activate(context:)`. The handler receives the arguments as text, and returns text, usually JSON:

```swift
try context.registerCommand(Command(
    id: "word-count.hello",
    title: "Word Count: say hello",
    summary: "Says hello and how many notes the vault has. No arguments; optional: name."
) { arguments in
    let notes = try vault.markdownPaths().count
    return "Hello \(arguments["name"] ?? "from Word Count"). Notes in this vault: \(notes)."
})
```

- `id`: `<plugin id>.<verb>`. A second command with the same id is refused.
- `title`: the name shown in the command palette.
- `summary`: what the command does, for agents. Name its arguments, and say whether it writes.
- `shortcut`: an optional default key, such as `KeyShortcut("up", [.command])`. The user can change it in `.loam/config.json`.
- `isInPalette`: set it to `false` for a command that needs arguments, or that returns data the palette cannot show. The CLI and agents still run it.

## Arguments and results

On the command line, arguments are `key=value`. For example, `loam word-count.hello name=Ada` gives the handler `["name": "Ada"]`.

- Read a required argument with `arguments.require("title")`. If it is missing, the command fails with `missing argument "title"`.
- Return text. For structured data, return JSON with `JSON.string(value)`, which prints pretty output with sorted keys.
- A successful command prints its result on stdout and exits with status 0.
- A failed command prints a JSON object with an `error` key on stderr, and exits with status 1.

## Where commands appear

- The command palette, unless `isInPalette` is `false`.
- `loam list`, which prints every command as JSON, with its id, title and summary.
- AI agents, which read the same list and the `summary` of each command.
- The open window: `loam app run <id>` runs a command inside the app, UI commands included.

The commands of a plugin that is off are not listed. `loam plugin list` shows which plugins are on.

> **Tip:** For AI agents: `loam list` returns every command as JSON. `loam guide commands` describes the commands of the app itself.
