@taskless-app/swarm 0.6.15 → 0.6.16

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 (3) hide show
  1. package/dist/cli.js +558 -176
  2. package/dist/overmind.js +311 -58
  3. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -93,6 +93,13 @@ var init_types = __esm({
93
93
  // вопрос дрона оператору (прод: SwarmEvent kind=question, TASWM-74)
94
94
  "answer",
95
95
  // ответ оператора на вопрос дрона (прод: SwarmEvent kind=answer)
96
+ // Черновик ответа, собранный машиной (TASWM-430): контекст задачи/репозитория + один
97
+ // вызов модели, реплика в ленте прогона рядом с вопросом. НЕ "answer" с флагом — и это
98
+ // главное в этом kind: вопрос закрывают (и дрона будят) все, кто сравнивает kind со
99
+ // строкой "answer"/"answered" — прод (state/taskRuns/timeline/fleetMetrics), веб-чат и
100
+ // синхронизация демона (droneBridge тянет вниз только ответ). Отдельный kind не знает
101
+ // никто из них, поэтому черновик физически не может разбудить дрона: он ждёт человека.
102
+ "answer_draft",
96
103
  "ci_wake",
97
104
  // review-PR покраснел (gh pr checks) — рой разбудил дрона со сжатым summary фейла (TASWM-102)
98
105
  "ci_checked",
@@ -758,6 +765,28 @@ function splitQueue(items, ref) {
758
765
  }
759
766
  return { queue, hosted };
760
767
  }
768
+ function hostTickStallMs(intervalMs) {
769
+ const iv = intervalMs != null && Number.isFinite(intervalMs) && intervalMs > 0 ? intervalMs : 0;
770
+ return Math.max(iv * HOST_TICK_STALL_TICKS, HOST_TICK_STALL_FLOOR_MS);
771
+ }
772
+ function hostTickAgeMs(host, nowMs) {
773
+ const tick = host.tick;
774
+ if (!tick || !tick.at) return null;
775
+ const at = Date.parse(tick.at);
776
+ if (Number.isNaN(at)) return null;
777
+ return Math.max(0, nowMs - at);
778
+ }
779
+ function hostTickStalled(host, nowMs, staleMs2 = HOST_HEARTBEAT_STALE_MS) {
780
+ if (host.lastSeenAt !== void 0 && isHostOffline(host.lastSeenAt, nowMs, staleMs2)) return false;
781
+ const age = hostTickAgeMs(host, nowMs);
782
+ if (age == null) return false;
783
+ return age > hostTickStallMs(host.tick?.intervalMs);
784
+ }
785
+ function hostTickStalledText(ageMs) {
786
+ const min = ageMs == null ? null : Math.max(1, Math.round(ageMs / 6e4));
787
+ const how = min == null ? "\u0442\u0438\u043A \u043D\u0435 \u0434\u0432\u0438\u0433\u0430\u0435\u0442\u0441\u044F" : `\u0442\u0438\u043A \u043D\u0435 \u0434\u0432\u0438\u0433\u0430\u043B\u0441\u044F ${min} \u043C\u0438\u043D`;
788
+ return `\u0446\u0438\u043A\u043B \u0432\u0441\u0442\u0430\u043B \u2014 \u043F\u0443\u043B\u044C\u0441 \u0438\u0434\u0451\u0442, ${how}; ${HOST_TICK_STALLED_ACTION}`;
789
+ }
761
790
  function groupByHost(items, ref, opts = {}) {
762
791
  const { hosts = [], nowMs = Date.now(), staleMs: staleMs2 = HOST_HEARTBEAT_STALE_MS } = opts;
763
792
  const infoById = new Map(hosts.map((h) => [h.hostId, h]));
@@ -811,6 +840,9 @@ function hostStateText(host, nowMs = Date.now(), staleMs2 = HOST_HEARTBEAT_STALE
811
840
  const at = Date.parse(host.lastSeenAt);
812
841
  if (Number.isNaN(at)) return "\u043F\u0443\u043B\u044C\u0441\u0430 \u043D\u0435 \u0431\u044B\u043B\u043E";
813
842
  if (!isHostOffline(host.lastSeenAt, nowMs, staleMs2)) {
843
+ if (hostTickStalled({ tick: host.tick, lastSeenAt: host.lastSeenAt }, nowMs, staleMs2)) {
844
+ return hostTickStalledText(hostTickAgeMs({ tick: host.tick }, nowMs));
845
+ }
814
846
  if (hostNotSignedIn(host)) return hostSignInHint(host.webUrl);
815
847
  if (host.paused) return host.pauseReason ? `\u043D\u0430 \u043F\u0430\u0443\u0437\u0435 \u2014 ${host.pauseReason}` : "\u043D\u0430 \u043F\u0430\u0443\u0437\u0435";
816
848
  if (host.storm) return host.stormReason ? `\u0448\u0442\u043E\u0440\u043C\u0438\u0442 \u2014 ${host.stormReason}` : "\u0448\u0442\u043E\u0440\u043C\u0438\u0442 \u2014 \u043D\u043E\u0432\u044B\u0435 \u0441\u0435\u0441\u0441\u0438\u0438 \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u0442";
@@ -822,7 +854,7 @@ function hostStateText(host, nowMs = Date.now(), staleMs2 = HOST_HEARTBEAT_STALE
822
854
  function shouldGroupByHost(groups) {
823
855
  return groups.length > 1;
824
856
  }
825
- var ANY_HOST_TOKENS, UNKNOWN_HOST_KEY, UNKNOWN_HOST_LABEL, QUEUE_LABEL, NOT_STARTED_STATUSES, HOST_HEARTBEAT_STALE_MS, HOST_ACCESS_REVOKED_TEXT;
857
+ var ANY_HOST_TOKENS, UNKNOWN_HOST_KEY, UNKNOWN_HOST_LABEL, QUEUE_LABEL, NOT_STARTED_STATUSES, HOST_HEARTBEAT_STALE_MS, HOST_TICK_STALL_TICKS, HOST_TICK_STALL_FLOOR_MS, HOST_TICK_STALLED_ACTION, HOST_ACCESS_REVOKED_TEXT;
826
858
  var init_hosts = __esm({
827
859
  "../swarm-core/src/hosts.ts"() {
828
860
  "use strict";
@@ -832,6 +864,9 @@ var init_hosts = __esm({
832
864
  QUEUE_LABEL = "\u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u0438";
833
865
  NOT_STARTED_STATUSES = /* @__PURE__ */ new Set(["queued"]);
834
866
  HOST_HEARTBEAT_STALE_MS = 45e3;
867
+ HOST_TICK_STALL_TICKS = 10;
868
+ HOST_TICK_STALL_FLOOR_MS = 12e4;
869
+ HOST_TICK_STALLED_ACTION = '\u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438 \u0434\u0435\u043C\u043E\u043D \u043C\u0430\u0448\u0438\u043D\u044B \u2014 swarm_host_command{kind:"restart"}';
835
870
  HOST_ACCESS_REVOKED_TEXT = "\u0434\u043E\u0441\u0442\u0443\u043F \u043E\u0442\u043E\u0437\u0432\u0430\u043D \u2014 \u0432\u043E\u0439\u0434\u0438 \u0437\u0430\u043D\u043E\u0432\u043E \u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0435 (swarm login)";
836
871
  }
837
872
  });
@@ -883,10 +918,27 @@ var init_hostResources = __esm({
883
918
  });
884
919
 
885
920
  // ../swarm-core/src/hostPool.ts
921
+ var HOST_REJECTION_TEXT, HOST_REPO_BLOCK_TEXT;
886
922
  var init_hostPool = __esm({
887
923
  "../swarm-core/src/hostPool.ts"() {
888
924
  "use strict";
889
925
  init_hosts();
926
+ HOST_REJECTION_TEXT = {
927
+ offline: "\u043D\u0435 \u043D\u0430 \u0441\u0432\u044F\u0437\u0438",
928
+ tick_stalled: `\u0446\u0438\u043A\u043B \u0440\u0435\u043A\u043E\u043D\u0441\u0430\u0439\u043B\u0430 \u0432\u0441\u0442\u0430\u043B \u2014 \u043F\u0443\u043B\u044C\u0441 \u0438\u0434\u0451\u0442, \u0440\u0430\u0431\u043E\u0442\u0443 \u043D\u0435 \u0431\u0435\u0440\u0451\u0442; ${HOST_TICK_STALLED_ACTION}`,
929
+ not_signed_in: "\u043D\u0435 \u0432\u043E\u0448\u043B\u0430 \u0432 Taskless \u2014 \u043D\u0430 \u0435\u0451 \u0432\u0435\u0431-\u043C\u043E\u0440\u0434\u0435 \u043D\u0435 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D \u0432\u0445\u043E\u0434",
930
+ paused: "\u043D\u0430 \u043F\u0430\u0443\u0437\u0435",
931
+ draining: "\u0432\u044B\u0432\u043E\u0434\u0438\u0442\u0441\u044F \u043D\u0430 \u043E\u0431\u0441\u043B\u0443\u0436\u0438\u0432\u0430\u043D\u0438\u0435",
932
+ repos_off: "\u0432\u0441\u0435 \u0435\u0451 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u044B",
933
+ full: "\u0437\u0430\u043D\u044F\u0442\u0430 \u043F\u043E\u043B\u043D\u043E\u0441\u0442\u044C\u044E"
934
+ };
935
+ HOST_REPO_BLOCK_TEXT = {
936
+ offline: HOST_REJECTION_TEXT.offline,
937
+ paused: HOST_REJECTION_TEXT.paused,
938
+ draining: HOST_REJECTION_TEXT.draining,
939
+ no_clone: "\u043A\u043B\u043E\u043D\u0430 \u043D\u0435\u0442",
940
+ repo_off: "\u0440\u0430\u0431\u043E\u0442\u0430 \u043F\u043E \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044E \u0432\u044B\u043A\u043B\u044E\u0447\u0435\u043D\u0430"
941
+ };
890
942
  }
891
943
  });
892
944
 
@@ -1515,13 +1567,50 @@ var init_claudeBin = __esm({
1515
1567
  });
1516
1568
 
1517
1569
  // src/swarmFiles.ts
1518
- var SWARM_MCP_CONFIG_FILE, SWARM_TASK_DIR, SWARM_EXCLUDE_PATTERNS;
1570
+ function sq(s) {
1571
+ return `'${s.replace(/'/g, `'\\''`)}'`;
1572
+ }
1573
+ function guardHookScript(hook, delegate) {
1574
+ const paths = SWARM_WORKTREE_ARTIFACTS.map(sq).join(" ");
1575
+ const lines = [
1576
+ "#!/usr/bin/env sh",
1577
+ `# \u0421\u0442\u0440\u0430\u0445\u043E\u0432\u043A\u0430 \u0440\u043E\u044F (TASWM-372): \u0441\u043B\u0443\u0436\u0435\u0431\u043D\u044B\u0435 \u0444\u0430\u0439\u043B\u044B \u0440\u043E\u044F \u043D\u0435 \u0443\u0435\u0437\u0436\u0430\u044E\u0442 \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u043F\u0440\u043E\u0435\u043A\u0442\u0430.`,
1578
+ "# \u0425\u0443\u043A \u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D \u041F\u0415\u0420-WORKTREE (git config --worktree core.hooksPath) \u043F\u0440\u0438 \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0435",
1579
+ "# worktree \u0434\u0440\u043E\u043D\u0430; \u0445\u0443\u043A\u0438 \u0441\u0430\u043C\u043E\u0433\u043E \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F \u043D\u0435 \u0442\u0440\u043E\u043D\u0443\u0442\u044B \u2014 \u043F\u0440\u0435\u0436\u043D\u0438\u0439 \u0432\u044B\u0437\u044B\u0432\u0430\u0435\u0442\u0441\u044F \u043D\u0438\u0436\u0435.",
1580
+ `swarm_found=$(git ls-files -- ${paths} 2>/dev/null)`,
1581
+ `if [ -n "$swarm_found" ]; then`,
1582
+ ` echo "\u2716 \u0421\u043B\u0443\u0436\u0435\u0431\u043D\u044B\u0435 \u0444\u0430\u0439\u043B\u044B \u0420\u041E\u042F \u0432 \u0438\u043D\u0434\u0435\u043A\u0441\u0435 git \u2014 ${ACTION[hook]} (TASWM-372):" >&2`,
1583
+ ` printf '%s\\n' "$swarm_found" | sed 's/^/ /' >&2`,
1584
+ ` echo " \u042D\u0442\u043E \u0444\u0430\u0439\u043B\u044B \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u0440\u043E\u044F, \u0430 \u043D\u0435 \u043F\u0440\u043E\u0435\u043A\u0442\u0430: \u0432 ${SWARM_MCP_CONFIG_FILE} \u043B\u0435\u0436\u0438\u0442 \u0442\u043E\u043A\u0435\u043D," >&2`,
1585
+ ` echo " \u043A\u043E\u0442\u043E\u0440\u043E\u043C\u0443 \u043D\u0435\u043B\u044C\u0437\u044F \u043E\u043A\u0430\u0437\u0430\u0442\u044C\u0441\u044F \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 (\u0438\u043D\u0446\u0438\u0434\u0435\u043D\u0442 11.08)." >&2`,
1586
+ ` echo " \u0423\u0431\u0435\u0440\u0438 \u0438\u0445 \u0438\u0437 \u0438\u043D\u0434\u0435\u043A\u0441\u0430 \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438:" >&2`,
1587
+ ` echo " git rm --cached -r --ignore-unmatch ${SWARM_WORKTREE_ARTIFACTS.join(" ")}" >&2`,
1588
+ ` echo " \u0424\u0430\u0439\u043B\u044B \u043D\u0430 \u0434\u0438\u0441\u043A\u0435 \u043E\u0441\u0442\u0430\u043D\u0443\u0442\u0441\u044F \u2014 \u043E\u043D\u0438 \u043D\u0443\u0436\u043D\u044B \u043F\u0440\u043E\u0433\u043E\u043D\u0443." >&2`,
1589
+ " exit 1",
1590
+ "fi"
1591
+ ];
1592
+ if (delegate) {
1593
+ lines.push(
1594
+ "# \u041F\u0440\u0435\u0436\u043D\u0438\u0439 \u0445\u0443\u043A \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F (husky \u0438 \u0442.\u043F.) \u2014 \u043E\u043D \u043D\u0435 \u0434\u043E\u043B\u0436\u0435\u043D \u043F\u0440\u043E\u043F\u0430\u0441\u0442\u044C \u0438\u0437-\u0437\u0430 \u043D\u0430\u0448\u0435\u0439 \u0441\u0442\u0440\u0430\u0445\u043E\u0432\u043A\u0438.",
1595
+ `if [ -x ${sq(delegate)} ]; then exec ${sq(delegate)} "$@"; fi`
1596
+ );
1597
+ }
1598
+ lines.push("exit 0", "");
1599
+ return lines.join("\n");
1600
+ }
1601
+ var SWARM_MCP_CONFIG_FILE, SWARM_TASK_DIR, SWARM_WORKTREE_ARTIFACTS, SWARM_EXCLUDE_PATTERNS, GUARD_HOOKS, ACTION;
1519
1602
  var init_swarmFiles = __esm({
1520
1603
  "src/swarmFiles.ts"() {
1521
1604
  "use strict";
1522
1605
  SWARM_MCP_CONFIG_FILE = ".swarm-mcp.json";
1523
1606
  SWARM_TASK_DIR = ".swarm-task";
1607
+ SWARM_WORKTREE_ARTIFACTS = [SWARM_MCP_CONFIG_FILE, SWARM_TASK_DIR];
1524
1608
  SWARM_EXCLUDE_PATTERNS = [`/${SWARM_MCP_CONFIG_FILE}`, `/${SWARM_TASK_DIR}/`];
1609
+ GUARD_HOOKS = ["pre-commit", "pre-push"];
1610
+ ACTION = {
1611
+ "pre-commit": "\u043A\u043E\u043C\u043C\u0438\u0442 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D",
1612
+ "pre-push": "\u043F\u0443\u0448 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D"
1613
+ };
1525
1614
  }
1526
1615
  });
1527
1616
 
@@ -2186,7 +2275,7 @@ var init_package = __esm({
2186
2275
  "package.json"() {
2187
2276
  package_default = {
2188
2277
  name: "@taskless-app/swarm",
2189
- version: "0.6.15",
2278
+ version: "0.6.16",
2190
2279
  type: "module",
2191
2280
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
2192
2281
  keywords: [
@@ -2236,7 +2325,7 @@ var init_package = __esm({
2236
2325
  execa: "^10.0.0",
2237
2326
  zod: "3.25.76"
2238
2327
  },
2239
- gitHead: "5bc8e0c067de58fd72b0eb434e80ad29c7a00223"
2328
+ gitHead: "785b1478f2b81406c9b6524913939707b6c096fb"
2240
2329
  };
2241
2330
  }
2242
2331
  });
@@ -5043,8 +5132,8 @@ var init_updateCli = __esm({
5043
5132
 
5044
5133
  // src/cli.ts
5045
5134
  init_cliIo();
5046
- import { basename as basename4, join as join24 } from "path";
5047
- import { existsSync as existsSync16, readFileSync as readFileSync17, readdirSync as readdirSync10, statSync as statSync10, openSync as openSync4, readSync as readSync2, closeSync as closeSync4, rmSync as rmSync2 } from "fs";
5135
+ import { basename as basename4, dirname as dirname4, join as join24 } from "path";
5136
+ import { existsSync as existsSync16, readFileSync as readFileSync17, writeFileSync as writeFileSync13, mkdirSync as mkdirSync9, readdirSync as readdirSync10, statSync as statSync10, openSync as openSync4, readSync as readSync2, closeSync as closeSync4 } from "fs";
5048
5137
  import { setTimeout as sleep2 } from "timers/promises";
5049
5138
 
5050
5139
  // src/swarmClient.ts
@@ -5255,6 +5344,29 @@ function hostResourceSnapshot() {
5255
5344
  init_names2();
5256
5345
  init_storm();
5257
5346
 
5347
+ // src/repoRules.ts
5348
+ var RULE_FIELDS = ["brief", "zones", "env", "verify", "conventions", "spine"];
5349
+ function parsePendingRepoRules(raw) {
5350
+ if (!Array.isArray(raw)) return [];
5351
+ const out = [];
5352
+ for (const item of raw) {
5353
+ if (!item || typeof item !== "object") continue;
5354
+ const r = item;
5355
+ const field = RULE_FIELDS.find((f) => f === r.field);
5356
+ if (!field) continue;
5357
+ out.push({
5358
+ field,
5359
+ value: r.value,
5360
+ by: typeof r.by === "string" ? r.by : null,
5361
+ task: typeof r.task === "string" ? r.task : null,
5362
+ why: typeof r.why === "string" ? r.why : null,
5363
+ cheap: r.cheap === true,
5364
+ overwritesHuman: r.overwritesHuman === true
5365
+ });
5366
+ }
5367
+ return out;
5368
+ }
5369
+
5258
5370
  // src/logTail.ts
5259
5371
  import { closeSync, existsSync as existsSync4, openSync, readSync, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
5260
5372
  import { join as join6 } from "path";
@@ -6611,6 +6723,25 @@ var SwarmClient = class _SwarmClient {
6611
6723
  const sc = res.structuredContent ?? {};
6612
6724
  return normalizeBaseBranch(sc.config?.baseBranch) ?? null;
6613
6725
  }
6726
+ /**
6727
+ * ПРАВИЛА ОТ ДРОНОВ, ждущие вердикта ревьюера (TASWM-431): их демон кладёт в промпт
6728
+ * ревьюера — тот решает по ним в своём же вердикте. `taskSlug` — правила ЭТОГО
6729
+ * прогона: ревьюер отвечает за находки своего дрона, а не за чужие.
6730
+ *
6731
+ * Прод недоступен / старый прод (тула нет) / ответ не той формы → пустой список:
6732
+ * ревью обязано подниматься и без этого блока — правила тут не главное.
6733
+ */
6734
+ async repoPendingRules(repoFullName, taskSlug) {
6735
+ let res;
6736
+ try {
6737
+ res = await this.call("swarm_repo_rule_pending", { repoFullName, ...taskSlug ? { taskSlug } : {} });
6738
+ } catch {
6739
+ return [];
6740
+ }
6741
+ if (res?.isError) return [];
6742
+ const sc = res.structuredContent ?? {};
6743
+ return parsePendingRepoRules(sc.rules);
6744
+ }
6614
6745
  /**
6615
6746
  * Live КОМАНДЫ ПОДГОТОВКИ worktree из Taskless (TASWM-312): тот же swarm_repo_config по
6616
6747
  * repoFullName — Project.swarmConfig.prepare. Настройка сильнее детекта демона (deps.ts):
@@ -6654,29 +6785,11 @@ var SwarmClient = class _SwarmClient {
6654
6785
  const p = sc.profile;
6655
6786
  return p && typeof p === "object" && !Array.isArray(p) ? p : null;
6656
6787
  }
6657
- /**
6658
- * РАЗВЕДКА репозитория (TASWM-382): отправить в прод то, что разведчик вычитал в коде.
6659
- * Живую разметку это НЕ меняет — предложение ложится рядом и ждёт человека; поэтому
6660
- * ошибка здесь дорога ровно на стоимость прогона разведки, и мы её возвращаем, а не
6661
- * глотаем, как чтения выше.
6662
- *
6663
- * Мимо outbox (в отличие от мутаций прогонов): предложение имеет смысл только вместе
6664
- * со свежим состоянием репозитория, и доехавшее через сутки после разведки — хуже,
6665
- * чем не доехавшее вовсе. Прод недоступен — человек перезапустит разведку.
6666
- */
6667
- async reportRepoScout(repoFullName, report, by) {
6668
- let res;
6669
- try {
6670
- res = await this.call("swarm_repo_scout_report", { repoFullName, report, ...by ? { by } : {} });
6671
- } catch (e) {
6672
- return { ok: false, error: `\u043F\u0440\u043E\u0434 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D: ${e.message}` };
6673
- }
6674
- const text = res?.content?.map((c) => "text" in c ? c.text : "").join(" ") ?? "";
6675
- if (res?.isError) return { ok: false, error: text || "\u043F\u0440\u043E\u0434 \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u043B \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0438" };
6676
- const sc = res.structuredContent ?? {};
6677
- const diff = Array.isArray(sc.diff) ? sc.diff : [];
6678
- return { ok: true, diff, display: text };
6679
- }
6788
+ // TASWM-441: отправка предложения разведки в прод (`swarm_repo_scout_report`) убрана —
6789
+ // разведчик больше не предлагает разметку в базу, а ведёт файл В САМОМ репозитории
6790
+ // (docs/AGENT_BRIEF.md, см. repoScout.ts). Прод-сторона тула и экран приёмки уезжают
6791
+ // отдельной задачей TASWM-442; `repoProfile` выше — её же остаток, читателей у него
6792
+ // в демоне не осталось.
6680
6793
  /**
6681
6794
  * Live ТРЕБОВАНИЯ К ОКРУЖЕНИЮ машины из Taskless (TASWM-313): тот же swarm_repo_config по
6682
6795
  * repoFullName — Project.swarmConfig.requires. Демон проверяет их ПЕРЕД спавном дрона и не
@@ -6892,7 +7005,7 @@ var SwarmClient = class _SwarmClient {
6892
7005
  *
6893
7006
  * Прод недоступен / ответ без поручений → пустые списки (демон работает дальше).
6894
7007
  */
6895
- async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn) {
7008
+ async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick) {
6896
7009
  try {
6897
7010
  const args = this.repoFullNames.length ? { repoFullNames: this.repoFullNames } : {};
6898
7011
  if (daemonVersion) args.daemonVersion = daemonVersion;
@@ -6919,6 +7032,9 @@ var SwarmClient = class _SwarmClient {
6919
7032
  args.hostId = this.host.hostId;
6920
7033
  args.hostName = this.host.hostName;
6921
7034
  }
7035
+ if (tick && Number.isFinite(tick.seq) && Number.isFinite(tick.ageMs) && tick.intervalMs > 0) {
7036
+ args.tick = { seq: Math.max(0, Math.trunc(tick.seq)), ageMs: Math.max(0, Math.trunc(tick.ageMs)), intervalMs: Math.trunc(tick.intervalMs) };
7037
+ }
6922
7038
  const res = await this.call("swarm_heartbeat", args);
6923
7039
  if (res?.isError) return EMPTY_HEARTBEAT_REPLY;
6924
7040
  this.heartbeatOkAt = this.now();
@@ -7605,6 +7721,7 @@ function withManualAreas(history, areas) {
7605
7721
 
7606
7722
  // src/cli.ts
7607
7723
  init_config();
7724
+ import { $ as $6 } from "execa";
7608
7725
 
7609
7726
  // src/daemonLaunch.ts
7610
7727
  import { openSync as openSync2, closeSync as closeSync2, readFileSync as readFileSync4 } from "fs";
@@ -8262,6 +8379,17 @@ init_storm();
8262
8379
  // src/gh.ts
8263
8380
  init_src();
8264
8381
  import { $ as $4 } from "execa";
8382
+ var realRepoRunner = async (args, cwd) => (await $4({ cwd })`gh ${args}`).stdout;
8383
+ async function openPrsForBranch(branch, repoDir, run = realRepoRunner) {
8384
+ if (!branch || !repoDir) return [];
8385
+ try {
8386
+ const out = await run(["pr", "list", "--head", branch, "--state", "open", "--json", "url"], repoDir);
8387
+ const arr = JSON.parse(out);
8388
+ return Array.isArray(arr) ? arr.map((p) => p.url).filter((u) => typeof u === "string") : [];
8389
+ } catch {
8390
+ return [];
8391
+ }
8392
+ }
8265
8393
  var NO_CHECKS_STDERR = /no checks reported/i;
8266
8394
  var realChecksRunner = async (pr) => {
8267
8395
  const r = await $4({ reject: false, timeout: 15e3 })`gh pr checks ${pr} --json state,name,bucket,link`;
@@ -10504,6 +10632,19 @@ import { appendFile, chmod, mkdir, readFile, rm, writeFile } from "fs/promises";
10504
10632
  import { isAbsolute, join as join13, sep as sep2 } from "path";
10505
10633
  import { $ as $5 } from "execa";
10506
10634
  init_swarmFiles();
10635
+ var WorktreeBusyError = class extends Error {
10636
+ constructor(branch, worktreePath) {
10637
+ super(`worktree \u0437\u0430\u043D\u044F\u0442: ${worktreePath} (\u0432\u0435\u0442\u043A\u0430 ${branch}) \u2014 \u043F\u0440\u0438\u0431\u0435\u0440\u0438 worktree \u0438\u043B\u0438 \u043E\u0442\u043C\u0435\u043D\u0438 \u043F\u0440\u043E\u0433\u043E\u043D (swarm cancel ${branch})`);
10638
+ this.branch = branch;
10639
+ this.worktreePath = worktreePath;
10640
+ this.name = "WorktreeBusyError";
10641
+ }
10642
+ branch;
10643
+ worktreePath;
10644
+ };
10645
+ async function reclaimStaleWorktree(repo, worktree) {
10646
+ await detachWorktree(repo, worktree);
10647
+ }
10507
10648
  function isLinkedWorktree(dir) {
10508
10649
  try {
10509
10650
  return statSync8(join13(dir, ".git")).isFile();
@@ -10538,12 +10679,159 @@ async function ensureWorktreeDirGone(worktree) {
10538
10679
  }
10539
10680
  return true;
10540
10681
  }
10682
+ async function ensureLongPaths(repo, opts = {}) {
10683
+ if ((opts.platform ?? process.platform) !== "win32") return false;
10684
+ const cur = (await $5({ reject: false })`git -C ${repo} config --get core.longpaths`).stdout.trim();
10685
+ if (cur === "true") return false;
10686
+ const res = await $5({ reject: false })`git -C ${repo} config core.longpaths true`;
10687
+ if (res.exitCode !== 0) {
10688
+ console.error(
10689
+ `[git] \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0432\u043A\u043B\u044E\u0447\u0438\u0442\u044C core.longpaths \u0432 ${repo} \u2014 \u043D\u0430 Windows \u0441\u043D\u043E\u0441 worktree \u0434\u0440\u043E\u043D\u0430 \u0431\u0443\u0434\u0435\u0442 \u043F\u0430\u0434\u0430\u0442\u044C \xABFilename too long\xBB \u043D\u0430 node_modules. \u0412\u043A\u043B\u044E\u0447\u0438\u0442\u0435 \u0440\u0443\u043A\u0430\u043C\u0438: \`git -C ${repo} config core.longpaths true\`.`
10690
+ );
10691
+ return false;
10692
+ }
10693
+ console.log(`[git] core.longpaths=true \u0432\u043A\u043B\u044E\u0447\u0451\u043D \u0434\u043B\u044F ${repo} (TASWM-279): \u0431\u0435\u0437 \u043D\u0435\u0433\u043E \u0441\u043D\u043E\u0441 worktree \u0434\u0440\u043E\u043D\u0430 \u043F\u0430\u0434\u0430\u0435\u0442 \u043D\u0430 \u0434\u043B\u0438\u043D\u043D\u044B\u0445 \u043F\u0443\u0442\u044F\u0445 node_modules`);
10694
+ return true;
10695
+ }
10696
+ async function revParse(repo, ref) {
10697
+ return (await $5({ reject: false })`git -C ${repo} rev-parse --verify --quiet ${ref}`).stdout.trim();
10698
+ }
10699
+ async function worktreeForBranch(repo, branch) {
10700
+ const out = (await $5`git -C ${repo} worktree list --porcelain`).stdout;
10701
+ const target = `refs/heads/${branch}`;
10702
+ let current = null;
10703
+ for (const line of out.split("\n")) {
10704
+ if (line.startsWith("worktree ")) current = line.slice("worktree ".length).trim();
10705
+ else if (line.startsWith("branch ") && line.slice("branch ".length).trim() === target) return current;
10706
+ }
10707
+ return null;
10708
+ }
10709
+ async function addWorktree(repo, path, branch, opts = {}) {
10710
+ const res = await checkoutWorktree(repo, path, branch, opts);
10711
+ await ensureWorktreeHygiene(path);
10712
+ return res;
10713
+ }
10714
+ async function checkoutWorktree(repo, path, branch, opts) {
10715
+ await ensureLongPaths(repo);
10716
+ const localSha = await revParse(repo, `refs/heads/${branch}`);
10717
+ const originSha = await revParse(repo, `refs/remotes/origin/${branch}`);
10718
+ if (!localSha && !originSha) {
10719
+ const freshBase = opts.base ?? await freshWorktreeBase(repo);
10720
+ await $5`git -C ${repo} worktree add -b ${branch} ${path} ${freshBase}`;
10721
+ return { reused: false };
10722
+ }
10723
+ const busyAt = await worktreeForBranch(repo, branch);
10724
+ if (busyAt) {
10725
+ const exists = existsSync9(busyAt);
10726
+ const live = exists && (opts.isWorktreeHeldLive ? await opts.isWorktreeHeldLive(busyAt) : true);
10727
+ if (live) throw new WorktreeBusyError(branch, busyAt);
10728
+ if (exists && await worktreeDirty(busyAt)) throw new WorktreeBusyError(branch, busyAt);
10729
+ await reclaimStaleWorktree(repo, busyAt);
10730
+ opts.onStaleReclaim?.(busyAt);
10731
+ }
10732
+ if (originSha) {
10733
+ await $5`git -C ${repo} worktree add -B ${branch} ${path} ${`origin/${branch}`}`;
10734
+ return { reused: true, from: "origin", sha: originSha };
10735
+ }
10736
+ await $5`git -C ${repo} worktree add ${path} ${branch}`;
10737
+ return { reused: true, from: "local" };
10738
+ }
10739
+ async function freshWorktreeBase(repo) {
10740
+ const hasOrigin = (await $5({ reject: false })`git -C ${repo} remote get-url origin`).exitCode === 0;
10741
+ if (!hasOrigin) return "HEAD";
10742
+ await $5`git -C ${repo} fetch --quiet origin`;
10743
+ return remoteMainRef(repo);
10744
+ }
10745
+ async function setWorktreeIdentity(worktree, name) {
10746
+ const local = name.toLowerCase().replace(/\s+/g, "-");
10747
+ await $5`git -C ${worktree} config extensions.worktreeConfig true`;
10748
+ await $5`git -C ${worktree} config --worktree user.name ${`${name} (swarm drone)`}`;
10749
+ await $5`git -C ${worktree} config --worktree user.email ${`${local}@drones.swarm.local`}`;
10750
+ }
10751
+ async function ensureGitExcluded(worktree, pattern) {
10752
+ try {
10753
+ const commonDir = await gitPath(worktree, "--git-common-dir");
10754
+ if (!commonDir) return false;
10755
+ const infoDir = join13(commonDir, "info");
10756
+ const excludeFile = join13(infoDir, "exclude");
10757
+ let current = "";
10758
+ try {
10759
+ current = await readFile(excludeFile, "utf8");
10760
+ } catch {
10761
+ }
10762
+ if (current.split(/\r?\n/).some((l) => l.trim() === pattern)) return true;
10763
+ await mkdir(infoDir, { recursive: true });
10764
+ const prefix = current && !current.endsWith("\n") ? "\n" : "";
10765
+ await appendFile(excludeFile, `${prefix}${pattern}
10766
+ `);
10767
+ return true;
10768
+ } catch {
10769
+ return false;
10770
+ }
10771
+ }
10772
+ async function gitPath(worktree, flag) {
10773
+ const out = await $5({ reject: false })`git -C ${worktree} rev-parse ${flag}`;
10774
+ const raw = out.stdout.trim();
10775
+ if (out.exitCode !== 0 || !raw) return null;
10776
+ return isAbsolute(raw) ? raw : join13(worktree, raw);
10777
+ }
10778
+ async function ensureWorktreeHygiene(worktree) {
10779
+ for (const pattern of SWARM_EXCLUDE_PATTERNS) await ensureGitExcluded(worktree, pattern);
10780
+ await installGuardHooks(worktree);
10781
+ }
10782
+ async function installGuardHooks(worktree) {
10783
+ try {
10784
+ const gitDir = await gitPath(worktree, "--git-dir");
10785
+ if (!gitDir) return false;
10786
+ const hooksDir = join13(gitDir, "swarm-hooks");
10787
+ const prev = await previousHooksPath(worktree);
10788
+ await mkdir(hooksDir, { recursive: true });
10789
+ for (const hook of GUARD_HOOKS) {
10790
+ await writeFile(join13(hooksDir, hook), guardHookScript(hook, prev ? `${posix(prev)}/${hook}` : null));
10791
+ await chmod(join13(hooksDir, hook), 493);
10792
+ }
10793
+ await $5`git -C ${worktree} config extensions.worktreeConfig true`;
10794
+ await $5`git -C ${worktree} config --worktree core.hooksPath ${posix(hooksDir)}`;
10795
+ return true;
10796
+ } catch {
10797
+ return false;
10798
+ }
10799
+ }
10800
+ function posix(p) {
10801
+ return (sep2 === "\\" ? p.replace(/\\/g, "/") : p).replace(/\/+$/, "");
10802
+ }
10803
+ async function previousHooksPath(worktree) {
10804
+ for (const scope of ["--local", "--global", "--system"]) {
10805
+ const out = await $5({ reject: false })`git -C ${worktree} config ${scope} --get core.hooksPath`;
10806
+ const value = out.stdout.trim();
10807
+ if (out.exitCode === 0 && value) return value;
10808
+ }
10809
+ const commonDir = await gitPath(worktree, "--git-common-dir");
10810
+ return commonDir ? join13(commonDir, "hooks") : null;
10811
+ }
10541
10812
  async function removeWorktree(repo, path) {
10542
10813
  const branch = (await $5({ reject: false })`git -C ${path} symbolic-ref --quiet --short HEAD`).stdout.trim();
10543
10814
  await cleanupWorktreeProcesses(path);
10544
10815
  await detachWorktree(repo, path);
10545
10816
  if (branch) await $5({ reject: false })`git -C ${repo} branch -D ${branch}`;
10546
10817
  }
10818
+ async function worktreeDirty(path) {
10819
+ const st = await $5({ reject: false })`git -C ${path} status --porcelain`;
10820
+ if (st.exitCode !== 0) return true;
10821
+ const status = st.stdout.trim();
10822
+ if (status) return true;
10823
+ const unpushed = (await $5({ reject: false })`git -C ${path} rev-list --count HEAD --not --remotes`).stdout.trim();
10824
+ return unpushed !== "" && unpushed !== "0";
10825
+ }
10826
+ async function remoteMainRef(repo) {
10827
+ const head = (await $5({ reject: false })`git -C ${repo} symbolic-ref --quiet --short refs/remotes/origin/HEAD`).stdout.trim();
10828
+ if (head) return head;
10829
+ for (const cand of ["origin/main", "origin/master"]) {
10830
+ const ok2 = await $5({ reject: false })`git -C ${repo} rev-parse --verify --quiet ${cand}`;
10831
+ if (ok2.exitCode === 0) return cand;
10832
+ }
10833
+ throw new Error(`\u043D\u0435 \u043D\u0430\u0448\u0451\u043B \u0434\u0435\u0444\u043E\u043B\u0442-\u0432\u0435\u0442\u043A\u0443 origin \u0432 ${repo} (\u043D\u0435\u0442 origin/HEAD, origin/main, origin/master)`);
10834
+ }
10547
10835
  var GITHUB_REMOTE_RE = /github\.com[:/]([^/]+\/[^/]+?)(?:\.git)?\/?$/i;
10548
10836
  async function remoteRepoFullName(repo) {
10549
10837
  const url = (await $5({ reject: false })`git -C ${repo} remote get-url origin`).stdout.trim();
@@ -10790,180 +11078,200 @@ function resilientSwarmMcp(opts = {}) {
10790
11078
 
10791
11079
  // src/repoScout.ts
10792
11080
  import { HEADLESS_BLOCK } from "./overmind.js";
10793
- var SCOUT_FIELDS = ["brief", "zones", "env", "verify", "conventions", "spine"];
10794
- var SHAPE = `{
10795
- "brief": "\u0427\u0442\u043E \u044D\u0442\u043E \u0437\u0430 \u043F\u0440\u043E\u0434\u0443\u043A\u0442 \u0438 \u043D\u0430 \u0447\u0451\u043C \u043D\u0430\u043F\u0438\u0441\u0430\u043D \u2014 3-5 \u0441\u0442\u0440\u043E\u043A, \u0447\u0442\u043E\u0431\u044B \u0434\u0440\u043E\u043D \u043D\u0435 \u0432\u044B\u0432\u043E\u0434\u0438\u043B \u044D\u0442\u043E \u0438\u0437 \u043A\u043E\u0434\u0430 \u0437\u0430\u043D\u043E\u0432\u043E.",
10796
- "zones": [
10797
- { "label": "area:<\u043A\u043E\u0440\u043E\u0442\u043A\u043E\u0435-\u0438\u043C\u044F>", "about": "\u0447\u0442\u043E \u044D\u0442\u043E \u0437\u0430 \u0437\u043E\u043D\u0430 \u043E\u0434\u043D\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u043E\u0439", "paths": ["packages/api/"] }
10798
- ],
10799
- "env": {
10800
- "database": "\u0447\u0435\u043C \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u0431\u0430\u0437\u0443, \u0440\u043E\u0432\u043D\u043E \u043E\u0434\u043D\u0430 \u043A\u043E\u043C\u0430\u043D\u0434\u0430",
10801
- "migrations": "\u0447\u0435\u043C \u043D\u0430\u043A\u0430\u0442\u0438\u0442\u044C \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u0440\u043E\u0432\u043D\u043E \u043E\u0434\u043D\u0430 \u043A\u043E\u043C\u0430\u043D\u0434\u0430",
10802
- "vars": ["\u0418\u041C\u0415\u041D\u0410", "\u041F\u0415\u0420\u0415\u041C\u0415\u041D\u041D\u042B\u0425", "\u0411\u0415\u0417_\u0417\u041D\u0410\u0427\u0415\u041D\u0418\u0419"]
10803
- },
10804
- "verify": {
10805
- "commands": ["pnpm typecheck", "pnpm test"],
10806
- "green": "\u0447\u0442\u043E \u0437\u0434\u0435\u0441\u044C \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F \u0437\u0435\u043B\u0451\u043D\u044B\u043C \u0441\u043B\u043E\u0432\u0430\u043C\u0438: \u0447\u0442\u043E \u0433\u043E\u043D\u044F\u0435\u0442\u0441\u044F \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E, \u0447\u0442\u043E \u0442\u043E\u043B\u044C\u043A\u043E \u043D\u0430 CI, \u043A\u0430\u043A\u043E\u0439 \u0447\u0435\u043A \u0444\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u043F\u0440\u0438\u0437\u043D\u0430\u043A"
10807
- },
10808
- "conventions": { "commitLanguage": "ru|en", "slugInCommit": true, "baseBranch": "main" },
10809
- "spine": ["packages/api/prisma/schema.prisma", "packages/shared/src/"]
10810
- }`;
11081
+ var BRIEF_PATH = "docs/AGENT_BRIEF.md";
11082
+ var CLAUDE_MD_PATH = "CLAUDE.md";
11083
+ var BRIEF_IMPORT = `@${BRIEF_PATH}`;
11084
+ var BRIEF_HEADER = [
11085
+ "<!-- \u042D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B \u0432\u0435\u0434\u0451\u0442 \u0440\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A \u0440\u043E\u044F (`swarm scout`). \u041F\u043E\u0432\u0442\u043E\u0440\u043D\u044B\u0439 \u043F\u0440\u043E\u0445\u043E\u0434 \u041F\u0415\u0420\u0415\u0417\u0410\u041F\u0418\u0421\u042B\u0412\u0410\u0415\u0422 \u0435\u0433\u043E \u0446\u0435\u043B\u0438\u043A\u043E\u043C:",
11086
+ " \u043F\u0440\u0430\u0432\u043A\u0438 \u0440\u0443\u043A\u0430\u043C\u0438 \u0437\u0434\u0435\u0441\u044C \u043D\u0435 \u043F\u0435\u0440\u0435\u0436\u0438\u0432\u0443\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0443\u044E \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0443. \u0421\u0432\u043E\u0438 \u043F\u0440\u0430\u0432\u0438\u043B\u0430 \u043F\u0438\u0448\u0438 \u0432 CLAUDE.md \u2014",
11087
+ ` \u043E\u043D \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B \u0441\u0442\u0440\u043E\u043A\u043E\u0439 \`${BRIEF_IMPORT}\` \u0438 \u0440\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A\u043E\u043C \u043D\u0435 \u0442\u0440\u043E\u0433\u0430\u0435\u0442\u0441\u044F. -->`
11088
+ ].join("\n");
11089
+ var SHAPE = `# \u0411\u0440\u0438\u0444 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F
11090
+
11091
+ \u041A\u043E\u0440\u043E\u0442\u043A\u0438\u0439 \u043E\u0442\u0432\u0435\u0442 \u043D\u0430 \xAB\u0447\u0442\u043E \u044D\u0442\u043E \u0438 \u043D\u0430 \u0447\u0451\u043C \u043D\u0430\u043F\u0438\u0441\u0430\u043D\u043E\xBB \u2014 3-5 \u0441\u0442\u0440\u043E\u043A, \u0447\u0442\u043E\u0431\u044B \u0430\u0433\u0435\u043D\u0442 \u043D\u0435 \u0432\u044B\u0432\u043E\u0434\u0438\u043B \u044D\u0442\u043E \u0438\u0437 \u043A\u043E\u0434\u0430 \u0437\u0430\u043D\u043E\u0432\u043E.
11092
+
11093
+ ## \u041E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435
11094
+
11095
+ - **\u0411\u0430\u0437\u0430 \u0434\u0430\u043D\u043D\u044B\u0445:** \u0447\u0435\u043C \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u2014 \u0440\u043E\u0432\u043D\u043E \u043E\u0434\u043D\u0430 \u043A\u043E\u043C\u0430\u043D\u0434\u0430, \u0441\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u0430\u044F \u0438\u0437 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F
11096
+ - **\u041C\u0438\u0433\u0440\u0430\u0446\u0438\u0438:** \u0447\u0435\u043C \u043D\u0430\u043A\u0430\u0442\u0438\u0442\u044C \u2014 \u0440\u043E\u0432\u043D\u043E \u043E\u0434\u043D\u0430 \u043A\u043E\u043C\u0430\u043D\u0434\u0430
11097
+ - **\u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F:** \`\u0418\u041C\u0415\u041D\u0410\`, \`\u0411\u0415\u0417\`, \`\u0417\u041D\u0410\u0427\u0415\u041D\u0418\u0419\`
11098
+
11099
+ ## \u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0438
11100
+
11101
+ - \`\u043A\u043E\u043C\u0430\u043D\u0434\u0430 \u0442\u0438\u043F\u043E\u0432\`
11102
+ - \`\u043A\u043E\u043C\u0430\u043D\u0434\u0430 \u0442\u0435\u0441\u0442\u043E\u0432\`
11103
+
11104
+ \u0427\u0442\u043E \u0437\u0434\u0435\u0441\u044C \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F \u0437\u0435\u043B\u0451\u043D\u044B\u043C: \u0441\u043B\u043E\u0432\u0430\u043C\u0438 \u2014 \u0447\u0442\u043E \u0433\u043E\u043D\u044F\u0435\u0442\u0441\u044F \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E, \u0447\u0442\u043E \u0442\u043E\u043B\u044C\u043A\u043E \u043D\u0430 CI, \u043A\u0430\u043A\u043E\u0439 \u0447\u0435\u043A \u0444\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u043F\u0440\u0438\u0437\u043D\u0430\u043A.
11105
+
11106
+ ## \u0421\u043E\u0433\u043B\u0430\u0448\u0435\u043D\u0438\u044F
11107
+
11108
+ - \u042F\u0437\u044B\u043A \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u043A\u043E\u043C\u043C\u0438\u0442\u043E\u0432: ru/en
11109
+ - \u0421\u043B\u0430\u0433 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0438 \u043A\u043E\u043C\u043C\u0438\u0442\u0430: \u043D\u0443\u0436\u0435\u043D/\u043D\u0435 \u043D\u0443\u0436\u0435\u043D
11110
+ - \u0411\u0430\u0437\u043E\u0432\u0430\u044F \u0432\u0435\u0442\u043A\u0430: main
11111
+
11112
+ ## \u0417\u043E\u043D\u044B \u043A\u043E\u0434\u0430
11113
+
11114
+ - \`packages/api/\` \u2014 \u0447\u0442\u043E \u044D\u0442\u043E \u0437\u0430 \u0437\u043E\u043D\u0430 \u043E\u0434\u043D\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u043E\u0439
11115
+ - \`packages/web/\` \u2014 \u2026
11116
+
11117
+ ## \u041E\u0431\u0449\u0438\u0439 \u0445\u0440\u0435\u0431\u0435\u0442
11118
+
11119
+ \u0424\u0430\u0439\u043B\u044B \u0438 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043A\u0430\u0442\u044F\u0442\u0441\u044F \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u043C\u0438 PR \u0438 \u043D\u0435 \u043F\u0430\u0440\u0430\u043B\u043B\u0435\u043B\u044F\u0442\u0441\u044F:
11120
+
11121
+ - \`packages/api/prisma/schema.prisma\`
11122
+ - \`packages/shared/src/\``;
10811
11123
  function buildScoutPrompt(input) {
10812
11124
  const name = input.name ?? "\u0440\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A";
10813
- const known = describeKnownProfile(input.profile ?? null);
10814
11125
  return [
10815
11126
  `\u0422\u044B \u2014 ${name}, \u0420\u0410\u0417\u0412\u0415\u0414\u0427\u0418\u041A \u0440\u043E\u044F: headless-\u0441\u0435\u0441\u0441\u0438\u044F (\u041D\u0415 \u0438\u043D\u0442\u0435\u0440\u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0439 \u0447\u0430\u0442), \u043E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \xAB\u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0430 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F\xBB.`,
10816
11127
  "",
10817
11128
  HEADLESS_BLOCK,
10818
11129
  "",
10819
- `\u0420\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439: ${input.repoFullName}, \u043A\u043B\u043E\u043D \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435: ${input.repoPath}. \u0422\u044B \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0448\u044C \u0432 \u043D\u0451\u043C.`,
11130
+ `\u0420\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439: ${input.repoFullName}, \u0440\u0430\u0431\u043E\u0447\u0430\u044F \u043A\u043E\u043F\u0438\u044F \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435: ${input.repoPath}. \u0422\u044B \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0448\u044C \u0432 \u043D\u0435\u0439.`,
10820
11131
  "",
10821
- "\u0427\u0422\u041E \u041E\u0422 \u0422\u0415\u0411\u042F \u041D\u0423\u0416\u041D\u041E. \u0420\u043E\u0439 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u043B\u0438 \u043A \u044D\u0442\u043E\u043C\u0443 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044E, \u0438 \u0434\u0430\u043B\u044C\u0448\u0435 \u043E\u043D \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u0432\u0441\u043B\u0435\u043F\u0443\u044E: \u0437\u043E\u043D\u044B \u0434\u043B\u044F \u0437\u0430\u0434\u0430\u0447 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0440\u0430\u0441\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442 \u0440\u0443\u043A\u0430\u043C\u0438, \u043A\u0430\u043A \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u2014 \u0434\u0440\u043E\u043D \u0443\u0433\u0430\u0434\u044B\u0432\u0430\u0435\u0442, \u0447\u0442\u043E \u0441\u0447\u0438\u0442\u0430\u0442\u044C \u0437\u0435\u043B\u0451\u043D\u044B\u043C \u2014 \u043D\u0435 \u0437\u043D\u0430\u0435\u0442 \u043D\u0438\u043A\u0442\u043E. \u041F\u0440\u043E\u0447\u0438\u0442\u0430\u0439 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0438 \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0438 \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0443: \u0442\u043E, \u0447\u0442\u043E \u0434\u0440\u043E\u043D \u043E\u0431\u044F\u0437\u0430\u043D \u0437\u043D\u0430\u0442\u044C \u0414\u041E \u043F\u0435\u0440\u0432\u043E\u0439 \u0437\u0430\u0434\u0430\u0447\u0438.",
11132
+ `\u0427\u0422\u041E \u041E\u0422 \u0422\u0415\u0411\u042F \u041D\u0423\u0416\u041D\u041E. \u0420\u043E\u0439 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u043B\u0438 \u043A \u044D\u0442\u043E\u043C\u0443 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044E, \u0438 \u0434\u0430\u043B\u044C\u0448\u0435 \u043E\u043D \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u0432\u0441\u043B\u0435\u043F\u0443\u044E: \u043A\u0430\u043A \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u2014 \u0430\u0433\u0435\u043D\u0442 \u0443\u0433\u0430\u0434\u044B\u0432\u0430\u0435\u0442, \u0447\u0442\u043E \u0441\u0447\u0438\u0442\u0430\u0442\u044C \u0437\u0435\u043B\u0451\u043D\u044B\u043C \u2014 \u043D\u0435 \u0437\u043D\u0430\u0435\u0442 \u043D\u0438\u043A\u0442\u043E, \u043A\u0430\u043A\u0438\u0435 \u043A\u0443\u0441\u043A\u0438 \u043A\u043E\u0434\u0430 \u043D\u0435\u043B\u044C\u0437\u044F \u043F\u0440\u0430\u0432\u0438\u0442\u044C \u043E\u0434\u043D\u043E\u0432\u0440\u0435\u043C\u0435\u043D\u043D\u043E \u2014 \u0442\u043E\u0436\u0435. \u041F\u0440\u043E\u0447\u0438\u0442\u0430\u0439 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0438 \u043D\u0430\u043F\u0438\u0448\u0438 \u0444\u0430\u0439\u043B \`${input.briefPath}\`: \u0442\u043E, \u043E\u0431\u043E \u0447\u0442\u043E \u0430\u0433\u0435\u043D\u0442 \u0441\u043F\u043E\u0442\u044B\u043A\u0430\u0435\u0442\u0441\u044F \u0432 \u043D\u0435\u0437\u043D\u0430\u043A\u043E\u043C\u043E\u043C \u043F\u0440\u043E\u0435\u043A\u0442\u0435.`,
10822
11133
  "",
10823
- "\u0421\u043E\u0431\u0435\u0440\u0438 \u0448\u0435\u0441\u0442\u044C \u043F\u043E\u043B\u0435\u0439:",
10824
- "1. `brief` \u2014 \u0447\u0442\u043E \u044D\u0442\u043E \u0437\u0430 \u043F\u0440\u043E\u0434\u0443\u043A\u0442 \u0438 \u043D\u0430 \u0447\u0451\u043C \u043D\u0430\u043F\u0438\u0441\u0430\u043D. \u041A\u043E\u0440\u043E\u0442\u043A\u043E, \u0447\u0442\u043E\u0431\u044B \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0434\u0440\u043E\u043D \u043D\u0435 \u0432\u044B\u0432\u043E\u0434\u0438\u043B \u044D\u0442\u043E \u0438\u0437 \u043A\u043E\u0434\u0430 \u0437\u0430\u043D\u043E\u0432\u043E.",
10825
- "2. `zones` \u2014 \u0437\u043E\u043D\u044B \u043A\u043E\u0434\u0430, \u043B\u0435\u0439\u0431\u043B\u044B `area:*`: \u043F\u0430\u043A\u0435\u0442\u044B, \u043C\u043E\u0434\u0443\u043B\u0438, \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0438 \u0444\u0438\u0447. \u041F\u043E \u043D\u0438\u043C \u0440\u043E\u0439 \u0440\u0430\u0437\u0432\u043E\u0434\u0438\u0442 \u043F\u0430\u0440\u0430\u043B\u043B\u0435\u043B\u044C\u043D\u044B\u0435 \u0437\u0430\u0434\u0430\u0447\u0438, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u0443 \u043A\u0430\u0436\u0434\u043E\u0439 \u0437\u043E\u043D\u044B \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u044B `paths` (\u043A\u0430\u0442\u0430\u043B\u043E\u0433/\u043F\u0440\u0435\u0444\u0438\u043A\u0441, glob). \u0414\u0440\u043E\u0431\u0438 \u043F\u043E \u0420\u0415\u0410\u041B\u042C\u041D\u041E\u0419 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0435 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F, \u0430 \u043D\u0435 \u043F\u043E \u043A\u0440\u0430\u0441\u0438\u0432\u044B\u043C \u0441\u043B\u043E\u0432\u0430\u043C: \u0437\u043E\u043D\u0430, \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u043D\u0435 \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043A\u0430\u0442\u0430\u043B\u043E\u0433, \u0431\u0435\u0441\u043F\u043E\u043B\u0435\u0437\u043D\u0430.",
10826
- "3. `env` \u2014 \u043A\u0430\u043A \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435: \u0447\u0435\u043C \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F \u0431\u0430\u0437\u0430 (`database`), \u0447\u0435\u043C \u043D\u0430\u043A\u0430\u0442\u044B\u0432\u0430\u044E\u0442\u0441\u044F \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 (`migrations`), \u043A\u0430\u043A\u0438\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043D\u0443\u0436\u043D\u044B (`vars` \u2014 \u0422\u041E\u041B\u042C\u041A\u041E \u0418\u041C\u0415\u041D\u0410; \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F \u044D\u0442\u043E \u0441\u0435\u043A\u0440\u0435\u0442\u044B, \u043E\u043D\u0438 \u0443\u0435\u0434\u0443\u0442 \u0432 \u043F\u0440\u043E\u043C\u043F\u0442 \u0434\u0440\u043E\u043D\u0430 \u0438 \u0432 \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043A\u0430\u043A \u043E\u0431\u044B\u0447\u043D\u044B\u0439 \u0442\u0435\u043A\u0441\u0442). \u042D\u0442\u043E \u043F\u0440\u043E \u0442\u043E, \u0447\u0435\u043C \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E\u0442 \u0421\u0410\u041C\u041E \u041F\u0420\u0418\u041B\u041E\u0416\u0415\u041D\u0418\u0415, \u0430 \u043D\u0435 \u0442\u0435\u0441\u0442\u044B: \u0432\u043E \u043C\u043D\u043E\u0433\u0438\u0445 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F\u0445 \u0442\u0435\u0441\u0442\u043E\u0432\u0430\u044F \u0431\u0430\u0437\u0430 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u0430\u044F \u2014 \u0441\u0432\u043E\u0439 compose-\u0444\u0430\u0439\u043B, \u0441\u0432\u043E\u0439 \u0441\u043A\u0440\u0438\u043F\u0442, \u0441\u0432\u043E\u0439 \u043F\u043E\u0440\u0442, \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u0430\u044F \u0432\u0438\u0434\u0430 `TEST_DATABASE_URL`. \u041E\u043D\u0430 \u0441\u044E\u0434\u0430 \u041D\u0415 \u0438\u0434\u0451\u0442. \u041C\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u043D\u0430\u043A\u0430\u0442\u0430\u043D\u043D\u044B\u0435 \u0432 \u0442\u0435\u0441\u0442\u043E\u0432\u0443\u044E \u0431\u0430\u0437\u0443, \u043E\u0441\u0442\u0430\u0432\u044F\u0442 \u0440\u0430\u0431\u043E\u0447\u0443\u044E \u043D\u0430 \u0441\u0442\u0430\u0440\u043E\u0439 \u0441\u0445\u0435\u043C\u0435, \u0430 \u0434\u0440\u043E\u043D \u0431\u0443\u0434\u0435\u0442 \u0441\u0447\u0438\u0442\u0430\u0442\u044C \u0435\u0451 \u043E\u0431\u043D\u043E\u0432\u043B\u0451\u043D\u043D\u043E\u0439. \u0418\u0449\u0438 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0432\u0445\u043E\u0434: \u0441\u043A\u0440\u0438\u043F\u0442 \u0437\u0430\u043F\u0443\u0441\u043A\u0430 \u0432 `scripts/`, `dev`/`start` \u0432 \u043C\u0430\u043D\u0438\u0444\u0435\u0441\u0442\u0435, \u0440\u0430\u0437\u0434\u0435\u043B README \xAB\u043A\u0430\u043A \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C\xBB, \u0444\u0430\u0439\u043B \u043F\u0440\u0430\u0432\u0438\u043B \u0434\u043B\u044F \u0430\u0433\u0435\u043D\u0442\u043E\u0432.",
10827
- "4. `verify` \u2014 \u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0442\u044C (`commands`) \u0438 \u0447\u0442\u043E \u0437\u0434\u0435\u0441\u044C \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F \u0437\u0435\u043B\u0451\u043D\u044B\u043C (`green` \u2014 \u0441\u043B\u043E\u0432\u0430\u043C\u0438: \u0447\u0442\u043E \u0433\u043E\u043D\u044F\u0435\u0442\u0441\u044F \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E, \u0447\u0442\u043E \u0442\u043E\u043B\u044C\u043A\u043E \u043D\u0430 CI, \u043A\u0430\u043A\u043E\u0439 \u0447\u0435\u043A \u0441\u043B\u0443\u0436\u0438\u0442 \u0444\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u043C \u043F\u0440\u0438\u0437\u043D\u0430\u043A\u043E\u043C \u0437\u0435\u043B\u0435\u043D\u0438).",
10828
- "5. `conventions` \u2014 \u044F\u0437\u044B\u043A \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u043A\u043E\u043C\u043C\u0438\u0442\u043E\u0432 (`commitLanguage`), \u043D\u0443\u0436\u0435\u043D \u043B\u0438 \u0441\u043B\u0430\u0433 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0438 (`slugInCommit`), \u0431\u0430\u0437\u043E\u0432\u0430\u044F \u0432\u0435\u0442\u043A\u0430 (`baseBranch`).",
10829
- "6. `spine` \u2014 \u041E\u0411\u0429\u0418\u0419 \u0425\u0420\u0415\u0411\u0415\u0422: \u0444\u0430\u0439\u043B\u044B \u0438 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043A\u0430\u0442\u044F\u0442\u0441\u044F \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u043C\u0438 PR \u0438 \u043D\u0435 \u043F\u0430\u0440\u0430\u043B\u043B\u0435\u043B\u044F\u0442\u0441\u044F (\u0441\u0445\u0435\u043C\u0430 \u0411\u0414, \u043E\u0431\u0449\u0438\u0435 \u0442\u0438\u043F\u044B, \u0440\u043E\u0443\u0442\u0438\u043D\u0433, \u043A\u043E\u043D\u0444\u0438\u0433 \u0441\u0431\u043E\u0440\u043A\u0438). \u0414\u0432\u0430 \u0434\u0440\u043E\u043D\u0430, \u043F\u0440\u0430\u0432\u044F\u0449\u0438\u0435 \u0442\u0430\u043A\u043E\u0435 \u043E\u0434\u043D\u043E\u0432\u0440\u0435\u043C\u0435\u043D\u043D\u043E, \u0433\u0430\u0440\u0430\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u043E \u0440\u0430\u0441\u0445\u043E\u0434\u044F\u0442\u0441\u044F \u0432 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0435.",
11134
+ "\u0427\u0442\u043E \u0432 \u043D\u0451\u043C \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C:",
11135
+ "1. **\u0427\u0442\u043E \u044D\u0442\u043E \u0437\u0430 \u043F\u0440\u043E\u0434\u0443\u043A\u0442 \u0438 \u043D\u0430 \u0447\u0451\u043C \u043D\u0430\u043F\u0438\u0441\u0430\u043D** \u2014 \u043A\u043E\u0440\u043E\u0442\u043A\u043E, \u0447\u0442\u043E\u0431\u044B \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0430\u0433\u0435\u043D\u0442 \u043D\u0435 \u0432\u044B\u0432\u043E\u0434\u0438\u043B \u044D\u0442\u043E \u0438\u0437 \u043A\u043E\u0434\u0430 \u0437\u0430\u043D\u043E\u0432\u043E.",
11136
+ "2. **\u041E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435** \u2014 \u0447\u0435\u043C \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F \u0431\u0430\u0437\u0430, \u0447\u0435\u043C \u043D\u0430\u043A\u0430\u0442\u044B\u0432\u0430\u044E\u0442\u0441\u044F \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u043A\u0430\u043A\u0438\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043D\u0443\u0436\u043D\u044B (\u0422\u041E\u041B\u042C\u041A\u041E \u0418\u041C\u0415\u041D\u0410; \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F \u044D\u0442\u043E \u0441\u0435\u043A\u0440\u0435\u0442\u044B, \u0430 \u0444\u0430\u0439\u043B \u0443\u0435\u0434\u0435\u0442 \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u043A\u0430\u043A \u043E\u0431\u044B\u0447\u043D\u044B\u0439 \u0442\u0435\u043A\u0441\u0442). \u042D\u0442\u043E \u043F\u0440\u043E \u0442\u043E, \u0447\u0435\u043C \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E\u0442 \u0421\u0410\u041C\u041E \u041F\u0420\u0418\u041B\u041E\u0416\u0415\u041D\u0418\u0415, \u0430 \u043D\u0435 \u0442\u0435\u0441\u0442\u044B: \u0432\u043E \u043C\u043D\u043E\u0433\u0438\u0445 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F\u0445 \u0442\u0435\u0441\u0442\u043E\u0432\u0430\u044F \u0431\u0430\u0437\u0430 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u0430\u044F \u2014 \u0441\u0432\u043E\u0439 compose-\u0444\u0430\u0439\u043B, \u0441\u0432\u043E\u0439 \u0441\u043A\u0440\u0438\u043F\u0442, \u0441\u0432\u043E\u0439 \u043F\u043E\u0440\u0442, \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u0430\u044F \u0432\u0438\u0434\u0430 `TEST_DATABASE_URL`. \u041E\u043D\u0430 \u0441\u044E\u0434\u0430 \u041D\u0415 \u0438\u0434\u0451\u0442: \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u043D\u0430\u043A\u0430\u0442\u0430\u043D\u043D\u044B\u0435 \u0432 \u0442\u0435\u0441\u0442\u043E\u0432\u0443\u044E \u0431\u0430\u0437\u0443, \u043E\u0441\u0442\u0430\u0432\u044F\u0442 \u0440\u0430\u0431\u043E\u0447\u0443\u044E \u043D\u0430 \u0441\u0442\u0430\u0440\u043E\u0439 \u0441\u0445\u0435\u043C\u0435, \u0430 \u0430\u0433\u0435\u043D\u0442 \u0431\u0443\u0434\u0435\u0442 \u0441\u0447\u0438\u0442\u0430\u0442\u044C \u0435\u0451 \u043E\u0431\u043D\u043E\u0432\u043B\u0451\u043D\u043D\u043E\u0439. \u0418\u0449\u0438 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0432\u0445\u043E\u0434: \u0441\u043A\u0440\u0438\u043F\u0442 \u0437\u0430\u043F\u0443\u0441\u043A\u0430 \u0432 `scripts/`, `dev`/`start` \u0432 \u043C\u0430\u043D\u0438\u0444\u0435\u0441\u0442\u0435, \u0440\u0430\u0437\u0434\u0435\u043B README \xAB\u043A\u0430\u043A \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C\xBB.",
11137
+ "3. **\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0438** \u2014 \u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0442\u044C \u0438 \u0447\u0442\u043E \u0437\u0434\u0435\u0441\u044C \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F \u0437\u0435\u043B\u0451\u043D\u044B\u043C (\u0441\u043B\u043E\u0432\u0430\u043C\u0438: \u0447\u0442\u043E \u0433\u043E\u043D\u044F\u0435\u0442\u0441\u044F \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E, \u0447\u0442\u043E \u0442\u043E\u043B\u044C\u043A\u043E \u043D\u0430 CI, \u043A\u0430\u043A\u043E\u0439 \u0447\u0435\u043A \u0441\u043B\u0443\u0436\u0438\u0442 \u0444\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u043C \u043F\u0440\u0438\u0437\u043D\u0430\u043A\u043E\u043C \u0437\u0435\u043B\u0435\u043D\u0438).",
11138
+ "4. **\u0421\u043E\u0433\u043B\u0430\u0448\u0435\u043D\u0438\u044F** \u2014 \u044F\u0437\u044B\u043A \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u043A\u043E\u043C\u043C\u0438\u0442\u043E\u0432, \u043D\u0443\u0436\u0435\u043D \u043B\u0438 \u0441\u043B\u0430\u0433 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0438, \u0431\u0430\u0437\u043E\u0432\u0430\u044F \u0432\u0435\u0442\u043A\u0430.",
11139
+ "5. **\u0417\u043E\u043D\u044B \u043A\u043E\u0434\u0430** \u2014 \u043F\u0430\u043A\u0435\u0442\u044B, \u043C\u043E\u0434\u0443\u043B\u0438, \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0438 \u0444\u0438\u0447 \u0421 \u041F\u0423\u0422\u042F\u041C\u0418. \u041F\u043E \u043D\u0438\u043C \u0440\u043E\u0439 \u0440\u0430\u0437\u0432\u043E\u0434\u0438\u0442 \u043F\u0430\u0440\u0430\u043B\u043B\u0435\u043B\u044C\u043D\u044B\u0435 \u0437\u0430\u0434\u0430\u0447\u0438, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u0437\u043E\u043D\u0430 \u0431\u0435\u0437 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 \u0431\u0435\u0441\u043F\u043E\u043B\u0435\u0437\u043D\u0430; \u0434\u0440\u043E\u0431\u0438 \u043F\u043E \u0420\u0415\u0410\u041B\u042C\u041D\u041E\u0419 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0435 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F, \u0430 \u043D\u0435 \u043F\u043E \u043A\u0440\u0430\u0441\u0438\u0432\u044B\u043C \u0441\u043B\u043E\u0432\u0430\u043C.",
11140
+ "6. **\u041E\u0431\u0449\u0438\u0439 \u0445\u0440\u0435\u0431\u0435\u0442** \u2014 \u0444\u0430\u0439\u043B\u044B \u0438 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043A\u0430\u0442\u044F\u0442\u0441\u044F \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u043C\u0438 PR \u0438 \u043D\u0435 \u043F\u0430\u0440\u0430\u043B\u043B\u0435\u043B\u044F\u0442\u0441\u044F (\u0441\u0445\u0435\u043C\u0430 \u0411\u0414, \u043E\u0431\u0449\u0438\u0435 \u0442\u0438\u043F\u044B, \u0440\u043E\u0443\u0442\u0438\u043D\u0433, \u043A\u043E\u043D\u0444\u0438\u0433 \u0441\u0431\u043E\u0440\u043A\u0438). \u0414\u0432\u0430 \u0430\u0433\u0435\u043D\u0442\u0430, \u043F\u0440\u0430\u0432\u044F\u0449\u0438\u0435 \u0442\u0430\u043A\u043E\u0435 \u043E\u0434\u043D\u043E\u0432\u0440\u0435\u043C\u0435\u043D\u043D\u043E, \u0433\u0430\u0440\u0430\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u043E \u0440\u0430\u0441\u0445\u043E\u0434\u044F\u0442\u0441\u044F \u0432 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0435.",
10830
11141
  "",
10831
11142
  "\u0413\u0414\u0415 \u0421\u041C\u041E\u0422\u0420\u0415\u0422\u042C (\u043D\u0435 \u0447\u0438\u0442\u0430\u0439 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0446\u0435\u043B\u0438\u043A\u043E\u043C \u2014 \u0441\u0435\u0441\u0441\u0438\u044F \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0435\u043D\u0430 \u043F\u043E\u0442\u043E\u043B\u043A\u043E\u043C \u0442\u0440\u0430\u0442): README \u0438 \u0444\u0430\u0439\u043B\u044B \u043F\u0440\u0430\u0432\u0438\u043B \u0434\u043B\u044F \u0430\u0433\u0435\u043D\u0442\u043E\u0432 (CLAUDE.md, AGENTS.md, .cursorrules, \u043A\u0430\u0442\u0430\u043B\u043E\u0433 .claude/skills), \u043C\u0430\u043D\u0438\u0444\u0435\u0441\u0442 \u043F\u0430\u043A\u0435\u0442\u043D\u043E\u0433\u043E \u043C\u0435\u043D\u0435\u0434\u0436\u0435\u0440\u0430 \u0438 \u0435\u0433\u043E scripts, \u043A\u043E\u043D\u0444\u0438\u0433 \u043C\u043E\u043D\u043E\u0440\u0435\u043F\u043E (workspaces/turbo/nx/lerna), docker-compose \u0438 Makefile, .env.example (\u0418\u041C\u0415\u041D\u0410 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0445), \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0439, CI-\u0432\u043E\u0440\u043A\u0444\u043B\u043E\u0443 (.github/workflows \u0438 \u0430\u043D\u0430\u043B\u043E\u0433\u0438), \u0432\u0435\u0440\u0445\u043D\u0438\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u043E\u0432 \u043A\u043E\u0434\u0430, `git log --oneline -30` (\u044F\u0437\u044B\u043A \u043A\u043E\u043C\u043C\u0438\u0442\u043E\u0432 \u0438 \u0435\u0441\u0442\u044C \u043B\u0438 \u0432 \u043D\u0438\u0445 \u0441\u043B\u0430\u0433 \u0437\u0430\u0434\u0430\u0447\u0438) \u0438 `git symbolic-ref refs/remotes/origin/HEAD` (\u0431\u0430\u0437\u043E\u0432\u0430\u044F \u0432\u0435\u0442\u043A\u0430).",
10832
11143
  "",
10833
11144
  "\u0427\u0415\u0413\u041E \u041D\u0415 \u0414\u0415\u041B\u0410\u0422\u042C:",
10834
- "\u2022 \u041D\u0418\u0427\u0415\u0413\u041E \u041D\u0415 \u041F\u0420\u0410\u0412\u042C \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438: \u043D\u0438 \u0444\u0430\u0439\u043B\u043E\u0432, \u043D\u0438 \u0432\u0435\u0442\u043E\u043A, \u043D\u0438 \u043A\u043E\u043C\u043C\u0438\u0442\u043E\u0432. \u0422\u044B \u0447\u0438\u0442\u0430\u0435\u0448\u044C, \u0430 \u043D\u0435 \u0447\u0438\u043D\u0438\u0448\u044C. \u041D\u0435\u0442 CI \u0438\u043B\u0438 \u0442\u0435\u0441\u0442\u043E\u0432 \u2014 \u0442\u0430\u043A \u0438 \u0437\u0430\u043F\u0438\u0448\u0438, \u044D\u0442\u043E \u0444\u0430\u043A\u0442 \u043E \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438, \u0430 \u043D\u0435 \u0437\u0430\u0434\u0430\u0447\u0430 \u0442\u0435\u0431\u0435.",
10835
- "\u2022 \u041D\u0415 \u0412\u042B\u0414\u0423\u041C\u042B\u0412\u0410\u0419 \u043A\u043E\u043C\u0430\u043D\u0434\u044B. \u041A\u0430\u0436\u0434\u0430\u044F \u043A\u043E\u043C\u0430\u043D\u0434\u0430, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u0442\u044B \u043F\u0438\u0448\u0435\u0448\u044C, \u0434\u043E\u043B\u0436\u043D\u0430 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0442\u044C\u0441\u044F \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0426\u0415\u041B\u0418\u041A\u041E\u041C \u2014 \u0441\u0442\u0440\u043E\u043A\u043E\u0439, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u043C\u043E\u0436\u043D\u043E \u0441\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C (scripts, \u043C\u0430\u043D\u0438\u0444\u0435\u0441\u0442 \u043F\u0430\u043A\u0435\u0442\u0430, compose, Makefile, \u0432\u043E\u0440\u043A\u0444\u043B\u043E\u0443, README) \u2014 \u043F\u043E \u044D\u0442\u0438\u043C \u043A\u043E\u043C\u0430\u043D\u0434\u0430\u043C \u0434\u0440\u043E\u043D \u0431\u0443\u0434\u0435\u0442 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0442\u044C \u0447\u0443\u0436\u0443\u044E \u0431\u0430\u0437\u0443. \u041D\u0435 \u043D\u0430\u0448\u0451\u043B \u2014 \u043F\u043E\u043B\u0435 \u043D\u0435 \u043F\u0440\u0438\u0441\u044B\u043B\u0430\u0439.",
10836
- "\u2022 \u041D\u0415 \u0421\u041A\u041B\u0415\u0418\u0412\u0410\u0419 \u043A\u043E\u043C\u0430\u043D\u0434\u0443 \u0438\u0437 \u043A\u0443\u0441\u043A\u043E\u0432. \u0414\u0432\u0435 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0438\u0435 \u0441\u0442\u0440\u043E\u043A\u0438, \u0441\u043E\u0435\u0434\u0438\u043D\u0451\u043D\u043D\u044B\u0435 \u0442\u043E\u0431\u043E\u0439 \u0432 \u043E\u0434\u043D\u0443 (\u043F\u043E\u0434\u0441\u0442\u0430\u0432\u0438\u043B URL \u0438\u0437 \u043E\u0434\u043D\u043E\u0433\u043E \u0441\u043A\u0440\u0438\u043F\u0442\u0430 \u0432 \u0432\u044B\u0437\u043E\u0432 \u0438\u0437 \u0434\u0440\u0443\u0433\u043E\u0433\u043E, \u0434\u043E\u043F\u0438\u0441\u0430\u043B \u0444\u043B\u0430\u0433, \u043E\u0431\u0435\u0440\u043D\u0443\u043B \u0432 \u043F\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u043D\u0438\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0439), \u0434\u0430\u044E\u0442 \u043A\u043E\u043C\u0430\u043D\u0434\u0443, \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043D\u0435\u0442 \u0438 \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u043D\u0438\u043A\u0442\u043E \u043D\u0438\u043A\u043E\u0433\u0434\u0430 \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B. \u0412\u044B\u0433\u043B\u044F\u0434\u0438\u0442 \u043E\u043D\u0430 \u043F\u0440\u0438 \u044D\u0442\u043E\u043C \u043A\u0430\u043A \u0444\u0430\u043A\u0442. \u041D\u0435 \u0443\u0432\u0435\u0440\u0435\u043D, \u0447\u0442\u043E \u0441\u0442\u0440\u043E\u043A\u0430 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0435\u0442\u0441\u044F \u0440\u043E\u0432\u043D\u043E \u0432 \u0442\u0430\u043A\u043E\u043C \u0432\u0438\u0434\u0435, \u2014 \u043D\u0435 \u043F\u0440\u0438\u0441\u044B\u043B\u0430\u0439 \u0435\u0451.",
10837
- "\u2022 \u041F\u043E\u043B\u0435, \u043F\u0440\u043E \u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u0441\u043A\u0430\u0437\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E, \u041F\u0420\u041E\u041F\u0423\u0421\u0422\u0418 \u0446\u0435\u043B\u0438\u043A\u043E\u043C. \u041F\u0440\u043E\u043F\u0443\u0441\u043A \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0441\u0442\u0438\u0440\u0430\u0435\u0442; \u0432\u044B\u0434\u0443\u043C\u0430\u043D\u043D\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 \u0441\u0442\u0438\u0440\u0430\u0435\u0442 \u043F\u0440\u0430\u0432\u0434\u0443.",
11145
+ `\u2022 \u041D\u0415 \u041F\u0420\u0410\u0412\u042C \u041D\u0418\u0427\u0415\u0413\u041E, \u041A\u0420\u041E\u041C\u0415 \`${input.briefPath}\`. \u041D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E \u0434\u0440\u0443\u0433\u043E\u0433\u043E \u0444\u0430\u0439\u043B\u0430, \u043D\u0438 \u0432\u0435\u0442\u043E\u043A, \u043D\u0438 \u043A\u043E\u043C\u043C\u0438\u0442\u043E\u0432, \u043D\u0438 PR \u2014 \u043A\u043E\u043C\u043C\u0438\u0442 \u0438 PR \u0441\u0434\u0435\u043B\u0430\u0435\u0442 \u0440\u043E\u0439 \u0441\u0430\u043C. CLAUDE.md \u041D\u0415 \u0422\u0420\u041E\u0413\u0410\u0419 \u0412\u041E\u0412\u0421\u0415: \u0441\u0442\u0440\u043E\u043A\u0443 \u0438\u043C\u043F\u043E\u0440\u0442\u0430 \u0442\u0443\u0434\u0430 \u0434\u043E\u043F\u0438\u0448\u0435\u0442 \u0440\u043E\u0439, \u044D\u0442\u043E \u043D\u0435 \u0442\u0432\u043E\u044F \u0440\u0430\u0431\u043E\u0442\u0430. \u041D\u0435\u0442 CI \u0438\u043B\u0438 \u0442\u0435\u0441\u0442\u043E\u0432 \u2014 \u0442\u0430\u043A \u0438 \u0437\u0430\u043F\u0438\u0448\u0438, \u044D\u0442\u043E \u0444\u0430\u043A\u0442 \u043E \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438, \u0430 \u043D\u0435 \u0437\u0430\u0434\u0430\u0447\u0430 \u0442\u0435\u0431\u0435.`,
11146
+ "\u2022 \u041D\u0415 \u0412\u042B\u0414\u0423\u041C\u042B\u0412\u0410\u0419 \u043A\u043E\u043C\u0430\u043D\u0434\u044B. \u041A\u0430\u0436\u0434\u0430\u044F \u043A\u043E\u043C\u0430\u043D\u0434\u0430, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u0442\u044B \u043F\u0438\u0448\u0435\u0448\u044C, \u0434\u043E\u043B\u0436\u043D\u0430 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0442\u044C\u0441\u044F \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0426\u0415\u041B\u0418\u041A\u041E\u041C \u2014 \u0441\u0442\u0440\u043E\u043A\u043E\u0439, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u043C\u043E\u0436\u043D\u043E \u0441\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C (scripts, \u043C\u0430\u043D\u0438\u0444\u0435\u0441\u0442 \u043F\u0430\u043A\u0435\u0442\u0430, compose, Makefile, \u0432\u043E\u0440\u043A\u0444\u043B\u043E\u0443, README) \u2014 \u043F\u043E \u044D\u0442\u0438\u043C \u043A\u043E\u043C\u0430\u043D\u0434\u0430\u043C \u0430\u0433\u0435\u043D\u0442 \u0431\u0443\u0434\u0435\u0442 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0442\u044C \u0447\u0443\u0436\u0443\u044E \u0431\u0430\u0437\u0443. \u041D\u0435 \u043D\u0430\u0448\u0451\u043B \u2014 \u0442\u0430\u043A \u0438 \u043D\u0430\u043F\u0438\u0448\u0438, \u0447\u0442\u043E \u043D\u0435 \u043D\u0430\u0448\u0451\u043B.",
11147
+ "\u2022 \u041D\u0415 \u0421\u041A\u041B\u0415\u0418\u0412\u0410\u0419 \u043A\u043E\u043C\u0430\u043D\u0434\u0443 \u0438\u0437 \u043A\u0443\u0441\u043A\u043E\u0432. \u0414\u0432\u0435 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0438\u0435 \u0441\u0442\u0440\u043E\u043A\u0438, \u0441\u043E\u0435\u0434\u0438\u043D\u0451\u043D\u043D\u044B\u0435 \u0442\u043E\u0431\u043E\u0439 \u0432 \u043E\u0434\u043D\u0443 (\u043F\u043E\u0434\u0441\u0442\u0430\u0432\u0438\u043B URL \u0438\u0437 \u043E\u0434\u043D\u043E\u0433\u043E \u0441\u043A\u0440\u0438\u043F\u0442\u0430 \u0432 \u0432\u044B\u0437\u043E\u0432 \u0438\u0437 \u0434\u0440\u0443\u0433\u043E\u0433\u043E, \u0434\u043E\u043F\u0438\u0441\u0430\u043B \u0444\u043B\u0430\u0433, \u043E\u0431\u0435\u0440\u043D\u0443\u043B \u0432 \u043F\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u043D\u0438\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0439), \u0434\u0430\u044E\u0442 \u043A\u043E\u043C\u0430\u043D\u0434\u0443, \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043D\u0435\u0442 \u0438 \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u043D\u0438\u043A\u0442\u043E \u043D\u0438\u043A\u043E\u0433\u0434\u0430 \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B. \u0412\u044B\u0433\u043B\u044F\u0434\u0438\u0442 \u043E\u043D\u0430 \u043F\u0440\u0438 \u044D\u0442\u043E\u043C \u043A\u0430\u043A \u0444\u0430\u043A\u0442.",
11148
+ "\u2022 \u0420\u0430\u0437\u0434\u0435\u043B, \u043F\u0440\u043E \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0441\u043A\u0430\u0437\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E, \u041F\u0420\u041E\u041F\u0423\u0421\u0422\u0418 \u0446\u0435\u043B\u0438\u043A\u043E\u043C. \u041F\u0440\u043E\u043F\u0443\u0441\u043A \u0447\u0435\u0441\u0442\u0435\u043D; \u0432\u044B\u0434\u0443\u043C\u0430\u043D\u043D\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 \u043D\u0435\u0442.",
10838
11149
  "\u2022 \u041D\u0438\u043A\u0430\u043A\u0438\u0445 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0439 \u0441\u0435\u043A\u0440\u0435\u0442\u043E\u0432, \u0442\u043E\u043A\u0435\u043D\u043E\u0432 \u0438 \u043F\u0430\u0440\u043E\u043B\u0435\u0439 \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0438\u043C\u0435\u043D\u0430 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0445.",
10839
11150
  "\u2022 \u041A\u043E\u043C\u0430\u043D\u0434\u044B \u043F\u043E\u0434\u043D\u044F\u0442\u0438\u044F \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u041D\u0415 \u0417\u0410\u041F\u0423\u0421\u041A\u0410\u0419 (\u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043D\u0430 \u0436\u0438\u0432\u043E\u0439 \u0431\u0430\u0437\u0435 \u2014 \u043D\u0435 \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0430).",
10840
11151
  "",
10841
- known,
11152
+ describeKnownBrief(input.brief ?? null, input.briefPath),
10842
11153
  "",
10843
- `\u041A\u0410\u041A \u0421\u0414\u0410\u0422\u042C. \u0417\u0430\u043F\u0438\u0448\u0438 \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u041E\u0414\u041D\u0418\u041C JSON-\u043E\u0431\u044A\u0435\u043A\u0442\u043E\u043C \u0432 \u0444\u0430\u0439\u043B \`${input.outFile}\` (\u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0438\u0448\u0438, \u0435\u0441\u043B\u0438 \u043E\u043D \u0435\u0441\u0442\u044C) \u2014 \u0438 \u043D\u0430 \u044D\u0442\u043E\u043C \u0437\u0430\u043A\u043E\u043D\u0447\u0438: \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0434\u0435\u043B\u0430\u0439 \u0438 \u043D\u0435 \u0432\u044B\u0432\u043E\u0434\u0438 \u0432 stdout \u043D\u0438\u0447\u0435\u0433\u043E, \u043A\u0440\u043E\u043C\u0435 \u043A\u043E\u0440\u043E\u0442\u043A\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u0438 \xAB\u0433\u043E\u0442\u043E\u0432\u043E\xBB. \u0424\u0430\u0439\u043B \u0447\u0438\u0442\u0430\u0435\u0442 \u0434\u0435\u043C\u043E\u043D \u0440\u043E\u044F; \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0443\u0432\u0438\u0434\u0438\u0442 \u0442\u0432\u043E\u0451 \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043D\u0430 \u044D\u043A\u0440\u0430\u043D\u0435 \u043F\u0440\u0438\u0451\u043C\u043A\u0438 \u0438 \u0440\u0435\u0448\u0438\u0442, \u0447\u0442\u043E \u043F\u0440\u0438\u043D\u044F\u0442\u044C.`,
11154
+ `\u0424\u0410\u0419\u041B \u0410\u0413\u0415\u041D\u0422-\u041D\u0415\u0419\u0422\u0420\u0410\u041B\u042C\u041D\u042B\u0419: \u043E\u0431\u044B\u0447\u043D\u044B\u0439 markdown \u043F\u0440\u043E \u0420\u0415\u041F\u041E\u0417\u0418\u0422\u041E\u0420\u0418\u0419, \u0431\u0435\u0437 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439 \u043A \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u043E\u043C\u0443 \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044E \u0438 \u0431\u0435\u0437 claude-\u0441\u043F\u0435\u0446\u0438\u0444\u0438\u043A\u0438. \u0415\u0433\u043E \u0447\u0438\u0442\u0430\u044E\u0442 \u0440\u0430\u0437\u043D\u044B\u0435 \u0430\u0433\u0435\u043D\u0442\u044B, \u043A\u0430\u0436\u0434\u044B\u0439 \u0441\u0432\u043E\u0438\u043C \u0441\u043F\u043E\u0441\u043E\u0431\u043E\u043C.`,
10844
11155
  "",
10845
- "\u0424\u043E\u0440\u043C\u0430 \u043E\u0442\u0432\u0435\u0442\u0430 (\u043B\u0438\u0448\u043D\u0438\u0435 \u043A\u043B\u044E\u0447\u0438 \u043D\u0435 \u0434\u043E\u0431\u0430\u0432\u043B\u044F\u0439, \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0435 \u043F\u043E\u043B\u044F \u043F\u0440\u043E\u0441\u0442\u043E \u043D\u0435 \u043F\u0438\u0448\u0438):",
10846
- "```json",
11156
+ `\u041A\u0410\u041A \u0421\u0414\u0410\u0422\u042C. \u0417\u0430\u043F\u0438\u0448\u0438 \u0444\u0430\u0439\u043B \`${input.briefPath}\` (\u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0438\u0448\u0438, \u0435\u0441\u043B\u0438 \u043E\u043D \u0435\u0441\u0442\u044C) \u2014 \u0438 \u043D\u0430 \u044D\u0442\u043E\u043C \u0437\u0430\u043A\u043E\u043D\u0447\u0438: \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0434\u0435\u043B\u0430\u0439 \u0438 \u043D\u0435 \u0432\u044B\u0432\u043E\u0434\u0438 \u0432 stdout \u043D\u0438\u0447\u0435\u0433\u043E, \u043A\u0440\u043E\u043C\u0435 \u043A\u043E\u0440\u043E\u0442\u043A\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u0438 \xAB\u0433\u043E\u0442\u043E\u0432\u043E\xBB.`,
11157
+ "",
11158
+ "\u0421\u043A\u0435\u043B\u0435\u0442 (\u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0438 \u0434\u0435\u0440\u0436\u0438 \u044D\u0442\u0438, \u043F\u0443\u0441\u0442\u044B\u0435 \u0440\u0430\u0437\u0434\u0435\u043B\u044B \u043D\u0435 \u043F\u0438\u0448\u0438):",
11159
+ "```markdown",
10847
11160
  SHAPE,
10848
11161
  "```",
10849
11162
  "",
10850
- "\u0422\u0412\u041E\u0401 \u041F\u0420\u0415\u0414\u041B\u041E\u0416\u0415\u041D\u0418\u0415 \u041D\u0415 \u041F\u0420\u0418\u041C\u0415\u041D\u042F\u0415\u0422\u0421\u042F \u041C\u041E\u041B\u0427\u0410: \u0435\u0433\u043E \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A, \u043F\u043E \u043E\u0434\u043D\u043E\u043C\u0443 \u043F\u043E\u043B\u044E. \u041F\u043E\u044D\u0442\u043E\u043C\u0443 \u043D\u0435 \u0437\u0430\u043D\u0438\u0436\u0430\u0439 \u043F\u043E\u0434\u0440\u043E\u0431\u043D\u043E\u0441\u0442\u044C \u0438\u0437 \u043E\u0441\u0442\u043E\u0440\u043E\u0436\u043D\u043E\u0441\u0442\u0438 \u2014 \u043D\u043E \u0438 \u043D\u0435 \u0432\u044B\u0434\u0430\u0432\u0430\u0439 \u0434\u043E\u0433\u0430\u0434\u043A\u0443 \u0437\u0430 \u043D\u0430\u0431\u043B\u044E\u0434\u0435\u043D\u0438\u0435: \u0442\u043E, \u0432 \u0447\u0451\u043C \u043D\u0435 \u0443\u0432\u0435\u0440\u0435\u043D, \u043B\u0443\u0447\u0448\u0435 \u043E\u0431\u044A\u044F\u0441\u043D\u0438 \u0441\u043B\u043E\u0432\u0430\u043C\u0438 \u0432 `green`/`about`, \u0447\u0435\u043C \u0443\u0433\u0430\u0434\u044B\u0432\u0430\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439."
11163
+ "\u0422\u0412\u041E\u0419 \u0424\u0410\u0419\u041B \u0423\u0412\u0418\u0414\u0418\u0422 \u0427\u0415\u041B\u041E\u0412\u0415\u041A \u0412 PR, \u043D\u043E \u0438\u0441\u043F\u043E\u043B\u043D\u044F\u0442\u044C \u043F\u043E \u043D\u0435\u043C\u0443 \u0431\u0443\u0434\u0443\u0442 \u0430\u0433\u0435\u043D\u0442\u044B. \u041F\u043E\u044D\u0442\u043E\u043C\u0443 \u043F\u0438\u0448\u0438 \u043A\u043E\u0440\u043E\u0442\u043A\u043E \u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0435\u043C\u043E: \u043A\u043E\u043C\u0430\u043D\u0434\u0430, \u043F\u0443\u0442\u044C, \u0438\u043C\u044F \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0439. \u0422\u043E, \u0432 \u0447\u0451\u043C \u043D\u0435 \u0443\u0432\u0435\u0440\u0435\u043D, \u043E\u0431\u044A\u044F\u0441\u043D\u0438 \u0441\u043B\u043E\u0432\u0430\u043C\u0438 \u2014 \u044D\u0442\u043E \u0447\u0435\u0441\u0442\u043D\u0435\u0435, \u0447\u0435\u043C \u0443\u0433\u0430\u0434\u0430\u043D\u043D\u0430\u044F \u043A\u043E\u043C\u0430\u043D\u0434\u0430, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u043A\u0442\u043E-\u0442\u043E \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442."
10851
11164
  ].join("\n");
10852
11165
  }
10853
- function describeKnownProfile(profile) {
10854
- if (!profile) {
10855
- return "\u0420\u0410\u0417\u041C\u0415\u0422\u041A\u0418 \u0423 \u042D\u0422\u041E\u0413\u041E \u0420\u0415\u041F\u041E\u0417\u0418\u0422\u041E\u0420\u0418\u042F \u0415\u0429\u0401 \u041D\u0415\u0422 \u2014 \u0442\u044B \u0440\u0430\u0437\u043C\u0435\u0447\u0430\u0435\u0448\u044C \u0435\u0433\u043E \u043F\u0435\u0440\u0432\u044B\u043C. \u041F\u0440\u0435\u0434\u043B\u0430\u0433\u0430\u0439 \u0432\u0441\u0435 \u0448\u0435\u0441\u0442\u044C \u043F\u043E\u043B\u0435\u0439, \u043A\u0430\u043A\u0438\u0435 \u0441\u043C\u043E\u0436\u0435\u0448\u044C \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C \u043A\u043E\u0434\u043E\u043C.";
11166
+ function describeKnownBrief(brief, briefPath = BRIEF_PATH) {
11167
+ if (!brief?.trim()) {
11168
+ return `\u0411\u0420\u0418\u0424\u0410 \u0423 \u042D\u0422\u041E\u0413\u041E \u0420\u0415\u041F\u041E\u0417\u0418\u0422\u041E\u0420\u0418\u042F \u0415\u0429\u0401 \u041D\u0415\u0422 \u2014 \u0442\u044B \u043F\u0438\u0448\u0435\u0448\u044C \u0435\u0433\u043E \u043F\u0435\u0440\u0432\u044B\u043C. \u0417\u0430\u043F\u043E\u043B\u043D\u0438 \u0432\u0441\u0435 \u0440\u0430\u0437\u0434\u0435\u043B\u044B, \u043A\u0430\u043A\u0438\u0435 \u0441\u043C\u043E\u0436\u0435\u0448\u044C \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C \u043A\u043E\u0434\u043E\u043C.`;
10856
11169
  }
10857
- const filled = SCOUT_FIELDS.filter((f) => profile[f] !== void 0 && profile[f] !== null);
10858
- const byHuman = filled.filter((f) => profile.sources?.[f] === "human");
10859
- const lines = [
10860
- "\u0420\u0415\u041F\u041E\u0417\u0418\u0422\u041E\u0420\u0418\u0419 \u0423\u0416\u0415 \u0420\u0410\u0417\u041C\u0415\u0427\u0415\u041D \u2014 \u044D\u0442\u043E \u041F\u041E\u0412\u0422\u041E\u0420\u041D\u0410\u042F \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0430. \u0427\u0442\u043E \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E \u0441\u0435\u0439\u0447\u0430\u0441:",
10861
- "```json",
10862
- JSON.stringify(pickFields(profile), null, 2),
11170
+ return [
11171
+ `\u042D\u0422\u041E \u041F\u041E\u0412\u0422\u041E\u0420\u041D\u042B\u0419 \u041F\u0420\u041E\u0425\u041E\u0414 \u2014 \`${briefPath}\` \u0443\u0436\u0435 \u0435\u0441\u0442\u044C, \u0438 \u0432\u043E\u0442 \u043E\u043D \u0446\u0435\u043B\u0438\u043A\u043E\u043C:`,
11172
+ "```markdown",
11173
+ brief.trim(),
10863
11174
  "```",
10864
- "\u0422\u0432\u043E\u044F \u0437\u0430\u0434\u0430\u0447\u0430 \u0442\u0435\u043F\u0435\u0440\u044C \u2014 \u0420\u0410\u0417\u041D\u0418\u0426\u0410, \u0430 \u043D\u0435 \u043F\u0435\u0440\u0435\u0441\u043A\u0430\u0437: \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0436\u0438\u0432\u0451\u0442, \u043F\u0430\u043A\u0435\u0442\u044B \u0434\u043E\u0431\u0430\u0432\u043B\u044F\u044E\u0442\u0441\u044F, \u043A\u043E\u043C\u0430\u043D\u0434\u044B \u043C\u0435\u043D\u044F\u044E\u0442\u0441\u044F. \u041F\u043E\u043B\u0435, \u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u043F\u043E-\u043F\u0440\u0435\u0436\u043D\u0435\u043C\u0443 \u0432\u0435\u0440\u043D\u043E, \u041D\u0415 \u041F\u0420\u0418\u0421\u042B\u041B\u0410\u0419 \u0412\u041E\u0412\u0421\u0415 \u2014 \u043F\u0440\u043E\u043F\u0443\u0441\u043A \u0438 \u0435\u0441\u0442\u044C \xAB\u0442\u0443\u0442 \u0431\u0435\u0437 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439\xBB.",
10865
- "\xAB\u041F\u0440\u0438\u0441\u043B\u0430\u0442\u044C \u043A\u0430\u043A \u0435\u0441\u0442\u044C\xBB \u2014 \u041D\u0415 \u0442\u043E \u0436\u0435 \u0441\u0430\u043C\u043E\u0435, \u0438 \u044D\u0442\u043E \u0433\u043B\u0430\u0432\u043D\u0430\u044F \u043B\u043E\u0432\u0443\u0448\u043A\u0430 \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u043E\u0439 \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0438: \u0437\u0430\u043D\u043E\u0432\u043E \u0442\u044B \u043D\u0430\u043F\u0438\u0448\u0435\u0448\u044C \u0434\u0440\u0443\u0433\u0438\u043C\u0438 \u0441\u043B\u043E\u0432\u0430\u043C\u0438, \u043F\u043E\u0434\u0440\u043E\u0431\u043D\u0435\u0435 \u0438 \u0434\u043B\u0438\u043D\u043D\u0435\u0435, \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u043F\u043E\u043B\u0443\u0447\u0438\u0442 \u044D\u0442\u043E \u043A\u0430\u043A \xAB\u0438\u0437\u043C\u0435\u043D\u0438\u043B\u043E\u0441\u044C\xBB, \u0438 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0430\u044F \u043F\u0440\u0430\u0432\u043A\u0430 \u0443\u0442\u043E\u043D\u0435\u0442 \u0441\u0440\u0435\u0434\u0438 \u043F\u0435\u0440\u0435\u0444\u043E\u0440\u043C\u0443\u043B\u0438\u0440\u043E\u0432\u043E\u043A. \u041F\u0440\u0438\u0441\u044B\u043B\u0430\u0439 \u043F\u043E\u043B\u0435, \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u043A\u043E\u0434 \u043F\u0440\u044F\u043C\u043E \u041F\u0420\u041E\u0422\u0418\u0412\u041E\u0420\u0415\u0427\u0418\u0422 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043D\u043E\u043C\u0443: \u043A\u043E\u043C\u0430\u043D\u0434\u044B \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u0442, \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u043F\u0435\u0440\u0435\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D, \u043F\u043E\u044F\u0432\u0438\u043B\u0441\u044F \u043F\u0430\u043A\u0435\u0442, \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0441\u044F \u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0439 \u0437\u0435\u043B\u0435\u043D\u0438. \u0421\u0442\u0430\u043B\u043E \u043A\u0440\u0430\u0441\u0438\u0432\u0435\u0435 \u2014 \u043D\u0435 \u043F\u043E\u0432\u043E\u0434.",
10866
- "\u041F\u0440\u0438\u0441\u044B\u043B\u0430\u0435\u0448\u044C \u2014 \u043F\u0440\u0438\u0441\u044B\u043B\u0430\u0439 \u043F\u043E\u043B\u0435 \u0426\u0415\u041B\u0418\u041A\u041E\u041C (\u043E\u043D\u043E \u0437\u0430\u043C\u0435\u043D\u044F\u0435\u0442 \u043F\u0440\u0435\u0436\u043D\u0435\u0435), \u043D\u043E \u0440\u043E\u0432\u043D\u043E \u0441 \u0442\u043E\u0439 \u043F\u0440\u0430\u0432\u043A\u043E\u0439, \u0440\u0430\u0434\u0438 \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u043F\u0440\u0438\u0441\u043B\u0430\u043B: \u043E\u0441\u0442\u0430\u043B\u044C\u043D\u043E\u0435 \u0432\u043D\u0443\u0442\u0440\u0438 \u043D\u0435\u0433\u043E \u043E\u0441\u0442\u0430\u0432\u044C \u0414\u041E\u0421\u041B\u041E\u0412\u041D\u041E \u0442\u0430\u043A, \u043A\u0430\u043A \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E \u0441\u0435\u0439\u0447\u0430\u0441, \u0432\u043F\u043B\u043E\u0442\u044C \u0434\u043E \u0444\u043E\u0440\u043C\u0443\u043B\u0438\u0440\u043E\u0432\u043E\u043A. \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043E\u0434\u043D\u0443 \u0437\u043E\u043D\u0443 \u2014 \u044D\u0442\u043E \u0441\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0441\u043F\u0438\u0441\u043E\u043A \u0438 \u0434\u043E\u043F\u0438\u0441\u0430\u0442\u044C \u0432 \u043D\u0435\u0433\u043E \u0441\u0442\u0440\u043E\u043A\u0443, \u0430 \u043D\u0435 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u0430\u0442\u044C \u0441\u043F\u0438\u0441\u043E\u043A \u0437\u0430\u043D\u043E\u0432\u043E."
10867
- ];
10868
- if (byHuman.length) {
10869
- lines.push(
10870
- `\u26A0\uFE0F \u042D\u0422\u0418 \u041F\u041E\u041B\u042F \u0412\u041F\u0418\u0421\u0410\u041B \u0427\u0415\u041B\u041E\u0412\u0415\u041A: ${byHuman.join(", ")}. \u041E\u043D \u0437\u043D\u0430\u0435\u0442 \u043F\u0440\u043E \u043F\u0440\u043E\u0435\u043A\u0442 \u0442\u043E, \u0447\u0435\u0433\u043E \u0432 \u043A\u043E\u0434\u0435 \u043D\u0435 \u043D\u0430\u043F\u0438\u0441\u0430\u043D\u043E, \u2014 \u043D\u0435 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u044B\u0432\u0430\u0439 \u0438\u0445 \xAB\u0434\u043B\u044F \u043F\u043E\u0440\u044F\u0434\u043A\u0430\xBB \u0438 \u043D\u0435 \u043F\u0440\u0438\u0432\u043E\u0434\u0438 \u043A \u0441\u0432\u043E\u0435\u043C\u0443 \u0441\u0442\u0438\u043B\u044E. \u041C\u0435\u043D\u044F\u0439, \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u043A\u043E\u0434 \u043F\u0440\u044F\u043C\u043E \u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0440\u0435\u0447\u0438\u0442 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043D\u043E\u043C\u0443 (\u043A\u043E\u043C\u0430\u043D\u0434\u044B \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u0442, \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u043F\u0435\u0440\u0435\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D), \u0438 \u0442\u043E\u0433\u0434\u0430 \u043E\u0431\u044A\u044F\u0441\u043D\u0438 \u0440\u0430\u0441\u0445\u043E\u0436\u0434\u0435\u043D\u0438\u0435 \u0432 \u0442\u0435\u043A\u0441\u0442\u0435 \u043F\u043E\u043B\u044F: \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0443\u0432\u0438\u0434\u0438\u0442 \u0435\u0433\u043E \u043D\u0430 \u043F\u0440\u0438\u0451\u043C\u043A\u0435 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\u0439 \u043F\u043E\u043C\u0435\u0442\u043A\u043E\u0439 \u0438 \u0440\u0435\u0448\u0438\u0442 \u0441\u0430\u043C.`
10871
- );
10872
- }
10873
- return lines.join("\n");
10874
- }
10875
- function pickFields(profile) {
10876
- const out = {};
10877
- for (const f of SCOUT_FIELDS) if (profile[f] !== void 0 && profile[f] !== null) out[f] = profile[f];
10878
- return out;
11175
+ "\u0424\u0430\u0439\u043B \u043F\u0440\u0438\u043D\u0430\u0434\u043B\u0435\u0436\u0438\u0442 \u0442\u0435\u0431\u0435 \u0446\u0435\u043B\u0438\u043A\u043E\u043C: \u0442\u044B \u0435\u0433\u043E \u041F\u0415\u0420\u0415\u0417\u0410\u041F\u0418\u0421\u042B\u0412\u0410\u0415\u0428\u042C, \u0430 \u043D\u0435 \u0434\u043E\u043F\u0438\u0441\u044B\u0432\u0430\u0435\u0448\u044C. \u041F\u043E\u044D\u0442\u043E\u043C\u0443 \u043F\u0438\u0448\u0438 \u0435\u0433\u043E \u0437\u0430\u043D\u043E\u0432\u043E \u2014 \u043D\u043E \u043D\u0430\u0447\u043D\u0438 \u0441 \u044D\u0442\u043E\u0433\u043E \u0442\u0435\u043A\u0441\u0442\u0430 \u0438 \u043F\u0440\u0430\u0432\u044C \u0442\u043E\u043B\u044C\u043A\u043E \u0442\u043E, \u0447\u0435\u043C\u0443 \u043A\u043E\u0434 \u043F\u0440\u044F\u043C\u043E \u041F\u0420\u041E\u0422\u0418\u0412\u041E\u0420\u0415\u0427\u0418\u0422: \u043A\u043E\u043C\u0430\u043D\u0434\u044B \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u0442, \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u043F\u0435\u0440\u0435\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D, \u043F\u043E\u044F\u0432\u0438\u043B\u0441\u044F \u043F\u0430\u043A\u0435\u0442, \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0441\u044F \u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0439 \u0437\u0435\u043B\u0435\u043D\u0438.",
11176
+ "\u041E\u0441\u0442\u0430\u043B\u044C\u043D\u043E\u0435 \u043E\u0441\u0442\u0430\u0432\u044C \u0414\u041E\u0421\u041B\u041E\u0412\u041D\u041E, \u0432\u043F\u043B\u043E\u0442\u044C \u0434\u043E \u0444\u043E\u0440\u043C\u0443\u043B\u0438\u0440\u043E\u0432\u043E\u043A. \u042D\u0442\u043E \u043D\u0435 \u043F\u0435\u0434\u0430\u043D\u0442\u0438\u0437\u043C: \u043F\u0440\u0430\u0432\u043A\u0443 \u0441\u043C\u043E\u0442\u0440\u0438\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0432 \u0434\u0438\u0444\u0444 PR, \u0438 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u0430\u043D\u043D\u0430\u044F \u0434\u0440\u0443\u0433\u0438\u043C\u0438 \u0441\u043B\u043E\u0432\u0430\u043C\u0438 \u0432\u0435\u0440\u043D\u0430\u044F \u0441\u0442\u0440\u043E\u043A\u0430 \u0432\u044B\u0433\u043B\u044F\u0434\u0438\u0442 \u0442\u0430\u043C \u0440\u043E\u0432\u043D\u043E \u043A\u0430\u043A \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0435 \u2014 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0430\u044F \u043F\u0440\u0430\u0432\u043A\u0430 \u0443\u0442\u043E\u043D\u0435\u0442 \u0441\u0440\u0435\u0434\u0438 \u043F\u0435\u0440\u0435\u0444\u043E\u0440\u043C\u0443\u043B\u0438\u0440\u043E\u0432\u043E\u043A. \xAB\u0421\u0442\u0430\u043B\u043E \u043A\u0440\u0430\u0441\u0438\u0432\u0435\u0435\xBB \u2014 \u043D\u0435 \u043F\u043E\u0432\u043E\u0434 \u0434\u043B\u044F \u0441\u0442\u0440\u043E\u043A\u0438 \u0432 \u0434\u0438\u0444\u0444\u0435."
11177
+ ].join("\n");
10879
11178
  }
10880
- function parseScoutReport(text) {
10881
- const json = stripFence(text).trim();
10882
- if (!json) return { ok: false, error: "\u0440\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 (\u0444\u0430\u0439\u043B \u043F\u0443\u0441\u0442)" };
10883
- let raw;
10884
- try {
10885
- raw = JSON.parse(json);
10886
- } catch (e) {
10887
- return { ok: false, error: `\u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043D\u0435 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044F \u043A\u0430\u043A JSON: ${e.message}` };
10888
- }
10889
- if (raw == null || typeof raw !== "object" || Array.isArray(raw)) {
10890
- return { ok: false, error: "\u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C JSON-\u043E\u0431\u044A\u0435\u043A\u0442\u043E\u043C \u0441 \u043F\u043E\u043B\u044F\u043C\u0438 \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0438" };
10891
- }
10892
- const src = raw;
10893
- const report = {};
10894
- const fields = [];
10895
- for (const f of SCOUT_FIELDS) {
10896
- const value = src[f];
10897
- if (value === void 0 || value === null) continue;
10898
- report[f] = value;
10899
- fields.push(f);
10900
- }
10901
- if (fields.length === 0) {
10902
- const foreign = Object.keys(src).filter((k) => !SCOUT_FIELDS.includes(k));
10903
- if (foreign.length) {
10904
- return { ok: false, error: `\u0432 \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0438 \u043D\u0435\u0442 \u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E \u043F\u043E\u043B\u044F \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0438 (\u0435\u0441\u0442\u044C \u0442\u043E\u043B\u044C\u043A\u043E: ${foreign.slice(0, 5).join(", ")})` };
10905
- }
10906
- }
10907
- return { ok: true, report, fields };
11179
+ function parseBrief(text) {
11180
+ if (text == null) return { ok: false, error: "\u0440\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A \u043D\u0435 \u043D\u0430\u043F\u0438\u0441\u0430\u043B \u0444\u0430\u0439\u043B \u0431\u0440\u0438\u0444\u0430" };
11181
+ const body = stripFence(text).trim();
11182
+ if (!body) return { ok: false, error: "\u0444\u0430\u0439\u043B \u0431\u0440\u0438\u0444\u0430 \u043F\u0443\u0441\u0442" };
11183
+ if (body.startsWith("{")) return { ok: false, error: "\u0432 \u0444\u0430\u0439\u043B\u0435 \u0431\u0440\u0438\u0444\u0430 JSON, \u0430 \u043E\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044F markdown" };
11184
+ return { ok: true, text: body };
10908
11185
  }
10909
11186
  function stripFence(text) {
10910
- const fenced = /```(?:json)?\s*([\s\S]*?)```/.exec(text);
11187
+ const t = text.trim();
11188
+ if (!t.startsWith("```")) return text;
11189
+ const fenced = /^```(?:markdown|md)?\s*([\s\S]*?)```\s*$/.exec(t);
10911
11190
  return fenced?.[1] ?? text;
10912
11191
  }
10913
- function renderScoutOutcome(repoFullName, diff) {
10914
- const label = { new: "\u043D\u043E\u0432\u043E\u0435", changed: "\u0438\u0437\u043C\u0435\u043D\u0438\u043B\u043E\u0441\u044C", same: "\u043A\u0430\u043A \u0431\u044B\u043B\u043E", gone: "\u043D\u0435 \u0442\u0440\u043E\u0433\u0430\u0435\u0442" };
10915
- const changed = diff.filter((d) => d.change === "new" || d.change === "changed");
10916
- if (!changed.length) {
10917
- return [`\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${repoFullName}: \u043D\u043E\u0432\u043E\u0433\u043E \u043D\u0438\u0447\u0435\u0433\u043E \u2014 \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0441\u043E\u0432\u043F\u0430\u043B\u043E \u0441 \u0442\u0435\u043C, \u0447\u0442\u043E \u0443\u0436\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E.`];
10918
- }
10919
- const lines = [`\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${repoFullName}: \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u043E \u043A \u043F\u0440\u0438\u0451\u043C\u043A\u0435 ${changed.length} \u043F\u043E\u043B(\u044F/\u0435\u0439).`];
10920
- for (const d of changed) {
10921
- const human = d.overwritesHuman ? " \u26A0\uFE0F \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u0430\u043B\u043E \u0431\u044B \u043F\u0440\u0430\u0432\u043A\u0443 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430 \u2014 \u0440\u0435\u0448\u0438\u0442 \u043E\u043D \u0441\u0430\u043C" : "";
10922
- lines.push(` \u2022 ${d.field} \u2014 ${label[d.change] ?? d.change}${human}`);
10923
- }
10924
- const untouched = diff.filter((d) => d.change === "gone").map((d) => d.field);
10925
- if (untouched.length) lines.push(` \u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 \u043F\u0440\u043E\u043C\u043E\u043B\u0447\u0430\u043B\u0430 (\u043E\u0441\u0442\u0430\u043D\u0435\u0442\u0441\u044F \u043A\u0430\u043A \u0435\u0441\u0442\u044C): ${untouched.join(", ")}`);
10926
- lines.push("\u0416\u0438\u0432\u0430\u044F \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0430 \u041D\u0415 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0430: \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0435\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u043D\u0430 \u044D\u043A\u0440\u0430\u043D\u0435 \u043F\u0440\u0438\u0451\u043C\u043A\u0438.");
10927
- return lines;
11192
+ function withBriefHeader(body) {
11193
+ const withoutHeader = body.replace(/^\s*<!--[\s\S]*?-->\s*/, (m) => m.includes("swarm scout") ? "" : m);
11194
+ return `${BRIEF_HEADER}
11195
+
11196
+ ${withoutHeader.trim()}
11197
+ `;
11198
+ }
11199
+ function ensureBriefImport(current) {
11200
+ if (current == null) {
11201
+ const text2 = [
11202
+ "# CLAUDE.md",
11203
+ "",
11204
+ "\u0417\u043D\u0430\u043D\u0438\u0435 \u043E\u0431 \u044D\u0442\u043E\u043C \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0434\u043B\u044F \u0430\u0433\u0435\u043D\u0442\u043E\u0432. \u0411\u0440\u0438\u0444 \u043D\u0438\u0436\u0435 \u0432\u0435\u0434\u0451\u0442 \u0440\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A \u0440\u043E\u044F (`swarm scout`),",
11205
+ "\u043E\u0441\u0442\u0430\u043B\u044C\u043D\u043E\u0435 \u0432 \u044D\u0442\u043E\u043C \u0444\u0430\u0439\u043B\u0435 \u2014 \u0442\u0435\u0440\u0440\u0438\u0442\u043E\u0440\u0438\u044F \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430.",
11206
+ "",
11207
+ BRIEF_IMPORT,
11208
+ ""
11209
+ ].join("\n");
11210
+ return { text: text2, changed: true, created: true };
11211
+ }
11212
+ if (current.includes(BRIEF_IMPORT)) return { text: current, changed: false, created: false };
11213
+ const base3 = current.endsWith("\n") ? current : `${current}
11214
+ `;
11215
+ const text = `${base3}
11216
+ ${BRIEF_IMPORT}
11217
+ `;
11218
+ return { text, changed: true, created: false };
11219
+ }
11220
+ function renderScoutOutcome(repoFullName, opts) {
11221
+ return [
11222
+ `\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${repoFullName}: \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u043E ${opts.files.length} \u0444\u0430\u0439\u043B(\u043E\u0432) \u2014 ${opts.files.join(", ")}.`,
11223
+ `PR: ${opts.prUrl}`,
11224
+ "\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043B\u043E\u0441\u044C \u043C\u043E\u043B\u0447\u0430: \u043F\u043E\u043A\u0430 PR \u043D\u0435 \u0441\u043C\u0435\u0440\u0436\u0435\u043D, \u043F\u043E\u0432\u0435\u0434\u0435\u043D\u0438\u0435 \u0430\u0433\u0435\u043D\u0442\u043E\u0432 \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043D\u0435 \u043C\u0435\u043D\u044F\u0435\u0442\u0441\u044F."
11225
+ ];
10928
11226
  }
10929
11227
  async function runRepoScout(deps) {
10930
- const profile = await deps.readProfile();
11228
+ const briefPath = deps.briefPath ?? BRIEF_PATH;
11229
+ const before = deps.readFile(briefPath);
10931
11230
  deps.log(
10932
- profile ? `\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${deps.repoFullName}: \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0443\u0436\u0435 \u0440\u0430\u0437\u043C\u0435\u0447\u0435\u043D \u2014 \u0438\u0449\u0435\u043C \u0420\u0410\u0417\u041D\u0418\u0426\u0423 (\u0440\u0443\u0447\u043D\u044B\u0435 \u043F\u0440\u0430\u0432\u043A\u0438 \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0430 \u043D\u0435 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u044B\u0432\u0430\u0435\u0442 \u043C\u043E\u043B\u0447\u0430).` : `\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${deps.repoFullName}: \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0438 \u043D\u0435\u0442 \u2014 \u0440\u0430\u0437\u043C\u0435\u0447\u0430\u0435\u043C \u0432\u043F\u0435\u0440\u0432\u044B\u0435.`
11231
+ before ? `\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${deps.repoFullName}: \u0431\u0440\u0438\u0444 \u0443\u0436\u0435 \u0435\u0441\u0442\u044C \u2014 \u0438\u0449\u0435\u043C, \u0447\u0442\u043E \u0432 \u043D\u0451\u043C \u0443\u0441\u0442\u0430\u0440\u0435\u043B\u043E (${briefPath}).` : `\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${deps.repoFullName}: \u0431\u0440\u0438\u0444\u0430 \u043D\u0435\u0442 \u2014 \u043F\u0438\u0448\u0435\u043C \u0432\u043F\u0435\u0440\u0432\u044B\u0435 (${briefPath}).`
10933
11232
  );
10934
11233
  const prompt = buildScoutPrompt({
10935
11234
  repoFullName: deps.repoFullName,
10936
11235
  repoPath: deps.repoPath,
10937
- outFile: deps.outFile,
10938
- profile,
11236
+ briefPath,
11237
+ brief: before,
10939
11238
  ...deps.name ? { name: deps.name } : {}
10940
11239
  });
10941
- deps.clearOut();
10942
11240
  const code = await deps.runScout(prompt);
10943
- const raw = deps.readOut();
10944
- if (raw == null) {
10945
- deps.log(`\u0420\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A \u043D\u0435 \u043E\u0441\u0442\u0430\u0432\u0438\u043B \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u044F (${deps.outFile} \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u043D, \u043A\u043E\u0434 \u0432\u044B\u0445\u043E\u0434\u0430 ${code}). \u0420\u0430\u0437\u043C\u0435\u0442\u043A\u0430 \u043D\u0435 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0430.`);
10946
- return 1;
10947
- }
10948
- const parsed = parseScoutReport(raw);
11241
+ const parsed = parseBrief(deps.readFile(briefPath));
10949
11242
  if (!parsed.ok) {
10950
- deps.log(`\u041F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0440\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A\u0430 \u043D\u0435 \u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F: ${parsed.error}. \u0420\u0430\u0437\u043C\u0435\u0442\u043A\u0430 \u043D\u0435 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0430, \u0444\u0430\u0439\u043B \u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D: ${deps.outFile}`);
11243
+ deps.log(`\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${deps.repoFullName} \u043D\u0438 \u043A \u0447\u0435\u043C\u0443 \u043D\u0435 \u043F\u0440\u0438\u0432\u0435\u043B\u0430: ${parsed.error} (\u043A\u043E\u0434 \u0432\u044B\u0445\u043E\u0434\u0430 ${code}). \u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043C\u0435\u043D\u044F\u0435\u043C.`);
10951
11244
  return 1;
10952
11245
  }
10953
- if (parsed.fields.length === 0) {
11246
+ const files = [];
11247
+ const brief = withBriefHeader(parsed.text);
11248
+ if (brief !== before) {
11249
+ deps.writeFile(briefPath, brief);
11250
+ files.push(briefPath);
11251
+ }
11252
+ const claudeBefore = deps.readFile(CLAUDE_MD_PATH);
11253
+ const claude = ensureBriefImport(claudeBefore);
11254
+ if (claude.changed) {
11255
+ deps.writeFile(CLAUDE_MD_PATH, claude.text);
11256
+ files.push(CLAUDE_MD_PATH);
10954
11257
  deps.log(
10955
- profile ? `\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${deps.repoFullName}: \u0440\u0430\u0437\u043D\u0438\u0446\u044B \u043D\u0435\u0442 \u2014 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0441\u043E\u0432\u043F\u0430\u043B \u0441 \u0442\u0435\u043C, \u0447\u0442\u043E \u0443\u0436\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E. \u0420\u0430\u0437\u043C\u0435\u0442\u043A\u0430 \u043D\u0435 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0430.` : `\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${deps.repoFullName}: \u0440\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A \u043D\u0435 \u043D\u0430\u0448\u0451\u043B, \u0447\u0442\u043E \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0438\u0442\u044C, \u2014 \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0430 \u043E\u0441\u0442\u0430\u043B\u0430\u0441\u044C \u043F\u0443\u0441\u0442\u043E\u0439. \u042D\u0442\u043E \u043E\u0442\u0432\u0435\u0442, \u0430 \u043D\u0435 \u0441\u0431\u043E\u0439: \u043F\u0443\u0441\u0442\u043E\u0435 \u043F\u043E\u043B\u0435 \u0447\u0435\u0441\u0442\u043D\u0435\u0435 \u0432\u044B\u0434\u0443\u043C\u0430\u043D\u043D\u043E\u0433\u043E.`
11258
+ claude.created ? `${CLAUDE_MD_PATH} \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043D\u0435 \u0431\u044B\u043B\u043E \u2014 \u0437\u0430\u0432\u0451\u043B \u043C\u0438\u043D\u0438\u043C\u0430\u043B\u044C\u043D\u044B\u0439, \u0441\u043E \u0441\u0442\u0440\u043E\u043A\u043E\u0439 \u0438\u043C\u043F\u043E\u0440\u0442\u0430 \`${BRIEF_IMPORT}\`.` : `\u0414\u043E\u0431\u0430\u0432\u0438\u043B \u0432 ${CLAUDE_MD_PATH} \u0441\u0442\u0440\u043E\u043A\u0443 \u0438\u043C\u043F\u043E\u0440\u0442\u0430 \`${BRIEF_IMPORT}\` \u2014 \u0431\u043E\u043B\u044C\u0448\u0435 \u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0430 \u043D\u0435 \u0442\u0440\u043E\u043D\u0435\u0442.`
10956
11259
  );
11260
+ }
11261
+ if (!files.length) {
11262
+ deps.log(`\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${deps.repoFullName}: \u0440\u0430\u0437\u043D\u0438\u0446\u044B \u043D\u0435\u0442 \u2014 \u0431\u0440\u0438\u0444 \u0441\u043E\u0432\u043F\u0430\u043B \u0441 \u0442\u0435\u043C, \u0447\u0442\u043E \u0443\u0436\u0435 \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438. PR \u043D\u0435 \u043D\u0443\u0436\u0435\u043D.`);
10957
11263
  return 0;
10958
11264
  }
10959
- deps.log(`\u0420\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0438\u043B \u043F\u043E\u043B\u044F: ${parsed.fields.join(", ")}. \u041E\u0442\u043F\u0440\u0430\u0432\u043B\u044F\u044E \u0432 Taskless \u2014 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0442\u044C \u0431\u0443\u0434\u0435\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A.`);
10960
- const sent = await deps.report(parsed.report);
11265
+ const sent = await deps.publish(files);
10961
11266
  if (!sent.ok) {
10962
- deps.log(`\u041E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C: ${sent.error}. \u041E\u043D\u043E \u043E\u0441\u0442\u0430\u043B\u043E\u0441\u044C \u0432 ${deps.outFile} \u2014 \u043C\u043E\u0436\u043D\u043E \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0443.`);
11267
+ deps.log(`\u041E\u0442\u043A\u0440\u044B\u0442\u044C PR \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C: ${sent.error}. \u041F\u0440\u0430\u0432\u043A\u0438 \u043E\u0441\u0442\u0430\u043B\u0438\u0441\u044C \u0432 \u0440\u0430\u0431\u043E\u0447\u0435\u0439 \u043A\u043E\u043F\u0438\u0438 ${deps.repoPath} \u2014 \u043C\u043E\u0436\u043D\u043E \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C.`);
10963
11268
  return 1;
10964
11269
  }
10965
- deps.clearOut();
10966
- for (const line of renderScoutOutcome(deps.repoFullName, sent.diff)) deps.log(line);
11270
+ if (!sent.prUrl) {
11271
+ deps.log(`\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${deps.repoFullName}: \u0440\u0430\u0437\u043D\u0438\u0446\u044B \u043D\u0435\u0442 \u2014 git \u043D\u0435 \u0443\u0432\u0438\u0434\u0435\u043B \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439 \u0432 ${files.join(", ")}. PR \u043D\u0435 \u043D\u0443\u0436\u0435\u043D.`);
11272
+ return 0;
11273
+ }
11274
+ for (const line of renderScoutOutcome(deps.repoFullName, { files, prUrl: sent.prUrl })) deps.log(line);
10967
11275
  return 0;
10968
11276
  }
10969
11277
 
@@ -11682,11 +11990,12 @@ function usage() {
11682
11990
  \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u044C
11683
11991
  swarm ls [\u0441\u0442\u0430\u0442\u0443\u0441] \u043F\u0440\u043E\u0433\u043E\u043D\u044B \u0436\u0438\u0432\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0440\u043E\u0432\u043D\u043E \u0442\u0435, \u0447\u0442\u043E \u0432 \u0434\u0430\u0448\u0431\u043E\u0440\u0434\u0435
11684
11992
  swarm scout [--repo <path>]
11685
- \u0420\u0410\u0417\u0412\u0415\u0414\u041A\u0410 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F: \u0434\u0440\u043E\u043D \u0447\u0438\u0442\u0430\u0435\u0442 \u043A\u043E\u0434 \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 \u0438 \u043F\u0440\u0435\u0434\u043B\u0430\u0433\u0430\u0435\u0442
11686
- \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0443 \u2014 \u0437\u043E\u043D\u044B area:*, \u0447\u0435\u043C \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435, \u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0442\u044C \u0438 \u0447\u0442\u043E
11687
- \u0437\u0434\u0435\u0441\u044C \u0437\u0435\u043B\u0451\u043D\u043E\u0435, \u043A\u043E\u043D\u0432\u0435\u043D\u0446\u0438\u0438, \u043E\u0431\u0449\u0438\u0439 \u0445\u0440\u0435\u0431\u0435\u0442, \u0431\u0440\u0438\u0444. \u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0435\u0442:
11688
- \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0432 Taskless (\u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u043F\u0440\u043E\u0435\u043A\u0442\u0430).
11689
- \u041F\u043E\u0432\u0442\u043E\u0440\u043D\u044B\u0439 \u0437\u0430\u043F\u0443\u0441\u043A \u043D\u0430 \u0440\u0430\u0437\u043C\u0435\u0447\u0435\u043D\u043D\u043E\u043C \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0440\u0430\u0437\u043D\u0438\u0446\u0443
11993
+ \u0420\u0410\u0417\u0412\u0415\u0414\u041A\u0410 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F: \u0434\u0440\u043E\u043D \u0447\u0438\u0442\u0430\u0435\u0442 \u043A\u043E\u0434 \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 \u0438 \u043F\u0438\u0448\u0435\u0442
11994
+ docs/AGENT_BRIEF.md \u2014 \u0447\u0435\u043C \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435, \u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0442\u044C \u0438 \u0447\u0442\u043E
11995
+ \u0437\u0434\u0435\u0441\u044C \u0437\u0435\u043B\u0451\u043D\u043E\u0435, \u0441\u043E\u0433\u043B\u0430\u0448\u0435\u043D\u0438\u044F, \u0437\u043E\u043D\u044B \u043A\u043E\u0434\u0430, \u043E\u0431\u0449\u0438\u0439 \u0445\u0440\u0435\u0431\u0435\u0442. CLAUDE.md
11996
+ \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u0444\u0430\u0439\u043B \u0441\u0442\u0440\u043E\u043A\u043E\u0439 \u0438\u043C\u043F\u043E\u0440\u0442\u0430 (\u043E\u0434\u0438\u043D \u0440\u0430\u0437, \u0434\u0430\u043B\u044C\u0448\u0435 \u043D\u0435 \u0442\u0440\u043E\u0433\u0430\u0435\u0442\u0441\u044F).
11997
+ \u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0435\u0442 \u043C\u043E\u043B\u0447\u0430: \u043E\u0442\u0434\u0430\u0451\u0442\u0441\u044F \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u043C PR, \u043C\u0435\u0440\u0436\u0438\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A.
11998
+ \u041F\u043E\u0432\u0442\u043E\u0440\u043D\u044B\u0439 \u0437\u0430\u043F\u0443\u0441\u043A \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u044B\u0432\u0430\u0435\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u0444\u0430\u0439\u043B \u0431\u0440\u0438\u0444\u0430
11690
11999
  swarm zones [check|suggest <\u0442\u0435\u043A\u0441\u0442|slug>]
11691
12000
  \u0437\u043E\u043D\u044B \u043A\u043E\u0434\u0430, \u0432\u044B\u0432\u0435\u0434\u0435\u043D\u043D\u044B\u0435 \u0438\u0437 \u0438\u0441\u0442\u043E\u0440\u0438\u0438 \u0440\u0435\u043F\u043E (\u043A\u0430\u043A\u0438\u0435 \u0444\u0430\u0439\u043B\u044B \u043C\u0435\u043D\u044F\u044E\u0442\u0441\u044F
11692
12001
  \u0432\u043C\u0435\u0441\u0442\u0435, \u0447\u0442\u043E \u0442\u0443\u0442 \u043E\u0431\u0449\u0438\u0439 \u0445\u0440\u0435\u0431\u0435\u0442). check \u2014 \u0441\u0432\u0435\u0440\u043A\u0430 \u0441 \u0440\u0443\u0447\u043D\u043E\u0439
@@ -11776,29 +12085,50 @@ async function makeCliStore(repo) {
11776
12085
  client.reconcileRuntime({ worktreesDir: effectiveWorktreesDir(), listProcesses: listDroneProcesses });
11777
12086
  return client;
11778
12087
  }
12088
+ var SCOUT_BRANCH = "swarm-scout/agent-brief";
11779
12089
  async function runScoutCommand(repo) {
12090
+ try {
12091
+ return await scoutRepo(repo);
12092
+ } catch (e) {
12093
+ const msg = e.message;
12094
+ console.error(`\u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0430 \u043D\u0435 \u043F\u043E\u0448\u043B\u0430: ${msg}`);
12095
+ if (e instanceof WorktreeBusyError) {
12096
+ console.error(`\u0440\u0430\u0431\u043E\u0447\u0430\u044F \u043A\u043E\u043F\u0438\u044F \u043F\u0440\u043E\u0448\u043B\u043E\u0439 \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0438 \u0437\u0430\u043D\u044F\u0442\u0430 \u0438\u043B\u0438 \u0432 \u043D\u0435\u0439 \u043E\u0441\u0442\u0430\u043B\u0438\u0441\u044C \u043F\u0440\u0430\u0432\u043A\u0438 \u2014 \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0438 \u0435\u0451 \u0438 \u0441\u043D\u0435\u0441\u0438: git worktree remove --force <\u043F\u0443\u0442\u044C \u0432\u044B\u0448\u0435>`);
12097
+ }
12098
+ return 1;
12099
+ }
12100
+ }
12101
+ async function scoutRepo(repo) {
11780
12102
  const repoFullName = await remoteRepoFullName(repo);
11781
12103
  if (!repoFullName) {
11782
- console.error(`\u043D\u0435 \u043F\u043E\u043D\u044F\u043B, \u0447\u0442\u043E \u0437\u0430 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0432 ${repo}: \u0443 origin \u043D\u0435\u0442 GitHub-\u0430\u0434\u0440\u0435\u0441\u0430. \u0420\u0430\u0437\u043C\u0435\u0442\u043A\u0430 \u0430\u0434\u0440\u0435\u0441\u0443\u0435\u0442\u0441\u044F \u043F\u043E owner/repo.`);
12104
+ console.error(`\u043D\u0435 \u043F\u043E\u043D\u044F\u043B, \u0447\u0442\u043E \u0437\u0430 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0432 ${repo}: \u0443 origin \u043D\u0435\u0442 GitHub-\u0430\u0434\u0440\u0435\u0441\u0430. PR \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0438 \u043E\u0442\u043A\u0440\u044B\u0432\u0430\u0442\u044C \u043D\u0435\u043A\u0443\u0434\u0430.`);
11783
12105
  return 1;
11784
12106
  }
11785
- const call = resilientSwarmMcp({
11786
- onDown: (ms) => console.warn(`Swarm MCP \u043C\u043E\u043B\u0447\u0438\u0442 ${Math.round(ms / 1e3)}\u0441 \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u044C \u0441\u0432\u044F\u0437\u044C \u0441 \u043F\u0440\u043E\u0434\u043E\u043C.`),
11787
- onUp: () => console.warn("Swarm MCP \u0441\u043D\u043E\u0432\u0430 \u043D\u0430 \u0441\u0432\u044F\u0437\u0438.")
11788
- });
11789
- const client = new SwarmClient({ call, outboxPath: SWARM_OUTBOX, defaultRepo: repo, autoFlush: false });
11790
12107
  const name = pickName([]);
11791
- const outFile = join24(SWARM_HOME, `scout-${repoFullName.replace(/[^\w.-]+/g, "-")}.json`);
11792
12108
  const bin = resolveExecutor().resolveBin();
12109
+ const worktree = droneWorktreePath(effectiveWorktreesDir(), repo, "swarm-scout");
12110
+ await $6({ reject: false })`git -C ${repo} fetch --quiet origin`;
12111
+ const baseRef = await remoteMainRef(repo);
12112
+ const baseBranch = baseRef.replace(/^origin\//, "");
12113
+ const openPr = await scoutPrState(repo);
12114
+ await addWorktree(repo, worktree, SCOUT_BRANCH, { base: baseRef, isWorktreeHeldLive: async () => false });
12115
+ if (openPr.known && !openPr.url) {
12116
+ await $6({ reject: false })`git -C ${worktree} reset --hard ${baseRef}`;
12117
+ }
12118
+ await setWorktreeIdentity(worktree, name);
12119
+ const abs = (rel) => join24(worktree, rel);
11793
12120
  const code = await runRepoScout({
11794
12121
  repoFullName,
11795
- repoPath: repo,
12122
+ repoPath: worktree,
11796
12123
  name,
11797
- outFile,
11798
- readProfile: () => client.repoProfile(repoFullName),
12124
+ readFile: (rel) => existsSync16(abs(rel)) ? readFileSync17(abs(rel), "utf8") : null,
12125
+ writeFile: (rel, text) => {
12126
+ mkdirSync9(dirname4(abs(rel)), { recursive: true });
12127
+ writeFileSync13(abs(rel), text);
12128
+ },
11799
12129
  runScout: (prompt) => new Promise((resolve4) => {
11800
12130
  const child = spawn3(bin, [...ISOLATION_FLAGS, "--dangerously-skip-permissions", "-p", prompt], {
11801
- cwd: repo,
12131
+ cwd: worktree,
11802
12132
  stdio: ["ignore", "inherit", "inherit"]
11803
12133
  // человек смотрит на разведку живьём
11804
12134
  });
@@ -11808,13 +12138,65 @@ async function runScoutCommand(repo) {
11808
12138
  });
11809
12139
  child.on("exit", (c) => resolve4(c ?? 1));
11810
12140
  }),
11811
- readOut: () => existsSync16(outFile) ? readFileSync17(outFile, "utf8") : null,
11812
- clearOut: () => rmSync2(outFile, { force: true }),
11813
- report: (report) => client.reportRepoScout(repoFullName, report, name),
12141
+ publish: (files) => publishScoutPr({ worktree, repo, files, name, baseBranch, openPr: openPr.url, fresh: openPr.known && !openPr.url }),
11814
12142
  log: (line) => console.log(line)
11815
12143
  });
12144
+ if (code === 0) await removeWorktree(repo, worktree);
11816
12145
  return code;
11817
12146
  }
12147
+ async function scoutPrState(repo) {
12148
+ const res = await $6({
12149
+ cwd: repo,
12150
+ reject: false
12151
+ })`gh pr list --head ${SCOUT_BRANCH} --state open --json url`;
12152
+ if (res.exitCode !== 0) return { known: false, url: null };
12153
+ try {
12154
+ const arr = JSON.parse(res.stdout);
12155
+ const url = Array.isArray(arr) ? arr.find((p) => typeof p.url === "string")?.url : void 0;
12156
+ return { known: true, url: url ?? null };
12157
+ } catch {
12158
+ return { known: false, url: null };
12159
+ }
12160
+ }
12161
+ async function publishScoutPr(opts) {
12162
+ const { worktree, files, name, baseBranch } = opts;
12163
+ const message = [
12164
+ `docs(agents): \u0431\u0440\u0438\u0444 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F \u0434\u043B\u044F \u0430\u0433\u0435\u043D\u0442\u043E\u0432 (${BRIEF_PATH})`,
12165
+ "",
12166
+ "\u0427\u0442\u043E \u0434\u0430\u043B\u044C\u0448\u0435 \u0434\u0435\u043B\u0430\u0435\u0442 \u0440\u043E\u0439 \u0441 \u044D\u0442\u0438\u043C \u0444\u0430\u0439\u043B\u043E\u043C: \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u0435\u0433\u043E \u0438\u043C\u043F\u043E\u0440\u0442\u043E\u043C \u0438\u0437 CLAUDE.md, \u043F\u043E\u044D\u0442\u043E\u043C\u0443",
12167
+ "\u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0435 \u043F\u043E\u043F\u0430\u0434\u0430\u0435\u0442 \u0432 \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442 \u043A\u0430\u0436\u0434\u043E\u0439 \u0430\u0433\u0435\u043D\u0442\u0441\u043A\u043E\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 \u0441\u0430\u043C\u043E, \u0431\u0435\u0437 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u044F \u043A \u0442\u0443\u043B\u0430\u043C.",
12168
+ "",
12169
+ `Scout: ${name}`
12170
+ ].join("\n");
12171
+ const add = await $6({ cwd: worktree, reject: false })`git add -- ${files}`;
12172
+ if (add.exitCode !== 0) return { ok: false, error: `git add: ${add.stderr || add.stdout}` };
12173
+ const staged = await $6({ cwd: worktree, reject: false })`git diff --cached --quiet`;
12174
+ if (staged.exitCode === 0) return { ok: true, prUrl: null };
12175
+ const commit = await $6({ cwd: worktree, reject: false })`git commit --no-verify -m ${message}`;
12176
+ if (commit.exitCode !== 0) return { ok: false, error: `git commit: ${commit.stderr || commit.stdout}` };
12177
+ const push = opts.fresh ? await $6({ cwd: worktree, reject: false })`git push --force-with-lease origin ${`HEAD:${SCOUT_BRANCH}`}` : await $6({ cwd: worktree, reject: false })`git push origin ${`HEAD:${SCOUT_BRANCH}`}`;
12178
+ if (push.exitCode !== 0) return { ok: false, error: `git push: ${push.stderr || push.stdout}` };
12179
+ if (opts.openPr) return { ok: true, prUrl: opts.openPr };
12180
+ const body = [
12181
+ `\u0411\u0440\u0438\u0444 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F \u0434\u043B\u044F \u0430\u0433\u0435\u043D\u0442\u043E\u0432: \u0447\u0435\u043C \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435, \u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0442\u044C \u0438 \u0447\u0442\u043E \u0437\u0434\u0435\u0441\u044C \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F \u0437\u0435\u043B\u0451\u043D\u044B\u043C, \u0441\u043E\u0433\u043B\u0430\u0448\u0435\u043D\u0438\u044F, \u0437\u043E\u043D\u044B \u043A\u043E\u0434\u0430 \u0438 \u043E\u0431\u0449\u0438\u0439 \u0445\u0440\u0435\u0431\u0435\u0442. \u041D\u0430\u043F\u0438\u0441\u0430\u043D \u0440\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A\u043E\u043C \u0440\u043E\u044F (\`swarm scout\`) \u043F\u043E \u043A\u043E\u0434\u0443 \u044D\u0442\u043E\u0433\u043E \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F.`,
12182
+ "",
12183
+ `\`${BRIEF_PATH}\` \u0446\u0435\u043B\u0438\u043A\u043E\u043C \u043F\u0440\u0438\u043D\u0430\u0434\u043B\u0435\u0436\u0438\u0442 \u0440\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A\u0443 \u2014 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u043F\u0440\u043E\u0445\u043E\u0434 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0438\u0448\u0435\u0442 \u0435\u0433\u043E \u0446\u0435\u043B\u0438\u043A\u043E\u043C, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u043F\u0440\u0430\u0432\u043A\u0438 \u0440\u0443\u043A\u0430\u043C\u0438 \u0432 \u043D\u0435\u0433\u043E \u043D\u0435 \u0432\u043D\u043E\u0441\u044F\u0442. \`CLAUDE.md\` \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u0444\u0430\u0439\u043B \u0441\u0442\u0440\u043E\u043A\u043E\u0439 \`${BRIEF_IMPORT}\`: \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0435 \u043F\u043E\u043F\u0430\u0434\u0430\u0435\u0442 \u0432 \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442 \u0430\u0433\u0435\u043D\u0442\u0441\u043A\u043E\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 \u0441\u0430\u043C\u043E, \u0430 \u043D\u0435 \xAB\u0435\u0441\u043B\u0438 \u0430\u0433\u0435\u043D\u0442 \u0432\u0441\u043F\u043E\u043C\u043D\u0438\u0442 \u0437\u0430\u0433\u043B\u044F\u043D\u0443\u0442\u044C\xBB. \u042D\u0442\u0443 \u0441\u0442\u0440\u043E\u043A\u0443 \u0440\u043E\u0439 \u0434\u043E\u0431\u0430\u0432\u043B\u044F\u0435\u0442 \u043E\u0434\u0438\u043D \u0440\u0430\u0437; \u0434\u0430\u043B\u044C\u0448\u0435 \`CLAUDE.md\` \u043E\u0441\u0442\u0430\u0451\u0442\u0441\u044F \u0442\u0435\u0440\u0440\u0438\u0442\u043E\u0440\u0438\u0435\u0439 \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430 \u0438 \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u043E\u0439 \u043D\u0435 \u0442\u0440\u043E\u0433\u0430\u0435\u0442\u0441\u044F.`,
12184
+ "",
12185
+ `\u{1F41D} \u0420\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A ${name}`
12186
+ ].join("\n");
12187
+ const create = await $6({
12188
+ cwd: worktree,
12189
+ reject: false
12190
+ })`gh pr create --base ${baseBranch} --head ${SCOUT_BRANCH} --title ${`docs(agents): \u0431\u0440\u0438\u0444 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F \u0434\u043B\u044F \u0430\u0433\u0435\u043D\u0442\u043E\u0432 [${name}]`} --body ${body}`;
12191
+ if (create.exitCode !== 0) return { ok: false, error: `gh pr create: ${create.stderr || create.stdout}` };
12192
+ const url = /https:\/\/github\.com\/\S+\/pull\/\d+/.exec(create.stdout)?.[0];
12193
+ if (!url) {
12194
+ const found = (await openPrsForBranch(SCOUT_BRANCH, opts.repo))[0];
12195
+ if (found) return { ok: true, prUrl: found };
12196
+ return { ok: false, error: `gh pr create \u043D\u0435 \u0432\u0435\u0440\u043D\u0443\u043B \u0441\u0441\u044B\u043B\u043A\u0443 \u043D\u0430 PR: ${create.stdout}` };
12197
+ }
12198
+ return { ok: true, prUrl: url };
12199
+ }
11818
12200
  async function runZones(db2, repo, args) {
11819
12201
  const manual = new Map(db2.listRuns().map((r) => [r.task_slug, r.area]));
11820
12202
  const raw = await readTaskHistory(repo);
@@ -12317,9 +12699,9 @@ switch (cmd) {
12317
12699
  // swarm zones — какие зоны видит рой и что считает общим хребтом
12318
12700
  // swarm zones check — сверка с ручной разметкой area:* на прошлых задачах
12319
12701
  // swarm zones suggest <текст|slug> — какую зону предложить задаче ПРИ ПОСТАНОВКЕ
12320
- // TASWM-379/382: РАЗВЕДКА репозитория — одноразовый прогон, после которого рой знает
12321
- // про чужой репозиторий то, что на taskless записано в CLAUDE.md и скиллах. Предложение
12322
- // разведчика НЕ применяется: его принимает человек (см. repoScout.ts).
12702
+ // TASWM-379/382/441: РАЗВЕДКА репозитория — прогон, после которого рой знает про чужой
12703
+ // репозиторий то, что на taskless записано в CLAUDE.md и скиллах. Знание ложится В САМ
12704
+ // репозиторий (docs/AGENT_BRIEF.md + импорт из CLAUDE.md) и отдаётся PR (см. repoScout.ts).
12323
12705
  case "scout": {
12324
12706
  const { repo: repoFlag, rest } = takeRepo(rawArgs);
12325
12707
  const repo = repoFlag ?? rest[0] ?? SWARM_REPO;