@gian-tiaga/eda 3.13.1 → 4.0.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/MIGRATION.md +23 -2
- package/README.md +153 -232
- package/agents/eda-aim-executor/prompt.md +2 -0
- package/agents/eda-aim-verifier/prompt.md +2 -0
- package/agents/eda-review-api/prompt.md +4 -4
- package/agents/eda-review-architecture/prompt.md +4 -4
- package/agents/eda-review-business/prompt.md +5 -12
- package/agents/eda-review-code-quality/prompt.md +4 -4
- package/agents/eda-review-correctness/prompt.md +3 -3
- package/agents/eda-review-database/prompt.md +4 -4
- package/agents/eda-review-documentation/prompt.md +4 -4
- package/agents/eda-review-frontend/prompt.md +4 -4
- package/agents/eda-review-infra/agent.json +14 -0
- package/agents/eda-review-infra/prompt.md +36 -0
- package/agents/eda-review-performance/prompt.md +7 -7
- package/agents/eda-review-plan-alignment/prompt.md +4 -4
- package/agents/eda-review-previous-reviews/prompt.md +5 -4
- package/agents/eda-review-references/prompt.md +4 -4
- package/agents/eda-review-rules/prompt.md +4 -4
- package/agents/eda-review-security/prompt.md +4 -4
- package/agents/eda-review-seo/agent.json +14 -0
- package/agents/eda-review-seo/prompt.md +36 -0
- package/agents/eda-review-tests/prompt.md +4 -4
- package/agents/eda-review-triage/agent.json +14 -0
- package/agents/eda-review-triage/prompt.md +38 -0
- 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 +32 -1
- package/lib/install.js +266 -611
- package/lib/prompts.js +53 -0
- package/lib/renderers/codex-agent.js +3 -1
- package/lib/renderers/codex-skill.js +21 -1
- package/lib/renderers/opencode-agent.js +23 -0
- package/lib/renderers/opencode-skill.js +24 -0
- package/package.json +4 -4
- package/skills/_shared/done.md +15 -0
- package/skills/_shared/questions.md +9 -0
- package/skills/eda-aim/SKILL.md +2 -3
- package/skills/eda-business/SKILL.md +5 -6
- package/skills/eda-commit/SKILL.md +2 -3
- package/skills/eda-discover-automations/SKILL.md +6 -7
- package/skills/eda-explore/SKILL.md +28 -15
- package/skills/eda-fix/SKILL.md +7 -7
- package/skills/eda-fix-by-review/SKILL.md +4 -3
- package/skills/eda-just-do-it/SKILL.md +4 -4
- package/skills/eda-manual-test/SKILL.md +7 -5
- package/skills/eda-merge-worktree/SKILL.md +2 -3
- package/skills/eda-new-project/SKILL.md +3 -5
- package/skills/eda-orhestra/SKILL.md +3 -4
- package/skills/eda-plan/SKILL.md +126 -168
- package/skills/eda-plan-execute/SKILL.md +72 -92
- package/skills/eda-plan-polish/SKILL.md +14 -14
- package/skills/eda-plan-review/SKILL.md +25 -10
- package/skills/eda-plan-review-fix/SKILL.md +10 -7
- package/skills/eda-polish/SKILL.md +9 -6
- package/skills/eda-prepare-ai/SKILL.md +35 -15
- package/skills/eda-prod-check/SKILL.md +129 -0
- package/skills/eda-review/SKILL.md +92 -64
- package/skills/eda-review/references/checks/api.md +18 -0
- package/skills/eda-review/references/checks/architecture.md +16 -0
- package/skills/eda-review/references/checks/business.md +13 -0
- package/skills/eda-review/references/checks/code-quality.md +18 -0
- package/skills/eda-review/references/checks/correctness.md +19 -0
- package/skills/eda-review/references/checks/database.md +17 -0
- package/skills/eda-review/references/checks/documentation.md +13 -0
- package/skills/eda-review/references/checks/frontend.md +19 -0
- package/skills/eda-review/references/checks/infra.md +16 -0
- package/skills/eda-review/references/checks/performance.md +21 -0
- package/skills/eda-review/references/checks/plan-alignment.md +16 -0
- package/skills/eda-review/references/checks/previous-reviews.md +9 -0
- package/skills/eda-review/references/checks/references.md +11 -0
- package/skills/eda-review/references/checks/rules.md +16 -0
- package/skills/eda-review/references/checks/security.md +26 -0
- package/skills/eda-review/references/checks/seo.md +16 -0
- package/skills/eda-review/references/checks/tests.md +18 -0
- package/skills/eda-review/references/defects.md +24 -0
- package/skills/eda-review/references/triage.md +23 -0
- package/skills/eda-roadmap/SKILL.md +2 -4
- package/skills/eda-send-review/SKILL.md +3 -5
- package/skills/eda-worktree/SKILL.md +2 -3
- package/skills/eda-review/references/review-matrix.md +0 -72
package/lib/install.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import checkbox from '@inquirer/checkbox';
|
|
2
|
-
import select from '@inquirer/select';
|
|
3
1
|
import { randomUUID } from 'node:crypto';
|
|
4
2
|
import fs from 'node:fs/promises';
|
|
5
3
|
import path from 'node:path';
|
|
6
4
|
import { fileURLToPath } from 'node:url';
|
|
7
5
|
import { parse as parseYaml } from 'yaml';
|
|
8
6
|
|
|
9
|
-
import { listAgents, listSkills } from './catalog.js';
|
|
7
|
+
import { expandIncludes, listAgents, listSkills } from './catalog.js';
|
|
8
|
+
import { checkboxPrompt as checkbox, finishPrompts, selectPrompt as select, startPrompts } from './prompts.js';
|
|
10
9
|
import { renderClaudeAgent } from './renderers/claude-agent.js';
|
|
11
10
|
import { renderClaudeSkill } from './renderers/claude-skill.js';
|
|
12
11
|
import { renderCodexAgent } from './renderers/codex-agent.js';
|
|
13
12
|
import { renderCodexSkill } from './renderers/codex-skill.js';
|
|
13
|
+
import { renderOpenCodeAgent } from './renderers/opencode-agent.js';
|
|
14
|
+
import { renderOpenCodeSkill } from './renderers/opencode-skill.js';
|
|
14
15
|
|
|
15
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
17
|
const __dirname = path.dirname(__filename);
|
|
@@ -37,10 +38,13 @@ const ARTIFACT_DIRECTORY_NAMES = [
|
|
|
37
38
|
];
|
|
38
39
|
const TARGET_CHOICES = [
|
|
39
40
|
{ value: 'claude', label: 'Claude Code', dir: '.claude/skills/' },
|
|
40
|
-
{ value: 'codex', label: 'Codex CLI', dir: '.agents/skills/' }
|
|
41
|
+
{ value: 'codex', label: 'Codex CLI', dir: '.agents/skills/' },
|
|
42
|
+
{ value: 'opencode', label: 'OpenCode', dir: '.opencode/skills/' }
|
|
41
43
|
];
|
|
44
|
+
// В вопросах платформы показываются в этом порядке; установка идёт в порядке TARGET_CHOICES.
|
|
45
|
+
const TARGET_PROMPT_ORDER = ['claude', 'opencode', 'codex'];
|
|
42
46
|
const UPDATE_ALL_MAX_DEPTH = 2;
|
|
43
|
-
const UPDATE_ALL_SKIP_DIRS = new Set(['.git', '.agents', '.claude', '.codex', 'node_modules']);
|
|
47
|
+
const UPDATE_ALL_SKIP_DIRS = new Set(['.git', '.agents', '.claude', '.codex', '.opencode', 'node_modules']);
|
|
44
48
|
const RETIRED_SKILLS = [
|
|
45
49
|
'eda-research',
|
|
46
50
|
'eda-review-check',
|
|
@@ -65,6 +69,8 @@ const RETIRED_AGENTS = [
|
|
|
65
69
|
'eda-plan-review-previous'
|
|
66
70
|
];
|
|
67
71
|
const RETIRED_SETTINGS_PATHS = [
|
|
72
|
+
'plan.review',
|
|
73
|
+
'plan.meta_review',
|
|
68
74
|
'plan-review.agents',
|
|
69
75
|
'review-check',
|
|
70
76
|
'review.strict',
|
|
@@ -72,138 +78,30 @@ const RETIRED_SETTINGS_PATHS = [
|
|
|
72
78
|
];
|
|
73
79
|
const MANIFEST_FILE = 'eda-manifest.json';
|
|
74
80
|
const SETTINGS_VERSION = 3;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
mode: 'always',
|
|
97
|
-
claude: 'haiku',
|
|
98
|
-
codex: 'gpt-5.6-luna'
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
key: 'references',
|
|
102
|
-
description: 'Проверяет соответствие применимым карточкам из docs/references/.',
|
|
103
|
-
auto: 'В auto запускается только когда для изменений выбраны применимые карточки.',
|
|
104
|
-
mode: 'auto',
|
|
105
|
-
claude: 'haiku',
|
|
106
|
-
codex: 'gpt-5.6-luna'
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
key: 'business',
|
|
110
|
-
description: 'Проверяет изменённое поведение по применимым карточкам из docs/business/.',
|
|
111
|
-
auto: 'В auto запускается только когда изменения затрагивают поведение с применимой business-карточкой.',
|
|
112
|
-
mode: 'auto',
|
|
113
|
-
claude: 'sonnet',
|
|
114
|
-
codex: 'gpt-5.6-terra'
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
key: 'plan_alignment',
|
|
118
|
-
description: 'Проверяет, что реализация соответствует указанному или однозначно найденному плану.',
|
|
119
|
-
auto: 'В auto запускается только когда у ревью есть конкретный план.',
|
|
120
|
-
mode: 'auto',
|
|
121
|
-
claude: 'sonnet',
|
|
122
|
-
codex: 'gpt-5.6-terra'
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
key: 'code_quality',
|
|
126
|
-
description: 'Проверяет читаемость, сложность, дублирование и поддерживаемость кода.',
|
|
127
|
-
auto: 'Эта базовая проверка по умолчанию запускается всегда.',
|
|
128
|
-
mode: 'always',
|
|
129
|
-
claude: 'sonnet',
|
|
130
|
-
codex: 'gpt-5.6-terra'
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
key: 'tests',
|
|
134
|
-
description: 'Проверяет достаточность, корректность и устойчивость тестов.',
|
|
135
|
-
auto: 'Эта базовая проверка по умолчанию запускается всегда.',
|
|
136
|
-
mode: 'always',
|
|
137
|
-
claude: 'sonnet',
|
|
138
|
-
codex: 'gpt-5.6-terra'
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
key: 'security',
|
|
142
|
-
description: 'Проверяет уязвимости, права доступа, валидацию и работу с чувствительными данными.',
|
|
143
|
-
auto: 'В auto запускается для auth, permissions, внешнего ввода, файлов, сети, секретов и криптографии.',
|
|
144
|
-
mode: 'auto',
|
|
145
|
-
claude: 'opus',
|
|
146
|
-
codex: 'gpt-5.6-sol'
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
key: 'performance',
|
|
150
|
-
description: 'Проверяет запросы, циклы, память, конкурентность и другие риски производительности.',
|
|
151
|
-
auto: 'В auto запускается при изменении запросов, пакетной обработки, кешей, конкурентности или горячих путей.',
|
|
152
|
-
mode: 'auto',
|
|
153
|
-
claude: 'sonnet',
|
|
154
|
-
codex: 'gpt-5.6-terra'
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
key: 'frontend',
|
|
158
|
-
description: 'Проверяет фронтенд-код, UI, UX, адаптивность и состояния интерфейса.',
|
|
159
|
-
auto: 'В auto обязательно запускается при изменении компонентов, стилей, клиентских маршрутов или состояния UI.',
|
|
160
|
-
mode: 'auto',
|
|
161
|
-
claude: 'sonnet',
|
|
162
|
-
codex: 'gpt-5.6-terra'
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
key: 'api',
|
|
166
|
-
description: 'Проверяет API-контракты, совместимость, валидацию и обработку ошибок.',
|
|
167
|
-
auto: 'В auto запускается при изменении маршрутов, handlers, DTO, схем API, GraphQL или RPC.',
|
|
168
|
-
mode: 'auto',
|
|
169
|
-
claude: 'sonnet',
|
|
170
|
-
codex: 'gpt-5.6-terra'
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
key: 'database',
|
|
174
|
-
description: 'Проверяет схемы, миграции, запросы, индексы и целостность данных.',
|
|
175
|
-
auto: 'В auto запускается при изменении миграций, моделей хранения, SQL, схем или транзакций.',
|
|
176
|
-
mode: 'auto',
|
|
177
|
-
claude: 'opus',
|
|
178
|
-
codex: 'gpt-5.6-sol'
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
key: 'documentation',
|
|
182
|
-
description: 'Проверяет, обновлена ли документация для изменившегося пользовательского поведения.',
|
|
183
|
-
auto: 'В auto запускается при изменении публичного API, CLI, настроек или документируемого поведения.',
|
|
184
|
-
mode: 'auto',
|
|
185
|
-
claude: 'haiku',
|
|
186
|
-
codex: 'gpt-5.6-luna'
|
|
187
|
-
},
|
|
188
|
-
{
|
|
189
|
-
key: 'previous_reviews',
|
|
190
|
-
description: 'Перепроверяет незакрытые замечания предыдущих ревью для удалённого PR или MR.',
|
|
191
|
-
auto: 'В auto запускается только для ссылки на PR/MR или другой цели с доступными удалёнными обсуждениями.',
|
|
192
|
-
mode: 'auto',
|
|
193
|
-
claude: 'haiku',
|
|
194
|
-
codex: 'gpt-5.6-luna'
|
|
195
|
-
}
|
|
81
|
+
// Состав проверок выбирает eda-review-triage по содержимому ревью. В настройках у роли одна строка:
|
|
82
|
+
// default — модель установленного агента — или provider/model#variant, где provider и variant необязательны.
|
|
83
|
+
const REVIEW_AGENT_KEYS = [
|
|
84
|
+
'triage',
|
|
85
|
+
'correctness',
|
|
86
|
+
'architecture',
|
|
87
|
+
'rules',
|
|
88
|
+
'references',
|
|
89
|
+
'business',
|
|
90
|
+
'plan_alignment',
|
|
91
|
+
'code_quality',
|
|
92
|
+
'tests',
|
|
93
|
+
'security',
|
|
94
|
+
'performance',
|
|
95
|
+
'frontend',
|
|
96
|
+
'api',
|
|
97
|
+
'database',
|
|
98
|
+
'infra',
|
|
99
|
+
'seo',
|
|
100
|
+
'documentation',
|
|
101
|
+
'previous_reviews'
|
|
196
102
|
];
|
|
197
|
-
const DEFAULT_REVIEW_AGENTS = Object.fromEntries(
|
|
198
|
-
|
|
199
|
-
{
|
|
200
|
-
mode: agent.mode,
|
|
201
|
-
model: {
|
|
202
|
-
claude: agent.claude,
|
|
203
|
-
codex: agent.codex
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
]));
|
|
103
|
+
const DEFAULT_REVIEW_AGENTS = Object.fromEntries(REVIEW_AGENT_KEYS.map(key => [key, 'default']));
|
|
104
|
+
const REVIEW_AGENT_MODEL_PATTERN = /^[\w.:-]+(?:\/[\w.:-]+)*(?:#[\w.-]+)?$/;
|
|
207
105
|
const ORHESTRA_STEP_DEFINITIONS = [
|
|
208
106
|
{
|
|
209
107
|
id: 'plan',
|
|
@@ -263,20 +161,19 @@ const DEFAULT_SETTINGS = {
|
|
|
263
161
|
},
|
|
264
162
|
plan: {
|
|
265
163
|
strict: false,
|
|
266
|
-
review: true,
|
|
267
164
|
size: 'normal',
|
|
268
165
|
decisionMode: 'recommend_and_ask',
|
|
269
|
-
testStrategy: '
|
|
270
|
-
loggingStrategy: '
|
|
166
|
+
testStrategy: 'phase_tests_final_checks',
|
|
167
|
+
loggingStrategy: 'standard'
|
|
271
168
|
},
|
|
272
169
|
planReview: {
|
|
273
170
|
threshold: 100
|
|
274
171
|
},
|
|
275
172
|
planPolish: {
|
|
276
|
-
limit:
|
|
173
|
+
limit: 5
|
|
277
174
|
},
|
|
278
175
|
planExecute: {
|
|
279
|
-
mode: '
|
|
176
|
+
mode: 'main'
|
|
280
177
|
},
|
|
281
178
|
polish: {
|
|
282
179
|
execution: 'subagents'
|
|
@@ -285,249 +182,83 @@ const DEFAULT_SETTINGS = {
|
|
|
285
182
|
depth: 'full'
|
|
286
183
|
},
|
|
287
184
|
review: {
|
|
288
|
-
execution: '
|
|
185
|
+
execution: 'main',
|
|
289
186
|
agents: DEFAULT_REVIEW_AGENTS
|
|
290
187
|
},
|
|
291
188
|
sendReview: {
|
|
292
|
-
closePreviousReviews:
|
|
189
|
+
closePreviousReviews: true
|
|
293
190
|
},
|
|
294
191
|
discoverAutomations: {
|
|
295
192
|
includePlans: false
|
|
296
193
|
}
|
|
297
194
|
};
|
|
195
|
+
// Разделы идут по ходу работы: исследование, план, его проверка, выполнение, ревью,
|
|
196
|
+
// ручные тесты; оркестраторы, которые собирают остальные скилы, — в конце.
|
|
298
197
|
const SETTINGS_SECTION_NAMES = [
|
|
299
|
-
'orhestra',
|
|
300
|
-
'aim',
|
|
301
198
|
'explore',
|
|
302
199
|
'plan',
|
|
303
200
|
'plan-review',
|
|
304
201
|
'plan-polish',
|
|
305
202
|
'plan-execute',
|
|
203
|
+
'review',
|
|
306
204
|
'polish',
|
|
307
205
|
'manual-test',
|
|
308
|
-
'review',
|
|
309
206
|
'send-review',
|
|
310
|
-
'discover-automations'
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
{
|
|
314
|
-
section: 'explore',
|
|
315
|
-
key: 'explore.strict',
|
|
316
|
-
value: 'exploreStrict',
|
|
317
|
-
name: 'Strict по умолчанию для eda-explore',
|
|
318
|
-
checked: DEFAULT_SETTINGS.explore.strict
|
|
319
|
-
},
|
|
320
|
-
{
|
|
321
|
-
section: 'plan',
|
|
322
|
-
key: 'plan.strict',
|
|
323
|
-
value: 'planStrict',
|
|
324
|
-
name: 'Strict по умолчанию для eda-plan',
|
|
325
|
-
checked: DEFAULT_SETTINGS.plan.strict
|
|
326
|
-
},
|
|
327
|
-
{
|
|
328
|
-
section: 'plan',
|
|
329
|
-
key: 'plan.review',
|
|
330
|
-
value: 'planReview',
|
|
331
|
-
name: 'Запускать eda-plan-review внутри eda-plan',
|
|
332
|
-
checked: DEFAULT_SETTINGS.plan.review
|
|
333
|
-
},
|
|
334
|
-
{
|
|
335
|
-
section: 'send-review',
|
|
336
|
-
key: 'sendReview.closePreviousReviews',
|
|
337
|
-
value: 'sendReviewClosePreviousReviews',
|
|
338
|
-
name: 'Закрывать предыдущие ревью eda-send-review',
|
|
339
|
-
checked: DEFAULT_SETTINGS.sendReview.closePreviousReviews
|
|
340
|
-
},
|
|
341
|
-
{
|
|
342
|
-
section: 'discover-automations',
|
|
343
|
-
key: 'discoverAutomations.includePlans',
|
|
344
|
-
value: 'discoverAutomationsIncludePlans',
|
|
345
|
-
name: 'Анализировать планы в eda-discover-automations по умолчанию',
|
|
346
|
-
checked: DEFAULT_SETTINGS.discoverAutomations.includePlans
|
|
347
|
-
}
|
|
348
|
-
];
|
|
349
|
-
const ORHESTRA_MODE_CHOICES = [
|
|
350
|
-
{
|
|
351
|
-
value: 'automatic',
|
|
352
|
-
name: 'Автоматически пройти план, выполнение, ревью и ручные тесты'
|
|
353
|
-
},
|
|
354
|
-
{
|
|
355
|
-
value: 'manual',
|
|
356
|
-
name: 'Писать план вместе с человеком и передавать ему вопросы'
|
|
357
|
-
}
|
|
358
|
-
];
|
|
359
|
-
const ORHESTRA_EXECUTION_CHOICES = [
|
|
360
|
-
{
|
|
361
|
-
value: 'subagents',
|
|
362
|
-
name: 'Каждый этап выполнять отдельным изолированным субагентом'
|
|
363
|
-
},
|
|
364
|
-
{
|
|
365
|
-
value: 'main',
|
|
366
|
-
name: 'Все этапы выполнять основным агентом без передачи режима внутрь'
|
|
367
|
-
},
|
|
368
|
-
{
|
|
369
|
-
value: 'main_propagated',
|
|
370
|
-
name: 'Все этапы выполнять основным агентом и передавать main совместимым скилам'
|
|
371
|
-
}
|
|
372
|
-
];
|
|
373
|
-
const AIM_MODE_CHOICES = [
|
|
374
|
-
{
|
|
375
|
-
value: 'automatic',
|
|
376
|
-
name: 'Автоматически отвечать на безопасные рабочие вопросы'
|
|
377
|
-
},
|
|
378
|
-
{
|
|
379
|
-
value: 'manual',
|
|
380
|
-
name: 'Передавать рабочие вопросы человеку'
|
|
381
|
-
}
|
|
382
|
-
];
|
|
383
|
-
const ORHESTRA_POLISH_LIMIT_CHOICES = [1, 2, 3, 5, 10].map(value => ({
|
|
384
|
-
value,
|
|
385
|
-
name: `${value} ${value === 1 ? 'итерация' : value < 5 ? 'итерации' : 'итераций'}`
|
|
386
|
-
}));
|
|
387
|
-
const PLAN_REVIEW_THRESHOLD_CHOICES = [80, 90, 100].map(value => ({
|
|
388
|
-
value,
|
|
389
|
-
name: `${value}/100`
|
|
390
|
-
}));
|
|
391
|
-
const PLAN_POLISH_LIMIT_CHOICES = [1, 2, 3].map(value => ({
|
|
392
|
-
value,
|
|
393
|
-
name: `${value} ${value === 1 ? 'итерация' : value < 5 ? 'итерации' : 'итераций'}`
|
|
394
|
-
}));
|
|
395
|
-
const PLAN_EXECUTE_MODE_CHOICES = [
|
|
396
|
-
{
|
|
397
|
-
value: 'auto',
|
|
398
|
-
name: 'Решать по плану: один контекст для простого плана, субагенты для остальных'
|
|
399
|
-
},
|
|
400
|
-
{
|
|
401
|
-
value: 'subagents',
|
|
402
|
-
name: 'Каждую фазу выполнять отдельным изолированным субагентом'
|
|
403
|
-
},
|
|
404
|
-
{
|
|
405
|
-
value: 'main',
|
|
406
|
-
name: 'Выполнять весь план основным агентом без субагентов'
|
|
407
|
-
}
|
|
408
|
-
];
|
|
409
|
-
const POLISH_EXECUTION_CHOICES = [
|
|
410
|
-
{
|
|
411
|
-
value: 'subagents',
|
|
412
|
-
name: 'Каждое review и исправление выполнять отдельным изолированным субагентом'
|
|
413
|
-
},
|
|
414
|
-
{
|
|
415
|
-
value: 'main',
|
|
416
|
-
name: 'Review и исправления выполнять основным агентом без субагентов'
|
|
417
|
-
}
|
|
207
|
+
'discover-automations',
|
|
208
|
+
'orhestra',
|
|
209
|
+
'aim'
|
|
418
210
|
];
|
|
211
|
+
// Варианты ответа: первым — значение по умолчанию, дальше — от ближайшего к дальнему,
|
|
212
|
+
// «Спрашивать каждый раз» всегда последним.
|
|
419
213
|
const PLAN_SIZE_CHOICES = [
|
|
420
|
-
{
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
},
|
|
424
|
-
{
|
|
425
|
-
value: 'short',
|
|
426
|
-
name: 'Короткий plan short по умолчанию'
|
|
427
|
-
},
|
|
428
|
-
{
|
|
429
|
-
value: 'ask_each_time',
|
|
430
|
-
name: 'Спрашивать размер плана каждый раз'
|
|
431
|
-
}
|
|
432
|
-
];
|
|
433
|
-
const MANUAL_TEST_DEPTH_CHOICES = [
|
|
434
|
-
{
|
|
435
|
-
value: 'full',
|
|
436
|
-
name: 'Полная проверка всех областей: API, база, консоль, интерфейс, алгоритмы'
|
|
437
|
-
},
|
|
438
|
-
{
|
|
439
|
-
value: 'smoke',
|
|
440
|
-
name: 'Только smoke: запуск и основной сценарий изменённой области'
|
|
441
|
-
},
|
|
442
|
-
{
|
|
443
|
-
value: 'ask_each_time',
|
|
444
|
-
name: 'Спрашивать глубину проверки каждый раз'
|
|
445
|
-
}
|
|
214
|
+
{ value: 'normal', name: 'normal', description: 'обычный план до 40 КБ' },
|
|
215
|
+
{ value: 'short', name: 'short', description: 'до 12 КБ и 2 фаз' },
|
|
216
|
+
{ value: 'ask_each_time', name: 'Спрашивать каждый раз' }
|
|
446
217
|
];
|
|
447
218
|
const DECISION_MODE_CHOICES = [
|
|
448
|
-
{
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
},
|
|
452
|
-
{
|
|
453
|
-
value: 'autonomous',
|
|
454
|
-
name: 'Самостоятельно выбирать по коду, правилам и рискам'
|
|
455
|
-
},
|
|
456
|
-
{
|
|
457
|
-
value: 'ask_each_time',
|
|
458
|
-
name: 'Спрашивать по каждой значимой развилке'
|
|
459
|
-
}
|
|
219
|
+
{ value: 'recommend_and_ask', name: 'Рекомендовать и спрашивать', description: 'вопрос только по существенным развилкам' },
|
|
220
|
+
{ value: 'autonomous', name: 'Самостоятельно', description: 'агент выбирает сам и записывает причину' },
|
|
221
|
+
{ value: 'ask_each_time', name: 'Спрашивать по каждой развилке' }
|
|
460
222
|
];
|
|
461
223
|
const TEST_STRATEGY_CHOICES = [
|
|
462
|
-
{
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
},
|
|
466
|
-
{
|
|
467
|
-
value: 'phase_tests_final_checks',
|
|
468
|
-
name: 'Писать тесты в фазах, запускать только их, остальные проверки — в конце'
|
|
469
|
-
},
|
|
470
|
-
{
|
|
471
|
-
value: 'tdd_each_phase',
|
|
472
|
-
name: 'В каждой фазе сначала тесты, затем код и все проверки фазы'
|
|
473
|
-
},
|
|
474
|
-
{
|
|
475
|
-
value: 'end_of_plan',
|
|
476
|
-
name: 'Писать тесты и запускать все проверки в конце плана'
|
|
477
|
-
},
|
|
478
|
-
{
|
|
479
|
-
value: 'ask_each_time',
|
|
480
|
-
name: 'Спрашивать стратегию тестов и проверок каждый раз'
|
|
481
|
-
}
|
|
482
|
-
];
|
|
483
|
-
const LOGGING_STRATEGY_CHOICES = [
|
|
484
|
-
{
|
|
485
|
-
value: 'standard',
|
|
486
|
-
name: 'Стандартные info / warning / error по необходимости'
|
|
487
|
-
},
|
|
488
|
-
{
|
|
489
|
-
value: 'debug_precise',
|
|
490
|
-
name: 'Подробные debug-логи на важных шагах'
|
|
491
|
-
},
|
|
492
|
-
{
|
|
493
|
-
value: 'ask_each_time',
|
|
494
|
-
name: 'Спрашивать стратегию логирования каждый раз'
|
|
495
|
-
}
|
|
224
|
+
{ value: 'phase_tests_final_checks', name: 'Тесты в фазах, проверки в конце', description: 'в фазе — только её тесты, остальные проверки в конце плана' },
|
|
225
|
+
{ value: 'tdd_each_phase', name: 'TDD', description: 'в каждой фазе сначала тесты, затем код и все проверки фазы' },
|
|
226
|
+
{ value: 'after_each_phase', name: 'После каждой фазы', description: 'тесты и все проверки после каждой фазы' },
|
|
227
|
+
{ value: 'end_of_plan', name: 'В конце плана', description: 'тесты и все проверки один раз в конце' },
|
|
228
|
+
{ value: 'ask_each_time', name: 'Спрашивать каждый раз' }
|
|
496
229
|
];
|
|
497
|
-
const
|
|
498
|
-
{ value: '
|
|
499
|
-
{ value: 'auto', name: 'auto —
|
|
500
|
-
{ value: '
|
|
230
|
+
const PLAN_EXECUTE_MODE_CHOICES = [
|
|
231
|
+
{ value: 'main', name: 'Основной агент', description: 'весь план одним контекстом' },
|
|
232
|
+
{ value: 'auto', name: 'auto', description: 'по плану: небольшой — одним контекстом, остальные — субагентами' },
|
|
233
|
+
{ value: 'subagents', name: 'Субагенты', description: 'каждая фаза — отдельный субагент с независимой проверкой' }
|
|
501
234
|
];
|
|
502
235
|
const REVIEW_EXECUTION_CHOICES = [
|
|
503
|
-
{
|
|
504
|
-
|
|
505
|
-
name: 'Каждую review-проверку выполнять отдельным специализированным субагентом'
|
|
506
|
-
},
|
|
507
|
-
{
|
|
508
|
-
value: 'main',
|
|
509
|
-
name: 'Все review-проверки выполнять основным агентом без субагентов'
|
|
510
|
-
}
|
|
236
|
+
{ value: 'main', name: 'Основной агент', description: 'все проверки в одном контексте' },
|
|
237
|
+
{ value: 'subagents', name: 'Субагенты', description: 'разведчик и отдельный агент на каждую проверку' }
|
|
511
238
|
];
|
|
512
|
-
const
|
|
513
|
-
{ value: '
|
|
514
|
-
{ value: '
|
|
515
|
-
{ value: 'opus', name: 'opus — максимальная глубина' }
|
|
239
|
+
const POLISH_EXECUTION_CHOICES = [
|
|
240
|
+
{ value: 'subagents', name: 'Субагенты', description: 'каждое ревью и исправление — отдельный субагент' },
|
|
241
|
+
{ value: 'main', name: 'Основной агент', description: 'ревью и исправления в одном контексте' }
|
|
516
242
|
];
|
|
517
|
-
const
|
|
518
|
-
{ value: '
|
|
519
|
-
{ value: '
|
|
520
|
-
{ value: '
|
|
243
|
+
const MANUAL_TEST_DEPTH_CHOICES = [
|
|
244
|
+
{ value: 'full', name: 'Полная', description: 'API, БД, консоль, интерфейс, алгоритмы, фон, права, логи, регрессии' },
|
|
245
|
+
{ value: 'smoke', name: 'Smoke', description: 'запуск и основной сценарий изменённой области' },
|
|
246
|
+
{ value: 'ask_each_time', name: 'Спрашивать каждый раз' }
|
|
247
|
+
];
|
|
248
|
+
const ORHESTRA_MODE_CHOICES = [
|
|
249
|
+
{ value: 'automatic', name: 'automatic', description: 'сам отвечает на безопасные рабочие вопросы' },
|
|
250
|
+
{ value: 'manual', name: 'manual', description: 'план пишется с человеком, вопросы передаются ему' }
|
|
521
251
|
];
|
|
252
|
+
const ORHESTRA_EXECUTION_CHOICES = [
|
|
253
|
+
{ value: 'subagents', name: 'Субагенты', description: 'каждый этап — отдельный субагент' },
|
|
254
|
+
{ value: 'main', name: 'Основной агент', description: 'цепочку ведёт текущий агент, вложенные скилы — по своим настройкам' },
|
|
255
|
+
{ value: 'main_propagated', name: 'Основной агент везде', description: 'plan-execute, polish и review тоже без субагентов' }
|
|
256
|
+
];
|
|
257
|
+
// План и выполнение в оркестраторе включены всегда; спрашиваются только необязательные этапы.
|
|
258
|
+
const ORHESTRA_OPTIONAL_STEPS = ['eda-plan-polish', 'eda-polish', 'eda-manual-test'];
|
|
522
259
|
const UPDATE_ALL_SETTINGS_MODE_CHOICES = [
|
|
523
|
-
{
|
|
524
|
-
|
|
525
|
-
name: 'Настроить один общий профиль и записать его во все проекты'
|
|
526
|
-
},
|
|
527
|
-
{
|
|
528
|
-
value: 'skip',
|
|
529
|
-
name: 'Skip — сохранить полный v3, остальные конфиги перенести с defaults'
|
|
530
|
-
}
|
|
260
|
+
{ value: 'configure', name: 'Настроить общий профиль', description: 'ответить один раз и записать во все проекты' },
|
|
261
|
+
{ value: 'skip', name: 'Skip', description: 'сохранить актуальные конфиги, старые перенести с defaults' }
|
|
531
262
|
];
|
|
532
263
|
|
|
533
264
|
export async function init({ cwd, input = process.stdin, output = process.stdout }) {
|
|
@@ -540,13 +271,14 @@ export async function init({ cwd, input = process.stdin, output = process.stdout
|
|
|
540
271
|
await syncPackage(cwd, targets, output, { action: 'install' });
|
|
541
272
|
}
|
|
542
273
|
|
|
543
|
-
export async function update({ cwd, input = process.stdin, output = process.stdout }) {
|
|
544
|
-
const
|
|
545
|
-
if (
|
|
274
|
+
export async function update({ cwd, input = process.stdin, output = process.stdout, checkboxPrompt = checkbox }) {
|
|
275
|
+
const installedTargets = await detectTargets(cwd);
|
|
276
|
+
if (installedTargets.length === 0) {
|
|
546
277
|
output.write('Не нашёл установленного пакета eda в этом проекте. Запусти `eda init`.\n');
|
|
547
278
|
return;
|
|
548
279
|
}
|
|
549
|
-
output.write(`Найдены установленные среды: ${
|
|
280
|
+
output.write(`Найдены установленные среды: ${installedTargets.join(', ')}\n`);
|
|
281
|
+
const targets = await askUpdateTargets({ installedTargets, input, output, checkboxPrompt });
|
|
550
282
|
await migrateArtifactDirectories(cwd, output);
|
|
551
283
|
await configureSettings(cwd, { input, output });
|
|
552
284
|
await syncPackage(cwd, targets, output, { action: 'update' });
|
|
@@ -626,6 +358,12 @@ export async function updateAll({
|
|
|
626
358
|
output.write(`Полный файл настроек сохранён: ${SETTINGS_RELATIVE_PATH}\n`);
|
|
627
359
|
} else {
|
|
628
360
|
await writeSettingsContent(projectDir, rendered);
|
|
361
|
+
if (migrated.legacyReviewAgents) {
|
|
362
|
+
output.write(`review.agents переведён на формат 4.0, все роли — default: ${SETTINGS_RELATIVE_PATH}\n`);
|
|
363
|
+
}
|
|
364
|
+
if (migrated.resetReviewAgents.length > 0) {
|
|
365
|
+
output.write(`Недопустимые модели review.agents заменены на default (${migrated.resetReviewAgents.join(', ')}): ${SETTINGS_RELATIVE_PATH}\n`);
|
|
366
|
+
}
|
|
629
367
|
output.write(migrated.retiredPaths.length > 0
|
|
630
368
|
? `Из файла настроек убраны устаревшие ключи (${migrated.retiredPaths.join(', ')}): ${SETTINGS_RELATIVE_PATH}\n`
|
|
631
369
|
: migrated.normalizedPaths.length > 0
|
|
@@ -677,22 +415,51 @@ export async function updateAll({
|
|
|
677
415
|
|
|
678
416
|
export async function askTargets({ input = process.stdin, output = process.stdout } = {}) {
|
|
679
417
|
if (!input.isTTY || !output.isTTY) {
|
|
680
|
-
output.write('Нет интерактивного терминала — устанавливаю Claude Code
|
|
418
|
+
output.write('Нет интерактивного терминала — устанавливаю Claude Code, Codex CLI и OpenCode.\n');
|
|
681
419
|
return TARGET_CHOICES.map(choice => choice.value);
|
|
682
420
|
}
|
|
683
421
|
|
|
684
|
-
|
|
685
|
-
message: 'Куда
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
name: `${choice.label} (${choice.dir})`,
|
|
422
|
+
const selected = await checkbox({
|
|
423
|
+
message: 'Куда установить eda?',
|
|
424
|
+
choices: targetPromptChoices().map(choice => ({
|
|
425
|
+
name: choice.label,
|
|
689
426
|
value: choice.value,
|
|
690
|
-
|
|
691
|
-
|
|
427
|
+
description: choice.dir,
|
|
428
|
+
checked: false
|
|
429
|
+
})),
|
|
430
|
+
required: true
|
|
692
431
|
}, {
|
|
693
432
|
input,
|
|
694
433
|
output
|
|
695
434
|
});
|
|
435
|
+
return TARGET_CHOICES.map(choice => choice.value).filter(target => selected.includes(target));
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
export async function askUpdateTargets({
|
|
439
|
+
installedTargets,
|
|
440
|
+
input = process.stdin,
|
|
441
|
+
output = process.stdout,
|
|
442
|
+
checkboxPrompt = checkbox
|
|
443
|
+
}) {
|
|
444
|
+
if (!input.isTTY || !output.isTTY) return [...installedTargets];
|
|
445
|
+
|
|
446
|
+
// Установленные платформы обновляются всегда: снятая с них отметка ничего не удаляет.
|
|
447
|
+
const selected = await checkboxPrompt({
|
|
448
|
+
message: 'Какие платформы обновить? Установленные уже отмечены.',
|
|
449
|
+
choices: targetPromptChoices().map(choice => ({
|
|
450
|
+
name: choice.label,
|
|
451
|
+
value: choice.value,
|
|
452
|
+
description: installedTargets.includes(choice.value) ? 'установлена — обновится в любом случае' : choice.dir,
|
|
453
|
+
checked: installedTargets.includes(choice.value)
|
|
454
|
+
}))
|
|
455
|
+
}, { input, output });
|
|
456
|
+
|
|
457
|
+
return TARGET_CHOICES.map(choice => choice.value)
|
|
458
|
+
.filter(target => installedTargets.includes(target) || selected.includes(target));
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function targetPromptChoices() {
|
|
462
|
+
return TARGET_PROMPT_ORDER.map(value => TARGET_CHOICES.find(choice => choice.value === value));
|
|
696
463
|
}
|
|
697
464
|
|
|
698
465
|
export async function askUpdateAllSettingsMode({
|
|
@@ -715,6 +482,8 @@ export async function askUpdateAllSettingsMode({
|
|
|
715
482
|
});
|
|
716
483
|
}
|
|
717
484
|
|
|
485
|
+
// Спрашиваются только основные настройки в порядке работы. Редкие получают defaults и
|
|
486
|
+
// меняются вручную в docs/settings.yaml, как и модели review.agents.
|
|
718
487
|
export async function askSettings({
|
|
719
488
|
input = process.stdin,
|
|
720
489
|
output = process.stdout,
|
|
@@ -731,161 +500,57 @@ export async function askSettings({
|
|
|
731
500
|
}
|
|
732
501
|
|
|
733
502
|
const requestedSections = new Set(sections);
|
|
734
|
-
const shouldAsk = key => missingKeys === null || missingKeys.has(key);
|
|
735
|
-
const
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
if (requestedSections.has('plan') && shouldAsk('plan.review')) {
|
|
753
|
-
settings.plan.review = selected.includes('planReview');
|
|
754
|
-
}
|
|
755
|
-
if (requestedSections.has('send-review') && shouldAsk('sendReview.closePreviousReviews')) {
|
|
756
|
-
settings.sendReview.closePreviousReviews = selected.includes('sendReviewClosePreviousReviews');
|
|
757
|
-
}
|
|
758
|
-
if (requestedSections.has('discover-automations') && shouldAsk('discoverAutomations.includePlans')) {
|
|
759
|
-
settings.discoverAutomations.includePlans = selected.includes('discoverAutomationsIncludePlans');
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
if (requestedSections.has('orhestra') && shouldAsk('orhestra.mode')) {
|
|
763
|
-
settings.orhestra.mode = await selectPrompt({
|
|
764
|
-
message: 'Как eda-orhestra должен вести полный цикл по умолчанию?',
|
|
765
|
-
choices: ORHESTRA_MODE_CHOICES,
|
|
766
|
-
default: settings.orhestra.mode
|
|
767
|
-
}, { input, output });
|
|
503
|
+
const shouldAsk = (section, key) => requestedSections.has(section) && (missingKeys === null || missingKeys.has(key));
|
|
504
|
+
const scalarPrompts = [
|
|
505
|
+
['plan', 'plan.size', settings.plan, 'size', 'eda-plan · размер плана', PLAN_SIZE_CHOICES],
|
|
506
|
+
['plan', 'plan.decisionMode', settings.plan, 'decisionMode', 'eda-plan · как принимать существенные решения', DECISION_MODE_CHOICES],
|
|
507
|
+
['plan', 'plan.testStrategy', settings.plan, 'testStrategy', 'eda-plan · когда писать тесты и запускать проверки', TEST_STRATEGY_CHOICES],
|
|
508
|
+
['plan-execute', 'planExecute.mode', settings.planExecute, 'mode', 'eda-plan-execute · где выполнять фазы плана', PLAN_EXECUTE_MODE_CHOICES],
|
|
509
|
+
['review', 'review.execution', settings.review, 'execution', 'eda-review · где выполнять проверки', REVIEW_EXECUTION_CHOICES],
|
|
510
|
+
['polish', 'polish.execution', settings.polish, 'execution', 'eda-polish · где выполнять ревью и исправления', POLISH_EXECUTION_CHOICES],
|
|
511
|
+
['manual-test', 'manualTest.depth', settings.manualTest, 'depth', 'eda-manual-test · глубина ручной проверки', MANUAL_TEST_DEPTH_CHOICES],
|
|
512
|
+
['orhestra', 'orhestra.mode', settings.orhestra, 'mode', 'eda-orhestra · как вести полный цикл', ORHESTRA_MODE_CHOICES]
|
|
513
|
+
].filter(([section, key]) => shouldAsk(section, key));
|
|
514
|
+
const askSteps = shouldAsk('orhestra', 'orhestra.steps');
|
|
515
|
+
const askOrhestraExecution = shouldAsk('orhestra', 'orhestra.execution');
|
|
516
|
+
if (scalarPrompts.length === 0 && !askSteps && !askOrhestraExecution) return settings;
|
|
517
|
+
|
|
518
|
+
startPrompts(`Настройки ${SETTINGS_RELATIVE_PATH}`, { output });
|
|
519
|
+
for (const [, , target, property, message, choices] of scalarPrompts) {
|
|
520
|
+
target[property] = await selectPrompt({ message, choices, default: target[property] }, { input, output });
|
|
768
521
|
}
|
|
769
|
-
|
|
770
|
-
if (requestedSections.has('orhestra') && shouldAsk('orhestra.steps')) {
|
|
522
|
+
if (askSteps) {
|
|
771
523
|
const enabledSteps = await checkboxPrompt({
|
|
772
|
-
message: '
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
524
|
+
message: 'eda-orhestra · какие этапы включить, кроме плана и выполнения',
|
|
525
|
+
choices: ORHESTRA_STEP_DEFINITIONS
|
|
526
|
+
.filter(step => ORHESTRA_OPTIONAL_STEPS.includes(step.skill))
|
|
527
|
+
.map(step => ({
|
|
528
|
+
name: step.name,
|
|
529
|
+
value: step.skill,
|
|
530
|
+
checked: settings.orhestra.steps.find(item => item.skill === step.skill)?.enabled !== false
|
|
531
|
+
}))
|
|
779
532
|
}, { input, output });
|
|
780
|
-
const codePolishLimit = enabledSteps.includes('eda-polish')
|
|
781
|
-
? await selectPrompt({
|
|
782
|
-
message: 'Сколько итераций разрешить eda-polish внутри eda-orhestra?',
|
|
783
|
-
choices: ORHESTRA_POLISH_LIMIT_CHOICES,
|
|
784
|
-
default: 5
|
|
785
|
-
}, { input, output })
|
|
786
|
-
: 5;
|
|
787
533
|
settings.orhestra.steps = DEFAULT_ORHESTRA_STEPS.map(step => ({
|
|
788
534
|
...step,
|
|
789
|
-
enabled: enabledSteps.includes(step.skill)
|
|
790
|
-
...(step.skill === 'eda-polish' ? { args: `limit ${codePolishLimit}` } : {})
|
|
535
|
+
enabled: !ORHESTRA_OPTIONAL_STEPS.includes(step.skill) || enabledSteps.includes(step.skill)
|
|
791
536
|
}));
|
|
792
537
|
}
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
['plan', 'plan.decisionMode', settings.plan, 'decisionMode', 'Как принимать важные решения в eda-plan?', DECISION_MODE_CHOICES],
|
|
800
|
-
['plan', 'plan.testStrategy', settings.plan, 'testStrategy', 'Какую стратегию тестов и проверок eda-plan использовать по умолчанию?', TEST_STRATEGY_CHOICES],
|
|
801
|
-
['plan', 'plan.loggingStrategy', settings.plan, 'loggingStrategy', 'Какую стратегию логирования eda-plan использовать по умолчанию?', LOGGING_STRATEGY_CHOICES],
|
|
802
|
-
['plan-review', 'planReview.threshold', settings.planReview, 'threshold', 'Какой порог готовности использовать в eda-plan-review?', PLAN_REVIEW_THRESHOLD_CHOICES],
|
|
803
|
-
['plan-polish', 'planPolish.limit', settings.planPolish, 'limit', 'Сколько итераций «проверка + исправление» разрешить eda-plan-polish?', PLAN_POLISH_LIMIT_CHOICES],
|
|
804
|
-
['plan-execute', 'planExecute.mode', settings.planExecute, 'mode', 'Как eda-plan-execute должен выполнять фазы по умолчанию?', PLAN_EXECUTE_MODE_CHOICES],
|
|
805
|
-
['polish', 'polish.execution', settings.polish, 'execution', 'Как eda-polish должен выполнять review и исправления по умолчанию?', POLISH_EXECUTION_CHOICES],
|
|
806
|
-
['manual-test', 'manualTest.depth', settings.manualTest, 'depth', 'Какую глубину ручной проверки использовать в eda-manual-test по умолчанию?', MANUAL_TEST_DEPTH_CHOICES],
|
|
807
|
-
['review', 'review.execution', settings.review, 'execution', 'Как eda-review должен выполнять проверки по умолчанию?', REVIEW_EXECUTION_CHOICES]
|
|
808
|
-
];
|
|
809
|
-
for (const [section, key, target, property, message, choices] of scalarPrompts) {
|
|
810
|
-
if (!requestedSections.has(section) || !shouldAsk(key)) continue;
|
|
811
|
-
target[property] = await selectPrompt({ message, choices, default: target[property] }, { input, output });
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
if (requestedSections.has('review')) {
|
|
815
|
-
settings.review.agents = await askReviewAgentSettings({
|
|
816
|
-
input,
|
|
817
|
-
output,
|
|
818
|
-
selectPrompt,
|
|
819
|
-
seedAgents: settings.review.agents,
|
|
820
|
-
missingKeys,
|
|
821
|
-
skipModels: settings.review.execution === 'main'
|
|
822
|
-
});
|
|
538
|
+
if (askOrhestraExecution) {
|
|
539
|
+
settings.orhestra.execution = await selectPrompt({
|
|
540
|
+
message: 'eda-orhestra · где выполнять этапы',
|
|
541
|
+
choices: ORHESTRA_EXECUTION_CHOICES,
|
|
542
|
+
default: settings.orhestra.execution
|
|
543
|
+
}, { input, output });
|
|
823
544
|
}
|
|
545
|
+
finishPrompts(`Остальные настройки — в ${SETTINGS_RELATIVE_PATH}`, { output });
|
|
824
546
|
return settings;
|
|
825
547
|
}
|
|
826
548
|
|
|
827
|
-
export async function askReviewAgentSettings({
|
|
828
|
-
input = process.stdin,
|
|
829
|
-
output = process.stdout,
|
|
830
|
-
selectPrompt = select,
|
|
831
|
-
seedAgents = DEFAULT_REVIEW_AGENTS,
|
|
832
|
-
missingKeys = null,
|
|
833
|
-
skipModels = false
|
|
834
|
-
} = {}) {
|
|
835
|
-
return askAgentSettings({
|
|
836
|
-
input,
|
|
837
|
-
output,
|
|
838
|
-
selectPrompt,
|
|
839
|
-
definitions: REVIEW_AGENT_DEFINITIONS,
|
|
840
|
-
seedAgents,
|
|
841
|
-
missingKeys,
|
|
842
|
-
keyPrefix: 'review.agents',
|
|
843
|
-
label: 'review',
|
|
844
|
-
skipModels
|
|
845
|
-
});
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
async function askAgentSettings({ input, output, selectPrompt, definitions, seedAgents, missingKeys, keyPrefix, label, skipModels = false }) {
|
|
849
|
-
const configuredAgents = structuredClone(seedAgents);
|
|
850
|
-
const shouldAsk = key => missingKeys === null || missingKeys.has(key);
|
|
851
|
-
for (const agent of definitions) {
|
|
852
|
-
const modeKey = `${keyPrefix}.${agent.key}.mode`;
|
|
853
|
-
const claudeKey = `${keyPrefix}.${agent.key}.model.claude`;
|
|
854
|
-
const codexKey = `${keyPrefix}.${agent.key}.model.codex`;
|
|
855
|
-
let mode = configuredAgents[agent.key].mode;
|
|
856
|
-
if (shouldAsk(modeKey)) {
|
|
857
|
-
mode = await selectPrompt({
|
|
858
|
-
message: `Когда запускать ${label}-проверку ${agent.key}?`,
|
|
859
|
-
choices: REVIEW_MODE_CHOICES,
|
|
860
|
-
default: mode
|
|
861
|
-
}, { input, output });
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
let claude = configuredAgents[agent.key].model.claude;
|
|
865
|
-
let codex = configuredAgents[agent.key].model.codex;
|
|
866
|
-
if (!skipModels && mode !== 'off' && shouldAsk(claudeKey)) {
|
|
867
|
-
claude = await selectPrompt({
|
|
868
|
-
message: `Какой моделью Claude проверять ${agent.key}?`,
|
|
869
|
-
choices: CLAUDE_REVIEW_MODEL_CHOICES,
|
|
870
|
-
default: claude
|
|
871
|
-
}, { input, output });
|
|
872
|
-
}
|
|
873
|
-
if (!skipModels && mode !== 'off' && shouldAsk(codexKey)) {
|
|
874
|
-
codex = await selectPrompt({
|
|
875
|
-
message: `Какой моделью Codex проверять ${agent.key}?`,
|
|
876
|
-
choices: CODEX_REVIEW_MODEL_CHOICES,
|
|
877
|
-
default: codex
|
|
878
|
-
}, { input, output });
|
|
879
|
-
}
|
|
880
|
-
configuredAgents[agent.key] = { mode, model: { claude, codex } };
|
|
881
|
-
}
|
|
882
|
-
return configuredAgents;
|
|
883
|
-
}
|
|
884
|
-
|
|
885
549
|
async function detectTargets(cwd) {
|
|
886
550
|
const targets = [];
|
|
887
|
-
|
|
888
|
-
|
|
551
|
+
for (const { value } of TARGET_CHOICES) {
|
|
552
|
+
if (await targetStructureExists(cwd, value)) targets.push(value);
|
|
553
|
+
}
|
|
889
554
|
return targets;
|
|
890
555
|
}
|
|
891
556
|
|
|
@@ -893,8 +558,9 @@ async function detectTargets(cwd) {
|
|
|
893
558
|
// или manifest владения. Одних пустых/чужих каталогов skills и agents недостаточно.
|
|
894
559
|
async function detectInstalledTargets(cwd) {
|
|
895
560
|
const targets = [];
|
|
896
|
-
|
|
897
|
-
|
|
561
|
+
for (const { value } of TARGET_CHOICES) {
|
|
562
|
+
if (await hasInstalledPackage(cwd, value)) targets.push(value);
|
|
563
|
+
}
|
|
898
564
|
return targets;
|
|
899
565
|
}
|
|
900
566
|
|
|
@@ -908,7 +574,7 @@ async function targetStructureExists(cwd, target) {
|
|
|
908
574
|
|
|
909
575
|
async function hasInstalledPackage(cwd, target) {
|
|
910
576
|
const { skillsDir, agentsDir, manifestPath, legacySkillsDir } = getTargetPaths(cwd, target);
|
|
911
|
-
const agentExtension = target === '
|
|
577
|
+
const agentExtension = target === 'codex' ? '.toml' : '.md';
|
|
912
578
|
return await fileExists(manifestPath)
|
|
913
579
|
|| await hasInstalledSkill(skillsDir)
|
|
914
580
|
|| (legacySkillsDir !== null && await hasInstalledSkill(legacySkillsDir))
|
|
@@ -952,13 +618,9 @@ async function syncPackage(cwd, targets, output = process.stdout, { action = 'up
|
|
|
952
618
|
|
|
953
619
|
const changedSkills = new Set();
|
|
954
620
|
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);
|
|
621
|
+
for (const { value } of TARGET_CHOICES) {
|
|
622
|
+
if (!targets.includes(value)) continue;
|
|
623
|
+
const result = await installTarget(cwd, value, skills, agents, packageVersion, output);
|
|
962
624
|
for (const skillName of result.changedSkills) changedSkills.add(skillName);
|
|
963
625
|
for (const agentName of result.changedAgents) changedAgents.add(agentName);
|
|
964
626
|
}
|
|
@@ -1115,6 +777,12 @@ async function ensureSettings(cwd, { input = process.stdin, output = process.std
|
|
|
1115
777
|
if (migrated.retiredPaths.length > 0) {
|
|
1116
778
|
output.write(`Убираю устаревшие настройки: ${migrated.retiredPaths.join(', ')}.\n`);
|
|
1117
779
|
}
|
|
780
|
+
if (migrated.legacyReviewAgents) {
|
|
781
|
+
output.write('Перевожу review.agents на формат 4.0: все роли — default, нужные модели впишите вручную.\n');
|
|
782
|
+
}
|
|
783
|
+
if (migrated.resetReviewAgents.length > 0) {
|
|
784
|
+
output.write(`Заменяю недопустимые модели review.agents на default: ${migrated.resetReviewAgents.join(', ')}.\n`);
|
|
785
|
+
}
|
|
1118
786
|
if (needsMigration) {
|
|
1119
787
|
output.write(`Переношу ${SETTINGS_RELATIVE_PATH} на version: ${SETTINGS_VERSION}; спрашиваются только отсутствующие значения.\n`);
|
|
1120
788
|
}
|
|
@@ -1153,6 +821,12 @@ async function configureSettings(cwd, { input = process.stdin, output = process.
|
|
|
1153
821
|
if (migrated.retiredPaths.length > 0) {
|
|
1154
822
|
output.write(`Убираю устаревшие настройки: ${migrated.retiredPaths.join(', ')}.\n`);
|
|
1155
823
|
}
|
|
824
|
+
if (migrated.legacyReviewAgents) {
|
|
825
|
+
output.write('Перевожу review.agents на формат 4.0: все роли — default, нужные модели впишите вручную.\n');
|
|
826
|
+
}
|
|
827
|
+
if (migrated.resetReviewAgents.length > 0) {
|
|
828
|
+
output.write(`Заменяю недопустимые модели review.agents на default: ${migrated.resetReviewAgents.join(', ')}.\n`);
|
|
829
|
+
}
|
|
1156
830
|
if (needsMigration) {
|
|
1157
831
|
output.write(`Переношу ${SETTINGS_RELATIVE_PATH} на version: ${SETTINGS_VERSION}; спрашиваются только отсутствующие значения.\n`);
|
|
1158
832
|
}
|
|
@@ -1172,7 +846,9 @@ function resolveSettingsUpdate(existingContent) {
|
|
|
1172
846
|
const migrated = migrateSettingsContent(existingContent);
|
|
1173
847
|
const needsMigration = migrated.version !== SETTINGS_VERSION
|
|
1174
848
|
|| migrated.missingKeys.size > 0
|
|
1175
|
-
|| migrated.normalizedPaths.length > 0
|
|
849
|
+
|| migrated.normalizedPaths.length > 0
|
|
850
|
+
|| migrated.legacyReviewAgents
|
|
851
|
+
|| migrated.resetReviewAgents.length > 0;
|
|
1176
852
|
const rendered = formatSettings(migrated.settings);
|
|
1177
853
|
return { migrated, needsMigration, rendered, formatChanged: rendered !== existingContent };
|
|
1178
854
|
}
|
|
@@ -1243,7 +919,6 @@ function migrateSettingsContent(content) {
|
|
|
1243
919
|
|
|
1244
920
|
const plan = objectValue(raw.plan);
|
|
1245
921
|
transfer('plan.strict', plan.strict ?? legacyStrict, bool, value => { settings.plan.strict = value; });
|
|
1246
|
-
transfer('plan.review', plan.review ?? plan.meta_review, bool, value => { settings.plan.review = value; });
|
|
1247
922
|
transfer(
|
|
1248
923
|
'plan.size',
|
|
1249
924
|
plan.size ?? legacyDefaults.plan_size,
|
|
@@ -1306,17 +981,7 @@ function migrateSettingsContent(content) {
|
|
|
1306
981
|
oneOf(['subagents', 'main']),
|
|
1307
982
|
value => { settings.review.execution = value; }
|
|
1308
983
|
);
|
|
1309
|
-
|
|
1310
|
-
rawAgents: objectValue(review.agents),
|
|
1311
|
-
settingsAgents: settings.review.agents,
|
|
1312
|
-
definitions: REVIEW_AGENT_DEFINITIONS,
|
|
1313
|
-
prefix: 'review.agents',
|
|
1314
|
-
missingKeys
|
|
1315
|
-
});
|
|
1316
|
-
if (typeof review.include_code_quality === 'boolean' && !objectValue(review.agents).code_quality) {
|
|
1317
|
-
settings.review.agents.code_quality.mode = review.include_code_quality ? 'always' : 'off';
|
|
1318
|
-
missingKeys.delete('review.agents.code_quality.mode');
|
|
1319
|
-
}
|
|
984
|
+
const { legacyReviewAgents, resetReviewAgents } = transferReviewAgents(objectValue(review.agents), settings.review.agents);
|
|
1320
985
|
|
|
1321
986
|
const sendReview = objectValue(raw['send-review']);
|
|
1322
987
|
transfer(
|
|
@@ -1338,25 +1003,27 @@ function migrateSettingsContent(content) {
|
|
|
1338
1003
|
settings,
|
|
1339
1004
|
missingKeys,
|
|
1340
1005
|
normalizedPaths,
|
|
1006
|
+
legacyReviewAgents,
|
|
1007
|
+
resetReviewAgents,
|
|
1341
1008
|
retiredPaths: findRetiredSettingsPaths(raw),
|
|
1342
1009
|
version: Number.isInteger(raw.version) ? raw.version : null
|
|
1343
1010
|
};
|
|
1344
1011
|
}
|
|
1345
1012
|
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
}
|
|
1013
|
+
// Формат до 4.0 ({ mode, model } по платформам) не переносится: при мажорном обновлении
|
|
1014
|
+
// все роли получают default. Иное значение не в формате default | provider/model#variant тоже
|
|
1015
|
+
// заменяется на default.
|
|
1016
|
+
function transferReviewAgents(rawAgents, settingsAgents) {
|
|
1017
|
+
const legacyReviewAgents = Object.values(rawAgents).some(value => value && typeof value === 'object');
|
|
1018
|
+
const resetReviewAgents = [];
|
|
1019
|
+
if (legacyReviewAgents) return { legacyReviewAgents, resetReviewAgents };
|
|
1020
|
+
for (const key of REVIEW_AGENT_KEYS) {
|
|
1021
|
+
if (!Object.hasOwn(rawAgents, key)) continue;
|
|
1022
|
+
const value = typeof rawAgents[key] === 'string' ? rawAgents[key].trim() : '';
|
|
1023
|
+
if (REVIEW_AGENT_MODEL_PATTERN.test(value)) settingsAgents[key] = value;
|
|
1024
|
+
else resetReviewAgents.push(key);
|
|
1025
|
+
}
|
|
1026
|
+
return { legacyReviewAgents, resetReviewAgents };
|
|
1360
1027
|
}
|
|
1361
1028
|
|
|
1362
1029
|
// У eda-polish больше нет порога: цикл завершается по нулю находок.
|
|
@@ -1457,66 +1124,36 @@ ${onFailureThen}
|
|
|
1457
1124
|
# Строка аргументов передаётся скиллу как часть текущего вызова.
|
|
1458
1125
|
args: ${JSON.stringify(step.args ?? '')}${onFailure}`;
|
|
1459
1126
|
}).join('\n');
|
|
1460
|
-
const
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
# ${agent.auto}
|
|
1464
|
-
${agent.key}:
|
|
1465
|
-
# always — всегда, auto — когда применимо, off — отключено.
|
|
1466
|
-
mode: ${configured.mode}
|
|
1467
|
-
model:
|
|
1468
|
-
# Модель для Claude Code.
|
|
1469
|
-
claude: ${configured.model.claude}
|
|
1470
|
-
# Модель для Codex.
|
|
1471
|
-
codex: ${configured.model.codex}`;
|
|
1472
|
-
}).join('\n\n');
|
|
1473
|
-
const reviewAgents = formatAgents(REVIEW_AGENT_DEFINITIONS, settings.review.agents, DEFAULT_REVIEW_AGENTS);
|
|
1127
|
+
const reviewAgents = REVIEW_AGENT_KEYS
|
|
1128
|
+
.map(key => ` ${key}: ${settings.review.agents[key] ?? DEFAULT_REVIEW_AGENTS[key]}`)
|
|
1129
|
+
.join('\n');
|
|
1474
1130
|
|
|
1475
1131
|
return `version: ${SETTINGS_VERSION}
|
|
1476
1132
|
|
|
1477
|
-
orhestra:
|
|
1478
|
-
# Режим полного цикла eda-orhestra.
|
|
1479
|
-
# automatic | manual
|
|
1480
|
-
mode: ${orhestra.mode ?? 'automatic'}
|
|
1481
|
-
# Где выполняются этапы и передаётся ли main совместимым вложенным скилам.
|
|
1482
|
-
# subagents | main | main_propagated
|
|
1483
|
-
execution: ${orhestra.execution ?? 'subagents'}
|
|
1484
|
-
# Упорядоченная цепочка. Шаги можно отключать, удалять и переставлять.
|
|
1485
|
-
steps:
|
|
1486
|
-
${orhestraSteps}
|
|
1487
|
-
|
|
1488
|
-
aim:
|
|
1489
|
-
# Режим ответов на рабочие вопросы eda-aim.
|
|
1490
|
-
# automatic | manual
|
|
1491
|
-
mode: ${aim.mode ?? 'automatic'}
|
|
1492
|
-
|
|
1493
1133
|
explore:
|
|
1494
1134
|
# Включает кросс-CLI ревью в eda-explore.
|
|
1495
|
-
#
|
|
1135
|
+
# false | true
|
|
1496
1136
|
strict: ${settings.explore.strict ? 'true' : 'false'}
|
|
1497
|
-
#
|
|
1498
|
-
#
|
|
1137
|
+
# Как eda-explore ведёт исследовательские развилки.
|
|
1138
|
+
# recommend_and_ask | autonomous | ask_each_time
|
|
1499
1139
|
decision_mode: ${settings.explore.decisionMode}
|
|
1500
1140
|
|
|
1501
1141
|
plan:
|
|
1502
|
-
#
|
|
1503
|
-
# true | false
|
|
1504
|
-
strict: ${settings.plan.strict ? 'true' : 'false'}
|
|
1505
|
-
# Запускает изолированный цикл eda-plan-polish.
|
|
1506
|
-
# true | false
|
|
1507
|
-
review: ${settings.plan.review !== false ? 'true' : 'false'}
|
|
1508
|
-
# Задаёт размер плана.
|
|
1142
|
+
# Размер плана.
|
|
1509
1143
|
# normal | short | ask_each_time
|
|
1510
1144
|
size: ${settings.plan.size}
|
|
1511
|
-
#
|
|
1512
|
-
#
|
|
1145
|
+
# Как eda-plan принимает существенные решения.
|
|
1146
|
+
# recommend_and_ask | autonomous | ask_each_time
|
|
1513
1147
|
decision_mode: ${settings.plan.decisionMode}
|
|
1514
|
-
#
|
|
1515
|
-
#
|
|
1148
|
+
# Когда писать тесты и запускать автоматические проверки.
|
|
1149
|
+
# phase_tests_final_checks | tdd_each_phase | after_each_phase | end_of_plan | ask_each_time
|
|
1516
1150
|
test_strategy: ${settings.plan.testStrategy}
|
|
1517
|
-
#
|
|
1518
|
-
#
|
|
1151
|
+
# Стратегия логирования.
|
|
1152
|
+
# standard | debug_precise | ask_each_time
|
|
1519
1153
|
logging_strategy: ${settings.plan.loggingStrategy}
|
|
1154
|
+
# Включает кросс-CLI ревью в eda-plan.
|
|
1155
|
+
# false | true
|
|
1156
|
+
strict: ${settings.plan.strict ? 'true' : 'false'}
|
|
1520
1157
|
|
|
1521
1158
|
plan-review:
|
|
1522
1159
|
# Доля закрытых пунктов чек-листа, при которой план считается готовым.
|
|
@@ -1530,9 +1167,21 @@ plan-polish:
|
|
|
1530
1167
|
|
|
1531
1168
|
plan-execute:
|
|
1532
1169
|
# Где eda-plan-execute выполняет фазы плана.
|
|
1533
|
-
#
|
|
1170
|
+
# main — весь план одним контекстом, auto — решает по плану, subagents — каждая фаза в отдельном субагенте.
|
|
1534
1171
|
mode: ${settings.planExecute.mode}
|
|
1535
1172
|
|
|
1173
|
+
review:
|
|
1174
|
+
# Где eda-review выполняет выбранные проверки.
|
|
1175
|
+
# main — все проверки основным агентом без субагентов, subagents — разведчик и отдельный агент на проверку.
|
|
1176
|
+
execution: ${settings.review.execution ?? 'main'}
|
|
1177
|
+
# Какие проверки нужны, решает eda-review-triage по содержимому ревью.
|
|
1178
|
+
# Модель роли в subagents: default — модель установленного агента, иначе provider/model#variant,
|
|
1179
|
+
# например anthropic/claude-opus-5-5#high или deepseek/deepseek-flash-4.1; variant задаёт effort.
|
|
1180
|
+
# Если среда не может запустить модель, роль идёт на модели агента с предупреждением в ревью.
|
|
1181
|
+
# В main все проверки выполняет текущая модель.
|
|
1182
|
+
agents:
|
|
1183
|
+
${reviewAgents}
|
|
1184
|
+
|
|
1536
1185
|
polish:
|
|
1537
1186
|
# Где eda-polish выполняет review и исправления цикла полировки.
|
|
1538
1187
|
# subagents — каждое review и каждое исправление в отдельном изолированном субагенте, main — оба контракта основным агентом.
|
|
@@ -1545,14 +1194,6 @@ manual-test:
|
|
|
1545
1194
|
# full | smoke | ask_each_time
|
|
1546
1195
|
depth: ${settings.manualTest.depth}
|
|
1547
1196
|
|
|
1548
|
-
review:
|
|
1549
|
-
# Где eda-review выполняет выбранные проверки.
|
|
1550
|
-
# subagents — отдельный специализированный агент на проверку, main — все проверки основным агентом без субагентов.
|
|
1551
|
-
execution: ${settings.review.execution ?? 'subagents'}
|
|
1552
|
-
# Каждая проверка имеет собственный режим запуска; модели применяются только в subagents.
|
|
1553
|
-
agents:
|
|
1554
|
-
${reviewAgents}
|
|
1555
|
-
|
|
1556
1197
|
send-review:
|
|
1557
1198
|
# После успешной отправки скрывает предыдущие сводки eda-send-review и резолвит их inline-треды.
|
|
1558
1199
|
# true | false
|
|
@@ -1560,8 +1201,24 @@ send-review:
|
|
|
1560
1201
|
|
|
1561
1202
|
discover-automations:
|
|
1562
1203
|
# Добавляет docs/artifacts/plans/ в обычный запуск eda-discover-automations.
|
|
1563
|
-
#
|
|
1204
|
+
# false | true
|
|
1564
1205
|
include_plans: ${settings.discoverAutomations.includePlans ? 'true' : 'false'}
|
|
1206
|
+
|
|
1207
|
+
orhestra:
|
|
1208
|
+
# Режим полного цикла eda-orhestra.
|
|
1209
|
+
# automatic | manual
|
|
1210
|
+
mode: ${orhestra.mode ?? 'automatic'}
|
|
1211
|
+
# Где выполняются этапы и передаётся ли main совместимым вложенным скилам.
|
|
1212
|
+
# subagents | main | main_propagated
|
|
1213
|
+
execution: ${orhestra.execution ?? 'subagents'}
|
|
1214
|
+
# Упорядоченная цепочка. Шаги можно отключать, удалять и переставлять.
|
|
1215
|
+
steps:
|
|
1216
|
+
${orhestraSteps}
|
|
1217
|
+
|
|
1218
|
+
aim:
|
|
1219
|
+
# Режим ответов на рабочие вопросы eda-aim.
|
|
1220
|
+
# automatic | manual
|
|
1221
|
+
mode: ${aim.mode ?? 'automatic'}
|
|
1565
1222
|
`;
|
|
1566
1223
|
}
|
|
1567
1224
|
|
|
@@ -1592,10 +1249,9 @@ async function installTarget(cwd, target, skills, agents, packageVersion, output
|
|
|
1592
1249
|
|
|
1593
1250
|
if (agents.length > 0) await fs.mkdir(agentsDir, { recursive: true });
|
|
1594
1251
|
for (const agent of agents) {
|
|
1595
|
-
const extension = target === '
|
|
1596
|
-
const
|
|
1597
|
-
|
|
1598
|
-
: renderCodexAgent(agent, agent.prompt);
|
|
1252
|
+
const extension = target === 'codex' ? '.toml' : '.md';
|
|
1253
|
+
const renderAgent = { claude: renderClaudeAgent, codex: renderCodexAgent, opencode: renderOpenCodeAgent }[target];
|
|
1254
|
+
const rendered = renderAgent(agent, agent.prompt);
|
|
1599
1255
|
const changed = await writeManagedFile(path.join(agentsDir, `${agent.name}${extension}`), rendered);
|
|
1600
1256
|
if (changed) changedAgents.push(agent.name);
|
|
1601
1257
|
}
|
|
@@ -1611,7 +1267,7 @@ async function installTarget(cwd, target, skills, agents, packageVersion, output
|
|
|
1611
1267
|
agents: agents.map(agent => agent.name)
|
|
1612
1268
|
});
|
|
1613
1269
|
|
|
1614
|
-
const label =
|
|
1270
|
+
const label = TARGET_CHOICES.find(choice => choice.value === target).label;
|
|
1615
1271
|
const location = skillsDir === path.join(targetRoot, 'skills')
|
|
1616
1272
|
? targetRoot
|
|
1617
1273
|
: `${skillsDir}; ${agentsDir}`;
|
|
@@ -1637,10 +1293,9 @@ async function buildSkillFiles(skill, target) {
|
|
|
1637
1293
|
const entrypoint = files.get('SKILL.md');
|
|
1638
1294
|
if (!entrypoint) throw new Error(`У скила ${skill.name} нет SKILL.md.`);
|
|
1639
1295
|
|
|
1640
|
-
const content = entrypoint.toString('utf8');
|
|
1641
|
-
const
|
|
1642
|
-
|
|
1643
|
-
: renderCodexSkill(content, skill.config);
|
|
1296
|
+
const content = await expandIncludes(`Скил ${skill.name}`, entrypoint.toString('utf8'), PACKAGE_ROOT);
|
|
1297
|
+
const renderSkill = { claude: renderClaudeSkill, codex: renderCodexSkill, opencode: renderOpenCodeSkill }[target];
|
|
1298
|
+
const rendered = renderSkill(content, skill.config);
|
|
1644
1299
|
files.set('SKILL.md', Buffer.from(rendered));
|
|
1645
1300
|
return files;
|
|
1646
1301
|
}
|
|
@@ -1828,7 +1483,7 @@ async function removeRetiredComponents(skillsDir, agentsDir, target, previousMan
|
|
|
1828
1483
|
await fs.rm(path.join(skillsDir, `${skillName}.md`), { force: true });
|
|
1829
1484
|
}
|
|
1830
1485
|
|
|
1831
|
-
const extension = target === '
|
|
1486
|
+
const extension = target === 'codex' ? '.toml' : '.md';
|
|
1832
1487
|
for (const agentName of staleAgents) {
|
|
1833
1488
|
await fs.rm(path.join(agentsDir, `${agentName}${extension}`), { force: true });
|
|
1834
1489
|
}
|