@kody-ade/kody-engine 0.4.31 → 0.4.33
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 +143 -17
- package/dist/executables/run/profile.json +1 -1
- 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.33",
|
|
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();
|
|
@@ -5304,20 +5328,37 @@ function ensurePr(opts) {
|
|
|
5304
5328
|
}
|
|
5305
5329
|
|
|
5306
5330
|
// src/scripts/ensurePr.ts
|
|
5331
|
+
function setOutcome(ctx, outcome) {
|
|
5332
|
+
ctx.data.prResult = outcome;
|
|
5333
|
+
if (outcome.kind === "created" || outcome.kind === "updated") {
|
|
5334
|
+
ctx.output.prUrl = outcome.url;
|
|
5335
|
+
}
|
|
5336
|
+
}
|
|
5307
5337
|
var ensurePr2 = async (ctx) => {
|
|
5308
5338
|
if (ctx.skipAgent && ctx.output.exitCode !== void 0) {
|
|
5339
|
+
setOutcome(ctx, { kind: "skipped", reason: "preflight short-circuited (skipAgent)" });
|
|
5309
5340
|
return;
|
|
5310
5341
|
}
|
|
5311
5342
|
const commitResult = ctx.data.commitResult;
|
|
5312
5343
|
const hasCommits = Boolean(ctx.data.hasCommitsAhead);
|
|
5313
5344
|
if (!commitResult?.committed && !hasCommits) {
|
|
5345
|
+
setOutcome(ctx, { kind: "skipped", reason: "no commits to ship" });
|
|
5314
5346
|
return;
|
|
5315
5347
|
}
|
|
5316
5348
|
if (commitResult?.committed && commitResult.pushed === false) {
|
|
5349
|
+
setOutcome(ctx, { kind: "skipped", reason: "local commit succeeded but push failed" });
|
|
5350
|
+
return;
|
|
5351
|
+
}
|
|
5352
|
+
if (ctx.data.verifyOk === false) {
|
|
5353
|
+
const reason = `verify failed: ${ctx.data.verifyReason ?? "unknown"}`;
|
|
5354
|
+
setOutcome(ctx, { kind: "skipped", reason });
|
|
5317
5355
|
return;
|
|
5318
5356
|
}
|
|
5319
5357
|
const branch = ctx.data.branch;
|
|
5320
|
-
if (!branch)
|
|
5358
|
+
if (!branch) {
|
|
5359
|
+
setOutcome(ctx, { kind: "skipped", reason: "no branch context (ctx.data.branch missing)" });
|
|
5360
|
+
return;
|
|
5361
|
+
}
|
|
5321
5362
|
const failureReason = computeFailureReason(ctx);
|
|
5322
5363
|
const isFailure = failureReason.length > 0;
|
|
5323
5364
|
const changedFiles = ctx.data.changedFiles ?? [];
|
|
@@ -5339,13 +5380,26 @@ var ensurePr2 = async (ctx) => {
|
|
|
5339
5380
|
baseBranch,
|
|
5340
5381
|
cwd: ctx.cwd
|
|
5341
5382
|
});
|
|
5342
|
-
|
|
5343
|
-
|
|
5383
|
+
if (!result.url || result.url.trim().length === 0) {
|
|
5384
|
+
const reason = `gh pr create returned empty URL (action=${result.action}); refusing to claim success`;
|
|
5385
|
+
ctx.data.prCrashReason = reason;
|
|
5386
|
+
ctx.output.exitCode = 4;
|
|
5387
|
+
ctx.output.reason = reason;
|
|
5388
|
+
setOutcome(ctx, { kind: "crashed", reason });
|
|
5389
|
+
return;
|
|
5390
|
+
}
|
|
5391
|
+
setOutcome(ctx, {
|
|
5392
|
+
kind: result.action === "created" ? "created" : "updated",
|
|
5393
|
+
url: result.url,
|
|
5394
|
+
number: result.number,
|
|
5395
|
+
draft: result.draft
|
|
5396
|
+
});
|
|
5344
5397
|
} catch (err) {
|
|
5345
5398
|
const reason = `PR creation failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
5346
5399
|
ctx.data.prCrashReason = reason;
|
|
5347
5400
|
ctx.output.exitCode = 4;
|
|
5348
5401
|
ctx.output.reason = reason;
|
|
5402
|
+
setOutcome(ctx, { kind: "crashed", reason });
|
|
5349
5403
|
}
|
|
5350
5404
|
};
|
|
5351
5405
|
function computeFailureReason(ctx) {
|
|
@@ -7235,6 +7289,22 @@ var persistFlowState = async (ctx) => {
|
|
|
7235
7289
|
}
|
|
7236
7290
|
};
|
|
7237
7291
|
|
|
7292
|
+
// src/scripts/prOutcome.ts
|
|
7293
|
+
function readPrOutcome(data) {
|
|
7294
|
+
const raw = data.prResult;
|
|
7295
|
+
if (!raw || typeof raw !== "object") return null;
|
|
7296
|
+
const r = raw;
|
|
7297
|
+
switch (r.kind) {
|
|
7298
|
+
case "created":
|
|
7299
|
+
case "updated":
|
|
7300
|
+
case "skipped":
|
|
7301
|
+
case "crashed":
|
|
7302
|
+
return raw;
|
|
7303
|
+
default:
|
|
7304
|
+
return null;
|
|
7305
|
+
}
|
|
7306
|
+
}
|
|
7307
|
+
|
|
7238
7308
|
// src/scripts/postIssueComment.ts
|
|
7239
7309
|
var FAILED_LABEL_SPEC = {
|
|
7240
7310
|
label: "kody:failed",
|
|
@@ -7248,8 +7318,7 @@ var postIssueComment2 = async (ctx) => {
|
|
|
7248
7318
|
if (!targetType || !targetNumber) return;
|
|
7249
7319
|
const commitResult = ctx.data.commitResult;
|
|
7250
7320
|
const hasCommits = Boolean(ctx.data.hasCommitsAhead);
|
|
7251
|
-
const
|
|
7252
|
-
const prAction = ctx.data.prResult?.action;
|
|
7321
|
+
const prResult = readPrOutcome(ctx.data);
|
|
7253
7322
|
if (!commitResult?.committed && !hasCommits) {
|
|
7254
7323
|
const specific = computeFailureReason2(ctx);
|
|
7255
7324
|
const reason = specific.length > 0 ? specific : "no changes to commit";
|
|
@@ -7267,18 +7336,17 @@ var postIssueComment2 = async (ctx) => {
|
|
|
7267
7336
|
}
|
|
7268
7337
|
const failureReason = computeFailureReason2(ctx);
|
|
7269
7338
|
const isFailure = failureReason.length > 0;
|
|
7270
|
-
const justPushedToExistingPr = prAction === "updated" && commitResult?.committed === true;
|
|
7271
|
-
const successMsg = justPushedToExistingPr ? `\u2705 kody pushed to ${prUrl}` : prAction === "updated" ? `\u2139\uFE0F kody made no changes \u2014 PR: ${prUrl}` : `\u2705 kody PR opened: ${prUrl}`;
|
|
7272
7339
|
const branch = ctx.data.branch;
|
|
7273
|
-
const
|
|
7274
|
-
|
|
7275
|
-
|
|
7340
|
+
const msg = renderMessage({
|
|
7341
|
+
prResult,
|
|
7342
|
+
isFailure,
|
|
7343
|
+
failureReason,
|
|
7344
|
+
justPushedToExistingPr: prResult?.kind === "updated" && commitResult?.committed === true,
|
|
7276
7345
|
branch,
|
|
7277
7346
|
branchPushed: commitResult?.committed === true,
|
|
7278
7347
|
githubOwner: ctx.config.github?.owner,
|
|
7279
7348
|
githubRepo: ctx.config.github?.repo
|
|
7280
7349
|
});
|
|
7281
|
-
const msg = isFailure ? `\u26A0\uFE0F kody FAILED: ${truncate2(failureReason, 1500)}${failurePrSuffix}` : successMsg;
|
|
7282
7350
|
postWith(targetType, targetNumber, msg, ctx.cwd);
|
|
7283
7351
|
let exitCode = 0;
|
|
7284
7352
|
const agentDone = Boolean(ctx.data.agentDone);
|
|
@@ -7302,12 +7370,29 @@ function markRunFailed(ctx) {
|
|
|
7302
7370
|
}
|
|
7303
7371
|
}
|
|
7304
7372
|
function computeFailureSuffix(input) {
|
|
7305
|
-
if (input.
|
|
7306
|
-
|
|
7307
|
-
}
|
|
7373
|
+
if (input.prResult?.kind === "created") return ` \u2014 draft PR: ${input.prResult.url}`;
|
|
7374
|
+
if (input.prResult?.kind === "updated") return ` \u2014 PR: ${input.prResult.url}`;
|
|
7308
7375
|
if (!input.branchPushed || !input.branch || !input.githubOwner || !input.githubRepo) return "";
|
|
7309
7376
|
return ` \u2014 branch: https://github.com/${input.githubOwner}/${input.githubRepo}/tree/${input.branch}`;
|
|
7310
7377
|
}
|
|
7378
|
+
function renderMessage(input) {
|
|
7379
|
+
const suffix = computeFailureSuffix(input);
|
|
7380
|
+
if (input.isFailure) {
|
|
7381
|
+
return `\u26A0\uFE0F kody FAILED: ${truncate2(input.failureReason, 1500)}${suffix}`;
|
|
7382
|
+
}
|
|
7383
|
+
switch (input.prResult?.kind) {
|
|
7384
|
+
case "created":
|
|
7385
|
+
return `\u2705 kody PR opened: ${input.prResult.url}`;
|
|
7386
|
+
case "updated":
|
|
7387
|
+
return input.justPushedToExistingPr ? `\u2705 kody pushed to ${input.prResult.url}` : `\u2139\uFE0F kody made no changes \u2014 PR: ${input.prResult.url}`;
|
|
7388
|
+
case "skipped":
|
|
7389
|
+
return `\u26A0\uFE0F kody finished but did not open a PR \u2014 ${input.prResult.reason}${suffix}`;
|
|
7390
|
+
case "crashed":
|
|
7391
|
+
return `\u26A0\uFE0F kody PR step crashed: ${truncate2(input.prResult.reason, 1500)}${suffix}`;
|
|
7392
|
+
case void 0:
|
|
7393
|
+
return `\u26A0\uFE0F kody finished but PR step did not run${suffix}`;
|
|
7394
|
+
}
|
|
7395
|
+
}
|
|
7311
7396
|
function computeFailureReason2(ctx) {
|
|
7312
7397
|
const misses = ctx.data.coverageMisses ?? [];
|
|
7313
7398
|
if (misses.length > 0) return `missing tests: ${misses.map((m) => m.expectedTest).join(", ")}`;
|
|
@@ -7560,12 +7645,35 @@ var resolveFlow = async (ctx) => {
|
|
|
7560
7645
|
ctx.data.pr = pr;
|
|
7561
7646
|
ctx.data.commentTargetType = "pr";
|
|
7562
7647
|
ctx.data.commentTargetNumber = prNumber;
|
|
7563
|
-
checkoutPrBranch(prNumber, ctx.cwd);
|
|
7564
|
-
ctx.data.branch = getCurrentBranch(ctx.cwd);
|
|
7565
7648
|
const baseBranch = pr.baseRefName || ctx.config.git.defaultBranch;
|
|
7566
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);
|
|
7567
7667
|
const mergeStatus = mergeBase(baseBranch, ctx.cwd);
|
|
7568
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
|
+
}
|
|
7569
7677
|
ctx.output.exitCode = 0;
|
|
7570
7678
|
ctx.output.reason = `already up to date with origin/${baseBranch} \u2014 nothing to resolve`;
|
|
7571
7679
|
ctx.skipAgent = true;
|
|
@@ -7652,6 +7760,24 @@ function tryPostPr3(prNumber, body, cwd) {
|
|
|
7652
7760
|
} catch {
|
|
7653
7761
|
}
|
|
7654
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
|
+
}
|
|
7655
7781
|
|
|
7656
7782
|
// src/deployments.ts
|
|
7657
7783
|
function findPreviewDeploymentUrl(prNumber, cwd) {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
{ "script": "checkCoverageWithRetry" },
|
|
66
66
|
{ "script": "abortUnfinishedGitOps" },
|
|
67
67
|
{ "script": "commitAndPush" },
|
|
68
|
-
{ "script": "ensurePr"
|
|
68
|
+
{ "script": "ensurePr" },
|
|
69
69
|
{ "script": "postIssueComment" },
|
|
70
70
|
{ "script": "writeRunSummary" },
|
|
71
71
|
{ "script": "saveTaskState" },
|
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.33",
|
|
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",
|