@rt-tools/agent-kit 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -1
- package/assets/checks/check-push-gate.mjs +139 -0
- package/assets/checks/check-reuse.mjs +32 -98
- package/assets/checks/rt-kit-checks.config.mjs +19 -0
- package/assets/checks/signals/angular.json +19 -0
- package/assets/checks/signals/core.json +19 -0
- package/assets/checks/signals/store.json +14 -0
- package/assets/checks/signals/ui-kit-v2.json +48 -0
- package/assets/checks/signals/ui-kit.json +11 -0
- package/assets/checks/signals/utils.json +15 -0
- package/assets/checks/signals.mjs +71 -0
- package/assets/defaults/project.sh +9 -7
- package/assets/hooks/reuse-first-guard.sh +90 -1
- package/assets/patterns/git-workflow-commit.azure.md +18 -0
- package/assets/patterns/git-workflow-commit.github.md +18 -0
- package/assets/patterns/git-workflow-commit.gitlab.md +18 -0
- package/assets/patterns/reuse-first-extend.md +30 -0
- package/assets/patterns/spec-driven-domain.md +2 -2
- package/assets/rules/git-workflow.azure.md +15 -0
- package/assets/rules/git-workflow.github.md +15 -0
- package/assets/rules/git-workflow.gitlab.md +15 -0
- package/assets/rules/reuse-first.md +31 -13
- package/assets/skills/agent-kit.md +13 -8
- package/lib/assets.d.ts +1 -1
- package/lib/assets.d.ts.map +1 -1
- package/lib/assets.js +2 -4
- package/lib/assets.js.map +1 -1
- package/lib/catalog.d.ts +73 -0
- package/lib/catalog.d.ts.map +1 -1
- package/lib/catalog.js +121 -0
- package/lib/catalog.js.map +1 -1
- package/lib/commands.d.ts.map +1 -1
- package/lib/commands.js +84 -5
- package/lib/commands.js.map +1 -1
- package/lib/integrity.d.ts +25 -12
- package/lib/integrity.d.ts.map +1 -1
- package/lib/integrity.js +40 -18
- package/lib/integrity.js.map +1 -1
- package/lib/proposals.d.ts +9 -1
- package/lib/proposals.d.ts.map +1 -1
- package/lib/proposals.js +11 -2
- package/lib/proposals.js.map +1 -1
- package/lib/retired.d.ts +30 -0
- package/lib/retired.d.ts.map +1 -0
- package/lib/retired.js +19 -0
- package/lib/retired.js.map +1 -0
- package/lib/sync.d.ts +45 -1
- package/lib/sync.d.ts.map +1 -1
- package/lib/sync.js +44 -10
- package/lib/sync.js.map +1 -1
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.8.0.tgz +0 -0
- package/assets/laws/application/money.md +0 -41
- package/assets/laws/application/ownership.md +0 -32
- package/assets/patterns/ownership-scope-resolve.md +0 -69
- package/assets/patterns/ownership-session-procedure.md +0 -102
- package/assets/patterns/pricing-quote.md +0 -71
- package/assets/rules/ownership-scope.md +0 -66
- package/assets/rules/ownership-session.md +0 -124
- package/assets/rules/pricing.md +0 -68
- package/rt-tools-agent-kit-0.6.0.tgz +0 -0
package/lib/catalog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/catalog.ts"],"names":[],"mappings":"AAUA,OAAO,EAAS,KAAK,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAS,QAAQ,EAAuD,MAAM,eAAe,CAAC;AAErG,MAAM,WAAW,eAAe;IAC5B,kGAAkG;IAClG,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kFAAkF;IAClF,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;
|
|
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
|
@@ -10,6 +10,29 @@ import { join } from 'node:path';
|
|
|
10
10
|
import { KINDS } from './config.js';
|
|
11
11
|
import { matchesVariant, readAxes, variantOf, withoutVariant } from './variants.js';
|
|
12
12
|
const TITLE = /^#\s+(\S.*)$/m;
|
|
13
|
+
const FIELD = /^([a-z]+):\s*(\S.*?)\s*$/;
|
|
14
|
+
/**
|
|
15
|
+
* Вступление между `---` в начале файла; его нет — вернётся пустая шапка.
|
|
16
|
+
*
|
|
17
|
+
* Живёт при каталоге, потому что связь родителя с потомком читают двое: сверка связности пакета
|
|
18
|
+
* и каскад отказа. Объявление у одного из них заводило бы круговой импорт между ними.
|
|
19
|
+
*/
|
|
20
|
+
export function frontMatterOf(text) {
|
|
21
|
+
const lines = text.split('\n');
|
|
22
|
+
const found = {};
|
|
23
|
+
if (lines[0]?.trim() === '---') {
|
|
24
|
+
for (const line of lines.slice(1)) {
|
|
25
|
+
if (line.trim() === '---') {
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
const match = line.match(FIELD);
|
|
29
|
+
if (match) {
|
|
30
|
+
found[match[1]] = match[2];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return { name: found['name'] ?? '', kind: found['kind'] ?? '', law: found['law'] ?? '', rule: found['rule'] ?? '' };
|
|
35
|
+
}
|
|
13
36
|
/**
|
|
14
37
|
* Строка требования в шапке ресурса. Двух видов, потому что ресурсы двух родов: у разметки
|
|
15
38
|
* `**Требует:**`, у исполняемого файла — комментарий `# Требует:`.
|
|
@@ -174,6 +197,104 @@ export function variantGaps(catalog, selection) {
|
|
|
174
197
|
}
|
|
175
198
|
return gaps;
|
|
176
199
|
}
|
|
200
|
+
/** Последнее звено имени: `application/money` → `money`. Им ресурсы и ссылаются друг на друга. */
|
|
201
|
+
const shortNameOf = (entry) => entry.name.split('/').pop() ?? entry.name;
|
|
202
|
+
/**
|
|
203
|
+
* Ресурсы, снятые каскадом: правила при невзятом законе и паттерны при невзятых правилах.
|
|
204
|
+
*
|
|
205
|
+
* Связь читается из вступления самого ресурса — отдельный список при пакете разошёлся бы с
|
|
206
|
+
* ресурсами молча, тем же молчанием, ради которого каскад и заводится. Родитель ищется по
|
|
207
|
+
* последнему звену имени: полного пути в шапке нет, потому что переезд закона между слоями
|
|
208
|
+
* переписывал бы шапки всех правил при нём.
|
|
209
|
+
*
|
|
210
|
+
* Родителя в каталоге нет вовсе — каскад молчит: снимать не по чему, а промах в шапке судит
|
|
211
|
+
* сверка связности пакета, до всякой раскладки в дереве.
|
|
212
|
+
*/
|
|
213
|
+
export function cascadeCuts(catalog, selection) {
|
|
214
|
+
const takenOf = (kind) => new Set(catalog.filter((entry) => entry.kind === kind && isChosen(entry, selection)).map(shortNameOf));
|
|
215
|
+
const knownOf = (kind) => new Set(catalog.filter((entry) => entry.kind === kind).map(shortNameOf));
|
|
216
|
+
const takenLaws = takenOf('laws');
|
|
217
|
+
const knownLaws = knownOf('laws');
|
|
218
|
+
const takenRules = takenOf('rules');
|
|
219
|
+
const knownRules = knownOf('rules');
|
|
220
|
+
const cuts = [];
|
|
221
|
+
/** Правило, снятое законом: для паттернов при нём корнем цепочки будет этот закон. */
|
|
222
|
+
const rootByRule = new Map();
|
|
223
|
+
for (const entry of catalog) {
|
|
224
|
+
if (entry.kind !== 'rules' || !isChosen(entry, selection)) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const law = frontMatterOf(entry.text).law;
|
|
228
|
+
if (!law || !knownLaws.has(law) || takenLaws.has(law)) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
cuts.push({ id: entry.id, parent: law, root: law });
|
|
232
|
+
rootByRule.set(shortNameOf(entry), law);
|
|
233
|
+
}
|
|
234
|
+
for (const entry of catalog) {
|
|
235
|
+
if (entry.kind !== 'patterns' || !isChosen(entry, selection)) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const rule = frontMatterOf(entry.text).rule;
|
|
239
|
+
if (!rule || !knownRules.has(rule) || (takenRules.has(rule) && !rootByRule.has(rule))) {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
cuts.push({ id: entry.id, parent: rule, root: rootByRule.get(rule) ?? rule });
|
|
243
|
+
}
|
|
244
|
+
return cuts;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Ресурсы, названные выбором поимённо и всё равно снятые каскадом.
|
|
248
|
+
*
|
|
249
|
+
* Дерево, назвавшее правило в выборе при невыбранном законе, не получает его молча — то самое
|
|
250
|
+
* молчание, ради которого каскад и заводится, только с другой стороны: там о снятии не знал тот,
|
|
251
|
+
* кто ничего не просил, здесь — тот, кто попросил прямо.
|
|
252
|
+
*/
|
|
253
|
+
export function namedButCut(catalog, selection) {
|
|
254
|
+
return cascadeCuts(catalog, selection).filter((one) => selection.only.includes(one.id));
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Что ложится в дерево: прямой отбор за вычетом снятого каскадом.
|
|
258
|
+
*
|
|
259
|
+
* Отдельной функцией, а не внутри `isChosen`, потому что каскад знает весь каталог, а отбор —
|
|
260
|
+
* один ресурс: связь родителя с потомком по одной записи не видна.
|
|
261
|
+
*/
|
|
262
|
+
export function chosenEntries(catalog, selection) {
|
|
263
|
+
const cut = new Set(cascadeCuts(catalog, selection).map((one) => one.id));
|
|
264
|
+
return catalog.filter((entry) => isChosen(entry, selection) && !cut.has(entry.id));
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Строки отказа, которые ничего не снимают: названное ими снято каскадом или в каталоге не
|
|
268
|
+
* значится вовсе.
|
|
269
|
+
*
|
|
270
|
+
* Не отказ, а предупреждение: настройка, верная до обновления пакета, становится лишней сама,
|
|
271
|
+
* без единой правки в дереве, и отбивать за это раскладку значило бы наказывать за вчерашнюю
|
|
272
|
+
* правоту.
|
|
273
|
+
*
|
|
274
|
+
* Строка на ресурс чужого вида лишней не считается никогда: ею дерево гасит отказ о ресурсе,
|
|
275
|
+
* у которого нет подходящего вида, — то есть работу она делает.
|
|
276
|
+
*/
|
|
277
|
+
export function idleSkips(catalog, selection) {
|
|
278
|
+
const idle = [];
|
|
279
|
+
for (const id of selection.skip) {
|
|
280
|
+
const entry = catalog.find((one) => one.id === id);
|
|
281
|
+
if (!entry) {
|
|
282
|
+
idle.push({ id, by: '' });
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (!matchesVariant(entry.variant, selection.variants)) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
// Каскад считается от отбора без этой строки: иначе снятое ею самой читалось бы как
|
|
289
|
+
// снятое родителем, и лишней оказалась бы любая строка отказа подряд.
|
|
290
|
+
const without = { ...selection, skip: selection.skip.filter((one) => one !== id) };
|
|
291
|
+
const cut = cascadeCuts(catalog, without).find((one) => one.id === id);
|
|
292
|
+
if (cut) {
|
|
293
|
+
idle.push({ id, by: cut.root });
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return idle;
|
|
297
|
+
}
|
|
177
298
|
/**
|
|
178
299
|
* Связи, разорванные выбором дерева.
|
|
179
300
|
*
|
package/lib/catalog.js.map
CHANGED
|
@@ -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 { 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 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/**\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 name: string = withoutVariant(file, variant).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 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 *\n * `only` ограничивает **только те роды, которые сам называет**. Проект, выбравший девять\n * законов из пятнадцати, шаблоны при этом не терял бы: перечисляя законы, он говорит о\n * законах, а не обо всём, что пакет везёт. Обратное правило читалось бы как «выбрал законы —\n * остался без шаблонов», и заметил бы это только тот, кто пошёл за шаблоном.\n *\n * `skip` вычитает из выбранного. Отказ от одного закона не должен требовать переписать\n * список из пятнадцати, а точечное исключение остаётся точечным.\n *\n * Вид отбирается раньше обоих: ресурс чужого хостинга не «не выбран» — его в этом дереве не\n * существует вовсе, и называть его в `skip` проекту незачем.\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\n return !restricted || selection.only.includes(entry.id);\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 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 { 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 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 name: string = withoutVariant(file, variant).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 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 *\n * `only` ограничивает **только те роды, которые сам называет**. Проект, выбравший девять\n * законов из пятнадцати, шаблоны при этом не терял бы: перечисляя законы, он говорит о\n * законах, а не обо всём, что пакет везёт. Обратное правило читалось бы как «выбрал законы —\n * остался без шаблонов», и заметил бы это только тот, кто пошёл за шаблоном.\n *\n * `skip` вычитает из выбранного. Отказ от одного закона не должен требовать переписать\n * список из пятнадцати, а точечное исключение остаётся точечным.\n *\n * Вид отбирается раньше обоих: ресурс чужого хостинга не «не выбран» — его в этом дереве не\n * существует вовсе, и называть его в `skip` проекту незачем.\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\n return !restricted || selection.only.includes(entry.id);\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"]}
|
package/lib/commands.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/commands.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAK7C,OAAO,EAAkB,OAAO,EAAE,MAAM,aAAa,CAAC;AAKtD,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;IACpC;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAChC;
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/commands.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAK7C,OAAO,EAAkB,OAAO,EAAE,MAAM,aAAa,CAAC;AAKtD,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;IACpC;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAChC;AAuDD,+DAA+D;AAC/D,eAAO,MAAM,WAAW,EAAE,MAAyB,CAAC;AAoQpD;;;;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;AAgBD,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,iBAAiB,CA+EzE;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;AAeD;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,GAAG,iBAAiB,CA2FlF;AAED,kFAAkF;AAClF,MAAM,WAAW,eAAe;IAC5B,uDAAuD;IACvD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAuBD;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,eAAe,GAAG,iBAAiB,CAkGtF;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CA+D3D;AAED;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CAsDzD"}
|
package/lib/commands.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
7
7
|
import { join } from 'node:path';
|
|
8
8
|
import { collectAssets } from './assets.js';
|
|
9
|
-
import { isChosen, readCatalog } from './catalog.js';
|
|
9
|
+
import { cascadeCuts, isChosen, readCatalog } from './catalog.js';
|
|
10
10
|
import { isUnfilled } from './companion.js';
|
|
11
11
|
import { CONFIG_PATH, DEFAULT_LAYOUT, KINDS, OVERRIDES_DIR, PROFILE_FILE, readConfig, RT_KIT_DIR, SKILL_KINDS, } from './config.js';
|
|
12
12
|
import { hooksSection, SETTINGS_PATH } from './hooks-map.js';
|
|
@@ -38,6 +38,8 @@ const STATE_WORD = {
|
|
|
38
38
|
ok: 'на месте',
|
|
39
39
|
};
|
|
40
40
|
const NOT_CHOSEN = 'не выбран';
|
|
41
|
+
/** Перечень отделяет снятое каскадом от невыбранного: дерево его не выбирало и не отвергало. */
|
|
42
|
+
const CUT_BY_CASCADE = 'снят каскадом';
|
|
41
43
|
const SKIPPED = 'пропущен';
|
|
42
44
|
/** Ресурс чужого вида: в этом дереве его не существует, а не «от него отказались». */
|
|
43
45
|
const OTHER_VARIANT = 'другой вид';
|
|
@@ -189,14 +191,81 @@ const brokenLines = (result) => result.broken.length
|
|
|
189
191
|
' это предупреждение, а не отказ: требование закрывается своим средством дерева либо выбором ресурса',
|
|
190
192
|
]
|
|
191
193
|
: [];
|
|
194
|
+
/**
|
|
195
|
+
* Строки отказа, которые ничего не снимают.
|
|
196
|
+
*
|
|
197
|
+
* Названы вместе с тем, из-за чего стали лишними: «строка ни на что не влияет» без этого читается
|
|
198
|
+
* как промах в имени, и дерево идёт искать опечатку там, где её нет.
|
|
199
|
+
*/
|
|
200
|
+
const idleLines = (result) => result.idle.length
|
|
201
|
+
? [
|
|
202
|
+
`строк отказа, которые ничего не снимают: ${result.idle.length}`,
|
|
203
|
+
...result.idle.map((one) => ` ${one.id} — ${one.by ? `снято отказом от ${one.by}` : 'нет в пакете'}`),
|
|
204
|
+
' это предупреждение, а не отказ: строки убирает дерево, и раскладка идёт дальше',
|
|
205
|
+
]
|
|
206
|
+
: [];
|
|
207
|
+
/**
|
|
208
|
+
* Разложенное раньше, а теперь снятое каскадом.
|
|
209
|
+
*
|
|
210
|
+
* Отдельно от брошенного: от брошенного дерево отказалось само и знает, где искать причину, а
|
|
211
|
+
* снятое каскадом ушло вслед за родителем — в отказе его имени нет и не будет.
|
|
212
|
+
*/
|
|
213
|
+
const cutOnDiskLines = (result) => result.cutOnDisk.length
|
|
214
|
+
? [
|
|
215
|
+
`лежит от ресурсов, снятых вслед за родителем: ${result.cutOnDisk.length}`,
|
|
216
|
+
...result.cutOnDisk.map((one) => ` ${one.path} — снят вслед за ${one.cut.parent}, отвергнут ${one.cut.root}`),
|
|
217
|
+
' их не стирает никто: убирать вручную, как и брошенные',
|
|
218
|
+
]
|
|
219
|
+
: [];
|
|
220
|
+
/**
|
|
221
|
+
* Названное выбором, чего дерево всё равно не получит.
|
|
222
|
+
*
|
|
223
|
+
* Молчать здесь нельзя вдвойне: дерево не просто осталось без ресурса — оно попросило его
|
|
224
|
+
* поимённо и прочло бы отсутствие как промах раскладки.
|
|
225
|
+
*/
|
|
226
|
+
const namedCutLines = (result) => result.namedCut.length
|
|
227
|
+
? [
|
|
228
|
+
`названо выбором, но не приедет: ${result.namedCut.length}`,
|
|
229
|
+
...result.namedCut.map((one) => ` ${one.id} — снято вслед за ${one.parent}, отвергнут ${one.root}`),
|
|
230
|
+
' это предупреждение, а не отказ: возьми родителя в выбор либо убери потомка из него',
|
|
231
|
+
]
|
|
232
|
+
: [];
|
|
233
|
+
/**
|
|
234
|
+
* Файлы ресурсов, ушедших из набора.
|
|
235
|
+
*
|
|
236
|
+
* Названы отдельно от брошенных: брошенный ресурс в наборе есть и вернётся, если дерево его
|
|
237
|
+
* выберет, а снятый не вернётся никогда — и правило, по которому агент работает, у него
|
|
238
|
+
* последнее.
|
|
239
|
+
*/
|
|
240
|
+
const retiredLines = (result) => result.retired.length
|
|
241
|
+
? [
|
|
242
|
+
`в дереве лежат файлы ресурсов, которых в пакете больше нет: ${result.retired.length}`,
|
|
243
|
+
...result.retired.map((one) => ` ${one.path} — снят в v${one.since}: ${one.why}`),
|
|
244
|
+
' убирает их дерево: в чужие файлы пакет не пишет',
|
|
245
|
+
]
|
|
246
|
+
: [];
|
|
247
|
+
/**
|
|
248
|
+
* Предупреждения раскладки: кода возврата они не меняют и печатаются на любом её исходе.
|
|
249
|
+
*
|
|
250
|
+
* Иначе их не видит никто: на сошедшемся дереве проверка молчит, а удавшаяся раскладка называет
|
|
251
|
+
* положенные файлы — и лишняя строка отказа, снятый каскадом файл и ушедший из набора ресурс
|
|
252
|
+
* всплывали бы только там, где и без них уже красно.
|
|
253
|
+
*/
|
|
254
|
+
const warnings = (result) => [
|
|
255
|
+
...brokenLines(result),
|
|
256
|
+
...idleLines(result),
|
|
257
|
+
...namedCutLines(result),
|
|
258
|
+
...retiredLines(result),
|
|
259
|
+
...cutOnDiskLines(result),
|
|
260
|
+
...abandonedLines(result),
|
|
261
|
+
];
|
|
192
262
|
const describe = (result) => [
|
|
193
263
|
...holes(result),
|
|
194
264
|
...gapLines(result),
|
|
195
265
|
...unboundLines(result),
|
|
196
|
-
...
|
|
266
|
+
...warnings(result),
|
|
197
267
|
...pendingOf(result).map((entry) => ` ${entry.path} — ${STATE_WORD[entry.outcome]}`),
|
|
198
268
|
...unfilled(result).map((entry) => ` ${entry.path} — ${COMPANION_WORD[entry.state]}`),
|
|
199
|
-
...abandonedLines(result),
|
|
200
269
|
];
|
|
201
270
|
/** Имена дырок во всех ресурсах, которые дерево берёт. Без конфига — ни одной: выбор неизвестен. */
|
|
202
271
|
function placeholdersIn(config, assetsDir) {
|
|
@@ -290,7 +359,7 @@ export function sync(env, check) {
|
|
|
290
359
|
// предупреждение печатается и там, где расходиться больше нечему.
|
|
291
360
|
const count = result.missing.size + result.gaps.length + pending.length + empty.length + result.unbound.length;
|
|
292
361
|
if (!count) {
|
|
293
|
-
return { code: 0, lines: [`sync --check: разложенное сходится с пакетом v${version}`, ...
|
|
362
|
+
return { code: 0, lines: [`sync --check: разложенное сходится с пакетом v${version}`, ...warnings(result)] };
|
|
294
363
|
}
|
|
295
364
|
return { code: 1, lines: [`sync --check: расхождений ${count}`, ...describe(result)] };
|
|
296
365
|
}
|
|
@@ -330,6 +399,7 @@ export function sync(env, check) {
|
|
|
330
399
|
? [`разложено файлов: ${result.written.length}`, ...result.written.map((path) => ` ${path}`)]
|
|
331
400
|
: ['всё уже разложено']),
|
|
332
401
|
...unboundLines(result),
|
|
402
|
+
...warnings(result),
|
|
333
403
|
],
|
|
334
404
|
};
|
|
335
405
|
}
|
|
@@ -624,6 +694,10 @@ export function doctor(env) {
|
|
|
624
694
|
// Ресурс, названный и в отказе, и чужим видом, считается один раз — иначе «не выбрано»
|
|
625
695
|
// уходит в минус, а минус в отчёте читается как поломка счёта, а не как двойной счёт.
|
|
626
696
|
const skipped = config.skip.filter((id) => foreignVariant.every((entry) => entry.id !== id)).length;
|
|
697
|
+
// Снятое каскадом считается и называется отдельно от невыбранного: дерево его не выбирало и
|
|
698
|
+
// не отвергало — оно ушло вслед за родителем, и искать его в отказе читатель пойдёт зря.
|
|
699
|
+
const cuts = cascadeCuts(catalog, config);
|
|
700
|
+
const cut = new Set(cuts.map((one) => one.id));
|
|
627
701
|
// Невыбранное называется поимённо: число «не выбрано: 73» не отвечает ни на один вопрос,
|
|
628
702
|
// ради которого его читают, — ни какого ресурса не хватает, ни требуется ли он соседу.
|
|
629
703
|
const unchosen = catalog.filter((entry) => !isChosen(entry, config) &&
|
|
@@ -631,8 +705,9 @@ export function doctor(env) {
|
|
|
631
705
|
foreignVariant.every((one) => one.id !== entry.id));
|
|
632
706
|
const lines = [
|
|
633
707
|
`пакет v${version}, везёт ресурсов ${catalog.length}, взято ${taken}`,
|
|
634
|
-
`не выбрано: ${catalog.length - taken - skipped - other}, пропущено: ${skipped}, другой вид: ${other}`,
|
|
708
|
+
`не выбрано: ${catalog.length - taken - skipped - other - cut.size}, пропущено: ${skipped}, другой вид: ${other}, снято каскадом: ${cut.size}`,
|
|
635
709
|
...unchosen.map((entry) => ` не выбран: ${entry.id}`),
|
|
710
|
+
...cuts.map((one) => ` снят каскадом: ${one.id} — вслед за ${one.parent}${one.parent === one.root ? '' : `, отвергнут ${one.root}`}`),
|
|
636
711
|
...profileLines(root, assetsDir, config),
|
|
637
712
|
`значений в конфиге: ${Object.keys(config.vars).length}`,
|
|
638
713
|
...chosen,
|
|
@@ -656,6 +731,7 @@ export function list(env) {
|
|
|
656
731
|
const config = readConfig(root);
|
|
657
732
|
const catalog = readCatalog(assetsDir);
|
|
658
733
|
const planned = new Map();
|
|
734
|
+
const cutByCascade = new Set(config ? cascadeCuts(catalog, config).map((one) => one.id) : []);
|
|
659
735
|
if (config) {
|
|
660
736
|
for (const entry of planSync(config, root, version, assetsDir).planned) {
|
|
661
737
|
planned.set(entry.asset, entry.outcome);
|
|
@@ -674,6 +750,9 @@ export function list(env) {
|
|
|
674
750
|
if (!isChosen(entry, config)) {
|
|
675
751
|
return NOT_CHOSEN;
|
|
676
752
|
}
|
|
753
|
+
if (cutByCascade.has(entry.id)) {
|
|
754
|
+
return CUT_BY_CASCADE;
|
|
755
|
+
}
|
|
677
756
|
const outcome = planned.get(entry.id);
|
|
678
757
|
// Ресурса нет среди запланированных ровно в одном случае: в нём осталась дырка без
|
|
679
758
|
// значения, и до решения о судьбе файла раскладка не дошла.
|