@kody-ade/kody-engine 0.2.61 → 0.2.62
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/bin/kody2.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// package.json
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "@kody-ade/kody-engine",
|
|
6
|
-
version: "0.2.
|
|
6
|
+
version: "0.2.62",
|
|
7
7
|
description: "kody2 \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
8
8
|
license: "MIT",
|
|
9
9
|
type: "module",
|
|
@@ -50,7 +50,7 @@ var package_default = {
|
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
// src/chat-cli.ts
|
|
53
|
-
import { execFileSync as
|
|
53
|
+
import { execFileSync as execFileSync21 } from "child_process";
|
|
54
54
|
import * as fs22 from "fs";
|
|
55
55
|
import * as path19 from "path";
|
|
56
56
|
|
|
@@ -543,7 +543,7 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
543
543
|
}
|
|
544
544
|
|
|
545
545
|
// src/kody2-cli.ts
|
|
546
|
-
import { execFileSync as
|
|
546
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
547
547
|
import * as fs21 from "fs";
|
|
548
548
|
import * as path18 from "path";
|
|
549
549
|
|
|
@@ -581,9 +581,6 @@ function autoDispatch(opts) {
|
|
|
581
581
|
if (!targetNum) return null;
|
|
582
582
|
const afterTag = extractAfterTag(body);
|
|
583
583
|
if (isPr) {
|
|
584
|
-
if (/\bapprove\b/.test(afterTag)) {
|
|
585
|
-
return { executable: "approve", cliArgs: { pr: targetNum }, target: targetNum };
|
|
586
|
-
}
|
|
587
584
|
if (/\bfix-ci\b/.test(afterTag)) {
|
|
588
585
|
return { executable: "fix-ci", cliArgs: { pr: targetNum }, target: targetNum };
|
|
589
586
|
}
|
|
@@ -1241,206 +1238,6 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
1241
1238
|
}
|
|
1242
1239
|
};
|
|
1243
1240
|
|
|
1244
|
-
// src/scripts/applyApprovals.ts
|
|
1245
|
-
import { execFileSync as execFileSync5 } from "child_process";
|
|
1246
|
-
|
|
1247
|
-
// src/issue.ts
|
|
1248
|
-
import { execFileSync as execFileSync4 } from "child_process";
|
|
1249
|
-
var API_TIMEOUT_MS3 = 3e4;
|
|
1250
|
-
function ghToken2() {
|
|
1251
|
-
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
1252
|
-
}
|
|
1253
|
-
function gh2(args, options) {
|
|
1254
|
-
const token = ghToken2();
|
|
1255
|
-
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
1256
|
-
return execFileSync4("gh", args, {
|
|
1257
|
-
encoding: "utf-8",
|
|
1258
|
-
timeout: API_TIMEOUT_MS3,
|
|
1259
|
-
cwd: options?.cwd,
|
|
1260
|
-
env,
|
|
1261
|
-
input: options?.input,
|
|
1262
|
-
stdio: options?.input ? ["pipe", "pipe", "pipe"] : ["inherit", "pipe", "pipe"]
|
|
1263
|
-
}).trim();
|
|
1264
|
-
}
|
|
1265
|
-
function getIssue(issueNumber, cwd) {
|
|
1266
|
-
const output = gh2(["issue", "view", String(issueNumber), "--json", "number,title,body,comments,labels"], { cwd });
|
|
1267
|
-
const parsed = JSON.parse(output);
|
|
1268
|
-
if (typeof parsed?.title !== "string") {
|
|
1269
|
-
throw new Error(`Issue #${issueNumber}: unexpected response shape`);
|
|
1270
|
-
}
|
|
1271
|
-
return {
|
|
1272
|
-
number: parsed.number ?? issueNumber,
|
|
1273
|
-
title: parsed.title,
|
|
1274
|
-
body: parsed.body ?? "",
|
|
1275
|
-
comments: (parsed.comments ?? []).map((c) => ({
|
|
1276
|
-
body: c.body ?? "",
|
|
1277
|
-
author: c.author?.login ?? "unknown",
|
|
1278
|
-
createdAt: c.createdAt ?? ""
|
|
1279
|
-
})),
|
|
1280
|
-
labels: Array.isArray(parsed.labels) ? parsed.labels.map((l) => l.name ?? "").filter((n) => n.length > 0) : []
|
|
1281
|
-
};
|
|
1282
|
-
}
|
|
1283
|
-
function stripKody2Mentions(body) {
|
|
1284
|
-
return body.replace(/(@)(kody2)/gi, "$1\u200B$2");
|
|
1285
|
-
}
|
|
1286
|
-
function postIssueComment(issueNumber, body, cwd) {
|
|
1287
|
-
try {
|
|
1288
|
-
gh2(["issue", "comment", String(issueNumber), "--body-file", "-"], { input: stripKody2Mentions(body), cwd });
|
|
1289
|
-
} catch (err) {
|
|
1290
|
-
process.stderr.write(
|
|
1291
|
-
`[kody2] failed to post comment on #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
1292
|
-
`
|
|
1293
|
-
);
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
function truncate2(s, maxBytes) {
|
|
1297
|
-
if (s.length <= maxBytes) return s;
|
|
1298
|
-
return `${s.slice(0, maxBytes)}\u2026 (+${s.length - maxBytes} chars)`;
|
|
1299
|
-
}
|
|
1300
|
-
function getPr(prNumber, cwd) {
|
|
1301
|
-
const output = gh2(["pr", "view", String(prNumber), "--json", "number,title,body,headRefName,baseRefName,state"], {
|
|
1302
|
-
cwd
|
|
1303
|
-
});
|
|
1304
|
-
const parsed = JSON.parse(output);
|
|
1305
|
-
if (typeof parsed?.title !== "string") {
|
|
1306
|
-
throw new Error(`PR #${prNumber}: unexpected response shape`);
|
|
1307
|
-
}
|
|
1308
|
-
return {
|
|
1309
|
-
number: parsed.number ?? prNumber,
|
|
1310
|
-
title: parsed.title,
|
|
1311
|
-
body: parsed.body ?? "",
|
|
1312
|
-
headRefName: String(parsed.headRefName ?? ""),
|
|
1313
|
-
baseRefName: String(parsed.baseRefName ?? ""),
|
|
1314
|
-
state: String(parsed.state ?? "")
|
|
1315
|
-
};
|
|
1316
|
-
}
|
|
1317
|
-
function getPrDiff(prNumber, cwd) {
|
|
1318
|
-
try {
|
|
1319
|
-
return gh2(["pr", "diff", String(prNumber)], { cwd });
|
|
1320
|
-
} catch (err) {
|
|
1321
|
-
process.stderr.write(
|
|
1322
|
-
`[kody2] failed to fetch diff for PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
1323
|
-
`
|
|
1324
|
-
);
|
|
1325
|
-
return "";
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
function getPrReviews(prNumber, cwd) {
|
|
1329
|
-
try {
|
|
1330
|
-
const output = gh2(["pr", "view", String(prNumber), "--json", "reviews"], { cwd });
|
|
1331
|
-
const parsed = JSON.parse(output);
|
|
1332
|
-
if (!Array.isArray(parsed?.reviews)) return [];
|
|
1333
|
-
return parsed.reviews.map(
|
|
1334
|
-
(r) => ({
|
|
1335
|
-
body: r.body ?? "",
|
|
1336
|
-
state: r.state ?? "",
|
|
1337
|
-
author: r.author?.login ?? "unknown",
|
|
1338
|
-
submittedAt: r.submittedAt ?? ""
|
|
1339
|
-
})
|
|
1340
|
-
);
|
|
1341
|
-
} catch {
|
|
1342
|
-
return [];
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
function getPrComments(prNumber, cwd) {
|
|
1346
|
-
try {
|
|
1347
|
-
const output = gh2(["pr", "view", String(prNumber), "--json", "comments"], { cwd });
|
|
1348
|
-
const parsed = JSON.parse(output);
|
|
1349
|
-
if (!Array.isArray(parsed?.comments)) return [];
|
|
1350
|
-
return parsed.comments.map((c) => ({
|
|
1351
|
-
body: c.body ?? "",
|
|
1352
|
-
author: c.author?.login ?? "unknown",
|
|
1353
|
-
createdAt: c.createdAt ?? ""
|
|
1354
|
-
})).filter((c) => c.body.trim().length > 0);
|
|
1355
|
-
} catch {
|
|
1356
|
-
return [];
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
var VERDICT_HEADING = /(^|\n)\s*#{1,6}\s*Verdict\s*:/i;
|
|
1360
|
-
function isReviewShaped(body) {
|
|
1361
|
-
return VERDICT_HEADING.test(body);
|
|
1362
|
-
}
|
|
1363
|
-
function getPrLatestReviewBody(prNumber, cwd) {
|
|
1364
|
-
const reviews = getPrReviews(prNumber, cwd).filter((r) => r.body.trim().length > 0).map((r) => ({ body: r.body, at: r.submittedAt }));
|
|
1365
|
-
const comments = getPrComments(prNumber, cwd).filter((c) => isReviewShaped(c.body)).map((c) => ({ body: c.body, at: c.createdAt }));
|
|
1366
|
-
const all = [...reviews, ...comments].sort((a, b) => (b.at || "").localeCompare(a.at || ""));
|
|
1367
|
-
if (all.length > 0) return all[0].body;
|
|
1368
|
-
const pr = getPr(prNumber, cwd);
|
|
1369
|
-
return pr.body;
|
|
1370
|
-
}
|
|
1371
|
-
function postPrReviewComment(prNumber, body, cwd) {
|
|
1372
|
-
try {
|
|
1373
|
-
gh2(["pr", "comment", String(prNumber), "--body-file", "-"], { input: stripKody2Mentions(body), cwd });
|
|
1374
|
-
} catch (err) {
|
|
1375
|
-
process.stderr.write(
|
|
1376
|
-
`[kody2] failed to post review comment on PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
1377
|
-
`
|
|
1378
|
-
);
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
// src/scripts/applyApprovals.ts
|
|
1383
|
-
var API_TIMEOUT_MS4 = 3e4;
|
|
1384
|
-
var applyApprovals = async (ctx) => {
|
|
1385
|
-
const issueArg = typeof ctx.args.issue === "number" ? ctx.args.issue : null;
|
|
1386
|
-
const prArg = typeof ctx.args.pr === "number" ? ctx.args.pr : null;
|
|
1387
|
-
const currentTarget = issueArg ? { type: "issue", number: issueArg } : prArg ? { type: "pr", number: prArg } : null;
|
|
1388
|
-
if (!currentTarget) {
|
|
1389
|
-
ctx.output.exitCode = 64;
|
|
1390
|
-
ctx.output.reason = "approve: must be invoked with --issue or --pr";
|
|
1391
|
-
return;
|
|
1392
|
-
}
|
|
1393
|
-
let state = null;
|
|
1394
|
-
try {
|
|
1395
|
-
state = readTaskState(currentTarget.type, currentTarget.number, ctx.cwd);
|
|
1396
|
-
} catch {
|
|
1397
|
-
state = null;
|
|
1398
|
-
}
|
|
1399
|
-
const issueNumber = currentTarget.type === "issue" ? currentTarget.number : state?.flow?.issueNumber ?? null;
|
|
1400
|
-
const flowName = state?.flow?.name ?? null;
|
|
1401
|
-
const confirmation = formatConfirmation(currentTarget, flowName, issueNumber);
|
|
1402
|
-
try {
|
|
1403
|
-
if (currentTarget.type === "issue") postIssueComment(currentTarget.number, confirmation, ctx.cwd);
|
|
1404
|
-
else postPrReviewComment(currentTarget.number, confirmation, ctx.cwd);
|
|
1405
|
-
} catch {
|
|
1406
|
-
}
|
|
1407
|
-
if (issueNumber && typeof flowName === "string" && flowName.length > 0) {
|
|
1408
|
-
try {
|
|
1409
|
-
execFileSync5("gh", ["issue", "comment", String(issueNumber), "--body", `@kody2 ${flowName}`], {
|
|
1410
|
-
timeout: API_TIMEOUT_MS4,
|
|
1411
|
-
cwd: ctx.cwd,
|
|
1412
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
1413
|
-
});
|
|
1414
|
-
} catch (err) {
|
|
1415
|
-
process.stderr.write(
|
|
1416
|
-
`[kody2 approve] failed to re-trigger flow on issue #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
1417
|
-
`
|
|
1418
|
-
);
|
|
1419
|
-
}
|
|
1420
|
-
}
|
|
1421
|
-
ctx.output.exitCode = 0;
|
|
1422
|
-
};
|
|
1423
|
-
function formatConfirmation(current, flowName, issueNumber) {
|
|
1424
|
-
const lines = [];
|
|
1425
|
-
lines.push("\u2705 **kody2 risk gates approved.**");
|
|
1426
|
-
lines.push("");
|
|
1427
|
-
lines.push("The approval is recorded as this comment \u2014 kody2 reads it directly on the next run.");
|
|
1428
|
-
if (flowName && issueNumber) {
|
|
1429
|
-
lines.push("");
|
|
1430
|
-
if (current.type === "pr") {
|
|
1431
|
-
lines.push(
|
|
1432
|
-
`Re-triggering the \`${flowName}\` flow on the originating issue (#${issueNumber}) \u2014 it will resume from the existing branch/PR checkpoint.`
|
|
1433
|
-
);
|
|
1434
|
-
} else {
|
|
1435
|
-
lines.push(`Re-triggering the \`${flowName}\` flow now \u2014 it will resume from the existing branch checkpoint.`);
|
|
1436
|
-
}
|
|
1437
|
-
} else {
|
|
1438
|
-
lines.push("");
|
|
1439
|
-
lines.push("No active flow found in task state. Post `@kody2 <command>` to resume manually.");
|
|
1440
|
-
}
|
|
1441
|
-
return lines.join("\n");
|
|
1442
|
-
}
|
|
1443
|
-
|
|
1444
1241
|
// src/scripts/buildSyntheticPlugin.ts
|
|
1445
1242
|
import * as fs8 from "fs";
|
|
1446
1243
|
import * as os2 from "os";
|
|
@@ -1535,7 +1332,7 @@ function copyDir(src, dst) {
|
|
|
1535
1332
|
}
|
|
1536
1333
|
|
|
1537
1334
|
// src/coverage.ts
|
|
1538
|
-
import { execFileSync as
|
|
1335
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
1539
1336
|
function patternToRegex(pattern) {
|
|
1540
1337
|
let s = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
1541
1338
|
s = s.replace(/\*\*\//g, "\xA7S").replace(/\*\*/g, "\xA7A").replace(/\*/g, "[^/]*");
|
|
@@ -1553,7 +1350,7 @@ function renderSiblingPath(file, requireSibling) {
|
|
|
1553
1350
|
}
|
|
1554
1351
|
function safeGit(args, cwd) {
|
|
1555
1352
|
try {
|
|
1556
|
-
return
|
|
1353
|
+
return execFileSync4("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
|
|
1557
1354
|
} catch {
|
|
1558
1355
|
return "";
|
|
1559
1356
|
}
|
|
@@ -1759,10 +1556,10 @@ function defaultLabelMap() {
|
|
|
1759
1556
|
}
|
|
1760
1557
|
|
|
1761
1558
|
// src/scripts/commitAndPush.ts
|
|
1762
|
-
import { execFileSync as
|
|
1559
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
1763
1560
|
|
|
1764
1561
|
// src/commit.ts
|
|
1765
|
-
import { execFileSync as
|
|
1562
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
1766
1563
|
import * as fs10 from "fs";
|
|
1767
1564
|
import * as path9 from "path";
|
|
1768
1565
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
@@ -1792,7 +1589,7 @@ var CONVENTIONAL_PREFIXES = [
|
|
|
1792
1589
|
];
|
|
1793
1590
|
function git(args, cwd) {
|
|
1794
1591
|
try {
|
|
1795
|
-
return
|
|
1592
|
+
return execFileSync5("git", args, {
|
|
1796
1593
|
encoding: "utf-8",
|
|
1797
1594
|
timeout: 12e4,
|
|
1798
1595
|
cwd,
|
|
@@ -1850,7 +1647,7 @@ function isForbiddenPath(p) {
|
|
|
1850
1647
|
return false;
|
|
1851
1648
|
}
|
|
1852
1649
|
function listChangedFiles(cwd) {
|
|
1853
|
-
const raw =
|
|
1650
|
+
const raw = execFileSync5("git", ["status", "--porcelain=v1", "-z"], {
|
|
1854
1651
|
encoding: "utf-8",
|
|
1855
1652
|
cwd,
|
|
1856
1653
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -1862,7 +1659,7 @@ function listChangedFiles(cwd) {
|
|
|
1862
1659
|
}
|
|
1863
1660
|
function listFilesInCommit(ref = "HEAD", cwd) {
|
|
1864
1661
|
try {
|
|
1865
|
-
const raw =
|
|
1662
|
+
const raw = execFileSync5("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
|
|
1866
1663
|
encoding: "utf-8",
|
|
1867
1664
|
cwd,
|
|
1868
1665
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -1942,7 +1739,7 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
1942
1739
|
const kind = profile.name;
|
|
1943
1740
|
if (kind === "resolve") {
|
|
1944
1741
|
try {
|
|
1945
|
-
|
|
1742
|
+
execFileSync6("git", ["add", "-A"], { cwd: ctx.cwd, env: { ...process.env, HUSKY: "0" }, stdio: "pipe" });
|
|
1946
1743
|
} catch {
|
|
1947
1744
|
}
|
|
1948
1745
|
} else {
|
|
@@ -2564,8 +2361,8 @@ var discoverQaContext = async (ctx) => {
|
|
|
2564
2361
|
};
|
|
2565
2362
|
|
|
2566
2363
|
// src/scripts/dispatch.ts
|
|
2567
|
-
import { execFileSync as
|
|
2568
|
-
var
|
|
2364
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
2365
|
+
var API_TIMEOUT_MS3 = 3e4;
|
|
2569
2366
|
var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
2570
2367
|
const next = args?.next;
|
|
2571
2368
|
if (!next) {
|
|
@@ -2587,8 +2384,8 @@ var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
|
2587
2384
|
const sub = usePr ? "pr" : "issue";
|
|
2588
2385
|
const body = `@kody2 ${next}`;
|
|
2589
2386
|
try {
|
|
2590
|
-
|
|
2591
|
-
timeout:
|
|
2387
|
+
execFileSync7("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
2388
|
+
timeout: API_TIMEOUT_MS3,
|
|
2592
2389
|
cwd: ctx.cwd,
|
|
2593
2390
|
stdio: ["ignore", "pipe", "pipe"]
|
|
2594
2391
|
});
|
|
@@ -2606,6 +2403,141 @@ function parsePr(url) {
|
|
|
2606
2403
|
return Number.isFinite(n) ? n : null;
|
|
2607
2404
|
}
|
|
2608
2405
|
|
|
2406
|
+
// src/issue.ts
|
|
2407
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
2408
|
+
var API_TIMEOUT_MS4 = 3e4;
|
|
2409
|
+
function ghToken2() {
|
|
2410
|
+
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
2411
|
+
}
|
|
2412
|
+
function gh2(args, options) {
|
|
2413
|
+
const token = ghToken2();
|
|
2414
|
+
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
2415
|
+
return execFileSync8("gh", args, {
|
|
2416
|
+
encoding: "utf-8",
|
|
2417
|
+
timeout: API_TIMEOUT_MS4,
|
|
2418
|
+
cwd: options?.cwd,
|
|
2419
|
+
env,
|
|
2420
|
+
input: options?.input,
|
|
2421
|
+
stdio: options?.input ? ["pipe", "pipe", "pipe"] : ["inherit", "pipe", "pipe"]
|
|
2422
|
+
}).trim();
|
|
2423
|
+
}
|
|
2424
|
+
function getIssue(issueNumber, cwd) {
|
|
2425
|
+
const output = gh2(["issue", "view", String(issueNumber), "--json", "number,title,body,comments,labels"], { cwd });
|
|
2426
|
+
const parsed = JSON.parse(output);
|
|
2427
|
+
if (typeof parsed?.title !== "string") {
|
|
2428
|
+
throw new Error(`Issue #${issueNumber}: unexpected response shape`);
|
|
2429
|
+
}
|
|
2430
|
+
return {
|
|
2431
|
+
number: parsed.number ?? issueNumber,
|
|
2432
|
+
title: parsed.title,
|
|
2433
|
+
body: parsed.body ?? "",
|
|
2434
|
+
comments: (parsed.comments ?? []).map((c) => ({
|
|
2435
|
+
body: c.body ?? "",
|
|
2436
|
+
author: c.author?.login ?? "unknown",
|
|
2437
|
+
createdAt: c.createdAt ?? ""
|
|
2438
|
+
})),
|
|
2439
|
+
labels: Array.isArray(parsed.labels) ? parsed.labels.map((l) => l.name ?? "").filter((n) => n.length > 0) : []
|
|
2440
|
+
};
|
|
2441
|
+
}
|
|
2442
|
+
function stripKody2Mentions(body) {
|
|
2443
|
+
return body.replace(/(@)(kody2)/gi, "$1\u200B$2");
|
|
2444
|
+
}
|
|
2445
|
+
function postIssueComment(issueNumber, body, cwd) {
|
|
2446
|
+
try {
|
|
2447
|
+
gh2(["issue", "comment", String(issueNumber), "--body-file", "-"], { input: stripKody2Mentions(body), cwd });
|
|
2448
|
+
} catch (err) {
|
|
2449
|
+
process.stderr.write(
|
|
2450
|
+
`[kody2] failed to post comment on #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
2451
|
+
`
|
|
2452
|
+
);
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
function truncate2(s, maxBytes) {
|
|
2456
|
+
if (s.length <= maxBytes) return s;
|
|
2457
|
+
return `${s.slice(0, maxBytes)}\u2026 (+${s.length - maxBytes} chars)`;
|
|
2458
|
+
}
|
|
2459
|
+
function getPr(prNumber, cwd) {
|
|
2460
|
+
const output = gh2(["pr", "view", String(prNumber), "--json", "number,title,body,headRefName,baseRefName,state"], {
|
|
2461
|
+
cwd
|
|
2462
|
+
});
|
|
2463
|
+
const parsed = JSON.parse(output);
|
|
2464
|
+
if (typeof parsed?.title !== "string") {
|
|
2465
|
+
throw new Error(`PR #${prNumber}: unexpected response shape`);
|
|
2466
|
+
}
|
|
2467
|
+
return {
|
|
2468
|
+
number: parsed.number ?? prNumber,
|
|
2469
|
+
title: parsed.title,
|
|
2470
|
+
body: parsed.body ?? "",
|
|
2471
|
+
headRefName: String(parsed.headRefName ?? ""),
|
|
2472
|
+
baseRefName: String(parsed.baseRefName ?? ""),
|
|
2473
|
+
state: String(parsed.state ?? "")
|
|
2474
|
+
};
|
|
2475
|
+
}
|
|
2476
|
+
function getPrDiff(prNumber, cwd) {
|
|
2477
|
+
try {
|
|
2478
|
+
return gh2(["pr", "diff", String(prNumber)], { cwd });
|
|
2479
|
+
} catch (err) {
|
|
2480
|
+
process.stderr.write(
|
|
2481
|
+
`[kody2] failed to fetch diff for PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
2482
|
+
`
|
|
2483
|
+
);
|
|
2484
|
+
return "";
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
function getPrReviews(prNumber, cwd) {
|
|
2488
|
+
try {
|
|
2489
|
+
const output = gh2(["pr", "view", String(prNumber), "--json", "reviews"], { cwd });
|
|
2490
|
+
const parsed = JSON.parse(output);
|
|
2491
|
+
if (!Array.isArray(parsed?.reviews)) return [];
|
|
2492
|
+
return parsed.reviews.map(
|
|
2493
|
+
(r) => ({
|
|
2494
|
+
body: r.body ?? "",
|
|
2495
|
+
state: r.state ?? "",
|
|
2496
|
+
author: r.author?.login ?? "unknown",
|
|
2497
|
+
submittedAt: r.submittedAt ?? ""
|
|
2498
|
+
})
|
|
2499
|
+
);
|
|
2500
|
+
} catch {
|
|
2501
|
+
return [];
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
function getPrComments(prNumber, cwd) {
|
|
2505
|
+
try {
|
|
2506
|
+
const output = gh2(["pr", "view", String(prNumber), "--json", "comments"], { cwd });
|
|
2507
|
+
const parsed = JSON.parse(output);
|
|
2508
|
+
if (!Array.isArray(parsed?.comments)) return [];
|
|
2509
|
+
return parsed.comments.map((c) => ({
|
|
2510
|
+
body: c.body ?? "",
|
|
2511
|
+
author: c.author?.login ?? "unknown",
|
|
2512
|
+
createdAt: c.createdAt ?? ""
|
|
2513
|
+
})).filter((c) => c.body.trim().length > 0);
|
|
2514
|
+
} catch {
|
|
2515
|
+
return [];
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
var VERDICT_HEADING = /(^|\n)\s*#{1,6}\s*Verdict\s*:/i;
|
|
2519
|
+
function isReviewShaped(body) {
|
|
2520
|
+
return VERDICT_HEADING.test(body);
|
|
2521
|
+
}
|
|
2522
|
+
function getPrLatestReviewBody(prNumber, cwd) {
|
|
2523
|
+
const reviews = getPrReviews(prNumber, cwd).filter((r) => r.body.trim().length > 0).map((r) => ({ body: r.body, at: r.submittedAt }));
|
|
2524
|
+
const comments = getPrComments(prNumber, cwd).filter((c) => isReviewShaped(c.body)).map((c) => ({ body: c.body, at: c.createdAt }));
|
|
2525
|
+
const all = [...reviews, ...comments].sort((a, b) => (b.at || "").localeCompare(a.at || ""));
|
|
2526
|
+
if (all.length > 0) return all[0].body;
|
|
2527
|
+
const pr = getPr(prNumber, cwd);
|
|
2528
|
+
return pr.body;
|
|
2529
|
+
}
|
|
2530
|
+
function postPrReviewComment(prNumber, body, cwd) {
|
|
2531
|
+
try {
|
|
2532
|
+
gh2(["pr", "comment", String(prNumber), "--body-file", "-"], { input: stripKody2Mentions(body), cwd });
|
|
2533
|
+
} catch (err) {
|
|
2534
|
+
process.stderr.write(
|
|
2535
|
+
`[kody2] failed to post review comment on PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
2536
|
+
`
|
|
2537
|
+
);
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2609
2541
|
// src/pr.ts
|
|
2610
2542
|
var TITLE_MAX = 72;
|
|
2611
2543
|
function stripTitlePrefixes(raw) {
|
|
@@ -2771,7 +2703,7 @@ function computeFailureReason(ctx) {
|
|
|
2771
2703
|
}
|
|
2772
2704
|
|
|
2773
2705
|
// src/scripts/finishFlow.ts
|
|
2774
|
-
import { execFileSync as
|
|
2706
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
2775
2707
|
|
|
2776
2708
|
// src/registry.ts
|
|
2777
2709
|
import * as fs14 from "fs";
|
|
@@ -2959,7 +2891,7 @@ function errMsg(err) {
|
|
|
2959
2891
|
}
|
|
2960
2892
|
|
|
2961
2893
|
// src/scripts/finishFlow.ts
|
|
2962
|
-
var
|
|
2894
|
+
var API_TIMEOUT_MS5 = 3e4;
|
|
2963
2895
|
var STATUS_ICON = {
|
|
2964
2896
|
"review-passed": "\u2705",
|
|
2965
2897
|
"fix-applied": "\u2705",
|
|
@@ -2991,8 +2923,8 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
|
|
|
2991
2923
|
**PR:** ${state.core.prUrl}` : "";
|
|
2992
2924
|
const body = `${icon} kody2 flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
|
|
2993
2925
|
try {
|
|
2994
|
-
|
|
2995
|
-
timeout:
|
|
2926
|
+
execFileSync9("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
2927
|
+
timeout: API_TIMEOUT_MS5,
|
|
2996
2928
|
cwd: ctx.cwd,
|
|
2997
2929
|
stdio: ["ignore", "pipe", "pipe"]
|
|
2998
2930
|
});
|
|
@@ -3005,7 +2937,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
|
|
|
3005
2937
|
};
|
|
3006
2938
|
|
|
3007
2939
|
// src/branch.ts
|
|
3008
|
-
import { execFileSync as
|
|
2940
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
3009
2941
|
var UncommittedChangesError = class extends Error {
|
|
3010
2942
|
constructor(branch) {
|
|
3011
2943
|
super(`Uncommitted changes on branch '${branch}' \u2014 refusing to run to protect work in progress`);
|
|
@@ -3015,7 +2947,7 @@ var UncommittedChangesError = class extends Error {
|
|
|
3015
2947
|
branch;
|
|
3016
2948
|
};
|
|
3017
2949
|
function git2(args, cwd) {
|
|
3018
|
-
return
|
|
2950
|
+
return execFileSync10("git", args, {
|
|
3019
2951
|
encoding: "utf-8",
|
|
3020
2952
|
timeout: 3e4,
|
|
3021
2953
|
cwd,
|
|
@@ -3040,7 +2972,7 @@ function checkoutPrBranch(prNumber, cwd) {
|
|
|
3040
2972
|
SKIP_HOOKS: "1",
|
|
3041
2973
|
GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
|
|
3042
2974
|
};
|
|
3043
|
-
|
|
2975
|
+
execFileSync10("gh", ["pr", "checkout", String(prNumber)], {
|
|
3044
2976
|
cwd,
|
|
3045
2977
|
env,
|
|
3046
2978
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -3107,7 +3039,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch, cwd) {
|
|
|
3107
3039
|
}
|
|
3108
3040
|
|
|
3109
3041
|
// src/gha.ts
|
|
3110
|
-
import { execFileSync as
|
|
3042
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
3111
3043
|
import * as fs15 from "fs";
|
|
3112
3044
|
function getRunUrl() {
|
|
3113
3045
|
const server = process.env.GITHUB_SERVER_URL;
|
|
@@ -3150,7 +3082,7 @@ function reactToTriggerComment(cwd) {
|
|
|
3150
3082
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
3151
3083
|
if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
|
|
3152
3084
|
try {
|
|
3153
|
-
|
|
3085
|
+
execFileSync11("gh", args, opts);
|
|
3154
3086
|
return;
|
|
3155
3087
|
} catch (err) {
|
|
3156
3088
|
lastErr = err;
|
|
@@ -3163,13 +3095,13 @@ function reactToTriggerComment(cwd) {
|
|
|
3163
3095
|
}
|
|
3164
3096
|
function sleepMs(ms) {
|
|
3165
3097
|
try {
|
|
3166
|
-
|
|
3098
|
+
execFileSync11("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
|
|
3167
3099
|
} catch {
|
|
3168
3100
|
}
|
|
3169
3101
|
}
|
|
3170
3102
|
|
|
3171
3103
|
// src/workflow.ts
|
|
3172
|
-
import { execFileSync as
|
|
3104
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
3173
3105
|
var GH_TIMEOUT_MS = 3e4;
|
|
3174
3106
|
function ghToken3() {
|
|
3175
3107
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
@@ -3177,7 +3109,7 @@ function ghToken3() {
|
|
|
3177
3109
|
function gh3(args, cwd) {
|
|
3178
3110
|
const token = ghToken3();
|
|
3179
3111
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
3180
|
-
return
|
|
3112
|
+
return execFileSync12("gh", args, {
|
|
3181
3113
|
encoding: "utf-8",
|
|
3182
3114
|
timeout: GH_TIMEOUT_MS,
|
|
3183
3115
|
cwd,
|
|
@@ -3361,7 +3293,7 @@ function tryPostPr2(prNumber, body, cwd) {
|
|
|
3361
3293
|
}
|
|
3362
3294
|
|
|
3363
3295
|
// src/scripts/initFlow.ts
|
|
3364
|
-
import { execFileSync as
|
|
3296
|
+
import { execFileSync as execFileSync13 } from "child_process";
|
|
3365
3297
|
import * as fs17 from "fs";
|
|
3366
3298
|
import * as path15 from "path";
|
|
3367
3299
|
|
|
@@ -3402,7 +3334,7 @@ function qualityCommandsFor(pm) {
|
|
|
3402
3334
|
function detectOwnerRepo(cwd) {
|
|
3403
3335
|
let url;
|
|
3404
3336
|
try {
|
|
3405
|
-
url =
|
|
3337
|
+
url = execFileSync13("git", ["remote", "get-url", "origin"], {
|
|
3406
3338
|
cwd,
|
|
3407
3339
|
encoding: "utf-8",
|
|
3408
3340
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3486,7 +3418,7 @@ jobs:
|
|
|
3486
3418
|
`;
|
|
3487
3419
|
function defaultBranchFromGit(cwd) {
|
|
3488
3420
|
try {
|
|
3489
|
-
const ref =
|
|
3421
|
+
const ref = execFileSync13("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
3490
3422
|
cwd,
|
|
3491
3423
|
encoding: "utf-8",
|
|
3492
3424
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3494,7 +3426,7 @@ function defaultBranchFromGit(cwd) {
|
|
|
3494
3426
|
return ref.replace("refs/remotes/origin/", "");
|
|
3495
3427
|
} catch {
|
|
3496
3428
|
try {
|
|
3497
|
-
return
|
|
3429
|
+
return execFileSync13("git", ["branch", "--show-current"], {
|
|
3498
3430
|
cwd,
|
|
3499
3431
|
encoding: "utf-8",
|
|
3500
3432
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3787,8 +3719,8 @@ var persistFlowState = async (ctx) => {
|
|
|
3787
3719
|
};
|
|
3788
3720
|
|
|
3789
3721
|
// src/scripts/postClassification.ts
|
|
3790
|
-
import { execFileSync as
|
|
3791
|
-
var
|
|
3722
|
+
import { execFileSync as execFileSync14 } from "child_process";
|
|
3723
|
+
var API_TIMEOUT_MS6 = 3e4;
|
|
3792
3724
|
var VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
3793
3725
|
var postClassification = async (ctx) => {
|
|
3794
3726
|
const issueNumber = ctx.args.issue;
|
|
@@ -3817,9 +3749,9 @@ var postClassification = async (ctx) => {
|
|
|
3817
3749
|
ctx.cwd
|
|
3818
3750
|
);
|
|
3819
3751
|
try {
|
|
3820
|
-
|
|
3752
|
+
execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", `@kody2 ${classification}`], {
|
|
3821
3753
|
cwd: ctx.cwd,
|
|
3822
|
-
timeout:
|
|
3754
|
+
timeout: API_TIMEOUT_MS6,
|
|
3823
3755
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3824
3756
|
});
|
|
3825
3757
|
} catch (err) {
|
|
@@ -3851,9 +3783,9 @@ function parseClassification(prSummary) {
|
|
|
3851
3783
|
}
|
|
3852
3784
|
function tryAuditComment(issueNumber, body, cwd) {
|
|
3853
3785
|
try {
|
|
3854
|
-
|
|
3786
|
+
execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
3855
3787
|
cwd,
|
|
3856
|
-
timeout:
|
|
3788
|
+
timeout: API_TIMEOUT_MS6,
|
|
3857
3789
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3858
3790
|
});
|
|
3859
3791
|
} catch {
|
|
@@ -4035,7 +3967,7 @@ REVIEW_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.github.
|
|
|
4035
3967
|
};
|
|
4036
3968
|
|
|
4037
3969
|
// src/scripts/releaseFlow.ts
|
|
4038
|
-
import { execFileSync as
|
|
3970
|
+
import { execFileSync as execFileSync15, spawnSync } from "child_process";
|
|
4039
3971
|
import * as fs18 from "fs";
|
|
4040
3972
|
import * as path16 from "path";
|
|
4041
3973
|
function bumpVersion(current, bump) {
|
|
@@ -4065,7 +3997,7 @@ function generateChangelog(cwd, newVersion, lastTag) {
|
|
|
4065
3997
|
const range = lastTag ? `${lastTag}..HEAD` : "HEAD";
|
|
4066
3998
|
let log = "";
|
|
4067
3999
|
try {
|
|
4068
|
-
log =
|
|
4000
|
+
log = execFileSync15("git", ["log", range, "--pretty=format:%s||%h", "--no-merges"], {
|
|
4069
4001
|
cwd,
|
|
4070
4002
|
encoding: "utf-8",
|
|
4071
4003
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4122,7 +4054,7 @@ ${entry}${prior.slice(idx + 1)}`);
|
|
|
4122
4054
|
}
|
|
4123
4055
|
}
|
|
4124
4056
|
function git3(args, cwd, timeout = 6e4) {
|
|
4125
|
-
return
|
|
4057
|
+
return execFileSync15("git", args, {
|
|
4126
4058
|
encoding: "utf-8",
|
|
4127
4059
|
timeout,
|
|
4128
4060
|
cwd,
|
|
@@ -4425,7 +4357,7 @@ var resolveArtifacts = async (ctx, profile) => {
|
|
|
4425
4357
|
};
|
|
4426
4358
|
|
|
4427
4359
|
// src/scripts/resolveFlow.ts
|
|
4428
|
-
import { execFileSync as
|
|
4360
|
+
import { execFileSync as execFileSync16 } from "child_process";
|
|
4429
4361
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
4430
4362
|
var resolveFlow = async (ctx) => {
|
|
4431
4363
|
const prNumber = ctx.args.pr;
|
|
@@ -4477,7 +4409,7 @@ var resolveFlow = async (ctx) => {
|
|
|
4477
4409
|
};
|
|
4478
4410
|
function getConflictedFiles(cwd) {
|
|
4479
4411
|
try {
|
|
4480
|
-
const out =
|
|
4412
|
+
const out = execFileSync16("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
4481
4413
|
encoding: "utf-8",
|
|
4482
4414
|
cwd,
|
|
4483
4415
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -4492,7 +4424,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
4492
4424
|
let total = 0;
|
|
4493
4425
|
for (const f of files) {
|
|
4494
4426
|
try {
|
|
4495
|
-
const content =
|
|
4427
|
+
const content = execFileSync16("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
4496
4428
|
const snippet = `### ${f}
|
|
4497
4429
|
|
|
4498
4430
|
\`\`\`
|
|
@@ -4560,295 +4492,6 @@ function tryPostPr4(prNumber, body, cwd) {
|
|
|
4560
4492
|
}
|
|
4561
4493
|
}
|
|
4562
4494
|
|
|
4563
|
-
// src/scripts/riskGate.ts
|
|
4564
|
-
import { execFileSync as execFileSync18 } from "child_process";
|
|
4565
|
-
var ALL_GATES = ["secrets", "workflow-edit", "large-diff", "dep-change", "test-deletion"];
|
|
4566
|
-
var WAITING_LABEL = "kody:waiting";
|
|
4567
|
-
var ADVISORY_MARKER = "kody2 risk gate halted the flow";
|
|
4568
|
-
var APPROVE_COMMAND = /(^|\s)@kody2\s+approve\b/i;
|
|
4569
|
-
var DEFAULT_MAX_FILES = 20;
|
|
4570
|
-
var DEFAULT_MAX_DELETIONS = 500;
|
|
4571
|
-
var riskGate = async (ctx, profile, _agent, args) => {
|
|
4572
|
-
const changedFiles = collectBranchChangedFiles(ctx);
|
|
4573
|
-
const gatesToRun = parseGates(args?.gates);
|
|
4574
|
-
const violations = evaluateGates(ctx, profile.name, changedFiles, gatesToRun, args);
|
|
4575
|
-
if (violations.length === 0) {
|
|
4576
|
-
ctx.data.riskGate = { violations: [], pending: [], decision: "allow" };
|
|
4577
|
-
return;
|
|
4578
|
-
}
|
|
4579
|
-
const targetType = ctx.data.commentTargetType;
|
|
4580
|
-
const targetNumber = Number(ctx.data.commentTargetNumber ?? 0);
|
|
4581
|
-
const approved = hasApproval(ctx, targetType, targetNumber);
|
|
4582
|
-
ctx.data.riskGate = {
|
|
4583
|
-
violations,
|
|
4584
|
-
pending: approved ? [] : violations,
|
|
4585
|
-
decision: approved ? "allow" : "halt"
|
|
4586
|
-
};
|
|
4587
|
-
if (approved || !targetType || targetNumber <= 0) return;
|
|
4588
|
-
try {
|
|
4589
|
-
setKodyLabel(
|
|
4590
|
-
targetNumber,
|
|
4591
|
-
{
|
|
4592
|
-
label: WAITING_LABEL,
|
|
4593
|
-
color: "fbca04",
|
|
4594
|
-
description: "kody2: awaiting human approval of risk gate(s)"
|
|
4595
|
-
},
|
|
4596
|
-
ctx.cwd
|
|
4597
|
-
);
|
|
4598
|
-
} catch {
|
|
4599
|
-
}
|
|
4600
|
-
const compareUrl = computeCompareUrl(ctx);
|
|
4601
|
-
const body = formatAdvisory(violations, compareUrl);
|
|
4602
|
-
try {
|
|
4603
|
-
if (targetType === "issue") postIssueComment(targetNumber, body, ctx.cwd);
|
|
4604
|
-
else postPrReviewComment(targetNumber, body, ctx.cwd);
|
|
4605
|
-
} catch {
|
|
4606
|
-
}
|
|
4607
|
-
if (!ctx.output.reason) {
|
|
4608
|
-
ctx.output.reason = `risk gate halt: ${violations.map((p) => p.name).join(", ")}`;
|
|
4609
|
-
}
|
|
4610
|
-
};
|
|
4611
|
-
function hasApproval(ctx, targetType, targetNumber) {
|
|
4612
|
-
const surfaces = [];
|
|
4613
|
-
if (targetNumber > 0) surfaces.push(targetNumber);
|
|
4614
|
-
if (targetType === "pr") {
|
|
4615
|
-
const state = ctx.data.taskState;
|
|
4616
|
-
const issueNum = state?.flow?.issueNumber;
|
|
4617
|
-
if (typeof issueNum === "number" && issueNum > 0 && !surfaces.includes(issueNum)) {
|
|
4618
|
-
surfaces.push(issueNum);
|
|
4619
|
-
}
|
|
4620
|
-
}
|
|
4621
|
-
for (const n of surfaces) {
|
|
4622
|
-
if (surfaceIsApproved(n, ctx.cwd)) return true;
|
|
4623
|
-
}
|
|
4624
|
-
return false;
|
|
4625
|
-
}
|
|
4626
|
-
function surfaceIsApproved(n, cwd) {
|
|
4627
|
-
let comments = [];
|
|
4628
|
-
try {
|
|
4629
|
-
comments = getIssue(n, cwd).comments;
|
|
4630
|
-
} catch {
|
|
4631
|
-
return false;
|
|
4632
|
-
}
|
|
4633
|
-
const advisoryAt = latestAdvisoryTimestamp(comments);
|
|
4634
|
-
for (const c of comments) {
|
|
4635
|
-
if (!APPROVE_COMMAND.test(c.body)) continue;
|
|
4636
|
-
if (advisoryAt !== null && c.createdAt <= advisoryAt) continue;
|
|
4637
|
-
return true;
|
|
4638
|
-
}
|
|
4639
|
-
return false;
|
|
4640
|
-
}
|
|
4641
|
-
function latestAdvisoryTimestamp(comments) {
|
|
4642
|
-
let latest = null;
|
|
4643
|
-
for (const c of comments) {
|
|
4644
|
-
if (!c.body.includes(ADVISORY_MARKER)) continue;
|
|
4645
|
-
if (latest === null || c.createdAt > latest) latest = c.createdAt;
|
|
4646
|
-
}
|
|
4647
|
-
return latest;
|
|
4648
|
-
}
|
|
4649
|
-
function evaluateGates(ctx, profileName, changedFiles, gatesToRun, args) {
|
|
4650
|
-
const violations = [];
|
|
4651
|
-
if (gatesToRun.includes("secrets")) {
|
|
4652
|
-
const hits = changedFiles.filter(isSecretPath);
|
|
4653
|
-
if (hits.length > 0) {
|
|
4654
|
-
violations.push({
|
|
4655
|
-
name: "secrets",
|
|
4656
|
-
severity: "hard",
|
|
4657
|
-
reason: `secret/credential files touched: ${preview(hits)}`
|
|
4658
|
-
});
|
|
4659
|
-
}
|
|
4660
|
-
}
|
|
4661
|
-
if (gatesToRun.includes("workflow-edit")) {
|
|
4662
|
-
const hits = changedFiles.filter((f) => f.startsWith(".github/workflows/"));
|
|
4663
|
-
if (hits.length > 0) {
|
|
4664
|
-
violations.push({
|
|
4665
|
-
name: "workflow-edit",
|
|
4666
|
-
severity: "soft",
|
|
4667
|
-
reason: `CI workflow files modified: ${preview(hits)}`
|
|
4668
|
-
});
|
|
4669
|
-
}
|
|
4670
|
-
}
|
|
4671
|
-
if (gatesToRun.includes("large-diff")) {
|
|
4672
|
-
const maxFiles = toPositiveInt(args?.maxFiles, DEFAULT_MAX_FILES);
|
|
4673
|
-
const maxDeletions = toPositiveInt(args?.maxDeletions, DEFAULT_MAX_DELETIONS);
|
|
4674
|
-
if (changedFiles.length > maxFiles) {
|
|
4675
|
-
violations.push({
|
|
4676
|
-
name: "large-diff",
|
|
4677
|
-
severity: "soft",
|
|
4678
|
-
reason: `${changedFiles.length} files changed (threshold: ${maxFiles})`
|
|
4679
|
-
});
|
|
4680
|
-
} else {
|
|
4681
|
-
const stats = computeDiffStats(ctx);
|
|
4682
|
-
if (stats && stats.deletions > maxDeletions) {
|
|
4683
|
-
violations.push({
|
|
4684
|
-
name: "large-diff",
|
|
4685
|
-
severity: "soft",
|
|
4686
|
-
reason: `${stats.deletions} lines deleted (threshold: ${maxDeletions})`
|
|
4687
|
-
});
|
|
4688
|
-
}
|
|
4689
|
-
}
|
|
4690
|
-
}
|
|
4691
|
-
if (gatesToRun.includes("dep-change") && profileName !== "chore") {
|
|
4692
|
-
const hits = changedFiles.filter(isDepFile);
|
|
4693
|
-
if (hits.length > 0) {
|
|
4694
|
-
violations.push({
|
|
4695
|
-
name: "dep-change",
|
|
4696
|
-
severity: "soft",
|
|
4697
|
-
reason: `dependency/lockfile changes outside a chore flow: ${preview(hits)}`
|
|
4698
|
-
});
|
|
4699
|
-
}
|
|
4700
|
-
}
|
|
4701
|
-
if (gatesToRun.includes("test-deletion")) {
|
|
4702
|
-
const deleted = listDeletedFilesInHeadCommit(ctx.cwd).filter(isTestFile);
|
|
4703
|
-
if (deleted.length > 0) {
|
|
4704
|
-
violations.push({
|
|
4705
|
-
name: "test-deletion",
|
|
4706
|
-
severity: "soft",
|
|
4707
|
-
reason: `test files deleted: ${preview(deleted)}`
|
|
4708
|
-
});
|
|
4709
|
-
}
|
|
4710
|
-
}
|
|
4711
|
-
return violations;
|
|
4712
|
-
}
|
|
4713
|
-
function parseGates(spec) {
|
|
4714
|
-
if (spec === void 0 || spec === null || spec === "") return ALL_GATES;
|
|
4715
|
-
const list = String(spec).split(",").map((s) => s.trim()).filter(Boolean);
|
|
4716
|
-
const valid = ALL_GATES;
|
|
4717
|
-
const matched = list.filter((n) => valid.includes(n));
|
|
4718
|
-
return matched.length > 0 ? matched : ALL_GATES;
|
|
4719
|
-
}
|
|
4720
|
-
function toPositiveInt(v, fallback) {
|
|
4721
|
-
const n = typeof v === "number" ? v : parseInt(String(v ?? ""), 10);
|
|
4722
|
-
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
4723
|
-
}
|
|
4724
|
-
function preview(list, max = 5) {
|
|
4725
|
-
if (list.length <= max) return list.join(", ");
|
|
4726
|
-
return `${list.slice(0, max).join(", ")} (+${list.length - max} more)`;
|
|
4727
|
-
}
|
|
4728
|
-
var SECRET_PATTERNS = [
|
|
4729
|
-
/(^|\/)\.env(\.|$)/i,
|
|
4730
|
-
/\.pem$/i,
|
|
4731
|
-
/\.key$/i,
|
|
4732
|
-
/(^|\/)(id_rsa|id_ed25519|id_ecdsa)(\.|$)/i,
|
|
4733
|
-
// Match the keyword anywhere inside the filename, as a whole word (so e.g.
|
|
4734
|
-
// `api-secrets.json`, `config/app.credentials.yaml`, `user-passwords.txt`
|
|
4735
|
-
// all trip — while false friends like `secretary.md` do not).
|
|
4736
|
-
/(^|\/)[^/]*\bsecrets?\b[^/]*$/i,
|
|
4737
|
-
/(^|\/)[^/]*\bcredentials?\b[^/]*$/i,
|
|
4738
|
-
/(^|\/)[^/]*\bpasswords?\b[^/]*$/i,
|
|
4739
|
-
/(^|\/)[^/]*\bapi[-_]keys?\b[^/]*$/i,
|
|
4740
|
-
/(^|\/)\.netrc$/i,
|
|
4741
|
-
/(^|\/)\.npmrc$/i
|
|
4742
|
-
];
|
|
4743
|
-
function isSecretPath(p) {
|
|
4744
|
-
return SECRET_PATTERNS.some((r) => r.test(p));
|
|
4745
|
-
}
|
|
4746
|
-
var DEP_FILES = /* @__PURE__ */ new Set([
|
|
4747
|
-
"package.json",
|
|
4748
|
-
"pnpm-lock.yaml",
|
|
4749
|
-
"package-lock.json",
|
|
4750
|
-
"yarn.lock",
|
|
4751
|
-
"requirements.txt",
|
|
4752
|
-
"Pipfile",
|
|
4753
|
-
"Pipfile.lock",
|
|
4754
|
-
"poetry.lock",
|
|
4755
|
-
"go.mod",
|
|
4756
|
-
"go.sum",
|
|
4757
|
-
"Cargo.toml",
|
|
4758
|
-
"Cargo.lock",
|
|
4759
|
-
"Gemfile",
|
|
4760
|
-
"Gemfile.lock"
|
|
4761
|
-
]);
|
|
4762
|
-
function isDepFile(p) {
|
|
4763
|
-
return DEP_FILES.has(p.split("/").pop() ?? "");
|
|
4764
|
-
}
|
|
4765
|
-
function isTestFile(p) {
|
|
4766
|
-
return /(^|\/)(tests?|__tests__|spec)\//i.test(p) || /\.(test|spec)\.[a-z0-9]+$/i.test(p);
|
|
4767
|
-
}
|
|
4768
|
-
function collectBranchChangedFiles(ctx) {
|
|
4769
|
-
const base = ctx.config.git.defaultBranch;
|
|
4770
|
-
for (const ref of [`origin/${base}...HEAD`, `${base}...HEAD`]) {
|
|
4771
|
-
try {
|
|
4772
|
-
const out = execFileSync18("git", ["diff", "--name-only", ref], {
|
|
4773
|
-
cwd: ctx.cwd,
|
|
4774
|
-
encoding: "utf-8",
|
|
4775
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
4776
|
-
});
|
|
4777
|
-
const files = out.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
4778
|
-
if (files.length > 0) return files;
|
|
4779
|
-
} catch {
|
|
4780
|
-
}
|
|
4781
|
-
}
|
|
4782
|
-
return ctx.data.changedFiles ?? [];
|
|
4783
|
-
}
|
|
4784
|
-
function computeDiffStats(ctx) {
|
|
4785
|
-
const base = ctx.config.git.defaultBranch;
|
|
4786
|
-
for (const ref of [`origin/${base}...HEAD`, `${base}...HEAD`]) {
|
|
4787
|
-
try {
|
|
4788
|
-
const out = execFileSync18("git", ["diff", "--shortstat", ref], {
|
|
4789
|
-
cwd: ctx.cwd,
|
|
4790
|
-
encoding: "utf-8",
|
|
4791
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
4792
|
-
}).trim();
|
|
4793
|
-
if (out) return parseShortstat(out);
|
|
4794
|
-
} catch {
|
|
4795
|
-
}
|
|
4796
|
-
}
|
|
4797
|
-
return null;
|
|
4798
|
-
}
|
|
4799
|
-
function parseShortstat(s) {
|
|
4800
|
-
const ins = /(\d+)\s+insertions?/.exec(s);
|
|
4801
|
-
const del = /(\d+)\s+deletions?/.exec(s);
|
|
4802
|
-
return {
|
|
4803
|
-
insertions: ins ? parseInt(ins[1], 10) : 0,
|
|
4804
|
-
deletions: del ? parseInt(del[1], 10) : 0
|
|
4805
|
-
};
|
|
4806
|
-
}
|
|
4807
|
-
function listDeletedFilesInHeadCommit(cwd) {
|
|
4808
|
-
try {
|
|
4809
|
-
const out = execFileSync18("git", ["show", "--name-status", "--pretty=format:", "HEAD"], {
|
|
4810
|
-
cwd,
|
|
4811
|
-
encoding: "utf-8",
|
|
4812
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
4813
|
-
});
|
|
4814
|
-
return out.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("D ")).map((l) => l.slice(2).trim()).filter(Boolean);
|
|
4815
|
-
} catch {
|
|
4816
|
-
return [];
|
|
4817
|
-
}
|
|
4818
|
-
}
|
|
4819
|
-
function formatAdvisory(violations, compareUrl) {
|
|
4820
|
-
const lines = [];
|
|
4821
|
-
lines.push(`\u23F8\uFE0F **${ADVISORY_MARKER}.**`);
|
|
4822
|
-
lines.push("");
|
|
4823
|
-
lines.push("The branch was pushed but **no PR was opened** \u2014 waiting for human approval:");
|
|
4824
|
-
lines.push("");
|
|
4825
|
-
for (const v of violations) {
|
|
4826
|
-
lines.push(`- **\`${v.name}\`** _(${v.severity})_ \u2014 ${v.reason}`);
|
|
4827
|
-
}
|
|
4828
|
-
lines.push("");
|
|
4829
|
-
if (compareUrl) {
|
|
4830
|
-
lines.push(`\u{1F4CE} Review the branch diff: ${compareUrl}`);
|
|
4831
|
-
lines.push("");
|
|
4832
|
-
}
|
|
4833
|
-
lines.push("**To approve and resume**, post a comment on this issue or PR:");
|
|
4834
|
-
lines.push("");
|
|
4835
|
-
lines.push("> `@kody2 approve`");
|
|
4836
|
-
lines.push("");
|
|
4837
|
-
lines.push(
|
|
4838
|
-
"kody2 will open the PR and continue the flow from this checkpoint. No re-running the agent."
|
|
4839
|
-
);
|
|
4840
|
-
return lines.join("\n");
|
|
4841
|
-
}
|
|
4842
|
-
function computeCompareUrl(ctx) {
|
|
4843
|
-
const branch = ctx.data.branch;
|
|
4844
|
-
if (!branch) return null;
|
|
4845
|
-
const owner = ctx.config.github?.owner;
|
|
4846
|
-
const repo = ctx.config.github?.repo;
|
|
4847
|
-
if (!owner || !repo) return null;
|
|
4848
|
-
const base = ctx.config.git.defaultBranch;
|
|
4849
|
-
return `https://github.com/${owner}/${repo}/compare/${base}...${branch}`;
|
|
4850
|
-
}
|
|
4851
|
-
|
|
4852
4495
|
// src/scripts/runFlow.ts
|
|
4853
4496
|
var runFlow = async (ctx) => {
|
|
4854
4497
|
const issueNumber = ctx.args.issue;
|
|
@@ -4945,8 +4588,8 @@ var skipAgent = async (ctx) => {
|
|
|
4945
4588
|
};
|
|
4946
4589
|
|
|
4947
4590
|
// src/scripts/startFlow.ts
|
|
4948
|
-
import { execFileSync as
|
|
4949
|
-
var
|
|
4591
|
+
import { execFileSync as execFileSync17 } from "child_process";
|
|
4592
|
+
var API_TIMEOUT_MS7 = 3e4;
|
|
4950
4593
|
var startFlow = async (ctx, profile, _agentResult, args) => {
|
|
4951
4594
|
const entry = args?.entry;
|
|
4952
4595
|
if (!entry) {
|
|
@@ -4979,8 +4622,8 @@ function postKody2Comment(target, issueNumber, state, next, cwd) {
|
|
|
4979
4622
|
const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
|
|
4980
4623
|
const body = `@kody2 ${next}`;
|
|
4981
4624
|
try {
|
|
4982
|
-
|
|
4983
|
-
timeout:
|
|
4625
|
+
execFileSync17("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
4626
|
+
timeout: API_TIMEOUT_MS7,
|
|
4984
4627
|
cwd,
|
|
4985
4628
|
stdio: ["ignore", "pipe", "pipe"]
|
|
4986
4629
|
});
|
|
@@ -4999,7 +4642,7 @@ function parsePr2(url) {
|
|
|
4999
4642
|
}
|
|
5000
4643
|
|
|
5001
4644
|
// src/scripts/syncFlow.ts
|
|
5002
|
-
import { execFileSync as
|
|
4645
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
5003
4646
|
var syncFlow = async (ctx) => {
|
|
5004
4647
|
ctx.skipAgent = true;
|
|
5005
4648
|
const prNumber = ctx.args.pr;
|
|
@@ -5058,7 +4701,7 @@ function bail2(ctx, prNumber, reason) {
|
|
|
5058
4701
|
}
|
|
5059
4702
|
function revParseHead(cwd) {
|
|
5060
4703
|
try {
|
|
5061
|
-
return
|
|
4704
|
+
return execFileSync18("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
5062
4705
|
} catch {
|
|
5063
4706
|
return "";
|
|
5064
4707
|
}
|
|
@@ -5066,9 +4709,9 @@ function revParseHead(cwd) {
|
|
|
5066
4709
|
function pushBranch(branch, cwd) {
|
|
5067
4710
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
5068
4711
|
try {
|
|
5069
|
-
|
|
4712
|
+
execFileSync18("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
5070
4713
|
} catch {
|
|
5071
|
-
|
|
4714
|
+
execFileSync18("git", ["push", "--force-with-lease", "-u", "origin", branch], {
|
|
5072
4715
|
cwd,
|
|
5073
4716
|
env,
|
|
5074
4717
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5310,9 +4953,7 @@ var postflightScripts = {
|
|
|
5310
4953
|
finishFlow,
|
|
5311
4954
|
advanceFlow,
|
|
5312
4955
|
persistFlowState,
|
|
5313
|
-
postClassification
|
|
5314
|
-
riskGate,
|
|
5315
|
-
applyApprovals
|
|
4956
|
+
postClassification
|
|
5316
4957
|
};
|
|
5317
4958
|
var allScriptNames = /* @__PURE__ */ new Set([
|
|
5318
4959
|
...Object.keys(preflightScripts),
|
|
@@ -5320,7 +4961,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
5320
4961
|
]);
|
|
5321
4962
|
|
|
5322
4963
|
// src/tools.ts
|
|
5323
|
-
import { execFileSync as
|
|
4964
|
+
import { execFileSync as execFileSync19 } from "child_process";
|
|
5324
4965
|
function verifyCliTools(tools, cwd) {
|
|
5325
4966
|
const out = [];
|
|
5326
4967
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -5353,7 +4994,7 @@ function verifyOne(tool, cwd) {
|
|
|
5353
4994
|
}
|
|
5354
4995
|
function runShell2(cmd, cwd, timeoutMs = 3e4) {
|
|
5355
4996
|
try {
|
|
5356
|
-
|
|
4997
|
+
execFileSync19("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
5357
4998
|
return true;
|
|
5358
4999
|
} catch {
|
|
5359
5000
|
return false;
|
|
@@ -5683,7 +5324,7 @@ function detectPackageManager2(cwd) {
|
|
|
5683
5324
|
}
|
|
5684
5325
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
5685
5326
|
try {
|
|
5686
|
-
|
|
5327
|
+
execFileSync20(cmd, args, {
|
|
5687
5328
|
cwd,
|
|
5688
5329
|
stdio: stream ? "inherit" : "pipe",
|
|
5689
5330
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -5696,7 +5337,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
5696
5337
|
}
|
|
5697
5338
|
function isOnPath(bin) {
|
|
5698
5339
|
try {
|
|
5699
|
-
|
|
5340
|
+
execFileSync20("which", [bin], { stdio: "pipe" });
|
|
5700
5341
|
return true;
|
|
5701
5342
|
} catch {
|
|
5702
5343
|
return false;
|
|
@@ -5730,7 +5371,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
5730
5371
|
} catch {
|
|
5731
5372
|
}
|
|
5732
5373
|
try {
|
|
5733
|
-
|
|
5374
|
+
execFileSync20("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
5734
5375
|
process.stdout.write("\u2192 kody2: litellm already installed\n");
|
|
5735
5376
|
return 0;
|
|
5736
5377
|
} catch {
|
|
@@ -5740,16 +5381,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
5740
5381
|
}
|
|
5741
5382
|
function configureGitIdentity(cwd) {
|
|
5742
5383
|
try {
|
|
5743
|
-
const name =
|
|
5384
|
+
const name = execFileSync20("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
5744
5385
|
if (name) return;
|
|
5745
5386
|
} catch {
|
|
5746
5387
|
}
|
|
5747
5388
|
try {
|
|
5748
|
-
|
|
5389
|
+
execFileSync20("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
5749
5390
|
} catch {
|
|
5750
5391
|
}
|
|
5751
5392
|
try {
|
|
5752
|
-
|
|
5393
|
+
execFileSync20("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
5753
5394
|
cwd,
|
|
5754
5395
|
stdio: "pipe"
|
|
5755
5396
|
});
|
|
@@ -5926,9 +5567,9 @@ function commitChatFiles(cwd, sessionId, verbose) {
|
|
|
5926
5567
|
if (paths.length === 0) return;
|
|
5927
5568
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
5928
5569
|
try {
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5570
|
+
execFileSync21("git", ["add", ...paths], opts);
|
|
5571
|
+
execFileSync21("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
5572
|
+
execFileSync21("git", ["push", "--quiet", "origin", "HEAD"], opts);
|
|
5932
5573
|
} catch (err) {
|
|
5933
5574
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5934
5575
|
process.stderr.write(`[kody2:chat] commit/push skipped: ${msg}
|
|
@@ -49,21 +49,11 @@
|
|
|
49
49
|
"description": "kody2: applying review feedback"
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
|
-
{
|
|
53
|
-
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"script": "loadConventions"
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
"script": "loadCoverageRules"
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
"script": "composePrompt"
|
|
66
|
-
}
|
|
52
|
+
{ "script": "fixFlow" },
|
|
53
|
+
{ "script": "loadTaskState" },
|
|
54
|
+
{ "script": "loadConventions" },
|
|
55
|
+
{ "script": "loadCoverageRules" },
|
|
56
|
+
{ "script": "composePrompt" }
|
|
67
57
|
],
|
|
68
58
|
"postflight": [
|
|
69
59
|
{ "script": "parseAgentResult" },
|
|
@@ -71,12 +61,11 @@
|
|
|
71
61
|
{ "script": "verify" },
|
|
72
62
|
{ "script": "checkCoverageWithRetry" },
|
|
73
63
|
{ "script": "commitAndPush" },
|
|
74
|
-
{ "script": "
|
|
75
|
-
{ "script": "
|
|
76
|
-
{ "script": "postIssueComment", "runWhen": { "data.riskGate.decision": "allow" } },
|
|
64
|
+
{ "script": "ensurePr" },
|
|
65
|
+
{ "script": "postIssueComment" },
|
|
77
66
|
{ "script": "writeRunSummary" },
|
|
78
|
-
{ "script": "saveTaskState"
|
|
79
|
-
{ "script": "advanceFlow"
|
|
67
|
+
{ "script": "saveTaskState" },
|
|
68
|
+
{ "script": "advanceFlow" }
|
|
80
69
|
]
|
|
81
70
|
},
|
|
82
71
|
"output": {
|
|
@@ -42,24 +42,12 @@
|
|
|
42
42
|
"description": "kody2: implementing the change"
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
|
-
{
|
|
46
|
-
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
{
|
|
52
|
-
"script": "resolveArtifacts"
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
"script": "loadConventions"
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
"script": "loadCoverageRules"
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
"script": "composePrompt"
|
|
62
|
-
}
|
|
45
|
+
{ "script": "runFlow" },
|
|
46
|
+
{ "script": "loadTaskState" },
|
|
47
|
+
{ "script": "resolveArtifacts" },
|
|
48
|
+
{ "script": "loadConventions" },
|
|
49
|
+
{ "script": "loadCoverageRules" },
|
|
50
|
+
{ "script": "composePrompt" }
|
|
63
51
|
],
|
|
64
52
|
"postflight": [
|
|
65
53
|
{ "script": "parseAgentResult" },
|
|
@@ -67,13 +55,12 @@
|
|
|
67
55
|
{ "script": "verify" },
|
|
68
56
|
{ "script": "checkCoverageWithRetry" },
|
|
69
57
|
{ "script": "commitAndPush" },
|
|
70
|
-
{ "script": "
|
|
71
|
-
{ "script": "
|
|
72
|
-
{ "script": "postIssueComment", "runWhen": { "data.riskGate.decision": "allow" } },
|
|
58
|
+
{ "script": "ensurePr" },
|
|
59
|
+
{ "script": "postIssueComment" },
|
|
73
60
|
{ "script": "writeRunSummary" },
|
|
74
|
-
{ "script": "saveTaskState"
|
|
75
|
-
{ "script": "mirrorStateToPr"
|
|
76
|
-
{ "script": "advanceFlow"
|
|
61
|
+
{ "script": "saveTaskState" },
|
|
62
|
+
{ "script": "mirrorStateToPr" },
|
|
63
|
+
{ "script": "advanceFlow" }
|
|
77
64
|
]
|
|
78
65
|
},
|
|
79
66
|
"input": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.62",
|
|
4
4
|
"description": "kody2 — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/templates/kody2.yml
CHANGED
|
@@ -20,8 +20,6 @@
|
|
|
20
20
|
# 2. Pushes from kody2 won't trigger downstream workflows.
|
|
21
21
|
# 3. Any commit that modifies `.github/workflows/*` is REJECTED by
|
|
22
22
|
# GitHub — the default GITHUB_TOKEN can't touch workflow files.
|
|
23
|
-
# (The `workflow-edit` risk gate is effectively unreachable without
|
|
24
|
-
# a token carrying the `workflow` scope.)
|
|
25
23
|
# Set KODY_TOKEN in repo Settings → Secrets → Actions.
|
|
26
24
|
|
|
27
25
|
name: kody2
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "approve",
|
|
3
|
-
"role": "utility",
|
|
4
|
-
"describe": "Acknowledge pending risk gate(s) on an issue or PR: applies kody-approve:* labels on both the issue and its PR (if discoverable via task state), then re-triggers the paused flow. Invoked by `@kody2 approve`.",
|
|
5
|
-
"inputs": [
|
|
6
|
-
{
|
|
7
|
-
"name": "issue",
|
|
8
|
-
"flag": "--issue",
|
|
9
|
-
"type": "int",
|
|
10
|
-
"required": false,
|
|
11
|
-
"describe": "Issue number (when approving on the originating issue)."
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"name": "pr",
|
|
15
|
-
"flag": "--pr",
|
|
16
|
-
"type": "int",
|
|
17
|
-
"required": false,
|
|
18
|
-
"describe": "PR number (when approving on the PR side)."
|
|
19
|
-
}
|
|
20
|
-
],
|
|
21
|
-
"claudeCode": {
|
|
22
|
-
"model": "inherit",
|
|
23
|
-
"permissionMode": "default",
|
|
24
|
-
"maxTurns": 0,
|
|
25
|
-
"maxThinkingTokens": null,
|
|
26
|
-
"systemPromptAppend": null,
|
|
27
|
-
"tools": [],
|
|
28
|
-
"hooks": [],
|
|
29
|
-
"skills": [],
|
|
30
|
-
"commands": [],
|
|
31
|
-
"subagents": [],
|
|
32
|
-
"plugins": [],
|
|
33
|
-
"mcpServers": []
|
|
34
|
-
},
|
|
35
|
-
"cliTools": [],
|
|
36
|
-
"scripts": {
|
|
37
|
-
"preflight": [
|
|
38
|
-
{ "script": "skipAgent" }
|
|
39
|
-
],
|
|
40
|
-
"postflight": [
|
|
41
|
-
{ "script": "applyApprovals" }
|
|
42
|
-
]
|
|
43
|
-
},
|
|
44
|
-
"output": {
|
|
45
|
-
"actionTypes": []
|
|
46
|
-
}
|
|
47
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(approve is an agent-less utility; this file is a placeholder kept to satisfy the profile layout convention.)
|