@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.
Files changed (132) hide show
  1. package/README.md +12 -0
  2. package/assets/checks/board.github.mjs +48 -1
  3. package/assets/checks/check-board.github.mjs +84 -1
  4. package/assets/checks/check-lib-layers.mjs +13 -524
  5. package/assets/checks/check-specs.mjs +11 -782
  6. package/assets/checks/check-styles.mjs +185 -15
  7. package/assets/checks/lib-boundaries.mjs +143 -0
  8. package/assets/checks/lib-common.mjs +149 -0
  9. package/assets/checks/lib-domains.mjs +205 -0
  10. package/assets/checks/lib-manifests.mjs +60 -0
  11. package/assets/checks/lib-reexports.mjs +101 -0
  12. package/assets/checks/rt-kit-checks.config.mjs +17 -0
  13. package/assets/checks/spec-anchors.mjs +297 -0
  14. package/assets/checks/spec-common.mjs +222 -0
  15. package/assets/checks/spec-contract.mjs +152 -0
  16. package/assets/checks/spec-scenarios.mjs +201 -0
  17. package/assets/defaults/project.sh +8 -0
  18. package/assets/hooks/git-guard-push-tests.sh +8 -4
  19. package/assets/hooks/skill-gate.sh +1 -1
  20. package/assets/hooks/sql-guard-parse.sh +187 -0
  21. package/assets/hooks/sql-guard-request.sh +117 -0
  22. package/assets/hooks/sql-guard-target.sh +134 -0
  23. package/assets/hooks/sql-guard-write.sh +212 -0
  24. package/assets/hooks/sql-guard.sh +26 -596
  25. package/assets/hooks/waiting-turn-guard.sh +42 -13
  26. package/assets/laws/delivery.md +7 -0
  27. package/assets/laws/work-conduct.md +9 -0
  28. package/assets/patterns/admin-lists-screen.md +25 -14
  29. package/assets/patterns/admin-nav-item.md +1 -1
  30. package/assets/patterns/component-structure-new.md +1 -1
  31. package/assets/patterns/entity-aside.md +4 -2
  32. package/assets/patterns/observability-record.md +9 -0
  33. package/assets/patterns/shared-code-new.md +2 -2
  34. package/assets/patterns/task-flow-close.md +7 -1
  35. package/assets/rules/git-workflow.azure.md +7 -0
  36. package/assets/rules/git-workflow.github.md +41 -0
  37. package/assets/rules/git-workflow.gitlab.md +7 -0
  38. package/assets/rules/lib-layers.md +4 -0
  39. package/assets/rules/lists.md +10 -10
  40. package/assets/rules/shared-code.md +1 -1
  41. package/assets/rules/task-flow.md +47 -7
  42. package/assets/rules/testing.md +31 -0
  43. package/assets/rules/typescript-conventions.md +7 -0
  44. package/assets/skills/agent-kit.md +36 -0
  45. package/assets/templates/proposal.md +21 -0
  46. package/bin/agent-kit.d.ts.map +1 -1
  47. package/bin/agent-kit.js +115 -87
  48. package/bin/agent-kit.js.map +1 -1
  49. package/index.d.ts +1 -0
  50. package/index.d.ts.map +1 -1
  51. package/index.js +1 -0
  52. package/index.js.map +1 -1
  53. package/lib/argv.d.ts.map +1 -1
  54. package/lib/argv.js +6 -4
  55. package/lib/argv.js.map +1 -1
  56. package/lib/assets.d.ts.map +1 -1
  57. package/lib/assets.js +2 -1
  58. package/lib/assets.js.map +1 -1
  59. package/lib/cargo.d.ts +20 -0
  60. package/lib/cargo.d.ts.map +1 -1
  61. package/lib/cargo.js.map +1 -1
  62. package/lib/cascade.d.ts +55 -0
  63. package/lib/cascade.d.ts.map +1 -0
  64. package/lib/cascade.js +131 -0
  65. package/lib/cascade.js.map +1 -0
  66. package/lib/catalog.d.ts +0 -75
  67. package/lib/catalog.d.ts.map +1 -1
  68. package/lib/catalog.js +44 -127
  69. package/lib/catalog.js.map +1 -1
  70. package/lib/commands.d.ts.map +1 -1
  71. package/lib/commands.js +152 -85
  72. package/lib/commands.js.map +1 -1
  73. package/lib/companion.d.ts.map +1 -1
  74. package/lib/companion.js +5 -5
  75. package/lib/companion.js.map +1 -1
  76. package/lib/config.d.ts +2 -0
  77. package/lib/config.d.ts.map +1 -1
  78. package/lib/config.js +7 -5
  79. package/lib/config.js.map +1 -1
  80. package/lib/enroll.d.ts +56 -0
  81. package/lib/enroll.d.ts.map +1 -0
  82. package/lib/enroll.js +123 -0
  83. package/lib/enroll.js.map +1 -0
  84. package/lib/freshness.d.ts.map +1 -1
  85. package/lib/freshness.js +31 -17
  86. package/lib/freshness.js.map +1 -1
  87. package/lib/hooks-map.d.ts +30 -0
  88. package/lib/hooks-map.d.ts.map +1 -1
  89. package/lib/hooks-map.js +80 -18
  90. package/lib/hooks-map.js.map +1 -1
  91. package/lib/integrity.d.ts +1 -2
  92. package/lib/integrity.d.ts.map +1 -1
  93. package/lib/integrity.js +0 -1
  94. package/lib/integrity.js.map +1 -1
  95. package/lib/observations.d.ts.map +1 -1
  96. package/lib/observations.js +25 -12
  97. package/lib/observations.js.map +1 -1
  98. package/lib/order.d.ts +10 -0
  99. package/lib/order.d.ts.map +1 -0
  100. package/lib/order.js +14 -0
  101. package/lib/order.js.map +1 -0
  102. package/lib/picker.d.ts.map +1 -1
  103. package/lib/picker.js +8 -2
  104. package/lib/picker.js.map +1 -1
  105. package/lib/plan.js +1 -1
  106. package/lib/plan.js.map +1 -1
  107. package/lib/proposals.d.ts.map +1 -1
  108. package/lib/proposals.js +25 -8
  109. package/lib/proposals.js.map +1 -1
  110. package/lib/sections.js +1 -1
  111. package/lib/sections.js.map +1 -1
  112. package/lib/ship.d.ts.map +1 -1
  113. package/lib/ship.js +9 -1
  114. package/lib/ship.js.map +1 -1
  115. package/lib/shipment.d.ts.map +1 -1
  116. package/lib/shipment.js +14 -10
  117. package/lib/shipment.js.map +1 -1
  118. package/lib/snapshot.d.ts.map +1 -1
  119. package/lib/snapshot.js +2 -1
  120. package/lib/snapshot.js.map +1 -1
  121. package/lib/stamp.js +1 -1
  122. package/lib/stamp.js.map +1 -1
  123. package/lib/sync.d.ts +12 -2
  124. package/lib/sync.d.ts.map +1 -1
  125. package/lib/sync.js +11 -10
  126. package/lib/sync.js.map +1 -1
  127. package/lib/vars.d.ts.map +1 -1
  128. package/lib/vars.js +2 -3
  129. package/lib/vars.js.map +1 -1
  130. package/package.json +1 -1
  131. package/rt-tools-agent-kit-0.9.0.tgz +0 -0
  132. package/rt-tools-agent-kit-0.8.3.tgz +0 -0
package/lib/catalog.d.ts CHANGED
@@ -47,12 +47,6 @@ export interface IFrontMatter {
47
47
  /** Правило, при котором стоит паттерн. */
48
48
  readonly rule: string;
49
49
  }
50
- /**
51
- * Вступление между `---` в начале файла; его нет — вернётся пустая шапка.
52
- *
53
- * Живёт при каталоге, потому что связь родителя с потомком читают двое: сверка связности пакета
54
- * и каскад отказа. Объявление у одного из них заводило бы круговой импорт между ними.
55
- */
56
50
  export declare function frontMatterOf(text: string): IFrontMatter;
57
51
  /**
58
52
  * Что ресурс о себе объявил. Имена читаются идентификаторами — `hooks/observe.sh`: короткая
@@ -111,76 +105,7 @@ export interface IGapOfVariant {
111
105
  /** Идентификаторы этих видов: ими же пробел и снимается через `skip`. */
