@rt-tools/agent-kit 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/assets/checks/check-state-next.mjs +194 -0
  2. package/assets/checks/check-states.mjs +142 -0
  3. package/assets/checks/check-turn-map.mjs +146 -0
  4. package/assets/commands/agent-kit-digest.md +6 -5
  5. package/assets/defaults/project.sh +58 -6
  6. package/assets/defaults/turn-map.md +46 -0
  7. package/assets/hooks/browser-device-id.sh +2 -0
  8. package/assets/hooks/browser-guard-device-id.sh +2 -0
  9. package/assets/hooks/browser-guard-no-asking.sh +2 -0
  10. package/assets/hooks/browser-guard-no-listing.sh +2 -0
  11. package/assets/hooks/browser-guard-no-other-drivers.sh +2 -0
  12. package/assets/hooks/browser-guard-require-select.sh +2 -0
  13. package/assets/hooks/claim-guard.sh +2 -0
  14. package/assets/hooks/commit-msg.sh +2 -0
  15. package/assets/hooks/conscience-guard.sh +2 -0
  16. package/assets/hooks/constitution-index.sh +2 -0
  17. package/assets/hooks/dev-server-guard.sh +2 -0
  18. package/assets/hooks/docs-guard.sh +2 -0
  19. package/assets/hooks/exam-guard.sh +2 -0
  20. package/assets/hooks/git-guard-delivery-signature.sh +2 -0
  21. package/assets/hooks/git-guard-delivery.sh +2 -0
  22. package/assets/hooks/git-guard-main.sh +2 -0
  23. package/assets/hooks/git-guard-push-tests.sh +2 -0
  24. package/assets/hooks/glossary-load.sh +2 -0
  25. package/assets/hooks/grill-gate.sh +2 -0
  26. package/assets/hooks/handoff-entry-guard.sh +2 -0
  27. package/assets/hooks/handoff-write.sh +103 -0
  28. package/assets/hooks/lint-after-edit.sh +2 -0
  29. package/assets/hooks/observe.sh +2 -0
  30. package/assets/hooks/postmortem-guard.sh +2 -0
  31. package/assets/hooks/proposal-guard.sh +2 -0
  32. package/assets/hooks/prose-style-guard.sh +2 -0
  33. package/assets/hooks/qa-dataid-guard.sh +2 -0
  34. package/assets/hooks/rerun-guard.sh +2 -0
  35. package/assets/hooks/reuse-first-guard.sh +2 -0
  36. package/assets/hooks/roles.sh +2 -0
  37. package/assets/hooks/skill-gate-layers.sh +2 -0
  38. package/assets/hooks/skill-gate-rearm.sh +2 -0
  39. package/assets/hooks/skill-gate.sh +2 -0
  40. package/assets/hooks/skill-loaded.sh +2 -0
  41. package/assets/hooks/sql-guard-parse.sh +2 -0
  42. package/assets/hooks/sql-guard-request.sh +2 -0
  43. package/assets/hooks/sql-guard-target.sh +2 -0
  44. package/assets/hooks/sql-guard-write.sh +2 -0
  45. package/assets/hooks/sql-guard.sh +2 -0
  46. package/assets/hooks/task-context-load.sh +2 -0
  47. package/assets/hooks/task-flow-guard.sh +2 -0
  48. package/assets/hooks/turn-entry-load.sh +62 -0
  49. package/assets/hooks/turn-exit-guard.sh +2 -0
  50. package/assets/hooks/utf8.sh +35 -0
  51. package/assets/hooks/waiting-turn-guard.sh +2 -0
  52. package/assets/hooks/window-fill-guard.sh +31 -1
  53. package/assets/laws/work-conduct.md +29 -0
  54. package/assets/patterns/cargo-triage-mark.md +119 -0
  55. package/assets/patterns/task-flow-close.md +21 -0
  56. package/assets/patterns/task-flow-handoff.md +18 -0
  57. package/assets/patterns/task-flow-resume.md +6 -0
  58. package/assets/patterns/task-flow-start.md +41 -0
  59. package/assets/patterns/turn-entry-map.md +81 -0
  60. package/assets/rules/cargo-triage.md +126 -0
  61. package/assets/rules/task-flow.md +14 -1
  62. package/assets/rules/turn-entry.md +93 -0
  63. package/bin/agent-kit.d.ts.map +1 -1
  64. package/bin/agent-kit.js +42 -1
  65. package/bin/agent-kit.js.map +1 -1
  66. package/lib/cargo-state.d.ts +62 -0
  67. package/lib/cargo-state.d.ts.map +1 -0
  68. package/lib/cargo-state.js +118 -0
  69. package/lib/cargo-state.js.map +1 -0
  70. package/lib/cargo.d.ts +42 -0
  71. package/lib/cargo.d.ts.map +1 -1
  72. package/lib/cargo.js +2 -0
  73. package/lib/cargo.js.map +1 -1
  74. package/lib/commands.d.ts.map +1 -1
  75. package/lib/commands.js +64 -4
  76. package/lib/commands.js.map +1 -1
  77. package/lib/observations.d.ts +35 -1
  78. package/lib/observations.d.ts.map +1 -1
  79. package/lib/observations.js +14 -2
  80. package/lib/observations.js.map +1 -1
  81. package/lib/ship.d.ts +3 -0
  82. package/lib/ship.d.ts.map +1 -1
  83. package/lib/ship.js +56 -0
  84. package/lib/ship.js.map +1 -1
  85. package/lib/thresholds.d.ts +49 -0
  86. package/lib/thresholds.d.ts.map +1 -0
  87. package/lib/thresholds.js +151 -0
  88. package/lib/thresholds.js.map +1 -0
  89. package/package.json +1 -1
  90. package/rt-tools-agent-kit-0.11.0.tgz +0 -0
  91. package/rt-tools-agent-kit-0.10.0.tgz +0 -0
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Отметка состояния груза: команда, которой исполнитель двигает свои записи в приёме.
3
+ *
4
+ * Разбор доводов и сборка пакета отделены от самого запроса нарочно — тем же приёмом, что и
5
+ * отправка груза: спека подставляет двойник вызова и никуда не стучится, а всё, что можно
6
+ * отбить до сети, отбивается до сети. Незнакомое состояние и вызов без записей — промахи
7
+ * исполнителя, и запрос по ним не уходит вовсе.
8
+ */
9
+ import { CARGO_SCHEMA_VERSION, CARGO_STATES } from './cargo.js';
10
+ /** Код возврата у всего, что не легло: отбитая строка кончает команду ненулевым кодом. */
11
+ const REFUSED = 1;
12
+ /** Довод, которым называется разбор происшествия. */
13
+ export const POSTMORTEM_FLAG = '--postmortem';
14
+ /** Довод, которым называется предложение. */
15
+ export const PROPOSAL_FLAG = '--proposal';
16
+ /** Довод текста починки: чем недочёт исправлен. */
17
+ export const FIX_FLAG = '--fix';
18
+ /**
19
+ * Довод версии выпуска: в какой версии искать фикс.
20
+ *
21
+ * Назван по выпуску, а не по версии: `--version` у команды строки запуска читается как «покажи
22
+ * свою версию», и заняв его под другое, пакет получил бы две разные вещи под одним словом.
23
+ */
24
+ export const RELEASE_FLAG = '--release';
25
+ /** Род записи по доводу, которым её назвали. Не наш довод — пусто. */
26
+ function kindOfFlag(flag) {
27
+ if (flag === POSTMORTEM_FLAG) {
28
+ return 'postmortem';
29
+ }
30
+ return flag === PROPOSAL_FLAG ? 'proposal' : null;
31
+ }
32
+ /** Записи, названные доводами строки запуска: род у каждой свой, порядок — как их назвали. */
33
+ export function itemsOf(argv, state, attached = { fixNote: '', releaseVersion: '' }) {
34
+ const items = [];
35
+ for (let at = 0; at < argv.length; at += 1) {
36
+ const kind = kindOfFlag(argv[at]);
37
+ const key = argv[at + 1] ?? '';
38
+ if (kind && key && !key.startsWith('--')) {
39
+ // Приложенное значение едет полем строки, а не своим вызовом: отметка одна, и все её
40
+ // записи чинились одним разбором либо уехали одним выпуском — второго значения на
41
+ // тот же вызов не задаётся
42
+ items.push({
43
+ kind,
44
+ key,
45
+ state,
46
+ ...(attached.fixNote ? { fixNote: attached.fixNote } : {}),
47
+ ...(attached.releaseVersion ? { releaseVersion: attached.releaseVersion } : {}),
48
+ });
49
+ }
50
+ }
51
+ return items;
52
+ }
53
+ /** Отказ строками. */
54
+ function refusal(...lines) {
55
+ return { code: REFUSED, lines: [...lines] };
56
+ }
57
+ /** Пакет одной строкой: её читает человек перед тем, как отправить. */
58
+ function describe(items, state) {
59
+ const postmortems = items.filter((one) => one.kind === 'postmortem').length;
60
+ return `в «${state}»: разборов ${postmortems}, предложений ${items.length - postmortems}`;
61
+ }
62
+ /** Причина отбоя словами человека: их шесть, и по каждой видно, что делать дальше. */
63
+ const DENIAL_WORDS = {
64
+ missing: 'такой записи у дерева нет',
65
+ forbidden: 'переход не разрешён порядком',
66
+ 'no-fix-note': 'переход в починку без довода `--fix`',
67
+ 'extra-fix-note': 'довод `--fix` приехал не с переходом в починку',
68
+ 'no-release-version': 'переход в выпуск без довода `--release`',
69
+ 'extra-release-version': 'довод `--release` приехал не с переходом в выпуск',
70
+ };
71
+ /** Отбитая строка человеку: род, ключ и причина словами. */
72
+ function deniedLine(kind, key, denial) {
73
+ const why = DENIAL_WORDS[denial] ?? denial;
74
+ return ` ${kind === 'postmortem' ? 'разбор' : 'предложение'} ${key} — ${why}`;
75
+ }
76
+ /**
77
+ * Отметить записи дерева названным состоянием.
78
+ *
79
+ * Порядок отказов не случаен: сперва то, что видно без сети, — незнакомое состояние, пустой
80
+ * вызов, отсутствующий токен, — и только потом запрос. Холостой ход стоит последним из них:
81
+ * он показывает собранный пакет, а собрать его можно лишь после того, как доводы сошлись.
82
+ */
83
+ export async function mark(options) {
84
+ if (!CARGO_STATES.includes(options.state)) {
85
+ return refusal(`состояния «${options.state}» не бывает`, `бывают: ${CARGO_STATES.join(', ')}`);
86
+ }
87
+ if (options.items.length === 0) {
88
+ return refusal('отмечать нечего: ни одной записи в доводах', `разбор называется \`${POSTMORTEM_FLAG} <имя файла>\`, предложение — \`${PROPOSAL_FLAG} <признак текста>\``);
89
+ }
90
+ if (!options.token) {
91
+ return refusal('токена дерева нет: отметка осталась неотправленной', 'дерево заводится командой `enroll` — по коду приглашения либо токеном из админки приёма');
92
+ }
93
+ const body = {
94
+ schema: CARGO_SCHEMA_VERSION,
95
+ tree: options.tree,
96
+ items: options.items,
97
+ };
98
+ if (options.dryRun) {
99
+ return {
100
+ code: 0,
101
+ lines: [`уехало бы в ${options.intake}, дерево ${options.tree}:`, ` ${describe(options.items, options.state)}`],
102
+ };
103
+ }
104
+ const marked = await options.call(options.intake, options.token, body);
105
+ if (!marked.ok || !marked.accepted) {
106
+ return refusal(`${options.intake} ответил ${marked.status || 'молчанием'} — ${marked.said}`);
107
+ }
108
+ const { changed, same, denied } = marked.accepted;
109
+ const counted = `переведено ${changed}, уже стояло ${same}, отбито ${denied.length}`;
110
+ return {
111
+ code: denied.length ? REFUSED : 0,
112
+ lines: [
113
+ `отмечено в ${options.intake}, дерево ${options.tree}: ${counted}`,
114
+ ...denied.map((one) => deniedLine(one.kind, one.key, one.denial)),
115
+ ],
116
+ };
117
+ }
118
+ //# sourceMappingURL=cargo-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cargo-state.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/cargo-state.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Отметка состояния груза: команда, которой исполнитель двигает свои записи в приёме.\n *\n * Разбор доводов и сборка пакета отделены от самого запроса нарочно — тем же приёмом, что и\n * отправка груза: спека подставляет двойник вызова и никуда не стучится, а всё, что можно\n * отбить до сети, отбивается до сети. Незнакомое состояние и вызов без записей — промахи\n * исполнителя, и запрос по ним не уходит вовсе.\n */\nimport { CARGO_SCHEMA_VERSION, CARGO_STATES, ICargoStateAccepted, ICargoStateBody, ICargoStateItem, TCargoStateKind } from './cargo.js';\nimport { IOutcomeOfCommand } from './commands.js';\n\n/** Код возврата у всего, что не легло: отбитая строка кончает команду ненулевым кодом. */\nconst REFUSED: number = 1;\n\n/** Довод, которым называется разбор происшествия. */\nexport const POSTMORTEM_FLAG: string = '--postmortem';\n\n/** Довод, которым называется предложение. */\nexport const PROPOSAL_FLAG: string = '--proposal';\n\n/** Довод текста починки: чем недочёт исправлен. */\nexport const FIX_FLAG: string = '--fix';\n\n/**\n * Довод версии выпуска: в какой версии искать фикс.\n *\n * Назван по выпуску, а не по версии: `--version` у команды строки запуска читается как «покажи\n * свою версию», и заняв его под другое, пакет получил бы две разные вещи под одним словом.\n */\nexport const RELEASE_FLAG: string = '--release';\n\n/** Что приём ответил на правку состояния. Отказ — такой же ответ, как принятое. */\nexport interface IMarked {\n readonly ok: boolean;\n /** Код ответа. Ноль — приём не ответил вовсе: не дозвонились, оборвалось, вышло время. */\n readonly status: number;\n readonly said: string;\n /** Ответ приёма. Пусто у отказа: два числа и отбитые строки он называет только у принятого. */\n readonly accepted: ICargoStateAccepted | null;\n}\n\n/** Чем отметка ходит в приём. Двойник в спеке — того же вида. */\nexport type TMarkCall = (intake: string, token: string, body: ICargoStateBody) => Promise<IMarked>;\n\n/** Приложенные к строке значения: что едет вместе с состоянием. */\nexport interface ICargoStateAttached {\n /** Чем недочёт исправлен. Пустая строка — довода не было. */\n readonly fixNote: string;\n /** В какой версии искать фикс. Пустая строка — довода не было. */\n readonly releaseVersion: string;\n}\n\n/** Всё, что отметке нужно знать: куда, чем представиться и что отмечать. */\nexport interface IMarkOptions {\n readonly intake: string;\n readonly tree: string;\n readonly token: string;\n readonly state: string;\n readonly items: readonly ICargoStateItem[];\n readonly dryRun: boolean;\n readonly call: TMarkCall;\n}\n\n/** Род записи по доводу, которым её назвали. Не наш довод — пусто. */\nfunction kindOfFlag(flag: string): TCargoStateKind | null {\n if (flag === POSTMORTEM_FLAG) {\n return 'postmortem';\n }\n\n return flag === PROPOSAL_FLAG ? 'proposal' : null;\n}\n\n/** Записи, названные доводами строки запуска: род у каждой свой, порядок — как их назвали. */\nexport function itemsOf(\n argv: readonly string[],\n state: string,\n attached: ICargoStateAttached = { fixNote: '', releaseVersion: '' }\n): readonly ICargoStateItem[] {\n const items: ICargoStateItem[] = [];\n\n for (let at: number = 0; at < argv.length; at += 1) {\n const kind: TCargoStateKind | null = kindOfFlag(argv[at]);\n const key: string = argv[at + 1] ?? '';\n\n if (kind && key && !key.startsWith('--')) {\n // Приложенное значение едет полем строки, а не своим вызовом: отметка одна, и все её\n // записи чинились одним разбором либо уехали одним выпуском — второго значения на\n // тот же вызов не задаётся\n items.push({\n kind,\n key,\n state,\n ...(attached.fixNote ? { fixNote: attached.fixNote } : {}),\n ...(attached.releaseVersion ? { releaseVersion: attached.releaseVersion } : {}),\n });\n }\n }\n\n return items;\n}\n\n/** Отказ строками. */\nfunction refusal(...lines: readonly string[]): IOutcomeOfCommand {\n return { code: REFUSED, lines: [...lines] };\n}\n\n/** Пакет одной строкой: её читает человек перед тем, как отправить. */\nfunction describe(items: readonly ICargoStateItem[], state: string): string {\n const postmortems: number = items.filter((one: ICargoStateItem): boolean => one.kind === 'postmortem').length;\n\n return `в «${state}»: разборов ${postmortems}, предложений ${items.length - postmortems}`;\n}\n\n/** Причина отбоя словами человека: их шесть, и по каждой видно, что делать дальше. */\nconst DENIAL_WORDS: Readonly<Record<string, string>> = {\n missing: 'такой записи у дерева нет',\n forbidden: 'переход не разрешён порядком',\n 'no-fix-note': 'переход в починку без довода `--fix`',\n 'extra-fix-note': 'довод `--fix` приехал не с переходом в починку',\n 'no-release-version': 'переход в выпуск без довода `--release`',\n 'extra-release-version': 'довод `--release` приехал не с переходом в выпуск',\n};\n\n/** Отбитая строка человеку: род, ключ и причина словами. */\nfunction deniedLine(kind: string, key: string, denial: string): string {\n const why: string = DENIAL_WORDS[denial] ?? denial;\n\n return ` ${kind === 'postmortem' ? 'разбор' : 'предложение'} ${key} — ${why}`;\n}\n\n/**\n * Отметить записи дерева названным состоянием.\n *\n * Порядок отказов не случаен: сперва то, что видно без сети, — незнакомое состояние, пустой\n * вызов, отсутствующий токен, — и только потом запрос. Холостой ход стоит последним из них:\n * он показывает собранный пакет, а собрать его можно лишь после того, как доводы сошлись.\n */\nexport async function mark(options: IMarkOptions): Promise<IOutcomeOfCommand> {\n if (!CARGO_STATES.includes(options.state)) {\n return refusal(`состояния «${options.state}» не бывает`, `бывают: ${CARGO_STATES.join(', ')}`);\n }\n\n if (options.items.length === 0) {\n return refusal(\n 'отмечать нечего: ни одной записи в доводах',\n `разбор называется \\`${POSTMORTEM_FLAG} <имя файла>\\`, предложение — \\`${PROPOSAL_FLAG} <признак текста>\\``\n );\n }\n\n if (!options.token) {\n return refusal(\n 'токена дерева нет: отметка осталась неотправленной',\n 'дерево заводится командой `enroll` — по коду приглашения либо токеном из админки приёма'\n );\n }\n\n const body: ICargoStateBody = {\n schema: CARGO_SCHEMA_VERSION,\n tree: options.tree,\n items: options.items,\n };\n\n if (options.dryRun) {\n return {\n code: 0,\n lines: [`уехало бы в ${options.intake}, дерево ${options.tree}:`, ` ${describe(options.items, options.state)}`],\n };\n }\n\n const marked: IMarked = await options.call(options.intake, options.token, body);\n\n if (!marked.ok || !marked.accepted) {\n return refusal(`${options.intake} ответил ${marked.status || 'молчанием'} — ${marked.said}`);\n }\n\n const { changed, same, denied }: ICargoStateAccepted = marked.accepted;\n const counted: string = `переведено ${changed}, уже стояло ${same}, отбито ${denied.length}`;\n\n return {\n code: denied.length ? REFUSED : 0,\n lines: [\n `отмечено в ${options.intake}, дерево ${options.tree}: ${counted}`,\n ...denied.map((one: { kind: string; key: string; denial: string }): string => deniedLine(one.kind, one.key, one.denial)),\n ],\n };\n}\n"]}
package/lib/cargo.d.ts CHANGED
@@ -105,4 +105,46 @@ export interface IIntakeAccepted {
105
105
  readonly added?: number;
106
106
  readonly known?: number;
107
107
  }
