@gian-tiaga/eda 2.1.0 → 3.1.1
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 +69 -69
- package/README.md +37 -78
- package/agents/eda-aim-planner/prompt.md +1 -1
- package/agents/eda-review-api/prompt.md +2 -1
- package/agents/eda-review-architecture/prompt.md +3 -2
- package/agents/eda-review-business/prompt.md +2 -1
- package/agents/eda-review-code-quality/prompt.md +4 -3
- package/agents/eda-review-correctness/prompt.md +2 -1
- package/agents/eda-review-database/prompt.md +2 -1
- package/agents/eda-review-documentation/prompt.md +2 -1
- package/agents/eda-review-frontend/prompt.md +2 -1
- package/agents/eda-review-performance/prompt.md +4 -3
- package/agents/eda-review-plan-alignment/prompt.md +2 -1
- package/agents/eda-review-previous-reviews/prompt.md +2 -1
- package/agents/eda-review-references/prompt.md +2 -1
- package/agents/eda-review-rules/prompt.md +2 -1
- package/agents/eda-review-security/prompt.md +2 -1
- package/agents/eda-review-tests/prompt.md +2 -1
- package/bin/cli.js +2 -2
- package/lib/install.js +174 -166
- package/package.json +1 -1
- package/skills/eda-aim/SKILL.md +2 -2
- package/skills/eda-discover-automations/SKILL.md +9 -9
- package/skills/eda-explore/SKILL.md +3 -3
- package/skills/eda-fix/SKILL.md +6 -6
- package/skills/eda-fix-by-review/SKILL.md +20 -14
- package/skills/eda-manual-test/SKILL.md +5 -5
- package/skills/eda-new-project/SKILL.md +3 -3
- package/skills/eda-orhestra/SKILL.md +2 -2
- package/skills/eda-plan/SKILL.md +92 -106
- package/skills/eda-plan-execute/SKILL.md +43 -68
- package/skills/eda-plan-polish/SKILL.md +49 -40
- package/skills/eda-plan-review/SKILL.md +89 -136
- package/skills/eda-plan-review-fix/SKILL.md +39 -47
- package/skills/eda-polish/SKILL.md +17 -15
- package/skills/eda-prepare-ai/SKILL.md +1 -1
- package/skills/eda-review/SKILL.md +18 -9
- package/skills/eda-roadmap/SKILL.md +5 -5
- package/skills/eda-send-review/SKILL.md +5 -5
- package/agents/eda-plan-review-api/agent.json +0 -9
- package/agents/eda-plan-review-api/prompt.md +0 -35
- package/agents/eda-plan-review-architecture/agent.json +0 -9
- package/agents/eda-plan-review-architecture/prompt.md +0 -35
- package/agents/eda-plan-review-database/agent.json +0 -9
- package/agents/eda-plan-review-database/prompt.md +0 -35
- package/agents/eda-plan-review-execution/agent.json +0 -9
- package/agents/eda-plan-review-execution/prompt.md +0 -35
- package/agents/eda-plan-review-feasibility/agent.json +0 -9
- package/agents/eda-plan-review-feasibility/prompt.md +0 -35
- package/agents/eda-plan-review-frontend/agent.json +0 -9
- package/agents/eda-plan-review-frontend/prompt.md +0 -35
- package/agents/eda-plan-review-performance/agent.json +0 -9
- package/agents/eda-plan-review-performance/prompt.md +0 -35
- package/agents/eda-plan-review-previous/agent.json +0 -9
- package/agents/eda-plan-review-previous/prompt.md +0 -28
- package/agents/eda-plan-review-requirements/agent.json +0 -9
- package/agents/eda-plan-review-requirements/prompt.md +0 -35
- package/agents/eda-plan-review-rules/agent.json +0 -9
- package/agents/eda-plan-review-rules/prompt.md +0 -35
- package/agents/eda-plan-review-security/agent.json +0 -9
- package/agents/eda-plan-review-security/prompt.md +0 -35
- package/agents/eda-plan-review-verification/agent.json +0 -9
- package/agents/eda-plan-review-verification/prompt.md +0 -35
package/lib/install.js
CHANGED
|
@@ -19,6 +19,22 @@ const SKILLS_SRC = path.join(PACKAGE_ROOT, 'skills');
|
|
|
19
19
|
const AGENTS_SRC = path.join(PACKAGE_ROOT, 'agents');
|
|
20
20
|
const PACKAGE_JSON_PATH = path.join(PACKAGE_ROOT, 'package.json');
|
|
21
21
|
const SETTINGS_RELATIVE_PATH = 'docs/settings.yaml';
|
|
22
|
+
const ARTIFACTS_RELATIVE_PATH = 'docs/artifacts';
|
|
23
|
+
const ARTIFACT_DIRECTORY_NAMES = [
|
|
24
|
+
'aims',
|
|
25
|
+
'automations',
|
|
26
|
+
'executions',
|
|
27
|
+
'fixes',
|
|
28
|
+
'manual-tests',
|
|
29
|
+
'plan-review-fixes',
|
|
30
|
+
'plan-reviews',
|
|
31
|
+
'plans',
|
|
32
|
+
'project-starts',
|
|
33
|
+
'researches',
|
|
34
|
+
'review-fixes',
|
|
35
|
+
'reviews',
|
|
36
|
+
'roadmaps'
|
|
37
|
+
];
|
|
22
38
|
const TARGET_CHOICES = [
|
|
23
39
|
{ value: 'claude', label: 'Claude Code', dir: '.claude/skills/' },
|
|
24
40
|
{ value: 'codex', label: 'Codex CLI', dir: '.codex/skills/' }
|
|
@@ -34,7 +50,25 @@ const RETIRED_SKILLS = [
|
|
|
34
50
|
'eda-start'
|
|
35
51
|
];
|
|
36
52
|
const RETIRED_AGENTS = [
|
|
37
|
-
'eda-commit-context'
|
|
53
|
+
'eda-commit-context',
|
|
54
|
+
'eda-plan-review-requirements',
|
|
55
|
+
'eda-plan-review-rules',
|
|
56
|
+
'eda-plan-review-architecture',
|
|
57
|
+
'eda-plan-review-feasibility',
|
|
58
|
+
'eda-plan-review-execution',
|
|
59
|
+
'eda-plan-review-verification',
|
|
60
|
+
'eda-plan-review-api',
|
|
61
|
+
'eda-plan-review-database',
|
|
62
|
+
'eda-plan-review-security',
|
|
63
|
+
'eda-plan-review-frontend',
|
|
64
|
+
'eda-plan-review-performance',
|
|
65
|
+
'eda-plan-review-previous'
|
|
66
|
+
];
|
|
67
|
+
const RETIRED_SETTINGS_PATHS = [
|
|
68
|
+
'plan-review.agents',
|
|
69
|
+
'review-check',
|
|
70
|
+
'review.strict',
|
|
71
|
+
'review.include_code_quality'
|
|
38
72
|
];
|
|
39
73
|
const MANIFEST_FILE = 'eda-manifest.json';
|
|
40
74
|
const SETTINGS_VERSION = 3;
|
|
@@ -170,114 +204,6 @@ const DEFAULT_REVIEW_AGENTS = Object.fromEntries(REVIEW_AGENT_DEFINITIONS.map(ag
|
|
|
170
204
|
}
|
|
171
205
|
}
|
|
172
206
|
]));
|
|
173
|
-
const PLAN_REVIEW_AGENT_DEFINITIONS = [
|
|
174
|
-
{
|
|
175
|
-
key: 'requirements',
|
|
176
|
-
description: 'Проверяет соответствие плана задаче, scope, критериям готовности и применимым business-требованиям.',
|
|
177
|
-
auto: 'Эта базовая проверка по умолчанию запускается всегда.',
|
|
178
|
-
mode: 'always',
|
|
179
|
-
claude: 'sonnet',
|
|
180
|
-
codex: 'gpt-5.6-terra'
|
|
181
|
-
},
|
|
182
|
-
{
|
|
183
|
-
key: 'rules',
|
|
184
|
-
description: 'Проверяет соблюдение AGENTS.md, CLAUDE.md и docs/rules.md.',
|
|
185
|
-
auto: 'Эта базовая проверка по умолчанию запускается всегда.',
|
|
186
|
-
mode: 'always',
|
|
187
|
-
claude: 'haiku',
|
|
188
|
-
codex: 'gpt-5.6-luna'
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
key: 'architecture',
|
|
192
|
-
description: 'Проверяет архитектурные границы, зависимости и соответствие docs/arch.md.',
|
|
193
|
-
auto: 'Эта базовая проверка по умолчанию запускается всегда.',
|
|
194
|
-
mode: 'always',
|
|
195
|
-
claude: 'opus',
|
|
196
|
-
codex: 'gpt-5.6-sol'
|
|
197
|
-
},
|
|
198
|
-
{
|
|
199
|
-
key: 'feasibility',
|
|
200
|
-
description: 'Проверяет реализуемость плана относительно фактического кода и отсутствие скрытого исследования.',
|
|
201
|
-
auto: 'Эта базовая проверка по умолчанию запускается всегда.',
|
|
202
|
-
mode: 'always',
|
|
203
|
-
claude: 'opus',
|
|
204
|
-
codex: 'gpt-5.6-sol'
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
key: 'execution',
|
|
208
|
-
description: 'Проверяет связность фаз, ID задач, зависимости и размер работы для одного исполнителя.',
|
|
209
|
-
auto: 'Эта базовая проверка по умолчанию запускается всегда.',
|
|
210
|
-
mode: 'always',
|
|
211
|
-
claude: 'sonnet',
|
|
212
|
-
codex: 'gpt-5.6-terra'
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
key: 'verification',
|
|
216
|
-
description: 'Проверяет тесты, команды проверок, ошибки, логирование, документацию и эксплуатационную готовность.',
|
|
217
|
-
auto: 'Эта базовая проверка по умолчанию запускается всегда.',
|
|
218
|
-
mode: 'always',
|
|
219
|
-
claude: 'sonnet',
|
|
220
|
-
codex: 'gpt-5.6-terra'
|
|
221
|
-
},
|
|
222
|
-
{
|
|
223
|
-
key: 'api',
|
|
224
|
-
description: 'Проверяет API, DTO, события, очереди, внешние интеграции и совместимость контрактов.',
|
|
225
|
-
auto: 'В auto запускается при изменении API, событий, очередей или внешних интеграций.',
|
|
226
|
-
mode: 'auto',
|
|
227
|
-
claude: 'sonnet',
|
|
228
|
-
codex: 'gpt-5.6-terra'
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
key: 'database',
|
|
232
|
-
description: 'Проверяет схему данных, миграции, backfill, индексы, транзакции и rollback.',
|
|
233
|
-
auto: 'В auto запускается при изменении хранения, моделей, SQL, схем или миграций.',
|
|
234
|
-
mode: 'auto',
|
|
235
|
-
claude: 'opus',
|
|
236
|
-
codex: 'gpt-5.6-sol'
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
key: 'security',
|
|
240
|
-
description: 'Проверяет auth, permissions, внешний ввод, секреты, персональные данные и опасные операции.',
|
|
241
|
-
auto: 'В auto запускается для auth, permissions, внешнего ввода, секретов и чувствительных данных.',
|
|
242
|
-
mode: 'auto',
|
|
243
|
-
claude: 'opus',
|
|
244
|
-
codex: 'gpt-5.6-sol'
|
|
245
|
-
},
|
|
246
|
-
{
|
|
247
|
-
key: 'frontend',
|
|
248
|
-
description: 'Проверяет пользовательский сценарий, состояния UI, ошибки, accessibility и адаптивность.',
|
|
249
|
-
auto: 'В auto запускается для компонентов, стилей, клиентских маршрутов и состояния UI.',
|
|
250
|
-
mode: 'auto',
|
|
251
|
-
claude: 'sonnet',
|
|
252
|
-
codex: 'gpt-5.6-terra'
|
|
253
|
-
},
|
|
254
|
-
{
|
|
255
|
-
key: 'performance',
|
|
256
|
-
description: 'Проверяет нагрузку, запросы, конкурентность, кеши и горячие пути.',
|
|
257
|
-
auto: 'В auto запускается для запросов, пакетной обработки, кешей, конкурентности и горячих путей.',
|
|
258
|
-
mode: 'auto',
|
|
259
|
-
claude: 'opus',
|
|
260
|
-
codex: 'gpt-5.6-sol'
|
|
261
|
-
},
|
|
262
|
-
{
|
|
263
|
-
key: 'previous',
|
|
264
|
-
description: 'Проверяет закрытие находок предыдущей связанной итерации plan-review.',
|
|
265
|
-
auto: 'В auto запускается только при наличии связанной цепочки review/fix для текущего SHA-256 плана.',
|
|
266
|
-
mode: 'auto',
|
|
267
|
-
claude: 'haiku',
|
|
268
|
-
codex: 'gpt-5.6-luna'
|
|
269
|
-
}
|
|
270
|
-
];
|
|
271
|
-
const DEFAULT_PLAN_REVIEW_AGENTS = Object.fromEntries(PLAN_REVIEW_AGENT_DEFINITIONS.map(agent => [
|
|
272
|
-
agent.key,
|
|
273
|
-
{
|
|
274
|
-
mode: agent.mode,
|
|
275
|
-
model: {
|
|
276
|
-
claude: agent.claude,
|
|
277
|
-
codex: agent.codex
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
]));
|
|
281
207
|
const ORHESTRA_STEP_DEFINITIONS = [
|
|
282
208
|
{
|
|
283
209
|
id: 'plan',
|
|
@@ -343,11 +269,10 @@ const DEFAULT_SETTINGS = {
|
|
|
343
269
|
loggingStrategy: 'ask_each_time'
|
|
344
270
|
},
|
|
345
271
|
planReview: {
|
|
346
|
-
threshold:
|
|
347
|
-
agents: DEFAULT_PLAN_REVIEW_AGENTS
|
|
272
|
+
threshold: 100
|
|
348
273
|
},
|
|
349
274
|
planPolish: {
|
|
350
|
-
limit:
|
|
275
|
+
limit: 2
|
|
351
276
|
},
|
|
352
277
|
review: {
|
|
353
278
|
agents: DEFAULT_REVIEW_AGENTS
|
|
@@ -431,11 +356,11 @@ const ORHESTRA_POLISH_LIMIT_CHOICES = [1, 2, 3, 5, 10].map(value => ({
|
|
|
431
356
|
value,
|
|
432
357
|
name: `${value} ${value === 1 ? 'итерация' : value < 5 ? 'итерации' : 'итераций'}`
|
|
433
358
|
}));
|
|
434
|
-
const PLAN_REVIEW_THRESHOLD_CHOICES = [
|
|
359
|
+
const PLAN_REVIEW_THRESHOLD_CHOICES = [80, 90, 100].map(value => ({
|
|
435
360
|
value,
|
|
436
361
|
name: `${value}/100`
|
|
437
362
|
}));
|
|
438
|
-
const PLAN_POLISH_LIMIT_CHOICES = [1, 2, 3
|
|
363
|
+
const PLAN_POLISH_LIMIT_CHOICES = [1, 2, 3].map(value => ({
|
|
439
364
|
value,
|
|
440
365
|
name: `${value} ${value === 1 ? 'итерация' : value < 5 ? 'итерации' : 'итераций'}`
|
|
441
366
|
}));
|
|
@@ -542,6 +467,7 @@ export async function update({ cwd, input = process.stdin, output = process.stdo
|
|
|
542
467
|
return;
|
|
543
468
|
}
|
|
544
469
|
output.write(`Найдены установленные среды: ${targets.join(', ')}\n`);
|
|
470
|
+
await migrateArtifactDirectories(cwd, output);
|
|
545
471
|
await configureSettings(cwd, { input, output });
|
|
546
472
|
await syncPackage(cwd, targets, output, { action: 'update' });
|
|
547
473
|
}
|
|
@@ -611,15 +537,18 @@ export async function updateAll({
|
|
|
611
537
|
}
|
|
612
538
|
|
|
613
539
|
output.write(`Найдены установленные среды: ${targets.join(', ')}\n`);
|
|
540
|
+
await migrateArtifactDirectories(projectDir, output);
|
|
614
541
|
const projectSettingsPath = path.join(projectDir, SETTINGS_RELATIVE_PATH);
|
|
615
542
|
if (resolvedSettingsMode === 'skip' && await fileExists(projectSettingsPath)) {
|
|
616
543
|
const existingContent = await fs.readFile(projectSettingsPath, 'utf8');
|
|
617
544
|
const migrated = migrateSettingsContent(existingContent);
|
|
618
|
-
if (migrated.version === SETTINGS_VERSION && migrated.missingKeys.size === 0) {
|
|
545
|
+
if (migrated.version === SETTINGS_VERSION && migrated.missingKeys.size === 0 && migrated.retiredPaths.length === 0) {
|
|
619
546
|
output.write(`Полный файл настроек сохранён: ${SETTINGS_RELATIVE_PATH}\n`);
|
|
620
547
|
} else {
|
|
621
548
|
await writeSettingsContent(projectDir, formatSettings(migrated.settings));
|
|
622
|
-
output.write(
|
|
549
|
+
output.write(migrated.retiredPaths.length > 0
|
|
550
|
+
? `Из файла настроек убраны устаревшие ключи (${migrated.retiredPaths.join(', ')}): ${SETTINGS_RELATIVE_PATH}\n`
|
|
551
|
+
: `Старый или неполный файл настроек перенесён на version: ${SETTINGS_VERSION}: ${SETTINGS_RELATIVE_PATH}\n`);
|
|
623
552
|
}
|
|
624
553
|
} else {
|
|
625
554
|
await writeSettingsContent(projectDir, sharedSettingsContent);
|
|
@@ -793,15 +722,6 @@ export async function askSettings({
|
|
|
793
722
|
target[property] = await selectPrompt({ message, choices, default: target[property] }, { input, output });
|
|
794
723
|
}
|
|
795
724
|
|
|
796
|
-
if (requestedSections.has('plan-review')) {
|
|
797
|
-
settings.planReview.agents = await askPlanReviewAgentSettings({
|
|
798
|
-
input,
|
|
799
|
-
output,
|
|
800
|
-
selectPrompt,
|
|
801
|
-
seedAgents: settings.planReview.agents,
|
|
802
|
-
missingKeys
|
|
803
|
-
});
|
|
804
|
-
}
|
|
805
725
|
if (requestedSections.has('review')) {
|
|
806
726
|
settings.review.agents = await askReviewAgentSettings({
|
|
807
727
|
input,
|
|
@@ -833,25 +753,6 @@ export async function askReviewAgentSettings({
|
|
|
833
753
|
});
|
|
834
754
|
}
|
|
835
755
|
|
|
836
|
-
export async function askPlanReviewAgentSettings({
|
|
837
|
-
input = process.stdin,
|
|
838
|
-
output = process.stdout,
|
|
839
|
-
selectPrompt = select,
|
|
840
|
-
seedAgents = DEFAULT_PLAN_REVIEW_AGENTS,
|
|
841
|
-
missingKeys = null
|
|
842
|
-
} = {}) {
|
|
843
|
-
return askAgentSettings({
|
|
844
|
-
input,
|
|
845
|
-
output,
|
|
846
|
-
selectPrompt,
|
|
847
|
-
definitions: PLAN_REVIEW_AGENT_DEFINITIONS,
|
|
848
|
-
seedAgents,
|
|
849
|
-
missingKeys,
|
|
850
|
-
keyPrefix: 'planReview.agents',
|
|
851
|
-
label: 'plan-review'
|
|
852
|
-
});
|
|
853
|
-
}
|
|
854
|
-
|
|
855
756
|
async function askAgentSettings({ input, output, selectPrompt, definitions, seedAgents, missingKeys, keyPrefix, label }) {
|
|
856
757
|
const configuredAgents = structuredClone(seedAgents);
|
|
857
758
|
const shouldAsk = key => missingKeys === null || missingKeys.has(key);
|
|
@@ -1010,6 +911,92 @@ async function findInstalledProjects(rootDir, maxDepth) {
|
|
|
1010
911
|
return projects;
|
|
1011
912
|
}
|
|
1012
913
|
|
|
914
|
+
async function migrateArtifactDirectories(cwd, output = process.stdout) {
|
|
915
|
+
const docsDir = path.join(cwd, 'docs');
|
|
916
|
+
const artifactsDir = path.join(cwd, ARTIFACTS_RELATIVE_PATH);
|
|
917
|
+
const legacyDirectories = [];
|
|
918
|
+
|
|
919
|
+
for (const directoryName of ARTIFACT_DIRECTORY_NAMES) {
|
|
920
|
+
const legacyPath = path.join(docsDir, directoryName);
|
|
921
|
+
const legacyStat = await lstatIfExists(legacyPath);
|
|
922
|
+
if (!legacyStat) continue;
|
|
923
|
+
if (!legacyStat.isDirectory() || legacyStat.isSymbolicLink()) {
|
|
924
|
+
throw new Error(`Ожидался каталог артефактов: ${legacyPath}`);
|
|
925
|
+
}
|
|
926
|
+
legacyDirectories.push({ directoryName, legacyPath });
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
if (legacyDirectories.length === 0) {
|
|
930
|
+
return { migratedDirectories: 0, movedFiles: 0, discardedFiles: 0 };
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
const artifactsStat = await lstatIfExists(artifactsDir);
|
|
934
|
+
if (artifactsStat && (!artifactsStat.isDirectory() || artifactsStat.isSymbolicLink())) {
|
|
935
|
+
throw new Error(`Ожидался каталог артефактов: ${artifactsDir}`);
|
|
936
|
+
}
|
|
937
|
+
await fs.mkdir(artifactsDir, { recursive: true });
|
|
938
|
+
|
|
939
|
+
const result = {
|
|
940
|
+
migratedDirectories: legacyDirectories.length,
|
|
941
|
+
movedFiles: 0,
|
|
942
|
+
discardedFiles: 0
|
|
943
|
+
};
|
|
944
|
+
for (const { directoryName, legacyPath } of legacyDirectories) {
|
|
945
|
+
await mergeArtifactEntry(legacyPath, path.join(artifactsDir, directoryName), result);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
output.write(
|
|
949
|
+
`Артефакты перенесены в ${ARTIFACTS_RELATIVE_PATH}/: каталогов ${result.migratedDirectories}, `
|
|
950
|
+
+ `файлов перемещено ${result.movedFiles}, конфликтующих старых файлов удалено ${result.discardedFiles}.\n`
|
|
951
|
+
);
|
|
952
|
+
return result;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
async function mergeArtifactEntry(sourcePath, destinationPath, result) {
|
|
956
|
+
const sourceStat = await fs.lstat(sourcePath);
|
|
957
|
+
const destinationStat = await lstatIfExists(destinationPath);
|
|
958
|
+
|
|
959
|
+
if (!destinationStat) {
|
|
960
|
+
result.movedFiles += await countArtifactFiles(sourcePath, sourceStat);
|
|
961
|
+
await fs.mkdir(path.dirname(destinationPath), { recursive: true });
|
|
962
|
+
await fs.rename(sourcePath, destinationPath);
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
const mergeDirectories = sourceStat.isDirectory()
|
|
967
|
+
&& !sourceStat.isSymbolicLink()
|
|
968
|
+
&& destinationStat.isDirectory()
|
|
969
|
+
&& !destinationStat.isSymbolicLink();
|
|
970
|
+
if (!mergeDirectories) {
|
|
971
|
+
result.discardedFiles += await countArtifactFiles(sourcePath, sourceStat);
|
|
972
|
+
await fs.rm(sourcePath, { recursive: true, force: true });
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
const entries = await fs.readdir(sourcePath, { withFileTypes: true });
|
|
977
|
+
entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
978
|
+
for (const entry of entries) {
|
|
979
|
+
await mergeArtifactEntry(
|
|
980
|
+
path.join(sourcePath, entry.name),
|
|
981
|
+
path.join(destinationPath, entry.name),
|
|
982
|
+
result
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
await fs.rmdir(sourcePath);
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
async function countArtifactFiles(entryPath, entryStat = null) {
|
|
989
|
+
const stat = entryStat ?? await fs.lstat(entryPath);
|
|
990
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) return 1;
|
|
991
|
+
|
|
992
|
+
let count = 0;
|
|
993
|
+
const entries = await fs.readdir(entryPath, { withFileTypes: true });
|
|
994
|
+
for (const entry of entries) {
|
|
995
|
+
count += await countArtifactFiles(path.join(entryPath, entry.name));
|
|
996
|
+
}
|
|
997
|
+
return count;
|
|
998
|
+
}
|
|
999
|
+
|
|
1013
1000
|
async function ensureSettings(cwd, { input = process.stdin, output = process.stdout } = {}) {
|
|
1014
1001
|
const settingsPath = path.join(cwd, SETTINGS_RELATIVE_PATH);
|
|
1015
1002
|
const existingContent = await readFileIfExists(settingsPath);
|
|
@@ -1021,12 +1008,18 @@ async function ensureSettings(cwd, { input = process.stdin, output = process.std
|
|
|
1021
1008
|
}
|
|
1022
1009
|
|
|
1023
1010
|
const migrated = migrateSettingsContent(existingContent);
|
|
1024
|
-
|
|
1011
|
+
const needsMigration = migrated.version !== SETTINGS_VERSION || migrated.missingKeys.size > 0;
|
|
1012
|
+
if (!needsMigration && migrated.retiredPaths.length === 0) {
|
|
1025
1013
|
output.write(`Настройки уже есть: ${SETTINGS_RELATIVE_PATH}\n`);
|
|
1026
1014
|
return;
|
|
1027
1015
|
}
|
|
1028
1016
|
|
|
1029
|
-
|
|
1017
|
+
if (migrated.retiredPaths.length > 0) {
|
|
1018
|
+
output.write(`Убираю устаревшие настройки: ${migrated.retiredPaths.join(', ')}.\n`);
|
|
1019
|
+
}
|
|
1020
|
+
if (needsMigration) {
|
|
1021
|
+
output.write(`Переношу ${SETTINGS_RELATIVE_PATH} на version: ${SETTINGS_VERSION}; спрашиваются только отсутствующие значения.\n`);
|
|
1022
|
+
}
|
|
1030
1023
|
const settings = await askSettings({
|
|
1031
1024
|
input,
|
|
1032
1025
|
output,
|
|
@@ -1049,12 +1042,18 @@ async function configureSettings(cwd, { input = process.stdin, output = process.
|
|
|
1049
1042
|
}
|
|
1050
1043
|
|
|
1051
1044
|
const migrated = migrateSettingsContent(existingContent);
|
|
1052
|
-
|
|
1045
|
+
const needsMigration = migrated.version !== SETTINGS_VERSION || migrated.missingKeys.size > 0;
|
|
1046
|
+
if (!needsMigration && migrated.retiredPaths.length === 0) {
|
|
1053
1047
|
output.write(`Настройки ${SETTINGS_RELATIVE_PATH} версии ${SETTINGS_VERSION} полные — вопросы не требуются.\n`);
|
|
1054
1048
|
return;
|
|
1055
1049
|
}
|
|
1056
1050
|
|
|
1057
|
-
|
|
1051
|
+
if (migrated.retiredPaths.length > 0) {
|
|
1052
|
+
output.write(`Убираю устаревшие настройки: ${migrated.retiredPaths.join(', ')}.\n`);
|
|
1053
|
+
}
|
|
1054
|
+
if (needsMigration) {
|
|
1055
|
+
output.write(`Переношу ${SETTINGS_RELATIVE_PATH} на version: ${SETTINGS_VERSION}; спрашиваются только отсутствующие значения.\n`);
|
|
1056
|
+
}
|
|
1058
1057
|
const settings = await askSettings({
|
|
1059
1058
|
input,
|
|
1060
1059
|
output,
|
|
@@ -1144,13 +1143,6 @@ function migrateSettingsContent(content) {
|
|
|
1144
1143
|
|
|
1145
1144
|
const planReview = objectValue(raw['plan-review']);
|
|
1146
1145
|
transfer('planReview.threshold', planReview.threshold, score, value => { settings.planReview.threshold = value; });
|
|
1147
|
-
transferAgentSettings({
|
|
1148
|
-
rawAgents: objectValue(planReview.agents),
|
|
1149
|
-
settingsAgents: settings.planReview.agents,
|
|
1150
|
-
definitions: PLAN_REVIEW_AGENT_DEFINITIONS,
|
|
1151
|
-
prefix: 'planReview.agents',
|
|
1152
|
-
missingKeys
|
|
1153
|
-
});
|
|
1154
1146
|
|
|
1155
1147
|
const planPolish = objectValue(raw['plan-polish']);
|
|
1156
1148
|
transfer('planPolish.limit', planPolish.limit, positiveInt, value => { settings.planPolish.limit = value; });
|
|
@@ -1187,6 +1179,7 @@ function migrateSettingsContent(content) {
|
|
|
1187
1179
|
return {
|
|
1188
1180
|
settings,
|
|
1189
1181
|
missingKeys,
|
|
1182
|
+
retiredPaths: findRetiredSettingsPaths(raw),
|
|
1190
1183
|
version: Number.isInteger(raw.version) ? raw.version : null
|
|
1191
1184
|
};
|
|
1192
1185
|
}
|
|
@@ -1207,6 +1200,12 @@ function transferAgentSettings({ rawAgents, settingsAgents, definitions, prefix,
|
|
|
1207
1200
|
}
|
|
1208
1201
|
}
|
|
1209
1202
|
|
|
1203
|
+
// У eda-polish больше нет порога: цикл завершается по нулю находок.
|
|
1204
|
+
function stripRetiredStepArgs(skill, args) {
|
|
1205
|
+
if (skill !== 'eda-polish') return args;
|
|
1206
|
+
return args.replace(/\bthreshold\s+\d+\s*/gi, '').replace(/\s+/g, ' ').trim();
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1210
1209
|
function normalizeStoredOrhestraSteps(value) {
|
|
1211
1210
|
if (!Array.isArray(value) || value.length === 0) return null;
|
|
1212
1211
|
const steps = [];
|
|
@@ -1220,7 +1219,7 @@ function normalizeStoredOrhestraSteps(value) {
|
|
|
1220
1219
|
id: step.id,
|
|
1221
1220
|
skill: step.skill,
|
|
1222
1221
|
enabled: step.enabled !== false,
|
|
1223
|
-
args: step.args ?? ''
|
|
1222
|
+
args: stripRetiredStepArgs(step.skill, step.args ?? '')
|
|
1224
1223
|
};
|
|
1225
1224
|
const onFailure = objectValue(step.on_failure);
|
|
1226
1225
|
if (Object.keys(onFailure).length > 0) {
|
|
@@ -1244,6 +1243,23 @@ function objectValue(value) {
|
|
|
1244
1243
|
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
1245
1244
|
}
|
|
1246
1245
|
|
|
1246
|
+
function findRetiredSettingsPaths(raw) {
|
|
1247
|
+
const found = [];
|
|
1248
|
+
for (const settingPath of RETIRED_SETTINGS_PATHS) {
|
|
1249
|
+
let current = raw;
|
|
1250
|
+
let present = true;
|
|
1251
|
+
for (const segment of settingPath.split('.')) {
|
|
1252
|
+
if (!current || typeof current !== 'object' || Array.isArray(current) || !(segment in current)) {
|
|
1253
|
+
present = false;
|
|
1254
|
+
break;
|
|
1255
|
+
}
|
|
1256
|
+
current = current[segment];
|
|
1257
|
+
}
|
|
1258
|
+
if (present && current !== undefined) found.push(settingPath);
|
|
1259
|
+
}
|
|
1260
|
+
return found;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1247
1263
|
async function writeFileAtomically(filePath, content) {
|
|
1248
1264
|
const currentStat = await fs.stat(filePath);
|
|
1249
1265
|
const tempPath = path.join(
|
|
@@ -1295,11 +1311,6 @@ ${onFailureThen}
|
|
|
1295
1311
|
# Модель для Codex.
|
|
1296
1312
|
codex: ${configured.model.codex}`;
|
|
1297
1313
|
}).join('\n\n');
|
|
1298
|
-
const planReviewAgents = formatAgents(
|
|
1299
|
-
PLAN_REVIEW_AGENT_DEFINITIONS,
|
|
1300
|
-
settings.planReview.agents,
|
|
1301
|
-
DEFAULT_PLAN_REVIEW_AGENTS
|
|
1302
|
-
);
|
|
1303
1314
|
const reviewAgents = formatAgents(REVIEW_AGENT_DEFINITIONS, settings.review.agents, DEFAULT_REVIEW_AGENTS);
|
|
1304
1315
|
|
|
1305
1316
|
return `version: ${SETTINGS_VERSION}
|
|
@@ -1346,12 +1357,9 @@ plan:
|
|
|
1346
1357
|
logging_strategy: ${settings.plan.loggingStrategy}
|
|
1347
1358
|
|
|
1348
1359
|
plan-review:
|
|
1349
|
-
#
|
|
1360
|
+
# Доля закрытых пунктов чек-листа, при которой план считается готовым.
|
|
1350
1361
|
# 1..100
|
|
1351
1362
|
threshold: ${settings.planReview.threshold}
|
|
1352
|
-
# Каждая проверка имеет собственный режим запуска и модели для обеих сред.
|
|
1353
|
-
agents:
|
|
1354
|
-
${planReviewAgents}
|
|
1355
1363
|
|
|
1356
1364
|
plan-polish:
|
|
1357
1365
|
# Максимальное число подтверждающих review-итераций.
|
|
@@ -1369,7 +1377,7 @@ send-review:
|
|
|
1369
1377
|
close_previous_reviews: ${settings.sendReview.closePreviousReviews ? 'true' : 'false'}
|
|
1370
1378
|
|
|
1371
1379
|
discover-automations:
|
|
1372
|
-
# Добавляет docs/plans/ в обычный запуск eda-discover-automations.
|
|
1380
|
+
# Добавляет docs/artifacts/plans/ в обычный запуск eda-discover-automations.
|
|
1373
1381
|
# true | false
|
|
1374
1382
|
include_plans: ${settings.discoverAutomations.includePlans ? 'true' : 'false'}
|
|
1375
1383
|
`;
|
package/package.json
CHANGED
package/skills/eda-aim/SKILL.md
CHANGED
|
@@ -28,7 +28,7 @@ aim:
|
|
|
28
28
|
|
|
29
29
|
В автоматическом режиме основной агент сам выбирает ответ на рабочие вопросы по рекомендации субагента и фактам проекта. Это не отменяет одно обязательное подтверждение человеком точного списка целей до создания нового файла.
|
|
30
30
|
|
|
31
|
-
Если пользователь передал существующий файл из `docs/aims/`, продолжай его с текущего статуса. Не создавай второй файл для той же цели.
|
|
31
|
+
Если пользователь передал существующий файл из `docs/artifacts/aims/`, продолжай его с текущего статуса. Не создавай второй файл для той же цели.
|
|
32
32
|
|
|
33
33
|
## Пакетные агенты
|
|
34
34
|
|
|
@@ -59,7 +59,7 @@ aim:
|
|
|
59
59
|
|
|
60
60
|
## Файл цели
|
|
61
61
|
|
|
62
|
-
После подтверждения `eda-aim-planner` создаёт `docs/aims/{YYYY-MM-DD}_{HH-MM}_{slug}.md`. Файл должен оставаться коротким и содержать только цель и фактический журнал:
|
|
62
|
+
После подтверждения `eda-aim-planner` создаёт `docs/artifacts/aims/{YYYY-MM-DD}_{HH-MM}_{slug}.md`. Файл должен оставаться коротким и содержать только цель и фактический журнал:
|
|
63
63
|
|
|
64
64
|
```markdown
|
|
65
65
|
# <Короткое название>
|
|
@@ -19,7 +19,7 @@ description: 'Аудирует автоматические проверки п
|
|
|
19
19
|
|---|---|---|
|
|
20
20
|
| `discover-automations.include_plans` | `true`, `false` | `plans`, «с планами»; выключение: `normal`, «без планов» |
|
|
21
21
|
|
|
22
|
-
Обычный режим анализирует текущий проект и, если они есть, `docs/reviews/`, `docs/review-fixes/`, `docs/fixes/`. Режим `discover-automations.include_plans: true` добавляет `docs/plans/`. Отсутствие истории не отменяет полный аудит текущих проверок.
|
|
22
|
+
Обычный режим анализирует текущий проект и, если они есть, `docs/artifacts/reviews/`, `docs/artifacts/review-fixes/`, `docs/artifacts/fixes/`. Режим `discover-automations.include_plans: true` добавляет `docs/artifacts/plans/`. Отсутствие истории не отменяет полный аудит текущих проверок.
|
|
23
23
|
|
|
24
24
|
## Вход из сообщения пользователя
|
|
25
25
|
|
|
@@ -49,11 +49,11 @@ description: 'Аудирует автоматические проверки п
|
|
|
49
49
|
|
|
50
50
|
Если вход неоднозначен — `AskUserQuestion`: все ревью и фиксы / все ревью, фиксы и планы / последние N / период / конкретные файлы.
|
|
51
51
|
|
|
52
|
-
Получи список файлов из `docs/reviews/`, `docs/review-fixes/` и `docs/fixes/`. В режиме `plans` дополнительно получи список файлов из `docs/plans/`. Отсутствующие или пустые папки отметь в отчёте, но не блокируйся: проведи baseline gap-аудит по коду, manifest-файлам, конфигам, scripts и CI текущего проекта.
|
|
52
|
+
Получи список файлов из `docs/artifacts/reviews/`, `docs/artifacts/review-fixes/` и `docs/artifacts/fixes/`. В режиме `plans` дополнительно получи список файлов из `docs/artifacts/plans/`. Отсутствующие или пустые папки отметь в отчёте, но не блокируйся: проведи baseline gap-аудит по коду, manifest-файлам, конфигам, scripts и CI текущего проекта.
|
|
53
53
|
|
|
54
54
|
### 2. Прочитать контекст
|
|
55
55
|
- Все выбранные ревью, отчёты о фиксах по ревью и обычные фиксы.
|
|
56
|
-
- В режиме `plans`: все выбранные планы из `docs/plans/`.
|
|
56
|
+
- В режиме `plans`: все выбранные планы из `docs/artifacts/plans/`.
|
|
57
57
|
- `docs/rules.md`, `docs/arch.md` (если есть) — прочитай и строго следуй им.
|
|
58
58
|
- Сначала определи затронутые историей и текущим аудитом доменные области. Затем через `docs/business.md` выбери только карточки этих областей; если подходящих нет, продолжай без business-контекста. Используй карточки лишь для поиска автоматизируемых инвариантов и acceptance-сценариев, не расширяй аудит на несвязанные темы.
|
|
59
59
|
- Какие языки и фреймворки в проекте — определи сам.
|
|
@@ -96,19 +96,19 @@ description: 'Аудирует автоматические проверки п
|
|
|
96
96
|
4. `agent` — MCP-сервер, новая/обновлённая eda-инструкция, специализированная агентная проверка или обязательный источник контекста. Предлагай только когда это закрывает значимую ошибку AI-процесса или даёт агенту данные, которые нельзя надёжно получить из кода.
|
|
97
97
|
5. `rules` / `architecture` — текстовые правила и архитектурные уточнения, если проблема связана с договорённостью, границей модулей или ownership.
|
|
98
98
|
|
|
99
|
-
Предпочитай уже установленный инструмент, если он ловит этот класс проблем. Новый инструмент предлагай только если он не дублирует инструмент того же класса. Для проблем из планов предлагай проверку `docs/plans/*.md`, а не runtime-линтер кода. Если правило или архитектура уже зафиксированы, не дублируй текст; предложи только уточнение, которое закрывает найденный случай.
|
|
99
|
+
Предпочитай уже установленный инструмент, если он ловит этот класс проблем. Новый инструмент предлагай только если он не дублирует инструмент того же класса. Для проблем из планов предлагай проверку `docs/artifacts/plans/*.md`, а не runtime-линтер кода. Если правило или архитектура уже зафиксированы, не дублируй текст; предложи только уточнение, которое закрывает найденный случай.
|
|
100
100
|
|
|
101
101
|
Подменять линтер, статанализатор или тест MCP-сервером/скилом нельзя, если ошибку можно надёжно поймать программной проверкой.
|
|
102
102
|
|
|
103
103
|
Для каждой программной проверки укажи точный инструмент, команду/сценарий и контур: `pre-commit`, `pre-push`, `PR CI`, `nightly`, `release`, `post-deploy`. Быстрые проверки отдели от тяжёлых. Для MCP укажи сервер/класс инструмента, проверяемый сценарий, минимальные права, тестовые данные и контур `MCP/manual`; MCP не называй автоматической CI-проверкой, если он реально не запускается без участия агента.
|
|
104
104
|
|
|
105
105
|
### 5. Сохранить отчёт
|
|
106
|
-
`docs/automations/{YYYY-MM-DD}_{HH-MM}_{slug}.md`. Slug — короткое описание набора (например, `php-strict-rules`).
|
|
106
|
+
`docs/artifacts/automations/{YYYY-MM-DD}_{HH-MM}_{slug}.md`. Slug — короткое описание набора (например, `php-strict-rules`).
|
|
107
107
|
|
|
108
108
|
```markdown
|
|
109
109
|
---
|
|
110
110
|
date: <YYYY-MM-DD HH:MM>
|
|
111
|
-
sources: [docs/reviews/..., docs/review-fixes/..., docs/fixes/..., docs/business/...]
|
|
111
|
+
sources: [docs/artifacts/reviews/..., docs/artifacts/review-fixes/..., docs/artifacts/fixes/..., docs/business/...]
|
|
112
112
|
mode: <normal | plans>
|
|
113
113
|
status: draft
|
|
114
114
|
---
|
|
@@ -132,7 +132,7 @@ status: draft
|
|
|
132
132
|
### 1. <название паттерна>
|
|
133
133
|
- **Тип:** automation / tests / tooling
|
|
134
134
|
- **Что ловит:** ...
|
|
135
|
-
- **Встречалось в:** `docs/reviews/...` (×1), `docs/reviews/...` (×2)
|
|
135
|
+
- **Встречалось в:** `docs/artifacts/reviews/...` (×1), `docs/artifacts/reviews/...` (×2)
|
|
136
136
|
- **Почему автоматизировать:** <повторяемость либо высокий ущерб и вероятность повторения>
|
|
137
137
|
- **Инструмент:** <название или текущий test runner> (если новый для проекта — пометка «требует установки»)
|
|
138
138
|
- **Команда / сценарий:** <точная команда, glob, правило или тестовый сценарий>
|
|
@@ -150,7 +150,7 @@ status: draft
|
|
|
150
150
|
### 1. <название паттерна>
|
|
151
151
|
- **Тип:** agent
|
|
152
152
|
- **Что закрывает:** <какую ошибку процесса или нехватку контекста>
|
|
153
|
-
- **Встречалось в:** `docs/reviews/...` (×1), `docs/fixes/...` (×1)
|
|
153
|
+
- **Встречалось в:** `docs/artifacts/reviews/...` (×1), `docs/artifacts/fixes/...` (×1)
|
|
154
154
|
- **Почему автоматизировать:** <повторяемость либо высокий ущерб и вероятность повторения>
|
|
155
155
|
- **Предложение:** MCP-сервер / новый eda-скил / правка существующего скила / специализированная агентная проверка
|
|
156
156
|
- **Сценарий и доступ:** <что проверяет, минимальные права, тестовые данные, почему CLI/CI недостаточно>
|
|
@@ -166,7 +166,7 @@ status: draft
|
|
|
166
166
|
- **Тип:** rules / architecture
|
|
167
167
|
- **Куда:** `docs/rules.md` / `docs/arch.md`, раздел <название или «новый раздел»>
|
|
168
168
|
- **Почему:** <какой паттерн закрывает>
|
|
169
|
-
- **Встречалось в:** `docs/fixes/...` (×1), `docs/reviews/...` (×1)
|
|
169
|
+
- **Встречалось в:** `docs/artifacts/fixes/...` (×1), `docs/artifacts/reviews/...` (×1)
|
|
170
170
|
- **Черновик текста:**
|
|
171
171
|
```markdown
|
|
172
172
|
<готовый фрагмент для docs/rules.md или docs/arch.md>
|
|
@@ -38,7 +38,7 @@ description: 'Исследует техническую тему или зада
|
|
|
38
38
|
|
|
39
39
|
## Главные правила
|
|
40
40
|
|
|
41
|
-
1. **Никаких правок кода.** Запись только в `docs/researches/`.
|
|
41
|
+
1. **Никаких правок кода.** Запись только в `docs/artifacts/researches/`.
|
|
42
42
|
2. **Итог — конкретика.** Нельзя заканчивать абстрактными описаниями, списком возможностей или «можно выбрать один из вариантов».
|
|
43
43
|
3. **Развилки закрывай по `decision_mode`.** Значимые развилки подтверждай до финализации, мелкие локальные решения выбирай сам и не создавай шум.
|
|
44
44
|
4. **Риски вплетай в исследование.** Не создавай отдельный обязательный раздел или этап рисков. Если риск влияет на выбор, источник, ограничение или будущую проверку, фиксируй его рядом с соответствующим решением: что может пойти не так и как это учитывается — снимаем, принимаем или выносим за рамки.
|
|
@@ -87,7 +87,7 @@ description: 'Исследует техническую тему или зада
|
|
|
87
87
|
|
|
88
88
|
### 4. Собрать отчёт
|
|
89
89
|
|
|
90
|
-
Сохрани отчёт в `docs/researches/{YYYY-MM-DD}_{HH-MM}_{slug}.md`.
|
|
90
|
+
Сохрани отчёт в `docs/artifacts/researches/{YYYY-MM-DD}_{HH-MM}_{slug}.md`.
|
|
91
91
|
|
|
92
92
|
Формат отчёта должен быть компактным и универсальным: четыре основных раздела, без обязательного раздувания под каждый тип исследования. Таблицы, ASCII-диаграммы и дополнительные подпункты добавляй внутри этих разделов только когда они действительно помогают ясности.
|
|
93
93
|
|
|
@@ -176,4 +176,4 @@ CLI нет — `AskUserQuestion`: продолжать без ревью или
|
|
|
176
176
|
- Перечислять риски без выбранного закрытия.
|
|
177
177
|
- Планировать реализацию по шагам — это работа `eda-plan`.
|
|
178
178
|
- Указывать `latest` без конкретной проверенной версии и источника.
|
|
179
|
-
- Сохранять отчёт куда-либо кроме `docs/researches/`.
|
|
179
|
+
- Сохранять отчёт куда-либо кроме `docs/artifacts/researches/`.
|