@kody-ade/kody-engine 0.3.31 → 0.3.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 +170 -27
- package/dist/executables/fix/profile.json +1 -0
- package/dist/executables/fix-ci/profile.json +1 -0
- package/dist/executables/release/profile.json +13 -2
- package/dist/executables/release-deploy/deploy.sh +22 -1
- package/dist/executables/release-prepare/prepare.sh +18 -0
- package/dist/executables/sync/profile.json +5 -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.3.
|
|
6
|
+
version: "0.3.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",
|
|
@@ -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
|
|
|
@@ -577,7 +577,7 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
577
577
|
}
|
|
578
578
|
|
|
579
579
|
// src/kody-cli.ts
|
|
580
|
-
import { execFileSync as
|
|
580
|
+
import { execFileSync as execFileSync22 } from "child_process";
|
|
581
581
|
import * as fs21 from "fs";
|
|
582
582
|
import * as path18 from "path";
|
|
583
583
|
|
|
@@ -4967,8 +4967,8 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
4967
4967
|
|
|
4968
4968
|
// src/scripts/syncFlow.ts
|
|
4969
4969
|
import { execFileSync as execFileSync19 } from "child_process";
|
|
4970
|
-
var syncFlow = async (ctx) => {
|
|
4971
|
-
|
|
4970
|
+
var syncFlow = async (ctx, _profile, args) => {
|
|
4971
|
+
const announceOnSuccess = Boolean(args?.announceOnSuccess);
|
|
4972
4972
|
const prNumber = ctx.args.pr;
|
|
4973
4973
|
const pr = getPr(prNumber, ctx.cwd);
|
|
4974
4974
|
if (pr.state !== "OPEN") {
|
|
@@ -4976,8 +4976,10 @@ var syncFlow = async (ctx) => {
|
|
|
4976
4976
|
return;
|
|
4977
4977
|
}
|
|
4978
4978
|
ctx.data.pr = pr;
|
|
4979
|
-
|
|
4980
|
-
|
|
4979
|
+
if (announceOnSuccess) {
|
|
4980
|
+
ctx.data.commentTargetType = "pr";
|
|
4981
|
+
ctx.data.commentTargetNumber = prNumber;
|
|
4982
|
+
}
|
|
4981
4983
|
checkoutPrBranch(prNumber, ctx.cwd);
|
|
4982
4984
|
ctx.data.branch = getCurrentBranch(ctx.cwd);
|
|
4983
4985
|
const baseBranch = pr.baseRefName || ctx.config.git.defaultBranch;
|
|
@@ -4998,9 +5000,12 @@ var syncFlow = async (ctx) => {
|
|
|
4998
5000
|
}
|
|
4999
5001
|
const headAfter = revParseHead(ctx.cwd);
|
|
5000
5002
|
if (headAfter === headBefore) {
|
|
5001
|
-
ctx.
|
|
5002
|
-
|
|
5003
|
-
|
|
5003
|
+
ctx.data.syncResult = "noop";
|
|
5004
|
+
if (announceOnSuccess) {
|
|
5005
|
+
ctx.output.exitCode = 0;
|
|
5006
|
+
ctx.output.reason = `already up to date with origin/${baseBranch}`;
|
|
5007
|
+
tryPostPr5(prNumber, `\u2139\uFE0F kody sync: already up to date with origin/${baseBranch}`, ctx.cwd);
|
|
5008
|
+
}
|
|
5004
5009
|
return;
|
|
5005
5010
|
}
|
|
5006
5011
|
try {
|
|
@@ -5010,13 +5015,21 @@ var syncFlow = async (ctx) => {
|
|
|
5010
5015
|
bail2(ctx, prNumber, `merge succeeded but push failed: ${msg}`);
|
|
5011
5016
|
return;
|
|
5012
5017
|
}
|
|
5013
|
-
ctx.
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
+
ctx.data.syncResult = "merged";
|
|
5019
|
+
if (announceOnSuccess) {
|
|
5020
|
+
ctx.output.exitCode = 0;
|
|
5021
|
+
ctx.output.reason = `merged origin/${baseBranch} into ${ctx.data.branch}`;
|
|
5022
|
+
const runUrl = getRunUrl();
|
|
5023
|
+
const runSuffix = runUrl ? ` ([logs](${runUrl}))` : "";
|
|
5024
|
+
tryPostPr5(
|
|
5025
|
+
prNumber,
|
|
5026
|
+
`\u2705 kody sync: merged \`origin/${baseBranch}\` into \`${ctx.data.branch}\`${runSuffix}`,
|
|
5027
|
+
ctx.cwd
|
|
5028
|
+
);
|
|
5029
|
+
}
|
|
5018
5030
|
};
|
|
5019
5031
|
function bail2(ctx, prNumber, reason) {
|
|
5032
|
+
ctx.skipAgent = true;
|
|
5020
5033
|
ctx.output.exitCode = 1;
|
|
5021
5034
|
ctx.output.reason = reason;
|
|
5022
5035
|
const runUrl = getRunUrl();
|
|
@@ -5145,6 +5158,135 @@ var verify = async (ctx) => {
|
|
|
5145
5158
|
}
|
|
5146
5159
|
};
|
|
5147
5160
|
|
|
5161
|
+
// src/scripts/waitForCi.ts
|
|
5162
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
5163
|
+
var API_TIMEOUT_MS9 = 3e4;
|
|
5164
|
+
var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
5165
|
+
const timeoutMinutes = numArg(args, "timeoutMinutes", 30);
|
|
5166
|
+
const pollSeconds = numArg(args, "pollSeconds", 30);
|
|
5167
|
+
const initialWaitSeconds = numArg(args, "initialWaitSeconds", 15);
|
|
5168
|
+
const maxFixCiAttempts = numArg(args, "maxFixCiAttempts", 3);
|
|
5169
|
+
const state = ctx.data.taskState;
|
|
5170
|
+
const prUrl = state?.core.prUrl;
|
|
5171
|
+
const prNumber = prUrl ? parsePrNumber(prUrl) : null;
|
|
5172
|
+
if (!prNumber) {
|
|
5173
|
+
ctx.data.action = mkAction("CI_GIVEUP", { reason: "no PR url in state \u2014 nothing to wait for" });
|
|
5174
|
+
return;
|
|
5175
|
+
}
|
|
5176
|
+
const fixCiAttempts = state?.core.attempts?.["fix-ci"] ?? 0;
|
|
5177
|
+
await sleep(initialWaitSeconds * 1e3);
|
|
5178
|
+
const deadline = Date.now() + timeoutMinutes * 6e4;
|
|
5179
|
+
let lastSummary = "";
|
|
5180
|
+
while (Date.now() < deadline) {
|
|
5181
|
+
const rows = fetchChecks(prNumber, ctx.cwd);
|
|
5182
|
+
if (rows === null) {
|
|
5183
|
+
await sleep(pollSeconds * 1e3);
|
|
5184
|
+
continue;
|
|
5185
|
+
}
|
|
5186
|
+
if (rows.length === 0) {
|
|
5187
|
+
await sleep(pollSeconds * 1e3);
|
|
5188
|
+
continue;
|
|
5189
|
+
}
|
|
5190
|
+
const summary = summarize(rows);
|
|
5191
|
+
if (summary !== lastSummary) {
|
|
5192
|
+
lastSummary = summary;
|
|
5193
|
+
tryPostPr6(prNumber, `\u23F3 kody waitForCi: ${summary}`, ctx.cwd);
|
|
5194
|
+
}
|
|
5195
|
+
const failed = rows.filter((r) => r.bucket === "fail" || r.bucket === "cancel");
|
|
5196
|
+
const pending = rows.filter((r) => r.bucket === "pending");
|
|
5197
|
+
if (failed.length > 0) {
|
|
5198
|
+
const detail = failed.slice(0, 5).map((r) => `${r.workflow ?? "?"} / ${r.name ?? "?"}${r.link ? ` (${r.link})` : ""}`).join("; ");
|
|
5199
|
+
if (fixCiAttempts >= maxFixCiAttempts) {
|
|
5200
|
+
ctx.data.action = mkAction("CI_GIVEUP", {
|
|
5201
|
+
reason: `fix-ci attempts (${fixCiAttempts}) hit cap (${maxFixCiAttempts})`,
|
|
5202
|
+
failedChecks: detail,
|
|
5203
|
+
prUrl
|
|
5204
|
+
});
|
|
5205
|
+
tryPostPr6(
|
|
5206
|
+
prNumber,
|
|
5207
|
+
`\u{1F6D1} kody waitForCi: giving up after ${fixCiAttempts} fix-ci attempts. Failed: ${detail}`,
|
|
5208
|
+
ctx.cwd
|
|
5209
|
+
);
|
|
5210
|
+
} else {
|
|
5211
|
+
ctx.data.action = mkAction("CI_FAILED", {
|
|
5212
|
+
failedChecks: detail,
|
|
5213
|
+
attempt: fixCiAttempts + 1,
|
|
5214
|
+
maxAttempts: maxFixCiAttempts,
|
|
5215
|
+
prUrl
|
|
5216
|
+
});
|
|
5217
|
+
tryPostPr6(
|
|
5218
|
+
prNumber,
|
|
5219
|
+
`\u274C kody waitForCi: CI failed (attempt ${fixCiAttempts + 1}/${maxFixCiAttempts}). Dispatching fix-ci. Failed: ${detail}`,
|
|
5220
|
+
ctx.cwd
|
|
5221
|
+
);
|
|
5222
|
+
}
|
|
5223
|
+
return;
|
|
5224
|
+
}
|
|
5225
|
+
if (pending.length === 0) {
|
|
5226
|
+
ctx.data.action = mkAction("CI_PASSED", { checks: rows.length, prUrl });
|
|
5227
|
+
tryPostPr6(prNumber, `\u2705 kody waitForCi: all ${rows.length} checks green on PR #${prNumber}`, ctx.cwd);
|
|
5228
|
+
return;
|
|
5229
|
+
}
|
|
5230
|
+
await sleep(pollSeconds * 1e3);
|
|
5231
|
+
}
|
|
5232
|
+
ctx.data.action = mkAction("CI_TIMEOUT", {
|
|
5233
|
+
reason: `CI did not complete within ${timeoutMinutes} minutes`,
|
|
5234
|
+
prUrl
|
|
5235
|
+
});
|
|
5236
|
+
tryPostPr6(prNumber, `\u231B kody waitForCi: timed out after ${timeoutMinutes} minutes`, ctx.cwd);
|
|
5237
|
+
};
|
|
5238
|
+
function fetchChecks(prNumber, cwd) {
|
|
5239
|
+
try {
|
|
5240
|
+
const raw = execFileSync20(
|
|
5241
|
+
"gh",
|
|
5242
|
+
["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"],
|
|
5243
|
+
{
|
|
5244
|
+
encoding: "utf-8",
|
|
5245
|
+
timeout: API_TIMEOUT_MS9,
|
|
5246
|
+
cwd,
|
|
5247
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
5248
|
+
}
|
|
5249
|
+
);
|
|
5250
|
+
const parsed = JSON.parse(raw);
|
|
5251
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
5252
|
+
} catch (err) {
|
|
5253
|
+
process.stderr.write(
|
|
5254
|
+
`[kody waitForCi] gh pr checks #${prNumber} failed: ${err instanceof Error ? err.message : String(err)}
|
|
5255
|
+
`
|
|
5256
|
+
);
|
|
5257
|
+
return null;
|
|
5258
|
+
}
|
|
5259
|
+
}
|
|
5260
|
+
function summarize(rows) {
|
|
5261
|
+
const counts = {};
|
|
5262
|
+
for (const r of rows) {
|
|
5263
|
+
const k = r.bucket ?? "unknown";
|
|
5264
|
+
counts[k] = (counts[k] ?? 0) + 1;
|
|
5265
|
+
}
|
|
5266
|
+
return Object.entries(counts).map(([k, v]) => `${k}:${v}`).join(" ");
|
|
5267
|
+
}
|
|
5268
|
+
function mkAction(type, payload) {
|
|
5269
|
+
return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5270
|
+
}
|
|
5271
|
+
function numArg(args, key, fallback) {
|
|
5272
|
+
const v = args?.[key];
|
|
5273
|
+
if (typeof v === "number" && Number.isFinite(v) && v >= 0) return v;
|
|
5274
|
+
if (typeof v === "string") {
|
|
5275
|
+
const n = Number(v);
|
|
5276
|
+
if (Number.isFinite(n) && n >= 0) return n;
|
|
5277
|
+
}
|
|
5278
|
+
return fallback;
|
|
5279
|
+
}
|
|
5280
|
+
function tryPostPr6(prNumber, body, cwd) {
|
|
5281
|
+
try {
|
|
5282
|
+
postPrReviewComment(prNumber, body, cwd);
|
|
5283
|
+
} catch {
|
|
5284
|
+
}
|
|
5285
|
+
}
|
|
5286
|
+
function sleep(ms) {
|
|
5287
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
5288
|
+
}
|
|
5289
|
+
|
|
5148
5290
|
// src/scripts/watchStalePrsFlow.ts
|
|
5149
5291
|
function readWatchConfig(ctx) {
|
|
5150
5292
|
const cfg = ctx.config.watch;
|
|
@@ -5336,7 +5478,8 @@ var postflightScripts = {
|
|
|
5336
5478
|
postClassification,
|
|
5337
5479
|
notifyTerminal,
|
|
5338
5480
|
recordOutcome,
|
|
5339
|
-
mergeReleasePr
|
|
5481
|
+
mergeReleasePr,
|
|
5482
|
+
waitForCi
|
|
5340
5483
|
};
|
|
5341
5484
|
var allScriptNames = /* @__PURE__ */ new Set([
|
|
5342
5485
|
...Object.keys(preflightScripts),
|
|
@@ -5344,7 +5487,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
5344
5487
|
]);
|
|
5345
5488
|
|
|
5346
5489
|
// src/tools.ts
|
|
5347
|
-
import { execFileSync as
|
|
5490
|
+
import { execFileSync as execFileSync21 } from "child_process";
|
|
5348
5491
|
function verifyCliTools(tools, cwd) {
|
|
5349
5492
|
const out = [];
|
|
5350
5493
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -5377,7 +5520,7 @@ function verifyOne(tool, cwd) {
|
|
|
5377
5520
|
}
|
|
5378
5521
|
function runShell(cmd, cwd, timeoutMs = 3e4) {
|
|
5379
5522
|
try {
|
|
5380
|
-
|
|
5523
|
+
execFileSync21("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
5381
5524
|
return true;
|
|
5382
5525
|
} catch {
|
|
5383
5526
|
return false;
|
|
@@ -5784,7 +5927,7 @@ function detectPackageManager2(cwd) {
|
|
|
5784
5927
|
}
|
|
5785
5928
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
5786
5929
|
try {
|
|
5787
|
-
|
|
5930
|
+
execFileSync22(cmd, args, {
|
|
5788
5931
|
cwd,
|
|
5789
5932
|
stdio: stream ? "inherit" : "pipe",
|
|
5790
5933
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -5797,7 +5940,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
5797
5940
|
}
|
|
5798
5941
|
function isOnPath(bin) {
|
|
5799
5942
|
try {
|
|
5800
|
-
|
|
5943
|
+
execFileSync22("which", [bin], { stdio: "pipe" });
|
|
5801
5944
|
return true;
|
|
5802
5945
|
} catch {
|
|
5803
5946
|
return false;
|
|
@@ -5831,7 +5974,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
5831
5974
|
} catch {
|
|
5832
5975
|
}
|
|
5833
5976
|
try {
|
|
5834
|
-
|
|
5977
|
+
execFileSync22("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
5835
5978
|
process.stdout.write("\u2192 kody: litellm already installed\n");
|
|
5836
5979
|
return 0;
|
|
5837
5980
|
} catch {
|
|
@@ -5841,16 +5984,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
5841
5984
|
}
|
|
5842
5985
|
function configureGitIdentity(cwd) {
|
|
5843
5986
|
try {
|
|
5844
|
-
const name =
|
|
5987
|
+
const name = execFileSync22("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
5845
5988
|
if (name) return;
|
|
5846
5989
|
} catch {
|
|
5847
5990
|
}
|
|
5848
5991
|
try {
|
|
5849
|
-
|
|
5992
|
+
execFileSync22("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
5850
5993
|
} catch {
|
|
5851
5994
|
}
|
|
5852
5995
|
try {
|
|
5853
|
-
|
|
5996
|
+
execFileSync22("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
5854
5997
|
cwd,
|
|
5855
5998
|
stdio: "pipe"
|
|
5856
5999
|
});
|
|
@@ -6032,9 +6175,9 @@ function commitChatFiles(cwd, sessionId, verbose) {
|
|
|
6032
6175
|
if (paths.length === 0) return;
|
|
6033
6176
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
6034
6177
|
try {
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6178
|
+
execFileSync23("git", ["add", ...paths], opts);
|
|
6179
|
+
execFileSync23("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
6180
|
+
execFileSync23("git", ["push", "--quiet", "origin", "HEAD"], opts);
|
|
6038
6181
|
} catch (err) {
|
|
6039
6182
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6040
6183
|
process.stderr.write(`[kody:chat] commit/push skipped: ${msg}
|
|
@@ -62,13 +62,24 @@
|
|
|
62
62
|
{ "script": "dispatch", "with": { "next": "release-deploy", "target": "issue" },
|
|
63
63
|
"runWhen": { "data.taskState.core.lastOutcome.type": "RELEASE_PUBLISH_COMPLETED" } },
|
|
64
64
|
|
|
65
|
+
{ "script": "waitForCi",
|
|
66
|
+
"with": { "timeoutMinutes": 30, "pollSeconds": 30, "initialWaitSeconds": 15, "maxFixCiAttempts": 3 },
|
|
67
|
+
"runWhen": { "data.taskState.core.lastOutcome.type": ["RELEASE_DEPLOY_COMPLETED", "FIX_CI_COMPLETED"] } },
|
|
68
|
+
|
|
69
|
+
{ "script": "dispatch", "with": { "next": "fix-ci", "target": "pr" },
|
|
70
|
+
"runWhen": { "data.action.type": "CI_FAILED" } },
|
|
71
|
+
|
|
65
72
|
{ "script": "finishFlow",
|
|
66
73
|
"with": { "reason": "release-completed", "label": "kody:done", "color": "0e8a16", "description": "kody: release complete" },
|
|
67
|
-
"runWhen": { "data.
|
|
74
|
+
"runWhen": { "data.action.type": "CI_PASSED" } },
|
|
75
|
+
|
|
76
|
+
{ "script": "finishFlow",
|
|
77
|
+
"with": { "reason": "release-failed", "label": "kody:failed", "color": "e11d21", "description": "kody: release flow failed" },
|
|
78
|
+
"runWhen": { "data.action.type": ["CI_GIVEUP", "CI_TIMEOUT"] } },
|
|
68
79
|
|
|
69
80
|
{ "script": "finishFlow",
|
|
70
81
|
"with": { "reason": "release-failed", "label": "kody:failed", "color": "e11d21", "description": "kody: release flow failed" },
|
|
71
|
-
"runWhen": { "data.taskState.core.lastOutcome.type": ["RELEASE_PREPARE_FAILED", "RELEASE_MERGE_FAILED", "RELEASE_PUBLISH_FAILED", "RELEASE_DEPLOY_FAILED"] } },
|
|
82
|
+
"runWhen": { "data.taskState.core.lastOutcome.type": ["RELEASE_PREPARE_FAILED", "RELEASE_MERGE_FAILED", "RELEASE_PUBLISH_FAILED", "RELEASE_DEPLOY_FAILED", "FIX_CI_FAILED"] } },
|
|
72
83
|
|
|
73
84
|
{ "script": "persistFlowState" }
|
|
74
85
|
]
|
|
@@ -74,6 +74,10 @@ existing=$(gh pr list --head "$default_branch" --base "$release_branch" --state
|
|
|
74
74
|
| python3 -c 'import json,sys; data=json.load(sys.stdin); print(data[0]["url"] if data else "")' 2>/dev/null \
|
|
75
75
|
|| echo "")
|
|
76
76
|
|
|
77
|
+
# Hoisted so the kody-release-pr marker write below also runs in the
|
|
78
|
+
# reuse-existing-PR path.
|
|
79
|
+
issue_arg="${KODY_ARG_ISSUE:-}"
|
|
80
|
+
|
|
77
81
|
if [[ -n "$existing" ]]; then
|
|
78
82
|
echo " reusing existing deploy PR: ${existing}"
|
|
79
83
|
pr_url="$existing"
|
|
@@ -81,7 +85,6 @@ else
|
|
|
81
85
|
# Same Tracking-Issue marker as release-prepare — non-closing reference
|
|
82
86
|
# so the originating release issue stays open through the deploy step
|
|
83
87
|
# while the Kody Dashboard can still link this PR to the task for preview.
|
|
84
|
-
issue_arg="${KODY_ARG_ISSUE:-}"
|
|
85
88
|
tracking_line=""
|
|
86
89
|
if [[ "$issue_arg" =~ ^[0-9]+$ && "$issue_arg" != "0" ]]; then
|
|
87
90
|
tracking_line=$'\n\nTracking-Issue: #'"${issue_arg}"
|
|
@@ -102,6 +105,24 @@ if [[ -z "$pr_url" ]]; then
|
|
|
102
105
|
exit 1
|
|
103
106
|
fi
|
|
104
107
|
|
|
108
|
+
# Persist the deploy-PR marker on the originating issue body. Mirrors the
|
|
109
|
+
# release-prepare path — the issue body is owned by the orchestrator, so
|
|
110
|
+
# this signal survives any @kody fix that overwrites the PR body. The
|
|
111
|
+
# marker replaces the prepare-PR ref so the dashboard pivots to the deploy
|
|
112
|
+
# PR (the now-current task) automatically.
|
|
113
|
+
if [[ "${issue_arg:-}" =~ ^[0-9]+$ && "${issue_arg:-0}" != "0" ]]; then
|
|
114
|
+
pr_number="${pr_url##*/}"
|
|
115
|
+
if [[ "$pr_number" =~ ^[0-9]+$ ]]; then
|
|
116
|
+
cur_body=$(gh issue view "$issue_arg" --json body -q .body 2>/dev/null || echo "")
|
|
117
|
+
cleaned_body=$(printf '%s' "$cur_body" | sed -E '/<!-- kody-release-pr:[^>]*-->/d')
|
|
118
|
+
{
|
|
119
|
+
printf '%s' "$cleaned_body"
|
|
120
|
+
printf '\n\n<!-- kody-release-pr: #%s -->\n' "$pr_number"
|
|
121
|
+
} | gh issue edit "$issue_arg" --body-file - >/dev/null 2>&1 || \
|
|
122
|
+
echo "[kody release-deploy] WARN: failed to write kody-release-pr marker to issue #${issue_arg}"
|
|
123
|
+
fi
|
|
124
|
+
fi
|
|
125
|
+
|
|
105
126
|
echo "RELEASE_DEPLOY_PR=${pr_url}"
|
|
106
127
|
echo "KODY_PR_URL=${pr_url}"
|
|
107
128
|
|
|
@@ -333,6 +333,24 @@ if [[ -z "$pr_url" ]]; then
|
|
|
333
333
|
fail "release prepare: gh pr create returned empty URL" 4
|
|
334
334
|
fi
|
|
335
335
|
|
|
336
|
+
# Persist a release-PR marker on the originating issue body so the Kody
|
|
337
|
+
# Dashboard can link the issue → PR even if @kody fix later overwrites the
|
|
338
|
+
# PR body. Idempotent: any existing marker line is stripped before append,
|
|
339
|
+
# so re-runs replace the previous PR ref. The issue body is owned by the
|
|
340
|
+
# orchestrator (no @kody fix touches it), so this signal is durable.
|
|
341
|
+
if [[ "${issue_arg:-}" =~ ^[0-9]+$ && "${issue_arg:-0}" != "0" ]]; then
|
|
342
|
+
pr_number="${pr_url##*/}"
|
|
343
|
+
if [[ "$pr_number" =~ ^[0-9]+$ ]]; then
|
|
344
|
+
cur_body=$(gh issue view "$issue_arg" --json body -q .body 2>/dev/null || echo "")
|
|
345
|
+
cleaned_body=$(printf '%s' "$cur_body" | sed -E '/<!-- kody-release-pr:[^>]*-->/d')
|
|
346
|
+
{
|
|
347
|
+
printf '%s' "$cleaned_body"
|
|
348
|
+
printf '\n\n<!-- kody-release-pr: #%s -->\n' "$pr_number"
|
|
349
|
+
} | gh issue edit "$issue_arg" --body-file - >/dev/null 2>&1 || \
|
|
350
|
+
echo "[kody release-prepare] WARN: failed to write kody-release-pr marker to issue #${issue_arg}"
|
|
351
|
+
fi
|
|
352
|
+
fi
|
|
353
|
+
|
|
336
354
|
echo "RELEASE_PR=${pr_url}"
|
|
337
355
|
echo "KODY_PR_URL=${pr_url}"
|
|
338
356
|
echo "KODY_REASON=opened release PR for ${tag}"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.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",
|