108
+ /** Род записи груза, у которой есть состояние. Набор закрыт: сводка месяца состояния не несёт. */
109
+ export type TCargoStateKind = 'postmortem' | 'proposal';
110
+ /** Состояния записи груза по порядку: шаг идёт вперёд на соседнее, назад — только из «в работе». */
111
+ export declare const CARGO_STATES: readonly string[];
112
+ /** Одна строка правки состояния: род записи, ключ и состояние, в которое её переводят. */
113
+ export interface ICargoStateItem {
114
+ readonly kind: TCargoStateKind;
115
+ /** Ключ записи: имя файла у разбора происшествия, признак текста у предложения. */
116
+ readonly key: string;
117
+ readonly state: string;
118
+ /**
119
+ * Чем недочёт исправлен. Едет только при переходе в «починено и не выпущено» — с другим
120
+ * переходом приём отбивает строку, а без него отбивает переход в починку.
121
+ */
122
+ readonly fixNote?: string;
123
+ /**
124
+ * В какой версии искать фикс. Едет только при переходе в «выпущено» — с другим переходом
125
+ * приём отбивает строку, а без него отбивает переход в выпуск.
126
+ */
127
+ readonly releaseVersion?: string;
128
+ }
129
+ /** Пакет правки состояния: то же общее, что у всякого груза, плюс строки правки. */
130
+ export interface ICargoStateBody extends ICargoHead {
131
+ readonly items: readonly ICargoStateItem[];
132
+ }
133
+ /** Строка, которую приём не исполнил: место в пакете, род записи, ключ и причина. */
134
+ export interface ICargoStateDenied {
135
+ readonly at: number;
136
+ readonly kind: TCargoStateKind;
137
+ readonly key: string;
138
+ /** Почему не исполнена: записи у дерева нет либо переход не разрешён. */
139
+ readonly denial: string;
140
+ }
141
+ /** Ответ приёма на правку состояния: признак дерева, два числа и отбитые строки. */
142
+ export interface ICargoStateAccepted {
143
+ readonly tree: string;
144
+ /** Сколько записей переведено. */
145
+ readonly changed: number;
146
+ /** Сколько уже стояло в названном состоянии: переходом это не считается. */
147
+ readonly same: number;
148
+ readonly denied: readonly ICargoStateDenied[];
149
+ }
108
150
  //# sourceMappingURL=cargo.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cargo.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/cargo.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cargo.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/cargo.ts"],"names":[],"mappings":"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"}
