@rt-tools/agent-kit 0.8.1 → 0.8.2

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 (101) hide show
  1. package/README.md +24 -19
  2. package/assets/agents/qa-engineer.md +1 -1
  3. package/assets/checks/board.github.mjs +56 -17
  4. package/assets/checks/check-board.github.mjs +49 -5
  5. package/assets/checks/task-new.github.mjs +33 -5
  6. package/assets/commands/agent-kit-digest.md +10 -5
  7. package/assets/commands/next-session.md +4 -4
  8. package/assets/commands/skill-curator.md +11 -9
  9. package/assets/defaults/gate-map.sh +11 -4
  10. package/assets/defaults/project.sh +46 -0
  11. package/assets/docs/GLOSSARY.md +28 -26
  12. package/assets/hooks/docs-guard.sh +19 -3
  13. package/assets/hooks/git-guard-delivery.sh +106 -13
  14. package/assets/hooks/proposal-guard.sh +93 -0
  15. package/assets/hooks/reuse-first-guard.sh +68 -14
  16. package/assets/hooks/skill-gate-layers.sh +1 -1
  17. package/assets/hooks/skill-gate.sh +26 -0
  18. package/assets/hooks/task-flow-guard.sh +44 -18
  19. package/assets/hooks/window-fill-guard.sh +1 -1
  20. package/assets/laws/delivery.md +13 -7
  21. package/assets/laws/project-documentation.md +21 -0
  22. package/assets/laws/verifiability.md +6 -1
  23. package/assets/laws/work-conduct.md +67 -3
  24. package/assets/patterns/git-workflow-commit.azure.md +10 -2
  25. package/assets/patterns/git-workflow-commit.github.md +15 -2
  26. package/assets/patterns/git-workflow-commit.gitlab.md +10 -2
  27. package/assets/patterns/git-workflow-merge.md +1 -1
  28. package/assets/patterns/spec-driven-domain.md +7 -1
  29. package/assets/patterns/spec-driven-rule.md +6 -0
  30. package/assets/patterns/task-flow-close.md +78 -9
  31. package/assets/patterns/task-flow-handoff.md +27 -4
  32. package/assets/patterns/task-flow-resume.md +23 -5
  33. package/assets/patterns/task-flow-start.md +5 -1
  34. package/assets/rules/browser-verification.md +10 -1
  35. package/assets/rules/doc-style.md +13 -5
  36. package/assets/rules/git-workflow.azure.md +12 -7
  37. package/assets/rules/git-workflow.github.md +31 -13
  38. package/assets/rules/git-workflow.gitlab.md +12 -7
  39. package/assets/rules/spec-driven.md +4 -0
  40. package/assets/rules/task-flow.md +47 -22
  41. package/assets/rules/testing.md +19 -0
  42. package/assets/rules/typescript-conventions.md +12 -0
  43. package/assets/skills/agent-kit-extend.md +24 -0
  44. package/assets/skills/agent-kit.md +54 -6
  45. package/assets/traits.json +14 -0
  46. package/bin/agent-kit.d.ts.map +1 -1
  47. package/bin/agent-kit.js +31 -16
  48. package/bin/agent-kit.js.map +1 -1
  49. package/index.d.ts +1 -0
  50. package/index.d.ts.map +1 -1
  51. package/index.js +1 -0
  52. package/index.js.map +1 -1
  53. package/lib/argv.d.ts +17 -0
  54. package/lib/argv.d.ts.map +1 -0
  55. package/lib/argv.js +44 -0
  56. package/lib/argv.js.map +1 -0
  57. package/lib/cargo.d.ts +88 -0
  58. package/lib/cargo.d.ts.map +1 -0
  59. package/lib/cargo.js +16 -0
  60. package/lib/cargo.js.map +1 -0
  61. package/lib/catalog.d.ts +18 -1
  62. package/lib/catalog.d.ts.map +1 -1
  63. package/lib/catalog.js +12 -2
  64. package/lib/catalog.js.map +1 -1
  65. package/lib/commands.d.ts +0 -26
  66. package/lib/commands.d.ts.map +1 -1
  67. package/lib/commands.js +78 -122
  68. package/lib/commands.js.map +1 -1
  69. package/lib/companion.d.ts +37 -0
  70. package/lib/companion.d.ts.map +1 -1
  71. package/lib/companion.js +42 -1
  72. package/lib/companion.js.map +1 -1
  73. package/lib/config.d.ts +28 -0
  74. package/lib/config.d.ts.map +1 -1
  75. package/lib/config.js +20 -0
  76. package/lib/config.js.map +1 -1
  77. package/lib/ship.d.ts +39 -0
  78. package/lib/ship.d.ts.map +1 -0
  79. package/lib/ship.js +87 -0
  80. package/lib/ship.js.map +1 -0
  81. package/lib/shipment.d.ts +60 -0
  82. package/lib/shipment.d.ts.map +1 -0
  83. package/lib/shipment.js +247 -0
  84. package/lib/shipment.js.map +1 -0
  85. package/lib/snapshot.d.ts +30 -0
  86. package/lib/snapshot.d.ts.map +1 -0
  87. package/lib/snapshot.js +73 -0
  88. package/lib/snapshot.js.map +1 -0
  89. package/lib/traits.d.ts +32 -0
  90. package/lib/traits.d.ts.map +1 -0
  91. package/lib/traits.js +82 -0
  92. package/lib/traits.js.map +1 -0
  93. package/package.json +6 -2
  94. package/rt-tools-agent-kit-0.8.2.tgz +0 -0
  95. package/lib/submit.d.ts +0 -24
  96. package/lib/submit.d.ts.map +0 -1
  97. package/lib/submit.js +0 -26
  98. package/lib/submit.js.map +0 -1
  99. package/rt-tools-agent-kit-0.8.1.tgz +0 -0
  100. /package/assets/rules/{entity-conventions.md → entity-conventions.needs-admin.md} +0 -0
  101. /package/assets/rules/{observability.md → observability.needs-app.md} +0 -0
