@rt-tools/agent-kit 0.8.3 → 0.9.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 +12 -0
- package/assets/checks/board.github.mjs +48 -1
- package/assets/checks/check-board.github.mjs +84 -1
- package/assets/checks/check-lib-layers.mjs +13 -524
- package/assets/checks/check-specs.mjs +11 -782
- package/assets/checks/check-styles.mjs +185 -15
- package/assets/checks/lib-boundaries.mjs +143 -0
- package/assets/checks/lib-common.mjs +149 -0
- package/assets/checks/lib-domains.mjs +205 -0
- package/assets/checks/lib-manifests.mjs +60 -0
- package/assets/checks/lib-reexports.mjs +101 -0
- package/assets/checks/rt-kit-checks.config.mjs +17 -0
- package/assets/checks/spec-anchors.mjs +297 -0
- package/assets/checks/spec-common.mjs +222 -0
- package/assets/checks/spec-contract.mjs +152 -0
- package/assets/checks/spec-scenarios.mjs +201 -0
- package/assets/defaults/project.sh +8 -0
- package/assets/hooks/git-guard-push-tests.sh +8 -4
- package/assets/hooks/skill-gate.sh +1 -1
- package/assets/hooks/sql-guard-parse.sh +187 -0
- package/assets/hooks/sql-guard-request.sh +117 -0
- package/assets/hooks/sql-guard-target.sh +134 -0
- package/assets/hooks/sql-guard-write.sh +212 -0
- package/assets/hooks/sql-guard.sh +26 -596
- package/assets/hooks/waiting-turn-guard.sh +42 -13
- package/assets/laws/delivery.md +7 -0
- package/assets/laws/work-conduct.md +9 -0
- package/assets/patterns/admin-lists-screen.md +25 -14
- package/assets/patterns/admin-nav-item.md +1 -1
- package/assets/patterns/component-structure-new.md +1 -1
- package/assets/patterns/entity-aside.md +4 -2
- package/assets/patterns/observability-record.md +9 -0
- package/assets/patterns/shared-code-new.md +2 -2
- package/assets/patterns/task-flow-close.md +7 -1
- package/assets/rules/git-workflow.azure.md +7 -0
- package/assets/rules/git-workflow.github.md +41 -0
- package/assets/rules/git-workflow.gitlab.md +7 -0
- package/assets/rules/lib-layers.md +4 -0
- package/assets/rules/lists.md +10 -10
- package/assets/rules/shared-code.md +1 -1
- package/assets/rules/task-flow.md +47 -7
- package/assets/rules/testing.md +31 -0
- package/assets/rules/typescript-conventions.md +7 -0
- package/assets/skills/agent-kit.md +36 -0
- package/assets/templates/proposal.md +21 -0
- package/bin/agent-kit.d.ts.map +1 -1
- package/bin/agent-kit.js +115 -87
- package/bin/agent-kit.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -1
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/lib/argv.d.ts.map +1 -1
- package/lib/argv.js +6 -4
- package/lib/argv.js.map +1 -1
- package/lib/assets.d.ts.map +1 -1
- package/lib/assets.js +2 -1
- package/lib/assets.js.map +1 -1
- package/lib/cargo.d.ts +20 -0
- package/lib/cargo.d.ts.map +1 -1
- package/lib/cargo.js.map +1 -1
- package/lib/cascade.d.ts +55 -0
- package/lib/cascade.d.ts.map +1 -0
- package/lib/cascade.js +131 -0
- package/lib/cascade.js.map +1 -0
- package/lib/catalog.d.ts +0 -75
- package/lib/catalog.d.ts.map +1 -1
- package/lib/catalog.js +44 -127
- package/lib/catalog.js.map +1 -1
- package/lib/commands.d.ts.map +1 -1
- package/lib/commands.js +152 -85
- package/lib/commands.js.map +1 -1
- package/lib/companion.d.ts.map +1 -1
- package/lib/companion.js +5 -5
- package/lib/companion.js.map +1 -1
- package/lib/config.d.ts +2 -0
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +7 -5
- package/lib/config.js.map +1 -1
- package/lib/enroll.d.ts +56 -0
- package/lib/enroll.d.ts.map +1 -0
- package/lib/enroll.js +123 -0
- package/lib/enroll.js.map +1 -0
- package/lib/freshness.d.ts.map +1 -1
- package/lib/freshness.js +31 -17
- package/lib/freshness.js.map +1 -1
- package/lib/hooks-map.d.ts +30 -0
- package/lib/hooks-map.d.ts.map +1 -1
- package/lib/hooks-map.js +80 -18
- package/lib/hooks-map.js.map +1 -1
- package/lib/integrity.d.ts +1 -2
- package/lib/integrity.d.ts.map +1 -1
- package/lib/integrity.js +0 -1
- package/lib/integrity.js.map +1 -1
- package/lib/observations.d.ts.map +1 -1
- package/lib/observations.js +25 -12
- package/lib/observations.js.map +1 -1
- package/lib/order.d.ts +10 -0
- package/lib/order.d.ts.map +1 -0
- package/lib/order.js +14 -0
- package/lib/order.js.map +1 -0
- package/lib/picker.d.ts.map +1 -1
- package/lib/picker.js +8 -2
- package/lib/picker.js.map +1 -1
- package/lib/plan.js +1 -1
- package/lib/plan.js.map +1 -1
- package/lib/proposals.d.ts.map +1 -1
- package/lib/proposals.js +25 -8
- package/lib/proposals.js.map +1 -1
- package/lib/sections.js +1 -1
- package/lib/sections.js.map +1 -1
- package/lib/ship.d.ts.map +1 -1
- package/lib/ship.js +9 -1
- package/lib/ship.js.map +1 -1
- package/lib/shipment.d.ts.map +1 -1
- package/lib/shipment.js +14 -10
- package/lib/shipment.js.map +1 -1
- package/lib/snapshot.d.ts.map +1 -1
- package/lib/snapshot.js +2 -1
- package/lib/snapshot.js.map +1 -1
- package/lib/stamp.js +1 -1
- package/lib/stamp.js.map +1 -1
- package/lib/sync.d.ts +12 -2
- package/lib/sync.d.ts.map +1 -1
- package/lib/sync.js +11 -10
- package/lib/sync.js.map +1 -1
- package/lib/vars.d.ts.map +1 -1
- package/lib/vars.js +2 -3
- package/lib/vars.js.map +1 -1
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.9.0.tgz +0 -0
- package/rt-tools-agent-kit-0.8.3.tgz +0 -0
|
@@ -143,16 +143,37 @@ flowchart TD
|
|
|
143
143
|
Открытый PR при этом читается как приглашение влить — поэтому незаконченная работа идёт
|
|
144
144
|
черновиком, и владельцу не приходится спрашивать, кончилась ли она. Черновик снимается тем
|
|
145
145
|
ходом, которым исполнитель говорит, что решение готово.
|
|
146
|
+
- **Открытый PR остановкой не является.** Отданное на разбор ждёт владельца, а не исполнителя:
|
|
147
|
+
следующая задача берётся тем же движением, которым предыдущая ушла на разбор. Заход,
|
|
148
|
+
закрытый на готовой задаче, стоил владельцу целого захода на то, чтобы вернуть работу в
|
|
149
|
+
движение, — при том что порядок был назначен и лежал записанным в замысле эпика.
|
|
150
|
+
- **Владельцу не предлагается выбор, чем заняться дальше, пока эпик не кончился.** Меню при
|
|
151
|
+
назначенном порядке — это просьба назначить его заново. Если работы в эпике не осталось, так
|
|
152
|
+
и говорится: эпик кончился, — а не «чем займёмся».
|
|
153
|
+
- **Гард замысла — нижняя граница требования, а не его предел.** Он требует папку только под
|
|
154
|
+
правку кода приложения, и работа, которая туда не доходит, проходит мимо него — но папку
|
|
155
|
+
заводит всё равно: статья правила говорит «под любую работу, без исключений». Прочитанный
|
|
156
|
+
как признак, гард становится разрешением работать без замысла везде, куда он не смотрит.
|
|
157
|
+
- **Ход, сообщающий владельцу о чужом шаге, называет своё следующее действие и начинает его.**
|
|
158
|
+
Прогон, разбор владельцем и слияние идут без исполнителя и быстрее от взгляда не становятся,
|
|
159
|
+
поэтому сообщение о них — не работа, а сводка. Ход, кончившийся такой сводкой, владелец
|
|
160
|
+
читает как работу: она полна, в ней названы номера и состояния, и пустоты за ней не видно.
|
|
161
|
+
Открытый PR, красный прогон и ожидание слова владельца — случаи одного и того же, и правило
|
|
162
|
+
у них одно: о чужом шаге говорят вместе с начатым своим, а не вместо него. За один заход это
|
|
163
|
+
было нарушено четырежды, и готовая работа простояла в невлитом PR почти три часа, пока
|
|
164
|
+
исполнитель отвечал владельцу сводками о её состоянии.
|
|
146
165
|
- **Пока PR ждёт разбора, исполнитель берёт следующую задачу.** Ожидание чужого шага заходом
|
|
147
166
|
не занимают: работа уходит на разбор, и тем же движением берётся следующая. Готовым к
|
|
148
167
|
слиянию прежний PR становится не сам — его доводит до готовности исполнитель, вернувшись к
|
|
149
168
|
нему тем же ходом, которым прочитал конец прогона.
|
|
150
|
-
-
|
|
151
|
-
|
|
152
|
-
ветки,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
169
|
+
- **Ход о чужом шаге стережёт гард ожидания, а не память исполнителя.** Он отбивает завершение
|
|
170
|
+
хода, в котором о чужом шаге сказано, а по следующей задаче не сделано ни одного действия —
|
|
171
|
+
ни заведения задачи, ни ветки, ни папки, ни перевода колонки. Чужой шаг он узнаёт по двум
|
|
172
|
+
признакам: в ходе открыт PR либо в ходе прочитан красный прогон. Оба берутся из самого хода:
|
|
173
|
+
спросить хостинг было бы точнее, но сетевой вызов на завершении хода падает вместе со связью
|
|
174
|
+
и отбивал бы работу вместо промаха, а вывод команды о прогоне в записи хода уже лежит. Слова
|
|
175
|
+
«беру следующую задачу» гард действием не считает — ровно потому, что их и произносят вместо
|
|
176
|
+
неё.
|
|
156
177
|
- **Конец прогона узнаётся возвратом фоновой команды, а не взглядом на страницу.** Ожидание,
|
|
157
178
|
запущенное в фоне отдельным ходом, возвращает исполнителя к PR само; до тех пор ход занят
|
|
158
179
|
следующей задачей. Взгляд на страницу этого не даёт: он либо повторяется вхолостую, либо не
|
|
@@ -171,7 +192,16 @@ flowchart TD
|
|
|
171
192
|
- **Действия, которые исполнитель не делает без слова владельца, перечислены в компаньоне
|
|
172
193
|
правила.** Список у каждого дерева свой — пакет знает только требование, чтобы список был
|
|
173
194
|
назван. Не названный, он выводится из общих слов, и «делай, что нужно по плану» становится
|
|
174
|
-
разрешением на пуш и правку общих документов заодно с коммитом.
|
|
195
|
+
разрешением на пуш и правку общих документов заодно с коммитом. Оценка «это безопасно» списка
|
|
196
|
+
не заменяет: её назначает тот, кому она в эту минуту удобна, и она плывёт. За один заход одна
|
|
197
|
+
и та же команда была сначала слишком опасной, чтобы её позвать, а через два хода —
|
|
198
|
+
достаточно безопасной, чтобы позвать без спроса.
|
|
199
|
+
- **У отказа от необратимого действия есть безопасная часть, и она делается.** Требование
|
|
200
|
+
спросить владельца относится к действию, а не к ходу: работа, у которой отделима часть без
|
|
201
|
+
последствий, делится, а не откладывается целиком. Список вариантов, поданный вместо работы,
|
|
202
|
+
читается как работа — тем полнее, чем аккуратнее он составлен: он пронумерован, в нём названы
|
|
203
|
+
цифры, и именно поэтому пустота хода за ним не видна. Владельцу называется, что уже сделано и
|
|
204
|
+
что осталось за его словом, — а не выбор из вариантов вместо и того и другого.
|
|
175
205
|
- **Ход, в котором исполнитель признал промах, не заканчивается, пока записи о происшествии
|
|
176
206
|
нет.** Отбивает гард происшествия — на завершении хода: к моменту признания промах уже
|
|
177
207
|
случился, и ловить раньше нечего. Признание ловится набором образцов, а не пониманием смысла;
|
|
@@ -202,6 +232,16 @@ flowchart TD
|
|
|
202
232
|
говорит, что эпик есть, но порядка задач не держит; папка задачи держала бы его ровно до
|
|
203
233
|
слияния первой из них. Каталог для замысла называет компаньон правила: у пакета своего пути
|
|
204
234
|
нет, а замысел, положенный каждым заходом заново, теряет решения предыдущих.
|
|
235
|
+
- **Сборка по образцу начинается с чтения самого образца, а не пересказа о нём.** Пересказ
|
|
236
|
+
лежит в разборе просьбы и в замысле эпика; читаются они оба, но образцом не считаются. Часть
|
|
237
|
+
образца, которую работа повторяет, открывается целиком — обходом каталогов, а не одним файлом,
|
|
238
|
+
за которым пришли. Расхождение, не найденное так, находит владелец на приёмке целой работой.
|
|
239
|
+
- **Путь к образцу лежит вне дерева и приходит в заход хуком запуска сессии.** Имя чужого
|
|
240
|
+
дерева в файлы репозитория не пишется, поэтому ни замысел эпика, ни разбор просьбы его не
|
|
241
|
+
держат: там законна ссылка без имени — «путь к образцу лежит вне дерева». Каталог для записи
|
|
242
|
+
— тот же, где лежит передача захода; называет его компаньон правила. Не записанный так,
|
|
243
|
+
образец теряется на первой же чистке контекста, и следующий заход собирает по памяти
|
|
244
|
+
предыдущего.
|
|
205
245
|
- **Закрытая работа разбирается правилами, и это шаг закрытия, а не отдельная просьба.** Что
|
|
206
246
|
грузилось, что помогло и чего не хватило, видно только тому заходу, который работу вёл; через
|
|
207
247
|
сутки этого нет ни у кого. Разбор кончается правкой слоя правил или предложением наружу —
|
package/assets/rules/testing.md
CHANGED
|
@@ -126,6 +126,19 @@ flowchart TD
|
|
|
126
126
|
сценария в заголовке стоит — сценарий числится покрытым, а что именно утверждается, не
|
|
127
127
|
спрашивает никто. Тело читается вместе с заголовком: обещание в заголовке и утверждение в
|
|
128
128
|
теле — два разных текста, и расходятся они молча.
|
|
129
|
+
- **Растр браузера называется явно, иначе кадр не сходится сам с собой.** Профиль цвета, взятый
|
|
130
|
+
у дисплея машины, ускоритель, считающий растр, и дорисовка кусками — каждый из трёх двигает
|
|
131
|
+
цвет на единицу-другую по каналу, и видно это только там, где смешение стоит на границе
|
|
132
|
+
округления: на сглаженных уголках тёмной темы. Ожидание вставшего кадра тут не помогает —
|
|
133
|
+
страница нарисована, а нарисована она каждый раз чуть иначе; расхождение гуляет по кадру,
|
|
134
|
+
приходит примерно раз в четыре прогона и на светлых экранах не показывается вовсе, поэтому
|
|
135
|
+
читается случайным. Все три называются доводами браузеру при запуске и становятся частью
|
|
136
|
+
эталона.
|
|
137
|
+
- **Маска закрывает содержимое, но не ширину.** Узел под маской занимает своё место в раскладке
|
|
138
|
+
по-прежнему, и плывущее значение внутри него двигает соседей мимо маски: кадр списка уезжал на
|
|
139
|
+
пиксель целиком, включая столбцы, где не менялось ничего. Там, где размер узла считается по
|
|
140
|
+
содержимому — ячейка таблицы, надпись, растягивающая кнопку, — плывущее лечится в данных
|
|
141
|
+
стенда постоянным значением, и тогда маска не нужна вовсе.
|
|
129
142
|
|
|
130
143
|
## Чего из закона здесь нет
|
|
131
144
|
|
|
@@ -186,3 +199,21 @@ flowchart TD
|
|
|
186
199
|
соревнуется за машину с тем, что снимают, и делает исход прогона зависящим от того, чем занят
|
|
187
200
|
сосед. Нагрузка, которую задание создаёт себе само — соседняя витрина, только что законченная
|
|
188
201
|
сборка, — ничем не отличается от чужой.
|
|
202
|
+
- **Свой стенд снимается перед тем, как звать набор.** Прогон переиспользует поднятое на его
|
|
203
|
+
портах, и стенд, оставленный для замера, отдаёт ему чужую сборку с чужими данными. Красное при
|
|
204
|
+
этом приходит не строкой про занятый порт, а десятком спек про экраны — то есть выглядит
|
|
205
|
+
дефектом правки: за один заход так покраснели сначала шесть новых тестов, потом гейт пуша, и
|
|
206
|
+
оба раза причиной был свой же стенд. Разобранный занятый порт эту сторону не закрывает: он про
|
|
207
|
+
чужой стенд, а этот — про свой.
|
|
208
|
+
- **Разбор упавшего кадра начинается с чисел, а не с картинки расхождения.** Доля площади
|
|
209
|
+
говорит, сколько разошлось, и молчит о том, что именно: сдвиг всего кадра на пиксель,
|
|
210
|
+
переставленные строки и рябь на сглаженных уголках выглядят на картинке одинаково — «стало
|
|
211
|
+
другим». Читаются координаты разошедшихся точек и величина расхождения по каналу: сдвинутые
|
|
212
|
+
границы блоков — это раскладка, разошедшийся текст при неподвижных границах — это данные,
|
|
213
|
+
единица-две по каналу на кривых краях — это цвет. Три расхождения одного набора разобрались
|
|
214
|
+
ровно так, и ни одно из трёх не оказалось дефектом экрана.
|
|
215
|
+
- **Ожидаемое значение теста не берётся из кода, который тест проверяет.** Вывезенное из
|
|
216
|
+
проверяемой либы, оно делает тест зелёным при любом значении: «колесо показывает пять строк»
|
|
217
|
+
сходится и тогда, когда строк стало три. Ожидаемое пишется числом в самой спеке рядом с
|
|
218
|
+
проверкой, а общий модуль сквозных спек держит приёмы — открыть, дождаться, снять со
|
|
219
|
+
страницы, — но не то, что от страницы ожидается.
|
|
@@ -59,6 +59,13 @@ flowchart TD
|
|
|
59
59
|
остального. Для `.ts` это держит правило линтера; файлы обвязки — сценарии и скрипты — до
|
|
60
60
|
него не доходят и судятся отдельной проверкой дерева. Накопленное к дню включения
|
|
61
61
|
перечислено поимённо, и строка оттуда снимается вместе с делением своего файла.
|
|
62
|
+
- **Значение из закрытого набора приходит перечислением `E<Имя>`, а не строкой или числом в
|
|
63
|
+
месте использования.** Ключ переводимого поля, имя вкладки, слот обложки, вид записи — всё
|
|
64
|
+
это наборы: их называют форма, стор, разметка и сравнение черновиков, и написанное на месте
|
|
65
|
+
значение ни находится по дереву, ни правится разом. Перечисление живёт в слое `util` того
|
|
66
|
+
домена, чей это набор, а общее нескольким приложениям — в общей либе. Одиночный адрес,
|
|
67
|
+
разделитель и знак-подпись перечислением не становятся: закрытого набора у них нет, и они
|
|
68
|
+
объявляются константой файла с говорящим именем.
|
|
62
69
|
- **Тип берётся из того пакета, где объявлен.** Своя копия чужого типа расходится с оригиналом
|
|
63
70
|
молча, а компилируется из них только одна.
|
|
64
71
|
- **Двухступенчатое приведение `as unknown as` запрещено правилом линтера.** Вместо него —
|
|
@@ -79,6 +79,14 @@ npx agent-kit propose # отправить груз в приём: сво
|
|
|
79
79
|
сам агент: до неё такое слово адреса не получало вовсе и умирало вместе с сессией. Оба пути
|
|
80
80
|
пишут в один файл дня и в одной форме; в сеть не ходит ни один — увозит их отправка.
|
|
81
81
|
|
|
82
|
+
Каждая запись несёт три строки: **место** — куда правка встаёт в ресурсе, **повод** — что пошло
|
|
83
|
+
не так без неё, и **чем закрывается** — какие надстройки и добавки этого дерева снимаются, когда
|
|
84
|
+
правка приедет редакцией пакета. Третья пишется затем, что предложение уезжает наружу, а
|
|
85
|
+
надстройка остаётся лежать здесь: без неё дерево не может сказать, какие из его надстроек
|
|
86
|
+
исправленная редакция закрыла, — снять наугад страшно, оставить дёшево, и надстройка остаётся
|
|
87
|
+
навсегда, молча замещая исправленный раздел пакета. Снимать нечего — так и пишется; пустой
|
|
88
|
+
третья строка не бывает.
|
|
89
|
+
|
|
82
90
|
Каждому предложению ставится адрес: «пакет», «компаньон» или «дерево». Выгружаются они файлом в `.claude/rt-kit/proposals/`
|
|
83
91
|
(форма — шаблон `proposal.md`), а `agent-kit propose` увозит в приём те, что адресованы
|
|
84
92
|
пакету, вместе со сводкой и разборами происшествий. Адрес дерева в сводке или в тексте
|
|
@@ -150,6 +158,26 @@ npx agent-kit propose # отправить груз в приём: сво
|
|
|
150
158
|
|
|
151
159
|
## Ловушки
|
|
152
160
|
|
|
161
|
+
- **Надстройка замещает раздел целиком, и пакетные пункты в нём приходится держать копией.**
|
|
162
|
+
Дописать в раздел одну статью нечем: слияние идёт по заголовку `## `. Дерево, которому нужен
|
|
163
|
+
один свой пункт, копирует к нему все пакетные — и с этого дня правка любого из них,
|
|
164
|
+
приехавшая с новой версией, до этого дерева не доходит. Сверка разложенного молчит: она
|
|
165
|
+
считает расхождением правку на месте, а не замещённый раздел. Признак виден по самим
|
|
166
|
+
надстройкам — три из трёх прочитанных кончались абзацем о том, что перенос придётся делать
|
|
167
|
+
руками. Поэтому надстройкой берут раздел, у которого пакетных пунктов немного, а разросшийся
|
|
168
|
+
замещённый раздел — повод внести своё в пакет, а не держать его копией. Предложение, которым
|
|
169
|
+
своё вносят, называет эту надстройку строкой «чем закрывается»: иначе приехавшая редакция и
|
|
170
|
+
надстройка, которую она закрыла, не сопоставляются ничем, и надстройка остаётся замещать уже
|
|
171
|
+
исправленный раздел.
|
|
172
|
+
|
|
173
|
+
- **Готовый код пакета не называет имён одного дерева.** Префикс директив кита, ключи подписей
|
|
174
|
+
и имена сущностей принадлежат тому дереву, где паттерн писали; разложенные в соседнем, они
|
|
175
|
+
учат звать то, чего там нет вовсе. Имя директивы при этом отличается от имени в примере: по
|
|
176
|
+
примеру видно, что он пример, а `<префикс>TableRow` из чужого дерева выглядит рабочим кодом
|
|
177
|
+
и правится только после того, как продовая сборка упадёт. Тринадцать таких имён простояли в
|
|
178
|
+
четырёх ресурсах пакета, пока их не нашёл потребитель — и не своей сборкой, а надстройкой,
|
|
179
|
+
которой перекрыл раздел.
|
|
180
|
+
|
|
153
181
|
- **Правила и паттерны при отвергнутом законе в отказе не перечисляются.** Их снимает каскад, а
|
|
154
182
|
строка на них становится выводимой: раскладка называет её лишней вместе с законом, из-за
|
|
155
183
|
которого она перестала снимать. Отказ мерит слой законов, а не число файлов в пакете.
|
|
@@ -168,6 +196,14 @@ npx agent-kit propose # отправить груз в приём: сво
|
|
|
168
196
|
конструкции не видит: `case` теряет свою `esac`, файл остаётся синтаксически неверным, а
|
|
169
197
|
гард с ошибкой синтаксиса отвечает ненулевым кодом — то есть «правка отбита». Два раза за
|
|
170
198
|
заход, и оба раза это выглядело дефектом самого гарда.
|
|
199
|
+
- **Гард, подписанный не на то, что объявляет, выглядит работающим.** Событие и образец вызова
|
|
200
|
+
гард несёт сам, строкой `# rt-hook:`, а зовёт его образец в настройке агента — и эти двое
|
|
201
|
+
расходятся молча: путь гарда в настройке назван, файл разложен, набор сценариев зелёный.
|
|
202
|
+
Набор тут ничего не ловит намеренно — он зовёт гард напрямую с подставленным вводом и
|
|
203
|
+
объявления не читает вовсе. Так гейт правил и разбирал вызовы браузера веткой, которая не
|
|
204
|
+
исполнялась ни разу. Расхождение находит сверка раскладки: она сравнивает объявленный образец
|
|
205
|
+
с тем, под которым гард стоит, называет обе стороны и идёт в счёт расхождений. Правится
|
|
206
|
+
настройка, а верное значение лежит в гарде — тело его разбирает то, что объявлено.
|
|
171
207
|
- **Разложенный файл узнаётся по шапке, а не по каталогу.** Раскладка ложится в те же
|
|
172
208
|
`tools/`, `.claude/hooks/` и `.claude/skills/`, где лежит своё, поэтому карта гейта,
|
|
173
209
|
написанная по путям, требует под него доменное правило — а оно уводит править файл на месте.
|
|
@@ -21,12 +21,32 @@
|
|
|
21
21
|
В тексте предложения не бывает ни путей этого дерева, ни имён его доменов, ни его собственного
|
|
22
22
|
имени: файл уезжает в чужой репозиторий целиком. Найденный адрес дерева отбивает отправку с
|
|
23
23
|
номером строки — это проверка, а не напоминание.
|
|
24
|
+
|
|
25
|
+
Строк при заголовке три, и все три обязательны:
|
|
26
|
+
|
|
27
|
+
место — куда правка встаёт в ресурсе
|
|
28
|
+
повод — что пошло не так без неё
|
|
29
|
+
чем закрывается — какие надстройки и добавки этого дерева снимаются, когда правка приедет
|
|
30
|
+
редакцией пакета
|
|
31
|
+
|
|
32
|
+
Третья пишется затем, что предложение уезжает наружу, а надстройка, ради которой оно написано,
|
|
33
|
+
остаётся лежать в дереве, и связи между ними нет никакой. Пакет выпускает исправленную редакцию
|
|
34
|
+
— и сказать, какие надстройки она закрыла, дереву нечем: имя ресурса совпадает у десятка
|
|
35
|
+
предложений, а раздел надстройки называется тем же заголовком, что и пакетный. Снять наугад
|
|
36
|
+
страшно, оставить дёшево — и надстройка остаётся навсегда, молча замещая исправленный раздел
|
|
37
|
+
пакета.
|
|
38
|
+
|
|
39
|
+
Снимать нечего — так и пишется: «ничего, надстройки под это нет». Пустая строка третьей не
|
|
40
|
+
считается; машине она не видна вовсе — ни одна проверка тела предложения не читает, и держится
|
|
41
|
+
эта форма тем, кто пишет запись.
|
|
24
42
|
-->
|
|
25
43
|
|
|
26
44
|
## пакет · rules/<правило>.md
|
|
27
45
|
|
|
28
46
|
- **место:** раздел «<заголовок>», в конец
|
|
29
47
|
- **повод:** что в этой задаче пошло не так без этого правила
|
|
48
|
+
- **чем закрывается:** `.claude/rt-kit/overrides/rules/<правило>.md`, раздел «<заголовок>» —
|
|
49
|
+
снимается целиком, когда правка приедет редакцией пакета
|
|
30
50
|
|
|
31
51
|
> Готовый текст правки — ровно то, что вставить, в стиле соседних правил: по-русски,
|
|
32
52
|
> утверждением, без воды.
|
|
@@ -35,5 +55,6 @@
|
|
|
35
55
|
|
|
36
56
|
- **место:** ветка `edit`, рядом с соседним родом файлов
|
|
37
57
|
- **повод:** свой род файлов, которого у других деревьев нет
|
|
58
|
+
- **чем закрывается:** ничего, надстройки под это нет — правка и есть надстройка
|
|
38
59
|
|
|
39
60
|
> Готовый текст правки.
|
package/bin/agent-kit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-kit.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/bin/agent-kit.ts"],"names":[],"mappings":";AAeA,OAAO,EAAqC,iBAAiB,EAAqB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-kit.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/bin/agent-kit.ts"],"names":[],"mappings":";AAeA,OAAO,EAAqC,iBAAiB,EAAqB,MAAM,oBAAoB,CAAC;AA4S7G,wBAAsB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAU9E"}
|
package/bin/agent-kit.js
CHANGED
|
@@ -13,8 +13,10 @@ import process from 'node:process';
|
|
|
13
13
|
import { unknownFlagsIn } from '../lib/argv.js';
|
|
14
14
|
import { readCatalog, resolveSelection } from '../lib/catalog.js';
|
|
15
15
|
import { adopt, doctor, init, list, stats, sync } from '../lib/commands.js';
|
|
16
|
+
import { CONFIG_PATH, readConfig } from '../lib/config.js';
|
|
17
|
+
import { enroll, httpEnroll } from '../lib/enroll.js';
|
|
16
18
|
import { httpShip } from '../lib/ship.js';
|
|
17
|
-
import { propose } from '../lib/shipment.js';
|
|
19
|
+
import { propose, treeSlugOf } from '../lib/shipment.js';
|
|
18
20
|
import { DEFAULT_DAYS } from '../lib/observations.js';
|
|
19
21
|
import { staleBuild } from '../lib/freshness.js';
|
|
20
22
|
import { packageRootFrom } from '../lib/package-root.js';
|
|
@@ -34,6 +36,8 @@ const USAGE = [
|
|
|
34
36
|
' propose отправить груз в приём: сводку со снимком надстроек, предложения и разборы',
|
|
35
37
|
' propose --dry-run показать, что уехало бы, и ничего не отправлять',
|
|
36
38
|
' adopt [файлы] отдать пакету файлы, лежащие на его путях не от него',
|
|
39
|
+
' enroll --code <код> завести дерево по приглашению владельца и положить его токен',
|
|
40
|
+
' enroll --force перезаписать уже лежащий токен намеренно',
|
|
37
41
|
'',
|
|
38
42
|
' --root <путь> корень проекта; по умолчанию текущий каталог',
|
|
39
43
|
'',
|
|
@@ -79,6 +83,7 @@ function environmentOf(root) {
|
|
|
79
83
|
/** Чем это дерево себя выдаёт снаружи. Нет удалённого репозитория — нечем, и это не отказ. */
|
|
80
84
|
function remoteOf(root) {
|
|
81
85
|
try {
|
|
86
|
+
// eslint-disable-next-line sonarjs/no-os-command-from-path -- путь к git у каждой машины свой, и прибитый здесь сделал бы пакет непереносимым
|
|
82
87
|
return execFileSync('git', ['-C', root, 'remote', 'get-url', 'origin'], {
|
|
83
88
|
encoding: 'utf8',
|
|
84
89
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
@@ -137,39 +142,36 @@ async function selectionFor(argv, assetsDir) {
|
|
|
137
142
|
* отказ там означал бы, что пакет нельзя разложить без человека. Первый вид — умолчание, а не
|
|
138
143
|
* догадка: порядок в объявлении оси и есть порядок предпочтения.
|
|
139
144
|
*/
|
|
145
|
+
async function variantOfAxis(axis, argv) {
|
|
146
|
+
const spoken = optionOf(argv, `--${axis.name}`, '');
|
|
147
|
+
const values = axis.options.map((option) => option.value);
|
|
148
|
+
if (spoken) {
|
|
149
|
+
return values.includes(spoken)
|
|
150
|
+
? spoken
|
|
151
|
+
: {
|
|
152
|
+
code: 1,
|
|
153
|
+
lines: [`такого вида по оси «${axis.name}» пакет не везёт: ${spoken}`, `есть: ${values.join(', ')}`],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (argv.includes('--all')) {
|
|
157
|
+
return axis.options[0]?.value ?? '';
|
|
158
|
+
}
|
|
159
|
+
if (!canAsk()) {
|
|
160
|
+
return {
|
|
161
|
+
code: 1,
|
|
162
|
+
lines: ['спросить некого: запуск без терминала', `назови вид флагом \`--${axis.name} <вид>\`: ${values.join(', ')}`],
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return askOne(axis.options.map((option) => ({ id: option.value, name: option.value, title: option.title })), axis.question);
|
|
166
|
+
}
|
|
140
167
|
async function variantsFor(argv, assetsDir) {
|
|
141
168
|
const axes = readAxes(assetsDir);
|
|
142
169
|
const chosen = {};
|
|
143
170
|
for (const axis of axes) {
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (!known(spoken)) {
|
|
148
|
-
return {
|
|
149
|
-
code: 1,
|
|
150
|
-
lines: [
|
|
151
|
-
`такого вида по оси «${axis.name}» пакет не везёт: ${spoken}`,
|
|
152
|
-
`есть: ${axis.options.map((option) => option.value).join(', ')}`,
|
|
153
|
-
],
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
chosen[axis.name] = spoken;
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
if (argv.includes('--all')) {
|
|
160
|
-
chosen[axis.name] = axis.options[0]?.value ?? '';
|
|
161
|
-
continue;
|
|
171
|
+
const answer = await variantOfAxis(axis, argv);
|
|
172
|
+
if (isOutcome(answer)) {
|
|
173
|
+
return answer;
|
|
162
174
|
}
|
|
163
|
-
if (!canAsk()) {
|
|
164
|
-
return {
|
|
165
|
-
code: 1,
|
|
166
|
-
lines: [
|
|
167
|
-
'спросить некого: запуск без терминала',
|
|
168
|
-
`назови вид флагом \`--${axis.name} <вид>\`: ${axis.options.map((option) => option.value).join(', ')}`,
|
|
169
|
-
],
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
const answer = await askOne(axis.options.map((option) => ({ id: option.value, name: option.value, title: option.title })), axis.question);
|
|
173
175
|
if (answer === null) {
|
|
174
176
|
return null;
|
|
175
177
|
}
|
|
@@ -177,68 +179,94 @@ async function variantsFor(argv, assetsDir) {
|
|
|
177
179
|
}
|
|
178
180
|
return chosen;
|
|
179
181
|
}
|
|
182
|
+
/** Заведение настройки: выбор ресурсов и видов, брошенный выбор ничего не пишет. */
|
|
183
|
+
async function runInit(env, argv) {
|
|
184
|
+
const selection = await selectionFor(argv, env.assetsDir);
|
|
185
|
+
if (isOutcome(selection)) {
|
|
186
|
+
return selection;
|
|
187
|
+
}
|
|
188
|
+
if (selection === null) {
|
|
189
|
+
return { code: 1, lines: ['выбор брошен — ничего не заведено'] };
|
|
190
|
+
}
|
|
191
|
+
const variants = await variantsFor(argv, env.assetsDir);
|
|
192
|
+
if (isOutcome(variants)) {
|
|
193
|
+
return variants;
|
|
194
|
+
}
|
|
195
|
+
return variants === null
|
|
196
|
+
? { code: 1, lines: ['выбор брошен — ничего не заведено'] }
|
|
197
|
+
: init(env.root, selection, variants, env.assetsDir);
|
|
198
|
+
}
|
|
199
|
+
/** Сводка наблюдений: отрезок из флага, сегодняшний день — с края. */
|
|
200
|
+
function runStats(env, argv) {
|
|
201
|
+
const spoken = Number(optionOf(argv, '--days', ''));
|
|
202
|
+
return stats(env, {
|
|
203
|
+
days: Number.isFinite(spoken) && spoken > 0 ? Math.floor(spoken) : DEFAULT_DAYS,
|
|
204
|
+
// Сегодняшний день берётся здесь: у команды своих часов нет, иначе сводку за
|
|
205
|
+
// отрезок не проверить спекой — вчерашняя фикстура завтра станет позавчерашней.
|
|
206
|
+
today: new Date().toISOString().slice(0, 10),
|
|
207
|
+
json: argv.includes('--json'),
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Отправка накопленного.
|
|
212
|
+
*
|
|
213
|
+
* Единственное действие этой команды необратимо и уходит наружу, поэтому незнакомый довод её
|
|
214
|
+
* кончает, а не пропускается молча: вызов ради списка режимов отправил в приём всё накопленное, и
|
|
215
|
+
* по выводу это не отличалось от «команда ничего не сделала». Прочие команды такого разбора не
|
|
216
|
+
* знают — их действие обратимо.
|
|
217
|
+
*/
|
|
218
|
+
async function runPropose(env, argv) {
|
|
219
|
+
const unknown = unknownFlagsIn(argv.slice(1), PROPOSE_FLAGS);
|
|
220
|
+
if (unknown.length) {
|
|
221
|
+
return {
|
|
222
|
+
code: 1,
|
|
223
|
+
lines: [`таких доводов у \`propose\` нет: ${unknown.join(', ')}`, '', ...PROPOSE_USAGE],
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
return propose(env, {
|
|
227
|
+
dryRun: argv.includes('--dry-run'),
|
|
228
|
+
ship: httpShip,
|
|
229
|
+
remote: remoteOf(env.root),
|
|
230
|
+
// Отрезок тот же, что у сводки по умолчанию: отправку зовут по свежей задаче.
|
|
231
|
+
days: DEFAULT_DAYS,
|
|
232
|
+
today: new Date().toISOString().slice(0, 10),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
/** Приписка дерева к приёмнику по коду приглашения. */
|
|
236
|
+
async function runEnroll(env, argv) {
|
|
237
|
+
const config = readConfig(env.root);
|
|
238
|
+
if (!config) {
|
|
239
|
+
return { code: 1, lines: [`настройки дерева нет: ${CONFIG_PATH}. Заведите её командой \`init\``] };
|
|
240
|
+
}
|
|
241
|
+
return enroll({
|
|
242
|
+
root: env.root,
|
|
243
|
+
intake: config.intake,
|
|
244
|
+
code: optionOf(argv, '--code', ''),
|
|
245
|
+
tree: treeSlugOf(remoteOf(env.root), config.tree),
|
|
246
|
+
token: config.token,
|
|
247
|
+
force: argv.includes('--force'),
|
|
248
|
+
call: httpEnroll,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
/** Что делает каждая команда строки запуска. Ключ — слово, которым её зовут. */
|
|
252
|
+
const COMMANDS = {
|
|
253
|
+
init: runInit,
|
|
254
|
+
list: (env) => list(env),
|
|
255
|
+
sync: (env, argv) => sync(env, argv.includes('--check')),
|
|
256
|
+
stats: runStats,
|
|
257
|
+
propose: runPropose,
|
|
258
|
+
enroll: runEnroll,
|
|
259
|
+
doctor: (env) => doctor(env),
|
|
260
|
+
adopt: (env, argv) => adopt(env, argv.slice(1).filter((value) => !value.startsWith('--') && value !== optionOf(argv, '--root', ''))),
|
|
261
|
+
};
|
|
180
262
|
export async function main(argv) {
|
|
181
263
|
const command = argv[0] ?? '';
|
|
182
264
|
const env = environmentOf(resolve(optionOf(argv, '--root', process.cwd())));
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (isOutcome(selection)) {
|
|
187
|
-
return selection;
|
|
188
|
-
}
|
|
189
|
-
if (selection === null) {
|
|
190
|
-
return { code: 1, lines: ['выбор брошен — ничего не заведено'] };
|
|
191
|
-
}
|
|
192
|
-
const variants = await variantsFor(argv, env.assetsDir);
|
|
193
|
-
if (isOutcome(variants)) {
|
|
194
|
-
return variants;
|
|
195
|
-
}
|
|
196
|
-
return variants === null
|
|
197
|
-
? { code: 1, lines: ['выбор брошен — ничего не заведено'] }
|
|
198
|
-
: init(env.root, selection, variants, env.assetsDir);
|
|
199
|
-
}
|
|
200
|
-
case 'list':
|
|
201
|
-
return list(env);
|
|
202
|
-
case 'sync':
|
|
203
|
-
return sync(env, argv.includes('--check'));
|
|
204
|
-
case 'stats': {
|
|
205
|
-
const spoken = Number(optionOf(argv, '--days', ''));
|
|
206
|
-
return stats(env, {
|
|
207
|
-
days: Number.isFinite(spoken) && spoken > 0 ? Math.floor(spoken) : DEFAULT_DAYS,
|
|
208
|
-
// Сегодняшний день берётся здесь: у команды своих часов нет, иначе сводку за
|
|
209
|
-
// отрезок не проверить спекой — вчерашняя фикстура завтра станет позавчерашней.
|
|
210
|
-
today: new Date().toISOString().slice(0, 10),
|
|
211
|
-
json: argv.includes('--json'),
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
case 'propose': {
|
|
215
|
-
// Единственное действие этой команды необратимо и уходит наружу, поэтому незнакомый
|
|
216
|
-
// довод её кончает, а не пропускается молча: вызов ради списка режимов отправил в
|
|
217
|
-
// приём всё накопленное, и по выводу это не отличалось от «команда ничего не
|
|
218
|
-
// сделала». Прочие команды такого разбора не знают — их действие обратимо.
|
|
219
|
-
const unknown = unknownFlagsIn(argv.slice(1), PROPOSE_FLAGS);
|
|
220
|
-
if (unknown.length) {
|
|
221
|
-
return {
|
|
222
|
-
code: 1,
|
|
223
|
-
lines: [`таких доводов у \`propose\` нет: ${unknown.join(', ')}`, '', ...PROPOSE_USAGE],
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
return propose(env, {
|
|
227
|
-
dryRun: argv.includes('--dry-run'),
|
|
228
|
-
ship: httpShip,
|
|
229
|
-
remote: remoteOf(env.root),
|
|
230
|
-
// Отрезок тот же, что у сводки по умолчанию: отправку зовут по свежей задаче.
|
|
231
|
-
days: DEFAULT_DAYS,
|
|
232
|
-
today: new Date().toISOString().slice(0, 10),
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
case 'doctor':
|
|
236
|
-
return doctor(env);
|
|
237
|
-
case 'adopt':
|
|
238
|
-
return adopt(env, argv.slice(1).filter((value) => !value.startsWith('--') && value !== optionOf(argv, '--root', '')));
|
|
239
|
-
default:
|
|
240
|
-
return { code: command ? 1 : 0, lines: command ? [`неизвестная команда «${command}»`, '', ...USAGE] : USAGE };
|
|
265
|
+
const run = Object.hasOwn(COMMANDS, command) ? COMMANDS[command] : undefined;
|
|
266
|
+
if (!run) {
|
|
267
|
+
return { code: command ? 1 : 0, lines: command ? [`неизвестная команда «${command}»`, '', ...USAGE] : USAGE };
|
|
241
268
|
}
|
|
269
|
+
return run(env, argv);
|
|
242
270
|
}
|
|
243
271
|
const outcome = await main(process.argv.slice(2));
|
|
244
272
|
for (const line of outcome.lines) {
|