@gian-tiaga/eda 0.8.5 → 0.8.6

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
@@ -47,7 +47,7 @@ eda update-all ~/Code
47
47
  | `eda-fix-by-review` | Применяет замечания из ревью. Обязательные — все сразу. Optional-пункты в обычном режиме спрашивает, а в `apply-optional` сам решает: применяет полезные, отклонённые обосновывает в отчёте. В конце ссылается на отчёт прямо в файле ревью | `docs/review-fixes/` |
48
48
  | `eda-manual-test` | Делает ручную smoke/acceptance-проверку после изменений: API прогоняет через `curl`/HTTP-запросы, фронт — через Playwright или доступную browser automation. Сам строит чеклист из входа, плана или diff, запускает очевидные dev/API-серверы и сохраняет QA-отчёт. Не правит код, не пишет автотесты и не коммитит | `docs/manual-tests/` |
49
49
  | `eda-polish` | Оркестрирует доводку изменений: запускает `eda-review draft`, затем `eda-review-check`, затем `eda-fix-by-review apply-optional` и повторяет цикл до оценки 95/100 или лимита 5 итераций. Обязательные замечания чинятся все, optional-пункты fix-агент применяет или отклоняет сам с обоснованием. Каждый шаг идёт в изолированном субагенте или отдельном CLI-процессе. Не коммитит | `docs/reviews/`, `docs/review-fixes/` |
50
- | `eda-send-review` | Отправляет ревью на GitHub PR через `gh` — как комментарий, review, approve или request-changes. Сам определяет PR по текущей ветке | (комментарий в PR) |
50
+ | `eda-send-review` | Отправляет ревью на GitHub PR через `gh` всегда в едином формате: общий комментарий-сводка плюс комментарии к строкам кода. Тип (`comment`/`approve`/`request-changes`) задаёт только статус review; `approve` и `request-changes` подтверждаются. По умолчанию шлёт все пункты. Сам определяет PR по текущей ветке | (комментарий в PR) |
51
51
  | `eda-commit` | Делает один аккуратный коммит в стиле проекта. В конце спрашивает: push, merge в main + удалить ветку, или ничего | (git) |
52
52
  | `eda-worktree` | Создаёт git worktree рядом с основным проектом в папке `{name}-work-{n}` и одноимённую ветку. Базу берёт из аргумента или спрашивает | соседняя папка `{name}-work-{n}` |
53
53
  | `eda-merge-worktree` | Мержит ветку из соседнего worktree в текущую ветку. Принимает номер `1`, короткое имя `work-1` или полное имя `{name}-work-1`. Worktree и ветку после merge не удаляет | (git) |
