@gian-tiaga/eda 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  > Поточная разработка с AI-агентами — на русском языке.
4
4
  > Набор готовых скилов для **Claude Code** и **Codex CLI**, которые ведут тебя через весь цикл: от исследования до коммита.
5
5
 
6
- `eda` — это тринадцать скилов, каждый отвечает за свой кусок работы. Вместо того чтобы каждый раз объяснять модели, как делать ревью или как исполнять план, ты говоришь одно слово — и она следует чёткой инструкции на русском, со всеми правилами, проверками и артефактами в нужных папках.
6
+ `eda` — это пятнадцать скилов, каждый отвечает за свой кусок работы. Вместо того чтобы каждый раз объяснять модели, как делать ревью или как исполнять план, ты говоришь одно слово — и она следует чёткой инструкции на русском, со всеми правилами, проверками и артефактами в нужных папках.
7
7
 
8
8
  ---
9
9
 
@@ -21,6 +21,14 @@ eda init
21
21
 
22
22
  Установщик покажет чекбоксы: стрелками выбираешь среду, пробелом отмечаешь, Enter продолжает. Можно поставить Claude Code, Codex CLI или обе среды сразу. Если `docs/settings.yaml` ещё нет, установщик также предложит выбрать настройки скилов и создаст этот файл. В конце `eda init` пишет, какие скилы реально установлены или изменились.
23
23
 
24
+ Если скилы уже стоят во многих проектах, можно обновить их одной командой:
25
+
26
+ ```bash
27
+ eda update-all ~/Code
28
+ ```
29
+
30
+ `eda update-all` ищет проекты в указанной директории, её прямых подпапках и подпапках второго уровня. Проект считается найденным, если в нём уже есть `.claude/skills` или `.codex/skills`. Команда обновляет только найденные среды и не создаёт `docs/settings.yaml`, чтобы массовый запуск не добавлял новые файлы в проекты.
31
+
24
32
  ---
25
33
 
26
34
  ## 🛠️ Скилы
@@ -32,14 +40,16 @@ eda init
32
40
  | `eda-plan` | Пишет план реализации через стандартный Plan Mode хост-агента. `docs/rules.md` и `docs/arch.md` использует как обязательную рамку, а не справочный контекст: план должен строго следовать правилам и архитектуре. В плане явно фиксирует контракты БД/API, если задача их затрагивает. Затем три параллельных субагента/модели (слабая, средняя, мощная) проверяют план на реалистичность, пропущенные шаги и нарушения правил — главный планировщик сам решает, что применить | `docs/plans/` |
33
41
  | `eda-execute` | Выполняет план. Спрашивает, где работать (текущая ветка, новая ветка, отдельный worktree). Перед правками читает план, `docs/rules.md` и `docs/arch.md`; правила и архитектура обязательны к исполнению. Если шаг плана им противоречит, останавливается и спрашивает. Тесты пишет внутри каждого шага. В конце — полный прогон тестов и линтеров | `docs/executions/` |
34
42
  | `eda-fix` | Делает обычные фиксы по короткому контексту, баг-репорту или файлу плана. Перед правками читает правила и архитектуру, добавляет нужные тесты, прогоняет проверки и сохраняет историю правок | `docs/fixes/` |
35
- | `eda-review` | Делает код-ревью с оценкой 0–100 и сверкой с планом работ, но в файл пишет только проблемы: ошибки, недоделки, неточности, нарушения правил/архитектуры, риски и недостающие тесты. Не перечисляет выполненную работу. Затем параллельные специализированные агенты проверяют выполнение плана, архитектуру, правила и при включённой настройке качество кода. В строгом режиме — ещё и кросс-ревью между Claude и Codex | `docs/reviews/` |
43
+ | `eda-review` | Делает код-ревью с оценкой 0–100 и сверкой с планом работ, но в файл пишет только проблемы: ошибки, недоделки, неточности, нарушения правил/архитектуры, риски и недостающие тесты. Не перечисляет выполненную работу. В обычном режиме после черновика передаёт файл в `eda-review-check`; с флагом `draft` сохраняет только первичное ревью без субагентов | `docs/reviews/` |
44
+ | `eda-review-check` | Проверяет готовое ревью из `docs/reviews/` специализированными субагентами: выполнение плана, архитектуру, правила и при включённой настройке качество кода. В строгом режиме добавляет кросс-ревью между Claude и Codex. Сам применяет подтверждённые замечания к файлу ревью, но не правит код | `docs/reviews/` |
36
45
  | `eda-fix-by-review` | Применяет замечания из ревью. Обязательные — сразу. Спорные — спрашивает. В конце ссылается на отчёт прямо в файле ревью | `docs/review-fixes/` |
