Spirit Agent
Скачать

Расширения

Структура пакета, поля spiritExtension, API активации и список в официальном реестре.

Расширение — это папка с package.json, содержащая spiritExtension. Spirit устанавливает его для каждого пользователя и хоста, а не в каталог .spirit/ рабочей области.

ХостПуть
Desktop{spiritDataDir}/extensions/desktop/
CLI{spiritDataDir}/extensions/cli/

Это отличается от Skills (папки SKILL.md), MCP (внешние серверы) и Hooks (скрипты hooks.json). contributes.cli.hooks оформляет слоты CLI TUI. Это не hooks.json.

Нет отдельного переключателя включения для каждого расширения. После установки расширение вносит вклад. Переключатель на панели CLI — это заглушка.

Структура пакета

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

name в package.json — это идентификатор расширения. Он должен соответствовать ^(?:@[a-z0-9][a-z0-9._-]*/)?[a-z0-9][a-z0-9._-]*$.

Поля package.json

ПолеОписание
nameОбязательное. Имя npm-пакета; используется как идентификатор расширения
versionОбязательное. Строка версии
spiritExtensionОбязательное. Объект. См. ниже
descriptionНеобязательное. Строка
authorНеобязательное. Строка или { name }
homepageНеобязательное. Строка
mainНеобязательное. Относительный путь к точке входа activate. Требуется, если расширение использует activationEvents, выполнение инструментов или системный промпт

main, spiritExtension.icon, пути CSS и пути хуков CLI должны быть относительными и оставаться внутри пакета.

spiritExtension

ПолеОписание
schemaVersionНеобязательное. По умолчанию 1. Поддерживается только 1
displayNameОбязательное. Имя, видимое пользователю
iconНеобязательное. Путь относительно корня пакета
supportedHostsОбязательное. Непустой массив из cli и/или desktop
activationEventsНеобязательное. См. События активации
requestedCapabilitiesНеобязательное. См. Возможности
contributesНеобязательное. tools, desktop и/или cli
settingsSchemaНеобязательное. Определения настроек
secretSlotsНеобязательное. Определения слотов секретов

Возможности

ЗначениеСреда выполнения
tool-definitionsТребуется с tool-execution для предоставления contributes.tools модели
tool-executionТребуется с tool-definitions для запуска этих инструментов
system-promptТребуется с main для добавления фрагмента системного промпта
desktop-uiДолжен использоваться вместе с contributes.desktop
cli-uiДолжен использоваться вместе с contributes.cli
approval-flowТолько объявлено. Одобрение определяется параметром approvalMode каждого инструмента
questions-flowТолько объявлено. Вопросы определяются параметром approvalMode: need-questions
settingsТолько объявлено. Настройки берутся из settingsSchema
secret-storageИспользуется с secretSlots
structured-resultsТолько объявлено

desktop-ui / cli-ui и соответствующий блок contributes должны либо присутствовать оба, либо отсутствовать оба.

События активации

СобытиеКогда срабатывает
onStartupПрогрев хоста
onExtensionInstalledПосле установки из ZIP или маркетплейса
onSessionOpenedСеанс становится активным
onSessionResetСброс сеанса
onUserMessageПользователь отправляет сообщение
onToolCallИнструмент собирается запуститься
onToolResultРезультат инструмента доступен
onApprovalResolvedРешение об одобрении принято

Хост вызывает activate, когда расширение перечисляет событие и имеет читаемый main.

contributes.tools

ПолеОписание
nameОбязательно. [a-z0-9]+(?:[._-][a-z0-9]+)*
descriptionОбязательно. Показывается модели
inputSchemaОбязательно. Объект JSON Schema
outputSchemaНеобязательно. Объект JSON Schema
approvalModeНеобязательно. allowed, need-approval или need-questions
executionModeНеобязательно. foreground или background

Модель не видит name как есть. Хост создаёт имя вызова, например, extension__{id}__{tool}__{hash}.

contributes.desktop

Требует desktop-ui.

ПолеОписание
css[].pathОбязательно. CSS-файл относительно корня пакета
css[].mediaНеобязательно. CSS media query
settingsPageНеобязательно. true, {} или { title } — добавляет запись в настройки Desktop

contributes.cli

Требует cli-ui. hooks — это объект с path, а не встроенный массив:

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

Файл должен быть { "hooks": [ ... ] }.

ПолеОписание
slotОбязательно. Один из слотов ниже
variantНеобязательно. default, accented, muted, warning, success, danger
tokensНеобязательно. { foreground?, border?, accent? }
prefixНеобязательно. Строка
suffixНеобязательно. Строка

Слоты: message.user, message.assistant, message.tool, assistant.thinking, input.frame, bottom_form, bottom_form.section, slash_suggestions, approval.panel, questions.panel.

Роли токенов: default, primary, secondary, muted, accent, success, warning, danger.

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

settingsSchema

ПолеОписание
keyОбязательно. Тот же шаблон, что и для имён инструментов
typeОбязательно. string, boolean, number или select
titleОбязательно. Ярлык интерфейса
descriptionНеобязательно
placeholderНеобязательно
requiredНеобязательно. Логическое значение
defaultValueНеобязательно. Должно соответствовать type
optionsОбязательно для select. Массив { value, label, description? }

Значения — string, number, boolean или null. null очищает необязательную настройку.

secretSlots

ПолеОписание
keyОбязательно
titleОбязательно
descriptionНеобязательно
requiredНеобязательно. Логическое значение