package/lib/config.d.ts CHANGED
@@ -39,17 +39,45 @@ export interface IConfig {
39
39
  * его значение; как именно — `isChosen`.
40
40
  */
41
41
  readonly variants: Readonly<Record<string, string>>;
42
+ /**
43
+ * Что у дерева есть: `["packages", "app"]`. Свойства объявлены пакетом в `assets/traits.json`,
44
+ * а ресурс, требующий свойства именем файла, дереву без него не кладётся вовсе — ни сам, ни
45
+ * черновик его компаньона. Пусто — дерево о себе ничего не сказало, и помеченного не получает.
46
+ */
47
+ readonly has: readonly string[];
42
48
  /**
43
49
  * Пишут ли гарды наблюдения о слое правил. Умолчание — да; выключает дерево, и целиком, а
44
50
  * не частями. Ключ читают двое: сводка — отсюда, гард — тем же именем прямо из файла, потому
45
51
  * что разборщика конфига у него нет.
46
52
  */
47
53
  readonly observe: boolean;
54
+ /**
55
+ * Адрес приёма, куда уезжает груз: `https://…`. Пусто — отправлять некуда, и отправка
56
+ * отказывает вместо молчания. Зашитый в код адрес назвал бы чужое дерево в текстах пакета.
57
+ */
58
+ readonly intake: string;
59
+ /**
60
+ * Файл с токеном дерева — путь от корня дерева или от домашнего каталога (`~/…`). Сам токен
61
+ * в конфиге не лежит: конфиг коммитится, а токен обязан остаться вне истории.
62
+ */
63
+ readonly token: string;
64
+ /**
65
+ * Признак дерева, когда удалённого репозитория нет. При нём — считается из адреса
66
+ * репозитория: две рабочие копии одного репозитория обязаны дать один признак.
67
+ */
68
+ readonly tree: string;
69
+ /** Где дерево держит разборы происшествий. Путь от корня дерева. */
70
+ readonly postmortems: string;
48
71
  }
49
72
  export declare const CONFIG_PATH: string;
50
73
  /** Каталог, в котором дерево держит своё при пакете: надстройки, умолчания, свою карту и профиль. */
51
74
  export declare const RT_KIT_DIR: string;
52
75
  export declare const OVERRIDES_DIR: string;
76
+ /**
77
+ * Где разборы происшествий лежат, когда конфиг не сказал иначе. Роды ресурсов сюда не годятся:
78
+ * разборы пишет дерево про себя, пакет их не раскладывает и не сверяет.
79
+ */
80
+ export declare const DEFAULT_POSTMORTEMS_DIR: string;
53
81
  /**
54
82
  * Карта «что правится — какое правило» и профиль дерева: команды, стенды, пары «правка —
55
83
  * документ». Оба лежат в двух видах, и это не дублирование, а разделение ответственности.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/config.ts"],"names":[],"mappings":"AAUA,2EAA2E;AAC3E,MAAM,MAAM,KAAK,GACX,MAAM,GACN,OAAO,GACP,UAAU,GACV,QAAQ,GACR,OAAO,GACP,UAAU,GACV,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,WAAW,GACX,WAAW;AACb,4EAA4E;GAC1E,MAAM,CAAC;AAEb,eAAO,MAAM,KAAK,EAAE,SAAS,KAAK,EAajC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,KAAK,EAAoC,CAAC;AAE7E,iFAAiF;AACjF,eAAO,MAAM,UAAU,EAAE,MAAmB,CAAC;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,MAA4B,CAAC;AAE1D,MAAM,WAAW,OAAO;IACpB,gCAAgC;IAChC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IACjD;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,2FAA2F;IAC3F,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC7B;AAED,eAAO,MAAM,WAAW,EAAE,MAA8B,CAAC;AACzD,qGAAqG;AACrG,eAAO,MAAM,UAAU,EAAE,MAAyB,CAAC;AACnD,eAAO,MAAM,aAAa,EAAE,MAAmC,CAAC;AAEhE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,EAAE,MAAkC,CAAC;AAC9D,eAAO,MAAM,aAAa,EAAE,MAAsB,CAAC;AACnD,eAAO,MAAM,YAAY,EAAE,MAAqB,CAAC;AAEjD;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAa1D,CAAC;AAEF,qBAAa,WAAY,SAAQ,KAAK;CAAG;AAgCzC,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAqCjD;AAED,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAUvD"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/config.ts"],"names":[],"mappings":"AAUA,2EAA2E;AAC3E,MAAM,MAAM,KAAK,GACX,MAAM,GACN,OAAO,GACP,UAAU,GACV,QAAQ,GACR,OAAO,GACP,UAAU,GACV,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,WAAW,GACX,WAAW;AACb,4EAA4E;GAC1E,MAAM,CAAC;AAEb,eAAO,MAAM,KAAK,EAAE,SAAS,KAAK,EAajC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,KAAK,EAAoC,CAAC;AAE7E,iFAAiF;AACjF,eAAO,MAAM,UAAU,EAAE,MAAmB,CAAC;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,MAA4B,CAAC;AAE1D,MAAM,WAAW,OAAO;IACpB,gCAAgC;IAChC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IACjD;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,2FAA2F;IAC3F,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAChC;AAED,eAAO,MAAM,WAAW,EAAE,MAA8B,CAAC;AACzD,qGAAqG;AACrG,eAAO,MAAM,UAAU,EAAE,MAAyB,CAAC;AACnD,eAAO,MAAM,aAAa,EAAE,MAAmC,CAAC;AAEhE;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAA2B,CAAC;AAElE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,EAAE,MAAkC,CAAC;AAC9D,eAAO,MAAM,aAAa,EAAE,MAAsB,CAAC;AACnD,eAAO,MAAM,YAAY,EAAE,MAAqB,CAAC;AAEjD;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAa1D,CAAC;AAEF,qBAAa,WAAY,SAAQ,KAAK;CAAG;AA4CzC,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CA0CjD;AAED,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAUvD"}
package/lib/config.js CHANGED
@@ -43,6 +43,11 @@ export const CONFIG_PATH = '.claude/rt-kit.json';
43
43
  /** Каталог, в котором дерево держит своё при пакете: надстройки, умолчания, свою карту и профиль. */
44
44
  export const RT_KIT_DIR = '.claude/rt-kit';
45
45
  export const OVERRIDES_DIR = '.claude/rt-kit/overrides';