46
+ | `eda-polish` | Оркестрирует доводку изменений: запускает `eda-review draft`, затем `eda-review-check`, затем `eda-fix-by-review apply-optional` и повторяет цикл до оценки 95/100 или лимита 5 итераций. Каждый шаг идёт в изолированном субагенте или отдельном CLI-процессе. Не коммитит | `docs/reviews/`, `docs/review-fixes/` |
37
47
  | `eda-send-review` | Отправляет ревью на GitHub PR через `gh` — как комментарий, review, approve или request-changes. Сам определяет PR по текущей ветке | (комментарий в PR) |
38
48
  | `eda-commit` | Делает один аккуратный коммит в стиле проекта. В конце спрашивает: push, merge в main + удалить ветку, или ничего | (git) |
39
49
  | `eda-worktree` | Создаёт git worktree рядом с основным проектом в папке `{name}-work-{n}` и одноимённую ветку. Базу берёт из аргумента или спрашивает | соседняя папка `{name}-work-{n}` |
40
50
  | `eda-merge-worktree` | Мержит ветку из соседнего worktree в текущую ветку. Принимает номер `1`, короткое имя `work-1` или полное имя `{name}-work-1`. Worktree и ветку после merge не удаляет | (git) |
41
51
  | `eda-docs` | Создаёт или обновляет `docs/rules.md` (максимально строгие правила для твоего стека), `docs/arch.md` (архитектура проекта) и шапку `AGENTS.md`. Может предлагать инструменты, которых ещё нет в проекте | `docs/rules.md`, `docs/arch.md`, `AGENTS.md` |
42
- | `eda-automate` | Анализирует историю ревью и фиксов. Если какое-то замечание встречается раз за разом предлагает кастомное правило линтера или статанализатора, чтобы ловить это автоматически | `docs/automations/` |
52
+ | `eda-automate` | Анализирует историю ревью и фиксов. Если ошибка повторяется предлагает автоматизации на уровне языка и тулчейна: линтеры, статанализаторы, typecheck, тесты, pre-commit/CI-проверки. Если кодовая проверка не подходит, может предложить MCP-сервер, агентную проверку, новый скил или уточнение правил/архитектуры | `docs/automations/` |
43
53
 
44
54
  ---
45
55
 
@@ -53,7 +63,10 @@ eda init
53
63
  |---|---|---|---|
54
64
  | `eda-explore` | `strict` | После сохранения отчёта отдаёт его соседнему CLI (Claude → Codex или наоборот) на критическое ревью конкретности, фактов, развилок, рисков и версий; затем доправляет отчёт по замечаниям | Серьёзные исследования, которые лягут в основу больших решений; когда хочется второго мнения от другой модели/среды |
55
65
  | `eda-plan` | `strict` | После обычного мета-ревью трёх субагентов/моделей отдаёт план соседнему CLI на дополнительный круг проверки; доправляет план | Большие или ответственные планы, особенно затрагивающие смежные системы |
56
- | `eda-review` | `strict` | После обычного мета-ревью специализированными субагентами отдаёт ревью соседнему CLI на дополнительный круг проверки; доправляет ревью по его замечаниям | Ревью важных PR; когда нужен максимальный охват |
66
+ | `eda-review` | `draft` | Сохраняет первичное ревью со статусом `draft` и не запускает `eda-review-check`, субагентов и кросс-CLI | Когда ревью нужно как черновик для внешней оркестрации, например `eda-polish` |
67
+ | `eda-review` | `strict` | После draft-ревью запускает `eda-review-check strict`: специализированные субагенты плюс соседний CLI; доправляет ревью по подтверждённым замечаниям | Ревью важных PR; когда нужен максимальный охват |
68
+ | `eda-review-check` | `strict` | Проверяет уже готовое ревью специализированными субагентами и затем отдаёт его соседнему CLI на дополнительный круг проверки | Когда черновик ревью уже есть и нужно усилить только проверку ревью |
69
+ | `eda-fix-by-review` | `apply-optional` | Применяет не только обязательные, но и пункты «на усмотрение автора» без дополнительного вопроса | Для заранее согласованной автоматической доводки, прежде всего внутри `eda-polish` |
57
70
 
