@rt-tools/agent-kit 0.16.1 → 0.18.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 (136) hide show
  1. package/assets/checks/board-epics.github.mjs +123 -0
  2. package/assets/checks/board-gh.github.mjs +121 -0
  3. package/assets/checks/board-paths.github.mjs +88 -0
  4. package/assets/checks/board-runs.github.mjs +8 -0
  5. package/assets/checks/board-titles.github.mjs +66 -0
  6. package/assets/checks/board.github.mjs +77 -73
  7. package/assets/checks/check-board.github.mjs +56 -7
  8. package/assets/checks/check-file-size.mjs +10 -2
  9. package/assets/checks/check-glossary.mjs +170 -0
  10. package/assets/checks/check-hook-scope.mjs +126 -0
  11. package/assets/checks/check-profile-drift.mjs +195 -0
  12. package/assets/checks/check-push-gate.mjs +59 -1
  13. package/assets/checks/check-schema-drift.mjs +12 -5
  14. package/assets/checks/check-specs.mjs +1 -1
  15. package/assets/checks/rt-kit-checks.config.mjs +13 -0
  16. package/assets/checks/spec-anchors.mjs +2 -2
  17. package/assets/checks/spec-contract.mjs +9 -0
  18. package/assets/defaults/gate-map.sh +13 -4
  19. package/assets/defaults/project.sh +31 -108
  20. package/assets/defaults/shell.sh +139 -0
  21. package/assets/docs/GLOSSARY.md +1 -1
  22. package/assets/hooks/browser-device-id.sh +20 -4
  23. package/assets/hooks/browser-guard-device-id.sh +5 -2
  24. package/assets/hooks/browser-guard-no-other-drivers.sh +41 -2
  25. package/assets/hooks/claim-guard.sh +22 -1
  26. package/assets/hooks/docs-guard.sh +1 -1
  27. package/assets/hooks/exam-guard.sh +85 -12
  28. package/assets/hooks/git-guard-delivery-conflict.sh +85 -0
  29. package/assets/hooks/git-guard-delivery-folder.sh +19 -0
  30. package/assets/hooks/git-guard-delivery-signature.sh +14 -5
  31. package/assets/hooks/git-guard-delivery.sh +54 -2
  32. package/assets/hooks/git-guard-push-tests.sh +21 -1
  33. package/assets/hooks/grill-gate-ask.sh +25 -0
  34. package/assets/hooks/grill-gate.sh +75 -11
  35. package/assets/hooks/lint-after-edit.sh +44 -16
  36. package/assets/hooks/proposal-guard.sh +11 -4
  37. package/assets/hooks/rule-source-guard.sh +8 -13
  38. package/assets/hooks/skill-gate-layers.sh +7 -0
  39. package/assets/hooks/skill-gate.sh +29 -0
  40. package/assets/hooks/task-context-load.sh +32 -0
  41. package/assets/hooks/task-flow-context.sh +194 -0
  42. package/assets/hooks/task-flow-draft-guard.sh +109 -0
  43. package/assets/hooks/task-flow-guard.sh +27 -159
  44. package/assets/hooks/turn-exit-guard.sh +53 -88
  45. package/assets/hooks/waiting-turn-guard.sh +65 -2
  46. package/assets/hooks/window-fill-guard.sh +5 -1
  47. package/assets/hooks/work-start-guard.sh +166 -0
  48. package/assets/hooks/write-targets.sh +24 -0
  49. package/assets/laws/delivery.md +19 -0
  50. package/assets/laws/frontend-application.md +4 -0
  51. package/assets/laws/verifiability.md +44 -0
  52. package/assets/laws/work-conduct.md +15 -0
  53. package/assets/patterns/browser-verification-measure.md +3 -1
  54. package/assets/patterns/browser-verification-stand.md +17 -4
  55. package/assets/patterns/doc-style-write.md +62 -2
  56. package/assets/patterns/git-workflow-commit.github.md +33 -5
  57. package/assets/patterns/git-workflow-docker.md +22 -0
  58. package/assets/patterns/git-workflow-freshness.md +87 -0
  59. package/assets/patterns/git-workflow-merge.md +41 -0
  60. package/assets/patterns/git-workflow-migration.md +11 -0
  61. package/assets/patterns/git-workflow-pr.github.md +12 -1
  62. package/assets/patterns/git-workflow-restart.md +18 -0
  63. package/assets/patterns/git-workflow-secrets.md +14 -0
  64. package/assets/patterns/git-workflow-stack.md +93 -1
  65. package/assets/patterns/lib-layers-move.md +4 -0
  66. package/assets/patterns/spec-driven-domain.md +47 -2
  67. package/assets/patterns/spec-driven-rule.md +16 -4
  68. package/assets/patterns/spec-driven-sweep.md +57 -0
  69. package/assets/patterns/status-report-table.github.md +1 -1
  70. package/assets/patterns/task-flow-archive.md +58 -16
  71. package/assets/patterns/task-flow-close.md +106 -23
  72. package/assets/patterns/task-flow-handoff.md +14 -2
  73. package/assets/patterns/task-flow-resume.md +35 -8
  74. package/assets/patterns/task-flow-start.md +60 -22
  75. package/assets/patterns/testing-e2e.md +23 -0
  76. package/assets/patterns/turn-entry-map.md +1 -1
  77. package/assets/pitfalls/agent-kit.md +71 -3
  78. package/assets/pitfalls/doc-style.md +15 -0
  79. package/assets/pitfalls/git-workflow.github.md +88 -0
  80. package/assets/pitfalls/spec-driven.md +22 -0
  81. package/assets/pitfalls/task-flow.md +113 -0
  82. package/assets/pitfalls/testing.md +6 -0
  83. package/assets/pitfalls/turn-conduct.md +49 -0
  84. package/assets/rules/browser-verification.md +58 -0
  85. package/assets/rules/deploy-flow.azure.md +8 -0
  86. package/assets/rules/deploy-flow.github.md +27 -0
  87. package/assets/rules/deploy-flow.gitlab.md +8 -0
  88. package/assets/rules/doc-style.md +20 -0
  89. package/assets/rules/git-workflow.azure.md +22 -2
  90. package/assets/rules/git-workflow.github.md +127 -118
  91. package/assets/rules/git-workflow.gitlab.md +17 -4
  92. package/assets/rules/lists.md +5 -0
  93. package/assets/rules/observability.needs-app.md +4 -0
  94. package/assets/rules/reuse-first.md +14 -0
  95. package/assets/rules/shared-code.md +5 -0
  96. package/assets/rules/spec-driven.md +33 -15
  97. package/assets/rules/task-flow.md +132 -126
  98. package/assets/rules/testing.md +41 -2
  99. package/assets/rules/turn-conduct.md +73 -57
  100. package/assets/rules/turn-entry.md +6 -0
  101. package/assets/samples/tasks/_template/grill.md +5 -0
  102. package/assets/samples/tasks/_template/plan.md +3 -0
  103. package/assets/skills/agent-kit.md +108 -76
  104. package/assets/templates/postmortem.md +5 -1
  105. package/bin/agent-kit.d.ts.map +1 -1
  106. package/bin/agent-kit.js +30 -6
  107. package/bin/agent-kit.js.map +1 -1
  108. package/lib/catalog.d.ts.map +1 -1
  109. package/lib/catalog.js +2 -1
  110. package/lib/catalog.js.map +1 -1
  111. package/lib/commands.d.ts.map +1 -1
  112. package/lib/commands.js +96 -7
  113. package/lib/commands.js.map +1 -1
  114. package/lib/hooks-map.d.ts +26 -0
  115. package/lib/hooks-map.d.ts.map +1 -1
  116. package/lib/hooks-map.js +58 -2
  117. package/lib/hooks-map.js.map +1 -1
  118. package/lib/sections.d.ts +6 -0
  119. package/lib/sections.d.ts.map +1 -1
  120. package/lib/sections.js +19 -0
  121. package/lib/sections.js.map +1 -1
  122. package/lib/shipment.d.ts +2 -0
  123. package/lib/shipment.d.ts.map +1 -1
  124. package/lib/shipment.fixture.d.ts +5 -0
  125. package/lib/shipment.fixture.d.ts.map +1 -1
  126. package/lib/shipment.fixture.js +7 -0
  127. package/lib/shipment.fixture.js.map +1 -1
  128. package/lib/shipment.js +13 -1
  129. package/lib/shipment.js.map +1 -1
  130. package/lib/sync.d.ts +35 -3
  131. package/lib/sync.d.ts.map +1 -1
  132. package/lib/sync.js +59 -8
  133. package/lib/sync.js.map +1 -1
  134. package/package.json +1 -1
  135. package/rt-tools-agent-kit-0.18.0.tgz +0 -0
  136. package/rt-tools-agent-kit-0.16.1.tgz +0 -0
