@rt-tools/agent-kit 0.20.0 → 0.22.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 (78) hide show
  1. package/assets/checks/check-reuse.mjs +11 -1
  2. package/assets/checks/check-specs.mjs +8 -23
  3. package/assets/checks/signals/core.json +12 -2
  4. package/assets/checks/signals.mjs +17 -0
  5. package/assets/checks/spec-proposed.mjs +122 -0
  6. package/assets/hooks/browser-guard-device-id.sh +3 -0
  7. package/assets/hooks/browser-guard-no-asking.sh +3 -0
  8. package/assets/hooks/browser-guard-no-listing.sh +3 -0
  9. package/assets/hooks/browser-guard-no-other-drivers.sh +3 -0
  10. package/assets/hooks/browser-guard-require-select.sh +3 -0
  11. package/assets/hooks/claim-guard.sh +4 -0
  12. package/assets/hooks/conscience-guard.sh +4 -0
  13. package/assets/hooks/deny-tail.sh +12 -0
  14. package/assets/hooks/dev-server-guard.sh +3 -0
  15. package/assets/hooks/docs-guard.sh +4 -11
  16. package/assets/hooks/exam-guard.sh +3 -0
  17. package/assets/hooks/git-guard-delivery.sh +4 -4
  18. package/assets/hooks/git-guard-main.sh +4 -9
  19. package/assets/hooks/git-guard-push-tests.sh +3 -0
  20. package/assets/hooks/grill-gate.sh +5 -0
  21. package/assets/hooks/guard-note.sh +45 -0
  22. package/assets/hooks/handoff-entry-guard.sh +3 -0
  23. package/assets/hooks/handoff-write.sh +58 -7
  24. package/assets/hooks/hook-input.sh +1 -0
  25. package/assets/hooks/override-write-guard.sh +4 -4
  26. package/assets/hooks/postmortem-guard.sh +4 -0
  27. package/assets/hooks/proposal-guard.sh +11 -0
  28. package/assets/hooks/prose-style-guard.sh +3 -0
  29. package/assets/hooks/qa-dataid-guard.sh +3 -0
  30. package/assets/hooks/rerun-guard.sh +3 -0
  31. package/assets/hooks/reuse-first-guard.sh +3 -0
  32. package/assets/hooks/rule-article.sh +1 -1
  33. package/assets/hooks/rule-source-guard.sh +4 -4
  34. package/assets/hooks/sql-guard.sh +3 -0
  35. package/assets/hooks/task-context-load.sh +1 -1
  36. package/assets/hooks/task-flow-draft-guard.sh +3 -0
  37. package/assets/hooks/task-flow-guard.sh +3 -0
  38. package/assets/hooks/turn-entry-load.sh +27 -2
  39. package/assets/hooks/turn-exit-guard.sh +4 -0
  40. package/assets/hooks/waiting-turn-guard.sh +32 -10
  41. package/assets/hooks/window-fill-guard.sh +3 -0
  42. package/assets/hooks/work-start-guard.sh +5 -0
  43. package/assets/hooks/write-targets.sh +59 -10
  44. package/assets/laws/delivery.md +5 -0
  45. package/assets/laws/frontend-application.md +10 -0
  46. package/assets/laws/work-conduct.md +8 -0
  47. package/assets/patterns/browser-verification-stand.md +24 -9
  48. package/assets/patterns/git-workflow-pr.github.md +29 -22
  49. package/assets/patterns/spec-driven-domain.md +5 -0
  50. package/assets/patterns/spec-driven-rule.md +41 -1
  51. package/assets/patterns/task-flow-archive.md +12 -0
  52. package/assets/patterns/task-flow-handoff.md +14 -1
  53. package/assets/patterns/task-flow-resume.md +11 -0
  54. package/assets/patterns/task-flow-start.md +40 -40
  55. package/assets/patterns/testing-e2e.md +15 -0
  56. package/assets/patterns/testing-unit.md +13 -0
  57. package/assets/pitfalls/git-workflow.github.md +13 -0
  58. package/assets/pitfalls/spec-driven.md +20 -0
  59. package/assets/pitfalls/task-flow.md +78 -48
  60. package/assets/rules/git-workflow.azure.md +5 -0
  61. package/assets/rules/git-workflow.github.md +26 -23
  62. package/assets/rules/git-workflow.gitlab.md +5 -0
  63. package/assets/rules/spec-driven.md +23 -24
  64. package/assets/rules/turn-conduct.md +13 -15
  65. package/assets/rules/turn-entry.md +6 -4
  66. package/assets/rules/typescript-conventions.md +7 -0
  67. package/assets/skills/agent-kit-extend.md +11 -0
  68. package/assets/skills/agent-kit.md +12 -2
  69. package/lib/commands.d.ts.map +1 -1
  70. package/lib/commands.js +36 -1
  71. package/lib/commands.js.map +1 -1
  72. package/lib/observations.d.ts +13 -1
  73. package/lib/observations.d.ts.map +1 -1
  74. package/lib/observations.js +7 -1
  75. package/lib/observations.js.map +1 -1
  76. package/package.json +1 -1
  77. package/rt-tools-agent-kit-0.22.0.tgz +0 -0
  78. package/rt-tools-agent-kit-0.20.0.tgz +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"observations.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/observations.ts"],"names":[],"mappings":"AAgBA,gEAAgE;AAChE,eAAO,MAAM,gBAAgB,EAAE,MAAsC,CAAC;AAEtE,oGAAoG;AACpG,eAAO,MAAM,SAAS,EAAE,MAAW,CAAC;AAEpC,gEAAgE;AAChE,eAAO,MAAM,YAAY,EAAE,MAAU,CAAC;AAEtC,yCAAyC;AACzC,MAAM,MAAM,MAAM,GAAG,YAAY,GAAG,WAAW,GAAG,YAAY,CAAC;AAE/D,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC5B;AAED,qFAAqF;AACrF,MAAM,WAAW,MAAM;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAExD,MAAM,WAAW,QAAQ;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,iCAAiC;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CAC1B;AAOD,gGAAgG;AAChG,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI7D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAoBlE;AAED,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,YAAY,EAAE,SAAS,YAAY,EAAE,CAAC;IAC/C,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,qDAAqD;IACrD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC5B;AAsBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CA8BvF;AAuBD;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,YAAY,EAAE,SAAS,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,QAAa,GAAG,QAAQ,CAqBzI"}