58
71
  ---
59
72
 
@@ -67,7 +80,7 @@ eda init
67
80
  version: 1
68
81
 
69
82
  defaults:
70
- # Включает strict-режим по умолчанию для eda-explore, eda-plan и eda-review.
83
+ # Включает strict-режим по умолчанию для eda-explore, eda-plan, eda-review и eda-review-check.
71
84
  # true | false
72
85
  strict: false
73
86
  # Задаёт размер плана по умолчанию для eda-plan.
@@ -89,21 +102,21 @@ automate:
89
102
  include_plans: false
90
103
 
91
104
  review:
92
- # Добавляет в eda-review проверку качества кода и meta-reviewer quality-check.
105
+ # Добавляет в eda-review / eda-review-check проверку качества кода и meta-reviewer quality-check.
93
106
  # true | false
94
107
  include_code_quality: true
95
108
  ```
96
109
 
97
110
  Что означают настройки:
98
- - `defaults.strict` — включает strict-режим по умолчанию для `eda-explore`, `eda-plan` и `eda-review`.
111
+ - `defaults.strict` — включает strict-режим по умолчанию для `eda-explore`, `eda-plan`, `eda-review` и `eda-review-check`.
99
112
  - `defaults.plan_size` — размер плана для `eda-plan`: `normal`, `short` или `ask_each_time`.
100
113
  - `defaults.decision_mode` — как `eda-explore` ведёт исследовательские развилки, а `eda-plan` принимает существенные решения: `autonomous` выбирает сам, `recommend_and_ask` рекомендует и спрашивает по значимым развилкам, `ask_each_time` спрашивает по каждой развилке, которая влияет на ход работы или итоговый выбор.
101
114
  - `defaults.test_strategy` — стратегия тестов для `eda-plan`: `after_each_phase`, `tdd_each_phase`, `end_of_plan` или `ask_each_time`.
102
115
  - `defaults.logging_strategy` — стратегия логирования для `eda-plan`: `debug_precise`, `standard` или `ask_each_time`.
103
116
  - `automate.include_plans` — добавляет `docs/plans/` в обычный запуск `eda-automate`.
104
- - `review.include_code_quality` — добавляет в `eda-review` проверку качества кода и отдельного мета-ревьюера `quality-check`.
117
+ - `review.include_code_quality` — добавляет в `eda-review` / `eda-review-check` проверку качества кода и отдельного мета-ревьюера `quality-check`.
105
118
 
106
- `eda init` и `eda update` создают `docs/settings.yaml`, только если файла ещё нет. Существующий файл не перезаписывается.
119
+ `eda init` и `eda update` создают `docs/settings.yaml`, только если файла ещё нет. Существующий файл не перезаписывается. `eda update-all` настройки не создаёт и не меняет.
107
120
 
108
121
  ---
109
122
 
@@ -113,11 +126,13 @@ review:
113
126
  docs ───────────────┬───────────────┐
114
127
  │ │
115
128
  v v
116
- roadmap ────────> explore ─────> plan ───> execute ───┬──> review ───> fix-by-review ───┬──> send-review
129
+ roadmap ────────> explore ─────> plan ───> execute ───┬──> review ───> review-check ───> fix-by-review ───┬──> send-review
117
130
  │ │ │
118
131
  └──────────────> plan v └──> commit
119
132
  fix ─────────────> review
120
133
 
134
+ polish = review draft ─> review-check ─> fix-by-review apply-optional ─> повтор до нужной оценки
135
+
121
136
  automate может запускаться от review, fix-by-review или fix.
122
137
  ```
123
138
 
@@ -129,7 +144,7 @@ automate может запускаться от review, fix-by-review или fix
129
144
 
