@gian-tiaga/eda 3.14.0 → 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 +131 -223
- 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/lib/catalog.js +28 -1
- package/lib/install.js +221 -590
- 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-skill.js +2 -2
- package/package.json +2 -3
- 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,12 +1,11 @@
|
|
|
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';
|
|
@@ -42,6 +41,8 @@ const TARGET_CHOICES = [
|
|
|
42
41
|
{ value: 'codex', label: 'Codex CLI', dir: '.agents/skills/' },
|
|
43
42
|
{ value: 'opencode', label: 'OpenCode', dir: '.opencode/skills/' }
|
|
44
43
|
];
|
|
44
|
+
// В вопросах платформы показываются в этом порядке; установка идёт в порядке TARGET_CHOICES.
|
|
45
|
+
const TARGET_PROMPT_ORDER = ['claude', 'opencode', 'codex'];
|
|
45
46
|
const UPDATE_ALL_MAX_DEPTH = 2;
|
|
46
47
|
const UPDATE_ALL_SKIP_DIRS = new Set(['.git', '.agents', '.claude', '.codex', '.opencode', 'node_modules']);
|
|
47
48
|
const RETIRED_SKILLS = [
|
|
@@ -68,6 +69,8 @@ const RETIRED_AGENTS = [
|
|
|
68
69
|
'eda-plan-review-previous'
|
|
69
70
|
];
|
|
70
71
|
const RETIRED_SETTINGS_PATHS = [
|
|
72
|
+
'plan.review',
|
|
73
|
+
'plan.meta_review',
|
|
71
74
|
'plan-review.agents',
|
|
72
75
|
'review-check',
|
|
73
76
|
'review.strict',
|
|
@@ -75,138 +78,30 @@ const RETIRED_SETTINGS_PATHS = [
|
|
|
75
78
|
];
|
|
76
79
|
const MANIFEST_FILE = 'eda-manifest.json';
|
|
77
80
|
const SETTINGS_VERSION = 3;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
mode: 'always',
|
|
100
|
-
claude: 'haiku',
|
|
101
|
-
codex: 'gpt-5.6-luna'
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
key: 'references',
|
|
105
|
-
description: 'Проверяет соответствие применимым карточкам из docs/references/.',
|
|
106
|
-
auto: 'В auto запускается только когда для изменений выбраны применимые карточки.',
|
|
107
|
-
mode: 'auto',
|
|
108
|
-
claude: 'haiku',
|
|
109
|
-
codex: 'gpt-5.6-luna'
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
key: 'business',
|
|
113
|
-
description: 'Проверяет изменённое поведение по применимым карточкам из docs/business/.',
|
|
114
|
-
auto: 'В auto запускается только когда изменения затрагивают поведение с применимой business-карточкой.',
|
|
115
|
-
mode: 'auto',
|
|
116
|
-
claude: 'sonnet',
|
|
117
|
-
codex: 'gpt-5.6-terra'
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
key: 'plan_alignment',
|
|
121
|
-
description: 'Проверяет, что реализация соответствует указанному или однозначно найденному плану.',
|
|
122
|
-
auto: 'В auto запускается только когда у ревью есть конкретный план.',
|
|
123
|
-
mode: 'auto',
|
|
124
|
-
claude: 'sonnet',
|
|
125
|
-
codex: 'gpt-5.6-terra'
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
key: 'code_quality',
|
|
129
|
-
description: 'Проверяет читаемость, сложность, дублирование и поддерживаемость кода.',
|
|
130
|
-
auto: 'Эта базовая проверка по умолчанию запускается всегда.',
|
|
131
|
-
mode: 'always',
|
|
132
|
-
claude: 'sonnet',
|
|
133
|
-
codex: 'gpt-5.6-terra'
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
key: 'tests',
|
|
137
|
-
description: 'Проверяет достаточность, корректность и устойчивость тестов.',
|
|
138
|
-
auto: 'Эта базовая проверка по умолчанию запускается всегда.',
|
|
139
|
-
mode: 'always',
|
|
140
|
-
claude: 'sonnet',
|
|
141
|
-
codex: 'gpt-5.6-terra'
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
key: 'security',
|
|
145
|
-
description: 'Проверяет уязвимости, права доступа, валидацию и работу с чувствительными данными.',
|
|
146
|
-
auto: 'В auto запускается для auth, permissions, внешнего ввода, файлов, сети, секретов и криптографии.',
|
|
147
|
-
mode: 'auto',
|
|
148
|
-
claude: 'opus',
|
|
149
|
-
codex: 'gpt-5.6-sol'
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
key: 'performance',
|
|
153
|
-
description: 'Проверяет запросы, циклы, память, конкурентность и другие риски производительности.',
|
|
154
|
-
auto: 'В auto запускается при изменении запросов, пакетной обработки, кешей, конкурентности или горячих путей.',
|
|
155
|
-
mode: 'auto',
|
|
156
|
-
claude: 'sonnet',
|
|
157
|
-
codex: 'gpt-5.6-terra'
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
key: 'frontend',
|
|
161
|
-
description: 'Проверяет фронтенд-код, UI, UX, адаптивность и состояния интерфейса.',
|
|
162
|
-
auto: 'В auto обязательно запускается при изменении компонентов, стилей, клиентских маршрутов или состояния UI.',
|
|
163
|
-
mode: 'auto',
|
|
164
|
-
claude: 'sonnet',
|
|
165
|
-
codex: 'gpt-5.6-terra'
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
key: 'api',
|
|
169
|
-
description: 'Проверяет API-контракты, совместимость, валидацию и обработку ошибок.',
|
|
170
|
-
auto: 'В auto запускается при изменении маршрутов, handlers, DTO, схем API, GraphQL или RPC.',
|
|
171
|
-
mode: 'auto',
|
|
172
|
-
claude: 'sonnet',
|
|
173
|
-
codex: 'gpt-5.6-terra'
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
key: 'database',
|
|
177
|
-
description: 'Проверяет схемы, миграции, запросы, индексы и целостность данных.',
|
|
178
|
-
auto: 'В auto запускается при изменении миграций, моделей хранения, SQL, схем или транзакций.',
|
|
179
|
-
mode: 'auto',
|
|
180
|
-
claude: 'opus',
|
|
181
|
-
codex: 'gpt-5.6-sol'
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
key: 'documentation',
|
|
185
|
-
description: 'Проверяет, обновлена ли документация для изменившегося пользовательского поведения.',
|
|
186
|
-
auto: 'В auto запускается при изменении публичного API, CLI, настроек или документируемого поведения.',
|
|
187
|
-
mode: 'auto',
|
|
188
|
-
claude: 'haiku',
|
|
189
|
-
codex: 'gpt-5.6-luna'
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
key: 'previous_reviews',
|
|
193
|
-
description: 'Перепроверяет незакрытые замечания предыдущих ревью для удалённого PR или MR.',
|
|
194
|
-
auto: 'В auto запускается только для ссылки на PR/MR или другой цели с доступными удалёнными обсуждениями.',
|
|
195
|
-
mode: 'auto',
|
|
196
|
-
claude: 'haiku',
|
|
197
|
-
codex: 'gpt-5.6-luna'
|
|
198
|
-
}
|
|
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'
|
|
199
102
|
];
|
|
200
|
-
const DEFAULT_REVIEW_AGENTS = Object.fromEntries(
|
|
201
|
-
|
|
202
|
-
{
|
|
203
|
-
mode: agent.mode,
|
|
204
|
-
model: {
|
|
205
|
-
claude: agent.claude,
|
|
206
|
-
codex: agent.codex
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
]));
|
|
103
|
+
const DEFAULT_REVIEW_AGENTS = Object.fromEntries(REVIEW_AGENT_KEYS.map(key => [key, 'default']));
|
|
104
|
+
const REVIEW_AGENT_MODEL_PATTERN = /^[\w.:-]+(?:\/[\w.:-]+)*(?:#[\w.-]+)?$/;
|
|
210
105
|
const ORHESTRA_STEP_DEFINITIONS = [
|
|
211
106
|
{
|
|
212
107
|
id: 'plan',
|
|
@@ -266,20 +161,19 @@ const DEFAULT_SETTINGS = {
|
|
|
266
161
|
},
|
|
267
162
|
plan: {
|
|
268
163
|
strict: false,
|
|
269
|
-
review: true,
|
|
270
164
|
size: 'normal',
|
|
271
165
|
decisionMode: 'recommend_and_ask',
|
|
272
|
-
testStrategy: '
|
|
273
|
-
loggingStrategy: '
|
|
166
|
+
testStrategy: 'phase_tests_final_checks',
|
|
167
|
+
loggingStrategy: 'standard'
|
|
274
168
|
},
|
|
275
169
|
planReview: {
|
|
276
170
|
threshold: 100
|
|
277
171
|
},
|
|
278
172
|
planPolish: {
|
|
279
|
-
limit:
|
|
173
|
+
limit: 5
|
|
280
174
|
},
|
|
281
175
|
planExecute: {
|
|
282
|
-
mode: '
|
|
176
|
+
mode: 'main'
|
|
283
177
|
},
|
|
284
178
|
polish: {
|
|
285
179
|
execution: 'subagents'
|
|
@@ -288,249 +182,83 @@ const DEFAULT_SETTINGS = {
|
|
|
288
182
|
depth: 'full'
|
|
289
183
|
},
|
|
290
184
|
review: {
|
|
291
|
-
execution: '
|
|
185
|
+
execution: 'main',
|
|
292
186
|
agents: DEFAULT_REVIEW_AGENTS
|
|
293
187
|
},
|
|
294
188
|
sendReview: {
|
|
295
|
-
closePreviousReviews:
|
|
189
|
+
closePreviousReviews: true
|
|
296
190
|
},
|
|
297
191
|
discoverAutomations: {
|
|
298
192
|
includePlans: false
|
|
299
193
|
}
|
|
300
194
|
};
|
|
195
|
+
// Разделы идут по ходу работы: исследование, план, его проверка, выполнение, ревью,
|
|
196
|
+
// ручные тесты; оркестраторы, которые собирают остальные скилы, — в конце.
|
|
301
197
|
const SETTINGS_SECTION_NAMES = [
|
|
302
|
-
'orhestra',
|
|
303
|
-
'aim',
|
|
304
198
|
'explore',
|
|
305
199
|
'plan',
|
|
306
200
|
'plan-review',
|
|
307
201
|
'plan-polish',
|
|
308
202
|
'plan-execute',
|
|
203
|
+
'review',
|
|
309
204
|
'polish',
|
|
310
205
|
'manual-test',
|
|
311
|
-
'review',
|
|
312
206
|
'send-review',
|
|
313
|
-
'discover-automations'
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
{
|
|
317
|
-
section: 'explore',
|
|
318
|
-
key: 'explore.strict',
|
|
319
|
-
value: 'exploreStrict',
|
|
320
|
-
name: 'Strict по умолчанию для eda-explore',
|
|
321
|
-
checked: DEFAULT_SETTINGS.explore.strict
|
|
322
|
-
},
|
|
323
|
-
{
|
|
324
|
-
section: 'plan',
|
|
325
|
-
key: 'plan.strict',
|
|
326
|
-
value: 'planStrict',
|
|
327
|
-
name: 'Strict по умолчанию для eda-plan',
|
|
328
|
-
checked: DEFAULT_SETTINGS.plan.strict
|
|
329
|
-
},
|
|
330
|
-
{
|
|
331
|
-
section: 'plan',
|
|
332
|
-
key: 'plan.review',
|
|
333
|
-
value: 'planReview',
|
|
334
|
-
name: 'Запускать eda-plan-review внутри eda-plan',
|
|
335
|
-
checked: DEFAULT_SETTINGS.plan.review
|
|
336
|
-
},
|
|
337
|
-
{
|
|
338
|
-
section: 'send-review',
|
|
339
|
-
key: 'sendReview.closePreviousReviews',
|
|
340
|
-
value: 'sendReviewClosePreviousReviews',
|
|
341
|
-
name: 'Закрывать предыдущие ревью eda-send-review',
|
|
342
|
-
checked: DEFAULT_SETTINGS.sendReview.closePreviousReviews
|
|
343
|
-
},
|
|
344
|
-
{
|
|
345
|
-
section: 'discover-automations',
|
|
346
|
-
key: 'discoverAutomations.includePlans',
|
|
347
|
-
value: 'discoverAutomationsIncludePlans',
|
|
348
|
-
name: 'Анализировать планы в eda-discover-automations по умолчанию',
|
|
349
|
-
checked: DEFAULT_SETTINGS.discoverAutomations.includePlans
|
|
350
|
-
}
|
|
351
|
-
];
|
|
352
|
-
const ORHESTRA_MODE_CHOICES = [
|
|
353
|
-
{
|
|
354
|
-
value: 'automatic',
|
|
355
|
-
name: 'Автоматически пройти план, выполнение, ревью и ручные тесты'
|
|
356
|
-
},
|
|
357
|
-
{
|
|
358
|
-
value: 'manual',
|
|
359
|
-
name: 'Писать план вместе с человеком и передавать ему вопросы'
|
|
360
|
-
}
|
|
361
|
-
];
|
|
362
|
-
const ORHESTRA_EXECUTION_CHOICES = [
|
|
363
|
-
{
|
|
364
|
-
value: 'subagents',
|
|
365
|
-
name: 'Каждый этап выполнять отдельным изолированным субагентом'
|
|
366
|
-
},
|
|
367
|
-
{
|
|
368
|
-
value: 'main',
|
|
369
|
-
name: 'Все этапы выполнять основным агентом без передачи режима внутрь'
|
|
370
|
-
},
|
|
371
|
-
{
|
|
372
|
-
value: 'main_propagated',
|
|
373
|
-
name: 'Все этапы выполнять основным агентом и передавать main совместимым скилам'
|
|
374
|
-
}
|
|
375
|
-
];
|
|
376
|
-
const AIM_MODE_CHOICES = [
|
|
377
|
-
{
|
|
378
|
-
value: 'automatic',
|
|
379
|
-
name: 'Автоматически отвечать на безопасные рабочие вопросы'
|
|
380
|
-
},
|
|
381
|
-
{
|
|
382
|
-
value: 'manual',
|
|
383
|
-
name: 'Передавать рабочие вопросы человеку'
|
|
384
|
-
}
|
|
385
|
-
];
|
|
386
|
-
const ORHESTRA_POLISH_LIMIT_CHOICES = [1, 2, 3, 5, 10].map(value => ({
|
|
387
|
-
value,
|
|
388
|
-
name: `${value} ${value === 1 ? 'итерация' : value < 5 ? 'итерации' : 'итераций'}`
|
|
389
|
-
}));
|
|
390
|
-
const PLAN_REVIEW_THRESHOLD_CHOICES = [80, 90, 100].map(value => ({
|
|
391
|
-
value,
|
|
392
|
-
name: `${value}/100`
|
|
393
|
-
}));
|
|
394
|
-
const PLAN_POLISH_LIMIT_CHOICES = [1, 2, 3].map(value => ({
|
|
395
|
-
value,
|
|
396
|
-
name: `${value} ${value === 1 ? 'итерация' : value < 5 ? 'итерации' : 'итераций'}`
|
|
397
|
-
}));
|
|
398
|
-
const PLAN_EXECUTE_MODE_CHOICES = [
|
|
399
|
-
{
|
|
400
|
-
value: 'auto',
|
|
401
|
-
name: 'Решать по плану: один контекст для простого плана, субагенты для остальных'
|
|
402
|
-
},
|
|
403
|
-
{
|
|
404
|
-
value: 'subagents',
|
|
405
|
-
name: 'Каждую фазу выполнять отдельным изолированным субагентом'
|
|
406
|
-
},
|
|
407
|
-
{
|
|
408
|
-
value: 'main',
|
|
409
|
-
name: 'Выполнять весь план основным агентом без субагентов'
|
|
410
|
-
}
|
|
411
|
-
];
|
|
412
|
-
const POLISH_EXECUTION_CHOICES = [
|
|
413
|
-
{
|
|
414
|
-
value: 'subagents',
|
|
415
|
-
name: 'Каждое review и исправление выполнять отдельным изолированным субагентом'
|
|
416
|
-
},
|
|
417
|
-
{
|
|
418
|
-
value: 'main',
|
|
419
|
-
name: 'Review и исправления выполнять основным агентом без субагентов'
|
|
420
|
-
}
|
|
207
|
+
'discover-automations',
|
|
208
|
+
'orhestra',
|
|
209
|
+
'aim'
|
|
421
210
|
];
|
|
211
|
+
// Варианты ответа: первым — значение по умолчанию, дальше — от ближайшего к дальнему,
|
|
212
|
+
// «Спрашивать каждый раз» всегда последним.
|
|
422
213
|
const PLAN_SIZE_CHOICES = [
|
|
423
|
-
{
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
},
|
|
427
|
-
{
|
|
428
|
-
value: 'short',
|
|
429
|
-
name: 'Короткий plan short по умолчанию'
|
|
430
|
-
},
|
|
431
|
-
{
|
|
432
|
-
value: 'ask_each_time',
|
|
433
|
-
name: 'Спрашивать размер плана каждый раз'
|
|
434
|
-
}
|
|
435
|
-
];
|
|
436
|
-
const MANUAL_TEST_DEPTH_CHOICES = [
|
|
437
|
-
{
|
|
438
|
-
value: 'full',
|
|
439
|
-
name: 'Полная проверка всех областей: API, база, консоль, интерфейс, алгоритмы'
|
|
440
|
-
},
|
|
441
|
-
{
|
|
442
|
-
value: 'smoke',
|
|
443
|
-
name: 'Только smoke: запуск и основной сценарий изменённой области'
|
|
444
|
-
},
|
|
445
|
-
{
|
|
446
|
-
value: 'ask_each_time',
|
|
447
|
-
name: 'Спрашивать глубину проверки каждый раз'
|
|
448
|
-
}
|
|
214
|
+
{ value: 'normal', name: 'normal', description: 'обычный план до 40 КБ' },
|
|
215
|
+
{ value: 'short', name: 'short', description: 'до 12 КБ и 2 фаз' },
|
|
216
|
+
{ value: 'ask_each_time', name: 'Спрашивать каждый раз' }
|
|
449
217
|
];
|
|
450
218
|
const DECISION_MODE_CHOICES = [
|
|
451
|
-
{
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
},
|
|
455
|
-
{
|
|
456
|
-
value: 'autonomous',
|
|
457
|
-
name: 'Самостоятельно выбирать по коду, правилам и рискам'
|
|
458
|
-
},
|
|
459
|
-
{
|
|
460
|
-
value: 'ask_each_time',
|
|
461
|
-
name: 'Спрашивать по каждой значимой развилке'
|
|
462
|
-
}
|
|
219
|
+
{ value: 'recommend_and_ask', name: 'Рекомендовать и спрашивать', description: 'вопрос только по существенным развилкам' },
|
|
220
|
+
{ value: 'autonomous', name: 'Самостоятельно', description: 'агент выбирает сам и записывает причину' },
|
|
221
|
+
{ value: 'ask_each_time', name: 'Спрашивать по каждой развилке' }
|
|
463
222
|
];
|
|
464
223
|
const TEST_STRATEGY_CHOICES = [
|
|
465
|
-
{
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
},
|
|
469
|
-
{
|
|
470
|
-
value: 'phase_tests_final_checks',
|
|
471
|
-
name: 'Писать тесты в фазах, запускать только их, остальные проверки — в конце'
|
|
472
|
-
},
|
|
473
|
-
{
|
|
474
|
-
value: 'tdd_each_phase',
|
|
475
|
-
name: 'В каждой фазе сначала тесты, затем код и все проверки фазы'
|
|
476
|
-
},
|
|
477
|
-
{
|
|
478
|
-
value: 'end_of_plan',
|
|
479
|
-
name: 'Писать тесты и запускать все проверки в конце плана'
|
|
480
|
-
},
|
|
481
|
-
{
|
|
482
|
-
value: 'ask_each_time',
|
|
483
|
-
name: 'Спрашивать стратегию тестов и проверок каждый раз'
|
|
484
|
-
}
|
|
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: 'Спрашивать каждый раз' }
|
|
485
229
|
];
|
|
486
|
-
const
|
|
487
|
-
{
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
},
|
|
491
|
-
{
|
|
492
|
-
value: 'debug_precise',
|
|
493
|
-
name: 'Подробные debug-логи на важных шагах'
|
|
494
|
-
},
|
|
495
|
-
{
|
|
496
|
-
value: 'ask_each_time',
|
|
497
|
-
name: 'Спрашивать стратегию логирования каждый раз'
|
|
498
|
-
}
|
|
499
|
-
];
|
|
500
|
-
const REVIEW_MODE_CHOICES = [
|
|
501
|
-
{ value: 'always', name: 'always — запускать при каждом ревью' },
|
|
502
|
-
{ value: 'auto', name: 'auto — запускать только когда проверка применима' },
|
|
503
|
-
{ value: 'off', name: 'off — отключить проверку' }
|
|
230
|
+
const PLAN_EXECUTE_MODE_CHOICES = [
|
|
231
|
+
{ value: 'main', name: 'Основной агент', description: 'весь план одним контекстом' },
|
|
232
|
+
{ value: 'auto', name: 'auto', description: 'по плану: небольшой — одним контекстом, остальные — субагентами' },
|
|
233
|
+
{ value: 'subagents', name: 'Субагенты', description: 'каждая фаза — отдельный субагент с независимой проверкой' }
|
|
504
234
|
];
|
|
505
235
|
const REVIEW_EXECUTION_CHOICES = [
|
|
506
|
-
{
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
{
|
|
511
|
-
|
|
512
|
-
name: 'Все review-проверки выполнять основным агентом без субагентов'
|
|
513
|
-
}
|
|
236
|
+
{ value: 'main', name: 'Основной агент', description: 'все проверки в одном контексте' },
|
|
237
|
+
{ value: 'subagents', name: 'Субагенты', description: 'разведчик и отдельный агент на каждую проверку' }
|
|
238
|
+
];
|
|
239
|
+
const POLISH_EXECUTION_CHOICES = [
|
|
240
|
+
{ value: 'subagents', name: 'Субагенты', description: 'каждое ревью и исправление — отдельный субагент' },
|
|
241
|
+
{ value: 'main', name: 'Основной агент', description: 'ревью и исправления в одном контексте' }
|
|
514
242
|
];
|
|
515
|
-
const
|
|
516
|
-
{ value: '
|
|
517
|
-
{ value: '
|
|
518
|
-
{ 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: 'план пишется с человеком, вопросы передаются ему' }
|
|
519
251
|
];
|
|
520
|
-
const
|
|
521
|
-
{ value: '
|
|
522
|
-
{ value: '
|
|
523
|
-
{ value: '
|
|
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 тоже без субагентов' }
|
|
524
256
|
];
|
|
257
|
+
// План и выполнение в оркестраторе включены всегда; спрашиваются только необязательные этапы.
|
|
258
|
+
const ORHESTRA_OPTIONAL_STEPS = ['eda-plan-polish', 'eda-polish', 'eda-manual-test'];
|
|
525
259
|
const UPDATE_ALL_SETTINGS_MODE_CHOICES = [
|
|
526
|
-
{
|
|
527
|
-
|
|
528
|
-
name: 'Настроить один общий профиль и записать его во все проекты'
|
|
529
|
-
},
|
|
530
|
-
{
|
|
531
|
-
value: 'skip',
|
|
532
|
-
name: 'Skip — сохранить полный v3, остальные конфиги перенести с defaults'
|
|
533
|
-
}
|
|
260
|
+
{ value: 'configure', name: 'Настроить общий профиль', description: 'ответить один раз и записать во все проекты' },
|
|
261
|
+
{ value: 'skip', name: 'Skip', description: 'сохранить актуальные конфиги, старые перенести с defaults' }
|
|
534
262
|
];
|
|
535
263
|
|
|
536
264
|
export async function init({ cwd, input = process.stdin, output = process.stdout }) {
|
|
@@ -630,6 +358,12 @@ export async function updateAll({
|
|
|
630
358
|
output.write(`Полный файл настроек сохранён: ${SETTINGS_RELATIVE_PATH}\n`);
|
|
631
359
|
} else {
|
|
632
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
|
+
}
|
|
633
367
|
output.write(migrated.retiredPaths.length > 0
|
|
634
368
|
? `Из файла настроек убраны устаревшие ключи (${migrated.retiredPaths.join(', ')}): ${SETTINGS_RELATIVE_PATH}\n`
|
|
635
369
|
: migrated.normalizedPaths.length > 0
|
|
@@ -685,18 +419,20 @@ export async function askTargets({ input = process.stdin, output = process.stdou
|
|
|
685
419
|
return TARGET_CHOICES.map(choice => choice.value);
|
|
686
420
|
}
|
|
687
421
|
|
|
688
|
-
|
|
689
|
-
message: 'Куда
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
name: `${choice.label} (${choice.dir})`,
|
|
422
|
+
const selected = await checkbox({
|
|
423
|
+
message: 'Куда установить eda?',
|
|
424
|
+
choices: targetPromptChoices().map(choice => ({
|
|
425
|
+
name: choice.label,
|
|
693
426
|
value: choice.value,
|
|
694
|
-
|
|
695
|
-
|
|
427
|
+
description: choice.dir,
|
|
428
|
+
checked: false
|
|
429
|
+
})),
|
|
430
|
+
required: true
|
|
696
431
|
}, {
|
|
697
432
|
input,
|
|
698
433
|
output
|
|
699
434
|
});
|
|
435
|
+
return TARGET_CHOICES.map(choice => choice.value).filter(target => selected.includes(target));
|
|
700
436
|
}
|
|
701
437
|
|
|
702
438
|
export async function askUpdateTargets({
|
|
@@ -707,22 +443,25 @@ export async function askUpdateTargets({
|
|
|
707
443
|
}) {
|
|
708
444
|
if (!input.isTTY || !output.isTTY) return [...installedTargets];
|
|
709
445
|
|
|
446
|
+
// Установленные платформы обновляются всегда: снятая с них отметка ничего не удаляет.
|
|
710
447
|
const selected = await checkboxPrompt({
|
|
711
|
-
message: 'Какие платформы
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
name: `${choice.label} (${choice.dir})${installedTargets.includes(choice.value) ? ' — установлена' : ''}`,
|
|
448
|
+
message: 'Какие платформы обновить? Установленные уже отмечены.',
|
|
449
|
+
choices: targetPromptChoices().map(choice => ({
|
|
450
|
+
name: choice.label,
|
|
715
451
|
value: choice.value,
|
|
452
|
+
description: installedTargets.includes(choice.value) ? 'установлена — обновится в любом случае' : choice.dir,
|
|
716
453
|
checked: installedTargets.includes(choice.value)
|
|
717
|
-
}))
|
|
718
|
-
validate: choices => installedTargets.every(target => choices.some(choice => choice.value === target))
|
|
719
|
-
|| 'Установленные платформы нужно оставить отмеченными: update их не удаляет.'
|
|
454
|
+
}))
|
|
720
455
|
}, { input, output });
|
|
721
456
|
|
|
722
457
|
return TARGET_CHOICES.map(choice => choice.value)
|
|
723
458
|
.filter(target => installedTargets.includes(target) || selected.includes(target));
|
|
724
459
|
}
|
|
725
460
|
|
|
461
|
+
function targetPromptChoices() {
|
|
462
|
+
return TARGET_PROMPT_ORDER.map(value => TARGET_CHOICES.find(choice => choice.value === value));
|
|
463
|
+
}
|
|
464
|
+
|
|
726
465
|
export async function askUpdateAllSettingsMode({
|
|
727
466
|
input = process.stdin,
|
|
728
467
|
output = process.stdout,
|
|
@@ -743,6 +482,8 @@ export async function askUpdateAllSettingsMode({
|
|
|
743
482
|
});
|
|
744
483
|
}
|
|
745
484
|
|
|
485
|
+
// Спрашиваются только основные настройки в порядке работы. Редкие получают defaults и
|
|
486
|
+
// меняются вручную в docs/settings.yaml, как и модели review.agents.
|
|
746
487
|
export async function askSettings({
|
|
747
488
|
input = process.stdin,
|
|
748
489
|
output = process.stdout,
|
|
@@ -759,157 +500,52 @@ export async function askSettings({
|
|
|
759
500
|
}
|
|
760
501
|
|
|
761
502
|
const requestedSections = new Set(sections);
|
|
762
|
-
const shouldAsk = key => missingKeys === null || missingKeys.has(key);
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
if (requestedSections.has('plan') && shouldAsk('plan.review')) {
|
|
781
|
-
settings.plan.review = selected.includes('planReview');
|
|
782
|
-
}
|
|
783
|
-
if (requestedSections.has('send-review') && shouldAsk('sendReview.closePreviousReviews')) {
|
|
784
|
-
settings.sendReview.closePreviousReviews = selected.includes('sendReviewClosePreviousReviews');
|
|
785
|
-
}
|
|
786
|
-
if (requestedSections.has('discover-automations') && shouldAsk('discoverAutomations.includePlans')) {
|
|
787
|
-
settings.discoverAutomations.includePlans = selected.includes('discoverAutomationsIncludePlans');
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
if (requestedSections.has('orhestra') && shouldAsk('orhestra.mode')) {
|
|
791
|
-
settings.orhestra.mode = await selectPrompt({
|
|
792
|
-
message: 'Как eda-orhestra должен вести полный цикл по умолчанию?',
|
|
793
|
-
choices: ORHESTRA_MODE_CHOICES,
|
|
794
|
-
default: settings.orhestra.mode
|
|
795
|
-
}, { 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 });
|
|
796
521
|
}
|
|
797
|
-
|
|
798
|
-
if (requestedSections.has('orhestra') && shouldAsk('orhestra.steps')) {
|
|
522
|
+
if (askSteps) {
|
|
799
523
|
const enabledSteps = await checkboxPrompt({
|
|
800
|
-
message: '
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
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
|
+
}))
|
|
807
532
|
}, { input, output });
|
|
808
|
-
const codePolishLimit = enabledSteps.includes('eda-polish')
|
|
809
|
-
? await selectPrompt({
|
|
810
|
-
message: 'Сколько итераций разрешить eda-polish внутри eda-orhestra?',
|
|
811
|
-
choices: ORHESTRA_POLISH_LIMIT_CHOICES,
|
|
812
|
-
default: 5
|
|
813
|
-
}, { input, output })
|
|
814
|
-
: 5;
|
|
815
533
|
settings.orhestra.steps = DEFAULT_ORHESTRA_STEPS.map(step => ({
|
|
816
534
|
...step,
|
|
817
|
-
enabled: enabledSteps.includes(step.skill)
|
|
818
|
-
...(step.skill === 'eda-polish' ? { args: `limit ${codePolishLimit}` } : {})
|
|
535
|
+
enabled: !ORHESTRA_OPTIONAL_STEPS.includes(step.skill) || enabledSteps.includes(step.skill)
|
|
819
536
|
}));
|
|
820
537
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
['plan', 'plan.decisionMode', settings.plan, 'decisionMode', 'Как принимать важные решения в eda-plan?', DECISION_MODE_CHOICES],
|
|
828
|
-
['plan', 'plan.testStrategy', settings.plan, 'testStrategy', 'Какую стратегию тестов и проверок eda-plan использовать по умолчанию?', TEST_STRATEGY_CHOICES],
|
|
829
|
-
['plan', 'plan.loggingStrategy', settings.plan, 'loggingStrategy', 'Какую стратегию логирования eda-plan использовать по умолчанию?', LOGGING_STRATEGY_CHOICES],
|
|
830
|
-
['plan-review', 'planReview.threshold', settings.planReview, 'threshold', 'Какой порог готовности использовать в eda-plan-review?', PLAN_REVIEW_THRESHOLD_CHOICES],
|
|
831
|
-
['plan-polish', 'planPolish.limit', settings.planPolish, 'limit', 'Сколько итераций «проверка + исправление» разрешить eda-plan-polish?', PLAN_POLISH_LIMIT_CHOICES],
|
|
832
|
-
['plan-execute', 'planExecute.mode', settings.planExecute, 'mode', 'Как eda-plan-execute должен выполнять фазы по умолчанию?', PLAN_EXECUTE_MODE_CHOICES],
|
|
833
|
-
['polish', 'polish.execution', settings.polish, 'execution', 'Как eda-polish должен выполнять review и исправления по умолчанию?', POLISH_EXECUTION_CHOICES],
|
|
834
|
-
['manual-test', 'manualTest.depth', settings.manualTest, 'depth', 'Какую глубину ручной проверки использовать в eda-manual-test по умолчанию?', MANUAL_TEST_DEPTH_CHOICES],
|
|
835
|
-
['review', 'review.execution', settings.review, 'execution', 'Как eda-review должен выполнять проверки по умолчанию?', REVIEW_EXECUTION_CHOICES]
|
|
836
|
-
];
|
|
837
|
-
for (const [section, key, target, property, message, choices] of scalarPrompts) {
|
|
838
|
-
if (!requestedSections.has(section) || !shouldAsk(key)) continue;
|
|
839
|
-
target[property] = await selectPrompt({ message, choices, default: target[property] }, { input, output });
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
if (requestedSections.has('review')) {
|
|
843
|
-
settings.review.agents = await askReviewAgentSettings({
|
|
844
|
-
input,
|
|
845
|
-
output,
|
|
846
|
-
selectPrompt,
|
|
847
|
-
seedAgents: settings.review.agents,
|
|
848
|
-
missingKeys,
|
|
849
|
-
skipModels: settings.review.execution === 'main'
|
|
850
|
-
});
|
|
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 });
|
|
851
544
|
}
|
|
545
|
+
finishPrompts(`Остальные настройки — в ${SETTINGS_RELATIVE_PATH}`, { output });
|
|
852
546
|
return settings;
|
|
853
547
|
}
|
|
854
548
|
|
|
855
|
-
export async function askReviewAgentSettings({
|
|
856
|
-
input = process.stdin,
|
|
857
|
-
output = process.stdout,
|
|
858
|
-
selectPrompt = select,
|
|
859
|
-
seedAgents = DEFAULT_REVIEW_AGENTS,
|
|
860
|
-
missingKeys = null,
|
|
861
|
-
skipModels = false
|
|
862
|
-
} = {}) {
|
|
863
|
-
return askAgentSettings({
|
|
864
|
-
input,
|
|
865
|
-
output,
|
|
866
|
-
selectPrompt,
|
|
867
|
-
definitions: REVIEW_AGENT_DEFINITIONS,
|
|
868
|
-
seedAgents,
|
|
869
|
-
missingKeys,
|
|
870
|
-
keyPrefix: 'review.agents',
|
|
871
|
-
label: 'review',
|
|
872
|
-
skipModels
|
|
873
|
-
});
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
async function askAgentSettings({ input, output, selectPrompt, definitions, seedAgents, missingKeys, keyPrefix, label, skipModels = false }) {
|
|
877
|
-
const configuredAgents = structuredClone(seedAgents);
|
|
878
|
-
const shouldAsk = key => missingKeys === null || missingKeys.has(key);
|
|
879
|
-
for (const agent of definitions) {
|
|
880
|
-
const modeKey = `${keyPrefix}.${agent.key}.mode`;
|
|
881
|
-
const claudeKey = `${keyPrefix}.${agent.key}.model.claude`;
|
|
882
|
-
const codexKey = `${keyPrefix}.${agent.key}.model.codex`;
|
|
883
|
-
let mode = configuredAgents[agent.key].mode;
|
|
884
|
-
if (shouldAsk(modeKey)) {
|
|
885
|
-
mode = await selectPrompt({
|
|
886
|
-
message: `Когда запускать ${label}-проверку ${agent.key}?`,
|
|
887
|
-
choices: REVIEW_MODE_CHOICES,
|
|
888
|
-
default: mode
|
|
889
|
-
}, { input, output });
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
let claude = configuredAgents[agent.key].model.claude;
|
|
893
|
-
let codex = configuredAgents[agent.key].model.codex;
|
|
894
|
-
if (!skipModels && mode !== 'off' && shouldAsk(claudeKey)) {
|
|
895
|
-
claude = await selectPrompt({
|
|
896
|
-
message: `Какой моделью Claude проверять ${agent.key}?`,
|
|
897
|
-
choices: CLAUDE_REVIEW_MODEL_CHOICES,
|
|
898
|
-
default: claude
|
|
899
|
-
}, { input, output });
|
|
900
|
-
}
|
|
901
|
-
if (!skipModels && mode !== 'off' && shouldAsk(codexKey)) {
|
|
902
|
-
codex = await selectPrompt({
|
|
903
|
-
message: `Какой моделью Codex проверять ${agent.key}?`,
|
|
904
|
-
choices: CODEX_REVIEW_MODEL_CHOICES,
|
|
905
|
-
default: codex
|
|
906
|
-
}, { input, output });
|
|
907
|
-
}
|
|
908
|
-
configuredAgents[agent.key] = { mode, model: { claude, codex } };
|
|
909
|
-
}
|
|
910
|
-
return configuredAgents;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
549
|
async function detectTargets(cwd) {
|
|
914
550
|
const targets = [];
|
|
915
551
|
for (const { value } of TARGET_CHOICES) {
|
|
@@ -1141,6 +777,12 @@ async function ensureSettings(cwd, { input = process.stdin, output = process.std
|
|
|
1141
777
|
if (migrated.retiredPaths.length > 0) {
|
|
1142
778
|
output.write(`Убираю устаревшие настройки: ${migrated.retiredPaths.join(', ')}.\n`);
|
|
1143
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
|
+
}
|
|
1144
786
|
if (needsMigration) {
|
|
1145
787
|
output.write(`Переношу ${SETTINGS_RELATIVE_PATH} на version: ${SETTINGS_VERSION}; спрашиваются только отсутствующие значения.\n`);
|
|
1146
788
|
}
|
|
@@ -1179,6 +821,12 @@ async function configureSettings(cwd, { input = process.stdin, output = process.
|
|
|
1179
821
|
if (migrated.retiredPaths.length > 0) {
|
|
1180
822
|
output.write(`Убираю устаревшие настройки: ${migrated.retiredPaths.join(', ')}.\n`);
|
|
1181
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
|
+
}
|
|
1182
830
|
if (needsMigration) {
|
|
1183
831
|
output.write(`Переношу ${SETTINGS_RELATIVE_PATH} на version: ${SETTINGS_VERSION}; спрашиваются только отсутствующие значения.\n`);
|
|
1184
832
|
}
|
|
@@ -1198,7 +846,9 @@ function resolveSettingsUpdate(existingContent) {
|
|
|
1198
846
|
const migrated = migrateSettingsContent(existingContent);
|
|
1199
847
|
const needsMigration = migrated.version !== SETTINGS_VERSION
|
|
1200
848
|
|| migrated.missingKeys.size > 0
|
|
1201
|
-
|| migrated.normalizedPaths.length > 0
|
|
849
|
+
|| migrated.normalizedPaths.length > 0
|
|
850
|
+
|| migrated.legacyReviewAgents
|
|
851
|
+
|| migrated.resetReviewAgents.length > 0;
|
|
1202
852
|
const rendered = formatSettings(migrated.settings);
|
|
1203
853
|
return { migrated, needsMigration, rendered, formatChanged: rendered !== existingContent };
|
|
1204
854
|
}
|
|
@@ -1269,7 +919,6 @@ function migrateSettingsContent(content) {
|
|
|
1269
919
|
|
|
1270
920
|
const plan = objectValue(raw.plan);
|
|
1271
921
|
transfer('plan.strict', plan.strict ?? legacyStrict, bool, value => { settings.plan.strict = value; });
|
|
1272
|
-
transfer('plan.review', plan.review ?? plan.meta_review, bool, value => { settings.plan.review = value; });
|
|
1273
922
|
transfer(
|
|
1274
923
|
'plan.size',
|
|
1275
924
|
plan.size ?? legacyDefaults.plan_size,
|
|
@@ -1332,17 +981,7 @@ function migrateSettingsContent(content) {
|
|
|
1332
981
|
oneOf(['subagents', 'main']),
|
|
1333
982
|
value => { settings.review.execution = value; }
|
|
1334
983
|
);
|
|
1335
|
-
|
|
1336
|
-
rawAgents: objectValue(review.agents),
|
|
1337
|
-
settingsAgents: settings.review.agents,
|
|
1338
|
-
definitions: REVIEW_AGENT_DEFINITIONS,
|
|
1339
|
-
prefix: 'review.agents',
|
|
1340
|
-
missingKeys
|
|
1341
|
-
});
|
|
1342
|
-
if (typeof review.include_code_quality === 'boolean' && !objectValue(review.agents).code_quality) {
|
|
1343
|
-
settings.review.agents.code_quality.mode = review.include_code_quality ? 'always' : 'off';
|
|
1344
|
-
missingKeys.delete('review.agents.code_quality.mode');
|
|
1345
|
-
}
|
|
984
|
+
const { legacyReviewAgents, resetReviewAgents } = transferReviewAgents(objectValue(review.agents), settings.review.agents);
|
|
1346
985
|
|
|
1347
986
|
const sendReview = objectValue(raw['send-review']);
|
|
1348
987
|
transfer(
|
|
@@ -1364,25 +1003,27 @@ function migrateSettingsContent(content) {
|
|
|
1364
1003
|
settings,
|
|
1365
1004
|
missingKeys,
|
|
1366
1005
|
normalizedPaths,
|
|
1006
|
+
legacyReviewAgents,
|
|
1007
|
+
resetReviewAgents,
|
|
1367
1008
|
retiredPaths: findRetiredSettingsPaths(raw),
|
|
1368
1009
|
version: Number.isInteger(raw.version) ? raw.version : null
|
|
1369
1010
|
};
|
|
1370
1011
|
}
|
|
1371
1012
|
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
}
|
|
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 };
|
|
1386
1027
|
}
|
|
1387
1028
|
|
|
1388
1029
|
// У eda-polish больше нет порога: цикл завершается по нулю находок.
|
|
@@ -1483,66 +1124,36 @@ ${onFailureThen}
|
|
|
1483
1124
|
# Строка аргументов передаётся скиллу как часть текущего вызова.
|
|
1484
1125
|
args: ${JSON.stringify(step.args ?? '')}${onFailure}`;
|
|
1485
1126
|
}).join('\n');
|
|
1486
|
-
const
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
# ${agent.auto}
|
|
1490
|
-
${agent.key}:
|
|
1491
|
-
# always — всегда, auto — когда применимо, off — отключено.
|
|
1492
|
-
mode: ${configured.mode}
|
|
1493
|
-
model:
|
|
1494
|
-
# Модель для Claude Code.
|
|
1495
|
-
claude: ${configured.model.claude}
|
|
1496
|
-
# Модель для Codex.
|
|
1497
|
-
codex: ${configured.model.codex}`;
|
|
1498
|
-
}).join('\n\n');
|
|
1499
|
-
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');
|
|
1500
1130
|
|
|
1501
1131
|
return `version: ${SETTINGS_VERSION}
|
|
1502
1132
|
|
|
1503
|
-
orhestra:
|
|
1504
|
-
# Режим полного цикла eda-orhestra.
|
|
1505
|
-
# automatic | manual
|
|
1506
|
-
mode: ${orhestra.mode ?? 'automatic'}
|
|
1507
|
-
# Где выполняются этапы и передаётся ли main совместимым вложенным скилам.
|
|
1508
|
-
# subagents | main | main_propagated
|
|
1509
|
-
execution: ${orhestra.execution ?? 'subagents'}
|
|
1510
|
-
# Упорядоченная цепочка. Шаги можно отключать, удалять и переставлять.
|
|
1511
|
-
steps:
|
|
1512
|
-
${orhestraSteps}
|
|
1513
|
-
|
|
1514
|
-
aim:
|
|
1515
|
-
# Режим ответов на рабочие вопросы eda-aim.
|
|
1516
|
-
# automatic | manual
|
|
1517
|
-
mode: ${aim.mode ?? 'automatic'}
|
|
1518
|
-
|
|
1519
1133
|
explore:
|
|
1520
1134
|
# Включает кросс-CLI ревью в eda-explore.
|
|
1521
|
-
#
|
|
1135
|
+
# false | true
|
|
1522
1136
|
strict: ${settings.explore.strict ? 'true' : 'false'}
|
|
1523
|
-
#
|
|
1524
|
-
#
|
|
1137
|
+
# Как eda-explore ведёт исследовательские развилки.
|
|
1138
|
+
# recommend_and_ask | autonomous | ask_each_time
|
|
1525
1139
|
decision_mode: ${settings.explore.decisionMode}
|
|
1526
1140
|
|
|
1527
1141
|
plan:
|
|
1528
|
-
#
|
|
1529
|
-
# true | false
|
|
1530
|
-
strict: ${settings.plan.strict ? 'true' : 'false'}
|
|
1531
|
-
# Запускает изолированный цикл eda-plan-polish.
|
|
1532
|
-
# true | false
|
|
1533
|
-
review: ${settings.plan.review !== false ? 'true' : 'false'}
|
|
1534
|
-
# Задаёт размер плана.
|
|
1142
|
+
# Размер плана.
|
|
1535
1143
|
# normal | short | ask_each_time
|
|
1536
1144
|
size: ${settings.plan.size}
|
|
1537
|
-
#
|
|
1538
|
-
#
|
|
1145
|
+
# Как eda-plan принимает существенные решения.
|
|
1146
|
+
# recommend_and_ask | autonomous | ask_each_time
|
|
1539
1147
|
decision_mode: ${settings.plan.decisionMode}
|
|
1540
|
-
#
|
|
1541
|
-
#
|
|
1148
|
+
# Когда писать тесты и запускать автоматические проверки.
|
|
1149
|
+
# phase_tests_final_checks | tdd_each_phase | after_each_phase | end_of_plan | ask_each_time
|
|
1542
1150
|
test_strategy: ${settings.plan.testStrategy}
|
|
1543
|
-
#
|
|
1544
|
-
#
|
|
1151
|
+
# Стратегия логирования.
|
|
1152
|
+
# standard | debug_precise | ask_each_time
|
|
1545
1153
|
logging_strategy: ${settings.plan.loggingStrategy}
|
|
1154
|
+
# Включает кросс-CLI ревью в eda-plan.
|
|
1155
|
+
# false | true
|
|
1156
|
+
strict: ${settings.plan.strict ? 'true' : 'false'}
|
|
1546
1157
|
|
|
1547
1158
|
plan-review:
|
|
1548
1159
|
# Доля закрытых пунктов чек-листа, при которой план считается готовым.
|
|
@@ -1556,9 +1167,21 @@ plan-polish:
|
|
|
1556
1167
|
|
|
1557
1168
|
plan-execute:
|
|
1558
1169
|
# Где eda-plan-execute выполняет фазы плана.
|
|
1559
|
-
#
|
|
1170
|
+
# main — весь план одним контекстом, auto — решает по плану, subagents — каждая фаза в отдельном субагенте.
|
|
1560
1171
|
mode: ${settings.planExecute.mode}
|
|
1561
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
|
+
|
|
1562
1185
|
polish:
|
|
1563
1186
|
# Где eda-polish выполняет review и исправления цикла полировки.
|
|
1564
1187
|
# subagents — каждое review и каждое исправление в отдельном изолированном субагенте, main — оба контракта основным агентом.
|
|
@@ -1571,14 +1194,6 @@ manual-test:
|
|
|
1571
1194
|
# full | smoke | ask_each_time
|
|
1572
1195
|
depth: ${settings.manualTest.depth}
|
|
1573
1196
|
|
|
1574
|
-
review:
|
|
1575
|
-
# Где eda-review выполняет выбранные проверки.
|
|
1576
|
-
# subagents — отдельный специализированный агент на проверку, main — все проверки основным агентом без субагентов.
|
|
1577
|
-
execution: ${settings.review.execution ?? 'subagents'}
|
|
1578
|
-
# Каждая проверка имеет собственный режим запуска; модели применяются только в subagents.
|
|
1579
|
-
agents:
|
|
1580
|
-
${reviewAgents}
|
|
1581
|
-
|
|
1582
1197
|
send-review:
|
|
1583
1198
|
# После успешной отправки скрывает предыдущие сводки eda-send-review и резолвит их inline-треды.
|
|
1584
1199
|
# true | false
|
|
@@ -1586,8 +1201,24 @@ send-review:
|
|
|
1586
1201
|
|
|
1587
1202
|
discover-automations:
|
|
1588
1203
|
# Добавляет docs/artifacts/plans/ в обычный запуск eda-discover-automations.
|
|
1589
|
-
#
|
|
1204
|
+
# false | true
|
|
1590
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'}
|
|
1591
1222
|
`;
|
|
1592
1223
|
}
|
|
1593
1224
|
|
|
@@ -1662,7 +1293,7 @@ async function buildSkillFiles(skill, target) {
|
|
|
1662
1293
|
const entrypoint = files.get('SKILL.md');
|
|
1663
1294
|
if (!entrypoint) throw new Error(`У скила ${skill.name} нет SKILL.md.`);
|
|
1664
1295
|
|
|
1665
|
-
const content = entrypoint.toString('utf8');
|
|
1296
|
+
const content = await expandIncludes(`Скил ${skill.name}`, entrypoint.toString('utf8'), PACKAGE_ROOT);
|
|
1666
1297
|
const renderSkill = { claude: renderClaudeSkill, codex: renderCodexSkill, opencode: renderOpenCodeSkill }[target];
|
|
1667
1298
|
const rendered = renderSkill(content, skill.config);
|
|
1668
1299
|
files.set('SKILL.md', Buffer.from(rendered));
|