# Anatomy of a plugin

The files of a plugin package, what each one does, and the naming rules for ids, classes and the entry point.

A vault plugin is a small Swift package. This page explains each file that `loam plugin new` creates, the fields of `plugin.json`, and the names the build expects. The examples use the `word-count` plugin from the quickstart.

## The package

```text
.loam/plugins/word-count/
  plugin.json                                       id, name, version, library, permissions
  Package.swift                                     the package; links LoamRuntime only
  Sources/WordCount/WordCountPlugin.swift           the plugin class and the entry point
  Tests/WordCountTests/WordCountPluginTests.swift   one test
  README.md                                         a short README to fill in
  .gitignore                                        ignores .build/ and .swiftpm/
```

Loam compiles the package into `.loam/cache/plugins/word-count/`, outside the plugin folder. A logic plugin uses Swift tools 5.10 and targets macOS 13.

Add `--ui` to `plugin new` for a plugin with a window part. Its `Package.swift` targets Swift tools 6.2 and macOS 26, and it also links `LoamUIRuntime`. See [Interface plugins](/docs/plugins/ui).

Link only the runtime libraries. Do not add other products of the Loam SDK: `loam plugin build` refuses a library that uses other parts of Loam.

## plugin.json

The manifest tells Loam what the plugin is before it loads any code. The generated file looks like this:

**plugin.json**

```json
{
  "id": "word-count",
  "name": "Word Count",
  "version": "0.1.0",
  "description": "",
  "icon": "puzzlepiece.extension",
  "library": "WordCountPlugin",
  "permissions": ["vault.read"]
}
```

- `id`: kebab-case, starting with a letter. It must equal the folder name.
- `name` and `version`: the display name and the version.
- `library`: the name of the library product. The generated value is `<Name>Plugin`.
- `description`: one line, shown under the name in Settings > Plugins.
- `icon`: an SF Symbol name. `iconFile` can name an image in the plugin folder instead.
- `permissions`: what the plugin does, such as `vault.read`. You see them before you enable the plugin. See [Permissions](/docs/permissions).

## The plugin class

The class conforms to `Plugin`. It declares its manifest, has an empty `init()`, and registers its features in `activate(context:)`.

**Sources/WordCount/WordCountPlugin.swift**

```swift
import PluginKit

public final class WordCountPlugin: Plugin {
    public static var manifest: PluginManifest {
        PluginManifest(id: "word-count", name: "Word Count", version: "0.1.0")
    }

    public required init() {}

    public func activate(context: PluginContext) throws {
        // Register commands, services and listeners here.
    }
}
```

Name the class `<PascalCase id>Plugin`. The id `word-count` gives the module `WordCount` and the class `WordCountPlugin`.

Everything you register through `context` is removed when the plugin turns off. Keep no global state, and do no cleanup by hand.

## The entry point

The last function of the file is the entry point. Loam calls it to get the plugin classes, and it records the SDK version the plugin was built against. The generated function is:

```swift
@_cdecl("loam_plugin_entry")
public func loamPluginEntry() -> UnsafeMutableRawPointer {
    Unmanaged.passRetained(PluginEntry(plugins: [WordCountPlugin.self])).toOpaque()
}
```

Keep it. If the package has more plugin classes, add each one to its list.

## Ids, imports and versions

- Every plugin class in the entry must use the package id, or the id followed by a dot: `word-count` or `word-count.extra`.
- Name commands, views and panels `<id>.<verb>`. Ids are shared by every plugin in the app.
- Import only `PluginKit`, `PluginKitUI`, `SwiftUI` and `Foundation`.
- On macOS, a plugin built once keeps working after app updates that only add to the SDK. Rebuild it when the SDK major version changes.
- Switch over an SDK enum with `@unknown default`, because a later release may add cases.

> **Tip:** For AI agents: `loam guide plugins` describes the same package, and the checks that `loam plugin build` runs.
