@kody-ade/kody-engine 0.3.71 → 0.3.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/kody.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// package.json
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "@kody-ade/kody-engine",
|
|
6
|
-
version: "0.3.
|
|
6
|
+
version: "0.3.74",
|
|
7
7
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
8
8
|
license: "MIT",
|
|
9
9
|
type: "module",
|
|
@@ -51,9 +51,9 @@ var package_default = {
|
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
// src/chat-cli.ts
|
|
54
|
-
import { execFileSync as
|
|
55
|
-
import * as
|
|
56
|
-
import * as
|
|
54
|
+
import { execFileSync as execFileSync28 } from "child_process";
|
|
55
|
+
import * as fs28 from "fs";
|
|
56
|
+
import * as path25 from "path";
|
|
57
57
|
|
|
58
58
|
// src/chat/events.ts
|
|
59
59
|
import * as fs from "fs";
|
|
@@ -473,6 +473,20 @@ import * as path4 from "path";
|
|
|
473
473
|
function sessionFilePath(cwd, sessionId) {
|
|
474
474
|
return path4.join(cwd, ".kody", "sessions", `${sessionId}.jsonl`);
|
|
475
475
|
}
|
|
476
|
+
function readMeta(file) {
|
|
477
|
+
if (!fs4.existsSync(file)) return null;
|
|
478
|
+
const raw = fs4.readFileSync(file, "utf-8");
|
|
479
|
+
const firstLine2 = raw.split("\n", 1)[0]?.trim();
|
|
480
|
+
if (!firstLine2) return null;
|
|
481
|
+
try {
|
|
482
|
+
const parsed = JSON.parse(firstLine2);
|
|
483
|
+
if (parsed.type !== "meta") return null;
|
|
484
|
+
if (parsed.mode !== "one-shot" && parsed.mode !== "interactive") return null;
|
|
485
|
+
return parsed;
|
|
486
|
+
} catch {
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
476
490
|
function readSession(file) {
|
|
477
491
|
if (!fs4.existsSync(file)) return [];
|
|
478
492
|
const raw = fs4.readFileSync(file, "utf-8").trim();
|
|
@@ -641,13 +655,169 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
641
655
|
});
|
|
642
656
|
}
|
|
643
657
|
|
|
658
|
+
// src/chat/modes/interactive.ts
|
|
659
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
660
|
+
import * as fs5 from "fs";
|
|
661
|
+
import * as path5 from "path";
|
|
662
|
+
|
|
663
|
+
// src/chat/inbox.ts
|
|
664
|
+
import { execFileSync } from "child_process";
|
|
665
|
+
var DEFAULT_POLL_MS = 3e4;
|
|
666
|
+
async function waitForNextUserMessage(opts) {
|
|
667
|
+
const pollMs = opts.pollIntervalMs ?? DEFAULT_POLL_MS;
|
|
668
|
+
const logger = opts.logger ?? {
|
|
669
|
+
warn: (m) => process.stderr.write(`[kody:chat:inbox] ${m}
|
|
670
|
+
`)
|
|
671
|
+
};
|
|
672
|
+
const idleStart = Date.now();
|
|
673
|
+
while (true) {
|
|
674
|
+
const now = Date.now();
|
|
675
|
+
if (now >= opts.deadlineMs) return { kind: "deadline" };
|
|
676
|
+
if (now - idleStart >= opts.idleTimeoutMs) return { kind: "idle-timeout" };
|
|
677
|
+
if (!opts.skipPull) {
|
|
678
|
+
try {
|
|
679
|
+
execFileSync("git", ["pull", "--ff-only", "--quiet", "origin", "HEAD"], {
|
|
680
|
+
cwd: opts.cwd,
|
|
681
|
+
stdio: "pipe"
|
|
682
|
+
});
|
|
683
|
+
} catch (err) {
|
|
684
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
685
|
+
logger.warn(`git pull failed (will retry): ${msg}`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
const turns = readSession(opts.sessionFile);
|
|
689
|
+
for (let i = opts.watermark; i < turns.length; i++) {
|
|
690
|
+
const t = turns[i];
|
|
691
|
+
if (t.role === "user") {
|
|
692
|
+
return { kind: "message", turn: t, turnIndex: i };
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
const remainingDeadline = opts.deadlineMs - Date.now();
|
|
696
|
+
const remainingIdle = opts.idleTimeoutMs - (Date.now() - idleStart);
|
|
697
|
+
const sleepMs2 = Math.max(0, Math.min(pollMs, remainingDeadline, remainingIdle));
|
|
698
|
+
if (sleepMs2 === 0) continue;
|
|
699
|
+
await sleep(sleepMs2);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function sleep(ms) {
|
|
703
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// src/chat/modes/interactive.ts
|
|
707
|
+
var DEFAULT_IDLE_EXIT_MS = 5 * 6e4;
|
|
708
|
+
var DEFAULT_HARD_CAP_MS = 30 * 6e4;
|
|
709
|
+
var DEFAULT_POLL_MS2 = 3e4;
|
|
710
|
+
async function runInteractiveMode(opts) {
|
|
711
|
+
const sessionFile = sessionFilePath(opts.cwd, opts.sessionId);
|
|
712
|
+
const idleExitMs = opts.meta.idleExitMs ?? DEFAULT_IDLE_EXIT_MS;
|
|
713
|
+
const hardCapMs = opts.meta.hardCapMs ?? DEFAULT_HARD_CAP_MS;
|
|
714
|
+
const startedAt = Date.now();
|
|
715
|
+
const deadlineMs = startedAt + hardCapMs;
|
|
716
|
+
await emit2(opts.sink, "chat.ready", opts.sessionId, "ready", {
|
|
717
|
+
sessionId: opts.sessionId,
|
|
718
|
+
startedAt: new Date(startedAt).toISOString(),
|
|
719
|
+
idleExitMs,
|
|
720
|
+
hardCapMs
|
|
721
|
+
});
|
|
722
|
+
let watermark = 0;
|
|
723
|
+
let turnsCompleted = 0;
|
|
724
|
+
while (true) {
|
|
725
|
+
const turns = readSession(sessionFile);
|
|
726
|
+
const pendingIdx = findNextUserTurn(turns, watermark);
|
|
727
|
+
if (pendingIdx === -1) {
|
|
728
|
+
const result = await waitForNextUserMessage({
|
|
729
|
+
sessionFile,
|
|
730
|
+
cwd: opts.cwd,
|
|
731
|
+
watermark,
|
|
732
|
+
idleTimeoutMs: idleExitMs,
|
|
733
|
+
deadlineMs,
|
|
734
|
+
pollIntervalMs: opts.pollIntervalMs ?? DEFAULT_POLL_MS2,
|
|
735
|
+
skipPull: opts.skipGit
|
|
736
|
+
});
|
|
737
|
+
if (result.kind === "idle-timeout") {
|
|
738
|
+
await emitExit(opts, "idle-timeout", turnsCompleted);
|
|
739
|
+
return { exitCode: 0, reason: "idle-timeout", turnsCompleted };
|
|
740
|
+
}
|
|
741
|
+
if (result.kind === "deadline") {
|
|
742
|
+
await emitExit(opts, "deadline", turnsCompleted);
|
|
743
|
+
return { exitCode: 0, reason: "deadline", turnsCompleted };
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
let turnResult;
|
|
747
|
+
try {
|
|
748
|
+
turnResult = await runChatTurn({
|
|
749
|
+
sessionId: opts.sessionId,
|
|
750
|
+
sessionFile,
|
|
751
|
+
cwd: opts.cwd,
|
|
752
|
+
model: opts.model,
|
|
753
|
+
litellmUrl: opts.litellmUrl,
|
|
754
|
+
sink: opts.sink,
|
|
755
|
+
verbose: opts.verbose,
|
|
756
|
+
quiet: opts.quiet,
|
|
757
|
+
invokeAgent: opts.invokeAgent
|
|
758
|
+
});
|
|
759
|
+
} catch (err) {
|
|
760
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
761
|
+
await emit2(opts.sink, "chat.error", opts.sessionId, `loop-${turnsCompleted}`, { error: msg });
|
|
762
|
+
await emitExit(opts, "fatal", turnsCompleted);
|
|
763
|
+
return { exitCode: 99, reason: "fatal", turnsCompleted };
|
|
764
|
+
}
|
|
765
|
+
if (turnResult.exitCode === 64) {
|
|
766
|
+
} else if (turnResult.exitCode !== 0) {
|
|
767
|
+
} else {
|
|
768
|
+
turnsCompleted += 1;
|
|
769
|
+
if (!opts.skipGit) commitTurn(opts.cwd, opts.sessionId, opts.verbose ?? false);
|
|
770
|
+
}
|
|
771
|
+
watermark = readSession(sessionFile).length;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
function findNextUserTurn(turns, fromIdx) {
|
|
775
|
+
for (let i = fromIdx; i < turns.length; i++) {
|
|
776
|
+
if (turns[i].role === "user") return i;
|
|
777
|
+
}
|
|
778
|
+
if (turns.length > 0 && turns[turns.length - 1].role === "user") return turns.length - 1;
|
|
779
|
+
return -1;
|
|
780
|
+
}
|
|
781
|
+
function commitTurn(cwd, sessionId, verbose) {
|
|
782
|
+
const sessionRel = path5.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
783
|
+
const eventsRel = path5.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
784
|
+
const paths = [sessionRel, eventsRel].filter((p) => fs5.existsSync(path5.join(cwd, p)));
|
|
785
|
+
if (paths.length === 0) return;
|
|
786
|
+
const stdio = verbose ? "inherit" : "pipe";
|
|
787
|
+
try {
|
|
788
|
+
execFileSync2("git", ["add", ...paths], { cwd, stdio });
|
|
789
|
+
execFileSync2("git", ["commit", "--quiet", "-m", `chat: interactive turn for ${sessionId}`], { cwd, stdio });
|
|
790
|
+
execFileSync2("git", ["push", "--quiet", "origin", "HEAD"], { cwd, stdio });
|
|
791
|
+
} catch (err) {
|
|
792
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
793
|
+
process.stderr.write(`[kody:chat:interactive] commit/push skipped: ${msg}
|
|
794
|
+
`);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
async function emitExit(opts, reason, turnsCompleted) {
|
|
798
|
+
await emit2(opts.sink, "chat.exit", opts.sessionId, "exit", {
|
|
799
|
+
sessionId: opts.sessionId,
|
|
800
|
+
reason,
|
|
801
|
+
turnsCompleted,
|
|
802
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
async function emit2(sink, type, sessionId, suffix, payload) {
|
|
806
|
+
await sink.emit({
|
|
807
|
+
event: type,
|
|
808
|
+
payload,
|
|
809
|
+
runId: makeRunId(sessionId, suffix),
|
|
810
|
+
emittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
|
|
644
814
|
// src/kody-cli.ts
|
|
645
|
-
import { execFileSync as
|
|
646
|
-
import * as
|
|
647
|
-
import * as
|
|
815
|
+
import { execFileSync as execFileSync27 } from "child_process";
|
|
816
|
+
import * as fs27 from "fs";
|
|
817
|
+
import * as path24 from "path";
|
|
648
818
|
|
|
649
819
|
// src/dispatch.ts
|
|
650
|
-
import * as
|
|
820
|
+
import * as fs7 from "fs";
|
|
651
821
|
|
|
652
822
|
// src/cron-match.ts
|
|
653
823
|
var FIELD_BOUNDS = [
|
|
@@ -712,25 +882,25 @@ function cronMatchesInWindow(spec, end, windowSec) {
|
|
|
712
882
|
}
|
|
713
883
|
|
|
714
884
|
// src/registry.ts
|
|
715
|
-
import * as
|
|
716
|
-
import * as
|
|
885
|
+
import * as fs6 from "fs";
|
|
886
|
+
import * as path6 from "path";
|
|
717
887
|
function getExecutablesRoot() {
|
|
718
|
-
const here =
|
|
888
|
+
const here = path6.dirname(new URL(import.meta.url).pathname);
|
|
719
889
|
const candidates = [
|
|
720
|
-
|
|
890
|
+
path6.join(here, "executables"),
|
|
721
891
|
// dev: src/
|
|
722
|
-
|
|
892
|
+
path6.join(here, "..", "executables"),
|
|
723
893
|
// built: dist/bin → dist/executables
|
|
724
|
-
|
|
894
|
+
path6.join(here, "..", "src", "executables")
|
|
725
895
|
// fallback
|
|
726
896
|
];
|
|
727
897
|
for (const c of candidates) {
|
|
728
|
-
if (
|
|
898
|
+
if (fs6.existsSync(c) && fs6.statSync(c).isDirectory()) return c;
|
|
729
899
|
}
|
|
730
900
|
return candidates[0];
|
|
731
901
|
}
|
|
732
902
|
function getProjectExecutablesRoot() {
|
|
733
|
-
return
|
|
903
|
+
return path6.join(process.cwd(), ".kody", "executables");
|
|
734
904
|
}
|
|
735
905
|
function getExecutableRoots() {
|
|
736
906
|
return [getProjectExecutablesRoot(), getExecutablesRoot()];
|
|
@@ -740,13 +910,13 @@ function listExecutables(roots = getExecutableRoots()) {
|
|
|
740
910
|
const seen = /* @__PURE__ */ new Set();
|
|
741
911
|
const out = [];
|
|
742
912
|
for (const root of rootList) {
|
|
743
|
-
if (!
|
|
744
|
-
const entries =
|
|
913
|
+
if (!fs6.existsSync(root)) continue;
|
|
914
|
+
const entries = fs6.readdirSync(root, { withFileTypes: true });
|
|
745
915
|
for (const ent of entries) {
|
|
746
916
|
if (!ent.isDirectory()) continue;
|
|
747
917
|
if (seen.has(ent.name)) continue;
|
|
748
|
-
const profilePath =
|
|
749
|
-
if (
|
|
918
|
+
const profilePath = path6.join(root, ent.name, "profile.json");
|
|
919
|
+
if (fs6.existsSync(profilePath) && fs6.statSync(profilePath).isFile()) {
|
|
750
920
|
out.push({ name: ent.name, profilePath });
|
|
751
921
|
seen.add(ent.name);
|
|
752
922
|
}
|
|
@@ -758,8 +928,8 @@ function resolveExecutable(name, roots = getExecutableRoots()) {
|
|
|
758
928
|
if (!isSafeName(name)) return null;
|
|
759
929
|
const rootList = typeof roots === "string" ? [roots] : roots;
|
|
760
930
|
for (const root of rootList) {
|
|
761
|
-
const profilePath =
|
|
762
|
-
if (
|
|
931
|
+
const profilePath = path6.join(root, name, "profile.json");
|
|
932
|
+
if (fs6.existsSync(profilePath) && fs6.statSync(profilePath).isFile()) {
|
|
763
933
|
return profilePath;
|
|
764
934
|
}
|
|
765
935
|
}
|
|
@@ -775,7 +945,7 @@ function getProfileInputs(name, roots = getExecutableRoots()) {
|
|
|
775
945
|
const profilePath = resolveExecutable(name, roots);
|
|
776
946
|
if (!profilePath) return null;
|
|
777
947
|
try {
|
|
778
|
-
const raw = JSON.parse(
|
|
948
|
+
const raw = JSON.parse(fs6.readFileSync(profilePath, "utf-8"));
|
|
779
949
|
if (!raw || typeof raw !== "object" || !Array.isArray(raw.inputs)) return [];
|
|
780
950
|
return raw.inputs;
|
|
781
951
|
} catch {
|
|
@@ -812,10 +982,10 @@ function autoDispatch(opts) {
|
|
|
812
982
|
}
|
|
813
983
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
814
984
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
815
|
-
if (!eventName || !eventPath || !
|
|
985
|
+
if (!eventName || !eventPath || !fs7.existsSync(eventPath)) return null;
|
|
816
986
|
let event = {};
|
|
817
987
|
try {
|
|
818
|
-
event = JSON.parse(
|
|
988
|
+
event = JSON.parse(fs7.readFileSync(eventPath, "utf-8"));
|
|
819
989
|
} catch {
|
|
820
990
|
return null;
|
|
821
991
|
}
|
|
@@ -890,7 +1060,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
890
1060
|
for (const exe of listExecutables()) {
|
|
891
1061
|
let raw;
|
|
892
1062
|
try {
|
|
893
|
-
raw =
|
|
1063
|
+
raw = fs7.readFileSync(exe.profilePath, "utf-8");
|
|
894
1064
|
} catch {
|
|
895
1065
|
continue;
|
|
896
1066
|
}
|
|
@@ -1004,14 +1174,14 @@ function coerceBare(spec, value) {
|
|
|
1004
1174
|
|
|
1005
1175
|
// src/executor.ts
|
|
1006
1176
|
import { spawn as spawn3 } from "child_process";
|
|
1007
|
-
import * as
|
|
1008
|
-
import * as
|
|
1177
|
+
import * as fs26 from "fs";
|
|
1178
|
+
import * as path23 from "path";
|
|
1009
1179
|
|
|
1010
1180
|
// src/litellm.ts
|
|
1011
|
-
import { execFileSync, spawn } from "child_process";
|
|
1012
|
-
import * as
|
|
1181
|
+
import { execFileSync as execFileSync3, spawn } from "child_process";
|
|
1182
|
+
import * as fs8 from "fs";
|
|
1013
1183
|
import * as os from "os";
|
|
1014
|
-
import * as
|
|
1184
|
+
import * as path7 from "path";
|
|
1015
1185
|
async function checkLitellmHealth(url) {
|
|
1016
1186
|
try {
|
|
1017
1187
|
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
@@ -1042,29 +1212,29 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
1042
1212
|
}
|
|
1043
1213
|
let cmd = "litellm";
|
|
1044
1214
|
try {
|
|
1045
|
-
|
|
1215
|
+
execFileSync3("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
|
|
1046
1216
|
} catch {
|
|
1047
1217
|
try {
|
|
1048
|
-
|
|
1218
|
+
execFileSync3("python3", ["-c", "import litellm"], { timeout: 1e4, stdio: "pipe" });
|
|
1049
1219
|
cmd = "python3";
|
|
1050
1220
|
} catch {
|
|
1051
1221
|
throw new Error("litellm not installed \u2014 run: pip install 'litellm[proxy]'");
|
|
1052
1222
|
}
|
|
1053
1223
|
}
|
|
1054
|
-
const configPath =
|
|
1055
|
-
|
|
1224
|
+
const configPath = path7.join(os.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
|
|
1225
|
+
fs8.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
1056
1226
|
const portMatch = url.match(/:(\d+)/);
|
|
1057
1227
|
const port = portMatch ? portMatch[1] : "4000";
|
|
1058
1228
|
const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
|
|
1059
1229
|
const dotenvVars = readDotenvApiKeys(projectDir);
|
|
1060
|
-
const logPath =
|
|
1061
|
-
const outFd =
|
|
1230
|
+
const logPath = path7.join(os.tmpdir(), `kody-litellm-${Date.now()}.log`);
|
|
1231
|
+
const outFd = fs8.openSync(logPath, "w");
|
|
1062
1232
|
const child = spawn(cmd, args, {
|
|
1063
1233
|
stdio: ["ignore", outFd, outFd],
|
|
1064
1234
|
detached: true,
|
|
1065
1235
|
env: stripBlockingEnv({ ...process.env, ...dotenvVars })
|
|
1066
1236
|
});
|
|
1067
|
-
|
|
1237
|
+
fs8.closeSync(outFd);
|
|
1068
1238
|
for (let i = 0; i < 30; i++) {
|
|
1069
1239
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
1070
1240
|
if (await checkLitellmHealth(url)) {
|
|
@@ -1081,7 +1251,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
1081
1251
|
}
|
|
1082
1252
|
let logTail = "";
|
|
1083
1253
|
try {
|
|
1084
|
-
logTail =
|
|
1254
|
+
logTail = fs8.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
1085
1255
|
} catch {
|
|
1086
1256
|
}
|
|
1087
1257
|
try {
|
|
@@ -1092,10 +1262,10 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
1092
1262
|
${logTail}`);
|
|
1093
1263
|
}
|
|
1094
1264
|
function readDotenvApiKeys(projectDir) {
|
|
1095
|
-
const dotenvPath =
|
|
1096
|
-
if (!
|
|
1265
|
+
const dotenvPath = path7.join(projectDir, ".env");
|
|
1266
|
+
if (!fs8.existsSync(dotenvPath)) return {};
|
|
1097
1267
|
const result = {};
|
|
1098
|
-
for (const rawLine of
|
|
1268
|
+
for (const rawLine of fs8.readFileSync(dotenvPath, "utf-8").split("\n")) {
|
|
1099
1269
|
const line = rawLine.trim();
|
|
1100
1270
|
if (!line || line.startsWith("#")) continue;
|
|
1101
1271
|
const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
|
|
@@ -1118,8 +1288,8 @@ function stripBlockingEnv(env) {
|
|
|
1118
1288
|
}
|
|
1119
1289
|
|
|
1120
1290
|
// src/profile.ts
|
|
1121
|
-
import * as
|
|
1122
|
-
import * as
|
|
1291
|
+
import * as fs9 from "fs";
|
|
1292
|
+
import * as path8 from "path";
|
|
1123
1293
|
var VALID_INPUT_TYPES = /* @__PURE__ */ new Set(["int", "string", "bool", "enum"]);
|
|
1124
1294
|
var VALID_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "acceptEdits", "plan", "bypassPermissions"]);
|
|
1125
1295
|
var VALID_ROLES = /* @__PURE__ */ new Set(["primitive", "orchestrator", "container", "watch", "utility"]);
|
|
@@ -1135,12 +1305,12 @@ var ProfileError = class extends Error {
|
|
|
1135
1305
|
profilePath;
|
|
1136
1306
|
};
|
|
1137
1307
|
function loadProfile(profilePath) {
|
|
1138
|
-
if (!
|
|
1308
|
+
if (!fs9.existsSync(profilePath)) {
|
|
1139
1309
|
throw new ProfileError(profilePath, "file not found");
|
|
1140
1310
|
}
|
|
1141
1311
|
let raw;
|
|
1142
1312
|
try {
|
|
1143
|
-
raw = JSON.parse(
|
|
1313
|
+
raw = JSON.parse(fs9.readFileSync(profilePath, "utf-8"));
|
|
1144
1314
|
} catch (err) {
|
|
1145
1315
|
throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
1146
1316
|
}
|
|
@@ -1179,7 +1349,7 @@ function loadProfile(profilePath) {
|
|
|
1179
1349
|
inputArtifacts: parseInputArtifacts(profilePath, r.input),
|
|
1180
1350
|
outputArtifacts: parseOutputArtifacts(profilePath, r.output),
|
|
1181
1351
|
children,
|
|
1182
|
-
dir:
|
|
1352
|
+
dir: path8.dirname(profilePath)
|
|
1183
1353
|
};
|
|
1184
1354
|
return profile;
|
|
1185
1355
|
}
|
|
@@ -1423,9 +1593,9 @@ function parseScriptList(p, key, raw) {
|
|
|
1423
1593
|
}
|
|
1424
1594
|
|
|
1425
1595
|
// src/commit.ts
|
|
1426
|
-
import { execFileSync as
|
|
1427
|
-
import * as
|
|
1428
|
-
import * as
|
|
1596
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
1597
|
+
import * as fs10 from "fs";
|
|
1598
|
+
import * as path9 from "path";
|
|
1429
1599
|
var FORBIDDEN_PATH_PREFIXES = [
|
|
1430
1600
|
".kody/",
|
|
1431
1601
|
".kody-engine/",
|
|
@@ -1454,7 +1624,7 @@ var CONVENTIONAL_PREFIXES = [
|
|
|
1454
1624
|
];
|
|
1455
1625
|
function git(args, cwd) {
|
|
1456
1626
|
try {
|
|
1457
|
-
return
|
|
1627
|
+
return execFileSync4("git", args, {
|
|
1458
1628
|
encoding: "utf-8",
|
|
1459
1629
|
timeout: 12e4,
|
|
1460
1630
|
cwd,
|
|
@@ -1481,18 +1651,18 @@ function tryGit(args, cwd) {
|
|
|
1481
1651
|
}
|
|
1482
1652
|
function abortUnfinishedGitOps(cwd) {
|
|
1483
1653
|
const aborted = [];
|
|
1484
|
-
const gitDir =
|
|
1485
|
-
if (!
|
|
1486
|
-
if (
|
|
1654
|
+
const gitDir = path9.join(cwd ?? process.cwd(), ".git");
|
|
1655
|
+
if (!fs10.existsSync(gitDir)) return aborted;
|
|
1656
|
+
if (fs10.existsSync(path9.join(gitDir, "MERGE_HEAD"))) {
|
|
1487
1657
|
if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
|
|
1488
1658
|
}
|
|
1489
|
-
if (
|
|
1659
|
+
if (fs10.existsSync(path9.join(gitDir, "CHERRY_PICK_HEAD"))) {
|
|
1490
1660
|
if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
|
|
1491
1661
|
}
|
|
1492
|
-
if (
|
|
1662
|
+
if (fs10.existsSync(path9.join(gitDir, "REVERT_HEAD"))) {
|
|
1493
1663
|
if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
|
|
1494
1664
|
}
|
|
1495
|
-
if (
|
|
1665
|
+
if (fs10.existsSync(path9.join(gitDir, "rebase-merge")) || fs10.existsSync(path9.join(gitDir, "rebase-apply"))) {
|
|
1496
1666
|
if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
|
|
1497
1667
|
}
|
|
1498
1668
|
try {
|
|
@@ -1513,7 +1683,7 @@ function isForbiddenPath(p) {
|
|
|
1513
1683
|
return false;
|
|
1514
1684
|
}
|
|
1515
1685
|
function listChangedFiles(cwd) {
|
|
1516
|
-
const raw =
|
|
1686
|
+
const raw = execFileSync4("git", ["status", "--porcelain=v1", "-z"], {
|
|
1517
1687
|
encoding: "utf-8",
|
|
1518
1688
|
cwd,
|
|
1519
1689
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -1525,7 +1695,7 @@ function listChangedFiles(cwd) {
|
|
|
1525
1695
|
}
|
|
1526
1696
|
function listFilesInCommit(ref = "HEAD", cwd) {
|
|
1527
1697
|
try {
|
|
1528
|
-
const raw =
|
|
1698
|
+
const raw = execFileSync4("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
|
|
1529
1699
|
encoding: "utf-8",
|
|
1530
1700
|
cwd,
|
|
1531
1701
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
@@ -1548,7 +1718,7 @@ function normalizeCommitMessage(raw) {
|
|
|
1548
1718
|
function commitAndPush(branch, agentMessage, cwd) {
|
|
1549
1719
|
const allChanged = listChangedFiles(cwd);
|
|
1550
1720
|
const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
|
|
1551
|
-
const mergeHeadExists =
|
|
1721
|
+
const mergeHeadExists = fs10.existsSync(path9.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
1552
1722
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
1553
1723
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
1554
1724
|
}
|
|
@@ -1614,10 +1784,10 @@ var abortUnfinishedGitOps2 = async (ctx) => {
|
|
|
1614
1784
|
};
|
|
1615
1785
|
|
|
1616
1786
|
// src/scripts/advanceFlow.ts
|
|
1617
|
-
import { execFileSync as
|
|
1787
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
1618
1788
|
|
|
1619
1789
|
// src/state.ts
|
|
1620
|
-
import { execFileSync as
|
|
1790
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
1621
1791
|
var STATE_BEGIN = "<!-- kody:state:v1:begin -->";
|
|
1622
1792
|
var STATE_END = "<!-- kody:state:v1:end -->";
|
|
1623
1793
|
var HISTORY_MAX_ENTRIES = 20;
|
|
@@ -1643,7 +1813,7 @@ function ghToken() {
|
|
|
1643
1813
|
function gh(args, input, cwd) {
|
|
1644
1814
|
const token = ghToken();
|
|
1645
1815
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
1646
|
-
return
|
|
1816
|
+
return execFileSync5("gh", args, {
|
|
1647
1817
|
encoding: "utf-8",
|
|
1648
1818
|
timeout: API_TIMEOUT_MS,
|
|
1649
1819
|
cwd,
|
|
@@ -1846,7 +2016,7 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
1846
2016
|
}
|
|
1847
2017
|
const body = `@kody ${flow.name}`;
|
|
1848
2018
|
try {
|
|
1849
|
-
|
|
2019
|
+
execFileSync6("gh", ["issue", "comment", String(flow.issueNumber), "--body", body], {
|
|
1850
2020
|
timeout: API_TIMEOUT_MS2,
|
|
1851
2021
|
cwd: ctx.cwd,
|
|
1852
2022
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -1860,21 +2030,21 @@ var advanceFlow = async (ctx, profile) => {
|
|
|
1860
2030
|
};
|
|
1861
2031
|
|
|
1862
2032
|
// src/scripts/buildSyntheticPlugin.ts
|
|
1863
|
-
import * as
|
|
2033
|
+
import * as fs11 from "fs";
|
|
1864
2034
|
import * as os2 from "os";
|
|
1865
|
-
import * as
|
|
2035
|
+
import * as path10 from "path";
|
|
1866
2036
|
function getPluginsCatalogRoot() {
|
|
1867
|
-
const here =
|
|
2037
|
+
const here = path10.dirname(new URL(import.meta.url).pathname);
|
|
1868
2038
|
const candidates = [
|
|
1869
|
-
|
|
2039
|
+
path10.join(here, "..", "plugins"),
|
|
1870
2040
|
// dev: src/scripts → src/plugins
|
|
1871
|
-
|
|
2041
|
+
path10.join(here, "..", "..", "plugins"),
|
|
1872
2042
|
// built: dist/scripts → dist/plugins
|
|
1873
|
-
|
|
2043
|
+
path10.join(here, "..", "..", "src", "plugins")
|
|
1874
2044
|
// fallback
|
|
1875
2045
|
];
|
|
1876
2046
|
for (const c of candidates) {
|
|
1877
|
-
if (
|
|
2047
|
+
if (fs11.existsSync(c) && fs11.statSync(c).isDirectory()) return c;
|
|
1878
2048
|
}
|
|
1879
2049
|
return candidates[0];
|
|
1880
2050
|
}
|
|
@@ -1884,52 +2054,52 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
1884
2054
|
if (!needsSynthetic) return;
|
|
1885
2055
|
const catalog = getPluginsCatalogRoot();
|
|
1886
2056
|
const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1887
|
-
const root =
|
|
1888
|
-
|
|
2057
|
+
const root = path10.join(os2.tmpdir(), `kody-synth-${runId}`);
|
|
2058
|
+
fs11.mkdirSync(path10.join(root, ".claude-plugin"), { recursive: true });
|
|
1889
2059
|
const resolvePart = (bucket, entry) => {
|
|
1890
|
-
const local =
|
|
1891
|
-
if (
|
|
1892
|
-
const central =
|
|
1893
|
-
if (
|
|
2060
|
+
const local = path10.join(profile.dir, bucket, entry);
|
|
2061
|
+
if (fs11.existsSync(local)) return local;
|
|
2062
|
+
const central = path10.join(catalog, bucket, entry);
|
|
2063
|
+
if (fs11.existsSync(central)) return central;
|
|
1894
2064
|
throw new Error(
|
|
1895
2065
|
`buildSyntheticPlugin: ${bucket} entry '${entry}' not found in executable dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
|
|
1896
2066
|
);
|
|
1897
2067
|
};
|
|
1898
2068
|
if (cc.skills.length > 0) {
|
|
1899
|
-
const dst =
|
|
1900
|
-
|
|
2069
|
+
const dst = path10.join(root, "skills");
|
|
2070
|
+
fs11.mkdirSync(dst, { recursive: true });
|
|
1901
2071
|
for (const name of cc.skills) {
|
|
1902
|
-
copyDir(resolvePart("skills", name),
|
|
2072
|
+
copyDir(resolvePart("skills", name), path10.join(dst, name));
|
|
1903
2073
|
}
|
|
1904
2074
|
}
|
|
1905
2075
|
if (cc.commands.length > 0) {
|
|
1906
|
-
const dst =
|
|
1907
|
-
|
|
2076
|
+
const dst = path10.join(root, "commands");
|
|
2077
|
+
fs11.mkdirSync(dst, { recursive: true });
|
|
1908
2078
|
for (const name of cc.commands) {
|
|
1909
|
-
|
|
2079
|
+
fs11.copyFileSync(resolvePart("commands", `${name}.md`), path10.join(dst, `${name}.md`));
|
|
1910
2080
|
}
|
|
1911
2081
|
}
|
|
1912
2082
|
if (cc.subagents.length > 0) {
|
|
1913
|
-
const dst =
|
|
1914
|
-
|
|
2083
|
+
const dst = path10.join(root, "agents");
|
|
2084
|
+
fs11.mkdirSync(dst, { recursive: true });
|
|
1915
2085
|
for (const name of cc.subagents) {
|
|
1916
|
-
|
|
2086
|
+
fs11.copyFileSync(resolvePart("agents", `${name}.md`), path10.join(dst, `${name}.md`));
|
|
1917
2087
|
}
|
|
1918
2088
|
}
|
|
1919
2089
|
if (cc.hooks.length > 0) {
|
|
1920
|
-
const dst =
|
|
1921
|
-
|
|
2090
|
+
const dst = path10.join(root, "hooks");
|
|
2091
|
+
fs11.mkdirSync(dst, { recursive: true });
|
|
1922
2092
|
const merged = { hooks: {} };
|
|
1923
2093
|
for (const name of cc.hooks) {
|
|
1924
2094
|
const src = resolvePart("hooks", `${name}.json`);
|
|
1925
|
-
const parsed = JSON.parse(
|
|
2095
|
+
const parsed = JSON.parse(fs11.readFileSync(src, "utf-8"));
|
|
1926
2096
|
for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
|
|
1927
2097
|
if (!Array.isArray(entries)) continue;
|
|
1928
2098
|
if (!merged.hooks[event]) merged.hooks[event] = [];
|
|
1929
2099
|
merged.hooks[event].push(...entries);
|
|
1930
2100
|
}
|
|
1931
2101
|
}
|
|
1932
|
-
|
|
2102
|
+
fs11.writeFileSync(path10.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
|
|
1933
2103
|
`);
|
|
1934
2104
|
}
|
|
1935
2105
|
const manifest = {
|
|
@@ -1940,22 +2110,22 @@ var buildSyntheticPlugin = async (ctx, profile) => {
|
|
|
1940
2110
|
if (cc.skills.length > 0) manifest.skills = ["./skills/"];
|
|
1941
2111
|
if (cc.commands.length > 0) manifest.commands = ["./commands/"];
|
|
1942
2112
|
if (cc.subagents.length > 0) manifest.agents = cc.subagents.map((n) => `./agents/${n}.md`);
|
|
1943
|
-
|
|
2113
|
+
fs11.writeFileSync(path10.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
|
|
1944
2114
|
`);
|
|
1945
2115
|
ctx.data.syntheticPluginPath = root;
|
|
1946
2116
|
};
|
|
1947
2117
|
function copyDir(src, dst) {
|
|
1948
|
-
|
|
1949
|
-
for (const ent of
|
|
1950
|
-
const s =
|
|
1951
|
-
const d =
|
|
2118
|
+
fs11.mkdirSync(dst, { recursive: true });
|
|
2119
|
+
for (const ent of fs11.readdirSync(src, { withFileTypes: true })) {
|
|
2120
|
+
const s = path10.join(src, ent.name);
|
|
2121
|
+
const d = path10.join(dst, ent.name);
|
|
1952
2122
|
if (ent.isDirectory()) copyDir(s, d);
|
|
1953
|
-
else if (ent.isFile())
|
|
2123
|
+
else if (ent.isFile()) fs11.copyFileSync(s, d);
|
|
1954
2124
|
}
|
|
1955
2125
|
}
|
|
1956
2126
|
|
|
1957
2127
|
// src/coverage.ts
|
|
1958
|
-
import { execFileSync as
|
|
2128
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
1959
2129
|
function patternToRegex(pattern) {
|
|
1960
2130
|
let s = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
1961
2131
|
s = s.replace(/\*\*\//g, "\xA7S").replace(/\*\*/g, "\xA7A").replace(/\*/g, "[^/]*");
|
|
@@ -1973,7 +2143,7 @@ function renderSiblingPath(file, requireSibling) {
|
|
|
1973
2143
|
}
|
|
1974
2144
|
function safeGit(args, cwd) {
|
|
1975
2145
|
try {
|
|
1976
|
-
return
|
|
2146
|
+
return execFileSync7("git", args, { encoding: "utf-8", cwd, env: { ...process.env, HUSKY: "0" } }).trim();
|
|
1977
2147
|
} catch {
|
|
1978
2148
|
return "";
|
|
1979
2149
|
}
|
|
@@ -2016,18 +2186,18 @@ function formatMissesForFeedback(misses) {
|
|
|
2016
2186
|
}
|
|
2017
2187
|
|
|
2018
2188
|
// src/prompt.ts
|
|
2019
|
-
import * as
|
|
2020
|
-
import * as
|
|
2189
|
+
import * as fs12 from "fs";
|
|
2190
|
+
import * as path11 from "path";
|
|
2021
2191
|
var CONVENTIONS_PER_FILE_MAX_BYTES = 3e4;
|
|
2022
2192
|
var CONVENTION_FILES = ["CLAUDE.md", "AGENTS.md"];
|
|
2023
2193
|
function loadProjectConventions(projectDir) {
|
|
2024
2194
|
const out = [];
|
|
2025
2195
|
for (const rel of CONVENTION_FILES) {
|
|
2026
|
-
const abs =
|
|
2027
|
-
if (!
|
|
2196
|
+
const abs = path11.join(projectDir, rel);
|
|
2197
|
+
if (!fs12.existsSync(abs)) continue;
|
|
2028
2198
|
let content;
|
|
2029
2199
|
try {
|
|
2030
|
-
content =
|
|
2200
|
+
content = fs12.readFileSync(abs, "utf-8");
|
|
2031
2201
|
} catch {
|
|
2032
2202
|
continue;
|
|
2033
2203
|
}
|
|
@@ -2232,20 +2402,20 @@ var commitAndPush2 = async (ctx) => {
|
|
|
2232
2402
|
};
|
|
2233
2403
|
|
|
2234
2404
|
// src/scripts/composePrompt.ts
|
|
2235
|
-
import * as
|
|
2236
|
-
import * as
|
|
2405
|
+
import * as fs13 from "fs";
|
|
2406
|
+
import * as path12 from "path";
|
|
2237
2407
|
var MUSTACHE = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
|
|
2238
2408
|
var composePrompt = async (ctx, profile) => {
|
|
2239
2409
|
const explicit = ctx.data.promptTemplate;
|
|
2240
2410
|
const mode = ctx.args.mode;
|
|
2241
2411
|
const candidates = [
|
|
2242
|
-
explicit ?
|
|
2243
|
-
mode ?
|
|
2244
|
-
|
|
2412
|
+
explicit ? path12.join(profile.dir, explicit) : null,
|
|
2413
|
+
mode ? path12.join(profile.dir, "prompts", `${mode}.md`) : null,
|
|
2414
|
+
path12.join(profile.dir, "prompt.md")
|
|
2245
2415
|
].filter(Boolean);
|
|
2246
2416
|
let templatePath = "";
|
|
2247
2417
|
for (const c of candidates) {
|
|
2248
|
-
if (
|
|
2418
|
+
if (fs13.existsSync(c)) {
|
|
2249
2419
|
templatePath = c;
|
|
2250
2420
|
break;
|
|
2251
2421
|
}
|
|
@@ -2253,7 +2423,7 @@ var composePrompt = async (ctx, profile) => {
|
|
|
2253
2423
|
if (!templatePath) {
|
|
2254
2424
|
throw new Error(`profile at ${profile.dir}: no prompt template found (tried ${candidates.join(", ")})`);
|
|
2255
2425
|
}
|
|
2256
|
-
const template =
|
|
2426
|
+
const template = fs13.readFileSync(templatePath, "utf-8");
|
|
2257
2427
|
const tokens = {
|
|
2258
2428
|
...stringifyAll(ctx.args, "args."),
|
|
2259
2429
|
...stringifyAll(ctx.data, ""),
|
|
@@ -2330,16 +2500,16 @@ function formatToolsUsage(profile) {
|
|
|
2330
2500
|
}
|
|
2331
2501
|
|
|
2332
2502
|
// src/scripts/diagMcp.ts
|
|
2333
|
-
import { execFileSync as
|
|
2334
|
-
import * as
|
|
2503
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
2504
|
+
import * as fs14 from "fs";
|
|
2335
2505
|
import * as os3 from "os";
|
|
2336
|
-
import * as
|
|
2506
|
+
import * as path13 from "path";
|
|
2337
2507
|
var diagMcp = async (_ctx) => {
|
|
2338
2508
|
const home = os3.homedir();
|
|
2339
|
-
const cacheDir =
|
|
2509
|
+
const cacheDir = path13.join(home, ".cache", "ms-playwright");
|
|
2340
2510
|
let entries = [];
|
|
2341
2511
|
try {
|
|
2342
|
-
entries =
|
|
2512
|
+
entries = fs14.readdirSync(cacheDir);
|
|
2343
2513
|
} catch {
|
|
2344
2514
|
}
|
|
2345
2515
|
const hasChromium = entries.some((e) => e.startsWith("chromium"));
|
|
@@ -2350,7 +2520,7 @@ var diagMcp = async (_ctx) => {
|
|
|
2350
2520
|
process.stderr.write(`[kody diag] chromium present: ${hasChromium ? "yes" : "no"}
|
|
2351
2521
|
`);
|
|
2352
2522
|
try {
|
|
2353
|
-
const v =
|
|
2523
|
+
const v = execFileSync8("npx", ["-y", "--package=@playwright/mcp@latest", "--", "playwright-mcp", "--version"], {
|
|
2354
2524
|
stdio: "pipe",
|
|
2355
2525
|
timeout: 6e4,
|
|
2356
2526
|
encoding: "utf8"
|
|
@@ -2365,17 +2535,17 @@ var diagMcp = async (_ctx) => {
|
|
|
2365
2535
|
};
|
|
2366
2536
|
|
|
2367
2537
|
// src/scripts/discoverQaContext.ts
|
|
2368
|
-
import * as
|
|
2369
|
-
import * as
|
|
2538
|
+
import * as fs16 from "fs";
|
|
2539
|
+
import * as path15 from "path";
|
|
2370
2540
|
|
|
2371
2541
|
// src/scripts/frameworkDetectors.ts
|
|
2372
|
-
import * as
|
|
2373
|
-
import * as
|
|
2542
|
+
import * as fs15 from "fs";
|
|
2543
|
+
import * as path14 from "path";
|
|
2374
2544
|
function detectFrameworks(cwd) {
|
|
2375
2545
|
const out = [];
|
|
2376
2546
|
let deps = {};
|
|
2377
2547
|
try {
|
|
2378
|
-
const pkg = JSON.parse(
|
|
2548
|
+
const pkg = JSON.parse(fs15.readFileSync(path14.join(cwd, "package.json"), "utf-8"));
|
|
2379
2549
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2380
2550
|
} catch {
|
|
2381
2551
|
return out;
|
|
@@ -2412,7 +2582,7 @@ function detectFrameworks(cwd) {
|
|
|
2412
2582
|
}
|
|
2413
2583
|
function findFile(cwd, candidates) {
|
|
2414
2584
|
for (const c of candidates) {
|
|
2415
|
-
if (
|
|
2585
|
+
if (fs15.existsSync(path14.join(cwd, c))) return c;
|
|
2416
2586
|
}
|
|
2417
2587
|
return null;
|
|
2418
2588
|
}
|
|
@@ -2425,18 +2595,18 @@ var COLLECTION_DIRS = [
|
|
|
2425
2595
|
function discoverPayloadCollections(cwd) {
|
|
2426
2596
|
const out = [];
|
|
2427
2597
|
for (const dir of COLLECTION_DIRS) {
|
|
2428
|
-
const full =
|
|
2429
|
-
if (!
|
|
2598
|
+
const full = path14.join(cwd, dir);
|
|
2599
|
+
if (!fs15.existsSync(full)) continue;
|
|
2430
2600
|
let files;
|
|
2431
2601
|
try {
|
|
2432
|
-
files =
|
|
2602
|
+
files = fs15.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
2433
2603
|
} catch {
|
|
2434
2604
|
continue;
|
|
2435
2605
|
}
|
|
2436
2606
|
for (const file of files) {
|
|
2437
2607
|
try {
|
|
2438
|
-
const filePath =
|
|
2439
|
-
const content =
|
|
2608
|
+
const filePath = path14.join(full, file);
|
|
2609
|
+
const content = fs15.readFileSync(filePath, "utf-8").slice(0, 1e4);
|
|
2440
2610
|
const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
|
|
2441
2611
|
if (!slugMatch) continue;
|
|
2442
2612
|
const slug = slugMatch[1];
|
|
@@ -2450,7 +2620,7 @@ function discoverPayloadCollections(cwd) {
|
|
|
2450
2620
|
out.push({
|
|
2451
2621
|
name,
|
|
2452
2622
|
slug,
|
|
2453
|
-
filePath:
|
|
2623
|
+
filePath: path14.relative(cwd, filePath),
|
|
2454
2624
|
fields: fields.slice(0, 20),
|
|
2455
2625
|
hasAdmin
|
|
2456
2626
|
});
|
|
@@ -2464,28 +2634,28 @@ var ADMIN_COMPONENT_DIRS = ["src/ui/admin", "src/admin/components", "src/compone
|
|
|
2464
2634
|
function discoverAdminComponents(cwd, collections) {
|
|
2465
2635
|
const out = [];
|
|
2466
2636
|
for (const dir of ADMIN_COMPONENT_DIRS) {
|
|
2467
|
-
const full =
|
|
2468
|
-
if (!
|
|
2637
|
+
const full = path14.join(cwd, dir);
|
|
2638
|
+
if (!fs15.existsSync(full)) continue;
|
|
2469
2639
|
let entries;
|
|
2470
2640
|
try {
|
|
2471
|
-
entries =
|
|
2641
|
+
entries = fs15.readdirSync(full, { withFileTypes: true });
|
|
2472
2642
|
} catch {
|
|
2473
2643
|
continue;
|
|
2474
2644
|
}
|
|
2475
2645
|
for (const entry of entries) {
|
|
2476
|
-
const entryPath =
|
|
2646
|
+
const entryPath = path14.join(full, entry.name);
|
|
2477
2647
|
let name;
|
|
2478
2648
|
let filePath;
|
|
2479
2649
|
if (entry.isDirectory()) {
|
|
2480
2650
|
const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
|
|
2481
|
-
(f) =>
|
|
2651
|
+
(f) => fs15.existsSync(path14.join(entryPath, f))
|
|
2482
2652
|
);
|
|
2483
2653
|
if (!indexFile) continue;
|
|
2484
2654
|
name = entry.name;
|
|
2485
|
-
filePath =
|
|
2655
|
+
filePath = path14.relative(cwd, path14.join(entryPath, indexFile));
|
|
2486
2656
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
2487
2657
|
name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
|
|
2488
|
-
filePath =
|
|
2658
|
+
filePath = path14.relative(cwd, entryPath);
|
|
2489
2659
|
} else {
|
|
2490
2660
|
continue;
|
|
2491
2661
|
}
|
|
@@ -2493,7 +2663,7 @@ function discoverAdminComponents(cwd, collections) {
|
|
|
2493
2663
|
if (collections) {
|
|
2494
2664
|
for (const col of collections) {
|
|
2495
2665
|
try {
|
|
2496
|
-
const colContent =
|
|
2666
|
+
const colContent = fs15.readFileSync(path14.join(cwd, col.filePath), "utf-8");
|
|
2497
2667
|
if (colContent.includes(name)) {
|
|
2498
2668
|
usedInCollection = col.slug;
|
|
2499
2669
|
break;
|
|
@@ -2512,8 +2682,8 @@ function scanApiRoutes(cwd) {
|
|
|
2512
2682
|
const out = [];
|
|
2513
2683
|
const appDirs = ["src/app", "app"];
|
|
2514
2684
|
for (const appDir of appDirs) {
|
|
2515
|
-
const apiDir =
|
|
2516
|
-
if (!
|
|
2685
|
+
const apiDir = path14.join(cwd, appDir, "api");
|
|
2686
|
+
if (!fs15.existsSync(apiDir)) continue;
|
|
2517
2687
|
walkApiRoutes(apiDir, "/api", cwd, out);
|
|
2518
2688
|
break;
|
|
2519
2689
|
}
|
|
@@ -2522,14 +2692,14 @@ function scanApiRoutes(cwd) {
|
|
|
2522
2692
|
function walkApiRoutes(dir, prefix, cwd, out) {
|
|
2523
2693
|
let entries;
|
|
2524
2694
|
try {
|
|
2525
|
-
entries =
|
|
2695
|
+
entries = fs15.readdirSync(dir, { withFileTypes: true });
|
|
2526
2696
|
} catch {
|
|
2527
2697
|
return;
|
|
2528
2698
|
}
|
|
2529
2699
|
const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
|
|
2530
2700
|
if (routeFile) {
|
|
2531
2701
|
try {
|
|
2532
|
-
const content =
|
|
2702
|
+
const content = fs15.readFileSync(path14.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
|
|
2533
2703
|
const methods = HTTP_METHODS.filter(
|
|
2534
2704
|
(m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
|
|
2535
2705
|
);
|
|
@@ -2537,7 +2707,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
2537
2707
|
out.push({
|
|
2538
2708
|
path: prefix,
|
|
2539
2709
|
methods,
|
|
2540
|
-
filePath:
|
|
2710
|
+
filePath: path14.relative(cwd, path14.join(dir, routeFile.name))
|
|
2541
2711
|
});
|
|
2542
2712
|
}
|
|
2543
2713
|
} catch {
|
|
@@ -2548,7 +2718,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
2548
2718
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
2549
2719
|
let segment = entry.name;
|
|
2550
2720
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
2551
|
-
walkApiRoutes(
|
|
2721
|
+
walkApiRoutes(path14.join(dir, entry.name), prefix, cwd, out);
|
|
2552
2722
|
continue;
|
|
2553
2723
|
}
|
|
2554
2724
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -2556,7 +2726,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
|
|
|
2556
2726
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
2557
2727
|
segment = `:${segment.slice(1, -1)}`;
|
|
2558
2728
|
}
|
|
2559
|
-
walkApiRoutes(
|
|
2729
|
+
walkApiRoutes(path14.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
|
|
2560
2730
|
}
|
|
2561
2731
|
}
|
|
2562
2732
|
var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
@@ -2576,10 +2746,10 @@ var BUILTIN_ENV_VARS = /* @__PURE__ */ new Set([
|
|
|
2576
2746
|
function scanEnvVars(cwd) {
|
|
2577
2747
|
const candidates = [".env.example", ".env.local.example", ".env.template"];
|
|
2578
2748
|
for (const envFile of candidates) {
|
|
2579
|
-
const envPath =
|
|
2580
|
-
if (!
|
|
2749
|
+
const envPath = path14.join(cwd, envFile);
|
|
2750
|
+
if (!fs15.existsSync(envPath)) continue;
|
|
2581
2751
|
try {
|
|
2582
|
-
const content =
|
|
2752
|
+
const content = fs15.readFileSync(envPath, "utf-8");
|
|
2583
2753
|
const vars = [];
|
|
2584
2754
|
for (const line of content.split("\n")) {
|
|
2585
2755
|
const trimmed = line.trim();
|
|
@@ -2627,9 +2797,9 @@ function runQaDiscovery(cwd) {
|
|
|
2627
2797
|
}
|
|
2628
2798
|
function detectDevServer(cwd, out) {
|
|
2629
2799
|
try {
|
|
2630
|
-
const pkg = JSON.parse(
|
|
2800
|
+
const pkg = JSON.parse(fs16.readFileSync(path15.join(cwd, "package.json"), "utf-8"));
|
|
2631
2801
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2632
|
-
const pm =
|
|
2802
|
+
const pm = fs16.existsSync(path15.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs16.existsSync(path15.join(cwd, "yarn.lock")) ? "yarn" : fs16.existsSync(path15.join(cwd, "bun.lockb")) ? "bun" : "npm";
|
|
2633
2803
|
if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
|
|
2634
2804
|
if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
|
|
2635
2805
|
else if (allDeps.vite) out.devPort = 5173;
|
|
@@ -2639,8 +2809,8 @@ function detectDevServer(cwd, out) {
|
|
|
2639
2809
|
function scanFrontendRoutes(cwd, out) {
|
|
2640
2810
|
const appDirs = ["src/app", "app"];
|
|
2641
2811
|
for (const appDir of appDirs) {
|
|
2642
|
-
const full =
|
|
2643
|
-
if (!
|
|
2812
|
+
const full = path15.join(cwd, appDir);
|
|
2813
|
+
if (!fs16.existsSync(full)) continue;
|
|
2644
2814
|
walkFrontendRoutes(full, "", out);
|
|
2645
2815
|
break;
|
|
2646
2816
|
}
|
|
@@ -2648,7 +2818,7 @@ function scanFrontendRoutes(cwd, out) {
|
|
|
2648
2818
|
function walkFrontendRoutes(dir, prefix, out) {
|
|
2649
2819
|
let entries;
|
|
2650
2820
|
try {
|
|
2651
|
-
entries =
|
|
2821
|
+
entries = fs16.readdirSync(dir, { withFileTypes: true });
|
|
2652
2822
|
} catch {
|
|
2653
2823
|
return;
|
|
2654
2824
|
}
|
|
@@ -2665,7 +2835,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
2665
2835
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
2666
2836
|
let segment = entry.name;
|
|
2667
2837
|
if (segment.startsWith("(") && segment.endsWith(")")) {
|
|
2668
|
-
walkFrontendRoutes(
|
|
2838
|
+
walkFrontendRoutes(path15.join(dir, entry.name), prefix, out);
|
|
2669
2839
|
continue;
|
|
2670
2840
|
}
|
|
2671
2841
|
if (segment.startsWith("[[") && segment.endsWith("]]")) {
|
|
@@ -2673,7 +2843,7 @@ function walkFrontendRoutes(dir, prefix, out) {
|
|
|
2673
2843
|
} else if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
2674
2844
|
segment = `:${segment.slice(1, -1)}`;
|
|
2675
2845
|
}
|
|
2676
|
-
walkFrontendRoutes(
|
|
2846
|
+
walkFrontendRoutes(path15.join(dir, entry.name), `${prefix}/${segment}`, out);
|
|
2677
2847
|
}
|
|
2678
2848
|
}
|
|
2679
2849
|
function detectAuthFiles(cwd, out) {
|
|
@@ -2690,23 +2860,23 @@ function detectAuthFiles(cwd, out) {
|
|
|
2690
2860
|
"src/app/api/oauth"
|
|
2691
2861
|
];
|
|
2692
2862
|
for (const c of candidates) {
|
|
2693
|
-
if (
|
|
2863
|
+
if (fs16.existsSync(path15.join(cwd, c))) out.authFiles.push(c);
|
|
2694
2864
|
}
|
|
2695
2865
|
}
|
|
2696
2866
|
function detectRoles(cwd, out) {
|
|
2697
2867
|
const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
|
|
2698
2868
|
for (const rp of rolePaths) {
|
|
2699
|
-
const dir =
|
|
2700
|
-
if (!
|
|
2869
|
+
const dir = path15.join(cwd, rp);
|
|
2870
|
+
if (!fs16.existsSync(dir)) continue;
|
|
2701
2871
|
let files;
|
|
2702
2872
|
try {
|
|
2703
|
-
files =
|
|
2873
|
+
files = fs16.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
|
2704
2874
|
} catch {
|
|
2705
2875
|
continue;
|
|
2706
2876
|
}
|
|
2707
2877
|
for (const f of files) {
|
|
2708
2878
|
try {
|
|
2709
|
-
const content =
|
|
2879
|
+
const content = fs16.readFileSync(path15.join(dir, f), "utf-8").slice(0, 5e3);
|
|
2710
2880
|
const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
|
|
2711
2881
|
if (roleMatches) {
|
|
2712
2882
|
for (const m of roleMatches) {
|
|
@@ -2852,7 +3022,7 @@ var discoverQaContext = async (ctx) => {
|
|
|
2852
3022
|
};
|
|
2853
3023
|
|
|
2854
3024
|
// src/scripts/dispatch.ts
|
|
2855
|
-
import { execFileSync as
|
|
3025
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
2856
3026
|
var API_TIMEOUT_MS3 = 3e4;
|
|
2857
3027
|
var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
2858
3028
|
const next = args?.next;
|
|
@@ -2888,7 +3058,7 @@ var dispatch = async (ctx, _profile, _agentResult, args) => {
|
|
|
2888
3058
|
const sub = usePr ? "pr" : "issue";
|
|
2889
3059
|
const body = `@kody ${next}`;
|
|
2890
3060
|
try {
|
|
2891
|
-
|
|
3061
|
+
execFileSync9("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
2892
3062
|
timeout: API_TIMEOUT_MS3,
|
|
2893
3063
|
cwd: ctx.cwd,
|
|
2894
3064
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2908,7 +3078,7 @@ function parsePr(url) {
|
|
|
2908
3078
|
}
|
|
2909
3079
|
|
|
2910
3080
|
// src/scripts/dispatchClassified.ts
|
|
2911
|
-
import { execFileSync as
|
|
3081
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
2912
3082
|
var API_TIMEOUT_MS4 = 3e4;
|
|
2913
3083
|
var VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
2914
3084
|
var dispatchClassified = async (ctx) => {
|
|
@@ -2917,7 +3087,7 @@ var dispatchClassified = async (ctx) => {
|
|
|
2917
3087
|
const classification = ctx.data.classification;
|
|
2918
3088
|
if (!classification || !VALID_CLASSES2.has(classification)) return;
|
|
2919
3089
|
try {
|
|
2920
|
-
|
|
3090
|
+
execFileSync10("gh", ["issue", "comment", String(issueNumber), "--body", `@kody ${classification}`], {
|
|
2921
3091
|
cwd: ctx.cwd,
|
|
2922
3092
|
timeout: API_TIMEOUT_MS4,
|
|
2923
3093
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -2937,11 +3107,11 @@ function failedAction(reason) {
|
|
|
2937
3107
|
}
|
|
2938
3108
|
|
|
2939
3109
|
// src/scripts/dispatchMissionFileTicks.ts
|
|
2940
|
-
import * as
|
|
2941
|
-
import * as
|
|
3110
|
+
import * as fs18 from "fs";
|
|
3111
|
+
import * as path17 from "path";
|
|
2942
3112
|
|
|
2943
3113
|
// src/issue.ts
|
|
2944
|
-
import { execFileSync as
|
|
3114
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
2945
3115
|
var API_TIMEOUT_MS5 = 3e4;
|
|
2946
3116
|
function ghToken2() {
|
|
2947
3117
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
@@ -2949,7 +3119,7 @@ function ghToken2() {
|
|
|
2949
3119
|
function gh2(args, options) {
|
|
2950
3120
|
const token = ghToken2();
|
|
2951
3121
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
2952
|
-
return
|
|
3122
|
+
return execFileSync11("gh", args, {
|
|
2953
3123
|
encoding: "utf-8",
|
|
2954
3124
|
timeout: API_TIMEOUT_MS5,
|
|
2955
3125
|
cwd: options?.cwd,
|
|
@@ -3251,8 +3421,8 @@ var ContentsApiBackend = class {
|
|
|
3251
3421
|
};
|
|
3252
3422
|
|
|
3253
3423
|
// src/scripts/missionState/localFileBackend.ts
|
|
3254
|
-
import * as
|
|
3255
|
-
import * as
|
|
3424
|
+
import * as fs17 from "fs";
|
|
3425
|
+
import * as path16 from "path";
|
|
3256
3426
|
var LocalFileBackend = class {
|
|
3257
3427
|
name = "local-file";
|
|
3258
3428
|
cwd;
|
|
@@ -3267,7 +3437,7 @@ var LocalFileBackend = class {
|
|
|
3267
3437
|
if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
|
|
3268
3438
|
this.cwd = opts.cwd;
|
|
3269
3439
|
this.missionsDir = opts.missionsDir;
|
|
3270
|
-
this.absDir =
|
|
3440
|
+
this.absDir = path16.join(opts.cwd, opts.missionsDir);
|
|
3271
3441
|
this.owner = opts.owner;
|
|
3272
3442
|
this.repo = opts.repo;
|
|
3273
3443
|
this.cache = opts.cache ?? defaultCacheAdapter();
|
|
@@ -3282,7 +3452,7 @@ var LocalFileBackend = class {
|
|
|
3282
3452
|
`);
|
|
3283
3453
|
return;
|
|
3284
3454
|
}
|
|
3285
|
-
|
|
3455
|
+
fs17.mkdirSync(this.absDir, { recursive: true });
|
|
3286
3456
|
const prefix = this.cacheKeyPrefix();
|
|
3287
3457
|
const probeKey = `${prefix}probe-${Date.now()}`;
|
|
3288
3458
|
try {
|
|
@@ -3311,7 +3481,7 @@ var LocalFileBackend = class {
|
|
|
3311
3481
|
`);
|
|
3312
3482
|
return;
|
|
3313
3483
|
}
|
|
3314
|
-
if (!
|
|
3484
|
+
if (!fs17.existsSync(this.absDir)) {
|
|
3315
3485
|
return;
|
|
3316
3486
|
}
|
|
3317
3487
|
const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
|
|
@@ -3327,11 +3497,11 @@ var LocalFileBackend = class {
|
|
|
3327
3497
|
}
|
|
3328
3498
|
load(slug) {
|
|
3329
3499
|
const relPath = stateFilePath(this.missionsDir, slug);
|
|
3330
|
-
const absPath =
|
|
3331
|
-
if (!
|
|
3500
|
+
const absPath = path16.join(this.cwd, relPath);
|
|
3501
|
+
if (!fs17.existsSync(absPath)) {
|
|
3332
3502
|
return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
|
|
3333
3503
|
}
|
|
3334
|
-
const raw =
|
|
3504
|
+
const raw = fs17.readFileSync(absPath, "utf-8");
|
|
3335
3505
|
let parsed;
|
|
3336
3506
|
try {
|
|
3337
3507
|
parsed = JSON.parse(raw);
|
|
@@ -3348,10 +3518,10 @@ var LocalFileBackend = class {
|
|
|
3348
3518
|
if (!loaded.created && isStateUnchanged(loaded.state, next)) {
|
|
3349
3519
|
return false;
|
|
3350
3520
|
}
|
|
3351
|
-
const absPath =
|
|
3352
|
-
|
|
3521
|
+
const absPath = path16.join(this.cwd, loaded.path);
|
|
3522
|
+
fs17.mkdirSync(path16.dirname(absPath), { recursive: true });
|
|
3353
3523
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
3354
|
-
|
|
3524
|
+
fs17.writeFileSync(absPath, body, "utf-8");
|
|
3355
3525
|
return true;
|
|
3356
3526
|
}
|
|
3357
3527
|
cacheKeyPrefix() {
|
|
@@ -3428,7 +3598,7 @@ var dispatchMissionFileTicks = async (ctx, _profile, args) => {
|
|
|
3428
3598
|
await backend.hydrate();
|
|
3429
3599
|
}
|
|
3430
3600
|
try {
|
|
3431
|
-
const slugs = listMissionSlugs(
|
|
3601
|
+
const slugs = listMissionSlugs(path17.join(ctx.cwd, missionsDir));
|
|
3432
3602
|
ctx.data.missionSlugCount = slugs.length;
|
|
3433
3603
|
if (slugs.length === 0) {
|
|
3434
3604
|
process.stdout.write(`[missions] no mission files in ${missionsDir}
|
|
@@ -3476,10 +3646,10 @@ var dispatchMissionFileTicks = async (ctx, _profile, args) => {
|
|
|
3476
3646
|
}
|
|
3477
3647
|
};
|
|
3478
3648
|
function listMissionSlugs(absDir) {
|
|
3479
|
-
if (!
|
|
3649
|
+
if (!fs18.existsSync(absDir)) return [];
|
|
3480
3650
|
let entries;
|
|
3481
3651
|
try {
|
|
3482
|
-
entries =
|
|
3652
|
+
entries = fs18.readdirSync(absDir, { withFileTypes: true });
|
|
3483
3653
|
} catch {
|
|
3484
3654
|
return [];
|
|
3485
3655
|
}
|
|
@@ -3848,7 +4018,7 @@ function collectExpectedTests(raw) {
|
|
|
3848
4018
|
}
|
|
3849
4019
|
|
|
3850
4020
|
// src/scripts/finishFlow.ts
|
|
3851
|
-
import { execFileSync as
|
|
4021
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
3852
4022
|
|
|
3853
4023
|
// src/lifecycleLabels.ts
|
|
3854
4024
|
var KODY_NAMESPACE = "kody";
|
|
@@ -4001,7 +4171,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
|
|
|
4001
4171
|
**PR:** ${state.core.prUrl}` : "";
|
|
4002
4172
|
const body = `${icon} kody flow \`${flowName}\` finished \u2014 \`${reason}\`${prSuffix}`;
|
|
4003
4173
|
try {
|
|
4004
|
-
|
|
4174
|
+
execFileSync12("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
4005
4175
|
timeout: API_TIMEOUT_MS6,
|
|
4006
4176
|
cwd: ctx.cwd,
|
|
4007
4177
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4015,7 +4185,7 @@ var finishFlow = async (ctx, _profile, _agentResult, args) => {
|
|
|
4015
4185
|
};
|
|
4016
4186
|
|
|
4017
4187
|
// src/branch.ts
|
|
4018
|
-
import { execFileSync as
|
|
4188
|
+
import { execFileSync as execFileSync13 } from "child_process";
|
|
4019
4189
|
var UncommittedChangesError = class extends Error {
|
|
4020
4190
|
constructor(branch) {
|
|
4021
4191
|
super(`Uncommitted changes on branch '${branch}' \u2014 refusing to run to protect work in progress`);
|
|
@@ -4025,7 +4195,7 @@ var UncommittedChangesError = class extends Error {
|
|
|
4025
4195
|
branch;
|
|
4026
4196
|
};
|
|
4027
4197
|
function git2(args, cwd) {
|
|
4028
|
-
return
|
|
4198
|
+
return execFileSync13("git", args, {
|
|
4029
4199
|
encoding: "utf-8",
|
|
4030
4200
|
timeout: 3e4,
|
|
4031
4201
|
cwd,
|
|
@@ -4050,7 +4220,7 @@ function checkoutPrBranch(prNumber, cwd) {
|
|
|
4050
4220
|
SKIP_HOOKS: "1",
|
|
4051
4221
|
GH_TOKEN: process.env.GH_PAT?.trim() || process.env.GH_TOKEN || ""
|
|
4052
4222
|
};
|
|
4053
|
-
|
|
4223
|
+
execFileSync13("gh", ["pr", "checkout", String(prNumber)], {
|
|
4054
4224
|
cwd,
|
|
4055
4225
|
env,
|
|
4056
4226
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -4117,8 +4287,8 @@ function ensureFeatureBranch(issueNumber, title, defaultBranch, cwd) {
|
|
|
4117
4287
|
}
|
|
4118
4288
|
|
|
4119
4289
|
// src/gha.ts
|
|
4120
|
-
import { execFileSync as
|
|
4121
|
-
import * as
|
|
4290
|
+
import { execFileSync as execFileSync14 } from "child_process";
|
|
4291
|
+
import * as fs19 from "fs";
|
|
4122
4292
|
function getRunUrl() {
|
|
4123
4293
|
const server = process.env.GITHUB_SERVER_URL;
|
|
4124
4294
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -4129,10 +4299,10 @@ function getRunUrl() {
|
|
|
4129
4299
|
function reactToTriggerComment(cwd) {
|
|
4130
4300
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
4131
4301
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
4132
|
-
if (!eventPath || !
|
|
4302
|
+
if (!eventPath || !fs19.existsSync(eventPath)) return;
|
|
4133
4303
|
let event = null;
|
|
4134
4304
|
try {
|
|
4135
|
-
event = JSON.parse(
|
|
4305
|
+
event = JSON.parse(fs19.readFileSync(eventPath, "utf-8"));
|
|
4136
4306
|
} catch {
|
|
4137
4307
|
return;
|
|
4138
4308
|
}
|
|
@@ -4160,7 +4330,7 @@ function reactToTriggerComment(cwd) {
|
|
|
4160
4330
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
4161
4331
|
if (attempt > 0) sleepMs(attempt === 1 ? 500 : 1500);
|
|
4162
4332
|
try {
|
|
4163
|
-
|
|
4333
|
+
execFileSync14("gh", args, opts);
|
|
4164
4334
|
return;
|
|
4165
4335
|
} catch (err) {
|
|
4166
4336
|
lastErr = err;
|
|
@@ -4173,13 +4343,13 @@ function reactToTriggerComment(cwd) {
|
|
|
4173
4343
|
}
|
|
4174
4344
|
function sleepMs(ms) {
|
|
4175
4345
|
try {
|
|
4176
|
-
|
|
4346
|
+
execFileSync14("sleep", [(ms / 1e3).toString()], { stdio: "ignore", timeout: ms + 1e3 });
|
|
4177
4347
|
} catch {
|
|
4178
4348
|
}
|
|
4179
4349
|
}
|
|
4180
4350
|
|
|
4181
4351
|
// src/workflow.ts
|
|
4182
|
-
import { execFileSync as
|
|
4352
|
+
import { execFileSync as execFileSync15 } from "child_process";
|
|
4183
4353
|
var GH_TIMEOUT_MS = 3e4;
|
|
4184
4354
|
function ghToken3() {
|
|
4185
4355
|
return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
|
|
@@ -4187,7 +4357,7 @@ function ghToken3() {
|
|
|
4187
4357
|
function gh3(args, cwd) {
|
|
4188
4358
|
const token = ghToken3();
|
|
4189
4359
|
const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
|
|
4190
|
-
return
|
|
4360
|
+
return execFileSync15("gh", args, {
|
|
4191
4361
|
encoding: "utf-8",
|
|
4192
4362
|
timeout: GH_TIMEOUT_MS,
|
|
4193
4363
|
cwd,
|
|
@@ -4371,23 +4541,23 @@ function tryPostPr2(prNumber, body, cwd) {
|
|
|
4371
4541
|
}
|
|
4372
4542
|
|
|
4373
4543
|
// src/scripts/initFlow.ts
|
|
4374
|
-
import { execFileSync as
|
|
4375
|
-
import * as
|
|
4376
|
-
import * as
|
|
4544
|
+
import { execFileSync as execFileSync16 } from "child_process";
|
|
4545
|
+
import * as fs21 from "fs";
|
|
4546
|
+
import * as path19 from "path";
|
|
4377
4547
|
|
|
4378
4548
|
// src/scripts/loadQaGuide.ts
|
|
4379
|
-
import * as
|
|
4380
|
-
import * as
|
|
4549
|
+
import * as fs20 from "fs";
|
|
4550
|
+
import * as path18 from "path";
|
|
4381
4551
|
var QA_GUIDE_REL_PATH = ".kody/qa-guide.md";
|
|
4382
4552
|
var loadQaGuide = async (ctx) => {
|
|
4383
|
-
const full =
|
|
4384
|
-
if (!
|
|
4553
|
+
const full = path18.join(ctx.cwd, QA_GUIDE_REL_PATH);
|
|
4554
|
+
if (!fs20.existsSync(full)) {
|
|
4385
4555
|
ctx.data.qaGuide = "";
|
|
4386
4556
|
ctx.data.qaGuidePath = "";
|
|
4387
4557
|
return;
|
|
4388
4558
|
}
|
|
4389
4559
|
try {
|
|
4390
|
-
ctx.data.qaGuide =
|
|
4560
|
+
ctx.data.qaGuide = fs20.readFileSync(full, "utf-8");
|
|
4391
4561
|
ctx.data.qaGuidePath = QA_GUIDE_REL_PATH;
|
|
4392
4562
|
} catch {
|
|
4393
4563
|
ctx.data.qaGuide = "";
|
|
@@ -4397,9 +4567,9 @@ var loadQaGuide = async (ctx) => {
|
|
|
4397
4567
|
|
|
4398
4568
|
// src/scripts/initFlow.ts
|
|
4399
4569
|
function detectPackageManager(cwd) {
|
|
4400
|
-
if (
|
|
4401
|
-
if (
|
|
4402
|
-
if (
|
|
4570
|
+
if (fs21.existsSync(path19.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
4571
|
+
if (fs21.existsSync(path19.join(cwd, "yarn.lock"))) return "yarn";
|
|
4572
|
+
if (fs21.existsSync(path19.join(cwd, "bun.lockb"))) return "bun";
|
|
4403
4573
|
return "npm";
|
|
4404
4574
|
}
|
|
4405
4575
|
function qualityCommandsFor(pm) {
|
|
@@ -4412,7 +4582,7 @@ function qualityCommandsFor(pm) {
|
|
|
4412
4582
|
function detectOwnerRepo(cwd) {
|
|
4413
4583
|
let url;
|
|
4414
4584
|
try {
|
|
4415
|
-
url =
|
|
4585
|
+
url = execFileSync16("git", ["remote", "get-url", "origin"], {
|
|
4416
4586
|
cwd,
|
|
4417
4587
|
encoding: "utf-8",
|
|
4418
4588
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4497,7 +4667,7 @@ jobs:
|
|
|
4497
4667
|
`;
|
|
4498
4668
|
function defaultBranchFromGit(cwd) {
|
|
4499
4669
|
try {
|
|
4500
|
-
const ref =
|
|
4670
|
+
const ref = execFileSync16("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
4501
4671
|
cwd,
|
|
4502
4672
|
encoding: "utf-8",
|
|
4503
4673
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4505,7 +4675,7 @@ function defaultBranchFromGit(cwd) {
|
|
|
4505
4675
|
return ref.replace("refs/remotes/origin/", "");
|
|
4506
4676
|
} catch {
|
|
4507
4677
|
try {
|
|
4508
|
-
return
|
|
4678
|
+
return execFileSync16("git", ["branch", "--show-current"], {
|
|
4509
4679
|
cwd,
|
|
4510
4680
|
encoding: "utf-8",
|
|
4511
4681
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4521,33 +4691,33 @@ function performInit(cwd, force) {
|
|
|
4521
4691
|
const pm = detectPackageManager(cwd);
|
|
4522
4692
|
const ownerRepo = detectOwnerRepo(cwd);
|
|
4523
4693
|
const defaultBranch = defaultBranchFromGit(cwd);
|
|
4524
|
-
const configPath =
|
|
4525
|
-
if (
|
|
4694
|
+
const configPath = path19.join(cwd, "kody.config.json");
|
|
4695
|
+
if (fs21.existsSync(configPath) && !force) {
|
|
4526
4696
|
skipped.push("kody.config.json");
|
|
4527
4697
|
} else {
|
|
4528
4698
|
const cfg = makeConfig(pm, ownerRepo, defaultBranch);
|
|
4529
|
-
|
|
4699
|
+
fs21.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
|
|
4530
4700
|
`);
|
|
4531
4701
|
wrote.push("kody.config.json");
|
|
4532
4702
|
}
|
|
4533
|
-
const workflowDir =
|
|
4534
|
-
const workflowPath =
|
|
4535
|
-
if (
|
|
4703
|
+
const workflowDir = path19.join(cwd, ".github", "workflows");
|
|
4704
|
+
const workflowPath = path19.join(workflowDir, "kody.yml");
|
|
4705
|
+
if (fs21.existsSync(workflowPath) && !force) {
|
|
4536
4706
|
skipped.push(".github/workflows/kody.yml");
|
|
4537
4707
|
} else {
|
|
4538
|
-
|
|
4539
|
-
|
|
4708
|
+
fs21.mkdirSync(workflowDir, { recursive: true });
|
|
4709
|
+
fs21.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
|
|
4540
4710
|
wrote.push(".github/workflows/kody.yml");
|
|
4541
4711
|
}
|
|
4542
|
-
const hasUi =
|
|
4712
|
+
const hasUi = fs21.existsSync(path19.join(cwd, "src/app")) || fs21.existsSync(path19.join(cwd, "app")) || fs21.existsSync(path19.join(cwd, "pages"));
|
|
4543
4713
|
if (hasUi) {
|
|
4544
|
-
const qaGuidePath =
|
|
4545
|
-
if (
|
|
4714
|
+
const qaGuidePath = path19.join(cwd, QA_GUIDE_REL_PATH);
|
|
4715
|
+
if (fs21.existsSync(qaGuidePath) && !force) {
|
|
4546
4716
|
skipped.push(QA_GUIDE_REL_PATH);
|
|
4547
4717
|
} else {
|
|
4548
|
-
|
|
4718
|
+
fs21.mkdirSync(path19.dirname(qaGuidePath), { recursive: true });
|
|
4549
4719
|
const discovery = runQaDiscovery(cwd);
|
|
4550
|
-
|
|
4720
|
+
fs21.writeFileSync(qaGuidePath, generateQaGuideTemplate(discovery));
|
|
4551
4721
|
wrote.push(QA_GUIDE_REL_PATH);
|
|
4552
4722
|
}
|
|
4553
4723
|
}
|
|
@@ -4559,12 +4729,12 @@ function performInit(cwd, force) {
|
|
|
4559
4729
|
continue;
|
|
4560
4730
|
}
|
|
4561
4731
|
if (profile.kind !== "scheduled" || !profile.schedule) continue;
|
|
4562
|
-
const target =
|
|
4563
|
-
if (
|
|
4732
|
+
const target = path19.join(workflowDir, `kody-${exe.name}.yml`);
|
|
4733
|
+
if (fs21.existsSync(target) && !force) {
|
|
4564
4734
|
skipped.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
4565
4735
|
continue;
|
|
4566
4736
|
}
|
|
4567
|
-
|
|
4737
|
+
fs21.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
|
|
4568
4738
|
wrote.push(`.github/workflows/kody-${exe.name}.yml`);
|
|
4569
4739
|
}
|
|
4570
4740
|
let labels;
|
|
@@ -4702,8 +4872,8 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
4702
4872
|
};
|
|
4703
4873
|
|
|
4704
4874
|
// src/scripts/loadMissionFromFile.ts
|
|
4705
|
-
import * as
|
|
4706
|
-
import * as
|
|
4875
|
+
import * as fs22 from "fs";
|
|
4876
|
+
import * as path20 from "path";
|
|
4707
4877
|
var loadMissionFromFile = async (ctx, _profile, args) => {
|
|
4708
4878
|
const missionsDir = String(args?.missionsDir ?? ".kody/missions");
|
|
4709
4879
|
const slugArg = String(args?.slugArg ?? "mission");
|
|
@@ -4711,11 +4881,11 @@ var loadMissionFromFile = async (ctx, _profile, args) => {
|
|
|
4711
4881
|
if (!slug) {
|
|
4712
4882
|
throw new Error(`loadMissionFromFile: ctx.args.${slugArg} must be a non-empty slug`);
|
|
4713
4883
|
}
|
|
4714
|
-
const absPath =
|
|
4715
|
-
if (!
|
|
4884
|
+
const absPath = path20.join(ctx.cwd, missionsDir, `${slug}.md`);
|
|
4885
|
+
if (!fs22.existsSync(absPath)) {
|
|
4716
4886
|
throw new Error(`loadMissionFromFile: mission file not found: ${absPath}`);
|
|
4717
4887
|
}
|
|
4718
|
-
const raw =
|
|
4888
|
+
const raw = fs22.readFileSync(absPath, "utf-8");
|
|
4719
4889
|
const { title, body } = parseMissionFile(raw, slug);
|
|
4720
4890
|
const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, missionsDir });
|
|
4721
4891
|
const loaded = await backend.load(slug);
|
|
@@ -4849,16 +5019,16 @@ var loadTaskState = async (ctx) => {
|
|
|
4849
5019
|
};
|
|
4850
5020
|
|
|
4851
5021
|
// src/scripts/loadVaultContext.ts
|
|
4852
|
-
import * as
|
|
4853
|
-
import * as
|
|
5022
|
+
import * as fs23 from "fs";
|
|
5023
|
+
import * as path21 from "path";
|
|
4854
5024
|
var VAULT_DIR_RELATIVE = ".kody/vault";
|
|
4855
5025
|
var MAX_PAGES = 8;
|
|
4856
5026
|
var PER_PAGE_MAX_BYTES = 4e3;
|
|
4857
5027
|
var TOTAL_MAX_BYTES2 = 24e3;
|
|
4858
5028
|
var TRUNCATED_SUFFIX2 = "\n\n\u2026 (truncated)";
|
|
4859
5029
|
var loadVaultContext = async (ctx) => {
|
|
4860
|
-
const vaultAbs =
|
|
4861
|
-
if (!
|
|
5030
|
+
const vaultAbs = path21.join(ctx.cwd, VAULT_DIR_RELATIVE);
|
|
5031
|
+
if (!fs23.existsSync(vaultAbs)) {
|
|
4862
5032
|
ctx.data.vaultContext = "";
|
|
4863
5033
|
return;
|
|
4864
5034
|
}
|
|
@@ -4883,21 +5053,21 @@ function collectPages(vaultAbs) {
|
|
|
4883
5053
|
walkMd(vaultAbs, (file) => {
|
|
4884
5054
|
let stat;
|
|
4885
5055
|
try {
|
|
4886
|
-
stat =
|
|
5056
|
+
stat = fs23.statSync(file);
|
|
4887
5057
|
} catch {
|
|
4888
5058
|
return;
|
|
4889
5059
|
}
|
|
4890
5060
|
let raw;
|
|
4891
5061
|
try {
|
|
4892
|
-
raw =
|
|
5062
|
+
raw = fs23.readFileSync(file, "utf-8");
|
|
4893
5063
|
} catch {
|
|
4894
5064
|
return;
|
|
4895
5065
|
}
|
|
4896
5066
|
const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
4897
|
-
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ??
|
|
5067
|
+
const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path21.basename(file, ".md");
|
|
4898
5068
|
const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
|
|
4899
5069
|
out.push({
|
|
4900
|
-
relPath:
|
|
5070
|
+
relPath: path21.relative(vaultAbs, file),
|
|
4901
5071
|
title,
|
|
4902
5072
|
updated,
|
|
4903
5073
|
content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX2 : raw,
|
|
@@ -4965,16 +5135,16 @@ function walkMd(root, visit) {
|
|
|
4965
5135
|
const dir = stack.pop();
|
|
4966
5136
|
let names;
|
|
4967
5137
|
try {
|
|
4968
|
-
names =
|
|
5138
|
+
names = fs23.readdirSync(dir);
|
|
4969
5139
|
} catch {
|
|
4970
5140
|
continue;
|
|
4971
5141
|
}
|
|
4972
5142
|
for (const name of names) {
|
|
4973
5143
|
if (name.startsWith(".")) continue;
|
|
4974
|
-
const full =
|
|
5144
|
+
const full = path21.join(dir, name);
|
|
4975
5145
|
let stat;
|
|
4976
5146
|
try {
|
|
4977
|
-
stat =
|
|
5147
|
+
stat = fs23.statSync(full);
|
|
4978
5148
|
} catch {
|
|
4979
5149
|
continue;
|
|
4980
5150
|
}
|
|
@@ -4996,16 +5166,16 @@ var markFlowSuccess = async (ctx) => {
|
|
|
4996
5166
|
};
|
|
4997
5167
|
|
|
4998
5168
|
// src/scripts/memorizeFlow.ts
|
|
4999
|
-
import { execFileSync as
|
|
5000
|
-
import * as
|
|
5001
|
-
import * as
|
|
5169
|
+
import { execFileSync as execFileSync17 } from "child_process";
|
|
5170
|
+
import * as fs24 from "fs";
|
|
5171
|
+
import * as path22 from "path";
|
|
5002
5172
|
var VAULT_DIR_RELATIVE2 = ".kody/vault";
|
|
5003
5173
|
var DEFAULT_LOOKBACK_HOURS = 36;
|
|
5004
5174
|
var MAX_RECENT_PRS = 25;
|
|
5005
5175
|
var MAX_VAULT_INDEX_ENTRIES = 200;
|
|
5006
5176
|
var PR_BODY_TRUNC = 2e3;
|
|
5007
5177
|
var memorizeFlow = async (ctx) => {
|
|
5008
|
-
const vaultAbs =
|
|
5178
|
+
const vaultAbs = path22.join(ctx.cwd, VAULT_DIR_RELATIVE2);
|
|
5009
5179
|
ensureBranch(ctx, vaultAbs);
|
|
5010
5180
|
if (ctx.skipAgent) return;
|
|
5011
5181
|
const sinceIso = computeSinceIso(vaultAbs);
|
|
@@ -5015,8 +5185,8 @@ var memorizeFlow = async (ctx) => {
|
|
|
5015
5185
|
const recent = fetchRecentPrs(ctx.cwd, sinceIso);
|
|
5016
5186
|
ctx.data.recentPrs = formatRecentPrs(recent);
|
|
5017
5187
|
ctx.data.recentPrCount = recent.length;
|
|
5018
|
-
if (!
|
|
5019
|
-
|
|
5188
|
+
if (!fs24.existsSync(vaultAbs)) {
|
|
5189
|
+
fs24.mkdirSync(vaultAbs, { recursive: true });
|
|
5020
5190
|
}
|
|
5021
5191
|
ctx.data.vaultIndex = formatVaultIndex(vaultAbs);
|
|
5022
5192
|
if (recent.length === 0) {
|
|
@@ -5048,18 +5218,18 @@ function ensureBranch(ctx, vaultAbs) {
|
|
|
5048
5218
|
}
|
|
5049
5219
|
}
|
|
5050
5220
|
ctx.data.branch = branch;
|
|
5051
|
-
if (!
|
|
5052
|
-
|
|
5221
|
+
if (!fs24.existsSync(vaultAbs)) {
|
|
5222
|
+
fs24.mkdirSync(vaultAbs, { recursive: true });
|
|
5053
5223
|
}
|
|
5054
5224
|
}
|
|
5055
5225
|
function computeSinceIso(vaultAbs) {
|
|
5056
5226
|
const fallback = new Date(Date.now() - DEFAULT_LOOKBACK_HOURS * 60 * 60 * 1e3).toISOString();
|
|
5057
|
-
if (!
|
|
5227
|
+
if (!fs24.existsSync(vaultAbs)) return fallback;
|
|
5058
5228
|
let latest = "";
|
|
5059
5229
|
walkMd2(vaultAbs, (file) => {
|
|
5060
5230
|
let raw;
|
|
5061
5231
|
try {
|
|
5062
|
-
raw =
|
|
5232
|
+
raw = fs24.readFileSync(file, "utf-8");
|
|
5063
5233
|
} catch {
|
|
5064
5234
|
return;
|
|
5065
5235
|
}
|
|
@@ -5136,10 +5306,10 @@ function formatVaultIndex(vaultAbs) {
|
|
|
5136
5306
|
const entries = [];
|
|
5137
5307
|
walkMd2(vaultAbs, (file) => {
|
|
5138
5308
|
if (entries.length >= MAX_VAULT_INDEX_ENTRIES) return;
|
|
5139
|
-
const rel =
|
|
5309
|
+
const rel = path22.relative(vaultAbs, file);
|
|
5140
5310
|
let title = rel;
|
|
5141
5311
|
try {
|
|
5142
|
-
const raw =
|
|
5312
|
+
const raw = fs24.readFileSync(file, "utf-8");
|
|
5143
5313
|
const m = raw.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
5144
5314
|
const titleMatch = m?.[1]?.match(/^title:\s*(.+)$/m);
|
|
5145
5315
|
if (titleMatch) title = `${titleMatch[1].trim()} (${rel})`;
|
|
@@ -5151,22 +5321,22 @@ function formatVaultIndex(vaultAbs) {
|
|
|
5151
5321
|
return entries.join("\n");
|
|
5152
5322
|
}
|
|
5153
5323
|
function walkMd2(root, visit) {
|
|
5154
|
-
if (!
|
|
5324
|
+
if (!fs24.existsSync(root)) return;
|
|
5155
5325
|
const stack = [root];
|
|
5156
5326
|
while (stack.length > 0) {
|
|
5157
5327
|
const dir = stack.pop();
|
|
5158
5328
|
let names;
|
|
5159
5329
|
try {
|
|
5160
|
-
names =
|
|
5330
|
+
names = fs24.readdirSync(dir);
|
|
5161
5331
|
} catch {
|
|
5162
5332
|
continue;
|
|
5163
5333
|
}
|
|
5164
5334
|
for (const name of names) {
|
|
5165
5335
|
if (name.startsWith(".")) continue;
|
|
5166
|
-
const full =
|
|
5336
|
+
const full = path22.join(dir, name);
|
|
5167
5337
|
let stat;
|
|
5168
5338
|
try {
|
|
5169
|
-
stat =
|
|
5339
|
+
stat = fs24.statSync(full);
|
|
5170
5340
|
} catch {
|
|
5171
5341
|
continue;
|
|
5172
5342
|
}
|
|
@@ -5179,7 +5349,7 @@ function walkMd2(root, visit) {
|
|
|
5179
5349
|
}
|
|
5180
5350
|
}
|
|
5181
5351
|
function git3(args, cwd) {
|
|
5182
|
-
return
|
|
5352
|
+
return execFileSync17("git", args, {
|
|
5183
5353
|
encoding: "utf-8",
|
|
5184
5354
|
timeout: 3e4,
|
|
5185
5355
|
cwd,
|
|
@@ -5189,7 +5359,7 @@ function git3(args, cwd) {
|
|
|
5189
5359
|
}
|
|
5190
5360
|
|
|
5191
5361
|
// src/scripts/mergeReleasePr.ts
|
|
5192
|
-
import { execFileSync as
|
|
5362
|
+
import { execFileSync as execFileSync18 } from "child_process";
|
|
5193
5363
|
var API_TIMEOUT_MS7 = 6e4;
|
|
5194
5364
|
var mergeReleasePr = async (ctx) => {
|
|
5195
5365
|
const state = ctx.data.taskState;
|
|
@@ -5208,7 +5378,7 @@ var mergeReleasePr = async (ctx) => {
|
|
|
5208
5378
|
process.stderr.write(`[kody mergeReleasePr] merging PR #${prNumber} (${prUrl})
|
|
5209
5379
|
`);
|
|
5210
5380
|
try {
|
|
5211
|
-
const out =
|
|
5381
|
+
const out = execFileSync18("gh", ["pr", "merge", String(prNumber), "--merge"], {
|
|
5212
5382
|
timeout: API_TIMEOUT_MS7,
|
|
5213
5383
|
cwd: ctx.cwd,
|
|
5214
5384
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5663,7 +5833,7 @@ REVIEW_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.github.
|
|
|
5663
5833
|
};
|
|
5664
5834
|
|
|
5665
5835
|
// src/scripts/recordClassification.ts
|
|
5666
|
-
import { execFileSync as
|
|
5836
|
+
import { execFileSync as execFileSync19 } from "child_process";
|
|
5667
5837
|
var API_TIMEOUT_MS8 = 3e4;
|
|
5668
5838
|
var VALID_CLASSES3 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
5669
5839
|
var recordClassification = async (ctx) => {
|
|
@@ -5711,7 +5881,7 @@ function parseClassification(prSummary) {
|
|
|
5711
5881
|
}
|
|
5712
5882
|
function tryAuditComment(issueNumber, body, cwd) {
|
|
5713
5883
|
try {
|
|
5714
|
-
|
|
5884
|
+
execFileSync19("gh", ["issue", "comment", String(issueNumber), "--body", body], {
|
|
5715
5885
|
cwd,
|
|
5716
5886
|
timeout: API_TIMEOUT_MS8,
|
|
5717
5887
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -5839,7 +6009,7 @@ var resolveArtifacts = async (ctx, profile) => {
|
|
|
5839
6009
|
};
|
|
5840
6010
|
|
|
5841
6011
|
// src/scripts/resolveFlow.ts
|
|
5842
|
-
import { execFileSync as
|
|
6012
|
+
import { execFileSync as execFileSync20 } from "child_process";
|
|
5843
6013
|
var CONFLICT_DIFF_MAX_BYTES = 4e4;
|
|
5844
6014
|
var resolveFlow = async (ctx) => {
|
|
5845
6015
|
const prNumber = ctx.args.pr;
|
|
@@ -5909,7 +6079,7 @@ function buildPreferBlock(prefer, baseBranch) {
|
|
|
5909
6079
|
}
|
|
5910
6080
|
function getConflictedFiles(cwd) {
|
|
5911
6081
|
try {
|
|
5912
|
-
const out =
|
|
6082
|
+
const out = execFileSync20("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
5913
6083
|
encoding: "utf-8",
|
|
5914
6084
|
cwd,
|
|
5915
6085
|
env: { ...process.env, HUSKY: "0" }
|
|
@@ -5924,7 +6094,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
|
|
|
5924
6094
|
let total = 0;
|
|
5925
6095
|
for (const f of files) {
|
|
5926
6096
|
try {
|
|
5927
|
-
const content =
|
|
6097
|
+
const content = execFileSync20("cat", [f], { encoding: "utf-8", cwd }).toString();
|
|
5928
6098
|
const snippet = `### ${f}
|
|
5929
6099
|
|
|
5930
6100
|
\`\`\`
|
|
@@ -6025,7 +6195,7 @@ var resolvePreviewUrl = async (ctx) => {
|
|
|
6025
6195
|
};
|
|
6026
6196
|
|
|
6027
6197
|
// src/scripts/revertFlow.ts
|
|
6028
|
-
import { execFileSync as
|
|
6198
|
+
import { execFileSync as execFileSync21 } from "child_process";
|
|
6029
6199
|
var SHA_RE = /^[0-9a-f]{4,40}$/i;
|
|
6030
6200
|
var revertFlow = async (ctx) => {
|
|
6031
6201
|
const prNumber = ctx.args.pr;
|
|
@@ -6107,7 +6277,7 @@ function buildPrSummary(resolved) {
|
|
|
6107
6277
|
return resolved.map((r) => `- Reverted \`${r.full.slice(0, 7)}\`${r.subject ? ` \u2014 ${r.subject}` : ""}`).join("\n");
|
|
6108
6278
|
}
|
|
6109
6279
|
function git4(args, cwd) {
|
|
6110
|
-
return
|
|
6280
|
+
return execFileSync21("git", args, {
|
|
6111
6281
|
encoding: "utf-8",
|
|
6112
6282
|
timeout: 3e4,
|
|
6113
6283
|
cwd,
|
|
@@ -6117,7 +6287,7 @@ function git4(args, cwd) {
|
|
|
6117
6287
|
}
|
|
6118
6288
|
function isAncestorOfHead(sha, cwd) {
|
|
6119
6289
|
try {
|
|
6120
|
-
|
|
6290
|
+
execFileSync21("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
|
|
6121
6291
|
cwd,
|
|
6122
6292
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
6123
6293
|
stdio: ["ignore", "ignore", "ignore"]
|
|
@@ -6270,11 +6440,11 @@ var skipAgent = async (ctx) => {
|
|
|
6270
6440
|
};
|
|
6271
6441
|
|
|
6272
6442
|
// src/scripts/stageMergeConflicts.ts
|
|
6273
|
-
import { execFileSync as
|
|
6443
|
+
import { execFileSync as execFileSync22 } from "child_process";
|
|
6274
6444
|
var stageMergeConflicts = async (ctx) => {
|
|
6275
6445
|
if (ctx.data.agentDone === false) return;
|
|
6276
6446
|
try {
|
|
6277
|
-
|
|
6447
|
+
execFileSync22("git", ["add", "-A"], {
|
|
6278
6448
|
cwd: ctx.cwd,
|
|
6279
6449
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
6280
6450
|
stdio: "pipe"
|
|
@@ -6284,7 +6454,7 @@ var stageMergeConflicts = async (ctx) => {
|
|
|
6284
6454
|
};
|
|
6285
6455
|
|
|
6286
6456
|
// src/scripts/startFlow.ts
|
|
6287
|
-
import { execFileSync as
|
|
6457
|
+
import { execFileSync as execFileSync23 } from "child_process";
|
|
6288
6458
|
var API_TIMEOUT_MS9 = 3e4;
|
|
6289
6459
|
var startFlow = async (ctx, profile, _agentResult, args) => {
|
|
6290
6460
|
const entry = args?.entry;
|
|
@@ -6318,7 +6488,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
6318
6488
|
const sub = target === "pr" && state?.core.prUrl ? "pr" : "issue";
|
|
6319
6489
|
const body = `@kody ${next}`;
|
|
6320
6490
|
try {
|
|
6321
|
-
|
|
6491
|
+
execFileSync23("gh", [sub, "comment", String(targetNumber), "--body", body], {
|
|
6322
6492
|
timeout: API_TIMEOUT_MS9,
|
|
6323
6493
|
cwd,
|
|
6324
6494
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6332,7 +6502,7 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
6332
6502
|
}
|
|
6333
6503
|
|
|
6334
6504
|
// src/scripts/syncFlow.ts
|
|
6335
|
-
import { execFileSync as
|
|
6505
|
+
import { execFileSync as execFileSync24 } from "child_process";
|
|
6336
6506
|
var syncFlow = async (ctx, _profile, args) => {
|
|
6337
6507
|
const announceOnSuccess = Boolean(args?.announceOnSuccess);
|
|
6338
6508
|
const prNumber = ctx.args.pr;
|
|
@@ -6404,7 +6574,7 @@ function bail2(ctx, prNumber, reason) {
|
|
|
6404
6574
|
}
|
|
6405
6575
|
function revParseHead(cwd) {
|
|
6406
6576
|
try {
|
|
6407
|
-
return
|
|
6577
|
+
return execFileSync24("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
|
|
6408
6578
|
} catch {
|
|
6409
6579
|
return "";
|
|
6410
6580
|
}
|
|
@@ -6412,9 +6582,9 @@ function revParseHead(cwd) {
|
|
|
6412
6582
|
function pushBranch(branch, cwd) {
|
|
6413
6583
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
|
|
6414
6584
|
try {
|
|
6415
|
-
|
|
6585
|
+
execFileSync24("git", ["push", "-u", "origin", branch], { cwd, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
6416
6586
|
} catch {
|
|
6417
|
-
|
|
6587
|
+
execFileSync24("git", ["push", "--force-with-lease", "-u", "origin", branch], {
|
|
6418
6588
|
cwd,
|
|
6419
6589
|
env,
|
|
6420
6590
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6525,7 +6695,7 @@ var verify = async (ctx) => {
|
|
|
6525
6695
|
};
|
|
6526
6696
|
|
|
6527
6697
|
// src/scripts/waitForCi.ts
|
|
6528
|
-
import { execFileSync as
|
|
6698
|
+
import { execFileSync as execFileSync25 } from "child_process";
|
|
6529
6699
|
var API_TIMEOUT_MS10 = 3e4;
|
|
6530
6700
|
var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
6531
6701
|
const timeoutMinutes = numArg(args, "timeoutMinutes", 30);
|
|
@@ -6540,17 +6710,17 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
|
6540
6710
|
return;
|
|
6541
6711
|
}
|
|
6542
6712
|
const fixCiAttempts = state?.core.attempts?.["fix-ci"] ?? 0;
|
|
6543
|
-
await
|
|
6713
|
+
await sleep2(initialWaitSeconds * 1e3);
|
|
6544
6714
|
const deadline = Date.now() + timeoutMinutes * 6e4;
|
|
6545
6715
|
let lastSummary = "";
|
|
6546
6716
|
while (Date.now() < deadline) {
|
|
6547
6717
|
const rows = fetchChecks(prNumber, ctx.cwd);
|
|
6548
6718
|
if (rows === null) {
|
|
6549
|
-
await
|
|
6719
|
+
await sleep2(pollSeconds * 1e3);
|
|
6550
6720
|
continue;
|
|
6551
6721
|
}
|
|
6552
6722
|
if (rows.length === 0) {
|
|
6553
|
-
await
|
|
6723
|
+
await sleep2(pollSeconds * 1e3);
|
|
6554
6724
|
continue;
|
|
6555
6725
|
}
|
|
6556
6726
|
const summary = summarize(rows);
|
|
@@ -6593,7 +6763,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
|
6593
6763
|
tryPostPr7(prNumber, `\u2705 kody waitForCi: all ${rows.length} checks green on PR #${prNumber}`, ctx.cwd);
|
|
6594
6764
|
return;
|
|
6595
6765
|
}
|
|
6596
|
-
await
|
|
6766
|
+
await sleep2(pollSeconds * 1e3);
|
|
6597
6767
|
}
|
|
6598
6768
|
ctx.data.action = mkAction("CI_TIMEOUT", {
|
|
6599
6769
|
reason: `CI did not complete within ${timeoutMinutes} minutes`,
|
|
@@ -6603,7 +6773,7 @@ var waitForCi = async (ctx, _profile, _agentResult, args) => {
|
|
|
6603
6773
|
};
|
|
6604
6774
|
function fetchChecks(prNumber, cwd) {
|
|
6605
6775
|
try {
|
|
6606
|
-
const raw =
|
|
6776
|
+
const raw = execFileSync25("gh", ["pr", "checks", String(prNumber), "--json", "bucket,state,name,workflow,link"], {
|
|
6607
6777
|
encoding: "utf-8",
|
|
6608
6778
|
timeout: API_TIMEOUT_MS10,
|
|
6609
6779
|
cwd,
|
|
@@ -6645,7 +6815,7 @@ function tryPostPr7(prNumber, body, cwd) {
|
|
|
6645
6815
|
} catch {
|
|
6646
6816
|
}
|
|
6647
6817
|
}
|
|
6648
|
-
function
|
|
6818
|
+
function sleep2(ms) {
|
|
6649
6819
|
return new Promise((res) => setTimeout(res, ms));
|
|
6650
6820
|
}
|
|
6651
6821
|
|
|
@@ -6775,7 +6945,7 @@ var writeMissionStateFile = async (ctx, _profile, _agentResult, args) => {
|
|
|
6775
6945
|
};
|
|
6776
6946
|
|
|
6777
6947
|
// src/scripts/writeRunSummary.ts
|
|
6778
|
-
import * as
|
|
6948
|
+
import * as fs25 from "fs";
|
|
6779
6949
|
var writeRunSummary = async (ctx, profile) => {
|
|
6780
6950
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
6781
6951
|
if (!summaryPath) return;
|
|
@@ -6797,7 +6967,7 @@ var writeRunSummary = async (ctx, profile) => {
|
|
|
6797
6967
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
6798
6968
|
lines.push("");
|
|
6799
6969
|
try {
|
|
6800
|
-
|
|
6970
|
+
fs25.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
6801
6971
|
`);
|
|
6802
6972
|
} catch {
|
|
6803
6973
|
}
|
|
@@ -6879,7 +7049,7 @@ var allScriptNames = /* @__PURE__ */ new Set([
|
|
|
6879
7049
|
]);
|
|
6880
7050
|
|
|
6881
7051
|
// src/tools.ts
|
|
6882
|
-
import { execFileSync as
|
|
7052
|
+
import { execFileSync as execFileSync26 } from "child_process";
|
|
6883
7053
|
function verifyCliTools(tools, cwd) {
|
|
6884
7054
|
const out = [];
|
|
6885
7055
|
for (const t of tools) out.push(verifyOne(t, cwd));
|
|
@@ -6912,7 +7082,7 @@ function verifyOne(tool, cwd) {
|
|
|
6912
7082
|
}
|
|
6913
7083
|
function runShell(cmd, cwd, timeoutMs = 3e4) {
|
|
6914
7084
|
try {
|
|
6915
|
-
|
|
7085
|
+
execFileSync26("sh", ["-c", cmd], { cwd, stdio: "pipe", timeout: timeoutMs });
|
|
6916
7086
|
return true;
|
|
6917
7087
|
} catch {
|
|
6918
7088
|
return false;
|
|
@@ -6981,9 +7151,9 @@ async function runExecutable(profileName, input) {
|
|
|
6981
7151
|
data: {},
|
|
6982
7152
|
output: { exitCode: 0 }
|
|
6983
7153
|
};
|
|
6984
|
-
const ndjsonDir =
|
|
7154
|
+
const ndjsonDir = path23.join(input.cwd, ".kody");
|
|
6985
7155
|
const invokeAgent = async (prompt) => {
|
|
6986
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
7156
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path23.isAbsolute(p) ? p : path23.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
6987
7157
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
6988
7158
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
6989
7159
|
return runAgent({
|
|
@@ -7078,17 +7248,17 @@ async function runExecutable(profileName, input) {
|
|
|
7078
7248
|
function resolveProfilePath(profileName) {
|
|
7079
7249
|
const found = resolveExecutable(profileName);
|
|
7080
7250
|
if (found) return found;
|
|
7081
|
-
const here =
|
|
7251
|
+
const here = path23.dirname(new URL(import.meta.url).pathname);
|
|
7082
7252
|
const candidates = [
|
|
7083
|
-
|
|
7253
|
+
path23.join(here, "executables", profileName, "profile.json"),
|
|
7084
7254
|
// same-dir sibling (dev)
|
|
7085
|
-
|
|
7255
|
+
path23.join(here, "..", "executables", profileName, "profile.json"),
|
|
7086
7256
|
// up one (prod: dist/bin → dist/executables)
|
|
7087
|
-
|
|
7257
|
+
path23.join(here, "..", "src", "executables", profileName, "profile.json")
|
|
7088
7258
|
// fallback
|
|
7089
7259
|
];
|
|
7090
7260
|
for (const c of candidates) {
|
|
7091
|
-
if (
|
|
7261
|
+
if (fs26.existsSync(c)) return c;
|
|
7092
7262
|
}
|
|
7093
7263
|
return candidates[0];
|
|
7094
7264
|
}
|
|
@@ -7192,8 +7362,8 @@ function resolveShellTimeoutMs(entry) {
|
|
|
7192
7362
|
var SIGKILL_GRACE_MS = 5e3;
|
|
7193
7363
|
async function runShellEntry(entry, ctx, profile) {
|
|
7194
7364
|
const shellName = entry.shell;
|
|
7195
|
-
const shellPath =
|
|
7196
|
-
if (!
|
|
7365
|
+
const shellPath = path23.join(profile.dir, shellName);
|
|
7366
|
+
if (!fs26.existsSync(shellPath)) {
|
|
7197
7367
|
ctx.skipAgent = true;
|
|
7198
7368
|
ctx.output.exitCode = 99;
|
|
7199
7369
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -7314,6 +7484,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
7314
7484
|
const issueNumber = ctx.args.issue;
|
|
7315
7485
|
let currentIdx = 0;
|
|
7316
7486
|
let iteration = 0;
|
|
7487
|
+
let knownPrUrl;
|
|
7317
7488
|
while (currentIdx >= 0 && currentIdx < children.length) {
|
|
7318
7489
|
iteration++;
|
|
7319
7490
|
if (iteration > CONTAINER_MAX_ITERATIONS) {
|
|
@@ -7328,6 +7499,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
7328
7499
|
process.stderr.write(`[kody container] step ${iteration}: invoking ${child.exec}
|
|
7329
7500
|
`);
|
|
7330
7501
|
const priorState = readContainerState(ctx, child, reader);
|
|
7502
|
+
if (priorState.core?.prUrl) knownPrUrl = priorState.core.prUrl;
|
|
7331
7503
|
const priorAction = priorState.executables?.[child.exec]?.lastAction;
|
|
7332
7504
|
let actionType;
|
|
7333
7505
|
if (priorAction && /_COMPLETED$/i.test(priorAction.type)) {
|
|
@@ -7337,8 +7509,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
7337
7509
|
} else {
|
|
7338
7510
|
let cliArgs;
|
|
7339
7511
|
if (child.target === "pr") {
|
|
7340
|
-
const
|
|
7341
|
-
const prNumber = prUrl ? parsePrNumber4(prUrl) : null;
|
|
7512
|
+
const prNumber = knownPrUrl ? parsePrNumber4(knownPrUrl) : null;
|
|
7342
7513
|
if (!prNumber) {
|
|
7343
7514
|
const reason = `container child "${child.exec}" needs --pr but state.core.prUrl is unset`;
|
|
7344
7515
|
process.stderr.write(`[kody container] aborting: ${reason}
|
|
@@ -7384,6 +7555,7 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
7384
7555
|
return;
|
|
7385
7556
|
}
|
|
7386
7557
|
const next = readContainerState(ctx, child, reader);
|
|
7558
|
+
if (next.core?.prUrl) knownPrUrl = next.core.prUrl;
|
|
7387
7559
|
ctx.data.taskState = next;
|
|
7388
7560
|
const actionFromState = next.core?.lastOutcome?.type;
|
|
7389
7561
|
actionType = actionFromState ?? (childOut.exitCode === 0 ? "RUN_COMPLETED" : "RUN_FAILED");
|
|
@@ -7420,16 +7592,25 @@ async function runContainerLoop(profile, ctx, input) {
|
|
|
7420
7592
|
currentIdx = nextIdx;
|
|
7421
7593
|
}
|
|
7422
7594
|
}
|
|
7423
|
-
function readContainerState(ctx,
|
|
7595
|
+
function readContainerState(ctx, child, reader) {
|
|
7424
7596
|
const issueNumber = ctx.args.issue;
|
|
7597
|
+
const cached = ctx.data.taskState;
|
|
7598
|
+
const prUrl = cached?.core?.prUrl;
|
|
7599
|
+
const prNumber = prUrl ? parsePrNumber4(prUrl) : null;
|
|
7600
|
+
if (child.target === "pr" && prNumber) {
|
|
7601
|
+
try {
|
|
7602
|
+
return reader("pr", prNumber, ctx.cwd);
|
|
7603
|
+
} catch {
|
|
7604
|
+
}
|
|
7605
|
+
}
|
|
7425
7606
|
if (issueNumber !== void 0) {
|
|
7426
7607
|
try {
|
|
7427
7608
|
return reader("issue", issueNumber, ctx.cwd);
|
|
7428
7609
|
} catch {
|
|
7429
7610
|
}
|
|
7430
7611
|
}
|
|
7431
|
-
if (
|
|
7432
|
-
return
|
|
7612
|
+
if (cached && typeof cached === "object") {
|
|
7613
|
+
return cached;
|
|
7433
7614
|
}
|
|
7434
7615
|
return {
|
|
7435
7616
|
schemaVersion: 1,
|
|
@@ -7552,14 +7733,14 @@ function resolveAuthToken(env = process.env) {
|
|
|
7552
7733
|
return token;
|
|
7553
7734
|
}
|
|
7554
7735
|
function detectPackageManager2(cwd) {
|
|
7555
|
-
if (
|
|
7556
|
-
if (
|
|
7557
|
-
if (
|
|
7736
|
+
if (fs27.existsSync(path24.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
7737
|
+
if (fs27.existsSync(path24.join(cwd, "yarn.lock"))) return "yarn";
|
|
7738
|
+
if (fs27.existsSync(path24.join(cwd, "bun.lockb"))) return "bun";
|
|
7558
7739
|
return "npm";
|
|
7559
7740
|
}
|
|
7560
7741
|
function shellOut(cmd, args, cwd, stream = true) {
|
|
7561
7742
|
try {
|
|
7562
|
-
|
|
7743
|
+
execFileSync27(cmd, args, {
|
|
7563
7744
|
cwd,
|
|
7564
7745
|
stdio: stream ? "inherit" : "pipe",
|
|
7565
7746
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" }
|
|
@@ -7572,7 +7753,7 @@ function shellOut(cmd, args, cwd, stream = true) {
|
|
|
7572
7753
|
}
|
|
7573
7754
|
function isOnPath(bin) {
|
|
7574
7755
|
try {
|
|
7575
|
-
|
|
7756
|
+
execFileSync27("which", [bin], { stdio: "pipe" });
|
|
7576
7757
|
return true;
|
|
7577
7758
|
} catch {
|
|
7578
7759
|
return false;
|
|
@@ -7606,7 +7787,7 @@ function installLitellmIfNeeded(cwd) {
|
|
|
7606
7787
|
} catch {
|
|
7607
7788
|
}
|
|
7608
7789
|
try {
|
|
7609
|
-
|
|
7790
|
+
execFileSync27("python3", ["-c", "import litellm"], { stdio: "pipe" });
|
|
7610
7791
|
process.stdout.write("\u2192 kody: litellm already installed\n");
|
|
7611
7792
|
return 0;
|
|
7612
7793
|
} catch {
|
|
@@ -7616,16 +7797,16 @@ function installLitellmIfNeeded(cwd) {
|
|
|
7616
7797
|
}
|
|
7617
7798
|
function configureGitIdentity(cwd) {
|
|
7618
7799
|
try {
|
|
7619
|
-
const name =
|
|
7800
|
+
const name = execFileSync27("git", ["config", "user.name"], { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
7620
7801
|
if (name) return;
|
|
7621
7802
|
} catch {
|
|
7622
7803
|
}
|
|
7623
7804
|
try {
|
|
7624
|
-
|
|
7805
|
+
execFileSync27("git", ["config", "user.name", "github-actions[bot]"], { cwd, stdio: "pipe" });
|
|
7625
7806
|
} catch {
|
|
7626
7807
|
}
|
|
7627
7808
|
try {
|
|
7628
|
-
|
|
7809
|
+
execFileSync27("git", ["config", "user.email", "41898282+github-actions[bot]@users.noreply.github.com"], {
|
|
7629
7810
|
cwd,
|
|
7630
7811
|
stdio: "pipe"
|
|
7631
7812
|
});
|
|
@@ -7634,11 +7815,11 @@ function configureGitIdentity(cwd) {
|
|
|
7634
7815
|
}
|
|
7635
7816
|
function postFailureTail(issueNumber, cwd, reason) {
|
|
7636
7817
|
if (!issueNumber) return;
|
|
7637
|
-
const logPath =
|
|
7818
|
+
const logPath = path24.join(cwd, ".kody", "last-run.jsonl");
|
|
7638
7819
|
let tail = "";
|
|
7639
7820
|
try {
|
|
7640
|
-
if (
|
|
7641
|
-
const content =
|
|
7821
|
+
if (fs27.existsSync(logPath)) {
|
|
7822
|
+
const content = fs27.readFileSync(logPath, "utf-8");
|
|
7642
7823
|
tail = content.slice(-3e3);
|
|
7643
7824
|
}
|
|
7644
7825
|
} catch {
|
|
@@ -7663,7 +7844,7 @@ async function runCi(argv) {
|
|
|
7663
7844
|
return 0;
|
|
7664
7845
|
}
|
|
7665
7846
|
const args = parseCiArgs(argv);
|
|
7666
|
-
const cwd = args.cwd ?
|
|
7847
|
+
const cwd = args.cwd ? path24.resolve(args.cwd) : process.cwd();
|
|
7667
7848
|
let earlyConfig;
|
|
7668
7849
|
try {
|
|
7669
7850
|
earlyConfig = loadConfig(cwd);
|
|
@@ -7673,9 +7854,9 @@ async function runCi(argv) {
|
|
|
7673
7854
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
7674
7855
|
const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
|
|
7675
7856
|
let manualWorkflowDispatch = false;
|
|
7676
|
-
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
7857
|
+
if (!args.issueNumber && !autoFallback && eventName === "workflow_dispatch" && dispatchEventPath && fs27.existsSync(dispatchEventPath)) {
|
|
7677
7858
|
try {
|
|
7678
|
-
const evt = JSON.parse(
|
|
7859
|
+
const evt = JSON.parse(fs27.readFileSync(dispatchEventPath, "utf-8"));
|
|
7679
7860
|
const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
|
|
7680
7861
|
const sessionInput = String(evt?.inputs?.sessionId ?? "");
|
|
7681
7862
|
manualWorkflowDispatch = !sessionInput && !(Number.isFinite(issueInput) && issueInput > 0);
|
|
@@ -7890,15 +8071,15 @@ function parseChatArgs(argv, env = process.env) {
|
|
|
7890
8071
|
return result;
|
|
7891
8072
|
}
|
|
7892
8073
|
function commitChatFiles(cwd, sessionId, verbose) {
|
|
7893
|
-
const sessionFile =
|
|
7894
|
-
const eventsFile =
|
|
7895
|
-
const paths = [sessionFile, eventsFile].filter((p) =>
|
|
8074
|
+
const sessionFile = path25.relative(cwd, sessionFilePath(cwd, sessionId));
|
|
8075
|
+
const eventsFile = path25.relative(cwd, eventsFilePath(cwd, sessionId));
|
|
8076
|
+
const paths = [sessionFile, eventsFile].filter((p) => fs28.existsSync(path25.join(cwd, p)));
|
|
7896
8077
|
if (paths.length === 0) return;
|
|
7897
8078
|
const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
|
|
7898
8079
|
try {
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
8080
|
+
execFileSync28("git", ["add", ...paths], opts);
|
|
8081
|
+
execFileSync28("git", ["commit", "--quiet", "-m", `chat: reply for ${sessionId}`], opts);
|
|
8082
|
+
execFileSync28("git", ["push", "--quiet", "origin", "HEAD"], opts);
|
|
7902
8083
|
} catch (err) {
|
|
7903
8084
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7904
8085
|
process.stderr.write(`[kody:chat] commit/push skipped: ${msg}
|
|
@@ -7930,7 +8111,7 @@ async function runChat(argv) {
|
|
|
7930
8111
|
${CHAT_HELP}`);
|
|
7931
8112
|
return 64;
|
|
7932
8113
|
}
|
|
7933
|
-
const cwd = args.cwd ?
|
|
8114
|
+
const cwd = args.cwd ? path25.resolve(args.cwd) : process.cwd();
|
|
7934
8115
|
const sessionId = args.sessionId;
|
|
7935
8116
|
const unpackedSecrets = unpackAllSecrets();
|
|
7936
8117
|
if (unpackedSecrets > 0) {
|
|
@@ -7974,7 +8155,21 @@ ${CHAT_HELP}`);
|
|
|
7974
8155
|
const sessionFile = sessionFilePath(cwd, sessionId);
|
|
7975
8156
|
if (args.initMessage) seedInitialMessage(sessionFile, args.initMessage);
|
|
7976
8157
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
8158
|
+
const meta = readMeta(sessionFile);
|
|
7977
8159
|
try {
|
|
8160
|
+
if (meta?.mode === "interactive") {
|
|
8161
|
+
const result2 = await runInteractiveMode({
|
|
8162
|
+
sessionId,
|
|
8163
|
+
cwd,
|
|
8164
|
+
model,
|
|
8165
|
+
litellmUrl: litellm?.url ?? null,
|
|
8166
|
+
sink,
|
|
8167
|
+
meta,
|
|
8168
|
+
verbose: args.verbose,
|
|
8169
|
+
quiet: args.quiet
|
|
8170
|
+
});
|
|
8171
|
+
return result2.exitCode;
|
|
8172
|
+
}
|
|
7978
8173
|
const result = await runChatTurn({
|
|
7979
8174
|
sessionId,
|
|
7980
8175
|
sessionFile,
|