Erweiterungen
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 (SKILL.md-Ordner), MCP (externe Server) und Hooks (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
example-extension/
package.json
dist/index.js
assets/icon.svg
styles/desktop.css
cli-hooks.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
| 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
| 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 |
requestedCapabilities | Optional. Siehe Fähigkeiten |
contributes | Optional. tools, desktop und/oder cli |
settingsSchema | Optional. Definitionen von Einstellungen |
secretSlots | Optional. Definitionen von Geheimnis-Slots |
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
| 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
| 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
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
Erfordert cli-ui. hooks ist ein Objekt mit path, kein Inline-Array:
{
"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.
{
"hooks": [
{
"slot": "input.frame",
"variant": "accented",
"tokens": { "border": "accent" },
"prefix": "[",
"suffix": "]"
}
]
}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
| 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
main wird mit dynamischem import geladen. Exportieren Sie eines von:
export function activate(ctx) { ... }export default function activate(ctx) { ... }export default { activate }
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
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
| 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
| 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 |
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
- ZIP — Das Archiv muss genau eine
package.jsonenthalten (sie darf in einem Unterverzeichnis liegen). Importieren Sie mitspirit extension import ./extension.zipoder ü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 für Kanäle und die Installationsoberfläche.
Im offiziellen Registry veröffentlichen
Das 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:
- Veröffentlichen Sie ein öffentliches npm-Paket.
- Erstellen Sie einen Pull-Request, der dieses Paket auflistet.
1. Veröffentlichen Sie das npm-Paket
Das veröffentlichte package.json ist die Quelle der Wahrheit, insbesondere spiritExtension. Der Registry-Builder erfordert:
spiritExtension.schemaVersionspiritExtension.displayNamespiritExtension.supportedHostsspiritExtension.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 (spiritagent.system-message-demo).
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.jsonregistry/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
| 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 |
{
"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:
./scripts/build-registry.ps1Das 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
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.