@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.
Files changed (63) hide show
  1. package/MIGRATION.md +69 -69
  2. package/README.md +37 -78
  3. package/agents/eda-aim-planner/prompt.md +1 -1
  4. package/agents/eda-review-api/prompt.md +2 -1
  5. package/agents/eda-review-architecture/prompt.md +3 -2
  6. package/agents/eda-review-business/prompt.md +2 -1
  7. package/agents/eda-review-code-quality/prompt.md +4 -3
  8. package/agents/eda-review-correctness/prompt.md +2 -1
  9. package/agents/eda-review-database/prompt.md +2 -1
  10. package/agents/eda-review-documentation/prompt.md +2 -1
  11. package/agents/eda-review-frontend/prompt.md +2 -1
  12. package/agents/eda-review-performance/prompt.md +4 -3
  13. package/agents/eda-review-plan-alignment/prompt.md +2 -1
  14. package/agents/eda-review-previous-reviews/prompt.md +2 -1
  15. package/agents/eda-review-references/prompt.md +2 -1
  16. package/agents/eda-review-rules/prompt.md +2 -1
  17. package/agents/eda-review-security/prompt.md +2 -1
  18. package/agents/eda-review-tests/prompt.md +2 -1
  19. package/bin/cli.js +2 -2
  20. package/lib/install.js +174 -166
  21. package/package.json +1 -1
  22. package/skills/eda-aim/SKILL.md +2 -2
  23. package/skills/eda-discover-automations/SKILL.md +9 -9
  24. package/skills/eda-explore/SKILL.md +3 -3
  25. package/skills/eda-fix/SKILL.md +6 -6
  26. package/skills/eda-fix-by-review/SKILL.md +20 -14
  27. package/skills/eda-manual-test/SKILL.md +5 -5
  28. package/skills/eda-new-project/SKILL.md +3 -3
  29. package/skills/eda-orhestra/SKILL.md +2 -2
  30. package/skills/eda-plan/SKILL.md +92 -106
  31. package/skills/eda-plan-execute/SKILL.md +43 -68
  32. package/skills/eda-plan-polish/SKILL.md +49 -40
  33. package/skills/eda-plan-review/SKILL.md +89 -136
  34. package/skills/eda-plan-review-fix/SKILL.md +39 -47
  35. package/skills/eda-polish/SKILL.md +17 -15
  36. package/skills/eda-prepare-ai/SKILL.md +1 -1
  37. package/skills/eda-review/SKILL.md +18 -9
  38. package/skills/eda-roadmap/SKILL.md +5 -5
  39. package/skills/eda-send-review/SKILL.md +5 -5
  40. package/agents/eda-plan-review-api/agent.json +0 -9
  41. package/agents/eda-plan-review-api/prompt.md +0 -35
  42. package/agents/eda-plan-review-architecture/agent.json +0 -9
  43. package/agents/eda-plan-review-architecture/prompt.md +0 -35
  44. package/agents/eda-plan-review-database/agent.json +0 -9
  45. package/agents/eda-plan-review-database/prompt.md +0 -35
  46. package/agents/eda-plan-review-execution/agent.json +0 -9
  47. package/agents/eda-plan-review-execution/prompt.md +0 -35
  48. package/agents/eda-plan-review-feasibility/agent.json +0 -9
  49. package/agents/eda-plan-review-feasibility/prompt.md +0 -35
  50. package/agents/eda-plan-review-frontend/agent.json +0 -9
  51. package/agents/eda-plan-review-frontend/prompt.md +0 -35
  52. package/agents/eda-plan-review-performance/agent.json +0 -9
  53. package/agents/eda-plan-review-performance/prompt.md +0 -35
  54. package/agents/eda-plan-review-previous/agent.json +0 -9
  55. package/agents/eda-plan-review-previous/prompt.md +0 -28
  56. package/agents/eda-plan-review-requirements/agent.json +0 -9
  57. package/agents/eda-plan-review-requirements/prompt.md +0 -35
  58. package/agents/eda-plan-review-rules/agent.json +0 -9
  59. package/agents/eda-plan-review-rules/prompt.md +0 -35
  60. package/agents/eda-plan-review-security/agent.json +0 -9
  61. package/agents/eda-plan-review-security/prompt.md +0 -35
  62. package/agents/eda-plan-review-verification/agent.json +0 -9
  63. 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: 95,
347
- agents: DEFAULT_PLAN_REVIEW_AGENTS
272
+ threshold: 100
348
273
  },
349
274
  planPolish: {
350
- limit: 3
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 = [90, 95, 100].map(value => ({
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, 5, 10].map(value => ({
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(`Старый или неполный файл настроек перенесён на version: ${SETTINGS_VERSION}: ${SETTINGS_RELATIVE_PATH}\n`);
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
- if (migrated.version === SETTINGS_VERSION && migrated.missingKeys.size === 0) {
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
- output.write(`Переношу ${SETTINGS_RELATIVE_PATH} на version: ${SETTINGS_VERSION}; спрашиваются только отсутствующие значения.\n`);
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
- if (migrated.version === SETTINGS_VERSION && migrated.missingKeys.size === 0) {
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
- output.write(`Переношу ${SETTINGS_RELATIVE_PATH} на version: ${SETTINGS_VERSION}; спрашиваются только отсутствующие значения.\n`);
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
- # План готов только без открытых находок и при score не ниже этого порога.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gian-tiaga/eda",
3
- "version": "2.1.0",
3
+ "version": "3.1.1",
4
4
  "description": "Пакет скилов и специализированных агентов eda-* для Claude Code и Codex CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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/`.