Spirit Agent
Descargar

Extensiones

Estructura del paquete, campos de spiritExtension, API de activación y listado en el registro oficial.

Una extensión es una carpeta con un package.json que contiene spiritExtension. Spirit la instala por usuario y por host, no bajo el directorio .spirit/ del espacio de trabajo.

HostRuta
Desktop{spiritDataDir}/extensions/desktop/
CLI{spiritDataDir}/extensions/cli/

Esto es diferente de Skills (carpetas SKILL.md), MCP (servidores externos) y Hooks (scripts de hooks.json). contributes.cli.hooks estiliza los slots de la TUI de CLI. No es hooks.json.

No hay un interruptor de habilitación por extensión. Después de la instalación, la extensión contribuye. El interruptor del panel de CLI es un marcador de posición.

Estructura del paquete

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
      }
    ]
  }
}

El name en package.json es el id de la extensión. Debe coincidir con ^(?:@[a-z0-9][a-z0-9._-]*/)?[a-z0-9][a-z0-9._-]*$.

Campos de package.json

CampoDescripción
nameRequerido. Nombre del paquete npm; se usa como id de la extensión
versionRequerido. Cadena de versión
spiritExtensionRequerido. Objeto. Ver abajo
descriptionOpcional. Cadena
authorOpcional. Cadena o { name }
homepageOpcional. Cadena
mainOpcional. Ruta relativa al punto de entrada de activate. Se requiere cuando la extensión usa activationEvents, ejecución de herramientas o un prompt del sistema

main, spiritExtension.icon, las rutas CSS y las rutas de hooks CLI deben ser relativas y deben permanecer dentro del paquete.

spiritExtension

CampoDescripción
schemaVersionOpcional. Por defecto es 1. Solo se soporta 1
displayNameRequerido. Nombre visible para el usuario
iconOpcional. Ruta relativa a la raíz del paquete
supportedHostsRequerido. Arreglo no vacío de cli y/o desktop
activationEventsOpcional. Ver Eventos de activación
requestedCapabilitiesOpcional. Ver Capacidades
contributesOpcional. tools, desktop, y/o cli
settingsSchemaOpcional. Definiciones de ajustes
secretSlotsOpcional. Definiciones de ranuras de secretos

Capacidades

ValorTiempo de ejecución
tool-definitionsRequerido con tool-execution para exponer contributes.tools al modelo
tool-executionRequerido con tool-definitions para ejecutar esas herramientas
system-promptRequerido con main para contribuir con un fragmento de prompt del sistema
desktop-uiDebe estar emparejado con contributes.desktop
cli-uiDebe estar emparejado con contributes.cli
approval-flowSolo declarado. La aprobación se rige por el approvalMode de cada herramienta
questions-flowSolo declarado. Las preguntas se rigen por approvalMode: need-questions
settingsSolo declarado. La configuración proviene de settingsSchema
secret-storageSe usa con secretSlots
structured-resultsSolo declarado

desktop-ui / cli-ui y el bloque contributes correspondiente deben estar ambos presentes o ambos ausentes.

Eventos de activación

EventoCuándo se dispara
onStartupCalentamiento del host
onExtensionInstalledDespués de instalar desde ZIP o marketplace
onSessionOpenedLa sesión se vuelve activa
onSessionResetReinicio de sesión
onUserMessageEl usuario envía un mensaje
onToolCallUna herramienta está a punto de ejecutarse
onToolResultUn resultado de herramienta está disponible
onApprovalResolvedUna decisión de aprobación se resuelve

El host llama a activate cuando la extensión lista el evento y tiene un main legible.

contributes.tools

CampoDescripción
nameRequerido. [a-z0-9]+(?:[._-][a-z0-9]+)*
descriptionRequerido. Se muestra al modelo
inputSchemaRequerido. Objeto JSON Schema
outputSchemaOpcional. Objeto JSON Schema
approvalModeOpcional. allowed, need-approval o need-questions
executionModeOpcional. foreground o background

El modelo no ve name tal cual. El host construye un nombre de invocación como extension__{id}__{tool}__{hash}.

contributes.desktop

Requiere desktop-ui.

CampoDescripción
css[].pathRequerido. Archivo CSS relativo a la raíz del paquete
css[].mediaOpcional. Media query CSS
settingsPageOpcional. true, {} o { title } — agrega una entrada de configuración de Desktop

contributes.cli

Requiere cli-ui. hooks es un objeto con path, no un array en línea:

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

El archivo debe ser { "hooks": [ ... ] }.

CampoDescripción
slotRequerido. Uno de los slots a continuación
variantOpcional. default, accented, muted, warning, success, danger
tokensOpcional. { foreground?, border?, accent? }
prefixOpcional. Cadena
suffixOpcional. Cadena

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

Roles de token: default, primary, secondary, muted, accent, success, warning, danger.

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

settingsSchema

CampoDescripción
keyRequerido. Mismo patrón que los nombres de herramientas
typeRequerido. string, boolean, number, o select
titleRequerido. Etiqueta de interfaz
descriptionOpcional
placeholderOpcional
requiredOpcional. Booleano
defaultValueOpcional. Debe coincidir con type
optionsRequerido para select. Arreglo de { value, label, description? }

Los valores son string, number, boolean, o null. null limpia un ajuste no requerido.

secretSlots

CampoDescripción
keyRequerido
titleRequerido
descriptionOpcional
requiredOpcional. Booleano

Desktop almacena secretos en el llavero del sistema. En la ruta del daemon de CLI, secrets.set / secrets.delete pueden no estar disponibles.

