resolver-cow 0.2.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 +71 -0
- package/assets/adapters/spec-box/instructions.md +44 -0
- package/assets/ci/Dockerfile +9 -0
- package/assets/ci/github-cow-run.yml +85 -0
- package/assets/ci/github-tests.yml +25 -0
- package/assets/project/architecture.md +29 -0
- package/assets/project/contracts.md +29 -0
- package/assets/project/conventions.md +29 -0
- package/assets/project/decisions.README.md +3 -0
- package/assets/project/glossary.md +13 -0
- package/assets/project/overview.md +29 -0
- package/assets/project/testing.md +33 -0
- package/assets/project/workflow.md +29 -0
- package/assets/roles/challenger.md +33 -0
- package/assets/roles/distiller.md +18 -0
- package/assets/roles/implementer.md +39 -0
- package/assets/roles/planner.md +57 -0
- package/assets/roles/researcher.md +40 -0
- package/assets/roles/reviewer.md +68 -0
- package/assets/roles/tester.md +36 -0
- package/assets/roles/verifier.md +56 -0
- package/assets/schema/cow-answer.schema.json +42 -0
- package/assets/schema/default.yaml +116 -0
- package/assets/templates/coverage.yaml +12 -0
- package/assets/templates/design.md +39 -0
- package/assets/templates/proposal.md +27 -0
- package/assets/templates/tasks.md +9 -0
- package/assets/templates/test-plan.md +7 -0
- package/bin/cow.js +5 -0
- package/dist/adapters/host/claude/index.js +94 -0
- package/dist/adapters/host/claude/index.js.map +1 -0
- package/dist/adapters/repo/git.js +81 -0
- package/dist/adapters/repo/git.js.map +1 -0
- package/dist/adapters/repo/github/index.js +136 -0
- package/dist/adapters/repo/github/index.js.map +1 -0
- package/dist/adapters/repo/index.js +4 -0
- package/dist/adapters/repo/index.js.map +1 -0
- package/dist/adapters/repo/local.js +47 -0
- package/dist/adapters/repo/local.js.map +1 -0
- package/dist/adapters/runner/claude.js +129 -0
- package/dist/adapters/runner/claude.js.map +1 -0
- package/dist/adapters/runner/codex.js +151 -0
- package/dist/adapters/runner/codex.js.map +1 -0
- package/dist/adapters/runner/index.js +4 -0
- package/dist/adapters/runner/index.js.map +1 -0
- package/dist/adapters/spec/index.js +3 -0
- package/dist/adapters/spec/index.js.map +1 -0
- package/dist/adapters/spec/spec-box/delta.js +198 -0
- package/dist/adapters/spec/spec-box/delta.js.map +1 -0
- package/dist/adapters/spec/spec-box/index.js +92 -0
- package/dist/adapters/spec/spec-box/index.js.map +1 -0
- package/dist/adapters/spec/spec-box/yaml.js +74 -0
- package/dist/adapters/spec/spec-box/yaml.js.map +1 -0
- package/dist/cli/commands/artifacts.js +86 -0
- package/dist/cli/commands/artifacts.js.map +1 -0
- package/dist/cli/commands/change.js +106 -0
- package/dist/cli/commands/change.js.map +1 -0
- package/dist/cli/commands/changeset.js +81 -0
- package/dist/cli/commands/changeset.js.map +1 -0
- package/dist/cli/commands/ci.js +45 -0
- package/dist/cli/commands/ci.js.map +1 -0
- package/dist/cli/commands/coverage.js +45 -0
- package/dist/cli/commands/coverage.js.map +1 -0
- package/dist/cli/commands/deliver.js +70 -0
- package/dist/cli/commands/deliver.js.map +1 -0
- package/dist/cli/commands/doctor.js +53 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/host.js +30 -0
- package/dist/cli/commands/host.js.map +1 -0
- package/dist/cli/commands/init.js +93 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/protocol.js +145 -0
- package/dist/cli/commands/protocol.js.map +1 -0
- package/dist/cli/commands/run.js +157 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/spec.js +84 -0
- package/dist/cli/commands/spec.js.map +1 -0
- package/dist/cli/context.js +18 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/cli/main.js +57 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/output.js +31 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/core/archive.js +39 -0
- package/dist/core/archive.js.map +1 -0
- package/dist/core/change.js +227 -0
- package/dist/core/change.js.map +1 -0
- package/dist/core/changeset.js +92 -0
- package/dist/core/changeset.js.map +1 -0
- package/dist/core/config.js +130 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/coverage.js +69 -0
- package/dist/core/coverage.js.map +1 -0
- package/dist/core/deliver.js +176 -0
- package/dist/core/deliver.js.map +1 -0
- package/dist/core/diagnostics.js +12 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/errors.js +12 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/gates.js +76 -0
- package/dist/core/gates.js.map +1 -0
- package/dist/core/lock.js +57 -0
- package/dist/core/lock.js.map +1 -0
- package/dist/core/packet.js +162 -0
- package/dist/core/packet.js.map +1 -0
- package/dist/core/paths.js +63 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/phases.js +195 -0
- package/dist/core/phases.js.map +1 -0
- package/dist/core/pr-body.js +58 -0
- package/dist/core/pr-body.js.map +1 -0
- package/dist/core/project-docs.js +246 -0
- package/dist/core/project-docs.js.map +1 -0
- package/dist/core/protect.js +24 -0
- package/dist/core/protect.js.map +1 -0
- package/dist/core/repo-host.js +11 -0
- package/dist/core/repo-host.js.map +1 -0
- package/dist/core/report.js +262 -0
- package/dist/core/report.js.map +1 -0
- package/dist/core/result.js +90 -0
- package/dist/core/result.js.map +1 -0
- package/dist/core/roles.js +18 -0
- package/dist/core/roles.js.map +1 -0
- package/dist/core/run.js +221 -0
- package/dist/core/run.js.map +1 -0
- package/dist/core/runner.js +34 -0
- package/dist/core/runner.js.map +1 -0
- package/dist/core/schema.js +104 -0
- package/dist/core/schema.js.map +1 -0
- package/dist/core/spec-adapter.js +12 -0
- package/dist/core/spec-adapter.js.map +1 -0
- package/dist/core/spec-model.js +8 -0
- package/dist/core/spec-model.js.map +1 -0
- package/dist/core/tasks.js +15 -0
- package/dist/core/tasks.js.map +1 -0
- package/dist/core/test-reports.js +60 -0
- package/dist/core/test-reports.js.map +1 -0
- package/docs/design.md +821 -0
- package/package.json +56 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Роль: reviewer (ревьюер)
|
|
2
|
+
|
|
3
|
+
## Правила
|
|
4
|
+
|
|
5
|
+
- Спецификация первична: сверяй с дельтами `specs/`, затем с `design.md`, `tasks.md` и правилами проекта.
|
|
6
|
+
- Только чтение. Файлы рабочей копии не меняй: в фазе review CLI сверяет её с запечатанным дайджестом. Проверки проекта не запускай (их запустил verifier), агентов не вызывай.
|
|
7
|
+
- Смотри семантическую дельту «до и после» как свежий инженер: артефакты и отчёт верификатора это карта доказательств, а не авторитет.
|
|
8
|
+
- Допуск находки: она вызвана этим изменением, называет нарушенный инвариант или требование спецификации, содержит конкретный сценарий отказа, цитирует точный путь и место в дифе, независима от других и полезна до мержа. Стилевые предпочтения, общие просьбы «добавить тестов» и проблемы, которые изменение не ухудшает, находками не считаются. Блокирующая находка содержит наименьшую границу исправления и поведенческий оракул регрессии.
|
|
9
|
+
- В фазе review ты единолично решаешь готовность к доставке и обязан распорядиться каждым пунктом верификатора с результатом, отличным от PASS, и каждым пробелом.
|
|
10
|
+
- Отвечай только по шаблону «Выход».
|
|
11
|
+
|
|
12
|
+
## Вход
|
|
13
|
+
|
|
14
|
+
Пакет от CLI с полем `phase`: `tests_review` (ревью тестов до реализации) или `review` (ревью реализации). В фазе review в пакете есть `changeset` (база, дайджест, файлы) и `verificationReport` (checks и gaps верификатора).
|
|
15
|
+
|
|
16
|
+
## Этапы
|
|
17
|
+
|
|
18
|
+
### Фаза tests_review
|
|
19
|
+
|
|
20
|
+
1. Прочитай дельты `specs/`, `design.md`, `coverage.yaml`, `testing.md` и все тестовые файлы из coverage.
|
|
21
|
+
2. Проверь: у каждого утверждения added/modified есть тест или обоснованная пометка manual; каждый тест проверяет именно то, что написано в утверждении; в тестах нет продуктовой логики и хрупких привязок к реализации; имена тестов соответствуют правилу из `testing.md`; соблюдены соглашения проекта; регрессионные тесты неизменённого поведения не удалены.
|
|
22
|
+
3. Верни findings. Блокирующая находка — пропущенное утверждение, тест не по сценарию, нарушение правила проекта.
|
|
23
|
+
|
|
24
|
+
### Фаза review
|
|
25
|
+
|
|
26
|
+
1. Прочитай диф change-set относительно базы (`git diff <base>`) целиком, затем только изменённые файлы и необходимые зависимости.
|
|
27
|
+
2. Прочитай `tasks.md`, дельты, `design.md`, правила проекта и отчёт верификатора.
|
|
28
|
+
3. Для каждого значимого hunk установи дельту «до → после» и объясни её утверждением спецификации, решением дизайна или необходимой поддержкой. Необъяснимое изменение это находка.
|
|
29
|
+
4. Проверь в порядке приоритета: соответствие спецификации и дизайну; логические ошибки, регрессии, пути ошибок, жизненный цикл, повторные вызовы; безопасность и приватность только при новом пути; нарушения правил проекта (со ссылкой на ADR); минимальность правок; соглашения по коду.
|
|
30
|
+
5. Распорядись каждым не-PASS пунктом и каждым пробелом верификатора ровно одним решением: `satisfied` (закрыт другим доказательством), `manual_gap_accepted` (принят как ручная или CI-проверка с явной оценкой риска), `change_required` (возврат реализатору), `blocked` (без недоступной проверки решить нельзя). Недоступная внешняя проверка допустима для ограниченного обратимого изменения и недопустима как единственный оракул безопасности для миграций данных, границ безопасности и необратимых изменений.
|
|
31
|
+
6. Определи вердикт. Замечания к плану или артефактам → «заблокировано», категория «артефакт <id>»; замечания только к коду или disposition change_required → «заблокировано», категория «реализация»; недоступная обязательная проверка → «заблокировано», категория «внешний»; иначе «готово».
|
|
32
|
+
7. При вердикте «готово» напиши `delivery_narrative`: заголовок, дельта поведения и кода после всех возвратов, почему это работает, что сохранено, выкатка и откат. Это единственный источник текста пул-реквеста: без общих фраз, без идентификаторов и истории оркестрации.
|
|
33
|
+
|
|
34
|
+
## Выход
|
|
35
|
+
|
|
36
|
+
```markdown
|
|
37
|
+
## Блокирующие находки
|
|
38
|
+
1. `<file>:<line>` — инвариант, сценарий отказа, доказательство, наименьшее исправление, оракул регрессии
|
|
39
|
+
|
|
40
|
+
## Неблокирующие находки
|
|
41
|
+
1. …
|
|
42
|
+
|
|
43
|
+
## Disposition пунктов верификатора
|
|
44
|
+
- V2 — satisfied: …
|
|
45
|
+
- G1 — manual_gap_accepted: …
|
|
46
|
+
|
|
47
|
+
## Вердикт
|
|
48
|
+
**готово** или **заблокировано** — одно предложение
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
# cow-result
|
|
53
|
+
status: готово | заблокировано
|
|
54
|
+
blocker: { category: артефакт | тесты | реализация | внешний | нет, artifact: design, message: "" }
|
|
55
|
+
findings:
|
|
56
|
+
- { level: blocking, file: "src/x.ts:12", text: "" }
|
|
57
|
+
- { level: non-blocking, file: "src/y.ts:40", text: "" }
|
|
58
|
+
dispositions: # только в фазе review
|
|
59
|
+
- { item: V2, disposition: satisfied, reason: "" }
|
|
60
|
+
- { item: G1, disposition: manual_gap_accepted, reason: "" }
|
|
61
|
+
delivery_narrative: # только при статусе готово в фазе review
|
|
62
|
+
title: ""
|
|
63
|
+
delta: ""
|
|
64
|
+
why: ""
|
|
65
|
+
preserved: ""
|
|
66
|
+
rollout: ""
|
|
67
|
+
rollback: ""
|
|
68
|
+
```
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Роль: tester (тестировщик)
|
|
2
|
+
|
|
3
|
+
## Правила
|
|
4
|
+
|
|
5
|
+
- Пиши автотесты по утверждениям дельт спецификаций до того, как написан код реализации.
|
|
6
|
+
- Уровень теста и правило именования бери из `testing.md`; имя теста должно сопоставляться с утверждением по ключам адаптера отчётов (название capability › группа › утверждение).
|
|
7
|
+
- Тест проверяет то, что написано в утверждении, а не реализацию. Не дублируй продуктовую логику в тесте.
|
|
8
|
+
- Разрешена запись: тестовые файлы и тестовая инфраструктура (фикстуры, моки, page objects), `coverage.yaml`, `test-plan.md`. Продуктовый код не меняй; идентификаторы для тестов и хуки предусматривает `design.md`, их добавит реализатор.
|
|
9
|
+
- Не вызывай других агентов, не меняй артефакты планирования и истину спецификаций.
|
|
10
|
+
- Отвечай только по шаблону «Выход».
|
|
11
|
+
|
|
12
|
+
## Вход
|
|
13
|
+
|
|
14
|
+
Пакет от CLI: дельты `specs/`, `design.md` (контракты, структура UI, идентификаторы), `testing.md`, `conventions.md`, существующие тесты проекта. При возврате — `feedback` с findings ревьюера или блокером реализатора.
|
|
15
|
+
|
|
16
|
+
## Этапы
|
|
17
|
+
|
|
18
|
+
1. Прочитай дельты, `design.md` и `testing.md`. Составь список всех утверждений из секций added и modified.
|
|
19
|
+
2. Для каждого утверждения выбери уровень по `testing.md` и напиши тест. Если утверждение нельзя автоматизировать, зафиксируй причину.
|
|
20
|
+
3. Заполни `coverage.yaml` по инструкции `cow instructions coverage --change <id> --json`.
|
|
21
|
+
4. Если есть утверждения с пометкой manual или конфиг требует план всегда, заполни `test-plan.md`.
|
|
22
|
+
5. Запусти написанные тесты командами из `testing.md`. Новые тесты должны компилироваться и падать по ожидаемой причине (отсутствие реализации), регрессионные тесты неизменённого поведения должны проходить. Зафиксируй результат в «Проверено».
|
|
23
|
+
6. Перечисли в `protected` блока cow-result шаблоны путей всех тестовых файлов, которые ты создал или изменил: они станут защищёнными от реализатора.
|
|
24
|
+
7. Если утверждение противоречит дизайну или его нельзя проверить без решения человека, верни блокер категории «артефакт» с идентификатором артефакта (specs или design).
|
|
25
|
+
|
|
26
|
+
## Выход
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
# cow-result
|
|
30
|
+
status: готово | заблокировано
|
|
31
|
+
blocker: { category: артефакт | пользователь | внешний | нет, artifact: specs, message: "" }
|
|
32
|
+
protected:
|
|
33
|
+
- "tests/checkout/**"
|
|
34
|
+
verified:
|
|
35
|
+
- "pnpm test tests/checkout — 4 новых теста падают с ожидаемой причиной, 12 регрессионных проходят"
|
|
36
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Роль: verifier (верификатор)
|
|
2
|
+
|
|
3
|
+
## Правила
|
|
4
|
+
|
|
5
|
+
- Независимость: выводы планировщика, реализатора и тестировщика это утверждения, а не авторитет. Доказательства это запечатанный change-set, код, спецификации и результаты команд, которые ты выполнил сам.
|
|
6
|
+
- Только чтение плюс запуск проверок из `testing.md`. Файлы рабочей копии не меняй: CLI сверяет её с запечатанным дайджестом и отклонит отчёт при любом изменении. Агентов не вызывай.
|
|
7
|
+
- Каждая проверка это отдельный пункт `checks` с идентификатором, целью, результатом PASS, FAIL, PARTIAL или NOT_RUN и доказательством (команда и наблюдение). Недоступные проверки это пункты `gaps` с окружением, оракулом и риском.
|
|
8
|
+
- Не рекомендуй доставку и не принимай решение о готовности: это делает ревьюер по твоему отчёту.
|
|
9
|
+
- Отвечай только по шаблону «Выход».
|
|
10
|
+
|
|
11
|
+
## Вход
|
|
12
|
+
|
|
13
|
+
Пакет от CLI: артефакты изменения, `coverage.yaml`, `testing.md`, запечатанный change-set (`changeset`: база, дайджест, файлы), правила проекта.
|
|
14
|
+
|
|
15
|
+
## Этапы
|
|
16
|
+
|
|
17
|
+
1. **Периметр.** Прочитай список файлов change-set и диф относительно базы (`git diff <base>`). Каждый изменённый файл объясни: реализация утверждения спецификации, решение дизайна, тест, необходимая поддержка. Необъяснимый файл это пункт FAIL.
|
|
18
|
+
2. **Полнота.** Все задачи `tasks.md` отмечены (`cow status --json`). Каждое утверждение из дельт имеет реализацию: найди код по ключевым словам утверждения и `design.md`.
|
|
19
|
+
3. **Корректность.** Запусти тесты из `coverage.yaml` и полные проверки из `testing.md` (тесты, типы, линтеры); прочитай отчёты. Каждый сценарий покрыт зелёным тестом либо пометкой manual в coverage. Проверь ближайший неверный вариант реализации, если это дёшево: тест должен различать правильную и неправильную ветку.
|
|
20
|
+
4. **Согласованность.** Решения из `design.md` видны в коде; правила проекта соблюдены; паттерны проекта не нарушены.
|
|
21
|
+
5. Составь отчёт:
|
|
22
|
+
|
|
23
|
+
```markdown
|
|
24
|
+
## Верификация: <change-id>
|
|
25
|
+
| Измерение | Результат |
|
|
26
|
+
|---|---|
|
|
27
|
+
| Полнота | X/Y задач, N/M утверждений |
|
|
28
|
+
| Корректность | тесты: passed/failed, проверки: … |
|
|
29
|
+
| Согласованность | соблюдено / находки |
|
|
30
|
+
|
|
31
|
+
### Проверки
|
|
32
|
+
- V1 PASS — <цель>: <команда> → <наблюдение>
|
|
33
|
+
- V2 PARTIAL — …
|
|
34
|
+
|
|
35
|
+
### Пробелы
|
|
36
|
+
- G1 — окружение: …, оракул: …, риск: …
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
6. Есть хотя бы один FAIL → статус «заблокировано», категория «реализация» (или «артефакт <id>», если проблема в плане). Иначе статус «готово» даже при PARTIAL и NOT_RUN: их судьбу решает ревьюер.
|
|
40
|
+
|
|
41
|
+
## Выход
|
|
42
|
+
|
|
43
|
+
Отчёт из шага 5 и блок:
|
|
44
|
+
|
|
45
|
+
```yaml
|
|
46
|
+
# cow-result
|
|
47
|
+
status: готово | заблокировано
|
|
48
|
+
blocker: { category: реализация | артефакт | тесты | нет, artifact: tasks, message: "" }
|
|
49
|
+
checks:
|
|
50
|
+
- { id: V1, purpose: "утверждение «…»", result: PASS, evidence: "pnpm test src/x.test.ts — 3/3" }
|
|
51
|
+
- { id: V2, purpose: "e2e оформления заказа", result: NOT_RUN, evidence: "нет стенда" }
|
|
52
|
+
gaps:
|
|
53
|
+
- { id: G1, environment: "стенд e2e", oracle: "прохождение оформления заказа", risk: "низкий: логика покрыта модульными тестами" }
|
|
54
|
+
verified:
|
|
55
|
+
- "pnpm test — 24/24"
|
|
56
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "cow role answer",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": ["markdown", "result"],
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"properties": {
|
|
8
|
+
"markdown": { "type": "string", "description": "Полный ответ роли в Markdown без блока cow-result" },
|
|
9
|
+
"result": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"required": ["status"],
|
|
12
|
+
"properties": {
|
|
13
|
+
"status": { "type": "string", "enum": ["готово", "утверждение", "заблокировано"] },
|
|
14
|
+
"blocker": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"properties": {
|
|
17
|
+
"category": { "type": "string", "enum": ["артефакт", "тесты", "реализация", "внешний", "пользователь", "нет"] },
|
|
18
|
+
"artifact": { "type": "string" },
|
|
19
|
+
"message": { "type": "string" }
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"size": { "type": "string", "enum": ["small", "normal", "large"] },
|
|
23
|
+
"skip_specs": { "type": "boolean" },
|
|
24
|
+
"complexity": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"properties": {
|
|
27
|
+
"implementation": { "type": "string", "enum": ["обычная", "высокая"] },
|
|
28
|
+
"review": { "type": "string", "enum": ["обычная", "высокая"] }
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"questions": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "priority": { "type": "string", "enum": ["P0", "P1", "P2"] }, "text": { "type": "string" } } } },
|
|
32
|
+
"findings": { "type": "array", "items": { "type": "object", "properties": { "level": { "type": "string", "enum": ["blocking", "non-blocking"] }, "file": { "type": "string" }, "text": { "type": "string" } } } },
|
|
33
|
+
"checks": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "purpose": { "type": "string" }, "result": { "type": "string", "enum": ["PASS", "FAIL", "PARTIAL", "NOT_RUN"] }, "evidence": { "type": "string" } } } },
|
|
34
|
+
"gaps": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "environment": { "type": "string" }, "oracle": { "type": "string" }, "risk": { "type": "string" } } } },
|
|
35
|
+
"dispositions": { "type": "array", "items": { "type": "object", "properties": { "item": { "type": "string" }, "disposition": { "type": "string", "enum": ["satisfied", "manual_gap_accepted", "change_required", "blocked"] }, "reason": { "type": "string" } } } },
|
|
36
|
+
"delivery_narrative": { "type": "object", "properties": { "title": { "type": "string" }, "delta": { "type": "string" }, "why": { "type": "string" }, "preserved": { "type": "string" }, "rollout": { "type": "string" }, "rollback": { "type": "string" } } },
|
|
37
|
+
"protected": { "type": "array", "items": { "type": "string" } },
|
|
38
|
+
"verified": { "type": "array", "items": { "type": "string" } }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
name: default
|
|
2
|
+
version: 1
|
|
3
|
+
artifacts:
|
|
4
|
+
- id: proposal
|
|
5
|
+
phase: propose
|
|
6
|
+
generates: proposal.md
|
|
7
|
+
template: proposal.md
|
|
8
|
+
description: "Предложение: зачем нужно изменение и его границы"
|
|
9
|
+
instruction: |
|
|
10
|
+
Создай предложение: объясни, ЗАЧЕМ нужно изменение, и определи его границы.
|
|
11
|
+
|
|
12
|
+
- перед записью прочитай request.md и evidence/research.md и используй их как принятый контекст;
|
|
13
|
+
- предложение — документ согласования проблемы, границ и состава изменения;
|
|
14
|
+
- если к разделу нет применимой информации, укажи «—».
|
|
15
|
+
|
|
16
|
+
Не включай: точные требования и сценарии; имена файлов, функций и модулей; технические решения; задачи реализации.
|
|
17
|
+
|
|
18
|
+
## Зачем — 1–3 предложения о проблеме, какую проблему решает, почему сейчас.
|
|
19
|
+
## Границы изменения — подразделы «Входит» и «Не входит».
|
|
20
|
+
## Функциональности — новые и изменяемые capability с идентификаторами (`cow spec list --json`); для новой capability придумай code в kebab-case. Если поведение продукта не меняется, укажи skip_specs: true в блоке cow-result.
|
|
21
|
+
## Внешнее влияние — затронутые потребители, контракты, зависимости; несовместимые изменения помечай **BREAKING**.
|
|
22
|
+
|
|
23
|
+
В блоке cow-result укажи size (small | normal | large) и complexity для реализации и ревью.
|
|
24
|
+
requires: []
|
|
25
|
+
|
|
26
|
+
- id: specs
|
|
27
|
+
phase: plan
|
|
28
|
+
generates: "specs/**/*.{yml,yaml,md}"
|
|
29
|
+
description: Дельты спецификаций
|
|
30
|
+
skippable: true
|
|
31
|
+
instruction: |
|
|
32
|
+
Создай дельты спецификаций: определи, ЧТО должна делать система после изменения.
|
|
33
|
+
|
|
34
|
+
- перед записью прочитай proposal.md и текущие спецификации затронутых capability (`cow spec show <code> --json`);
|
|
35
|
+
- описывай наблюдаемое поведение с точки зрения пользователя или внешнего контракта;
|
|
36
|
+
- каждое утверждение проверяемо одним тестом: одно поведение, одно действие, один наблюдаемый результат;
|
|
37
|
+
- формулировки бери из glossary.md;
|
|
38
|
+
- по одной дельте на capability из раздела «Функциональности» предложения.
|
|
39
|
+
|
|
40
|
+
Не включай: архитектурные решения, структуры данных, алгоритмы, задачи разработки.
|
|
41
|
+
|
|
42
|
+
Формат дельты задаёт адаптер спецификаций проекта, его инструкция приложена ниже.
|
|
43
|
+
requires: [proposal]
|
|
44
|
+
|
|
45
|
+
- id: design
|
|
46
|
+
phase: plan
|
|
47
|
+
generates: design.md
|
|
48
|
+
template: design.md
|
|
49
|
+
description: "Дизайн-документ: общая картина, контракты, решения, вопросы"
|
|
50
|
+
instruction: |
|
|
51
|
+
Создай дизайн-документ: покажи человеку общую картину и собери все решения в одном месте.
|
|
52
|
+
|
|
53
|
+
- перед записью прочитай proposal.md, дельты specs/ и architecture.md;
|
|
54
|
+
- раздел «Общая картина» описывает, как будет работать изменяемая функциональность целиком: путь пользователя, участвующие компоненты, что меняется в каждом; одна схема потока данных;
|
|
55
|
+
- раздел «Контракты» перечисляет изменения интерфейсов: API, события, схемы, структура UI и идентификаторы для тестов;
|
|
56
|
+
- раздел «Решения»: каждое с идентификатором D1, D2…, обоснованием, альтернативами и последствиями; флаг promote, если решение стоит сделать постоянным правилом;
|
|
57
|
+
- раздел «Вопросы, требующие решения»: таблица с приоритетом P0 (нельзя писать спецификации и тесты), P1 (меняет дизайн, но есть рекомендация), P2 (вкусовщина), вариантами, рекомендацией и влиянием; продублируй вопросы в блоке cow-result;
|
|
58
|
+
- раздел «Соответствие правилам»: каждое применимое правило из rules пакета и как дизайн его соблюдает;
|
|
59
|
+
- «Риски и компромиссы» в формате «риск → мера снижения»; «Стратегия проверки» по уровням из testing.md; «Миграция и откат», если применимо.
|
|
60
|
+
|
|
61
|
+
Не включай: мотивацию и границы из proposal; задачи; построчную реализацию.
|
|
62
|
+
requires: [proposal, specs]
|
|
63
|
+
|
|
64
|
+
- id: tasks
|
|
65
|
+
phase: plan
|
|
66
|
+
generates: tasks.md
|
|
67
|
+
template: tasks.md
|
|
68
|
+
description: Проверяемый список задач реализации
|
|
69
|
+
instruction: |
|
|
70
|
+
Создай список задач: разбей реализацию на проверяемые шаги.
|
|
71
|
+
|
|
72
|
+
- перед записью прочитай дельты specs/ и design.md;
|
|
73
|
+
- группируй задачи под нумерованными заголовками `##`;
|
|
74
|
+
- каждая задача — флажок `- [ ] X.Y Описание`; задачи без флажка не отслеживаются;
|
|
75
|
+
- упорядочи по зависимостям: подготовка → основная реализация → проверки;
|
|
76
|
+
- в каждой задаче ссылайся на утверждение спецификации или помечай её как техническую;
|
|
77
|
+
- добавь задачи на прогон проверок из testing.md (тесты, типы, линтеры).
|
|
78
|
+
|
|
79
|
+
Не включай: новые требования; пересмотр решений; ручные проверки и шаги, требующие человека; написание или изменение тестов (их пишет tester до реализации).
|
|
80
|
+
requires: [specs, design]
|
|
81
|
+
|
|
82
|
+
- id: coverage
|
|
83
|
+
phase: cover
|
|
84
|
+
generates: coverage.yaml
|
|
85
|
+
template: coverage.yaml
|
|
86
|
+
description: Связь сценариев с автотестами
|
|
87
|
+
instruction: |
|
|
88
|
+
Заполни coverage.yaml: для каждого утверждения из дельт specs/ укажи тесты (путь, полное имя, уровень) либо manual с причиной.
|
|
89
|
+
|
|
90
|
+
- имя теста строится по правилу из testing.md, чтобы отчёт тестов сопоставился с утверждением;
|
|
91
|
+
- уровень выбирай по testing.md: unit, component, module, e2e;
|
|
92
|
+
- утверждения без теста допустимы только с пометкой manual и причиной.
|
|
93
|
+
requires: [specs, design]
|
|
94
|
+
|
|
95
|
+
- id: test-plan
|
|
96
|
+
phase: cover
|
|
97
|
+
generates: test-plan.md
|
|
98
|
+
template: test-plan.md
|
|
99
|
+
optional: true
|
|
100
|
+
description: План ручного тестирования
|
|
101
|
+
instruction: |
|
|
102
|
+
Создай план ручной проверки только для утверждений с пометкой manual в coverage.yaml и короткий чеклист для человека перед мержем.
|
|
103
|
+
requires: [coverage]
|
|
104
|
+
|
|
105
|
+
apply:
|
|
106
|
+
requires: [tasks, coverage]
|
|
107
|
+
tracks: tasks.md
|
|
108
|
+
instruction: |
|
|
109
|
+
Выполни утверждённый план изменения.
|
|
110
|
+
|
|
111
|
+
- источник требований — дельты specs/, технических решений — design.md, шагов — tasks.md;
|
|
112
|
+
- выполняй задачи последовательно с учётом зависимостей;
|
|
113
|
+
- защищённые тестовые файлы не меняй; если тест неверен, верни блокер категории «тесты» с доказательством;
|
|
114
|
+
- отмечай задачу `- [x]` только после фактической реализации и прогона проверки;
|
|
115
|
+
- единственная допустимая правка артефактов — отметки в tasks.md;
|
|
116
|
+
- при невыполнимой задаче или расхождении с планом остановись и верни блокер категории «артефакт» с идентификатором артефакта.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Связь утверждений спецификации с автотестами.
|
|
2
|
+
# capability → группа → утверждение → тесты | manual
|
|
3
|
+
# uncomment and fill:
|
|
4
|
+
# checkout-page:
|
|
5
|
+
# "Пользователь может выбрать адрес доставки":
|
|
6
|
+
# "При нажатии «Выбрать адрес» открывается диалог выбора адреса на карте":
|
|
7
|
+
# tests:
|
|
8
|
+
# - level: e2e
|
|
9
|
+
# file: e2e/checkout/address.spec.ts
|
|
10
|
+
# name: "Страница оформления заказа › Пользователь может выбрать адрес доставки › При нажатии «Выбрать адрес» открывается диалог выбора адреса на карте"
|
|
11
|
+
# "Адрес сохраняется в профиле":
|
|
12
|
+
# manual: "Требует реального аккаунта с историей заказов"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
## Общая картина
|
|
2
|
+
|
|
3
|
+
<!-- путь пользователя от входа до результата, участвующие компоненты, что меняется в каждом; схема потока данных -->
|
|
4
|
+
|
|
5
|
+
## Контракты
|
|
6
|
+
|
|
7
|
+
<!-- API, события, схемы данных, структура UI и идентификаторы для тестов -->
|
|
8
|
+
|
|
9
|
+
## Решения
|
|
10
|
+
|
|
11
|
+
### D1. <!-- название решения -->
|
|
12
|
+
|
|
13
|
+
- Решение: <!-- выбранный подход -->
|
|
14
|
+
- Обоснование: <!-- почему -->
|
|
15
|
+
- Альтернативы: <!-- отклонённые варианты и почему -->
|
|
16
|
+
- Последствия: <!-- что меняется -->
|
|
17
|
+
- promote: false
|
|
18
|
+
|
|
19
|
+
## Вопросы, требующие решения
|
|
20
|
+
|
|
21
|
+
| ID | Приоритет | Вопрос | Варианты | Рекомендация | Влияние |
|
|
22
|
+
|---|---|---|---|---|---|
|
|
23
|
+
<!-- | Q1 | P1 | … | A / B | A | … | -->
|
|
24
|
+
|
|
25
|
+
## Соответствие правилам
|
|
26
|
+
|
|
27
|
+
<!-- ADR-xxxx: как соблюдается -->
|
|
28
|
+
|
|
29
|
+
## Риски и компромиссы
|
|
30
|
+
|
|
31
|
+
<!-- риск → мера снижения -->
|
|
32
|
+
|
|
33
|
+
## Стратегия проверки
|
|
34
|
+
|
|
35
|
+
<!-- что и на каком уровне проверяется -->
|
|
36
|
+
|
|
37
|
+
## Миграция и откат
|
|
38
|
+
|
|
39
|
+
<!-- шаги применения и возврата или «—» -->
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
## Зачем
|
|
2
|
+
|
|
3
|
+
<!-- проблема или возможность, почему сейчас -->
|
|
4
|
+
|
|
5
|
+
## Границы изменения
|
|
6
|
+
|
|
7
|
+
### Входит
|
|
8
|
+
|
|
9
|
+
<!-- результаты в границах изменения -->
|
|
10
|
+
|
|
11
|
+
### Не входит
|
|
12
|
+
|
|
13
|
+
<!-- смежные результаты, которые остаются за пределами -->
|
|
14
|
+
|
|
15
|
+
## Функциональности
|
|
16
|
+
|
|
17
|
+
### Новые
|
|
18
|
+
|
|
19
|
+
<!-- - `<code>`: краткое описание -->
|
|
20
|
+
|
|
21
|
+
### Изменяемые
|
|
22
|
+
|
|
23
|
+
<!-- - `<code>`: что меняется в поведении -->
|
|
24
|
+
|
|
25
|
+
## Внешнее влияние
|
|
26
|
+
|
|
27
|
+
<!-- затронутые потребители, контракты, зависимости; **BREAKING** для несовместимых -->
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
## 1. <!-- Название группы задач -->
|
|
2
|
+
|
|
3
|
+
- [ ] 1.1 <!-- Описание задачи (утверждение спецификации или «техническая») -->
|
|
4
|
+
- [ ] 1.2 <!-- Описание задачи -->
|
|
5
|
+
|
|
6
|
+
## 2. Проверки
|
|
7
|
+
|
|
8
|
+
- [ ] 2.1 <!-- Прогнать тесты из coverage.yaml -->
|
|
9
|
+
- [ ] 2.2 <!-- Прогнать проверки из testing.md -->
|
package/bin/cow.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { loadRoleText } from '../../../core/roles.js';
|
|
3
|
+
import { ROLES } from '../../../core/phases.js';
|
|
4
|
+
import { writeText } from '../../../core/paths.js';
|
|
5
|
+
const AGENT_ROLES = ['researcher', 'planner', 'tester', 'implementer', 'reviewer', 'verifier', 'challenger'];
|
|
6
|
+
const TOOLS = {
|
|
7
|
+
researcher: 'Read, Grep, Glob, Bash',
|
|
8
|
+
planner: 'Read, Write, Edit, Grep, Glob, Bash',
|
|
9
|
+
challenger: 'Read, Grep, Glob, Bash',
|
|
10
|
+
tester: 'Read, Write, Edit, Grep, Glob, Bash',
|
|
11
|
+
implementer: 'Read, Write, Edit, Grep, Glob, Bash',
|
|
12
|
+
reviewer: 'Read, Grep, Glob, Bash',
|
|
13
|
+
verifier: 'Read, Grep, Glob, Bash',
|
|
14
|
+
distiller: 'Read, Write, Edit, Grep, Glob',
|
|
15
|
+
};
|
|
16
|
+
const DESCRIPTIONS = {
|
|
17
|
+
researcher: 'Исследователь cow: Evidence Pack по изменению. Вызывается только скиллом cow-run.',
|
|
18
|
+
planner: 'Планировщик cow: proposal, дельты спецификаций, design, tasks. Вызывается только скиллом cow-run.',
|
|
19
|
+
challenger: 'Аудитор плана cow. Вызывается только скиллом cow-run.',
|
|
20
|
+
tester: 'Тестировщик cow: тесты по сценариям до реализации. Вызывается только скиллом cow-run.',
|
|
21
|
+
implementer: 'Реализатор cow: выполняет tasks.md, не трогая защищённые тесты. Вызывается только скиллом cow-run.',
|
|
22
|
+
reviewer: 'Ревьюер cow: тесты против спецификаций, код против артефактов. Вызывается только скиллом cow-run.',
|
|
23
|
+
verifier: 'Верификатор cow: полнота, корректность, согласованность. Вызывается только скиллом cow-run.',
|
|
24
|
+
distiller: 'Дистиллятор знаний cow.',
|
|
25
|
+
};
|
|
26
|
+
/** Материалы для Claude Code: тонкий скилл-оркестратор и по агенту на роль (docs/design.md, раздел 11). */
|
|
27
|
+
export function installClaudeMaterials(root) {
|
|
28
|
+
const written = [];
|
|
29
|
+
const skill = path.join(root, '.claude', 'skills', 'cow-run', 'SKILL.md');
|
|
30
|
+
writeText(skill, RUN_SKILL);
|
|
31
|
+
written.push(skill);
|
|
32
|
+
const approve = path.join(root, '.claude', 'skills', 'cow-approve', 'SKILL.md');
|
|
33
|
+
writeText(approve, APPROVE_SKILL);
|
|
34
|
+
written.push(approve);
|
|
35
|
+
for (const role of ROLES) {
|
|
36
|
+
if (!AGENT_ROLES.includes(role))
|
|
37
|
+
continue;
|
|
38
|
+
const file = path.join(root, '.claude', 'agents', `cow-${role}.md`);
|
|
39
|
+
writeText(file, agentFile(root, role));
|
|
40
|
+
written.push(file);
|
|
41
|
+
}
|
|
42
|
+
return written;
|
|
43
|
+
}
|
|
44
|
+
function agentFile(root, role) {
|
|
45
|
+
const body = loadRoleText(root, role);
|
|
46
|
+
return `---
|
|
47
|
+
name: cow-${role}
|
|
48
|
+
description: ${DESCRIPTIONS[role]}
|
|
49
|
+
tools: ${TOOLS[role]}
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
Ты выполняешь роль ${role} инструмента cow. Во входном сообщении путь к JSON-пакету (\`packet\`). Прочитай пакет целиком: там цель, допущенные файлы, ограничения, правила проекта, инструкции к артефактам и путь \`resultFile\`.
|
|
53
|
+
|
|
54
|
+
Порядок работы: прочитай пакет → выполни этапы роли ниже → запиши полный ответ (Markdown и завершающий блок \`# cow-result\`) в файл \`resultFile\` из пакета → в сообщении верни только две строки: путь к resultFile и статус.
|
|
55
|
+
|
|
56
|
+
${body.trim()}
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
59
|
+
const RUN_SKILL = `---
|
|
60
|
+
name: cow-run
|
|
61
|
+
description: Выполнить следующий шаг изменения cow — запросить пакет у CLI, выполнить роль субагентом и сдать отчёт. Используй, когда пользователь просит продвинуть изменение, запустить cow, выполнить следующий шаг.
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
# cow-run
|
|
65
|
+
|
|
66
|
+
Ты оркестратор: не исследуешь проект и не решаешь задачу сам. Роли выполняют субагенты \`cow-<role>\`.
|
|
67
|
+
|
|
68
|
+
1. Определи изменение: из аргумента, из контекста или \`cow change list --json\`. Если активных изменений несколько и не ясно, какое брать, спроси пользователя.
|
|
69
|
+
2. Запроси следующий шаг: \`cow next --change <id> --json\`.
|
|
70
|
+
3. По полю \`kind\` ответа:
|
|
71
|
+
- \`role\`: запусти субагента \`cow-<role>\` с сообщением: \`packet: <path из поля packetFile>\`. Дождись ответа. Затем сдай отчёт: \`cow report --change <id> --role <role> --file <resultFile> --json\`. Если CLI вернул ошибки (например, не создан артефакт или изменены защищённые файлы), передай их тому же субагенту одним сообщением и повтори отчёт; не больше двух повторов.
|
|
72
|
+
- \`gate\`: покажи пользователю, что нужно посмотреть (пути артефактов из ответа), и спроси: «Утвердить <gate>? Ответьте «да» или замечанием». «да» → \`cow approve <gate> --change <id> --by <user>\`; замечание → \`cow reject <gate> --change <id> --comment "<текст>"\`. Затем вернись к шагу 2.
|
|
73
|
+
- \`deliver\`: покажи чеклист \`cow deliver --change <id> --check\` и предложи доставку \`cow deliver --change <id>\` (архив, коммит, push, пул-реквест). Не запускай доставку без просьбы пользователя.
|
|
74
|
+
- \`wait\`: покажи статус и блокер, спроси пользователя, что делать.
|
|
75
|
+
- \`done\`: сообщи об окончании.
|
|
76
|
+
4. Повторяй шаги 2–3, пока не встретишь gate, wait, deliver или done. Не запускай субагентов параллельно.
|
|
77
|
+
|
|
78
|
+
Полезные команды: \`cow status --change <id>\` (состояние, change-set, привязка ревью), \`cow changeset show\` (запечатанный диф и дрейф), \`cow coverage --report jest=<path>\` (покрытие утверждений тестами), \`cow change resume --returns N\` (продолжить после parked).
|
|
79
|
+
|
|
80
|
+
Запрещено: переформулировать задачу пользователя, редактировать артефакты, вызывать роли напрямую в обход пакета, менять код.
|
|
81
|
+
`;
|
|
82
|
+
const APPROVE_SKILL = `---
|
|
83
|
+
name: cow-approve
|
|
84
|
+
description: Утвердить или отклонить гейт изменения cow (proposal, plan, tests). Используй, когда пользователь говорит «утверждаю», «одобряю план», «отклоняю с замечанием».
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
# cow-approve
|
|
88
|
+
|
|
89
|
+
1. Определи изменение и гейт: \`cow status --change <id> --json\` показывает ожидающий гейт.
|
|
90
|
+
2. Утверждение: \`cow approve <gate> --change <id> --by <user> [--comment "..."] [--answer Q1=B]\`.
|
|
91
|
+
3. Отклонение: \`cow reject <gate> --change <id> --by <user> --comment "<замечание>"\`.
|
|
92
|
+
4. Покажи результат и предложи продолжить скиллом cow-run.
|
|
93
|
+
`;
|
|
94
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/adapters/host/claude/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,KAAK,EAAa,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,WAAW,GAAW,CAAC,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;AAErH,MAAM,KAAK,GAAyB;IAClC,UAAU,EAAE,wBAAwB;IACpC,OAAO,EAAE,qCAAqC;IAC9C,UAAU,EAAE,wBAAwB;IACpC,MAAM,EAAE,qCAAqC;IAC7C,WAAW,EAAE,qCAAqC;IAClD,QAAQ,EAAE,wBAAwB;IAClC,QAAQ,EAAE,wBAAwB;IAClC,SAAS,EAAE,+BAA+B;CAC3C,CAAC;AAEF,MAAM,YAAY,GAAyB;IACzC,UAAU,EAAE,mFAAmF;IAC/F,OAAO,EAAE,mGAAmG;IAC5G,UAAU,EAAE,uDAAuD;IACnE,MAAM,EAAE,uFAAuF;IAC/F,WAAW,EAAE,oGAAoG;IACjH,QAAQ,EAAE,mGAAmG;IAC7G,QAAQ,EAAE,6FAA6F;IACvG,SAAS,EAAE,yBAAyB;CACrC,CAAC;AAEF,2GAA2G;AAC3G,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IAC1E,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAC5B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC;IAChF,SAAS,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAClC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,SAAS;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,IAAI,KAAK,CAAC,CAAC;QACpE,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,SAAS,CAAC,IAAY,EAAE,IAAU;IACzC,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACtC,OAAO;YACG,IAAI;eACD,YAAY,CAAC,IAAI,CAAC;SACxB,KAAK,CAAC,IAAI,CAAC;;;qBAGC,IAAI;;;;EAIvB,IAAI,CAAC,IAAI,EAAE;CACZ,CAAC;AACF,CAAC;AAED,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;CAsBjB,CAAC;AAEF,MAAM,aAAa,GAAG;;;;;;;;;;;CAWrB,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { CowError } from '../../core/errors.js';
|
|
3
|
+
/** Локальные операции git, общие для адаптеров GitHub и local. */
|
|
4
|
+
export class GitOps {
|
|
5
|
+
root;
|
|
6
|
+
constructor(root) {
|
|
7
|
+
this.root = root;
|
|
8
|
+
}
|
|
9
|
+
run(args, opts = {}) {
|
|
10
|
+
try {
|
|
11
|
+
return execFileSync('git', args, { cwd: this.root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
12
|
+
}
|
|
13
|
+
catch (e) {
|
|
14
|
+
if (opts.allowFail)
|
|
15
|
+
return '';
|
|
16
|
+
const err = e;
|
|
17
|
+
throw new CowError('GIT_FAILED', `git ${args.join(' ')}: ${(err.stderr ?? err.message).trim()}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
currentBranch() {
|
|
21
|
+
return this.run(['rev-parse', '--abbrev-ref', 'HEAD']).trim();
|
|
22
|
+
}
|
|
23
|
+
branchExists(name) {
|
|
24
|
+
return this.run(['rev-parse', '--verify', '--quiet', `refs/heads/${name}`], { allowFail: true }).trim().length > 0;
|
|
25
|
+
}
|
|
26
|
+
ensureBranch(name, from) {
|
|
27
|
+
if (this.currentBranch() === name)
|
|
28
|
+
return;
|
|
29
|
+
if (this.branchExists(name))
|
|
30
|
+
this.run(['checkout', name]);
|
|
31
|
+
else
|
|
32
|
+
this.run(['checkout', '-b', name, from]);
|
|
33
|
+
}
|
|
34
|
+
findCommitByIntent(intentKey) {
|
|
35
|
+
const out = this.run(['log', '--all', '--format=%H', `--grep=Cow-Intent-Key: ${intentKey}`], { allowFail: true }).trim();
|
|
36
|
+
return out ? out.split('\n')[0] : null;
|
|
37
|
+
}
|
|
38
|
+
commitAll(message, intentKey) {
|
|
39
|
+
this.run(['add', '-A']);
|
|
40
|
+
const staged = this.run(['diff', '--cached', '--name-only']).trim();
|
|
41
|
+
if (!staged) {
|
|
42
|
+
const existing = this.findCommitByIntent(intentKey);
|
|
43
|
+
if (existing)
|
|
44
|
+
return existing;
|
|
45
|
+
throw new CowError('NOTHING_TO_COMMIT', 'Нет изменений для коммита.');
|
|
46
|
+
}
|
|
47
|
+
const full = `${message.trimEnd()}\n\nCow-Intent-Key: ${intentKey}\n`;
|
|
48
|
+
this.run(['commit', '-q', '-F', '-'], { allowFail: false });
|
|
49
|
+
void full;
|
|
50
|
+
return this.run(['rev-parse', 'HEAD']).trim();
|
|
51
|
+
}
|
|
52
|
+
commitAllWithMessage(message, intentKey) {
|
|
53
|
+
this.run(['add', '-A']);
|
|
54
|
+
const staged = this.run(['diff', '--cached', '--name-only']).trim();
|
|
55
|
+
if (!staged) {
|
|
56
|
+
const existing = this.findCommitByIntent(intentKey);
|
|
57
|
+
if (existing)
|
|
58
|
+
return existing;
|
|
59
|
+
throw new CowError('NOTHING_TO_COMMIT', 'Нет изменений для коммита.');
|
|
60
|
+
}
|
|
61
|
+
const full = `${message.trimEnd()}\n\nCow-Intent-Key: ${intentKey}\n`;
|
|
62
|
+
execFileSync('git', ['commit', '-q', '-F', '-'], { cwd: this.root, input: full, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
63
|
+
return this.run(['rev-parse', 'HEAD']).trim();
|
|
64
|
+
}
|
|
65
|
+
hasRemote(name = 'origin') {
|
|
66
|
+
return this.run(['remote'], { allowFail: true }).split('\n').map((l) => l.trim()).includes(name);
|
|
67
|
+
}
|
|
68
|
+
push(branch, remote = 'origin') {
|
|
69
|
+
this.run(['push', '-u', remote, branch]);
|
|
70
|
+
}
|
|
71
|
+
remoteUrl(remote = 'origin') {
|
|
72
|
+
const out = this.run(['remote', 'get-url', remote], { allowFail: true }).trim();
|
|
73
|
+
return out || null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** owner/repo из URL GitHub: https://github.com/o/r(.git) или git@github.com:o/r(.git). */
|
|
77
|
+
export function parseGitHubRemote(url) {
|
|
78
|
+
const m = url.match(/github\.com[:/]([^/]+)\/([^/]+?)(?:\.git)?\/?$/);
|
|
79
|
+
return m ? { owner: m[1], repo: m[2] } : null;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=git.js.map
|