@kody-ade/kody-engine 0.4.32 → 0.4.34
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/kody.js +106 -4
- package/package.json +1 -1
package/dist/bin/kody.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.4.
|
|
6
|
+
version: "0.4.34",
|
|
7
7
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
8
8
|
license: "MIT",
|
|
9
9
|
type: "module",
|
|
@@ -5169,6 +5169,30 @@ var ensureLifecycleLabels = async (ctx) => {
|
|
|
5169
5169
|
};
|
|
5170
5170
|
|
|
5171
5171
|
// src/pr.ts
|
|
5172
|
+
function prMergeStatus(prNumber, cwd) {
|
|
5173
|
+
try {
|
|
5174
|
+
const out = gh(
|
|
5175
|
+
["pr", "view", String(prNumber), "--json", "mergeable,mergeStateStatus"],
|
|
5176
|
+
{ cwd }
|
|
5177
|
+
);
|
|
5178
|
+
const parsed = JSON.parse(out);
|
|
5179
|
+
const mergeable = parsed.mergeable ?? "UNKNOWN";
|
|
5180
|
+
const mergeStateStatus = parsed.mergeStateStatus ?? "UNKNOWN";
|
|
5181
|
+
return { status: classifyMergeStatus(mergeable, mergeStateStatus), mergeable, mergeStateStatus };
|
|
5182
|
+
} catch {
|
|
5183
|
+
return { status: "ERROR", mergeable: "", mergeStateStatus: "" };
|
|
5184
|
+
}
|
|
5185
|
+
}
|
|
5186
|
+
function classifyMergeStatus(mergeable, mergeStateStatus) {
|
|
5187
|
+
if (mergeable === "CONFLICTING") return "CONFLICTING";
|
|
5188
|
+
if (mergeable === "UNKNOWN") return "UNKNOWN";
|
|
5189
|
+
if (mergeable === "MERGEABLE") {
|
|
5190
|
+
if (mergeStateStatus === "CLEAN") return "MERGEABLE";
|
|
5191
|
+
if (mergeStateStatus === "DIRTY") return "CONFLICTING";
|
|
5192
|
+
return "BLOCKED";
|
|
5193
|
+
}
|
|
5194
|
+
return "UNKNOWN";
|
|
5195
|
+
}
|
|
5172
5196
|
var TITLE_MAX = 72;
|
|
5173
5197
|
function stripTitlePrefixes(raw) {
|
|
5174
5198
|
let s = raw.trim();
|
|
@@ -7621,12 +7645,35 @@ var resolveFlow = async (ctx) => {
|
|
|
7621
7645
|
ctx.data.pr = pr;
|
|
7622
7646
|
ctx.data.commentTargetType = "pr";
|
|
7623
7647
|
ctx.data.commentTargetNumber = prNumber;
|
|
7624
|
-
checkoutPrBranch(prNumber, ctx.cwd);
|
|
7625
|
-
ctx.data.branch = getCurrentBranch(ctx.cwd);
|
|
7626
7648
|
const baseBranch = pr.baseRefName || ctx.config.git.defaultBranch;
|
|
7627
7649
|
ctx.data.baseBranch = baseBranch;
|
|
7650
|
+
const ghStatus = prMergeStatus(prNumber, ctx.cwd);
|
|
7651
|
+
if (ghStatus.status === "MERGEABLE") {
|
|
7652
|
+
ctx.output.exitCode = 0;
|
|
7653
|
+
ctx.output.reason = `PR #${prNumber} is mergeable (no conflicts) \u2014 nothing to resolve`;
|
|
7654
|
+
ctx.skipAgent = true;
|
|
7655
|
+
tryPostPr3(prNumber, `\u2139\uFE0F kody resolve: ${ctx.output.reason}`, ctx.cwd);
|
|
7656
|
+
return;
|
|
7657
|
+
}
|
|
7658
|
+
if (ghStatus.status === "BLOCKED") {
|
|
7659
|
+
ctx.output.exitCode = 0;
|
|
7660
|
+
ctx.output.reason = `PR #${prNumber} is mergeable but blocked by checks/reviews (mergeStateStatus=${ghStatus.mergeStateStatus}) \u2014 nothing for resolve to do`;
|
|
7661
|
+
ctx.skipAgent = true;
|
|
7662
|
+
tryPostPr3(prNumber, `\u2139\uFE0F kody resolve: ${ctx.output.reason}`, ctx.cwd);
|
|
7663
|
+
return;
|
|
7664
|
+
}
|
|
7665
|
+
checkoutPrBranch(prNumber, ctx.cwd);
|
|
7666
|
+
ctx.data.branch = getCurrentBranch(ctx.cwd);
|
|
7628
7667
|
const mergeStatus = mergeBase(baseBranch, ctx.cwd);
|
|
7629
7668
|
if (mergeStatus === "clean") {
|
|
7669
|
+
if (ghStatus.status === "CONFLICTING") {
|
|
7670
|
+
const pushed = pushEmptyCommit(ctx.data.branch, ctx.cwd);
|
|
7671
|
+
ctx.output.exitCode = 0;
|
|
7672
|
+
ctx.output.reason = pushed ? `local merge clean despite GitHub reporting CONFLICTING \u2014 pushed empty commit to force re-evaluation` : `local merge clean despite GitHub reporting CONFLICTING \u2014 couldn't refresh GitHub cache (push failed)`;
|
|
7673
|
+
ctx.skipAgent = true;
|
|
7674
|
+
tryPostPr3(prNumber, `\u2139\uFE0F kody resolve: ${ctx.output.reason}`, ctx.cwd);
|
|
7675
|
+
return;
|
|
7676
|
+
}
|
|
7630
7677
|
ctx.output.exitCode = 0;
|
|
7631
7678
|
ctx.output.reason = `already up to date with origin/${baseBranch} \u2014 nothing to resolve`;
|
|
7632
7679
|
ctx.skipAgent = true;
|
|
@@ -7713,6 +7760,24 @@ function tryPostPr3(prNumber, body, cwd) {
|
|
|
7713
7760
|
} catch {
|
|
7714
7761
|
}
|
|
7715
7762
|
}
|
|
7763
|
+
function pushEmptyCommit(branch, cwd) {
|
|
7764
|
+
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
7765
|
+
try {
|
|
7766
|
+
execFileSync22(
|
|
7767
|
+
"git",
|
|
7768
|
+
["commit", "--allow-empty", "-m", "chore: kody resolve refresh \u2014 empty commit to recompute mergeable status"],
|
|
7769
|
+
{ cwd, env, stdio: ["ignore", "pipe", "pipe"] }
|
|
7770
|
+
);
|
|
7771
|
+
execFileSync22("git", ["push", "-u", "origin", branch], {
|
|
7772
|
+
cwd,
|
|
7773
|
+
env,
|
|
7774
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
7775
|
+
});
|
|
7776
|
+
return true;
|
|
7777
|
+
} catch {
|
|
7778
|
+
return false;
|
|
7779
|
+
}
|
|
7780
|
+
}
|
|
7716
7781
|
|
|
7717
7782
|
// src/deployments.ts
|
|
7718
7783
|
function findPreviewDeploymentUrl(prNumber, cwd) {
|
|
@@ -8449,6 +8514,7 @@ function tryPostPr6(prNumber, body, cwd) {
|
|
|
8449
8514
|
import { spawn as spawn2 } from "child_process";
|
|
8450
8515
|
var TAIL_CHARS = 4e3;
|
|
8451
8516
|
var COMMAND_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
8517
|
+
var DEFAULT_TEST_RETRIES = 2;
|
|
8452
8518
|
function runCommand(command, cwd) {
|
|
8453
8519
|
return new Promise((resolve4) => {
|
|
8454
8520
|
const start = Date.now();
|
|
@@ -8502,6 +8568,28 @@ async function verifyAll(config, cwd) {
|
|
|
8502
8568
|
}
|
|
8503
8569
|
return { ok: failed.length === 0, failed, details };
|
|
8504
8570
|
}
|
|
8571
|
+
async function applyTestRetries(initial, testCommand, cwd, runner, testRetries = DEFAULT_TEST_RETRIES) {
|
|
8572
|
+
if (initial.ok) return { ...initial, recovered: [] };
|
|
8573
|
+
const recovered = [];
|
|
8574
|
+
const details = { ...initial.details };
|
|
8575
|
+
let failed = [...initial.failed];
|
|
8576
|
+
if (failed.includes("test") && testCommand && testRetries > 0) {
|
|
8577
|
+
for (let attempt = 1; attempt <= testRetries; attempt++) {
|
|
8578
|
+
const retry = await runner(testCommand, cwd);
|
|
8579
|
+
details[`test (retry ${attempt})`] = retry;
|
|
8580
|
+
if (retry.exitCode === 0) {
|
|
8581
|
+
failed = failed.filter((f) => f !== "test");
|
|
8582
|
+
recovered.push("test");
|
|
8583
|
+
break;
|
|
8584
|
+
}
|
|
8585
|
+
}
|
|
8586
|
+
}
|
|
8587
|
+
return { ok: failed.length === 0, failed, details, recovered };
|
|
8588
|
+
}
|
|
8589
|
+
async function verifyAllWithRetry(config, cwd, opts) {
|
|
8590
|
+
const initial = await verifyAll(config, cwd);
|
|
8591
|
+
return applyTestRetries(initial, config.quality.testUnit, cwd, runCommand, opts?.testRetries);
|
|
8592
|
+
}
|
|
8505
8593
|
var ANSI_RE = /\x1B\[[0-?]*[ -/]*[@-~]/g;
|
|
8506
8594
|
function stripAnsi(s) {
|
|
8507
8595
|
return s.replace(ANSI_RE, "");
|
|
@@ -8514,6 +8602,13 @@ function summarizeFailure(result) {
|
|
|
8514
8602
|
lines.push(`
|
|
8515
8603
|
--- ${name} (exit ${d.exitCode}, ${(d.durationMs / 1e3).toFixed(1)}s) ---`);
|
|
8516
8604
|
lines.push(stripAnsi(d.tail));
|
|
8605
|
+
for (let attempt = 1; ; attempt++) {
|
|
8606
|
+
const retry = result.details[`${name} (retry ${attempt})`];
|
|
8607
|
+
if (!retry) break;
|
|
8608
|
+
lines.push(`
|
|
8609
|
+
--- ${name} (retry ${attempt}: exit ${retry.exitCode}, ${(retry.durationMs / 1e3).toFixed(1)}s) ---`);
|
|
8610
|
+
lines.push(stripAnsi(retry.tail));
|
|
8611
|
+
}
|
|
8517
8612
|
}
|
|
8518
8613
|
return lines.join("\n");
|
|
8519
8614
|
}
|
|
@@ -8521,9 +8616,16 @@ function summarizeFailure(result) {
|
|
|
8521
8616
|
// src/scripts/verify.ts
|
|
8522
8617
|
var verify = async (ctx) => {
|
|
8523
8618
|
try {
|
|
8524
|
-
const result = await
|
|
8619
|
+
const result = await verifyAllWithRetry(ctx.config, ctx.cwd);
|
|
8525
8620
|
ctx.data.verifyOk = result.ok;
|
|
8526
8621
|
ctx.data.verifyReason = result.ok ? "" : summarizeFailure(result);
|
|
8622
|
+
ctx.data.verifyRecovered = result.recovered ?? [];
|
|
8623
|
+
if (result.recovered && result.recovered.length > 0) {
|
|
8624
|
+
process.stderr.write(
|
|
8625
|
+
`[kody verify] caught flake on: ${result.recovered.join(", ")} (passed on retry)
|
|
8626
|
+
`
|
|
8627
|
+
);
|
|
8628
|
+
}
|
|
8527
8629
|
} catch (err) {
|
|
8528
8630
|
ctx.data.verifyOk = false;
|
|
8529
8631
|
ctx.data.verifyReason = `verify crashed: ${err instanceof Error ? err.message : String(err)}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.34",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|