130
145
  - **Простой язык.** Скилы общаются с тобой так, чтобы понял человек без глубоких знаний предмета. Термины — только когда без них нельзя.
131
146
  - **Все вопросы — интерактивно.** В Claude Code — через `AskUserQuestion`; в интерактивном Codex — через `request_user_input` или блокирующий вопрос в чат. В `codex exec` и других неинтерактивных запусках скил не имитирует диалог: если без ответа нельзя безопасно продолжать, он завершает работу со статусом `blocked: нужен ответ пользователя`.
132
- - **Мета-проверки — через субагентов.** В интерактивном Codex обычные проверки планов и ревью запускаются через `spawn_agent` или аналогичный инструмент субагентов. Отдельный `codex exec` остаётся fallback для неинтерактивного режима и механизмом strict-кросс-проверки соседним CLI.
147
+ - **Мета-проверки — через субагентов.** В интерактивном Codex обычные проверки планов и ревью запускаются через `spawn_agent` или аналогичный инструмент субагентов. Проверки готового ревью собраны в `eda-review-check`. Отдельный `codex exec` остаётся fallback для неинтерактивного режима и механизмом strict-кросс-проверки соседним CLI.
133
148
  - **Артефакты по папкам.** Исследования отдельно, планы отдельно, ревью отдельно. Между файлами — ссылки. Через месяц легко найти, кто что когда решал.
134
149
  - **Границы между скилами жёсткие.** `execute` не коммитит — это работа `commit`. `review` не правит код — это `fix-by-review`. Никаких размытых ответственностей.
135
150
  - **Доверие модели.** Скилы короткие. Они говорят «что» и «почему», а «как именно» — модель разбирается сама.
@@ -141,6 +156,7 @@ automate может запускаться от review, fix-by-review или fix
141
156
  ```bash
142
157
  eda init # выбрать Claude Code / Codex / обе среды и установить скилы
143
158
  eda update # обновить скилы, показать реально изменившиеся и создать docs/settings.yaml, если его ещё нет
159
+ eda update-all [dir] # обновить скилы во всех найденных проектах внутри dir на глубине 2
144
160
  eda --version # показать версию установленного пакета
145
161
  eda --help # справка
146
162
  ```
@@ -152,6 +168,13 @@ npm update -g @gian-tiaga/eda # подтянуть новый код
152
168
  eda update # синхронизировать скилы в текущем проекте
153
169
  ```
154
170
 
171
+ Для папки с несколькими проектами:
172
+
173
+ ```bash
174
+ npm update -g @gian-tiaga/eda
175
+ eda update-all ~/Code
176
+ ```
177
+
155
178
  ---
156
179
 
157
180
  ## 📁 Куда устанавливаются скилы
@@ -163,7 +186,7 @@ eda update # синхронизировать скилы в текущ
163
186
  | **Claude Code** | `<project>/.claude/skills/<skill>/SKILL.md` — отдельная папка на каждый скил |
164
187
  | **Codex CLI** | `<project>/.codex/skills/<skill>/SKILL.md` — отдельная папка на каждый скил |
165
188
 
166
- `eda update` идемпотентно перезаписывает файлы в обеих папках и в конце пишет только те скилы, содержимое которых реально изменилось относительно установленной копии. При обновлении старые файлы Codex-формата `<project>/.codex/skills/<skill>.md`, созданные версиями `eda` до этой структуры, удаляются. Если ты хочешь подправить скил локально — лучше копию сделай рядом, а не прямо в `.claude/skills/` или `.codex/skills/`, иначе при следующем `update` твои правки потеряются.
189
+ `eda update` идемпотентно перезаписывает файлы в обеих папках и в конце пишет только те скилы, содержимое которых реально изменилось относительно установленной копии. `eda update-all [dir]` делает то же самое для каждого найденного проекта внутри `dir`, но не создаёт `docs/settings.yaml`. При обновлении старые файлы Codex-формата `<project>/.codex/skills/<skill>.md`, созданные версиями `eda` до этой структуры, удаляются. Если ты хочешь подправить скил локально — лучше копию сделай рядом, а не прямо в `.claude/skills/` или `.codex/skills/`, иначе при следующем `update` твои правки потеряются.
167
190
 
168
191
  `AGENTS.md` (как и любые другие файлы в корне проекта) установщик **не трогает**. Если хочешь, чтобы Codex автоматически подгружал скилы, дай ему знать сам — например, одной строкой в `AGENTS.md`: «следуй инструкциям из `.codex/skills/`».
169
192
 
package/bin/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
- import { init, update } from '../lib/install.js';
3
+ import { init, update, updateAll } from '../lib/install.js';
4
4
 
5
5
  const require = createRequire(import.meta.url);
6
6
  const { version } = require('../package.json');
@@ -11,6 +11,7 @@ const HELP = `eda — установка и обновление скилов ed
11
11
  Использование:
