# Erweiterungen
URL: /de/docs/develop/extensions

Paketstruktur, spiritExtension-Felder, Aktivierungs-API und Auflistung im offiziellen Registry.



Eine Erweiterung ist ein Ordner mit einer `package.json`, die das Feld `spiritExtension` enthält. Spirit installiert sie pro Benutzer und pro Host, nicht im `.spirit/`-Verzeichnis des Workspaces.

| Host    | Pfad                                  |
| ------- | ------------------------------------- |
| Desktop | `{spiritDataDir}/extensions/desktop/` |
| CLI     | `{spiritDataDir}/extensions/cli/`     |

Das unterscheidet sich von [Skills](../customize/skills.mdx) (`SKILL.md`-Ordner), [MCP](../customize/mcp.mdx) (externe Server) und [Hooks](../customize/hooks.mdx) (`hooks.json`-Skripte). `contributes.cli.hooks` gestaltet die CLI-TUI-Slots. Das ist nicht `hooks.json`.

Es gibt keinen pro Erweiterung deaktivierbaren Schalter. Nach der Installation trägt die Erweiterung bei. Der Schalter im CLI-Panel ist ein Platzhalter.

## Paketstruktur [#paketstruktur]

```text
example-extension/
  package.json
  dist/index.js
  assets/icon.svg
  styles/desktop.css
  cli-hooks.json
```

```json
{
  "name": "@example/spirit-extension",
  "version": "0.1.0",
  "description": "Example Spirit Agent extension.",
  "author": { "name": "example" },
  "homepage": "https://example.com/spirit-extension",
  "main": "dist/index.js",
  "spiritExtension": {
    "schemaVersion": 1,
    "displayName": "Example extension",
    "icon": "assets/icon.svg",
    "supportedHosts": ["cli", "desktop"],
    "activationEvents": ["onStartup", "onUserMessage"],
    "requestedCapabilities": [
      "tool-definitions",
      "tool-execution",
      "system-prompt",
      "settings",
      "secret-storage",
      "desktop-ui",
      "cli-ui"
    ],
    "contributes": {
      "tools": [
        {
          "name": "lookup_item",
          "description": "Look up an item by id.",
          "inputSchema": {
            "type": "object",
            "properties": {
              "id": { "type": "string" }
            },
            "required": ["id"]
          },
          "approvalMode": "allowed",
          "executionMode": "foreground"
        }
      ],
      "desktop": {
        "css": [{ "path": "styles/desktop.css" }],
        "settingsPage": { "title": "Example extension" }
      },
      "cli": {
        "hooks": { "path": "cli-hooks.json" }
      }
    },
    "settingsSchema": [
      {
        "key": "region",
        "type": "select",
        "title": "Region",
        "required": true,
        "defaultValue": "us",
        "options": [
          { "value": "us", "label": "US" },
          { "value": "eu", "label": "EU" }
        ]
      }
    ],
    "secretSlots": [
      {
        "key": "api_token",
        "title": "API token",
        "required": true
      }
    ]
  }
}
```

Der `name` in der `package.json` ist die Erweiterungs-ID. Er muss dem Muster `^(?:@[a-z0-9][a-z0-9._-]*/)?[a-z0-9][a-z0-9._-]*$` entsprechen.

## Felder der `package.json` [#felder-der-packagejson]

