docs: спек упаковки в плагин Claude Code и публикации через Gitea

This commit is contained in:
2026-07-31 00:37:26 +03:00
parent 4e119afdba
commit 63bd9db5f3
@@ -0,0 +1,207 @@
# Спек: упаковка kompas3d-mcp в плагин Claude Code и публикация через Gitea
Дата: 2026-07-31 · Статус: согласовано, к реализации
## 1. Цель
Сделать так, чтобы возможности этого проекта (MCP-сервер КОМПАС-3D + методические навыки)
устанавливались в чужой Claude Code одной командой, а не воспроизведением ручной настройки:
клонировать репозиторий, собрать `.NET`, прописать абсолютный путь в `.mcp.json`, скопировать навыки.
Аудитория — автор и узкий круг знакомых, с прицелом на возможную публичность. Отсюда: структура и
README сразу рассчитаны на постороннего, но вылизанный онбординг (мастера установки, автодиагностика
всех отказов) в объём не входит.
**Вне объёма этой спеки:** сценарий выполнения задач в репозитории (рабочий цикл доработки MCP) —
прорабатывается отдельно и с нуля; `docs/superpowers/NEXT-SESSION.md` пока остаётся как есть.
## 2. Исходные факты (проверены)
- `git.shahovalov.ru/mikhail/claude-plugins` **редиректит** на `home-repo-cc` — репозиторий
переименован. Каталог плагинов = `home-repo-cc` (`.claude-plugin/marketplace.json`, плагин
`obsidian-autodoc`).
- `home-repo-cc`**приватный** (Gitea API отдаёт 404 без токена). `kompas3d-mcp`**публичный**,
релизы включены.
- Marketplace поддерживает source-тип **`git-subdir`** (`{url, path, ref?, sha?}`, разрежённый клон) —
плагин может физически жить в подкаталоге другого репозитория.
- Плагин объявляет MCP-серверы через `.mcp.json` в своём корне; в путях доступна переменная
`${CLAUDE_PLUGIN_ROOT}`. Плагин копируется в кеш `~/.claude/plugins/cache` при установке.
- `.agents/skills/` в этом репозитории — побайтово идентичная копия `.claude/skills/` (untracked):
дублирование навыков между харнессами уже началось вручную.
- Сервер: `net8.0-windows`, x64, вендорские interop-DLL АСКОН в `libs/kompas-interop` (4.4 МБ,
в репозитории). Ни WinForms, ни WPF не используются.
## 3. Принятые решения
| Вопрос | Решение |
| --- | --- |
| Аудитория | автор + знакомые; структура «как для чужого», публичность — потом |
| Источник бинаря | готовый `win-x64` из Gitea Release, собирается CI |
| Состав плагина | навыки `kompas-3d`, `kompas-fdm-design`; команда `/kompas:doctor`; MCP-сервер |
| Каталог | остаётся приватным `home-repo-cc`, доступ знакомым выдаётся в Gitea |
| Раскладка | плагин лежит в `kompas3d-mcp/plugin/`, каталог ссылается через `git-subdir` |
| Имя плагина | `kompas` |
`kompas`, а не `kompas-3d`: пространство имён даёт `/kompas:doctor`, навыки становятся
`kompas:kompas-3d` / `kompas:kompas-fdm-design`, и это совпадает с именем MCP-сервера (`mcp__kompas__*`).
Субагент `kompas-sdk-research` и база `docs/Kompas3D_SDK/` (14 МБ, 2466 файлов) **в плагин не входят**
это инструмент разработки сервера, а не построения деталей.
## 4. Раскладка
```
kompas3d-mcp/
plugin/ ← весь плагин, единственный источник истины навыков
.claude-plugin/plugin.json
skills/kompas-3d/SKILL.md
skills/kompas-fdm-design/{SKILL.md,references/}
commands/doctor.md → /kompas:doctor
.mcp.json
scripts/launch-kompas-mcp.ps1
server.lock.json
README.md
adapters/{codex,opencode}/
tools/sync-agent-assets.ps1
.gitea/workflows/{ci.yml,release.yml}
```
`plugin/.claude-plugin/plugin.json`: `name: "kompas"`, `displayName`, `description`, `version`
(семвер, поднимается релизным коммитом), `author`, `homepage`, `repository`, `license`, `keywords`.
## 5. Источник истины навыков
Навыки хранятся **только** в `plugin/skills/`. `tools/sync-agent-assets.ps1` создаёт на них junction'ы
из `.claude/skills/<name>` и `.agents/skills/<name>` (на Windows junction создаётся без прав
администратора); если junction создать не удалось — копирует и печатает предупреждение. Обе целевые
папки добавляются в `.gitignore`, прежние копии удаляются из индекса.
Смысл: сегодняшний дрейф между `.claude` и `.agents` устраняется структурно. Для локальной работы в
этом репозитории плагин **не устанавливается** — иначе навыки задвоятся (плагинная копия + junction).
## 6. Лаунчер и версионирование сервера
`plugin/server.lock.json` — пин ровно одной версии:
```json
{ "version": "1.0.0",
"url": "https://git.shahovalov.ru/mikhail/kompas3d-mcp/releases/download/v1.0.0/kompas-mcp-1.0.0-win-x64.zip",
"sha256": "…" }
```
`plugin/.mcp.json`:
```json
{ "mcpServers": { "kompas": {
"command": "powershell",
"args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File",
"${CLAUDE_PLUGIN_ROOT}/scripts/launch-kompas-mcp.ps1"] } } }
```
Алгоритм `launch-kompas-mcp.ps1`:
1. Задан `KOMPAS_MCP_EXE` → использовать его (цикл разработки: локальная сборка вместо релиза).
2. Иначе целевой путь `%LOCALAPPDATA%\kompas-mcp\<version>\kompas-mcp.exe`; существует → запуск.
3. Отсутствует → скачать ассет во временный каталог, посчитать SHA256; **несовпадение — отказ с
ненулевым кодом, скачанный файл не запускается**; распаковать во временный каталог и атомарно
переименовать в целевой (устойчиво к параллельному старту двух сессий).
4. `& $exe @args`; stdio наследуется дочерним процессом; код возврата пробрасывается.
**Инвариант:** скрипт не пишет в stdout ни байта (stdout — канал JSON-RPC). Всё — в stderr;
`Write-Host` запрещён.
Публикуется **self-contained win-x64**: другу не нужен установленный .NET 8 Runtime, требуется только
КОМПАС. Цена — ~70 МБ на ассет. Старые версии остаются в кеше рядом — откат сводится к правке
`server.lock.json`.
## 7. Команда `/kompas:doctor`
Командный промт (`plugin/commands/doctor.md`), проводящий агента по проверкам с конкретным действием
на каждый отказ:
1. `server.lock.json` — какая версия ожидается;
2. наличие `%LOCALAPPDATA%\kompas-mcp\<version>\kompas-mcp.exe``KOMPAS_MCP_EXE`, если задан);
3. запущен ли процесс КОМПАС;
4. отвечает ли инструмент `kompas_status`;
5. итоговый отчёт.
## 8. CI (Gitea Actions в `kompas3d-mcp`)
**`ci.yml`** — push/PR в `main`: `dotnet build -c Release` + `dotnet test --filter Category=Unit`.
**Интеграционные тесты в CI не выполняются никогда** — им нужен запущенный КОМПАС с GUI и лицензией.
Это граница, а не задача на будущее; фиксируется в README и в workflow комментарием.
**`release.yml`** — на тег `v*`:
1. `dotnet publish -c Release -r win-x64 --self-contained` → zip → sha256;
2. создание релиза и загрузка ассета через Gitea API (`curl` + `secrets.GITEA_TOKEN`);
3. бот-коммит в `main` с пометкой `[skip ci]`: обновить `plugin/server.lock.json`
(version/url/sha256) и `version` в `plugin/.claude-plugin/plugin.json`.
Требования к раннеру (готовится в отдельной сессии «LXC для Gitea runner»): .NET 8 SDK, доступ к
nuget.org, `curl` и `git`, токен Gitea с правами на релизы и запись в репозиторий. Если раннер
Linux — сборка с `-p:EnableWindowsTargeting=true` (ожидается, что проект соберётся: WinForms/WPF не
используются; **подлежит проверке первым же прогоном**, при неудаче — Windows-раннер).
## 9. Каталог и установка
В `home-repo-cc/.claude-plugin/marketplace.json` добавляется одна запись, один раз:
```json
{ "name": "kompas",
"source": { "source": "git-subdir",
"url": "https://git.shahovalov.ru/mikhail/kompas3d-mcp.git",
"path": "plugin", "ref": "main" },
"description": "КОМПАС-3D через MCP: построение деталей, сборки, чертежи, STEP",
"category": "cad" }
```
`ref: main`, а не тег: версия для Claude Code берётся из `plugin.json.version`, поднятого релизным
коммитом, поэтому обновления доезжают через `/plugin marketplace update` сами и каталог руками больше
не правится. Каталог приватный, плагин тянется из публичного репозитория — знакомому нужен доступ
только к каталогу.
Путь пользователя: доступ в Gitea → `/plugin marketplace add https://git.shahovalov.ru/mikhail/home-repo-cc.git`
`/plugin install kompas@home-repo-cc``/kompas:doctor`.
Предпосылки в `plugin/README.md`: Windows x64; установленный и **запущенный** КОМПАС-3D (проверено на
v24 Home); работа только на одной машине с КОМПАС (сервер — COM-клиент, не автономный CAD-движок).
## 10. Задел под Codex и opencode
Ни Codex, ни opencode не знают ни `${CLAUDE_PLUGIN_ROOT}`, ни маркетплейсов: для них модель —
клон репозитория и абсолютный путь. Задел выражается не декларацией, а тем, что контент не копируется:
- `adapters/codex/config.snippet.toml``[mcp_servers.kompas]` через тот же лаунчер + README:
куда вставлять (`~/.codex/config.toml`), навыки берутся из `.agents/skills` (создаёт sync-скрипт);
- `adapters/opencode/opencode.json` — фрагмент local-MCP + README;
- лаунчер параметризуется только `server.lock.json`, навыки лежат в одном месте — новый харнесс стоит
README и сниппета, а не форка контента.
Граница явная: это сниппеты и инструкция, а не дистрибутивы. Полноценные плагины Codex/opencode —
отдельная веха, когда у них стабилизируется формат пакета.
## 11. Риски и открытые вопросы
- **Приватный каталог** требует у знакомого аккаунта в Gitea и настроенного git-credential-helper;
что Claude Code корректно клонирует приватный marketplace по https — проверить на живом человеке.
- **Скачивание исполняемого файла** закрыто sha256-пином в репозитории; при несовпадении — отказ.
- **Лицензионный статус interop-DLL АСКОН** при публичности плагина и self-contained-сборки —
открытый вопрос, требует отдельного решения до выхода за круг знакомых.
- **Рекурсия CI**: бот-коммит релиза помечается `[skip ci]`; проверить, что раннер это уважает.
- **Сборка `net8.0-windows` на Linux-раннере** — ожидаемо работает с `EnableWindowsTargeting`,
но не проверена.
- **Расход диска**: каждая версия сервера ~70 МБ в `%LOCALAPPDATA%`; очистка старых версий — вручную.
## 12. Критерии приёмки
1. `tools/sync-agent-assets.ps1` отрабатывает на чистом клоне: навыки видны и в Claude Code, и в
`.agents/skills`, при этом файлы физически существуют в одном месте.
2. Тег `v*` даёт релиз в Gitea с ассетом и обновлённые `server.lock.json` / `plugin.json` в `main`.
3. На машине без предустановленного сервера: `/plugin install kompas@home-repo-cc` → первый запуск
скачивает бинарь, `/kompas:doctor` зелёный.
4. Порча sha256 в `server.lock.json` приводит к отказу запуска с внятным сообщением в stderr.
5. Ручная проверка канала: лаунчер получает `initialize` на stdin и отдаёт корректный JSON-RPC-ответ,
в stdout нет посторонних строк.
6. По playbook'у навыка `kompas:kompas-3d` строится деталь на свежей установке.