1
+ {"version":3,"file":"observations.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/observations.ts"],"names":[],"mappings":"AAgBA,gEAAgE;AAChE,eAAO,MAAM,gBAAgB,EAAE,MAAsC,CAAC;AAEtE,oGAAoG;AACpG,eAAO,MAAM,SAAS,EAAE,MAAW,CAAC;AAEpC,gEAAgE;AAChE,eAAO,MAAM,YAAY,EAAE,MAAU,CAAC;AAEtC,yCAAyC;AACzC,MAAM,MAAM,MAAM,GAAG,YAAY,GAAG,WAAW,GAAG,YAAY,CAAC;AAE/D,MAAM,WAAW,YAAY;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC5B;AAED,qFAAqF;AACrF,MAAM,WAAW,MAAM;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAExD,MAAM,WAAW,QAAQ;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,iCAAiC;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CAC1B;AAOD,gGAAgG;AAChG,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI7D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAoBlE;AAED,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,YAAY,EAAE,SAAS,YAAY,EAAE,CAAC;IAC/C,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,qDAAqD;IACrD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC5B;AAsBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CA8BvF;AAuBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CACrB,YAAY,EAAE,SAAS,YAAY,EAAE,EACrC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,QAAa,EACtB,MAAM,GAAE,SAAS,MAAM,EAAO,GAC/B,QAAQ,CAuBV"}
@@ -122,17 +122,22 @@ const OFF_FILE_KINDS = ['command', 'browser'];
122
122
  * «чем пользовались», а самое ценное — чем не пользовались ни разу — сказать нечем: в самих
123
123
  * наблюдениях незагруженного правила нет по определению.
124
124
  *
125
+ * `guards` — имена гардов, разложенных в это дерево. Гардом здесь считается тот, кто заявил своё
126
+ * имя для наблюдений: хук без заявки в наблюдениях не появится ни при каком отрезке, и молчащим
127
+ * он назывался бы неверно — он молчит не потому, что не понадобился.
128
+ *
125
129
  * `weights` — вес этих правил на диске. Без них сводка говорит, сколько раз правило открывали, и
