@rt-tools/agent-kit 0.8.2 → 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.
Files changed (170) hide show
  1. package/README.md +13 -0
  2. package/assets/agents/rules-reviewer.md +83 -0
  3. package/assets/checks/board.github.mjs +48 -1
  4. package/assets/checks/check-board.github.mjs +84 -1
  5. package/assets/checks/check-dupes.mjs +66 -6
  6. package/assets/checks/check-lib-layers.mjs +13 -524
  7. package/assets/checks/check-specs.mjs +61 -747
  8. package/assets/checks/check-styles.mjs +185 -15
  9. package/assets/checks/lib-boundaries.mjs +143 -0
  10. package/assets/checks/lib-common.mjs +149 -0
  11. package/assets/checks/lib-domains.mjs +205 -0
  12. package/assets/checks/lib-manifests.mjs +60 -0
  13. package/assets/checks/lib-reexports.mjs +101 -0
  14. package/assets/checks/rt-kit-checks.config.mjs +26 -0
  15. package/assets/checks/spec-anchors.mjs +297 -0
  16. package/assets/checks/spec-common.mjs +222 -0
  17. package/assets/checks/spec-contract.mjs +152 -0
  18. package/assets/checks/spec-scenarios.mjs +201 -0
  19. package/assets/commands/feedback.md +95 -0
  20. package/assets/commands/rules-review.md +98 -0
  21. package/assets/commands/skill-curator.md +39 -22
  22. package/assets/defaults/project.sh +8 -0
  23. package/assets/docs/GLOSSARY.md +21 -20
  24. package/assets/hooks/git-guard-push-tests.sh +8 -4
  25. package/assets/hooks/reuse-first-guard.sh +16 -2
  26. package/assets/hooks/skill-gate.sh +1 -1
  27. package/assets/hooks/sql-guard-parse.sh +187 -0
  28. package/assets/hooks/sql-guard-request.sh +117 -0
  29. package/assets/hooks/sql-guard-target.sh +134 -0
  30. package/assets/hooks/sql-guard-write.sh +212 -0
  31. package/assets/hooks/sql-guard.sh +26 -596
  32. package/assets/hooks/task-flow-guard.sh +16 -2
  33. package/assets/hooks/waiting-turn-guard.sh +116 -0
  34. package/assets/laws/delivery.md +35 -0
  35. package/assets/laws/project-documentation.md +18 -0
  36. package/assets/laws/work-conduct.md +25 -0
  37. package/assets/patterns/admin-lists-screen.md +25 -14
  38. package/assets/patterns/admin-nav-item.md +1 -1
  39. package/assets/patterns/component-structure-new.md +1 -1
  40. package/assets/patterns/entity-aside.md +4 -2
  41. package/assets/patterns/git-workflow-commit.azure.md +74 -2
  42. package/assets/patterns/git-workflow-commit.github.md +75 -2
  43. package/assets/patterns/git-workflow-commit.gitlab.md +75 -4
  44. package/assets/patterns/git-workflow-docker.md +30 -0
  45. package/assets/patterns/observability-record.md +9 -0
  46. package/assets/patterns/shared-code-new.md +2 -2
  47. package/assets/patterns/task-flow-close.md +160 -47
  48. package/assets/patterns/task-flow-handoff.md +1 -1
  49. package/assets/patterns/task-flow-resume.md +3 -3
  50. package/assets/patterns/task-flow-start.md +32 -5
  51. package/assets/rules/angular-patterns.md +22 -0
  52. package/assets/rules/api-layer.md +25 -0
  53. package/assets/rules/browser-verification.md +32 -0
  54. package/assets/rules/component-structure.md +21 -0
  55. package/assets/rules/dependencies.md +22 -0
  56. package/assets/rules/doc-style.md +24 -0
  57. package/assets/rules/entity-conventions.needs-admin.md +21 -0
  58. package/assets/rules/entity-models.md +21 -0
  59. package/assets/rules/git-workflow.azure.md +57 -1
  60. package/assets/rules/git-workflow.github.md +89 -2
  61. package/assets/rules/git-workflow.gitlab.md +56 -1
  62. package/assets/rules/lib-layers.md +29 -0
  63. package/assets/rules/lists.md +37 -10
  64. package/assets/rules/navigation.md +21 -0
  65. package/assets/rules/observability.needs-app.md +23 -0
  66. package/assets/rules/permissions.md +23 -0
  67. package/assets/rules/platform-access.md +21 -0
  68. package/assets/rules/reuse-first.md +20 -0
  69. package/assets/rules/seo.md +19 -0
  70. package/assets/rules/shared-code.md +20 -1
  71. package/assets/rules/spec-driven.md +32 -0
  72. package/assets/rules/styling-bem.md +19 -0
  73. package/assets/rules/task-flow.md +148 -18
  74. package/assets/rules/testing.md +62 -0
  75. package/assets/rules/translations.md +21 -0
  76. package/assets/rules/typescript-conventions.md +28 -0
  77. package/assets/samples/specs/_template/spec.md +83 -0
  78. package/assets/samples/tasks/_template/grill.md +28 -0
  79. package/assets/samples/tasks/_template/plan.md +39 -0
  80. package/assets/samples/tasks/_template/progress.md +23 -0
  81. package/assets/skills/agent-kit.md +52 -2
  82. package/assets/templates/proposal.md +21 -0
  83. package/assets/templates/rule.md +31 -2
  84. package/bin/agent-kit.d.ts.map +1 -1
  85. package/bin/agent-kit.js +115 -87
  86. package/bin/agent-kit.js.map +1 -1
  87. package/index.d.ts +1 -0
  88. package/index.d.ts.map +1 -1
  89. package/index.js +1 -0
  90. package/index.js.map +1 -1
  91. package/lib/argv.d.ts.map +1 -1
  92. package/lib/argv.js +6 -4
  93. package/lib/argv.js.map +1 -1
  94. package/lib/assets.d.ts.map +1 -1
  95. package/lib/assets.js +2 -1
  96. package/lib/assets.js.map +1 -1
  97. package/lib/cargo.d.ts +20 -0
  98. package/lib/cargo.d.ts.map +1 -1
  99. package/lib/cargo.js.map +1 -1
  100. package/lib/cascade.d.ts +55 -0
  101. package/lib/cascade.d.ts.map +1 -0
  102. package/lib/cascade.js +131 -0
  103. package/lib/cascade.js.map +1 -0
  104. package/lib/catalog.d.ts +0 -75
  105. package/lib/catalog.d.ts.map +1 -1
  106. package/lib/catalog.js +44 -127
  107. package/lib/catalog.js.map +1 -1
  108. package/lib/commands.d.ts.map +1 -1
  109. package/lib/commands.js +153 -85
  110. package/lib/commands.js.map +1 -1
  111. package/lib/companion.d.ts.map +1 -1
  112. package/lib/companion.js +5 -5
  113. package/lib/companion.js.map +1 -1
  114. package/lib/config.d.ts +12 -1
  115. package/lib/config.d.ts.map +1 -1
  116. package/lib/config.js +11 -5
  117. package/lib/config.js.map +1 -1
  118. package/lib/enroll.d.ts +56 -0
  119. package/lib/enroll.d.ts.map +1 -0
  120. package/lib/enroll.js +123 -0
  121. package/lib/enroll.js.map +1 -0
  122. package/lib/freshness.d.ts.map +1 -1
  123. package/lib/freshness.js +31 -17
  124. package/lib/freshness.js.map +1 -1
  125. package/lib/hooks-map.d.ts +30 -0
  126. package/lib/hooks-map.d.ts.map +1 -1
  127. package/lib/hooks-map.js +80 -18
  128. package/lib/hooks-map.js.map +1 -1
  129. package/lib/integrity.d.ts +1 -2
  130. package/lib/integrity.d.ts.map +1 -1
  131. package/lib/integrity.js +0 -1
  132. package/lib/integrity.js.map +1 -1
  133. package/lib/observations.d.ts.map +1 -1
  134. package/lib/observations.js +25 -12
  135. package/lib/observations.js.map +1 -1
  136. package/lib/order.d.ts +10 -0
  137. package/lib/order.d.ts.map +1 -0
  138. package/lib/order.js +14 -0
  139. package/lib/order.js.map +1 -0
  140. package/lib/picker.d.ts.map +1 -1
  141. package/lib/picker.js +8 -2
  142. package/lib/picker.js.map +1 -1
  143. package/lib/plan.js +1 -1
  144. package/lib/plan.js.map +1 -1
  145. package/lib/proposals.d.ts.map +1 -1
  146. package/lib/proposals.js +25 -8
  147. package/lib/proposals.js.map +1 -1
  148. package/lib/sections.js +1 -1
  149. package/lib/sections.js.map +1 -1
  150. package/lib/ship.d.ts.map +1 -1
  151. package/lib/ship.js +9 -1
  152. package/lib/ship.js.map +1 -1
  153. package/lib/shipment.d.ts.map +1 -1
  154. package/lib/shipment.js +14 -10
  155. package/lib/shipment.js.map +1 -1
  156. package/lib/snapshot.d.ts.map +1 -1
  157. package/lib/snapshot.js +2 -1
  158. package/lib/snapshot.js.map +1 -1
  159. package/lib/stamp.js +1 -1
  160. package/lib/stamp.js.map +1 -1
  161. package/lib/sync.d.ts +12 -2
  162. package/lib/sync.d.ts.map +1 -1
  163. package/lib/sync.js +11 -10
  164. package/lib/sync.js.map +1 -1
  165. package/lib/vars.d.ts.map +1 -1
  166. package/lib/vars.js +2 -3
  167. package/lib/vars.js.map +1 -1
  168. package/package.json +1 -1
  169. package/rt-tools-agent-kit-0.9.0.tgz +0 -0
  170. package/rt-tools-agent-kit-0.8.2.tgz +0 -0
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Сценарии домена и уровень их привязки: что сценарий обещает, каким тестом это покрыто и не
3
+ * выключен ли тест переменной окружения.
4
+ */
5
+ import {
6
+ ACTOR,
7
+ E2E_ROOTS,
8
+ PARTIAL,
9
+ PERCEIVES,
10
+ PROMISE,
11
+ SCENARIO_HEADING,
12
+ SCENARIO_REFERENCE,
13
+ TEST_ROOTS,
14
+ UNCOVERED,
15
+ read,
16
+ walk,
17
+ } from './spec-common.mjs';
18
+
19
+ // ── 4. Сценарии и уровень привязки ────────────────────────────────────────────
20
+
21
+ function parseScenarios(file) {
22
+ const lines = read(file).split('\n');
23
+ const scenarios = [];
24
+ let current = null;
25
+ let inPromise = false;
26
+
27
+ lines.forEach((line, index) => {
28
+ const heading = SCENARIO_HEADING.exec(line);
29
+ if (heading) {
30
+ current = {
31
+ id: heading[1],
32
+ prefix: heading[2],
33
+ title: heading[4],
34
+ file,
35
+ line: index + 1,
36
+ uncovered: false,
37
+ partial: false,
38
+ promise: '',
39
+ };
40
+ inPromise = false;
41
+ scenarios.push(current);
42
+
43
+ return;
44
+ }
45
+ if (/^#{1,6}\s/.test(line)) {
46
+ current = null;
47
+
48
+ return;
49
+ }
50
+ if (!current) {
51
+ return;
52
+ }
53
+ if (UNCOVERED.test(line)) {
54
+ current.uncovered = true;
55
+ }
56
+ if (PARTIAL.test(line)) {
57
+ current.partial = true;
58
+ }
59
+ // «Тогда» и его продолжения с отступом — то, что сценарий обещает
60
+ if (PROMISE.test(line)) {
61
+ inPromise = true;
62
+ current.promise += ` ${line.trim()}`;
63
+
64
+ return;
65
+ }
66
+ if (inPromise && /^\s+\S/.test(line)) {
67
+ current.promise += ` ${line.trim()}`;
68
+
69
+ return;
70
+ }
71
+ inPromise = false;
72
+ });
73
+
74
+ return scenarios;
75
+ }
76
+
77
+ /**
78
+ * Обещан ли сценарием экран. Признак читается только из «Тогда»: «Дано» описывает
79
+ * обстановку, «Когда» — повод, а обещание пользователю стоит именно здесь.
80
+ *
81
+ * Человек и глагол восприятия требуются вместе, потому что порознь оба ошибаются.
82
+ * «Показывается» без человека стоит и там, где показывается запись в базе, а человек без
83
+ * восприятия — в каждом втором сценарии приёма заявки. Признак нарочно молчалив: сценарий,
84
+ * чьё «Тогда» человека не называет, под него не подпадает вовсе.
85
+ */
86
+ function promisesScreen(promise) {
87
+ return ACTOR.test(promise) && PERCEIVES.test(promise);
88
+ }
89
+
90
+ /**
91
+ * Константы, собранные из окружения, вместе с теми, что собраны из них. Ими выключают
92
+ * сквозной тест целиком: без `BASE_URL` или пары входа он не исполняется ни разу.
93
+ * Цепочка раскрывается, пока есть что раскрывать: `HAS_ADMIN_SESSION` собран из двух
94
+ * других констант, а не из `process.env` напрямую.
95
+ */
96
+ function environmentSwitches(root) {
97
+ // Объявление верхнего уровня: с отступом стоят локальные, и они гасят не тест, а случай
98
+ const declaration = /^const\s+([A-Za-z_]\w*)\s*(?::[^=]+)?=\s*([^;]+);/gm;
99
+ const assignments = [];
100
+ for (const file of walk(root, (name) => name.endsWith('.ts'))) {
101
+ for (const [, name, value] of read(file).matchAll(declaration)) {
102
+ assignments.push({ name, value });
103
+ }
104
+ }
105
+
106
+ const switches = new Set();
107
+ for (let pass = 0; pass <= assignments.length; pass += 1) {
108
+ const before = switches.size;
109
+ for (const { name, value } of assignments) {
110
+ if (value.includes('process.env') || [...switches].some((known) => new RegExp(`\\b${known}\\b`).test(value))) {
111
+ switches.add(name);
112
+ }
113
+ }
114
+ if (switches.size === before) {
115
+ break;
116
+ }
117
+ }
118
+
119
+ return switches;
120
+ }
121
+
122
+ /**
123
+ * Упоминания сценария в тестах: где стоит, идёт ли тест путём пользователя и не выключен ли
124
+ * он переменной окружения.
125
+ *
126
+ * Выключатель по состоянию стенда («у объекта меньше двух помещений») — это пропуск случая,
127
+ * и покрытие он не отменяет. Выключатель по переменной отменяет: тест с ним в обычном
128
+ * прогоне значится пропущенным, а сводка без этого читала бы его покрытием.
129
+ */
130
+ function collectReferences() {
131
+ const references = new Map();
132
+ const remember = (id, place) => {
133
+ if (!references.has(id)) {
134
+ references.set(id, []);
135
+ }
136
+ references.get(id).push(place);
137
+ };
138
+ const switchesByRoot = new Map();
139
+
140
+ for (const root of TEST_ROOTS) {
141
+ for (const file of walk(root, (name) => name.endsWith('.spec.ts'))) {
142
+ const e2eRoot = E2E_ROOTS.find((dir) => file.startsWith(`${dir}/`));
143
+ if (e2eRoot && !switchesByRoot.has(e2eRoot)) {
144
+ switchesByRoot.set(e2eRoot, environmentSwitches(e2eRoot));
145
+ }
146
+ const switches = switchesByRoot.get(e2eRoot) ?? new Set();
147
+ const switched = (line) =>
148
+ [...line.matchAll(/test\.skip\(([^,]*)/g)].some(
149
+ ([, condition]) =>
150
+ condition.includes('process.env') || [...switches].some((name) => new RegExp(`\\b${name}\\b`).test(condition))
151
+ );
152
+
153
+ const found = [];
154
+ let test = null;
155
+ let describeSwitched = false;
156
+
157
+ read(file)
158
+ .split('\n')
159
+ .forEach((line, index) => {
160
+ if (/^\s*test\.describe[.(]/.test(line)) {
161
+ describeSwitched = false;
162
+ test = null;
163
+ } else if (/^\s*test\s*\(/.test(line)) {
164
+ test = { off: describeSwitched };
165
+ } else if (/test\.skip\(/.test(line)) {
166
+ if (test) {
167
+ test.off = test.off || switched(line);
168
+ } else {
169
+ describeSwitched = describeSwitched || switched(line);
170
+ }
171
+ }
172
+
173
+ for (const [id] of line.matchAll(SCENARIO_REFERENCE)) {
174
+ found.push({ id, test, place: `${file}:${index + 1}` });
175
+ }
176
+ });
177
+
178
+ // Выключатель стоит первой строкой тела, то есть ниже заголовка теста с
179
+ // идентификатором: состояние теста читается, когда файл разобран целиком
180
+ found.forEach(({ id, test: own, place }) => remember(id, { place, screen: Boolean(e2eRoot), off: Boolean(own?.off) }));
181
+ }
182
+
183
+ // Наборы сценариев на shell. Так проверяются исполняемые файлы — гарды, проверки,
184
+ // умолчания: они не на TypeScript, и набор к ним пишут на том же языке, что и их
185
+ // самих. Выключателей здесь нет: пропустить сценарий в таком наборе нечем, поэтому
186
+ // достаточно найти идентификатор.
187
+ for (const file of walk(root, (name) => name.endsWith('.test.sh'))) {
188
+ read(file)
189
+ .split('\n')
190
+ .forEach((line, index) => {
191
+ for (const [id] of line.matchAll(SCENARIO_REFERENCE)) {
192
+ remember(id, { place: `${file}:${index + 1}`, screen: false, off: false });
193
+ }
194
+ });
195
+ }
196
+ }
197
+
198
+ return references;
199
+ }
200
+
201
+ export { collectReferences, parseScenarios, promisesScreen };
@@ -0,0 +1,95 @@
1
+ ---
2
+ description: Слово о слое правил, сказанное посреди работы, ложится блоком в файл предложений
3
+ argument-hint: '<что мешает, чего не хватило, что сработало не так>'
4
+ ---
5
+
6
+ Положи слово пользователя блоком в файл предложений. Слово: `$ARGUMENTS`
7
+
8
+ Зовётся **посреди работы**, а не после неё: то, обо что споткнулись час назад, к разбору закрытой
9
+ задачи уже забыто, а сама реплика живёт до конца сессии и умирает вместе с ней. Разбор закрытой
10
+ задачи смотрит на загруженное и на ход работы; реплик он не видит вовсе.
11
+
12
+ Команда ничего не отправляет. Она кладёт блок на диск, а увозит его обычная отправка, позванная
13
+ отдельно. Скажи об этом пользователю последней строкой — иначе положенное читается как
14
+ отправленное, и он ждёт ответа, которого никто не посылал.
15
+
16
+ ## 1. Пойми, о чём слово
17
+
18
+ Слово пользователя — проза: «вот это правило мешает», «гейт требует не то», «этого в правилах
19
+ нет вовсе». Твоё дело — перевести её в три вещи:
20
+
21
+ - **адрес** — куда правка идёт;
22
+ - **ресурс** — что именно правится;
23
+ - **готовый текст** — ровно то, что вставить.
24
+
25
+ Адрес один из трёх, и выбирается он не по удобству:
26
+
27
+ пакет — правка ресурса @rt-tools/agent-kit; верна любому дереву и уезжает наружу
28
+ компаньон — implementation.md рядом с правилом: имена этого дерева и привязка статей
29
+ дерево — надстройка этого дерева; наружу не уезжает никогда
30
+
31
+ Ресурс называется идентификатором пакета — `rules/styling-bem.md`, `hooks/skill-gate.sh`,
32
+ `patterns/git-workflow-commit.md`, — а у адресов «компаньон» и «дерево» путём в дереве.
33
+
34
+ **Непонятный адрес спрашивается, а не назначается по догадке.** Неверный адрес уводит правку в
35
+ чужой репозиторий: сказанное о своём дереве уезжает всем, а сказанное обо всех остаётся лежать
36
+ дома. Пока пользователь не ответил, в файл не записывается ничего.
37
+
38
+ Спрашивать не надо, когда адрес виден из самого слова: речь о правиле, которое ты только что
39
+ грузил, — это `пакет`; речь об именах, путях и командах этого дерева — `компаньон` или `дерево`.
40
+
41
+ ## 2. Найди файл сегодняшнего дня
42
+
43
+ Блок ложится туда же, куда его кладёт разбор закрытой задачи: у них один адресат и один формат, а
44
+ второй файл рядом означал бы, что отправка читает два места, а пользователь не помнит, в каком
45
+ лежит его слово.
46
+
47
+ ```bash
48
+ ls .claude/rt-kit/proposals/$(date +%F)-*.md 2>/dev/null
49
+ ```
50
+
51
+ Нашёлся — дописывай в него. Не нашёлся — заведи с образца, назвав по ветке:
52
+
53
+ ```bash
54
+ mkdir -p .claude/rt-kit/proposals
55
+ cp .claude/rt-kit/templates/proposal.md \
56
+ ".claude/rt-kit/proposals/$(date +%F)-$(git branch --show-current).md"
57
+ ```
58
+
59
+ У свежего файла шапка образца остаётся, а незаполненный образец блока — `rules/<правило>.md` со
60
+ скобками — заменяется твоим блоком: отправка такой образец пропускает, но лежит он молчаливым
61
+ мусором.
62
+
63
+ ## 3. Напиши блок
64
+
65
+ Форма заголовка — не украшение: по ней отправка отбирает то, что уезжает наружу. Блок без адреса
66
+ в заголовке не уедет никуда и останется лежать молча.
67
+
68
+ ```markdown
69
+ ## <адрес> · <ресурс>
70
+
71
+ - **место:** раздел «<заголовок>», в конец
72
+ - **повод:** что в этой работе пошло не так без этого правила
73
+
74
+ > Готовый текст правки — ровно то, что вставить, в стиле соседних правил: по-русски,
75
+ > утверждением, без воды.
76
+ ```
77
+
78
+ Повод пишется от случая, а не от желания: «здесь было неудобно» правилом не становится. Слово
79
+ пользователя пересказывается его смыслом, а не твоими выводами о том, как надо было бы.
80
+
81
+ **Адреса этого дерева в тексте блока не бывает** — ни пути, ни имени корня, ни имени чужого
82
+ репозитория: файл уезжает в чужой репозиторий целиком. Найденный адрес отбивает отправку с
83
+ номером строки, и это проверка, а не напоминание. Правь текст, а не обходи её.
84
+
85
+ ## 4. Скажи, что вышло
86
+
87
+ Одной строкой: в какой файл лёг блок, сколько блоков в нём теперь и чем он уедет.
88
+
89
+ ```bash
90
+ npx agent-kit propose --dry-run # что уехало бы
91
+ npx agent-kit propose # отправить груз в приём
92
+ ```
93
+
94
+ Отправка увозит блоки с адресом «пакет» и метит их отправленными; блоки «компаньон» и «дерево»
95
+ остаются лежать — их правит тот, кто работает в этом дереве.
@@ -0,0 +1,98 @@
1
+ ---
2
+ description: Смысловое ревью семьи текстов слоя правил — закон, его правила и паттерны при них
3
+ argument-hint: '<имя закона>'
4
+ ---
5
+
6
+ Прогони смысловое ревью одной семьи текстов пакета правил. Семья: `$ARGUMENTS`
7
+
8
+ Зовётся **в репозитории самого пакета**, а не в дереве, где он стоит: судятся исходные тексты
9
+ ресурсов, и лежат они только здесь. В чужом дереве лежит разложенная копия выбранных ресурсов, а
10
+ не семья целиком, и путей, по которым команда ходит, в нём нет вовсе: обход по ним вернёт
11
+ пустоту, неотличимую от «такого закона не бывает». Каталог ресурсов ниже назван так, как он
12
+ зовётся в репозитории пакета.
13
+
14
+ Считаемое ловит проверка полноты текстов — недостающий раздел, правило без паттерна, имя соседа,
15
+ которому в наборе ничего не отвечает. Здесь ищется то, чего она не считает: два текста,
16
+ говорящих об одном разное, и случай, которого не назвал ни один. Ответ роли не повторяется от
17
+ запуска к запуску, поэтому в гейт он не идёт и ветку не отбивает.
18
+
19
+ ## 1. Пойми, какая семья
20
+
21
+ Семья зовётся именем закона — без пути и без расширения: `work-conduct`, `delivery`,
22
+ `project-documentation`.
23
+
24
+ ```bash
25
+ ls projects/agent-kit/assets/laws/*.md projects/agent-kit/assets/laws/*/*.md
26
+ ```
27
+
28
+ **Имени нет** — назови человеку перечень имён и остановись. Догадываться по похожести нельзя:
29
+ ревью уйдёт на чужую семью, и его находки человек примет за находки о своей.
30
+
31
+ **Названо несколько** — гони по одной, по очереди, и ответы не смешивай: находка семьи читается
32
+ вместе с её законом, а сваленные в кучу они теряют адрес.
33
+
34
+ **Довода нет вовсе** — потребуй имя и напечатай перечень. Умолчания здесь нет: «первый
35
+ попавшийся закон» даёт прогон, неотличимый от осмысленного.
36
+
37
+ **Закон лежит в слое приложения** — это законный случай, а не промах: у денег, локалей и доступа
38
+ семья такая же. Читается он оттуда же, где лежит.
39
+
40
+ ## 2. Собери семью
41
+
42
+ Правило принадлежит закону полем `law:`, паттерн правилу — полем `rule:`. Приставка имени
43
+ ненадёжна: её несут не все паттерны.
44
+
45
+ ```bash
46
+ LAW=<имя закона>
47
+ grep -l "^law: $LAW\$" projects/agent-kit/assets/rules/*.md
48
+ ```
49
+
50
+ Дальше по каждому найденному правилу — его паттерны. Имя правила для поиска берётся голым: без
51
+ вида и без требования — оба стоят суффиксами в имени файла, а поле `rule:` у паттерна несёт
52
+ только само имя.
53
+
54
+ ```bash
55
+ RULE=<имя правила: первое слово имени файла, до первой точки>
56
+ grep -l "^rule: $RULE\$" projects/agent-kit/assets/patterns/*.md
57
+ ```
58
+
59
+ **Виды берутся все.** `git-workflow.github`, `git-workflow.gitlab`, `git-workflow.azure` — три
60
+ редакции одного правила: дерево раскладывает одну, а расходятся они молча.
61
+
62
+ **Суффикс требования — не вид.** `entity-conventions.needs-admin`, `observability.needs-app` —
63
+ это правила, которые дерево берёт, только когда объявило нужную черту. Имя с этим суффиксом в
64
+ поле `rule:` не стоит ни у одного паттерна: поиск по нему возвращает пустоту, и семья уезжает в
65
+ ревью без паттернов вовсе — молча, потому что пустой ответ выглядит как «паттернов нет».
66
+
67
+ **У закона нет ни одного правила** — скажи это человеку и остановись. Читать один закон нечем:
68
+ расхождение живёт между двумя текстами, а пробел уровня статьи без правил под ней — не находка
69
+ ревью, а отсутствие целого слоя. Сам по себе такой закон стоит разговора: его находит и проверка
70
+ полноты текстов, и она же скажет, сколько их.
71
+
72
+ ## 3. Запусти роль
73
+
74
+ Инструментом `Agent`, `subagent_type: 'rules-reviewer'`. В промпт — имя закона и полный список
75
+ путей: закон, все его правила со всеми видами, все паттерны при них. Список собираешь ты: роль
76
+ git-команд не зовёт и историю не читает.
77
+
78
+ Одна роль на семью. Веер из нескольких ролей со сведением ответов здесь не заводится: взгляд на
79
+ предмет один — два текста об одном говорят разное, — а сведение превращает дословные цитаты в
80
+ пересказ.
81
+
82
+ ## 4. Покажи находки человеку
83
+
84
+ **Как есть.** Роль возвращает цитаты дословно, и пересказ их портит: по пересказу человек не
85
+ отличит настоящее расхождение от прочтения роли.
86
+
87
+ Порядок сохрани: сперва расхождения — у каждого два места и две цитаты, — потом пробелы, у
88
+ которых второго места нет.
89
+
90
+ По каждой находке скажи своё: согласен или нет и почему. Правку не вноси — тексты правил
91
+ действуют на все будущие сессии всех деревьев, и решает по ним человек.
92
+
93
+ Находка, с которой человек согласился, идёт дальше двумя путями, и выбирает он:
94
+
95
+ - **правится тут же** — если это правка текста пакета и она укладывается в текущую работу;
96
+ - **уходит задачей** в очередь работ — если тянет за собой код, раскладку или другой закон.
97
+
98
+ Пустой ответ роли — тоже результат: скажи, что находок нет, и не выдумывай их из вежливости.
@@ -6,8 +6,14 @@ argument-hint: '[пусто | <акцент, на что смотреть в п
6
6
  Запусти агента `skill-curator` на разбор только что закрытой задачи. Акцент от пользователя:
7
7
  `$ARGUMENTS`
8
8
 
9
- Вызывается **после того, как задача сделана и проверена**, до перехода к следующей. Агент ничего
10
- не правит приносит готовые формулировки, а решение вставлять их принимает пользователь.
9
+ Вызывается **сразу за открытием PR** тем же ходом, которым работа отдана на разбор. Агент
10
+ ничего не правит: он приносит готовые формулировки, а решение вставлять их принимает
11
+ пользователь.
12
+
13
+ **Запуск фоновый, и ход на нём не кончается.** Пока агент работает, берётся следующая задача:
14
+ он ничего не спрашивает и быстрее от ожидания не идёт. Шаги 1 и 2 делаются до запуска — пока
15
+ задача ещё в голове; шаги 4–6 принимают вернувшийся ответ одним ходом и возвращают исполнителя
16
+ к прежней работе.
11
17
 
12
18
  ## 1. Найди список загруженного
13
19
 
@@ -42,50 +48,61 @@ ls -t "${TMPDIR}claude-skill-gate/"*.loaded | head -5
42
48
  которой не было» — ровно то, из чего получаются правила. Приглаженная сводка даёт приглаженный
43
49
  разбор.
44
50
 
45
- ## 3. Запусти агента
51
+ ## 3. Запусти агента в фон и вернись к работе
46
52
 
47
53
  Инструментом `Agent`, `subagent_type: 'skill-curator'`. В промпт — путь к `.loaded` и сводку
48
54
  целиком.
49
55
 
50
- ## 4. Выгрузи предложения файлом
56
+ Ход на этом не кончается: пока агент работает, берётся следующая задача. Ответ придёт
57
+ уведомлением, и тогда идут шаги 4–6 — один ход, после которого исполнитель возвращается к тому,
58
+ что делал.
59
+
60
+ ## 4. Положи находки в папку задачи
51
61
 
52
- Ответ роли живёт в переписке и умирает вместе с ней, а правки в пакет идут из другого дерева и
53
- в другой день. Поэтому предложения ложатся на диск — их пишешь ты, не роль: файлов она не
54
- пишет вовсе.
62
+ Ответ роли живёт в переписке и умирает вместе с ней, поэтому он сразу ложится на диск рядом с
63
+ ходом работы, в папку задачи. Пишешь его ты, не роль: файлов она не пишет вовсе.
55
64
 
56
65
  ```bash
57
- mkdir -p .claude/rt-kit/proposals
58
- cp .claude/rt-kit/templates/proposal.md .claude/rt-kit/proposals/$(date +%F)-<ветка>.md
66
+ cat > docs/tasks/<ветка>/curator.md # заголовок блока — «## <адрес> · <ресурс>»
59
67
  ```
60
68
 
61
- Дальше по блоку на предложение, заголовком `## <адрес> · <ресурс>`. Адрес роль уже поставила,
62
- твоё дело — не потерять его и не переписать текст своими словами.
69
+ Адрес у каждого блока роль уже поставила «пакет», «компаньон» или «дерево». Твоё дело — не
70
+ потерять его и не переписать текст своими словами.
63
71
 
64
- Файл читает `agent-kit propose`: по заголовку он отбирает то, что уезжает в приём. Блок без
65
- адреса в заголовке не уедет никуда и останется лежать молча.
72
+ Папка задачи умирает со слиянием, а находки должны пережить весь эпик: владелец читает их
73
+ разом, когда эпик кончился. Поэтому при разборе папки файл находок не удаляется, а переезжает к
74
+ замыслу эпика — паттерн закрытия работы. Работа вне эпика показывает находки владельцу сразу.
66
75
 
67
- ## 5. Отправь то, что адресовано пакету
76
+ ## 5. Отправь сводку наблюдений и только её
68
77
 
69
78
  ```bash
70
79
  npx agent-kit propose --dry-run # что уехало бы
71
80
  npx agent-kit propose # отправить груз в приём
72
81
  ```
73
82
 
74
- Груз уезжает при каждом прогоне: сводка наблюдений со снимком надстроек, предложения с адресом
75
- «пакет» когда они есть,и разборы происшествий. Прогон без замечаний тоже говорит, чем
76
- пользовались, чем не пользовались ни разу и что дерево переопределило. Отправленное предложение
77
- помечается в том же файле и второй раз не уезжает.
83
+ Груз уезжает при каждом прогоне: сводка наблюдений со снимком надстроек, разборы происшествий и
84
+ предложения, лежащие в каталоге предложений. Сводка факт: чем пользовались, чем не
85
+ пользовались ни разу, что дерево переопределило. Она уезжает всегда и слова владельца не ждёт.
86
+
87
+ **Находки разбора в каталог предложений сами не ложатся, и потому не уезжают.** Предложение —
88
+ заготовка правки чужого дерева, и часть заготовок отпадает при первом же чтении; уехавшая без
89
+ разбора, она становится работой того, кто её не заказывал. В каталог предложений переносится
90
+ только то, что владелец назвал верным, — и тогда же уезжает.
78
91
 
79
92
  Отправка отказывает, если адрес этого дерева нашёлся в сводке или в тексте предложения — путь,
80
93
  имя корня, чужой репозиторий. Это не придирка: груз уезжает наружу целиком. Правь текст, а не
81
94
  обходи проверку. Разбор происшествия проверкой не накрыт: он по устройству называет файлы
82
95
  дерева, где промах случился.
83
96
 
84
- ## 6. Отдай результат владельцу
97
+ ## 6. Отдай находки владельцу — по концу эпика
98
+
99
+ Покажи находки **как есть**: роль пишет готовый текст для вставки, и пересказ его портит. По
100
+ каждой скажи своё — согласен или нет и почему; правило, с которым ты не согласен, вставлять не
101
+ надо.
85
102
 
86
- Покажи предложения агента **как есть**: он пишет готовый текст для вставки, и пересказ его
87
- портит. По каждому скажи своё согласен или нет и почему; правило, с которым ты не согласен,
88
- вставлять не надо.
103
+ Работа в эпике показывает их не сразу: находки копятся у замысла эпика и читаются разом, когда
104
+ эпик кончился,так владелец видит повторяющееся, а не разрозненные заметки. Названное им
105
+ верным переносится в каталог предложений и уезжает шагом 5.
89
106
 
90
107
  У каждого предложения агент ставит пометку «пакет», «компаньон» или «дерево»: тексты приезжают
91
108
  из `@rt-tools/agent-kit`, и правка разложенного файла на месте теряется на следующем
@@ -43,9 +43,17 @@ RT_HOOKS_TESTS="${RT_HOOKS_TESTS:-.claude/hooks/tests/run.sh}"
43
43
  # Заведённая проверка встаёт сюда, а не только в общий прогон, который никто не зовёт сам:
44
44
  # новая строка в её списке известного уезжает в главную ветку молча, а список при этом читается
45
45
  # как действующая охрана.
46
+ #
47
+ # Сверка раскладки идёт первой строкой и по двум причинам. Она стоит секунды, поэтому отбивает
48
+ # раньше, чем начнётся долгий прогон. И до неё расхождение разложенного с пакетом не отбивало
49
+ # ничего: правка, положенная в разложенную копию мимо источника, ничего не ломает в день, когда
50
+ # её делают, а всплывает на чужой работе — раскладка отказывает по правленому файлу целиком и не
51
+ # кладёт ни одного другого. Признак — сама настройка раскладки: дерево без неё пакета не ставит,
52
+ # и звать в нём нечего.
46
53
  rt_push_checks_default() {
47
54
  runner="$(rt_runner)"
48
55
  root="${CLAUDE_PROJECT_DIR:-.}"
56
+ [ -f "$root/.claude/rt-kit.json" ] && printf '%s\n' "$runner agent-kit sync --check"
49
57
  if [ -n "$1" ]; then
50
58
  printf '%s\n' "$runner nx affected -t lint test build --base=$1"
51
59
  else
@@ -13,26 +13,27 @@
13
13
 
14
14
  ## Слой правил
15
15
 
16
- | Термин | Что это |
17
- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
18
- | Закон | Файл в каталоге конституции. Говорит, что должно быть верно, и не знает ни путей, ни имён файлов. Верен для любого приложения этого класса |
19
- | Законы приложения | Слой законов, верных только для этого приложения: деньги, локали, доступ. Предметность в них законна — она их предмет |
20
- | Правило | Скил с `kind: rule`. Привязывает закон к этому дереву: чем это здесь названо и где лежит |
21
- | Паттерн | Скил с `kind: pattern`. Готовый код и порядок действий; стоит при правиле |
22
- | Компаньон | Файл `implementation.md` рядом с правилом: имена и пути этого дерева. Пакет знает приём, но не знает имён — их пишет проект |
23
- | Спек | Описание домена: как он работает. Говорит об установившемся, а не о предстоящем |
24
- | Домен | Предмет, у которого свой спек. Выросший домен делится на поддомены, а не на соседние домены |
25
- | Сценарий | Наблюдаемое поведение под номером `SC-<ПРЕФИКС>-<НОМЕР>`. Номер стоит в заголовке теста |
26
- | Привязка | Строка `` `файл:символ` `` в компаньоне или в спутнике спека — место, где утверждение исполняется |
27
- | Спутник | Файл рядом со спеком или правилом: компаньон, перечень сценариев |
28
- | Договорённость о продукте | Как продукт себя поведёт, записанное до кода. Единственное место, где спек говорит о будущем; после выкатки вливается в спек домена, а директория удаляется |
29
- | Ресурс | Единица того, что везёт пакет правил: закон, правило, паттерн, гард, проверка, роль, команда, конвейер, шаблон, умолчание, документ |
30
- | Раскладка | Перенос ресурса из пакета в дерево по его роду и настройке слоя |
31
- | Разложенный файл | Файл в дереве с шапкой пакета. Правится не на месте, а надстройкой: правка на месте теряется на следующей раскладке |
32
- | Надстройка | Файл дерева, который сливается с разложенным по заголовкам разделов |
33
- | Наблюдение | Строка о событии слоя правил: правило загружено, гейт отбил, гард отказал. Пишет гард, живёт в дереве, наружу уезжает счётчиками. Журналом не называется |
34
- | Сводка | Что наблюдения говорят за отрезок дней: чем пользовались, чем ни разу, обо что спотыкались |
35
- | Предложение | Готовая формулировка правки правил с адресом: пакет, компаньон или дерево. Приносит разбор закрытой задачи, отправляет человек командой |
16
+ | Термин | Что это |
17
+ | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
18
+ | Закон | Файл в каталоге конституции. Говорит, что должно быть верно, и не знает ни путей, ни имён файлов. Верен для любого приложения этого класса |
19
+ | Законы приложения | Слой законов, верных только для этого приложения: деньги, локали, доступ. Предметность в них законна — она их предмет |
20
+ | Правило | Скил с `kind: rule`. Привязывает закон к этому дереву: чем это здесь названо и где лежит |
21
+ | Паттерн | Скил с `kind: pattern`. Готовый код и порядок действий; стоит при правиле |
22
+ | Компаньон | Файл `implementation.md` рядом с правилом: имена и пути этого дерева. Пакет знает приём, но не знает имён — их пишет проект |
23
+ | Спек | Описание домена: как он работает. Говорит об установившемся, а не о предстоящем |
24
+ | Домен | Предмет, у которого свой спек. Выросший домен делится на поддомены, а не на соседние домены |
25
+ | Сценарий | Наблюдаемое поведение под номером `SC-<ПРЕФИКС>-<НОМЕР>`. Номер стоит в заголовке теста |
26
+ | Привязка | Строка `` `файл:символ` `` в компаньоне или в спутнике спека — место, где утверждение исполняется |
27
+ | Спутник | Файл рядом со спеком или правилом: компаньон, перечень сценариев |
28
+ | Договорённость о продукте | Как продукт себя поведёт, записанное до кода. Единственное место, где спек говорит о будущем; после выкатки вливается в спек домена, а директория удаляется |
29
+ | Ресурс | Единица того, что везёт пакет правил: закон, правило, паттерн, гард, проверка, роль, команда, конвейер, шаблон, умолчание, документ |
30
+ | Раскладка | Перенос ресурса из пакета в дерево по его роду и настройке слоя |
31
+ | Разложенный файл | Файл в дереве с шапкой пакета. Правится не на месте, а надстройкой: правка на месте теряется на следующей раскладке |
32
+ | Надстройка | Файл дерева, который сливается с разложенным по заголовкам разделов |
33
+ | Наблюдение | Строка о событии слоя правил: правило загружено, гейт отбил, гард отказал. Пишет гард, живёт в дереве, наружу уезжает счётчиками. Журналом не называется |
34
+ | Сводка | Что наблюдения говорят за отрезок дней: чем пользовались, чем ни разу, обо что спотыкались |
35
+ | Предложение | Готовая формулировка правки правил с адресом: пакет, компаньон или дерево. Приносит разбор закрытой задачи или слово посреди работы, отправляет человек командой |
36
+ | Слово | Реплика человека посреди работы о слое правил: что мешает, чего не хватило, что сработало не так. Ложится блоком в файл предложений, а не живёт до конца захода |
36
37
 
37
38
  ## Работа
38
39
 
@@ -36,10 +36,14 @@ case "$tool" in
36
36
  esac
37
37
 
38
38
  cmd="$(printf '%s' "$input" | jq -r '.tool_input.command // empty' 2>/dev/null)"
39
- case "$cmd" in
40
- *git\ push*) ;;
41
- *) exit 0 ;;
42
- esac
39
+
40
+ # Вызов пуша узнаётся по двум признакам сразу — команда `git` в начале строки или за
41
+ # разделителем и слово `push` отдельным словом. Тем же приёмом, что у гарда поставки: одной
42
+ # подстрокой «git push» пуш не поймать — помощник учётных данных и заголовок запроса ставятся
43
+ # ключами `-c` между ними, и ровно этой формой здесь и пушат. Пока признаком была подстрока,
44
+ # весь набор гейта на таком пуше не гонялся вовсе, а молчание гарда читалось как «зелено».
45
+ printf '%s' "$cmd" | grep -qE '(^|[;&|(]|&&|\|\|)[[:space:]]*git([[:space:]]|$)' || exit 0
46
+ printf '%s' "$cmd" | grep -qE '(^|[[:space:]])push([[:space:]]|$)' || exit 0
43
47
 
44
48
  # Пробный пуш ничего не отправляет: гонять ради него весь набор незачем.
45
49
  case "$cmd" in