@kody-ade/kody-engine 0.3.50 → 0.3.52
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
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.52",
|
|
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 execFileSync26 } from "child_process";
|
|
54
54
|
import * as fs26 from "fs";
|
|
55
55
|
import * as path23 from "path";
|
|
56
56
|
|
|
@@ -154,7 +154,7 @@ function loadConfig(projectDir = process.cwd()) {
|
|
|
154
154
|
throw new Error(`kody.config.json is invalid JSON: ${msg}`);
|
|
155
155
|
}
|
|
156
156
|
const quality = raw.quality ?? {};
|
|
157
|
-
const
|
|
157
|
+
const git5 = raw.git ?? {};
|
|
158
158
|
const github = raw.github ?? {};
|
|
159
159
|
const agent = raw.agent ?? {};
|
|
160
160
|
if (!agent.model || typeof agent.model !== "string") {
|
|
@@ -171,7 +171,7 @@ function loadConfig(projectDir = process.cwd()) {
|
|
|
171
171
|
testUnit: typeof quality.testUnit === "string" ? quality.testUnit : ""
|
|
172
172
|
},
|
|
173
173
|
git: {
|
|
174
|
-
defaultBranch: typeof
|
|
174
|
+
defaultBranch: typeof git5.defaultBranch === "string" ? git5.defaultBranch : "main"
|
|
175
175
|
},
|
|
176
176
|
github: {
|
|
177
177
|
owner: String(github.owner),
|
|
@@ -605,7 +605,7 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
605
605
|
}
|
|
606
606
|
|
|
607
607
|
// src/kody-cli.ts
|
|
608
|
-
import { execFileSync as
|
|
608
|
+
import { execFileSync as execFileSync25 } from "child_process";
|
|
609
609
|
import * as fs25 from "fs";
|
|
610
610
|
import * as path22 from "path";
|
|
611
611
|
|
|
@@ -1313,6 +1313,12 @@ function parseScriptList(p, key, raw) {
|
|
|
1313
1313
|
if (r.with && typeof r.with === "object") {
|
|
1314
1314
|
entry.with = r.with;
|
|
1315
1315
|
}
|
|
1316
|
+
if (typeof r.timeoutSec === "number" && r.timeoutSec > 0) {
|
|
1317
|
+
if (!hasShell) {
|
|
1318
|
+
throw new ProfileError(p, `scripts.${key}[${i}] "timeoutSec" only applies to shell entries`);
|
|
1319
|
+
}
|
|
1320
|
+
entry.timeoutSec = r.timeoutSec;
|
|
1321
|
+
}
|
|
1316
1322
|
out.push(entry);
|
|
1317
1323
|
}
|
|
1318
1324
|
return out;
|
|
@@ -4632,6 +4638,14 @@ function walkMd(root, visit) {
|
|
|
4632
4638
|
}
|
|
4633
4639
|
}
|
|
4634
4640
|
|
|
4641
|
+
// src/scripts/markFlowSuccess.ts
|
|
4642
|
+
var markFlowSuccess = async (ctx) => {
|
|
4643
|
+
const exit = ctx.output.exitCode;
|
|
4644
|
+
if (exit === void 0 || exit === 0) {
|
|
4645
|
+
ctx.data.agentDone = true;
|
|
4646
|
+
}
|
|
4647
|
+
};
|
|
4648
|
+
|
|
4635
4649
|
// src/scripts/memorizeFlow.ts
|
|
4636
4650
|
import { execFileSync as execFileSync15 } from "child_process";
|
|
4637
4651
|
import * as fs22 from "fs";
|
|
@@ -5558,6 +5572,120 @@ function tryPostPr3(prNumber, body, cwd) {
|
|
|
5558
5572
|
}
|
|
5559
5573
|
}
|
|
5560
5574
|
|
|
5575
|
+
// src/scripts/revertFlow.ts
|
|
5576
|
+
import { execFileSync as execFileSync19 } from "child_process";
|
|
5577
|
+
var SHA_RE = /^[0-9a-f]{4,40}$/i;
|
|
5578
|
+
var revertFlow = async (ctx) => {
|
|
5579
|
+
const prNumber = ctx.args.pr;
|
|
5580
|
+
const pr = getPr(prNumber, ctx.cwd);
|
|
5581
|
+
if (pr.state !== "OPEN") {
|
|
5582
|
+
ctx.output.exitCode = 1;
|
|
5583
|
+
ctx.output.reason = `PR #${prNumber} is not OPEN (state: ${pr.state})`;
|
|
5584
|
+
ctx.skipAgent = true;
|
|
5585
|
+
return;
|
|
5586
|
+
}
|
|
5587
|
+
ctx.data.pr = pr;
|
|
5588
|
+
ctx.data.commentTargetType = "pr";
|
|
5589
|
+
ctx.data.commentTargetNumber = prNumber;
|
|
5590
|
+
checkoutPrBranch(prNumber, ctx.cwd);
|
|
5591
|
+
ctx.data.branch = getCurrentBranch(ctx.cwd);
|
|
5592
|
+
const shasArg = String(ctx.args.shas ?? "").trim();
|
|
5593
|
+
if (!shasArg) {
|
|
5594
|
+
ctx.output.exitCode = 64;
|
|
5595
|
+
ctx.output.reason = "no commit SHAs provided \u2014 usage: @kody revert <sha> [<sha> \u2026]";
|
|
5596
|
+
ctx.skipAgent = true;
|
|
5597
|
+
tryPostPr4(prNumber, `\u26A0\uFE0F kody revert FAILED: ${ctx.output.reason}`, ctx.cwd);
|
|
5598
|
+
return;
|
|
5599
|
+
}
|
|
5600
|
+
const requested = shasArg.split(/\s+/).filter((s) => s.length > 0);
|
|
5601
|
+
const bad = requested.filter((s) => !SHA_RE.test(s));
|
|
5602
|
+
if (bad.length > 0) {
|
|
5603
|
+
ctx.output.exitCode = 64;
|
|
5604
|
+
ctx.output.reason = `not valid SHA-shaped tokens: ${bad.join(", ")}`;
|
|
5605
|
+
ctx.skipAgent = true;
|
|
5606
|
+
tryPostPr4(prNumber, `\u26A0\uFE0F kody revert FAILED: ${ctx.output.reason}`, ctx.cwd);
|
|
5607
|
+
return;
|
|
5608
|
+
}
|
|
5609
|
+
const resolved = [];
|
|
5610
|
+
const unreachable = [];
|
|
5611
|
+
for (const s of requested) {
|
|
5612
|
+
let full;
|
|
5613
|
+
try {
|
|
5614
|
+
full = git4(["rev-parse", "--verify", `${s}^{commit}`], ctx.cwd);
|
|
5615
|
+
} catch {
|
|
5616
|
+
unreachable.push(s);
|
|
5617
|
+
continue;
|
|
5618
|
+
}
|
|
5619
|
+
if (!isAncestorOfHead(full, ctx.cwd)) {
|
|
5620
|
+
unreachable.push(s);
|
|
5621
|
+
continue;
|
|
5622
|
+
}
|
|
5623
|
+
let subject = "";
|
|
5624
|
+
try {
|
|
5625
|
+
subject = git4(["log", "-1", "--format=%s", full], ctx.cwd);
|
|
5626
|
+
} catch {
|
|
5627
|
+
}
|
|
5628
|
+
resolved.push({ input: s, full, subject });
|
|
5629
|
+
}
|
|
5630
|
+
if (unreachable.length > 0) {
|
|
5631
|
+
ctx.output.exitCode = 64;
|
|
5632
|
+
ctx.output.reason = `commit(s) not found in this PR branch: ${unreachable.join(", ")}`;
|
|
5633
|
+
ctx.skipAgent = true;
|
|
5634
|
+
tryPostPr4(prNumber, `\u26A0\uFE0F kody revert FAILED: ${ctx.output.reason}`, ctx.cwd);
|
|
5635
|
+
return;
|
|
5636
|
+
}
|
|
5637
|
+
ctx.args.shas = resolved.map((r) => r.full).join(" ");
|
|
5638
|
+
ctx.data.commitMessage = buildCommitMessage(resolved);
|
|
5639
|
+
ctx.data.prSummary = buildPrSummary(resolved);
|
|
5640
|
+
ctx.skipAgent = true;
|
|
5641
|
+
const runUrl = getRunUrl();
|
|
5642
|
+
const runSuffix = runUrl ? `, run ${runUrl}` : "";
|
|
5643
|
+
const shaList = resolved.map((r) => `\`${r.full.slice(0, 7)}\``).join(", ");
|
|
5644
|
+
tryPostPr4(
|
|
5645
|
+
prNumber,
|
|
5646
|
+
`\u2699\uFE0F kody revert started on \`${ctx.data.branch}\`${runSuffix} \u2014 reverting ${shaList}`,
|
|
5647
|
+
ctx.cwd
|
|
5648
|
+
);
|
|
5649
|
+
};
|
|
5650
|
+
function buildCommitMessage(resolved) {
|
|
5651
|
+
if (resolved.length === 1) {
|
|
5652
|
+
const { full, subject } = resolved[0];
|
|
5653
|
+
return subject ? `revert: "${subject}" (${full.slice(0, 7)})` : `revert: ${full.slice(0, 7)}`;
|
|
5654
|
+
}
|
|
5655
|
+
const shas = resolved.map((r) => r.full.slice(0, 7)).join(", ");
|
|
5656
|
+
return `revert: ${resolved.length} commit(s) (${shas})`;
|
|
5657
|
+
}
|
|
5658
|
+
function buildPrSummary(resolved) {
|
|
5659
|
+
return resolved.map((r) => `- Reverted \`${r.full.slice(0, 7)}\`${r.subject ? ` \u2014 ${r.subject}` : ""}`).join("\n");
|
|
5660
|
+
}
|
|
5661
|
+
function git4(args, cwd) {
|
|
5662
|
+
return execFileSync19("git", args, {
|
|
5663
|
+
encoding: "utf-8",
|
|
5664
|
+
timeout: 3e4,
|
|
5665
|
+
cwd,
|
|
5666
|
+
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
5667
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
5668
|
+
}).trim();
|
|
5669
|
+
}
|
|
5670
|
+
function isAncestorOfHead(sha, cwd) {
|
|
5671
|
+
try {
|
|
5672
|
+
execFileSync19("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
|
|
5673
|
+
cwd,
|
|
5674
|
+
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
5675
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
5676
|
+
});
|
|
5677
|
+
return true;
|
|
5678
|
+
} catch {
|
|
5679
|
+
return false;
|
|
5680
|
+
}
|
|
5681
|
+
}
|
|
5682
|
+
function tryPostPr4(prNumber, body, cwd) {
|
|
5683
|
+
try {
|
|
5684
|
+
postPrReviewComment(prNumber, body, cwd);
|
|
5685
|
+
} catch {
|
|
5686
|
+
}
|
|
5687
|
+
}
|
|
5688
|
+
|
|
5561
5689
|
// src/scripts/resolvePreviewUrl.ts
|
|
5562
5690
|
var DEFAULT_PREVIEW_URL = "http://localhost:3000";
|
|
5563
5691
|
var resolvePreviewUrl = async (ctx) => {
|
|
@@ -5595,9 +5723,9 @@ var reviewFlow = async (ctx) => {
|
|
|
5595
5723
|
ctx.data.prDiff = getPrDiff(prNumber, ctx.cwd);
|
|
5596
5724
|
const runUrl = getRunUrl();
|
|
5597
5725
|
const runSuffix = runUrl ? `, run ${runUrl}` : "";
|
|
5598
|
-
|
|
5726
|
+
tryPostPr5(prNumber, `\u{1F440} kody review started on PR #${prNumber}${runSuffix}`, ctx.cwd);
|
|
5599
5727
|
};
|
|
5600
|
-
function
|
|
5728
|
+
function tryPostPr5(prNumber, body, cwd) {
|
|
5601
5729
|
try {
|
|
5602
5730
|
postPrReviewComment(prNumber, body, cwd);
|
|
5603
5731
|
} catch {
|
|
@@ -5710,11 +5838,11 @@ var skipAgent = async (ctx) => {
|
|
|
5710
5838
|
};
|
|
5711
5839
|
|
|
5712
5840
|
// src/scripts/stageMergeConflicts.ts
|
|
5713
|
-
import { execFileSync as
|
|
5841
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
5714
5842
|
var stageMergeConflicts = async (ctx) => {
|
|
5715
5843
|
if (ctx.data.agentDone === false) return;
|
|
5716
5844
|
try {
|
|
5717
|
-
|
|
5845
|
+
execFileSync20("git", ["add", "-A"], {
|
|
5718
5846
|
cwd: ctx.cwd,
|
|
5719
5847
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
5720
5848
|
stdio: "pipe"
|
|
@@ -5724,7 +5852,7 @@ var stageMergeConflicts = async (ctx) => {
|
|
|
5724
5852
|
};
|
|
5725
5853
|
|
|
5726
5854
|
// src/scripts/startFlow.ts
|
|
5727
|
-
import { execFileSync as
|
|
5855
|
+
import { execFileSync as execFileSync21 } from "child_process";
|
|
5728
5856
|
var API_TIMEOUT_MS9 = 3e4;
|
|
5729
5857
|
var startFlow = async (ctx, profile, _agentResult, args) => {
|
|
5730
5858
|
const entry = args?.entry;
|
|
@@ -5758,7 +5886,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
5758
5886
|
const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
|
|
5759
5887
|
const body = `@kody ${next}`;
|
|
5760
5888
|
try {
|
|
5761
|
-
|
|
5889
|
+
execFileSync21("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
5762
5890
|
timeout: API_TIMEOUT_MS9,
|
|
5763
5891
|
cwd,
|
|
5764
5892
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5772,7 +5900,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
5772
5900
|
}
|
|
5773
5901
|
|
|
5774
5902
|
// src/scripts/syncFlow.ts
|
|
5775
|
-
import { execFileSync as
|
|
5903
|
+
import { execFileSync as execFileSync22 } from "child_process";
|
|
5776
5904
|
var syncFlow = async (ctx, _profile, args) => {
|
|
5777
5905
|
const announceOnSuccess = Boolean(args?.announceOnSuccess);
|
|
5778
5906
|
const prNumber = ctx.args.pr;
|
|
@@ -5810,7 +5938,7 @@ var syncFlow = async (ctx, _profile, args) => {
|
|
|
5810
5938
|
if (announceOnSuccess) {
|
|
5811
5939
|
ctx.output.exitCode = 0;
|
|
5812
5940
|
ctx.output.reason = `already up to date with origin/${baseBranch}`;
|
|
5813
|
-
|
|
5941
|
+
tryPostPr6(prNumber, `\u2139\uFE0F kody sync: already up to date with origin/${baseBranch}`, ctx.cwd);
|
|
5814
5942
|
}
|
|
5815
5943
|
return;
|
|
5816
5944
|
}
|
|
@@ -5827,7 +5955,7 @@ var syncFlow = async (ctx, _profile, args) => {
|
|
|
5827
5955
|
ctx.output.reason = `merged origin/${baseBranch} into ${ctx.data.branch}`;
|
|
5828
5956
|
const runUrl = getRunUrl();
|
|
5829
5957
|
const runSuffix = runUrl ? ` ([logs](${runUrl}))` : "";
|
|
5830
|
-
|
|
5958
|
+
tryPostPr6(
|
|
5831
5959
|
prNumber,
|
|
5832
5960
|
`\u2705 kody sync: merged \`origin/${baseBranch}\` into \`${ctx.data.branch}\`${runSuffix}`,
|
|
5833
5961
|
ctx.cwd
|
|
@@ -5840,11 +5968,11 @@ function bail2(ctx, prNumber, reason) {
|
|
|
5840
5968
|
ctx.output.reason = reason;
|
|
5841
5969
|
const runUrl = getRunUrl();
|
|
5842
5970
|
const runSuffix = runUrl ? ` ([logs](${runUrl}))` : "";
|
|
5843
|
-
|
|
5971
|
+
tryPostPr6(prNumber, `\u274C kody sync could not complete${runSuffix}: ${reason}`, ctx.cwd);
|
|
5844
5972
|
}
|
|
5845
5973
|
function revParseHead(cwd) {
|
|
5846
5974
|
try {
|
|
5847
|
-
return
|
|
5975
|
+
return execFileSync22("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
5848
5976
|
} catch {
|
|
5849
5977
|
return "";
|
|
5850
5978
|
}
|
|
@@ -5852,16 +5980,16 @@ function revParseHead(cwd) {
|
|
|
5852
5980
|
function pushBranch(branch, cwd) {
|
|
5853
5981
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
5854
5982
|
try {
|
|
5855
|
-
|
|
5983
|
+
execFileSync22("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
5856
5984
|
} catch {
|
|
5857
|
-
|
|
5985
|
+
execFileSync22("git", ["push", "--force-with-lease", "-u", "origin", branch], {
|
|
5858
5986
|
cwd,
|
|
5859
5987
|
env,
|
|
5860
5988
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5861
5989
|
});
|
|
5862
5990
|
}
|
|
5863
5991
|
}
|
|
5864
|
-
function
|
|
5992
|
+
function tryPostPr6(prNumber, body, cwd) {
|
|
5865
5993
|
try {
|
|
5866
5994
|
postPrReviewComment(prNumber, body, cwd);
|
|
5867
5995
|
} catch {
|
|
@@ -5965,7 +6093,7 @@ var verify = async (ctx) => {
|
|
|
5965
6093
|
};
|
|
5966
6094
|
|
|
5967
6095
|
// src/scripts/waitForCi.ts
|
|
5968
|
-
import { execFileSync as
|
|
6096
|
+
import { execFileSync as execFileSync23 } from "child_process";
|
|
5969
6097
|
var API_TIMEOUT_MS10 = 3e4;
|
|
5970
6098
|
var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
5971
6099
|
const timeoutMinutes = numArg(args, "timeoutMinutes", 30);
|
|
@@ -5996,7 +6124,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
|
5996
6124
|
const summary = summarize(rows);
|
|
5997
6125
|
if (summary !== lastSummary) {
|
|
5998
6126
|
lastSummary = summary;
|
|
5999
|
-
|
|
6127
|
+
tryPostPr7(prNumber, `\u23F3 kody waitForCi: ${summary}`, ctx.cwd);
|
|
6000
6128
|
}
|
|
6001
6129
|
const failed = rows.filter((r) => r.bucket === "fail" || r.bucket === "cancel");
|
|
6002
6130
|
const pending = rows.filter((r) => r.bucket === "pending");
|
|
@@ -6008,7 +6136,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
|
6008
6136
|
failedChecks: detail,
|
|
6009
6137
|
prUrl
|
|
6010
6138
|
});
|
|
6011
|
-
|
|
6139
|
+
tryPostPr7(
|
|
6012
6140
|
prNumber,
|
|
6013
6141
|
`\u{1F6D1} kody waitForCi: giving up after ${fixCiAttempts} fix-ci attempts. Failed: ${detail}`,
|
|
6014
6142
|
ctx.cwd
|
|
@@ -6020,7 +6148,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
|
6020
6148
|
maxAttempts: maxFixCiAttempts,
|
|
6021
6149
|
prUrl
|
|
6022
6150
|
});
|
|
6023
|
-
|
|
6151
|
+
tryPostPr7(
|
|
6024
6152
|
prNumber,
|
|
6025
6153
|
`\u274C kody waitForCi: CI failed (attempt ${fixCiAttempts + 1}/${maxFixCiAttempts}). Dispatching fix-ci. Failed: ${detail}`,
|
|
6026
6154
|
ctx.cwd
|
|
@@ -6030,7 +6158,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
|
6030
6158
|
}
|
|
6031
6159
|
if (pending.length === 0) {
|
|
6032
6160
|
ctx.data.action = mkAction("CI_PASSED", { checks: rows.length, prUrl });
|
|
6033
|
-
|
|
6161
|
+
tryPostPr7(prNumber, `\u2705 kody waitForCi: all ${rows.length} checks green on PR #${prNumber}`, ctx.cwd);
|
|
6034
6162
|
return;
|
|
6035
6163
|
}
|
|
6036
6164
|
await sleep(pollSeconds * 1e3);
|
|
@@ -6039,11 +6167,11 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
|
6039
6167
|
reason: `CI did not complete within ${timeoutMinutes} minutes`,
|
|
6040
6168
|
prUrl
|
|
6041
6169
|
});
|
|
6042
|
-
|
|
6170
|
+
tryPostPr7(prNumber, `\u231B kody waitForCi: timed out after ${timeoutMinutes} minutes`, ctx.cwd);
|
|
6043
6171
|
};
|
|
6044
6172
|
function fetchChecks(prNumber, cwd) {
|
|
6045
6173
|
try {
|
|
6046
|
-
const raw =
|
|
6174
|
+
const raw = execFileSync23("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
|
|
6047
6175
|
encoding: "utf-8",
|
|
6048
6176
|
timeout: API_TIMEOUT_MS10,
|
|
6049
6177
|
cwd,
|
|
@@ -6079,7 +6207,7 @@ function numArg(args, key, fallback) {
|
|
|
6079
6207
|
}
|
|
6080
6208
|
return fallback;
|
|
6081
6209
|
}
|
|
6082
|
-
function
|
|
6210
|
+
function tryPostPr7(prNumber, body, cwd) {
|
|
6083
6211
|
try {
|
|
6084
6212
|
postPrReviewComment(prNumber, body, cwd);
|
|
6085
6213
|
} catch {
|
|
@@ -6252,6 +6380,7 @@ var preflightScripts = {
|
|
|
6252
6380
|
fixFlow,
|
|
6253
6381
|
fixCiFlow,
|
|
6254
6382
|
resolveFlow,
|
|
6383
|
+
revertFlow,
|
|
6255
6384
|
reviewFlow,
|
|
6256
6385
|
syncFlow,
|
|
6257
6386
|
initFlow,
|
|
@@ -6312,7 +6441,8 @@ var postflightScripts = {
|
|
|
6312
6441
|
notifyTerminal,
|
|
6313
6442
|
recordOutcome,
|
|
6314
6443
|
mergeReleasePr,
|
|
6315
|
-
waitForCi
|
|
6444
|
+
waitForCi,
|
|
6445
|
+
markFlowSuccess
|
|
6316
6446
|
};
|
|
6317
6447
|
var allScriptNames = /* @__PURE__ */ new Set([
|
|
6318
6448
|
...Object.keys(preflightScripts),
|
|
@@ -6320,7 +6450,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
6320
6450
|
]);
|
|
6321
6451
|
|
|
6322
6452
|
// src/tools.ts
|
|
6323
|
-
import { execFileSync as
|
|
6453
|
+
import { execFileSync as execFileSync24 } from "child_process";
|
|
6324
6454
|
function verifyCliTools(tools, cwd) {
|
|
6325
6455
|
const out = [];
|
|
6326
6456
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -6353,7 +6483,7 @@ function verifyOne(tool, cwd) {
|
|
|
6353
6483
|
}
|
|
6354
6484
|
function runShell(cmd, cwd, timeoutMs = 3e4) {
|
|
6355
6485
|
try {
|
|
6356
|
-
|
|
6486
|
+
execFileSync24("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
6357
6487
|
return true;
|
|
6358
6488
|
} catch {
|
|
6359
6489
|
return false;
|
|
@@ -6601,7 +6731,17 @@ function finish(out) {
|
|
|
6601
6731
|
`);
|
|
6602
6732
|
return out;
|
|
6603
6733
|
}
|
|
6604
|
-
var
|
|
6734
|
+
var DEFAULT_SHELL_TIMEOUT_MS = 3e5;
|
|
6735
|
+
function resolveShellTimeoutMs(entry) {
|
|
6736
|
+
if (typeof entry.timeoutSec === "number" && entry.timeoutSec > 0) {
|
|
6737
|
+
return Math.floor(entry.timeoutSec * 1e3);
|
|
6738
|
+
}
|
|
6739
|
+
const envSec = Number(process.env.KODY_SHELL_TIMEOUT_SEC);
|
|
6740
|
+
if (Number.isFinite(envSec) && envSec > 0) {
|
|
6741
|
+
return Math.floor(envSec * 1e3);
|
|
6742
|
+
}
|
|
6743
|
+
return DEFAULT_SHELL_TIMEOUT_MS;
|
|
6744
|
+
}
|
|
6605
6745
|
function runShellEntry(entry, ctx, profile) {
|
|
6606
6746
|
const shellName = entry.shell;
|
|
6607
6747
|
const shellPath = path21.join(profile.dir, shellName);
|
|
@@ -6620,12 +6760,13 @@ function runShellEntry(entry, ctx, profile) {
|
|
|
6620
6760
|
for (const [k, v] of flattenConfig(ctx.config)) {
|
|
6621
6761
|
env[`KODY_CFG_${k}`] = v;
|
|
6622
6762
|
}
|
|
6763
|
+
const timeoutMs = resolveShellTimeoutMs(entry);
|
|
6623
6764
|
const r = spawnSync("bash", [shellPath, ...positional], {
|
|
6624
6765
|
cwd: ctx.cwd,
|
|
6625
6766
|
encoding: "utf-8",
|
|
6626
6767
|
env,
|
|
6627
6768
|
stdio: ["pipe", "pipe", "pipe"],
|
|
6628
|
-
timeout:
|
|
6769
|
+
timeout: timeoutMs
|
|
6629
6770
|
});
|
|
6630
6771
|
const stdout = r.stdout ?? "";
|
|
6631
6772
|
const stderr = r.stderr ?? "";
|
|
@@ -6639,6 +6780,18 @@ function runShellEntry(entry, ctx, profile) {
|
|
|
6639
6780
|
if (prUrlMatch?.[1]) ctx.output.prUrl = prUrlMatch[1].trim();
|
|
6640
6781
|
const reasonMatch = stdout.match(/^KODY_REASON=(.+)$/m);
|
|
6641
6782
|
if (reasonMatch?.[1]) ctx.output.reason = reasonMatch[1].trim();
|
|
6783
|
+
const timedOut = r.status === null && r.signal !== null;
|
|
6784
|
+
if (timedOut) {
|
|
6785
|
+
ctx.skipAgent = true;
|
|
6786
|
+
const seconds = Math.round(timeoutMs / 1e3);
|
|
6787
|
+
if (ctx.output.exitCode === void 0 || ctx.output.exitCode === 0) {
|
|
6788
|
+
ctx.output.exitCode = 124;
|
|
6789
|
+
}
|
|
6790
|
+
if (!ctx.output.reason) {
|
|
6791
|
+
ctx.output.reason = `shell '${shellName}' timed out after ${seconds}s (signal=${r.signal})`;
|
|
6792
|
+
}
|
|
6793
|
+
return;
|
|
6794
|
+
}
|
|
6642
6795
|
const exit = r.status ?? -1;
|
|
6643
6796
|
if (exit !== 0) {
|
|
6644
6797
|
ctx.skipAgent = true;
|
|
@@ -6762,7 +6915,7 @@ function detectPackageManager2(cwd) {
|
|
|
6762
6915
|
}
|
|
6763
6916
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
6764
6917
|
try {
|
|
6765
|
-
|
|
6918
|
+
execFileSync25(cmd, args, {
|
|
6766
6919
|
cwd,
|
|
6767
6920
|
stdio: stream ? "inherit" : "pipe",
|
|
6768
6921
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -6775,7 +6928,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
6775
6928
|
}
|
|
6776
6929
|
function isOnPath(bin) {
|
|
6777
6930
|
try {
|
|
6778
|
-
|
|
6931
|
+
execFileSync25("which", [bin], { stdio: "pipe" });
|
|
6779
6932
|
return true;
|
|
6780
6933
|
} catch {
|
|
6781
6934
|
return false;
|
|
@@ -6809,7 +6962,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
6809
6962
|
} catch {
|
|
6810
6963
|
}
|
|
6811
6964
|
try {
|
|
6812
|
-
|
|
6965
|
+
execFileSync25("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
6813
6966
|
process.stdout.write("\u2192 kody: litellm already installed\n");
|
|
6814
6967
|
return 0;
|
|
6815
6968
|
} catch {
|
|
@@ -6819,16 +6972,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
6819
6972
|
}
|
|
6820
6973
|
function configureGitIdentity(cwd) {
|
|
6821
6974
|
try {
|
|
6822
|
-
const name =
|
|
6975
|
+
const name = execFileSync25("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
6823
6976
|
if (name) return;
|
|
6824
6977
|
} catch {
|
|
6825
6978
|
}
|
|
6826
6979
|
try {
|
|
6827
|
-
|
|
6980
|
+
execFileSync25("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
6828
6981
|
} catch {
|
|
6829
6982
|
}
|
|
6830
6983
|
try {
|
|
6831
|
-
|
|
6984
|
+
execFileSync25("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
6832
6985
|
cwd,
|
|
6833
6986
|
stdio: "pipe"
|
|
6834
6987
|
});
|
|
@@ -7099,9 +7252,9 @@ function commitChatFiles(cwd, sessionId, verbose) {
|
|
|
7099
7252
|
if (paths.length === 0) return;
|
|
7100
7253
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
7101
7254
|
try {
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
|
|
7255
|
+
execFileSync26("git", ["add", ...paths], opts);
|
|
7256
|
+
execFileSync26("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
7257
|
+
execFileSync26("git", ["push", "--quiet", "origin", "HEAD"], opts);
|
|
7105
7258
|
} catch (err) {
|
|
7106
7259
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7107
7260
|
process.stderr.write(`[kody:chat] commit/push skipped: ${msg}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "revert",
|
|
3
|
+
"role": "primitive",
|
|
4
|
+
"describe": "Revert one or more commits on an existing PR branch via `git revert`. No agent — fully mechanical.",
|
|
5
|
+
"inputs": [
|
|
6
|
+
{
|
|
7
|
+
"name": "pr",
|
|
8
|
+
"flag": "--pr",
|
|
9
|
+
"type": "int",
|
|
10
|
+
"required": true,
|
|
11
|
+
"describe": "GitHub PR number whose branch to revert commits on."
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "shas",
|
|
15
|
+
"flag": "--shas",
|
|
16
|
+
"type": "string",
|
|
17
|
+
"required": true,
|
|
18
|
+
"bindsCommentRest": true,
|
|
19
|
+
"describe": "One or more commit SHAs (whitespace-separated) to revert. Each must exist in the PR branch's history."
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"claudeCode": {
|
|
23
|
+
"model": "inherit",
|
|
24
|
+
"permissionMode": "acceptEdits",
|
|
25
|
+
"maxTurns": 0,
|
|
26
|
+
"maxThinkingTokens": null,
|
|
27
|
+
"systemPromptAppend": null,
|
|
28
|
+
"tools": [],
|
|
29
|
+
"hooks": [],
|
|
30
|
+
"skills": [],
|
|
31
|
+
"commands": [],
|
|
32
|
+
"subagents": [],
|
|
33
|
+
"plugins": [],
|
|
34
|
+
"mcpServers": []
|
|
35
|
+
},
|
|
36
|
+
"cliTools": [],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"preflight": [
|
|
39
|
+
{
|
|
40
|
+
"script": "setLifecycleLabel",
|
|
41
|
+
"with": {
|
|
42
|
+
"label": "kody:reverting",
|
|
43
|
+
"color": "cccccc",
|
|
44
|
+
"description": "kody: reverting commits"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
{ "script": "revertFlow" },
|
|
48
|
+
{ "shell": "revert.sh" }
|
|
49
|
+
],
|
|
50
|
+
"postflight": [
|
|
51
|
+
{ "script": "markFlowSuccess" },
|
|
52
|
+
{ "script": "commitAndPush" },
|
|
53
|
+
{ "script": "ensurePr" },
|
|
54
|
+
{ "script": "postIssueComment" },
|
|
55
|
+
{ "script": "writeRunSummary" }
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
"output": {
|
|
59
|
+
"actionTypes": [
|
|
60
|
+
"REVERT_COMPLETED",
|
|
61
|
+
"REVERT_FAILED"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# revert: stage `git revert` of one or more commits on the PR branch.
|
|
4
|
+
#
|
|
5
|
+
# Runs as a preflight shell entry after revertFlow has validated inputs and
|
|
6
|
+
# checked out the branch. revertFlow:
|
|
7
|
+
# - Resolved every requested SHA to its full form and re-set
|
|
8
|
+
# ctx.args.shas to a whitespace-separated list (we read it via
|
|
9
|
+
# KODY_ARG_SHAS).
|
|
10
|
+
# - Set ctx.skipAgent=true and ctx.data.commitMessage already, so the
|
|
11
|
+
# agent never runs and commitAndPush will use that message.
|
|
12
|
+
#
|
|
13
|
+
# This script does only the staging — `--no-commit` so commitAndPush
|
|
14
|
+
# (postflight) makes the actual commit. That keeps kody's invariant
|
|
15
|
+
# intact (only commitAndPush commits) and means the message comes from
|
|
16
|
+
# revertFlow, not from git's auto-generated revert subject.
|
|
17
|
+
#
|
|
18
|
+
# Exits:
|
|
19
|
+
# 0 — staged successfully
|
|
20
|
+
# 1+ — git revert failed (executor surfaces stderr; postflight bails)
|
|
21
|
+
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
|
|
24
|
+
shas="${KODY_ARG_SHAS:-}"
|
|
25
|
+
if [[ -z "$shas" ]]; then
|
|
26
|
+
echo "revert.sh: KODY_ARG_SHAS is empty (revertFlow should have set it)" >&2
|
|
27
|
+
exit 64
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# shellcheck disable=SC2086 # Intentional word-splitting on whitespace-separated SHAs.
|
|
31
|
+
git revert --no-commit --no-edit $shas
|
|
32
|
+
|
|
33
|
+
echo "revert.sh: staged revert of: $shas"
|
|
@@ -185,6 +185,14 @@ export interface ScriptEntry {
|
|
|
185
185
|
* dispatcher script can be reused with different `next` targets.
|
|
186
186
|
*/
|
|
187
187
|
with?: Record<string, string | number | boolean>
|
|
188
|
+
/**
|
|
189
|
+
* Optional shell-script timeout in seconds. Only honored on `shell` entries.
|
|
190
|
+
* Falls back to `KODY_SHELL_TIMEOUT_SEC` env var, then the 300s default.
|
|
191
|
+
* Long-running shells (release publish, large repo verify) should declare
|
|
192
|
+
* a higher value rather than relying on the default and getting SIGKILLed
|
|
193
|
+
* with an opaque "exited -1".
|
|
194
|
+
*/
|
|
195
|
+
timeoutSec?: number
|
|
188
196
|
}
|
|
189
197
|
|
|
190
198
|
export interface OutputContract {
|
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.52",
|
|
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",
|