12
12
  eda init — выбрать целевые среды (Claude / Codex / обе) и установить скилы
13
13
  eda update — обновить уже установленные скилы в текущем проекте
14
+ eda update-all [dir] — обновить уже установленные скилы во всех проектах внутри dir
14
15
  eda --version — показать версию
15
16
  eda --help — показать эту справку
16
17
  `;
@@ -23,6 +24,9 @@ try {
23
24
  case 'update':
24
25
  await update({ cwd: process.cwd() });
25
26
  break;
27
+ case 'update-all':
28
+ await updateAll({ root: process.argv[3] ?? process.cwd() });
29
+ break;
26
30
  case '-v':
27
31
  case '--version':
28
32
  process.stdout.write(`${version}\n`);
package/lib/install.js CHANGED
@@ -13,6 +13,8 @@ const TARGET_CHOICES = [
13
13
  { value: 'claude', label: 'Claude Code', dir: '.claude/skills/' },
14
14
  { value: 'codex', label: 'Codex CLI', dir: '.codex/skills/' }
15
15
  ];
16
+ const UPDATE_ALL_MAX_DEPTH = 2;
17
+ const UPDATE_ALL_SKIP_DIRS = new Set(['.git', '.claude', '.codex', 'node_modules']);
16
18
  const RETIRED_SKILLS = ['eda-research'];
17
19
  const DEFAULT_SETTINGS = {
18
20
  strict: false,
@@ -26,7 +28,7 @@ const DEFAULT_SETTINGS = {
26
28
  const SETTINGS_CHOICES = [
27
29
  {
28
30
  value: 'strict',
29
- name: 'Strict по умолчанию для explore / plan / review',
31
+ name: 'Strict по умолчанию для explore / plan / review / review-check',
30
32
  checked: DEFAULT_SETTINGS.strict
31
33
  },
32
34
  {
@@ -36,7 +38,7 @@ const SETTINGS_CHOICES = [
36
38
  },
37
39
  {
38
40
  value: 'includeCodeQuality',
39
- name: 'Проверять качество кода в eda-review',
41
+ name: 'Проверять качество кода в eda-review / eda-review-check',
40
42
  checked: DEFAULT_SETTINGS.includeCodeQuality
41
43
  }
42
44
  ];
@@ -122,6 +124,88 @@ export async function update({ cwd, input = process.stdin, output = process.stdo
122
124
  await syncSkills(cwd, targets, output, { action: 'update' });
123
125
  }
124
126
 
127
+ export async function updateAll({
128
+ root = process.cwd(),
129
+ output = process.stdout,
130
+ maxDepth = UPDATE_ALL_MAX_DEPTH
131
+ } = {}) {
132
+ if (!Number.isInteger(maxDepth) || maxDepth < 0) {
133
+ throw new Error('Глубина поиска должна быть неотрицательным целым числом.');
134
+ }
135
+
136
+ const rootDir = path.resolve(root);
137
+ const rootStat = await statIfExists(rootDir);
138
+ if (!rootStat?.isDirectory()) {
139
+ throw new Error(`Директория не найдена: ${rootDir}`);
140
+ }
141
+
142
+ output.write(`Ищу проекты с установленными скилами в ${rootDir} (глубина ${maxDepth}).\n`);
143
+
144
+ const projects = await findInstalledProjects(rootDir, maxDepth);
145
+ if (projects.length === 0) {
146
+ output.write('Не нашёл проектов с установленными скилами.\n');
147
+ return {
148
+ root: rootDir,
149
+ maxDepth,
150
+ projects,
151
+ updatedProjects: [],
152
+ skippedProjects: [],
153
+ failedProjects: []
154
+ };
155
+ }
156
+
157
+ output.write(`Найдено ${formatProjectCount(projects.length)}: ${projects.map(project => formatProjectPath(rootDir, project)).join(', ')}\n`);
158
+
159
+ const updatedProjects = [];
160
+ const skippedProjects = [];
161
+ const failedProjects = [];
162
+
163
+ for (const projectDir of projects) {
164
+ const projectLabel = formatProjectPath(rootDir, projectDir);
165
+ output.write(`\n=== ${projectLabel} ===\n`);
166
+
167
+ try {
168
+ const targets = await detectTargets(projectDir);
169
+ if (targets.length === 0) {
170
+ output.write('Пропускаю: установленные среды исчезли во время обхода.\n');
171
+ skippedProjects.push(projectDir);
172
+ continue;
173
+ }
174
+
175
+ output.write(`Найдены установленные среды: ${targets.join(', ')}\n`);
176
+ const result = await syncSkills(projectDir, targets, output, {
177
+ action: 'update',
178
+ writeDone: false
179
+ });
180
+ updatedProjects.push({
181
+ path: projectDir,
182
+ targets,
183
+ changedSkills: result.changedSkills
184
+ });
185
+ } catch (err) {
186
+ failedProjects.push({ path: projectDir, error: err });
187
+ output.write(`Ошибка: ${err.message}\n`);
188
+ }
189
+ }
190
+
191
+ output.write(`\nСводка: обновлено ${formatProjectCount(updatedProjects.length)}, пропущено ${formatProjectCount(skippedProjects.length)}, ошибки: ${formatErrorCount(failedProjects.length)}.\n`);
192
+ if (failedProjects.length > 0) {
193
+ output.write('Ошибки по проектам:\n');
194
+ for (const failedProject of failedProjects) {
195
+ output.write(` - ${formatProjectPath(rootDir, failedProject.path)}: ${failedProject.error.message}\n`);
196
+ }
197
+ }
198
+
199
+ return {
200
+ root: rootDir,
201
+ maxDepth,
202
+ projects,
203
+ updatedProjects,
204
+ skippedProjects,
205
+ failedProjects
206
+ };
207
+ }
208
+
125
209
  export async function askTargets({ input = process.stdin, output = process.stdout } = {}) {
126
210
  if (!input.isTTY || !output.isTTY) {
127
211
  output.write('Нет интерактивного терминала — устанавливаю Claude Code и Codex CLI.\n');
@@ -211,7 +295,7 @@ async function detectTargets(cwd) {
211
295
  return targets;
212
296
  }
213
297
 
214
- async function syncSkills(cwd, targets, output = process.stdout, { action = 'update' } = {}) {
298
+ async function syncSkills(cwd, targets, output = process.stdout, { action = 'update', writeDone = true } = {}) {
215
299
  const skills = await listSkills();
216
300
  if (skills.length === 0) {
217
301
  throw new Error(`В пакете нет скилов (искал в ${SKILLS_SRC}).`);
@@ -234,7 +318,37 @@ async function syncSkills(cwd, targets, output = process.stdout, { action = 'upd
234
318
  .map(skill => skill.name)
235
319
  .filter(skillName => changedSkills.has(skillName));
236
320
  output.write(formatChangedSkills(actionLabel, changedSkillNames));
237
- output.write('\nГотово.\n');
321
+ if (writeDone) output.write('\nГотово.\n');
322
+ return { changedSkills: changedSkillNames };
323
+ }
324
+
325
+ async function findInstalledProjects(rootDir, maxDepth) {
326
+ const projects = [];
327
+
328
+ async function walk(dir, depth) {
329
+ const targets = await detectTargets(dir);
330
+ if (targets.length > 0) projects.push(dir);
331
+ if (depth >= maxDepth) return;
332
+
333
+ let entries;
334
+ try {
335
+ entries = await fs.readdir(dir, { withFileTypes: true });
336
+ } catch (err) {
337
+ if (err?.code === 'EACCES' || err?.code === 'EPERM') return;
338
+ throw err;
339
+ }
340
+
341
+ const childDirs = entries
342
+ .filter(entry => !entry.isSymbolicLink() && entry.isDirectory() && !UPDATE_ALL_SKIP_DIRS.has(entry.name))
343
+ .sort((a, b) => a.name.localeCompare(b.name));
344
+
345
+ for (const entry of childDirs) {
346
+ await walk(path.join(dir, entry.name), depth + 1);
347
+ }
348
+ }
349
+
350
+ await walk(rootDir, 0);
351
+ return projects;
238
352
  }
239
353
 
240
354
  async function listSkills() {
@@ -264,7 +378,7 @@ function formatSettings(settings) {
264
378
  return `version: 1