112
106
  readonly ids: readonly string[];
113
107
  }
114
- /**
115
- * Ресурсы, оставшиеся без вида под выбор дерева.
116
- *
117
- * Молчаливый пропуск такого ресурса — худший из исходов. Правило поставки едет тремя видами, а
118
- * команды заведения задачи и сверки очереди — одним: дерево, выбравшее чужой хостинг, получает
119
- * правило, которое зовёт три команды, и ни одного скрипта под ними. Отказа при этом не бывает —
120
- * ресурс просто не приезжает, и узнают об этом, когда команда из правила не находится.
121
- *
122
- * Отсюда правило: **ресурс с видами, ни один из которых не совпал, — отказ, а не пропуск.**
123
- * Снимается он двумя способами, и оба явные: завести недостающий вид в пакете либо назвать
124
- * существующие в `skip` — дерево тем самым говорит, что обходится без этого ресурса.
125
- */
126
108
  export declare function variantGaps(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly IGapOfVariant[];
127
- /** Ресурс, снятый вслед за отвергнутым родителем. */
128
- export interface ICascadeCut {
129
- readonly id: string;
130
- /** Ближайший родитель: у правила — его закон, у паттерна — его правило. */
131
- readonly parent: string;
132
- /**
133
- * Отвергнутый корень цепочки — то, что дерево действительно отвергло или не выбрало.
134
- * Ближайшего родителя мало: паттерн уходит вслед за правилом, которого в отказе нет, и
135
- * искать его там читатель пойдёт зря.
136
- */
137
- readonly root: string;
138
- }
139
- /**
140
- * Ресурсы, снятые каскадом: правила при невзятом законе и паттерны при невзятых правилах.
141
- *
142
- * Связь читается из вступления самого ресурса — отдельный список при пакете разошёлся бы с
143
- * ресурсами молча, тем же молчанием, ради которого каскад и заводится. Родитель ищется по
144
- * последнему звену имени: полного пути в шапке нет, потому что переезд закона между слоями
145
- * переписывал бы шапки всех правил при нём.
146
- *
147
- * Родителя в каталоге нет вовсе — каскад молчит: снимать не по чему, а промах в шапке судит
148
- * сверка связности пакета, до всякой раскладки в дереве.
149
- */
150
- export declare function cascadeCuts(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly ICascadeCut[];
151
- /**
152
- * Ресурсы, названные выбором поимённо и всё равно снятые каскадом.
153
- *
154
- * Дерево, назвавшее правило в выборе при невыбранном законе, не получает его молча — то самое
155
- * молчание, ради которого каскад и заводится, только с другой стороны: там о снятии не знал тот,
156
- * кто ничего не просил, здесь — тот, кто попросил прямо.
157
- */
158
- export declare function namedButCut(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly ICascadeCut[];
159
- /**
160
- * Что ложится в дерево: прямой отбор за вычетом снятого каскадом.
161
- *
162
- * Отдельной функцией, а не внутри `isChosen`, потому что каскад знает весь каталог, а отбор —
163
- * один ресурс: связь родителя с потомком по одной записи не видна.
164
- */
165
- export declare function chosenEntries(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly IEntryOfCatalog[];
166
- /** Строка отказа, которая ничего не снимает. */
167
- export interface IIdleSkip {
168
- readonly id: string;
169
- /** Чем названное ею уже снято: имя отвергнутого родителя, либо пусто — такого ресурса нет. */
170
- readonly by: string;
171
- }
172
- /**
173
- * Строки отказа, которые ничего не снимают: названное ими снято каскадом или в каталоге не
174
- * значится вовсе.
175
- *
176
- * Не отказ, а предупреждение: настройка, верная до обновления пакета, становится лишней сама,
177
- * без единой правки в дереве, и отбивать за это раскладку значило бы наказывать за вчерашнюю
178
- * правоту.
179
- *
180
- * Строка на ресурс чужого вида лишней не считается никогда: ею дерево гасит отказ о ресурсе,
181
- * у которого нет подходящего вида, — то есть работу она делает.
182
- */
183
- export declare function idleSkips(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly IIdleSkip[];
184
109
  /** Выбранный ресурс, чьё требование в дерево не поехало. */
185
110
  export interface IBrokenLink {
186
111
  /** Кто требует — идентификатор выбранного ресурса. */
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/catalog.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/catalog.ts"],"names":[],"mappings":"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"}
package/lib/catalog.js CHANGED
@@ -11,13 +11,20 @@ import { KINDS } from './config.js';
11
11
  import { answersRequirement, requirementOf, withoutRequirement } from './traits.js';
12
12
  import { matchesVariant, readAxes, variantOf, withoutVariant } from './variants.js';
13
13
  const TITLE = /^#\s+(\S.*)$/m;
14
- const FIELD = /^([a-z]+):\s*(\S.*?)\s*$/;
14
+ const FIELD = /^([a-z]+):(.*)$/;
15
15
  /**
16
16
  * Вступление между `---` в начале файла; его нет — вернётся пустая шапка.
17
17
  *
18
18
  * Живёт при каталоге, потому что связь родителя с потомком читают двое: сверка связности пакета
19
19
  * и каскад отказа. Объявление у одного из них заводило бы круговой импорт между ними.
20
20
  */
21
+ function rememberField(found, line) {
22
+ const match = FIELD.exec(line);
23
+ const value = match ? match[2].trim() : '';
24
+ if (match && value) {
25
+ found[match[1]] = value;
26
+ }
27
+ }
21
28
  export function frontMatterOf(text) {
22
29
  const lines = text.split('\n');
23
30
  const found = {};
@@ -26,10 +33,7 @@ export function frontMatterOf(text) {
26
33
  if (line.trim() === '---') {
27
34
  break;
28
35
  }
29
- const match = line.match(FIELD);
30
- if (match) {
31
- found[match[1]] = match[2];
32
- }
36
+ rememberField(found, line);
33
37
  }
34
38
  }
35
39
  return { name: found['name'] ?? '', kind: found['kind'] ?? '', law: found['law'] ?? '', rule: found['rule'] ?? '' };
@@ -38,14 +42,14 @@ export function frontMatterOf(text) {
38
42
  * Строка требования в шапке ресурса. Двух видов, потому что ресурсы двух родов: у разметки
39
43
  * `**Требует:**`, у исполняемого файла — комментарий `# Требует:`.
40
44
  */
41
- const REQUIRES = /^(?:#\s*Требует:|\*\*Требует:\*\*)\s*(.+)$/m;
45
+ const REQUIRES = /^(?:#[^\S\n]*Требует:|\*\*Требует:\*\*)([^\n]*)$/m;
42
46
  /**
43
47
  * Что ресурс о себе объявил. Имена читаются идентификаторами — `hooks/observe.sh`: короткая
44
48
  * форма совпала бы у закона и правила с одним именем, а требование, указавшее не туда, хуже
45
49
  * ненайденного.
46
50
  */
47
51
  export function requiresOf(text) {
48
- const found = text.match(REQUIRES);
52
+ const found = REQUIRES.exec(text);
49
53
  if (!found) {
50
54
  return [];
51
55
  }
@@ -66,7 +70,7 @@ function isExecutable(path) {
66
70
  }
67
71
  /** Заголовок нужен человеку, который выбирает: имена законов ему ни о чём не говорят. */
68
72
  export function titleOf(text, fallback) {
69
- const found = text.match(TITLE);
73
+ const found = TITLE.exec(text);
70
74
  return found ? found[1].trim() : fallback;
71
75
  }
72
76
  /**
@@ -93,6 +97,9 @@ function filesOf(dir, prefix = '') {
93
97
  else if (entry.isDirectory()) {
94
98
  files.push(...filesOf(join(dir, entry.name), `${prefix}${entry.name}/`));
95
99
  }
100
+ else {
101
+ // Ни файл, ни каталог — связь или узел устройства: ресурсом пакета это не бывает
102
+ }
96
103
  }
97
104
  return files;
98
105
  }
@@ -114,14 +121,14 @@ export function readCatalog(assetsDir) {
114
121
  const executable = isExecutable(path);
115
122
  entries.push({
116
123
  id: `${kind}/${file}`,
124
+ title: titleOf(text, name),
125
+ requires: requiresOf(text),
117
126
  kind,
118
127
  name,
119
- title: titleOf(text, name),
120
128
  variant,
121
129
  text,
122
130
  needs,
123
131
  executable,
124
- requires: requiresOf(text),
125
132
  });
126
133
  }
127
134
  }
@@ -170,6 +177,23 @@ export function isChosen(entry, selection) {
170
177
  * Снимается он двумя способами, и оба явные: завести недостающий вид в пакете либо назвать
171
178
  * существующие в `skip` — дерево тем самым говорит, что обходится без этого ресурса.
172
179
  */
180
+ /**
181
+ * Закрыт ли ресурс выбором: подходящий вид взят, все виды отвергнуты либо род сужен мимо него.
182
+ *
183
+ * Отказ от ресурса — законный ответ, и повторять его отказом раскладки незачем: дерево уже
184
+ * сказало, что обходится без него. Род, суженный через `only`, отбирает ресурсы поимённо: не
185
+ * названный в нём ресурс не пропал — его не просили.
186
+ */
187
+ function closedBySelection(entries, selection) {
188
+ if (entries.some((entry) => matchesVariant(entry.variant, selection.variants))) {
189
+ return true;
190
+ }
191
+ if (entries.every((entry) => selection.skip.includes(entry.id))) {
192
+ return true;
193
+ }
194
+ const restricted = selection.only.some((id) => id.startsWith(`${entries[0].kind}/`));
195
+ return restricted && !entries.some((entry) => selection.only.includes(entry.id));
196
+ }
173
197
  export function variantGaps(catalog, selection) {
174
198
  const groups = new Map();
175
199
  for (const entry of catalog) {
@@ -180,26 +204,12 @@ export function variantGaps(catalog, selection) {
180
204
  groups.set(key, [...(groups.get(key) ?? []), entry]);
181
205
  }
182
206
  const gaps = [];
183
- for (const entries of groups.values()) {
207
+ for (const entries of [...groups.values()].filter((one) => !closedBySelection(one, selection))) {
184
208
  const axis = entries[0].variant?.axis ?? '';
185
- if (entries.some((entry) => matchesVariant(entry.variant, selection.variants))) {
186
- continue;
187
- }
188
- // Отказ от ресурса — законный ответ, и повторять его отказом раскладки незачем: дерево
189
- // уже сказало, что обходится без него.
190
- if (entries.every((entry) => selection.skip.includes(entry.id))) {
191
- continue;
192
- }
193
- // Род, суженный через `only`, отбирает ресурсы поимённо: не названный в нём ресурс не
194
- // пропал — его не просили.
195
- const restricted = selection.only.some((id) => id.startsWith(`${entries[0].kind}/`));
196
- if (restricted && !entries.some((entry) => selection.only.includes(entry.id))) {
197
- continue;
198
- }
199
209
  gaps.push({
210
+ axis,
200
211
  kind: entries[0].kind,
201
212
  name: entries[0].name,
202
- axis,
203
213
  chosen: selection.variants[axis] ?? '',
204
214
  available: entries.map((entry) => entry.variant?.value ?? ''),
205
215
  ids: entries.map((entry) => entry.id),
@@ -207,104 +217,6 @@ export function variantGaps(catalog, selection) {
207
217
  }
208
218
  return gaps;
209
219
  }
210
- /** Последнее звено имени: `application/money` → `money`. Им ресурсы и ссылаются друг на друга. */
211
- const shortNameOf = (entry) => entry.name.split('/').pop() ?? entry.name;
212
- /**
213
- * Ресурсы, снятые каскадом: правила при невзятом законе и паттерны при невзятых правилах.
214
- *
215
- * Связь читается из вступления самого ресурса — отдельный список при пакете разошёлся бы с
216
- * ресурсами молча, тем же молчанием, ради которого каскад и заводится. Родитель ищется по
217
- * последнему звену имени: полного пути в шапке нет, потому что переезд закона между слоями
218
- * переписывал бы шапки всех правил при нём.
219
- *
220
- * Родителя в каталоге нет вовсе — каскад молчит: снимать не по чему, а промах в шапке судит
221
- * сверка связности пакета, до всякой раскладки в дереве.
222
- */
223
- export function cascadeCuts(catalog, selection) {
224
- const takenOf = (kind) => new Set(catalog.filter((entry) => entry.kind === kind && isChosen(entry, selection)).map(shortNameOf));
225
- const knownOf = (kind) => new Set(catalog.filter((entry) => entry.kind === kind).map(shortNameOf));
226
- const takenLaws = takenOf('laws');
227
- const knownLaws = knownOf('laws');
228
- const takenRules = takenOf('rules');
229
- const knownRules = knownOf('rules');
230
- const cuts = [];
231
- /** Правило, снятое законом: для паттернов при нём корнем цепочки будет этот закон. */
232
- const rootByRule = new Map();
233
- for (const entry of catalog) {
234
- if (entry.kind !== 'rules' || !isChosen(entry, selection)) {
235
- continue;
236
- }
237
- const law = frontMatterOf(entry.text).law;
238
- if (!law || !knownLaws.has(law) || takenLaws.has(law)) {
239
- continue;
240
- }
241
- cuts.push({ id: entry.id, parent: law, root: law });
242
- rootByRule.set(shortNameOf(entry), law);
243
- }
244
- for (const entry of catalog) {
245
- if (entry.kind !== 'patterns' || !isChosen(entry, selection)) {
246
- continue;
247
- }
248
- const rule = frontMatterOf(entry.text).rule;
249
- if (!rule || !knownRules.has(rule) || (takenRules.has(rule) && !rootByRule.has(rule))) {
250
- continue;
251
- }
252
- cuts.push({ id: entry.id, parent: rule, root: rootByRule.get(rule) ?? rule });
253
- }
254
- return cuts;
255
- }
256
- /**
257
- * Ресурсы, названные выбором поимённо и всё равно снятые каскадом.
258
- *
259
- * Дерево, назвавшее правило в выборе при невыбранном законе, не получает его молча — то самое
260
- * молчание, ради которого каскад и заводится, только с другой стороны: там о снятии не знал тот,
261
- * кто ничего не просил, здесь — тот, кто попросил прямо.
262
- */
263
- export function namedButCut(catalog, selection) {
264
- return cascadeCuts(catalog, selection).filter((one) => selection.only.includes(one.id));
265
- }
266
- /**
267
- * Что ложится в дерево: прямой отбор за вычетом снятого каскадом.
268
- *
269
- * Отдельной функцией, а не внутри `isChosen`, потому что каскад знает весь каталог, а отбор —
270
- * один ресурс: связь родителя с потомком по одной записи не видна.
271
- */
272
- export function chosenEntries(catalog, selection) {
273
- const cut = new Set(cascadeCuts(catalog, selection).map((one) => one.id));
274
- return catalog.filter((entry) => isChosen(entry, selection) && !cut.has(entry.id));
275
- }
276
- /**
277
- * Строки отказа, которые ничего не снимают: названное ими снято каскадом или в каталоге не
278
- * значится вовсе.
279
- *
280
- * Не отказ, а предупреждение: настройка, верная до обновления пакета, становится лишней сама,
281
- * без единой правки в дереве, и отбивать за это раскладку значило бы наказывать за вчерашнюю
282
- * правоту.
283
- *
284
- * Строка на ресурс чужого вида лишней не считается никогда: ею дерево гасит отказ о ресурсе,
285
- * у которого нет подходящего вида, — то есть работу она делает.
286
- */
287
- export function idleSkips(catalog, selection) {
288
- const idle = [];
289
- for (const id of selection.skip) {
290
- const entry = catalog.find((one) => one.id === id);
291
- if (!entry) {
292
- idle.push({ id, by: '' });
293
- continue;
294
- }
295
- if (!matchesVariant(entry.variant, selection.variants)) {
296
- continue;
297
- }
298
- // Каскад считается от отбора без этой строки: иначе снятое ею самой читалось бы как
299
- // снятое родителем, и лишней оказалась бы любая строка отказа подряд.
300
- const without = { ...selection, skip: selection.skip.filter((one) => one !== id) };
301
- const cut = cascadeCuts(catalog, without).find((one) => one.id === id);
302
- if (cut) {
303
- idle.push({ id, by: cut.root });
304
- }
305
- }
306
- return idle;
307
- }
308
220
  /**
309
221
  * Связи, разорванные выбором дерева.
310
222
  *
@@ -336,9 +248,11 @@ export function brokenLinks(catalog, selection) {
336
248
  */
337
249
  export function idOf(spoken, kind, catalog) {
338
250
  const wanted = spoken.trim();
339
- const shortOf = (entry) => entry.name.split('/').pop() ?? entry.name;
340
- const found = catalog.find((entry) => entry.kind === kind &&
341
- (entry.id === wanted || entry.name === wanted || shortOf(entry) === wanted || entry.id.endsWith(`/${wanted}`)));
251
+ const named = (entry) => {
252
+ const short = entry.name.split('/').pop() ?? entry.name;
253
+ return entry.id === wanted || entry.name === wanted || short === wanted || entry.id.endsWith(`/${wanted}`);
254
+ };
255
+ const found = catalog.find((entry) => entry.kind === kind && named(entry));
342
256
  return found ? found.id : null;
343
257
  }
344
258
  /** Разбор `--laws access,delivery`: имя без ответа — промах, и молчать о нём нельзя. */
@@ -353,6 +267,9 @@ export function resolveSelection(spoken, kind, catalog) {
353
267
  else if (!ids.includes(id)) {
354
268
  ids.push(id);
355
269
  }
270
+ else {
271
+ // Тот же ресурс назван дважды: повтор в списке ничего не меняет
272
+ }
356
273
  }
357
274
  return { ids, unknown };
358
275
  }
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/catalog.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Что пакет везёт — прочитанное с диска, до всякого выбора проекта.\n *\n * Каталог нужен отдельно от раскладки, потому что выбирать приходится раньше, чем раскладывать:\n * `init` спрашивает, какие законы взять, а `list` показывает и то, от чего проект отказался.\n * И там и там нужен полный набор, а `collectAssets` по устройству отдаёт уже отобранный.\n */\nimport { accessSync, constants, Dirent, readdirSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { KINDS, TKind } from './config.js';\nimport { answersRequirement, requirementOf, withoutRequirement } from './traits.js';\nimport { IAxis, IVariant, matchesVariant, readAxes, variantOf, withoutVariant } from './variants.js';\n\nexport interface IEntryOfCatalog {\n /** Идентификатор: `laws/application/money.md`. Он же путь надстройки и ключ в `only` и `skip`. */\n readonly id: string;\n readonly kind: TKind;\n /**\n * Имя без рода, вида и расширения: `delivery`, `application/money`. Им ресурс называют в\n * строке запуска, и им же он ложится в дерево проекта.\n */\n readonly name: string;\n /** Заголовок первой строки файла; у файла без заголовка — его имя. */\n readonly title: string;\n /** Вид ресурса, если он лежит в пакете в нескольких: `git-workflow.github.md`. */\n readonly variant: IVariant | null;\n /**\n * Свойство дерева, без которого этот ресурс дереву не нужен: `observability.needs-db.md`.\n *\n * От вида отличается вопросом. Вид спрашивает «какой из трёх» и выбирается один; требование\n * спрашивает «есть ли», и у дерева таких свойств сразу несколько. Ресурс без требования —\n * `null`: он верен всякому дереву.\n */\n readonly needs: string | null;\n readonly text: string;\n /**\n * Запускается ли файл сам по себе. Гард зовут по пути, а не через оболочку, и файл без\n * права на запуск отвечает отказом доступа — то есть ненулевым кодом, который читается как\n * «правка отбита». Признак берётся с файла в пакете и переносится на разложенный.\n */\n readonly executable: boolean;\n /**\n * Ресурсы, без которых этот неисполним, — их идентификаторы.\n *\n * Читается из шапки самого ресурса, а не из отдельного списка при пакете: список разошёлся\n * бы с ресурсами молча, ровно тем же молчанием, ради которого требование и заведено. Паттерн\n * возвращения к работе начинается словами «хук запуска сессии отдал замысел и ход работы» —\n * без хука он неисполним, и до этой строки узнать об этом было нечем.\n */\n readonly requires: readonly string[];\n}\n\nconst TITLE: RegExp = /^#\\s+(\\S.*)$/m;\n\n/** Что объявила шапка ресурса. Пустое поле и отсутствующее здесь одно и то же. */\nexport interface IFrontMatter {\n readonly name: string;\n readonly kind: string;\n /** Закон, под которым стоит правило. */\n readonly law: string;\n /** Правило, при котором стоит паттерн. */\n readonly rule: string;\n}\n\nconst FIELD: RegExp = /^([a-z]+):\\s*(\\S.*?)\\s*$/;\n\n/**\n * Вступление между `---` в начале файла; его нет — вернётся пустая шапка.\n *\n * Живёт при каталоге, потому что связь родителя с потомком читают двое: сверка связности пакета\n * и каскад отказа. Объявление у одного из них заводило бы круговой импорт между ними.\n */\nexport function frontMatterOf(text: string): IFrontMatter {\n const lines: readonly string[] = text.split('\\n');\n const found: Record<string, string> = {};\n\n if (lines[0]?.trim() === '---') {\n for (const line of lines.slice(1)) {\n if (line.trim() === '---') {\n break;\n }\n const match: RegExpMatchArray | null = line.match(FIELD);\n if (match) {\n found[match[1]] = match[2];\n }\n }\n }\n\n return { name: found['name'] ?? '', kind: found['kind'] ?? '', law: found['law'] ?? '', rule: found['rule'] ?? '' };\n}\n\n/**\n * Строка требования в шапке ресурса. Двух видов, потому что ресурсы двух родов: у разметки\n * `**Требует:**`, у исполняемого файла — комментарий `# Требует:`.\n */\nconst REQUIRES: RegExp = /^(?:#\\s*Требует:|\\*\\*Требует:\\*\\*)\\s*(.+)$/m;\n\n/**\n * Что ресурс о себе объявил. Имена читаются идентификаторами — `hooks/observe.sh`: короткая\n * форма совпала бы у закона и правила с одним именем, а требование, указавшее не туда, хуже\n * ненайденного.\n */\nexport function requiresOf(text: string): readonly string[] {\n const found: RegExpMatchArray | null = text.match(REQUIRES);\n if (!found) {\n return [];\n }\n\n return found[1]\n .split(',')\n .map((name: string): string => name.trim().replace(/^`|`$/g, '').trim())\n .filter((name: string): boolean => name.length > 0);\n}\n\n/** Запускается ли файл сам по себе. Спрашиваем систему, а не разбираем биты режима руками. */\nfunction isExecutable(path: string): boolean {\n try {\n accessSync(path, constants.X_OK);\n\n return true;\n } catch {\n return false;\n }\n}\n\n/** Заголовок нужен человеку, который выбирает: имена законов ему ни о чём не говорят. */\nexport function titleOf(text: string, fallback: string): string {\n const found: RegExpMatchArray | null = text.match(TITLE);\n\n return found ? found[1].trim() : fallback;\n}\n\n/**\n * Файлы рода — вместе с теми, что лежат в подкаталогах.\n *\n * Обход рекурсивный потому, что у законов два слоя: общий закон верен любому приложению этого\n * класса, а закон приложения — только такому, где есть деньги, локали или владеющая сущность.\n * Слой отражён каталогом, и путь внутри рода едет в идентификатор целиком, чтобы разложенное\n * повторяло раскладку пакета.\n */\nfunction filesOf(dir: string, prefix: string = ''): readonly string[] {\n let entries: readonly Dirent[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n\n const files: string[] = [];\n for (const entry of [...entries].sort((one: Dirent, two: Dirent): number => one.name.localeCompare(two.name))) {\n if (entry.isFile()) {\n files.push(`${prefix}${entry.name}`);\n } else if (entry.isDirectory()) {\n files.push(...filesOf(join(dir, entry.name), `${prefix}${entry.name}/`));\n }\n }\n\n return files;\n}\n\n/**\n * Всё, что лежит в ресурсах пакета. Список не выписан руками, а читается из каталога:\n * выписанный разошёлся бы с ним на первом же добавленном законе, и заметить это было бы\n * нечем — ресурс просто не приезжал бы к проекту.\n */\nexport function readCatalog(assetsDir: string): readonly IEntryOfCatalog[] {\n const axes: readonly IAxis[] = readAxes(assetsDir);\n const entries: IEntryOfCatalog[] = [];\n\n for (const kind of KINDS) {\n for (const file of filesOf(join(assetsDir, kind))) {\n const path: string = join(assetsDir, kind, file);\n const text: string = readFileSync(path, 'utf8');\n const variant: IVariant | null = variantOf(file, axes);\n const needs: string | null = requirementOf(withoutVariant(file, variant));\n const name: string = withoutRequirement(withoutVariant(file, variant), needs).replace(/\\.[^./]+$/, '');\n const executable: boolean = isExecutable(path);\n entries.push({\n id: `${kind}/${file}`,\n kind,\n name,\n title: titleOf(text, name),\n variant,\n text,\n needs,\n executable,\n requires: requiresOf(text),\n });\n }\n }\n\n return entries;\n}\n\n/** Чем проект ограничил раскладку: выбором, отказом, видами и свойствами, которые он назвал. */\nexport interface ISelection {\n readonly only: readonly string[];\n readonly skip: readonly string[];\n readonly variants: Readonly<Record<string, string>>;\n /**\n * Свойства дерева: что у него есть. Пусто и не названо вовсе — одно и то же: дерево о себе\n * ничего не сказало и помеченного требованием не получает.\n */\n readonly has?: readonly string[];\n}\n\n/**\n * Взят ли ресурс при этом выборе.\n *\n * `only` ограничивает **только те роды, которые сам называет**. Проект, выбравший девять\n * законов из пятнадцати, шаблоны при этом не терял бы: перечисляя законы, он говорит о\n * законах, а не обо всём, что пакет везёт. Обратное правило читалось бы как «выбрал законы —\n * остался без шаблонов», и заметил бы это только тот, кто пошёл за шаблоном.\n *\n * `skip` вычитает из выбранного. Отказ от одного закона не должен требовать переписать\n * список из пятнадцати, а точечное исключение остаётся точечным.\n *\n * Вид отбирается раньше обоих: ресурс чужого хостинга не «не выбран» — его в этом дереве не\n * существует вовсе, и называть его в `skip` проекту незачем.\n *\n * Требование к свойству дерева слабее выбора поимённо: дерево, назвавшее ресурс словом, знает\n * про своё дерево больше, чем пакет выводит по признаку. Молчаливым такое исключение не\n * остаётся — неотвеченное требование называет перечень раскладки.\n */\nexport function isChosen(entry: IEntryOfCatalog, selection: ISelection): boolean {\n if (!matchesVariant(entry.variant, selection.variants)) {\n return false;\n }\n if (selection.skip.includes(entry.id)) {\n return false;\n }\n const restricted: boolean = selection.only.some((id: string): boolean => id.startsWith(`${entry.kind}/`));\n if (restricted) {\n return selection.only.includes(entry.id);\n }\n\n return answersRequirement(entry.needs, selection.has ?? []);\n}\n\n/** Ресурс, у которого виды есть, но ни один не отвечает тому, что выбрало дерево. */\nexport interface IGapOfVariant {\n readonly kind: TKind;\n /** Имя без вида — под ним ресурс лёг бы в дерево. */\n readonly name: string;\n readonly axis: string;\n /** Что выбрало дерево по этой оси. */\n readonly chosen: string;\n /** Виды, которые у ресурса есть на самом деле. */\n readonly available: readonly string[];\n /** Идентификаторы этих видов: ими же пробел и снимается через `skip`. */\n readonly ids: readonly string[];\n}\n\n/**\n * Ресурсы, оставшиеся без вида под выбор дерева.\n *\n * Молчаливый пропуск такого ресурса — худший из исходов. Правило поставки едет тремя видами, а\n * команды заведения задачи и сверки очереди — одним: дерево, выбравшее чужой хостинг, получает\n * правило, которое зовёт три команды, и ни одного скрипта под ними. Отказа при этом не бывает —\n * ресурс просто не приезжает, и узнают об этом, когда команда из правила не находится.\n *\n * Отсюда правило: **ресурс с видами, ни один из которых не совпал, — отказ, а не пропуск.**\n * Снимается он двумя способами, и оба явные: завести недостающий вид в пакете либо назвать\n * существующие в `skip` — дерево тем самым говорит, что обходится без этого ресурса.\n */\nexport function variantGaps(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly IGapOfVariant[] {\n const groups: Map<string, IEntryOfCatalog[]> = new Map();\n for (const entry of catalog) {\n if (entry.variant === null) {\n continue;\n }\n const key: string = `${entry.kind}/${entry.name}`;\n groups.set(key, [...(groups.get(key) ?? []), entry]);\n }\n\n const gaps: IGapOfVariant[] = [];\n for (const entries of groups.values()) {\n const axis: string = entries[0].variant?.axis ?? '';\n if (entries.some((entry: IEntryOfCatalog): boolean => matchesVariant(entry.variant, selection.variants))) {\n continue;\n }\n // Отказ от ресурса — законный ответ, и повторять его отказом раскладки незачем: дерево\n // уже сказало, что обходится без него.\n if (entries.every((entry: IEntryOfCatalog): boolean => selection.skip.includes(entry.id))) {\n continue;\n }\n // Род, суженный через `only`, отбирает ресурсы поимённо: не названный в нём ресурс не\n // пропал — его не просили.\n const restricted: boolean = selection.only.some((id: string): boolean => id.startsWith(`${entries[0].kind}/`));\n if (restricted && !entries.some((entry: IEntryOfCatalog): boolean => selection.only.includes(entry.id))) {\n continue;\n }\n gaps.push({\n kind: entries[0].kind,\n name: entries[0].name,\n axis,\n chosen: selection.variants[axis] ?? '',\n available: entries.map((entry: IEntryOfCatalog): string => entry.variant?.value ?? ''),\n ids: entries.map((entry: IEntryOfCatalog): string => entry.id),\n });\n }\n\n return gaps;\n}\n\n/** Ресурс, снятый вслед за отвергнутым родителем. */\nexport interface ICascadeCut {\n readonly id: string;\n /** Ближайший родитель: у правила — его закон, у паттерна — его правило. */\n readonly parent: string;\n /**\n * Отвергнутый корень цепочки — то, что дерево действительно отвергло или не выбрало.\n * Ближайшего родителя мало: паттерн уходит вслед за правилом, которого в отказе нет, и\n * искать его там читатель пойдёт зря.\n */\n readonly root: string;\n}\n\n/** Последнее звено имени: `application/money` → `money`. Им ресурсы и ссылаются друг на друга. */\nconst shortNameOf: (entry: IEntryOfCatalog) => string = (entry: IEntryOfCatalog): string => entry.name.split('/').pop() ?? entry.name;\n\n/**\n * Ресурсы, снятые каскадом: правила при невзятом законе и паттерны при невзятых правилах.\n *\n * Связь читается из вступления самого ресурса — отдельный список при пакете разошёлся бы с\n * ресурсами молча, тем же молчанием, ради которого каскад и заводится. Родитель ищется по\n * последнему звену имени: полного пути в шапке нет, потому что переезд закона между слоями\n * переписывал бы шапки всех правил при нём.\n *\n * Родителя в каталоге нет вовсе — каскад молчит: снимать не по чему, а промах в шапке судит\n * сверка связности пакета, до всякой раскладки в дереве.\n */\nexport function cascadeCuts(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly ICascadeCut[] {\n const takenOf: (kind: TKind) => ReadonlySet<string> = (kind: TKind): ReadonlySet<string> =>\n new Set(catalog.filter((entry: IEntryOfCatalog): boolean => entry.kind === kind && isChosen(entry, selection)).map(shortNameOf));\n const knownOf: (kind: TKind) => ReadonlySet<string> = (kind: TKind): ReadonlySet<string> =>\n new Set(catalog.filter((entry: IEntryOfCatalog): boolean => entry.kind === kind).map(shortNameOf));\n\n const takenLaws: ReadonlySet<string> = takenOf('laws');\n const knownLaws: ReadonlySet<string> = knownOf('laws');\n const takenRules: ReadonlySet<string> = takenOf('rules');\n const knownRules: ReadonlySet<string> = knownOf('rules');\n\n const cuts: ICascadeCut[] = [];\n /** Правило, снятое законом: для паттернов при нём корнем цепочки будет этот закон. */\n const rootByRule: Map<string, string> = new Map<string, string>();\n\n for (const entry of catalog) {\n if (entry.kind !== 'rules' || !isChosen(entry, selection)) {\n continue;\n }\n const law: string = frontMatterOf(entry.text).law;\n if (!law || !knownLaws.has(law) || takenLaws.has(law)) {\n continue;\n }\n cuts.push({ id: entry.id, parent: law, root: law });\n rootByRule.set(shortNameOf(entry), law);\n }\n\n for (const entry of catalog) {\n if (entry.kind !== 'patterns' || !isChosen(entry, selection)) {\n continue;\n }\n const rule: string = frontMatterOf(entry.text).rule;\n if (!rule || !knownRules.has(rule) || (takenRules.has(rule) && !rootByRule.has(rule))) {\n continue;\n }\n cuts.push({ id: entry.id, parent: rule, root: rootByRule.get(rule) ?? rule });\n }\n\n return cuts;\n}\n\n/**\n * Ресурсы, названные выбором поимённо и всё равно снятые каскадом.\n *\n * Дерево, назвавшее правило в выборе при невыбранном законе, не получает его молча — то самое\n * молчание, ради которого каскад и заводится, только с другой стороны: там о снятии не знал тот,\n * кто ничего не просил, здесь — тот, кто попросил прямо.\n */\nexport function namedButCut(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly ICascadeCut[] {\n return cascadeCuts(catalog, selection).filter((one: ICascadeCut): boolean => selection.only.includes(one.id));\n}\n\n/**\n * Что ложится в дерево: прямой отбор за вычетом снятого каскадом.\n *\n * Отдельной функцией, а не внутри `isChosen`, потому что каскад знает весь каталог, а отбор —\n * один ресурс: связь родителя с потомком по одной записи не видна.\n */\nexport function chosenEntries(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly IEntryOfCatalog[] {\n const cut: ReadonlySet<string> = new Set(cascadeCuts(catalog, selection).map((one: ICascadeCut): string => one.id));\n\n return catalog.filter((entry: IEntryOfCatalog): boolean => isChosen(entry, selection) && !cut.has(entry.id));\n}\n\n/** Строка отказа, которая ничего не снимает. */\nexport interface IIdleSkip {\n readonly id: string;\n /** Чем названное ею уже снято: имя отвергнутого родителя, либо пусто — такого ресурса нет. */\n readonly by: string;\n}\n\n/**\n * Строки отказа, которые ничего не снимают: названное ими снято каскадом или в каталоге не\n * значится вовсе.\n *\n * Не отказ, а предупреждение: настройка, верная до обновления пакета, становится лишней сама,\n * без единой правки в дереве, и отбивать за это раскладку значило бы наказывать за вчерашнюю\n * правоту.\n *\n * Строка на ресурс чужого вида лишней не считается никогда: ею дерево гасит отказ о ресурсе,\n * у которого нет подходящего вида, — то есть работу она делает.\n */\nexport function idleSkips(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly IIdleSkip[] {\n const idle: IIdleSkip[] = [];\n\n for (const id of selection.skip) {\n const entry: IEntryOfCatalog | undefined = catalog.find((one: IEntryOfCatalog): boolean => one.id === id);\n if (!entry) {\n idle.push({ id, by: '' });\n continue;\n }\n if (!matchesVariant(entry.variant, selection.variants)) {\n continue;\n }\n // Каскад считается от отбора без этой строки: иначе снятое ею самой читалось бы как\n // снятое родителем, и лишней оказалась бы любая строка отказа подряд.\n const without: ISelection = { ...selection, skip: selection.skip.filter((one: string): boolean => one !== id) };\n const cut: ICascadeCut | undefined = cascadeCuts(catalog, without).find((one: ICascadeCut): boolean => one.id === id);\n if (cut) {\n idle.push({ id, by: cut.root });\n }\n }\n\n return idle;\n}\n\n/** Выбранный ресурс, чьё требование в дерево не поехало. */\nexport interface IBrokenLink {\n /** Кто требует — идентификатор выбранного ресурса. */\n readonly id: string;\n /** Чего не хватает — идентификатор требования. */\n readonly requires: string;\n /** Требования нет в пакете вовсе: промах в шапке ресурса, а не выбор дерева. */\n readonly unknown: boolean;\n}\n\n/**\n * Связи, разорванные выбором дерева.\n *\n * Не отказ, а предупреждение — и это главное свойство. Дерево вправе закрыть требование своим\n * средством: паттерн исполним и с чужим хуком, если тот делает то же самое. Отказ здесь отбивал\n * бы законную раскладку, а молчание оставляет дерево исправным на вид — сверка зелена на любом\n * подмножестве, сколько бы связок ни было разорвано.\n */\nexport function brokenLinks(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly IBrokenLink[] {\n const known: ReadonlySet<string> = new Set(catalog.map((entry: IEntryOfCatalog): string => entry.id));\n const chosen: ReadonlySet<string> = new Set(\n catalog.filter((entry: IEntryOfCatalog): boolean => isChosen(entry, selection)).map((entry: IEntryOfCatalog): string => entry.id)\n );\n\n const broken: IBrokenLink[] = [];\n for (const entry of catalog) {\n if (!chosen.has(entry.id)) {\n continue;\n }\n for (const required of entry.requires) {\n if (chosen.has(required)) {\n continue;\n }\n broken.push({ id: entry.id, requires: required, unknown: !known.has(required) });\n }\n }\n\n return broken;\n}\n\n/**\n * Идентификатор по тому, как ресурс назвали в строке запуска. Принимаются все формы, которыми\n * его называют вслух: `money`, `application/money`, `money.md` и `laws/application/money.md`.\n */\nexport function idOf(spoken: string, kind: TKind, catalog: readonly IEntryOfCatalog[]): string | null {\n const wanted: string = spoken.trim();\n const shortOf: (entry: IEntryOfCatalog) => string = (entry: IEntryOfCatalog): string => entry.name.split('/').pop() ?? entry.name;\n const found: IEntryOfCatalog | undefined = catalog.find(\n (entry: IEntryOfCatalog): boolean =>\n entry.kind === kind &&\n (entry.id === wanted || entry.name === wanted || shortOf(entry) === wanted || entry.id.endsWith(`/${wanted}`))\n );\n\n return found ? found.id : null;\n}\n\nexport interface IResolvedSelection {\n readonly ids: readonly string[];\n /** Имена, которым в каталоге ничего не отвечает: промах в строке запуска, а не выбор. */\n readonly unknown: readonly string[];\n}\n\n/** Разбор `--laws access,delivery`: имя без ответа — промах, и молчать о нём нельзя. */\nexport function resolveSelection(spoken: readonly string[], kind: TKind, catalog: readonly IEntryOfCatalog[]): IResolvedSelection {\n const ids: string[] = [];\n const unknown: string[] = [];\n\n for (const name of spoken) {\n const id: string | null = idOf(name, kind, catalog);\n if (id === null) {\n unknown.push(name.trim());\n } else if (!ids.includes(id)) {\n ids.push(id);\n }\n }\n\n return { ids, unknown };\n}\n"]}
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/catalog.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Что пакет везёт — прочитанное с диска, до всякого выбора проекта.\n *\n * Каталог нужен отдельно от раскладки, потому что выбирать приходится раньше, чем раскладывать:\n * `init` спрашивает, какие законы взять, а `list` показывает и то, от чего проект отказался.\n * И там и там нужен полный набор, а `collectAssets` по устройству отдаёт уже отобранный.\n */\nimport { accessSync, constants, Dirent, readdirSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { KINDS, TKind } from './config.js';\nimport { answersRequirement, requirementOf, withoutRequirement } from './traits.js';\nimport { IAxis, IVariant, matchesVariant, readAxes, variantOf, withoutVariant } from './variants.js';\n\nexport interface IEntryOfCatalog {\n /** Идентификатор: `laws/application/money.md`. Он же путь надстройки и ключ в `only` и `skip`. */\n readonly id: string;\n readonly kind: TKind;\n /**\n * Имя без рода, вида и расширения: `delivery`, `application/money`. Им ресурс называют в\n * строке запуска, и им же он ложится в дерево проекта.\n */\n readonly name: string;\n /** Заголовок первой строки файла; у файла без заголовка — его имя. */\n readonly title: string;\n /** Вид ресурса, если он лежит в пакете в нескольких: `git-workflow.github.md`. */\n readonly variant: IVariant | null;\n /**\n * Свойство дерева, без которого этот ресурс дереву не нужен: `observability.needs-db.md`.\n *\n * От вида отличается вопросом. Вид спрашивает «какой из трёх» и выбирается один; требование\n * спрашивает «есть ли», и у дерева таких свойств сразу несколько. Ресурс без требования —\n * `null`: он верен всякому дереву.\n */\n readonly needs: string | null;\n readonly text: string;\n /**\n * Запускается ли файл сам по себе. Гард зовут по пути, а не через оболочку, и файл без\n * права на запуск отвечает отказом доступа — то есть ненулевым кодом, который читается как\n * «правка отбита». Признак берётся с файла в пакете и переносится на разложенный.\n */\n readonly executable: boolean;\n /**\n * Ресурсы, без которых этот неисполним, — их идентификаторы.\n *\n * Читается из шапки самого ресурса, а не из отдельного списка при пакете: список разошёлся\n * бы с ресурсами молча, ровно тем же молчанием, ради которого требование и заведено. Паттерн\n * возвращения к работе начинается словами «хук запуска сессии отдал замысел и ход работы» —\n * без хука он неисполним, и до этой строки узнать об этом было нечем.\n */\n readonly requires: readonly string[];\n}\n\nconst TITLE: RegExp = /^#\\s+(\\S.*)$/m;\n\n/** Что объявила шапка ресурса. Пустое поле и отсутствующее здесь одно и то же. */\nexport interface IFrontMatter {\n readonly name: string;\n readonly kind: string;\n /** Закон, под которым стоит правило. */\n readonly law: string;\n /** Правило, при котором стоит паттерн. */\n readonly rule: string;\n}\n\nconst FIELD: RegExp = /^([a-z]+):(.*)$/;\n\n/**\n * Вступление между `---` в начале файла; его нет — вернётся пустая шапка.\n *\n * Живёт при каталоге, потому что связь родителя с потомком читают двое: сверка связности пакета\n * и каскад отказа. Объявление у одного из них заводило бы круговой импорт между ними.\n */\nfunction rememberField(found: Record<string, string>, line: string): void {\n const match: RegExpExecArray | null = FIELD.exec(line);\n const value: string = match ? match[2].trim() : '';\n\n if (match && value) {\n found[match[1]] = value;\n }\n}\n\nexport function frontMatterOf(text: string): IFrontMatter {\n const lines: readonly string[] = text.split('\\n');\n const found: Record<string, string> = {};\n\n if (lines[0]?.trim() === '---') {\n for (const line of lines.slice(1)) {\n if (line.trim() === '---') {\n break;\n }\n rememberField(found, line);\n }\n }\n\n return { name: found['name'] ?? '', kind: found['kind'] ?? '', law: found['law'] ?? '', rule: found['rule'] ?? '' };\n}\n\n/**\n * Строка требования в шапке ресурса. Двух видов, потому что ресурсы двух родов: у разметки\n * `**Требует:**`, у исполняемого файла — комментарий `# Требует:`.\n */\nconst REQUIRES: RegExp = /^(?:#[^\\S\\n]*Требует:|\\*\\*Требует:\\*\\*)([^\\n]*)$/m;\n\n/**\n * Что ресурс о себе объявил. Имена читаются идентификаторами — `hooks/observe.sh`: короткая\n * форма совпала бы у закона и правила с одним именем, а требование, указавшее не туда, хуже\n * ненайденного.\n */\nexport function requiresOf(text: string): readonly string[] {\n const found: RegExpExecArray | null = REQUIRES.exec(text);\n if (!found) {\n return [];\n }\n\n return found[1]\n .split(',')\n .map((name: string): string => name.trim().replace(/^`|`$/g, '').trim())\n .filter((name: string): boolean => name.length > 0);\n}\n\n/** Запускается ли файл сам по себе. Спрашиваем систему, а не разбираем биты режима руками. */\nfunction isExecutable(path: string): boolean {\n try {\n accessSync(path, constants.X_OK);\n\n return true;\n } catch {\n return false;\n }\n}\n\n/** Заголовок нужен человеку, который выбирает: имена законов ему ни о чём не говорят. */\nexport function titleOf(text: string, fallback: string): string {\n const found: RegExpExecArray | null = TITLE.exec(text);\n\n return found ? found[1].trim() : fallback;\n}\n\n/**\n * Файлы рода — вместе с теми, что лежат в подкаталогах.\n *\n * Обход рекурсивный потому, что у законов два слоя: общий закон верен любому приложению этого\n * класса, а закон приложения — только такому, где есть деньги, локали или владеющая сущность.\n * Слой отражён каталогом, и путь внутри рода едет в идентификатор целиком, чтобы разложенное\n * повторяло раскладку пакета.\n */\nfunction filesOf(dir: string, prefix: string = ''): readonly string[] {\n let entries: readonly Dirent[];\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n\n const files: string[] = [];\n for (const entry of [...entries].sort((one: Dirent, two: Dirent): number => one.name.localeCompare(two.name))) {\n if (entry.isFile()) {\n files.push(`${prefix}${entry.name}`);\n } else if (entry.isDirectory()) {\n files.push(...filesOf(join(dir, entry.name), `${prefix}${entry.name}/`));\n } else {\n // Ни файл, ни каталог — связь или узел устройства: ресурсом пакета это не бывает\n }\n }\n\n return files;\n}\n\n/**\n * Всё, что лежит в ресурсах пакета. Список не выписан руками, а читается из каталога:\n * выписанный разошёлся бы с ним на первом же добавленном законе, и заметить это было бы\n * нечем — ресурс просто не приезжал бы к проекту.\n */\nexport function readCatalog(assetsDir: string): readonly IEntryOfCatalog[] {\n const axes: readonly IAxis[] = readAxes(assetsDir);\n const entries: IEntryOfCatalog[] = [];\n\n for (const kind of KINDS) {\n for (const file of filesOf(join(assetsDir, kind))) {\n const path: string = join(assetsDir, kind, file);\n const text: string = readFileSync(path, 'utf8');\n const variant: IVariant | null = variantOf(file, axes);\n const needs: string | null = requirementOf(withoutVariant(file, variant));\n const name: string = withoutRequirement(withoutVariant(file, variant), needs).replace(/\\.[^./]+$/, '');\n const executable: boolean = isExecutable(path);\n entries.push({\n id: `${kind}/${file}`,\n title: titleOf(text, name),\n requires: requiresOf(text),\n kind,\n name,\n variant,\n text,\n needs,\n executable,\n });\n }\n }\n\n return entries;\n}\n\n/** Чем проект ограничил раскладку: выбором, отказом, видами и свойствами, которые он назвал. */\nexport interface ISelection {\n readonly only: readonly string[];\n readonly skip: readonly string[];\n readonly variants: Readonly<Record<string, string>>;\n /**\n * Свойства дерева: что у него есть. Пусто и не названо вовсе — одно и то же: дерево о себе\n * ничего не сказало и помеченного требованием не получает.\n */\n readonly has?: readonly string[];\n}\n\n/**\n * Взят ли ресурс при этом выборе.\n *\n * `only` ограничивает **только те роды, которые сам называет**. Проект, выбравший девять\n * законов из пятнадцати, шаблоны при этом не терял бы: перечисляя законы, он говорит о\n * законах, а не обо всём, что пакет везёт. Обратное правило читалось бы как «выбрал законы —\n * остался без шаблонов», и заметил бы это только тот, кто пошёл за шаблоном.\n *\n * `skip` вычитает из выбранного. Отказ от одного закона не должен требовать переписать\n * список из пятнадцати, а точечное исключение остаётся точечным.\n *\n * Вид отбирается раньше обоих: ресурс чужого хостинга не «не выбран» — его в этом дереве не\n * существует вовсе, и называть его в `skip` проекту незачем.\n *\n * Требование к свойству дерева слабее выбора поимённо: дерево, назвавшее ресурс словом, знает\n * про своё дерево больше, чем пакет выводит по признаку. Молчаливым такое исключение не\n * остаётся — неотвеченное требование называет перечень раскладки.\n */\nexport function isChosen(entry: IEntryOfCatalog, selection: ISelection): boolean {\n if (!matchesVariant(entry.variant, selection.variants)) {\n return false;\n }\n if (selection.skip.includes(entry.id)) {\n return false;\n }\n const restricted: boolean = selection.only.some((id: string): boolean => id.startsWith(`${entry.kind}/`));\n if (restricted) {\n return selection.only.includes(entry.id);\n }\n\n return answersRequirement(entry.needs, selection.has ?? []);\n}\n\n/** Ресурс, у которого виды есть, но ни один не отвечает тому, что выбрало дерево. */\nexport interface IGapOfVariant {\n readonly kind: TKind;\n /** Имя без вида — под ним ресурс лёг бы в дерево. */\n readonly name: string;\n readonly axis: string;\n /** Что выбрало дерево по этой оси. */\n readonly chosen: string;\n /** Виды, которые у ресурса есть на самом деле. */\n readonly available: readonly string[];\n /** Идентификаторы этих видов: ими же пробел и снимается через `skip`. */\n readonly ids: readonly string[];\n}\n\n/**\n * Ресурсы, оставшиеся без вида под выбор дерева.\n *\n * Молчаливый пропуск такого ресурса — худший из исходов. Правило поставки едет тремя видами, а\n * команды заведения задачи и сверки очереди — одним: дерево, выбравшее чужой хостинг, получает\n * правило, которое зовёт три команды, и ни одного скрипта под ними. Отказа при этом не бывает —\n * ресурс просто не приезжает, и узнают об этом, когда команда из правила не находится.\n *\n * Отсюда правило: **ресурс с видами, ни один из которых не совпал, — отказ, а не пропуск.**\n * Снимается он двумя способами, и оба явные: завести недостающий вид в пакете либо назвать\n * существующие в `skip` — дерево тем самым говорит, что обходится без этого ресурса.\n */\n/**\n * Закрыт ли ресурс выбором: подходящий вид взят, все виды отвергнуты либо род сужен мимо него.\n *\n * Отказ от ресурса — законный ответ, и повторять его отказом раскладки незачем: дерево уже\n * сказало, что обходится без него. Род, суженный через `only`, отбирает ресурсы поимённо: не\n * названный в нём ресурс не пропал — его не просили.\n */\nfunction closedBySelection(entries: readonly IEntryOfCatalog[], selection: ISelection): boolean {\n if (entries.some((entry: IEntryOfCatalog): boolean => matchesVariant(entry.variant, selection.variants))) {\n return true;\n }\n\n if (entries.every((entry: IEntryOfCatalog): boolean => selection.skip.includes(entry.id))) {\n return true;\n }\n\n const restricted: boolean = selection.only.some((id: string): boolean => id.startsWith(`${entries[0].kind}/`));\n\n return restricted && !entries.some((entry: IEntryOfCatalog): boolean => selection.only.includes(entry.id));\n}\n\nexport function variantGaps(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly IGapOfVariant[] {\n const groups: Map<string, IEntryOfCatalog[]> = new Map();\n for (const entry of catalog) {\n if (entry.variant === null) {\n continue;\n }\n const key: string = `${entry.kind}/${entry.name}`;\n groups.set(key, [...(groups.get(key) ?? []), entry]);\n }\n\n const gaps: IGapOfVariant[] = [];\n for (const entries of [...groups.values()].filter((one: IEntryOfCatalog[]): boolean => !closedBySelection(one, selection))) {\n const axis: string = entries[0].variant?.axis ?? '';\n gaps.push({\n axis,\n kind: entries[0].kind,\n name: entries[0].name,\n chosen: selection.variants[axis] ?? '',\n available: entries.map((entry: IEntryOfCatalog): string => entry.variant?.value ?? ''),\n ids: entries.map((entry: IEntryOfCatalog): string => entry.id),\n });\n }\n\n return gaps;\n}\n\n/** Выбранный ресурс, чьё требование в дерево не поехало. */\nexport interface IBrokenLink {\n /** Кто требует — идентификатор выбранного ресурса. */\n readonly id: string;\n /** Чего не хватает — идентификатор требования. */\n readonly requires: string;\n /** Требования нет в пакете вовсе: промах в шапке ресурса, а не выбор дерева. */\n readonly unknown: boolean;\n}\n\n/**\n * Связи, разорванные выбором дерева.\n *\n * Не отказ, а предупреждение — и это главное свойство. Дерево вправе закрыть требование своим\n * средством: паттерн исполним и с чужим хуком, если тот делает то же самое. Отказ здесь отбивал\n * бы законную раскладку, а молчание оставляет дерево исправным на вид — сверка зелена на любом\n * подмножестве, сколько бы связок ни было разорвано.\n */\nexport function brokenLinks(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly IBrokenLink[] {\n const known: ReadonlySet<string> = new Set(catalog.map((entry: IEntryOfCatalog): string => entry.id));\n const chosen: ReadonlySet<string> = new Set(\n catalog.filter((entry: IEntryOfCatalog): boolean => isChosen(entry, selection)).map((entry: IEntryOfCatalog): string => entry.id)\n );\n\n const broken: IBrokenLink[] = [];\n for (const entry of catalog) {\n if (!chosen.has(entry.id)) {\n continue;\n }\n for (const required of entry.requires) {\n if (chosen.has(required)) {\n continue;\n }\n broken.push({ id: entry.id, requires: required, unknown: !known.has(required) });\n }\n }\n\n return broken;\n}\n\n/**\n * Идентификатор по тому, как ресурс назвали в строке запуска. Принимаются все формы, которыми\n * его называют вслух: `money`, `application/money`, `money.md` и `laws/application/money.md`.\n */\nexport function idOf(spoken: string, kind: TKind, catalog: readonly IEntryOfCatalog[]): string | null {\n const wanted: string = spoken.trim();\n const named: (entry: IEntryOfCatalog) => boolean = (entry: IEntryOfCatalog): boolean => {\n const short: string = entry.name.split('/').pop() ?? entry.name;\n\n return entry.id === wanted || entry.name === wanted || short === wanted || entry.id.endsWith(`/${wanted}`);\n };\n const found: IEntryOfCatalog | undefined = catalog.find((entry: IEntryOfCatalog): boolean => entry.kind === kind && named(entry));\n\n return found ? found.id : null;\n}\n\nexport interface IResolvedSelection {\n readonly ids: readonly string[];\n /** Имена, которым в каталоге ничего не отвечает: промах в строке запуска, а не выбор. */\n readonly unknown: readonly string[];\n}\n\n/** Разбор `--laws access,delivery`: имя без ответа — промах, и молчать о нём нельзя. */\nexport function resolveSelection(spoken: readonly string[], kind: TKind, catalog: readonly IEntryOfCatalog[]): IResolvedSelection {\n const ids: string[] = [];\n const unknown: string[] = [];\n\n for (const name of spoken) {\n const id: string | null = idOf(name, kind, catalog);\n if (id === null) {\n unknown.push(name.trim());\n } else if (!ids.includes(id)) {\n ids.push(id);\n } else {\n // Тот же ресурс назван дважды: повтор в списке ничего не меняет\n }\n }\n\n return { ids, unknown };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/commands.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAU7C,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,2FAA2F;AAC3F,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CACvC;AA0DD,+DAA+D;AAC/D,eAAO,MAAM,WAAW,EAAE,MAAyB,CAAC;AAuSpD;;;;GAIG;AACH,wBAAgB,IAAI,CAChB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,SAAS,MAAM,EAAO,EAC5B,QAAQ,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,EAC/C,SAAS,GAAE,MAAW,GACvB,iBAAiB,CAgCnB;AAsCD,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAwFzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,iBAAiB,CA+CpF;AAQD,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8FAA8F;IAC9F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CAC1B;AASD;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,GAAG,iBAAiB,CA2FlF;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CAyE3D;AAED;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CA2DzD"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/commands.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAW7C,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,2FAA2F;AAC3F,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CACvC;AA0DD,+DAA+D;AAC/D,eAAO,MAAM,WAAW,EAAE,MAAyB,CAAC;AA+UpD;;;;GAIG;AACH,wBAAgB,IAAI,CAChB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,SAAS,MAAM,EAAO,EAC5B,QAAQ,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,EAC/C,SAAS,GAAE,MAAW,GACvB,iBAAiB,CAgCnB;AA8FD,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,iBAAiB,CA4DzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,iBAAiB,CA+CpF;AAQD,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8FAA8F;IAC9F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CAC1B;AAqDD;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,GAAG,iBAAiB,CAoDlF;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CA0E3D;AAED;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CA2DzD"}