package/lib/sync.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/sync.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAuC,MAAM,cAAc,CAAC;AAC3F,OAAO,EAAe,WAAW,EAAmB,aAAa,EAA4B,MAAM,cAAc,CAAC;AAClH,OAAO,EAAE,UAAU,EAAyB,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,OAAO,EAAiB,MAAM,aAAa,CAAC;AACrD,OAAO,EAA6C,YAAY,EAAE,aAAa,EAAgB,MAAM,gBAAgB,CAAC;AACtH,OAAO,EAAE,QAAQ,EAAkC,MAAM,WAAW,CAAC;AAOrE,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;IACtC,oFAAoF;IACpF,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;IACzD,+EAA+E;IAC/E,QAAQ,CAAC,UAAU,EAAE,SAAS,UAAU,EAAE,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IAC1C;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,SAAS,SAAS,EAAE,CAAC;IACpC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,SAAS,EAAE,CAAC;IACzC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,6FAA6F;IAC7F,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAoBD,+FAA+F;AAC/F,MAAM,WAAW,SAAS;IACtB,qCAAqC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;CAC7B;AAgDD;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC1B,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACxB;AAgCD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,CAqCvG;AAED,mGAAmG;AACnG,wBAAgB,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,CA0CtG;AAED,eAAO,MAAM,SAAS,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,SAAS,QAAQ,EACe,CAAC"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/sync.ts"],"names":[],"mappings":"AAUA,OAAO,EAAiB,MAAM,EAAY,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,SAAS,EAAuC,MAAM,cAAc,CAAC;AAC3F,OAAO,EAAe,WAAW,EAAmB,aAAa,EAA4B,MAAM,cAAc,CAAC;AAClH,OAAO,EAAE,UAAU,EAAyB,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,OAAO,EAA+B,MAAM,aAAa,CAAC;AACnE,OAAO,EAIH,WAAW,EACX,YAAY,EACZ,aAAa,EAEhB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,QAAQ,EAAkC,MAAM,WAAW,CAAC;AAOrE,MAAM,WAAW,WAAW;IACxB,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;IACtC,oFAAoF;IACpF,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;IACzD,+EAA+E;IAC/E,QAAQ,CAAC,UAAU,EAAE,SAAS,UAAU,EAAE,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,CAAC;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IAC1C;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,SAAS,SAAS,EAAE,CAAC;IACpC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,SAAS,SAAS,EAAE,CAAC;IACzC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,6FAA6F;IAC7F,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C;;;;;;;;OAQG;IACH,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;CACtC;AAOD;;;;;GAKG;AACH;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI9D;AAMD,+FAA+F;AAC/F,MAAM,WAAW,SAAS;IACtB,qCAAqC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;CAC7B;AAgDD;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC1B,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACxB;AAsED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,CAuCvG;AAED,mGAAmG;AACnG,wBAAgB,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,CAgDtG;AAED,eAAO,MAAM,SAAS,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,SAAS,QAAQ,EACe,CAAC"}
package/lib/sync.js CHANGED
@@ -5,14 +5,15 @@
5
5
  * только потом писать. `sync --check` отличается от `sync` ровно последним шагом — иначе гейт
6
6
  * пуша проверял бы не то, что кладёт раскладка.
7
7
  */
