@gaberrb/polypus 0.4.5 → 0.4.7
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/index.js +324 -67
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -110,6 +110,7 @@ var en = {
|
|
|
110
110
|
"cli.opt.mode": "plan | review | bypass (overrides config)",
|
|
111
111
|
"cli.opt.maxSteps": "maximum agent steps",
|
|
112
112
|
"cli.opt.json": "headless mode: emit a single JSON object (steps, tool calls, files changed, usage) instead of the TUI \u2014 use with --mode bypass",
|
|
113
|
+
"cli.opt.verify": "after the agent finishes, run project checks (typecheck/build/test) and iterate until they pass",
|
|
113
114
|
"cli.arg.swarmTask": "high-level task to split across agents",
|
|
114
115
|
"cli.opt.agents": "comma-separated agent names (default: all configured)",
|
|
115
116
|
"cli.opt.maxSubtasks": "maximum number of parallel subtasks",
|
|
@@ -137,6 +138,15 @@ var en = {
|
|
|
137
138
|
"run.autocorrect": "\u21BB tool failed \u2014 auto-correcting with extra context",
|
|
138
139
|
"run.cancelled": "\u25A0 cancelled",
|
|
139
140
|
"run.jsonNeedsTask": "--json requires a task argument (headless mode has no interactive REPL).",
|
|
141
|
+
"review.approveAll": "approve all",
|
|
142
|
+
"review.reject": "reject",
|
|
143
|
+
"review.pickHunks": "pick hunks\u2026",
|
|
144
|
+
"review.selectHunks": "Select the hunks to apply (space to toggle, enter to confirm)",
|
|
145
|
+
"verify.running": "verifying (running project checks)",
|
|
146
|
+
"verify.noChecks": "no verification checks detected (no package.json scripts) \u2014 skipping",
|
|
147
|
+
"verify.passed": "verification passed",
|
|
148
|
+
"verify.failed": "{n} check(s) failed \u2014 handing the output back to the agent (attempt {attempt})",
|
|
149
|
+
"verify.giveUp": "{n} check(s) still failing after the retry budget \u2014 stopping",
|
|
140
150
|
// repl
|
|
141
151
|
"repl.welcome": "Polypus interactive session.",
|
|
142
152
|
"repl.welcomeHint": " Type /help for commands, /exit to quit.",
|
|
@@ -340,6 +350,7 @@ var ptBR = {
|
|
|
340
350
|
"cli.opt.mode": "plan | review | bypass (sobrescreve a config)",
|
|
341
351
|
"cli.opt.maxSteps": "n\xFAmero m\xE1ximo de passos do agente",
|
|
342
352
|
"cli.opt.json": "modo headless: emite um \xFAnico objeto JSON (passos, tool calls, arquivos alterados, uso) em vez da TUI \u2014 use com --mode bypass",
|
|
353
|
+
"cli.opt.verify": "ap\xF3s o agente terminar, roda as checagens do projeto (typecheck/build/test) e itera at\xE9 passar",
|
|
343
354
|
"cli.arg.swarmTask": "tarefa de alto n\xEDvel para dividir entre os agentes",
|
|
344
355
|
"cli.opt.agents": "nomes de agentes separados por v\xEDrgula (padr\xE3o: todos)",
|
|
345
356
|
"cli.opt.maxSubtasks": "n\xFAmero m\xE1ximo de subtarefas paralelas",
|
|
@@ -365,6 +376,15 @@ var ptBR = {
|
|
|
365
376
|
"run.autocorrect": "\u21BB tool falhou \u2014 autocorrigindo com contexto extra",
|
|
366
377
|
"run.cancelled": "\u25A0 cancelado",
|
|
367
378
|
"run.jsonNeedsTask": "--json exige um argumento de tarefa (o modo headless n\xE3o tem REPL interativo).",
|
|
379
|
+
"review.approveAll": "aprovar tudo",
|
|
380
|
+
"review.reject": "rejeitar",
|
|
381
|
+
"review.pickHunks": "escolher hunks\u2026",
|
|
382
|
+
"review.selectHunks": "Selecione os hunks a aplicar (espa\xE7o alterna, enter confirma)",
|
|
383
|
+
"verify.running": "verificando (rodando as checagens do projeto)",
|
|
384
|
+
"verify.noChecks": "nenhuma checagem detectada (sem scripts no package.json) \u2014 pulando",
|
|
385
|
+
"verify.passed": "verifica\xE7\xE3o passou",
|
|
386
|
+
"verify.failed": "{n} checagem(ns) falharam \u2014 devolvendo a sa\xEDda ao agente (tentativa {attempt})",
|
|
387
|
+
"verify.giveUp": "{n} checagem(ns) ainda falhando ap\xF3s o limite de tentativas \u2014 parando",
|
|
368
388
|
"repl.welcome": "Sess\xE3o interativa do Polypus.",
|
|
369
389
|
"repl.welcomeHint": " Digite /help para comandos, /exit para sair.",
|
|
370
390
|
"repl.modeChanged": "modo \u2192 {mode}",
|
|
@@ -963,6 +983,10 @@ function createProvider(agent) {
|
|
|
963
983
|
return { config: agent, provider, toolMode: resolveToolMode(agent) };
|
|
964
984
|
}
|
|
965
985
|
|
|
986
|
+
// src/core/permissions/modes.ts
|
|
987
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
988
|
+
import { resolve as resolve2 } from "path";
|
|
989
|
+
|
|
966
990
|
// src/core/permissions/allowlist.ts
|
|
967
991
|
import { isAbsolute, relative, resolve, sep } from "path";
|
|
968
992
|
function globToRegExp(glob) {
|
|
@@ -1058,6 +1082,111 @@ function scanSecrets(text2) {
|
|
|
1058
1082
|
return findings;
|
|
1059
1083
|
}
|
|
1060
1084
|
|
|
1085
|
+
// src/core/permissions/diff.ts
|
|
1086
|
+
var CONTEXT = 3;
|
|
1087
|
+
function splitLines(text2) {
|
|
1088
|
+
return text2 === "" ? [] : text2.split("\n");
|
|
1089
|
+
}
|
|
1090
|
+
function lcsOps(a, b) {
|
|
1091
|
+
const n = a.length;
|
|
1092
|
+
const m = b.length;
|
|
1093
|
+
const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
|
1094
|
+
for (let i2 = n - 1; i2 >= 0; i2--) {
|
|
1095
|
+
for (let j2 = m - 1; j2 >= 0; j2--) {
|
|
1096
|
+
dp[i2][j2] = a[i2] === b[j2] ? dp[i2 + 1][j2 + 1] + 1 : Math.max(dp[i2 + 1][j2], dp[i2][j2 + 1]);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
const out = [];
|
|
1100
|
+
let i = 0;
|
|
1101
|
+
let j = 0;
|
|
1102
|
+
while (i < n && j < m) {
|
|
1103
|
+
if (a[i] === b[j]) {
|
|
1104
|
+
out.push({ type: " ", text: a[i] });
|
|
1105
|
+
i++;
|
|
1106
|
+
j++;
|
|
1107
|
+
} else if (dp[i + 1][j] >= dp[i][j + 1]) {
|
|
1108
|
+
out.push({ type: "-", text: a[i] });
|
|
1109
|
+
i++;
|
|
1110
|
+
} else {
|
|
1111
|
+
out.push({ type: "+", text: b[j] });
|
|
1112
|
+
j++;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
while (i < n) out.push({ type: "-", text: a[i++] });
|
|
1116
|
+
while (j < m) out.push({ type: "+", text: b[j++] });
|
|
1117
|
+
return out;
|
|
1118
|
+
}
|
|
1119
|
+
function computeHunks(oldText, newText) {
|
|
1120
|
+
const a = splitLines(oldText);
|
|
1121
|
+
const b = splitLines(newText);
|
|
1122
|
+
const ops = lcsOps(a, b);
|
|
1123
|
+
const isChange = ops.map((o) => o.type !== " ");
|
|
1124
|
+
const keep = new Array(ops.length).fill(false);
|
|
1125
|
+
for (let k2 = 0; k2 < ops.length; k2++) {
|
|
1126
|
+
if (isChange[k2]) {
|
|
1127
|
+
for (let c = Math.max(0, k2 - CONTEXT); c <= Math.min(ops.length - 1, k2 + CONTEXT); c++) {
|
|
1128
|
+
keep[c] = true;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
const hunks = [];
|
|
1133
|
+
let oldLine = 0;
|
|
1134
|
+
let newLine = 0;
|
|
1135
|
+
let k = 0;
|
|
1136
|
+
while (k < ops.length) {
|
|
1137
|
+
const op = ops[k];
|
|
1138
|
+
if (!keep[k]) {
|
|
1139
|
+
if (op.type !== "+") oldLine++;
|
|
1140
|
+
if (op.type !== "-") newLine++;
|
|
1141
|
+
k++;
|
|
1142
|
+
continue;
|
|
1143
|
+
}
|
|
1144
|
+
const oldStart = oldLine;
|
|
1145
|
+
const newStart = newLine;
|
|
1146
|
+
const lines = [];
|
|
1147
|
+
let oldCount = 0;
|
|
1148
|
+
let newCount = 0;
|
|
1149
|
+
while (k < ops.length && keep[k]) {
|
|
1150
|
+
const cur = ops[k];
|
|
1151
|
+
lines.push(cur);
|
|
1152
|
+
if (cur.type !== "+") {
|
|
1153
|
+
oldLine++;
|
|
1154
|
+
oldCount++;
|
|
1155
|
+
}
|
|
1156
|
+
if (cur.type !== "-") {
|
|
1157
|
+
newLine++;
|
|
1158
|
+
newCount++;
|
|
1159
|
+
}
|
|
1160
|
+
k++;
|
|
1161
|
+
}
|
|
1162
|
+
hunks.push({ oldStart, oldCount, newStart, newCount, lines });
|
|
1163
|
+
}
|
|
1164
|
+
return hunks;
|
|
1165
|
+
}
|
|
1166
|
+
function applyHunks(oldText, hunks, approved) {
|
|
1167
|
+
const a = splitLines(oldText);
|
|
1168
|
+
const out = [];
|
|
1169
|
+
let oldIdx = 0;
|
|
1170
|
+
hunks.forEach((hunk, idx) => {
|
|
1171
|
+
while (oldIdx < hunk.oldStart) out.push(a[oldIdx++]);
|
|
1172
|
+
if (approved.has(idx)) {
|
|
1173
|
+
for (const l of hunk.lines) if (l.type !== "-") out.push(l.text);
|
|
1174
|
+
} else {
|
|
1175
|
+
for (const l of hunk.lines) if (l.type !== "+") out.push(l.text);
|
|
1176
|
+
}
|
|
1177
|
+
oldIdx = hunk.oldStart + hunk.oldCount;
|
|
1178
|
+
});
|
|
1179
|
+
while (oldIdx < a.length) out.push(a[oldIdx++]);
|
|
1180
|
+
return out.join("\n");
|
|
1181
|
+
}
|
|
1182
|
+
function hunkLabel(hunk) {
|
|
1183
|
+
const added = hunk.lines.filter((l) => l.type === "+").length;
|
|
1184
|
+
const removed = hunk.lines.filter((l) => l.type === "-").length;
|
|
1185
|
+
const firstChange = hunk.lines.find((l) => l.type !== " ");
|
|
1186
|
+
const preview = firstChange ? firstChange.text.trim().slice(0, 50) : "";
|
|
1187
|
+
return `@@ -${hunk.oldStart + 1},${hunk.oldCount} +${hunk.newStart + 1},${hunk.newCount} @@ (+${added}/-${removed}) ${preview}`;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1061
1190
|
// src/core/permissions/modes.ts
|
|
1062
1191
|
var PermissionEngine = class {
|
|
1063
1192
|
constructor(opts) {
|
|
@@ -1075,7 +1204,14 @@ var PermissionEngine = class {
|
|
|
1075
1204
|
async authorizeWrite(target, preview, content) {
|
|
1076
1205
|
const d = checkPath(this.opts.policy, target);
|
|
1077
1206
|
if (!d.allowed) return { allowed: false, reason: d.reason };
|
|
1078
|
-
|
|
1207
|
+
let oldContent = "";
|
|
1208
|
+
try {
|
|
1209
|
+
oldContent = await readFile2(resolve2(this.opts.policy.workspace, target), "utf8");
|
|
1210
|
+
} catch {
|
|
1211
|
+
}
|
|
1212
|
+
const hunks = content !== void 0 ? computeHunks(oldContent, content) : [];
|
|
1213
|
+
const added = hunks.flatMap((h) => h.lines.filter((l) => l.type === "+").map((l) => l.text)).join("\n");
|
|
1214
|
+
const findings = scanSecrets(hunks.length > 0 ? added : content ?? preview ?? "");
|
|
1079
1215
|
if (findings.length > 0) {
|
|
1080
1216
|
const first = findings[0];
|
|
1081
1217
|
return {
|
|
@@ -1087,8 +1223,13 @@ var PermissionEngine = class {
|
|
|
1087
1223
|
return { allowed: false, reason: "plan mode: file modifications are disabled" };
|
|
1088
1224
|
}
|
|
1089
1225
|
if (this.opts.mode === "bypass") return { allowed: true };
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1226
|
+
const res = await this.ask({ kind: "write", summary: `write ${d.rel}`, preview, hunks });
|
|
1227
|
+
if (res === true) return { allowed: true };
|
|
1228
|
+
if (res === false) return { allowed: false, reason: "rejected by user" };
|
|
1229
|
+
const approved = new Set(res);
|
|
1230
|
+
if (approved.size === 0) return { allowed: false, reason: "rejected by user" };
|
|
1231
|
+
if (approved.size === hunks.length) return { allowed: true };
|
|
1232
|
+
return { allowed: true, content: applyHunks(oldContent, hunks, approved) };
|
|
1092
1233
|
}
|
|
1093
1234
|
async authorizeCommand(command) {
|
|
1094
1235
|
const screen = screenCommand(command);
|
|
@@ -1100,8 +1241,8 @@ var PermissionEngine = class {
|
|
|
1100
1241
|
}
|
|
1101
1242
|
if (this.opts.mode === "bypass") return { allowed: true };
|
|
1102
1243
|
if (isCommandPreApproved(this.opts.allowedCommands, command)) return { allowed: true };
|
|
1103
|
-
const
|
|
1104
|
-
return
|
|
1244
|
+
const res = await this.ask({ kind: "command", summary: `run: ${command}` });
|
|
1245
|
+
return res === true ? { allowed: true } : { allowed: false, reason: "rejected by user" };
|
|
1105
1246
|
}
|
|
1106
1247
|
async ask(req) {
|
|
1107
1248
|
if (!this.opts.confirm) return false;
|
|
@@ -1330,8 +1471,8 @@ function makeDriver(kind, tools) {
|
|
|
1330
1471
|
}
|
|
1331
1472
|
|
|
1332
1473
|
// src/core/tools/edit-file.ts
|
|
1333
|
-
import { readFile as
|
|
1334
|
-
import { resolve as
|
|
1474
|
+
import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
1475
|
+
import { resolve as resolve3 } from "path";
|
|
1335
1476
|
import { z as z2 } from "zod";
|
|
1336
1477
|
var Args = z2.object({
|
|
1337
1478
|
path: z2.string().min(1),
|
|
@@ -1361,10 +1502,10 @@ var editFileTool = {
|
|
|
1361
1502
|
output: "edit_file needs three arguments: 'path', 'search' (exact text to find), and 'replace'. Resend the tool call with all three filled in."
|
|
1362
1503
|
};
|
|
1363
1504
|
}
|
|
1364
|
-
const abs =
|
|
1505
|
+
const abs = resolve3(ctx.workspace, args.data.path);
|
|
1365
1506
|
let content;
|
|
1366
1507
|
try {
|
|
1367
|
-
content = await
|
|
1508
|
+
content = await readFile3(abs, "utf8");
|
|
1368
1509
|
} catch (err) {
|
|
1369
1510
|
return { ok: false, output: `Could not read file to edit: ${err.message}` };
|
|
1370
1511
|
}
|
|
@@ -1383,11 +1524,11 @@ var editFileTool = {
|
|
|
1383
1524
|
args.data.path,
|
|
1384
1525
|
`- ${firstLine(args.data.search)}
|
|
1385
1526
|
+ ${firstLine(args.data.replace)}`,
|
|
1386
|
-
|
|
1527
|
+
updated
|
|
1387
1528
|
);
|
|
1388
1529
|
if (!decision.allowed) return { ok: false, output: `Edit denied: ${decision.reason}` };
|
|
1389
1530
|
try {
|
|
1390
|
-
await writeFile2(abs, updated, "utf8");
|
|
1531
|
+
await writeFile2(abs, decision.content ?? updated, "utf8");
|
|
1391
1532
|
return { ok: true, output: `Edited ${args.data.path}.` };
|
|
1392
1533
|
} catch (err) {
|
|
1393
1534
|
return { ok: false, output: `Could not write edit: ${err.message}` };
|
|
@@ -1400,7 +1541,7 @@ function firstLine(s) {
|
|
|
1400
1541
|
|
|
1401
1542
|
// src/core/tools/list-dir.ts
|
|
1402
1543
|
import { readdir } from "fs/promises";
|
|
1403
|
-
import { resolve as
|
|
1544
|
+
import { resolve as resolve4 } from "path";
|
|
1404
1545
|
import { z as z3 } from "zod";
|
|
1405
1546
|
var Args2 = z3.object({ path: z3.string().default(".") });
|
|
1406
1547
|
var listDirTool = {
|
|
@@ -1421,7 +1562,7 @@ var listDirTool = {
|
|
|
1421
1562
|
return { ok: false, output: `List denied: ${decision.reason}` };
|
|
1422
1563
|
}
|
|
1423
1564
|
try {
|
|
1424
|
-
const entries = await readdir(
|
|
1565
|
+
const entries = await readdir(resolve4(ctx.workspace, path), { withFileTypes: true });
|
|
1425
1566
|
const lines = entries.map((e) => e.isDirectory() ? `${e.name}/` : e.name).sort();
|
|
1426
1567
|
return { ok: true, output: lines.length ? lines.join("\n") : "(empty)" };
|
|
1427
1568
|
} catch (err) {
|
|
@@ -1431,8 +1572,8 @@ var listDirTool = {
|
|
|
1431
1572
|
};
|
|
1432
1573
|
|
|
1433
1574
|
// src/core/tools/read-file.ts
|
|
1434
|
-
import { readFile as
|
|
1435
|
-
import { resolve as
|
|
1575
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1576
|
+
import { resolve as resolve5 } from "path";
|
|
1436
1577
|
import { z as z4 } from "zod";
|
|
1437
1578
|
var Args3 = z4.object({ path: z4.string().min(1) });
|
|
1438
1579
|
var MAX_CHARS = 6e4;
|
|
@@ -1453,7 +1594,7 @@ var readFileTool = {
|
|
|
1453
1594
|
const decision = ctx.permissions.authorizeRead(args.data.path);
|
|
1454
1595
|
if (!decision.allowed) return { ok: false, output: `Read denied: ${decision.reason}` };
|
|
1455
1596
|
try {
|
|
1456
|
-
const content = await
|
|
1597
|
+
const content = await readFile4(resolve5(ctx.workspace, args.data.path), "utf8");
|
|
1457
1598
|
const truncated = content.length > MAX_CHARS;
|
|
1458
1599
|
return {
|
|
1459
1600
|
ok: true,
|
|
@@ -1514,8 +1655,8 @@ function clamp(s) {
|
|
|
1514
1655
|
}
|
|
1515
1656
|
|
|
1516
1657
|
// src/core/tools/search-file.ts
|
|
1517
|
-
import { readdir as readdir2, readFile as
|
|
1518
|
-
import { join as join2, resolve as
|
|
1658
|
+
import { readdir as readdir2, readFile as readFile5, stat } from "fs/promises";
|
|
1659
|
+
import { join as join2, resolve as resolve6 } from "path";
|
|
1519
1660
|
import { z as z6 } from "zod";
|
|
1520
1661
|
var Args5 = z6.object({
|
|
1521
1662
|
query: z6.string().min(1),
|
|
@@ -1567,7 +1708,7 @@ var searchTool = {
|
|
|
1567
1708
|
}
|
|
1568
1709
|
const globRe = glob ? globToRegExp(glob) : void 0;
|
|
1569
1710
|
const limit = max_results ?? DEFAULT_MAX_RESULTS;
|
|
1570
|
-
const root =
|
|
1711
|
+
const root = resolve6(ctx.workspace, path);
|
|
1571
1712
|
const matches = [];
|
|
1572
1713
|
let truncated = false;
|
|
1573
1714
|
const walk = async (dir) => {
|
|
@@ -1593,7 +1734,7 @@ var searchTool = {
|
|
|
1593
1734
|
try {
|
|
1594
1735
|
const info = await stat(abs);
|
|
1595
1736
|
if (info.size > MAX_FILE_BYTES) continue;
|
|
1596
|
-
const content = await
|
|
1737
|
+
const content = await readFile5(abs, "utf8");
|
|
1597
1738
|
if (content.includes(NUL)) continue;
|
|
1598
1739
|
const lines = content.split("\n");
|
|
1599
1740
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -1638,7 +1779,7 @@ var FINISH_TOOL = {
|
|
|
1638
1779
|
|
|
1639
1780
|
// src/core/tools/write-file.ts
|
|
1640
1781
|
import { mkdir as mkdir2, writeFile as writeFile3 } from "fs/promises";
|
|
1641
|
-
import { dirname, resolve as
|
|
1782
|
+
import { dirname, resolve as resolve7 } from "path";
|
|
1642
1783
|
import { z as z7 } from "zod";
|
|
1643
1784
|
var Args6 = z7.object({ path: z7.string().min(1), content: z7.string() });
|
|
1644
1785
|
var writeFileTool = {
|
|
@@ -1667,11 +1808,12 @@ var writeFileTool = {
|
|
|
1667
1808
|
const preview = previewContent(args.data.content);
|
|
1668
1809
|
const decision = await ctx.permissions.authorizeWrite(args.data.path, preview, args.data.content);
|
|
1669
1810
|
if (!decision.allowed) return { ok: false, output: `Write denied: ${decision.reason}` };
|
|
1811
|
+
const finalContent = decision.content ?? args.data.content;
|
|
1670
1812
|
try {
|
|
1671
|
-
const abs =
|
|
1813
|
+
const abs = resolve7(ctx.workspace, args.data.path);
|
|
1672
1814
|
await mkdir2(dirname(abs), { recursive: true });
|
|
1673
|
-
await writeFile3(abs,
|
|
1674
|
-
const lines =
|
|
1815
|
+
await writeFile3(abs, finalContent, "utf8");
|
|
1816
|
+
const lines = finalContent.split("\n").length;
|
|
1675
1817
|
return { ok: true, output: `Wrote ${args.data.path} (${lines} lines).` };
|
|
1676
1818
|
} catch (err) {
|
|
1677
1819
|
return { ok: false, output: `Could not write file: ${err.message}` };
|
|
@@ -1700,8 +1842,8 @@ function getTool(name) {
|
|
|
1700
1842
|
}
|
|
1701
1843
|
|
|
1702
1844
|
// src/core/agent/correction.ts
|
|
1703
|
-
import { readFile as
|
|
1704
|
-
import { dirname as dirname2, resolve as
|
|
1845
|
+
import { readFile as readFile6, readdir as readdir3 } from "fs/promises";
|
|
1846
|
+
import { dirname as dirname2, resolve as resolve8 } from "path";
|
|
1705
1847
|
function truncationGuidance(toolName) {
|
|
1706
1848
|
const fileHint = toolName === "write_file" || toolName === "edit_file" ? " Write large files in parts: create the file with the first chunk via write_file, then append the rest with edit_file in the next steps." : "";
|
|
1707
1849
|
return [
|
|
@@ -1801,7 +1943,7 @@ ${text2}` : null;
|
|
|
1801
1943
|
}
|
|
1802
1944
|
async function readWorkspaceFile(workspace, path) {
|
|
1803
1945
|
try {
|
|
1804
|
-
return await
|
|
1946
|
+
return await readFile6(resolve8(workspace, path), "utf8");
|
|
1805
1947
|
} catch {
|
|
1806
1948
|
return null;
|
|
1807
1949
|
}
|
|
@@ -1859,11 +2001,11 @@ async function occurrenceLines(workspace, path, search) {
|
|
|
1859
2001
|
return out;
|
|
1860
2002
|
}
|
|
1861
2003
|
async function listNearest(workspace, path) {
|
|
1862
|
-
let dir = dirname2(
|
|
2004
|
+
let dir = dirname2(resolve8(workspace, path));
|
|
1863
2005
|
for (let i = 0; i < 8; i++) {
|
|
1864
2006
|
try {
|
|
1865
2007
|
const entries = await readdir3(dir, { withFileTypes: true });
|
|
1866
|
-
const rel = dir ===
|
|
2008
|
+
const rel = dir === resolve8(workspace) ? "." : dir;
|
|
1867
2009
|
const names = entries.slice(0, 40).map((e) => e.isDirectory() ? `${e.name}/` : e.name);
|
|
1868
2010
|
return `${rel}:
|
|
1869
2011
|
${names.join(" ") || "(empty)"}`;
|
|
@@ -1885,14 +2027,14 @@ function formatSchema(spec) {
|
|
|
1885
2027
|
}
|
|
1886
2028
|
|
|
1887
2029
|
// src/core/agent/project-context.ts
|
|
1888
|
-
import { readFile as
|
|
2030
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
1889
2031
|
import { join as join3 } from "path";
|
|
1890
2032
|
var INSTRUCTION_FILES = [join3(".poly", "agents.md"), "AGENTS.md"];
|
|
1891
2033
|
var MAX_CHARS2 = 8e3;
|
|
1892
2034
|
async function loadProjectInstructions(workspace) {
|
|
1893
2035
|
for (const rel of INSTRUCTION_FILES) {
|
|
1894
2036
|
try {
|
|
1895
|
-
const raw = (await
|
|
2037
|
+
const raw = (await readFile7(join3(workspace, rel), "utf8")).trim();
|
|
1896
2038
|
if (!raw) continue;
|
|
1897
2039
|
return raw.length > MAX_CHARS2 ? raw.slice(0, MAX_CHARS2) + "\n\u2026(truncated)" : raw;
|
|
1898
2040
|
} catch {
|
|
@@ -2053,8 +2195,8 @@ ${guidance}`;
|
|
|
2053
2195
|
}
|
|
2054
2196
|
|
|
2055
2197
|
// src/core/context/mentions.ts
|
|
2056
|
-
import { readdir as readdir4, readFile as
|
|
2057
|
-
import { resolve as
|
|
2198
|
+
import { readdir as readdir4, readFile as readFile8, stat as stat2 } from "fs/promises";
|
|
2199
|
+
import { resolve as resolve9 } from "path";
|
|
2058
2200
|
var MAX_FILE_CHARS = 1e4;
|
|
2059
2201
|
var MENTION_RE = /(?:^|\s)@([\w./-]+)/g;
|
|
2060
2202
|
async function resolveMentions(task, policy) {
|
|
@@ -2070,7 +2212,7 @@ async function resolveMentions(task, policy) {
|
|
|
2070
2212
|
${t("mentions.notFound", { path: token })}`);
|
|
2071
2213
|
continue;
|
|
2072
2214
|
}
|
|
2073
|
-
const abs =
|
|
2215
|
+
const abs = resolve9(policy.workspace, token);
|
|
2074
2216
|
try {
|
|
2075
2217
|
const info = await stat2(abs);
|
|
2076
2218
|
if (info.isDirectory()) {
|
|
@@ -2080,7 +2222,7 @@ ${t("mentions.notFound", { path: token })}`);
|
|
|
2080
2222
|
${listing || "(empty)"}`);
|
|
2081
2223
|
injected.push(decision.rel);
|
|
2082
2224
|
} else {
|
|
2083
|
-
const raw = await
|
|
2225
|
+
const raw = await readFile8(abs, "utf8");
|
|
2084
2226
|
const content = raw.length > MAX_FILE_CHARS ? raw.slice(0, MAX_FILE_CHARS) + "\n\u2026[truncated]" : raw;
|
|
2085
2227
|
blocks.push(`## @${decision.rel}
|
|
2086
2228
|
\`\`\`
|
|
@@ -2102,6 +2244,58 @@ ${blocks.join("\n\n")}`;
|
|
|
2102
2244
|
return { task: augmented, injected };
|
|
2103
2245
|
}
|
|
2104
2246
|
|
|
2247
|
+
// src/core/agent/verify.ts
|
|
2248
|
+
import { exec as exec2 } from "child_process";
|
|
2249
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
2250
|
+
import { resolve as resolve10 } from "path";
|
|
2251
|
+
import { promisify as promisify2 } from "util";
|
|
2252
|
+
var execAsync2 = promisify2(exec2);
|
|
2253
|
+
var MAX_OUTPUT3 = 8e3;
|
|
2254
|
+
var CHECK_SCRIPTS = ["typecheck", "build", "test"];
|
|
2255
|
+
async function detectChecks(workspace) {
|
|
2256
|
+
try {
|
|
2257
|
+
const raw = await readFile9(resolve10(workspace, "package.json"), "utf8");
|
|
2258
|
+
const scripts = JSON.parse(raw).scripts ?? {};
|
|
2259
|
+
return CHECK_SCRIPTS.filter((s) => typeof scripts[s] === "string").map((s) => `npm run ${s}`);
|
|
2260
|
+
} catch {
|
|
2261
|
+
return [];
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
async function runChecks(workspace, commands) {
|
|
2265
|
+
const results = [];
|
|
2266
|
+
for (const command of commands) {
|
|
2267
|
+
try {
|
|
2268
|
+
const { stdout: stdout2, stderr } = await execAsync2(command, {
|
|
2269
|
+
cwd: workspace,
|
|
2270
|
+
timeout: 3e5,
|
|
2271
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
2272
|
+
windowsHide: true
|
|
2273
|
+
});
|
|
2274
|
+
results.push({ command, ok: true, output: clamp2(`${stdout2}${stderr ? `
|
|
2275
|
+
${stderr}` : ""}`.trim()) });
|
|
2276
|
+
} catch (err) {
|
|
2277
|
+
const e = err;
|
|
2278
|
+
const body = `${e.stdout ?? ""}${e.stderr ?? ""}`.trim() || e.message;
|
|
2279
|
+
results.push({ command, ok: false, output: clamp2(body) });
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
return results;
|
|
2283
|
+
}
|
|
2284
|
+
function buildVerifyFeedback(failed) {
|
|
2285
|
+
const blocks = failed.map((f) => `$ ${f.command}
|
|
2286
|
+
${f.output}`).join("\n\n");
|
|
2287
|
+
return [
|
|
2288
|
+
"Verification failed. The following checks did not pass:",
|
|
2289
|
+
"",
|
|
2290
|
+
blocks,
|
|
2291
|
+
"",
|
|
2292
|
+
"Fix the underlying problem in the code (do not disable or skip the checks), then call `finish`."
|
|
2293
|
+
].join("\n");
|
|
2294
|
+
}
|
|
2295
|
+
function clamp2(s) {
|
|
2296
|
+
return s.length > MAX_OUTPUT3 ? s.slice(-MAX_OUTPUT3) : s;
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2105
2299
|
// src/cli/commands/json-output.ts
|
|
2106
2300
|
var OUTPUT_PREVIEW = 500;
|
|
2107
2301
|
function createJsonCollector() {
|
|
@@ -2794,10 +2988,10 @@ async function readLineTTY(prompt) {
|
|
|
2794
2988
|
stdin.resume();
|
|
2795
2989
|
stdin.on("data", onData);
|
|
2796
2990
|
try {
|
|
2797
|
-
const line = await new Promise((
|
|
2798
|
-
rl.question(prompt).then(
|
|
2799
|
-
rl.on("SIGINT", () =>
|
|
2800
|
-
rl.on("close", () =>
|
|
2991
|
+
const line = await new Promise((resolve12) => {
|
|
2992
|
+
rl.question(prompt).then(resolve12, () => resolve12(null));
|
|
2993
|
+
rl.on("SIGINT", () => resolve12(null));
|
|
2994
|
+
rl.on("close", () => resolve12(null));
|
|
2801
2995
|
});
|
|
2802
2996
|
return line === null ? null : store.expand(line);
|
|
2803
2997
|
} finally {
|
|
@@ -3391,6 +3585,7 @@ var Spinner = class {
|
|
|
3391
3585
|
};
|
|
3392
3586
|
|
|
3393
3587
|
// src/cli/commands/run.ts
|
|
3588
|
+
var MAX_VERIFY_FIXES = 3;
|
|
3394
3589
|
async function run(task, opts) {
|
|
3395
3590
|
let config = await loadConfig();
|
|
3396
3591
|
const agentConfig = resolveAgent(config, opts.agent);
|
|
@@ -3425,7 +3620,7 @@ async function run(task, opts) {
|
|
|
3425
3620
|
)
|
|
3426
3621
|
);
|
|
3427
3622
|
}
|
|
3428
|
-
await executeTask(task, resolved2, workspace, session, opts.json ?? false);
|
|
3623
|
+
await executeTask(task, resolved2, workspace, session, opts.json ?? false, opts.verify ?? false);
|
|
3429
3624
|
return;
|
|
3430
3625
|
}
|
|
3431
3626
|
const resolved = createProvider(agentConfig);
|
|
@@ -3448,7 +3643,7 @@ async function run(task, opts) {
|
|
|
3448
3643
|
};
|
|
3449
3644
|
await startRepl(ctx);
|
|
3450
3645
|
}
|
|
3451
|
-
async function executeTask(task, resolved, workspace, session, json = false) {
|
|
3646
|
+
async function executeTask(task, resolved, workspace, session, json = false, verify = false) {
|
|
3452
3647
|
const mention = await resolveMentions(task, {
|
|
3453
3648
|
workspace,
|
|
3454
3649
|
allow: session.allow,
|
|
@@ -3475,25 +3670,29 @@ async function executeTask(task, resolved, workspace, session, json = false) {
|
|
|
3475
3670
|
return ok;
|
|
3476
3671
|
}
|
|
3477
3672
|
});
|
|
3673
|
+
const runOnce = (taskText) => runAgent({
|
|
3674
|
+
task: taskText,
|
|
3675
|
+
workspace,
|
|
3676
|
+
agent: resolved,
|
|
3677
|
+
permissions,
|
|
3678
|
+
promptContext: { workspace, mode: session.mode, allow: session.allow },
|
|
3679
|
+
history: session.history,
|
|
3680
|
+
maxSteps: session.maxSteps,
|
|
3681
|
+
signal: controller.signal,
|
|
3682
|
+
events: collector ? collector.events : renderEvents(spinner3)
|
|
3683
|
+
});
|
|
3478
3684
|
if (!json) spinner3.start(t("ui.thinking"));
|
|
3479
3685
|
let result;
|
|
3480
3686
|
try {
|
|
3481
|
-
result = await
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
promptContext: { workspace, mode: session.mode, allow: session.allow },
|
|
3487
|
-
history: session.history,
|
|
3488
|
-
maxSteps: session.maxSteps,
|
|
3489
|
-
signal: controller.signal,
|
|
3490
|
-
events: collector ? collector.events : renderEvents(spinner3)
|
|
3491
|
-
});
|
|
3687
|
+
result = await runOnce(task);
|
|
3688
|
+
session.history = result.messages;
|
|
3689
|
+
if (verify && result.reason === "finished" && !controller.signal.aborted) {
|
|
3690
|
+
result = await runVerification(runOnce, workspace, session, spinner3, json, controller.signal, result);
|
|
3691
|
+
}
|
|
3492
3692
|
} finally {
|
|
3493
3693
|
spinner3.stop();
|
|
3494
3694
|
cancel2.dispose();
|
|
3495
3695
|
}
|
|
3496
|
-
session.history = result.messages;
|
|
3497
3696
|
if (collector) {
|
|
3498
3697
|
process.stdout.write(JSON.stringify(collector.build(result)) + "\n");
|
|
3499
3698
|
return;
|
|
@@ -3549,11 +3748,69 @@ function listenForCancel(controller) {
|
|
|
3549
3748
|
return { pause: detach, resume: attach, dispose: detach };
|
|
3550
3749
|
}
|
|
3551
3750
|
async function confirmAction(req) {
|
|
3751
|
+
if (req.kind === "write" && req.hunks && req.hunks.length > 0) {
|
|
3752
|
+
renderDiff(req.hunks);
|
|
3753
|
+
const options = [
|
|
3754
|
+
{ value: "approve", label: t("review.approveAll") },
|
|
3755
|
+
{ value: "reject", label: t("review.reject") },
|
|
3756
|
+
...req.hunks.length > 1 ? [{ value: "hunks", label: t("review.pickHunks") }] : []
|
|
3757
|
+
];
|
|
3758
|
+
const choice = await p2.select({ message: t("run.confirm", { summary: req.summary }), options });
|
|
3759
|
+
if (p2.isCancel(choice) || choice === "reject") return false;
|
|
3760
|
+
if (choice === "approve") return true;
|
|
3761
|
+
const selected = await p2.multiselect({
|
|
3762
|
+
message: t("review.selectHunks"),
|
|
3763
|
+
options: req.hunks.map((h, i) => ({ value: i, label: hunkLabel(h) })),
|
|
3764
|
+
required: false
|
|
3765
|
+
});
|
|
3766
|
+
if (p2.isCancel(selected)) return false;
|
|
3767
|
+
return selected;
|
|
3768
|
+
}
|
|
3552
3769
|
if (req.preview) console.log(pc8.dim(req.preview));
|
|
3553
3770
|
const answer = await p2.confirm({ message: t("run.confirm", { summary: req.summary }) });
|
|
3554
3771
|
if (p2.isCancel(answer)) return false;
|
|
3555
3772
|
return answer === true;
|
|
3556
3773
|
}
|
|
3774
|
+
async function runVerification(runOnce, workspace, session, spinner3, json, signal, initial) {
|
|
3775
|
+
const checks = await detectChecks(workspace);
|
|
3776
|
+
if (checks.length === 0) {
|
|
3777
|
+
if (!json) console.log(pc8.dim(t("verify.noChecks")));
|
|
3778
|
+
return initial;
|
|
3779
|
+
}
|
|
3780
|
+
let result = initial;
|
|
3781
|
+
for (let fix = 0; ; fix++) {
|
|
3782
|
+
if (signal.aborted) return result;
|
|
3783
|
+
if (!json) spinner3.start(t("verify.running"));
|
|
3784
|
+
const results = await runChecks(workspace, checks);
|
|
3785
|
+
spinner3.stop();
|
|
3786
|
+
const failed = results.filter((r) => !r.ok);
|
|
3787
|
+
if (failed.length === 0) {
|
|
3788
|
+
if (!json) console.log(pc8.green("\u2713 " + t("verify.passed")));
|
|
3789
|
+
return result;
|
|
3790
|
+
}
|
|
3791
|
+
if (fix >= MAX_VERIFY_FIXES) {
|
|
3792
|
+
if (!json) console.log(pc8.yellow("\u26A0 " + t("verify.giveUp", { n: failed.length })));
|
|
3793
|
+
return result;
|
|
3794
|
+
}
|
|
3795
|
+
if (!json) {
|
|
3796
|
+
console.log(pc8.yellow("\u2717 " + t("verify.failed", { n: failed.length, attempt: fix + 1 })));
|
|
3797
|
+
}
|
|
3798
|
+
if (!json) spinner3.start(t("ui.thinking"));
|
|
3799
|
+
result = await runOnce(buildVerifyFeedback(failed));
|
|
3800
|
+
spinner3.stop();
|
|
3801
|
+
session.history = result.messages;
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
function renderDiff(hunks) {
|
|
3805
|
+
for (const h of hunks) {
|
|
3806
|
+
console.log(pc8.cyan(`@@ -${h.oldStart + 1},${h.oldCount} +${h.newStart + 1},${h.newCount} @@`));
|
|
3807
|
+
for (const l of h.lines) {
|
|
3808
|
+
if (l.type === "+") console.log(pc8.green(`+${l.text}`));
|
|
3809
|
+
else if (l.type === "-") console.log(pc8.red(`-${l.text}`));
|
|
3810
|
+
else console.log(pc8.dim(` ${l.text}`));
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3557
3814
|
function renderEvents(spinner3) {
|
|
3558
3815
|
return {
|
|
3559
3816
|
onStep() {
|
|
@@ -3942,9 +4199,9 @@ async function resolveOpenRouterKey() {
|
|
|
3942
4199
|
}
|
|
3943
4200
|
|
|
3944
4201
|
// src/cli/commands/prd.ts
|
|
3945
|
-
import { writeFile as writeFile5, readFile as
|
|
4202
|
+
import { writeFile as writeFile5, readFile as readFile10 } from "fs/promises";
|
|
3946
4203
|
import { execFile } from "child_process";
|
|
3947
|
-
import { promisify as
|
|
4204
|
+
import { promisify as promisify3 } from "util";
|
|
3948
4205
|
import pc11 from "picocolors";
|
|
3949
4206
|
|
|
3950
4207
|
// src/core/agent/prd.ts
|
|
@@ -4034,13 +4291,13 @@ async function withRetry(fn, opts = {}) {
|
|
|
4034
4291
|
|
|
4035
4292
|
// src/cli/commands/cli-io.ts
|
|
4036
4293
|
import { readFileSync, existsSync as existsSync2 } from "fs";
|
|
4037
|
-
import { resolve as
|
|
4294
|
+
import { resolve as resolve11 } from "path";
|
|
4038
4295
|
var GUIDE_MAX = 12e3;
|
|
4039
4296
|
function readProjectGuide(files) {
|
|
4040
4297
|
const parts = [];
|
|
4041
4298
|
for (const file of files) {
|
|
4042
4299
|
try {
|
|
4043
|
-
const path =
|
|
4300
|
+
const path = resolve11(process.cwd(), file);
|
|
4044
4301
|
if (existsSync2(path)) parts.push(`# ${file}
|
|
4045
4302
|
${readFileSync(path, "utf8").trim()}`);
|
|
4046
4303
|
} catch {
|
|
@@ -4066,7 +4323,7 @@ function stripBom(s) {
|
|
|
4066
4323
|
}
|
|
4067
4324
|
|
|
4068
4325
|
// src/cli/commands/prd.ts
|
|
4069
|
-
var
|
|
4326
|
+
var exec3 = promisify3(execFile);
|
|
4070
4327
|
async function prd(issueRef, opts) {
|
|
4071
4328
|
const issue = await loadIssue(issueRef, opts.input);
|
|
4072
4329
|
const { provider } = resolveFreeProvider(opts.model ?? DEFAULT_PRD_MODEL);
|
|
@@ -4081,11 +4338,11 @@ async function prd(issueRef, opts) {
|
|
|
4081
4338
|
}
|
|
4082
4339
|
async function loadIssue(issueRef, input) {
|
|
4083
4340
|
if (input) {
|
|
4084
|
-
const raw = input === "-" ? await readStdin() : await
|
|
4341
|
+
const raw = input === "-" ? await readStdin() : await readFile10(input, "utf8");
|
|
4085
4342
|
return normalize2(JSON.parse(stripBom(raw)));
|
|
4086
4343
|
}
|
|
4087
4344
|
const num = numericRef(issueRef);
|
|
4088
|
-
const { stdout: stdout2 } = await
|
|
4345
|
+
const { stdout: stdout2 } = await exec3("gh", ["issue", "view", num, "--json", "number,title,body,comments"]);
|
|
4089
4346
|
const data = normalize2(JSON.parse(stdout2));
|
|
4090
4347
|
data.number ??= Number(num);
|
|
4091
4348
|
return data;
|
|
@@ -4100,9 +4357,9 @@ function normalize2(raw) {
|
|
|
4100
4357
|
}
|
|
4101
4358
|
|
|
4102
4359
|
// src/cli/commands/review.ts
|
|
4103
|
-
import { writeFile as writeFile6, readFile as
|
|
4360
|
+
import { writeFile as writeFile6, readFile as readFile11 } from "fs/promises";
|
|
4104
4361
|
import { execFile as execFile2 } from "child_process";
|
|
4105
|
-
import { promisify as
|
|
4362
|
+
import { promisify as promisify4 } from "util";
|
|
4106
4363
|
import pc12 from "picocolors";
|
|
4107
4364
|
|
|
4108
4365
|
// src/core/agent/review.ts
|
|
@@ -4160,7 +4417,7 @@ ${projectGuide}`
|
|
|
4160
4417
|
}
|
|
4161
4418
|
|
|
4162
4419
|
// src/cli/commands/review.ts
|
|
4163
|
-
var
|
|
4420
|
+
var exec4 = promisify4(execFile2);
|
|
4164
4421
|
async function review(prRef, opts) {
|
|
4165
4422
|
const num = opts.input ? prRef.replace(/^#/, "") : numericRef(prRef);
|
|
4166
4423
|
const diff = await loadDiff(num, opts.input);
|
|
@@ -4176,13 +4433,13 @@ async function review(prRef, opts) {
|
|
|
4176
4433
|
}
|
|
4177
4434
|
}
|
|
4178
4435
|
async function loadDiff(num, input) {
|
|
4179
|
-
if (input) return input === "-" ? readStdin() :
|
|
4180
|
-
const { stdout: stdout2 } = await
|
|
4436
|
+
if (input) return input === "-" ? readStdin() : readFile11(input, "utf8");
|
|
4437
|
+
const { stdout: stdout2 } = await exec4("gh", ["pr", "diff", num]);
|
|
4181
4438
|
return stdout2;
|
|
4182
4439
|
}
|
|
4183
4440
|
async function loadMeta(num, input) {
|
|
4184
4441
|
if (input) return { number: Number(num) || void 0, title: `PR ${num}`, body: "" };
|
|
4185
|
-
const { stdout: stdout2 } = await
|
|
4442
|
+
const { stdout: stdout2 } = await exec4("gh", ["pr", "view", num, "--json", "number,title,body"]);
|
|
4186
4443
|
const raw = JSON.parse(stdout2);
|
|
4187
4444
|
return { number: raw.number, title: raw.title ?? "", body: raw.body ?? "" };
|
|
4188
4445
|
}
|
|
@@ -4248,7 +4505,7 @@ function buildProgram() {
|
|
|
4248
4505
|
program.command("add-agent").argument("<name>", t("cli.arg.addAgentName")).requiredOption("--provider <provider>", t("cli.opt.provider")).requiredOption("--model <model>", t("cli.opt.model")).option("--api-key <key>", t("cli.opt.apiKey")).option("--base-url <url>", t("cli.opt.baseUrl")).option("--tool-mode <mode>", t("cli.opt.toolMode"), "auto").option("--set-default", t("cli.opt.setDefault")).description(t("cli.cmd.addAgent")).action((name, opts) => addAgent(name, opts));
|
|
4249
4506
|
program.command("remove-agent").argument("<name>", t("cli.arg.removeAgentName")).description(t("cli.cmd.removeAgent")).action((name) => removeAgent(name));
|
|
4250
4507
|
program.command("list-agents").alias("agents").description(t("cli.cmd.listAgents")).action(() => listAgents());
|
|
4251
|
-
program.command("run").argument("[task]", t("cli.arg.runTask")).option("--agent <name>", t("cli.opt.agent")).option("--mode <mode>", t("cli.opt.mode")).option("--max-steps <n>", t("cli.opt.maxSteps")).option("--json", t("cli.opt.json")).description(t("cli.cmd.run")).action((task, opts) => run(task, opts));
|
|
4508
|
+
program.command("run").argument("[task]", t("cli.arg.runTask")).option("--agent <name>", t("cli.opt.agent")).option("--mode <mode>", t("cli.opt.mode")).option("--max-steps <n>", t("cli.opt.maxSteps")).option("--json", t("cli.opt.json")).option("--verify", t("cli.opt.verify")).description(t("cli.cmd.run")).action((task, opts) => run(task, opts));
|
|
4252
4509
|
program.command("swarm").argument("<task>", t("cli.arg.swarmTask")).option("--agents <names>", t("cli.opt.agents")).option("--max-subtasks <n>", t("cli.opt.maxSubtasks")).description(t("cli.cmd.swarm")).action((task, opts) => swarm(task, opts));
|
|
4253
4510
|
program.command("models").option("--search <text>", t("cli.opt.search")).option("--tools", t("cli.opt.toolsOnly")).option("--free", t("cli.opt.free")).option("--max-price <usd>", t("cli.opt.maxPrice")).option("--sort <order>", t("cli.opt.sort")).option("--limit <n>", t("cli.opt.limit")).description(t("cli.cmd.models")).action((opts) => models(opts));
|
|
4254
4511
|
program.command("prd").argument("<issue>", t("cli.arg.prdIssue")).option("--out <file>", t("cli.opt.out")).option("--model <model>", t("cli.opt.model")).option("--input <file>", t("cli.opt.input")).description(t("cli.cmd.prd")).action((issue, opts) => prd(issue, opts));
|