@gian-tiaga/eda 3.13.0 → 3.14.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.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # 🌊 eda
2
2
 
3
3
  > Поточная разработка с AI-агентами — на русском языке.
4
- > Пакет готовых скилов и специализированных агентов для **Claude Code** и **Codex CLI**, которые ведут тебя через весь цикл: от исследования до коммита.
4
+ > Пакет готовых скилов и специализированных агентов для **Claude Code**, **Codex CLI** и **OpenCode V2**, которые ведут тебя через весь цикл: от исследования до коммита.
5
5
 
6
6
  `eda` — это двадцать три скила и пакетные агенты для повторяемых ролей. Скил отвечает за пользовательский workflow и оркестрацию, агент — за изолированную работу с закреплённой моделью и ограничениями. Вместо того чтобы каждый раз объяснять модели, как делать ревью или как исполнять план, ты говоришь одно слово — и она следует чёткой инструкции на русском, со всеми правилами, проверками и артефактами в нужных папках.
7
7
 
@@ -21,9 +21,11 @@ npm install -g @gian-tiaga/eda
21
21
  eda init
22
22
  ```
23
23
 
24
- Установщик покажет чекбоксы: стрелками выбираешь среду, пробелом отмечаешь, Enter продолжает. Можно поставить Claude Code, Codex CLI или обе среды сразу. Устанавливаются и скилы, и агенты. Если `docs/settings.yaml` ещё нет, установщик предложит настройки скилов, а для каждой проверки `plan-review` и `review` отдельно спросит режим и модели Claude/Codex. В неинтерактивном запуске записывается сбалансированный профиль. В конце `eda init` отдельно пишет, какие скилы и агенты реально установлены или изменились.
24
+ Установщик покажет чекбоксы: стрелками выбираешь среду, пробелом отмечаешь, Enter продолжает. Можно выбрать любую комбинацию Claude Code, Codex CLI и OpenCode. Устанавливаются и скилы, и агенты. Если `docs/settings.yaml` ещё нет, установщик предложит настройки скилов и проверок review, включая модели Claude/Codex. Без интерактивного терминала устанавливаются все три среды и записывается сбалансированный профиль. В конце `eda init` отдельно пишет, какие скилы и агенты реально установлены или изменились.
25
25
 
26
- `eda update` сначала переносит старые рабочие каталоги из `docs/<тип>/` в `docs/artifacts/<тип>/`, затем обновляет настройки, скилы и агентов. Содержимое файлов при переносе не меняется. Если один относительный путь уже существует в новой структуре, новый файл сохраняется, а старый удаляется.
26
+ Чтобы добавить OpenCode или другую платформу в проект с уже установленным EDA, запусти `eda update`. В интерактивном терминале команда при каждом запуске показывает все платформы: установленные уже отмечены, остальные можно добавить. Установленные снимать нельзя — `update` их обновляет, а не удаляет. Enter без изменений обновляет текущий набор. Выбор фиксируется установленными каталогами и manifest каждой платформы, поэтому при следующем запуске она уже отмечена; отдельный список в `docs/settings.yaml` не нужен. Без TTY новые платформы не добавляются. Если ни одной среды ещё нет, сначала нужен `eda init`. У `update-all` выбор платформ не меняется: он обновляет только уже найденные среды.
27
+
28
+ После выбора платформ `eda update` переносит старые рабочие каталоги из `docs/<тип>/` в `docs/artifacts/<тип>/`, затем обновляет настройки, скилы и агентов. Содержимое файлов при переносе не меняется. Если один относительный путь уже существует в новой структуре, новый файл сохраняется, а старый удаляется.
27
29
 
28
30
  Полный валидный v3-конфиг сохраняется, пока совпадает с актуальным шаблоном; если в пакете появились новые значения или подсказки, файл молча перерисовывается с сохранением ваших значений. Старый, неизвестный или неполный файл разбирается как YAML: известные значения переносятся, в TTY спрашиваются только отсутствующие, затем профиль целиком атомарно записывается как v3. Без TTY используются перенесённые значения и defaults.
29
31
 
@@ -33,7 +35,7 @@ eda init
33
35
  eda update-all ~/Code
34
36
  ```
35
37
 
36
- `eda update-all` ищет проекты в указанной директории, её прямых подпапках и подпапках второго уровня. Проект считается найденным, если в нём уже есть установленный скил, агент или manifest `eda` для Claude/Codex. В каждом проекте команда переносит рабочие артефакты, затем предлагает настроить один общий профиль для всех проектов или выбрать `skip`. В режиме `skip` актуальный полный v3 сохраняется, устаревший по шаблону перерисовывается, старые и неполные конфиги индивидуально переносятся, отсутствующие создаются с defaults. Без TTY автоматически используется `skip`. Ошибка одного проекта не останавливает остальные.
38
+ `eda update-all` ищет проекты в указанной директории, её прямых подпапках и подпапках второго уровня. Проект считается найденным, если в нём уже есть установленный скил, агент или manifest `eda` для Claude/Codex/OpenCode. Служебные каталоги, включая `.opencode`, и symlink-директории не обходятся. В каждом проекте команда переносит рабочие артефакты, затем предлагает настроить один общий профиль для всех проектов или выбрать `skip`. В режиме `skip` актуальный полный v3 сохраняется, устаревший по шаблону перерисовывается, старые и неполные конфиги индивидуально переносятся, отсутствующие создаются с defaults. Без TTY автоматически используется `skip`. Ошибка одного проекта не останавливает остальные.
37
39
 
38
40
  ---
39
41
 
@@ -54,7 +56,7 @@ eda update-all ~/Code
54
56
  | `eda-plan-execute` | Управляет выполнением плана по фазам в режиме `subagents`, `main` или `auto`. В `subagents` каждую фазу целиком выполняет отдельный изолированный субагент: он читает план, правила, архитектуру и только business/reference-карточки затронутого поведения и компонентов. Фазы проходят строго последовательно и принимаются независимыми проверяющими. В `main` весь план по тем же правилам выполняет и проверяет основной агент в одном контексте, а `auto` выбирает режим по плану. Отметки подтверждённых задач и журнал ведёт основной агент | `docs/artifacts/executions/` |