8
- import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
9
- import { dirname, join } from 'node:path';
8
+ import { chmodSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
9
+ import { dirname, join, relative } from 'node:path';
10
10
  import { collectAssets, targetOf } from './assets.js';
11
11
  import { cascadeCuts, idleSkips, namedButCut } from './cascade.js';
12
12
  import { brokenLinks, readCatalog, variantGaps } from './catalog.js';
13
13
  import { pathOf, planCompanion } from './companion.js';
14
- import { OVERRIDES_DIR } from './config.js';
15
- import { bindingsOf as declaredIn, driftedMatchers, unboundHooks } from './hooks-map.js';
14
+ import { KINDS, OVERRIDES_DIR } from './config.js';
15
+ import { bindDispatch, bindingsOf as declaredIn, driftedMatchers, unboundHooks, } from './hooks-map.js';
16
+ import { byText } from './order.js';
16
17
  import { isPending, isRefusal, planFile } from './plan.js';
17
18
  import { RETIRED } from './retired.js';
18
19
  import { mergeDocuments, parseDocument, renderDocument } from './sections.js';
@@ -28,10 +29,20 @@ const read = (path) => (existsSync(path) ? readFileSync(path, 'utf8') : null);
28
29
  * Порядок именно такой. Надстройка тоже пишется с дырками — иначе проект, дописавший раздел про
29
30
  * свою главную ветку, зашил бы её имя в двух местах: в конфиге и в тексте.
30
31
  */
31
- function renderAsset(asset, config, root) {
32
+ /**
33
+ * Текст ресурса с наложенной надстройкой дерева — то, что ложится в дерево, без подстановки
34
+ * значений.
35
+ *
36
+ * Экспортируется потому, что о разложенном тексте спрашивает не одна раскладка: счёт долга
37
+ * привязок судит статьи компаньона против правила, и судить их по пакетной редакции значит
38
+ * называть долгом статьи, которых в разложенном правиле нет вовсе.
39
+ */
40
+ export function mergedBody(asset, root) {
32
41
  const override = read(join(root, OVERRIDES_DIR, asset.id));
33
- const merged = override ? renderDocument(mergeDocuments(parseDocument(asset.text), parseDocument(override))) : asset.text;
34
- return renderVars(merged, config.vars);
42
+ return override ? renderDocument(mergeDocuments(parseDocument(asset.text), parseDocument(override))) : asset.text;
43
+ }
44
+ function renderAsset(asset, config, root) {
45
+ return renderVars(mergedBody(asset, root), config.vars);
35
46
  }
36
47
  /**
37
48
  * Что лежит в дереве от ресурсов, которые больше не раскладываются.
@@ -91,6 +102,39 @@ function bindingsOf(config, assetsDir) {
91
102
  }
92
103
  return bindings;
93
104
  }
105
+ /**
106
+ * Надстройки, которым в пакете ничего не отвечает.
107
+ *
108
+ * Ищутся по всему каталогу надстроек, а не по выбранным ресурсам: надстройка над ресурсом,
109
+ * от которого дерево отказалось, названа своей строкой отказа, а эта — про имя, которого у
110
+ * пакета нет вовсе.
111
+ */
112
+ function strayOverrides(root, assetsDir) {
113
+ const dir = join(root, OVERRIDES_DIR);
114
+ if (!existsSync(dir)) {
115
+ return [];
116
+ }
117
+ const known = new Set(readCatalog(assetsDir).map((entry) => entry.id));
118
+ const found = [];
119
+ const walk = (at) => {
120
+ for (const entry of readdirSync(at, { withFileTypes: true })) {
121
+ const path = join(at, entry.name);
122
+ if (entry.isDirectory()) {
123
+ walk(path);
124
+ continue;
125
+ }
126
+ const id = relative(dir, path);
127
+ // Файл вне рода ресурсов надстройкой не считается вовсе: рядом с ней законно лежит
128
+ // README каталога, и звать его неприменённым значило бы шуметь на каждой раскладке.
129
+ const kind = id.split('/')[0];
130
+ if (KINDS.includes(kind) && !known.has(id)) {
131
+ found.push(id);
132
+ }
133
+ }
134
+ };
135
+ walk(dir);
136
+ return found.sort(byText);
137
+ }
94
138
  export function planSync(config, root, version, assetsDir) {
95
139
  const planned = [];
96
140
  const missing = new Map();
@@ -121,7 +165,9 @@ export function planSync(config, root, version, assetsDir) {
121
165
  cutOnDisk: left.cut,
122
166
  namedCut: namedButCut(readCatalog(assetsDir), config),
123
167
  retired: retiredOf(config, root),
168
+ strayOverrides: strayOverrides(root, assetsDir),
124
169
  written: [],
170
+ bound: null,
125
171
  };
126
172
  }
127
173
  /** Раскладка. Отказ хотя бы по одному файлу не пишет ничего: половина разложенного хуже целого. */
@@ -160,7 +206,12 @@ export function runSync(config, root, version, assetsDir) {
160
206
  writeFileSync(path, companion.content, 'utf8');
161
207
  written.push(companion.path);
162
208
  }
163
- return { ...result, written };
209
+ // Объявление кладётся вместе с самим гардом, а не печатается просьбой вставить его рукой:
210
+ // разложенный и никем не зовомый гард снаружи неотличим от работающего. Идёт это последним —
211
+ // после того как файлы легли: запись, зовущая гард, которого на диске ещё нет, обещает
212
+ // больше, чем стоит.
213
+ const bound = bindDispatch(bindingsOf(config, assetsDir), root);
214
+ return { ...result, written, bound };
164
215
  }
165
216
  export const pendingOf = (result) => result.planned.filter((entry) => isPending(entry.outcome));
166
217
  //# sourceMappingURL=sync.js.map
package/lib/sync.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/sync.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Раскладка ресурсов пакета в дерево проекта.\n *\n * Порядок один и тот же и для записи, и для проверки: собрать тело, решить судьбу файла, и\n * только потом писать. `sync --check` отличается от `sync` ровно последним шагом — иначе гейт\n * пуша проверял бы не то, что кладёт раскладка.\n */\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nimport { collectAssets, IAsset, targetOf } from './assets.js';\nimport { ICascadeCut, IIdleSkip, cascadeCuts, idleSkips, namedButCut } from './cascade.js';\nimport { brokenLinks, IBrokenLink, IEntryOfCatalog, IGapOfVariant, readCatalog, variantGaps } from './catalog.js';\nimport { ICompanion, pathOf, planCompanion } from './companion.js';\nimport { IConfig, OVERRIDES_DIR } from './config.js';\nimport { bindingsOf as declaredIn, driftedMatchers, IHookBinding, IMatcherDrift, unboundHooks } from './hooks-map.js';\nimport { IPlanned, isPending, isRefusal, planFile } from './plan.js';\nimport { RETIRED } from './retired.js';\nimport { mergeDocuments, parseDocument, renderDocument } from './sections.js';\nimport { readStamped } from './stamp.js';\nimport { IRenderResult, renderVars } from './vars.js';\nimport { matchesVariant } from './variants.js';\n\nexport interface ISyncResult {\n readonly planned: readonly IPlanned[];\n /** Дырки без значения, по ресурсам. Непустой список — отказ: подставлять нечего. */\n readonly missing: ReadonlyMap<string, readonly string[]>;\n /** Компаньоны разложенных правил: имена этого дерева, которые пишет проект. */\n readonly companions: readonly ICompanion[];\n /**\n * Файлы, лежащие в дереве от ресурсов, от которых проект отказался.\n *\n * Отказ значит «больше не клади», а не «убери»: разложенное коммитится, рядом с правилом\n * лежит написанный проектом компаньон, и стирать это молча пакет не вправе. Но и молчать\n * нельзя — брошенный файл читается как действующее правило, и агент по нему работает.\n */\n readonly abandoned: readonly string[];\n /**\n * Ресурсы, у которых виды есть, а вида под выбор дерева нет.\n *\n * Пропустить такой ресурс молча значит разложить правило и не разложить инструмент, который\n * оно зовёт: дерево получает указание без исполнителя и узнаёт об этом, когда команда из\n * правила не находится. Поэтому непустой список — отказ раскладки наравне с дыркой без\n * значения и с файлом, который правили руками.\n */\n readonly gaps: readonly IGapOfVariant[];\n /**\n * Разложенные гарды, которых нет в настройке агента: файл лежит, а позвать его некому.\n *\n * Раскладку это не отбивает — настройка принадлежит дереву, и пакет в неё не пишет, — но и\n * молчать нельзя: гард, который не зовут, неотличим от гарда, который всё пропускает.\n */\n readonly unbound: readonly IHookBinding[];\n /**\n * Гарды, подписанные не на то, что объявляют: их путь в настройке агента назван, а образец\n * вызова разошёлся с объявлением.\n *\n * Хуже неподключённого: снаружи такой гард выглядит работающим, и вызов, ради которого его\n * тело написано, до него не доходит никогда. Гейт правил так и разбирал вызовы браузера\n * веткой, которая не исполнялась ни разу.\n */\n readonly drifted: readonly IMatcherDrift[];\n /**\n * Выбранные ресурсы, чьи требования в дерево не поехали.\n *\n * Предупреждение, а не отказ: дерево вправе закрыть требование своим средством, но обязано\n * знать, что закрывает. Молчание же оставляет его исправным на вид — сверка зелена на любом\n * подмножестве, сколько бы связок ни было разорвано.\n */\n readonly broken: readonly IBrokenLink[];\n /**\n * Строки отказа, которые ничего не снимают.\n *\n * Предупреждение, а не отказ: строка становится лишней сама, обновлением пакета, без единой\n * правки в дереве. Молчание же оставляет её в настройке навсегда — ровно так список отказа и\n * дорастает до полусотни строк, ни одна из которых ни на что не влияет.\n */\n readonly idle: readonly IIdleSkip[];\n /**\n * Разложенные раньше файлы ресурсов, снятых теперь каскадом.\n *\n * Названы отдельно от брошенных: брошенное дерево отвергло само и знает об этом, а снятое\n * каскадом ушло вслед за родителем — искать причину в отказе читатель пойдёт зря.\n */\n readonly cutOnDisk: readonly ICutFound[];\n /**\n * Ресурсы, названные выбором поимённо и снятые каскадом: дерево просило их прямо.\n *\n * Предупреждение, а не отказ: выбор с потомком при невыбранном родителе — состояние\n * настройки, а не промах раскладки, и чинит его дерево у себя.\n */\n readonly namedCut: readonly ICascadeCut[];\n /** Файлы ресурсов, которых в наборе больше нет: их убирает дерево, пакет только называет. */\n readonly retired: readonly IRetiredFound[];\n readonly written: readonly string[];\n}\n\n/** Шаблон черновика компаньона — тот же, что пакет кладёт проекту в шаблоны. */\nconst COMPANION_TEMPLATE: string = 'templates/implementation.md';\n\nconst read: (path: string) => string | null = (path: string): string | null => (existsSync(path) ? readFileSync(path, 'utf8') : null);\n\n/**\n * Тело ресурса: текст пакета, поверх него надстройка проекта, и уже потом подстановка значений.\n *\n * Порядок именно такой. Надстройка тоже пишется с дырками — иначе проект, дописавший раздел про\n * свою главную ветку, зашил бы её имя в двух местах: в конфиге и в тексте.\n */\nfunction renderAsset(asset: IAsset, config: IConfig, root: string): IRenderResult {\n const override: string | null = read(join(root, OVERRIDES_DIR, asset.id));\n const merged: string = override ? renderDocument(mergeDocuments(parseDocument(asset.text), parseDocument(override))) : asset.text;\n\n return renderVars(merged, config.vars);\n}\n\n/** Разложенный раньше файл ресурса, снятого теперь каскадом, и причина, по которой он снят. */\nexport interface ICutFound {\n /** Путь в дереве, где лежит файл. */\n readonly path: string;\n readonly cut: ICascadeCut;\n}\n\n/** Что дерево держит от ресурсов, которые больше не раскладываются. */\ninterface ILeftOnDisk {\n readonly abandoned: readonly string[];\n readonly cut: readonly ICutFound[];\n}\n\n/**\n * Что лежит в дереве от ресурсов, которые больше не раскладываются.\n *\n * Ищется по тем же правилам раскладки, что и всё остальное: путь ресурса вычисляется так, будто\n * его кладут, и проверяется, лежит ли там файл с шапкой пакета. Чужой файл на том же пути\n * брошенным не считается — его пакет не клал.\n *\n * Снятое каскадом отделяется здесь же: дерево от него не отказывалось, и, увидев его среди\n * брошенного, читатель пойдёт искать строку отказа, которой нет.\n */\nfunction leftOnDisk(config: IConfig, root: string, assetsDir: string): ILeftOnDisk {\n const taken: ReadonlySet<string> = new Set(collectAssets(config, assetsDir).map((asset: IAsset): string => asset.id));\n const catalog: readonly IEntryOfCatalog[] = readCatalog(assetsDir);\n const cuts: Map<string, ICascadeCut> = new Map(\n cascadeCuts(catalog, config).map((one: ICascadeCut): [string, ICascadeCut] => [one.id, one])\n );\n\n const abandoned: string[] = [];\n const cut: ICutFound[] = [];\n for (const entry of catalog) {\n const path: string = targetOf(entry, config.layout);\n const existing: string | null = taken.has(entry.id) ? null : read(join(root, path));\n const stamped: boolean = existing !== null && readStamped(existing) !== null;\n\n if (!stamped || !matchesVariant(entry.variant, config.variants)) {\n continue;\n }\n\n const found: ICascadeCut | undefined = cuts.get(entry.id);\n\n if (found) {\n cut.push({ path, cut: found });\n } else {\n abandoned.push(path);\n }\n }\n\n return { abandoned, cut };\n}\n\n/**\n * Что лежит в дереве от ресурсов, которых в наборе больше нет.\n *\n * Брошенным такой файл не назовёт никто: брошенное ищется по каталогу пакета, а снятого в\n * каталоге нет вовсе. Поэтому его ищут по списку снятых имён — там же, где записано, почему\n * ресурс ушёл.\n */\nexport interface IRetiredFound {\n /** Путь в дереве, где лежит файл снятого ресурса. */\n readonly path: string;\n /** Редакция пакета, в которой ресурс снят. */\n readonly since: string;\n readonly why: string;\n}\n\nfunction retiredOf(config: IConfig, root: string): readonly IRetiredFound[] {\n const found: IRetiredFound[] = [];\n\n for (const entry of RETIRED) {\n const path: string = targetOf(entry, config.layout);\n const existing: string | null = read(join(root, path));\n if (existing !== null && readStamped(existing) !== null) {\n found.push({ path, since: entry.since, why: entry.why });\n }\n }\n\n return found;\n}\n\n/**\n * Что о себе говорят гарды, которые дерево берёт. Читается у ресурсов, а не у разложенных\n * файлов: карта нужна и до первой раскладки — чтобы было что вставить в настройку.\n */\nfunction bindingsOf(config: IConfig, assetsDir: string): readonly IHookBinding[] {\n const bindings: IHookBinding[] = [];\n for (const asset of collectAssets(config, assetsDir)) {\n if (asset.kind !== 'hooks') {\n continue;\n }\n bindings.push(...declaredIn(asset.text, asset.target));\n }\n\n return bindings;\n}\n\nexport function planSync(config: IConfig, root: string, version: string, assetsDir: string): ISyncResult {\n const planned: IPlanned[] = [];\n const missing: Map<string, readonly string[]> = new Map();\n const companions: ICompanion[] = [];\n const template: string | null = read(join(assetsDir, COMPANION_TEMPLATE));\n\n for (const asset of collectAssets(config, assetsDir)) {\n const rendered: IRenderResult = renderAsset(asset, config, root);\n if (rendered.missing.length) {\n missing.set(asset.id, rendered.missing);\n continue;\n }\n planned.push(\n planFile({ version, path: asset.target, asset: asset.id, rendered: rendered.text, existing: read(join(root, asset.target)) })\n );\n if (asset.kind === 'rules' && template !== null) {\n companions.push(planCompanion(asset, read(join(root, pathOf(asset))), template));\n }\n }\n\n const left: ILeftOnDisk = leftOnDisk(config, root, assetsDir);\n\n return {\n planned,\n missing,\n companions,\n abandoned: left.abandoned,\n gaps: variantGaps(readCatalog(assetsDir), config),\n unbound: unboundHooks(bindingsOf(config, assetsDir), root),\n drifted: driftedMatchers(bindingsOf(config, assetsDir), root),\n broken: brokenLinks(readCatalog(assetsDir), config),\n idle: idleSkips(readCatalog(assetsDir), config),\n cutOnDisk: left.cut,\n namedCut: namedButCut(readCatalog(assetsDir), config),\n retired: retiredOf(config, root),\n written: [],\n };\n}\n\n/** Раскладка. Отказ хотя бы по одному файлу не пишет ничего: половина разложенного хуже целого. */\nexport function runSync(config: IConfig, root: string, version: string, assetsDir: string): ISyncResult {\n const result: ISyncResult = planSync(config, root, version, assetsDir);\n if (result.missing.size || result.gaps.length || result.planned.some((entry: IPlanned): boolean => isRefusal(entry.outcome))) {\n return result;\n }\n\n // Право на запуск переносится с файла в пакете. Без него гард отвечает отказом доступа, то\n // есть ненулевым кодом, а ненулевой код у гарда значит «правка отбита»: разложенный набор\n // отбивал бы подряд всё, включая сборку и тесты, и причина при этом нигде не называлась.\n const executable: ReadonlySet<string> = new Set(\n collectAssets(config, assetsDir)\n .filter((asset: IAsset): boolean => asset.executable)\n .map((asset: IAsset): string => asset.id)\n );\n\n const written: string[] = [];\n for (const entry of result.planned) {\n if (entry.content === null) {\n continue;\n }\n const path: string = join(root, entry.path);\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, entry.content, 'utf8');\n if (executable.has(entry.asset)) {\n chmodSync(path, 0o755);\n }\n written.push(entry.path);\n }\n\n // Черновик компаньона кладётся только там, где файла нет вовсе. Он без шапки и без суммы:\n // сверять в нём нечего — с первой правки проекта это его текст, а не текст пакета.\n for (const companion of result.companions) {\n if (companion.content === null) {\n continue;\n }\n const path: string = join(root, companion.path);\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, companion.content, 'utf8');\n written.push(companion.path);\n }\n\n return { ...result, written };\n}\n\nexport const pendingOf: (result: ISyncResult) => readonly IPlanned[] = (result: ISyncResult): readonly IPlanned[] =>\n result.planned.filter((entry: IPlanned): boolean => isPending(entry.outcome));\n"]}
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/sync.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACrG,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAU,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAA0B,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3F,OAAO,EAAE,WAAW,EAA+C,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAClH,OAAO,EAAc,MAAM,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAW,KAAK,EAAE,aAAa,EAAS,MAAM,aAAa,CAAC;AACnE,OAAO,EACH,YAAY,EACZ,UAAU,IAAI,UAAU,EACxB,eAAe,EAIf,YAAY,GACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAY,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAiB,UAAU,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AA4F/C,gFAAgF;AAChF,MAAM,kBAAkB,GAAW,6BAA6B,CAAC;AAEjE,MAAM,IAAI,GAAoC,CAAC,IAAY,EAAiB,EAAE,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAEtI;;;;;GAKG;AACH;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,KAAa,EAAE,IAAY;IAClD,MAAM,QAAQ,GAAkB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAE1E,OAAO,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,cAAc,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;AACtH,CAAC;AAED,SAAS,WAAW,CAAC,KAAa,EAAE,MAAe,EAAE,IAAY;IAC7D,OAAO,UAAU,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;AAC5D,CAAC;AAeD;;;;;;;;;GASG;AACH,SAAS,UAAU,CAAC,MAAe,EAAE,IAAY,EAAE,SAAiB;IAChE,MAAM,KAAK,GAAwB,IAAI,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IACtH,MAAM,OAAO,GAA+B,WAAW,CAAC,SAAS,CAAC,CAAC;IACnE,MAAM,IAAI,GAA6B,IAAI,GAAG,CAC1C,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,GAAgB,EAAyB,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAC/F,CAAC;IAEF,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,MAAM,GAAG,GAAgB,EAAE,CAAC;IAC5B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAW,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,QAAQ,GAAkB,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QACpF,MAAM,OAAO,GAAY,QAAQ,KAAK,IAAI,IAAI,WAAW,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;QAE7E,IAAI,CAAC,OAAO,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9D,SAAS;QACb,CAAC;QAED,MAAM,KAAK,GAA4B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAE1D,IAAI,KAAK,EAAE,CAAC;YACR,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;QACnC,CAAC;aAAM,CAAC;YACJ,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;IACL,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;AAC9B,CAAC;AAiBD,SAAS,SAAS,CAAC,MAAe,EAAE,IAAY;IAC5C,MAAM,KAAK,GAAoB,EAAE,CAAC;IAElC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAW,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,QAAQ,GAAkB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QACvD,IAAI,QAAQ,KAAK,IAAI,IAAI,WAAW,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC;YACtD,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7D,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU,CAAC,MAAe,EAAE,SAAiB;IAClD,MAAM,QAAQ,GAAmB,EAAE,CAAC;IACpC,KAAK,MAAM,KAAK,IAAI,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC;QACnD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACzB,SAAS;QACb,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,IAAY,EAAE,SAAiB;IACnD,MAAM,GAAG,GAAW,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IAC9C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACnB,OAAO,EAAE,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAwB,IAAI,GAAG,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,KAAsB,EAAU,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IACrH,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,MAAM,IAAI,GAAyB,CAAC,EAAU,EAAQ,EAAE;QACpD,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,EAAE,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YAC3D,MAAM,IAAI,GAAW,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC1C,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACtB,IAAI,CAAC,IAAI,CAAC,CAAC;gBACX,SAAS;YACb,CAAC;YACD,MAAM,EAAE,GAAW,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACvC,mFAAmF;YACnF,oFAAoF;YACpF,MAAM,IAAI,GAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAa,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBAClD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACnB,CAAC;QACL,CAAC;IACL,CAAC,CAAC;IAEF,IAAI,CAAC,GAAG,CAAC,CAAC;IAEV,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAe,EAAE,IAAY,EAAE,OAAe,EAAE,SAAiB;IACtF,MAAM,OAAO,GAAe,EAAE,CAAC;IAC/B,MAAM,OAAO,GAAmC,IAAI,GAAG,EAAE,CAAC;IAC1D,MAAM,UAAU,GAAiB,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAkB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAE1E,KAAK,MAAM,KAAK,IAAI,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC;QACnD,MAAM,QAAQ,GAAkB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QACjE,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;YACxC,SAAS;QACb,CAAC;QACD,OAAO,CAAC,IAAI,CACR,QAAQ,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAChI,CAAC;QACF,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YAC9C,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;QACrF,CAAC;IACL,CAAC;IAED,MAAM,IAAI,GAAgB,UAAU,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;IAE9D,OAAO;QACH,OAAO;QACP,OAAO;QACP,UAAU;QACV,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,IAAI,EAAE,WAAW,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QACjD,OAAO,EAAE,YAAY,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC;QAC1D,OAAO,EAAE,eAAe,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC;QAC7D,MAAM,EAAE,WAAW,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnD,IAAI,EAAE,SAAS,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAC/C,SAAS,EAAE,IAAI,CAAC,GAAG;QACnB,QAAQ,EAAE,WAAW,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QACrD,OAAO,EAAE,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC;QAChC,cAAc,EAAE,cAAc,CAAC,IAAI,EAAE,SAAS,CAAC;QAC/C,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,IAAI;KACd,CAAC;AACN,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,OAAO,CAAC,MAAe,EAAE,IAAY,EAAE,OAAe,EAAE,SAAiB;IACrF,MAAM,MAAM,GAAgB,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IACvE,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAe,EAAW,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QAC3H,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,2FAA2F;IAC3F,0FAA0F;IAC1F,yFAAyF;IACzF,MAAM,UAAU,GAAwB,IAAI,GAAG,CAC3C,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC;SAC3B,MAAM,CAAC,CAAC,KAAa,EAAW,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC;SACpD,GAAG,CAAC,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAChD,CAAC;IAEF,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YACzB,SAAS;QACb,CAAC;QACD,MAAM,IAAI,GAAW,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5C,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC3C,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC3B,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,0FAA0F;IAC1F,mFAAmF;IACnF,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACxC,IAAI,SAAS,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YAC7B,SAAS;QACb,CAAC;QACD,MAAM,IAAI,GAAW,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QAChD,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,aAAa,CAAC,IAAI,EAAE,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC/C,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,0FAA0F;IAC1F,6FAA6F;IAC7F,uFAAuF;IACvF,qBAAqB;IACrB,MAAM,KAAK,GAAgB,YAAY,CAAC,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;IAE7E,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AACzC,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAiD,CAAC,MAAmB,EAAuB,EAAE,CAChH,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAe,EAAW,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC","sourcesContent":["/**\n * Раскладка ресурсов пакета в дерево проекта.\n *\n * Порядок один и тот же и для записи, и для проверки: собрать тело, решить судьбу файла, и\n * только потом писать. `sync --check` отличается от `sync` ровно последним шагом — иначе гейт\n * пуша проверял бы не то, что кладёт раскладка.\n */\nimport { chmodSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join, relative } from 'node:path';\n\nimport { collectAssets, IAsset, targetOf } from './assets.js';\nimport { ICascadeCut, IIdleSkip, cascadeCuts, idleSkips, namedButCut } from './cascade.js';\nimport { brokenLinks, IBrokenLink, IEntryOfCatalog, IGapOfVariant, readCatalog, variantGaps } from './catalog.js';\nimport { ICompanion, pathOf, planCompanion } from './companion.js';\nimport { IConfig, KINDS, OVERRIDES_DIR, TKind } from './config.js';\nimport {\n bindDispatch,\n bindingsOf as declaredIn,\n driftedMatchers,\n IBindResult,\n IHookBinding,\n IMatcherDrift,\n unboundHooks,\n} from './hooks-map.js';\nimport { byText } from './order.js';\nimport { IPlanned, isPending, isRefusal, planFile } from './plan.js';\nimport { RETIRED } from './retired.js';\nimport { mergeDocuments, parseDocument, renderDocument } from './sections.js';\nimport { readStamped } from './stamp.js';\nimport { IRenderResult, renderVars } from './vars.js';\nimport { matchesVariant } from './variants.js';\n\nexport interface ISyncResult {\n readonly planned: readonly IPlanned[];\n /** Дырки без значения, по ресурсам. Непустой список — отказ: подставлять нечего. */\n readonly missing: ReadonlyMap<string, readonly string[]>;\n /** Компаньоны разложенных правил: имена этого дерева, которые пишет проект. */\n readonly companions: readonly ICompanion[];\n /**\n * Файлы, лежащие в дереве от ресурсов, от которых проект отказался.\n *\n * Отказ значит «больше не клади», а не «убери»: разложенное коммитится, рядом с правилом\n * лежит написанный проектом компаньон, и стирать это молча пакет не вправе. Но и молчать\n * нельзя — брошенный файл читается как действующее правило, и агент по нему работает.\n */\n readonly abandoned: readonly string[];\n /**\n * Ресурсы, у которых виды есть, а вида под выбор дерева нет.\n *\n * Пропустить такой ресурс молча значит разложить правило и не разложить инструмент, который\n * оно зовёт: дерево получает указание без исполнителя и узнаёт об этом, когда команда из\n * правила не находится. Поэтому непустой список — отказ раскладки наравне с дыркой без\n * значения и с файлом, который правили руками.\n */\n readonly gaps: readonly IGapOfVariant[];\n /**\n * Разложенные гарды, которых нет в настройке агента: файл лежит, а позвать его некому.\n *\n * Раскладку это не отбивает: недостающую запись она дописывает сама и говорит, что дописала.\n * Список остаётся тем, что видит планирование и сверка, — они на диск не пишут, — а также\n * деревом, чью настройку не разобрать: там запись по-прежнему делает рука.\n */\n readonly unbound: readonly IHookBinding[];\n /**\n * Гарды, подписанные не на то, что объявляют: их путь в настройке агента назван, а образец\n * вызова разошёлся с объявлением.\n *\n * Хуже неподключённого: снаружи такой гард выглядит работающим, и вызов, ради которого его\n * тело написано, до него не доходит никогда. Гейт правил так и разбирал вызовы браузера\n * веткой, которая не исполнялась ни разу.\n */\n readonly drifted: readonly IMatcherDrift[];\n /**\n * Выбранные ресурсы, чьи требования в дерево не поехали.\n *\n * Предупреждение, а не отказ: дерево вправе закрыть требование своим средством, но обязано\n * знать, что закрывает. Молчание же оставляет его исправным на вид — сверка зелена на любом\n * подмножестве, сколько бы связок ни было разорвано.\n */\n readonly broken: readonly IBrokenLink[];\n /**\n * Строки отказа, которые ничего не снимают.\n *\n * Предупреждение, а не отказ: строка становится лишней сама, обновлением пакета, без единой\n * правки в дереве. Молчание же оставляет её в настройке навсегда — ровно так список отказа и\n * дорастает до полусотни строк, ни одна из которых ни на что не влияет.\n */\n readonly idle: readonly IIdleSkip[];\n /**\n * Разложенные раньше файлы ресурсов, снятых теперь каскадом.\n *\n * Названы отдельно от брошенных: брошенное дерево отвергло само и знает об этом, а снятое\n * каскадом ушло вслед за родителем — искать причину в отказе читатель пойдёт зря.\n */\n readonly cutOnDisk: readonly ICutFound[];\n /**\n * Ресурсы, названные выбором поимённо и снятые каскадом: дерево просило их прямо.\n *\n * Предупреждение, а не отказ: выбор с потомком при невыбранном родителе — состояние\n * настройки, а не промах раскладки, и чинит его дерево у себя.\n */\n readonly namedCut: readonly ICascadeCut[];\n /** Файлы ресурсов, которых в наборе больше нет: их убирает дерево, пакет только называет. */\n readonly retired: readonly IRetiredFound[];\n /**\n * Надстройки, не подобранные ни к одному ресурсу пакета.\n *\n * Подбор идёт по идентификатору ресурса целиком, вместе с приставкой свойства: надстройка,\n * чьё имя приставку потеряло или носит чужую, не привязывается ни к чему, и раскладка\n * молча кладёт пакетный текст. Сверка этого не видит и видеть не может — она сравнивает\n * разложенное с тем, что собирает сама, а неподобранное в сборку не берёт: обе стороны\n * сходятся, и молчание читается как «всё применено».\n */\n readonly strayOverrides: readonly string[];\n readonly written: readonly string[];\n /**\n * Что запись объявления сделала с настройкой агента. `null` — раскладки не было: планирование\n * и сверка на диск не пишут вовсе.\n */\n readonly bound: IBindResult | null;\n}\n\n/** Шаблон черновика компаньона — тот же, что пакет кладёт проекту в шаблоны. */\nconst COMPANION_TEMPLATE: string = 'templates/implementation.md';\n\nconst read: (path: string) => string | null = (path: string): string | null => (existsSync(path) ? readFileSync(path, 'utf8') : null);\n\n/**\n * Тело ресурса: текст пакета, поверх него надстройка проекта, и уже потом подстановка значений.\n *\n * Порядок именно такой. Надстройка тоже пишется с дырками — иначе проект, дописавший раздел про\n * свою главную ветку, зашил бы её имя в двух местах: в конфиге и в тексте.\n */\n/**\n * Текст ресурса с наложенной надстройкой дерева — то, что ложится в дерево, без подстановки\n * значений.\n *\n * Экспортируется потому, что о разложенном тексте спрашивает не одна раскладка: счёт долга\n * привязок судит статьи компаньона против правила, и судить их по пакетной редакции значит\n * называть долгом статьи, которых в разложенном правиле нет вовсе.\n */\nexport function mergedBody(asset: IAsset, root: string): string {\n const override: string | null = read(join(root, OVERRIDES_DIR, asset.id));\n\n return override ? renderDocument(mergeDocuments(parseDocument(asset.text), parseDocument(override))) : asset.text;\n}\n\nfunction renderAsset(asset: IAsset, config: IConfig, root: string): IRenderResult {\n return renderVars(mergedBody(asset, root), config.vars);\n}\n\n/** Разложенный раньше файл ресурса, снятого теперь каскадом, и причина, по которой он снят. */\nexport interface ICutFound {\n /** Путь в дереве, где лежит файл. */\n readonly path: string;\n readonly cut: ICascadeCut;\n}\n\n/** Что дерево держит от ресурсов, которые больше не раскладываются. */\ninterface ILeftOnDisk {\n readonly abandoned: readonly string[];\n readonly cut: readonly ICutFound[];\n}\n\n/**\n * Что лежит в дереве от ресурсов, которые больше не раскладываются.\n *\n * Ищется по тем же правилам раскладки, что и всё остальное: путь ресурса вычисляется так, будто\n * его кладут, и проверяется, лежит ли там файл с шапкой пакета. Чужой файл на том же пути\n * брошенным не считается — его пакет не клал.\n *\n * Снятое каскадом отделяется здесь же: дерево от него не отказывалось, и, увидев его среди\n * брошенного, читатель пойдёт искать строку отказа, которой нет.\n */\nfunction leftOnDisk(config: IConfig, root: string, assetsDir: string): ILeftOnDisk {\n const taken: ReadonlySet<string> = new Set(collectAssets(config, assetsDir).map((asset: IAsset): string => asset.id));\n const catalog: readonly IEntryOfCatalog[] = readCatalog(assetsDir);\n const cuts: Map<string, ICascadeCut> = new Map(\n cascadeCuts(catalog, config).map((one: ICascadeCut): [string, ICascadeCut] => [one.id, one])\n );\n\n const abandoned: string[] = [];\n const cut: ICutFound[] = [];\n for (const entry of catalog) {\n const path: string = targetOf(entry, config.layout);\n const existing: string | null = taken.has(entry.id) ? null : read(join(root, path));\n const stamped: boolean = existing !== null && readStamped(existing) !== null;\n\n if (!stamped || !matchesVariant(entry.variant, config.variants)) {\n continue;\n }\n\n const found: ICascadeCut | undefined = cuts.get(entry.id);\n\n if (found) {\n cut.push({ path, cut: found });\n } else {\n abandoned.push(path);\n }\n }\n\n return { abandoned, cut };\n}\n\n/**\n * Что лежит в дереве от ресурсов, которых в наборе больше нет.\n *\n * Брошенным такой файл не назовёт никто: брошенное ищется по каталогу пакета, а снятого в\n * каталоге нет вовсе. Поэтому его ищут по списку снятых имён — там же, где записано, почему\n * ресурс ушёл.\n */\nexport interface IRetiredFound {\n /** Путь в дереве, где лежит файл снятого ресурса. */\n readonly path: string;\n /** Редакция пакета, в которой ресурс снят. */\n readonly since: string;\n readonly why: string;\n}\n\nfunction retiredOf(config: IConfig, root: string): readonly IRetiredFound[] {\n const found: IRetiredFound[] = [];\n\n for (const entry of RETIRED) {\n const path: string = targetOf(entry, config.layout);\n const existing: string | null = read(join(root, path));\n if (existing !== null && readStamped(existing) !== null) {\n found.push({ path, since: entry.since, why: entry.why });\n }\n }\n\n return found;\n}\n\n/**\n * Что о себе говорят гарды, которые дерево берёт. Читается у ресурсов, а не у разложенных\n * файлов: карта нужна и до первой раскладки — чтобы было что вставить в настройку.\n */\nfunction bindingsOf(config: IConfig, assetsDir: string): readonly IHookBinding[] {\n const bindings: IHookBinding[] = [];\n for (const asset of collectAssets(config, assetsDir)) {\n if (asset.kind !== 'hooks') {\n continue;\n }\n bindings.push(...declaredIn(asset.text, asset.target));\n }\n\n return bindings;\n}\n\n/**\n * Надстройки, которым в пакете ничего не отвечает.\n *\n * Ищутся по всему каталогу надстроек, а не по выбранным ресурсам: надстройка над ресурсом,\n * от которого дерево отказалось, названа своей строкой отказа, а эта — про имя, которого у\n * пакета нет вовсе.\n */\nfunction strayOverrides(root: string, assetsDir: string): string[] {\n const dir: string = join(root, OVERRIDES_DIR);\n if (!existsSync(dir)) {\n return [];\n }\n\n const known: ReadonlySet<string> = new Set(readCatalog(assetsDir).map((entry: IEntryOfCatalog): string => entry.id));\n const found: string[] = [];\n\n const walk: (at: string) => void = (at: string): void => {\n for (const entry of readdirSync(at, { withFileTypes: true })) {\n const path: string = join(at, entry.name);\n if (entry.isDirectory()) {\n walk(path);\n continue;\n }\n const id: string = relative(dir, path);\n // Файл вне рода ресурсов надстройкой не считается вовсе: рядом с ней законно лежит\n // README каталога, и звать его неприменённым значило бы шуметь на каждой раскладке.\n const kind: string = id.split('/')[0];\n if (KINDS.includes(kind as TKind) && !known.has(id)) {\n found.push(id);\n }\n }\n };\n\n walk(dir);\n\n return found.sort(byText);\n}\n\nexport function planSync(config: IConfig, root: string, version: string, assetsDir: string): ISyncResult {\n const planned: IPlanned[] = [];\n const missing: Map<string, readonly string[]> = new Map();\n const companions: ICompanion[] = [];\n const template: string | null = read(join(assetsDir, COMPANION_TEMPLATE));\n\n for (const asset of collectAssets(config, assetsDir)) {\n const rendered: IRenderResult = renderAsset(asset, config, root);\n if (rendered.missing.length) {\n missing.set(asset.id, rendered.missing);\n continue;\n }\n planned.push(\n planFile({ version, path: asset.target, asset: asset.id, rendered: rendered.text, existing: read(join(root, asset.target)) })\n );\n if (asset.kind === 'rules' && template !== null) {\n companions.push(planCompanion(asset, read(join(root, pathOf(asset))), template));\n }\n }\n\n const left: ILeftOnDisk = leftOnDisk(config, root, assetsDir);\n\n return {\n planned,\n missing,\n companions,\n abandoned: left.abandoned,\n gaps: variantGaps(readCatalog(assetsDir), config),\n unbound: unboundHooks(bindingsOf(config, assetsDir), root),\n drifted: driftedMatchers(bindingsOf(config, assetsDir), root),\n broken: brokenLinks(readCatalog(assetsDir), config),\n idle: idleSkips(readCatalog(assetsDir), config),\n cutOnDisk: left.cut,\n namedCut: namedButCut(readCatalog(assetsDir), config),\n retired: retiredOf(config, root),\n strayOverrides: strayOverrides(root, assetsDir),\n written: [],\n bound: null,\n };\n}\n\n/** Раскладка. Отказ хотя бы по одному файлу не пишет ничего: половина разложенного хуже целого. */\nexport function runSync(config: IConfig, root: string, version: string, assetsDir: string): ISyncResult {\n const result: ISyncResult = planSync(config, root, version, assetsDir);\n if (result.missing.size || result.gaps.length || result.planned.some((entry: IPlanned): boolean => isRefusal(entry.outcome))) {\n return result;\n }\n\n // Право на запуск переносится с файла в пакете. Без него гард отвечает отказом доступа, то\n // есть ненулевым кодом, а ненулевой код у гарда значит «правка отбита»: разложенный набор\n // отбивал бы подряд всё, включая сборку и тесты, и причина при этом нигде не называлась.\n const executable: ReadonlySet<string> = new Set(\n collectAssets(config, assetsDir)\n .filter((asset: IAsset): boolean => asset.executable)\n .map((asset: IAsset): string => asset.id)\n );\n\n const written: string[] = [];\n for (const entry of result.planned) {\n if (entry.content === null) {\n continue;\n }\n const path: string = join(root, entry.path);\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, entry.content, 'utf8');\n if (executable.has(entry.asset)) {\n chmodSync(path, 0o755);\n }\n written.push(entry.path);\n }\n\n // Черновик компаньона кладётся только там, где файла нет вовсе. Он без шапки и без суммы:\n // сверять в нём нечего — с первой правки проекта это его текст, а не текст пакета.\n for (const companion of result.companions) {\n if (companion.content === null) {\n continue;\n }\n const path: string = join(root, companion.path);\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, companion.content, 'utf8');\n written.push(companion.path);\n }\n\n // Объявление кладётся вместе с самим гардом, а не печатается просьбой вставить его рукой:\n // разложенный и никем не зовомый гард снаружи неотличим от работающего. Идёт это последним —\n // после того как файлы легли: запись, зовущая гард, которого на диске ещё нет, обещает\n // больше, чем стоит.\n const bound: IBindResult = bindDispatch(bindingsOf(config, assetsDir), root);\n\n return { ...result, written, bound };\n}\n\nexport const pendingOf: (result: ISyncResult) => readonly IPlanned[] = (result: ISyncResult): readonly IPlanned[] =>\n result.planned.filter((entry: IPlanned): boolean => isPending(entry.outcome));\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rt-tools/agent-kit",
3
- "version": "0.16.1",
3
+ "version": "0.18.0",
4
4
  "description": "Переносимый слой правил для агента: законы, хуки, проверки и агенты, раскладываемые в репозиторий одной командой",
5
5
  "author": "RT Team",
6
6
  "license": "Apache-2.0",
Binary file
Binary file