@rt-tools/agent-kit 0.16.0 → 0.17.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/assets/checks/board-paths.github.mjs +88 -0
- package/assets/checks/board-runs.github.mjs +8 -0
- package/assets/checks/board-titles.github.mjs +66 -0
- package/assets/checks/check-board.github.mjs +17 -4
- package/assets/checks/check-file-size.mjs +10 -2
- package/assets/checks/check-glossary.mjs +170 -0
- package/assets/checks/check-push-gate.mjs +59 -1
- package/assets/checks/check-schema-drift.mjs +12 -5
- package/assets/checks/spec-contract.mjs +9 -0
- package/assets/defaults/gate-map.sh +13 -4
- package/assets/defaults/project.sh +22 -3
- package/assets/docs/GLOSSARY.md +1 -1
- package/assets/hooks/browser-device-id.sh +20 -4
- package/assets/hooks/browser-guard-device-id.sh +5 -2
- package/assets/hooks/git-guard-delivery-folder.sh +19 -0
- package/assets/hooks/git-guard-delivery.sh +39 -0
- package/assets/hooks/git-guard-push-tests.sh +21 -1
- package/assets/hooks/grill-gate-ask.sh +25 -0
- package/assets/hooks/grill-gate.sh +12 -5
- package/assets/hooks/lint-after-edit.sh +44 -16
- package/assets/hooks/proposal-guard.sh +17 -1
- package/assets/hooks/skill-gate-layers.sh +7 -0
- package/assets/hooks/skill-gate.sh +29 -0
- package/assets/hooks/task-context-load.sh +10 -0
- package/assets/hooks/task-flow-context.sh +185 -0
- package/assets/hooks/task-flow-draft-guard.sh +92 -0
- package/assets/hooks/task-flow-guard.sh +27 -159
- package/assets/hooks/turn-exit-guard.sh +8 -1
- package/assets/hooks/window-fill-guard.sh +5 -1
- package/assets/laws/delivery.md +19 -0
- package/assets/laws/frontend-application.md +4 -0
- package/assets/laws/verifiability.md +21 -0
- package/assets/laws/work-conduct.md +15 -0
- package/assets/patterns/browser-verification-stand.md +7 -2
- package/assets/patterns/doc-style-write.md +41 -1
- package/assets/patterns/git-workflow-commit.github.md +15 -2
- package/assets/patterns/git-workflow-docker.md +14 -0
- package/assets/patterns/git-workflow-merge.md +18 -0
- package/assets/patterns/git-workflow-migration.md +11 -0
- package/assets/patterns/git-workflow-pr.github.md +6 -1
- package/assets/patterns/git-workflow-secrets.md +14 -0
- package/assets/patterns/git-workflow-stack.md +63 -1
- package/assets/patterns/spec-driven-domain.md +34 -1
- package/assets/patterns/spec-driven-rule.md +11 -0
- package/assets/patterns/spec-driven-sweep.md +57 -0
- package/assets/patterns/task-flow-archive.md +46 -14
- package/assets/patterns/task-flow-close.md +78 -2
- package/assets/patterns/task-flow-resume.md +17 -5
- package/assets/patterns/task-flow-start.md +42 -4
- package/assets/pitfalls/agent-kit.md +73 -3
- package/assets/pitfalls/doc-style.md +5 -0
- package/assets/pitfalls/git-workflow.github.md +68 -0
- package/assets/pitfalls/spec-driven.md +16 -0
- package/assets/pitfalls/task-flow.md +93 -0
- package/assets/pitfalls/turn-conduct.md +14 -0
- package/assets/rules/browser-verification.md +39 -0
- package/assets/rules/deploy-flow.azure.md +8 -0
- package/assets/rules/deploy-flow.github.md +18 -0
- package/assets/rules/deploy-flow.gitlab.md +8 -0
- package/assets/rules/doc-style.md +14 -0
- package/assets/rules/git-workflow.azure.md +5 -0
- package/assets/rules/git-workflow.github.md +85 -75
- package/assets/rules/git-workflow.gitlab.md +5 -0
- package/assets/rules/reuse-first.md +8 -0
- package/assets/rules/shared-code.md +5 -0
- package/assets/rules/spec-driven.md +14 -0
- package/assets/rules/task-flow.md +112 -112
- package/assets/rules/testing.md +14 -1
- package/assets/rules/turn-conduct.md +40 -27
- package/assets/rules/turn-entry.md +6 -0
- package/assets/samples/tasks/_template/grill.md +5 -0
- package/assets/samples/tasks/_template/plan.md +3 -0
- package/assets/skills/agent-kit.md +99 -76
- package/assets/templates/postmortem.md +5 -1
- package/bin/agent-kit.d.ts.map +1 -1
- package/bin/agent-kit.js +30 -6
- package/bin/agent-kit.js.map +1 -1
- package/lib/catalog.d.ts.map +1 -1
- package/lib/catalog.js +2 -1
- package/lib/catalog.js.map +1 -1
- package/lib/commands.d.ts.map +1 -1
- package/lib/commands.js +96 -7
- package/lib/commands.js.map +1 -1
- package/lib/hooks-map.d.ts +26 -0
- package/lib/hooks-map.d.ts.map +1 -1
- package/lib/hooks-map.js +58 -2
- package/lib/hooks-map.js.map +1 -1
- package/lib/sections.d.ts +6 -0
- package/lib/sections.d.ts.map +1 -1
- package/lib/sections.js +19 -0
- package/lib/sections.js.map +1 -1
- package/lib/shipment.d.ts +2 -0
- package/lib/shipment.d.ts.map +1 -1
- package/lib/shipment.fixture.d.ts +5 -0
- package/lib/shipment.fixture.d.ts.map +1 -1
- package/lib/shipment.fixture.js +7 -0
- package/lib/shipment.fixture.js.map +1 -1
- package/lib/shipment.js +13 -1
- package/lib/shipment.js.map +1 -1
- package/lib/sync.d.ts +35 -3
- package/lib/sync.d.ts.map +1 -1
- package/lib/sync.js +59 -8
- package/lib/sync.js.map +1 -1
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.17.0.tgz +0 -0
- package/rt-tools-agent-kit-0.16.0.tgz +0 -0
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 { 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
|
+
{"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 { linesOutsideFences } from './sections.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(linesOutsideFences(text).join('\\n'));\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"]}
|
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":"
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/commands.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAsB7C,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;AA2DD,+DAA+D;AAC/D,eAAO,MAAM,WAAW,EAAE,MAAyB,CAAC;AA0ZpD;;;;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,CA8DzE;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;AAqHD;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,GAAG,iBAAiB,CAuDlF;AA0BD,wBAAgB,MAAM,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CA6E3D;AAED;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CA2DzD"}
|
package/lib/commands.js
CHANGED
|
@@ -14,8 +14,8 @@ import { CONFIG_PATH, DEFAULT_LAYOUT, KINDS, OVERRIDES_DIR, PROFILE_FILE, readCo
|
|
|
14
14
|
import { hooksSection, SETTINGS_PATH } from './hooks-map.js';
|
|
15
15
|
import { DEFAULT_DAYS, KEEP_DAYS, OBSERVATIONS_DIR, readObservations, summarize, } from './observations.js';
|
|
16
16
|
import { isRefusal } from './plan.js';
|
|
17
|
-
import { laidOutSkills } from './snapshot.js';
|
|
18
|
-
import { pendingOf, planSync, runSync } from './sync.js';
|
|
17
|
+
import { laidOutSkills, treeSnapshot } from './snapshot.js';
|
|
18
|
+
import { mergedBody, pendingOf, planSync, runSync } from './sync.js';
|
|
19
19
|
import { thresholdLines } from './thresholds.js';
|
|
20
20
|
import { answersRequirement, readTraits, unknownTraits } from './traits.js';
|
|
21
21
|
import { placeholdersOf } from './vars.js';
|
|
@@ -143,6 +143,40 @@ function profileLines(root, assetsDir, config) {
|
|
|
143
143
|
: [' все определены']),
|
|
144
144
|
];
|
|
145
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Местное значение, которым живёт хук: путь от корня дерева и то, что перестаёт проверяться без
|
|
148
|
+
* него. Стоит строкой шапки, тем же приёмом, каким хук называет требуемые ресурсы.
|
|
149
|
+
*/
|
|
150
|
+
const LOCAL_VALUE = /^# Местное значение: (\S+) — ([^\n]+)$/gm;
|
|
151
|
+
/**
|
|
152
|
+
* Местные значения, которых ждут взятые хуки, и те из них, которых в дереве нет.
|
|
153
|
+
*
|
|
154
|
+
* Считать их приходится отдельно от файлов и функций: разложенный хук с пустым значением лежит
|
|
155
|
+
* на месте, зовётся и выходит нулём — сводка называет такое дерево настроенным, а проверять оно
|
|
156
|
+
* перестало. Хук, отказывающий в пользу работы, тем и опасен: снаружи это выглядит исправной
|
|
157
|
+
* работой.
|
|
158
|
+
*/
|
|
159
|
+
function localValueLines(root, assetsDir, config) {
|
|
160
|
+
const absent = [];
|
|
161
|
+
let wanted = 0;
|
|
162
|
+
for (const asset of collectAssets(config, assetsDir).filter((one) => one.kind === 'hooks')) {
|
|
163
|
+
for (const [, path, loss] of asset.text.matchAll(LOCAL_VALUE)) {
|
|
164
|
+
wanted += 1;
|
|
165
|
+
if (!existsSync(join(root, path))) {
|
|
166
|
+
absent.push(` нет значения ${path} — его ждёт ${asset.id}: ${loss.trim()}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (!wanted) {
|
|
171
|
+
return [];
|
|
172
|
+
}
|
|
173
|
+
return [
|
|
174
|
+
`местные значения, которых ждут взятые хуки: ${wanted}`,
|
|
175
|
+
...(absent.length
|
|
176
|
+
? [...absent.sort(byText), ' хук без своего значения проверку не делает и действие пропускает']
|
|
177
|
+
: [' все на месте']),
|
|
178
|
+
];
|
|
179
|
+
}
|
|
146
180
|
/** Дырка в тексте пакета, как она написана в самом тексте. */
|
|
147
181
|
function holeOf(name) {
|
|
148
182
|
return `{{${name}}}`;
|
|
@@ -206,6 +240,26 @@ function strangeTraits(config, assetsDir) {
|
|
|
206
240
|
}
|
|
207
241
|
return lines.length ? [...lines, ` объявленные свойства: ${traits.map((trait) => trait.value).join(', ')}`] : [];
|
|
208
242
|
}
|
|
243
|
+
/**
|
|
244
|
+
* Что запись объявления сделала с настройкой агента.
|
|
245
|
+
*
|
|
246
|
+
* Дописанное называется поимённо: правка легла в файл дерева, и увидеть её оно должно тем же
|
|
247
|
+
* выводом, каким видит положенные файлы, — а не следующим просмотром истории.
|
|
248
|
+
*/
|
|
249
|
+
const boundLines = (result) => {
|
|
250
|
+
if (result.bound === null || (!result.bound.added.length && !result.bound.unreadable)) {
|
|
251
|
+
return [];
|
|
252
|
+
}
|
|
253
|
+
return result.bound.unreadable
|
|
254
|
+
? [
|
|
255
|
+
`\`${SETTINGS_PATH}\` не разбирается как JSON — записи в неё не делалось`,
|
|
256
|
+
' дерево вправе держать её по-своему, вплоть до комментариев; тогда запись идёт рукой',
|
|
257
|
+
]
|
|
258
|
+
: [
|
|
259
|
+
`в \`${SETTINGS_PATH}\` дописано событий: ${result.bound.added.length}`,
|
|
260
|
+
...result.bound.added.map((event) => ` ${event} — зовёт диспетчер`),
|
|
261
|
+
];
|
|
262
|
+
};
|
|
209
263
|
/**
|
|
210
264
|
* Гарды, которых нет в настройке агента, и готовый кусок для неё.
|
|
211
265
|
*
|
|
@@ -313,6 +367,17 @@ const retiredLines = (result) => result.retired.length
|
|
|
313
367
|
' убирает их дерево: в чужие файлы пакет не пишет',
|
|
314
368
|
]
|
|
315
369
|
: [];
|
|
370
|
+
/**
|
|
371
|
+
* Надстройка, которой в пакете ничего не отвечает: она не применена, и молчание об этом читается
|
|
372
|
+
* как «всё применено».
|
|
373
|
+
*/
|
|
374
|
+
const strayLines = (result) => result.strayOverrides.length
|
|
375
|
+
? [
|
|
376
|
+
`надстроек, не подобранных ни к одному ресурсу: ${result.strayOverrides.length}`,
|
|
377
|
+
...result.strayOverrides.map((id) => ` ${join(OVERRIDES_DIR, id)} — применена не была`),
|
|
378
|
+
' подбор идёт по идентификатору ресурса целиком, вместе с приставкой свойства',
|
|
379
|
+
]
|
|
380
|
+
: [];
|
|
316
381
|
/**
|
|
317
382
|
* Предупреждения раскладки: кода возврата они не меняют и печатаются на любом её исходе.
|
|
318
383
|
*
|
|
@@ -327,6 +392,7 @@ const warnings = (result) => [
|
|
|
327
392
|
...retiredLines(result),
|
|
328
393
|
...cutOnDiskLines(result),
|
|
329
394
|
...abandonedLines(result),
|
|
395
|
+
...strayLines(result),
|
|
330
396
|
];
|
|
331
397
|
const describe = (result) => [
|
|
332
398
|
...holes(result),
|
|
@@ -402,7 +468,7 @@ function debtLines(config, root, assetsDir) {
|
|
|
402
468
|
}
|
|
403
469
|
const path = join(root, companionPathOf(asset));
|
|
404
470
|
const existing = existsSync(path) ? readFileSync(path, 'utf8') : null;
|
|
405
|
-
found.push(unaddressedOf(asset.name, asset
|
|
471
|
+
found.push(unaddressedOf(asset.name, mergedBody(asset, root), existing));
|
|
406
472
|
}
|
|
407
473
|
const line = debtLine(found);
|
|
408
474
|
return line === null ? [] : [line, 'адрес статье дописывается в компаньоне рядом с правилом'];
|
|
@@ -508,16 +574,18 @@ export function sync(env, check) {
|
|
|
508
574
|
],
|
|
509
575
|
};
|
|
510
576
|
}
|
|
511
|
-
// Раскладка удалась,
|
|
512
|
-
//
|
|
513
|
-
//
|
|
577
|
+
// Раскладка удалась, и объявление легло вместе с файлами. Остаётся оно неположенным ровно
|
|
578
|
+
// там, где настройку не разобрать: комментарии дерева пакет не сносит своим разбором, и
|
|
579
|
+
// тогда печатается прежний готовый кусок. Код возврата в обоих случаях нулевой — файлы
|
|
580
|
+
// легли, — но молчать нельзя: узнают иначе, когда что-нибудь пройдёт мимо гарда.
|
|
514
581
|
return {
|
|
515
582
|
code: 0,
|
|
516
583
|
lines: [
|
|
517
584
|
...(result.written.length
|
|
518
585
|
? [`разложено файлов: ${result.written.length}`, ...result.written.map((path) => ` ${path}`)]
|
|
519
586
|
: ['всё уже разложено']),
|
|
520
|
-
...
|
|
587
|
+
...boundLines(result),
|
|
588
|
+
...(result.bound?.unreadable === false ? [] : unboundLines(result)),
|
|
521
589
|
...driftedLines(result),
|
|
522
590
|
...debtLines(config, root, assetsDir),
|
|
523
591
|
...warnings(result),
|
|
@@ -742,6 +810,25 @@ export function stats(env, options) {
|
|
|
742
810
|
lines: statsLines(summary, result.swept, days, version, laidOutSkills(config, assetsDir).length),
|
|
743
811
|
};
|
|
744
812
|
}
|
|
813
|
+
/**
|
|
814
|
+
* Разделы пакета, замещённые надстройками дерева.
|
|
815
|
+
*
|
|
816
|
+
* Совпавший заголовок замещает раздел целиком, и всё, что пакет дописал в такой раздел новой
|
|
817
|
+
* версией, пропадает молча: раскладка сходится, заголовки совпадают, а утверждений нет. Сверка
|
|
818
|
+
* заголовков ловит переименование раздела, а не пополнение, и других свидетелей у потери не
|
|
819
|
+
* бывает. Разбор состояния поэтому называет замещённое поимённо — это половина ответа, которую
|
|
820
|
+
* машина знает и без прежней редакции; вторую половину даёт снимок, снятый до установки.
|
|
821
|
+
*/
|
|
822
|
+
function replacedLines(config, assetsDir, root) {
|
|
823
|
+
const replaced = treeSnapshot(config, assetsDir, root).filter((one) => one.kind === 'replace');
|
|
824
|
+
if (replaced.length === 0) {
|
|
825
|
+
return [];
|
|
826
|
+
}
|
|
827
|
+
return [
|
|
828
|
+
`замещено надстройками разделов: ${replaced.length} — что пакет дописал в них новой версией, теряется молча`,
|
|
829
|
+
...replaced.map((one) => ` ${one.resource} · ${one.section}`),
|
|
830
|
+
];
|
|
831
|
+
}
|
|
745
832
|
export function doctor(env) {
|
|
746
833
|
const { root, version, assetsDir } = env;
|
|
747
834
|
const config = readConfig(root);
|
|
@@ -788,6 +875,8 @@ export function doctor(env) {
|
|
|
788
875
|
return ` снят каскадом: ${one.id} — вслед за ${one.parent}${root}`;
|
|
789
876
|
}),
|
|
790
877
|
...profileLines(root, assetsDir, config),
|
|
878
|
+
...localValueLines(root, assetsDir, config),
|
|
879
|
+
...replacedLines(config, assetsDir, root),
|
|
791
880
|
...thresholdLines(root),
|
|
792
881
|
`значений в конфиге: ${Object.keys(config.vars).length}`,
|
|
793
882
|
...chosen,
|