package/lib/cargo.js CHANGED
@@ -13,4 +13,6 @@
13
13
  export const CARGO_SCHEMA_VERSION = '1';
14
14
  /** Заголовок, которым дерево представляется приёму. */
15
15
  export const TREE_TOKEN_HEADER = 'x-tree-token';
16
+ /** Состояния записи груза по порядку: шаг идёт вперёд на соседнее, назад — только из «в работе». */
17
+ export const CARGO_STATES = ['new', 'in_work', 'fixed', 'released'];
16
18
  //# sourceMappingURL=cargo.js.map
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"]}
@@ -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;AAW7C,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,2FAA2F;AAC3F,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CACvC;AA0DD,+DAA+D;AAC/D,eAAO,MAAM,WAAW,EAAE,MAAyB,CAAC;AA+UpD;;;;GAIG;AACH,wBAAgB,IAAI,CAChB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,SAAS,MAAM,EAAO,EAC5B,QAAQ,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,EAC/C,SAAS,GAAE,MAAW,GACvB,iBAAiB,CAgCnB;AA8FD,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,iBAAiB,CA4DzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,iBAAiB,CA+CpF;AAQD,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8FAA8F;IAC9F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CAC1B;AAqDD;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,GAAG,iBAAiB,CAoDlF;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CA0E3D;AAED;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,GAAG,iBAAiB,CA2DzD"}
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;AA0DD,+DAA+D;AAC/D,eAAO,MAAM,WAAW,EAAE,MAAyB,CAAC;AA+UpD;;;;GAIG;AACH,wBAAgB,IAAI,CAChB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,SAAS,MAAM,EAAO,EAC5B,QAAQ,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,EAC/C,SAAS,GAAE,MAAW,GACvB,iBAAiB,CAgCnB;AA8FD,wBAAgB,IAAI,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,iBAAiB,CA4DzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,iBAAiB,CA+CpF;AAQD,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8FAA8F;IAC9F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CAC1B;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';
@@ -585,6 +586,52 @@ const countLines = (counted) => {
585
586
  const width = counted.reduce((found, entry) => Math.max(found, entry.name.length), 0);
586
587
  return counted.map((entry) => ` ${entry.name.padEnd(width)} ${entry.count}`);
587
588
  };