126
130
  * молчит о том, во что это обошлось: тридцать загрузок паттерна и тридцать загрузок правила на
127
131
  * пятьсот строк стоят разного, а в счёте выглядят одинаково. Не назван — вес считается нулём, и
128
132
  * строки о нём в выводе не будет вовсе.
129
133
  */
130
- export function summarize(observations, known, days, weights = {}) {
134
+ export function summarize(observations, known, days, weights = {}, guards = []) {
131
135
  const loads = of(observations, 'skill-load');
132
136
  const denials = of(observations, 'gate-deny');
133
137
  const loaded = new Set(loads.map((entry) => entry.resource));
134
138
  const sessions = new Set(observations.map((entry) => entry.session).filter(Boolean)).size;
135
139
  const bytes = loads.reduce((found, entry) => found + (weights[entry.resource] ?? 0), 0);
140
+ const denied = new Set(of(observations, 'guard-deny').map((entry) => entry.resource));
136
141
  return {
137
142
  days,
138
143
  sessions,
@@ -143,6 +148,7 @@ export function summarize(observations, known, days, weights = {}) {
143
148
  kinds: countBy(denials.map((entry) => entry.kind)),
144
149
  denialsOffFile: denials.filter((entry) => OFF_FILE_KINDS.includes(entry.kind)).length,
145
150
  guards: countBy(of(observations, 'guard-deny').map((entry) => entry.resource)),
151
+ silentGuards: guards.filter((name) => !denied.has(name)).sort(byText),
146
152
  unused: known.filter((name) => !loaded.has(name)).sort(byText),
147
153
  versions: [...new Set(observations.map((entry) => entry.version).filter(Boolean))].sort(byText),
148
154
  total: observations.length,
@@ -1 +1 @@
1
- {"version":3,"file":"observations.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/observations.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Наблюдения и сводка по ним.\n *\n * Наблюдения пишет `hooks/observe.sh` — по строке на событие, по файлу на день. Здесь они\n * читаются и сводятся: чем пользовались, чем не пользовались ни разу, обо что спотыкались.\n *\n * Разбор отделён от чтения диска намеренно: сводка — то, ради чего наблюдения ведутся, и\n * проверять её на подложенных файлах значило бы проверять файловую систему.\n *\n * Дата приходит доводом, а не берётся из часов: сводка за отрезок дней иначе не проверяется\n * спекой — вчерашний файл фикстуры завтра оказывается позавчерашним.\n */\nimport { existsSync, readFileSync, readdirSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { byText } from './order.js';\n\n/** Куда `observe.sh` пишет наблюдения. Путь от корня дерева. */\nexport const OBSERVATIONS_DIR: string = '.claude/rt-kit/observations';\n\n/** Сколько дней наблюдения лежат. Каталог, который только растёт, через год перестают открывать. */\nexport const KEEP_DAYS: number = 30;\n\n/** За сколько дней сводка отвечает, когда отрезок не назван. */\nexport const DEFAULT_DAYS: number = 3;\n\n/** Роды событий, которые пишут гарды. */\nexport type TEvent = 'skill-load' | 'gate-deny' | 'guard-deny';\n\nexport interface IObservation {\n readonly event: string;\n /** Имя правила или гарда — имя ресурса пакета, и только оно. */\n readonly resource: string;\n /** Род правки: расширение, `command` или `browser`. Пути в наблюдении нет. */\n readonly kind: string;\n /** Признак сессии: считает заходы, имени сессии не выдаёт. */\n readonly session: string;\n readonly version: string;\n}\n\n/** Сколько раз что-то встретилось. Порядок — по убыванию, при равенстве по имени. */\nexport interface ICount {\n readonly name: string;\n readonly count: number;\n}\n\n/**\n * Вес ресурса на диске, байтами: имя правила в число.\n *\n * Приходит доводом, а не читается здесь, по той же причине, что и день: разбор отделён от диска,\n * и сводку, читающую каталог правил сама, пришлось бы проверять подложенными файлами.\n */\nexport type TWeights = Readonly<Record<string, number>>;\n\nexport interface ISummary {\n readonly days: number;\n /** Сколько заходов оставило хоть одно наблюдение. */\n readonly sessions: number;\n readonly loads: readonly ICount[];\n /**\n * Вес того, что заходы прочитали за отрезок, байтами. Каждая загрузка считается своим весом:\n * правило, открытое в трёх заходах, стоило места трижды — тем, что оно одно, это не дешевле.\n * Ресурс, веса которого не назвали, считается нулём: врать числом хуже, чем не знать его.\n */\n readonly bytes: number;\n /**\n * Тот же вес, приходящийся на один заход, — цена входа в работу.\n *\n * Ноль заходов даёт ноль, а не отказ: сводка за пустой отрезок печатается наравне с полной,\n * и деление на ноль уронило бы её на том единственном случае, ради которого её и зовут, —\n * «а было ли вообще что-нибудь».\n */\n readonly bytesPerSession: number;\n /** Отбития гейта по правилам. */\n readonly denials: readonly ICount[];\n /** Род правки, на котором гейт отбивал. */\n readonly kinds: readonly ICount[];\n /**\n * Отбития, пришедшие не на правку файла: род правки — команда оболочки или браузер.\n *\n * Считается здесь, а не выводится читателем из разбивки по родам: доля, которую надо\n * сложить глазами из десятка строк, не читается вовсе — а именно она говорит, требует ли\n * гейт правило под то, что правкой не является.\n */\n readonly denialsOffFile: number;\n readonly guards: readonly ICount[];\n /**\n * Правила, разложенные в дерево и не загруженные за отрезок ни разу. Мёртвый ресурс иначе\n * неотличим от работающего — и это единственная строка сводки, которую нельзя получить из\n * самих наблюдений.\n */\n readonly unused: readonly string[];\n readonly versions: readonly string[];\n readonly total: number;\n}\n\n/** Дата файла наблюдений: `2026-08-12.jsonl`. Чужое имя в каталоге не наблюдение. */\nconst DAY_FILE: RegExp = /^(\\d{4}-\\d{2}-\\d{2})\\.jsonl$/;\n\nconst dayOf: (name: string) => string | null = (name: string): string | null => DAY_FILE.exec(name)?.[1] ?? null;\n\n/** День, отстоящий от названного на столько-то суток назад. Считается в UTC — как и пишется. */\nexport function dayBefore(today: string, days: number): string {\n const at: number = Date.parse(`${today}T00:00:00Z`);\n\n return Number.isNaN(at) ? today : new Date(at - days * 86_400_000).toISOString().slice(0, 10);\n}\n\n/**\n * Одна строка наблюдения. Битая пропускается молча: наблюдение — побочная запись гарда, и\n * оборванная на полуслове строка не имеет права уронить сводку.\n */\nexport function parseObservation(line: string): IObservation | null {\n if (!line.trim()) {\n return null;\n }\n try {\n const found: Record<string, unknown> = JSON.parse(line) as Record<string, unknown>;\n const event: unknown = found['ev'];\n\n return typeof event === 'string' && event\n ? {\n event,\n resource: typeof found['res'] === 'string' ? found['res'] : '',\n kind: typeof found['kind'] === 'string' ? found['kind'] : '',\n session: typeof found['sid'] === 'string' ? found['sid'] : '',\n version: typeof found['v'] === 'string' ? found['v'] : '',\n }\n : null;\n } catch {\n return null;\n }\n}\n\nexport interface IReadResult {\n readonly observations: readonly IObservation[];\n /** Файлы, снятые по сроку хранения: их называют вслух, а не убирают молча. */\n readonly swept: readonly string[];\n /** Каталога нет вовсе — записи не велось ни разу. */\n readonly silent: boolean;\n}\n\n/** Раньше ли день черты. Дни записаны `ГГГГ-ММ-ДД`, и порядок строк у них хронологический. */\nfunction isBefore(day: string, edge: string): boolean {\n return byText(day, edge) < 0;\n}\n\n/** Записи одного дня из его файла. Строка, которая не разбирается, пропускается молча. */\nfunction observationsOfDay(path: string): readonly IObservation[] {\n const found: IObservation[] = [];\n\n for (const line of readFileSync(path, 'utf8').split('\\n')) {\n const one: IObservation | null = parseObservation(line);\n\n if (one) {\n found.push(one);\n }\n }\n\n return found;\n}\n\n/**\n * Наблюдения за отрезок и уборка того, что старше срока хранения.\n *\n * Уборка идёт здесь, а не отдельной командой: каталог растёт от работы, а не от запусков, и\n * команда, о которой надо помнить, до второго месяца не доживает.\n */\nexport function readObservations(root: string, today: string, days: number): IReadResult {\n const dir: string = join(root, OBSERVATIONS_DIR);\n if (!existsSync(dir)) {\n return { observations: [], swept: [], silent: true };\n }\n\n const since: string = dayBefore(today, days - 1);\n const keepFrom: string = dayBefore(today, KEEP_DAYS);\n const observations: IObservation[] = [];\n const swept: string[] = [];\n\n for (const name of readdirSync(dir).sort(byText)) {\n const day: string | null = dayOf(name);\n\n if (!day) {\n continue;\n }\n\n if (isBefore(day, keepFrom)) {\n rmSync(join(dir, name), { force: true });\n swept.push(name);\n } else if (isBefore(day, since)) {\n // День старше запрошенного отрезка, но моложе черты хранения: файл остаётся лежать,\n // а в этот отчёт не идёт\n } else {\n observations.push(...observationsOfDay(join(dir, name)));\n }\n }\n\n return { observations, swept, silent: false };\n}\n\nconst countBy: (values: readonly string[]) => readonly ICount[] = (values: readonly string[]): readonly ICount[] => {\n const counted: Map<string, number> = new Map();\n for (const value of values) {\n if (value) {\n counted.set(value, (counted.get(value) ?? 0) + 1);\n }\n }\n\n return [...counted]\n .map(([name, count]: [string, number]): ICount => ({ name, count }))\n .sort((left: ICount, right: ICount): number => right.count - left.count || left.name.localeCompare(right.name));\n};\n\nconst of: (observations: readonly IObservation[], event: TEvent) => readonly IObservation[] = (\n observations: readonly IObservation[],\n event: TEvent\n): readonly IObservation[] => observations.filter((entry: IObservation): boolean => entry.event === event);\n\n/** Роды правки, которые правкой файла не являются: гейт сработал не на файле. */\nconst OFF_FILE_KINDS: readonly string[] = ['command', 'browser'];\n\n/**\n * Сводка за отрезок.\n *\n * `known` — имена правил, разложенных в это дерево. Без них сводка отвечает только на вопрос\n * «чем пользовались», а самое ценное — чем не пользовались ни разу — сказать нечем: в самих\n * наблюдениях незагруженного правила нет по определению.\n *\n * `weights` — вес этих правил на диске. Без них сводка говорит, сколько раз правило открывали, и\n * молчит о том, во что это обошлось: тридцать загрузок паттерна и тридцать загрузок правила на\n * пятьсот строк стоят разного, а в счёте выглядят одинаково. Не назван — вес считается нулём, и\n * строки о нём в выводе не будет вовсе.\n */\nexport function summarize(observations: readonly IObservation[], known: readonly string[], days: number, weights: TWeights = {}): ISummary {\n const loads: readonly IObservation[] = of(observations, 'skill-load');\n const denials: readonly IObservation[] = of(observations, 'gate-deny');\n const loaded: Set<string> = new Set(loads.map((entry: IObservation): string => entry.resource));\n const sessions: number = new Set(observations.map((entry: IObservation): string => entry.session).filter(Boolean)).size;\n const bytes: number = loads.reduce((found: number, entry: IObservation): number => found + (weights[entry.resource] ?? 0), 0);\n\n return {\n days,\n sessions,\n bytes,\n loads: countBy(loads.map((entry: IObservation): string => entry.resource)),\n bytesPerSession: sessions > 0 ? Math.round(bytes / sessions) : 0,\n denials: countBy(denials.map((entry: IObservation): string => entry.resource)),\n kinds: countBy(denials.map((entry: IObservation): string => entry.kind)),\n denialsOffFile: denials.filter((entry: IObservation): boolean => OFF_FILE_KINDS.includes(entry.kind)).length,\n guards: countBy(of(observations, 'guard-deny').map((entry: IObservation): string => entry.resource)),\n unused: known.filter((name: string): boolean => !loaded.has(name)).sort(byText),\n versions: [...new Set(observations.map((entry: IObservation): string => entry.version).filter(Boolean))].sort(byText),\n total: observations.length,\n };\n}\n"]}
1
+ {"version":3,"file":"observations.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/observations.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Наблюдения и сводка по ним.\n *\n * Наблюдения пишет `hooks/observe.sh` — по строке на событие, по файлу на день. Здесь они\n * читаются и сводятся: чем пользовались, чем не пользовались ни разу, обо что спотыкались.\n *\n * Разбор отделён от чтения диска намеренно: сводка — то, ради чего наблюдения ведутся, и\n * проверять её на подложенных файлах значило бы проверять файловую систему.\n *\n * Дата приходит доводом, а не берётся из часов: сводка за отрезок дней иначе не проверяется\n * спекой — вчерашний файл фикстуры завтра оказывается позавчерашним.\n */\nimport { existsSync, readFileSync, readdirSync, rmSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { byText } from './order.js';\n\n/** Куда `observe.sh` пишет наблюдения. Путь от корня дерева. */\nexport const OBSERVATIONS_DIR: string = '.claude/rt-kit/observations';\n\n/** Сколько дней наблюдения лежат. Каталог, который только растёт, через год перестают открывать. */\nexport const KEEP_DAYS: number = 30;\n\n/** За сколько дней сводка отвечает, когда отрезок не назван. */\nexport const DEFAULT_DAYS: number = 3;\n\n/** Роды событий, которые пишут гарды. */\nexport type TEvent = 'skill-load' | 'gate-deny' | 'guard-deny';\n\nexport interface IObservation {\n readonly event: string;\n /** Имя правила или гарда — имя ресурса пакета, и только оно. */\n readonly resource: string;\n /** Род правки: расширение, `command` или `browser`. Пути в наблюдении нет. */\n readonly kind: string;\n /** Признак сессии: считает заходы, имени сессии не выдаёт. */\n readonly session: string;\n readonly version: string;\n}\n\n/** Сколько раз что-то встретилось. Порядок — по убыванию, при равенстве по имени. */\nexport interface ICount {\n readonly name: string;\n readonly count: number;\n}\n\n/**\n * Вес ресурса на диске, байтами: имя правила в число.\n *\n * Приходит доводом, а не читается здесь, по той же причине, что и день: разбор отделён от диска,\n * и сводку, читающую каталог правил сама, пришлось бы проверять подложенными файлами.\n */\nexport type TWeights = Readonly<Record<string, number>>;\n\nexport interface ISummary {\n readonly days: number;\n /** Сколько заходов оставило хоть одно наблюдение. */\n readonly sessions: number;\n readonly loads: readonly ICount[];\n /**\n * Вес того, что заходы прочитали за отрезок, байтами. Каждая загрузка считается своим весом:\n * правило, открытое в трёх заходах, стоило места трижды — тем, что оно одно, это не дешевле.\n * Ресурс, веса которого не назвали, считается нулём: врать числом хуже, чем не знать его.\n */\n readonly bytes: number;\n /**\n * Тот же вес, приходящийся на один заход, — цена входа в работу.\n *\n * Ноль заходов даёт ноль, а не отказ: сводка за пустой отрезок печатается наравне с полной,\n * и деление на ноль уронило бы её на том единственном случае, ради которого её и зовут, —\n * «а было ли вообще что-нибудь».\n */\n readonly bytesPerSession: number;\n /** Отбития гейта по правилам. */\n readonly denials: readonly ICount[];\n /** Род правки, на котором гейт отбивал. */\n readonly kinds: readonly ICount[];\n /**\n * Отбития, пришедшие не на правку файла: род правки — команда оболочки или браузер.\n *\n * Считается здесь, а не выводится читателем из разбивки по родам: доля, которую надо\n * сложить глазами из десятка строк, не читается вовсе — а именно она говорит, требует ли\n * гейт правило под то, что правкой не является.\n */\n readonly denialsOffFile: number;\n readonly guards: readonly ICount[];\n /**\n * Гарды, разложенные в дерево и не отбившие за отрезок ни разу.\n *\n * Считается той же разностью, что и незагруженные правила, и по той же причине: в самих\n * наблюдениях молчащего гарда нет по определению, а из перечня защит его никто не вычеркнет\n * — молчащий гард и ненужный выглядят одинаково ровно до разбора.\n */\n readonly silentGuards: readonly string[];\n /**\n * Правила, разложенные в дерево и не загруженные за отрезок ни разу. Мёртвый ресурс иначе\n * неотличим от работающего — и это единственная строка сводки, которую нельзя получить из\n * самих наблюдений.\n */\n readonly unused: readonly string[];\n readonly versions: readonly string[];\n readonly total: number;\n}\n\n/** Дата файла наблюдений: `2026-08-12.jsonl`. Чужое имя в каталоге не наблюдение. */\nconst DAY_FILE: RegExp = /^(\\d{4}-\\d{2}-\\d{2})\\.jsonl$/;\n\nconst dayOf: (name: string) => string | null = (name: string): string | null => DAY_FILE.exec(name)?.[1] ?? null;\n\n/** День, отстоящий от названного на столько-то суток назад. Считается в UTC — как и пишется. */\nexport function dayBefore(today: string, days: number): string {\n const at: number = Date.parse(`${today}T00:00:00Z`);\n\n return Number.isNaN(at) ? today : new Date(at - days * 86_400_000).toISOString().slice(0, 10);\n}\n\n/**\n * Одна строка наблюдения. Битая пропускается молча: наблюдение — побочная запись гарда, и\n * оборванная на полуслове строка не имеет права уронить сводку.\n */\nexport function parseObservation(line: string): IObservation | null {\n if (!line.trim()) {\n return null;\n }\n try {\n const found: Record<string, unknown> = JSON.parse(line) as Record<string, unknown>;\n const event: unknown = found['ev'];\n\n return typeof event === 'string' && event\n ? {\n event,\n resource: typeof found['res'] === 'string' ? found['res'] : '',\n kind: typeof found['kind'] === 'string' ? found['kind'] : '',\n session: typeof found['sid'] === 'string' ? found['sid'] : '',\n version: typeof found['v'] === 'string' ? found['v'] : '',\n }\n : null;\n } catch {\n return null;\n }\n}\n\nexport interface IReadResult {\n readonly observations: readonly IObservation[];\n /** Файлы, снятые по сроку хранения: их называют вслух, а не убирают молча. */\n readonly swept: readonly string[];\n /** Каталога нет вовсе — записи не велось ни разу. */\n readonly silent: boolean;\n}\n\n/** Раньше ли день черты. Дни записаны `ГГГГ-ММ-ДД`, и порядок строк у них хронологический. */\nfunction isBefore(day: string, edge: string): boolean {\n return byText(day, edge) < 0;\n}\n\n/** Записи одного дня из его файла. Строка, которая не разбирается, пропускается молча. */\nfunction observationsOfDay(path: string): readonly IObservation[] {\n const found: IObservation[] = [];\n\n for (const line of readFileSync(path, 'utf8').split('\\n')) {\n const one: IObservation | null = parseObservation(line);\n\n if (one) {\n found.push(one);\n }\n }\n\n return found;\n}\n\n/**\n * Наблюдения за отрезок и уборка того, что старше срока хранения.\n *\n * Уборка идёт здесь, а не отдельной командой: каталог растёт от работы, а не от запусков, и\n * команда, о которой надо помнить, до второго месяца не доживает.\n */\nexport function readObservations(root: string, today: string, days: number): IReadResult {\n const dir: string = join(root, OBSERVATIONS_DIR);\n if (!existsSync(dir)) {\n return { observations: [], swept: [], silent: true };\n }\n\n const since: string = dayBefore(today, days - 1);\n const keepFrom: string = dayBefore(today, KEEP_DAYS);\n const observations: IObservation[] = [];\n const swept: string[] = [];\n\n for (const name of readdirSync(dir).sort(byText)) {\n const day: string | null = dayOf(name);\n\n if (!day) {\n continue;\n }\n\n if (isBefore(day, keepFrom)) {\n rmSync(join(dir, name), { force: true });\n swept.push(name);\n } else if (isBefore(day, since)) {\n // День старше запрошенного отрезка, но моложе черты хранения: файл остаётся лежать,\n // а в этот отчёт не идёт\n } else {\n observations.push(...observationsOfDay(join(dir, name)));\n }\n }\n\n return { observations, swept, silent: false };\n}\n\nconst countBy: (values: readonly string[]) => readonly ICount[] = (values: readonly string[]): readonly ICount[] => {\n const counted: Map<string, number> = new Map();\n for (const value of values) {\n if (value) {\n counted.set(value, (counted.get(value) ?? 0) + 1);\n }\n }\n\n return [...counted]\n .map(([name, count]: [string, number]): ICount => ({ name, count }))\n .sort((left: ICount, right: ICount): number => right.count - left.count || left.name.localeCompare(right.name));\n};\n\nconst of: (observations: readonly IObservation[], event: TEvent) => readonly IObservation[] = (\n observations: readonly IObservation[],\n event: TEvent\n): readonly IObservation[] => observations.filter((entry: IObservation): boolean => entry.event === event);\n\n/** Роды правки, которые правкой файла не являются: гейт сработал не на файле. */\nconst OFF_FILE_KINDS: readonly string[] = ['command', 'browser'];\n\n/**\n * Сводка за отрезок.\n *\n * `known` — имена правил, разложенных в это дерево. Без них сводка отвечает только на вопрос\n * «чем пользовались», а самое ценное — чем не пользовались ни разу — сказать нечем: в самих\n * наблюдениях незагруженного правила нет по определению.\n *\n * `guards` — имена гардов, разложенных в это дерево. Гардом здесь считается тот, кто заявил своё\n * имя для наблюдений: хук без заявки в наблюдениях не появится ни при каком отрезке, и молчащим\n * он назывался бы неверно — он молчит не потому, что не понадобился.\n *\n * `weights` — вес этих правил на диске. Без них сводка говорит, сколько раз правило открывали, и\n * молчит о том, во что это обошлось: тридцать загрузок паттерна и тридцать загрузок правила на\n * пятьсот строк стоят разного, а в счёте выглядят одинаково. Не назван — вес считается нулём, и\n * строки о нём в выводе не будет вовсе.\n */\nexport function summarize(\n observations: readonly IObservation[],\n known: readonly string[],\n days: number,\n weights: TWeights = {},\n guards: readonly string[] = []\n): ISummary {\n const loads: readonly IObservation[] = of(observations, 'skill-load');\n const denials: readonly IObservation[] = of(observations, 'gate-deny');\n const loaded: Set<string> = new Set(loads.map((entry: IObservation): string => entry.resource));\n const sessions: number = new Set(observations.map((entry: IObservation): string => entry.session).filter(Boolean)).size;\n const bytes: number = loads.reduce((found: number, entry: IObservation): number => found + (weights[entry.resource] ?? 0), 0);\n const denied: Set<string> = new Set(of(observations, 'guard-deny').map((entry: IObservation): string => entry.resource));\n\n return {\n days,\n sessions,\n bytes,\n loads: countBy(loads.map((entry: IObservation): string => entry.resource)),\n bytesPerSession: sessions > 0 ? Math.round(bytes / sessions) : 0,\n denials: countBy(denials.map((entry: IObservation): string => entry.resource)),\n kinds: countBy(denials.map((entry: IObservation): string => entry.kind)),\n denialsOffFile: denials.filter((entry: IObservation): boolean => OFF_FILE_KINDS.includes(entry.kind)).length,\n guards: countBy(of(observations, 'guard-deny').map((entry: IObservation): string => entry.resource)),\n silentGuards: guards.filter((name: string): boolean => !denied.has(name)).sort(byText),\n unused: known.filter((name: string): boolean => !loaded.has(name)).sort(byText),\n versions: [...new Set(observations.map((entry: IObservation): string => entry.version).filter(Boolean))].sort(byText),\n total: observations.length,\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rt-tools/agent-kit",
3
- "version": "0.20.0",
3
+ "version": "0.22.0",
4
4
  "description": "Переносимый слой правил для агента: законы, хуки, проверки и агенты, раскладываемые в репозиторий одной командой",
5
5
  "author": "RT Team",
6
6
  "license": "Apache-2.0",
Binary file
Binary file