55
57
  | `eda-just-do-it` | Сразу выполняет задачу из текста рядом с вызовом: определяет scope, загружает только необходимые проектные инструкции, business/reference-карточки, код и тесты, вносит нужные изменения и проверяет результат. Не подменяет выполнение планом и не делает git- или внешние действия без прямого указания | `docs/artifacts/just-do-it/` |
56
58
  | `eda-fix` | Делает обычные фиксы по короткому контексту, баг-репорту или плану. Перед правками читает правила, архитектуру и только применимые карточки, добавляет нужные тесты, прогоняет проверки и сохраняет историю правок | `docs/artifacts/fixes/` |
57
- | `eda-review` | Оркестрирует отдельные read-only проверки корректности, архитектуры, правил, references, business-правил, плана, качества, тестов, безопасности, производительности, frontend/UI/UX, API, БД, документации и предыдущих ревью. Сохраняет только доказанные дефекты, которые обязательно исправить; косметические и спекулятивные предложения отбрасывает. Любая находка даёт `changes-required`, а чистое ревью — `clean` и 100/100. Без аргументов ревьюит `git diff HEAD` | `docs/artifacts/reviews/` |
59
+ | `eda-review` | Оркестрирует отдельные read-only проверки корректности, архитектуры, правил, references, business-правил, плана, качества, тестов, безопасности, производительности, frontend/UI/UX, API, БД, документации и предыдущих ревью. Для PR/MR переиспользует результаты CI текущего head и не запускает локально уже завершённые там тесты, линтеры, typecheck, сборку и аналогичные проверки; смысловое ревью diff при этом выполняется полностью. Сохраняет только доказанные дефекты, которые обязательно исправить; косметические и спекулятивные предложения отбрасывает. Любая находка даёт `changes-required`, а чистое ревью — `clean` и 100/100. Без аргументов ревьюит `git diff HEAD` | `docs/artifacts/reviews/` |
58
60
  | `eda-fix-by-review` | Применяет все находки из ревью с учётом правил, архитектуры и применимых карточек. Спрашивает только при неоднозначности, конфликте, расширении scope или рискованном действии. В конце ссылается на отчёт прямо в файле ревью | `docs/artifacts/review-fixes/` |
59
61
  | `eda-manual-test` | Делает ручную проверку после изменений. По умолчанию проверка полная: разбирает план или другой артефакт по разделам, дополняет его фактическим `git diff HEAD` и закрывает матрицу областей — API, база данных, консольные команды, интерфейс, алгоритмы, фон и интеграции, права и роли, конфигурация, логи, регрессии рядом. Область нельзя закрыть чтением кода или зелёными автотестами; незакрытая область даёт `blocked`. Быстрый smoke включается настройкой `manual-test.depth` или словом в запросе. Применимая business-карточка может добавить только относящийся к задаче инвариант или сценарий. Не правит код, не пишет автотесты и не коммитит | `docs/artifacts/manual-tests/` |
60
62
  | `eda-polish` | Оркестрирует доводку изменений: запускает полное `eda-review`, затем `eda-fix-by-review` и повторяет цикл до `clean` и отсутствия находок либо до лимита 5 итераций. Режим цикла задаёт `polish.execution`: в `main` оба контракта выполняет основной агент без субагентов, в `subagents` каждый шаг получает отдельный изолированный контекст. Все находки исправляются, score остаётся справочным. Не коммитит | `docs/artifacts/reviews/`, `docs/artifacts/review-fixes/` |
@@ -96,6 +98,7 @@ eda update-all ~/Code
96
98
  | `eda-aim-planner` | `sonnet`, workspace-write | `gpt-5.6-terra`, workspace-write | Задаёт вопросы, формулирует простой проверяемый список и создаёт файл только после подтверждения человека |
97
99
  | `eda-aim-executor` | `opus`, workspace-write | `gpt-5.6-sol`, workspace-write | Выполняет назначенные пункты, при необходимости запрашивает установленные скилы для запуска в изолированных субагентах и дополняет общий журнал |
98
100
  | `eda-aim-verifier` | `opus`, workspace-write | `gpt-5.6-sol`, workspace-write | Независимо проверяет каждый критерий, отмечает подтверждённые цели и возвращает пробелы на новый круг выполнения |
101
+ | `eda-skill-runner` | `opus`, workspace-write | `gpt-5.6-sol`, workspace-write | Выполняет один установленный скил в изолированной сессии; в OpenCode сохраняет возможность вложенных субагентов |
99
102
  | `eda-commit-executor` | `sonnet` | `gpt-5.6-terra` | Одним вызовом собирает git-контекст, пишет сообщения коммитов и PR по запросу, артефактам и фактическим изменениям, коммитит состав одним коммитом или по группам при `split` и выполняет явно разрешённые push/PR/merge-действия |
100
103
  | `eda-worktree-executor` | `haiku` | `gpt-5.6-luna` | Создаёт соседний worktree и одноимённую ветку, не изменяя файлы проекта |
101
104
  | `eda-merge-worktree-executor` | `haiku` | `gpt-5.6-luna` | Мержит ветку из соседнего worktree в текущую без cleanup и ручного разрешения конфликтов |
@@ -119,6 +122,16 @@ eda update-all ~/Code
119
122
 
120
123
  Для Claude Code установленный `eda-commit` получает `model: sonnet`, а `eda-worktree` и `eda-merge-worktree` — `model: haiku`. В Codex у скилов пока нет отдельного per-skill model override: основной поток остаётся на выбранной пользователем модели, но он выполняет только лёгкую оркестрацию; содержательная работа `eda-commit` закреплена за `gpt-5.6-terra`, а worktree-операции — за `gpt-5.6-luna`.
121
124
 
