@taskless-app/swarm 0.6.15 → 0.6.17
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.
- package/dist/cli.js +558 -176
- package/dist/overmind.js +311 -58
- 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
|
-
|
|
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.
|
|
2278
|
+
version: "0.6.17",
|
|
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: "
|
|
2328
|
+
gitHead: "a4ed164a36295bd7ebc27e7f6fe73809e09b142a"
|
|
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
|
|
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
|
-
|
|
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
|
|
10794
|
-
var
|
|
10795
|
-
|
|
10796
|
-
|
|
10797
|
-
|
|
10798
|
-
|
|
10799
|
-
|
|
10800
|
-
|
|
10801
|
-
|
|
10802
|
-
|
|
10803
|
-
|
|
10804
|
-
|
|
10805
|
-
|
|
10806
|
-
|
|
10807
|
-
|
|
10808
|
-
|
|
10809
|
-
|
|
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}, \
|
|
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
|
-
|
|
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
|
-
"\
|
|
10824
|
-
"1.
|
|
10825
|
-
"2.
|
|
10826
|
-
"3.
|
|
10827
|
-
"4.
|
|
10828
|
-
"5.
|
|
10829
|
-
"6.
|
|
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
|
-
|
|
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 \
|
|
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.
|
|
10837
|
-
"\u2022 \
|
|
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
|
-
|
|
11152
|
+
describeKnownBrief(input.brief ?? null, input.briefPath),
|
|
10842
11153
|
"",
|
|
10843
|
-
`\
|
|
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
|
-
|
|
10846
|
-
"
|
|
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\
|
|
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
|
|
10854
|
-
if (!
|
|
10855
|
-
return
|
|
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
|
-
|
|
10858
|
-
|
|
10859
|
-
|
|
10860
|
-
|
|
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
|
-
"\
|
|
10865
|
-
"\
|
|
10866
|
-
|
|
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
|
|
10881
|
-
|
|
10882
|
-
|
|
10883
|
-
|
|
10884
|
-
|
|
10885
|
-
|
|
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
|
|
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
|
|
10914
|
-
const
|
|
10915
|
-
|
|
10916
|
-
|
|
10917
|
-
|
|
10918
|
-
|
|
10919
|
-
|
|
10920
|
-
|
|
10921
|
-
|
|
10922
|
-
|
|
10923
|
-
|
|
10924
|
-
|
|
10925
|
-
|
|
10926
|
-
|
|
10927
|
-
|
|
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
|
|
11228
|
+
const briefPath = deps.briefPath ?? BRIEF_PATH;
|
|
11229
|
+
const before = deps.readFile(briefPath);
|
|
10931
11230
|
deps.log(
|
|
10932
|
-
|
|
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
|
-
|
|
10938
|
-
|
|
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
|
|
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(`\
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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\
|
|
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
|
-
|
|
10966
|
-
|
|
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\
|
|
11686
|
-
|
|
11687
|
-
\u0437\u0434\u0435\u0441\u044C \u0437\u0435\u043B\u0451\u043D\u043E\u0435, \
|
|
11688
|
-
\u043F\
|
|
11689
|
-
\
|
|
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. \
|
|
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:
|
|
12122
|
+
repoPath: worktree,
|
|
11796
12123
|
name,
|
|
11797
|
-
|
|
11798
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
//
|
|
12322
|
-
//
|
|
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;
|