Desktop хранит секреты в системной связке ключей. В режиме демона CLI secrets.set / secrets.delete могут быть недоступны.

activate

main загружается с помощью динамического import. Экспортируйте одно из:

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

ctx

ПолеОписание
extension{ id, name, version, directoryPath, manifestPath, main }
hostAPI хоста. Desktop реализует showMessageBox. CLI — {}
log(message: string) => void
settingsget(key), getAll(), set(key, value), setAll(values)
secretsget(key), has(key), set(key, value), delete(key) — ключи должны быть объявлены в secretSlots
activationEventНеобязательно. { type, detail? }

Возвращаемое значение

Верните объект или экспортируйте те же поля из модуля.

ПолеОписание
toolsRecord<string, (ctx) => unknown>. Ключи — имена инструментов манифеста
invokeTool(ctx) => unknown. Используется вместо tools[name], если присутствует
systemPromptСтатический фрагмент системного промпта
getSystemPrompt() => string | Promise<string>. Используется вместо systemPrompt, если присутствует
onEvent(event) => void
dispose() => void. Вызывается при удалении или перезагрузке

Контекст обработчика инструмента ctx

ПолеОписание
extensionТа же информация о времени выполнения, что и в activate
hostТот же хост API
toolNameИмя инструмента из манифеста
argumentsОбъект из модели
logТот же логгер
settingsТот же доступ к настройкам
secretsТот же доступ к секретам
toolCallIdНеобязательно
questionsResultНеобязательно. Устанавливается, когда approvalMode имеет значение need-questions

Результат string передается как есть. Другие значения — JSON.stringify(value, null, 2). undefined превращается в "".

Desktop host.showMessageBox

ПолеОписание
titleОбязательно. Строка
messageОбязательно. Строка
detailНеобязательно. Строка
buttonsНеобязательно. Массив строк
cancelIdНеобязательно. Число
defaultIdНеобязательно. Число
noLinkНеобязательно. Логическое значение
typeНеобязательно. 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");
    },
  };
}

Установка

  • ZIP — архив должен содержать ровно один package.json (он может находиться в подкаталоге). Импортируйте с помощью spirit extension import ./extension.zip или Settings → Extensions.
  • Tarball из маркетплейса — в корне архива должна находиться директория package/.

supportedHosts должен включать текущий хост. Повторная установка того же id по умолчанию не заменяет существующую копию. См. Marketplace для каналов и интерфейса установки.

Публикация в официальный реестр

Репозиторий SpiritAgents/registry является индексом маркетплейса. Он не содержит исходный код расширений, dist, ZIP-файлы или tarball.

Выполните следующие шаги по порядку:

  1. Опубликуйте публичный npm-пакет.
  2. Откройте pull request, в котором указан этот пакет.

1. Опубликуйте npm-пакет

Опубликованный package.json является источником истины, особенно spiritExtension. Сборщик реестра требует:

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

Он также читает name, version, description, author, repository, homepage, keywords и опциональный spiritExtension.icon. Если вы установили icon, включите этот файл в опубликованный пакет. Отсутствие объекта spiritExtension приводит к сбою сборки реестра для этой версии.

Официальный пример: @spiritagent/extension-system-message-demo (spiritagent.system-message-demo).

2. Укажите пакет

Создайте registry/extensions/<extension-id>/ со следующими файлами:

ФайлОписание
entry.jsonУправление маркетплейсом
README.mdТекст описания для маркетплейса

Не редактируйте эти файлы вручную (перегенерируйте их):

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

extensionId должен содержать как минимум два сегмента, разделенных точкой, состоящих из строчных букв и цифр, с точками или дефисами внутри сегмента. Примеры: spiritagent.system-message-demo, yourteam.some-extension. packageName и extensionId должны быть уникальными в репозитории.

entry.json

ПолеОписание
schemaVersionОбязательное. 1
extensionIdОбязательное. См. правила выше
packageNameОбязательное. Имя npm-пакета
statusОбязательное. listed, hidden, deprecated или blocked
featuredОбязательное. Логическое значение
defaultVersionОбязательное. Строка версии по умолчанию
defaultReviewStatusОбязательное. unverified, verified или revoked
versionsОбязательное. Как минимум один элемент

Каждый элемент versions[]:

ПолеОписание
versionОбязательное
channelОбязательное. stable, preview или experimental
reviewStatusОбязательное. unverified, verified или revoked
changelogНеобязательное. { summary, body } — оба обязательны, если присутствуют
{
  "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."
      }
    }
  ]
}

В README маркетплейса следует описать, что делает расширение, имя пакета, версию по умолчанию, совместимость с хостами и возможностями, а также примечания для читателей маркетплейса.

Перегенерируйте производные файлы локально:

./scripts/build-registry.ps1

Скрипт получает метаданные npm для указанных версий, пересобирает catalog.json и каждый detail.json, а затем проверяет согласованность.

Запрос на включение

Включите entry.json, README.md из маркетплейса и перегенерированные catalog.json / detail.json. Укажите только те версии, которые уже опубликованы в npm. Установите reviewStatus и channel точно. Новые версии обычно начинаются как unverified.

Не коммитьте исходные деревья расширений, dist, ZIP-файлы, тарболы и другие бинарные артефакты.

Открытие запроса на включение не гарантирует публикацию. Проверка может потребовать изменений до того, как версия будет одобрена или проверена.