125
+ ### OpenCode V2
126
+
127
+ Вызывай скилы через `@eda-plan`, `@eda-review` и другие точные ID. OpenCode-копии содержат платформенный адаптер: вложенные скилы загружаются через `skill`, агенты запускаются через `subagent`, вопросы задаются через `question`. Изолированные шаги оркестраторов выполняет `eda-skill-runner`, а не встроенный general, у которого запрещены вложенные субагенты. Режимы `main` / `subagents` сохраняют обычные границы.
128
+
129
+ Все OpenCode-агенты по умолчанию наследуют модель сессии: колонки Claude/Codex в таблице выше и соответствующие поля моделей в `docs/settings.yaml` к OpenCode не применяются. В каноническом `agent.json` можно задать необязательное `models.opencode` в формате `provider/model#variant`; variant необязателен. Модель можно настроить средствами OpenCode. Per-skill модель не назначается. При явном runtime override, который хост не поддерживает, скил возвращает `blocked`, а не молча заменяет модель.
130
+
131
+ Read-only роли используют V2 `permissions`: разрешены чтение, поиск и web-инструменты, запрещены shell, редакторы, MCP и субагенты. Оркестратор передаёт им diff и git/CI-контекст; такие агенты сами не запускают тесты. Git-write роли не получают редакторов, но shell наследует разрешения пользователя; workspace-write также не переопределяет пользовательские разрешения. Это правила инструментов, не отдельный OS sandbox.
132
+
133
+ Установщик не меняет `opencode.json(c)` и не требует перезапуска OpenCode. Скилы `.opencode/skills` имеют приоритет над совместимыми копиями `.claude/skills` и `.agents/skills` с тем же ID. Если нет обязательного инструмента, разрешения или нативного Plan Mode, workflow сообщает конкретный блокер. В strict из OpenCode нужно явно выбрать внешний Claude Code или Codex CLI либо ответить на вопрос скила.
134
+
122
135
  ---
123
136
 
124
137
  ## 🚩 Флаги скилов
@@ -498,8 +511,8 @@ discover-automations может запускаться от review, fix-by-revie
498
511
  ## 💻 Команды
499
512
 
