@rt-tools/agent-kit 0.10.0 → 0.12.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/check-file-size.mjs +19 -4
- package/assets/checks/check-state-next.mjs +202 -0
- package/assets/checks/check-states.mjs +142 -0
- package/assets/checks/check-turn-map.mjs +146 -0
- package/assets/checks/rt-kit-checks.config.mjs +16 -2
- package/assets/defaults/project.sh +67 -7
- package/assets/defaults/turn-map.md +48 -0
- package/assets/hooks/browser-device-id.sh +2 -0
- package/assets/hooks/browser-guard-device-id.sh +5 -1
- package/assets/hooks/browser-guard-no-asking.sh +5 -1
- package/assets/hooks/browser-guard-no-listing.sh +2 -0
- package/assets/hooks/browser-guard-no-other-drivers.sh +7 -3
- package/assets/hooks/browser-guard-require-select.sh +6 -2
- package/assets/hooks/claim-guard.sh +5 -1
- package/assets/hooks/commit-msg.sh +2 -0
- package/assets/hooks/conscience-guard.sh +5 -1
- package/assets/hooks/constitution-index.sh +2 -0
- package/assets/hooks/dev-server-guard.sh +7 -3
- package/assets/hooks/dispatch.sh +69 -0
- package/assets/hooks/docs-guard.sh +8 -4
- package/assets/hooks/exam-guard.sh +7 -3
- package/assets/hooks/git-guard-delivery-signature.sh +2 -0
- package/assets/hooks/git-guard-delivery.sh +39 -5
- package/assets/hooks/git-guard-main.sh +8 -4
- package/assets/hooks/git-guard-push-tests.sh +8 -4
- package/assets/hooks/glossary-load.sh +2 -0
- package/assets/hooks/grill-gate.sh +6 -2
- package/assets/hooks/handoff-entry-guard.sh +6 -2
- package/assets/hooks/handoff-write.sh +124 -0
- package/assets/hooks/hook-input.sh +54 -0
- package/assets/hooks/lint-after-edit.sh +7 -3
- package/assets/hooks/observe.sh +2 -0
- package/assets/hooks/postmortem-guard.sh +5 -1
- package/assets/hooks/proposal-guard.sh +5 -1
- package/assets/hooks/prose-style-guard.sh +7 -3
- package/assets/hooks/qa-dataid-guard.sh +6 -2
- package/assets/hooks/rerun-guard.sh +7 -3
- package/assets/hooks/reuse-first-guard.sh +7 -3
- package/assets/hooks/roles.sh +2 -0
- package/assets/hooks/rule-article.sh +99 -0
- package/assets/hooks/skill-gate-layers.sh +2 -0
- package/assets/hooks/skill-gate-rearm.sh +5 -1
- package/assets/hooks/skill-gate.sh +25 -2
- package/assets/hooks/skill-loaded.sh +5 -1
- package/assets/hooks/sql-guard-parse.sh +2 -0
- package/assets/hooks/sql-guard-request.sh +4 -1
- package/assets/hooks/sql-guard-target.sh +2 -0
- package/assets/hooks/sql-guard-write.sh +2 -0
- package/assets/hooks/sql-guard.sh +6 -2
- package/assets/hooks/task-context-load.sh +2 -0
- package/assets/hooks/task-flow-guard.sh +8 -4
- package/assets/hooks/turn-entry-load.sh +62 -0
- package/assets/hooks/turn-exit-guard.sh +44 -17
- package/assets/hooks/utf8.sh +35 -0
- package/assets/hooks/waiting-turn-guard.sh +5 -1
- package/assets/hooks/window-fill-guard.sh +37 -5
- package/assets/laws/work-conduct.md +34 -9
- package/assets/patterns/dependencies-upgrade.md +1 -1
- package/assets/patterns/doc-style-write.md +3 -3
- package/assets/patterns/git-workflow-commit.azure.md +2 -202
- package/assets/patterns/git-workflow-commit.github.md +2 -258
- package/assets/patterns/git-workflow-commit.gitlab.md +1 -217
- package/assets/patterns/git-workflow-docker.md +3 -3
- package/assets/patterns/git-workflow-merge.md +3 -2
- package/assets/patterns/git-workflow-migration.md +3 -3
- package/assets/patterns/git-workflow-pr.azure.md +224 -0
- package/assets/patterns/git-workflow-pr.github.md +280 -0
- package/assets/patterns/git-workflow-pr.gitlab.md +240 -0
- package/assets/patterns/git-workflow-restart.md +3 -3
- package/assets/patterns/git-workflow-secrets.md +3 -3
- package/assets/patterns/task-flow-archive.md +193 -0
- package/assets/patterns/task-flow-close.md +11 -160
- package/assets/patterns/task-flow-handoff.md +22 -4
- package/assets/patterns/task-flow-resume.md +6 -0
- package/assets/patterns/task-flow-start.md +41 -0
- package/assets/patterns/turn-entry-map.md +81 -0
- package/assets/pitfalls/doc-style.md +80 -0
- package/assets/pitfalls/git-workflow.azure.md +50 -0
- package/assets/pitfalls/git-workflow.github.md +78 -0
- package/assets/pitfalls/git-workflow.gitlab.md +49 -0
- package/assets/pitfalls/spec-driven.md +36 -0
- package/assets/pitfalls/styling-bem.md +45 -0
- package/assets/pitfalls/task-flow.md +62 -0
- package/assets/pitfalls/testing.md +70 -0
- package/assets/rules/deploy-flow.azure.md +106 -0
- package/assets/rules/deploy-flow.github.md +113 -0
- package/assets/rules/deploy-flow.gitlab.md +108 -0
- package/assets/rules/doc-style.md +25 -76
- package/assets/rules/git-workflow.azure.md +6 -92
- package/assets/rules/git-workflow.github.md +14 -127
- package/assets/rules/git-workflow.gitlab.md +6 -93
- package/assets/rules/spec-driven.md +39 -30
- package/assets/rules/styling-bem.md +20 -39
- package/assets/rules/task-flow.md +17 -186
- package/assets/rules/testing.md +3 -64
- package/assets/rules/turn-conduct.md +206 -0
- package/assets/rules/turn-entry.md +93 -0
- package/assets/rules/typescript-conventions.md +15 -0
- package/assets/skills/agent-kit.md +35 -12
- package/assets/templates/pitfalls.md +10 -0
- package/assets/templates/rule.md +5 -3
- package/lib/assets.d.ts.map +1 -1
- package/lib/assets.js +6 -1
- package/lib/assets.js.map +1 -1
- package/lib/cargo.d.ts +42 -0
- package/lib/cargo.d.ts.map +1 -1
- package/lib/cargo.js +2 -0
- package/lib/cargo.js.map +1 -1
- package/lib/cascade.d.ts.map +1 -1
- package/lib/cascade.js +19 -1
- package/lib/cascade.js.map +1 -1
- package/lib/commands.d.ts.map +1 -1
- package/lib/commands.js +65 -4
- package/lib/commands.js.map +1 -1
- package/lib/config.d.ts +16 -1
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +8 -0
- package/lib/config.js.map +1 -1
- package/lib/hooks-map.d.ts +13 -0
- package/lib/hooks-map.d.ts.map +1 -1
- package/lib/hooks-map.js +33 -1
- package/lib/hooks-map.js.map +1 -1
- package/lib/observations.d.ts +35 -1
- package/lib/observations.d.ts.map +1 -1
- package/lib/observations.js +14 -2
- package/lib/observations.js.map +1 -1
- package/lib/ship.d.ts +2 -0
- package/lib/ship.d.ts.map +1 -1
- package/lib/ship.js +2 -0
- package/lib/ship.js.map +1 -1
- package/lib/thresholds.d.ts +49 -0
- package/lib/thresholds.d.ts.map +1 -0
- package/lib/thresholds.js +151 -0
- package/lib/thresholds.js.map +1 -0
- package/package.json +1 -1
- package/rt-tools-agent-kit-0.12.0.tgz +0 -0
- package/assets/commands/agent-kit-digest.md +0 -88
- package/assets/commands/rules-review.md +0 -98
- package/rt-tools-agent-kit-0.10.0.tgz +0 -0
package/lib/cargo.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cargo.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/cargo.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,yFAAyF;AACzF,MAAM,CAAC,MAAM,oBAAoB,GAAW,GAAG,CAAC;AAEhD,uDAAuD;AACvD,MAAM,CAAC,MAAM,iBAAiB,GAAW,cAAc,CAAC","sourcesContent":["/**\n * Форма груза: то, что уезжает с дерева в приём.\n *\n * Место объявления одно на обе стороны — правило договорённости приёма. Объявляет его\n * отправляющая сторона: пакет уходит в реестр, и импорт внутренней либы приёма уехал бы в\n * описания его типов и сломал бы установку. Приём берёт форму отсюда, а своей копии не заводит:\n * из двух копий компилируется только одна, а расходятся они молча.\n *\n * Файл держится без единого импорта намеренно: его читает и приём, и всякий, кто ставит пакет,\n * — а всё остальное в пакете написано под запуск из строки запуска.\n */\n\n/** Версия формата запроса приёма. Меняется, когда меняется состав полей самого груза. */\nexport const CARGO_SCHEMA_VERSION: string = '1';\n\n/** Заголовок, которым дерево представляется приёму. */\nexport const TREE_TOKEN_HEADER: string = 'x-tree-token';\n\n/** Род правки надстройки: дерево замещает раздел пакета, дописывает свой или снимает пакетный. */\nexport type TOverrideKind = 'replace' | 'append' | 'drop';\n\n/** Сколько раз что-то встретилось за отрезок сводки. */\nexport interface ICargoCount {\n readonly name: string;\n readonly count: number;\n}\n\n/**\n * Одна правка надстройки. У замещения и снятия заголовок раздела пакетный и общий у всех, потому\n * называется; у своего раздела заголовок придумало дерево — он не уезжает вовсе.\n */\nexport interface ICargoOverride {\n readonly resource: string;\n readonly section: string | null;\n readonly kind: TOverrideKind;\n}\n\n/** Общее у всякого груза: чем разбирать и от какого дерева приехало. */\nexport interface ICargoHead {\n readonly schema: string;\n readonly tree: string;\n}\n\n/** Сводка наблюдений дерева за отрезок дней вместе со снимком надстроек. */\nexport interface ISummaryCargo extends ICargoHead {\n readonly days: number;\n readonly sessions: number;\n readonly loads: readonly ICargoCount[];\n readonly denials: readonly ICargoCount[];\n readonly kinds: readonly ICargoCount[];\n readonly guards: readonly ICargoCount[];\n /** Ресурсы, разложенные в дерево и не загруженные за отрезок ни разу. */\n readonly unused: readonly string[];\n /** Ресурсы пакета, которых дерево не разложило вовсе. */\n readonly unpicked: readonly string[];\n readonly overrides: readonly ICargoOverride[];\n /** Версии схемы строк наблюдения, встреченные за отрезок. Приём их не судит. */\n readonly versions: readonly string[];\n readonly total: number;\n}\n\n/** Одно предложение по слою правил. */\nexport interface IProposalItem {\n readonly text: string;\n readonly address: string;\n readonly resource: string;\n}\n\nexport interface IProposalsCargo extends ICargoHead {\n readonly items: readonly IProposalItem[];\n}\n\n/** Один разбор происшествия: имя файла на дереве и текст целиком. */\nexport interface IPostmortemItem {\n readonly file: string;\n readonly text: string;\n}\n\nexport interface IPostmortemsCargo extends ICargoHead {\n readonly items: readonly IPostmortemItem[];\n}\n\n/**\n * Обращение дерева за токеном.\n *\n * Имени дерева в нём нет вовсе: его приём берёт из приглашения — принятое из обращения, оно\n * позволило бы назваться чужим именем тому, кто добыл код.\n */\nexport interface IEnrollCargo {\n readonly schema: string;\n /** Признак дерева: его дерево считает у себя, а приём только сверяет. */\n readonly tree: string;\n /** Код приглашения, выданный владельцем. Уходит один раз и в журнал не попадает. */\n readonly code: string;\n}\n\n/** Ответ приёма на годное обращение: токен уходит дереву единственным этим ответом. */\nexport interface IEnrollGranted {\n readonly tree: string;\n /** Имя дерева, как его назвал владелец в приглашении. */\n readonly name: string;\n readonly token: string;\n}\n\n/** Ответ приёма на принятый груз: дерево и месяц печатаются владельцу отправляющей стороной. */\nexport interface IIntakeAccepted {\n readonly tree: string;\n /** Месяц записи в форме `2026-08`, по всемирному времени приёма. */\n readonly month: string;\n /** Запись месяца заведена этим запросом, а не обновлена. */\n readonly created: boolean;\n /**\n * Сколько записей легло этим запросом и сколько приехало повторно.\n *\n * Есть только у предложений: у них приём отбирает уже приехавшее по признаку, и без этих\n * двух чисел отправитель печатает «уехало» в том числе тогда, когда нового не уехало ничего.\n * Приём прежней редакции их не присылает вовсе — отсюда необязательность.\n */\n readonly added?: number;\n readonly known?: number;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cargo.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/cargo.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,yFAAyF;AACzF,MAAM,CAAC,MAAM,oBAAoB,GAAW,GAAG,CAAC;AAEhD,uDAAuD;AACvD,MAAM,CAAC,MAAM,iBAAiB,GAAW,cAAc,CAAC;AA6GxD,oGAAoG;AACpG,MAAM,CAAC,MAAM,YAAY,GAAsB,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC","sourcesContent":["/**\n * Форма груза: то, что уезжает с дерева в приём.\n *\n * Место объявления одно на обе стороны — правило договорённости приёма. Объявляет его\n * отправляющая сторона: пакет уходит в реестр, и импорт внутренней либы приёма уехал бы в\n * описания его типов и сломал бы установку. Приём берёт форму отсюда, а своей копии не заводит:\n * из двух копий компилируется только одна, а расходятся они молча.\n *\n * Файл держится без единого импорта намеренно: его читает и приём, и всякий, кто ставит пакет,\n * — а всё остальное в пакете написано под запуск из строки запуска.\n */\n\n/** Версия формата запроса приёма. Меняется, когда меняется состав полей самого груза. */\nexport const CARGO_SCHEMA_VERSION: string = '1';\n\n/** Заголовок, которым дерево представляется приёму. */\nexport const TREE_TOKEN_HEADER: string = 'x-tree-token';\n\n/** Род правки надстройки: дерево замещает раздел пакета, дописывает свой или снимает пакетный. */\nexport type TOverrideKind = 'replace' | 'append' | 'drop';\n\n/** Сколько раз что-то встретилось за отрезок сводки. */\nexport interface ICargoCount {\n readonly name: string;\n readonly count: number;\n}\n\n/**\n * Одна правка надстройки. У замещения и снятия заголовок раздела пакетный и общий у всех, потому\n * называется; у своего раздела заголовок придумало дерево — он не уезжает вовсе.\n */\nexport interface ICargoOverride {\n readonly resource: string;\n readonly section: string | null;\n readonly kind: TOverrideKind;\n}\n\n/** Общее у всякого груза: чем разбирать и от какого дерева приехало. */\nexport interface ICargoHead {\n readonly schema: string;\n readonly tree: string;\n}\n\n/** Сводка наблюдений дерева за отрезок дней вместе со снимком надстроек. */\nexport interface ISummaryCargo extends ICargoHead {\n readonly days: number;\n readonly sessions: number;\n readonly loads: readonly ICargoCount[];\n readonly denials: readonly ICargoCount[];\n readonly kinds: readonly ICargoCount[];\n readonly guards: readonly ICargoCount[];\n /** Ресурсы, разложенные в дерево и не загруженные за отрезок ни разу. */\n readonly unused: readonly string[];\n /** Ресурсы пакета, которых дерево не разложило вовсе. */\n readonly unpicked: readonly string[];\n readonly overrides: readonly ICargoOverride[];\n /** Версии схемы строк наблюдения, встреченные за отрезок. Приём их не судит. */\n readonly versions: readonly string[];\n readonly total: number;\n}\n\n/** Одно предложение по слою правил. */\nexport interface IProposalItem {\n readonly text: string;\n readonly address: string;\n readonly resource: string;\n}\n\nexport interface IProposalsCargo extends ICargoHead {\n readonly items: readonly IProposalItem[];\n}\n\n/** Один разбор происшествия: имя файла на дереве и текст целиком. */\nexport interface IPostmortemItem {\n readonly file: string;\n readonly text: string;\n}\n\nexport interface IPostmortemsCargo extends ICargoHead {\n readonly items: readonly IPostmortemItem[];\n}\n\n/**\n * Обращение дерева за токеном.\n *\n * Имени дерева в нём нет вовсе: его приём берёт из приглашения — принятое из обращения, оно\n * позволило бы назваться чужим именем тому, кто добыл код.\n */\nexport interface IEnrollCargo {\n readonly schema: string;\n /** Признак дерева: его дерево считает у себя, а приём только сверяет. */\n readonly tree: string;\n /** Код приглашения, выданный владельцем. Уходит один раз и в журнал не попадает. */\n readonly code: string;\n}\n\n/** Ответ приёма на годное обращение: токен уходит дереву единственным этим ответом. */\nexport interface IEnrollGranted {\n readonly tree: string;\n /** Имя дерева, как его назвал владелец в приглашении. */\n readonly name: string;\n readonly token: string;\n}\n\n/** Ответ приёма на принятый груз: дерево и месяц печатаются владельцу отправляющей стороной. */\nexport interface IIntakeAccepted {\n readonly tree: string;\n /** Месяц записи в форме `2026-08`, по всемирному времени приёма. */\n readonly month: string;\n /** Запись месяца заведена этим запросом, а не обновлена. */\n readonly created: boolean;\n /**\n * Сколько записей легло этим запросом и сколько приехало повторно.\n *\n * Есть только у предложений: у них приём отбирает уже приехавшее по признаку, и без этих\n * двух чисел отправитель печатает «уехало» в том числе тогда, когда нового не уехало ничего.\n * Приём прежней редакции их не присылает вовсе — отсюда необязательность.\n */\n readonly added?: number;\n readonly known?: number;\n}\n\n/** Род записи груза, у которой есть состояние. Набор закрыт: сводка месяца состояния не несёт. */\nexport type TCargoStateKind = 'postmortem' | 'proposal';\n\n/** Состояния записи груза по порядку: шаг идёт вперёд на соседнее, назад — только из «в работе». */\nexport const CARGO_STATES: readonly string[] = ['new', 'in_work', 'fixed', 'released'];\n\n/** Одна строка правки состояния: род записи, ключ и состояние, в которое её переводят. */\nexport interface ICargoStateItem {\n readonly kind: TCargoStateKind;\n /** Ключ записи: имя файла у разбора происшествия, признак текста у предложения. */\n readonly key: string;\n readonly state: string;\n /**\n * Чем недочёт исправлен. Едет только при переходе в «починено и не выпущено» — с другим\n * переходом приём отбивает строку, а без него отбивает переход в починку.\n */\n readonly fixNote?: string;\n /**\n * В какой версии искать фикс. Едет только при переходе в «выпущено» — с другим переходом\n * приём отбивает строку, а без него отбивает переход в выпуск.\n */\n readonly releaseVersion?: string;\n}\n\n/** Пакет правки состояния: то же общее, что у всякого груза, плюс строки правки. */\nexport interface ICargoStateBody extends ICargoHead {\n readonly items: readonly ICargoStateItem[];\n}\n\n/** Строка, которую приём не исполнил: место в пакете, род записи, ключ и причина. */\nexport interface ICargoStateDenied {\n readonly at: number;\n readonly kind: TCargoStateKind;\n readonly key: string;\n /** Почему не исполнена: записи у дерева нет либо переход не разрешён. */\n readonly denial: string;\n}\n\n/** Ответ приёма на правку состояния: признак дерева, два числа и отбитые строки. */\nexport interface ICargoStateAccepted {\n readonly tree: string;\n /** Сколько записей переведено. */\n readonly changed: number;\n /** Сколько уже стояло в названном состоянии: переходом это не считается. */\n readonly same: number;\n readonly denied: readonly ICargoStateDenied[];\n}\n"]}
|
package/lib/cascade.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cascade.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/cascade.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAiB,eAAe,EAAY,UAAU,EAAE,MAAM,cAAc,CAAC;AAIpF,qDAAqD;AACrD,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;
|
|
1
|
+
{"version":3,"file":"cascade.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/cascade.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAiB,eAAe,EAAY,UAAU,EAAE,MAAM,cAAc,CAAC;AAIpF,qDAAqD;AACrD,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AA0FD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,EAAE,SAAS,EAAE,UAAU,GAAG,SAAS,WAAW,EAAE,CAM9G;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,EAAE,SAAS,EAAE,UAAU,GAAG,SAAS,WAAW,EAAE,CAE9G;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,EAAE,SAAS,EAAE,UAAU,GAAG,SAAS,eAAe,EAAE,CAIpH;AAED,gDAAgD;AAChD,MAAM,WAAW,SAAS;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACvB;AA0BD,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,EAAE,SAAS,EAAE,UAAU,GAAG,SAAS,SAAS,EAAE,CAqB1G"}
|
package/lib/cascade.js
CHANGED
|
@@ -49,6 +49,24 @@ function patternsCutByRules(catalog, selection, rootByRule) {
|
|
|
49
49
|
}
|
|
50
50
|
return cuts;
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Холодные части, снятые вслед за своим правилом. Родитель ищется по имени, а не по полю шапки:
|
|
54
|
+
* холодная часть принадлежит одному правилу и называется так же, как оно, — второго имени у неё
|
|
55
|
+
* нет и быть не может.
|
|
56
|
+
*/
|
|
57
|
+
function pitfallsCutByRules(catalog, selection, rootByRule) {
|
|
58
|
+
const taken = takenNames(catalog, selection, 'rules');
|
|
59
|
+
const known = knownNames(catalog, 'rules');
|
|
60
|
+
const cuts = [];
|
|
61
|
+
for (const entry of chosenOfKind(catalog, selection, 'pitfalls')) {
|
|
62
|
+
const rule = shortNameOf(entry);
|
|
63
|
+
const standing = taken.has(rule) && !rootByRule.has(rule);
|
|
64
|
+
if (known.has(rule) && !standing) {
|
|
65
|
+
cuts.push({ id: entry.id, parent: rule, root: rootByRule.get(rule) ?? rule });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return cuts;
|
|
69
|
+
}
|
|
52
70
|
/**
|
|
53
71
|
* Ресурсы, снятые каскадом: правила при невзятом законе и паттерны при невзятых правилах.
|
|
54
72
|
*
|
|
@@ -64,7 +82,7 @@ export function cascadeCuts(catalog, selection) {
|
|
|
64
82
|
/** Правило, снятое законом: для паттернов при нём корнем цепочки будет этот закон. */
|
|
65
83
|
const rootByRule = new Map();
|
|
66
84
|
const rules = rulesCutByLaws(catalog, selection, rootByRule);
|
|
67
|
-
return [...rules, ...patternsCutByRules(catalog, selection, rootByRule)];
|
|
85
|
+
return [...rules, ...patternsCutByRules(catalog, selection, rootByRule), ...pitfallsCutByRules(catalog, selection, rootByRule)];
|
|
68
86
|
}
|
|
69
87
|
/**
|
|
70
88
|
* Ресурсы, названные выбором поимённо и всё равно снятые каскадом.
|
package/lib/cascade.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cascade.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/cascade.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAmB,QAAQ,EAAc,MAAM,cAAc,CAAC;AAEpF,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAe/C,kGAAkG;AAClG,MAAM,WAAW,GAAuC,CAAC,KAAsB,EAAU,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC;AAEtI,kCAAkC;AAClC,SAAS,YAAY,CAAC,OAAmC,EAAE,SAAqB,EAAE,IAAW;IACzF,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,KAAsB,EAAW,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;AAClH,CAAC;AAED,2CAA2C;AAC3C,SAAS,UAAU,CAAC,OAAmC,EAAE,SAAqB,EAAE,IAAW;IACvF,OAAO,IAAI,GAAG,CAAC,YAAY,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED,4DAA4D;AAC5D,SAAS,UAAU,CAAC,OAAmC,EAAE,IAAW;IAChE,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAsB,EAAW,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;AAC9G,CAAC;AAED,oGAAoG;AACpG,SAAS,cAAc,CACnB,OAAmC,EACnC,SAAqB,EACrB,UAA+B;IAE/B,MAAM,KAAK,GAAwB,UAAU,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAC1E,MAAM,KAAK,GAAwB,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC/D,MAAM,IAAI,GAAkB,EAAE,CAAC;IAE/B,KAAK,MAAM,KAAK,IAAI,YAAY,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC;QAC5D,MAAM,GAAG,GAAW,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC;QAElD,IAAI,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3C,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;YACpD,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;QAC5C,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,oGAAoG;AACpG,SAAS,kBAAkB,CACvB,OAAmC,EACnC,SAAqB,EACrB,UAAuC;IAEvC,MAAM,KAAK,GAAwB,UAAU,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAC3E,MAAM,KAAK,GAAwB,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAChE,MAAM,IAAI,GAAkB,EAAE,CAAC;IAE/B,KAAK,MAAM,KAAK,IAAI,YAAY,CAAC,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,GAAW,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;QACpD,MAAM,QAAQ,GAAY,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAEnE,IAAI,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACvC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAClF,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,OAAmC,EAAE,SAAqB;IAClF,sFAAsF;IACtF,MAAM,UAAU,GAAwB,IAAI,GAAG,EAAkB,CAAC;IAClE,MAAM,KAAK,GAA2B,cAAc,CAAC,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IAErF,OAAO,CAAC,GAAG,KAAK,EAAE,GAAG,kBAAkB,CAAC,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,OAAmC,EAAE,SAAqB;IAClF,OAAO,WAAW,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,GAAgB,EAAW,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;AAClH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,OAAmC,EAAE,SAAqB;IACpF,MAAM,GAAG,GAAwB,IAAI,GAAG,CAAC,WAAW,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,GAAgB,EAAU,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAEpH,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,KAAsB,EAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;AACjH,CAAC;AASD;;;;;;;;;;GAUG;AACH;;;;;GAKG;AACH,SAAS,WAAW,CAAC,OAAmC,EAAE,SAAqB,EAAE,EAAU;IACvF,MAAM,OAAO,GAAe,EAAE,GAAG,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAW,EAAW,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,CAAC;IAChH,MAAM,GAAG,GAA4B,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,GAAgB,EAAW,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAEtH,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAmC,EAAE,SAAqB;IAChF,MAAM,IAAI,GAAgB,EAAE,CAAC;IAE7B,KAAK,MAAM,EAAE,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAgC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAoB,EAAW,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAE1G,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAC9B,CAAC;aAAM,IAAI,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3D,MAAM,EAAE,GAAkB,WAAW,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;YAE9D,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;gBACd,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;YAC1B,CAAC;QACL,CAAC;aAAM,CAAC;YACJ,iFAAiF;YACjF,gCAAgC;QACpC,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC","sourcesContent":["/**\n * Каскад отказа: ресурс, чей родитель не поехал в дерево, снимается вслед за ним.\n *\n * Отдельным модулем от каталога: каталог знает, какие ресурсы бывают, а каскад — как они связаны\n * родством. Связь родителя с потомком читают двое — сверка связности пакета и сама раскладка, —\n * и оба берут её отсюда.\n */\n\nimport { frontMatterOf, IEntryOfCatalog, isChosen, ISelection } from './catalog.js';\nimport { TKind } from './config.js';\nimport { matchesVariant } from './variants.js';\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/** Взятые ресурсы одного рода. */\nfunction chosenOfKind(catalog: readonly IEntryOfCatalog[], selection: ISelection, kind: TKind): readonly IEntryOfCatalog[] {\n return catalog.filter((entry: IEntryOfCatalog): boolean => entry.kind === kind && isChosen(entry, selection));\n}\n\n/** Короткие имена взятых ресурсов рода. */\nfunction takenNames(catalog: readonly IEntryOfCatalog[], selection: ISelection, kind: TKind): ReadonlySet<string> {\n return new Set(chosenOfKind(catalog, selection, kind).map(shortNameOf));\n}\n\n/** Короткие имена всех ресурсов рода, взяты они или нет. */\nfunction knownNames(catalog: readonly IEntryOfCatalog[], kind: TKind): ReadonlySet<string> {\n return new Set(catalog.filter((entry: IEntryOfCatalog): boolean => entry.kind === kind).map(shortNameOf));\n}\n\n/** Правила, снятые вслед за своим законом. Заодно называет корень цепочки для паттернов при них. */\nfunction rulesCutByLaws(\n catalog: readonly IEntryOfCatalog[],\n selection: ISelection,\n rootByRule: Map<string, string>\n): readonly ICascadeCut[] {\n const taken: ReadonlySet<string> = takenNames(catalog, selection, 'laws');\n const known: ReadonlySet<string> = knownNames(catalog, 'laws');\n const cuts: ICascadeCut[] = [];\n\n for (const entry of chosenOfKind(catalog, selection, 'rules')) {\n const law: string = frontMatterOf(entry.text).law;\n\n if (law && known.has(law) && !taken.has(law)) {\n cuts.push({ id: entry.id, parent: law, root: law });\n rootByRule.set(shortNameOf(entry), law);\n }\n }\n\n return cuts;\n}\n\n/** Паттерны, снятые вслед за своим правилом: либо оно не взято, либо само ушло за своим законом. */\nfunction patternsCutByRules(\n catalog: readonly IEntryOfCatalog[],\n selection: ISelection,\n rootByRule: ReadonlyMap<string, string>\n): readonly ICascadeCut[] {\n const taken: ReadonlySet<string> = takenNames(catalog, selection, 'rules');\n const known: ReadonlySet<string> = knownNames(catalog, 'rules');\n const cuts: ICascadeCut[] = [];\n\n for (const entry of chosenOfKind(catalog, selection, 'patterns')) {\n const rule: string = frontMatterOf(entry.text).rule;\n const standing: boolean = taken.has(rule) && !rootByRule.has(rule);\n\n if (rule && known.has(rule) && !standing) {\n cuts.push({ id: entry.id, parent: rule, root: rootByRule.get(rule) ?? rule });\n }\n }\n\n return cuts;\n}\n\n/**\n * Ресурсы, снятые каскадом: правила при невзятом законе и паттерны при невзятых правилах.\n *\n * Связь читается из вступления самого ресурса — отдельный список при пакете разошёлся бы с\n * ресурсами молча, тем же молчанием, ради которого каскад и заводится. Родитель ищется по\n * последнему звену имени: полного пути в шапке нет, потому что переезд закона между слоями\n * переписывал бы шапки всех правил при нём.\n *\n * Родителя в каталоге нет вовсе — каскад молчит: снимать не по чему, а промах в шапке судит\n * сверка связности пакета, до всякой раскладки в дереве.\n */\nexport function cascadeCuts(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly ICascadeCut[] {\n /** Правило, снятое законом: для паттернов при нём корнем цепочки будет этот закон. */\n const rootByRule: Map<string, string> = new Map<string, string>();\n const rules: readonly ICascadeCut[] = rulesCutByLaws(catalog, selection, rootByRule);\n\n return [...rules, ...patternsCutByRules(catalog, selection, rootByRule)];\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 */\n/**\n * Корень цепочки, которой ресурс снят и без этой строки отказа. Пусто — каскад его не трогает.\n *\n * Каскад считается от отбора без самой строки: иначе снятое ею читалось бы как снятое родителем,\n * и лишней оказалась бы любая строка отказа подряд.\n */\nfunction cutParentOf(catalog: readonly IEntryOfCatalog[], selection: ISelection, id: string): string | null {\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\n return cut ? cut.root : null;\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\n if (!entry) {\n idle.push({ id, by: '' });\n } else if (matchesVariant(entry.variant, selection.variants)) {\n const by: string | null = cutParentOf(catalog, selection, id);\n\n if (by !== null) {\n idle.push({ id, by });\n }\n } else {\n // Строка на ресурс чужого вида работу делает: ею дерево гасит отказ о ресурсе, у\n // которого подходящего вида нет\n }\n }\n\n return idle;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cascade.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/cascade.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Каскад отказа: ресурс, чей родитель не поехал в дерево, снимается вслед за ним.\n *\n * Отдельным модулем от каталога: каталог знает, какие ресурсы бывают, а каскад — как они связаны\n * родством. Связь родителя с потомком читают двое — сверка связности пакета и сама раскладка, —\n * и оба берут её отсюда.\n */\n\nimport { frontMatterOf, IEntryOfCatalog, isChosen, ISelection } from './catalog.js';\nimport { TKind } from './config.js';\nimport { matchesVariant } from './variants.js';\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/** Взятые ресурсы одного рода. */\nfunction chosenOfKind(catalog: readonly IEntryOfCatalog[], selection: ISelection, kind: TKind): readonly IEntryOfCatalog[] {\n return catalog.filter((entry: IEntryOfCatalog): boolean => entry.kind === kind && isChosen(entry, selection));\n}\n\n/** Короткие имена взятых ресурсов рода. */\nfunction takenNames(catalog: readonly IEntryOfCatalog[], selection: ISelection, kind: TKind): ReadonlySet<string> {\n return new Set(chosenOfKind(catalog, selection, kind).map(shortNameOf));\n}\n\n/** Короткие имена всех ресурсов рода, взяты они или нет. */\nfunction knownNames(catalog: readonly IEntryOfCatalog[], kind: TKind): ReadonlySet<string> {\n return new Set(catalog.filter((entry: IEntryOfCatalog): boolean => entry.kind === kind).map(shortNameOf));\n}\n\n/** Правила, снятые вслед за своим законом. Заодно называет корень цепочки для паттернов при них. */\nfunction rulesCutByLaws(\n catalog: readonly IEntryOfCatalog[],\n selection: ISelection,\n rootByRule: Map<string, string>\n): readonly ICascadeCut[] {\n const taken: ReadonlySet<string> = takenNames(catalog, selection, 'laws');\n const known: ReadonlySet<string> = knownNames(catalog, 'laws');\n const cuts: ICascadeCut[] = [];\n\n for (const entry of chosenOfKind(catalog, selection, 'rules')) {\n const law: string = frontMatterOf(entry.text).law;\n\n if (law && known.has(law) && !taken.has(law)) {\n cuts.push({ id: entry.id, parent: law, root: law });\n rootByRule.set(shortNameOf(entry), law);\n }\n }\n\n return cuts;\n}\n\n/** Паттерны, снятые вслед за своим правилом: либо оно не взято, либо само ушло за своим законом. */\nfunction patternsCutByRules(\n catalog: readonly IEntryOfCatalog[],\n selection: ISelection,\n rootByRule: ReadonlyMap<string, string>\n): readonly ICascadeCut[] {\n const taken: ReadonlySet<string> = takenNames(catalog, selection, 'rules');\n const known: ReadonlySet<string> = knownNames(catalog, 'rules');\n const cuts: ICascadeCut[] = [];\n\n for (const entry of chosenOfKind(catalog, selection, 'patterns')) {\n const rule: string = frontMatterOf(entry.text).rule;\n const standing: boolean = taken.has(rule) && !rootByRule.has(rule);\n\n if (rule && known.has(rule) && !standing) {\n cuts.push({ id: entry.id, parent: rule, root: rootByRule.get(rule) ?? rule });\n }\n }\n\n return cuts;\n}\n\n/**\n * Холодные части, снятые вслед за своим правилом. Родитель ищется по имени, а не по полю шапки:\n * холодная часть принадлежит одному правилу и называется так же, как оно, — второго имени у неё\n * нет и быть не может.\n */\nfunction pitfallsCutByRules(\n catalog: readonly IEntryOfCatalog[],\n selection: ISelection,\n rootByRule: ReadonlyMap<string, string>\n): readonly ICascadeCut[] {\n const taken: ReadonlySet<string> = takenNames(catalog, selection, 'rules');\n const known: ReadonlySet<string> = knownNames(catalog, 'rules');\n const cuts: ICascadeCut[] = [];\n\n for (const entry of chosenOfKind(catalog, selection, 'pitfalls')) {\n const rule: string = shortNameOf(entry);\n const standing: boolean = taken.has(rule) && !rootByRule.has(rule);\n\n if (known.has(rule) && !standing) {\n cuts.push({ id: entry.id, parent: rule, root: rootByRule.get(rule) ?? rule });\n }\n }\n\n return cuts;\n}\n\n/**\n * Ресурсы, снятые каскадом: правила при невзятом законе и паттерны при невзятых правилах.\n *\n * Связь читается из вступления самого ресурса — отдельный список при пакете разошёлся бы с\n * ресурсами молча, тем же молчанием, ради которого каскад и заводится. Родитель ищется по\n * последнему звену имени: полного пути в шапке нет, потому что переезд закона между слоями\n * переписывал бы шапки всех правил при нём.\n *\n * Родителя в каталоге нет вовсе — каскад молчит: снимать не по чему, а промах в шапке судит\n * сверка связности пакета, до всякой раскладки в дереве.\n */\nexport function cascadeCuts(catalog: readonly IEntryOfCatalog[], selection: ISelection): readonly ICascadeCut[] {\n /** Правило, снятое законом: для паттернов при нём корнем цепочки будет этот закон. */\n const rootByRule: Map<string, string> = new Map<string, string>();\n const rules: readonly ICascadeCut[] = rulesCutByLaws(catalog, selection, rootByRule);\n\n return [...rules, ...patternsCutByRules(catalog, selection, rootByRule), ...pitfallsCutByRules(catalog, selection, rootByRule)];\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 */\n/**\n * Корень цепочки, которой ресурс снят и без этой строки отказа. Пусто — каскад его не трогает.\n *\n * Каскад считается от отбора без самой строки: иначе снятое ею читалось бы как снятое родителем,\n * и лишней оказалась бы любая строка отказа подряд.\n */\nfunction cutParentOf(catalog: readonly IEntryOfCatalog[], selection: ISelection, id: string): string | null {\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\n return cut ? cut.root : null;\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\n if (!entry) {\n idle.push({ id, by: '' });\n } else if (matchesVariant(entry.variant, selection.variants)) {\n const by: string | null = cutParentOf(catalog, selection, id);\n\n if (by !== null) {\n idle.push({ id, by });\n }\n } else {\n // Строка на ресурс чужого вида работу делает: ею дерево гасит отказ о ресурсе, у\n // которого подходящего вида нет\n }\n }\n\n return idle;\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":"AAcA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/commands.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;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;AA+UpD;;;;GAIG;AACH,wBAAgB,IAAI,CAChB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,SAAS,MAAM,EAAO,EAC5B,QAAQ,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,EAC/C,SAAS,GAAE,MAAW,GACvB,iBAAiB,CAgCnB;AA8FD,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,iBAAiB,CA4DzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,iBAAiB,CA+CpF;AAQD,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8FAA8F;IAC9F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CAC1B;AAqHD;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,GAAG,iBAAiB,CAuDlF;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CA2E3D;AAED;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CA2DzD"}
|
package/lib/commands.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* а `process.exit` зовёт только точка входа — иначе ни одну из них нельзя было бы проверить
|
|
5
5
|
* спекой, не перехватывая поток вывода.
|
|
6
6
|
*/
|
|
7
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync } from 'node:fs';
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
import { collectAssets } from './assets.js';
|
|
10
10
|
import { cascadeCuts } from './cascade.js';
|
|
@@ -12,10 +12,11 @@ import { isChosen, readCatalog } from './catalog.js';
|
|
|
12
12
|
import { debtLine, isUnfilled, pathOf as companionPathOf, unaddressedOf } from './companion.js';
|
|
13
13
|
import { CONFIG_PATH, DEFAULT_LAYOUT, KINDS, OVERRIDES_DIR, PROFILE_FILE, readConfig, RT_KIT_DIR } from './config.js';
|
|
14
14
|
import { hooksSection, SETTINGS_PATH } from './hooks-map.js';
|
|
15
|
-
import { DEFAULT_DAYS, KEEP_DAYS, OBSERVATIONS_DIR, readObservations, summarize } from './observations.js';
|
|
15
|
+
import { DEFAULT_DAYS, KEEP_DAYS, OBSERVATIONS_DIR, readObservations, summarize, } from './observations.js';
|
|
16
16
|
import { isRefusal } from './plan.js';
|
|
17
17
|
import { laidOutSkills } from './snapshot.js';
|
|
18
18
|
import { pendingOf, planSync, runSync } from './sync.js';
|
|
19
|
+
import { thresholdLines } from './thresholds.js';
|
|
19
20
|
import { answersRequirement, readTraits, unknownTraits } from './traits.js';
|
|
20
21
|
import { placeholdersOf } from './vars.js';
|
|
21
22
|
import { readAxes, unansweredAxes } from './variants.js';
|
|
@@ -51,6 +52,7 @@ const NEEDS_TRAIT = 'нужно свойство';
|
|
|
51
52
|
const KIND_TITLE = {
|
|
52
53
|
laws: 'ЗАКОНЫ',
|
|
53
54
|
rules: 'ПРАВИЛА',
|
|
55
|
+
pitfalls: 'ХОЛОДНЫЕ ЧАСТИ',
|
|
54
56
|
patterns: 'ПАТТЕРНЫ',
|
|
55
57
|
skills: 'СКИЛЫ',
|
|
56
58
|
hooks: 'ХУКИ',
|
|
@@ -585,6 +587,52 @@ const countLines = (counted) => {
|
|
|
585
587
|
const width = counted.reduce((found, entry) => Math.max(found, entry.name.length), 0);
|
|
586
588
|
return counted.map((entry) => ` ${entry.name.padEnd(width)} ${entry.count}`);
|
|
587
589
|
};
|
|
590
|
+
/**
|
|
591
|
+
* Вес разложенных правил на диске: имя правила в сумму байт его текстов.
|
|
592
|
+
*
|
|
593
|
+
* Считается здесь, а не в сводке: сводка разбирает наблюдения и диска не знает. Складываются все
|
|
594
|
+
* `.md` папки правила — само правило, компаньон с именами дерева и всё, что дерево положило
|
|
595
|
+
* рядом: загрузка правила тянет их не поодиночке, и раздельный счёт врал бы в меньшую сторону.
|
|
596
|
+
*
|
|
597
|
+
* Правило, которого в дереве нет, в карту не попадает вовсе. Ноль от него неотличим по числу, но
|
|
598
|
+
* отличим по смыслу: не «ничего не весит», а «весить нечему».
|
|
599
|
+
*/
|
|
600
|
+
function weightsOfSkills(root, dir, names) {
|
|
601
|
+
const at = join(root, dir);
|
|
602
|
+
const found = {};
|
|
603
|
+
for (const name of names) {
|
|
604
|
+
const folder = join(at, name);
|
|
605
|
+
if (!existsSync(folder)) {
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
found[name] = readdirSync(folder)
|
|
609
|
+
.filter((file) => file.endsWith('.md'))
|
|
610
|
+
.reduce((sum, file) => sum + statSync(join(folder, file)).size, 0);
|
|
611
|
+
}
|
|
612
|
+
return found;
|
|
613
|
+
}
|
|
614
|
+
/** Байты человеку: килобайтами, потому что счёт идёт на сотни тысяч и читается по одному. */
|
|
615
|
+
function kbOf(bytes) {
|
|
616
|
+
return `${Math.round(bytes / 1024)} КБ`;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Строка о том, сколько отбитий пришло не на правку файла.
|
|
620
|
+
*
|
|
621
|
+
* Своей строкой, а не долей, выведенной читателем из разбивки по родам: гейт стоит ради правки, и
|
|
622
|
+
* доля, пришедшая на команду оболочки и на браузер, говорит, сколько раз правило потребовали под
|
|
623
|
+
* то, что правкой не является. Сложенная глазами из десятка строк, эта доля не складывается
|
|
624
|
+
* никогда.
|
|
625
|
+
*
|
|
626
|
+
* Молчит, когда отбитий не было вовсе: «0 процентов» при нуле отбитий — ответ на незаданный
|
|
627
|
+
* вопрос.
|
|
628
|
+
*/
|
|
629
|
+
function offFileLines(summary) {
|
|
630
|
+
const total = summary.denials.reduce((found, entry) => found + entry.count, 0);
|
|
631
|
+
if (total === 0) {
|
|
632
|
+
return [];
|
|
633
|
+
}
|
|
634
|
+
return [` не на правке файла: ${summary.denialsOffFile} из ${total} — ${Math.round((summary.denialsOffFile / total) * 100)}%`];
|
|
635
|
+
}
|
|
588
636
|
/** Сводка строками: загруженное, незагруженное, отбивки гейта и отказы гардов. */
|
|
589
637
|
function statsLines(summary, swept, days, version, laidOut) {
|
|
590
638
|
return [
|
|
@@ -592,6 +640,14 @@ function statsLines(summary, swept, days, version, laidOut) {
|
|
|
592
640
|
...(summary.versions.length ? [`версии пакета в записях: ${summary.versions.join(', ')}`] : []),
|
|
593
641
|
'',
|
|
594
642
|
`правил загружено: ${summary.loads.reduce((found, entry) => found + entry.count, 0)}`,
|
|
643
|
+
// Вес молчит, когда весить нечем: каталог правил не разложен или пуст. Ноль килобайт при
|
|
644
|
+
// сотне загрузок читался бы как «правила ничего не стоят», а это не то же самое.
|
|
645
|
+
...(summary.bytes > 0
|
|
646
|
+
? [
|
|
647
|
+
` весом ${kbOf(summary.bytes)}, на один заход ${kbOf(summary.bytesPerSession)}`,
|
|
648
|
+
' — столько текста заход прочитал прежде, чем взяться за работу',
|
|
649
|
+
]
|
|
650
|
+
: []),
|
|
595
651
|
...countLines(summary.loads),
|
|
596
652
|
...(summary.unused.length
|
|
597
653
|
? [
|
|
@@ -610,9 +666,10 @@ function statsLines(summary, swept, days, version, laidOut) {
|
|
|
610
666
|
? [
|
|
611
667
|
'',
|
|
612
668
|
`гейт отбивал: ${summary.denials.reduce((found, entry) => found + entry.count, 0)}`,
|
|
669
|
+
...offFileLines(summary),
|
|
613
670
|
...countLines(summary.denials),
|
|
614
671
|
...(summary.kinds.length
|
|
615
|
-
? ['
|
|
672
|
+
? [' по роду правки:', ...countLines(summary.kinds).map((line) => ` ${line}`)]
|
|
616
673
|
: []),
|
|
617
674
|
]
|
|
618
675
|
: []),
|
|
@@ -654,7 +711,10 @@ export function stats(env, options) {
|
|
|
654
711
|
}
|
|
655
712
|
const days = options.days > 0 ? options.days : DEFAULT_DAYS;
|
|
656
713
|
const result = readObservations(root, options.today, days);
|
|
657
|
-
const
|
|
714
|
+
const known = laidOutSkills(config, assetsDir);
|
|
715
|
+
// Вес спрашивается у дерева, а не у пакета: правило дерево могло переписать надстройкой, и
|
|
716
|
+
// заход читал то, что лежит здесь, а не то, что уехало бы из пакета.
|
|
717
|
+
const summary = summarize(result.observations, known, days, weightsOfSkills(root, config.layout.rules, known));
|
|
658
718
|
if (options.json) {
|
|
659
719
|
return { code: 0, lines: [JSON.stringify({ ...summary, swept: result.swept })] };
|
|
660
720
|
}
|
|
@@ -728,6 +788,7 @@ export function doctor(env) {
|
|
|
728
788
|
return ` снят каскадом: ${one.id} — вслед за ${one.parent}${root}`;
|
|
729
789
|
}),
|
|
730
790
|
...profileLines(root, assetsDir, config),
|
|
791
|
+
...thresholdLines(root),
|
|
731
792
|
`значений в конфиге: ${Object.keys(config.vars).length}`,
|
|
732
793
|
...chosen,
|
|
733
794
|
...[...counted].map(([outcome, count]) => `${STATE_WORD[outcome]}: ${count}`),
|