activate

main se carga con import dinámico. Exporta uno de:

  • export function activate(ctx) { ... }
  • export default function activate(ctx) { ... }
  • export default { activate }

ctx

CampoDescripción
extension{ id, name, version, directoryPath, manifestPath, main }
hostAPI del host. Desktop implementa showMessageBox. CLI es {}
log(message: string) => void
settingsget(key), getAll(), set(key, value), setAll(values)
secretsget(key), has(key), set(key, value), delete(key) — las claves deben declararse en secretSlots
activationEventOpcional. { type, detail? }

Valor de retorno

Retorna un objeto, o exporta los mismos campos desde el módulo.

CampoDescripción
toolsRecord<string, (ctx) => unknown>. Las claves son los nombres de las herramientas del manifiesto
invokeTool(ctx) => unknown. Se usa en lugar de tools[name] cuando está presente
systemPromptFragmento estático del sistema
getSystemPrompt() => string | Promise<string>. Se usa en lugar de systemPrompt cuando está presente
onEvent(event) => void
dispose() => void. Se llama al eliminar o recargar

ctx del manejador de herramientas

CampoDescripción
extensionLa misma información de ejecución que activate
hostLa misma API de host
toolNameNombre de la herramienta del manifiesto
argumentsObjeto del modelo
logEl mismo registrador
settingsEl mismo acceso a configuración
secretsEl mismo acceso a secretos
toolCallIdOpcional
questionsResultOpcional. Se establece cuando approvalMode es need-questions

Un resultado string se pasa directamente. Otros valores se convierten con JSON.stringify(value, null, 2). undefined se convierte en "".

Escritorio host.showMessageBox

CampoDescripción
titleRequerido. Cadena
messageRequerido. Cadena
detailOpcional. Cadena
buttonsOpcional. Arreglo de cadenas
cancelIdOpcional. Número
defaultIdOpcional. Número
noLinkOpcional. Booleano
typeOpcional. 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");
    },
  };
}

Instalación

  • ZIP — el archivo debe contener exactamente un package.json (puede estar en un subdirectorio). Importa con spirit extension import ./extension.zip o Configuración → Extensiones.
  • Paquete de marketplace — la raíz del archivo debe contener un directorio package/.

supportedHosts debe incluir el host actual. Una segunda instalación del mismo id no reemplaza la copia existente por defecto. Consulta Marketplace para los canales y la interfaz de instalación.

Publicar en el registro oficial

El repositorio SpiritAgents/registry es un índice de marketplace. No aloja el código fuente de extensiones, dist, archivos ZIP ni paquetes.

Realiza estos pasos en orden:

  1. Publica un paquete npm público.
  2. Abre una pull request que liste ese paquete.

1. Publica el paquete npm

El package.json publicado es la fuente de verdad, especialmente spiritExtension. El constructor del registro requiere:

  • spiritExtension.schemaVersion
  • spiritExtension.displayName
  • spiritExtension.supportedHosts
  • spiritExtension.requestedCapabilities

También lee name, version, description, author, repository, homepage, keywords y el opcional spiritExtension.icon. Si estableces icon, incluye ese archivo en el paquete publicado. Un objeto spiritExtension faltante hace que la compilación del registro falle para esa versión.

Ejemplo oficial: @spiritagent/extension-system-message-demo (spiritagent.system-message-demo).

2. Lista el paquete

Crea registry/extensions/<extension-id>/ con:

ArchivoDescripción
entry.jsonGobernanza del marketplace
README.mdCopia de detalle del marketplace

No edites estos archivos manualmente (regéralos):

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

extensionId necesita al menos dos segmentos separados por puntos, letras minúsculas y dígitos, con puntos o guiones dentro de un segmento. Ejemplos: spiritagent.system-message-demo, yourteam.some-extension. packageName y extensionId deben ser únicos en el repositorio.

entry.json

CampoDescripción
schemaVersionRequerido. 1
extensionIdRequerido. Ver las reglas anteriores
packageNameRequerido. Nombre del paquete npm
statusRequerido. listed, hidden, deprecated o blocked
featuredRequerido. Booleano
defaultVersionRequerido. Cadena de versión predeterminada
defaultReviewStatusRequerido. unverified, verified o revoked
versionsRequerido. Al menos un elemento

Cada elemento de versions[]:

CampoDescripción
versionRequerido
channelRequerido. stable, preview o experimental
reviewStatusRequerido. unverified, verified o revoked
changelogOpcional. { summary, body } — ambos requeridos cuando esté presente
{
  "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."
      }
    }
  ]
}

El README del marketplace debe cubrir qué hace la extensión, el nombre del paquete, la versión aprobada predeterminada, la compatibilidad de host y capacidades, y notas para los lectores del marketplace.

Regenera los archivos derivados localmente:

./scripts/build-registry.ps1

El script obtiene los metadatos de npm para las versiones enumeradas, reconstruye catalog.json y cada detail.json, y luego valida la consistencia.

Pull request

Incluye entry.json, el README.md del marketplace, y los catalog.json / detail.json regenerados. Enumera solo versiones que ya sean públicas en npm. Establece reviewStatus y channel con precisión. Las nuevas versiones típicamente comienzan como unverified.

No confirmes árboles de código fuente de extensiones, dist, archivos ZIP, tarballs u otros artefactos binarios.

Abrir una pull request no garantiza la publicación. La revisión puede solicitar cambios antes de que una versión sea aprobada o verificada.