500
513
  ```bash
501
- eda init # выбрать Claude Code / Codex / обе среды и установить пакет
502
- eda update # дополнить недостающие настройки и обновить скилы и агенты
514
+ eda init # выбрать Claude Code / Codex / OpenCode и установить пакет
515
+ eda update # предложить добавить платформы, обновить настройки, скилы и агентов
503
516
  eda update-all [dir] # сохранить конфиги или настроить общий и обновить проекты на глубине 2
504
517
  eda --version # показать версию установленного пакета
505
518
  eda --help # справка
@@ -527,15 +540,15 @@ eda update-all ~/Code
527
540
 
528
541
  Исходники агентов лежат в `agents/<agent>/agent.json` и `prompt.md`. Установщик рендерит единый агентный контракт в нативный формат выбранной среды.
529
542
 
530
- | Компонент | Claude Code | Codex CLI |
531
- |---|---|---|
532
- | Скил | `.claude/skills/<skill>/SKILL.md` | `.agents/skills/<skill>/SKILL.md` |
533
- | Агент | `.claude/agents/<agent>.md` | `.codex/agents/<agent>.toml` |
534
- | Manifest владения | `.claude/eda-manifest.json` | `.codex/eda-manifest.json` |
543
+ | Компонент | Claude Code | Codex CLI | OpenCode V2 |
544
+ |---|---|---|---|
545
+ | Скил | `.claude/skills/<skill>/SKILL.md` | `.agents/skills/<skill>/SKILL.md` | `.opencode/skills/<skill>/SKILL.md` |
546
+ | Агент | `.claude/agents/<agent>.md` | `.codex/agents/<agent>.toml` | `.opencode/agents/<agent>.md` |
547
+ | Manifest владения | `.claude/eda-manifest.json` | `.codex/eda-manifest.json` | `.opencode/eda-manifest.json` |
535
548
 
536
549
  `eda update` переносит известные старые каталоги артефактов, сохраняет актуальный полный v3-конфиг как есть, устаревший по шаблону перерисовывает с сохранением значений, а старый или неполный умно переносит и целиком нормализует; затем идемпотентно синхронизирует обе группы компонентов и отдельно показывает реально изменившиеся скилы и агенты. Manifest хранит только идентификаторы компонентов, которыми владеет `eda`: retired-компоненты удаляются, а чужие скилы и агенты остаются нетронутыми. `eda update-all [dir]` выполняет тот же перенос артефактов в каждом проекте, затем либо записывает общий профиль, либо в `skip` сохраняет актуальный полный v3, перерисовывает устаревший по шаблону и переносит остальные настройки. При миграции Codex установщик переносит актуальные скилы в `.agents/skills/` и удаляет принадлежащие `eda` старые копии `.codex/skills/<skill>/` и `.codex/skills/<skill>.md`, не затрагивая чужие компоненты.
537
550
 
538
- Установленные копии не являются источником правды. Локальные изменения внутри `.claude/skills/eda-*`, `.agents/skills/eda-*`, `.claude/agents/eda-*` или `.codex/agents/eda-*` будут заменены следующим обновлением.
551
+ Установленные копии не являются источником правды. Локальные изменения внутри `.claude/skills/eda-*`, `.agents/skills/eda-*`, `.opencode/skills/eda-*`, `.claude/agents/eda-*`, `.codex/agents/eda-*` или `.opencode/agents/eda-*` будут заменены следующим обновлением.
539
552
 
540
553
  `AGENTS.md` (как и любые другие файлы в корне проекта) установщик **не трогает**. Codex автоматически обнаруживает проектные скилы в `.agents/skills/`.
541
554
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## Вход и работа
6
6
 
7
- В task-сообщении получи рабочую директорию, `TARGET`, `PLAN_FILE`, `REFERENCE_FILES`, `MODEL` и краткий `REVIEW_CONTEXT`. Прочитай diff, изменённое поведение, существующие тесты и проектные команды тестирования. Запускай только безопасные read-only проверки, если это нужно для подтверждения; не меняй snapshots и другие tracked-файлы.
7
+ В task-сообщении получи рабочую директорию, `TARGET`, `PLAN_FILE`, `REFERENCE_FILES`, `MODEL`, краткий `REVIEW_CONTEXT` и `CI_CONTEXT`. Прочитай diff, изменённое поведение, существующие тесты и проектные команды тестирования. Запускай только безопасные read-only проверки, если это нужно для подтверждения; не меняй snapshots и другие tracked-файлы. Для PR/MR не повторяй тест, линтер, typecheck, сборку или другую автоматическую команду, уже завершённую в CI на текущем head: используй переданный результат и лог CI как доказательство. Это не отменяет анализ полноты и корректности самих тестов.
8
8
 
9
9
  Не требуй тест на каждую строку. Находка должна описывать конкретную регрессию, которую текущие тесты не поймают, либо ошибку в самих тестах. Не дублируй функциональную проблему, если можешь сформулировать только тестовый пробел. Не правь файлы.
10
10
 
@@ -0,0 +1,14 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "name": "eda-skill-runner",
4
+ "description": "Выполняет один установленный eda-скил в изолированной сессии с сохранением его режима и границ ответственности.",
5
+ "models": {
6
+ "claude": "opus",
7
+ "codex": "gpt-5.6-sol"
8
+ },
9
+ "reasoning": {
10
+ "claude": "high",
11
+ "codex": "high"
12
+ },
13
+ "access": "workspace-write"
14
+ }
@@ -0,0 +1,11 @@
1
+ # Роль
2
+
3
+ Выполни один установленный eda-скил в изолированной сессии. Получи от оркестратора рабочую директорию, точный ID скила, исходный запрос, аргументы, применимые overrides, пути входных артефактов и требуемый результат.
4
+
5
+ Загрузи указанный скил нативным инструментом хоста и следуй его инструкции целиком. Не заменяй его своей процедурой. Режим исполнения и границы ответственности обязательны; запускай вложенных агентов только когда это предписывает выбранный скил. Не делай git/publish-действий без прямого разрешения пользователя, переданного оркестратором.
6
+
7
+ Если вход или возможности хоста недостаточны, верни `status: blocked` с одним конкретным вопросом или техническим блокером. Не обходи разрешения и не меняй режим исполнения.
8
+
9
+ ## Результат
10
+
11
+ Верни результат по контракту вызванного скила, пути созданных артефактов и фактически выполненные проверки. Если скил не задаёт машинный формат, верни YAML с `status: completed | blocked`, `summary`, `artifacts`, `checks`, `question`. Не выдавай непроведённые проверки за успешные.
package/bin/cli.js CHANGED
@@ -6,11 +6,11 @@ const require = createRequire(import.meta.url);
6
6
  const { version } = require('../package.json');
7
7
  const cmd = process.argv[2];
8
8
 
9
- const HELP = `eda — установка и обновление скилов и агентов eda-* для Claude Code и Codex CLI.
9
+ const HELP = `eda — установка и обновление скилов и агентов eda-* для Claude Code, Codex CLI и OpenCode.
10
10
 
11
11
  Использование:
12
- eda init — выбрать целевые среды (Claude / Codex / обе) и установить пакет
13
- eda update — перенести артефакты и настройки, обновить текущий проект
12
+ eda init — выбрать целевые среды (Claude / Codex / OpenCode) и установить пакет
13
+ eda update — предложить добавить платформы и обновить текущий проект
14
14
  eda update-all [dir] — перенести артефакты и настройки найденных проектов
15
15
  eda --version — показать версию
16
16
  eda --help — показать эту справку
package/lib/catalog.js CHANGED
@@ -89,6 +89,10 @@ function validateAgent(directoryName, agent, prompt) {
89
89
  if (!agent.models?.claude || !agent.models?.codex) {
90
90
  throw new Error(`Агент ${directoryName}: нужны модели Claude и Codex.`);
91
91
  }
92
+ if (agent.models.opencode !== undefined
93
+ && (typeof agent.models.opencode !== 'string' || !/^[^\s/#]+\/[^\s/#]+(?:#[^\s#]+)?$/.test(agent.models.opencode))) {
94
+ throw new Error(`Агент ${directoryName}: models.opencode должен иметь формат provider/model#variant.`);
95
+ }
92
96
  if (!['read-only', 'workspace-write', 'git-write'].includes(agent.access)) {
93
97
  throw new Error(`Агент ${directoryName}: access должен быть read-only, workspace-write или git-write.`);
94
98
  }
package/lib/install.js CHANGED
@@ -11,6 +11,8 @@ import { renderClaudeAgent } from './renderers/claude-agent.js';
11
11
  import { renderClaudeSkill } from './renderers/claude-skill.js';
12
12
  import { renderCodexAgent } from './renderers/codex-agent.js';
13
13
  import { renderCodexSkill } from './renderers/codex-skill.js';
14
+ import { renderOpenCodeAgent } from './renderers/opencode-agent.js';
15
+ import { renderOpenCodeSkill } from './renderers/opencode-skill.js';
14
16
 
15
17
  const __filename = fileURLToPath(import.meta.url);
16
18
  const __dirname = path.dirname(__filename);
@@ -37,10 +39,11 @@ const ARTIFACT_DIRECTORY_NAMES = [
37
39
  ];
38
40
  const TARGET_CHOICES = [
39
41
  { value: 'claude', label: 'Claude Code', dir: '.claude/skills/' },
40
- { value: 'codex', label: 'Codex CLI', dir: '.agents/skills/' }
42
+ { value: 'codex', label: 'Codex CLI', dir: '.agents/skills/' },
43
+ { value: 'opencode', label: 'OpenCode', dir: '.opencode/skills/' }
41
44
  ];
42
45
  const UPDATE_ALL_MAX_DEPTH = 2;
