@gian-tiaga/eda 3.13.1 → 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 +25 -12
- package/agents/eda-skill-runner/agent.json +14 -0
- package/agents/eda-skill-runner/prompt.md +11 -0
- package/bin/cli.js +3 -3
- package/lib/catalog.js +4 -0
- package/lib/install.js +52 -28
- package/lib/renderers/opencode-agent.js +23 -0
- package/lib/renderers/opencode-skill.js +24 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 🌊 eda
|
|
2
2
|
|
|
3
3
|
> Поточная разработка с AI-агентами — на русском языке.
|
|
4
|
-
> Пакет готовых скилов и специализированных агентов для **Claude Code** и **
|
|
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 продолжает. Можно
|
|
24
|
+
Установщик покажет чекбоксы: стрелками выбираешь среду, пробелом отмечаешь, Enter продолжает. Можно выбрать любую комбинацию Claude Code, Codex CLI и OpenCode. Устанавливаются и скилы, и агенты. Если `docs/settings.yaml` ещё нет, установщик предложит настройки скилов и проверок review, включая модели Claude/Codex. Без интерактивного терминала устанавливаются все три среды и записывается сбалансированный профиль. В конце `eda init` отдельно пишет, какие скилы и агенты реально установлены или изменились.
|
|
25
25
|
|
|
26
|
-
`eda update
|
|
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
|
|
|
@@ -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-*` или `.
|
|
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
|
|
|
@@ -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
|
|
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
|
|
545
|
-
if (
|
|
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(`Найдены установленные среды: ${
|
|
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
|
|
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
|
-
|
|
888
|
-
|
|
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
|
-
|
|
897
|
-
|
|
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 === '
|
|
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
|
-
|
|
956
|
-
|
|
957
|
-
|
|
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 === '
|
|
1596
|
-
const
|
|
1597
|
-
|
|
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 =
|
|
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
|
|
1642
|
-
|
|
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 === '
|
|
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.
|
|
4
|
-
"description": "Пакет скилов и специализированных агентов eda-* для Claude Code
|
|
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
|
],
|