@kody-ade/kody-engine 0.2.60 → 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.60",
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 execFileSync23 } from "child_process";
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 execFileSync22 } from "child_process";
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,265 +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 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
-
1503
1241
  // src/scripts/buildSyntheticPlugin.ts
1504
1242
  import * as fs8 from "fs";
1505
1243
  import * as os2 from "os";
@@ -1594,7 +1332,7 @@ function copyDir(src, dst) {
1594
1332
  }
1595
1333
 
1596
1334
  // src/coverage.ts
1597
- import { execFileSync as execFileSync6 } from "child_process";
1335
+ import { execFileSync as execFileSync4 } from "child_process";
1598
1336
  function patternToRegex(pattern) {
1599
1337
  let s = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
1600
1338
  s = s.replace(/\*\*\//g, "\xA7S").replace(/\*\*/g, "\xA7A").replace(/\*/g, "[^/]*");
@@ -1612,7 +1350,7 @@ function renderSiblingPath(file, requireSibling) {
1612
1350
  }
1613
1351
  function safeGit(args, cwd) {
1614
1352
  try {
1615
- return execFileSync6("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
1353
+ return execFileSync4("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
1616
1354
  } catch {
1617
1355
  return "";
1618
1356
  }
@@ -1818,10 +1556,10 @@ function defaultLabelMap() {
1818
1556
  }
1819
1557
 
1820
1558
  // src/scripts/commitAndPush.ts
1821
- import { execFileSync as execFileSync8 } from "child_process";
1559
+ import { execFileSync as execFileSync6 } from "child_process";
1822
1560
 
1823
1561
  // src/commit.ts
1824
- import { execFileSync as execFileSync7 } from "child_process";
1562
+ import { execFileSync as execFileSync5 } from "child_process";
1825
1563
  import * as fs10 from "fs";
1826
1564
  import * as path9 from "path";
1827
1565
  var FORBIDDEN_PATH_PREFIXES = [
@@ -1851,7 +1589,7 @@ var CONVENTIONAL_PREFIXES = [
1851
1589
  ];
1852
1590
  function git(args, cwd) {
1853
1591
  try {
1854
- return execFileSync7("git", args, {
1592
+ return execFileSync5("git", args, {
1855
1593
  encoding: "utf-8",
1856
1594
  timeout: 12e4,
1857
1595
  cwd,
@@ -1909,7 +1647,7 @@ function isForbiddenPath(p) {
1909
1647
  return false;
1910
1648
  }
1911
1649
  function listChangedFiles(cwd) {
1912
- const raw = execFileSync7("git", ["status", "--porcelain=v1", "-z"], {
1650
+ const raw = execFileSync5("git", ["status", "--porcelain=v1", "-z"], {
1913
1651
  encoding: "utf-8",
1914
1652
  cwd,
1915
1653
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
@@ -1921,7 +1659,7 @@ function listChangedFiles(cwd) {
1921
1659
  }
1922
1660
  function listFilesInCommit(ref = "HEAD", cwd) {
1923
1661
  try {
1924
- const raw = execFileSync7("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
1662
+ const raw = execFileSync5("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
1925
1663
  encoding: "utf-8",
1926
1664
  cwd,
1927
1665
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
@@ -2001,7 +1739,7 @@ var commitAndPush2 = async (ctx, profile) => {
2001
1739
  const kind = profile.name;
2002
1740
  if (kind === "resolve") {
2003
1741
  try {
2004
- execFileSync8("git", ["add", "-A"], { cwd: ctx.cwd, env: { ...process.env, HUSKY: "0" }, stdio: "pipe" });
1742
+ execFileSync6("git", ["add", "-A"], { cwd: ctx.cwd, env: { ...process.env, HUSKY: "0" }, stdio: "pipe" });
2005
1743
  } catch {
2006
1744
  }
2007
1745
  } else {
@@ -2623,8 +2361,8 @@ var discoverQaContext = async (ctx) => {
2623
2361
  };
2624
2362
 
2625
2363
  // src/scripts/dispatch.ts
2626
- import { execFileSync as execFileSync9 } from "child_process";
2627
- var API_TIMEOUT_MS5 = 3e4;
2364
+ import { execFileSync as execFileSync7 } from "child_process";
2365
+ var API_TIMEOUT_MS3 = 3e4;
2628
2366
  var dispatch = async (ctx, _profile, _agentResult, args) => {
2629
2367
  const next = args?.next;
2630
2368
  if (!next) {
@@ -2646,8 +2384,8 @@ var dispatch = async (ctx, _profile, _agentResult, args) => {
2646
2384
  const sub = usePr ? "pr" : "issue";
2647
2385
  const body = `@kody2 ${next}`;
2648
2386
  try {
2649
- execFileSync9("gh", [sub, "comment", String(targetNumber), "--body", body], {
2650
- timeout: API_TIMEOUT_MS5,
2387
+ execFileSync7("gh", [sub, "comment", String(targetNumber), "--body", body], {
2388
+ timeout: API_TIMEOUT_MS3,
2651
2389
  cwd: ctx.cwd,
2652
2390
  stdio: ["ignore", "pipe", "pipe"]
2653
2391
  });
@@ -2665,6 +2403,141 @@ function parsePr(url) {
2665
2403
  return Number.isFinite(n) ? n : null;
2666
2404
  }
2667
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
+
2668
2541
  // src/pr.ts
2669
2542
  var TITLE_MAX = 72;
2670
2543
  function stripTitlePrefixes(raw) {
@@ -2830,7 +2703,7 @@ function computeFailureReason(ctx) {
2830
2703
  }
2831
2704
 
2832
2705
  // src/scripts/finishFlow.ts
2833
- import { execFileSync as execFileSync10 } from "child_process";
2706
+ import { execFileSync as execFileSync9 } from "child_process";
2834
2707
 
2835
2708
  // src/registry.ts
2836
2709
  import * as fs14 from "fs";
@@ -2949,7 +2822,7 @@ function getIssueLabels(issueNumber, cwd) {
2949
2822
  return [];
2950
2823
  }
2951
2824
  }
2952
- function addLabel2(issueNumber, label, cwd) {
2825
+ function addLabel(issueNumber, label, cwd) {
2953
2826
  gh2(["issue", "edit", String(issueNumber), "--add-label", label], { cwd });
2954
2827
  }
2955
2828
  function removeLabel(issueNumber, label, cwd) {
@@ -2981,12 +2854,12 @@ function setKodyLabel(issueNumber, spec, cwd) {
2981
2854
  }
2982
2855
  }
2983
2856
  try {
2984
- addLabel2(issueNumber, target, cwd);
2857
+ addLabel(issueNumber, target, cwd);
2985
2858
  } catch (err) {
2986
2859
  if (looksLikeMissingLabel(err)) {
2987
2860
  try {
2988
2861
  createLabelInRepo(spec, cwd);
2989
- addLabel2(issueNumber, target, cwd);
2862
+ addLabel(issueNumber, target, cwd);
2990
2863
  return;
2991
2864
  } catch (retryErr) {
2992
2865
  process.stderr.write(
@@ -3018,7 +2891,7 @@ function errMsg(err) {
3018
2891
  }
3019
2892
 
3020
2893
  // src/scripts/finishFlow.ts
3021
- var API_TIMEOUT_MS6 = 3e4;
2894
+ var API_TIMEOUT_MS5 = 3e4;
3022
2895
  var STATUS_ICON = {
3023
2896
  "review-passed": "\u2705",
3024
2897
  "fix-applied": "\u2705",
@@ -3050,8 +2923,8 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
3050
2923
  **PR:** ${state.core.prUrl}` : "";
3051
2924
  const body = `${icon} kody2 flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
3052
2925
  try {
3053
- execFileSync10("gh", ["issue", "comment", String(issueNumber), "--body", body], {
3054
- timeout: API_TIMEOUT_MS6,
2926
+ execFileSync9("gh", ["issue", "comment", String(issueNumber), "--body", body], {
2927
+ timeout: API_TIMEOUT_MS5,
3055
2928
  cwd: ctx.cwd,
3056
2929
  stdio: ["ignore", "pipe", "pipe"]
3057
2930
  });
@@ -3064,7 +2937,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
3064
2937
  };
3065
2938
 
3066
2939
  // src/branch.ts
3067
- import { execFileSync as execFileSync11 } from "child_process";
2940
+ import { execFileSync as execFileSync10 } from "child_process";
3068
2941
  var UncommittedChangesError = class extends Error {
3069
2942
  constructor(branch) {
3070
2943
  super(`Uncommitted changes on branch '${branch}' \u2014 refusing to run to protect work in progress`);
@@ -3074,7 +2947,7 @@ var UncommittedChangesError = class extends Error {
3074
2947
  branch;
3075
2948
  };
3076
2949
  function git2(args, cwd) {
3077
- return execFileSync11("git", args, {
2950
+ return execFileSync10("git", args, {
3078
2951
  encoding: "utf-8",
3079
2952
  timeout: 3e4,
3080
2953
  cwd,
@@ -3099,7 +2972,7 @@ function checkoutPrBranch(prNumber, cwd) {
3099
2972
  SKIP_HOOKS: "1",
3100
2973
  GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
3101
2974
  };
3102
- execFileSync11("gh", ["pr", "checkout", String(prNumber)], {
2975
+ execFileSync10("gh", ["pr", "checkout", String(prNumber)], {
3103
2976
  cwd,
3104
2977
  env,
3105
2978
  stdio: ["ignore", "pipe", "pipe"],
@@ -3166,7 +3039,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch, cwd) {
3166
3039
  }
3167
3040
 
3168
3041
  // src/gha.ts
3169
- import { execFileSync as execFileSync12 } from "child_process";
3042
+ import { execFileSync as execFileSync11 } from "child_process";
3170
3043
  import * as fs15 from "fs";
3171
3044
  function getRunUrl() {
3172
3045
  const server = process.env.GITHUB_SERVER_URL;
@@ -3209,7 +3082,7 @@ function reactToTriggerComment(cwd) {
3209
3082
  for (let attempt = 0; attempt < 3; attempt++) {
3210
3083
  if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
3211
3084
  try {
3212
- execFileSync12("gh", args, opts);
3085
+ execFileSync11("gh", args, opts);
3213
3086
  return;
3214
3087
  } catch (err) {
3215
3088
  lastErr = err;
@@ -3222,13 +3095,13 @@ function reactToTriggerComment(cwd) {
3222
3095
  }
3223
3096
  function sleepMs(ms) {
3224
3097
  try {
3225
- execFileSync12("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
3098
+ execFileSync11("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
3226
3099
  } catch {
3227
3100
  }
3228
3101
  }
3229
3102
 
3230
3103
  // src/workflow.ts
3231
- import { execFileSync as execFileSync13 } from "child_process";
3104
+ import { execFileSync as execFileSync12 } from "child_process";
3232
3105
  var GH_TIMEOUT_MS = 3e4;
3233
3106
  function ghToken3() {
3234
3107
  return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
@@ -3236,7 +3109,7 @@ function ghToken3() {
3236
3109
  function gh3(args, cwd) {
3237
3110
  const token = ghToken3();
3238
3111
  const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
3239
- return execFileSync13("gh", args, {
3112
+ return execFileSync12("gh", args, {
3240
3113
  encoding: "utf-8",
3241
3114
  timeout: GH_TIMEOUT_MS,
3242
3115
  cwd,
@@ -3420,7 +3293,7 @@ function tryPostPr2(prNumber, body, cwd) {
3420
3293
  }
3421
3294
 
3422
3295
  // src/scripts/initFlow.ts
3423
- import { execFileSync as execFileSync14 } from "child_process";
3296
+ import { execFileSync as execFileSync13 } from "child_process";
3424
3297
  import * as fs17 from "fs";
3425
3298
  import * as path15 from "path";
3426
3299
 
@@ -3461,7 +3334,7 @@ function qualityCommandsFor(pm) {
3461
3334
  function detectOwnerRepo(cwd) {
3462
3335
  let url;
3463
3336
  try {
3464
- url = execFileSync14("git", ["remote", "get-url", "origin"], {
3337
+ url = execFileSync13("git", ["remote", "get-url", "origin"], {
3465
3338
  cwd,
3466
3339
  encoding: "utf-8",
3467
3340
  stdio: ["ignore", "pipe", "pipe"]
@@ -3545,7 +3418,7 @@ jobs:
3545
3418
  `;
3546
3419
  function defaultBranchFromGit(cwd) {
3547
3420
  try {
3548
- const ref = execFileSync14("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
3421
+ const ref = execFileSync13("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
3549
3422
  cwd,
3550
3423
  encoding: "utf-8",
3551
3424
  stdio: ["ignore", "pipe", "pipe"]
@@ -3553,7 +3426,7 @@ function defaultBranchFromGit(cwd) {
3553
3426
  return ref.replace("refs/remotes/origin/", "");
3554
3427
  } catch {
3555
3428
  try {
3556
- return execFileSync14("git", ["branch", "--show-current"], {
3429
+ return execFileSync13("git", ["branch", "--show-current"], {
3557
3430
  cwd,
3558
3431
  encoding: "utf-8",
3559
3432
  stdio: ["ignore", "pipe", "pipe"]
@@ -3743,7 +3616,7 @@ var mirrorStateToPr = async (ctx) => {
3743
3616
  if (!issueNumber || issueTarget !== "issue") return;
3744
3617
  const prUrl = ctx.output.prUrl ?? ctx.data.prResult?.url;
3745
3618
  if (!prUrl) return;
3746
- const prNumber = parsePrNumber2(prUrl);
3619
+ const prNumber = parsePrNumber(prUrl);
3747
3620
  if (!prNumber) return;
3748
3621
  let state;
3749
3622
  try {
@@ -3761,7 +3634,7 @@ var mirrorStateToPr = async (ctx) => {
3761
3634
  );
3762
3635
  }
3763
3636
  };
3764
- function parsePrNumber2(prUrl) {
3637
+ function parsePrNumber(prUrl) {
3765
3638
  const m = prUrl.match(/\/pull\/(\d+)(?:[/?#]|$)/);
3766
3639
  if (!m) return null;
3767
3640
  const n = parseInt(m[1], 10);
@@ -3846,8 +3719,8 @@ var persistFlowState = async (ctx) => {
3846
3719
  };
3847
3720
 
3848
3721
  // src/scripts/postClassification.ts
3849
- import { execFileSync as execFileSync15 } from "child_process";
3850
- var API_TIMEOUT_MS7 = 3e4;
3722
+ import { execFileSync as execFileSync14 } from "child_process";
3723
+ var API_TIMEOUT_MS6 = 3e4;
3851
3724
  var VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
3852
3725
  var postClassification = async (ctx) => {
3853
3726
  const issueNumber = ctx.args.issue;
@@ -3876,9 +3749,9 @@ var postClassification = async (ctx) => {
3876
3749
  ctx.cwd
3877
3750
  );
3878
3751
  try {
3879
- execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", `@kody2 ${classification}`], {
3752
+ execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", `@kody2 ${classification}`], {
3880
3753
  cwd: ctx.cwd,
3881
- timeout: API_TIMEOUT_MS7,
3754
+ timeout: API_TIMEOUT_MS6,
3882
3755
  stdio: ["ignore", "pipe", "pipe"]
3883
3756
  });
3884
3757
  } catch (err) {
@@ -3910,9 +3783,9 @@ function parseClassification(prSummary) {
3910
3783
  }
3911
3784
  function tryAuditComment(issueNumber, body, cwd) {
3912
3785
  try {
3913
- execFileSync15("gh", ["issue", "comment", String(issueNumber), "--body", body], {
3786
+ execFileSync14("gh", ["issue", "comment", String(issueNumber), "--body", body], {
3914
3787
  cwd,
3915
- timeout: API_TIMEOUT_MS7,
3788
+ timeout: API_TIMEOUT_MS6,
3916
3789
  stdio: ["ignore", "pipe", "pipe"]
3917
3790
  });
3918
3791
  } catch {
@@ -4094,7 +3967,7 @@ REVIEW_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.github.
4094
3967
  };
4095
3968
 
4096
3969
  // src/scripts/releaseFlow.ts
4097
- import { execFileSync as execFileSync16, spawnSync } from "child_process";
3970
+ import { execFileSync as execFileSync15, spawnSync } from "child_process";
4098
3971
  import * as fs18 from "fs";
4099
3972
  import * as path16 from "path";
4100
3973
  function bumpVersion(current, bump) {
@@ -4124,7 +3997,7 @@ function generateChangelog(cwd, newVersion, lastTag) {
4124
3997
  const range = lastTag ? `${lastTag}..HEAD` : "HEAD";
4125
3998
  let log = "";
4126
3999
  try {
4127
- log = execFileSync16("git", ["log", range, "--pretty=format:%s||%h", "--no-merges"], {
4000
+ log = execFileSync15("git", ["log", range, "--pretty=format:%s||%h", "--no-merges"], {
4128
4001
  cwd,
4129
4002
  encoding: "utf-8",
4130
4003
  stdio: ["ignore", "pipe", "pipe"]
@@ -4181,7 +4054,7 @@ ${entry}${prior.slice(idx + 1)}`);
4181
4054
  }
4182
4055
  }
4183
4056
  function git3(args, cwd, timeout = 6e4) {
4184
- return execFileSync16("git", args, {
4057
+ return execFileSync15("git", args, {
4185
4058
  encoding: "utf-8",
4186
4059
  timeout,
4187
4060
  cwd,
@@ -4484,7 +4357,7 @@ var resolveArtifacts = async (ctx, profile) => {
4484
4357
  };
4485
4358
 
4486
4359
  // src/scripts/resolveFlow.ts
4487
- import { execFileSync as execFileSync17 } from "child_process";
4360
+ import { execFileSync as execFileSync16 } from "child_process";
4488
4361
  var CONFLICT_DIFF_MAX_BYTES = 4e4;
4489
4362
  var resolveFlow = async (ctx) => {
4490
4363
  const prNumber = ctx.args.pr;
@@ -4536,7 +4409,7 @@ var resolveFlow = async (ctx) => {
4536
4409
  };
4537
4410
  function getConflictedFiles(cwd) {
4538
4411
  try {
4539
- const out = execFileSync17("git", ["diff", "--name-only", "--diff-filter=U"], {
4412
+ const out = execFileSync16("git", ["diff", "--name-only", "--diff-filter=U"], {
4540
4413
  encoding: "utf-8",
4541
4414
  cwd,
4542
4415
  env: { ...process.env, HUSKY: "0" }
@@ -4551,7 +4424,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
4551
4424
  let total = 0;
4552
4425
  for (const f of files) {
4553
4426
  try {
4554
- const content = execFileSync17("cat", [f], { encoding: "utf-8", cwd }).toString();
4427
+ const content = execFileSync16("cat", [f], { encoding: "utf-8", cwd }).toString();
4555
4428
  const snippet = `### ${f}
4556
4429
 
4557
4430
  \`\`\`
@@ -4619,299 +4492,6 @@ function tryPostPr4(prNumber, body, cwd) {
4619
4492
  }
4620
4493
  }
4621
4494
 
4622
- // src/scripts/riskGate.ts
4623
- import { execFileSync as execFileSync18 } from "child_process";
4624
- var ALL_GATES = ["secrets", "workflow-edit", "large-diff", "dep-change", "test-deletion"];
4625
- var HARD_GATES = /* @__PURE__ */ new Set(["secrets"]);
4626
- var APPROVE_ALL = "kody-approve:all";
4627
- var GATED_LABEL = "kody:gated";
4628
- var DEFAULT_MAX_FILES = 20;
4629
- var DEFAULT_MAX_DELETIONS = 500;
4630
- var riskGate = async (ctx, profile, _agent, args) => {
4631
- const changedFiles = collectBranchChangedFiles(ctx);
4632
- const gatesToRun = parseGates(args?.gates);
4633
- const violations = evaluateGates(ctx, profile.name, changedFiles, gatesToRun, args);
4634
- if (violations.length === 0) {
4635
- ctx.data.riskGate = { violations: [], pending: [], decision: "allow" };
4636
- return;
4637
- }
4638
- const targetType = ctx.data.commentTargetType;
4639
- const targetNumber = Number(ctx.data.commentTargetNumber ?? 0);
4640
- const labels = collectApprovalLabels(ctx, targetType, targetNumber);
4641
- const approveAll = labels.includes(APPROVE_ALL);
4642
- const pending = violations.filter((v) => !isApproved(v, labels, approveAll));
4643
- ctx.data.riskGate = {
4644
- violations,
4645
- pending,
4646
- decision: pending.length === 0 ? "allow" : "halt"
4647
- };
4648
- if (pending.length === 0 || !targetType || targetNumber <= 0) return;
4649
- for (const v of pending) {
4650
- ensureApproveLabel(v.name, ctx.cwd);
4651
- }
4652
- try {
4653
- setKodyLabel(
4654
- targetNumber,
4655
- {
4656
- label: GATED_LABEL,
4657
- color: "fbca04",
4658
- description: "kody2: awaiting human approval of risk gate(s)"
4659
- },
4660
- ctx.cwd
4661
- );
4662
- } catch {
4663
- }
4664
- const compareUrl = computeCompareUrl(ctx);
4665
- const body = formatAdvisory(pending, compareUrl);
4666
- try {
4667
- if (targetType === "issue") postIssueComment(targetNumber, body, ctx.cwd);
4668
- else postPrReviewComment(targetNumber, body, ctx.cwd);
4669
- } catch {
4670
- }
4671
- if (!ctx.output.reason) {
4672
- ctx.output.reason = `risk gate halt: ${pending.map((p) => p.name).join(", ")}`;
4673
- }
4674
- };
4675
- function evaluateGates(ctx, profileName, changedFiles, gatesToRun, args) {
4676
- const violations = [];
4677
- if (gatesToRun.includes("secrets")) {
4678
- const hits = changedFiles.filter(isSecretPath);
4679
- if (hits.length > 0) {
4680
- violations.push({
4681
- name: "secrets",
4682
- severity: "hard",
4683
- reason: `secret/credential files touched: ${preview(hits)}`
4684
- });
4685
- }
4686
- }
4687
- if (gatesToRun.includes("workflow-edit")) {
4688
- const hits = changedFiles.filter((f) => f.startsWith(".github/workflows/"));
4689
- if (hits.length > 0) {
4690
- violations.push({
4691
- name: "workflow-edit",
4692
- severity: "soft",
4693
- reason: `CI workflow files modified: ${preview(hits)}`
4694
- });
4695
- }
4696
- }
4697
- if (gatesToRun.includes("large-diff")) {
4698
- const maxFiles = toPositiveInt(args?.maxFiles, DEFAULT_MAX_FILES);
4699
- const maxDeletions = toPositiveInt(args?.maxDeletions, DEFAULT_MAX_DELETIONS);
4700
- if (changedFiles.length > maxFiles) {
4701
- violations.push({
4702
- name: "large-diff",
4703
- severity: "soft",
4704
- reason: `${changedFiles.length} files changed (threshold: ${maxFiles})`
4705
- });
4706
- } else {
4707
- const stats = computeDiffStats(ctx);
4708
- if (stats && stats.deletions > maxDeletions) {
4709
- violations.push({
4710
- name: "large-diff",
4711
- severity: "soft",
4712
- reason: `${stats.deletions} lines deleted (threshold: ${maxDeletions})`
4713
- });
4714
- }
4715
- }
4716
- }
4717
- if (gatesToRun.includes("dep-change") && profileName !== "chore") {
4718
- const hits = changedFiles.filter(isDepFile);
4719
- if (hits.length > 0) {
4720
- violations.push({
4721
- name: "dep-change",
4722
- severity: "soft",
4723
- reason: `dependency/lockfile changes outside a chore flow: ${preview(hits)}`
4724
- });
4725
- }
4726
- }
4727
- if (gatesToRun.includes("test-deletion")) {
4728
- const deleted = listDeletedFilesInHeadCommit(ctx.cwd).filter(isTestFile);
4729
- if (deleted.length > 0) {
4730
- violations.push({
4731
- name: "test-deletion",
4732
- severity: "soft",
4733
- reason: `test files deleted: ${preview(deleted)}`
4734
- });
4735
- }
4736
- }
4737
- return violations;
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
- }
4753
- function isApproved(v, labels, approveAll) {
4754
- if (labels.includes(`kody-approve:${v.name}`)) return true;
4755
- if (!HARD_GATES.has(v.name) && approveAll) return true;
4756
- return false;
4757
- }
4758
- function parseGates(spec) {
4759
- if (spec === void 0 || spec === null || spec === "") return ALL_GATES;
4760
- const list = String(spec).split(",").map((s) => s.trim()).filter(Boolean);
4761
- const valid = ALL_GATES;
4762
- const matched = list.filter((n) => valid.includes(n));
4763
- return matched.length > 0 ? matched : ALL_GATES;
4764
- }
4765
- function toPositiveInt(v, fallback) {
4766
- const n = typeof v === "number" ? v : parseInt(String(v ?? ""), 10);
4767
- return Number.isFinite(n) && n > 0 ? n : fallback;
4768
- }
4769
- function preview(list, max = 5) {
4770
- if (list.length <= max) return list.join(", ");
4771
- return `${list.slice(0, max).join(", ")} (+${list.length - max} more)`;
4772
- }
4773
- var SECRET_PATTERNS = [
4774
- /(^|\/)\.env(\.|$)/i,
4775
- /\.pem$/i,
4776
- /\.key$/i,
4777
- /(^|\/)(id_rsa|id_ed25519|id_ecdsa)(\.|$)/i,
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,
4785
- /(^|\/)\.netrc$/i,
4786
- /(^|\/)\.npmrc$/i
4787
- ];
4788
- function isSecretPath(p) {
4789
- return SECRET_PATTERNS.some((r) => r.test(p));
4790
- }
4791
- var DEP_FILES = /* @__PURE__ */ new Set([
4792
- "package.json",
4793
- "pnpm-lock.yaml",
4794
- "package-lock.json",
4795
- "yarn.lock",
4796
- "requirements.txt",
4797
- "Pipfile",
4798
- "Pipfile.lock",
4799
- "poetry.lock",
4800
- "go.mod",
4801
- "go.sum",
4802
- "Cargo.toml",
4803
- "Cargo.lock",
4804
- "Gemfile",
4805
- "Gemfile.lock"
4806
- ]);
4807
- function isDepFile(p) {
4808
- return DEP_FILES.has(p.split("/").pop() ?? "");
4809
- }
4810
- function isTestFile(p) {
4811
- return /(^|\/)(tests?|__tests__|spec)\//i.test(p) || /\.(test|spec)\.[a-z0-9]+$/i.test(p);
4812
- }
4813
- function collectBranchChangedFiles(ctx) {
4814
- const base = ctx.config.git.defaultBranch;
4815
- for (const ref of [`origin/${base}...HEAD`, `${base}...HEAD`]) {
4816
- try {
4817
- const out = execFileSync18("git", ["diff", "--name-only", ref], {
4818
- cwd: ctx.cwd,
4819
- encoding: "utf-8",
4820
- stdio: ["pipe", "pipe", "pipe"]
4821
- });
4822
- const files = out.split("\n").map((s) => s.trim()).filter(Boolean);
4823
- if (files.length > 0) return files;
4824
- } catch {
4825
- }
4826
- }
4827
- return ctx.data.changedFiles ?? [];
4828
- }
4829
- function computeDiffStats(ctx) {
4830
- const base = ctx.config.git.defaultBranch;
4831
- for (const ref of [`origin/${base}...HEAD`, `${base}...HEAD`]) {
4832
- try {
4833
- const out = execFileSync18("git", ["diff", "--shortstat", ref], {
4834
- cwd: ctx.cwd,
4835
- encoding: "utf-8",
4836
- stdio: ["pipe", "pipe", "pipe"]
4837
- }).trim();
4838
- if (out) return parseShortstat(out);
4839
- } catch {
4840
- }
4841
- }
4842
- return null;
4843
- }
4844
- function parseShortstat(s) {
4845
- const ins = /(\d+)\s+insertions?/.exec(s);
4846
- const del = /(\d+)\s+deletions?/.exec(s);
4847
- return {
4848
- insertions: ins ? parseInt(ins[1], 10) : 0,
4849
- deletions: del ? parseInt(del[1], 10) : 0
4850
- };
4851
- }
4852
- function listDeletedFilesInHeadCommit(cwd) {
4853
- try {
4854
- const out = execFileSync18("git", ["show", "--name-status", "--pretty=format:", "HEAD"], {
4855
- cwd,
4856
- encoding: "utf-8",
4857
- stdio: ["pipe", "pipe", "pipe"]
4858
- });
4859
- return out.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("D ")).map((l) => l.slice(2).trim()).filter(Boolean);
4860
- } catch {
4861
- return [];
4862
- }
4863
- }
4864
- function ensureApproveLabel(gate, cwd) {
4865
- try {
4866
- gh2(
4867
- [
4868
- "label",
4869
- "create",
4870
- `kody-approve:${gate}`,
4871
- "--force",
4872
- "--color",
4873
- "0e8a16",
4874
- "--description",
4875
- `kody2: approve the ${gate} risk gate and resume the flow`
4876
- ],
4877
- { cwd }
4878
- );
4879
- } catch {
4880
- }
4881
- }
4882
- function formatAdvisory(pending, compareUrl) {
4883
- const lines = [];
4884
- lines.push("\u23F8\uFE0F **kody2 risk gate halted the flow.**");
4885
- lines.push("");
4886
- lines.push("The branch was pushed but **no PR was opened** \u2014 waiting for human approval:");
4887
- lines.push("");
4888
- for (const v of pending) {
4889
- lines.push(`- **\`${v.name}\`** _(${v.severity})_ \u2014 ${v.reason}`);
4890
- }
4891
- lines.push("");
4892
- if (compareUrl) {
4893
- lines.push(`\u{1F4CE} Review the branch diff: ${compareUrl}`);
4894
- lines.push("");
4895
- }
4896
- lines.push("**To approve and resume**, post a comment:");
4897
- lines.push("");
4898
- lines.push("> `@kody2 approve`");
4899
- lines.push("");
4900
- lines.push(
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."
4902
- );
4903
- return lines.join("\n");
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
- }
4914
-
4915
4495
  // src/scripts/runFlow.ts
4916
4496
  var runFlow = async (ctx) => {
4917
4497
  const issueNumber = ctx.args.issue;
@@ -5008,8 +4588,8 @@ var skipAgent = async (ctx) => {
5008
4588
  };
5009
4589
 
5010
4590
  // src/scripts/startFlow.ts
5011
- import { execFileSync as execFileSync19 } from "child_process";
5012
- var API_TIMEOUT_MS8 = 3e4;
4591
+ import { execFileSync as execFileSync17 } from "child_process";
4592
+ var API_TIMEOUT_MS7 = 3e4;
5013
4593
  var startFlow = async (ctx, profile, _agentResult, args) => {
5014
4594
  const entry = args?.entry;
5015
4595
  if (!entry) {
@@ -5042,8 +4622,8 @@ function postKody2Comment(target, issueNumber, state, next, cwd) {
5042
4622
  const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
5043
4623
  const body = `@kody2 ${next}`;
5044
4624
  try {
5045
- execFileSync19("gh", [sub, "comment", String(targetNumber), "--body", body], {
5046
- timeout: API_TIMEOUT_MS8,
4625
+ execFileSync17("gh", [sub, "comment", String(targetNumber), "--body", body], {
4626
+ timeout: API_TIMEOUT_MS7,
5047
4627
  cwd,
5048
4628
  stdio: ["ignore", "pipe", "pipe"]
5049
4629
  });
@@ -5062,7 +4642,7 @@ function parsePr2(url) {
5062
4642
  }
5063
4643
 
5064
4644
  // src/scripts/syncFlow.ts
5065
- import { execFileSync as execFileSync20 } from "child_process";
4645
+ import { execFileSync as execFileSync18 } from "child_process";
5066
4646
  var syncFlow = async (ctx) => {
5067
4647
  ctx.skipAgent = true;
5068
4648
  const prNumber = ctx.args.pr;
@@ -5121,7 +4701,7 @@ function bail2(ctx, prNumber, reason) {
5121
4701
  }
5122
4702
  function revParseHead(cwd) {
5123
4703
  try {
5124
- return execFileSync20("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
4704
+ return execFileSync18("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
5125
4705
  } catch {
5126
4706
  return "";
5127
4707
  }
@@ -5129,9 +4709,9 @@ function revParseHead(cwd) {
5129
4709
  function pushBranch(branch, cwd) {
5130
4710
  const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
5131
4711
  try {
5132
- execFileSync20("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
4712
+ execFileSync18("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
5133
4713
  } catch {
5134
- execFileSync20("git", ["push", "--force-with-lease", "-u", "origin", branch], {
4714
+ execFileSync18("git", ["push", "--force-with-lease", "-u", "origin", branch], {
5135
4715
  cwd,
5136
4716
  env,
5137
4717
  stdio: ["ignore", "pipe", "pipe"]
@@ -5373,9 +4953,7 @@ var postflightScripts = {
5373
4953
  finishFlow,
5374
4954
  advanceFlow,
5375
4955
  persistFlowState,
5376
- postClassification,
5377
- riskGate,
5378
- applyApprovals
4956
+ postClassification
5379
4957
  };
5380
4958
  var allScriptNames = /* @__PURE__ */ new Set([
5381
4959
  ...Object.keys(preflightScripts),
@@ -5383,7 +4961,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
5383
4961
  ]);
5384
4962
 
5385
4963
  // src/tools.ts
5386
- import { execFileSync as execFileSync21 } from "child_process";
4964
+ import { execFileSync as execFileSync19 } from "child_process";
5387
4965
  function verifyCliTools(tools, cwd) {
5388
4966
  const out = [];
5389
4967
  for (const t of tools) out.push(verifyOne(t, cwd));
@@ -5416,7 +4994,7 @@ function verifyOne(tool, cwd) {
5416
4994
  }
5417
4995
  function runShell2(cmd, cwd, timeoutMs = 3e4) {
5418
4996
  try {
5419
- execFileSync21("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
4997
+ execFileSync19("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
5420
4998
  return true;
5421
4999
  } catch {
5422
5000
  return false;
@@ -5746,7 +5324,7 @@ function detectPackageManager2(cwd) {
5746
5324
  }
5747
5325
  function shellOut(cmd, args, cwd, stream = true) {
5748
5326
  try {
5749
- execFileSync22(cmd, args, {
5327
+ execFileSync20(cmd, args, {
5750
5328
  cwd,
5751
5329
  stdio: stream ? "inherit" : "pipe",
5752
5330
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
@@ -5759,7 +5337,7 @@ function shellOut(cmd, args, cwd, stream = true) {
5759
5337
  }
5760
5338
  function isOnPath(bin) {
5761
5339
  try {
5762
- execFileSync22("which", [bin], { stdio: "pipe" });
5340
+ execFileSync20("which", [bin], { stdio: "pipe" });
5763
5341
  return true;
5764
5342
  } catch {
5765
5343
  return false;
@@ -5793,7 +5371,7 @@ function installLitellmIfNeeded(cwd) {
5793
5371
  } catch {
5794
5372
  }
5795
5373
  try {
5796
- execFileSync22("python3", ["-c", "import litellm"], { stdio: "pipe" });
5374
+ execFileSync20("python3", ["-c", "import litellm"], { stdio: "pipe" });
5797
5375
  process.stdout.write("\u2192 kody2: litellm already installed\n");
5798
5376
  return 0;
5799
5377
  } catch {
@@ -5803,16 +5381,16 @@ function installLitellmIfNeeded(cwd) {
5803
5381
  }
5804
5382
  function configureGitIdentity(cwd) {
5805
5383
  try {
5806
- const name = execFileSync22("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
5384
+ const name = execFileSync20("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
5807
5385
  if (name) return;
5808
5386
  } catch {
5809
5387
  }
5810
5388
  try {
5811
- execFileSync22("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
5389
+ execFileSync20("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
5812
5390
  } catch {
5813
5391
  }
5814
5392
  try {
5815
- execFileSync22("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
5393
+ execFileSync20("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
5816
5394
  cwd,
5817
5395
  stdio: "pipe"
5818
5396
  });
@@ -5989,9 +5567,9 @@ function commitChatFiles(cwd, sessionId, verbose) {
5989
5567
  if (paths.length === 0) return;
5990
5568
  const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
5991
5569
  try {
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);
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);
5995
5573
  } catch (err) {
5996
5574
  const msg = err instanceof Error ? err.message : String(err);
5997
5575
  process.stderr.write(`[kody2:chat] commit/push skipped: ${msg}