@kody-ade/kody-engine 0.2.44 → 0.2.46
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.
|
|
6
|
+
version: "0.2.46",
|
|
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
|
|
53
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
54
54
|
import * as fs19 from "fs";
|
|
55
55
|
import * as path16 from "path";
|
|
56
56
|
|
|
@@ -528,7 +528,7 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
528
528
|
}
|
|
529
529
|
|
|
530
530
|
// src/kody2-cli.ts
|
|
531
|
-
import { execFileSync as
|
|
531
|
+
import { execFileSync as execFileSync19 } from "child_process";
|
|
532
532
|
import * as fs18 from "fs";
|
|
533
533
|
import * as path15 from "path";
|
|
534
534
|
|
|
@@ -953,6 +953,248 @@ function parseScriptList(p, key, raw) {
|
|
|
953
953
|
return out;
|
|
954
954
|
}
|
|
955
955
|
|
|
956
|
+
// src/scripts/advanceFlow.ts
|
|
957
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
958
|
+
|
|
959
|
+
// src/state.ts
|
|
960
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
961
|
+
var STATE_BEGIN = "<!-- kody2:state:v1:begin -->";
|
|
962
|
+
var STATE_END = "<!-- kody2:state:v1:end -->";
|
|
963
|
+
var HISTORY_MAX_ENTRIES = 20;
|
|
964
|
+
var API_TIMEOUT_MS = 3e4;
|
|
965
|
+
function emptyState() {
|
|
966
|
+
return {
|
|
967
|
+
schemaVersion: 1,
|
|
968
|
+
core: {
|
|
969
|
+
phase: "idle",
|
|
970
|
+
status: "pending",
|
|
971
|
+
currentExecutable: null,
|
|
972
|
+
lastOutcome: null,
|
|
973
|
+
attempts: {}
|
|
974
|
+
},
|
|
975
|
+
executables: {},
|
|
976
|
+
artifacts: {},
|
|
977
|
+
history: []
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
function ghToken() {
|
|
981
|
+
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
982
|
+
}
|
|
983
|
+
function gh(args, input, cwd) {
|
|
984
|
+
const token = ghToken();
|
|
985
|
+
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
986
|
+
return execFileSync2("gh", args, {
|
|
987
|
+
encoding: "utf-8",
|
|
988
|
+
timeout: API_TIMEOUT_MS,
|
|
989
|
+
cwd,
|
|
990
|
+
env,
|
|
991
|
+
input,
|
|
992
|
+
stdio: input ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"]
|
|
993
|
+
}).trim();
|
|
994
|
+
}
|
|
995
|
+
function findStateComment(target, number, cwd) {
|
|
996
|
+
const apiPath = target === "issue" ? `repos/{owner}/{repo}/issues/${number}/comments` : `repos/{owner}/{repo}/issues/${number}/comments`;
|
|
997
|
+
try {
|
|
998
|
+
const raw = gh(["api", "--paginate", apiPath], void 0, cwd);
|
|
999
|
+
const list = JSON.parse(raw);
|
|
1000
|
+
for (const c of list) {
|
|
1001
|
+
if (c.body?.includes(STATE_BEGIN)) {
|
|
1002
|
+
return { id: String(c.id), body: c.body };
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
} catch {
|
|
1006
|
+
}
|
|
1007
|
+
return null;
|
|
1008
|
+
}
|
|
1009
|
+
function parseStateComment(body) {
|
|
1010
|
+
const beginIdx = body.indexOf(STATE_BEGIN);
|
|
1011
|
+
const endIdx = body.indexOf(STATE_END, beginIdx + 1);
|
|
1012
|
+
if (beginIdx < 0 || endIdx < 0) return emptyState();
|
|
1013
|
+
const between = body.slice(beginIdx + STATE_BEGIN.length, endIdx).trim();
|
|
1014
|
+
const OPEN = "```json";
|
|
1015
|
+
const CLOSE = "```";
|
|
1016
|
+
if (!between.startsWith(OPEN) || !between.endsWith(CLOSE)) return emptyState();
|
|
1017
|
+
const jsonStr = between.slice(OPEN.length, between.length - CLOSE.length).trim();
|
|
1018
|
+
try {
|
|
1019
|
+
const parsed = JSON.parse(jsonStr);
|
|
1020
|
+
if (parsed?.schemaVersion !== 1) return emptyState();
|
|
1021
|
+
return {
|
|
1022
|
+
schemaVersion: 1,
|
|
1023
|
+
core: { ...emptyState().core, ...parsed.core },
|
|
1024
|
+
executables: parsed.executables ?? {},
|
|
1025
|
+
artifacts: parsed.artifacts && typeof parsed.artifacts === "object" ? parsed.artifacts : {},
|
|
1026
|
+
history: Array.isArray(parsed.history) ? parsed.history : [],
|
|
1027
|
+
flow: parsed.flow
|
|
1028
|
+
};
|
|
1029
|
+
} catch {
|
|
1030
|
+
return emptyState();
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
function reduce(state, executable, action) {
|
|
1034
|
+
if (!action) return state;
|
|
1035
|
+
const newAttempts = { ...state.core.attempts, [executable]: (state.core.attempts[executable] ?? 0) + 1 };
|
|
1036
|
+
const newExecutables = {
|
|
1037
|
+
...state.executables,
|
|
1038
|
+
[executable]: { ...state.executables[executable] ?? { lastAction: null }, lastAction: action }
|
|
1039
|
+
};
|
|
1040
|
+
const newHistory = [
|
|
1041
|
+
...state.history,
|
|
1042
|
+
{ timestamp: action.timestamp, executable, action: action.type, note: noteFromAction(action) }
|
|
1043
|
+
].slice(-HISTORY_MAX_ENTRIES);
|
|
1044
|
+
return {
|
|
1045
|
+
schemaVersion: 1,
|
|
1046
|
+
core: {
|
|
1047
|
+
...state.core,
|
|
1048
|
+
attempts: newAttempts,
|
|
1049
|
+
lastOutcome: action,
|
|
1050
|
+
currentExecutable: executable,
|
|
1051
|
+
status: statusFromAction(action),
|
|
1052
|
+
phase: phaseFromAction(executable, action)
|
|
1053
|
+
},
|
|
1054
|
+
executables: newExecutables,
|
|
1055
|
+
artifacts: { ...state.artifacts ?? {} },
|
|
1056
|
+
history: newHistory,
|
|
1057
|
+
flow: state.flow
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
function statusFromAction(action) {
|
|
1061
|
+
if (/FAILED$|ERROR$|MISSING$|REJECTED$/i.test(action.type)) return "failed";
|
|
1062
|
+
if (/COMPLETED$|SHIPPED$|MERGED$|SUCCESS$/i.test(action.type)) return "succeeded";
|
|
1063
|
+
return "running";
|
|
1064
|
+
}
|
|
1065
|
+
function phaseFromAction(executable, action) {
|
|
1066
|
+
if (/FAILED$|ERROR$|REJECTED$/i.test(action.type)) return "failed";
|
|
1067
|
+
if (executable === "build") return statusFromAction(action) === "succeeded" ? "implementing" : "implementing";
|
|
1068
|
+
if (executable === "review") return "reviewing";
|
|
1069
|
+
if (executable === "release") return "shipped";
|
|
1070
|
+
return "idle";
|
|
1071
|
+
}
|
|
1072
|
+
function noteFromAction(action) {
|
|
1073
|
+
const p = action.payload;
|
|
1074
|
+
if (typeof p?.prUrl === "string") return p.prUrl;
|
|
1075
|
+
if (typeof p?.reason === "string") return p.reason.slice(0, 120);
|
|
1076
|
+
if (typeof p?.commitMessage === "string") return p.commitMessage.slice(0, 120);
|
|
1077
|
+
return void 0;
|
|
1078
|
+
}
|
|
1079
|
+
function renderStateComment(state) {
|
|
1080
|
+
const lines = [];
|
|
1081
|
+
lines.push(STATE_BEGIN);
|
|
1082
|
+
lines.push("");
|
|
1083
|
+
lines.push("```json");
|
|
1084
|
+
lines.push(
|
|
1085
|
+
JSON.stringify(
|
|
1086
|
+
{
|
|
1087
|
+
schemaVersion: state.schemaVersion,
|
|
1088
|
+
core: state.core,
|
|
1089
|
+
artifacts: state.artifacts ?? {},
|
|
1090
|
+
executables: state.executables,
|
|
1091
|
+
history: state.history,
|
|
1092
|
+
...state.flow ? { flow: state.flow } : {}
|
|
1093
|
+
},
|
|
1094
|
+
null,
|
|
1095
|
+
2
|
|
1096
|
+
)
|
|
1097
|
+
);
|
|
1098
|
+
lines.push("```");
|
|
1099
|
+
lines.push("");
|
|
1100
|
+
lines.push(STATE_END);
|
|
1101
|
+
lines.push("");
|
|
1102
|
+
lines.push("## kody2 task state");
|
|
1103
|
+
lines.push("");
|
|
1104
|
+
lines.push(`- **Phase:** \`${state.core.phase}\` **Status:** \`${state.core.status}\``);
|
|
1105
|
+
if (state.core.currentExecutable) {
|
|
1106
|
+
lines.push(`- **Last executable:** \`${state.core.currentExecutable}\``);
|
|
1107
|
+
}
|
|
1108
|
+
if (state.core.lastOutcome) {
|
|
1109
|
+
lines.push(`- **Last action:** \`${state.core.lastOutcome.type}\``);
|
|
1110
|
+
}
|
|
1111
|
+
const attempts = Object.entries(state.core.attempts).map(([k, v]) => `${k}:${v}`).join(", ");
|
|
1112
|
+
if (attempts) lines.push(`- **Attempts:** ${attempts}`);
|
|
1113
|
+
if (state.core.prUrl) lines.push(`- **PR:** ${state.core.prUrl}`);
|
|
1114
|
+
if (state.core.runUrl) lines.push(`- **Run:** ${state.core.runUrl}`);
|
|
1115
|
+
const artifactNames = Object.keys(state.artifacts ?? {});
|
|
1116
|
+
if (artifactNames.length > 0) {
|
|
1117
|
+
lines.push(`- **Artifacts:** ${artifactNames.map((n) => `\`${n}\``).join(", ")}`);
|
|
1118
|
+
}
|
|
1119
|
+
lines.push("");
|
|
1120
|
+
if (state.history.length > 0) {
|
|
1121
|
+
lines.push("### Recent history");
|
|
1122
|
+
lines.push("");
|
|
1123
|
+
const recent = state.history.slice(-10).reverse();
|
|
1124
|
+
for (const h of recent) {
|
|
1125
|
+
const note = h.note ? ` \u2014 ${h.note}` : "";
|
|
1126
|
+
lines.push(`- \`${h.timestamp}\` **${h.executable}** \u2192 \`${h.action}\`${note}`);
|
|
1127
|
+
}
|
|
1128
|
+
lines.push("");
|
|
1129
|
+
}
|
|
1130
|
+
return lines.join("\n");
|
|
1131
|
+
}
|
|
1132
|
+
function readTaskState(target, number, cwd) {
|
|
1133
|
+
const existing = findStateComment(target, number, cwd);
|
|
1134
|
+
return existing ? parseStateComment(existing.body) : emptyState();
|
|
1135
|
+
}
|
|
1136
|
+
function setArtifact(state, name, artifact) {
|
|
1137
|
+
return {
|
|
1138
|
+
...state,
|
|
1139
|
+
artifacts: { ...state.artifacts ?? {}, [name]: artifact }
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
function writeTaskState(target, number, state, cwd) {
|
|
1143
|
+
const body = renderStateComment(state);
|
|
1144
|
+
const existing = findStateComment(target, number, cwd);
|
|
1145
|
+
try {
|
|
1146
|
+
if (existing) {
|
|
1147
|
+
gh(["api", `repos/{owner}/{repo}/issues/comments/${existing.id}`, "-X", "PATCH", "-F", "body=@-"], body, cwd);
|
|
1148
|
+
} else {
|
|
1149
|
+
const sub = target === "issue" ? "issue" : "pr";
|
|
1150
|
+
gh([sub, "comment", String(number), "--body-file", "-"], body, cwd);
|
|
1151
|
+
}
|
|
1152
|
+
} catch (err) {
|
|
1153
|
+
process.stderr.write(
|
|
1154
|
+
`[kody2 state] failed to write state on ${target} #${number}: ${err instanceof Error ? err.message : String(err)}
|
|
1155
|
+
`
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// src/scripts/advanceFlow.ts
|
|
1161
|
+
var API_TIMEOUT_MS2 = 3e4;
|
|
1162
|
+
var advanceFlow = async (ctx, profile) => {
|
|
1163
|
+
const state = ctx.data.taskState;
|
|
1164
|
+
const flow = state?.flow;
|
|
1165
|
+
if (!flow?.issueNumber) return;
|
|
1166
|
+
const targetType = ctx.data.commentTargetType;
|
|
1167
|
+
const action = ctx.data.action;
|
|
1168
|
+
if (targetType === "pr" && action) {
|
|
1169
|
+
try {
|
|
1170
|
+
const issueState = readTaskState("issue", flow.issueNumber, ctx.cwd);
|
|
1171
|
+
issueState.flow = flow;
|
|
1172
|
+
const next = reduce(issueState, profile.name, action);
|
|
1173
|
+
if (state?.core.prUrl && !next.core.prUrl) next.core.prUrl = state.core.prUrl;
|
|
1174
|
+
next.flow = flow;
|
|
1175
|
+
writeTaskState("issue", flow.issueNumber, next, ctx.cwd);
|
|
1176
|
+
} catch (err) {
|
|
1177
|
+
process.stderr.write(
|
|
1178
|
+
`[kody2 advanceFlow] failed to mirror action to issue #${flow.issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
1179
|
+
`
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
const body = `@kody2 orchestrate --flow ${flow.name}`;
|
|
1184
|
+
try {
|
|
1185
|
+
execFileSync3("gh", ["issue", "comment", String(flow.issueNumber), "--body", body], {
|
|
1186
|
+
timeout: API_TIMEOUT_MS2,
|
|
1187
|
+
cwd: ctx.cwd,
|
|
1188
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1189
|
+
});
|
|
1190
|
+
} catch (err) {
|
|
1191
|
+
process.stderr.write(
|
|
1192
|
+
`[kody2 advanceFlow] failed to re-trigger orchestrator on issue #${flow.issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
1193
|
+
`
|
|
1194
|
+
);
|
|
1195
|
+
}
|
|
1196
|
+
};
|
|
1197
|
+
|
|
956
1198
|
// src/scripts/buildSyntheticPlugin.ts
|
|
957
1199
|
import * as fs8 from "fs";
|
|
958
1200
|
import * as os2 from "os";
|
|
@@ -1047,7 +1289,7 @@ function copyDir(src, dst) {
|
|
|
1047
1289
|
}
|
|
1048
1290
|
|
|
1049
1291
|
// src/coverage.ts
|
|
1050
|
-
import { execFileSync as
|
|
1292
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
1051
1293
|
function patternToRegex(pattern) {
|
|
1052
1294
|
let s = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
1053
1295
|
s = s.replace(/\*\*\//g, "\xA7S").replace(/\*\*/g, "\xA7A").replace(/\*/g, "[^/]*");
|
|
@@ -1065,7 +1307,7 @@ function renderSiblingPath(file, requireSibling) {
|
|
|
1065
1307
|
}
|
|
1066
1308
|
function safeGit(args, cwd) {
|
|
1067
1309
|
try {
|
|
1068
|
-
return
|
|
1310
|
+
return execFileSync4("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
|
|
1069
1311
|
} catch {
|
|
1070
1312
|
return "";
|
|
1071
1313
|
}
|
|
@@ -1236,10 +1478,10 @@ ${formatMissesForFeedback(misses)}`;
|
|
|
1236
1478
|
};
|
|
1237
1479
|
|
|
1238
1480
|
// src/scripts/commitAndPush.ts
|
|
1239
|
-
import { execFileSync as
|
|
1481
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
1240
1482
|
|
|
1241
1483
|
// src/commit.ts
|
|
1242
|
-
import { execFileSync as
|
|
1484
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
1243
1485
|
import * as fs10 from "fs";
|
|
1244
1486
|
import * as path9 from "path";
|
|
1245
1487
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
@@ -1269,7 +1511,7 @@ var CONVENTIONAL_PREFIXES = [
|
|
|
1269
1511
|
];
|
|
1270
1512
|
function git(args, cwd) {
|
|
1271
1513
|
try {
|
|
1272
|
-
return
|
|
1514
|
+
return execFileSync5("git", args, {
|
|
1273
1515
|
encoding: "utf-8",
|
|
1274
1516
|
timeout: 12e4,
|
|
1275
1517
|
cwd,
|
|
@@ -1327,7 +1569,7 @@ function isForbiddenPath(p) {
|
|
|
1327
1569
|
return false;
|
|
1328
1570
|
}
|
|
1329
1571
|
function listChangedFiles(cwd) {
|
|
1330
|
-
const raw =
|
|
1572
|
+
const raw = execFileSync5("git", ["status", "--porcelain=v1", "-z"], {
|
|
1331
1573
|
encoding: "utf-8",
|
|
1332
1574
|
cwd,
|
|
1333
1575
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -1339,7 +1581,7 @@ function listChangedFiles(cwd) {
|
|
|
1339
1581
|
}
|
|
1340
1582
|
function listFilesInCommit(ref = "HEAD", cwd) {
|
|
1341
1583
|
try {
|
|
1342
|
-
const raw =
|
|
1584
|
+
const raw = execFileSync5("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
|
|
1343
1585
|
encoding: "utf-8",
|
|
1344
1586
|
cwd,
|
|
1345
1587
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -1419,7 +1661,7 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
1419
1661
|
const kind = profile.name;
|
|
1420
1662
|
if (kind === "resolve") {
|
|
1421
1663
|
try {
|
|
1422
|
-
|
|
1664
|
+
execFileSync6("git", ["add", "-A"], { cwd: ctx.cwd, env: { ...process.env, HUSKY: "0" }, stdio: "pipe" });
|
|
1423
1665
|
} catch {
|
|
1424
1666
|
}
|
|
1425
1667
|
} else {
|
|
@@ -1555,18 +1797,61 @@ function formatToolsUsage(profile) {
|
|
|
1555
1797
|
return lines.join("\n");
|
|
1556
1798
|
}
|
|
1557
1799
|
|
|
1800
|
+
// src/scripts/dispatch.ts
|
|
1801
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
1802
|
+
var API_TIMEOUT_MS3 = 3e4;
|
|
1803
|
+
var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
1804
|
+
const next = args?.next;
|
|
1805
|
+
if (!next) {
|
|
1806
|
+
process.stderr.write("[kody2 dispatch] missing `with.next` \u2014 skipping\n");
|
|
1807
|
+
return;
|
|
1808
|
+
}
|
|
1809
|
+
const target = args?.target ?? "issue";
|
|
1810
|
+
const issueNumber = ctx.args.issue;
|
|
1811
|
+
if (!issueNumber) {
|
|
1812
|
+
process.stderr.write("[kody2 dispatch] no --issue arg \u2014 skipping\n");
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
const state = ctx.data.taskState;
|
|
1816
|
+
if (state?.flow) {
|
|
1817
|
+
state.flow.step = next;
|
|
1818
|
+
}
|
|
1819
|
+
const usePr = target === "pr" && state?.core.prUrl;
|
|
1820
|
+
const targetNumber = usePr ? parsePr(state.core.prUrl) ?? issueNumber : issueNumber;
|
|
1821
|
+
const sub = usePr ? "pr" : "issue";
|
|
1822
|
+
const body = `@kody2 ${next}`;
|
|
1823
|
+
try {
|
|
1824
|
+
execFileSync7("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
1825
|
+
timeout: API_TIMEOUT_MS3,
|
|
1826
|
+
cwd: ctx.cwd,
|
|
1827
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1828
|
+
});
|
|
1829
|
+
} catch (err) {
|
|
1830
|
+
process.stderr.write(
|
|
1831
|
+
`[kody2 dispatch] failed to post @kody2 ${next} on ${sub} #${targetNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
1832
|
+
`
|
|
1833
|
+
);
|
|
1834
|
+
}
|
|
1835
|
+
};
|
|
1836
|
+
function parsePr(url) {
|
|
1837
|
+
const m = url.match(/\/pull\/(\d+)(?:[/?#]|$)/);
|
|
1838
|
+
if (!m) return null;
|
|
1839
|
+
const n = parseInt(m[1], 10);
|
|
1840
|
+
return Number.isFinite(n) ? n : null;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1558
1843
|
// src/issue.ts
|
|
1559
|
-
import { execFileSync as
|
|
1560
|
-
var
|
|
1561
|
-
function
|
|
1844
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
1845
|
+
var API_TIMEOUT_MS4 = 3e4;
|
|
1846
|
+
function ghToken2() {
|
|
1562
1847
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
1563
1848
|
}
|
|
1564
|
-
function
|
|
1565
|
-
const token =
|
|
1849
|
+
function gh2(args, options) {
|
|
1850
|
+
const token = ghToken2();
|
|
1566
1851
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
1567
|
-
return
|
|
1852
|
+
return execFileSync8("gh", args, {
|
|
1568
1853
|
encoding: "utf-8",
|
|
1569
|
-
timeout:
|
|
1854
|
+
timeout: API_TIMEOUT_MS4,
|
|
1570
1855
|
cwd: options?.cwd,
|
|
1571
1856
|
env,
|
|
1572
1857
|
input: options?.input,
|
|
@@ -1574,7 +1859,7 @@ function gh(args, options) {
|
|
|
1574
1859
|
}).trim();
|
|
1575
1860
|
}
|
|
1576
1861
|
function getIssue(issueNumber, cwd) {
|
|
1577
|
-
const output =
|
|
1862
|
+
const output = gh2(["issue", "view", String(issueNumber), "--json", "number,title,body,comments"], { cwd });
|
|
1578
1863
|
const parsed = JSON.parse(output);
|
|
1579
1864
|
if (typeof parsed?.title !== "string") {
|
|
1580
1865
|
throw new Error(`Issue #${issueNumber}: unexpected response shape`);
|
|
@@ -1592,7 +1877,7 @@ function getIssue(issueNumber, cwd) {
|
|
|
1592
1877
|
}
|
|
1593
1878
|
function postIssueComment(issueNumber, body, cwd) {
|
|
1594
1879
|
try {
|
|
1595
|
-
|
|
1880
|
+
gh2(["issue", "comment", String(issueNumber), "--body-file", "-"], { input: body, cwd });
|
|
1596
1881
|
} catch (err) {
|
|
1597
1882
|
process.stderr.write(
|
|
1598
1883
|
`[kody2] failed to post comment on #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -1605,7 +1890,7 @@ function truncate2(s, maxBytes) {
|
|
|
1605
1890
|
return `${s.slice(0, maxBytes)}\u2026 (+${s.length - maxBytes} chars)`;
|
|
1606
1891
|
}
|
|
1607
1892
|
function getPr(prNumber, cwd) {
|
|
1608
|
-
const output =
|
|
1893
|
+
const output = gh2(["pr", "view", String(prNumber), "--json", "number,title,body,headRefName,baseRefName,state"], {
|
|
1609
1894
|
cwd
|
|
1610
1895
|
});
|
|
1611
1896
|
const parsed = JSON.parse(output);
|
|
@@ -1623,7 +1908,7 @@ function getPr(prNumber, cwd) {
|
|
|
1623
1908
|
}
|
|
1624
1909
|
function getPrDiff(prNumber, cwd) {
|
|
1625
1910
|
try {
|
|
1626
|
-
return
|
|
1911
|
+
return gh2(["pr", "diff", String(prNumber)], { cwd });
|
|
1627
1912
|
} catch (err) {
|
|
1628
1913
|
process.stderr.write(
|
|
1629
1914
|
`[kody2] failed to fetch diff for PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -1634,7 +1919,7 @@ function getPrDiff(prNumber, cwd) {
|
|
|
1634
1919
|
}
|
|
1635
1920
|
function getPrReviews(prNumber, cwd) {
|
|
1636
1921
|
try {
|
|
1637
|
-
const output =
|
|
1922
|
+
const output = gh2(["pr", "view", String(prNumber), "--json", "reviews"], { cwd });
|
|
1638
1923
|
const parsed = JSON.parse(output);
|
|
1639
1924
|
if (!Array.isArray(parsed?.reviews)) return [];
|
|
1640
1925
|
return parsed.reviews.map(
|
|
@@ -1651,7 +1936,7 @@ function getPrReviews(prNumber, cwd) {
|
|
|
1651
1936
|
}
|
|
1652
1937
|
function getPrComments(prNumber, cwd) {
|
|
1653
1938
|
try {
|
|
1654
|
-
const output =
|
|
1939
|
+
const output = gh2(["pr", "view", String(prNumber), "--json", "comments"], { cwd });
|
|
1655
1940
|
const parsed = JSON.parse(output);
|
|
1656
1941
|
if (!Array.isArray(parsed?.comments)) return [];
|
|
1657
1942
|
return parsed.comments.map((c) => ({
|
|
@@ -1677,7 +1962,7 @@ function getPrLatestReviewBody(prNumber, cwd) {
|
|
|
1677
1962
|
}
|
|
1678
1963
|
function postPrReviewComment(prNumber, body, cwd) {
|
|
1679
1964
|
try {
|
|
1680
|
-
|
|
1965
|
+
gh2(["pr", "comment", String(prNumber), "--body-file", "-"], { input: body, cwd });
|
|
1681
1966
|
} catch (err) {
|
|
1682
1967
|
process.stderr.write(
|
|
1683
1968
|
`[kody2] failed to post review comment on PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -1754,7 +2039,7 @@ function firstLine(s) {
|
|
|
1754
2039
|
}
|
|
1755
2040
|
function findExistingPr(branch, cwd) {
|
|
1756
2041
|
try {
|
|
1757
|
-
const output =
|
|
2042
|
+
const output = gh2(["pr", "view", branch, "--json", "number,url"], { cwd });
|
|
1758
2043
|
const parsed = JSON.parse(output);
|
|
1759
2044
|
if (typeof parsed?.number === "number" && typeof parsed?.url === "string") {
|
|
1760
2045
|
return { number: parsed.number, url: parsed.url };
|
|
@@ -1770,7 +2055,7 @@ function ensurePr(opts) {
|
|
|
1770
2055
|
const existing = findExistingPr(opts.branch, opts.cwd);
|
|
1771
2056
|
if (existing) {
|
|
1772
2057
|
try {
|
|
1773
|
-
|
|
2058
|
+
gh2(["pr", "edit", String(existing.number), "--body-file", "-"], { input: body, cwd: opts.cwd });
|
|
1774
2059
|
} catch (err) {
|
|
1775
2060
|
process.stderr.write(
|
|
1776
2061
|
`[kody2] failed to update PR #${existing.number}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -1792,7 +2077,7 @@ function ensurePr(opts) {
|
|
|
1792
2077
|
"-"
|
|
1793
2078
|
];
|
|
1794
2079
|
if (opts.draft) args.push("--draft");
|
|
1795
|
-
const output =
|
|
2080
|
+
const output = gh2(args, { input: body, cwd: opts.cwd });
|
|
1796
2081
|
const url = output.trim();
|
|
1797
2082
|
const match = url.match(/\/pull\/(\d+)$/);
|
|
1798
2083
|
const number = match ? parseInt(match[1], 10) : 0;
|
|
@@ -1850,8 +2135,43 @@ function computeFailureReason(ctx) {
|
|
|
1850
2135
|
return "";
|
|
1851
2136
|
}
|
|
1852
2137
|
|
|
2138
|
+
// src/scripts/finishFlow.ts
|
|
2139
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
2140
|
+
var API_TIMEOUT_MS5 = 3e4;
|
|
2141
|
+
var STATUS_ICON = {
|
|
2142
|
+
"review-passed": "\u2705",
|
|
2143
|
+
"fix-applied": "\u2705",
|
|
2144
|
+
"review-failed": "\u26A0\uFE0F",
|
|
2145
|
+
aborted: "\u26A0\uFE0F"
|
|
2146
|
+
};
|
|
2147
|
+
var finishFlow = async (ctx, _profile, _agentResult, args) => {
|
|
2148
|
+
const reason = args?.reason ?? "completed";
|
|
2149
|
+
const issueNumber = ctx.args.issue;
|
|
2150
|
+
const state = ctx.data.taskState;
|
|
2151
|
+
const flowName = state?.flow?.name ?? "(unknown flow)";
|
|
2152
|
+
if (state) state.flow = void 0;
|
|
2153
|
+
if (!issueNumber) return;
|
|
2154
|
+
const icon = STATUS_ICON[reason] ?? "\u2139\uFE0F";
|
|
2155
|
+
const prSuffix = state?.core.prUrl ? `
|
|
2156
|
+
|
|
2157
|
+
**PR:** ${state.core.prUrl}` : "";
|
|
2158
|
+
const body = `${icon} kody2 flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
|
|
2159
|
+
try {
|
|
2160
|
+
execFileSync9("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
2161
|
+
timeout: API_TIMEOUT_MS5,
|
|
2162
|
+
cwd: ctx.cwd,
|
|
2163
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2164
|
+
});
|
|
2165
|
+
} catch (err) {
|
|
2166
|
+
process.stderr.write(
|
|
2167
|
+
`[kody2 finishFlow] failed to post final summary on issue #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
2168
|
+
`
|
|
2169
|
+
);
|
|
2170
|
+
}
|
|
2171
|
+
};
|
|
2172
|
+
|
|
1853
2173
|
// src/branch.ts
|
|
1854
|
-
import { execFileSync as
|
|
2174
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
1855
2175
|
var UncommittedChangesError = class extends Error {
|
|
1856
2176
|
constructor(branch) {
|
|
1857
2177
|
super(`Uncommitted changes on branch '${branch}' \u2014 refusing to run to protect work in progress`);
|
|
@@ -1861,7 +2181,7 @@ var UncommittedChangesError = class extends Error {
|
|
|
1861
2181
|
branch;
|
|
1862
2182
|
};
|
|
1863
2183
|
function git2(args, cwd) {
|
|
1864
|
-
return
|
|
2184
|
+
return execFileSync10("git", args, {
|
|
1865
2185
|
encoding: "utf-8",
|
|
1866
2186
|
timeout: 3e4,
|
|
1867
2187
|
cwd,
|
|
@@ -1886,7 +2206,7 @@ function checkoutPrBranch(prNumber, cwd) {
|
|
|
1886
2206
|
SKIP_HOOKS: "1",
|
|
1887
2207
|
GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
|
|
1888
2208
|
};
|
|
1889
|
-
|
|
2209
|
+
execFileSync10("gh", ["pr", "checkout", String(prNumber)], {
|
|
1890
2210
|
cwd,
|
|
1891
2211
|
env,
|
|
1892
2212
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -1953,7 +2273,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch, cwd) {
|
|
|
1953
2273
|
}
|
|
1954
2274
|
|
|
1955
2275
|
// src/gha.ts
|
|
1956
|
-
import { execFileSync as
|
|
2276
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
1957
2277
|
import * as fs12 from "fs";
|
|
1958
2278
|
function getRunUrl() {
|
|
1959
2279
|
const server = process.env.GITHUB_SERVER_URL;
|
|
@@ -1977,7 +2297,7 @@ function reactToTriggerComment(cwd) {
|
|
|
1977
2297
|
if (!commentId || !repo) return;
|
|
1978
2298
|
const token = process.env.KODY_TOKEN?.trim() || process.env.GH_TOKEN || process.env.GITHUB_TOKEN;
|
|
1979
2299
|
try {
|
|
1980
|
-
|
|
2300
|
+
execFileSync11(
|
|
1981
2301
|
"gh",
|
|
1982
2302
|
[
|
|
1983
2303
|
"api",
|
|
@@ -2001,15 +2321,15 @@ function reactToTriggerComment(cwd) {
|
|
|
2001
2321
|
}
|
|
2002
2322
|
|
|
2003
2323
|
// src/workflow.ts
|
|
2004
|
-
import { execFileSync as
|
|
2324
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
2005
2325
|
var GH_TIMEOUT_MS = 3e4;
|
|
2006
|
-
function
|
|
2326
|
+
function ghToken3() {
|
|
2007
2327
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
2008
2328
|
}
|
|
2009
|
-
function
|
|
2010
|
-
const token =
|
|
2329
|
+
function gh3(args, cwd) {
|
|
2330
|
+
const token = ghToken3();
|
|
2011
2331
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
2012
|
-
return
|
|
2332
|
+
return execFileSync12("gh", args, {
|
|
2013
2333
|
encoding: "utf-8",
|
|
2014
2334
|
timeout: GH_TIMEOUT_MS,
|
|
2015
2335
|
cwd,
|
|
@@ -2020,14 +2340,14 @@ function gh2(args, cwd) {
|
|
|
2020
2340
|
function getRecentFailedRunsForPr(prNumber, limit, cwd) {
|
|
2021
2341
|
let headBranch;
|
|
2022
2342
|
try {
|
|
2023
|
-
const out =
|
|
2343
|
+
const out = gh3(["pr", "view", String(prNumber), "--json", "headRefName"], cwd);
|
|
2024
2344
|
headBranch = JSON.parse(out).headRefName;
|
|
2025
2345
|
} catch {
|
|
2026
2346
|
return [];
|
|
2027
2347
|
}
|
|
2028
2348
|
if (!headBranch) return [];
|
|
2029
2349
|
try {
|
|
2030
|
-
const out =
|
|
2350
|
+
const out = gh3(
|
|
2031
2351
|
[
|
|
2032
2352
|
"run",
|
|
2033
2353
|
"list",
|
|
@@ -2058,7 +2378,7 @@ function getRecentFailedRunsForPr(prNumber, limit, cwd) {
|
|
|
2058
2378
|
}
|
|
2059
2379
|
function getFailedRunLogTail(runId, maxBytes, cwd) {
|
|
2060
2380
|
try {
|
|
2061
|
-
const raw =
|
|
2381
|
+
const raw = gh3(["run", "view", String(runId), "--log-failed"], cwd);
|
|
2062
2382
|
if (raw.length <= maxBytes) return raw;
|
|
2063
2383
|
return raw.slice(-maxBytes);
|
|
2064
2384
|
} catch {
|
|
@@ -2193,7 +2513,7 @@ function tryPostPr2(prNumber, body, cwd) {
|
|
|
2193
2513
|
}
|
|
2194
2514
|
|
|
2195
2515
|
// src/scripts/initFlow.ts
|
|
2196
|
-
import { execFileSync as
|
|
2516
|
+
import { execFileSync as execFileSync13 } from "child_process";
|
|
2197
2517
|
import * as fs14 from "fs";
|
|
2198
2518
|
import * as path12 from "path";
|
|
2199
2519
|
|
|
@@ -2275,7 +2595,7 @@ function qualityCommandsFor(pm) {
|
|
|
2275
2595
|
function detectOwnerRepo(cwd) {
|
|
2276
2596
|
let url;
|
|
2277
2597
|
try {
|
|
2278
|
-
url =
|
|
2598
|
+
url = execFileSync13("git", ["remote", "get-url", "origin"], {
|
|
2279
2599
|
cwd,
|
|
2280
2600
|
encoding: "utf-8",
|
|
2281
2601
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2359,7 +2679,7 @@ jobs:
|
|
|
2359
2679
|
`;
|
|
2360
2680
|
function defaultBranchFromGit(cwd) {
|
|
2361
2681
|
try {
|
|
2362
|
-
const ref =
|
|
2682
|
+
const ref = execFileSync13("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
2363
2683
|
cwd,
|
|
2364
2684
|
encoding: "utf-8",
|
|
2365
2685
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2367,7 +2687,7 @@ function defaultBranchFromGit(cwd) {
|
|
|
2367
2687
|
return ref.replace("refs/remotes/origin/", "");
|
|
2368
2688
|
} catch {
|
|
2369
2689
|
try {
|
|
2370
|
-
return
|
|
2690
|
+
return execFileSync13("git", ["branch", "--show-current"], {
|
|
2371
2691
|
cwd,
|
|
2372
2692
|
encoding: "utf-8",
|
|
2373
2693
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2507,214 +2827,48 @@ var loadIssueContext = async (ctx) => {
|
|
|
2507
2827
|
ctx.data.commentTargetNumber = issueNumber;
|
|
2508
2828
|
};
|
|
2509
2829
|
|
|
2510
|
-
// src/
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
return {
|
|
2518
|
-
schemaVersion: 1,
|
|
2519
|
-
core: {
|
|
2520
|
-
phase: "idle",
|
|
2521
|
-
status: "pending",
|
|
2522
|
-
currentExecutable: null,
|
|
2523
|
-
lastOutcome: null,
|
|
2524
|
-
attempts: {}
|
|
2525
|
-
},
|
|
2526
|
-
executables: {},
|
|
2527
|
-
artifacts: {},
|
|
2528
|
-
history: []
|
|
2529
|
-
};
|
|
2530
|
-
}
|
|
2531
|
-
function ghToken3() {
|
|
2532
|
-
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
2533
|
-
}
|
|
2534
|
-
function gh3(args, input, cwd) {
|
|
2535
|
-
const token = ghToken3();
|
|
2536
|
-
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
2537
|
-
return execFileSync10("gh", args, {
|
|
2538
|
-
encoding: "utf-8",
|
|
2539
|
-
timeout: API_TIMEOUT_MS2,
|
|
2540
|
-
cwd,
|
|
2541
|
-
env,
|
|
2542
|
-
input,
|
|
2543
|
-
stdio: input ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"]
|
|
2544
|
-
}).trim();
|
|
2545
|
-
}
|
|
2546
|
-
function findStateComment(target, number, cwd) {
|
|
2547
|
-
const apiPath = target === "issue" ? `repos/{owner}/{repo}/issues/${number}/comments` : `repos/{owner}/{repo}/issues/${number}/comments`;
|
|
2548
|
-
try {
|
|
2549
|
-
const raw = gh3(["api", "--paginate", apiPath], void 0, cwd);
|
|
2550
|
-
const list = JSON.parse(raw);
|
|
2551
|
-
for (const c of list) {
|
|
2552
|
-
if (c.body?.includes(STATE_BEGIN)) {
|
|
2553
|
-
return { id: String(c.id), body: c.body };
|
|
2554
|
-
}
|
|
2555
|
-
}
|
|
2556
|
-
} catch {
|
|
2830
|
+
// src/scripts/loadTaskState.ts
|
|
2831
|
+
var loadTaskState = async (ctx) => {
|
|
2832
|
+
const target = ctx.data.commentTargetType;
|
|
2833
|
+
const number = ctx.data.commentTargetNumber;
|
|
2834
|
+
if (!target || !number) {
|
|
2835
|
+
ctx.data.taskState = emptyState();
|
|
2836
|
+
return;
|
|
2557
2837
|
}
|
|
2558
|
-
|
|
2559
|
-
}
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
const
|
|
2565
|
-
|
|
2566
|
-
const
|
|
2567
|
-
if (!
|
|
2568
|
-
const
|
|
2838
|
+
ctx.data.taskState = readTaskState(target, number, ctx.cwd);
|
|
2839
|
+
};
|
|
2840
|
+
|
|
2841
|
+
// src/scripts/mirrorStateToPr.ts
|
|
2842
|
+
var mirrorStateToPr = async (ctx) => {
|
|
2843
|
+
const issueNumber = ctx.data.commentTargetNumber;
|
|
2844
|
+
const issueTarget = ctx.data.commentTargetType;
|
|
2845
|
+
if (!issueNumber || issueTarget !== "issue") return;
|
|
2846
|
+
const prUrl = ctx.output.prUrl ?? ctx.data.prResult?.url;
|
|
2847
|
+
if (!prUrl) return;
|
|
2848
|
+
const prNumber = parsePrNumber(prUrl);
|
|
2849
|
+
if (!prNumber) return;
|
|
2850
|
+
let state;
|
|
2569
2851
|
try {
|
|
2570
|
-
|
|
2571
|
-
if (parsed?.schemaVersion !== 1) return emptyState();
|
|
2572
|
-
return {
|
|
2573
|
-
schemaVersion: 1,
|
|
2574
|
-
core: { ...emptyState().core, ...parsed.core },
|
|
2575
|
-
executables: parsed.executables ?? {},
|
|
2576
|
-
artifacts: parsed.artifacts && typeof parsed.artifacts === "object" ? parsed.artifacts : {},
|
|
2577
|
-
history: Array.isArray(parsed.history) ? parsed.history : []
|
|
2578
|
-
};
|
|
2852
|
+
state = readTaskState("issue", issueNumber, ctx.cwd);
|
|
2579
2853
|
} catch {
|
|
2580
|
-
return
|
|
2581
|
-
}
|
|
2582
|
-
}
|
|
2583
|
-
function reduce(state, executable, action) {
|
|
2584
|
-
if (!action) return state;
|
|
2585
|
-
const newAttempts = { ...state.core.attempts, [executable]: (state.core.attempts[executable] ?? 0) + 1 };
|
|
2586
|
-
const newExecutables = {
|
|
2587
|
-
...state.executables,
|
|
2588
|
-
[executable]: { ...state.executables[executable] ?? { lastAction: null }, lastAction: action }
|
|
2589
|
-
};
|
|
2590
|
-
const newHistory = [
|
|
2591
|
-
...state.history,
|
|
2592
|
-
{ timestamp: action.timestamp, executable, action: action.type, note: noteFromAction(action) }
|
|
2593
|
-
].slice(-HISTORY_MAX_ENTRIES);
|
|
2594
|
-
return {
|
|
2595
|
-
schemaVersion: 1,
|
|
2596
|
-
core: {
|
|
2597
|
-
...state.core,
|
|
2598
|
-
attempts: newAttempts,
|
|
2599
|
-
lastOutcome: action,
|
|
2600
|
-
currentExecutable: executable,
|
|
2601
|
-
status: statusFromAction(action),
|
|
2602
|
-
phase: phaseFromAction(executable, action)
|
|
2603
|
-
},
|
|
2604
|
-
executables: newExecutables,
|
|
2605
|
-
artifacts: { ...state.artifacts ?? {} },
|
|
2606
|
-
history: newHistory
|
|
2607
|
-
};
|
|
2608
|
-
}
|
|
2609
|
-
function statusFromAction(action) {
|
|
2610
|
-
if (/FAILED$|ERROR$|MISSING$|REJECTED$/i.test(action.type)) return "failed";
|
|
2611
|
-
if (/COMPLETED$|SHIPPED$|MERGED$|SUCCESS$/i.test(action.type)) return "succeeded";
|
|
2612
|
-
return "running";
|
|
2613
|
-
}
|
|
2614
|
-
function phaseFromAction(executable, action) {
|
|
2615
|
-
if (/FAILED$|ERROR$|REJECTED$/i.test(action.type)) return "failed";
|
|
2616
|
-
if (executable === "build") return statusFromAction(action) === "succeeded" ? "implementing" : "implementing";
|
|
2617
|
-
if (executable === "review") return "reviewing";
|
|
2618
|
-
if (executable === "release") return "shipped";
|
|
2619
|
-
return "idle";
|
|
2620
|
-
}
|
|
2621
|
-
function noteFromAction(action) {
|
|
2622
|
-
const p = action.payload;
|
|
2623
|
-
if (typeof p?.prUrl === "string") return p.prUrl;
|
|
2624
|
-
if (typeof p?.reason === "string") return p.reason.slice(0, 120);
|
|
2625
|
-
if (typeof p?.commitMessage === "string") return p.commitMessage.slice(0, 120);
|
|
2626
|
-
return void 0;
|
|
2627
|
-
}
|
|
2628
|
-
function renderStateComment(state) {
|
|
2629
|
-
const lines = [];
|
|
2630
|
-
lines.push(STATE_BEGIN);
|
|
2631
|
-
lines.push("");
|
|
2632
|
-
lines.push("```json");
|
|
2633
|
-
lines.push(
|
|
2634
|
-
JSON.stringify(
|
|
2635
|
-
{
|
|
2636
|
-
schemaVersion: state.schemaVersion,
|
|
2637
|
-
core: state.core,
|
|
2638
|
-
artifacts: state.artifacts ?? {},
|
|
2639
|
-
executables: state.executables,
|
|
2640
|
-
history: state.history
|
|
2641
|
-
},
|
|
2642
|
-
null,
|
|
2643
|
-
2
|
|
2644
|
-
)
|
|
2645
|
-
);
|
|
2646
|
-
lines.push("```");
|
|
2647
|
-
lines.push("");
|
|
2648
|
-
lines.push(STATE_END);
|
|
2649
|
-
lines.push("");
|
|
2650
|
-
lines.push("## kody2 task state");
|
|
2651
|
-
lines.push("");
|
|
2652
|
-
lines.push(`- **Phase:** \`${state.core.phase}\` **Status:** \`${state.core.status}\``);
|
|
2653
|
-
if (state.core.currentExecutable) {
|
|
2654
|
-
lines.push(`- **Last executable:** \`${state.core.currentExecutable}\``);
|
|
2655
|
-
}
|
|
2656
|
-
if (state.core.lastOutcome) {
|
|
2657
|
-
lines.push(`- **Last action:** \`${state.core.lastOutcome.type}\``);
|
|
2658
|
-
}
|
|
2659
|
-
const attempts = Object.entries(state.core.attempts).map(([k, v]) => `${k}:${v}`).join(", ");
|
|
2660
|
-
if (attempts) lines.push(`- **Attempts:** ${attempts}`);
|
|
2661
|
-
if (state.core.prUrl) lines.push(`- **PR:** ${state.core.prUrl}`);
|
|
2662
|
-
if (state.core.runUrl) lines.push(`- **Run:** ${state.core.runUrl}`);
|
|
2663
|
-
const artifactNames = Object.keys(state.artifacts ?? {});
|
|
2664
|
-
if (artifactNames.length > 0) {
|
|
2665
|
-
lines.push(`- **Artifacts:** ${artifactNames.map((n) => `\`${n}\``).join(", ")}`);
|
|
2666
|
-
}
|
|
2667
|
-
lines.push("");
|
|
2668
|
-
if (state.history.length > 0) {
|
|
2669
|
-
lines.push("### Recent history");
|
|
2670
|
-
lines.push("");
|
|
2671
|
-
const recent = state.history.slice(-10).reverse();
|
|
2672
|
-
for (const h of recent) {
|
|
2673
|
-
const note = h.note ? ` \u2014 ${h.note}` : "";
|
|
2674
|
-
lines.push(`- \`${h.timestamp}\` **${h.executable}** \u2192 \`${h.action}\`${note}`);
|
|
2675
|
-
}
|
|
2676
|
-
lines.push("");
|
|
2854
|
+
return;
|
|
2677
2855
|
}
|
|
2678
|
-
|
|
2679
|
-
}
|
|
2680
|
-
function readTaskState(target, number, cwd) {
|
|
2681
|
-
const existing = findStateComment(target, number, cwd);
|
|
2682
|
-
return existing ? parseStateComment(existing.body) : emptyState();
|
|
2683
|
-
}
|
|
2684
|
-
function setArtifact(state, name, artifact) {
|
|
2685
|
-
return {
|
|
2686
|
-
...state,
|
|
2687
|
-
artifacts: { ...state.artifacts ?? {}, [name]: artifact }
|
|
2688
|
-
};
|
|
2689
|
-
}
|
|
2690
|
-
function writeTaskState(target, number, state, cwd) {
|
|
2691
|
-
const body = renderStateComment(state);
|
|
2692
|
-
const existing = findStateComment(target, number, cwd);
|
|
2856
|
+
if (prUrl && !state.core.prUrl) state.core.prUrl = prUrl;
|
|
2693
2857
|
try {
|
|
2694
|
-
|
|
2695
|
-
gh3(["api", `repos/{owner}/{repo}/issues/comments/${existing.id}`, "-X", "PATCH", "-F", "body=@-"], body, cwd);
|
|
2696
|
-
} else {
|
|
2697
|
-
const sub = target === "issue" ? "issue" : "pr";
|
|
2698
|
-
gh3([sub, "comment", String(number), "--body-file", "-"], body, cwd);
|
|
2699
|
-
}
|
|
2858
|
+
writeTaskState("pr", prNumber, state, ctx.cwd);
|
|
2700
2859
|
} catch (err) {
|
|
2701
2860
|
process.stderr.write(
|
|
2702
|
-
`[kody2
|
|
2861
|
+
`[kody2 mirrorStateToPr] failed to mirror state to PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
2703
2862
|
`
|
|
2704
2863
|
);
|
|
2705
2864
|
}
|
|
2706
|
-
}
|
|
2707
|
-
|
|
2708
|
-
// src/scripts/loadTaskState.ts
|
|
2709
|
-
var loadTaskState = async (ctx) => {
|
|
2710
|
-
const target = ctx.data.commentTargetType;
|
|
2711
|
-
const number = ctx.data.commentTargetNumber;
|
|
2712
|
-
if (!target || !number) {
|
|
2713
|
-
ctx.data.taskState = emptyState();
|
|
2714
|
-
return;
|
|
2715
|
-
}
|
|
2716
|
-
ctx.data.taskState = readTaskState(target, number, ctx.cwd);
|
|
2717
2865
|
};
|
|
2866
|
+
function parsePrNumber(prUrl) {
|
|
2867
|
+
const m = prUrl.match(/\/pull\/(\d+)(?:[/?#]|$)/);
|
|
2868
|
+
if (!m) return null;
|
|
2869
|
+
const n = parseInt(m[1], 10);
|
|
2870
|
+
return Number.isFinite(n) ? n : null;
|
|
2871
|
+
}
|
|
2718
2872
|
|
|
2719
2873
|
// src/scripts/parseAgentResult.ts
|
|
2720
2874
|
var parseAgentResult2 = async (ctx, profile, agentResult) => {
|
|
@@ -2938,7 +3092,7 @@ REVIEW_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.github.
|
|
|
2938
3092
|
};
|
|
2939
3093
|
|
|
2940
3094
|
// src/scripts/releaseFlow.ts
|
|
2941
|
-
import { execFileSync as
|
|
3095
|
+
import { execFileSync as execFileSync14, spawnSync } from "child_process";
|
|
2942
3096
|
import * as fs15 from "fs";
|
|
2943
3097
|
import * as path13 from "path";
|
|
2944
3098
|
function bumpVersion(current, bump) {
|
|
@@ -2968,7 +3122,7 @@ function generateChangelog(cwd, newVersion, lastTag) {
|
|
|
2968
3122
|
const range = lastTag ? `${lastTag}..HEAD` : "HEAD";
|
|
2969
3123
|
let log = "";
|
|
2970
3124
|
try {
|
|
2971
|
-
log =
|
|
3125
|
+
log = execFileSync14("git", ["log", range, "--pretty=format:%s||%h", "--no-merges"], {
|
|
2972
3126
|
cwd,
|
|
2973
3127
|
encoding: "utf-8",
|
|
2974
3128
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3025,7 +3179,7 @@ ${entry}${prior.slice(idx + 1)}`);
|
|
|
3025
3179
|
}
|
|
3026
3180
|
}
|
|
3027
3181
|
function git3(args, cwd, timeout = 6e4) {
|
|
3028
|
-
return
|
|
3182
|
+
return execFileSync14("git", args, {
|
|
3029
3183
|
encoding: "utf-8",
|
|
3030
3184
|
timeout,
|
|
3031
3185
|
cwd,
|
|
@@ -3132,7 +3286,7 @@ ${entry}
|
|
|
3132
3286
|
Merge this and then run \`kody2 release --mode finalize\`.`;
|
|
3133
3287
|
let prUrl = "";
|
|
3134
3288
|
try {
|
|
3135
|
-
prUrl =
|
|
3289
|
+
prUrl = gh2(["pr", "create", "--head", releaseBranch, "--base", base, "--title", title, "--body-file", "-"], {
|
|
3136
3290
|
input: body,
|
|
3137
3291
|
cwd
|
|
3138
3292
|
}).trim();
|
|
@@ -3209,7 +3363,7 @@ ${truncate2(r.stderr, 2e3)}
|
|
|
3209
3363
|
try {
|
|
3210
3364
|
const releaseArgs = ["release", "create", tag, "--title", tag, "--notes", `Release ${tag} \u2014 automated by kody2.`];
|
|
3211
3365
|
if (releaseCfg.draftRelease) releaseArgs.push("--draft");
|
|
3212
|
-
releaseUrl =
|
|
3366
|
+
releaseUrl = gh2(releaseArgs, { cwd }).trim();
|
|
3213
3367
|
} catch (err) {
|
|
3214
3368
|
process.stderr.write(
|
|
3215
3369
|
`[kody2 release] gh release create failed: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -3328,7 +3482,7 @@ var resolveArtifacts = async (ctx, profile) => {
|
|
|
3328
3482
|
};
|
|
3329
3483
|
|
|
3330
3484
|
// src/scripts/resolveFlow.ts
|
|
3331
|
-
import { execFileSync as
|
|
3485
|
+
import { execFileSync as execFileSync15 } from "child_process";
|
|
3332
3486
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
3333
3487
|
var resolveFlow = async (ctx) => {
|
|
3334
3488
|
const prNumber = ctx.args.pr;
|
|
@@ -3380,7 +3534,7 @@ var resolveFlow = async (ctx) => {
|
|
|
3380
3534
|
};
|
|
3381
3535
|
function getConflictedFiles(cwd) {
|
|
3382
3536
|
try {
|
|
3383
|
-
const out =
|
|
3537
|
+
const out = execFileSync15("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
3384
3538
|
encoding: "utf-8",
|
|
3385
3539
|
cwd,
|
|
3386
3540
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -3395,7 +3549,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
3395
3549
|
let total = 0;
|
|
3396
3550
|
for (const f of files) {
|
|
3397
3551
|
try {
|
|
3398
|
-
const content =
|
|
3552
|
+
const content = execFileSync15("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
3399
3553
|
const snippet = `### ${f}
|
|
3400
3554
|
|
|
3401
3555
|
\`\`\`
|
|
@@ -3504,8 +3658,67 @@ function synthesizeAction(ctx) {
|
|
|
3504
3658
|
};
|
|
3505
3659
|
}
|
|
3506
3660
|
|
|
3661
|
+
// src/scripts/skipAgent.ts
|
|
3662
|
+
var skipAgent = async (ctx) => {
|
|
3663
|
+
ctx.skipAgent = true;
|
|
3664
|
+
};
|
|
3665
|
+
|
|
3666
|
+
// src/scripts/startFlow.ts
|
|
3667
|
+
import { execFileSync as execFileSync16 } from "child_process";
|
|
3668
|
+
var API_TIMEOUT_MS6 = 3e4;
|
|
3669
|
+
var startFlow = async (ctx, _profile, _agentResult, args) => {
|
|
3670
|
+
const entry = args?.entry;
|
|
3671
|
+
if (!entry) {
|
|
3672
|
+
process.stderr.write("[kody2 startFlow] missing `with.entry` \u2014 skipping\n");
|
|
3673
|
+
return;
|
|
3674
|
+
}
|
|
3675
|
+
const target = args?.target ?? "issue";
|
|
3676
|
+
const flowName = ctx.args.flow ?? "default";
|
|
3677
|
+
const issueNumber = ctx.args.issue;
|
|
3678
|
+
if (!issueNumber) {
|
|
3679
|
+
process.stderr.write("[kody2 startFlow] no --issue arg \u2014 skipping\n");
|
|
3680
|
+
return;
|
|
3681
|
+
}
|
|
3682
|
+
const state = ctx.data.taskState;
|
|
3683
|
+
if (state?.flow) {
|
|
3684
|
+
return;
|
|
3685
|
+
}
|
|
3686
|
+
if (state) {
|
|
3687
|
+
state.flow = {
|
|
3688
|
+
name: flowName,
|
|
3689
|
+
step: entry,
|
|
3690
|
+
issueNumber,
|
|
3691
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3692
|
+
};
|
|
3693
|
+
}
|
|
3694
|
+
postKody2Comment(target, issueNumber, state, entry, ctx.cwd);
|
|
3695
|
+
};
|
|
3696
|
+
function postKody2Comment(target, issueNumber, state, next, cwd) {
|
|
3697
|
+
const targetNumber = target === "pr" && state?.core.prUrl ? parsePr2(state.core.prUrl) ?? issueNumber : issueNumber;
|
|
3698
|
+
const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
|
|
3699
|
+
const body = `@kody2 ${next}`;
|
|
3700
|
+
try {
|
|
3701
|
+
execFileSync16("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
3702
|
+
timeout: API_TIMEOUT_MS6,
|
|
3703
|
+
cwd,
|
|
3704
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3705
|
+
});
|
|
3706
|
+
} catch (err) {
|
|
3707
|
+
process.stderr.write(
|
|
3708
|
+
`[kody2 startFlow] failed to post @kody2 ${next} on ${sub} #${targetNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
3709
|
+
`
|
|
3710
|
+
);
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
function parsePr2(url) {
|
|
3714
|
+
const m = url.match(/\/pull\/(\d+)(?:[/?#]|$)/);
|
|
3715
|
+
if (!m) return null;
|
|
3716
|
+
const n = parseInt(m[1], 10);
|
|
3717
|
+
return Number.isFinite(n) ? n : null;
|
|
3718
|
+
}
|
|
3719
|
+
|
|
3507
3720
|
// src/scripts/syncFlow.ts
|
|
3508
|
-
import { execFileSync as
|
|
3721
|
+
import { execFileSync as execFileSync17 } from "child_process";
|
|
3509
3722
|
var syncFlow = async (ctx) => {
|
|
3510
3723
|
ctx.skipAgent = true;
|
|
3511
3724
|
const prNumber = ctx.args.pr;
|
|
@@ -3564,7 +3777,7 @@ function bail2(ctx, prNumber, reason) {
|
|
|
3564
3777
|
}
|
|
3565
3778
|
function revParseHead(cwd) {
|
|
3566
3779
|
try {
|
|
3567
|
-
return
|
|
3780
|
+
return execFileSync17("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
3568
3781
|
} catch {
|
|
3569
3782
|
return "";
|
|
3570
3783
|
}
|
|
@@ -3572,9 +3785,9 @@ function revParseHead(cwd) {
|
|
|
3572
3785
|
function pushBranch(branch, cwd) {
|
|
3573
3786
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
3574
3787
|
try {
|
|
3575
|
-
|
|
3788
|
+
execFileSync17("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
3576
3789
|
} catch {
|
|
3577
|
-
|
|
3790
|
+
execFileSync17("git", ["push", "--force-with-lease", "-u", "origin", branch], {
|
|
3578
3791
|
cwd,
|
|
3579
3792
|
env,
|
|
3580
3793
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3685,7 +3898,7 @@ function readWatchConfig(ctx) {
|
|
|
3685
3898
|
function findStalePrs(cwd, staleDays, now = /* @__PURE__ */ new Date()) {
|
|
3686
3899
|
let raw = "";
|
|
3687
3900
|
try {
|
|
3688
|
-
raw =
|
|
3901
|
+
raw = gh2(["pr", "list", "--state", "open", "--limit", "100", "--json", "number,title,url,updatedAt"], { cwd });
|
|
3689
3902
|
} catch {
|
|
3690
3903
|
return [];
|
|
3691
3904
|
}
|
|
@@ -3787,7 +4000,8 @@ var preflightScripts = {
|
|
|
3787
4000
|
loadCoverageRules,
|
|
3788
4001
|
buildSyntheticPlugin,
|
|
3789
4002
|
resolveArtifacts,
|
|
3790
|
-
composePrompt
|
|
4003
|
+
composePrompt,
|
|
4004
|
+
skipAgent
|
|
3791
4005
|
};
|
|
3792
4006
|
var postflightScripts = {
|
|
3793
4007
|
parseAgentResult: parseAgentResult2,
|
|
@@ -3803,7 +4017,12 @@ var postflightScripts = {
|
|
|
3803
4017
|
postReviewResult,
|
|
3804
4018
|
persistArtifacts,
|
|
3805
4019
|
writeRunSummary,
|
|
3806
|
-
saveTaskState
|
|
4020
|
+
saveTaskState,
|
|
4021
|
+
mirrorStateToPr,
|
|
4022
|
+
startFlow,
|
|
4023
|
+
dispatch,
|
|
4024
|
+
finishFlow,
|
|
4025
|
+
advanceFlow
|
|
3807
4026
|
};
|
|
3808
4027
|
var allScriptNames = /* @__PURE__ */ new Set([
|
|
3809
4028
|
...Object.keys(preflightScripts),
|
|
@@ -3811,7 +4030,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
3811
4030
|
]);
|
|
3812
4031
|
|
|
3813
4032
|
// src/tools.ts
|
|
3814
|
-
import { execFileSync as
|
|
4033
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
3815
4034
|
function verifyCliTools(tools, cwd) {
|
|
3816
4035
|
const out = [];
|
|
3817
4036
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -3844,7 +4063,7 @@ function verifyOne(tool, cwd) {
|
|
|
3844
4063
|
}
|
|
3845
4064
|
function runShell2(cmd, cwd, timeoutMs = 3e4) {
|
|
3846
4065
|
try {
|
|
3847
|
-
|
|
4066
|
+
execFileSync18("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
3848
4067
|
return true;
|
|
3849
4068
|
} catch {
|
|
3850
4069
|
return false;
|
|
@@ -3941,7 +4160,7 @@ async function runExecutable(profileName, input) {
|
|
|
3941
4160
|
if (!shouldRun(entry, ctx)) continue;
|
|
3942
4161
|
const fn = preflightScripts[entry.script];
|
|
3943
4162
|
if (!fn) return finish({ exitCode: 99, reason: `preflight script not registered: ${entry.script}` });
|
|
3944
|
-
await fn(ctx, profile);
|
|
4163
|
+
await fn(ctx, profile, entry.with);
|
|
3945
4164
|
if (ctx.skipAgent && ctx.output.exitCode !== void 0 && ctx.output.exitCode !== 0) {
|
|
3946
4165
|
return finish(ctx.output);
|
|
3947
4166
|
}
|
|
@@ -3959,7 +4178,7 @@ async function runExecutable(profileName, input) {
|
|
|
3959
4178
|
const fn = postflightScripts[entry.script];
|
|
3960
4179
|
if (!fn) return finish({ exitCode: 99, reason: `postflight script not registered: ${entry.script}` });
|
|
3961
4180
|
try {
|
|
3962
|
-
await fn(ctx, profile, agentResult);
|
|
4181
|
+
await fn(ctx, profile, agentResult, entry.with);
|
|
3963
4182
|
} catch (err) {
|
|
3964
4183
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3965
4184
|
process.stderr.write(`[kody2] postflight script "${entry.script}" crashed: ${msg}
|
|
@@ -4174,7 +4393,7 @@ function detectPackageManager2(cwd) {
|
|
|
4174
4393
|
}
|
|
4175
4394
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
4176
4395
|
try {
|
|
4177
|
-
|
|
4396
|
+
execFileSync19(cmd, args, {
|
|
4178
4397
|
cwd,
|
|
4179
4398
|
stdio: stream ? "inherit" : "pipe",
|
|
4180
4399
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -4187,7 +4406,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
4187
4406
|
}
|
|
4188
4407
|
function isOnPath(bin) {
|
|
4189
4408
|
try {
|
|
4190
|
-
|
|
4409
|
+
execFileSync19("which", [bin], { stdio: "pipe" });
|
|
4191
4410
|
return true;
|
|
4192
4411
|
} catch {
|
|
4193
4412
|
return false;
|
|
@@ -4221,7 +4440,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
4221
4440
|
} catch {
|
|
4222
4441
|
}
|
|
4223
4442
|
try {
|
|
4224
|
-
|
|
4443
|
+
execFileSync19("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
4225
4444
|
process.stdout.write("\u2192 kody2: litellm already installed\n");
|
|
4226
4445
|
return 0;
|
|
4227
4446
|
} catch {
|
|
@@ -4231,16 +4450,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
4231
4450
|
}
|
|
4232
4451
|
function configureGitIdentity(cwd) {
|
|
4233
4452
|
try {
|
|
4234
|
-
const name =
|
|
4453
|
+
const name = execFileSync19("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
4235
4454
|
if (name) return;
|
|
4236
4455
|
} catch {
|
|
4237
4456
|
}
|
|
4238
4457
|
try {
|
|
4239
|
-
|
|
4458
|
+
execFileSync19("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
4240
4459
|
} catch {
|
|
4241
4460
|
}
|
|
4242
4461
|
try {
|
|
4243
|
-
|
|
4462
|
+
execFileSync19("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
4244
4463
|
cwd,
|
|
4245
4464
|
stdio: "pipe"
|
|
4246
4465
|
});
|
|
@@ -4296,13 +4515,13 @@ async function runCi(argv) {
|
|
|
4296
4515
|
${CI_HELP}`);
|
|
4297
4516
|
return 64;
|
|
4298
4517
|
}
|
|
4299
|
-
const
|
|
4518
|
+
const dispatch2 = autoFallback ?? {
|
|
4300
4519
|
executable: "run",
|
|
4301
4520
|
cliArgs: { issue: args.issueNumber },
|
|
4302
4521
|
target: args.issueNumber
|
|
4303
4522
|
};
|
|
4304
|
-
const issueNumber =
|
|
4305
|
-
process.stdout.write(`\u2192 kody2 preflight (cwd=${cwd}, executable=${
|
|
4523
|
+
const issueNumber = dispatch2.target;
|
|
4524
|
+
process.stdout.write(`\u2192 kody2 preflight (cwd=${cwd}, executable=${dispatch2.executable}, target=${issueNumber})
|
|
4306
4525
|
`);
|
|
4307
4526
|
try {
|
|
4308
4527
|
const n = unpackAllSecrets();
|
|
@@ -4339,13 +4558,13 @@ ${CI_HELP}`);
|
|
|
4339
4558
|
postFailureTail(issueNumber, cwd, `preflight crashed: ${msg}`);
|
|
4340
4559
|
return 99;
|
|
4341
4560
|
}
|
|
4342
|
-
process.stdout.write(`\u2192 kody2: preflight done, handing off to kody2 ${
|
|
4561
|
+
process.stdout.write(`\u2192 kody2: preflight done, handing off to kody2 ${dispatch2.executable}
|
|
4343
4562
|
|
|
4344
4563
|
`);
|
|
4345
4564
|
try {
|
|
4346
4565
|
const config = earlyConfig ?? loadConfig(cwd);
|
|
4347
|
-
const result = await runExecutable(
|
|
4348
|
-
cliArgs:
|
|
4566
|
+
const result = await runExecutable(dispatch2.executable, {
|
|
4567
|
+
cliArgs: dispatch2.cliArgs,
|
|
4349
4568
|
cwd,
|
|
4350
4569
|
config,
|
|
4351
4570
|
verbose: args.verbose,
|
|
@@ -4417,9 +4636,9 @@ function commitChatFiles(cwd, sessionId, verbose) {
|
|
|
4417
4636
|
if (paths.length === 0) return;
|
|
4418
4637
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
4419
4638
|
try {
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4639
|
+
execFileSync20("git", ["add", ...paths], opts);
|
|
4640
|
+
execFileSync20("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
4641
|
+
execFileSync20("git", ["push", "--quiet", "origin", "HEAD"], opts);
|
|
4423
4642
|
} catch (err) {
|
|
4424
4643
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4425
4644
|
process.stderr.write(`[kody2:chat] commit/push skipped: ${msg}
|
|
@@ -140,6 +140,12 @@ export interface ScriptEntry {
|
|
|
140
140
|
* The script runs only when every key matches. Missing `runWhen` = always.
|
|
141
141
|
*/
|
|
142
142
|
runWhen?: Record<string, string | number | boolean | Array<string | number | boolean>>
|
|
143
|
+
/**
|
|
144
|
+
* Optional per-call arguments passed to the script as the last positional
|
|
145
|
+
* parameter. Used by the orchestrator's transition table so the same
|
|
146
|
+
* dispatcher script can be reused with different `next` targets.
|
|
147
|
+
*/
|
|
148
|
+
with?: Record<string, string | number | boolean>
|
|
143
149
|
}
|
|
144
150
|
|
|
145
151
|
export interface OutputContract {
|
|
@@ -183,9 +189,16 @@ export interface Context {
|
|
|
183
189
|
// Script signatures. Two phases, two contracts.
|
|
184
190
|
// ────────────────────────────────────────────────────────────────────────────
|
|
185
191
|
|
|
186
|
-
export type
|
|
192
|
+
export type ScriptArgs = Record<string, string | number | boolean>
|
|
193
|
+
|
|
194
|
+
export type PreflightScript = (ctx: Context, profile: Profile, args?: ScriptArgs) => Promise<void>
|
|
187
195
|
|
|
188
|
-
export type PostflightScript = (
|
|
196
|
+
export type PostflightScript = (
|
|
197
|
+
ctx: Context,
|
|
198
|
+
profile: Profile,
|
|
199
|
+
agentResult: AgentResult | null,
|
|
200
|
+
args?: ScriptArgs,
|
|
201
|
+
) => Promise<void>
|
|
189
202
|
|
|
190
203
|
/** A registered script may be either phase; registry looks it up by name. */
|
|
191
204
|
export type AnyScript = PreflightScript | PostflightScript
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.46",
|
|
4
4
|
"description": "kody2 — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|