@taskless-app/swarm 0.6.8 → 0.6.10
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 +322 -7
- package/dist/overmind.js +303 -35
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -110,7 +110,11 @@ var init_types = __esm({
|
|
|
110
110
|
// прогона перед самым спавном: «поехал молча» означало бы, что причина запуска исчезла из
|
|
111
111
|
// истории, и следующий разбор «почему он стартовал поверх незакрытого блокера» упёрся бы в
|
|
112
112
|
// пустоту. Успешное снятие (блокер смержен) события не порождает — это штатный ход.
|
|
113
|
-
"dep_cleared"
|
|
113
|
+
"dep_cleared",
|
|
114
|
+
// TASWM-412: дрон вернул сырую задачу автору (см. RunStatus "returned"). Веха, а не step:
|
|
115
|
+
// прогон на этом кончается, и в истории должно быть видно, что он кончился ВОЗВРАТОМ —
|
|
116
|
+
// иначе возврат читается как брошенная посреди работы задача.
|
|
117
|
+
"returned"
|
|
114
118
|
];
|
|
115
119
|
MILESTONE_KINDS = [
|
|
116
120
|
"spawned",
|
|
@@ -133,8 +137,10 @@ var init_types = __esm({
|
|
|
133
137
|
// надзор overmind (TASWM-194) — та же нотификация, что улетела в инбокс, вехой в Истории
|
|
134
138
|
"note",
|
|
135
139
|
// заметка дрона «замечено по пути» (TASWM-279) — веха, иначе находка тонет в ленте step'ов
|
|
136
|
-
"dep_cleared"
|
|
140
|
+
"dep_cleared",
|
|
137
141
|
// зависимость снята неуспешным финалом блокера (TASWM-361) — решение роя, веха как conflict
|
|
142
|
+
"returned"
|
|
143
|
+
// дрон вернул сырую задачу автору (TASWM-412) — финал прогона, веха как done/failed
|
|
138
144
|
];
|
|
139
145
|
MILESTONE_KIND_SET = new Set(MILESTONE_KINDS);
|
|
140
146
|
}
|
|
@@ -212,6 +218,14 @@ function buildSegments(created, events, laneEnd) {
|
|
|
212
218
|
push(e.at);
|
|
213
219
|
phase = "superseded";
|
|
214
220
|
break;
|
|
221
|
+
// TASWM-412: возврат автору — конец прогона, хвост дорожки не должен дорисовываться
|
|
222
|
+
// «работающим» зелёным (та же готча, что чинил TASWM-166 для stopped/superseded).
|
|
223
|
+
// Фаза — ЧУЖАЯ, "stopped": своя фаза значит свой цвет-токен в обеих темах web, то есть
|
|
224
|
+
// правку дизайн-системы; здесь важнее, что полоса перестаёт врать про живую работу.
|
|
225
|
+
case "returned":
|
|
226
|
+
push(e.at);
|
|
227
|
+
phase = "stopped";
|
|
228
|
+
break;
|
|
215
229
|
}
|
|
216
230
|
}
|
|
217
231
|
push(laneEnd);
|
|
@@ -249,6 +263,8 @@ function computeBlocker(status, stopReason, lastStep, pr, ci, deps, openQ) {
|
|
|
249
263
|
return { icon: "\u23F9", text: stopReason || "\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D", tone: "stopped" };
|
|
250
264
|
case "superseded":
|
|
251
265
|
return { icon: "\u21BA", text: "\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D \u043D\u0430 \u0434\u0440\u0443\u0433\u043E\u0439 \u0432\u0430\u0440\u0438\u0430\u043D\u0442", tone: "superseded" };
|
|
266
|
+
case "returned":
|
|
267
|
+
return { icon: "\u21A9", text: stopReason || "\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443 \u043D\u0430 \u0443\u0442\u043E\u0447\u043D\u0435\u043D\u0438\u0435", tone: "stopped" };
|
|
252
268
|
case "done":
|
|
253
269
|
return { icon: "\u2705", text: "\u0441\u043C\u0435\u0440\u0436\u0435\u043D\u043E", tone: "done" };
|
|
254
270
|
default:
|
|
@@ -383,7 +399,11 @@ var init_status = __esm({
|
|
|
383
399
|
storm_dead: { pill: "storm", label: "\u0448\u0442\u043E\u0440\u043C \u2014 \u0437\u0430\u043C\u043E\u0440\u043E\u0436\u0435\u043D", color: "--warning", terminal: false },
|
|
384
400
|
// умер в шторм Anthropic, поднимется после отбоя (TASWM-23)
|
|
385
401
|
stopped: { pill: "stopped", label: "\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D", color: "--muted-foreground", terminal: true },
|
|
386
|
-
superseded: { pill: "superseded", label: "\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D", color: "--muted-foreground", terminal: true }
|
|
402
|
+
superseded: { pill: "superseded", label: "\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D", color: "--muted-foreground", terminal: true },
|
|
403
|
+
// TASWM-412: дрон вернул сырую задачу автору (вопросы комментом, задача назначена на автора
|
|
404
|
+
// и переведена в работу) и освободил слот. Терминальный — работа этого прогона закончена;
|
|
405
|
+
// цвет предупреждающий, а не destructive: смотреть должен АВТОР задачи, чинить нечего.
|
|
406
|
+
returned: { pill: "returned", label: "\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443", color: "--warning", terminal: true }
|
|
387
407
|
};
|
|
388
408
|
FALLBACK = STATUS_META.queued;
|
|
389
409
|
TERMINAL_STATUSES = Object.keys(STATUS_META).filter(
|
|
@@ -2035,7 +2055,7 @@ var init_package = __esm({
|
|
|
2035
2055
|
"package.json"() {
|
|
2036
2056
|
package_default = {
|
|
2037
2057
|
name: "@taskless-app/swarm",
|
|
2038
|
-
version: "0.6.
|
|
2058
|
+
version: "0.6.10",
|
|
2039
2059
|
type: "module",
|
|
2040
2060
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
2041
2061
|
keywords: [
|
|
@@ -2085,7 +2105,7 @@ var init_package = __esm({
|
|
|
2085
2105
|
execa: "^10.0.0",
|
|
2086
2106
|
zod: "3.25.76"
|
|
2087
2107
|
},
|
|
2088
|
-
gitHead: "
|
|
2108
|
+
gitHead: "06e2edbcc9a21f0a1ef402868ea37b2b7d591020"
|
|
2089
2109
|
};
|
|
2090
2110
|
}
|
|
2091
2111
|
});
|
|
@@ -4162,8 +4182,8 @@ var init_updateCli = __esm({
|
|
|
4162
4182
|
|
|
4163
4183
|
// src/cli.ts
|
|
4164
4184
|
init_cliIo();
|
|
4165
|
-
import { basename as basename4 } from "path";
|
|
4166
|
-
import { existsSync as existsSync14, readdirSync as readdirSync9, statSync as statSync9, openSync as openSync4, readSync as readSync2, closeSync as closeSync4 } from "fs";
|
|
4185
|
+
import { basename as basename4, join as join23 } from "path";
|
|
4186
|
+
import { existsSync as existsSync14, readFileSync as readFileSync16, readdirSync as readdirSync9, statSync as statSync9, openSync as openSync4, readSync as readSync2, closeSync as closeSync4, rmSync as rmSync2 } from "fs";
|
|
4167
4187
|
import { setTimeout as sleep2 } from "timers/promises";
|
|
4168
4188
|
|
|
4169
4189
|
// src/swarmClient.ts
|
|
@@ -5615,6 +5635,51 @@ var SwarmClient = class _SwarmClient {
|
|
|
5615
5635
|
if (!Array.isArray(prepare)) return null;
|
|
5616
5636
|
return prepare.filter((c) => typeof c === "string" && c.trim().length > 0).map((c) => c.trim());
|
|
5617
5637
|
}
|
|
5638
|
+
/**
|
|
5639
|
+
* РАЗМЕТКА репозитория (TASWM-381) из того же swarm_repo_config: зоны, чем поднять
|
|
5640
|
+
* окружение, чем проверять, конвенции, общий хребет, бриф — плюс карта источников
|
|
5641
|
+
* (кто вписал поле: разведчик или человек). Отдаём КАК ЕСТЬ, объектом: строгая
|
|
5642
|
+
* схема разметки живёт в проде (@taskless-app/shared), и второй её копии в демоне
|
|
5643
|
+
* быть не должно — разъехавшись, они спорили бы о том, что считать валидным.
|
|
5644
|
+
*
|
|
5645
|
+
* null — разметки нет (разведка не проходила; РАБОЧЕЕ состояние), репо не смэплено
|
|
5646
|
+
* на swarm-проект владельца токена или прод недоступен.
|
|
5647
|
+
*/
|
|
5648
|
+
async repoProfile(repoFullName) {
|
|
5649
|
+
let res;
|
|
5650
|
+
try {
|
|
5651
|
+
res = await this.call("swarm_repo_config", { repoFullName });
|
|
5652
|
+
} catch {
|
|
5653
|
+
return null;
|
|
5654
|
+
}
|
|
5655
|
+
if (res?.isError) return null;
|
|
5656
|
+
const sc = res.structuredContent ?? {};
|
|
5657
|
+
const p = sc.profile;
|
|
5658
|
+
return p && typeof p === "object" && !Array.isArray(p) ? p : null;
|
|
5659
|
+
}
|
|
5660
|
+
/**
|
|
5661
|
+
* РАЗВЕДКА репозитория (TASWM-382): отправить в прод то, что разведчик вычитал в коде.
|
|
5662
|
+
* Живую разметку это НЕ меняет — предложение ложится рядом и ждёт человека; поэтому
|
|
5663
|
+
* ошибка здесь дорога ровно на стоимость прогона разведки, и мы её возвращаем, а не
|
|
5664
|
+
* глотаем, как чтения выше.
|
|
5665
|
+
*
|
|
5666
|
+
* Мимо outbox (в отличие от мутаций прогонов): предложение имеет смысл только вместе
|
|
5667
|
+
* со свежим состоянием репозитория, и доехавшее через сутки после разведки — хуже,
|
|
5668
|
+
* чем не доехавшее вовсе. Прод недоступен — человек перезапустит разведку.
|
|
5669
|
+
*/
|
|
5670
|
+
async reportRepoScout(repoFullName, report, by) {
|
|
5671
|
+
let res;
|
|
5672
|
+
try {
|
|
5673
|
+
res = await this.call("swarm_repo_scout_report", { repoFullName, report, ...by ? { by } : {} });
|
|
5674
|
+
} catch (e) {
|
|
5675
|
+
return { ok: false, error: `\u043F\u0440\u043E\u0434 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D: ${e.message}` };
|
|
5676
|
+
}
|
|
5677
|
+
const text = res?.content?.map((c) => "text" in c ? c.text : "").join(" ") ?? "";
|
|
5678
|
+
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" };
|
|
5679
|
+
const sc = res.structuredContent ?? {};
|
|
5680
|
+
const diff = Array.isArray(sc.diff) ? sc.diff : [];
|
|
5681
|
+
return { ok: true, diff, display: text };
|
|
5682
|
+
}
|
|
5618
5683
|
/**
|
|
5619
5684
|
* Live ТРЕБОВАНИЯ К ОКРУЖЕНИЮ машины из Taskless (TASWM-313): тот же swarm_repo_config по
|
|
5620
5685
|
* repoFullName — Project.swarmConfig.requires. Демон проверяет их ПЕРЕД спавном дрона и не
|
|
@@ -9323,6 +9388,12 @@ async function removeWorktree(repo, path) {
|
|
|
9323
9388
|
await detachWorktree(repo, path);
|
|
9324
9389
|
if (branch) await $5({ reject: false })`git -C ${repo} branch -D ${branch}`;
|
|
9325
9390
|
}
|
|
9391
|
+
var GITHUB_REMOTE_RE = /github\.com[:/]([^/]+\/[^/]+?)(?:\.git)?\/?$/i;
|
|
9392
|
+
async function remoteRepoFullName(repo) {
|
|
9393
|
+
const url = (await $5({ reject: false })`git -C ${repo} remote get-url origin`).stdout.trim();
|
|
9394
|
+
const m = url.match(GITHUB_REMOTE_RE);
|
|
9395
|
+
return m ? m[1] : null;
|
|
9396
|
+
}
|
|
9326
9397
|
|
|
9327
9398
|
// src/cli.ts
|
|
9328
9399
|
init_agentToken();
|
|
@@ -9517,6 +9588,191 @@ function resilientSwarmMcp(opts = {}) {
|
|
|
9517
9588
|
};
|
|
9518
9589
|
}
|
|
9519
9590
|
|
|
9591
|
+
// src/repoScout.ts
|
|
9592
|
+
import { HEADLESS_BLOCK } from "./overmind.js";
|
|
9593
|
+
var SCOUT_FIELDS = ["brief", "zones", "env", "verify", "conventions", "spine"];
|
|
9594
|
+
var SHAPE = `{
|
|
9595
|
+
"brief": "\u0427\u0442\u043E \u044D\u0442\u043E \u0437\u0430 \u043F\u0440\u043E\u0434\u0443\u043A\u0442 \u0438 \u043D\u0430 \u0447\u0451\u043C \u043D\u0430\u043F\u0438\u0441\u0430\u043D \u2014 3-5 \u0441\u0442\u0440\u043E\u043A, \u0447\u0442\u043E\u0431\u044B \u0434\u0440\u043E\u043D \u043D\u0435 \u0432\u044B\u0432\u043E\u0434\u0438\u043B \u044D\u0442\u043E \u0438\u0437 \u043A\u043E\u0434\u0430 \u0437\u0430\u043D\u043E\u0432\u043E.",
|
|
9596
|
+
"zones": [
|
|
9597
|
+
{ "label": "area:<\u043A\u043E\u0440\u043E\u0442\u043A\u043E\u0435-\u0438\u043C\u044F>", "about": "\u0447\u0442\u043E \u044D\u0442\u043E \u0437\u0430 \u0437\u043E\u043D\u0430 \u043E\u0434\u043D\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u043E\u0439", "paths": ["packages/api/"] }
|
|
9598
|
+
],
|
|
9599
|
+
"env": {
|
|
9600
|
+
"database": "\u0447\u0435\u043C \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u0431\u0430\u0437\u0443, \u0440\u043E\u0432\u043D\u043E \u043E\u0434\u043D\u0430 \u043A\u043E\u043C\u0430\u043D\u0434\u0430",
|
|
9601
|
+
"migrations": "\u0447\u0435\u043C \u043D\u0430\u043A\u0430\u0442\u0438\u0442\u044C \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u0440\u043E\u0432\u043D\u043E \u043E\u0434\u043D\u0430 \u043A\u043E\u043C\u0430\u043D\u0434\u0430",
|
|
9602
|
+
"vars": ["\u0418\u041C\u0415\u041D\u0410", "\u041F\u0415\u0420\u0415\u041C\u0415\u041D\u041D\u042B\u0425", "\u0411\u0415\u0417_\u0417\u041D\u0410\u0427\u0415\u041D\u0418\u0419"]
|
|
9603
|
+
},
|
|
9604
|
+
"verify": {
|
|
9605
|
+
"commands": ["pnpm typecheck", "pnpm test"],
|
|
9606
|
+
"green": "\u0447\u0442\u043E \u0437\u0434\u0435\u0441\u044C \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F \u0437\u0435\u043B\u0451\u043D\u044B\u043C \u0441\u043B\u043E\u0432\u0430\u043C\u0438: \u0447\u0442\u043E \u0433\u043E\u043D\u044F\u0435\u0442\u0441\u044F \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E, \u0447\u0442\u043E \u0442\u043E\u043B\u044C\u043A\u043E \u043D\u0430 CI, \u043A\u0430\u043A\u043E\u0439 \u0447\u0435\u043A \u0444\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u043F\u0440\u0438\u0437\u043D\u0430\u043A"
|
|
9607
|
+
},
|
|
9608
|
+
"conventions": { "commitLanguage": "ru|en", "slugInCommit": true, "baseBranch": "main" },
|
|
9609
|
+
"spine": ["packages/api/prisma/schema.prisma", "packages/shared/src/"]
|
|
9610
|
+
}`;
|
|
9611
|
+
function buildScoutPrompt(input) {
|
|
9612
|
+
const name = input.name ?? "\u0440\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A";
|
|
9613
|
+
const known = describeKnownProfile(input.profile ?? null);
|
|
9614
|
+
return [
|
|
9615
|
+
`\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.`,
|
|
9616
|
+
"",
|
|
9617
|
+
HEADLESS_BLOCK,
|
|
9618
|
+
"",
|
|
9619
|
+
`\u0420\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439: ${input.repoFullName}, \u043A\u043B\u043E\u043D \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435: ${input.repoPath}. \u0422\u044B \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0448\u044C \u0432 \u043D\u0451\u043C.`,
|
|
9620
|
+
"",
|
|
9621
|
+
"\u0427\u0422\u041E \u041E\u0422 \u0422\u0415\u0411\u042F \u041D\u0423\u0416\u041D\u041E. \u0420\u043E\u0439 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0438\u043B\u0438 \u043A \u044D\u0442\u043E\u043C\u0443 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044E, \u0438 \u0434\u0430\u043B\u044C\u0448\u0435 \u043E\u043D \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u0432\u0441\u043B\u0435\u043F\u0443\u044E: \u0437\u043E\u043D\u044B \u0434\u043B\u044F \u0437\u0430\u0434\u0430\u0447 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0440\u0430\u0441\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442 \u0440\u0443\u043A\u0430\u043C\u0438, \u043A\u0430\u043A \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u2014 \u0434\u0440\u043E\u043D \u0443\u0433\u0430\u0434\u044B\u0432\u0430\u0435\u0442, \u0447\u0442\u043E \u0441\u0447\u0438\u0442\u0430\u0442\u044C \u0437\u0435\u043B\u0451\u043D\u044B\u043C \u2014 \u043D\u0435 \u0437\u043D\u0430\u0435\u0442 \u043D\u0438\u043A\u0442\u043E. \u041F\u0440\u043E\u0447\u0438\u0442\u0430\u0439 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0438 \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0438 \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0443: \u0442\u043E, \u0447\u0442\u043E \u0434\u0440\u043E\u043D \u043E\u0431\u044F\u0437\u0430\u043D \u0437\u043D\u0430\u0442\u044C \u0414\u041E \u043F\u0435\u0440\u0432\u043E\u0439 \u0437\u0430\u0434\u0430\u0447\u0438.",
|
|
9622
|
+
"",
|
|
9623
|
+
"\u0421\u043E\u0431\u0435\u0440\u0438 \u0448\u0435\u0441\u0442\u044C \u043F\u043E\u043B\u0435\u0439:",
|
|
9624
|
+
"1. `brief` \u2014 \u0447\u0442\u043E \u044D\u0442\u043E \u0437\u0430 \u043F\u0440\u043E\u0434\u0443\u043A\u0442 \u0438 \u043D\u0430 \u0447\u0451\u043C \u043D\u0430\u043F\u0438\u0441\u0430\u043D. \u041A\u043E\u0440\u043E\u0442\u043A\u043E, \u0447\u0442\u043E\u0431\u044B \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0434\u0440\u043E\u043D \u043D\u0435 \u0432\u044B\u0432\u043E\u0434\u0438\u043B \u044D\u0442\u043E \u0438\u0437 \u043A\u043E\u0434\u0430 \u0437\u0430\u043D\u043E\u0432\u043E.",
|
|
9625
|
+
"2. `zones` \u2014 \u0437\u043E\u043D\u044B \u043A\u043E\u0434\u0430, \u043B\u0435\u0439\u0431\u043B\u044B `area:*`: \u043F\u0430\u043A\u0435\u0442\u044B, \u043C\u043E\u0434\u0443\u043B\u0438, \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0438 \u0444\u0438\u0447. \u041F\u043E \u043D\u0438\u043C \u0440\u043E\u0439 \u0440\u0430\u0437\u0432\u043E\u0434\u0438\u0442 \u043F\u0430\u0440\u0430\u043B\u043B\u0435\u043B\u044C\u043D\u044B\u0435 \u0437\u0430\u0434\u0430\u0447\u0438, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u0443 \u043A\u0430\u0436\u0434\u043E\u0439 \u0437\u043E\u043D\u044B \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u044B `paths` (\u043A\u0430\u0442\u0430\u043B\u043E\u0433/\u043F\u0440\u0435\u0444\u0438\u043A\u0441, glob). \u0414\u0440\u043E\u0431\u0438 \u043F\u043E \u0420\u0415\u0410\u041B\u042C\u041D\u041E\u0419 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0435 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F, \u0430 \u043D\u0435 \u043F\u043E \u043A\u0440\u0430\u0441\u0438\u0432\u044B\u043C \u0441\u043B\u043E\u0432\u0430\u043C: \u0437\u043E\u043D\u0430, \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u043D\u0435 \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043A\u0430\u0442\u0430\u043B\u043E\u0433, \u0431\u0435\u0441\u043F\u043E\u043B\u0435\u0437\u043D\u0430.",
|
|
9626
|
+
"3. `env` \u2014 \u043A\u0430\u043A \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435: \u0447\u0435\u043C \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F \u0431\u0430\u0437\u0430 (`database`), \u0447\u0435\u043C \u043D\u0430\u043A\u0430\u0442\u044B\u0432\u0430\u044E\u0442\u0441\u044F \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 (`migrations`), \u043A\u0430\u043A\u0438\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u043D\u0443\u0436\u043D\u044B (`vars` \u2014 \u0422\u041E\u041B\u042C\u041A\u041E \u0418\u041C\u0415\u041D\u0410; \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F \u044D\u0442\u043E \u0441\u0435\u043A\u0440\u0435\u0442\u044B, \u043E\u043D\u0438 \u0443\u0435\u0434\u0443\u0442 \u0432 \u043F\u0440\u043E\u043C\u043F\u0442 \u0434\u0440\u043E\u043D\u0430 \u0438 \u0432 \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043A\u0430\u043A \u043E\u0431\u044B\u0447\u043D\u044B\u0439 \u0442\u0435\u043A\u0441\u0442). \u042D\u0442\u043E \u043F\u0440\u043E \u0442\u043E, \u0447\u0435\u043C \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E\u0442 \u0421\u0410\u041C\u041E \u041F\u0420\u0418\u041B\u041E\u0416\u0415\u041D\u0418\u0415, \u0430 \u043D\u0435 \u0442\u0435\u0441\u0442\u044B: \u0432\u043E \u043C\u043D\u043E\u0433\u0438\u0445 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F\u0445 \u0442\u0435\u0441\u0442\u043E\u0432\u0430\u044F \u0431\u0430\u0437\u0430 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u0430\u044F \u2014 \u0441\u0432\u043E\u0439 compose-\u0444\u0430\u0439\u043B, \u0441\u0432\u043E\u0439 \u0441\u043A\u0440\u0438\u043F\u0442, \u0441\u0432\u043E\u0439 \u043F\u043E\u0440\u0442, \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u0430\u044F \u0432\u0438\u0434\u0430 `TEST_DATABASE_URL`. \u041E\u043D\u0430 \u0441\u044E\u0434\u0430 \u041D\u0415 \u0438\u0434\u0451\u0442. \u041C\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u043D\u0430\u043A\u0430\u0442\u0430\u043D\u043D\u044B\u0435 \u0432 \u0442\u0435\u0441\u0442\u043E\u0432\u0443\u044E \u0431\u0430\u0437\u0443, \u043E\u0441\u0442\u0430\u0432\u044F\u0442 \u0440\u0430\u0431\u043E\u0447\u0443\u044E \u043D\u0430 \u0441\u0442\u0430\u0440\u043E\u0439 \u0441\u0445\u0435\u043C\u0435, \u0430 \u0434\u0440\u043E\u043D \u0431\u0443\u0434\u0435\u0442 \u0441\u0447\u0438\u0442\u0430\u0442\u044C \u0435\u0451 \u043E\u0431\u043D\u043E\u0432\u043B\u0451\u043D\u043D\u043E\u0439. \u0418\u0449\u0438 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0432\u0445\u043E\u0434: \u0441\u043A\u0440\u0438\u043F\u0442 \u0437\u0430\u043F\u0443\u0441\u043A\u0430 \u0432 `scripts/`, `dev`/`start` \u0432 \u043C\u0430\u043D\u0438\u0444\u0435\u0441\u0442\u0435, \u0440\u0430\u0437\u0434\u0435\u043B README \xAB\u043A\u0430\u043A \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C\xBB, \u0444\u0430\u0439\u043B \u043F\u0440\u0430\u0432\u0438\u043B \u0434\u043B\u044F \u0430\u0433\u0435\u043D\u0442\u043E\u0432.",
|
|
9627
|
+
"4. `verify` \u2014 \u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0442\u044C (`commands`) \u0438 \u0447\u0442\u043E \u0437\u0434\u0435\u0441\u044C \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F \u0437\u0435\u043B\u0451\u043D\u044B\u043C (`green` \u2014 \u0441\u043B\u043E\u0432\u0430\u043C\u0438: \u0447\u0442\u043E \u0433\u043E\u043D\u044F\u0435\u0442\u0441\u044F \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E, \u0447\u0442\u043E \u0442\u043E\u043B\u044C\u043A\u043E \u043D\u0430 CI, \u043A\u0430\u043A\u043E\u0439 \u0447\u0435\u043A \u0441\u043B\u0443\u0436\u0438\u0442 \u0444\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u043C \u043F\u0440\u0438\u0437\u043D\u0430\u043A\u043E\u043C \u0437\u0435\u043B\u0435\u043D\u0438).",
|
|
9628
|
+
"5. `conventions` \u2014 \u044F\u0437\u044B\u043A \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u043A\u043E\u043C\u043C\u0438\u0442\u043E\u0432 (`commitLanguage`), \u043D\u0443\u0436\u0435\u043D \u043B\u0438 \u0441\u043B\u0430\u0433 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0438 (`slugInCommit`), \u0431\u0430\u0437\u043E\u0432\u0430\u044F \u0432\u0435\u0442\u043A\u0430 (`baseBranch`).",
|
|
9629
|
+
"6. `spine` \u2014 \u041E\u0411\u0429\u0418\u0419 \u0425\u0420\u0415\u0411\u0415\u0422: \u0444\u0430\u0439\u043B\u044B \u0438 \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043A\u0430\u0442\u044F\u0442\u0441\u044F \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u043C\u0438 PR \u0438 \u043D\u0435 \u043F\u0430\u0440\u0430\u043B\u043B\u0435\u043B\u044F\u0442\u0441\u044F (\u0441\u0445\u0435\u043C\u0430 \u0411\u0414, \u043E\u0431\u0449\u0438\u0435 \u0442\u0438\u043F\u044B, \u0440\u043E\u0443\u0442\u0438\u043D\u0433, \u043A\u043E\u043D\u0444\u0438\u0433 \u0441\u0431\u043E\u0440\u043A\u0438). \u0414\u0432\u0430 \u0434\u0440\u043E\u043D\u0430, \u043F\u0440\u0430\u0432\u044F\u0449\u0438\u0435 \u0442\u0430\u043A\u043E\u0435 \u043E\u0434\u043D\u043E\u0432\u0440\u0435\u043C\u0435\u043D\u043D\u043E, \u0433\u0430\u0440\u0430\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u043E \u0440\u0430\u0441\u0445\u043E\u0434\u044F\u0442\u0441\u044F \u0432 \u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0435.",
|
|
9630
|
+
"",
|
|
9631
|
+
"\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).",
|
|
9632
|
+
"",
|
|
9633
|
+
"\u0427\u0415\u0413\u041E \u041D\u0415 \u0414\u0415\u041B\u0410\u0422\u042C:",
|
|
9634
|
+
"\u2022 \u041D\u0418\u0427\u0415\u0413\u041E \u041D\u0415 \u041F\u0420\u0410\u0412\u042C \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438: \u043D\u0438 \u0444\u0430\u0439\u043B\u043E\u0432, \u043D\u0438 \u0432\u0435\u0442\u043E\u043A, \u043D\u0438 \u043A\u043E\u043C\u043C\u0438\u0442\u043E\u0432. \u0422\u044B \u0447\u0438\u0442\u0430\u0435\u0448\u044C, \u0430 \u043D\u0435 \u0447\u0438\u043D\u0438\u0448\u044C. \u041D\u0435\u0442 CI \u0438\u043B\u0438 \u0442\u0435\u0441\u0442\u043E\u0432 \u2014 \u0442\u0430\u043A \u0438 \u0437\u0430\u043F\u0438\u0448\u0438, \u044D\u0442\u043E \u0444\u0430\u043A\u0442 \u043E \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438, \u0430 \u043D\u0435 \u0437\u0430\u0434\u0430\u0447\u0430 \u0442\u0435\u0431\u0435.",
|
|
9635
|
+
"\u2022 \u041D\u0415 \u0412\u042B\u0414\u0423\u041C\u042B\u0412\u0410\u0419 \u043A\u043E\u043C\u0430\u043D\u0434\u044B. \u041A\u0430\u0436\u0434\u0430\u044F \u043A\u043E\u043C\u0430\u043D\u0434\u0430, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u0442\u044B \u043F\u0438\u0448\u0435\u0448\u044C, \u0434\u043E\u043B\u0436\u043D\u0430 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0442\u044C\u0441\u044F \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0426\u0415\u041B\u0418\u041A\u041E\u041C \u2014 \u0441\u0442\u0440\u043E\u043A\u043E\u0439, \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u043C\u043E\u0436\u043D\u043E \u0441\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C (scripts, \u043C\u0430\u043D\u0438\u0444\u0435\u0441\u0442 \u043F\u0430\u043A\u0435\u0442\u0430, compose, Makefile, \u0432\u043E\u0440\u043A\u0444\u043B\u043E\u0443, README) \u2014 \u043F\u043E \u044D\u0442\u0438\u043C \u043A\u043E\u043C\u0430\u043D\u0434\u0430\u043C \u0434\u0440\u043E\u043D \u0431\u0443\u0434\u0435\u0442 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0442\u044C \u0447\u0443\u0436\u0443\u044E \u0431\u0430\u0437\u0443. \u041D\u0435 \u043D\u0430\u0448\u0451\u043B \u2014 \u043F\u043E\u043B\u0435 \u043D\u0435 \u043F\u0440\u0438\u0441\u044B\u043B\u0430\u0439.",
|
|
9636
|
+
"\u2022 \u041D\u0415 \u0421\u041A\u041B\u0415\u0418\u0412\u0410\u0419 \u043A\u043E\u043C\u0430\u043D\u0434\u0443 \u0438\u0437 \u043A\u0443\u0441\u043A\u043E\u0432. \u0414\u0432\u0435 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0438\u0435 \u0441\u0442\u0440\u043E\u043A\u0438, \u0441\u043E\u0435\u0434\u0438\u043D\u0451\u043D\u043D\u044B\u0435 \u0442\u043E\u0431\u043E\u0439 \u0432 \u043E\u0434\u043D\u0443 (\u043F\u043E\u0434\u0441\u0442\u0430\u0432\u0438\u043B URL \u0438\u0437 \u043E\u0434\u043D\u043E\u0433\u043E \u0441\u043A\u0440\u0438\u043F\u0442\u0430 \u0432 \u0432\u044B\u0437\u043E\u0432 \u0438\u0437 \u0434\u0440\u0443\u0433\u043E\u0433\u043E, \u0434\u043E\u043F\u0438\u0441\u0430\u043B \u0444\u043B\u0430\u0433, \u043E\u0431\u0435\u0440\u043D\u0443\u043B \u0432 \u043F\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u043D\u0438\u0435 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0439), \u0434\u0430\u044E\u0442 \u043A\u043E\u043C\u0430\u043D\u0434\u0443, \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043D\u0435\u0442 \u0438 \u043A\u043E\u0442\u043E\u0440\u0443\u044E \u043D\u0438\u043A\u0442\u043E \u043D\u0438\u043A\u043E\u0433\u0434\u0430 \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B. \u0412\u044B\u0433\u043B\u044F\u0434\u0438\u0442 \u043E\u043D\u0430 \u043F\u0440\u0438 \u044D\u0442\u043E\u043C \u043A\u0430\u043A \u0444\u0430\u043A\u0442. \u041D\u0435 \u0443\u0432\u0435\u0440\u0435\u043D, \u0447\u0442\u043E \u0441\u0442\u0440\u043E\u043A\u0430 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0435\u0442\u0441\u044F \u0440\u043E\u0432\u043D\u043E \u0432 \u0442\u0430\u043A\u043E\u043C \u0432\u0438\u0434\u0435, \u2014 \u043D\u0435 \u043F\u0440\u0438\u0441\u044B\u043B\u0430\u0439 \u0435\u0451.",
|
|
9637
|
+
"\u2022 \u041F\u043E\u043B\u0435, \u043F\u0440\u043E \u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u0441\u043A\u0430\u0437\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E, \u041F\u0420\u041E\u041F\u0423\u0421\u0422\u0418 \u0446\u0435\u043B\u0438\u043A\u043E\u043C. \u041F\u0440\u043E\u043F\u0443\u0441\u043A \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0441\u0442\u0438\u0440\u0430\u0435\u0442; \u0432\u044B\u0434\u0443\u043C\u0430\u043D\u043D\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u2014 \u0441\u0442\u0438\u0440\u0430\u0435\u0442 \u043F\u0440\u0430\u0432\u0434\u0443.",
|
|
9638
|
+
"\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.",
|
|
9639
|
+
"\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).",
|
|
9640
|
+
"",
|
|
9641
|
+
known,
|
|
9642
|
+
"",
|
|
9643
|
+
`\u041A\u0410\u041A \u0421\u0414\u0410\u0422\u042C. \u0417\u0430\u043F\u0438\u0448\u0438 \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u041E\u0414\u041D\u0418\u041C JSON-\u043E\u0431\u044A\u0435\u043A\u0442\u043E\u043C \u0432 \u0444\u0430\u0439\u043B \`${input.outFile}\` (\u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0438\u0448\u0438, \u0435\u0441\u043B\u0438 \u043E\u043D \u0435\u0441\u0442\u044C) \u2014 \u0438 \u043D\u0430 \u044D\u0442\u043E\u043C \u0437\u0430\u043A\u043E\u043D\u0447\u0438: \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0434\u0435\u043B\u0430\u0439 \u0438 \u043D\u0435 \u0432\u044B\u0432\u043E\u0434\u0438 \u0432 stdout \u043D\u0438\u0447\u0435\u0433\u043E, \u043A\u0440\u043E\u043C\u0435 \u043A\u043E\u0440\u043E\u0442\u043A\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u0438 \xAB\u0433\u043E\u0442\u043E\u0432\u043E\xBB. \u0424\u0430\u0439\u043B \u0447\u0438\u0442\u0430\u0435\u0442 \u0434\u0435\u043C\u043E\u043D \u0440\u043E\u044F; \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0443\u0432\u0438\u0434\u0438\u0442 \u0442\u0432\u043E\u0451 \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043D\u0430 \u044D\u043A\u0440\u0430\u043D\u0435 \u043F\u0440\u0438\u0451\u043C\u043A\u0438 \u0438 \u0440\u0435\u0448\u0438\u0442, \u0447\u0442\u043E \u043F\u0440\u0438\u043D\u044F\u0442\u044C.`,
|
|
9644
|
+
"",
|
|
9645
|
+
"\u0424\u043E\u0440\u043C\u0430 \u043E\u0442\u0432\u0435\u0442\u0430 (\u043B\u0438\u0448\u043D\u0438\u0435 \u043A\u043B\u044E\u0447\u0438 \u043D\u0435 \u0434\u043E\u0431\u0430\u0432\u043B\u044F\u0439, \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0435 \u043F\u043E\u043B\u044F \u043F\u0440\u043E\u0441\u0442\u043E \u043D\u0435 \u043F\u0438\u0448\u0438):",
|
|
9646
|
+
"```json",
|
|
9647
|
+
SHAPE,
|
|
9648
|
+
"```",
|
|
9649
|
+
"",
|
|
9650
|
+
"\u0422\u0412\u041E\u0401 \u041F\u0420\u0415\u0414\u041B\u041E\u0416\u0415\u041D\u0418\u0415 \u041D\u0415 \u041F\u0420\u0418\u041C\u0415\u041D\u042F\u0415\u0422\u0421\u042F \u041C\u041E\u041B\u0427\u0410: \u0435\u0433\u043E \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A, \u043F\u043E \u043E\u0434\u043D\u043E\u043C\u0443 \u043F\u043E\u043B\u044E. \u041F\u043E\u044D\u0442\u043E\u043C\u0443 \u043D\u0435 \u0437\u0430\u043D\u0438\u0436\u0430\u0439 \u043F\u043E\u0434\u0440\u043E\u0431\u043D\u043E\u0441\u0442\u044C \u0438\u0437 \u043E\u0441\u0442\u043E\u0440\u043E\u0436\u043D\u043E\u0441\u0442\u0438 \u2014 \u043D\u043E \u0438 \u043D\u0435 \u0432\u044B\u0434\u0430\u0432\u0430\u0439 \u0434\u043E\u0433\u0430\u0434\u043A\u0443 \u0437\u0430 \u043D\u0430\u0431\u043B\u044E\u0434\u0435\u043D\u0438\u0435: \u0442\u043E, \u0432 \u0447\u0451\u043C \u043D\u0435 \u0443\u0432\u0435\u0440\u0435\u043D, \u043B\u0443\u0447\u0448\u0435 \u043E\u0431\u044A\u044F\u0441\u043D\u0438 \u0441\u043B\u043E\u0432\u0430\u043C\u0438 \u0432 `green`/`about`, \u0447\u0435\u043C \u0443\u0433\u0430\u0434\u044B\u0432\u0430\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439."
|
|
9651
|
+
].join("\n");
|
|
9652
|
+
}
|
|
9653
|
+
function describeKnownProfile(profile) {
|
|
9654
|
+
if (!profile) {
|
|
9655
|
+
return "\u0420\u0410\u0417\u041C\u0415\u0422\u041A\u0418 \u0423 \u042D\u0422\u041E\u0413\u041E \u0420\u0415\u041F\u041E\u0417\u0418\u0422\u041E\u0420\u0418\u042F \u0415\u0429\u0401 \u041D\u0415\u0422 \u2014 \u0442\u044B \u0440\u0430\u0437\u043C\u0435\u0447\u0430\u0435\u0448\u044C \u0435\u0433\u043E \u043F\u0435\u0440\u0432\u044B\u043C. \u041F\u0440\u0435\u0434\u043B\u0430\u0433\u0430\u0439 \u0432\u0441\u0435 \u0448\u0435\u0441\u0442\u044C \u043F\u043E\u043B\u0435\u0439, \u043A\u0430\u043A\u0438\u0435 \u0441\u043C\u043E\u0436\u0435\u0448\u044C \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044C \u043A\u043E\u0434\u043E\u043C.";
|
|
9656
|
+
}
|
|
9657
|
+
const filled = SCOUT_FIELDS.filter((f) => profile[f] !== void 0 && profile[f] !== null);
|
|
9658
|
+
const byHuman = filled.filter((f) => profile.sources?.[f] === "human");
|
|
9659
|
+
const lines = [
|
|
9660
|
+
"\u0420\u0415\u041F\u041E\u0417\u0418\u0422\u041E\u0420\u0418\u0419 \u0423\u0416\u0415 \u0420\u0410\u0417\u041C\u0415\u0427\u0415\u041D \u2014 \u044D\u0442\u043E \u041F\u041E\u0412\u0422\u041E\u0420\u041D\u0410\u042F \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0430. \u0427\u0442\u043E \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E \u0441\u0435\u0439\u0447\u0430\u0441:",
|
|
9661
|
+
"```json",
|
|
9662
|
+
JSON.stringify(pickFields(profile), null, 2),
|
|
9663
|
+
"```",
|
|
9664
|
+
"\u0422\u0432\u043E\u044F \u0437\u0430\u0434\u0430\u0447\u0430 \u0442\u0435\u043F\u0435\u0440\u044C \u2014 \u0420\u0410\u0417\u041D\u0418\u0426\u0410, \u0430 \u043D\u0435 \u043F\u0435\u0440\u0435\u0441\u043A\u0430\u0437: \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0436\u0438\u0432\u0451\u0442, \u043F\u0430\u043A\u0435\u0442\u044B \u0434\u043E\u0431\u0430\u0432\u043B\u044F\u044E\u0442\u0441\u044F, \u043A\u043E\u043C\u0430\u043D\u0434\u044B \u043C\u0435\u043D\u044F\u044E\u0442\u0441\u044F. \u041F\u043E\u043B\u0435, \u043A\u043E\u0442\u043E\u0440\u043E\u0435 \u043F\u043E-\u043F\u0440\u0435\u0436\u043D\u0435\u043C\u0443 \u0432\u0435\u0440\u043D\u043E, \u041D\u0415 \u041F\u0420\u0418\u0421\u042B\u041B\u0410\u0419 \u0412\u041E\u0412\u0421\u0415 \u2014 \u043F\u0440\u043E\u043F\u0443\u0441\u043A \u0438 \u0435\u0441\u0442\u044C \xAB\u0442\u0443\u0442 \u0431\u0435\u0437 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439\xBB.",
|
|
9665
|
+
"\xAB\u041F\u0440\u0438\u0441\u043B\u0430\u0442\u044C \u043A\u0430\u043A \u0435\u0441\u0442\u044C\xBB \u2014 \u041D\u0415 \u0442\u043E \u0436\u0435 \u0441\u0430\u043C\u043E\u0435, \u0438 \u044D\u0442\u043E \u0433\u043B\u0430\u0432\u043D\u0430\u044F \u043B\u043E\u0432\u0443\u0448\u043A\u0430 \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u043E\u0439 \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0438: \u0437\u0430\u043D\u043E\u0432\u043E \u0442\u044B \u043D\u0430\u043F\u0438\u0448\u0435\u0448\u044C \u0434\u0440\u0443\u0433\u0438\u043C\u0438 \u0441\u043B\u043E\u0432\u0430\u043C\u0438, \u043F\u043E\u0434\u0440\u043E\u0431\u043D\u0435\u0435 \u0438 \u0434\u043B\u0438\u043D\u043D\u0435\u0435, \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u043F\u043E\u043B\u0443\u0447\u0438\u0442 \u044D\u0442\u043E \u043A\u0430\u043A \xAB\u0438\u0437\u043C\u0435\u043D\u0438\u043B\u043E\u0441\u044C\xBB, \u0438 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0430\u044F \u043F\u0440\u0430\u0432\u043A\u0430 \u0443\u0442\u043E\u043D\u0435\u0442 \u0441\u0440\u0435\u0434\u0438 \u043F\u0435\u0440\u0435\u0444\u043E\u0440\u043C\u0443\u043B\u0438\u0440\u043E\u0432\u043E\u043A. \u041F\u0440\u0438\u0441\u044B\u043B\u0430\u0439 \u043F\u043E\u043B\u0435, \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u043A\u043E\u0434 \u043F\u0440\u044F\u043C\u043E \u041F\u0420\u041E\u0422\u0418\u0412\u041E\u0420\u0415\u0427\u0418\u0422 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043D\u043E\u043C\u0443: \u043A\u043E\u043C\u0430\u043D\u0434\u044B \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u0442, \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u043F\u0435\u0440\u0435\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u043D, \u043F\u043E\u044F\u0432\u0438\u043B\u0441\u044F \u043F\u0430\u043A\u0435\u0442, \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0441\u044F \u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0439 \u0437\u0435\u043B\u0435\u043D\u0438. \u0421\u0442\u0430\u043B\u043E \u043A\u0440\u0430\u0441\u0438\u0432\u0435\u0435 \u2014 \u043D\u0435 \u043F\u043E\u0432\u043E\u0434.",
|
|
9666
|
+
"\u041F\u0440\u0438\u0441\u044B\u043B\u0430\u0435\u0448\u044C \u2014 \u043F\u0440\u0438\u0441\u044B\u043B\u0430\u0439 \u043F\u043E\u043B\u0435 \u0426\u0415\u041B\u0418\u041A\u041E\u041C (\u043E\u043D\u043E \u0437\u0430\u043C\u0435\u043D\u044F\u0435\u0442 \u043F\u0440\u0435\u0436\u043D\u0435\u0435), \u043D\u043E \u0440\u043E\u0432\u043D\u043E \u0441 \u0442\u043E\u0439 \u043F\u0440\u0430\u0432\u043A\u043E\u0439, \u0440\u0430\u0434\u0438 \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u043F\u0440\u0438\u0441\u043B\u0430\u043B: \u043E\u0441\u0442\u0430\u043B\u044C\u043D\u043E\u0435 \u0432\u043D\u0443\u0442\u0440\u0438 \u043D\u0435\u0433\u043E \u043E\u0441\u0442\u0430\u0432\u044C \u0414\u041E\u0421\u041B\u041E\u0412\u041D\u041E \u0442\u0430\u043A, \u043A\u0430\u043A \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E \u0441\u0435\u0439\u0447\u0430\u0441, \u0432\u043F\u043B\u043E\u0442\u044C \u0434\u043E \u0444\u043E\u0440\u043C\u0443\u043B\u0438\u0440\u043E\u0432\u043E\u043A. \u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043E\u0434\u043D\u0443 \u0437\u043E\u043D\u0443 \u2014 \u044D\u0442\u043E \u0441\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0441\u043F\u0438\u0441\u043E\u043A \u0438 \u0434\u043E\u043F\u0438\u0441\u0430\u0442\u044C \u0432 \u043D\u0435\u0433\u043E \u0441\u0442\u0440\u043E\u043A\u0443, \u0430 \u043D\u0435 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u0430\u0442\u044C \u0441\u043F\u0438\u0441\u043E\u043A \u0437\u0430\u043D\u043E\u0432\u043E."
|
|
9667
|
+
];
|
|
9668
|
+
if (byHuman.length) {
|
|
9669
|
+
lines.push(
|
|
9670
|
+
`\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.`
|
|
9671
|
+
);
|
|
9672
|
+
}
|
|
9673
|
+
return lines.join("\n");
|
|
9674
|
+
}
|
|
9675
|
+
function pickFields(profile) {
|
|
9676
|
+
const out = {};
|
|
9677
|
+
for (const f of SCOUT_FIELDS) if (profile[f] !== void 0 && profile[f] !== null) out[f] = profile[f];
|
|
9678
|
+
return out;
|
|
9679
|
+
}
|
|
9680
|
+
function parseScoutReport(text) {
|
|
9681
|
+
const json = stripFence(text).trim();
|
|
9682
|
+
if (!json) return { ok: false, error: "\u0440\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 (\u0444\u0430\u0439\u043B \u043F\u0443\u0441\u0442)" };
|
|
9683
|
+
let raw;
|
|
9684
|
+
try {
|
|
9685
|
+
raw = JSON.parse(json);
|
|
9686
|
+
} catch (e) {
|
|
9687
|
+
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}` };
|
|
9688
|
+
}
|
|
9689
|
+
if (raw == null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
9690
|
+
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" };
|
|
9691
|
+
}
|
|
9692
|
+
const src = raw;
|
|
9693
|
+
const report = {};
|
|
9694
|
+
const fields = [];
|
|
9695
|
+
for (const f of SCOUT_FIELDS) {
|
|
9696
|
+
const value = src[f];
|
|
9697
|
+
if (value === void 0 || value === null) continue;
|
|
9698
|
+
report[f] = value;
|
|
9699
|
+
fields.push(f);
|
|
9700
|
+
}
|
|
9701
|
+
if (fields.length === 0) {
|
|
9702
|
+
const foreign = Object.keys(src).filter((k) => !SCOUT_FIELDS.includes(k));
|
|
9703
|
+
if (foreign.length) {
|
|
9704
|
+
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(", ")})` };
|
|
9705
|
+
}
|
|
9706
|
+
}
|
|
9707
|
+
return { ok: true, report, fields };
|
|
9708
|
+
}
|
|
9709
|
+
function stripFence(text) {
|
|
9710
|
+
const fenced = /```(?:json)?\s*([\s\S]*?)```/.exec(text);
|
|
9711
|
+
return fenced?.[1] ?? text;
|
|
9712
|
+
}
|
|
9713
|
+
function renderScoutOutcome(repoFullName, diff) {
|
|
9714
|
+
const label = { new: "\u043D\u043E\u0432\u043E\u0435", changed: "\u0438\u0437\u043C\u0435\u043D\u0438\u043B\u043E\u0441\u044C", same: "\u043A\u0430\u043A \u0431\u044B\u043B\u043E", gone: "\u043D\u0435 \u0442\u0440\u043E\u0433\u0430\u0435\u0442" };
|
|
9715
|
+
const changed = diff.filter((d) => d.change === "new" || d.change === "changed");
|
|
9716
|
+
if (!changed.length) {
|
|
9717
|
+
return [`\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${repoFullName}: \u043D\u043E\u0432\u043E\u0433\u043E \u043D\u0438\u0447\u0435\u0433\u043E \u2014 \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0441\u043E\u0432\u043F\u0430\u043B\u043E \u0441 \u0442\u0435\u043C, \u0447\u0442\u043E \u0443\u0436\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E.`];
|
|
9718
|
+
}
|
|
9719
|
+
const lines = [`\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${repoFullName}: \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u043E \u043A \u043F\u0440\u0438\u0451\u043C\u043A\u0435 ${changed.length} \u043F\u043E\u043B(\u044F/\u0435\u0439).`];
|
|
9720
|
+
for (const d of changed) {
|
|
9721
|
+
const human = d.overwritesHuman ? " \u26A0\uFE0F \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u0430\u043B\u043E \u0431\u044B \u043F\u0440\u0430\u0432\u043A\u0443 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430 \u2014 \u0440\u0435\u0448\u0438\u0442 \u043E\u043D \u0441\u0430\u043C" : "";
|
|
9722
|
+
lines.push(` \u2022 ${d.field} \u2014 ${label[d.change] ?? d.change}${human}`);
|
|
9723
|
+
}
|
|
9724
|
+
const untouched = diff.filter((d) => d.change === "gone").map((d) => d.field);
|
|
9725
|
+
if (untouched.length) lines.push(` \u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 \u043F\u0440\u043E\u043C\u043E\u043B\u0447\u0430\u043B\u0430 (\u043E\u0441\u0442\u0430\u043D\u0435\u0442\u0441\u044F \u043A\u0430\u043A \u0435\u0441\u0442\u044C): ${untouched.join(", ")}`);
|
|
9726
|
+
lines.push("\u0416\u0438\u0432\u0430\u044F \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0430 \u041D\u0415 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0430: \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0435\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u043D\u0430 \u044D\u043A\u0440\u0430\u043D\u0435 \u043F\u0440\u0438\u0451\u043C\u043A\u0438.");
|
|
9727
|
+
return lines;
|
|
9728
|
+
}
|
|
9729
|
+
async function runRepoScout(deps) {
|
|
9730
|
+
const profile = await deps.readProfile();
|
|
9731
|
+
deps.log(
|
|
9732
|
+
profile ? `\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${deps.repoFullName}: \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0443\u0436\u0435 \u0440\u0430\u0437\u043C\u0435\u0447\u0435\u043D \u2014 \u0438\u0449\u0435\u043C \u0420\u0410\u0417\u041D\u0418\u0426\u0423 (\u0440\u0443\u0447\u043D\u044B\u0435 \u043F\u0440\u0430\u0432\u043A\u0438 \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0430 \u043D\u0435 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u044B\u0432\u0430\u0435\u0442 \u043C\u043E\u043B\u0447\u0430).` : `\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${deps.repoFullName}: \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0438 \u043D\u0435\u0442 \u2014 \u0440\u0430\u0437\u043C\u0435\u0447\u0430\u0435\u043C \u0432\u043F\u0435\u0440\u0432\u044B\u0435.`
|
|
9733
|
+
);
|
|
9734
|
+
const prompt = buildScoutPrompt({
|
|
9735
|
+
repoFullName: deps.repoFullName,
|
|
9736
|
+
repoPath: deps.repoPath,
|
|
9737
|
+
outFile: deps.outFile,
|
|
9738
|
+
profile,
|
|
9739
|
+
...deps.name ? { name: deps.name } : {}
|
|
9740
|
+
});
|
|
9741
|
+
deps.clearOut();
|
|
9742
|
+
const code = await deps.runScout(prompt);
|
|
9743
|
+
const raw = deps.readOut();
|
|
9744
|
+
if (raw == null) {
|
|
9745
|
+
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.`);
|
|
9746
|
+
return 1;
|
|
9747
|
+
}
|
|
9748
|
+
const parsed = parseScoutReport(raw);
|
|
9749
|
+
if (!parsed.ok) {
|
|
9750
|
+
deps.log(`\u041F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0440\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A\u0430 \u043D\u0435 \u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F: ${parsed.error}. \u0420\u0430\u0437\u043C\u0435\u0442\u043A\u0430 \u043D\u0435 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0430, \u0444\u0430\u0439\u043B \u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D: ${deps.outFile}`);
|
|
9751
|
+
return 1;
|
|
9752
|
+
}
|
|
9753
|
+
if (parsed.fields.length === 0) {
|
|
9754
|
+
deps.log(
|
|
9755
|
+
profile ? `\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${deps.repoFullName}: \u0440\u0430\u0437\u043D\u0438\u0446\u044B \u043D\u0435\u0442 \u2014 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0441\u043E\u0432\u043F\u0430\u043B \u0441 \u0442\u0435\u043C, \u0447\u0442\u043E \u0443\u0436\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E. \u0420\u0430\u0437\u043C\u0435\u0442\u043A\u0430 \u043D\u0435 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0430.` : `\u0420\u0430\u0437\u0432\u0435\u0434\u043A\u0430 ${deps.repoFullName}: \u0440\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A \u043D\u0435 \u043D\u0430\u0448\u0451\u043B, \u0447\u0442\u043E \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0438\u0442\u044C, \u2014 \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0430 \u043E\u0441\u0442\u0430\u043B\u0430\u0441\u044C \u043F\u0443\u0441\u0442\u043E\u0439. \u042D\u0442\u043E \u043E\u0442\u0432\u0435\u0442, \u0430 \u043D\u0435 \u0441\u0431\u043E\u0439: \u043F\u0443\u0441\u0442\u043E\u0435 \u043F\u043E\u043B\u0435 \u0447\u0435\u0441\u0442\u043D\u0435\u0435 \u0432\u044B\u0434\u0443\u043C\u0430\u043D\u043D\u043E\u0433\u043E.`
|
|
9756
|
+
);
|
|
9757
|
+
return 0;
|
|
9758
|
+
}
|
|
9759
|
+
deps.log(`\u0420\u0430\u0437\u0432\u0435\u0434\u0447\u0438\u043A \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0438\u043B \u043F\u043E\u043B\u044F: ${parsed.fields.join(", ")}. \u041E\u0442\u043F\u0440\u0430\u0432\u043B\u044F\u044E \u0432 Taskless \u2014 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0442\u044C \u0431\u0443\u0434\u0435\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A.`);
|
|
9760
|
+
const sent = await deps.report(parsed.report);
|
|
9761
|
+
if (!sent.ok) {
|
|
9762
|
+
deps.log(`\u041E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C \u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C: ${sent.error}. \u041E\u043D\u043E \u043E\u0441\u0442\u0430\u043B\u043E\u0441\u044C \u0432 ${deps.outFile} \u2014 \u043C\u043E\u0436\u043D\u043E \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C \u0440\u0430\u0437\u0432\u0435\u0434\u043A\u0443.`);
|
|
9763
|
+
return 1;
|
|
9764
|
+
}
|
|
9765
|
+
deps.clearOut();
|
|
9766
|
+
for (const line of renderScoutOutcome(deps.repoFullName, sent.diff)) deps.log(line);
|
|
9767
|
+
return 0;
|
|
9768
|
+
}
|
|
9769
|
+
|
|
9770
|
+
// src/cli.ts
|
|
9771
|
+
init_names2();
|
|
9772
|
+
init_executors();
|
|
9773
|
+
init_claudeCode();
|
|
9774
|
+
import { spawn as spawn3 } from "child_process";
|
|
9775
|
+
|
|
9520
9776
|
// src/login.ts
|
|
9521
9777
|
import { execa as execa2 } from "execa";
|
|
9522
9778
|
import { createServer } from "net";
|
|
@@ -10277,6 +10533,12 @@ function usage() {
|
|
|
10277
10533
|
swarm spawn <slug> [area] [\u0444\u043B\u0430\u0433\u0438 \u043D\u0438\u0436\u0435]
|
|
10278
10534
|
\u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u044C
|
|
10279
10535
|
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
|
|
10536
|
+
swarm scout [--repo <path>]
|
|
10537
|
+
\u0420\u0410\u0417\u0412\u0415\u0414\u041A\u0410 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F: \u0434\u0440\u043E\u043D \u0447\u0438\u0442\u0430\u0435\u0442 \u043A\u043E\u0434 \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 \u0438 \u043F\u0440\u0435\u0434\u043B\u0430\u0433\u0430\u0435\u0442
|
|
10538
|
+
\u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0443 \u2014 \u0437\u043E\u043D\u044B area:*, \u0447\u0435\u043C \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435, \u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0442\u044C \u0438 \u0447\u0442\u043E
|
|
10539
|
+
\u0437\u0434\u0435\u0441\u044C \u0437\u0435\u043B\u0451\u043D\u043E\u0435, \u043A\u043E\u043D\u0432\u0435\u043D\u0446\u0438\u0438, \u043E\u0431\u0449\u0438\u0439 \u0445\u0440\u0435\u0431\u0435\u0442, \u0431\u0440\u0438\u0444. \u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u044F\u0435\u0442:
|
|
10540
|
+
\u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0432 Taskless (\u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u043F\u0440\u043E\u0435\u043A\u0442\u0430).
|
|
10541
|
+
\u041F\u043E\u0432\u0442\u043E\u0440\u043D\u044B\u0439 \u0437\u0430\u043F\u0443\u0441\u043A \u043D\u0430 \u0440\u0430\u0437\u043C\u0435\u0447\u0435\u043D\u043D\u043E\u043C \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0440\u0430\u0437\u043D\u0438\u0446\u0443
|
|
10280
10542
|
swarm zones [check|suggest <\u0442\u0435\u043A\u0441\u0442|slug>]
|
|
10281
10543
|
\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
|
|
10282
10544
|
\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
|
|
@@ -10364,6 +10626,45 @@ async function makeCliStore(repo) {
|
|
|
10364
10626
|
client.reconcileRuntime({ worktreesDir: effectiveWorktreesDir(), listProcesses: listDroneProcesses });
|
|
10365
10627
|
return client;
|
|
10366
10628
|
}
|
|
10629
|
+
async function runScoutCommand(repo) {
|
|
10630
|
+
const repoFullName = await remoteRepoFullName(repo);
|
|
10631
|
+
if (!repoFullName) {
|
|
10632
|
+
console.error(`\u043D\u0435 \u043F\u043E\u043D\u044F\u043B, \u0447\u0442\u043E \u0437\u0430 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u0432 ${repo}: \u0443 origin \u043D\u0435\u0442 GitHub-\u0430\u0434\u0440\u0435\u0441\u0430. \u0420\u0430\u0437\u043C\u0435\u0442\u043A\u0430 \u0430\u0434\u0440\u0435\u0441\u0443\u0435\u0442\u0441\u044F \u043F\u043E owner/repo.`);
|
|
10633
|
+
return 1;
|
|
10634
|
+
}
|
|
10635
|
+
const call = resilientSwarmMcp({
|
|
10636
|
+
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.`),
|
|
10637
|
+
onUp: () => console.warn("Swarm MCP \u0441\u043D\u043E\u0432\u0430 \u043D\u0430 \u0441\u0432\u044F\u0437\u0438.")
|
|
10638
|
+
});
|
|
10639
|
+
const client = new SwarmClient({ call, outboxPath: SWARM_OUTBOX, defaultRepo: repo, autoFlush: false });
|
|
10640
|
+
const name = pickName([]);
|
|
10641
|
+
const outFile = join23(SWARM_HOME, `scout-${repoFullName.replace(/[^\w.-]+/g, "-")}.json`);
|
|
10642
|
+
const bin = resolveExecutor().resolveBin();
|
|
10643
|
+
const code = await runRepoScout({
|
|
10644
|
+
repoFullName,
|
|
10645
|
+
repoPath: repo,
|
|
10646
|
+
name,
|
|
10647
|
+
outFile,
|
|
10648
|
+
readProfile: () => client.repoProfile(repoFullName),
|
|
10649
|
+
runScout: (prompt) => new Promise((resolve4) => {
|
|
10650
|
+
const child = spawn3(bin, [...ISOLATION_FLAGS, "--dangerously-skip-permissions", "-p", prompt], {
|
|
10651
|
+
cwd: repo,
|
|
10652
|
+
stdio: ["ignore", "inherit", "inherit"]
|
|
10653
|
+
// человек смотрит на разведку живьём
|
|
10654
|
+
});
|
|
10655
|
+
child.on("error", (e) => {
|
|
10656
|
+
console.error(`\u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u043B\u0441\u044F \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044C (${bin}): ${e.message}`);
|
|
10657
|
+
resolve4(1);
|
|
10658
|
+
});
|
|
10659
|
+
child.on("exit", (c) => resolve4(c ?? 1));
|
|
10660
|
+
}),
|
|
10661
|
+
readOut: () => existsSync14(outFile) ? readFileSync16(outFile, "utf8") : null,
|
|
10662
|
+
clearOut: () => rmSync2(outFile, { force: true }),
|
|
10663
|
+
report: (report) => client.reportRepoScout(repoFullName, report, name),
|
|
10664
|
+
log: (line) => console.log(line)
|
|
10665
|
+
});
|
|
10666
|
+
return code;
|
|
10667
|
+
}
|
|
10367
10668
|
async function runZones(db2, repo, args) {
|
|
10368
10669
|
const manual = new Map(db2.listRuns().map((r) => [r.task_slug, r.area]));
|
|
10369
10670
|
const raw = await readTaskHistory(repo);
|
|
@@ -10856,6 +11157,20 @@ switch (cmd) {
|
|
|
10856
11157
|
// swarm zones — какие зоны видит рой и что считает общим хребтом
|
|
10857
11158
|
// swarm zones check — сверка с ручной разметкой area:* на прошлых задачах
|
|
10858
11159
|
// swarm zones suggest <текст|slug> — какую зону предложить задаче ПРИ ПОСТАНОВКЕ
|
|
11160
|
+
// TASWM-379/382: РАЗВЕДКА репозитория — одноразовый прогон, после которого рой знает
|
|
11161
|
+
// про чужой репозиторий то, что на taskless записано в CLAUDE.md и скиллах. Предложение
|
|
11162
|
+
// разведчика НЕ применяется: его принимает человек (см. repoScout.ts).
|
|
11163
|
+
case "scout": {
|
|
11164
|
+
const { repo: repoFlag, rest } = takeRepo(rawArgs);
|
|
11165
|
+
const repo = repoFlag ?? rest[0] ?? SWARM_REPO;
|
|
11166
|
+
if (!repo) {
|
|
11167
|
+
console.error("\u043D\u0435\u0442 \u0440\u0435\u043F\u043E: \u0437\u0430\u0434\u0430\u0439 --repo <path> \u0438\u043B\u0438 SWARM_REPO");
|
|
11168
|
+
process.exit(1);
|
|
11169
|
+
}
|
|
11170
|
+
const code = await runScoutCommand(repo);
|
|
11171
|
+
db.close();
|
|
11172
|
+
process.exit(code);
|
|
11173
|
+
}
|
|
10859
11174
|
case "zones": {
|
|
10860
11175
|
const { repo: repoFlag, rest } = takeRepo(rawArgs);
|
|
10861
11176
|
const repo = repoFlag ?? SWARM_REPO;
|
package/dist/overmind.js
CHANGED
|
@@ -75,7 +75,11 @@ var init_types = __esm({
|
|
|
75
75
|
// прогона перед самым спавном: «поехал молча» означало бы, что причина запуска исчезла из
|
|
76
76
|
// истории, и следующий разбор «почему он стартовал поверх незакрытого блокера» упёрся бы в
|
|
77
77
|
// пустоту. Успешное снятие (блокер смержен) события не порождает — это штатный ход.
|
|
78
|
-
"dep_cleared"
|
|
78
|
+
"dep_cleared",
|
|
79
|
+
// TASWM-412: дрон вернул сырую задачу автору (см. RunStatus "returned"). Веха, а не step:
|
|
80
|
+
// прогон на этом кончается, и в истории должно быть видно, что он кончился ВОЗВРАТОМ —
|
|
81
|
+
// иначе возврат читается как брошенная посреди работы задача.
|
|
82
|
+
"returned"
|
|
79
83
|
];
|
|
80
84
|
MILESTONE_KINDS = [
|
|
81
85
|
"spawned",
|
|
@@ -98,8 +102,10 @@ var init_types = __esm({
|
|
|
98
102
|
// надзор overmind (TASWM-194) — та же нотификация, что улетела в инбокс, вехой в Истории
|
|
99
103
|
"note",
|
|
100
104
|
// заметка дрона «замечено по пути» (TASWM-279) — веха, иначе находка тонет в ленте step'ов
|
|
101
|
-
"dep_cleared"
|
|
105
|
+
"dep_cleared",
|
|
102
106
|
// зависимость снята неуспешным финалом блокера (TASWM-361) — решение роя, веха как conflict
|
|
107
|
+
"returned"
|
|
108
|
+
// дрон вернул сырую задачу автору (TASWM-412) — финал прогона, веха как done/failed
|
|
103
109
|
];
|
|
104
110
|
MILESTONE_KIND_SET = new Set(MILESTONE_KINDS);
|
|
105
111
|
}
|
|
@@ -177,6 +183,14 @@ function buildSegments(created, events, laneEnd) {
|
|
|
177
183
|
push(e.at);
|
|
178
184
|
phase = "superseded";
|
|
179
185
|
break;
|
|
186
|
+
// TASWM-412: возврат автору — конец прогона, хвост дорожки не должен дорисовываться
|
|
187
|
+
// «работающим» зелёным (та же готча, что чинил TASWM-166 для stopped/superseded).
|
|
188
|
+
// Фаза — ЧУЖАЯ, "stopped": своя фаза значит свой цвет-токен в обеих темах web, то есть
|
|
189
|
+
// правку дизайн-системы; здесь важнее, что полоса перестаёт врать про живую работу.
|
|
190
|
+
case "returned":
|
|
191
|
+
push(e.at);
|
|
192
|
+
phase = "stopped";
|
|
193
|
+
break;
|
|
180
194
|
}
|
|
181
195
|
}
|
|
182
196
|
push(laneEnd);
|
|
@@ -214,6 +228,8 @@ function computeBlocker(status, stopReason, lastStep, pr, ci, deps, openQ) {
|
|
|
214
228
|
return { icon: "\u23F9", text: stopReason || "\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D", tone: "stopped" };
|
|
215
229
|
case "superseded":
|
|
216
230
|
return { icon: "\u21BA", text: "\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D \u043D\u0430 \u0434\u0440\u0443\u0433\u043E\u0439 \u0432\u0430\u0440\u0438\u0430\u043D\u0442", tone: "superseded" };
|
|
231
|
+
case "returned":
|
|
232
|
+
return { icon: "\u21A9", text: stopReason || "\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443 \u043D\u0430 \u0443\u0442\u043E\u0447\u043D\u0435\u043D\u0438\u0435", tone: "stopped" };
|
|
217
233
|
case "done":
|
|
218
234
|
return { icon: "\u2705", text: "\u0441\u043C\u0435\u0440\u0436\u0435\u043D\u043E", tone: "done" };
|
|
219
235
|
default:
|
|
@@ -380,7 +396,11 @@ var init_status = __esm({
|
|
|
380
396
|
storm_dead: { pill: "storm", label: "\u0448\u0442\u043E\u0440\u043C \u2014 \u0437\u0430\u043C\u043E\u0440\u043E\u0436\u0435\u043D", color: "--warning", terminal: false },
|
|
381
397
|
// умер в шторм Anthropic, поднимется после отбоя (TASWM-23)
|
|
382
398
|
stopped: { pill: "stopped", label: "\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D", color: "--muted-foreground", terminal: true },
|
|
383
|
-
superseded: { pill: "superseded", label: "\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D", color: "--muted-foreground", terminal: true }
|
|
399
|
+
superseded: { pill: "superseded", label: "\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D", color: "--muted-foreground", terminal: true },
|
|
400
|
+
// TASWM-412: дрон вернул сырую задачу автору (вопросы комментом, задача назначена на автора
|
|
401
|
+
// и переведена в работу) и освободил слот. Терминальный — работа этого прогона закончена;
|
|
402
|
+
// цвет предупреждающий, а не destructive: смотреть должен АВТОР задачи, чинить нечего.
|
|
403
|
+
returned: { pill: "returned", label: "\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443", color: "--warning", terminal: true }
|
|
384
404
|
};
|
|
385
405
|
AWAITING_CI_REASON = "\u0436\u0434\u0451\u0442 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 CI \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 PR \u0435\u0449\u0451 \u0438\u0434\u0443\u0442";
|
|
386
406
|
FALLBACK = STATUS_META.queued;
|
|
@@ -4231,13 +4251,17 @@ __export(taskless_exports, {
|
|
|
4231
4251
|
requireTaskBody: () => requireTaskBody,
|
|
4232
4252
|
resetLegacyTokenWarning: () => resetLegacyTokenWarning,
|
|
4233
4253
|
resolveApiToken: () => resolveApiToken,
|
|
4234
|
-
swarmToolArgs: () => swarmToolArgs
|
|
4254
|
+
swarmToolArgs: () => swarmToolArgs,
|
|
4255
|
+
taskAuthor: () => taskAuthor
|
|
4235
4256
|
});
|
|
4236
4257
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4237
4258
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
4238
4259
|
function isClosingTaskCategory(category) {
|
|
4239
4260
|
return category === "completed" || category === "cancelled" || category === "archived";
|
|
4240
4261
|
}
|
|
4262
|
+
function taskAuthor(u) {
|
|
4263
|
+
return u?.id ? { id: u.id, name: u.displayName || "\u0430\u0432\u0442\u043E\u0440 \u0437\u0430\u0434\u0430\u0447\u0438" } : null;
|
|
4264
|
+
}
|
|
4241
4265
|
function makeTasklessClient(call, workspaceId, taskUrlBase, fetchFile, fetchImpl = fetch) {
|
|
4242
4266
|
const ws = workspaceId ? { workspaceId } : {};
|
|
4243
4267
|
const idCache = /* @__PURE__ */ new Map();
|
|
@@ -4277,7 +4301,10 @@ function makeTasklessClient(call, workspaceId, taskUrlBase, fetchFile, fetchImpl
|
|
|
4277
4301
|
documents: (task.documents ?? []).filter((d) => !!d.id).map((d) => ({ id: d.id, title: d.title ?? d.id, url: d.url ?? null })),
|
|
4278
4302
|
// TASWM-283: только ССЫЛКИ (порядок = порядок в задаче). Скачивание — на спавне,
|
|
4279
4303
|
// в worktree конкретного дрона; getTask зовут и там, где выкладывать некуда.
|
|
4280
|
-
attachments: collectTaskAttachments({ description: task.description, comments: task.comments ?? [] })
|
|
4304
|
+
attachments: collectTaskAttachments({ description: task.description, comments: task.comments ?? [] }),
|
|
4305
|
+
// TASWM-412: creator, иначе requester. Прод старее этой правки автора не отдаёт вовсе —
|
|
4306
|
+
// тогда null, и возврат просто обойдётся без переназначения (комментарий важнее).
|
|
4307
|
+
author: taskAuthor(task.creator) ?? taskAuthor(task.requester)
|
|
4281
4308
|
};
|
|
4282
4309
|
idCache.set(slug, body.id);
|
|
4283
4310
|
idCache.set(body.id, body.id);
|
|
@@ -4333,6 +4360,17 @@ function makeTasklessClient(call, workspaceId, taskUrlBase, fetchFile, fetchImpl
|
|
|
4333
4360
|
startTask: (idOrSlug) => transition("start_task", idOrSlug),
|
|
4334
4361
|
// TASWM-294: intent — «человек, посмотри», а не «Готово» (см. докблок в TasklessClient).
|
|
4335
4362
|
finishTask: (idOrSlug) => transition("finish_task", idOrSlug, { intent: "needs_check" }),
|
|
4363
|
+
// TASWM-412: назначение — через общий update_task (тот же путь, что и у UI-роута),
|
|
4364
|
+
// отдельного «assign»-тула у Taskless нет. Успех считаем по факту ответа тула:
|
|
4365
|
+
// safeCall уже отсеял isError и падения, вернув null.
|
|
4366
|
+
assignTask: async (idOrSlug, userId) => {
|
|
4367
|
+
const id = await resolveId(idOrSlug);
|
|
4368
|
+
if (!id) {
|
|
4369
|
+
console.error(`[taskless] update_task(assigneeId) \u043F\u0440\u043E\u043F\u0443\u0449\u0435\u043D: \u0437\u0430\u0434\u0430\u0447\u0430 ${idOrSlug} \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u0430`);
|
|
4370
|
+
return false;
|
|
4371
|
+
}
|
|
4372
|
+
return await safeCall("update_task", { id, assigneeId: userId }) != null;
|
|
4373
|
+
},
|
|
4336
4374
|
uploadShot: (file) => uploadShot(safeCall, file, fetchImpl),
|
|
4337
4375
|
addComment: async (idOrSlug, body, label, attachments) => {
|
|
4338
4376
|
const id = await resolveId(idOrSlug);
|
|
@@ -7008,6 +7046,51 @@ var SwarmClient = class _SwarmClient {
|
|
|
7008
7046
|
if (!Array.isArray(prepare)) return null;
|
|
7009
7047
|
return prepare.filter((c) => typeof c === "string" && c.trim().length > 0).map((c) => c.trim());
|
|
7010
7048
|
}
|
|
7049
|
+
/**
|
|
7050
|
+
* РАЗМЕТКА репозитория (TASWM-381) из того же swarm_repo_config: зоны, чем поднять
|
|
7051
|
+
* окружение, чем проверять, конвенции, общий хребет, бриф — плюс карта источников
|
|
7052
|
+
* (кто вписал поле: разведчик или человек). Отдаём КАК ЕСТЬ, объектом: строгая
|
|
7053
|
+
* схема разметки живёт в проде (@taskless-app/shared), и второй её копии в демоне
|
|
7054
|
+
* быть не должно — разъехавшись, они спорили бы о том, что считать валидным.
|
|
7055
|
+
*
|
|
7056
|
+
* null — разметки нет (разведка не проходила; РАБОЧЕЕ состояние), репо не смэплено
|
|
7057
|
+
* на swarm-проект владельца токена или прод недоступен.
|
|
7058
|
+
*/
|
|
7059
|
+
async repoProfile(repoFullName) {
|
|
7060
|
+
let res;
|
|
7061
|
+
try {
|
|
7062
|
+
res = await this.call("swarm_repo_config", { repoFullName });
|
|
7063
|
+
} catch {
|
|
7064
|
+
return null;
|
|
7065
|
+
}
|
|
7066
|
+
if (res?.isError) return null;
|
|
7067
|
+
const sc = res.structuredContent ?? {};
|
|
7068
|
+
const p = sc.profile;
|
|
7069
|
+
return p && typeof p === "object" && !Array.isArray(p) ? p : null;
|
|
7070
|
+
}
|
|
7071
|
+
/**
|
|
7072
|
+
* РАЗВЕДКА репозитория (TASWM-382): отправить в прод то, что разведчик вычитал в коде.
|
|
7073
|
+
* Живую разметку это НЕ меняет — предложение ложится рядом и ждёт человека; поэтому
|
|
7074
|
+
* ошибка здесь дорога ровно на стоимость прогона разведки, и мы её возвращаем, а не
|
|
7075
|
+
* глотаем, как чтения выше.
|
|
7076
|
+
*
|
|
7077
|
+
* Мимо outbox (в отличие от мутаций прогонов): предложение имеет смысл только вместе
|
|
7078
|
+
* со свежим состоянием репозитория, и доехавшее через сутки после разведки — хуже,
|
|
7079
|
+
* чем не доехавшее вовсе. Прод недоступен — человек перезапустит разведку.
|
|
7080
|
+
*/
|
|
7081
|
+
async reportRepoScout(repoFullName, report, by) {
|
|
7082
|
+
let res;
|
|
7083
|
+
try {
|
|
7084
|
+
res = await this.call("swarm_repo_scout_report", { repoFullName, report, ...by ? { by } : {} });
|
|
7085
|
+
} catch (e) {
|
|
7086
|
+
return { ok: false, error: `\u043F\u0440\u043E\u0434 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D: ${e.message}` };
|
|
7087
|
+
}
|
|
7088
|
+
const text = res?.content?.map((c) => "text" in c ? c.text : "").join(" ") ?? "";
|
|
7089
|
+
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" };
|
|
7090
|
+
const sc = res.structuredContent ?? {};
|
|
7091
|
+
const diff = Array.isArray(sc.diff) ? sc.diff : [];
|
|
7092
|
+
return { ok: true, diff, display: text };
|
|
7093
|
+
}
|
|
7011
7094
|
/**
|
|
7012
7095
|
* Live ТРЕБОВАНИЯ К ОКРУЖЕНИЮ машины из Taskless (TASWM-313): тот же swarm_repo_config по
|
|
7013
7096
|
* repoFullName — Project.swarmConfig.requires. Демон проверяет их ПЕРЕД спавном дрона и не
|
|
@@ -9751,6 +9834,7 @@ async function buildDroneServer(store, opts = {}) {
|
|
|
9751
9834
|
const server = new McpServer({ name: "overmind-drone", version: "0.1.0" });
|
|
9752
9835
|
const overmindDead = async () => !await store.alive();
|
|
9753
9836
|
const slugForInstruction = (fallback) => opts.expectedTaskSlug ?? fallback ?? "<slug>";
|
|
9837
|
+
const iAmReviewer = (run) => run?.reviews_run_id != null || run?.kind === "reviewer" || opts.reviewsRunId != null || opts.runKind === "reviewer";
|
|
9754
9838
|
const freeze = async (withdraw = false) => {
|
|
9755
9839
|
let slug;
|
|
9756
9840
|
let branch;
|
|
@@ -9903,6 +9987,120 @@ ${notFoundText}` : notFoundText }],
|
|
|
9903
9987
|
return { content: [{ type: "text", text: "\u0437\u0430\u043C\u0435\u0442\u043A\u0430 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u0430 \u0438 \u0443\u0435\u0434\u0435\u0442 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0443; \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0439 \u0440\u0430\u0431\u043E\u0442\u0443 \u2014 \u043E\u0442\u0432\u0435\u0442\u0430 \u043D\u0435 \u0436\u0434\u0438." }] };
|
|
9904
9988
|
})
|
|
9905
9989
|
);
|
|
9990
|
+
server.tool(
|
|
9991
|
+
"return_task",
|
|
9992
|
+
"\u0412\u0435\u0440\u043D\u0443\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u0410\u0412\u0422\u041E\u0420\u0423 \u043D\u0430 \u0443\u0442\u043E\u0447\u043D\u0435\u043D\u0438\u0435 \u0438 \u0437\u0430\u043A\u0440\u044B\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D (\u0441\u043B\u043E\u0442 \u043E\u0441\u0432\u043E\u0431\u043E\u0434\u0438\u0442\u0441\u044F, \u0440\u043E\u0439 \u0432\u043E\u0437\u044C\u043C\u0451\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0443\u044E \u0437\u0430\u0434\u0430\u0447\u0443). \u0417\u043E\u0432\u0438, \u0422\u041E\u041B\u042C\u041A\u041E \u0435\u0441\u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435\u043B\u044C\u0437\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u043D\u0438 \u0432 \u043E\u0434\u043D\u043E\u043C \u0440\u0430\u0437\u0443\u043C\u043D\u043E\u043C \u0442\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u0438 \u0418\u041B\u0418 \u043D\u0435\u043F\u043E\u043D\u044F\u0442\u043D\u043E, \u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442. \u0414\u0432\u0430 \u043E\u0441\u043C\u044B\u0441\u043B\u0435\u043D\u043D\u044B\u0445 \u0442\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u044F \u2014 \u041D\u0415 \u043F\u043E\u0432\u043E\u0434: \u0432\u044B\u0431\u0435\u0440\u0438 \u043E\u0447\u0435\u0432\u0438\u0434\u043D\u043E\u0435, \u0441\u0434\u0435\u043B\u0430\u0439 \u0438 \u043D\u0430\u0437\u043E\u0432\u0438 \u0440\u0430\u0437\u0432\u0438\u043B\u043A\u0443 \u0432 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0438 PR. \u041F\u043E\u043D\u044F\u0442\u043D\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430, \u043A\u043E\u0442\u043E\u0440\u0430\u044F \u0432 \u043E\u0434\u043D\u043E\u043C \u043F\u0443\u043D\u043A\u0442\u0435 \u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0440\u0435\u0447\u0438\u0442 \u043F\u0440\u0430\u0432\u0438\u043B\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430, \u2014 \u0442\u043E\u0436\u0435 \u043D\u0435 \u043F\u043E\u0432\u043E\u0434: \u044D\u0442\u043E ask. \u0417\u0430\u0434\u0430\u0447\u0430 \u0443\u0439\u0434\u0451\u0442 \u0430\u0432\u0442\u043E\u0440\u0443 (\u043D\u0430\u0437\u043D\u0430\u0447\u0438\u043C \u043D\u0430 \u043D\u0435\u0433\u043E \u0438 \u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0451\u043C \u0432 \u0440\u0430\u0431\u043E\u0442\u0443), \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0443\u0435\u0434\u0443\u0442 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u043E\u0442 \u0442\u0432\u043E\u0435\u0433\u043E \u0438\u043C\u0435\u043D\u0438. \u041F\u043E\u0441\u043B\u0435 \u0432\u044B\u0437\u043E\u0432\u0430 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0437\u043E\u0432\u0438 \u2014 \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0441\u0435\u0441\u0441\u0438\u044E.",
|
|
9993
|
+
{
|
|
9994
|
+
missing: z.string().min(30).describe("\u0447\u0435\u0433\u043E \u0432 \u0437\u0430\u0434\u0430\u0447\u0435 \u043D\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442, \u0447\u0442\u043E\u0431\u044B \u0435\u0451 \u0441\u0434\u0435\u043B\u0430\u0442\u044C \u0438\u043B\u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u2014 \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u043E, \u0430 \u043D\u0435 \xAB\u043D\u0435\u043F\u043E\u043D\u044F\u0442\u043D\u043E\xBB"),
|
|
9995
|
+
readings: z.array(z.string().min(10)).optional().describe("\u0442\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u044F \u043F\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0442\u044B \u0440\u0430\u0441\u0441\u043C\u043E\u0442\u0440\u0435\u043B (\u0438 \u0447\u0435\u043C \u043A\u0430\u0436\u0434\u043E\u0435 \u043F\u043B\u043E\u0445\u043E/\u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0440\u0435\u0447\u0438\u0432\u043E)"),
|
|
9996
|
+
assumed: z.string().min(30).describe("\u0447\u0442\u043E \u0442\u044B \u043F\u0440\u0435\u0434\u043F\u043E\u043B\u043E\u0436\u0438\u043B \u0431\u044B \u0441\u0434\u0435\u043B\u0430\u0442\u044C, \u0435\u0441\u043B\u0438 \u0431\u044B \u043F\u0440\u0438\u0448\u043B\u043E\u0441\u044C \u0433\u0430\u0434\u0430\u0442\u044C, \u0438 \u043F\u043E\u0447\u0435\u043C\u0443 \u043D\u0435 \u0441\u0442\u0430\u043B"),
|
|
9997
|
+
questions: z.array(z.string().min(10)).min(1).max(7).describe("\u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u044B\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0430\u0432\u0442\u043E\u0440\u0443 \u2014 \u043E\u0442\u0432\u0435\u0442\u044B \u043D\u0430 \u043D\u0438\u0445 \u0434\u043E\u043B\u0436\u043D\u044B \u0434\u0435\u043B\u0430\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u043C\u043E\u0439")
|
|
9998
|
+
},
|
|
9999
|
+
guard(
|
|
10000
|
+
async ({
|
|
10001
|
+
missing,
|
|
10002
|
+
readings,
|
|
10003
|
+
assumed,
|
|
10004
|
+
questions
|
|
10005
|
+
}) => {
|
|
10006
|
+
if (await overmindDead()) return freeze();
|
|
10007
|
+
const run = await store.myRun();
|
|
10008
|
+
if (iAmReviewer(run) || run?.kind === "hero" || opts.runKind === "hero") {
|
|
10009
|
+
return {
|
|
10010
|
+
content: [
|
|
10011
|
+
{
|
|
10012
|
+
type: "text",
|
|
10013
|
+
text: "return_task \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0434\u043B\u044F \u0434\u0440\u043E\u043D\u0430-\u0418\u0421\u041F\u041E\u041B\u041D\u0418\u0422\u0415\u041B\u042F: \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u044E\u0442 \u0421\u0412\u041E\u042E \u0437\u0430\u0434\u0430\u0447\u0443 \u0430\u0432\u0442\u043E\u0440\u0443. \u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u043F\u0440\u043E\u0433\u043E\u043D \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u043E\u043C \u0432 done(), \u0413\u0435\u0440\u043E\u0439 \u2014 rest()."
|
|
10014
|
+
}
|
|
10015
|
+
],
|
|
10016
|
+
isError: true
|
|
10017
|
+
};
|
|
10018
|
+
}
|
|
10019
|
+
const slug = opts.expectedTaskSlug ?? run?.task_slug;
|
|
10020
|
+
if (!opts.taskless || !slug) {
|
|
10021
|
+
return {
|
|
10022
|
+
content: [
|
|
10023
|
+
{
|
|
10024
|
+
type: "text",
|
|
10025
|
+
text: "\u0412\u0435\u0440\u043D\u0443\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435\u0447\u0435\u043C: Taskless \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D (slug-only \u0440\u0435\u0436\u0438\u043C) \u0438\u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0430 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430 \u2014 \u043D\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0430\u0432\u0442\u043E\u0440\u0443 \u0438 \u043F\u0435\u0440\u0435\u043D\u0430\u0437\u043D\u0430\u0447\u0438\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E. \u0421\u043F\u0440\u043E\u0441\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 ask(), \u0447\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C."
|
|
10026
|
+
}
|
|
10027
|
+
],
|
|
10028
|
+
isError: true
|
|
10029
|
+
};
|
|
10030
|
+
}
|
|
10031
|
+
const droneName = run?.name ?? "(\u0438\u043C\u044F \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E)";
|
|
10032
|
+
const task = await opts.taskless.getTask(slug);
|
|
10033
|
+
const author = task?.author ?? null;
|
|
10034
|
+
const body = [
|
|
10035
|
+
`\u21A9\uFE0F **\u0412\u043E\u0437\u0432\u0440\u0430\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 \u0430\u0432\u0442\u043E\u0440\u0443** \u2014 \u0434\u0440\u043E\u043D ${droneName}${run ? `, \u043F\u0440\u043E\u0433\u043E\u043D #${run.id}` : ""}.`,
|
|
10036
|
+
"",
|
|
10037
|
+
`${author ? `${author.name}, ` : ""}\u044F \u043D\u0435 \u0431\u0435\u0440\u0443\u0441\u044C \u0437\u0430 \u044D\u0442\u0443 \u0437\u0430\u0434\u0430\u0447\u0443: \u0435\u0451 \u043D\u0435\u043B\u044C\u0437\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C, \u043D\u0435 \u0432\u044B\u0434\u0443\u043C\u0430\u0432 \u043F\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0443 \u0437\u0430 \u0442\u0435\u0431\u044F. \u0427\u0442\u043E \u043C\u0435\u0448\u0430\u0435\u0442:`,
|
|
10038
|
+
"",
|
|
10039
|
+
`**\u0427\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442.** ${missing.trim()}`,
|
|
10040
|
+
...readings?.length ? ["", "**\u041A\u0430\u043A \u044D\u0442\u043E \u043C\u043E\u0436\u043D\u043E \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0442\u044C.**", ...readings.map((r) => `- ${r.trim()}`)] : [],
|
|
10041
|
+
"",
|
|
10042
|
+
`**\u0427\u0442\u043E \u044F \u043F\u0440\u0435\u0434\u043F\u043E\u043B\u043E\u0436\u0438\u043B \u0431\u044B \u0438 \u043F\u043E\u0447\u0435\u043C\u0443 \u043D\u0435 \u0441\u0442\u0430\u043B.** ${assumed.trim()}`,
|
|
10043
|
+
"",
|
|
10044
|
+
"**\u0412\u043E\u043F\u0440\u043E\u0441\u044B, \u043F\u043E\u0441\u043B\u0435 \u043E\u0442\u0432\u0435\u0442\u043E\u0432 \u043D\u0430 \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0437\u0430\u0434\u0430\u0447\u0443 \u043C\u043E\u0436\u043D\u043E \u0431\u0440\u0430\u0442\u044C:**",
|
|
10045
|
+
...questions.map((q, i) => `${i + 1}. ${q.trim()}`)
|
|
10046
|
+
].join("\n");
|
|
10047
|
+
const failures = [];
|
|
10048
|
+
try {
|
|
10049
|
+
await opts.taskless.addComment(slug, body);
|
|
10050
|
+
} catch (e) {
|
|
10051
|
+
failures.push(`\u043A\u043E\u043C\u043C\u0435\u043D\u0442 \u0441 \u0432\u043E\u043F\u0440\u043E\u0441\u0430\u043C\u0438 \u043D\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D (${e instanceof Error ? e.message : String(e)})`);
|
|
10052
|
+
}
|
|
10053
|
+
let assignedOk = false;
|
|
10054
|
+
if (author) {
|
|
10055
|
+
assignedOk = await opts.taskless.assignTask(slug, author.id);
|
|
10056
|
+
if (!assignedOk) failures.push("\u0437\u0430\u0434\u0430\u0447\u0430 \u043D\u0435 \u043F\u0435\u0440\u0435\u043D\u0430\u0437\u043D\u0430\u0447\u0435\u043D\u0430 \u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0430");
|
|
10057
|
+
} else {
|
|
10058
|
+
failures.push("\u0430\u0432\u0442\u043E\u0440 \u0437\u0430\u0434\u0430\u0447\u0438 \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043D (get_task \u0435\u0433\u043E \u043D\u0435 \u043E\u0442\u0434\u0430\u043B) \u2014 \u0437\u0430\u0434\u0430\u0447\u0430 \u043E\u0441\u0442\u0430\u043B\u0430\u0441\u044C \u043D\u0430 \u043F\u0440\u0435\u0436\u043D\u0435\u043C \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u0435");
|
|
10059
|
+
}
|
|
10060
|
+
await opts.taskless.startTask(slug).catch((e) => {
|
|
10061
|
+
failures.push(`\u0437\u0430\u0434\u0430\u0447\u0430 \u043D\u0435 \u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043D\u0430 \u0432 \u0440\u0430\u0431\u043E\u0442\u0443 (${e instanceof Error ? e.message : String(e)})`);
|
|
10062
|
+
});
|
|
10063
|
+
if (!assignedOk) {
|
|
10064
|
+
const after = await opts.taskless.getTask(slug).catch(() => null);
|
|
10065
|
+
if (!after || after.statusCategory === "unstarted") {
|
|
10066
|
+
await store.addEvent(
|
|
10067
|
+
null,
|
|
10068
|
+
"step",
|
|
10069
|
+
`\u0432\u043E\u0437\u0432\u0440\u0430\u0442 \u043D\u0435 \u0437\u0430\u043A\u0440\u044B\u0442: \u0437\u0430\u0434\u0430\u0447\u0430 ${slug} \u043E\u0441\u0442\u0430\u043B\u0430\u0441\u044C claimable (${failures.join("; ")}) \u2014 \u043F\u0440\u043E\u0433\u043E\u043D \u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D running, \u0447\u0442\u043E\u0431\u044B \u0440\u043E\u0439 \u043D\u0435 \u0432\u0437\u044F\u043B \u0435\u0451 \u043F\u043E \u043A\u0440\u0443\u0433\u0443`
|
|
10070
|
+
);
|
|
10071
|
+
return {
|
|
10072
|
+
content: [
|
|
10073
|
+
{
|
|
10074
|
+
type: "text",
|
|
10075
|
+
text: [
|
|
10076
|
+
`\u0412\u043E\u0437\u0432\u0440\u0430\u0442 \u041D\u0415 \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D: ${failures.join("; ")}.`,
|
|
10077
|
+
`\u0417\u0430\u0434\u0430\u0447\u0430 ${slug} \u043E\u0441\u0442\u0430\u043B\u0430\u0441\u044C \u0432 \u0442\u043E\u043C \u0436\u0435 \u0441\u0442\u0430\u0442\u0443\u0441\u0435 \u0438 \u043D\u0430 \u0442\u043E\u043C \u0436\u0435 \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u0435 \u2014 \u0437\u0430\u043A\u0440\u043E\u0439 \u044F \u043F\u0440\u043E\u0433\u043E\u043D \u0441\u0435\u0439\u0447\u0430\u0441, \u0440\u043E\u0439 \u0432\u0437\u044F\u043B \u0431\u044B \u0435\u0451 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u043C \u0434\u0440\u043E\u043D\u043E\u043C \u043F\u043E \u043A\u0440\u0443\u0433\u0443.`,
|
|
10078
|
+
"\u0412\u043E\u043F\u0440\u043E\u0441\u044B \u0430\u0432\u0442\u043E\u0440\u0443 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0443\u0436\u0435 \u0443\u0435\u0445\u0430\u043B\u0438. \u041F\u043E\u0432\u0442\u043E\u0440\u0438 return_task \u0447\u0435\u0440\u0435\u0437 \u043C\u0438\u043D\u0443\u0442\u0443 (\u043A\u043E\u043C\u043C\u0435\u043D\u0442 \u0437\u0430\u0434\u0432\u043E\u0438\u0442\u0441\u044F \u2014 \u044D\u0442\u043E \u043C\u0435\u043D\u044C\u0448\u0435\u0435 \u0437\u043B\u043E) \u043B\u0438\u0431\u043E \u0441\u043F\u0440\u043E\u0441\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 ask()."
|
|
10079
|
+
].join("\n")
|
|
10080
|
+
}
|
|
10081
|
+
],
|
|
10082
|
+
isError: true
|
|
10083
|
+
};
|
|
10084
|
+
}
|
|
10085
|
+
}
|
|
10086
|
+
const reason = `\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443${author ? ` (${author.name})` : ""}: ${missing.trim()}`;
|
|
10087
|
+
await store.updateRun(null, { status: "returned", stop_reason: reason });
|
|
10088
|
+
await store.addEvent(null, "returned", failures.length ? `${reason} \xB7 \u043D\u0435 \u0434\u043E\u0435\u0445\u0430\u043B\u043E: ${failures.join("; ")}` : reason);
|
|
10089
|
+
return {
|
|
10090
|
+
content: [
|
|
10091
|
+
{
|
|
10092
|
+
type: "text",
|
|
10093
|
+
text: [
|
|
10094
|
+
`\u0417\u0430\u0434\u0430\u0447\u0430 ${slug} \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443${author ? ` (${author.name})` : ""}, \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u0440\u044B\u0442 \u043A\u0430\u043A \xAB\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443\xBB, \u0441\u043B\u043E\u0442 \u043E\u0441\u0432\u043E\u0431\u043E\u0436\u0434\u0451\u043D.`,
|
|
10095
|
+
...failures.length ? [`\u26A0\uFE0F \u041D\u0435 \u0434\u043E\u0435\u0445\u0430\u043B\u043E: ${failures.join("; ")} \u2014 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u0443\u0432\u0438\u0434\u0438\u0442 \u044D\u0442\u043E \u0432 \u043B\u043E\u0433\u0435 \u043F\u0440\u043E\u0433\u043E\u043D\u0430.`] : [],
|
|
10096
|
+
"\u0411\u043E\u043B\u044C\u0448\u0435 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u0432 \u041D\u0415 \u0437\u043E\u0432\u0438 \u0438 \u0437\u0430\u0434\u0430\u0447\u0443 \u0434\u0430\u043B\u044C\u0448\u0435 \u041D\u0415 \u0434\u0435\u043B\u0430\u0439 \u2014 \u043F\u0440\u043E\u0441\u0442\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0441\u0435\u0441\u0441\u0438\u044E."
|
|
10097
|
+
].join("\n")
|
|
10098
|
+
}
|
|
10099
|
+
]
|
|
10100
|
+
};
|
|
10101
|
+
}
|
|
10102
|
+
)
|
|
10103
|
+
);
|
|
9906
10104
|
server.tool(
|
|
9907
10105
|
"rest",
|
|
9908
10106
|
"\u0422\u043E\u043B\u044C\u043A\u043E \u0434\u043B\u044F \u0413\u0435\u0440\u043E\u044F-\u043B\u0435\u0439\u0442\u0435\u043D\u0430\u043D\u0442\u0430. \u0417\u0430\u043C\u0435\u0440\u0435\u0442\u044C \u0434\u043E \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u0441\u043E\u0431\u044B\u0442\u0438\u044F \u043E\u0442\u0440\u044F\u0434\u0430: \u0440\u044B\u0432\u043E\u043A \u0441\u0434\u0435\u043B\u0430\u043D (\u043F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0438 \u0441\u043E\u0437\u0434\u0430\u043D\u044B / \u0434\u043E\u043A\u043B\u0430\u0434 \u0434\u0430\u043D / \u043E\u0442\u0432\u0435\u0442\u044B \u0440\u043E\u0437\u0434\u0430\u043D\u044B), \u0437\u0432\u0430\u0442\u044C \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u0447\u0435\u0433\u043E. \u041F\u0440\u043E\u0433\u043E\u043D \u2192 paused, \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u0441\u044F; \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442 \u043E\u0432\u0435\u0440\u043C\u0430\u0439\u043D\u0434 \u043F\u043E \u0441\u043E\u0431\u044B\u0442\u0438\u044E \u043E\u0442\u0440\u044F\u0434\u0430 \u0438\u043B\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u0432\u0440\u0443\u0447\u043D\u0443\u044E. \u041E\u0431\u044B\u0447\u043D\u044B\u0439 \u0434\u0440\u043E\u043D \u0441\u0434\u0430\u0451\u0442 \u0440\u0430\u0431\u043E\u0442\u0443 \u0447\u0435\u0440\u0435\u0437 done, \u043D\u0435 rest.",
|
|
@@ -9962,9 +10160,7 @@ ${notFoundText}` : notFoundText }],
|
|
|
9962
10160
|
isError: true
|
|
9963
10161
|
};
|
|
9964
10162
|
}
|
|
9965
|
-
|
|
9966
|
-
const iAmReviewer = run?.reviews_run_id != null || run?.kind === "reviewer" || reviewerBySpawn;
|
|
9967
|
-
if (iAmReviewer && run?.reviews_run_id == null) {
|
|
10163
|
+
if (iAmReviewer(run) && run?.reviews_run_id == null) {
|
|
9968
10164
|
return {
|
|
9969
10165
|
content: [
|
|
9970
10166
|
{
|
|
@@ -10628,11 +10824,26 @@ function buildAnomalyCandidates(db, now, thresholds, opts) {
|
|
|
10628
10824
|
group: PR_GROUP,
|
|
10629
10825
|
build: () => ({
|
|
10630
10826
|
runId: run.id,
|
|
10631
|
-
//
|
|
10632
|
-
//
|
|
10827
|
+
// В ЛЕНТУ, не пушем (TASWM-417). Раньше тут стоял "decision" с обоснованием «рой
|
|
10828
|
+
// этого сам не расшивает» — оно перестало быть правдой ровно на том же пороге:
|
|
10829
|
+
// wakeAuthorOnChanges поднимает автора (`--continue`), а нечем продолжать —
|
|
10830
|
+
// свежего дрона на ту же ветку и тот же PR. Значит в момент, когда это правило
|
|
10831
|
+
// созревает, рой УЖЕ действует, и пуш звал бы человека к работе, которую делают
|
|
10832
|
+
// без него — ровно тот шум, из-за которого настоящие сигналы тонут. Настоящий
|
|
10833
|
+
// «нужен человек» приходит отдельно и позже: рой исчерпал побудки (маркер
|
|
10834
|
+
// CHANGES_WAKE_ESCALATION_MARK, NEEDS_HUMAN_MARKS → buildEscalationCandidates)
|
|
10835
|
+
// или упёрся в потолок трат. Правило остаётся страховкой ВИДИМОСТИ: если подъём
|
|
10836
|
+
// почему-то не случился вовсе, ленту это всё равно покажет.
|
|
10837
|
+
//
|
|
10838
|
+
// …и всё-таки ПУШ (TASWM-417, ревью диффа). Понизить его было соблазнительно, но
|
|
10839
|
+
// подъём гарантирован не во всех исходах: рой не лезет на ветку, где после вердикта
|
|
10840
|
+
// появились чужие коммиты, и молча ждёт, пока проект/ресурс-гейт откроется. В этих
|
|
10841
|
+
// случаях лента — это снова «никто не заметит», ровно инцидент 13.08. Пусть лучше
|
|
10842
|
+
// один лишний пуш на PR, который рой в итоге расшил сам, чем тишина на том, который
|
|
10843
|
+
// не расшил: цена ошибки несимметрична.
|
|
10633
10844
|
level: "decision",
|
|
10634
10845
|
title: runTitle(run, "PR \u0437\u0430\u0441\u0442\u0440\u044F\u043B \u043D\u0430 \xAB\u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438\xBB"),
|
|
10635
|
-
body: `${droneOf(run)}: \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \xAB\u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438\xBB \u0434\u0435\u0440\u0436\u0438\u0442\u0441\u044F \u2265${Math.round(thresholds.prNoReviewerMs / 6e4)} \u043C\u0438\u043D
|
|
10846
|
+
body: `${droneOf(run)}: \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \xAB\u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438\xBB \u0434\u0435\u0440\u0436\u0438\u0442\u0441\u044F \u2265${Math.round(thresholds.prNoReviewerMs / 6e4)} \u043C\u0438\u043D \u0431\u0435\u0437 \u0440\u0435-\u0440\u0435\u0432\u044C\u044E\u0435\u0440\u0430 \u2014 \u0440\u043E\u0439 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u0435\u0442 \u0430\u0432\u0442\u043E\u0440\u0430 \u043D\u0430 \u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044E \u0441\u0430\u043C; \u043D\u0435 \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442\u0441\u044F \u0437\u0430 \u043E\u0442\u0432\u0435\u0434\u0451\u043D\u043D\u044B\u0435 \u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u2014 \u043F\u043E\u0437\u043E\u0432\u0451\u0442 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E \u2014 ${run.pr_url}.`,
|
|
10636
10847
|
...hrefOf(run.task_url)
|
|
10637
10848
|
})
|
|
10638
10849
|
});
|
|
@@ -11454,7 +11665,7 @@ async function changedPaths(worktree, opts = {}) {
|
|
|
11454
11665
|
// package.json
|
|
11455
11666
|
var package_default = {
|
|
11456
11667
|
name: "@taskless-app/swarm",
|
|
11457
|
-
version: "0.6.
|
|
11668
|
+
version: "0.6.10",
|
|
11458
11669
|
type: "module",
|
|
11459
11670
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
11460
11671
|
keywords: [
|
|
@@ -11504,7 +11715,7 @@ var package_default = {
|
|
|
11504
11715
|
execa: "^10.0.0",
|
|
11505
11716
|
zod: "3.25.76"
|
|
11506
11717
|
},
|
|
11507
|
-
gitHead: "
|
|
11718
|
+
gitHead: "06e2edbcc9a21f0a1ef402868ea37b2b7d591020"
|
|
11508
11719
|
};
|
|
11509
11720
|
|
|
11510
11721
|
// src/version.ts
|
|
@@ -13989,9 +14200,11 @@ var CI_POLL_WINDOW_MS = Math.max(
|
|
|
13989
14200
|
);
|
|
13990
14201
|
var CI_WAIT_ESCALATION_MARK = "\u043E\u0436\u0438\u0434\u0430\u043D\u0438\u0435 CI \u0437\u0430\u0442\u044F\u043D\u0443\u043B\u043E\u0441\u044C";
|
|
13991
14202
|
var CHANGES_WAKE_COOLDOWN_MS = Number(process.env.SWARM_CHANGES_WAKE_COOLDOWN_MS) || 3 * 60 * 1e3;
|
|
13992
|
-
var CHANGES_WAKE_LIMIT = Number(process.env.SWARM_CHANGES_WAKE_LIMIT) ||
|
|
14203
|
+
var CHANGES_WAKE_LIMIT = Number(process.env.SWARM_CHANGES_WAKE_LIMIT) || 2;
|
|
14204
|
+
var CHANGES_FRESH_WAKE_AFTER_MS = Number(process.env.SWARM_CHANGES_FRESH_WAKE_AFTER_MS) || SWARM_ANOMALY_PR_NO_REVIEWER_MS;
|
|
13993
14205
|
var CHANGES_WAKE_MARK = "\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044F \u043F\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0443 \xAB\u043D\u0443\u0436\u043D\u044B \u043F\u0440\u0430\u0432\u043A\u0438\xBB";
|
|
13994
14206
|
var CHANGES_WAKE_ESCALATION_MARK = "\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044F \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u043B\u0430\u0441\u044C";
|
|
14207
|
+
var CHANGES_WAKE_FAILED_MARK = "\u043F\u043E\u0431\u0443\u0434\u043A\u0430 \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C";
|
|
13995
14208
|
var BUDGET_STOP_REASON = "\u043D\u0435 \u0443\u043B\u043E\u0436\u0438\u043B\u0441\u044F \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442: \u0441\u0435\u0441\u0441\u0438\u044E \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u043B \u0431\u044E\u0434\u0436\u0435\u0442 \u043F\u0440\u043E\u0433\u043E\u043D\u0430";
|
|
13996
14209
|
var NO_AUTO_WAKE_REASONS = [EXITED_NO_DONE_REASON, BUDGET_STOP_REASON];
|
|
13997
14210
|
var REVIEW_BUDGET_MARK = "\u0440\u0435\u0432\u044C\u044E \u043D\u0435 \u0443\u043B\u043E\u0436\u0438\u043B\u043E\u0441\u044C \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442";
|
|
@@ -14385,7 +14598,12 @@ ${task.description}` : `\u0417\u0430\u0434\u0430\u0447\u0430 ${run.task_slug} (\
|
|
|
14385
14598
|
// вторую ветку мимо PR. Пишем ту, что реально записана на прогоне; пусто (первый спавн —
|
|
14386
14599
|
// ветку заводит spawnDrone из слага) — прежняя формулировка.
|
|
14387
14600
|
`\u0422\u044B \u2014 \u0434\u0440\u043E\u043D \u0440\u043E\u044F. \u0420\u0430\u0431\u043E\u0442\u0430\u0435\u0448\u044C \u0432 \u044D\u0442\u043E\u043C git-worktree, \u0432\u0435\u0442\u043A\u0430 \`${run.branch || run.task_slug}\`${run.branch && run.branch !== run.task_slug ? " (\u043E\u043D\u0430 \u041D\u0415 \u0440\u0430\u0432\u043D\u0430 \u0441\u043B\u0430\u0433\u0443 \u0437\u0430\u0434\u0430\u0447\u0438 \u2014 \u044D\u0442\u043E \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0435\u043D\u0438\u0435 \u0443\u0436\u0435 \u043E\u0442\u043A\u0440\u044B\u0442\u043E\u0433\u043E PR, \u043F\u0443\u0448 \u0442\u043E\u043B\u044C\u043A\u043E \u0432 \u043D\u0435\u0451)" : " \u0441\u043E\u0437\u0434\u0430\u043D\u0430 \u043F\u043E slug \u0437\u0430\u0434\u0430\u0447\u0438"}.`,
|
|
14388
|
-
"\u0418\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B overmind (MCP): report_step(text) \u2014 \u043B\u043E\u0433\u0438\u0440\u0443\u0439 \u0445\u043E\u0434 \u0440\u0430\u0431\u043E\u0442\u044B; read_task() \u2014 \u0441\u0432\u0435\u0436\u0438\u0439 \u0441\u043D\u0430\u043F\u0448\u043E\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 (\u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435+\u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0438 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438+\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u044B+\u0434\u043E\u043A\u0438+\u0438\u0441\u0445\u043E\u0434\u044B \u0441\u043E\u0441\u0435\u0434\u043D\u0438\u0445 \u0437\u0430\u0434\u0430\u0447 \u043F\u043E \u0437\u043E\u043D\u0435 \u043A\u043E\u0434\u0430) \u0431\u0435\u0437 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432, \u0435\u0441\u043B\u0438 \u0442\u0440\u0435\u0431\u043E\u0432\u0430\u043D\u0438\u044F \u043C\u043E\u0433\u043B\u0438 \u0434\u043E\u043F\u0438\u0441\u0430\u0442\u044C \u043F\u043E\u0441\u043B\u0435 \u0441\u0442\u0430\u0440\u0442\u0430; ask(question, options?) \u2014 \u0435\u0441\u043B\u0438 \u0443\u043F\u0451\u0440\u0441\u044F \u0432 \u0420\u0415\u0428\u0415\u041D\u0418\u0415, \u0441\u043F\u0440\u043E\u0441\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 (\u0434\u0430\u0439 2\u20134 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u0430 \u0432 options, \u0435\u0441\u043B\u0438 \u0440\u0430\u0437\u0432\u0438\u043B\u043A\u0430 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u0430\u044F) \u0438 \u0434\u043E\u0436\u0434\u0438\u0441\u044C \u043E\u0442\u0432\u0435\u0442\u0430 (\u041D\u0415 \u0443\u0433\u0430\u0434\u044B\u0432\u0430\u0439); report_acceptance_check(id, state, evidence) \u2014 \u0437\u0430\u043A\u0440\u043E\u0439 \u041A\u0410\u0416\u0414\u042B\u0419 \u043F\u0443\u043D\u043A\u0442 \u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0435\u0432 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 (id \u0431\u0435\u0440\u0451\u0442\u0441\u044F \u0438\u0437 \u0431\u043B\u043E\u043A\u0430 \xAB\u041A\u0440\u0438\u0442\u0435\u0440\u0438\u0438 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438\xBB, state = passed|failed|pending \u043F\u043E \u0444\u0430\u043A\u0442\u0443 \u043F\u0440\u043E\u0433\u043E\u043D\u0430, evidence \u2014 \u0432\u044B\u0432\u043E\u0434 \u043A\u043E\u043C\u0430\u043D\u0434\u044B/\u0441\u0441\u044B\u043B\u043A\u0430/\u0447\u0442\u043E \u043D\u0430\u0431\u043B\u044E\u0434\u0430\u043B); \u043F\u0440\u043E\u0439\u0434\u0438\u0441\u044C \u043F\u043E \u043D\u0438\u043C \u041F\u0415\u0420\u0415\u0414 done(), \u0438\u043D\u0430\u0447\u0435 \u0433\u0430\u043B\u043E\u0447\u043A\u0438 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442 \u0440\u0443\u043A\u0430\u043C\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440; done(pr_url) \u2014 \u043A\u043E\u0433\u0434\u0430 \u043E\u0442\u043A\u0440\u043E\u0435\u0448\u044C PR; note(text) \u2014 \u0437\u0430\u043C\u0435\u0442\u043A\u0430 \xAB\u0437\u0430\u043C\u0435\u0447\u0435\u043D\u043E \u043F\u043E \u043F\u0443\u0442\u0438\xBB \u0411\u0415\u0417 \u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u043A\u0438: \u043D\u0430\u0442\u043A\u043D\u0443\u043B\u0441\u044F \u043D\u0430 \u043F\u0440\u043E\u0431\u0435\u043B \u0432 \u0434\u0438\u0437\u0430\u0439\u043D-\u0441\u0438\u0441\u0442\u0435\u043C\u0435, \u043F\u0440\u043E\u0442\u0443\u0445\u0448\u0443\u044E \u0438\u043D\u0441\u0442\u0440\u0443\u043A\u0446\u0438\u044E, \u0431\u0430\u0433 \u0440\u044F\u0434\u043E\u043C \u0441\u043E \u0441\u0432\u043E\u0438\u043C \u0434\u0438\u0444\u0444\u043E\u043C \u2014 \u0441\u043A\u0430\u0436\u0438 \u0438 \u0440\u0430\u0431\u043E\u0442\u0430\u0439 \u0434\u0430\u043B\u044C\u0448\u0435, \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u0443\u0432\u0438\u0434\u0438\u0442 \u0438 \u0441\u0430\u043C \u0440\u0435\u0448\u0438\u0442, \u0437\u0430\u0432\u043E\u0434\u0438\u0442\u044C \u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 (\u043D\u0435 \u043F\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0439 \u0442\u0430\u043A\u043E\u0435 \u0432 \u0434\u043E\u0440\u043E\u0433\u043E\u0439 \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u044E\u0449\u0438\u0439 ask \u0438 \u043D\u0435 \u0445\u043E\u0440\u043E\u043D\u0438 \u0432 \u043E\u0442\u0447\u0451\u0442\u0435 \u043E \u0441\u0434\u0430\u0447\u0435).",
|
|
14601
|
+
"\u0418\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B overmind (MCP): report_step(text) \u2014 \u043B\u043E\u0433\u0438\u0440\u0443\u0439 \u0445\u043E\u0434 \u0440\u0430\u0431\u043E\u0442\u044B; read_task() \u2014 \u0441\u0432\u0435\u0436\u0438\u0439 \u0441\u043D\u0430\u043F\u0448\u043E\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 (\u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435+\u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0438 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438+\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u044B+\u0434\u043E\u043A\u0438+\u0438\u0441\u0445\u043E\u0434\u044B \u0441\u043E\u0441\u0435\u0434\u043D\u0438\u0445 \u0437\u0430\u0434\u0430\u0447 \u043F\u043E \u0437\u043E\u043D\u0435 \u043A\u043E\u0434\u0430) \u0431\u0435\u0437 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u043E\u0432, \u0435\u0441\u043B\u0438 \u0442\u0440\u0435\u0431\u043E\u0432\u0430\u043D\u0438\u044F \u043C\u043E\u0433\u043B\u0438 \u0434\u043E\u043F\u0438\u0441\u0430\u0442\u044C \u043F\u043E\u0441\u043B\u0435 \u0441\u0442\u0430\u0440\u0442\u0430; ask(question, options?) \u2014 \u0435\u0441\u043B\u0438 \u0443\u043F\u0451\u0440\u0441\u044F \u0432 \u0420\u0415\u0428\u0415\u041D\u0418\u0415, \u0441\u043F\u0440\u043E\u0441\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 (\u0434\u0430\u0439 2\u20134 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u0430 \u0432 options, \u0435\u0441\u043B\u0438 \u0440\u0430\u0437\u0432\u0438\u043B\u043A\u0430 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u043D\u0430\u044F) \u0438 \u0434\u043E\u0436\u0434\u0438\u0441\u044C \u043E\u0442\u0432\u0435\u0442\u0430 (\u041D\u0415 \u0443\u0433\u0430\u0434\u044B\u0432\u0430\u0439); report_acceptance_check(id, state, evidence) \u2014 \u0437\u0430\u043A\u0440\u043E\u0439 \u041A\u0410\u0416\u0414\u042B\u0419 \u043F\u0443\u043D\u043A\u0442 \u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0435\u0432 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 \u0437\u0430\u0434\u0430\u0447\u0438 (id \u0431\u0435\u0440\u0451\u0442\u0441\u044F \u0438\u0437 \u0431\u043B\u043E\u043A\u0430 \xAB\u041A\u0440\u0438\u0442\u0435\u0440\u0438\u0438 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438\xBB, state = passed|failed|pending \u043F\u043E \u0444\u0430\u043A\u0442\u0443 \u043F\u0440\u043E\u0433\u043E\u043D\u0430, evidence \u2014 \u0432\u044B\u0432\u043E\u0434 \u043A\u043E\u043C\u0430\u043D\u0434\u044B/\u0441\u0441\u044B\u043B\u043A\u0430/\u0447\u0442\u043E \u043D\u0430\u0431\u043B\u044E\u0434\u0430\u043B); \u043F\u0440\u043E\u0439\u0434\u0438\u0441\u044C \u043F\u043E \u043D\u0438\u043C \u041F\u0415\u0420\u0415\u0414 done(), \u0438\u043D\u0430\u0447\u0435 \u0433\u0430\u043B\u043E\u0447\u043A\u0438 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442 \u0440\u0443\u043A\u0430\u043C\u0438 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440; done(pr_url) \u2014 \u043A\u043E\u0433\u0434\u0430 \u043E\u0442\u043A\u0440\u043E\u0435\u0448\u044C PR; return_task(missing, readings, assumed, questions) \u2014 \u0432\u0435\u0440\u043D\u0443\u0442\u044C \u0421\u042B\u0420\u0423\u042E \u0437\u0430\u0434\u0430\u0447\u0443 \u0430\u0432\u0442\u043E\u0440\u0443 (\u0441\u043C. \u043F\u0440\u0430\u0432\u0438\u043B\u043E \u043D\u0438\u0436\u0435); note(text) \u2014 \u0437\u0430\u043C\u0435\u0442\u043A\u0430 \xAB\u0437\u0430\u043C\u0435\u0447\u0435\u043D\u043E \u043F\u043E \u043F\u0443\u0442\u0438\xBB \u0411\u0415\u0417 \u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u043A\u0438: \u043D\u0430\u0442\u043A\u043D\u0443\u043B\u0441\u044F \u043D\u0430 \u043F\u0440\u043E\u0431\u0435\u043B \u0432 \u0434\u0438\u0437\u0430\u0439\u043D-\u0441\u0438\u0441\u0442\u0435\u043C\u0435, \u043F\u0440\u043E\u0442\u0443\u0445\u0448\u0443\u044E \u0438\u043D\u0441\u0442\u0440\u0443\u043A\u0446\u0438\u044E, \u0431\u0430\u0433 \u0440\u044F\u0434\u043E\u043C \u0441\u043E \u0441\u0432\u043E\u0438\u043C \u0434\u0438\u0444\u0444\u043E\u043C \u2014 \u0441\u043A\u0430\u0436\u0438 \u0438 \u0440\u0430\u0431\u043E\u0442\u0430\u0439 \u0434\u0430\u043B\u044C\u0448\u0435, \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u0443\u0432\u0438\u0434\u0438\u0442 \u0438 \u0441\u0430\u043C \u0440\u0435\u0448\u0438\u0442, \u0437\u0430\u0432\u043E\u0434\u0438\u0442\u044C \u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 (\u043D\u0435 \u043F\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0439 \u0442\u0430\u043A\u043E\u0435 \u0432 \u0434\u043E\u0440\u043E\u0433\u043E\u0439 \u0431\u043B\u043E\u043A\u0438\u0440\u0443\u044E\u0449\u0438\u0439 ask \u0438 \u043D\u0435 \u0445\u043E\u0440\u043E\u043D\u0438 \u0432 \u043E\u0442\u0447\u0451\u0442\u0435 \u043E \u0441\u0434\u0430\u0447\u0435).",
|
|
14602
|
+
// TASWM-412: третий выход из непонятной задачи. Раньше их было два, и оба плохие —
|
|
14603
|
+
// угадать постановку (сделает не то) или ask (слот занят, а ответить может только автор
|
|
14604
|
+
// задачи, не оператор роя). Граница здесь ОБЯЗАТЕЛЬНА: дрон, легко возвращающий задачи,
|
|
14605
|
+
// становится отписочником, а это хуже, чем дрон, который иногда угадывает неверно.
|
|
14606
|
+
"\u0421\u042B\u0420\u0423\u042E \u0417\u0410\u0414\u0410\u0427\u0423 \u041D\u0415 \u0412\u042B\u0414\u0423\u041C\u042B\u0412\u0410\u0419 \u2014 \u0412\u0415\u0420\u041D\u0418 \u0410\u0412\u0422\u041E\u0420\u0423. \u041F\u0440\u043E\u0447\u0438\u0442\u0430\u0432 \u0437\u0430\u0434\u0430\u0447\u0443, \u043E\u0442\u0432\u0435\u0442\u044C \u0441\u0435\u0431\u0435 \u043D\u0430 \u0434\u0432\u0430 \u0432\u043E\u043F\u0440\u043E\u0441\u0430: \u043F\u043E\u043D\u044F\u0442\u043D\u043E \u043B\u0438, \u0427\u0422\u041E \u043D\u0443\u0436\u043D\u043E \u0441\u0434\u0435\u043B\u0430\u0442\u044C, \u0438 \u0435\u0441\u0442\u044C \u043B\u0438 \u0442\u043E, \u041F\u041E \u0427\u0415\u041C\u0423 \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442. \u041E\u0431\u0430 \xAB\u043D\u0435\u0442\xBB \u2192 return_task(missing, readings, assumed, questions): \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0443\u0435\u0434\u0443\u0442 \u043A\u043E\u043C\u043C\u0435\u043D\u0442\u043E\u043C \u0430\u0432\u0442\u043E\u0440\u0443, \u0437\u0430\u0434\u0430\u0447\u0430 \u0443\u0439\u0434\u0451\u0442 \u043D\u0430 \u043D\u0435\u0433\u043E \u0438 \u0432 \u0440\u0430\u0431\u043E\u0442\u0443, \u043F\u0440\u043E\u0433\u043E\u043D \u0437\u0430\u043A\u0440\u043E\u0435\u0442\u0441\u044F \u043A\u0430\u043A \xAB\u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u0430 \u0430\u0432\u0442\u043E\u0440\u0443\xBB (\u043D\u0435 \xAB\u043F\u0440\u043E\u0432\u0430\u043B\u0435\u043D\u0430\xBB), \u0441\u043B\u043E\u0442 \u043E\u0441\u0432\u043E\u0431\u043E\u0434\u0438\u0442\u0441\u044F. \u0412\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044C \u041C\u041E\u0416\u041D\u041E, \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u0437\u0430\u0434\u0430\u0447\u0443 \u043D\u0435\u043B\u044C\u0437\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u043D\u0438 \u0432 \u043E\u0434\u043D\u043E\u043C \u0440\u0430\u0437\u0443\u043C\u043D\u043E\u043C \u0442\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u0438 \u0438\u043B\u0438 \u043D\u0435\u0447\u0435\u043C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442. \u0422\u043E\u043B\u043A\u043E\u0432\u0430\u043D\u0438\u0439 \u0434\u0432\u0430 \u0438 \u043E\u0431\u0430 \u043E\u0441\u043C\u044B\u0441\u043B\u0435\u043D\u043D\u044B \u2014 \u0432\u044B\u0431\u0435\u0440\u0438 \u043E\u0447\u0435\u0432\u0438\u0434\u043D\u043E\u0435, \u0441\u0434\u0435\u043B\u0430\u0439 \u0438 \u043D\u0430\u0437\u043E\u0432\u0438 \u0440\u0430\u0437\u0432\u0438\u043B\u043A\u0443 \u0432 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0438 PR. \u0417\u0430\u0434\u0430\u0447\u0430 \u043F\u043E\u043D\u044F\u0442\u043D\u0430, \u043D\u043E \u0432 \u043E\u0434\u043D\u043E\u043C \u043F\u0443\u043D\u043A\u0442\u0435 \u043F\u0440\u043E\u0442\u0438\u0432\u043E\u0440\u0435\u0447\u0438\u0442 \u043F\u0440\u0430\u0432\u0438\u043B\u0443 \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u2014 \u044D\u0442\u043E ask, \u0430 \u043D\u0435 \u0432\u043E\u0437\u0432\u0440\u0430\u0442.",
|
|
14389
14607
|
"\u041F\u0440\u0430\u0432\u0438\u043B\u0430: \u0442\u0440\u043E\u0433\u0430\u0435\u0448\u044C UI \u2014 \u0421\u041D\u0410\u0427\u0410\u041B\u0410 \u043F\u043E\u0441\u043C\u043E\u0442\u0440\u0438, \u0435\u0441\u0442\u044C \u043B\u0438 \u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0441\u043A\u0438\u043B\u043B \u0441 UI-\u043F\u0440\u0430\u0432\u0438\u043B\u0430\u043C\u0438 \u043F\u0440\u043E\u0435\u043A\u0442\u0430, \u0438 \u0441\u043B\u0435\u0434\u0443\u0439 \u0435\u0433\u043E \u0434\u0438\u0437\u0430\u0439\u043D-\u0441\u0438\u0441\u0442\u0435\u043C\u0435 (\u043D\u0435\u0442 \u043D\u0443\u0436\u043D\u043E\u0433\u043E \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u0430 \u2014 \u0437\u0430\u0432\u0435\u0434\u0438 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u0443\u044E \u0437\u0430\u0434\u0430\u0447\u0443, \u043D\u0435 \u0445\u0430\u0440\u0434\u043A\u043E\u0434\u044C \u043E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u043E\u0435); \u043A\u043E\u043C\u043C\u0438\u0442\u044B \u0441\u043E slug \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0438; \u043F\u043E\u0441\u043B\u0435 UI-\u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439 \u043F\u0440\u0438\u043B\u043E\u0436\u0438 \u0441\u043A\u0440\u0438\u043D\u044B mobile+desktop \u041A \u0417\u0410\u0414\u0410\u0427\u0415 (\u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 screenshots \u0443 done); \u043E\u0442\u043A\u0440\u043E\u0439 PR \u0447\u0435\u0440\u0435\u0437 gh \u0438 \u0432\u044B\u0437\u043E\u0432\u0438 done(pr_url).",
|
|
14390
14608
|
// TASWM-377: у сессии есть ПОТОЛОК ТРАТ, и он режет её без предупреждения, прямо посреди
|
|
14391
14609
|
// работы. Дрон об этом знать обязан: цена сессии растёт от ЧИСЛА ХОДОВ (каждый ход заново
|
|
@@ -14528,14 +14746,20 @@ ${priorReview}` : "",
|
|
|
14528
14746
|
"- [ ] \u0415\u0441\u043B\u0438 \u043F\u0440\u0430\u0432\u0438\u0442\u0441\u044F router.tsx \u2014 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0430 per-page meta \u0434\u043B\u044F \u0430\u0433\u0435\u043D\u0442\u0441\u043A\u043E\u0433\u043E \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u0430 (\u0438\u043D\u0430\u0447\u0435 NL-\u043D\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044F \u043C\u043E\u043B\u0447\u0430 \u043B\u043E\u043C\u0430\u0435\u0442\u0441\u044F)."
|
|
14529
14747
|
].join("\n");
|
|
14530
14748
|
}
|
|
14531
|
-
|
|
14749
|
+
var VERDICT_EVENT_PREFIXES = ["\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0440\u0435\u0432\u044C\u044E \u043E\u0442", "\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0440\u0435\u0432\u044C\u044E \u043F\u043E\u0434\u0445\u0432\u0430\u0447\u0435\u043D \u0438\u0437 PR-\u043A\u043E\u043C\u043C\u0435\u043D\u0442\u0430"];
|
|
14750
|
+
function lastVerdictEvent(db, reviewRunId) {
|
|
14532
14751
|
const events = db.events(reviewRunId);
|
|
14533
14752
|
for (let i = events.length - 1; i >= 0; i--) {
|
|
14534
|
-
|
|
14535
|
-
if (text.startsWith("\u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0440\u0435\u0432\u044C\u044E \u043E\u0442")) return text;
|
|
14753
|
+
if (VERDICT_EVENT_PREFIXES.some((p) => (events[i].text ?? "").startsWith(p))) return events[i];
|
|
14536
14754
|
}
|
|
14537
14755
|
return null;
|
|
14538
14756
|
}
|
|
14757
|
+
function lastChangesSummary(db, reviewRunId) {
|
|
14758
|
+
return lastVerdictEvent(db, reviewRunId)?.text ?? null;
|
|
14759
|
+
}
|
|
14760
|
+
function changesVerdictAt(db, reviewRunId) {
|
|
14761
|
+
return lastVerdictEvent(db, reviewRunId)?.at ?? null;
|
|
14762
|
+
}
|
|
14539
14763
|
var REVIEW_SCOPE_BLOCK = [
|
|
14540
14764
|
"\u{1F4B8} \u041A\u041E\u041D\u0422\u0423\u0420 \u0420\u0415\u0412\u042C\u042E (\u0436\u0451\u0441\u0442\u043A\u043E). \u0422\u0432\u043E\u044F \u0441\u0435\u0441\u0441\u0438\u044F \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0435\u043D\u0430 \u0431\u044E\u0434\u0436\u0435\u0442\u043E\u043C \u043F\u0440\u043E\u0433\u043E\u043D\u0430, \u0438 \u043E\u043D \u0441\u0440\u0435\u0436\u0435\u0442 \u0435\u0451 \u0411\u0415\u0417 \u043F\u0440\u0435\u0434\u0443\u043F\u0440\u0435\u0436\u0434\u0435\u043D\u0438\u044F: \u0443\u043F\u0440\u0451\u0448\u044C\u0441\u044F \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0430 \u043D\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u043E\u0432\u0441\u0435, \u0430 PR \u0443\u0439\u0434\u0451\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443. \u041F\u043E\u044D\u0442\u043E\u043C\u0443:",
|
|
14541
14765
|
"\u2022 \u0427\u0438\u0442\u0430\u0439 \u0414\u0418\u0424\u0424 \u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u0442\u043E, \u0447\u0435\u0433\u043E \u043E\u043D \u043A\u0430\u0441\u0430\u0435\u0442\u0441\u044F: \u0441\u0430\u043C\u0438 \u0438\u0437\u043C\u0435\u043D\u0451\u043D\u043D\u044B\u0435 \u0444\u0430\u0439\u043B\u044B + \u043C\u0435\u0441\u0442\u0430, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0438\u043C\u0438 \u043B\u043E\u043C\u0430\u044E\u0442\u0441\u044F. \u0420\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \xAB\u0434\u043B\u044F \u043E\u0431\u0449\u0435\u0433\u043E \u043F\u043E\u043D\u0438\u043C\u0430\u043D\u0438\u044F\xBB \u043D\u0435 \u0441\u043A\u0430\u043D\u0438\u0440\u0443\u0439 \u2014 \u043D\u0430 \u044D\u0442\u043E \u0443\u0445\u043E\u0434\u0438\u0442 \u0432\u0435\u0441\u044C \u0431\u044E\u0434\u0436\u0435\u0442 \u0438 \u043D\u0435 \u043D\u0430\u0445\u043E\u0434\u0438\u0442\u0441\u044F \u043D\u0438\u0447\u0435\u0433\u043E.",
|
|
@@ -14963,29 +15187,70 @@ async function wakeAuthorOnChanges(deps, run, now, droneCap) {
|
|
|
14963
15187
|
if (countActiveDrones(deps.db) >= droneCap) return false;
|
|
14964
15188
|
if (!await projectQuotaGateAllows(deps, run)) return false;
|
|
14965
15189
|
if (run.review_cycle >= REVIEW_CYCLE_LIMIT) return false;
|
|
14966
|
-
const
|
|
14967
|
-
|
|
14968
|
-
|
|
14969
|
-
|
|
14970
|
-
|
|
15190
|
+
const prUrl = run.pr_url;
|
|
15191
|
+
const events = deps.db.events(run.id);
|
|
15192
|
+
const mark = `${CHANGES_WAKE_ESCALATION_MARK} (\u043A\u0440\u0443\u0433 ${run.review_cycle ?? 0})`;
|
|
15193
|
+
const escalate = (why, said) => {
|
|
15194
|
+
if (!events.some((e) => (e.text ?? "").includes(mark))) {
|
|
15195
|
+
deps.db.addEvent(run.id, "step", `${mark}: ${why} \u2014 \u043D\u0443\u0436\u0435\u043D \u0447\u0435\u043B\u043E\u0432\u0435\u043A`);
|
|
15196
|
+
narrate(deps, said);
|
|
14971
15197
|
}
|
|
14972
15198
|
return false;
|
|
15199
|
+
};
|
|
15200
|
+
const alreadyEscalated = events.some((e) => (e.text ?? "").includes(mark));
|
|
15201
|
+
if (run.stop_reason?.startsWith(BUDGET_STOP_REASON)) {
|
|
15202
|
+
return escalate(
|
|
15203
|
+
"\u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044E \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u0442\u044C: \u043F\u0440\u043E\u0448\u043B\u0443\u044E \u0441\u0435\u0441\u0441\u0438\u044E \u0441\u0440\u0435\u0437\u0430\u043B \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442 \u043F\u0440\u043E\u0433\u043E\u043D\u0430, \u043F\u043E\u0432\u0442\u043E\u0440 \u0443\u043F\u0440\u0451\u0442\u0441\u044F \u0432 \u0442\u043E\u0442 \u0436\u0435",
|
|
15204
|
+
`${droneLabel(run)} \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u0432\u0437\u044F\u0442\u044C\u0441\u044F \u0437\u0430 \u043F\u0440\u0430\u0432\u043A\u0438 \u2014 \u0443\u043F\u0451\u0440\u0441\u044F \u0432 \u043F\u043E\u0442\u043E\u043B\u043E\u043A \u0442\u0440\u0430\u0442 \u043F\u0440\u043E\u0433\u043E\u043D\u0430. \u041F\u043E\u0434\u043D\u0438\u043C\u0438 \u043B\u0438\u043C\u0438\u0442 \u0438\u043B\u0438 \u0434\u043E\u0434\u0435\u043B\u0430\u0439 \u0440\u0443\u043A\u0430\u043C\u0438.`
|
|
15205
|
+
);
|
|
15206
|
+
}
|
|
15207
|
+
if (alreadyEscalated) return false;
|
|
15208
|
+
const attempts = events.filter((e) => e.kind === "resumed" && (e.text ?? "").includes(CHANGES_WAKE_MARK));
|
|
15209
|
+
const lastAttemptMs = attempts.length ? sqliteMs(attempts[attempts.length - 1].at) : null;
|
|
15210
|
+
if (lastAttemptMs != null && now - lastAttemptMs < CHANGES_WAKE_COOLDOWN_MS) return false;
|
|
15211
|
+
const wakes = attempts.filter((e) => !(e.text ?? "").includes(CHANGES_WAKE_FAILED_MARK));
|
|
15212
|
+
if (wakes.length >= CHANGES_WAKE_LIMIT) {
|
|
15213
|
+
const streakStart = wakes[wakes.length - CHANGES_WAKE_LIMIT].at;
|
|
15214
|
+
const moved = deps.commitsSince ? await deps.commitsSince(prUrl, streakStart).catch(() => false) : false;
|
|
15215
|
+
if (!moved) {
|
|
15216
|
+
return escalate(
|
|
15217
|
+
`${CHANGES_WAKE_LIMIT} \u043F\u043E\u0431\u0443\u0434(\u043E\u043A) \u043F\u043E\u0434\u0440\u044F\u0434 \u043D\u0435 \u043F\u043E\u043B\u043E\u0436\u0438\u043B\u0438 \u0432 \u0432\u0435\u0442\u043A\u0443 \u043D\u0438 \u043E\u0434\u043D\u043E\u0433\u043E \u043A\u043E\u043C\u043C\u0438\u0442\u0430`,
|
|
15218
|
+
`${droneLabel(run)} \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u043B\u0441\u044F \u043D\u0430 \u043F\u0440\u0430\u0432\u043A\u0438 ${CHANGES_WAKE_LIMIT} \u0440\u0430\u0437(\u0430) \u0438 \u043D\u0435 \u0441\u0434\u0435\u043B\u0430\u043B \u043D\u0438 \u043A\u043E\u043C\u043C\u0438\u0442\u0430 \u2014 \u0434\u0430\u043B\u044C\u0448\u0435 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440.`
|
|
15219
|
+
);
|
|
15220
|
+
}
|
|
14973
15221
|
}
|
|
14974
|
-
const lastWakeMs = wakes.length ? Date.parse(wakes[wakes.length - 1].at.replace(" ", "T") + "Z") : null;
|
|
14975
|
-
if (lastWakeMs != null && now - lastWakeMs < CHANGES_WAKE_COOLDOWN_MS) return false;
|
|
14976
15222
|
const wt = run.worktree_path;
|
|
14977
|
-
|
|
15223
|
+
const mode = wt && worktreeAndTranscriptAlive(deps, wt) ? "continue" : "fresh";
|
|
15224
|
+
if (mode === "fresh" && !await freshChangesWakeAllowed(deps, run, prUrl, now)) return false;
|
|
15225
|
+
const nudge = buildChangesWakeNudge(prUrl, lastChangesSummary(deps.db, run.id));
|
|
14978
15226
|
try {
|
|
14979
|
-
await deps.respawn(run,
|
|
14980
|
-
deps.db.
|
|
14981
|
-
|
|
15227
|
+
await deps.respawn(run, mode, nudge);
|
|
15228
|
+
if (run.stop_reason != null) deps.db.updateRun(run.id, { stop_reason: null });
|
|
15229
|
+
const how = mode === "continue" ? "\u0431\u0443\u0436\u0443 \u0430\u0432\u0442\u043E\u0440\u0430 (--continue) \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u0442\u044C \u0437\u0430\u043C\u0435\u0447\u0430\u043D\u0438\u044F" : "\u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u043C (worktree/\u0441\u0435\u0441\u0441\u0438\u044F \u043F\u043E\u0442\u0435\u0440\u044F\u043D\u044B) \u2014 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E \u0441\u0432\u0435\u0436\u0435\u0433\u043E \u0434\u0440\u043E\u043D\u0430 \u043D\u0430 \u0442\u0443 \u0436\u0435 \u0432\u0435\u0442\u043A\u0443 \u0438 \u0442\u043E\u0442 \u0436\u0435 PR";
|
|
15230
|
+
deps.db.addEvent(run.id, "resumed", `${CHANGES_WAKE_MARK} \u2014 ${how}`);
|
|
15231
|
+
narrate(
|
|
15232
|
+
deps,
|
|
15233
|
+
mode === "continue" ? `\u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u043F\u043E\u043F\u0440\u043E\u0441\u0438\u043B \u043F\u0440\u0430\u0432\u043A\u0438 \u0443 ${droneLabel(run)} \u2014 \u0431\u0443\u0436\u0443 \u0430\u0432\u0442\u043E\u0440\u0430 \u043D\u0430 \u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044E.` : `\u0420\u0435\u0432\u044C\u044E\u0435\u0440 \u043F\u043E\u043F\u0440\u043E\u0441\u0438\u043B \u043F\u0440\u0430\u0432\u043A\u0438 \u0443 ${droneLabel(run)}, \u0430 \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E \u2014 \u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E \u0441\u0432\u0435\u0436\u0435\u0433\u043E \u0434\u0440\u043E\u043D\u0430 \u043D\u0430 \u0442\u0443 \u0436\u0435 \u0432\u0435\u0442\u043A\u0443.`
|
|
15234
|
+
);
|
|
14982
15235
|
return true;
|
|
14983
15236
|
} catch (e) {
|
|
14984
|
-
deps.db.addEvent(run.id, "resumed", `${CHANGES_WAKE_MARK}:
|
|
15237
|
+
deps.db.addEvent(run.id, "resumed", `${CHANGES_WAKE_MARK}: ${CHANGES_WAKE_FAILED_MARK} \u2014 ${String(e)}`);
|
|
14985
15238
|
narrate(deps, `\u043F\u043E\u0431\u0443\u0434\u043A\u0430 ${droneLabel(run)} \u043D\u0430 \u0444\u0438\u043A\u0441-\u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044E \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C.`);
|
|
14986
15239
|
return false;
|
|
14987
15240
|
}
|
|
14988
15241
|
}
|
|
15242
|
+
var freshWakeProbeAt = /* @__PURE__ */ new Map();
|
|
15243
|
+
async function freshChangesWakeAllowed(deps, run, prUrl, now) {
|
|
15244
|
+
const verdictAt = changesVerdictAt(deps.db, run.id);
|
|
15245
|
+
const verdictMs = verdictAt ? sqliteMs(verdictAt) : NaN;
|
|
15246
|
+
if (Number.isFinite(verdictMs) && now - verdictMs < CHANGES_FRESH_WAKE_AFTER_MS) return false;
|
|
15247
|
+
if (!deps.commitsSince) return true;
|
|
15248
|
+
const probedAt = freshWakeProbeAt.get(run.id);
|
|
15249
|
+
if (probedAt != null && now - probedAt < CHANGES_WAKE_COOLDOWN_MS) return false;
|
|
15250
|
+
freshWakeProbeAt.set(run.id, now);
|
|
15251
|
+
const moved = await deps.commitsSince(prUrl, verdictAt ?? run.created_at).catch(() => true);
|
|
15252
|
+
return !moved;
|
|
15253
|
+
}
|
|
14989
15254
|
async function prodStillAllows(deps, run, allowed) {
|
|
14990
15255
|
if (!deps.db.refreshRun) return true;
|
|
14991
15256
|
let fresh;
|
|
@@ -16369,9 +16634,12 @@ async function spawnReviewer(deps, reviewRun, files) {
|
|
|
16369
16634
|
}
|
|
16370
16635
|
}
|
|
16371
16636
|
async function respawnRun(deps, run, mode, nudge) {
|
|
16372
|
-
const wt = run.worktree_path;
|
|
16373
|
-
if (!wt) throw new Error(`\u043D\u0435\u0442 worktree_path \u0434\u043B\u044F \u043E\u0436\u0438\u0432\u043B\u0435\u043D\u0438\u044F \u043F\u0440\u043E\u0433\u043E\u043D\u0430 #${run.id}`);
|
|
16374
16637
|
const repo = normalizeRepo(run.repo || deps.repoFallback);
|
|
16638
|
+
const wt = run.worktree_path || (mode === "fresh" ? droneWorktreePath(deps.worktreesDir, repo, run.task_slug) : null);
|
|
16639
|
+
if (!wt) throw new Error(`\u043D\u0435\u0442 worktree_path \u0434\u043B\u044F \u043E\u0436\u0438\u0432\u043B\u0435\u043D\u0438\u044F \u043F\u0440\u043E\u0433\u043E\u043D\u0430 #${run.id}`);
|
|
16640
|
+
if (!run.worktree_path && worktreePathHeldLive(deps.db, wt, run.id)) {
|
|
16641
|
+
throw new Error(`worktree ${wt} \u0434\u0435\u0440\u0436\u0438\u0442 \u0434\u0440\u0443\u0433\u043E\u0439 \u0436\u0438\u0432\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u2014 \u043E\u0436\u0438\u0432\u043B\u0435\u043D\u0438\u0435 #${run.id} \u043E\u0442\u043C\u0435\u043D\u0435\u043D\u043E (TASWM-417)`);
|
|
16642
|
+
}
|
|
16375
16643
|
if (mode === "continue") {
|
|
16376
16644
|
const prompt2 = nudge ?? (run.kind === "hero" ? HERO_CONTINUE_NUDGE : CONTINUE_NUDGE);
|
|
16377
16645
|
const pid2 = deps.launch({ worktree: wt, run, prompt: prompt2, continueSession: true });
|
|
@@ -16407,9 +16675,7 @@ async function respawnRun(deps, run, mode, nudge) {
|
|
|
16407
16675
|
});
|
|
16408
16676
|
const freshPrompt = runToSpawn.kind === "hero" ? buildHeroPrompt(task, runToSpawn, taskContext, attachments) : buildPrompt(task, runToSpawn, verifyMode, taskContext, attachments);
|
|
16409
16677
|
const notice = depsPromptNotice(depsReport);
|
|
16410
|
-
const prompt = notice
|
|
16411
|
-
|
|
16412
|
-
${notice}` : freshPrompt;
|
|
16678
|
+
const prompt = [freshPrompt, notice, nudge].filter(Boolean).join("\n\n");
|
|
16413
16679
|
const pid = deps.launch({ worktree: wt, run: runToSpawn, prompt });
|
|
16414
16680
|
deps.db.updateRun(run.id, { status: "running", pid });
|
|
16415
16681
|
}
|
|
@@ -17540,8 +17806,10 @@ if (isEntrypoint) {
|
|
|
17540
17806
|
export {
|
|
17541
17807
|
AWAITING_CI_REASON,
|
|
17542
17808
|
BUDGET_STOP_REASON,
|
|
17809
|
+
CHANGES_FRESH_WAKE_AFTER_MS,
|
|
17543
17810
|
CHANGES_WAKE_COOLDOWN_MS,
|
|
17544
17811
|
CHANGES_WAKE_ESCALATION_MARK,
|
|
17812
|
+
CHANGES_WAKE_FAILED_MARK,
|
|
17545
17813
|
CHANGES_WAKE_LIMIT,
|
|
17546
17814
|
CHANGES_WAKE_MARK,
|
|
17547
17815
|
CI_CHECK_COOLDOWN_MS,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taskless-app/swarm",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
6
6
|
"keywords": [
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"execa": "^10.0.0",
|
|
39
39
|
"zod": "3.25.76"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "06e2edbcc9a21f0a1ef402868ea37b2b7d591020",
|
|
42
42
|
"scripts": {
|
|
43
43
|
"overmind": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/overmind.ts",
|
|
44
44
|
"swarm": "NODE_OPTIONS=\"${NODE_OPTIONS:-} --conditions=development\" tsx src/cli.ts",
|