589
+ /**
590
+ * Вес разложенных правил на диске: имя правила в сумму байт его текстов.
591
+ *
592
+ * Считается здесь, а не в сводке: сводка разбирает наблюдения и диска не знает. Складываются все
593
+ * `.md` папки правила — само правило, компаньон с именами дерева и всё, что дерево положило
594
+ * рядом: загрузка правила тянет их не поодиночке, и раздельный счёт врал бы в меньшую сторону.
595
+ *
596
+ * Правило, которого в дереве нет, в карту не попадает вовсе. Ноль от него неотличим по числу, но
597
+ * отличим по смыслу: не «ничего не весит», а «весить нечему».
598
+ */
599
+ function weightsOfSkills(root, dir, names) {
600
+ const at = join(root, dir);
601
+ const found = {};
602
+ for (const name of names) {
603
+ const folder = join(at, name);
604
+ if (!existsSync(folder)) {
605
+ continue;
606
+ }
607
+ found[name] = readdirSync(folder)
608
+ .filter((file) => file.endsWith('.md'))
609
+ .reduce((sum, file) => sum + statSync(join(folder, file)).size, 0);
610
+ }
611
+ return found;
612
+ }
613
+ /** Байты человеку: килобайтами, потому что счёт идёт на сотни тысяч и читается по одному. */
614
+ function kbOf(bytes) {
615
+ return `${Math.round(bytes / 1024)} КБ`;
616
+ }
617
+ /**
618
+ * Строка о том, сколько отбитий пришло не на правку файла.
619
+ *
620
+ * Своей строкой, а не долей, выведенной читателем из разбивки по родам: гейт стоит ради правки, и
621
+ * доля, пришедшая на команду оболочки и на браузер, говорит, сколько раз правило потребовали под
622
+ * то, что правкой не является. Сложенная глазами из десятка строк, эта доля не складывается
623
+ * никогда.
624
+ *
625
+ * Молчит, когда отбитий не было вовсе: «0 процентов» при нуле отбитий — ответ на незаданный
626
+ * вопрос.
627
+ */
628
+ function offFileLines(summary) {
629
+ const total = summary.denials.reduce((found, entry) => found + entry.count, 0);
630
+ if (total === 0) {
631
+ return [];
632
+ }
633
+ return [` не на правке файла: ${summary.denialsOffFile} из ${total} — ${Math.round((summary.denialsOffFile / total) * 100)}%`];
634
+ }
588
635
  /** Сводка строками: загруженное, незагруженное, отбивки гейта и отказы гардов. */