46
+ /**
47
+ * Где разборы происшествий лежат, когда конфиг не сказал иначе. Роды ресурсов сюда не годятся:
48
+ * разборы пишет дерево про себя, пакет их не раскладывает и не сверяет.
49
+ */
50
+ export const DEFAULT_POSTMORTEMS_DIR = 'docs/postmortems';
46
51
  /**
47
52
  * Карта «что правится — какое правило» и профиль дерева: команды, стенды, пары «правка —
48
53
  * документ». Оба лежат в двух видах, и это не дублирование, а разделение ответственности.
@@ -97,6 +102,16 @@ function stringMap(value, where) {
97
102
  }
98
103
  return result;
99
104
  }
105
+ /** Строковый ключ конфига. Ключа нет — пустая строка: значение необязательное у всех четырёх. */
106
+ function textOf(value, where) {
107
+ if (value === undefined || value === null) {
108
+ return '';
109
+ }
110
+ if (typeof value !== 'string') {
111
+ throw new ConfigError(`${where}: ожидается строка`);
112
+ }
113
+ return value.trim();
114
+ }
100
115
  function idList(value, where) {
101
116
  const list = value ?? [];
102
117
  if (!Array.isArray(list) || list.some((entry) => typeof entry !== 'string')) {
@@ -135,8 +150,13 @@ export function parseConfig(text) {
135
150
  only,
136
151
  skip,
137
152
  variants: stringMap(raw['variants'], 'variants'),
153
+ has: idList(raw['has'], 'has'),
138
154
  // Выключателем считается только явное «нет»: ключа нет — запись идёт, как и у гарда.
139
155
  observe: raw['observe'] !== false,
156
+ intake: textOf(raw['intake'], 'intake'),
157
+ token: textOf(raw['token'], 'token'),
158
+ tree: textOf(raw['tree'], 'tree'),
159
+ postmortems: textOf(raw['postmortems'], 'postmortems') || DEFAULT_POSTMORTEMS_DIR,
140
160
  };
141
161
  }
142
162
  /** Конфиг проекта; его отсутствие — не отказ, а повод сказать про `agent-kit init`. */
package/lib/config.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/config.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Конфиг проекта — `.claude/rt-kit.json`.\n *\n * Всё, что пакет не может знать сам: значения для дырок, куда класть каждый род ресурса и от\n * чего проект отказался. Конфиг лежит в дереве проекта и коммитится: раскладка обязана\n * повторяться на чужой машине без вопросов, иначе разложенное расходится между разработчиками.\n */\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/** Род ресурса. Он же имя каталога в `assets/` пакета и ключ раскладки. */\nexport type TKind =\n | 'laws'\n | 'rules'\n | 'patterns'\n | 'skills'\n | 'hooks'\n | 'defaults'\n | 'checks'\n | 'agents'\n | 'commands'\n | 'workflows'\n | 'templates'\n /** Документы дерева, которые читает и человек, и агент: словарь проекта. */\n | 'docs';\n\nexport const KINDS: readonly TKind[] = [\n 'laws',\n 'rules',\n 'patterns',\n 'skills',\n 'hooks',\n 'defaults',\n 'checks',\n 'agents',\n 'commands',\n 'workflows',\n 'templates',\n 'docs',\n];\n\n/**\n * Роды, которые ложатся не файлом в каталог, а каталогом по имени ресурса: скил читается как\n * `<имя>/SKILL.md`, и рядом с ним лежит то, что пишет проект.\n *\n * `skills` — скил, у которого нет закона над собой: он не про то, что должно быть верно в\n * продукте, а про то, как здесь делается работа. Правилом его называть нельзя — тогда придётся\n * выдумать ему закон, — а паттерном тем более: паттерн стоит при правиле.\n */\nexport const SKILL_KINDS: readonly TKind[] = ['rules', 'patterns', 'skills'];\n\n/** Имя файла скила: так его ищет агент, и другого имени у него быть не может. */\nexport const SKILL_FILE: string = 'SKILL.md';\n\n/**\n * Файл-компаньон правила: чем названо в этом дереве то, о чём правило говорит приёмом.\n *\n * Пакет знает приём, но не знает ни путей, ни имён — их пишет проект. Черновик кладётся один\n * раз и дальше не сверяется: это единственный файл раскладки, который принадлежит проекту.\n */\nexport const COMPANION_FILE: string = 'implementation.md';\n\nexport interface IConfig {\n /** Значения дырок `{{имя}}`. */\n readonly vars: Readonly<Record<string, string>>;\n /** Куда класть каждый род ресурса, путями от корня проекта. */\n readonly layout: Readonly<Record<TKind, string>>;\n /**\n * Ресурсы, которые проект выбрал, идентификаторами: `laws/access.md`. Пусто — берётся всё.\n * Ограничивает только те роды, которые сам называет; как именно — `isChosen`.\n */\n readonly only: readonly string[];\n /** Ресурсы, от которых проект отказался, идентификаторами: `laws/application/money.md`. */\n readonly skip: readonly string[];\n /**\n * Выбор проекта по осям различия: `{ \"host\": \"gitlab\" }`. Оси объявлены пакетом в\n * `assets/variants.json`, а ресурс с вариантом в имени берётся, только если проект выбрал\n * его значение; как именно — `isChosen`.\n */\n readonly variants: Readonly<Record<string, string>>;\n /**\n * Пишут ли гарды наблюдения о слое правил. Умолчание — да; выключает дерево, и целиком, а\n * не частями. Ключ читают двое: сводка — отсюда, гард — тем же именем прямо из файла, потому\n * что разборщика конфига у него нет.\n */\n readonly observe: boolean;\n}\n\nexport const CONFIG_PATH: string = '.claude/rt-kit.json';\n/** Каталог, в котором дерево держит своё при пакете: надстройки, умолчания, свою карту и профиль. */\nexport const RT_KIT_DIR: string = '.claude/rt-kit';\nexport const OVERRIDES_DIR: string = '.claude/rt-kit/overrides';\n\n/**\n * Карта «что правится — какое правило» и профиль дерева: команды, стенды, пары «правка —\n * документ». Оба лежат в двух видах, и это не дублирование, а разделение ответственности.\n *\n * Умолчание везёт пакет: проекты этой мастерской устроены одинаково — Nx, те же расширения, те\n * же имена каталогов, — и переписывать одну и ту же карту в каждом дереве заново значило бы\n * заводить пятнадцать её редакций, расходящихся молча. Оно разложено, под шапкой, и правится\n * не на месте.\n *\n * Надстройку пишет проект, и она необязательна: витрина, свой род файлов, другой запускатель\n * тестов есть не у всех. Объявленная в ней функция замещает умолчание целиком и вправе позвать\n * его обратно суффиксом `_default` — так дерево дописывает своё, не теряя общего.\n */\nexport const DEFAULTS_DIR: string = '.claude/rt-kit/defaults';\nexport const GATE_MAP_FILE: string = 'gate-map.sh';\nexport const PROFILE_FILE: string = 'project.sh';\n\n/**\n * Умолчания раскладки. Это не единственно возможные пути, но менять их без нужды не стоит:\n * агент ищет законы в `docs/constitution`, потому что так написано в правилах, которые пакет\n * же и везёт.\n */\nexport const DEFAULT_LAYOUT: Readonly<Record<TKind, string>> = {\n laws: 'docs/constitution',\n rules: '.claude/skills',\n patterns: '.claude/skills',\n skills: '.claude/skills',\n hooks: '.claude/hooks',\n defaults: DEFAULTS_DIR,\n checks: 'tools',\n agents: '.claude/agents',\n commands: '.claude/commands',\n workflows: '.claude/workflows',\n templates: '.claude/rt-kit/templates',\n docs: 'docs',\n};\n\nexport class ConfigError extends Error {}\n\nconst isRecord: (value: unknown) => value is Record<string, unknown> = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nfunction stringMap(value: unknown, where: string): Record<string, string> {\n if (value === undefined) {\n return {};\n }\n if (!isRecord(value)) {\n throw new ConfigError(`${where}: ожидается объект «имя — значение»`);\n }\n const result: Record<string, string> = {};\n for (const [key, entry] of Object.entries(value)) {\n if (typeof entry !== 'string') {\n throw new ConfigError(`${where}.${key}: ожидается строка`);\n }\n result[key] = entry;\n }\n\n return result;\n}\n\nfunction idList(value: unknown, where: string): readonly string[] {\n const list: unknown = value ?? [];\n if (!Array.isArray(list) || list.some((entry: unknown): boolean => typeof entry !== 'string')) {\n throw new ConfigError(`${where}: ожидается список идентификаторов ресурсов`);\n }\n\n return list as string[];\n}\n\nexport function parseConfig(text: string): IConfig {\n let raw: unknown;\n try {\n raw = JSON.parse(text);\n } catch (error: unknown) {\n throw new ConfigError(`${CONFIG_PATH} — не разбирается как JSON: ${(error as Error).message}`);\n }\n if (!isRecord(raw)) {\n throw new ConfigError(`${CONFIG_PATH}: ожидается объект`);\n }\n\n const layout: Record<string, string> = { ...DEFAULT_LAYOUT, ...stringMap(raw['layout'], 'layout') };\n for (const kind of Object.keys(layout)) {\n if (!KINDS.includes(kind as TKind)) {\n throw new ConfigError(`layout.${kind}: такого рода ресурсов нет — есть ${KINDS.join(', ')}`);\n }\n }\n\n const only: readonly string[] = idList(raw['only'], 'only');\n const skip: readonly string[] = idList(raw['skip'], 'skip');\n\n // Пути раскладки — тоже значения проекта, и пакет их уже знает. Требовать их вторым списком\n // значило бы держать одно и то же в двух местах: расходятся такие пары молча.\n const derived: Record<string, string> = {};\n for (const kind of KINDS) {\n derived[`${kind.replace(/s$/, '')}sDir`] = layout[kind];\n }\n\n return {\n vars: { ...derived, ...stringMap(raw['vars'], 'vars') },\n layout: layout as Record<TKind, string>,\n only,\n skip,\n variants: stringMap(raw['variants'], 'variants'),\n // Выключателем считается только явное «нет»: ключа нет — запись идёт, как и у гарда.\n observe: raw['observe'] !== false,\n };\n}\n\n/** Конфиг проекта; его отсутствие — не отказ, а повод сказать про `agent-kit init`. */\nexport function readConfig(root: string): IConfig | null {\n try {\n return parseConfig(readFileSync(join(root, CONFIG_PATH), 'utf8'));\n } catch (error: unknown) {\n if (error instanceof ConfigError) {\n throw error;\n }\n\n return null;\n }\n}\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/config.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Конфиг проекта — `.claude/rt-kit.json`.\n *\n * Всё, что пакет не может знать сам: значения для дырок, куда класть каждый род ресурса и от\n * чего проект отказался. Конфиг лежит в дереве проекта и коммитится: раскладка обязана\n * повторяться на чужой машине без вопросов, иначе разложенное расходится между разработчиками.\n */\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/** Род ресурса. Он же имя каталога в `assets/` пакета и ключ раскладки. */\nexport type TKind =\n | 'laws'\n | 'rules'\n | 'patterns'\n | 'skills'\n | 'hooks'\n | 'defaults'\n | 'checks'\n | 'agents'\n | 'commands'\n | 'workflows'\n | 'templates'\n /** Документы дерева, которые читает и человек, и агент: словарь проекта. */\n | 'docs';\n\nexport const KINDS: readonly TKind[] = [\n 'laws',\n 'rules',\n 'patterns',\n 'skills',\n 'hooks',\n 'defaults',\n 'checks',\n 'agents',\n 'commands',\n 'workflows',\n 'templates',\n 'docs',\n];\n\n/**\n * Роды, которые ложатся не файлом в каталог, а каталогом по имени ресурса: скил читается как\n * `<имя>/SKILL.md`, и рядом с ним лежит то, что пишет проект.\n *\n * `skills` — скил, у которого нет закона над собой: он не про то, что должно быть верно в\n * продукте, а про то, как здесь делается работа. Правилом его называть нельзя — тогда придётся\n * выдумать ему закон, — а паттерном тем более: паттерн стоит при правиле.\n */\nexport const SKILL_KINDS: readonly TKind[] = ['rules', 'patterns', 'skills'];\n\n/** Имя файла скила: так его ищет агент, и другого имени у него быть не может. */\nexport const SKILL_FILE: string = 'SKILL.md';\n\n/**\n * Файл-компаньон правила: чем названо в этом дереве то, о чём правило говорит приёмом.\n *\n * Пакет знает приём, но не знает ни путей, ни имён — их пишет проект. Черновик кладётся один\n * раз и дальше не сверяется: это единственный файл раскладки, который принадлежит проекту.\n */\nexport const COMPANION_FILE: string = 'implementation.md';\n\nexport interface IConfig {\n /** Значения дырок `{{имя}}`. */\n readonly vars: Readonly<Record<string, string>>;\n /** Куда класть каждый род ресурса, путями от корня проекта. */\n readonly layout: Readonly<Record<TKind, string>>;\n /**\n * Ресурсы, которые проект выбрал, идентификаторами: `laws/access.md`. Пусто — берётся всё.\n * Ограничивает только те роды, которые сам называет; как именно — `isChosen`.\n */\n readonly only: readonly string[];\n /** Ресурсы, от которых проект отказался, идентификаторами: `laws/application/money.md`. */\n readonly skip: readonly string[];\n /**\n * Выбор проекта по осям различия: `{ \"host\": \"gitlab\" }`. Оси объявлены пакетом в\n * `assets/variants.json`, а ресурс с вариантом в имени берётся, только если проект выбрал\n * его значение; как именно — `isChosen`.\n */\n readonly variants: Readonly<Record<string, string>>;\n /**\n * Что у дерева есть: `[\"packages\", \"app\"]`. Свойства объявлены пакетом в `assets/traits.json`,\n * а ресурс, требующий свойства именем файла, дереву без него не кладётся вовсе — ни сам, ни\n * черновик его компаньона. Пусто — дерево о себе ничего не сказало, и помеченного не получает.\n */\n readonly has: readonly string[];\n /**\n * Пишут ли гарды наблюдения о слое правил. Умолчание — да; выключает дерево, и целиком, а\n * не частями. Ключ читают двое: сводка — отсюда, гард — тем же именем прямо из файла, потому\n * что разборщика конфига у него нет.\n */\n readonly observe: boolean;\n /**\n * Адрес приёма, куда уезжает груз: `https://…`. Пусто — отправлять некуда, и отправка\n * отказывает вместо молчания. Зашитый в код адрес назвал бы чужое дерево в текстах пакета.\n */\n readonly intake: string;\n /**\n * Файл с токеном дерева — путь от корня дерева или от домашнего каталога (`~/…`). Сам токен\n * в конфиге не лежит: конфиг коммитится, а токен обязан остаться вне истории.\n */\n readonly token: string;\n /**\n * Признак дерева, когда удалённого репозитория нет. При нём — считается из адреса\n * репозитория: две рабочие копии одного репозитория обязаны дать один признак.\n */\n readonly tree: string;\n /** Где дерево держит разборы происшествий. Путь от корня дерева. */\n readonly postmortems: string;\n}\n\nexport const CONFIG_PATH: string = '.claude/rt-kit.json';\n/** Каталог, в котором дерево держит своё при пакете: надстройки, умолчания, свою карту и профиль. */\nexport const RT_KIT_DIR: string = '.claude/rt-kit';\nexport const OVERRIDES_DIR: string = '.claude/rt-kit/overrides';\n\n/**\n * Где разборы происшествий лежат, когда конфиг не сказал иначе. Роды ресурсов сюда не годятся:\n * разборы пишет дерево про себя, пакет их не раскладывает и не сверяет.\n */\nexport const DEFAULT_POSTMORTEMS_DIR: string = 'docs/postmortems';\n\n/**\n * Карта «что правится — какое правило» и профиль дерева: команды, стенды, пары «правка —\n * документ». Оба лежат в двух видах, и это не дублирование, а разделение ответственности.\n *\n * Умолчание везёт пакет: проекты этой мастерской устроены одинаково — Nx, те же расширения, те\n * же имена каталогов, — и переписывать одну и ту же карту в каждом дереве заново значило бы\n * заводить пятнадцать её редакций, расходящихся молча. Оно разложено, под шапкой, и правится\n * не на месте.\n *\n * Надстройку пишет проект, и она необязательна: витрина, свой род файлов, другой запускатель\n * тестов есть не у всех. Объявленная в ней функция замещает умолчание целиком и вправе позвать\n * его обратно суффиксом `_default` — так дерево дописывает своё, не теряя общего.\n */\nexport const DEFAULTS_DIR: string = '.claude/rt-kit/defaults';\nexport const GATE_MAP_FILE: string = 'gate-map.sh';\nexport const PROFILE_FILE: string = 'project.sh';\n\n/**\n * Умолчания раскладки. Это не единственно возможные пути, но менять их без нужды не стоит:\n * агент ищет законы в `docs/constitution`, потому что так написано в правилах, которые пакет\n * же и везёт.\n */\nexport const DEFAULT_LAYOUT: Readonly<Record<TKind, string>> = {\n laws: 'docs/constitution',\n rules: '.claude/skills',\n patterns: '.claude/skills',\n skills: '.claude/skills',\n hooks: '.claude/hooks',\n defaults: DEFAULTS_DIR,\n checks: 'tools',\n agents: '.claude/agents',\n commands: '.claude/commands',\n workflows: '.claude/workflows',\n templates: '.claude/rt-kit/templates',\n docs: 'docs',\n};\n\nexport class ConfigError extends Error {}\n\nconst isRecord: (value: unknown) => value is Record<string, unknown> = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nfunction stringMap(value: unknown, where: string): Record<string, string> {\n if (value === undefined) {\n return {};\n }\n if (!isRecord(value)) {\n throw new ConfigError(`${where}: ожидается объект «имя — значение»`);\n }\n const result: Record<string, string> = {};\n for (const [key, entry] of Object.entries(value)) {\n if (typeof entry !== 'string') {\n throw new ConfigError(`${where}.${key}: ожидается строка`);\n }\n result[key] = entry;\n }\n\n return result;\n}\n\n/** Строковый ключ конфига. Ключа нет — пустая строка: значение необязательное у всех четырёх. */\nfunction textOf(value: unknown, where: string): string {\n if (value === undefined || value === null) {\n return '';\n }\n if (typeof value !== 'string') {\n throw new ConfigError(`${where}: ожидается строка`);\n }\n\n return value.trim();\n}\n\nfunction idList(value: unknown, where: string): readonly string[] {\n const list: unknown = value ?? [];\n if (!Array.isArray(list) || list.some((entry: unknown): boolean => typeof entry !== 'string')) {\n throw new ConfigError(`${where}: ожидается список идентификаторов ресурсов`);\n }\n\n return list as string[];\n}\n\nexport function parseConfig(text: string): IConfig {\n let raw: unknown;\n try {\n raw = JSON.parse(text);\n } catch (error: unknown) {\n throw new ConfigError(`${CONFIG_PATH} — не разбирается как JSON: ${(error as Error).message}`);\n }\n if (!isRecord(raw)) {\n throw new ConfigError(`${CONFIG_PATH}: ожидается объект`);\n }\n\n const layout: Record<string, string> = { ...DEFAULT_LAYOUT, ...stringMap(raw['layout'], 'layout') };\n for (const kind of Object.keys(layout)) {\n if (!KINDS.includes(kind as TKind)) {\n throw new ConfigError(`layout.${kind}: такого рода ресурсов нет — есть ${KINDS.join(', ')}`);\n }\n }\n\n const only: readonly string[] = idList(raw['only'], 'only');\n const skip: readonly string[] = idList(raw['skip'], 'skip');\n\n // Пути раскладки — тоже значения проекта, и пакет их уже знает. Требовать их вторым списком\n // значило бы держать одно и то же в двух местах: расходятся такие пары молча.\n const derived: Record<string, string> = {};\n for (const kind of KINDS) {\n derived[`${kind.replace(/s$/, '')}sDir`] = layout[kind];\n }\n\n return {\n vars: { ...derived, ...stringMap(raw['vars'], 'vars') },\n layout: layout as Record<TKind, string>,\n only,\n skip,\n variants: stringMap(raw['variants'], 'variants'),\n has: idList(raw['has'], 'has'),\n // Выключателем считается только явное «нет»: ключа нет — запись идёт, как и у гарда.\n observe: raw['observe'] !== false,\n intake: textOf(raw['intake'], 'intake'),\n token: textOf(raw['token'], 'token'),\n tree: textOf(raw['tree'], 'tree'),\n postmortems: textOf(raw['postmortems'], 'postmortems') || DEFAULT_POSTMORTEMS_DIR,\n };\n}\n\n/** Конфиг проекта; его отсутствие — не отказ, а повод сказать про `agent-kit init`. */\nexport function readConfig(root: string): IConfig | null {\n try {\n return parseConfig(readFileSync(join(root, CONFIG_PATH), 'utf8'));\n } catch (error: unknown) {\n if (error instanceof ConfigError) {\n throw error;\n }\n\n return null;\n }\n}\n"]}
package/lib/ship.d.ts ADDED
@@ -0,0 +1,39 @@
1
+ import { IIntakeAccepted } from './cargo.js';
2
+ /**
3
+ * Предел ожидания приёма. Без него молчащая — не отказавшая — служба держит отправку до
4
+ * умолчания среды, а это минуты: человек, позвавший команду, видит не отказ, а зависший запуск.
5
+ */
6
+ export declare const SHIP_TIMEOUT_MS: number;
7
+ /** Что уезжает одним запросом: род груза словом, операция приёма и тело. */
8
+ export interface IShipment {
9
+ /** Род груза словами — его называет отказ: дерево шлёт три рода и должно знать, какой отбит. */
10
+ readonly kind: string;
11
+ /** Последнее звено адреса операции: `summary`, `proposals`, `postmortems`. */
12
+ readonly operation: string;
13
+ readonly body: unknown;
14
+ }
15
+ /** Чем кончился один запрос. Отказ — такой же ответ, как принятое: его печатает вызывающий. */
16
+ export interface IShipped {
17
+ readonly ok: boolean;
18
+ /** Код ответа. Ноль — приём не ответил вовсе: не дозвонились, оборвалось, вышло время. */
19
+ readonly status: number;
20
+ /** Что сказал приём. У неотвеченного запроса — причина, по которой он не состоялся. */
21
+ readonly said: string;
22
+ /** Дерево и месяц записи. Пусто у отказа: приём их называет только у принятого груза. */
23
+ readonly accepted: IIntakeAccepted | null;
24
+ }
25
+ /** Чем груз уезжает. Двойник в спеке — того же вида. */
26
+ export type TShip = (intake: string, token: string, shipment: IShipment) => Promise<IShipped>;
27
+ /** Адрес операции приёма. Косая черта в конце адреса приёма второй не становится. */
28
+ export declare function intakeUrl(intake: string, operation: string): string;
29
+ /**
30
+ * Токен дерева из файла, названного настройкой.
31
+ *
32
+ * Путь читается и от корня дерева, и от домашнего каталога: токен обязан лежать вне дерева —
33
+ * конфиг коммитится, а токен переживать историю не должен. Файла нет — пустая строка, и отказ
34
+ * об этом скажет словами.
35
+ */
36
+ export declare function readToken(root: string, spoken: string): string;
37
+ /** Запрос приёма. Токен уезжает заголовком и в теле не появляется ни разу. */
38
+ export declare const httpShip: TShip;
39
+ //# sourceMappingURL=ship.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ship.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/ship.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,eAAe,EAAqB,MAAM,YAAY,CAAC;AAEhE;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,MAAe,CAAC;AAE9C,4EAA4E;AAC5E,MAAM,WAAW,SAAS;IACtB,gGAAgG;IAChG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CAC1B;AAED,+FAA+F;AAC/F,MAAM,WAAW,QAAQ;IACrB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,0FAA0F;IAC1F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,uFAAuF;IACvF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;CAC7C;AAED,wDAAwD;AACxD,MAAM,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAE9F,qFAAqF;AACrF,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQ9D;AAqCD,8EAA8E;AAC9E,eAAO,MAAM,QAAQ,EAAE,KAiBtB,CAAC"}
package/lib/ship.js ADDED
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Отправка груза в приём: единственное место пакета, которое ходит в сеть.
3
+ *
4
+ * Ходит оно только по команде человека — ни один гард в сеть не ходит вовсе. Вынесено отдельно,
5
+ * чтобы сборка груза осталась проверяемой: спека подставляет двойник и никуда не стучится.
6
+ *
7
+ * Груз уезжает в закрытый приём, а не в открытую очередь работ: сводка говорит о рабочих
8
+ * привычках команды — чем пользуются, обо что спотыкаются, сколько раз признавали промах, — и в
9
+ * открытой очереди это выложено всему свету.
10
+ */
11
+ import { existsSync, readFileSync } from 'node:fs';
12
+ import { homedir } from 'node:os';
13
+ import { join } from 'node:path';
14
+ import { TREE_TOKEN_HEADER } from './cargo.js';
15
+ /**
16
+ * Предел ожидания приёма. Без него молчащая — не отказавшая — служба держит отправку до
17
+ * умолчания среды, а это минуты: человек, позвавший команду, видит не отказ, а зависший запуск.
18
+ */
19
+ export const SHIP_TIMEOUT_MS = 15_000;
20
+ /** Адрес операции приёма. Косая черта в конце адреса приёма второй не становится. */
21
+ export function intakeUrl(intake, operation) {
22
+ return `${intake.replace(/\/+$/, '')}/api/intake/${operation}`;
23
+ }
24
+ /**
25
+ * Токен дерева из файла, названного настройкой.
26
+ *
27
+ * Путь читается и от корня дерева, и от домашнего каталога: токен обязан лежать вне дерева —
28
+ * конфиг коммитится, а токен переживать историю не должен. Файла нет — пустая строка, и отказ
29
+ * об этом скажет словами.
30
+ */
31
+ export function readToken(root, spoken) {
32
+ if (!spoken) {
33
+ return '';
34
+ }
35
+ const path = spoken.startsWith('~/') ? join(homedir(), spoken.slice(2)) : join(root, spoken);
36
+ return existsSync(path) ? readFileSync(path, 'utf8').trim() : '';
37
+ }
38
+ /** Число из ответа приёма. Не число — пусто: поля есть только у предложений и не у всякого приёма. */
39
+ function countOf(said, field) {
40
+ return typeof said[field] === 'number' ? said[field] : undefined;
41
+ }
42
+ /** Дерево и месяц из ответа приёма. Ответ не тем — пусто: своё устройство приём не пересказывает. */
43
+ function acceptedOf(text) {
44
+ try {
45
+ const said = JSON.parse(text);
46
+ return typeof said['tree'] === 'string' && typeof said['month'] === 'string'
47
+ ? {
48
+ tree: said['tree'],
49
+ month: said['month'],
50
+ created: said['created'] === true,
51
+ added: countOf(said, 'added'),
52
+ known: countOf(said, 'known'),
53
+ }
54
+ : null;
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ }
60
+ /** Что сказал приём: его собственное сообщение, а если тело не разбирается — тело как приехало. */
61
+ function saidOf(text) {
62
+ try {
63
+ const said = JSON.parse(text);
64
+ return typeof said['message'] === 'string' ? said['message'] : text.trim();
65
+ }
66
+ catch {
67
+ return text.trim();
68
+ }
69
+ }
70
+ /** Запрос приёма. Токен уезжает заголовком и в теле не появляется ни разу. */
71
+ export const httpShip = async (intake, token, shipment) => {
72
+ let answer;
73
+ try {
74
+ answer = await fetch(intakeUrl(intake, shipment.operation), {
75
+ method: 'POST',
76
+ headers: { 'content-type': 'application/json', [TREE_TOKEN_HEADER]: token },
77
+ body: JSON.stringify(shipment.body),
78
+ signal: AbortSignal.timeout(SHIP_TIMEOUT_MS),
79
+ });
80
+ }
81
+ catch (error) {
82
+ return { ok: false, status: 0, said: error.message, accepted: null };
83
+ }
84
+ const text = await answer.text();
85
+ return { ok: answer.ok, status: answer.status, said: saidOf(text), accepted: answer.ok ? acceptedOf(text) : null };
86
+ };
87
+ //# sourceMappingURL=ship.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ship.js","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/ship.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Отправка груза в приём: единственное место пакета, которое ходит в сеть.\n *\n * Ходит оно только по команде человека — ни один гард в сеть не ходит вовсе. Вынесено отдельно,\n * чтобы сборка груза осталась проверяемой: спека подставляет двойник и никуда не стучится.\n *\n * Груз уезжает в закрытый приём, а не в открытую очередь работ: сводка говорит о рабочих\n * привычках команды — чем пользуются, обо что спотыкаются, сколько раз признавали промах, — и в\n * открытой очереди это выложено всему свету.\n */\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { IIntakeAccepted, TREE_TOKEN_HEADER } from './cargo.js';\n\n/**\n * Предел ожидания приёма. Без него молчащая — не отказавшая — служба держит отправку до\n * умолчания среды, а это минуты: человек, позвавший команду, видит не отказ, а зависший запуск.\n */\nexport const SHIP_TIMEOUT_MS: number = 15_000;\n\n/** Что уезжает одним запросом: род груза словом, операция приёма и тело. */\nexport interface IShipment {\n /** Род груза словами — его называет отказ: дерево шлёт три рода и должно знать, какой отбит. */\n readonly kind: string;\n /** Последнее звено адреса операции: `summary`, `proposals`, `postmortems`. */\n readonly operation: string;\n readonly body: unknown;\n}\n\n/** Чем кончился один запрос. Отказ — такой же ответ, как принятое: его печатает вызывающий. */\nexport interface IShipped {\n readonly ok: boolean;\n /** Код ответа. Ноль — приём не ответил вовсе: не дозвонились, оборвалось, вышло время. */\n readonly status: number;\n /** Что сказал приём. У неотвеченного запроса — причина, по которой он не состоялся. */\n readonly said: string;\n /** Дерево и месяц записи. Пусто у отказа: приём их называет только у принятого груза. */\n readonly accepted: IIntakeAccepted | null;\n}\n\n/** Чем груз уезжает. Двойник в спеке — того же вида. */\nexport type TShip = (intake: string, token: string, shipment: IShipment) => Promise<IShipped>;\n\n/** Адрес операции приёма. Косая черта в конце адреса приёма второй не становится. */\nexport function intakeUrl(intake: string, operation: string): string {\n return `${intake.replace(/\\/+$/, '')}/api/intake/${operation}`;\n}\n\n/**\n * Токен дерева из файла, названного настройкой.\n *\n * Путь читается и от корня дерева, и от домашнего каталога: токен обязан лежать вне дерева —\n * конфиг коммитится, а токен переживать историю не должен. Файла нет — пустая строка, и отказ\n * об этом скажет словами.\n */\nexport function readToken(root: string, spoken: string): string {\n if (!spoken) {\n return '';\n }\n\n const path: string = spoken.startsWith('~/') ? join(homedir(), spoken.slice(2)) : join(root, spoken);\n\n return existsSync(path) ? readFileSync(path, 'utf8').trim() : '';\n}\n\n/** Число из ответа приёма. Не число — пусто: поля есть только у предложений и не у всякого приёма. */\nfunction countOf(said: Record<string, unknown>, field: string): number | undefined {\n return typeof said[field] === 'number' ? said[field] : undefined;\n}\n\n/** Дерево и месяц из ответа приёма. Ответ не тем — пусто: своё устройство приём не пересказывает. */\nfunction acceptedOf(text: string): IIntakeAccepted | null {\n try {\n const said: Record<string, unknown> = JSON.parse(text) as Record<string, unknown>;\n\n return typeof said['tree'] === 'string' && typeof said['month'] === 'string'\n ? {\n tree: said['tree'],\n month: said['month'],\n created: said['created'] === true,\n added: countOf(said, 'added'),\n known: countOf(said, 'known'),\n }\n : null;\n } catch {\n return null;\n }\n}\n\n/** Что сказал приём: его собственное сообщение, а если тело не разбирается — тело как приехало. */\nfunction saidOf(text: string): string {\n try {\n const said: Record<string, unknown> = JSON.parse(text) as Record<string, unknown>;\n\n return typeof said['message'] === 'string' ? said['message'] : text.trim();\n } catch {\n return text.trim();\n }\n}\n\n/** Запрос приёма. Токен уезжает заголовком и в теле не появляется ни разу. */\nexport const httpShip: TShip = async (intake: string, token: string, shipment: IShipment): Promise<IShipped> => {\n let answer: Response;\n\n try {\n answer = await fetch(intakeUrl(intake, shipment.operation), {\n method: 'POST',\n headers: { 'content-type': 'application/json', [TREE_TOKEN_HEADER]: token },\n body: JSON.stringify(shipment.body),\n signal: AbortSignal.timeout(SHIP_TIMEOUT_MS),\n });\n } catch (error: unknown) {\n return { ok: false, status: 0, said: (error as Error).message, accepted: null };\n }\n\n const text: string = await answer.text();\n\n return { ok: answer.ok, status: answer.status, said: saidOf(text), accepted: answer.ok ? acceptedOf(text) : null };\n};\n"]}
@@ -0,0 +1,60 @@
1
+ import { ICargoOverride, IPostmortemItem, IPostmortemsCargo, IProposalsCargo, ISummaryCargo } from './cargo.js';
2
+ import { IEnvironment, IOutcomeOfCommand } from './commands.js';
3
+ import { ISummary } from './observations.js';
4
+ import { IProposal } from './proposals.js';
5
+ import { IShipment, TShip } from './ship.js';
6
+ /** Чем отправка живёт: чем отправлять, чем себя выдаёт дерево и за какой отрезок сводка. */
7
+ export interface IShipOptions {
8
+ /** Печатать, что уехало бы, и не отправлять ничего. */
9
+ readonly dryRun: boolean;
10
+ readonly ship: TShip;
11
+ /** Адрес удалённого репозитория этого дерева: из него считается признак. */
12
+ readonly remote: string;
13
+ /** Сегодняшний день: своих часов у команды нет — иначе сводку за отрезок не проверить спекой. */
14
+ readonly today: string;
15
+ readonly days: number;
16
+ }
17
+ /**
18
+ * Адрес удалённого репозитория, приведённый к одному виду.
19
+ *
20
+ * Форм у него несколько — `git@host:владелец/имя.git`, `https://host/владелец/имя`, — и все они
21
+ * ведут к одному дереву. Две рабочие копии одного репозитория обязаны дать один признак, а
22
+ * непривёденные формы дали бы два.
23
+ */
24
+ export declare function remoteMarkOf(remote: string): string;
25
+ /**
26
+ * Признак дерева: короткое значение, различающее деревья и не выдающее их адреса.
27
+ *
28
+ * Считается хешем от адреса репозитория: по нему адрес не восстанавливается, а у двух рабочих
29
+ * копий одного репозитория он один. Репозитория нет — берётся то, что назвала настройка: иначе
30
+ * все такие деревья слились бы в одно, и число деревьев в сводке стало бы неправдой молча.
31
+ */
32
+ export declare function treeSlugOf(remote: string, spoken: string): string;
33
+ /** Сводка наблюдений вместе со снимком надстроек — то, что уезжает первым запросом. */
34
+ export declare function summaryCargo(summary: ISummary, tree: string, overrides: readonly ICargoOverride[], unpicked: readonly string[], known: ReadonlySet<string>): ISummaryCargo;
35
+ /**
36
+ * Разборы происшествий, как они лежат на дереве.
37
+ *
38
+ * Текст едет целиком: разбор объясняет механизм промаха, и шапка без механизма не отвечает ни на
39
+ * один вопрос, ради которого его читают.
40
+ */
41
+ export declare function readPostmortems(root: string, dir: string): IPostmortemItem[];
42
+ /** Что уезжает и в каком порядке: сводка первой — ею заводится запись месяца. */
43
+ export declare function shipmentsOf(summary: ISummaryCargo, proposals: IProposalsCargo, postmortems: IPostmortemsCargo): readonly IShipment[];
44
+ /**
45
+ * Что в грузе называет дерево, из которого он уезжает.
46
+ *
47
+ * Проверка накрывает сводку и предложения, но не разбор происшествия: разбор по устройству
48
+ * называет файлы дерева, где промах случился, и накрытая проверка отбивала бы каждую его
49
+ * отправку.
50
+ */
51
+ export declare function leaksOfCargo(summary: ISummaryCargo, proposals: readonly IProposal[], marks: readonly string[]): string[];
52
+ /**
53
+ * Отправка груза в приём.
54
+ *
55
+ * Порядок отказов не случаен: выключенная запись отвечает нулём и молчит про адрес — она гасит
56
+ * отправку целиком, вместе со снимком надстроек, и спрашивать про адрес у выключенного дерева
57
+ * незачем.
58
+ */
59
+ export declare function propose(env: IEnvironment, options: IShipOptions): Promise<IOutcomeOfCommand>;
60
+ //# sourceMappingURL=shipment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipment.d.ts","sourceRoot":"","sources":["../../../projects/agent-kit/src/lib/shipment.ts"],"names":[],"mappings":"AAcA,OAAO,EAGH,cAAc,EACd,eAAe,EACf,iBAAiB,EAEjB,eAAe,EACf,aAAa,EAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAe,QAAQ,EAA+B,MAAM,mBAAmB,CAAC;AACvF,OAAO,EAAS,SAAS,EAAyD,MAAM,gBAAgB,CAAC;AACzG,OAAO,EAAE,SAAS,EAAuB,KAAK,EAAE,MAAM,WAAW,CAAC;AAYlE,4FAA4F;AAC5F,MAAM,WAAW,YAAY;IACzB,uDAAuD;IACvD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iGAAiG;IACjG,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CASnD;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAIjE;AAaD,uFAAuF;AACvF,wBAAgB,YAAY,CACxB,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,SAAS,cAAc,EAAE,EACpC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAC3B,aAAa,CAkBf;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,eAAe,EAAE,CAe5E;AAED,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB,GAAG,SAAS,SAAS,EAAE,CAMpI;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAWxH;AAyFD;;;;;;GAMG;AACH,wBAAsB,OAAO,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAkFlG"}