@kody-ade/kody-engine 0.2.45 → 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,204 +2827,6 @@ var loadIssueContext = async (ctx) => {
|
|
|
2507
2827
|
ctx.data.commentTargetNumber = issueNumber;
|
|
2508
2828
|
};
|
|
2509
2829
|
|
|
2510
|
-
// src/state.ts
|
|
2511
|
-
import { execFileSync as execFileSync10 } from "child_process";
|
|
2512
|
-
var STATE_BEGIN = "<!-- kody2:state:v1:begin -->";
|
|
2513
|
-
var STATE_END = "<!-- kody2:state:v1:end -->";
|
|
2514
|
-
var HISTORY_MAX_ENTRIES = 20;
|
|
2515
|
-
var API_TIMEOUT_MS2 = 3e4;
|
|
2516
|
-
function emptyState() {
|
|
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 {
|
|
2557
|
-
}
|
|
2558
|
-
return null;
|
|
2559
|
-
}
|
|
2560
|
-
function parseStateComment(body) {
|
|
2561
|
-
const beginIdx = body.indexOf(STATE_BEGIN);
|
|
2562
|
-
const endIdx = body.indexOf(STATE_END, beginIdx + 1);
|
|
2563
|
-
if (beginIdx < 0 || endIdx < 0) return emptyState();
|
|
2564
|
-
const between = body.slice(beginIdx + STATE_BEGIN.length, endIdx).trim();
|
|
2565
|
-
const OPEN = "```json";
|
|
2566
|
-
const CLOSE = "```";
|
|
2567
|
-
if (!between.startsWith(OPEN) || !between.endsWith(CLOSE)) return emptyState();
|
|
2568
|
-
const jsonStr = between.slice(OPEN.length, between.length - CLOSE.length).trim();
|
|
2569
|
-
try {
|
|
2570
|
-
const parsed = JSON.parse(jsonStr);
|
|
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
|
-
};
|
|
2579
|
-
} catch {
|
|
2580
|
-
return emptyState();
|
|
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("");
|
|
2677
|
-
}
|
|
2678
|
-
return lines.join("\n");
|
|
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);
|
|
2693
|
-
try {
|
|
2694
|
-
if (existing) {
|
|
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
|
-
}
|
|
2700
|
-
} catch (err) {
|
|
2701
|
-
process.stderr.write(
|
|
2702
|
-
`[kody2 state] failed to write state on ${target} #${number}: ${err instanceof Error ? err.message : String(err)}
|
|
2703
|
-
`
|
|
2704
|
-
);
|
|
2705
|
-
}
|
|
2706
|
-
}
|
|
2707
|
-
|
|
2708
2830
|
// src/scripts/loadTaskState.ts
|
|
2709
2831
|
var loadTaskState = async (ctx) => {
|
|
2710
2832
|
const target = ctx.data.commentTargetType;
|
|
@@ -2970,7 +3092,7 @@ REVIEW_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.github.
|
|
|
2970
3092
|
};
|
|
2971
3093
|
|
|
2972
3094
|
// src/scripts/releaseFlow.ts
|
|
2973
|
-
import { execFileSync as
|
|
3095
|
+
import { execFileSync as execFileSync14, spawnSync } from "child_process";
|
|
2974
3096
|
import * as fs15 from "fs";
|
|
2975
3097
|
import * as path13 from "path";
|
|
2976
3098
|
function bumpVersion(current, bump) {
|
|
@@ -3000,7 +3122,7 @@ function generateChangelog(cwd, newVersion, lastTag) {
|
|
|
3000
3122
|
const range = lastTag ? `${lastTag}..HEAD` : "HEAD";
|
|
3001
3123
|
let log = "";
|
|
3002
3124
|
try {
|
|
3003
|
-
log =
|
|
3125
|
+
log = execFileSync14("git", ["log", range, "--pretty=format:%s||%h", "--no-merges"], {
|
|
3004
3126
|
cwd,
|
|
3005
3127
|
encoding: "utf-8",
|
|
3006
3128
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3057,7 +3179,7 @@ ${entry}${prior.slice(idx + 1)}`);
|
|
|
3057
3179
|
}
|
|
3058
3180
|
}
|
|
3059
3181
|
function git3(args, cwd, timeout = 6e4) {
|
|
3060
|
-
return
|
|
3182
|
+
return execFileSync14("git", args, {
|
|
3061
3183
|
encoding: "utf-8",
|
|
3062
3184
|
timeout,
|
|
3063
3185
|
cwd,
|
|
@@ -3164,7 +3286,7 @@ ${entry}
|
|
|
3164
3286
|
Merge this and then run \`kody2 release --mode finalize\`.`;
|
|
3165
3287
|
let prUrl = "";
|
|
3166
3288
|
try {
|
|
3167
|
-
prUrl =
|
|
3289
|
+
prUrl = gh2(["pr", "create", "--head", releaseBranch, "--base", base, "--title", title, "--body-file", "-"], {
|
|
3168
3290
|
input: body,
|
|
3169
3291
|
cwd
|
|
3170
3292
|
}).trim();
|
|
@@ -3241,7 +3363,7 @@ ${truncate2(r.stderr, 2e3)}
|
|
|
3241
3363
|
try {
|
|
3242
3364
|
const releaseArgs = ["release", "create", tag, "--title", tag, "--notes", `Release ${tag} \u2014 automated by kody2.`];
|
|
3243
3365
|
if (releaseCfg.draftRelease) releaseArgs.push("--draft");
|
|
3244
|
-
releaseUrl =
|
|
3366
|
+
releaseUrl = gh2(releaseArgs, { cwd }).trim();
|
|
3245
3367
|
} catch (err) {
|
|
3246
3368
|
process.stderr.write(
|
|
3247
3369
|
`[kody2 release] gh release create failed: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -3360,7 +3482,7 @@ var resolveArtifacts = async (ctx, profile) => {
|
|
|
3360
3482
|
};
|
|
3361
3483
|
|
|
3362
3484
|
// src/scripts/resolveFlow.ts
|
|
3363
|
-
import { execFileSync as
|
|
3485
|
+
import { execFileSync as execFileSync15 } from "child_process";
|
|
3364
3486
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
3365
3487
|
var resolveFlow = async (ctx) => {
|
|
3366
3488
|
const prNumber = ctx.args.pr;
|
|
@@ -3412,7 +3534,7 @@ var resolveFlow = async (ctx) => {
|
|
|
3412
3534
|
};
|
|
3413
3535
|
function getConflictedFiles(cwd) {
|
|
3414
3536
|
try {
|
|
3415
|
-
const out =
|
|
3537
|
+
const out = execFileSync15("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
3416
3538
|
encoding: "utf-8",
|
|
3417
3539
|
cwd,
|
|
3418
3540
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -3427,7 +3549,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
3427
3549
|
let total = 0;
|
|
3428
3550
|
for (const f of files) {
|
|
3429
3551
|
try {
|
|
3430
|
-
const content =
|
|
3552
|
+
const content = execFileSync15("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
3431
3553
|
const snippet = `### ${f}
|
|
3432
3554
|
|
|
3433
3555
|
\`\`\`
|
|
@@ -3541,8 +3663,62 @@ var skipAgent = async (ctx) => {
|
|
|
3541
3663
|
ctx.skipAgent = true;
|
|
3542
3664
|
};
|
|
3543
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
|
+
|
|
3544
3720
|
// src/scripts/syncFlow.ts
|
|
3545
|
-
import { execFileSync as
|
|
3721
|
+
import { execFileSync as execFileSync17 } from "child_process";
|
|
3546
3722
|
var syncFlow = async (ctx) => {
|
|
3547
3723
|
ctx.skipAgent = true;
|
|
3548
3724
|
const prNumber = ctx.args.pr;
|
|
@@ -3601,7 +3777,7 @@ function bail2(ctx, prNumber, reason) {
|
|
|
3601
3777
|
}
|
|
3602
3778
|
function revParseHead(cwd) {
|
|
3603
3779
|
try {
|
|
3604
|
-
return
|
|
3780
|
+
return execFileSync17("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
3605
3781
|
} catch {
|
|
3606
3782
|
return "";
|
|
3607
3783
|
}
|
|
@@ -3609,9 +3785,9 @@ function revParseHead(cwd) {
|
|
|
3609
3785
|
function pushBranch(branch, cwd) {
|
|
3610
3786
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
3611
3787
|
try {
|
|
3612
|
-
|
|
3788
|
+
execFileSync17("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
3613
3789
|
} catch {
|
|
3614
|
-
|
|
3790
|
+
execFileSync17("git", ["push", "--force-with-lease", "-u", "origin", branch], {
|
|
3615
3791
|
cwd,
|
|
3616
3792
|
env,
|
|
3617
3793
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3722,7 +3898,7 @@ function readWatchConfig(ctx) {
|
|
|
3722
3898
|
function findStalePrs(cwd, staleDays, now = /* @__PURE__ */ new Date()) {
|
|
3723
3899
|
let raw = "";
|
|
3724
3900
|
try {
|
|
3725
|
-
raw =
|
|
3901
|
+
raw = gh2(["pr", "list", "--state", "open", "--limit", "100", "--json", "number,title,url,updatedAt"], { cwd });
|
|
3726
3902
|
} catch {
|
|
3727
3903
|
return [];
|
|
3728
3904
|
}
|
|
@@ -3842,7 +4018,11 @@ var postflightScripts = {
|
|
|
3842
4018
|
persistArtifacts,
|
|
3843
4019
|
writeRunSummary,
|
|
3844
4020
|
saveTaskState,
|
|
3845
|
-
mirrorStateToPr
|
|
4021
|
+
mirrorStateToPr,
|
|
4022
|
+
startFlow,
|
|
4023
|
+
dispatch,
|
|
4024
|
+
finishFlow,
|
|
4025
|
+
advanceFlow
|
|
3846
4026
|
};
|
|
3847
4027
|
var allScriptNames = /* @__PURE__ */ new Set([
|
|
3848
4028
|
...Object.keys(preflightScripts),
|
|
@@ -3850,7 +4030,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
3850
4030
|
]);
|
|
3851
4031
|
|
|
3852
4032
|
// src/tools.ts
|
|
3853
|
-
import { execFileSync as
|
|
4033
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
3854
4034
|
function verifyCliTools(tools, cwd) {
|
|
3855
4035
|
const out = [];
|
|
3856
4036
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -3883,7 +4063,7 @@ function verifyOne(tool, cwd) {
|
|
|
3883
4063
|
}
|
|
3884
4064
|
function runShell2(cmd, cwd, timeoutMs = 3e4) {
|
|
3885
4065
|
try {
|
|
3886
|
-
|
|
4066
|
+
execFileSync18("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
3887
4067
|
return true;
|
|
3888
4068
|
} catch {
|
|
3889
4069
|
return false;
|
|
@@ -3980,7 +4160,7 @@ async function runExecutable(profileName, input) {
|
|
|
3980
4160
|
if (!shouldRun(entry, ctx)) continue;
|
|
3981
4161
|
const fn = preflightScripts[entry.script];
|
|
3982
4162
|
if (!fn) return finish({ exitCode: 99, reason: `preflight script not registered: ${entry.script}` });
|
|
3983
|
-
await fn(ctx, profile);
|
|
4163
|
+
await fn(ctx, profile, entry.with);
|
|
3984
4164
|
if (ctx.skipAgent && ctx.output.exitCode !== void 0 && ctx.output.exitCode !== 0) {
|
|
3985
4165
|
return finish(ctx.output);
|
|
3986
4166
|
}
|
|
@@ -3998,7 +4178,7 @@ async function runExecutable(profileName, input) {
|
|
|
3998
4178
|
const fn = postflightScripts[entry.script];
|
|
3999
4179
|
if (!fn) return finish({ exitCode: 99, reason: `postflight script not registered: ${entry.script}` });
|
|
4000
4180
|
try {
|
|
4001
|
-
await fn(ctx, profile, agentResult);
|
|
4181
|
+
await fn(ctx, profile, agentResult, entry.with);
|
|
4002
4182
|
} catch (err) {
|
|
4003
4183
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4004
4184
|
process.stderr.write(`[kody2] postflight script "${entry.script}" crashed: ${msg}
|
|
@@ -4213,7 +4393,7 @@ function detectPackageManager2(cwd) {
|
|
|
4213
4393
|
}
|
|
4214
4394
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
4215
4395
|
try {
|
|
4216
|
-
|
|
4396
|
+
execFileSync19(cmd, args, {
|
|
4217
4397
|
cwd,
|
|
4218
4398
|
stdio: stream ? "inherit" : "pipe",
|
|
4219
4399
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -4226,7 +4406,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
4226
4406
|
}
|
|
4227
4407
|
function isOnPath(bin) {
|
|
4228
4408
|
try {
|
|
4229
|
-
|
|
4409
|
+
execFileSync19("which", [bin], { stdio: "pipe" });
|
|
4230
4410
|
return true;
|
|
4231
4411
|
} catch {
|
|
4232
4412
|
return false;
|
|
@@ -4260,7 +4440,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
4260
4440
|
} catch {
|
|
4261
4441
|
}
|
|
4262
4442
|
try {
|
|
4263
|
-
|
|
4443
|
+
execFileSync19("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
4264
4444
|
process.stdout.write("\u2192 kody2: litellm already installed\n");
|
|
4265
4445
|
return 0;
|
|
4266
4446
|
} catch {
|
|
@@ -4270,16 +4450,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
4270
4450
|
}
|
|
4271
4451
|
function configureGitIdentity(cwd) {
|
|
4272
4452
|
try {
|
|
4273
|
-
const name =
|
|
4453
|
+
const name = execFileSync19("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
4274
4454
|
if (name) return;
|
|
4275
4455
|
} catch {
|
|
4276
4456
|
}
|
|
4277
4457
|
try {
|
|
4278
|
-
|
|
4458
|
+
execFileSync19("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
4279
4459
|
} catch {
|
|
4280
4460
|
}
|
|
4281
4461
|
try {
|
|
4282
|
-
|
|
4462
|
+
execFileSync19("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
4283
4463
|
cwd,
|
|
4284
4464
|
stdio: "pipe"
|
|
4285
4465
|
});
|
|
@@ -4335,13 +4515,13 @@ async function runCi(argv) {
|
|
|
4335
4515
|
${CI_HELP}`);
|
|
4336
4516
|
return 64;
|
|
4337
4517
|
}
|
|
4338
|
-
const
|
|
4518
|
+
const dispatch2 = autoFallback ?? {
|
|
4339
4519
|
executable: "run",
|
|
4340
4520
|
cliArgs: { issue: args.issueNumber },
|
|
4341
4521
|
target: args.issueNumber
|
|
4342
4522
|
};
|
|
4343
|
-
const issueNumber =
|
|
4344
|
-
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})
|
|
4345
4525
|
`);
|
|
4346
4526
|
try {
|
|
4347
4527
|
const n = unpackAllSecrets();
|
|
@@ -4378,13 +4558,13 @@ ${CI_HELP}`);
|
|
|
4378
4558
|
postFailureTail(issueNumber, cwd, `preflight crashed: ${msg}`);
|
|
4379
4559
|
return 99;
|
|
4380
4560
|
}
|
|
4381
|
-
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}
|
|
4382
4562
|
|
|
4383
4563
|
`);
|
|
4384
4564
|
try {
|
|
4385
4565
|
const config = earlyConfig ?? loadConfig(cwd);
|
|
4386
|
-
const result = await runExecutable(
|
|
4387
|
-
cliArgs:
|
|
4566
|
+
const result = await runExecutable(dispatch2.executable, {
|
|
4567
|
+
cliArgs: dispatch2.cliArgs,
|
|
4388
4568
|
cwd,
|
|
4389
4569
|
config,
|
|
4390
4570
|
verbose: args.verbose,
|
|
@@ -4456,9 +4636,9 @@ function commitChatFiles(cwd, sessionId, verbose) {
|
|
|
4456
4636
|
if (paths.length === 0) return;
|
|
4457
4637
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
4458
4638
|
try {
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
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);
|
|
4462
4642
|
} catch (err) {
|
|
4463
4643
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4464
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",
|