@kody-ade/kody-engine 0.2.58 → 0.2.60
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 +392 -228
- package/dist/executables/approve/profile.json +47 -0
- package/dist/executables/approve/prompt.md +1 -0
- package/dist/executables/fix/profile.json +11 -34
- package/dist/executables/release/profile.json +1 -0
- package/dist/executables/review/profile.json +1 -0
- package/dist/executables/run/profile.json +12 -37
- package/dist/executables/types.ts +7 -0
- package/package.json +1 -1
- package/templates/kody2.yml +6 -2
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.60",
|
|
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 execFileSync23 } 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 execFileSync22 } from "child_process";
|
|
547
547
|
import * as fs21 from "fs";
|
|
548
548
|
import * as path18 from "path";
|
|
549
549
|
|
|
@@ -581,6 +581,9 @@ 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
|
+
}
|
|
584
587
|
if (/\bfix-ci\b/.test(afterTag)) {
|
|
585
588
|
return { executable: "fix-ci", cliArgs: { pr: targetNum }, target: targetNum };
|
|
586
589
|
}
|
|
@@ -754,6 +757,7 @@ import * as path6 from "path";
|
|
|
754
757
|
var VALID_INPUT_TYPES = /* @__PURE__ */ new Set(["int", "string", "bool", "enum"]);
|
|
755
758
|
var VALID_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "acceptEdits", "plan", "bypassPermissions"]);
|
|
756
759
|
var VALID_ROLES = /* @__PURE__ */ new Set(["primitive", "orchestrator", "watch", "utility"]);
|
|
760
|
+
var VALID_PHASES = /* @__PURE__ */ new Set(["research", "planning", "implementing", "reviewing", "shipped", "failed", "idle"]);
|
|
757
761
|
var ProfileError = class extends Error {
|
|
758
762
|
constructor(profilePath, message) {
|
|
759
763
|
super(`Invalid profile at ${profilePath}:
|
|
@@ -788,12 +792,20 @@ function loadProfile(profilePath) {
|
|
|
788
792
|
);
|
|
789
793
|
}
|
|
790
794
|
const role = r.role;
|
|
795
|
+
let phase;
|
|
796
|
+
if (r.phase !== void 0) {
|
|
797
|
+
if (typeof r.phase !== "string" || !VALID_PHASES.has(r.phase)) {
|
|
798
|
+
throw new ProfileError(profilePath, `"phase" must be one of: ${[...VALID_PHASES].join(" | ")}`);
|
|
799
|
+
}
|
|
800
|
+
phase = r.phase;
|
|
801
|
+
}
|
|
791
802
|
const profile = {
|
|
792
803
|
name: requireString(profilePath, r, "name"),
|
|
793
804
|
describe: typeof r.describe === "string" ? r.describe : "",
|
|
794
805
|
role,
|
|
795
806
|
kind,
|
|
796
807
|
schedule: typeof r.schedule === "string" ? r.schedule : void 0,
|
|
808
|
+
phase,
|
|
797
809
|
inputs: parseInputs(profilePath, r.inputs),
|
|
798
810
|
claudeCode: parseClaudeCode(profilePath, r.claudeCode),
|
|
799
811
|
cliTools: parseCliTools(profilePath, r.cliTools),
|
|
@@ -1060,7 +1072,7 @@ function parseStateComment(body) {
|
|
|
1060
1072
|
return emptyState();
|
|
1061
1073
|
}
|
|
1062
1074
|
}
|
|
1063
|
-
function reduce(state, executable, action) {
|
|
1075
|
+
function reduce(state, executable, action, phase) {
|
|
1064
1076
|
if (!action) return state;
|
|
1065
1077
|
const newAttempts = { ...state.core.attempts, [executable]: (state.core.attempts[executable] ?? 0) + 1 };
|
|
1066
1078
|
const newExecutables = {
|
|
@@ -1079,7 +1091,7 @@ function reduce(state, executable, action) {
|
|
|
1079
1091
|
lastOutcome: action,
|
|
1080
1092
|
currentExecutable: executable,
|
|
1081
1093
|
status: statusFromAction(action),
|
|
1082
|
-
phase: phaseFromAction(
|
|
1094
|
+
phase: phaseFromAction(action, phase)
|
|
1083
1095
|
},
|
|
1084
1096
|
executables: newExecutables,
|
|
1085
1097
|
artifacts: { ...state.artifacts ?? {} },
|
|
@@ -1092,12 +1104,9 @@ function statusFromAction(action) {
|
|
|
1092
1104
|
if (/COMPLETED$|SHIPPED$|MERGED$|SUCCESS$/i.test(action.type)) return "succeeded";
|
|
1093
1105
|
return "running";
|
|
1094
1106
|
}
|
|
1095
|
-
function phaseFromAction(
|
|
1107
|
+
function phaseFromAction(action, phase) {
|
|
1096
1108
|
if (/FAILED$|ERROR$|REJECTED$/i.test(action.type)) return "failed";
|
|
1097
|
-
|
|
1098
|
-
if (executable === "review") return "reviewing";
|
|
1099
|
-
if (executable === "release") return "shipped";
|
|
1100
|
-
return "idle";
|
|
1109
|
+
return phase ?? "idle";
|
|
1101
1110
|
}
|
|
1102
1111
|
function noteFromAction(action) {
|
|
1103
1112
|
const p = action.payload;
|
|
@@ -1206,7 +1215,7 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
1206
1215
|
try {
|
|
1207
1216
|
const issueState = readTaskState("issue", flow.issueNumber, ctx.cwd);
|
|
1208
1217
|
issueState.flow = flow;
|
|
1209
|
-
const next = reduce(issueState, profile.name, action);
|
|
1218
|
+
const next = reduce(issueState, profile.name, action, profile.phase);
|
|
1210
1219
|
if (state?.core.prUrl && !next.core.prUrl) next.core.prUrl = state.core.prUrl;
|
|
1211
1220
|
next.flow = flow;
|
|
1212
1221
|
writeTaskState("issue", flow.issueNumber, next, ctx.cwd);
|
|
@@ -1232,6 +1241,265 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
1232
1241
|
}
|
|
1233
1242
|
};
|
|
1234
1243
|
|
|
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 ALL_APPROVE_LABELS = [
|
|
1385
|
+
{ label: "kody-approve:all", description: "kody2: approve all soft risk gates" },
|
|
1386
|
+
{ label: "kody-approve:secrets", description: "kody2: approve the secrets risk gate and resume the flow" },
|
|
1387
|
+
{ label: "kody-approve:workflow-edit", description: "kody2: approve the workflow-edit risk gate and resume the flow" },
|
|
1388
|
+
{ label: "kody-approve:large-diff", description: "kody2: approve the large-diff risk gate and resume the flow" },
|
|
1389
|
+
{ label: "kody-approve:dep-change", description: "kody2: approve the dep-change risk gate and resume the flow" },
|
|
1390
|
+
{ label: "kody-approve:test-deletion", description: "kody2: approve the test-deletion risk gate and resume the flow" }
|
|
1391
|
+
];
|
|
1392
|
+
var applyApprovals = async (ctx) => {
|
|
1393
|
+
const issueArg = typeof ctx.args.issue === "number" ? ctx.args.issue : null;
|
|
1394
|
+
const prArg = typeof ctx.args.pr === "number" ? ctx.args.pr : null;
|
|
1395
|
+
const currentTarget = issueArg ? { type: "issue", number: issueArg } : prArg ? { type: "pr", number: prArg } : null;
|
|
1396
|
+
if (!currentTarget) {
|
|
1397
|
+
ctx.output.exitCode = 64;
|
|
1398
|
+
ctx.output.reason = "approve: must be invoked with --issue or --pr";
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
let state = null;
|
|
1402
|
+
try {
|
|
1403
|
+
state = readTaskState(currentTarget.type, currentTarget.number, ctx.cwd);
|
|
1404
|
+
} catch {
|
|
1405
|
+
state = null;
|
|
1406
|
+
}
|
|
1407
|
+
const issueNumber = currentTarget.type === "issue" ? currentTarget.number : state?.flow?.issueNumber ?? null;
|
|
1408
|
+
const prNumber = currentTarget.type === "pr" ? currentTarget.number : parsePrNumber(state?.core?.prUrl);
|
|
1409
|
+
const targets = uniquePairs(
|
|
1410
|
+
[
|
|
1411
|
+
{ type: "issue", number: issueNumber },
|
|
1412
|
+
{ type: "pr", number: prNumber }
|
|
1413
|
+
].filter((t) => typeof t.number === "number" && t.number > 0)
|
|
1414
|
+
);
|
|
1415
|
+
for (const spec of ALL_APPROVE_LABELS) {
|
|
1416
|
+
ensureLabel(spec, ctx.cwd);
|
|
1417
|
+
}
|
|
1418
|
+
for (const t of targets) {
|
|
1419
|
+
for (const spec of ALL_APPROVE_LABELS) {
|
|
1420
|
+
addLabel(t.number, spec.label, ctx.cwd);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
const confirmation = formatConfirmation(currentTarget, targets, state);
|
|
1424
|
+
try {
|
|
1425
|
+
if (currentTarget.type === "issue") postIssueComment(currentTarget.number, confirmation, ctx.cwd);
|
|
1426
|
+
else postPrReviewComment(currentTarget.number, confirmation, ctx.cwd);
|
|
1427
|
+
} catch {
|
|
1428
|
+
}
|
|
1429
|
+
const flowName = state?.flow?.name;
|
|
1430
|
+
if (issueNumber && typeof flowName === "string" && flowName.length > 0) {
|
|
1431
|
+
try {
|
|
1432
|
+
execFileSync5("gh", ["issue", "comment", String(issueNumber), "--body", `@kody2 ${flowName}`], {
|
|
1433
|
+
timeout: API_TIMEOUT_MS4,
|
|
1434
|
+
cwd: ctx.cwd,
|
|
1435
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1436
|
+
});
|
|
1437
|
+
} catch (err) {
|
|
1438
|
+
process.stderr.write(
|
|
1439
|
+
`[kody2 approve] failed to re-trigger flow on issue #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
1440
|
+
`
|
|
1441
|
+
);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
ctx.output.exitCode = 0;
|
|
1445
|
+
};
|
|
1446
|
+
function parsePrNumber(url) {
|
|
1447
|
+
if (!url) return null;
|
|
1448
|
+
const m = url.match(/\/pull\/(\d+)(?:[/?#]|$)/);
|
|
1449
|
+
if (!m) return null;
|
|
1450
|
+
const n = parseInt(m[1], 10);
|
|
1451
|
+
return Number.isFinite(n) ? n : null;
|
|
1452
|
+
}
|
|
1453
|
+
function uniquePairs(pairs) {
|
|
1454
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1455
|
+
const out = [];
|
|
1456
|
+
for (const p of pairs) {
|
|
1457
|
+
const key = `${p.type}:${p.number}`;
|
|
1458
|
+
if (seen.has(key)) continue;
|
|
1459
|
+
seen.add(key);
|
|
1460
|
+
out.push(p);
|
|
1461
|
+
}
|
|
1462
|
+
return out;
|
|
1463
|
+
}
|
|
1464
|
+
function ensureLabel(spec, cwd) {
|
|
1465
|
+
try {
|
|
1466
|
+
gh2(
|
|
1467
|
+
["label", "create", spec.label, "--force", "--color", "0e8a16", "--description", spec.description],
|
|
1468
|
+
{ cwd }
|
|
1469
|
+
);
|
|
1470
|
+
} catch {
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
function addLabel(number, label, cwd) {
|
|
1474
|
+
try {
|
|
1475
|
+
gh2(["issue", "edit", String(number), "--add-label", label], { cwd });
|
|
1476
|
+
} catch {
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
function formatConfirmation(current, targets, state) {
|
|
1480
|
+
const other = targets.find((t) => t.type !== current.type);
|
|
1481
|
+
const lines = [];
|
|
1482
|
+
lines.push("\u2705 **kody2 risk gates approved.**");
|
|
1483
|
+
lines.push("");
|
|
1484
|
+
lines.push(
|
|
1485
|
+
`Applied \`kody-approve:*\` labels on ${targets.map((t) => `${t.type} #${t.number}`).join(" and ")}.`
|
|
1486
|
+
);
|
|
1487
|
+
if (other && current.type === "pr") {
|
|
1488
|
+
lines.push(`Mirrored to the originating issue (#${other.number}) so the orchestrator also sees the approval.`);
|
|
1489
|
+
} else if (other && current.type === "issue") {
|
|
1490
|
+
lines.push(`Mirrored to PR #${other.number} so a pending \`fix\` primitive also passes the gate.`);
|
|
1491
|
+
}
|
|
1492
|
+
const flowName = state?.flow?.name;
|
|
1493
|
+
if (flowName) {
|
|
1494
|
+
lines.push("");
|
|
1495
|
+
lines.push(`Re-triggering the \`${flowName}\` flow now \u2014 it will resume from the existing branch/PR checkpoint.`);
|
|
1496
|
+
} else {
|
|
1497
|
+
lines.push("");
|
|
1498
|
+
lines.push("No active flow found in task state. Post `@kody2 <command>` to resume manually.");
|
|
1499
|
+
}
|
|
1500
|
+
return lines.join("\n");
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1235
1503
|
// src/scripts/buildSyntheticPlugin.ts
|
|
1236
1504
|
import * as fs8 from "fs";
|
|
1237
1505
|
import * as os2 from "os";
|
|
@@ -1326,7 +1594,7 @@ function copyDir(src, dst) {
|
|
|
1326
1594
|
}
|
|
1327
1595
|
|
|
1328
1596
|
// src/coverage.ts
|
|
1329
|
-
import { execFileSync as
|
|
1597
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
1330
1598
|
function patternToRegex(pattern) {
|
|
1331
1599
|
let s = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
1332
1600
|
s = s.replace(/\*\*\//g, "\xA7S").replace(/\*\*/g, "\xA7A").replace(/\*/g, "[^/]*");
|
|
@@ -1344,7 +1612,7 @@ function renderSiblingPath(file, requireSibling) {
|
|
|
1344
1612
|
}
|
|
1345
1613
|
function safeGit(args, cwd) {
|
|
1346
1614
|
try {
|
|
1347
|
-
return
|
|
1615
|
+
return execFileSync6("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
|
|
1348
1616
|
} catch {
|
|
1349
1617
|
return "";
|
|
1350
1618
|
}
|
|
@@ -1550,10 +1818,10 @@ function defaultLabelMap() {
|
|
|
1550
1818
|
}
|
|
1551
1819
|
|
|
1552
1820
|
// src/scripts/commitAndPush.ts
|
|
1553
|
-
import { execFileSync as
|
|
1821
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
1554
1822
|
|
|
1555
1823
|
// src/commit.ts
|
|
1556
|
-
import { execFileSync as
|
|
1824
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
1557
1825
|
import * as fs10 from "fs";
|
|
1558
1826
|
import * as path9 from "path";
|
|
1559
1827
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
@@ -1583,7 +1851,7 @@ var CONVENTIONAL_PREFIXES = [
|
|
|
1583
1851
|
];
|
|
1584
1852
|
function git(args, cwd) {
|
|
1585
1853
|
try {
|
|
1586
|
-
return
|
|
1854
|
+
return execFileSync7("git", args, {
|
|
1587
1855
|
encoding: "utf-8",
|
|
1588
1856
|
timeout: 12e4,
|
|
1589
1857
|
cwd,
|
|
@@ -1641,7 +1909,7 @@ function isForbiddenPath(p) {
|
|
|
1641
1909
|
return false;
|
|
1642
1910
|
}
|
|
1643
1911
|
function listChangedFiles(cwd) {
|
|
1644
|
-
const raw =
|
|
1912
|
+
const raw = execFileSync7("git", ["status", "--porcelain=v1", "-z"], {
|
|
1645
1913
|
encoding: "utf-8",
|
|
1646
1914
|
cwd,
|
|
1647
1915
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -1653,7 +1921,7 @@ function listChangedFiles(cwd) {
|
|
|
1653
1921
|
}
|
|
1654
1922
|
function listFilesInCommit(ref = "HEAD", cwd) {
|
|
1655
1923
|
try {
|
|
1656
|
-
const raw =
|
|
1924
|
+
const raw = execFileSync7("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
|
|
1657
1925
|
encoding: "utf-8",
|
|
1658
1926
|
cwd,
|
|
1659
1927
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -1733,7 +2001,7 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
1733
2001
|
const kind = profile.name;
|
|
1734
2002
|
if (kind === "resolve") {
|
|
1735
2003
|
try {
|
|
1736
|
-
|
|
2004
|
+
execFileSync8("git", ["add", "-A"], { cwd: ctx.cwd, env: { ...process.env, HUSKY: "0" }, stdio: "pipe" });
|
|
1737
2005
|
} catch {
|
|
1738
2006
|
}
|
|
1739
2007
|
} else {
|
|
@@ -2355,8 +2623,8 @@ var discoverQaContext = async (ctx) => {
|
|
|
2355
2623
|
};
|
|
2356
2624
|
|
|
2357
2625
|
// src/scripts/dispatch.ts
|
|
2358
|
-
import { execFileSync as
|
|
2359
|
-
var
|
|
2626
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
2627
|
+
var API_TIMEOUT_MS5 = 3e4;
|
|
2360
2628
|
var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
2361
2629
|
const next = args?.next;
|
|
2362
2630
|
if (!next) {
|
|
@@ -2378,8 +2646,8 @@ var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
|
2378
2646
|
const sub = usePr ? "pr" : "issue";
|
|
2379
2647
|
const body = `@kody2 ${next}`;
|
|
2380
2648
|
try {
|
|
2381
|
-
|
|
2382
|
-
timeout:
|
|
2649
|
+
execFileSync9("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
2650
|
+
timeout: API_TIMEOUT_MS5,
|
|
2383
2651
|
cwd: ctx.cwd,
|
|
2384
2652
|
stdio: ["ignore", "pipe", "pipe"]
|
|
2385
2653
|
});
|
|
@@ -2397,141 +2665,6 @@ function parsePr(url) {
|
|
|
2397
2665
|
return Number.isFinite(n) ? n : null;
|
|
2398
2666
|
}
|
|
2399
2667
|
|
|
2400
|
-
// src/issue.ts
|
|
2401
|
-
import { execFileSync as execFileSync8 } from "child_process";
|
|
2402
|
-
var API_TIMEOUT_MS4 = 3e4;
|
|
2403
|
-
function ghToken2() {
|
|
2404
|
-
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
2405
|
-
}
|
|
2406
|
-
function gh2(args, options) {
|
|
2407
|
-
const token = ghToken2();
|
|
2408
|
-
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
2409
|
-
return execFileSync8("gh", args, {
|
|
2410
|
-
encoding: "utf-8",
|
|
2411
|
-
timeout: API_TIMEOUT_MS4,
|
|
2412
|
-
cwd: options?.cwd,
|
|
2413
|
-
env,
|
|
2414
|
-
input: options?.input,
|
|
2415
|
-
stdio: options?.input ? ["pipe", "pipe", "pipe"] : ["inherit", "pipe", "pipe"]
|
|
2416
|
-
}).trim();
|
|
2417
|
-
}
|
|
2418
|
-
function getIssue(issueNumber, cwd) {
|
|
2419
|
-
const output = gh2(["issue", "view", String(issueNumber), "--json", "number,title,body,comments,labels"], { cwd });
|
|
2420
|
-
const parsed = JSON.parse(output);
|
|
2421
|
-
if (typeof parsed?.title !== "string") {
|
|
2422
|
-
throw new Error(`Issue #${issueNumber}: unexpected response shape`);
|
|
2423
|
-
}
|
|
2424
|
-
return {
|
|
2425
|
-
number: parsed.number ?? issueNumber,
|
|
2426
|
-
title: parsed.title,
|
|
2427
|
-
body: parsed.body ?? "",
|
|
2428
|
-
comments: (parsed.comments ?? []).map((c) => ({
|
|
2429
|
-
body: c.body ?? "",
|
|
2430
|
-
author: c.author?.login ?? "unknown",
|
|
2431
|
-
createdAt: c.createdAt ?? ""
|
|
2432
|
-
})),
|
|
2433
|
-
labels: Array.isArray(parsed.labels) ? parsed.labels.map((l) => l.name ?? "").filter((n) => n.length > 0) : []
|
|
2434
|
-
};
|
|
2435
|
-
}
|
|
2436
|
-
function stripKody2Mentions(body) {
|
|
2437
|
-
return body.replace(/(@)(kody2)/gi, "$1\u200B$2");
|
|
2438
|
-
}
|
|
2439
|
-
function postIssueComment(issueNumber, body, cwd) {
|
|
2440
|
-
try {
|
|
2441
|
-
gh2(["issue", "comment", String(issueNumber), "--body-file", "-"], { input: stripKody2Mentions(body), cwd });
|
|
2442
|
-
} catch (err) {
|
|
2443
|
-
process.stderr.write(
|
|
2444
|
-
`[kody2] failed to post comment on #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
2445
|
-
`
|
|
2446
|
-
);
|
|
2447
|
-
}
|
|
2448
|
-
}
|
|
2449
|
-
function truncate2(s, maxBytes) {
|
|
2450
|
-
if (s.length <= maxBytes) return s;
|
|
2451
|
-
return `${s.slice(0, maxBytes)}\u2026 (+${s.length - maxBytes} chars)`;
|
|
2452
|
-
}
|
|
2453
|
-
function getPr(prNumber, cwd) {
|
|
2454
|
-
const output = gh2(["pr", "view", String(prNumber), "--json", "number,title,body,headRefName,baseRefName,state"], {
|
|
2455
|
-
cwd
|
|
2456
|
-
});
|
|
2457
|
-
const parsed = JSON.parse(output);
|
|
2458
|
-
if (typeof parsed?.title !== "string") {
|
|
2459
|
-
throw new Error(`PR #${prNumber}: unexpected response shape`);
|
|
2460
|
-
}
|
|
2461
|
-
return {
|
|
2462
|
-
number: parsed.number ?? prNumber,
|
|
2463
|
-
title: parsed.title,
|
|
2464
|
-
body: parsed.body ?? "",
|
|
2465
|
-
headRefName: String(parsed.headRefName ?? ""),
|
|
2466
|
-
baseRefName: String(parsed.baseRefName ?? ""),
|
|
2467
|
-
state: String(parsed.state ?? "")
|
|
2468
|
-
};
|
|
2469
|
-
}
|
|
2470
|
-
function getPrDiff(prNumber, cwd) {
|
|
2471
|
-
try {
|
|
2472
|
-
return gh2(["pr", "diff", String(prNumber)], { cwd });
|
|
2473
|
-
} catch (err) {
|
|
2474
|
-
process.stderr.write(
|
|
2475
|
-
`[kody2] failed to fetch diff for PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
2476
|
-
`
|
|
2477
|
-
);
|
|
2478
|
-
return "";
|
|
2479
|
-
}
|
|
2480
|
-
}
|
|
2481
|
-
function getPrReviews(prNumber, cwd) {
|
|
2482
|
-
try {
|
|
2483
|
-
const output = gh2(["pr", "view", String(prNumber), "--json", "reviews"], { cwd });
|
|
2484
|
-
const parsed = JSON.parse(output);
|
|
2485
|
-
if (!Array.isArray(parsed?.reviews)) return [];
|
|
2486
|
-
return parsed.reviews.map(
|
|
2487
|
-
(r) => ({
|
|
2488
|
-
body: r.body ?? "",
|
|
2489
|
-
state: r.state ?? "",
|
|
2490
|
-
author: r.author?.login ?? "unknown",
|
|
2491
|
-
submittedAt: r.submittedAt ?? ""
|
|
2492
|
-
})
|
|
2493
|
-
);
|
|
2494
|
-
} catch {
|
|
2495
|
-
return [];
|
|
2496
|
-
}
|
|
2497
|
-
}
|
|
2498
|
-
function getPrComments(prNumber, cwd) {
|
|
2499
|
-
try {
|
|
2500
|
-
const output = gh2(["pr", "view", String(prNumber), "--json", "comments"], { cwd });
|
|
2501
|
-
const parsed = JSON.parse(output);
|
|
2502
|
-
if (!Array.isArray(parsed?.comments)) return [];
|
|
2503
|
-
return parsed.comments.map((c) => ({
|
|
2504
|
-
body: c.body ?? "",
|
|
2505
|
-
author: c.author?.login ?? "unknown",
|
|
2506
|
-
createdAt: c.createdAt ?? ""
|
|
2507
|
-
})).filter((c) => c.body.trim().length > 0);
|
|
2508
|
-
} catch {
|
|
2509
|
-
return [];
|
|
2510
|
-
}
|
|
2511
|
-
}
|
|
2512
|
-
var VERDICT_HEADING = /(^|\n)\s*#{1,6}\s*Verdict\s*:/i;
|
|
2513
|
-
function isReviewShaped(body) {
|
|
2514
|
-
return VERDICT_HEADING.test(body);
|
|
2515
|
-
}
|
|
2516
|
-
function getPrLatestReviewBody(prNumber, cwd) {
|
|
2517
|
-
const reviews = getPrReviews(prNumber, cwd).filter((r) => r.body.trim().length > 0).map((r) => ({ body: r.body, at: r.submittedAt }));
|
|
2518
|
-
const comments = getPrComments(prNumber, cwd).filter((c) => isReviewShaped(c.body)).map((c) => ({ body: c.body, at: c.createdAt }));
|
|
2519
|
-
const all = [...reviews, ...comments].sort((a, b) => (b.at || "").localeCompare(a.at || ""));
|
|
2520
|
-
if (all.length > 0) return all[0].body;
|
|
2521
|
-
const pr = getPr(prNumber, cwd);
|
|
2522
|
-
return pr.body;
|
|
2523
|
-
}
|
|
2524
|
-
function postPrReviewComment(prNumber, body, cwd) {
|
|
2525
|
-
try {
|
|
2526
|
-
gh2(["pr", "comment", String(prNumber), "--body-file", "-"], { input: stripKody2Mentions(body), cwd });
|
|
2527
|
-
} catch (err) {
|
|
2528
|
-
process.stderr.write(
|
|
2529
|
-
`[kody2] failed to post review comment on PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
2530
|
-
`
|
|
2531
|
-
);
|
|
2532
|
-
}
|
|
2533
|
-
}
|
|
2534
|
-
|
|
2535
2668
|
// src/pr.ts
|
|
2536
2669
|
var TITLE_MAX = 72;
|
|
2537
2670
|
function stripTitlePrefixes(raw) {
|
|
@@ -2697,7 +2830,7 @@ function computeFailureReason(ctx) {
|
|
|
2697
2830
|
}
|
|
2698
2831
|
|
|
2699
2832
|
// src/scripts/finishFlow.ts
|
|
2700
|
-
import { execFileSync as
|
|
2833
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
2701
2834
|
|
|
2702
2835
|
// src/registry.ts
|
|
2703
2836
|
import * as fs14 from "fs";
|
|
@@ -2816,7 +2949,7 @@ function getIssueLabels(issueNumber, cwd) {
|
|
|
2816
2949
|
return [];
|
|
2817
2950
|
}
|
|
2818
2951
|
}
|
|
2819
|
-
function
|
|
2952
|
+
function addLabel2(issueNumber, label, cwd) {
|
|
2820
2953
|
gh2(["issue", "edit", String(issueNumber), "--add-label", label], { cwd });
|
|
2821
2954
|
}
|
|
2822
2955
|
function removeLabel(issueNumber, label, cwd) {
|
|
@@ -2848,12 +2981,12 @@ function setKodyLabel(issueNumber, spec, cwd) {
|
|
|
2848
2981
|
}
|
|
2849
2982
|
}
|
|
2850
2983
|
try {
|
|
2851
|
-
|
|
2984
|
+
addLabel2(issueNumber, target, cwd);
|
|
2852
2985
|
} catch (err) {
|
|
2853
2986
|
if (looksLikeMissingLabel(err)) {
|
|
2854
2987
|
try {
|
|
2855
2988
|
createLabelInRepo(spec, cwd);
|
|
2856
|
-
|
|
2989
|
+
addLabel2(issueNumber, target, cwd);
|
|
2857
2990
|
return;
|
|
2858
2991
|
} catch (retryErr) {
|
|
2859
2992
|
process.stderr.write(
|
|
@@ -2885,7 +3018,7 @@ function errMsg(err) {
|
|
|
2885
3018
|
}
|
|
2886
3019
|
|
|
2887
3020
|
// src/scripts/finishFlow.ts
|
|
2888
|
-
var
|
|
3021
|
+
var API_TIMEOUT_MS6 = 3e4;
|
|
2889
3022
|
var STATUS_ICON = {
|
|
2890
3023
|
"review-passed": "\u2705",
|
|
2891
3024
|
"fix-applied": "\u2705",
|
|
@@ -2917,8 +3050,8 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
|
|
|
2917
3050
|
**PR:** ${state.core.prUrl}` : "";
|
|
2918
3051
|
const body = `${icon} kody2 flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
|
|
2919
3052
|
try {
|
|
2920
|
-
|
|
2921
|
-
timeout:
|
|
3053
|
+
execFileSync10("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
3054
|
+
timeout: API_TIMEOUT_MS6,
|
|
2922
3055
|
cwd: ctx.cwd,
|
|
2923
3056
|
stdio: ["ignore", "pipe", "pipe"]
|
|
2924
3057
|
});
|
|
@@ -2931,7 +3064,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
|
|
|
2931
3064
|
};
|
|
2932
3065
|
|
|
2933
3066
|
// src/branch.ts
|
|
2934
|
-
import { execFileSync as
|
|
3067
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
2935
3068
|
var UncommittedChangesError = class extends Error {
|
|
2936
3069
|
constructor(branch) {
|
|
2937
3070
|
super(`Uncommitted changes on branch '${branch}' \u2014 refusing to run to protect work in progress`);
|
|
@@ -2941,7 +3074,7 @@ var UncommittedChangesError = class extends Error {
|
|
|
2941
3074
|
branch;
|
|
2942
3075
|
};
|
|
2943
3076
|
function git2(args, cwd) {
|
|
2944
|
-
return
|
|
3077
|
+
return execFileSync11("git", args, {
|
|
2945
3078
|
encoding: "utf-8",
|
|
2946
3079
|
timeout: 3e4,
|
|
2947
3080
|
cwd,
|
|
@@ -2966,7 +3099,7 @@ function checkoutPrBranch(prNumber, cwd) {
|
|
|
2966
3099
|
SKIP_HOOKS: "1",
|
|
2967
3100
|
GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
|
|
2968
3101
|
};
|
|
2969
|
-
|
|
3102
|
+
execFileSync11("gh", ["pr", "checkout", String(prNumber)], {
|
|
2970
3103
|
cwd,
|
|
2971
3104
|
env,
|
|
2972
3105
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -3033,7 +3166,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch, cwd) {
|
|
|
3033
3166
|
}
|
|
3034
3167
|
|
|
3035
3168
|
// src/gha.ts
|
|
3036
|
-
import { execFileSync as
|
|
3169
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
3037
3170
|
import * as fs15 from "fs";
|
|
3038
3171
|
function getRunUrl() {
|
|
3039
3172
|
const server = process.env.GITHUB_SERVER_URL;
|
|
@@ -3076,7 +3209,7 @@ function reactToTriggerComment(cwd) {
|
|
|
3076
3209
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
3077
3210
|
if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
|
|
3078
3211
|
try {
|
|
3079
|
-
|
|
3212
|
+
execFileSync12("gh", args, opts);
|
|
3080
3213
|
return;
|
|
3081
3214
|
} catch (err) {
|
|
3082
3215
|
lastErr = err;
|
|
@@ -3089,13 +3222,13 @@ function reactToTriggerComment(cwd) {
|
|
|
3089
3222
|
}
|
|
3090
3223
|
function sleepMs(ms) {
|
|
3091
3224
|
try {
|
|
3092
|
-
|
|
3225
|
+
execFileSync12("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
|
|
3093
3226
|
} catch {
|
|
3094
3227
|
}
|
|
3095
3228
|
}
|
|
3096
3229
|
|
|
3097
3230
|
// src/workflow.ts
|
|
3098
|
-
import { execFileSync as
|
|
3231
|
+
import { execFileSync as execFileSync13 } from "child_process";
|
|
3099
3232
|
var GH_TIMEOUT_MS = 3e4;
|
|
3100
3233
|
function ghToken3() {
|
|
3101
3234
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
@@ -3103,7 +3236,7 @@ function ghToken3() {
|
|
|
3103
3236
|
function gh3(args, cwd) {
|
|
3104
3237
|
const token = ghToken3();
|
|
3105
3238
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
3106
|
-
return
|
|
3239
|
+
return execFileSync13("gh", args, {
|
|
3107
3240
|
encoding: "utf-8",
|
|
3108
3241
|
timeout: GH_TIMEOUT_MS,
|
|
3109
3242
|
cwd,
|
|
@@ -3287,7 +3420,7 @@ function tryPostPr2(prNumber, body, cwd) {
|
|
|
3287
3420
|
}
|
|
3288
3421
|
|
|
3289
3422
|
// src/scripts/initFlow.ts
|
|
3290
|
-
import { execFileSync as
|
|
3423
|
+
import { execFileSync as execFileSync14 } from "child_process";
|
|
3291
3424
|
import * as fs17 from "fs";
|
|
3292
3425
|
import * as path15 from "path";
|
|
3293
3426
|
|
|
@@ -3328,7 +3461,7 @@ function qualityCommandsFor(pm) {
|
|
|
3328
3461
|
function detectOwnerRepo(cwd) {
|
|
3329
3462
|
let url;
|
|
3330
3463
|
try {
|
|
3331
|
-
url =
|
|
3464
|
+
url = execFileSync14("git", ["remote", "get-url", "origin"], {
|
|
3332
3465
|
cwd,
|
|
3333
3466
|
encoding: "utf-8",
|
|
3334
3467
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3412,7 +3545,7 @@ jobs:
|
|
|
3412
3545
|
`;
|
|
3413
3546
|
function defaultBranchFromGit(cwd) {
|
|
3414
3547
|
try {
|
|
3415
|
-
const ref =
|
|
3548
|
+
const ref = execFileSync14("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
3416
3549
|
cwd,
|
|
3417
3550
|
encoding: "utf-8",
|
|
3418
3551
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3420,7 +3553,7 @@ function defaultBranchFromGit(cwd) {
|
|
|
3420
3553
|
return ref.replace("refs/remotes/origin/", "");
|
|
3421
3554
|
} catch {
|
|
3422
3555
|
try {
|
|
3423
|
-
return
|
|
3556
|
+
return execFileSync14("git", ["branch", "--show-current"], {
|
|
3424
3557
|
cwd,
|
|
3425
3558
|
encoding: "utf-8",
|
|
3426
3559
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3610,7 +3743,7 @@ var mirrorStateToPr = async (ctx) => {
|
|
|
3610
3743
|
if (!issueNumber || issueTarget !== "issue") return;
|
|
3611
3744
|
const prUrl = ctx.output.prUrl ?? ctx.data.prResult?.url;
|
|
3612
3745
|
if (!prUrl) return;
|
|
3613
|
-
const prNumber =
|
|
3746
|
+
const prNumber = parsePrNumber2(prUrl);
|
|
3614
3747
|
if (!prNumber) return;
|
|
3615
3748
|
let state;
|
|
3616
3749
|
try {
|
|
@@ -3628,7 +3761,7 @@ var mirrorStateToPr = async (ctx) => {
|
|
|
3628
3761
|
);
|
|
3629
3762
|
}
|
|
3630
3763
|
};
|
|
3631
|
-
function
|
|
3764
|
+
function parsePrNumber2(prUrl) {
|
|
3632
3765
|
const m = prUrl.match(/\/pull\/(\d+)(?:[/?#]|$)/);
|
|
3633
3766
|
if (!m) return null;
|
|
3634
3767
|
const n = parseInt(m[1], 10);
|
|
@@ -3713,8 +3846,8 @@ var persistFlowState = async (ctx) => {
|
|
|
3713
3846
|
};
|
|
3714
3847
|
|
|
3715
3848
|
// src/scripts/postClassification.ts
|
|
3716
|
-
import { execFileSync as
|
|
3717
|
-
var
|
|
3849
|
+
import { execFileSync as execFileSync15 } from "child_process";
|
|
3850
|
+
var API_TIMEOUT_MS7 = 3e4;
|
|
3718
3851
|
var VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
3719
3852
|
var postClassification = async (ctx) => {
|
|
3720
3853
|
const issueNumber = ctx.args.issue;
|
|
@@ -3743,9 +3876,9 @@ var postClassification = async (ctx) => {
|
|
|
3743
3876
|
ctx.cwd
|
|
3744
3877
|
);
|
|
3745
3878
|
try {
|
|
3746
|
-
|
|
3879
|
+
execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", `@kody2 ${classification}`], {
|
|
3747
3880
|
cwd: ctx.cwd,
|
|
3748
|
-
timeout:
|
|
3881
|
+
timeout: API_TIMEOUT_MS7,
|
|
3749
3882
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3750
3883
|
});
|
|
3751
3884
|
} catch (err) {
|
|
@@ -3777,9 +3910,9 @@ function parseClassification(prSummary) {
|
|
|
3777
3910
|
}
|
|
3778
3911
|
function tryAuditComment(issueNumber, body, cwd) {
|
|
3779
3912
|
try {
|
|
3780
|
-
|
|
3913
|
+
execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
3781
3914
|
cwd,
|
|
3782
|
-
timeout:
|
|
3915
|
+
timeout: API_TIMEOUT_MS7,
|
|
3783
3916
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3784
3917
|
});
|
|
3785
3918
|
} catch {
|
|
@@ -3961,7 +4094,7 @@ REVIEW_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.github.
|
|
|
3961
4094
|
};
|
|
3962
4095
|
|
|
3963
4096
|
// src/scripts/releaseFlow.ts
|
|
3964
|
-
import { execFileSync as
|
|
4097
|
+
import { execFileSync as execFileSync16, spawnSync } from "child_process";
|
|
3965
4098
|
import * as fs18 from "fs";
|
|
3966
4099
|
import * as path16 from "path";
|
|
3967
4100
|
function bumpVersion(current, bump) {
|
|
@@ -3991,7 +4124,7 @@ function generateChangelog(cwd, newVersion, lastTag) {
|
|
|
3991
4124
|
const range = lastTag ? `${lastTag}..HEAD` : "HEAD";
|
|
3992
4125
|
let log = "";
|
|
3993
4126
|
try {
|
|
3994
|
-
log =
|
|
4127
|
+
log = execFileSync16("git", ["log", range, "--pretty=format:%s||%h", "--no-merges"], {
|
|
3995
4128
|
cwd,
|
|
3996
4129
|
encoding: "utf-8",
|
|
3997
4130
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4048,7 +4181,7 @@ ${entry}${prior.slice(idx + 1)}`);
|
|
|
4048
4181
|
}
|
|
4049
4182
|
}
|
|
4050
4183
|
function git3(args, cwd, timeout = 6e4) {
|
|
4051
|
-
return
|
|
4184
|
+
return execFileSync16("git", args, {
|
|
4052
4185
|
encoding: "utf-8",
|
|
4053
4186
|
timeout,
|
|
4054
4187
|
cwd,
|
|
@@ -4351,7 +4484,7 @@ var resolveArtifacts = async (ctx, profile) => {
|
|
|
4351
4484
|
};
|
|
4352
4485
|
|
|
4353
4486
|
// src/scripts/resolveFlow.ts
|
|
4354
|
-
import { execFileSync as
|
|
4487
|
+
import { execFileSync as execFileSync17 } from "child_process";
|
|
4355
4488
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
4356
4489
|
var resolveFlow = async (ctx) => {
|
|
4357
4490
|
const prNumber = ctx.args.pr;
|
|
@@ -4403,7 +4536,7 @@ var resolveFlow = async (ctx) => {
|
|
|
4403
4536
|
};
|
|
4404
4537
|
function getConflictedFiles(cwd) {
|
|
4405
4538
|
try {
|
|
4406
|
-
const out =
|
|
4539
|
+
const out = execFileSync17("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
4407
4540
|
encoding: "utf-8",
|
|
4408
4541
|
cwd,
|
|
4409
4542
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -4418,7 +4551,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
4418
4551
|
let total = 0;
|
|
4419
4552
|
for (const f of files) {
|
|
4420
4553
|
try {
|
|
4421
|
-
const content =
|
|
4554
|
+
const content = execFileSync17("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
4422
4555
|
const snippet = `### ${f}
|
|
4423
4556
|
|
|
4424
4557
|
\`\`\`
|
|
@@ -4487,7 +4620,7 @@ function tryPostPr4(prNumber, body, cwd) {
|
|
|
4487
4620
|
}
|
|
4488
4621
|
|
|
4489
4622
|
// src/scripts/riskGate.ts
|
|
4490
|
-
import { execFileSync as
|
|
4623
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
4491
4624
|
var ALL_GATES = ["secrets", "workflow-edit", "large-diff", "dep-change", "test-deletion"];
|
|
4492
4625
|
var HARD_GATES = /* @__PURE__ */ new Set(["secrets"]);
|
|
4493
4626
|
var APPROVE_ALL = "kody-approve:all";
|
|
@@ -4504,7 +4637,7 @@ var riskGate = async (ctx, profile, _agent, args) => {
|
|
|
4504
4637
|
}
|
|
4505
4638
|
const targetType = ctx.data.commentTargetType;
|
|
4506
4639
|
const targetNumber = Number(ctx.data.commentTargetNumber ?? 0);
|
|
4507
|
-
const labels =
|
|
4640
|
+
const labels = collectApprovalLabels(ctx, targetType, targetNumber);
|
|
4508
4641
|
const approveAll = labels.includes(APPROVE_ALL);
|
|
4509
4642
|
const pending = violations.filter((v) => !isApproved(v, labels, approveAll));
|
|
4510
4643
|
ctx.data.riskGate = {
|
|
@@ -4528,7 +4661,8 @@ var riskGate = async (ctx, profile, _agent, args) => {
|
|
|
4528
4661
|
);
|
|
4529
4662
|
} catch {
|
|
4530
4663
|
}
|
|
4531
|
-
const
|
|
4664
|
+
const compareUrl = computeCompareUrl(ctx);
|
|
4665
|
+
const body = formatAdvisory(pending, compareUrl);
|
|
4532
4666
|
try {
|
|
4533
4667
|
if (targetType === "issue") postIssueComment(targetNumber, body, ctx.cwd);
|
|
4534
4668
|
else postPrReviewComment(targetNumber, body, ctx.cwd);
|
|
@@ -4602,6 +4736,20 @@ function evaluateGates(ctx, profileName, changedFiles, gatesToRun, args) {
|
|
|
4602
4736
|
}
|
|
4603
4737
|
return violations;
|
|
4604
4738
|
}
|
|
4739
|
+
function collectApprovalLabels(ctx, targetType, targetNumber) {
|
|
4740
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4741
|
+
if (targetNumber > 0) {
|
|
4742
|
+
for (const l of getIssueLabels(targetNumber, ctx.cwd)) seen.add(l);
|
|
4743
|
+
}
|
|
4744
|
+
if (targetType === "pr") {
|
|
4745
|
+
const state = ctx.data.taskState;
|
|
4746
|
+
const issueNum = state?.flow?.issueNumber;
|
|
4747
|
+
if (typeof issueNum === "number" && issueNum > 0 && issueNum !== targetNumber) {
|
|
4748
|
+
for (const l of getIssueLabels(issueNum, ctx.cwd)) seen.add(l);
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
return [...seen];
|
|
4752
|
+
}
|
|
4605
4753
|
function isApproved(v, labels, approveAll) {
|
|
4606
4754
|
if (labels.includes(`kody-approve:${v.name}`)) return true;
|
|
4607
4755
|
if (!HARD_GATES.has(v.name) && approveAll) return true;
|
|
@@ -4627,8 +4775,13 @@ var SECRET_PATTERNS = [
|
|
|
4627
4775
|
/\.pem$/i,
|
|
4628
4776
|
/\.key$/i,
|
|
4629
4777
|
/(^|\/)(id_rsa|id_ed25519|id_ecdsa)(\.|$)/i,
|
|
4630
|
-
|
|
4631
|
-
/
|
|
4778
|
+
// Match the keyword anywhere inside the filename, as a whole word (so e.g.
|
|
4779
|
+
// `api-secrets.json`, `config/app.credentials.yaml`, `user-passwords.txt`
|
|
4780
|
+
// all trip — while false friends like `secretary.md` do not).
|
|
4781
|
+
/(^|\/)[^/]*\bsecrets?\b[^/]*$/i,
|
|
4782
|
+
/(^|\/)[^/]*\bcredentials?\b[^/]*$/i,
|
|
4783
|
+
/(^|\/)[^/]*\bpasswords?\b[^/]*$/i,
|
|
4784
|
+
/(^|\/)[^/]*\bapi[-_]keys?\b[^/]*$/i,
|
|
4632
4785
|
/(^|\/)\.netrc$/i,
|
|
4633
4786
|
/(^|\/)\.npmrc$/i
|
|
4634
4787
|
];
|
|
@@ -4661,7 +4814,7 @@ function collectBranchChangedFiles(ctx) {
|
|
|
4661
4814
|
const base = ctx.config.git.defaultBranch;
|
|
4662
4815
|
for (const ref of [`origin/${base}...HEAD`, `${base}...HEAD`]) {
|
|
4663
4816
|
try {
|
|
4664
|
-
const out =
|
|
4817
|
+
const out = execFileSync18("git", ["diff", "--name-only", ref], {
|
|
4665
4818
|
cwd: ctx.cwd,
|
|
4666
4819
|
encoding: "utf-8",
|
|
4667
4820
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4677,7 +4830,7 @@ function computeDiffStats(ctx) {
|
|
|
4677
4830
|
const base = ctx.config.git.defaultBranch;
|
|
4678
4831
|
for (const ref of [`origin/${base}...HEAD`, `${base}...HEAD`]) {
|
|
4679
4832
|
try {
|
|
4680
|
-
const out =
|
|
4833
|
+
const out = execFileSync18("git", ["diff", "--shortstat", ref], {
|
|
4681
4834
|
cwd: ctx.cwd,
|
|
4682
4835
|
encoding: "utf-8",
|
|
4683
4836
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4698,7 +4851,7 @@ function parseShortstat(s) {
|
|
|
4698
4851
|
}
|
|
4699
4852
|
function listDeletedFilesInHeadCommit(cwd) {
|
|
4700
4853
|
try {
|
|
4701
|
-
const out =
|
|
4854
|
+
const out = execFileSync18("git", ["show", "--name-status", "--pretty=format:", "HEAD"], {
|
|
4702
4855
|
cwd,
|
|
4703
4856
|
encoding: "utf-8",
|
|
4704
4857
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -4726,28 +4879,38 @@ function ensureApproveLabel(gate, cwd) {
|
|
|
4726
4879
|
} catch {
|
|
4727
4880
|
}
|
|
4728
4881
|
}
|
|
4729
|
-
function formatAdvisory(pending) {
|
|
4882
|
+
function formatAdvisory(pending, compareUrl) {
|
|
4730
4883
|
const lines = [];
|
|
4731
4884
|
lines.push("\u23F8\uFE0F **kody2 risk gate halted the flow.**");
|
|
4732
4885
|
lines.push("");
|
|
4733
|
-
lines.push("The
|
|
4886
|
+
lines.push("The branch was pushed but **no PR was opened** \u2014 waiting for human approval:");
|
|
4734
4887
|
lines.push("");
|
|
4735
4888
|
for (const v of pending) {
|
|
4736
4889
|
lines.push(`- **\`${v.name}\`** _(${v.severity})_ \u2014 ${v.reason}`);
|
|
4737
4890
|
}
|
|
4738
4891
|
lines.push("");
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
if (pending.some((v) => v.severity === "soft")) {
|
|
4743
|
-
lines.push("- `kody-approve:all` \u2014 bypass **soft** gates at once (hard gates still require their specific label)");
|
|
4892
|
+
if (compareUrl) {
|
|
4893
|
+
lines.push(`\u{1F4CE} Review the branch diff: ${compareUrl}`);
|
|
4894
|
+
lines.push("");
|
|
4744
4895
|
}
|
|
4896
|
+
lines.push("**To approve and resume**, post a comment:");
|
|
4897
|
+
lines.push("");
|
|
4898
|
+
lines.push("> `@kody2 approve`");
|
|
4745
4899
|
lines.push("");
|
|
4746
4900
|
lines.push(
|
|
4747
|
-
"
|
|
4901
|
+
"kody2 will acknowledge all currently-pending gates (soft **and** hard), open the PR, and continue the flow from this checkpoint. No re-running the agent."
|
|
4748
4902
|
);
|
|
4749
4903
|
return lines.join("\n");
|
|
4750
4904
|
}
|
|
4905
|
+
function computeCompareUrl(ctx) {
|
|
4906
|
+
const branch = ctx.data.branch;
|
|
4907
|
+
if (!branch) return null;
|
|
4908
|
+
const owner = ctx.config.github?.owner;
|
|
4909
|
+
const repo = ctx.config.github?.repo;
|
|
4910
|
+
if (!owner || !repo) return null;
|
|
4911
|
+
const base = ctx.config.git.defaultBranch;
|
|
4912
|
+
return `https://github.com/${owner}/${repo}/compare/${base}...${branch}`;
|
|
4913
|
+
}
|
|
4751
4914
|
|
|
4752
4915
|
// src/scripts/runFlow.ts
|
|
4753
4916
|
var runFlow = async (ctx) => {
|
|
@@ -4790,7 +4953,7 @@ var saveTaskState = async (ctx, profile) => {
|
|
|
4790
4953
|
const action = ctx.data.action ?? synthesizeAction(ctx);
|
|
4791
4954
|
if (ctx.output.prUrl && !state.core.prUrl) state.core.prUrl = ctx.output.prUrl;
|
|
4792
4955
|
if (typeof ctx.data.runUrl === "string") state.core.runUrl = ctx.data.runUrl;
|
|
4793
|
-
const next = reduce(state, executable, action);
|
|
4956
|
+
const next = reduce(state, executable, action, profile.phase);
|
|
4794
4957
|
if (ctx.output.prUrl) next.core.prUrl = ctx.output.prUrl;
|
|
4795
4958
|
if (typeof ctx.data.runUrl === "string") next.core.runUrl = ctx.data.runUrl;
|
|
4796
4959
|
writeTaskState(target, number, next, ctx.cwd);
|
|
@@ -4845,8 +5008,8 @@ var skipAgent = async (ctx) => {
|
|
|
4845
5008
|
};
|
|
4846
5009
|
|
|
4847
5010
|
// src/scripts/startFlow.ts
|
|
4848
|
-
import { execFileSync as
|
|
4849
|
-
var
|
|
5011
|
+
import { execFileSync as execFileSync19 } from "child_process";
|
|
5012
|
+
var API_TIMEOUT_MS8 = 3e4;
|
|
4850
5013
|
var startFlow = async (ctx, profile, _agentResult, args) => {
|
|
4851
5014
|
const entry = args?.entry;
|
|
4852
5015
|
if (!entry) {
|
|
@@ -4879,8 +5042,8 @@ function postKody2Comment(target, issueNumber, state, next, cwd) {
|
|
|
4879
5042
|
const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
|
|
4880
5043
|
const body = `@kody2 ${next}`;
|
|
4881
5044
|
try {
|
|
4882
|
-
|
|
4883
|
-
timeout:
|
|
5045
|
+
execFileSync19("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
5046
|
+
timeout: API_TIMEOUT_MS8,
|
|
4884
5047
|
cwd,
|
|
4885
5048
|
stdio: ["ignore", "pipe", "pipe"]
|
|
4886
5049
|
});
|
|
@@ -4899,7 +5062,7 @@ function parsePr2(url) {
|
|
|
4899
5062
|
}
|
|
4900
5063
|
|
|
4901
5064
|
// src/scripts/syncFlow.ts
|
|
4902
|
-
import { execFileSync as
|
|
5065
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
4903
5066
|
var syncFlow = async (ctx) => {
|
|
4904
5067
|
ctx.skipAgent = true;
|
|
4905
5068
|
const prNumber = ctx.args.pr;
|
|
@@ -4958,7 +5121,7 @@ function bail2(ctx, prNumber, reason) {
|
|
|
4958
5121
|
}
|
|
4959
5122
|
function revParseHead(cwd) {
|
|
4960
5123
|
try {
|
|
4961
|
-
return
|
|
5124
|
+
return execFileSync20("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
4962
5125
|
} catch {
|
|
4963
5126
|
return "";
|
|
4964
5127
|
}
|
|
@@ -4966,9 +5129,9 @@ function revParseHead(cwd) {
|
|
|
4966
5129
|
function pushBranch(branch, cwd) {
|
|
4967
5130
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
4968
5131
|
try {
|
|
4969
|
-
|
|
5132
|
+
execFileSync20("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
4970
5133
|
} catch {
|
|
4971
|
-
|
|
5134
|
+
execFileSync20("git", ["push", "--force-with-lease", "-u", "origin", branch], {
|
|
4972
5135
|
cwd,
|
|
4973
5136
|
env,
|
|
4974
5137
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5211,7 +5374,8 @@ var postflightScripts = {
|
|
|
5211
5374
|
advanceFlow,
|
|
5212
5375
|
persistFlowState,
|
|
5213
5376
|
postClassification,
|
|
5214
|
-
riskGate
|
|
5377
|
+
riskGate,
|
|
5378
|
+
applyApprovals
|
|
5215
5379
|
};
|
|
5216
5380
|
var allScriptNames = /* @__PURE__ */ new Set([
|
|
5217
5381
|
...Object.keys(preflightScripts),
|
|
@@ -5219,7 +5383,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
5219
5383
|
]);
|
|
5220
5384
|
|
|
5221
5385
|
// src/tools.ts
|
|
5222
|
-
import { execFileSync as
|
|
5386
|
+
import { execFileSync as execFileSync21 } from "child_process";
|
|
5223
5387
|
function verifyCliTools(tools, cwd) {
|
|
5224
5388
|
const out = [];
|
|
5225
5389
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -5252,7 +5416,7 @@ function verifyOne(tool, cwd) {
|
|
|
5252
5416
|
}
|
|
5253
5417
|
function runShell2(cmd, cwd, timeoutMs = 3e4) {
|
|
5254
5418
|
try {
|
|
5255
|
-
|
|
5419
|
+
execFileSync21("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
5256
5420
|
return true;
|
|
5257
5421
|
} catch {
|
|
5258
5422
|
return false;
|
|
@@ -5582,7 +5746,7 @@ function detectPackageManager2(cwd) {
|
|
|
5582
5746
|
}
|
|
5583
5747
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
5584
5748
|
try {
|
|
5585
|
-
|
|
5749
|
+
execFileSync22(cmd, args, {
|
|
5586
5750
|
cwd,
|
|
5587
5751
|
stdio: stream ? "inherit" : "pipe",
|
|
5588
5752
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -5595,7 +5759,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
5595
5759
|
}
|
|
5596
5760
|
function isOnPath(bin) {
|
|
5597
5761
|
try {
|
|
5598
|
-
|
|
5762
|
+
execFileSync22("which", [bin], { stdio: "pipe" });
|
|
5599
5763
|
return true;
|
|
5600
5764
|
} catch {
|
|
5601
5765
|
return false;
|
|
@@ -5629,7 +5793,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
5629
5793
|
} catch {
|
|
5630
5794
|
}
|
|
5631
5795
|
try {
|
|
5632
|
-
|
|
5796
|
+
execFileSync22("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
5633
5797
|
process.stdout.write("\u2192 kody2: litellm already installed\n");
|
|
5634
5798
|
return 0;
|
|
5635
5799
|
} catch {
|
|
@@ -5639,16 +5803,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
5639
5803
|
}
|
|
5640
5804
|
function configureGitIdentity(cwd) {
|
|
5641
5805
|
try {
|
|
5642
|
-
const name =
|
|
5806
|
+
const name = execFileSync22("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
5643
5807
|
if (name) return;
|
|
5644
5808
|
} catch {
|
|
5645
5809
|
}
|
|
5646
5810
|
try {
|
|
5647
|
-
|
|
5811
|
+
execFileSync22("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
5648
5812
|
} catch {
|
|
5649
5813
|
}
|
|
5650
5814
|
try {
|
|
5651
|
-
|
|
5815
|
+
execFileSync22("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
5652
5816
|
cwd,
|
|
5653
5817
|
stdio: "pipe"
|
|
5654
5818
|
});
|
|
@@ -5825,9 +5989,9 @@ function commitChatFiles(cwd, sessionId, verbose) {
|
|
|
5825
5989
|
if (paths.length === 0) return;
|
|
5826
5990
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
5827
5991
|
try {
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5992
|
+
execFileSync23("git", ["add", ...paths], opts);
|
|
5993
|
+
execFileSync23("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
5994
|
+
execFileSync23("git", ["push", "--quiet", "origin", "HEAD"], opts);
|
|
5831
5995
|
} catch (err) {
|
|
5832
5996
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5833
5997
|
process.stderr.write(`[kody2:chat] commit/push skipped: ${msg}
|