265
379
 
266
380
  defaults:
267
- # Включает strict-режим по умолчанию для eda-explore, eda-plan и eda-review.
381
+ # Включает strict-режим по умолчанию для eda-explore, eda-plan, eda-review и eda-review-check.
268
382
  # true | false
269
383
  strict: ${settings.strict ? 'true' : 'false'}
270
384
  # Задаёт размер плана по умолчанию для eda-plan.
@@ -286,7 +400,7 @@ automate:
286
400
  include_plans: ${settings.includePlans ? 'true' : 'false'}
287
401
 
288
402
  review:
289
- # Добавляет в eda-review проверку качества кода и meta-reviewer quality-check.
403
+ # Добавляет в eda-review / eda-review-check проверку качества кода и meta-reviewer quality-check.
290
404
  # true | false
291
405
  include_code_quality: ${settings.includeCodeQuality ? 'true' : 'false'}
292
406
  `;
@@ -347,6 +461,35 @@ function formatSkillCount(count) {
347
461
  return `${count} ${pluralizeSkill(count)}`;
348
462
  }
349
463
 
464
+ function formatProjectPath(rootDir, projectDir) {
465
+ const relative = path.relative(rootDir, projectDir);
466
+ return relative === '' ? '.' : relative;
467
+ }
468
+
469
+ function formatProjectCount(count) {
470
+ return `${count} ${pluralizeProject(count)}`;
471
+ }
472
+
473
+ function pluralizeProject(count) {
474
+ const mod10 = count % 10;
475
+ const mod100 = count % 100;
476
+ if (mod10 === 1 && mod100 !== 11) return 'проект';
477
+ if (mod10 >= 2 && mod10 <= 4 && (mod100 < 12 || mod100 > 14)) return 'проекта';
478
+ return 'проектов';
479
+ }
480
+
481
+ function formatErrorCount(count) {
482
+ return `${count} ${pluralizeError(count)}`;
483
+ }
484
+
485
+ function pluralizeError(count) {
486
+ const mod10 = count % 10;
487
+ const mod100 = count % 100;
488
+ if (mod10 === 1 && mod100 !== 11) return 'ошибка';
489
+ if (mod10 >= 2 && mod10 <= 4 && (mod100 < 12 || mod100 > 14)) return 'ошибки';
490
+ return 'ошибок';
491
+ }
492
+
350
493
  function pluralizeSkill(count) {
351
494
  const mod10 = count % 10;
352
495
  const mod100 = count % 100;
@@ -397,3 +540,12 @@ async function fileExists(p) {
397
540
  return false;
398
541
  }
399
542
  }
543
+
544
+ async function statIfExists(p) {
545
+ try {
546
+ return await fs.stat(p);
547
+ } catch (err) {
548
+ if (err?.code !== 'ENOENT') throw err;
549
+ return null;
550
+ }
551
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gian-tiaga/eda",
3
- "version": "0.6.2",
4
- "description": "Набор скилов eda-* для Claude Code и Codex CLI: roadmap, explore, plan, execute, fix, review, fix-by-review, send-review, commit, worktree, merge-worktree, docs, automate",
3
+ "version": "0.7.0",
4
+ "description": "Набор скилов eda-* для Claude Code и Codex CLI: roadmap, explore, plan, execute, fix, review, review-check, fix-by-review, polish, send-review, commit, worktree, merge-worktree, docs, automate",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "eda": "bin/cli.js"