43
- const UPDATE_ALL_SKIP_DIRS = new Set(['.git', '.agents', '.claude', '.codex', 'node_modules']);
46
+ const UPDATE_ALL_SKIP_DIRS = new Set(['.git', '.agents', '.claude', '.codex', '.opencode', 'node_modules']);
44
47
  const RETIRED_SKILLS = [
45
48
  'eda-research',
46
49
  'eda-review-check',
@@ -540,13 +543,14 @@ export async function init({ cwd, input = process.stdin, output = process.stdout
540
543
  await syncPackage(cwd, targets, output, { action: 'install' });
541
544
  }
542
545
 
543
- export async function update({ cwd, input = process.stdin, output = process.stdout }) {
544
- const targets = await detectTargets(cwd);
545
- if (targets.length === 0) {
546
+ export async function update({ cwd, input = process.stdin, output = process.stdout, checkboxPrompt = checkbox }) {
547
+ const installedTargets = await detectTargets(cwd);
548
+ if (installedTargets.length === 0) {
546
549
  output.write('Не нашёл установленного пакета eda в этом проекте. Запусти `eda init`.\n');
547
550
  return;
548
551
  }
549
- output.write(`Найдены установленные среды: ${targets.join(', ')}\n`);
552
+ output.write(`Найдены установленные среды: ${installedTargets.join(', ')}\n`);
553
+ const targets = await askUpdateTargets({ installedTargets, input, output, checkboxPrompt });
550
554
  await migrateArtifactDirectories(cwd, output);
551
555
  await configureSettings(cwd, { input, output });
552
556
  await syncPackage(cwd, targets, output, { action: 'update' });
@@ -677,7 +681,7 @@ export async function updateAll({
677
681
 
678
682
  export async function askTargets({ input = process.stdin, output = process.stdout } = {}) {
679
683
  if (!input.isTTY || !output.isTTY) {
680
- output.write('Нет интерактивного терминала — устанавливаю Claude Code и Codex CLI.\n');
684
+ output.write('Нет интерактивного терминала — устанавливаю Claude Code, Codex CLI и OpenCode.\n');
681
685
  return TARGET_CHOICES.map(choice => choice.value);
682
686
  }
683
687
 
@@ -695,6 +699,30 @@ export async function askTargets({ input = process.stdin, output = process.stdou
695
699
  });
696
700
  }
697
701
 
702
+ export async function askUpdateTargets({
703
+ installedTargets,
704
+ input = process.stdin,
705
+ output = process.stdout,
706
+ checkboxPrompt = checkbox
707
+ }) {
708
+ if (!input.isTTY || !output.isTTY) return [...installedTargets];
709
+
710
+ const selected = await checkboxPrompt({
711
+ message: 'Какие платформы добавить? Установленные уже отмечены и будут обновлены.',
712
+ instructions: 'Space — добавить платформу, Enter — обновить выбранные; установленные снимать нельзя',
713
+ choices: TARGET_CHOICES.map(choice => ({
714
+ name: `${choice.label} (${choice.dir})${installedTargets.includes(choice.value) ? ' — установлена' : ''}`,
715
+ value: choice.value,
716
+ checked: installedTargets.includes(choice.value)
717
+ })),
718
+ validate: choices => installedTargets.every(target => choices.some(choice => choice.value === target))
719
+ || 'Установленные платформы нужно оставить отмеченными: update их не удаляет.'
720
+ }, { input, output });
721
+
722
+ return TARGET_CHOICES.map(choice => choice.value)
723
+ .filter(target => installedTargets.includes(target) || selected.includes(target));
724
+ }
725
+
698
726
  export async function askUpdateAllSettingsMode({
699
727
  input = process.stdin,
700
728
  output = process.stdout,
@@ -884,8 +912,9 @@ async function askAgentSettings({ input, output, selectPrompt, definitions, seed
884
912
 
885
913
  async function detectTargets(cwd) {
886
914
  const targets = [];
887
- if (await targetStructureExists(cwd, 'claude')) targets.push('claude');
888
- if (await targetStructureExists(cwd, 'codex')) targets.push('codex');
915
+ for (const { value } of TARGET_CHOICES) {
916
+ if (await targetStructureExists(cwd, value)) targets.push(value);
917
+ }
889
918
  return targets;
890
919
  }
891
920
 
@@ -893,8 +922,9 @@ async function detectTargets(cwd) {
893
922
  // или manifest владения. Одних пустых/чужих каталогов skills и agents недостаточно.
894
923
  async function detectInstalledTargets(cwd) {
895
924
  const targets = [];
896
- if (await hasInstalledPackage(cwd, 'claude')) targets.push('claude');
897
- if (await hasInstalledPackage(cwd, 'codex')) targets.push('codex');
925
+ for (const { value } of TARGET_CHOICES) {
926
+ if (await hasInstalledPackage(cwd, value)) targets.push(value);
927
+ }
898
928
  return targets;
899
929
  }
900
930
 
@@ -908,7 +938,7 @@ async function targetStructureExists(cwd, target) {
908
938
 
909
939
  async function hasInstalledPackage(cwd, target) {
910
940
  const { skillsDir, agentsDir, manifestPath, legacySkillsDir } = getTargetPaths(cwd, target);
911
- const agentExtension = target === 'claude' ? '.md' : '.toml';
941
+ const agentExtension = target === 'codex' ? '.toml' : '.md';
912
942
  return await fileExists(manifestPath)
913
943
  || await hasInstalledSkill(skillsDir)
914
944
  || (legacySkillsDir !== null && await hasInstalledSkill(legacySkillsDir))
@@ -952,13 +982,9 @@ async function syncPackage(cwd, targets, output = process.stdout, { action = 'up
952
982
 
953
983
  const changedSkills = new Set();
954
984
  const changedAgents = new Set();
955
- if (targets.includes('claude')) {
956
- const result = await installTarget(cwd, 'claude', skills, agents, packageVersion, output);
957
- for (const skillName of result.changedSkills) changedSkills.add(skillName);
958
- for (const agentName of result.changedAgents) changedAgents.add(agentName);
959
- }
960
- if (targets.includes('codex')) {
961
- const result = await installTarget(cwd, 'codex', skills, agents, packageVersion, output);
985
+ for (const { value } of TARGET_CHOICES) {
986
+ if (!targets.includes(value)) continue;
987
+ const result = await installTarget(cwd, value, skills, agents, packageVersion, output);
962
988
  for (const skillName of result.changedSkills) changedSkills.add(skillName);
963
989
  for (const agentName of result.changedAgents) changedAgents.add(agentName);
964
990
  }
@@ -1592,10 +1618,9 @@ async function installTarget(cwd, target, skills, agents, packageVersion, output
1592
1618
 
1593
1619
  if (agents.length > 0) await fs.mkdir(agentsDir, { recursive: true });
1594
1620
  for (const agent of agents) {
1595
- const extension = target === 'claude' ? '.md' : '.toml';
1596
- const rendered = target === 'claude'
1597
- ? renderClaudeAgent(agent, agent.prompt)
1598
- : renderCodexAgent(agent, agent.prompt);
1621
+ const extension = target === 'codex' ? '.toml' : '.md';
1622
+ const renderAgent = { claude: renderClaudeAgent, codex: renderCodexAgent, opencode: renderOpenCodeAgent }[target];
1623
+ const rendered = renderAgent(agent, agent.prompt);
1599
1624
  const changed = await writeManagedFile(path.join(agentsDir, `${agent.name}${extension}`), rendered);
1600
1625
  if (changed) changedAgents.push(agent.name);
1601
1626
  }
@@ -1611,7 +1636,7 @@ async function installTarget(cwd, target, skills, agents, packageVersion, output
1611
1636
  agents: agents.map(agent => agent.name)
1612
1637
  });
1613
1638
 
1614
- const label = target === 'claude' ? 'Claude Code' : 'Codex CLI';
1639
+ const label = TARGET_CHOICES.find(choice => choice.value === target).label;
1615
1640
  const location = skillsDir === path.join(targetRoot, 'skills')
1616
1641
  ? targetRoot
1617
1642
  : `${skillsDir}; ${agentsDir}`;
@@ -1638,9 +1663,8 @@ async function buildSkillFiles(skill, target) {
1638
1663
  if (!entrypoint) throw new Error(`У скила ${skill.name} нет SKILL.md.`);
1639
1664
 
1640
1665
  const content = entrypoint.toString('utf8');
1641
- const rendered = target === 'claude'
1642
- ? renderClaudeSkill(content, skill.config)
1643
- : renderCodexSkill(content, skill.config);
1666
+ const renderSkill = { claude: renderClaudeSkill, codex: renderCodexSkill, opencode: renderOpenCodeSkill }[target];
1667
+ const rendered = renderSkill(content, skill.config);
1644
1668
  files.set('SKILL.md', Buffer.from(rendered));
1645
1669
  return files;
1646
1670
  }
@@ -1828,7 +1852,7 @@ async function removeRetiredComponents(skillsDir, agentsDir, target, previousMan
1828
1852
  await fs.rm(path.join(skillsDir, `${skillName}.md`), { force: true });
1829
1853
  }
1830
1854
 
1831
- const extension = target === 'claude' ? '.md' : '.toml';
1855
+ const extension = target === 'codex' ? '.toml' : '.md';
1832
1856
  for (const agentName of staleAgents) {
1833
1857
  await fs.rm(path.join(agentsDir, `${agentName}${extension}`), { force: true });
1834
1858
  }
@@ -0,0 +1,23 @@
1
+ import { stringify } from 'yaml';
2
+
3
+ export function renderOpenCodeAgent(agent, prompt) {
4
+ const metadata = {
5
+ description: agent.description,
6
+ mode: 'subagent'
7
+ };
8
+ // Без явного provider/model OpenCode наследует модель родительской сессии.
9
+ if (agent.models?.opencode) metadata.model = agent.models.opencode;
10
+ if (agent.access === 'read-only') {
11
+ metadata.permissions = [
12
+ { action: '*', resource: '*', effect: 'deny' },
13
+ ...['read', 'glob', 'grep', 'webfetch', 'websearch'].map(action => ({
14
+ action, resource: '*', effect: 'allow'
15
+ })),
16
+ { action: 'read', resource: '*.env', effect: 'ask' },
17
+ { action: 'read', resource: '*.env.*', effect: 'ask' }
18
+ ];
19
+ } else if (agent.access === 'git-write') {
20
+ metadata.permissions = [{ action: 'edit', resource: '*', effect: 'deny' }];
21
+ }
22
+ return `---\n${stringify(metadata)}---\n\n${prompt.trimEnd()}\n`;
23
+ }
@@ -0,0 +1,24 @@
1
+ const OPEN_CODE_INSTRUCTIONS = `## Среда исполнения: OpenCode V2
2
+
3
+ Этот раздел адаптирует только платформенные механизмы инструкции ниже; её scope, режимы исполнения, подтверждения, лимиты и контракты результатов сохраняются. В OpenCode применяй следующие правила вместо веток Claude Code/Codex:
4
+
5
+ - Загружай вложенный скил через инструмент \`skill\` по точному ID; его ресурсы читай относительно выданной базовой директории. Установленные скилы находятся в \`.opencode/skills/\`, агенты — в \`.opencode/agents/\`.
6
+ - Установленного агента запускай через \`subagent\` с \`agent: <имя>\` и \`prompt: <вход>\`. Для изолированного выполнения скила используй установленного \`eda-skill-runner\` с указанием загрузить нужный скил. Дочерние сессии получают свежий контекст: передавай весь необходимый вход явно. Не передавай Codex-параметр \`fork_turns\`.
7
+ - Для параллельных проверок отправь независимые вызовы одновременно, используй background только при независимой работе; ожидай уведомления, не опрашивай shell или файлы. Для продолжения передай \`sessionID\` того же агента.
8
+ - Не заменяй \`eda-skill-runner\` встроенным general: general не может запускать субагентов. При недоступности нужной роли, вложенного запуска или инструмента верни конкретный \`blocked\`, не меняй режим и не подменяй работу отдельным CLI или основным агентом.
9
+ - Вместо \`AskUserQuestion\` / \`request_user_input\` используй \`question\`; если интерактивный инструмент недоступен, задай короткий вопрос и остановись. В неинтерактивном запуске не читай stdin, верни \`blocked: нужен ответ пользователя\`.
10
+ - Модели из колонок Claude/Codex и \`review.agents.*.model.claude/codex\` не относятся к OpenCode. По умолчанию используй модель установленного агента или наследуемую модель сессии; в поле MODEL передавай фактическую модель, иначе \`current\`. Явный runtime override должен иметь формат \`provider/model#variant\` (variant необязателен); если хост не может его применить, верни \`blocked\`. Не выдумывай provider ID и не переписывай настройки проекта.
11
+ - Read-only агенты не имеют shell, редакторов, MCP и субагентов. Оркестратор заранее получает diff и нужные git/CI-данные разрешёнными read-only командами и передаёт их вместе с исходным target в prompt. Агент может дочитывать исходники; отсутствие shell не означает, что тесты были выполнены. Если доказательств недостаточно, сообщи блокер.
12
+ - Если инструкция требует Plan Mode, используй доступный нативный режим планирования без изменения кода. Если его нет либо он запрещает сохранение подтверждённого плана в требуемый каталог, остановись с конкретным блокером, не обходи permissions.
13
+ - Для strict кросс-CLI ревью из OpenCode попроси выбрать Claude Code или Codex CLI, если CLI не указан явно. Не считай OpenCode автоматически одной из двух сторон и не пропускай включённый strict.
14
+
15
+ `;
16
+
17
+ export function renderOpenCodeSkill(content) {
18
+ const closingMarker = content.indexOf('\n---', 4);
19
+ if (!content.startsWith('---\n') || closingMarker === -1) {
20
+ throw new Error('Не удалось добавить настройки OpenCode: некорректный front matter скила.');
21
+ }
22
+ const bodyStart = closingMarker + '\n---'.length;
23
+ return `${content.slice(0, bodyStart)}\n\n${OPEN_CODE_INSTRUCTIONS}${content.slice(bodyStart).trimStart()}`;
24
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gian-tiaga/eda",
3
- "version": "3.13.0",
4
- "description": "Пакет скилов и специализированных агентов eda-* для Claude Code и Codex CLI",
3
+ "version": "3.14.0",
4
+ "description": "Пакет скилов и специализированных агентов eda-* для Claude Code, Codex CLI и OpenCode",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "eda": "bin/cli.js"
@@ -25,6 +25,7 @@
25
25
  "claude-code",
26
26
  "codex",
27
27
  "codex-cli",
28
+ "opencode",
28
29
  "skills",
29
30
  "agents"
30
31
  ],
@@ -70,12 +70,15 @@ Default — `subagents`. При неизвестном или отсутству
70
70
 
71
71
  ## Подготовить контекст
72
72
 
73
- 1. Определи `$TARGET`: явный файл, папка, URL PR/MR, ветка, коммит, указанный diff или `git diff HEAD` без аргументов. Для GitHub/GitLab URL получи metadata и diff read-only командами `gh`/`glab`; для другого сервиса используй доступное read-only средство. Если основной diff недоступен, остановись: warning разрешён только для недоступных прошлых обсуждений, а не для самого target.
73
+ 1. Определи `$TARGET`: явный файл, папка, URL PR/MR, ветка, коммит, указанный diff или `git diff HEAD` без аргументов. Для GitHub/GitLab URL получи metadata, diff, SHA текущего head и статусы CI read-only командами `gh`/`glab`; для другого сервиса используй доступное read-only средство. Если основной diff недоступен, остановись: warning разрешён для недоступных CI-результатов и прошлых обсуждений, но не для самого target.
74
74
  2. Прочитай `AGENTS.md`, `CLAUDE.md`, `docs/rules.md`, `docs/arch.md` и сам target настолько, чтобы выбрать проверки и сформировать точный контекст. В `subagents` на этом шаге не делай собственное code review; в `main` эти документы и target станут общей основой всех проходов и не должны загружаться заново для каждой проверки.
75
75
  3. Определи `$PLAN_FILE`. Если его нет, используй `none` и не пытайся восстановить последний план наугад.
76
76
  4. Определи `$REFERENCE_FILES`: если план содержит `sources.references`, используй их; иначе через `docs/references.md` выбери только применимые карточки для компонентов target. Если карточек нет, используй `none`.
77
77
  5. Определи `$BUSINESS_FILES`: сначала установи исходную задачу и фактически затронутое пользовательское или доменное поведение по текущему сообщению, плану и target. Если план содержит `sources.business`, используй только относящиеся к этому поведению пути; иначе прочитай `docs/business.md` и по описаниям выбери только применимые карточки. Если индекс отсутствует, подходящих карточек нет или изменения не затрагивают бизнес-поведение, используй `none`. Не загружай все карточки только потому, что индекс существует.
78
78
  6. Сформируй краткий `$REVIEW_CONTEXT`: тип target, base, исходную задачу из текущего сообщения, плана, PR/MR title/body или явно связанного issue/артефакта, изменённые области, применимые документы и явные решения пользователя. Если надёжного описания задачи нет, явно напиши `task: unknown`, а не восстанавливай намерение по догадке. Не включай в контекст domain-инструкции агентов.
79
+ 7. Для PR/MR сформируй `$CI_CONTEXT`: SHA head, названия, статусы, определимые команды и ссылки на автоматические CI-проверки; для failure добавь только значимый фрагмент лога, не передавай полные логи всех jobs. Учитывай только результаты для текущего head. Завершённую в CI проверку со статусом success или failure не запускай локально повторно: используй её результат и лог как доказательство. Локальный запуск допустим только для отсутствующей, отменённой, пропущенной, устаревшей или не сопоставимой с CI проверки либо по прямому указанию пользователя. Недоступный CI отметь warning и выполняй нужные проверки обычным способом. Для не-PR/MR target используй `CI_CONTEXT: none`.
80
+
81
+ CI переиспользуется только как результат автоматических команд — тестов, линтеров, typecheck, сборки и аналогичных jobs. Он не заменяет выбранные смысловые проходы `correctness`, `architecture`, `tests` и другие: они по-прежнему анализируют diff и качество покрытия, но не дублируют уже завершённые CI-команды.
79
82
 
80
83
  Текущая задача задаёт область и намерение изменений. Применимые business-карточки проверяют затронутое поведение, но не расширяют target. Если задача явно меняет правило карточки, это возможная рассинхронизация business-документации, которую должен классифицировать `eda-review-business`, а не основание молча объявить реализацию неверной.
81
84
 
@@ -106,10 +109,10 @@ Default — `subagents`. При неизвестном или отсутству
106
109
 
107
110
  Прочитай [references/review-matrix.md](references/review-matrix.md) целиком. Это обязательный контракт всех специализированных проходов в `main`; не читай packaged prompts из `agents/eda-review-*` и не пытайся вызвать их как скилы.
108
111
 
109
- 1. Прочитай target и общий код вокруг изменений один раз. Применимые инструкции, `$PLAN_FILE`, `$REFERENCE_FILES` и `$BUSINESS_FILES` также загрузи один раз и только когда соответствующая проверка выбрана.
112
+ 1. Прочитай target и общий код вокруг изменений один раз. Применимые инструкции, `$PLAN_FILE`, `$REFERENCE_FILES` и `$BUSINESS_FILES` также загрузи один раз и только когда соответствующая проверка выбрана. Соблюдай `$CI_CONTEXT`: не перезапускай автоматическую команду, уже завершённую в CI на текущем head.
110
113
  2. Последовательно выполни отдельный проход по каждой выбранной проверке из матрицы. Не смешивай области и не пропускай проход из-за того, что соседняя проверка уже изучала тот же код.
111
114
  3. Для каждого прохода сохрани внутренний результат: `check`, `status`, `summary` и кандидаты в находки с `severity`, `explanation`, `evidence`, `repro`, `fix` и `tests`. YAML между проходами не нужен.
112
- 4. Если проверка оказалась неприменима, перенеси её в skipped с конкретной причиной. Если обязательную проверку невозможно выполнить из-за недоступного target или контекста, остановись без итогового файла. Недоступные предыдущие удалённые обсуждения преврати в warning и продолжай.
115
+ 4. Если проверка оказалась неприменима, перенеси её в skipped с конкретной причиной. Если обязательную проверку невозможно выполнить из-за недоступного target или контекста, остановись без итогового файла. Недоступные CI-результаты и предыдущие удалённые обсуждения преврати в warning и продолжай.
113
116
  5. После всех проходов проверь доказательства, дедуплицируй кандидатов, выставь score и сохрани итог по общим правилам ниже. Не запускай отдельный редакторский или подтверждающий проход.
114
117
 
115
118
  ### Режим subagents
@@ -150,6 +153,9 @@ REFERENCE_FILES: <paths | none>
150
153
  BUSINESS_FILES: <paths | none>
151
154
  MODEL: <фактически выбранная модель>
152
155
  REVIEW_CONTEXT: <краткий общий контекст>
156
+ CI_CONTEXT: <head SHA, CI-проверки со статусами, командами, ссылками и значимыми failure-логами | none>
157
+
158
+ Для PR/MR не запускай локально автоматическую команду, если CI_CONTEXT подтверждает, что эквивалентная проверка уже завершилась в CI на текущем head. Используй результат и лог CI как доказательство; смысловой review-проход выполни полностью.
153
159
 
154
160
  Верни результат строго по YAML-контракту установленного агента.
155
161
  ```
@@ -214,6 +220,7 @@ checks:
214
220
  - **Completed:** <проверки и модели>
215
221
  - **Skipped:** <проверки и причины>
216
222
  - **Warnings:** <предупреждения или «нет»>
223
+ - **CI:** <переиспользованные результаты текущего head, локально запущенные отсутствующие проверки и недоступные данные>
217
224
  - **Frontend:** <если запускался: «UI/UX проверены по коду; браузерная проверка не выполнялась»>
218
225
  - **Previous reviews:** <актуальные / исправленные, но открытые / недоступны>
219
226
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  Этот файл используется только при `review.execution: main`. Выполни отдельный проход по каждой выбранной проверке, но читай общий target и проектные инструкции один раз. Не смешивай области: одна проблема должна остаться в наиболее точной проверке.
4
4
 
5
+ Для PR/MR соблюдай переданный `CI_CONTEXT`: не запускай локально тесты, линтеры, typecheck, сборку или другую автоматическую команду, если эквивалентная проверка уже завершилась в CI на текущем head. Используй CI-результат и доступный лог как доказательство. Это не отменяет смысловые проходы матрицы: анализируй diff и полноту тестов как обычно.
6
+
5
7
  Для любой находки обязательны точное место, достижимый на текущем коде сценарий, практический ущерб, доказательство, `repro`, способ исправления и нужный тест. Не принимай вкусовой рефакторинг, косметику, спекулятивное усиление, необязательный тест или проблему, которая появится только после гипотетического будущего изменения. Не перечисляй удачные решения и выполненные пункты.
6
8
 
7
9
  ## correctness
@@ -39,7 +41,7 @@
39
41
 
40
42
  ## tests
41
43
 
42
- Ищи пропущенные регрессии, неверные ожидания, неподходящий уровень теста, хрупкие проверки и изменённое поведение без защиты. Не требуй тест на каждую строку. Находка должна назвать конкретную регрессию, которую текущие тесты не поймают, либо ошибку в самих тестах. Для подтверждения разрешены только безопасные проверки, не меняющие tracked-файлы.
44
+ Ищи пропущенные регрессии, неверные ожидания, неподходящий уровень теста, хрупкие проверки и изменённое поведение без защиты. Не требуй тест на каждую строку. Находка должна назвать конкретную регрессию, которую текущие тесты не поймают, либо ошибку в самих тестах. Для подтверждения разрешены только безопасные проверки, не меняющие tracked-файлы и не дублирующие завершённые CI-проверки текущего head.
43
45
 
44
46
  ## security
45
47