@kody-ade/kody-engine 0.2.45 → 0.2.47
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 +493 -277
- package/dist/executables/fix/profile.json +3 -0
- package/dist/executables/orchestrator-plan-build-review/profile.json +76 -0
- package/dist/executables/orchestrator-plan-build-review/prompt.md +7 -0
- package/dist/executables/plan/profile.json +3 -0
- package/dist/executables/review/profile.json +3 -0
- package/dist/executables/run/profile.json +3 -0
- package/dist/executables/types.ts +15 -2
- package/package.json +1 -1
- package/dist/executables/orchestrator/profile.json +0 -67
- package/dist/executables/orchestrator/prompt.md +0 -56
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.47",
|
|
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
|
|
|
@@ -591,7 +591,19 @@ function autoDispatch(opts) {
|
|
|
591
591
|
return asDispatch(defaultExec, targetNum);
|
|
592
592
|
}
|
|
593
593
|
if (sub === "orchestrate" || sub === "orchestrator") {
|
|
594
|
-
|
|
594
|
+
const flow = extractFlowName(afterTag);
|
|
595
|
+
if (flow) {
|
|
596
|
+
return {
|
|
597
|
+
executable: `orchestrator-${flow}`,
|
|
598
|
+
cliArgs: { issue: targetNum, flow },
|
|
599
|
+
target: targetNum
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
return {
|
|
603
|
+
executable: "orchestrator-plan-build-review",
|
|
604
|
+
cliArgs: { issue: targetNum, flow: "plan-build-review" },
|
|
605
|
+
target: targetNum
|
|
606
|
+
};
|
|
595
607
|
}
|
|
596
608
|
if (sub === "build") {
|
|
597
609
|
return { executable: "run", cliArgs: { issue: targetNum }, target: targetNum };
|
|
@@ -611,6 +623,10 @@ function extractSubcommand(afterTag) {
|
|
|
611
623
|
if (!match) return null;
|
|
612
624
|
return match[1];
|
|
613
625
|
}
|
|
626
|
+
function extractFlowName(afterTag) {
|
|
627
|
+
const match = afterTag.match(/--flow[=\s]+([a-z][a-z0-9-]{0,60})/);
|
|
628
|
+
return match ? match[1] : null;
|
|
629
|
+
}
|
|
614
630
|
function extractFeedback(afterTag) {
|
|
615
631
|
const cleaned = afterTag.replace(/^(fix|please|kindly)(?:[\s:,.-]+|$)/i, "").trim();
|
|
616
632
|
return cleaned.length > 0 ? cleaned : void 0;
|
|
@@ -948,11 +964,256 @@ function parseScriptList(p, key, raw) {
|
|
|
948
964
|
if (r.runWhen && typeof r.runWhen === "object") {
|
|
949
965
|
entry.runWhen = r.runWhen;
|
|
950
966
|
}
|
|
967
|
+
if (r.with && typeof r.with === "object") {
|
|
968
|
+
entry.with = r.with;
|
|
969
|
+
}
|
|
951
970
|
out.push(entry);
|
|
952
971
|
}
|
|
953
972
|
return out;
|
|
954
973
|
}
|
|
955
974
|
|
|
975
|
+
// src/scripts/advanceFlow.ts
|
|
976
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
977
|
+
|
|
978
|
+
// src/state.ts
|
|
979
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
980
|
+
var STATE_BEGIN = "<!-- kody2:state:v1:begin -->";
|
|
981
|
+
var STATE_END = "<!-- kody2:state:v1:end -->";
|
|
982
|
+
var HISTORY_MAX_ENTRIES = 20;
|
|
983
|
+
var API_TIMEOUT_MS = 3e4;
|
|
984
|
+
function emptyState() {
|
|
985
|
+
return {
|
|
986
|
+
schemaVersion: 1,
|
|
987
|
+
core: {
|
|
988
|
+
phase: "idle",
|
|
989
|
+
status: "pending",
|
|
990
|
+
currentExecutable: null,
|
|
991
|
+
lastOutcome: null,
|
|
992
|
+
attempts: {}
|
|
993
|
+
},
|
|
994
|
+
executables: {},
|
|
995
|
+
artifacts: {},
|
|
996
|
+
history: []
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
function ghToken() {
|
|
1000
|
+
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
1001
|
+
}
|
|
1002
|
+
function gh(args, input, cwd) {
|
|
1003
|
+
const token = ghToken();
|
|
1004
|
+
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
1005
|
+
return execFileSync2("gh", args, {
|
|
1006
|
+
encoding: "utf-8",
|
|
1007
|
+
timeout: API_TIMEOUT_MS,
|
|
1008
|
+
cwd,
|
|
1009
|
+
env,
|
|
1010
|
+
input,
|
|
1011
|
+
stdio: input ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"]
|
|
1012
|
+
}).trim();
|
|
1013
|
+
}
|
|
1014
|
+
function findStateComment(target, number, cwd) {
|
|
1015
|
+
const apiPath = target === "issue" ? `repos/{owner}/{repo}/issues/${number}/comments` : `repos/{owner}/{repo}/issues/${number}/comments`;
|
|
1016
|
+
try {
|
|
1017
|
+
const raw = gh(["api", "--paginate", apiPath], void 0, cwd);
|
|
1018
|
+
const list = JSON.parse(raw);
|
|
1019
|
+
for (const c of list) {
|
|
1020
|
+
if (c.body?.includes(STATE_BEGIN)) {
|
|
1021
|
+
return { id: String(c.id), body: c.body };
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
} catch {
|
|
1025
|
+
}
|
|
1026
|
+
return null;
|
|
1027
|
+
}
|
|
1028
|
+
function parseStateComment(body) {
|
|
1029
|
+
const beginIdx = body.indexOf(STATE_BEGIN);
|
|
1030
|
+
const endIdx = body.indexOf(STATE_END, beginIdx + 1);
|
|
1031
|
+
if (beginIdx < 0 || endIdx < 0) return emptyState();
|
|
1032
|
+
const between = body.slice(beginIdx + STATE_BEGIN.length, endIdx).trim();
|
|
1033
|
+
const OPEN = "```json";
|
|
1034
|
+
const CLOSE = "```";
|
|
1035
|
+
if (!between.startsWith(OPEN) || !between.endsWith(CLOSE)) return emptyState();
|
|
1036
|
+
const jsonStr = between.slice(OPEN.length, between.length - CLOSE.length).trim();
|
|
1037
|
+
try {
|
|
1038
|
+
const parsed = JSON.parse(jsonStr);
|
|
1039
|
+
if (parsed?.schemaVersion !== 1) return emptyState();
|
|
1040
|
+
return {
|
|
1041
|
+
schemaVersion: 1,
|
|
1042
|
+
core: { ...emptyState().core, ...parsed.core },
|
|
1043
|
+
executables: parsed.executables ?? {},
|
|
1044
|
+
artifacts: parsed.artifacts && typeof parsed.artifacts === "object" ? parsed.artifacts : {},
|
|
1045
|
+
history: Array.isArray(parsed.history) ? parsed.history : [],
|
|
1046
|
+
flow: parsed.flow
|
|
1047
|
+
};
|
|
1048
|
+
} catch {
|
|
1049
|
+
return emptyState();
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
function reduce(state, executable, action) {
|
|
1053
|
+
if (!action) return state;
|
|
1054
|
+
const newAttempts = { ...state.core.attempts, [executable]: (state.core.attempts[executable] ?? 0) + 1 };
|
|
1055
|
+
const newExecutables = {
|
|
1056
|
+
...state.executables,
|
|
1057
|
+
[executable]: { ...state.executables[executable] ?? { lastAction: null }, lastAction: action }
|
|
1058
|
+
};
|
|
1059
|
+
const newHistory = [
|
|
1060
|
+
...state.history,
|
|
1061
|
+
{ timestamp: action.timestamp, executable, action: action.type, note: noteFromAction(action) }
|
|
1062
|
+
].slice(-HISTORY_MAX_ENTRIES);
|
|
1063
|
+
return {
|
|
1064
|
+
schemaVersion: 1,
|
|
1065
|
+
core: {
|
|
1066
|
+
...state.core,
|
|
1067
|
+
attempts: newAttempts,
|
|
1068
|
+
lastOutcome: action,
|
|
1069
|
+
currentExecutable: executable,
|
|
1070
|
+
status: statusFromAction(action),
|
|
1071
|
+
phase: phaseFromAction(executable, action)
|
|
1072
|
+
},
|
|
1073
|
+
executables: newExecutables,
|
|
1074
|
+
artifacts: { ...state.artifacts ?? {} },
|
|
1075
|
+
history: newHistory,
|
|
1076
|
+
flow: state.flow
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
function statusFromAction(action) {
|
|
1080
|
+
if (/FAILED$|ERROR$|MISSING$|REJECTED$/i.test(action.type)) return "failed";
|
|
1081
|
+
if (/COMPLETED$|SHIPPED$|MERGED$|SUCCESS$/i.test(action.type)) return "succeeded";
|
|
1082
|
+
return "running";
|
|
1083
|
+
}
|
|
1084
|
+
function phaseFromAction(executable, action) {
|
|
1085
|
+
if (/FAILED$|ERROR$|REJECTED$/i.test(action.type)) return "failed";
|
|
1086
|
+
if (executable === "build") return statusFromAction(action) === "succeeded" ? "implementing" : "implementing";
|
|
1087
|
+
if (executable === "review") return "reviewing";
|
|
1088
|
+
if (executable === "release") return "shipped";
|
|
1089
|
+
return "idle";
|
|
1090
|
+
}
|
|
1091
|
+
function noteFromAction(action) {
|
|
1092
|
+
const p = action.payload;
|
|
1093
|
+
if (typeof p?.prUrl === "string") return p.prUrl;
|
|
1094
|
+
if (typeof p?.reason === "string") return p.reason.slice(0, 120);
|
|
1095
|
+
if (typeof p?.commitMessage === "string") return p.commitMessage.slice(0, 120);
|
|
1096
|
+
return void 0;
|
|
1097
|
+
}
|
|
1098
|
+
function renderStateComment(state) {
|
|
1099
|
+
const lines = [];
|
|
1100
|
+
lines.push(STATE_BEGIN);
|
|
1101
|
+
lines.push("");
|
|
1102
|
+
lines.push("```json");
|
|
1103
|
+
lines.push(
|
|
1104
|
+
JSON.stringify(
|
|
1105
|
+
{
|
|
1106
|
+
schemaVersion: state.schemaVersion,
|
|
1107
|
+
core: state.core,
|
|
1108
|
+
artifacts: state.artifacts ?? {},
|
|
1109
|
+
executables: state.executables,
|
|
1110
|
+
history: state.history,
|
|
1111
|
+
...state.flow ? { flow: state.flow } : {}
|
|
1112
|
+
},
|
|
1113
|
+
null,
|
|
1114
|
+
2
|
|
1115
|
+
)
|
|
1116
|
+
);
|
|
1117
|
+
lines.push("```");
|
|
1118
|
+
lines.push("");
|
|
1119
|
+
lines.push(STATE_END);
|
|
1120
|
+
lines.push("");
|
|
1121
|
+
lines.push("## kody2 task state");
|
|
1122
|
+
lines.push("");
|
|
1123
|
+
lines.push(`- **Phase:** \`${state.core.phase}\` **Status:** \`${state.core.status}\``);
|
|
1124
|
+
if (state.core.currentExecutable) {
|
|
1125
|
+
lines.push(`- **Last executable:** \`${state.core.currentExecutable}\``);
|
|
1126
|
+
}
|
|
1127
|
+
if (state.core.lastOutcome) {
|
|
1128
|
+
lines.push(`- **Last action:** \`${state.core.lastOutcome.type}\``);
|
|
1129
|
+
}
|
|
1130
|
+
const attempts = Object.entries(state.core.attempts).map(([k, v]) => `${k}:${v}`).join(", ");
|
|
1131
|
+
if (attempts) lines.push(`- **Attempts:** ${attempts}`);
|
|
1132
|
+
if (state.core.prUrl) lines.push(`- **PR:** ${state.core.prUrl}`);
|
|
1133
|
+
if (state.core.runUrl) lines.push(`- **Run:** ${state.core.runUrl}`);
|
|
1134
|
+
const artifactNames = Object.keys(state.artifacts ?? {});
|
|
1135
|
+
if (artifactNames.length > 0) {
|
|
1136
|
+
lines.push(`- **Artifacts:** ${artifactNames.map((n) => `\`${n}\``).join(", ")}`);
|
|
1137
|
+
}
|
|
1138
|
+
lines.push("");
|
|
1139
|
+
if (state.history.length > 0) {
|
|
1140
|
+
lines.push("### Recent history");
|
|
1141
|
+
lines.push("");
|
|
1142
|
+
const recent = state.history.slice(-10).reverse();
|
|
1143
|
+
for (const h of recent) {
|
|
1144
|
+
const note = h.note ? ` \u2014 ${h.note}` : "";
|
|
1145
|
+
lines.push(`- \`${h.timestamp}\` **${h.executable}** \u2192 \`${h.action}\`${note}`);
|
|
1146
|
+
}
|
|
1147
|
+
lines.push("");
|
|
1148
|
+
}
|
|
1149
|
+
return lines.join("\n");
|
|
1150
|
+
}
|
|
1151
|
+
function readTaskState(target, number, cwd) {
|
|
1152
|
+
const existing = findStateComment(target, number, cwd);
|
|
1153
|
+
return existing ? parseStateComment(existing.body) : emptyState();
|
|
1154
|
+
}
|
|
1155
|
+
function setArtifact(state, name, artifact) {
|
|
1156
|
+
return {
|
|
1157
|
+
...state,
|
|
1158
|
+
artifacts: { ...state.artifacts ?? {}, [name]: artifact }
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
function writeTaskState(target, number, state, cwd) {
|
|
1162
|
+
const body = renderStateComment(state);
|
|
1163
|
+
const existing = findStateComment(target, number, cwd);
|
|
1164
|
+
try {
|
|
1165
|
+
if (existing) {
|
|
1166
|
+
gh(["api", `repos/{owner}/{repo}/issues/comments/${existing.id}`, "-X", "PATCH", "-F", "body=@-"], body, cwd);
|
|
1167
|
+
} else {
|
|
1168
|
+
const sub = target === "issue" ? "issue" : "pr";
|
|
1169
|
+
gh([sub, "comment", String(number), "--body-file", "-"], body, cwd);
|
|
1170
|
+
}
|
|
1171
|
+
} catch (err) {
|
|
1172
|
+
process.stderr.write(
|
|
1173
|
+
`[kody2 state] failed to write state on ${target} #${number}: ${err instanceof Error ? err.message : String(err)}
|
|
1174
|
+
`
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
// src/scripts/advanceFlow.ts
|
|
1180
|
+
var API_TIMEOUT_MS2 = 3e4;
|
|
1181
|
+
var advanceFlow = async (ctx, profile) => {
|
|
1182
|
+
const state = ctx.data.taskState;
|
|
1183
|
+
const flow = state?.flow;
|
|
1184
|
+
if (!flow?.issueNumber) return;
|
|
1185
|
+
const targetType = ctx.data.commentTargetType;
|
|
1186
|
+
const action = ctx.data.action;
|
|
1187
|
+
if (targetType === "pr" && action) {
|
|
1188
|
+
try {
|
|
1189
|
+
const issueState = readTaskState("issue", flow.issueNumber, ctx.cwd);
|
|
1190
|
+
issueState.flow = flow;
|
|
1191
|
+
const next = reduce(issueState, profile.name, action);
|
|
1192
|
+
if (state?.core.prUrl && !next.core.prUrl) next.core.prUrl = state.core.prUrl;
|
|
1193
|
+
next.flow = flow;
|
|
1194
|
+
writeTaskState("issue", flow.issueNumber, next, ctx.cwd);
|
|
1195
|
+
} catch (err) {
|
|
1196
|
+
process.stderr.write(
|
|
1197
|
+
`[kody2 advanceFlow] failed to mirror action to issue #${flow.issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
1198
|
+
`
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
const body = `@kody2 orchestrate --flow ${flow.name}`;
|
|
1203
|
+
try {
|
|
1204
|
+
execFileSync3("gh", ["issue", "comment", String(flow.issueNumber), "--body", body], {
|
|
1205
|
+
timeout: API_TIMEOUT_MS2,
|
|
1206
|
+
cwd: ctx.cwd,
|
|
1207
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1208
|
+
});
|
|
1209
|
+
} catch (err) {
|
|
1210
|
+
process.stderr.write(
|
|
1211
|
+
`[kody2 advanceFlow] failed to re-trigger orchestrator on issue #${flow.issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
1212
|
+
`
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
|
|
956
1217
|
// src/scripts/buildSyntheticPlugin.ts
|
|
957
1218
|
import * as fs8 from "fs";
|
|
958
1219
|
import * as os2 from "os";
|
|
@@ -1047,7 +1308,7 @@ function copyDir(src, dst) {
|
|
|
1047
1308
|
}
|
|
1048
1309
|
|
|
1049
1310
|
// src/coverage.ts
|
|
1050
|
-
import { execFileSync as
|
|
1311
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
1051
1312
|
function patternToRegex(pattern) {
|
|
1052
1313
|
let s = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
1053
1314
|
s = s.replace(/\*\*\//g, "\xA7S").replace(/\*\*/g, "\xA7A").replace(/\*/g, "[^/]*");
|
|
@@ -1065,7 +1326,7 @@ function renderSiblingPath(file, requireSibling) {
|
|
|
1065
1326
|
}
|
|
1066
1327
|
function safeGit(args, cwd) {
|
|
1067
1328
|
try {
|
|
1068
|
-
return
|
|
1329
|
+
return execFileSync4("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
|
|
1069
1330
|
} catch {
|
|
1070
1331
|
return "";
|
|
1071
1332
|
}
|
|
@@ -1236,10 +1497,10 @@ ${formatMissesForFeedback(misses)}`;
|
|
|
1236
1497
|
};
|
|
1237
1498
|
|
|
1238
1499
|
// src/scripts/commitAndPush.ts
|
|
1239
|
-
import { execFileSync as
|
|
1500
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
1240
1501
|
|
|
1241
1502
|
// src/commit.ts
|
|
1242
|
-
import { execFileSync as
|
|
1503
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
1243
1504
|
import * as fs10 from "fs";
|
|
1244
1505
|
import * as path9 from "path";
|
|
1245
1506
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
@@ -1269,7 +1530,7 @@ var CONVENTIONAL_PREFIXES = [
|
|
|
1269
1530
|
];
|
|
1270
1531
|
function git(args, cwd) {
|
|
1271
1532
|
try {
|
|
1272
|
-
return
|
|
1533
|
+
return execFileSync5("git", args, {
|
|
1273
1534
|
encoding: "utf-8",
|
|
1274
1535
|
timeout: 12e4,
|
|
1275
1536
|
cwd,
|
|
@@ -1327,7 +1588,7 @@ function isForbiddenPath(p) {
|
|
|
1327
1588
|
return false;
|
|
1328
1589
|
}
|
|
1329
1590
|
function listChangedFiles(cwd) {
|
|
1330
|
-
const raw =
|
|
1591
|
+
const raw = execFileSync5("git", ["status", "--porcelain=v1", "-z"], {
|
|
1331
1592
|
encoding: "utf-8",
|
|
1332
1593
|
cwd,
|
|
1333
1594
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -1339,7 +1600,7 @@ function listChangedFiles(cwd) {
|
|
|
1339
1600
|
}
|
|
1340
1601
|
function listFilesInCommit(ref = "HEAD", cwd) {
|
|
1341
1602
|
try {
|
|
1342
|
-
const raw =
|
|
1603
|
+
const raw = execFileSync5("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
|
|
1343
1604
|
encoding: "utf-8",
|
|
1344
1605
|
cwd,
|
|
1345
1606
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -1419,7 +1680,7 @@ var commitAndPush2 = async (ctx, profile) => {
|
|
|
1419
1680
|
const kind = profile.name;
|
|
1420
1681
|
if (kind === "resolve") {
|
|
1421
1682
|
try {
|
|
1422
|
-
|
|
1683
|
+
execFileSync6("git", ["add", "-A"], { cwd: ctx.cwd, env: { ...process.env, HUSKY: "0" }, stdio: "pipe" });
|
|
1423
1684
|
} catch {
|
|
1424
1685
|
}
|
|
1425
1686
|
} else {
|
|
@@ -1555,18 +1816,61 @@ function formatToolsUsage(profile) {
|
|
|
1555
1816
|
return lines.join("\n");
|
|
1556
1817
|
}
|
|
1557
1818
|
|
|
1819
|
+
// src/scripts/dispatch.ts
|
|
1820
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
1821
|
+
var API_TIMEOUT_MS3 = 3e4;
|
|
1822
|
+
var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
1823
|
+
const next = args?.next;
|
|
1824
|
+
if (!next) {
|
|
1825
|
+
process.stderr.write("[kody2 dispatch] missing `with.next` \u2014 skipping\n");
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1828
|
+
const target = args?.target ?? "issue";
|
|
1829
|
+
const issueNumber = ctx.args.issue;
|
|
1830
|
+
if (!issueNumber) {
|
|
1831
|
+
process.stderr.write("[kody2 dispatch] no --issue arg \u2014 skipping\n");
|
|
1832
|
+
return;
|
|
1833
|
+
}
|
|
1834
|
+
const state = ctx.data.taskState;
|
|
1835
|
+
if (state?.flow) {
|
|
1836
|
+
state.flow.step = next;
|
|
1837
|
+
}
|
|
1838
|
+
const usePr = target === "pr" && state?.core.prUrl;
|
|
1839
|
+
const targetNumber = usePr ? parsePr(state.core.prUrl) ?? issueNumber : issueNumber;
|
|
1840
|
+
const sub = usePr ? "pr" : "issue";
|
|
1841
|
+
const body = `@kody2 ${next}`;
|
|
1842
|
+
try {
|
|
1843
|
+
execFileSync7("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
1844
|
+
timeout: API_TIMEOUT_MS3,
|
|
1845
|
+
cwd: ctx.cwd,
|
|
1846
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1847
|
+
});
|
|
1848
|
+
} catch (err) {
|
|
1849
|
+
process.stderr.write(
|
|
1850
|
+
`[kody2 dispatch] failed to post @kody2 ${next} on ${sub} #${targetNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
1851
|
+
`
|
|
1852
|
+
);
|
|
1853
|
+
}
|
|
1854
|
+
};
|
|
1855
|
+
function parsePr(url) {
|
|
1856
|
+
const m = url.match(/\/pull\/(\d+)(?:[/?#]|$)/);
|
|
1857
|
+
if (!m) return null;
|
|
1858
|
+
const n = parseInt(m[1], 10);
|
|
1859
|
+
return Number.isFinite(n) ? n : null;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1558
1862
|
// src/issue.ts
|
|
1559
|
-
import { execFileSync as
|
|
1560
|
-
var
|
|
1561
|
-
function
|
|
1863
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
1864
|
+
var API_TIMEOUT_MS4 = 3e4;
|
|
1865
|
+
function ghToken2() {
|
|
1562
1866
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
1563
1867
|
}
|
|
1564
|
-
function
|
|
1565
|
-
const token =
|
|
1868
|
+
function gh2(args, options) {
|
|
1869
|
+
const token = ghToken2();
|
|
1566
1870
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
1567
|
-
return
|
|
1871
|
+
return execFileSync8("gh", args, {
|
|
1568
1872
|
encoding: "utf-8",
|
|
1569
|
-
timeout:
|
|
1873
|
+
timeout: API_TIMEOUT_MS4,
|
|
1570
1874
|
cwd: options?.cwd,
|
|
1571
1875
|
env,
|
|
1572
1876
|
input: options?.input,
|
|
@@ -1574,7 +1878,7 @@ function gh(args, options) {
|
|
|
1574
1878
|
}).trim();
|
|
1575
1879
|
}
|
|
1576
1880
|
function getIssue(issueNumber, cwd) {
|
|
1577
|
-
const output =
|
|
1881
|
+
const output = gh2(["issue", "view", String(issueNumber), "--json", "number,title,body,comments"], { cwd });
|
|
1578
1882
|
const parsed = JSON.parse(output);
|
|
1579
1883
|
if (typeof parsed?.title !== "string") {
|
|
1580
1884
|
throw new Error(`Issue #${issueNumber}: unexpected response shape`);
|
|
@@ -1592,7 +1896,7 @@ function getIssue(issueNumber, cwd) {
|
|
|
1592
1896
|
}
|
|
1593
1897
|
function postIssueComment(issueNumber, body, cwd) {
|
|
1594
1898
|
try {
|
|
1595
|
-
|
|
1899
|
+
gh2(["issue", "comment", String(issueNumber), "--body-file", "-"], { input: body, cwd });
|
|
1596
1900
|
} catch (err) {
|
|
1597
1901
|
process.stderr.write(
|
|
1598
1902
|
`[kody2] failed to post comment on #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -1605,7 +1909,7 @@ function truncate2(s, maxBytes) {
|
|
|
1605
1909
|
return `${s.slice(0, maxBytes)}\u2026 (+${s.length - maxBytes} chars)`;
|
|
1606
1910
|
}
|
|
1607
1911
|
function getPr(prNumber, cwd) {
|
|
1608
|
-
const output =
|
|
1912
|
+
const output = gh2(["pr", "view", String(prNumber), "--json", "number,title,body,headRefName,baseRefName,state"], {
|
|
1609
1913
|
cwd
|
|
1610
1914
|
});
|
|
1611
1915
|
const parsed = JSON.parse(output);
|
|
@@ -1623,7 +1927,7 @@ function getPr(prNumber, cwd) {
|
|
|
1623
1927
|
}
|
|
1624
1928
|
function getPrDiff(prNumber, cwd) {
|
|
1625
1929
|
try {
|
|
1626
|
-
return
|
|
1930
|
+
return gh2(["pr", "diff", String(prNumber)], { cwd });
|
|
1627
1931
|
} catch (err) {
|
|
1628
1932
|
process.stderr.write(
|
|
1629
1933
|
`[kody2] failed to fetch diff for PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -1634,7 +1938,7 @@ function getPrDiff(prNumber, cwd) {
|
|
|
1634
1938
|
}
|
|
1635
1939
|
function getPrReviews(prNumber, cwd) {
|
|
1636
1940
|
try {
|
|
1637
|
-
const output =
|
|
1941
|
+
const output = gh2(["pr", "view", String(prNumber), "--json", "reviews"], { cwd });
|
|
1638
1942
|
const parsed = JSON.parse(output);
|
|
1639
1943
|
if (!Array.isArray(parsed?.reviews)) return [];
|
|
1640
1944
|
return parsed.reviews.map(
|
|
@@ -1651,7 +1955,7 @@ function getPrReviews(prNumber, cwd) {
|
|
|
1651
1955
|
}
|
|
1652
1956
|
function getPrComments(prNumber, cwd) {
|
|
1653
1957
|
try {
|
|
1654
|
-
const output =
|
|
1958
|
+
const output = gh2(["pr", "view", String(prNumber), "--json", "comments"], { cwd });
|
|
1655
1959
|
const parsed = JSON.parse(output);
|
|
1656
1960
|
if (!Array.isArray(parsed?.comments)) return [];
|
|
1657
1961
|
return parsed.comments.map((c) => ({
|
|
@@ -1677,7 +1981,7 @@ function getPrLatestReviewBody(prNumber, cwd) {
|
|
|
1677
1981
|
}
|
|
1678
1982
|
function postPrReviewComment(prNumber, body, cwd) {
|
|
1679
1983
|
try {
|
|
1680
|
-
|
|
1984
|
+
gh2(["pr", "comment", String(prNumber), "--body-file", "-"], { input: body, cwd });
|
|
1681
1985
|
} catch (err) {
|
|
1682
1986
|
process.stderr.write(
|
|
1683
1987
|
`[kody2] failed to post review comment on PR #${prNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -1754,7 +2058,7 @@ function firstLine(s) {
|
|
|
1754
2058
|
}
|
|
1755
2059
|
function findExistingPr(branch, cwd) {
|
|
1756
2060
|
try {
|
|
1757
|
-
const output =
|
|
2061
|
+
const output = gh2(["pr", "view", branch, "--json", "number,url"], { cwd });
|
|
1758
2062
|
const parsed = JSON.parse(output);
|
|
1759
2063
|
if (typeof parsed?.number === "number" && typeof parsed?.url === "string") {
|
|
1760
2064
|
return { number: parsed.number, url: parsed.url };
|
|
@@ -1770,7 +2074,7 @@ function ensurePr(opts) {
|
|
|
1770
2074
|
const existing = findExistingPr(opts.branch, opts.cwd);
|
|
1771
2075
|
if (existing) {
|
|
1772
2076
|
try {
|
|
1773
|
-
|
|
2077
|
+
gh2(["pr", "edit", String(existing.number), "--body-file", "-"], { input: body, cwd: opts.cwd });
|
|
1774
2078
|
} catch (err) {
|
|
1775
2079
|
process.stderr.write(
|
|
1776
2080
|
`[kody2] failed to update PR #${existing.number}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -1792,7 +2096,7 @@ function ensurePr(opts) {
|
|
|
1792
2096
|
"-"
|
|
1793
2097
|
];
|
|
1794
2098
|
if (opts.draft) args.push("--draft");
|
|
1795
|
-
const output =
|
|
2099
|
+
const output = gh2(args, { input: body, cwd: opts.cwd });
|
|
1796
2100
|
const url = output.trim();
|
|
1797
2101
|
const match = url.match(/\/pull\/(\d+)$/);
|
|
1798
2102
|
const number = match ? parseInt(match[1], 10) : 0;
|
|
@@ -1850,8 +2154,43 @@ function computeFailureReason(ctx) {
|
|
|
1850
2154
|
return "";
|
|
1851
2155
|
}
|
|
1852
2156
|
|
|
2157
|
+
// src/scripts/finishFlow.ts
|
|
2158
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
2159
|
+
var API_TIMEOUT_MS5 = 3e4;
|
|
2160
|
+
var STATUS_ICON = {
|
|
2161
|
+
"review-passed": "\u2705",
|
|
2162
|
+
"fix-applied": "\u2705",
|
|
2163
|
+
"review-failed": "\u26A0\uFE0F",
|
|
2164
|
+
aborted: "\u26A0\uFE0F"
|
|
2165
|
+
};
|
|
2166
|
+
var finishFlow = async (ctx, _profile, _agentResult, args) => {
|
|
2167
|
+
const reason = args?.reason ?? "completed";
|
|
2168
|
+
const issueNumber = ctx.args.issue;
|
|
2169
|
+
const state = ctx.data.taskState;
|
|
2170
|
+
const flowName = state?.flow?.name ?? "(unknown flow)";
|
|
2171
|
+
if (state) state.flow = void 0;
|
|
2172
|
+
if (!issueNumber) return;
|
|
2173
|
+
const icon = STATUS_ICON[reason] ?? "\u2139\uFE0F";
|
|
2174
|
+
const prSuffix = state?.core.prUrl ? `
|
|
2175
|
+
|
|
2176
|
+
**PR:** ${state.core.prUrl}` : "";
|
|
2177
|
+
const body = `${icon} kody2 flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
|
|
2178
|
+
try {
|
|
2179
|
+
execFileSync9("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
2180
|
+
timeout: API_TIMEOUT_MS5,
|
|
2181
|
+
cwd: ctx.cwd,
|
|
2182
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2183
|
+
});
|
|
2184
|
+
} catch (err) {
|
|
2185
|
+
process.stderr.write(
|
|
2186
|
+
`[kody2 finishFlow] failed to post final summary on issue #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
2187
|
+
`
|
|
2188
|
+
);
|
|
2189
|
+
}
|
|
2190
|
+
};
|
|
2191
|
+
|
|
1853
2192
|
// src/branch.ts
|
|
1854
|
-
import { execFileSync as
|
|
2193
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
1855
2194
|
var UncommittedChangesError = class extends Error {
|
|
1856
2195
|
constructor(branch) {
|
|
1857
2196
|
super(`Uncommitted changes on branch '${branch}' \u2014 refusing to run to protect work in progress`);
|
|
@@ -1861,7 +2200,7 @@ var UncommittedChangesError = class extends Error {
|
|
|
1861
2200
|
branch;
|
|
1862
2201
|
};
|
|
1863
2202
|
function git2(args, cwd) {
|
|
1864
|
-
return
|
|
2203
|
+
return execFileSync10("git", args, {
|
|
1865
2204
|
encoding: "utf-8",
|
|
1866
2205
|
timeout: 3e4,
|
|
1867
2206
|
cwd,
|
|
@@ -1886,7 +2225,7 @@ function checkoutPrBranch(prNumber, cwd) {
|
|
|
1886
2225
|
SKIP_HOOKS: "1",
|
|
1887
2226
|
GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
|
|
1888
2227
|
};
|
|
1889
|
-
|
|
2228
|
+
execFileSync10("gh", ["pr", "checkout", String(prNumber)], {
|
|
1890
2229
|
cwd,
|
|
1891
2230
|
env,
|
|
1892
2231
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -1953,7 +2292,7 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch, cwd) {
|
|
|
1953
2292
|
}
|
|
1954
2293
|
|
|
1955
2294
|
// src/gha.ts
|
|
1956
|
-
import { execFileSync as
|
|
2295
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
1957
2296
|
import * as fs12 from "fs";
|
|
1958
2297
|
function getRunUrl() {
|
|
1959
2298
|
const server = process.env.GITHUB_SERVER_URL;
|
|
@@ -1977,7 +2316,7 @@ function reactToTriggerComment(cwd) {
|
|
|
1977
2316
|
if (!commentId || !repo) return;
|
|
1978
2317
|
const token = process.env.KODY_TOKEN?.trim() || process.env.GH_TOKEN || process.env.GITHUB_TOKEN;
|
|
1979
2318
|
try {
|
|
1980
|
-
|
|
2319
|
+
execFileSync11(
|
|
1981
2320
|
"gh",
|
|
1982
2321
|
[
|
|
1983
2322
|
"api",
|
|
@@ -2001,15 +2340,15 @@ function reactToTriggerComment(cwd) {
|
|
|
2001
2340
|
}
|
|
2002
2341
|
|
|
2003
2342
|
// src/workflow.ts
|
|
2004
|
-
import { execFileSync as
|
|
2343
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
2005
2344
|
var GH_TIMEOUT_MS = 3e4;
|
|
2006
|
-
function
|
|
2345
|
+
function ghToken3() {
|
|
2007
2346
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
2008
2347
|
}
|
|
2009
|
-
function
|
|
2010
|
-
const token =
|
|
2348
|
+
function gh3(args, cwd) {
|
|
2349
|
+
const token = ghToken3();
|
|
2011
2350
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
2012
|
-
return
|
|
2351
|
+
return execFileSync12("gh", args, {
|
|
2013
2352
|
encoding: "utf-8",
|
|
2014
2353
|
timeout: GH_TIMEOUT_MS,
|
|
2015
2354
|
cwd,
|
|
@@ -2020,14 +2359,14 @@ function gh2(args, cwd) {
|
|
|
2020
2359
|
function getRecentFailedRunsForPr(prNumber, limit, cwd) {
|
|
2021
2360
|
let headBranch;
|
|
2022
2361
|
try {
|
|
2023
|
-
const out =
|
|
2362
|
+
const out = gh3(["pr", "view", String(prNumber), "--json", "headRefName"], cwd);
|
|
2024
2363
|
headBranch = JSON.parse(out).headRefName;
|
|
2025
2364
|
} catch {
|
|
2026
2365
|
return [];
|
|
2027
2366
|
}
|
|
2028
2367
|
if (!headBranch) return [];
|
|
2029
2368
|
try {
|
|
2030
|
-
const out =
|
|
2369
|
+
const out = gh3(
|
|
2031
2370
|
[
|
|
2032
2371
|
"run",
|
|
2033
2372
|
"list",
|
|
@@ -2058,7 +2397,7 @@ function getRecentFailedRunsForPr(prNumber, limit, cwd) {
|
|
|
2058
2397
|
}
|
|
2059
2398
|
function getFailedRunLogTail(runId, maxBytes, cwd) {
|
|
2060
2399
|
try {
|
|
2061
|
-
const raw =
|
|
2400
|
+
const raw = gh3(["run", "view", String(runId), "--log-failed"], cwd);
|
|
2062
2401
|
if (raw.length <= maxBytes) return raw;
|
|
2063
2402
|
return raw.slice(-maxBytes);
|
|
2064
2403
|
} catch {
|
|
@@ -2193,7 +2532,7 @@ function tryPostPr2(prNumber, body, cwd) {
|
|
|
2193
2532
|
}
|
|
2194
2533
|
|
|
2195
2534
|
// src/scripts/initFlow.ts
|
|
2196
|
-
import { execFileSync as
|
|
2535
|
+
import { execFileSync as execFileSync13 } from "child_process";
|
|
2197
2536
|
import * as fs14 from "fs";
|
|
2198
2537
|
import * as path12 from "path";
|
|
2199
2538
|
|
|
@@ -2275,7 +2614,7 @@ function qualityCommandsFor(pm) {
|
|
|
2275
2614
|
function detectOwnerRepo(cwd) {
|
|
2276
2615
|
let url;
|
|
2277
2616
|
try {
|
|
2278
|
-
url =
|
|
2617
|
+
url = execFileSync13("git", ["remote", "get-url", "origin"], {
|
|
2279
2618
|
cwd,
|
|
2280
2619
|
encoding: "utf-8",
|
|
2281
2620
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2359,7 +2698,7 @@ jobs:
|
|
|
2359
2698
|
`;
|
|
2360
2699
|
function defaultBranchFromGit(cwd) {
|
|
2361
2700
|
try {
|
|
2362
|
-
const ref =
|
|
2701
|
+
const ref = execFileSync13("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
2363
2702
|
cwd,
|
|
2364
2703
|
encoding: "utf-8",
|
|
2365
2704
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2367,7 +2706,7 @@ function defaultBranchFromGit(cwd) {
|
|
|
2367
2706
|
return ref.replace("refs/remotes/origin/", "");
|
|
2368
2707
|
} catch {
|
|
2369
2708
|
try {
|
|
2370
|
-
return
|
|
2709
|
+
return execFileSync13("git", ["branch", "--show-current"], {
|
|
2371
2710
|
cwd,
|
|
2372
2711
|
encoding: "utf-8",
|
|
2373
2712
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2507,204 +2846,6 @@ var loadIssueContext = async (ctx) => {
|
|
|
2507
2846
|
ctx.data.commentTargetNumber = issueNumber;
|
|
2508
2847
|
};
|
|
2509
2848
|
|
|
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
2849
|
// src/scripts/loadTaskState.ts
|
|
2709
2850
|
var loadTaskState = async (ctx) => {
|
|
2710
2851
|
const target = ctx.data.commentTargetType;
|
|
@@ -2809,6 +2950,22 @@ function readDottedString(source, dotted) {
|
|
|
2809
2950
|
return String(cur);
|
|
2810
2951
|
}
|
|
2811
2952
|
|
|
2953
|
+
// src/scripts/persistFlowState.ts
|
|
2954
|
+
var persistFlowState = async (ctx) => {
|
|
2955
|
+
const state = ctx.data.taskState;
|
|
2956
|
+
if (!state) return;
|
|
2957
|
+
const issueNumber = ctx.args.issue ?? state.flow?.issueNumber;
|
|
2958
|
+
if (!issueNumber) return;
|
|
2959
|
+
try {
|
|
2960
|
+
writeTaskState("issue", issueNumber, state, ctx.cwd);
|
|
2961
|
+
} catch (err) {
|
|
2962
|
+
process.stderr.write(
|
|
2963
|
+
`[kody2 persistFlowState] failed to write state on issue #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
2964
|
+
`
|
|
2965
|
+
);
|
|
2966
|
+
}
|
|
2967
|
+
};
|
|
2968
|
+
|
|
2812
2969
|
// src/scripts/postIssueComment.ts
|
|
2813
2970
|
var postIssueComment2 = async (ctx) => {
|
|
2814
2971
|
if (ctx.skipAgent && ctx.output.exitCode !== void 0) return;
|
|
@@ -2970,7 +3127,7 @@ REVIEW_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.github.
|
|
|
2970
3127
|
};
|
|
2971
3128
|
|
|
2972
3129
|
// src/scripts/releaseFlow.ts
|
|
2973
|
-
import { execFileSync as
|
|
3130
|
+
import { execFileSync as execFileSync14, spawnSync } from "child_process";
|
|
2974
3131
|
import * as fs15 from "fs";
|
|
2975
3132
|
import * as path13 from "path";
|
|
2976
3133
|
function bumpVersion(current, bump) {
|
|
@@ -3000,7 +3157,7 @@ function generateChangelog(cwd, newVersion, lastTag) {
|
|
|
3000
3157
|
const range = lastTag ? `${lastTag}..HEAD` : "HEAD";
|
|
3001
3158
|
let log = "";
|
|
3002
3159
|
try {
|
|
3003
|
-
log =
|
|
3160
|
+
log = execFileSync14("git", ["log", range, "--pretty=format:%s||%h", "--no-merges"], {
|
|
3004
3161
|
cwd,
|
|
3005
3162
|
encoding: "utf-8",
|
|
3006
3163
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3057,7 +3214,7 @@ ${entry}${prior.slice(idx + 1)}`);
|
|
|
3057
3214
|
}
|
|
3058
3215
|
}
|
|
3059
3216
|
function git3(args, cwd, timeout = 6e4) {
|
|
3060
|
-
return
|
|
3217
|
+
return execFileSync14("git", args, {
|
|
3061
3218
|
encoding: "utf-8",
|
|
3062
3219
|
timeout,
|
|
3063
3220
|
cwd,
|
|
@@ -3164,7 +3321,7 @@ ${entry}
|
|
|
3164
3321
|
Merge this and then run \`kody2 release --mode finalize\`.`;
|
|
3165
3322
|
let prUrl = "";
|
|
3166
3323
|
try {
|
|
3167
|
-
prUrl =
|
|
3324
|
+
prUrl = gh2(["pr", "create", "--head", releaseBranch, "--base", base, "--title", title, "--body-file", "-"], {
|
|
3168
3325
|
input: body,
|
|
3169
3326
|
cwd
|
|
3170
3327
|
}).trim();
|
|
@@ -3241,7 +3398,7 @@ ${truncate2(r.stderr, 2e3)}
|
|
|
3241
3398
|
try {
|
|
3242
3399
|
const releaseArgs = ["release", "create", tag, "--title", tag, "--notes", `Release ${tag} \u2014 automated by kody2.`];
|
|
3243
3400
|
if (releaseCfg.draftRelease) releaseArgs.push("--draft");
|
|
3244
|
-
releaseUrl =
|
|
3401
|
+
releaseUrl = gh2(releaseArgs, { cwd }).trim();
|
|
3245
3402
|
} catch (err) {
|
|
3246
3403
|
process.stderr.write(
|
|
3247
3404
|
`[kody2 release] gh release create failed: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -3360,7 +3517,7 @@ var resolveArtifacts = async (ctx, profile) => {
|
|
|
3360
3517
|
};
|
|
3361
3518
|
|
|
3362
3519
|
// src/scripts/resolveFlow.ts
|
|
3363
|
-
import { execFileSync as
|
|
3520
|
+
import { execFileSync as execFileSync15 } from "child_process";
|
|
3364
3521
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
3365
3522
|
var resolveFlow = async (ctx) => {
|
|
3366
3523
|
const prNumber = ctx.args.pr;
|
|
@@ -3412,7 +3569,7 @@ var resolveFlow = async (ctx) => {
|
|
|
3412
3569
|
};
|
|
3413
3570
|
function getConflictedFiles(cwd) {
|
|
3414
3571
|
try {
|
|
3415
|
-
const out =
|
|
3572
|
+
const out = execFileSync15("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
3416
3573
|
encoding: "utf-8",
|
|
3417
3574
|
cwd,
|
|
3418
3575
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -3427,7 +3584,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
3427
3584
|
let total = 0;
|
|
3428
3585
|
for (const f of files) {
|
|
3429
3586
|
try {
|
|
3430
|
-
const content =
|
|
3587
|
+
const content = execFileSync15("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
3431
3588
|
const snippet = `### ${f}
|
|
3432
3589
|
|
|
3433
3590
|
\`\`\`
|
|
@@ -3541,8 +3698,62 @@ var skipAgent = async (ctx) => {
|
|
|
3541
3698
|
ctx.skipAgent = true;
|
|
3542
3699
|
};
|
|
3543
3700
|
|
|
3701
|
+
// src/scripts/startFlow.ts
|
|
3702
|
+
import { execFileSync as execFileSync16 } from "child_process";
|
|
3703
|
+
var API_TIMEOUT_MS6 = 3e4;
|
|
3704
|
+
var startFlow = async (ctx, _profile, _agentResult, args) => {
|
|
3705
|
+
const entry = args?.entry;
|
|
3706
|
+
if (!entry) {
|
|
3707
|
+
process.stderr.write("[kody2 startFlow] missing `with.entry` \u2014 skipping\n");
|
|
3708
|
+
return;
|
|
3709
|
+
}
|
|
3710
|
+
const target = args?.target ?? "issue";
|
|
3711
|
+
const flowName = ctx.args.flow ?? "default";
|
|
3712
|
+
const issueNumber = ctx.args.issue;
|
|
3713
|
+
if (!issueNumber) {
|
|
3714
|
+
process.stderr.write("[kody2 startFlow] no --issue arg \u2014 skipping\n");
|
|
3715
|
+
return;
|
|
3716
|
+
}
|
|
3717
|
+
const state = ctx.data.taskState;
|
|
3718
|
+
if (state?.flow) {
|
|
3719
|
+
return;
|
|
3720
|
+
}
|
|
3721
|
+
if (state) {
|
|
3722
|
+
state.flow = {
|
|
3723
|
+
name: flowName,
|
|
3724
|
+
step: entry,
|
|
3725
|
+
issueNumber,
|
|
3726
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3727
|
+
};
|
|
3728
|
+
}
|
|
3729
|
+
postKody2Comment(target, issueNumber, state, entry, ctx.cwd);
|
|
3730
|
+
};
|
|
3731
|
+
function postKody2Comment(target, issueNumber, state, next, cwd) {
|
|
3732
|
+
const targetNumber = target === "pr" && state?.core.prUrl ? parsePr2(state.core.prUrl) ?? issueNumber : issueNumber;
|
|
3733
|
+
const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
|
|
3734
|
+
const body = `@kody2 ${next}`;
|
|
3735
|
+
try {
|
|
3736
|
+
execFileSync16("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
3737
|
+
timeout: API_TIMEOUT_MS6,
|
|
3738
|
+
cwd,
|
|
3739
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3740
|
+
});
|
|
3741
|
+
} catch (err) {
|
|
3742
|
+
process.stderr.write(
|
|
3743
|
+
`[kody2 startFlow] failed to post @kody2 ${next} on ${sub} #${targetNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
3744
|
+
`
|
|
3745
|
+
);
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
function parsePr2(url) {
|
|
3749
|
+
const m = url.match(/\/pull\/(\d+)(?:[/?#]|$)/);
|
|
3750
|
+
if (!m) return null;
|
|
3751
|
+
const n = parseInt(m[1], 10);
|
|
3752
|
+
return Number.isFinite(n) ? n : null;
|
|
3753
|
+
}
|
|
3754
|
+
|
|
3544
3755
|
// src/scripts/syncFlow.ts
|
|
3545
|
-
import { execFileSync as
|
|
3756
|
+
import { execFileSync as execFileSync17 } from "child_process";
|
|
3546
3757
|
var syncFlow = async (ctx) => {
|
|
3547
3758
|
ctx.skipAgent = true;
|
|
3548
3759
|
const prNumber = ctx.args.pr;
|
|
@@ -3601,7 +3812,7 @@ function bail2(ctx, prNumber, reason) {
|
|
|
3601
3812
|
}
|
|
3602
3813
|
function revParseHead(cwd) {
|
|
3603
3814
|
try {
|
|
3604
|
-
return
|
|
3815
|
+
return execFileSync17("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
3605
3816
|
} catch {
|
|
3606
3817
|
return "";
|
|
3607
3818
|
}
|
|
@@ -3609,9 +3820,9 @@ function revParseHead(cwd) {
|
|
|
3609
3820
|
function pushBranch(branch, cwd) {
|
|
3610
3821
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
3611
3822
|
try {
|
|
3612
|
-
|
|
3823
|
+
execFileSync17("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
3613
3824
|
} catch {
|
|
3614
|
-
|
|
3825
|
+
execFileSync17("git", ["push", "--force-with-lease", "-u", "origin", branch], {
|
|
3615
3826
|
cwd,
|
|
3616
3827
|
env,
|
|
3617
3828
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3722,7 +3933,7 @@ function readWatchConfig(ctx) {
|
|
|
3722
3933
|
function findStalePrs(cwd, staleDays, now = /* @__PURE__ */ new Date()) {
|
|
3723
3934
|
let raw = "";
|
|
3724
3935
|
try {
|
|
3725
|
-
raw =
|
|
3936
|
+
raw = gh2(["pr", "list", "--state", "open", "--limit", "100", "--json", "number,title,url,updatedAt"], { cwd });
|
|
3726
3937
|
} catch {
|
|
3727
3938
|
return [];
|
|
3728
3939
|
}
|
|
@@ -3842,7 +4053,12 @@ var postflightScripts = {
|
|
|
3842
4053
|
persistArtifacts,
|
|
3843
4054
|
writeRunSummary,
|
|
3844
4055
|
saveTaskState,
|
|
3845
|
-
mirrorStateToPr
|
|
4056
|
+
mirrorStateToPr,
|
|
4057
|
+
startFlow,
|
|
4058
|
+
dispatch,
|
|
4059
|
+
finishFlow,
|
|
4060
|
+
advanceFlow,
|
|
4061
|
+
persistFlowState
|
|
3846
4062
|
};
|
|
3847
4063
|
var allScriptNames = /* @__PURE__ */ new Set([
|
|
3848
4064
|
...Object.keys(preflightScripts),
|
|
@@ -3850,7 +4066,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
3850
4066
|
]);
|
|
3851
4067
|
|
|
3852
4068
|
// src/tools.ts
|
|
3853
|
-
import { execFileSync as
|
|
4069
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
3854
4070
|
function verifyCliTools(tools, cwd) {
|
|
3855
4071
|
const out = [];
|
|
3856
4072
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -3883,7 +4099,7 @@ function verifyOne(tool, cwd) {
|
|
|
3883
4099
|
}
|
|
3884
4100
|
function runShell2(cmd, cwd, timeoutMs = 3e4) {
|
|
3885
4101
|
try {
|
|
3886
|
-
|
|
4102
|
+
execFileSync18("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
3887
4103
|
return true;
|
|
3888
4104
|
} catch {
|
|
3889
4105
|
return false;
|
|
@@ -3980,7 +4196,7 @@ async function runExecutable(profileName, input) {
|
|
|
3980
4196
|
if (!shouldRun(entry, ctx)) continue;
|
|
3981
4197
|
const fn = preflightScripts[entry.script];
|
|
3982
4198
|
if (!fn) return finish({ exitCode: 99, reason: `preflight script not registered: ${entry.script}` });
|
|
3983
|
-
await fn(ctx, profile);
|
|
4199
|
+
await fn(ctx, profile, entry.with);
|
|
3984
4200
|
if (ctx.skipAgent && ctx.output.exitCode !== void 0 && ctx.output.exitCode !== 0) {
|
|
3985
4201
|
return finish(ctx.output);
|
|
3986
4202
|
}
|
|
@@ -3998,7 +4214,7 @@ async function runExecutable(profileName, input) {
|
|
|
3998
4214
|
const fn = postflightScripts[entry.script];
|
|
3999
4215
|
if (!fn) return finish({ exitCode: 99, reason: `postflight script not registered: ${entry.script}` });
|
|
4000
4216
|
try {
|
|
4001
|
-
await fn(ctx, profile, agentResult);
|
|
4217
|
+
await fn(ctx, profile, agentResult, entry.with);
|
|
4002
4218
|
} catch (err) {
|
|
4003
4219
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4004
4220
|
process.stderr.write(`[kody2] postflight script "${entry.script}" crashed: ${msg}
|
|
@@ -4213,7 +4429,7 @@ function detectPackageManager2(cwd) {
|
|
|
4213
4429
|
}
|
|
4214
4430
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
4215
4431
|
try {
|
|
4216
|
-
|
|
4432
|
+
execFileSync19(cmd, args, {
|
|
4217
4433
|
cwd,
|
|
4218
4434
|
stdio: stream ? "inherit" : "pipe",
|
|
4219
4435
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -4226,7 +4442,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
4226
4442
|
}
|
|
4227
4443
|
function isOnPath(bin) {
|
|
4228
4444
|
try {
|
|
4229
|
-
|
|
4445
|
+
execFileSync19("which", [bin], { stdio: "pipe" });
|
|
4230
4446
|
return true;
|
|
4231
4447
|
} catch {
|
|
4232
4448
|
return false;
|
|
@@ -4260,7 +4476,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
4260
4476
|
} catch {
|
|
4261
4477
|
}
|
|
4262
4478
|
try {
|
|
4263
|
-
|
|
4479
|
+
execFileSync19("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
4264
4480
|
process.stdout.write("\u2192 kody2: litellm already installed\n");
|
|
4265
4481
|
return 0;
|
|
4266
4482
|
} catch {
|
|
@@ -4270,16 +4486,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
4270
4486
|
}
|
|
4271
4487
|
function configureGitIdentity(cwd) {
|
|
4272
4488
|
try {
|
|
4273
|
-
const name =
|
|
4489
|
+
const name = execFileSync19("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
4274
4490
|
if (name) return;
|
|
4275
4491
|
} catch {
|
|
4276
4492
|
}
|
|
4277
4493
|
try {
|
|
4278
|
-
|
|
4494
|
+
execFileSync19("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
4279
4495
|
} catch {
|
|
4280
4496
|
}
|
|
4281
4497
|
try {
|
|
4282
|
-
|
|
4498
|
+
execFileSync19("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
4283
4499
|
cwd,
|
|
4284
4500
|
stdio: "pipe"
|
|
4285
4501
|
});
|
|
@@ -4335,13 +4551,13 @@ async function runCi(argv) {
|
|
|
4335
4551
|
${CI_HELP}`);
|
|
4336
4552
|
return 64;
|
|
4337
4553
|
}
|
|
4338
|
-
const
|
|
4554
|
+
const dispatch2 = autoFallback ?? {
|
|
4339
4555
|
executable: "run",
|
|
4340
4556
|
cliArgs: { issue: args.issueNumber },
|
|
4341
4557
|
target: args.issueNumber
|
|
4342
4558
|
};
|
|
4343
|
-
const issueNumber =
|
|
4344
|
-
process.stdout.write(`\u2192 kody2 preflight (cwd=${cwd}, executable=${
|
|
4559
|
+
const issueNumber = dispatch2.target;
|
|
4560
|
+
process.stdout.write(`\u2192 kody2 preflight (cwd=${cwd}, executable=${dispatch2.executable}, target=${issueNumber})
|
|
4345
4561
|
`);
|
|
4346
4562
|
try {
|
|
4347
4563
|
const n = unpackAllSecrets();
|
|
@@ -4378,13 +4594,13 @@ ${CI_HELP}`);
|
|
|
4378
4594
|
postFailureTail(issueNumber, cwd, `preflight crashed: ${msg}`);
|
|
4379
4595
|
return 99;
|
|
4380
4596
|
}
|
|
4381
|
-
process.stdout.write(`\u2192 kody2: preflight done, handing off to kody2 ${
|
|
4597
|
+
process.stdout.write(`\u2192 kody2: preflight done, handing off to kody2 ${dispatch2.executable}
|
|
4382
4598
|
|
|
4383
4599
|
`);
|
|
4384
4600
|
try {
|
|
4385
4601
|
const config = earlyConfig ?? loadConfig(cwd);
|
|
4386
|
-
const result = await runExecutable(
|
|
4387
|
-
cliArgs:
|
|
4602
|
+
const result = await runExecutable(dispatch2.executable, {
|
|
4603
|
+
cliArgs: dispatch2.cliArgs,
|
|
4388
4604
|
cwd,
|
|
4389
4605
|
config,
|
|
4390
4606
|
verbose: args.verbose,
|
|
@@ -4456,9 +4672,9 @@ function commitChatFiles(cwd, sessionId, verbose) {
|
|
|
4456
4672
|
if (paths.length === 0) return;
|
|
4457
4673
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
4458
4674
|
try {
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4675
|
+
execFileSync20("git", ["add", ...paths], opts);
|
|
4676
|
+
execFileSync20("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
4677
|
+
execFileSync20("git", ["push", "--quiet", "origin", "HEAD"], opts);
|
|
4462
4678
|
} catch (err) {
|
|
4463
4679
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4464
4680
|
process.stderr.write(`[kody2:chat] commit/push skipped: ${msg}
|