package/lib/install.js CHANGED
@@ -165,7 +165,7 @@ export async function updateAll({
165
165
  output.write(`\n=== ${projectLabel} ===\n`);
166
166
 
167
167
  try {
168
- const targets = await detectTargets(projectDir);
168
+ const targets = await detectInstalledTargets(projectDir);
169
169
  if (targets.length === 0) {
170
170
  output.write('Пропускаю: установленные среды исчезли во время обхода.\n');
171
171
  skippedProjects.push(projectDir);
@@ -295,6 +295,28 @@ async function detectTargets(cwd) {
295
295
  return targets;
296
296
  }
297
297
 
298
+ // Среда считается установленной, только если в её папке скилов лежит хотя бы один eda-скил.
299
+ // Используется при массовом обходе (update-all), чтобы не засыпать скилами проекты,
300
+ // где папка .claude/skills или .codex/skills есть, но eda-скилов в ней нет.
301
+ async function detectInstalledTargets(cwd) {
302
+ const targets = [];
303
+ if (await hasInstalledSkill(path.join(cwd, '.claude/skills'))) targets.push('claude');
304
+ if (await hasInstalledSkill(path.join(cwd, '.codex/skills'))) targets.push('codex');
305
+ return targets;
306
+ }
307
+
308
+ async function hasInstalledSkill(skillsDir) {
309
+ let entries;
310
+ try {
311
+ entries = await fs.readdir(skillsDir, { withFileTypes: true });
312
+ } catch (err) {
313
+ if (['ENOENT', 'ENOTDIR', 'EACCES', 'EPERM'].includes(err?.code)) return false;
314
+ throw err;
315
+ }
316
+ // eda-скилы лежат как папка eda-* (новый layout) или файл eda-*.md (старый Codex layout).
317
+ return entries.some(entry => entry.name.startsWith('eda-'));
318
+ }
319
+
298
320
  async function syncSkills(cwd, targets, output = process.stdout, { action = 'update', writeDone = true } = {}) {
299
321
  const skills = await listSkills();
300
322
  if (skills.length === 0) {
@@ -326,7 +348,7 @@ async function findInstalledProjects(rootDir, maxDepth) {
326
348
  const projects = [];
327
349
 
328
350
  async function walk(dir, depth) {
329
- const targets = await detectTargets(dir);
351
+ const targets = await detectInstalledTargets(dir);
330
352
  if (targets.length > 0) projects.push(dir);
331
353
  if (depth >= maxDepth) return;
332
354
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gian-tiaga/eda",
3
- "version": "0.8.5",
3
+ "version": "0.8.6",
4
4
  "description": "Набор скилов eda-* для Claude Code и Codex CLI: start, roadmap, explore, plan, plan-polish, execute, fix, review, review-check, fix-by-review, manual-test, polish, send-review, commit, worktree, merge-worktree, docs, automate",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,25 +1,27 @@
1
1
  ---
2
2
  name: eda-send-review
3
- description: 'Отправляет ревью из `docs/reviews/...`, выбранное по тексту рядом с вызовом, на GitHub PR через `gh` — как review (comment/approve/request-changes) или как обычный комментарий. Перед отправкой читает `docs/rules.md` и `docs/arch.md` и строго следует им. Целевой PR определяется из текста или автоматически по текущей ветке; если PR нет — предлагает создать. После отправки в конец файла ревью дописывает ссылку на PR-комментарий. Не правит код, не делает само ревью. Вопросы задаёт только когда без ответа нельзя безопасно продолжать.'
3
+ description: 'Отправляет ревью из `docs/reviews/...`, выбранное по тексту рядом с вызовом, на GitHub PR через `gh` — всегда в едином формате: общий комментарий-сводка плюс комментарии к строкам кода (inline). Тип отправки (`comment`/`approve`/`request-changes`) задаёт только статус review, но не структуру вывода. По умолчанию отправляет все пункты; если в тексте указано подмножество — только его. Перед отправкой читает `docs/rules.md` и `docs/arch.md` и строго следует им. Целевой PR определяется из текста или по текущей ветке; если PR нет — предлагает создать. После отправки в конец файла ревью дописывает ссылку. Не правит код, не делает само ревью. Вопросы задаёт только когда без ответа нельзя безопасно продолжать.'
4
4
  ---
5
5
 
6
6
  # Скил: Отправитель ревью (eda-send-review)
7
7
 
8
- Берёшь готовое ревью из `docs/reviews/`, отправляешь его на GitHub PR через `gh`. В конец файла ревью дописываешь, куда отправил.
8
+ Берёшь готовое ревью из `docs/reviews/`, отправляешь его на GitHub PR через `gh`. Отправляешь **всегда в одном формате**: общий комментарий-сводка плюс комментарии к строкам кода. В конец файла ревью дописываешь, куда отправил.
9
9
 
10
10
  ## Вход из сообщения пользователя
11
11
 
12
- Текст рядом с вызовом скилла в текущем сообщении пользователя — главный вход. Сначала разбери именно его: тему, задачу, путь к файлу, режим, ограничения и прямые указания.
12
+ Текст рядом с вызовом скилла в текущем сообщении пользователя — главный вход. Сначала разбери именно его: тему, задачу, путь к файлу, режим, ограничения, какие пункты отправлять и прямые указания.
13
13
 
14
14
  Если этого входа достаточно, продолжай без вопроса. `AskUserQuestion` задавай только если входа нет, он противоречивый, найдено несколько равных вариантов или есть риск выполнить не ту задачу. Старое обсуждение в истории не считай входом, если пользователь не связал его с текущим вызовом явно.
15
15
 
16
16
  ## Главные правила
17
17
 
18
- 1. **Интерактивные вопросы** — по разделу ниже.
19
- 2. **Прочитай `docs/rules.md` и `docs/arch.md`**, если есть, и строго следуй им при выборе способа отправки и финальной отметке.
20
- 3. **Не правишь код, не делаешь само ревью.** Только переупаковка и отправка.
21
- 4. **Никаких самостоятельных оценок.** Тип отправки определяется на основе ревью и текста пользователя. `comment` означает обычный комментарий PR, `review-comment` — review без статуса. `approve` и `request-changes` всегда подтверждаются пользователем.
22
- 5. **Простой язык.** В сообщениях пользователю и комментарии PR без лишних терминов.
18
+ 1. **Единый формат всегда.** Отправляешь как GitHub review: тело — общий комментарий-сводка, а каждое замечание с привязкой к строке — отдельным комментарием к строке кода (inline). Тип отправки меняет только статус review (`event`), но не структуру вывода.
19
+ 2. **По умолчанию — все пункты.** Если пользователь не указал, что отправлять, отправляешь все замечания ревью. Если указал подмножество — только его.
20
+ 3. **Интерактивные вопросы** — по разделу ниже.
21
+ 4. **Прочитай `docs/rules.md` и `docs/arch.md`**, если есть, и строго следуй им при выборе способа отправки и финальной отметке.
22
+ 5. **Не правишь код, не делаешь само ревью.** Только переупаковка и отправка.
23
+ 6. **Никаких самостоятельных оценок.** Статус review определяется по оценке ревью и тексту пользователя. `approve` и `request-changes` всегда подтверждаются пользователем.
24
+ 7. **Простой язык.** В сообщениях пользователю и комментариях PR без лишних терминов.
23
25
 
24
26
  ## Интерактивные вопросы
25
27
 
@@ -28,61 +30,82 @@ description: 'Отправляет ревью из `docs/reviews/...`, выбр
28
30
  - Codex interactive: если доступен `request_user_input`, используй его. Если tool недоступен, задай один короткий вопрос в чат, дай варианты 1–3, напиши «Ответь номером или своим вариантом. Я продолжу только после ответа.» и остановись.
29
31
  - Codex exec / неинтерактивный запуск: не задавай вопросы и не пытайся читать stdin. Если без ответа нельзя безопасно продолжать, заверши работу со статусом `blocked: нужен ответ пользователя`, перечисли вопросы и варианты, не выполняй рискованные действия.
30
32
  - Не запускай команды, которые ждут интерактивного ввода в терминале; перед такими командами спроси в хост-интерфейсе или остановись с `blocked`.
31
- - Для отправки ревью это правило важнее продолжения: не создавай PR, не отправляй approve/request-changes и не меняй тип отправки вместо ответа пользователя.
33
+ - Для отправки ревью это правило важнее продолжения: не создавай PR, не отправляй approve/request-changes и не меняй статус отправки вместо ответа пользователя.
32
34
 
33
35
  ## Этапы
34
36
 
35
37
  ### 1. Проверить инструменты
36
38
  `gh --version` + `gh auth status`. Если `gh` отсутствует или не авторизован — остановись, сообщи и выйди (это пользователю нужно решить).
37
39
 
38
- ### 2. Выбрать ревью
39
- Сначала разбери текст рядом с вызовом: путь к ревью, название или часть названия, «последнее ревью», PR (`PR 12`, `#12`) и тип отправки (`comment`, `review-comment`, `approve`, `request-changes`).
40
+ ### 2. Выбрать ревью и что отправлять
41
+ Сначала разбери текст рядом с вызовом: путь к ревью, название или часть названия, «последнее ревью», PR (`PR 12`, `#12`) и статус отправки (`comment`, `review-comment`, `approve`, `request-changes`).
40
42
 
41
43
  Если найдено ровно одно ревью — продолжай без вопроса. Если найдено несколько одинаково подходящих ревью — `AskUserQuestion` со списком вариантов. Если входа нет — `AskUserQuestion` со списком последних файлов из `docs/reviews/`. Если `docs/reviews/` отсутствует или пуста — остановись с понятным вопросом или статусом `blocked: нужно ревью`. Прочитай файл целиком: оценку, замечания, рекомендации.
42
44
 
45
+ **Что отправлять.** По умолчанию — все замечания. Если в тексте указано подмножество, отправь только его. Синтаксис свободный, распознавай по смыслу: по номерам («пункты 1, 3, 5»), по обязательности («только обязательные» → раздел «Править обязательно»), по типу («только security» → замечания с этим `Тип`). Выбор влияет и на сводку, и на комментарии к строкам.
46
+
43
47
  ### 3. Найти целевой PR
44
48
  Если номер PR указан в тексте пользователя — используй его. Иначе попробуй `gh pr view --json number,url,headRefName` — если у текущей ветки уже есть PR, возьми его номер.
45
49
 
46
50
  Если PR нет — `AskUserQuestion`: «PR ещё нет: создать PR из текущей ветки / указать номер существующего PR / прервать». При создании используй `gh pr create` (заголовок и тело — по последним коммитам ветки).
47
51
 
48
- ### 4. Выбрать тип отправки
49
- Предложи вариант по оценке: score ≥ 80 → **approve** с комментарием; score < 50 → **request-changes**; иначе → **review-comment**.
52
+ ### 4. Выбрать статус review
53
+ Review отправляется всегда; здесь выбирается только его статус (`event`).
54
+
55
+ | Условие | event | Подтверждение |
56
+ |---|---|---|
57
+ | score ≥ 80 (или в тексте `approve`) | `APPROVE` | всегда `AskUserQuestion` |
58
+ | score < 50 (или в тексте `request-changes`) | `REQUEST_CHANGES` | всегда `AskUserQuestion` |
59
+ | иначе (или в тексте `comment` / `review-comment`) | `COMMENT` | без подтверждения |
60
+
61
+ `comment` и `review-comment` означают review без статуса (`event=COMMENT`) — обычный комментарий-ревью, который тоже несёт сводку и комментарии к строкам. `approve` и `request-changes` подтверждай через `AskUserQuestion` всегда, даже если указаны в тексте. Если предложенный статус противоречит ревью — `AskUserQuestion`: подтвердить / выбрать другой.
50
62
 
51
- Если тип отправки указан в тексте и это `comment` или `review-comment`, используй его без дополнительного выбора, если нет противоречий с ревью. `approve` и `request-changes` всегда подтверждай через `AskUserQuestion`, даже если они указаны inline. Если тип не указан или противоречит ревью — `AskUserQuestion`: подтвердить предложенный тип / выбрать другой / отправить как обычный комментарий PR (вне review).
63
+ ### 5. Собрать сводку и комментарии к строкам
64
+ Из выбранных пунктов собери две части.
52
65
 
53
- ### 5. Сформировать тело
54
- Структура комментария:
66
+ **Сводка** — тело review:
55
67
 
56
68
  ```markdown
57
69
  ## Ревью: <заголовок>
58
70
 
59
71
  **Оценка:** N/100. <одно-два предложения почему>
60
72
 
61
- ### Замечания
62
- 1. **[тип]** `path/file.py:42` — <что не так>. → <что предлагаешь>
63
- 2. ...
73
+ <короткое резюме: что отправлено и общий вывод>
64
74
 
65
75
  ### Рекомендации
66
76
  - **Править обязательно:** 1, 3, 5
67
77
  - **На усмотрение автора:** 2, 4
68
- - **Не править:** —
78
+
79
+ ### Замечания вне diff
80
+ 1. **[тип]** `path/file.py:42` — <что не так>. → <что предлагаешь>
69
81
 
70
82
  _Сгенерировано из `<путь к docs/reviews/...>`_
71
83
  ```
72
84
 
85
+ Раздел «Замечания вне diff» добавляй только если такие пункты есть (см. ниже); иначе опусти его.
86
+
87
+ **Комментарии к строкам** — по одному на каждое отправляемое замечание, чьё `Где: path:line` входит в diff PR. Для каждого: `{ "path": "<path>", "line": <line>, "side": "RIGHT", "body": "<суть простым языком + рекомендация + как исправить>" }`. Для замечания об удалённой строке используй `"side": "LEFT"`.
88
+
89
+ Какие строки входят в diff — определи по `gh pr diff <num>`. Замечания, чьи строки в diff не попали (или без `path:line`), в комментарии к строкам **не** клади — перенеси их в раздел «Замечания вне diff» сводки, чтобы ничего не потерялось.
90
+
73
91
  ### 6. Отправить
74
- По выбранному типу:
92
+ Тело сводки и комментарии собери в один JSON через `mktemp`:
75
93
 
76
- | Тип | Команда |
77
- |---|---|
78
- | comment (PR comment) | `gh pr comment <num> --body-file <tmpfile>` |
79
- | review-comment | `gh pr review <num> --comment --body-file <tmpfile>` |
80
- | approve | `gh pr review <num> --approve --body-file <tmpfile>` |
81
- | request-changes | `gh pr review <num> --request-changes --body-file <tmpfile>` |
94
+ ```json
95
+ { "body": "<сводка>", "event": "COMMENT|APPROVE|REQUEST_CHANGES", "comments": [ { "path": "...", "line": 42, "side": "RIGHT", "body": "..." } ] }
96
+ ```
97
+
98
+ Отправь одним вызовом Reviews API:
82
99
 
83
- Тело пиши во временный файл (`mktemp`) и передавай через `--body-file` — иначе спецсимволы и переносы поедут не туда.
100
+ ```bash
101
+ OWNER_REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
102
+ gh api "repos/$OWNER_REPO/pulls/<num>/reviews" --method POST --input <payload.json>
103
+ ```
84
104
 
85
- Если команда упала — покажи вывод пользователю и спроси через `AskUserQuestion` (повторить / другой тип / прервать).
105
+ Fallback'ы:
106
+ - API упал с `422` из-за конкретных строк — перенеси эти пункты в раздел «Замечания вне diff» сводки и переотправь без них.
107
+ - У цели нет diff/строк PR вовсе (нечего привязывать) — отправь только сводку обычным комментарием: `gh pr comment <num> --body-file <tmpfile>`.
108
+ - Другая ошибка — покажи вывод пользователю и спроси через `AskUserQuestion` (повторить / другой статус / прервать).
86
109
 
87
110
  ### 7. Дописать ссылку в файл ревью
88
111
  В конец `$REVIEW_FILE` добавь:
@@ -90,16 +113,19 @@ _Сгенерировано из `<путь к docs/reviews/...>`_
90
113
  ```markdown
91
114
  ## Отправлено
92
115
  - PR: <URL из ответа gh>
93
- - Тип: <comment | review-comment | approve | request-changes>
116
+ - Статус: <comment | approve | request-changes>
117
+ - Отправлено пунктов: <сколько / inline + сколько в сводке>
94
118
  - Дата: <YYYY-MM-DD HH:MM>
95
119
  ```
96
120
 
97
121
  ### 8. Финал
98
- Короткое сообщение: PR-номер и URL, тип отправки, путь к файлу ревью.
122
+ Короткое сообщение: PR-номер и URL, статус отправки, сколько пунктов ушло комментариями к строкам и сколько в сводку, путь к файлу ревью.
99
123
 
100
124
  ## Чего НЕ делать
101
125
  - Делать само ревью или править замечания — это `eda-review` / `eda-fix-by-review`.
102
- - Отправлять `approve` без явного подтверждения через `AskUserQuestion`, даже если оценка ≥ 80.
103
- - Передавать тело комментария через `-b "..."` со спецсимволами без `--body-file` — поломает форматирование.
126
+ - Отправлять всё одним обычным комментарием PR, когда строки можно привязать к diff — обычный комментарий только fallback при отсутствии diff.
127
+ - Терять замечания вне diff — они идут в сводку отдельным разделом.
128
+ - Отправлять `approve` или `request-changes` без явного подтверждения через `AskUserQuestion`, даже если оценка ≥ 80.
129
+ - Передавать тело через `-b "..."` со спецсимволами — только `--input` (Reviews API) или `--body-file` (fallback).
104
130
  - Создавать PR молча, если его нет — только через `AskUserQuestion`.
105
131
  - Сохранять копии отправленного в отдельные файлы — связь через ссылку в файле ревью.