589
636
  function statsLines(summary, swept, days, version, laidOut) {
590
637
  return [
@@ -592,6 +639,14 @@ function statsLines(summary, swept, days, version, laidOut) {
592
639
  ...(summary.versions.length ? [`версии пакета в записях: ${summary.versions.join(', ')}`] : []),
593
640
  '',
594
641
  `правил загружено: ${summary.loads.reduce((found, entry) => found + entry.count, 0)}`,
642
+ // Вес молчит, когда весить нечем: каталог правил не разложен или пуст. Ноль килобайт при
643
+ // сотне загрузок читался бы как «правила ничего не стоят», а это не то же самое.
644
+ ...(summary.bytes > 0
645
+ ? [
646
+ ` весом ${kbOf(summary.bytes)}, на один заход ${kbOf(summary.bytesPerSession)}`,
647
+ ' — столько текста заход прочитал прежде, чем взяться за работу',
648
+ ]
649
+ : []),
595
650
  ...countLines(summary.loads),
596
651
  ...(summary.unused.length
597
652
  ? [
@@ -610,9 +665,10 @@ function statsLines(summary, swept, days, version, laidOut) {
610
665
  ? [
611
666
  '',
612
667
  `гейт отбивал: ${summary.denials.reduce((found, entry) => found + entry.count, 0)}`,
668
+ ...offFileLines(summary),
613
669
  ...countLines(summary.denials),
614
670
  ...(summary.kinds.length
615
- ? [' чаще всего на:', ...countLines(summary.kinds).map((line) => ` ${line}`)]
671
+ ? [' по роду правки:', ...countLines(summary.kinds).map((line) => ` ${line}`)]
616
672
  : []),
617
673
  ]
618
674
  : []),
@@ -654,7 +710,10 @@ export function stats(env, options) {
654
710
  }
655
711
  const days = options.days > 0 ? options.days : DEFAULT_DAYS;
656
712
  const result = readObservations(root, options.today, days);
657
- const summary = summarize(result.observations, laidOutSkills(config, assetsDir), days);
713
+ const known = laidOutSkills(config, assetsDir);
714
+ // Вес спрашивается у дерева, а не у пакета: правило дерево могло переписать надстройкой, и
715
+ // заход читал то, что лежит здесь, а не то, что уехало бы из пакета.
716
+ const summary = summarize(result.observations, known, days, weightsOfSkills(root, config.layout.rules, known));
658
717
  if (options.json) {
659
718
  return { code: 0, lines: [JSON.stringify({ ...summary, swept: result.swept })] };
660
719
  }
@@ -728,6 +787,7 @@ export function doctor(env) {
728
787
  return ` снят каскадом: ${one.id} — вслед за ${one.parent}${root}`;
729
788
  }),
730
789
  ...profileLines(root, assetsDir, config),
790
+ ...thresholdLines(root),
731
791
  `значений в конфиге: ${Object.keys(config.vars).length}`,
732
792
  ...chosen,
733
793
  ...[...counted].map(([outcome, count]) => `${STATE_WORD[outcome]}: ${count}`),