amxx-builder 1.5.2 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,554 @@
1
+ ---
2
+ name: amxb-migration
3
+ description: >-
4
+ Миграция AMXX-проекта (AMX Mod X, Counter-Strike) на сборщик amxb (amxx-builder):
5
+ перевод репозитория на amxbuild.yml, подключение зависимостей (deps), настройка
6
+ .gitignore / CI / MCP, замена старых скриптов сборки. Использовать, когда пользователь
7
+ просит «настроить сборку через amxb», «перевести/перенести проект на amxb / amxbuild.yml»,
8
+ «заменить старые bat/ps1/sh-скрипты или CI на amxb», «добавить/дополнить amxbuild.yml»,
9
+ либо мигрирует проект с ручной компиляции (compile.exe/amxxpc) на автоматическую.
10
+ Скилл работает в проекте, где amxb ещё не установлен (установка — шаг 0). Триггеры:
11
+ amxb, amxx-builder, amxbuild.yml, AMX Mod X, AMXX, миграция сборки, build system
12
+ migration, amxx-builder migration. Use when migrating an AMX Mod X plugin/server
13
+ project to the amxb builder.
14
+ ---
15
+
16
+ # Миграция AMXX-проекта на amxb
17
+
18
+ Перевод репозитория AMXX-плагинов на сборщик **amxb** (`amxx-builder`).
19
+ Конфигурация — один файл `amxbuild.yml` в корне проекта. Результат миграции:
20
+ проект собирается командой `amxb build` (или в CI через
21
+ `AmxxModularEcosystem/amxx-builder@v1`) в готовый `.zip`.
22
+
23
+ ## Когда применять / не применять
24
+
25
+ **Применять**, когда пользователь просит:
26
+ - настроить сборку через amxb / перевести проект на `amxbuild.yml`;
27
+ - заменить старые скрипты сборки (`.bat`/`.ps1`/`.sh`, `config.bat`,
28
+ `.build-config`) или старый CI на amxb;
29
+ - разобрать существующий `amxbuild.yml`, дополнить его, найти ошибку в deps.
30
+
31
+ **Не применять**, когда:
32
+ - задача — просто собрать/задеплоить уже настроенный amxb-проект (это
33
+ обычные команды `amxb build` / `amxb deploy`, скилл не нужен);
34
+ - пользователь хочет писать плагины (это не миграция).
35
+
36
+ ## Стартовое состояние
37
+
38
+ Проект может находиться в любом из состояний, все они корректны:
39
+
40
+ - уже есть старая система сборки (`bat`/`ps1`/`sh`-скрипты,
41
+ `.build-config`, CI со сборкой);
42
+ - есть только CI со списком зависимостей;
43
+ - вообще нет никакой системы сборки — это нормально, просто входных
44
+ данных меньше.
45
+
46
+ **Правило (границы доступа):** вся информация берётся **только из папки
47
+ целевого проекта**. Запрещено читать, открывать или иначе исследовать любые
48
+ файлы/папки **вне её** — соседние проекты на диске, sibling-репозитории,
49
+ аналоги пользователя, чужие репозитории. Запрет действует **независимо от
50
+ цели**: и как источник данных, и «просто для понимания экосистемы», и для
51
+ проверки гипотез. Такое чтение нарушает границы задачи и приватность
52
+ (соседний проект может быть приватным/чужим/нерелевантным). Если данных
53
+ не хватает — задаём вопросы пользователю, а не докапываемся самостоятельно.
54
+
55
+ **Где работать:** все шаги выполняются в корне целевого проекта (там, где
56
+ должен лежать `amxbuild.yml`). Пути в манифесте относительны именно него.
57
+
58
+ ## Что amxb делает «из коробки» (минимальный манифест)
59
+
60
+ Прежде чем задавать вопросы о структуре пакета — понять, что amxb даёт сам.
61
+ Манифест из одного `name:` уже собирает проект в архив по умолчанию:
62
+
63
+ ```text
64
+ {name}.zip ← output.archive_name (default: "{name}.zip")
65
+ ├── README.md ← output.readme: true (если есть в корне проекта)
66
+ └── {name}/ ← обёртка: часть output.amxmodx_path / assets_path
67
+ ├── addons/amxmodx/ ← output.amxmodx_path (default: "{name}/addons/amxmodx")
68
+ │ ├── configs/... ← локальная amxmodx/configs (мержится с репо)
69
+ │ ├── data/... lang/...
70
+ │ ├── plugins/*.amxx ← скомпилированные .sma
71
+ │ └── scripting/... ← исходники (копируются и компилируются)
72
+ └── sound/, models/, ... ← содержимое assets/ → в {name}/ (source: local)
73
+ ```
74
+
75
+ Ключевое: **дефолтная раскладка архива уже совпадает с типовым пакетом
76
+ плагина/сервера** (`{name}/addons/amxmodx/...`) — обычно структуру менять не
77
+ нужно и вопросы про неё не задаются. README попадает в архив только при
78
+ `output.readme: true` (это дефолт). Архив ложится в `output.dir` рядом с
79
+ манифестом (default: `./`).
80
+
81
+ Точные пути печатает план сборки — см. Шаг 4 (проверка через
82
+ `amxb build --dry-run`), а не догадки о схеме.
83
+
84
+ ## Шаг 0. Убедиться, что amxb доступен
85
+
86
+ Скилл рассчитан на проект, где amxb может быть ещё **не установлен**.
87
+
88
+ 1. Проверить наличие:
89
+ ```bash
90
+ amxb --version
91
+ ```
92
+ 2. Если команды нет — установить (нужен Node.js 18+):
93
+
94
+ Глобально через npm:
95
+ ```bash
96
+ npm i -g amxx-builder
97
+ ```
98
+
99
+ Или через официальный установщик (не требует npm):
100
+ ```bash
101
+ # Windows (PowerShell):
102
+ irm https://raw.githubusercontent.com/AmxxModularEcosystem/amxx-builder/master/install.ps1 | iex
103
+ ```
104
+ ```bash
105
+ # Linux / macOS:
106
+ curl -fsSL https://raw.githubusercontent.com/AmxxModularEcosystem/amxx-builder/master/install.sh | bash
107
+ ```
108
+
109
+ Для приватных репозиториев во время установки передать `GITHUB_TOKEN`:
110
+ ```bash
111
+ GITHUB_TOKEN=ghp_xxx curl -fsSL https://raw.githubusercontent.com/AmxxModularEcosystem/amxx-builder/master/install.sh | bash
112
+ ```
113
+
114
+ Конкретную версию можно зафиксировать переменной `AMXB_VERSION` (тег `v1.2.3`).
115
+
116
+ 3. Если глобальная установка нежелательна/невозможна — можно запускать любую
117
+ команду amxb через npx без установки:
118
+ ```bash
119
+ npx --yes amxx-builder@latest build --dry-run
120
+ ```
121
+ (далее в тексте команды записаны как `amxb ...` — при работе через npx
122
+ подставлять `npx --yes amxx-builder@latest ...`.)
123
+
124
+ 4. Проверить окружение (Node, доступность GitHub API, кэш, при наличии —
125
+ валидность манифеста):
126
+ ```bash
127
+ amxb doctor
128
+ ```
129
+
130
+ > Для самой миграции amxb не нужно добавлять в зависимости проекта —
131
+ > это глобальный CLI (или GitHub Action в CI).
132
+
133
+ ## Шаг 1. Собрать входные данные (только внутри проекта)
134
+
135
+ 1. **Список плагинов**: все `.sma` в `amxmodx/scripting/` (рекурсивно).
136
+ amxb компилирует `**/*.sma` и сохраняет относительный путь
137
+ (вложенный `.sma` → `plugins/подпапка/имя.amxx`).
138
+ 2. **Используемые include** — по всему `scripting/` (и `.sma`, и `.inc`):
139
+ ```bash
140
+ grep -rhoE '^\s*#include\s*[<"][^>"]+[>"]' amxmodx/scripting \
141
+ --include='*.sma' --include='*.inc' \
142
+ | sed -E 's/^\s*#include\s*([<"])(.*)/\2/' | sed -E 's/[>"]$//' \
143
+ | sort | uniq -c | sort -rn
144
+ ```
145
+ 3. **Целевая структура пакета**: если есть старый `*.zip` или каталог
146
+ `.build/` — распаковать и посмотреть, что входило: `configs/`,
147
+ `plugins/`, `scripting/`, ini-файлы, `data/lang/`. Это эталон для сверки.
148
+ 4. **Подсказки о зависимостях** — что может лежать в проекте. Приоритет — по
149
+ ценности источника, от самого конкретного к самому общему:
150
+ 1. **CI/workflow** (`.github/workflows/`, `.gitlab-ci.yml` и т.п.) — самый
151
+ ценный источник: в нём проект когда-то собирался и все версии зафиксированы.
152
+ Искать конкретику, а не «список зависимостей вообще»:
153
+ - упоминания репозиториев: `uses: AmxxModularEcosystem/...`, `wget`/`curl`
154
+ `github.com/<owner>/<repo>`, переменные `REPO=...`, `OWNER/REPO`;
155
+ - **версии**: `*_TAG=...`, `ref: v...`, `@v...`, теги в URL релизов;
156
+ - **include-пути**: `INCLUDE_PATH=...`, флаги `-i...`, пути вида
157
+ `addons/amxmodx/scripting/include`, `amxmodx/scripting/include`;
158
+ - версию компилятора/AMXX (обычно в той же строке установки include).
159
+ 2. `deps.txt` — список GitHub-репо зависимостей (если есть).
160
+ 3. `.build-config` / `config.bat` / другие скрипты сборки — дают доп.
161
+ параметры для манифеста (имя пакета, ini-постфикс, defines). В них же
162
+ могут встречаться пути вида `C:\AmxModX\1.9.0`, имена `amxx190`/`amxxpc`
163
+ — это указание на линию AMXX, под которую собран проект; значение оно
164
+ имеет только если линия <= 1.8.3 (см. Шаг 4 — версию компилятора пиним
165
+ только для этой старой линии).
166
+ Если CI в проекте есть — он даёт и источники, и версии, и include-пути
167
+ сразу, и вопросов пользователю может не понадобиться вовсе.
168
+
169
+ ## Шаг 2. Классифицировать include
170
+
171
+ | Группа | Примеры | Действие |
172
+ |---|---|---|
173
+ | Стандартная библиотека AMXX | `amxmodx`, `fakemeta`, `hamsandwich`, `nvault`, `regex`, `json` | ничего не нужно |
174
+ | Локальные (внутри проекта) | свои `.inc` в `scripting/`, `scripting/include/` | ничего не нужно — amxb сам подключает `scripting/` и `scripting/include/`, а дерево `amxmodx/` мержится в пакет |
175
+ | Внешние | чужие API-библиотеки/модули | запись в `deps:` манифеста |
176
+
177
+ > Публичные API самого проекта (свои `.inc`, объявляющие natives, которые
178
+ > реализуют плагины проекта) — НЕ deps. deps — только то, что приходит извне.
179
+
180
+ ## Шаг 3. Поиск источников внешних include
181
+
182
+ Имея только имя инклуда (`<cwapi>`, `<player_prefs>`, `<reapi>`...), агент
183
+ никак не может надёжно определить его источник. Поэтому **источник include
184
+ ищется только внутри проекта или уточняется у пользователя** — поиск по
185
+ GitHub/вебу по имени инклуда запрещён (это чтение чужих репозиториев, см.
186
+ правило выше):
187
+
188
+ 1. **CI/workflow проекта** — самый конкретный источник (см. Шаг 1): репо в
189
+ `uses:`/URL, версии в `*_TAG=`, include-пути. Если CI есть — начать с него.
190
+ 2. `deps.txt` / `.build-config` — если упоминают репозиторий, дающий этот
191
+ include — берём его.
192
+ 3. Чтение шапки `.inc` или места использования внутри проекта: часто инклуд
193
+ сам включает другие (`#include <reapi>` внутри чужого API → ReAPI нужен
194
+ отдельным dep).
195
+ 4. **Если после разбора CI и скриптов сборки источник не находится — НЕ копаем
196
+ сами.** Задаём вопрос пользователю: откуда инклуд, в каком репозитории он
197
+ лежит. Вопрос задаём по каждому неопознанному include, а не по одному
198
+ самому очевидному.
199
+
200
+ **Репозиторий «не найден» — это ещё не «репозитория не существует».**
201
+ GitHub отвечает 404 одинаково и для несуществующих, и для приватных (или
202
+ недоступных без токена) репозиториев. Поэтому, если подтверждённый (названный
203
+ пользователем или найденный в CI) репозиторий не резолвится (`amxb deps-tree`
204
+ показывает not found), действуем по порядку:
205
+
206
+ 1. **Сначала пробуем токены из `.env`** рядом с манифестом (если они там
207
+ есть: `GITHUB_TOKEN`, `GITHUB_TOKEN_*`). Прописываем их в манифест
208
+ (`github.token_env` / `github.tokens`, см. Шаг 5) и повторяем
209
+ `amxb deps-tree` — репозиторий мог быть приватным, и токен уже лежит
210
+ рядом. Не спрашиваем пользователя, пока не проверили имеющиеся токены.
211
+ 2. **Если токенов нет или они не помогли** — первая гипотеза: репозиторий
212
+ приватный. Задаём пользователю вопрос с двумя вариантами:
213
+ - дать токен для этого владельца (`github.tokens` + `.env`), либо
214
+ - уточнить имя репозитория — если в CI/запросе была опечатка
215
+ (`Owner/Repoo` → `Owner/Repo`).
216
+ Не делаем вывод «репозитория не существует» без подтверждения пользователя.
217
+
218
+ **Проверка версии у подтверждённого источника — штатными средствами amxb,
219
+ не вручную.** Список релизов/тегов репозитория-источника (после того как
220
+ источник назван пользователем или найден в CI) смотрим командой:
221
+
222
+ ```bash
223
+ amxb releases Owner/Repo # релизы; --tags для тегов, --limit N
224
+ ```
225
+
226
+ Ту же информацию (и резолв include/деревьев) дают MCP-инструменты
227
+ `amxx-dep-resolver` (`list_releases`, `get_dep_tree`, `resolve_include`) —
228
+ **это инструменты для АГЕНТА: они дают миграции больше данных о проекте без
229
+ ручных запросов**. Но они доступны только если реально видны в текущей
230
+ сессии — MCP подхватывается **после перезапуска opencode, который нужен
231
+ самому агенту** (см. Шаг 9), а не пользователю. Если инструментов в сессии
232
+ нет — не ждём и не молчим, а работаем CLI: `amxb releases` / `amxb deps-tree`
233
+ дают ту же информацию без перезапуска. Ручные запросы к GitHub API/вебу для
234
+ выбора версии не нужны и не делаются — см. «Формат deps» ниже. Чужие/соседние
235
+ репозитории и их содержимое при этом не открываем.
236
+
237
+ ### Формат deps
238
+
239
+ ```yaml
240
+ deps:
241
+ # git-репо: owner/repo@ref, опционально :путь_до_include_внутри_репо
242
+ - Owner/Repo@tag
243
+ - Owner/Repo@tag:нестандартный/путь/до/include
244
+
245
+ # модуль из release-архива; ref: latest резолвится в последний релиз,
246
+ # но в манифест пишем конкретный тег (см. «Выбор версии» ниже)
247
+ - repo: Owner/Repo
248
+ ref: 5.29.0.358
249
+ source: release
250
+ include_path: путь/внутри/архива/до/include
251
+
252
+ # .inc закрытого плагина с fungun.net (магазин без архивов и git-репо):
253
+ # id — индекс плагина в адресе страницы; можно указать полную ссылку url:
254
+ - source: fungun
255
+ id: 106
256
+ ```
257
+
258
+ Источников у deps три: `git` (по умолчанию), `release` (GitHub release-архив)
259
+ и `fungun` (публичный `.inc` со страницы закрытого плагина на fungun.net).
260
+
261
+ **Что даёт `deps:` и чего не даёт.** `deps:` — это **только заголовочные
262
+ файлы (`.inc`) для компиляции**. amxb **не компилирует и не упаковывает**
263
+ плагины из репозиториев-зависимостей. Для большинства плагинов это
264
+ корректно (модули ставятся на сервер отдельно). Но если мигрируемый проект —
265
+ это сборка, где плагины зависимостей должны **попасть в итоговый пакет**,
266
+ нужны `repos:` (репозитории как часть сборки) и/или `assets:` — а не `deps:`.
267
+ Определить, что нужно, можно по старой системе сборки: если она клонировала
268
+ репозитории и компилировала их `.sma` в один пакет — это `repos:`; если
269
+ только клала их include в папку инклудов — это `deps:`.
270
+
271
+ **Выбор версии** — под «последней версией» понимается не строка `latest` в
272
+ манифесте, а **получение конкретной версии и её фиксация**. Алгоритм:
273
+
274
+ 1. **Узнать последнюю версию** подтверждённого источника: для `source: release`
275
+ `ref: latest` резолвится в последний GitHub-релиз, для git-репо — в
276
+ последний тег. Смотрим штатно: `amxb releases Owner/Repo` (релизы),
277
+ `amxb releases Owner/Repo --tags` (теги), либо MCP `list_releases`.
278
+ 2. **Проверить последнюю версию реальной сборкой** (`amxb deps-tree`, затем
279
+ `amxb build`), что include резолвится и natives находятся (`--dry-run` план
280
+ не компилирует и этого не покажет).
281
+ 3. **Если последняя версия найдена и подходит — берём её**, в манифест пишем
282
+ **конкретный тег** (а не строку `latest`) — сборка становится
283
+ воспроизводимой. `latest` в манифесте оставляем только если политика
284
+ источника осознанно «всегда последняя» (подтверждена старым CI).
285
+ 4. **Если последней версии нет или она не подходит** (свежая линия сменила
286
+ неймспейс include `<Module>` → `<Namespace/Module>` или префиксы natives
287
+ `MOD_*` → `NS_MOD_*`) — берём версию, **найденную по проекту** (старый CI:
288
+ `*_TAG=...`, `ref: v...`; `deps.txt`; README) — с ней проект уже собирался.
289
+ Её тоже проверяем сборкой.
290
+ 5. **Если нет ни того ни другого** — НЕ молчим и НЕ правим код втихую.
291
+ Формулируем пользователю развилку: **(A)** зафиксировать совместимый старый
292
+ тег (например rc-линию) — если на серверах стоит именно эта линия, либо
293
+ **(B)** обновлять код проекта под новое API — отдельная задача за рамками
294
+ миграции. Без ответа пользователя версию не закрепляем.
295
+ 6. Если пользователь не может назвать точные версии (отвечает «latest» /
296
+ «не знаю») — закрепляем выбранные, но **явно помечаем их как
297
+ предварительные** и выносим в итоговый отчёт как открытый вопрос, чтобы
298
+ риск несовместимости был зафиксирован, а не замолчан.
299
+
300
+ ## Шаг 4. Манифест и структура проекта
301
+
302
+ 1. **Сначала написать минимальный манифест и посмотреть план**, а не
303
+ угадывать структуру/задавать вопросы о ней. Дефолтная раскладка уже
304
+ совпадает с типовым пакетом (см. «Что amxb делает из коробки» выше), а
305
+ `amxb build --dry-run` печатает фактические пути:
306
+ ```bash
307
+ amxb build --dry-run
308
+ # ...
309
+ # Output:
310
+ # archive → /abs/path/./{name}.zip
311
+ # amxmodx path in archive: {name}/addons/amxmodx/
312
+ # assets path: {name}/
313
+ # generate_ini: false | on_conflict: last_wins
314
+ ```
315
+ Вопросы пользователю про структуру задаём **только если** фактическая
316
+ раскладка отличается от дефолтной и это принципиально (серверная сборка,
317
+ нестандартное расположение `amxmodx/`).
318
+ 2. Если структура проекта отличается от дефолтной для amxb
319
+ (`amxmodx/scripting/`, `amxmodx/configs/`, `assets/`) — можно предложить
320
+ привести её к стандарту. **Если пользователь отказывается менять
321
+ структуру — описываем в манифесте всё, что нужно для работы с текущей**
322
+ (поля `amxmodx.dir`, пути в deps, `output.*`).
323
+ 3. Базовые вопросы при нехватке данных: имя пакета, нужен ли ini со списком
324
+ плагинов и с каким постфиксом, что входит в пакет.
325
+ 4. **Версия компилятора (amxxpc) — всегда последняя.** В манифесте поле
326
+ `amxmodx.version` **не указываем** — amxb сам берёт последнюю доступную
327
+ версию. Единственное исключение: если проект таргетит AMXX **1.8.3 или
328
+ старее** — эта линия достаточно старая и сильно отличается (другой набор
329
+ инклудов/нативов, поведение компилятора), поэтому её версию пиним явно.
330
+ Подсказки в старых скриптах (`amxx190`, пути `C:\AmxModX\1.9.0`) — не
331
+ повод пинить: 1.9 и новее компилируются последним amxxpc. Уточнять у
332
+ пользователя нужно только одно: не 1.8.x ли у него сервер.
333
+ 5. Минимально полезный манифест:
334
+ ```yaml
335
+ # yaml-language-server: $schema=https://raw.githubusercontent.com/AmxxModularEcosystem/amxx-builder/master/schema/amxbuild.schema.json
336
+
337
+ name: MyPackage
338
+
339
+ # amxmodx.version не указываем — amxb берёт последний компилятор.
340
+ # Исключение — AMXX 1.8.3 или старее, тогда пиним явно (точное значение
341
+ # версии согласовать с пользователем — у линии 1.8 своя нумерация):
342
+ # amxmodx:
343
+ # version: "1.8.3" # строка в кавычках
344
+
345
+ deps:
346
+ - Owner/Repo@tag
347
+
348
+ # plugins-*.ini: по умолчанию НЕ генерируется (generate_ini: false).
349
+ # Если старой сборке ini не нужен — ничего добавлять не надо.
350
+ # Если нужен (как в старой сборке) — включить генерацию и постфикс:
351
+ # plugins_ini_postfix: core # → plugins-core.ini
352
+ # output:
353
+ # generate_ini: true
354
+ ```
355
+ 6. **Правила `plugins:`** — фильтрация локальных плагинов (к репо-плагинам
356
+ не применяются). Первое совпадение побеждает. Например, исключить из
357
+ сборки тестовые/легаси `.sma`:
358
+ ```yaml
359
+ plugins:
360
+ - match: "*Test*.sma"
361
+ enabled: false
362
+ - match: "utils/*.sma"
363
+ ini: false # компилировать, но не включать ни в один INI
364
+ ```
365
+ 7. Помнить особенности amxb:
366
+ - локальная папка `amxmodx/` всегда выигрывает у файлов из репо
367
+ (намеренный слой переопределения, предупреждений нет);
368
+ - `.sma`-файлы **и копируются** в пакет (как любые файлы), **и
369
+ компилируются**; если исходники не должны попадать в архив — исключить
370
+ их (для репо — `exclude:`/`exclude_files:` в правиле репозитория);
371
+ - README.md попадает в архив только при `output.readme: true` (дефолт).
372
+
373
+ ## Шаг 5. Приватные репозитории
374
+
375
+ **Сигнал приватности — 404.** Если deps/repos не резолвятся (`amxb deps-tree`
376
+ показывает not found), а пользователь уверен, что репозиторий существует, —
377
+ первая гипотеза: репозиторий приватный, а не удалённый и не опечатка
378
+ (GitHub отдаёт 404 одинаково в обоих случаях). Порядок действий:
379
+
380
+ 1. Проверить, нет ли уже токенов в `.env` рядом с манифестом; если есть —
381
+ подключить (`github.token_env` / `github.tokens`) и повторить `deps-tree`,
382
+ прежде чем что-либо спрашивать.
383
+ 2. Если токенов нет/не помогли — спросить пользователя: дать токен для
384
+ владельца приватного репо или уточнить имя (вдруг опечатка).
385
+
386
+ Если приватность подтверждена (или сам проект приватный) — конфигурация
387
+ такая:
388
+
389
+ ```yaml
390
+ github:
391
+ tokens:
392
+ OwnerName: GITHUB_TOKEN_OWNER
393
+ ```
394
+
395
+ и рядом с манифестом — `.env`:
396
+
397
+ ```
398
+ GITHUB_TOKEN_OWNER=ghp_...
399
+ ```
400
+
401
+ `.env` обязательно в `.gitignore`! В CI те же переменные прокидываются из
402
+ секретов в `env:` шага с action `AmxxModularEcosystem/amxx-builder@v1`
403
+ (встроенного `GITHUB_TOKEN` для чужих приватных репо недостаточно).
404
+
405
+ ## Шаг 6. .gitignore
406
+
407
+ За основу берётся шаблон `amxb init --gitignore`. При миграции **свериться
408
+ с исходным `.gitignore`** и сохранить специфичные для проекта строки,
409
+ которых нет в шаблоне. Типовой результат:
410
+
411
+ ```gitignore
412
+ # Артефакты сборки
413
+ *.amxx
414
+ *.zip
415
+ build/
416
+ dist/
417
+ plugins-*.ini
418
+
419
+ # старый bat-билд (если был)
420
+ .build
421
+
422
+ # amxb: секреты и кэш
423
+ .env
424
+ .env.local
425
+ .amxb-cache/
426
+
427
+ # инструменты / редактор
428
+ .omo/
429
+ .codegraph/
430
+ .vscode/
431
+ .claude/
432
+ node_modules/
433
+ ```
434
+
435
+ ## Шаг 7. Замена старых скриптов
436
+
437
+ Мини-чек-лист адаптации (шаблоны `amxb init` не перезаписывают существующие
438
+ файлы — только явный `amxb init --force` перезапишет; всё, что уже есть,
439
+ правим/создаём вручную):
440
+
441
+ - `amxb init --script` создаёт тонкий `build.bat`/`build.sh` (просто
442
+ `amxb build`) — только если файлов ещё нет. Если `build.bat`/`build.sh`
443
+ уже существуют — **заменяем их вручную** тонкими (init их не тронет без
444
+ `--force`).
445
+ - Старые скрипты сборки (`config.bat`, `build-debug.bat`,
446
+ `build-release.bat`, `.build-config`, `deps.txt` после переноса данных в
447
+ манифест, старые `*.zip`, `.build/`) — удаляем.
448
+ - Debug/release — через defines: `amxb build --define DEBUG`.
449
+ - **НЕ создавать тестовые плагины** через `amxb init --plugin <name>` в
450
+ рамках миграции/отладки: файл создаётся пустым и сам провоцирует
451
+ `error 100` при компиляции (см. Шаг 10). Проверка идёт по реальным `.sma`
452
+ проекта.
453
+
454
+ ## Шаг 8. CI
455
+
456
+ Если CI уже есть — правим существующий файл, не плодим новый. Что учесть:
457
+
458
+ - ветки и триггеры: сверить с фактической дефолтной веткой репозитория
459
+ (`main`/`master`) и со старым CI (перенести `paths-ignore`, типы PR и т.п.);
460
+ - секреты токенов для приватных deps — в `env:` шага сборки;
461
+ - dev-артефакт на каждый push + zip в релиз — как в шаблоне
462
+ `amxb init --workflow` (именование dev: `{name}-{sha}-dev`, релизный zip:
463
+ `{name}-{ref_name}.zip`);
464
+ - `output.readme: true` работает и при `output.pack: false` — README копируется
465
+ в каталог артефакта сам, отдельный шаг копирования в publish-джобе не нужен;
466
+ - если в шаблоне CI ветки/триггеры не совпадают с реальностью — правим,
467
+ шаблон не догма.
468
+
469
+ ## Шаг 9. MCP (опционально)
470
+
471
+ `amxb init --opencode` создаёт `.opencode/opencode.json` с MCP-сервером
472
+ `amxx-dep-resolver` (`amxb mcp`), дающим агенту инструменты резолва include,
473
+ дерева зависимостей и компиляции. **Это инструменты для АГЕНТА, а не для
474
+ пользователя**: они дают миграции больше данных о проекте (резолв инклудов,
475
+ список релизов, дерево deps) без ручных запросов и ускоряют её. Файл коммитим
476
+ (внутренние `node_modules`/`package*.json` — нет, у них свой `.gitignore`).
477
+
478
+ **Перезапуск opencode нужен самому агенту, а не пользователю.** Без
479
+ перезапуска инструменты `amxx-dep-resolver` не появятся в сессии агента, и
480
+ миграция лишится MCP-данных о проекте (придётся работать через CLI
481
+ `amxb releases`/`deps-tree` — это работает, но медленнее). Поэтому после
482
+ создания/правки конфига:
483
+
484
+ 1. проверить, появились ли инструменты `amxx-dep-resolver` в текущей сессии;
485
+ 2. если не появились (или проверить не удалось) — **явно попросить
486
+ пользователя перезапустить opencode** (выйти и запустить заново),
487
+ объяснив, что перезапуск нужен агенту, чтобы получить инструменты для
488
+ миграции; продолжить миграцию можно и без них, но с MCP она точнее;
489
+ 3. не утверждать, что MCP работает, если инструменты не видны, и не молча
490
+ продолжать без MCP там, где он мог бы дать данные о проекте.
491
+
492
+ ## Шаг 10. Проверка результата
493
+
494
+ ```bash
495
+ amxb validate # манифест валиден
496
+ amxb deps-tree # все deps резолвятся (приватные — по токенам)
497
+ amxb build --dry-run # план: компилятор, deps, архив, ini
498
+ amxb build # полная сборка
499
+ ```
500
+
501
+ Критерии успеха:
502
+
503
+ - все плагины компилируются (`OK` по каждому);
504
+ - ini-файл(ы) сгенерированы, если включены;
505
+ - содержимое архива структурно совпадает со старым пакетом (если он был).
506
+
507
+ Как читать ошибки:
508
+
509
+ - `fatal error 100: cannot read from file: "<путь>.sma"` — сначала проверить
510
+ **сам файл**, а не окружение:
511
+ 1. файл существует (`ls -l <путь>.sma`);
512
+ 2. файл **не пустой** — пустой `.sma` (0 байт) даёт ровно ту же ошибку.
513
+ Пустые `.sma` не должны попадать в сборку: `amxb init --plugin` создаёт
514
+ пустую заготовку, и если она осталась в `scripting/` — это ложный след.
515
+ Такие файлы удаляем или исключаем (`plugins:` → `enabled: false`).
516
+ 3. Если файлы на месте и непустые, а ошибка **для всех** файлов при верных
517
+ путях → проблема окружения, не манифеста (например, WSL: amxxpc
518
+ 32-битный и не читает `/mnt/c`).
519
+ - **WSL (`/mnt/c`) — готовый рецепт полной проверки.** В WSL из `/mnt/c`
520
+ работают `validate`, `deps-tree`, `--dry-run` (компилятор не запускается),
521
+ но полный `amxb build` падает на 32-битном amxxpc. Полную сборку выполняем
522
+ из нативной Linux-папки — кэш общий, повторно ничего не качается:
523
+ ```bash
524
+ mkdir -p ~/amxb-build-check && cp -r amxmodx assets amxbuild.yml README.md ~/amxb-build-check/
525
+ cd ~/amxb-build-check && amxb build
526
+ # кэш ~/.cache/amxx-builder общий для всех папок — deps/компилятор уже там
527
+ ```
528
+ Финальную сборку из `/mnt/c` пользователь выполняет на Windows (`build.bat`)
529
+ или в CI; в WSL проверяются `validate`/`deps-tree`/`--dry-run`.
530
+ - `unable to open include file` для конкретного инклуда → не хватает dep
531
+ или неверный путь до include внутри dep.
532
+ - `undefined symbol/native` → несовпадение версии API: include резолвится,
533
+ но код использует другой неймспейс/префиксы (`MOD_*` vs `NS_MOD_*`) —
534
+ значит, выбрана не та линия релизов зависимости (см. «Формат deps»).
535
+ Уточнить у пользователя корректную версию.
536
+
537
+ ## Чек-лист
538
+
539
+ - [ ] amxb доступен (установлен глобально или вызывается через npx)
540
+ - [ ] НЕ читались файлы/репозитории вне папки целевого проекта (в т.ч. соседние проекты, «для понимания»)
541
+ - [ ] Входные данные собраны только из папки проекта: CI/workflow разобран первым (репо, версии, include-пути)
542
+ - [ ] Внешние include сопоставлены с источниками; не найденные после разбора CI/скриптов — согласованы с пользователем
543
+ - [ ] При «репо не найден» (404): сначала проверены токены из `.env`, затем у пользователя запрошен токен/уточнение имени — вывод «репо не существует» без подтверждения не делался
544
+ - [ ] Дефолтная раскладка проверена `amxb build --dry-run`; вопросы про структуру — только при отличии от дефолта
545
+ - [ ] `amxbuild.yml`: name, deps, ini/постфикс при необходимости; `amxmodx.version` не указан (последний компилятор), кроме AMXX <= 1.8.3
546
+ - [ ] Версии deps: последняя проверена сборкой → иначе версия из проекта/CI → иначе вопрос; в манифесте конкретные теги, не «latest»
547
+ - [ ] Приватные deps: 404 обработан как «приватный/опечатка» → `github.tokens` + `.env` (в .gitignore)
548
+ - [ ] `.gitignore`: шаблон amxb + сохранены специфичные строки проекта
549
+ - [ ] Старые скрипты удалены; существовавший `build.bat`/`build.sh` заменён тонким вручную
550
+ - [ ] Пустые/заглушечные `.sma` (в т.ч. от `init --plugin`) не попадают в сборку
551
+ - [ ] CI обновлён: ветки/триггеры сверены со старым CI и дефолтной веткой; README при `pack=false` не копируется вручную
552
+ - [ ] MCP (если делали): файл создан; если инструменты `amxx-dep-resolver` не подхватились — пользователю сказано перезапустить opencode (перезапуск нужен агенту для доступа к инструментам)
553
+ - [ ] `amxb validate` + `deps-tree` + `build` проходят (в WSL из `/mnt/c` — полная сборка из нативной Linux-папки)
554
+ - [ ] Архив структурно совпадает со старым пакетом
package/src/build-plan.js CHANGED
@@ -41,8 +41,10 @@ function buildPlanData(manifest, options = {}) {
41
41
  })),