| Feld              | Beschreibung                                                                                                                                                        |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`            | Erforderlich. npm-Paketname; wird als Erweiterungs-ID verwendet                                                                                                     |
| `version`         | Erforderlich. Versionszeichenfolge                                                                                                                                  |
| `spiritExtension` | Erforderlich. Objekt. Siehe unten                                                                                                                                   |
| `description`     | Optional. Zeichenfolge                                                                                                                                              |
| `author`          | Optional. Zeichenfolge oder `{ name }`                                                                                                                              |
| `homepage`        | Optional. Zeichenfolge                                                                                                                                              |
| `main`            | Optional. Relativer Pfad zum `activate`-Einstiegspunkt. Erforderlich, wenn die Erweiterung `activationEvents`, Werkzeugausführung oder einen Systemprompt verwendet |

`main`, `spiritExtension.icon`, CSS-Pfade und CLI-Hook-Pfade müssen relativ sein und innerhalb des Pakets bleiben.

## `spiritExtension` [#spiritextension]

| Feld                    | Beschreibung                                                  |
| ----------------------- | ------------------------------------------------------------- |
| `schemaVersion`         | Optional. Standardwert: `1`. Nur `1` wird unterstützt         |
| `displayName`           | Erforderlich. Sichtbarer Name                                 |
| `icon`                  | Optional. Pfad relativ zum Paketstamm                         |
| `supportedHosts`        | Erforderlich. Nicht leeres Array mit `cli` und/oder `desktop` |
| `activationEvents`      | Optional. Siehe [Aktivierungsereignisse](#activation-events)  |
| `requestedCapabilities` | Optional. Siehe [Fähigkeiten](#capabilities)                  |
| `contributes`           | Optional. `tools`, `desktop` und/oder `cli`                   |
| `settingsSchema`        | Optional. Definitionen von Einstellungen                      |
| `secretSlots`           | Optional. Definitionen von Geheimnis-Slots                    |

## Fähigkeiten [#fähigkeiten]

| Wert                 | Laufzeit                                                                                 |
| -------------------- | ---------------------------------------------------------------------------------------- |
| `tool-definitions`   | Erforderlich mit `tool-execution`, um `contributes.tools` für das Modell bereitzustellen |
| `tool-execution`     | Erforderlich mit `tool-definitions`, um diese Werkzeuge auszuführen                      |
| `system-prompt`      | Erforderlich mit `main`, um einen Systemprompt-Fragment beizutragen                      |
| `desktop-ui`         | Muss mit `contributes.desktop` gepaart sein                                              |
| `cli-ui`             | Muss mit `contributes.cli` gepaart sein                                                  |
| `approval-flow`      | Nur deklariert. Die Genehmigung wird durch den `approvalMode` jedes Tools gesteuert      |
| `questions-flow`     | Nur deklariert. Fragen werden durch `approvalMode: need-questions` gesteuert             |
| `settings`           | Nur deklariert. Einstellungen kommen aus `settingsSchema`                                |
| `secret-storage`     | Wird mit `secretSlots` verwendet                                                         |
| `structured-results` | Nur deklariert                                                                           |

`desktop-ui` / `cli-ui` und der passende `contributes`-Block müssen entweder beide vorhanden oder beide nicht vorhanden sein.

## Aktivierungsereignisse [#aktivierungsereignisse]

| Ereignis               | Wann es ausgelöst wird                      |
| ---------------------- | ------------------------------------------- |
| `onStartup`            | Host-Aufwärmphase                           |
| `onExtensionInstalled` | Nach ZIP- oder Marketplace-Installation     |
| `onSessionOpened`      | Sitzung wird aktiv                          |
| `onSessionReset`       | Sitzung wird zurückgesetzt                  |
| `onUserMessage`        | Benutzer sendet eine Nachricht              |
| `onToolCall`           | Ein Tool wird gleich ausgeführt             |
| `onToolResult`         | Ein Tool-Ergebnis ist verfügbar             |
| `onApprovalResolved`   | Eine Genehmigungsentscheidung ist aufgelöst |

Der Host ruft `activate` auf, wenn die Erweiterung das Ereignis auflistet und eine lesbare `main` hat.

## `contributes.tools` [#contributestools]

| Feld            | Beschreibung                                               |
| --------------- | ---------------------------------------------------------- |
| `name`          | Erforderlich. `[a-z0-9]+(?:[._-][a-z0-9]+)*`               |
| `description`   | Erforderlich. Wird dem Modell angezeigt                    |
| `inputSchema`   | Erforderlich. JSON-Schema-Objekt                           |
| `outputSchema`  | Optional. JSON-Schema-Objekt                               |
| `approvalMode`  | Optional. `allowed`, `need-approval` oder `need-questions` |
| `executionMode` | Optional. `foreground` oder `background`                   |

Das Modell sieht `name` nicht unverändert. Der Host erstellt einen Aufrufnamen wie `extension__{id}__{tool}__{hash}`.

## `contributes.desktop` [#contributesdesktop]

Erfordert `desktop-ui`.

| Feld           | Beschreibung                                                                           |
| -------------- | -------------------------------------------------------------------------------------- |
| `css[].path`   | Erforderlich. CSS-Datei relativ zum Paketstamm                                         |
| `css[].media`  | Optional. CSS-Medienabfrage                                                            |
| `settingsPage` | Optional. `true`, `{}` oder `{ title }` – fügt einen Desktop-Einstellungseintrag hinzu |

## `contributes.cli` [#contributescli]

Erfordert `cli-ui`. `hooks` ist ein Objekt mit `path`, kein Inline-Array:

```json
{
  "hooks": { "path": "cli-hooks.json" }
}
```

Die Datei muss `{ "hooks": [ ... ] }` sein.

| Feld      | Beschreibung                                                             |
| --------- | ------------------------------------------------------------------------ |
| `slot`    | Erforderlich. Einer der folgenden Slots                                  |
| `variant` | Optional. `default`, `accented`, `muted`, `warning`, `success`, `danger` |
| `tokens`  | Optional. `{ foreground?, border?, accent? }`                            |
| `prefix`  | Optional. String                                                         |
| `suffix`  | Optional. String                                                         |

Slots: `message.user`, `message.assistant`, `message.tool`, `assistant.thinking`, `input.frame`, `bottom_form`, `bottom_form.section`, `slash_suggestions`, `approval.panel`, `questions.panel`.

Token-Rollen: `default`, `primary`, `secondary`, `muted`, `accent`, `success`, `warning`, `danger`.

```json
{
  "hooks": [
    {
      "slot": "input.frame",
      "variant": "accented",
      "tokens": { "border": "accent" },
      "prefix": "[",
      "suffix": "]"
    }
  ]
}
```

## `settingsSchema` [#settingsschema]

| Feld           | Beschreibung                                                          |
| -------------- | --------------------------------------------------------------------- |
| `key`          | Erforderlich. Gleiches Muster wie Tool-Namen                          |
| `type`         | Erforderlich. `string`, `boolean`, `number` oder `select`             |
| `title`        | Erforderlich. UI-Label                                                |
| `description`  | Optional                                                              |
| `placeholder`  | Optional                                                              |
| `required`     | Optional. Boolean                                                     |
| `defaultValue` | Optional. Muss mit `type` übereinstimmen                              |
| `options`      | Erforderlich für `select`. Array von `{ value, label, description? }` |

Werte sind `string`, `number`, `boolean` oder `null`. `null` löscht eine nicht erforderliche Einstellung.

## `secretSlots` [#secretslots]

| Feld          | Beschreibung      |
| ------------- | ----------------- |
| `key`         | Erforderlich      |
| `title`       | Erforderlich      |
| `description` | Optional          |
| `required`    | Optional. Boolean |

Desktop speichert Geheimnisse im OS-Keyring. Beim CLI-Daemon-Pfad sind `secrets.set` / `secrets.delete` möglicherweise nicht verfügbar.

## `activate` [#activate]

`main` wird mit dynamischem `import` geladen. Exportieren Sie eines von:

* `export function activate(ctx) { ... }`
* `export default function activate(ctx) { ... }`
* `export default { activate }`

### `ctx` [#ctx]

| Feld              | Beschreibung                                                                                                 |
| ----------------- | ------------------------------------------------------------------------------------------------------------ |
| `extension`       | `{ id, name, version, directoryPath, manifestPath, main }`                                                   |
| `host`            | Host-API. Desktop implementiert `showMessageBox`. CLI ist `{}`                                               |
| `log`             | `(message: string) => void`                                                                                  |
| `settings`        | `get(key)`, `getAll()`, `set(key, value)`, `setAll(values)`                                                  |
| `secrets`         | `get(key)`, `has(key)`, `set(key, value)`, `delete(key)` — Schlüssel müssen in `secretSlots` deklariert sein |
| `activationEvent` | Optional. `{ type, detail? }`                                                                                |

### Rückgabewert [#rückgabewert]

Geben Sie ein Objekt zurück oder exportieren Sie dieselben Felder aus dem Modul.

| Feld              | Beschreibung                                                                                  |
| ----------------- | --------------------------------------------------------------------------------------------- |
| `tools`           | `Record<string, (ctx) => unknown>`. Schlüssel sind Manifest-Toolnamen                         |
| `invokeTool`      | `(ctx) => unknown`. Wird anstelle von `tools[name]` verwendet, wenn vorhanden                 |
| `systemPrompt`    | Statisches Systemfragment                                                                     |
| `getSystemPrompt` | `() => string \| Promise<string>`. Wird anstelle von `systemPrompt` verwendet, wenn vorhanden |
| `onEvent`         | `(event) => void`                                                                             |
| `dispose`         | `() => void`. Wird beim Entfernen oder Neuladen aufgerufen                                    |

### Tool-Handler `ctx` [#tool-handler-ctx]

| Feld              | Beschreibung                                                   |
| ----------------- | -------------------------------------------------------------- |
| `extension`       | Gleiche Laufzeitinformationen wie `activate`                   |
| `host`            | Gleiche Host-API                                               |
| `toolName`        | Manifest-Toolname                                              |
| `arguments`       | Objekt vom Modell                                              |
| `log`             | Gleicher Logger                                                |
| `settings`        | Gleicher Settings-Zugriff                                      |
| `secrets`         | Gleicher Secrets-Zugriff                                       |
| `toolCallId`      | Optional                                                       |
| `questionsResult` | Optional. Festgelegt, wenn `approvalMode` `need-questions` ist |

Ein `string`-Ergebnis wird durchgereicht. Andere Werte werden `JSON.stringify(value, null, 2)`. `undefined` wird zu `""`.

### Desktop `host.showMessageBox` [#desktop-hostshowmessagebox]

| Feld        | Beschreibung                                             |
| ----------- | -------------------------------------------------------- |
| `title`     | Erforderlich. Zeichenkette                               |
| `message`   | Erforderlich. Zeichenkette                               |
| `detail`    | Optional. Zeichenkette                                   |
| `buttons`   | Optional. Zeichenkettenarray                             |
| `cancelId`  | Optional. Zahl                                           |
| `defaultId` | Optional. Zahl                                           |
| `noLink`    | Optional. Boolesch                                       |
| `type`      | Optional. `none`, `info`, `error`, `question`, `warning` |

```js
export function activate(ctx) {
  return {
    systemPrompt: "Use lookup_item when the user asks for an item by id.",
    async invokeTool({ toolName, arguments: args, settings, secrets }) {
      if (toolName !== "lookup_item") {
        throw new Error(`Unknown tool: ${toolName}`);
      }
      const region = await settings.get("region");
      const token = await secrets.get("api_token");
      return { id: args.id, region, hasToken: Boolean(token) };
    },
    dispose() {
      ctx.log("example-extension disposed");
    },
  };
}
```

## Installation [#installation]

* **ZIP** — Das Archiv muss genau eine `package.json` enthalten (sie darf in einem Unterverzeichnis liegen). Importieren Sie mit `spirit extension import ./extension.zip` oder über **Einstellungen → Erweiterungen**.
* **Marktplatz-Tarball** — Das Archivstammverzeichnis muss ein `package/`-Verzeichnis enthalten.

`supportedHosts` muss den aktuellen Host enthalten. Eine zweite Installation derselben ID ersetzt standardmäßig nicht die vorhandene Kopie. Siehe [Marktplatz](../customize/marketplace.mdx) für Kanäle und die Installationsoberfläche.

## Im offiziellen Registry veröffentlichen [#im-offiziellen-registry-veröffentlichen]

Das [SpiritAgents/registry](https://github.com/SpiritAgents/registry)-Repository ist ein Marktplatzindex. Es hostet keine Erweiterungsquellen, `dist`, ZIP-Dateien oder Tarballs.

Führen Sie diese Schritte in der angegebenen Reihenfolge aus:

1. Veröffentlichen Sie ein öffentliches npm-Paket.
2. Erstellen Sie einen Pull-Request, der dieses Paket auflistet.

### 1. Veröffentlichen Sie das npm-Paket [#1-veröffentlichen-sie-das-npm-paket]

Das veröffentlichte `package.json` ist die Quelle der Wahrheit, insbesondere `spiritExtension`. Der Registry-Builder erfordert:

* `spiritExtension.schemaVersion`
* `spiritExtension.displayName`
* `spiritExtension.supportedHosts`
* `spiritExtension.requestedCapabilities`

Es liest auch `name`, `version`, `description`, `author`, `repository`, `homepage`, `keywords` und optional `spiritExtension.icon`. Wenn Sie `icon` festlegen, fügen Sie diese Datei in das veröffentlichte Paket ein. Ein fehlendes `spiritExtension`-Objekt führt dazu, dass der Registry-Build für diese Version fehlschlägt.

Offizielles Beispiel: [`@spiritagent/extension-system-message-demo`](https://www.npmjs.com/package/@spiritagent/extension-system-message-demo) (`spiritagent.system-message-demo`).

### 2. Paket auflisten [#2-paket-auflisten]

Erstellen Sie `registry/extensions/<extension-id>/` mit:

| Datei        | Beschreibung           |
| ------------ | ---------------------- |
| `entry.json` | Marktplatz-Governance  |
| `README.md`  | Marktplatz-Detailtexte |

Bearbeiten Sie diese nicht von Hand (generieren Sie sie neu):

* `registry/catalog.json`
* `registry/extensions/<extension-id>/detail.json`

`extensionId` benötigt mindestens zwei durch Punkte getrennte Segmente, Kleinbuchstaben und Ziffern, mit Punkten oder Bindestrichen innerhalb eines Segments. Beispiele: `spiritagent.system-message-demo`, `yourteam.some-extension`. `packageName` und `extensionId` müssen im Repository eindeutig sein.

### `entry.json` [#entryjson]

| Feld                  | Beschreibung                                                  |
| --------------------- | ------------------------------------------------------------- |
| `schemaVersion`       | Erforderlich. `1`                                             |
| `extensionId`         | Erforderlich. Siehe die obigen Regeln                         |
| `packageName`         | Erforderlich. npm-Paketname                                   |
| `status`              | Erforderlich. `listed`, `hidden`, `deprecated` oder `blocked` |
| `featured`            | Erforderlich. Boolescher Wert                                 |
| `defaultVersion`      | Erforderlich. Standardversionszeichenfolge                    |
| `defaultReviewStatus` | Erforderlich. `unverified`, `verified` oder `revoked`         |
| `versions`            | Erforderlich. Mindestens ein Element                          |

Jedes `versions[]`-Element:

| Feld           | Beschreibung                                                       |
| -------------- | ------------------------------------------------------------------ |
| `version`      | Erforderlich                                                       |
| `channel`      | Erforderlich. `stable`, `preview` oder `experimental`              |
| `reviewStatus` | Erforderlich. `unverified`, `verified` oder `revoked`              |
| `changelog`    | Optional. `{ summary, body }` – beide erforderlich, wenn vorhanden |

```json
{
  "schemaVersion": 1,
  "extensionId": "example.spirit-extension",
  "packageName": "@example/spirit-extension",
  "status": "listed",
  "featured": false,
  "defaultVersion": "0.1.0",
  "defaultReviewStatus": "unverified",
  "versions": [
    {
      "version": "0.1.0",
      "channel": "stable",
      "reviewStatus": "unverified",
      "changelog": {
        "summary": "Initial public release.",
        "body": "- Initial public release."
      }
    }
  ]
}
```

Die Marktplatz-README sollte abdecken, was die Erweiterung tut, den Paketnamen, die standardmäßig genehmigte Version, die Kompatibilität von Host und Fähigkeiten sowie Hinweise für Marktplatz-Leser.

Generierte abgeleitete Dateien lokal neu erzeugen:

```powershell
./scripts/build-registry.ps1
```

Das Skript ruft die npm-Metadaten für die aufgeführten Versionen ab, baut `catalog.json` und jede `detail.json` neu auf und validiert dann die Konsistenz.

### Pull request [#pull-request]

Fügen Sie `entry.json`, die Marktplatz-`README.md` sowie das neu generierte `catalog.json` / `detail.json` hinzu. Listen Sie nur Versionen auf, die bereits öffentlich auf npm verfügbar sind. Setzen Sie `reviewStatus` und `channel` genau. Neue Versionen starten typischerweise als `unverified`.

Committen Sie keine Extension-Source-Trees, `dist`, ZIP-Dateien, Tarballs oder andere Binärartefakte.

Das Eröffnen eines Pull Requests garantiert keine Auflistung. Die Überprüfung kann Änderungen anfordern, bevor eine Version genehmigt oder verifiziert wird.
