@rt-tools/agent-kit 0.9.1 → 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 (119) hide show
  1. package/README.md +5 -0
  2. package/assets/agents/conscience.md +58 -0
  3. package/assets/agents/prose-editor.md +44 -0
  4. package/assets/agents/strict-teacher.md +59 -0
  5. package/assets/checks/board.github.mjs +56 -1
  6. package/assets/checks/check-board.github.mjs +24 -0
  7. package/assets/checks/check-doc-paths.mjs +4 -15
  8. package/assets/checks/check-dupes.mjs +5 -6
  9. package/assets/checks/check-file-size.mjs +6 -20
  10. package/assets/checks/check-prose-style.mjs +137 -0
  11. package/assets/checks/check-reuse.mjs +5 -5
  12. package/assets/checks/check-state-next.mjs +194 -0
  13. package/assets/checks/check-states.mjs +142 -0
  14. package/assets/checks/check-styles.mjs +5 -5
  15. package/assets/checks/check-turn-map.mjs +146 -0
  16. package/assets/checks/lib-common.mjs +3 -3
  17. package/assets/checks/rt-kit-checks.config.mjs +85 -1
  18. package/assets/commands/agent-kit-digest.md +6 -5
  19. package/assets/defaults/project.sh +108 -6
  20. package/assets/defaults/turn-map.md +46 -0
  21. package/assets/docs/GLOSSARY.md +17 -15
  22. package/assets/hooks/browser-device-id.sh +2 -0
  23. package/assets/hooks/browser-guard-device-id.sh +11 -1
  24. package/assets/hooks/browser-guard-no-asking.sh +11 -1
  25. package/assets/hooks/browser-guard-no-listing.sh +11 -1
  26. package/assets/hooks/browser-guard-no-other-drivers.sh +9 -1
  27. package/assets/hooks/browser-guard-require-select.sh +13 -2
  28. package/assets/hooks/claim-guard.sh +115 -0
  29. package/assets/hooks/commit-msg.sh +2 -0
  30. package/assets/hooks/conscience-guard.sh +100 -0
  31. package/assets/hooks/constitution-index.sh +2 -0
  32. package/assets/hooks/deny-tail.sh +32 -0
  33. package/assets/hooks/dev-server-guard.sh +10 -2
  34. package/assets/hooks/docs-guard.sh +16 -2
  35. package/assets/hooks/exam-guard.sh +123 -0
  36. package/assets/hooks/git-guard-delivery-signature.sh +77 -0
  37. package/assets/hooks/git-guard-delivery.sh +156 -68
  38. package/assets/hooks/git-guard-main.sh +14 -0
  39. package/assets/hooks/git-guard-push-tests.sh +51 -2
  40. package/assets/hooks/glossary-load.sh +2 -0
  41. package/assets/hooks/grill-gate.sh +14 -0
  42. package/assets/hooks/handoff-entry-guard.sh +73 -0
  43. package/assets/hooks/handoff-write.sh +103 -0
  44. package/assets/hooks/lint-after-edit.sh +2 -0
  45. package/assets/hooks/observe.sh +2 -0
  46. package/assets/hooks/postmortem-guard.sh +14 -0
  47. package/assets/hooks/proposal-guard.sh +14 -0
  48. package/assets/hooks/prose-style-guard.sh +75 -0
  49. package/assets/hooks/qa-dataid-guard.sh +14 -1
  50. package/assets/hooks/rerun-guard.sh +88 -0
  51. package/assets/hooks/reuse-first-guard.sh +14 -1
  52. package/assets/hooks/roles.sh +34 -0
  53. package/assets/hooks/skill-gate-layers.sh +2 -0
  54. package/assets/hooks/skill-gate-rearm.sh +2 -0
  55. package/assets/hooks/skill-gate.sh +14 -0
  56. package/assets/hooks/skill-loaded.sh +2 -0
  57. package/assets/hooks/sql-guard-parse.sh +2 -0
  58. package/assets/hooks/sql-guard-request.sh +2 -0
  59. package/assets/hooks/sql-guard-target.sh +2 -0
  60. package/assets/hooks/sql-guard-write.sh +4 -1
  61. package/assets/hooks/sql-guard.sh +14 -1
  62. package/assets/hooks/task-context-load.sh +2 -0
  63. package/assets/hooks/task-flow-guard.sh +73 -7
  64. package/assets/hooks/turn-entry-load.sh +62 -0
  65. package/assets/hooks/turn-exit-guard.sh +191 -0
  66. package/assets/hooks/utf8.sh +35 -0
  67. package/assets/hooks/waiting-turn-guard.sh +14 -0
  68. package/assets/hooks/window-fill-guard.sh +43 -2
  69. package/assets/laws/work-conduct.md +29 -0
  70. package/assets/patterns/cargo-triage-mark.md +119 -0
  71. package/assets/patterns/task-flow-close.md +29 -8
  72. package/assets/patterns/task-flow-handoff.md +19 -1
  73. package/assets/patterns/task-flow-resume.md +36 -4
  74. package/assets/patterns/task-flow-start.md +56 -6
  75. package/assets/patterns/turn-entry-map.md +81 -0
  76. package/assets/rules/cargo-triage.md +126 -0
  77. package/assets/rules/git-workflow.azure.md +36 -9
  78. package/assets/rules/git-workflow.github.md +74 -9
  79. package/assets/rules/git-workflow.gitlab.md +40 -12
  80. package/assets/rules/task-flow.md +136 -71
  81. package/assets/rules/testing.md +15 -1
  82. package/assets/rules/turn-entry.md +93 -0
  83. package/assets/samples/tasks/_template/plan.md +4 -1
  84. package/assets/samples/tasks/_template/progress.md +1 -0
  85. package/assets/skills/agent-kit.md +33 -0
  86. package/assets/templates/project.sh +16 -0
  87. package/bin/agent-kit.d.ts.map +1 -1
  88. package/bin/agent-kit.js +42 -1
  89. package/bin/agent-kit.js.map +1 -1
  90. package/lib/cargo-state.d.ts +62 -0
  91. package/lib/cargo-state.d.ts.map +1 -0
  92. package/lib/cargo-state.js +118 -0
  93. package/lib/cargo-state.js.map +1 -0
  94. package/lib/cargo.d.ts +42 -0
  95. package/lib/cargo.d.ts.map +1 -1
  96. package/lib/cargo.js +2 -0
  97. package/lib/cargo.js.map +1 -1
  98. package/lib/commands.d.ts.map +1 -1
  99. package/lib/commands.js +64 -4
  100. package/lib/commands.js.map +1 -1
  101. package/lib/config.d.ts +8 -0
  102. package/lib/config.d.ts.map +1 -1
  103. package/lib/config.js +1 -0
  104. package/lib/config.js.map +1 -1
  105. package/lib/observations.d.ts +35 -1
  106. package/lib/observations.d.ts.map +1 -1
  107. package/lib/observations.js +14 -2
  108. package/lib/observations.js.map +1 -1
  109. package/lib/ship.d.ts +3 -0
  110. package/lib/ship.d.ts.map +1 -1
  111. package/lib/ship.js +56 -0
  112. package/lib/ship.js.map +1 -1
  113. package/lib/thresholds.d.ts +49 -0
  114. package/lib/thresholds.d.ts.map +1 -0
  115. package/lib/thresholds.js +151 -0
  116. package/lib/thresholds.js.map +1 -0
  117. package/package.json +1 -1
  118. package/rt-tools-agent-kit-0.11.0.tgz +0 -0
  119. package/rt-tools-agent-kit-0.9.1.tgz +0 -0
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Пороги окна захода: где заход останавливается и где инструмент сжимает контекст сам.
3
+ *
4
+ * Чисел два, и живут они в разных местах чужой настройки. Размер окна и доля, на которой страж
5
+ * останавливает заход, приходят переменными окружения — их читает профиль дерева. Порог, на
6
+ * котором инструмент сжимает контекст, задаётся своей парой: размером окна автосжатия и долей в
7
+ * процентах. Пакет ни тем ни другим не владеет — окно принадлежит дереву.
8
+ *
9
+ * Разъехавшись, пары дают заход, который либо сжимается раньше, чем передача написана, либо
10
+ * доживает до предела окна и роняет работу. Заметить это нечем: обе стороны по отдельности
11
+ * выглядят настроенными, а расхождение видно только тому, кто положит их рядом. Здесь их и
12
+ * кладут рядом — разбор состояния раскладки печатает числа и называет разошедшееся.
13
+ *
14
+ * Сведёнными считаются РАЗВЕДЁННЫЕ доли, а не совпавшие. Порог сжатия обязан стоять ниже порога
15
+ * остановки: первым обязано срабатывать то, что заход продолжает, а не то, что его
16
+ * останавливает. Совпавшая пара — гонка, и выигрывает её страж: он стоит на вызове инструмента,
17
+ * а сжатие приходит между ходами. Ровно так заход и вставал на пороге вместо того, чтобы
18
+ * продолжиться сжатым, — при том что прежняя сверка обе стороны считала настроенными и молчала.
19
+ *
20
+ * Насколько ниже — своё число дерева, `RT_WINDOW_MARGIN_PCT`. Запас объявляется, а не выводится
21
+ * разницей: сжатие идёт не мгновенно, и разница в один процент требование «ниже» удовлетворяет,
22
+ * а работу не спасает.
23
+ *
24
+ * Размер окна при этом сверяется по-прежнему на равенство: это одно и то же окно, названное в
25
+ * двух местах, и разные числа там означают, что одна из сторон считает долю не от того.
26
+ *
27
+ * Дерево, не объявившее размера окна, стража не получает вовсе, и судить у него нечего: раздел
28
+ * молчит целиком, а не краснеет отсутствием.
29
+ */
30
+ import { existsSync, readFileSync } from 'node:fs';
31
+ import { join } from 'node:path';
32
+ import { SETTINGS_PATH } from './hooks-map.js';
33
+ /** Доля окна, на которой страж напоминает выбрать точку остановки. Умолчание профиля. */
34
+ const WARN_PCT_DEFAULT = 40;
35
+ /** Доля окна, на которой страж отбивает всё, кроме закрытия захода. Умолчание профиля. */
36
+ const STOP_PCT_DEFAULT = 50;
37
+ /** На сколько долей окна порог сжатия обязан стоять ниже порога остановки. Умолчание профиля. */
38
+ const MARGIN_PCT_DEFAULT = 5;
39
+ /**
40
+ * Число из строки настройки. Пусто, не число, ноль и меньше — ничего: значение, которого нельзя
41
+ * посчитать, объявленным не считается, иначе расхождением станет опечатка, а не расхождение.
42
+ */
43
+ function numberOf(value) {
44
+ const parsed = typeof value === 'number' ? value : Number.parseInt(String(value ?? ''), 10);
45
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
46
+ }
47
+ /**
48
+ * Что дерево объявило о порогах.
49
+ *
50
+ * Настройку читаем разбором и только им: пороги лежат полями объекта, и вынуть их из текста
51
+ * поиском значило бы гадать. Не разобралась — молчим целиком, как молчит карта хуков: назвать
52
+ * расхождением непрочитанное хуже, чем не назвать ничего.
53
+ */
54
+ export function readThresholds(root) {
55
+ const path = join(root, SETTINGS_PATH);
56
+ if (!existsSync(path)) {
57
+ return null;
58
+ }
59
+ let settings;
60
+ try {
61
+ settings = JSON.parse(readFileSync(path, 'utf8'));
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ const env = settings['env'] ?? {};
67
+ const windowTokens = numberOf(env['RT_WINDOW_TOKENS']);
68
+ if (windowTokens === null) {
69
+ return null;
70
+ }
71
+ return {
72
+ window: windowTokens,
73
+ warnPct: numberOf(env['RT_WINDOW_WARN_PCT']) ?? WARN_PCT_DEFAULT,
74
+ stopPct: numberOf(env['RT_WINDOW_STOP_PCT']) ?? STOP_PCT_DEFAULT,
75
+ marginPct: numberOf(env['RT_WINDOW_MARGIN_PCT']) ?? MARGIN_PCT_DEFAULT,
76
+ compactWindow: numberOf(settings['autoCompactWindow']),
77
+ compactPct: numberOf(env['CLAUDE_AUTOCOMPACT_PCT_OVERRIDE']),
78
+ };
79
+ }
80
+ /**
81
+ * Чем пара стража разошлась с парой сжатия.
82
+ *
83
+ * Незаданное расхождением не считается: дерево вправе не задавать порога сжатия вовсе — тогда
84
+ * его назначает инструмент, и сказать об этом надо один раз, а не двумя строками о разнице.
85
+ *
86
+ * Долю судит расстояние, а не равенство. Три случая дают расхождение, и у каждого свой исход
87
+ * для захода: сжатие выше остановки — страж отбивает раньше, чем клиент успевает начать;
88
+ * сжатие вровень с остановкой — гонка, которую страж выигрывает; сжатие ниже, но ближе
89
+ * объявленного запаса — клиент начать успевает, а докончить нет.
90
+ */
91
+ export function thresholdDrift(thresholds) {
92
+ const drift = [];
93
+ if (thresholds.compactWindow !== null && thresholds.compactWindow !== thresholds.window) {
94
+ drift.push({
95
+ what: 'размер окна',
96
+ guard: String(thresholds.window),
97
+ compact: String(thresholds.compactWindow),
98
+ why: 'доля считается не от того же окна',
99
+ });
100
+ }
101
+ const compactPct = thresholds.compactPct;
102
+ if (compactPct === null) {
103
+ return drift;
104
+ }
105
+ const gap = thresholds.stopPct - compactPct;
106
+ const tooHigh = 'сжатие стоит выше остановки: страж отбивает заход раньше, чем приходит сжатие';
107
+ const tied = 'пороги совпали: кто сработает первым, не назначено, и выигрывает страж';
108
+ if (gap <= 0) {
109
+ drift.push({
110
+ what: 'доля окна',
111
+ guard: `${thresholds.stopPct}%`,
112
+ compact: `${compactPct}%`,
113
+ why: gap < 0 ? tooHigh : tied,
114
+ });
115
+ return drift;
116
+ }
117
+ if (gap < thresholds.marginPct) {
118
+ drift.push({
119
+ what: 'запас между порогами',
120
+ guard: `${thresholds.marginPct}% объявлено`,
121
+ compact: `${gap}% на деле`,
122
+ why: 'сжатие идёт не мгновенно и может не успеть до порога остановки',
123
+ });
124
+ }
125
+ return drift;
126
+ }
127
+ /** Раздел разбора состояния: числа порогов и то, что о них известно. */
128
+ export function thresholdLines(root) {
129
+ const thresholds = readThresholds(root);
130
+ if (!thresholds) {
131
+ return [];
132
+ }
133
+ const lines = [
134
+ `окно захода: ${thresholds.window} токенов, напоминание ${thresholds.warnPct}%, остановка ${thresholds.stopPct}%`,
135
+ ];
136
+ if (thresholds.compactWindow === null && thresholds.compactPct === null) {
137
+ lines.push(' порог сжатия деревом не задан — его назначает инструмент, и передача пишется когда придётся', ` объяви в \`${SETTINGS_PATH}\`: \`autoCompactWindow\` числом ${thresholds.window}, а \`CLAUDE_AUTOCOMPACT_PCT_OVERRIDE\` — числом ${thresholds.stopPct - thresholds.marginPct}`);
138
+ return lines;
139
+ }
140
+ const drift = thresholdDrift(thresholds);
141
+ if (!drift.length) {
142
+ lines.push(` порог сжатия ниже порога остановки на ${thresholds.marginPct}% и более: сжатие приходит первым, страж остаётся страховкой`);
143
+ return lines;
144
+ }
145
+ lines.push(' порог сжатия разошёлся с порогом остановки:');
146
+ for (const one of drift) {
147
+ lines.push(` ${one.what}: у стража ${one.guard}, у сжатия ${one.compact}`, ` ${one.why}`);
148
+ }
149
+ return lines;
150
+ }
151
+ //# sourceMappingURL=thresholds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thresholds.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/thresholds.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Пороги окна захода: где заход останавливается и где инструмент сжимает контекст сам.\n *\n * Чисел два, и живут они в разных местах чужой настройки. Размер окна и доля, на которой страж\n * останавливает заход, приходят переменными окружения — их читает профиль дерева. Порог, на\n * котором инструмент сжимает контекст, задаётся своей парой: размером окна автосжатия и долей в\n * процентах. Пакет ни тем ни другим не владеет — окно принадлежит дереву.\n *\n * Разъехавшись, пары дают заход, который либо сжимается раньше, чем передача написана, либо\n * доживает до предела окна и роняет работу. Заметить это нечем: обе стороны по отдельности\n * выглядят настроенными, а расхождение видно только тому, кто положит их рядом. Здесь их и\n * кладут рядом — разбор состояния раскладки печатает числа и называет разошедшееся.\n *\n * Сведёнными считаются РАЗВЕДЁННЫЕ доли, а не совпавшие. Порог сжатия обязан стоять ниже порога\n * остановки: первым обязано срабатывать то, что заход продолжает, а не то, что его\n * останавливает. Совпавшая пара — гонка, и выигрывает её страж: он стоит на вызове инструмента,\n * а сжатие приходит между ходами. Ровно так заход и вставал на пороге вместо того, чтобы\n * продолжиться сжатым, — при том что прежняя сверка обе стороны считала настроенными и молчала.\n *\n * Насколько ниже — своё число дерева, `RT_WINDOW_MARGIN_PCT`. Запас объявляется, а не выводится\n * разницей: сжатие идёт не мгновенно, и разница в один процент требование «ниже» удовлетворяет,\n * а работу не спасает.\n *\n * Размер окна при этом сверяется по-прежнему на равенство: это одно и то же окно, названное в\n * двух местах, и разные числа там означают, что одна из сторон считает долю не от того.\n *\n * Дерево, не объявившее размера окна, стража не получает вовсе, и судить у него нечего: раздел\n * молчит целиком, а не краснеет отсутствием.\n */\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { SETTINGS_PATH } from './hooks-map.js';\n\n/** Доля окна, на которой страж напоминает выбрать точку остановки. Умолчание профиля. */\nconst WARN_PCT_DEFAULT: number = 40;\n\n/** Доля окна, на которой страж отбивает всё, кроме закрытия захода. Умолчание профиля. */\nconst STOP_PCT_DEFAULT: number = 50;\n\n/** На сколько долей окна порог сжатия обязан стоять ниже порога остановки. Умолчание профиля. */\nconst MARGIN_PCT_DEFAULT: number = 5;\n\n/** Пороги, объявленные деревом. Размер окна не объявлен — стража нет, и судить нечего. */\nexport interface IThresholds {\n /** Размер окна захода в токенах: `RT_WINDOW_TOKENS`. */\n readonly window: number;\n /** Доля окна, на которой страж напоминает: `RT_WINDOW_WARN_PCT`. */\n readonly warnPct: number;\n /** Доля окна, на которой страж отбивает работу: `RT_WINDOW_STOP_PCT`. */\n readonly stopPct: number;\n /** Запас между порогом сжатия и порогом остановки: `RT_WINDOW_MARGIN_PCT`. */\n readonly marginPct: number;\n /** Размер окна автосжатия: настройка `autoCompactWindow`. Не объявлен — `null`. */\n readonly compactWindow: number | null;\n /** Доля, на которой приходит сжатие: `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE`. Не объявлена — `null`. */\n readonly compactPct: number | null;\n}\n\n/** Расхождение пары: что с чем разъехалось, какими числами и чем это кончится для захода. */\nexport interface IThresholdDrift {\n /** Что разошлось, словами читателя. */\n readonly what: string;\n /** Что объявлено стражу. */\n readonly guard: string;\n /** Что объявлено сжатию. */\n readonly compact: string;\n /** Чем это кончится для захода. Пусто у расхождения, которое говорит само за себя. */\n readonly why: string;\n}\n\n/**\n * Число из строки настройки. Пусто, не число, ноль и меньше — ничего: значение, которого нельзя\n * посчитать, объявленным не считается, иначе расхождением станет опечатка, а не расхождение.\n */\nfunction numberOf(value: unknown): number | null {\n const parsed: number = typeof value === 'number' ? value : Number.parseInt(String(value ?? ''), 10);\n\n return Number.isFinite(parsed) && parsed > 0 ? parsed : null;\n}\n\n/**\n * Что дерево объявило о порогах.\n *\n * Настройку читаем разбором и только им: пороги лежат полями объекта, и вынуть их из текста\n * поиском значило бы гадать. Не разобралась — молчим целиком, как молчит карта хуков: назвать\n * расхождением непрочитанное хуже, чем не назвать ничего.\n */\nexport function readThresholds(root: string): IThresholds | null {\n const path: string = join(root, SETTINGS_PATH);\n if (!existsSync(path)) {\n return null;\n }\n\n let settings: Record<string, unknown>;\n try {\n settings = JSON.parse(readFileSync(path, 'utf8')) as Record<string, unknown>;\n } catch {\n return null;\n }\n\n const env: Record<string, unknown> = (settings['env'] as Record<string, unknown>) ?? {};\n const windowTokens: number | null = numberOf(env['RT_WINDOW_TOKENS']);\n if (windowTokens === null) {\n return null;\n }\n\n return {\n window: windowTokens,\n warnPct: numberOf(env['RT_WINDOW_WARN_PCT']) ?? WARN_PCT_DEFAULT,\n stopPct: numberOf(env['RT_WINDOW_STOP_PCT']) ?? STOP_PCT_DEFAULT,\n marginPct: numberOf(env['RT_WINDOW_MARGIN_PCT']) ?? MARGIN_PCT_DEFAULT,\n compactWindow: numberOf(settings['autoCompactWindow']),\n compactPct: numberOf(env['CLAUDE_AUTOCOMPACT_PCT_OVERRIDE']),\n };\n}\n\n/**\n * Чем пара стража разошлась с парой сжатия.\n *\n * Незаданное расхождением не считается: дерево вправе не задавать порога сжатия вовсе — тогда\n * его назначает инструмент, и сказать об этом надо один раз, а не двумя строками о разнице.\n *\n * Долю судит расстояние, а не равенство. Три случая дают расхождение, и у каждого свой исход\n * для захода: сжатие выше остановки — страж отбивает раньше, чем клиент успевает начать;\n * сжатие вровень с остановкой — гонка, которую страж выигрывает; сжатие ниже, но ближе\n * объявленного запаса — клиент начать успевает, а докончить нет.\n */\nexport function thresholdDrift(thresholds: IThresholds): readonly IThresholdDrift[] {\n const drift: IThresholdDrift[] = [];\n\n if (thresholds.compactWindow !== null && thresholds.compactWindow !== thresholds.window) {\n drift.push({\n what: 'размер окна',\n guard: String(thresholds.window),\n compact: String(thresholds.compactWindow),\n why: 'доля считается не от того же окна',\n });\n }\n\n const compactPct: number | null = thresholds.compactPct;\n if (compactPct === null) {\n return drift;\n }\n\n const gap: number = thresholds.stopPct - compactPct;\n const tooHigh: string = 'сжатие стоит выше остановки: страж отбивает заход раньше, чем приходит сжатие';\n const tied: string = 'пороги совпали: кто сработает первым, не назначено, и выигрывает страж';\n\n if (gap <= 0) {\n drift.push({\n what: 'доля окна',\n guard: `${thresholds.stopPct}%`,\n compact: `${compactPct}%`,\n why: gap < 0 ? tooHigh : tied,\n });\n\n return drift;\n }\n\n if (gap < thresholds.marginPct) {\n drift.push({\n what: 'запас между порогами',\n guard: `${thresholds.marginPct}% объявлено`,\n compact: `${gap}% на деле`,\n why: 'сжатие идёт не мгновенно и может не успеть до порога остановки',\n });\n }\n\n return drift;\n}\n\n/** Раздел разбора состояния: числа порогов и то, что о них известно. */\nexport function thresholdLines(root: string): string[] {\n const thresholds: IThresholds | null = readThresholds(root);\n if (!thresholds) {\n return [];\n }\n\n const lines: string[] = [\n `окно захода: ${thresholds.window} токенов, напоминание ${thresholds.warnPct}%, остановка ${thresholds.stopPct}%`,\n ];\n\n if (thresholds.compactWindow === null && thresholds.compactPct === null) {\n lines.push(\n ' порог сжатия деревом не задан — его назначает инструмент, и передача пишется когда придётся',\n ` объяви в \\`${SETTINGS_PATH}\\`: \\`autoCompactWindow\\` числом ${thresholds.window}, а \\`CLAUDE_AUTOCOMPACT_PCT_OVERRIDE\\` — числом ${thresholds.stopPct - thresholds.marginPct}`\n );\n\n return lines;\n }\n\n const drift: readonly IThresholdDrift[] = thresholdDrift(thresholds);\n if (!drift.length) {\n lines.push(\n ` порог сжатия ниже порога остановки на ${thresholds.marginPct}% и более: сжатие приходит первым, страж остаётся страховкой`\n );\n\n return lines;\n }\n\n lines.push(' порог сжатия разошёлся с порогом остановки:');\n for (const one of drift) {\n lines.push(` ${one.what}: у стража ${one.guard}, у сжатия ${one.compact}`, ` ${one.why}`);\n }\n\n return lines;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rt-tools/agent-kit",
3
- "version": "0.9.1",
3
+ "version": "0.11.0",
4
4
  "description": "Переносимый слой правил для агента: законы, хуки, проверки и агенты, раскладываемые в репозиторий одной командой",
5
5
  "author": "RT Team",
6
6
  "license": "Apache-2.0",
Binary file
Binary file