42
42
  globalDeps: manifest.globalDeps.map((d) => ({
43
43
  source: d.source,
44
- repo: d.repo,
44
+ repo: d.source === 'fungun' ? null : d.repo,
45
45
  ref: d.ref,
46
+ id: d.id ?? null,
47
+ url: d.url ?? null,
46
48
  include_path: d.include_path || null,
47
49
  asset: d.asset ?? null,
48
50
  })),
package/src/cli.js CHANGED
@@ -248,6 +248,7 @@ program
248
248
  .option('--opencode', 'Create .opencode/opencode.json with MCP config (amxb mcp)')
249
249
  .option('--deploy', 'Create .env with deploy stubs (AMXB_DEPLOY_*)')
250
250
  .option('--script', 'Create build.bat and build.sh quick-build scripts')
251
+ .option('-f, --force', 'Overwrite existing files instead of skipping them')
251
252
  .option('-i, --interactive', 'Interactive mode with prompts')
252
253
  .action(async (options) => {
253
254
  try {
@@ -56,7 +56,10 @@ function printNode(node, prefix, isLast) {
56
56
  const childPrefix = isLast ? ' ' : '│ ';
57
57
 
58
58
  const tag = buildNodeTag(node);
59
- logger.dim(`${prefix}${connector}${node.repo}@${node.ref || 'HEAD'}${tag}`);
59
+ const label = node.source === 'fungun'
60
+ ? `[fungun] plugin #${node.id}`
61
+ : `${node.repo}@${node.ref || 'HEAD'}`;
62
+ logger.dim(`${prefix}${connector}${label}${tag}`);
60
63
 
61
64
  if (node.cycle) return;
62
65
 
@@ -28,6 +28,10 @@ function printDryRun(manifest) {
28
28
  if (manifest.globalDeps.length) {
29
29
  logger.info(`\nGlobal deps (${manifest.globalDeps.length}):`);
30
30
  for (const d of manifest.globalDeps) {
31
+ if (d.source === 'fungun') {
32
+ logger.dim(` [fungun] plugin #${d.id} (${d.url})`);
33
+ continue;
34
+ }
31
35
  const src = d.source === 'release' ? 'release' : 'git';
32
36
  logger.dim(` [${src}] ${d.repo}@${d.ref}${d.include_path ? ':' + d.include_path : ''}`);
33
37
  }